Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch trunk Through [8be0372c10] Excluding Merge-Ins
This is equivalent to a diff from c06edd231f to 8be0372c10
2024-04-24
| ||
21:25 | Fix or disable brittle test cases that were broken by changes in 2.23. ... (check-in: 5ad708085a user: drh tags: branch-2.24) | |
2024-04-23
| ||
13:43 | Update the homepage for the 2.24 release. ... (check-in: dee02ab642 user: drh tags: trunk) | |
13:25 | Version 2.24 ... (check-in: 8be0372c10 user: drh tags: trunk, release, version-2.24) | |
2024-04-22
| ||
16:29 | cgi.md: be less specific about the Apache version in which the Content-Length change happened because a new forum post reports that it happens at least as far back as 2.4.41. ... (check-in: 9af5f38671 user: stephan tags: trunk) | |
2010-05-16
| ||
19:16 | Rename the "clear-title" branch as the new trunk. The trunk is now relicensed using the Simplified BSD License. ... (check-in: bf1c21ba16 user: drh tags: trunk) | |
19:08 | Change from GPL to the Simplified BSD License. ... (Closed-Leaf check-in: c06edd231f user: drh tags: clear-title) | |
11:18 | Pull the latest trunk changes into clear-title. ... (check-in: 96722b6d01 user: drh tags: clear-title) | |
Added .editorconfig.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # EditorConfig (https://editorconfig.com) Configuration for Fossil # # Following https://fossil-scm.org/fossil/doc/trunk/www/style.wiki # # Defaults for all files [*] end_of_line = lf insert_final_newline = true indent_style = space indent_size = 2 [{Makefile,Makefile.*,*.mk}] indent_style = tab |
Added .fossil-settings/binary-glob.
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | *.gif *.ico *.jpg *.odp *.dia *.pdf *.png *.wav compat/zlib/contrib/blast/test.pk compat/zlib/contrib/dotzlib/DotZLib.chm compat/zlib/contrib/puff/zeros.raw compat/zlib/zlib.3.pdf extsrc/pikchr.wasm |
Added .fossil-settings/clean-glob.
> > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | *.a *.lib *.manifest *.o *.obj *.pdb *.res Makefile autosetup/jimsh0 autosetup/jimsh0.exe bld/* msvcbld/* wbld/* win/*.c win/*.h win/*.exe win/headers win/linkopts autoconfig.h config.log |
Added .fossil-settings/crlf-glob.
> > > > > | 1 2 3 4 5 | compat/zlib/* setup/fossil.iss test/th1-docs-input.txt test/th1-hooks-input.txt win/buildmsvc.bat |
Added .fossil-settings/encoding-glob.
> > > > | 1 2 3 4 | compat/zlib/contrib/dotzlib/DotZLib/*.cs test/utf16be.txt test/utf16le.txt win/fossil.rc |
Added .fossil-settings/ignore-glob.
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | compat/openssl* compat/tcl* compat/zlib/contrib/ada/* compat/zlib/doc/* fossil fossil.exe win/fossil.exe *shell-see.* *sqlite3-see.* |
Added .project.
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | <?xml version="1.0" encoding="UTF-8"?> <projectDescription> <name>fossil</name> <comment></comment> <projects> </projects> <buildSpec> </buildSpec> <natures> </natures> </projectDescription> |
Added .settings/org.eclipse.core.resources.prefs.
> > | 1 2 | eclipse.preferences.version=1 encoding/<project>=UTF-8 |
Added .settings/org.eclipse.core.runtime.prefs.
> > | 1 2 | eclipse.preferences.version=1 line.separator=\n |
Changes to BUILD.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 | To do a complete build, just type: ./configure; make The ./configure script builds Makefile from Makefile.in based on your system and any options you select (run "./configure --help" for a listing of the available options.) If you wish to use the original Makefile with no configuration, you can instead use: make -f Makefile.classic On a windows box, use one of the Makefiles in the win/ subdirectory, according to your compiler and environment. If you have MinGW or MinGW-w64 installed on your system (Msys or Cygwin, or as cross-compile environment on Linux or Darwin), then consider: make -f win/Makefile.mingw 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: 1. Make and change to a new directory to do the builds in. 2. Run the "configure" script from this directory. 3. Type: "make" For example: mkdir build cd build ../configure make This will now keep all generates files separate from the maintained source code. -------------------------------------------------------------------------- Here are some notes on what is happening behind the scenes: * The configure script (if used) examines the options given and runs various tests with the C compiler to create Makefile from the Makefile.in template as well as autoconfig.h * The Makefile just sets up a few macros and then invokes the real makefile in src/main.mk. The src/main.mk makefile is automatically generated by a TCL script found at tools/makemake.tcl. Do not edit src/main.mk directly. Update tools/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 tools/makeheaders.c. Documentation is found in tools/makeheaders.html. * Most *.c source files are preprocessed using a program called "translate". The sources to translate are found in tools/translate.c. A header comment in tools/translate.c explains in detail what it does. * The tools/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://fossil-scm.org/home/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 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 | # syntax=docker/dockerfile:1.3 # See www/containers.md for documentation on how to use this file. ## --------------------------------------------------------------------- ## STAGE 1: Build static Fossil binary ## --------------------------------------------------------------------- ### We aren't pinning to a more stable version of Alpine because we want ### to build with the latest tools and libraries available in case they ### fixed something that matters to us since the last build. Everything ### below depends on this layer, and so, alas, we toss this container's ### cache on Alpine's release schedule, roughly once a month. FROM alpine:latest AS bld WORKDIR /fsl ### Bake the basic Alpine Linux into a base layer so it only changes ### when the upstream image is updated or we change the package set. RUN set -x \ && apk update \ && apk upgrade --no-cache \ && apk add --no-cache \ gcc make \ linux-headers musl-dev \ openssl-dev openssl-libs-static \ zlib-dev zlib-static ### Build Fossil as a separate layer so we don't have to rebuild the ### Alpine environment for each iteration of Fossil's dev cycle. ### ### We must cope with a bizarre ADD misfeature here: it unpacks tarballs ### automatically when you give it a local file name but not if you give ### it a /tarball URL! It matters because we default to a URL in case ### you're building outside a Fossil checkout, but when building via the ### container-image target, we avoid a costly hit on fossil-scm.org ### by leveraging its DVCS nature via the "tarball" command and passing ### the resulting file's name in. ARG FSLCFG="" ARG FSLVER="trunk" ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}" ENV FSLSTB=/fsl/src.tar.gz ADD $FSLURL $FSLSTB RUN set -x \ && if [ -d $FSLSTB ] ; \ then mv $FSLSTB/src . ; \ else tar -xf src.tar.gz ; fi \ && src/configure --static CFLAGS='-Os -s' $FSLCFG && make -j16 ## --------------------------------------------------------------------- ## STAGE 2: Pare that back to the bare essentials. ## --------------------------------------------------------------------- FROM busybox AS os ARG UID=499 ### Set up that base OS for our specific use without tying it to ### anything likely to change often. So long as the user leaves ### UID alone, this layer will be durable. RUN set -x \ && mkdir e log museum \ && echo "root:x:0:0:Admin:/:/false" > /e/passwd \ && echo "root:x:0:root" > /e/group \ && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /e/passwd \ && echo "fossil:x:${UID}:fossil" >> /e/group ## --------------------------------------------------------------------- ## STAGE 3: Drop BusyBox, too, now that we're done with its /bin/sh &c ## --------------------------------------------------------------------- FROM scratch AS run COPY --from=bld --chmod=755 /fsl/fossil /bin/ COPY --from=os --chmod=600 /e/* /etc/ COPY --from=os --chmod=1777 /tmp /tmp/ COPY --from=os --chown=fossil:fossil /log /log/ COPY --from=os --chown=fossil:fossil /museum /museum/ ## --------------------------------------------------------------------- ## RUN! ## --------------------------------------------------------------------- ENV PATH "/bin" EXPOSE 8080/tcp USER fossil ENTRYPOINT [ "fossil", "server" ] CMD [ \ "--create", \ "--jsmode", "bundled", \ "--user", "admin", \ "museum/repo.fossil" ] |
Deleted Makefile.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added Makefile.classic.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/make # # This is the top-level makefile for Fossil when the build is occurring # on a unix platform. This works out-of-the-box on most unix platforms. # But you are free to vary some of the definitions if desired. # #### The toplevel directory of the source tree. Fossil can be built # in a directory that is separate from the source tree. Just change # the following to point from the build directory to the src/ folder. # SRCDIR = ./src #### Upstream source files included directly in this repository. # SRCDIR_extsrc = ./extsrc #### In-tree tools such as code generators and translators: # SRCDIR_tools = ./tools #### The directory into which object code files should be written. # # OBJDIR = ./bld #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. This is used # to compile code-generator programs as part of the build process. # See TCC below for the C compiler for building the finished binary. # BCC = gcc BCCFLAGS = $(CFLAGS) #### 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 add support for HTTPS TCC += -DFOSSIL_ENABLE_SSL #### We sometimes add the -static option here so that we can build a # static executable that will run in a chroot jail. #LIB = -static TCC += -DFOSSIL_DYNAMIC_BUILD=1 TCCFLAGS = $(CFLAGS) # We don't attempt to use libedit or libreadline in this simplified # build system (contrast auto.def and Makefile.in) so use the included # copy of linenoise. MinGW can't make use of this, but linenoise is # ifdef'd out elsewhere for that platform. Note that this is a make # flag handled in src/main.mk, not a C preprocessor flag. USE_LINENOISE := 1 #### Extra arguments for linking the finished binary. Fossil needs # to link against the Z-Lib compression library. There are no # other required dependencies. ZLIB_LIB = -lz # If using zlib: LIB += $(ZLIB_LIB) $(LDFLAGS) # If using HTTPS: LIB += -lcrypto -lssl # Many platforms put cos() needed by src/piechart.c in libm, rather than # in libc. We cannot enable this by default because libm doesn't exist # everywhere. #LIB += -lm #### 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 CFLAGS += -fPIE CPPFLAGS += -I. -I$(SRCDIR_extsrc) -I$(SRCDIR) LIB = -lm -lz -lssl INSTALLDIR = $(DESTDIR)$(prefix)/bin SQLITE3_ORIGINAL = 0 USE_LINENOISE = 1 # You should not need to change anything below this line ############################################################################### # # Automatic platform-specific options. HOST_OS_CMD = uname -s HOST_OS = $(HOST_OS_CMD:sh) LIB.SunOS= -lsocket -lnsl LIB += $(LIB.$(HOST_OS)) TCC.DragonFly += -DUSE_PREAD TCC.FreeBSD += -DUSE_PREAD TCC.NetBSD += -DUSE_PREAD TCC.OpenBSD += -DUSE_PREAD TCC += $(TCC.$(HOST_OS)) APPNAME = fossil$(E) .PHONY: all tags include $(SRCDIR)/main.mk |
Added Makefile.in.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/make # # This is the top-level makefile for Fossil when the build is occurring # on a unix platform. This works out-of-the-box on most unix platforms. # But you are free to vary some of the definitions if desired. # #### The toplevel directory of the source tree. Fossil can be built # in a directory that is separate from the source tree. Just change # the following to point from the build directory to the src/ folder. # SRCDIR = @srcdir@/src TOPDIR = @srcdir@ #### Upstream source files included directly in this repository. # SRCDIR_extsrc = @srcdir@/extsrc #### In-tree tools such as code generators and translators: # SRCDIR_tools = @srcdir@/tools #### The directory into which object code files should be written. # Having a "./" prefix in the value of this variable breaks our use of the # "makeheaders" tool when running make on the MinGW platform, apparently # due to some command line argument manipulation performed automatically # by the shell. # # OBJDIR = bld #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. This is used # to compile code-generator programs as part of the build process. # See TCC below for the C compiler for building the finished binary. # BCC = @CC_FOR_BUILD@ #### The suffix to add to final executable file. When cross-compiling # to windows, make this ".exe". Otherwise leave it blank. # E = @EXEEXT@ TCC = @CC@ #### 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@ CFLAGS = @CFLAGS@ CFLAGS_INCLUDE = @CFLAGS_INCLUDE@ LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ BCCFLAGS = @CPPFLAGS@ $(CFLAGS) TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ $(CFLAGS) -DHAVE_AUTOCONFIG_H # # Fuzzing may be enable by appending -fsanitize=fuzzer -DFOSSIL_FUZZ # to the TCCFLAGS variable. # For more thorouth (but also slower) investigation # -fsanitize=fuzzer,undefined,address # might be more useful. INSTALLDIR = $(DESTDIR)@prefix@/bin USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ SQLITE3_SRC.2 = @SQLITE3_SRC.2@ SQLITE3_OBJ.2 = @SQLITE3_OBJ.2@ SQLITE3_SHELL_SRC.2 = @SQLITE3_SHELL_SRC.2@ SQLITE3_ORIGIN = @SQLITE3_ORIGIN@ # SQLITE3_ORIGIN changes... # SQLITE3_SRC: # 0=src/sqlite3.c, 1=src/sqlite3-see.c, 2=$(SQLITE3_SRC.2) # SQLITE3_SHELL_SRC: # 0=src/shell.c, 1=src/shell-see.c, 2=$(SQLITE3_SHELL_SRC.2) USE_LINENOISE = @USE_LINENOISE@ USE_MMAN_H = @USE_MMAN_H@ USE_SEE = @USE_SEE@ APPNAME = fossil # # APPNAME = fossil-fuzz # may be more appropriate for fuzzing. #### Emscripten stuff for optionally doing in-tree builds # of any WASM components. We store precompiled WASM in the the SCM, so # this is only useful for people who actively work on WASM # components. EMSDK_ENV refers to the "environment" script which comes # with the Emscripten SDK package: # https://emscripten.org/docs/getting_started/downloads.html EMSDK_HOME = @EMSDK_HOME@ EMSDK_ENV = @EMSDK_ENV@ EMCC_OPT = @EMCC_OPT@ EMCC_WRAPPER = $(SRCDIR_tools)/emcc.sh # MAKE_COMPILATION_DB (yes/no) determines whether or not the # compile_commands.json file will be generated. MAKE_COMPILATION_DB = @MAKE_COMPILATION_DB@ .PHONY: all tags include $(SRCDIR)/main.mk distclean: clean -rm -f autoconfig.h config.log Makefile -rm -f cscope.out tags reconfig: @AUTOREMAKE@ tags: ctags -R @srcdir@/src @COLLECT_CSCOPE_DATA@ # Automatically reconfigure whenever an autosetup file or one of the # make source files change. # # The "touch" is necessary to avoid a make loop due to a new upstream # feature in autosetup (GH 0a71e3c3b7) which rewrites *.in outputs only # if doing so will write different contents; otherwise, it leaves them # alone so the mtime doesn't change. This means that if you change one # our depdendencies besides Makefile.in, we'll reconfigure but Makefile # won't change, so this rule will remain out of date, so we'll reconfig # but Makefile won't change, so we'll reconfig but... endlessly. # # This is also why we repeat the reconfig target's command here instead # of delegating to it with "$(MAKE) reconfig": having children running # around interfering makes this failure mode even worse. Makefile: @srcdir@/Makefile.in $(SRCDIR)/main.mk @AUTODEPS@ @AUTOREMAKE@ touch @builddir@/Makefile # Container stuff SRCTB := src-@FOSSIL_CI_PFX@.tar.gz IMGVER := fossil:@FOSSIL_CI_PFX@ CNTVER := fossil-@FOSSIL_CI_PFX@ CENGINE := docker container: $(CENGINE) image inspect $(IMGVER) > /dev/null 2>&1 || \ $(MAKE) container-image $(CENGINE) container inspect $(CNTVER) > /dev/null 2>&1 || \ $(CENGINE) create \ --name $(CNTVER) \ --cap-drop AUDIT_WRITE \ --cap-drop CHOWN \ --cap-drop FSETID \ --cap-drop KILL \ --cap-drop MKNOD \ --cap-drop NET_BIND_SERVICE \ --cap-drop NET_RAW \ --cap-drop SETFCAP \ --cap-drop SETPCAP \ --publish 8080:8080 \ $(DCFLAGS) $(IMGVER) $(DCCMD) container-clean: -$(CENGINE) container kill $(CNTVER) -$(CENGINE) container rm $(CNTVER) -$(CENGINE) image rm $(IMGVER) container-image: $(APPNAME) tarball --name src @FOSSIL_CI_PFX@ $(SRCTB) $(CENGINE) buildx build \ --load \ --tag $(IMGVER) \ --build-arg FSLURL=$(SRCTB) \ $(DBFLAGS) @srcdir@ rm -f $(SRCTB) container-run container-start: container $(CENGINE) start $(DSFLAGS) $(CNTVER) @sleep 1 # decrease likelihood of logging race condition $(CENGINE) container logs $(CNTVER) container-stop: $(CENGINE) stop $(CNTVER) container-version: @echo $(CNTVER) |
Added Makefile.osx-jaguar.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/make # # This is a specially modified version of the Makefile that will build # Fossil on Mac OSX Jaguar (10.2) circa 2002. This Makefile is used for # testing on an old PPC iBook. The use of this old platform helps to verify # Fossil and SQLite running on big-endian hardware. # # To build with this Makefile, run: # # make -f Makefile.osx-jaguar # # # This is the top-level makefile for Fossil when the build is occurring # on a unix platform. This works out-of-the-box on most unix platforms. # But you are free to vary some of the definitions if desired. # #### The toplevel directory of the source tree. Fossil can be built # in a directory that is separate from the source tree. Just change # the following to point from the build directory to the src/ folder. # SRCDIR = ./src #### The directory into which object code files should be written. # Having a "./" prefix in the value of this variable breaks our use of the # "makeheaders" tool when running make on the MinGW platform, apparently # due to some command line argument manipulation performed automatically # by the shell. # # OBJDIR = bld #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. This is used # to compile code-generator programs as part of the build process. # See TCC below for the C compiler for building the finished binary. # BCC = cc BCCFLAGS = $(CFLAGS) #### The suffix to add to final executable file. When cross-compiling # to windows, make this ".exe". Otherwise leave it blank. # E = TCC = cc TCCFLAGS = $(CFLAGS) #### 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 = -lz LIB = compat/zlib/libz.a TCC += -g -O0 -DHAVE_AUTOCONFIG_H TCC += -Icompat/zlib TCC += -DSQLITE_WITHOUT_ZONEMALLOC TCC += -D_BSD_SOURCE=1 TCC += -DWITHOUT_ICONV TCC += -Dsocklen_t=int TCC += -DSQLITE_MAX_MMAP_SIZE=0 INSTALLDIR = $(DESTDIR)/usr/local/bin USE_SYSTEM_SQLITE = USE_LINENOISE = 1 # FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@ FOSSIL_ENABLE_TCL = 0 include $(SRCDIR)/main.mk distclean: clean rm -f autoconfig.h config.log Makefile |
Deleted Makefile.w32.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added README.md.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # About Fossil Fossil is a distributed version control system that has been widely used since 2007. Fossil was originally designed to support the [SQLite](https://sqlite.org) project but has been adopted by many other projects as well. Fossil is self-hosting at <https://fossil-scm.org>. If you are reading this on GitHub, then you are looking at a Git mirror of the self-hosting Fossil repository. The purpose of that mirror is to test and exercise Fossil's ability to export a Git mirror. Nobody much uses the GitHub mirror, except to verify that the mirror logic works. If you want to know more about Fossil, visit the official self-hosting site linked above. |
Added VERSION.
> | 1 | 2.24 |
Deleted art/CollRev1.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/CollRev2.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/CollRev3.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/CollRev4.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/branching.odp.
cannot compute difference between binary files
Deleted art/concept1.dia.
cannot compute difference between binary files
Deleted art/concept2.dia.
cannot compute difference between binary files
Deleted art/delta1.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/delta2.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/delta3.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/delta4.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/delta5.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/delta6.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/encode1.tex.
|
| < < |
Deleted art/encode10.dia.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted art/encode2.tex.
|
| < |
Deleted art/encode3.tex.
|
| < |
Deleted art/encode4.tex.
|
| < |
Deleted art/encode5.tex.
|
| < |
Deleted art/encode6.tex.
|
| < |
Deleted art/encode7.tex.
|
| < |
Deleted art/encode8.tex.
|
| < |
Deleted art/encode9.tex.
|
| < |
Added auto.def.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # System autoconfiguration. Try: ./configure --help # This must be above "options" below because it implicitly brings in the # default Autosetup options, things like --prefix. use cc cc-lib options { with-openssl:path|auto|tree|none => {Look for OpenSSL in the given path, automatically, in the source tree, or none} with-zlib:path|auto|tree => {Look for zlib in the given path, automatically, or in the source tree} with-sqlite:path|auto|tree => {Look for sqlite in the given path, automatically, or in the source tree.} with-exec-rel-paths=0 => {Enable relative paths for external diff/gdiff} with-sanitizer: => {Build with C compiler's -fsanitize=LIST; e.g. address,enum,null,undefined} 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} with-mman=0 => {Enable use of POSIX memory APIs from "sys/mman.h"} with-see=0 => {Enable the SQLite Encryption Extension (SEE)} print-minimum-sqlite-version=0 => {print the minimum SQLite version number required, and exit} internal-sqlite=1 => {Don't use the internal SQLite, use the system one} static=0 => {Link a static executable} fusefs=1 => {Disable the Fuse Filesystem} fossil-debug=0 => {Build with fossil debugging enabled} no-opt=0 => {Build without optimization} json=0 => {Build with fossil JSON API enabled} with-emsdk:path => {Directory containing the Emscripten SDK} compile-commands=0 => "Check for compile_commands.json support." } # Update the minimum required SQLite version number here, and also # in src/main.c near the sqlite3_libversion_number() call. Take care # that both places agree! define MINIMUM_SQLITE_VERSION "3.43.0" # This is useful for people wanting Fossil to use an external SQLite library # to compare the one they have against the minimum required if {[opt-bool print-minimum-sqlite-version]} { puts [get-define MINIMUM_SQLITE_VERSION] exit 0 } set outOfTreeBuild 0 if {![file exists fossil.1]} { puts "This appears to be an out-of-tree build." set outOfTreeBuild 1 } # 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] # If we have cscope here, we'll use it in the "tags" target if {[cc-check-progs cscope]} { define COLLECT_CSCOPE_DATA "cscope -bR $::autosetup(srcdir)/src/*.\[ch\]" } else { define COLLECT_CSCOPE_DATA "" } # Find tclsh for the test suite. # # We can't use jimsh for this: the test suite uses features of Tcl that # Jim doesn't support, either statically or due to the way it's built by # autosetup. For example, Jim supports `file normalize`, but only if # you build it with HAVE_REALPATH, which won't ever be defined in this # context because autosetup doesn't try to discover platform-specific # details like that before it decides to build jimsh0. Besides which, # autosetup won't build jimsh0 at all if it can find tclsh itself. # Ironically, this means we may right now be running under either jimsh0 # or a version of tclsh that we find unsuitable below! cc-check-progs tclsh set hbtd /usr/local/Cellar/tcl-tk if {[string equal false [get-define TCLSH]]} { msg-result "WARNING: 'make test' will not run here." } else { set v [exec /bin/sh -c "echo 'puts \$tcl_version' | tclsh"] if {[expr {$v >= 8.6}]} { msg-result "Found Tclsh version $v in the PATH." define TCLSH tclsh } elseif {[file isdirectory $hbtd]} { # This is a macOS system with the Homebrew version of Tcl/Tk # installed. Select the newest version. It won't normally be # in the PATH to avoid shadowing /usr/bin/tclsh, and even if it # were in the PATH, it's bad practice to put /usr/local/bin (the # Homebrew default) ahead of /usr/bin, especially given that # it's user-writeable by default with Homebrew. Thus, we can be # pretty sure the only way to call it is with an absolute path. set v [exec ls -tr $hbtd | tail -1] set path "$hbtd/$v/bin/tclsh" define TCLSH $path msg-result "Using Homebrew Tcl/Tk version $path." } else { msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'." define TCLSH false ;# force "make test" failure via /usr/bin/false } } define EXTRA_CFLAGS "-Wall" define EXTRA_LDFLAGS "" define USE_SYSTEM_SQLITE 0 define USE_LINENOISE 0 define USE_MMAN_H 0 define USE_SEE 0 define SQLITE3_ORIGIN 0 # SQLITE3_ORIGIN 0 = src/sqlite3, 1=src/sqlite3-see.c, 2=client-provided # Maintain the C89/C90-style order of variable declarations before statements. # Check if the compiler supports the respective warning flag. if {[cctest -cflags -Wdeclaration-after-statement]} { define-append EXTRA_CFLAGS -Wdeclaration-after-statement } # This procedure is a customized version of "cc-check-function-in-lib", # that does not modify the LIBS variable. Its use prevents prematurely # pulling in libraries that will be added later anyhow (e.g. "-ldl"). proc check-function-in-lib {function libs {otherlibs {}}} { if {[string length $otherlibs]} { msg-checking "Checking for $function in $libs with $otherlibs..." } else { msg-checking "Checking for $function in $libs..." } set found 0 cc-with [list -libs $otherlibs] { if {[cctest_function $function]} { msg-result "none needed" define lib_$function "" incr found } else { foreach lib $libs { cc-with [list -libs -l$lib] { if {[cctest_function $function]} { msg-result -l$lib define lib_$function -l$lib incr found break } } } } } if {$found} { define [feature-define-name $function] } else { msg-result "no" } return $found } if {![opt-bool internal-sqlite]} { proc find_system_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_stmt_isexplain can be found as well. If we can find open() but # not stmt_isexplain(), then the system SQLite is too old to link against # fossil. # if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} { # Success. Update symbols and return. # define USE_SYSTEM_SQLITE 1 define-append LIBS -lsqlite3 define-append LIBS $extralibs return } } user-error "system sqlite3 not found" } find_system_sqlite proc test_system_sqlite {} { # Check compatibility of the system SQLite library by running the # sqlcompttest.c program in the source tree passes # MINIMUM_SQLITE_VERSION set at the top of this file to # sqlcompttest.c # set cmdline {} lappend cmdline {*}[get-define CCACHE] lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS] lappend cmdline $::autosetup(dir)/../tools/sqlcompattest.c -o conftest__ lappend cmdline {*}[get-define LDFLAGS] lappend cmdline {*}[get-define LIBS] set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]] lappend cmdline {*}[set sqlite-version] set ok 1 set err [catch {exec-with-stderr {*}$cmdline} result errinfo] if {$err} { configlog "Failed: [join $cmdline]" if {[string length $result]>0} {configlog $result} configlog "============" set ok 0 } elseif {$::autosetup(debug)} { configlog "Compiled OK: [join $cmdline]" configlog "============" } if {!$ok} { user-error "unable to compile SQLite compatibility test program" } set err [catch {exec-with-stderr ./conftest__} result errinfo] if {[get-define build] eq [get-define host]} { set err [catch {exec-with-stderr ./conftest__} result errinfo] if {$err} { user-error $result } } file delete ./conftest__ } test_system_sqlite } proc is_mingw {} { return [string match *mingw* [get-define host]] } if {[is_mingw]} { define-append EXTRA_CFLAGS -DBROKEN_MINGW_CMDLINE define-append LIBS -lkernel32 -lws2_32 } else { # # NOTE: All platforms except MinGW should use the linenoise # package. It is currently unsupported on Win32. # define USE_LINENOISE 1 } if {[string match *-solaris* [get-define host]]} { define-append EXTRA_CFLAGS {-D__EXTENSIONS__} } if {[opt-bool fossil-debug]} { define CFLAGS {-g -O0 -Wall} define-append CFLAGS -DFOSSIL_DEBUG msg-result "Debugging support enabled" } if {[opt-bool no-opt]} { define CFLAGS {-g -O0 -Wall} msg-result "Builting without compiler optimization" if {[opt-bool fossil-debug]} { define-append CFLAGS -DFOSSIL_DEBUG } } if {[opt-bool with-mman]} { define-append EXTRA_CFLAGS -DUSE_MMAN_H define USE_MMAN_H 1 msg-result "Enabling \"sys/mman.h\" support" } if {[opt-bool with-see]} { define-append EXTRA_CFLAGS -DUSE_SEE define USE_SEE 1 define SQLITE3_ORIGIN 1 msg-result "Enabling encryption support" } 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-exec-rel-paths]} { define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_EXEC_REL_PATHS define FOSSIL_ENABLE_EXEC_REL_PATHS msg-result "Relative paths in external diff/gdiff 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" } else { define-append EXTRA_CFLAGS -DFOSSIL_DYNAMIC_BUILD=1 define FOSSIL_DYNAMIC_BUILD } # Check for libraries that need to be sorted out early cc-check-function-in-lib iconv iconv # Helper for OpenSSL checking proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} { msg-checking "Checking for $msg..." set rc 0 if {[is_mingw]} { lappend libs -lgdi32 -lwsock32 -lcrypt32 } if {[info exists ::zlib_lib]} { lappend libs $::zlib_lib } msg-quiet cc-with [list -cflags $cflags -libs $libs] { if {[cc-check-includes openssl/ssl.h] && \ [cc-check-functions SSL_new]} { incr rc } } if {!$rc && ![is_mingw]} { # On some systems, OpenSSL appears to require -ldl to link. lappend libs -ldl msg-quiet cc-with [list -cflags $cflags -libs $libs] { if {[cc-check-includes openssl/ssl.h] && \ [cc-check-functions SSL_new]} { incr rc } } } if {$rc} { msg-result "ok" return 1 } else { msg-result "no" return 0 } } # Check for zlib, using the given location if specified set zlibpath [opt-val with-zlib] if {$zlibpath eq "tree"} { set zlibdir [file dirname $autosetup(dir)]/compat/zlib if {![file isdirectory $zlibdir]} { user-error "The zlib in source tree directory does not exist" } elseif { ([llength [glob -nocomplain -directory $zlibdir libz*]] == 0) } { user-error "With --with-zlib=tree, $zlibdir must be configured and built first." } cc-with [list -cflags "-I$zlibdir -L$zlibdir"] define-append EXTRA_CFLAGS -I$zlibdir define-append LIBS $zlibdir/libz.a set ::zlib_lib $zlibdir/libz.a msg-result "Using zlib in source tree" } else { set cftry {""} set ldtry {""} if {$zlibpath ni {auto ""}} { lappend cftry "-I$zlibpath" lappend cftry "-I$zlibpath/include" lappend ldtry "-L$zlibpath" lappend ldtry "-L$zlibpath/lib" } # Reverse the list of tests so we check most-specific to least, else # platform devel files will shadow local --with-zlib overrides. foreach c [lreverse $cftry] { if {[cc-with [list -cflags $c] {cc-check-includes zlib.h}]} { if {$c eq ""} { msg-result "Found zlib.h in default include path" } else { define-append EXTRA_CFLAGS "$c" msg-result "Found zlib.h via $c" } set cfound $c break } } if {![info exists cfound]} { user-error "zlib.h not found; either install it or specify its location via --with-zlib" } foreach lcheck [lreverse $ldtry] { if {[cc-with [list -cflags "$cfound $lcheck"] {check-function-in-lib inflateEnd z}]} { if {$lcheck eq ""} { msg-result "Linked to zlib via default library path" } else { define-append EXTRA_LDFLAGS "$lcheck" msg-result "Linked to zlib via $lcheck" } break } } set ::zlib_lib -lz } set ssldirs [opt-val with-openssl] if {$ssldirs ne "none"} { set found 0 if {$ssldirs eq "tree"} { set ssldir [file dirname $autosetup(dir)]/compat/openssl if {![file isdirectory $ssldir]} { user-error "The OpenSSL in source tree directory does not exist" } set msg "ssl in $ssldir" set cflags "-I$ssldir/include" set ldflags "-L$ssldir" set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread" set found [check-for-openssl "ssl in source tree" "$cflags $ldflags" $ssllibs] } else { if {$ssldirs in {auto ""}} { catch { set cflags [exec pkg-config openssl --cflags-only-I] set ldflags [exec pkg-config openssl --libs-only-L] set found [check-for-openssl "ssl via pkg-config" "$cflags $ldflags"] } msg if {!$found} { set ssldirs "{} /usr/sfw /usr/local/ssl /usr/lib/ssl /usr/ssl \ /usr/pkg /usr/local /usr /usr/local/opt/openssl \ /opt/homebrew/opt/openssl" } } if {!$found} { foreach dir $ssldirs { if {$dir eq ""} { set msg "system ssl" set cflags "" set ldflags "" } else { set msg "ssl in $dir" set cflags "-I$dir/include" set ldflags "-L$dir/lib" } if {[check-for-openssl $msg "$cflags $ldflags"]} { incr found break } } } } if {$found} { define FOSSIL_ENABLE_SSL define-append EXTRA_CFLAGS $cflags define-append EXTRA_LDFLAGS $ldflags define-append CFLAGS $cflags define-append LDFLAGS $ldflags if {[info exists ssllibs]} { define-append LIBS $ssllibs } else { define-append LIBS -lssl -lcrypto } if {[info exists ::zlib_lib]} { define-append LIBS $::zlib_lib } if {[is_mingw]} { define-append LIBS -lgdi32 -lwsock32 -lcrypt32 } msg-result "HTTPS support enabled" # Silence OpenSSL deprecation warnings on Mac OS X 10.7. if {[string match *-darwin* [get-define host]]} { if {[cctest -cflags {-Wdeprecated-declarations}]} { define-append EXTRA_CFLAGS -Wdeprecated-declarations } } } else { user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support" } } else { if {[info exists ::zlib_lib]} { define-append LIBS $::zlib_lib } } ######################################################################## # --with-sqlite=PATH checks for the first it finds of the following... # - PATH/sqlite3.c and PATH/sqlite3.h # - PATH/sqlite3.o (and assumes sqlite3.h is with it) # - PATH/lib/libsqlite3* and PATH/include/sqlite3.h define CFLAGS_INCLUDE {} # ^^^ CFLAGS_INCLUDE is ONLY for -I... flags and their order is # significant so that --with-sqlite=PATH's header can shadow our # own. One caveat with this is that we cannot point --with-sqlite=PATH # to the root of sqlite3's own build tree because that dir has a # config.h which ends up shadowing src/config.h, breaking our build. set sq3path [opt-val with-sqlite] define SQLITE3_SRC.2 {} define SQLITE3_OBJ.2 {} define SQLITE3_SHELL_SRC.2 {$(SQLITE3_SHELL_SRC.0)} if {$sq3path in {tree ""}} { msg-result "Using sqlite3.c from this source tree." } else { # SQLITE3_ORIGIN: # 0 = local source tree # 1 = use external lib or sqlite3.o # 2 = use external sqlite3.c and (if found) shell.c define USE_SYSTEM_SQLITE 1 define SQLITE3_ORIGIN 2 if {$sq3path != "auto"} { if {([file exists $sq3path/sqlite3.c]) && \ ([file exists $sq3path/sqlite3.h]) } { # Prefer sqlite3.[ch] if found. define SQLITE3_SRC.2 $sq3path/sqlite3.c define SQLITE3_OBJ.2 {$(SQLITE3_OBJ.0)} define USE_SYSTEM_SQLITE 2 define SQLITE3_ORIGIN 2 if {[file exists $sq3path/shell.c]} { define SQLITE3_SHELL_SRC.2 $sq3path/shell.c } define-append CFLAGS_INCLUDE -I$sq3path define-append EXTRA_LDFLAGS -lpthread # ^^^ additional -lXXX flags are conservative estimates msg-result "Using sqlite3.c and sqlite3.h from $sq3path" } elseif {[file exists $sq3path/sqlite3.o]} { # Use sqlite3.o if found. define SQLITE3_OBJ.2 $sq3path/sqlite3.o define-append CFLAGS_INCLUDE -I$sq3path define-append EXTRA_LDFLAGS $sq3path/sqlite3.o -lpthread # ^^^ additional -lXXX flags are conservative estimates msg-result "Using sqlite3.o from $sq3path" } elseif { ([llength [glob -nocomplain -directory $sq3path/lib libsqlite3*]] != 0) \ && ([file exists $sq3path/include/sqlite3.h]) } { # e.g. --with-sqlite=/usr/local. Try $sq3path/lib/libsqlite3* # and $sq3path/include/sqlite3.h define-append CFLAGS_INCLUDE -I$sq3path/include define-append EXTRA_LDFLAGS -L$sq3path/lib -lsqlite3 -lpthread # ^^^ additional -lXXX flags are conservative estimates msg-result "Using -lsqlite3 from $sq3path" } else { # Assume $sq3path holds both the lib and header cc-with [list -cflags "-I$sq3path -L$sq3path"] define-append CFLAGS_INCLUDE -I$sq3path define-append EXTRA_LDFLAGS -L$sq3path -lsqlite3 -lpthread # ^^^ additional -lXXX flags are conservative estimates msg-result "Using -lsqlite3 from $sq3path" } } elseif {![cc-check-includes sqlite3.h] || ![check-function-in-lib sqlite3_open_v2 sqlite3]} { user-error "libsqlite3 not found please install it or specify the location with --with-sqlite" } } define-append CFLAGS_INCLUDE {-I. -I$(SRCDIR) -I$(SRCDIR_extsrc)} 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"} { set tcldir [file dirname $autosetup(dir)]/compat/tcl-8.6 if {$tclprivatestubs} { set tclconfig(TCL_INCLUDE_SPEC) -I$tcldir/generic set tclconfig(TCL_VERSION) {Private Stubs} set tclconfig(TCL_PATCH_LEVEL) {} set tclconfig(TCL_PREFIX) $tcldir set tclconfig(TCL_LD_FLAGS) { } } else { # Use the system Tcl. Look in some likely places. array set tclconfig [parse-tclconfig-sh \ $tcldir/unix $tcldir/win \ /usr /usr/local /usr/share /opt/local] set msg "on your system" } } else { array set tclconfig [parse-tclconfig-sh $tclpath] set msg "at $tclpath" } if {[opt-bool static]} { set tclconfig(TCL_LD_FLAGS) { } } if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} { user-error "Cannot find Tcl $msg" } set tclstubs [opt-bool with-tcl-stubs] if {$tclprivatestubs} { define FOSSIL_ENABLE_TCL_PRIVATE_STUBS define USE_TCL_STUBS } elseif {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} { set libs "$tclconfig(TCL_STUB_LIB_SPEC)" define FOSSIL_ENABLE_TCL_STUBS define USE_TCL_STUBS } else { set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)" } set cflags $tclconfig(TCL_INCLUDE_SPEC) if {!$tclprivatestubs} { set foundtcl 0; # Did we find a working Tcl library? cc-with [list -cflags $cflags -libs $libs] { if {$tclstubs} { if {[cc-check-functions Tcl_InitStubs]} { set foundtcl 1 } } else { if {[cc-check-functions Tcl_CreateInterp]} { set foundtcl 1 } } } if {!$foundtcl && [string match *-lieee* $libs]} { # On some systems, using "-lieee" from TCL_LIB_SPEC appears # to cause issues. msg-result "Removing \"-lieee\" and retrying for Tcl..." set libs [string map [list -lieee ""] $libs] cc-with [list -cflags $cflags -libs $libs] { if {$tclstubs} { if {[cc-check-functions Tcl_InitStubs]} { set foundtcl 1 } } else { if {[cc-check-functions Tcl_CreateInterp]} { set foundtcl 1 } } } } if {!$foundtcl && ![string match *-lpthread* $libs]} { # On some systems, TCL_LIB_SPEC appears to be missing # "-lpthread". Try adding it. msg-result "Adding \"-lpthread\" and retrying for Tcl..." set libs "$libs -lpthread" cc-with [list -cflags $cflags -libs $libs] { if {$tclstubs} { if {[cc-check-functions Tcl_InitStubs]} { set foundtcl 1 } } else { if {[cc-check-functions Tcl_CreateInterp]} { set foundtcl 1 } } } } if {!$foundtcl} { if {$tclstubs} { user-error "Cannot find a usable Tcl stubs library $msg" } else { user-error "Cannot find a usable Tcl library $msg" } } } set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL) msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)" if {!$tclprivatestubs} { define-append LIBS $libs } define-append EXTRA_CFLAGS $cflags define-append CFLAGS $cflags if {[info exists zlibpath] && $zlibpath eq "tree"} { # # NOTE: When using zlib in the source tree, prevent Tcl from # pulling in the system one. # set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \ $tclconfig(TCL_LD_FLAGS)] } # # NOTE: Remove "-ldl" from the TCL_LD_FLAGS because it will be # be checked for near the bottom of this file. # set tclconfig(TCL_LD_FLAGS) [string map [list -ldl ""] \ $tclconfig(TCL_LD_FLAGS)] define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS) define FOSSIL_ENABLE_TCL } # Emscripten is a purely optional component used only for doing # in-tree builds of WASM stuff, as opposed to WASM binaries we import # from other places. This is only set up for Unix-style OSes and is # untested anywhere but Linux. set emsdkHome [opt-val with-emsdk] define EMSDK_HOME "" define EMSDK_ENV "" define EMCC_OPT "-Oz" if {$emsdkHome eq "" && [info exists ::env(EMSDK)]} { # Fall back to checking the environment. $EMSDK gets set # by sourcing emsdk_env.sh. set emsdkHome $::env(EMSDK) } if {$emsdkHome ne ""} { define EMSDK_HOME $emsdkHome set emsdkEnv "$emsdkHome/emsdk_env.sh" if {[file exists $emsdkEnv]} { puts "Using Emscripten SDK environment from $emsdkEnv." define EMSDK_ENV $emsdkEnv if {[info exists ::env(EMCC_OPT)]} { define EMCC_OPT $::env(EMCC_OPT) } } else { puts "emsdk_env.sh not found. Assuming emcc is in the PATH." } } # 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 {[is_mingw]} { define-append LIBS -lwsock32 } } # The SMTP module requires special libraries and headers for MX DNS # record lookups and such. cc-check-includes arpa/nameser.h cc-include-needs bind/resolv.h netinet/in.h cc-check-includes bind/resolv.h cc-check-includes resolv.h if { !(([cc-check-function-in-lib dn_expand resolv] || [cc-check-function-in-lib ns_name_uncompress {bind resolv}] || [cc-check-function-in-lib __ns_name_uncompress {bind resolv}]) && ([cc-check-function-in-lib ns_parserr {bind resolv}] || [cc-check-function-in-lib __ns_parserr {bind resolv}]) && ([cc-check-function-in-lib res_query {bind resolv}] || [cc-check-function-in-lib __res_query {bind resolv}]))} { msg-result "WARNING: SMTP feature will not be able to look up local MX." } cc-check-function-in-lib res_9_ns_initparse resolv # Other nonstandard function checks cc-check-functions utime cc-check-functions usleep cc-check-functions strchrnul cc-check-functions pledge cc-check-functions backtrace # Termux on Android adds "getpass(char *)" to unistd.h, so check this so we # guard against including it again; use cctest as cc-check-functions and # cctest_function check for "getpass()" with no args and fail if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} { define FOSSIL_HAVE_GETPASS 1 msg-result "Found getpass() with unistd.h" } # 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 sin m # Check for the FuseFS library if {[opt-bool fusefs]} { if {[opt-bool static]} { msg-result "FuseFS support disabled due to -static" } elseif {[cc-check-function-in-lib fuse_mount fuse]} { define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS define FOSSIL_HAVE_FUSEFS 1 define-append LIBS -lfuse msg-result "FuseFS support enabled" } } ######################################################################## # Checks the compiler for compile_commands.json support. # # Returns 1 if supported, else 0. Defines MAKE_COMPILATION_DB to "yes" # if supported, "no" if not. proc check-compile-commands {} { msg-checking "compile_commands.json support... " if {[cctest -lang c -cflags {/dev/null -MJ} -source {}]} { # This test reportedly incorrectly succeeds on one of # Martin G.'s older systems. msg-result "compiler supports compile_commands.json" define MAKE_COMPILATION_DB yes return 1 } else { msg-result "compiler does not support compile_commands.json" define MAKE_COMPILATION_DB no return 0 } } define MAKE_COMPILATION_DB no if {!$outOfTreeBuild} { if {[opt-bool compile-commands]} { check-compile-commands } else { puts "Use --compile-commands to enable check for compile-commands-capable compiler." } } else { puts "Disabling compile_commands.json check for out-of-tree build." # This is an attempt to resolve the problem reported at # https://fossil-scm.org/forum/forumpost/d19061d09a8179d0 } # Add -fsanitize compile and link options late: we don't want the C # checks above to run with those sanitizers enabled. It can not only # be pointless, it can actually break correct tests. set fsan [opt-val with-sanitizer] if {[string length $fsan]} { define-append EXTRA_CFLAGS -fsanitize=$fsan define-append EXTRA_LDFLAGS -fsanitize=$fsan if {[string first "undefined" $fsan] != -1} { # We need to link with libubsan if we're compiling under # GCC with -fsanitize=undefined. cc-check-function-in-lib __ubsan_handle_add_overflow ubsan } } # Finally, append libraries that must be last. This matters more on some # OSes than others, but is most broadly required for static linking. if {[check-function-in-lib dlopen dl]} { # Some platforms (*BSD) have the dl functions already in libc and no libdl. # In such case we can link directly without -ldl. define-append LIBS [get-define lib_dlopen] } if {[opt-bool static]} { # Linux can only infer the dependency on pthread from OpenSSL when # doing dynamic linkage. define-append LIBS -lpthread } if {[get-define EMSDK_HOME] ne ""} { define EMCC_WRAPPER $::autosetup(dir)/../tools/emcc.sh make-template tools/emcc.sh.in catch {exec chmod u+x tools/emcc.sh} } else { define EMCC_WRAPPER "" catch {exec rm -f tools/emcc.sh} } # Tag container builds with a prefix of the checkin ID of the version # of Fossil each one contains. This not only allows multiple images # to coexist and multiple containers to be created unamgiguosly from # them, it also changes the URL we fetch the source tarball from, so # repeated builds of a given version generate and fetch the source # tarball once only, keeping it in the local Docker/Podman cache. set ci [readfile "$::autosetup(srcdir)/manifest.uuid"] define FOSSIL_CI_PFX [string range $ci 0 11] make-template Makefile.in make-config-header autoconfig.h -auto {USE_* FOSSIL_*} |
Added autosetup/LICENSE.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | Unless explicitly stated, all files which form part of autosetup are released under the following license: --------------------------------------------------------------------- autosetup - A build environment "autoconfigurator" Copyright (c) 2010-2011, WorkWare Systems <http://workware.net.au/> 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 WORKWARE SYSTEMS ``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 WORKWARE SYSTEMS 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 should not be interpreted as representing official policies, either expressed or implied, of WorkWare Systems. |
Added autosetup/README.autosetup.
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | README.autosetup created by autosetup v0.6.9 This is the autosetup directory for a local install of autosetup. It contains autosetup, support files and loadable modules. *.tcl files in this directory are optional modules which can be loaded with the 'use' directive. *.auto files in this directory are auto-loaded. For more information, see http://msteveb.github.com/autosetup/ |
Added autosetup/autosetup.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/bin/sh # Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # vim:se syntax=tcl: # \ dir=`dirname "$0"`; exec "`$dir/autosetup-find-tclsh`" "$0" "$@" # Note that the version has a trailing + on unreleased versions set autosetup(version) 0.6.9 # Can be set to 1 to debug early-init problems set autosetup(debug) [expr {"--debug" in $argv}] ################################################################## # # Main flow of control, option handling # proc main {argv} { global autosetup define # There are 3 potential directories involved: # 1. The directory containing autosetup (this script) # 2. The directory containing auto.def # 3. The current directory # From this we need to determine: # a. The path to this script (and related support files) # b. The path to auto.def # c. The build directory, where output files are created # This is also complicated by the fact that autosetup may # have been run via the configure wrapper ([getenv WRAPPER] is set) # Here are the rules. # a. This script is $::argv0 # => dir, prog, exe, libdir # b. auto.def is in the directory containing the configure wrapper, # otherwise it is in the current directory. # => srcdir, autodef # c. The build directory is the current directory # => builddir, [pwd] # 'misc' is needed before we can do anything, so set a temporary libdir # in case this is the development version set autosetup(libdir) [file dirname $::argv0]/lib use misc # (a) set autosetup(dir) [realdir [file dirname [realpath $::argv0]]] set autosetup(prog) [file join $autosetup(dir) [file tail $::argv0]] set autosetup(exe) [getenv WRAPPER $autosetup(prog)] if {$autosetup(installed)} { set autosetup(libdir) $autosetup(dir) } else { set autosetup(libdir) [file join $autosetup(dir) lib] } autosetup_add_dep $autosetup(prog) # (b) if {[getenv WRAPPER ""] eq ""} { # Invoked directly set autosetup(srcdir) [pwd] } else { # Invoked via the configure wrapper set autosetup(srcdir) [file-normalize [file dirname $autosetup(exe)]] } set autosetup(autodef) [relative-path $autosetup(srcdir)/auto.def] # (c) set autosetup(builddir) [pwd] set autosetup(argv) $argv set autosetup(cmdline) {} # options is a list of known options set autosetup(options) {} # optset is a dictionary of option values set by the user based on getopt set autosetup(optset) {} # optdefault is a dictionary of default values set autosetup(optdefault) {} # options-defaults is a dictionary of overrides for default values for options set autosetup(options-defaults) {} set autosetup(optionhelp) {} set autosetup(showhelp) 0 use util # Parse options use getopt # At the is point we don't know what is a valid option # We simply parse anything that looks like an option set autosetup(getopt) [getopt argv] #"=Core Options:" options-add { help:=local => "display help and options. Optionally specify a module name, such as --help=system" licence license => "display the autosetup license" version => "display the version of autosetup" ref:=text manual:=text reference:=text => "display the autosetup command reference. 'text', 'wiki', 'asciidoc' or 'markdown'" debug => "display debugging output as autosetup runs" install:=. => "install autosetup to the current or given directory" } if {$autosetup(installed)} { # hidden options so we can produce a nice error options-add { sysinstall:path } } else { options-add { sysinstall:path => "install standalone autosetup to the given directory (e.g.: /usr/local)" } } options-add { force init:=help => "create initial auto.def, etc. Use --init=help for known types" # Undocumented options option-checking=1 nopager quiet timing conf: } if {[opt-bool version]} { puts $autosetup(version) exit 0 } # autosetup --conf=alternate-auto.def if {[opt-str conf o]} { set autosetup(autodef) $o } # Debugging output (set this early) incr autosetup(debug) [opt-bool debug] incr autosetup(force) [opt-bool force] incr autosetup(msg-quiet) [opt-bool quiet] incr autosetup(msg-timing) [opt-bool timing] # If the local module exists, source it now to allow for # project-local customisations if {[file exists $autosetup(libdir)/local.tcl]} { use local } # Now any auto-load modules autosetup_load_auto_modules if {[opt-str help o]} { incr autosetup(showhelp) use help autosetup_help $o } if {[opt-bool licence license]} { use help autosetup_show_license exit 0 } if {[opt-str {manual ref reference} o]} { use help autosetup_reference $o } # Allow combining --install and --init set earlyexit 0 if {[opt-str install o]} { use install autosetup_install $o incr earlyexit } if {[opt-str init o]} { use init autosetup_init $o incr earlyexit } if {$earlyexit} { exit 0 } if {[opt-str sysinstall o]} { use install autosetup_install $o 1 exit 0 } if {![file exists $autosetup(autodef)]} { # Check for invalid option first options {} user-error "No auto.def found in \"$autosetup(srcdir)\" (use [file tail $::autosetup(exe)] --init to create one)" } # Parse extra arguments into autosetup(cmdline) foreach arg $argv { if {[regexp {([^=]*)=(.*)} $arg -> n v]} { dict set autosetup(cmdline) $n $v define $n $v } else { user-error "Unexpected parameter: $arg" } } autosetup_add_dep $autosetup(autodef) define CONFIGURE_OPTS "" foreach arg $autosetup(argv) { define-append CONFIGURE_OPTS [quote-if-needed $arg] } define AUTOREMAKE [file-normalize $autosetup(exe)] define-append AUTOREMAKE [get-define CONFIGURE_OPTS] # Log how we were invoked configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]" configlog "Tclsh: [info nameofexecutable]" # Note that auto.def is *not* loaded in the global scope source $autosetup(autodef) # Could warn here if options {} was not specified show-notices if {$autosetup(debug)} { msg-result "Writing all defines to config.log" configlog "================ defines ======================" foreach n [lsort [array names define]] { configlog "define $n $define($n)" } } exit 0 } # @opt-bool ?-nodefault? option ... # # Check each of the named, boolean options and if any have been explicitly enabled # or disabled by the user, return 1 or 0 accordingly. # # If the option was specified more than once, the last value wins. # e.g. With '--enable-foo --disable-foo', '[opt-bool foo]' will return 0 # # If no value was specified by the user, returns the default value for the # first option. If '-nodefault' is given, this behaviour changes and # -1 is returned instead. # proc opt-bool {args} { set nodefault 0 if {[lindex $args 0] eq "-nodefault"} { set nodefault 1 set args [lrange $args 1 end] } option-check-names {*}$args foreach opt $args { if {[dict exists $::autosetup(optset) $opt]} { return [dict get $::autosetup(optset) $opt] } } if {$nodefault} { return -1 } # Default value is the default for the first option return [dict get $::autosetup(optdefault) [lindex $args 0]] } # @opt-val optionlist ?default=""? # # Returns a list containing all the values given for the non-boolean options in '$optionlist'. # There will be one entry in the list for each option given by the user, including if the # same option was used multiple times. # # If no options were set, '$default' is returned (exactly, not as a list). # # Note: For most use cases, 'opt-str' should be preferred. # proc opt-val {names {default ""}} { option-check-names {*}$names foreach opt $names { if {[dict exists $::autosetup(optset) $opt]} { lappend result {*}[dict get $::autosetup(optset) $opt] } } if {[info exists result]} { return $result } return $default } # @opt-str optionlist varname ?default? # # Sets '$varname' in the callers scope to the value for one of the given options. # # For the list of options given in '$optionlist', if any value is set for any option, # the option value is taken to be the *last* value of the last option (in the order given). # # If no option was given, and a default was specified with 'options-defaults', # that value is used. # # If no 'options-defaults' value was given and '$default' was given, it is used. # # If none of the above provided a value, no value is set. # # The return value depends on whether '$default' was specified. # If it was, the option value is returned. # If it was not, 1 is returns if a value was set, or 0 if not. # # Typical usage is as follows: # ## if {[opt-str {myopt altname} o]} { ## do something with $o ## } # # Or: ## define myname [opt-str {myopt altname} o "/usr/local"] # proc opt-str {names varname args} { global autosetup option-check-names {*}$names upvar $varname value if {[llength $args]} { # A default was given, so always return the string value of the option set default [lindex $args 0] set retopt 1 } else { # No default, so return 0 or 1 to indicate if a value was found set retopt 0 } foreach opt $names { if {[dict exists $::autosetup(optset) $opt]} { set result [lindex [dict get $::autosetup(optset) $opt] end] } } if {![info exists result]} { # No user-specified value. Has options-defaults been set? foreach opt $names { if {[dict exists $::autosetup(options-defaults) $opt]} { set result [dict get $autosetup(options-defaults) $opt] } } } if {[info exists result]} { set value $result if {$retopt} { return $value } return 1 } if {$retopt} { set value $default return $value } return 0 } proc option-check-names {args} { foreach o $args { if {$o ni $::autosetup(options)} { autosetup-error "Request for undeclared option --$o" } } } # Parse the option definition in $opts and update # ::autosetup(setoptions) and ::autosetup(optionhelp) appropriately # proc options-add {opts {header ""}} { global autosetup # First weed out comment lines set realopts {} foreach line [split $opts \n] { if {![string match "#*" [string trimleft $line]]} { append realopts $line \n } } set opts $realopts for {set i 0} {$i < [llength $opts]} {incr i} { set opt [lindex $opts $i] if {[string match =* $opt]} { # This is a special heading lappend autosetup(optionhelp) $opt "" set header {} continue } unset -nocomplain defaultvalue equal value #puts "i=$i, opt=$opt" regexp {^([^:=]*)(:)?(=)?(.*)$} $opt -> name colon equal value if {$name in $autosetup(options)} { autosetup-error "Option $name already specified" } #puts "$opt => $name $colon $equal $value" # Find the corresponding value in the user options # and set the default if necessary if {[string match "-*" $opt]} { # This is a documentation-only option, like "-C <dir>" set opthelp $opt } elseif {$colon eq ""} { # Boolean option lappend autosetup(options) $name # Check for override if {[dict exists $autosetup(options-defaults) $name]} { # A default was specified with options-defaults, so use it set value [dict get $autosetup(options-defaults) $name] } if {$value eq "1"} { set opthelp "--disable-$name" } else { set opthelp "--$name" } # Set the default if {$value eq ""} { set value 0 } set defaultvalue $value dict set autosetup(optdefault) $name $defaultvalue if {[dict exists $autosetup(getopt) $name]} { # The option was specified by the user. Look at the last value. lassign [lindex [dict get $autosetup(getopt) $name] end] type setvalue if {$type eq "str"} { # Can we convert the value to a boolean? if {$setvalue in {1 enabled yes}} { set setvalue 1 } elseif {$setvalue in {0 disabled no}} { set setvalue 0 } else { user-error "Boolean option $name given as --$name=$setvalue" } } dict set autosetup(optset) $name $setvalue #puts "Found boolean option --$name=$setvalue" } } else { # String option. lappend autosetup(options) $name if {$colon eq ":"} { # Was ":name=default" given? # If so, set $value to the display name and $defaultvalue to the default # (This is the preferred way to set a default value for a string option) if {[regexp {^([^=]+)=(.*)$} $value -> value defaultvalue]} { dict set autosetup(optdefault) $name $defaultvalue } } # Maybe override the default value if {[dict exists $autosetup(options-defaults) $name]} { # A default was specified with options-defaults, so use it set defaultvalue [dict get $autosetup(options-defaults) $name] dict set autosetup(optdefault) $name $defaultvalue } elseif {![info exists defaultvalue]} { # For backward compatibility, if ":name" was given, use name as both # the display text and the default value, but only if the user # specified the option without the value set defaultvalue $value } if {$equal eq "="} { # String option with optional value set opthelp "--$name?=$value?" } else { # String option with required value set opthelp "--$name=$value" } # Get the values specified by the user if {[dict exists $autosetup(getopt) $name]} { set listvalue {} foreach pair [dict get $autosetup(getopt) $name] { lassign $pair type setvalue if {$type eq "bool" && $setvalue} { if {$equal ne "="} { user-error "Option --$name requires a value" } # If given as a boolean, use the default value set setvalue $defaultvalue } lappend listvalue $setvalue } #puts "Found string option --$name=$listvalue" dict set autosetup(optset) $name $listvalue } } # Now create the help for this option if appropriate if {[lindex $opts $i+1] eq "=>"} { set desc [lindex $opts $i+2] if {[info exists defaultvalue]} { set desc [string map [list @default@ $defaultvalue] $desc] } #string match \n* $desc if {$header ne ""} { lappend autosetup(optionhelp) $header "" set header "" } # A multi-line description lappend autosetup(optionhelp) $opthelp $desc incr i 2 } } } # @module-options optionlist # # Like 'options', but used within a module. proc module-options {opts} { set header "" if {$::autosetup(showhelp) > 1 && [llength $opts]} { set header "Module Options:" } options-add $opts $header if {$::autosetup(showhelp)} { # Ensure that the module isn't executed on --help # We are running under eval or source, so use break # to prevent further execution #return -code break -level 2 return -code break } } proc max {a b} { expr {$a > $b ? $a : $b} } proc options-wrap-desc {text length firstprefix nextprefix initial} { set len $initial set space $firstprefix foreach word [split $text] { set word [string trim $word] if {$word == ""} { continue } if {$len && [string length $space$word] + $len >= $length} { puts "" set len 0 set space $nextprefix } incr len [string length $space$word] puts -nonewline $space$word set space " " } if {$len} { puts "" } } proc options-show {} { # Determine the max option width set max 0 foreach {opt desc} $::autosetup(optionhelp) { if {[string match =* $opt] || [string match \n* $desc]} { continue } set max [max $max [string length $opt]] } set indent [string repeat " " [expr $max+4]] set cols [getenv COLUMNS 80] catch { lassign [exec stty size] rows cols } incr cols -1 # Now output foreach {opt desc} $::autosetup(optionhelp) { if {[string match =* $opt]} { puts [string range $opt 1 end] continue } puts -nonewline " [format %-${max}s $opt]" if {[string match \n* $desc]} { puts $desc } else { options-wrap-desc [string trim $desc] $cols " " $indent [expr $max + 2] } } } # @options optionspec # # Specifies configuration-time options which may be selected by the user # and checked with 'opt-str' and 'opt-bool'. '$optionspec' contains a series # of options specifications separated by newlines, as follows: # # A boolean option is of the form: # ## name[=0|1] => "Description of this boolean option" # # The default is 'name=0', meaning that the option is disabled by default. # If 'name=1' is used to make the option enabled by default, the description should reflect # that with text like "Disable support for ...". # # An argument option (one which takes a parameter) is of the form: # ## name:[=]value => "Description of this option" # # If the 'name:value' form is used, the value must be provided with the option (as '--name=myvalue'). # If the 'name:=value' form is used, the value is optional and the given value is used as the default # if it is not provided. # # The description may contain '@default@', in which case it will be replaced with the default # value for the option (taking into account defaults specified with 'options-defaults'. # # Undocumented options are also supported by omitting the '=> description'. # These options are not displayed with '--help' and can be useful for internal options or as aliases. # # For example, '--disable-lfs' is an alias for '--disable=largefile': # ## lfs=1 largefile=1 => "Disable large file support" # proc options {optlist} { # Allow options as a list or args options-add $optlist "Local Options:" if {$::autosetup(showhelp)} { options-show exit 0 } # Check for invalid options if {[opt-bool option-checking]} { foreach o [dict keys $::autosetup(getopt)] { if {$o ni $::autosetup(options)} { user-error "Unknown option --$o" } } } } # @options-defaults dictionary # # Specifies a dictionary of options and a new default value for each of those options. # Use before any 'use' statements in 'auto.def' to change the defaults for # subsequently included modules. proc options-defaults {dict} { foreach {n v} $dict { dict set ::autosetup(options-defaults) $n $v } } proc config_guess {} { if {[file-isexec $::autosetup(dir)/autosetup-config.guess]} { if {[catch {exec-with-stderr sh $::autosetup(dir)/autosetup-config.guess} alias]} { user-error $alias } return $alias } else { configlog "No autosetup-config.guess, so using uname" string tolower [exec uname -p]-unknown-[exec uname -s][exec uname -r] } } proc config_sub {alias} { if {[file-isexec $::autosetup(dir)/autosetup-config.sub]} { if {[catch {exec-with-stderr sh $::autosetup(dir)/autosetup-config.sub $alias} alias]} { user-error $alias } } return $alias } # @define name ?value=1? # # Defines the named variable to the given value. # These (name, value) pairs represent the results of the configuration check # and are available to be subsequently checked, modified and substituted. # proc define {name {value 1}} { set ::define($name) $value #dputs "$name <= $value" } # @undefine name # # Undefine the named variable. # proc undefine {name} { unset -nocomplain ::define($name) #dputs "$name <= <undef>" } # @define-append name value ... # # Appends the given value(s) to the given "defined" variable. # If the variable is not defined or empty, it is set to '$value'. # Otherwise the value is appended, separated by a space. # Any extra values are similarly appended. # If any value is already contained in the variable (as a substring) it is omitted. # proc define-append {name args} { if {[get-define $name ""] ne ""} { # Avoid duplicates foreach arg $args { if {$arg eq ""} { continue } set found 0 foreach str [split $::define($name) " "] { if {$str eq $arg} { incr found } } if {!$found} { append ::define($name) " " $arg } } } else { set ::define($name) [join $args] } #dputs "$name += [join $args] => $::define($name)" } # @get-define name ?default=0? # # Returns the current value of the "defined" variable, or '$default' # if not set. # proc get-define {name {default 0}} { if {[info exists ::define($name)]} { #dputs "$name => $::define($name)" return $::define($name) } #dputs "$name => $default" return $default } # @is-defined name # # Returns 1 if the given variable is defined. # proc is-defined {name} { info exists ::define($name) } # @is-define-set name # # Returns 1 if the given variable is defined and is set # to a value other than "" or 0 # proc is-define-set {name} { if {[get-define $name] in {0 ""}} { return 0 } return 1 } # @all-defines # # Returns a dictionary (name, value list) of all defined variables. # # This is suitable for use with 'dict', 'array set' or 'foreach' # and allows for arbitrary processing of the defined variables. # proc all-defines {} { array get ::define } # @get-env name default # # If '$name' was specified on the command line, return it. # Otherwise if '$name' was set in the environment, return it. # Otherwise return '$default'. # proc get-env {name default} { if {[dict exists $::autosetup(cmdline) $name]} { return [dict get $::autosetup(cmdline) $name] } getenv $name $default } # @env-is-set name # # Returns 1 if '$name' was specified on the command line or in the environment. # Note that an empty environment variable is not considered to be set. # proc env-is-set {name} { if {[dict exists $::autosetup(cmdline) $name]} { return 1 } if {[getenv $name ""] ne ""} { return 1 } return 0 } # @readfile filename ?default=""? # # Return the contents of the file, without the trailing newline. # If the file doesn't exist or can't be read, returns '$default'. # proc readfile {filename {default_value ""}} { set result $default_value catch { set f [open $filename] set result [read -nonewline $f] close $f } return $result } # @writefile filename value # # Creates the given file containing '$value'. # Does not add an extra newline. # proc writefile {filename value} { set f [open $filename w] puts -nonewline $f $value close $f } proc quote-if-needed {str} { if {[string match {*[\" ]*} $str]} { return \"[string map [list \" \\" \\ \\\\] $str]\" } return $str } proc quote-argv {argv} { set args {} foreach arg $argv { lappend args [quote-if-needed $arg] } join $args } # @list-non-empty list # # Returns a copy of the given list with empty elements removed proc list-non-empty {list} { set result {} foreach p $list { if {$p ne ""} { lappend result $p } } return $result } # @find-executable-path name # # Searches the path for an executable with the given name. # Note that the name may include some parameters, e.g. 'cc -mbig-endian', # in which case the parameters are ignored. # The full path to the executable if found, or "" if not found. # Returns 1 if found, or 0 if not. # proc find-executable-path {name} { # Ignore any parameters set name [lindex $name 0] # The empty string is never a valid executable if {$name ne ""} { foreach p [split-path] { dputs "Looking for $name in $p" set exec [file join $p $name] if {[file-isexec $exec]} { dputs "Found $name -> $exec" return $exec } } } return {} } # @find-executable name # # Searches the path for an executable with the given name. # Note that the name may include some parameters, e.g. 'cc -mbig-endian', # in which case the parameters are ignored. # Returns 1 if found, or 0 if not. # proc find-executable {name} { if {[find-executable-path $name] eq {}} { return 0 } return 1 } # @find-an-executable ?-required? name ... # # Given a list of possible executable names, # searches for one of these on the path. # # Returns the name found, or "" if none found. # If the first parameter is '-required', an error is generated # if no executable is found. # proc find-an-executable {args} { set required 0 if {[lindex $args 0] eq "-required"} { set args [lrange $args 1 end] incr required } foreach name $args { if {[find-executable $name]} { return $name } } if {$required} { if {[llength $args] == 1} { user-error "failed to find: [join $args]" } else { user-error "failed to find one of: [join $args]" } } return "" } # @configlog msg # # Writes the given message to the configuration log, 'config.log'. # proc configlog {msg} { if {![info exists ::autosetup(logfh)]} { set ::autosetup(logfh) [open config.log w] } puts $::autosetup(logfh) $msg } # @msg-checking msg # # Writes the message with no newline to stdout. # proc msg-checking {msg} { if {$::autosetup(msg-quiet) == 0} { maybe-show-timestamp puts -nonewline $msg set ::autosetup(msg-checking) 1 } } # @msg-result msg # # Writes the message to stdout. # proc msg-result {msg} { if {$::autosetup(msg-quiet) == 0} { maybe-show-timestamp puts $msg set ::autosetup(msg-checking) 0 show-notices } } # @msg-quiet command ... # # 'msg-quiet' evaluates it's arguments as a command with output # from 'msg-checking' and 'msg-result' suppressed. # # This is useful if a check needs to run a subcheck which isn't # of interest to the user. proc msg-quiet {args} { incr ::autosetup(msg-quiet) set rc [uplevel 1 $args] incr ::autosetup(msg-quiet) -1 return $rc } # Will be overridden by 'use misc' proc error-stacktrace {msg} { return $msg } proc error-location {msg} { return $msg } ################################################################## # # Debugging output # proc dputs {msg} { if {$::autosetup(debug)} { puts $msg } } ################################################################## # # User and system warnings and errors # # Usage errors such as wrong command line options # @user-error msg # # Indicate incorrect usage to the user, including if required components # or features are not found. # 'autosetup' exits with a non-zero return code. # proc user-error {msg} { show-notices puts stderr "Error: $msg" puts stderr "Try: '[file tail $::autosetup(exe)] --help' for options" exit 1 } # @user-notice msg # # Output the given message to stderr. # proc user-notice {msg} { lappend ::autosetup(notices) $msg } # Incorrect usage in the auto.def file. Identify the location. proc autosetup-error {msg} { autosetup-full-error [error-location $msg] } # Like autosetup-error, except $msg is the full error message. proc autosetup-full-error {msg} { show-notices puts stderr $msg exit 1 } proc show-notices {} { if {$::autosetup(msg-checking)} { puts "" set ::autosetup(msg-checking) 0 } flush stdout if {[info exists ::autosetup(notices)]} { puts stderr [join $::autosetup(notices) \n] unset ::autosetup(notices) } } proc maybe-show-timestamp {} { if {$::autosetup(msg-timing) && $::autosetup(msg-checking) == 0} { puts -nonewline [format {[%6.2f] } [expr {([clock millis] - $::autosetup(start)) % 10000 / 1000.0}]] } } # @autosetup-require-version required # # Checks the current version of 'autosetup' against '$required'. # A fatal error is generated if the current version is less than that required. # proc autosetup-require-version {required} { if {[compare-versions $::autosetup(version) $required] < 0} { user-error "autosetup version $required is required, but this is $::autosetup(version)" } } proc autosetup_version {} { return "autosetup v$::autosetup(version)" } ################################################################## # # Directory/path handling # proc realdir {dir} { set oldpwd [pwd] cd $dir set pwd [pwd] cd $oldpwd return $pwd } # Follow symlinks until we get to something which is not a symlink proc realpath {path} { while {1} { if {[catch { set path [file readlink $path] }]} { # Not a link break } } return $path } # Convert absolute path, $path into a path relative # to the given directory (or the current dir, if not given). # proc relative-path {path {pwd {}}} { set diff 0 set same 0 set newf {} set prefix {} set path [file-normalize $path] if {$pwd eq ""} { set pwd [pwd] } else { set pwd [file-normalize $pwd] } if {$path eq $pwd} { return . } # Try to make the filename relative to the current dir foreach p [split $pwd /] f [split $path /] { if {$p ne $f} { incr diff } elseif {!$diff} { incr same } if {$diff} { if {$p ne ""} { # Add .. for sibling or parent dir lappend prefix .. } if {$f ne ""} { lappend newf $f } } } if {$same == 1 || [llength $prefix] > 3} { return $path } file join [join $prefix /] [join $newf /] } # Add filename as a dependency to rerun autosetup # The name will be normalised (converted to a full path) # proc autosetup_add_dep {filename} { lappend ::autosetup(deps) [file-normalize $filename] } ################################################################## # # Library module support # # @use module ... # # Load the given library modules. # e.g. 'use cc cc-shared' # # Note that module 'X' is implemented in either 'autosetup/X.tcl' # or 'autosetup/X/init.tcl' # # The latter form is useful for a complex module which requires additional # support file. In this form, '$::usedir' is set to the module directory # when it is loaded. # proc use {args} { global autosetup libmodule modsource set dirs [list $autosetup(libdir)] if {[info exists autosetup(srcdir)]} { lappend dirs $autosetup(srcdir)/autosetup } foreach m $args { if {[info exists libmodule($m)]} { continue } set libmodule($m) 1 if {[info exists modsource(${m}.tcl)]} { automf_load eval $modsource(${m}.tcl) } else { set locs [list ${m}.tcl ${m}/init.tcl] set found 0 foreach dir $dirs { foreach loc $locs { set source $dir/$loc if {[file exists $source]} { incr found break } } if {$found} { break } } if {$found} { # For the convenience of the "use" source, point to the directory # it is being loaded from set ::usedir [file dirname $source] automf_load source $source autosetup_add_dep $source } else { autosetup-error "use: No such module: $m" } } } } proc autosetup_load_auto_modules {} { global autosetup modsource # First load any embedded auto modules foreach mod [array names modsource *.auto] { automf_load eval $modsource($mod) } # Now any external auto modules foreach file [glob -nocomplain $autosetup(libdir)/*.auto $autosetup(libdir)/*/*.auto] { automf_load source $file } } # 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] set autosetup(installed) 0 set autosetup(sysinstall) 0 set autosetup(msg-checking) 0 set autosetup(msg-quiet) 0 set autosetup(inittypes) {} # Embedded modules are inserted below here set autosetup(installed) 1 set autosetup(sysinstall) 0 # ----- @module asciidoc-formatting.tcl ----- set modsource(asciidoc-formatting.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting # asciidoc format use formatting proc para {text} { regsub -all "\[ \t\n\]+" [string trim $text] " " } proc title {text} { underline [para $text] = nl } proc p {text} { puts [para $text] nl } proc code {text} { foreach line [parse_code_block $text] { puts " $line" } nl } proc codelines {lines} { foreach line $lines { puts " $line" } nl } proc nl {} { puts "" } proc underline {text char} { regexp "^(\[ \t\]*)(.*)" $text -> indent words puts $text puts $indent[string repeat $char [string length $words]] } proc section {text} { underline "[para $text]" - nl } proc subsection {text} { underline "$text" ~ nl } proc bullet {text} { puts "* [para $text]" } proc indent {text} { puts " :: " puts [para $text] } proc defn {first args} { set sep "" if {$first ne ""} { puts "${first}::" } else { puts " :: " } set defn [string trim [join $args \n]] regsub -all "\n\n" $defn "\n ::\n" defn puts $defn } } # ----- @module formatting.tcl ----- set modsource(formatting.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides common text formatting # This is designed for documentation which looks like: # code {...} # or # code { # ... # ... # } # In the second case, we need to work out the indenting # and strip it from all lines but preserve the remaining indenting. # Note that all lines need to be indented with the same initial # spaces/tabs. # # Returns a list of lines with the indenting removed. # proc parse_code_block {text} { # If the text begins with newline, take the following text, # otherwise just return the original if {![regexp "^\n(.*)" $text -> text]} { return [list [string trim $text]] } # And trip spaces off the end set text [string trimright $text] set min 100 # Examine each line to determine the minimum indent foreach line [split $text \n] { if {$line eq ""} { # Ignore empty lines for the indent calculation continue } regexp "^(\[ \t\]*)" $line -> indent set len [string length $indent] if {$len < $min} { set min $len } } # Now make a list of lines with this indent removed set lines {} foreach line [split $text \n] { lappend lines [string range $line $min end] } # Return the result return $lines } } # ----- @module getopt.tcl ----- set modsource(getopt.tcl) { # Copyright (c) 2006 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Simple getopt module # Parse everything out of the argv list which looks like an option # Everything which doesn't look like an option, or is after --, is left unchanged # Understands --enable-xxx as a synonym for --xxx to enable the boolean option xxx. # Understands --disable-xxx to disable the boolean option xxx. # # The returned value is a dictionary keyed by option name # Each value is a list of {type value} ... where type is "bool" or "str". # The value for a boolean option is 0 or 1. The value of a string option is the value given. proc getopt {argvname} { upvar $argvname argv set nargv {} set opts {} for {set i 0} {$i < [llength $argv]} {incr i} { set arg [lindex $argv $i] #dputs arg=$arg if {$arg eq "--"} { # End of options incr i lappend nargv {*}[lrange $argv $i end] break } if {[regexp {^--([^=][^=]+)=(.*)$} $arg -> name value]} { # --name=value dict lappend opts $name [list str $value] } elseif {[regexp {^--(enable-|disable-)?([^=]*)$} $arg -> prefix name]} { if {$prefix in {enable- ""}} { set value 1 } else { set value 0 } dict lappend opts $name [list bool $value] } else { lappend nargv $arg } } #puts "getopt: argv=[join $argv] => [join $nargv]" #array set getopt $opts #parray getopt set argv $nargv return $opts } } # ----- @module help.tcl ----- set modsource(help.tcl) { # Copyright (c) 2010 WorkWare Systems http://workware.net.au/ # All rights reserved # Module which provides usage, help and the command reference proc autosetup_help {what} { use_pager puts "Usage: [file tail $::autosetup(exe)] \[options\] \[settings\]\n" puts "This is [autosetup_version], a build environment \"autoconfigurator\"" puts "See the documentation online at http://msteveb.github.com/autosetup/\n" if {$what eq "local"} { if {[file exists $::autosetup(autodef)]} { # This relies on auto.def having a call to 'options' # which will display options and quit source $::autosetup(autodef) } else { options-show } } else { incr ::autosetup(showhelp) if {[catch {use $what}]} { user-error "Unknown module: $what" } else { options-show } } exit 0 } proc autosetup_show_license {} { global modsource autosetup use_pager if {[info exists modsource(LICENSE)]} { puts $modsource(LICENSE) return } foreach dir [list $autosetup(libdir) $autosetup(srcdir)] { set path [file join $dir LICENSE] if {[file exists $path]} { puts [readfile $path] return } } puts "LICENSE not found" } # If not already paged and stdout is a tty, pipe the output through the pager # This is done by reinvoking autosetup with --nopager added proc use_pager {} { if {![opt-bool nopager] && [getenv PAGER ""] ne "" && [isatty? stdin] && [isatty? stdout]} { if {[catch { exec [info nameofexecutable] $::argv0 --nopager {*}$::argv |& {*}[getenv PAGER] >@stdout <@stdin 2>@stderr } msg opts] == 1} { if {[dict get $opts -errorcode] eq "NONE"} { # an internal/exec error puts stderr $msg exit 1 } } exit 0 } } # Outputs the autosetup references in one of several formats proc autosetup_reference {{type text}} { use_pager switch -glob -- $type { wiki {use wiki-formatting} ascii* {use asciidoc-formatting} md - markdown {use markdown-formatting} default {use text-formatting} } title "[autosetup_version] -- Command Reference" section {Introduction} p { See http://msteveb.github.com/autosetup/ for the online documentation for 'autosetup' } p { 'autosetup' provides a number of built-in commands which are documented below. These may be used from 'auto.def' to test for features, define variables, create files from templates and other similar actions. } automf_command_reference exit 0 } proc autosetup_output_block {type lines} { if {[llength $lines]} { switch $type { section { section $lines } subsection { subsection $lines } code { codelines $lines } p { p [join $lines] } list { foreach line $lines { bullet $line } nl } } } } # Generate a command reference from inline documentation proc automf_command_reference {} { lappend files $::autosetup(prog) lappend files {*}[lsort [glob -nocomplain $::autosetup(libdir)/*.tcl]] # We want to process all non-module files before module files # and then modules in alphabetical order. # So examine all files and extract docs into doc($modulename) and doc(_core_) # # Each entry is a list of {type data} where $type is one of: section, subsection, code, list, p # and $data is a string for section, subsection or a list of text lines for other types. # XXX: Should commands be in alphabetical order too? Currently they are in file order. set doc(_core_) {} lappend doc(_core_) [list section "Core Commands"] foreach file $files { set modulename [file rootname [file tail $file]] set current _core_ set f [open $file] while {![eof $f]} { set line [gets $f] # Find embedded module names if {[regexp {^#.*@module ([^ ]*)} $line -> modulename]} { continue } # Find lines starting with "# @*" and continuing through the remaining comment lines if {![regexp {^# @(.*)} $line -> cmd]} { continue } # Synopsis or command? if {$cmd eq "synopsis:"} { set current $modulename lappend doc($current) [list section "Module: $modulename"] } else { lappend doc($current) [list subsection $cmd] } set lines {} set type p # Now the description while {![eof $f]} { set line [gets $f] if {![regexp {^#(#)? ?(.*)} $line -> hash cmd]} { break } if {$hash eq "#"} { set t code } elseif {[regexp {^- (.*)} $cmd -> cmd]} { set t list } else { set t p } #puts "hash=$hash, oldhash=$oldhash, lines=[llength $lines], cmd=$cmd" if {$t ne $type || $cmd eq ""} { # Finish the current block lappend doc($current) [list $type $lines] set lines {} set type $t } if {$cmd ne ""} { lappend lines $cmd } } lappend doc($current) [list $type $lines] } close $f } # Now format and output the results # _core_ will sort first foreach module [lsort [array names doc]] { foreach item $doc($module) { autosetup_output_block {*}$item } } } } # ----- @module init.tcl ----- set modsource(init.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module to help create auto.def and configure proc autosetup_init {type} { set help 0 if {$type in {? help}} { incr help } elseif {![dict exists $::autosetup(inittypes) $type]} { puts "Unknown type, --init=$type" incr help } if {$help} { puts "Use one of the following types (e.g. --init=make)\n" foreach type [lsort [dict keys $::autosetup(inittypes)]] { lassign [dict get $::autosetup(inittypes) $type] desc # XXX: Use the options-show code to wrap the description puts [format "%-10s %s" $type $desc] } return } lassign [dict get $::autosetup(inittypes) $type] desc script puts "Initialising $type: $desc\n" # All initialisations happens in the top level srcdir cd $::autosetup(srcdir) uplevel #0 $script } proc autosetup_add_init_type {type desc script} { dict set ::autosetup(inittypes) $type [list $desc $script] } # This is for in creating build-system init scripts # # If the file doesn't exist, create it containing $contents # If the file does exist, only overwrite if --force is specified. # proc autosetup_check_create {filename contents} { if {[file exists $filename]} { if {!$::autosetup(force)} { puts "I see $filename already exists." return } else { puts "I will overwrite the existing $filename because you used --force." } } else { puts "I don't see $filename, so I will create it." } writefile $filename $contents } } # ----- @module install.tcl ----- set modsource(install.tcl) { # Copyright (c) 2006-2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which can install autosetup # autosetup(installed)=1 means that autosetup is not running from source # autosetup(sysinstall)=1 means that autosetup is running from a sysinstall version # shared=1 means that we are trying to do a sysinstall. This is only possible from the development source. proc autosetup_install {dir {shared 0}} { global autosetup if {$shared} { if {$autosetup(installed) || $autosetup(sysinstall)} { user-error "Can only --sysinstall from development sources" } } elseif {$autosetup(installed) && !$autosetup(sysinstall)} { user-error "Can't --install from project install" } if {$autosetup(sysinstall)} { # This is the sysinstall version, so install just uses references cd $dir puts "[autosetup_version] creating configure to use system-installed autosetup" autosetup_create_configure 1 puts "Creating autosetup/README.autosetup" file mkdir autosetup autosetup_install_readme autosetup/README.autosetup 1 return } if {[catch { if {$shared} { set target $dir/bin/autosetup set installedas $target } else { if {$dir eq "."} { set installedas autosetup } else { set installedas $dir/autosetup } cd $dir file mkdir autosetup set target autosetup/autosetup } set targetdir [file dirname $target] file mkdir $targetdir set f [open $target w] set publicmodules {} # First the main script, but only up until "CUT HERE" set in [open $autosetup(dir)/autosetup] while {[gets $in buf] >= 0} { if {$buf ne "##-- CUT HERE --##"} { puts $f $buf continue } # Insert the static modules here # i.e. those which don't contain @synopsis: # All modules are inserted if $shared is set puts $f "set autosetup(installed) 1" puts $f "set autosetup(sysinstall) $shared" foreach file [lsort [glob $autosetup(libdir)/*.{tcl,auto}]] { set modname [file tail $file] set ext [file ext $modname] set buf [readfile $file] if {!$shared} { if {$ext eq ".auto" || [string match "*\n# @synopsis:*" $buf]} { lappend publicmodules $file continue } } dputs "install: importing lib/[file tail $file]" puts $f "# ----- @module $modname -----" puts $f "\nset modsource($modname) \{" puts $f $buf puts $f "\}\n" } if {$shared} { foreach {srcname destname} [list $autosetup(libdir)/README.autosetup-lib README.autosetup \ $autosetup(srcdir)/LICENSE LICENSE] { dputs "install: importing $srcname as $destname" puts $f "\nset modsource($destname) \\\n[list [readfile $srcname]\n]\n" } } } close $in close $f catch {exec chmod 755 $target} set installfiles {autosetup-config.guess autosetup-config.sub autosetup-test-tclsh} set removefiles {} if {!$shared} { autosetup_install_readme $targetdir/README.autosetup 0 # Install public modules foreach file $publicmodules { set tail [file tail $file] autosetup_install_file $file $targetdir/$tail } lappend installfiles jimsh0.c autosetup-find-tclsh LICENSE lappend removefiles config.guess config.sub test-tclsh find-tclsh } else { lappend installfiles {sys-find-tclsh autosetup-find-tclsh} } # Install support files foreach fileinfo $installfiles { if {[llength $fileinfo] == 2} { lassign $fileinfo source dest } else { lassign $fileinfo source set dest $source } autosetup_install_file $autosetup(dir)/$source $targetdir/$dest } # Remove obsolete files foreach file $removefiles { if {[file exists $targetdir/$file]} { file delete $targetdir/$file } } } error]} { user-error "Failed to install autosetup: $error" } if {$shared} { set type "system" } else { set type "local" } puts "Installed $type [autosetup_version] to $installedas" if {!$shared} { # Now create 'configure' if necessary autosetup_create_configure 0 } } proc autosetup_create_configure {shared} { if {[file exists configure]} { if {!$::autosetup(force)} { # Could this be an autosetup configure? if {![string match "*\nWRAPPER=*" [readfile configure]]} { puts "I see configure, but not created by autosetup, so I won't overwrite it." puts "Remove it or use --force to overwrite." return } } else { puts "I will overwrite the existing configure because you used --force." } } else { puts "I don't see configure, so I will create it." } if {$shared} { writefile configure \ {#!/bin/sh WRAPPER="$0"; export WRAPPER; "autosetup" "$@" } } else { writefile configure \ {#!/bin/sh dir="`dirname "$0"`/autosetup" WRAPPER="$0"; export WRAPPER; exec "`"$dir/autosetup-find-tclsh"`" "$dir/autosetup" "$@" } } catch {exec chmod 755 configure} } # Append the contents of $file to filehandle $f proc autosetup_install_append {f file} { dputs "install: include $file" set in [open $file] puts $f [read $in] close $in } proc autosetup_install_file {source target} { dputs "install: $source => $target" if {![file exists $source]} { error "Missing installation file '$source'" } writefile $target [readfile $source]\n # If possible, copy the file mode file stat $source stat set mode [format %o [expr {$stat(mode) & 0x1ff}]] catch {exec chmod $mode $target} } proc autosetup_install_readme {target sysinstall} { set readme "README.autosetup created by [autosetup_version]\n\n" if {$sysinstall} { append readme \ {This is the autosetup directory for a system install of autosetup. Loadable modules can be added here. } } else { append readme \ {This is the autosetup directory for a local install of autosetup. It contains autosetup, support files and loadable modules. } } append readme { *.tcl files in this directory are optional modules which can be loaded with the 'use' directive. *.auto files in this directory are auto-loaded. For more information, see http://msteveb.github.com/autosetup/ } dputs "install: autosetup/README.autosetup" writefile $target $readme } } # ----- @module markdown-formatting.tcl ----- set modsource(markdown-formatting.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting # markdown format (kramdown syntax) use formatting proc para {text} { regsub -all "\[ \t\n\]+" [string trim $text] " " text regsub -all {([^a-zA-Z])'([^']*)'} $text {\1**`\2`**} text regsub -all {^'([^']*)'} $text {**`\1`**} text regsub -all {(http[^ \t\n]*)} $text {[\1](\1)} text return $text } proc title {text} { underline [para $text] = nl } proc p {text} { puts [para $text] nl } proc codelines {lines} { puts "~~~~~~~~~~~~" foreach line $lines { puts $line } puts "~~~~~~~~~~~~" nl } proc code {text} { puts "~~~~~~~~~~~~" foreach line [parse_code_block $text] { puts $line } puts "~~~~~~~~~~~~" nl } proc nl {} { puts "" } proc underline {text char} { regexp "^(\[ \t\]*)(.*)" $text -> indent words puts $text puts $indent[string repeat $char [string length $words]] } proc section {text} { underline "[para $text]" - nl } proc subsection {text} { puts "### `$text`" nl } proc bullet {text} { puts "* [para $text]" } proc defn {first args} { puts "^" set defn [string trim [join $args \n]] if {$first ne ""} { puts "**${first}**" puts -nonewline ": " regsub -all "\n\n" $defn "\n: " defn } puts "$defn" } } # ----- @module misc.tcl ----- set modsource(misc.tcl) { # Copyright (c) 2007-2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module containing misc procs useful to modules # Largely for platform compatibility set autosetup(istcl) [info exists ::tcl_library] set autosetup(iswin) [string equal windows $tcl_platform(platform)] if {$autosetup(iswin)} { # mingw/windows separates $PATH with semicolons # and doesn't have an executable bit proc split-path {} { split [getenv PATH .] {;} } proc file-isexec {exec} { # Basic test for windows. We ignore .bat if {[file isfile $exec] || [file isfile $exec.exe]} { return 1 } return 0 } } else { # unix separates $PATH with colons and has and executable bit proc split-path {} { split [getenv PATH .] : } proc file-isexec {exec} { file executable $exec } } # Assume that exec can return stdout and stderr proc exec-with-stderr {args} { exec {*}$args 2>@1 } if {$autosetup(istcl)} { # Tcl doesn't have the env command proc getenv {name args} { if {[info exists ::env($name)]} { return $::env($name) } if {[llength $args]} { return [lindex $args 0] } return -code error "environment variable \"$name\" does not exist" } proc isatty? {channel} { dict exists [fconfigure $channel] -xchar } } else { if {$autosetup(iswin)} { # On Windows, backslash convert all environment variables # (Assume that Tcl does this for us) proc getenv {name args} { string map {\\ /} [env $name {*}$args] } } else { # Jim on unix is simple alias getenv env } proc isatty? {channel} { set tty 0 catch { # isatty is a recent addition to Jim Tcl set tty [$channel isatty] } return $tty } } # In case 'file normalize' doesn't exist # proc file-normalize {path} { if {[catch {file normalize $path} result]} { if {$path eq ""} { return "" } set oldpwd [pwd] if {[file isdir $path]} { cd $path set result [pwd] } else { cd [file dirname $path] set result [file join [pwd] [file tail $path]] } cd $oldpwd } return $result } # If everything is working properly, the only errors which occur # should be generated in user code (e.g. auto.def). # By default, we only want to show the error location in user code. # We use [info frame] to achieve this, but it works differently on Tcl and Jim. # # This is designed to be called for incorrect usage in auto.def, via autosetup-error # proc error-location {msg} { if {$::autosetup(debug)} { return -code error $msg } # Search back through the stack trace for the first error in a .def file for {set i 1} {$i < [info level]} {incr i} { if {$::autosetup(istcl)} { array set info [info frame -$i] } else { lassign [info frame -$i] info(caller) info(file) info(line) } if {[string match *.def $info(file)]} { return "[relative-path $info(file)]:$info(line): Error: $msg" } #puts "Skipping $info(file):$info(line)" } return $msg } # If everything is working properly, the only errors which occur # should be generated in user code (e.g. auto.def). # By default, we only want to show the error location in user code. # We use [info frame] to achieve this, but it works differently on Tcl and Jim. # # This is designed to be called for incorrect usage in auto.def, via autosetup-error # proc error-stacktrace {msg} { if {$::autosetup(debug)} { return -code error $msg } # Search back through the stack trace for the first error in a .def file for {set i 1} {$i < [info level]} {incr i} { if {$::autosetup(istcl)} { array set info [info frame -$i] } else { lassign [info frame -$i] info(caller) info(file) info(line) } if {[string match *.def $info(file)]} { return "[relative-path $info(file)]:$info(line): Error: $msg" } #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.tcl ----- set modsource(text-formatting.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting use formatting proc wordwrap {text length {firstprefix ""} {nextprefix ""}} { set len 0 set space $firstprefix foreach word [split $text] { set word [string trim $word] if {$word eq ""} { continue } if {[info exists partial]} { append partial " " $word if {[string first $quote $word] < 0} { # Haven't found end of quoted word continue } # Finished quoted word set word $partial unset partial unset quote } else { set quote [string index $word 0] if {$quote in {' *}} { if {[string first $quote $word 1] < 0} { # Haven't found end of quoted word # Not a whole word. set first [string index $word 0] # Start of quoted word set partial $word continue } } } if {$len && [string length $space$word] + $len >= $length} { puts "" set len 0 set space $nextprefix } incr len [string length $space$word] # Use man-page conventions for highlighting 'quoted' and *quoted* # single words. # Use x^Hx for *bold* and _^Hx for 'underline'. # # less and more will both understand this. # Pipe through 'col -b' to remove them. if {[regexp {^'(.*)'(.*)} $word -> quoted after]} { set quoted [string map {~ " "} $quoted] regsub -all . $quoted "&\b&" quoted set word $quoted$after } elseif {[regexp {^[*](.*)[*](.*)} $word -> quoted after]} { set quoted [string map {~ " "} $quoted] regsub -all . $quoted "_\b&" quoted set word $quoted$after } puts -nonewline $space$word set space " " } if {[info exists partial]} { # Missing end of quote puts -nonewline $space$partial } if {$len} { puts "" } } proc title {text} { underline [string trim $text] = nl } proc p {text} { wordwrap $text 80 nl } proc codelines {lines} { foreach line $lines { puts " $line" } nl } proc nl {} { puts "" } proc underline {text char} { regexp "^(\[ \t\]*)(.*)" $text -> indent words puts $text puts $indent[string repeat $char [string length $words]] } proc section {text} { underline "[string trim $text]" - nl } proc subsection {text} { underline "$text" ~ nl } proc bullet {text} { wordwrap $text 76 " * " " " } proc indent {text} { wordwrap $text 76 " " " " } proc defn {first args} { if {$first ne ""} { underline " $first" ~ } foreach p $args { if {$p ne ""} { indent $p } } } } # ----- @module util.tcl ----- set modsource(util.tcl) { # Copyright (c) 2012 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which contains miscellaneous utility functions # @compare-versions version1 version2 # # Versions are of the form 'a.b.c' (may be any number of numeric components) # # Compares the two versions and returns: ## -1 if v1 < v2 ## 0 if v1 == v2 ## 1 if v1 > v2 # # If one version has fewer components than the other, 0 is substituted to the right. e.g. ## 0.2 < 0.3 ## 0.2.5 > 0.2 ## 1.1 == 1.1.0 # proc compare-versions {v1 v2} { foreach c1 [split $v1 .] c2 [split $v2 .] { if {$c1 eq ""} { set c1 0 } if {$c2 eq ""} { set c2 0 } if {$c1 < $c2} { return -1 } if {$c1 > $c2} { return 1 } } return 0 } # @suffix suf list # # Takes a list and returns a new list with '$suf' appended # to each element # ## suffix .c {a b c} => {a.c b.c c.c} # proc suffix {suf list} { set result {} foreach p $list { lappend result $p$suf } return $result } # @prefix pre list # # Takes a list and returns a new list with '$pre' prepended # to each element # ## prefix jim- {a.c b.c} => {jim-a.c jim-b.c} # proc prefix {pre list} { set result {} foreach p $list { lappend result $pre$p } return $result } # @lpop list # # Removes the last entry from the given list and returns it. proc lpop {listname} { upvar $listname list set val [lindex $list end] set list [lrange $list 0 end-1] return $val } } # ----- @module wiki-formatting.tcl ----- set modsource(wiki-formatting.tcl) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting # wiki.tcl.tk format output use formatting proc joinlines {text} { set lines {} foreach l [split [string trim $text] \n] { lappend lines [string trim $l] } join $lines } proc p {text} { puts [joinlines $text] puts "" } proc title {text} { puts "*** [joinlines $text] ***" puts "" } proc codelines {lines} { puts "======" foreach line $lines { puts " $line" } puts "======" } proc code {text} { puts "======" foreach line [parse_code_block $text] { puts " $line" } puts "======" } proc nl {} { } proc section {text} { puts "'''$text'''" puts "" } proc subsection {text} { puts "''$text''" puts "" } proc bullet {text} { puts " * [joinlines $text]" } proc indent {text} { puts " : [joinlines $text]" } proc defn {first args} { if {$first ne ""} { indent '''$first''' } foreach p $args { p $p } } } ################################################################## # # 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 } |
Added autosetup/autosetup-config.guess.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-03-08' # 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 <https://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; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches 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. Options: -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-2018 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." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac 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" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > "$dummy.c" ; for c in cc gcc c89 c99 ; do if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then 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'`" # If ldd exists, use it to detect musl libc. if command -v ldd >/dev/null && \ ldd --version 2>&1 | grep -q ^musl then LIBC=musl fi ;; 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 # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ "/sbin/$sysctl" 2>/dev/null || \ "/usr/sbin/$sysctl" 2>/dev/null || \ echo unknown)` case "$UNAME_MACHINE_ARCH" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine="${arch}${endian}"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case "$UNAME_MACHINE_ARCH" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval "$set_cc_for_build" if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "$UNAME_MACHINE_ARCH" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; 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/[-_].*//' | cut -d. -f1,2` ;; 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}${abi}" 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 ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" exit ;; *:MidnightBSD:*:*) echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:Sortix:*:*) echo "$UNAME_MACHINE"-unknown-sortix exit ;; *:Redox:*:*) echo "$UNAME_MACHINE"-unknown-redox exit ;; mips:OSF1:*.*) echo mips-dec-osf1 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` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; 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 ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-morphos exit ;; *: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. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval "$set_cc_for_build" SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "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:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include <stdio.h> /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; 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 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include <sys/systemcfg.h> main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) 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 eval "$set_cc_for_build" # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include <unistd.h> int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo "$UNAME_MACHINE"-unknown-osf1mk else echo "$UNAME_MACHINE"-unknown-osf1 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/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) 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) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" 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*: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) echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; i*:UWIN*:*) echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-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 "[:upper:]" "[:lower:]"``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 ;; e2k:Linux:*:*) echo "$UNAME_MACHINE"-unknown-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 ;; k1om: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; } ;; mips64el:Linux:*:*) echo "$UNAME_MACHINE"-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 ;; riscv32:Linux:*:* | riscv64:Linux:*:*) echo "$UNAME_MACHINE"-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"-pc-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 ;; i*86:XTS-300:*:STOP) echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` 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 ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL" elif /bin/uname -X 2>/dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 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 configure 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 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' 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 \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) 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. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) 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:*:*) echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux"$UNAME_RELEASE" exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) 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 # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc 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 ;; NSV-*:NONSTOP_KERNEL:*:*) echo nsv-tandem-nsk"$UNAME_RELEASE" exit ;; NSX-*:NONSTOP_KERNEL:*:*) echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *: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 ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; esac echo "$0: unable to guess system type" >&2 case "$UNAME_MACHINE:$UNAME_SYSTEM" in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <<EOF NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize the system type. Please install a C compiler and try again. EOF ;; esac cat >&2 <<EOF This script (version $timestamp), has failed to recognize the operating system you are using. If your script is old, overwrite *all* copies of config.guess and config.sub with the latest versions from: https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess and https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub If $0 has already been updated, send the following data and any information you think might be pertinent to config-patches@gnu.org to provide the necessary information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: |
Added autosetup/autosetup-config.sub.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-03-08' # 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 <https://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 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: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -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.sub ($timestamp) Copyright 1992-2018 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." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; 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* | netbsd*-eabi* | \ kopensolaris*-gnu* | cloudabi*-eabi* | \ 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 ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -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/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -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/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # 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 \ | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia16 | 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 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]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 \ | visium \ | wasm32 \ | 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 ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) ;; 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-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia16-* | 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-* \ | pru-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | 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-* \ | visium-* \ | wasm32-* \ | 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 ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-pc os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 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) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; 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 ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) 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*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2*) basic_machine=m68k-bull os=-sysv3 ;; e500v[12]) basic_machine=powerpc-unknown os=$os"spe" ;; e500v[12]-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=$os"spe" ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; 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 ;; i*86v) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; magnum | m3230) 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) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; 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 ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; 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 ;; nsv-tandem) basic_machine=nsv-tandem ;; nsx-tandem) basic_machine=nsx-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium4-*) 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) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; 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 ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh5el) basic_machine=sh5le-unknown ;; simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; 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 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) 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 ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; x64) basic_machine=x86_64-pc ;; 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 ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` ;; *) ;; 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 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # es1800 is here to avoid being matched by es* (a different OS) -es1800*) os=-ose ;; # Now 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* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* | -hcos* \ | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \ | -midnightbsd*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -xray | -os68k* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo "$os" | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2) os=-nextstep2 ;; -nsk*) os=-nsk ;; # 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 ;; -svr4*) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -pikeos*) # Until real need of OS specific support for # particular features comes up, bare metal # configurations are quite functional. case $basic_machine in arm*) os=-eabi ;; *) os=-elf ;; esac ;; -nacl*) ;; -ios) ;; -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 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # 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 ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) 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 ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; pru-*) os=-elf ;; *-be) os=-beos ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` ;; esac echo "$basic_machine$os" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: |
Added autosetup/autosetup-find-tclsh.
> > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/bin/sh # Looks for a suitable tclsh or jimsh in the PATH # If not found, builds a bootstrap jimsh from source # Prefer $autosetup_tclsh if is set in the environment d=`dirname "$0"` { "$d/jimsh0" "$d/autosetup-test-tclsh"; } 2>/dev/null && exit 0 PATH="$PATH:$d"; export PATH for tclsh in $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6; do { $tclsh "$d/autosetup-test-tclsh"; } 2>/dev/null && exit 0 done echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" for cc in ${CC_FOR_BUILD:-cc} gcc; do { $cc -o "$d/jimsh0" "$d/jimsh0.c"; } >/dev/null 2>&1 || continue "$d/jimsh0" "$d/autosetup-test-tclsh" && exit 0 done echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." echo false |
Added autosetup/autosetup-test-tclsh.
> > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # A small Tcl script to verify that the chosen # interpreter works. Sometimes we might e.g. pick up # an interpreter for a different arch. # Outputs the full path to the interpreter if {[catch {info version} version] == 0} { # This is Jim Tcl if {$version >= 0.72} { # Ensure that regexp works regexp (a.*?) a puts [info nameofexecutable] exit 0 } } elseif {[catch {info tclversion} version] == 0} { if {$version >= 8.5 && ![string match 8.5a* [info patchlevel]]} { puts [info nameofexecutable] exit 0 } } exit 1 |
Added autosetup/cc-db.tcl.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc-db' module provides a knowledge-base of system idiosyncrasies. # In general, this module can always be included. use cc module-options {} # openbsd needs sys/types.h to detect some system headers cc-include-needs sys/socket.h sys/types.h cc-include-needs netinet/in.h sys/types.h |
Added autosetup/cc-lib.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 | # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # Provides a library of common tests on top of the 'cc' module. use cc module-options {} # @cc-check-lfs # # The equivalent of the 'AC_SYS_LARGEFILE' macro. # # defines 'HAVE_LFS' if LFS is available, # and defines '_FILE_OFFSET_BITS=64' if necessary # # Returns 1 if 'LFS' is available or 0 otherwise # proc cc-check-lfs {} { cc-check-includes sys/types.h msg-checking "Checking if -D_FILE_OFFSET_BITS=64 is needed..." set lfs 1 if {[msg-quiet cc-with {-includes sys/types.h} {cc-check-sizeof off_t}] == 8} { msg-result no } elseif {[msg-quiet cc-with {-includes sys/types.h -cflags -D_FILE_OFFSET_BITS=64} {cc-check-sizeof off_t}] == 8} { define _FILE_OFFSET_BITS 64 msg-result yes } else { set lfs 0 msg-result none } define-feature lfs $lfs return $lfs } # @cc-check-endian # # The equivalent of the 'AC_C_BIGENDIAN' macro. # # defines 'HAVE_BIG_ENDIAN' if endian is known to be big, # or 'HAVE_LITTLE_ENDIAN' if endian is known to be little. # # Returns 1 if determined, or 0 if not. # proc cc-check-endian {} { cc-check-includes sys/types.h sys/param.h set rc 0 msg-checking "Checking endian..." cc-with {-includes {sys/types.h sys/param.h}} { if {[cctest -code { #if !defined(BIG_ENDIAN) || !defined(BYTE_ORDER) #error unknown #elif BYTE_ORDER != BIG_ENDIAN #error little #endif }]} { define-feature big-endian msg-result "big" set rc 1 } elseif {[cctest -code { #if !defined(LITTLE_ENDIAN) || !defined(BYTE_ORDER) #error unknown #elif BYTE_ORDER != LITTLE_ENDIAN #error big #endif }]} { define-feature little-endian msg-result "little" set rc 1 } else { msg-result "unknown" } } return $rc } # @cc-check-flags flag ?...? # # Checks whether the given C/C++ compiler flags can be used. Defines feature # names prefixed with 'HAVE_CFLAG' and 'HAVE_CXXFLAG' respectively, and # appends working flags to '-cflags' and 'CFLAGS' or 'CXXFLAGS'. proc cc-check-flags {args} { set result 1 array set opts [cc-get-settings] switch -exact -- $opts(-lang) { c++ { set lang C++ set prefix CXXFLAG } c { set lang C set prefix CFLAG } default { autosetup-error "cc-check-flags failed with unknown language: $opts(-lang)" } } foreach flag $args { msg-checking "Checking whether the $lang compiler accepts $flag..." if {[cctest -cflags $flag]} { msg-result yes define-feature $prefix$flag cc-with [list -cflags [list $flag]] define-append ${prefix}S $flag } else { msg-result no set result 0 } } return $result } # @cc-check-standards ver ?...? # # Checks whether the C/C++ compiler accepts one of the specified '-std=$ver' # options, and appends the first working one to '-cflags' and 'CFLAGS' or # 'CXXFLAGS'. proc cc-check-standards {args} { array set opts [cc-get-settings] foreach std $args { if {[cc-check-flags -std=$std]} { return $std } } return "" } # Checks whether $keyword is usable as alignof proc cctest_alignof {keyword} { msg-checking "Checking for $keyword..." if {[cctest -code "int x = ${keyword}(char), y = ${keyword}('x');"]} then { msg-result ok define-feature $keyword } else { msg-result "not found" } } # @cc-check-c11 # # Checks for several C11/C++11 extensions and their alternatives. Currently # checks for '_Static_assert', '_Alignof', '__alignof__', '__alignof'. proc cc-check-c11 {} { msg-checking "Checking for _Static_assert..." if {[cctest -code { _Static_assert(1, "static assertions are available"); }]} then { msg-result ok define-feature _Static_assert } else { msg-result "not found" } cctest_alignof _Alignof cctest_alignof __alignof__ cctest_alignof __alignof } # @cc-check-alloca # # The equivalent of the 'AC_FUNC_ALLOCA' macro. # # Checks for the existence of 'alloca' # defines 'HAVE_ALLOCA' and returns 1 if it exists. proc cc-check-alloca {} { cc-check-some-feature alloca { cctest -includes alloca.h -code { alloca (2 * sizeof (int)); } } } # @cc-signal-return-type # # The equivalent of the 'AC_TYPE_SIGNAL' macro. # # defines 'RETSIGTYPE' to 'int' or 'void'. proc cc-signal-return-type {} { msg-checking "Checking return type of signal handlers..." cc-with {-includes {sys/types.h signal.h}} { if {[cctest -code {return *(signal (0, 0)) (0) == 1;}]} { set type int } else { set type void } define RETSIGTYPE $type msg-result $type } } |
Added autosetup/cc-shared.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 | # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc-shared' module provides support for shared libraries and shared objects. # It defines the following variables: # ## SH_CFLAGS Flags to use compiling sources destined for a shared library ## SH_LDFLAGS Flags to use linking (creating) a shared library ## SH_SOPREFIX Prefix to use to set the soname when creating a shared library ## SH_SOFULLPATH Set to 1 if the shared library soname should include the full install path ## SH_SOEXT Extension for shared libs ## SH_SOEXTVER Format for versioned shared libs - %s = version ## SHOBJ_CFLAGS Flags to use compiling sources destined for a shared object ## SHOBJ_LDFLAGS Flags to use linking a shared object, undefined symbols allowed ## SHOBJ_LDFLAGS_R - as above, but all symbols must be resolved ## SH_LINKRPATH Format for setting the rpath when linking an executable, %s = path ## SH_LINKFLAGS Flags to use linking an executable which will load shared objects ## LD_LIBRARY_PATH Environment variable which specifies path to shared libraries ## STRIPLIBFLAGS Arguments to strip a dynamic library module-options {} # Defaults: gcc on unix define SHOBJ_CFLAGS -fPIC define SHOBJ_LDFLAGS -shared define SH_CFLAGS -fPIC define SH_LDFLAGS -shared define SH_LINKFLAGS -rdynamic define SH_LINKRPATH "-Wl,-rpath -Wl,%s" define SH_SOEXT .so define SH_SOEXTVER .so.%s define SH_SOPREFIX -Wl,-soname, define LD_LIBRARY_PATH LD_LIBRARY_PATH define STRIPLIBFLAGS --strip-unneeded # Note: This is a helpful reference for identifying the toolchain # http://sourceforge.net/apps/mediawiki/predef/index.php?title=Compilers switch -glob -- [get-define host] { *-*-darwin* { define SHOBJ_CFLAGS "-dynamic -fno-common" define SHOBJ_LDFLAGS "-bundle -undefined dynamic_lookup" define SHOBJ_LDFLAGS_R -bundle define SH_CFLAGS -dynamic define SH_LDFLAGS -dynamiclib define SH_LINKFLAGS "" define SH_SOEXT .dylib define SH_SOEXTVER .%s.dylib define SH_SOPREFIX -Wl,-install_name, define SH_SOFULLPATH define LD_LIBRARY_PATH DYLD_LIBRARY_PATH define STRIPLIBFLAGS -x } *-*-ming* - *-*-cygwin - *-*-msys { define SHOBJ_CFLAGS "" define SHOBJ_LDFLAGS -shared define SH_CFLAGS "" define SH_LDFLAGS -shared define SH_LINKRPATH "" define SH_LINKFLAGS "" define SH_SOEXT .dll define SH_SOEXTVER .dll define SH_SOPREFIX "" define LD_LIBRARY_PATH PATH } sparc* { if {[msg-quiet cc-check-decls __SUNPRO_C]} { msg-result "Found sun stdio compiler" # sun stdio compiler # XXX: These haven't been fully tested. define SHOBJ_CFLAGS -KPIC define SHOBJ_LDFLAGS "-G" define SH_CFLAGS -KPIC define SH_LINKFLAGS -Wl,-export-dynamic define SH_SOPREFIX -Wl,-h, } } *-*-solaris* { if {[msg-quiet cc-check-decls __SUNPRO_C]} { msg-result "Found sun stdio compiler" # sun stdio compiler # XXX: These haven't been fully tested. define SHOBJ_CFLAGS -KPIC define SHOBJ_LDFLAGS "-G" define SH_CFLAGS -KPIC define SH_LINKFLAGS -Wl,-export-dynamic define SH_SOPREFIX -Wl,-h, } } *-*-hpux { # 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] } |
Added autosetup/cc.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 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 | # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc' module supports checking various 'features' of the C or C++ # compiler/linker environment. Common commands are 'cc-check-includes', # 'cc-check-types', 'cc-check-functions', 'cc-with', 'make-config-header' and 'make-template'. # # The following environment variables are used if set: # ## CC - C compiler ## CXX - C++ compiler ## CPP - C preprocessor ## CCACHE - Set to "none" to disable automatic use of ccache ## CFLAGS - Additional C compiler flags ## CXXFLAGS - Additional C++ compiler flags ## LDFLAGS - Additional compiler flags during linking ## LIBS - Additional libraries to use (for all tests) ## CROSS - Tool prefix for cross compilation # # The following variables are defined from the corresponding # environment variables if set. # ## CPPFLAGS ## LINKFLAGS ## CC_FOR_BUILD ## LD use system module-options {} # Checks for the existence of the given function by linking # proc cctest_function {function} { cctest -link 1 -declare "extern void $function\(void);" -code "$function\();" } # Checks for the existence of the given type by compiling proc cctest_type {type} { cctest -code "$type _x;" } # Checks for the existence of the given type/structure member. # e.g. "struct stat.st_mtime" proc cctest_member {struct_member} { # split at the first dot regexp {^([^.]+)[.](.*)$} $struct_member -> struct member cctest -code "static $struct _s; return sizeof(_s.$member);" } # Checks for the existence of the given define by compiling # proc cctest_define {name} { cctest -code "#ifndef $name\n#error not defined\n#endif" } # Checks for the existence of the given name either as # a macro (#define) or an rvalue (such as an enum) # proc cctest_decl {name} { cctest -code "#ifndef $name\n(void)$name;\n#endif" } # @cc-check-sizeof type ... # # Checks the size of the given types (between 1 and 32, inclusive). # Defines a variable with the size determined, or 'unknown' otherwise. # e.g. for type 'long long', defines 'SIZEOF_LONG_LONG'. # Returns the size of the last type. # proc cc-check-sizeof {args} { foreach type $args { msg-checking "Checking for sizeof $type..." set size unknown # Try the most common sizes first foreach i {4 8 1 2 16 32} { if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} { set size $i break } } msg-result $size set define [feature-define-name $type SIZEOF_] define $define $size } # Return the last result get-define $define } # Checks for each feature in $list by using the given script. # # When the script is evaluated, $each is set to the feature # being checked, and $extra is set to any additional cctest args. # # Returns 1 if all features were found, or 0 otherwise. proc cc-check-some-feature {list script} { set ret 1 foreach each $list { if {![check-feature $each $script]} { set ret 0 } } return $ret } # @cc-check-includes includes ... # # Checks that the given include files can be used. proc cc-check-includes {args} { cc-check-some-feature $args { set with {} if {[dict exists $::autosetup(cc-include-deps) $each]} { set deps [dict keys [dict get $::autosetup(cc-include-deps) $each]] msg-quiet cc-check-includes {*}$deps foreach i $deps { if {[have-feature $i]} { lappend with $i } } } if {[llength $with]} { cc-with [list -includes $with] { cctest -includes $each } } else { cctest -includes $each } } } # @cc-include-needs include required ... # # Ensures that when checking for '$include', a check is first # made for each '$required' file, and if found, it is included with '#include'. proc cc-include-needs {file args} { foreach depfile $args { dict set ::autosetup(cc-include-deps) $file $depfile 1 } } # @cc-check-types type ... # # Checks that the types exist. proc cc-check-types {args} { cc-check-some-feature $args { cctest_type $each } } # @cc-check-defines define ... # # Checks that the given preprocessor symbols are defined. proc cc-check-defines {args} { cc-check-some-feature $args { cctest_define $each } } # @cc-check-decls name ... # # Checks that each given name is either a preprocessor symbol or rvalue # such as an enum. Note that the define used is 'HAVE_DECL_xxx' # rather than 'HAVE_xxx'. proc cc-check-decls {args} { set ret 1 foreach name $args { msg-checking "Checking for $name..." set r [cctest_decl $name] define-feature "decl $name" $r if {$r} { msg-result "ok" } else { msg-result "not found" set ret 0 } } return $ret } # @cc-check-functions function ... # # Checks that the given functions exist (can be linked). proc cc-check-functions {args} { cc-check-some-feature $args { cctest_function $each } } # @cc-check-members type.member ... # # Checks that the given type/structure members exist. # A structure member is of the form 'struct stat.st_mtime'. proc cc-check-members {args} { cc-check-some-feature $args { cctest_member $each } } # @cc-check-function-in-lib function libs ?otherlibs? # # Checks that the given function can be found in one of the libs. # # First checks for no library required, then checks each of the libraries # in turn. # # If the function is found, the feature is defined and 'lib_$function' is defined # to '-l$lib' where the function was found, or "" if no library required. # In addition, '-l$lib' is prepended to the 'LIBS' define. # # If additional libraries may be needed for linking, they should be specified # with '$extralibs' as '-lotherlib1 -lotherlib2'. # These libraries are not automatically added to 'LIBS'. # # Returns 1 if found or 0 if not. # proc cc-check-function-in-lib {function libs {otherlibs {}}} { msg-checking "Checking libs for $function..." set found 0 cc-with [list -libs $otherlibs] { if {[cctest_function $function]} { msg-result "none needed" define lib_$function "" incr found } else { foreach lib $libs { cc-with [list -libs -l$lib] { if {[cctest_function $function]} { msg-result -l$lib define lib_$function -l$lib # prepend to LIBS define LIBS "-l$lib [get-define LIBS]" incr found break } } } } } define-feature $function $found if {!$found} { msg-result "no" } return $found } # @cc-check-tools tool ... # # Checks for existence of the given compiler tools, taking # into account any cross compilation prefix. # # For example, when checking for 'ar', first 'AR' is checked on the command # line and then in the environment. If not found, '${host}-ar' or # simply 'ar' is assumed depending upon whether cross compiling. # The path is searched for this executable, and if found 'AR' is defined # to the executable name. # Note that even when cross compiling, the simple 'ar' is used as a fallback, # but a warning is generated. This is necessary for some toolchains. # # It is an error if the executable is not found. # proc cc-check-tools {args} { foreach tool $args { set TOOL [string toupper $tool] set exe [get-env $TOOL [get-define cross]$tool] if {[find-executable {*}$exe]} { define $TOOL $exe continue } if {[find-executable {*}$tool]} { msg-result "Warning: Failed to find $exe, falling back to $tool which may be incorrect" define $TOOL $tool continue } user-error "Failed to find $exe" } } # @cc-check-progs prog ... # # Checks for existence of the given executables on the path. # # For example, when checking for 'grep', the path is searched for # the executable, 'grep', and if found 'GREP' is defined as 'grep'. # # If the executable is not found, the variable is defined as 'false'. # Returns 1 if all programs were found, or 0 otherwise. # proc cc-check-progs {args} { set failed 0 foreach prog $args { set PROG [string toupper $prog] msg-checking "Checking for $prog..." if {![find-executable $prog]} { msg-result no define $PROG false incr failed } else { msg-result ok define $PROG $prog } } expr {!$failed} } # @cc-path-progs prog ... # # Like cc-check-progs, but sets the define to the full path rather # than just the program name. # proc cc-path-progs {args} { set failed 0 foreach prog $args { set PROG [string toupper $prog] msg-checking "Checking for $prog..." set path [find-executable-path $prog] if {$path eq ""} { msg-result no define $PROG false incr failed } else { msg-result $path define $PROG $path } } expr {!$failed} } # Adds the given settings to $::autosetup(ccsettings) and # returns the old settings. # proc cc-add-settings {settings} { if {[llength $settings] % 2} { autosetup-error "settings list is missing a value: $settings" } set prev [cc-get-settings] # workaround a bug in some versions of jimsh by forcing # conversion of $prev to a list llength $prev array set new $prev foreach {name value} $settings { switch -exact -- $name { -cflags - -includes { # These are given as lists lappend new($name) {*}[list-non-empty $value] } -declare { lappend new($name) $value } -libs { # Note that new libraries are added before previous libraries set new($name) [list {*}[list-non-empty $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 { autosetup-error "unknown cctest setting: $name" } } } cc-store-settings [array get new] return $prev } proc cc-store-settings {new} { set ::autosetup(ccsettings) $new } proc cc-get-settings {} { return $::autosetup(ccsettings) } # Similar to cc-add-settings, but each given setting # simply replaces the existing value. # # Returns the previous settings proc cc-update-settings {args} { set prev [cc-get-settings] cc-store-settings [dict merge $prev $args] return $prev } # @cc-with settings ?{ script }? # # Sets the given 'cctest' settings and then runs the tests in '$script'. # Note that settings such as '-lang' replace the current setting, while # those such as '-includes' are appended to the existing setting. # # If no script is given, the settings become the default for the remainder # of the 'auto.def' file. # ## cc-with {-lang c++} { ## # This will check with the C++ compiler ## cc-check-types bool ## cc-with {-includes signal.h} { ## # This will check with the C++ compiler, signal.h and any existing includes. ## ... ## } ## # back to just the C++ compiler ## } # # The '-libs' setting is special in that newer values are added *before* earlier ones. # ## cc-with {-libs {-lc -lm}} { ## cc-with {-libs -ldl} { ## cctest -libs -lsocket ... ## # libs will be in this order: -lsocket -ldl -lc -lm ## } ## } proc cc-with {settings args} { if {[llength $args] == 0} { cc-add-settings $settings } elseif {[llength $args] > 1} { autosetup-error "usage: cc-with settings ?script?" } else { set save [cc-add-settings $settings] set rc [catch {uplevel 1 [lindex $args 0]} result info] cc-store-settings $save if {$rc != 0} { return -code [dict get $info -code] $result } return $result } } # @cctest ?settings? # # Low level C/C++ compiler checker. Compiles and or links a small C program # according to the arguments and returns 1 if OK, or 0 if not. # # Supported settings are: # ## -cflags cflags A list of flags to pass to the compiler ## -includes list A list of includes, e.g. {stdlib.h stdio.h} ## -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 */ ## ## int main(void) { ## code /* any code in -code, verbatim */ ## return 0; ## } # # Any failures are recorded in 'config.log' # proc cctest {args} { set tmp conftest__ # Easiest way to merge in the settings cc-with $args { array set opts [cc-get-settings] } if {[info exists opts(-sourcefile)]} { set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"] } if {[info exists opts(-source)]} { set lines $opts(-source) } else { foreach i $opts(-includes) { if {$opts(-code) ne "" && ![feature-checked $i]} { # Compiling real code with an unchecked header file # Quickly (and silently) check for it now # Remove all -includes from settings before checking set saveopts [cc-update-settings -includes {}] msg-quiet cc-check-includes $i cc-store-settings $saveopts } if {$opts(-code) eq "" || [have-feature $i]} { lappend source "#include <$i>" } } lappend source {*}$opts(-declare) lappend source "int main(void) {" lappend source $opts(-code) lappend source "return 0;" lappend source "}" set lines [join $source \n] } # Build the command line set cmdline {} lappend cmdline {*}[get-define CCACHE] switch -exact -- $opts(-lang) { c++ { set src conftest__.cpp lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS] } c { set src conftest__.c lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS] } default { autosetup-error "cctest called with unknown language: $opts(-lang)" } } if {$opts(-link)} { lappend cmdline {*}[get-define LDFLAGS] } else { set tmp conftest__.o lappend cmdline -c } lappend cmdline {*}$opts(-cflags) {*}[get-define cc-default-debug ""] lappend cmdline $src -o $tmp {*}$opts(-libs) if {$opts(-link)} { lappend cmdline {*}[get-define LIBS] } # At this point we have the complete command line and the # complete source to be compiled. Get the result from cache if # we can if {[info exists ::cc_cache($cmdline,$lines)]} { msg-checking "(cached) " set ok $::cc_cache($cmdline,$lines) if {$::autosetup(debug)} { configlog "From cache (ok=$ok): [join $cmdline]" configlog "============" configlog $lines configlog "============" } 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 } elseif {$::autosetup(debug)} { configlog "Compiled OK: [join $cmdline]" configlog "============" configlog $lines configlog "============" } file delete $src file delete $tmp # cache it set ::cc_cache($cmdline,$lines) $ok return $ok } # @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*? # # Deprecated - see 'make-config-header' proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} { user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead" make-config-header $file -auto $autopatterns -bare $barepatterns } # @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ... # # Examines all defined variables which match the given patterns # and writes an include file, '$file', which defines each of these. # Variables which match '-auto' are output as follows: # - defines which have the value '0' are ignored. # - defines which have integer values are defined as the integer value. # - any other value is defined as a string, e.g. '"value"' # Variables which match '-bare' are defined as-is. # Variables which match '-str' are defined as a string, e.g. '"value"' # Variables which match '-none' are omitted. # # Note that order is important. The first pattern that matches is selected. # Default behaviour is: # ## -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none * # # If the file would be unchanged, it is not written. proc make-config-header {file args} { set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]] file mkdir [file dirname $file] set lines {} lappend lines "#ifndef $guard" lappend lines "#define $guard" # Add some defaults lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* foreach n [lsort [dict keys [all-defines]]] { set value [get-define $n] set type [calc-define-output-type $n $args] switch -exact -- $type { -bare { # Just output the value unchanged } -none { continue } -str { set value \"[string map [list \\ \\\\ \" \\\"] $value]\" } -auto { # Automatically determine the type if {$value eq "0"} { lappend lines "/* #undef $n */" continue } if {![string is integer -strict $value]} { set value \"[string map [list \\ \\\\ \" \\\"] $value]\" } } "" { continue } default { autosetup-error "Unknown type in make-config-header: $type" } } lappend lines "#define $n $value" } lappend lines "#endif" set buf [join $lines \n] write-if-changed $file $buf { msg-result "Created $file" } } proc calc-define-output-type {name spec} { foreach {type patterns} $spec { foreach pattern $patterns { if {[string match $pattern $name]} { return $type } } } return "" } # Initialise some values from the environment or commandline or default settings foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS {CFLAGS "-g -O2"}} { lassign $i var default define $var [get-env $var $default] } if {[env-is-set CC]} { # Set by the user, so don't try anything else set try [list [get-env CC ""]] } else { # Try some reasonable options set try [list [get-define cross]cc [get-define cross]gcc] } define CC [find-an-executable {*}$try] if {[get-define CC] eq ""} { user-error "Could not find a C compiler. Tried: [join $try ", "]" } define CPP [get-env CPP "[get-define CC] -E"] # XXX: Could avoid looking for a C++ compiler until requested # Note that if CXX isn't found, we just set it to "false". It might not be needed. if {[env-is-set CXX]} { define CXX [find-an-executable -required [get-env CXX ""]] } else { define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++ false] } # CXXFLAGS default to CFLAGS if not specified define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]] # May need a CC_FOR_BUILD, so look for one define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false] 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]] # If any of these are set in the environment, propagate them to the AUTOREMAKE commandline foreach i {CC CXX CCACHE CPP CFLAGS CXXFLAGS CXXFLAGS LDFLAGS LIBS CROSS CPPFLAGS LINKFLAGS CC_FOR_BUILD LD} { if {[env-is-set $i]} { # Note: If the variable is set on the command line, get-env will return that value # so the command line will continue to override the environment define-append AUTOREMAKE [quote-if-needed $i=[get-env $i ""]] } } # 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" } |
Added autosetup/default.auto.
> > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Copyright (c) 2012 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Auto-load module for 'make' build system integration use init autosetup_add_init_type make {Simple "make" build system} { autosetup_check_create auto.def \ {# Initial auto.def created by 'autosetup --init=make' use cc # Add any user options here options { } make-config-header config.h make-template Makefile.in } if {![file exists Makefile.in]} { puts "Note: I don't see Makefile.in. You will probably need to create one." } } |
Added autosetup/jimsh0.c.
more than 10,000 changes
Added autosetup/local.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 | # For this project, disable the pager for --help and --ref # The user can still enable by using --nopager=0 or --disable-nopager dict set autosetup(optdefault) nopager 1 # Searches for a usable Tcl (prefer 8.6, 8.5, 8.4) in the given paths # Returns a dictionary of the contents of the tclConfig.sh file, or # empty if not found proc parse-tclconfig-sh {args} { foreach p $args { # Allow pointing directly to the path containing tclConfig.sh if {[file exists $p/tclConfig.sh]} { return [parse-tclconfig-sh-file $p/tclConfig.sh] } # Some systems allow for multiple versions foreach libpath {lib/tcl8.6 lib/tcl8.5 lib/tcl8.4 lib/tcl tcl lib} { if {[file exists $p/$libpath/tclConfig.sh]} { return [parse-tclconfig-sh-file $p/$libpath/tclConfig.sh] } } } } proc parse-tclconfig-sh-file {filename} { foreach line [split [readfile $filename] \n] { if {[regexp {^(TCL_[^=]*)=(.*)$} $line -> name value]} { set value [regsub -all {\$\{.*\}} $value ""] set tclconfig($name) [string trim $value '] } } return [array get tclconfig] } |
Added autosetup/pkg-config.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 | # Copyright (c) 2016 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'pkg-config' module allows package information to be found via 'pkg-config'. # # If not cross-compiling, the package path should be determined automatically # by 'pkg-config'. # If cross-compiling, the default package path is the compiler sysroot. # If the C compiler doesn't support '-print-sysroot', the path can be supplied # by the '--sysroot' option or by defining 'SYSROOT'. # # 'PKG_CONFIG' may be set to use an alternative to 'pkg-config'. use cc module-options { sysroot:dir => "Override compiler sysroot for pkg-config search path" } # @pkg-config-init ?required? # # Initialises the 'pkg-config' system. Unless '$required' is set to 0, # it is a fatal error if a usable 'pkg-config' is not found . # # This command will normally be called automatically as required, # but it may be invoked explicitly if lack of 'pkg-config' is acceptable. # # Returns 1 if ok, or 0 if 'pkg-config' not found/usable (only if '$required' is 0). # proc pkg-config-init {{required 1}} { if {[is-defined HAVE_PKG_CONFIG]} { return [get-define HAVE_PKG_CONFIG] } set found 0 define PKG_CONFIG [get-env PKG_CONFIG pkg-config] msg-checking "Checking for pkg-config..." if {[catch {exec [get-define PKG_CONFIG] --version} version]} { msg-result "[get-define PKG_CONFIG] (not found)" if {$required} { user-error "No usable pkg-config" } } else { msg-result $version define PKG_CONFIG_VERSION $version set found 1 if {[opt-str sysroot o]} { define SYSROOT [file-normalize $o] msg-result "Using specified sysroot [get-define SYSROOT]" } elseif {[get-define build] ne [get-define host]} { if {[catch {exec-with-stderr [get-define CC] -print-sysroot} result errinfo] == 0} { # Use the compiler sysroot, if there is one define SYSROOT $result msg-result "Found compiler sysroot $result" } else { set msg "pkg-config: Cross compiling, but no compiler sysroot and no --sysroot supplied" if {$required} { user-error $msg } else { msg-result $msg } set found 0 } } if {[is-defined SYSROOT]} { set sysroot [get-define SYSROOT] # XXX: It's possible that these should be set only when invoking pkg-config global env set env(PKG_CONFIG_DIR) "" # Do we need to try /usr/local as well or instead? set env(PKG_CONFIG_LIBDIR) $sysroot/usr/lib/pkgconfig:$sysroot/usr/share/pkgconfig set env(PKG_CONFIG_SYSROOT_DIR) $sysroot } } define HAVE_PKG_CONFIG $found return $found } # @pkg-config module ?requirements? # # Use 'pkg-config' to find the given module meeting the given requirements. # e.g. # ## pkg-config pango >= 1.37.0 # # If found, returns 1 and sets 'HAVE_PKG_PANGO' to 1 along with: # ## PKG_PANGO_VERSION to the found version ## PKG_PANGO_LIBS to the required libs (--libs-only-l) ## PKG_PANGO_LDFLAGS to the required linker flags (--libs-only-L) ## PKG_PANGO_CFLAGS to the required compiler flags (--cflags) # # If not found, returns 0. # proc pkg-config {module args} { set ok [pkg-config-init] msg-checking "Checking for $module $args..." if {!$ok} { msg-result "no pkg-config" return 0 } if {[catch {exec [get-define PKG_CONFIG] --modversion "$module $args"} version]} { msg-result "not found" configlog "pkg-config --modversion $module $args: $version" return 0 } msg-result $version set prefix [feature-define-name $module PKG_] define HAVE_${prefix} define ${prefix}_VERSION $version define ${prefix}_LIBS [exec pkg-config --libs-only-l $module] define ${prefix}_LDFLAGS [exec pkg-config --libs-only-L $module] define ${prefix}_CFLAGS [exec pkg-config --cflags $module] return 1 } # @pkg-config-get module setting # # Convenience access to the results of 'pkg-config'. # # For example, '[pkg-config-get pango CFLAGS]' returns # the value of 'PKG_PANGO_CFLAGS', or '""' if not defined. proc pkg-config-get {module name} { set prefix [feature-define-name $module PKG_] get-define ${prefix}_${name} "" } |
Added autosetup/system.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 398 399 400 401 402 403 404 405 406 407 | # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # This module supports common system interrogation and options # such as '--host', '--build', '--prefix', and setting 'srcdir', 'builddir', and 'EXEEXT'. # # It also support the "feature" naming convention, where searching # for a feature such as 'sys/type.h' defines 'HAVE_SYS_TYPES_H'. # # It defines the following variables, based on '--prefix' unless overridden by the user: # ## datadir ## sysconfdir ## sharedstatedir ## localstatedir ## infodir ## mandir ## includedir # # If '--prefix' is not supplied, it defaults to '/usr/local' unless 'defaultprefix' is defined *before* # including the 'system' module. if {[is-defined defaultprefix]} { user-notice "Note: defaultprefix is deprecated. Use options-defaults to set default options" options-defaults [list prefix [get-define defaultprefix]] } module-options [subst -noc -nob { host:host-alias => {a complete or partial cpu-vendor-opsys for the system where the application will run (defaults to the same value as --build)} build:build-alias => {a complete or partial cpu-vendor-opsys for the system where the application will be built (defaults to the result of running config.guess)} prefix:dir=/usr/local => {the target directory for the build (default: '@default@')} # These (hidden) options are supported for autoconf/automake compatibility exec-prefix: bindir: sbindir: includedir: mandir: infodir: libexecdir: datadir: libdir: sysconfdir: sharedstatedir: localstatedir: runstatedir: maintainer-mode=0 dependency-tracking=0 silent-rules=0 }] # @check-feature name { script } # # defines feature '$name' to the return value of '$script', # which should be 1 if found or 0 if not found. # # e.g. the following will define 'HAVE_CONST' to 0 or 1. # ## check-feature const { ## cctest -code {const int _x = 0;} ## } proc check-feature {name code} { msg-checking "Checking for $name..." set r [uplevel 1 $code] define-feature $name $r if {$r} { msg-result "ok" } else { msg-result "not found" } return $r } # @have-feature name ?default=0? # # Returns the value of feature '$name' if defined, or '$default' if not. # # See 'feature-define-name' for how the "feature" name # is translated into the "define" name. # proc have-feature {name {default 0}} { get-define [feature-define-name $name] $default } # @define-feature name ?value=1? # # Sets the feature 'define' to '$value'. # # See 'feature-define-name' for how the "feature" name # is translated into the "define" name. # proc define-feature {name {value 1}} { define [feature-define-name $name] $value } # @feature-checked name # # Returns 1 if feature '$name' has been checked, whether true or not. # proc feature-checked {name} { is-defined [feature-define-name $name] } # @feature-define-name name ?prefix=HAVE_? # # Converts a "feature" name to the corresponding "define", # e.g. 'sys/stat.h' becomes 'HAVE_SYS_STAT_H'. # # Converts '*' to 'P' and all non-alphanumeric to underscore. # proc feature-define-name {name {prefix HAVE_}} { string toupper $prefix[regsub -all {[^a-zA-Z0-9]} [regsub -all {[*]} $name p] _] } # @write-if-changed filename contents ?script? # # If '$filename' doesn't exist, or it's contents are different to '$contents', # the file is written and '$script' is evaluated. # # Otherwise a "file is unchanged" message is displayed. proc write-if-changed {file buf {script {}}} { set old [readfile $file ""] if {$old eq $buf && [file exists $file]} { msg-result "$file is unchanged" } else { writefile $file $buf\n uplevel 1 $script } } # @include-file infile mapping # # The core of make-template, called recursively for each @include # directive found within that template so that this proc's result # is the fully-expanded template. # # The mapping parameter is how we expand @varname@ within the template. # We do that inline within this step only for @include directives which # can have variables in the filename arg. A separate substitution pass # happens when this recursive function returns, expanding the rest of # the variables. # proc include-file {infile mapping} { # A stack of true/false conditions, one for each nested conditional # starting with "true" set condstack {1} set result {} set linenum 0 foreach line [split [readfile $infile] \n] { incr linenum if {[regexp {^@(if|else|endif)(\s*)(.*)} $line -> condtype condspace condargs]} { if {$condtype eq "if"} { if {[string length $condspace] == 0} { autosetup-error "$infile:$linenum: Invalid expression: $line" } if {[llength $condargs] == 1} { # ABC => [get-define ABC] ni {0 ""} # !ABC => [get-define ABC] in {0 ""} lassign $condargs condvar if {[regexp {^!(.*)} $condvar -> condvar]} { set op in } else { set op ni } set condexpr "\[[list get-define $condvar]\] $op {0 {}}" } else { # Translate alphanumeric ABC into [get-define ABC] and leave the # rest of the expression untouched regsub -all {([A-Z][[:alnum:]_]*)} $condargs {[get-define \1]} condexpr } if {[catch [list expr $condexpr] condval]} { dputs $condval autosetup-error "$infile:$linenum: Invalid expression: $line" } dputs "@$condtype: $condexpr => $condval" } if {$condtype ne "if"} { if {[llength $condstack] <= 1} { autosetup-error "$infile:$linenum: Error: @$condtype missing @if" } elseif {[string length $condargs] && [string index $condargs 0] ne "#"} { autosetup-error "$infile:$linenum: Error: Extra arguments after @$condtype" } } switch -exact $condtype { if { # push condval lappend condstack $condval } else { # Toggle the last entry set condval [lpop condstack] set condval [expr {!$condval}] lappend condstack $condval } endif { if {[llength $condstack] == 0} { user-notice "$infile:$linenum: Error: @endif missing @if" } lpop condstack } } continue } elseif {[regexp {^@include\s+(.*)} $line -> filearg]} { set incfile [string map $mapping $filearg] if {[file exists $incfile]} { lappend ::autosetup(deps) [file-normalize $incfile] lappend result {*}[include-file $incfile $mapping] } else { user-error "$infile:$linenum: Include file $incfile is missing" } continue } elseif {[regexp {^@define\s+(\w+)\s+(.*)} $line -> var val]} { define $var $val continue } # Only output this line if the stack contains all "true" if {"0" in $condstack} { continue } lappend result $line } return $result } # @make-template template ?outfile? # # Reads the input file '<srcdir>/$template' and writes the output file '$outfile' # (unless unchanged). # If '$outfile' is blank/omitted, '$template' should end with '.in' which # is removed to create the output file name. # # Each pattern of the form '@define@' is replaced with the corresponding # "define", if it exists, or left unchanged if not. # # The special value '@srcdir@' is substituted with the relative # path to the source directory from the directory where the output # file is created, while the special value '@top_srcdir@' is substituted # with the relative path to the top level source directory. # # Conditional sections may be specified as follows: ## @if NAME eq "value" ## lines ## @else ## lines ## @endif # # Where 'NAME' is a defined variable name and '@else' is optional. # Note that variables names *must* start with an uppercase letter. # If the expression does not match, all lines through '@endif' are ignored. # # The alternative forms may also be used: ## @if NAME (true if the variable is defined, but not empty and not "0") ## @if !NAME (opposite of the form above) ## @if <general-tcl-expression> # # In the general Tcl expression, any words beginning with an uppercase letter # are translated into [get-define NAME] # # Expressions may be nested # proc make-template {template {out {}}} { set infile [file join $::autosetup(srcdir) $template] if {![file exists $infile]} { user-error "Template $template is missing" } # Define this as late as possible define AUTODEPS $::autosetup(deps) if {$out eq ""} { if {[file ext $template] ne ".in"} { autosetup-error "make_template $template has no target file and can't guess" } set out [file rootname $template] } set outdir [file dirname $out] # Make sure the directory exists file mkdir $outdir # Set up srcdir and top_srcdir to be relative to the target dir define srcdir [relative-path [file join $::autosetup(srcdir) $outdir] $outdir] define top_srcdir [relative-path $::autosetup(srcdir) $outdir] # Build map from global defines to their values so they can be # substituted into @include file names. proc build-define-mapping {} { set mapping {} foreach {n v} [array get ::define] { lappend mapping @$n@ $v } return $mapping } set mapping [build-define-mapping] set result [include-file $infile $mapping] # Rebuild the define mapping in case we ran across @define # directives in the template or a file it @included, then # apply that mapping to the expanded template. set mapping [build-define-mapping] write-if-changed $out [string map $mapping [join $result \n]] { msg-result "Created [relative-path $out] from [relative-path $template]" } } # build/host tuples and cross-compilation prefix opt-str build build "" define build_alias $build if {$build eq ""} { define build [config_guess] } else { define build [config_sub $build] } opt-str host host "" define host_alias $host if {$host eq ""} { define host [get-define build] set cross "" } else { define host [config_sub $host] set cross $host- } define cross [get-env CROSS $cross] # build/host _cpu, _vendor and _os foreach type {build host} { set v [get-define $type] if {![regexp {^([^-]+)-([^-]+)-(.*)$} $v -> cpu vendor os]} { user-error "Invalid canonical $type: $v" } define ${type}_cpu $cpu define ${type}_vendor $vendor define ${type}_os $os } opt-str prefix prefix /usr/local # These are for compatibility with autoconf define target [get-define host] define prefix $prefix define builddir $autosetup(builddir) define srcdir $autosetup(srcdir) define top_srcdir $autosetup(srcdir) define abs_top_srcdir [file-normalize $autosetup(srcdir)] define abs_top_builddir [file-normalize $autosetup(builddir)] # autoconf supports all of these define exec_prefix [opt-str exec-prefix exec_prefix $prefix] foreach {name defpath} { bindir /bin sbindir /sbin libexecdir /libexec libdir /lib } { define $name [opt-str $name o $exec_prefix$defpath] } foreach {name defpath} { datadir /share sharedstatedir /com infodir /share/info mandir /share/man includedir /include } { define $name [opt-str $name o $prefix$defpath] } if {$prefix ne {/usr}} { opt-str sysconfdir sysconfdir $prefix/etc } else { opt-str sysconfdir sysconfdir /etc } define sysconfdir $sysconfdir define localstatedir [opt-str localstatedir o /var] define runstatedir [opt-str runstatedir o /run] define SHELL [get-env SHELL [find-an-executable sh bash ksh]] # These could be used to generate Makefiles following some automake conventions define AM_SILENT_RULES [opt-bool silent-rules] define AM_MAINTAINER_MODE [opt-bool maintainer-mode] define AM_DEPENDENCY_TRACKING [opt-bool dependency-tracking] # Windows vs. non-Windows switch -glob -- [get-define host] { *-*-ming* - *-*-cygwin - *-*-msys { define-feature windows define EXEEXT .exe } default { define EXEEXT "" } } # Display msg-result "Host System...[get-define host]" msg-result "Build System...[get-define build]" |
Added autosetup/tmake.auto.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Copyright (c) 2016 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Auto-load module for 'tmake' build system integration use init autosetup_add_init_type tmake "Tcl-based tmake build system" { autosetup_check_create auto.def \ {# Initial auto.def created by 'autosetup --init=tmake' # vim:set syntax=tcl: use cc cc-lib cc-db cc-shared use tmake # Add any user options here # Really want a --configure that takes over the rest of the command line options { } cc-check-tools ar ranlib set objdir [get-env BUILDDIR objdir] make-config-header $objdir/include/autoconf.h make-tmake-settings $objdir/settings.conf {[A-Z]*} *dir lib_* } autosetup_check_create project.spec \ {# Initial project.spec created by 'autosetup --init=tmake' tmake-require-version 0.7.3 # vim:set syntax=tcl: define? DESTDIR _install # XXX If configure creates additional/different files than include/autoconf.h # that should be reflected here Autosetup include/autoconf.h # e.g. for autoconf.h IncludePaths include ifconfig !CONFIGURED { # Not configured, so don't process subdirs AutoSubDirs off # And don't process this file any further ifconfig false } } if {![file exists build.spec]} { puts "Note: I don't see build.spec. Try running: tmake --genie" } } |
Added autosetup/tmake.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 | # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'tmake' module makes it easy to support the tmake build system. # # The following variables are set: # ## CONFIGURED - to indicate that the project is configured use system module-options {} define CONFIGURED # @make-tmake-settings outfile patterns ... # # Examines all defined variables which match the given patterns (defaults to '*') # and writes a tmake-compatible .conf file defining those variables. # For example, if 'ABC' is '"3 monkeys"' and 'ABC' matches a pattern, then the file will include: # ## define ABC {3 monkeys} # # If the file would be unchanged, it is not written. # # Typical usage is: # ## make-tmake-settings [get-env BUILDDIR objdir]/settings.conf {[A-Z]*} proc make-tmake-settings {file args} { file mkdir [file dirname $file] set lines {} if {[llength $args] == 0} { set args * } foreach n [lsort [dict keys [all-defines]]] { foreach p $args { if {[string match $p $n]} { set value [get-define $n] lappend lines "define $n [list $value]" break } } } set buf [join $lines \n] write-if-changed $file $buf { msg-result "Created $file" } } |
Deleted ci_cvs.txt.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted ci_fossil.txt.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added compat/tcl-8.6/generic/tcl.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* * tcl.h -- * * This header file describes the externally-visible facilities of the * Tcl interpreter. * * Copyright (c) 1987-1994 The Regents of the University of California. * Copyright (c) 1993-1996 Lucent Technologies. * Copyright (c) 1994-1998 Sun Microsystems, Inc. * Copyright (c) 1998-2000 by Scriptics Corporation. * Copyright (c) 2002 by Kevin B. Kenny. All rights reserved. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #ifndef _TCL #define _TCL /* * For C++ compilers, use extern "C" */ #ifdef __cplusplus extern "C" { #endif /* * The following defines are used to indicate the various release levels. */ #define TCL_ALPHA_RELEASE 0 #define TCL_BETA_RELEASE 1 #define TCL_FINAL_RELEASE 2 /* * When version numbers change here, must also go into the following files and * update the version numbers: * * library/init.tcl (1 LOC patch) * unix/configure.in (2 LOC Major, 2 LOC minor, 1 LOC patch) * win/configure.in (as above) * win/tcl.m4 (not patchlevel) * win/makefile.bc (not patchlevel) 2 LOC * README (sections 0 and 2, with and without separator) * macosx/Tcl.pbproj/project.pbxproj (not patchlevel) 1 LOC * macosx/Tcl.pbproj/default.pbxuser (not patchlevel) 1 LOC * macosx/Tcl.xcode/project.pbxproj (not patchlevel) 2 LOC * macosx/Tcl.xcode/default.pbxuser (not patchlevel) 1 LOC * macosx/Tcl-Common.xcconfig (not patchlevel) 1 LOC * win/README (not patchlevel) (sections 0 and 2) * unix/tcl.spec (1 LOC patch) * tools/tcl.hpj.in (not patchlevel, for windows installer) */ #define TCL_MAJOR_VERSION 8 #define TCL_MINOR_VERSION 6 #define TCL_RELEASE_LEVEL TCL_FINAL_RELEASE #define TCL_RELEASE_SERIAL 0 #define TCL_VERSION "8.6" #define TCL_PATCH_LEVEL "8.6.0" /* *---------------------------------------------------------------------------- * The following definitions set up the proper options for Windows compilers. * We use this method because there is no autoconf equivalent. */ #ifndef __WIN32__ # if defined(_WIN32) || defined(WIN32) || defined(__MINGW32__) || defined(__BORLANDC__) || (defined(__WATCOMC__) && defined(__WINDOWS_386__)) # define __WIN32__ # ifndef WIN32 # define WIN32 # endif # ifndef _WIN32 # define _WIN32 # endif # endif #endif /* * STRICT: See MSDN Article Q83456 */ #ifdef __WIN32__ # ifndef STRICT # define STRICT # endif #endif /* __WIN32__ */ /* * Utility macros: STRINGIFY takes an argument and wraps it in "" (double * quotation marks), JOIN joins two arguments. */ #ifndef STRINGIFY # define STRINGIFY(x) STRINGIFY1(x) # define STRINGIFY1(x) #x #endif #ifndef JOIN # define JOIN(a,b) JOIN1(a,b) # define JOIN1(a,b) a##b #endif /* * A special definition used to allow this header file to be included from * windows resource files so that they can obtain version information. * RC_INVOKED is defined by default by the windows RC tool. * * Resource compilers don't like all the C stuff, like typedefs and function * declarations, that occur below, so block them out. */ #ifndef RC_INVOKED /* * Special macro to define mutexes, that doesn't do anything if we are not * using threads. */ #ifdef TCL_THREADS #define TCL_DECLARE_MUTEX(name) static Tcl_Mutex name; #else #define TCL_DECLARE_MUTEX(name) #endif /* * Tcl's public routine Tcl_FSSeek() uses the values SEEK_SET, SEEK_CUR, and * SEEK_END, all #define'd by stdio.h . * * Also, many extensions need stdio.h, and they've grown accustomed to tcl.h * providing it for them rather than #include-ing it themselves as they * should, so also for their sake, we keep the #include to be consistent with * prior Tcl releases. */ #include <stdio.h> /* *---------------------------------------------------------------------------- * Support for functions with a variable number of arguments. * * The following TCL_VARARGS* macros are to support old extensions * written for older versions of Tcl where the macros permitted * support for the varargs.h system as well as stdarg.h . * * New code should just directly be written to use stdarg.h conventions. */ #include <stdarg.h> #ifndef TCL_NO_DEPRECATED # define TCL_VARARGS(type, name) (type name, ...) # define TCL_VARARGS_DEF(type, name) (type name, ...) # define TCL_VARARGS_START(type, name, list) (va_start(list, name), name) #endif #if defined(__GNUC__) && (__GNUC__ > 2) # define TCL_FORMAT_PRINTF(a,b) __attribute__ ((__format__ (__printf__, a, b))) #else # define TCL_FORMAT_PRINTF(a,b) #endif /* * Allow a part of Tcl's API to be explicitly marked as deprecated. * * Used to make TIP 330/336 generate moans even if people use the * compatibility macros. Change your code, guys! We won't support you forever. */ #if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC__MINOR__ >= 5)) # define TCL_DEPRECATED_API(msg) __attribute__ ((__deprecated__ (msg))) # else # define TCL_DEPRECATED_API(msg) __attribute__ ((__deprecated__)) # endif #else # define TCL_DEPRECATED_API(msg) /* nothing portable */ #endif /* *---------------------------------------------------------------------------- * Macros used to declare a function to be exported by a DLL. Used by Windows, * maps to no-op declarations on non-Windows systems. The default build on * windows is for a DLL, which causes the DLLIMPORT and DLLEXPORT macros to be * nonempty. To build a static library, the macro STATIC_BUILD should be * defined. * * Note: when building static but linking dynamically to MSVCRT we must still * correctly decorate the C library imported function. Use CRTIMPORT * for this purpose. _DLL is defined by the compiler when linking to * MSVCRT. */ #if (defined(__WIN32__) && (defined(_MSC_VER) || (__BORLANDC__ >= 0x0550) || defined(__LCC__) || defined(__WATCOMC__) || (defined(__GNUC__) && defined(__declspec)))) # define HAVE_DECLSPEC 1 # ifdef STATIC_BUILD # define DLLIMPORT # define DLLEXPORT # ifdef _DLL # define CRTIMPORT __declspec(dllimport) # else # define CRTIMPORT # endif # else # define DLLIMPORT __declspec(dllimport) # define DLLEXPORT __declspec(dllexport) # define CRTIMPORT __declspec(dllimport) # endif #else # define DLLIMPORT # if defined(__GNUC__) && __GNUC__ > 3 # define DLLEXPORT __attribute__ ((visibility("default"))) # else # define DLLEXPORT # endif # define CRTIMPORT #endif /* * These macros are used to control whether functions are being declared for * import or export. If a function is being declared while it is being built * to be included in a shared library, then it should have the DLLEXPORT * storage class. If is being declared for use by a module that is going to * link against the shared library, then it should have the DLLIMPORT storage * class. If the symbol is beind declared for a static build or for use from a * stub library, then the storage class should be empty. * * The convention is that a macro called BUILD_xxxx, where xxxx is the name of * a library we are building, is set on the compile line for sources that are * to be placed in the library. When this macro is set, the storage class will * be set to DLLEXPORT. At the end of the header file, the storage class will * be reset to DLLIMPORT. */ #undef TCL_STORAGE_CLASS #ifdef BUILD_tcl # define TCL_STORAGE_CLASS DLLEXPORT #else # ifdef USE_TCL_STUBS # define TCL_STORAGE_CLASS # else # define TCL_STORAGE_CLASS DLLIMPORT # endif #endif /* * The following _ANSI_ARGS_ macro is to support old extensions * written for older versions of Tcl where it permitted support * for compilers written in the pre-prototype era of C. * * New code should use prototypes. */ #ifndef TCL_NO_DEPRECATED # undef _ANSI_ARGS_ # define _ANSI_ARGS_(x) x #endif /* * Definitions that allow this header file to be used either with or without * ANSI C features. */ #ifndef INLINE # define INLINE #endif #ifdef NO_CONST # ifndef const # define const # endif #endif #ifndef CONST # define CONST const #endif #ifdef USE_NON_CONST # ifdef USE_COMPAT_CONST # error define at most one of USE_NON_CONST and USE_COMPAT_CONST # endif # define CONST84 # define CONST84_RETURN #else # ifdef USE_COMPAT_CONST # define CONST84 # define CONST84_RETURN const # else # define CONST84 const # define CONST84_RETURN const # endif #endif #ifndef CONST86 # define CONST86 CONST84 #endif /* * Make sure EXTERN isn't defined elsewhere. */ #ifdef EXTERN # undef EXTERN #endif /* EXTERN */ #ifdef __cplusplus # define EXTERN extern "C" TCL_STORAGE_CLASS #else # define EXTERN extern TCL_STORAGE_CLASS #endif /* *---------------------------------------------------------------------------- * The following code is copied from winnt.h. If we don't replicate it here, * then <windows.h> can't be included after tcl.h, since tcl.h also defines * VOID. This block is skipped under Cygwin and Mingw. */ #if defined(__WIN32__) && !defined(HAVE_WINNT_IGNORE_VOID) #ifndef VOID #define VOID void typedef char CHAR; typedef short SHORT; typedef long LONG; #endif #endif /* __WIN32__ && !HAVE_WINNT_IGNORE_VOID */ /* * Macro to use instead of "void" for arguments that must have type "void *" * in ANSI C; maps them to type "char *" in non-ANSI systems. */ #ifndef NO_VOID # define VOID void #else # define VOID char #endif /* * Miscellaneous declarations. */ #ifndef _CLIENTDATA # ifndef NO_VOID typedef void *ClientData; # else typedef int *ClientData; # endif # define _CLIENTDATA #endif /* * Darwin specific configure overrides (to support fat compiles, where * configure runs only once for multiple architectures): */ #ifdef __APPLE__ # ifdef __LP64__ # undef TCL_WIDE_INT_TYPE # define TCL_WIDE_INT_IS_LONG 1 # define TCL_CFG_DO64BIT 1 # else /* !__LP64__ */ # define TCL_WIDE_INT_TYPE long long # undef TCL_WIDE_INT_IS_LONG # undef TCL_CFG_DO64BIT # endif /* __LP64__ */ # undef HAVE_STRUCT_STAT64 #endif /* __APPLE__ */ /* * Define Tcl_WideInt to be a type that is (at least) 64-bits wide, and define * Tcl_WideUInt to be the unsigned variant of that type (assuming that where * we have one, we can have the other.) * * Also defines the following macros: * TCL_WIDE_INT_IS_LONG - if wide ints are really longs (i.e. we're on a real * 64-bit system.) * Tcl_WideAsLong - forgetful converter from wideInt to long. * Tcl_LongAsWide - sign-extending converter from long to wideInt. * Tcl_WideAsDouble - converter from wideInt to double. * Tcl_DoubleAsWide - converter from double to wideInt. * * The following invariant should hold for any long value 'longVal': * longVal == Tcl_WideAsLong(Tcl_LongAsWide(longVal)) * * Note on converting between Tcl_WideInt and strings. This implementation (in * tclObj.c) depends on the function * sprintf(...,"%" TCL_LL_MODIFIER "d",...). */ #if !defined(TCL_WIDE_INT_TYPE)&&!defined(TCL_WIDE_INT_IS_LONG) # if defined(__WIN32__) # define TCL_WIDE_INT_TYPE __int64 # ifdef __BORLANDC__ # define TCL_LL_MODIFIER "L" # else /* __BORLANDC__ */ # define TCL_LL_MODIFIER "I64" # endif /* __BORLANDC__ */ # elif defined(__GNUC__) # define TCL_WIDE_INT_TYPE long long # define TCL_LL_MODIFIER "ll" # else /* ! __WIN32__ && ! __GNUC__ */ /* * Don't know what platform it is and configure hasn't discovered what is * going on for us. Try to guess... */ # ifdef NO_LIMITS_H # error please define either TCL_WIDE_INT_TYPE or TCL_WIDE_INT_IS_LONG # else /* !NO_LIMITS_H */ # include <limits.h> # if (INT_MAX < LONG_MAX) # define TCL_WIDE_INT_IS_LONG 1 # else # define TCL_WIDE_INT_TYPE long long # endif # endif /* NO_LIMITS_H */ # endif /* __WIN32__ */ #endif /* !TCL_WIDE_INT_TYPE & !TCL_WIDE_INT_IS_LONG */ #ifdef TCL_WIDE_INT_IS_LONG # undef TCL_WIDE_INT_TYPE # define TCL_WIDE_INT_TYPE long #endif /* TCL_WIDE_INT_IS_LONG */ typedef TCL_WIDE_INT_TYPE Tcl_WideInt; typedef unsigned TCL_WIDE_INT_TYPE Tcl_WideUInt; #ifdef TCL_WIDE_INT_IS_LONG # define Tcl_WideAsLong(val) ((long)(val)) # define Tcl_LongAsWide(val) ((long)(val)) # define Tcl_WideAsDouble(val) ((double)((long)(val))) # define Tcl_DoubleAsWide(val) ((long)((double)(val))) # ifndef TCL_LL_MODIFIER # define TCL_LL_MODIFIER "l" # endif /* !TCL_LL_MODIFIER */ #else /* TCL_WIDE_INT_IS_LONG */ /* * The next short section of defines are only done when not running on Windows * or some other strange platform. */ # ifndef TCL_LL_MODIFIER # define TCL_LL_MODIFIER "ll" # endif /* !TCL_LL_MODIFIER */ # define Tcl_WideAsLong(val) ((long)((Tcl_WideInt)(val))) # define Tcl_LongAsWide(val) ((Tcl_WideInt)((long)(val))) # define Tcl_WideAsDouble(val) ((double)((Tcl_WideInt)(val))) # define Tcl_DoubleAsWide(val) ((Tcl_WideInt)((double)(val))) #endif /* TCL_WIDE_INT_IS_LONG */ #if defined(__WIN32__) # ifdef __BORLANDC__ typedef struct stati64 Tcl_StatBuf; # elif defined(_WIN64) typedef struct __stat64 Tcl_StatBuf; # elif (defined(_MSC_VER) && (_MSC_VER < 1400)) || defined(_USE_32BIT_TIME_T) typedef struct _stati64 Tcl_StatBuf; # else typedef struct _stat32i64 Tcl_StatBuf; # endif /* _MSC_VER < 1400 */ #elif defined(__CYGWIN__) typedef struct _stat32i64 { dev_t st_dev; unsigned short st_ino; unsigned short st_mode; short st_nlink; short st_uid; short st_gid; /* Here is a 2-byte gap */ dev_t st_rdev; /* Here is a 4-byte gap */ long long st_size; struct {long tv_sec;} st_atim; struct {long tv_sec;} st_mtim; struct {long tv_sec;} st_ctim; /* Here is a 4-byte gap */ } Tcl_StatBuf; #elif defined(HAVE_STRUCT_STAT64) typedef struct stat64 Tcl_StatBuf; #else typedef struct stat Tcl_StatBuf; #endif /* *---------------------------------------------------------------------------- * Data structures defined opaquely in this module. The definitions below just * provide dummy types. A few fields are made visible in Tcl_Interp * structures, namely those used for returning a string result from commands. * Direct access to the result field is discouraged in Tcl 8.0. The * interpreter result is either an object or a string, and the two values are * kept consistent unless some C code sets interp->result directly. * Programmers should use either the function Tcl_GetObjResult() or * Tcl_GetStringResult() to read the interpreter's result. See the SetResult * man page for details. * * Note: any change to the Tcl_Interp definition below must be mirrored in the * "real" definition in tclInt.h. * * Note: Tcl_ObjCmdProc functions do not directly set result and freeProc. * Instead, they set a Tcl_Obj member in the "real" structure that can be * accessed with Tcl_GetObjResult() and Tcl_SetObjResult(). */ typedef struct Tcl_Interp #ifndef TCL_NO_DEPRECATED { /* TIP #330: Strongly discourage extensions from using the string * result. */ #ifdef USE_INTERP_RESULT char *result TCL_DEPRECATED_API("use Tcl_GetResult/Tcl_SetResult"); /* If the last command returned a string * result, this points to it. */ void (*freeProc) (char *blockPtr) TCL_DEPRECATED_API("use Tcl_GetResult/Tcl_SetResult"); /* Zero means the string result is statically * allocated. TCL_DYNAMIC means it was * allocated with ckalloc and should be freed * with ckfree. Other values give the address * of function to invoke to free the result. * Tcl_Eval must free it before executing next * command. */ #else char *resultDontUse; /* Don't use in extensions! */ void (*freeProcDontUse) (char *); /* Don't use in extensions! */ #endif #ifdef USE_INTERP_ERRORLINE int errorLine TCL_DEPRECATED_API("use Tcl_GetErrorLine/Tcl_SetErrorLine"); /* When TCL_ERROR is returned, this gives the * line number within the command where the * error occurred (1 if first line). */ #else int errorLineDontUse; /* Don't use in extensions! */ #endif } #endif /* TCL_NO_DEPRECATED */ Tcl_Interp; typedef struct Tcl_AsyncHandler_ *Tcl_AsyncHandler; typedef struct Tcl_Channel_ *Tcl_Channel; typedef struct Tcl_ChannelTypeVersion_ *Tcl_ChannelTypeVersion; typedef struct Tcl_Command_ *Tcl_Command; typedef struct Tcl_Condition_ *Tcl_Condition; typedef struct Tcl_Dict_ *Tcl_Dict; typedef struct Tcl_EncodingState_ *Tcl_EncodingState; typedef struct Tcl_Encoding_ *Tcl_Encoding; typedef struct Tcl_Event Tcl_Event; typedef struct Tcl_InterpState_ *Tcl_InterpState; typedef struct Tcl_LoadHandle_ *Tcl_LoadHandle; typedef struct Tcl_Mutex_ *Tcl_Mutex; typedef struct Tcl_Pid_ *Tcl_Pid; typedef struct Tcl_RegExp_ *Tcl_RegExp; typedef struct Tcl_ThreadDataKey_ *Tcl_ThreadDataKey; typedef struct Tcl_ThreadId_ *Tcl_ThreadId; typedef struct Tcl_TimerToken_ *Tcl_TimerToken; typedef struct Tcl_Trace_ *Tcl_Trace; typedef struct Tcl_Var_ *Tcl_Var; typedef struct Tcl_ZLibStream_ *Tcl_ZlibStream; /* *---------------------------------------------------------------------------- * Definition of the interface to functions implementing threads. A function * following this definition is given to each call of 'Tcl_CreateThread' and * will be called as the main fuction of the new thread created by that call. */ #if defined __WIN32__ typedef unsigned (__stdcall Tcl_ThreadCreateProc) (ClientData clientData); #else typedef void (Tcl_ThreadCreateProc) (ClientData clientData); #endif /* * Threading function return types used for abstracting away platform * differences when writing a Tcl_ThreadCreateProc. See the NewThread function * in generic/tclThreadTest.c for it's usage. */ #if defined __WIN32__ # define Tcl_ThreadCreateType unsigned __stdcall # define TCL_THREAD_CREATE_RETURN return 0 #else # define Tcl_ThreadCreateType void # define TCL_THREAD_CREATE_RETURN #endif /* * Definition of values for default stacksize and the possible flags to be * given to Tcl_CreateThread. */ #define TCL_THREAD_STACK_DEFAULT (0) /* Use default size for stack. */ #define TCL_THREAD_NOFLAGS (0000) /* Standard flags, default * behaviour. */ #define TCL_THREAD_JOINABLE (0001) /* Mark the thread as joinable. */ /* * Flag values passed to Tcl_StringCaseMatch. */ #define TCL_MATCH_NOCASE (1<<0) /* * Flag values passed to Tcl_GetRegExpFromObj. */ #define TCL_REG_BASIC 000000 /* BREs (convenience). */ #define TCL_REG_EXTENDED 000001 /* EREs. */ #define TCL_REG_ADVF 000002 /* Advanced features in EREs. */ #define TCL_REG_ADVANCED 000003 /* AREs (which are also EREs). */ #define TCL_REG_QUOTE 000004 /* No special characters, none. */ #define TCL_REG_NOCASE 000010 /* Ignore case. */ #define TCL_REG_NOSUB 000020 /* Don't care about subexpressions. */ #define TCL_REG_EXPANDED 000040 /* Expanded format, white space & * comments. */ #define TCL_REG_NLSTOP 000100 /* \n doesn't match . or [^ ] */ #define TCL_REG_NLANCH 000200 /* ^ matches after \n, $ before. */ #define TCL_REG_NEWLINE 000300 /* Newlines are line terminators. */ #define TCL_REG_CANMATCH 001000 /* Report details on partial/limited * matches. */ /* * Flags values passed to Tcl_RegExpExecObj. */ #define TCL_REG_NOTBOL 0001 /* Beginning of string does not match ^. */ #define TCL_REG_NOTEOL 0002 /* End of string does not match $. */ /* * Structures filled in by Tcl_RegExpInfo. Note that all offset values are * relative to the start of the match string, not the beginning of the entire * string. */ typedef struct Tcl_RegExpIndices { long start; /* Character offset of first character in * match. */ long end; /* Character offset of first character after * the match. */ } Tcl_RegExpIndices; typedef struct Tcl_RegExpInfo { int nsubs; /* Number of subexpressions in the compiled * expression. */ Tcl_RegExpIndices *matches; /* Array of nsubs match offset pairs. */ long extendStart; /* The offset at which a subsequent match * might begin. */ long reserved; /* Reserved for later use. */ } Tcl_RegExpInfo; /* * Picky compilers complain if this typdef doesn't appear before the struct's * reference in tclDecls.h. */ typedef Tcl_StatBuf *Tcl_Stat_; typedef struct stat *Tcl_OldStat_; /* *---------------------------------------------------------------------------- * When a TCL command returns, the interpreter contains a result from the * command. Programmers are strongly encouraged to use one of the functions * Tcl_GetObjResult() or Tcl_GetStringResult() to read the interpreter's * result. See the SetResult man page for details. Besides this result, the * command function returns an integer code, which is one of the following: * * TCL_OK Command completed normally; the interpreter's result * contains the command's result. * TCL_ERROR The command couldn't be completed successfully; the * interpreter's result describes what went wrong. * TCL_RETURN The command requests that the current function return; * the interpreter's result contains the function's * return value. * TCL_BREAK The command requests that the innermost loop be * exited; the interpreter's result is meaningless. * TCL_CONTINUE Go on to the next iteration of the current loop; the * interpreter's result is meaningless. */ #define TCL_OK 0 #define TCL_ERROR 1 #define TCL_RETURN 2 #define TCL_BREAK 3 #define TCL_CONTINUE 4 #define TCL_RESULT_SIZE 200 /* *---------------------------------------------------------------------------- * Flags to control what substitutions are performed by Tcl_SubstObj(): */ #define TCL_SUBST_COMMANDS 001 #define TCL_SUBST_VARIABLES 002 #define TCL_SUBST_BACKSLASHES 004 #define TCL_SUBST_ALL 007 /* * Argument descriptors for math function callbacks in expressions: */ typedef enum { TCL_INT, TCL_DOUBLE, TCL_EITHER, TCL_WIDE_INT } Tcl_ValueType; typedef struct Tcl_Value { Tcl_ValueType type; /* Indicates intValue or doubleValue is valid, * or both. */ long intValue; /* Integer value. */ double doubleValue; /* Double-precision floating value. */ Tcl_WideInt wideValue; /* Wide (min. 64-bit) integer value. */ } Tcl_Value; /* * Forward declaration of Tcl_Obj to prevent an error when the forward * reference to Tcl_Obj is encountered in the function types declared below. */ struct Tcl_Obj; /* *---------------------------------------------------------------------------- * Function types defined by Tcl: */ typedef int (Tcl_AppInitProc) (Tcl_Interp *interp); typedef int (Tcl_AsyncProc) (ClientData clientData, Tcl_Interp *interp, int code); typedef void (Tcl_ChannelProc) (ClientData clientData, int mask); typedef void (Tcl_CloseProc) (ClientData data); typedef void (Tcl_CmdDeleteProc) (ClientData clientData); typedef int (Tcl_CmdProc) (ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]); typedef void (Tcl_CmdTraceProc) (ClientData clientData, Tcl_Interp *interp, int level, char *command, Tcl_CmdProc *proc, ClientData cmdClientData, int argc, CONST84 char *argv[]); typedef int (Tcl_CmdObjTraceProc) (ClientData clientData, Tcl_Interp *interp, int level, const char *command, Tcl_Command commandInfo, int objc, struct Tcl_Obj *const *objv); typedef void (Tcl_CmdObjTraceDeleteProc) (ClientData clientData); typedef void (Tcl_DupInternalRepProc) (struct Tcl_Obj *srcPtr, struct Tcl_Obj *dupPtr); typedef int (Tcl_EncodingConvertProc) (ClientData clientData, const char *src, int srcLen, int flags, Tcl_EncodingState *statePtr, char *dst, int dstLen, int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr); typedef void (Tcl_EncodingFreeProc) (ClientData clientData); typedef int (Tcl_EventProc) (Tcl_Event *evPtr, int flags); typedef void (Tcl_EventCheckProc) (ClientData clientData, int flags); typedef int (Tcl_EventDeleteProc) (Tcl_Event *evPtr, ClientData clientData); typedef void (Tcl_EventSetupProc) (ClientData clientData, int flags); typedef void (Tcl_ExitProc) (ClientData clientData); typedef void (Tcl_FileProc) (ClientData clientData, int mask); typedef void (Tcl_FileFreeProc) (ClientData clientData); typedef void (Tcl_FreeInternalRepProc) (struct Tcl_Obj *objPtr); typedef void (Tcl_FreeProc) (char *blockPtr); typedef void (Tcl_IdleProc) (ClientData clientData); typedef void (Tcl_InterpDeleteProc) (ClientData clientData, Tcl_Interp *interp); typedef int (Tcl_MathProc) (ClientData clientData, Tcl_Interp *interp, Tcl_Value *args, Tcl_Value *resultPtr); typedef void (Tcl_NamespaceDeleteProc) (ClientData clientData); typedef int (Tcl_ObjCmdProc) (ClientData clientData, Tcl_Interp *interp, int objc, struct Tcl_Obj *const *objv); typedef int (Tcl_PackageInitProc) (Tcl_Interp *interp); typedef int (Tcl_PackageUnloadProc) (Tcl_Interp *interp, int flags); typedef void (Tcl_PanicProc) (const char *format, ...); typedef void (Tcl_TcpAcceptProc) (ClientData callbackData, Tcl_Channel chan, char *address, int port); typedef void (Tcl_TimerProc) (ClientData clientData); typedef int (Tcl_SetFromAnyProc) (Tcl_Interp *interp, struct Tcl_Obj *objPtr); typedef void (Tcl_UpdateStringProc) (struct Tcl_Obj *objPtr); typedef char * (Tcl_VarTraceProc) (ClientData clientData, Tcl_Interp *interp, CONST84 char *part1, CONST84 char *part2, int flags); typedef void (Tcl_CommandTraceProc) (ClientData clientData, Tcl_Interp *interp, const char *oldName, const char *newName, int flags); typedef void (Tcl_CreateFileHandlerProc) (int fd, int mask, Tcl_FileProc *proc, ClientData clientData); typedef void (Tcl_DeleteFileHandlerProc) (int fd); typedef void (Tcl_AlertNotifierProc) (ClientData clientData); typedef void (Tcl_ServiceModeHookProc) (int mode); typedef ClientData (Tcl_InitNotifierProc) (void); typedef void (Tcl_FinalizeNotifierProc) (ClientData clientData); typedef void (Tcl_MainLoopProc) (void); /* *---------------------------------------------------------------------------- * The following structure represents a type of object, which is a particular * internal representation for an object plus a set of functions that provide * standard operations on objects of that type. */ typedef struct Tcl_ObjType { const char *name; /* Name of the type, e.g. "int". */ Tcl_FreeInternalRepProc *freeIntRepProc; /* Called to free any storage for the type's * internal rep. NULL if the internal rep does * not need freeing. */ Tcl_DupInternalRepProc *dupIntRepProc; /* Called to create a new object as a copy of * an existing object. */ Tcl_UpdateStringProc *updateStringProc; /* Called to update the string rep from the * type's internal representation. */ Tcl_SetFromAnyProc *setFromAnyProc; /* Called to convert the object's internal rep * to this type. Frees the internal rep of the * old type. Returns TCL_ERROR on failure. */ } Tcl_ObjType; /* * One of the following structures exists for each object in the Tcl system. * An object stores a value as either a string, some internal representation, * or both. */ typedef struct Tcl_Obj { int refCount; /* When 0 the object will be freed. */ char *bytes; /* This points to the first byte of the * object's string representation. The array * must be followed by a null byte (i.e., at * offset length) but may also contain * embedded null characters. The array's * storage is allocated by ckalloc. NULL means * the string rep is invalid and must be * regenerated from the internal rep. Clients * should use Tcl_GetStringFromObj or * Tcl_GetString to get a pointer to the byte * array as a readonly value. */ int length; /* The number of bytes at *bytes, not * including the terminating null. */ const Tcl_ObjType *typePtr; /* Denotes the object's type. Always * corresponds to the type of the object's * internal rep. NULL indicates the object has * no internal rep (has no type). */ union { /* The internal representation: */ long longValue; /* - an long integer value. */ double doubleValue; /* - a double-precision floating value. */ void *otherValuePtr; /* - another, type-specific value. */ Tcl_WideInt wideValue; /* - a long long value. */ struct { /* - internal rep as two pointers. */ void *ptr1; void *ptr2; } twoPtrValue; struct { /* - internal rep as a pointer and a long, * the main use of which is a bignum's * tightly packed fields, where the alloc, * used and signum flags are packed into a * single word with everything else hung * off the pointer. */ void *ptr; unsigned long value; } ptrAndLongRep; } internalRep; } Tcl_Obj; /* * Macros to increment and decrement a Tcl_Obj's reference count, and to test * whether an object is shared (i.e. has reference count > 1). Note: clients * should use Tcl_DecrRefCount() when they are finished using an object, and * should never call TclFreeObj() directly. TclFreeObj() is only defined and * made public in tcl.h to support Tcl_DecrRefCount's macro definition. */ void Tcl_IncrRefCount(Tcl_Obj *objPtr); void Tcl_DecrRefCount(Tcl_Obj *objPtr); int Tcl_IsShared(Tcl_Obj *objPtr); /* *---------------------------------------------------------------------------- * The following structure contains the state needed by Tcl_SaveResult. No-one * outside of Tcl should access any of these fields. This structure is * typically allocated on the stack. */ typedef struct Tcl_SavedResult { char *result; Tcl_FreeProc *freeProc; Tcl_Obj *objResultPtr; char *appendResult; int appendAvl; int appendUsed; char resultSpace[TCL_RESULT_SIZE+1]; } Tcl_SavedResult; /* *---------------------------------------------------------------------------- * The following definitions support Tcl's namespace facility. Note: the first * five fields must match exactly the fields in a Namespace structure (see * tclInt.h). */ typedef struct Tcl_Namespace { char *name; /* The namespace's name within its parent * namespace. This contains no ::'s. The name * of the global namespace is "" although "::" * is an synonym. */ char *fullName; /* The namespace's fully qualified name. This * starts with ::. */ ClientData clientData; /* Arbitrary value associated with this * namespace. */ Tcl_NamespaceDeleteProc *deleteProc; /* Function invoked when deleting the * namespace to, e.g., free clientData. */ struct Tcl_Namespace *parentPtr; /* Points to the namespace that contains this * one. NULL if this is the global * namespace. */ } Tcl_Namespace; /* *---------------------------------------------------------------------------- * The following structure represents a call frame, or activation record. A * call frame defines a naming context for a procedure call: its local scope * (for local variables) and its namespace scope (used for non-local * variables; often the global :: namespace). A call frame can also define the * naming context for a namespace eval or namespace inscope command: the * namespace in which the command's code should execute. The Tcl_CallFrame * structures exist only while procedures or namespace eval/inscope's are * being executed, and provide a Tcl call stack. * * A call frame is initialized and pushed using Tcl_PushCallFrame and popped * using Tcl_PopCallFrame. Storage for a Tcl_CallFrame must be provided by the * Tcl_PushCallFrame caller, and callers typically allocate them on the C call * stack for efficiency. For this reason, Tcl_CallFrame is defined as a * structure and not as an opaque token. However, most Tcl_CallFrame fields * are hidden since applications should not access them directly; others are * declared as "dummyX". * * WARNING!! The structure definition must be kept consistent with the * CallFrame structure in tclInt.h. If you change one, change the other. */ typedef struct Tcl_CallFrame { Tcl_Namespace *nsPtr; int dummy1; int dummy2; void *dummy3; void *dummy4; void *dummy5; int dummy6; void *dummy7; void *dummy8; int dummy9; void *dummy10; void *dummy11; void *dummy12; void *dummy13; } Tcl_CallFrame; /* *---------------------------------------------------------------------------- * Information about commands that is returned by Tcl_GetCommandInfo and * passed to Tcl_SetCommandInfo. objProc is an objc/objv object-based command * function while proc is a traditional Tcl argc/argv string-based function. * Tcl_CreateObjCommand and Tcl_CreateCommand ensure that both objProc and * proc are non-NULL and can be called to execute the command. However, it may * be faster to call one instead of the other. The member isNativeObjectProc * is set to 1 if an object-based function was registered by * Tcl_CreateObjCommand, and to 0 if a string-based function was registered by * Tcl_CreateCommand. The other function is typically set to a compatibility * wrapper that does string-to-object or object-to-string argument conversions * then calls the other function. */ typedef struct Tcl_CmdInfo { int isNativeObjectProc; /* 1 if objProc was registered by a call to * Tcl_CreateObjCommand; 0 otherwise. * Tcl_SetCmdInfo does not modify this * field. */ Tcl_ObjCmdProc *objProc; /* Command's object-based function. */ ClientData objClientData; /* ClientData for object proc. */ Tcl_CmdProc *proc; /* Command's string-based function. */ ClientData clientData; /* ClientData for string proc. */ Tcl_CmdDeleteProc *deleteProc; /* Function to call when command is * deleted. */ ClientData deleteData; /* Value to pass to deleteProc (usually the * same as clientData). */ Tcl_Namespace *namespacePtr;/* Points to the namespace that contains this * command. Note that Tcl_SetCmdInfo will not * change a command's namespace; use * TclRenameCommand or Tcl_Eval (of 'rename') * to do that. */ } Tcl_CmdInfo; /* *---------------------------------------------------------------------------- * The structure defined below is used to hold dynamic strings. The only * fields that clients should use are string and length, accessible via the * macros Tcl_DStringValue and Tcl_DStringLength. */ #define TCL_DSTRING_STATIC_SIZE 200 typedef struct Tcl_DString { char *string; /* Points to beginning of string: either * staticSpace below or a malloced array. */ int length; /* Number of non-NULL characters in the * string. */ int spaceAvl; /* Total number of bytes available for the * string and its terminating NULL char. */ char staticSpace[TCL_DSTRING_STATIC_SIZE]; /* Space to use in common case where string is * small. */ } Tcl_DString; #define Tcl_DStringLength(dsPtr) ((dsPtr)->length) #define Tcl_DStringValue(dsPtr) ((dsPtr)->string) #define Tcl_DStringTrunc Tcl_DStringSetLength /* * Definitions for the maximum number of digits of precision that may be * specified in the "tcl_precision" variable, and the number of bytes of * buffer space required by Tcl_PrintDouble. */ #define TCL_MAX_PREC 17 #define TCL_DOUBLE_SPACE (TCL_MAX_PREC+10) /* * Definition for a number of bytes of buffer space sufficient to hold the * string representation of an integer in base 10 (assuming the existence of * 64-bit integers). */ #define TCL_INTEGER_SPACE 24 /* * Flag values passed to Tcl_ConvertElement. * TCL_DONT_USE_BRACES forces it not to enclose the element in braces, but to * use backslash quoting instead. * TCL_DONT_QUOTE_HASH disables the default quoting of the '#' character. It * is safe to leave the hash unquoted when the element is not the first * element of a list, and this flag can be used by the caller to indicate * that condition. */ #define TCL_DONT_USE_BRACES 1 #define TCL_DONT_QUOTE_HASH 8 /* * Flag that may be passed to Tcl_GetIndexFromObj to force it to disallow * abbreviated strings. */ #define TCL_EXACT 1 /* *---------------------------------------------------------------------------- * Flag values passed to Tcl_RecordAndEval, Tcl_EvalObj, Tcl_EvalObjv. * WARNING: these bit choices must not conflict with the bit choices for * evalFlag bits in tclInt.h! * * Meanings: * TCL_NO_EVAL: Just record this command * TCL_EVAL_GLOBAL: Execute script in global namespace * TCL_EVAL_DIRECT: Do not compile this script * TCL_EVAL_INVOKE: Magical Tcl_EvalObjv mode for aliases/ensembles * o Run in iPtr->lookupNsPtr or global namespace * o Cut out of error traces * o Don't reset the flags controlling ensemble * error message rewriting. * TCL_CANCEL_UNWIND: Magical Tcl_CancelEval mode that causes the * stack for the script in progress to be * completely unwound. * TCL_EVAL_NOERR: Do no exception reporting at all, just return * as the caller will report. */ #define TCL_NO_EVAL 0x010000 #define TCL_EVAL_GLOBAL 0x020000 #define TCL_EVAL_DIRECT 0x040000 #define TCL_EVAL_INVOKE 0x080000 #define TCL_CANCEL_UNWIND 0x100000 #define TCL_EVAL_NOERR 0x200000 /* * Special freeProc values that may be passed to Tcl_SetResult (see the man * page for details): */ #define TCL_VOLATILE ((Tcl_FreeProc *) 1) #define TCL_STATIC ((Tcl_FreeProc *) 0) #define TCL_DYNAMIC ((Tcl_FreeProc *) 3) /* * Flag values passed to variable-related functions. * WARNING: these bit choices must not conflict with the bit choice for * TCL_CANCEL_UNWIND, above. */ #define TCL_GLOBAL_ONLY 1 #define TCL_NAMESPACE_ONLY 2 #define TCL_APPEND_VALUE 4 #define TCL_LIST_ELEMENT 8 #define TCL_TRACE_READS 0x10 #define TCL_TRACE_WRITES 0x20 #define TCL_TRACE_UNSETS 0x40 #define TCL_TRACE_DESTROYED 0x80 #define TCL_INTERP_DESTROYED 0x100 #define TCL_LEAVE_ERR_MSG 0x200 #define TCL_TRACE_ARRAY 0x800 #ifndef TCL_REMOVE_OBSOLETE_TRACES /* Required to support old variable/vdelete/vinfo traces. */ #define TCL_TRACE_OLD_STYLE 0x1000 #endif /* Indicate the semantics of the result of a trace. */ #define TCL_TRACE_RESULT_DYNAMIC 0x8000 #define TCL_TRACE_RESULT_OBJECT 0x10000 /* * Flag values for ensemble commands. */ #define TCL_ENSEMBLE_PREFIX 0x02/* Flag value to say whether to allow * unambiguous prefixes of commands or to * require exact matches for command names. */ /* * Flag values passed to command-related functions. */ #define TCL_TRACE_RENAME 0x2000 #define TCL_TRACE_DELETE 0x4000 #define TCL_ALLOW_INLINE_COMPILATION 0x20000 /* * The TCL_PARSE_PART1 flag is deprecated and has no effect. The part1 is now * always parsed whenever the part2 is NULL. (This is to avoid a common error * when converting code to use the new object based APIs and forgetting to * give the flag) */ #ifndef TCL_NO_DEPRECATED # define TCL_PARSE_PART1 0x400 #endif /* * Types for linked variables: */ #define TCL_LINK_INT 1 #define TCL_LINK_DOUBLE 2 #define TCL_LINK_BOOLEAN 3 #define TCL_LINK_STRING 4 #define TCL_LINK_WIDE_INT 5 #define TCL_LINK_CHAR 6 #define TCL_LINK_UCHAR 7 #define TCL_LINK_SHORT 8 #define TCL_LINK_USHORT 9 #define TCL_LINK_UINT 10 #define TCL_LINK_LONG 11 #define TCL_LINK_ULONG 12 #define TCL_LINK_FLOAT 13 #define TCL_LINK_WIDE_UINT 14 #define TCL_LINK_READ_ONLY 0x80 /* *---------------------------------------------------------------------------- * Forward declarations of Tcl_HashTable and related types. */ typedef struct Tcl_HashKeyType Tcl_HashKeyType; typedef struct Tcl_HashTable Tcl_HashTable; typedef struct Tcl_HashEntry Tcl_HashEntry; typedef unsigned (Tcl_HashKeyProc) (Tcl_HashTable *tablePtr, void *keyPtr); typedef int (Tcl_CompareHashKeysProc) (void *keyPtr, Tcl_HashEntry *hPtr); typedef Tcl_HashEntry * (Tcl_AllocHashEntryProc) (Tcl_HashTable *tablePtr, void *keyPtr); typedef void (Tcl_FreeHashEntryProc) (Tcl_HashEntry *hPtr); /* * This flag controls whether the hash table stores the hash of a key, or * recalculates it. There should be no reason for turning this flag off as it * is completely binary and source compatible unless you directly access the * bucketPtr member of the Tcl_HashTableEntry structure. This member has been * removed and the space used to store the hash value. */ #ifndef TCL_HASH_KEY_STORE_HASH # define TCL_HASH_KEY_STORE_HASH 1 #endif /* * Structure definition for an entry in a hash table. No-one outside Tcl * should access any of these fields directly; use the macros defined below. */ struct Tcl_HashEntry { Tcl_HashEntry *nextPtr; /* Pointer to next entry in this hash bucket, * or NULL for end of chain. */ Tcl_HashTable *tablePtr; /* Pointer to table containing entry. */ #if TCL_HASH_KEY_STORE_HASH void *hash; /* Hash value, stored as pointer to ensure * that the offsets of the fields in this * structure are not changed. */ #else Tcl_HashEntry **bucketPtr; /* Pointer to bucket that points to first * entry in this entry's chain: used for * deleting the entry. */ #endif ClientData clientData; /* Application stores something here with * Tcl_SetHashValue. */ union { /* Key has one of these forms: */ char *oneWordValue; /* One-word value for key. */ Tcl_Obj *objPtr; /* Tcl_Obj * key value. */ int words[1]; /* Multiple integer words for key. The actual * size will be as large as necessary for this * table's keys. */ char string[1]; /* String for key. The actual size will be as * large as needed to hold the key. */ } key; /* MUST BE LAST FIELD IN RECORD!! */ }; /* * Flags used in Tcl_HashKeyType. * * TCL_HASH_KEY_RANDOMIZE_HASH - * There are some things, pointers for example * which don't hash well because they do not use * the lower bits. If this flag is set then the * hash table will attempt to rectify this by * randomising the bits and then using the upper * N bits as the index into the table. * TCL_HASH_KEY_SYSTEM_HASH - If this flag is set then all memory internally * allocated for the hash table that is not for an * entry will use the system heap. */ #define TCL_HASH_KEY_RANDOMIZE_HASH 0x1 #define TCL_HASH_KEY_SYSTEM_HASH 0x2 /* * Structure definition for the methods associated with a hash table key type. */ #define TCL_HASH_KEY_TYPE_VERSION 1 struct Tcl_HashKeyType { int version; /* Version of the table. If this structure is * extended in future then the version can be * used to distinguish between different * structures. */ int flags; /* Flags, see above for details. */ Tcl_HashKeyProc *hashKeyProc; /* Calculates a hash value for the key. If * this is NULL then the pointer itself is * used as a hash value. */ Tcl_CompareHashKeysProc *compareKeysProc; /* Compares two keys and returns zero if they * do not match, and non-zero if they do. If * this is NULL then the pointers are * compared. */ Tcl_AllocHashEntryProc *allocEntryProc; /* Called to allocate memory for a new entry, * i.e. if the key is a string then this could * allocate a single block which contains * enough space for both the entry and the * string. Only the key field of the allocated * Tcl_HashEntry structure needs to be filled * in. If something else needs to be done to * the key, i.e. incrementing a reference * count then that should be done by this * function. If this is NULL then Tcl_Alloc is * used to allocate enough space for a * Tcl_HashEntry and the key pointer is * assigned to key.oneWordValue. */ Tcl_FreeHashEntryProc *freeEntryProc; /* Called to free memory associated with an * entry. If something else needs to be done * to the key, i.e. decrementing a reference * count then that should be done by this * function. If this is NULL then Tcl_Free is * used to free the Tcl_HashEntry. */ }; /* * Structure definition for a hash table. Must be in tcl.h so clients can * allocate space for these structures, but clients should never access any * fields in this structure. */ #define TCL_SMALL_HASH_TABLE 4 struct Tcl_HashTable { Tcl_HashEntry **buckets; /* Pointer to bucket array. Each element * points to first entry in bucket's hash * chain, or NULL. */ Tcl_HashEntry *staticBuckets[TCL_SMALL_HASH_TABLE]; /* Bucket array used for small tables (to * avoid mallocs and frees). */ int numBuckets; /* Total number of buckets allocated at * **bucketPtr. */ int numEntries; /* Total number of entries present in * table. */ int rebuildSize; /* Enlarge table when numEntries gets to be * this large. */ int downShift; /* Shift count used in hashing function. * Designed to use high-order bits of * randomized keys. */ int mask; /* Mask value used in hashing function. */ int keyType; /* Type of keys used in this table. It's * either TCL_CUSTOM_KEYS, TCL_STRING_KEYS, * TCL_ONE_WORD_KEYS, or an integer giving the * number of ints that is the size of the * key. */ Tcl_HashEntry *(*findProc) (Tcl_HashTable *tablePtr, const char *key); Tcl_HashEntry *(*createProc) (Tcl_HashTable *tablePtr, const char *key, int *newPtr); const Tcl_HashKeyType *typePtr; /* Type of the keys used in the * Tcl_HashTable. */ }; /* * Structure definition for information used to keep track of searches through * hash tables: */ typedef struct Tcl_HashSearch { Tcl_HashTable *tablePtr; /* Table being searched. */ int nextIndex; /* Index of next bucket to be enumerated after * present one. */ Tcl_HashEntry *nextEntryPtr;/* Next entry to be enumerated in the current * bucket. */ } Tcl_HashSearch; /* * Acceptable key types for hash tables: * * TCL_STRING_KEYS: The keys are strings, they are copied into the * entry. * TCL_ONE_WORD_KEYS: The keys are pointers, the pointer is stored * in the entry. * TCL_CUSTOM_TYPE_KEYS: The keys are arbitrary types which are copied * into the entry. * TCL_CUSTOM_PTR_KEYS: The keys are pointers to arbitrary types, the * pointer is stored in the entry. * * While maintaining binary compatability the above have to be distinct values * as they are used to differentiate between old versions of the hash table * which don't have a typePtr and new ones which do. Once binary compatability * is discarded in favour of making more wide spread changes TCL_STRING_KEYS * can be the same as TCL_CUSTOM_TYPE_KEYS, and TCL_ONE_WORD_KEYS can be the * same as TCL_CUSTOM_PTR_KEYS because they simply determine how the key is * accessed from the entry and not the behaviour. */ #define TCL_STRING_KEYS (0) #define TCL_ONE_WORD_KEYS (1) #define TCL_CUSTOM_TYPE_KEYS (-2) #define TCL_CUSTOM_PTR_KEYS (-1) /* * Structure definition for information used to keep track of searches through * dictionaries. These fields should not be accessed by code outside * tclDictObj.c */ typedef struct { void *next; /* Search position for underlying hash * table. */ int epoch; /* Epoch marker for dictionary being searched, * or -1 if search has terminated. */ Tcl_Dict dictionaryPtr; /* Reference to dictionary being searched. */ } Tcl_DictSearch; /* *---------------------------------------------------------------------------- * Flag values to pass to Tcl_DoOneEvent to disable searches for some kinds of * events: */ #define TCL_DONT_WAIT (1<<1) #define TCL_WINDOW_EVENTS (1<<2) #define TCL_FILE_EVENTS (1<<3) #define TCL_TIMER_EVENTS (1<<4) #define TCL_IDLE_EVENTS (1<<5) /* WAS 0x10 ???? */ #define TCL_ALL_EVENTS (~TCL_DONT_WAIT) /* * The following structure defines a generic event for the Tcl event system. * These are the things that are queued in calls to Tcl_QueueEvent and * serviced later by Tcl_DoOneEvent. There can be many different kinds of * events with different fields, corresponding to window events, timer events, * etc. The structure for a particular event consists of a Tcl_Event header * followed by additional information specific to that event. */ struct Tcl_Event { Tcl_EventProc *proc; /* Function to call to service this event. */ struct Tcl_Event *nextPtr; /* Next in list of pending events, or NULL. */ }; /* * Positions to pass to Tcl_QueueEvent: */ typedef enum { TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, TCL_QUEUE_MARK } Tcl_QueuePosition; /* * Values to pass to Tcl_SetServiceMode to specify the behavior of notifier * event routines. */ #define TCL_SERVICE_NONE 0 #define TCL_SERVICE_ALL 1 /* * The following structure keeps is used to hold a time value, either as an * absolute time (the number of seconds from the epoch) or as an elapsed time. * On Unix systems the epoch is Midnight Jan 1, 1970 GMT. */ typedef struct Tcl_Time { long sec; /* Seconds. */ long usec; /* Microseconds. */ } Tcl_Time; typedef void (Tcl_SetTimerProc) (CONST86 Tcl_Time *timePtr); typedef int (Tcl_WaitForEventProc) (CONST86 Tcl_Time *timePtr); /* * TIP #233 (Virtualized Time) */ typedef void (Tcl_GetTimeProc) (Tcl_Time *timebuf, ClientData clientData); typedef void (Tcl_ScaleTimeProc) (Tcl_Time *timebuf, ClientData clientData); /* *---------------------------------------------------------------------------- * Bits to pass to Tcl_CreateFileHandler and Tcl_CreateChannelHandler to * indicate what sorts of events are of interest: */ #define TCL_READABLE (1<<1) #define TCL_WRITABLE (1<<2) #define TCL_EXCEPTION (1<<3) /* * Flag values to pass to Tcl_OpenCommandChannel to indicate the disposition * of the stdio handles. TCL_STDIN, TCL_STDOUT, TCL_STDERR, are also used in * Tcl_GetStdChannel. */ #define TCL_STDIN (1<<1) #define TCL_STDOUT (1<<2) #define TCL_STDERR (1<<3) #define TCL_ENFORCE_MODE (1<<4) /* * Bits passed to Tcl_DriverClose2Proc to indicate which side of a channel * should be closed. */ #define TCL_CLOSE_READ (1<<1) #define TCL_CLOSE_WRITE (1<<2) /* * Value to use as the closeProc for a channel that supports the close2Proc * interface. */ #define TCL_CLOSE2PROC ((Tcl_DriverCloseProc *) 1) /* * Channel version tag. This was introduced in 8.3.2/8.4. */ #define TCL_CHANNEL_VERSION_1 ((Tcl_ChannelTypeVersion) 0x1) #define TCL_CHANNEL_VERSION_2 ((Tcl_ChannelTypeVersion) 0x2) #define TCL_CHANNEL_VERSION_3 ((Tcl_ChannelTypeVersion) 0x3) #define TCL_CHANNEL_VERSION_4 ((Tcl_ChannelTypeVersion) 0x4) #define TCL_CHANNEL_VERSION_5 ((Tcl_ChannelTypeVersion) 0x5) /* * TIP #218: Channel Actions, Ids for Tcl_DriverThreadActionProc. */ #define TCL_CHANNEL_THREAD_INSERT (0) #define TCL_CHANNEL_THREAD_REMOVE (1) /* * Typedefs for the various operations in a channel type: */ typedef int (Tcl_DriverBlockModeProc) (ClientData instanceData, int mode); typedef int (Tcl_DriverCloseProc) (ClientData instanceData, Tcl_Interp *interp); typedef int (Tcl_DriverClose2Proc) (ClientData instanceData, Tcl_Interp *interp, int flags); typedef int (Tcl_DriverInputProc) (ClientData instanceData, char *buf, int toRead, int *errorCodePtr); typedef int (Tcl_DriverOutputProc) (ClientData instanceData, CONST84 char *buf, int toWrite, int *errorCodePtr); typedef int (Tcl_DriverSeekProc) (ClientData instanceData, long offset, int mode, int *errorCodePtr); typedef int (Tcl_DriverSetOptionProc) (ClientData instanceData, Tcl_Interp *interp, const char *optionName, const char *value); typedef int (Tcl_DriverGetOptionProc) (ClientData instanceData, Tcl_Interp *interp, CONST84 char *optionName, Tcl_DString *dsPtr); typedef void (Tcl_DriverWatchProc) (ClientData instanceData, int mask); typedef int (Tcl_DriverGetHandleProc) (ClientData instanceData, int direction, ClientData *handlePtr); typedef int (Tcl_DriverFlushProc) (ClientData instanceData); typedef int (Tcl_DriverHandlerProc) (ClientData instanceData, int interestMask); typedef Tcl_WideInt (Tcl_DriverWideSeekProc) (ClientData instanceData, Tcl_WideInt offset, int mode, int *errorCodePtr); /* * TIP #218, Channel Thread Actions */ typedef void (Tcl_DriverThreadActionProc) (ClientData instanceData, int action); /* * TIP #208, File Truncation (etc.) */ typedef int (Tcl_DriverTruncateProc) (ClientData instanceData, Tcl_WideInt length); /* * struct Tcl_ChannelType: * * One such structure exists for each type (kind) of channel. It collects * together in one place all the functions that are part of the specific * channel type. * * It is recommend that the Tcl_Channel* functions are used to access elements * of this structure, instead of direct accessing. */ typedef struct Tcl_ChannelType { const char *typeName; /* The name of the channel type in Tcl * commands. This storage is owned by channel * type. */ Tcl_ChannelTypeVersion version; /* Version of the channel type. */ Tcl_DriverCloseProc *closeProc; /* Function to call to close the channel, or * TCL_CLOSE2PROC if the close2Proc should be * used instead. */ Tcl_DriverInputProc *inputProc; /* Function to call for input on channel. */ Tcl_DriverOutputProc *outputProc; /* Function to call for output on channel. */ Tcl_DriverSeekProc *seekProc; /* Function to call to seek on the channel. * May be NULL. */ Tcl_DriverSetOptionProc *setOptionProc; /* Set an option on a channel. */ Tcl_DriverGetOptionProc *getOptionProc; /* Get an option from a channel. */ Tcl_DriverWatchProc *watchProc; /* Set up the notifier to watch for events on * this channel. */ Tcl_DriverGetHandleProc *getHandleProc; /* Get an OS handle from the channel or NULL * if not supported. */ Tcl_DriverClose2Proc *close2Proc; /* Function to call to close the channel if * the device supports closing the read & * write sides independently. */ Tcl_DriverBlockModeProc *blockModeProc; /* Set blocking mode for the raw channel. May * be NULL. */ /* * Only valid in TCL_CHANNEL_VERSION_2 channels or later. */ Tcl_DriverFlushProc *flushProc; /* Function to call to flush a channel. May be * NULL. */ Tcl_DriverHandlerProc *handlerProc; /* Function to call to handle a channel event. * This will be passed up the stacked channel * chain. */ /* * Only valid in TCL_CHANNEL_VERSION_3 channels or later. */ Tcl_DriverWideSeekProc *wideSeekProc; /* Function to call to seek on the channel * which can handle 64-bit offsets. May be * NULL, and must be NULL if seekProc is * NULL. */ /* * Only valid in TCL_CHANNEL_VERSION_4 channels or later. * TIP #218, Channel Thread Actions. */ Tcl_DriverThreadActionProc *threadActionProc; /* Function to call to notify the driver of * thread specific activity for a channel. May * be NULL. */ /* * Only valid in TCL_CHANNEL_VERSION_5 channels or later. * TIP #208, File Truncation. */ Tcl_DriverTruncateProc *truncateProc; /* Function to call to truncate the underlying * file to a particular length. May be NULL if * the channel does not support truncation. */ } Tcl_ChannelType; /* * The following flags determine whether the blockModeProc above should set * the channel into blocking or nonblocking mode. They are passed as arguments * to the blockModeProc function in the above structure. */ #define TCL_MODE_BLOCKING 0 /* Put channel into blocking mode. */ #define TCL_MODE_NONBLOCKING 1 /* Put channel into nonblocking * mode. */ /* *---------------------------------------------------------------------------- * Enum for different types of file paths. */ typedef enum Tcl_PathType { TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, TCL_PATH_VOLUME_RELATIVE } Tcl_PathType; /* * The following structure is used to pass glob type data amongst the various * glob routines and Tcl_FSMatchInDirectory. */ typedef struct Tcl_GlobTypeData { int type; /* Corresponds to bcdpfls as in 'find -t'. */ int perm; /* Corresponds to file permissions. */ Tcl_Obj *macType; /* Acceptable Mac type. */ Tcl_Obj *macCreator; /* Acceptable Mac creator. */ } Tcl_GlobTypeData; /* * Type and permission definitions for glob command. */ #define TCL_GLOB_TYPE_BLOCK (1<<0) #define TCL_GLOB_TYPE_CHAR (1<<1) #define TCL_GLOB_TYPE_DIR (1<<2) #define TCL_GLOB_TYPE_PIPE (1<<3) #define TCL_GLOB_TYPE_FILE (1<<4) #define TCL_GLOB_TYPE_LINK (1<<5) #define TCL_GLOB_TYPE_SOCK (1<<6) #define TCL_GLOB_TYPE_MOUNT (1<<7) #define TCL_GLOB_PERM_RONLY (1<<0) #define TCL_GLOB_PERM_HIDDEN (1<<1) #define TCL_GLOB_PERM_R (1<<2) #define TCL_GLOB_PERM_W (1<<3) #define TCL_GLOB_PERM_X (1<<4) /* * Flags for the unload callback function. */ #define TCL_UNLOAD_DETACH_FROM_INTERPRETER (1<<0) #define TCL_UNLOAD_DETACH_FROM_PROCESS (1<<1) /* * Typedefs for the various filesystem operations: */ typedef int (Tcl_FSStatProc) (Tcl_Obj *pathPtr, Tcl_StatBuf *buf); typedef int (Tcl_FSAccessProc) (Tcl_Obj *pathPtr, int mode); typedef Tcl_Channel (Tcl_FSOpenFileChannelProc) (Tcl_Interp *interp, Tcl_Obj *pathPtr, int mode, int permissions); typedef int (Tcl_FSMatchInDirectoryProc) (Tcl_Interp *interp, Tcl_Obj *result, Tcl_Obj *pathPtr, const char *pattern, Tcl_GlobTypeData *types); typedef Tcl_Obj * (Tcl_FSGetCwdProc) (Tcl_Interp *interp); typedef int (Tcl_FSChdirProc) (Tcl_Obj *pathPtr); typedef int (Tcl_FSLstatProc) (Tcl_Obj *pathPtr, Tcl_StatBuf *buf); typedef int (Tcl_FSCreateDirectoryProc) (Tcl_Obj *pathPtr); typedef int (Tcl_FSDeleteFileProc) (Tcl_Obj *pathPtr); typedef int (Tcl_FSCopyDirectoryProc) (Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr, Tcl_Obj **errorPtr); typedef int (Tcl_FSCopyFileProc) (Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr); typedef int (Tcl_FSRemoveDirectoryProc) (Tcl_Obj *pathPtr, int recursive, Tcl_Obj **errorPtr); typedef int (Tcl_FSRenameFileProc) (Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr); typedef void (Tcl_FSUnloadFileProc) (Tcl_LoadHandle loadHandle); typedef Tcl_Obj * (Tcl_FSListVolumesProc) (void); /* We have to declare the utime structure here. */ struct utimbuf; typedef int (Tcl_FSUtimeProc) (Tcl_Obj *pathPtr, struct utimbuf *tval); typedef int (Tcl_FSNormalizePathProc) (Tcl_Interp *interp, Tcl_Obj *pathPtr, int nextCheckpoint); typedef int (Tcl_FSFileAttrsGetProc) (Tcl_Interp *interp, int index, Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef); typedef const char *CONST86 * (Tcl_FSFileAttrStringsProc) (Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef); typedef int (Tcl_FSFileAttrsSetProc) (Tcl_Interp *interp, int index, Tcl_Obj *pathPtr, Tcl_Obj *objPtr); typedef Tcl_Obj * (Tcl_FSLinkProc) (Tcl_Obj *pathPtr, Tcl_Obj *toPtr, int linkType); typedef int (Tcl_FSLoadFileProc) (Tcl_Interp *interp, Tcl_Obj *pathPtr, Tcl_LoadHandle *handlePtr, Tcl_FSUnloadFileProc **unloadProcPtr); typedef int (Tcl_FSPathInFilesystemProc) (Tcl_Obj *pathPtr, ClientData *clientDataPtr); typedef Tcl_Obj * (Tcl_FSFilesystemPathTypeProc) (Tcl_Obj *pathPtr); typedef Tcl_Obj * (Tcl_FSFilesystemSeparatorProc) (Tcl_Obj *pathPtr); typedef void (Tcl_FSFreeInternalRepProc) (ClientData clientData); typedef ClientData (Tcl_FSDupInternalRepProc) (ClientData clientData); typedef Tcl_Obj * (Tcl_FSInternalToNormalizedProc) (ClientData clientData); typedef ClientData (Tcl_FSCreateInternalRepProc) (Tcl_Obj *pathPtr); typedef struct Tcl_FSVersion_ *Tcl_FSVersion; /* *---------------------------------------------------------------------------- * Data structures related to hooking into the filesystem */ /* * Filesystem version tag. This was introduced in 8.4. */ #define TCL_FILESYSTEM_VERSION_1 ((Tcl_FSVersion) 0x1) /* * struct Tcl_Filesystem: * * One such structure exists for each type (kind) of filesystem. It collects * together in one place all the functions that are part of the specific * filesystem. Tcl always accesses the filesystem through one of these * structures. * * Not all entries need be non-NULL; any which are NULL are simply ignored. * However, a complete filesystem should provide all of these functions. The * explanations in the structure show the importance of each function. */ typedef struct Tcl_Filesystem { const char *typeName; /* The name of the filesystem. */ int structureLength; /* Length of this structure, so future binary * compatibility can be assured. */ Tcl_FSVersion version; /* Version of the filesystem type. */ Tcl_FSPathInFilesystemProc *pathInFilesystemProc; /* Function to check whether a path is in this * filesystem. This is the most important * filesystem function. */ Tcl_FSDupInternalRepProc *dupInternalRepProc; /* Function to duplicate internal fs rep. May * be NULL (but then fs is less efficient). */ Tcl_FSFreeInternalRepProc *freeInternalRepProc; /* Function to free internal fs rep. Must be * implemented if internal representations * need freeing, otherwise it can be NULL. */ Tcl_FSInternalToNormalizedProc *internalToNormalizedProc; /* Function to convert internal representation * to a normalized path. Only required if the * fs creates pure path objects with no * string/path representation. */ Tcl_FSCreateInternalRepProc *createInternalRepProc; /* Function to create a filesystem-specific * internal representation. May be NULL if * paths have no internal representation, or * if the Tcl_FSPathInFilesystemProc for this * filesystem always immediately creates an * internal representation for paths it * accepts. */ Tcl_FSNormalizePathProc *normalizePathProc; /* Function to normalize a path. Should be * implemented for all filesystems which can * have multiple string representations for * the same path object. */ Tcl_FSFilesystemPathTypeProc *filesystemPathTypeProc; /* Function to determine the type of a path in * this filesystem. May be NULL. */ Tcl_FSFilesystemSeparatorProc *filesystemSeparatorProc; /* Function to return the separator * character(s) for this filesystem. Must be * implemented. */ Tcl_FSStatProc *statProc; /* Function to process a 'Tcl_FSStat()' call. * Must be implemented for any reasonable * filesystem. */ Tcl_FSAccessProc *accessProc; /* Function to process a 'Tcl_FSAccess()' * call. Must be implemented for any * reasonable filesystem. */ Tcl_FSOpenFileChannelProc *openFileChannelProc; /* Function to process a * 'Tcl_FSOpenFileChannel()' call. Must be * implemented for any reasonable * filesystem. */ Tcl_FSMatchInDirectoryProc *matchInDirectoryProc; /* Function to process a * 'Tcl_FSMatchInDirectory()'. If not * implemented, then glob and recursive copy * functionality will be lacking in the * filesystem. */ Tcl_FSUtimeProc *utimeProc; /* Function to process a 'Tcl_FSUtime()' call. * Required to allow setting (not reading) of * times with 'file mtime', 'file atime' and * the open-r/open-w/fcopy implementation of * 'file copy'. */ Tcl_FSLinkProc *linkProc; /* Function to process a 'Tcl_FSLink()' call. * Should be implemented only if the * filesystem supports links (reading or * creating). */ Tcl_FSListVolumesProc *listVolumesProc; /* Function to list any filesystem volumes * added by this filesystem. Should be * implemented only if the filesystem adds * volumes at the head of the filesystem. */ Tcl_FSFileAttrStringsProc *fileAttrStringsProc; /* Function to list all attributes strings * which are valid for this filesystem. If not * implemented the filesystem will not support * the 'file attributes' command. This allows * arbitrary additional information to be * attached to files in the filesystem. */ Tcl_FSFileAttrsGetProc *fileAttrsGetProc; /* Function to process a * 'Tcl_FSFileAttrsGet()' call, used by 'file * attributes'. */ Tcl_FSFileAttrsSetProc *fileAttrsSetProc; /* Function to process a * 'Tcl_FSFileAttrsSet()' call, used by 'file * attributes'. */ Tcl_FSCreateDirectoryProc *createDirectoryProc; /* Function to process a * 'Tcl_FSCreateDirectory()' call. Should be * implemented unless the FS is read-only. */ Tcl_FSRemoveDirectoryProc *removeDirectoryProc; /* Function to process a * 'Tcl_FSRemoveDirectory()' call. Should be * implemented unless the FS is read-only. */ Tcl_FSDeleteFileProc *deleteFileProc; /* Function to process a 'Tcl_FSDeleteFile()' * call. Should be implemented unless the FS * is read-only. */ Tcl_FSCopyFileProc *copyFileProc; /* Function to process a 'Tcl_FSCopyFile()' * call. If not implemented Tcl will fall back * on open-r, open-w and fcopy as a copying * mechanism, for copying actions initiated in * Tcl (not C). */ Tcl_FSRenameFileProc *renameFileProc; /* Function to process a 'Tcl_FSRenameFile()' * call. If not implemented, Tcl will fall * back on a copy and delete mechanism, for * rename actions initiated in Tcl (not C). */ Tcl_FSCopyDirectoryProc *copyDirectoryProc; /* Function to process a * 'Tcl_FSCopyDirectory()' call. If not * implemented, Tcl will fall back on a * recursive create-dir, file copy mechanism, * for copying actions initiated in Tcl (not * C). */ Tcl_FSLstatProc *lstatProc; /* Function to process a 'Tcl_FSLstat()' call. * If not implemented, Tcl will attempt to use * the 'statProc' defined above instead. */ Tcl_FSLoadFileProc *loadFileProc; /* Function to process a 'Tcl_FSLoadFile()' * call. If not implemented, Tcl will fall * back on a copy to native-temp followed by a * Tcl_FSLoadFile on that temporary copy. */ Tcl_FSGetCwdProc *getCwdProc; /* Function to process a 'Tcl_FSGetCwd()' * call. Most filesystems need not implement * this. It will usually only be called once, * if 'getcwd' is called before 'chdir'. May * be NULL. */ Tcl_FSChdirProc *chdirProc; /* Function to process a 'Tcl_FSChdir()' call. * If filesystems do not implement this, it * will be emulated by a series of directory * access checks. Otherwise, virtual * filesystems which do implement it need only * respond with a positive return result if * the dirName is a valid directory in their * filesystem. They need not remember the * result, since that will be automatically * remembered for use by GetCwd. Real * filesystems should carry out the correct * action (i.e. call the correct system * 'chdir' api). If not implemented, then 'cd' * and 'pwd' will fail inside the * filesystem. */ } Tcl_Filesystem; /* * The following definitions are used as values for the 'linkAction' flag to * Tcl_FSLink, or the linkProc of any filesystem. Any combination of flags can * be given. For link creation, the linkProc should create a link which * matches any of the types given. * * TCL_CREATE_SYMBOLIC_LINK - Create a symbolic or soft link. * TCL_CREATE_HARD_LINK - Create a hard link. */ #define TCL_CREATE_SYMBOLIC_LINK 0x01 #define TCL_CREATE_HARD_LINK 0x02 /* *---------------------------------------------------------------------------- * The following structure represents the Notifier functions that you can * override with the Tcl_SetNotifier call. */ typedef struct Tcl_NotifierProcs { Tcl_SetTimerProc *setTimerProc; Tcl_WaitForEventProc *waitForEventProc; Tcl_CreateFileHandlerProc *createFileHandlerProc; Tcl_DeleteFileHandlerProc *deleteFileHandlerProc; Tcl_InitNotifierProc *initNotifierProc; Tcl_FinalizeNotifierProc *finalizeNotifierProc; Tcl_AlertNotifierProc *alertNotifierProc; Tcl_ServiceModeHookProc *serviceModeHookProc; } Tcl_NotifierProcs; /* *---------------------------------------------------------------------------- * The following data structures and declarations are for the new Tcl parser. * * For each word of a command, and for each piece of a word such as a variable * reference, one of the following structures is created to describe the * token. */ typedef struct Tcl_Token { int type; /* Type of token, such as TCL_TOKEN_WORD; see * below for valid types. */ const char *start; /* First character in token. */ int size; /* Number of bytes in token. */ int numComponents; /* If this token is composed of other tokens, * this field tells how many of them there are * (including components of components, etc.). * The component tokens immediately follow * this one. */ } Tcl_Token; /* * Type values defined for Tcl_Token structures. These values are defined as * mask bits so that it's easy to check for collections of types. * * TCL_TOKEN_WORD - The token describes one word of a command, * from the first non-blank character of the word * (which may be " or {) up to but not including * the space, semicolon, or bracket that * terminates the word. NumComponents counts the * total number of sub-tokens that make up the * word. This includes, for example, sub-tokens * of TCL_TOKEN_VARIABLE tokens. * TCL_TOKEN_SIMPLE_WORD - This token is just like TCL_TOKEN_WORD except * that the word is guaranteed to consist of a * single TCL_TOKEN_TEXT sub-token. * TCL_TOKEN_TEXT - The token describes a range of literal text * that is part of a word. NumComponents is * always 0. * TCL_TOKEN_BS - The token describes a backslash sequence that * must be collapsed. NumComponents is always 0. * TCL_TOKEN_COMMAND - The token describes a command whose result * must be substituted into the word. The token * includes the enclosing brackets. NumComponents * is always 0. * TCL_TOKEN_VARIABLE - The token describes a variable substitution, * including the dollar sign, variable name, and * array index (if there is one) up through the * right parentheses. NumComponents tells how * many additional tokens follow to represent the * variable name. The first token will be a * TCL_TOKEN_TEXT token that describes the * variable name. If the variable is an array * reference then there will be one or more * additional tokens, of type TCL_TOKEN_TEXT, * TCL_TOKEN_BS, TCL_TOKEN_COMMAND, and * TCL_TOKEN_VARIABLE, that describe the array * index; numComponents counts the total number * of nested tokens that make up the variable * reference, including sub-tokens of * TCL_TOKEN_VARIABLE tokens. * TCL_TOKEN_SUB_EXPR - The token describes one subexpression of an * expression, from the first non-blank character * of the subexpression up to but not including * the space, brace, or bracket that terminates * the subexpression. NumComponents counts the * total number of following subtokens that make * up the subexpression; this includes all * subtokens for any nested TCL_TOKEN_SUB_EXPR * tokens. For example, a numeric value used as a * primitive operand is described by a * TCL_TOKEN_SUB_EXPR token followed by a * TCL_TOKEN_TEXT token. A binary subexpression * is described by a TCL_TOKEN_SUB_EXPR token * followed by the TCL_TOKEN_OPERATOR token for * the operator, then TCL_TOKEN_SUB_EXPR tokens * for the left then the right operands. * TCL_TOKEN_OPERATOR - The token describes one expression operator. * An operator might be the name of a math * function such as "abs". A TCL_TOKEN_OPERATOR * token is always preceeded by one * TCL_TOKEN_SUB_EXPR token for the operator's * subexpression, and is followed by zero or more * TCL_TOKEN_SUB_EXPR tokens for the operator's * operands. NumComponents is always 0. * TCL_TOKEN_EXPAND_WORD - This token is just like TCL_TOKEN_WORD except * that it marks a word that began with the * literal character prefix "{*}". This word is * marked to be expanded - that is, broken into * words after substitution is complete. */ #define TCL_TOKEN_WORD 1 #define TCL_TOKEN_SIMPLE_WORD 2 #define TCL_TOKEN_TEXT 4 #define TCL_TOKEN_BS 8 #define TCL_TOKEN_COMMAND 16 #define TCL_TOKEN_VARIABLE 32 #define TCL_TOKEN_SUB_EXPR 64 #define TCL_TOKEN_OPERATOR 128 #define TCL_TOKEN_EXPAND_WORD 256 /* * Parsing error types. On any parsing error, one of these values will be * stored in the error field of the Tcl_Parse structure defined below. */ #define TCL_PARSE_SUCCESS 0 #define TCL_PARSE_QUOTE_EXTRA 1 #define TCL_PARSE_BRACE_EXTRA 2 #define TCL_PARSE_MISSING_BRACE 3 #define TCL_PARSE_MISSING_BRACKET 4 #define TCL_PARSE_MISSING_PAREN 5 #define TCL_PARSE_MISSING_QUOTE 6 #define TCL_PARSE_MISSING_VAR_BRACE 7 #define TCL_PARSE_SYNTAX 8 #define TCL_PARSE_BAD_NUMBER 9 /* * A structure of the following type is filled in by Tcl_ParseCommand. It * describes a single command parsed from an input string. */ #define NUM_STATIC_TOKENS 20 typedef struct Tcl_Parse { const char *commentStart; /* Pointer to # that begins the first of one * or more comments preceding the command. */ int commentSize; /* Number of bytes in comments (up through * newline character that terminates the last * comment). If there were no comments, this * field is 0. */ const char *commandStart; /* First character in first word of * command. */ int commandSize; /* Number of bytes in command, including first * character of first word, up through the * terminating newline, close bracket, or * semicolon. */ int numWords; /* Total number of words in command. May be * 0. */ Tcl_Token *tokenPtr; /* Pointer to first token representing the * words of the command. Initially points to * staticTokens, but may change to point to * malloc-ed space if command exceeds space in * staticTokens. */ int numTokens; /* Total number of tokens in command. */ int tokensAvailable; /* Total number of tokens available at * *tokenPtr. */ int errorType; /* One of the parsing error types defined * above. */ /* * The fields below are intended only for the private use of the parser. * They should not be used by functions that invoke Tcl_ParseCommand. */ const char *string; /* The original command string passed to * Tcl_ParseCommand. */ const char *end; /* Points to the character just after the last * one in the command string. */ Tcl_Interp *interp; /* Interpreter to use for error reporting, or * NULL. */ const char *term; /* Points to character in string that * terminated most recent token. Filled in by * ParseTokens. If an error occurs, points to * beginning of region where the error * occurred (e.g. the open brace if the close * brace is missing). */ int incomplete; /* This field is set to 1 by Tcl_ParseCommand * if the command appears to be incomplete. * This information is used by * Tcl_CommandComplete. */ Tcl_Token staticTokens[NUM_STATIC_TOKENS]; /* Initial space for tokens for command. This * space should be large enough to accommodate * most commands; dynamic space is allocated * for very large commands that don't fit * here. */ } Tcl_Parse; /* *---------------------------------------------------------------------------- * The following structure represents a user-defined encoding. It collects * together all the functions that are used by the specific encoding. */ typedef struct Tcl_EncodingType { const char *encodingName; /* The name of the encoding, e.g. "euc-jp". * This name is the unique key for this * encoding type. */ Tcl_EncodingConvertProc *toUtfProc; /* Function to convert from external encoding * into UTF-8. */ Tcl_EncodingConvertProc *fromUtfProc; /* Function to convert from UTF-8 into * external encoding. */ Tcl_EncodingFreeProc *freeProc; /* If non-NULL, function to call when this * encoding is deleted. */ ClientData clientData; /* Arbitrary value associated with encoding * type. Passed to conversion functions. */ int nullSize; /* Number of zero bytes that signify * end-of-string in this encoding. This number * is used to determine the source string * length when the srcLen argument is * negative. Must be 1 or 2. */ } Tcl_EncodingType; /* * The following definitions are used as values for the conversion control * flags argument when converting text from one character set to another: * * TCL_ENCODING_START - Signifies that the source buffer is the first * block in a (potentially multi-block) input * stream. Tells the conversion function to reset * to an initial state and perform any * initialization that needs to occur before the * first byte is converted. If the source buffer * contains the entire input stream to be * converted, this flag should be set. * TCL_ENCODING_END - Signifies that the source buffer is the last * block in a (potentially multi-block) input * stream. Tells the conversion routine to * perform any finalization that needs to occur * after the last byte is converted and then to * reset to an initial state. If the source * buffer contains the entire input stream to be * converted, this flag should be set. * TCL_ENCODING_STOPONERROR - If set, then the converter will return * immediately upon encountering an invalid byte * sequence or a source character that has no * mapping in the target encoding. If clear, then * the converter will skip the problem, * substituting one or more "close" characters in * the destination buffer and then continue to * convert the source. */ #define TCL_ENCODING_START 0x01 #define TCL_ENCODING_END 0x02 #define TCL_ENCODING_STOPONERROR 0x04 /* * The following definitions are the error codes returned by the conversion * routines: * * TCL_OK - All characters were converted. * TCL_CONVERT_NOSPACE - The output buffer would not have been large * enough for all of the converted data; as many * characters as could fit were converted though. * TCL_CONVERT_MULTIBYTE - The last few bytes in the source string were * the beginning of a multibyte sequence, but * more bytes were needed to complete this * sequence. A subsequent call to the conversion * routine should pass the beginning of this * unconverted sequence plus additional bytes * from the source stream to properly convert the * formerly split-up multibyte sequence. * TCL_CONVERT_SYNTAX - The source stream contained an invalid * character sequence. This may occur if the * input stream has been damaged or if the input * encoding method was misidentified. This error * is reported only if TCL_ENCODING_STOPONERROR * was specified. * TCL_CONVERT_UNKNOWN - The source string contained a character that * could not be represented in the target * encoding. This error is reported only if * TCL_ENCODING_STOPONERROR was specified. */ #define TCL_CONVERT_MULTIBYTE (-1) #define TCL_CONVERT_SYNTAX (-2) #define TCL_CONVERT_UNKNOWN (-3) #define TCL_CONVERT_NOSPACE (-4) /* * The maximum number of bytes that are necessary to represent a single * Unicode character in UTF-8. The valid values should be 3, 4 or 6 * (or perhaps 1 if we want to support a non-unicode enabled core). If 3 or * 4, then Tcl_UniChar must be 2-bytes in size (UCS-2) (the default). If 6, * then Tcl_UniChar must be 4-bytes in size (UCS-4). At this time UCS-2 mode * is the default and recommended mode. UCS-4 is experimental and not * recommended. It works for the core, but most extensions expect UCS-2. */ #ifndef TCL_UTF_MAX #define TCL_UTF_MAX 3 #endif /* * This represents a Unicode character. Any changes to this should also be * reflected in regcustom.h. */ #if TCL_UTF_MAX > 4 /* * unsigned int isn't 100% accurate as it should be a strict 4-byte value * (perhaps wchar_t). 64-bit systems may have troubles. The size of this * value must be reflected correctly in regcustom.h and * in tclEncoding.c. * XXX: Tcl is currently UCS-2 and planning UTF-16 for the Unicode * XXX: string rep that Tcl_UniChar represents. Changing the size * XXX: of Tcl_UniChar is /not/ supported. */ typedef unsigned int Tcl_UniChar; #else typedef unsigned short Tcl_UniChar; #endif /* *---------------------------------------------------------------------------- * TIP #59: The following structure is used in calls 'Tcl_RegisterConfig' to * provide the system with the embedded configuration data. */ typedef struct Tcl_Config { const char *key; /* Configuration key to register. ASCII * encoded, thus UTF-8. */ const char *value; /* The value associated with the key. System * encoding. */ } Tcl_Config; /* *---------------------------------------------------------------------------- * Flags for TIP#143 limits, detailing which limits are active in an * interpreter. Used for Tcl_{Add,Remove}LimitHandler type argument. */ #define TCL_LIMIT_COMMANDS 0x01 #define TCL_LIMIT_TIME 0x02 /* * Structure containing information about a limit handler to be called when a * command- or time-limit is exceeded by an interpreter. */ typedef void (Tcl_LimitHandlerProc) (ClientData clientData, Tcl_Interp *interp); typedef void (Tcl_LimitHandlerDeleteProc) (ClientData clientData); /* *---------------------------------------------------------------------------- * Override definitions for libtommath. */ typedef struct mp_int mp_int; #define MP_INT_DECLARED typedef unsigned int mp_digit; #define MP_DIGIT_DECLARED /* *---------------------------------------------------------------------------- * Definitions needed for Tcl_ParseArgvObj routines. * Based on tkArgv.c. * Modifications from the original are copyright (c) Sam Bromley 2006 */ typedef struct { int type; /* Indicates the option type; see below. */ const char *keyStr; /* The key string that flags the option in the * argv array. */ void *srcPtr; /* Value to be used in setting dst; usage * depends on type.*/ void *dstPtr; /* Address of value to be modified; usage * depends on type.*/ const char *helpStr; /* Documentation message describing this * option. */ ClientData clientData; /* Word to pass to function callbacks. */ } Tcl_ArgvInfo; /* * Legal values for the type field of a Tcl_ArgInfo: see the user * documentation for details. */ #define TCL_ARGV_CONSTANT 15 #define TCL_ARGV_INT 16 #define TCL_ARGV_STRING 17 #define TCL_ARGV_REST 18 #define TCL_ARGV_FLOAT 19 #define TCL_ARGV_FUNC 20 #define TCL_ARGV_GENFUNC 21 #define TCL_ARGV_HELP 22 #define TCL_ARGV_END 23 /* * Types of callback functions for the TCL_ARGV_FUNC and TCL_ARGV_GENFUNC * argument types: */ typedef int (Tcl_ArgvFuncProc)(ClientData clientData, Tcl_Obj *objPtr, void *dstPtr); typedef int (Tcl_ArgvGenFuncProc)(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv, void *dstPtr); /* * Shorthand for commonly used argTable entries. */ #define TCL_ARGV_AUTO_HELP \ {TCL_ARGV_HELP, "-help", NULL, NULL, \ "Print summary of command-line options and abort", NULL} #define TCL_ARGV_AUTO_REST \ {TCL_ARGV_REST, "--", NULL, NULL, \ "Marks the end of the options", NULL} #define TCL_ARGV_TABLE_END \ {TCL_ARGV_END, NULL, NULL, NULL, NULL, NULL} /* *---------------------------------------------------------------------------- * Definitions needed for Tcl_Zlib routines. [TIP #234] * * Constants for the format flags describing what sort of data format is * desired/expected for the Tcl_ZlibDeflate, Tcl_ZlibInflate and * Tcl_ZlibStreamInit functions. */ #define TCL_ZLIB_FORMAT_RAW 1 #define TCL_ZLIB_FORMAT_ZLIB 2 #define TCL_ZLIB_FORMAT_GZIP 4 #define TCL_ZLIB_FORMAT_AUTO 8 /* * Constants that describe whether the stream is to operate in compressing or * decompressing mode. */ #define TCL_ZLIB_STREAM_DEFLATE 16 #define TCL_ZLIB_STREAM_INFLATE 32 /* * Constants giving compression levels. Use of TCL_ZLIB_COMPRESS_DEFAULT is * recommended. */ #define TCL_ZLIB_COMPRESS_NONE 0 #define TCL_ZLIB_COMPRESS_FAST 1 #define TCL_ZLIB_COMPRESS_BEST 9 #define TCL_ZLIB_COMPRESS_DEFAULT (-1) /* * Constants for types of flushing, used with Tcl_ZlibFlush. */ #define TCL_ZLIB_NO_FLUSH 0 #define TCL_ZLIB_FLUSH 2 #define TCL_ZLIB_FULLFLUSH 3 #define TCL_ZLIB_FINALIZE 4 /* *---------------------------------------------------------------------------- * Definitions needed for the Tcl_LoadFile function. [TIP #416] */ #define TCL_LOAD_GLOBAL 1 #define TCL_LOAD_LAZY 2 /* *---------------------------------------------------------------------------- * Single public declaration for NRE. */ typedef int (Tcl_NRPostProc) (ClientData data[], Tcl_Interp *interp, int result); /* *---------------------------------------------------------------------------- * The following constant is used to test for older versions of Tcl in the * stubs tables. * * Jan Nijtman's plus patch uses 0xFCA1BACF, so we need to pick a different * value since the stubs tables don't match. */ #define TCL_STUB_MAGIC ((int) 0xFCA3BACF) /* * The following function is required to be defined in all stubs aware * extensions. The function is actually implemented in the stub library, not * the main Tcl library, although there is a trivial implementation in the * main library in case an extension is statically linked into an application. */ const char * Tcl_InitStubs(Tcl_Interp *interp, const char *version, int exact); const char * TclTomMathInitializeStubs(Tcl_Interp *interp, const char *version, int epoch, int revision); /* * When not using stubs, make it a macro. */ #ifndef USE_TCL_STUBS #define Tcl_InitStubs(interp, version, exact) \ Tcl_PkgInitStubsCheck(interp, version, exact) #endif /* * TODO - tommath stubs export goes here! */ /* * Public functions that are not accessible via the stubs table. * Tcl_GetMemoryInfo is needed for AOLserver. [Bug 1868171] */ #define Tcl_Main(argc, argv, proc) Tcl_MainEx(argc, argv, proc, \ (Tcl_FindExecutable(argv[0]), (Tcl_CreateInterp)())) EXTERN void Tcl_MainEx(int argc, char **argv, Tcl_AppInitProc *appInitProc, Tcl_Interp *interp); EXTERN const char * Tcl_PkgInitStubsCheck(Tcl_Interp *interp, const char *version, int exact); #if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC) EXTERN void Tcl_GetMemoryInfo(Tcl_DString *dsPtr); #endif /* *---------------------------------------------------------------------------- * Include the public function declarations that are accessible via the stubs * table. */ #include "tclDecls.h" /* * Include platform specific public function declarations that are accessible * via the stubs table. */ #include "tclPlatDecls.h" /* *---------------------------------------------------------------------------- * The following declarations either map ckalloc and ckfree to malloc and * free, or they map them to functions with all sorts of debugging hooks * defined in tclCkalloc.c. */ #ifdef TCL_MEM_DEBUG # define ckalloc(x) \ ((VOID *) Tcl_DbCkalloc((unsigned)(x), __FILE__, __LINE__)) # define ckfree(x) \ Tcl_DbCkfree((char *)(x), __FILE__, __LINE__) # define ckrealloc(x,y) \ ((VOID *) Tcl_DbCkrealloc((char *)(x), (unsigned)(y), __FILE__, __LINE__)) # define attemptckalloc(x) \ ((VOID *) Tcl_AttemptDbCkalloc((unsigned)(x), __FILE__, __LINE__)) # define attemptckrealloc(x,y) \ ((VOID *) Tcl_AttemptDbCkrealloc((char *)(x), (unsigned)(y), __FILE__, __LINE__)) #else /* !TCL_MEM_DEBUG */ /* * If we are not using the debugging allocator, we should call the Tcl_Alloc, * et al. routines in order to guarantee that every module is using the same * memory allocator both inside and outside of the Tcl library. */ # define ckalloc(x) \ ((VOID *) Tcl_Alloc((unsigned)(x))) # define ckfree(x) \ Tcl_Free((char *)(x)) # define ckrealloc(x,y) \ ((VOID *) Tcl_Realloc((char *)(x), (unsigned)(y))) # define attemptckalloc(x) \ ((VOID *) Tcl_AttemptAlloc((unsigned)(x))) # define attemptckrealloc(x,y) \ ((VOID *) Tcl_AttemptRealloc((char *)(x), (unsigned)(y))) # undef Tcl_InitMemory # define Tcl_InitMemory(x) # undef Tcl_DumpActiveMemory # define Tcl_DumpActiveMemory(x) # undef Tcl_ValidateAllMemory # define Tcl_ValidateAllMemory(x,y) #endif /* !TCL_MEM_DEBUG */ #ifdef TCL_MEM_DEBUG # define Tcl_IncrRefCount(objPtr) \ Tcl_DbIncrRefCount(objPtr, __FILE__, __LINE__) # define Tcl_DecrRefCount(objPtr) \ Tcl_DbDecrRefCount(objPtr, __FILE__, __LINE__) # define Tcl_IsShared(objPtr) \ Tcl_DbIsShared(objPtr, __FILE__, __LINE__) #else # define Tcl_IncrRefCount(objPtr) \ ++(objPtr)->refCount /* * Use do/while0 idiom for optimum correctness without compiler warnings. * http://c2.com/cgi/wiki?TrivialDoWhileLoop */ # define Tcl_DecrRefCount(objPtr) \ do { \ Tcl_Obj *_objPtr = (objPtr); \ if (--(_objPtr)->refCount <= 0) { \ TclFreeObj(_objPtr); \ } \ } while(0) # define Tcl_IsShared(objPtr) \ ((objPtr)->refCount > 1) #endif /* * Macros and definitions that help to debug the use of Tcl objects. When * TCL_MEM_DEBUG is defined, the Tcl_New declarations are overridden to call * debugging versions of the object creation functions. */ #ifdef TCL_MEM_DEBUG # undef Tcl_NewBignumObj # define Tcl_NewBignumObj(val) \ Tcl_DbNewBignumObj(val, __FILE__, __LINE__) # undef Tcl_NewBooleanObj # define Tcl_NewBooleanObj(val) \ Tcl_DbNewBooleanObj(val, __FILE__, __LINE__) # undef Tcl_NewByteArrayObj # define Tcl_NewByteArrayObj(bytes, len) \ Tcl_DbNewByteArrayObj(bytes, len, __FILE__, __LINE__) # undef Tcl_NewDoubleObj # define Tcl_NewDoubleObj(val) \ Tcl_DbNewDoubleObj(val, __FILE__, __LINE__) # undef Tcl_NewIntObj # define Tcl_NewIntObj(val) \ Tcl_DbNewLongObj(val, __FILE__, __LINE__) # undef Tcl_NewListObj # define Tcl_NewListObj(objc, objv) \ Tcl_DbNewListObj(objc, objv, __FILE__, __LINE__) # undef Tcl_NewLongObj # define Tcl_NewLongObj(val) \ Tcl_DbNewLongObj(val, __FILE__, __LINE__) # undef Tcl_NewObj # define Tcl_NewObj() \ Tcl_DbNewObj(__FILE__, __LINE__) # undef Tcl_NewStringObj # define Tcl_NewStringObj(bytes, len) \ Tcl_DbNewStringObj(bytes, len, __FILE__, __LINE__) # undef Tcl_NewWideIntObj # define Tcl_NewWideIntObj(val) \ Tcl_DbNewWideIntObj(val, __FILE__, __LINE__) #endif /* TCL_MEM_DEBUG */ /* *---------------------------------------------------------------------------- * Macros for clients to use to access fields of hash entries: */ #define Tcl_GetHashValue(h) ((h)->clientData) #define Tcl_SetHashValue(h, value) ((h)->clientData = (ClientData) (value)) #define Tcl_GetHashKey(tablePtr, h) \ ((void *) (((tablePtr)->keyType == TCL_ONE_WORD_KEYS || \ (tablePtr)->keyType == TCL_CUSTOM_PTR_KEYS) \ ? (h)->key.oneWordValue \ : (h)->key.string)) /* * Macros to use for clients to use to invoke find and create functions for * hash tables: */ #undef Tcl_FindHashEntry #define Tcl_FindHashEntry(tablePtr, key) \ (*((tablePtr)->findProc))(tablePtr, (const char *)(key)) #undef Tcl_CreateHashEntry #define Tcl_CreateHashEntry(tablePtr, key, newPtr) \ (*((tablePtr)->createProc))(tablePtr, (const char *)(key), newPtr) /* *---------------------------------------------------------------------------- * Macros that eliminate the overhead of the thread synchronization functions * when compiling without thread support. */ #ifndef TCL_THREADS #undef Tcl_MutexLock #define Tcl_MutexLock(mutexPtr) #undef Tcl_MutexUnlock #define Tcl_MutexUnlock(mutexPtr) #undef Tcl_MutexFinalize #define Tcl_MutexFinalize(mutexPtr) #undef Tcl_ConditionNotify #define Tcl_ConditionNotify(condPtr) #undef Tcl_ConditionWait #define Tcl_ConditionWait(condPtr, mutexPtr, timePtr) #undef Tcl_ConditionFinalize #define Tcl_ConditionFinalize(condPtr) #endif /* TCL_THREADS */ /* *---------------------------------------------------------------------------- * Deprecated Tcl functions: */ #ifndef TCL_NO_DEPRECATED # undef Tcl_EvalObj # define Tcl_EvalObj(interp,objPtr) \ Tcl_EvalObjEx((interp),(objPtr),0) # undef Tcl_GlobalEvalObj # define Tcl_GlobalEvalObj(interp,objPtr) \ Tcl_EvalObjEx((interp),(objPtr),TCL_EVAL_GLOBAL) /* * These function have been renamed. The old names are deprecated, but we * define these macros for backwards compatibilty. */ # define Tcl_Ckalloc Tcl_Alloc # define Tcl_Ckfree Tcl_Free # define Tcl_Ckrealloc Tcl_Realloc # define Tcl_Return Tcl_SetResult # define Tcl_TildeSubst Tcl_TranslateFileName # define panic Tcl_Panic # define panicVA Tcl_PanicVA #endif /* !TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------------- * Convenience declaration of Tcl_AppInit for backwards compatibility. This * function is not *implemented* by the tcl library, so the storage class is * neither DLLEXPORT nor DLLIMPORT. */ extern Tcl_AppInitProc Tcl_AppInit; #endif /* RC_INVOKED */ /* * end block for C++ */ #ifdef __cplusplus } #endif #endif /* _TCL */ /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */ |
Added compat/tcl-8.6/generic/tclDecls.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* * tclDecls.h -- * * Declarations of functions in the platform independent public Tcl API. * * Copyright (c) 1998-1999 by Scriptics Corporation. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #ifndef _TCLDECLS #define _TCLDECLS #undef TCL_STORAGE_CLASS #ifdef BUILD_tcl # define TCL_STORAGE_CLASS DLLEXPORT #else # ifdef USE_TCL_STUBS # define TCL_STORAGE_CLASS # else # define TCL_STORAGE_CLASS DLLIMPORT # endif #endif /* * WARNING: This file is automatically generated by the tools/genStubs.tcl * script. Any modifications to the function declarations below should be made * in the generic/tcl.decls script. */ /* !BEGIN!: Do not edit below this line. */ /* * Exported function declarations: */ /* 0 */ EXTERN int Tcl_PkgProvideEx(Tcl_Interp *interp, const char *name, const char *version, const void *clientData); /* 1 */ EXTERN CONST84_RETURN char * Tcl_PkgRequireEx(Tcl_Interp *interp, const char *name, const char *version, int exact, void *clientDataPtr); /* 2 */ EXTERN void Tcl_Panic(const char *format, ...) TCL_FORMAT_PRINTF(1, 2); /* 3 */ EXTERN char * Tcl_Alloc(unsigned int size); /* 4 */ EXTERN void Tcl_Free(char *ptr); /* 5 */ EXTERN char * Tcl_Realloc(char *ptr, unsigned int size); /* 6 */ EXTERN char * Tcl_DbCkalloc(unsigned int size, const char *file, int line); /* 7 */ EXTERN void Tcl_DbCkfree(char *ptr, const char *file, int line); /* 8 */ EXTERN char * Tcl_DbCkrealloc(char *ptr, unsigned int size, const char *file, int line); #if !defined(__WIN32__) && !defined(MAC_OSX_TCL) /* UNIX */ /* 9 */ EXTERN void Tcl_CreateFileHandler(int fd, int mask, Tcl_FileProc *proc, ClientData clientData); #endif /* UNIX */ #ifdef MAC_OSX_TCL /* MACOSX */ /* 9 */ EXTERN void Tcl_CreateFileHandler(int fd, int mask, Tcl_FileProc *proc, ClientData clientData); #endif /* MACOSX */ #if !defined(__WIN32__) && !defined(MAC_OSX_TCL) /* UNIX */ /* 10 */ EXTERN void Tcl_DeleteFileHandler(int fd); #endif /* UNIX */ #ifdef MAC_OSX_TCL /* MACOSX */ /* 10 */ EXTERN void Tcl_DeleteFileHandler(int fd); #endif /* MACOSX */ /* 11 */ EXTERN void Tcl_SetTimer(const Tcl_Time *timePtr); /* 12 */ EXTERN void Tcl_Sleep(int ms); /* 13 */ EXTERN int Tcl_WaitForEvent(const Tcl_Time *timePtr); /* 14 */ EXTERN int Tcl_AppendAllObjTypes(Tcl_Interp *interp, Tcl_Obj *objPtr); /* 15 */ EXTERN void Tcl_AppendStringsToObj(Tcl_Obj *objPtr, ...); /* 16 */ EXTERN void Tcl_AppendToObj(Tcl_Obj *objPtr, const char *bytes, int length); /* 17 */ EXTERN Tcl_Obj * Tcl_ConcatObj(int objc, Tcl_Obj *const objv[]); /* 18 */ EXTERN int Tcl_ConvertToType(Tcl_Interp *interp, Tcl_Obj *objPtr, const Tcl_ObjType *typePtr); /* 19 */ EXTERN void Tcl_DbDecrRefCount(Tcl_Obj *objPtr, const char *file, int line); /* 20 */ EXTERN void Tcl_DbIncrRefCount(Tcl_Obj *objPtr, const char *file, int line); /* 21 */ EXTERN int Tcl_DbIsShared(Tcl_Obj *objPtr, const char *file, int line); /* 22 */ EXTERN Tcl_Obj * Tcl_DbNewBooleanObj(int boolValue, const char *file, int line); /* 23 */ EXTERN Tcl_Obj * Tcl_DbNewByteArrayObj(const unsigned char *bytes, int length, const char *file, int line); /* 24 */ EXTERN Tcl_Obj * Tcl_DbNewDoubleObj(double doubleValue, const char *file, int line); /* 25 */ EXTERN Tcl_Obj * Tcl_DbNewListObj(int objc, Tcl_Obj *const *objv, const char *file, int line); /* 26 */ EXTERN Tcl_Obj * Tcl_DbNewLongObj(long longValue, const char *file, int line); /* 27 */ EXTERN Tcl_Obj * Tcl_DbNewObj(const char *file, int line); /* 28 */ EXTERN Tcl_Obj * Tcl_DbNewStringObj(const char *bytes, int length, const char *file, int line); /* 29 */ EXTERN Tcl_Obj * Tcl_DuplicateObj(Tcl_Obj *objPtr); /* 30 */ EXTERN void TclFreeObj(Tcl_Obj *objPtr); /* 31 */ EXTERN int Tcl_GetBoolean(Tcl_Interp *interp, const char *src, int *boolPtr); /* 32 */ EXTERN int Tcl_GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *boolPtr); /* 33 */ EXTERN unsigned char * Tcl_GetByteArrayFromObj(Tcl_Obj *objPtr, int *lengthPtr); /* 34 */ EXTERN int Tcl_GetDouble(Tcl_Interp *interp, const char *src, double *doublePtr); /* 35 */ EXTERN int Tcl_GetDoubleFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, double *doublePtr); /* 36 */ EXTERN int Tcl_GetIndexFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, CONST84 char *const *tablePtr, const char *msg, int flags, int *indexPtr); /* 37 */ EXTERN int Tcl_GetInt(Tcl_Interp *interp, const char *src, int *intPtr); /* 38 */ EXTERN int Tcl_GetIntFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *intPtr); /* 39 */ EXTERN int Tcl_GetLongFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, long *longPtr); /* 40 */ EXTERN CONST86 Tcl_ObjType * Tcl_GetObjType(const char *typeName); /* 41 */ EXTERN char * Tcl_GetStringFromObj(Tcl_Obj *objPtr, int *lengthPtr); /* 42 */ EXTERN void Tcl_InvalidateStringRep(Tcl_Obj *objPtr); /* 43 */ EXTERN int Tcl_ListObjAppendList(Tcl_Interp *interp, Tcl_Obj *listPtr, Tcl_Obj *elemListPtr); /* 44 */ EXTERN int Tcl_ListObjAppendElement(Tcl_Interp *interp, Tcl_Obj *listPtr, Tcl_Obj *objPtr); /* 45 */ EXTERN int Tcl_ListObjGetElements(Tcl_Interp *interp, Tcl_Obj *listPtr, int *objcPtr, Tcl_Obj ***objvPtr); /* 46 */ EXTERN int Tcl_ListObjIndex(Tcl_Interp *interp, Tcl_Obj *listPtr, int index, Tcl_Obj **objPtrPtr); /* 47 */ EXTERN int Tcl_ListObjLength(Tcl_Interp *interp, Tcl_Obj *listPtr, int *lengthPtr); /* 48 */ EXTERN int Tcl_ListObjReplace(Tcl_Interp *interp, Tcl_Obj *listPtr, int first, int count, int objc, Tcl_Obj *const objv[]); /* 49 */ EXTERN Tcl_Obj * Tcl_NewBooleanObj(int boolValue); /* 50 */ EXTERN Tcl_Obj * Tcl_NewByteArrayObj(const unsigned char *bytes, int length); /* 51 */ EXTERN Tcl_Obj * Tcl_NewDoubleObj(double doubleValue); /* 52 */ EXTERN Tcl_Obj * Tcl_NewIntObj(int intValue); /* 53 */ EXTERN Tcl_Obj * Tcl_NewListObj(int objc, Tcl_Obj *const objv[]); /* 54 */ EXTERN Tcl_Obj * Tcl_NewLongObj(long longValue); /* 55 */ EXTERN Tcl_Obj * Tcl_NewObj(void); /* 56 */ EXTERN Tcl_Obj * Tcl_NewStringObj(const char *bytes, int length); /* 57 */ EXTERN void Tcl_SetBooleanObj(Tcl_Obj *objPtr, int boolValue); /* 58 */ EXTERN unsigned char * Tcl_SetByteArrayLength(Tcl_Obj *objPtr, int length); /* 59 */ EXTERN void Tcl_SetByteArrayObj(Tcl_Obj *objPtr, const unsigned char *bytes, int length); /* 60 */ EXTERN void Tcl_SetDoubleObj(Tcl_Obj *objPtr, double doubleValue); /* 61 */ EXTERN void Tcl_SetIntObj(Tcl_Obj *objPtr, int intValue); /* 62 */ EXTERN void Tcl_SetListObj(Tcl_Obj *objPtr, int objc, Tcl_Obj *const objv[]); /* 63 */ EXTERN void Tcl_SetLongObj(Tcl_Obj *objPtr, long longValue); /* 64 */ EXTERN void Tcl_SetObjLength(Tcl_Obj *objPtr, int length); /* 65 */ EXTERN void Tcl_SetStringObj(Tcl_Obj *objPtr, const char *bytes, int length); /* 66 */ EXTERN void Tcl_AddErrorInfo(Tcl_Interp *interp, const char *message); /* 67 */ EXTERN void Tcl_AddObjErrorInfo(Tcl_Interp *interp, const char *message, int length); /* 68 */ EXTERN void Tcl_AllowExceptions(Tcl_Interp *interp); /* 69 */ EXTERN void Tcl_AppendElement(Tcl_Interp *interp, const char *element); /* 70 */ EXTERN void Tcl_AppendResult(Tcl_Interp *interp, ...); /* 71 */ EXTERN Tcl_AsyncHandler Tcl_AsyncCreate(Tcl_AsyncProc *proc, ClientData clientData); /* 72 */ EXTERN void Tcl_AsyncDelete(Tcl_AsyncHandler async); /* 73 */ EXTERN int Tcl_AsyncInvoke(Tcl_Interp *interp, int code); /* 74 */ EXTERN void Tcl_AsyncMark(Tcl_AsyncHandler async); /* 75 */ EXTERN int Tcl_AsyncReady(void); /* 76 */ EXTERN void Tcl_BackgroundError(Tcl_Interp *interp); /* 77 */ EXTERN char Tcl_Backslash(const char *src, int *readPtr); /* 78 */ EXTERN int Tcl_BadChannelOption(Tcl_Interp *interp, const char *optionName, const char *optionList); /* 79 */ EXTERN void Tcl_CallWhenDeleted(Tcl_Interp *interp, Tcl_InterpDeleteProc *proc, ClientData clientData); /* 80 */ EXTERN void Tcl_CancelIdleCall(Tcl_IdleProc *idleProc, ClientData clientData); /* 81 */ EXTERN int Tcl_Close(Tcl_Interp *interp, Tcl_Channel chan); /* 82 */ EXTERN int Tcl_CommandComplete(const char *cmd); /* 83 */ EXTERN char * Tcl_Concat(int argc, CONST84 char *const *argv); /* 84 */ EXTERN int Tcl_ConvertElement(const char *src, char *dst, int flags); /* 85 */ EXTERN int Tcl_ConvertCountedElement(const char *src, int length, char *dst, int flags); /* 86 */ EXTERN int Tcl_CreateAlias(Tcl_Interp *slave, const char *slaveCmd, Tcl_Interp *target, const char *targetCmd, int argc, CONST84 char *const *argv); /* 87 */ EXTERN int Tcl_CreateAliasObj(Tcl_Interp *slave, const char *slaveCmd, Tcl_Interp *target, const char *targetCmd, int objc, Tcl_Obj *const objv[]); /* 88 */ EXTERN Tcl_Channel Tcl_CreateChannel(const Tcl_ChannelType *typePtr, const char *chanName, ClientData instanceData, int mask); /* 89 */ EXTERN void Tcl_CreateChannelHandler(Tcl_Channel chan, int mask, Tcl_ChannelProc *proc, ClientData clientData); /* 90 */ EXTERN void Tcl_CreateCloseHandler(Tcl_Channel chan, Tcl_CloseProc *proc, ClientData clientData); /* 91 */ EXTERN Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); /* 92 */ EXTERN void Tcl_CreateEventSource(Tcl_EventSetupProc *setupProc, Tcl_EventCheckProc *checkProc, ClientData clientData); /* 93 */ EXTERN void Tcl_CreateExitHandler(Tcl_ExitProc *proc, ClientData clientData); /* 94 */ EXTERN Tcl_Interp * Tcl_CreateInterp(void); /* 95 */ EXTERN void Tcl_CreateMathFunc(Tcl_Interp *interp, const char *name, int numArgs, Tcl_ValueType *argTypes, Tcl_MathProc *proc, ClientData clientData); /* 96 */ EXTERN Tcl_Command Tcl_CreateObjCommand(Tcl_Interp *interp, const char *cmdName, Tcl_ObjCmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); /* 97 */ EXTERN Tcl_Interp * Tcl_CreateSlave(Tcl_Interp *interp, const char *slaveName, int isSafe); /* 98 */ EXTERN Tcl_TimerToken Tcl_CreateTimerHandler(int milliseconds, Tcl_TimerProc *proc, ClientData clientData); /* 99 */ EXTERN Tcl_Trace Tcl_CreateTrace(Tcl_Interp *interp, int level, Tcl_CmdTraceProc *proc, ClientData clientData); /* 100 */ EXTERN void Tcl_DeleteAssocData(Tcl_Interp *interp, const char *name); /* 101 */ EXTERN void Tcl_DeleteChannelHandler(Tcl_Channel chan, Tcl_ChannelProc *proc, ClientData clientData); /* 102 */ EXTERN void Tcl_DeleteCloseHandler(Tcl_Channel chan, Tcl_CloseProc *proc, ClientData clientData); /* 103 */ EXTERN int Tcl_DeleteCommand(Tcl_Interp *interp, const char *cmdName); /* 104 */ EXTERN int Tcl_DeleteCommandFromToken(Tcl_Interp *interp, Tcl_Command command); /* 105 */ EXTERN void Tcl_DeleteEvents(Tcl_EventDeleteProc *proc, ClientData clientData); /* 106 */ EXTERN void Tcl_DeleteEventSource(Tcl_EventSetupProc *setupProc, Tcl_EventCheckProc *checkProc, ClientData clientData); /* 107 */ EXTERN void Tcl_DeleteExitHandler(Tcl_ExitProc *proc, ClientData clientData); /* 108 */ EXTERN void Tcl_DeleteHashEntry(Tcl_HashEntry *entryPtr); /* 109 */ EXTERN void Tcl_DeleteHashTable(Tcl_HashTable *tablePtr); /* 110 */ EXTERN void Tcl_DeleteInterp(Tcl_Interp *interp); /* 111 */ EXTERN void Tcl_DetachPids(int numPids, Tcl_Pid *pidPtr); /* 112 */ EXTERN void Tcl_DeleteTimerHandler(Tcl_TimerToken token); /* 113 */ EXTERN void Tcl_DeleteTrace(Tcl_Interp *interp, Tcl_Trace trace); /* 114 */ EXTERN void Tcl_DontCallWhenDeleted(Tcl_Interp *interp, Tcl_InterpDeleteProc *proc, ClientData clientData); /* 115 */ EXTERN int Tcl_DoOneEvent(int flags); /* 116 */ EXTERN void Tcl_DoWhenIdle(Tcl_IdleProc *proc, ClientData clientData); /* 117 */ EXTERN char * Tcl_DStringAppend(Tcl_DString *dsPtr, const char *bytes, int length); /* 118 */ EXTERN char * Tcl_DStringAppendElement(Tcl_DString *dsPtr, const char *element); /* 119 */ EXTERN void Tcl_DStringEndSublist(Tcl_DString *dsPtr); /* 120 */ EXTERN void Tcl_DStringFree(Tcl_DString *dsPtr); /* 121 */ EXTERN void Tcl_DStringGetResult(Tcl_Interp *interp, Tcl_DString *dsPtr); /* 122 */ EXTERN void Tcl_DStringInit(Tcl_DString *dsPtr); /* 123 */ EXTERN void Tcl_DStringResult(Tcl_Interp *interp, Tcl_DString *dsPtr); /* 124 */ EXTERN void Tcl_DStringSetLength(Tcl_DString *dsPtr, int length); /* 125 */ EXTERN void Tcl_DStringStartSublist(Tcl_DString *dsPtr); /* 126 */ EXTERN int Tcl_Eof(Tcl_Channel chan); /* 127 */ EXTERN CONST84_RETURN char * Tcl_ErrnoId(void); /* 128 */ EXTERN CONST84_RETURN char * Tcl_ErrnoMsg(int err); /* 129 */ EXTERN int Tcl_Eval(Tcl_Interp *interp, const char *script); /* 130 */ EXTERN int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName); /* 131 */ EXTERN int Tcl_EvalObj(Tcl_Interp *interp, Tcl_Obj *objPtr); /* 132 */ EXTERN void Tcl_EventuallyFree(ClientData clientData, Tcl_FreeProc *freeProc); /* 133 */ EXTERN void Tcl_Exit(int status); /* 134 */ EXTERN int Tcl_ExposeCommand(Tcl_Interp *interp, const char *hiddenCmdToken, const char *cmdName); /* 135 */ EXTERN int Tcl_ExprBoolean(Tcl_Interp *interp, const char *expr, int *ptr); /* 136 */ EXTERN int Tcl_ExprBooleanObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *ptr); /* 137 */ EXTERN int Tcl_ExprDouble(Tcl_Interp *interp, const char *expr, double *ptr); /* 138 */ EXTERN int Tcl_ExprDoubleObj(Tcl_Interp *interp, Tcl_Obj *objPtr, double *ptr); /* 139 */ EXTERN int Tcl_ExprLong(Tcl_Interp *interp, const char *expr, long *ptr); /* 140 */ EXTERN int Tcl_ExprLongObj(Tcl_Interp *interp, Tcl_Obj *objPtr, long *ptr); /* 141 */ EXTERN int Tcl_ExprObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_Obj **resultPtrPtr); /* 142 */ EXTERN int Tcl_ExprString(Tcl_Interp *interp, const char *expr); /* 143 */ EXTERN void Tcl_Finalize(void); /* 144 */ EXTERN void Tcl_FindExecutable(const char *argv0); /* 145 */ EXTERN Tcl_HashEntry * Tcl_FirstHashEntry(Tcl_HashTable *tablePtr, Tcl_HashSearch *searchPtr); /* 146 */ EXTERN int Tcl_Flush(Tcl_Channel chan); /* 147 */ EXTERN void Tcl_FreeResult(Tcl_Interp *interp); /* 148 */ EXTERN int Tcl_GetAlias(Tcl_Interp *interp, const char *slaveCmd, Tcl_Interp **targetInterpPtr, CONST84 char **targetCmdPtr, int *argcPtr, CONST84 char ***argvPtr); /* 149 */ EXTERN int Tcl_GetAliasObj(Tcl_Interp *interp, const char *slaveCmd, Tcl_Interp **targetInterpPtr, CONST84 char **targetCmdPtr, int *objcPtr, Tcl_Obj ***objv); /* 150 */ EXTERN ClientData Tcl_GetAssocData(Tcl_Interp *interp, const char *name, Tcl_InterpDeleteProc **procPtr); /* 151 */ EXTERN Tcl_Channel Tcl_GetChannel(Tcl_Interp *interp, const char *chanName, int *modePtr); /* 152 */ EXTERN int Tcl_GetChannelBufferSize(Tcl_Channel chan); /* 153 */ EXTERN int Tcl_GetChannelHandle(Tcl_Channel chan, int direction, ClientData *handlePtr); /* 154 */ EXTERN ClientData Tcl_GetChannelInstanceData(Tcl_Channel chan); /* 155 */ EXTERN int Tcl_GetChannelMode(Tcl_Channel chan); /* 156 */ EXTERN CONST84_RETURN char * Tcl_GetChannelName(Tcl_Channel chan); /* 157 */ EXTERN int Tcl_GetChannelOption(Tcl_Interp *interp, Tcl_Channel chan, const char *optionName, Tcl_DString *dsPtr); /* 158 */ EXTERN CONST86 Tcl_ChannelType * Tcl_GetChannelType(Tcl_Channel chan); /* 159 */ EXTERN int Tcl_GetCommandInfo(Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr); /* 160 */ EXTERN CONST84_RETURN char * Tcl_GetCommandName(Tcl_Interp *interp, Tcl_Command command); /* 161 */ EXTERN int Tcl_GetErrno(void); /* 162 */ EXTERN CONST84_RETURN char * Tcl_GetHostName(void); /* 163 */ EXTERN int Tcl_GetInterpPath(Tcl_Interp *askInterp, Tcl_Interp *slaveInterp); /* 164 */ EXTERN Tcl_Interp * Tcl_GetMaster(Tcl_Interp *interp); /* 165 */ EXTERN const char * Tcl_GetNameOfExecutable(void); /* 166 */ EXTERN Tcl_Obj * Tcl_GetObjResult(Tcl_Interp *interp); #if !defined(__WIN32__) && !defined(MAC_OSX_TCL) /* UNIX */ /* 167 */ EXTERN int Tcl_GetOpenFile(Tcl_Interp *interp, const char *chanID, int forWriting, int checkUsage, ClientData *filePtr); #endif /* UNIX */ #ifdef MAC_OSX_TCL /* MACOSX */ /* 167 */ EXTERN int Tcl_GetOpenFile(Tcl_Interp *interp, const char *chanID, int forWriting, int checkUsage, ClientData *filePtr); #endif /* MACOSX */ /* 168 */ EXTERN Tcl_PathType Tcl_GetPathType(const char *path); /* 169 */ EXTERN int Tcl_Gets(Tcl_Channel chan, Tcl_DString *dsPtr); /* 170 */ EXTERN int Tcl_GetsObj(Tcl_Channel chan, Tcl_Obj *objPtr); /* 171 */ EXTERN int Tcl_GetServiceMode(void); /* 172 */ EXTERN Tcl_Interp * Tcl_GetSlave(Tcl_Interp *interp, const char *slaveName); /* 173 */ EXTERN Tcl_Channel Tcl_GetStdChannel(int type); /* 174 */ EXTERN CONST84_RETURN char * Tcl_GetStringResult(Tcl_Interp *interp); /* 175 */ EXTERN CONST84_RETURN char * Tcl_GetVar(Tcl_Interp *interp, const char *varName, int flags); /* 176 */ EXTERN CONST84_RETURN char * Tcl_GetVar2(Tcl_Interp *interp, const char *part1, const char *part2, int flags); /* 177 */ EXTERN int Tcl_GlobalEval(Tcl_Interp *interp, const char *command); /* 178 */ EXTERN int Tcl_GlobalEvalObj(Tcl_Interp *interp, Tcl_Obj *objPtr); /* 179 */ EXTERN int Tcl_HideCommand(Tcl_Interp *interp, const char *cmdName, const char *hiddenCmdToken); /* 180 */ EXTERN int Tcl_Init(Tcl_Interp *interp); /* 181 */ EXTERN void Tcl_InitHashTable(Tcl_HashTable *tablePtr, int keyType); /* 182 */ EXTERN int Tcl_InputBlocked(Tcl_Channel chan); /* 183 */ EXTERN int Tcl_InputBuffered(Tcl_Channel chan); /* 184 */ EXTERN int Tcl_InterpDeleted(Tcl_Interp *interp); /* 185 */ EXTERN int Tcl_IsSafe(Tcl_Interp *interp); /* 186 */ EXTERN char * Tcl_JoinPath(int argc, CONST84 char *const *argv, Tcl_DString *resultPtr); /* 187 */ EXTERN int Tcl_LinkVar(Tcl_Interp *interp, const char *varName, char *addr, int type); /* Slot 188 is reserved */ /* 189 */ EXTERN Tcl_Channel Tcl_MakeFileChannel(ClientData handle, int mode); /* 190 */ EXTERN int Tcl_MakeSafe(Tcl_Interp *interp); /* 191 */ EXTERN Tcl_Channel Tcl_MakeTcpClientChannel(ClientData tcpSocket); /* 192 */ EXTERN char * Tcl_Merge(int argc, CONST84 char *const *argv); /* 193 */ EXTERN Tcl_HashEntry * Tcl_NextHashEntry(Tcl_HashSearch *searchPtr); /* 194 */ EXTERN void Tcl_NotifyChannel(Tcl_Channel channel, int mask); /* 195 */ EXTERN Tcl_Obj * Tcl_ObjGetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, int flags); /* 196 */ EXTERN Tcl_Obj * Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); /* 197 */ EXTERN Tcl_Channel Tcl_OpenCommandChannel(Tcl_Interp *interp, int argc, CONST84 char **argv, int flags); /* 198 */ EXTERN Tcl_Channel Tcl_OpenFileChannel(Tcl_Interp *interp, const char *fileName, const char *modeString, int permissions); /* 199 */ EXTERN Tcl_Channel Tcl_OpenTcpClient(Tcl_Interp *interp, int port, const char *address, const char *myaddr, int myport, int async); /* 200 */ EXTERN Tcl_Channel Tcl_OpenTcpServer(Tcl_Interp *interp, int port, const char *host, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); /* 201 */ EXTERN void Tcl_Preserve(ClientData data); /* 202 */ EXTERN void Tcl_PrintDouble(Tcl_Interp *interp, double value, char *dst); /* 203 */ EXTERN int Tcl_PutEnv(const char *assignment); /* 204 */ EXTERN CONST84_RETURN char * Tcl_PosixError(Tcl_Interp *interp); /* 205 */ EXTERN void Tcl_QueueEvent(Tcl_Event *evPtr, Tcl_QueuePosition position); /* 206 */ EXTERN int Tcl_Read(Tcl_Channel chan, char *bufPtr, int toRead); /* 207 */ EXTERN void Tcl_ReapDetachedProcs(void); /* 208 */ EXTERN int Tcl_RecordAndEval(Tcl_Interp *interp, const char *cmd, int flags); /* 209 */ EXTERN int Tcl_RecordAndEvalObj(Tcl_Interp *interp, Tcl_Obj *cmdPtr, int flags); /* 210 */ EXTERN void Tcl_RegisterChannel(Tcl_Interp *interp, Tcl_Channel chan); /* 211 */ EXTERN void Tcl_RegisterObjType(const Tcl_ObjType *typePtr); /* 212 */ EXTERN Tcl_RegExp Tcl_RegExpCompile(Tcl_Interp *interp, const char *pattern); /* 213 */ EXTERN int Tcl_RegExpExec(Tcl_Interp *interp, Tcl_RegExp regexp, const char *text, const char *start); /* 214 */ EXTERN int Tcl_RegExpMatch(Tcl_Interp *interp, const char *text, const char *pattern); /* 215 */ EXTERN void Tcl_RegExpRange(Tcl_RegExp regexp, int index, CONST84 char **startPtr, CONST84 char **endPtr); /* 216 */ EXTERN void Tcl_Release(ClientData clientData); /* 217 */ EXTERN void Tcl_ResetResult(Tcl_Interp *interp); /* 218 */ EXTERN int Tcl_ScanElement(const char *src, int *flagPtr); /* 219 */ EXTERN int Tcl_ScanCountedElement(const char *src, int length, int *flagPtr); /* 220 */ EXTERN int Tcl_SeekOld(Tcl_Channel chan, int offset, int mode); /* 221 */ EXTERN int Tcl_ServiceAll(void); /* 222 */ EXTERN int Tcl_ServiceEvent(int flags); /* 223 */ EXTERN void Tcl_SetAssocData(Tcl_Interp *interp, const char *name, Tcl_InterpDeleteProc *proc, ClientData clientData); /* 224 */ EXTERN void Tcl_SetChannelBufferSize(Tcl_Channel chan, int sz); /* 225 */ EXTERN int Tcl_SetChannelOption(Tcl_Interp *interp, Tcl_Channel chan, const char *optionName, const char *newValue); /* 226 */ EXTERN int Tcl_SetCommandInfo(Tcl_Interp *interp, const char *cmdName, const Tcl_CmdInfo *infoPtr); /* 227 */ EXTERN void Tcl_SetErrno(int err); /* 228 */ EXTERN void Tcl_SetErrorCode(Tcl_Interp *interp, ...); /* 229 */ EXTERN void Tcl_SetMaxBlockTime(const Tcl_Time *timePtr); /* 230 */ EXTERN void Tcl_SetPanicProc(Tcl_PanicProc *panicProc); /* 231 */ EXTERN int Tcl_SetRecursionLimit(Tcl_Interp *interp, int depth); /* 232 */ EXTERN void Tcl_SetResult(Tcl_Interp *interp, char *result, Tcl_FreeProc *freeProc); /* 233 */ EXTERN int Tcl_SetServiceMode(int mode); /* 234 */ EXTERN void Tcl_SetObjErrorCode(Tcl_Interp *interp, Tcl_Obj *errorObjPtr); /* 235 */ EXTERN void Tcl_SetObjResult(Tcl_Interp *interp, Tcl_Obj *resultObjPtr); /* 236 */ EXTERN void Tcl_SetStdChannel(Tcl_Channel channel, int type); /* 237 */ EXTERN CONST84_RETURN char * Tcl_SetVar(Tcl_Interp *interp, const char *varName, const char *newValue, int flags); /* 238 */ EXTERN CONST84_RETURN char * Tcl_SetVar2(Tcl_Interp *interp, const char *part1, const char *part2, const char *newValue, int flags); /* 239 */ EXTERN CONST84_RETURN char * Tcl_SignalId(int sig); /* 240 */ EXTERN CONST84_RETURN char * Tcl_SignalMsg(int sig); /* 241 */ EXTERN void Tcl_SourceRCFile(Tcl_Interp *interp); /* 242 */ EXTERN int Tcl_SplitList(Tcl_Interp *interp, const char *listStr, int *argcPtr, CONST84 char ***argvPtr); /* 243 */ EXTERN void Tcl_SplitPath(const char *path, int *argcPtr, CONST84 char ***argvPtr); /* 244 */ EXTERN void Tcl_StaticPackage(Tcl_Interp *interp, const char *pkgName, Tcl_PackageInitProc *initProc, Tcl_PackageInitProc *safeInitProc); /* 245 */ EXTERN int Tcl_StringMatch(const char *str, const char *pattern); /* 246 */ EXTERN int Tcl_TellOld(Tcl_Channel chan); /* 247 */ EXTERN int Tcl_TraceVar(Tcl_Interp *interp, const char *varName, int flags, Tcl_VarTraceProc *proc, ClientData clientData); /* 248 */ EXTERN int Tcl_TraceVar2(Tcl_Interp *interp, const char *part1, const char *part2, int flags, Tcl_VarTraceProc *proc, ClientData clientData); /* 249 */ EXTERN char * Tcl_TranslateFileName(Tcl_Interp *interp, const char *name, Tcl_DString *bufferPtr); /* 250 */ EXTERN int Tcl_Ungets(Tcl_Channel chan, const char *str, int len, int atHead); /* 251 */ EXTERN void Tcl_UnlinkVar(Tcl_Interp *interp, const char *varName); /* 252 */ EXTERN int Tcl_UnregisterChannel(Tcl_Interp *interp, Tcl_Channel chan); /* 253 */ EXTERN int Tcl_UnsetVar(Tcl_Interp *interp, const char *varName, int flags); /* 254 */ EXTERN int Tcl_UnsetVar2(Tcl_Interp *interp, const char *part1, const char *part2, int flags); /* 255 */ EXTERN void Tcl_UntraceVar(Tcl_Interp *interp, const char *varName, int flags, Tcl_VarTraceProc *proc, ClientData clientData); /* 256 */ EXTERN void Tcl_UntraceVar2(Tcl_Interp *interp, const char *part1, const char *part2, int flags, Tcl_VarTraceProc *proc, ClientData clientData); /* 257 */ EXTERN void Tcl_UpdateLinkedVar(Tcl_Interp *interp, const char *varName); /* 258 */ EXTERN int Tcl_UpVar(Tcl_Interp *interp, const char *frameName, const char *varName, const char *localName, int flags); /* 259 */ EXTERN int Tcl_UpVar2(Tcl_Interp *interp, const char *frameName, const char *part1, const char *part2, const char *localName, int flags); /* 260 */ EXTERN int Tcl_VarEval(Tcl_Interp *interp, ...); /* 261 */ EXTERN ClientData Tcl_VarTraceInfo(Tcl_Interp *interp, const char *varName, int flags, Tcl_VarTraceProc *procPtr, ClientData prevClientData); /* 262 */ EXTERN ClientData Tcl_VarTraceInfo2(Tcl_Interp *interp, const char *part1, const char *part2, int flags, Tcl_VarTraceProc *procPtr, ClientData prevClientData); /* 263 */ EXTERN int Tcl_Write(Tcl_Channel chan, const char *s, int slen); /* 264 */ EXTERN void Tcl_WrongNumArgs(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], const char *message); /* 265 */ EXTERN int Tcl_DumpActiveMemory(const char *fileName); /* 266 */ EXTERN void Tcl_ValidateAllMemory(const char *file, int line); /* 267 */ EXTERN void Tcl_AppendResultVA(Tcl_Interp *interp, va_list argList); /* 268 */ EXTERN void Tcl_AppendStringsToObjVA(Tcl_Obj *objPtr, va_list argList); /* 269 */ EXTERN char * Tcl_HashStats(Tcl_HashTable *tablePtr); /* 270 */ EXTERN CONST84_RETURN char * Tcl_ParseVar(Tcl_Interp *interp, const char *start, CONST84 char **termPtr); /* 271 */ EXTERN CONST84_RETURN char * Tcl_PkgPresent(Tcl_Interp *interp, const char *name, const char *version, int exact); /* 272 */ EXTERN CONST84_RETURN char * Tcl_PkgPresentEx(Tcl_Interp *interp, const char *name, const char *version, int exact, void *clientDataPtr); /* 273 */ EXTERN int Tcl_PkgProvide(Tcl_Interp *interp, const char *name, const char *version); /* 274 */ EXTERN CONST84_RETURN char * Tcl_PkgRequire(Tcl_Interp *interp, const char *name, const char *version, int exact); /* 275 */ EXTERN void Tcl_SetErrorCodeVA(Tcl_Interp *interp, va_list argList); /* 276 */ EXTERN int Tcl_VarEvalVA(Tcl_Interp *interp, va_list argList); /* 277 */ EXTERN Tcl_Pid Tcl_WaitPid(Tcl_Pid pid, int *statPtr, int options); /* 278 */ EXTERN void Tcl_PanicVA(const char *format, va_list argList); /* 279 */ EXTERN void Tcl_GetVersion(int *major, int *minor, int *patchLevel, int *type); /* 280 */ EXTERN void Tcl_InitMemory(Tcl_Interp *interp); /* 281 */ EXTERN Tcl_Channel Tcl_StackChannel(Tcl_Interp *interp, const Tcl_ChannelType *typePtr, ClientData instanceData, int mask, Tcl_Channel prevChan); /* 282 */ EXTERN int Tcl_UnstackChannel(Tcl_Interp *interp, Tcl_Channel chan); /* 283 */ EXTERN Tcl_Channel Tcl_GetStackedChannel(Tcl_Channel chan); /* 284 */ EXTERN void Tcl_SetMainLoop(Tcl_MainLoopProc *proc); /* Slot 285 is reserved */ /* 286 */ EXTERN void Tcl_AppendObjToObj(Tcl_Obj *objPtr, Tcl_Obj *appendObjPtr); /* 287 */ EXTERN Tcl_Encoding Tcl_CreateEncoding(const Tcl_EncodingType *typePtr); /* 288 */ EXTERN void Tcl_CreateThreadExitHandler(Tcl_ExitProc *proc, ClientData clientData); /* 289 */ EXTERN void Tcl_DeleteThreadExitHandler(Tcl_ExitProc *proc, ClientData clientData); /* 290 */ EXTERN void Tcl_DiscardResult(Tcl_SavedResult *statePtr); /* 291 */ EXTERN int Tcl_EvalEx(Tcl_Interp *interp, const char *script, int numBytes, int flags); /* 292 */ EXTERN int Tcl_EvalObjv(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], int flags); /* 293 */ EXTERN int Tcl_EvalObjEx(Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 294 */ EXTERN void Tcl_ExitThread(int status); /* 295 */ EXTERN int Tcl_ExternalToUtf(Tcl_Interp *interp, Tcl_Encoding encoding, const char *src, int srcLen, int flags, Tcl_EncodingState *statePtr, char *dst, int dstLen, int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr); /* 296 */ EXTERN char * Tcl_ExternalToUtfDString(Tcl_Encoding encoding, const char *src, int srcLen, Tcl_DString *dsPtr); /* 297 */ EXTERN void Tcl_FinalizeThread(void); /* 298 */ EXTERN void Tcl_FinalizeNotifier(ClientData clientData); /* 299 */ EXTERN void Tcl_FreeEncoding(Tcl_Encoding encoding); /* 300 */ EXTERN Tcl_ThreadId Tcl_GetCurrentThread(void); /* 301 */ EXTERN Tcl_Encoding Tcl_GetEncoding(Tcl_Interp *interp, const char *name); /* 302 */ EXTERN CONST84_RETURN char * Tcl_GetEncodingName(Tcl_Encoding encoding); /* 303 */ EXTERN void Tcl_GetEncodingNames(Tcl_Interp *interp); /* 304 */ EXTERN int Tcl_GetIndexFromObjStruct(Tcl_Interp *interp, Tcl_Obj *objPtr, const void *tablePtr, int offset, const char *msg, int flags, int *indexPtr); /* 305 */ EXTERN void * Tcl_GetThreadData(Tcl_ThreadDataKey *keyPtr, int size); /* 306 */ EXTERN Tcl_Obj * Tcl_GetVar2Ex(Tcl_Interp *interp, const char *part1, const char *part2, int flags); /* 307 */ EXTERN ClientData Tcl_InitNotifier(void); /* 308 */ EXTERN void Tcl_MutexLock(Tcl_Mutex *mutexPtr); /* 309 */ EXTERN void Tcl_MutexUnlock(Tcl_Mutex *mutexPtr); /* 310 */ EXTERN void Tcl_ConditionNotify(Tcl_Condition *condPtr); /* 311 */ EXTERN void Tcl_ConditionWait(Tcl_Condition *condPtr, Tcl_Mutex *mutexPtr, const Tcl_Time *timePtr); /* 312 */ EXTERN int Tcl_NumUtfChars(const char *src, int length); /* 313 */ EXTERN int Tcl_ReadChars(Tcl_Channel channel, Tcl_Obj *objPtr, int charsToRead, int appendFlag); /* 314 */ EXTERN void Tcl_RestoreResult(Tcl_Interp *interp, Tcl_SavedResult *statePtr); /* 315 */ EXTERN void Tcl_SaveResult(Tcl_Interp *interp, Tcl_SavedResult *statePtr); /* 316 */ EXTERN int Tcl_SetSystemEncoding(Tcl_Interp *interp, const char *name); /* 317 */ EXTERN Tcl_Obj * Tcl_SetVar2Ex(Tcl_Interp *interp, const char *part1, const char *part2, Tcl_Obj *newValuePtr, int flags); /* 318 */ EXTERN void Tcl_ThreadAlert(Tcl_ThreadId threadId); /* 319 */ EXTERN void Tcl_ThreadQueueEvent(Tcl_ThreadId threadId, Tcl_Event *evPtr, Tcl_QueuePosition position); /* 320 */ EXTERN Tcl_UniChar Tcl_UniCharAtIndex(const char *src, int index); /* 321 */ EXTERN Tcl_UniChar Tcl_UniCharToLower(int ch); /* 322 */ EXTERN Tcl_UniChar Tcl_UniCharToTitle(int ch); /* 323 */ EXTERN Tcl_UniChar Tcl_UniCharToUpper(int ch); /* 324 */ EXTERN int Tcl_UniCharToUtf(int ch, char *buf); /* 325 */ EXTERN CONST84_RETURN char * Tcl_UtfAtIndex(const char *src, int index); /* 326 */ EXTERN int Tcl_UtfCharComplete(const char *src, int length); /* 327 */ EXTERN int Tcl_UtfBackslash(const char *src, int *readPtr, char *dst); /* 328 */ EXTERN CONST84_RETURN char * Tcl_UtfFindFirst(const char *src, int ch); /* 329 */ EXTERN CONST84_RETURN char * Tcl_UtfFindLast(const char *src, int ch); /* 330 */ EXTERN CONST84_RETURN char * Tcl_UtfNext(const char *src); /* 331 */ EXTERN CONST84_RETURN char * Tcl_UtfPrev(const char *src, const char *start); /* 332 */ EXTERN int Tcl_UtfToExternal(Tcl_Interp *interp, Tcl_Encoding encoding, const char *src, int srcLen, int flags, Tcl_EncodingState *statePtr, char *dst, int dstLen, int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr); /* 333 */ EXTERN char * Tcl_UtfToExternalDString(Tcl_Encoding encoding, const char *src, int srcLen, Tcl_DString *dsPtr); /* 334 */ EXTERN int Tcl_UtfToLower(char *src); /* 335 */ EXTERN int Tcl_UtfToTitle(char *src); /* 336 */ EXTERN int Tcl_UtfToUniChar(const char *src, Tcl_UniChar *chPtr); /* 337 */ EXTERN int Tcl_UtfToUpper(char *src); /* 338 */ EXTERN int Tcl_WriteChars(Tcl_Channel chan, const char *src, int srcLen); /* 339 */ EXTERN int Tcl_WriteObj(Tcl_Channel chan, Tcl_Obj *objPtr); /* 340 */ EXTERN char * Tcl_GetString(Tcl_Obj *objPtr); /* 341 */ EXTERN CONST84_RETURN char * Tcl_GetDefaultEncodingDir(void); /* 342 */ EXTERN void Tcl_SetDefaultEncodingDir(const char *path); /* 343 */ EXTERN void Tcl_AlertNotifier(ClientData clientData); /* 344 */ EXTERN void Tcl_ServiceModeHook(int mode); /* 345 */ EXTERN int Tcl_UniCharIsAlnum(int ch); /* 346 */ EXTERN int Tcl_UniCharIsAlpha(int ch); /* 347 */ EXTERN int Tcl_UniCharIsDigit(int ch); /* 348 */ EXTERN int Tcl_UniCharIsLower(int ch); /* 349 */ EXTERN int Tcl_UniCharIsSpace(int ch); /* 350 */ EXTERN int Tcl_UniCharIsUpper(int ch); /* 351 */ EXTERN int Tcl_UniCharIsWordChar(int ch); /* 352 */ EXTERN int Tcl_UniCharLen(const Tcl_UniChar *uniStr); /* 353 */ EXTERN int Tcl_UniCharNcmp(const Tcl_UniChar *ucs, const Tcl_UniChar *uct, unsigned long numChars); /* 354 */ EXTERN char * Tcl_UniCharToUtfDString(const Tcl_UniChar *uniStr, int uniLength, Tcl_DString *dsPtr); /* 355 */ EXTERN Tcl_UniChar * Tcl_UtfToUniCharDString(const char *src, int length, Tcl_DString *dsPtr); /* 356 */ EXTERN Tcl_RegExp Tcl_GetRegExpFromObj(Tcl_Interp *interp, Tcl_Obj *patObj, int flags); /* 357 */ EXTERN Tcl_Obj * Tcl_EvalTokens(Tcl_Interp *interp, Tcl_Token *tokenPtr, int count); /* 358 */ EXTERN void Tcl_FreeParse(Tcl_Parse *parsePtr); /* 359 */ EXTERN void Tcl_LogCommandInfo(Tcl_Interp *interp, const char *script, const char *command, int length); /* 360 */ EXTERN int Tcl_ParseBraces(Tcl_Interp *interp, const char *start, int numBytes, Tcl_Parse *parsePtr, int append, CONST84 char **termPtr); /* 361 */ EXTERN int Tcl_ParseCommand(Tcl_Interp *interp, const char *start, int numBytes, int nested, Tcl_Parse *parsePtr); /* 362 */ EXTERN int Tcl_ParseExpr(Tcl_Interp *interp, const char *start, int numBytes, Tcl_Parse *parsePtr); /* 363 */ EXTERN int Tcl_ParseQuotedString(Tcl_Interp *interp, const char *start, int numBytes, Tcl_Parse *parsePtr, int append, CONST84 char **termPtr); /* 364 */ EXTERN int Tcl_ParseVarName(Tcl_Interp *interp, const char *start, int numBytes, Tcl_Parse *parsePtr, int append); /* 365 */ EXTERN char * Tcl_GetCwd(Tcl_Interp *interp, Tcl_DString *cwdPtr); /* 366 */ EXTERN int Tcl_Chdir(const char *dirName); /* 367 */ EXTERN int Tcl_Access(const char *path, int mode); /* 368 */ EXTERN int Tcl_Stat(const char *path, struct stat *bufPtr); /* 369 */ EXTERN int Tcl_UtfNcmp(const char *s1, const char *s2, unsigned long n); /* 370 */ EXTERN int Tcl_UtfNcasecmp(const char *s1, const char *s2, unsigned long n); /* 371 */ EXTERN int Tcl_StringCaseMatch(const char *str, const char *pattern, int nocase); /* 372 */ EXTERN int Tcl_UniCharIsControl(int ch); /* 373 */ EXTERN int Tcl_UniCharIsGraph(int ch); /* 374 */ EXTERN int Tcl_UniCharIsPrint(int ch); /* 375 */ EXTERN int Tcl_UniCharIsPunct(int ch); /* 376 */ EXTERN int Tcl_RegExpExecObj(Tcl_Interp *interp, Tcl_RegExp regexp, Tcl_Obj *textObj, int offset, int nmatches, int flags); /* 377 */ EXTERN void Tcl_RegExpGetInfo(Tcl_RegExp regexp, Tcl_RegExpInfo *infoPtr); /* 378 */ EXTERN Tcl_Obj * Tcl_NewUnicodeObj(const Tcl_UniChar *unicode, int numChars); /* 379 */ EXTERN void Tcl_SetUnicodeObj(Tcl_Obj *objPtr, const Tcl_UniChar *unicode, int numChars); /* 380 */ EXTERN int Tcl_GetCharLength(Tcl_Obj *objPtr); /* 381 */ EXTERN Tcl_UniChar Tcl_GetUniChar(Tcl_Obj *objPtr, int index); /* 382 */ EXTERN Tcl_UniChar * Tcl_GetUnicode(Tcl_Obj *objPtr); /* 383 */ EXTERN Tcl_Obj * Tcl_GetRange(Tcl_Obj *objPtr, int first, int last); /* 384 */ EXTERN void Tcl_AppendUnicodeToObj(Tcl_Obj *objPtr, const Tcl_UniChar *unicode, int length); /* 385 */ EXTERN int Tcl_RegExpMatchObj(Tcl_Interp *interp, Tcl_Obj *textObj, Tcl_Obj *patternObj); /* 386 */ EXTERN void Tcl_SetNotifier(Tcl_NotifierProcs *notifierProcPtr); /* 387 */ EXTERN Tcl_Mutex * Tcl_GetAllocMutex(void); /* 388 */ EXTERN int Tcl_GetChannelNames(Tcl_Interp *interp); /* 389 */ EXTERN int Tcl_GetChannelNamesEx(Tcl_Interp *interp, const char *pattern); /* 390 */ EXTERN int Tcl_ProcObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); /* 391 */ EXTERN void Tcl_ConditionFinalize(Tcl_Condition *condPtr); /* 392 */ EXTERN void Tcl_MutexFinalize(Tcl_Mutex *mutex); /* 393 */ EXTERN int Tcl_CreateThread(Tcl_ThreadId *idPtr, Tcl_ThreadCreateProc *proc, ClientData clientData, int stackSize, int flags); /* 394 */ EXTERN int Tcl_ReadRaw(Tcl_Channel chan, char *dst, int bytesToRead); /* 395 */ EXTERN int Tcl_WriteRaw(Tcl_Channel chan, const char *src, int srcLen); /* 396 */ EXTERN Tcl_Channel Tcl_GetTopChannel(Tcl_Channel chan); /* 397 */ EXTERN int Tcl_ChannelBuffered(Tcl_Channel chan); /* 398 */ EXTERN CONST84_RETURN char * Tcl_ChannelName( const Tcl_ChannelType *chanTypePtr); /* 399 */ EXTERN Tcl_ChannelTypeVersion Tcl_ChannelVersion( const Tcl_ChannelType *chanTypePtr); /* 400 */ EXTERN Tcl_DriverBlockModeProc * Tcl_ChannelBlockModeProc( const Tcl_ChannelType *chanTypePtr); /* 401 */ EXTERN Tcl_DriverCloseProc * Tcl_ChannelCloseProc( const Tcl_ChannelType *chanTypePtr); /* 402 */ EXTERN Tcl_DriverClose2Proc * Tcl_ChannelClose2Proc( const Tcl_ChannelType *chanTypePtr); /* 403 */ EXTERN Tcl_DriverInputProc * Tcl_ChannelInputProc( const Tcl_ChannelType *chanTypePtr); /* 404 */ EXTERN Tcl_DriverOutputProc * Tcl_ChannelOutputProc( const Tcl_ChannelType *chanTypePtr); /* 405 */ EXTERN Tcl_DriverSeekProc * Tcl_ChannelSeekProc( const Tcl_ChannelType *chanTypePtr); /* 406 */ EXTERN Tcl_DriverSetOptionProc * Tcl_ChannelSetOptionProc( const Tcl_ChannelType *chanTypePtr); /* 407 */ EXTERN Tcl_DriverGetOptionProc * Tcl_ChannelGetOptionProc( const Tcl_ChannelType *chanTypePtr); /* 408 */ EXTERN Tcl_DriverWatchProc * Tcl_ChannelWatchProc( const Tcl_ChannelType *chanTypePtr); /* 409 */ EXTERN Tcl_DriverGetHandleProc * Tcl_ChannelGetHandleProc( const Tcl_ChannelType *chanTypePtr); /* 410 */ EXTERN Tcl_DriverFlushProc * Tcl_ChannelFlushProc( const Tcl_ChannelType *chanTypePtr); /* 411 */ EXTERN Tcl_DriverHandlerProc * Tcl_ChannelHandlerProc( const Tcl_ChannelType *chanTypePtr); /* 412 */ EXTERN int Tcl_JoinThread(Tcl_ThreadId threadId, int *result); /* 413 */ EXTERN int Tcl_IsChannelShared(Tcl_Channel channel); /* 414 */ EXTERN int Tcl_IsChannelRegistered(Tcl_Interp *interp, Tcl_Channel channel); /* 415 */ EXTERN void Tcl_CutChannel(Tcl_Channel channel); /* 416 */ EXTERN void Tcl_SpliceChannel(Tcl_Channel channel); /* 417 */ EXTERN void Tcl_ClearChannelHandlers(Tcl_Channel channel); /* 418 */ EXTERN int Tcl_IsChannelExisting(const char *channelName); /* 419 */ EXTERN int Tcl_UniCharNcasecmp(const Tcl_UniChar *ucs, const Tcl_UniChar *uct, unsigned long numChars); /* 420 */ EXTERN int Tcl_UniCharCaseMatch(const Tcl_UniChar *uniStr, const Tcl_UniChar *uniPattern, int nocase); /* 421 */ EXTERN Tcl_HashEntry * Tcl_FindHashEntry(Tcl_HashTable *tablePtr, const void *key); /* 422 */ EXTERN Tcl_HashEntry * Tcl_CreateHashEntry(Tcl_HashTable *tablePtr, const void *key, int *newPtr); /* 423 */ EXTERN void Tcl_InitCustomHashTable(Tcl_HashTable *tablePtr, int keyType, const Tcl_HashKeyType *typePtr); /* 424 */ EXTERN void Tcl_InitObjHashTable(Tcl_HashTable *tablePtr); /* 425 */ EXTERN ClientData Tcl_CommandTraceInfo(Tcl_Interp *interp, const char *varName, int flags, Tcl_CommandTraceProc *procPtr, ClientData prevClientData); /* 426 */ EXTERN int Tcl_TraceCommand(Tcl_Interp *interp, const char *varName, int flags, Tcl_CommandTraceProc *proc, ClientData clientData); /* 427 */ EXTERN void Tcl_UntraceCommand(Tcl_Interp *interp, const char *varName, int flags, Tcl_CommandTraceProc *proc, ClientData clientData); /* 428 */ EXTERN char * Tcl_AttemptAlloc(unsigned int size); /* 429 */ EXTERN char * Tcl_AttemptDbCkalloc(unsigned int size, const char *file, int line); /* 430 */ EXTERN char * Tcl_AttemptRealloc(char *ptr, unsigned int size); /* 431 */ EXTERN char * Tcl_AttemptDbCkrealloc(char *ptr, unsigned int size, const char *file, int line); /* 432 */ EXTERN int Tcl_AttemptSetObjLength(Tcl_Obj *objPtr, int length); /* 433 */ EXTERN Tcl_ThreadId Tcl_GetChannelThread(Tcl_Channel channel); /* 434 */ EXTERN Tcl_UniChar * Tcl_GetUnicodeFromObj(Tcl_Obj *objPtr, int *lengthPtr); /* 435 */ EXTERN int Tcl_GetMathFuncInfo(Tcl_Interp *interp, const char *name, int *numArgsPtr, Tcl_ValueType **argTypesPtr, Tcl_MathProc **procPtr, ClientData *clientDataPtr); /* 436 */ EXTERN Tcl_Obj * Tcl_ListMathFuncs(Tcl_Interp *interp, const char *pattern); /* 437 */ EXTERN Tcl_Obj * Tcl_SubstObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 438 */ EXTERN int Tcl_DetachChannel(Tcl_Interp *interp, Tcl_Channel channel); /* 439 */ EXTERN int Tcl_IsStandardChannel(Tcl_Channel channel); /* 440 */ EXTERN int Tcl_FSCopyFile(Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr); /* 441 */ EXTERN int Tcl_FSCopyDirectory(Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr, Tcl_Obj **errorPtr); /* 442 */ EXTERN int Tcl_FSCreateDirectory(Tcl_Obj *pathPtr); /* 443 */ EXTERN int Tcl_FSDeleteFile(Tcl_Obj *pathPtr); /* 444 */ EXTERN int Tcl_FSLoadFile(Tcl_Interp *interp, Tcl_Obj *pathPtr, const char *sym1, const char *sym2, Tcl_PackageInitProc **proc1Ptr, Tcl_PackageInitProc **proc2Ptr, Tcl_LoadHandle *handlePtr, Tcl_FSUnloadFileProc **unloadProcPtr); /* 445 */ EXTERN int Tcl_FSMatchInDirectory(Tcl_Interp *interp, Tcl_Obj *result, Tcl_Obj *pathPtr, const char *pattern, Tcl_GlobTypeData *types); /* 446 */ EXTERN Tcl_Obj * Tcl_FSLink(Tcl_Obj *pathPtr, Tcl_Obj *toPtr, int linkAction); /* 447 */ EXTERN int Tcl_FSRemoveDirectory(Tcl_Obj *pathPtr, int recursive, Tcl_Obj **errorPtr); /* 448 */ EXTERN int Tcl_FSRenameFile(Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr); /* 449 */ EXTERN int Tcl_FSLstat(Tcl_Obj *pathPtr, Tcl_StatBuf *buf); /* 450 */ EXTERN int Tcl_FSUtime(Tcl_Obj *pathPtr, struct utimbuf *tval); /* 451 */ EXTERN int Tcl_FSFileAttrsGet(Tcl_Interp *interp, int index, Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef); /* 452 */ EXTERN int Tcl_FSFileAttrsSet(Tcl_Interp *interp, int index, Tcl_Obj *pathPtr, Tcl_Obj *objPtr); /* 453 */ EXTERN const char *CONST86 * Tcl_FSFileAttrStrings(Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef); /* 454 */ EXTERN int Tcl_FSStat(Tcl_Obj *pathPtr, Tcl_StatBuf *buf); /* 455 */ EXTERN int Tcl_FSAccess(Tcl_Obj *pathPtr, int mode); /* 456 */ EXTERN Tcl_Channel Tcl_FSOpenFileChannel(Tcl_Interp *interp, Tcl_Obj *pathPtr, const char *modeString, int permissions); /* 457 */ EXTERN Tcl_Obj * Tcl_FSGetCwd(Tcl_Interp *interp); /* 458 */ EXTERN int Tcl_FSChdir(Tcl_Obj *pathPtr); /* 459 */ EXTERN int Tcl_FSConvertToPathType(Tcl_Interp *interp, Tcl_Obj *pathPtr); /* 460 */ EXTERN Tcl_Obj * Tcl_FSJoinPath(Tcl_Obj *listObj, int elements); /* 461 */ EXTERN Tcl_Obj * Tcl_FSSplitPath(Tcl_Obj *pathPtr, int *lenPtr); /* 462 */ EXTERN int Tcl_FSEqualPaths(Tcl_Obj *firstPtr, Tcl_Obj *secondPtr); /* 463 */ EXTERN Tcl_Obj * Tcl_FSGetNormalizedPath(Tcl_Interp *interp, Tcl_Obj *pathPtr); /* 464 */ EXTERN Tcl_Obj * Tcl_FSJoinToPath(Tcl_Obj *pathPtr, int objc, Tcl_Obj *const objv[]); /* 465 */ EXTERN ClientData Tcl_FSGetInternalRep(Tcl_Obj *pathPtr, const Tcl_Filesystem *fsPtr); /* 466 */ EXTERN Tcl_Obj * Tcl_FSGetTranslatedPath(Tcl_Interp *interp, Tcl_Obj *pathPtr); /* 467 */ EXTERN int Tcl_FSEvalFile(Tcl_Interp *interp, Tcl_Obj *fileName); /* 468 */ EXTERN Tcl_Obj * Tcl_FSNewNativePath( const Tcl_Filesystem *fromFilesystem, ClientData clientData); /* 469 */ EXTERN const void * Tcl_FSGetNativePath(Tcl_Obj *pathPtr); /* 470 */ EXTERN Tcl_Obj * Tcl_FSFileSystemInfo(Tcl_Obj *pathPtr); /* 471 */ EXTERN Tcl_Obj * Tcl_FSPathSeparator(Tcl_Obj *pathPtr); /* 472 */ EXTERN Tcl_Obj * Tcl_FSListVolumes(void); /* 473 */ EXTERN int Tcl_FSRegister(ClientData clientData, const Tcl_Filesystem *fsPtr); /* 474 */ EXTERN int Tcl_FSUnregister(const Tcl_Filesystem *fsPtr); /* 475 */ EXTERN ClientData Tcl_FSData(const Tcl_Filesystem *fsPtr); /* 476 */ EXTERN const char * Tcl_FSGetTranslatedStringPath(Tcl_Interp *interp, Tcl_Obj *pathPtr); /* 477 */ EXTERN CONST86 Tcl_Filesystem * Tcl_FSGetFileSystemForPath(Tcl_Obj *pathPtr); /* 478 */ EXTERN Tcl_PathType Tcl_FSGetPathType(Tcl_Obj *pathPtr); /* 479 */ EXTERN int Tcl_OutputBuffered(Tcl_Channel chan); /* 480 */ EXTERN void Tcl_FSMountsChanged(const Tcl_Filesystem *fsPtr); /* 481 */ EXTERN int Tcl_EvalTokensStandard(Tcl_Interp *interp, Tcl_Token *tokenPtr, int count); /* 482 */ EXTERN void Tcl_GetTime(Tcl_Time *timeBuf); /* 483 */ EXTERN Tcl_Trace Tcl_CreateObjTrace(Tcl_Interp *interp, int level, int flags, Tcl_CmdObjTraceProc *objProc, ClientData clientData, Tcl_CmdObjTraceDeleteProc *delProc); /* 484 */ EXTERN int Tcl_GetCommandInfoFromToken(Tcl_Command token, Tcl_CmdInfo *infoPtr); /* 485 */ EXTERN int Tcl_SetCommandInfoFromToken(Tcl_Command token, const Tcl_CmdInfo *infoPtr); /* 486 */ EXTERN Tcl_Obj * Tcl_DbNewWideIntObj(Tcl_WideInt wideValue, const char *file, int line); /* 487 */ EXTERN int Tcl_GetWideIntFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_WideInt *widePtr); /* 488 */ EXTERN Tcl_Obj * Tcl_NewWideIntObj(Tcl_WideInt wideValue); /* 489 */ EXTERN void Tcl_SetWideIntObj(Tcl_Obj *objPtr, Tcl_WideInt wideValue); /* 490 */ EXTERN Tcl_StatBuf * Tcl_AllocStatBuf(void); /* 491 */ EXTERN Tcl_WideInt Tcl_Seek(Tcl_Channel chan, Tcl_WideInt offset, int mode); /* 492 */ EXTERN Tcl_WideInt Tcl_Tell(Tcl_Channel chan); /* 493 */ EXTERN Tcl_DriverWideSeekProc * Tcl_ChannelWideSeekProc( const Tcl_ChannelType *chanTypePtr); /* 494 */ EXTERN int Tcl_DictObjPut(Tcl_Interp *interp, Tcl_Obj *dictPtr, Tcl_Obj *keyPtr, Tcl_Obj *valuePtr); /* 495 */ EXTERN int Tcl_DictObjGet(Tcl_Interp *interp, Tcl_Obj *dictPtr, Tcl_Obj *keyPtr, Tcl_Obj **valuePtrPtr); /* 496 */ EXTERN int Tcl_DictObjRemove(Tcl_Interp *interp, Tcl_Obj *dictPtr, Tcl_Obj *keyPtr); /* 497 */ EXTERN int Tcl_DictObjSize(Tcl_Interp *interp, Tcl_Obj *dictPtr, int *sizePtr); /* 498 */ EXTERN int Tcl_DictObjFirst(Tcl_Interp *interp, Tcl_Obj *dictPtr, Tcl_DictSearch *searchPtr, Tcl_Obj **keyPtrPtr, Tcl_Obj **valuePtrPtr, int *donePtr); /* 499 */ EXTERN void Tcl_DictObjNext(Tcl_DictSearch *searchPtr, Tcl_Obj **keyPtrPtr, Tcl_Obj **valuePtrPtr, int *donePtr); /* 500 */ EXTERN void Tcl_DictObjDone(Tcl_DictSearch *searchPtr); /* 501 */ EXTERN int Tcl_DictObjPutKeyList(Tcl_Interp *interp, Tcl_Obj *dictPtr, int keyc, Tcl_Obj *const *keyv, Tcl_Obj *valuePtr); /* 502 */ EXTERN int Tcl_DictObjRemoveKeyList(Tcl_Interp *interp, Tcl_Obj *dictPtr, int keyc, Tcl_Obj *const *keyv); /* 503 */ EXTERN Tcl_Obj * Tcl_NewDictObj(void); /* 504 */ EXTERN Tcl_Obj * Tcl_DbNewDictObj(const char *file, int line); /* 505 */ EXTERN void Tcl_RegisterConfig(Tcl_Interp *interp, const char *pkgName, const Tcl_Config *configuration, const char *valEncoding); /* 506 */ EXTERN Tcl_Namespace * Tcl_CreateNamespace(Tcl_Interp *interp, const char *name, ClientData clientData, Tcl_NamespaceDeleteProc *deleteProc); /* 507 */ EXTERN void Tcl_DeleteNamespace(Tcl_Namespace *nsPtr); /* 508 */ EXTERN int Tcl_AppendExportList(Tcl_Interp *interp, Tcl_Namespace *nsPtr, Tcl_Obj *objPtr); /* 509 */ EXTERN int Tcl_Export(Tcl_Interp *interp, Tcl_Namespace *nsPtr, const char *pattern, int resetListFirst); /* 510 */ EXTERN int Tcl_Import(Tcl_Interp *interp, Tcl_Namespace *nsPtr, const char *pattern, int allowOverwrite); /* 511 */ EXTERN int Tcl_ForgetImport(Tcl_Interp *interp, Tcl_Namespace *nsPtr, const char *pattern); /* 512 */ EXTERN Tcl_Namespace * Tcl_GetCurrentNamespace(Tcl_Interp *interp); /* 513 */ EXTERN Tcl_Namespace * Tcl_GetGlobalNamespace(Tcl_Interp *interp); /* 514 */ EXTERN Tcl_Namespace * Tcl_FindNamespace(Tcl_Interp *interp, const char *name, Tcl_Namespace *contextNsPtr, int flags); /* 515 */ EXTERN Tcl_Command Tcl_FindCommand(Tcl_Interp *interp, const char *name, Tcl_Namespace *contextNsPtr, int flags); /* 516 */ EXTERN Tcl_Command Tcl_GetCommandFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr); /* 517 */ EXTERN void Tcl_GetCommandFullName(Tcl_Interp *interp, Tcl_Command command, Tcl_Obj *objPtr); /* 518 */ EXTERN int Tcl_FSEvalFileEx(Tcl_Interp *interp, Tcl_Obj *fileName, const char *encodingName); /* 519 */ EXTERN Tcl_ExitProc * Tcl_SetExitProc(Tcl_ExitProc *proc); /* 520 */ EXTERN void Tcl_LimitAddHandler(Tcl_Interp *interp, int type, Tcl_LimitHandlerProc *handlerProc, ClientData clientData, Tcl_LimitHandlerDeleteProc *deleteProc); /* 521 */ EXTERN void Tcl_LimitRemoveHandler(Tcl_Interp *interp, int type, Tcl_LimitHandlerProc *handlerProc, ClientData clientData); /* 522 */ EXTERN int Tcl_LimitReady(Tcl_Interp *interp); /* 523 */ EXTERN int Tcl_LimitCheck(Tcl_Interp *interp); /* 524 */ EXTERN int Tcl_LimitExceeded(Tcl_Interp *interp); /* 525 */ EXTERN void Tcl_LimitSetCommands(Tcl_Interp *interp, int commandLimit); /* 526 */ EXTERN void Tcl_LimitSetTime(Tcl_Interp *interp, Tcl_Time *timeLimitPtr); /* 527 */ EXTERN void Tcl_LimitSetGranularity(Tcl_Interp *interp, int type, int granularity); /* 528 */ EXTERN int Tcl_LimitTypeEnabled(Tcl_Interp *interp, int type); /* 529 */ EXTERN int Tcl_LimitTypeExceeded(Tcl_Interp *interp, int type); /* 530 */ EXTERN void Tcl_LimitTypeSet(Tcl_Interp *interp, int type); /* 531 */ EXTERN void Tcl_LimitTypeReset(Tcl_Interp *interp, int type); /* 532 */ EXTERN int Tcl_LimitGetCommands(Tcl_Interp *interp); /* 533 */ EXTERN void Tcl_LimitGetTime(Tcl_Interp *interp, Tcl_Time *timeLimitPtr); /* 534 */ EXTERN int Tcl_LimitGetGranularity(Tcl_Interp *interp, int type); /* 535 */ EXTERN Tcl_InterpState Tcl_SaveInterpState(Tcl_Interp *interp, int status); /* 536 */ EXTERN int Tcl_RestoreInterpState(Tcl_Interp *interp, Tcl_InterpState state); /* 537 */ EXTERN void Tcl_DiscardInterpState(Tcl_InterpState state); /* 538 */ EXTERN int Tcl_SetReturnOptions(Tcl_Interp *interp, Tcl_Obj *options); /* 539 */ EXTERN Tcl_Obj * Tcl_GetReturnOptions(Tcl_Interp *interp, int result); /* 540 */ EXTERN int Tcl_IsEnsemble(Tcl_Command token); /* 541 */ EXTERN Tcl_Command Tcl_CreateEnsemble(Tcl_Interp *interp, const char *name, Tcl_Namespace *namespacePtr, int flags); /* 542 */ EXTERN Tcl_Command Tcl_FindEnsemble(Tcl_Interp *interp, Tcl_Obj *cmdNameObj, int flags); /* 543 */ EXTERN int Tcl_SetEnsembleSubcommandList(Tcl_Interp *interp, Tcl_Command token, Tcl_Obj *subcmdList); /* 544 */ EXTERN int Tcl_SetEnsembleMappingDict(Tcl_Interp *interp, Tcl_Command token, Tcl_Obj *mapDict); /* 545 */ EXTERN int Tcl_SetEnsembleUnknownHandler(Tcl_Interp *interp, Tcl_Command token, Tcl_Obj *unknownList); /* 546 */ EXTERN int Tcl_SetEnsembleFlags(Tcl_Interp *interp, Tcl_Command token, int flags); /* 547 */ EXTERN int Tcl_GetEnsembleSubcommandList(Tcl_Interp *interp, Tcl_Command token, Tcl_Obj **subcmdListPtr); /* 548 */ EXTERN int Tcl_GetEnsembleMappingDict(Tcl_Interp *interp, Tcl_Command token, Tcl_Obj **mapDictPtr); /* 549 */ EXTERN int Tcl_GetEnsembleUnknownHandler(Tcl_Interp *interp, Tcl_Command token, Tcl_Obj **unknownListPtr); /* 550 */ EXTERN int Tcl_GetEnsembleFlags(Tcl_Interp *interp, Tcl_Command token, int *flagsPtr); /* 551 */ EXTERN int Tcl_GetEnsembleNamespace(Tcl_Interp *interp, Tcl_Command token, Tcl_Namespace **namespacePtrPtr); /* 552 */ EXTERN void Tcl_SetTimeProc(Tcl_GetTimeProc *getProc, Tcl_ScaleTimeProc *scaleProc, ClientData clientData); /* 553 */ EXTERN void Tcl_QueryTimeProc(Tcl_GetTimeProc **getProc, Tcl_ScaleTimeProc **scaleProc, ClientData *clientData); /* 554 */ EXTERN Tcl_DriverThreadActionProc * Tcl_ChannelThreadActionProc( const Tcl_ChannelType *chanTypePtr); /* 555 */ EXTERN Tcl_Obj * Tcl_NewBignumObj(mp_int *value); /* 556 */ EXTERN Tcl_Obj * Tcl_DbNewBignumObj(mp_int *value, const char *file, int line); /* 557 */ EXTERN void Tcl_SetBignumObj(Tcl_Obj *obj, mp_int *value); /* 558 */ EXTERN int Tcl_GetBignumFromObj(Tcl_Interp *interp, Tcl_Obj *obj, mp_int *value); /* 559 */ EXTERN int Tcl_TakeBignumFromObj(Tcl_Interp *interp, Tcl_Obj *obj, mp_int *value); /* 560 */ EXTERN int Tcl_TruncateChannel(Tcl_Channel chan, Tcl_WideInt length); /* 561 */ EXTERN Tcl_DriverTruncateProc * Tcl_ChannelTruncateProc( const Tcl_ChannelType *chanTypePtr); /* 562 */ EXTERN void Tcl_SetChannelErrorInterp(Tcl_Interp *interp, Tcl_Obj *msg); /* 563 */ EXTERN void Tcl_GetChannelErrorInterp(Tcl_Interp *interp, Tcl_Obj **msg); /* 564 */ EXTERN void Tcl_SetChannelError(Tcl_Channel chan, Tcl_Obj *msg); /* 565 */ EXTERN void Tcl_GetChannelError(Tcl_Channel chan, Tcl_Obj **msg); /* 566 */ EXTERN int Tcl_InitBignumFromDouble(Tcl_Interp *interp, double initval, mp_int *toInit); /* 567 */ EXTERN Tcl_Obj * Tcl_GetNamespaceUnknownHandler(Tcl_Interp *interp, Tcl_Namespace *nsPtr); /* 568 */ EXTERN int Tcl_SetNamespaceUnknownHandler(Tcl_Interp *interp, Tcl_Namespace *nsPtr, Tcl_Obj *handlerPtr); /* 569 */ EXTERN int Tcl_GetEncodingFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_Encoding *encodingPtr); /* 570 */ EXTERN Tcl_Obj * Tcl_GetEncodingSearchPath(void); /* 571 */ EXTERN int Tcl_SetEncodingSearchPath(Tcl_Obj *searchPath); /* 572 */ EXTERN const char * Tcl_GetEncodingNameFromEnvironment( Tcl_DString *bufPtr); /* 573 */ EXTERN int Tcl_PkgRequireProc(Tcl_Interp *interp, const char *name, int objc, Tcl_Obj *const objv[], void *clientDataPtr); /* 574 */ EXTERN void Tcl_AppendObjToErrorInfo(Tcl_Interp *interp, Tcl_Obj *objPtr); /* 575 */ EXTERN void Tcl_AppendLimitedToObj(Tcl_Obj *objPtr, const char *bytes, int length, int limit, const char *ellipsis); /* 576 */ EXTERN Tcl_Obj * Tcl_Format(Tcl_Interp *interp, const char *format, int objc, Tcl_Obj *const objv[]); /* 577 */ EXTERN int Tcl_AppendFormatToObj(Tcl_Interp *interp, Tcl_Obj *objPtr, const char *format, int objc, Tcl_Obj *const objv[]); /* 578 */ EXTERN Tcl_Obj * Tcl_ObjPrintf(const char *format, ...) TCL_FORMAT_PRINTF(1, 2); /* 579 */ EXTERN void Tcl_AppendPrintfToObj(Tcl_Obj *objPtr, const char *format, ...) TCL_FORMAT_PRINTF(2, 3); /* 580 */ EXTERN int Tcl_CancelEval(Tcl_Interp *interp, Tcl_Obj *resultObjPtr, ClientData clientData, int flags); /* 581 */ EXTERN int Tcl_Canceled(Tcl_Interp *interp, int flags); /* 582 */ EXTERN int Tcl_CreatePipe(Tcl_Interp *interp, Tcl_Channel *rchan, Tcl_Channel *wchan, int flags); /* 583 */ EXTERN Tcl_Command Tcl_NRCreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_ObjCmdProc *proc, Tcl_ObjCmdProc *nreProc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); /* 584 */ EXTERN int Tcl_NREvalObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 585 */ EXTERN int Tcl_NREvalObjv(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], int flags); /* 586 */ EXTERN int Tcl_NRCmdSwap(Tcl_Interp *interp, Tcl_Command cmd, int objc, Tcl_Obj *const objv[], int flags); /* 587 */ EXTERN void Tcl_NRAddCallback(Tcl_Interp *interp, Tcl_NRPostProc *postProcPtr, ClientData data0, ClientData data1, ClientData data2, ClientData data3); /* 588 */ EXTERN int Tcl_NRCallObjProc(Tcl_Interp *interp, Tcl_ObjCmdProc *objProc, ClientData clientData, int objc, Tcl_Obj *const objv[]); /* 589 */ EXTERN unsigned Tcl_GetFSDeviceFromStat(const Tcl_StatBuf *statPtr); /* 590 */ EXTERN unsigned Tcl_GetFSInodeFromStat(const Tcl_StatBuf *statPtr); /* 591 */ EXTERN unsigned Tcl_GetModeFromStat(const Tcl_StatBuf *statPtr); /* 592 */ EXTERN int Tcl_GetLinkCountFromStat(const Tcl_StatBuf *statPtr); /* 593 */ EXTERN int Tcl_GetUserIdFromStat(const Tcl_StatBuf *statPtr); /* 594 */ EXTERN int Tcl_GetGroupIdFromStat(const Tcl_StatBuf *statPtr); /* 595 */ EXTERN int Tcl_GetDeviceTypeFromStat(const Tcl_StatBuf *statPtr); /* 596 */ EXTERN Tcl_WideInt Tcl_GetAccessTimeFromStat(const Tcl_StatBuf *statPtr); /* 597 */ EXTERN Tcl_WideInt Tcl_GetModificationTimeFromStat( const Tcl_StatBuf *statPtr); /* 598 */ EXTERN Tcl_WideInt Tcl_GetChangeTimeFromStat(const Tcl_StatBuf *statPtr); /* 599 */ EXTERN Tcl_WideUInt Tcl_GetSizeFromStat(const Tcl_StatBuf *statPtr); /* 600 */ EXTERN Tcl_WideUInt Tcl_GetBlocksFromStat(const Tcl_StatBuf *statPtr); /* 601 */ EXTERN unsigned Tcl_GetBlockSizeFromStat(const Tcl_StatBuf *statPtr); /* 602 */ EXTERN int Tcl_SetEnsembleParameterList(Tcl_Interp *interp, Tcl_Command token, Tcl_Obj *paramList); /* 603 */ EXTERN int Tcl_GetEnsembleParameterList(Tcl_Interp *interp, Tcl_Command token, Tcl_Obj **paramListPtr); /* 604 */ EXTERN int Tcl_ParseArgsObjv(Tcl_Interp *interp, const Tcl_ArgvInfo *argTable, int *objcPtr, Tcl_Obj *const *objv, Tcl_Obj ***remObjv); /* 605 */ EXTERN int Tcl_GetErrorLine(Tcl_Interp *interp); /* 606 */ EXTERN void Tcl_SetErrorLine(Tcl_Interp *interp, int lineNum); /* 607 */ EXTERN void Tcl_TransferResult(Tcl_Interp *sourceInterp, int result, Tcl_Interp *targetInterp); /* 608 */ EXTERN int Tcl_InterpActive(Tcl_Interp *interp); /* 609 */ EXTERN void Tcl_BackgroundException(Tcl_Interp *interp, int code); /* 610 */ EXTERN int Tcl_ZlibDeflate(Tcl_Interp *interp, int format, Tcl_Obj *data, int level, Tcl_Obj *gzipHeaderDictObj); /* 611 */ EXTERN int Tcl_ZlibInflate(Tcl_Interp *interp, int format, Tcl_Obj *data, int buffersize, Tcl_Obj *gzipHeaderDictObj); /* 612 */ EXTERN unsigned int Tcl_ZlibCRC32(unsigned int crc, const unsigned char *buf, int len); /* 613 */ EXTERN unsigned int Tcl_ZlibAdler32(unsigned int adler, const unsigned char *buf, int len); /* 614 */ EXTERN int Tcl_ZlibStreamInit(Tcl_Interp *interp, int mode, int format, int level, Tcl_Obj *dictObj, Tcl_ZlibStream *zshandle); /* 615 */ EXTERN Tcl_Obj * Tcl_ZlibStreamGetCommandName(Tcl_ZlibStream zshandle); /* 616 */ EXTERN int Tcl_ZlibStreamEof(Tcl_ZlibStream zshandle); /* 617 */ EXTERN int Tcl_ZlibStreamChecksum(Tcl_ZlibStream zshandle); /* 618 */ EXTERN int Tcl_ZlibStreamPut(Tcl_ZlibStream zshandle, Tcl_Obj *data, int flush); /* 619 */ EXTERN int Tcl_ZlibStreamGet(Tcl_ZlibStream zshandle, Tcl_Obj *data, int count); /* 620 */ EXTERN int Tcl_ZlibStreamClose(Tcl_ZlibStream zshandle); /* 621 */ EXTERN int Tcl_ZlibStreamReset(Tcl_ZlibStream zshandle); /* 622 */ EXTERN void Tcl_SetStartupScript(Tcl_Obj *path, const char *encoding); /* 623 */ EXTERN Tcl_Obj * Tcl_GetStartupScript(const char **encodingPtr); /* 624 */ EXTERN int Tcl_CloseEx(Tcl_Interp *interp, Tcl_Channel chan, int flags); /* 625 */ EXTERN int Tcl_NRExprObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_Obj *resultPtr); /* 626 */ EXTERN int Tcl_NRSubstObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 627 */ EXTERN int Tcl_LoadFile(Tcl_Interp *interp, Tcl_Obj *pathPtr, const char *const symv[], int flags, void *procPtrs, Tcl_LoadHandle *handlePtr); /* 628 */ EXTERN void * Tcl_FindSymbol(Tcl_Interp *interp, Tcl_LoadHandle handle, const char *symbol); /* 629 */ EXTERN int Tcl_FSUnloadFile(Tcl_Interp *interp, Tcl_LoadHandle handlePtr); /* 630 */ EXTERN void Tcl_ZlibStreamSetCompressionDictionary( Tcl_ZlibStream zhandle, Tcl_Obj *compressionDictionaryObj); typedef struct { const struct TclPlatStubs *tclPlatStubs; const struct TclIntStubs *tclIntStubs; const struct TclIntPlatStubs *tclIntPlatStubs; } TclStubHooks; typedef struct TclStubs { int magic; const TclStubHooks *hooks; int (*tcl_PkgProvideEx) (Tcl_Interp *interp, const char *name, const char *version, const void *clientData); /* 0 */ CONST84_RETURN char * (*tcl_PkgRequireEx) (Tcl_Interp *interp, const char *name, const char *version, int exact, void *clientDataPtr); /* 1 */ void (*tcl_Panic) (const char *format, ...) TCL_FORMAT_PRINTF(1, 2); /* 2 */ char * (*tcl_Alloc) (unsigned int size); /* 3 */ void (*tcl_Free) (char *ptr); /* 4 */ char * (*tcl_Realloc) (char *ptr, unsigned int size); /* 5 */ char * (*tcl_DbCkalloc) (unsigned int size, const char *file, int line); /* 6 */ void (*tcl_DbCkfree) (char *ptr, const char *file, int line); /* 7 */ char * (*tcl_DbCkrealloc) (char *ptr, unsigned int size, const char *file, int line); /* 8 */ #if !defined(__WIN32__) && !defined(MAC_OSX_TCL) /* UNIX */ void (*tcl_CreateFileHandler) (int fd, int mask, Tcl_FileProc *proc, ClientData clientData); /* 9 */ #endif /* UNIX */ #if defined(__WIN32__) /* WIN */ void (*reserved9)(void); #endif /* WIN */ #ifdef MAC_OSX_TCL /* MACOSX */ void (*tcl_CreateFileHandler) (int fd, int mask, Tcl_FileProc *proc, ClientData clientData); /* 9 */ #endif /* MACOSX */ #if !defined(__WIN32__) && !defined(MAC_OSX_TCL) /* UNIX */ void (*tcl_DeleteFileHandler) (int fd); /* 10 */ #endif /* UNIX */ #if defined(__WIN32__) /* WIN */ void (*reserved10)(void); #endif /* WIN */ #ifdef MAC_OSX_TCL /* MACOSX */ void (*tcl_DeleteFileHandler) (int fd); /* 10 */ #endif /* MACOSX */ void (*tcl_SetTimer) (const Tcl_Time *timePtr); /* 11 */ void (*tcl_Sleep) (int ms); /* 12 */ int (*tcl_WaitForEvent) (const Tcl_Time *timePtr); /* 13 */ int (*tcl_AppendAllObjTypes) (Tcl_Interp *interp, Tcl_Obj *objPtr); /* 14 */ void (*tcl_AppendStringsToObj) (Tcl_Obj *objPtr, ...); /* 15 */ void (*tcl_AppendToObj) (Tcl_Obj *objPtr, const char *bytes, int length); /* 16 */ Tcl_Obj * (*tcl_ConcatObj) (int objc, Tcl_Obj *const objv[]); /* 17 */ int (*tcl_ConvertToType) (Tcl_Interp *interp, Tcl_Obj *objPtr, const Tcl_ObjType *typePtr); /* 18 */ void (*tcl_DbDecrRefCount) (Tcl_Obj *objPtr, const char *file, int line); /* 19 */ void (*tcl_DbIncrRefCount) (Tcl_Obj *objPtr, const char *file, int line); /* 20 */ int (*tcl_DbIsShared) (Tcl_Obj *objPtr, const char *file, int line); /* 21 */ Tcl_Obj * (*tcl_DbNewBooleanObj) (int boolValue, const char *file, int line); /* 22 */ Tcl_Obj * (*tcl_DbNewByteArrayObj) (const unsigned char *bytes, int length, const char *file, int line); /* 23 */ Tcl_Obj * (*tcl_DbNewDoubleObj) (double doubleValue, const char *file, int line); /* 24 */ Tcl_Obj * (*tcl_DbNewListObj) (int objc, Tcl_Obj *const *objv, const char *file, int line); /* 25 */ Tcl_Obj * (*tcl_DbNewLongObj) (long longValue, const char *file, int line); /* 26 */ Tcl_Obj * (*tcl_DbNewObj) (const char *file, int line); /* 27 */ Tcl_Obj * (*tcl_DbNewStringObj) (const char *bytes, int length, const char *file, int line); /* 28 */ Tcl_Obj * (*tcl_DuplicateObj) (Tcl_Obj *objPtr); /* 29 */ void (*tclFreeObj) (Tcl_Obj *objPtr); /* 30 */ int (*tcl_GetBoolean) (Tcl_Interp *interp, const char *src, int *boolPtr); /* 31 */ int (*tcl_GetBooleanFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int *boolPtr); /* 32 */ unsigned char * (*tcl_GetByteArrayFromObj) (Tcl_Obj *objPtr, int *lengthPtr); /* 33 */ int (*tcl_GetDouble) (Tcl_Interp *interp, const char *src, double *doublePtr); /* 34 */ int (*tcl_GetDoubleFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, double *doublePtr); /* 35 */ int (*tcl_GetIndexFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, CONST84 char *const *tablePtr, const char *msg, int flags, int *indexPtr); /* 36 */ int (*tcl_GetInt) (Tcl_Interp *interp, const char *src, int *intPtr); /* 37 */ int (*tcl_GetIntFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int *intPtr); /* 38 */ int (*tcl_GetLongFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, long *longPtr); /* 39 */ CONST86 Tcl_ObjType * (*tcl_GetObjType) (const char *typeName); /* 40 */ char * (*tcl_GetStringFromObj) (Tcl_Obj *objPtr, int *lengthPtr); /* 41 */ void (*tcl_InvalidateStringRep) (Tcl_Obj *objPtr); /* 42 */ int (*tcl_ListObjAppendList) (Tcl_Interp *interp, Tcl_Obj *listPtr, Tcl_Obj *elemListPtr); /* 43 */ int (*tcl_ListObjAppendElement) (Tcl_Interp *interp, Tcl_Obj *listPtr, Tcl_Obj *objPtr); /* 44 */ int (*tcl_ListObjGetElements) (Tcl_Interp *interp, Tcl_Obj *listPtr, int *objcPtr, Tcl_Obj ***objvPtr); /* 45 */ int (*tcl_ListObjIndex) (Tcl_Interp *interp, Tcl_Obj *listPtr, int index, Tcl_Obj **objPtrPtr); /* 46 */ int (*tcl_ListObjLength) (Tcl_Interp *interp, Tcl_Obj *listPtr, int *lengthPtr); /* 47 */ int (*tcl_ListObjReplace) (Tcl_Interp *interp, Tcl_Obj *listPtr, int first, int count, int objc, Tcl_Obj *const objv[]); /* 48 */ Tcl_Obj * (*tcl_NewBooleanObj) (int boolValue); /* 49 */ Tcl_Obj * (*tcl_NewByteArrayObj) (const unsigned char *bytes, int length); /* 50 */ Tcl_Obj * (*tcl_NewDoubleObj) (double doubleValue); /* 51 */ Tcl_Obj * (*tcl_NewIntObj) (int intValue); /* 52 */ Tcl_Obj * (*tcl_NewListObj) (int objc, Tcl_Obj *const objv[]); /* 53 */ Tcl_Obj * (*tcl_NewLongObj) (long longValue); /* 54 */ Tcl_Obj * (*tcl_NewObj) (void); /* 55 */ Tcl_Obj * (*tcl_NewStringObj) (const char *bytes, int length); /* 56 */ void (*tcl_SetBooleanObj) (Tcl_Obj *objPtr, int boolValue); /* 57 */ unsigned char * (*tcl_SetByteArrayLength) (Tcl_Obj *objPtr, int length); /* 58 */ void (*tcl_SetByteArrayObj) (Tcl_Obj *objPtr, const unsigned char *bytes, int length); /* 59 */ void (*tcl_SetDoubleObj) (Tcl_Obj *objPtr, double doubleValue); /* 60 */ void (*tcl_SetIntObj) (Tcl_Obj *objPtr, int intValue); /* 61 */ void (*tcl_SetListObj) (Tcl_Obj *objPtr, int objc, Tcl_Obj *const objv[]); /* 62 */ void (*tcl_SetLongObj) (Tcl_Obj *objPtr, long longValue); /* 63 */ void (*tcl_SetObjLength) (Tcl_Obj *objPtr, int length); /* 64 */ void (*tcl_SetStringObj) (Tcl_Obj *objPtr, const char *bytes, int length); /* 65 */ void (*tcl_AddErrorInfo) (Tcl_Interp *interp, const char *message); /* 66 */ void (*tcl_AddObjErrorInfo) (Tcl_Interp *interp, const char *message, int length); /* 67 */ void (*tcl_AllowExceptions) (Tcl_Interp *interp); /* 68 */ void (*tcl_AppendElement) (Tcl_Interp *interp, const char *element); /* 69 */ void (*tcl_AppendResult) (Tcl_Interp *interp, ...); /* 70 */ Tcl_AsyncHandler (*tcl_AsyncCreate) (Tcl_AsyncProc *proc, ClientData clientData); /* 71 */ void (*tcl_AsyncDelete) (Tcl_AsyncHandler async); /* 72 */ int (*tcl_AsyncInvoke) (Tcl_Interp *interp, int code); /* 73 */ void (*tcl_AsyncMark) (Tcl_AsyncHandler async); /* 74 */ int (*tcl_AsyncReady) (void); /* 75 */ void (*tcl_BackgroundError) (Tcl_Interp *interp); /* 76 */ char (*tcl_Backslash) (const char *src, int *readPtr); /* 77 */ int (*tcl_BadChannelOption) (Tcl_Interp *interp, const char *optionName, const char *optionList); /* 78 */ void (*tcl_CallWhenDeleted) (Tcl_Interp *interp, Tcl_InterpDeleteProc *proc, ClientData clientData); /* 79 */ void (*tcl_CancelIdleCall) (Tcl_IdleProc *idleProc, ClientData clientData); /* 80 */ int (*tcl_Close) (Tcl_Interp *interp, Tcl_Channel chan); /* 81 */ int (*tcl_CommandComplete) (const char *cmd); /* 82 */ char * (*tcl_Concat) (int argc, CONST84 char *const *argv); /* 83 */ int (*tcl_ConvertElement) (const char *src, char *dst, int flags); /* 84 */ int (*tcl_ConvertCountedElement) (const char *src, int length, char *dst, int flags); /* 85 */ int (*tcl_CreateAlias) (Tcl_Interp *slave, const char *slaveCmd, Tcl_Interp *target, const char *targetCmd, int argc, CONST84 char *const *argv); /* 86 */ int (*tcl_CreateAliasObj) (Tcl_Interp *slave, const char *slaveCmd, Tcl_Interp *target, const char *targetCmd, int objc, Tcl_Obj *const objv[]); /* 87 */ Tcl_Channel (*tcl_CreateChannel) (const Tcl_ChannelType *typePtr, const char *chanName, ClientData instanceData, int mask); /* 88 */ void (*tcl_CreateChannelHandler) (Tcl_Channel chan, int mask, Tcl_ChannelProc *proc, ClientData clientData); /* 89 */ void (*tcl_CreateCloseHandler) (Tcl_Channel chan, Tcl_CloseProc *proc, ClientData clientData); /* 90 */ Tcl_Command (*tcl_CreateCommand) (Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); /* 91 */ void (*tcl_CreateEventSource) (Tcl_EventSetupProc *setupProc, Tcl_EventCheckProc *checkProc, ClientData clientData); /* 92 */ void (*tcl_CreateExitHandler) (Tcl_ExitProc *proc, ClientData clientData); /* 93 */ Tcl_Interp * (*tcl_CreateInterp) (void); /* 94 */ void (*tcl_CreateMathFunc) (Tcl_Interp *interp, const char *name, int numArgs, Tcl_ValueType *argTypes, Tcl_MathProc *proc, ClientData clientData); /* 95 */ Tcl_Command (*tcl_CreateObjCommand) (Tcl_Interp *interp, const char *cmdName, Tcl_ObjCmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); /* 96 */ Tcl_Interp * (*tcl_CreateSlave) (Tcl_Interp *interp, const char *slaveName, int isSafe); /* 97 */ Tcl_TimerToken (*tcl_CreateTimerHandler) (int milliseconds, Tcl_TimerProc *proc, ClientData clientData); /* 98 */ Tcl_Trace (*tcl_CreateTrace) (Tcl_Interp *interp, int level, Tcl_CmdTraceProc *proc, ClientData clientData); /* 99 */ void (*tcl_DeleteAssocData) (Tcl_Interp *interp, const char *name); /* 100 */ void (*tcl_DeleteChannelHandler) (Tcl_Channel chan, Tcl_ChannelProc *proc, ClientData clientData); /* 101 */ void (*tcl_DeleteCloseHandler) (Tcl_Channel chan, Tcl_CloseProc *proc, ClientData clientData); /* 102 */ int (*tcl_DeleteCommand) (Tcl_Interp *interp, const char *cmdName); /* 103 */ int (*tcl_DeleteCommandFromToken) (Tcl_Interp *interp, Tcl_Command command); /* 104 */ void (*tcl_DeleteEvents) (Tcl_EventDeleteProc *proc, ClientData clientData); /* 105 */ void (*tcl_DeleteEventSource) (Tcl_EventSetupProc *setupProc, Tcl_EventCheckProc *checkProc, ClientData clientData); /* 106 */ void (*tcl_DeleteExitHandler) (Tcl_ExitProc *proc, ClientData clientData); /* 107 */ void (*tcl_DeleteHashEntry) (Tcl_HashEntry *entryPtr); /* 108 */ void (*tcl_DeleteHashTable) (Tcl_HashTable *tablePtr); /* 109 */ void (*tcl_DeleteInterp) (Tcl_Interp *interp); /* 110 */ void (*tcl_DetachPids) (int numPids, Tcl_Pid *pidPtr); /* 111 */ void (*tcl_DeleteTimerHandler) (Tcl_TimerToken token); /* 112 */ void (*tcl_DeleteTrace) (Tcl_Interp *interp, Tcl_Trace trace); /* 113 */ void (*tcl_DontCallWhenDeleted) (Tcl_Interp *interp, Tcl_InterpDeleteProc *proc, ClientData clientData); /* 114 */ int (*tcl_DoOneEvent) (int flags); /* 115 */ void (*tcl_DoWhenIdle) (Tcl_IdleProc *proc, ClientData clientData); /* 116 */ char * (*tcl_DStringAppend) (Tcl_DString *dsPtr, const char *bytes, int length); /* 117 */ char * (*tcl_DStringAppendElement) (Tcl_DString *dsPtr, const char *element); /* 118 */ void (*tcl_DStringEndSublist) (Tcl_DString *dsPtr); /* 119 */ void (*tcl_DStringFree) (Tcl_DString *dsPtr); /* 120 */ void (*tcl_DStringGetResult) (Tcl_Interp *interp, Tcl_DString *dsPtr); /* 121 */ void (*tcl_DStringInit) (Tcl_DString *dsPtr); /* 122 */ void (*tcl_DStringResult) (Tcl_Interp *interp, Tcl_DString *dsPtr); /* 123 */ void (*tcl_DStringSetLength) (Tcl_DString *dsPtr, int length); /* 124 */ void (*tcl_DStringStartSublist) (Tcl_DString *dsPtr); /* 125 */ int (*tcl_Eof) (Tcl_Channel chan); /* 126 */ CONST84_RETURN char * (*tcl_ErrnoId) (void); /* 127 */ CONST84_RETURN char * (*tcl_ErrnoMsg) (int err); /* 128 */ int (*tcl_Eval) (Tcl_Interp *interp, const char *script); /* 129 */ int (*tcl_EvalFile) (Tcl_Interp *interp, const char *fileName); /* 130 */ int (*tcl_EvalObj) (Tcl_Interp *interp, Tcl_Obj *objPtr); /* 131 */ void (*tcl_EventuallyFree) (ClientData clientData, Tcl_FreeProc *freeProc); /* 132 */ void (*tcl_Exit) (int status); /* 133 */ int (*tcl_ExposeCommand) (Tcl_Interp *interp, const char *hiddenCmdToken, const char *cmdName); /* 134 */ int (*tcl_ExprBoolean) (Tcl_Interp *interp, const char *expr, int *ptr); /* 135 */ int (*tcl_ExprBooleanObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int *ptr); /* 136 */ int (*tcl_ExprDouble) (Tcl_Interp *interp, const char *expr, double *ptr); /* 137 */ int (*tcl_ExprDoubleObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, double *ptr); /* 138 */ int (*tcl_ExprLong) (Tcl_Interp *interp, const char *expr, long *ptr); /* 139 */ int (*tcl_ExprLongObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, long *ptr); /* 140 */ int (*tcl_ExprObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_Obj **resultPtrPtr); /* 141 */ int (*tcl_ExprString) (Tcl_Interp *interp, const char *expr); /* 142 */ void (*tcl_Finalize) (void); /* 143 */ void (*tcl_FindExecutable) (const char *argv0); /* 144 */ Tcl_HashEntry * (*tcl_FirstHashEntry) (Tcl_HashTable *tablePtr, Tcl_HashSearch *searchPtr); /* 145 */ int (*tcl_Flush) (Tcl_Channel chan); /* 146 */ void (*tcl_FreeResult) (Tcl_Interp *interp); /* 147 */ int (*tcl_GetAlias) (Tcl_Interp *interp, const char *slaveCmd, Tcl_Interp **targetInterpPtr, CONST84 char **targetCmdPtr, int *argcPtr, CONST84 char ***argvPtr); /* 148 */ int (*tcl_GetAliasObj) (Tcl_Interp *interp, const char *slaveCmd, Tcl_Interp **targetInterpPtr, CONST84 char **targetCmdPtr, int *objcPtr, Tcl_Obj ***objv); /* 149 */ ClientData (*tcl_GetAssocData) (Tcl_Interp *interp, const char *name, Tcl_InterpDeleteProc **procPtr); /* 150 */ Tcl_Channel (*tcl_GetChannel) (Tcl_Interp *interp, const char *chanName, int *modePtr); /* 151 */ int (*tcl_GetChannelBufferSize) (Tcl_Channel chan); /* 152 */ int (*tcl_GetChannelHandle) (Tcl_Channel chan, int direction, ClientData *handlePtr); /* 153 */ ClientData (*tcl_GetChannelInstanceData) (Tcl_Channel chan); /* 154 */ int (*tcl_GetChannelMode) (Tcl_Channel chan); /* 155 */ CONST84_RETURN char * (*tcl_GetChannelName) (Tcl_Channel chan); /* 156 */ int (*tcl_GetChannelOption) (Tcl_Interp *interp, Tcl_Channel chan, const char *optionName, Tcl_DString *dsPtr); /* 157 */ CONST86 Tcl_ChannelType * (*tcl_GetChannelType) (Tcl_Channel chan); /* 158 */ int (*tcl_GetCommandInfo) (Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr); /* 159 */ CONST84_RETURN char * (*tcl_GetCommandName) (Tcl_Interp *interp, Tcl_Command command); /* 160 */ int (*tcl_GetErrno) (void); /* 161 */ CONST84_RETURN char * (*tcl_GetHostName) (void); /* 162 */ int (*tcl_GetInterpPath) (Tcl_Interp *askInterp, Tcl_Interp *slaveInterp); /* 163 */ Tcl_Interp * (*tcl_GetMaster) (Tcl_Interp *interp); /* 164 */ const char * (*tcl_GetNameOfExecutable) (void); /* 165 */ Tcl_Obj * (*tcl_GetObjResult) (Tcl_Interp *interp); /* 166 */ #if !defined(__WIN32__) && !defined(MAC_OSX_TCL) /* UNIX */ int (*tcl_GetOpenFile) (Tcl_Interp *interp, const char *chanID, int forWriting, int checkUsage, ClientData *filePtr); /* 167 */ #endif /* UNIX */ #if defined(__WIN32__) /* WIN */ void (*reserved167)(void); #endif /* WIN */ #ifdef MAC_OSX_TCL /* MACOSX */ int (*tcl_GetOpenFile) (Tcl_Interp *interp, const char *chanID, int forWriting, int checkUsage, ClientData *filePtr); /* 167 */ #endif /* MACOSX */ Tcl_PathType (*tcl_GetPathType) (const char *path); /* 168 */ int (*tcl_Gets) (Tcl_Channel chan, Tcl_DString *dsPtr); /* 169 */ int (*tcl_GetsObj) (Tcl_Channel chan, Tcl_Obj *objPtr); /* 170 */ int (*tcl_GetServiceMode) (void); /* 171 */ Tcl_Interp * (*tcl_GetSlave) (Tcl_Interp *interp, const char *slaveName); /* 172 */ Tcl_Channel (*tcl_GetStdChannel) (int type); /* 173 */ CONST84_RETURN char * (*tcl_GetStringResult) (Tcl_Interp *interp); /* 174 */ CONST84_RETURN char * (*tcl_GetVar) (Tcl_Interp *interp, const char *varName, int flags); /* 175 */ CONST84_RETURN char * (*tcl_GetVar2) (Tcl_Interp *interp, const char *part1, const char *part2, int flags); /* 176 */ int (*tcl_GlobalEval) (Tcl_Interp *interp, const char *command); /* 177 */ int (*tcl_GlobalEvalObj) (Tcl_Interp *interp, Tcl_Obj *objPtr); /* 178 */ int (*tcl_HideCommand) (Tcl_Interp *interp, const char *cmdName, const char *hiddenCmdToken); /* 179 */ int (*tcl_Init) (Tcl_Interp *interp); /* 180 */ void (*tcl_InitHashTable) (Tcl_HashTable *tablePtr, int keyType); /* 181 */ int (*tcl_InputBlocked) (Tcl_Channel chan); /* 182 */ int (*tcl_InputBuffered) (Tcl_Channel chan); /* 183 */ int (*tcl_InterpDeleted) (Tcl_Interp *interp); /* 184 */ int (*tcl_IsSafe) (Tcl_Interp *interp); /* 185 */ char * (*tcl_JoinPath) (int argc, CONST84 char *const *argv, Tcl_DString *resultPtr); /* 186 */ int (*tcl_LinkVar) (Tcl_Interp *interp, const char *varName, char *addr, int type); /* 187 */ void (*reserved188)(void); Tcl_Channel (*tcl_MakeFileChannel) (ClientData handle, int mode); /* 189 */ int (*tcl_MakeSafe) (Tcl_Interp *interp); /* 190 */ Tcl_Channel (*tcl_MakeTcpClientChannel) (ClientData tcpSocket); /* 191 */ char * (*tcl_Merge) (int argc, CONST84 char *const *argv); /* 192 */ Tcl_HashEntry * (*tcl_NextHashEntry) (Tcl_HashSearch *searchPtr); /* 193 */ void (*tcl_NotifyChannel) (Tcl_Channel channel, int mask); /* 194 */ Tcl_Obj * (*tcl_ObjGetVar2) (Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, int flags); /* 195 */ Tcl_Obj * (*tcl_ObjSetVar2) (Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); /* 196 */ Tcl_Channel (*tcl_OpenCommandChannel) (Tcl_Interp *interp, int argc, CONST84 char **argv, int flags); /* 197 */ Tcl_Channel (*tcl_OpenFileChannel) (Tcl_Interp *interp, const char *fileName, const char *modeString, int permissions); /* 198 */ Tcl_Channel (*tcl_OpenTcpClient) (Tcl_Interp *interp, int port, const char *address, const char *myaddr, int myport, int async); /* 199 */ Tcl_Channel (*tcl_OpenTcpServer) (Tcl_Interp *interp, int port, const char *host, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); /* 200 */ void (*tcl_Preserve) (ClientData data); /* 201 */ void (*tcl_PrintDouble) (Tcl_Interp *interp, double value, char *dst); /* 202 */ int (*tcl_PutEnv) (const char *assignment); /* 203 */ CONST84_RETURN char * (*tcl_PosixError) (Tcl_Interp *interp); /* 204 */ void (*tcl_QueueEvent) (Tcl_Event *evPtr, Tcl_QueuePosition position); /* 205 */ int (*tcl_Read) (Tcl_Channel chan, char *bufPtr, int toRead); /* 206 */ void (*tcl_ReapDetachedProcs) (void); /* 207 */ int (*tcl_RecordAndEval) (Tcl_Interp *interp, const char *cmd, int flags); /* 208 */ int (*tcl_RecordAndEvalObj) (Tcl_Interp *interp, Tcl_Obj *cmdPtr, int flags); /* 209 */ void (*tcl_RegisterChannel) (Tcl_Interp *interp, Tcl_Channel chan); /* 210 */ void (*tcl_RegisterObjType) (const Tcl_ObjType *typePtr); /* 211 */ Tcl_RegExp (*tcl_RegExpCompile) (Tcl_Interp *interp, const char *pattern); /* 212 */ int (*tcl_RegExpExec) (Tcl_Interp *interp, Tcl_RegExp regexp, const char *text, const char *start); /* 213 */ int (*tcl_RegExpMatch) (Tcl_Interp *interp, const char *text, const char *pattern); /* 214 */ void (*tcl_RegExpRange) (Tcl_RegExp regexp, int index, CONST84 char **startPtr, CONST84 char **endPtr); /* 215 */ void (*tcl_Release) (ClientData clientData); /* 216 */ void (*tcl_ResetResult) (Tcl_Interp *interp); /* 217 */ int (*tcl_ScanElement) (const char *src, int *flagPtr); /* 218 */ int (*tcl_ScanCountedElement) (const char *src, int length, int *flagPtr); /* 219 */ int (*tcl_SeekOld) (Tcl_Channel chan, int offset, int mode); /* 220 */ int (*tcl_ServiceAll) (void); /* 221 */ int (*tcl_ServiceEvent) (int flags); /* 222 */ void (*tcl_SetAssocData) (Tcl_Interp *interp, const char *name, Tcl_InterpDeleteProc *proc, ClientData clientData); /* 223 */ void (*tcl_SetChannelBufferSize) (Tcl_Channel chan, int sz); /* 224 */ int (*tcl_SetChannelOption) (Tcl_Interp *interp, Tcl_Channel chan, const char *optionName, const char *newValue); /* 225 */ int (*tcl_SetCommandInfo) (Tcl_Interp *interp, const char *cmdName, const Tcl_CmdInfo *infoPtr); /* 226 */ void (*tcl_SetErrno) (int err); /* 227 */ void (*tcl_SetErrorCode) (Tcl_Interp *interp, ...); /* 228 */ void (*tcl_SetMaxBlockTime) (const Tcl_Time *timePtr); /* 229 */ void (*tcl_SetPanicProc) (Tcl_PanicProc *panicProc); /* 230 */ int (*tcl_SetRecursionLimit) (Tcl_Interp *interp, int depth); /* 231 */ void (*tcl_SetResult) (Tcl_Interp *interp, char *result, Tcl_FreeProc *freeProc); /* 232 */ int (*tcl_SetServiceMode) (int mode); /* 233 */ void (*tcl_SetObjErrorCode) (Tcl_Interp *interp, Tcl_Obj *errorObjPtr); /* 234 */ void (*tcl_SetObjResult) (Tcl_Interp *interp, Tcl_Obj *resultObjPtr); /* 235 */ void (*tcl_SetStdChannel) (Tcl_Channel channel, int type); /* 236 */ CONST84_RETURN char * (*tcl_SetVar) (Tcl_Interp *interp, const char *varName, const char *newValue, int flags); /* 237 */ CONST84_RETURN char * (*tcl_SetVar2) (Tcl_Interp *interp, const char *part1, const char *part2, const char *newValue, int flags); /* 238 */ CONST84_RETURN char * (*tcl_SignalId) (int sig); /* 239 */ CONST84_RETURN char * (*tcl_SignalMsg) (int sig); /* 240 */ void (*tcl_SourceRCFile) (Tcl_Interp *interp); /* 241 */ int (*tcl_SplitList) (Tcl_Interp *interp, const char *listStr, int *argcPtr, CONST84 char ***argvPtr); /* 242 */ void (*tcl_SplitPath) (const char *path, int *argcPtr, CONST84 char ***argvPtr); /* 243 */ void (*tcl_StaticPackage) (Tcl_Interp *interp, const char *pkgName, Tcl_PackageInitProc *initProc, Tcl_PackageInitProc *safeInitProc); /* 244 */ int (*tcl_StringMatch) (const char *str, const char *pattern); /* 245 */ int (*tcl_TellOld) (Tcl_Channel chan); /* 246 */ int (*tcl_TraceVar) (Tcl_Interp *interp, const char *varName, int flags, Tcl_VarTraceProc *proc, ClientData clientData); /* 247 */ int (*tcl_TraceVar2) (Tcl_Interp *interp, const char *part1, const char *part2, int flags, Tcl_VarTraceProc *proc, ClientData clientData); /* 248 */ char * (*tcl_TranslateFileName) (Tcl_Interp *interp, const char *name, Tcl_DString *bufferPtr); /* 249 */ int (*tcl_Ungets) (Tcl_Channel chan, const char *str, int len, int atHead); /* 250 */ void (*tcl_UnlinkVar) (Tcl_Interp *interp, const char *varName); /* 251 */ int (*tcl_UnregisterChannel) (Tcl_Interp *interp, Tcl_Channel chan); /* 252 */ int (*tcl_UnsetVar) (Tcl_Interp *interp, const char *varName, int flags); /* 253 */ int (*tcl_UnsetVar2) (Tcl_Interp *interp, const char *part1, const char *part2, int flags); /* 254 */ void (*tcl_UntraceVar) (Tcl_Interp *interp, const char *varName, int flags, Tcl_VarTraceProc *proc, ClientData clientData); /* 255 */ void (*tcl_UntraceVar2) (Tcl_Interp *interp, const char *part1, const char *part2, int flags, Tcl_VarTraceProc *proc, ClientData clientData); /* 256 */ void (*tcl_UpdateLinkedVar) (Tcl_Interp *interp, const char *varName); /* 257 */ int (*tcl_UpVar) (Tcl_Interp *interp, const char *frameName, const char *varName, const char *localName, int flags); /* 258 */ int (*tcl_UpVar2) (Tcl_Interp *interp, const char *frameName, const char *part1, const char *part2, const char *localName, int flags); /* 259 */ int (*tcl_VarEval) (Tcl_Interp *interp, ...); /* 260 */ ClientData (*tcl_VarTraceInfo) (Tcl_Interp *interp, const char *varName, int flags, Tcl_VarTraceProc *procPtr, ClientData prevClientData); /* 261 */ ClientData (*tcl_VarTraceInfo2) (Tcl_Interp *interp, const char *part1, const char *part2, int flags, Tcl_VarTraceProc *procPtr, ClientData prevClientData); /* 262 */ int (*tcl_Write) (Tcl_Channel chan, const char *s, int slen); /* 263 */ void (*tcl_WrongNumArgs) (Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], const char *message); /* 264 */ int (*tcl_DumpActiveMemory) (const char *fileName); /* 265 */ void (*tcl_ValidateAllMemory) (const char *file, int line); /* 266 */ void (*tcl_AppendResultVA) (Tcl_Interp *interp, va_list argList); /* 267 */ void (*tcl_AppendStringsToObjVA) (Tcl_Obj *objPtr, va_list argList); /* 268 */ char * (*tcl_HashStats) (Tcl_HashTable *tablePtr); /* 269 */ CONST84_RETURN char * (*tcl_ParseVar) (Tcl_Interp *interp, const char *start, CONST84 char **termPtr); /* 270 */ CONST84_RETURN char * (*tcl_PkgPresent) (Tcl_Interp *interp, const char *name, const char *version, int exact); /* 271 */ CONST84_RETURN char * (*tcl_PkgPresentEx) (Tcl_Interp *interp, const char *name, const char *version, int exact, void *clientDataPtr); /* 272 */ int (*tcl_PkgProvide) (Tcl_Interp *interp, const char *name, const char *version); /* 273 */ CONST84_RETURN char * (*tcl_PkgRequire) (Tcl_Interp *interp, const char *name, const char *version, int exact); /* 274 */ void (*tcl_SetErrorCodeVA) (Tcl_Interp *interp, va_list argList); /* 275 */ int (*tcl_VarEvalVA) (Tcl_Interp *interp, va_list argList); /* 276 */ Tcl_Pid (*tcl_WaitPid) (Tcl_Pid pid, int *statPtr, int options); /* 277 */ void (*tcl_PanicVA) (const char *format, va_list argList); /* 278 */ void (*tcl_GetVersion) (int *major, int *minor, int *patchLevel, int *type); /* 279 */ void (*tcl_InitMemory) (Tcl_Interp *interp); /* 280 */ Tcl_Channel (*tcl_StackChannel) (Tcl_Interp *interp, const Tcl_ChannelType *typePtr, ClientData instanceData, int mask, Tcl_Channel prevChan); /* 281 */ int (*tcl_UnstackChannel) (Tcl_Interp *interp, Tcl_Channel chan); /* 282 */ Tcl_Channel (*tcl_GetStackedChannel) (Tcl_Channel chan); /* 283 */ void (*tcl_SetMainLoop) (Tcl_MainLoopProc *proc); /* 284 */ void (*reserved285)(void); void (*tcl_AppendObjToObj) (Tcl_Obj *objPtr, Tcl_Obj *appendObjPtr); /* 286 */ Tcl_Encoding (*tcl_CreateEncoding) (const Tcl_EncodingType *typePtr); /* 287 */ void (*tcl_CreateThreadExitHandler) (Tcl_ExitProc *proc, ClientData clientData); /* 288 */ void (*tcl_DeleteThreadExitHandler) (Tcl_ExitProc *proc, ClientData clientData); /* 289 */ void (*tcl_DiscardResult) (Tcl_SavedResult *statePtr); /* 290 */ int (*tcl_EvalEx) (Tcl_Interp *interp, const char *script, int numBytes, int flags); /* 291 */ int (*tcl_EvalObjv) (Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], int flags); /* 292 */ int (*tcl_EvalObjEx) (Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 293 */ void (*tcl_ExitThread) (int status); /* 294 */ int (*tcl_ExternalToUtf) (Tcl_Interp *interp, Tcl_Encoding encoding, const char *src, int srcLen, int flags, Tcl_EncodingState *statePtr, char *dst, int dstLen, int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr); /* 295 */ char * (*tcl_ExternalToUtfDString) (Tcl_Encoding encoding, const char *src, int srcLen, Tcl_DString *dsPtr); /* 296 */ void (*tcl_FinalizeThread) (void); /* 297 */ void (*tcl_FinalizeNotifier) (ClientData clientData); /* 298 */ void (*tcl_FreeEncoding) (Tcl_Encoding encoding); /* 299 */ Tcl_ThreadId (*tcl_GetCurrentThread) (void); /* 300 */ Tcl_Encoding (*tcl_GetEncoding) (Tcl_Interp *interp, const char *name); /* 301 */ CONST84_RETURN char * (*tcl_GetEncodingName) (Tcl_Encoding encoding); /* 302 */ void (*tcl_GetEncodingNames) (Tcl_Interp *interp); /* 303 */ int (*tcl_GetIndexFromObjStruct) (Tcl_Interp *interp, Tcl_Obj *objPtr, const void *tablePtr, int offset, const char *msg, int flags, int *indexPtr); /* 304 */ void * (*tcl_GetThreadData) (Tcl_ThreadDataKey *keyPtr, int size); /* 305 */ Tcl_Obj * (*tcl_GetVar2Ex) (Tcl_Interp *interp, const char *part1, const char *part2, int flags); /* 306 */ ClientData (*tcl_InitNotifier) (void); /* 307 */ void (*tcl_MutexLock) (Tcl_Mutex *mutexPtr); /* 308 */ void (*tcl_MutexUnlock) (Tcl_Mutex *mutexPtr); /* 309 */ void (*tcl_ConditionNotify) (Tcl_Condition *condPtr); /* 310 */ void (*tcl_ConditionWait) (Tcl_Condition *condPtr, Tcl_Mutex *mutexPtr, const Tcl_Time *timePtr); /* 311 */ int (*tcl_NumUtfChars) (const char *src, int length); /* 312 */ int (*tcl_ReadChars) (Tcl_Channel channel, Tcl_Obj *objPtr, int charsToRead, int appendFlag); /* 313 */ void (*tcl_RestoreResult) (Tcl_Interp *interp, Tcl_SavedResult *statePtr); /* 314 */ void (*tcl_SaveResult) (Tcl_Interp *interp, Tcl_SavedResult *statePtr); /* 315 */ int (*tcl_SetSystemEncoding) (Tcl_Interp *interp, const char *name); /* 316 */ Tcl_Obj * (*tcl_SetVar2Ex) (Tcl_Interp *interp, const char *part1, const char *part2, Tcl_Obj *newValuePtr, int flags); /* 317 */ void (*tcl_ThreadAlert) (Tcl_ThreadId threadId); /* 318 */ void (*tcl_ThreadQueueEvent) (Tcl_ThreadId threadId, Tcl_Event *evPtr, Tcl_QueuePosition position); /* 319 */ Tcl_UniChar (*tcl_UniCharAtIndex) (const char *src, int index); /* 320 */ Tcl_UniChar (*tcl_UniCharToLower) (int ch); /* 321 */ Tcl_UniChar (*tcl_UniCharToTitle) (int ch); /* 322 */ Tcl_UniChar (*tcl_UniCharToUpper) (int ch); /* 323 */ int (*tcl_UniCharToUtf) (int ch, char *buf); /* 324 */ CONST84_RETURN char * (*tcl_UtfAtIndex) (const char *src, int index); /* 325 */ int (*tcl_UtfCharComplete) (const char *src, int length); /* 326 */ int (*tcl_UtfBackslash) (const char *src, int *readPtr, char *dst); /* 327 */ CONST84_RETURN char * (*tcl_UtfFindFirst) (const char *src, int ch); /* 328 */ CONST84_RETURN char * (*tcl_UtfFindLast) (const char *src, int ch); /* 329 */ CONST84_RETURN char * (*tcl_UtfNext) (const char *src); /* 330 */ CONST84_RETURN char * (*tcl_UtfPrev) (const char *src, const char *start); /* 331 */ int (*tcl_UtfToExternal) (Tcl_Interp *interp, Tcl_Encoding encoding, const char *src, int srcLen, int flags, Tcl_EncodingState *statePtr, char *dst, int dstLen, int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr); /* 332 */ char * (*tcl_UtfToExternalDString) (Tcl_Encoding encoding, const char *src, int srcLen, Tcl_DString *dsPtr); /* 333 */ int (*tcl_UtfToLower) (char *src); /* 334 */ int (*tcl_UtfToTitle) (char *src); /* 335 */ int (*tcl_UtfToUniChar) (const char *src, Tcl_UniChar *chPtr); /* 336 */ int (*tcl_UtfToUpper) (char *src); /* 337 */ int (*tcl_WriteChars) (Tcl_Channel chan, const char *src, int srcLen); /* 338 */ int (*tcl_WriteObj) (Tcl_Channel chan, Tcl_Obj *objPtr); /* 339 */ char * (*tcl_GetString) (Tcl_Obj *objPtr); /* 340 */ CONST84_RETURN char * (*tcl_GetDefaultEncodingDir) (void); /* 341 */ void (*tcl_SetDefaultEncodingDir) (const char *path); /* 342 */ void (*tcl_AlertNotifier) (ClientData clientData); /* 343 */ void (*tcl_ServiceModeHook) (int mode); /* 344 */ int (*tcl_UniCharIsAlnum) (int ch); /* 345 */ int (*tcl_UniCharIsAlpha) (int ch); /* 346 */ int (*tcl_UniCharIsDigit) (int ch); /* 347 */ int (*tcl_UniCharIsLower) (int ch); /* 348 */ int (*tcl_UniCharIsSpace) (int ch); /* 349 */ int (*tcl_UniCharIsUpper) (int ch); /* 350 */ int (*tcl_UniCharIsWordChar) (int ch); /* 351 */ int (*tcl_UniCharLen) (const Tcl_UniChar *uniStr); /* 352 */ int (*tcl_UniCharNcmp) (const Tcl_UniChar *ucs, const Tcl_UniChar *uct, unsigned long numChars); /* 353 */ char * (*tcl_UniCharToUtfDString) (const Tcl_UniChar *uniStr, int uniLength, Tcl_DString *dsPtr); /* 354 */ Tcl_UniChar * (*tcl_UtfToUniCharDString) (const char *src, int length, Tcl_DString *dsPtr); /* 355 */ Tcl_RegExp (*tcl_GetRegExpFromObj) (Tcl_Interp *interp, Tcl_Obj *patObj, int flags); /* 356 */ Tcl_Obj * (*tcl_EvalTokens) (Tcl_Interp *interp, Tcl_Token *tokenPtr, int count); /* 357 */ void (*tcl_FreeParse) (Tcl_Parse *parsePtr); /* 358 */ void (*tcl_LogCommandInfo) (Tcl_Interp *interp, const char *script, const char *command, int length); /* 359 */ int (*tcl_ParseBraces) (Tcl_Interp *interp, const char *start, int numBytes, Tcl_Parse *parsePtr, int append, CONST84 char **termPtr); /* 360 */ int (*tcl_ParseCommand) (Tcl_Interp *interp, const char *start, int numBytes, int nested, Tcl_Parse *parsePtr); /* 361 */ int (*tcl_ParseExpr) (Tcl_Interp *interp, const char *start, int numBytes, Tcl_Parse *parsePtr); /* 362 */ int (*tcl_ParseQuotedString) (Tcl_Interp *interp, const char *start, int numBytes, Tcl_Parse *parsePtr, int append, CONST84 char **termPtr); /* 363 */ int (*tcl_ParseVarName) (Tcl_Interp *interp, const char *start, int numBytes, Tcl_Parse *parsePtr, int append); /* 364 */ char * (*tcl_GetCwd) (Tcl_Interp *interp, Tcl_DString *cwdPtr); /* 365 */ int (*tcl_Chdir) (const char *dirName); /* 366 */ int (*tcl_Access) (const char *path, int mode); /* 367 */ int (*tcl_Stat) (const char *path, struct stat *bufPtr); /* 368 */ int (*tcl_UtfNcmp) (const char *s1, const char *s2, unsigned long n); /* 369 */ int (*tcl_UtfNcasecmp) (const char *s1, const char *s2, unsigned long n); /* 370 */ int (*tcl_StringCaseMatch) (const char *str, const char *pattern, int nocase); /* 371 */ int (*tcl_UniCharIsControl) (int ch); /* 372 */ int (*tcl_UniCharIsGraph) (int ch); /* 373 */ int (*tcl_UniCharIsPrint) (int ch); /* 374 */ int (*tcl_UniCharIsPunct) (int ch); /* 375 */ int (*tcl_RegExpExecObj) (Tcl_Interp *interp, Tcl_RegExp regexp, Tcl_Obj *textObj, int offset, int nmatches, int flags); /* 376 */ void (*tcl_RegExpGetInfo) (Tcl_RegExp regexp, Tcl_RegExpInfo *infoPtr); /* 377 */ Tcl_Obj * (*tcl_NewUnicodeObj) (const Tcl_UniChar *unicode, int numChars); /* 378 */ void (*tcl_SetUnicodeObj) (Tcl_Obj *objPtr, const Tcl_UniChar *unicode, int numChars); /* 379 */ int (*tcl_GetCharLength) (Tcl_Obj *objPtr); /* 380 */ Tcl_UniChar (*tcl_GetUniChar) (Tcl_Obj *objPtr, int index); /* 381 */ Tcl_UniChar * (*tcl_GetUnicode) (Tcl_Obj *objPtr); /* 382 */ Tcl_Obj * (*tcl_GetRange) (Tcl_Obj *objPtr, int first, int last); /* 383 */ void (*tcl_AppendUnicodeToObj) (Tcl_Obj *objPtr, const Tcl_UniChar *unicode, int length); /* 384 */ int (*tcl_RegExpMatchObj) (Tcl_Interp *interp, Tcl_Obj *textObj, Tcl_Obj *patternObj); /* 385 */ void (*tcl_SetNotifier) (Tcl_NotifierProcs *notifierProcPtr); /* 386 */ Tcl_Mutex * (*tcl_GetAllocMutex) (void); /* 387 */ int (*tcl_GetChannelNames) (Tcl_Interp *interp); /* 388 */ int (*tcl_GetChannelNamesEx) (Tcl_Interp *interp, const char *pattern); /* 389 */ int (*tcl_ProcObjCmd) (ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); /* 390 */ void (*tcl_ConditionFinalize) (Tcl_Condition *condPtr); /* 391 */ void (*tcl_MutexFinalize) (Tcl_Mutex *mutex); /* 392 */ int (*tcl_CreateThread) (Tcl_ThreadId *idPtr, Tcl_ThreadCreateProc *proc, ClientData clientData, int stackSize, int flags); /* 393 */ int (*tcl_ReadRaw) (Tcl_Channel chan, char *dst, int bytesToRead); /* 394 */ int (*tcl_WriteRaw) (Tcl_Channel chan, const char *src, int srcLen); /* 395 */ Tcl_Channel (*tcl_GetTopChannel) (Tcl_Channel chan); /* 396 */ int (*tcl_ChannelBuffered) (Tcl_Channel chan); /* 397 */ CONST84_RETURN char * (*tcl_ChannelName) (const Tcl_ChannelType *chanTypePtr); /* 398 */ Tcl_ChannelTypeVersion (*tcl_ChannelVersion) (const Tcl_ChannelType *chanTypePtr); /* 399 */ Tcl_DriverBlockModeProc * (*tcl_ChannelBlockModeProc) (const Tcl_ChannelType *chanTypePtr); /* 400 */ Tcl_DriverCloseProc * (*tcl_ChannelCloseProc) (const Tcl_ChannelType *chanTypePtr); /* 401 */ Tcl_DriverClose2Proc * (*tcl_ChannelClose2Proc) (const Tcl_ChannelType *chanTypePtr); /* 402 */ Tcl_DriverInputProc * (*tcl_ChannelInputProc) (const Tcl_ChannelType *chanTypePtr); /* 403 */ Tcl_DriverOutputProc * (*tcl_ChannelOutputProc) (const Tcl_ChannelType *chanTypePtr); /* 404 */ Tcl_DriverSeekProc * (*tcl_ChannelSeekProc) (const Tcl_ChannelType *chanTypePtr); /* 405 */ Tcl_DriverSetOptionProc * (*tcl_ChannelSetOptionProc) (const Tcl_ChannelType *chanTypePtr); /* 406 */ Tcl_DriverGetOptionProc * (*tcl_ChannelGetOptionProc) (const Tcl_ChannelType *chanTypePtr); /* 407 */ Tcl_DriverWatchProc * (*tcl_ChannelWatchProc) (const Tcl_ChannelType *chanTypePtr); /* 408 */ Tcl_DriverGetHandleProc * (*tcl_ChannelGetHandleProc) (const Tcl_ChannelType *chanTypePtr); /* 409 */ Tcl_DriverFlushProc * (*tcl_ChannelFlushProc) (const Tcl_ChannelType *chanTypePtr); /* 410 */ Tcl_DriverHandlerProc * (*tcl_ChannelHandlerProc) (const Tcl_ChannelType *chanTypePtr); /* 411 */ int (*tcl_JoinThread) (Tcl_ThreadId threadId, int *result); /* 412 */ int (*tcl_IsChannelShared) (Tcl_Channel channel); /* 413 */ int (*tcl_IsChannelRegistered) (Tcl_Interp *interp, Tcl_Channel channel); /* 414 */ void (*tcl_CutChannel) (Tcl_Channel channel); /* 415 */ void (*tcl_SpliceChannel) (Tcl_Channel channel); /* 416 */ void (*tcl_ClearChannelHandlers) (Tcl_Channel channel); /* 417 */ int (*tcl_IsChannelExisting) (const char *channelName); /* 418 */ int (*tcl_UniCharNcasecmp) (const Tcl_UniChar *ucs, const Tcl_UniChar *uct, unsigned long numChars); /* 419 */ int (*tcl_UniCharCaseMatch) (const Tcl_UniChar *uniStr, const Tcl_UniChar *uniPattern, int nocase); /* 420 */ Tcl_HashEntry * (*tcl_FindHashEntry) (Tcl_HashTable *tablePtr, const void *key); /* 421 */ Tcl_HashEntry * (*tcl_CreateHashEntry) (Tcl_HashTable *tablePtr, const void *key, int *newPtr); /* 422 */ void (*tcl_InitCustomHashTable) (Tcl_HashTable *tablePtr, int keyType, const Tcl_HashKeyType *typePtr); /* 423 */ void (*tcl_InitObjHashTable) (Tcl_HashTable *tablePtr); /* 424 */ ClientData (*tcl_CommandTraceInfo) (Tcl_Interp *interp, const char *varName, int flags, Tcl_CommandTraceProc *procPtr, ClientData prevClientData); /* 425 */ int (*tcl_TraceCommand) (Tcl_Interp *interp, const char *varName, int flags, Tcl_CommandTraceProc *proc, ClientData clientData); /* 426 */ void (*tcl_UntraceCommand) (Tcl_Interp *interp, const char *varName, int flags, Tcl_CommandTraceProc *proc, ClientData clientData); /* 427 */ char * (*tcl_AttemptAlloc) (unsigned int size); /* 428 */ char * (*tcl_AttemptDbCkalloc) (unsigned int size, const char *file, int line); /* 429 */ char * (*tcl_AttemptRealloc) (char *ptr, unsigned int size); /* 430 */ char * (*tcl_AttemptDbCkrealloc) (char *ptr, unsigned int size, const char *file, int line); /* 431 */ int (*tcl_AttemptSetObjLength) (Tcl_Obj *objPtr, int length); /* 432 */ Tcl_ThreadId (*tcl_GetChannelThread) (Tcl_Channel channel); /* 433 */ Tcl_UniChar * (*tcl_GetUnicodeFromObj) (Tcl_Obj *objPtr, int *lengthPtr); /* 434 */ int (*tcl_GetMathFuncInfo) (Tcl_Interp *interp, const char *name, int *numArgsPtr, Tcl_ValueType **argTypesPtr, Tcl_MathProc **procPtr, ClientData *clientDataPtr); /* 435 */ Tcl_Obj * (*tcl_ListMathFuncs) (Tcl_Interp *interp, const char *pattern); /* 436 */ Tcl_Obj * (*tcl_SubstObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 437 */ int (*tcl_DetachChannel) (Tcl_Interp *interp, Tcl_Channel channel); /* 438 */ int (*tcl_IsStandardChannel) (Tcl_Channel channel); /* 439 */ int (*tcl_FSCopyFile) (Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr); /* 440 */ int (*tcl_FSCopyDirectory) (Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr, Tcl_Obj **errorPtr); /* 441 */ int (*tcl_FSCreateDirectory) (Tcl_Obj *pathPtr); /* 442 */ int (*tcl_FSDeleteFile) (Tcl_Obj *pathPtr); /* 443 */ int (*tcl_FSLoadFile) (Tcl_Interp *interp, Tcl_Obj *pathPtr, const char *sym1, const char *sym2, Tcl_PackageInitProc **proc1Ptr, Tcl_PackageInitProc **proc2Ptr, Tcl_LoadHandle *handlePtr, Tcl_FSUnloadFileProc **unloadProcPtr); /* 444 */ int (*tcl_FSMatchInDirectory) (Tcl_Interp *interp, Tcl_Obj *result, Tcl_Obj *pathPtr, const char *pattern, Tcl_GlobTypeData *types); /* 445 */ Tcl_Obj * (*tcl_FSLink) (Tcl_Obj *pathPtr, Tcl_Obj *toPtr, int linkAction); /* 446 */ int (*tcl_FSRemoveDirectory) (Tcl_Obj *pathPtr, int recursive, Tcl_Obj **errorPtr); /* 447 */ int (*tcl_FSRenameFile) (Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr); /* 448 */ int (*tcl_FSLstat) (Tcl_Obj *pathPtr, Tcl_StatBuf *buf); /* 449 */ int (*tcl_FSUtime) (Tcl_Obj *pathPtr, struct utimbuf *tval); /* 450 */ int (*tcl_FSFileAttrsGet) (Tcl_Interp *interp, int index, Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef); /* 451 */ int (*tcl_FSFileAttrsSet) (Tcl_Interp *interp, int index, Tcl_Obj *pathPtr, Tcl_Obj *objPtr); /* 452 */ const char *CONST86 * (*tcl_FSFileAttrStrings) (Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef); /* 453 */ int (*tcl_FSStat) (Tcl_Obj *pathPtr, Tcl_StatBuf *buf); /* 454 */ int (*tcl_FSAccess) (Tcl_Obj *pathPtr, int mode); /* 455 */ Tcl_Channel (*tcl_FSOpenFileChannel) (Tcl_Interp *interp, Tcl_Obj *pathPtr, const char *modeString, int permissions); /* 456 */ Tcl_Obj * (*tcl_FSGetCwd) (Tcl_Interp *interp); /* 457 */ int (*tcl_FSChdir) (Tcl_Obj *pathPtr); /* 458 */ int (*tcl_FSConvertToPathType) (Tcl_Interp *interp, Tcl_Obj *pathPtr); /* 459 */ Tcl_Obj * (*tcl_FSJoinPath) (Tcl_Obj *listObj, int elements); /* 460 */ Tcl_Obj * (*tcl_FSSplitPath) (Tcl_Obj *pathPtr, int *lenPtr); /* 461 */ int (*tcl_FSEqualPaths) (Tcl_Obj *firstPtr, Tcl_Obj *secondPtr); /* 462 */ Tcl_Obj * (*tcl_FSGetNormalizedPath) (Tcl_Interp *interp, Tcl_Obj *pathPtr); /* 463 */ Tcl_Obj * (*tcl_FSJoinToPath) (Tcl_Obj *pathPtr, int objc, Tcl_Obj *const objv[]); /* 464 */ ClientData (*tcl_FSGetInternalRep) (Tcl_Obj *pathPtr, const Tcl_Filesystem *fsPtr); /* 465 */ Tcl_Obj * (*tcl_FSGetTranslatedPath) (Tcl_Interp *interp, Tcl_Obj *pathPtr); /* 466 */ int (*tcl_FSEvalFile) (Tcl_Interp *interp, Tcl_Obj *fileName); /* 467 */ Tcl_Obj * (*tcl_FSNewNativePath) (const Tcl_Filesystem *fromFilesystem, ClientData clientData); /* 468 */ const void * (*tcl_FSGetNativePath) (Tcl_Obj *pathPtr); /* 469 */ Tcl_Obj * (*tcl_FSFileSystemInfo) (Tcl_Obj *pathPtr); /* 470 */ Tcl_Obj * (*tcl_FSPathSeparator) (Tcl_Obj *pathPtr); /* 471 */ Tcl_Obj * (*tcl_FSListVolumes) (void); /* 472 */ int (*tcl_FSRegister) (ClientData clientData, const Tcl_Filesystem *fsPtr); /* 473 */ int (*tcl_FSUnregister) (const Tcl_Filesystem *fsPtr); /* 474 */ ClientData (*tcl_FSData) (const Tcl_Filesystem *fsPtr); /* 475 */ const char * (*tcl_FSGetTranslatedStringPath) (Tcl_Interp *interp, Tcl_Obj *pathPtr); /* 476 */ CONST86 Tcl_Filesystem * (*tcl_FSGetFileSystemForPath) (Tcl_Obj *pathPtr); /* 477 */ Tcl_PathType (*tcl_FSGetPathType) (Tcl_Obj *pathPtr); /* 478 */ int (*tcl_OutputBuffered) (Tcl_Channel chan); /* 479 */ void (*tcl_FSMountsChanged) (const Tcl_Filesystem *fsPtr); /* 480 */ int (*tcl_EvalTokensStandard) (Tcl_Interp *interp, Tcl_Token *tokenPtr, int count); /* 481 */ void (*tcl_GetTime) (Tcl_Time *timeBuf); /* 482 */ Tcl_Trace (*tcl_CreateObjTrace) (Tcl_Interp *interp, int level, int flags, Tcl_CmdObjTraceProc *objProc, ClientData clientData, Tcl_CmdObjTraceDeleteProc *delProc); /* 483 */ int (*tcl_GetCommandInfoFromToken) (Tcl_Command token, Tcl_CmdInfo *infoPtr); /* 484 */ int (*tcl_SetCommandInfoFromToken) (Tcl_Command token, const Tcl_CmdInfo *infoPtr); /* 485 */ Tcl_Obj * (*tcl_DbNewWideIntObj) (Tcl_WideInt wideValue, const char *file, int line); /* 486 */ int (*tcl_GetWideIntFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_WideInt *widePtr); /* 487 */ Tcl_Obj * (*tcl_NewWideIntObj) (Tcl_WideInt wideValue); /* 488 */ void (*tcl_SetWideIntObj) (Tcl_Obj *objPtr, Tcl_WideInt wideValue); /* 489 */ Tcl_StatBuf * (*tcl_AllocStatBuf) (void); /* 490 */ Tcl_WideInt (*tcl_Seek) (Tcl_Channel chan, Tcl_WideInt offset, int mode); /* 491 */ Tcl_WideInt (*tcl_Tell) (Tcl_Channel chan); /* 492 */ Tcl_DriverWideSeekProc * (*tcl_ChannelWideSeekProc) (const Tcl_ChannelType *chanTypePtr); /* 493 */ int (*tcl_DictObjPut) (Tcl_Interp *interp, Tcl_Obj *dictPtr, Tcl_Obj *keyPtr, Tcl_Obj *valuePtr); /* 494 */ int (*tcl_DictObjGet) (Tcl_Interp *interp, Tcl_Obj *dictPtr, Tcl_Obj *keyPtr, Tcl_Obj **valuePtrPtr); /* 495 */ int (*tcl_DictObjRemove) (Tcl_Interp *interp, Tcl_Obj *dictPtr, Tcl_Obj *keyPtr); /* 496 */ int (*tcl_DictObjSize) (Tcl_Interp *interp, Tcl_Obj *dictPtr, int *sizePtr); /* 497 */ int (*tcl_DictObjFirst) (Tcl_Interp *interp, Tcl_Obj *dictPtr, Tcl_DictSearch *searchPtr, Tcl_Obj **keyPtrPtr, Tcl_Obj **valuePtrPtr, int *donePtr); /* 498 */ void (*tcl_DictObjNext) (Tcl_DictSearch *searchPtr, Tcl_Obj **keyPtrPtr, Tcl_Obj **valuePtrPtr, int *donePtr); /* 499 */ void (*tcl_DictObjDone) (Tcl_DictSearch *searchPtr); /* 500 */ int (*tcl_DictObjPutKeyList) (Tcl_Interp *interp, Tcl_Obj *dictPtr, int keyc, Tcl_Obj *const *keyv, Tcl_Obj *valuePtr); /* 501 */ int (*tcl_DictObjRemoveKeyList) (Tcl_Interp *interp, Tcl_Obj *dictPtr, int keyc, Tcl_Obj *const *keyv); /* 502 */ Tcl_Obj * (*tcl_NewDictObj) (void); /* 503 */ Tcl_Obj * (*tcl_DbNewDictObj) (const char *file, int line); /* 504 */ void (*tcl_RegisterConfig) (Tcl_Interp *interp, const char *pkgName, const Tcl_Config *configuration, const char *valEncoding); /* 505 */ Tcl_Namespace * (*tcl_CreateNamespace) (Tcl_Interp *interp, const char *name, ClientData clientData, Tcl_NamespaceDeleteProc *deleteProc); /* 506 */ void (*tcl_DeleteNamespace) (Tcl_Namespace *nsPtr); /* 507 */ int (*tcl_AppendExportList) (Tcl_Interp *interp, Tcl_Namespace *nsPtr, Tcl_Obj *objPtr); /* 508 */ int (*tcl_Export) (Tcl_Interp *interp, Tcl_Namespace *nsPtr, const char *pattern, int resetListFirst); /* 509 */ int (*tcl_Import) (Tcl_Interp *interp, Tcl_Namespace *nsPtr, const char *pattern, int allowOverwrite); /* 510 */ int (*tcl_ForgetImport) (Tcl_Interp *interp, Tcl_Namespace *nsPtr, const char *pattern); /* 511 */ Tcl_Namespace * (*tcl_GetCurrentNamespace) (Tcl_Interp *interp); /* 512 */ Tcl_Namespace * (*tcl_GetGlobalNamespace) (Tcl_Interp *interp); /* 513 */ Tcl_Namespace * (*tcl_FindNamespace) (Tcl_Interp *interp, const char *name, Tcl_Namespace *contextNsPtr, int flags); /* 514 */ Tcl_Command (*tcl_FindCommand) (Tcl_Interp *interp, const char *name, Tcl_Namespace *contextNsPtr, int flags); /* 515 */ Tcl_Command (*tcl_GetCommandFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr); /* 516 */ void (*tcl_GetCommandFullName) (Tcl_Interp *interp, Tcl_Command command, Tcl_Obj *objPtr); /* 517 */ int (*tcl_FSEvalFileEx) (Tcl_Interp *interp, Tcl_Obj *fileName, const char *encodingName); /* 518 */ Tcl_ExitProc * (*tcl_SetExitProc) (Tcl_ExitProc *proc); /* 519 */ void (*tcl_LimitAddHandler) (Tcl_Interp *interp, int type, Tcl_LimitHandlerProc *handlerProc, ClientData clientData, Tcl_LimitHandlerDeleteProc *deleteProc); /* 520 */ void (*tcl_LimitRemoveHandler) (Tcl_Interp *interp, int type, Tcl_LimitHandlerProc *handlerProc, ClientData clientData); /* 521 */ int (*tcl_LimitReady) (Tcl_Interp *interp); /* 522 */ int (*tcl_LimitCheck) (Tcl_Interp *interp); /* 523 */ int (*tcl_LimitExceeded) (Tcl_Interp *interp); /* 524 */ void (*tcl_LimitSetCommands) (Tcl_Interp *interp, int commandLimit); /* 525 */ void (*tcl_LimitSetTime) (Tcl_Interp *interp, Tcl_Time *timeLimitPtr); /* 526 */ void (*tcl_LimitSetGranularity) (Tcl_Interp *interp, int type, int granularity); /* 527 */ int (*tcl_LimitTypeEnabled) (Tcl_Interp *interp, int type); /* 528 */ int (*tcl_LimitTypeExceeded) (Tcl_Interp *interp, int type); /* 529 */ void (*tcl_LimitTypeSet) (Tcl_Interp *interp, int type); /* 530 */ void (*tcl_LimitTypeReset) (Tcl_Interp *interp, int type); /* 531 */ int (*tcl_LimitGetCommands) (Tcl_Interp *interp); /* 532 */ void (*tcl_LimitGetTime) (Tcl_Interp *interp, Tcl_Time *timeLimitPtr); /* 533 */ int (*tcl_LimitGetGranularity) (Tcl_Interp *interp, int type); /* 534 */ Tcl_InterpState (*tcl_SaveInterpState) (Tcl_Interp *interp, int status); /* 535 */ int (*tcl_RestoreInterpState) (Tcl_Interp *interp, Tcl_InterpState state); /* 536 */ void (*tcl_DiscardInterpState) (Tcl_InterpState state); /* 537 */ int (*tcl_SetReturnOptions) (Tcl_Interp *interp, Tcl_Obj *options); /* 538 */ Tcl_Obj * (*tcl_GetReturnOptions) (Tcl_Interp *interp, int result); /* 539 */ int (*tcl_IsEnsemble) (Tcl_Command token); /* 540 */ Tcl_Command (*tcl_CreateEnsemble) (Tcl_Interp *interp, const char *name, Tcl_Namespace *namespacePtr, int flags); /* 541 */ Tcl_Command (*tcl_FindEnsemble) (Tcl_Interp *interp, Tcl_Obj *cmdNameObj, int flags); /* 542 */ int (*tcl_SetEnsembleSubcommandList) (Tcl_Interp *interp, Tcl_Command token, Tcl_Obj *subcmdList); /* 543 */ int (*tcl_SetEnsembleMappingDict) (Tcl_Interp *interp, Tcl_Command token, Tcl_Obj *mapDict); /* 544 */ int (*tcl_SetEnsembleUnknownHandler) (Tcl_Interp *interp, Tcl_Command token, Tcl_Obj *unknownList); /* 545 */ int (*tcl_SetEnsembleFlags) (Tcl_Interp *interp, Tcl_Command token, int flags); /* 546 */ int (*tcl_GetEnsembleSubcommandList) (Tcl_Interp *interp, Tcl_Command token, Tcl_Obj **subcmdListPtr); /* 547 */ int (*tcl_GetEnsembleMappingDict) (Tcl_Interp *interp, Tcl_Command token, Tcl_Obj **mapDictPtr); /* 548 */ int (*tcl_GetEnsembleUnknownHandler) (Tcl_Interp *interp, Tcl_Command token, Tcl_Obj **unknownListPtr); /* 549 */ int (*tcl_GetEnsembleFlags) (Tcl_Interp *interp, Tcl_Command token, int *flagsPtr); /* 550 */ int (*tcl_GetEnsembleNamespace) (Tcl_Interp *interp, Tcl_Command token, Tcl_Namespace **namespacePtrPtr); /* 551 */ void (*tcl_SetTimeProc) (Tcl_GetTimeProc *getProc, Tcl_ScaleTimeProc *scaleProc, ClientData clientData); /* 552 */ void (*tcl_QueryTimeProc) (Tcl_GetTimeProc **getProc, Tcl_ScaleTimeProc **scaleProc, ClientData *clientData); /* 553 */ Tcl_DriverThreadActionProc * (*tcl_ChannelThreadActionProc) (const Tcl_ChannelType *chanTypePtr); /* 554 */ Tcl_Obj * (*tcl_NewBignumObj) (mp_int *value); /* 555 */ Tcl_Obj * (*tcl_DbNewBignumObj) (mp_int *value, const char *file, int line); /* 556 */ void (*tcl_SetBignumObj) (Tcl_Obj *obj, mp_int *value); /* 557 */ int (*tcl_GetBignumFromObj) (Tcl_Interp *interp, Tcl_Obj *obj, mp_int *value); /* 558 */ int (*tcl_TakeBignumFromObj) (Tcl_Interp *interp, Tcl_Obj *obj, mp_int *value); /* 559 */ int (*tcl_TruncateChannel) (Tcl_Channel chan, Tcl_WideInt length); /* 560 */ Tcl_DriverTruncateProc * (*tcl_ChannelTruncateProc) (const Tcl_ChannelType *chanTypePtr); /* 561 */ void (*tcl_SetChannelErrorInterp) (Tcl_Interp *interp, Tcl_Obj *msg); /* 562 */ void (*tcl_GetChannelErrorInterp) (Tcl_Interp *interp, Tcl_Obj **msg); /* 563 */ void (*tcl_SetChannelError) (Tcl_Channel chan, Tcl_Obj *msg); /* 564 */ void (*tcl_GetChannelError) (Tcl_Channel chan, Tcl_Obj **msg); /* 565 */ int (*tcl_InitBignumFromDouble) (Tcl_Interp *interp, double initval, mp_int *toInit); /* 566 */ Tcl_Obj * (*tcl_GetNamespaceUnknownHandler) (Tcl_Interp *interp, Tcl_Namespace *nsPtr); /* 567 */ int (*tcl_SetNamespaceUnknownHandler) (Tcl_Interp *interp, Tcl_Namespace *nsPtr, Tcl_Obj *handlerPtr); /* 568 */ int (*tcl_GetEncodingFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_Encoding *encodingPtr); /* 569 */ Tcl_Obj * (*tcl_GetEncodingSearchPath) (void); /* 570 */ int (*tcl_SetEncodingSearchPath) (Tcl_Obj *searchPath); /* 571 */ const char * (*tcl_GetEncodingNameFromEnvironment) (Tcl_DString *bufPtr); /* 572 */ int (*tcl_PkgRequireProc) (Tcl_Interp *interp, const char *name, int objc, Tcl_Obj *const objv[], void *clientDataPtr); /* 573 */ void (*tcl_AppendObjToErrorInfo) (Tcl_Interp *interp, Tcl_Obj *objPtr); /* 574 */ void (*tcl_AppendLimitedToObj) (Tcl_Obj *objPtr, const char *bytes, int length, int limit, const char *ellipsis); /* 575 */ Tcl_Obj * (*tcl_Format) (Tcl_Interp *interp, const char *format, int objc, Tcl_Obj *const objv[]); /* 576 */ int (*tcl_AppendFormatToObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, const char *format, int objc, Tcl_Obj *const objv[]); /* 577 */ Tcl_Obj * (*tcl_ObjPrintf) (const char *format, ...) TCL_FORMAT_PRINTF(1, 2); /* 578 */ void (*tcl_AppendPrintfToObj) (Tcl_Obj *objPtr, const char *format, ...) TCL_FORMAT_PRINTF(2, 3); /* 579 */ int (*tcl_CancelEval) (Tcl_Interp *interp, Tcl_Obj *resultObjPtr, ClientData clientData, int flags); /* 580 */ int (*tcl_Canceled) (Tcl_Interp *interp, int flags); /* 581 */ int (*tcl_CreatePipe) (Tcl_Interp *interp, Tcl_Channel *rchan, Tcl_Channel *wchan, int flags); /* 582 */ Tcl_Command (*tcl_NRCreateCommand) (Tcl_Interp *interp, const char *cmdName, Tcl_ObjCmdProc *proc, Tcl_ObjCmdProc *nreProc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); /* 583 */ int (*tcl_NREvalObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 584 */ int (*tcl_NREvalObjv) (Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], int flags); /* 585 */ int (*tcl_NRCmdSwap) (Tcl_Interp *interp, Tcl_Command cmd, int objc, Tcl_Obj *const objv[], int flags); /* 586 */ void (*tcl_NRAddCallback) (Tcl_Interp *interp, Tcl_NRPostProc *postProcPtr, ClientData data0, ClientData data1, ClientData data2, ClientData data3); /* 587 */ int (*tcl_NRCallObjProc) (Tcl_Interp *interp, Tcl_ObjCmdProc *objProc, ClientData clientData, int objc, Tcl_Obj *const objv[]); /* 588 */ unsigned (*tcl_GetFSDeviceFromStat) (const Tcl_StatBuf *statPtr); /* 589 */ unsigned (*tcl_GetFSInodeFromStat) (const Tcl_StatBuf *statPtr); /* 590 */ unsigned (*tcl_GetModeFromStat) (const Tcl_StatBuf *statPtr); /* 591 */ int (*tcl_GetLinkCountFromStat) (const Tcl_StatBuf *statPtr); /* 592 */ int (*tcl_GetUserIdFromStat) (const Tcl_StatBuf *statPtr); /* 593 */ int (*tcl_GetGroupIdFromStat) (const Tcl_StatBuf *statPtr); /* 594 */ int (*tcl_GetDeviceTypeFromStat) (const Tcl_StatBuf *statPtr); /* 595 */ Tcl_WideInt (*tcl_GetAccessTimeFromStat) (const Tcl_StatBuf *statPtr); /* 596 */ Tcl_WideInt (*tcl_GetModificationTimeFromStat) (const Tcl_StatBuf *statPtr); /* 597 */ Tcl_WideInt (*tcl_GetChangeTimeFromStat) (const Tcl_StatBuf *statPtr); /* 598 */ Tcl_WideUInt (*tcl_GetSizeFromStat) (const Tcl_StatBuf *statPtr); /* 599 */ Tcl_WideUInt (*tcl_GetBlocksFromStat) (const Tcl_StatBuf *statPtr); /* 600 */ unsigned (*tcl_GetBlockSizeFromStat) (const Tcl_StatBuf *statPtr); /* 601 */ int (*tcl_SetEnsembleParameterList) (Tcl_Interp *interp, Tcl_Command token, Tcl_Obj *paramList); /* 602 */ int (*tcl_GetEnsembleParameterList) (Tcl_Interp *interp, Tcl_Command token, Tcl_Obj **paramListPtr); /* 603 */ int (*tcl_ParseArgsObjv) (Tcl_Interp *interp, const Tcl_ArgvInfo *argTable, int *objcPtr, Tcl_Obj *const *objv, Tcl_Obj ***remObjv); /* 604 */ int (*tcl_GetErrorLine) (Tcl_Interp *interp); /* 605 */ void (*tcl_SetErrorLine) (Tcl_Interp *interp, int lineNum); /* 606 */ void (*tcl_TransferResult) (Tcl_Interp *sourceInterp, int result, Tcl_Interp *targetInterp); /* 607 */ int (*tcl_InterpActive) (Tcl_Interp *interp); /* 608 */ void (*tcl_BackgroundException) (Tcl_Interp *interp, int code); /* 609 */ int (*tcl_ZlibDeflate) (Tcl_Interp *interp, int format, Tcl_Obj *data, int level, Tcl_Obj *gzipHeaderDictObj); /* 610 */ int (*tcl_ZlibInflate) (Tcl_Interp *interp, int format, Tcl_Obj *data, int buffersize, Tcl_Obj *gzipHeaderDictObj); /* 611 */ unsigned int (*tcl_ZlibCRC32) (unsigned int crc, const unsigned char *buf, int len); /* 612 */ unsigned int (*tcl_ZlibAdler32) (unsigned int adler, const unsigned char *buf, int len); /* 613 */ int (*tcl_ZlibStreamInit) (Tcl_Interp *interp, int mode, int format, int level, Tcl_Obj *dictObj, Tcl_ZlibStream *zshandle); /* 614 */ Tcl_Obj * (*tcl_ZlibStreamGetCommandName) (Tcl_ZlibStream zshandle); /* 615 */ int (*tcl_ZlibStreamEof) (Tcl_ZlibStream zshandle); /* 616 */ int (*tcl_ZlibStreamChecksum) (Tcl_ZlibStream zshandle); /* 617 */ int (*tcl_ZlibStreamPut) (Tcl_ZlibStream zshandle, Tcl_Obj *data, int flush); /* 618 */ int (*tcl_ZlibStreamGet) (Tcl_ZlibStream zshandle, Tcl_Obj *data, int count); /* 619 */ int (*tcl_ZlibStreamClose) (Tcl_ZlibStream zshandle); /* 620 */ int (*tcl_ZlibStreamReset) (Tcl_ZlibStream zshandle); /* 621 */ void (*tcl_SetStartupScript) (Tcl_Obj *path, const char *encoding); /* 622 */ Tcl_Obj * (*tcl_GetStartupScript) (const char **encodingPtr); /* 623 */ int (*tcl_CloseEx) (Tcl_Interp *interp, Tcl_Channel chan, int flags); /* 624 */ int (*tcl_NRExprObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_Obj *resultPtr); /* 625 */ int (*tcl_NRSubstObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 626 */ int (*tcl_LoadFile) (Tcl_Interp *interp, Tcl_Obj *pathPtr, const char *const symv[], int flags, void *procPtrs, Tcl_LoadHandle *handlePtr); /* 627 */ void * (*tcl_FindSymbol) (Tcl_Interp *interp, Tcl_LoadHandle handle, const char *symbol); /* 628 */ int (*tcl_FSUnloadFile) (Tcl_Interp *interp, Tcl_LoadHandle handlePtr); /* 629 */ void (*tcl_ZlibStreamSetCompressionDictionary) (Tcl_ZlibStream zhandle, Tcl_Obj *compressionDictionaryObj); /* 630 */ } TclStubs; #ifdef __cplusplus extern "C" { #endif extern const TclStubs *tclStubsPtr; #ifdef __cplusplus } #endif #if defined(USE_TCL_STUBS) /* * Inline function declarations: */ #define Tcl_PkgProvideEx \ (tclStubsPtr->tcl_PkgProvideEx) /* 0 */ #define Tcl_PkgRequireEx \ (tclStubsPtr->tcl_PkgRequireEx) /* 1 */ #define Tcl_Panic \ (tclStubsPtr->tcl_Panic) /* 2 */ #define Tcl_Alloc \ (tclStubsPtr->tcl_Alloc) /* 3 */ #define Tcl_Free \ (tclStubsPtr->tcl_Free) /* 4 */ #define Tcl_Realloc \ (tclStubsPtr->tcl_Realloc) /* 5 */ #define Tcl_DbCkalloc \ (tclStubsPtr->tcl_DbCkalloc) /* 6 */ #define Tcl_DbCkfree \ (tclStubsPtr->tcl_DbCkfree) /* 7 */ #define Tcl_DbCkrealloc \ (tclStubsPtr->tcl_DbCkrealloc) /* 8 */ #if !defined(__WIN32__) && !defined(MAC_OSX_TCL) /* UNIX */ #define Tcl_CreateFileHandler \ (tclStubsPtr->tcl_CreateFileHandler) /* 9 */ #endif /* UNIX */ #ifdef MAC_OSX_TCL /* MACOSX */ #define Tcl_CreateFileHandler \ (tclStubsPtr->tcl_CreateFileHandler) /* 9 */ #endif /* MACOSX */ #if !defined(__WIN32__) && !defined(MAC_OSX_TCL) /* UNIX */ #define Tcl_DeleteFileHandler \ (tclStubsPtr->tcl_DeleteFileHandler) /* 10 */ #endif /* UNIX */ #ifdef MAC_OSX_TCL /* MACOSX */ #define Tcl_DeleteFileHandler \ (tclStubsPtr->tcl_DeleteFileHandler) /* 10 */ #endif /* MACOSX */ #define Tcl_SetTimer \ (tclStubsPtr->tcl_SetTimer) /* 11 */ #define Tcl_Sleep \ (tclStubsPtr->tcl_Sleep) /* 12 */ #define Tcl_WaitForEvent \ (tclStubsPtr->tcl_WaitForEvent) /* 13 */ #define Tcl_AppendAllObjTypes \ (tclStubsPtr->tcl_AppendAllObjTypes) /* 14 */ #define Tcl_AppendStringsToObj \ (tclStubsPtr->tcl_AppendStringsToObj) /* 15 */ #define Tcl_AppendToObj \ (tclStubsPtr->tcl_AppendToObj) /* 16 */ #define Tcl_ConcatObj \ (tclStubsPtr->tcl_ConcatObj) /* 17 */ #define Tcl_ConvertToType \ (tclStubsPtr->tcl_ConvertToType) /* 18 */ #define Tcl_DbDecrRefCount \ (tclStubsPtr->tcl_DbDecrRefCount) /* 19 */ #define Tcl_DbIncrRefCount \ (tclStubsPtr->tcl_DbIncrRefCount) /* 20 */ #define Tcl_DbIsShared \ (tclStubsPtr->tcl_DbIsShared) /* 21 */ #define Tcl_DbNewBooleanObj \ (tclStubsPtr->tcl_DbNewBooleanObj) /* 22 */ #define Tcl_DbNewByteArrayObj \ (tclStubsPtr->tcl_DbNewByteArrayObj) /* 23 */ #define Tcl_DbNewDoubleObj \ (tclStubsPtr->tcl_DbNewDoubleObj) /* 24 */ #define Tcl_DbNewListObj \ (tclStubsPtr->tcl_DbNewListObj) /* 25 */ #define Tcl_DbNewLongObj \ (tclStubsPtr->tcl_DbNewLongObj) /* 26 */ #define Tcl_DbNewObj \ (tclStubsPtr->tcl_DbNewObj) /* 27 */ #define Tcl_DbNewStringObj \ (tclStubsPtr->tcl_DbNewStringObj) /* 28 */ #define Tcl_DuplicateObj \ (tclStubsPtr->tcl_DuplicateObj) /* 29 */ #define TclFreeObj \ (tclStubsPtr->tclFreeObj) /* 30 */ #define Tcl_GetBoolean \ (tclStubsPtr->tcl_GetBoolean) /* 31 */ #define Tcl_GetBooleanFromObj \ (tclStubsPtr->tcl_GetBooleanFromObj) /* 32 */ #define Tcl_GetByteArrayFromObj \ (tclStubsPtr->tcl_GetByteArrayFromObj) /* 33 */ #define Tcl_GetDouble \ (tclStubsPtr->tcl_GetDouble) /* 34 */ #define Tcl_GetDoubleFromObj \ (tclStubsPtr->tcl_GetDoubleFromObj) /* 35 */ #define Tcl_GetIndexFromObj \ (tclStubsPtr->tcl_GetIndexFromObj) /* 36 */ #define Tcl_GetInt \ (tclStubsPtr->tcl_GetInt) /* 37 */ #define Tcl_GetIntFromObj \ (tclStubsPtr->tcl_GetIntFromObj) /* 38 */ #define Tcl_GetLongFromObj \ (tclStubsPtr->tcl_GetLongFromObj) /* 39 */ #define Tcl_GetObjType \ (tclStubsPtr->tcl_GetObjType) /* 40 */ #define Tcl_GetStringFromObj \ (tclStubsPtr->tcl_GetStringFromObj) /* 41 */ #define Tcl_InvalidateStringRep \ (tclStubsPtr->tcl_InvalidateStringRep) /* 42 */ #define Tcl_ListObjAppendList \ (tclStubsPtr->tcl_ListObjAppendList) /* 43 */ #define Tcl_ListObjAppendElement \ (tclStubsPtr->tcl_ListObjAppendElement) /* 44 */ #define Tcl_ListObjGetElements \ (tclStubsPtr->tcl_ListObjGetElements) /* 45 */ #define Tcl_ListObjIndex \ (tclStubsPtr->tcl_ListObjIndex) /* 46 */ #define Tcl_ListObjLength \ (tclStubsPtr->tcl_ListObjLength) /* 47 */ #define Tcl_ListObjReplace \ (tclStubsPtr->tcl_ListObjReplace) /* 48 */ #define Tcl_NewBooleanObj \ (tclStubsPtr->tcl_NewBooleanObj) /* 49 */ #define Tcl_NewByteArrayObj \ (tclStubsPtr->tcl_NewByteArrayObj) /* 50 */ #define Tcl_NewDoubleObj \ (tclStubsPtr->tcl_NewDoubleObj) /* 51 */ #define Tcl_NewIntObj \ (tclStubsPtr->tcl_NewIntObj) /* 52 */ #define Tcl_NewListObj \ (tclStubsPtr->tcl_NewListObj) /* 53 */ #define Tcl_NewLongObj \ (tclStubsPtr->tcl_NewLongObj) /* 54 */ #define Tcl_NewObj \ (tclStubsPtr->tcl_NewObj) /* 55 */ #define Tcl_NewStringObj \ (tclStubsPtr->tcl_NewStringObj) /* 56 */ #define Tcl_SetBooleanObj \ (tclStubsPtr->tcl_SetBooleanObj) /* 57 */ #define Tcl_SetByteArrayLength \ (tclStubsPtr->tcl_SetByteArrayLength) /* 58 */ #define Tcl_SetByteArrayObj \ (tclStubsPtr->tcl_SetByteArrayObj) /* 59 */ #define Tcl_SetDoubleObj \ (tclStubsPtr->tcl_SetDoubleObj) /* 60 */ #define Tcl_SetIntObj \ (tclStubsPtr->tcl_SetIntObj) /* 61 */ #define Tcl_SetListObj \ (tclStubsPtr->tcl_SetListObj) /* 62 */ #define Tcl_SetLongObj \ (tclStubsPtr->tcl_SetLongObj) /* 63 */ #define Tcl_SetObjLength \ (tclStubsPtr->tcl_SetObjLength) /* 64 */ #define Tcl_SetStringObj \ (tclStubsPtr->tcl_SetStringObj) /* 65 */ #define Tcl_AddErrorInfo \ (tclStubsPtr->tcl_AddErrorInfo) /* 66 */ #define Tcl_AddObjErrorInfo \ (tclStubsPtr->tcl_AddObjErrorInfo) /* 67 */ #define Tcl_AllowExceptions \ (tclStubsPtr->tcl_AllowExceptions) /* 68 */ #define Tcl_AppendElement \ (tclStubsPtr->tcl_AppendElement) /* 69 */ #define Tcl_AppendResult \ (tclStubsPtr->tcl_AppendResult) /* 70 */ #define Tcl_AsyncCreate \ (tclStubsPtr->tcl_AsyncCreate) /* 71 */ #define Tcl_AsyncDelete \ (tclStubsPtr->tcl_AsyncDelete) /* 72 */ #define Tcl_AsyncInvoke \ (tclStubsPtr->tcl_AsyncInvoke) /* 73 */ #define Tcl_AsyncMark \ (tclStubsPtr->tcl_AsyncMark) /* 74 */ #define Tcl_AsyncReady \ (tclStubsPtr->tcl_AsyncReady) /* 75 */ #define Tcl_BackgroundError \ (tclStubsPtr->tcl_BackgroundError) /* 76 */ #define Tcl_Backslash \ (tclStubsPtr->tcl_Backslash) /* 77 */ #define Tcl_BadChannelOption \ (tclStubsPtr->tcl_BadChannelOption) /* 78 */ #define Tcl_CallWhenDeleted \ (tclStubsPtr->tcl_CallWhenDeleted) /* 79 */ #define Tcl_CancelIdleCall \ (tclStubsPtr->tcl_CancelIdleCall) /* 80 */ #define Tcl_Close \ (tclStubsPtr->tcl_Close) /* 81 */ #define Tcl_CommandComplete \ (tclStubsPtr->tcl_CommandComplete) /* 82 */ #define Tcl_Concat \ (tclStubsPtr->tcl_Concat) /* 83 */ #define Tcl_ConvertElement \ (tclStubsPtr->tcl_ConvertElement) /* 84 */ #define Tcl_ConvertCountedElement \ (tclStubsPtr->tcl_ConvertCountedElement) /* 85 */ #define Tcl_CreateAlias \ (tclStubsPtr->tcl_CreateAlias) /* 86 */ #define Tcl_CreateAliasObj \ (tclStubsPtr->tcl_CreateAliasObj) /* 87 */ #define Tcl_CreateChannel \ (tclStubsPtr->tcl_CreateChannel) /* 88 */ #define Tcl_CreateChannelHandler \ (tclStubsPtr->tcl_CreateChannelHandler) /* 89 */ #define Tcl_CreateCloseHandler \ (tclStubsPtr->tcl_CreateCloseHandler) /* 90 */ #define Tcl_CreateCommand \ (tclStubsPtr->tcl_CreateCommand) /* 91 */ #define Tcl_CreateEventSource \ (tclStubsPtr->tcl_CreateEventSource) /* 92 */ #define Tcl_CreateExitHandler \ (tclStubsPtr->tcl_CreateExitHandler) /* 93 */ #define Tcl_CreateInterp \ (tclStubsPtr->tcl_CreateInterp) /* 94 */ #define Tcl_CreateMathFunc \ (tclStubsPtr->tcl_CreateMathFunc) /* 95 */ #define Tcl_CreateObjCommand \ (tclStubsPtr->tcl_CreateObjCommand) /* 96 */ #define Tcl_CreateSlave \ (tclStubsPtr->tcl_CreateSlave) /* 97 */ #define Tcl_CreateTimerHandler \ (tclStubsPtr->tcl_CreateTimerHandler) /* 98 */ #define Tcl_CreateTrace \ (tclStubsPtr->tcl_CreateTrace) /* 99 */ #define Tcl_DeleteAssocData \ (tclStubsPtr->tcl_DeleteAssocData) /* 100 */ #define Tcl_DeleteChannelHandler \ (tclStubsPtr->tcl_DeleteChannelHandler) /* 101 */ #define Tcl_DeleteCloseHandler \ (tclStubsPtr->tcl_DeleteCloseHandler) /* 102 */ #define Tcl_DeleteCommand \ (tclStubsPtr->tcl_DeleteCommand) /* 103 */ #define Tcl_DeleteCommandFromToken \ (tclStubsPtr->tcl_DeleteCommandFromToken) /* 104 */ #define Tcl_DeleteEvents \ (tclStubsPtr->tcl_DeleteEvents) /* 105 */ #define Tcl_DeleteEventSource \ (tclStubsPtr->tcl_DeleteEventSource) /* 106 */ #define Tcl_DeleteExitHandler \ (tclStubsPtr->tcl_DeleteExitHandler) /* 107 */ #define Tcl_DeleteHashEntry \ (tclStubsPtr->tcl_DeleteHashEntry) /* 108 */ #define Tcl_DeleteHashTable \ (tclStubsPtr->tcl_DeleteHashTable) /* 109 */ #define Tcl_DeleteInterp \ (tclStubsPtr->tcl_DeleteInterp) /* 110 */ #define Tcl_DetachPids \ (tclStubsPtr->tcl_DetachPids) /* 111 */ #define Tcl_DeleteTimerHandler \ (tclStubsPtr->tcl_DeleteTimerHandler) /* 112 */ #define Tcl_DeleteTrace \ (tclStubsPtr->tcl_DeleteTrace) /* 113 */ #define Tcl_DontCallWhenDeleted \ (tclStubsPtr->tcl_DontCallWhenDeleted) /* 114 */ #define Tcl_DoOneEvent \ (tclStubsPtr->tcl_DoOneEvent) /* 115 */ #define Tcl_DoWhenIdle \ (tclStubsPtr->tcl_DoWhenIdle) /* 116 */ #define Tcl_DStringAppend \ (tclStubsPtr->tcl_DStringAppend) /* 117 */ #define Tcl_DStringAppendElement \ (tclStubsPtr->tcl_DStringAppendElement) /* 118 */ #define Tcl_DStringEndSublist \ (tclStubsPtr->tcl_DStringEndSublist) /* 119 */ #define Tcl_DStringFree \ (tclStubsPtr->tcl_DStringFree) /* 120 */ #define Tcl_DStringGetResult \ (tclStubsPtr->tcl_DStringGetResult) /* 121 */ #define Tcl_DStringInit \ (tclStubsPtr->tcl_DStringInit) /* 122 */ #define Tcl_DStringResult \ (tclStubsPtr->tcl_DStringResult) /* 123 */ #define Tcl_DStringSetLength \ (tclStubsPtr->tcl_DStringSetLength) /* 124 */ #define Tcl_DStringStartSublist \ (tclStubsPtr->tcl_DStringStartSublist) /* 125 */ #define Tcl_Eof \ (tclStubsPtr->tcl_Eof) /* 126 */ #define Tcl_ErrnoId \ (tclStubsPtr->tcl_ErrnoId) /* 127 */ #define Tcl_ErrnoMsg \ (tclStubsPtr->tcl_ErrnoMsg) /* 128 */ #define Tcl_Eval \ (tclStubsPtr->tcl_Eval) /* 129 */ #define Tcl_EvalFile \ (tclStubsPtr->tcl_EvalFile) /* 130 */ #define Tcl_EvalObj \ (tclStubsPtr->tcl_EvalObj) /* 131 */ #define Tcl_EventuallyFree \ (tclStubsPtr->tcl_EventuallyFree) /* 132 */ #define Tcl_Exit \ (tclStubsPtr->tcl_Exit) /* 133 */ #define Tcl_ExposeCommand \ (tclStubsPtr->tcl_ExposeCommand) /* 134 */ #define Tcl_ExprBoolean \ (tclStubsPtr->tcl_ExprBoolean) /* 135 */ #define Tcl_ExprBooleanObj \ (tclStubsPtr->tcl_ExprBooleanObj) /* 136 */ #define Tcl_ExprDouble \ (tclStubsPtr->tcl_ExprDouble) /* 137 */ #define Tcl_ExprDoubleObj \ (tclStubsPtr->tcl_ExprDoubleObj) /* 138 */ #define Tcl_ExprLong \ (tclStubsPtr->tcl_ExprLong) /* 139 */ #define Tcl_ExprLongObj \ (tclStubsPtr->tcl_ExprLongObj) /* 140 */ #define Tcl_ExprObj \ (tclStubsPtr->tcl_ExprObj) /* 141 */ #define Tcl_ExprString \ (tclStubsPtr->tcl_ExprString) /* 142 */ #define Tcl_Finalize \ (tclStubsPtr->tcl_Finalize) /* 143 */ #define Tcl_FindExecutable \ (tclStubsPtr->tcl_FindExecutable) /* 144 */ #define Tcl_FirstHashEntry \ (tclStubsPtr->tcl_FirstHashEntry) /* 145 */ #define Tcl_Flush \ (tclStubsPtr->tcl_Flush) /* 146 */ #define Tcl_FreeResult \ (tclStubsPtr->tcl_FreeResult) /* 147 */ #define Tcl_GetAlias \ (tclStubsPtr->tcl_GetAlias) /* 148 */ #define Tcl_GetAliasObj \ (tclStubsPtr->tcl_GetAliasObj) /* 149 */ #define Tcl_GetAssocData \ (tclStubsPtr->tcl_GetAssocData) /* 150 */ #define Tcl_GetChannel \ (tclStubsPtr->tcl_GetChannel) /* 151 */ #define Tcl_GetChannelBufferSize \ (tclStubsPtr->tcl_GetChannelBufferSize) /* 152 */ #define Tcl_GetChannelHandle \ (tclStubsPtr->tcl_GetChannelHandle) /* 153 */ #define Tcl_GetChannelInstanceData \ (tclStubsPtr->tcl_GetChannelInstanceData) /* 154 */ #define Tcl_GetChannelMode \ (tclStubsPtr->tcl_GetChannelMode) /* 155 */ #define Tcl_GetChannelName \ (tclStubsPtr->tcl_GetChannelName) /* 156 */ #define Tcl_GetChannelOption \ (tclStubsPtr->tcl_GetChannelOption) /* 157 */ #define Tcl_GetChannelType \ (tclStubsPtr->tcl_GetChannelType) /* 158 */ #define Tcl_GetCommandInfo \ (tclStubsPtr->tcl_GetCommandInfo) /* 159 */ #define Tcl_GetCommandName \ (tclStubsPtr->tcl_GetCommandName) /* 160 */ #define Tcl_GetErrno \ (tclStubsPtr->tcl_GetErrno) /* 161 */ #define Tcl_GetHostName \ (tclStubsPtr->tcl_GetHostName) /* 162 */ #define Tcl_GetInterpPath \ (tclStubsPtr->tcl_GetInterpPath) /* 163 */ #define Tcl_GetMaster \ (tclStubsPtr->tcl_GetMaster) /* 164 */ #define Tcl_GetNameOfExecutable \ (tclStubsPtr->tcl_GetNameOfExecutable) /* 165 */ #define Tcl_GetObjResult \ (tclStubsPtr->tcl_GetObjResult) /* 166 */ #if !defined(__WIN32__) && !defined(MAC_OSX_TCL) /* UNIX */ #define Tcl_GetOpenFile \ (tclStubsPtr->tcl_GetOpenFile) /* 167 */ #endif /* UNIX */ #ifdef MAC_OSX_TCL /* MACOSX */ #define Tcl_GetOpenFile \ (tclStubsPtr->tcl_GetOpenFile) /* 167 */ #endif /* MACOSX */ #define Tcl_GetPathType \ (tclStubsPtr->tcl_GetPathType) /* 168 */ #define Tcl_Gets \ (tclStubsPtr->tcl_Gets) /* 169 */ #define Tcl_GetsObj \ (tclStubsPtr->tcl_GetsObj) /* 170 */ #define Tcl_GetServiceMode \ (tclStubsPtr->tcl_GetServiceMode) /* 171 */ #define Tcl_GetSlave \ (tclStubsPtr->tcl_GetSlave) /* 172 */ #define Tcl_GetStdChannel \ (tclStubsPtr->tcl_GetStdChannel) /* 173 */ #define Tcl_GetStringResult \ (tclStubsPtr->tcl_GetStringResult) /* 174 */ #define Tcl_GetVar \ (tclStubsPtr->tcl_GetVar) /* 175 */ #define Tcl_GetVar2 \ (tclStubsPtr->tcl_GetVar2) /* 176 */ #define Tcl_GlobalEval \ (tclStubsPtr->tcl_GlobalEval) /* 177 */ #define Tcl_GlobalEvalObj \ (tclStubsPtr->tcl_GlobalEvalObj) /* 178 */ #define Tcl_HideCommand \ (tclStubsPtr->tcl_HideCommand) /* 179 */ #define Tcl_Init \ (tclStubsPtr->tcl_Init) /* 180 */ #define Tcl_InitHashTable \ (tclStubsPtr->tcl_InitHashTable) /* 181 */ #define Tcl_InputBlocked \ (tclStubsPtr->tcl_InputBlocked) /* 182 */ #define Tcl_InputBuffered \ (tclStubsPtr->tcl_InputBuffered) /* 183 */ #define Tcl_InterpDeleted \ (tclStubsPtr->tcl_InterpDeleted) /* 184 */ #define Tcl_IsSafe \ (tclStubsPtr->tcl_IsSafe) /* 185 */ #define Tcl_JoinPath \ (tclStubsPtr->tcl_JoinPath) /* 186 */ #define Tcl_LinkVar \ (tclStubsPtr->tcl_LinkVar) /* 187 */ /* Slot 188 is reserved */ #define Tcl_MakeFileChannel \ (tclStubsPtr->tcl_MakeFileChannel) /* 189 */ #define Tcl_MakeSafe \ (tclStubsPtr->tcl_MakeSafe) /* 190 */ #define Tcl_MakeTcpClientChannel \ (tclStubsPtr->tcl_MakeTcpClientChannel) /* 191 */ #define Tcl_Merge \ (tclStubsPtr->tcl_Merge) /* 192 */ #define Tcl_NextHashEntry \ (tclStubsPtr->tcl_NextHashEntry) /* 193 */ #define Tcl_NotifyChannel \ (tclStubsPtr->tcl_NotifyChannel) /* 194 */ #define Tcl_ObjGetVar2 \ (tclStubsPtr->tcl_ObjGetVar2) /* 195 */ #define Tcl_ObjSetVar2 \ (tclStubsPtr->tcl_ObjSetVar2) /* 196 */ #define Tcl_OpenCommandChannel \ (tclStubsPtr->tcl_OpenCommandChannel) /* 197 */ #define Tcl_OpenFileChannel \ (tclStubsPtr->tcl_OpenFileChannel) /* 198 */ #define Tcl_OpenTcpClient \ (tclStubsPtr->tcl_OpenTcpClient) /* 199 */ #define Tcl_OpenTcpServer \ (tclStubsPtr->tcl_OpenTcpServer) /* 200 */ #define Tcl_Preserve \ (tclStubsPtr->tcl_Preserve) /* 201 */ #define Tcl_PrintDouble \ (tclStubsPtr->tcl_PrintDouble) /* 202 */ #define Tcl_PutEnv \ (tclStubsPtr->tcl_PutEnv) /* 203 */ #define Tcl_PosixError \ (tclStubsPtr->tcl_PosixError) /* 204 */ #define Tcl_QueueEvent \ (tclStubsPtr->tcl_QueueEvent) /* 205 */ #define Tcl_Read \ (tclStubsPtr->tcl_Read) /* 206 */ #define Tcl_ReapDetachedProcs \ (tclStubsPtr->tcl_ReapDetachedProcs) /* 207 */ #define Tcl_RecordAndEval \ (tclStubsPtr->tcl_RecordAndEval) /* 208 */ #define Tcl_RecordAndEvalObj \ (tclStubsPtr->tcl_RecordAndEvalObj) /* 209 */ #define Tcl_RegisterChannel \ (tclStubsPtr->tcl_RegisterChannel) /* 210 */ #define Tcl_RegisterObjType \ (tclStubsPtr->tcl_RegisterObjType) /* 211 */ #define Tcl_RegExpCompile \ (tclStubsPtr->tcl_RegExpCompile) /* 212 */ #define Tcl_RegExpExec \ (tclStubsPtr->tcl_RegExpExec) /* 213 */ #define Tcl_RegExpMatch \ (tclStubsPtr->tcl_RegExpMatch) /* 214 */ #define Tcl_RegExpRange \ (tclStubsPtr->tcl_RegExpRange) /* 215 */ #define Tcl_Release \ (tclStubsPtr->tcl_Release) /* 216 */ #define Tcl_ResetResult \ (tclStubsPtr->tcl_ResetResult) /* 217 */ #define Tcl_ScanElement \ (tclStubsPtr->tcl_ScanElement) /* 218 */ #define Tcl_ScanCountedElement \ (tclStubsPtr->tcl_ScanCountedElement) /* 219 */ #define Tcl_SeekOld \ (tclStubsPtr->tcl_SeekOld) /* 220 */ #define Tcl_ServiceAll \ (tclStubsPtr->tcl_ServiceAll) /* 221 */ #define Tcl_ServiceEvent \ (tclStubsPtr->tcl_ServiceEvent) /* 222 */ #define Tcl_SetAssocData \ (tclStubsPtr->tcl_SetAssocData) /* 223 */ #define Tcl_SetChannelBufferSize \ (tclStubsPtr->tcl_SetChannelBufferSize) /* 224 */ #define Tcl_SetChannelOption \ (tclStubsPtr->tcl_SetChannelOption) /* 225 */ #define Tcl_SetCommandInfo \ (tclStubsPtr->tcl_SetCommandInfo) /* 226 */ #define Tcl_SetErrno \ (tclStubsPtr->tcl_SetErrno) /* 227 */ #define Tcl_SetErrorCode \ (tclStubsPtr->tcl_SetErrorCode) /* 228 */ #define Tcl_SetMaxBlockTime \ (tclStubsPtr->tcl_SetMaxBlockTime) /* 229 */ #define Tcl_SetPanicProc \ (tclStubsPtr->tcl_SetPanicProc) /* 230 */ #define Tcl_SetRecursionLimit \ (tclStubsPtr->tcl_SetRecursionLimit) /* 231 */ #define Tcl_SetResult \ (tclStubsPtr->tcl_SetResult) /* 232 */ #define Tcl_SetServiceMode \ (tclStubsPtr->tcl_SetServiceMode) /* 233 */ #define Tcl_SetObjErrorCode \ (tclStubsPtr->tcl_SetObjErrorCode) /* 234 */ #define Tcl_SetObjResult \ (tclStubsPtr->tcl_SetObjResult) /* 235 */ #define Tcl_SetStdChannel \ (tclStubsPtr->tcl_SetStdChannel) /* 236 */ #define Tcl_SetVar \ (tclStubsPtr->tcl_SetVar) /* 237 */ #define Tcl_SetVar2 \ (tclStubsPtr->tcl_SetVar2) /* 238 */ #define Tcl_SignalId \ (tclStubsPtr->tcl_SignalId) /* 239 */ #define Tcl_SignalMsg \ (tclStubsPtr->tcl_SignalMsg) /* 240 */ #define Tcl_SourceRCFile \ (tclStubsPtr->tcl_SourceRCFile) /* 241 */ #define Tcl_SplitList \ (tclStubsPtr->tcl_SplitList) /* 242 */ #define Tcl_SplitPath \ (tclStubsPtr->tcl_SplitPath) /* 243 */ #define Tcl_StaticPackage \ (tclStubsPtr->tcl_StaticPackage) /* 244 */ #define Tcl_StringMatch \ (tclStubsPtr->tcl_StringMatch) /* 245 */ #define Tcl_TellOld \ (tclStubsPtr->tcl_TellOld) /* 246 */ #define Tcl_TraceVar \ (tclStubsPtr->tcl_TraceVar) /* 247 */ #define Tcl_TraceVar2 \ (tclStubsPtr->tcl_TraceVar2) /* 248 */ #define Tcl_TranslateFileName \ (tclStubsPtr->tcl_TranslateFileName) /* 249 */ #define Tcl_Ungets \ (tclStubsPtr->tcl_Ungets) /* 250 */ #define Tcl_UnlinkVar \ (tclStubsPtr->tcl_UnlinkVar) /* 251 */ #define Tcl_UnregisterChannel \ (tclStubsPtr->tcl_UnregisterChannel) /* 252 */ #define Tcl_UnsetVar \ (tclStubsPtr->tcl_UnsetVar) /* 253 */ #define Tcl_UnsetVar2 \ (tclStubsPtr->tcl_UnsetVar2) /* 254 */ #define Tcl_UntraceVar \ (tclStubsPtr->tcl_UntraceVar) /* 255 */ #define Tcl_UntraceVar2 \ (tclStubsPtr->tcl_UntraceVar2) /* 256 */ #define Tcl_UpdateLinkedVar \ (tclStubsPtr->tcl_UpdateLinkedVar) /* 257 */ #define Tcl_UpVar \ (tclStubsPtr->tcl_UpVar) /* 258 */ #define Tcl_UpVar2 \ (tclStubsPtr->tcl_UpVar2) /* 259 */ #define Tcl_VarEval \ (tclStubsPtr->tcl_VarEval) /* 260 */ #define Tcl_VarTraceInfo \ (tclStubsPtr->tcl_VarTraceInfo) /* 261 */ #define Tcl_VarTraceInfo2 \ (tclStubsPtr->tcl_VarTraceInfo2) /* 262 */ #define Tcl_Write \ (tclStubsPtr->tcl_Write) /* 263 */ #define Tcl_WrongNumArgs \ (tclStubsPtr->tcl_WrongNumArgs) /* 264 */ #define Tcl_DumpActiveMemory \ (tclStubsPtr->tcl_DumpActiveMemory) /* 265 */ #define Tcl_ValidateAllMemory \ (tclStubsPtr->tcl_ValidateAllMemory) /* 266 */ #define Tcl_AppendResultVA \ (tclStubsPtr->tcl_AppendResultVA) /* 267 */ #define Tcl_AppendStringsToObjVA \ (tclStubsPtr->tcl_AppendStringsToObjVA) /* 268 */ #define Tcl_HashStats \ (tclStubsPtr->tcl_HashStats) /* 269 */ #define Tcl_ParseVar \ (tclStubsPtr->tcl_ParseVar) /* 270 */ #define Tcl_PkgPresent \ (tclStubsPtr->tcl_PkgPresent) /* 271 */ #define Tcl_PkgPresentEx \ (tclStubsPtr->tcl_PkgPresentEx) /* 272 */ #define Tcl_PkgProvide \ (tclStubsPtr->tcl_PkgProvide) /* 273 */ #define Tcl_PkgRequire \ (tclStubsPtr->tcl_PkgRequire) /* 274 */ #define Tcl_SetErrorCodeVA \ (tclStubsPtr->tcl_SetErrorCodeVA) /* 275 */ #define Tcl_VarEvalVA \ (tclStubsPtr->tcl_VarEvalVA) /* 276 */ #define Tcl_WaitPid \ (tclStubsPtr->tcl_WaitPid) /* 277 */ #define Tcl_PanicVA \ (tclStubsPtr->tcl_PanicVA) /* 278 */ #define Tcl_GetVersion \ (tclStubsPtr->tcl_GetVersion) /* 279 */ #define Tcl_InitMemory \ (tclStubsPtr->tcl_InitMemory) /* 280 */ #define Tcl_StackChannel \ (tclStubsPtr->tcl_StackChannel) /* 281 */ #define Tcl_UnstackChannel \ (tclStubsPtr->tcl_UnstackChannel) /* 282 */ #define Tcl_GetStackedChannel \ (tclStubsPtr->tcl_GetStackedChannel) /* 283 */ #define Tcl_SetMainLoop \ (tclStubsPtr->tcl_SetMainLoop) /* 284 */ /* Slot 285 is reserved */ #define Tcl_AppendObjToObj \ (tclStubsPtr->tcl_AppendObjToObj) /* 286 */ #define Tcl_CreateEncoding \ (tclStubsPtr->tcl_CreateEncoding) /* 287 */ #define Tcl_CreateThreadExitHandler \ (tclStubsPtr->tcl_CreateThreadExitHandler) /* 288 */ #define Tcl_DeleteThreadExitHandler \ (tclStubsPtr->tcl_DeleteThreadExitHandler) /* 289 */ #define Tcl_DiscardResult \ (tclStubsPtr->tcl_DiscardResult) /* 290 */ #define Tcl_EvalEx \ (tclStubsPtr->tcl_EvalEx) /* 291 */ #define Tcl_EvalObjv \ (tclStubsPtr->tcl_EvalObjv) /* 292 */ #define Tcl_EvalObjEx \ (tclStubsPtr->tcl_EvalObjEx) /* 293 */ #define Tcl_ExitThread \ (tclStubsPtr->tcl_ExitThread) /* 294 */ #define Tcl_ExternalToUtf \ (tclStubsPtr->tcl_ExternalToUtf) /* 295 */ #define Tcl_ExternalToUtfDString \ (tclStubsPtr->tcl_ExternalToUtfDString) /* 296 */ #define Tcl_FinalizeThread \ (tclStubsPtr->tcl_FinalizeThread) /* 297 */ #define Tcl_FinalizeNotifier \ (tclStubsPtr->tcl_FinalizeNotifier) /* 298 */ #define Tcl_FreeEncoding \ (tclStubsPtr->tcl_FreeEncoding) /* 299 */ #define Tcl_GetCurrentThread \ (tclStubsPtr->tcl_GetCurrentThread) /* 300 */ #define Tcl_GetEncoding \ (tclStubsPtr->tcl_GetEncoding) /* 301 */ #define Tcl_GetEncodingName \ (tclStubsPtr->tcl_GetEncodingName) /* 302 */ #define Tcl_GetEncodingNames \ (tclStubsPtr->tcl_GetEncodingNames) /* 303 */ #define Tcl_GetIndexFromObjStruct \ (tclStubsPtr->tcl_GetIndexFromObjStruct) /* 304 */ #define Tcl_GetThreadData \ (tclStubsPtr->tcl_GetThreadData) /* 305 */ #define Tcl_GetVar2Ex \ (tclStubsPtr->tcl_GetVar2Ex) /* 306 */ #define Tcl_InitNotifier \ (tclStubsPtr->tcl_InitNotifier) /* 307 */ #define Tcl_MutexLock \ (tclStubsPtr->tcl_MutexLock) /* 308 */ #define Tcl_MutexUnlock \ (tclStubsPtr->tcl_MutexUnlock) /* 309 */ #define Tcl_ConditionNotify \ (tclStubsPtr->tcl_ConditionNotify) /* 310 */ #define Tcl_ConditionWait \ (tclStubsPtr->tcl_ConditionWait) /* 311 */ #define Tcl_NumUtfChars \ (tclStubsPtr->tcl_NumUtfChars) /* 312 */ #define Tcl_ReadChars \ (tclStubsPtr->tcl_ReadChars) /* 313 */ #define Tcl_RestoreResult \ (tclStubsPtr->tcl_RestoreResult) /* 314 */ #define Tcl_SaveResult \ (tclStubsPtr->tcl_SaveResult) /* 315 */ #define Tcl_SetSystemEncoding \ (tclStubsPtr->tcl_SetSystemEncoding) /* 316 */ #define Tcl_SetVar2Ex \ (tclStubsPtr->tcl_SetVar2Ex) /* 317 */ #define Tcl_ThreadAlert \ (tclStubsPtr->tcl_ThreadAlert) /* 318 */ #define Tcl_ThreadQueueEvent \ (tclStubsPtr->tcl_ThreadQueueEvent) /* 319 */ #define Tcl_UniCharAtIndex \ (tclStubsPtr->tcl_UniCharAtIndex) /* 320 */ #define Tcl_UniCharToLower \ (tclStubsPtr->tcl_UniCharToLower) /* 321 */ #define Tcl_UniCharToTitle \ (tclStubsPtr->tcl_UniCharToTitle) /* 322 */ #define Tcl_UniCharToUpper \ (tclStubsPtr->tcl_UniCharToUpper) /* 323 */ #define Tcl_UniCharToUtf \ (tclStubsPtr->tcl_UniCharToUtf) /* 324 */ #define Tcl_UtfAtIndex \ (tclStubsPtr->tcl_UtfAtIndex) /* 325 */ #define Tcl_UtfCharComplete \ (tclStubsPtr->tcl_UtfCharComplete) /* 326 */ #define Tcl_UtfBackslash \ (tclStubsPtr->tcl_UtfBackslash) /* 327 */ #define Tcl_UtfFindFirst \ (tclStubsPtr->tcl_UtfFindFirst) /* 328 */ #define Tcl_UtfFindLast \ (tclStubsPtr->tcl_UtfFindLast) /* 329 */ #define Tcl_UtfNext \ (tclStubsPtr->tcl_UtfNext) /* 330 */ #define Tcl_UtfPrev \ (tclStubsPtr->tcl_UtfPrev) /* 331 */ #define Tcl_UtfToExternal \ (tclStubsPtr->tcl_UtfToExternal) /* 332 */ #define Tcl_UtfToExternalDString \ (tclStubsPtr->tcl_UtfToExternalDString) /* 333 */ #define Tcl_UtfToLower \ (tclStubsPtr->tcl_UtfToLower) /* 334 */ #define Tcl_UtfToTitle \ (tclStubsPtr->tcl_UtfToTitle) /* 335 */ #define Tcl_UtfToUniChar \ (tclStubsPtr->tcl_UtfToUniChar) /* 336 */ #define Tcl_UtfToUpper \ (tclStubsPtr->tcl_UtfToUpper) /* 337 */ #define Tcl_WriteChars \ (tclStubsPtr->tcl_WriteChars) /* 338 */ #define Tcl_WriteObj \ (tclStubsPtr->tcl_WriteObj) /* 339 */ #define Tcl_GetString \ (tclStubsPtr->tcl_GetString) /* 340 */ #define Tcl_GetDefaultEncodingDir \ (tclStubsPtr->tcl_GetDefaultEncodingDir) /* 341 */ #define Tcl_SetDefaultEncodingDir \ (tclStubsPtr->tcl_SetDefaultEncodingDir) /* 342 */ #define Tcl_AlertNotifier \ (tclStubsPtr->tcl_AlertNotifier) /* 343 */ #define Tcl_ServiceModeHook \ (tclStubsPtr->tcl_ServiceModeHook) /* 344 */ #define Tcl_UniCharIsAlnum \ (tclStubsPtr->tcl_UniCharIsAlnum) /* 345 */ #define Tcl_UniCharIsAlpha \ (tclStubsPtr->tcl_UniCharIsAlpha) /* 346 */ #define Tcl_UniCharIsDigit \ (tclStubsPtr->tcl_UniCharIsDigit) /* 347 */ #define Tcl_UniCharIsLower \ (tclStubsPtr->tcl_UniCharIsLower) /* 348 */ #define Tcl_UniCharIsSpace \ (tclStubsPtr->tcl_UniCharIsSpace) /* 349 */ #define Tcl_UniCharIsUpper \ (tclStubsPtr->tcl_UniCharIsUpper) /* 350 */ #define Tcl_UniCharIsWordChar \ (tclStubsPtr->tcl_UniCharIsWordChar) /* 351 */ #define Tcl_UniCharLen \ (tclStubsPtr->tcl_UniCharLen) /* 352 */ #define Tcl_UniCharNcmp \ (tclStubsPtr->tcl_UniCharNcmp) /* 353 */ #define Tcl_UniCharToUtfDString \ (tclStubsPtr->tcl_UniCharToUtfDString) /* 354 */ #define Tcl_UtfToUniCharDString \ (tclStubsPtr->tcl_UtfToUniCharDString) /* 355 */ #define Tcl_GetRegExpFromObj \ (tclStubsPtr->tcl_GetRegExpFromObj) /* 356 */ #define Tcl_EvalTokens \ (tclStubsPtr->tcl_EvalTokens) /* 357 */ #define Tcl_FreeParse \ (tclStubsPtr->tcl_FreeParse) /* 358 */ #define Tcl_LogCommandInfo \ (tclStubsPtr->tcl_LogCommandInfo) /* 359 */ #define Tcl_ParseBraces \ (tclStubsPtr->tcl_ParseBraces) /* 360 */ #define Tcl_ParseCommand \ (tclStubsPtr->tcl_ParseCommand) /* 361 */ #define Tcl_ParseExpr \ (tclStubsPtr->tcl_ParseExpr) /* 362 */ #define Tcl_ParseQuotedString \ (tclStubsPtr->tcl_ParseQuotedString) /* 363 */ #define Tcl_ParseVarName \ (tclStubsPtr->tcl_ParseVarName) /* 364 */ #define Tcl_GetCwd \ (tclStubsPtr->tcl_GetCwd) /* 365 */ #define Tcl_Chdir \ (tclStubsPtr->tcl_Chdir) /* 366 */ #define Tcl_Access \ (tclStubsPtr->tcl_Access) /* 367 */ #define Tcl_Stat \ (tclStubsPtr->tcl_Stat) /* 368 */ #define Tcl_UtfNcmp \ (tclStubsPtr->tcl_UtfNcmp) /* 369 */ #define Tcl_UtfNcasecmp \ (tclStubsPtr->tcl_UtfNcasecmp) /* 370 */ #define Tcl_StringCaseMatch \ (tclStubsPtr->tcl_StringCaseMatch) /* 371 */ #define Tcl_UniCharIsControl \ (tclStubsPtr->tcl_UniCharIsControl) /* 372 */ #define Tcl_UniCharIsGraph \ (tclStubsPtr->tcl_UniCharIsGraph) /* 373 */ #define Tcl_UniCharIsPrint \ (tclStubsPtr->tcl_UniCharIsPrint) /* 374 */ #define Tcl_UniCharIsPunct \ (tclStubsPtr->tcl_UniCharIsPunct) /* 375 */ #define Tcl_RegExpExecObj \ (tclStubsPtr->tcl_RegExpExecObj) /* 376 */ #define Tcl_RegExpGetInfo \ (tclStubsPtr->tcl_RegExpGetInfo) /* 377 */ #define Tcl_NewUnicodeObj \ (tclStubsPtr->tcl_NewUnicodeObj) /* 378 */ #define Tcl_SetUnicodeObj \ (tclStubsPtr->tcl_SetUnicodeObj) /* 379 */ #define Tcl_GetCharLength \ (tclStubsPtr->tcl_GetCharLength) /* 380 */ #define Tcl_GetUniChar \ (tclStubsPtr->tcl_GetUniChar) /* 381 */ #define Tcl_GetUnicode \ (tclStubsPtr->tcl_GetUnicode) /* 382 */ #define Tcl_GetRange \ (tclStubsPtr->tcl_GetRange) /* 383 */ #define Tcl_AppendUnicodeToObj \ (tclStubsPtr->tcl_AppendUnicodeToObj) /* 384 */ #define Tcl_RegExpMatchObj \ (tclStubsPtr->tcl_RegExpMatchObj) /* 385 */ #define Tcl_SetNotifier \ (tclStubsPtr->tcl_SetNotifier) /* 386 */ #define Tcl_GetAllocMutex \ (tclStubsPtr->tcl_GetAllocMutex) /* 387 */ #define Tcl_GetChannelNames \ (tclStubsPtr->tcl_GetChannelNames) /* 388 */ #define Tcl_GetChannelNamesEx \ (tclStubsPtr->tcl_GetChannelNamesEx) /* 389 */ #define Tcl_ProcObjCmd \ (tclStubsPtr->tcl_ProcObjCmd) /* 390 */ #define Tcl_ConditionFinalize \ (tclStubsPtr->tcl_ConditionFinalize) /* 391 */ #define Tcl_MutexFinalize \ (tclStubsPtr->tcl_MutexFinalize) /* 392 */ #define Tcl_CreateThread \ (tclStubsPtr->tcl_CreateThread) /* 393 */ #define Tcl_ReadRaw \ (tclStubsPtr->tcl_ReadRaw) /* 394 */ #define Tcl_WriteRaw \ (tclStubsPtr->tcl_WriteRaw) /* 395 */ #define Tcl_GetTopChannel \ (tclStubsPtr->tcl_GetTopChannel) /* 396 */ #define Tcl_ChannelBuffered \ (tclStubsPtr->tcl_ChannelBuffered) /* 397 */ #define Tcl_ChannelName \ (tclStubsPtr->tcl_ChannelName) /* 398 */ #define Tcl_ChannelVersion \ (tclStubsPtr->tcl_ChannelVersion) /* 399 */ #define Tcl_ChannelBlockModeProc \ (tclStubsPtr->tcl_ChannelBlockModeProc) /* 400 */ #define Tcl_ChannelCloseProc \ (tclStubsPtr->tcl_ChannelCloseProc) /* 401 */ #define Tcl_ChannelClose2Proc \ (tclStubsPtr->tcl_ChannelClose2Proc) /* 402 */ #define Tcl_ChannelInputProc \ (tclStubsPtr->tcl_ChannelInputProc) /* 403 */ #define Tcl_ChannelOutputProc \ (tclStubsPtr->tcl_ChannelOutputProc) /* 404 */ #define Tcl_ChannelSeekProc \ (tclStubsPtr->tcl_ChannelSeekProc) /* 405 */ #define Tcl_ChannelSetOptionProc \ (tclStubsPtr->tcl_ChannelSetOptionProc) /* 406 */ #define Tcl_ChannelGetOptionProc \ (tclStubsPtr->tcl_ChannelGetOptionProc) /* 407 */ #define Tcl_ChannelWatchProc \ (tclStubsPtr->tcl_ChannelWatchProc) /* 408 */ #define Tcl_ChannelGetHandleProc \ (tclStubsPtr->tcl_ChannelGetHandleProc) /* 409 */ #define Tcl_ChannelFlushProc \ (tclStubsPtr->tcl_ChannelFlushProc) /* 410 */ #define Tcl_ChannelHandlerProc \ (tclStubsPtr->tcl_ChannelHandlerProc) /* 411 */ #define Tcl_JoinThread \ (tclStubsPtr->tcl_JoinThread) /* 412 */ #define Tcl_IsChannelShared \ (tclStubsPtr->tcl_IsChannelShared) /* 413 */ #define Tcl_IsChannelRegistered \ (tclStubsPtr->tcl_IsChannelRegistered) /* 414 */ #define Tcl_CutChannel \ (tclStubsPtr->tcl_CutChannel) /* 415 */ #define Tcl_SpliceChannel \ (tclStubsPtr->tcl_SpliceChannel) /* 416 */ #define Tcl_ClearChannelHandlers \ (tclStubsPtr->tcl_ClearChannelHandlers) /* 417 */ #define Tcl_IsChannelExisting \ (tclStubsPtr->tcl_IsChannelExisting) /* 418 */ #define Tcl_UniCharNcasecmp \ (tclStubsPtr->tcl_UniCharNcasecmp) /* 419 */ #define Tcl_UniCharCaseMatch \ (tclStubsPtr->tcl_UniCharCaseMatch) /* 420 */ #define Tcl_FindHashEntry \ (tclStubsPtr->tcl_FindHashEntry) /* 421 */ #define Tcl_CreateHashEntry \ (tclStubsPtr->tcl_CreateHashEntry) /* 422 */ #define Tcl_InitCustomHashTable \ (tclStubsPtr->tcl_InitCustomHashTable) /* 423 */ #define Tcl_InitObjHashTable \ (tclStubsPtr->tcl_InitObjHashTable) /* 424 */ #define Tcl_CommandTraceInfo \ (tclStubsPtr->tcl_CommandTraceInfo) /* 425 */ #define Tcl_TraceCommand \ (tclStubsPtr->tcl_TraceCommand) /* 426 */ #define Tcl_UntraceCommand \ (tclStubsPtr->tcl_UntraceCommand) /* 427 */ #define Tcl_AttemptAlloc \ (tclStubsPtr->tcl_AttemptAlloc) /* 428 */ #define Tcl_AttemptDbCkalloc \ (tclStubsPtr->tcl_AttemptDbCkalloc) /* 429 */ #define Tcl_AttemptRealloc \ (tclStubsPtr->tcl_AttemptRealloc) /* 430 */ #define Tcl_AttemptDbCkrealloc \ (tclStubsPtr->tcl_AttemptDbCkrealloc) /* 431 */ #define Tcl_AttemptSetObjLength \ (tclStubsPtr->tcl_AttemptSetObjLength) /* 432 */ #define Tcl_GetChannelThread \ (tclStubsPtr->tcl_GetChannelThread) /* 433 */ #define Tcl_GetUnicodeFromObj \ (tclStubsPtr->tcl_GetUnicodeFromObj) /* 434 */ #define Tcl_GetMathFuncInfo \ (tclStubsPtr->tcl_GetMathFuncInfo) /* 435 */ #define Tcl_ListMathFuncs \ (tclStubsPtr->tcl_ListMathFuncs) /* 436 */ #define Tcl_SubstObj \ (tclStubsPtr->tcl_SubstObj) /* 437 */ #define Tcl_DetachChannel \ (tclStubsPtr->tcl_DetachChannel) /* 438 */ #define Tcl_IsStandardChannel \ (tclStubsPtr->tcl_IsStandardChannel) /* 439 */ #define Tcl_FSCopyFile \ (tclStubsPtr->tcl_FSCopyFile) /* 440 */ #define Tcl_FSCopyDirectory \ (tclStubsPtr->tcl_FSCopyDirectory) /* 441 */ #define Tcl_FSCreateDirectory \ (tclStubsPtr->tcl_FSCreateDirectory) /* 442 */ #define Tcl_FSDeleteFile \ (tclStubsPtr->tcl_FSDeleteFile) /* 443 */ #define Tcl_FSLoadFile \ (tclStubsPtr->tcl_FSLoadFile) /* 444 */ #define Tcl_FSMatchInDirectory \ (tclStubsPtr->tcl_FSMatchInDirectory) /* 445 */ #define Tcl_FSLink \ (tclStubsPtr->tcl_FSLink) /* 446 */ #define Tcl_FSRemoveDirectory \ (tclStubsPtr->tcl_FSRemoveDirectory) /* 447 */ #define Tcl_FSRenameFile \ (tclStubsPtr->tcl_FSRenameFile) /* 448 */ #define Tcl_FSLstat \ (tclStubsPtr->tcl_FSLstat) /* 449 */ #define Tcl_FSUtime \ (tclStubsPtr->tcl_FSUtime) /* 450 */ #define Tcl_FSFileAttrsGet \ (tclStubsPtr->tcl_FSFileAttrsGet) /* 451 */ #define Tcl_FSFileAttrsSet \ (tclStubsPtr->tcl_FSFileAttrsSet) /* 452 */ #define Tcl_FSFileAttrStrings \ (tclStubsPtr->tcl_FSFileAttrStrings) /* 453 */ #define Tcl_FSStat \ (tclStubsPtr->tcl_FSStat) /* 454 */ #define Tcl_FSAccess \ (tclStubsPtr->tcl_FSAccess) /* 455 */ #define Tcl_FSOpenFileChannel \ (tclStubsPtr->tcl_FSOpenFileChannel) /* 456 */ #define Tcl_FSGetCwd \ (tclStubsPtr->tcl_FSGetCwd) /* 457 */ #define Tcl_FSChdir \ (tclStubsPtr->tcl_FSChdir) /* 458 */ #define Tcl_FSConvertToPathType \ (tclStubsPtr->tcl_FSConvertToPathType) /* 459 */ #define Tcl_FSJoinPath \ (tclStubsPtr->tcl_FSJoinPath) /* 460 */ #define Tcl_FSSplitPath \ (tclStubsPtr->tcl_FSSplitPath) /* 461 */ #define Tcl_FSEqualPaths \ (tclStubsPtr->tcl_FSEqualPaths) /* 462 */ #define Tcl_FSGetNormalizedPath \ (tclStubsPtr->tcl_FSGetNormalizedPath) /* 463 */ #define Tcl_FSJoinToPath \ (tclStubsPtr->tcl_FSJoinToPath) /* 464 */ #define Tcl_FSGetInternalRep \ (tclStubsPtr->tcl_FSGetInternalRep) /* 465 */ #define Tcl_FSGetTranslatedPath \ (tclStubsPtr->tcl_FSGetTranslatedPath) /* 466 */ #define Tcl_FSEvalFile \ (tclStubsPtr->tcl_FSEvalFile) /* 467 */ #define Tcl_FSNewNativePath \ (tclStubsPtr->tcl_FSNewNativePath) /* 468 */ #define Tcl_FSGetNativePath \ (tclStubsPtr->tcl_FSGetNativePath) /* 469 */ #define Tcl_FSFileSystemInfo \ (tclStubsPtr->tcl_FSFileSystemInfo) /* 470 */ #define Tcl_FSPathSeparator \ (tclStubsPtr->tcl_FSPathSeparator) /* 471 */ #define Tcl_FSListVolumes \ (tclStubsPtr->tcl_FSListVolumes) /* 472 */ #define Tcl_FSRegister \ (tclStubsPtr->tcl_FSRegister) /* 473 */ #define Tcl_FSUnregister \ (tclStubsPtr->tcl_FSUnregister) /* 474 */ #define Tcl_FSData \ (tclStubsPtr->tcl_FSData) /* 475 */ #define Tcl_FSGetTranslatedStringPath \ (tclStubsPtr->tcl_FSGetTranslatedStringPath) /* 476 */ #define Tcl_FSGetFileSystemForPath \ (tclStubsPtr->tcl_FSGetFileSystemForPath) /* 477 */ #define Tcl_FSGetPathType \ (tclStubsPtr->tcl_FSGetPathType) /* 478 */ #define Tcl_OutputBuffered \ (tclStubsPtr->tcl_OutputBuffered) /* 479 */ #define Tcl_FSMountsChanged \ (tclStubsPtr->tcl_FSMountsChanged) /* 480 */ #define Tcl_EvalTokensStandard \ (tclStubsPtr->tcl_EvalTokensStandard) /* 481 */ #define Tcl_GetTime \ (tclStubsPtr->tcl_GetTime) /* 482 */ #define Tcl_CreateObjTrace \ (tclStubsPtr->tcl_CreateObjTrace) /* 483 */ #define Tcl_GetCommandInfoFromToken \ (tclStubsPtr->tcl_GetCommandInfoFromToken) /* 484 */ #define Tcl_SetCommandInfoFromToken \ (tclStubsPtr->tcl_SetCommandInfoFromToken) /* 485 */ #define Tcl_DbNewWideIntObj \ (tclStubsPtr->tcl_DbNewWideIntObj) /* 486 */ #define Tcl_GetWideIntFromObj \ (tclStubsPtr->tcl_GetWideIntFromObj) /* 487 */ #define Tcl_NewWideIntObj \ (tclStubsPtr->tcl_NewWideIntObj) /* 488 */ #define Tcl_SetWideIntObj \ (tclStubsPtr->tcl_SetWideIntObj) /* 489 */ #define Tcl_AllocStatBuf \ (tclStubsPtr->tcl_AllocStatBuf) /* 490 */ #define Tcl_Seek \ (tclStubsPtr->tcl_Seek) /* 491 */ #define Tcl_Tell \ (tclStubsPtr->tcl_Tell) /* 492 */ #define Tcl_ChannelWideSeekProc \ (tclStubsPtr->tcl_ChannelWideSeekProc) /* 493 */ #define Tcl_DictObjPut \ (tclStubsPtr->tcl_DictObjPut) /* 494 */ #define Tcl_DictObjGet \ (tclStubsPtr->tcl_DictObjGet) /* 495 */ #define Tcl_DictObjRemove \ (tclStubsPtr->tcl_DictObjRemove) /* 496 */ #define Tcl_DictObjSize \ (tclStubsPtr->tcl_DictObjSize) /* 497 */ #define Tcl_DictObjFirst \ (tclStubsPtr->tcl_DictObjFirst) /* 498 */ #define Tcl_DictObjNext \ (tclStubsPtr->tcl_DictObjNext) /* 499 */ #define Tcl_DictObjDone \ (tclStubsPtr->tcl_DictObjDone) /* 500 */ #define Tcl_DictObjPutKeyList \ (tclStubsPtr->tcl_DictObjPutKeyList) /* 501 */ #define Tcl_DictObjRemoveKeyList \ (tclStubsPtr->tcl_DictObjRemoveKeyList) /* 502 */ #define Tcl_NewDictObj \ (tclStubsPtr->tcl_NewDictObj) /* 503 */ #define Tcl_DbNewDictObj \ (tclStubsPtr->tcl_DbNewDictObj) /* 504 */ #define Tcl_RegisterConfig \ (tclStubsPtr->tcl_RegisterConfig) /* 505 */ #define Tcl_CreateNamespace \ (tclStubsPtr->tcl_CreateNamespace) /* 506 */ #define Tcl_DeleteNamespace \ (tclStubsPtr->tcl_DeleteNamespace) /* 507 */ #define Tcl_AppendExportList \ (tclStubsPtr->tcl_AppendExportList) /* 508 */ #define Tcl_Export \ (tclStubsPtr->tcl_Export) /* 509 */ #define Tcl_Import \ (tclStubsPtr->tcl_Import) /* 510 */ #define Tcl_ForgetImport \ (tclStubsPtr->tcl_ForgetImport) /* 511 */ #define Tcl_GetCurrentNamespace \ (tclStubsPtr->tcl_GetCurrentNamespace) /* 512 */ #define Tcl_GetGlobalNamespace \ (tclStubsPtr->tcl_GetGlobalNamespace) /* 513 */ #define Tcl_FindNamespace \ (tclStubsPtr->tcl_FindNamespace) /* 514 */ #define Tcl_FindCommand \ (tclStubsPtr->tcl_FindCommand) /* 515 */ #define Tcl_GetCommandFromObj \ (tclStubsPtr->tcl_GetCommandFromObj) /* 516 */ #define Tcl_GetCommandFullName \ (tclStubsPtr->tcl_GetCommandFullName) /* 517 */ #define Tcl_FSEvalFileEx \ (tclStubsPtr->tcl_FSEvalFileEx) /* 518 */ #define Tcl_SetExitProc \ (tclStubsPtr->tcl_SetExitProc) /* 519 */ #define Tcl_LimitAddHandler \ (tclStubsPtr->tcl_LimitAddHandler) /* 520 */ #define Tcl_LimitRemoveHandler \ (tclStubsPtr->tcl_LimitRemoveHandler) /* 521 */ #define Tcl_LimitReady \ (tclStubsPtr->tcl_LimitReady) /* 522 */ #define Tcl_LimitCheck \ (tclStubsPtr->tcl_LimitCheck) /* 523 */ #define Tcl_LimitExceeded \ (tclStubsPtr->tcl_LimitExceeded) /* 524 */ #define Tcl_LimitSetCommands \ (tclStubsPtr->tcl_LimitSetCommands) /* 525 */ #define Tcl_LimitSetTime \ (tclStubsPtr->tcl_LimitSetTime) /* 526 */ #define Tcl_LimitSetGranularity \ (tclStubsPtr->tcl_LimitSetGranularity) /* 527 */ #define Tcl_LimitTypeEnabled \ (tclStubsPtr->tcl_LimitTypeEnabled) /* 528 */ #define Tcl_LimitTypeExceeded \ (tclStubsPtr->tcl_LimitTypeExceeded) /* 529 */ #define Tcl_LimitTypeSet \ (tclStubsPtr->tcl_LimitTypeSet) /* 530 */ #define Tcl_LimitTypeReset \ (tclStubsPtr->tcl_LimitTypeReset) /* 531 */ #define Tcl_LimitGetCommands \ (tclStubsPtr->tcl_LimitGetCommands) /* 532 */ #define Tcl_LimitGetTime \ (tclStubsPtr->tcl_LimitGetTime) /* 533 */ #define Tcl_LimitGetGranularity \ (tclStubsPtr->tcl_LimitGetGranularity) /* 534 */ #define Tcl_SaveInterpState \ (tclStubsPtr->tcl_SaveInterpState) /* 535 */ #define Tcl_RestoreInterpState \ (tclStubsPtr->tcl_RestoreInterpState) /* 536 */ #define Tcl_DiscardInterpState \ (tclStubsPtr->tcl_DiscardInterpState) /* 537 */ #define Tcl_SetReturnOptions \ (tclStubsPtr->tcl_SetReturnOptions) /* 538 */ #define Tcl_GetReturnOptions \ (tclStubsPtr->tcl_GetReturnOptions) /* 539 */ #define Tcl_IsEnsemble \ (tclStubsPtr->tcl_IsEnsemble) /* 540 */ #define Tcl_CreateEnsemble \ (tclStubsPtr->tcl_CreateEnsemble) /* 541 */ #define Tcl_FindEnsemble \ (tclStubsPtr->tcl_FindEnsemble) /* 542 */ #define Tcl_SetEnsembleSubcommandList \ (tclStubsPtr->tcl_SetEnsembleSubcommandList) /* 543 */ #define Tcl_SetEnsembleMappingDict \ (tclStubsPtr->tcl_SetEnsembleMappingDict) /* 544 */ #define Tcl_SetEnsembleUnknownHandler \ (tclStubsPtr->tcl_SetEnsembleUnknownHandler) /* 545 */ #define Tcl_SetEnsembleFlags \ (tclStubsPtr->tcl_SetEnsembleFlags) /* 546 */ #define Tcl_GetEnsembleSubcommandList \ (tclStubsPtr->tcl_GetEnsembleSubcommandList) /* 547 */ #define Tcl_GetEnsembleMappingDict \ (tclStubsPtr->tcl_GetEnsembleMappingDict) /* 548 */ #define Tcl_GetEnsembleUnknownHandler \ (tclStubsPtr->tcl_GetEnsembleUnknownHandler) /* 549 */ #define Tcl_GetEnsembleFlags \ (tclStubsPtr->tcl_GetEnsembleFlags) /* 550 */ #define Tcl_GetEnsembleNamespace \ (tclStubsPtr->tcl_GetEnsembleNamespace) /* 551 */ #define Tcl_SetTimeProc \ (tclStubsPtr->tcl_SetTimeProc) /* 552 */ #define Tcl_QueryTimeProc \ (tclStubsPtr->tcl_QueryTimeProc) /* 553 */ #define Tcl_ChannelThreadActionProc \ (tclStubsPtr->tcl_ChannelThreadActionProc) /* 554 */ #define Tcl_NewBignumObj \ (tclStubsPtr->tcl_NewBignumObj) /* 555 */ #define Tcl_DbNewBignumObj \ (tclStubsPtr->tcl_DbNewBignumObj) /* 556 */ #define Tcl_SetBignumObj \ (tclStubsPtr->tcl_SetBignumObj) /* 557 */ #define Tcl_GetBignumFromObj \ (tclStubsPtr->tcl_GetBignumFromObj) /* 558 */ #define Tcl_TakeBignumFromObj \ (tclStubsPtr->tcl_TakeBignumFromObj) /* 559 */ #define Tcl_TruncateChannel \ (tclStubsPtr->tcl_TruncateChannel) /* 560 */ #define Tcl_ChannelTruncateProc \ (tclStubsPtr->tcl_ChannelTruncateProc) /* 561 */ #define Tcl_SetChannelErrorInterp \ (tclStubsPtr->tcl_SetChannelErrorInterp) /* 562 */ #define Tcl_GetChannelErrorInterp \ (tclStubsPtr->tcl_GetChannelErrorInterp) /* 563 */ #define Tcl_SetChannelError \ (tclStubsPtr->tcl_SetChannelError) /* 564 */ #define Tcl_GetChannelError \ (tclStubsPtr->tcl_GetChannelError) /* 565 */ #define Tcl_InitBignumFromDouble \ (tclStubsPtr->tcl_InitBignumFromDouble) /* 566 */ #define Tcl_GetNamespaceUnknownHandler \ (tclStubsPtr->tcl_GetNamespaceUnknownHandler) /* 567 */ #define Tcl_SetNamespaceUnknownHandler \ (tclStubsPtr->tcl_SetNamespaceUnknownHandler) /* 568 */ #define Tcl_GetEncodingFromObj \ (tclStubsPtr->tcl_GetEncodingFromObj) /* 569 */ #define Tcl_GetEncodingSearchPath \ (tclStubsPtr->tcl_GetEncodingSearchPath) /* 570 */ #define Tcl_SetEncodingSearchPath \ (tclStubsPtr->tcl_SetEncodingSearchPath) /* 571 */ #define Tcl_GetEncodingNameFromEnvironment \ (tclStubsPtr->tcl_GetEncodingNameFromEnvironment) /* 572 */ #define Tcl_PkgRequireProc \ (tclStubsPtr->tcl_PkgRequireProc) /* 573 */ #define Tcl_AppendObjToErrorInfo \ (tclStubsPtr->tcl_AppendObjToErrorInfo) /* 574 */ #define Tcl_AppendLimitedToObj \ (tclStubsPtr->tcl_AppendLimitedToObj) /* 575 */ #define Tcl_Format \ (tclStubsPtr->tcl_Format) /* 576 */ #define Tcl_AppendFormatToObj \ (tclStubsPtr->tcl_AppendFormatToObj) /* 577 */ #define Tcl_ObjPrintf \ (tclStubsPtr->tcl_ObjPrintf) /* 578 */ #define Tcl_AppendPrintfToObj \ (tclStubsPtr->tcl_AppendPrintfToObj) /* 579 */ #define Tcl_CancelEval \ (tclStubsPtr->tcl_CancelEval) /* 580 */ #define Tcl_Canceled \ (tclStubsPtr->tcl_Canceled) /* 581 */ #define Tcl_CreatePipe \ (tclStubsPtr->tcl_CreatePipe) /* 582 */ #define Tcl_NRCreateCommand \ (tclStubsPtr->tcl_NRCreateCommand) /* 583 */ #define Tcl_NREvalObj \ (tclStubsPtr->tcl_NREvalObj) /* 584 */ #define Tcl_NREvalObjv \ (tclStubsPtr->tcl_NREvalObjv) /* 585 */ #define Tcl_NRCmdSwap \ (tclStubsPtr->tcl_NRCmdSwap) /* 586 */ #define Tcl_NRAddCallback \ (tclStubsPtr->tcl_NRAddCallback) /* 587 */ #define Tcl_NRCallObjProc \ (tclStubsPtr->tcl_NRCallObjProc) /* 588 */ #define Tcl_GetFSDeviceFromStat \ (tclStubsPtr->tcl_GetFSDeviceFromStat) /* 589 */ #define Tcl_GetFSInodeFromStat \ (tclStubsPtr->tcl_GetFSInodeFromStat) /* 590 */ #define Tcl_GetModeFromStat \ (tclStubsPtr->tcl_GetModeFromStat) /* 591 */ #define Tcl_GetLinkCountFromStat \ (tclStubsPtr->tcl_GetLinkCountFromStat) /* 592 */ #define Tcl_GetUserIdFromStat \ (tclStubsPtr->tcl_GetUserIdFromStat) /* 593 */ #define Tcl_GetGroupIdFromStat \ (tclStubsPtr->tcl_GetGroupIdFromStat) /* 594 */ #define Tcl_GetDeviceTypeFromStat \ (tclStubsPtr->tcl_GetDeviceTypeFromStat) /* 595 */ #define Tcl_GetAccessTimeFromStat \ (tclStubsPtr->tcl_GetAccessTimeFromStat) /* 596 */ #define Tcl_GetModificationTimeFromStat \ (tclStubsPtr->tcl_GetModificationTimeFromStat) /* 597 */ #define Tcl_GetChangeTimeFromStat \ (tclStubsPtr->tcl_GetChangeTimeFromStat) /* 598 */ #define Tcl_GetSizeFromStat \ (tclStubsPtr->tcl_GetSizeFromStat) /* 599 */ #define Tcl_GetBlocksFromStat \ (tclStubsPtr->tcl_GetBlocksFromStat) /* 600 */ #define Tcl_GetBlockSizeFromStat \ (tclStubsPtr->tcl_GetBlockSizeFromStat) /* 601 */ #define Tcl_SetEnsembleParameterList \ (tclStubsPtr->tcl_SetEnsembleParameterList) /* 602 */ #define Tcl_GetEnsembleParameterList \ (tclStubsPtr->tcl_GetEnsembleParameterList) /* 603 */ #define Tcl_ParseArgsObjv \ (tclStubsPtr->tcl_ParseArgsObjv) /* 604 */ #define Tcl_GetErrorLine \ (tclStubsPtr->tcl_GetErrorLine) /* 605 */ #define Tcl_SetErrorLine \ (tclStubsPtr->tcl_SetErrorLine) /* 606 */ #define Tcl_TransferResult \ (tclStubsPtr->tcl_TransferResult) /* 607 */ #define Tcl_InterpActive \ (tclStubsPtr->tcl_InterpActive) /* 608 */ #define Tcl_BackgroundException \ (tclStubsPtr->tcl_BackgroundException) /* 609 */ #define Tcl_ZlibDeflate \ (tclStubsPtr->tcl_ZlibDeflate) /* 610 */ #define Tcl_ZlibInflate \ (tclStubsPtr->tcl_ZlibInflate) /* 611 */ #define Tcl_ZlibCRC32 \ (tclStubsPtr->tcl_ZlibCRC32) /* 612 */ #define Tcl_ZlibAdler32 \ (tclStubsPtr->tcl_ZlibAdler32) /* 613 */ #define Tcl_ZlibStreamInit \ (tclStubsPtr->tcl_ZlibStreamInit) /* 614 */ #define Tcl_ZlibStreamGetCommandName \ (tclStubsPtr->tcl_ZlibStreamGetCommandName) /* 615 */ #define Tcl_ZlibStreamEof \ (tclStubsPtr->tcl_ZlibStreamEof) /* 616 */ #define Tcl_ZlibStreamChecksum \ (tclStubsPtr->tcl_ZlibStreamChecksum) /* 617 */ #define Tcl_ZlibStreamPut \ (tclStubsPtr->tcl_ZlibStreamPut) /* 618 */ #define Tcl_ZlibStreamGet \ (tclStubsPtr->tcl_ZlibStreamGet) /* 619 */ #define Tcl_ZlibStreamClose \ (tclStubsPtr->tcl_ZlibStreamClose) /* 620 */ #define Tcl_ZlibStreamReset \ (tclStubsPtr->tcl_ZlibStreamReset) /* 621 */ #define Tcl_SetStartupScript \ (tclStubsPtr->tcl_SetStartupScript) /* 622 */ #define Tcl_GetStartupScript \ (tclStubsPtr->tcl_GetStartupScript) /* 623 */ #define Tcl_CloseEx \ (tclStubsPtr->tcl_CloseEx) /* 624 */ #define Tcl_NRExprObj \ (tclStubsPtr->tcl_NRExprObj) /* 625 */ #define Tcl_NRSubstObj \ (tclStubsPtr->tcl_NRSubstObj) /* 626 */ #define Tcl_LoadFile \ (tclStubsPtr->tcl_LoadFile) /* 627 */ #define Tcl_FindSymbol \ (tclStubsPtr->tcl_FindSymbol) /* 628 */ #define Tcl_FSUnloadFile \ (tclStubsPtr->tcl_FSUnloadFile) /* 629 */ #define Tcl_ZlibStreamSetCompressionDictionary \ (tclStubsPtr->tcl_ZlibStreamSetCompressionDictionary) /* 630 */ #endif /* defined(USE_TCL_STUBS) */ /* !END!: Do not edit above this line. */ #if defined(USE_TCL_STUBS) # undef Tcl_CreateInterp # undef Tcl_FindExecutable # undef Tcl_GetStringResult # undef Tcl_Init # undef Tcl_SetPanicProc # undef Tcl_SetVar # undef Tcl_StaticPackage # undef TclFSGetNativePath # define Tcl_CreateInterp() (tclStubsPtr->tcl_CreateInterp()) # define Tcl_GetStringResult(interp) (tclStubsPtr->tcl_GetStringResult(interp)) # define Tcl_Init(interp) (tclStubsPtr->tcl_Init(interp)) # define Tcl_SetPanicProc(proc) (tclStubsPtr->tcl_SetPanicProc(proc)) # define Tcl_SetVar(interp, varName, newValue, flags) \ (tclStubsPtr->tcl_SetVar(interp, varName, newValue, flags)) #endif #if defined(_WIN32) && defined(UNICODE) # define Tcl_FindExecutable(arg) ((Tcl_FindExecutable)((const char *)(arg))) # define Tcl_MainEx Tcl_MainExW EXTERN void Tcl_MainExW(int argc, wchar_t **argv, Tcl_AppInitProc *appInitProc, Tcl_Interp *interp); #endif #undef TCL_STORAGE_CLASS #define TCL_STORAGE_CLASS DLLIMPORT #endif /* _TCLDECLS */ |
Added compat/tcl-8.6/generic/tclPlatDecls.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* * tclPlatDecls.h -- * * Declarations of platform specific Tcl APIs. * * Copyright (c) 1998-1999 by Scriptics Corporation. * All rights reserved. */ #ifndef _TCLPLATDECLS #define _TCLPLATDECLS #undef TCL_STORAGE_CLASS #ifdef BUILD_tcl # define TCL_STORAGE_CLASS DLLEXPORT #else # ifdef USE_TCL_STUBS # define TCL_STORAGE_CLASS # else # define TCL_STORAGE_CLASS DLLIMPORT # endif #endif /* * WARNING: This file is automatically generated by the tools/genStubs.tcl * script. Any modifications to the function declarations below should be made * in the generic/tcl.decls script. */ /* * TCHAR is needed here for win32, so if it is not defined yet do it here. * This way, we don't need to include <tchar.h> just for one define. */ #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(_TCHAR_DEFINED) # if defined(_UNICODE) typedef wchar_t TCHAR; # else typedef char TCHAR; # endif # define _TCHAR_DEFINED #endif /* !BEGIN!: Do not edit below this line. */ /* * Exported function declarations: */ #if defined(__WIN32__) || defined(__CYGWIN__) /* WIN */ /* 0 */ EXTERN TCHAR * Tcl_WinUtfToTChar(const char *str, int len, Tcl_DString *dsPtr); /* 1 */ EXTERN char * Tcl_WinTCharToUtf(const TCHAR *str, int len, Tcl_DString *dsPtr); #endif /* WIN */ #ifdef MAC_OSX_TCL /* MACOSX */ /* 0 */ EXTERN int Tcl_MacOSXOpenBundleResources(Tcl_Interp *interp, const char *bundleName, int hasResourceFile, int maxPathLen, char *libraryPath); /* 1 */ EXTERN int Tcl_MacOSXOpenVersionedBundleResources( Tcl_Interp *interp, const char *bundleName, const char *bundleVersion, int hasResourceFile, int maxPathLen, char *libraryPath); #endif /* MACOSX */ typedef struct TclPlatStubs { int magic; void *hooks; #if defined(__WIN32__) || defined(__CYGWIN__) /* WIN */ TCHAR * (*tcl_WinUtfToTChar) (const char *str, int len, Tcl_DString *dsPtr); /* 0 */ char * (*tcl_WinTCharToUtf) (const TCHAR *str, int len, Tcl_DString *dsPtr); /* 1 */ #endif /* WIN */ #ifdef MAC_OSX_TCL /* MACOSX */ int (*tcl_MacOSXOpenBundleResources) (Tcl_Interp *interp, const char *bundleName, int hasResourceFile, int maxPathLen, char *libraryPath); /* 0 */ int (*tcl_MacOSXOpenVersionedBundleResources) (Tcl_Interp *interp, const char *bundleName, const char *bundleVersion, int hasResourceFile, int maxPathLen, char *libraryPath); /* 1 */ #endif /* MACOSX */ } TclPlatStubs; #ifdef __cplusplus extern "C" { #endif extern const TclPlatStubs *tclPlatStubsPtr; #ifdef __cplusplus } #endif #if defined(USE_TCL_STUBS) /* * Inline function declarations: */ #if defined(__WIN32__) || defined(__CYGWIN__) /* WIN */ #define Tcl_WinUtfToTChar \ (tclPlatStubsPtr->tcl_WinUtfToTChar) /* 0 */ #define Tcl_WinTCharToUtf \ (tclPlatStubsPtr->tcl_WinTCharToUtf) /* 1 */ #endif /* WIN */ #ifdef MAC_OSX_TCL /* MACOSX */ #define Tcl_MacOSXOpenBundleResources \ (tclPlatStubsPtr->tcl_MacOSXOpenBundleResources) /* 0 */ #define Tcl_MacOSXOpenVersionedBundleResources \ (tclPlatStubsPtr->tcl_MacOSXOpenVersionedBundleResources) /* 1 */ #endif /* MACOSX */ #endif /* defined(USE_TCL_STUBS) */ /* !END!: Do not edit above this line. */ #undef TCL_STORAGE_CLASS #define TCL_STORAGE_CLASS DLLIMPORT #endif /* _TCLPLATDECLS */ |
Added compat/zlib/CMakeLists.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 | cmake_minimum_required(VERSION 2.4.4...3.15.0) set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON) project(zlib C) set(VERSION "1.3.1") option(ZLIB_BUILD_EXAMPLES "Enable Zlib Examples" ON) set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables") set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation directory for libraries") set(INSTALL_INC_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "Installation directory for headers") set(INSTALL_MAN_DIR "${CMAKE_INSTALL_PREFIX}/share/man" CACHE PATH "Installation directory for manual pages") set(INSTALL_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH "Installation directory for pkgconfig (.pc) files") include(CheckTypeSize) include(CheckFunctionExists) include(CheckIncludeFile) include(CheckCSourceCompiles) enable_testing() check_include_file(sys/types.h HAVE_SYS_TYPES_H) check_include_file(stdint.h HAVE_STDINT_H) check_include_file(stddef.h HAVE_STDDEF_H) # # Check to see if we have large file support # set(CMAKE_REQUIRED_DEFINITIONS -D_LARGEFILE64_SOURCE=1) # We add these other definitions here because CheckTypeSize.cmake # in CMake 2.4.x does not automatically do so and we want # compatibility with CMake 2.4.x. if(HAVE_SYS_TYPES_H) list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_SYS_TYPES_H) endif() if(HAVE_STDINT_H) list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_STDINT_H) endif() if(HAVE_STDDEF_H) list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_STDDEF_H) endif() check_type_size(off64_t OFF64_T) if(HAVE_OFF64_T) add_definitions(-D_LARGEFILE64_SOURCE=1) endif() set(CMAKE_REQUIRED_DEFINITIONS) # clear variable # # Check for fseeko # check_function_exists(fseeko HAVE_FSEEKO) if(NOT HAVE_FSEEKO) add_definitions(-DNO_FSEEKO) endif() # # Check for unistd.h # check_include_file(unistd.h Z_HAVE_UNISTD_H) if(MSVC) set(CMAKE_DEBUG_POSTFIX "d") add_definitions(-D_CRT_SECURE_NO_DEPRECATE) add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) endif() if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) # If we're doing an out of source build and the user has a zconf.h # in their source tree... if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h) message(STATUS "Renaming") message(STATUS " ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h") message(STATUS "to 'zconf.h.included' because this file is included with zlib") message(STATUS "but CMake generates it automatically in the build directory.") file(RENAME ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h.included) endif() endif() set(ZLIB_PC ${CMAKE_CURRENT_BINARY_DIR}/zlib.pc) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/zlib.pc.cmakein ${ZLIB_PC} @ONLY) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/zconf.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/zconf.h @ONLY) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}) #============================================================================ # zlib #============================================================================ set(ZLIB_PUBLIC_HDRS ${CMAKE_CURRENT_BINARY_DIR}/zconf.h zlib.h ) set(ZLIB_PRIVATE_HDRS crc32.h deflate.h gzguts.h inffast.h inffixed.h inflate.h inftrees.h trees.h zutil.h ) set(ZLIB_SRCS adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c inflate.c infback.c inftrees.c inffast.c trees.c uncompr.c zutil.c ) if(NOT MINGW) set(ZLIB_DLL_SRCS win32/zlib1.rc # If present will override custom build rule below. ) endif() # parse the full version number from zlib.h and include in ZLIB_FULL_VERSION file(READ ${CMAKE_CURRENT_SOURCE_DIR}/zlib.h _zlib_h_contents) string(REGEX REPLACE ".*#define[ \t]+ZLIB_VERSION[ \t]+\"([-0-9A-Za-z.]+)\".*" "\\1" ZLIB_FULL_VERSION ${_zlib_h_contents}) if(MINGW) # This gets us DLL resource information when compiling on MinGW. if(NOT CMAKE_RC_COMPILER) set(CMAKE_RC_COMPILER windres.exe) endif() add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj COMMAND ${CMAKE_RC_COMPILER} -D GCC_WINDRES -I ${CMAKE_CURRENT_SOURCE_DIR} -I ${CMAKE_CURRENT_BINARY_DIR} -o ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj -i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zlib1.rc) set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj) endif(MINGW) add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) target_include_directories(zlib PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) target_include_directories(zlibstatic PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL) set_target_properties(zlib PROPERTIES SOVERSION 1) if(NOT CYGWIN) # This property causes shared libraries on Linux to have the full version # encoded into their final filename. We disable this on Cygwin because # it causes cygz-${ZLIB_FULL_VERSION}.dll to be created when cygz.dll # seems to be the default. # # This has no effect with MSVC, on that platform the version info for # the DLL comes from the resource file win32/zlib1.rc set_target_properties(zlib PROPERTIES VERSION ${ZLIB_FULL_VERSION}) endif() if(UNIX) # On unix-like platforms the library is almost always called libz set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z) if(NOT APPLE AND NOT(CMAKE_SYSTEM_NAME STREQUAL AIX)) set_target_properties(zlib PROPERTIES LINK_FLAGS "-Wl,--version-script,\"${CMAKE_CURRENT_SOURCE_DIR}/zlib.map\"") endif() elseif(BUILD_SHARED_LIBS AND WIN32) # Creates zlib1.dll when building shared library version set_target_properties(zlib PROPERTIES SUFFIX "1.dll") endif() if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL ) install(TARGETS zlib zlibstatic RUNTIME DESTINATION "${INSTALL_BIN_DIR}" ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" LIBRARY DESTINATION "${INSTALL_LIB_DIR}" ) endif() if(NOT SKIP_INSTALL_HEADERS AND NOT SKIP_INSTALL_ALL ) install(FILES ${ZLIB_PUBLIC_HDRS} DESTINATION "${INSTALL_INC_DIR}") endif() if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL ) install(FILES zlib.3 DESTINATION "${INSTALL_MAN_DIR}/man3") endif() if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL ) install(FILES ${ZLIB_PC} DESTINATION "${INSTALL_PKGCONFIG_DIR}") endif() #============================================================================ # Example binaries #============================================================================ if(ZLIB_BUILD_EXAMPLES) add_executable(example test/example.c) target_link_libraries(example zlib) add_test(example example) add_executable(minigzip test/minigzip.c) target_link_libraries(minigzip zlib) if(HAVE_OFF64_T) add_executable(example64 test/example.c) target_link_libraries(example64 zlib) set_target_properties(example64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64") add_test(example64 example64) add_executable(minigzip64 test/minigzip.c) target_link_libraries(minigzip64 zlib) set_target_properties(minigzip64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64") endif() endif() |
Added compat/zlib/ChangeLog.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ChangeLog file for zlib Changes in 1.3.1 (22 Jan 2024) - Reject overflows of zip header fields in minizip - Fix bug in inflateSync() for data held in bit buffer - Add LIT_MEM define to use more memory for a small deflate speedup - Fix decision on the emission of Zip64 end records in minizip - Add bounds checking to ERR_MSG() macro, used by zError() - Neutralize zip file traversal attacks in miniunz - Fix a bug in ZLIB_DEBUG compiles in check_match() - Various portability and appearance improvements Changes in 1.3 (18 Aug 2023) - Remove K&R function definitions and zlib2ansi - Fix bug in deflateBound() for level 0 and memLevel 9 - Fix bug when gzungetc() is used immediately after gzopen() - Fix bug when using gzflush() with a very small buffer - Fix crash when gzsetparams() attempted for transparent write - Fix test/example.c to work with FORCE_STORED - Rewrite of zran in examples (see zran.c version history) - Fix minizip to allow it to open an empty zip file - Fix reading disk number start on zip64 files in minizip - Fix logic error in minizip argument processing - Add minizip testing to Makefile - Read multiple bytes instead of byte-by-byte in minizip unzip.c - Add memory sanitizer to configure (--memory) - Various portability improvements - Various documentation improvements - Various spelling and typo corrections Changes in 1.2.13 (13 Oct 2022) - Fix configure issue that discarded provided CC definition - Correct incorrect inputs provided to the CRC functions - Repair prototypes and exporting of new CRC functions - Fix inflateBack to detect invalid input with distances too far - Have infback() deliver all of the available output up to any error - Fix a bug when getting a gzip header extra field with inflate() - Fix bug in block type selection when Z_FIXED used - Tighten deflateBound bounds - Remove deleted assembler code references - Various portability and appearance improvements Changes in 1.2.12 (27 Mar 2022) - Cygwin does not have _wopen(), so do not create gzopen_w() there - Permit a deflateParams() parameter change as soon as possible - Limit hash table inserts after switch from stored deflate - Fix bug when window full in deflate_stored() - Fix CLEAR_HASH macro to be usable as a single statement - Avoid a conversion error in gzseek when off_t type too small - Have Makefile return non-zero error code on test failure - Avoid some conversion warnings in gzread.c and gzwrite.c - Update use of errno for newer Windows CE versions - Small speedup to inflate [psumbera] - Return an error if the gzputs string length can't fit in an int - Add address checking in clang to -w option of configure - Don't compute check value for raw inflate if asked to validate - Handle case where inflateSync used when header never processed - Avoid the use of ptrdiff_t - Avoid an undefined behavior of memcpy() in gzappend() - Avoid undefined behaviors of memcpy() in gz*printf() - Avoid an undefined behavior of memcpy() in _tr_stored_block() - Make the names in functions declarations identical to definitions - Remove old assembler code in which bugs have manifested - Fix deflateEnd() to not report an error at start of raw deflate - Add legal disclaimer to README - Emphasize the need to continue decompressing gzip members - Correct the initialization requirements for deflateInit2() - Fix a bug that can crash deflate on some input when using Z_FIXED - Assure that the number of bits for deflatePrime() is valid - Use a structure to make globals in enough.c evident - Use a macro for the printf format of big_t in enough.c - Clean up code style in enough.c, update version - Use inline function instead of macro for index in enough.c - Clarify that prefix codes are counted in enough.c - Show all the codes for the maximum tables size in enough.c - Add gznorm.c example, which normalizes gzip files - Fix the zran.c example to work on a multiple-member gzip file - Add tables for crc32_combine(), to speed it up by a factor of 200 - Add crc32_combine_gen() and crc32_combine_op() for fast combines - Speed up software CRC-32 computation by a factor of 1.5 to 3 - Use atomic test and set, if available, for dynamic CRC tables - Don't bother computing check value after successful inflateSync() - Correct comment in crc32.c - Add use of the ARMv8 crc32 instructions when requested - Use ARM crc32 instructions if the ARM architecture has them - Explicitly note that the 32-bit check values are 32 bits - Avoid adding empty gzip member after gzflush with Z_FINISH - Fix memory leak on error in gzlog.c - Fix error in comment on the polynomial representation of a byte - Clarify gz* function interfaces, referring to parameter names - Change macro name in inflate.c to avoid collision in VxWorks - Correct typo in blast.c - Improve portability of contrib/minizip - Fix indentation in minizip's zip.c - Replace black/white with allow/block. (theresa-m) - minizip warning fix if MAXU32 already defined. (gvollant) - Fix unztell64() in minizip to work past 4GB. (Daniël Hörchner) - Clean up minizip to reduce warnings for testing - Add fallthrough comments for gcc - Eliminate use of ULL constants - Separate out address sanitizing from warnings in configure - Remove destructive aspects of make distclean - Check for cc masquerading as gcc or clang in configure - Fix crc32.c to compile local functions only if used Changes in 1.2.11 (15 Jan 2017) - Fix deflate stored bug when pulling last block from window - Permit immediate deflateParams changes before any deflate input Changes in 1.2.10 (2 Jan 2017) - Avoid warnings on snprintf() return value - Fix bug in deflate_stored() for zero-length input - Fix bug in gzwrite.c that produced corrupt gzip files - Remove files to be installed before copying them in Makefile.in - Add warnings when compiling with assembler code Changes in 1.2.9 (31 Dec 2016) - Fix contrib/minizip to permit unzipping with desktop API [Zouzou] - Improve contrib/blast to return unused bytes - Assure that gzoffset() is correct when appending - Improve compress() and uncompress() to support large lengths - Fix bug in test/example.c where error code not saved - Remedy Coverity warning [Randers-Pehrson] - Improve speed of gzprintf() in transparent mode - Fix inflateInit2() bug when windowBits is 16 or 32 - Change DEBUG macro to ZLIB_DEBUG - Avoid uninitialized access by gzclose_w() - Allow building zlib outside of the source directory - Fix bug that accepted invalid zlib header when windowBits is zero - Fix gzseek() problem on MinGW due to buggy _lseeki64 there - Loop on write() calls in gzwrite.c in case of non-blocking I/O - Add --warn (-w) option to ./configure for more compiler warnings - Reject a window size of 256 bytes if not using the zlib wrapper - Fix bug when level 0 used with Z_HUFFMAN or Z_RLE - Add --debug (-d) option to ./configure to define ZLIB_DEBUG - Fix bugs in creating a very large gzip header - Add uncompress2() function, which returns the input size used - Assure that deflateParams() will not switch functions mid-block - Dramatically speed up deflation for level 0 (storing) - Add gzfread(), duplicating the interface of fread() - Add gzfwrite(), duplicating the interface of fwrite() - Add deflateGetDictionary() function - Use snprintf() for later versions of Microsoft C - Fix *Init macros to use z_ prefix when requested - Replace as400 with os400 for OS/400 support [Monnerat] - Add crc32_z() and adler32_z() functions with size_t lengths - Update Visual Studio project files [AraHaan] Changes in 1.2.8 (28 Apr 2013) - Update contrib/minizip/iowin32.c for Windows RT [Vollant] - Do not force Z_CONST for C++ - Clean up contrib/vstudio [Roß] - Correct spelling error in zlib.h - Fix mixed line endings in contrib/vstudio Changes in 1.2.7.3 (13 Apr 2013) - Fix version numbers and DLL names in contrib/vstudio/*/zlib.rc Changes in 1.2.7.2 (13 Apr 2013) - Change check for a four-byte type back to hexadecimal - Fix typo in win32/Makefile.msc - Add casts in gzwrite.c for pointer differences Changes in 1.2.7.1 (24 Mar 2013) - Replace use of unsafe string functions with snprintf if available - Avoid including stddef.h on Windows for Z_SOLO compile [Niessink] - Fix gzgetc undefine when Z_PREFIX set [Turk] - Eliminate use of mktemp in Makefile (not always available) - Fix bug in 'F' mode for gzopen() - Add inflateGetDictionary() function - Correct comment in deflate.h - Use _snprintf for snprintf in Microsoft C - On Darwin, only use /usr/bin/libtool if libtool is not Apple - Delete "--version" file if created by "ar --version" [Richard G.] - Fix configure check for veracity of compiler error return codes - Fix CMake compilation of static lib for MSVC2010 x64 - Remove unused variable in infback9.c - Fix argument checks in gzlog_compress() and gzlog_write() - Clean up the usage of z_const and respect const usage within zlib - Clean up examples/gzlog.[ch] comparisons of different types - Avoid shift equal to bits in type (caused endless loop) - Fix uninitialized value bug in gzputc() introduced by const patches - Fix memory allocation error in examples/zran.c [Nor] - Fix bug where gzopen(), gzclose() would write an empty file - Fix bug in gzclose() when gzwrite() runs out of memory - Check for input buffer malloc failure in examples/gzappend.c - Add note to contrib/blast to use binary mode in stdio - Fix comparisons of differently signed integers in contrib/blast - Check for invalid code length codes in contrib/puff - Fix serious but very rare decompression bug in inftrees.c - Update inflateBack() comments, since inflate() can be faster - Use underscored I/O function names for WINAPI_FAMILY - Add _tr_flush_bits to the external symbols prefixed by --zprefix - Add contrib/vstudio/vc10 pre-build step for static only - Quote --version-script argument in CMakeLists.txt - Don't specify --version-script on Apple platforms in CMakeLists.txt - Fix casting error in contrib/testzlib/testzlib.c - Fix types in contrib/minizip to match result of get_crc_table() - Simplify contrib/vstudio/vc10 with 'd' suffix - Add TOP support to win32/Makefile.msc - Support i686 and amd64 assembler builds in CMakeLists.txt - Fix typos in the use of _LARGEFILE64_SOURCE in zconf.h - Add vc11 and vc12 build files to contrib/vstudio - Add gzvprintf() as an undocumented function in zlib - Fix configure for Sun shell - Remove runtime check in configure for four-byte integer type - Add casts and consts to ease user conversion to C++ - Add man pages for minizip and miniunzip - In Makefile uninstall, don't rm if preceding cd fails - Do not return Z_BUF_ERROR if deflateParam() has nothing to write Changes in 1.2.7 (2 May 2012) - Replace use of memmove() with a simple copy for portability - Test for existence of strerror - Restore gzgetc_ for backward compatibility with 1.2.6 - Fix build with non-GNU make on Solaris - Require gcc 4.0 or later on Mac OS X to use the hidden attribute - Include unistd.h for Watcom C - Use __WATCOMC__ instead of __WATCOM__ - Do not use the visibility attribute if NO_VIZ defined - Improve the detection of no hidden visibility attribute - Avoid using __int64 for gcc or solo compilation - Cast to char * in gzprintf to avoid warnings [Zinser] - Fix make_vms.com for VAX [Zinser] - Don't use library or built-in byte swaps - Simplify test and use of gcc hidden attribute - Fix bug in gzclose_w() when gzwrite() fails to allocate memory - Add "x" (O_EXCL) and "e" (O_CLOEXEC) modes support to gzopen() - Fix bug in test/minigzip.c for configure --solo - Fix contrib/vstudio project link errors [Mohanathas] - Add ability to choose the builder in make_vms.com [Schweda] - Add DESTDIR support to mingw32 win32/Makefile.gcc - Fix comments in win32/Makefile.gcc for proper usage - Allow overriding the default install locations for cmake - Generate and install the pkg-config file with cmake - Build both a static and a shared version of zlib with cmake - Include version symbols for cmake builds - If using cmake with MSVC, add the source directory to the includes - Remove unneeded EXTRA_CFLAGS from win32/Makefile.gcc [Truta] - Move obsolete emx makefile to old [Truta] - Allow the use of -Wundef when compiling or using zlib - Avoid the use of the -u option with mktemp - Improve inflate() documentation on the use of Z_FINISH - Recognize clang as gcc - Add gzopen_w() in Windows for wide character path names - Rename zconf.h in CMakeLists.txt to move it out of the way - Add source directory in CMakeLists.txt for building examples - Look in build directory for zlib.pc in CMakeLists.txt - Remove gzflags from zlibvc.def in vc9 and vc10 - Fix contrib/minizip compilation in the MinGW environment - Update ./configure for Solaris, support --64 [Mooney] - Remove -R. from Solaris shared build (possible security issue) - Avoid race condition for parallel make (-j) running example - Fix type mismatch between get_crc_table() and crc_table - Fix parsing of version with "-" in CMakeLists.txt [Snider, Ziegler] - Fix the path to zlib.map in CMakeLists.txt - Force the native libtool in Mac OS X to avoid GNU libtool [Beebe] - Add instructions to win32/Makefile.gcc for shared install [Torri] Changes in 1.2.6.1 (12 Feb 2012) - Avoid the use of the Objective-C reserved name "id" - Include io.h in gzguts.h for Microsoft compilers - Fix problem with ./configure --prefix and gzgetc macro - Include gz_header definition when compiling zlib solo - Put gzflags() functionality back in zutil.c - Avoid library header include in crc32.c for Z_SOLO - Use name in GCC_CLASSIC as C compiler for coverage testing, if set - Minor cleanup in contrib/minizip/zip.c [Vollant] - Update make_vms.com [Zinser] - Remove unnecessary gzgetc_ function - Use optimized byte swap operations for Microsoft and GNU [Snyder] - Fix minor typo in zlib.h comments [Rzesniowiecki] Changes in 1.2.6 (29 Jan 2012) - Update the Pascal interface in contrib/pascal - Fix function numbers for gzgetc_ in zlibvc.def files - Fix configure.ac for contrib/minizip [Schiffer] - Fix large-entry detection in minizip on 64-bit systems [Schiffer] - Have ./configure use the compiler return code for error indication - Fix CMakeLists.txt for cross compilation [McClure] - Fix contrib/minizip/zip.c for 64-bit architectures [Dalsnes] - Fix compilation of contrib/minizip on FreeBSD [Marquez] - Correct suggested usages in win32/Makefile.msc [Shachar, Horvath] - Include io.h for Turbo C / Borland C on all platforms [Truta] - Make version explicit in contrib/minizip/configure.ac [Bosmans] - Avoid warning for no encryption in contrib/minizip/zip.c [Vollant] - Minor cleanup up contrib/minizip/unzip.c [Vollant] - Fix bug when compiling minizip with C++ [Vollant] - Protect for long name and extra fields in contrib/minizip [Vollant] - Avoid some warnings in contrib/minizip [Vollant] - Add -I../.. -L../.. to CFLAGS for minizip and miniunzip - Add missing libs to minizip linker command - Add support for VPATH builds in contrib/minizip - Add an --enable-demos option to contrib/minizip/configure - Add the generation of configure.log by ./configure - Exit when required parameters not provided to win32/Makefile.gcc - Have gzputc return the character written instead of the argument - Use the -m option on ldconfig for BSD systems [Tobias] - Correct in zlib.map when deflateResetKeep was added Changes in 1.2.5.3 (15 Jan 2012) - Restore gzgetc function for binary compatibility - Do not use _lseeki64 under Borland C++ [Truta] - Update win32/Makefile.msc to build test/*.c [Truta] - Remove old/visualc6 given CMakefile and other alternatives - Update AS400 build files and documentation [Monnerat] - Update win32/Makefile.gcc to build test/*.c [Truta] - Permit stronger flushes after Z_BLOCK flushes - Avoid extraneous empty blocks when doing empty flushes - Permit Z_NULL arguments to deflatePending - Allow deflatePrime() to insert bits in the middle of a stream - Remove second empty static block for Z_PARTIAL_FLUSH - Write out all of the available bits when using Z_BLOCK - Insert the first two strings in the hash table after a flush Changes in 1.2.5.2 (17 Dec 2011) - fix ld error: unable to find version dependency 'ZLIB_1.2.5' - use relative symlinks for shared libs - Avoid searching past window for Z_RLE strategy - Assure that high-water mark initialization is always applied in deflate - Add assertions to fill_window() in deflate.c to match comments - Update python link in README - Correct spelling error in gzread.c - Fix bug in gzgets() for a concatenated empty gzip stream - Correct error in comment for gz_make() - Change gzread() and related to ignore junk after gzip streams - Allow gzread() and related to continue after gzclearerr() - Allow gzrewind() and gzseek() after a premature end-of-file - Simplify gzseek() now that raw after gzip is ignored - Change gzgetc() to a macro for speed (~40% speedup in testing) - Fix gzclose() to return the actual error last encountered - Always add large file support for windows - Include zconf.h for windows large file support - Include zconf.h.cmakein for windows large file support - Update zconf.h.cmakein on make distclean - Merge vestigial vsnprintf determination from zutil.h to gzguts.h - Clarify how gzopen() appends in zlib.h comments - Correct documentation of gzdirect() since junk at end now ignored - Add a transparent write mode to gzopen() when 'T' is in the mode - Update python link in zlib man page - Get inffixed.h and MAKEFIXED result to match - Add a ./config --solo option to make zlib subset with no library use - Add undocumented inflateResetKeep() function for CAB file decoding - Add --cover option to ./configure for gcc coverage testing - Add #define ZLIB_CONST option to use const in the z_stream interface - Add comment to gzdopen() in zlib.h to use dup() when using fileno() - Note behavior of uncompress() to provide as much data as it can - Add files in contrib/minizip to aid in building libminizip - Split off AR options in Makefile.in and configure - Change ON macro to Z_ARG to avoid application conflicts - Facilitate compilation with Borland C++ for pragmas and vsnprintf - Include io.h for Turbo C / Borland C++ - Move example.c and minigzip.c to test/ - Simplify incomplete code table filling in inflate_table() - Remove code from inflate.c and infback.c that is impossible to execute - Test the inflate code with full coverage - Allow deflateSetDictionary, inflateSetDictionary at any time (in raw) - Add deflateResetKeep and fix inflateResetKeep to retain dictionary - Fix gzwrite.c to accommodate reduced memory zlib compilation - Have inflate() with Z_FINISH avoid the allocation of a window - Do not set strm->adler when doing raw inflate - Fix gzeof() to behave just like feof() when read is not past end of file - Fix bug in gzread.c when end-of-file is reached - Avoid use of Z_BUF_ERROR in gz* functions except for premature EOF - Document gzread() capability to read concurrently written files - Remove hard-coding of resource compiler in CMakeLists.txt [Blammo] Changes in 1.2.5.1 (10 Sep 2011) - Update FAQ entry on shared builds (#13) - Avoid symbolic argument to chmod in Makefile.in - Fix bug and add consts in contrib/puff [Oberhumer] - Update contrib/puff/zeros.raw test file to have all block types - Add full coverage test for puff in contrib/puff/Makefile - Fix static-only-build install in Makefile.in - Fix bug in unzGetCurrentFileInfo() in contrib/minizip [Kuno] - Add libz.a dependency to shared in Makefile.in for parallel builds - Spell out "number" (instead of "nb") in zlib.h for total_in, total_out - Replace $(...) with `...` in configure for non-bash sh [Bowler] - Add darwin* to Darwin* and solaris* to SunOS\ 5* in configure [Groffen] - Add solaris* to Linux* in configure to allow gcc use [Groffen] - Add *bsd* to Linux* case in configure [Bar-Lev] - Add inffast.obj to dependencies in win32/Makefile.msc - Correct spelling error in deflate.h [Kohler] - Change libzdll.a again to libz.dll.a (!) in win32/Makefile.gcc - Add test to configure for GNU C looking for gcc in output of $cc -v - Add zlib.pc generation to win32/Makefile.gcc [Weigelt] - Fix bug in zlib.h for _FILE_OFFSET_BITS set and _LARGEFILE64_SOURCE not - Add comment in zlib.h that adler32_combine with len2 < 0 makes no sense - Make NO_DIVIDE option in adler32.c much faster (thanks to John Reiser) - Make stronger test in zconf.h to include unistd.h for LFS - Apply Darwin patches for 64-bit file offsets to contrib/minizip [Slack] - Fix zlib.h LFS support when Z_PREFIX used - Add updated as400 support (removed from old) [Monnerat] - Avoid deflate sensitivity to volatile input data - Avoid division in adler32_combine for NO_DIVIDE - Clarify the use of Z_FINISH with deflateBound() amount of space - Set binary for output file in puff.c - Use u4 type for crc_table to avoid conversion warnings - Apply casts in zlib.h to avoid conversion warnings - Add OF to prototypes for adler32_combine_ and crc32_combine_ [Miller] - Improve inflateSync() documentation to note indeterminacy - Add deflatePending() function to return the amount of pending output - Correct the spelling of "specification" in FAQ [Randers-Pehrson] - Add a check in configure for stdarg.h, use for gzprintf() - Check that pointers fit in ints when gzprint() compiled old style - Add dummy name before $(SHAREDLIBV) in Makefile [Bar-Lev, Bowler] - Delete line in configure that adds -L. libz.a to LDFLAGS [Weigelt] - Add debug records in assembler code [Londer] - Update RFC references to use http://tools.ietf.org/html/... [Li] - Add --archs option, use of libtool to configure for Mac OS X [Borstel] Changes in 1.2.5 (19 Apr 2010) - Disable visibility attribute in win32/Makefile.gcc [Bar-Lev] - Default to libdir as sharedlibdir in configure [Nieder] - Update copyright dates on modified source files - Update trees.c to be able to generate modified trees.h - Exit configure for MinGW, suggesting win32/Makefile.gcc - Check for NULL path in gz_open [Homurlu] Changes in 1.2.4.5 (18 Apr 2010) - Set sharedlibdir in configure [Torok] - Set LDFLAGS in Makefile.in [Bar-Lev] - Avoid mkdir objs race condition in Makefile.in [Bowler] - Add ZLIB_INTERNAL in front of internal inter-module functions and arrays - Define ZLIB_INTERNAL to hide internal functions and arrays for GNU C - Don't use hidden attribute when it is a warning generator (e.g. Solaris) Changes in 1.2.4.4 (18 Apr 2010) - Fix CROSS_PREFIX executable testing, CHOST extract, mingw* [Torok] - Undefine _LARGEFILE64_SOURCE in zconf.h if it is zero, but not if empty - Try to use bash or ksh regardless of functionality of /bin/sh - Fix configure incompatibility with NetBSD sh - Remove attempt to run under bash or ksh since have better NetBSD fix - Fix win32/Makefile.gcc for MinGW [Bar-Lev] - Add diagnostic messages when using CROSS_PREFIX in configure - Added --sharedlibdir option to configure [Weigelt] - Use hidden visibility attribute when available [Frysinger] Changes in 1.2.4.3 (10 Apr 2010) - Only use CROSS_PREFIX in configure for ar and ranlib if they exist - Use CROSS_PREFIX for nm [Bar-Lev] - Assume _LARGEFILE64_SOURCE defined is equivalent to true - Avoid use of undefined symbols in #if with && and || - Make *64 prototypes in gzguts.h consistent with functions - Add -shared load option for MinGW in configure [Bowler] - Move z_off64_t to public interface, use instead of off64_t - Remove ! from shell test in configure (not portable to Solaris) - Change +0 macro tests to -0 for possibly increased portability Changes in 1.2.4.2 (9 Apr 2010) - Add consistent carriage returns to readme.txt's in masmx86 and masmx64 - Really provide prototypes for *64 functions when building without LFS - Only define unlink() in minigzip.c if unistd.h not included - Update README to point to contrib/vstudio project files - Move projects/vc6 to old/ and remove projects/ - Include stdlib.h in minigzip.c for setmode() definition under WinCE - Clean up assembler builds in win32/Makefile.msc [Rowe] - Include sys/types.h for Microsoft for off_t definition - Fix memory leak on error in gz_open() - Symbolize nm as $NM in configure [Weigelt] - Use TEST_LDSHARED instead of LDSHARED to link test programs [Weigelt] - Add +0 to _FILE_OFFSET_BITS and _LFS64_LARGEFILE in case not defined - Fix bug in gzeof() to take into account unused input data - Avoid initialization of structures with variables in puff.c - Updated win32/README-WIN32.txt [Rowe] Changes in 1.2.4.1 (28 Mar 2010) - Remove the use of [a-z] constructs for sed in configure [gentoo 310225] - Remove $(SHAREDLIB) from LIBS in Makefile.in [Creech] - Restore "for debugging" comment on sprintf() in gzlib.c - Remove fdopen for MVS from gzguts.h - Put new README-WIN32.txt in win32 [Rowe] - Add check for shell to configure and invoke another shell if needed - Fix big fat stinking bug in gzseek() on uncompressed files - Remove vestigial F_OPEN64 define in zutil.h - Set and check the value of _LARGEFILE_SOURCE and _LARGEFILE64_SOURCE - Avoid errors on non-LFS systems when applications define LFS macros - Set EXE to ".exe" in configure for MINGW [Kahle] - Match crc32() in crc32.c exactly to the prototype in zlib.h [Sherrill] - Add prefix for cross-compilation in win32/makefile.gcc [Bar-Lev] - Add DLL install in win32/makefile.gcc [Bar-Lev] - Allow Linux* or linux* from uname in configure [Bar-Lev] - Allow ldconfig to be redefined in configure and Makefile.in [Bar-Lev] - Add cross-compilation prefixes to configure [Bar-Lev] - Match type exactly in gz_load() invocation in gzread.c - Match type exactly of zcalloc() in zutil.c to zlib.h alloc_func - Provide prototypes for *64 functions when building zlib without LFS - Don't use -lc when linking shared library on MinGW - Remove errno.h check in configure and vestigial errno code in zutil.h Changes in 1.2.4 (14 Mar 2010) - Fix VER3 extraction in configure for no fourth subversion - Update zlib.3, add docs to Makefile.in to make .pdf out of it - Add zlib.3.pdf to distribution - Don't set error code in gzerror() if passed pointer is NULL - Apply destination directory fixes to CMakeLists.txt [Lowman] - Move #cmakedefine's to a new zconf.in.cmakein - Restore zconf.h for builds that don't use configure or cmake - Add distclean to dummy Makefile for convenience - Update and improve INDEX, README, and FAQ - Update CMakeLists.txt for the return of zconf.h [Lowman] - Update contrib/vstudio/vc9 and vc10 [Vollant] - Change libz.dll.a back to libzdll.a in win32/Makefile.gcc - Apply license and readme changes to contrib/asm686 [Raiter] - Check file name lengths and add -c option in minigzip.c [Li] - Update contrib/amd64 and contrib/masmx86/ [Vollant] - Avoid use of "eof" parameter in trees.c to not shadow library variable - Update make_vms.com for removal of zlibdefs.h [Zinser] - Update assembler code and vstudio projects in contrib [Vollant] - Remove outdated assembler code contrib/masm686 and contrib/asm586 - Remove old vc7 and vc8 from contrib/vstudio - Update win32/Makefile.msc, add ZLIB_VER_SUBREVISION [Rowe] - Fix memory leaks in gzclose_r() and gzclose_w(), file leak in gz_open() - Add contrib/gcc_gvmat64 for longest_match and inflate_fast [Vollant] - Remove *64 functions from win32/zlib.def (they're not 64-bit yet) - Fix bug in void-returning vsprintf() case in gzwrite.c - Fix name change from inflate.h in contrib/inflate86/inffas86.c - Check if temporary file exists before removing in make_vms.com [Zinser] - Fix make install and uninstall for --static option - Fix usage of _MSC_VER in gzguts.h and zutil.h [Truta] - Update readme.txt in contrib/masmx64 and masmx86 to assemble Changes in 1.2.3.9 (21 Feb 2010) - Expunge gzio.c - Move as400 build information to old - Fix updates in contrib/minizip and contrib/vstudio - Add const to vsnprintf test in configure to avoid warnings [Weigelt] - Delete zconf.h (made by configure) [Weigelt] - Change zconf.in.h to zconf.h.in per convention [Weigelt] - Check for NULL buf in gzgets() - Return empty string for gzgets() with len == 1 (like fgets()) - Fix description of gzgets() in zlib.h for end-of-file, NULL return - Update minizip to 1.1 [Vollant] - Avoid MSVC loss of data warnings in gzread.c, gzwrite.c - Note in zlib.h that gzerror() should be used to distinguish from EOF - Remove use of snprintf() from gzlib.c - Fix bug in gzseek() - Update contrib/vstudio, adding vc9 and vc10 [Kuno, Vollant] - Fix zconf.h generation in CMakeLists.txt [Lowman] - Improve comments in zconf.h where modified by configure Changes in 1.2.3.8 (13 Feb 2010) - Clean up text files (tabs, trailing whitespace, etc.) [Oberhumer] - Use z_off64_t in gz_zero() and gz_skip() to match state->skip - Avoid comparison problem when sizeof(int) == sizeof(z_off64_t) - Revert to Makefile.in from 1.2.3.6 (live with the clutter) - Fix missing error return in gzflush(), add zlib.h note - Add *64 functions to zlib.map [Levin] - Fix signed/unsigned comparison in gz_comp() - Use SFLAGS when testing shared linking in configure - Add --64 option to ./configure to use -m64 with gcc - Fix ./configure --help to correctly name options - Have make fail if a test fails [Levin] - Avoid buffer overrun in contrib/masmx64/gvmat64.asm [Simpson] - Remove assembler object files from contrib Changes in 1.2.3.7 (24 Jan 2010) - Always gzopen() with O_LARGEFILE if available - Fix gzdirect() to work immediately after gzopen() or gzdopen() - Make gzdirect() more precise when the state changes while reading - Improve zlib.h documentation in many places - Catch memory allocation failure in gz_open() - Complete close operation if seek forward in gzclose_w() fails - Return Z_ERRNO from gzclose_r() if close() fails - Return Z_STREAM_ERROR instead of EOF for gzclose() being passed NULL - Return zero for gzwrite() errors to match zlib.h description - Return -1 on gzputs() error to match zlib.h description - Add zconf.in.h to allow recovery from configure modification [Weigelt] - Fix static library permissions in Makefile.in [Weigelt] - Avoid warnings in configure tests that hide functionality [Weigelt] - Add *BSD and DragonFly to Linux case in configure [gentoo 123571] - Change libzdll.a to libz.dll.a in win32/Makefile.gcc [gentoo 288212] - Avoid access of uninitialized data for first inflateReset2 call [Gomes] - Keep object files in subdirectories to reduce the clutter somewhat - Remove default Makefile and zlibdefs.h, add dummy Makefile - Add new external functions to Z_PREFIX, remove duplicates, z_z_ -> z_ - Remove zlibdefs.h completely -- modify zconf.h instead Changes in 1.2.3.6 (17 Jan 2010) - Avoid void * arithmetic in gzread.c and gzwrite.c - Make compilers happier with const char * for gz_error message - Avoid unused parameter warning in inflate.c - Avoid signed-unsigned comparison warning in inflate.c - Indent #pragma's for traditional C - Fix usage of strwinerror() in glib.c, change to gz_strwinerror() - Correct email address in configure for system options - Update make_vms.com and add make_vms.com to contrib/minizip [Zinser] - Update zlib.map [Brown] - Fix Makefile.in for Solaris 10 make of example64 and minizip64 [Torok] - Apply various fixes to CMakeLists.txt [Lowman] - Add checks on len in gzread() and gzwrite() - Add error message for no more room for gzungetc() - Remove zlib version check in gzwrite() - Defer compression of gzprintf() result until need to - Use snprintf() in gzdopen() if available - Remove USE_MMAP configuration determination (only used by minigzip) - Remove examples/pigz.c (available separately) - Update examples/gun.c to 1.6 Changes in 1.2.3.5 (8 Jan 2010) - Add space after #if in zutil.h for some compilers - Fix relatively harmless bug in deflate_fast() [Exarevsky] - Fix same problem in deflate_slow() - Add $(SHAREDLIBV) to LIBS in Makefile.in [Brown] - Add deflate_rle() for faster Z_RLE strategy run-length encoding - Add deflate_huff() for faster Z_HUFFMAN_ONLY encoding - Change name of "write" variable in inffast.c to avoid library collisions - Fix premature EOF from gzread() in gzio.c [Brown] - Use zlib header window size if windowBits is 0 in inflateInit2() - Remove compressBound() call in deflate.c to avoid linking compress.o - Replace use of errno in gz* with functions, support WinCE [Alves] - Provide alternative to perror() in minigzip.c for WinCE [Alves] - Don't use _vsnprintf on later versions of MSVC [Lowman] - Add CMake build script and input file [Lowman] - Update contrib/minizip to 1.1 [Svensson, Vollant] - Moved nintendods directory from contrib to root - Replace gzio.c with a new set of routines with the same functionality - Add gzbuffer(), gzoffset(), gzclose_r(), gzclose_w() as part of above - Update contrib/minizip to 1.1b - Change gzeof() to return 0 on error instead of -1 to agree with zlib.h Changes in 1.2.3.4 (21 Dec 2009) - Use old school .SUFFIXES in Makefile.in for FreeBSD compatibility - Update comments in configure and Makefile.in for default --shared - Fix test -z's in configure [Marquess] - Build examplesh and minigzipsh when not testing - Change NULL's to Z_NULL's in deflate.c and in comments in zlib.h - Import LDFLAGS from the environment in configure - Fix configure to populate SFLAGS with discovered CFLAGS options - Adapt make_vms.com to the new Makefile.in [Zinser] - Add zlib2ansi script for C++ compilation [Marquess] - Add _FILE_OFFSET_BITS=64 test to make test (when applicable) - Add AMD64 assembler code for longest match to contrib [Teterin] - Include options from $SFLAGS when doing $LDSHARED - Simplify 64-bit file support by introducing z_off64_t type - Make shared object files in objs directory to work around old Sun cc - Use only three-part version number for Darwin shared compiles - Add rc option to ar in Makefile.in for when ./configure not run - Add -WI,-rpath,. to LDFLAGS for OSF 1 V4* - Set LD_LIBRARYN32_PATH for SGI IRIX shared compile - Protect against _FILE_OFFSET_BITS being defined when compiling zlib - Rename Makefile.in targets allstatic to static and allshared to shared - Fix static and shared Makefile.in targets to be independent - Correct error return bug in gz_open() by setting state [Brown] - Put spaces before ;;'s in configure for better sh compatibility - Add pigz.c (parallel implementation of gzip) to examples/ - Correct constant in crc32.c to UL [Leventhal] - Reject negative lengths in crc32_combine() - Add inflateReset2() function to work like inflateEnd()/inflateInit2() - Include sys/types.h for _LARGEFILE64_SOURCE [Brown] - Correct typo in doc/algorithm.txt [Janik] - Fix bug in adler32_combine() [Zhu] - Catch missing-end-of-block-code error in all inflates and in puff Assures that random input to inflate eventually results in an error - Added enough.c (calculation of ENOUGH for inftrees.h) to examples/ - Update ENOUGH and its usage to reflect discovered bounds - Fix gzerror() error report on empty input file [Brown] - Add ush casts in trees.c to avoid pedantic runtime errors - Fix typo in zlib.h uncompress() description [Reiss] - Correct inflate() comments with regard to automatic header detection - Remove deprecation comment on Z_PARTIAL_FLUSH (it stays) - Put new version of gzlog (2.0) in examples with interruption recovery - Add puff compile option to permit invalid distance-too-far streams - Add puff TEST command options, ability to read piped input - Prototype the *64 functions in zlib.h when _FILE_OFFSET_BITS == 64, but _LARGEFILE64_SOURCE not defined - Fix Z_FULL_FLUSH to truly erase the past by resetting s->strstart - Fix deflateSetDictionary() to use all 32K for output consistency - Remove extraneous #define MIN_LOOKAHEAD in deflate.c (in deflate.h) - Clear bytes after deflate lookahead to avoid use of uninitialized data - Change a limit in inftrees.c to be more transparent to Coverity Prevent - Update win32/zlib.def with exported symbols from zlib.h - Correct spelling errors in zlib.h [Willem, Sobrado] - Allow Z_BLOCK for deflate() to force a new block - Allow negative bits in inflatePrime() to delete existing bit buffer - Add Z_TREES flush option to inflate() to return at end of trees - Add inflateMark() to return current state information for random access - Add Makefile for NintendoDS to contrib [Costa] - Add -w in configure compile tests to avoid spurious warnings [Beucler] - Fix typos in zlib.h comments for deflateSetDictionary() - Fix EOF detection in transparent gzread() [Maier] Changes in 1.2.3.3 (2 October 2006) - Make --shared the default for configure, add a --static option - Add compile option to permit invalid distance-too-far streams - Add inflateUndermine() function which is required to enable above - Remove use of "this" variable name for C++ compatibility [Marquess] - Add testing of shared library in make test, if shared library built - Use ftello() and fseeko() if available instead of ftell() and fseek() - Provide two versions of all functions that use the z_off_t type for binary compatibility -- a normal version and a 64-bit offset version, per the Large File Support Extension when _LARGEFILE64_SOURCE is defined; use the 64-bit versions by default when _FILE_OFFSET_BITS is defined to be 64 - Add a --uname= option to configure to perhaps help with cross-compiling Changes in 1.2.3.2 (3 September 2006) - Turn off silly Borland warnings [Hay] - Use off64_t and define _LARGEFILE64_SOURCE when present - Fix missing dependency on inffixed.h in Makefile.in - Rig configure --shared to build both shared and static [Teredesai, Truta] - Remove zconf.in.h and instead create a new zlibdefs.h file - Fix contrib/minizip/unzip.c non-encrypted after encrypted [Vollant] - Add treebuild.xml (see http://treebuild.metux.de/) [Weigelt] Changes in 1.2.3.1 (16 August 2006) - Add watcom directory with OpenWatcom make files [Daniel] - Remove #undef of FAR in zconf.in.h for MVS [Fedtke] - Update make_vms.com [Zinser] - Use -fPIC for shared build in configure [Teredesai, Nicholson] - Use only major version number for libz.so on IRIX and OSF1 [Reinholdtsen] - Use fdopen() (not _fdopen()) for Interix in zutil.h [Bäck] - Add some FAQ entries about the contrib directory - Update the MVS question in the FAQ - Avoid extraneous reads after EOF in gzio.c [Brown] - Correct spelling of "successfully" in gzio.c [Randers-Pehrson] - Add comments to zlib.h about gzerror() usage [Brown] - Set extra flags in gzip header in gzopen() like deflate() does - Make configure options more compatible with double-dash conventions [Weigelt] - Clean up compilation under Solaris SunStudio cc [Rowe, Reinholdtsen] - Fix uninstall target in Makefile.in [Truta] - Add pkgconfig support [Weigelt] - Use $(DESTDIR) macro in Makefile.in [Reinholdtsen, Weigelt] - Replace set_data_type() with a more accurate detect_data_type() in trees.c, according to the txtvsbin.txt document [Truta] - Swap the order of #include <stdio.h> and #include "zlib.h" in gzio.c, example.c and minigzip.c [Truta] - Shut up annoying VS2005 warnings about standard C deprecation [Rowe, Truta] (where?) - Fix target "clean" from win32/Makefile.bor [Truta] - Create .pdb and .manifest files in win32/makefile.msc [Ziegler, Rowe] - Update zlib www home address in win32/DLL_FAQ.txt [Truta] - Update contrib/masmx86/inffas32.asm for VS2005 [Vollant, Van Wassenhove] - Enable browse info in the "Debug" and "ASM Debug" configurations in the Visual C++ 6 project, and set (non-ASM) "Debug" as default [Truta] - Add pkgconfig support [Weigelt] - Add ZLIB_VER_MAJOR, ZLIB_VER_MINOR and ZLIB_VER_REVISION in zlib.h, for use in win32/zlib1.rc [Polushin, Rowe, Truta] - Add a document that explains the new text detection scheme to doc/txtvsbin.txt [Truta] - Add rfc1950.txt, rfc1951.txt and rfc1952.txt to doc/ [Truta] - Move algorithm.txt into doc/ [Truta] - Synchronize FAQ with website - Fix compressBound(), was low for some pathological cases [Fearnley] - Take into account wrapper variations in deflateBound() - Set examples/zpipe.c input and output to binary mode for Windows - Update examples/zlib_how.html with new zpipe.c (also web site) - Fix some warnings in examples/gzlog.c and examples/zran.c (it seems that gcc became pickier in 4.0) - Add zlib.map for Linux: "All symbols from zlib-1.1.4 remain un-versioned, the patch adds versioning only for symbols introduced in zlib-1.2.0 or later. It also declares as local those symbols which are not designed to be exported." [Levin] - Update Z_PREFIX list in zconf.in.h, add --zprefix option to configure - Do not initialize global static by default in trees.c, add a response NO_INIT_GLOBAL_POINTERS to initialize them if needed [Marquess] - Don't use strerror() in gzio.c under WinCE [Yakimov] - Don't use errno.h in zutil.h under WinCE [Yakimov] - Move arguments for AR to its usage to allow replacing ar [Marot] - Add HAVE_VISIBILITY_PRAGMA in zconf.in.h for Mozilla [Randers-Pehrson] - Improve inflateInit() and inflateInit2() documentation - Fix structure size comment in inflate.h - Change configure help option from --h* to --help [Santos] Changes in 1.2.3 (18 July 2005) - Apply security vulnerability fixes to contrib/infback9 as well - Clean up some text files (carriage returns, trailing space) - Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant] Changes in 1.2.2.4 (11 July 2005) - Add inflatePrime() function for starting inflation at bit boundary - Avoid some Visual C warnings in deflate.c - Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit compile - Fix some spelling errors in comments [Betts] - Correct inflateInit2() error return documentation in zlib.h - Add zran.c example of compressed data random access to examples directory, shows use of inflatePrime() - Fix cast for assignments to strm->state in inflate.c and infback.c - Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer] - Move declarations of gf2 functions to right place in crc32.c [Oberhumer] - Add cast in trees.c t avoid a warning [Oberhumer] - Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer] - Update make_vms.com [Zinser] - Initialize state->write in inflateReset() since copied in inflate_fast() - Be more strict on incomplete code sets in inflate_table() and increase ENOUGH and MAXD -- this repairs a possible security vulnerability for invalid inflate input. Thanks to Tavis Ormandy and Markus Oberhumer for discovering the vulnerability and providing test cases - Add ia64 support to configure for HP-UX [Smith] - Add error return to gzread() for format or i/o error [Levin] - Use malloc.h for OS/2 [Necasek] Changes in 1.2.2.3 (27 May 2005) - Replace 1U constants in inflate.c and inftrees.c for 64-bit compile - Typecast fread() return values in gzio.c [Vollant] - Remove trailing space in minigzip.c outmode (VC++ can't deal with it) - Fix crc check bug in gzread() after gzungetc() [Heiner] - Add the deflateTune() function to adjust internal compression parameters - Add a fast gzip decompressor, gun.c, to examples (use of inflateBack) - Remove an incorrect assertion in examples/zpipe.c - Add C++ wrapper in infback9.h [Donais] - Fix bug in inflateCopy() when decoding fixed codes - Note in zlib.h how much deflateSetDictionary() actually uses - Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used) - Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer] - Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer] - Add gzdirect() function to indicate transparent reads - Update contrib/minizip [Vollant] - Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer] - Add casts in crc32.c to avoid warnings [Oberhumer] - Add contrib/masmx64 [Vollant] - Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant] Changes in 1.2.2.2 (30 December 2004) - Replace structure assignments in deflate.c and inflate.c with zmemcpy to avoid implicit memcpy calls (portability for no-library compilation) - Increase sprintf() buffer size in gzdopen() to allow for large numbers - Add INFLATE_STRICT to check distances against zlib header - Improve WinCE errno handling and comments [Chang] - Remove comment about no gzip header processing in FAQ - Add Z_FIXED strategy option to deflateInit2() to force fixed trees - Add updated make_vms.com [Coghlan], update README - Create a new "examples" directory, move gzappend.c there, add zpipe.c, fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html - Add FAQ entry and comments in deflate.c on uninitialized memory access - Add Solaris 9 make options in configure [Gilbert] - Allow strerror() usage in gzio.c for STDC - Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer] - Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant] - Use z_off_t for adler32_combine() and crc32_combine() lengths - Make adler32() much faster for small len - Use OS_CODE in deflate() default gzip header Changes in 1.2.2.1 (31 October 2004) - Allow inflateSetDictionary() call for raw inflate - Fix inflate header crc check bug for file names and comments - Add deflateSetHeader() and gz_header structure for custom gzip headers - Add inflateGetheader() to retrieve gzip headers - Add crc32_combine() and adler32_combine() functions - Add alloc_func, free_func, in_func, out_func to Z_PREFIX list - Use zstreamp consistently in zlib.h (inflate_back functions) - Remove GUNZIP condition from definition of inflate_mode in inflate.h and in contrib/inflate86/inffast.S [Truta, Anderson] - Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson] - Update projects/README.projects and projects/visualc6 [Truta] - Update win32/DLL_FAQ.txt [Truta] - Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta] - Deprecate Z_ASCII; use Z_TEXT instead [Truta] - Use a new algorithm for setting strm->data_type in trees.c [Truta] - Do not define an exit() prototype in zutil.c unless DEBUG defined - Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta] - Add comment in zlib.h for Z_NO_FLUSH parameter to deflate() - Fix Darwin build version identification [Peterson] Changes in 1.2.2 (3 October 2004) - Update zlib.h comments on gzip in-memory processing - Set adler to 1 in inflateReset() to support Java test suite [Walles] - Add contrib/dotzlib [Ravn] - Update win32/DLL_FAQ.txt [Truta] - Update contrib/minizip [Vollant] - Move contrib/visual-basic.txt to old/ [Truta] - Fix assembler builds in projects/visualc6/ [Truta] Changes in 1.2.1.2 (9 September 2004) - Update INDEX file - Fix trees.c to update strm->data_type (no one ever noticed!) - Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown] - Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE) - Add limited multitasking protection to DYNAMIC_CRC_TABLE - Add NO_vsnprintf for VMS in zutil.h [Mozilla] - Don't declare strerror() under VMS [Mozilla] - Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize - Update contrib/ada [Anisimkov] - Update contrib/minizip [Vollant] - Fix configure to not hardcode directories for Darwin [Peterson] - Fix gzio.c to not return error on empty files [Brown] - Fix indentation; update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas [Truta] - Update mkasm.bat in contrib/masmx86 [Truta] - Update contrib/untgz [Truta] - Add projects/README.projects [Truta] - Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta] - Update win32/DLL_FAQ.txt [Truta] - Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta] - Remove an unnecessary assignment to curr in inftrees.c [Truta] - Add OS/2 to exe builds in configure [Poltorak] - Remove err dummy parameter in zlib.h [Kientzle] Changes in 1.2.1.1 (9 January 2004) - Update email address in README - Several FAQ updates - Fix a big fat bug in inftrees.c that prevented decoding valid dynamic blocks with only literals and no distance codes -- Thanks to "Hot Emu" for the bug report and sample file - Add a note to puff.c on no distance codes case Changes in 1.2.1 (17 November 2003) - Remove a tab in contrib/gzappend/gzappend.c - Update some interfaces in contrib for new zlib functions - Update zlib version number in some contrib entries - Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta] - Support shared libraries on Hurd and KFreeBSD [Brown] - Fix error in NO_DIVIDE option of adler32.c Changes in 1.2.0.8 (4 November 2003) - Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas - Add experimental NO_DIVIDE #define in adler32.c - Possibly faster on some processors (let me know if it is) - Correct Z_BLOCK to not return on first inflate call if no wrap - Fix strm->data_type on inflate() return to correctly indicate EOB - Add deflatePrime() function for appending in the middle of a byte - Add contrib/gzappend for an example of appending to a stream - Update win32/DLL_FAQ.txt [Truta] - Delete Turbo C comment in README [Truta] - Improve some indentation in zconf.h [Truta] - Fix infinite loop on bad input in configure script [Church] - Fix gzeof() for concatenated gzip files [Johnson] - Add example to contrib/visual-basic.txt [Michael B.] - Add -p to mkdir's in Makefile.in [vda] - Fix configure to properly detect presence or lack of printf functions - Add AS400 support [Monnerat] - Add a little Cygwin support [Wilson] Changes in 1.2.0.7 (21 September 2003) - Correct some debug formats in contrib/infback9 - Cast a type in a debug statement in trees.c - Change search and replace delimiter in configure from % to # [Beebe] - Update contrib/untgz to 0.2 with various fixes [Truta] - Add build support for Amiga [Nikl] - Remove some directories in old that have been updated to 1.2 - Add dylib building for Mac OS X in configure and Makefile.in - Remove old distribution stuff from Makefile - Update README to point to DLL_FAQ.txt, and add comment on Mac OS X - Update links in README Changes in 1.2.0.6 (13 September 2003) - Minor FAQ updates - Update contrib/minizip to 1.00 [Vollant] - Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta] - Update POSTINC comment for 68060 [Nikl] - Add contrib/infback9 with deflate64 decoding (unsupported) - For MVS define NO_vsnprintf and undefine FAR [van Burik] - Add pragma for fdopen on MVS [van Burik] Changes in 1.2.0.5 (8 September 2003) - Add OF to inflateBackEnd() declaration in zlib.h - Remember start when using gzdopen in the middle of a file - Use internal off_t counters in gz* functions to properly handle seeks - Perform more rigorous check for distance-too-far in inffast.c - Add Z_BLOCK flush option to return from inflate at block boundary - Set strm->data_type on return from inflate - Indicate bits unused, if at block boundary, and if in last block - Replace size_t with ptrdiff_t in crc32.c, and check for correct size - Add condition so old NO_DEFLATE define still works for compatibility - FAQ update regarding the Windows DLL [Truta] - INDEX update: add qnx entry, remove aix entry [Truta] - Install zlib.3 into mandir [Wilson] - Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta] - Adapt the zlib interface to the new DLL convention guidelines [Truta] - Introduce ZLIB_WINAPI macro to allow the export of functions using the WINAPI calling convention, for Visual Basic [Vollant, Truta] - Update msdos and win32 scripts and makefiles [Truta] - Export symbols by name, not by ordinal, in win32/zlib.def [Truta] - Add contrib/ada [Anisimkov] - Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta] - Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant] - Add contrib/masm686 [Truta] - Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm [Truta, Vollant] - Update contrib/delphi; rename to contrib/pascal; add example [Truta] - Remove contrib/delphi2; add a new contrib/delphi [Truta] - Avoid inclusion of the nonstandard <memory.h> in contrib/iostream, and fix some method prototypes [Truta] - Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip [Truta] - Avoid the use of backslash (\) in contrib/minizip [Vollant] - Fix file time handling in contrib/untgz; update makefiles [Truta] - Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines [Vollant] - Remove contrib/vstudio/vc15_16 [Vollant] - Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta] - Update README.contrib [Truta] - Invert the assignment order of match_head and s->prev[...] in INSERT_STRING [Truta] - Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings [Truta] - Compare function pointers with 0, not with NULL or Z_NULL [Truta] - Fix prototype of syncsearch in inflate.c [Truta] - Introduce ASMINF macro to be enabled when using an ASM implementation of inflate_fast [Truta] - Change NO_DEFLATE to NO_GZCOMPRESS [Truta] - Modify test_gzio in example.c to take a single file name as a parameter [Truta] - Exit the example.c program if gzopen fails [Truta] - Add type casts around strlen in example.c [Truta] - Remove casting to sizeof in minigzip.c; give a proper type to the variable compared with SUFFIX_LEN [Truta] - Update definitions of STDC and STDC99 in zconf.h [Truta] - Synchronize zconf.h with the new Windows DLL interface [Truta] - Use SYS16BIT instead of __32BIT__ to distinguish between 16- and 32-bit platforms [Truta] - Use far memory allocators in small 16-bit memory models for Turbo C [Truta] - Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in zlibCompileFlags [Truta] - Cygwin has vsnprintf [Wilson] - In Windows16, OS_CODE is 0, as in MSDOS [Truta] - In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson] Changes in 1.2.0.4 (10 August 2003) - Minor FAQ updates - Be more strict when checking inflateInit2's windowBits parameter - Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well - Add gzip wrapper option to deflateInit2 using windowBits - Add updated QNX rule in configure and qnx directory [Bonnefoy] - Make inflate distance-too-far checks more rigorous - Clean up FAR usage in inflate - Add casting to sizeof() in gzio.c and minigzip.c Changes in 1.2.0.3 (19 July 2003) - Fix silly error in gzungetc() implementation [Vollant] - Update contrib/minizip and contrib/vstudio [Vollant] - Fix printf format in example.c - Correct cdecl support in zconf.in.h [Anisimkov] - Minor FAQ updates Changes in 1.2.0.2 (13 July 2003) - Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons - Attempt to avoid warnings in crc32.c for pointer-int conversion - Add AIX to configure, remove aix directory [Bakker] - Add some casts to minigzip.c - Improve checking after insecure sprintf() or vsprintf() calls - Remove #elif's from crc32.c - Change leave label to inf_leave in inflate.c and infback.c to avoid library conflicts - Remove inflate gzip decoding by default--only enable gzip decoding by special request for stricter backward compatibility - Add zlibCompileFlags() function to return compilation information - More typecasting in deflate.c to avoid warnings - Remove leading underscore from _Capital #defines [Truta] - Fix configure to link shared library when testing - Add some Windows CE target adjustments [Mai] - Remove #define ZLIB_DLL in zconf.h [Vollant] - Add zlib.3 [Rodgers] - Update RFC URL in deflate.c and algorithm.txt [Mai] - Add zlib_dll_FAQ.txt to contrib [Truta] - Add UL to some constants [Truta] - Update minizip and vstudio [Vollant] - Remove vestigial NEED_DUMMY_RETURN from zconf.in.h - Expand use of NO_DUMMY_DECL to avoid all dummy structures - Added iostream3 to contrib [Schwardt] - Replace rewind() with fseek() for WinCE [Truta] - Improve setting of zlib format compression level flags - Report 0 for huffman and rle strategies and for level == 0 or 1 - Report 2 only for level == 6 - Only deal with 64K limit when necessary at compile time [Truta] - Allow TOO_FAR check to be turned off at compile time [Truta] - Add gzclearerr() function [Souza] - Add gzungetc() function Changes in 1.2.0.1 (17 March 2003) - Add Z_RLE strategy for run-length encoding [Truta] - When Z_RLE requested, restrict matches to distance one - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE - Correct FASTEST compilation to allow level == 0 - Clean up what gets compiled for FASTEST - Incorporate changes to zconf.in.h [Vollant] - Refine detection of Turbo C need for dummy returns - Refine ZLIB_DLL compilation - Include additional header file on VMS for off_t typedef - Try to use _vsnprintf where it supplants vsprintf [Vollant] - Add some casts in inffast.c - Enhance comments in zlib.h on what happens if gzprintf() tries to write more than 4095 bytes before compression - Remove unused state from inflateBackEnd() - Remove exit(0) from minigzip.c, example.c - Get rid of all those darn tabs - Add "check" target to Makefile.in that does the same thing as "test" - Add "mostlyclean" and "maintainer-clean" targets to Makefile.in - Update contrib/inflate86 [Anderson] - Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant] - Add msdos and win32 directories with makefiles [Truta] - More additions and improvements to the FAQ Changes in 1.2.0 (9 March 2003) - New and improved inflate code - About 20% faster - Does not allocate 32K window unless and until needed - Automatically detects and decompresses gzip streams - Raw inflate no longer needs an extra dummy byte at end - Added inflateBack functions using a callback interface--even faster than inflate, useful for file utilities (gzip, zip) - Added inflateCopy() function to record state for random access on externally generated deflate streams (e.g. in gzip files) - More readable code (I hope) - New and improved crc32() - About 50% faster, thanks to suggestions from Rodney Brown - Add deflateBound() and compressBound() functions - Fix memory leak in deflateInit2() - Permit setting dictionary for raw deflate (for parallel deflate) - Fix const declaration for gzwrite() - Check for some malloc() failures in gzio.c - Fix bug in gzopen() on single-byte file 0x1f - Fix bug in gzread() on concatenated file with 0x1f at end of buffer and next buffer doesn't start with 0x8b - Fix uncompress() to return Z_DATA_ERROR on truncated input - Free memory at end of example.c - Remove MAX #define in trees.c (conflicted with some libraries) - Fix static const's in deflate.c, gzio.c, and zutil.[ch] - Declare malloc() and free() in gzio.c if STDC not defined - Use malloc() instead of calloc() in zutil.c if int big enough - Define STDC for AIX - Add aix/ with approach for compiling shared library on AIX - Add HP-UX support for shared libraries in configure - Add OpenUNIX support for shared libraries in configure - Use $cc instead of gcc to build shared library - Make prefix directory if needed when installing - Correct Macintosh avoidance of typedef Byte in zconf.h - Correct Turbo C memory allocation when under Linux - Use libz.a instead of -lz in Makefile (assure use of compiled library) - Update configure to check for snprintf or vsnprintf functions and their return value, warn during make if using an insecure function - Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that is lost when library is used--resolution is to build new zconf.h - Documentation improvements (in zlib.h): - Document raw deflate and inflate - Update RFCs URL - Point out that zlib and gzip formats are different - Note that Z_BUF_ERROR is not fatal - Document string limit for gzprintf() and possible buffer overflow - Note requirement on avail_out when flushing - Note permitted values of flush parameter of inflate() - Add some FAQs (and even answers) to the FAQ - Add contrib/inflate86/ for x86 faster inflate - Add contrib/blast/ for PKWare Data Compression Library decompression - Add contrib/puff/ simple inflate for deflate format description Changes in 1.1.4 (11 March 2002) - ZFREE was repeated on same allocation on some error conditions This creates a security problem described in http://www.zlib.org/advisory-2002-03-11.txt - Returned incorrect error (Z_MEM_ERROR) on some invalid data - Avoid accesses before window for invalid distances with inflate window less than 32K - force windowBits > 8 to avoid a bug in the encoder for a window size of 256 bytes. (A complete fix will be available in 1.1.5) Changes in 1.1.3 (9 July 1998) - fix "an inflate input buffer bug that shows up on rare but persistent occasions" (Mark) - fix gzread and gztell for concatenated .gz files (Didier Le Botlan) - fix gzseek(..., SEEK_SET) in write mode - fix crc check after a gzeek (Frank Faubert) - fix miniunzip when the last entry in a zip file is itself a zip file (J Lillge) - add contrib/asm586 and contrib/asm686 (Brian Raiter) See http://www.muppetlabs.com/~breadbox/software/assembly.html - add support for Delphi 3 in contrib/delphi (Bob Dellaca) - add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) - do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) - use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) - added a FAQ file - Support gzdopen on Mac with Metrowerks (Jason Linhart) - Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) - define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) - avoid some warnings with Borland C (Tom Tanner) - fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) - emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) - allow several arguments to configure (Tim Mooney, Frodo Looijaard) - use libdir and includedir in Makefile.in (Tim Mooney) - support shared libraries on OSF1 V4 (Tim Mooney) - remove so_locations in "make clean" (Tim Mooney) - fix maketree.c compilation error (Glenn, Mark) - Python interface to zlib now in Python 1.5 (Jeremy Hylton) - new Makefile.riscos (Rich Walker) - initialize static descriptors in trees.c for embedded targets (Nick Smith) - use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) - add the OS/2 files in Makefile.in too (Andrew Zabolotny) - fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) - fix maketree.c to allow clean compilation of inffixed.h (Mark) - fix parameter check in deflateCopy (Gunther Nikl) - cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) - Many portability patches by Christian Spieler: . zutil.c, zutil.h: added "const" for zmem* . Make_vms.com: fixed some typos . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists . msdos/Makefile.msc: remove "default rtl link library" info from obj files . msdos/Makefile.*: use model-dependent name for the built zlib library . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) - use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) - replace __far with _far for better portability (Christian Spieler, Tom Lane) - fix test for errno.h in configure (Tim Newsham) Changes in 1.1.2 (19 March 98) - added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) See http://www.winimage.com/zLibDll/unzip.html - preinitialize the inflate tables for fixed codes, to make the code completely thread safe (Mark) - some simplifications and slight speed-up to the inflate code (Mark) - fix gzeof on non-compressed files (Allan Schrum) - add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) - use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) - added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) - add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) - do not wrap extern "C" around system includes (Tom Lane) - mention zlib binding for TCL in README (Andreas Kupries) - added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) - allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) - allow "configure --prefix $HOME" (Tim Mooney) - remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) - move Makefile.sas to amiga/Makefile.sas Changes in 1.1.1 (27 Feb 98) - fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) - remove block truncation heuristic which had very marginal effect for zlib (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the compression ratio on some files. This also allows inlining _tr_tally for matches in deflate_slow - added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) Changes in 1.1.0 (24 Feb 98) - do not return STREAM_END prematurely in inflate (John Bowler) - revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) - compile with -DFASTEST to get compression code optimized for speed only - in minigzip, try mmap'ing the input file first (Miguel Albrecht) - increase size of I/O buffers in minigzip.c and gzio.c (not a big gain on Sun but significant on HP) - add a pointer to experimental unzip library in README (Gilles Vollant) - initialize variable gcc in configure (Chris Herborth) Changes in 1.0.9 (17 Feb 1998) - added gzputs and gzgets functions - do not clear eof flag in gzseek (Mark Diekhans) - fix gzseek for files in transparent mode (Mark Diekhans) - do not assume that vsprintf returns the number of bytes written (Jens Krinke) - replace EXPORT with ZEXPORT to avoid conflict with other programs - added compress2 in zconf.h, zlib.def, zlib.dnt - new asm code from Gilles Vollant in contrib/asm386 - simplify the inflate code (Mark): . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() . ZALLOC the length list in inflate_trees_fixed() instead of using stack . ZALLOC the value area for huft_build() instead of using stack . Simplify Z_FINISH check in inflate() - Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 - in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) - in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with the declaration of FAR (Gilles Vollant) - install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) - read_buf buf parameter of type Bytef* instead of charf* - zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) - do not redeclare unlink in minigzip.c for WIN32 (John Bowler) - fix check for presence of directories in "make install" (Ian Willis) Changes in 1.0.8 (27 Jan 1998) - fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) - fix gzgetc and gzputc for big endian systems (Markus Oberhumer) - added compress2() to allow setting the compression level - include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) - use constant arrays for the static trees in trees.c instead of computing them at run time (thanks to Ken Raeburn for this suggestion). To create trees.h, compile with GEN_TREES_H and run "make test" - check return code of example in "make test" and display result - pass minigzip command line options to file_compress - simplifying code of inflateSync to avoid gcc 2.8 bug - support CC="gcc -Wall" in configure -s (QingLong) - avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) - fix test for shared library support to avoid compiler warnings - zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) - check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) - do not use fdopen for Metrowerks on Mac (Brad Pettit)) - add checks for gzputc and gzputc in example.c - avoid warnings in gzio.c and deflate.c (Andreas Kleinert) - use const for the CRC table (Ken Raeburn) - fixed "make uninstall" for shared libraries - use Tracev instead of Trace in infblock.c - in example.c use correct compressed length for test_sync - suppress +vnocompatwarnings in configure for HPUX (not always supported) Changes in 1.0.7 (20 Jan 1998) - fix gzseek which was broken in write mode - return error for gzseek to negative absolute position - fix configure for Linux (Chun-Chung Chen) - increase stack space for MSC (Tim Wegner) - get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) - define EXPORTVA for gzprintf (Gilles Vollant) - added man page zlib.3 (Rick Rodgers) - for contrib/untgz, fix makedir() and improve Makefile - check gzseek in write mode in example.c - allocate extra buffer for seeks only if gzseek is actually called - avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) - add inflateSyncPoint in zconf.h - fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def Changes in 1.0.6 (19 Jan 1998) - add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) - Fix a deflate bug occurring only with compression level 0 (thanks to Andy Buckler for finding this one) - In minigzip, pass transparently also the first byte for .Z files - return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() - check Z_FINISH in inflate (thanks to Marc Schluper) - Implement deflateCopy (thanks to Adam Costello) - make static libraries by default in configure, add --shared option - move MSDOS or Windows specific files to directory msdos - suppress the notion of partial flush to simplify the interface (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) - suppress history buffer provided by application to simplify the interface (this feature was not implemented anyway in 1.0.4) - next_in and avail_in must be initialized before calling inflateInit or inflateInit2 - add EXPORT in all exported functions (for Windows DLL) - added Makefile.nt (thanks to Stephen Williams) - added the unsupported "contrib" directory: contrib/asm386/ by Gilles Vollant <info@winimage.com> 386 asm code replacing longest_match() contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu> A C++ I/O streams interface to the zlib gz* functions contrib/iostream2/ by Tyge Løvset <Tyge.Lovset@cmr.no> Another C++ I/O streams interface contrib/untgz/ by "Pedro A. Aranda Guti\irrez" <paag@tid.es> A very simple tar.gz file extractor using zlib contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl> How to use compress(), uncompress() and the gz* functions from VB - pass params -f (filtered data), -h (huffman only), -1 to -9 (compression level) in minigzip (thanks to Tom Lane) - use const for rommable constants in deflate - added test for gzseek and gztell in example.c - add undocumented function inflateSyncPoint() (hack for Paul Mackerras) - add undocumented function zError to convert error code to string (for Tim Smithers) - Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code - Use default memcpy for Symantec MSDOS compiler - Add EXPORT keyword for check_func (needed for Windows DLL) - add current directory to LD_LIBRARY_PATH for "make test" - create also a link for libz.so.1 - added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) - use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) - added -soname for Linux in configure (Chun-Chung Chen, - assign numbers to the exported functions in zlib.def (for Windows DLL) - add advice in zlib.h for best usage of deflateSetDictionary - work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) - allow compilation with ANSI keywords only enabled for TurboC in large model - avoid "versionString"[0] (Borland bug) - add NEED_DUMMY_RETURN for Borland - use variable z_verbose for tracing in debug mode (L. Peter Deutsch) - allow compilation with CC - defined STDC for OS/2 (David Charlap) - limit external names to 8 chars for MVS (Thomas Lund) - in minigzip.c, use static buffers only for 16-bit systems - fix suffix check for "minigzip -d foo.gz" - do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) - use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) - added makelcc.bat for lcc-win32 (Tom St Denis) - in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) - Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion - check for unistd.h in configure (for off_t) - remove useless check parameter in inflate_blocks_free - avoid useless assignment of s->check to itself in inflate_blocks_new - do not flush twice in gzclose (thanks to Ken Raeburn) - rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h - use NO_ERRNO_H instead of enumeration of operating systems with errno.h - work around buggy fclose on pipes for HP/UX - support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) - fix configure if CC is already equal to gcc Changes in 1.0.5 (3 Jan 98) - Fix inflate to terminate gracefully when fed corrupted or invalid data - Use const for rommable constants in inflate - Eliminate memory leaks on error conditions in inflate - Removed some vestigial code in inflate - Update web address in README Changes in 1.0.4 (24 Jul 96) - In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF bit, so the decompressor could decompress all the correct data but went on to attempt decompressing extra garbage data. This affected minigzip too - zlibVersion and gzerror return const char* (needed for DLL) - port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) - use z_error only for DEBUG (avoid problem with DLLs) Changes in 1.0.3 (2 Jul 96) - use z_streamp instead of z_stream *, which is now a far pointer in MSDOS small and medium models; this makes the library incompatible with previous versions for these models. (No effect in large model or on other systems.) - return OK instead of BUF_ERROR if previous deflate call returned with avail_out as zero but there is nothing to do - added memcmp for non STDC compilers - define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) - define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) - better check for 16-bit mode MSC (avoids problem with Symantec) Changes in 1.0.2 (23 May 96) - added Windows DLL support - added a function zlibVersion (for the DLL support) - fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) - Bytef is define's instead of typedef'd only for Borland C - avoid reading uninitialized memory in example.c - mention in README that the zlib format is now RFC1950 - updated Makefile.dj2 - added algorithm.doc Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] - fix array overlay in deflate.c which sometimes caused bad compressed data - fix inflate bug with empty stored block - fix MSDOS medium model which was broken in 0.99 - fix deflateParams() which could generate bad compressed data - Bytef is define'd instead of typedef'ed (work around Borland bug) - added an INDEX file - new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) - speed up adler32 for modern machines without auto-increment - added -ansi for IRIX in configure - static_init_done in trees.c is an int - define unlink as delete for VMS - fix configure for QNX - add configure branch for SCO and HPUX - avoid many warnings (unused variables, dead assignments, etc...) - no fdopen for BeOS - fix the Watcom fix for 32 bit mode (define FAR as empty) - removed redefinition of Byte for MKWERKS - work around an MWKERKS bug (incorrect merge of all .h files) Changes in 0.99 (27 Jan 96) - allow preset dictionary shared between compressor and decompressor - allow compression level 0 (no compression) - add deflateParams in zlib.h: allow dynamic change of compression level and compression strategy - test large buffers and deflateParams in example.c - add optional "configure" to build zlib as a shared library - suppress Makefile.qnx, use configure instead - fixed deflate for 64-bit systems (detected on Cray) - fixed inflate_blocks for 64-bit systems (detected on Alpha) - declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) - always return Z_BUF_ERROR when deflate() has nothing to do - deflateInit and inflateInit are now macros to allow version checking - prefix all global functions and types with z_ with -DZ_PREFIX - make falloc completely reentrant (inftrees.c) - fixed very unlikely race condition in ct_static_init - free in reverse order of allocation to help memory manager - use zlib-1.0/* instead of zlib/* inside the tar.gz - make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion -Wstrict-prototypes -Wmissing-prototypes" - allow gzread on concatenated .gz files - deflateEnd now returns Z_DATA_ERROR if it was premature - deflate is finally (?) fully deterministic (no matches beyond end of input) - Document Z_SYNC_FLUSH - add uninstall in Makefile - Check for __cpluplus in zlib.h - Better test in ct_align for partial flush - avoid harmless warnings for Borland C++ - initialize hash_head in deflate.c - avoid warning on fdopen (gzio.c) for HP cc -Aa - include stdlib.h for STDC compilers - include errno.h for Cray - ignore error if ranlib doesn't exist - call ranlib twice for NeXTSTEP - use exec_prefix instead of prefix for libz.a - renamed ct_* as _tr_* to avoid conflict with applications - clear z->msg in inflateInit2 before any error return - initialize opaque in example.c, gzio.c, deflate.c and inflate.c - fixed typo in zconf.h (_GNUC__ => __GNUC__) - check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) - fix typo in Make_vms.com (f$trnlnm -> f$getsyi) - in fcalloc, normalize pointer if size > 65520 bytes - don't use special fcalloc for 32 bit Borland C++ - use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc. - use Z_BINARY instead of BINARY - document that gzclose after gzdopen will close the file - allow "a" as mode in gzopen - fix error checking in gzread - allow skipping .gz extra-field on pipes - added reference to Perl interface in README - put the crc table in FAR data (I dislike more and more the medium model :) - added get_crc_table - added a dimension to all arrays (Borland C can't count) - workaround Borland C bug in declaration of inflate_codes_new & inflate_fast - guard against multiple inclusion of *.h (for precompiled header on Mac) - Watcom C pretends to be Microsoft C small model even in 32 bit mode - don't use unsized arrays to avoid silly warnings by Visual C++: warning C4746: 'inflate_mask' : unsized array treated as '__far' (what's wrong with far data in far model?) - define enum out of inflate_blocks_state to allow compilation with C++ Changes in 0.95 (16 Aug 95) - fix MSDOS small and medium model (now easier to adapt to any compiler) - inlined send_bits - fix the final (:-) bug for deflate with flush (output was correct but not completely flushed in rare occasions) - default window size is same for compression and decompression (it's now sufficient to set MAX_WBITS in zconf.h) - voidp -> voidpf and voidnp -> voidp (for consistency with other typedefs and because voidnp was not near in large model) Changes in 0.94 (13 Aug 95) - support MSDOS medium model - fix deflate with flush (could sometimes generate bad output) - fix deflateReset (zlib header was incorrectly suppressed) - added support for VMS - allow a compression level in gzopen() - gzflush now calls fflush - For deflate with flush, flush even if no more input is provided - rename libgz.a as libz.a - avoid complex expression in infcodes.c triggering Turbo C bug - work around a problem with gcc on Alpha (in INSERT_STRING) - don't use inline functions (problem with some gcc versions) - allow renaming of Byte, uInt, etc... with #define - avoid warning about (unused) pointer before start of array in deflate.c - avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c - avoid reserved word 'new' in trees.c Changes in 0.93 (25 June 95) - temporarily disable inline functions - make deflate deterministic - give enough lookahead for PARTIAL_FLUSH - Set binary mode for stdin/stdout in minigzip.c for OS/2 - don't even use signed char in inflate (not portable enough) - fix inflate memory leak for segmented architectures Changes in 0.92 (3 May 95) - don't assume that char is signed (problem on SGI) - Clear bit buffer when starting a stored block - no memcpy on Pyramid - suppressed inftest.c - optimized fill_window, put longest_match inline for gcc - optimized inflate on stored blocks - untabify all sources to simplify patches Changes in 0.91 (2 May 95) - Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h - Document the memory requirements in zconf.h - added "make install" - fix sync search logic in inflateSync - deflate(Z_FULL_FLUSH) now works even if output buffer too short - after inflateSync, don't scare people with just "lo world" - added support for DJGPP Changes in 0.9 (1 May 95) - don't assume that zalloc clears the allocated memory (the TurboC bug was Mark's bug after all :) - let again gzread copy uncompressed data unchanged (was working in 0.71) - deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented - added a test of inflateSync in example.c - moved MAX_WBITS to zconf.h because users might want to change that - document explicitly that zalloc(64K) on MSDOS must return a normalized pointer (zero offset) - added Makefiles for Microsoft C, Turbo C, Borland C++ - faster crc32() Changes in 0.8 (29 April 95) - added fast inflate (inffast.c) - deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this is incompatible with previous versions of zlib which returned Z_OK - work around a TurboC compiler bug (bad code for b << 0, see infutil.h) (actually that was not a compiler bug, see 0.81 above) - gzread no longer reads one extra byte in certain cases - In gzio destroy(), don't reference a freed structure - avoid many warnings for MSDOS - avoid the ERROR symbol which is used by MS Windows Changes in 0.71 (14 April 95) - Fixed more MSDOS compilation problems :( There is still a bug with TurboC large model Changes in 0.7 (14 April 95) - Added full inflate support - Simplified the crc32() interface. The pre- and post-conditioning (one's complement) is now done inside crc32(). WARNING: this is incompatible with previous versions; see zlib.h for the new usage Changes in 0.61 (12 April 95) - workaround for a bug in TurboC. example and minigzip now work on MSDOS Changes in 0.6 (11 April 95) - added minigzip.c - added gzdopen to reopen a file descriptor as gzFile - added transparent reading of non-gziped files in gzread - fixed bug in gzread (don't read crc as data) - fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose) - don't allocate big arrays in the stack (for MSDOS) - fix some MSDOS compilation problems Changes in 0.5: - do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but not yet Z_FULL_FLUSH - support decompression but only in a single step (forced Z_FINISH) - added opaque object for zalloc and zfree - added deflateReset and inflateReset - added a variable zlib_version for consistency checking - renamed the 'filter' parameter of deflateInit2 as 'strategy' Added Z_FILTERED and Z_HUFFMAN_ONLY constants Changes in 0.4: - avoid "zip" everywhere, use zlib instead of ziplib - suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush if compression method == 8 - added adler32 and crc32 - renamed deflateOptions as deflateInit2, call one or the other but not both - added the method parameter for deflateInit2 - added inflateInit2 - simplified considerably deflateInit and inflateInit by not supporting user-provided history buffer. This is supported only in deflateInit2 and inflateInit2 Changes in 0.3: - prefix all macro names with Z_ - use Z_FINISH instead of deflateEnd to finish compression - added Z_HUFFMAN_ONLY - added gzerror() |
Added compat/zlib/FAQ.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | Frequently Asked Questions about zlib If your question is not there, please check the zlib home page http://zlib.net/ which may have more recent information. The latest zlib FAQ is at http://zlib.net/zlib_faq.html 1. Is zlib Y2K-compliant? Yes. zlib doesn't handle dates. 2. Where can I get a Windows DLL version? The zlib sources can be compiled without change to produce a DLL. See the file win32/DLL_FAQ.txt in the zlib distribution. 3. Where can I get a Visual Basic interface to zlib? See * http://marknelson.us/1997/01/01/zlib-engine/ * win32/DLL_FAQ.txt in the zlib distribution 4. compress() returns Z_BUF_ERROR. Make sure that before the call of compress(), the length of the compressed buffer is equal to the available size of the compressed buffer and not zero. For Visual Basic, check that this parameter is passed by reference ("as any"), not by value ("as long"). 5. deflate() or inflate() returns Z_BUF_ERROR. Before making the call, make sure that avail_in and avail_out are not zero. When setting the parameter flush equal to Z_FINISH, also make sure that avail_out is big enough to allow processing all pending input. Note that a Z_BUF_ERROR is not fatal--another call to deflate() or inflate() can be made with more input or output space. A Z_BUF_ERROR may in fact be unavoidable depending on how the functions are used, since it is not possible to tell whether or not there is more output pending when strm.avail_out returns with zero. See http://zlib.net/zlib_how.html for a heavily annotated example. 6. Where's the zlib documentation (man pages, etc.)? It's in zlib.h . Examples of zlib usage are in the files test/example.c and test/minigzip.c, with more in examples/ . 7. Why don't you use GNU autoconf or libtool or ...? Because we would like to keep zlib as a very small and simple package. zlib is rather portable and doesn't need much configuration. 8. I found a bug in zlib. Most of the time, such problems are due to an incorrect usage of zlib. Please try to reproduce the problem with a small program and send the corresponding source to us at zlib@gzip.org . Do not send multi-megabyte data files without prior agreement. 9. Why do I get "undefined reference to gzputc"? If "make test" produces something like example.o(.text+0x154): undefined reference to `gzputc' check that you don't have old files libz.* in /usr/lib, /usr/local/lib or /usr/X11R6/lib. Remove any old versions, then do "make install". 10. I need a Delphi interface to zlib. See the contrib/delphi directory in the zlib distribution. 11. Can zlib handle .zip archives? Not by itself, no. See the directory contrib/minizip in the zlib distribution. 12. Can zlib handle .Z files? No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt the code of uncompress on your own. 13. How can I make a Unix shared library? By default a shared (and a static) library is built for Unix. So: make distclean ./configure make 14. How do I install a shared zlib library on Unix? After the above, then: make install However, many flavors of Unix come with a shared zlib already installed. Before going to the trouble of compiling a shared version of zlib and trying to install it, you may want to check if it's already there! If you can #include <zlib.h>, it's there. The -lz option will probably link to it. You can check the version at the top of zlib.h or with the ZLIB_VERSION symbol defined in zlib.h . 15. I have a question about OttoPDF. We are not the authors of OttoPDF. The real author is on the OttoPDF web site: Joel Hainley, jhainley@myndkryme.com. 16. Can zlib decode Flate data in an Adobe PDF file? Yes. See http://www.pdflib.com/ . To modify PDF forms, see http://sourceforge.net/projects/acroformtool/ . 17. Why am I getting this "register_frame_info not found" error on Solaris? After installing zlib 1.1.4 on Solaris 2.6, running applications using zlib generates an error such as: ld.so.1: rpm: fatal: relocation error: file /usr/local/lib/libz.so: symbol __register_frame_info: referenced symbol not found The symbol __register_frame_info is not part of zlib, it is generated by the C compiler (cc or gcc). You must recompile applications using zlib which have this problem. This problem is specific to Solaris. See http://www.sunfreeware.com for Solaris versions of zlib and applications using zlib. 18. Why does gzip give an error on a file I make with compress/deflate? The compress and deflate functions produce data in the zlib format, which is different and incompatible with the gzip format. The gz* functions in zlib on the other hand use the gzip format. Both the zlib and gzip formats use the same compressed data format internally, but have different headers and trailers around the compressed data. 19. Ok, so why are there two different formats? The gzip format was designed to retain the directory information about a single file, such as the name and last modification date. The zlib format on the other hand was designed for in-memory and communication channel applications, and has a much more compact header and trailer and uses a faster integrity check than gzip. 20. Well that's nice, but how do I make a gzip file in memory? You can request that deflate write the gzip format instead of the zlib format using deflateInit2(). You can also request that inflate decode the gzip format using inflateInit2(). Read zlib.h for more details. 21. Is zlib thread-safe? Yes. However any library routines that zlib uses and any application- provided memory allocation routines must also be thread-safe. zlib's gz* functions use stdio library routines, and most of zlib's functions use the library memory allocation routines by default. zlib's *Init* functions allow for the application to provide custom memory allocation routines. Of course, you should only operate on any given zlib or gzip stream from a single thread at a time. 22. Can I use zlib in my commercial application? Yes. Please read the license in zlib.h. 23. Is zlib under the GNU license? No. Please read the license in zlib.h. 24. The license says that altered source versions must be "plainly marked". So what exactly do I need to do to meet that requirement? You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In particular, the final version number needs to be changed to "f", and an identification string should be appended to ZLIB_VERSION. Version numbers x.x.x.f are reserved for modifications to zlib by others than the zlib maintainers. For example, if the version of the base zlib you are altering is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also update the version strings in deflate.c and inftrees.c. For altered source distributions, you should also note the origin and nature of the changes in zlib.h, as well as in ChangeLog and README, along with the dates of the alterations. The origin should include at least your name (or your company's name), and an email address to contact for help or issues with the library. Note that distributing a compiled zlib library along with zlib.h and zconf.h is also a source distribution, and so you should change ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes in zlib.h as you would for a full source distribution. 25. Will zlib work on a big-endian or little-endian architecture, and can I exchange compressed data between them? Yes and yes. 26. Will zlib work on a 64-bit machine? Yes. It has been tested on 64-bit machines, and has no dependence on any data types being limited to 32-bits in length. If you have any difficulties, please provide a complete problem report to zlib@gzip.org 27. Will zlib decompress data from the PKWare Data Compression Library? No. The PKWare DCL uses a completely different compressed data format than does PKZIP and zlib. However, you can look in zlib's contrib/blast directory for a possible solution to your problem. 28. Can I access data randomly in a compressed stream? No, not without some preparation. If when compressing you periodically use Z_FULL_FLUSH, carefully write all the pending data at those points, and keep an index of those locations, then you can start decompression at those points. You have to be careful to not use Z_FULL_FLUSH too often, since it can significantly degrade compression. Alternatively, you can scan a deflate stream once to generate an index, and then use that index for random access. See examples/zran.c . 29. Does zlib work on MVS, OS/390, CICS, etc.? It has in the past, but we have not heard of any recent evidence. There were working ports of zlib 1.1.4 to MVS, but those links no longer work. If you know of recent, successful applications of zlib on these operating systems, please let us know. Thanks. 30. Is there some simpler, easier to read version of inflate I can look at to understand the deflate format? First off, you should read RFC 1951. Second, yes. Look in zlib's contrib/puff directory. 31. Does zlib infringe on any patents? As far as we know, no. In fact, that was originally the whole point behind zlib. Look here for some more information: http://www.gzip.org/#faq11 32. Can zlib work with greater than 4 GB of data? Yes. inflate() and deflate() will process any amount of data correctly. Each call of inflate() or deflate() is limited to input and output chunks of the maximum value that can be stored in the compiler's "unsigned int" type, but there is no limit to the number of chunks. Note however that the strm.total_in and strm_total_out counters may be limited to 4 GB. These counters are provided as a convenience and are not used internally by inflate() or deflate(). The application can easily set up its own counters updated after each call of inflate() or deflate() to count beyond 4 GB. compress() and uncompress() may be limited to 4 GB, since they operate in a single call. gzseek() and gztell() may be limited to 4 GB depending on how zlib is compiled. See the zlibCompileFlags() function in zlib.h. The word "may" appears several times above since there is a 4 GB limit only if the compiler's "long" type is 32 bits. If the compiler's "long" type is 64 bits, then the limit is 16 exabytes. 33. Does zlib have any security vulnerabilities? The only one that we are aware of is potentially in gzprintf(). If zlib is compiled to use sprintf() or vsprintf(), then there is no protection against a buffer overflow of an 8K string space (or other value as set by gzbuffer()), other than the caller of gzprintf() assuring that the output will not exceed 8K. On the other hand, if zlib is compiled to use snprintf() or vsnprintf(), which should normally be the case, then there is no vulnerability. The ./configure script will display warnings if an insecure variation of sprintf() will be used by gzprintf(). Also the zlibCompileFlags() function will return information on what variant of sprintf() is used by gzprintf(). If you don't have snprintf() or vsnprintf() and would like one, you can find a portable implementation here: http://www.ijs.si/software/snprintf/ Note that you should be using the most recent version of zlib. Versions 1.1.3 and before were subject to a double-free vulnerability, and versions 1.2.1 and 1.2.2 were subject to an access exception when decompressing invalid compressed data. 34. Is there a Java version of zlib? Probably what you want is to use zlib in Java. zlib is already included as part of the Java SDK in the java.util.zip package. If you really want a version of zlib written in the Java language, look on the zlib home page for links: http://zlib.net/ . 35. I get this or that compiler or source-code scanner warning when I crank it up to maximally-pedantic. Can't you guys write proper code? Many years ago, we gave up attempting to avoid warnings on every compiler in the universe. It just got to be a waste of time, and some compilers were downright silly as well as contradicted each other. So now, we simply make sure that the code always works. 36. Valgrind (or some similar memory access checker) says that deflate is performing a conditional jump that depends on an uninitialized value. Isn't that a bug? No. That is intentional for performance reasons, and the output of deflate is not affected. This only started showing up recently since zlib 1.2.x uses malloc() by default for allocations, whereas earlier versions used calloc(), which zeros out the allocated memory. Even though the code was correct, versions 1.2.4 and later was changed to not stimulate these checkers. 37. Will zlib read the (insert any ancient or arcane format here) compressed data format? Probably not. Look in the comp.compression FAQ for pointers to various formats and associated software. 38. How can I encrypt/decrypt zip files with zlib? zlib doesn't support encryption. The original PKZIP encryption is very weak and can be broken with freely available programs. To get strong encryption, use GnuPG, http://www.gnupg.org/ , which already includes zlib compression. For PKZIP compatible "encryption", look at http://www.info-zip.org/ 39. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings? "gzip" is the gzip format, and "deflate" is the zlib format. They should probably have called the second one "zlib" instead to avoid confusion with the raw deflate compressed data format. While the HTTP 1.1 RFC 2616 correctly points to the zlib specification in RFC 1950 for the "deflate" transfer encoding, there have been reports of servers and browsers that incorrectly produce or expect raw deflate data per the deflate specification in RFC 1951, most notably Microsoft. So even though the "deflate" transfer encoding using the zlib format would be the more efficient approach (and in fact exactly what the zlib format was designed for), using the "gzip" transfer encoding is probably more reliable due to an unfortunate choice of name on the part of the HTTP 1.1 authors. Bottom line: use the gzip format for HTTP 1.1 encoding. 40. Does zlib support the new "Deflate64" format introduced by PKWare? No. PKWare has apparently decided to keep that format proprietary, since they have not documented it as they have previous compression formats. In any case, the compression improvements are so modest compared to other more modern approaches, that it's not worth the effort to implement. 41. I'm having a problem with the zip functions in zlib, can you help? There are no zip functions in zlib. You are probably using minizip by Giles Vollant, which is found in the contrib directory of zlib. It is not part of zlib. In fact none of the stuff in contrib is part of zlib. The files in there are not supported by the zlib authors. You need to contact the authors of the respective contribution for help. 42. The match.asm code in contrib is under the GNU General Public License. Since it's part of zlib, doesn't that mean that all of zlib falls under the GNU GPL? No. The files in contrib are not part of zlib. They were contributed by other authors and are provided as a convenience to the user within the zlib distribution. Each item in contrib has its own license. 43. Is zlib subject to export controls? What is its ECCN? zlib is not subject to export controls, and so is classified as EAR99. 44. Can you please sign these lengthy legal documents and fax them back to us so that we can use your software in our product? No. Go away. Shoo. |
Added compat/zlib/INDEX.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | CMakeLists.txt cmake build file ChangeLog history of changes FAQ Frequently Asked Questions about zlib INDEX this file Makefile dummy Makefile that tells you to ./configure Makefile.in template for Unix Makefile README guess what configure configure script for Unix make_vms.com makefile for VMS test/example.c zlib usages examples for build testing test/minigzip.c minimal gzip-like functionality for build testing test/infcover.c inf*.c code coverage for build coverage testing treebuild.xml XML description of source file dependencies zconf.h.cmakein zconf.h template for cmake zconf.h.in zconf.h template for configure zlib.3 Man page for zlib zlib.3.pdf Man page in PDF format zlib.map Linux symbol information zlib.pc.in Template for pkg-config descriptor zlib.pc.cmakein zlib.pc template for cmake zlib2ansi perl script to convert source files for C++ compilation amiga/ makefiles for Amiga SAS C as400/ makefiles for AS/400 doc/ documentation for formats and algorithms msdos/ makefiles for MSDOS nintendods/ makefile for Nintendo DS old/ makefiles for various architectures and zlib documentation files that have not yet been updated for zlib 1.2.x qnx/ makefiles for QNX watcom/ makefiles for OpenWatcom win32/ makefiles for Windows zlib public header files (required for library use): zconf.h zlib.h private source files used to build the zlib library: adler32.c compress.c crc32.c crc32.h deflate.c deflate.h gzclose.c gzguts.h gzlib.c gzread.c gzwrite.c infback.c inffast.c inffast.h inffixed.h inflate.c inflate.h inftrees.c inftrees.h trees.c trees.h uncompr.c zutil.c zutil.h source files for sample programs See examples/README.examples unsupported contributions by third parties See contrib/README.contrib |
Added compat/zlib/LICENSE.
> > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Copyright notice: (C) 1995-2022 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu |
Added compat/zlib/Makefile.
> > > > > | 1 2 3 4 5 | all: -@echo "Please use ./configure first. Thank you." distclean: make -f Makefile.in distclean |
Added compat/zlib/Makefile.in.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib # Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler # For conditions of distribution and use, see copyright notice in zlib.h # To compile and test, type: # ./configure; make test # Normally configure builds both a static and a shared library. # If you want to build just a static library, use: ./configure --static # To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type: # make install # To install in $HOME instead of /usr/local, use: # make install prefix=$HOME CC=cc CFLAGS=-O #CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 #CFLAGS=-g -DZLIB_DEBUG #CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ # -Wstrict-prototypes -Wmissing-prototypes SFLAGS=-O LDFLAGS= TEST_LIBS=-L. libz.a LDSHARED=$(CC) CPP=$(CC) -E STATICLIB=libz.a SHAREDLIB=libz.so SHAREDLIBV=libz.so.1.3.1 SHAREDLIBM=libz.so.1 LIBS=$(STATICLIB) $(SHAREDLIBV) AR=ar ARFLAGS=rc RANLIB=ranlib LDCONFIG=ldconfig LDSHAREDLIBC=-lc TAR=tar SHELL=/bin/sh EXE= prefix = /usr/local exec_prefix = ${prefix} libdir = ${exec_prefix}/lib sharedlibdir = ${libdir} includedir = ${prefix}/include mandir = ${prefix}/share/man man3dir = ${mandir}/man3 pkgconfigdir = ${libdir}/pkgconfig SRCDIR= ZINC= ZINCOUT=-I. OBJZ = adler32.o crc32.o deflate.o infback.o inffast.o inflate.o inftrees.o trees.o zutil.o OBJG = compress.o uncompr.o gzclose.o gzlib.o gzread.o gzwrite.o OBJC = $(OBJZ) $(OBJG) PIC_OBJZ = adler32.lo crc32.lo deflate.lo infback.lo inffast.lo inflate.lo inftrees.lo trees.lo zutil.lo PIC_OBJG = compress.lo uncompr.lo gzclose.lo gzlib.lo gzread.lo gzwrite.lo PIC_OBJC = $(PIC_OBJZ) $(PIC_OBJG) # to use the asm code: make OBJA=match.o, PIC_OBJA=match.lo OBJA = PIC_OBJA = OBJS = $(OBJC) $(OBJA) PIC_OBJS = $(PIC_OBJC) $(PIC_OBJA) all: static shared static: example$(EXE) minigzip$(EXE) shared: examplesh$(EXE) minigzipsh$(EXE) all64: example64$(EXE) minigzip64$(EXE) check: test test: all teststatic testshared teststatic: static @TMPST=tmpst_$$; \ if echo hello world | ${QEMU_RUN} ./minigzip | ${QEMU_RUN} ./minigzip -d && ${QEMU_RUN} ./example $$TMPST ; then \ echo ' *** zlib test OK ***'; \ else \ echo ' *** zlib test FAILED ***'; false; \ fi @rm -f tmpst_$$ testshared: shared @LD_LIBRARY_PATH=`pwd`:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \ LD_LIBRARYN32_PATH=`pwd`:$(LD_LIBRARYN32_PATH) ; export LD_LIBRARYN32_PATH; \ DYLD_LIBRARY_PATH=`pwd`:$(DYLD_LIBRARY_PATH) ; export DYLD_LIBRARY_PATH; \ SHLIB_PATH=`pwd`:$(SHLIB_PATH) ; export SHLIB_PATH; \ TMPSH=tmpsh_$$; \ if echo hello world | ${QEMU_RUN} ./minigzipsh | ${QEMU_RUN} ./minigzipsh -d && ${QEMU_RUN} ./examplesh $$TMPSH; then \ echo ' *** zlib shared test OK ***'; \ else \ echo ' *** zlib shared test FAILED ***'; false; \ fi @rm -f tmpsh_$$ test64: all64 @TMP64=tmp64_$$; \ if echo hello world | ${QEMU_RUN} ./minigzip64 | ${QEMU_RUN} ./minigzip64 -d && ${QEMU_RUN} ./example64 $$TMP64; then \ echo ' *** zlib 64-bit test OK ***'; \ else \ echo ' *** zlib 64-bit test FAILED ***'; false; \ fi @rm -f tmp64_$$ infcover.o: $(SRCDIR)test/infcover.c $(SRCDIR)zlib.h zconf.h $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/infcover.c infcover: infcover.o libz.a $(CC) $(CFLAGS) -o $@ infcover.o libz.a cover: infcover rm -f *.gcda ${QEMU_RUN} ./infcover gcov inf*.c libz.a: $(OBJS) $(AR) $(ARFLAGS) $@ $(OBJS) -@ ($(RANLIB) $@ || true) >/dev/null 2>&1 match.o: match.S $(CPP) match.S > _match.s $(CC) -c _match.s mv _match.o match.o rm -f _match.s match.lo: match.S $(CPP) match.S > _match.s $(CC) -c -fPIC _match.s mv _match.o match.lo rm -f _match.s example.o: $(SRCDIR)test/example.c $(SRCDIR)zlib.h zconf.h $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/example.c minigzip.o: $(SRCDIR)test/minigzip.c $(SRCDIR)zlib.h zconf.h $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/minigzip.c example64.o: $(SRCDIR)test/example.c $(SRCDIR)zlib.h zconf.h $(CC) $(CFLAGS) $(ZINCOUT) -D_FILE_OFFSET_BITS=64 -c -o $@ $(SRCDIR)test/example.c minigzip64.o: $(SRCDIR)test/minigzip.c $(SRCDIR)zlib.h zconf.h $(CC) $(CFLAGS) $(ZINCOUT) -D_FILE_OFFSET_BITS=64 -c -o $@ $(SRCDIR)test/minigzip.c adler32.o: $(SRCDIR)adler32.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)adler32.c crc32.o: $(SRCDIR)crc32.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)crc32.c deflate.o: $(SRCDIR)deflate.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)deflate.c infback.o: $(SRCDIR)infback.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)infback.c inffast.o: $(SRCDIR)inffast.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)inffast.c inflate.o: $(SRCDIR)inflate.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)inflate.c inftrees.o: $(SRCDIR)inftrees.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)inftrees.c trees.o: $(SRCDIR)trees.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)trees.c zutil.o: $(SRCDIR)zutil.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)zutil.c compress.o: $(SRCDIR)compress.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)compress.c uncompr.o: $(SRCDIR)uncompr.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)uncompr.c gzclose.o: $(SRCDIR)gzclose.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzclose.c gzlib.o: $(SRCDIR)gzlib.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzlib.c gzread.o: $(SRCDIR)gzread.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzread.c gzwrite.o: $(SRCDIR)gzwrite.c $(CC) $(CFLAGS) $(ZINC) -c -o $@ $(SRCDIR)gzwrite.c adler32.lo: $(SRCDIR)adler32.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/adler32.o $(SRCDIR)adler32.c -@mv objs/adler32.o $@ crc32.lo: $(SRCDIR)crc32.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/crc32.o $(SRCDIR)crc32.c -@mv objs/crc32.o $@ deflate.lo: $(SRCDIR)deflate.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/deflate.o $(SRCDIR)deflate.c -@mv objs/deflate.o $@ infback.lo: $(SRCDIR)infback.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/infback.o $(SRCDIR)infback.c -@mv objs/infback.o $@ inffast.lo: $(SRCDIR)inffast.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/inffast.o $(SRCDIR)inffast.c -@mv objs/inffast.o $@ inflate.lo: $(SRCDIR)inflate.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/inflate.o $(SRCDIR)inflate.c -@mv objs/inflate.o $@ inftrees.lo: $(SRCDIR)inftrees.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/inftrees.o $(SRCDIR)inftrees.c -@mv objs/inftrees.o $@ trees.lo: $(SRCDIR)trees.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/trees.o $(SRCDIR)trees.c -@mv objs/trees.o $@ zutil.lo: $(SRCDIR)zutil.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/zutil.o $(SRCDIR)zutil.c -@mv objs/zutil.o $@ compress.lo: $(SRCDIR)compress.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/compress.o $(SRCDIR)compress.c -@mv objs/compress.o $@ uncompr.lo: $(SRCDIR)uncompr.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/uncompr.o $(SRCDIR)uncompr.c -@mv objs/uncompr.o $@ gzclose.lo: $(SRCDIR)gzclose.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/gzclose.o $(SRCDIR)gzclose.c -@mv objs/gzclose.o $@ gzlib.lo: $(SRCDIR)gzlib.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/gzlib.o $(SRCDIR)gzlib.c -@mv objs/gzlib.o $@ gzread.lo: $(SRCDIR)gzread.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/gzread.o $(SRCDIR)gzread.c -@mv objs/gzread.o $@ gzwrite.lo: $(SRCDIR)gzwrite.c -@mkdir objs 2>/dev/null || test -d objs $(CC) $(SFLAGS) $(ZINC) -fPIC -c -o objs/gzwrite.o $(SRCDIR)gzwrite.c -@mv objs/gzwrite.o $@ placebo $(SHAREDLIBV): $(PIC_OBJS) libz.a $(LDSHARED) $(SFLAGS) -o $@ $(PIC_OBJS) $(LDSHAREDLIBC) $(LDFLAGS) rm -f $(SHAREDLIB) $(SHAREDLIBM) ln -s $@ $(SHAREDLIB) ln -s $@ $(SHAREDLIBM) -@rmdir objs example$(EXE): example.o $(STATICLIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ example.o $(TEST_LIBS) minigzip$(EXE): minigzip.o $(STATICLIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip.o $(TEST_LIBS) examplesh$(EXE): example.o $(SHAREDLIBV) $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) -L. $(SHAREDLIBV) minigzipsh$(EXE): minigzip.o $(SHAREDLIBV) $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS) -L. $(SHAREDLIBV) example64$(EXE): example64.o $(STATICLIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ example64.o $(TEST_LIBS) minigzip64$(EXE): minigzip64.o $(STATICLIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip64.o $(TEST_LIBS) install-libs: $(LIBS) -@if [ ! -d $(DESTDIR)$(exec_prefix) ]; then mkdir -p $(DESTDIR)$(exec_prefix); fi -@if [ ! -d $(DESTDIR)$(libdir) ]; then mkdir -p $(DESTDIR)$(libdir); fi -@if [ ! -d $(DESTDIR)$(sharedlibdir) ]; then mkdir -p $(DESTDIR)$(sharedlibdir); fi -@if [ ! -d $(DESTDIR)$(man3dir) ]; then mkdir -p $(DESTDIR)$(man3dir); fi -@if [ ! -d $(DESTDIR)$(pkgconfigdir) ]; then mkdir -p $(DESTDIR)$(pkgconfigdir); fi rm -f $(DESTDIR)$(libdir)/$(STATICLIB) cp $(STATICLIB) $(DESTDIR)$(libdir) chmod 644 $(DESTDIR)$(libdir)/$(STATICLIB) -@($(RANLIB) $(DESTDIR)$(libdir)/libz.a || true) >/dev/null 2>&1 -@if test -n "$(SHAREDLIBV)"; then \ rm -f $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV); \ cp $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir); \ echo "cp $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)"; \ chmod 755 $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV); \ echo "chmod 755 $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBV)"; \ rm -f $(DESTDIR)$(sharedlibdir)/$(SHAREDLIB) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBM); \ ln -s $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIB); \ ln -s $(SHAREDLIBV) $(DESTDIR)$(sharedlibdir)/$(SHAREDLIBM); \ ($(LDCONFIG) || true) >/dev/null 2>&1; \ fi rm -f $(DESTDIR)$(man3dir)/zlib.3 cp $(SRCDIR)zlib.3 $(DESTDIR)$(man3dir) chmod 644 $(DESTDIR)$(man3dir)/zlib.3 rm -f $(DESTDIR)$(pkgconfigdir)/zlib.pc cp zlib.pc $(DESTDIR)$(pkgconfigdir) chmod 644 $(DESTDIR)$(pkgconfigdir)/zlib.pc # The ranlib in install is needed on NeXTSTEP which checks file times # ldconfig is for Linux install: install-libs -@if [ ! -d $(DESTDIR)$(includedir) ]; then mkdir -p $(DESTDIR)$(includedir); fi rm -f $(DESTDIR)$(includedir)/zlib.h $(DESTDIR)$(includedir)/zconf.h cp $(SRCDIR)zlib.h zconf.h $(DESTDIR)$(includedir) chmod 644 $(DESTDIR)$(includedir)/zlib.h $(DESTDIR)$(includedir)/zconf.h uninstall: cd $(DESTDIR)$(includedir) && rm -f zlib.h zconf.h cd $(DESTDIR)$(libdir) && rm -f libz.a; \ if test -n "$(SHAREDLIBV)" -a -f $(SHAREDLIBV); then \ rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \ fi cd $(DESTDIR)$(man3dir) && rm -f zlib.3 cd $(DESTDIR)$(pkgconfigdir) && rm -f zlib.pc docs: zlib.3.pdf zlib.3.pdf: $(SRCDIR)zlib.3 groff -mandoc -f H -T ps $(SRCDIR)zlib.3 | ps2pdf - $@ zconf.h.cmakein: $(SRCDIR)zconf.h.in -@ TEMPFILE=zconfh_$$; \ echo "/#define ZCONF_H/ a\\\\\n#cmakedefine Z_PREFIX\\\\\n#cmakedefine Z_HAVE_UNISTD_H\n" >> $$TEMPFILE &&\ sed -f $$TEMPFILE $(SRCDIR)zconf.h.in > $@ &&\ touch -r $(SRCDIR)zconf.h.in $@ &&\ rm $$TEMPFILE zconf: $(SRCDIR)zconf.h.in cp -p $(SRCDIR)zconf.h.in zconf.h minizip-test: static cd contrib/minizip && { CC="$(CC)" CFLAGS="$(CFLAGS)" $(MAKE) test ; cd ../.. ; } minizip-clean: cd contrib/minizip && { $(MAKE) clean ; cd ../.. ; } mostlyclean: clean clean: minizip-clean rm -f *.o *.lo *~ \ example$(EXE) minigzip$(EXE) examplesh$(EXE) minigzipsh$(EXE) \ example64$(EXE) minigzip64$(EXE) \ infcover \ libz.* foo.gz so_locations \ _match.s maketree contrib/infback9/*.o rm -rf objs rm -f *.gcda *.gcno *.gcov rm -f contrib/infback9/*.gcda contrib/infback9/*.gcno contrib/infback9/*.gcov maintainer-clean: distclean distclean: clean zconf zconf.h.cmakein rm -f Makefile zlib.pc configure.log -@rm -f .DS_Store @if [ -f Makefile.in ]; then \ printf 'all:\n\t-@echo "Please use ./configure first. Thank you."\n' > Makefile ; \ printf '\ndistclean:\n\tmake -f Makefile.in distclean\n' >> Makefile ; \ touch -r $(SRCDIR)Makefile.in Makefile ; fi tags: etags $(SRCDIR)*.[ch] adler32.o zutil.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h gzclose.o gzlib.o gzread.o gzwrite.o: $(SRCDIR)zlib.h zconf.h $(SRCDIR)gzguts.h compress.o example.o minigzip.o uncompr.o: $(SRCDIR)zlib.h zconf.h crc32.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)crc32.h deflate.o: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h infback.o inflate.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h $(SRCDIR)inffixed.h inffast.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h inftrees.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h trees.o: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)trees.h adler32.lo zutil.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h gzclose.lo gzlib.lo gzread.lo gzwrite.lo: $(SRCDIR)zlib.h zconf.h $(SRCDIR)gzguts.h compress.lo example.lo minigzip.lo uncompr.lo: $(SRCDIR)zlib.h zconf.h crc32.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)crc32.h deflate.lo: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h infback.lo inflate.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h $(SRCDIR)inffixed.h inffast.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h inftrees.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h trees.lo: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)trees.h |
Added compat/zlib/README.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ZLIB DATA COMPRESSION LIBRARY zlib 1.3.1 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). All functions of the compression library are documented in the file zlib.h (volunteer to write man pages welcome, contact zlib@gzip.org). A usage example of the library is given in the file test/example.c which also tests that the library is working correctly. Another example is given in the file test/minigzip.c. The compression library itself is composed of all source files in the root directory. To compile all files and run the test program, follow the instructions given at the top of Makefile.in. In short "./configure; make test", and if that goes well, "make install" should work for most flavors of Unix. For Windows, use one of the special makefiles in win32/ or contrib/vstudio/ . For VMS, use make_vms.com. Questions about zlib should be sent to <zlib@gzip.org>, or to Gilles Vollant <info@winimage.com> for the Windows DLL version. The zlib home page is http://zlib.net/ . Before reporting a problem, please check this site to verify that you have the latest version of zlib; otherwise get the latest version and check whether the problem still exists or not. PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at https://marknelson.us/posts/1997/01/01/zlib-engine.html . The changes made in version 1.3.1 are documented in the file ChangeLog. Unsupported third party contributions are provided in directory contrib/ . zlib is available in Java using the java.util.zip package. Follow the API Documentation link at: https://docs.oracle.com/search/?q=java.util.zip . A Perl interface to zlib and bzip2 written by Paul Marquess <pmqs@cpan.org> can be found at https://github.com/pmqs/IO-Compress . A Python interface to zlib written by A.M. Kuchling <amk@amk.ca> is available in Python 1.5 and later versions, see http://docs.python.org/library/zlib.html . zlib is built into tcl: http://wiki.tcl.tk/4610 . An experimental package to read and write files in .zip format, written on top of zlib by Gilles Vollant <info@winimage.com>, is available in the contrib/minizip directory of zlib. Notes for some targets: - For Windows DLL versions, please see win32/DLL_FAQ.txt - For 64-bit Irix, deflate.c must be compiled without any optimization. With -O, one libpng test fails. The test works in 32 bit mode (with the -n32 compiler flag). The compiler bug has been reported to SGI. - zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works when compiled with cc. - On Digital Unix 4.0D (formerly OSF/1) on AlphaServer, the cc option -std1 is necessary to get gzprintf working correctly. This is done by configure. - zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with other compilers. Use "make test" to check your compiler. - gzdopen is not supported on RISCOS or BEOS. - For PalmOs, see http://palmzlib.sourceforge.net/ Acknowledgments: The deflate format used by zlib was defined by Phil Katz. The deflate and zlib specifications were written by L. Peter Deutsch. Thanks to all the people who reported problems and suggested various improvements in zlib; they are too numerous to cite here. Copyright notice: (C) 1995-2024 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu If you use the zlib library in a product, we would appreciate *not* receiving lengthy legal documents to sign. The sources are provided for free but without warranty of any kind. The library has been entirely written by Jean-loup Gailly and Mark Adler; it does not include third-party code. We make all contributions to and distributions of this project solely in our personal capacity, and are not conveying any rights to any intellectual property of any third parties. If you redistribute modified sources, we would appreciate that you include in the file ChangeLog history information documenting your changes. Please read the FAQ for more information on the distribution of modified source versions. |
Added compat/zlib/adler32.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 | /* adler32.c -- compute the Adler-32 checksum of a data stream * Copyright (C) 1995-2011, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zutil.h" #define BASE 65521U /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ #define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); /* use NO_DIVIDE if your processor does not do division in hardware -- try it both ways to see which is faster */ #ifdef NO_DIVIDE /* note that this assumes BASE is 65521, where 65536 % 65521 == 15 (thank you to John Reiser for pointing this out) */ # define CHOP(a) \ do { \ unsigned long tmp = a >> 16; \ a &= 0xffffUL; \ a += (tmp << 4) - tmp; \ } while (0) # define MOD28(a) \ do { \ CHOP(a); \ if (a >= BASE) a -= BASE; \ } while (0) # define MOD(a) \ do { \ CHOP(a); \ MOD28(a); \ } while (0) # define MOD63(a) \ do { /* this assumes a is not negative */ \ z_off64_t tmp = a >> 32; \ a &= 0xffffffffL; \ a += (tmp << 8) - (tmp << 5) + tmp; \ tmp = a >> 16; \ a &= 0xffffL; \ a += (tmp << 4) - tmp; \ tmp = a >> 16; \ a &= 0xffffL; \ a += (tmp << 4) - tmp; \ if (a >= BASE) a -= BASE; \ } while (0) #else # define MOD(a) a %= BASE # define MOD28(a) a %= BASE # define MOD63(a) a %= BASE #endif /* ========================================================================= */ uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, z_size_t len) { unsigned long sum2; unsigned n; /* split Adler-32 into component sums */ sum2 = (adler >> 16) & 0xffff; adler &= 0xffff; /* in case user likes doing a byte at a time, keep it fast */ if (len == 1) { adler += buf[0]; if (adler >= BASE) adler -= BASE; sum2 += adler; if (sum2 >= BASE) sum2 -= BASE; return adler | (sum2 << 16); } /* initial Adler-32 value (deferred check for len == 1 speed) */ if (buf == Z_NULL) return 1L; /* in case short lengths are provided, keep it somewhat fast */ if (len < 16) { while (len--) { adler += *buf++; sum2 += adler; } if (adler >= BASE) adler -= BASE; MOD28(sum2); /* only added so many BASE's */ return adler | (sum2 << 16); } /* do length NMAX blocks -- requires just one modulo operation */ while (len >= NMAX) { len -= NMAX; n = NMAX / 16; /* NMAX is divisible by 16 */ do { DO16(buf); /* 16 sums unrolled */ buf += 16; } while (--n); MOD(adler); MOD(sum2); } /* do remaining bytes (less than NMAX, still just one modulo) */ if (len) { /* avoid modulos if none remaining */ while (len >= 16) { len -= 16; DO16(buf); buf += 16; } while (len--) { adler += *buf++; sum2 += adler; } MOD(adler); MOD(sum2); } /* return recombined sums */ return adler | (sum2 << 16); } /* ========================================================================= */ uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) { return adler32_z(adler, buf, len); } /* ========================================================================= */ local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2) { unsigned long sum1; unsigned long sum2; unsigned rem; /* for negative len, return invalid adler32 as a clue for debugging */ if (len2 < 0) return 0xffffffffUL; /* the derivation of this formula is left as an exercise for the reader */ MOD63(len2); /* assumes len2 >= 0 */ rem = (unsigned)len2; sum1 = adler1 & 0xffff; sum2 = rem * sum1; MOD(sum2); sum1 += (adler2 & 0xffff) + BASE - 1; sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; if (sum1 >= BASE) sum1 -= BASE; if (sum1 >= BASE) sum1 -= BASE; if (sum2 >= ((unsigned long)BASE << 1)) sum2 -= ((unsigned long)BASE << 1); if (sum2 >= BASE) sum2 -= BASE; return sum1 | (sum2 << 16); } /* ========================================================================= */ uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) { return adler32_combine_(adler1, adler2, len2); } uLong ZEXPORT adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2) { return adler32_combine_(adler1, adler2, len2); } |
Added compat/zlib/amiga/Makefile.pup.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Amiga powerUP (TM) Makefile # makefile for libpng and SAS C V6.58/7.00 PPC compiler # Copyright (C) 1998 by Andreas R. Kleinert LIBNAME = libzip.a CC = scppc CFLAGS = NOSTKCHK NOSINT OPTIMIZE OPTGO OPTPEEP OPTINLOCAL OPTINL \ OPTLOOP OPTRDEP=8 OPTDEP=8 OPTCOMP=8 NOVER AR = ppc-amigaos-ar cr RANLIB = ppc-amigaos-ranlib LD = ppc-amigaos-ld -r LDFLAGS = -o LDLIBS = LIB:scppc.a LIB:end.o RM = delete quiet OBJS = adler32.o compress.o crc32.o gzclose.o gzlib.o gzread.o gzwrite.o \ uncompr.o deflate.o trees.o zutil.o inflate.o infback.o inftrees.o inffast.o TEST_OBJS = example.o minigzip.o all: example minigzip check: test test: all example echo hello world | minigzip | minigzip -d $(LIBNAME): $(OBJS) $(AR) $@ $(OBJS) -$(RANLIB) $@ example: example.o $(LIBNAME) $(LD) $(LDFLAGS) $@ LIB:c_ppc.o $@.o $(LIBNAME) $(LDLIBS) minigzip: minigzip.o $(LIBNAME) $(LD) $(LDFLAGS) $@ LIB:c_ppc.o $@.o $(LIBNAME) $(LDLIBS) mostlyclean: clean clean: $(RM) *.o example minigzip $(LIBNAME) foo.gz zip: zip -ul9 zlib README ChangeLog Makefile Make????.??? Makefile.?? \ descrip.mms *.[ch] tgz: cd ..; tar cfz zlib/zlib.tgz zlib/README zlib/ChangeLog zlib/Makefile \ zlib/Make????.??? zlib/Makefile.?? zlib/descrip.mms zlib/*.[ch] # DO NOT DELETE THIS LINE -- make depend depends on it. adler32.o: zlib.h zconf.h compress.o: zlib.h zconf.h crc32.o: crc32.h zlib.h zconf.h deflate.o: deflate.h zutil.h zlib.h zconf.h example.o: zlib.h zconf.h gzclose.o: zlib.h zconf.h gzguts.h gzlib.o: zlib.h zconf.h gzguts.h gzread.o: zlib.h zconf.h gzguts.h gzwrite.o: zlib.h zconf.h gzguts.h inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inftrees.o: zutil.h zlib.h zconf.h inftrees.h minigzip.o: zlib.h zconf.h trees.o: deflate.h zutil.h zlib.h zconf.h trees.h uncompr.o: zlib.h zconf.h zutil.o: zutil.h zlib.h zconf.h |
Added compat/zlib/amiga/Makefile.sas.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # SMakefile for zlib # Modified from the standard UNIX Makefile Copyright Jean-loup Gailly # Osma Ahvenlampi <Osma.Ahvenlampi@hut.fi> # Amiga, SAS/C 6.56 & Smake CC=sc CFLAGS=OPT #CFLAGS=OPT CPU=68030 #CFLAGS=DEBUG=LINE LDFLAGS=LIB z.lib SCOPTIONS=OPTSCHED OPTINLINE OPTALIAS OPTTIME OPTINLOCAL STRMERGE \ NOICONS PARMS=BOTH NOSTACKCHECK UTILLIB NOVERSION ERRORREXX \ DEF=POSTINC OBJS = adler32.o compress.o crc32.o gzclose.o gzlib.o gzread.o gzwrite.o \ uncompr.o deflate.o trees.o zutil.o inflate.o infback.o inftrees.o inffast.o TEST_OBJS = example.o minigzip.o all: SCOPTIONS example minigzip check: test test: all example echo hello world | minigzip | minigzip -d install: z.lib copy clone zlib.h zconf.h INCLUDE: copy clone z.lib LIB: z.lib: $(OBJS) oml z.lib r $(OBJS) example: example.o z.lib $(CC) $(CFLAGS) LINK TO $@ example.o $(LDFLAGS) minigzip: minigzip.o z.lib $(CC) $(CFLAGS) LINK TO $@ minigzip.o $(LDFLAGS) mostlyclean: clean clean: -delete force quiet example minigzip *.o z.lib foo.gz *.lnk SCOPTIONS SCOPTIONS: Makefile.sas copy to $@ <from < $(SCOPTIONS) < # DO NOT DELETE THIS LINE -- make depend depends on it. adler32.o: zlib.h zconf.h compress.o: zlib.h zconf.h crc32.o: crc32.h zlib.h zconf.h deflate.o: deflate.h zutil.h zlib.h zconf.h example.o: zlib.h zconf.h gzclose.o: zlib.h zconf.h gzguts.h gzlib.o: zlib.h zconf.h gzguts.h gzread.o: zlib.h zconf.h gzguts.h gzwrite.o: zlib.h zconf.h gzguts.h inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inftrees.o: zutil.h zlib.h zconf.h inftrees.h minigzip.o: zlib.h zconf.h trees.o: deflate.h zutil.h zlib.h zconf.h trees.h uncompr.o: zlib.h zconf.h zutil.o: zutil.h zlib.h zconf.h |
Added compat/zlib/compress.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 | /* compress.c -- compress a memory buffer * Copyright (C) 1995-2005, 2014, 2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #define ZLIB_INTERNAL #include "zlib.h" /* =========================================================================== Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least 0.1% larger than sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level) { z_stream stream; int err; const uInt max = (uInt)-1; uLong left; left = *destLen; *destLen = 0; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; stream.opaque = (voidpf)0; err = deflateInit(&stream, level); if (err != Z_OK) return err; stream.next_out = dest; stream.avail_out = 0; stream.next_in = (z_const Bytef *)source; stream.avail_in = 0; do { if (stream.avail_out == 0) { stream.avail_out = left > (uLong)max ? max : (uInt)left; left -= stream.avail_out; } if (stream.avail_in == 0) { stream.avail_in = sourceLen > (uLong)max ? max : (uInt)sourceLen; sourceLen -= stream.avail_in; } err = deflate(&stream, sourceLen ? Z_NO_FLUSH : Z_FINISH); } while (err == Z_OK); *destLen = stream.total_out; deflateEnd(&stream); return err == Z_STREAM_END ? Z_OK : err; } /* =========================================================================== */ int ZEXPORT compress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen) { return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); } /* =========================================================================== If the default memLevel or windowBits for deflateInit() is changed, then this function needs to be updated. */ uLong ZEXPORT compressBound(uLong sourceLen) { return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13; } |
Added compat/zlib/configure.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/bin/sh # configure script for zlib. # # Normally configure builds both a static and a shared library. # If you want to build just a static library, use: ./configure --static # # To impose specific compiler or flags or install directory, use for example: # prefix=$HOME CC=cc CFLAGS="-O4" ./configure # or for csh/tcsh users: # (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure) # Incorrect settings of CC or CFLAGS may prevent creating a shared library. # If you have problems, try without defining CC and CFLAGS before reporting # an error. # start off configure.log echo -------------------- >> configure.log echo $0 $* >> configure.log date >> configure.log # get source directory SRCDIR=`dirname $0` if test $SRCDIR = "."; then ZINC="" ZINCOUT="-I." SRCDIR="" else ZINC='-I. -include zconf.h' ZINCOUT='-I. -I$(SRCDIR)' SRCDIR="$SRCDIR/" fi # set command prefix for cross-compilation if [ -n "${CHOST}" ]; then uname=${CHOST} mname=${CHOST} CROSS_PREFIX="${CHOST}-" else mname=`(uname -a || echo unknown) 2>/dev/null` fi # destination name for static library STATICLIB=libz.a # extract zlib version numbers from zlib.h VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < ${SRCDIR}zlib.h` VER3=`echo ${VER}|sed -n -e 's/\([0-9]\{1,\}\(\\.[0-9]\{1,\}\)\{1,2\}\).*/\1/p'` VER1=`echo ${VER}|sed -n -e 's/\([0-9]\{1,\}\)\\..*/\1/p'` # establish commands for library building if "${CROSS_PREFIX}ar" --version >/dev/null 2>/dev/null || test $? -lt 126; then AR=${AR-"${CROSS_PREFIX}ar"} test -n "${CROSS_PREFIX}" && echo Using ${AR} | tee -a configure.log else AR=${AR-"ar"} test -n "${CROSS_PREFIX}" && echo Using ${AR} | tee -a configure.log fi ARFLAGS=${ARFLAGS-"rc"} if "${CROSS_PREFIX}ranlib" --version >/dev/null 2>/dev/null || test $? -lt 126; then RANLIB=${RANLIB-"${CROSS_PREFIX}ranlib"} test -n "${CROSS_PREFIX}" && echo Using ${RANLIB} | tee -a configure.log else RANLIB=${RANLIB-"ranlib"} fi if "${CROSS_PREFIX}nm" --version >/dev/null 2>/dev/null || test $? -lt 126; then NM=${NM-"${CROSS_PREFIX}nm"} test -n "${CROSS_PREFIX}" && echo Using ${NM} | tee -a configure.log else NM=${NM-"nm"} fi # set defaults before processing command line options LDCONFIG=${LDCONFIG-"ldconfig"} LDSHAREDLIBC="${LDSHAREDLIBC--lc}" ARCHS= prefix=${prefix-/usr/local} exec_prefix=${exec_prefix-'${prefix}'} libdir=${libdir-'${exec_prefix}/lib'} sharedlibdir=${sharedlibdir-'${libdir}'} includedir=${includedir-'${prefix}/include'} mandir=${mandir-'${prefix}/share/man'} shared_ext='.so' shared=1 solo=0 cover=0 zprefix=0 zconst=0 build64=0 gcc=0 warn=0 debug=0 address=0 memory=0 old_cc="$CC" old_cflags="$CFLAGS" OBJC='$(OBJZ) $(OBJG)' PIC_OBJC='$(PIC_OBJZ) $(PIC_OBJG)' # leave this script, optionally in a bad way leave() { if test "$*" != "0"; then echo "** $0 aborting." | tee -a configure.log fi rm -rf $test.[co] $test $test$shared_ext $test.gcno $test.dSYM ./--version echo -------------------- >> configure.log echo >> configure.log echo >> configure.log exit $1 } # process command line options while test $# -ge 1 do case "$1" in -h* | --help) echo 'usage:' | tee -a configure.log echo ' configure [--const] [--zprefix] [--prefix=PREFIX] [--eprefix=EXPREFIX]' | tee -a configure.log echo ' [--static] [--64] [--libdir=LIBDIR] [--sharedlibdir=LIBDIR]' | tee -a configure.log echo ' [--includedir=INCLUDEDIR] [--archs="-arch i386 -arch x86_64"]' | tee -a configure.log exit 0 ;; -p*=* | --prefix=*) prefix=`echo $1 | sed 's/.*=//'`; shift ;; -e*=* | --eprefix=*) exec_prefix=`echo $1 | sed 's/.*=//'`; shift ;; -l*=* | --libdir=*) libdir=`echo $1 | sed 's/.*=//'`; shift ;; --sharedlibdir=*) sharedlibdir=`echo $1 | sed 's/.*=//'`; shift ;; -i*=* | --includedir=*) includedir=`echo $1 | sed 's/.*=//'`;shift ;; -u*=* | --uname=*) uname=`echo $1 | sed 's/.*=//'`;shift ;; -p* | --prefix) prefix="$2"; shift; shift ;; -e* | --eprefix) exec_prefix="$2"; shift; shift ;; -l* | --libdir) libdir="$2"; shift; shift ;; -i* | --includedir) includedir="$2"; shift; shift ;; -s* | --shared | --enable-shared) shared=1; shift ;; -t | --static) shared=0; shift ;; --solo) solo=1; shift ;; --cover) cover=1; shift ;; -z* | --zprefix) zprefix=1; shift ;; -6* | --64) build64=1; shift ;; -a*=* | --archs=*) ARCHS=`echo $1 | sed 's/.*=//'`; shift ;; --sysconfdir=*) echo "ignored option: --sysconfdir" | tee -a configure.log; shift ;; --localstatedir=*) echo "ignored option: --localstatedir" | tee -a configure.log; shift ;; -c* | --const) zconst=1; shift ;; -w* | --warn) warn=1; shift ;; -d* | --debug) debug=1; shift ;; --sanitize) address=1; shift ;; --address) address=1; shift ;; --memory) memory=1; shift ;; *) echo "unknown option: $1" | tee -a configure.log echo "$0 --help for help" | tee -a configure.log leave 1;; esac done # temporary file name test=ztest$$ # put arguments in log, also put test file in log if used in arguments show() { case "$*" in *$test.c*) echo === $test.c === >> configure.log cat $test.c >> configure.log echo === >> configure.log;; esac echo $* >> configure.log } # check for gcc vs. cc and set compile and link flags based on the system identified by uname cat > $test.c <<EOF extern int getchar(); int hello() {return getchar();} EOF if test -z "$CC"; then echo Checking for ${CROSS_PREFIX}gcc... | tee -a configure.log if ${CROSS_PREFIX}gcc -v >/dev/null 2>&1; then cc=${CROSS_PREFIX}gcc else cc=${CROSS_PREFIX}cc fi else cc=${CC} fi case "$cc" in *gcc*) gcc=1 ;; *clang*) gcc=1 ;; esac case `$cc -v 2>&1` in *gcc*) gcc=1 ;; *clang*) gcc=1 ;; esac show $cc -c $test.c if test "$gcc" -eq 1 && ($cc -c $test.c) >> configure.log 2>&1; then echo ... using gcc >> configure.log CC="$cc" CFLAGS="${CFLAGS--O3}" SFLAGS="${CFLAGS--O3} -fPIC" if test "$ARCHS"; then CFLAGS="${CFLAGS} ${ARCHS}" LDFLAGS="${LDFLAGS} ${ARCHS}" fi if test $build64 -eq 1; then CFLAGS="${CFLAGS} -m64" SFLAGS="${SFLAGS} -m64" fi if test "$warn" -eq 1; then if test "$zconst" -eq 1; then CFLAGS="${CFLAGS} -Wall -Wextra -Wcast-qual -DZLIB_CONST" else CFLAGS="${CFLAGS} -Wall -Wextra" fi fi if test $address -eq 1; then CFLAGS="${CFLAGS} -g -fsanitize=address -fno-omit-frame-pointer" fi if test $memory -eq 1; then CFLAGS="${CFLAGS} -g -fsanitize=memory -fno-omit-frame-pointer" fi if test $debug -eq 1; then CFLAGS="${CFLAGS} -DZLIB_DEBUG" SFLAGS="${SFLAGS} -DZLIB_DEBUG" fi if test -z "$uname"; then uname=`(uname -s || echo unknown) 2>/dev/null` fi case "$uname" in Linux* | linux* | *-linux* | GNU | GNU/* | solaris*) case "$mname" in *sparc*) LDFLAGS="${LDFLAGS} -Wl,--no-warn-rwx-segments" ;; esac LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1,--version-script,${SRCDIR}zlib.map"} ;; *BSD | *bsd* | DragonFly) LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1,--version-script,${SRCDIR}zlib.map"} LDCONFIG="ldconfig -m" ;; CYGWIN* | Cygwin* | cygwin* | *-cygwin* | OS/2*) EXE='.exe' ;; MINGW* | mingw* | *-mingw*) rm -f $test.[co] $test $test$shared_ext echo "If this doesn't work for you, try win32/Makefile.gcc." | tee -a configure.log LDSHARED=${LDSHARED-"$cc -shared"} LDSHAREDLIBC="" EXE='.exe' ;; QNX*) # This is for QNX6. I suppose that the QNX rule below is for QNX2,QNX4 # (alain.bonnefoy@icbt.com) LDSHARED=${LDSHARED-"$cc -shared -Wl,-hlibz.so.1"} ;; HP-UX*) LDSHARED=${LDSHARED-"$cc -shared $SFLAGS"} case `(uname -m || echo unknown) 2>/dev/null` in ia64) shared_ext='.so' SHAREDLIB='libz.so' ;; *) shared_ext='.sl' SHAREDLIB='libz.sl' ;; esac ;; AIX*) LDFLAGS="${LDFLAGS} -Wl,-brtl" ;; Darwin* | darwin* | *-darwin*) shared_ext='.dylib' SHAREDLIB=libz$shared_ext SHAREDLIBV=libz.$VER$shared_ext SHAREDLIBM=libz.$VER1$shared_ext LDSHARED=${LDSHARED-"$cc -dynamiclib -install_name $libdir/$SHAREDLIBM -compatibility_version $VER1 -current_version $VER3"} if "${CROSS_PREFIX}libtool" -V 2>&1 | grep Apple > /dev/null; then AR="${CROSS_PREFIX}libtool" elif libtool -V 2>&1 | grep Apple > /dev/null; then AR="libtool" else AR="/usr/bin/libtool" fi ARFLAGS="-o" ;; *) LDSHARED=${LDSHARED-"$cc -shared"} ;; esac else # find system name and corresponding cc options CC=${CC-cc} gcc=0 echo ... using $CC >> configure.log if test -z "$uname"; then uname=`(uname -sr || echo unknown) 2>/dev/null` fi case "$uname" in HP-UX*) SFLAGS=${CFLAGS-"-O +z"} CFLAGS=${CFLAGS-"-O"} # LDSHARED=${LDSHARED-"ld -b +vnocompatwarnings"} LDSHARED=${LDSHARED-"ld -b"} case `(uname -m || echo unknown) 2>/dev/null` in ia64) shared_ext='.so' SHAREDLIB='libz.so' ;; *) shared_ext='.sl' SHAREDLIB='libz.sl' ;; esac ;; IRIX*) SFLAGS=${CFLAGS-"-ansi -O2 -rpath ."} CFLAGS=${CFLAGS-"-ansi -O2"} LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"} ;; OSF1\ V4*) SFLAGS=${CFLAGS-"-O -std1"} CFLAGS=${CFLAGS-"-O -std1"} LDFLAGS="${LDFLAGS} -Wl,-rpath,." LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"} ;; OSF1*) SFLAGS=${CFLAGS-"-O -std1"} CFLAGS=${CFLAGS-"-O -std1"} LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"} ;; QNX*) SFLAGS=${CFLAGS-"-4 -O"} CFLAGS=${CFLAGS-"-4 -O"} LDSHARED=${LDSHARED-"cc"} RANLIB=${RANLIB-"true"} AR="cc" ARFLAGS="-A" ;; SCO_SV\ 3.2*) SFLAGS=${CFLAGS-"-O3 -dy -KPIC "} CFLAGS=${CFLAGS-"-O3"} LDSHARED=${LDSHARED-"cc -dy -KPIC -G"} ;; SunOS\ 5* | solaris*) LDSHARED=${LDSHARED-"cc -G -h libz$shared_ext.$VER1"} SFLAGS=${CFLAGS-"-fast -KPIC"} CFLAGS=${CFLAGS-"-fast"} if test $build64 -eq 1; then # old versions of SunPRO/Workshop/Studio don't support -m64, # but newer ones do. Check for it. flag64=`$CC -flags | egrep -- '^-m64'` if test x"$flag64" != x"" ; then CFLAGS="${CFLAGS} -m64" SFLAGS="${SFLAGS} -m64" else case `(uname -m || echo unknown) 2>/dev/null` in i86*) SFLAGS="$SFLAGS -xarch=amd64" CFLAGS="$CFLAGS -xarch=amd64" ;; *) SFLAGS="$SFLAGS -xarch=v9" CFLAGS="$CFLAGS -xarch=v9" ;; esac fi fi if test -n "$ZINC"; then ZINC='-I- -I. -I$(SRCDIR)' fi ;; SunOS\ 4*) SFLAGS=${CFLAGS-"-O2 -PIC"} CFLAGS=${CFLAGS-"-O2"} LDSHARED=${LDSHARED-"ld"} ;; SunStudio\ 9*) SFLAGS=${CFLAGS-"-fast -xcode=pic32 -xtarget=ultra3 -xarch=v9b"} CFLAGS=${CFLAGS-"-fast -xtarget=ultra3 -xarch=v9b"} LDSHARED=${LDSHARED-"cc -xarch=v9b"} ;; UNIX_System_V\ 4.2.0) SFLAGS=${CFLAGS-"-KPIC -O"} CFLAGS=${CFLAGS-"-O"} LDSHARED=${LDSHARED-"cc -G"} ;; UNIX_SV\ 4.2MP) SFLAGS=${CFLAGS-"-Kconform_pic -O"} CFLAGS=${CFLAGS-"-O"} LDSHARED=${LDSHARED-"cc -G"} ;; OpenUNIX\ 5) SFLAGS=${CFLAGS-"-KPIC -O"} CFLAGS=${CFLAGS-"-O"} LDSHARED=${LDSHARED-"cc -G"} ;; AIX*) # Courtesy of dbakker@arrayasolutions.com SFLAGS=${CFLAGS-"-O -qmaxmem=8192"} CFLAGS=${CFLAGS-"-O -qmaxmem=8192"} LDSHARED=${LDSHARED-"xlc -G"} ;; # send working options for other systems to zlib@gzip.org *) SFLAGS=${CFLAGS-"-O"} CFLAGS=${CFLAGS-"-O"} LDSHARED=${LDSHARED-"cc -shared"} ;; esac fi # destination names for shared library if not defined above SHAREDLIB=${SHAREDLIB-"libz$shared_ext"} SHAREDLIBV=${SHAREDLIBV-"libz$shared_ext.$VER"} SHAREDLIBM=${SHAREDLIBM-"libz$shared_ext.$VER1"} echo >> configure.log # define functions for testing compiler and library characteristics and logging the results cat > $test.c <<EOF #error error EOF if ($CC -c $CFLAGS $test.c) 2>/dev/null; then try() { show $* test "`( $* ) 2>&1 | tee -a configure.log`" = "" } echo - using any output from compiler to indicate an error >> configure.log else try() { show $* got=`( $* ) 2>&1` ret=$? if test "$got" != ""; then printf "%s\n" "$got" >> configure.log fi if test $ret -ne 0; then echo "(exit code "$ret")" >> configure.log fi return $ret } fi tryboth() { show $* got=`( $* ) 2>&1` ret=$? if test "$got" != ""; then printf "%s\n" "$got" >> configure.log fi if test $ret -ne 0; then echo "(exit code "$ret")" >> configure.log return $ret fi test "$got" = "" } cat > $test.c << EOF int foo() { return 0; } EOF echo "Checking for obsessive-compulsive compiler options..." >> configure.log if try $CC -c $CFLAGS $test.c; then : else echo "Compiler error reporting is too harsh for $0 (perhaps remove -Werror)." | tee -a configure.log leave 1 fi echo >> configure.log # see if shared library build supported cat > $test.c <<EOF extern int getchar(); int hello() {return getchar();} EOF if test $shared -eq 1; then echo Checking for shared library support... | tee -a configure.log # we must test in two steps (cc then ld), required at least on SunOS 4.x if try $CC -c $SFLAGS $test.c && try $LDSHARED $SFLAGS -o $test$shared_ext $test.o; then echo Building shared library $SHAREDLIBV with $CC. | tee -a configure.log elif test -z "$old_cc" -a -z "$old_cflags"; then echo No shared library support. | tee -a configure.log shared=0; else echo 'No shared library support; try without defining CC and CFLAGS' | tee -a configure.log shared=0; fi fi if test $shared -eq 0; then LDSHARED="$CC" ALL="static" TEST="all teststatic" SHAREDLIB="" SHAREDLIBV="" SHAREDLIBM="" echo Building static library $STATICLIB version $VER with $CC. | tee -a configure.log else ALL="static shared" TEST="all teststatic testshared" fi echo >> configure.log # check for size_t cat > $test.c <<EOF #include <stdio.h> #include <stdlib.h> size_t dummy = 0; EOF if try $CC -c $CFLAGS $test.c; then echo "Checking for size_t... Yes." | tee -a configure.log else echo "Checking for size_t... No." | tee -a configure.log # find a size_t integer type # check for long long cat > $test.c << EOF long long dummy = 0; EOF if try $CC -c $CFLAGS $test.c; then echo "Checking for long long... Yes." | tee -a configure.log cat > $test.c <<EOF #include <stdio.h> int main(void) { if (sizeof(void *) <= sizeof(int)) puts("int"); else if (sizeof(void *) <= sizeof(long)) puts("long"); else puts("z_longlong"); return 0; } EOF else echo "Checking for long long... No." | tee -a configure.log cat > $test.c <<EOF #include <stdio.h> int main(void) { if (sizeof(void *) <= sizeof(int)) puts("int"); else puts("long"); return 0; } EOF fi if try $CC $CFLAGS -o $test $test.c; then sizet=`./$test` echo "Checking for a pointer-size integer type..." $sizet"." | tee -a configure.log CFLAGS="${CFLAGS} -DNO_SIZE_T=${sizet}" SFLAGS="${SFLAGS} -DNO_SIZE_T=${sizet}" else echo "Checking for a pointer-size integer type... not found." | tee -a configure.log fi fi echo >> configure.log # check for large file support, and if none, check for fseeko() cat > $test.c <<EOF #include <sys/types.h> off64_t dummy = 0; EOF if try $CC -c $CFLAGS -D_LARGEFILE64_SOURCE=1 $test.c; then CFLAGS="${CFLAGS} -D_LARGEFILE64_SOURCE=1" SFLAGS="${SFLAGS} -D_LARGEFILE64_SOURCE=1" ALL="${ALL} all64" TEST="${TEST} test64" echo "Checking for off64_t... Yes." | tee -a configure.log echo "Checking for fseeko... Yes." | tee -a configure.log else echo "Checking for off64_t... No." | tee -a configure.log echo >> configure.log cat > $test.c <<EOF #include <stdio.h> int main(void) { fseeko(NULL, 0, 0); return 0; } EOF if try $CC $CFLAGS -o $test $test.c; then echo "Checking for fseeko... Yes." | tee -a configure.log else CFLAGS="${CFLAGS} -DNO_FSEEKO" SFLAGS="${SFLAGS} -DNO_FSEEKO" echo "Checking for fseeko... No." | tee -a configure.log fi fi echo >> configure.log # check for strerror() for use by gz* functions cat > $test.c <<EOF #include <string.h> #include <errno.h> int main() { return strlen(strerror(errno)); } EOF if try $CC $CFLAGS -o $test $test.c; then echo "Checking for strerror... Yes." | tee -a configure.log else CFLAGS="${CFLAGS} -DNO_STRERROR" SFLAGS="${SFLAGS} -DNO_STRERROR" echo "Checking for strerror... No." | tee -a configure.log fi # copy clean zconf.h for subsequent edits cp -p ${SRCDIR}zconf.h.in zconf.h echo >> configure.log # check for unistd.h and save result in zconf.h cat > $test.c <<EOF #include <unistd.h> int main() { return 0; } EOF if try $CC -c $CFLAGS $test.c; then sed < zconf.h "/^#ifdef HAVE_UNISTD_H.* may be/s/def HAVE_UNISTD_H\(.*\) may be/ 1\1 was/" > zconf.temp.h mv zconf.temp.h zconf.h echo "Checking for unistd.h... Yes." | tee -a configure.log else echo "Checking for unistd.h... No." | tee -a configure.log fi echo >> configure.log # check for stdarg.h and save result in zconf.h cat > $test.c <<EOF #include <stdarg.h> int main() { return 0; } EOF if try $CC -c $CFLAGS $test.c; then sed < zconf.h "/^#ifdef HAVE_STDARG_H.* may be/s/def HAVE_STDARG_H\(.*\) may be/ 1\1 was/" > zconf.temp.h mv zconf.temp.h zconf.h echo "Checking for stdarg.h... Yes." | tee -a configure.log else echo "Checking for stdarg.h... No." | tee -a configure.log fi # if the z_ prefix was requested, save that in zconf.h if test $zprefix -eq 1; then sed < zconf.h "/#ifdef Z_PREFIX.* may be/s/def Z_PREFIX\(.*\) may be/ 1\1 was/" > zconf.temp.h mv zconf.temp.h zconf.h echo >> configure.log echo "Using z_ prefix on all symbols." | tee -a configure.log fi # if --solo compilation was requested, save that in zconf.h and remove gz stuff from object lists if test $solo -eq 1; then sed '/#define ZCONF_H/a\ #define Z_SOLO ' < zconf.h > zconf.temp.h mv zconf.temp.h zconf.h OBJC='$(OBJZ)' PIC_OBJC='$(PIC_OBJZ)' fi # if code coverage testing was requested, use older gcc if defined, e.g. "gcc-4.2" on Mac OS X if test $cover -eq 1; then CFLAGS="${CFLAGS} -fprofile-arcs -ftest-coverage" if test -n "$GCC_CLASSIC"; then CC=$GCC_CLASSIC fi fi echo >> configure.log # conduct a series of tests to resolve eight possible cases of using "vs" or "s" printf functions # (using stdarg or not), with or without "n" (proving size of buffer), and with or without a # return value. The most secure result is vsnprintf() with a return value. snprintf() with a # return value is secure as well, but then gzprintf() will be limited to 20 arguments. cat > $test.c <<EOF #include <stdio.h> #include <stdarg.h> #include "zconf.h" int main() { #ifndef STDC choke me #endif return 0; } EOF if try $CC -c $CFLAGS $test.c; then echo "Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf()." | tee -a configure.log echo >> configure.log cat > $test.c <<EOF #include <stdio.h> #include <stdarg.h> int mytest(const char *fmt, ...) { char buf[20]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); return 0; } int main() { return (mytest("Hello%d\n", 1)); } EOF if try $CC $CFLAGS -o $test $test.c; then echo "Checking for vsnprintf() in stdio.h... Yes." | tee -a configure.log echo >> configure.log cat >$test.c <<EOF #include <stdio.h> #include <stdarg.h> int mytest(const char *fmt, ...) { int n; char buf[20]; va_list ap; va_start(ap, fmt); n = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); return n; } int main() { return (mytest("Hello%d\n", 1)); } EOF if try $CC -c $CFLAGS $test.c; then echo "Checking for return value of vsnprintf()... Yes." | tee -a configure.log else CFLAGS="$CFLAGS -DHAS_vsnprintf_void" SFLAGS="$SFLAGS -DHAS_vsnprintf_void" echo "Checking for return value of vsnprintf()... No." | tee -a configure.log echo " WARNING: apparently vsnprintf() does not return a value. zlib" | tee -a configure.log echo " can build but will be open to possible string-format security" | tee -a configure.log echo " vulnerabilities." | tee -a configure.log fi else CFLAGS="$CFLAGS -DNO_vsnprintf" SFLAGS="$SFLAGS -DNO_vsnprintf" echo "Checking for vsnprintf() in stdio.h... No." | tee -a configure.log echo " WARNING: vsnprintf() not found, falling back to vsprintf(). zlib" | tee -a configure.log echo " can build but will be open to possible buffer-overflow security" | tee -a configure.log echo " vulnerabilities." | tee -a configure.log echo >> configure.log cat >$test.c <<EOF #include <stdio.h> #include <stdarg.h> int mytest(const char *fmt, ...) { int n; char buf[20]; va_list ap; va_start(ap, fmt); n = vsprintf(buf, fmt, ap); va_end(ap); return n; } int main() { return (mytest("Hello%d\n", 1)); } EOF if try $CC -c $CFLAGS $test.c; then echo "Checking for return value of vsprintf()... Yes." | tee -a configure.log else CFLAGS="$CFLAGS -DHAS_vsprintf_void" SFLAGS="$SFLAGS -DHAS_vsprintf_void" echo "Checking for return value of vsprintf()... No." | tee -a configure.log echo " WARNING: apparently vsprintf() does not return a value. zlib" | tee -a configure.log echo " can build but will be open to possible string-format security" | tee -a configure.log echo " vulnerabilities." | tee -a configure.log fi fi else echo "Checking whether to use vs[n]printf() or s[n]printf()... using s[n]printf()." | tee -a configure.log echo >> configure.log cat >$test.c <<EOF #include <stdio.h> int mytest() { char buf[20]; snprintf(buf, sizeof(buf), "%s", "foo"); return 0; } int main() { return (mytest()); } EOF if try $CC $CFLAGS -o $test $test.c; then echo "Checking for snprintf() in stdio.h... Yes." | tee -a configure.log echo >> configure.log cat >$test.c <<EOF #include <stdio.h> int mytest() { char buf[20]; return snprintf(buf, sizeof(buf), "%s", "foo"); } int main() { return (mytest()); } EOF if try $CC -c $CFLAGS $test.c; then echo "Checking for return value of snprintf()... Yes." | tee -a configure.log else CFLAGS="$CFLAGS -DHAS_snprintf_void" SFLAGS="$SFLAGS -DHAS_snprintf_void" echo "Checking for return value of snprintf()... No." | tee -a configure.log echo " WARNING: apparently snprintf() does not return a value. zlib" | tee -a configure.log echo " can build but will be open to possible string-format security" | tee -a configure.log echo " vulnerabilities." | tee -a configure.log fi else CFLAGS="$CFLAGS -DNO_snprintf" SFLAGS="$SFLAGS -DNO_snprintf" echo "Checking for snprintf() in stdio.h... No." | tee -a configure.log echo " WARNING: snprintf() not found, falling back to sprintf(). zlib" | tee -a configure.log echo " can build but will be open to possible buffer-overflow security" | tee -a configure.log echo " vulnerabilities." | tee -a configure.log echo >> configure.log cat >$test.c <<EOF #include <stdio.h> int mytest() { char buf[20]; return sprintf(buf, "%s", "foo"); } int main() { return (mytest()); } EOF if try $CC -c $CFLAGS $test.c; then echo "Checking for return value of sprintf()... Yes." | tee -a configure.log else CFLAGS="$CFLAGS -DHAS_sprintf_void" SFLAGS="$SFLAGS -DHAS_sprintf_void" echo "Checking for return value of sprintf()... No." | tee -a configure.log echo " WARNING: apparently sprintf() does not return a value. zlib" | tee -a configure.log echo " can build but will be open to possible string-format security" | tee -a configure.log echo " vulnerabilities." | tee -a configure.log fi fi fi # see if we can hide zlib internal symbols that are linked between separate source files if test "$gcc" -eq 1; then echo >> configure.log cat > $test.c <<EOF #define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) int ZLIB_INTERNAL foo; int main() { return 0; } EOF if tryboth $CC -c $CFLAGS $test.c; then CFLAGS="$CFLAGS -DHAVE_HIDDEN" SFLAGS="$SFLAGS -DHAVE_HIDDEN" echo "Checking for attribute(visibility) support... Yes." | tee -a configure.log else echo "Checking for attribute(visibility) support... No." | tee -a configure.log fi fi # show the results in the log echo >> configure.log echo ALL = $ALL >> configure.log echo AR = $AR >> configure.log echo ARFLAGS = $ARFLAGS >> configure.log echo CC = $CC >> configure.log echo CFLAGS = $CFLAGS >> configure.log echo CPP = $CPP >> configure.log echo EXE = $EXE >> configure.log echo LDCONFIG = $LDCONFIG >> configure.log echo LDFLAGS = $LDFLAGS >> configure.log echo LDSHARED = $LDSHARED >> configure.log echo LDSHAREDLIBC = $LDSHAREDLIBC >> configure.log echo OBJC = $OBJC >> configure.log echo PIC_OBJC = $PIC_OBJC >> configure.log echo RANLIB = $RANLIB >> configure.log echo SFLAGS = $SFLAGS >> configure.log echo SHAREDLIB = $SHAREDLIB >> configure.log echo SHAREDLIBM = $SHAREDLIBM >> configure.log echo SHAREDLIBV = $SHAREDLIBV >> configure.log echo STATICLIB = $STATICLIB >> configure.log echo TEST = $TEST >> configure.log echo VER = $VER >> configure.log echo SRCDIR = $SRCDIR >> configure.log echo exec_prefix = $exec_prefix >> configure.log echo includedir = $includedir >> configure.log echo libdir = $libdir >> configure.log echo mandir = $mandir >> configure.log echo prefix = $prefix >> configure.log echo sharedlibdir = $sharedlibdir >> configure.log echo uname = $uname >> configure.log # update Makefile with the configure results sed < ${SRCDIR}Makefile.in " /^CC *=/s#=.*#=$CC# /^CFLAGS *=/s#=.*#=$CFLAGS# /^SFLAGS *=/s#=.*#=$SFLAGS# /^LDFLAGS *=/s#=.*#=$LDFLAGS# /^LDSHARED *=/s#=.*#=$LDSHARED# /^CPP *=/s#=.*#=$CPP# /^STATICLIB *=/s#=.*#=$STATICLIB# /^SHAREDLIB *=/s#=.*#=$SHAREDLIB# /^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV# /^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM# /^AR *=/s#=.*#=$AR# /^ARFLAGS *=/s#=.*#=$ARFLAGS# /^RANLIB *=/s#=.*#=$RANLIB# /^LDCONFIG *=/s#=.*#=$LDCONFIG# /^LDSHAREDLIBC *=/s#=.*#=$LDSHAREDLIBC# /^EXE *=/s#=.*#=$EXE# /^SRCDIR *=/s#=.*#=$SRCDIR# /^ZINC *=/s#=.*#=$ZINC# /^ZINCOUT *=/s#=.*#=$ZINCOUT# /^prefix *=/s#=.*#=$prefix# /^exec_prefix *=/s#=.*#=$exec_prefix# /^libdir *=/s#=.*#=$libdir# /^sharedlibdir *=/s#=.*#=$sharedlibdir# /^includedir *=/s#=.*#=$includedir# /^mandir *=/s#=.*#=$mandir# /^OBJC *=/s#=.*#= $OBJC# /^PIC_OBJC *=/s#=.*#= $PIC_OBJC# /^all: */s#:.*#: $ALL# /^test: */s#:.*#: $TEST# " > Makefile # create zlib.pc with the configure results sed < ${SRCDIR}zlib.pc.in " /^CC *=/s#=.*#=$CC# /^CFLAGS *=/s#=.*#=$CFLAGS# /^CPP *=/s#=.*#=$CPP# /^LDSHARED *=/s#=.*#=$LDSHARED# /^STATICLIB *=/s#=.*#=$STATICLIB# /^SHAREDLIB *=/s#=.*#=$SHAREDLIB# /^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV# /^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM# /^AR *=/s#=.*#=$AR# /^ARFLAGS *=/s#=.*#=$ARFLAGS# /^RANLIB *=/s#=.*#=$RANLIB# /^EXE *=/s#=.*#=$EXE# /^prefix *=/s#=.*#=$prefix# /^exec_prefix *=/s#=.*#=$exec_prefix# /^libdir *=/s#=.*#=$libdir# /^sharedlibdir *=/s#=.*#=$sharedlibdir# /^includedir *=/s#=.*#=$includedir# /^mandir *=/s#=.*#=$mandir# /^LDFLAGS *=/s#=.*#=$LDFLAGS# " | sed -e " s/\@VERSION\@/$VER/g; " > zlib.pc # done leave 0 |
Added compat/zlib/contrib/README.contrib.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | All files under this contrib directory are UNSUPPORTED. They were provided by users of zlib and were not tested by the authors of zlib. Use at your own risk. Please contact the authors of the contributions for help about these, not the zlib authors. Thanks. ada/ by Dmitriy Anisimkov <anisimkov@yahoo.com> Support for Ada See http://zlib-ada.sourceforge.net/ blast/ by Mark Adler <madler@alumni.caltech.edu> Decompressor for output of PKWare Data Compression Library (DCL) delphi/ by Cosmin Truta <cosmint@cs.ubbcluj.ro> Support for Delphi and C++ Builder dotzlib/ by Henrik Ravn <henrik@ravn.com> Support for Microsoft .Net and Visual C++ .Net gcc_gvmat64/by Gilles Vollant <info@winimage.com> GCC Version of x86 64-bit (AMD64 and Intel EM64t) code for x64 assembler to replace longest_match() and inflate_fast() infback9/ by Mark Adler <madler@alumni.caltech.edu> Unsupported diffs to infback to decode the deflate64 format iostream/ by Kevin Ruland <kevin@rodin.wustl.edu> A C++ I/O streams interface to the zlib gz* functions iostream2/ by Tyge Løvset <Tyge.Lovset@cmr.no> Another C++ I/O streams interface iostream3/ by Ludwig Schwardt <schwardt@sun.ac.za> and Kevin Ruland <kevin@rodin.wustl.edu> Yet another C++ I/O streams interface minizip/ by Gilles Vollant <info@winimage.com> Mini zip and unzip based on zlib Includes Zip64 support by Mathias Svensson <mathias@result42.com> See http://www.winimage.com/zLibDll/minizip.html pascal/ by Bob Dellaca <bobdl@xtra.co.nz> et al. Support for Pascal puff/ by Mark Adler <madler@alumni.caltech.edu> Small, low memory usage inflate. Also serves to provide an unambiguous description of the deflate format. testzlib/ by Gilles Vollant <info@winimage.com> Example of the use of zlib untgz/ by Pedro A. Aranda Gutierrez <paag@tid.es> A very simple tar.gz file extractor using zlib vstudio/ by Gilles Vollant <info@winimage.com> Building a minizip-enhanced zlib with Microsoft Visual Studio Includes vc11 from kreuzerkrieg and vc12 from davispuh |
Added compat/zlib/contrib/blast/Makefile.
> > > > > > > > | 1 2 3 4 5 6 7 8 | blast: blast.c blast.h cc -DTEST -o blast blast.c test: blast blast < test.pk | cmp - test.txt clean: rm -f blast blast.o |
Added compat/zlib/contrib/blast/README.
> > > > | 1 2 3 4 | Read blast.h for purpose and usage. Mark Adler madler@alumni.caltech.edu |
Added compat/zlib/contrib/blast/blast.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 | /* blast.c * Copyright (C) 2003, 2012, 2013 Mark Adler * For conditions of distribution and use, see copyright notice in blast.h * version 1.3, 24 Aug 2013 * * blast.c decompresses data compressed by the PKWare Compression Library. * This function provides functionality similar to the explode() function of * the PKWare library, hence the name "blast". * * This decompressor is based on the excellent format description provided by * Ben Rudiak-Gould in comp.compression on August 13, 2001. Interestingly, the * example Ben provided in the post is incorrect. The distance 110001 should * instead be 111000. When corrected, the example byte stream becomes: * * 00 04 82 24 25 8f 80 7f * * which decompresses to "AIAIAIAIAIAIA" (without the quotes). */ /* * Change history: * * 1.0 12 Feb 2003 - First version * 1.1 16 Feb 2003 - Fixed distance check for > 4 GB uncompressed data * 1.2 24 Oct 2012 - Add note about using binary mode in stdio * - Fix comparisons of differently signed integers * 1.3 24 Aug 2013 - Return unused input from blast() * - Fix test code to correctly report unused input * - Enable the provision of initial input to blast() */ #include <stddef.h> /* for NULL */ #include <setjmp.h> /* for setjmp(), longjmp(), and jmp_buf */ #include "blast.h" /* prototype for blast() */ #define local static /* for local function definitions */ #define MAXBITS 13 /* maximum code length */ #define MAXWIN 4096 /* maximum window size */ /* input and output state */ struct state { /* input state */ blast_in infun; /* input function provided by user */ void *inhow; /* opaque information passed to infun() */ unsigned char *in; /* next input location */ unsigned left; /* available input at in */ int bitbuf; /* bit buffer */ int bitcnt; /* number of bits in bit buffer */ /* input limit error return state for bits() and decode() */ jmp_buf env; /* output state */ blast_out outfun; /* output function provided by user */ void *outhow; /* opaque information passed to outfun() */ unsigned next; /* index of next write location in out[] */ int first; /* true to check distances (for first 4K) */ unsigned char out[MAXWIN]; /* output buffer and sliding window */ }; /* * Return need bits from the input stream. This always leaves less than * eight bits in the buffer. bits() works properly for need == 0. * * Format notes: * * - Bits are stored in bytes from the least significant bit to the most * significant bit. Therefore bits are dropped from the bottom of the bit * buffer, using shift right, and new bytes are appended to the top of the * bit buffer, using shift left. */ local int bits(struct state *s, int need) { int val; /* bit accumulator */ /* load at least need bits into val */ val = s->bitbuf; while (s->bitcnt < need) { if (s->left == 0) { s->left = s->infun(s->inhow, &(s->in)); if (s->left == 0) longjmp(s->env, 1); /* out of input */ } val |= (int)(*(s->in)++) << s->bitcnt; /* load eight bits */ s->left--; s->bitcnt += 8; } /* drop need bits and update buffer, always zero to seven bits left */ s->bitbuf = val >> need; s->bitcnt -= need; /* return need bits, zeroing the bits above that */ return val & ((1 << need) - 1); } /* * Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of * each length, which for a canonical code are stepped through in order. * symbol[] are the symbol values in canonical order, where the number of * entries is the sum of the counts in count[]. The decoding process can be * seen in the function decode() below. */ struct huffman { short *count; /* number of symbols of each length */ short *symbol; /* canonically ordered symbols */ }; /* * Decode a code from the stream s using huffman table h. Return the symbol or * a negative value if there is an error. If all of the lengths are zero, i.e. * an empty code, or if the code is incomplete and an invalid code is received, * then -9 is returned after reading MAXBITS bits. * * Format notes: * * - The codes as stored in the compressed data are bit-reversed relative to * a simple integer ordering of codes of the same lengths. Hence below the * bits are pulled from the compressed data one at a time and used to * build the code value reversed from what is in the stream in order to * permit simple integer comparisons for decoding. * * - The first code for the shortest length is all ones. Subsequent codes of * the same length are simply integer decrements of the previous code. When * moving up a length, a one bit is appended to the code. For a complete * code, the last code of the longest length will be all zeros. To support * this ordering, the bits pulled during decoding are inverted to apply the * more "natural" ordering starting with all zeros and incrementing. */ local int decode(struct state *s, struct huffman *h) { int len; /* current number of bits in code */ int code; /* len bits being decoded */ int first; /* first code of length len */ int count; /* number of codes of length len */ int index; /* index of first code of length len in symbol table */ int bitbuf; /* bits from stream */ int left; /* bits left in next or left to process */ short *next; /* next number of codes */ bitbuf = s->bitbuf; left = s->bitcnt; code = first = index = 0; len = 1; next = h->count + 1; while (1) { while (left--) { code |= (bitbuf & 1) ^ 1; /* invert code */ bitbuf >>= 1; count = *next++; if (code < first + count) { /* if length len, return symbol */ s->bitbuf = bitbuf; s->bitcnt = (s->bitcnt - len) & 7; return h->symbol[index + (code - first)]; } index += count; /* else update for next length */ first += count; first <<= 1; code <<= 1; len++; } left = (MAXBITS+1) - len; if (left == 0) break; if (s->left == 0) { s->left = s->infun(s->inhow, &(s->in)); if (s->left == 0) longjmp(s->env, 1); /* out of input */ } bitbuf = *(s->in)++; s->left--; if (left > 8) left = 8; } return -9; /* ran out of codes */ } /* * Given a list of repeated code lengths rep[0..n-1], where each byte is a * count (high four bits + 1) and a code length (low four bits), generate the * list of code lengths. This compaction reduces the size of the object code. * Then given the list of code lengths length[0..n-1] representing a canonical * Huffman code for n symbols, construct the tables required to decode those * codes. Those tables are the number of codes of each length, and the symbols * sorted by length, retaining their original order within each length. The * return value is zero for a complete code set, negative for an over- * subscribed code set, and positive for an incomplete code set. The tables * can be used if the return value is zero or positive, but they cannot be used * if the return value is negative. If the return value is zero, it is not * possible for decode() using that table to return an error--any stream of * enough bits will resolve to a symbol. If the return value is positive, then * it is possible for decode() using that table to return an error for received * codes past the end of the incomplete lengths. */ local int construct(struct huffman *h, const unsigned char *rep, int n) { int symbol; /* current symbol when stepping through length[] */ int len; /* current length when stepping through h->count[] */ int left; /* number of possible codes left of current length */ short offs[MAXBITS+1]; /* offsets in symbol table for each length */ short length[256]; /* code lengths */ /* convert compact repeat counts into symbol bit length list */ symbol = 0; do { len = *rep++; left = (len >> 4) + 1; len &= 15; do { length[symbol++] = len; } while (--left); } while (--n); n = symbol; /* count number of codes of each length */ for (len = 0; len <= MAXBITS; len++) h->count[len] = 0; for (symbol = 0; symbol < n; symbol++) (h->count[length[symbol]])++; /* assumes lengths are within bounds */ if (h->count[0] == n) /* no codes! */ return 0; /* complete, but decode() will fail */ /* check for an over-subscribed or incomplete set of lengths */ left = 1; /* one possible code of zero length */ for (len = 1; len <= MAXBITS; len++) { left <<= 1; /* one more bit, double codes left */ left -= h->count[len]; /* deduct count from possible codes */ if (left < 0) return left; /* over-subscribed--return negative */ } /* left > 0 means incomplete */ /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h->count[len]; /* * put symbols in table sorted by length, by symbol order within each * length */ for (symbol = 0; symbol < n; symbol++) if (length[symbol] != 0) h->symbol[offs[length[symbol]]++] = symbol; /* return zero for complete set, positive for incomplete set */ return left; } /* * Decode PKWare Compression Library stream. * * Format notes: * * - First byte is 0 if literals are uncoded or 1 if they are coded. Second * byte is 4, 5, or 6 for the number of extra bits in the distance code. * This is the base-2 logarithm of the dictionary size minus six. * * - Compressed data is a combination of literals and length/distance pairs * terminated by an end code. Literals are either Huffman coded or * uncoded bytes. A length/distance pair is a coded length followed by a * coded distance to represent a string that occurs earlier in the * uncompressed data that occurs again at the current location. * * - A bit preceding a literal or length/distance pair indicates which comes * next, 0 for literals, 1 for length/distance. * * - If literals are uncoded, then the next eight bits are the literal, in the * normal bit order in the stream, i.e. no bit-reversal is needed. Similarly, * no bit reversal is needed for either the length extra bits or the distance * extra bits. * * - Literal bytes are simply written to the output. A length/distance pair is * an instruction to copy previously uncompressed bytes to the output. The * copy is from distance bytes back in the output stream, copying for length * bytes. * * - Distances pointing before the beginning of the output data are not * permitted. * * - Overlapped copies, where the length is greater than the distance, are * allowed and common. For example, a distance of one and a length of 518 * simply copies the last byte 518 times. A distance of four and a length of * twelve copies the last four bytes three times. A simple forward copy * ignoring whether the length is greater than the distance or not implements * this correctly. */ local int decomp(struct state *s) { int lit; /* true if literals are coded */ int dict; /* log2(dictionary size) - 6 */ int symbol; /* decoded symbol, extra bits for distance */ int len; /* length for copy */ unsigned dist; /* distance for copy */ int copy; /* copy counter */ unsigned char *from, *to; /* copy pointers */ static int virgin = 1; /* build tables once */ static short litcnt[MAXBITS+1], litsym[256]; /* litcode memory */ static short lencnt[MAXBITS+1], lensym[16]; /* lencode memory */ static short distcnt[MAXBITS+1], distsym[64]; /* distcode memory */ static struct huffman litcode = {litcnt, litsym}; /* length code */ static struct huffman lencode = {lencnt, lensym}; /* length code */ static struct huffman distcode = {distcnt, distsym};/* distance code */ /* bit lengths of literal codes */ static const unsigned char litlen[] = { 11, 124, 8, 7, 28, 7, 188, 13, 76, 4, 10, 8, 12, 10, 12, 10, 8, 23, 8, 9, 7, 6, 7, 8, 7, 6, 55, 8, 23, 24, 12, 11, 7, 9, 11, 12, 6, 7, 22, 5, 7, 24, 6, 11, 9, 6, 7, 22, 7, 11, 38, 7, 9, 8, 25, 11, 8, 11, 9, 12, 8, 12, 5, 38, 5, 38, 5, 11, 7, 5, 6, 21, 6, 10, 53, 8, 7, 24, 10, 27, 44, 253, 253, 253, 252, 252, 252, 13, 12, 45, 12, 45, 12, 61, 12, 45, 44, 173}; /* bit lengths of length codes 0..15 */ static const unsigned char lenlen[] = {2, 35, 36, 53, 38, 23}; /* bit lengths of distance codes 0..63 */ static const unsigned char distlen[] = {2, 20, 53, 230, 247, 151, 248}; static const short base[16] = { /* base for length codes */ 3, 2, 4, 5, 6, 7, 8, 9, 10, 12, 16, 24, 40, 72, 136, 264}; static const char extra[16] = { /* extra bits for length codes */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}; /* set up decoding tables (once--might not be thread-safe) */ if (virgin) { construct(&litcode, litlen, sizeof(litlen)); construct(&lencode, lenlen, sizeof(lenlen)); construct(&distcode, distlen, sizeof(distlen)); virgin = 0; } /* read header */ lit = bits(s, 8); if (lit > 1) return -1; dict = bits(s, 8); if (dict < 4 || dict > 6) return -2; /* decode literals and length/distance pairs */ do { if (bits(s, 1)) { /* get length */ symbol = decode(s, &lencode); len = base[symbol] + bits(s, extra[symbol]); if (len == 519) break; /* end code */ /* get distance */ symbol = len == 2 ? 2 : dict; dist = decode(s, &distcode) << symbol; dist += bits(s, symbol); dist++; if (s->first && dist > s->next) return -3; /* distance too far back */ /* copy length bytes from distance bytes back */ do { to = s->out + s->next; from = to - dist; copy = MAXWIN; if (s->next < dist) { from += copy; copy = dist; } copy -= s->next; if (copy > len) copy = len; len -= copy; s->next += copy; do { *to++ = *from++; } while (--copy); if (s->next == MAXWIN) { if (s->outfun(s->outhow, s->out, s->next)) return 1; s->next = 0; s->first = 0; } } while (len != 0); } else { /* get literal and write it */ symbol = lit ? decode(s, &litcode) : bits(s, 8); s->out[s->next++] = symbol; if (s->next == MAXWIN) { if (s->outfun(s->outhow, s->out, s->next)) return 1; s->next = 0; s->first = 0; } } } while (1); return 0; } /* See comments in blast.h */ int blast(blast_in infun, void *inhow, blast_out outfun, void *outhow, unsigned *left, unsigned char **in) { struct state s; /* input/output state */ int err; /* return value */ /* initialize input state */ s.infun = infun; s.inhow = inhow; if (left != NULL && *left) { s.left = *left; s.in = *in; } else s.left = 0; s.bitbuf = 0; s.bitcnt = 0; /* initialize output state */ s.outfun = outfun; s.outhow = outhow; s.next = 0; s.first = 1; /* return if bits() or decode() tries to read past available input */ if (setjmp(s.env) != 0) /* if came back here via longjmp(), */ err = 2; /* then skip decomp(), return error */ else err = decomp(&s); /* decompress */ /* return unused input */ if (left != NULL) *left = s.left; if (in != NULL) *in = s.left ? s.in : NULL; /* write any leftover output and update the error code if needed */ if (err != 1 && s.next && s.outfun(s.outhow, s.out, s.next) && err == 0) err = 1; return err; } #ifdef TEST /* Example of how to use blast() */ #include <stdio.h> #include <stdlib.h> #define CHUNK 16384 local unsigned inf(void *how, unsigned char **buf) { static unsigned char hold[CHUNK]; *buf = hold; return fread(hold, 1, CHUNK, (FILE *)how); } local int outf(void *how, unsigned char *buf, unsigned len) { return fwrite(buf, 1, len, (FILE *)how) != len; } /* Decompress a PKWare Compression Library stream from stdin to stdout */ int main(void) { int ret; unsigned left; /* decompress to stdout */ left = 0; ret = blast(inf, stdin, outf, stdout, &left, NULL); if (ret != 0) fprintf(stderr, "blast error: %d\n", ret); /* count any leftover bytes */ while (getchar() != EOF) left++; if (left) fprintf(stderr, "blast warning: %u unused bytes of input\n", left); /* return blast() error code */ return ret; } #endif |
Added compat/zlib/contrib/blast/blast.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* blast.h -- interface for blast.c Copyright (C) 2003, 2012, 2013 Mark Adler version 1.3, 24 Aug 2013 This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Mark Adler madler@alumni.caltech.edu */ /* * blast() decompresses the PKWare Data Compression Library (DCL) compressed * format. It provides the same functionality as the explode() function in * that library. (Note: PKWare overused the "implode" verb, and the format * used by their library implode() function is completely different and * incompatible with the implode compression method supported by PKZIP.) * * The binary mode for stdio functions should be used to assure that the * compressed data is not corrupted when read or written. For example: * fopen(..., "rb") and fopen(..., "wb"). */ typedef unsigned (*blast_in)(void *how, unsigned char **buf); typedef int (*blast_out)(void *how, unsigned char *buf, unsigned len); /* Definitions for input/output functions passed to blast(). See below for * what the provided functions need to do. */ int blast(blast_in infun, void *inhow, blast_out outfun, void *outhow, unsigned *left, unsigned char **in); /* Decompress input to output using the provided infun() and outfun() calls. * On success, the return value of blast() is zero. If there is an error in * the source data, i.e. it is not in the proper format, then a negative value * is returned. If there is not enough input available or there is not enough * output space, then a positive error is returned. * * The input function is invoked: len = infun(how, &buf), where buf is set by * infun() to point to the input buffer, and infun() returns the number of * available bytes there. If infun() returns zero, then blast() returns with * an input error. (blast() only asks for input if it needs it.) inhow is for * use by the application to pass an input descriptor to infun(), if desired. * * If left and in are not NULL and *left is not zero when blast() is called, * then the *left bytes at *in are consumed for input before infun() is used. * * The output function is invoked: err = outfun(how, buf, len), where the bytes * to be written are buf[0..len-1]. If err is not zero, then blast() returns * with an output error. outfun() is always called with len <= 4096. outhow * is for use by the application to pass an output descriptor to outfun(), if * desired. * * If there is any unused input, *left is set to the number of bytes that were * read and *in points to them. Otherwise *left is set to zero and *in is set * to NULL. If left or in are NULL, then they are not set. * * The return codes are: * * 2: ran out of input before completing decompression * 1: output error before completing decompression * 0: successful decompression * -1: literal flag not zero or one * -2: dictionary size not in 4..6 * -3: distance is too far back * * At the bottom of blast.c is an example program that uses blast() that can be * compiled to produce a command-line decompression filter by defining TEST. */ |
Added compat/zlib/contrib/blast/test.pk.
cannot compute difference between binary files
Added compat/zlib/contrib/blast/test.txt.
> | 1 | AIAIAIAIAIAIA |
Added compat/zlib/contrib/delphi/ZLib.pas.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | {*******************************************************} { } { Borland Delphi Supplemental Components } { ZLIB Data Compression Interface Unit } { } { Copyright (c) 1997,99 Borland Corporation } { } {*******************************************************} { Updated for zlib 1.2.x by Cosmin Truta <cosmint@cs.ubbcluj.ro> } unit ZLib; interface uses SysUtils, Classes; type TAlloc = function (AppData: Pointer; Items, Size: Integer): Pointer; cdecl; TFree = procedure (AppData, Block: Pointer); cdecl; // Internal structure. Ignore. TZStreamRec = packed record next_in: PChar; // next input byte avail_in: Integer; // number of bytes available at next_in total_in: Longint; // total nb of input bytes read so far next_out: PChar; // next output byte should be put here avail_out: Integer; // remaining free space at next_out total_out: Longint; // total nb of bytes output so far msg: PChar; // last error message, NULL if no error internal: Pointer; // not visible by applications zalloc: TAlloc; // used to allocate the internal state zfree: TFree; // used to free the internal state AppData: Pointer; // private data object passed to zalloc and zfree data_type: Integer; // best guess about the data type: ascii or binary adler: Longint; // adler32 value of the uncompressed data reserved: Longint; // reserved for future use end; // Abstract ancestor class TCustomZlibStream = class(TStream) private FStrm: TStream; FStrmPos: Integer; FOnProgress: TNotifyEvent; FZRec: TZStreamRec; FBuffer: array [Word] of Char; protected procedure Progress(Sender: TObject); dynamic; property OnProgress: TNotifyEvent read FOnProgress write FOnProgress; constructor Create(Strm: TStream); end; { TCompressionStream compresses data on the fly as data is written to it, and stores the compressed data to another stream. TCompressionStream is write-only and strictly sequential. Reading from the stream will raise an exception. Using Seek to move the stream pointer will raise an exception. Output data is cached internally, written to the output stream only when the internal output buffer is full. All pending output data is flushed when the stream is destroyed. The Position property returns the number of uncompressed bytes of data that have been written to the stream so far. CompressionRate returns the on-the-fly percentage by which the original data has been compressed: (1 - (CompressedBytes / UncompressedBytes)) * 100 If raw data size = 100 and compressed data size = 25, the CompressionRate is 75% The OnProgress event is called each time the output buffer is filled and written to the output stream. This is useful for updating a progress indicator when you are writing a large chunk of data to the compression stream in a single call.} TCompressionLevel = (clNone, clFastest, clDefault, clMax); TCompressionStream = class(TCustomZlibStream) private function GetCompressionRate: Single; public constructor Create(CompressionLevel: TCompressionLevel; Dest: TStream); destructor Destroy; override; function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; function Seek(Offset: Longint; Origin: Word): Longint; override; property CompressionRate: Single read GetCompressionRate; property OnProgress; end; { TDecompressionStream decompresses data on the fly as data is read from it. Compressed data comes from a separate source stream. TDecompressionStream is read-only and unidirectional; you can seek forward in the stream, but not backwards. The special case of setting the stream position to zero is allowed. Seeking forward decompresses data until the requested position in the uncompressed data has been reached. Seeking backwards, seeking relative to the end of the stream, requesting the size of the stream, and writing to the stream will raise an exception. The Position property returns the number of bytes of uncompressed data that have been read from the stream so far. The OnProgress event is called each time the internal input buffer of compressed data is exhausted and the next block is read from the input stream. This is useful for updating a progress indicator when you are reading a large chunk of data from the decompression stream in a single call.} TDecompressionStream = class(TCustomZlibStream) public constructor Create(Source: TStream); destructor Destroy; override; function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; function Seek(Offset: Longint; Origin: Word): Longint; override; property OnProgress; end; { CompressBuf compresses data, buffer to buffer, in one call. In: InBuf = ptr to compressed data InBytes = number of bytes in InBuf Out: OutBuf = ptr to newly allocated buffer containing decompressed data OutBytes = number of bytes in OutBuf } procedure CompressBuf(const InBuf: Pointer; InBytes: Integer; out OutBuf: Pointer; out OutBytes: Integer); { DecompressBuf decompresses data, buffer to buffer, in one call. In: InBuf = ptr to compressed data InBytes = number of bytes in InBuf OutEstimate = zero, or est. size of the decompressed data Out: OutBuf = ptr to newly allocated buffer containing decompressed data OutBytes = number of bytes in OutBuf } procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer; OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer); { DecompressToUserBuf decompresses data, buffer to buffer, in one call. In: InBuf = ptr to compressed data InBytes = number of bytes in InBuf Out: OutBuf = ptr to user-allocated buffer to contain decompressed data BufSize = number of bytes in OutBuf } procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer; const OutBuf: Pointer; BufSize: Integer); const zlib_version = '1.3.1'; type EZlibError = class(Exception); ECompressionError = class(EZlibError); EDecompressionError = class(EZlibError); implementation uses ZLibConst; const Z_NO_FLUSH = 0; Z_PARTIAL_FLUSH = 1; Z_SYNC_FLUSH = 2; Z_FULL_FLUSH = 3; Z_FINISH = 4; Z_OK = 0; Z_STREAM_END = 1; Z_NEED_DICT = 2; Z_ERRNO = (-1); Z_STREAM_ERROR = (-2); Z_DATA_ERROR = (-3); Z_MEM_ERROR = (-4); Z_BUF_ERROR = (-5); Z_VERSION_ERROR = (-6); Z_NO_COMPRESSION = 0; Z_BEST_SPEED = 1; Z_BEST_COMPRESSION = 9; Z_DEFAULT_COMPRESSION = (-1); Z_FILTERED = 1; Z_HUFFMAN_ONLY = 2; Z_RLE = 3; Z_DEFAULT_STRATEGY = 0; Z_BINARY = 0; Z_ASCII = 1; Z_UNKNOWN = 2; Z_DEFLATED = 8; {$L adler32.obj} {$L compress.obj} {$L crc32.obj} {$L deflate.obj} {$L infback.obj} {$L inffast.obj} {$L inflate.obj} {$L inftrees.obj} {$L trees.obj} {$L uncompr.obj} {$L zutil.obj} procedure adler32; external; procedure compressBound; external; procedure crc32; external; procedure deflateInit2_; external; procedure deflateParams; external; function _malloc(Size: Integer): Pointer; cdecl; begin Result := AllocMem(Size); end; procedure _free(Block: Pointer); cdecl; begin FreeMem(Block); end; procedure _memset(P: Pointer; B: Byte; count: Integer); cdecl; begin FillChar(P^, count, B); end; procedure _memcpy(dest, source: Pointer; count: Integer); cdecl; begin Move(source^, dest^, count); end; // deflate compresses data function deflateInit_(var strm: TZStreamRec; level: Integer; version: PChar; recsize: Integer): Integer; external; function deflate(var strm: TZStreamRec; flush: Integer): Integer; external; function deflateEnd(var strm: TZStreamRec): Integer; external; // inflate decompresses data function inflateInit_(var strm: TZStreamRec; version: PChar; recsize: Integer): Integer; external; function inflate(var strm: TZStreamRec; flush: Integer): Integer; external; function inflateEnd(var strm: TZStreamRec): Integer; external; function inflateReset(var strm: TZStreamRec): Integer; external; function zlibAllocMem(AppData: Pointer; Items, Size: Integer): Pointer; cdecl; begin // GetMem(Result, Items*Size); Result := AllocMem(Items * Size); end; procedure zlibFreeMem(AppData, Block: Pointer); cdecl; begin FreeMem(Block); end; {function zlibCheck(code: Integer): Integer; begin Result := code; if code < 0 then raise EZlibError.Create('error'); //!! end;} function CCheck(code: Integer): Integer; begin Result := code; if code < 0 then raise ECompressionError.Create('error'); //!! end; function DCheck(code: Integer): Integer; begin Result := code; if code < 0 then raise EDecompressionError.Create('error'); //!! end; procedure CompressBuf(const InBuf: Pointer; InBytes: Integer; out OutBuf: Pointer; out OutBytes: Integer); var strm: TZStreamRec; P: Pointer; begin FillChar(strm, sizeof(strm), 0); strm.zalloc := zlibAllocMem; strm.zfree := zlibFreeMem; OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255; GetMem(OutBuf, OutBytes); try strm.next_in := InBuf; strm.avail_in := InBytes; strm.next_out := OutBuf; strm.avail_out := OutBytes; CCheck(deflateInit_(strm, Z_BEST_COMPRESSION, zlib_version, sizeof(strm))); try while CCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do begin P := OutBuf; Inc(OutBytes, 256); ReallocMem(OutBuf, OutBytes); strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P))); strm.avail_out := 256; end; finally CCheck(deflateEnd(strm)); end; ReallocMem(OutBuf, strm.total_out); OutBytes := strm.total_out; except FreeMem(OutBuf); raise end; end; procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer; OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer); var strm: TZStreamRec; P: Pointer; BufInc: Integer; begin FillChar(strm, sizeof(strm), 0); strm.zalloc := zlibAllocMem; strm.zfree := zlibFreeMem; BufInc := (InBytes + 255) and not 255; if OutEstimate = 0 then OutBytes := BufInc else OutBytes := OutEstimate; GetMem(OutBuf, OutBytes); try strm.next_in := InBuf; strm.avail_in := InBytes; strm.next_out := OutBuf; strm.avail_out := OutBytes; DCheck(inflateInit_(strm, zlib_version, sizeof(strm))); try while DCheck(inflate(strm, Z_NO_FLUSH)) <> Z_STREAM_END do begin P := OutBuf; Inc(OutBytes, BufInc); ReallocMem(OutBuf, OutBytes); strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P))); strm.avail_out := BufInc; end; finally DCheck(inflateEnd(strm)); end; ReallocMem(OutBuf, strm.total_out); OutBytes := strm.total_out; except FreeMem(OutBuf); raise end; end; procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer; const OutBuf: Pointer; BufSize: Integer); var strm: TZStreamRec; begin FillChar(strm, sizeof(strm), 0); strm.zalloc := zlibAllocMem; strm.zfree := zlibFreeMem; strm.next_in := InBuf; strm.avail_in := InBytes; strm.next_out := OutBuf; strm.avail_out := BufSize; DCheck(inflateInit_(strm, zlib_version, sizeof(strm))); try if DCheck(inflate(strm, Z_FINISH)) <> Z_STREAM_END then raise EZlibError.CreateRes(@sTargetBufferTooSmall); finally DCheck(inflateEnd(strm)); end; end; // TCustomZlibStream constructor TCustomZLibStream.Create(Strm: TStream); begin inherited Create; FStrm := Strm; FStrmPos := Strm.Position; FZRec.zalloc := zlibAllocMem; FZRec.zfree := zlibFreeMem; end; procedure TCustomZLibStream.Progress(Sender: TObject); begin if Assigned(FOnProgress) then FOnProgress(Sender); end; // TCompressionStream constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel; Dest: TStream); const Levels: array [TCompressionLevel] of ShortInt = (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION); begin inherited Create(Dest); FZRec.next_out := FBuffer; FZRec.avail_out := sizeof(FBuffer); CCheck(deflateInit_(FZRec, Levels[CompressionLevel], zlib_version, sizeof(FZRec))); end; destructor TCompressionStream.Destroy; begin FZRec.next_in := nil; FZRec.avail_in := 0; try if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos; while (CCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END) and (FZRec.avail_out = 0) do begin FStrm.WriteBuffer(FBuffer, sizeof(FBuffer)); FZRec.next_out := FBuffer; FZRec.avail_out := sizeof(FBuffer); end; if FZRec.avail_out < sizeof(FBuffer) then FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out); finally deflateEnd(FZRec); end; inherited Destroy; end; function TCompressionStream.Read(var Buffer; Count: Longint): Longint; begin raise ECompressionError.CreateRes(@sInvalidStreamOp); end; function TCompressionStream.Write(const Buffer; Count: Longint): Longint; begin FZRec.next_in := @Buffer; FZRec.avail_in := Count; if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos; while (FZRec.avail_in > 0) do begin CCheck(deflate(FZRec, 0)); if FZRec.avail_out = 0 then begin FStrm.WriteBuffer(FBuffer, sizeof(FBuffer)); FZRec.next_out := FBuffer; FZRec.avail_out := sizeof(FBuffer); FStrmPos := FStrm.Position; Progress(Self); end; end; Result := Count; end; function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint; begin if (Offset = 0) and (Origin = soFromCurrent) then Result := FZRec.total_in else raise ECompressionError.CreateRes(@sInvalidStreamOp); end; function TCompressionStream.GetCompressionRate: Single; begin if FZRec.total_in = 0 then Result := 0 else Result := (1.0 - (FZRec.total_out / FZRec.total_in)) * 100.0; end; // TDecompressionStream constructor TDecompressionStream.Create(Source: TStream); begin inherited Create(Source); FZRec.next_in := FBuffer; FZRec.avail_in := 0; DCheck(inflateInit_(FZRec, zlib_version, sizeof(FZRec))); end; destructor TDecompressionStream.Destroy; begin FStrm.Seek(-FZRec.avail_in, 1); inflateEnd(FZRec); inherited Destroy; end; function TDecompressionStream.Read(var Buffer; Count: Longint): Longint; begin FZRec.next_out := @Buffer; FZRec.avail_out := Count; if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos; while (FZRec.avail_out > 0) do begin if FZRec.avail_in = 0 then begin FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer)); if FZRec.avail_in = 0 then begin Result := Count - FZRec.avail_out; Exit; end; FZRec.next_in := FBuffer; FStrmPos := FStrm.Position; Progress(Self); end; CCheck(inflate(FZRec, 0)); end; Result := Count; end; function TDecompressionStream.Write(const Buffer; Count: Longint): Longint; begin raise EDecompressionError.CreateRes(@sInvalidStreamOp); end; function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint; var I: Integer; Buf: array [0..4095] of Char; begin if (Offset = 0) and (Origin = soFromBeginning) then begin DCheck(inflateReset(FZRec)); FZRec.next_in := FBuffer; FZRec.avail_in := 0; FStrm.Position := 0; FStrmPos := 0; end else if ( (Offset >= 0) and (Origin = soFromCurrent)) or ( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then begin if Origin = soFromBeginning then Dec(Offset, FZRec.total_out); if Offset > 0 then begin for I := 1 to Offset div sizeof(Buf) do ReadBuffer(Buf, sizeof(Buf)); ReadBuffer(Buf, Offset mod sizeof(Buf)); end; end else raise EDecompressionError.CreateRes(@sInvalidStreamOp); Result := FZRec.total_out; end; end. |
Added compat/zlib/contrib/delphi/ZLibConst.pas.
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | unit ZLibConst; interface resourcestring sTargetBufferTooSmall = 'ZLib error: target buffer may be too small'; sInvalidStreamOp = 'Invalid stream operation'; implementation end. |
Added compat/zlib/contrib/delphi/readme.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 | Overview ======== This directory contains an update to the ZLib interface unit, distributed by Borland as a Delphi supplemental component. The original ZLib unit is Copyright (c) 1997,99 Borland Corp., and is based on zlib version 1.0.4. There are a series of bugs and security problems associated with that old zlib version, and we recommend the users to update their ZLib unit. Summary of modifications ======================== - Improved makefile, adapted to zlib version 1.2.1. - Some field types from TZStreamRec are changed from Integer to Longint, for consistency with the zlib.h header, and for 64-bit readiness. - The zlib_version constant is updated. - The new Z_RLE strategy has its corresponding symbolic constant. - The allocation and deallocation functions and function types (TAlloc, TFree, zlibAllocMem and zlibFreeMem) are now cdecl, and _malloc and _free are added as C RTL stubs. As a result, the original C sources of zlib can be compiled out of the box, and linked to the ZLib unit. Suggestions for improvements ============================ Currently, the ZLib unit provides only a limited wrapper around the zlib library, and much of the original zlib functionality is missing. Handling compressed file formats like ZIP/GZIP or PNG cannot be implemented without having this functionality. Applications that handle these formats are either using their own, duplicated code, or not using the ZLib unit at all. Here are a few suggestions: - Checksum class wrappers around adler32() and crc32(), similar to the Java classes that implement the java.util.zip.Checksum interface. - The ability to read and write raw deflate streams, without the zlib stream header and trailer. Raw deflate streams are used in the ZIP file format. - The ability to read and write gzip streams, used in the GZIP file format, and normally produced by the gzip program. - The ability to select a different compression strategy, useful to PNG and MNG image compression, and to multimedia compression in general. Besides the compression level TCompressionLevel = (clNone, clFastest, clDefault, clMax); which, in fact, could have used the 'z' prefix and avoided TColor-like symbols TCompressionLevel = (zcNone, zcFastest, zcDefault, zcMax); there could be a compression strategy TCompressionStrategy = (zsDefault, zsFiltered, zsHuffmanOnly, zsRle); - ZIP and GZIP stream handling via TStreams. -- Cosmin Truta <cosmint@cs.ubbcluj.ro> |
Added compat/zlib/contrib/delphi/zlibd32.mak.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib # For use with Delphi and C++ Builder under Win32 # Updated for zlib 1.2.x by Cosmin Truta # ------------ Borland C++ ------------ # This project uses the Delphi (fastcall/register) calling convention: LOC = -DZEXPORT=__fastcall -DZEXPORTVA=__cdecl CC = bcc32 LD = bcc32 AR = tlib # do not use "-pr" in CFLAGS CFLAGS = -a -d -k- -O2 $(LOC) LDFLAGS = # variables ZLIB_LIB = zlib.lib OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj # targets all: $(ZLIB_LIB) example.exe minigzip.exe .c.obj: $(CC) -c $(CFLAGS) $*.c adler32.obj: adler32.c zlib.h zconf.h compress.obj: compress.c zlib.h zconf.h crc32.obj: crc32.c zlib.h zconf.h crc32.h deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h gzread.obj: gzread.c zlib.h zconf.h gzguts.h gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h uncompr.obj: uncompr.c zlib.h zconf.h zutil.obj: zutil.c zutil.h zlib.h zconf.h example.obj: test/example.c zlib.h zconf.h minigzip.obj: test/minigzip.c zlib.h zconf.h # For the sake of the old Borland make, # the command line is cut to fit in the MS-DOS 128 byte limit: $(ZLIB_LIB): $(OBJ1) $(OBJ2) -del $(ZLIB_LIB) $(AR) $(ZLIB_LIB) $(OBJP1) $(AR) $(ZLIB_LIB) $(OBJP2) # testing test: example.exe minigzip.exe example echo hello world | minigzip | minigzip -d example.exe: example.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) example.obj $(ZLIB_LIB) minigzip.exe: minigzip.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB) # cleanup clean: -del *.obj -del *.exe -del *.lib -del *.tds -del zlib.bak -del foo.gz |
Added compat/zlib/contrib/dotzlib/DotZLib.build.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8" ?> <project name="DotZLib" default="build" basedir="./DotZLib"> <description>A .Net wrapper library around ZLib1.dll</description> <property name="nunit.location" value="c:/program files/NUnit V2.1/bin" /> <property name="build.root" value="bin" /> <property name="debug" value="true" /> <property name="nunit" value="true" /> <property name="build.folder" value="${build.root}/debug/" if="${debug}" /> <property name="build.folder" value="${build.root}/release/" unless="${debug}" /> <target name="clean" description="Remove all generated files"> <delete dir="${build.root}" failonerror="false" /> </target> <target name="build" description="compiles the source code"> <mkdir dir="${build.folder}" /> <csc target="library" output="${build.folder}DotZLib.dll" debug="${debug}"> <references basedir="${nunit.location}"> <includes if="${nunit}" name="nunit.framework.dll" /> </references> <sources> <includes name="*.cs" /> <excludes name="UnitTests.cs" unless="${nunit}" /> </sources> <arg value="/d:nunit" if="${nunit}" /> </csc> </target> </project> |
Added compat/zlib/contrib/dotzlib/DotZLib.chm.
cannot compute difference between binary files
Added compat/zlib/contrib/dotzlib/DotZLib.sln.
> > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | Microsoft Visual Studio Solution File, Format Version 8.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotZLib", "DotZLib\DotZLib.csproj", "{BB1EE0B1-1808-46CB-B786-949D91117FC5}" ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject Global GlobalSection(SolutionConfiguration) = preSolution Debug = Debug Release = Release EndGlobalSection GlobalSection(ProjectConfiguration) = postSolution {BB1EE0B1-1808-46CB-B786-949D91117FC5}.Debug.ActiveCfg = Debug|.NET {BB1EE0B1-1808-46CB-B786-949D91117FC5}.Debug.Build.0 = Debug|.NET {BB1EE0B1-1808-46CB-B786-949D91117FC5}.Release.ActiveCfg = Release|.NET {BB1EE0B1-1808-46CB-B786-949D91117FC5}.Release.Build.0 = Release|.NET EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection GlobalSection(ExtensibilityAddIns) = postSolution EndGlobalSection EndGlobal |
Added compat/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | using System.Reflection; using System.Runtime.CompilerServices; // // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. // [assembly: AssemblyTitle("DotZLib")] [assembly: AssemblyDescription(".Net bindings for ZLib compression dll 1.2.x")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Henrik Ravn")] [assembly: AssemblyProduct("")] [assembly: AssemblyCopyright("(c) 2004 by Henrik Ravn")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.*")] // // In order to sign your assembly you must specify a key to use. Refer to the // Microsoft .NET Framework documentation for more information on assembly signing. // // Use the attributes below to control which key is used for signing. // // Notes: // (*) If no key is specified, the assembly is not signed. // (*) KeyName refers to a key that has been installed in the Crypto Service // Provider (CSP) on your machine. KeyFile refers to a file which contains // a key. // (*) If the KeyFile and the KeyName values are both specified, the // following processing occurs: // (1) If the KeyName can be found in the CSP, that key is used. // (2) If the KeyName does not exist and the KeyFile does exist, the key // in the KeyFile is installed into the CSP and used. // (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. // When specifying the KeyFile, the location of the KeyFile should be // relative to the project output directory which is // %Project Directory%\obj\<configuration>. For example, if your KeyFile is // located in the project directory, you would specify the AssemblyKeyFile // attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] // (*) Delay Signing is an advanced option - see the Microsoft .NET Framework // documentation for more information on this. // [assembly: AssemblyDelaySign(false)] [assembly: AssemblyKeyFile("")] [assembly: AssemblyKeyName("")] |
Added compat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | // // © Copyright Henrik Ravn 2004 // // Use, modification and distribution are subject to the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // using System; using System.Runtime.InteropServices; using System.Text; namespace DotZLib { #region ChecksumGeneratorBase /// <summary> /// Implements the common functionality needed for all <see cref="ChecksumGenerator"/>s /// </summary> /// <example></example> public abstract class ChecksumGeneratorBase : ChecksumGenerator { /// <summary> /// The value of the current checksum /// </summary> protected uint _current; /// <summary> /// Initializes a new instance of the checksum generator base - the current checksum is /// set to zero /// </summary> public ChecksumGeneratorBase() { _current = 0; } /// <summary> /// Initializes a new instance of the checksum generator base with a specified value /// </summary> /// <param name="initialValue">The value to set the current checksum to</param> public ChecksumGeneratorBase(uint initialValue) { _current = initialValue; } /// <summary> /// Resets the current checksum to zero /// </summary> public void Reset() { _current = 0; } /// <summary> /// Gets the current checksum value /// </summary> public uint Value { get { return _current; } } /// <summary> /// Updates the current checksum with part of an array of bytes /// </summary> /// <param name="data">The data to update the checksum with</param> /// <param name="offset">Where in <c>data</c> to start updating</param> /// <param name="count">The number of bytes from <c>data</c> to use</param> /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception> /// <exception cref="NullReferenceException"><c>data</c> is a null reference</exception> /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception> /// <remarks>All the other <c>Update</c> methods are implemented in terms of this one. /// This is therefore the only method a derived class has to implement</remarks> public abstract void Update(byte[] data, int offset, int count); /// <summary> /// Updates the current checksum with an array of bytes. /// </summary> /// <param name="data">The data to update the checksum with</param> public void Update(byte[] data) { Update(data, 0, data.Length); } /// <summary> /// Updates the current checksum with the data from a string /// </summary> /// <param name="data">The string to update the checksum with</param> /// <remarks>The characters in the string are converted by the UTF-8 encoding</remarks> public void Update(string data) { Update(Encoding.UTF8.GetBytes(data)); } /// <summary> /// Updates the current checksum with the data from a string, using a specific encoding /// </summary> /// <param name="data">The string to update the checksum with</param> /// <param name="encoding">The encoding to use</param> public void Update(string data, Encoding encoding) { Update(encoding.GetBytes(data)); } } #endregion #region CRC32 /// <summary> /// Implements a CRC32 checksum generator /// </summary> public sealed class CRC32Checksum : ChecksumGeneratorBase { #region DLL imports [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern uint crc32(uint crc, int data, uint length); #endregion /// <summary> /// Initializes a new instance of the CRC32 checksum generator /// </summary> public CRC32Checksum() : base() {} /// <summary> /// Initializes a new instance of the CRC32 checksum generator with a specified value /// </summary> /// <param name="initialValue">The value to set the current checksum to</param> public CRC32Checksum(uint initialValue) : base(initialValue) {} /// <summary> /// Updates the current checksum with part of an array of bytes /// </summary> /// <param name="data">The data to update the checksum with</param> /// <param name="offset">Where in <c>data</c> to start updating</param> /// <param name="count">The number of bytes from <c>data</c> to use</param> /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception> /// <exception cref="NullReferenceException"><c>data</c> is a null reference</exception> /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception> public override void Update(byte[] data, int offset, int count) { if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); if ((offset+count) > data.Length) throw new ArgumentException(); GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned); try { _current = crc32(_current, hData.AddrOfPinnedObject().ToInt32()+offset, (uint)count); } finally { hData.Free(); } } } #endregion #region Adler /// <summary> /// Implements a checksum generator that computes the Adler checksum on data /// </summary> public sealed class AdlerChecksum : ChecksumGeneratorBase { #region DLL imports [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern uint adler32(uint adler, int data, uint length); #endregion /// <summary> /// Initializes a new instance of the Adler checksum generator /// </summary> public AdlerChecksum() : base() {} /// <summary> /// Initializes a new instance of the Adler checksum generator with a specified value /// </summary> /// <param name="initialValue">The value to set the current checksum to</param> public AdlerChecksum(uint initialValue) : base(initialValue) {} /// <summary> /// Updates the current checksum with part of an array of bytes /// </summary> /// <param name="data">The data to update the checksum with</param> /// <param name="offset">Where in <c>data</c> to start updating</param> /// <param name="count">The number of bytes from <c>data</c> to use</param> /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception> /// <exception cref="NullReferenceException"><c>data</c> is a null reference</exception> /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception> public override void Update(byte[] data, int offset, int count) { if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); if ((offset+count) > data.Length) throw new ArgumentException(); GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned); try { _current = adler32(_current, hData.AddrOfPinnedObject().ToInt32()+offset, (uint)count); } finally { hData.Free(); } } } #endregion } |
Added compat/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | // // © Copyright Henrik Ravn 2004 // // Use, modification and distribution are subject to the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // using System; using System.Diagnostics; namespace DotZLib { /// <summary> /// This class implements a circular buffer /// </summary> internal class CircularBuffer { #region Private data private int _capacity; private int _head; private int _tail; private int _size; private byte[] _buffer; #endregion public CircularBuffer(int capacity) { Debug.Assert( capacity > 0 ); _buffer = new byte[capacity]; _capacity = capacity; _head = 0; _tail = 0; _size = 0; } public int Size { get { return _size; } } public int Put(byte[] source, int offset, int count) { Debug.Assert( count > 0 ); int trueCount = Math.Min(count, _capacity - Size); for (int i = 0; i < trueCount; ++i) _buffer[(_tail+i) % _capacity] = source[offset+i]; _tail += trueCount; _tail %= _capacity; _size += trueCount; return trueCount; } public bool Put(byte b) { if (Size == _capacity) // no room return false; _buffer[_tail++] = b; _tail %= _capacity; ++_size; return true; } public int Get(byte[] destination, int offset, int count) { int trueCount = Math.Min(count,Size); for (int i = 0; i < trueCount; ++i) destination[offset + i] = _buffer[(_head+i) % _capacity]; _head += trueCount; _head %= _capacity; _size -= trueCount; return trueCount; } public int Get() { if (Size == 0) return -1; int result = (int)_buffer[_head++ % _capacity]; --_size; return result; } } } |
Added compat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | // // © Copyright Henrik Ravn 2004 // // Use, modification and distribution are subject to the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // using System; using System.Runtime.InteropServices; namespace DotZLib { /// <summary> /// Implements the common functionality needed for all <see cref="Codec"/>s /// </summary> public abstract class CodecBase : Codec, IDisposable { #region Data members /// <summary> /// Instance of the internal zlib buffer structure that is /// passed to all functions in the zlib dll /// </summary> internal ZStream _ztream = new ZStream(); /// <summary> /// True if the object instance has been disposed, false otherwise /// </summary> protected bool _isDisposed = false; /// <summary> /// The size of the internal buffers /// </summary> protected const int kBufferSize = 16384; private byte[] _outBuffer = new byte[kBufferSize]; private byte[] _inBuffer = new byte[kBufferSize]; private GCHandle _hInput; private GCHandle _hOutput; private uint _checksum = 0; #endregion /// <summary> /// Initializes a new instance of the <c>CodeBase</c> class. /// </summary> public CodecBase() { try { _hInput = GCHandle.Alloc(_inBuffer, GCHandleType.Pinned); _hOutput = GCHandle.Alloc(_outBuffer, GCHandleType.Pinned); } catch (Exception) { CleanUp(false); throw; } } #region Codec Members /// <summary> /// Occurs when more processed data are available. /// </summary> public event DataAvailableHandler DataAvailable; /// <summary> /// Fires the <see cref="DataAvailable"/> event /// </summary> protected void OnDataAvailable() { if (_ztream.total_out > 0) { if (DataAvailable != null) DataAvailable( _outBuffer, 0, (int)_ztream.total_out); resetOutput(); } } /// <summary> /// Adds more data to the codec to be processed. /// </summary> /// <param name="data">Byte array containing the data to be added to the codec</param> /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks> public void Add(byte[] data) { Add(data,0,data.Length); } /// <summary> /// Adds more data to the codec to be processed. /// </summary> /// <param name="data">Byte array containing the data to be added to the codec</param> /// <param name="offset">The index of the first byte to add from <c>data</c></param> /// <param name="count">The number of bytes to add</param> /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks> /// <remarks>This must be implemented by a derived class</remarks> public abstract void Add(byte[] data, int offset, int count); /// <summary> /// Finishes up any pending data that needs to be processed and handled. /// </summary> /// <remarks>This must be implemented by a derived class</remarks> public abstract void Finish(); /// <summary> /// Gets the checksum of the data that has been added so far /// </summary> public uint Checksum { get { return _checksum; } } #endregion #region Destructor & IDisposable stuff /// <summary> /// Destroys this instance /// </summary> ~CodecBase() { CleanUp(false); } /// <summary> /// Releases any unmanaged resources and calls the <see cref="CleanUp()"/> method of the derived class /// </summary> public void Dispose() { CleanUp(true); } /// <summary> /// Performs any codec specific cleanup /// </summary> /// <remarks>This must be implemented by a derived class</remarks> protected abstract void CleanUp(); // performs the release of the handles and calls the derived CleanUp() private void CleanUp(bool isDisposing) { if (!_isDisposed) { CleanUp(); if (_hInput.IsAllocated) _hInput.Free(); if (_hOutput.IsAllocated) _hOutput.Free(); _isDisposed = true; } } #endregion #region Helper methods /// <summary> /// Copies a number of bytes to the internal codec buffer - ready for processing /// </summary> /// <param name="data">The byte array that contains the data to copy</param> /// <param name="startIndex">The index of the first byte to copy</param> /// <param name="count">The number of bytes to copy from <c>data</c></param> protected void copyInput(byte[] data, int startIndex, int count) { Array.Copy(data, startIndex, _inBuffer,0, count); _ztream.next_in = _hInput.AddrOfPinnedObject(); _ztream.total_in = 0; _ztream.avail_in = (uint)count; } /// <summary> /// Resets the internal output buffers to a known state - ready for processing /// </summary> protected void resetOutput() { _ztream.total_out = 0; _ztream.avail_out = kBufferSize; _ztream.next_out = _hOutput.AddrOfPinnedObject(); } /// <summary> /// Updates the running checksum property /// </summary> /// <param name="newSum">The new checksum value</param> protected void setChecksum(uint newSum) { _checksum = newSum; } #endregion } } |
Added compat/zlib/contrib/dotzlib/DotZLib/Deflater.cs.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | // // © Copyright Henrik Ravn 2004 // // Use, modification and distribution are subject to the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace DotZLib { /// <summary> /// Implements a data compressor, using the deflate algorithm in the ZLib dll /// </summary> public sealed class Deflater : CodecBase { #region Dll imports [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)] private static extern int deflateInit_(ref ZStream sz, int level, string vs, int size); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int deflate(ref ZStream sz, int flush); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int deflateReset(ref ZStream sz); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int deflateEnd(ref ZStream sz); #endregion /// <summary> /// Constructs an new instance of the <c>Deflater</c> /// </summary> /// <param name="level">The compression level to use for this <c>Deflater</c></param> public Deflater(CompressLevel level) : base() { int retval = deflateInit_(ref _ztream, (int)level, Info.Version, Marshal.SizeOf(_ztream)); if (retval != 0) throw new ZLibException(retval, "Could not initialize deflater"); resetOutput(); } /// <summary> /// Adds more data to the codec to be processed. /// </summary> /// <param name="data">Byte array containing the data to be added to the codec</param> /// <param name="offset">The index of the first byte to add from <c>data</c></param> /// <param name="count">The number of bytes to add</param> /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks> public override void Add(byte[] data, int offset, int count) { if (data == null) throw new ArgumentNullException(); if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); if ((offset+count) > data.Length) throw new ArgumentException(); int total = count; int inputIndex = offset; int err = 0; while (err >= 0 && inputIndex < total) { copyInput(data, inputIndex, Math.Min(total - inputIndex, kBufferSize)); while (err >= 0 && _ztream.avail_in > 0) { err = deflate(ref _ztream, (int)FlushTypes.None); if (err == 0) while (_ztream.avail_out == 0) { OnDataAvailable(); err = deflate(ref _ztream, (int)FlushTypes.None); } inputIndex += (int)_ztream.total_in; } } setChecksum( _ztream.adler ); } /// <summary> /// Finishes up any pending data that needs to be processed and handled. /// </summary> public override void Finish() { int err; do { err = deflate(ref _ztream, (int)FlushTypes.Finish); OnDataAvailable(); } while (err == 0); setChecksum( _ztream.adler ); deflateReset(ref _ztream); resetOutput(); } /// <summary> /// Closes the internal zlib deflate stream /// </summary> protected override void CleanUp() { deflateEnd(ref _ztream); } } } |
Added compat/zlib/contrib/dotzlib/DotZLib/DotZLib.cs.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | // // © Copyright Henrik Ravn 2004 // // Use, modification and distribution are subject to the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // using System; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace DotZLib { #region Internal types /// <summary> /// Defines constants for the various flush types used with zlib /// </summary> internal enum FlushTypes { None, Partial, Sync, Full, Finish, Block } #region ZStream structure // internal mapping of the zlib zstream structure for marshalling [StructLayoutAttribute(LayoutKind.Sequential, Pack=4, Size=0, CharSet=CharSet.Ansi)] internal struct ZStream { public IntPtr next_in; public uint avail_in; public uint total_in; public IntPtr next_out; public uint avail_out; public uint total_out; [MarshalAs(UnmanagedType.LPStr)] string msg; uint state; uint zalloc; uint zfree; uint opaque; int data_type; public uint adler; uint reserved; } #endregion #endregion #region Public enums /// <summary> /// Defines constants for the available compression levels in zlib /// </summary> public enum CompressLevel : int { /// <summary> /// The default compression level with a reasonable compromise between compression and speed /// </summary> Default = -1, /// <summary> /// No compression at all. The data are passed straight through. /// </summary> None = 0, /// <summary> /// The maximum compression rate available. /// </summary> Best = 9, /// <summary> /// The fastest available compression level. /// </summary> Fastest = 1 } #endregion #region Exception classes /// <summary> /// The exception that is thrown when an error occurs on the zlib dll /// </summary> public class ZLibException : ApplicationException { /// <summary> /// Initializes a new instance of the <see cref="ZLibException"/> class with a specified /// error message and error code /// </summary> /// <param name="errorCode">The zlib error code that caused the exception</param> /// <param name="msg">A message that (hopefully) describes the error</param> public ZLibException(int errorCode, string msg) : base(String.Format("ZLib error {0} {1}", errorCode, msg)) { } /// <summary> /// Initializes a new instance of the <see cref="ZLibException"/> class with a specified /// error code /// </summary> /// <param name="errorCode">The zlib error code that caused the exception</param> public ZLibException(int errorCode) : base(String.Format("ZLib error {0}", errorCode)) { } } #endregion #region Interfaces /// <summary> /// Declares methods and properties that enables a running checksum to be calculated /// </summary> public interface ChecksumGenerator { /// <summary> /// Gets the current value of the checksum /// </summary> uint Value { get; } /// <summary> /// Clears the current checksum to 0 /// </summary> void Reset(); /// <summary> /// Updates the current checksum with an array of bytes /// </summary> /// <param name="data">The data to update the checksum with</param> void Update(byte[] data); /// <summary> /// Updates the current checksum with part of an array of bytes /// </summary> /// <param name="data">The data to update the checksum with</param> /// <param name="offset">Where in <c>data</c> to start updating</param> /// <param name="count">The number of bytes from <c>data</c> to use</param> /// <exception cref="ArgumentException">The sum of offset and count is larger than the length of <c>data</c></exception> /// <exception cref="ArgumentNullException"><c>data</c> is a null reference</exception> /// <exception cref="ArgumentOutOfRangeException">Offset or count is negative.</exception> void Update(byte[] data, int offset, int count); /// <summary> /// Updates the current checksum with the data from a string /// </summary> /// <param name="data">The string to update the checksum with</param> /// <remarks>The characters in the string are converted by the UTF-8 encoding</remarks> void Update(string data); /// <summary> /// Updates the current checksum with the data from a string, using a specific encoding /// </summary> /// <param name="data">The string to update the checksum with</param> /// <param name="encoding">The encoding to use</param> void Update(string data, Encoding encoding); } /// <summary> /// Represents the method that will be called from a codec when new data /// are available. /// </summary> /// <paramref name="data">The byte array containing the processed data</paramref> /// <paramref name="startIndex">The index of the first processed byte in <c>data</c></paramref> /// <paramref name="count">The number of processed bytes available</paramref> /// <remarks>On return from this method, the data may be overwritten, so grab it while you can. /// You cannot assume that startIndex will be zero. /// </remarks> public delegate void DataAvailableHandler(byte[] data, int startIndex, int count); /// <summary> /// Declares methods and events for implementing compressors/decompressors /// </summary> public interface Codec { /// <summary> /// Occurs when more processed data are available. /// </summary> event DataAvailableHandler DataAvailable; /// <summary> /// Adds more data to the codec to be processed. /// </summary> /// <param name="data">Byte array containing the data to be added to the codec</param> /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks> void Add(byte[] data); /// <summary> /// Adds more data to the codec to be processed. /// </summary> /// <param name="data">Byte array containing the data to be added to the codec</param> /// <param name="offset">The index of the first byte to add from <c>data</c></param> /// <param name="count">The number of bytes to add</param> /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks> void Add(byte[] data, int offset, int count); /// <summary> /// Finishes up any pending data that needs to be processed and handled. /// </summary> void Finish(); /// <summary> /// Gets the checksum of the data that has been added so far /// </summary> uint Checksum { get; } } #endregion #region Classes /// <summary> /// Encapsulates general information about the ZLib library /// </summary> public class Info { #region DLL imports [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern uint zlibCompileFlags(); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern string zlibVersion(); #endregion #region Private stuff private uint _flags; // helper function that unpacks a bitsize mask private static int bitSize(uint bits) { switch (bits) { case 0: return 16; case 1: return 32; case 2: return 64; } return -1; } #endregion /// <summary> /// Constructs an instance of the <c>Info</c> class. /// </summary> public Info() { _flags = zlibCompileFlags(); } /// <summary> /// True if the library is compiled with debug info /// </summary> public bool HasDebugInfo { get { return 0 != (_flags & 0x100); } } /// <summary> /// True if the library is compiled with assembly optimizations /// </summary> public bool UsesAssemblyCode { get { return 0 != (_flags & 0x200); } } /// <summary> /// Gets the size of the unsigned int that was compiled into Zlib /// </summary> public int SizeOfUInt { get { return bitSize(_flags & 3); } } /// <summary> /// Gets the size of the unsigned long that was compiled into Zlib /// </summary> public int SizeOfULong { get { return bitSize((_flags >> 2) & 3); } } /// <summary> /// Gets the size of the pointers that were compiled into Zlib /// </summary> public int SizeOfPointer { get { return bitSize((_flags >> 4) & 3); } } /// <summary> /// Gets the size of the z_off_t type that was compiled into Zlib /// </summary> public int SizeOfOffset { get { return bitSize((_flags >> 6) & 3); } } /// <summary> /// Gets the version of ZLib as a string, e.g. "1.2.1" /// </summary> public static string Version { get { return zlibVersion(); } } } #endregion } |
Added compat/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <VisualStudioProject> <CSHARP ProjectType = "Local" ProductVersion = "7.10.3077" SchemaVersion = "2.0" ProjectGuid = "{BB1EE0B1-1808-46CB-B786-949D91117FC5}" > <Build> <Settings ApplicationIcon = "" AssemblyKeyContainerName = "" AssemblyName = "DotZLib" AssemblyOriginatorKeyFile = "" DefaultClientScript = "JScript" DefaultHTMLPageLayout = "Grid" DefaultTargetSchema = "IE50" DelaySign = "false" OutputType = "Library" PreBuildEvent = "" PostBuildEvent = "" RootNamespace = "DotZLib" RunPostBuildEvent = "OnBuildSuccess" StartupObject = "" > <Config Name = "Debug" AllowUnsafeBlocks = "false" BaseAddress = "285212672" CheckForOverflowUnderflow = "false" ConfigurationOverrideFile = "" DefineConstants = "DEBUG;TRACE" DocumentationFile = "docs\DotZLib.xml" DebugSymbols = "true" FileAlignment = "4096" IncrementalBuild = "false" NoStdLib = "false" NoWarn = "1591" Optimize = "false" OutputPath = "bin\Debug\" RegisterForComInterop = "false" RemoveIntegerChecks = "false" TreatWarningsAsErrors = "false" WarningLevel = "4" /> <Config Name = "Release" AllowUnsafeBlocks = "false" BaseAddress = "285212672" CheckForOverflowUnderflow = "false" ConfigurationOverrideFile = "" DefineConstants = "TRACE" DocumentationFile = "docs\DotZLib.xml" DebugSymbols = "false" FileAlignment = "4096" IncrementalBuild = "false" NoStdLib = "false" NoWarn = "" Optimize = "true" OutputPath = "bin\Release\" RegisterForComInterop = "false" RemoveIntegerChecks = "false" TreatWarningsAsErrors = "false" WarningLevel = "4" /> </Settings> <References> <Reference Name = "System" AssemblyName = "System" HintPath = "C:\WINNT\Microsoft.NET\Framework\v1.1.4322\System.dll" /> <Reference Name = "System.Data" AssemblyName = "System.Data" HintPath = "C:\WINNT\Microsoft.NET\Framework\v1.1.4322\System.Data.dll" /> <Reference Name = "System.XML" AssemblyName = "System.Xml" HintPath = "C:\WINNT\Microsoft.NET\Framework\v1.1.4322\System.XML.dll" /> <Reference Name = "nunit.framework" AssemblyName = "nunit.framework" HintPath = "E:\apps\NUnit V2.1\\bin\nunit.framework.dll" AssemblyFolderKey = "hklm\dn\nunit.framework" /> </References> </Build> <Files> <Include> <File RelPath = "AssemblyInfo.cs" SubType = "Code" BuildAction = "Compile" /> <File RelPath = "ChecksumImpl.cs" SubType = "Code" BuildAction = "Compile" /> <File RelPath = "CircularBuffer.cs" SubType = "Code" BuildAction = "Compile" /> <File RelPath = "CodecBase.cs" SubType = "Code" BuildAction = "Compile" /> <File RelPath = "Deflater.cs" SubType = "Code" BuildAction = "Compile" /> <File RelPath = "DotZLib.cs" SubType = "Code" BuildAction = "Compile" /> <File RelPath = "GZipStream.cs" SubType = "Code" BuildAction = "Compile" /> <File RelPath = "Inflater.cs" SubType = "Code" BuildAction = "Compile" /> <File RelPath = "UnitTests.cs" SubType = "Code" BuildAction = "Compile" /> </Include> </Files> </CSHARP> </VisualStudioProject> |
Added compat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | // // © Copyright Henrik Ravn 2004 // // Use, modification and distribution are subject to the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // using System; using System.IO; using System.Runtime.InteropServices; namespace DotZLib { /// <summary> /// Implements a compressed <see cref="Stream"/>, in GZip (.gz) format. /// </summary> public class GZipStream : Stream, IDisposable { #region Dll Imports [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)] private static extern IntPtr gzopen(string name, string mode); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int gzclose(IntPtr gzFile); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int gzwrite(IntPtr gzFile, int data, int length); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int gzread(IntPtr gzFile, int data, int length); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int gzgetc(IntPtr gzFile); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int gzputc(IntPtr gzFile, int c); #endregion #region Private data private IntPtr _gzFile; private bool _isDisposed = false; private bool _isWriting; #endregion #region Constructors /// <summary> /// Creates a new file as a writeable GZipStream /// </summary> /// <param name="fileName">The name of the compressed file to create</param> /// <param name="level">The compression level to use when adding data</param> /// <exception cref="ZLibException">If an error occurred in the internal zlib function</exception> public GZipStream(string fileName, CompressLevel level) { _isWriting = true; _gzFile = gzopen(fileName, String.Format("wb{0}", (int)level)); if (_gzFile == IntPtr.Zero) throw new ZLibException(-1, "Could not open " + fileName); } /// <summary> /// Opens an existing file as a readable GZipStream /// </summary> /// <param name="fileName">The name of the file to open</param> /// <exception cref="ZLibException">If an error occurred in the internal zlib function</exception> public GZipStream(string fileName) { _isWriting = false; _gzFile = gzopen(fileName, "rb"); if (_gzFile == IntPtr.Zero) throw new ZLibException(-1, "Could not open " + fileName); } #endregion #region Access properties /// <summary> /// Returns true of this stream can be read from, false otherwise /// </summary> public override bool CanRead { get { return !_isWriting; } } /// <summary> /// Returns false. /// </summary> public override bool CanSeek { get { return false; } } /// <summary> /// Returns true if this tsream is writeable, false otherwise /// </summary> public override bool CanWrite { get { return _isWriting; } } #endregion #region Destructor & IDispose stuff /// <summary> /// Destroys this instance /// </summary> ~GZipStream() { cleanUp(false); } /// <summary> /// Closes the external file handle /// </summary> public void Dispose() { cleanUp(true); } // Does the actual closing of the file handle. private void cleanUp(bool isDisposing) { if (!_isDisposed) { gzclose(_gzFile); _isDisposed = true; } } #endregion #region Basic reading and writing /// <summary> /// Attempts to read a number of bytes from the stream. /// </summary> /// <param name="buffer">The destination data buffer</param> /// <param name="offset">The index of the first destination byte in <c>buffer</c></param> /// <param name="count">The number of bytes requested</param> /// <returns>The number of bytes read</returns> /// <exception cref="ArgumentNullException">If <c>buffer</c> is null</exception> /// <exception cref="ArgumentOutOfRangeException">If <c>count</c> or <c>offset</c> are negative</exception> /// <exception cref="ArgumentException">If <c>offset</c> + <c>count</c> is > buffer.Length</exception> /// <exception cref="NotSupportedException">If this stream is not readable.</exception> /// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception> public override int Read(byte[] buffer, int offset, int count) { if (!CanRead) throw new NotSupportedException(); if (buffer == null) throw new ArgumentNullException(); if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); if ((offset+count) > buffer.Length) throw new ArgumentException(); if (_isDisposed) throw new ObjectDisposedException("GZipStream"); GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned); int result; try { result = gzread(_gzFile, h.AddrOfPinnedObject().ToInt32() + offset, count); if (result < 0) throw new IOException(); } finally { h.Free(); } return result; } /// <summary> /// Attempts to read a single byte from the stream. /// </summary> /// <returns>The byte that was read, or -1 in case of error or End-Of-File</returns> public override int ReadByte() { if (!CanRead) throw new NotSupportedException(); if (_isDisposed) throw new ObjectDisposedException("GZipStream"); return gzgetc(_gzFile); } /// <summary> /// Writes a number of bytes to the stream /// </summary> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="count"></param> /// <exception cref="ArgumentNullException">If <c>buffer</c> is null</exception> /// <exception cref="ArgumentOutOfRangeException">If <c>count</c> or <c>offset</c> are negative</exception> /// <exception cref="ArgumentException">If <c>offset</c> + <c>count</c> is > buffer.Length</exception> /// <exception cref="NotSupportedException">If this stream is not writeable.</exception> /// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception> public override void Write(byte[] buffer, int offset, int count) { if (!CanWrite) throw new NotSupportedException(); if (buffer == null) throw new ArgumentNullException(); if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); if ((offset+count) > buffer.Length) throw new ArgumentException(); if (_isDisposed) throw new ObjectDisposedException("GZipStream"); GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { int result = gzwrite(_gzFile, h.AddrOfPinnedObject().ToInt32() + offset, count); if (result < 0) throw new IOException(); } finally { h.Free(); } } /// <summary> /// Writes a single byte to the stream /// </summary> /// <param name="value">The byte to add to the stream.</param> /// <exception cref="NotSupportedException">If this stream is not writeable.</exception> /// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception> public override void WriteByte(byte value) { if (!CanWrite) throw new NotSupportedException(); if (_isDisposed) throw new ObjectDisposedException("GZipStream"); int result = gzputc(_gzFile, (int)value); if (result < 0) throw new IOException(); } #endregion #region Position & length stuff /// <summary> /// Not supported. /// </summary> /// <param name="value"></param> /// <exception cref="NotSupportedException">Always thrown</exception> public override void SetLength(long value) { throw new NotSupportedException(); } /// <summary> /// Not supported. /// </summary> /// <param name="offset"></param> /// <param name="origin"></param> /// <returns></returns> /// <exception cref="NotSupportedException">Always thrown</exception> public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } /// <summary> /// Flushes the <c>GZipStream</c>. /// </summary> /// <remarks>In this implementation, this method does nothing. This is because excessive /// flushing may degrade the achievable compression rates.</remarks> public override void Flush() { // left empty on purpose } /// <summary> /// Gets/sets the current position in the <c>GZipStream</c>. Not supported. /// </summary> /// <remarks>In this implementation this property is not supported</remarks> /// <exception cref="NotSupportedException">Always thrown</exception> public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } /// <summary> /// Gets the size of the stream. Not supported. /// </summary> /// <remarks>In this implementation this property is not supported</remarks> /// <exception cref="NotSupportedException">Always thrown</exception> public override long Length { get { throw new NotSupportedException(); } } #endregion } } |
Added compat/zlib/contrib/dotzlib/DotZLib/Inflater.cs.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | // // © Copyright Henrik Ravn 2004 // // Use, modification and distribution are subject to the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace DotZLib { /// <summary> /// Implements a data decompressor, using the inflate algorithm in the ZLib dll /// </summary> public class Inflater : CodecBase { #region Dll imports [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)] private static extern int inflateInit_(ref ZStream sz, string vs, int size); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int inflate(ref ZStream sz, int flush); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int inflateReset(ref ZStream sz); [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] private static extern int inflateEnd(ref ZStream sz); #endregion /// <summary> /// Constructs an new instance of the <c>Inflater</c> /// </summary> public Inflater() : base() { int retval = inflateInit_(ref _ztream, Info.Version, Marshal.SizeOf(_ztream)); if (retval != 0) throw new ZLibException(retval, "Could not initialize inflater"); resetOutput(); } /// <summary> /// Adds more data to the codec to be processed. /// </summary> /// <param name="data">Byte array containing the data to be added to the codec</param> /// <param name="offset">The index of the first byte to add from <c>data</c></param> /// <param name="count">The number of bytes to add</param> /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks> public override void Add(byte[] data, int offset, int count) { if (data == null) throw new ArgumentNullException(); if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); if ((offset+count) > data.Length) throw new ArgumentException(); int total = count; int inputIndex = offset; int err = 0; while (err >= 0 && inputIndex < total) { copyInput(data, inputIndex, Math.Min(total - inputIndex, kBufferSize)); err = inflate(ref _ztream, (int)FlushTypes.None); if (err == 0) while (_ztream.avail_out == 0) { OnDataAvailable(); err = inflate(ref _ztream, (int)FlushTypes.None); } inputIndex += (int)_ztream.total_in; } setChecksum( _ztream.adler ); } /// <summary> /// Finishes up any pending data that needs to be processed and handled. /// </summary> public override void Finish() { int err; do { err = inflate(ref _ztream, (int)FlushTypes.Finish); OnDataAvailable(); } while (err == 0); setChecksum( _ztream.adler ); inflateReset(ref _ztream); resetOutput(); } /// <summary> /// Closes the internal zlib inflate stream /// </summary> protected override void CleanUp() { inflateEnd(ref _ztream); } } } |
Added compat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | // // © Copyright Henrik Ravn 2004 // // Use, modification and distribution are subject to the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // using System; using System.Collections; using System.IO; // uncomment the define below to include unit tests //#define nunit #if nunit using NUnit.Framework; // Unit tests for the DotZLib class library // ---------------------------------------- // // Use this with NUnit 2 from http://www.nunit.org // namespace DotZLibTests { using DotZLib; // helper methods internal class Utils { public static bool byteArrEqual( byte[] lhs, byte[] rhs ) { if (lhs.Length != rhs.Length) return false; for (int i = lhs.Length-1; i >= 0; --i) if (lhs[i] != rhs[i]) return false; return true; } } [TestFixture] public class CircBufferTests { #region Circular buffer tests [Test] public void SinglePutGet() { CircularBuffer buf = new CircularBuffer(10); Assert.AreEqual( 0, buf.Size ); Assert.AreEqual( -1, buf.Get() ); Assert.IsTrue(buf.Put( 1 )); Assert.AreEqual( 1, buf.Size ); Assert.AreEqual( 1, buf.Get() ); Assert.AreEqual( 0, buf.Size ); Assert.AreEqual( -1, buf.Get() ); } [Test] public void BlockPutGet() { CircularBuffer buf = new CircularBuffer(10); byte[] arr = {1,2,3,4,5,6,7,8,9,10}; Assert.AreEqual( 10, buf.Put(arr,0,10) ); Assert.AreEqual( 10, buf.Size ); Assert.IsFalse( buf.Put(11) ); Assert.AreEqual( 1, buf.Get() ); Assert.IsTrue( buf.Put(11) ); byte[] arr2 = (byte[])arr.Clone(); Assert.AreEqual( 9, buf.Get(arr2,1,9) ); Assert.IsTrue( Utils.byteArrEqual(arr,arr2) ); } #endregion } [TestFixture] public class ChecksumTests { #region CRC32 Tests [Test] public void CRC32_Null() { CRC32Checksum crc32 = new CRC32Checksum(); Assert.AreEqual( 0, crc32.Value ); crc32 = new CRC32Checksum(1); Assert.AreEqual( 1, crc32.Value ); crc32 = new CRC32Checksum(556); Assert.AreEqual( 556, crc32.Value ); } [Test] public void CRC32_Data() { CRC32Checksum crc32 = new CRC32Checksum(); byte[] data = { 1,2,3,4,5,6,7 }; crc32.Update(data); Assert.AreEqual( 0x70e46888, crc32.Value ); crc32 = new CRC32Checksum(); crc32.Update("penguin"); Assert.AreEqual( 0x0e5c1a120, crc32.Value ); crc32 = new CRC32Checksum(1); crc32.Update("penguin"); Assert.AreEqual(0x43b6aa94, crc32.Value); } #endregion #region Adler tests [Test] public void Adler_Null() { AdlerChecksum adler = new AdlerChecksum(); Assert.AreEqual(0, adler.Value); adler = new AdlerChecksum(1); Assert.AreEqual( 1, adler.Value ); adler = new AdlerChecksum(556); Assert.AreEqual( 556, adler.Value ); } [Test] public void Adler_Data() { AdlerChecksum adler = new AdlerChecksum(1); byte[] data = { 1,2,3,4,5,6,7 }; adler.Update(data); Assert.AreEqual( 0x5b001d, adler.Value ); adler = new AdlerChecksum(); adler.Update("penguin"); Assert.AreEqual(0x0bcf02f6, adler.Value ); adler = new AdlerChecksum(1); adler.Update("penguin"); Assert.AreEqual(0x0bd602f7, adler.Value); } #endregion } [TestFixture] public class InfoTests { #region Info tests [Test] public void Info_Version() { Info info = new Info(); Assert.AreEqual("1.3.1", Info.Version); Assert.AreEqual(32, info.SizeOfUInt); Assert.AreEqual(32, info.SizeOfULong); Assert.AreEqual(32, info.SizeOfPointer); Assert.AreEqual(32, info.SizeOfOffset); } #endregion } [TestFixture] public class DeflateInflateTests { #region Deflate tests [Test] public void Deflate_Init() { using (Deflater def = new Deflater(CompressLevel.Default)) { } } private ArrayList compressedData = new ArrayList(); private uint adler1; private ArrayList uncompressedData = new ArrayList(); private uint adler2; public void CDataAvail(byte[] data, int startIndex, int count) { for (int i = 0; i < count; ++i) compressedData.Add(data[i+startIndex]); } [Test] public void Deflate_Compress() { compressedData.Clear(); byte[] testData = new byte[35000]; for (int i = 0; i < testData.Length; ++i) testData[i] = 5; using (Deflater def = new Deflater((CompressLevel)5)) { def.DataAvailable += new DataAvailableHandler(CDataAvail); def.Add(testData); def.Finish(); adler1 = def.Checksum; } } #endregion #region Inflate tests [Test] public void Inflate_Init() { using (Inflater inf = new Inflater()) { } } private void DDataAvail(byte[] data, int startIndex, int count) { for (int i = 0; i < count; ++i) uncompressedData.Add(data[i+startIndex]); } [Test] public void Inflate_Expand() { uncompressedData.Clear(); using (Inflater inf = new Inflater()) { inf.DataAvailable += new DataAvailableHandler(DDataAvail); inf.Add((byte[])compressedData.ToArray(typeof(byte))); inf.Finish(); adler2 = inf.Checksum; } Assert.AreEqual( adler1, adler2 ); } #endregion } [TestFixture] public class GZipStreamTests { #region GZipStream test [Test] public void GZipStream_WriteRead() { using (GZipStream gzOut = new GZipStream("gzstream.gz", CompressLevel.Best)) { BinaryWriter writer = new BinaryWriter(gzOut); writer.Write("hi there"); writer.Write(Math.PI); writer.Write(42); } using (GZipStream gzIn = new GZipStream("gzstream.gz")) { BinaryReader reader = new BinaryReader(gzIn); string s = reader.ReadString(); Assert.AreEqual("hi there",s); double d = reader.ReadDouble(); Assert.AreEqual(Math.PI, d); int i = reader.ReadInt32(); Assert.AreEqual(42,i); } } #endregion } } #endif |
Added compat/zlib/contrib/dotzlib/LICENSE_1_0.txt.
> > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
Added compat/zlib/contrib/dotzlib/readme.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 | This directory contains a .Net wrapper class library for the ZLib1.dll The wrapper includes support for inflating/deflating memory buffers, .Net streaming wrappers for the gz streams part of zlib, and wrappers for the checksum parts of zlib. See DotZLib/UnitTests.cs for examples. Directory structure: -------------------- LICENSE_1_0.txt - License file. readme.txt - This file. DotZLib.chm - Class library documentation DotZLib.build - NAnt build file DotZLib.sln - Microsoft Visual Studio 2003 solution file DotZLib\*.cs - Source files for the class library Unit tests: ----------- The file DotZLib/UnitTests.cs contains unit tests for use with NUnit 2.1 or higher. To include unit tests in the build, define nunit before building. Build instructions: ------------------- 1. Using Visual Studio.Net 2003: Open DotZLib.sln in VS.Net and build from there. Output file (DotZLib.dll) will be found ./DotZLib/bin/release or ./DotZLib/bin/debug, depending on you are building the release or debug version of the library. Check DotZLib/UnitTests.cs for instructions on how to include unit tests in the build. 2. Using NAnt: Open a command prompt with access to the build environment and run nant in the same directory as the DotZLib.build file. You can define 2 properties on the nant command-line to control the build: debug={true|false} to toggle between release/debug builds (default=true). nunit={true|false} to include or exclude unit tests (default=true). Also the target clean will remove binaries. Output file (DotZLib.dll) will be found in either ./DotZLib/bin/release or ./DotZLib/bin/debug, depending on whether you are building the release or debug version of the library. Examples: nant -D:debug=false -D:nunit=false will build a release mode version of the library without unit tests. nant will build a debug version of the library with unit tests nant clean will remove all previously built files. --------------------------------- Copyright (c) Henrik Ravn 2004 Use, modification and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
Added compat/zlib/contrib/gcc_gvmat64/gvmat64.S.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ;uInt longest_match_x64( ; deflate_state *s, ; IPos cur_match); // current match ; gvmat64.S -- Asm portion of the optimized longest_match for 32 bits x86_64 ; (AMD64 on Athlon 64, Opteron, Phenom ; and Intel EM64T on Pentium 4 with EM64T, Pentium D, Core 2 Duo, Core I5/I7) ; this file is translation from gvmat64.asm to GCC 4.x (for Linux, Mac XCode) ; Copyright (C) 1995-2010 Jean-loup Gailly, Brian Raiter and Gilles Vollant. ; ; File written by Gilles Vollant, by converting to assembly the longest_match ; from Jean-loup Gailly in deflate.c of zLib and infoZip zip. ; and by taking inspiration on asm686 with masm, optimised assembly code ; from Brian Raiter, written 1998 ; ; This software is provided 'as-is', without any express or implied ; warranty. In no event will the authors be held liable for any damages ; arising from the use of this software. ; ; Permission is granted to anyone to use this software for any purpose, ; including commercial applications, and to alter it and redistribute it ; freely, subject to the following restrictions: ; ; 1. The origin of this software must not be misrepresented; you must not ; claim that you wrote the original software. If you use this software ; in a product, an acknowledgment in the product documentation would be ; appreciated but is not required. ; 2. Altered source versions must be plainly marked as such, and must not be ; misrepresented as being the original software ; 3. This notice may not be removed or altered from any source distribution. ; ; http://www.zlib.net ; http://www.winimage.com/zLibDll ; http://www.muppetlabs.com/~breadbox/software/assembly.html ; ; to compile this file for zLib, I use option: ; gcc -c -arch x86_64 gvmat64.S ;uInt longest_match(s, cur_match) ; deflate_state *s; ; IPos cur_match; // current match / ; ; with XCode for Mac, I had strange error with some jump on intel syntax ; this is why BEFORE_JMP and AFTER_JMP are used */ #define BEFORE_JMP .att_syntax #define AFTER_JMP .intel_syntax noprefix #ifndef NO_UNDERLINE # define match_init _match_init # define longest_match _longest_match #endif .intel_syntax noprefix .globl match_init, longest_match .text longest_match: #define LocalVarsSize 96 /* ; register used : rax,rbx,rcx,rdx,rsi,rdi,r8,r9,r10,r11,r12 ; free register : r14,r15 ; register can be saved : rsp */ #define chainlenwmask (rsp + 8 - LocalVarsSize) #define nicematch (rsp + 16 - LocalVarsSize) #define save_rdi (rsp + 24 - LocalVarsSize) #define save_rsi (rsp + 32 - LocalVarsSize) #define save_rbx (rsp + 40 - LocalVarsSize) #define save_rbp (rsp + 48 - LocalVarsSize) #define save_r12 (rsp + 56 - LocalVarsSize) #define save_r13 (rsp + 64 - LocalVarsSize) #define save_r14 (rsp + 72 - LocalVarsSize) #define save_r15 (rsp + 80 - LocalVarsSize) /* ; all the +4 offsets are due to the addition of pending_buf_size (in zlib ; in the deflate_state structure since the asm code was first written ; (if you compile with zlib 1.0.4 or older, remove the +4). ; Note : these value are good with a 8 bytes boundary pack structure */ #define MAX_MATCH 258 #define MIN_MATCH 3 #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) /* ;;; Offsets for fields in the deflate_state structure. These numbers ;;; are calculated from the definition of deflate_state, with the ;;; assumption that the compiler will dword-align the fields. (Thus, ;;; changing the definition of deflate_state could easily cause this ;;; program to crash horribly, without so much as a warning at ;;; compile time. Sigh.) ; all the +zlib1222add offsets are due to the addition of fields ; in zlib in the deflate_state structure since the asm code was first written ; (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)"). ; (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0"). ; if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8"). */ /* you can check the structure offset by running #include <stdlib.h> #include <stdio.h> #include "deflate.h" void print_depl() { deflate_state ds; deflate_state *s=&ds; printf("size pointer=%u\n",(int)sizeof(void*)); printf("#define dsWSize %u\n",(int)(((char*)&(s->w_size))-((char*)s))); printf("#define dsWMask %u\n",(int)(((char*)&(s->w_mask))-((char*)s))); printf("#define dsWindow %u\n",(int)(((char*)&(s->window))-((char*)s))); printf("#define dsPrev %u\n",(int)(((char*)&(s->prev))-((char*)s))); printf("#define dsMatchLen %u\n",(int)(((char*)&(s->match_length))-((char*)s))); printf("#define dsPrevMatch %u\n",(int)(((char*)&(s->prev_match))-((char*)s))); printf("#define dsStrStart %u\n",(int)(((char*)&(s->strstart))-((char*)s))); printf("#define dsMatchStart %u\n",(int)(((char*)&(s->match_start))-((char*)s))); printf("#define dsLookahead %u\n",(int)(((char*)&(s->lookahead))-((char*)s))); printf("#define dsPrevLen %u\n",(int)(((char*)&(s->prev_length))-((char*)s))); printf("#define dsMaxChainLen %u\n",(int)(((char*)&(s->max_chain_length))-((char*)s))); printf("#define dsGoodMatch %u\n",(int)(((char*)&(s->good_match))-((char*)s))); printf("#define dsNiceMatch %u\n",(int)(((char*)&(s->nice_match))-((char*)s))); } */ #define dsWSize 68 #define dsWMask 76 #define dsWindow 80 #define dsPrev 96 #define dsMatchLen 144 #define dsPrevMatch 148 #define dsStrStart 156 #define dsMatchStart 160 #define dsLookahead 164 #define dsPrevLen 168 #define dsMaxChainLen 172 #define dsGoodMatch 188 #define dsNiceMatch 192 #define window_size [ rcx + dsWSize] #define WMask [ rcx + dsWMask] #define window_ad [ rcx + dsWindow] #define prev_ad [ rcx + dsPrev] #define strstart [ rcx + dsStrStart] #define match_start [ rcx + dsMatchStart] #define Lookahead [ rcx + dsLookahead] //; 0ffffffffh on infozip #define prev_length [ rcx + dsPrevLen] #define max_chain_length [ rcx + dsMaxChainLen] #define good_match [ rcx + dsGoodMatch] #define nice_match [ rcx + dsNiceMatch] /* ; windows: ; parameter 1 in rcx(deflate state s), param 2 in rdx (cur match) ; see http://weblogs.asp.net/oldnewthing/archive/2004/01/14/58579.aspx and ; http://msdn.microsoft.com/library/en-us/kmarch/hh/kmarch/64bitAMD_8e951dd2-ee77-4728-8702-55ce4b5dd24a.xml.asp ; ; All registers must be preserved across the call, except for ; rax, rcx, rdx, r8, r9, r10, and r11, which are scratch. ; ; gcc on macosx-linux: ; see http://www.x86-64.org/documentation/abi-0.99.pdf ; param 1 in rdi, param 2 in rsi ; rbx, rsp, rbp, r12 to r15 must be preserved ;;; Save registers that the compiler may be using, and adjust esp to ;;; make room for our stack frame. ;;; Retrieve the function arguments. r8d will hold cur_match ;;; throughout the entire function. edx will hold the pointer to the ;;; deflate_state structure during the function's setup (before ;;; entering the main loop. ; ms: parameter 1 in rcx (deflate_state* s), param 2 in edx -> r8 (cur match) ; mac: param 1 in rdi, param 2 rsi ; this clear high 32 bits of r8, which can be garbage in both r8 and rdx */ mov [save_rbx],rbx mov [save_rbp],rbp mov rcx,rdi mov r8d,esi mov [save_r12],r12 mov [save_r13],r13 mov [save_r14],r14 mov [save_r15],r15 //;;; uInt wmask = s->w_mask; //;;; unsigned chain_length = s->max_chain_length; //;;; if (s->prev_length >= s->good_match) { //;;; chain_length >>= 2; //;;; } mov edi, prev_length mov esi, good_match mov eax, WMask mov ebx, max_chain_length cmp edi, esi jl LastMatchGood shr ebx, 2 LastMatchGood: //;;; chainlen is decremented once beforehand so that the function can //;;; use the sign flag instead of the zero flag for the exit test. //;;; It is then shifted into the high word, to make room for the wmask //;;; value, which it will always accompany. dec ebx shl ebx, 16 or ebx, eax //;;; on zlib only //;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; mov eax, nice_match mov [chainlenwmask], ebx mov r10d, Lookahead cmp r10d, eax cmovnl r10d, eax mov [nicematch],r10d //;;; register Bytef *scan = s->window + s->strstart; mov r10, window_ad mov ebp, strstart lea r13, [r10 + rbp] //;;; Determine how many bytes the scan ptr is off from being //;;; dword-aligned. mov r9,r13 neg r13 and r13,3 //;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ? //;;; s->strstart - (IPos)MAX_DIST(s) : NIL; mov eax, window_size sub eax, MIN_LOOKAHEAD xor edi,edi sub ebp, eax mov r11d, prev_length cmovng ebp,edi //;;; int best_len = s->prev_length; //;;; Store the sum of s->window + best_len in esi locally, and in esi. lea rsi,[r10+r11] //;;; register ush scan_start = *(ushf*)scan; //;;; register ush scan_end = *(ushf*)(scan+best_len-1); //;;; Posf *prev = s->prev; movzx r12d,word ptr [r9] movzx ebx, word ptr [r9 + r11 - 1] mov rdi, prev_ad //;;; Jump into the main loop. mov edx, [chainlenwmask] cmp bx,word ptr [rsi + r8 - 1] jz LookupLoopIsZero LookupLoop1: and r8d, edx movzx r8d, word ptr [rdi + r8*2] cmp r8d, ebp jbe LeaveNow sub edx, 0x00010000 BEFORE_JMP js LeaveNow AFTER_JMP LoopEntry1: cmp bx,word ptr [rsi + r8 - 1] BEFORE_JMP jz LookupLoopIsZero AFTER_JMP LookupLoop2: and r8d, edx movzx r8d, word ptr [rdi + r8*2] cmp r8d, ebp BEFORE_JMP jbe LeaveNow AFTER_JMP sub edx, 0x00010000 BEFORE_JMP js LeaveNow AFTER_JMP LoopEntry2: cmp bx,word ptr [rsi + r8 - 1] BEFORE_JMP jz LookupLoopIsZero AFTER_JMP LookupLoop4: and r8d, edx movzx r8d, word ptr [rdi + r8*2] cmp r8d, ebp BEFORE_JMP jbe LeaveNow AFTER_JMP sub edx, 0x00010000 BEFORE_JMP js LeaveNow AFTER_JMP LoopEntry4: cmp bx,word ptr [rsi + r8 - 1] BEFORE_JMP jnz LookupLoop1 jmp LookupLoopIsZero AFTER_JMP /* ;;; do { ;;; match = s->window + cur_match; ;;; if (*(ushf*)(match+best_len-1) != scan_end || ;;; *(ushf*)match != scan_start) continue; ;;; [...] ;;; } while ((cur_match = prev[cur_match & wmask]) > limit ;;; && --chain_length != 0); ;;; ;;; Here is the inner loop of the function. The function will spend the ;;; majority of its time in this loop, and majority of that time will ;;; be spent in the first ten instructions. ;;; ;;; Within this loop: ;;; ebx = scanend ;;; r8d = curmatch ;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask) ;;; esi = windowbestlen - i.e., (window + bestlen) ;;; edi = prev ;;; ebp = limit */ .balign 16 LookupLoop: and r8d, edx movzx r8d, word ptr [rdi + r8*2] cmp r8d, ebp BEFORE_JMP jbe LeaveNow AFTER_JMP sub edx, 0x00010000 BEFORE_JMP js LeaveNow AFTER_JMP LoopEntry: cmp bx,word ptr [rsi + r8 - 1] BEFORE_JMP jnz LookupLoop1 AFTER_JMP LookupLoopIsZero: cmp r12w, word ptr [r10 + r8] BEFORE_JMP jnz LookupLoop1 AFTER_JMP //;;; Store the current value of chainlen. mov [chainlenwmask], edx /* ;;; Point edi to the string under scrutiny, and esi to the string we ;;; are hoping to match it up with. In actuality, esi and edi are ;;; both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and edx is ;;; initialized to -(MAX_MATCH_8 - scanalign). */ lea rsi,[r8+r10] mov rdx, 0xfffffffffffffef8 //; -(MAX_MATCH_8) lea rsi, [rsi + r13 + 0x0108] //;MAX_MATCH_8] lea rdi, [r9 + r13 + 0x0108] //;MAX_MATCH_8] prefetcht1 [rsi+rdx] prefetcht1 [rdi+rdx] /* ;;; Test the strings for equality, 8 bytes at a time. At the end, ;;; adjust rdx so that it is offset to the exact byte that mismatched. ;;; ;;; We already know at this point that the first three bytes of the ;;; strings match each other, and they can be safely passed over before ;;; starting the compare loop. So what this code does is skip over 0-3 ;;; bytes, as much as necessary in order to dword-align the edi ;;; pointer. (rsi will still be misaligned three times out of four.) ;;; ;;; It should be confessed that this loop usually does not represent ;;; much of the total running time. Replacing it with a more ;;; straightforward "rep cmpsb" would not drastically degrade ;;; performance. */ LoopCmps: mov rax, [rsi + rdx] xor rax, [rdi + rdx] jnz LeaveLoopCmps mov rax, [rsi + rdx + 8] xor rax, [rdi + rdx + 8] jnz LeaveLoopCmps8 mov rax, [rsi + rdx + 8+8] xor rax, [rdi + rdx + 8+8] jnz LeaveLoopCmps16 add rdx,8+8+8 BEFORE_JMP jnz LoopCmps jmp LenMaximum AFTER_JMP LeaveLoopCmps16: add rdx,8 LeaveLoopCmps8: add rdx,8 LeaveLoopCmps: test eax, 0x0000FFFF jnz LenLower test eax,0xffffffff jnz LenLower32 add rdx,4 shr rax,32 or ax,ax BEFORE_JMP jnz LenLower AFTER_JMP LenLower32: shr eax,16 add rdx,2 LenLower: sub al, 1 adc rdx, 0 //;;; Calculate the length of the match. If it is longer than MAX_MATCH, //;;; then automatically accept it as the best possible match and leave. lea rax, [rdi + rdx] sub rax, r9 cmp eax, MAX_MATCH BEFORE_JMP jge LenMaximum AFTER_JMP /* ;;; If the length of the match is not longer than the best match we ;;; have so far, then forget it and return to the lookup loop. ;/////////////////////////////////// */ cmp eax, r11d jg LongerMatch lea rsi,[r10+r11] mov rdi, prev_ad mov edx, [chainlenwmask] BEFORE_JMP jmp LookupLoop AFTER_JMP /* ;;; s->match_start = cur_match; ;;; best_len = len; ;;; if (len >= nice_match) break; ;;; scan_end = *(ushf*)(scan+best_len-1); */ LongerMatch: mov r11d, eax mov match_start, r8d cmp eax, [nicematch] BEFORE_JMP jge LeaveNow AFTER_JMP lea rsi,[r10+rax] movzx ebx, word ptr [r9 + rax - 1] mov rdi, prev_ad mov edx, [chainlenwmask] BEFORE_JMP jmp LookupLoop AFTER_JMP //;;; Accept the current string, with the maximum possible length. LenMaximum: mov r11d,MAX_MATCH mov match_start, r8d //;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len; //;;; return s->lookahead; LeaveNow: mov eax, Lookahead cmp r11d, eax cmovng eax, r11d //;;; Restore the stack and return from whence we came. // mov rsi,[save_rsi] // mov rdi,[save_rdi] mov rbx,[save_rbx] mov rbp,[save_rbp] mov r12,[save_r12] mov r13,[save_r13] mov r14,[save_r14] mov r15,[save_r15] ret 0 //; please don't remove this string ! //; Your can freely use gvmat64 in any free or commercial app //; but it is far better don't remove the string in the binary! // db 0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998, converted to amd 64 by Gilles Vollant 2005",0dh,0ah,0 match_init: ret 0 |
Added compat/zlib/contrib/infback9/README.
> | 1 | See infback9.h for what this is and how to use it. |
Added compat/zlib/contrib/infback9/infback9.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 | /* infback9.c -- inflate deflate64 data using a call-back interface * Copyright (C) 1995-2008 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "infback9.h" #include "inftree9.h" #include "inflate9.h" #define WSIZE 65536UL /* strm provides memory allocation functions in zalloc and zfree, or Z_NULL to use the library memory allocation functions. window is a user-supplied window and output buffer that is 64K bytes. */ int ZEXPORT inflateBack9Init_(z_stream FAR *strm, unsigned char FAR *window, const char *version, int stream_size) { struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != (int)(sizeof(z_stream))) return Z_VERSION_ERROR; if (strm == Z_NULL || window == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { strm->zalloc = zcalloc; strm->opaque = (voidpf)0; } if (strm->zfree == (free_func)0) strm->zfree = zcfree; state = (struct inflate_state FAR *)ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (voidpf)state; state->window = window; return Z_OK; } /* Build and output length and distance decoding tables for fixed code decoding. */ #ifdef MAKEFIXED #include <stdio.h> void makefixed9(void) { unsigned sym, bits, low, size; code *next, *lenfix, *distfix; struct inflate_state state; code fixed[544]; /* literal/length table */ sym = 0; while (sym < 144) state.lens[sym++] = 8; while (sym < 256) state.lens[sym++] = 9; while (sym < 280) state.lens[sym++] = 7; while (sym < 288) state.lens[sym++] = 8; next = fixed; lenfix = next; bits = 9; inflate_table9(LENS, state.lens, 288, &(next), &(bits), state.work); /* distance table */ sym = 0; while (sym < 32) state.lens[sym++] = 5; distfix = next; bits = 5; inflate_table9(DISTS, state.lens, 32, &(next), &(bits), state.work); /* write tables */ puts(" /* inffix9.h -- table for decoding deflate64 fixed codes"); puts(" * Generated automatically by makefixed9()."); puts(" */"); puts(""); puts(" /* WARNING: this file should *not* be used by applications."); puts(" It is part of the implementation of this library and is"); puts(" subject to change. Applications should only use zlib.h."); puts(" */"); puts(""); size = 1U << 9; printf(" static const code lenfix[%u] = {", size); low = 0; for (;;) { if ((low % 6) == 0) printf("\n "); printf("{%u,%u,%d}", lenfix[low].op, lenfix[low].bits, lenfix[low].val); if (++low == size) break; putchar(','); } puts("\n };"); size = 1U << 5; printf("\n static const code distfix[%u] = {", size); low = 0; for (;;) { if ((low % 5) == 0) printf("\n "); printf("{%u,%u,%d}", distfix[low].op, distfix[low].bits, distfix[low].val); if (++low == size) break; putchar(','); } puts("\n };"); } #endif /* MAKEFIXED */ /* Macros for inflateBack(): */ /* Clear the input bit accumulator */ #define INITBITS() \ do { \ hold = 0; \ bits = 0; \ } while (0) /* Assure that some input is available. If input is requested, but denied, then return a Z_BUF_ERROR from inflateBack(). */ #define PULL() \ do { \ if (have == 0) { \ have = in(in_desc, &next); \ if (have == 0) { \ next = Z_NULL; \ ret = Z_BUF_ERROR; \ goto inf_leave; \ } \ } \ } while (0) /* Get a byte of input into the bit accumulator, or return from inflateBack() with an error if there is no input available. */ #define PULLBYTE() \ do { \ PULL(); \ have--; \ hold += (unsigned long)(*next++) << bits; \ bits += 8; \ } while (0) /* Assure that there are at least n bits in the bit accumulator. If there is not enough available input to do that, then return from inflateBack() with an error. */ #define NEEDBITS(n) \ do { \ while (bits < (unsigned)(n)) \ PULLBYTE(); \ } while (0) /* Return the low n bits of the bit accumulator (n <= 16) */ #define BITS(n) \ ((unsigned)hold & ((1U << (n)) - 1)) /* Remove n bits from the bit accumulator */ #define DROPBITS(n) \ do { \ hold >>= (n); \ bits -= (unsigned)(n); \ } while (0) /* Remove zero to seven bits as needed to go to a byte boundary */ #define BYTEBITS() \ do { \ hold >>= bits & 7; \ bits -= bits & 7; \ } while (0) /* Assure that some output space is available, by writing out the window if it's full. If the write fails, return from inflateBack() with a Z_BUF_ERROR. */ #define ROOM() \ do { \ if (left == 0) { \ put = window; \ left = WSIZE; \ wrap = 1; \ if (out(out_desc, put, (unsigned)left)) { \ ret = Z_BUF_ERROR; \ goto inf_leave; \ } \ } \ } while (0) /* strm provides the memory allocation functions and window buffer on input, and provides information on the unused input on return. For Z_DATA_ERROR returns, strm will also provide an error message. in() and out() are the call-back input and output functions. When inflateBack() needs more input, it calls in(). When inflateBack() has filled the window with output, or when it completes with data in the window, it calls out() to write out the data. The application must not change the provided input until in() is called again or inflateBack() returns. The application must not change the window/output buffer until inflateBack() returns. in() and out() are called with a descriptor parameter provided in the inflateBack() call. This parameter can be a structure that provides the information required to do the read or write, as well as accumulated information on the input and output such as totals and check values. in() should return zero on failure. out() should return non-zero on failure. If either in() or out() fails, than inflateBack() returns a Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it was in() or out() that caused in the error. Otherwise, inflateBack() returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format error, or Z_MEM_ERROR if it could not allocate memory for the state. inflateBack() can also return Z_STREAM_ERROR if the input parameters are not correct, i.e. strm is Z_NULL or the state was not initialized. */ int ZEXPORT inflateBack9(z_stream FAR *strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc) { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have; /* available input */ unsigned long left; /* available output */ inflate_mode mode; /* current inflate mode */ int lastblock; /* true if processing last block */ int wrap; /* true if the window has wrapped */ unsigned char FAR *window; /* allocated sliding window, if needed */ unsigned long hold; /* bit buffer */ unsigned bits; /* bits in bit buffer */ unsigned extra; /* extra bits needed */ unsigned long length; /* literal or length of data to copy */ unsigned long offset; /* distance back to copy string from */ unsigned long copy; /* number of stored or match bytes to copy */ unsigned char FAR *from; /* where to copy match bytes from */ code const FAR *lencode; /* starting table for length/literal codes */ code const FAR *distcode; /* starting table for distance codes */ unsigned lenbits; /* index bits for lencode */ unsigned distbits; /* index bits for distcode */ code here; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; #include "inffix9.h" /* Check that the strm exists and that the state was initialized */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* Reset the state */ strm->msg = Z_NULL; mode = TYPE; lastblock = 0; wrap = 0; window = state->window; next = strm->next_in; have = next != Z_NULL ? strm->avail_in : 0; hold = 0; bits = 0; put = window; left = WSIZE; lencode = Z_NULL; distcode = Z_NULL; /* Inflate until end of block marked as last */ for (;;) switch (mode) { case TYPE: /* determine and dispatch block type */ if (lastblock) { BYTEBITS(); mode = DONE; break; } NEEDBITS(3); lastblock = BITS(1); DROPBITS(1); switch (BITS(2)) { case 0: /* stored block */ Tracev((stderr, "inflate: stored block%s\n", lastblock ? " (last)" : "")); mode = STORED; break; case 1: /* fixed block */ lencode = lenfix; lenbits = 9; distcode = distfix; distbits = 5; Tracev((stderr, "inflate: fixed codes block%s\n", lastblock ? " (last)" : "")); mode = LEN; /* decode codes */ break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", lastblock ? " (last)" : "")); mode = TABLE; break; case 3: strm->msg = (char *)"invalid block type"; mode = BAD; } DROPBITS(2); break; case STORED: /* get and verify stored block length */ BYTEBITS(); /* go to byte boundary */ NEEDBITS(32); if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { strm->msg = (char *)"invalid stored block lengths"; mode = BAD; break; } length = (unsigned)hold & 0xffff; Tracev((stderr, "inflate: stored length %lu\n", length)); INITBITS(); /* copy stored block from input to output */ while (length != 0) { copy = length; PULL(); ROOM(); if (copy > have) copy = have; if (copy > left) copy = left; zmemcpy(put, next, copy); have -= copy; next += copy; left -= copy; put += copy; length -= copy; } Tracev((stderr, "inflate: stored end\n")); mode = TYPE; break; case TABLE: /* get dynamic table entries descriptor */ NEEDBITS(14); state->nlen = BITS(5) + 257; DROPBITS(5); state->ndist = BITS(5) + 1; DROPBITS(5); state->ncode = BITS(4) + 4; DROPBITS(4); if (state->nlen > 286) { strm->msg = (char *)"too many length symbols"; mode = BAD; break; } Tracev((stderr, "inflate: table sizes ok\n")); /* get code length code lengths (not a typo) */ state->have = 0; while (state->have < state->ncode) { NEEDBITS(3); state->lens[order[state->have++]] = (unsigned short)BITS(3); DROPBITS(3); } while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; lencode = (code const FAR *)(state->next); lenbits = 7; ret = inflate_table9(CODES, state->lens, 19, &(state->next), &(lenbits), state->work); if (ret) { strm->msg = (char *)"invalid code lengths set"; mode = BAD; break; } Tracev((stderr, "inflate: code lengths ok\n")); /* get length and distance code code lengths */ state->have = 0; while (state->have < state->nlen + state->ndist) { for (;;) { here = lencode[BITS(lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.val < 16) { NEEDBITS(here.bits); DROPBITS(here.bits); state->lens[state->have++] = here.val; } else { if (here.val == 16) { NEEDBITS(here.bits + 2); DROPBITS(here.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; mode = BAD; break; } len = (unsigned)(state->lens[state->have - 1]); copy = 3 + BITS(2); DROPBITS(2); } else if (here.val == 17) { NEEDBITS(here.bits + 3); DROPBITS(here.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { NEEDBITS(here.bits + 7); DROPBITS(here.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); } if (state->have + copy > state->nlen + state->ndist) { strm->msg = (char *)"invalid bit length repeat"; mode = BAD; break; } while (copy--) state->lens[state->have++] = (unsigned short)len; } } /* handle error breaks in while */ if (mode == BAD) break; /* check for end-of-block code (better have one) */ if (state->lens[256] == 0) { strm->msg = (char *)"invalid code -- missing end-of-block"; mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftree9.h concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; lencode = (code const FAR *)(state->next); lenbits = 9; ret = inflate_table9(LENS, state->lens, state->nlen, &(state->next), &(lenbits), state->work); if (ret) { strm->msg = (char *)"invalid literal/lengths set"; mode = BAD; break; } distcode = (code const FAR *)(state->next); distbits = 6; ret = inflate_table9(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(distbits), state->work); if (ret) { strm->msg = (char *)"invalid distances set"; mode = BAD; break; } Tracev((stderr, "inflate: codes ok\n")); mode = LEN; case LEN: /* get a literal, length, or end-of-block code */ for (;;) { here = lencode[BITS(lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.op && (here.op & 0xf0) == 0) { last = here; for (;;) { here = lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); } DROPBITS(here.bits); length = (unsigned)here.val; /* process literal */ if (here.op == 0) { Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); ROOM(); *put++ = (unsigned char)(length); left--; mode = LEN; break; } /* process end of block */ if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); mode = TYPE; break; } /* invalid code */ if (here.op & 64) { strm->msg = (char *)"invalid literal/length code"; mode = BAD; break; } /* length code -- get extra bits, if any */ extra = (unsigned)(here.op) & 31; if (extra != 0) { NEEDBITS(extra); length += BITS(extra); DROPBITS(extra); } Tracevv((stderr, "inflate: length %lu\n", length)); /* get distance code */ for (;;) { here = distcode[BITS(distbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if ((here.op & 0xf0) == 0) { last = here; for (;;) { here = distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); } DROPBITS(here.bits); if (here.op & 64) { strm->msg = (char *)"invalid distance code"; mode = BAD; break; } offset = (unsigned)here.val; /* get distance extra bits, if any */ extra = (unsigned)(here.op) & 15; if (extra != 0) { NEEDBITS(extra); offset += BITS(extra); DROPBITS(extra); } if (offset > WSIZE - (wrap ? 0: left)) { strm->msg = (char *)"invalid distance too far back"; mode = BAD; break; } Tracevv((stderr, "inflate: distance %lu\n", offset)); /* copy match from window to output */ do { ROOM(); copy = WSIZE - offset; if (copy < left) { from = put + copy; copy = left - copy; } else { from = put - offset; copy = left; } if (copy > length) copy = length; length -= copy; left -= copy; do { *put++ = *from++; } while (--copy); } while (length != 0); break; case DONE: /* inflate stream terminated properly -- write leftover output */ ret = Z_STREAM_END; if (left < WSIZE) { if (out(out_desc, window, (unsigned)(WSIZE - left))) ret = Z_BUF_ERROR; } goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; default: /* can't happen, but makes compilers happy */ ret = Z_STREAM_ERROR; goto inf_leave; } /* Return unused input */ inf_leave: strm->next_in = next; strm->avail_in = have; return ret; } int ZEXPORT inflateBack9End(z_stream FAR *strm) { if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) return Z_STREAM_ERROR; ZFREE(strm, strm->state); strm->state = Z_NULL; Tracev((stderr, "inflate: end\n")); return Z_OK; } |
Added compat/zlib/contrib/infback9/infback9.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* infback9.h -- header for using inflateBack9 functions * Copyright (C) 2003 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * This header file and associated patches provide a decoder for PKWare's * undocumented deflate64 compression method (method 9). Use with infback9.c, * inftree9.h, inftree9.c, and inffix9.h. These patches are not supported. * This should be compiled with zlib, since it uses zutil.h and zutil.o. * This code has not yet been tested on 16-bit architectures. See the * comments in zlib.h for inflateBack() usage. These functions are used * identically, except that there is no windowBits parameter, and a 64K * window must be provided. Also if int's are 16 bits, then a zero for * the third parameter of the "out" function actually means 65536UL. * zlib.h must be included before this header file. */ #ifdef __cplusplus extern "C" { #endif ZEXTERN int ZEXPORT inflateBack9(z_stream FAR *strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc); ZEXTERN int ZEXPORT inflateBack9End(z_stream FAR *strm); ZEXTERN int ZEXPORT inflateBack9Init_(z_stream FAR *strm, unsigned char FAR *window, const char *version, int stream_size); #define inflateBack9Init(strm, window) \ inflateBack9Init_((strm), (window), \ ZLIB_VERSION, sizeof(z_stream)) #ifdef __cplusplus } #endif |
Added compat/zlib/contrib/infback9/inffix9.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* inffix9.h -- table for decoding deflate64 fixed codes * Generated automatically by makefixed9(). */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of this library and is subject to change. Applications should only use zlib.h. */ static const code lenfix[512] = { {96,7,0},{0,8,80},{0,8,16},{132,8,115},{130,7,31},{0,8,112}, {0,8,48},{0,9,192},{128,7,10},{0,8,96},{0,8,32},{0,9,160}, {0,8,0},{0,8,128},{0,8,64},{0,9,224},{128,7,6},{0,8,88}, {0,8,24},{0,9,144},{131,7,59},{0,8,120},{0,8,56},{0,9,208}, {129,7,17},{0,8,104},{0,8,40},{0,9,176},{0,8,8},{0,8,136}, {0,8,72},{0,9,240},{128,7,4},{0,8,84},{0,8,20},{133,8,227}, {131,7,43},{0,8,116},{0,8,52},{0,9,200},{129,7,13},{0,8,100}, {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232}, {128,7,8},{0,8,92},{0,8,28},{0,9,152},{132,7,83},{0,8,124}, {0,8,60},{0,9,216},{130,7,23},{0,8,108},{0,8,44},{0,9,184}, {0,8,12},{0,8,140},{0,8,76},{0,9,248},{128,7,3},{0,8,82}, {0,8,18},{133,8,163},{131,7,35},{0,8,114},{0,8,50},{0,9,196}, {129,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},{0,8,130}, {0,8,66},{0,9,228},{128,7,7},{0,8,90},{0,8,26},{0,9,148}, {132,7,67},{0,8,122},{0,8,58},{0,9,212},{130,7,19},{0,8,106}, {0,8,42},{0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244}, {128,7,5},{0,8,86},{0,8,22},{65,8,0},{131,7,51},{0,8,118}, {0,8,54},{0,9,204},{129,7,15},{0,8,102},{0,8,38},{0,9,172}, {0,8,6},{0,8,134},{0,8,70},{0,9,236},{128,7,9},{0,8,94}, {0,8,30},{0,9,156},{132,7,99},{0,8,126},{0,8,62},{0,9,220}, {130,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{133,8,131}, {130,7,31},{0,8,113},{0,8,49},{0,9,194},{128,7,10},{0,8,97}, {0,8,33},{0,9,162},{0,8,1},{0,8,129},{0,8,65},{0,9,226}, {128,7,6},{0,8,89},{0,8,25},{0,9,146},{131,7,59},{0,8,121}, {0,8,57},{0,9,210},{129,7,17},{0,8,105},{0,8,41},{0,9,178}, {0,8,9},{0,8,137},{0,8,73},{0,9,242},{128,7,4},{0,8,85}, {0,8,21},{144,8,3},{131,7,43},{0,8,117},{0,8,53},{0,9,202}, {129,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133}, {0,8,69},{0,9,234},{128,7,8},{0,8,93},{0,8,29},{0,9,154}, {132,7,83},{0,8,125},{0,8,61},{0,9,218},{130,7,23},{0,8,109}, {0,8,45},{0,9,186},{0,8,13},{0,8,141},{0,8,77},{0,9,250}, {128,7,3},{0,8,83},{0,8,19},{133,8,195},{131,7,35},{0,8,115}, {0,8,51},{0,9,198},{129,7,11},{0,8,99},{0,8,35},{0,9,166}, {0,8,3},{0,8,131},{0,8,67},{0,9,230},{128,7,7},{0,8,91}, {0,8,27},{0,9,150},{132,7,67},{0,8,123},{0,8,59},{0,9,214}, {130,7,19},{0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139}, {0,8,75},{0,9,246},{128,7,5},{0,8,87},{0,8,23},{77,8,0}, {131,7,51},{0,8,119},{0,8,55},{0,9,206},{129,7,15},{0,8,103}, {0,8,39},{0,9,174},{0,8,7},{0,8,135},{0,8,71},{0,9,238}, {128,7,9},{0,8,95},{0,8,31},{0,9,158},{132,7,99},{0,8,127}, {0,8,63},{0,9,222},{130,7,27},{0,8,111},{0,8,47},{0,9,190}, {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80}, {0,8,16},{132,8,115},{130,7,31},{0,8,112},{0,8,48},{0,9,193}, {128,7,10},{0,8,96},{0,8,32},{0,9,161},{0,8,0},{0,8,128}, {0,8,64},{0,9,225},{128,7,6},{0,8,88},{0,8,24},{0,9,145}, {131,7,59},{0,8,120},{0,8,56},{0,9,209},{129,7,17},{0,8,104}, {0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},{0,9,241}, {128,7,4},{0,8,84},{0,8,20},{133,8,227},{131,7,43},{0,8,116}, {0,8,52},{0,9,201},{129,7,13},{0,8,100},{0,8,36},{0,9,169}, {0,8,4},{0,8,132},{0,8,68},{0,9,233},{128,7,8},{0,8,92}, {0,8,28},{0,9,153},{132,7,83},{0,8,124},{0,8,60},{0,9,217}, {130,7,23},{0,8,108},{0,8,44},{0,9,185},{0,8,12},{0,8,140}, {0,8,76},{0,9,249},{128,7,3},{0,8,82},{0,8,18},{133,8,163}, {131,7,35},{0,8,114},{0,8,50},{0,9,197},{129,7,11},{0,8,98}, {0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, {128,7,7},{0,8,90},{0,8,26},{0,9,149},{132,7,67},{0,8,122}, {0,8,58},{0,9,213},{130,7,19},{0,8,106},{0,8,42},{0,9,181}, {0,8,10},{0,8,138},{0,8,74},{0,9,245},{128,7,5},{0,8,86}, {0,8,22},{65,8,0},{131,7,51},{0,8,118},{0,8,54},{0,9,205}, {129,7,15},{0,8,102},{0,8,38},{0,9,173},{0,8,6},{0,8,134}, {0,8,70},{0,9,237},{128,7,9},{0,8,94},{0,8,30},{0,9,157}, {132,7,99},{0,8,126},{0,8,62},{0,9,221},{130,7,27},{0,8,110}, {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253}, {96,7,0},{0,8,81},{0,8,17},{133,8,131},{130,7,31},{0,8,113}, {0,8,49},{0,9,195},{128,7,10},{0,8,97},{0,8,33},{0,9,163}, {0,8,1},{0,8,129},{0,8,65},{0,9,227},{128,7,6},{0,8,89}, {0,8,25},{0,9,147},{131,7,59},{0,8,121},{0,8,57},{0,9,211}, {129,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},{0,8,137}, {0,8,73},{0,9,243},{128,7,4},{0,8,85},{0,8,21},{144,8,3}, {131,7,43},{0,8,117},{0,8,53},{0,9,203},{129,7,13},{0,8,101}, {0,8,37},{0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235}, {128,7,8},{0,8,93},{0,8,29},{0,9,155},{132,7,83},{0,8,125}, {0,8,61},{0,9,219},{130,7,23},{0,8,109},{0,8,45},{0,9,187}, {0,8,13},{0,8,141},{0,8,77},{0,9,251},{128,7,3},{0,8,83}, {0,8,19},{133,8,195},{131,7,35},{0,8,115},{0,8,51},{0,9,199}, {129,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, {0,8,67},{0,9,231},{128,7,7},{0,8,91},{0,8,27},{0,9,151}, {132,7,67},{0,8,123},{0,8,59},{0,9,215},{130,7,19},{0,8,107}, {0,8,43},{0,9,183},{0,8,11},{0,8,139},{0,8,75},{0,9,247}, {128,7,5},{0,8,87},{0,8,23},{77,8,0},{131,7,51},{0,8,119}, {0,8,55},{0,9,207},{129,7,15},{0,8,103},{0,8,39},{0,9,175}, {0,8,7},{0,8,135},{0,8,71},{0,9,239},{128,7,9},{0,8,95}, {0,8,31},{0,9,159},{132,7,99},{0,8,127},{0,8,63},{0,9,223}, {130,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143}, {0,8,79},{0,9,255} }; static const code distfix[32] = { {128,5,1},{135,5,257},{131,5,17},{139,5,4097},{129,5,5}, {137,5,1025},{133,5,65},{141,5,16385},{128,5,3},{136,5,513}, {132,5,33},{140,5,8193},{130,5,9},{138,5,2049},{134,5,129}, {142,5,32769},{128,5,2},{135,5,385},{131,5,25},{139,5,6145}, {129,5,7},{137,5,1537},{133,5,97},{141,5,24577},{128,5,4}, {136,5,769},{132,5,49},{140,5,12289},{130,5,13},{138,5,3073}, {134,5,193},{142,5,49153} }; |
Added compat/zlib/contrib/infback9/inflate9.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* inflate9.h -- internal inflate state definition * Copyright (C) 1995-2003 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Possible inflate modes between inflate() calls */ typedef enum { TYPE, /* i: waiting for type bits, including last-flag bit */ STORED, /* i: waiting for stored size (length and complement) */ TABLE, /* i: waiting for dynamic block table lengths */ LEN, /* i: waiting for length/lit code */ DONE, /* finished check, done -- remain here until reset */ BAD /* got a data error -- remain here until reset */ } inflate_mode; /* State transitions between above modes - (most modes can go to the BAD mode -- not shown for clarity) Read deflate blocks: TYPE -> STORED or TABLE or LEN or DONE STORED -> TYPE TABLE -> LENLENS -> CODELENS -> LEN Read deflate codes: LEN -> LEN or TYPE */ /* state maintained between inflate() calls. Approximately 7K bytes. */ struct inflate_state { /* sliding window */ unsigned char FAR *window; /* allocated sliding window, if needed */ /* dynamic table building */ unsigned ncode; /* number of code length code lengths */ unsigned nlen; /* number of length code lengths */ unsigned ndist; /* number of distance code lengths */ unsigned have; /* number of code lengths in lens[] */ code FAR *next; /* next available space in codes[] */ unsigned short lens[320]; /* temporary storage for code lengths */ unsigned short work[288]; /* work area for code table building */ code codes[ENOUGH]; /* space for code tables */ }; |
Added compat/zlib/contrib/infback9/inftree9.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 | /* inftree9.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftree9.h" #define MAXBITS 15 const char inflate9_copyright[] = " inflate9 1.3.1 Copyright 1995-2024 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* Build a set of tables to decode the provided canonical Huffman code. The code lengths are lens[0..codes-1]. The result starts at *table, whose indices are 0..2^bits-1. work is a writable array of at least lens shorts, which is used as a work area. type is the type of code to be generated, CODES, LENS, or DISTS. On return, zero is success, -1 is an invalid code, and +1 means that ENOUGH isn't enough. table on return points to the next available entry's address. bits is the requested root table index bits, and on return it is the actual root table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ int inflate_table9(codetype type, unsigned short FAR *lens, unsigned codes, code FAR * FAR *table, unsigned FAR *bits, unsigned short FAR *work) { unsigned len; /* a code's length in bits */ unsigned sym; /* index of code symbols */ unsigned min, max; /* minimum and maximum code lengths */ unsigned root; /* number of index bits for root table */ unsigned curr; /* number of index bits for current table */ unsigned drop; /* code bits to drop for sub-table */ int left; /* number of prefix codes available */ unsigned used; /* code entries in table used */ unsigned huff; /* Huffman code */ unsigned incr; /* for incrementing code, index */ unsigned fill; /* index for replicating entries */ unsigned low; /* low bits for current root entry */ unsigned mask; /* mask for low root bits */ code this; /* table entry for duplication */ code FAR *next; /* next available space in table */ const unsigned short FAR *base; /* base value table to use */ const unsigned short FAR *extra; /* extra bits table to use */ int end; /* use base and extra for symbol > end */ unsigned short count[MAXBITS+1]; /* number of codes of each length */ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ static const unsigned short lbase[31] = { /* Length codes 257..285 base */ 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, 3, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 130, 130, 130, 130, 131, 131, 131, 131, 132, 132, 132, 132, 133, 133, 133, 133, 144, 203, 77}; static const unsigned short dbase[32] = { /* Distance codes 0..31 base */ 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, 32769, 49153}; static const unsigned short dext[32] = { /* Distance codes 0..31 extra */ 128, 128, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141, 142, 142}; /* Process a set of code lengths to create a canonical Huffman code. The code lengths are lens[0..codes-1]. Each length corresponds to the symbols 0..codes-1. The Huffman code is generated by first sorting the symbols by length from short to long, and retaining the symbol order for codes with equal lengths. Then the code starts with all zero bits for the first code of the shortest length, and the codes are integer increments for the same length, and zeros are appended as the length increases. For the deflate format, these bits are stored backwards from their more natural integer increment ordering, and so when the decoding tables are built in the large loop below, the integer codes are incremented backwards. This routine assumes, but does not check, that all of the entries in lens[] are in the range 0..MAXBITS. The caller must assure this. 1..MAXBITS is interpreted as that code length. zero means that that symbol does not occur in this code. The codes are sorted by computing a count of codes for each length, creating from that a table of starting indices for each length in the sorted table, and then entering the symbols in order in the sorted table. The sorted table is work[], with that space being provided by the caller. The length counts are used for other purposes as well, i.e. finding the minimum and maximum length codes, determining if there are any codes at all, checking for a valid set of lengths, and looking ahead at length counts to determine sub-table sizes when building the decoding tables. */ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ for (len = 0; len <= MAXBITS; len++) count[len] = 0; for (sym = 0; sym < codes; sym++) count[lens[sym]]++; /* bound code lengths, force root to be within code lengths */ root = *bits; for (max = MAXBITS; max >= 1; max--) if (count[max] != 0) break; if (root > max) root = max; if (max == 0) return -1; /* no codes! */ for (min = 1; min <= MAXBITS; min++) if (count[min] != 0) break; if (root < min) root = min; /* check for an over-subscribed or incomplete set of lengths */ left = 1; for (len = 1; len <= MAXBITS; len++) { left <<= 1; left -= count[len]; if (left < 0) return -1; /* over-subscribed */ } if (left > 0 && (type == CODES || max != 1)) return -1; /* incomplete set */ /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + count[len]; /* sort symbols by length, by symbol order within each length */ for (sym = 0; sym < codes; sym++) if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; /* Create and fill in decoding tables. In this loop, the table being filled is at next and has curr index bits. The code being used is huff with length len. That code is converted to an index by dropping drop bits off of the bottom. For codes where len is less than drop + curr, those top drop + curr - len bits are incremented through all values to fill the table with replicated entries. root is the number of index bits for the root table. When len exceeds root, sub-tables are created pointed to by the root entry with an index of the low root bits of huff. This is saved in low to check for when a new sub-table should be started. drop is zero when the root table is being filled, and drop is root when sub-tables are being filled. When a new sub-table is needed, it is necessary to look ahead in the code lengths to determine what size sub-table is needed. The length counts are used for this, and so count[] is decremented as codes are entered in the tables. used keeps track of how many table entries have been allocated from the provided *table space. It is checked for LENS and DIST tables against the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in the initial root table size constants. See the comments in inftree9.h for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This routine permits incomplete codes, so another loop after this one fills in the rest of the decoding tables with invalid code markers. */ /* set up for code type */ switch (type) { case CODES: base = extra = work; /* dummy value--not used */ end = 19; break; case LENS: base = lbase; base -= 257; extra = lext; extra -= 257; end = 256; break; default: /* DISTS */ base = dbase; extra = dext; end = -1; } /* initialize state for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ next = *table; /* current table to fill in */ curr = root; /* current table index bits */ drop = 0; /* current bits to drop from code for index */ low = (unsigned)(-1); /* trigger new sub-table when len > root */ used = 1U << root; /* use root table entries */ mask = used - 1; /* mask for comparing low */ /* check available table space */ if ((type == LENS && used >= ENOUGH_LENS) || (type == DISTS && used >= ENOUGH_DISTS)) return 1; /* process all codes and make table entries */ for (;;) { /* create table entry */ this.bits = (unsigned char)(len - drop); if ((int)(work[sym]) < end) { this.op = (unsigned char)0; this.val = work[sym]; } else if ((int)(work[sym]) > end) { this.op = (unsigned char)(extra[work[sym]]); this.val = base[work[sym]]; } else { this.op = (unsigned char)(32 + 64); /* end of block */ this.val = 0; } /* replicate for those indices with low len bits equal to huff */ incr = 1U << (len - drop); fill = 1U << curr; do { fill -= incr; next[(huff >> drop) + fill] = this; } while (fill != 0); /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; if (incr != 0) { huff &= incr - 1; huff += incr; } else huff = 0; /* go to next symbol, update count, len */ sym++; if (--(count[len]) == 0) { if (len == max) break; len = lens[work[sym]]; } /* create new sub-table if needed */ if (len > root && (huff & mask) != low) { /* if first time, transition to sub-tables */ if (drop == 0) drop = root; /* increment past last table */ next += 1U << curr; /* determine length of next table */ curr = len - drop; left = (int)(1 << curr); while (curr + drop < max) { left -= count[curr + drop]; if (left <= 0) break; curr++; left <<= 1; } /* check for enough space */ used += 1U << curr; if ((type == LENS && used >= ENOUGH_LENS) || (type == DISTS && used >= ENOUGH_DISTS)) return 1; /* point entry in root table to sub-table */ low = huff & mask; (*table)[low].op = (unsigned char)curr; (*table)[low].bits = (unsigned char)root; (*table)[low].val = (unsigned short)(next - *table); } } /* Fill in rest of table for incomplete codes. This loop is similar to the loop above in incrementing huff for table indices. It is assumed that len is equal to curr + drop, so there is no loop needed to increment through high index bits. When the current sub-table is filled, the loop drops back to the root table to fill in any remaining entries there. */ this.op = (unsigned char)64; /* invalid code marker */ this.bits = (unsigned char)(len - drop); this.val = (unsigned short)0; while (huff != 0) { /* when done with sub-table, drop back to root table */ if (drop != 0 && (huff & mask) != low) { drop = 0; len = root; next = *table; curr = root; this.bits = (unsigned char)len; } /* put invalid code marker in table */ next[huff >> drop] = this; /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; if (incr != 0) { huff &= incr - 1; huff += incr; } else huff = 0; } /* set return parameters */ *table += used; *bits = root; return 0; } |
Added compat/zlib/contrib/infback9/inftree9.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* inftree9.h -- header to use inftree9.c * Copyright (C) 1995-2008 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Structure for decoding tables. Each entry provides either the information needed to do the operation requested by the code that indexed that table entry, or it provides a pointer to another table that indexes more bits of the code. op indicates whether the entry is a pointer to another table, a literal, a length or distance, an end-of-block, or an invalid code. For a table pointer, the low four bits of op is the number of index bits of that table. For a length or distance, the low four bits of op is the number of extra bits to get after the code. bits is the number of bits in this code or part of the code to drop off of the bit buffer. val is the actual byte to output in the case of a literal, the base length or distance, or the offset from the current table to the next table. Each entry is four bytes. */ typedef struct { unsigned char op; /* operation, extra bits, table bits */ unsigned char bits; /* bits in this part of the code */ unsigned short val; /* offset in table or code value */ } code; /* op values as set by inflate_table(): 00000000 - literal 0000tttt - table link, tttt != 0 is the number of table index bits 100eeeee - length or distance, eeee is the number of extra bits 01100000 - end of block 01000000 - invalid code */ /* Maximum size of the dynamic table. The maximum number of code structures is 1446, which is the sum of 852 for literal/length codes and 594 for distance codes. These values were found by exhaustive searches using the program examples/enough.c found in the zlib distribution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes returns 852, and "enough 32 6 15" for distance codes returns 594. The initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in infback9.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ #define ENOUGH_LENS 852 #define ENOUGH_DISTS 594 #define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) /* Type of code to build for inflate_table9() */ typedef enum { CODES, LENS, DISTS } codetype; extern int inflate_table9(codetype type, unsigned short FAR *lens, unsigned codes, code FAR * FAR *table, unsigned FAR *bits, unsigned short FAR *work); |
Added compat/zlib/contrib/iostream/test.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include "zfstream.h" int main() { // Construct a stream object with this filebuffer. Anything sent // to this stream will go to standard out. gzofstream os( 1, ios::out ); // This text is getting compressed and sent to stdout. // To prove this, run 'test | zcat'. os << "Hello, Mommy" << endl; os << setcompressionlevel( Z_NO_COMPRESSION ); os << "hello, hello, hi, ho!" << endl; setcompressionlevel( os, Z_DEFAULT_COMPRESSION ) << "I'm compressing again" << endl; os.close(); return 0; } |
Added compat/zlib/contrib/iostream/zfstream.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include "zfstream.h" gzfilebuf::gzfilebuf() : file(NULL), mode(0), own_file_descriptor(0) { } gzfilebuf::~gzfilebuf() { sync(); if ( own_file_descriptor ) close(); } gzfilebuf *gzfilebuf::open( const char *name, int io_mode ) { if ( is_open() ) return NULL; char char_mode[10]; char *p = char_mode; if ( io_mode & ios::in ) { mode = ios::in; *p++ = 'r'; } else if ( io_mode & ios::app ) { mode = ios::app; *p++ = 'a'; } else { mode = ios::out; *p++ = 'w'; } if ( io_mode & ios::binary ) { mode |= ios::binary; *p++ = 'b'; } // Hard code the compression level if ( io_mode & (ios::out|ios::app )) { *p++ = '9'; } // Put the end-of-string indicator *p = '\0'; if ( (file = gzopen(name, char_mode)) == NULL ) return NULL; own_file_descriptor = 1; return this; } gzfilebuf *gzfilebuf::attach( int file_descriptor, int io_mode ) { if ( is_open() ) return NULL; char char_mode[10]; char *p = char_mode; if ( io_mode & ios::in ) { mode = ios::in; *p++ = 'r'; } else if ( io_mode & ios::app ) { mode = ios::app; *p++ = 'a'; } else { mode = ios::out; *p++ = 'w'; } if ( io_mode & ios::binary ) { mode |= ios::binary; *p++ = 'b'; } // Hard code the compression level if ( io_mode & (ios::out|ios::app )) { *p++ = '9'; } // Put the end-of-string indicator *p = '\0'; if ( (file = gzdopen(file_descriptor, char_mode)) == NULL ) return NULL; own_file_descriptor = 0; return this; } gzfilebuf *gzfilebuf::close() { if ( is_open() ) { sync(); gzclose( file ); file = NULL; } return this; } int gzfilebuf::setcompressionlevel( int comp_level ) { return gzsetparams(file, comp_level, -2); } int gzfilebuf::setcompressionstrategy( int comp_strategy ) { return gzsetparams(file, -2, comp_strategy); } streampos gzfilebuf::seekoff( streamoff off, ios::seek_dir dir, int which ) { return streampos(EOF); } int gzfilebuf::underflow() { // If the file hasn't been opened for reading, error. if ( !is_open() || !(mode & ios::in) ) return EOF; // if a buffer doesn't exists, allocate one. if ( !base() ) { if ( (allocate()) == EOF ) return EOF; setp(0,0); } else { if ( in_avail() ) return (unsigned char) *gptr(); if ( out_waiting() ) { if ( flushbuf() == EOF ) return EOF; } } // Attempt to fill the buffer. int result = fillbuf(); if ( result == EOF ) { // disable get area setg(0,0,0); return EOF; } return (unsigned char) *gptr(); } int gzfilebuf::overflow( int c ) { if ( !is_open() || !(mode & ios::out) ) return EOF; if ( !base() ) { if ( allocate() == EOF ) return EOF; setg(0,0,0); } else { if (in_avail()) { return EOF; } if (out_waiting()) { if (flushbuf() == EOF) return EOF; } } int bl = blen(); setp( base(), base() + bl); if ( c != EOF ) { *pptr() = c; pbump(1); } return 0; } int gzfilebuf::sync() { if ( !is_open() ) return EOF; if ( out_waiting() ) return flushbuf(); return 0; } int gzfilebuf::flushbuf() { int n; char *q; q = pbase(); n = pptr() - q; if ( gzwrite( file, q, n) < n ) return EOF; setp(0,0); return 0; } int gzfilebuf::fillbuf() { int required; char *p; p = base(); required = blen(); int t = gzread( file, p, required ); if ( t <= 0) return EOF; setg( base(), base(), base()+t); return t; } gzfilestream_common::gzfilestream_common() : ios( gzfilestream_common::rdbuf() ) { } gzfilestream_common::~gzfilestream_common() { } void gzfilestream_common::attach( int fd, int io_mode ) { if ( !buffer.attach( fd, io_mode) ) clear( ios::failbit | ios::badbit ); else clear(); } void gzfilestream_common::open( const char *name, int io_mode ) { if ( !buffer.open( name, io_mode ) ) clear( ios::failbit | ios::badbit ); else clear(); } void gzfilestream_common::close() { if ( !buffer.close() ) clear( ios::failbit | ios::badbit ); } gzfilebuf *gzfilestream_common::rdbuf() { return &buffer; } gzifstream::gzifstream() : ios( gzfilestream_common::rdbuf() ) { clear( ios::badbit ); } gzifstream::gzifstream( const char *name, int io_mode ) : ios( gzfilestream_common::rdbuf() ) { gzfilestream_common::open( name, io_mode ); } gzifstream::gzifstream( int fd, int io_mode ) : ios( gzfilestream_common::rdbuf() ) { gzfilestream_common::attach( fd, io_mode ); } gzifstream::~gzifstream() { } gzofstream::gzofstream() : ios( gzfilestream_common::rdbuf() ) { clear( ios::badbit ); } gzofstream::gzofstream( const char *name, int io_mode ) : ios( gzfilestream_common::rdbuf() ) { gzfilestream_common::open( name, io_mode ); } gzofstream::gzofstream( int fd, int io_mode ) : ios( gzfilestream_common::rdbuf() ) { gzfilestream_common::attach( fd, io_mode ); } gzofstream::~gzofstream() { } |
Added compat/zlib/contrib/iostream/zfstream.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #ifndef zfstream_h #define zfstream_h #include <fstream.h> #include "zlib.h" class gzfilebuf : public streambuf { public: gzfilebuf( ); virtual ~gzfilebuf(); gzfilebuf *open( const char *name, int io_mode ); gzfilebuf *attach( int file_descriptor, int io_mode ); gzfilebuf *close(); int setcompressionlevel( int comp_level ); int setcompressionstrategy( int comp_strategy ); inline int is_open() const { return (file !=NULL); } virtual streampos seekoff( streamoff, ios::seek_dir, int ); virtual int sync(); protected: virtual int underflow(); virtual int overflow( int = EOF ); private: gzFile file; short mode; short own_file_descriptor; int flushbuf(); int fillbuf(); }; class gzfilestream_common : virtual public ios { friend class gzifstream; friend class gzofstream; friend gzofstream &setcompressionlevel( gzofstream &, int ); friend gzofstream &setcompressionstrategy( gzofstream &, int ); public: virtual ~gzfilestream_common(); void attach( int fd, int io_mode ); void open( const char *name, int io_mode ); void close(); protected: gzfilestream_common(); private: gzfilebuf *rdbuf(); gzfilebuf buffer; }; class gzifstream : public gzfilestream_common, public istream { public: gzifstream(); gzifstream( const char *name, int io_mode = ios::in ); gzifstream( int fd, int io_mode = ios::in ); virtual ~gzifstream(); }; class gzofstream : public gzfilestream_common, public ostream { public: gzofstream(); gzofstream( const char *name, int io_mode = ios::out ); gzofstream( int fd, int io_mode = ios::out ); virtual ~gzofstream(); }; template<class T> class gzomanip { friend gzofstream &operator<<(gzofstream &, const gzomanip<T> &); public: gzomanip(gzofstream &(*f)(gzofstream &, T), T v) : func(f), val(v) { } private: gzofstream &(*func)(gzofstream &, T); T val; }; template<class T> gzofstream &operator<<(gzofstream &s, const gzomanip<T> &m) { return (*m.func)(s, m.val); } inline gzofstream &setcompressionlevel( gzofstream &s, int l ) { (s.rdbuf())->setcompressionlevel(l); return s; } inline gzofstream &setcompressionstrategy( gzofstream &s, int l ) { (s.rdbuf())->setcompressionstrategy(l); return s; } inline gzomanip<int> setcompressionlevel(int l) { return gzomanip<int>(&setcompressionlevel,l); } inline gzomanip<int> setcompressionstrategy(int l) { return gzomanip<int>(&setcompressionstrategy,l); } #endif |
Added compat/zlib/contrib/iostream2/zstream.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* * * Copyright (c) 1997 * Christian Michelsen Research AS * Advanced Computing * Fantoftvegen 38, 5036 BERGEN, Norway * http://www.cmr.no * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Christian Michelsen Research AS makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * */ #ifndef ZSTREAM__H #define ZSTREAM__H /* * zstream.h - C++ interface to the 'zlib' general purpose compression library * $Id: zstream.h 1.1 1997-06-25 12:00:56+02 tyge Exp tyge $ */ #include <strstream.h> #include <string.h> #include <stdio.h> #include "zlib.h" #if defined(_WIN32) # include <fcntl.h> # include <io.h> # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) #else # define SET_BINARY_MODE(file) #endif class zstringlen { public: zstringlen(class izstream&); zstringlen(class ozstream&, const char*); size_t value() const { return val.word; } private: struct Val { unsigned char byte; size_t word; } val; }; // ----------------------------- izstream ----------------------------- class izstream { public: izstream() : m_fp(0) {} izstream(FILE* fp) : m_fp(0) { open(fp); } izstream(const char* name) : m_fp(0) { open(name); } ~izstream() { close(); } /* Opens a gzip (.gz) file for reading. * open() can be used to read a file which is not in gzip format; * in this case read() will directly read from the file without * decompression. errno can be checked to distinguish two error * cases (if errno is zero, the zlib error is Z_MEM_ERROR). */ void open(const char* name) { if (m_fp) close(); m_fp = ::gzopen(name, "rb"); } void open(FILE* fp) { SET_BINARY_MODE(fp); if (m_fp) close(); m_fp = ::gzdopen(fileno(fp), "rb"); } /* Flushes all pending input if necessary, closes the compressed file * and deallocates all the (de)compression state. The return value is * the zlib error number (see function error() below). */ int close() { int r = ::gzclose(m_fp); m_fp = 0; return r; } /* Binary read the given number of bytes from the compressed file. */ int read(void* buf, size_t len) { return ::gzread(m_fp, buf, len); } /* Returns the error message for the last error which occurred on the * given compressed file. errnum is set to zlib error number. If an * error occurred in the file system and not in the compression library, * errnum is set to Z_ERRNO and the application may consult errno * to get the exact error code. */ const char* error(int* errnum) { return ::gzerror(m_fp, errnum); } gzFile fp() { return m_fp; } private: gzFile m_fp; }; /* * Binary read the given (array of) object(s) from the compressed file. * If the input file was not in gzip format, read() copies the objects number * of bytes into the buffer. * returns the number of uncompressed bytes actually read * (0 for end of file, -1 for error). */ template <class T, class Items> inline int read(izstream& zs, T* x, Items items) { return ::gzread(zs.fp(), x, items*sizeof(T)); } /* * Binary input with the '>' operator. */ template <class T> inline izstream& operator>(izstream& zs, T& x) { ::gzread(zs.fp(), &x, sizeof(T)); return zs; } inline zstringlen::zstringlen(izstream& zs) { zs > val.byte; if (val.byte == 255) zs > val.word; else val.word = val.byte; } /* * Read length of string + the string with the '>' operator. */ inline izstream& operator>(izstream& zs, char* x) { zstringlen len(zs); ::gzread(zs.fp(), x, len.value()); x[len.value()] = '\0'; return zs; } inline char* read_string(izstream& zs) { zstringlen len(zs); char* x = new char[len.value()+1]; ::gzread(zs.fp(), x, len.value()); x[len.value()] = '\0'; return x; } // ----------------------------- ozstream ----------------------------- class ozstream { public: ozstream() : m_fp(0), m_os(0) { } ozstream(FILE* fp, int level = Z_DEFAULT_COMPRESSION) : m_fp(0), m_os(0) { open(fp, level); } ozstream(const char* name, int level = Z_DEFAULT_COMPRESSION) : m_fp(0), m_os(0) { open(name, level); } ~ozstream() { close(); } /* Opens a gzip (.gz) file for writing. * The compression level parameter should be in 0..9 * errno can be checked to distinguish two error cases * (if errno is zero, the zlib error is Z_MEM_ERROR). */ void open(const char* name, int level = Z_DEFAULT_COMPRESSION) { char mode[4] = "wb\0"; if (level != Z_DEFAULT_COMPRESSION) mode[2] = '0'+level; if (m_fp) close(); m_fp = ::gzopen(name, mode); } /* open from a FILE pointer. */ void open(FILE* fp, int level = Z_DEFAULT_COMPRESSION) { SET_BINARY_MODE(fp); char mode[4] = "wb\0"; if (level != Z_DEFAULT_COMPRESSION) mode[2] = '0'+level; if (m_fp) close(); m_fp = ::gzdopen(fileno(fp), mode); } /* Flushes all pending output if necessary, closes the compressed file * and deallocates all the (de)compression state. The return value is * the zlib error number (see function error() below). */ int close() { if (m_os) { ::gzwrite(m_fp, m_os->str(), m_os->pcount()); delete[] m_os->str(); delete m_os; m_os = 0; } int r = ::gzclose(m_fp); m_fp = 0; return r; } /* Binary write the given number of bytes into the compressed file. */ int write(const void* buf, size_t len) { return ::gzwrite(m_fp, (voidp) buf, len); } /* Flushes all pending output into the compressed file. The parameter * _flush is as in the deflate() function. The return value is the zlib * error number (see function gzerror below). flush() returns Z_OK if * the flush_ parameter is Z_FINISH and all output could be flushed. * flush() should be called only when strictly necessary because it can * degrade compression. */ int flush(int _flush) { os_flush(); return ::gzflush(m_fp, _flush); } /* Returns the error message for the last error which occurred on the * given compressed file. errnum is set to zlib error number. If an * error occurred in the file system and not in the compression library, * errnum is set to Z_ERRNO and the application may consult errno * to get the exact error code. */ const char* error(int* errnum) { return ::gzerror(m_fp, errnum); } gzFile fp() { return m_fp; } ostream& os() { if (m_os == 0) m_os = new ostrstream; return *m_os; } void os_flush() { if (m_os && m_os->pcount()>0) { ostrstream* oss = new ostrstream; oss->fill(m_os->fill()); oss->flags(m_os->flags()); oss->precision(m_os->precision()); oss->width(m_os->width()); ::gzwrite(m_fp, m_os->str(), m_os->pcount()); delete[] m_os->str(); delete m_os; m_os = oss; } } private: gzFile m_fp; ostrstream* m_os; }; /* * Binary write the given (array of) object(s) into the compressed file. * returns the number of uncompressed bytes actually written * (0 in case of error). */ template <class T, class Items> inline int write(ozstream& zs, const T* x, Items items) { return ::gzwrite(zs.fp(), (voidp) x, items*sizeof(T)); } /* * Binary output with the '<' operator. */ template <class T> inline ozstream& operator<(ozstream& zs, const T& x) { ::gzwrite(zs.fp(), (voidp) &x, sizeof(T)); return zs; } inline zstringlen::zstringlen(ozstream& zs, const char* x) { val.byte = 255; val.word = ::strlen(x); if (val.word < 255) zs < (val.byte = val.word); else zs < val; } /* * Write length of string + the string with the '<' operator. */ inline ozstream& operator<(ozstream& zs, const char* x) { zstringlen len(zs, x); ::gzwrite(zs.fp(), (voidp) x, len.value()); return zs; } #ifdef _MSC_VER inline ozstream& operator<(ozstream& zs, char* const& x) { return zs < (const char*) x; } #endif /* * Ascii write with the << operator; */ template <class T> inline ostream& operator<<(ozstream& zs, const T& x) { zs.os_flush(); return zs.os() << x; } #endif |
Added compat/zlib/contrib/iostream2/zstream_test.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include "zstream.h" #include <math.h> #include <stdlib.h> #include <iomanip.h> void main() { char h[256] = "Hello"; char* g = "Goodbye"; ozstream out("temp.gz"); out < "This works well" < h < g; out.close(); izstream in("temp.gz"); // read it back char *x = read_string(in), *y = new char[256], z[256]; in > y > z; in.close(); cout << x << endl << y << endl << z << endl; out.open("temp.gz"); // try ascii output; zcat temp.gz to see the results out << setw(50) << setfill('#') << setprecision(20) << x << endl << y << endl << z << endl; out << z << endl << y << endl << x << endl; out << 1.1234567890123456789 << endl; delete[] x; delete[] y; } |
Added compat/zlib/contrib/iostream3/README.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | These classes provide a C++ stream interface to the zlib library. It allows you to do things like: gzofstream outf("blah.gz"); outf << "These go into the gzip file " << 123 << endl; It does this by deriving a specialized stream buffer for gzipped files, which is the way Stroustrup would have done it. :-> The gzifstream and gzofstream classes were originally written by Kevin Ruland and made available in the zlib contrib/iostream directory. The older version still compiles under gcc 2.xx, but not under gcc 3.xx, which sparked the development of this version. The new classes are as standard-compliant as possible, closely following the approach of the standard library's fstream classes. It compiles under gcc versions 3.2 and 3.3, but not under gcc 2.xx. This is mainly due to changes in the standard library naming scheme. The new version of gzifstream/gzofstream/gzfilebuf differs from the previous one in the following respects: - added showmanyc - added setbuf, with support for unbuffered output via setbuf(0,0) - a few bug fixes of stream behavior - gzipped output file opened with default compression level instead of maximum level - setcompressionlevel()/strategy() members replaced by single setcompression() The code is provided "as is", with the permission to use, copy, modify, distribute and sell it for any purpose without fee. Ludwig Schwardt <schwardt@sun.ac.za> DSP Lab Electrical & Electronic Engineering Department University of Stellenbosch South Africa |
Added compat/zlib/contrib/iostream3/TODO.
> > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Possible upgrades to gzfilebuf: - The ability to do putback (e.g. putbackfail) - The ability to seek (zlib supports this, but could be slow/tricky) - Simultaneous read/write access (does it make sense?) - Support for ios_base::ate open mode - Locale support? - Check public interface to see which calls give problems (due to dependence on library internals) - Override operator<<(ostream&, gzfilebuf*) to allow direct copying of stream buffer to stream ( i.e. os << is.rdbuf(); ) |
Added compat/zlib/contrib/iostream3/test.cc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* * Test program for gzifstream and gzofstream * * by Ludwig Schwardt <schwardt@sun.ac.za> * original version by Kevin Ruland <kevin@rodin.wustl.edu> */ #include "zfstream.h" #include <iostream> // for cout int main() { gzofstream outf; gzifstream inf; char buf[80]; outf.open("test1.txt.gz"); outf << "The quick brown fox sidestepped the lazy canine\n" << 1.3 << "\nPlan " << 9 << std::endl; outf.close(); std::cout << "Wrote the following message to 'test1.txt.gz' (check with zcat or zless):\n" << "The quick brown fox sidestepped the lazy canine\n" << 1.3 << "\nPlan " << 9 << std::endl; std::cout << "\nReading 'test1.txt.gz' (buffered) produces:\n"; inf.open("test1.txt.gz"); while (inf.getline(buf,80,'\n')) { std::cout << buf << "\t(" << inf.rdbuf()->in_avail() << " chars left in buffer)\n"; } inf.close(); outf.rdbuf()->pubsetbuf(0,0); outf.open("test2.txt.gz"); outf << setcompression(Z_NO_COMPRESSION) << "The quick brown fox sidestepped the lazy canine\n" << 1.3 << "\nPlan " << 9 << std::endl; outf.close(); std::cout << "\nWrote the same message to 'test2.txt.gz' in uncompressed form"; std::cout << "\nReading 'test2.txt.gz' (unbuffered) produces:\n"; inf.rdbuf()->pubsetbuf(0,0); inf.open("test2.txt.gz"); while (inf.getline(buf,80,'\n')) { std::cout << buf << "\t(" << inf.rdbuf()->in_avail() << " chars left in buffer)\n"; } inf.close(); return 0; } |
Added compat/zlib/contrib/iostream3/zfstream.cc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* * A C++ I/O streams interface to the zlib gz* functions * * by Ludwig Schwardt <schwardt@sun.ac.za> * original version by Kevin Ruland <kevin@rodin.wustl.edu> * * This version is standard-compliant and compatible with gcc 3.x. */ #include "zfstream.h" #include <cstring> // for strcpy, strcat, strlen (mode strings) #include <cstdio> // for BUFSIZ // Internal buffer sizes (default and "unbuffered" versions) #define BIGBUFSIZE BUFSIZ #define SMALLBUFSIZE 1 /*****************************************************************************/ // Default constructor gzfilebuf::gzfilebuf() : file(NULL), io_mode(std::ios_base::openmode(0)), own_fd(false), buffer(NULL), buffer_size(BIGBUFSIZE), own_buffer(true) { // No buffers to start with this->disable_buffer(); } // Destructor gzfilebuf::~gzfilebuf() { // Sync output buffer and close only if responsible for file // (i.e. attached streams should be left open at this stage) this->sync(); if (own_fd) this->close(); // Make sure internal buffer is deallocated this->disable_buffer(); } // Set compression level and strategy int gzfilebuf::setcompression(int comp_level, int comp_strategy) { return gzsetparams(file, comp_level, comp_strategy); } // Open gzipped file gzfilebuf* gzfilebuf::open(const char *name, std::ios_base::openmode mode) { // Fail if file already open if (this->is_open()) return NULL; // Don't support simultaneous read/write access (yet) if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) return NULL; // Build mode string for gzopen and check it [27.8.1.3.2] char char_mode[6] = "\0\0\0\0\0"; if (!this->open_mode(mode, char_mode)) return NULL; // Attempt to open file if ((file = gzopen(name, char_mode)) == NULL) return NULL; // On success, allocate internal buffer and set flags this->enable_buffer(); io_mode = mode; own_fd = true; return this; } // Attach to gzipped file gzfilebuf* gzfilebuf::attach(int fd, std::ios_base::openmode mode) { // Fail if file already open if (this->is_open()) return NULL; // Don't support simultaneous read/write access (yet) if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) return NULL; // Build mode string for gzdopen and check it [27.8.1.3.2] char char_mode[6] = "\0\0\0\0\0"; if (!this->open_mode(mode, char_mode)) return NULL; // Attempt to attach to file if ((file = gzdopen(fd, char_mode)) == NULL) return NULL; // On success, allocate internal buffer and set flags this->enable_buffer(); io_mode = mode; own_fd = false; return this; } // Close gzipped file gzfilebuf* gzfilebuf::close() { // Fail immediately if no file is open if (!this->is_open()) return NULL; // Assume success gzfilebuf* retval = this; // Attempt to sync and close gzipped file if (this->sync() == -1) retval = NULL; if (gzclose(file) < 0) retval = NULL; // File is now gone anyway (postcondition [27.8.1.3.8]) file = NULL; own_fd = false; // Destroy internal buffer if it exists this->disable_buffer(); return retval; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Convert int open mode to mode string bool gzfilebuf::open_mode(std::ios_base::openmode mode, char* c_mode) const { bool testb = mode & std::ios_base::binary; bool testi = mode & std::ios_base::in; bool testo = mode & std::ios_base::out; bool testt = mode & std::ios_base::trunc; bool testa = mode & std::ios_base::app; // Check for valid flag combinations - see [27.8.1.3.2] (Table 92) // Original zfstream hardcoded the compression level to maximum here... // Double the time for less than 1% size improvement seems // excessive though - keeping it at the default level // To change back, just append "9" to the next three mode strings if (!testi && testo && !testt && !testa) strcpy(c_mode, "w"); if (!testi && testo && !testt && testa) strcpy(c_mode, "a"); if (!testi && testo && testt && !testa) strcpy(c_mode, "w"); if (testi && !testo && !testt && !testa) strcpy(c_mode, "r"); // No read/write mode yet // if (testi && testo && !testt && !testa) // strcpy(c_mode, "r+"); // if (testi && testo && testt && !testa) // strcpy(c_mode, "w+"); // Mode string should be empty for invalid combination of flags if (strlen(c_mode) == 0) return false; if (testb) strcat(c_mode, "b"); return true; } // Determine number of characters in internal get buffer std::streamsize gzfilebuf::showmanyc() { // Calls to underflow will fail if file not opened for reading if (!this->is_open() || !(io_mode & std::ios_base::in)) return -1; // Make sure get area is in use if (this->gptr() && (this->gptr() < this->egptr())) return std::streamsize(this->egptr() - this->gptr()); else return 0; } // Fill get area from gzipped file gzfilebuf::int_type gzfilebuf::underflow() { // If something is left in the get area by chance, return it // (this shouldn't normally happen, as underflow is only supposed // to be called when gptr >= egptr, but it serves as error check) if (this->gptr() && (this->gptr() < this->egptr())) return traits_type::to_int_type(*(this->gptr())); // If the file hasn't been opened for reading, produce error if (!this->is_open() || !(io_mode & std::ios_base::in)) return traits_type::eof(); // Attempt to fill internal buffer from gzipped file // (buffer must be guaranteed to exist...) int bytes_read = gzread(file, buffer, buffer_size); // Indicates error or EOF if (bytes_read <= 0) { // Reset get area this->setg(buffer, buffer, buffer); return traits_type::eof(); } // Make all bytes read from file available as get area this->setg(buffer, buffer, buffer + bytes_read); // Return next character in get area return traits_type::to_int_type(*(this->gptr())); } // Write put area to gzipped file gzfilebuf::int_type gzfilebuf::overflow(int_type c) { // Determine whether put area is in use if (this->pbase()) { // Double-check pointer range if (this->pptr() > this->epptr() || this->pptr() < this->pbase()) return traits_type::eof(); // Add extra character to buffer if not EOF if (!traits_type::eq_int_type(c, traits_type::eof())) { *(this->pptr()) = traits_type::to_char_type(c); this->pbump(1); } // Number of characters to write to file int bytes_to_write = this->pptr() - this->pbase(); // Overflow doesn't fail if nothing is to be written if (bytes_to_write > 0) { // If the file hasn't been opened for writing, produce error if (!this->is_open() || !(io_mode & std::ios_base::out)) return traits_type::eof(); // If gzipped file won't accept all bytes written to it, fail if (gzwrite(file, this->pbase(), bytes_to_write) != bytes_to_write) return traits_type::eof(); // Reset next pointer to point to pbase on success this->pbump(-bytes_to_write); } } // Write extra character to file if not EOF else if (!traits_type::eq_int_type(c, traits_type::eof())) { // If the file hasn't been opened for writing, produce error if (!this->is_open() || !(io_mode & std::ios_base::out)) return traits_type::eof(); // Impromptu char buffer (allows "unbuffered" output) char_type last_char = traits_type::to_char_type(c); // If gzipped file won't accept this character, fail if (gzwrite(file, &last_char, 1) != 1) return traits_type::eof(); } // If you got here, you have succeeded (even if c was EOF) // The return value should therefore be non-EOF if (traits_type::eq_int_type(c, traits_type::eof())) return traits_type::not_eof(c); else return c; } // Assign new buffer std::streambuf* gzfilebuf::setbuf(char_type* p, std::streamsize n) { // First make sure stuff is sync'ed, for safety if (this->sync() == -1) return NULL; // If buffering is turned off on purpose via setbuf(0,0), still allocate one... // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at // least a buffer of size 1 (very inefficient though, therefore make it bigger?) // This follows from [27.5.2.4.3]/12 (gptr needs to point at something, it seems) if (!p || !n) { // Replace existing buffer (if any) with small internal buffer this->disable_buffer(); buffer = NULL; buffer_size = 0; own_buffer = true; this->enable_buffer(); } else { // Replace existing buffer (if any) with external buffer this->disable_buffer(); buffer = p; buffer_size = n; own_buffer = false; this->enable_buffer(); } return this; } // Write put area to gzipped file (i.e. ensures that put area is empty) int gzfilebuf::sync() { return traits_type::eq_int_type(this->overflow(), traits_type::eof()) ? -1 : 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Allocate internal buffer void gzfilebuf::enable_buffer() { // If internal buffer required, allocate one if (own_buffer && !buffer) { // Check for buffered vs. "unbuffered" if (buffer_size > 0) { // Allocate internal buffer buffer = new char_type[buffer_size]; // Get area starts empty and will be expanded by underflow as need arises this->setg(buffer, buffer, buffer); // Setup entire internal buffer as put area. // The one-past-end pointer actually points to the last element of the buffer, // so that overflow(c) can safely add the extra character c to the sequence. // These pointers remain in place for the duration of the buffer this->setp(buffer, buffer + buffer_size - 1); } else { // Even in "unbuffered" case, (small?) get buffer is still required buffer_size = SMALLBUFSIZE; buffer = new char_type[buffer_size]; this->setg(buffer, buffer, buffer); // "Unbuffered" means no put buffer this->setp(0, 0); } } else { // If buffer already allocated, reset buffer pointers just to make sure no // stale chars are lying around this->setg(buffer, buffer, buffer); this->setp(buffer, buffer + buffer_size - 1); } } // Destroy internal buffer void gzfilebuf::disable_buffer() { // If internal buffer exists, deallocate it if (own_buffer && buffer) { // Preserve unbuffered status by zeroing size if (!this->pbase()) buffer_size = 0; delete[] buffer; buffer = NULL; this->setg(0, 0, 0); this->setp(0, 0); } else { // Reset buffer pointers to initial state if external buffer exists this->setg(buffer, buffer, buffer); if (buffer) this->setp(buffer, buffer + buffer_size - 1); else this->setp(0, 0); } } /*****************************************************************************/ // Default constructor initializes stream buffer gzifstream::gzifstream() : std::istream(NULL), sb() { this->init(&sb); } // Initialize stream buffer and open file gzifstream::gzifstream(const char* name, std::ios_base::openmode mode) : std::istream(NULL), sb() { this->init(&sb); this->open(name, mode); } // Initialize stream buffer and attach to file gzifstream::gzifstream(int fd, std::ios_base::openmode mode) : std::istream(NULL), sb() { this->init(&sb); this->attach(fd, mode); } // Open file and go into fail() state if unsuccessful void gzifstream::open(const char* name, std::ios_base::openmode mode) { if (!sb.open(name, mode | std::ios_base::in)) this->setstate(std::ios_base::failbit); else this->clear(); } // Attach to file and go into fail() state if unsuccessful void gzifstream::attach(int fd, std::ios_base::openmode mode) { if (!sb.attach(fd, mode | std::ios_base::in)) this->setstate(std::ios_base::failbit); else this->clear(); } // Close file void gzifstream::close() { if (!sb.close()) this->setstate(std::ios_base::failbit); } /*****************************************************************************/ // Default constructor initializes stream buffer gzofstream::gzofstream() : std::ostream(NULL), sb() { this->init(&sb); } // Initialize stream buffer and open file gzofstream::gzofstream(const char* name, std::ios_base::openmode mode) : std::ostream(NULL), sb() { this->init(&sb); this->open(name, mode); } // Initialize stream buffer and attach to file gzofstream::gzofstream(int fd, std::ios_base::openmode mode) : std::ostream(NULL), sb() { this->init(&sb); this->attach(fd, mode); } // Open file and go into fail() state if unsuccessful void gzofstream::open(const char* name, std::ios_base::openmode mode) { if (!sb.open(name, mode | std::ios_base::out)) this->setstate(std::ios_base::failbit); else this->clear(); } // Attach to file and go into fail() state if unsuccessful void gzofstream::attach(int fd, std::ios_base::openmode mode) { if (!sb.attach(fd, mode | std::ios_base::out)) this->setstate(std::ios_base::failbit); else this->clear(); } // Close file void gzofstream::close() { if (!sb.close()) this->setstate(std::ios_base::failbit); } |
Added compat/zlib/contrib/iostream3/zfstream.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* * A C++ I/O streams interface to the zlib gz* functions * * by Ludwig Schwardt <schwardt@sun.ac.za> * original version by Kevin Ruland <kevin@rodin.wustl.edu> * * This version is standard-compliant and compatible with gcc 3.x. */ #ifndef ZFSTREAM_H #define ZFSTREAM_H #include <istream> // not iostream, since we don't need cin/cout #include <ostream> #include "zlib.h" /*****************************************************************************/ /** * @brief Gzipped file stream buffer class. * * This class implements basic_filebuf for gzipped files. It doesn't yet support * seeking (allowed by zlib but slow/limited), putback and read/write access * (tricky). Otherwise, it attempts to be a drop-in replacement for the standard * file streambuf. */ class gzfilebuf : public std::streambuf { public: // Default constructor. gzfilebuf(); // Destructor. virtual ~gzfilebuf(); /** * @brief Set compression level and strategy on the fly. * @param comp_level Compression level (see zlib.h for allowed values) * @param comp_strategy Compression strategy (see zlib.h for allowed values) * @return Z_OK on success, Z_STREAM_ERROR otherwise. * * Unfortunately, these parameters cannot be modified separately, as the * previous zfstream version assumed. Since the strategy is seldom changed, * it can default and setcompression(level) then becomes like the old * setcompressionlevel(level). */ int setcompression(int comp_level, int comp_strategy = Z_DEFAULT_STRATEGY); /** * @brief Check if file is open. * @return True if file is open. */ bool is_open() const { return (file != NULL); } /** * @brief Open gzipped file. * @param name File name. * @param mode Open mode flags. * @return @c this on success, NULL on failure. */ gzfilebuf* open(const char* name, std::ios_base::openmode mode); /** * @brief Attach to already open gzipped file. * @param fd File descriptor. * @param mode Open mode flags. * @return @c this on success, NULL on failure. */ gzfilebuf* attach(int fd, std::ios_base::openmode mode); /** * @brief Close gzipped file. * @return @c this on success, NULL on failure. */ gzfilebuf* close(); protected: /** * @brief Convert ios open mode int to mode string used by zlib. * @return True if valid mode flag combination. */ bool open_mode(std::ios_base::openmode mode, char* c_mode) const; /** * @brief Number of characters available in stream buffer. * @return Number of characters. * * This indicates number of characters in get area of stream buffer. * These characters can be read without accessing the gzipped file. */ virtual std::streamsize showmanyc(); /** * @brief Fill get area from gzipped file. * @return First character in get area on success, EOF on error. * * This actually reads characters from gzipped file to stream * buffer. Always buffered. */ virtual int_type underflow(); /** * @brief Write put area to gzipped file. * @param c Extra character to add to buffer contents. * @return Non-EOF on success, EOF on error. * * This actually writes characters in stream buffer to * gzipped file. With unbuffered output this is done one * character at a time. */ virtual int_type overflow(int_type c = traits_type::eof()); /** * @brief Installs external stream buffer. * @param p Pointer to char buffer. * @param n Size of external buffer. * @return @c this on success, NULL on failure. * * Call setbuf(0,0) to enable unbuffered output. */ virtual std::streambuf* setbuf(char_type* p, std::streamsize n); /** * @brief Flush stream buffer to file. * @return 0 on success, -1 on error. * * This calls underflow(EOF) to do the job. */ virtual int sync(); // // Some future enhancements // // virtual int_type uflow(); // virtual int_type pbackfail(int_type c = traits_type::eof()); // virtual pos_type // seekoff(off_type off, // std::ios_base::seekdir way, // std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out); // virtual pos_type // seekpos(pos_type sp, // std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out); private: /** * @brief Allocate internal buffer. * * This function is safe to call multiple times. It will ensure * that a proper internal buffer exists if it is required. If the * buffer already exists or is external, the buffer pointers will be * reset to their original state. */ void enable_buffer(); /** * @brief Destroy internal buffer. * * This function is safe to call multiple times. It will ensure * that the internal buffer is deallocated if it exists. In any * case, it will also reset the buffer pointers. */ void disable_buffer(); /** * Underlying file pointer. */ gzFile file; /** * Mode in which file was opened. */ std::ios_base::openmode io_mode; /** * @brief True if this object owns file descriptor. * * This makes the class responsible for closing the file * upon destruction. */ bool own_fd; /** * @brief Stream buffer. * * For simplicity this remains allocated on the free store for the * entire life span of the gzfilebuf object, unless replaced by setbuf. */ char_type* buffer; /** * @brief Stream buffer size. * * Defaults to system default buffer size (typically 8192 bytes). * Modified by setbuf. */ std::streamsize buffer_size; /** * @brief True if this object owns stream buffer. * * This makes the class responsible for deleting the buffer * upon destruction. */ bool own_buffer; }; /*****************************************************************************/ /** * @brief Gzipped file input stream class. * * This class implements ifstream for gzipped files. Seeking and putback * is not supported yet. */ class gzifstream : public std::istream { public: // Default constructor gzifstream(); /** * @brief Construct stream on gzipped file to be opened. * @param name File name. * @param mode Open mode flags (forced to contain ios::in). */ explicit gzifstream(const char* name, std::ios_base::openmode mode = std::ios_base::in); /** * @brief Construct stream on already open gzipped file. * @param fd File descriptor. * @param mode Open mode flags (forced to contain ios::in). */ explicit gzifstream(int fd, std::ios_base::openmode mode = std::ios_base::in); /** * Obtain underlying stream buffer. */ gzfilebuf* rdbuf() const { return const_cast<gzfilebuf*>(&sb); } /** * @brief Check if file is open. * @return True if file is open. */ bool is_open() { return sb.is_open(); } /** * @brief Open gzipped file. * @param name File name. * @param mode Open mode flags (forced to contain ios::in). * * Stream will be in state good() if file opens successfully; * otherwise in state fail(). This differs from the behavior of * ifstream, which never sets the state to good() and therefore * won't allow you to reuse the stream for a second file unless * you manually clear() the state. The choice is a matter of * convenience. */ void open(const char* name, std::ios_base::openmode mode = std::ios_base::in); /** * @brief Attach to already open gzipped file. * @param fd File descriptor. * @param mode Open mode flags (forced to contain ios::in). * * Stream will be in state good() if attach succeeded; otherwise * in state fail(). */ void attach(int fd, std::ios_base::openmode mode = std::ios_base::in); /** * @brief Close gzipped file. * * Stream will be in state fail() if close failed. */ void close(); private: /** * Underlying stream buffer. */ gzfilebuf sb; }; /*****************************************************************************/ /** * @brief Gzipped file output stream class. * * This class implements ofstream for gzipped files. Seeking and putback * is not supported yet. */ class gzofstream : public std::ostream { public: // Default constructor gzofstream(); /** * @brief Construct stream on gzipped file to be opened. * @param name File name. * @param mode Open mode flags (forced to contain ios::out). */ explicit gzofstream(const char* name, std::ios_base::openmode mode = std::ios_base::out); /** * @brief Construct stream on already open gzipped file. * @param fd File descriptor. * @param mode Open mode flags (forced to contain ios::out). */ explicit gzofstream(int fd, std::ios_base::openmode mode = std::ios_base::out); /** * Obtain underlying stream buffer. */ gzfilebuf* rdbuf() const { return const_cast<gzfilebuf*>(&sb); } /** * @brief Check if file is open. * @return True if file is open. */ bool is_open() { return sb.is_open(); } /** * @brief Open gzipped file. * @param name File name. * @param mode Open mode flags (forced to contain ios::out). * * Stream will be in state good() if file opens successfully; * otherwise in state fail(). This differs from the behavior of * ofstream, which never sets the state to good() and therefore * won't allow you to reuse the stream for a second file unless * you manually clear() the state. The choice is a matter of * convenience. */ void open(const char* name, std::ios_base::openmode mode = std::ios_base::out); /** * @brief Attach to already open gzipped file. * @param fd File descriptor. * @param mode Open mode flags (forced to contain ios::out). * * Stream will be in state good() if attach succeeded; otherwise * in state fail(). */ void attach(int fd, std::ios_base::openmode mode = std::ios_base::out); /** * @brief Close gzipped file. * * Stream will be in state fail() if close failed. */ void close(); private: /** * Underlying stream buffer. */ gzfilebuf sb; }; /*****************************************************************************/ /** * @brief Gzipped file output stream manipulator class. * * This class defines a two-argument manipulator for gzofstream. It is used * as base for the setcompression(int,int) manipulator. */ template<typename T1, typename T2> class gzomanip2 { public: // Allows inserter to peek at internals template <typename Ta, typename Tb> friend gzofstream& operator<<(gzofstream&, const gzomanip2<Ta,Tb>&); // Constructor gzomanip2(gzofstream& (*f)(gzofstream&, T1, T2), T1 v1, T2 v2); private: // Underlying manipulator function gzofstream& (*func)(gzofstream&, T1, T2); // Arguments for manipulator function T1 val1; T2 val2; }; /*****************************************************************************/ // Manipulator function thunks through to stream buffer inline gzofstream& setcompression(gzofstream &gzs, int l, int s = Z_DEFAULT_STRATEGY) { (gzs.rdbuf())->setcompression(l, s); return gzs; } // Manipulator constructor stores arguments template<typename T1, typename T2> inline gzomanip2<T1,T2>::gzomanip2(gzofstream &(*f)(gzofstream &, T1, T2), T1 v1, T2 v2) : func(f), val1(v1), val2(v2) { } // Inserter applies underlying manipulator function to stream template<typename T1, typename T2> inline gzofstream& operator<<(gzofstream& s, const gzomanip2<T1,T2>& m) { return (*m.func)(s, m.val1, m.val2); } // Insert this onto stream to simplify setting of compression level inline gzomanip2<int,int> setcompression(int l, int s = Z_DEFAULT_STRATEGY) { return gzomanip2<int,int>(&setcompression, l, s); } #endif // ZFSTREAM_H |
Added compat/zlib/contrib/minizip/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 | CC?=cc CFLAGS := $(CFLAGS) -O -I../.. UNZ_OBJS = miniunz.o unzip.o ioapi.o ../../libz.a ZIP_OBJS = minizip.o zip.o ioapi.o ../../libz.a .c.o: $(CC) -c $(CFLAGS) $*.c all: miniunz minizip miniunz: $(UNZ_OBJS) $(CC) $(CFLAGS) -o $@ $(UNZ_OBJS) minizip: $(ZIP_OBJS) $(CC) $(CFLAGS) -o $@ $(ZIP_OBJS) test: miniunz minizip @rm -f test.* @echo hello hello hello > test.txt ./minizip test test.txt ./miniunz -l test.zip @mv test.txt test.old ./miniunz test.zip @cmp test.txt test.old @rm -f test.* clean: /bin/rm -f *.o *~ minizip miniunz test.* |
Added compat/zlib/contrib/minizip/Makefile.am.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | lib_LTLIBRARIES = libminizip.la if COND_DEMOS bin_PROGRAMS = miniunzip minizip endif zlib_top_srcdir = $(top_srcdir)/../.. zlib_top_builddir = $(top_builddir)/../.. AM_CPPFLAGS = -I$(zlib_top_srcdir) AM_LDFLAGS = -L$(zlib_top_builddir) if WIN32 iowin32_src = iowin32.c iowin32_h = iowin32.h endif libminizip_la_SOURCES = \ ioapi.c \ mztools.c \ unzip.c \ zip.c \ ${iowin32_src} libminizip_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:0:0 -lz minizip_includedir = $(includedir)/minizip minizip_include_HEADERS = \ crypt.h \ ioapi.h \ mztools.h \ unzip.h \ zip.h \ ${iowin32_h} pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = minizip.pc EXTRA_PROGRAMS = miniunzip minizip miniunzip_SOURCES = miniunz.c miniunzip_LDADD = libminizip.la minizip_SOURCES = minizip.c minizip_LDADD = libminizip.la -lz |
Added compat/zlib/contrib/minizip/MiniZip64_Changes.txt.
> > > > > > | 1 2 3 4 5 6 | MiniZip 1.1 was derived from MiniZip at version 1.01f Change in 1.0 (Okt 2009) - **TODO - Add history** |
Added compat/zlib/contrib/minizip/MiniZip64_info.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 | MiniZip - Copyright (c) 1998-2010 - by Gilles Vollant - version 1.1 64 bits from Mathias Svensson Introduction --------------------- MiniZip 1.1 is built from MiniZip 1.0 by Gilles Vollant ( http://www.winimage.com/zLibDll/minizip.html ) When adding ZIP64 support into minizip it would result into risk of breaking compatibility with minizip 1.0. All possible work was done for compatibility. Background --------------------- When adding ZIP64 support Mathias Svensson found that Even Rouault have added ZIP64 support for unzip.c into minizip for a open source project called gdal ( http://www.gdal.org/ ) That was used as a starting point. And after that ZIP64 support was added to zip.c some refactoring and code cleanup was also done. Changed from MiniZip 1.0 to MiniZip 1.1 --------------------------------------- * Added ZIP64 support for unzip ( by Even Rouault ) * Added ZIP64 support for zip ( by Mathias Svensson ) * Reverted some changed that Even Rouault did. * Bunch of patches received from Gulles Vollant that he received for MiniZip from various users. * Added unzip patch for BZIP Compression method (patch create by Daniel Borca) * Added BZIP Compress method for zip * Did some refactoring and code cleanup Credits Gilles Vollant - Original MiniZip author Even Rouault - ZIP64 unzip Support Daniel Borca - BZip Compression method support in unzip Mathias Svensson - ZIP64 zip support Mathias Svensson - BZip Compression method support in zip Resources ZipLayout http://result42.com/projects/ZipFileLayout Command line tool for Windows that shows the layout and information of the headers in a zip archive. Used when debugging and validating the creation of zip files using MiniZip64 ZIP App Note http://www.pkware.com/documents/casestudies/APPNOTE.TXT Zip File specification Notes. * To be able to use BZip compression method in zip64.c or unzip64.c the BZIP2 lib is needed and HAVE_BZIP2 need to be defined. License ---------------------------------------------------------- Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ---------------------------------------------------------- |
Added compat/zlib/contrib/minizip/configure.ac.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_INIT([minizip], [1.3.1], [bugzilla.redhat.com]) AC_CONFIG_SRCDIR([minizip.c]) AM_INIT_AUTOMAKE([foreign]) LT_INIT AC_MSG_CHECKING([whether to build example programs]) AC_ARG_ENABLE([demos], AC_HELP_STRING([--enable-demos], [build example programs])) AM_CONDITIONAL([COND_DEMOS], [test "$enable_demos" = yes]) if test "$enable_demos" = yes then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi case "${host}" in *-mingw* | mingw*) WIN32="yes" ;; *) ;; esac AM_CONDITIONAL([WIN32], [test "${WIN32}" = "yes"]) AC_SUBST([HAVE_UNISTD_H], [0]) AC_CHECK_HEADER([unistd.h], [HAVE_UNISTD_H=1], []) AC_CONFIG_FILES([Makefile minizip.pc]) AC_OUTPUT |
Added compat/zlib/contrib/minizip/crypt.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* crypt.h -- base code for crypt/uncrypt ZIPfile Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant This code is a modified version of crypting code in Infozip distribution The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). If you don't need crypting in your application, just define symbols NOCRYPT and NOUNCRYPT. This code support the "Traditional PKWARE Encryption". The new AES encryption added on Zip format by Winzip (see the page http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong Encryption is not supported. */ #define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) /*********************************************************************** * Return the next byte in the pseudo-random sequence */ static int decrypt_byte(unsigned long* pkeys, const z_crc_t* pcrc_32_tab) { unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an * unpredictable manner on 16-bit systems; not a problem * with any known compiler so far, though */ (void)pcrc_32_tab; temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); } /*********************************************************************** * Update the encryption keys with the next byte of plain text */ static int update_keys(unsigned long* pkeys, const z_crc_t* pcrc_32_tab, int c) { (*(pkeys+0)) = CRC32((*(pkeys+0)), c); (*(pkeys+1)) += (*(pkeys+0)) & 0xff; (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; { register int keyshift = (int)((*(pkeys+1)) >> 24); (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); } return c; } /*********************************************************************** * Initialize the encryption keys and the random header according to * the given password. */ static void init_keys(const char* passwd, unsigned long* pkeys, const z_crc_t* pcrc_32_tab) { *(pkeys+0) = 305419896L; *(pkeys+1) = 591751049L; *(pkeys+2) = 878082192L; while (*passwd != '\0') { update_keys(pkeys,pcrc_32_tab,(int)*passwd); passwd++; } } #define zdecode(pkeys,pcrc_32_tab,c) \ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) #define zencode(pkeys,pcrc_32_tab,c,t) \ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), (Byte)t^(c)) #ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED #define RAND_HEAD_LEN 12 /* "last resort" source for second part of crypt seed pattern */ # ifndef ZCR_SEED2 # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ # endif static unsigned crypthead(const char* passwd, /* password string */ unsigned char* buf, /* where to write header */ int bufSize, unsigned long* pkeys, const z_crc_t* pcrc_32_tab, unsigned long crcForCrypting) { unsigned n; /* index in random header */ int t; /* temporary */ int c; /* random byte */ unsigned char header[RAND_HEAD_LEN-2]; /* random header */ static unsigned calls = 0; /* ensure different random header each time */ if (bufSize<RAND_HEAD_LEN) return 0; /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the * output of rand() to get less predictability, since rand() is * often poorly implemented. */ if (++calls == 1) { srand((unsigned)(time(NULL) ^ ZCR_SEED2)); } init_keys(passwd, pkeys, pcrc_32_tab); for (n = 0; n < RAND_HEAD_LEN-2; n++) { c = (rand() >> 7) & 0xff; header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); } /* Encrypt random header (last two bytes is high word of crc) */ init_keys(passwd, pkeys, pcrc_32_tab); for (n = 0; n < RAND_HEAD_LEN-2; n++) { buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); } buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); return n; } #endif |
Added compat/zlib/contrib/minizip/ioapi.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 | /* ioapi.h -- IO base function header for compress/uncompress .zip part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt */ #if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS))) #define _CRT_SECURE_NO_WARNINGS #endif #if defined(__APPLE__) || defined(IOAPI_NO_64) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions #define FOPEN_FUNC(filename, mode) fopen(filename, mode) #define FTELLO_FUNC(stream) ftello(stream) #define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) #else #define FOPEN_FUNC(filename, mode) fopen64(filename, mode) #define FTELLO_FUNC(stream) ftello64(stream) #define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) #endif #include "ioapi.h" voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc, const void*filename, int mode) { if (pfilefunc->zfile_func64.zopen64_file != NULL) return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); else { return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); } } long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); else { uLong offsetTruncated = (uLong)offset; if (offsetTruncated != offset) return -1; else return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); } } ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc, voidpf filestream) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); else { uLong tell_uLong = (uLong)(*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); if ((tell_uLong) == MAXU32) return (ZPOS64_T)-1; else return tell_uLong; } } void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32, const zlib_filefunc_def* p_filefunc32) { p_filefunc64_32->zfile_func64.zopen64_file = NULL; p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; p_filefunc64_32->zfile_func64.ztell64_file = NULL; p_filefunc64_32->zfile_func64.zseek64_file = NULL; p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; } static voidpf ZCALLBACK fopen_file_func(voidpf opaque, const char* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; (void)opaque; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) mode_fopen = "r+b"; else if (mode & ZLIB_FILEFUNC_MODE_CREATE) mode_fopen = "wb"; if ((filename!=NULL) && (mode_fopen != NULL)) file = fopen(filename, mode_fopen); return file; } static voidpf ZCALLBACK fopen64_file_func(voidpf opaque, const void* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; (void)opaque; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) mode_fopen = "r+b"; else if (mode & ZLIB_FILEFUNC_MODE_CREATE) mode_fopen = "wb"; if ((filename!=NULL) && (mode_fopen != NULL)) file = FOPEN_FUNC((const char*)filename, mode_fopen); return file; } static uLong ZCALLBACK fread_file_func(voidpf opaque, voidpf stream, void* buf, uLong size) { uLong ret; (void)opaque; ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); return ret; } static uLong ZCALLBACK fwrite_file_func(voidpf opaque, voidpf stream, const void* buf, uLong size) { uLong ret; (void)opaque; ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); return ret; } static long ZCALLBACK ftell_file_func(voidpf opaque, voidpf stream) { long ret; (void)opaque; ret = ftell((FILE *)stream); return ret; } static ZPOS64_T ZCALLBACK ftell64_file_func(voidpf opaque, voidpf stream) { ZPOS64_T ret; (void)opaque; ret = (ZPOS64_T)FTELLO_FUNC((FILE *)stream); return ret; } static long ZCALLBACK fseek_file_func(voidpf opaque, voidpf stream, uLong offset, int origin) { int fseek_origin=0; long ret; (void)opaque; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : fseek_origin = SEEK_CUR; break; case ZLIB_FILEFUNC_SEEK_END : fseek_origin = SEEK_END; break; case ZLIB_FILEFUNC_SEEK_SET : fseek_origin = SEEK_SET; break; default: return -1; } ret = 0; if (fseek((FILE *)stream, (long)offset, fseek_origin) != 0) ret = -1; return ret; } static long ZCALLBACK fseek64_file_func(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) { int fseek_origin=0; long ret; (void)opaque; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : fseek_origin = SEEK_CUR; break; case ZLIB_FILEFUNC_SEEK_END : fseek_origin = SEEK_END; break; case ZLIB_FILEFUNC_SEEK_SET : fseek_origin = SEEK_SET; break; default: return -1; } ret = 0; if(FSEEKO_FUNC((FILE *)stream, (z_off64_t)offset, fseek_origin) != 0) ret = -1; return ret; } static int ZCALLBACK fclose_file_func(voidpf opaque, voidpf stream) { int ret; (void)opaque; ret = fclose((FILE *)stream); return ret; } static int ZCALLBACK ferror_file_func(voidpf opaque, voidpf stream) { int ret; (void)opaque; ret = ferror((FILE *)stream); return ret; } void fill_fopen_filefunc(zlib_filefunc_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen_file = fopen_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; pzlib_filefunc_def->ztell_file = ftell_file_func; pzlib_filefunc_def->zseek_file = fseek_file_func; pzlib_filefunc_def->zclose_file = fclose_file_func; pzlib_filefunc_def->zerror_file = ferror_file_func; pzlib_filefunc_def->opaque = NULL; } void fill_fopen64_filefunc(zlib_filefunc64_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen64_file = fopen64_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; pzlib_filefunc_def->ztell64_file = ftell64_file_func; pzlib_filefunc_def->zseek64_file = fseek64_file_func; pzlib_filefunc_def->zclose_file = fclose_file_func; pzlib_filefunc_def->zerror_file = ferror_file_func; pzlib_filefunc_def->opaque = NULL; } |
Added compat/zlib/contrib/minizip/ioapi.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ioapi.h -- IO base function header for compress/uncompress .zip part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt Changes Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. More if/def section may be needed to support other platforms Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. (but you should use iowin32.c for windows instead) */ #ifndef _ZLIBIOAPI64_H #define _ZLIBIOAPI64_H #if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) // Linux needs this to support file operation on files larger then 4+GB // But might need better if/def to select just the platforms that needs them. #ifndef __USE_FILE_OFFSET64 #define __USE_FILE_OFFSET64 #endif #ifndef __USE_LARGEFILE64 #define __USE_LARGEFILE64 #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _FILE_OFFSET_BIT #define _FILE_OFFSET_BIT 64 #endif #endif #include <stdio.h> #include <stdlib.h> #include "zlib.h" #if defined(USE_FILE32API) #define fopen64 fopen #define ftello64 ftell #define fseeko64 fseek #else #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) #define fopen64 fopen #define ftello64 ftello #define fseeko64 fseeko #endif #ifdef _MSC_VER #define fopen64 fopen #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) #define ftello64 _ftelli64 #define fseeko64 _fseeki64 #else // old MSC #define ftello64 ftell #define fseeko64 fseek #endif #endif #endif /* #ifndef ZPOS64_T #ifdef _WIN32 #define ZPOS64_T fpos_t #else #include <stdint.h> #define ZPOS64_T uint64_t #endif #endif */ #ifdef HAVE_MINIZIP64_CONF_H #include "mz64conf.h" #endif /* a type chosen by DEFINE */ #ifdef HAVE_64BIT_INT_CUSTOM typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; #else #ifdef HAS_STDINT_H #include "stdint.h" typedef uint64_t ZPOS64_T; #else #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 ZPOS64_T; #else typedef unsigned long long int ZPOS64_T; #endif #endif #endif /* Maximum unsigned 32-bit value used as placeholder for zip64 */ #ifndef MAXU32 #define MAXU32 (0xffffffff) #endif #ifdef __cplusplus extern "C" { #endif #define ZLIB_FILEFUNC_SEEK_CUR (1) #define ZLIB_FILEFUNC_SEEK_END (2) #define ZLIB_FILEFUNC_SEEK_SET (0) #define ZLIB_FILEFUNC_MODE_READ (1) #define ZLIB_FILEFUNC_MODE_WRITE (2) #define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) #define ZLIB_FILEFUNC_MODE_EXISTING (4) #define ZLIB_FILEFUNC_MODE_CREATE (8) #ifndef ZCALLBACK #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) #define ZCALLBACK CALLBACK #else #define ZCALLBACK #endif #endif typedef voidpf (ZCALLBACK *open_file_func) (voidpf opaque, const char* filename, int mode); typedef uLong (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uLong size); typedef uLong (ZCALLBACK *write_file_func) (voidpf opaque, voidpf stream, const void* buf, uLong size); typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream); typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin); /* here is the "old" 32 bits structure */ typedef struct zlib_filefunc_def_s { open_file_func zopen_file; read_file_func zread_file; write_file_func zwrite_file; tell_file_func ztell_file; seek_file_func zseek_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc_def; typedef ZPOS64_T (ZCALLBACK *tell64_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *seek64_file_func) (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin); typedef voidpf (ZCALLBACK *open64_file_func) (voidpf opaque, const void* filename, int mode); typedef struct zlib_filefunc64_def_s { open64_file_func zopen64_file; read_file_func zread_file; write_file_func zwrite_file; tell64_file_func ztell64_file; seek64_file_func zseek64_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc64_def; void fill_fopen64_filefunc(zlib_filefunc64_def* pzlib_filefunc_def); void fill_fopen_filefunc(zlib_filefunc_def* pzlib_filefunc_def); /* now internal definition, only for zip.c and unzip.h */ typedef struct zlib_filefunc64_32_def_s { zlib_filefunc64_def zfile_func64; open_file_func zopen32_file; tell_file_func ztell32_file; seek_file_func zseek32_file; } zlib_filefunc64_32_def; #define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) #define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) //#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) //#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) #define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) #define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) voidpf call_zopen64(const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode); long call_zseek64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin); ZPOS64_T call_ztell64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream); void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); #define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) #define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) #define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) #ifdef __cplusplus } #endif #endif |
Added compat/zlib/contrib/minizip/iowin32.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 | /* iowin32.c -- IO base function header for compress/uncompress .zip Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt */ #include <stdlib.h> #include "zlib.h" #include "ioapi.h" #include "iowin32.h" #ifndef INVALID_HANDLE_VALUE #define INVALID_HANDLE_VALUE (0xFFFFFFFF) #endif #ifndef INVALID_SET_FILE_POINTER #define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif // see Include/shared/winapifamily.h in the Windows Kit #if defined(WINAPI_FAMILY_PARTITION) && (!(defined(IOWIN32_USING_WINRT_API))) #if !defined(WINAPI_FAMILY_ONE_PARTITION) #define WINAPI_FAMILY_ONE_PARTITION(PartitionSet, Partition) ((WINAPI_FAMILY & PartitionSet) == Partition) #endif #if WINAPI_FAMILY_ONE_PARTITION(WINAPI_FAMILY, WINAPI_PARTITION_APP) #define IOWIN32_USING_WINRT_API 1 #endif #endif typedef struct { HANDLE hf; int error; } WIN32FILE_IOWIN; static void win32_translate_open_mode(int mode, DWORD* lpdwDesiredAccess, DWORD* lpdwCreationDisposition, DWORD* lpdwShareMode, DWORD* lpdwFlagsAndAttributes) { *lpdwDesiredAccess = *lpdwShareMode = *lpdwFlagsAndAttributes = *lpdwCreationDisposition = 0; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) { *lpdwDesiredAccess = GENERIC_READ; *lpdwCreationDisposition = OPEN_EXISTING; *lpdwShareMode = FILE_SHARE_READ; } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) { *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; *lpdwCreationDisposition = OPEN_EXISTING; } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) { *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; *lpdwCreationDisposition = CREATE_ALWAYS; } } static voidpf win32_build_iowin(HANDLE hFile) { voidpf ret=NULL; if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) { WIN32FILE_IOWIN w32fiow; w32fiow.hf = hFile; w32fiow.error = 0; ret = malloc(sizeof(WIN32FILE_IOWIN)); if (ret==NULL) CloseHandle(hFile); else *((WIN32FILE_IOWIN*)ret) = w32fiow; } return ret; } voidpf ZCALLBACK win32_open64_file_func(voidpf opaque, const void* filename, int mode) { const char* mode_fopen = NULL; DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; HANDLE hFile = NULL; win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); #ifdef IOWIN32_USING_WINRT_API #ifdef UNICODE if ((filename!=NULL) && (dwDesiredAccess != 0)) hFile = CreateFile2((LPCTSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); #else if ((filename!=NULL) && (dwDesiredAccess != 0)) { WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); } #endif #else if ((filename!=NULL) && (dwDesiredAccess != 0)) hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); #endif return win32_build_iowin(hFile); } voidpf ZCALLBACK win32_open64_file_funcA(voidpf opaque, const void* filename, int mode) { const char* mode_fopen = NULL; DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; HANDLE hFile = NULL; win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); #ifdef IOWIN32_USING_WINRT_API if ((filename!=NULL) && (dwDesiredAccess != 0)) { WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); } #else if ((filename!=NULL) && (dwDesiredAccess != 0)) hFile = CreateFileA((LPCSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); #endif return win32_build_iowin(hFile); } voidpf ZCALLBACK win32_open64_file_funcW(voidpf opaque, const void* filename, int mode) { const char* mode_fopen = NULL; DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; HANDLE hFile = NULL; win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); #ifdef IOWIN32_USING_WINRT_API if ((filename!=NULL) && (dwDesiredAccess != 0)) hFile = CreateFile2((LPCWSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition,NULL); #else if ((filename!=NULL) && (dwDesiredAccess != 0)) hFile = CreateFileW((LPCWSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); #endif return win32_build_iowin(hFile); } voidpf ZCALLBACK win32_open_file_func(voidpf opaque, const char* filename, int mode) { const char* mode_fopen = NULL; DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; HANDLE hFile = NULL; win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); #ifdef IOWIN32_USING_WINRT_API #ifdef UNICODE if ((filename!=NULL) && (dwDesiredAccess != 0)) hFile = CreateFile2((LPCTSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); #else if ((filename!=NULL) && (dwDesiredAccess != 0)) { WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); } #endif #else if ((filename!=NULL) && (dwDesiredAccess != 0)) hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); #endif return win32_build_iowin(hFile); } uLong ZCALLBACK win32_read_file_func(voidpf opaque, voidpf stream, void* buf,uLong size) { uLong ret=0; HANDLE hFile = NULL; if (stream!=NULL) hFile = ((WIN32FILE_IOWIN*)stream) -> hf; if (hFile != NULL) { if (!ReadFile(hFile, buf, size, &ret, NULL)) { DWORD dwErr = GetLastError(); if (dwErr == ERROR_HANDLE_EOF) dwErr = 0; ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; } } return ret; } uLong ZCALLBACK win32_write_file_func(voidpf opaque, voidpf stream, const void* buf, uLong size) { uLong ret=0; HANDLE hFile = NULL; if (stream!=NULL) hFile = ((WIN32FILE_IOWIN*)stream) -> hf; if (hFile != NULL) { if (!WriteFile(hFile, buf, size, &ret, NULL)) { DWORD dwErr = GetLastError(); if (dwErr == ERROR_HANDLE_EOF) dwErr = 0; ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; } } return ret; } static BOOL MySetFilePointerEx(HANDLE hFile, LARGE_INTEGER pos, LARGE_INTEGER *newPos, DWORD dwMoveMethod) { #ifdef IOWIN32_USING_WINRT_API return SetFilePointerEx(hFile, pos, newPos, dwMoveMethod); #else LONG lHigh = pos.HighPart; DWORD dwNewPos = SetFilePointer(hFile, pos.LowPart, &lHigh, dwMoveMethod); BOOL fOk = TRUE; if (dwNewPos == 0xFFFFFFFF) if (GetLastError() != NO_ERROR) fOk = FALSE; if ((newPos != NULL) && (fOk)) { newPos->LowPart = dwNewPos; newPos->HighPart = lHigh; } return fOk; #endif } long ZCALLBACK win32_tell_file_func(voidpf opaque, voidpf stream) { long ret=-1; HANDLE hFile = NULL; if (stream!=NULL) hFile = ((WIN32FILE_IOWIN*)stream) -> hf; if (hFile != NULL) { LARGE_INTEGER pos; pos.QuadPart = 0; if (!MySetFilePointerEx(hFile, pos, &pos, FILE_CURRENT)) { DWORD dwErr = GetLastError(); ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; ret = -1; } else ret=(long)pos.LowPart; } return ret; } ZPOS64_T ZCALLBACK win32_tell64_file_func(voidpf opaque, voidpf stream) { ZPOS64_T ret= (ZPOS64_T)-1; HANDLE hFile = NULL; if (stream!=NULL) hFile = ((WIN32FILE_IOWIN*)stream)->hf; if (hFile) { LARGE_INTEGER pos; pos.QuadPart = 0; if (!MySetFilePointerEx(hFile, pos, &pos, FILE_CURRENT)) { DWORD dwErr = GetLastError(); ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; ret = (ZPOS64_T)-1; } else ret=pos.QuadPart; } return ret; } long ZCALLBACK win32_seek_file_func(voidpf opaque, voidpf stream, uLong offset, int origin) { DWORD dwMoveMethod=0xFFFFFFFF; HANDLE hFile = NULL; long ret=-1; if (stream!=NULL) hFile = ((WIN32FILE_IOWIN*)stream) -> hf; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : dwMoveMethod = FILE_CURRENT; break; case ZLIB_FILEFUNC_SEEK_END : dwMoveMethod = FILE_END; break; case ZLIB_FILEFUNC_SEEK_SET : dwMoveMethod = FILE_BEGIN; break; default: return -1; } if (hFile != NULL) { LARGE_INTEGER pos; pos.QuadPart = offset; if (!MySetFilePointerEx(hFile, pos, NULL, dwMoveMethod)) { DWORD dwErr = GetLastError(); ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; ret = -1; } else ret=0; } return ret; } long ZCALLBACK win32_seek64_file_func(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) { DWORD dwMoveMethod=0xFFFFFFFF; HANDLE hFile = NULL; long ret=-1; if (stream!=NULL) hFile = ((WIN32FILE_IOWIN*)stream)->hf; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : dwMoveMethod = FILE_CURRENT; break; case ZLIB_FILEFUNC_SEEK_END : dwMoveMethod = FILE_END; break; case ZLIB_FILEFUNC_SEEK_SET : dwMoveMethod = FILE_BEGIN; break; default: return -1; } if (hFile) { LARGE_INTEGER pos; pos.QuadPart = offset; if (!MySetFilePointerEx(hFile, pos, NULL, dwMoveMethod)) { DWORD dwErr = GetLastError(); ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; ret = -1; } else ret=0; } return ret; } int ZCALLBACK win32_close_file_func(voidpf opaque, voidpf stream) { int ret=-1; if (stream!=NULL) { HANDLE hFile; hFile = ((WIN32FILE_IOWIN*)stream) -> hf; if (hFile != NULL) { CloseHandle(hFile); ret=0; } free(stream); } return ret; } int ZCALLBACK win32_error_file_func(voidpf opaque, voidpf stream) { int ret=-1; if (stream!=NULL) { ret = ((WIN32FILE_IOWIN*)stream) -> error; } return ret; } void fill_win32_filefunc(zlib_filefunc_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen_file = win32_open_file_func; pzlib_filefunc_def->zread_file = win32_read_file_func; pzlib_filefunc_def->zwrite_file = win32_write_file_func; pzlib_filefunc_def->ztell_file = win32_tell_file_func; pzlib_filefunc_def->zseek_file = win32_seek_file_func; pzlib_filefunc_def->zclose_file = win32_close_file_func; pzlib_filefunc_def->zerror_file = win32_error_file_func; pzlib_filefunc_def->opaque = NULL; } void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen64_file = win32_open64_file_func; pzlib_filefunc_def->zread_file = win32_read_file_func; pzlib_filefunc_def->zwrite_file = win32_write_file_func; pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; pzlib_filefunc_def->zclose_file = win32_close_file_func; pzlib_filefunc_def->zerror_file = win32_error_file_func; pzlib_filefunc_def->opaque = NULL; } void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen64_file = win32_open64_file_funcA; pzlib_filefunc_def->zread_file = win32_read_file_func; pzlib_filefunc_def->zwrite_file = win32_write_file_func; pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; pzlib_filefunc_def->zclose_file = win32_close_file_func; pzlib_filefunc_def->zerror_file = win32_error_file_func; pzlib_filefunc_def->opaque = NULL; } void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen64_file = win32_open64_file_funcW; pzlib_filefunc_def->zread_file = win32_read_file_func; pzlib_filefunc_def->zwrite_file = win32_write_file_func; pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; pzlib_filefunc_def->zclose_file = win32_close_file_func; pzlib_filefunc_def->zerror_file = win32_error_file_func; pzlib_filefunc_def->opaque = NULL; } |
Added compat/zlib/contrib/minizip/iowin32.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* iowin32.h -- IO base function header for compress/uncompress .zip Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt */ #include <windows.h> #ifdef __cplusplus extern "C" { #endif void fill_win32_filefunc(zlib_filefunc_def* pzlib_filefunc_def); void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def); void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def); void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def); #ifdef __cplusplus } #endif |
Added compat/zlib/contrib/minizip/make_vms.com.
> > > > > > > > > > > > > > > > > > > > > > > > > | 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 | $ if f$search("ioapi.h_orig") .eqs. "" then copy ioapi.h ioapi.h_orig $ open/write zdef vmsdefs.h $ copy sys$input: zdef $ deck #define unix #define fill_zlib_filefunc64_32_def_from_filefunc32 fillzffunc64from #define Write_Zip64EndOfCentralDirectoryLocator Write_Zip64EoDLocator #define Write_Zip64EndOfCentralDirectoryRecord Write_Zip64EoDRecord #define Write_EndOfCentralDirectoryRecord Write_EoDRecord $ eod $ close zdef $ copy vmsdefs.h,ioapi.h_orig ioapi.h $ cc/include=[--]/prefix=all ioapi.c $ cc/include=[--]/prefix=all miniunz.c $ cc/include=[--]/prefix=all unzip.c $ cc/include=[--]/prefix=all minizip.c $ cc/include=[--]/prefix=all zip.c $ link miniunz,unzip,ioapi,[--]libz.olb/lib $ link minizip,zip,ioapi,[--]libz.olb/lib $ mcr []minizip test minizip_info.txt $ mcr []miniunz -l test.zip $ rename minizip_info.txt; minizip_info.txt_old $ mcr []miniunz test.zip $ delete test.zip;* $exit |
Added compat/zlib/contrib/minizip/miniunz.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 | /* miniunz.c Version 1.1, February 14h, 2010 sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications of Unzip for Zip64 Copyright (C) 2007-2008 Even Rouault Modifications for Zip64 support on both zip and unzip Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) */ #if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) #ifndef __USE_FILE_OFFSET64 #define __USE_FILE_OFFSET64 #endif #ifndef __USE_LARGEFILE64 #define __USE_LARGEFILE64 #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _FILE_OFFSET_BIT #define _FILE_OFFSET_BIT 64 #endif #endif #if defined(__APPLE__) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions #define FOPEN_FUNC(filename, mode) fopen(filename, mode) #define FTELLO_FUNC(stream) ftello(stream) #define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) #else #define FOPEN_FUNC(filename, mode) fopen64(filename, mode) #define FTELLO_FUNC(stream) ftello64(stream) #define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <errno.h> #include <fcntl.h> #include <sys/stat.h> #ifdef _WIN32 # include <direct.h> # include <io.h> #else # include <unistd.h> # include <utime.h> #endif #include "unzip.h" #define CASESENSITIVITY (0) #define WRITEBUFFERSIZE (8192) #define MAXFILENAME (256) #ifdef _WIN32 #define USEWIN32IOAPI #include "iowin32.h" #endif /* mini unzip, demo of unzip package usage : Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir] list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT if it exists */ /* change_file_date : change the date/time of a file filename : the filename of the file where date/time must be modified dosdate : the new date at the MSDOS format (4 bytes) tmu_date : the SAME new date at the tm_unz format */ static void change_file_date(const char *filename, uLong dosdate, tm_unz tmu_date) { #ifdef _WIN32 HANDLE hFile; FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite; hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE, 0,NULL,OPEN_EXISTING,0,NULL); GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite); DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal); LocalFileTimeToFileTime(&ftLocal,&ftm); SetFileTime(hFile,&ftm,&ftLastAcc,&ftm); CloseHandle(hFile); #else #if defined(unix) || defined(__APPLE__) (void)dosdate; struct utimbuf ut; struct tm newdate; newdate.tm_sec = tmu_date.tm_sec; newdate.tm_min=tmu_date.tm_min; newdate.tm_hour=tmu_date.tm_hour; newdate.tm_mday=tmu_date.tm_mday; newdate.tm_mon=tmu_date.tm_mon; if (tmu_date.tm_year > 1900) newdate.tm_year=tmu_date.tm_year - 1900; else newdate.tm_year=tmu_date.tm_year ; newdate.tm_isdst=-1; ut.actime=ut.modtime=mktime(&newdate); utime(filename,&ut); #else (void)filename; (void)dosdate; (void)tmu_date; #endif #endif } /* mymkdir and change_file_date are not 100 % portable As I don't know well Unix, I wait feedback for the unix portion */ static int mymkdir(const char* dirname) { int ret=0; #ifdef _WIN32 ret = _mkdir(dirname); #elif unix ret = mkdir (dirname,0775); #elif __APPLE__ ret = mkdir (dirname,0775); #else (void)dirname; #endif return ret; } static int makedir(const char *newdir) { char *buffer ; char *p; size_t len = strlen(newdir); if (len == 0) return 0; buffer = (char*)malloc(len+1); if (buffer==NULL) { printf("Error allocating memory\n"); return UNZ_INTERNALERROR; } strcpy(buffer,newdir); if (buffer[len-1] == '/') { buffer[len-1] = '\0'; } if (mymkdir(buffer) == 0) { free(buffer); return 1; } p = buffer+1; while (1) { char hold; while(*p && *p != '\\' && *p != '/') p++; hold = *p; *p = 0; if ((mymkdir(buffer) == -1) && (errno == ENOENT)) { printf("couldn't create directory %s\n",buffer); free(buffer); return 0; } if (hold == 0) break; *p++ = hold; } free(buffer); return 1; } static void do_banner(void) { printf("MiniUnz 1.1, demo of zLib + Unz package written by Gilles Vollant\n"); printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n"); } static void do_help(void) { printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \ " -e Extract without pathname (junk paths)\n" \ " -x Extract with pathname\n" \ " -v list files\n" \ " -l list files\n" \ " -d directory to extract into\n" \ " -o overwrite files without prompting\n" \ " -p extract encrypted file using password\n\n"); } static void Display64BitsSize(ZPOS64_T n, int size_char) { /* to avoid compatibility problem , we do here the conversion */ char number[21]; int offset=19; int pos_string = 19; number[20]=0; for (;;) { number[offset]=(char)((n%10)+'0'); if (number[offset] != '0') pos_string=offset; n/=10; if (offset==0) break; offset--; } { int size_display_string = 19-pos_string; while (size_char > size_display_string) { size_char--; printf(" "); } } printf("%s",&number[pos_string]); } static int do_list(unzFile uf) { uLong i; unz_global_info64 gi; int err; err = unzGetGlobalInfo64(uf,&gi); if (err!=UNZ_OK) printf("error %d with zipfile in unzGetGlobalInfo \n",err); printf(" Length Method Size Ratio Date Time CRC-32 Name\n"); printf(" ------ ------ ---- ----- ---- ---- ------ ----\n"); for (i=0;i<gi.number_entry;i++) { char filename_inzip[256]; unz_file_info64 file_info; uLong ratio=0; const char *string_method = ""; char charCrypt=' '; err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0); if (err!=UNZ_OK) { printf("error %d with zipfile in unzGetCurrentFileInfo\n",err); break; } if (file_info.uncompressed_size>0) ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size); /* display a '*' if the file is encrypted */ if ((file_info.flag & 1) != 0) charCrypt='*'; if (file_info.compression_method==0) string_method="Stored"; else if (file_info.compression_method==Z_DEFLATED) { uInt iLevel=(uInt)((file_info.flag & 0x6)/2); if (iLevel==0) string_method="Defl:N"; else if (iLevel==1) string_method="Defl:X"; else if ((iLevel==2) || (iLevel==3)) string_method="Defl:F"; /* 2:fast , 3 : extra fast*/ } else if (file_info.compression_method==Z_BZIP2ED) { string_method="BZip2 "; } else string_method="Unkn. "; Display64BitsSize(file_info.uncompressed_size,7); printf(" %6s%c",string_method,charCrypt); Display64BitsSize(file_info.compressed_size,7); printf(" %3lu%% %2.2lu-%2.2lu-%2.2lu %2.2lu:%2.2lu %8.8lx %s\n", ratio, (uLong)file_info.tmu_date.tm_mon + 1, (uLong)file_info.tmu_date.tm_mday, (uLong)file_info.tmu_date.tm_year % 100, (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min, (uLong)file_info.crc,filename_inzip); if ((i+1)<gi.number_entry) { err = unzGoToNextFile(uf); if (err!=UNZ_OK) { printf("error %d with zipfile in unzGoToNextFile\n",err); break; } } } return 0; } static int do_extract_currentfile(unzFile uf, const int* popt_extract_without_path, int* popt_overwrite, const char* password) { char filename_inzip[256]; char* filename_withoutpath; char* p; int err=UNZ_OK; FILE *fout=NULL; void* buf; uInt size_buf; unz_file_info64 file_info; err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0); if (err!=UNZ_OK) { printf("error %d with zipfile in unzGetCurrentFileInfo\n",err); return err; } size_buf = WRITEBUFFERSIZE; buf = (void*)malloc(size_buf); if (buf==NULL) { printf("Error allocating memory\n"); return UNZ_INTERNALERROR; } p = filename_withoutpath = filename_inzip; while ((*p) != '\0') { if (((*p)=='/') || ((*p)=='\\')) filename_withoutpath = p+1; p++; } if ((*filename_withoutpath)=='\0') { if ((*popt_extract_without_path)==0) { printf("creating directory: %s\n",filename_inzip); mymkdir(filename_inzip); } } else { const char* write_filename; int skip=0; if ((*popt_extract_without_path)==0) write_filename = filename_inzip; else write_filename = filename_withoutpath; if (write_filename[0]!='\0') { const char* relative_check = write_filename; while (relative_check[1]!='\0') { if (relative_check[0]=='.' && relative_check[1]=='.') write_filename = relative_check; relative_check++; } } while (write_filename[0]=='/' || write_filename[0]=='.') write_filename++; err = unzOpenCurrentFilePassword(uf,password); if (err!=UNZ_OK) { printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err); } if (((*popt_overwrite)==0) && (err==UNZ_OK)) { char rep=0; FILE* ftestexist; ftestexist = FOPEN_FUNC(write_filename,"rb"); if (ftestexist!=NULL) { fclose(ftestexist); do { char answer[128]; int ret; printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename); ret = scanf("%1s",answer); if (ret != 1) { exit(EXIT_FAILURE); } rep = answer[0] ; if ((rep>='a') && (rep<='z')) rep -= 0x20; } while ((rep!='Y') && (rep!='N') && (rep!='A')); } if (rep == 'N') skip = 1; if (rep == 'A') *popt_overwrite=1; } if ((skip==0) && (err==UNZ_OK)) { fout=FOPEN_FUNC(write_filename,"wb"); /* some zipfile don't contain directory alone before file */ if ((fout==NULL) && ((*popt_extract_without_path)==0) && (filename_withoutpath!=(char*)filename_inzip)) { char c=*(filename_withoutpath-1); *(filename_withoutpath-1)='\0'; makedir(write_filename); *(filename_withoutpath-1)=c; fout=FOPEN_FUNC(write_filename,"wb"); } if (fout==NULL) { printf("error opening %s\n",write_filename); } } if (fout!=NULL) { printf(" extracting: %s\n",write_filename); do { err = unzReadCurrentFile(uf,buf,size_buf); if (err<0) { printf("error %d with zipfile in unzReadCurrentFile\n",err); break; } if (err>0) if (fwrite(buf,(unsigned)err,1,fout)!=1) { printf("error in writing extracted file\n"); err=UNZ_ERRNO; break; } } while (err>0); if (fout) fclose(fout); if (err==0) change_file_date(write_filename,file_info.dosDate, file_info.tmu_date); } if (err==UNZ_OK) { err = unzCloseCurrentFile (uf); if (err!=UNZ_OK) { printf("error %d with zipfile in unzCloseCurrentFile\n",err); } } else unzCloseCurrentFile(uf); /* don't lose the error */ } free(buf); return err; } static int do_extract(unzFile uf, int opt_extract_without_path, int opt_overwrite, const char* password) { uLong i; unz_global_info64 gi; int err; err = unzGetGlobalInfo64(uf,&gi); if (err!=UNZ_OK) printf("error %d with zipfile in unzGetGlobalInfo \n",err); for (i=0;i<gi.number_entry;i++) { if (do_extract_currentfile(uf,&opt_extract_without_path, &opt_overwrite, password) != UNZ_OK) break; if ((i+1)<gi.number_entry) { err = unzGoToNextFile(uf); if (err!=UNZ_OK) { printf("error %d with zipfile in unzGoToNextFile\n",err); break; } } } return 0; } static int do_extract_onefile(unzFile uf, const char* filename, int opt_extract_without_path, int opt_overwrite, const char* password) { if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK) { printf("file %s not found in the zipfile\n",filename); return 2; } if (do_extract_currentfile(uf,&opt_extract_without_path, &opt_overwrite, password) == UNZ_OK) return 0; else return 1; } int main(int argc, char *argv[]) { const char *zipfilename=NULL; const char *filename_to_extract=NULL; const char *password=NULL; char filename_try[MAXFILENAME+16] = ""; int i; int ret_value=0; int opt_do_list=0; int opt_do_extract=1; int opt_do_extract_withoutpath=0; int opt_overwrite=0; int opt_extractdir=0; const char *dirname=NULL; unzFile uf=NULL; do_banner(); if (argc==1) { do_help(); return 0; } else { for (i=1;i<argc;i++) { if ((*argv[i])=='-') { const char *p=argv[i]+1; while ((*p)!='\0') { char c=*(p++); if ((c=='l') || (c=='L')) opt_do_list = 1; if ((c=='v') || (c=='V')) opt_do_list = 1; if ((c=='x') || (c=='X')) opt_do_extract = 1; if ((c=='e') || (c=='E')) opt_do_extract = opt_do_extract_withoutpath = 1; if ((c=='o') || (c=='O')) opt_overwrite=1; if ((c=='d') || (c=='D')) { opt_extractdir=1; dirname=argv[i+1]; } if (((c=='p') || (c=='P')) && (i+1<argc)) { password=argv[i+1]; i++; } } } else { if (zipfilename == NULL) zipfilename = argv[i]; else if ((filename_to_extract==NULL) && (!opt_extractdir)) filename_to_extract = argv[i] ; } } } if (zipfilename!=NULL) { # ifdef USEWIN32IOAPI zlib_filefunc64_def ffunc; # endif strncpy(filename_try, zipfilename,MAXFILENAME-1); /* strncpy doesn't append the trailing NULL, of the string is too long. */ filename_try[ MAXFILENAME ] = '\0'; # ifdef USEWIN32IOAPI fill_win32_filefunc64A(&ffunc); uf = unzOpen2_64(zipfilename,&ffunc); # else uf = unzOpen64(zipfilename); # endif if (uf==NULL) { strcat(filename_try,".zip"); # ifdef USEWIN32IOAPI uf = unzOpen2_64(filename_try,&ffunc); # else uf = unzOpen64(filename_try); # endif } } if (uf==NULL) { printf("Cannot open %s or %s.zip\n",zipfilename,zipfilename); return 1; } printf("%s opened\n",filename_try); if (opt_do_list==1) ret_value = do_list(uf); else if (opt_do_extract==1) { #ifdef _WIN32 if (opt_extractdir && _chdir(dirname)) #else if (opt_extractdir && chdir(dirname)) #endif { printf("Error changing into %s, aborting\n", dirname); exit(-1); } if (filename_to_extract == NULL) ret_value = do_extract(uf, opt_do_extract_withoutpath, opt_overwrite, password); else ret_value = do_extract_onefile(uf, filename_to_extract, opt_do_extract_withoutpath, opt_overwrite, password); } unzClose(uf); return ret_value; } |
Added compat/zlib/contrib/minizip/miniunzip.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 | .\" Hey, EMACS: -*- nroff -*- .TH miniunzip 1 "Nov 7, 2001" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp <n> insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME miniunzip - uncompress and examine ZIP archives .SH SYNOPSIS .B miniunzip .RI [ -exvlo ] zipfile [ files_to_extract ] [-d tempdir] .SH DESCRIPTION .B minizip is a simple tool which allows the extraction of compressed file archives in the ZIP format used by the MS-DOS utility PKZIP. It was written as a demonstration of the .IR zlib (3) library and therefore lack many of the features of the .IR unzip (1) program. .SH OPTIONS A number of options are supported. With the exception of .BI \-d\ tempdir these must be supplied before any other arguments and are: .TP .BI \-l\ ,\ \-\-v List the files in the archive without extracting them. .TP .B \-o Overwrite files without prompting for confirmation. .TP .B \-x Extract files (default). .PP The .I zipfile argument is the name of the archive to process. The next argument can be used to specify a single file to extract from the archive. Lastly, the following option can be specified at the end of the command-line: .TP .BI \-d\ tempdir Extract the archive in the directory .I tempdir rather than the current directory. .SH SEE ALSO .BR minizip (1), .BR zlib (3), .BR unzip (1). .SH AUTHOR This program was written by Gilles Vollant. This manual page was written by Mark Brown <broonie@sirena.org.uk>. The -d tempdir option was added by Dirk Eddelbuettel <edd@debian.org>. |
Added compat/zlib/contrib/minizip/minizip.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 | .\" Hey, EMACS: -*- nroff -*- .TH minizip 1 "May 2, 2001" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp <n> insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME minizip - create ZIP archives .SH SYNOPSIS .B minizip .RI [ -o ] zipfile [ " files" ... ] .SH DESCRIPTION .B minizip is a simple tool which allows the creation of compressed file archives in the ZIP format used by the MS-DOS utility PKZIP. It was written as a demonstration of the .IR zlib (3) library and therefore lack many of the features of the .IR zip (1) program. .SH OPTIONS The first argument supplied is the name of the ZIP archive to create or .RI -o in which case it is ignored and the second argument treated as the name of the ZIP file. If the ZIP file already exists it will be overwritten. .PP Subsequent arguments specify a list of files to place in the ZIP archive. If none are specified then an empty archive will be created. .SH SEE ALSO .BR miniunzip (1), .BR zlib (3), .BR zip (1). .SH AUTHOR This program was written by Gilles Vollant. This manual page was written by Mark Brown <broonie@sirena.org.uk>. |
Added compat/zlib/contrib/minizip/minizip.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 | /* minizip.c Version 1.1, February 14h, 2010 sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications of Unzip for Zip64 Copyright (C) 2007-2008 Even Rouault Modifications for Zip64 support on both zip and unzip Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) */ #if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) #ifndef __USE_FILE_OFFSET64 #define __USE_FILE_OFFSET64 #endif #ifndef __USE_LARGEFILE64 #define __USE_LARGEFILE64 #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _FILE_OFFSET_BIT #define _FILE_OFFSET_BIT 64 #endif #endif #if defined(__APPLE__) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions #define FOPEN_FUNC(filename, mode) fopen(filename, mode) #define FTELLO_FUNC(stream) ftello(stream) #define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) #else #define FOPEN_FUNC(filename, mode) fopen64(filename, mode) #define FTELLO_FUNC(stream) ftello64(stream) #define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <errno.h> #include <fcntl.h> #ifdef _WIN32 # include <direct.h> # include <io.h> #else # include <unistd.h> # include <utime.h> # include <sys/types.h> # include <sys/stat.h> #endif #include "zip.h" #ifdef _WIN32 #define USEWIN32IOAPI #include "iowin32.h" #endif #define WRITEBUFFERSIZE (16384) #define MAXFILENAME (256) #ifdef _WIN32 /* f: name of file to get info on, tmzip: return value: access, modification and creation times, dt: dostime */ static int filetime(const char *f, tm_zip *tmzip, uLong *dt) { int ret = 0; { FILETIME ftLocal; HANDLE hFind; WIN32_FIND_DATAA ff32; hFind = FindFirstFileA(f,&ff32); if (hFind != INVALID_HANDLE_VALUE) { FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal); FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0); FindClose(hFind); ret = 1; } } return ret; } #else #if defined(unix) || defined(__APPLE__) /* f: name of file to get info on, tmzip: return value: access, modification and creation times, dt: dostime */ static int filetime(const char *f, tm_zip *tmzip, uLong *dt) { (void)dt; int ret=0; struct stat s; /* results of stat() */ struct tm* filedate; time_t tm_t=0; if (strcmp(f,"-")!=0) { char name[MAXFILENAME+1]; size_t len = strlen(f); if (len > MAXFILENAME) len = MAXFILENAME; strncpy(name, f,MAXFILENAME-1); /* strncpy doesn't append the trailing NULL, of the string is too long. */ name[ MAXFILENAME ] = '\0'; if (name[len - 1] == '/') name[len - 1] = '\0'; /* not all systems allow stat'ing a file with / appended */ if (stat(name,&s)==0) { tm_t = s.st_mtime; ret = 1; } } filedate = localtime(&tm_t); tmzip->tm_sec = filedate->tm_sec; tmzip->tm_min = filedate->tm_min; tmzip->tm_hour = filedate->tm_hour; tmzip->tm_mday = filedate->tm_mday; tmzip->tm_mon = filedate->tm_mon ; tmzip->tm_year = filedate->tm_year; return ret; } #else /* f: name of file to get info on, tmzip: return value: access, modification and creation times, dt: dostime */ static int filetime(const char *f, tm_zip *tmzip, uLong *dt) { (void)f; (void)tmzip; (void)dt; return 0; } #endif #endif static int check_exist_file(const char* filename) { FILE* ftestexist; int ret = 1; ftestexist = FOPEN_FUNC(filename,"rb"); if (ftestexist==NULL) ret = 0; else fclose(ftestexist); return ret; } static void do_banner(void) { printf("MiniZip 1.1, demo of zLib + MiniZip64 package, written by Gilles Vollant\n"); printf("more info on MiniZip at http://www.winimage.com/zLibDll/minizip.html\n\n"); } static void do_help(void) { printf("Usage : minizip [-o] [-a] [-0 to -9] [-p password] [-j] file.zip [files_to_add]\n\n" \ " -o Overwrite existing file.zip\n" \ " -a Append to existing file.zip\n" \ " -0 Store only\n" \ " -1 Compress faster\n" \ " -9 Compress better\n\n" \ " -j exclude path. store only the file name.\n\n"); } /* calculate the CRC32 of a file, because to encrypt a file, we need known the CRC32 of the file before */ static int getFileCrc(const char* filenameinzip, void* buf, unsigned long size_buf, unsigned long* result_crc) { unsigned long calculate_crc=0; int err=ZIP_OK; FILE * fin = FOPEN_FUNC(filenameinzip,"rb"); unsigned long size_read = 0; /* unsigned long total_read = 0; */ if (fin==NULL) { err = ZIP_ERRNO; } if (err == ZIP_OK) do { err = ZIP_OK; size_read = fread(buf,1,size_buf,fin); if (size_read < size_buf) if (feof(fin)==0) { printf("error in reading %s\n",filenameinzip); err = ZIP_ERRNO; } if (size_read>0) calculate_crc = crc32_z(calculate_crc,buf,size_read); /* total_read += size_read; */ } while ((err == ZIP_OK) && (size_read>0)); if (fin) fclose(fin); *result_crc=calculate_crc; printf("file %s crc %lx\n", filenameinzip, calculate_crc); return err; } static int isLargeFile(const char* filename) { int largeFile = 0; ZPOS64_T pos = 0; FILE* pFile = FOPEN_FUNC(filename, "rb"); if(pFile != NULL) { FSEEKO_FUNC(pFile, 0, SEEK_END); pos = (ZPOS64_T)FTELLO_FUNC(pFile); printf("File : %s is %llu bytes\n", filename, pos); if(pos >= 0xffffffff) largeFile = 1; fclose(pFile); } return largeFile; } int main(int argc, char *argv[]) { int i; int opt_overwrite=0; int opt_compress_level=Z_DEFAULT_COMPRESSION; int opt_exclude_path=0; int zipfilenamearg = 0; char filename_try[MAXFILENAME+16]; int zipok; int err=0; size_t size_buf=0; void* buf=NULL; const char* password=NULL; do_banner(); if (argc==1) { do_help(); return 0; } else { for (i=1;i<argc;i++) { if ((*argv[i])=='-') { const char *p=argv[i]+1; while ((*p)!='\0') { char c=*(p++); if ((c=='o') || (c=='O')) opt_overwrite = 1; if ((c=='a') || (c=='A')) opt_overwrite = 2; if ((c>='0') && (c<='9')) opt_compress_level = c-'0'; if ((c=='j') || (c=='J')) opt_exclude_path = 1; if (((c=='p') || (c=='P')) && (i+1<argc)) { password=argv[i+1]; i++; } } } else { if (zipfilenamearg == 0) { zipfilenamearg = i ; } } } } size_buf = WRITEBUFFERSIZE; buf = (void*)malloc(size_buf); if (buf==NULL) { printf("Error allocating memory\n"); return ZIP_INTERNALERROR; } if (zipfilenamearg==0) { zipok=0; } else { int i,len; int dot_found=0; zipok = 1 ; strncpy(filename_try, argv[zipfilenamearg],MAXFILENAME-1); /* strncpy doesn't append the trailing NULL, of the string is too long. */ filename_try[ MAXFILENAME ] = '\0'; len=(int)strlen(filename_try); for (i=0;i<len;i++) if (filename_try[i]=='.') dot_found=1; if (dot_found==0) strcat(filename_try,".zip"); if (opt_overwrite==2) { /* if the file don't exist, we not append file */ if (check_exist_file(filename_try)==0) opt_overwrite=1; } else if (opt_overwrite==0) if (check_exist_file(filename_try)!=0) { char rep=0; do { char answer[128]; int ret; printf("The file %s exists. Overwrite ? [y]es, [n]o, [a]ppend : ",filename_try); ret = scanf("%1s",answer); if (ret != 1) { exit(EXIT_FAILURE); } rep = answer[0] ; if ((rep>='a') && (rep<='z')) rep -= 0x20; } while ((rep!='Y') && (rep!='N') && (rep!='A')); if (rep=='N') zipok = 0; if (rep=='A') opt_overwrite = 2; } } if (zipok==1) { zipFile zf; int errclose; # ifdef USEWIN32IOAPI zlib_filefunc64_def ffunc; fill_win32_filefunc64A(&ffunc); zf = zipOpen2_64(filename_try,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc); # else zf = zipOpen64(filename_try,(opt_overwrite==2) ? 2 : 0); # endif if (zf == NULL) { printf("error opening %s\n",filename_try); err= ZIP_ERRNO; } else printf("creating %s\n",filename_try); for (i=zipfilenamearg+1;(i<argc) && (err==ZIP_OK);i++) { if (!((((*(argv[i]))=='-') || ((*(argv[i]))=='/')) && ((argv[i][1]=='o') || (argv[i][1]=='O') || (argv[i][1]=='a') || (argv[i][1]=='A') || (argv[i][1]=='p') || (argv[i][1]=='P') || ((argv[i][1]>='0') && (argv[i][1]<='9'))) && (strlen(argv[i]) == 2))) { FILE * fin = NULL; size_t size_read; const char* filenameinzip = argv[i]; const char *savefilenameinzip; zip_fileinfo zi; unsigned long crcFile=0; int zip64 = 0; zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; zi.dosDate = 0; zi.internal_fa = 0; zi.external_fa = 0; filetime(filenameinzip,&zi.tmz_date,&zi.dosDate); /* err = zipOpenNewFileInZip(zf,filenameinzip,&zi, NULL,0,NULL,0,NULL / * comment * /, (opt_compress_level != 0) ? Z_DEFLATED : 0, opt_compress_level); */ if ((password != NULL) && (err==ZIP_OK)) err = getFileCrc(filenameinzip,buf,size_buf,&crcFile); zip64 = isLargeFile(filenameinzip); /* The path name saved, should not include a leading slash. */ /*if it did, windows/xp and dynazip couldn't read the zip file. */ savefilenameinzip = filenameinzip; while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' ) { savefilenameinzip++; } /*should the zip file contain any path at all?*/ if( opt_exclude_path ) { const char *tmpptr; const char *lastslash = 0; for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++) { if( *tmpptr == '\\' || *tmpptr == '/') { lastslash = tmpptr; } } if( lastslash != NULL ) { savefilenameinzip = lastslash+1; // base filename follows last slash. } } /**/ err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi, NULL,0,NULL,0,NULL /* comment*/, (opt_compress_level != 0) ? Z_DEFLATED : 0, opt_compress_level,0, /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */ -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, password,crcFile, zip64); if (err != ZIP_OK) printf("error in opening %s in zipfile\n",filenameinzip); else { fin = FOPEN_FUNC(filenameinzip,"rb"); if (fin==NULL) { err=ZIP_ERRNO; printf("error in opening %s for reading\n",filenameinzip); } } if (err == ZIP_OK) do { err = ZIP_OK; size_read = fread(buf,1,size_buf,fin); if (size_read < size_buf) if (feof(fin)==0) { printf("error in reading %s\n",filenameinzip); err = ZIP_ERRNO; } if (size_read>0) { err = zipWriteInFileInZip (zf,buf,(unsigned)size_read); if (err<0) { printf("error in writing %s in the zipfile\n", filenameinzip); } } } while ((err == ZIP_OK) && (size_read>0)); if (fin) fclose(fin); if (err<0) err=ZIP_ERRNO; else { err = zipCloseFileInZip(zf); if (err!=ZIP_OK) printf("error in closing %s in the zipfile\n", filenameinzip); } } } errclose = zipClose(zf,NULL); if (errclose != ZIP_OK) printf("error in closing %s\n",filename_try); } else { do_help(); } free(buf); return 0; } |
Added compat/zlib/contrib/minizip/minizip.pc.in.
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/minizip Name: minizip Description: Minizip zip file manipulation library Requires: Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lminizip Libs.private: -lz Cflags: -I${includedir} |
Added compat/zlib/contrib/minizip/mztools.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 | /* Additional tools for Minizip Code: Xavier Roche '2004 License: Same as ZLIB (www.gzip.org) */ /* Code */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "zlib.h" #include "unzip.h" #define READ_8(adr) ((unsigned char)*(adr)) #define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) ) #define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) ) #define WRITE_8(buff, n) do { \ *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \ } while(0) #define WRITE_16(buff, n) do { \ WRITE_8((unsigned char*)(buff), n); \ WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \ } while(0) #define WRITE_32(buff, n) do { \ WRITE_16((unsigned char*)(buff), (n) & 0xffff); \ WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \ } while(0) extern int ZEXPORT unzRepair(const char* file, const char* fileOut, const char* fileOutTmp, uLong* nRecovered, uLong* bytesRecovered) { int err = Z_OK; FILE* fpZip = fopen(file, "rb"); FILE* fpOut = fopen(fileOut, "wb"); FILE* fpOutCD = fopen(fileOutTmp, "wb"); if (fpZip != NULL && fpOut != NULL) { int entries = 0; uLong totalBytes = 0; char header[30]; char filename[1024]; char extra[1024]; int offset = 0; int offsetCD = 0; while ( fread(header, 1, 30, fpZip) == 30 ) { int currentOffset = offset; /* File entry */ if (READ_32(header) == 0x04034b50) { unsigned int version = READ_16(header + 4); unsigned int gpflag = READ_16(header + 6); unsigned int method = READ_16(header + 8); unsigned int filetime = READ_16(header + 10); unsigned int filedate = READ_16(header + 12); unsigned int crc = READ_32(header + 14); /* crc */ unsigned int cpsize = READ_32(header + 18); /* compressed size */ unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */ unsigned int fnsize = READ_16(header + 26); /* file name length */ unsigned int extsize = READ_16(header + 28); /* extra field length */ filename[0] = extra[0] = '\0'; /* Header */ if (fwrite(header, 1, 30, fpOut) == 30) { offset += 30; } else { err = Z_ERRNO; break; } /* Filename */ if (fnsize > 0) { if (fnsize < sizeof(filename)) { if (fread(filename, 1, fnsize, fpZip) == fnsize) { if (fwrite(filename, 1, fnsize, fpOut) == fnsize) { offset += fnsize; } else { err = Z_ERRNO; break; } } else { err = Z_ERRNO; break; } } else { err = Z_ERRNO; break; } } else { err = Z_STREAM_ERROR; break; } /* Extra field */ if (extsize > 0) { if (extsize < sizeof(extra)) { if (fread(extra, 1, extsize, fpZip) == extsize) { if (fwrite(extra, 1, extsize, fpOut) == extsize) { offset += extsize; } else { err = Z_ERRNO; break; } } else { err = Z_ERRNO; break; } } else { err = Z_ERRNO; break; } } /* Data */ { int dataSize = cpsize; if (dataSize == 0) { dataSize = uncpsize; } if (dataSize > 0) { char* data = malloc(dataSize); if (data != NULL) { if ((int)fread(data, 1, dataSize, fpZip) == dataSize) { if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) { offset += dataSize; totalBytes += dataSize; } else { err = Z_ERRNO; } } else { err = Z_ERRNO; } free(data); if (err != Z_OK) { break; } } else { err = Z_MEM_ERROR; break; } } } /* Central directory entry */ { char header[46]; char* comment = ""; int comsize = (int) strlen(comment); WRITE_32(header, 0x02014b50); WRITE_16(header + 4, version); WRITE_16(header + 6, version); WRITE_16(header + 8, gpflag); WRITE_16(header + 10, method); WRITE_16(header + 12, filetime); WRITE_16(header + 14, filedate); WRITE_32(header + 16, crc); WRITE_32(header + 20, cpsize); WRITE_32(header + 24, uncpsize); WRITE_16(header + 28, fnsize); WRITE_16(header + 30, extsize); WRITE_16(header + 32, comsize); WRITE_16(header + 34, 0); /* disk # */ WRITE_16(header + 36, 0); /* int attrb */ WRITE_32(header + 38, 0); /* ext attrb */ WRITE_32(header + 42, currentOffset); /* Header */ if (fwrite(header, 1, 46, fpOutCD) == 46) { offsetCD += 46; /* Filename */ if (fnsize > 0) { if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) { offsetCD += fnsize; } else { err = Z_ERRNO; break; } } else { err = Z_STREAM_ERROR; break; } /* Extra field */ if (extsize > 0) { if (fwrite(extra, 1, extsize, fpOutCD) == extsize) { offsetCD += extsize; } else { err = Z_ERRNO; break; } } /* Comment field */ if (comsize > 0) { if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) { offsetCD += comsize; } else { err = Z_ERRNO; break; } } } else { err = Z_ERRNO; break; } } /* Success */ entries++; } else { break; } } /* Final central directory */ { int entriesZip = entries; char header[22]; char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools"; int comsize = (int) strlen(comment); if (entriesZip > 0xffff) { entriesZip = 0xffff; } WRITE_32(header, 0x06054b50); WRITE_16(header + 4, 0); /* disk # */ WRITE_16(header + 6, 0); /* disk # */ WRITE_16(header + 8, entriesZip); /* hack */ WRITE_16(header + 10, entriesZip); /* hack */ WRITE_32(header + 12, offsetCD); /* size of CD */ WRITE_32(header + 16, offset); /* offset to CD */ WRITE_16(header + 20, comsize); /* comment */ /* Header */ if (fwrite(header, 1, 22, fpOutCD) == 22) { /* Comment field */ if (comsize > 0) { if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) { err = Z_ERRNO; } } } else { err = Z_ERRNO; } } /* Final merge (file + central directory) */ fclose(fpOutCD); if (err == Z_OK) { fpOutCD = fopen(fileOutTmp, "rb"); if (fpOutCD != NULL) { int nRead; char buffer[8192]; while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) { if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) { err = Z_ERRNO; break; } } fclose(fpOutCD); } } /* Close */ fclose(fpZip); fclose(fpOut); /* Wipe temporary file */ (void)remove(fileOutTmp); /* Number of recovered entries */ if (err == Z_OK) { if (nRecovered != NULL) { *nRecovered = entries; } if (bytesRecovered != NULL) { *bytesRecovered = totalBytes; } } } else { err = Z_STREAM_ERROR; } return err; } |
Added compat/zlib/contrib/minizip/mztools.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* Additional tools for Minizip Code: Xavier Roche '2004 License: Same as ZLIB (www.gzip.org) */ #ifndef _zip_tools_H #define _zip_tools_H #ifdef __cplusplus extern "C" { #endif #ifndef _ZLIB_H #include "zlib.h" #endif #include "unzip.h" /* Repair a ZIP file (missing central directory) file: file to recover fileOut: output file after recovery fileOutTmp: temporary file name used for recovery */ extern int ZEXPORT unzRepair(const char* file, const char* fileOut, const char* fileOutTmp, uLong* nRecovered, uLong* bytesRecovered); #ifdef __cplusplus } #endif #endif |
Added compat/zlib/contrib/minizip/unzip.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 | /* unzip.c -- IO for uncompress .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications of Unzip for Zip64 Copyright (C) 2007-2008 Even Rouault Modifications for Zip64 support on both zip and unzip Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt ------------------------------------------------------------------------------------ Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of compatibility with older software. The following is from the original crypt.c. Code woven in by Terry Thorsen 1/2003. Copyright (c) 1990-2000 Info-ZIP. All rights reserved. See the accompanying file LICENSE, version 2000-Apr-09 or later (the contents of which are also included in zip.h) for terms of use. If, for some reason, all these files are missing, the Info-ZIP license also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). ------------------------------------------------------------------------------------ Changes in unzip.c 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* 2007-2008 - Even Rouault - Remove old C style function prototypes 2007-2008 - Even Rouault - Add unzip support for ZIP64 Copyright (C) 2007-2008 Even Rouault Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G should only read the compressed/uncompressed size from the Zip64 format if the size from normal header was 0xFFFFFFFF Oct-2009 - Mathias Svensson - Applied some bug fixes from patches received from Gilles Vollant Oct-2009 - Mathias Svensson - Applied support to unzip files with compression method BZIP2 (bzip2 lib is required) Patch created by Daniel Borca Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson */ #include <stdio.h> #include <stdlib.h> #include <string.h> #ifndef NOUNCRYPT #define NOUNCRYPT #endif #include "zlib.h" #include "unzip.h" #ifdef STDC # include <stddef.h> #endif #ifdef NO_ERRNO_H extern int errno; #else # include <errno.h> #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef CASESENSITIVITYDEFAULT_NO # if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) # define CASESENSITIVITYDEFAULT_NO # endif #endif #ifndef UNZ_BUFSIZE #define UNZ_BUFSIZE (16384) #endif #ifndef UNZ_MAXFILENAMEINZIP #define UNZ_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) const char unz_copyright[] = " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; /* unz_file_info64_internal contain internal info about a file in zipfile*/ typedef struct unz_file_info64_internal_s { ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ } unz_file_info64_internal; /* file_in_zip_read_info_s contain internal information about a file in zipfile, when reading and decompress it */ typedef struct { char *read_buffer; /* internal buffer for compressed data */ z_stream stream; /* zLib stream structure for inflate */ #ifdef HAVE_BZIP2 bz_stream bstream; /* bzLib stream structure for bziped */ #endif ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ uLong stream_initialised; /* flag set if stream structure is initialised*/ ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ uInt size_local_extrafield;/* size of the local extra field */ ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ ZPOS64_T total_out_64; uLong crc32; /* crc32 of all data uncompressed */ uLong crc32_wait; /* crc32 we must obtain after decompress all */ ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ zlib_filefunc64_32_def z_filefunc; voidpf filestream; /* io structure of the zipfile */ uLong compression_method; /* compression method (0==store) */ ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ int raw; } file_in_zip64_read_info_s; /* unz64_s contain internal information about the zipfile */ typedef struct { zlib_filefunc64_32_def z_filefunc; int is64bitOpenFunction; voidpf filestream; /* io structure of the zipfile */ unz_global_info64 gi; /* public global information */ ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ ZPOS64_T num_file; /* number of the current file in the zipfile*/ ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ ZPOS64_T central_pos; /* position of the beginning of the central dir*/ ZPOS64_T size_central_dir; /* size of the central directory */ ZPOS64_T offset_central_dir; /* offset of start of central directory with respect to the starting disk number */ unz_file_info64 cur_file_info; /* public info about the current file in zip*/ unz_file_info64_internal cur_file_info_internal; /* private info about it*/ file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current file if we are decompressing it */ int encrypted; int isZip64; # ifndef NOUNCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const z_crc_t* pcrc_32_tab; # endif } unz64_s; #ifndef NOUNCRYPT #include "crypt.h" #endif /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int unz64local_getShort(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX) { unsigned char c[2]; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,c,2); if (err==2) { *pX = c[0] | ((uLong)c[1] << 8); return UNZ_OK; } else { *pX = 0; if (ZERROR64(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else return UNZ_EOF; } } local int unz64local_getLong(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX) { unsigned char c[4]; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,c,4); if (err==4) { *pX = c[0] | ((uLong)c[1] << 8) | ((uLong)c[2] << 16) | ((uLong)c[3] << 24); return UNZ_OK; } else { *pX = 0; if (ZERROR64(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else return UNZ_EOF; } } local int unz64local_getLong64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) { unsigned char c[8]; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,c,8); if (err==8) { *pX = c[0] | ((ZPOS64_T)c[1] << 8) | ((ZPOS64_T)c[2] << 16) | ((ZPOS64_T)c[3] << 24) | ((ZPOS64_T)c[4] << 32) | ((ZPOS64_T)c[5] << 40) | ((ZPOS64_T)c[6] << 48) | ((ZPOS64_T)c[7] << 56); return UNZ_OK; } else { *pX = 0; if (ZERROR64(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else return UNZ_EOF; } } /* My own strcmpi / strcasecmp */ local int strcmpcasenosensitive_internal(const char* fileName1, const char* fileName2) { for (;;) { char c1=*(fileName1++); char c2=*(fileName2++); if ((c1>='a') && (c1<='z')) c1 -= 0x20; if ((c2>='a') && (c2<='z')) c2 -= 0x20; if (c1=='\0') return ((c2=='\0') ? 0 : -1); if (c2=='\0') return 1; if (c1<c2) return -1; if (c1>c2) return 1; } } #ifdef CASESENSITIVITYDEFAULT_NO #define CASESENSITIVITYDEFAULTVALUE 2 #else #define CASESENSITIVITYDEFAULTVALUE 1 #endif #ifndef STRCMPCASENOSENTIVEFUNCTION #define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal #endif /* Compare two filenames (fileName1,fileName2). If iCaseSensitivity = 1, comparison is case sensitive (like strcmp) If iCaseSensitivity = 2, comparison is not case sensitive (like strcmpi or strcasecmp) If iCaseSensitivity = 0, case sensitivity is default of your operating system (like 1 on Unix, 2 on Windows) */ extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, const char* fileName2, int iCaseSensitivity) { if (iCaseSensitivity==0) iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); } #ifndef BUFREADCOMMENT #define BUFREADCOMMENT (0x400) #endif #ifndef CENTRALDIRINVALID #define CENTRALDIRINVALID ((ZPOS64_T)(-1)) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=CENTRALDIRINVALID; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return CENTRALDIRINVALID; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return CENTRALDIRINVALID; uBackRead = 4; while (uBackRead<uMaxBack) { uLong uReadSize; ZPOS64_T uReadPos ; int i; if (uBackRead+BUFREADCOMMENT>uMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+(unsigned)i; break; } if (uPosFound!=CENTRALDIRINVALID) break; } free(buf); return uPosFound; } /* Locate the Central directory 64 of a zipfile (at the end, just before the global comment) */ local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=CENTRALDIRINVALID; uLong uL; ZPOS64_T relativeOffset; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return CENTRALDIRINVALID; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return CENTRALDIRINVALID; uBackRead = 4; while (uBackRead<uMaxBack) { uLong uReadSize; ZPOS64_T uReadPos; int i; if (uBackRead+BUFREADCOMMENT>uMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) { uPosFound = uReadPos+(unsigned)i; break; } if (uPosFound!=CENTRALDIRINVALID) break; } free(buf); if (uPosFound == CENTRALDIRINVALID) return CENTRALDIRINVALID; /* Zip64 end of central directory locator */ if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) return CENTRALDIRINVALID; /* the signature, already checked */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return CENTRALDIRINVALID; /* number of the disk with the start of the zip64 end of central directory */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return CENTRALDIRINVALID; if (uL != 0) return CENTRALDIRINVALID; /* relative offset of the zip64 end of central directory record */ if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) return CENTRALDIRINVALID; /* total number of disks */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return CENTRALDIRINVALID; if (uL != 1) return CENTRALDIRINVALID; /* Goto end of central directory record */ if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) return CENTRALDIRINVALID; /* the signature */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return CENTRALDIRINVALID; if (uL != 0x06064b50) return CENTRALDIRINVALID; return relativeOffset; } /* Open a Zip file. path contain the full pathname (by example, on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer "zlib/zlib114.zip". If the zipfile cannot be opened (file doesn't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ local unzFile unzOpenInternal(const void *path, zlib_filefunc64_32_def* pzlib_filefunc64_32_def, int is64bitOpenFunction) { unz64_s us; unz64_s *s; ZPOS64_T central_pos; uLong uL; uLong number_disk; /* number of the current disk, used for spanning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the disk with central dir, used for spanning ZIP, unsupported, always 0*/ ZPOS64_T number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ int err=UNZ_OK; if (unz_copyright[0]!=' ') return NULL; us.z_filefunc.zseek32_file = NULL; us.z_filefunc.ztell32_file = NULL; if (pzlib_filefunc64_32_def==NULL) fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); else us.z_filefunc = *pzlib_filefunc64_32_def; us.is64bitOpenFunction = is64bitOpenFunction; us.filestream = ZOPEN64(us.z_filefunc, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); if (us.filestream==NULL) return NULL; central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); if (central_pos!=CENTRALDIRINVALID) { uLong uS; ZPOS64_T uL64; us.isZip64 = 1; if (ZSEEK64(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* size of zip64 end of central directory record */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) err=UNZ_ERRNO; /* version made by */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) err=UNZ_ERRNO; /* version needed to extract */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central directory on this disk */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central directory */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; /* size of the central directory */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; us.gi.size_comment = 0; } else { central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); if (central_pos==CENTRALDIRINVALID) err=UNZ_ERRNO; us.isZip64 = 0; if (ZSEEK64(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir on this disk */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.gi.number_entry = uL; /* total number of entries in the central dir */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; number_entry_CD = uL; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; /* size of the central directory */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.size_central_dir = uL; /* offset of start of central directory with respect to the starting disk number */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.offset_central_dir = uL; /* zipfile comment length */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; } if ((central_pos<us.offset_central_dir+us.size_central_dir) && (err==UNZ_OK)) err=UNZ_BADZIPFILE; if (err!=UNZ_OK) { ZCLOSE64(us.z_filefunc, us.filestream); return NULL; } us.byte_before_the_zipfile = central_pos - (us.offset_central_dir+us.size_central_dir); us.central_pos = central_pos; us.pfile_in_zip_read = NULL; us.encrypted = 0; s=(unz64_s*)ALLOC(sizeof(unz64_s)); if( s != NULL) { *s=us; unzGoToFirstFile((unzFile)s); } return (unzFile)s; } extern unzFile ZEXPORT unzOpen2(const char *path, zlib_filefunc_def* pzlib_filefunc32_def) { if (pzlib_filefunc32_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); return unzOpenInternal(path, &zlib_filefunc64_32_def_fill, 0); } else return unzOpenInternal(path, NULL, 0); } extern unzFile ZEXPORT unzOpen2_64(const void *path, zlib_filefunc64_def* pzlib_filefunc_def) { if (pzlib_filefunc_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; zlib_filefunc64_32_def_fill.ztell32_file = NULL; zlib_filefunc64_32_def_fill.zseek32_file = NULL; return unzOpenInternal(path, &zlib_filefunc64_32_def_fill, 1); } else return unzOpenInternal(path, NULL, 1); } extern unzFile ZEXPORT unzOpen(const char *path) { return unzOpenInternal(path, NULL, 0); } extern unzFile ZEXPORT unzOpen64(const void *path) { return unzOpenInternal(path, NULL, 1); } /* Close a ZipFile opened with unzOpen. If there is files inside the .Zip opened with unzOpenCurrentFile (see later), these files MUST be closed with unzCloseCurrentFile before call unzClose. return UNZ_OK if there is no problem. */ extern int ZEXPORT unzClose(unzFile file) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (s->pfile_in_zip_read!=NULL) unzCloseCurrentFile(file); ZCLOSE64(s->z_filefunc, s->filestream); free(s); return UNZ_OK; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64* pglobal_info) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; *pglobal_info=s->gi; return UNZ_OK; } extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info* pglobal_info32) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; /* to do : check if number_entry is not truncated */ pglobal_info32->number_entry = (uLong)s->gi.number_entry; pglobal_info32->size_comment = s->gi.size_comment; return UNZ_OK; } /* Translate date/time from Dos format to tm_unz (readable more easily) */ local void unz64local_DosDateToTmuDate(ZPOS64_T ulDosDate, tm_unz* ptm) { ZPOS64_T uDate; uDate = (ZPOS64_T)(ulDosDate>>16); ptm->tm_mday = (int)(uDate&0x1f) ; ptm->tm_mon = (int)((((uDate)&0x1E0)/0x20)-1) ; ptm->tm_year = (int)(((uDate&0x0FE00)/0x0200)+1980) ; ptm->tm_hour = (int) ((ulDosDate &0xF800)/0x800); ptm->tm_min = (int) ((ulDosDate&0x7E0)/0x20) ; ptm->tm_sec = (int) (2*(ulDosDate&0x1f)) ; } /* Get Info about the current file in the zipfile, with internal only info */ local int unz64local_GetCurrentFileInfoInternal(unzFile file, unz_file_info64 *pfile_info, unz_file_info64_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize) { unz64_s* s; unz_file_info64 file_info; unz_file_info64_internal file_info_internal; int err=UNZ_OK; uLong uMagic; long lSeek=0; uLong uL; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (ZSEEK64(s->z_filefunc, s->filestream, s->pos_in_central_dir+s->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* we check the magic */ if (err==UNZ_OK) { if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; } if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) err=UNZ_ERRNO; unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info.compressed_size = uL; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info.uncompressed_size = uL; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) err=UNZ_ERRNO; // relative offset of local header if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info_internal.offset_curfile = uL; lSeek+=file_info.size_filename; if ((err==UNZ_OK) && (szFileName!=NULL)) { uLong uSizeRead ; if (file_info.size_filename<fileNameBufferSize) { *(szFileName+file_info.size_filename)='\0'; uSizeRead = file_info.size_filename; } else uSizeRead = fileNameBufferSize; if ((file_info.size_filename>0) && (fileNameBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek -= uSizeRead; } // Read extrafield if ((err==UNZ_OK) && (extraField!=NULL)) { ZPOS64_T uSizeRead ; if (file_info.size_file_extra<extraFieldBufferSize) uSizeRead = file_info.size_file_extra; else uSizeRead = extraFieldBufferSize; if (lSeek!=0) { if (ZSEEK64(s->z_filefunc, s->filestream,(ZPOS64_T)lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek += file_info.size_file_extra - (uLong)uSizeRead; } else lSeek += file_info.size_file_extra; if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) { uLong acc = 0; // since lSeek now points to after the extra field we need to move back lSeek -= file_info.size_file_extra; if (lSeek!=0) { if (ZSEEK64(s->z_filefunc, s->filestream,(ZPOS64_T)lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } while(acc < file_info.size_file_extra) { uLong headerId; uLong dataSize; if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) err=UNZ_ERRNO; /* ZIP64 extra fields */ if (headerId == 0x0001) { if(file_info.uncompressed_size == MAXU32) { if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) err=UNZ_ERRNO; } if(file_info.compressed_size == MAXU32) { if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) err=UNZ_ERRNO; } if(file_info_internal.offset_curfile == MAXU32) { /* Relative Header offset */ if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) err=UNZ_ERRNO; } if(file_info.disk_num_start == 0xffff) { /* Disk Start Number */ if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; } } else { if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) err=UNZ_ERRNO; } acc += 2 + 2 + dataSize; } } if ((err==UNZ_OK) && (szComment!=NULL)) { uLong uSizeRead ; if (file_info.size_file_comment<commentBufferSize) { *(szComment+file_info.size_file_comment)='\0'; uSizeRead = file_info.size_file_comment; } else uSizeRead = commentBufferSize; if (lSeek!=0) { if (ZSEEK64(s->z_filefunc, s->filestream,(ZPOS64_T)lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek+=file_info.size_file_comment - uSizeRead; } else lSeek+=file_info.size_file_comment; if ((err==UNZ_OK) && (pfile_info!=NULL)) *pfile_info=file_info; if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) *pfile_info_internal=file_info_internal; return err; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 * pfile_info, char * szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char* szComment, uLong commentBufferSize) { return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); } extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info * pfile_info, char * szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char* szComment, uLong commentBufferSize) { int err; unz_file_info64 file_info64; err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); if ((err==UNZ_OK) && (pfile_info != NULL)) { pfile_info->version = file_info64.version; pfile_info->version_needed = file_info64.version_needed; pfile_info->flag = file_info64.flag; pfile_info->compression_method = file_info64.compression_method; pfile_info->dosDate = file_info64.dosDate; pfile_info->crc = file_info64.crc; pfile_info->size_filename = file_info64.size_filename; pfile_info->size_file_extra = file_info64.size_file_extra; pfile_info->size_file_comment = file_info64.size_file_comment; pfile_info->disk_num_start = file_info64.disk_num_start; pfile_info->internal_fa = file_info64.internal_fa; pfile_info->external_fa = file_info64.external_fa; pfile_info->tmu_date = file_info64.tmu_date; pfile_info->compressed_size = (uLong)file_info64.compressed_size; pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; } return err; } /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToFirstFile(unzFile file) { int err=UNZ_OK; unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; s->pos_in_central_dir=s->offset_central_dir; s->num_file=0; err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzGoToNextFile(unzFile file) { unz64_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ if (s->num_file+1==s->gi.number_entry) return UNZ_END_OF_LIST_OF_FILE; s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; s->num_file++; err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ extern int ZEXPORT unzLocateFile(unzFile file, const char *szFileName, int iCaseSensitivity) { unz64_s* s; int err; /* We remember the 'current' position in the file so that we can jump * back there if we fail. */ unz_file_info64 cur_file_infoSaved; unz_file_info64_internal cur_file_info_internalSaved; ZPOS64_T num_fileSaved; ZPOS64_T pos_in_central_dirSaved; if (file==NULL) return UNZ_PARAMERROR; if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; /* Save the current state */ num_fileSaved = s->num_file; pos_in_central_dirSaved = s->pos_in_central_dir; cur_file_infoSaved = s->cur_file_info; cur_file_info_internalSaved = s->cur_file_info_internal; err = unzGoToFirstFile(file); while (err == UNZ_OK) { char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; err = unzGetCurrentFileInfo64(file,NULL, szCurrentFileName,sizeof(szCurrentFileName)-1, NULL,0,NULL,0); if (err == UNZ_OK) { if (unzStringFileNameCompare(szCurrentFileName, szFileName,iCaseSensitivity)==0) return UNZ_OK; err = unzGoToNextFile(file); } } /* We failed, so restore the state of the 'current file' to where we * were. */ s->num_file = num_fileSaved ; s->pos_in_central_dir = pos_in_central_dirSaved ; s->cur_file_info = cur_file_infoSaved; s->cur_file_info_internal = cur_file_info_internalSaved; return err; } /* /////////////////////////////////////////// // Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) // I need random access // // Further optimization could be realized by adding an ability // to cache the directory in memory. The goal being a single // comprehensive file read to put the file I need in a memory. */ /* typedef struct unz_file_pos_s { ZPOS64_T pos_in_zip_directory; // offset in file ZPOS64_T num_of_file; // # of file } unz_file_pos; */ extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) { unz64_s* s; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; file_pos->pos_in_zip_directory = s->pos_in_central_dir; file_pos->num_of_file = s->num_file; return UNZ_OK; } extern int ZEXPORT unzGetFilePos(unzFile file, unz_file_pos* file_pos) { unz64_file_pos file_pos64; int err = unzGetFilePos64(file,&file_pos64); if (err==UNZ_OK) { file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; file_pos->num_of_file = (uLong)file_pos64.num_of_file; } return err; } extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) { unz64_s* s; int err; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; /* jump to the right spot */ s->pos_in_central_dir = file_pos->pos_in_zip_directory; s->num_file = file_pos->num_of_file; /* set the current file */ err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); /* return results */ s->current_file_ok = (err == UNZ_OK); return err; } extern int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos* file_pos) { unz64_file_pos file_pos64; if (file_pos == NULL) return UNZ_PARAMERROR; file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; file_pos64.num_of_file = file_pos->num_of_file; return unzGoToFilePos64(file,&file_pos64); } /* // Unzip Helper Functions - should be here? /////////////////////////////////////////// */ /* Read the local header of the current zipfile Check the coherency of the local header and info in the end of central directory about this file store in *piSizeVar the size of extra info in local header (filename and size of extra field data) */ local int unz64local_CheckCurrentFileCoherencyHeader(unz64_s* s, uInt* piSizeVar, ZPOS64_T * poffset_local_extrafield, uInt * psize_local_extrafield) { uLong uMagic,uData,uFlags; uLong size_filename; uLong size_extra_field; int err=UNZ_OK; *piSizeVar = 0; *poffset_local_extrafield = 0; *psize_local_extrafield = 0; if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (err==UNZ_OK) { if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; } if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; /* else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) err=UNZ_BADZIPFILE; */ if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) err=UNZ_BADZIPFILE; if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && /* #ifdef HAVE_BZIP2 */ (s->cur_file_info.compression_method!=Z_BZIP2ED) && /* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ err=UNZ_ERRNO; else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ err=UNZ_ERRNO; else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) err=UNZ_BADZIPFILE; *piSizeVar += (uInt)size_filename; if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) err=UNZ_ERRNO; *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename; *psize_local_extrafield = (uInt)size_extra_field; *piSizeVar += (uInt)size_extra_field; return err; } /* Open for reading data the current file in the zipfile. If there is no error and the file is opened, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int* method, int* level, int raw, const char* password) { int err=UNZ_OK; uInt iSizeVar; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ uInt size_local_extrafield; /* size of the local extra field */ # ifndef NOUNCRYPT char source[12]; # else if (password != NULL) return UNZ_PARAMERROR; # endif if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return UNZ_PARAMERROR; if (s->pfile_in_zip_read != NULL) unzCloseCurrentFile(file); if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) return UNZ_BADZIPFILE; pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); if (pfile_in_zip_read_info==NULL) return UNZ_INTERNALERROR; pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; pfile_in_zip_read_info->pos_local_extrafield=0; pfile_in_zip_read_info->raw=raw; if (pfile_in_zip_read_info->read_buffer==NULL) { free(pfile_in_zip_read_info); return UNZ_INTERNALERROR; } pfile_in_zip_read_info->stream_initialised=0; if (method!=NULL) *method = (int)s->cur_file_info.compression_method; if (level!=NULL) { *level = 6; switch (s->cur_file_info.flag & 0x06) { case 6 : *level = 1; break; case 4 : *level = 2; break; case 2 : *level = 9; break; } } if ((s->cur_file_info.compression_method!=0) && /* #ifdef HAVE_BZIP2 */ (s->cur_file_info.compression_method!=Z_BZIP2ED) && /* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; pfile_in_zip_read_info->crc32=0; pfile_in_zip_read_info->total_out_64=0; pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; pfile_in_zip_read_info->filestream=s->filestream; pfile_in_zip_read_info->z_filefunc=s->z_filefunc; pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; pfile_in_zip_read_info->stream.total_out = 0; if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) { #ifdef HAVE_BZIP2 pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; pfile_in_zip_read_info->bstream.bzfree = (free_func)0; pfile_in_zip_read_info->bstream.opaque = (voidpf)0; pfile_in_zip_read_info->bstream.state = (voidpf)0; pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; pfile_in_zip_read_info->stream.next_in = (voidpf)0; pfile_in_zip_read_info->stream.avail_in = 0; err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; else { free(pfile_in_zip_read_info->read_buffer); free(pfile_in_zip_read_info); return err; } #else pfile_in_zip_read_info->raw=1; #endif } else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) { pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; pfile_in_zip_read_info->stream.next_in = 0; pfile_in_zip_read_info->stream.avail_in = 0; err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; else { free(pfile_in_zip_read_info->read_buffer); free(pfile_in_zip_read_info); return err; } /* windowBits is passed < 0 to tell that there is no zlib header. * Note that in this case inflate *requires* an extra "dummy" byte * after the compressed stream in order to complete decompression and * return Z_STREAM_END. * In unzip, i don't wait absolutely Z_STREAM_END because I known the * size of both compressed and uncompressed data */ } pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size ; pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size ; pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + iSizeVar; pfile_in_zip_read_info->stream.avail_in = (uInt)0; s->pfile_in_zip_read = pfile_in_zip_read_info; s->encrypted = 0; # ifndef NOUNCRYPT if (password != NULL) { int i; s->pcrc_32_tab = get_crc_table(); init_keys(password,s->keys,s->pcrc_32_tab); if (ZSEEK64(s->z_filefunc, s->filestream, s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile, SEEK_SET)!=0) return UNZ_INTERNALERROR; if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) return UNZ_INTERNALERROR; for (i = 0; i<12; i++) zdecode(s->keys,s->pcrc_32_tab,source[i]); s->pfile_in_zip_read->pos_in_zipfile+=12; s->encrypted=1; } # endif return UNZ_OK; } extern int ZEXPORT unzOpenCurrentFile(unzFile file) { return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); } extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char* password) { return unzOpenCurrentFile3(file, NULL, NULL, 0, password); } extern int ZEXPORT unzOpenCurrentFile2(unzFile file, int* method, int* level, int raw) { return unzOpenCurrentFile3(file, method, level, raw, NULL); } /** Addition for GDAL : START */ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; s=(unz64_s*)file; if (file==NULL) return 0; //UNZ_PARAMERROR; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return 0; //UNZ_PARAMERROR; return pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile; } /** Addition for GDAL : END */ /* Read bytes from the current file. buf contain buffer where data must be copied len the size of buf. return the number of byte copied if some bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, unsigned len) { int err=UNZ_OK; uInt iRead = 0; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->read_buffer == NULL) return UNZ_END_OF_LIST_OF_FILE; if (len==0) return 0; pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; pfile_in_zip_read_info->stream.avail_out = (uInt)len; if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && (!(pfile_in_zip_read_info->raw))) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; if ((len>pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in) && (pfile_in_zip_read_info->raw)) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in; while (pfile_in_zip_read_info->stream.avail_out>0) { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) { uInt uReadThis = UNZ_BUFSIZE; if (pfile_in_zip_read_info->rest_read_compressed<uReadThis) uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed; if (uReadThis == 0) return UNZ_EOF; if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->read_buffer, uReadThis)!=uReadThis) return UNZ_ERRNO; # ifndef NOUNCRYPT if(s->encrypted) { uInt i; for(i=0;i<uReadThis;i++) pfile_in_zip_read_info->read_buffer[i] = zdecode(s->keys,s->pcrc_32_tab, pfile_in_zip_read_info->read_buffer[i]); } # endif pfile_in_zip_read_info->pos_in_zipfile += uReadThis; pfile_in_zip_read_info->rest_read_compressed-=uReadThis; pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->read_buffer; pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; } if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) { uInt uDoCopy,i ; if ((pfile_in_zip_read_info->stream.avail_in == 0) && (pfile_in_zip_read_info->rest_read_compressed == 0)) return (iRead==0) ? UNZ_EOF : (int)iRead; if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) uDoCopy = pfile_in_zip_read_info->stream.avail_out ; else uDoCopy = pfile_in_zip_read_info->stream.avail_in ; for (i=0;i<uDoCopy;i++) *(pfile_in_zip_read_info->stream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, pfile_in_zip_read_info->stream.next_out, uDoCopy); pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; pfile_in_zip_read_info->stream.avail_in -= uDoCopy; pfile_in_zip_read_info->stream.avail_out -= uDoCopy; pfile_in_zip_read_info->stream.next_out += uDoCopy; pfile_in_zip_read_info->stream.next_in += uDoCopy; pfile_in_zip_read_info->stream.total_out += uDoCopy; iRead += uDoCopy; } else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) { #ifdef HAVE_BZIP2 uLong uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; uLong uOutThis; pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; pfile_in_zip_read_info->bstream.total_in_hi32 = 0; pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; pfile_in_zip_read_info->bstream.total_out_hi32 = 0; uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; if (err==BZ_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; if (err!=BZ_OK) break; #endif } // end Z_BZIP2ED else { ZPOS64_T uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; ZPOS64_T uOutThis; int flush=Z_SYNC_FLUSH; uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; bufBefore = pfile_in_zip_read_info->stream.next_out; /* if ((pfile_in_zip_read_info->rest_read_uncompressed == pfile_in_zip_read_info->stream.avail_out) && (pfile_in_zip_read_info->rest_read_compressed == 0)) flush = Z_FINISH; */ err=inflate(&pfile_in_zip_read_info->stream,flush); if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) err = Z_DATA_ERROR; uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; /* Detect overflow, because z_stream.total_out is uLong (32 bits) */ if (uTotalOutAfter<uTotalOutBefore) uTotalOutAfter += 1LL << 32; /* Add maximum value of uLong + 1 */ uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : (int)iRead; if (err!=Z_OK) break; } } if (err==Z_OK) return (int)iRead; return err; } /* Give the current position in uncompressed data */ extern z_off_t ZEXPORT unztell(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; return (z_off_t)pfile_in_zip_read_info->stream.total_out; } extern ZPOS64_T ZEXPORT unztell64(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return (ZPOS64_T)-1; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return (ZPOS64_T)-1; return pfile_in_zip_read_info->total_out_64; } /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzeof(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->rest_read_uncompressed == 0) return 1; else return 0; } /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field that can be read if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ extern int ZEXPORT unzGetLocalExtrafield(unzFile file, voidp buf, unsigned len) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; uInt read_now; ZPOS64_T size_to_read; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; size_to_read = (pfile_in_zip_read_info->size_local_extrafield - pfile_in_zip_read_info->pos_local_extrafield); if (buf==NULL) return (int)size_to_read; if (len>size_to_read) read_now = (uInt)size_to_read; else read_now = (uInt)len ; if (read_now==0) return 0; if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD64(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, buf,read_now)!=read_now) return UNZ_ERRNO; return (int)read_now; } /* Close the file in zip opened with unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzCloseCurrentFile(unzFile file) { int err=UNZ_OK; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && (!pfile_in_zip_read_info->raw)) { if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) err=UNZ_CRCERROR; } free(pfile_in_zip_read_info->read_buffer); pfile_in_zip_read_info->read_buffer = NULL; if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) inflateEnd(&pfile_in_zip_read_info->stream); #ifdef HAVE_BZIP2 else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); #endif pfile_in_zip_read_info->stream_initialised = 0; free(pfile_in_zip_read_info); s->pfile_in_zip_read=NULL; return err; } /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ extern int ZEXPORT unzGetGlobalComment(unzFile file, char * szComment, uLong uSizeBuf) { unz64_s* s; uLong uReadThis ; if (file==NULL) return (int)UNZ_PARAMERROR; s=(unz64_s*)file; uReadThis = uSizeBuf; if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (uReadThis>0) { *szComment='\0'; if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) return UNZ_ERRNO; } if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; return (int)uReadThis; } /* Additions by RX '2004 */ extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) { unz64_s* s; if (file==NULL) return 0; //UNZ_PARAMERROR; s=(unz64_s*)file; if (!s->current_file_ok) return 0; if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) if (s->num_file==s->gi.number_entry) return 0; return s->pos_in_central_dir; } extern uLong ZEXPORT unzGetOffset(unzFile file) { ZPOS64_T offset64; if (file==NULL) return 0; //UNZ_PARAMERROR; offset64 = unzGetOffset64(file); return (uLong)offset64; } extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) { unz64_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; s->pos_in_central_dir = pos; s->num_file = s->gi.number_entry; /* hack */ err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) { return unzSetOffset64(file,pos); } |
Added compat/zlib/contrib/minizip/unzip.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* unzip.h -- IO for uncompress .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications of Unzip for Zip64 Copyright (C) 2007-2008 Even Rouault Modifications for Zip64 support on both zip and unzip Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt --------------------------------------------------------------------------------- Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. --------------------------------------------------------------------------------- Changes See header of unzip64.c */ #ifndef _unz64_H #define _unz64_H #ifdef __cplusplus extern "C" { #endif #ifndef _ZLIB_H #include "zlib.h" #endif #ifndef _ZLIBIOAPI_H #include "ioapi.h" #endif #ifdef HAVE_BZIP2 #include "bzlib.h" #endif #define Z_BZIP2ED 12 #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagunzFile__ { int unused; } unzFile__; typedef unzFile__ *unzFile; #else typedef voidp unzFile; #endif #define UNZ_OK (0) #define UNZ_END_OF_LIST_OF_FILE (-100) #define UNZ_ERRNO (Z_ERRNO) #define UNZ_EOF (0) #define UNZ_PARAMERROR (-102) #define UNZ_BADZIPFILE (-103) #define UNZ_INTERNALERROR (-104) #define UNZ_CRCERROR (-105) /* tm_unz contain date/time info */ typedef struct tm_unz_s { int tm_sec; /* seconds after the minute - [0,59] */ int tm_min; /* minutes after the hour - [0,59] */ int tm_hour; /* hours since midnight - [0,23] */ int tm_mday; /* day of the month - [1,31] */ int tm_mon; /* months since January - [0,11] */ int tm_year; /* years - [1980..2044] */ } tm_unz; /* unz_global_info structure contain global data about the ZIPfile These data comes from the end of central dir */ typedef struct unz_global_info64_s { ZPOS64_T number_entry; /* total number of entries in the central dir on this disk */ uLong size_comment; /* size of the global comment of the zipfile */ } unz_global_info64; typedef struct unz_global_info_s { uLong number_entry; /* total number of entries in the central dir on this disk */ uLong size_comment; /* size of the global comment of the zipfile */ } unz_global_info; /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_info64_s { uLong version; /* version made by 2 bytes */ uLong version_needed; /* version needed to extract 2 bytes */ uLong flag; /* general purpose bit flag 2 bytes */ uLong compression_method; /* compression method 2 bytes */ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ uLong crc; /* crc-32 4 bytes */ ZPOS64_T compressed_size; /* compressed size 8 bytes */ ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ uLong size_filename; /* filename length 2 bytes */ uLong size_file_extra; /* extra field length 2 bytes */ uLong size_file_comment; /* file comment length 2 bytes */ uLong disk_num_start; /* disk number start 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ tm_unz tmu_date; } unz_file_info64; typedef struct unz_file_info_s { uLong version; /* version made by 2 bytes */ uLong version_needed; /* version needed to extract 2 bytes */ uLong flag; /* general purpose bit flag 2 bytes */ uLong compression_method; /* compression method 2 bytes */ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ uLong crc; /* crc-32 4 bytes */ uLong compressed_size; /* compressed size 4 bytes */ uLong uncompressed_size; /* uncompressed size 4 bytes */ uLong size_filename; /* filename length 2 bytes */ uLong size_file_extra; /* extra field length 2 bytes */ uLong size_file_comment; /* file comment length 2 bytes */ uLong disk_num_start; /* disk number start 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ tm_unz tmu_date; } unz_file_info; extern int ZEXPORT unzStringFileNameCompare(const char* fileName1, const char* fileName2, int iCaseSensitivity); /* Compare two filenames (fileName1,fileName2). If iCaseSensitivity = 1, comparison is case sensitive (like strcmp) If iCaseSensitivity = 2, comparison is not case sensitive (like strcmpi or strcasecmp) If iCaseSensitivity = 0, case sensitivity is default of your operating system (like 1 on Unix, 2 on Windows) */ extern unzFile ZEXPORT unzOpen(const char *path); extern unzFile ZEXPORT unzOpen64(const void *path); /* Open a Zip file. path contain the full pathname (by example, on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". If the zipfile cannot be opened (file don't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. the "64" function take a const void* pointer, because the path is just the value passed to the open64_file_func callback. Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* does not describe the reality */ extern unzFile ZEXPORT unzOpen2(const char *path, zlib_filefunc_def* pzlib_filefunc_def); /* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ extern unzFile ZEXPORT unzOpen2_64(const void *path, zlib_filefunc64_def* pzlib_filefunc_def); /* Open a Zip file, like unz64Open, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ extern int ZEXPORT unzClose(unzFile file); /* Close a ZipFile opened with unzOpen. If there is files inside the .Zip opened with unzOpenCurrentFile (see later), these files MUST be closed with unzCloseCurrentFile before call unzClose. return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info); extern int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64 *pglobal_info); /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalComment(unzFile file, char *szComment, uLong uSizeBuf); /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ /***************************************************************************/ /* Unzip package allow you browse the directory of the zipfile */ extern int ZEXPORT unzGoToFirstFile(unzFile file); /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToNextFile(unzFile file); /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzLocateFile(unzFile file, const char *szFileName, int iCaseSensitivity); /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ /* ****************************************** */ /* Ryan supplied functions */ /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_pos_s { uLong pos_in_zip_directory; /* offset in zip file directory */ uLong num_of_file; /* # of file */ } unz_file_pos; extern int ZEXPORT unzGetFilePos( unzFile file, unz_file_pos* file_pos); extern int ZEXPORT unzGoToFilePos( unzFile file, unz_file_pos* file_pos); typedef struct unz64_file_pos_s { ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ ZPOS64_T num_of_file; /* # of file */ } unz64_file_pos; extern int ZEXPORT unzGetFilePos64( unzFile file, unz64_file_pos* file_pos); extern int ZEXPORT unzGoToFilePos64( unzFile file, const unz64_file_pos* file_pos); /* ****************************************** */ extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize); extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize); /* Get Info about the current file if pfile_info!=NULL, the *pfile_info structure will contain some info about the current file if szFileName!=NULL, the filename string will be copied in szFileName (fileNameBufferSize is the size of the buffer) if extraField!=NULL, the extra field information will be copied in extraField (extraFieldBufferSize is the size of the buffer). This is the Central-header version of the extra field if szComment!=NULL, the comment string of the file will be copied in szComment (commentBufferSize is the size of the buffer) */ /** Addition for GDAL : START */ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64(unzFile file); /** Addition for GDAL : END */ /***************************************************************************/ /* for reading the content of the current zipfile, you can open it, read data from it, and close it (you can close it before reading all the file) */ extern int ZEXPORT unzOpenCurrentFile(unzFile file); /* Open for reading data the current file in the zipfile. If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char* password); /* Open for reading data the current file in the zipfile. password is a crypting password If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile2(unzFile file, int* method, int* level, int raw); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int* method, int* level, int raw, const char* password); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzCloseCurrentFile(unzFile file); /* Close the file in zip opened with unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, unsigned len); /* Read bytes from the current file (opened by unzOpenCurrentFile) buf contain buffer where data must be copied len the size of buf. return the number of byte copied if some bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern z_off_t ZEXPORT unztell(unzFile file); extern ZPOS64_T ZEXPORT unztell64(unzFile file); /* Give the current position in uncompressed data */ extern int ZEXPORT unzeof(unzFile file); /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzGetLocalExtrafield(unzFile file, voidp buf, unsigned len); /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ /***************************************************************************/ /* Get the current file offset */ extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); extern uLong ZEXPORT unzGetOffset (unzFile file); /* Set the current file offset */ extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); #ifdef __cplusplus } #endif #endif /* _unz64_H */ |
Added compat/zlib/contrib/minizip/zip.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 | /* zip.c -- IO on .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt Changes Oct-2009 - Mathias Svensson - Remove old C style function prototypes Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data It is used when recreating zip archive with RAW when deleting items from a zip. ZIP64 data is automatically added to items that needs it, and existing ZIP64 data need to be removed. Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <time.h> #include "zlib.h" #include "zip.h" #ifdef STDC # include <stddef.h> #endif #ifdef NO_ERRNO_H extern int errno; #else # include <errno.h> #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef VERSIONMADEBY # define VERSIONMADEBY (0x0) /* platform dependent */ #endif #ifndef Z_BUFSIZE #define Z_BUFSIZE (64*1024) //(16384) #endif #ifndef Z_MAXFILENAMEINZIP #define Z_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif /* #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) */ /* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ // NOT sure that this work on ALL platform #define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_END #define SEEK_END 2 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef DEF_MEM_LEVEL #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif #endif const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; #define SIZEDATA_INDATABLOCK (4096-(4*4)) #define LOCALHEADERMAGIC (0x04034b50) #define CENTRALHEADERMAGIC (0x02014b50) #define ENDHEADERMAGIC (0x06054b50) #define ZIP64ENDHEADERMAGIC (0x6064b50) #define ZIP64ENDLOCHEADERMAGIC (0x7064b50) #define FLAG_LOCALHEADER_OFFSET (0x06) #define CRC_LOCALHEADER_OFFSET (0x0e) #define SIZECENTRALHEADER (0x2e) /* 46 */ typedef struct linkedlist_datablock_internal_s { struct linkedlist_datablock_internal_s* next_datablock; uLong avail_in_this_block; uLong filled_in_this_block; uLong unused; /* for future use and alignment */ unsigned char data[SIZEDATA_INDATABLOCK]; } linkedlist_datablock_internal; typedef struct linkedlist_data_s { linkedlist_datablock_internal* first_block; linkedlist_datablock_internal* last_block; } linkedlist_data; typedef struct { z_stream stream; /* zLib stream structure for inflate */ #ifdef HAVE_BZIP2 bz_stream bstream; /* bzLib stream structure for bziped */ #endif int stream_initialised; /* 1 is stream is initialised */ uInt pos_in_buffered_data; /* last written byte in buffered_data */ ZPOS64_T pos_local_header; /* offset of the local header of the file currently writing */ char* central_header; /* central header data for the current file */ uLong size_centralExtra; uLong size_centralheader; /* size of the central header for cur file */ uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ uLong flag; /* flag of the file currently writing */ int method; /* compression method of file currently wr.*/ int raw; /* 1 for directly writing raw data */ Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ uLong dosDate; uLong crc32; int encrypt; int zip64; /* Add ZIP64 extended information in the extra field */ ZPOS64_T pos_zip64extrainfo; ZPOS64_T totalCompressedData; ZPOS64_T totalUncompressedData; #ifndef NOCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const z_crc_t* pcrc_32_tab; unsigned crypt_header_size; #endif } curfile64_info; typedef struct { zlib_filefunc64_32_def z_filefunc; voidpf filestream; /* io structure of the zipfile */ linkedlist_data central_dir;/* datablock with central dir in construction*/ int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ curfile64_info ci; /* info on the file currently writing */ ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ ZPOS64_T add_position_when_writing_offset; ZPOS64_T number_entry; #ifndef NO_ADDFILEINEXISTINGZIP char *globalcomment; #endif } zip64_internal; #ifndef NOCRYPT #define INCLUDECRYPTINGCODE_IFCRYPTALLOWED #include "crypt.h" #endif local linkedlist_datablock_internal* allocate_new_datablock(void) { linkedlist_datablock_internal* ldi; ldi = (linkedlist_datablock_internal*) ALLOC(sizeof(linkedlist_datablock_internal)); if (ldi!=NULL) { ldi->next_datablock = NULL ; ldi->filled_in_this_block = 0 ; ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; } return ldi; } local void free_datablock(linkedlist_datablock_internal* ldi) { while (ldi!=NULL) { linkedlist_datablock_internal* ldinext = ldi->next_datablock; free(ldi); ldi = ldinext; } } local void init_linkedlist(linkedlist_data* ll) { ll->first_block = ll->last_block = NULL; } local void free_linkedlist(linkedlist_data* ll) { free_datablock(ll->first_block); ll->first_block = ll->last_block = NULL; } local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) { linkedlist_datablock_internal* ldi; const unsigned char* from_copy; if (ll==NULL) return ZIP_INTERNALERROR; if (ll->last_block == NULL) { ll->first_block = ll->last_block = allocate_new_datablock(); if (ll->first_block == NULL) return ZIP_INTERNALERROR; } ldi = ll->last_block; from_copy = (const unsigned char*)buf; while (len>0) { uInt copy_this; uInt i; unsigned char* to_copy; if (ldi->avail_in_this_block==0) { ldi->next_datablock = allocate_new_datablock(); if (ldi->next_datablock == NULL) return ZIP_INTERNALERROR; ldi = ldi->next_datablock ; ll->last_block = ldi; } if (ldi->avail_in_this_block < len) copy_this = (uInt)ldi->avail_in_this_block; else copy_this = (uInt)len; to_copy = &(ldi->data[ldi->filled_in_this_block]); for (i=0;i<copy_this;i++) *(to_copy+i)=*(from_copy+i); ldi->filled_in_this_block += copy_this; ldi->avail_in_this_block -= copy_this; from_copy += copy_this ; len -= copy_this; } return ZIP_OK; } /****************************************************************************/ #ifndef NO_ADDFILEINEXISTINGZIP /* =========================================================================== Inputs a long in LSB order to the given file nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) */ local int zip64local_putValue(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) { unsigned char buf[8]; int n; for (n = 0; n < nbByte; n++) { buf[n] = (unsigned char)(x & 0xff); x >>= 8; } if (x != 0) { /* data overflow - hack for ZIP64 (X Roche) */ for (n = 0; n < nbByte; n++) { buf[n] = 0xff; } } if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,(uLong)nbByte)!=(uLong)nbByte) return ZIP_ERRNO; else return ZIP_OK; } local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) { unsigned char* buf=(unsigned char*)dest; int n; for (n = 0; n < nbByte; n++) { buf[n] = (unsigned char)(x & 0xff); x >>= 8; } if (x != 0) { /* data overflow - hack for ZIP64 */ for (n = 0; n < nbByte; n++) { buf[n] = 0xff; } } } /****************************************************************************/ local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) { uLong year = (uLong)ptm->tm_year; if (year>=1980) year-=1980; else if (year>=80) year-=80; return (uLong) (((uLong)(ptm->tm_mday) + (32 * (uLong)(ptm->tm_mon+1)) + (512 * year)) << 16) | (((uLong)ptm->tm_sec/2) + (32 * (uLong)ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); } /****************************************************************************/ local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int* pi) { unsigned char c; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); if (err==1) { *pi = (int)c; return ZIP_OK; } else { if (ZERROR64(*pzlib_filefunc_def,filestream)) return ZIP_ERRNO; else return ZIP_EOF; } } /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int zip64local_getShort(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) { uLong x ; int i = 0; int err; err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } local int zip64local_getLong(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) { uLong x ; int i = 0; int err; err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<16; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<24; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } local int zip64local_getLong64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) { ZPOS64_T x; int i = 0; int err; err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x = (ZPOS64_T)i; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<8; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<16; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<24; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<32; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<40; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<48; if (err==ZIP_OK) err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); x += ((ZPOS64_T)i)<<56; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } #ifndef BUFREADCOMMENT #define BUFREADCOMMENT (0x400) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=0; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackRead<uMaxBack) { uLong uReadSize; ZPOS64_T uReadPos ; int i; if (uBackRead+BUFREADCOMMENT>uMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+(unsigned)i; break; } if (uPosFound!=0) break; } free(buf); return uPosFound; } /* Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before the global comment) */ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=0; uLong uL; ZPOS64_T relativeOffset; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackRead<uMaxBack) { uLong uReadSize; ZPOS64_T uReadPos; int i; if (uBackRead+BUFREADCOMMENT>uMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) { // Signature "0x07064b50" Zip64 end of central directory locater if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) { uPosFound = uReadPos+(unsigned)i; break; } } if (uPosFound!=0) break; } free(buf); if (uPosFound == 0) return 0; /* Zip64 end of central directory locator */ if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) return 0; /* the signature, already checked */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; /* number of the disk with the start of the zip64 end of central directory */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 0) return 0; /* relative offset of the zip64 end of central directory record */ if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) return 0; /* total number of disks */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 1) return 0; /* Goto Zip64 end of central directory record */ if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) return 0; /* the signature */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' return 0; return relativeOffset; } local int LoadCentralDirectoryRecord(zip64_internal* pziinit) { int err=ZIP_OK; ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ ZPOS64_T size_central_dir; /* size of the central directory */ ZPOS64_T offset_central_dir; /* offset of start of central directory */ ZPOS64_T central_pos; uLong uL; uLong number_disk; /* number of the current disk, used for spanning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number of the disk with central dir, used for spanning ZIP, unsupported, always 0*/ ZPOS64_T number_entry; ZPOS64_T number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ uLong VersionMadeBy; uLong VersionNeeded; uLong size_comment; int hasZIP64Record = 0; // check first if we find a ZIP64 record central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); if(central_pos > 0) { hasZIP64Record = 1; } else if(central_pos == 0) { central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); } /* disable to allow appending to empty ZIP archive if (central_pos==0) err=ZIP_ERRNO; */ if(hasZIP64Record) { ZPOS64_T sizeEndOfCentralDirectory; if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) err=ZIP_ERRNO; /* the signature, already checked */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) err=ZIP_ERRNO; /* size of zip64 end of central directory record */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) err=ZIP_ERRNO; /* version made by */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) err=ZIP_ERRNO; /* version needed to extract */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) err=ZIP_ERRNO; /* number of this disk */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) err=ZIP_ERRNO; /* number of the disk with the start of the central directory */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central directory on this disk */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central directory */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) err=ZIP_ERRNO; if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=ZIP_BADZIPFILE; /* size of the central directory */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) err=ZIP_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) err=ZIP_ERRNO; // TODO.. // read the comment from the standard central header. size_comment = 0; } else { // Read End of central Directory info if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=ZIP_ERRNO; /* the signature, already checked */ if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) err=ZIP_ERRNO; /* number of this disk */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) err=ZIP_ERRNO; /* number of the disk with the start of the central directory */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central dir on this disk */ number_entry = 0; if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else number_entry = uL; /* total number of entries in the central dir */ number_entry_CD = 0; if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else number_entry_CD = uL; if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=ZIP_BADZIPFILE; /* size of the central directory */ size_central_dir = 0; if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else size_central_dir = uL; /* offset of start of central directory with respect to the starting disk number */ offset_central_dir = 0; if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) err=ZIP_ERRNO; else offset_central_dir = uL; /* zipfile global comment length */ if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) err=ZIP_ERRNO; } if ((central_pos<offset_central_dir+size_central_dir) && (err==ZIP_OK)) err=ZIP_BADZIPFILE; if (err!=ZIP_OK) { ZCLOSE64(pziinit->z_filefunc, pziinit->filestream); return ZIP_ERRNO; } if (size_comment>0) { pziinit->globalcomment = (char*)ALLOC(size_comment+1); if (pziinit->globalcomment) { size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); pziinit->globalcomment[size_comment]=0; } } byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); pziinit->add_position_when_writing_offset = byte_before_the_zipfile; { ZPOS64_T size_central_dir_to_read = size_central_dir; size_t buf_size = SIZEDATA_INDATABLOCK; void* buf_read = (void*)ALLOC(buf_size); if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) err=ZIP_ERRNO; while ((size_central_dir_to_read>0) && (err==ZIP_OK)) { ZPOS64_T read_this = SIZEDATA_INDATABLOCK; if (read_this > size_central_dir_to_read) read_this = size_central_dir_to_read; if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) err=ZIP_ERRNO; if (err==ZIP_OK) err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); size_central_dir_to_read-=read_this; } free(buf_read); } pziinit->begin_pos = byte_before_the_zipfile; pziinit->number_entry = number_entry_CD; if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) err=ZIP_ERRNO; return err; } #endif /* !NO_ADDFILEINEXISTINGZIP*/ /************************************************************/ extern zipFile ZEXPORT zipOpen3(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) { zip64_internal ziinit; zip64_internal* zi; int err=ZIP_OK; ziinit.z_filefunc.zseek32_file = NULL; ziinit.z_filefunc.ztell32_file = NULL; if (pzlib_filefunc64_32_def==NULL) fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); else ziinit.z_filefunc = *pzlib_filefunc64_32_def; ziinit.filestream = ZOPEN64(ziinit.z_filefunc, pathname, (append == APPEND_STATUS_CREATE) ? (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); if (ziinit.filestream == NULL) return NULL; if (append == APPEND_STATUS_CREATEAFTER) ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); ziinit.in_opened_file_inzip = 0; ziinit.ci.stream_initialised = 0; ziinit.number_entry = 0; ziinit.add_position_when_writing_offset = 0; init_linkedlist(&(ziinit.central_dir)); zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); if (zi==NULL) { ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); return NULL; } /* now we add file in a zipfile */ # ifndef NO_ADDFILEINEXISTINGZIP ziinit.globalcomment = NULL; if (append == APPEND_STATUS_ADDINZIP) { // Read and Cache Central Directory Records err = LoadCentralDirectoryRecord(&ziinit); } if (globalcomment) { *globalcomment = ziinit.globalcomment; } # endif /* !NO_ADDFILEINEXISTINGZIP*/ if (err != ZIP_OK) { # ifndef NO_ADDFILEINEXISTINGZIP free(ziinit.globalcomment); # endif /* !NO_ADDFILEINEXISTINGZIP*/ free(zi); return NULL; } else { *zi = ziinit; return (zipFile)zi; } } extern zipFile ZEXPORT zipOpen2(const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) { if (pzlib_filefunc32_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); } else return zipOpen3(pathname, append, globalcomment, NULL); } extern zipFile ZEXPORT zipOpen2_64(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) { if (pzlib_filefunc_def != NULL) { zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; zlib_filefunc64_32_def_fill.ztell32_file = NULL; zlib_filefunc64_32_def_fill.zseek32_file = NULL; return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); } else return zipOpen3(pathname, append, globalcomment, NULL); } extern zipFile ZEXPORT zipOpen(const char* pathname, int append) { return zipOpen3((const void*)pathname,append,NULL,NULL); } extern zipFile ZEXPORT zipOpen64(const void* pathname, int append) { return zipOpen3(pathname,append,NULL,NULL); } local int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) { /* write the local header */ int err; uInt size_filename = (uInt)strlen(filename); uInt size_extrafield = size_extrafield_local; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); if (err==ZIP_OK) { if(zi->ci.zip64) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ } if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ if (err==ZIP_OK) { if(zi->ci.zip64) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ } if (err==ZIP_OK) { if(zi->ci.zip64) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ } if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); if(zi->ci.zip64) { size_extrafield += 20; } if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); if ((err==ZIP_OK) && (size_filename > 0)) { if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) err = ZIP_ERRNO; } if ((err==ZIP_OK) && (size_extrafield_local > 0)) { if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) err = ZIP_ERRNO; } if ((err==ZIP_OK) && (zi->ci.zip64)) { // write the Zip64 extended info short HeaderID = 1; short DataSize = 16; ZPOS64_T CompressedSize = 0; ZPOS64_T UncompressedSize = 0; // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)HeaderID,2); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)DataSize,2); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); } return err; } /* NOTE. When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped before calling this function it can be done with zipRemoveExtraInfoBlock It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize unnecessary allocations. */ extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase, int zip64) { zip64_internal* zi; uInt size_filename; uInt size_comment; uInt i; int err = ZIP_OK; # ifdef NOCRYPT (crcForCrypting); if (password != NULL) return ZIP_PARAMERROR; # endif if (file == NULL) return ZIP_PARAMERROR; #ifdef HAVE_BZIP2 if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) return ZIP_PARAMERROR; #else if ((method!=0) && (method!=Z_DEFLATED)) return ZIP_PARAMERROR; #endif // The filename and comment length must fit in 16 bits. if ((filename!=NULL) && (strlen(filename)>0xffff)) return ZIP_PARAMERROR; if ((comment!=NULL) && (strlen(comment)>0xffff)) return ZIP_PARAMERROR; // The extra field length must fit in 16 bits. If the member also requires // a Zip64 extra block, that will also need to fit within that 16-bit // length, but that will be checked for later. if ((size_extrafield_local>0xffff) || (size_extrafield_global>0xffff)) return ZIP_PARAMERROR; zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 1) { err = zipCloseFileInZip (file); if (err != ZIP_OK) return err; } if (filename==NULL) filename="-"; if (comment==NULL) size_comment = 0; else size_comment = (uInt)strlen(comment); size_filename = (uInt)strlen(filename); if (zipfi == NULL) zi->ci.dosDate = 0; else { if (zipfi->dosDate != 0) zi->ci.dosDate = zipfi->dosDate; else zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); } zi->ci.flag = flagBase; if ((level==8) || (level==9)) zi->ci.flag |= 2; if (level==2) zi->ci.flag |= 4; if (level==1) zi->ci.flag |= 6; if (password != NULL) zi->ci.flag |= 1; zi->ci.crc32 = 0; zi->ci.method = method; zi->ci.encrypt = 0; zi->ci.stream_initialised = 0; zi->ci.pos_in_buffered_data = 0; zi->ci.raw = raw; zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); zi->ci.size_centralExtra = size_extrafield_global; zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); /* version info */ zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ if (zipfi==NULL) zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); else zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); if (zipfi==NULL) zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); else zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); if(zi->ci.pos_local_header >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); else zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writing_offset,4); for (i=0;i<size_filename;i++) *(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i); for (i=0;i<size_extrafield_global;i++) *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+i) = *(((const char*)extrafield_global)+i); for (i=0;i<size_comment;i++) *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+ size_extrafield_global+i) = *(comment+i); if (zi->ci.central_header == NULL) return ZIP_INTERNALERROR; zi->ci.zip64 = zip64; zi->ci.totalCompressedData = 0; zi->ci.totalUncompressedData = 0; zi->ci.pos_zip64extrainfo = 0; err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); #ifdef HAVE_BZIP2 zi->ci.bstream.avail_in = (uInt)0; zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; zi->ci.bstream.total_in_hi32 = 0; zi->ci.bstream.total_in_lo32 = 0; zi->ci.bstream.total_out_hi32 = 0; zi->ci.bstream.total_out_lo32 = 0; #endif zi->ci.stream.avail_in = (uInt)0; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; zi->ci.stream.total_in = 0; zi->ci.stream.total_out = 0; zi->ci.stream.data_type = Z_BINARY; #ifdef HAVE_BZIP2 if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) #else if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) #endif { if(zi->ci.method == Z_DEFLATED) { zi->ci.stream.zalloc = (alloc_func)0; zi->ci.stream.zfree = (free_func)0; zi->ci.stream.opaque = (voidpf)0; if (windowBits>0) windowBits = -windowBits; err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); if (err==Z_OK) zi->ci.stream_initialised = Z_DEFLATED; } else if(zi->ci.method == Z_BZIP2ED) { #ifdef HAVE_BZIP2 // Init BZip stuff here zi->ci.bstream.bzalloc = 0; zi->ci.bstream.bzfree = 0; zi->ci.bstream.opaque = (voidpf)0; err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); if(err == BZ_OK) zi->ci.stream_initialised = Z_BZIP2ED; #endif } } # ifndef NOCRYPT zi->ci.crypt_header_size = 0; if ((err==Z_OK) && (password != NULL)) { unsigned char bufHead[RAND_HEAD_LEN]; unsigned int sizeHead; zi->ci.encrypt = 1; zi->ci.pcrc_32_tab = get_crc_table(); /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); zi->ci.crypt_header_size = sizeHead; if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) err = ZIP_ERRNO; } # endif if (err==Z_OK) zi->in_opened_file_inzip = 1; return err; } extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase) { return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, password, crcForCrypting, versionMadeBy, flagBase, 0); } extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting) { return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, password, crcForCrypting, VERSIONMADEBY, 0, 0); } extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits,int memLevel, int strategy, const char* password, uLong crcForCrypting, int zip64) { return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, password, crcForCrypting, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw) { return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, 0); } extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int zip64) { return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void*extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int zip64) { return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, zip64); } extern int ZEXPORT zipOpenNewFileInZip(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void*extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level) { return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, 0); } local int zip64FlushWriteBuffer(zip64_internal* zi) { int err=ZIP_OK; if (zi->ci.encrypt != 0) { #ifndef NOCRYPT uInt i; int t; for (i=0;i<zi->ci.pos_in_buffered_data;i++) zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); #endif } if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) err = ZIP_ERRNO; zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; #ifdef HAVE_BZIP2 if(zi->ci.method == Z_BZIP2ED) { zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; zi->ci.bstream.total_in_lo32 = 0; zi->ci.bstream.total_in_hi32 = 0; } else #endif { zi->ci.totalUncompressedData += zi->ci.stream.total_in; zi->ci.stream.total_in = 0; } zi->ci.pos_in_buffered_data = 0; return err; } extern int ZEXPORT zipWriteInFileInZip(zipFile file, const void* buf, unsigned int len) { zip64_internal* zi; int err=ZIP_OK; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 0) return ZIP_PARAMERROR; zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); #ifdef HAVE_BZIP2 if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) { zi->ci.bstream.next_in = (void*)buf; zi->ci.bstream.avail_in = len; err = BZ_RUN_OK; while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) { if (zi->ci.bstream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; } if(err != BZ_RUN_OK) break; if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) { uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; // uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; } } if(err == BZ_RUN_OK) err = ZIP_OK; } else #endif { zi->ci.stream.next_in = (Bytef*)(uintptr_t)buf; zi->ci.stream.avail_in = len; while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) { if (zi->ci.stream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; } if(err != ZIP_OK) break; if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { uLong uTotalOutBefore = zi->ci.stream.total_out; err=deflate(&zi->ci.stream, Z_NO_FLUSH); zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; } else { uInt copy_this,i; if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) copy_this = zi->ci.stream.avail_in; else copy_this = zi->ci.stream.avail_out; for (i = 0; i < copy_this; i++) *(((char*)zi->ci.stream.next_out)+i) = *(((const char*)zi->ci.stream.next_in)+i); { zi->ci.stream.avail_in -= copy_this; zi->ci.stream.avail_out-= copy_this; zi->ci.stream.next_in+= copy_this; zi->ci.stream.next_out+= copy_this; zi->ci.stream.total_in+= copy_this; zi->ci.stream.total_out+= copy_this; zi->ci.pos_in_buffered_data += copy_this; } } }// while(...) } return err; } extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uLong uncompressed_size, uLong crc32) { return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); } extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, ZPOS64_T uncompressed_size, uLong crc32) { zip64_internal* zi; ZPOS64_T compressed_size; uLong invalidValue = 0xffffffff; unsigned datasize = 0; int err=ZIP_OK; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 0) return ZIP_PARAMERROR; zi->ci.stream.avail_in = 0; if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { while (err==ZIP_OK) { uLong uTotalOutBefore; if (zi->ci.stream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; } uTotalOutBefore = zi->ci.stream.total_out; err=deflate(&zi->ci.stream, Z_FINISH); zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; } } else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) { #ifdef HAVE_BZIP2 err = BZ_FINISH_OK; while (err==BZ_FINISH_OK) { uLong uTotalOutBefore; if (zi->ci.bstream.avail_out == 0) { if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; } uTotalOutBefore = zi->ci.bstream.total_out_lo32; err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); if(err == BZ_STREAM_END) err = Z_STREAM_END; zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); } if(err == BZ_FINISH_OK) err = ZIP_OK; #endif } if (err==Z_STREAM_END) err=ZIP_OK; /* this is normal */ if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) { if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) err = ZIP_ERRNO; } if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { int tmp_err = deflateEnd(&zi->ci.stream); if (err == ZIP_OK) err = tmp_err; zi->ci.stream_initialised = 0; } #ifdef HAVE_BZIP2 else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) { int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); if (err==ZIP_OK) err = tmperr; zi->ci.stream_initialised = 0; } #endif if (!zi->ci.raw) { crc32 = (uLong)zi->ci.crc32; uncompressed_size = zi->ci.totalUncompressedData; } compressed_size = zi->ci.totalCompressedData; # ifndef NOCRYPT compressed_size += zi->ci.crypt_header_size; # endif // update Current Item crc and sizes, if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) { /*version Made by*/ zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); /*version needed*/ zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); } zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ if(compressed_size >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ else zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ /// set internal file attributes field if (zi->ci.stream.data_type == Z_ASCII) zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); if(uncompressed_size >= 0xffffffff) zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ else zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ // Add ZIP64 extra info field for uncompressed size if(uncompressed_size >= 0xffffffff) datasize += 8; // Add ZIP64 extra info field for compressed size if(compressed_size >= 0xffffffff) datasize += 8; // Add ZIP64 extra info field for relative offset to local file header of current file if(zi->ci.pos_local_header >= 0xffffffff) datasize += 8; if(datasize > 0) { char* p = NULL; if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) { // we cannot write more data to the buffer that we have room for. return ZIP_BADZIPFILE; } p = zi->ci.central_header + zi->ci.size_centralheader; // Add Extra Information Header for 'ZIP64 information' zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID p += 2; zip64local_putValue_inmemory(p, datasize, 2); // DataSize p += 2; if(uncompressed_size >= 0xffffffff) { zip64local_putValue_inmemory(p, uncompressed_size, 8); p += 8; } if(compressed_size >= 0xffffffff) { zip64local_putValue_inmemory(p, compressed_size, 8); p += 8; } if(zi->ci.pos_local_header >= 0xffffffff) { zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); p += 8; } // Update how much extra free space we got in the memory buffer // and increase the centralheader size so the new ZIP64 fields are included // ( 4 below is the size of HeaderID and DataSize field ) zi->ci.size_centralExtraFree -= datasize + 4; zi->ci.size_centralheader += datasize + 4; // Update the extra info size field zi->ci.size_centralExtra += datasize + 4; zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); } if (err==ZIP_OK) err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); free(zi->ci.central_header); if (err==ZIP_OK) { // Update the LocalFileHeader with the new values. ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; if (err==ZIP_OK) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ if(uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff ) { if(zi->ci.pos_zip64extrainfo > 0) { // Update the size in the ZIP64 extended field. if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; if (err==ZIP_OK) /* compressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); if (err==ZIP_OK) /* uncompressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); } else err = ZIP_BADZIPFILE; // Caller passed zip64 = 0, so no room for zip64 info -> fatal } else { if (err==ZIP_OK) /* compressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); if (err==ZIP_OK) /* uncompressed size, unknown */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); } if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; } zi->number_entry ++; zi->in_opened_file_inzip = 0; return err; } extern int ZEXPORT zipCloseFileInZip(zipFile file) { return zipCloseFileInZipRaw (file,0,0); } local int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) { int err = ZIP_OK; ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writing_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); /*num disks*/ if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /*relative offset*/ if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); return err; } local int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; uLong Zip64DataSize = 44; err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? if (err==ZIP_OK) /* version made by */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); if (err==ZIP_OK) /* version needed */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); if (err==ZIP_OK) /* number of this disk */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); if (err==ZIP_OK) /* total number of entries in the central dir */ err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); if (err==ZIP_OK) /* size of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ { ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); } return err; } local int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; /*signature*/ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); if (err==ZIP_OK) /* number of this disk */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ { { if(zi->number_entry >= 0xFFFF) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); } } if (err==ZIP_OK) /* total number of entries in the central dir */ { if(zi->number_entry >= 0xFFFF) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); } if (err==ZIP_OK) /* size of the central directory */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ { ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; if(pos >= 0xffffffff) { err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); } else err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writing_offset),4); } return err; } local int Write_GlobalComment(zip64_internal* zi, const char* global_comment) { int err = ZIP_OK; uInt size_global_comment = 0; if(global_comment != NULL) size_global_comment = (uInt)strlen(global_comment); err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); if (err == ZIP_OK && size_global_comment > 0) { if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) err = ZIP_ERRNO; } return err; } extern int ZEXPORT zipClose(zipFile file, const char* global_comment) { zip64_internal* zi; int err = 0; uLong size_centraldir = 0; ZPOS64_T centraldir_pos_inzip; ZPOS64_T pos; if (file == NULL) return ZIP_PARAMERROR; zi = (zip64_internal*)file; if (zi->in_opened_file_inzip == 1) { err = zipCloseFileInZip (file); } #ifndef NO_ADDFILEINEXISTINGZIP if (global_comment==NULL) global_comment = zi->globalcomment; #endif centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); if (err==ZIP_OK) { linkedlist_datablock_internal* ldi = zi->central_dir.first_block; while (ldi!=NULL) { if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) { if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) err = ZIP_ERRNO; } size_centraldir += ldi->filled_in_this_block; ldi = ldi->next_datablock; } } free_linkedlist(&(zi->central_dir)); pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; if(pos >= 0xffffffff || zi->number_entry >= 0xFFFF) { ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); } if (err==ZIP_OK) err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); if(err == ZIP_OK) err = Write_GlobalComment(zi, global_comment); if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) if (err == ZIP_OK) err = ZIP_ERRNO; #ifndef NO_ADDFILEINEXISTINGZIP free(zi->globalcomment); #endif free(zi); return err; } extern int ZEXPORT zipRemoveExtraInfoBlock(char* pData, int* dataLen, short sHeader) { char* p = pData; int size = 0; char* pNewHeader; char* pTmp; short header; short dataSize; int retVal = ZIP_OK; if(pData == NULL || dataLen == NULL || *dataLen < 4) return ZIP_PARAMERROR; pNewHeader = (char*)ALLOC((unsigned)*dataLen); pTmp = pNewHeader; while(p < (pData + *dataLen)) { header = *(short*)p; dataSize = *(((short*)p)+1); if( header == sHeader ) // Header found. { p += dataSize + 4; // skip it. do not copy to temp buffer } else { // Extra Info block should not be removed, So copy it to the temp buffer. memcpy(pTmp, p, dataSize + 4); p += dataSize + 4; size += dataSize + 4; } } if(size < *dataLen) { // clean old extra info block. memset(pData,0, *dataLen); // copy the new extra info block over the old if(size > 0) memcpy(pData, pNewHeader, size); // set the new extra info size *dataLen = size; retVal = ZIP_OK; } else retVal = ZIP_ERRNO; free(pNewHeader); return retVal; } |
Added compat/zlib/contrib/minizip/zip.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* zip.h -- IO on .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt --------------------------------------------------------------------------- Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. --------------------------------------------------------------------------- Changes See header of zip.h */ #ifndef _zip12_H #define _zip12_H #ifdef __cplusplus extern "C" { #endif //#define HAVE_BZIP2 #ifndef _ZLIB_H #include "zlib.h" #endif #ifndef _ZLIBIOAPI_H #include "ioapi.h" #endif #ifdef HAVE_BZIP2 #include "bzlib.h" #endif #define Z_BZIP2ED 12 #if defined(STRICTZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagzipFile__ { int unused; } zipFile__; typedef zipFile__ *zipFile; #else typedef voidp zipFile; #endif #define ZIP_OK (0) #define ZIP_EOF (0) #define ZIP_ERRNO (Z_ERRNO) #define ZIP_PARAMERROR (-102) #define ZIP_BADZIPFILE (-103) #define ZIP_INTERNALERROR (-104) #ifndef DEF_MEM_LEVEL # if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 # else # define DEF_MEM_LEVEL MAX_MEM_LEVEL # endif #endif /* default memLevel */ /* tm_zip contain date/time info */ typedef struct tm_zip_s { int tm_sec; /* seconds after the minute - [0,59] */ int tm_min; /* minutes after the hour - [0,59] */ int tm_hour; /* hours since midnight - [0,23] */ int tm_mday; /* day of the month - [1,31] */ int tm_mon; /* months since January - [0,11] */ int tm_year; /* years - [1980..2044] */ } tm_zip; typedef struct { tm_zip tmz_date; /* date in understandable format */ uLong dosDate; /* if dos_date == 0, tmu_date is used */ /* uLong flag; */ /* general purpose bit flag 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ } zip_fileinfo; typedef const char* zipcharpc; #define APPEND_STATUS_CREATE (0) #define APPEND_STATUS_CREATEAFTER (1) #define APPEND_STATUS_ADDINZIP (2) extern zipFile ZEXPORT zipOpen(const char *pathname, int append); extern zipFile ZEXPORT zipOpen64(const void *pathname, int append); /* Create a zipfile. pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip will be created at the end of the file. (useful if the file contain a self extractor code) if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will add files in existing zip (be sure you don't add file that doesn't exist) If the zipfile cannot be opened, the return value is NULL. Else, the return value is a zipFile Handle, usable with other function of this zip package. */ /* Note : there is no delete function into a zipfile. If you want delete file into a zipfile, you must open a zipfile, and create another Of course, you can use RAW reading and writing to copy the file you did not want delete */ extern zipFile ZEXPORT zipOpen2(const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc_def); extern zipFile ZEXPORT zipOpen2_64(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def); extern zipFile ZEXPORT zipOpen3(const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def); extern int ZEXPORT zipOpenNewFileInZip(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level); extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int zip64); /* Open a file in the ZIP for writing. filename : the filename in zip (if NULL, '-' without quote will be used *zipfi contain supplemental information if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local contains the extrafield data for the local header if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global contains the extrafield data for the global header if comment != NULL, comment contain the comment string method contain the compression method (0 for store, Z_DEFLATED for deflate) level contain the level of compression (can be Z_DEFAULT_COMPRESSION) zip64 is set to 1 if a zip64 extended information block should be added to the local file header. this MUST be '1' if the uncompressed size is >= 0xffffffff. */ extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw); extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int zip64); /* Same than zipOpenNewFileInZip, except if raw=1, we write raw file */ extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting); extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, int zip64); /* Same than zipOpenNewFileInZip2, except windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 password : crypting password (NULL for no crypting) crcForCrypting : crc of file to compress (needed for crypting) */ extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase); extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase, int zip64); /* Same than zipOpenNewFileInZip4, except versionMadeBy : value for Version made by field flag : value for flag field (compression level info will be added) */ extern int ZEXPORT zipWriteInFileInZip(zipFile file, const void* buf, unsigned len); /* Write data in the zipfile */ extern int ZEXPORT zipCloseFileInZip(zipFile file); /* Close the current file in the zipfile */ extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uLong uncompressed_size, uLong crc32); extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, ZPOS64_T uncompressed_size, uLong crc32); /* Close the current file in the zipfile, for file opened with parameter raw=1 in zipOpenNewFileInZip2 uncompressed_size and crc32 are value for the uncompressed size */ extern int ZEXPORT zipClose(zipFile file, const char* global_comment); /* Close the zipfile */ extern int ZEXPORT zipRemoveExtraInfoBlock(char* pData, int* dataLen, short sHeader); /* zipRemoveExtraInfoBlock - Added by Mathias Svensson Remove extra information block from a extra information data for the local file header or central directory header It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. 0x0001 is the signature header for the ZIP64 extra information blocks usage. Remove ZIP64 Extra information from a central director extra field data zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); Remove ZIP64 Extra information from a Local File Header extra field data zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); */ #ifdef __cplusplus } #endif #endif /* _zip64_H */ |
Added compat/zlib/contrib/pascal/example.pas.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (* example.c -- usage example of the zlib compression library * Copyright (C) 1995-2003 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h * * Pascal translation * Copyright (C) 1998 by Jacques Nomssi Nzali. * For conditions of distribution and use, see copyright notice in readme.txt * * Adaptation to the zlibpas interface * Copyright (C) 2003 by Cosmin Truta. * For conditions of distribution and use, see copyright notice in readme.txt *) program example; {$DEFINE TEST_COMPRESS} {DO NOT $DEFINE TEST_GZIO} {$DEFINE TEST_DEFLATE} {$DEFINE TEST_INFLATE} {$DEFINE TEST_FLUSH} {$DEFINE TEST_SYNC} {$DEFINE TEST_DICT} uses SysUtils, zlibpas; const TESTFILE = 'foo.gz'; (* "hello world" would be more standard, but the repeated "hello" * stresses the compression code better, sorry... *) const hello: PChar = 'hello, hello!'; const dictionary: PChar = 'hello'; var dictId: LongInt; (* Adler32 value of the dictionary *) procedure CHECK_ERR(err: Integer; msg: String); begin if err <> Z_OK then begin WriteLn(msg, ' error: ', err); Halt(1); end; end; procedure EXIT_ERR(const msg: String); begin WriteLn('Error: ', msg); Halt(1); end; (* =========================================================================== * Test compress and uncompress *) {$IFDEF TEST_COMPRESS} procedure test_compress(compr: Pointer; comprLen: LongInt; uncompr: Pointer; uncomprLen: LongInt); var err: Integer; len: LongInt; begin len := StrLen(hello)+1; err := compress(compr, comprLen, hello, len); CHECK_ERR(err, 'compress'); StrCopy(PChar(uncompr), 'garbage'); err := uncompress(uncompr, uncomprLen, compr, comprLen); CHECK_ERR(err, 'uncompress'); if StrComp(PChar(uncompr), hello) <> 0 then EXIT_ERR('bad uncompress') else WriteLn('uncompress(): ', PChar(uncompr)); end; {$ENDIF} (* =========================================================================== * Test read/write of .gz files *) {$IFDEF TEST_GZIO} procedure test_gzio(const fname: PChar; (* compressed file name *) uncompr: Pointer; uncomprLen: LongInt); var err: Integer; len: Integer; zfile: gzFile; pos: LongInt; begin len := StrLen(hello)+1; zfile := gzopen(fname, 'wb'); if zfile = NIL then begin WriteLn('gzopen error'); Halt(1); end; gzputc(zfile, 'h'); if gzputs(zfile, 'ello') <> 4 then begin WriteLn('gzputs err: ', gzerror(zfile, err)); Halt(1); end; {$IFDEF GZ_FORMAT_STRING} if gzprintf(zfile, ', %s!', 'hello') <> 8 then begin WriteLn('gzprintf err: ', gzerror(zfile, err)); Halt(1); end; {$ELSE} if gzputs(zfile, ', hello!') <> 8 then begin WriteLn('gzputs err: ', gzerror(zfile, err)); Halt(1); end; {$ENDIF} gzseek(zfile, 1, SEEK_CUR); (* add one zero byte *) gzclose(zfile); zfile := gzopen(fname, 'rb'); if zfile = NIL then begin WriteLn('gzopen error'); Halt(1); end; StrCopy(PChar(uncompr), 'garbage'); if gzread(zfile, uncompr, uncomprLen) <> len then begin WriteLn('gzread err: ', gzerror(zfile, err)); Halt(1); end; if StrComp(PChar(uncompr), hello) <> 0 then begin WriteLn('bad gzread: ', PChar(uncompr)); Halt(1); end else WriteLn('gzread(): ', PChar(uncompr)); pos := gzseek(zfile, -8, SEEK_CUR); if (pos <> 6) or (gztell(zfile) <> pos) then begin WriteLn('gzseek error, pos=', pos, ', gztell=', gztell(zfile)); Halt(1); end; if gzgetc(zfile) <> ' ' then begin WriteLn('gzgetc error'); Halt(1); end; if gzungetc(' ', zfile) <> ' ' then begin WriteLn('gzungetc error'); Halt(1); end; gzgets(zfile, PChar(uncompr), uncomprLen); uncomprLen := StrLen(PChar(uncompr)); if uncomprLen <> 7 then (* " hello!" *) begin WriteLn('gzgets err after gzseek: ', gzerror(zfile, err)); Halt(1); end; if StrComp(PChar(uncompr), hello + 6) <> 0 then begin WriteLn('bad gzgets after gzseek'); Halt(1); end else WriteLn('gzgets() after gzseek: ', PChar(uncompr)); gzclose(zfile); end; {$ENDIF} (* =========================================================================== * Test deflate with small buffers *) {$IFDEF TEST_DEFLATE} procedure test_deflate(compr: Pointer; comprLen: LongInt); var c_stream: z_stream; (* compression stream *) err: Integer; len: LongInt; begin len := StrLen(hello)+1; c_stream.zalloc := NIL; c_stream.zfree := NIL; c_stream.opaque := NIL; err := deflateInit(c_stream, Z_DEFAULT_COMPRESSION); CHECK_ERR(err, 'deflateInit'); c_stream.next_in := hello; c_stream.next_out := compr; while (c_stream.total_in <> len) and (c_stream.total_out < comprLen) do begin c_stream.avail_out := 1; { force small buffers } c_stream.avail_in := 1; err := deflate(c_stream, Z_NO_FLUSH); CHECK_ERR(err, 'deflate'); end; (* Finish the stream, still forcing small buffers: *) while TRUE do begin c_stream.avail_out := 1; err := deflate(c_stream, Z_FINISH); if err = Z_STREAM_END then break; CHECK_ERR(err, 'deflate'); end; err := deflateEnd(c_stream); CHECK_ERR(err, 'deflateEnd'); end; {$ENDIF} (* =========================================================================== * Test inflate with small buffers *) {$IFDEF TEST_INFLATE} procedure test_inflate(compr: Pointer; comprLen : LongInt; uncompr: Pointer; uncomprLen : LongInt); var err: Integer; d_stream: z_stream; (* decompression stream *) begin StrCopy(PChar(uncompr), 'garbage'); d_stream.zalloc := NIL; d_stream.zfree := NIL; d_stream.opaque := NIL; d_stream.next_in := compr; d_stream.avail_in := 0; d_stream.next_out := uncompr; err := inflateInit(d_stream); CHECK_ERR(err, 'inflateInit'); while (d_stream.total_out < uncomprLen) and (d_stream.total_in < comprLen) do begin d_stream.avail_out := 1; (* force small buffers *) d_stream.avail_in := 1; err := inflate(d_stream, Z_NO_FLUSH); if err = Z_STREAM_END then break; CHECK_ERR(err, 'inflate'); end; err := inflateEnd(d_stream); CHECK_ERR(err, 'inflateEnd'); if StrComp(PChar(uncompr), hello) <> 0 then EXIT_ERR('bad inflate') else WriteLn('inflate(): ', PChar(uncompr)); end; {$ENDIF} (* =========================================================================== * Test deflate with large buffers and dynamic change of compression level *) {$IFDEF TEST_DEFLATE} procedure test_large_deflate(compr: Pointer; comprLen: LongInt; uncompr: Pointer; uncomprLen: LongInt); var c_stream: z_stream; (* compression stream *) err: Integer; begin c_stream.zalloc := NIL; c_stream.zfree := NIL; c_stream.opaque := NIL; err := deflateInit(c_stream, Z_BEST_SPEED); CHECK_ERR(err, 'deflateInit'); c_stream.next_out := compr; c_stream.avail_out := Integer(comprLen); (* At this point, uncompr is still mostly zeroes, so it should compress * very well: *) c_stream.next_in := uncompr; c_stream.avail_in := Integer(uncomprLen); err := deflate(c_stream, Z_NO_FLUSH); CHECK_ERR(err, 'deflate'); if c_stream.avail_in <> 0 then EXIT_ERR('deflate not greedy'); (* Feed in already compressed data and switch to no compression: *) deflateParams(c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); c_stream.next_in := compr; c_stream.avail_in := Integer(comprLen div 2); err := deflate(c_stream, Z_NO_FLUSH); CHECK_ERR(err, 'deflate'); (* Switch back to compressing mode: *) deflateParams(c_stream, Z_BEST_COMPRESSION, Z_FILTERED); c_stream.next_in := uncompr; c_stream.avail_in := Integer(uncomprLen); err := deflate(c_stream, Z_NO_FLUSH); CHECK_ERR(err, 'deflate'); err := deflate(c_stream, Z_FINISH); if err <> Z_STREAM_END then EXIT_ERR('deflate should report Z_STREAM_END'); err := deflateEnd(c_stream); CHECK_ERR(err, 'deflateEnd'); end; {$ENDIF} (* =========================================================================== * Test inflate with large buffers *) {$IFDEF TEST_INFLATE} procedure test_large_inflate(compr: Pointer; comprLen: LongInt; uncompr: Pointer; uncomprLen: LongInt); var err: Integer; d_stream: z_stream; (* decompression stream *) begin StrCopy(PChar(uncompr), 'garbage'); d_stream.zalloc := NIL; d_stream.zfree := NIL; d_stream.opaque := NIL; d_stream.next_in := compr; d_stream.avail_in := Integer(comprLen); err := inflateInit(d_stream); CHECK_ERR(err, 'inflateInit'); while TRUE do begin d_stream.next_out := uncompr; (* discard the output *) d_stream.avail_out := Integer(uncomprLen); err := inflate(d_stream, Z_NO_FLUSH); if err = Z_STREAM_END then break; CHECK_ERR(err, 'large inflate'); end; err := inflateEnd(d_stream); CHECK_ERR(err, 'inflateEnd'); if d_stream.total_out <> 2 * uncomprLen + comprLen div 2 then begin WriteLn('bad large inflate: ', d_stream.total_out); Halt(1); end else WriteLn('large_inflate(): OK'); end; {$ENDIF} (* =========================================================================== * Test deflate with full flush *) {$IFDEF TEST_FLUSH} procedure test_flush(compr: Pointer; var comprLen : LongInt); var c_stream: z_stream; (* compression stream *) err: Integer; len: Integer; begin len := StrLen(hello)+1; c_stream.zalloc := NIL; c_stream.zfree := NIL; c_stream.opaque := NIL; err := deflateInit(c_stream, Z_DEFAULT_COMPRESSION); CHECK_ERR(err, 'deflateInit'); c_stream.next_in := hello; c_stream.next_out := compr; c_stream.avail_in := 3; c_stream.avail_out := Integer(comprLen); err := deflate(c_stream, Z_FULL_FLUSH); CHECK_ERR(err, 'deflate'); Inc(PByteArray(compr)^[3]); (* force an error in first compressed block *) c_stream.avail_in := len - 3; err := deflate(c_stream, Z_FINISH); if err <> Z_STREAM_END then CHECK_ERR(err, 'deflate'); err := deflateEnd(c_stream); CHECK_ERR(err, 'deflateEnd'); comprLen := c_stream.total_out; end; {$ENDIF} (* =========================================================================== * Test inflateSync() *) {$IFDEF TEST_SYNC} procedure test_sync(compr: Pointer; comprLen: LongInt; uncompr: Pointer; uncomprLen : LongInt); var err: Integer; d_stream: z_stream; (* decompression stream *) begin StrCopy(PChar(uncompr), 'garbage'); d_stream.zalloc := NIL; d_stream.zfree := NIL; d_stream.opaque := NIL; d_stream.next_in := compr; d_stream.avail_in := 2; (* just read the zlib header *) err := inflateInit(d_stream); CHECK_ERR(err, 'inflateInit'); d_stream.next_out := uncompr; d_stream.avail_out := Integer(uncomprLen); inflate(d_stream, Z_NO_FLUSH); CHECK_ERR(err, 'inflate'); d_stream.avail_in := Integer(comprLen-2); (* read all compressed data *) err := inflateSync(d_stream); (* but skip the damaged part *) CHECK_ERR(err, 'inflateSync'); err := inflate(d_stream, Z_FINISH); if err <> Z_DATA_ERROR then EXIT_ERR('inflate should report DATA_ERROR'); (* Because of incorrect adler32 *) err := inflateEnd(d_stream); CHECK_ERR(err, 'inflateEnd'); WriteLn('after inflateSync(): hel', PChar(uncompr)); end; {$ENDIF} (* =========================================================================== * Test deflate with preset dictionary *) {$IFDEF TEST_DICT} procedure test_dict_deflate(compr: Pointer; comprLen: LongInt); var c_stream: z_stream; (* compression stream *) err: Integer; begin c_stream.zalloc := NIL; c_stream.zfree := NIL; c_stream.opaque := NIL; err := deflateInit(c_stream, Z_BEST_COMPRESSION); CHECK_ERR(err, 'deflateInit'); err := deflateSetDictionary(c_stream, dictionary, StrLen(dictionary)); CHECK_ERR(err, 'deflateSetDictionary'); dictId := c_stream.adler; c_stream.next_out := compr; c_stream.avail_out := Integer(comprLen); c_stream.next_in := hello; c_stream.avail_in := StrLen(hello)+1; err := deflate(c_stream, Z_FINISH); if err <> Z_STREAM_END then EXIT_ERR('deflate should report Z_STREAM_END'); err := deflateEnd(c_stream); CHECK_ERR(err, 'deflateEnd'); end; {$ENDIF} (* =========================================================================== * Test inflate with a preset dictionary *) {$IFDEF TEST_DICT} procedure test_dict_inflate(compr: Pointer; comprLen: LongInt; uncompr: Pointer; uncomprLen: LongInt); var err: Integer; d_stream: z_stream; (* decompression stream *) begin StrCopy(PChar(uncompr), 'garbage'); d_stream.zalloc := NIL; d_stream.zfree := NIL; d_stream.opaque := NIL; d_stream.next_in := compr; d_stream.avail_in := Integer(comprLen); err := inflateInit(d_stream); CHECK_ERR(err, 'inflateInit'); d_stream.next_out := uncompr; d_stream.avail_out := Integer(uncomprLen); while TRUE do begin err := inflate(d_stream, Z_NO_FLUSH); if err = Z_STREAM_END then break; if err = Z_NEED_DICT then begin if d_stream.adler <> dictId then EXIT_ERR('unexpected dictionary'); err := inflateSetDictionary(d_stream, dictionary, StrLen(dictionary)); end; CHECK_ERR(err, 'inflate with dict'); end; err := inflateEnd(d_stream); CHECK_ERR(err, 'inflateEnd'); if StrComp(PChar(uncompr), hello) <> 0 then EXIT_ERR('bad inflate with dict') else WriteLn('inflate with dictionary: ', PChar(uncompr)); end; {$ENDIF} var compr, uncompr: Pointer; comprLen, uncomprLen: LongInt; begin if zlibVersion^ <> ZLIB_VERSION[1] then EXIT_ERR('Incompatible zlib version'); WriteLn('zlib version: ', zlibVersion); WriteLn('zlib compile flags: ', Format('0x%x', [zlibCompileFlags])); comprLen := 10000 * SizeOf(Integer); (* don't overflow on MSDOS *) uncomprLen := comprLen; GetMem(compr, comprLen); GetMem(uncompr, uncomprLen); if (compr = NIL) or (uncompr = NIL) then EXIT_ERR('Out of memory'); (* compr and uncompr are cleared to avoid reading uninitialized * data and to ensure that uncompr compresses well. *) FillChar(compr^, comprLen, 0); FillChar(uncompr^, uncomprLen, 0); {$IFDEF TEST_COMPRESS} WriteLn('** Testing compress'); test_compress(compr, comprLen, uncompr, uncomprLen); {$ENDIF} {$IFDEF TEST_GZIO} WriteLn('** Testing gzio'); if ParamCount >= 1 then test_gzio(ParamStr(1), uncompr, uncomprLen) else test_gzio(TESTFILE, uncompr, uncomprLen); {$ENDIF} {$IFDEF TEST_DEFLATE} WriteLn('** Testing deflate with small buffers'); test_deflate(compr, comprLen); {$ENDIF} {$IFDEF TEST_INFLATE} WriteLn('** Testing inflate with small buffers'); test_inflate(compr, comprLen, uncompr, uncomprLen); {$ENDIF} {$IFDEF TEST_DEFLATE} WriteLn('** Testing deflate with large buffers'); test_large_deflate(compr, comprLen, uncompr, uncomprLen); {$ENDIF} {$IFDEF TEST_INFLATE} WriteLn('** Testing inflate with large buffers'); test_large_inflate(compr, comprLen, uncompr, uncomprLen); {$ENDIF} {$IFDEF TEST_FLUSH} WriteLn('** Testing deflate with full flush'); test_flush(compr, comprLen); {$ENDIF} {$IFDEF TEST_SYNC} WriteLn('** Testing inflateSync'); test_sync(compr, comprLen, uncompr, uncomprLen); {$ENDIF} comprLen := uncomprLen; {$IFDEF TEST_DICT} WriteLn('** Testing deflate and inflate with preset dictionary'); test_dict_deflate(compr, comprLen); test_dict_inflate(compr, comprLen, uncompr, uncomprLen); {$ENDIF} FreeMem(compr, comprLen); FreeMem(uncompr, uncomprLen); end. |
Added compat/zlib/contrib/pascal/readme.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 | This directory contains a Pascal (Delphi, Kylix) interface to the zlib data compression library. Directory listing ================= zlibd32.mak makefile for Borland C++ example.pas usage example of zlib zlibpas.pas the Pascal interface to zlib readme.txt this file Compatibility notes =================== - Although the name "zlib" would have been more normal for the zlibpas unit, this name is already taken by Borland's ZLib unit. This is somehow unfortunate, because that unit is not a genuine interface to the full-fledged zlib functionality, but a suite of class wrappers around zlib streams. Other essential features, such as checksums, are missing. It would have been more appropriate for that unit to have a name like "ZStreams", or something similar. - The C and zlib-supplied types int, uInt, long, uLong, etc. are translated directly into Pascal types of similar sizes (Integer, LongInt, etc.), to avoid namespace pollution. In particular, there is no conversion of unsigned int into a Pascal unsigned integer. The Word type is non-portable and has the same size (16 bits) both in a 16-bit and in a 32-bit environment, unlike Integer. Even if there is a 32-bit Cardinal type, there is no real need for unsigned int in zlib under a 32-bit environment. - Except for the callbacks, the zlib function interfaces are assuming the calling convention normally used in Pascal (__pascal for DOS and Windows16, __fastcall for Windows32). Since the cdecl keyword is used, the old Turbo Pascal does not work with this interface. - The gz* function interfaces are not translated, to avoid interfacing problems with the C runtime library. Besides, gzprintf(gzFile file, const char *format, ...) cannot be translated into Pascal. Legal issues ============ The zlibpas interface is: Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler. Copyright (C) 1998 by Bob Dellaca. Copyright (C) 2003 by Cosmin Truta. The example program is: Copyright (C) 1995-2003 by Jean-loup Gailly. Copyright (C) 1998,1999,2000 by Jacques Nomssi Nzali. Copyright (C) 2003 by Cosmin Truta. This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. |
Added compat/zlib/contrib/pascal/zlibd32.mak.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib # For use with Delphi and C++ Builder under Win32 # Updated for zlib 1.2.x by Cosmin Truta # ------------ Borland C++ ------------ # This project uses the Delphi (fastcall/register) calling convention: LOC = -DZEXPORT=__fastcall -DZEXPORTVA=__cdecl CC = bcc32 LD = bcc32 AR = tlib # do not use "-pr" in CFLAGS CFLAGS = -a -d -k- -O2 $(LOC) LDFLAGS = # variables ZLIB_LIB = zlib.lib OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj # targets all: $(ZLIB_LIB) example.exe minigzip.exe .c.obj: $(CC) -c $(CFLAGS) $*.c adler32.obj: adler32.c zlib.h zconf.h compress.obj: compress.c zlib.h zconf.h crc32.obj: crc32.c zlib.h zconf.h crc32.h deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h gzread.obj: gzread.c zlib.h zconf.h gzguts.h gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h uncompr.obj: uncompr.c zlib.h zconf.h zutil.obj: zutil.c zutil.h zlib.h zconf.h example.obj: test/example.c zlib.h zconf.h minigzip.obj: test/minigzip.c zlib.h zconf.h # For the sake of the old Borland make, # the command line is cut to fit in the MS-DOS 128 byte limit: $(ZLIB_LIB): $(OBJ1) $(OBJ2) -del $(ZLIB_LIB) $(AR) $(ZLIB_LIB) $(OBJP1) $(AR) $(ZLIB_LIB) $(OBJP2) # testing test: example.exe minigzip.exe example echo hello world | minigzip | minigzip -d example.exe: example.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) example.obj $(ZLIB_LIB) minigzip.exe: minigzip.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB) # cleanup clean: -del *.obj -del *.exe -del *.lib -del *.tds -del zlib.bak -del foo.gz |
Added compat/zlib/contrib/pascal/zlibpas.pas.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (* zlibpas -- Pascal interface to the zlib data compression library * * Copyright (C) 2003 Cosmin Truta. * Derived from original sources by Bob Dellaca. * For conditions of distribution and use, see copyright notice in readme.txt *) unit zlibpas; interface const ZLIB_VERSION = '1.3.1'; ZLIB_VERNUM = $12a0; type alloc_func = function(opaque: Pointer; items, size: Integer): Pointer; cdecl; free_func = procedure(opaque, address: Pointer); cdecl; in_func = function(opaque: Pointer; var buf: PByte): Integer; cdecl; out_func = function(opaque: Pointer; buf: PByte; size: Integer): Integer; cdecl; z_streamp = ^z_stream; z_stream = packed record next_in: PChar; (* next input byte *) avail_in: Integer; (* number of bytes available at next_in *) total_in: LongInt; (* total nb of input bytes read so far *) next_out: PChar; (* next output byte should be put there *) avail_out: Integer; (* remaining free space at next_out *) total_out: LongInt; (* total nb of bytes output so far *) msg: PChar; (* last error message, NULL if no error *) state: Pointer; (* not visible by applications *) zalloc: alloc_func; (* used to allocate the internal state *) zfree: free_func; (* used to free the internal state *) opaque: Pointer; (* private data object passed to zalloc and zfree *) data_type: Integer; (* best guess about the data type: ascii or binary *) adler: LongInt; (* adler32 value of the uncompressed data *) reserved: LongInt; (* reserved for future use *) end; gz_headerp = ^gz_header; gz_header = packed record text: Integer; (* true if compressed data believed to be text *) time: LongInt; (* modification time *) xflags: Integer; (* extra flags (not used when writing a gzip file) *) os: Integer; (* operating system *) extra: PChar; (* pointer to extra field or Z_NULL if none *) extra_len: Integer; (* extra field length (valid if extra != Z_NULL) *) extra_max: Integer; (* space at extra (only when reading header) *) name: PChar; (* pointer to zero-terminated file name or Z_NULL *) name_max: Integer; (* space at name (only when reading header) *) comment: PChar; (* pointer to zero-terminated comment or Z_NULL *) comm_max: Integer; (* space at comment (only when reading header) *) hcrc: Integer; (* true if there was or will be a header crc *) done: Integer; (* true when done reading gzip header *) end; (* constants *) const Z_NO_FLUSH = 0; Z_PARTIAL_FLUSH = 1; Z_SYNC_FLUSH = 2; Z_FULL_FLUSH = 3; Z_FINISH = 4; Z_BLOCK = 5; Z_TREES = 6; Z_OK = 0; Z_STREAM_END = 1; Z_NEED_DICT = 2; Z_ERRNO = -1; Z_STREAM_ERROR = -2; Z_DATA_ERROR = -3; Z_MEM_ERROR = -4; Z_BUF_ERROR = -5; Z_VERSION_ERROR = -6; Z_NO_COMPRESSION = 0; Z_BEST_SPEED = 1; Z_BEST_COMPRESSION = 9; Z_DEFAULT_COMPRESSION = -1; Z_FILTERED = 1; Z_HUFFMAN_ONLY = 2; Z_RLE = 3; Z_FIXED = 4; Z_DEFAULT_STRATEGY = 0; Z_BINARY = 0; Z_TEXT = 1; Z_ASCII = 1; Z_UNKNOWN = 2; Z_DEFLATED = 8; (* basic functions *) function zlibVersion: PChar; function deflateInit(var strm: z_stream; level: Integer): Integer; function deflate(var strm: z_stream; flush: Integer): Integer; function deflateEnd(var strm: z_stream): Integer; function inflateInit(var strm: z_stream): Integer; function inflate(var strm: z_stream; flush: Integer): Integer; function inflateEnd(var strm: z_stream): Integer; (* advanced functions *) function deflateInit2(var strm: z_stream; level, method, windowBits, memLevel, strategy: Integer): Integer; function deflateSetDictionary(var strm: z_stream; const dictionary: PChar; dictLength: Integer): Integer; function deflateCopy(var dest, source: z_stream): Integer; function deflateReset(var strm: z_stream): Integer; function deflateParams(var strm: z_stream; level, strategy: Integer): Integer; function deflateTune(var strm: z_stream; good_length, max_lazy, nice_length, max_chain: Integer): Integer; function deflateBound(var strm: z_stream; sourceLen: LongInt): LongInt; function deflatePending(var strm: z_stream; var pending: Integer; var bits: Integer): Integer; function deflatePrime(var strm: z_stream; bits, value: Integer): Integer; function deflateSetHeader(var strm: z_stream; head: gz_header): Integer; function inflateInit2(var strm: z_stream; windowBits: Integer): Integer; function inflateSetDictionary(var strm: z_stream; const dictionary: PChar; dictLength: Integer): Integer; function inflateSync(var strm: z_stream): Integer; function inflateCopy(var dest, source: z_stream): Integer; function inflateReset(var strm: z_stream): Integer; function inflateReset2(var strm: z_stream; windowBits: Integer): Integer; function inflatePrime(var strm: z_stream; bits, value: Integer): Integer; function inflateMark(var strm: z_stream): LongInt; function inflateGetHeader(var strm: z_stream; var head: gz_header): Integer; function inflateBackInit(var strm: z_stream; windowBits: Integer; window: PChar): Integer; function inflateBack(var strm: z_stream; in_fn: in_func; in_desc: Pointer; out_fn: out_func; out_desc: Pointer): Integer; function inflateBackEnd(var strm: z_stream): Integer; function zlibCompileFlags: LongInt; (* utility functions *) function compress(dest: PChar; var destLen: LongInt; const source: PChar; sourceLen: LongInt): Integer; function compress2(dest: PChar; var destLen: LongInt; const source: PChar; sourceLen: LongInt; level: Integer): Integer; function compressBound(sourceLen: LongInt): LongInt; function uncompress(dest: PChar; var destLen: LongInt; const source: PChar; sourceLen: LongInt): Integer; (* checksum functions *) function adler32(adler: LongInt; const buf: PChar; len: Integer): LongInt; function adler32_combine(adler1, adler2, len2: LongInt): LongInt; function crc32(crc: LongInt; const buf: PChar; len: Integer): LongInt; function crc32_combine(crc1, crc2, len2: LongInt): LongInt; (* various hacks, don't look :) *) function deflateInit_(var strm: z_stream; level: Integer; const version: PChar; stream_size: Integer): Integer; function inflateInit_(var strm: z_stream; const version: PChar; stream_size: Integer): Integer; function deflateInit2_(var strm: z_stream; level, method, windowBits, memLevel, strategy: Integer; const version: PChar; stream_size: Integer): Integer; function inflateInit2_(var strm: z_stream; windowBits: Integer; const version: PChar; stream_size: Integer): Integer; function inflateBackInit_(var strm: z_stream; windowBits: Integer; window: PChar; const version: PChar; stream_size: Integer): Integer; implementation {$L adler32.obj} {$L compress.obj} {$L crc32.obj} {$L deflate.obj} {$L infback.obj} {$L inffast.obj} {$L inflate.obj} {$L inftrees.obj} {$L trees.obj} {$L uncompr.obj} {$L zutil.obj} function adler32; external; function adler32_combine; external; function compress; external; function compress2; external; function compressBound; external; function crc32; external; function crc32_combine; external; function deflate; external; function deflateBound; external; function deflateCopy; external; function deflateEnd; external; function deflateInit_; external; function deflateInit2_; external; function deflateParams; external; function deflatePending; external; function deflatePrime; external; function deflateReset; external; function deflateSetDictionary; external; function deflateSetHeader; external; function deflateTune; external; function inflate; external; function inflateBack; external; function inflateBackEnd; external; function inflateBackInit_; external; function inflateCopy; external; function inflateEnd; external; function inflateGetHeader; external; function inflateInit_; external; function inflateInit2_; external; function inflateMark; external; function inflatePrime; external; function inflateReset; external; function inflateReset2; external; function inflateSetDictionary; external; function inflateSync; external; function uncompress; external; function zlibCompileFlags; external; function zlibVersion; external; function deflateInit(var strm: z_stream; level: Integer): Integer; begin Result := deflateInit_(strm, level, ZLIB_VERSION, sizeof(z_stream)); end; function deflateInit2(var strm: z_stream; level, method, windowBits, memLevel, strategy: Integer): Integer; begin Result := deflateInit2_(strm, level, method, windowBits, memLevel, strategy, ZLIB_VERSION, sizeof(z_stream)); end; function inflateInit(var strm: z_stream): Integer; begin Result := inflateInit_(strm, ZLIB_VERSION, sizeof(z_stream)); end; function inflateInit2(var strm: z_stream; windowBits: Integer): Integer; begin Result := inflateInit2_(strm, windowBits, ZLIB_VERSION, sizeof(z_stream)); end; function inflateBackInit(var strm: z_stream; windowBits: Integer; window: PChar): Integer; begin Result := inflateBackInit_(strm, windowBits, window, ZLIB_VERSION, sizeof(z_stream)); end; function _malloc(Size: Integer): Pointer; cdecl; begin GetMem(Result, Size); end; procedure _free(Block: Pointer); cdecl; begin FreeMem(Block); end; procedure _memset(P: Pointer; B: Byte; count: Integer); cdecl; begin FillChar(P^, count, B); end; procedure _memcpy(dest, source: Pointer; count: Integer); cdecl; begin Move(source^, dest^, count); end; end. |
Added compat/zlib/contrib/puff/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 35 36 37 38 39 40 41 42 | CFLAGS=-O puff: puff.o pufftest.o puff.o: puff.h pufftest.o: puff.h test: puff puff zeros.raw puft: puff.c puff.h pufftest.o cc -fprofile-arcs -ftest-coverage -o puft puff.c pufftest.o # puff full coverage test (should say 100%) cov: puft @rm -f *.gcov *.gcda @puft -w zeros.raw 2>&1 | cat > /dev/null @echo '04' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2 @echo '00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2 @echo '00 00 00 00 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 254 @echo '00 01 00 fe ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2 @echo '01 01 00 fe ff 0a' | xxd -r -p | puft -f 2>&1 | cat > /dev/null @echo '02 7e ff ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 246 @echo '02' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2 @echo '04 80 49 92 24 49 92 24 0f b4 ff ff c3 04' | xxd -r -p | puft 2> /dev/null || test $$? -eq 2 @echo '04 80 49 92 24 49 92 24 71 ff ff 93 11 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 249 @echo '04 c0 81 08 00 00 00 00 20 7f eb 0b 00 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 246 @echo '0b 00 00' | xxd -r -p | puft -f 2>&1 | cat > /dev/null @echo '1a 07' | xxd -r -p | puft 2> /dev/null || test $$? -eq 246 @echo '0c c0 81 00 00 00 00 00 90 ff 6b 04' | xxd -r -p | puft 2> /dev/null || test $$? -eq 245 @puft -f zeros.raw 2>&1 | cat > /dev/null @echo 'fc 00 00' | xxd -r -p | puft 2> /dev/null || test $$? -eq 253 @echo '04 00 fe ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 252 @echo '04 00 24 49' | xxd -r -p | puft 2> /dev/null || test $$? -eq 251 @echo '04 80 49 92 24 49 92 24 0f b4 ff ff c3 84' | xxd -r -p | puft 2> /dev/null || test $$? -eq 248 @echo '04 00 24 e9 ff ff' | xxd -r -p | puft 2> /dev/null || test $$? -eq 250 @echo '04 00 24 e9 ff 6d' | xxd -r -p | puft 2> /dev/null || test $$? -eq 247 @gcov -n puff.c clean: rm -f puff puft *.o *.gc* |
Added compat/zlib/contrib/puff/README.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | Puff -- A Simple Inflate 3 Mar 2003 Mark Adler madler@alumni.caltech.edu What this is -- puff.c provides the routine puff() to decompress the deflate data format. It does so more slowly than zlib, but the code is about one-fifth the size of the inflate code in zlib, and written to be very easy to read. Why I wrote this -- puff.c was written to document the deflate format unambiguously, by virtue of being working C code. It is meant to supplement RFC 1951, which formally describes the deflate format. I have received many questions on details of the deflate format, and I hope that reading this code will answer those questions. puff.c is heavily commented with details of the deflate format, especially those little nooks and cranies of the format that might not be obvious from a specification. puff.c may also be useful in applications where code size or memory usage is a very limited resource, and speed is not as important. How to use it -- Well, most likely you should just be reading puff.c and using zlib for actual applications, but if you must ... Include puff.h in your code, which provides this prototype: int puff(unsigned char *dest, /* pointer to destination pointer */ unsigned long *destlen, /* amount of output space */ unsigned char *source, /* pointer to source data pointer */ unsigned long *sourcelen); /* amount of input available */ Then you can call puff() to decompress a deflate stream that is in memory in its entirety at source, to a sufficiently sized block of memory for the decompressed data at dest. puff() is the only external symbol in puff.c The only C library functions that puff.c needs are setjmp() and longjmp(), which are used to simplify error checking in the code to improve readability. puff.c does no memory allocation, and uses less than 2K bytes off of the stack. If destlen is not enough space for the uncompressed data, then inflate will return an error without writing more than destlen bytes. Note that this means that in order to decompress the deflate data successfully, you need to know the size of the uncompressed data ahead of time. If needed, puff() can determine the size of the uncompressed data with no output space. This is done by passing dest equal to (unsigned char *)0. Then the initial value of *destlen is ignored and *destlen is set to the length of the uncompressed data. So if the size of the uncompressed data is not known, then two passes of puff() can be used--first to determine the size, and second to do the actual inflation after allocating the appropriate memory. Not pretty, but it works. (This is one of the reasons you should be using zlib.) The deflate format is self-terminating. If the deflate stream does not end in *sourcelen bytes, puff() will return an error without reading at or past endsource. On return, *sourcelen is updated to the amount of input data consumed, and *destlen is updated to the size of the uncompressed data. See the comments in puff.c for the possible return codes for puff(). |
Added compat/zlib/contrib/puff/puff.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 | /* * puff.c * Copyright (C) 2002-2013 Mark Adler * For conditions of distribution and use, see copyright notice in puff.h * version 2.3, 21 Jan 2013 * * puff.c is a simple inflate written to be an unambiguous way to specify the * deflate format. It is not written for speed but rather simplicity. As a * side benefit, this code might actually be useful when small code is more * important than speed, such as bootstrap applications. For typical deflate * data, zlib's inflate() is about four times as fast as puff(). zlib's * inflate compiles to around 20K on my machine, whereas puff.c compiles to * around 4K on my machine (a PowerPC using GNU cc). If the faster decode() * function here is used, then puff() is only twice as slow as zlib's * inflate(). * * All dynamically allocated memory comes from the stack. The stack required * is less than 2K bytes. This code is compatible with 16-bit int's and * assumes that long's are at least 32 bits. puff.c uses the short data type, * assumed to be 16 bits, for arrays in order to conserve memory. The code * works whether integers are stored big endian or little endian. * * In the comments below are "Format notes" that describe the inflate process * and document some of the less obvious aspects of the format. This source * code is meant to supplement RFC 1951, which formally describes the deflate * format: * * http://www.zlib.org/rfc-deflate.html */ /* * Change history: * * 1.0 10 Feb 2002 - First version * 1.1 17 Feb 2002 - Clarifications of some comments and notes * - Update puff() dest and source pointers on negative * errors to facilitate debugging deflators * - Remove longest from struct huffman -- not needed * - Simplify offs[] index in construct() * - Add input size and checking, using longjmp() to * maintain easy readability * - Use short data type for large arrays * - Use pointers instead of long to specify source and * destination sizes to avoid arbitrary 4 GB limits * 1.2 17 Mar 2002 - Add faster version of decode(), doubles speed (!), * but leave simple version for readability * - Make sure invalid distances detected if pointers * are 16 bits * - Fix fixed codes table error * - Provide a scanning mode for determining size of * uncompressed data * 1.3 20 Mar 2002 - Go back to lengths for puff() parameters [Gailly] * - Add a puff.h file for the interface * - Add braces in puff() for else do [Gailly] * - Use indexes instead of pointers for readability * 1.4 31 Mar 2002 - Simplify construct() code set check * - Fix some comments * - Add FIXLCODES #define * 1.5 6 Apr 2002 - Minor comment fixes * 1.6 7 Aug 2002 - Minor format changes * 1.7 3 Mar 2003 - Added test code for distribution * - Added zlib-like license * 1.8 9 Jan 2004 - Added some comments on no distance codes case * 1.9 21 Feb 2008 - Fix bug on 16-bit integer architectures [Pohland] * - Catch missing end-of-block symbol error * 2.0 25 Jul 2008 - Add #define to permit distance too far back * - Add option in TEST code for puff to write the data * - Add option in TEST code to skip input bytes * - Allow TEST code to read from piped stdin * 2.1 4 Apr 2010 - Avoid variable initialization for happier compilers * - Avoid unsigned comparisons for even happier compilers * 2.2 25 Apr 2010 - Fix bug in variable initializations [Oberhumer] * - Add const where appropriate [Oberhumer] * - Split if's and ?'s for coverage testing * - Break out test code to separate file * - Move NIL to puff.h * - Allow incomplete code only if single code length is 1 * - Add full code coverage test to Makefile * 2.3 21 Jan 2013 - Check for invalid code length codes in dynamic blocks */ #include <setjmp.h> /* for setjmp(), longjmp(), and jmp_buf */ #include "puff.h" /* prototype for puff() */ #define local static /* for local function definitions */ /* * Maximums for allocations and loops. It is not useful to change these -- * they are fixed by the deflate format. */ #define MAXBITS 15 /* maximum bits in a code */ #define MAXLCODES 286 /* maximum number of literal/length codes */ #define MAXDCODES 30 /* maximum number of distance codes */ #define MAXCODES (MAXLCODES+MAXDCODES) /* maximum codes lengths to read */ #define FIXLCODES 288 /* number of fixed literal/length codes */ /* input and output state */ struct state { /* output state */ unsigned char *out; /* output buffer */ unsigned long outlen; /* available space at out */ unsigned long outcnt; /* bytes written to out so far */ /* input state */ const unsigned char *in; /* input buffer */ unsigned long inlen; /* available input at in */ unsigned long incnt; /* bytes read so far */ int bitbuf; /* bit buffer */ int bitcnt; /* number of bits in bit buffer */ /* input limit error return state for bits() and decode() */ jmp_buf env; }; /* * Return need bits from the input stream. This always leaves less than * eight bits in the buffer. bits() works properly for need == 0. * * Format notes: * * - Bits are stored in bytes from the least significant bit to the most * significant bit. Therefore bits are dropped from the bottom of the bit * buffer, using shift right, and new bytes are appended to the top of the * bit buffer, using shift left. */ local int bits(struct state *s, int need) { long val; /* bit accumulator (can use up to 20 bits) */ /* load at least need bits into val */ val = s->bitbuf; while (s->bitcnt < need) { if (s->incnt == s->inlen) longjmp(s->env, 1); /* out of input */ val |= (long)(s->in[s->incnt++]) << s->bitcnt; /* load eight bits */ s->bitcnt += 8; } /* drop need bits and update buffer, always zero to seven bits left */ s->bitbuf = (int)(val >> need); s->bitcnt -= need; /* return need bits, zeroing the bits above that */ return (int)(val & ((1L << need) - 1)); } /* * Process a stored block. * * Format notes: * * - After the two-bit stored block type (00), the stored block length and * stored bytes are byte-aligned for fast copying. Therefore any leftover * bits in the byte that has the last bit of the type, as many as seven, are * discarded. The value of the discarded bits are not defined and should not * be checked against any expectation. * * - The second inverted copy of the stored block length does not have to be * checked, but it's probably a good idea to do so anyway. * * - A stored block can have zero length. This is sometimes used to byte-align * subsets of the compressed data for random access or partial recovery. */ local int stored(struct state *s) { unsigned len; /* length of stored block */ /* discard leftover bits from current byte (assumes s->bitcnt < 8) */ s->bitbuf = 0; s->bitcnt = 0; /* get length and check against its one's complement */ if (s->incnt + 4 > s->inlen) return 2; /* not enough input */ len = s->in[s->incnt++]; len |= s->in[s->incnt++] << 8; if (s->in[s->incnt++] != (~len & 0xff) || s->in[s->incnt++] != ((~len >> 8) & 0xff)) return -2; /* didn't match complement! */ /* copy len bytes from in to out */ if (s->incnt + len > s->inlen) return 2; /* not enough input */ if (s->out != NIL) { if (s->outcnt + len > s->outlen) return 1; /* not enough output space */ while (len--) s->out[s->outcnt++] = s->in[s->incnt++]; } else { /* just scanning */ s->outcnt += len; s->incnt += len; } /* done with a valid stored block */ return 0; } /* * Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of * each length, which for a canonical code are stepped through in order. * symbol[] are the symbol values in canonical order, where the number of * entries is the sum of the counts in count[]. The decoding process can be * seen in the function decode() below. */ struct huffman { short *count; /* number of symbols of each length */ short *symbol; /* canonically ordered symbols */ }; /* * Decode a code from the stream s using huffman table h. Return the symbol or * a negative value if there is an error. If all of the lengths are zero, i.e. * an empty code, or if the code is incomplete and an invalid code is received, * then -10 is returned after reading MAXBITS bits. * * Format notes: * * - The codes as stored in the compressed data are bit-reversed relative to * a simple integer ordering of codes of the same lengths. Hence below the * bits are pulled from the compressed data one at a time and used to * build the code value reversed from what is in the stream in order to * permit simple integer comparisons for decoding. A table-based decoding * scheme (as used in zlib) does not need to do this reversal. * * - The first code for the shortest length is all zeros. Subsequent codes of * the same length are simply integer increments of the previous code. When * moving up a length, a zero bit is appended to the code. For a complete * code, the last code of the longest length will be all ones. * * - Incomplete codes are handled by this decoder, since they are permitted * in the deflate format. See the format notes for fixed() and dynamic(). */ #ifdef SLOW local int decode(struct state *s, const struct huffman *h) { int len; /* current number of bits in code */ int code; /* len bits being decoded */ int first; /* first code of length len */ int count; /* number of codes of length len */ int index; /* index of first code of length len in symbol table */ code = first = index = 0; for (len = 1; len <= MAXBITS; len++) { code |= bits(s, 1); /* get next bit */ count = h->count[len]; if (code - count < first) /* if length len, return symbol */ return h->symbol[index + (code - first)]; index += count; /* else update for next length */ first += count; first <<= 1; code <<= 1; } return -10; /* ran out of codes */ } /* * A faster version of decode() for real applications of this code. It's not * as readable, but it makes puff() twice as fast. And it only makes the code * a few percent larger. */ #else /* !SLOW */ local int decode(struct state *s, const struct huffman *h) { int len; /* current number of bits in code */ int code; /* len bits being decoded */ int first; /* first code of length len */ int count; /* number of codes of length len */ int index; /* index of first code of length len in symbol table */ int bitbuf; /* bits from stream */ int left; /* bits left in next or left to process */ short *next; /* next number of codes */ bitbuf = s->bitbuf; left = s->bitcnt; code = first = index = 0; len = 1; next = h->count + 1; while (1) { while (left--) { code |= bitbuf & 1; bitbuf >>= 1; count = *next++; if (code - count < first) { /* if length len, return symbol */ s->bitbuf = bitbuf; s->bitcnt = (s->bitcnt - len) & 7; return h->symbol[index + (code - first)]; } index += count; /* else update for next length */ first += count; first <<= 1; code <<= 1; len++; } left = (MAXBITS+1) - len; if (left == 0) break; if (s->incnt == s->inlen) longjmp(s->env, 1); /* out of input */ bitbuf = s->in[s->incnt++]; if (left > 8) left = 8; } return -10; /* ran out of codes */ } #endif /* SLOW */ /* * Given the list of code lengths length[0..n-1] representing a canonical * Huffman code for n symbols, construct the tables required to decode those * codes. Those tables are the number of codes of each length, and the symbols * sorted by length, retaining their original order within each length. The * return value is zero for a complete code set, negative for an over- * subscribed code set, and positive for an incomplete code set. The tables * can be used if the return value is zero or positive, but they cannot be used * if the return value is negative. If the return value is zero, it is not * possible for decode() using that table to return an error--any stream of * enough bits will resolve to a symbol. If the return value is positive, then * it is possible for decode() using that table to return an error for received * codes past the end of the incomplete lengths. * * Not used by decode(), but used for error checking, h->count[0] is the number * of the n symbols not in the code. So n - h->count[0] is the number of * codes. This is useful for checking for incomplete codes that have more than * one symbol, which is an error in a dynamic block. * * Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS * This is assured by the construction of the length arrays in dynamic() and * fixed() and is not verified by construct(). * * Format notes: * * - Permitted and expected examples of incomplete codes are one of the fixed * codes and any code with a single symbol which in deflate is coded as one * bit instead of zero bits. See the format notes for fixed() and dynamic(). * * - Within a given code length, the symbols are kept in ascending order for * the code bits definition. */ local int construct(struct huffman *h, const short *length, int n) { int symbol; /* current symbol when stepping through length[] */ int len; /* current length when stepping through h->count[] */ int left; /* number of possible codes left of current length */ short offs[MAXBITS+1]; /* offsets in symbol table for each length */ /* count number of codes of each length */ for (len = 0; len <= MAXBITS; len++) h->count[len] = 0; for (symbol = 0; symbol < n; symbol++) (h->count[length[symbol]])++; /* assumes lengths are within bounds */ if (h->count[0] == n) /* no codes! */ return 0; /* complete, but decode() will fail */ /* check for an over-subscribed or incomplete set of lengths */ left = 1; /* one possible code of zero length */ for (len = 1; len <= MAXBITS; len++) { left <<= 1; /* one more bit, double codes left */ left -= h->count[len]; /* deduct count from possible codes */ if (left < 0) return left; /* over-subscribed--return negative */ } /* left > 0 means incomplete */ /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h->count[len]; /* * put symbols in table sorted by length, by symbol order within each * length */ for (symbol = 0; symbol < n; symbol++) if (length[symbol] != 0) h->symbol[offs[length[symbol]]++] = symbol; /* return zero for complete set, positive for incomplete set */ return left; } /* * Decode literal/length and distance codes until an end-of-block code. * * Format notes: * * - Compressed data that is after the block type if fixed or after the code * description if dynamic is a combination of literals and length/distance * pairs terminated by and end-of-block code. Literals are simply Huffman * coded bytes. A length/distance pair is a coded length followed by a * coded distance to represent a string that occurs earlier in the * uncompressed data that occurs again at the current location. * * - Literals, lengths, and the end-of-block code are combined into a single * code of up to 286 symbols. They are 256 literals (0..255), 29 length * symbols (257..285), and the end-of-block symbol (256). * * - There are 256 possible lengths (3..258), and so 29 symbols are not enough * to represent all of those. Lengths 3..10 and 258 are in fact represented * by just a length symbol. Lengths 11..257 are represented as a symbol and * some number of extra bits that are added as an integer to the base length * of the length symbol. The number of extra bits is determined by the base * length symbol. These are in the static arrays below, lens[] for the base * lengths and lext[] for the corresponding number of extra bits. * * - The reason that 258 gets its own symbol is that the longest length is used * often in highly redundant files. Note that 258 can also be coded as the * base value 227 plus the maximum extra value of 31. While a good deflate * should never do this, it is not an error, and should be decoded properly. * * - If a length is decoded, including its extra bits if any, then it is * followed a distance code. There are up to 30 distance symbols. Again * there are many more possible distances (1..32768), so extra bits are added * to a base value represented by the symbol. The distances 1..4 get their * own symbol, but the rest require extra bits. The base distances and * corresponding number of extra bits are below in the static arrays dist[] * and dext[]. * * - Literal bytes are simply written to the output. A length/distance pair is * an instruction to copy previously uncompressed bytes to the output. The * copy is from distance bytes back in the output stream, copying for length * bytes. * * - Distances pointing before the beginning of the output data are not * permitted. * * - Overlapped copies, where the length is greater than the distance, are * allowed and common. For example, a distance of one and a length of 258 * simply copies the last byte 258 times. A distance of four and a length of * twelve copies the last four bytes three times. A simple forward copy * ignoring whether the length is greater than the distance or not implements * this correctly. You should not use memcpy() since its behavior is not * defined for overlapped arrays. You should not use memmove() or bcopy() * since though their behavior -is- defined for overlapping arrays, it is * defined to do the wrong thing in this case. */ local int codes(struct state *s, const struct huffman *lencode, const struct huffman *distcode) { int symbol; /* decoded symbol */ int len; /* length for copy */ unsigned dist; /* distance for copy */ static const short lens[29] = { /* Size base for length codes 257..285 */ 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}; static const short lext[29] = { /* Extra bits for length codes 257..285 */ 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}; static const short dists[30] = { /* Offset base for distance codes 0..29 */ 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}; static const short dext[30] = { /* Extra bits for distance codes 0..29 */ 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}; /* decode literals and length/distance pairs */ do { symbol = decode(s, lencode); if (symbol < 0) return symbol; /* invalid symbol */ if (symbol < 256) { /* literal: symbol is the byte */ /* write out the literal */ if (s->out != NIL) { if (s->outcnt == s->outlen) return 1; s->out[s->outcnt] = symbol; } s->outcnt++; } else if (symbol > 256) { /* length */ /* get and compute length */ symbol -= 257; if (symbol >= 29) return -10; /* invalid fixed code */ len = lens[symbol] + bits(s, lext[symbol]); /* get and check distance */ symbol = decode(s, distcode); if (symbol < 0) return symbol; /* invalid symbol */ dist = dists[symbol] + bits(s, dext[symbol]); #ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR if (dist > s->outcnt) return -11; /* distance too far back */ #endif /* copy length bytes from distance bytes back */ if (s->out != NIL) { if (s->outcnt + len > s->outlen) return 1; while (len--) { s->out[s->outcnt] = #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR dist > s->outcnt ? 0 : #endif s->out[s->outcnt - dist]; s->outcnt++; } } else s->outcnt += len; } } while (symbol != 256); /* end of block symbol */ /* done with a valid fixed or dynamic block */ return 0; } /* * Process a fixed codes block. * * Format notes: * * - This block type can be useful for compressing small amounts of data for * which the size of the code descriptions in a dynamic block exceeds the * benefit of custom codes for that block. For fixed codes, no bits are * spent on code descriptions. Instead the code lengths for literal/length * codes and distance codes are fixed. The specific lengths for each symbol * can be seen in the "for" loops below. * * - The literal/length code is complete, but has two symbols that are invalid * and should result in an error if received. This cannot be implemented * simply as an incomplete code since those two symbols are in the "middle" * of the code. They are eight bits long and the longest literal/length\ * code is nine bits. Therefore the code must be constructed with those * symbols, and the invalid symbols must be detected after decoding. * * - The fixed distance codes also have two invalid symbols that should result * in an error if received. Since all of the distance codes are the same * length, this can be implemented as an incomplete code. Then the invalid * codes are detected while decoding. */ local int fixed(struct state *s) { static int virgin = 1; static short lencnt[MAXBITS+1], lensym[FIXLCODES]; static short distcnt[MAXBITS+1], distsym[MAXDCODES]; static struct huffman lencode, distcode; /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { int symbol; short lengths[FIXLCODES]; /* construct lencode and distcode */ lencode.count = lencnt; lencode.symbol = lensym; distcode.count = distcnt; distcode.symbol = distsym; /* literal/length table */ for (symbol = 0; symbol < 144; symbol++) lengths[symbol] = 8; for (; symbol < 256; symbol++) lengths[symbol] = 9; for (; symbol < 280; symbol++) lengths[symbol] = 7; for (; symbol < FIXLCODES; symbol++) lengths[symbol] = 8; construct(&lencode, lengths, FIXLCODES); /* distance table */ for (symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5; construct(&distcode, lengths, MAXDCODES); /* do this just once */ virgin = 0; } /* decode data until end-of-block code */ return codes(s, &lencode, &distcode); } /* * Process a dynamic codes block. * * Format notes: * * - A dynamic block starts with a description of the literal/length and * distance codes for that block. New dynamic blocks allow the compressor to * rapidly adapt to changing data with new codes optimized for that data. * * - The codes used by the deflate format are "canonical", which means that * the actual bits of the codes are generated in an unambiguous way simply * from the number of bits in each code. Therefore the code descriptions * are simply a list of code lengths for each symbol. * * - The code lengths are stored in order for the symbols, so lengths are * provided for each of the literal/length symbols, and for each of the * distance symbols. * * - If a symbol is not used in the block, this is represented by a zero as the * code length. This does not mean a zero-length code, but rather that no * code should be created for this symbol. There is no way in the deflate * format to represent a zero-length code. * * - The maximum number of bits in a code is 15, so the possible lengths for * any code are 1..15. * * - The fact that a length of zero is not permitted for a code has an * interesting consequence. Normally if only one symbol is used for a given * code, then in fact that code could be represented with zero bits. However * in deflate, that code has to be at least one bit. So for example, if * only a single distance base symbol appears in a block, then it will be * represented by a single code of length one, in particular one 0 bit. This * is an incomplete code, since if a 1 bit is received, it has no meaning, * and should result in an error. So incomplete distance codes of one symbol * should be permitted, and the receipt of invalid codes should be handled. * * - It is also possible to have a single literal/length code, but that code * must be the end-of-block code, since every dynamic block has one. This * is not the most efficient way to create an empty block (an empty fixed * block is fewer bits), but it is allowed by the format. So incomplete * literal/length codes of one symbol should also be permitted. * * - If there are only literal codes and no lengths, then there are no distance * codes. This is represented by one distance code with zero bits. * * - The list of up to 286 length/literal lengths and up to 30 distance lengths * are themselves compressed using Huffman codes and run-length encoding. In * the list of code lengths, a 0 symbol means no code, a 1..15 symbol means * that length, and the symbols 16, 17, and 18 are run-length instructions. * Each of 16, 17, and 18 are followed by extra bits to define the length of * the run. 16 copies the last length 3 to 6 times. 17 represents 3 to 10 * zero lengths, and 18 represents 11 to 138 zero lengths. Unused symbols * are common, hence the special coding for zero lengths. * * - The symbols for 0..18 are Huffman coded, and so that code must be * described first. This is simply a sequence of up to 19 three-bit values * representing no code (0) or the code length for that symbol (1..7). * * - A dynamic block starts with three fixed-size counts from which is computed * the number of literal/length code lengths, the number of distance code * lengths, and the number of code length code lengths (ok, you come up with * a better name!) in the code descriptions. For the literal/length and * distance codes, lengths after those provided are considered zero, i.e. no * code. The code length code lengths are received in a permuted order (see * the order[] array below) to make a short code length code length list more * likely. As it turns out, very short and very long codes are less likely * to be seen in a dynamic code description, hence what may appear initially * to be a peculiar ordering. * * - Given the number of literal/length code lengths (nlen) and distance code * lengths (ndist), then they are treated as one long list of nlen + ndist * code lengths. Therefore run-length coding can and often does cross the * boundary between the two sets of lengths. * * - So to summarize, the code description at the start of a dynamic block is * three counts for the number of code lengths for the literal/length codes, * the distance codes, and the code length codes. This is followed by the * code length code lengths, three bits each. This is used to construct the * code length code which is used to read the remainder of the lengths. Then * the literal/length code lengths and distance lengths are read as a single * set of lengths using the code length codes. Codes are constructed from * the resulting two sets of lengths, and then finally you can start * decoding actual compressed data in the block. * * - For reference, a "typical" size for the code description in a dynamic * block is around 80 bytes. */ local int dynamic(struct state *s) { int nlen, ndist, ncode; /* number of lengths in descriptor */ int index; /* index of lengths[] */ int err; /* construct() return value */ short lengths[MAXCODES]; /* descriptor code lengths */ short lencnt[MAXBITS+1], lensym[MAXLCODES]; /* lencode memory */ short distcnt[MAXBITS+1], distsym[MAXDCODES]; /* distcode memory */ struct huffman lencode, distcode; /* length and distance codes */ static const short order[19] = /* permutation of code length codes */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* construct lencode and distcode */ lencode.count = lencnt; lencode.symbol = lensym; distcode.count = distcnt; distcode.symbol = distsym; /* get number of lengths in each table, check lengths */ nlen = bits(s, 5) + 257; ndist = bits(s, 5) + 1; ncode = bits(s, 4) + 4; if (nlen > MAXLCODES || ndist > MAXDCODES) return -3; /* bad counts */ /* read code length code lengths (really), missing lengths are zero */ for (index = 0; index < ncode; index++) lengths[order[index]] = bits(s, 3); for (; index < 19; index++) lengths[order[index]] = 0; /* build huffman table for code lengths codes (use lencode temporarily) */ err = construct(&lencode, lengths, 19); if (err != 0) /* require complete code set here */ return -4; /* read length/literal and distance code length tables */ index = 0; while (index < nlen + ndist) { int symbol; /* decoded value */ int len; /* last length to repeat */ symbol = decode(s, &lencode); if (symbol < 0) return symbol; /* invalid symbol */ if (symbol < 16) /* length in 0..15 */ lengths[index++] = symbol; else { /* repeat instruction */ len = 0; /* assume repeating zeros */ if (symbol == 16) { /* repeat last length 3..6 times */ if (index == 0) return -5; /* no last length! */ len = lengths[index - 1]; /* last length */ symbol = 3 + bits(s, 2); } else if (symbol == 17) /* repeat zero 3..10 times */ symbol = 3 + bits(s, 3); else /* == 18, repeat zero 11..138 times */ symbol = 11 + bits(s, 7); if (index + symbol > nlen + ndist) return -6; /* too many lengths! */ while (symbol--) /* repeat last or zero symbol times */ lengths[index++] = len; } } /* check for end-of-block code -- there better be one! */ if (lengths[256] == 0) return -9; /* build huffman table for literal/length codes */ err = construct(&lencode, lengths, nlen); if (err && (err < 0 || nlen != lencode.count[0] + lencode.count[1])) return -7; /* incomplete code ok only for single length 1 code */ /* build huffman table for distance codes */ err = construct(&distcode, lengths + nlen, ndist); if (err && (err < 0 || ndist != distcode.count[0] + distcode.count[1])) return -8; /* incomplete code ok only for single length 1 code */ /* decode data until end-of-block code */ return codes(s, &lencode, &distcode); } /* * Inflate source to dest. On return, destlen and sourcelen are updated to the * size of the uncompressed data and the size of the deflate data respectively. * On success, the return value of puff() is zero. If there is an error in the * source data, i.e. it is not in the deflate format, then a negative value is * returned. If there is not enough input available or there is not enough * output space, then a positive error is returned. In that case, destlen and * sourcelen are not updated to facilitate retrying from the beginning with the * provision of more input data or more output space. In the case of invalid * inflate data (a negative error), the dest and source pointers are updated to * facilitate the debugging of deflators. * * puff() also has a mode to determine the size of the uncompressed output with * no output written. For this dest must be (unsigned char *)0. In this case, * the input value of *destlen is ignored, and on return *destlen is set to the * size of the uncompressed output. * * The return codes are: * * 2: available inflate data did not terminate * 1: output space exhausted before completing inflate * 0: successful inflate * -1: invalid block type (type == 3) * -2: stored block length did not match one's complement * -3: dynamic block code description: too many length or distance codes * -4: dynamic block code description: code lengths codes incomplete * -5: dynamic block code description: repeat lengths with no first length * -6: dynamic block code description: repeat more than specified lengths * -7: dynamic block code description: invalid literal/length code lengths * -8: dynamic block code description: invalid distance code lengths * -9: dynamic block code description: missing end-of-block code * -10: invalid literal/length or distance code in fixed or dynamic block * -11: distance is too far back in fixed or dynamic block * * Format notes: * * - Three bits are read for each block to determine the kind of block and * whether or not it is the last block. Then the block is decoded and the * process repeated if it was not the last block. * * - The leftover bits in the last byte of the deflate data after the last * block (if it was a fixed or dynamic block) are undefined and have no * expected values to check. */ int puff(unsigned char *dest, /* pointer to destination pointer */ unsigned long *destlen, /* amount of output space */ const unsigned char *source, /* pointer to source data pointer */ unsigned long *sourcelen) /* amount of input available */ { struct state s; /* input/output state */ int last, type; /* block information */ int err; /* return value */ /* initialize output state */ s.out = dest; s.outlen = *destlen; /* ignored if dest is NIL */ s.outcnt = 0; /* initialize input state */ s.in = source; s.inlen = *sourcelen; s.incnt = 0; s.bitbuf = 0; s.bitcnt = 0; /* return if bits() or decode() tries to read past available input */ if (setjmp(s.env) != 0) /* if came back here via longjmp() */ err = 2; /* then skip do-loop, return error */ else { /* process blocks until last block or error */ do { last = bits(&s, 1); /* one if last block */ type = bits(&s, 2); /* block type 0..3 */ err = type == 0 ? stored(&s) : (type == 1 ? fixed(&s) : (type == 2 ? dynamic(&s) : -1)); /* type == 3, invalid */ if (err != 0) break; /* return with error */ } while (!last); } /* update the lengths and return */ if (err <= 0) { *destlen = s.outcnt; *sourcelen = s.incnt; } return err; } |
Added compat/zlib/contrib/puff/puff.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* puff.h Copyright (C) 2002-2013 Mark Adler, all rights reserved version 2.3, 21 Jan 2013 This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Mark Adler madler@alumni.caltech.edu */ /* * See puff.c for purpose and usage. */ #ifndef NIL # define NIL ((unsigned char *)0) /* for no output option */ #endif int puff(unsigned char *dest, /* pointer to destination pointer */ unsigned long *destlen, /* amount of output space */ const unsigned char *source, /* pointer to source data pointer */ unsigned long *sourcelen); /* amount of input available */ |
Added compat/zlib/contrib/puff/pufftest.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 | /* * pufftest.c * Copyright (C) 2002-2013 Mark Adler * For conditions of distribution and use, see copyright notice in puff.h * version 2.3, 21 Jan 2013 */ /* Example of how to use puff(). Usage: puff [-w] [-f] [-nnn] file ... | puff [-w] [-f] [-nnn] where file is the input file with deflate data, nnn is the number of bytes of input to skip before inflating (e.g. to skip a zlib or gzip header), and -w is used to write the decompressed data to stdout. -f is for coverage testing, and causes pufftest to fail with not enough output space (-f does a write like -w, so -w is not required). */ #include <stdio.h> #include <stdlib.h> #include "puff.h" #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) # include <fcntl.h> # include <io.h> # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) #else # define SET_BINARY_MODE(file) #endif #define local static /* Return size times approximately the cube root of 2, keeping the result as 1, 3, or 5 times a power of 2 -- the result is always > size, until the result is the maximum value of an unsigned long, where it remains. This is useful to keep reallocations less than ~33% over the actual data. */ local size_t bythirds(size_t size) { int n; size_t m; m = size; for (n = 0; m; n++) m >>= 1; if (n < 3) return size + 1; n -= 3; m = size >> n; m += m == 6 ? 2 : 1; m <<= n; return m > size ? m : (size_t)(-1); } /* Read the input file *name, or stdin if name is NULL, into allocated memory. Reallocate to larger buffers until the entire file is read in. Return a pointer to the allocated data, or NULL if there was a memory allocation failure. *len is the number of bytes of data read from the input file (even if load() returns NULL). If the input file was empty or could not be opened or read, *len is zero. */ local void *load(const char *name, size_t *len) { size_t size; void *buf, *swap; FILE *in; *len = 0; buf = malloc(size = 4096); if (buf == NULL) return NULL; in = name == NULL ? stdin : fopen(name, "rb"); if (in != NULL) { for (;;) { *len += fread((char *)buf + *len, 1, size - *len, in); if (*len < size) break; size = bythirds(size); if (size == *len || (swap = realloc(buf, size)) == NULL) { free(buf); buf = NULL; break; } buf = swap; } fclose(in); } return buf; } int main(int argc, char **argv) { int ret, put = 0, fail = 0; unsigned skip = 0; char *arg, *name = NULL; unsigned char *source = NULL, *dest; size_t len = 0; unsigned long sourcelen, destlen; /* process arguments */ while (arg = *++argv, --argc) if (arg[0] == '-') { if (arg[1] == 'w' && arg[2] == 0) put = 1; else if (arg[1] == 'f' && arg[2] == 0) fail = 1, put = 1; else if (arg[1] >= '0' && arg[1] <= '9') skip = (unsigned)atoi(arg + 1); else { fprintf(stderr, "invalid option %s\n", arg); return 3; } } else if (name != NULL) { fprintf(stderr, "only one file name allowed\n"); return 3; } else name = arg; source = load(name, &len); if (source == NULL) { fprintf(stderr, "memory allocation failure\n"); return 4; } if (len == 0) { fprintf(stderr, "could not read %s, or it was empty\n", name == NULL ? "<stdin>" : name); free(source); return 3; } if (skip >= len) { fprintf(stderr, "skip request of %d leaves no input\n", skip); free(source); return 3; } /* test inflate data with offset skip */ len -= skip; sourcelen = (unsigned long)len; ret = puff(NIL, &destlen, source + skip, &sourcelen); if (ret) fprintf(stderr, "puff() failed with return code %d\n", ret); else { fprintf(stderr, "puff() succeeded uncompressing %lu bytes\n", destlen); if (sourcelen < len) fprintf(stderr, "%lu compressed bytes unused\n", len - sourcelen); } /* if requested, inflate again and write decompressed data to stdout */ if (put && ret == 0) { if (fail) destlen >>= 1; dest = malloc(destlen); if (dest == NULL) { fprintf(stderr, "memory allocation failure\n"); free(source); return 4; } puff(dest, &destlen, source + skip, &sourcelen); SET_BINARY_MODE(stdout); fwrite(dest, 1, destlen, stdout); free(dest); } /* clean up */ free(source); return ret; } |
Added compat/zlib/contrib/puff/zeros.raw.
cannot compute difference between binary files
Added compat/zlib/contrib/testzlib/testzlib.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 | #include <stdio.h> #include <stdlib.h> #include <windows.h> #include "zlib.h" void MyDoMinus64(LARGE_INTEGER *R,LARGE_INTEGER A,LARGE_INTEGER B) { R->HighPart = A.HighPart - B.HighPart; if (A.LowPart >= B.LowPart) R->LowPart = A.LowPart - B.LowPart; else { R->LowPart = A.LowPart - B.LowPart; R->HighPart --; } } #ifdef _M_X64 // see http://msdn2.microsoft.com/library/twchhe95(en-us,vs.80).aspx for __rdtsc unsigned __int64 __rdtsc(void); void BeginCountRdtsc(LARGE_INTEGER * pbeginTime64) { // printf("rdtsc = %I64x\n",__rdtsc()); pbeginTime64->QuadPart=__rdtsc(); } LARGE_INTEGER GetResRdtsc(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf) { LARGE_INTEGER LIres; unsigned _int64 res=__rdtsc()-((unsigned _int64)(beginTime64.QuadPart)); LIres.QuadPart=res; // printf("rdtsc = %I64x\n",__rdtsc()); return LIres; } #else #ifdef _M_IX86 void myGetRDTSC32(LARGE_INTEGER * pbeginTime64) { DWORD dwEdx,dwEax; _asm { rdtsc mov dwEax,eax mov dwEdx,edx } pbeginTime64->LowPart=dwEax; pbeginTime64->HighPart=dwEdx; } void BeginCountRdtsc(LARGE_INTEGER * pbeginTime64) { myGetRDTSC32(pbeginTime64); } LARGE_INTEGER GetResRdtsc(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf) { LARGE_INTEGER LIres,endTime64; myGetRDTSC32(&endTime64); LIres.LowPart=LIres.HighPart=0; MyDoMinus64(&LIres,endTime64,beginTime64); return LIres; } #else void myGetRDTSC32(LARGE_INTEGER * pbeginTime64) { } void BeginCountRdtsc(LARGE_INTEGER * pbeginTime64) { } LARGE_INTEGER GetResRdtsc(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf) { LARGE_INTEGER lr; lr.QuadPart=0; return lr; } #endif #endif void BeginCountPerfCounter(LARGE_INTEGER * pbeginTime64,BOOL fComputeTimeQueryPerf) { if ((!fComputeTimeQueryPerf) || (!QueryPerformanceCounter(pbeginTime64))) { pbeginTime64->LowPart = GetTickCount(); pbeginTime64->HighPart = 0; } } DWORD GetMsecSincePerfCounter(LARGE_INTEGER beginTime64,BOOL fComputeTimeQueryPerf) { LARGE_INTEGER endTime64,ticksPerSecond,ticks; DWORDLONG ticksShifted,tickSecShifted; DWORD dwLog=16+0; DWORD dwRet; if ((!fComputeTimeQueryPerf) || (!QueryPerformanceCounter(&endTime64))) dwRet = (GetTickCount() - beginTime64.LowPart)*1; else { MyDoMinus64(&ticks,endTime64,beginTime64); QueryPerformanceFrequency(&ticksPerSecond); { ticksShifted = Int64ShrlMod32(*(DWORDLONG*)&ticks,dwLog); tickSecShifted = Int64ShrlMod32(*(DWORDLONG*)&ticksPerSecond,dwLog); } dwRet = (DWORD)((((DWORD)ticksShifted)*1000)/(DWORD)(tickSecShifted)); dwRet *=1; } return dwRet; } int ReadFileMemory(const char* filename,long* plFileSize,unsigned char** pFilePtr) { FILE* stream; unsigned char* ptr; int retVal=1; stream=fopen(filename, "rb"); if (stream==NULL) return 0; fseek(stream,0,SEEK_END); *plFileSize=ftell(stream); fseek(stream,0,SEEK_SET); ptr=malloc((*plFileSize)+1); if (ptr==NULL) retVal=0; else { if (fread(ptr, 1, *plFileSize,stream) != (*plFileSize)) retVal=0; } fclose(stream); *pFilePtr=ptr; return retVal; } int main(int argc, char *argv[]) { int BlockSizeCompress=0x8000; int BlockSizeUncompress=0x8000; int cprLevel=Z_DEFAULT_COMPRESSION ; long lFileSize; unsigned char* FilePtr; long lBufferSizeCpr; long lBufferSizeUncpr; long lCompressedSize=0; unsigned char* CprPtr; unsigned char* UncprPtr; long lSizeCpr,lSizeUncpr; DWORD dwGetTick,dwMsecQP; LARGE_INTEGER li_qp,li_rdtsc,dwResRdtsc; if (argc<=1) { printf("run TestZlib <File> [BlockSizeCompress] [BlockSizeUncompress] [compres. level]\n"); return 0; } if (ReadFileMemory(argv[1],&lFileSize,&FilePtr)==0) { printf("error reading %s\n",argv[1]); return 1; } else printf("file %s read, %ld bytes\n",argv[1],lFileSize); if (argc>=3) BlockSizeCompress=atol(argv[2]); if (argc>=4) BlockSizeUncompress=atol(argv[3]); if (argc>=5) cprLevel=(int)atol(argv[4]); lBufferSizeCpr = lFileSize + (lFileSize/0x10) + 0x200; lBufferSizeUncpr = lBufferSizeCpr; CprPtr=(unsigned char*)malloc(lBufferSizeCpr + BlockSizeCompress); BeginCountPerfCounter(&li_qp,TRUE); dwGetTick=GetTickCount(); BeginCountRdtsc(&li_rdtsc); { z_stream zcpr; int ret=Z_OK; long lOrigToDo = lFileSize; long lOrigDone = 0; int step=0; memset(&zcpr,0,sizeof(z_stream)); deflateInit(&zcpr,cprLevel); zcpr.next_in = FilePtr; zcpr.next_out = CprPtr; do { long all_read_before = zcpr.total_in; zcpr.avail_in = min(lOrigToDo,BlockSizeCompress); zcpr.avail_out = BlockSizeCompress; ret=deflate(&zcpr,(zcpr.avail_in==lOrigToDo) ? Z_FINISH : Z_SYNC_FLUSH); lOrigDone += (zcpr.total_in-all_read_before); lOrigToDo -= (zcpr.total_in-all_read_before); step++; } while (ret==Z_OK); lSizeCpr=zcpr.total_out; deflateEnd(&zcpr); dwGetTick=GetTickCount()-dwGetTick; dwMsecQP=GetMsecSincePerfCounter(li_qp,TRUE); dwResRdtsc=GetResRdtsc(li_rdtsc,TRUE); printf("total compress size = %u, in %u step\n",lSizeCpr,step); printf("time = %u msec = %f sec\n",dwGetTick,dwGetTick/(double)1000.); printf("defcpr time QP = %u msec = %f sec\n",dwMsecQP,dwMsecQP/(double)1000.); printf("defcpr result rdtsc = %I64x\n\n",dwResRdtsc.QuadPart); } CprPtr=(unsigned char*)realloc(CprPtr,lSizeCpr); UncprPtr=(unsigned char*)malloc(lBufferSizeUncpr + BlockSizeUncompress); BeginCountPerfCounter(&li_qp,TRUE); dwGetTick=GetTickCount(); BeginCountRdtsc(&li_rdtsc); { z_stream zcpr; int ret=Z_OK; long lOrigToDo = lSizeCpr; long lOrigDone = 0; int step=0; memset(&zcpr,0,sizeof(z_stream)); inflateInit(&zcpr); zcpr.next_in = CprPtr; zcpr.next_out = UncprPtr; do { long all_read_before = zcpr.total_in; zcpr.avail_in = min(lOrigToDo,BlockSizeUncompress); zcpr.avail_out = BlockSizeUncompress; ret=inflate(&zcpr,Z_SYNC_FLUSH); lOrigDone += (zcpr.total_in-all_read_before); lOrigToDo -= (zcpr.total_in-all_read_before); step++; } while (ret==Z_OK); lSizeUncpr=zcpr.total_out; inflateEnd(&zcpr); dwGetTick=GetTickCount()-dwGetTick; dwMsecQP=GetMsecSincePerfCounter(li_qp,TRUE); dwResRdtsc=GetResRdtsc(li_rdtsc,TRUE); printf("total uncompress size = %u, in %u step\n",lSizeUncpr,step); printf("time = %u msec = %f sec\n",dwGetTick,dwGetTick/(double)1000.); printf("uncpr time QP = %u msec = %f sec\n",dwMsecQP,dwMsecQP/(double)1000.); printf("uncpr result rdtsc = %I64x\n\n",dwResRdtsc.QuadPart); } if (lSizeUncpr==lFileSize) { if (memcmp(FilePtr,UncprPtr,lFileSize)==0) printf("compare ok\n"); } return 0; } |
Added compat/zlib/contrib/testzlib/testzlib.txt.
> > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 | To build testzLib with Visual Studio 2005: copy to a directory file from : - root of zLib tree - contrib/testzlib - contrib/masmx86 - contrib/masmx64 - contrib/vstudio/vc7 and open testzlib8.sln |
Added compat/zlib/contrib/untgz/Makefile.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | CC=cc CFLAGS=-g untgz: untgz.o ../../libz.a $(CC) $(CFLAGS) -o untgz untgz.o -L../.. -lz untgz.o: untgz.c ../../zlib.h $(CC) $(CFLAGS) -c -I../.. untgz.c ../../libz.a: cd ../..; ./configure; make clean: rm -f untgz untgz.o *~ |
Added compat/zlib/contrib/untgz/Makefile.msc.
> > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | CC=cl CFLAGS=-MD untgz.exe: untgz.obj ..\..\zlib.lib $(CC) $(CFLAGS) untgz.obj ..\..\zlib.lib untgz.obj: untgz.c ..\..\zlib.h $(CC) $(CFLAGS) -c -I..\.. untgz.c ..\..\zlib.lib: cd ..\.. $(MAKE) -f win32\makefile.msc cd contrib\untgz clean: -del untgz.obj -del untgz.exe |
Added compat/zlib/contrib/untgz/untgz.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 | /* * untgz.c -- Display contents and extract files from a gzip'd TAR file * * written by Pedro A. Aranda Gutierrez <paag@tid.es> * adaptation to Unix by Jean-loup Gailly <jloup@gzip.org> * various fixes by Cosmin Truta <cosmint@cs.ubbcluj.ro> * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <errno.h> #include "zlib.h" #ifdef _WIN32 # include <direct.h> # include <io.h> # include <windows.h> # ifndef F_OK # define F_OK 0 # endif # define mkdir(dirname,mode) _mkdir(dirname) # ifdef _MSC_VER # define access(path,mode) _access(path,mode) # define chmod(path,mode) _chmod(path,mode) # define strdup(str) _strdup(str) # endif #else # include <sys/stat.h> # include <unistd.h> # include <utime.h> #endif /* values used in typeflag field */ #define REGTYPE '0' /* regular file */ #define AREGTYPE '\0' /* regular file */ #define LNKTYPE '1' /* link */ #define SYMTYPE '2' /* reserved */ #define CHRTYPE '3' /* character special */ #define BLKTYPE '4' /* block special */ #define DIRTYPE '5' /* directory */ #define FIFOTYPE '6' /* FIFO special */ #define CONTTYPE '7' /* reserved */ /* GNU tar extensions */ #define GNUTYPE_DUMPDIR 'D' /* file names from dumped directory */ #define GNUTYPE_LONGLINK 'K' /* long link name */ #define GNUTYPE_LONGNAME 'L' /* long file name */ #define GNUTYPE_MULTIVOL 'M' /* continuation of file from another volume */ #define GNUTYPE_NAMES 'N' /* file name that does not fit into main hdr */ #define GNUTYPE_SPARSE 'S' /* sparse file */ #define GNUTYPE_VOLHDR 'V' /* tape/volume header */ /* tar header */ #define BLOCKSIZE 512 #define SHORTNAMESIZE 100 struct tar_header { /* byte offset */ char name[100]; /* 0 */ char mode[8]; /* 100 */ char uid[8]; /* 108 */ char gid[8]; /* 116 */ char size[12]; /* 124 */ char mtime[12]; /* 136 */ char chksum[8]; /* 148 */ char typeflag; /* 156 */ char linkname[100]; /* 157 */ char magic[6]; /* 257 */ char version[2]; /* 263 */ char uname[32]; /* 265 */ char gname[32]; /* 297 */ char devmajor[8]; /* 329 */ char devminor[8]; /* 337 */ char prefix[155]; /* 345 */ /* 500 */ }; union tar_buffer { char buffer[BLOCKSIZE]; struct tar_header header; }; struct attr_item { struct attr_item *next; char *fname; int mode; time_t time; }; enum { TGZ_EXTRACT, TGZ_LIST, TGZ_INVALID }; char *prog; void error(const char *msg) { fprintf(stderr, "%s: %s\n", prog, msg); exit(1); } const char *TGZsuffix[] = { "\0", ".tar", ".tar.gz", ".taz", ".tgz", NULL }; /* return the file name of the TGZ archive */ /* or NULL if it does not exist */ char *TGZfname (const char *arcname) { static char buffer[1024]; int origlen,i; strcpy(buffer,arcname); origlen = strlen(buffer); for (i=0; TGZsuffix[i]; i++) { strcpy(buffer+origlen,TGZsuffix[i]); if (access(buffer,F_OK) == 0) return buffer; } return NULL; } /* error message for the filename */ void TGZnotfound (const char *arcname) { int i; fprintf(stderr,"%s: Couldn't find ",prog); for (i=0;TGZsuffix[i];i++) fprintf(stderr,(TGZsuffix[i+1]) ? "%s%s, " : "or %s%s\n", arcname, TGZsuffix[i]); exit(1); } /* convert octal digits to int */ /* on error return -1 */ int getoct (char *p,int width) { int result = 0; char c; while (width--) { c = *p++; if (c == 0) break; if (c == ' ') continue; if (c < '0' || c > '7') return -1; result = result * 8 + (c - '0'); } return result; } /* convert time_t to string */ /* use the "YYYY/MM/DD hh:mm:ss" format */ char *strtime (time_t *t) { struct tm *local; static char result[32]; local = localtime(t); sprintf(result,"%4d/%02d/%02d %02d:%02d:%02d", local->tm_year+1900, local->tm_mon+1, local->tm_mday, local->tm_hour, local->tm_min, local->tm_sec); return result; } /* set file time */ int setfiletime (char *fname,time_t ftime) { #ifdef _WIN32 static int isWinNT = -1; SYSTEMTIME st; FILETIME locft, modft; struct tm *loctm; HANDLE hFile; int result; loctm = localtime(&ftime); if (loctm == NULL) return -1; st.wYear = (WORD)loctm->tm_year + 1900; st.wMonth = (WORD)loctm->tm_mon + 1; st.wDayOfWeek = (WORD)loctm->tm_wday; st.wDay = (WORD)loctm->tm_mday; st.wHour = (WORD)loctm->tm_hour; st.wMinute = (WORD)loctm->tm_min; st.wSecond = (WORD)loctm->tm_sec; st.wMilliseconds = 0; if (!SystemTimeToFileTime(&st, &locft) || !LocalFileTimeToFileTime(&locft, &modft)) return -1; if (isWinNT < 0) isWinNT = (GetVersion() < 0x80000000) ? 1 : 0; hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, (isWinNT ? FILE_FLAG_BACKUP_SEMANTICS : 0), NULL); if (hFile == INVALID_HANDLE_VALUE) return -1; result = SetFileTime(hFile, NULL, NULL, &modft) ? 0 : -1; CloseHandle(hFile); return result; #else struct utimbuf settime; settime.actime = settime.modtime = ftime; return utime(fname,&settime); #endif } /* push file attributes */ void push_attr(struct attr_item **list,char *fname,int mode,time_t time) { struct attr_item *item; item = (struct attr_item *)malloc(sizeof(struct attr_item)); if (item == NULL) error("Out of memory"); item->fname = strdup(fname); item->mode = mode; item->time = time; item->next = *list; *list = item; } /* restore file attributes */ void restore_attr(struct attr_item **list) { struct attr_item *item, *prev; for (item = *list; item != NULL; ) { setfiletime(item->fname,item->time); chmod(item->fname,item->mode); prev = item; item = item->next; free(prev); } *list = NULL; } /* match regular expression */ #define ISSPECIAL(c) (((c) == '*') || ((c) == '/')) int ExprMatch (char *string,char *expr) { while (1) { if (ISSPECIAL(*expr)) { if (*expr == '/') { if (*string != '\\' && *string != '/') return 0; string ++; expr++; } else if (*expr == '*') { if (*expr ++ == 0) return 1; while (*++string != *expr) if (*string == 0) return 0; } } else { if (*string != *expr) return 0; if (*expr++ == 0) return 1; string++; } } } /* recursive mkdir */ /* abort on ENOENT; ignore other errors like "directory already exists" */ /* return 1 if OK */ /* 0 on error */ int makedir (char *newdir) { char *buffer = strdup(newdir); char *p; int len = strlen(buffer); if (len <= 0) { free(buffer); return 0; } if (buffer[len-1] == '/') { buffer[len-1] = '\0'; } if (mkdir(buffer, 0755) == 0) { free(buffer); return 1; } p = buffer+1; while (1) { char hold; while(*p && *p != '\\' && *p != '/') p++; hold = *p; *p = 0; if ((mkdir(buffer, 0755) == -1) && (errno == ENOENT)) { fprintf(stderr,"%s: Couldn't create directory %s\n",prog,buffer); free(buffer); return 0; } if (hold == 0) break; *p++ = hold; } free(buffer); return 1; } int matchname (int arg,int argc,char **argv,char *fname) { if (arg == argc) /* no arguments given (untgz tgzarchive) */ return 1; while (arg < argc) if (ExprMatch(fname,argv[arg++])) return 1; return 0; /* ignore this for the moment being */ } /* tar file list or extract */ int tar (gzFile in,int action,int arg,int argc,char **argv) { union tar_buffer buffer; int len; int err; int getheader = 1; int remaining = 0; FILE *outfile = NULL; char fname[BLOCKSIZE]; int tarmode; time_t tartime; struct attr_item *attributes = NULL; if (action == TGZ_LIST) printf(" date time size file\n" " ---------- -------- --------- -------------------------------------\n"); while (1) { len = gzread(in, &buffer, BLOCKSIZE); if (len < 0) error(gzerror(in, &err)); /* * Always expect complete blocks to process * the tar information. */ if (len != BLOCKSIZE) { action = TGZ_INVALID; /* force error exit */ remaining = 0; /* force I/O cleanup */ } /* * If we have to get a tar header */ if (getheader >= 1) { /* * if we met the end of the tar * or the end-of-tar block, * we are done */ if (len == 0 || buffer.header.name[0] == 0) break; tarmode = getoct(buffer.header.mode,8); tartime = (time_t)getoct(buffer.header.mtime,12); if (tarmode == -1 || tartime == (time_t)-1) { buffer.header.name[0] = 0; action = TGZ_INVALID; } if (getheader == 1) { strncpy(fname,buffer.header.name,SHORTNAMESIZE); if (fname[SHORTNAMESIZE-1] != 0) fname[SHORTNAMESIZE] = 0; } else { /* * The file name is longer than SHORTNAMESIZE */ if (strncmp(fname,buffer.header.name,SHORTNAMESIZE-1) != 0) error("bad long name"); getheader = 1; } /* * Act according to the type flag */ switch (buffer.header.typeflag) { case DIRTYPE: if (action == TGZ_LIST) printf(" %s <dir> %s\n",strtime(&tartime),fname); if (action == TGZ_EXTRACT) { makedir(fname); push_attr(&attributes,fname,tarmode,tartime); } break; case REGTYPE: case AREGTYPE: remaining = getoct(buffer.header.size,12); if (remaining == -1) { action = TGZ_INVALID; break; } if (action == TGZ_LIST) printf(" %s %9d %s\n",strtime(&tartime),remaining,fname); else if (action == TGZ_EXTRACT) { if (matchname(arg,argc,argv,fname)) { outfile = fopen(fname,"wb"); if (outfile == NULL) { /* try creating directory */ char *p = strrchr(fname, '/'); if (p != NULL) { *p = '\0'; makedir(fname); *p = '/'; outfile = fopen(fname,"wb"); } } if (outfile != NULL) printf("Extracting %s\n",fname); else fprintf(stderr, "%s: Couldn't create %s",prog,fname); } else outfile = NULL; } getheader = 0; break; case GNUTYPE_LONGLINK: case GNUTYPE_LONGNAME: remaining = getoct(buffer.header.size,12); if (remaining < 0 || remaining >= BLOCKSIZE) { action = TGZ_INVALID; break; } len = gzread(in, fname, BLOCKSIZE); if (len < 0) error(gzerror(in, &err)); if (fname[BLOCKSIZE-1] != 0 || (int)strlen(fname) > remaining) { action = TGZ_INVALID; break; } getheader = 2; break; default: if (action == TGZ_LIST) printf(" %s <---> %s\n",strtime(&tartime),fname); break; } } else { unsigned int bytes = (remaining > BLOCKSIZE) ? BLOCKSIZE : remaining; if (outfile != NULL) { if (fwrite(&buffer,sizeof(char),bytes,outfile) != bytes) { fprintf(stderr, "%s: Error writing %s -- skipping\n",prog,fname); fclose(outfile); outfile = NULL; remove(fname); } } remaining -= bytes; } if (remaining == 0) { getheader = 1; if (outfile != NULL) { fclose(outfile); outfile = NULL; if (action != TGZ_INVALID) push_attr(&attributes,fname,tarmode,tartime); } } /* * Abandon if errors are found */ if (action == TGZ_INVALID) { error("broken archive"); break; } } /* * Restore file modes and time stamps */ restore_attr(&attributes); if (gzclose(in) != Z_OK) error("failed gzclose"); return 0; } /* ============================================================ */ void help(int exitval) { printf("untgz version 0.2.1\n" " using zlib version %s\n\n", zlibVersion()); printf("Usage: untgz file.tgz extract all files\n" " untgz file.tgz fname ... extract selected files\n" " untgz -l file.tgz list archive contents\n" " untgz -h display this help\n"); exit(exitval); } /* ============================================================ */ #if defined(WIN32) && defined(__GNUC__) int _CRT_glob = 0; /* disable argument globbing in MinGW */ #endif int main(int argc,char **argv) { int action = TGZ_EXTRACT; int arg = 1; char *TGZfile; gzFile f; prog = strrchr(argv[0],'\\'); if (prog == NULL) { prog = strrchr(argv[0],'/'); if (prog == NULL) { prog = strrchr(argv[0],':'); if (prog == NULL) prog = argv[0]; else prog++; } else prog++; } else prog++; if (argc == 1) help(0); if (strcmp(argv[arg],"-l") == 0) { action = TGZ_LIST; if (argc == ++arg) help(0); } else if (strcmp(argv[arg],"-h") == 0) { help(0); } if ((TGZfile = TGZfname(argv[arg])) == NULL) TGZnotfound(argv[arg]); ++arg; if ((action == TGZ_LIST) && (arg != argc)) help(1); /* * Process the TGZ file */ switch(action) { case TGZ_LIST: case TGZ_EXTRACT: f = gzopen(TGZfile,"rb"); if (f == NULL) { fprintf(stderr,"%s: Couldn't gzopen %s\n",prog,TGZfile); return 1; } exit(tar(f, action, arg, argc, argv)); break; default: error("Unknown option"); exit(1); } return 0; } |
Added compat/zlib/contrib/vstudio/readme.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 | Building instructions for the DLL versions of Zlib 1.3.1 ======================================================== This directory contains projects that build zlib and minizip using Microsoft Visual C++ 9.0/10.0. You don't need to build these projects yourself. You can download the binaries from: http://www.winimage.com/zLibDll More information can be found at this site. Build instructions for Visual Studio 2008 (32 bits or 64 bits) -------------------------------------------------------------- - Decompress current zlib, including all contrib/* files - Open contrib\vstudio\vc9\zlibvc.sln with Microsoft Visual C++ 2008 - Or run: vcbuild /rebuild contrib\vstudio\vc9\zlibvc.sln "Release|Win32" Build instructions for Visual Studio 2010 (32 bits or 64 bits) -------------------------------------------------------------- - Decompress current zlib, including all contrib/* files - Open contrib\vstudio\vc10\zlibvc.sln with Microsoft Visual C++ 2010 Build instructions for Visual Studio 2012 (32 bits or 64 bits) -------------------------------------------------------------- - Decompress current zlib, including all contrib/* files - Open contrib\vstudio\vc11\zlibvc.sln with Microsoft Visual C++ 2012 Build instructions for Visual Studio 2013 (32 bits or 64 bits) -------------------------------------------------------------- - Decompress current zlib, including all contrib/* files - Open contrib\vstudio\vc12\zlibvc.sln with Microsoft Visual C++ 2013 Build instructions for Visual Studio 2015 (32 bits or 64 bits) -------------------------------------------------------------- - Decompress current zlib, including all contrib/* files - Open contrib\vstudio\vc14\zlibvc.sln with Microsoft Visual C++ 2015 Build instructions for Visual Studio 2022 (64 bits) -------------------------------------------------------------- - Decompress current zlib, including all contrib/* files - Open contrib\vstudio\vc143\zlibvc.sln with Microsoft Visual C++ 2022 Important --------- - To use zlibwapi.dll in your application, you must define the macro ZLIB_WINAPI when compiling your application's source files. Additional notes ---------------- - This DLL, named zlibwapi.dll, is compatible to the old zlib.dll built by Gilles Vollant from the zlib 1.1.x sources, and distributed at http://www.winimage.com/zLibDll It uses the WINAPI calling convention for the exported functions, and includes the minizip functionality. If your application needs that particular build of zlib.dll, you can rename zlibwapi.dll to zlib.dll. - The new DLL was renamed because there exist several incompatible versions of zlib.dll on the Internet. - There is also an official DLL build of zlib, named zlib1.dll. This one is exporting the functions using the CDECL convention. See the file win32\DLL_FAQ.txt found in this zlib distribution. - There used to be a ZLIB_DLL macro in zlib 1.1.x, but now this symbol has a slightly different effect. To avoid compatibility problems, do not define it here. Gilles Vollant info@winimage.com Visual Studio 2013, 2015, and 2022 Projects from Sean Hunt seandhunt_7@yahoo.com |
Added compat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{C52F9E7B-498A-42BE-8DB4-85A15694382A}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\minizip\miniunz.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters.
> > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Source Files"> <UniqueIdentifier>{048af943-022b-4db6-beeb-a54c34774ee2}</UniqueIdentifier> <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat</Extensions> </Filter> <Filter Include="Header Files"> <UniqueIdentifier>{c1d600d2-888f-4aea-b73e-8b0dd9befa0c}</UniqueIdentifier> <Extensions>h;hpp;hxx;hm;inl;inc</Extensions> </Filter> <Filter Include="Resource Files"> <UniqueIdentifier>{0844199a-966b-4f19-81db-1e0125e141b9}</UniqueIdentifier> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions> </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="..\..\minizip\miniunz.c"> <Filter>Source Files</Filter> </ClCompile> </ItemGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/minizip.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniZip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniZip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniZip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniZip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\minizip\minizip.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters.
> > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Source Files"> <UniqueIdentifier>{c0419b40-bf50-40da-b153-ff74215b79de}</UniqueIdentifier> <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat</Extensions> </Filter> <Filter Include="Header Files"> <UniqueIdentifier>{bb87b070-735b-478e-92ce-7383abb2f36c}</UniqueIdentifier> <Extensions>h;hpp;hxx;hm;inl;inc</Extensions> </Filter> <Filter Include="Resource Files"> <UniqueIdentifier>{f46ab6a6-548f-43cb-ae96-681abb5bd5db}</UniqueIdentifier> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions> </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="..\..\minizip\minizip.c"> <Filter>Source Files</Filter> </ClCompile> </ItemGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}</ProjectGuid> <RootNamespace>testzlib</RootNamespace> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\testzlib\testzlib.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Source Files"> <UniqueIdentifier>{c1f6a2e3-5da5-4955-8653-310d3efe05a9}</UniqueIdentifier> <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat</Extensions> </Filter> <Filter Include="Header Files"> <UniqueIdentifier>{c2aaffdc-2c95-4d6f-8466-4bec5890af2c}</UniqueIdentifier> <Extensions>h;hpp;hxx;hm;inl;inc</Extensions> </Filter> <Filter Include="Resource Files"> <UniqueIdentifier>{c274fe07-05f2-461c-964b-f6341e4e7eb5}</UniqueIdentifier> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions> </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\compress.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\crc32.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\deflate.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\infback.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\inffast.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\inflate.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\inftrees.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\testzlib\testzlib.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\trees.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\uncompr.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\zutil.c"> <Filter>Source Files</Filter> </ClCompile> </ItemGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{C52F9E7B-498A-42BE-8DB4-85A15694366A}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\testzlib\testzlib.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters.
> > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Source Files"> <UniqueIdentifier>{fa61a89f-93fc-4c89-b29e-36224b7592f4}</UniqueIdentifier> <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat</Extensions> </Filter> <Filter Include="Header Files"> <UniqueIdentifier>{d4b85da0-2ba2-4934-b57f-e2584e3848ee}</UniqueIdentifier> <Extensions>h;hpp;hxx;hm;inl;inc</Extensions> </Filter> <Filter Include="Resource Files"> <UniqueIdentifier>{e573e075-00bd-4a7d-bd67-a8cc9bfc5aca}</UniqueIdentifier> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions> </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="..\..\testzlib\testzlib.c"> <Filter>Source Files</Filter> </ClCompile> </ItemGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/zlib.rc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include <windows.h> #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE FILEVERSION 1, 3, 1, 0 PRODUCTVERSION 1, 3, 1, 0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0 FILEOS VOS_DOS_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE 0 // not used BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" //language ID = U.S. English, char set = Windows, Multilingual BEGIN VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0" VALUE "FileVersion", "1.3.1\0" VALUE "InternalName", "zlib\0" VALUE "OriginalFilename", "zlibwapi.dll\0" VALUE "ProductName", "ZLib.DLL\0" VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 1252 END END |
Added compat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}</ProjectGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\gzclose.c" /> <ClCompile Include="..\..\..\gzlib.c" /> <ClCompile Include="..\..\..\gzread.c" /> <ClCompile Include="..\..\..\gzwrite.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\minizip\ioapi.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\minizip\unzip.c" /> <ClCompile Include="..\..\minizip\zip.c" /> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="zlib.rc" /> </ItemGroup> <ItemGroup> <None Include="zlibvc.def" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Source Files"> <UniqueIdentifier>{174213f6-7f66-4ae8-a3a8-a1e0a1e6ffdd}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\compress.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\crc32.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\deflate.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\gzclose.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\gzlib.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\gzread.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\gzwrite.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\infback.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\inffast.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\inflate.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\inftrees.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\minizip\ioapi.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\trees.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\uncompr.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\minizip\unzip.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\minizip\zip.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\zutil.c"> <Filter>Source Files</Filter> </ClCompile> </ItemGroup> <ItemGroup> <ResourceCompile Include="zlib.rc"> <Filter>Source Files</Filter> </ResourceCompile> </ItemGroup> <ItemGroup> <None Include="zlibvc.def"> <Filter>Source Files</Filter> </None> </ItemGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/zlibvc.def.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | LIBRARY ; zlib data compression and ZIP file I/O library VERSION 1.3.1 EXPORTS adler32 @1 compress @2 crc32 @3 deflate @4 deflateCopy @5 deflateEnd @6 deflateInit2_ @7 deflateInit_ @8 deflateParams @9 deflateReset @10 deflateSetDictionary @11 gzclose @12 gzdopen @13 gzerror @14 gzflush @15 gzopen @16 gzread @17 gzwrite @18 inflate @19 inflateEnd @20 inflateInit2_ @21 inflateInit_ @22 inflateReset @23 inflateSetDictionary @24 inflateSync @25 uncompress @26 zlibVersion @27 gzprintf @28 gzputc @29 gzgetc @30 gzseek @31 gzrewind @32 gztell @33 gzeof @34 gzsetparams @35 zError @36 inflateSyncPoint @37 get_crc_table @38 compress2 @39 gzputs @40 gzgets @41 inflateCopy @42 inflateBackInit_ @43 inflateBack @44 inflateBackEnd @45 compressBound @46 deflateBound @47 gzclearerr @48 gzungetc @49 zlibCompileFlags @50 deflatePrime @51 deflatePending @52 unzOpen @61 unzClose @62 unzGetGlobalInfo @63 unzGetCurrentFileInfo @64 unzGoToFirstFile @65 unzGoToNextFile @66 unzOpenCurrentFile @67 unzReadCurrentFile @68 unzOpenCurrentFile3 @69 unztell @70 unzeof @71 unzCloseCurrentFile @72 unzGetGlobalComment @73 unzStringFileNameCompare @74 unzLocateFile @75 unzGetLocalExtrafield @76 unzOpen2 @77 unzOpenCurrentFile2 @78 unzOpenCurrentFilePassword @79 zipOpen @80 zipOpenNewFileInZip @81 zipWriteInFileInZip @82 zipCloseFileInZip @83 zipClose @84 zipOpenNewFileInZip2 @86 zipCloseFileInZipRaw @87 zipOpen2 @88 zipOpenNewFileInZip3 @89 unzGetFilePos @100 unzGoToFilePos @101 fill_win32_filefunc @110 ; zlibwapi v1.2.4 added: fill_win32_filefunc64 @111 fill_win32_filefunc64A @112 fill_win32_filefunc64W @113 unzOpen64 @120 unzOpen2_64 @121 unzGetGlobalInfo64 @122 unzGetCurrentFileInfo64 @124 unzGetCurrentFileZStreamPos64 @125 unztell64 @126 unzGetFilePos64 @127 unzGoToFilePos64 @128 zipOpen64 @130 zipOpen2_64 @131 zipOpenNewFileInZip64 @132 zipOpenNewFileInZip2_64 @133 zipOpenNewFileInZip3_64 @134 zipOpenNewFileInZip4_64 @135 zipCloseFileInZipRaw64 @136 ; zlib1 v1.2.4 added: adler32_combine @140 crc32_combine @142 deflateSetHeader @144 deflateTune @145 gzbuffer @146 gzclose_r @147 gzclose_w @148 gzdirect @149 gzoffset @150 inflateGetHeader @156 inflateMark @157 inflatePrime @158 inflateReset2 @159 inflateUndermine @160 ; zlib1 v1.2.6 added: gzgetc_ @161 inflateResetKeep @163 deflateResetKeep @164 ; zlib1 v1.2.7 added: gzopen_w @165 ; zlib1 v1.2.8 added: inflateGetDictionary @166 gzvprintf @167 ; zlib1 v1.2.9 added: inflateCodesUsed @168 inflateValidate @169 uncompress2 @170 gzfread @171 gzfwrite @172 deflateGetDictionary @173 adler32_z @174 crc32_z @175 ; zlib1 v1.2.12 added: crc32_combine_gen @176 crc32_combine_gen64 @177 crc32_combine_op @178 |
Added compat/zlib/contrib/vstudio/vc10/zlibvc.sln.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcxproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcxproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcxproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlibdll", "testzlibdll.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcxproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Itanium = Debug|Itanium Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Itanium = Release|Itanium Release|Win32 = Release|Win32 Release|x64 = Release|x64 ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32 ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.Build.0 = Debug|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.Build.0 = Release|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.Build.0 = Debug|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.Build.0 = Release|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.Build.0 = Debug|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.Build.0 = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.Build.0 = Debug|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.Build.0 = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal |
Added compat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{8FD826F8-3739-44E6-8CC8-997122E53B8D}</ProjectGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">zlibwapid</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">zlibwapid</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">zlibwapi</TargetName> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <GenerateMapFile>true</GenerateMapFile> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateMapFile>true</GenerateMapFile> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateMapFile>true</GenerateMapFile> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <GenerateMapFile>true</GenerateMapFile> <SubSystem>Windows</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateMapFile>true</GenerateMapFile> <SubSystem>Windows</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateMapFile>true</GenerateMapFile> <SubSystem>Windows</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\gzclose.c" /> <ClCompile Include="..\..\..\gzlib.c" /> <ClCompile Include="..\..\..\gzread.c" /> <ClCompile Include="..\..\..\gzwrite.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\minizip\ioapi.c" /> <ClCompile Include="..\..\minizip\iowin32.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\minizip\unzip.c"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <ClCompile Include="..\..\minizip\zip.c"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="zlib.rc" /> </ItemGroup> <ItemGroup> <None Include="zlibvc.def" /> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\deflate.h" /> <ClInclude Include="..\..\..\infblock.h" /> <ClInclude Include="..\..\..\infcodes.h" /> <ClInclude Include="..\..\..\inffast.h" /> <ClInclude Include="..\..\..\inftrees.h" /> <ClInclude Include="..\..\..\infutil.h" /> <ClInclude Include="..\..\..\zconf.h" /> <ClInclude Include="..\..\..\zlib.h" /> <ClInclude Include="..\..\..\zutil.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Source Files"> <UniqueIdentifier>{07934a85-8b61-443d-a0ee-b2eedb74f3cd}</UniqueIdentifier> <Extensions>cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90</Extensions> </Filter> <Filter Include="Header Files"> <UniqueIdentifier>{1d99675b-433d-4a21-9e50-ed4ab8b19762}</UniqueIdentifier> <Extensions>h;hpp;hxx;hm;inl;fi;fd</Extensions> </Filter> <Filter Include="Resource Files"> <UniqueIdentifier>{431c0958-fa71-44d0-9084-2d19d100c0cc}</UniqueIdentifier> <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe</Extensions> </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\compress.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\crc32.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\deflate.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\gzclose.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\gzlib.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\gzread.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\gzwrite.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\infback.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\inffast.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\inflate.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\inftrees.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\minizip\ioapi.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\minizip\iowin32.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\trees.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\uncompr.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\minizip\unzip.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\minizip\zip.c"> <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\zutil.c"> <Filter>Source Files</Filter> </ClCompile> </ItemGroup> <ItemGroup> <ResourceCompile Include="zlib.rc"> <Filter>Source Files</Filter> </ResourceCompile> </ItemGroup> <ItemGroup> <None Include="zlibvc.def"> <Filter>Source Files</Filter> </None> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\deflate.h"> <Filter>Header Files</Filter> </ClInclude> <ClInclude Include="..\..\..\infblock.h"> <Filter>Header Files</Filter> </ClInclude> <ClInclude Include="..\..\..\infcodes.h"> <Filter>Header Files</Filter> </ClInclude> <ClInclude Include="..\..\..\inffast.h"> <Filter>Header Files</Filter> </ClInclude> <ClInclude Include="..\..\..\inftrees.h"> <Filter>Header Files</Filter> </ClInclude> <ClInclude Include="..\..\..\infutil.h"> <Filter>Header Files</Filter> </ClInclude> <ClInclude Include="..\..\..\zconf.h"> <Filter>Header Files</Filter> </ClInclude> <ClInclude Include="..\..\..\zlib.h"> <Filter>Header Files</Filter> </ClInclude> <ClInclude Include="..\..\..\zutil.h"> <Filter>Header Files</Filter> </ClInclude> </ItemGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc11/miniunz.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{C52F9E7B-498A-42BE-8DB4-85A15694382A}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\minizip\miniunz.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc11/minizip.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniZip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniZip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniZip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniZip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\minizip\minizip.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc11/testzlib.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}</ProjectGuid> <RootNamespace>testzlib</RootNamespace> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\testzlib\testzlib.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{C52F9E7B-498A-42BE-8DB4-85A15694366A}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\testzlib\testzlib.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc11/zlib.rc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include <windows.h> #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE FILEVERSION 1, 3, 1, 0 PRODUCTVERSION 1, 3, 1, 0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0 FILEOS VOS_DOS_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE 0 // not used BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" //language ID = U.S. English, char set = Windows, Multilingual BEGIN VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0" VALUE "FileVersion", "1.3.1\0" VALUE "InternalName", "zlib\0" VALUE "OriginalFilename", "zlibwapi.dll\0" VALUE "ProductName", "ZLib.DLL\0" VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 1252 END END |
Added compat/zlib/contrib/vstudio/vc11/zlibstat.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}</ProjectGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v110</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\gzclose.c" /> <ClCompile Include="..\..\..\gzlib.c" /> <ClCompile Include="..\..\..\gzread.c" /> <ClCompile Include="..\..\..\gzwrite.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\minizip\ioapi.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\minizip\unzip.c" /> <ClCompile Include="..\..\minizip\zip.c" /> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="zlib.rc" /> </ItemGroup> <ItemGroup> <None Include="zlibvc.def" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc11/zlibvc.def.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | LIBRARY ; zlib data compression and ZIP file I/O library VERSION 1.3.1 EXPORTS adler32 @1 compress @2 crc32 @3 deflate @4 deflateCopy @5 deflateEnd @6 deflateInit2_ @7 deflateInit_ @8 deflateParams @9 deflateReset @10 deflateSetDictionary @11 gzclose @12 gzdopen @13 gzerror @14 gzflush @15 gzopen @16 gzread @17 gzwrite @18 inflate @19 inflateEnd @20 inflateInit2_ @21 inflateInit_ @22 inflateReset @23 inflateSetDictionary @24 inflateSync @25 uncompress @26 zlibVersion @27 gzprintf @28 gzputc @29 gzgetc @30 gzseek @31 gzrewind @32 gztell @33 gzeof @34 gzsetparams @35 zError @36 inflateSyncPoint @37 get_crc_table @38 compress2 @39 gzputs @40 gzgets @41 inflateCopy @42 inflateBackInit_ @43 inflateBack @44 inflateBackEnd @45 compressBound @46 deflateBound @47 gzclearerr @48 gzungetc @49 zlibCompileFlags @50 deflatePrime @51 deflatePending @52 unzOpen @61 unzClose @62 unzGetGlobalInfo @63 unzGetCurrentFileInfo @64 unzGoToFirstFile @65 unzGoToNextFile @66 unzOpenCurrentFile @67 unzReadCurrentFile @68 unzOpenCurrentFile3 @69 unztell @70 unzeof @71 unzCloseCurrentFile @72 unzGetGlobalComment @73 unzStringFileNameCompare @74 unzLocateFile @75 unzGetLocalExtrafield @76 unzOpen2 @77 unzOpenCurrentFile2 @78 unzOpenCurrentFilePassword @79 zipOpen @80 zipOpenNewFileInZip @81 zipWriteInFileInZip @82 zipCloseFileInZip @83 zipClose @84 zipOpenNewFileInZip2 @86 zipCloseFileInZipRaw @87 zipOpen2 @88 zipOpenNewFileInZip3 @89 unzGetFilePos @100 unzGoToFilePos @101 fill_win32_filefunc @110 ; zlibwapi v1.2.4 added: fill_win32_filefunc64 @111 fill_win32_filefunc64A @112 fill_win32_filefunc64W @113 unzOpen64 @120 unzOpen2_64 @121 unzGetGlobalInfo64 @122 unzGetCurrentFileInfo64 @124 unzGetCurrentFileZStreamPos64 @125 unztell64 @126 unzGetFilePos64 @127 unzGoToFilePos64 @128 zipOpen64 @130 zipOpen2_64 @131 zipOpenNewFileInZip64 @132 zipOpenNewFileInZip2_64 @133 zipOpenNewFileInZip3_64 @134 zipOpenNewFileInZip4_64 @135 zipCloseFileInZipRaw64 @136 ; zlib1 v1.2.4 added: adler32_combine @140 crc32_combine @142 deflateSetHeader @144 deflateTune @145 gzbuffer @146 gzclose_r @147 gzclose_w @148 gzdirect @149 gzoffset @150 inflateGetHeader @156 inflateMark @157 inflatePrime @158 inflateReset2 @159 inflateUndermine @160 ; zlib1 v1.2.6 added: gzgetc_ @161 inflateResetKeep @163 deflateResetKeep @164 ; zlib1 v1.2.7 added: gzopen_w @165 ; zlib1 v1.2.8 added: inflateGetDictionary @166 gzvprintf @167 ; zlib1 v1.2.9 added: inflateCodesUsed @168 inflateValidate @169 uncompress2 @170 gzfread @171 gzfwrite @172 deflateGetDictionary @173 adler32_z @174 crc32_z @175 ; zlib1 v1.2.12 added: crc32_combine_gen @176 crc32_combine_gen64 @177 crc32_combine_op @178 |
Added compat/zlib/contrib/vstudio/vc11/zlibvc.sln.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcxproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcxproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcxproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlibdll", "testzlibdll.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcxproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Itanium = Debug|Itanium Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Itanium = Release|Itanium Release|Win32 = Release|Win32 Release|x64 = Release|x64 ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32 ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal |
Added compat/zlib/contrib/vstudio/vc11/zlibvc.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{8FD826F8-3739-44E6-8CC8-997122E53B8D}</ProjectGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v110</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v110</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">zlibwapi</TargetName> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\gzclose.c" /> <ClCompile Include="..\..\..\gzlib.c" /> <ClCompile Include="..\..\..\gzread.c" /> <ClCompile Include="..\..\..\gzwrite.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\minizip\ioapi.c" /> <ClCompile Include="..\..\minizip\iowin32.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\minizip\unzip.c"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <ClCompile Include="..\..\minizip\zip.c"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="zlib.rc" /> </ItemGroup> <ItemGroup> <None Include="zlibvc.def" /> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\deflate.h" /> <ClInclude Include="..\..\..\infblock.h" /> <ClInclude Include="..\..\..\infcodes.h" /> <ClInclude Include="..\..\..\inffast.h" /> <ClInclude Include="..\..\..\inftrees.h" /> <ClInclude Include="..\..\..\infutil.h" /> <ClInclude Include="..\..\..\zconf.h" /> <ClInclude Include="..\..\..\zlib.h" /> <ClInclude Include="..\..\..\zutil.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc12/miniunz.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{C52F9E7B-498A-42BE-8DB4-85A15694382A}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\minizip\miniunz.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc12/minizip.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniZip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniZip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniZip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniZip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\minizip\minizip.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc12/testzlib.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}</ProjectGuid> <RootNamespace>testzlib</RootNamespace> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\testzlib\testzlib.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc12/testzlibdll.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{C52F9E7B-498A-42BE-8DB4-85A15694366A}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\testzlib\testzlib.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc12/zlib.rc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include <windows.h> #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE FILEVERSION 1, 3, 1, 0 PRODUCTVERSION 1, 3, 1, 0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0 FILEOS VOS_DOS_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE 0 // not used BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" //language ID = U.S. English, char set = Windows, Multilingual BEGIN VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0" VALUE "FileVersion", "1.3.1\0" VALUE "InternalName", "zlib\0" VALUE "OriginalFilename", "zlibwapi.dll\0" VALUE "ProductName", "ZLib.DLL\0" VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 1252 END END |
Added compat/zlib/contrib/vstudio/vc12/zlibstat.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}</ProjectGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\gzclose.c" /> <ClCompile Include="..\..\..\gzlib.c" /> <ClCompile Include="..\..\..\gzread.c" /> <ClCompile Include="..\..\..\gzwrite.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\minizip\ioapi.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\minizip\unzip.c" /> <ClCompile Include="..\..\minizip\zip.c" /> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="zlib.rc" /> </ItemGroup> <ItemGroup> <None Include="zlibvc.def" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc12/zlibvc.def.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | LIBRARY ; zlib data compression and ZIP file I/O library VERSION 1.3.1 EXPORTS adler32 @1 compress @2 crc32 @3 deflate @4 deflateCopy @5 deflateEnd @6 deflateInit2_ @7 deflateInit_ @8 deflateParams @9 deflateReset @10 deflateSetDictionary @11 gzclose @12 gzdopen @13 gzerror @14 gzflush @15 gzopen @16 gzread @17 gzwrite @18 inflate @19 inflateEnd @20 inflateInit2_ @21 inflateInit_ @22 inflateReset @23 inflateSetDictionary @24 inflateSync @25 uncompress @26 zlibVersion @27 gzprintf @28 gzputc @29 gzgetc @30 gzseek @31 gzrewind @32 gztell @33 gzeof @34 gzsetparams @35 zError @36 inflateSyncPoint @37 get_crc_table @38 compress2 @39 gzputs @40 gzgets @41 inflateCopy @42 inflateBackInit_ @43 inflateBack @44 inflateBackEnd @45 compressBound @46 deflateBound @47 gzclearerr @48 gzungetc @49 zlibCompileFlags @50 deflatePrime @51 deflatePending @52 unzOpen @61 unzClose @62 unzGetGlobalInfo @63 unzGetCurrentFileInfo @64 unzGoToFirstFile @65 unzGoToNextFile @66 unzOpenCurrentFile @67 unzReadCurrentFile @68 unzOpenCurrentFile3 @69 unztell @70 unzeof @71 unzCloseCurrentFile @72 unzGetGlobalComment @73 unzStringFileNameCompare @74 unzLocateFile @75 unzGetLocalExtrafield @76 unzOpen2 @77 unzOpenCurrentFile2 @78 unzOpenCurrentFilePassword @79 zipOpen @80 zipOpenNewFileInZip @81 zipWriteInFileInZip @82 zipCloseFileInZip @83 zipClose @84 zipOpenNewFileInZip2 @86 zipCloseFileInZipRaw @87 zipOpen2 @88 zipOpenNewFileInZip3 @89 unzGetFilePos @100 unzGoToFilePos @101 fill_win32_filefunc @110 ; zlibwapi v1.2.4 added: fill_win32_filefunc64 @111 fill_win32_filefunc64A @112 fill_win32_filefunc64W @113 unzOpen64 @120 unzOpen2_64 @121 unzGetGlobalInfo64 @122 unzGetCurrentFileInfo64 @124 unzGetCurrentFileZStreamPos64 @125 unztell64 @126 unzGetFilePos64 @127 unzGoToFilePos64 @128 zipOpen64 @130 zipOpen2_64 @131 zipOpenNewFileInZip64 @132 zipOpenNewFileInZip2_64 @133 zipOpenNewFileInZip3_64 @134 zipOpenNewFileInZip4_64 @135 zipCloseFileInZipRaw64 @136 ; zlib1 v1.2.4 added: adler32_combine @140 crc32_combine @142 deflateSetHeader @144 deflateTune @145 gzbuffer @146 gzclose_r @147 gzclose_w @148 gzdirect @149 gzoffset @150 inflateGetHeader @156 inflateMark @157 inflatePrime @158 inflateReset2 @159 inflateUndermine @160 ; zlib1 v1.2.6 added: gzgetc_ @161 inflateResetKeep @163 deflateResetKeep @164 ; zlib1 v1.2.7 added: gzopen_w @165 ; zlib1 v1.2.8 added: inflateGetDictionary @166 gzvprintf @167 ; zlib1 v1.2.9 added: inflateCodesUsed @168 inflateValidate @169 uncompress2 @170 gzfread @171 gzfwrite @172 deflateGetDictionary @173 adler32_z @174 crc32_z @175 ; zlib1 v1.2.12 added: crc32_combine_gen @176 crc32_combine_gen64 @177 crc32_combine_op @178 |
Added compat/zlib/contrib/vstudio/vc12/zlibvc.sln.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.40629.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcxproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcxproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcxproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlibdll", "testzlibdll.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcxproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Itanium = Debug|Itanium Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Itanium = Release|Itanium Release|Win32 = Release|Win32 Release|x64 = Release|x64 ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32 ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal |
Added compat/zlib/contrib/vstudio/vc12/zlibvc.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{8FD826F8-3739-44E6-8CC8-997122E53B8D}</ProjectGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">zlibwapi</TargetName> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\gzclose.c" /> <ClCompile Include="..\..\..\gzlib.c" /> <ClCompile Include="..\..\..\gzread.c" /> <ClCompile Include="..\..\..\gzwrite.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\minizip\ioapi.c" /> <ClCompile Include="..\..\minizip\iowin32.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\minizip\unzip.c"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <ClCompile Include="..\..\minizip\zip.c"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="zlib.rc" /> </ItemGroup> <ItemGroup> <None Include="zlibvc.def" /> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\deflate.h" /> <ClInclude Include="..\..\..\infblock.h" /> <ClInclude Include="..\..\..\infcodes.h" /> <ClInclude Include="..\..\..\inffast.h" /> <ClInclude Include="..\..\..\inftrees.h" /> <ClInclude Include="..\..\..\infutil.h" /> <ClInclude Include="..\..\..\zconf.h" /> <ClInclude Include="..\..\..\zlib.h" /> <ClInclude Include="..\..\..\zutil.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc14/miniunz.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{C52F9E7B-498A-42BE-8DB4-85A15694382A}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\MiniUnzip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\MiniUnzip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)miniunz.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)miniunz.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\minizip\miniunz.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc14/minizip.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniZip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\MiniZip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniZip$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\MiniZip$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\$(Configuration)\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)minizip.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)minizip.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\minizip\minizip.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc14/testzlib.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}</ProjectGuid> <RootNamespace>testzlib</RootNamespace> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlib$(Configuration)\Tmp\</IntDir> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlib$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlib$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> </ClCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <OutputFile>$(OutDir)testzlib.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\testzlib\testzlib.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc14/testzlibdll.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{C52F9E7B-498A-42BE-8DB4-85A15694366A}</ProjectGuid> <Keyword>Win32Proj</Keyword> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>Unicode</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <CharacterSet>MultiByte</CharacterSet> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\TestZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x86\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <TargetMachine>MachineX86</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MinimalRebuild>true</MinimalRebuild> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllDebug\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)testzlib.pdb</ProgramDatabaseFile> <SubSystem>Console</SubSystem> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>x64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>MaxSpeed</Optimization> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <OmitFramePointers>true</OmitFramePointers> <AdditionalIncludeDirectories>..\..\..;..\..\minizip;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeader> </PrecompiledHeader> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <WarningLevel>Level3</WarningLevel> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <Link> <AdditionalDependencies>ia64\ZlibDllRelease\zlibwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)testzlibdll.exe</OutputFile> <GenerateDebugInformation>true</GenerateDebugInformation> <SubSystem>Console</SubSystem> <OptimizeReferences>true</OptimizeReferences> <EnableCOMDATFolding>true</EnableCOMDATFolding> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\testzlib\testzlib.c" /> </ItemGroup> <ItemGroup> <ProjectReference Include="zlibvc.vcxproj"> <Project>{8fd826f8-3739-44e6-8cc8-997122e53b8d}</Project> </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc14/zlib.rc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include <windows.h> #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE FILEVERSION 1, 3, 1, 0 PRODUCTVERSION 1, 3, 1, 0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0 FILEOS VOS_DOS_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE 0 // not used BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" //language ID = U.S. English, char set = Windows, Multilingual BEGIN VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0" VALUE "FileVersion", "1.3.1\0" VALUE "InternalName", "zlib\0" VALUE "OriginalFilename", "zlibwapi.dll\0" VALUE "ProductName", "ZLib.DLL\0" VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 1252 END END |
Added compat/zlib/contrib/vstudio/vc14/zlibstat.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}</ProjectGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibStat$(Configuration)\Tmp\</IntDir> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibStat$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibStat$(Configuration)\Tmp\</IntDir> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:X86 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>OldStyle</DebugInformationFormat> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <Midl> <TargetEnvironment>X64</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:AMD64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <TargetEnvironment>Itanium</TargetEnvironment> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibstat.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <Culture>0x040c</Culture> </ResourceCompile> <Lib> <AdditionalOptions>/MACHINE:IA64 /NODEFAULTLIB %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibstat.lib</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> </Lib> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\gzclose.c" /> <ClCompile Include="..\..\..\gzlib.c" /> <ClCompile Include="..\..\..\gzread.c" /> <ClCompile Include="..\..\..\gzwrite.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\minizip\ioapi.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\minizip\unzip.c" /> <ClCompile Include="..\..\minizip\zip.c" /> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="zlib.rc" /> </ItemGroup> <ItemGroup> <None Include="zlibvc.def" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc14/zlibvc.def.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | LIBRARY ; zlib data compression and ZIP file I/O library VERSION 1.3.1 EXPORTS adler32 @1 compress @2 crc32 @3 deflate @4 deflateCopy @5 deflateEnd @6 deflateInit2_ @7 deflateInit_ @8 deflateParams @9 deflateReset @10 deflateSetDictionary @11 gzclose @12 gzdopen @13 gzerror @14 gzflush @15 gzopen @16 gzread @17 gzwrite @18 inflate @19 inflateEnd @20 inflateInit2_ @21 inflateInit_ @22 inflateReset @23 inflateSetDictionary @24 inflateSync @25 uncompress @26 zlibVersion @27 gzprintf @28 gzputc @29 gzgetc @30 gzseek @31 gzrewind @32 gztell @33 gzeof @34 gzsetparams @35 zError @36 inflateSyncPoint @37 get_crc_table @38 compress2 @39 gzputs @40 gzgets @41 inflateCopy @42 inflateBackInit_ @43 inflateBack @44 inflateBackEnd @45 compressBound @46 deflateBound @47 gzclearerr @48 gzungetc @49 zlibCompileFlags @50 deflatePrime @51 deflatePending @52 unzOpen @61 unzClose @62 unzGetGlobalInfo @63 unzGetCurrentFileInfo @64 unzGoToFirstFile @65 unzGoToNextFile @66 unzOpenCurrentFile @67 unzReadCurrentFile @68 unzOpenCurrentFile3 @69 unztell @70 unzeof @71 unzCloseCurrentFile @72 unzGetGlobalComment @73 unzStringFileNameCompare @74 unzLocateFile @75 unzGetLocalExtrafield @76 unzOpen2 @77 unzOpenCurrentFile2 @78 unzOpenCurrentFilePassword @79 zipOpen @80 zipOpenNewFileInZip @81 zipWriteInFileInZip @82 zipCloseFileInZip @83 zipClose @84 zipOpenNewFileInZip2 @86 zipCloseFileInZipRaw @87 zipOpen2 @88 zipOpenNewFileInZip3 @89 unzGetFilePos @100 unzGoToFilePos @101 fill_win32_filefunc @110 ; zlibwapi v1.2.4 added: fill_win32_filefunc64 @111 fill_win32_filefunc64A @112 fill_win32_filefunc64W @113 unzOpen64 @120 unzOpen2_64 @121 unzGetGlobalInfo64 @122 unzGetCurrentFileInfo64 @124 unzGetCurrentFileZStreamPos64 @125 unztell64 @126 unzGetFilePos64 @127 unzGoToFilePos64 @128 zipOpen64 @130 zipOpen2_64 @131 zipOpenNewFileInZip64 @132 zipOpenNewFileInZip2_64 @133 zipOpenNewFileInZip3_64 @134 zipOpenNewFileInZip4_64 @135 zipCloseFileInZipRaw64 @136 ; zlib1 v1.2.4 added: adler32_combine @140 crc32_combine @142 deflateSetHeader @144 deflateTune @145 gzbuffer @146 gzclose_r @147 gzclose_w @148 gzdirect @149 gzoffset @150 inflateGetHeader @156 inflateMark @157 inflatePrime @158 inflateReset2 @159 inflateUndermine @160 ; zlib1 v1.2.6 added: gzgetc_ @161 inflateResetKeep @163 deflateResetKeep @164 ; zlib1 v1.2.7 added: gzopen_w @165 ; zlib1 v1.2.8 added: inflateGetDictionary @166 gzvprintf @167 ; zlib1 v1.2.9 added: inflateCodesUsed @168 inflateValidate @169 uncompress2 @170 gzfread @171 gzfwrite @172 deflateGetDictionary @173 adler32_z @174 crc32_z @175 ; zlib1 v1.2.12 added: crc32_combine_gen @176 crc32_combine_gen64 @177 crc32_combine_op @178 |
Added compat/zlib/contrib/vstudio/vc14/zlibvc.sln.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcxproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcxproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcxproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlibdll", "testzlibdll.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcxproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcxproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Itanium = Debug|Itanium Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Itanium = Release|Itanium Release|Win32 = Release|Win32 Release|x64 = Release|x64 ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32 ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal |
Added compat/zlib/contrib/vstudio/vc14/zlibvc.vcxproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Itanium"> <Configuration>Debug</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Itanium"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|Win32"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="ReleaseWithoutAsm|x64"> <Configuration>ReleaseWithoutAsm</Configuration> <Platform>x64</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Itanium"> <Configuration>Release</Configuration> <Platform>Itanium</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|Win32"> <Configuration>Release</Configuration> <Platform>Win32</Platform> </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> <ProjectGuid>{8FD826F8-3739-44E6-8CC8-997122E53B8D}</ProjectGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <WholeProgramOptimization>true</WholeProgramOptimization> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseOfMfc>false</UseOfMfc> <PlatformToolset>v140</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> <PropertyGroup> <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">x86\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">true</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">x64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest> <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibDll$(Configuration)\</OutDir> <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ia64\ZlibDll$(Configuration)\Tmp\</IntDir> <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</LinkIncremental> <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">false</GenerateManifest> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'">zlibwapi</TargetName> <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">zlibwapi</TargetName> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Win32'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Win32</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalOptions>/MACHINE:I386 %(AdditionalOptions)</AdditionalOptions> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <RandomizedBaseAddress>false</RandomizedBaseAddress> <DataExecutionPrevention> </DataExecutionPrevention> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Itanium'"> <Midl> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <Optimization>Disabled</Optimization> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <GenerateDebugInformation>true</GenerateDebugInformation> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|x64'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWithoutAsm|Itanium'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>X64</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineX64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'"> <Midl> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <MkTypLibCompatible>true</MkTypLibCompatible> <SuppressStartupBanner>true</SuppressStartupBanner> <TargetEnvironment>Itanium</TargetEnvironment> <TypeLibraryName>$(OutDir)zlibvc.tlb</TypeLibraryName> </Midl> <ClCompile> <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> <StringPooling>true</StringPooling> <ExceptionHandling> </ExceptionHandling> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <BufferSecurityCheck>false</BufferSecurityCheck> <FunctionLevelLinking>true</FunctionLevelLinking> <PrecompiledHeaderOutputFile>$(IntDir)zlibvc.pch</PrecompiledHeaderOutputFile> <AssemblerOutput>All</AssemblerOutput> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <ObjectFileName>$(IntDir)</ObjectFileName> <ProgramDataBaseFileName>$(OutDir)</ProgramDataBaseFileName> <BrowseInformation> </BrowseInformation> <WarningLevel>Level3</WarningLevel> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> <ResourceCompile> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> <Culture>0x040c</Culture> </ResourceCompile> <Link> <OutputFile>$(OutDir)zlibwapi.dll</OutputFile> <SuppressStartupBanner>true</SuppressStartupBanner> <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries> <ModuleDefinitionFile>.\zlibvc.def</ModuleDefinitionFile> <ProgramDatabaseFile>$(OutDir)zlibwapi.pdb</ProgramDatabaseFile> <GenerateMapFile>true</GenerateMapFile> <MapFileName>$(OutDir)zlibwapi.map</MapFileName> <SubSystem>Windows</SubSystem> <ImportLibrary>$(OutDir)zlibwapi.lib</ImportLibrary> <TargetMachine>MachineIA64</TargetMachine> </Link> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\adler32.c" /> <ClCompile Include="..\..\..\compress.c" /> <ClCompile Include="..\..\..\crc32.c" /> <ClCompile Include="..\..\..\deflate.c" /> <ClCompile Include="..\..\..\gzclose.c" /> <ClCompile Include="..\..\..\gzlib.c" /> <ClCompile Include="..\..\..\gzread.c" /> <ClCompile Include="..\..\..\gzwrite.c" /> <ClCompile Include="..\..\..\infback.c" /> <ClCompile Include="..\..\..\inffast.c" /> <ClCompile Include="..\..\..\inflate.c" /> <ClCompile Include="..\..\..\inftrees.c" /> <ClCompile Include="..\..\minizip\ioapi.c" /> <ClCompile Include="..\..\minizip\iowin32.c" /> <ClCompile Include="..\..\..\trees.c" /> <ClCompile Include="..\..\..\uncompr.c" /> <ClCompile Include="..\..\minizip\unzip.c"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <ClCompile Include="..\..\minizip\zip.c"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Itanium'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">ZLIB_INTERNAL;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <ClCompile Include="..\..\..\zutil.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="zlib.rc" /> </ItemGroup> <ItemGroup> <None Include="zlibvc.def" /> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\deflate.h" /> <ClInclude Include="..\..\..\infblock.h" /> <ClInclude Include="..\..\..\infcodes.h" /> <ClInclude Include="..\..\..\inffast.h" /> <ClInclude Include="..\..\..\inftrees.h" /> <ClInclude Include="..\..\..\infutil.h" /> <ClInclude Include="..\..\..\zconf.h" /> <ClInclude Include="..\..\..\zlib.h" /> <ClInclude Include="..\..\..\zutil.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> </Project> |
Added compat/zlib/contrib/vstudio/vc9/miniunz.vcproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="Windows-1252"?> <VisualStudioProject ProjectType="Visual C++" Version="9.00" Name="miniunz" ProjectGUID="{C52F9E7B-498A-42BE-8DB4-85A15694382A}" Keyword="Win32Proj" TargetFrameworkVersion="131072" > <Platforms> <Platform Name="Win32" /> <Platform Name="x64" /> <Platform Name="Itanium" /> </Platforms> <ToolFiles> </ToolFiles> <Configurations> <Configuration Name="Debug|Win32" OutputDirectory="x86\MiniUnzip$(ConfigurationName)" IntermediateDirectory="x86\MiniUnzip$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="1" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="4" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x86\ZlibDllDebug\zlibwapi.lib" OutputFile="$(OutDir)/miniunz.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/miniunz.pdb" SubSystem="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Win32" OutputDirectory="x86\MiniUnzip$(ConfigurationName)" IntermediateDirectory="x86\MiniUnzip$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="0" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x86\ZlibDllRelease\zlibwapi.lib" OutputFile="$(OutDir)/miniunz.exe" LinkIncremental="1" GenerateManifest="false" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|x64" OutputDirectory="x64\MiniUnzip$(ConfigurationName)" IntermediateDirectory="x64\MiniUnzip$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="3" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x64\ZlibDllDebug\zlibwapi.lib" OutputFile="$(OutDir)/miniunz.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/miniunz.pdb" SubSystem="1" TargetMachine="17" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|Itanium" OutputDirectory="ia64\MiniUnzip$(ConfigurationName)" IntermediateDirectory="ia64\MiniUnzip$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="3" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="ia64\ZlibDllDebug\zlibwapi.lib" OutputFile="$(OutDir)/miniunz.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/miniunz.pdb" SubSystem="1" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|x64" OutputDirectory="x64\MiniUnzip$(ConfigurationName)" IntermediateDirectory="x64\MiniUnzip$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x64\ZlibDllRelease\zlibwapi.lib" OutputFile="$(OutDir)/miniunz.exe" LinkIncremental="1" GenerateManifest="false" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" TargetMachine="17" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Itanium" OutputDirectory="ia64\MiniUnzip$(ConfigurationName)" IntermediateDirectory="ia64\MiniUnzip$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="ia64\ZlibDllRelease\zlibwapi.lib" OutputFile="$(OutDir)/miniunz.exe" LinkIncremental="1" GenerateManifest="false" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> </Configurations> <References> </References> <Files> <Filter Name="Source Files" Filter="cpp;c;cxx;def;odl;idl;hpj;bat" > <File RelativePath="..\..\minizip\miniunz.c" > </File> </Filter> <Filter Name="Header Files" Filter="h;hpp;hxx;hm;inl;inc" > </Filter> <Filter Name="Resource Files" Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" > </Filter> </Files> <Globals> </Globals> </VisualStudioProject> |
Added compat/zlib/contrib/vstudio/vc9/minizip.vcproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="Windows-1252"?> <VisualStudioProject ProjectType="Visual C++" Version="9.00" Name="minizip" ProjectGUID="{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" Keyword="Win32Proj" TargetFrameworkVersion="131072" > <Platforms> <Platform Name="Win32" /> <Platform Name="x64" /> <Platform Name="Itanium" /> </Platforms> <ToolFiles> </ToolFiles> <Configurations> <Configuration Name="Debug|Win32" OutputDirectory="x86\MiniZip$(ConfigurationName)" IntermediateDirectory="x86\MiniZip$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="1" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="4" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x86\ZlibDllDebug\zlibwapi.lib" OutputFile="$(OutDir)/minizip.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/minizip.pdb" SubSystem="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Win32" OutputDirectory="x86\MiniZip$(ConfigurationName)" IntermediateDirectory="x86\MiniZip$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="0" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x86\ZlibDllRelease\zlibwapi.lib" OutputFile="$(OutDir)/minizip.exe" LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|x64" OutputDirectory="x64\$(ConfigurationName)" IntermediateDirectory="x64\$(ConfigurationName)" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="3" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x64\ZlibDllDebug\zlibwapi.lib" OutputFile="$(OutDir)/minizip.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/minizip.pdb" SubSystem="1" TargetMachine="17" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|Itanium" OutputDirectory="ia64\$(ConfigurationName)" IntermediateDirectory="ia64\$(ConfigurationName)" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="3" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="ia64\ZlibDllDebug\zlibwapi.lib" OutputFile="$(OutDir)/minizip.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/minizip.pdb" SubSystem="1" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|x64" OutputDirectory="x64\$(ConfigurationName)" IntermediateDirectory="x64\$(ConfigurationName)" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x64\ZlibDllRelease\zlibwapi.lib" OutputFile="$(OutDir)/minizip.exe" LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" TargetMachine="17" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Itanium" OutputDirectory="ia64\$(ConfigurationName)" IntermediateDirectory="ia64\$(ConfigurationName)" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="ia64\ZlibDllRelease\zlibwapi.lib" OutputFile="$(OutDir)/minizip.exe" LinkIncremental="1" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> </Configurations> <References> </References> <Files> <Filter Name="Source Files" Filter="cpp;c;cxx;def;odl;idl;hpj;bat" > <File RelativePath="..\..\minizip\minizip.c" > </File> </Filter> <Filter Name="Header Files" Filter="h;hpp;hxx;hm;inl;inc" > </Filter> <Filter Name="Resource Files" Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" > </Filter> </Files> <Globals> </Globals> </VisualStudioProject> |
Added compat/zlib/contrib/vstudio/vc9/testzlib.vcproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="Windows-1252"?> <VisualStudioProject ProjectType="Visual C++" Version="9,00" Name="testzlib" ProjectGUID="{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" RootNamespace="testzlib" Keyword="Win32Proj" TargetFrameworkVersion="131072" > <Platforms> <Platform Name="Win32" /> <Platform Name="x64" /> <Platform Name="Itanium" /> </Platforms> <ToolFiles> </ToolFiles> <Configurations> <Configuration Name="Debug|Win32" OutputDirectory="x86\TestZlib$(ConfigurationName)" IntermediateDirectory="x86\TestZlib$(ConfigurationName)\Tmp" ConfigurationType="1" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="1" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerOutput="4" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="4" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/testzlib.pdb" SubSystem="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|x64" OutputDirectory="x64\TestZlib$(ConfigurationName)" IntermediateDirectory="x64\TestZlib$(ConfigurationName)\Tmp" ConfigurationType="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS" BasicRuntimeChecks="0" RuntimeLibrary="3" BufferSecurityCheck="false" AssemblerListingLocation="$(IntDir)\" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" GenerateManifest="false" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|Itanium" OutputDirectory="ia64\TestZlib$(ConfigurationName)" IntermediateDirectory="ia64\TestZlib$(ConfigurationName)\Tmp" ConfigurationType="1" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="ZLIB_WINAPI;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="3" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerOutput="4" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/testzlib.pdb" SubSystem="1" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="ReleaseWithoutAsm|Win32" OutputDirectory="x86\TestZlib$(ConfigurationName)" IntermediateDirectory="x86\TestZlib$(ConfigurationName)\Tmp" ConfigurationType="1" CharacterSet="2" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="0" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="1" GenerateManifest="false" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="ReleaseWithoutAsm|x64" OutputDirectory="x64\TestZlib$(ConfigurationName)" IntermediateDirectory="x64\TestZlib$(ConfigurationName)\Tmp" ConfigurationType="1" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS" BasicRuntimeChecks="0" RuntimeLibrary="2" BufferSecurityCheck="false" AssemblerListingLocation="$(IntDir)\" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="" GenerateManifest="false" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="ReleaseWithoutAsm|Itanium" OutputDirectory="ia64\TestZlib$(ConfigurationName)" IntermediateDirectory="ia64\TestZlib$(ConfigurationName)\Tmp" ConfigurationType="1" CharacterSet="2" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="1" GenerateManifest="false" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Win32" OutputDirectory="x86\TestZlib$(ConfigurationName)" IntermediateDirectory="x86\TestZlib$(ConfigurationName)\Tmp" ConfigurationType="1" CharacterSet="2" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="0" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="1" GenerateManifest="false" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|x64" OutputDirectory="x64\TestZlib$(ConfigurationName)" IntermediateDirectory="x64\TestZlib$(ConfigurationName)\Tmp" ConfigurationType="1" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS" BasicRuntimeChecks="0" RuntimeLibrary="0" BufferSecurityCheck="false" AssemblerListingLocation="$(IntDir)\" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" GenerateManifest="false" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Itanium" OutputDirectory="ia64\TestZlib$(ConfigurationName)" IntermediateDirectory="ia64\TestZlib$(ConfigurationName)\Tmp" ConfigurationType="1" CharacterSet="2" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="ZLIB_WINAPI;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="1" GenerateManifest="false" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> </Configurations> <References> </References> <Files> <Filter Name="Source Files" Filter="cpp;c;cxx;def;odl;idl;hpj;bat" > <File RelativePath="..\..\..\adler32.c" > </File> <File RelativePath="..\..\..\compress.c" > </File> <File RelativePath="..\..\..\crc32.c" > </File> <File RelativePath="..\..\..\deflate.c" > </File> <File RelativePath="..\..\..\infback.c" > </File> <File RelativePath="..\..\..\inffast.c" > </File> <File RelativePath="..\..\..\inflate.c" > </File> <File RelativePath="..\..\..\inftrees.c" > </File> <File RelativePath="..\..\testzlib\testzlib.c" > </File> <File RelativePath="..\..\..\trees.c" > </File> <File RelativePath="..\..\..\uncompr.c" > </File> <File RelativePath="..\..\..\zutil.c" > </File> </Filter> <Filter Name="Header Files" Filter="h;hpp;hxx;hm;inl;inc" > </Filter> <Filter Name="Resource Files" Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" > </Filter> </Files> <Globals> </Globals> </VisualStudioProject> |
Added compat/zlib/contrib/vstudio/vc9/testzlibdll.vcproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="Windows-1252"?> <VisualStudioProject ProjectType="Visual C++" Version="9.00" Name="TestZlibDll" ProjectGUID="{C52F9E7B-498A-42BE-8DB4-85A15694366A}" Keyword="Win32Proj" TargetFrameworkVersion="131072" > <Platforms> <Platform Name="Win32" /> <Platform Name="x64" /> <Platform Name="Itanium" /> </Platforms> <ToolFiles> </ToolFiles> <Configurations> <Configuration Name="Debug|Win32" OutputDirectory="x86\TestZlibDll$(ConfigurationName)" IntermediateDirectory="x86\TestZlibDll$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="1" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="4" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x86\ZlibDllDebug\zlibwapi.lib" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/testzlib.pdb" SubSystem="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Win32" OutputDirectory="x86\TestZlibDll$(ConfigurationName)" IntermediateDirectory="x86\TestZlibDll$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="0" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x86\ZlibDllRelease\zlibwapi.lib" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="1" GenerateManifest="false" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" TargetMachine="1" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|x64" OutputDirectory="x64\TestZlibDll$(ConfigurationName)" IntermediateDirectory="x64\TestZlibDll$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="3" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x64\ZlibDllDebug\zlibwapi.lib" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/testzlib.pdb" SubSystem="1" TargetMachine="17" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|Itanium" OutputDirectory="ia64\TestZlibDll$(ConfigurationName)" IntermediateDirectory="ia64\TestZlibDll$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;_DEBUG;_CONSOLE;WIN64" MinimalRebuild="true" BasicRuntimeChecks="0" RuntimeLibrary="3" BufferSecurityCheck="false" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="ia64\ZlibDllDebug\zlibwapi.lib" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="2" GenerateManifest="false" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/testzlib.pdb" SubSystem="1" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|x64" OutputDirectory="x64\TestZlibDll$(ConfigurationName)" IntermediateDirectory="x64\TestZlibDll$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="x64\ZlibDllRelease\zlibwapi.lib" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="1" GenerateManifest="false" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" TargetMachine="17" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Itanium" OutputDirectory="ia64\TestZlibDll$(ConfigurationName)" IntermediateDirectory="ia64\TestZlibDll$(ConfigurationName)\Tmp" ConfigurationType="1" InheritedPropertySheets="UpgradeFromVC70.vsprops" CharacterSet="2" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" OmitFramePointers="true" AdditionalIncludeDirectories="..\..\..;..\..\minizip" PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;ZLIB_WINAPI;NDEBUG;_CONSOLE;WIN64" StringPooling="true" BasicRuntimeChecks="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalDependencies="ia64\ZlibDllRelease\zlibwapi.lib" OutputFile="$(OutDir)/testzlib.exe" LinkIncremental="1" GenerateManifest="false" GenerateDebugInformation="true" SubSystem="1" OptimizeReferences="2" EnableCOMDATFolding="2" OptimizeForWindows98="1" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCWebDeploymentTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> </Configurations> <References> </References> <Files> <Filter Name="Source Files" Filter="cpp;c;cxx;def;odl;idl;hpj;bat" > <File RelativePath="..\..\testzlib\testzlib.c" > </File> </Filter> <Filter Name="Header Files" Filter="h;hpp;hxx;hm;inl;inc" > </Filter> <Filter Name="Resource Files" Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" > </Filter> </Files> <Globals> </Globals> </VisualStudioProject> |
Added compat/zlib/contrib/vstudio/vc9/zlib.rc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include <windows.h> #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE FILEVERSION 1, 3, 1, 0 PRODUCTVERSION 1, 3, 1, 0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0 FILEOS VOS_DOS_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE 0 // not used BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" //language ID = U.S. English, char set = Windows, Multilingual BEGIN VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0" VALUE "FileVersion", "1.3.1\0" VALUE "InternalName", "zlib\0" VALUE "OriginalFilename", "zlibwapi.dll\0" VALUE "ProductName", "ZLib.DLL\0" VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 1252 END END |
Added compat/zlib/contrib/vstudio/vc9/zlibstat.vcproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="Windows-1252"?> <VisualStudioProject ProjectType="Visual C++" Version="9,00" Name="zlibstat" ProjectGUID="{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" TargetFrameworkVersion="131072" > <Platforms> <Platform Name="Win32" /> <Platform Name="x64" /> <Platform Name="Itanium" /> </Platforms> <ToolFiles> </ToolFiles> <Configurations> <Configuration Name="Debug|Win32" OutputDirectory="x86\ZlibStat$(ConfigurationName)" IntermediateDirectory="x86\ZlibStat$(ConfigurationName)\Tmp" ConfigurationType="4" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS" ExceptionHandling="0" RuntimeLibrary="1" BufferSecurityCheck="false" PrecompiledHeaderFile="$(IntDir)/zlibstat.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" WarningLevel="3" SuppressStartupBanner="true" Detect64BitPortabilityProblems="true" DebugInformationFormat="1" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLibrarianTool" AdditionalOptions="/MACHINE:X86 /NODEFAULTLIB" OutputFile="$(OutDir)\zlibstat.lib" SuppressStartupBanner="true" /> <Tool Name="VCALinkTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|x64" OutputDirectory="x64\ZlibStat$(ConfigurationName)" IntermediateDirectory="x64\ZlibStat$(ConfigurationName)\Tmp" ConfigurationType="4" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64" ExceptionHandling="0" RuntimeLibrary="3" BufferSecurityCheck="false" PrecompiledHeaderFile="$(IntDir)/zlibstat.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" WarningLevel="3" SuppressStartupBanner="true" Detect64BitPortabilityProblems="true" DebugInformationFormat="1" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLibrarianTool" AdditionalOptions="/MACHINE:AMD64 /NODEFAULTLIB" OutputFile="$(OutDir)\zlibstat.lib" SuppressStartupBanner="true" /> <Tool Name="VCALinkTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|Itanium" OutputDirectory="ia64\ZlibStat$(ConfigurationName)" IntermediateDirectory="ia64\ZlibStat$(ConfigurationName)\Tmp" ConfigurationType="4" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64" ExceptionHandling="0" RuntimeLibrary="3" BufferSecurityCheck="false" PrecompiledHeaderFile="$(IntDir)/zlibstat.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" WarningLevel="3" SuppressStartupBanner="true" Detect64BitPortabilityProblems="true" DebugInformationFormat="1" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLibrarianTool" AdditionalOptions="/MACHINE:IA64 /NODEFAULTLIB" OutputFile="$(OutDir)\zlibstat.lib" SuppressStartupBanner="true" /> <Tool Name="VCALinkTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Win32" OutputDirectory="x86\ZlibStat$(ConfigurationName)" IntermediateDirectory="x86\ZlibStat$(ConfigurationName)\Tmp" ConfigurationType="4" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="0" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibstat.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLibrarianTool" AdditionalOptions="/MACHINE:X86 /NODEFAULTLIB" OutputFile="$(OutDir)\zlibstat.lib" SuppressStartupBanner="true" /> <Tool Name="VCALinkTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|x64" OutputDirectory="x64\ZlibStat$(ConfigurationName)" IntermediateDirectory="x64\ZlibStat$(ConfigurationName)\Tmp" ConfigurationType="4" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibstat.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLibrarianTool" AdditionalOptions="/MACHINE:AMD64 /NODEFAULTLIB" OutputFile="$(OutDir)\zlibstat.lib" SuppressStartupBanner="true" /> <Tool Name="VCALinkTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Itanium" OutputDirectory="ia64\ZlibStat$(ConfigurationName)" IntermediateDirectory="ia64\ZlibStat$(ConfigurationName)\Tmp" ConfigurationType="4" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibstat.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLibrarianTool" AdditionalOptions="/MACHINE:IA64 /NODEFAULTLIB" OutputFile="$(OutDir)\zlibstat.lib" SuppressStartupBanner="true" /> <Tool Name="VCALinkTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="ReleaseWithoutAsm|Win32" OutputDirectory="x86\ZlibStat$(ConfigurationName)" IntermediateDirectory="x86\ZlibStat$(ConfigurationName)\Tmp" ConfigurationType="4" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="0" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibstat.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLibrarianTool" AdditionalOptions="/MACHINE:X86 /NODEFAULTLIB" OutputFile="$(OutDir)\zlibstat.lib" SuppressStartupBanner="true" /> <Tool Name="VCALinkTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="ReleaseWithoutAsm|x64" OutputDirectory="x64\ZlibStat$(ConfigurationName)" IntermediateDirectory="x64\ZlibStat$(ConfigurationName)\Tmp" ConfigurationType="4" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="3" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibstat.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLibrarianTool" AdditionalOptions="/MACHINE:AMD64 /NODEFAULTLIB" OutputFile="$(OutDir)\zlibstat.lib" SuppressStartupBanner="true" /> <Tool Name="VCALinkTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="ReleaseWithoutAsm|Itanium" OutputDirectory="ia64\ZlibStat$(ConfigurationName)" IntermediateDirectory="ia64\ZlibStat$(ConfigurationName)\Tmp" ConfigurationType="4" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" TargetEnvironment="2" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="ZLIB_WINAPI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;WIN64" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibstat.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLibrarianTool" AdditionalOptions="/MACHINE:IA64 /NODEFAULTLIB" OutputFile="$(OutDir)\zlibstat.lib" SuppressStartupBanner="true" /> <Tool Name="VCALinkTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> </Configurations> <References> </References> <Files> <Filter Name="Source Files" > <File RelativePath="..\..\..\adler32.c" > </File> <File RelativePath="..\..\..\compress.c" > </File> <File RelativePath="..\..\..\crc32.c" > </File> <File RelativePath="..\..\..\deflate.c" > </File> <File RelativePath="..\..\..\gzclose.c" > </File> <File RelativePath="..\..\..\gzguts.h" > </File> <File RelativePath="..\..\..\gzlib.c" > </File> <File RelativePath="..\..\..\gzread.c" > </File> <File RelativePath="..\..\..\gzwrite.c" > </File> <File RelativePath="..\..\..\infback.c" > </File> <File RelativePath="..\..\..\inffast.c" > </File> <File RelativePath="..\..\..\inflate.c" > </File> <File RelativePath="..\..\..\inftrees.c" > </File> <File RelativePath="..\..\minizip\ioapi.c" > </File> <File RelativePath="..\..\..\trees.c" > </File> <File RelativePath="..\..\..\uncompr.c" > </File> <File RelativePath="..\..\minizip\unzip.c" > </File> <File RelativePath="..\..\minizip\zip.c" > </File> <File RelativePath=".\zlib.rc" > </File> <File RelativePath=".\zlibvc.def" > </File> <File RelativePath="..\..\..\zutil.c" > </File> </Filter> </Files> <Globals> </Globals> </VisualStudioProject> |
Added compat/zlib/contrib/vstudio/vc9/zlibvc.def.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | LIBRARY ; zlib data compression and ZIP file I/O library VERSION 1.3.1 EXPORTS adler32 @1 compress @2 crc32 @3 deflate @4 deflateCopy @5 deflateEnd @6 deflateInit2_ @7 deflateInit_ @8 deflateParams @9 deflateReset @10 deflateSetDictionary @11 gzclose @12 gzdopen @13 gzerror @14 gzflush @15 gzopen @16 gzread @17 gzwrite @18 inflate @19 inflateEnd @20 inflateInit2_ @21 inflateInit_ @22 inflateReset @23 inflateSetDictionary @24 inflateSync @25 uncompress @26 zlibVersion @27 gzprintf @28 gzputc @29 gzgetc @30 gzseek @31 gzrewind @32 gztell @33 gzeof @34 gzsetparams @35 zError @36 inflateSyncPoint @37 get_crc_table @38 compress2 @39 gzputs @40 gzgets @41 inflateCopy @42 inflateBackInit_ @43 inflateBack @44 inflateBackEnd @45 compressBound @46 deflateBound @47 gzclearerr @48 gzungetc @49 zlibCompileFlags @50 deflatePrime @51 deflatePending @52 unzOpen @61 unzClose @62 unzGetGlobalInfo @63 unzGetCurrentFileInfo @64 unzGoToFirstFile @65 unzGoToNextFile @66 unzOpenCurrentFile @67 unzReadCurrentFile @68 unzOpenCurrentFile3 @69 unztell @70 unzeof @71 unzCloseCurrentFile @72 unzGetGlobalComment @73 unzStringFileNameCompare @74 unzLocateFile @75 unzGetLocalExtrafield @76 unzOpen2 @77 unzOpenCurrentFile2 @78 unzOpenCurrentFilePassword @79 zipOpen @80 zipOpenNewFileInZip @81 zipWriteInFileInZip @82 zipCloseFileInZip @83 zipClose @84 zipOpenNewFileInZip2 @86 zipCloseFileInZipRaw @87 zipOpen2 @88 zipOpenNewFileInZip3 @89 unzGetFilePos @100 unzGoToFilePos @101 fill_win32_filefunc @110 ; zlibwapi v1.2.4 added: fill_win32_filefunc64 @111 fill_win32_filefunc64A @112 fill_win32_filefunc64W @113 unzOpen64 @120 unzOpen2_64 @121 unzGetGlobalInfo64 @122 unzGetCurrentFileInfo64 @124 unzGetCurrentFileZStreamPos64 @125 unztell64 @126 unzGetFilePos64 @127 unzGoToFilePos64 @128 zipOpen64 @130 zipOpen2_64 @131 zipOpenNewFileInZip64 @132 zipOpenNewFileInZip2_64 @133 zipOpenNewFileInZip3_64 @134 zipOpenNewFileInZip4_64 @135 zipCloseFileInZipRaw64 @136 ; zlib1 v1.2.4 added: adler32_combine @140 crc32_combine @142 deflateSetHeader @144 deflateTune @145 gzbuffer @146 gzclose_r @147 gzclose_w @148 gzdirect @149 gzoffset @150 inflateGetHeader @156 inflateMark @157 inflatePrime @158 inflateReset2 @159 inflateUndermine @160 ; zlib1 v1.2.6 added: gzgetc_ @161 inflateResetKeep @163 deflateResetKeep @164 ; zlib1 v1.2.7 added: gzopen_w @165 ; zlib1 v1.2.8 added: inflateGetDictionary @166 gzvprintf @167 ; zlib1 v1.2.9 added: inflateCodesUsed @168 inflateValidate @169 uncompress2 @170 gzfread @171 gzfwrite @172 deflateGetDictionary @173 adler32_z @174 crc32_z @175 ; zlib1 v1.2.12 added: crc32_combine_gen @176 crc32_combine_gen64 @177 crc32_combine_op @178 |
Added compat/zlib/contrib/vstudio/vc9/zlibvc.sln.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibvc", "zlibvc.vcproj", "{8FD826F8-3739-44E6-8CC8-997122E53B8D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlibstat", "zlibstat.vcproj", "{745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testzlib", "testzlib.vcproj", "{AA6666AA-E09F-4135-9C0C-4FE50C3C654B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestZlibDll", "testzlibdll.vcproj", "{C52F9E7B-498A-42BE-8DB4-85A15694366A}" ProjectSection(ProjectDependencies) = postProject {8FD826F8-3739-44E6-8CC8-997122E53B8D} = {8FD826F8-3739-44E6-8CC8-997122E53B8D} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "minizip.vcproj", "{48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}" ProjectSection(ProjectDependencies) = postProject {8FD826F8-3739-44E6-8CC8-997122E53B8D} = {8FD826F8-3739-44E6-8CC8-997122E53B8D} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniunz", "miniunz.vcproj", "{C52F9E7B-498A-42BE-8DB4-85A15694382A}" ProjectSection(ProjectDependencies) = postProject {8FD826F8-3739-44E6-8CC8-997122E53B8D} = {8FD826F8-3739-44E6-8CC8-997122E53B8D} EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Itanium = Debug|Itanium Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Itanium = Release|Itanium Release|Win32 = Release|Win32 Release|x64 = Release|x64 ReleaseWithoutAsm|Itanium = ReleaseWithoutAsm|Itanium ReleaseWithoutAsm|Win32 = ReleaseWithoutAsm|Win32 ReleaseWithoutAsm|x64 = ReleaseWithoutAsm|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.ActiveCfg = Debug|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Itanium.Build.0 = Debug|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.ActiveCfg = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|Win32.Build.0 = Debug|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.ActiveCfg = Debug|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Debug|x64.Build.0 = Debug|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.ActiveCfg = Release|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Itanium.Build.0 = Release|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.ActiveCfg = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|Win32.Build.0 = Release|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.ActiveCfg = Release|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.Release|x64.Build.0 = Release|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {8FD826F8-3739-44E6-8CC8-997122E53B8D}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.ActiveCfg = Debug|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Itanium.Build.0 = Debug|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.ActiveCfg = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|Win32.Build.0 = Debug|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.ActiveCfg = Debug|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Debug|x64.Build.0 = Debug|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.ActiveCfg = Release|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Itanium.Build.0 = Release|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.ActiveCfg = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|Win32.Build.0 = Release|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.ActiveCfg = Release|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.Release|x64.Build.0 = Release|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {745DEC58-EBB3-47A9-A9B8-4C6627C01BF8}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = ReleaseWithoutAsm|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = ReleaseWithoutAsm|Itanium {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.Build.0 = ReleaseWithoutAsm|Win32 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = ReleaseWithoutAsm|x64 {AA6666AA-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.Build.0 = ReleaseWithoutAsm|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.ActiveCfg = Debug|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Itanium.Build.0 = Debug|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|Win32.Build.0 = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.ActiveCfg = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Debug|x64.Build.0 = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.ActiveCfg = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Itanium.Build.0 = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|Win32.Build.0 = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.Release|x64.Build.0 = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694366A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.ActiveCfg = Debug|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Itanium.Build.0 = Debug|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.ActiveCfg = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|Win32.Build.0 = Debug|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.ActiveCfg = Debug|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Debug|x64.Build.0 = Debug|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.ActiveCfg = Release|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Itanium.Build.0 = Release|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|Win32.Build.0 = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.ActiveCfg = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.Release|x64.Build.0 = Release|x64 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {48CDD9DC-E09F-4135-9C0C-4FE50C3C654B}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.ActiveCfg = Debug|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Itanium.Build.0 = Debug|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.ActiveCfg = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|Win32.Build.0 = Debug|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.ActiveCfg = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Debug|x64.Build.0 = Debug|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.ActiveCfg = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Itanium.Build.0 = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|Win32.Build.0 = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.ActiveCfg = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.Release|x64.Build.0 = Release|x64 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.ActiveCfg = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Itanium.Build.0 = Release|Itanium {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|Win32.ActiveCfg = Release|Win32 {C52F9E7B-498A-42BE-8DB4-85A15694382A}.ReleaseWithoutAsm|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal |
Added compat/zlib/contrib/vstudio/vc9/zlibvc.vcproj.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="Windows-1252"?> <VisualStudioProject ProjectType="Visual C++" Version="9,00" Name="zlibvc" ProjectGUID="{8FD826F8-3739-44E6-8CC8-997122E53B8D}" RootNamespace="zlibvc" TargetFrameworkVersion="131072" > <Platforms> <Platform Name="Win32" /> <Platform Name="x64" /> <Platform Name="Itanium" /> </Platforms> <ToolFiles> </ToolFiles> <Configurations> <Configuration Name="Debug|Win32" OutputDirectory="x86\ZlibDll$(ConfigurationName)" IntermediateDirectory="x86\ZlibDll$(ConfigurationName)\Tmp" ConfigurationType="2" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" PreprocessorDefinitions="_DEBUG" MkTypLibCompatible="true" SuppressStartupBanner="true" TargetEnvironment="1" TypeLibraryName="$(OutDir)/zlibvc.tlb" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI" ExceptionHandling="0" RuntimeLibrary="1" BufferSecurityCheck="false" PrecompiledHeaderFile="$(IntDir)/zlibvc.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" BrowseInformation="0" WarningLevel="3" SuppressStartupBanner="true" DebugInformationFormat="4" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="_DEBUG" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalOptions="/MACHINE:I386" OutputFile="$(OutDir)\zlibwapi.dll" LinkIncremental="2" SuppressStartupBanner="true" GenerateManifest="false" ModuleDefinitionFile=".\zlibvc.def" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb" GenerateMapFile="true" MapFileName="$(OutDir)/zlibwapi.map" SubSystem="2" RandomizedBaseAddress="1" DataExecutionPrevention="0" ImportLibrary="$(OutDir)/zlibwapi.lib" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|x64" OutputDirectory="x64\ZlibDll$(ConfigurationName)" IntermediateDirectory="x64\ZlibDll$(ConfigurationName)\Tmp" ConfigurationType="2" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" PreprocessorDefinitions="_DEBUG" MkTypLibCompatible="true" SuppressStartupBanner="true" TargetEnvironment="3" TypeLibraryName="$(OutDir)/zlibvc.tlb" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64" ExceptionHandling="0" RuntimeLibrary="3" BufferSecurityCheck="false" PrecompiledHeaderFile="$(IntDir)/zlibvc.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" BrowseInformation="0" WarningLevel="3" SuppressStartupBanner="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="_DEBUG" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)\zlibwapi.dll" LinkIncremental="2" SuppressStartupBanner="true" GenerateManifest="false" ModuleDefinitionFile=".\zlibvc.def" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb" GenerateMapFile="true" MapFileName="$(OutDir)/zlibwapi.map" SubSystem="2" ImportLibrary="$(OutDir)/zlibwapi.lib" TargetMachine="17" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Debug|Itanium" OutputDirectory="ia64\ZlibDll$(ConfigurationName)" IntermediateDirectory="ia64\ZlibDll$(ConfigurationName)\Tmp" ConfigurationType="2" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" PreprocessorDefinitions="_DEBUG" MkTypLibCompatible="true" SuppressStartupBanner="true" TargetEnvironment="2" TypeLibraryName="$(OutDir)/zlibvc.tlb" /> <Tool Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64" ExceptionHandling="0" RuntimeLibrary="3" BufferSecurityCheck="false" PrecompiledHeaderFile="$(IntDir)/zlibvc.pch" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" BrowseInformation="0" WarningLevel="3" SuppressStartupBanner="true" DebugInformationFormat="3" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="_DEBUG" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)\zlibwapi.dll" LinkIncremental="2" SuppressStartupBanner="true" GenerateManifest="false" ModuleDefinitionFile=".\zlibvc.def" GenerateDebugInformation="true" ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb" GenerateMapFile="true" MapFileName="$(OutDir)/zlibwapi.map" SubSystem="2" ImportLibrary="$(OutDir)/zlibwapi.lib" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="ReleaseWithoutAsm|Win32" OutputDirectory="x86\ZlibDll$(ConfigurationName)" IntermediateDirectory="x86\ZlibDll$(ConfigurationName)\Tmp" ConfigurationType="2" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" PreprocessorDefinitions="NDEBUG" MkTypLibCompatible="true" SuppressStartupBanner="true" TargetEnvironment="1" TypeLibraryName="$(OutDir)/zlibvc.tlb" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibvc.pch" AssemblerOutput="2" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" BrowseInformation="0" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="NDEBUG" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalOptions="/MACHINE:I386" OutputFile="$(OutDir)\zlibwapi.dll" LinkIncremental="1" SuppressStartupBanner="true" GenerateManifest="false" IgnoreAllDefaultLibraries="false" ModuleDefinitionFile=".\zlibvc.def" ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb" GenerateMapFile="true" MapFileName="$(OutDir)/zlibwapi.map" SubSystem="2" OptimizeForWindows98="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" ImportLibrary="$(OutDir)/zlibwapi.lib" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="ReleaseWithoutAsm|x64" OutputDirectory="x64\ZlibDll$(ConfigurationName)" IntermediateDirectory="x64\ZlibDll$(ConfigurationName)\Tmp" ConfigurationType="2" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" PreprocessorDefinitions="NDEBUG" MkTypLibCompatible="true" SuppressStartupBanner="true" TargetEnvironment="3" TypeLibraryName="$(OutDir)/zlibvc.tlb" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibvc.pch" AssemblerOutput="2" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" BrowseInformation="0" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="NDEBUG" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)\zlibwapi.dll" LinkIncremental="1" SuppressStartupBanner="true" GenerateManifest="false" IgnoreAllDefaultLibraries="false" ModuleDefinitionFile=".\zlibvc.def" ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb" GenerateMapFile="true" MapFileName="$(OutDir)/zlibwapi.map" SubSystem="2" OptimizeForWindows98="1" ImportLibrary="$(OutDir)/zlibwapi.lib" TargetMachine="17" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="ReleaseWithoutAsm|Itanium" OutputDirectory="ia64\ZlibDll$(ConfigurationName)" IntermediateDirectory="ia64\ZlibDll$(ConfigurationName)\Tmp" ConfigurationType="2" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" PreprocessorDefinitions="NDEBUG" MkTypLibCompatible="true" SuppressStartupBanner="true" TargetEnvironment="2" TypeLibraryName="$(OutDir)/zlibvc.tlb" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibvc.pch" AssemblerOutput="2" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" BrowseInformation="0" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="NDEBUG" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)\zlibwapi.dll" LinkIncremental="1" SuppressStartupBanner="true" GenerateManifest="false" IgnoreAllDefaultLibraries="false" ModuleDefinitionFile=".\zlibvc.def" ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb" GenerateMapFile="true" MapFileName="$(OutDir)/zlibwapi.map" SubSystem="2" OptimizeForWindows98="1" ImportLibrary="$(OutDir)/zlibwapi.lib" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Win32" OutputDirectory="x86\ZlibDll$(ConfigurationName)" IntermediateDirectory="x86\ZlibDll$(ConfigurationName)\Tmp" ConfigurationType="2" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" PreprocessorDefinitions="NDEBUG" MkTypLibCompatible="true" SuppressStartupBanner="true" TargetEnvironment="1" TypeLibraryName="$(OutDir)/zlibvc.tlb" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="WIN32;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="0" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibvc.pch" AssemblerOutput="2" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" BrowseInformation="0" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="NDEBUG" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" AdditionalOptions="/MACHINE:I386" OutputFile="$(OutDir)\zlibwapi.dll" LinkIncremental="1" SuppressStartupBanner="true" GenerateManifest="false" IgnoreAllDefaultLibraries="false" ModuleDefinitionFile=".\zlibvc.def" ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb" GenerateMapFile="true" MapFileName="$(OutDir)/zlibwapi.map" SubSystem="2" OptimizeForWindows98="1" RandomizedBaseAddress="1" DataExecutionPrevention="0" ImportLibrary="$(OutDir)/zlibwapi.lib" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|x64" OutputDirectory="x64\ZlibDll$(ConfigurationName)" IntermediateDirectory="x64\ZlibDll$(ConfigurationName)\Tmp" ConfigurationType="2" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" PreprocessorDefinitions="NDEBUG" MkTypLibCompatible="true" SuppressStartupBanner="true" TargetEnvironment="3" TypeLibraryName="$(OutDir)/zlibvc.tlb" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibvc.pch" AssemblerOutput="2" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" BrowseInformation="0" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="NDEBUG" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)\zlibwapi.dll" LinkIncremental="1" SuppressStartupBanner="true" GenerateManifest="false" IgnoreAllDefaultLibraries="false" ModuleDefinitionFile=".\zlibvc.def" ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb" GenerateMapFile="true" MapFileName="$(OutDir)/zlibwapi.map" SubSystem="2" OptimizeForWindows98="1" ImportLibrary="$(OutDir)/zlibwapi.lib" TargetMachine="17" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> <Configuration Name="Release|Itanium" OutputDirectory="ia64\ZlibDll$(ConfigurationName)" IntermediateDirectory="ia64\ZlibDll$(ConfigurationName)\Tmp" ConfigurationType="2" InheritedPropertySheets="UpgradeFromVC70.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" WholeProgramOptimization="1" > <Tool Name="VCPreBuildEventTool" /> <Tool Name="VCCustomBuildTool" /> <Tool Name="VCXMLDataGeneratorTool" /> <Tool Name="VCWebServiceProxyGeneratorTool" /> <Tool Name="VCMIDLTool" PreprocessorDefinitions="NDEBUG" MkTypLibCompatible="true" SuppressStartupBanner="true" TargetEnvironment="2" TypeLibraryName="$(OutDir)/zlibvc.tlb" /> <Tool Name="VCCLCompilerTool" InlineFunctionExpansion="1" AdditionalIncludeDirectories="..\..\.." PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_WARNINGS;ZLIB_WINAPI;WIN64" StringPooling="true" ExceptionHandling="0" RuntimeLibrary="2" BufferSecurityCheck="false" EnableFunctionLevelLinking="true" PrecompiledHeaderFile="$(IntDir)/zlibvc.pch" AssemblerOutput="2" AssemblerListingLocation="$(IntDir)\" ObjectFile="$(IntDir)\" ProgramDataBaseFileName="$(OutDir)\" BrowseInformation="0" WarningLevel="3" SuppressStartupBanner="true" /> <Tool Name="VCManagedResourceCompilerTool" /> <Tool Name="VCResourceCompilerTool" PreprocessorDefinitions="NDEBUG" Culture="1036" /> <Tool Name="VCPreLinkEventTool" /> <Tool Name="VCLinkerTool" OutputFile="$(OutDir)\zlibwapi.dll" LinkIncremental="1" SuppressStartupBanner="true" GenerateManifest="false" IgnoreAllDefaultLibraries="false" ModuleDefinitionFile=".\zlibvc.def" ProgramDatabaseFile="$(OutDir)/zlibwapi.pdb" GenerateMapFile="true" MapFileName="$(OutDir)/zlibwapi.map" SubSystem="2" OptimizeForWindows98="1" ImportLibrary="$(OutDir)/zlibwapi.lib" TargetMachine="5" /> <Tool Name="VCALinkTool" /> <Tool Name="VCManifestTool" /> <Tool Name="VCXDCMakeTool" /> <Tool Name="VCBscMakeTool" /> <Tool Name="VCFxCopTool" /> <Tool Name="VCAppVerifierTool" /> <Tool Name="VCPostBuildEventTool" /> </Configuration> </Configurations> <References> </References> <Files> <Filter Name="Source Files" Filter="cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" > <File RelativePath="..\..\..\adler32.c" > </File> <File RelativePath="..\..\..\compress.c" > </File> <File RelativePath="..\..\..\crc32.c" > </File> <File RelativePath="..\..\..\deflate.c" > </File> <File RelativePath="..\..\..\gzclose.c" > </File> <File RelativePath="..\..\..\gzguts.h" > </File> <File RelativePath="..\..\..\gzlib.c" > </File> <File RelativePath="..\..\..\gzread.c" > </File> <File RelativePath="..\..\..\gzwrite.c" > </File> <File RelativePath="..\..\..\infback.c" > </File> <File RelativePath="..\..\..\inffast.c" > </File> <File RelativePath="..\..\..\inflate.c" > </File> <File RelativePath="..\..\..\inftrees.c" > </File> <File RelativePath="..\..\minizip\ioapi.c" > </File> <File RelativePath="..\..\minizip\iowin32.c" > </File> <File RelativePath="..\..\..\trees.c" > </File> <File RelativePath="..\..\..\uncompr.c" > </File> <File RelativePath="..\..\minizip\unzip.c" > <FileConfiguration Name="Release|Win32" > <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="" PreprocessorDefinitions="ZLIB_INTERNAL" /> </FileConfiguration> <FileConfiguration Name="Release|x64" > <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="" PreprocessorDefinitions="ZLIB_INTERNAL" /> </FileConfiguration> <FileConfiguration Name="Release|Itanium" > <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="" PreprocessorDefinitions="ZLIB_INTERNAL" /> </FileConfiguration> </File> <File RelativePath="..\..\minizip\zip.c" > <FileConfiguration Name="Release|Win32" > <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="" PreprocessorDefinitions="ZLIB_INTERNAL" /> </FileConfiguration> <FileConfiguration Name="Release|x64" > <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="" PreprocessorDefinitions="ZLIB_INTERNAL" /> </FileConfiguration> <FileConfiguration Name="Release|Itanium" > <Tool Name="VCCLCompilerTool" AdditionalIncludeDirectories="" PreprocessorDefinitions="ZLIB_INTERNAL" /> </FileConfiguration> </File> <File RelativePath=".\zlib.rc" > </File> <File RelativePath=".\zlibvc.def" > </File> <File RelativePath="..\..\..\zutil.c" > </File> </Filter> <Filter Name="Header Files" Filter="h;hpp;hxx;hm;inl;fi;fd" > <File RelativePath="..\..\..\deflate.h" > </File> <File RelativePath="..\..\..\infblock.h" > </File> <File RelativePath="..\..\..\infcodes.h" > </File> <File RelativePath="..\..\..\inffast.h" > </File> <File RelativePath="..\..\..\inftrees.h" > </File> <File RelativePath="..\..\..\infutil.h" > </File> <File RelativePath="..\..\..\zconf.h" > </File> <File RelativePath="..\..\..\zlib.h" > </File> <File RelativePath="..\..\..\zutil.h" > </File> </Filter> <Filter Name="Resource Files" Filter="ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" > </Filter> </Files> <Globals> </Globals> </VisualStudioProject> |
Added compat/zlib/crc32.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 | /* crc32.c -- compute the CRC-32 of a data stream * Copyright (C) 1995-2022 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * * This interleaved implementation of a CRC makes use of pipelined multiple * arithmetic-logic units, commonly found in modern CPU cores. It is due to * Kadatch and Jenkins (2010). See doc/crc-doc.1.0.pdf in this distribution. */ /* @(#) $Id$ */ /* Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore protection on the static variables used to control the first-use generation of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should first call get_crc_table() to initialize the tables before allowing more than one thread to use crc32(). MAKECRCH can be #defined to write out crc32.h. A main() routine is also produced, so that this one source file can be compiled to an executable. */ #ifdef MAKECRCH # include <stdio.h> # ifndef DYNAMIC_CRC_TABLE # define DYNAMIC_CRC_TABLE # endif /* !DYNAMIC_CRC_TABLE */ #endif /* MAKECRCH */ #include "zutil.h" /* for Z_U4, Z_U8, z_crc_t, and FAR definitions */ /* A CRC of a message is computed on N braids of words in the message, where each word consists of W bytes (4 or 8). If N is 3, for example, then three running sparse CRCs are calculated respectively on each braid, at these indices in the array of words: 0, 3, 6, ..., 1, 4, 7, ..., and 2, 5, 8, ... This is done starting at a word boundary, and continues until as many blocks of N * W bytes as are available have been processed. The results are combined into a single CRC at the end. For this code, N must be in the range 1..6 and W must be 4 or 8. The upper limit on N can be increased if desired by adding more #if blocks, extending the patterns apparent in the code. In addition, crc32.h would need to be regenerated, if the maximum N value is increased. N and W are chosen empirically by benchmarking the execution time on a given processor. The choices for N and W below were based on testing on Intel Kaby Lake i7, AMD Ryzen 7, ARM Cortex-A57, Sparc64-VII, PowerPC POWER9, and MIPS64 Octeon II processors. The Intel, AMD, and ARM processors were all fastest with N=5, W=8. The Sparc, PowerPC, and MIPS64 were all fastest at N=5, W=4. They were all tested with either gcc or clang, all using the -O3 optimization level. Your mileage may vary. */ /* Define N */ #ifdef Z_TESTN # define N Z_TESTN #else # define N 5 #endif #if N < 1 || N > 6 # error N must be in 1..6 #endif /* z_crc_t must be at least 32 bits. z_word_t must be at least as long as z_crc_t. It is assumed here that z_word_t is either 32 bits or 64 bits, and that bytes are eight bits. */ /* Define W and the associated z_word_t type. If W is not defined, then a braided calculation is not used, and the associated tables and code are not compiled. */ #ifdef Z_TESTW # if Z_TESTW-1 != -1 # define W Z_TESTW # endif #else # ifdef MAKECRCH # define W 8 /* required for MAKECRCH */ # else # if defined(__x86_64__) || defined(__aarch64__) # define W 8 # else # define W 4 # endif # endif #endif #ifdef W # if W == 8 && defined(Z_U8) typedef Z_U8 z_word_t; # elif defined(Z_U4) # undef W # define W 4 typedef Z_U4 z_word_t; # else # undef W # endif #endif /* If available, use the ARM processor CRC32 instruction. */ #if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) && W == 8 # define ARMCRC32 #endif #if defined(W) && (!defined(ARMCRC32) || defined(DYNAMIC_CRC_TABLE)) /* Swap the bytes in a z_word_t to convert between little and big endian. Any self-respecting compiler will optimize this to a single machine byte-swap instruction, if one is available. This assumes that word_t is either 32 bits or 64 bits. */ local z_word_t byte_swap(z_word_t word) { # if W == 8 return (word & 0xff00000000000000) >> 56 | (word & 0xff000000000000) >> 40 | (word & 0xff0000000000) >> 24 | (word & 0xff00000000) >> 8 | (word & 0xff000000) << 8 | (word & 0xff0000) << 24 | (word & 0xff00) << 40 | (word & 0xff) << 56; # else /* W == 4 */ return (word & 0xff000000) >> 24 | (word & 0xff0000) >> 8 | (word & 0xff00) << 8 | (word & 0xff) << 24; # endif } #endif #ifdef DYNAMIC_CRC_TABLE /* ========================================================================= * Table of powers of x for combining CRC-32s, filled in by make_crc_table() * below. */ local z_crc_t FAR x2n_table[32]; #else /* ========================================================================= * Tables for byte-wise and braided CRC-32 calculations, and a table of powers * of x for combining CRC-32s, all made by make_crc_table(). */ # include "crc32.h" #endif /* CRC polynomial. */ #define POLY 0xedb88320 /* p(x) reflected, with x^32 implied */ /* Return a(x) multiplied by b(x) modulo p(x), where p(x) is the CRC polynomial, reflected. For speed, this requires that a not be zero. */ local z_crc_t multmodp(z_crc_t a, z_crc_t b) { z_crc_t m, p; m = (z_crc_t)1 << 31; p = 0; for (;;) { if (a & m) { p ^= b; if ((a & (m - 1)) == 0) break; } m >>= 1; b = b & 1 ? (b >> 1) ^ POLY : b >> 1; } return p; } /* Return x^(n * 2^k) modulo p(x). Requires that x2n_table[] has been initialized. */ local z_crc_t x2nmodp(z_off64_t n, unsigned k) { z_crc_t p; p = (z_crc_t)1 << 31; /* x^0 == 1 */ while (n) { if (n & 1) p = multmodp(x2n_table[k & 31], p); n >>= 1; k++; } return p; } #ifdef DYNAMIC_CRC_TABLE /* ========================================================================= * Build the tables for byte-wise and braided CRC-32 calculations, and a table * of powers of x for combining CRC-32s. */ local z_crc_t FAR crc_table[256]; #ifdef W local z_word_t FAR crc_big_table[256]; local z_crc_t FAR crc_braid_table[W][256]; local z_word_t FAR crc_braid_big_table[W][256]; local void braid(z_crc_t [][256], z_word_t [][256], int, int); #endif #ifdef MAKECRCH local void write_table(FILE *, const z_crc_t FAR *, int); local void write_table32hi(FILE *, const z_word_t FAR *, int); local void write_table64(FILE *, const z_word_t FAR *, int); #endif /* MAKECRCH */ /* Define a once() function depending on the availability of atomics. If this is compiled with DYNAMIC_CRC_TABLE defined, and if CRCs will be computed in multiple threads, and if atomics are not available, then get_crc_table() must be called to initialize the tables and must return before any threads are allowed to compute or combine CRCs. */ /* Definition of once functionality. */ typedef struct once_s once_t; /* Check for the availability of atomics. */ #if defined(__STDC__) && __STDC_VERSION__ >= 201112L && \ !defined(__STDC_NO_ATOMICS__) #include <stdatomic.h> /* Structure for once(), which must be initialized with ONCE_INIT. */ struct once_s { atomic_flag begun; atomic_int done; }; #define ONCE_INIT {ATOMIC_FLAG_INIT, 0} /* Run the provided init() function exactly once, even if multiple threads invoke once() at the same time. The state must be a once_t initialized with ONCE_INIT. */ local void once(once_t *state, void (*init)(void)) { if (!atomic_load(&state->done)) { if (atomic_flag_test_and_set(&state->begun)) while (!atomic_load(&state->done)) ; else { init(); atomic_store(&state->done, 1); } } } #else /* no atomics */ /* Structure for once(), which must be initialized with ONCE_INIT. */ struct once_s { volatile int begun; volatile int done; }; #define ONCE_INIT {0, 0} /* Test and set. Alas, not atomic, but tries to minimize the period of vulnerability. */ local int test_and_set(int volatile *flag) { int was; was = *flag; *flag = 1; return was; } /* Run the provided init() function once. This is not thread-safe. */ local void once(once_t *state, void (*init)(void)) { if (!state->done) { if (test_and_set(&state->begun)) while (!state->done) ; else { init(); state->done = 1; } } } #endif /* State for once(). */ local once_t made = ONCE_INIT; /* Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. Polynomials over GF(2) are represented in binary, one bit per coefficient, with the lowest powers in the most significant bit. Then adding polynomials is just exclusive-or, and multiplying a polynomial by x is a right shift by one. If we call the above polynomial p, and represent a byte as the polynomial q, also with the lowest power in the most significant bit (so the byte 0xb1 is the polynomial x^7+x^3+x^2+1), then the CRC is (q*x^32) mod p, where a mod b means the remainder after dividing a by b. This calculation is done using the shift-register method of multiplying and taking the remainder. The register is initialized to zero, and for each incoming bit, x^32 is added mod p to the register if the bit is a one (where x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by x (which is shifting right by one and adding x^32 mod p if the bit shifted out is a one). We start with the highest power (least significant bit) of q and repeat for all eight bits of q. The table is simply the CRC of all possible eight bit values. This is all the information needed to generate CRCs on data a byte at a time for all combinations of CRC register values and incoming bytes. */ local void make_crc_table(void) { unsigned i, j, n; z_crc_t p; /* initialize the CRC of bytes tables */ for (i = 0; i < 256; i++) { p = i; for (j = 0; j < 8; j++) p = p & 1 ? (p >> 1) ^ POLY : p >> 1; crc_table[i] = p; #ifdef W crc_big_table[i] = byte_swap(p); #endif } /* initialize the x^2^n mod p(x) table */ p = (z_crc_t)1 << 30; /* x^1 */ x2n_table[0] = p; for (n = 1; n < 32; n++) x2n_table[n] = p = multmodp(p, p); #ifdef W /* initialize the braiding tables -- needs x2n_table[] */ braid(crc_braid_table, crc_braid_big_table, N, W); #endif #ifdef MAKECRCH { /* The crc32.h header file contains tables for both 32-bit and 64-bit z_word_t's, and so requires a 64-bit type be available. In that case, z_word_t must be defined to be 64-bits. This code then also generates and writes out the tables for the case that z_word_t is 32 bits. */ #if !defined(W) || W != 8 # error Need a 64-bit integer type in order to generate crc32.h. #endif FILE *out; int k, n; z_crc_t ltl[8][256]; z_word_t big[8][256]; out = fopen("crc32.h", "w"); if (out == NULL) return; /* write out little-endian CRC table to crc32.h */ fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n" " * Generated automatically by crc32.c\n */\n" "\n" "local const z_crc_t FAR crc_table[] = {\n" " "); write_table(out, crc_table, 256); fprintf(out, "};\n"); /* write out big-endian CRC table for 64-bit z_word_t to crc32.h */ fprintf(out, "\n" "#ifdef W\n" "\n" "#if W == 8\n" "\n" "local const z_word_t FAR crc_big_table[] = {\n" " "); write_table64(out, crc_big_table, 256); fprintf(out, "};\n"); /* write out big-endian CRC table for 32-bit z_word_t to crc32.h */ fprintf(out, "\n" "#else /* W == 4 */\n" "\n" "local const z_word_t FAR crc_big_table[] = {\n" " "); write_table32hi(out, crc_big_table, 256); fprintf(out, "};\n" "\n" "#endif\n"); /* write out braid tables for each value of N */ for (n = 1; n <= 6; n++) { fprintf(out, "\n" "#if N == %d\n", n); /* compute braid tables for this N and 64-bit word_t */ braid(ltl, big, n, 8); /* write out braid tables for 64-bit z_word_t to crc32.h */ fprintf(out, "\n" "#if W == 8\n" "\n" "local const z_crc_t FAR crc_braid_table[][256] = {\n"); for (k = 0; k < 8; k++) { fprintf(out, " {"); write_table(out, ltl[k], 256); fprintf(out, "}%s", k < 7 ? ",\n" : ""); } fprintf(out, "};\n" "\n" "local const z_word_t FAR crc_braid_big_table[][256] = {\n"); for (k = 0; k < 8; k++) { fprintf(out, " {"); write_table64(out, big[k], 256); fprintf(out, "}%s", k < 7 ? ",\n" : ""); } fprintf(out, "};\n"); /* compute braid tables for this N and 32-bit word_t */ braid(ltl, big, n, 4); /* write out braid tables for 32-bit z_word_t to crc32.h */ fprintf(out, "\n" "#else /* W == 4 */\n" "\n" "local const z_crc_t FAR crc_braid_table[][256] = {\n"); for (k = 0; k < 4; k++) { fprintf(out, " {"); write_table(out, ltl[k], 256); fprintf(out, "}%s", k < 3 ? ",\n" : ""); } fprintf(out, "};\n" "\n" "local const z_word_t FAR crc_braid_big_table[][256] = {\n"); for (k = 0; k < 4; k++) { fprintf(out, " {"); write_table32hi(out, big[k], 256); fprintf(out, "}%s", k < 3 ? ",\n" : ""); } fprintf(out, "};\n" "\n" "#endif\n" "\n" "#endif\n"); } fprintf(out, "\n" "#endif\n"); /* write out zeros operator table to crc32.h */ fprintf(out, "\n" "local const z_crc_t FAR x2n_table[] = {\n" " "); write_table(out, x2n_table, 32); fprintf(out, "};\n"); fclose(out); } #endif /* MAKECRCH */ } #ifdef MAKECRCH /* Write the 32-bit values in table[0..k-1] to out, five per line in hexadecimal separated by commas. */ local void write_table(FILE *out, const z_crc_t FAR *table, int k) { int n; for (n = 0; n < k; n++) fprintf(out, "%s0x%08lx%s", n == 0 || n % 5 ? "" : " ", (unsigned long)(table[n]), n == k - 1 ? "" : (n % 5 == 4 ? ",\n" : ", ")); } /* Write the high 32-bits of each value in table[0..k-1] to out, five per line in hexadecimal separated by commas. */ local void write_table32hi(FILE *out, const z_word_t FAR *table, int k) { int n; for (n = 0; n < k; n++) fprintf(out, "%s0x%08lx%s", n == 0 || n % 5 ? "" : " ", (unsigned long)(table[n] >> 32), n == k - 1 ? "" : (n % 5 == 4 ? ",\n" : ", ")); } /* Write the 64-bit values in table[0..k-1] to out, three per line in hexadecimal separated by commas. This assumes that if there is a 64-bit type, then there is also a long long integer type, and it is at least 64 bits. If not, then the type cast and format string can be adjusted accordingly. */ local void write_table64(FILE *out, const z_word_t FAR *table, int k) { int n; for (n = 0; n < k; n++) fprintf(out, "%s0x%016llx%s", n == 0 || n % 3 ? "" : " ", (unsigned long long)(table[n]), n == k - 1 ? "" : (n % 3 == 2 ? ",\n" : ", ")); } /* Actually do the deed. */ int main(void) { make_crc_table(); return 0; } #endif /* MAKECRCH */ #ifdef W /* Generate the little and big-endian braid tables for the given n and z_word_t size w. Each array must have room for w blocks of 256 elements. */ local void braid(z_crc_t ltl[][256], z_word_t big[][256], int n, int w) { int k; z_crc_t i, p, q; for (k = 0; k < w; k++) { p = x2nmodp((n * w + 3 - k) << 3, 0); ltl[k][0] = 0; big[w - 1 - k][0] = 0; for (i = 1; i < 256; i++) { ltl[k][i] = q = multmodp(i << 24, p); big[w - 1 - k][i] = byte_swap(q); } } } #endif #endif /* DYNAMIC_CRC_TABLE */ /* ========================================================================= * This function can be used by asm versions of crc32(), and to force the * generation of the CRC tables in a threaded application. */ const z_crc_t FAR * ZEXPORT get_crc_table(void) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ return (const z_crc_t FAR *)crc_table; } /* ========================================================================= * Use ARM machine instructions if available. This will compute the CRC about * ten times faster than the braided calculation. This code does not check for * the presence of the CRC instruction at run time. __ARM_FEATURE_CRC32 will * only be defined if the compilation specifies an ARM processor architecture * that has the instructions. For example, compiling with -march=armv8.1-a or * -march=armv8-a+crc, or -march=native if the compile machine has the crc32 * instructions. */ #ifdef ARMCRC32 /* Constants empirically determined to maximize speed. These values are from measurements on a Cortex-A57. Your mileage may vary. */ #define Z_BATCH 3990 /* number of words in a batch */ #define Z_BATCH_ZEROS 0xa10d3d0c /* computed from Z_BATCH = 3990 */ #define Z_BATCH_MIN 800 /* fewest words in a final batch */ unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf, z_size_t len) { z_crc_t val; z_word_t crc1, crc2; const z_word_t *word; z_word_t val0, val1, val2; z_size_t last, last2, i; z_size_t num; /* Return initial CRC, if requested. */ if (buf == Z_NULL) return 0; #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ /* Pre-condition the CRC */ crc = (~crc) & 0xffffffff; /* Compute the CRC up to a word boundary. */ while (len && ((z_size_t)buf & 7) != 0) { len--; val = *buf++; __asm__ volatile("crc32b %w0, %w0, %w1" : "+r"(crc) : "r"(val)); } /* Prepare to compute the CRC on full 64-bit words word[0..num-1]. */ word = (z_word_t const *)buf; num = len >> 3; len &= 7; /* Do three interleaved CRCs to realize the throughput of one crc32x instruction per cycle. Each CRC is calculated on Z_BATCH words. The three CRCs are combined into a single CRC after each set of batches. */ while (num >= 3 * Z_BATCH) { crc1 = 0; crc2 = 0; for (i = 0; i < Z_BATCH; i++) { val0 = word[i]; val1 = word[i + Z_BATCH]; val2 = word[i + 2 * Z_BATCH]; __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc) : "r"(val0)); __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc1) : "r"(val1)); __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc2) : "r"(val2)); } word += 3 * Z_BATCH; num -= 3 * Z_BATCH; crc = multmodp(Z_BATCH_ZEROS, crc) ^ crc1; crc = multmodp(Z_BATCH_ZEROS, crc) ^ crc2; } /* Do one last smaller batch with the remaining words, if there are enough to pay for the combination of CRCs. */ last = num / 3; if (last >= Z_BATCH_MIN) { last2 = last << 1; crc1 = 0; crc2 = 0; for (i = 0; i < last; i++) { val0 = word[i]; val1 = word[i + last]; val2 = word[i + last2]; __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc) : "r"(val0)); __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc1) : "r"(val1)); __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc2) : "r"(val2)); } word += 3 * last; num -= 3 * last; val = x2nmodp(last, 6); crc = multmodp(val, crc) ^ crc1; crc = multmodp(val, crc) ^ crc2; } /* Compute the CRC on any remaining words. */ for (i = 0; i < num; i++) { val0 = word[i]; __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc) : "r"(val0)); } word += num; /* Complete the CRC on any remaining bytes. */ buf = (const unsigned char FAR *)word; while (len) { len--; val = *buf++; __asm__ volatile("crc32b %w0, %w0, %w1" : "+r"(crc) : "r"(val)); } /* Return the CRC, post-conditioned. */ return crc ^ 0xffffffff; } #else #ifdef W /* Return the CRC of the W bytes in the word_t data, taking the least-significant byte of the word as the first byte of data, without any pre or post conditioning. This is used to combine the CRCs of each braid. */ local z_crc_t crc_word(z_word_t data) { int k; for (k = 0; k < W; k++) data = (data >> 8) ^ crc_table[data & 0xff]; return (z_crc_t)data; } local z_word_t crc_word_big(z_word_t data) { int k; for (k = 0; k < W; k++) data = (data << 8) ^ crc_big_table[(data >> ((W - 1) << 3)) & 0xff]; return data; } #endif /* ========================================================================= */ unsigned long ZEXPORT crc32_z(unsigned long crc, const unsigned char FAR *buf, z_size_t len) { /* Return initial CRC, if requested. */ if (buf == Z_NULL) return 0; #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ /* Pre-condition the CRC */ crc = (~crc) & 0xffffffff; #ifdef W /* If provided enough bytes, do a braided CRC calculation. */ if (len >= N * W + W - 1) { z_size_t blks; z_word_t const *words; unsigned endian; int k; /* Compute the CRC up to a z_word_t boundary. */ while (len && ((z_size_t)buf & (W - 1)) != 0) { len--; crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; } /* Compute the CRC on as many N z_word_t blocks as are available. */ blks = len / (N * W); len -= blks * N * W; words = (z_word_t const *)buf; /* Do endian check at execution time instead of compile time, since ARM processors can change the endianness at execution time. If the compiler knows what the endianness will be, it can optimize out the check and the unused branch. */ endian = 1; if (*(unsigned char *)&endian) { /* Little endian. */ z_crc_t crc0; z_word_t word0; #if N > 1 z_crc_t crc1; z_word_t word1; #if N > 2 z_crc_t crc2; z_word_t word2; #if N > 3 z_crc_t crc3; z_word_t word3; #if N > 4 z_crc_t crc4; z_word_t word4; #if N > 5 z_crc_t crc5; z_word_t word5; #endif #endif #endif #endif #endif /* Initialize the CRC for each braid. */ crc0 = crc; #if N > 1 crc1 = 0; #if N > 2 crc2 = 0; #if N > 3 crc3 = 0; #if N > 4 crc4 = 0; #if N > 5 crc5 = 0; #endif #endif #endif #endif #endif /* Process the first blks-1 blocks, computing the CRCs on each braid independently. */ while (--blks) { /* Load the word for each braid into registers. */ word0 = crc0 ^ words[0]; #if N > 1 word1 = crc1 ^ words[1]; #if N > 2 word2 = crc2 ^ words[2]; #if N > 3 word3 = crc3 ^ words[3]; #if N > 4 word4 = crc4 ^ words[4]; #if N > 5 word5 = crc5 ^ words[5]; #endif #endif #endif #endif #endif words += N; /* Compute and update the CRC for each word. The loop should get unrolled. */ crc0 = crc_braid_table[0][word0 & 0xff]; #if N > 1 crc1 = crc_braid_table[0][word1 & 0xff]; #if N > 2 crc2 = crc_braid_table[0][word2 & 0xff]; #if N > 3 crc3 = crc_braid_table[0][word3 & 0xff]; #if N > 4 crc4 = crc_braid_table[0][word4 & 0xff]; #if N > 5 crc5 = crc_braid_table[0][word5 & 0xff]; #endif #endif #endif #endif #endif for (k = 1; k < W; k++) { crc0 ^= crc_braid_table[k][(word0 >> (k << 3)) & 0xff]; #if N > 1 crc1 ^= crc_braid_table[k][(word1 >> (k << 3)) & 0xff]; #if N > 2 crc2 ^= crc_braid_table[k][(word2 >> (k << 3)) & 0xff]; #if N > 3 crc3 ^= crc_braid_table[k][(word3 >> (k << 3)) & 0xff]; #if N > 4 crc4 ^= crc_braid_table[k][(word4 >> (k << 3)) & 0xff]; #if N > 5 crc5 ^= crc_braid_table[k][(word5 >> (k << 3)) & 0xff]; #endif #endif #endif #endif #endif } } /* Process the last block, combining the CRCs of the N braids at the same time. */ crc = crc_word(crc0 ^ words[0]); #if N > 1 crc = crc_word(crc1 ^ words[1] ^ crc); #if N > 2 crc = crc_word(crc2 ^ words[2] ^ crc); #if N > 3 crc = crc_word(crc3 ^ words[3] ^ crc); #if N > 4 crc = crc_word(crc4 ^ words[4] ^ crc); #if N > 5 crc = crc_word(crc5 ^ words[5] ^ crc); #endif #endif #endif #endif #endif words += N; } else { /* Big endian. */ z_word_t crc0, word0, comb; #if N > 1 z_word_t crc1, word1; #if N > 2 z_word_t crc2, word2; #if N > 3 z_word_t crc3, word3; #if N > 4 z_word_t crc4, word4; #if N > 5 z_word_t crc5, word5; #endif #endif #endif #endif #endif /* Initialize the CRC for each braid. */ crc0 = byte_swap(crc); #if N > 1 crc1 = 0; #if N > 2 crc2 = 0; #if N > 3 crc3 = 0; #if N > 4 crc4 = 0; #if N > 5 crc5 = 0; #endif #endif #endif #endif #endif /* Process the first blks-1 blocks, computing the CRCs on each braid independently. */ while (--blks) { /* Load the word for each braid into registers. */ word0 = crc0 ^ words[0]; #if N > 1 word1 = crc1 ^ words[1]; #if N > 2 word2 = crc2 ^ words[2]; #if N > 3 word3 = crc3 ^ words[3]; #if N > 4 word4 = crc4 ^ words[4]; #if N > 5 word5 = crc5 ^ words[5]; #endif #endif #endif #endif #endif words += N; /* Compute and update the CRC for each word. The loop should get unrolled. */ crc0 = crc_braid_big_table[0][word0 & 0xff]; #if N > 1 crc1 = crc_braid_big_table[0][word1 & 0xff]; #if N > 2 crc2 = crc_braid_big_table[0][word2 & 0xff]; #if N > 3 crc3 = crc_braid_big_table[0][word3 & 0xff]; #if N > 4 crc4 = crc_braid_big_table[0][word4 & 0xff]; #if N > 5 crc5 = crc_braid_big_table[0][word5 & 0xff]; #endif #endif #endif #endif #endif for (k = 1; k < W; k++) { crc0 ^= crc_braid_big_table[k][(word0 >> (k << 3)) & 0xff]; #if N > 1 crc1 ^= crc_braid_big_table[k][(word1 >> (k << 3)) & 0xff]; #if N > 2 crc2 ^= crc_braid_big_table[k][(word2 >> (k << 3)) & 0xff]; #if N > 3 crc3 ^= crc_braid_big_table[k][(word3 >> (k << 3)) & 0xff]; #if N > 4 crc4 ^= crc_braid_big_table[k][(word4 >> (k << 3)) & 0xff]; #if N > 5 crc5 ^= crc_braid_big_table[k][(word5 >> (k << 3)) & 0xff]; #endif #endif #endif #endif #endif } } /* Process the last block, combining the CRCs of the N braids at the same time. */ comb = crc_word_big(crc0 ^ words[0]); #if N > 1 comb = crc_word_big(crc1 ^ words[1] ^ comb); #if N > 2 comb = crc_word_big(crc2 ^ words[2] ^ comb); #if N > 3 comb = crc_word_big(crc3 ^ words[3] ^ comb); #if N > 4 comb = crc_word_big(crc4 ^ words[4] ^ comb); #if N > 5 comb = crc_word_big(crc5 ^ words[5] ^ comb); #endif #endif #endif #endif #endif words += N; crc = byte_swap(comb); } /* Update the pointer to the remaining bytes to process. */ buf = (unsigned char const *)words; } #endif /* W */ /* Complete the computation of the CRC on any remaining bytes. */ while (len >= 8) { len -= 8; crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; } while (len) { len--; crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; } /* Return the CRC, post-conditioned. */ return crc ^ 0xffffffff; } #endif /* ========================================================================= */ unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, uInt len) { return crc32_z(crc, buf, len); } /* ========================================================================= */ uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ return multmodp(x2nmodp(len2, 3), crc1) ^ (crc2 & 0xffffffff); } /* ========================================================================= */ uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) { return crc32_combine64(crc1, crc2, (z_off64_t)len2); } /* ========================================================================= */ uLong ZEXPORT crc32_combine_gen64(z_off64_t len2) { #ifdef DYNAMIC_CRC_TABLE once(&made, make_crc_table); #endif /* DYNAMIC_CRC_TABLE */ return x2nmodp(len2, 3); } /* ========================================================================= */ uLong ZEXPORT crc32_combine_gen(z_off_t len2) { return crc32_combine_gen64((z_off64_t)len2); } /* ========================================================================= */ uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op) { return multmodp(op, crc1) ^ (crc2 & 0xffffffff); } |
Added compat/zlib/crc32.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 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 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 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 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 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 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 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 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 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 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 5433 5434 5435 5436 5437 5438 5439 5440 5441 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 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 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 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 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 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 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 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 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 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 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 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 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 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 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 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 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 6189 6190 6191 6192 6193 6194 6195 6196 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 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 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 6278 6279 6280 6281 6282 6283 6284 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 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 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 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 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 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 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 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 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 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 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 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 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 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 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 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 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 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 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 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 7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 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 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278 8279 8280 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 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 8440 8441 8442 8443 8444 8445 8446 8447 8448 8449 8450 8451 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 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 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 8663 8664 8665 8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678 8679 8680 8681 8682 8683 8684 8685 8686 8687 8688 8689 8690 8691 8692 8693 8694 8695 8696 8697 8698 8699 8700 8701 8702 8703 8704 8705 8706 8707 8708 8709 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797 8798 8799 8800 8801 8802 8803 8804 8805 8806 8807 8808 8809 8810 8811 8812 8813 8814 8815 8816 8817 8818 8819 8820 8821 8822 8823 8824 8825 8826 8827 8828 8829 8830 8831 8832 8833 8834 8835 8836 8837 8838 8839 8840 8841 8842 8843 8844 8845 8846 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 8897 8898 8899 8900 8901 8902 8903 8904 8905 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 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 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 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 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 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 9139 9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162 9163 9164 9165 9166 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 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 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 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 9324 9325 9326 9327 9328 9329 9330 9331 9332 9333 9334 9335 9336 9337 9338 9339 9340 9341 9342 9343 9344 9345 9346 9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358 9359 9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392 9393 9394 9395 9396 9397 9398 9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 | /* crc32.h -- tables for rapid CRC calculation * Generated automatically by crc32.c */ local const z_crc_t FAR crc_table[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; #ifdef W #if W == 8 local const z_word_t FAR crc_big_table[] = { 0x0000000000000000, 0x9630077700000000, 0x2c610eee00000000, 0xba51099900000000, 0x19c46d0700000000, 0x8ff46a7000000000, 0x35a563e900000000, 0xa395649e00000000, 0x3288db0e00000000, 0xa4b8dc7900000000, 0x1ee9d5e000000000, 0x88d9d29700000000, 0x2b4cb60900000000, 0xbd7cb17e00000000, 0x072db8e700000000, 0x911dbf9000000000, 0x6410b71d00000000, 0xf220b06a00000000, 0x4871b9f300000000, 0xde41be8400000000, 0x7dd4da1a00000000, 0xebe4dd6d00000000, 0x51b5d4f400000000, 0xc785d38300000000, 0x56986c1300000000, 0xc0a86b6400000000, 0x7af962fd00000000, 0xecc9658a00000000, 0x4f5c011400000000, 0xd96c066300000000, 0x633d0ffa00000000, 0xf50d088d00000000, 0xc8206e3b00000000, 0x5e10694c00000000, 0xe44160d500000000, 0x727167a200000000, 0xd1e4033c00000000, 0x47d4044b00000000, 0xfd850dd200000000, 0x6bb50aa500000000, 0xfaa8b53500000000, 0x6c98b24200000000, 0xd6c9bbdb00000000, 0x40f9bcac00000000, 0xe36cd83200000000, 0x755cdf4500000000, 0xcf0dd6dc00000000, 0x593dd1ab00000000, 0xac30d92600000000, 0x3a00de5100000000, 0x8051d7c800000000, 0x1661d0bf00000000, 0xb5f4b42100000000, 0x23c4b35600000000, 0x9995bacf00000000, 0x0fa5bdb800000000, 0x9eb8022800000000, 0x0888055f00000000, 0xb2d90cc600000000, 0x24e90bb100000000, 0x877c6f2f00000000, 0x114c685800000000, 0xab1d61c100000000, 0x3d2d66b600000000, 0x9041dc7600000000, 0x0671db0100000000, 0xbc20d29800000000, 0x2a10d5ef00000000, 0x8985b17100000000, 0x1fb5b60600000000, 0xa5e4bf9f00000000, 0x33d4b8e800000000, 0xa2c9077800000000, 0x34f9000f00000000, 0x8ea8099600000000, 0x18980ee100000000, 0xbb0d6a7f00000000, 0x2d3d6d0800000000, 0x976c649100000000, 0x015c63e600000000, 0xf4516b6b00000000, 0x62616c1c00000000, 0xd830658500000000, 0x4e0062f200000000, 0xed95066c00000000, 0x7ba5011b00000000, 0xc1f4088200000000, 0x57c40ff500000000, 0xc6d9b06500000000, 0x50e9b71200000000, 0xeab8be8b00000000, 0x7c88b9fc00000000, 0xdf1ddd6200000000, 0x492dda1500000000, 0xf37cd38c00000000, 0x654cd4fb00000000, 0x5861b24d00000000, 0xce51b53a00000000, 0x7400bca300000000, 0xe230bbd400000000, 0x41a5df4a00000000, 0xd795d83d00000000, 0x6dc4d1a400000000, 0xfbf4d6d300000000, 0x6ae9694300000000, 0xfcd96e3400000000, 0x468867ad00000000, 0xd0b860da00000000, 0x732d044400000000, 0xe51d033300000000, 0x5f4c0aaa00000000, 0xc97c0ddd00000000, 0x3c71055000000000, 0xaa41022700000000, 0x10100bbe00000000, 0x86200cc900000000, 0x25b5685700000000, 0xb3856f2000000000, 0x09d466b900000000, 0x9fe461ce00000000, 0x0ef9de5e00000000, 0x98c9d92900000000, 0x2298d0b000000000, 0xb4a8d7c700000000, 0x173db35900000000, 0x810db42e00000000, 0x3b5cbdb700000000, 0xad6cbac000000000, 0x2083b8ed00000000, 0xb6b3bf9a00000000, 0x0ce2b60300000000, 0x9ad2b17400000000, 0x3947d5ea00000000, 0xaf77d29d00000000, 0x1526db0400000000, 0x8316dc7300000000, 0x120b63e300000000, 0x843b649400000000, 0x3e6a6d0d00000000, 0xa85a6a7a00000000, 0x0bcf0ee400000000, 0x9dff099300000000, 0x27ae000a00000000, 0xb19e077d00000000, 0x44930ff000000000, 0xd2a3088700000000, 0x68f2011e00000000, 0xfec2066900000000, 0x5d5762f700000000, 0xcb67658000000000, 0x71366c1900000000, 0xe7066b6e00000000, 0x761bd4fe00000000, 0xe02bd38900000000, 0x5a7ada1000000000, 0xcc4add6700000000, 0x6fdfb9f900000000, 0xf9efbe8e00000000, 0x43beb71700000000, 0xd58eb06000000000, 0xe8a3d6d600000000, 0x7e93d1a100000000, 0xc4c2d83800000000, 0x52f2df4f00000000, 0xf167bbd100000000, 0x6757bca600000000, 0xdd06b53f00000000, 0x4b36b24800000000, 0xda2b0dd800000000, 0x4c1b0aaf00000000, 0xf64a033600000000, 0x607a044100000000, 0xc3ef60df00000000, 0x55df67a800000000, 0xef8e6e3100000000, 0x79be694600000000, 0x8cb361cb00000000, 0x1a8366bc00000000, 0xa0d26f2500000000, 0x36e2685200000000, 0x95770ccc00000000, 0x03470bbb00000000, 0xb916022200000000, 0x2f26055500000000, 0xbe3bbac500000000, 0x280bbdb200000000, 0x925ab42b00000000, 0x046ab35c00000000, 0xa7ffd7c200000000, 0x31cfd0b500000000, 0x8b9ed92c00000000, 0x1daede5b00000000, 0xb0c2649b00000000, 0x26f263ec00000000, 0x9ca36a7500000000, 0x0a936d0200000000, 0xa906099c00000000, 0x3f360eeb00000000, 0x8567077200000000, 0x1357000500000000, 0x824abf9500000000, 0x147ab8e200000000, 0xae2bb17b00000000, 0x381bb60c00000000, 0x9b8ed29200000000, 0x0dbed5e500000000, 0xb7efdc7c00000000, 0x21dfdb0b00000000, 0xd4d2d38600000000, 0x42e2d4f100000000, 0xf8b3dd6800000000, 0x6e83da1f00000000, 0xcd16be8100000000, 0x5b26b9f600000000, 0xe177b06f00000000, 0x7747b71800000000, 0xe65a088800000000, 0x706a0fff00000000, 0xca3b066600000000, 0x5c0b011100000000, 0xff9e658f00000000, 0x69ae62f800000000, 0xd3ff6b6100000000, 0x45cf6c1600000000, 0x78e20aa000000000, 0xeed20dd700000000, 0x5483044e00000000, 0xc2b3033900000000, 0x612667a700000000, 0xf71660d000000000, 0x4d47694900000000, 0xdb776e3e00000000, 0x4a6ad1ae00000000, 0xdc5ad6d900000000, 0x660bdf4000000000, 0xf03bd83700000000, 0x53aebca900000000, 0xc59ebbde00000000, 0x7fcfb24700000000, 0xe9ffb53000000000, 0x1cf2bdbd00000000, 0x8ac2baca00000000, 0x3093b35300000000, 0xa6a3b42400000000, 0x0536d0ba00000000, 0x9306d7cd00000000, 0x2957de5400000000, 0xbf67d92300000000, 0x2e7a66b300000000, 0xb84a61c400000000, 0x021b685d00000000, 0x942b6f2a00000000, 0x37be0bb400000000, 0xa18e0cc300000000, 0x1bdf055a00000000, 0x8def022d00000000}; #else /* W == 4 */ local const z_word_t FAR crc_big_table[] = { 0x00000000, 0x96300777, 0x2c610eee, 0xba510999, 0x19c46d07, 0x8ff46a70, 0x35a563e9, 0xa395649e, 0x3288db0e, 0xa4b8dc79, 0x1ee9d5e0, 0x88d9d297, 0x2b4cb609, 0xbd7cb17e, 0x072db8e7, 0x911dbf90, 0x6410b71d, 0xf220b06a, 0x4871b9f3, 0xde41be84, 0x7dd4da1a, 0xebe4dd6d, 0x51b5d4f4, 0xc785d383, 0x56986c13, 0xc0a86b64, 0x7af962fd, 0xecc9658a, 0x4f5c0114, 0xd96c0663, 0x633d0ffa, 0xf50d088d, 0xc8206e3b, 0x5e10694c, 0xe44160d5, 0x727167a2, 0xd1e4033c, 0x47d4044b, 0xfd850dd2, 0x6bb50aa5, 0xfaa8b535, 0x6c98b242, 0xd6c9bbdb, 0x40f9bcac, 0xe36cd832, 0x755cdf45, 0xcf0dd6dc, 0x593dd1ab, 0xac30d926, 0x3a00de51, 0x8051d7c8, 0x1661d0bf, 0xb5f4b421, 0x23c4b356, 0x9995bacf, 0x0fa5bdb8, 0x9eb80228, 0x0888055f, 0xb2d90cc6, 0x24e90bb1, 0x877c6f2f, 0x114c6858, 0xab1d61c1, 0x3d2d66b6, 0x9041dc76, 0x0671db01, 0xbc20d298, 0x2a10d5ef, 0x8985b171, 0x1fb5b606, 0xa5e4bf9f, 0x33d4b8e8, 0xa2c90778, 0x34f9000f, 0x8ea80996, 0x18980ee1, 0xbb0d6a7f, 0x2d3d6d08, 0x976c6491, 0x015c63e6, 0xf4516b6b, 0x62616c1c, 0xd8306585, 0x4e0062f2, 0xed95066c, 0x7ba5011b, 0xc1f40882, 0x57c40ff5, 0xc6d9b065, 0x50e9b712, 0xeab8be8b, 0x7c88b9fc, 0xdf1ddd62, 0x492dda15, 0xf37cd38c, 0x654cd4fb, 0x5861b24d, 0xce51b53a, 0x7400bca3, 0xe230bbd4, 0x41a5df4a, 0xd795d83d, 0x6dc4d1a4, 0xfbf4d6d3, 0x6ae96943, 0xfcd96e34, 0x468867ad, 0xd0b860da, 0x732d0444, 0xe51d0333, 0x5f4c0aaa, 0xc97c0ddd, 0x3c710550, 0xaa410227, 0x10100bbe, 0x86200cc9, 0x25b56857, 0xb3856f20, 0x09d466b9, 0x9fe461ce, 0x0ef9de5e, 0x98c9d929, 0x2298d0b0, 0xb4a8d7c7, 0x173db359, 0x810db42e, 0x3b5cbdb7, 0xad6cbac0, 0x2083b8ed, 0xb6b3bf9a, 0x0ce2b603, 0x9ad2b174, 0x3947d5ea, 0xaf77d29d, 0x1526db04, 0x8316dc73, 0x120b63e3, 0x843b6494, 0x3e6a6d0d, 0xa85a6a7a, 0x0bcf0ee4, 0x9dff0993, 0x27ae000a, 0xb19e077d, 0x44930ff0, 0xd2a30887, 0x68f2011e, 0xfec20669, 0x5d5762f7, 0xcb676580, 0x71366c19, 0xe7066b6e, 0x761bd4fe, 0xe02bd389, 0x5a7ada10, 0xcc4add67, 0x6fdfb9f9, 0xf9efbe8e, 0x43beb717, 0xd58eb060, 0xe8a3d6d6, 0x7e93d1a1, 0xc4c2d838, 0x52f2df4f, 0xf167bbd1, 0x6757bca6, 0xdd06b53f, 0x4b36b248, 0xda2b0dd8, 0x4c1b0aaf, 0xf64a0336, 0x607a0441, 0xc3ef60df, 0x55df67a8, 0xef8e6e31, 0x79be6946, 0x8cb361cb, 0x1a8366bc, 0xa0d26f25, 0x36e26852, 0x95770ccc, 0x03470bbb, 0xb9160222, 0x2f260555, 0xbe3bbac5, 0x280bbdb2, 0x925ab42b, 0x046ab35c, 0xa7ffd7c2, 0x31cfd0b5, 0x8b9ed92c, 0x1daede5b, 0xb0c2649b, 0x26f263ec, 0x9ca36a75, 0x0a936d02, 0xa906099c, 0x3f360eeb, 0x85670772, 0x13570005, 0x824abf95, 0x147ab8e2, 0xae2bb17b, 0x381bb60c, 0x9b8ed292, 0x0dbed5e5, 0xb7efdc7c, 0x21dfdb0b, 0xd4d2d386, 0x42e2d4f1, 0xf8b3dd68, 0x6e83da1f, 0xcd16be81, 0x5b26b9f6, 0xe177b06f, 0x7747b718, 0xe65a0888, 0x706a0fff, 0xca3b0666, 0x5c0b0111, 0xff9e658f, 0x69ae62f8, 0xd3ff6b61, 0x45cf6c16, 0x78e20aa0, 0xeed20dd7, 0x5483044e, 0xc2b30339, 0x612667a7, 0xf71660d0, 0x4d476949, 0xdb776e3e, 0x4a6ad1ae, 0xdc5ad6d9, 0x660bdf40, 0xf03bd837, 0x53aebca9, 0xc59ebbde, 0x7fcfb247, 0xe9ffb530, 0x1cf2bdbd, 0x8ac2baca, 0x3093b353, 0xa6a3b424, 0x0536d0ba, 0x9306d7cd, 0x2957de54, 0xbf67d923, 0x2e7a66b3, 0xb84a61c4, 0x021b685d, 0x942b6f2a, 0x37be0bb4, 0xa18e0cc3, 0x1bdf055a, 0x8def022d}; #endif #if N == 1 #if W == 8 local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0xccaa009e, 0x4225077d, 0x8e8f07e3, 0x844a0efa, 0x48e00e64, 0xc66f0987, 0x0ac50919, 0xd3e51bb5, 0x1f4f1b2b, 0x91c01cc8, 0x5d6a1c56, 0x57af154f, 0x9b0515d1, 0x158a1232, 0xd92012ac, 0x7cbb312b, 0xb01131b5, 0x3e9e3656, 0xf23436c8, 0xf8f13fd1, 0x345b3f4f, 0xbad438ac, 0x767e3832, 0xaf5e2a9e, 0x63f42a00, 0xed7b2de3, 0x21d12d7d, 0x2b142464, 0xe7be24fa, 0x69312319, 0xa59b2387, 0xf9766256, 0x35dc62c8, 0xbb53652b, 0x77f965b5, 0x7d3c6cac, 0xb1966c32, 0x3f196bd1, 0xf3b36b4f, 0x2a9379e3, 0xe639797d, 0x68b67e9e, 0xa41c7e00, 0xaed97719, 0x62737787, 0xecfc7064, 0x205670fa, 0x85cd537d, 0x496753e3, 0xc7e85400, 0x0b42549e, 0x01875d87, 0xcd2d5d19, 0x43a25afa, 0x8f085a64, 0x562848c8, 0x9a824856, 0x140d4fb5, 0xd8a74f2b, 0xd2624632, 0x1ec846ac, 0x9047414f, 0x5ced41d1, 0x299dc2ed, 0xe537c273, 0x6bb8c590, 0xa712c50e, 0xadd7cc17, 0x617dcc89, 0xeff2cb6a, 0x2358cbf4, 0xfa78d958, 0x36d2d9c6, 0xb85dde25, 0x74f7debb, 0x7e32d7a2, 0xb298d73c, 0x3c17d0df, 0xf0bdd041, 0x5526f3c6, 0x998cf358, 0x1703f4bb, 0xdba9f425, 0xd16cfd3c, 0x1dc6fda2, 0x9349fa41, 0x5fe3fadf, 0x86c3e873, 0x4a69e8ed, 0xc4e6ef0e, 0x084cef90, 0x0289e689, 0xce23e617, 0x40ace1f4, 0x8c06e16a, 0xd0eba0bb, 0x1c41a025, 0x92cea7c6, 0x5e64a758, 0x54a1ae41, 0x980baedf, 0x1684a93c, 0xda2ea9a2, 0x030ebb0e, 0xcfa4bb90, 0x412bbc73, 0x8d81bced, 0x8744b5f4, 0x4beeb56a, 0xc561b289, 0x09cbb217, 0xac509190, 0x60fa910e, 0xee7596ed, 0x22df9673, 0x281a9f6a, 0xe4b09ff4, 0x6a3f9817, 0xa6959889, 0x7fb58a25, 0xb31f8abb, 0x3d908d58, 0xf13a8dc6, 0xfbff84df, 0x37558441, 0xb9da83a2, 0x7570833c, 0x533b85da, 0x9f918544, 0x111e82a7, 0xddb48239, 0xd7718b20, 0x1bdb8bbe, 0x95548c5d, 0x59fe8cc3, 0x80de9e6f, 0x4c749ef1, 0xc2fb9912, 0x0e51998c, 0x04949095, 0xc83e900b, 0x46b197e8, 0x8a1b9776, 0x2f80b4f1, 0xe32ab46f, 0x6da5b38c, 0xa10fb312, 0xabcaba0b, 0x6760ba95, 0xe9efbd76, 0x2545bde8, 0xfc65af44, 0x30cfafda, 0xbe40a839, 0x72eaa8a7, 0x782fa1be, 0xb485a120, 0x3a0aa6c3, 0xf6a0a65d, 0xaa4de78c, 0x66e7e712, 0xe868e0f1, 0x24c2e06f, 0x2e07e976, 0xe2ade9e8, 0x6c22ee0b, 0xa088ee95, 0x79a8fc39, 0xb502fca7, 0x3b8dfb44, 0xf727fbda, 0xfde2f2c3, 0x3148f25d, 0xbfc7f5be, 0x736df520, 0xd6f6d6a7, 0x1a5cd639, 0x94d3d1da, 0x5879d144, 0x52bcd85d, 0x9e16d8c3, 0x1099df20, 0xdc33dfbe, 0x0513cd12, 0xc9b9cd8c, 0x4736ca6f, 0x8b9ccaf1, 0x8159c3e8, 0x4df3c376, 0xc37cc495, 0x0fd6c40b, 0x7aa64737, 0xb60c47a9, 0x3883404a, 0xf42940d4, 0xfeec49cd, 0x32464953, 0xbcc94eb0, 0x70634e2e, 0xa9435c82, 0x65e95c1c, 0xeb665bff, 0x27cc5b61, 0x2d095278, 0xe1a352e6, 0x6f2c5505, 0xa386559b, 0x061d761c, 0xcab77682, 0x44387161, 0x889271ff, 0x825778e6, 0x4efd7878, 0xc0727f9b, 0x0cd87f05, 0xd5f86da9, 0x19526d37, 0x97dd6ad4, 0x5b776a4a, 0x51b26353, 0x9d1863cd, 0x1397642e, 0xdf3d64b0, 0x83d02561, 0x4f7a25ff, 0xc1f5221c, 0x0d5f2282, 0x079a2b9b, 0xcb302b05, 0x45bf2ce6, 0x89152c78, 0x50353ed4, 0x9c9f3e4a, 0x121039a9, 0xdeba3937, 0xd47f302e, 0x18d530b0, 0x965a3753, 0x5af037cd, 0xff6b144a, 0x33c114d4, 0xbd4e1337, 0x71e413a9, 0x7b211ab0, 0xb78b1a2e, 0x39041dcd, 0xf5ae1d53, 0x2c8e0fff, 0xe0240f61, 0x6eab0882, 0xa201081c, 0xa8c40105, 0x646e019b, 0xeae10678, 0x264b06e6}, {0x00000000, 0xa6770bb4, 0x979f1129, 0x31e81a9d, 0xf44f2413, 0x52382fa7, 0x63d0353a, 0xc5a73e8e, 0x33ef4e67, 0x959845d3, 0xa4705f4e, 0x020754fa, 0xc7a06a74, 0x61d761c0, 0x503f7b5d, 0xf64870e9, 0x67de9cce, 0xc1a9977a, 0xf0418de7, 0x56368653, 0x9391b8dd, 0x35e6b369, 0x040ea9f4, 0xa279a240, 0x5431d2a9, 0xf246d91d, 0xc3aec380, 0x65d9c834, 0xa07ef6ba, 0x0609fd0e, 0x37e1e793, 0x9196ec27, 0xcfbd399c, 0x69ca3228, 0x582228b5, 0xfe552301, 0x3bf21d8f, 0x9d85163b, 0xac6d0ca6, 0x0a1a0712, 0xfc5277fb, 0x5a257c4f, 0x6bcd66d2, 0xcdba6d66, 0x081d53e8, 0xae6a585c, 0x9f8242c1, 0x39f54975, 0xa863a552, 0x0e14aee6, 0x3ffcb47b, 0x998bbfcf, 0x5c2c8141, 0xfa5b8af5, 0xcbb39068, 0x6dc49bdc, 0x9b8ceb35, 0x3dfbe081, 0x0c13fa1c, 0xaa64f1a8, 0x6fc3cf26, 0xc9b4c492, 0xf85cde0f, 0x5e2bd5bb, 0x440b7579, 0xe27c7ecd, 0xd3946450, 0x75e36fe4, 0xb044516a, 0x16335ade, 0x27db4043, 0x81ac4bf7, 0x77e43b1e, 0xd19330aa, 0xe07b2a37, 0x460c2183, 0x83ab1f0d, 0x25dc14b9, 0x14340e24, 0xb2430590, 0x23d5e9b7, 0x85a2e203, 0xb44af89e, 0x123df32a, 0xd79acda4, 0x71edc610, 0x4005dc8d, 0xe672d739, 0x103aa7d0, 0xb64dac64, 0x87a5b6f9, 0x21d2bd4d, 0xe47583c3, 0x42028877, 0x73ea92ea, 0xd59d995e, 0x8bb64ce5, 0x2dc14751, 0x1c295dcc, 0xba5e5678, 0x7ff968f6, 0xd98e6342, 0xe86679df, 0x4e11726b, 0xb8590282, 0x1e2e0936, 0x2fc613ab, 0x89b1181f, 0x4c162691, 0xea612d25, 0xdb8937b8, 0x7dfe3c0c, 0xec68d02b, 0x4a1fdb9f, 0x7bf7c102, 0xdd80cab6, 0x1827f438, 0xbe50ff8c, 0x8fb8e511, 0x29cfeea5, 0xdf879e4c, 0x79f095f8, 0x48188f65, 0xee6f84d1, 0x2bc8ba5f, 0x8dbfb1eb, 0xbc57ab76, 0x1a20a0c2, 0x8816eaf2, 0x2e61e146, 0x1f89fbdb, 0xb9fef06f, 0x7c59cee1, 0xda2ec555, 0xebc6dfc8, 0x4db1d47c, 0xbbf9a495, 0x1d8eaf21, 0x2c66b5bc, 0x8a11be08, 0x4fb68086, 0xe9c18b32, 0xd82991af, 0x7e5e9a1b, 0xefc8763c, 0x49bf7d88, 0x78576715, 0xde206ca1, 0x1b87522f, 0xbdf0599b, 0x8c184306, 0x2a6f48b2, 0xdc27385b, 0x7a5033ef, 0x4bb82972, 0xedcf22c6, 0x28681c48, 0x8e1f17fc, 0xbff70d61, 0x198006d5, 0x47abd36e, 0xe1dcd8da, 0xd034c247, 0x7643c9f3, 0xb3e4f77d, 0x1593fcc9, 0x247be654, 0x820cede0, 0x74449d09, 0xd23396bd, 0xe3db8c20, 0x45ac8794, 0x800bb91a, 0x267cb2ae, 0x1794a833, 0xb1e3a387, 0x20754fa0, 0x86024414, 0xb7ea5e89, 0x119d553d, 0xd43a6bb3, 0x724d6007, 0x43a57a9a, 0xe5d2712e, 0x139a01c7, 0xb5ed0a73, 0x840510ee, 0x22721b5a, 0xe7d525d4, 0x41a22e60, 0x704a34fd, 0xd63d3f49, 0xcc1d9f8b, 0x6a6a943f, 0x5b828ea2, 0xfdf58516, 0x3852bb98, 0x9e25b02c, 0xafcdaab1, 0x09baa105, 0xfff2d1ec, 0x5985da58, 0x686dc0c5, 0xce1acb71, 0x0bbdf5ff, 0xadcafe4b, 0x9c22e4d6, 0x3a55ef62, 0xabc30345, 0x0db408f1, 0x3c5c126c, 0x9a2b19d8, 0x5f8c2756, 0xf9fb2ce2, 0xc813367f, 0x6e643dcb, 0x982c4d22, 0x3e5b4696, 0x0fb35c0b, 0xa9c457bf, 0x6c636931, 0xca146285, 0xfbfc7818, 0x5d8b73ac, 0x03a0a617, 0xa5d7ada3, 0x943fb73e, 0x3248bc8a, 0xf7ef8204, 0x519889b0, 0x6070932d, 0xc6079899, 0x304fe870, 0x9638e3c4, 0xa7d0f959, 0x01a7f2ed, 0xc400cc63, 0x6277c7d7, 0x539fdd4a, 0xf5e8d6fe, 0x647e3ad9, 0xc209316d, 0xf3e12bf0, 0x55962044, 0x90311eca, 0x3646157e, 0x07ae0fe3, 0xa1d90457, 0x579174be, 0xf1e67f0a, 0xc00e6597, 0x66796e23, 0xa3de50ad, 0x05a95b19, 0x34414184, 0x92364a30}, {0x00000000, 0xcb5cd3a5, 0x4dc8a10b, 0x869472ae, 0x9b914216, 0x50cd91b3, 0xd659e31d, 0x1d0530b8, 0xec53826d, 0x270f51c8, 0xa19b2366, 0x6ac7f0c3, 0x77c2c07b, 0xbc9e13de, 0x3a0a6170, 0xf156b2d5, 0x03d6029b, 0xc88ad13e, 0x4e1ea390, 0x85427035, 0x9847408d, 0x531b9328, 0xd58fe186, 0x1ed33223, 0xef8580f6, 0x24d95353, 0xa24d21fd, 0x6911f258, 0x7414c2e0, 0xbf481145, 0x39dc63eb, 0xf280b04e, 0x07ac0536, 0xccf0d693, 0x4a64a43d, 0x81387798, 0x9c3d4720, 0x57619485, 0xd1f5e62b, 0x1aa9358e, 0xebff875b, 0x20a354fe, 0xa6372650, 0x6d6bf5f5, 0x706ec54d, 0xbb3216e8, 0x3da66446, 0xf6fab7e3, 0x047a07ad, 0xcf26d408, 0x49b2a6a6, 0x82ee7503, 0x9feb45bb, 0x54b7961e, 0xd223e4b0, 0x197f3715, 0xe82985c0, 0x23755665, 0xa5e124cb, 0x6ebdf76e, 0x73b8c7d6, 0xb8e41473, 0x3e7066dd, 0xf52cb578, 0x0f580a6c, 0xc404d9c9, 0x4290ab67, 0x89cc78c2, 0x94c9487a, 0x5f959bdf, 0xd901e971, 0x125d3ad4, 0xe30b8801, 0x28575ba4, 0xaec3290a, 0x659ffaaf, 0x789aca17, 0xb3c619b2, 0x35526b1c, 0xfe0eb8b9, 0x0c8e08f7, 0xc7d2db52, 0x4146a9fc, 0x8a1a7a59, 0x971f4ae1, 0x5c439944, 0xdad7ebea, 0x118b384f, 0xe0dd8a9a, 0x2b81593f, 0xad152b91, 0x6649f834, 0x7b4cc88c, 0xb0101b29, 0x36846987, 0xfdd8ba22, 0x08f40f5a, 0xc3a8dcff, 0x453cae51, 0x8e607df4, 0x93654d4c, 0x58399ee9, 0xdeadec47, 0x15f13fe2, 0xe4a78d37, 0x2ffb5e92, 0xa96f2c3c, 0x6233ff99, 0x7f36cf21, 0xb46a1c84, 0x32fe6e2a, 0xf9a2bd8f, 0x0b220dc1, 0xc07ede64, 0x46eaacca, 0x8db67f6f, 0x90b34fd7, 0x5bef9c72, 0xdd7beedc, 0x16273d79, 0xe7718fac, 0x2c2d5c09, 0xaab92ea7, 0x61e5fd02, 0x7ce0cdba, 0xb7bc1e1f, 0x31286cb1, 0xfa74bf14, 0x1eb014d8, 0xd5ecc77d, 0x5378b5d3, 0x98246676, 0x852156ce, 0x4e7d856b, 0xc8e9f7c5, 0x03b52460, 0xf2e396b5, 0x39bf4510, 0xbf2b37be, 0x7477e41b, 0x6972d4a3, 0xa22e0706, 0x24ba75a8, 0xefe6a60d, 0x1d661643, 0xd63ac5e6, 0x50aeb748, 0x9bf264ed, 0x86f75455, 0x4dab87f0, 0xcb3ff55e, 0x006326fb, 0xf135942e, 0x3a69478b, 0xbcfd3525, 0x77a1e680, 0x6aa4d638, 0xa1f8059d, 0x276c7733, 0xec30a496, 0x191c11ee, 0xd240c24b, 0x54d4b0e5, 0x9f886340, 0x828d53f8, 0x49d1805d, 0xcf45f2f3, 0x04192156, 0xf54f9383, 0x3e134026, 0xb8873288, 0x73dbe12d, 0x6eded195, 0xa5820230, 0x2316709e, 0xe84aa33b, 0x1aca1375, 0xd196c0d0, 0x5702b27e, 0x9c5e61db, 0x815b5163, 0x4a0782c6, 0xcc93f068, 0x07cf23cd, 0xf6999118, 0x3dc542bd, 0xbb513013, 0x700de3b6, 0x6d08d30e, 0xa65400ab, 0x20c07205, 0xeb9ca1a0, 0x11e81eb4, 0xdab4cd11, 0x5c20bfbf, 0x977c6c1a, 0x8a795ca2, 0x41258f07, 0xc7b1fda9, 0x0ced2e0c, 0xfdbb9cd9, 0x36e74f7c, 0xb0733dd2, 0x7b2fee77, 0x662adecf, 0xad760d6a, 0x2be27fc4, 0xe0beac61, 0x123e1c2f, 0xd962cf8a, 0x5ff6bd24, 0x94aa6e81, 0x89af5e39, 0x42f38d9c, 0xc467ff32, 0x0f3b2c97, 0xfe6d9e42, 0x35314de7, 0xb3a53f49, 0x78f9ecec, 0x65fcdc54, 0xaea00ff1, 0x28347d5f, 0xe368aefa, 0x16441b82, 0xdd18c827, 0x5b8cba89, 0x90d0692c, 0x8dd55994, 0x46898a31, 0xc01df89f, 0x0b412b3a, 0xfa1799ef, 0x314b4a4a, 0xb7df38e4, 0x7c83eb41, 0x6186dbf9, 0xaada085c, 0x2c4e7af2, 0xe712a957, 0x15921919, 0xdececabc, 0x585ab812, 0x93066bb7, 0x8e035b0f, 0x455f88aa, 0xc3cbfa04, 0x089729a1, 0xf9c19b74, 0x329d48d1, 0xb4093a7f, 0x7f55e9da, 0x6250d962, 0xa90c0ac7, 0x2f987869, 0xe4c4abcc}, {0x00000000, 0x3d6029b0, 0x7ac05360, 0x47a07ad0, 0xf580a6c0, 0xc8e08f70, 0x8f40f5a0, 0xb220dc10, 0x30704bc1, 0x0d106271, 0x4ab018a1, 0x77d03111, 0xc5f0ed01, 0xf890c4b1, 0xbf30be61, 0x825097d1, 0x60e09782, 0x5d80be32, 0x1a20c4e2, 0x2740ed52, 0x95603142, 0xa80018f2, 0xefa06222, 0xd2c04b92, 0x5090dc43, 0x6df0f5f3, 0x2a508f23, 0x1730a693, 0xa5107a83, 0x98705333, 0xdfd029e3, 0xe2b00053, 0xc1c12f04, 0xfca106b4, 0xbb017c64, 0x866155d4, 0x344189c4, 0x0921a074, 0x4e81daa4, 0x73e1f314, 0xf1b164c5, 0xccd14d75, 0x8b7137a5, 0xb6111e15, 0x0431c205, 0x3951ebb5, 0x7ef19165, 0x4391b8d5, 0xa121b886, 0x9c419136, 0xdbe1ebe6, 0xe681c256, 0x54a11e46, 0x69c137f6, 0x2e614d26, 0x13016496, 0x9151f347, 0xac31daf7, 0xeb91a027, 0xd6f18997, 0x64d15587, 0x59b17c37, 0x1e1106e7, 0x23712f57, 0x58f35849, 0x659371f9, 0x22330b29, 0x1f532299, 0xad73fe89, 0x9013d739, 0xd7b3ade9, 0xead38459, 0x68831388, 0x55e33a38, 0x124340e8, 0x2f236958, 0x9d03b548, 0xa0639cf8, 0xe7c3e628, 0xdaa3cf98, 0x3813cfcb, 0x0573e67b, 0x42d39cab, 0x7fb3b51b, 0xcd93690b, 0xf0f340bb, 0xb7533a6b, 0x8a3313db, 0x0863840a, 0x3503adba, 0x72a3d76a, 0x4fc3feda, 0xfde322ca, 0xc0830b7a, 0x872371aa, 0xba43581a, 0x9932774d, 0xa4525efd, 0xe3f2242d, 0xde920d9d, 0x6cb2d18d, 0x51d2f83d, 0x167282ed, 0x2b12ab5d, 0xa9423c8c, 0x9422153c, 0xd3826fec, 0xeee2465c, 0x5cc29a4c, 0x61a2b3fc, 0x2602c92c, 0x1b62e09c, 0xf9d2e0cf, 0xc4b2c97f, 0x8312b3af, 0xbe729a1f, 0x0c52460f, 0x31326fbf, 0x7692156f, 0x4bf23cdf, 0xc9a2ab0e, 0xf4c282be, 0xb362f86e, 0x8e02d1de, 0x3c220dce, 0x0142247e, 0x46e25eae, 0x7b82771e, 0xb1e6b092, 0x8c869922, 0xcb26e3f2, 0xf646ca42, 0x44661652, 0x79063fe2, 0x3ea64532, 0x03c66c82, 0x8196fb53, 0xbcf6d2e3, 0xfb56a833, 0xc6368183, 0x74165d93, 0x49767423, 0x0ed60ef3, 0x33b62743, 0xd1062710, 0xec660ea0, 0xabc67470, 0x96a65dc0, 0x248681d0, 0x19e6a860, 0x5e46d2b0, 0x6326fb00, 0xe1766cd1, 0xdc164561, 0x9bb63fb1, 0xa6d61601, 0x14f6ca11, 0x2996e3a1, 0x6e369971, 0x5356b0c1, 0x70279f96, 0x4d47b626, 0x0ae7ccf6, 0x3787e546, 0x85a73956, 0xb8c710e6, 0xff676a36, 0xc2074386, 0x4057d457, 0x7d37fde7, 0x3a978737, 0x07f7ae87, 0xb5d77297, 0x88b75b27, 0xcf1721f7, 0xf2770847, 0x10c70814, 0x2da721a4, 0x6a075b74, 0x576772c4, 0xe547aed4, 0xd8278764, 0x9f87fdb4, 0xa2e7d404, 0x20b743d5, 0x1dd76a65, 0x5a7710b5, 0x67173905, 0xd537e515, 0xe857cca5, 0xaff7b675, 0x92979fc5, 0xe915e8db, 0xd475c16b, 0x93d5bbbb, 0xaeb5920b, 0x1c954e1b, 0x21f567ab, 0x66551d7b, 0x5b3534cb, 0xd965a31a, 0xe4058aaa, 0xa3a5f07a, 0x9ec5d9ca, 0x2ce505da, 0x11852c6a, 0x562556ba, 0x6b457f0a, 0x89f57f59, 0xb49556e9, 0xf3352c39, 0xce550589, 0x7c75d999, 0x4115f029, 0x06b58af9, 0x3bd5a349, 0xb9853498, 0x84e51d28, 0xc34567f8, 0xfe254e48, 0x4c059258, 0x7165bbe8, 0x36c5c138, 0x0ba5e888, 0x28d4c7df, 0x15b4ee6f, 0x521494bf, 0x6f74bd0f, 0xdd54611f, 0xe03448af, 0xa794327f, 0x9af41bcf, 0x18a48c1e, 0x25c4a5ae, 0x6264df7e, 0x5f04f6ce, 0xed242ade, 0xd044036e, 0x97e479be, 0xaa84500e, 0x4834505d, 0x755479ed, 0x32f4033d, 0x0f942a8d, 0xbdb4f69d, 0x80d4df2d, 0xc774a5fd, 0xfa148c4d, 0x78441b9c, 0x4524322c, 0x028448fc, 0x3fe4614c, 0x8dc4bd5c, 0xb0a494ec, 0xf704ee3c, 0xca64c78c}, {0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, 0x8f629757, 0x37def032, 0x256b5fdc, 0x9dd738b9, 0xc5b428ef, 0x7d084f8a, 0x6fbde064, 0xd7018701, 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, 0x58631056, 0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26, 0x95ad7f70, 0x2d111815, 0x3fa4b7fb, 0x8718d09e, 0x1acfe827, 0xa2738f42, 0xb0c620ac, 0x087a47c9, 0xa032af3e, 0x188ec85b, 0x0a3b67b5, 0xb28700d0, 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787, 0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, 0xeae41086, 0x525877e3, 0x40edd80d, 0xf851bf68, 0xf02bf8a1, 0x48979fc4, 0x5a22302a, 0xe29e574f, 0x7f496ff6, 0xc7f50893, 0xd540a77d, 0x6dfcc018, 0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7, 0x9b14583d, 0x23a83f58, 0x311d90b6, 0x89a1f7d3, 0x1476cf6a, 0xaccaa80f, 0xbe7f07e1, 0x06c36084, 0x5ea070d2, 0xe61c17b7, 0xf4a9b859, 0x4c15df3c, 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b, 0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, 0x446f98f5, 0xfcd3ff90, 0xee66507e, 0x56da371b, 0x0eb9274d, 0xb6054028, 0xa4b0efc6, 0x1c0c88a3, 0x81dbb01a, 0x3967d77f, 0x2bd27891, 0x936e1ff4, 0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba, 0xfe92dfec, 0x462eb889, 0x549b1767, 0xec277002, 0x71f048bb, 0xc94c2fde, 0xdbf98030, 0x6345e755, 0x6b3fa09c, 0xd383c7f9, 0xc1366817, 0x798a0f72, 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825, 0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, 0x21e91f24, 0x99557841, 0x8be0d7af, 0x335cb0ca, 0xed59b63b, 0x55e5d15e, 0x47507eb0, 0xffec19d5, 0x623b216c, 0xda874609, 0xc832e9e7, 0x708e8e82, 0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d, 0xbd40e1a4, 0x05fc86c1, 0x1749292f, 0xaff54e4a, 0x322276f3, 0x8a9e1196, 0x982bbe78, 0x2097d91d, 0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, 0x6a4166a5, 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2, 0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, 0xc2098e52, 0x7ab5e937, 0x680046d9, 0xd0bc21bc, 0x88df31ea, 0x3063568f, 0x22d6f961, 0x9a6a9e04, 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, 0x15080953, 0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623, 0xd8c66675, 0x607a0110, 0x72cfaefe, 0xca73c99b, 0x57a4f122, 0xef189647, 0xfdad39a9, 0x45115ecc, 0x764dee06, 0xcef18963, 0xdc44268d, 0x64f841e8, 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf, 0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, 0x3c9b51be, 0x842736db, 0x96929935, 0x2e2efe50, 0x2654b999, 0x9ee8defc, 0x8c5d7112, 0x34e11677, 0xa9362ece, 0x118a49ab, 0x033fe645, 0xbb838120, 0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf, 0xd67f4138, 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, 0x591dd66f, 0xe1a1b10a, 0xf3141ee4, 0x4ba87981, 0x13cb69d7, 0xab770eb2, 0xb9c2a15c, 0x017ec639, 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e, 0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, 0x090481f0, 0xb1b8e695, 0xa30d497b, 0x1bb12e1e, 0x43d23e48, 0xfb6e592d, 0xe9dbf6c3, 0x516791a6, 0xccb0a91f, 0x740cce7a, 0x66b96194, 0xde0506f1}, {0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, 0x0709a8dc, 0x06cbc2eb, 0x048d7cb2, 0x054f1685, 0x0e1351b8, 0x0fd13b8f, 0x0d9785d6, 0x0c55efe1, 0x091af964, 0x08d89353, 0x0a9e2d0a, 0x0b5c473d, 0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5, 0x1235f2c8, 0x13f798ff, 0x11b126a6, 0x10734c91, 0x153c5a14, 0x14fe3023, 0x16b88e7a, 0x177ae44d, 0x384d46e0, 0x398f2cd7, 0x3bc9928e, 0x3a0bf8b9, 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065, 0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, 0x3157bf84, 0x3095d5b3, 0x32d36bea, 0x331101dd, 0x246be590, 0x25a98fa7, 0x27ef31fe, 0x262d5bc9, 0x23624d4c, 0x22a0277b, 0x20e69922, 0x2124f315, 0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad, 0x709a8dc0, 0x7158e7f7, 0x731e59ae, 0x72dc3399, 0x7793251c, 0x76514f2b, 0x7417f172, 0x75d59b45, 0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, 0x7ccf6221, 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd, 0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, 0x6bb5866c, 0x6a77ec5b, 0x68315202, 0x69f33835, 0x62af7f08, 0x636d153f, 0x612bab66, 0x60e9c151, 0x65a6d7d4, 0x6464bde3, 0x662203ba, 0x67e0698d, 0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5, 0x46c49a98, 0x4706f0af, 0x45404ef6, 0x448224c1, 0x41cd3244, 0x400f5873, 0x4249e62a, 0x438b8c1d, 0x54f16850, 0x55330267, 0x5775bc3e, 0x56b7d609, 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5, 0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, 0x5deb9134, 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d, 0xe1351b80, 0xe0f771b7, 0xe2b1cfee, 0xe373a5d9, 0xe63cb35c, 0xe7fed96b, 0xe5b86732, 0xe47a0d05, 0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd, 0xfd13b8f0, 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, 0xfa1a102c, 0xfbd87a1b, 0xf99ec442, 0xf85cae75, 0xf300e948, 0xf2c2837f, 0xf0843d26, 0xf1465711, 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd, 0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, 0xde71f5bc, 0xdfb39f8b, 0xddf521d2, 0xdc374be5, 0xd76b0cd8, 0xd6a966ef, 0xd4efd8b6, 0xd52db281, 0xd062a404, 0xd1a0ce33, 0xd3e6706a, 0xd2241a5d, 0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895, 0xcb4dafa8, 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, 0xcc440774, 0xcd866d43, 0xcfc0d31a, 0xce02b92d, 0x91af9640, 0x906dfc77, 0x922b422e, 0x93e92819, 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5, 0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, 0x98b56f24, 0x99770513, 0x9b31bb4a, 0x9af3d17d, 0x8d893530, 0x8c4b5f07, 0x8e0de15e, 0x8fcf8b69, 0x8a809dec, 0x8b42f7db, 0x89044982, 0x88c623b5, 0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d, 0xa9e2d0a0, 0xa820ba97, 0xaa6604ce, 0xaba46ef9, 0xaeeb787c, 0xaf29124b, 0xad6fac12, 0xacadc625, 0xa7f18118, 0xa633eb2f, 0xa4755576, 0xa5b73f41, 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d, 0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, 0xb2cddb0c, 0xb30fb13b, 0xb1490f62, 0xb08b6555, 0xbbd72268, 0xba15485f, 0xb853f606, 0xb9919c31, 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, 0xbe9834ed}, {0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, 0x646cc504, 0x7d77f445, 0x565aa786, 0x4f4196c7, 0xc8d98a08, 0xd1c2bb49, 0xfaefe88a, 0xe3f4d9cb, 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, 0x87981ccf, 0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496, 0x821b9859, 0x9b00a918, 0xb02dfadb, 0xa936cb9a, 0xe6775d5d, 0xff6c6c1c, 0xd4413fdf, 0xcd5a0e9e, 0x958424a2, 0x8c9f15e3, 0xa7b24620, 0xbea97761, 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265, 0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, 0x39316bae, 0x202a5aef, 0x0b07092c, 0x121c386d, 0xdf4636f3, 0xc65d07b2, 0xed705471, 0xf46b6530, 0xbb2af3f7, 0xa231c2b6, 0x891c9175, 0x9007a034, 0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c, 0xf0794f05, 0xe9627e44, 0xc24f2d87, 0xdb541cc6, 0x94158a01, 0x8d0ebb40, 0xa623e883, 0xbf38d9c2, 0x38a0c50d, 0x21bbf44c, 0x0a96a78f, 0x138d96ce, 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca, 0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, 0xded79850, 0xc7cca911, 0xece1fad2, 0xf5facb93, 0x7262d75c, 0x6b79e61d, 0x4054b5de, 0x594f849f, 0x160e1258, 0x0f152319, 0x243870da, 0x3d23419b, 0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60, 0xad24e1af, 0xb43fd0ee, 0x9f12832d, 0x8609b26c, 0xc94824ab, 0xd05315ea, 0xfb7e4629, 0xe2657768, 0x2f3f79f6, 0x362448b7, 0x1d091b74, 0x04122a35, 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31, 0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, 0x838a36fa, 0x9a9107bb, 0xb1bc5478, 0xa8a76539, 0x3b83984b, 0x2298a90a, 0x09b5fac9, 0x10aecb88, 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, 0x74c20e8c, 0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484, 0x71418a1a, 0x685abb5b, 0x4377e898, 0x5a6cd9d9, 0x152d4f1e, 0x0c367e5f, 0x271b2d9c, 0x3e001cdd, 0xb9980012, 0xa0833153, 0x8bae6290, 0x92b553d1, 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5, 0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, 0xca6b79ed, 0xd37048ac, 0xf85d1b6f, 0xe1462a2e, 0x66de36e1, 0x7fc507a0, 0x54e85463, 0x4df36522, 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, 0x299fa026, 0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f, 0x2c1c24b0, 0x350715f1, 0x1e2a4632, 0x07317773, 0x4870e1b4, 0x516bd0f5, 0x7a468336, 0x635db277, 0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, 0xe0d7848d, 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189, 0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, 0x674f9842, 0x7e54a903, 0x5579fac0, 0x4c62cb81, 0x8138c51f, 0x9823f45e, 0xb30ea79d, 0xaa1596dc, 0xe554001b, 0xfc4f315a, 0xd7626299, 0xce7953d8, 0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0, 0x5e7ef3ec, 0x4765c2ad, 0x6c48916e, 0x7553a02f, 0x3a1236e8, 0x230907a9, 0x0824546a, 0x113f652b, 0x96a779e4, 0x8fbc48a5, 0xa4911b66, 0xbd8a2a27, 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23, 0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, 0x70d024b9, 0x69cb15f8, 0x42e6463b, 0x5bfd777a, 0xdc656bb5, 0xc57e5af4, 0xee530937, 0xf7483876, 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, 0x9324fd72}, {0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x0000000000000000, 0x9630077700000000, 0x2c610eee00000000, 0xba51099900000000, 0x19c46d0700000000, 0x8ff46a7000000000, 0x35a563e900000000, 0xa395649e00000000, 0x3288db0e00000000, 0xa4b8dc7900000000, 0x1ee9d5e000000000, 0x88d9d29700000000, 0x2b4cb60900000000, 0xbd7cb17e00000000, 0x072db8e700000000, 0x911dbf9000000000, 0x6410b71d00000000, 0xf220b06a00000000, 0x4871b9f300000000, 0xde41be8400000000, 0x7dd4da1a00000000, 0xebe4dd6d00000000, 0x51b5d4f400000000, 0xc785d38300000000, 0x56986c1300000000, 0xc0a86b6400000000, 0x7af962fd00000000, 0xecc9658a00000000, 0x4f5c011400000000, 0xd96c066300000000, 0x633d0ffa00000000, 0xf50d088d00000000, 0xc8206e3b00000000, 0x5e10694c00000000, 0xe44160d500000000, 0x727167a200000000, 0xd1e4033c00000000, 0x47d4044b00000000, 0xfd850dd200000000, 0x6bb50aa500000000, 0xfaa8b53500000000, 0x6c98b24200000000, 0xd6c9bbdb00000000, 0x40f9bcac00000000, 0xe36cd83200000000, 0x755cdf4500000000, 0xcf0dd6dc00000000, 0x593dd1ab00000000, 0xac30d92600000000, 0x3a00de5100000000, 0x8051d7c800000000, 0x1661d0bf00000000, 0xb5f4b42100000000, 0x23c4b35600000000, 0x9995bacf00000000, 0x0fa5bdb800000000, 0x9eb8022800000000, 0x0888055f00000000, 0xb2d90cc600000000, 0x24e90bb100000000, 0x877c6f2f00000000, 0x114c685800000000, 0xab1d61c100000000, 0x3d2d66b600000000, 0x9041dc7600000000, 0x0671db0100000000, 0xbc20d29800000000, 0x2a10d5ef00000000, 0x8985b17100000000, 0x1fb5b60600000000, 0xa5e4bf9f00000000, 0x33d4b8e800000000, 0xa2c9077800000000, 0x34f9000f00000000, 0x8ea8099600000000, 0x18980ee100000000, 0xbb0d6a7f00000000, 0x2d3d6d0800000000, 0x976c649100000000, 0x015c63e600000000, 0xf4516b6b00000000, 0x62616c1c00000000, 0xd830658500000000, 0x4e0062f200000000, 0xed95066c00000000, 0x7ba5011b00000000, 0xc1f4088200000000, 0x57c40ff500000000, 0xc6d9b06500000000, 0x50e9b71200000000, 0xeab8be8b00000000, 0x7c88b9fc00000000, 0xdf1ddd6200000000, 0x492dda1500000000, 0xf37cd38c00000000, 0x654cd4fb00000000, 0x5861b24d00000000, 0xce51b53a00000000, 0x7400bca300000000, 0xe230bbd400000000, 0x41a5df4a00000000, 0xd795d83d00000000, 0x6dc4d1a400000000, 0xfbf4d6d300000000, 0x6ae9694300000000, 0xfcd96e3400000000, 0x468867ad00000000, 0xd0b860da00000000, 0x732d044400000000, 0xe51d033300000000, 0x5f4c0aaa00000000, 0xc97c0ddd00000000, 0x3c71055000000000, 0xaa41022700000000, 0x10100bbe00000000, 0x86200cc900000000, 0x25b5685700000000, 0xb3856f2000000000, 0x09d466b900000000, 0x9fe461ce00000000, 0x0ef9de5e00000000, 0x98c9d92900000000, 0x2298d0b000000000, 0xb4a8d7c700000000, 0x173db35900000000, 0x810db42e00000000, 0x3b5cbdb700000000, 0xad6cbac000000000, 0x2083b8ed00000000, 0xb6b3bf9a00000000, 0x0ce2b60300000000, 0x9ad2b17400000000, 0x3947d5ea00000000, 0xaf77d29d00000000, 0x1526db0400000000, 0x8316dc7300000000, 0x120b63e300000000, 0x843b649400000000, 0x3e6a6d0d00000000, 0xa85a6a7a00000000, 0x0bcf0ee400000000, 0x9dff099300000000, 0x27ae000a00000000, 0xb19e077d00000000, 0x44930ff000000000, 0xd2a3088700000000, 0x68f2011e00000000, 0xfec2066900000000, 0x5d5762f700000000, 0xcb67658000000000, 0x71366c1900000000, 0xe7066b6e00000000, 0x761bd4fe00000000, 0xe02bd38900000000, 0x5a7ada1000000000, 0xcc4add6700000000, 0x6fdfb9f900000000, 0xf9efbe8e00000000, 0x43beb71700000000, 0xd58eb06000000000, 0xe8a3d6d600000000, 0x7e93d1a100000000, 0xc4c2d83800000000, 0x52f2df4f00000000, 0xf167bbd100000000, 0x6757bca600000000, 0xdd06b53f00000000, 0x4b36b24800000000, 0xda2b0dd800000000, 0x4c1b0aaf00000000, 0xf64a033600000000, 0x607a044100000000, 0xc3ef60df00000000, 0x55df67a800000000, 0xef8e6e3100000000, 0x79be694600000000, 0x8cb361cb00000000, 0x1a8366bc00000000, 0xa0d26f2500000000, 0x36e2685200000000, 0x95770ccc00000000, 0x03470bbb00000000, 0xb916022200000000, 0x2f26055500000000, 0xbe3bbac500000000, 0x280bbdb200000000, 0x925ab42b00000000, 0x046ab35c00000000, 0xa7ffd7c200000000, 0x31cfd0b500000000, 0x8b9ed92c00000000, 0x1daede5b00000000, 0xb0c2649b00000000, 0x26f263ec00000000, 0x9ca36a7500000000, 0x0a936d0200000000, 0xa906099c00000000, 0x3f360eeb00000000, 0x8567077200000000, 0x1357000500000000, 0x824abf9500000000, 0x147ab8e200000000, 0xae2bb17b00000000, 0x381bb60c00000000, 0x9b8ed29200000000, 0x0dbed5e500000000, 0xb7efdc7c00000000, 0x21dfdb0b00000000, 0xd4d2d38600000000, 0x42e2d4f100000000, 0xf8b3dd6800000000, 0x6e83da1f00000000, 0xcd16be8100000000, 0x5b26b9f600000000, 0xe177b06f00000000, 0x7747b71800000000, 0xe65a088800000000, 0x706a0fff00000000, 0xca3b066600000000, 0x5c0b011100000000, 0xff9e658f00000000, 0x69ae62f800000000, 0xd3ff6b6100000000, 0x45cf6c1600000000, 0x78e20aa000000000, 0xeed20dd700000000, 0x5483044e00000000, 0xc2b3033900000000, 0x612667a700000000, 0xf71660d000000000, 0x4d47694900000000, 0xdb776e3e00000000, 0x4a6ad1ae00000000, 0xdc5ad6d900000000, 0x660bdf4000000000, 0xf03bd83700000000, 0x53aebca900000000, 0xc59ebbde00000000, 0x7fcfb24700000000, 0xe9ffb53000000000, 0x1cf2bdbd00000000, 0x8ac2baca00000000, 0x3093b35300000000, 0xa6a3b42400000000, 0x0536d0ba00000000, 0x9306d7cd00000000, 0x2957de5400000000, 0xbf67d92300000000, 0x2e7a66b300000000, 0xb84a61c400000000, 0x021b685d00000000, 0x942b6f2a00000000, 0x37be0bb400000000, 0xa18e0cc300000000, 0x1bdf055a00000000, 0x8def022d00000000}, {0x0000000000000000, 0x41311b1900000000, 0x8262363200000000, 0xc3532d2b00000000, 0x04c56c6400000000, 0x45f4777d00000000, 0x86a75a5600000000, 0xc796414f00000000, 0x088ad9c800000000, 0x49bbc2d100000000, 0x8ae8effa00000000, 0xcbd9f4e300000000, 0x0c4fb5ac00000000, 0x4d7eaeb500000000, 0x8e2d839e00000000, 0xcf1c988700000000, 0x5112c24a00000000, 0x1023d95300000000, 0xd370f47800000000, 0x9241ef6100000000, 0x55d7ae2e00000000, 0x14e6b53700000000, 0xd7b5981c00000000, 0x9684830500000000, 0x59981b8200000000, 0x18a9009b00000000, 0xdbfa2db000000000, 0x9acb36a900000000, 0x5d5d77e600000000, 0x1c6c6cff00000000, 0xdf3f41d400000000, 0x9e0e5acd00000000, 0xa224849500000000, 0xe3159f8c00000000, 0x2046b2a700000000, 0x6177a9be00000000, 0xa6e1e8f100000000, 0xe7d0f3e800000000, 0x2483dec300000000, 0x65b2c5da00000000, 0xaaae5d5d00000000, 0xeb9f464400000000, 0x28cc6b6f00000000, 0x69fd707600000000, 0xae6b313900000000, 0xef5a2a2000000000, 0x2c09070b00000000, 0x6d381c1200000000, 0xf33646df00000000, 0xb2075dc600000000, 0x715470ed00000000, 0x30656bf400000000, 0xf7f32abb00000000, 0xb6c231a200000000, 0x75911c8900000000, 0x34a0079000000000, 0xfbbc9f1700000000, 0xba8d840e00000000, 0x79dea92500000000, 0x38efb23c00000000, 0xff79f37300000000, 0xbe48e86a00000000, 0x7d1bc54100000000, 0x3c2ade5800000000, 0x054f79f000000000, 0x447e62e900000000, 0x872d4fc200000000, 0xc61c54db00000000, 0x018a159400000000, 0x40bb0e8d00000000, 0x83e823a600000000, 0xc2d938bf00000000, 0x0dc5a03800000000, 0x4cf4bb2100000000, 0x8fa7960a00000000, 0xce968d1300000000, 0x0900cc5c00000000, 0x4831d74500000000, 0x8b62fa6e00000000, 0xca53e17700000000, 0x545dbbba00000000, 0x156ca0a300000000, 0xd63f8d8800000000, 0x970e969100000000, 0x5098d7de00000000, 0x11a9ccc700000000, 0xd2fae1ec00000000, 0x93cbfaf500000000, 0x5cd7627200000000, 0x1de6796b00000000, 0xdeb5544000000000, 0x9f844f5900000000, 0x58120e1600000000, 0x1923150f00000000, 0xda70382400000000, 0x9b41233d00000000, 0xa76bfd6500000000, 0xe65ae67c00000000, 0x2509cb5700000000, 0x6438d04e00000000, 0xa3ae910100000000, 0xe29f8a1800000000, 0x21cca73300000000, 0x60fdbc2a00000000, 0xafe124ad00000000, 0xeed03fb400000000, 0x2d83129f00000000, 0x6cb2098600000000, 0xab2448c900000000, 0xea1553d000000000, 0x29467efb00000000, 0x687765e200000000, 0xf6793f2f00000000, 0xb748243600000000, 0x741b091d00000000, 0x352a120400000000, 0xf2bc534b00000000, 0xb38d485200000000, 0x70de657900000000, 0x31ef7e6000000000, 0xfef3e6e700000000, 0xbfc2fdfe00000000, 0x7c91d0d500000000, 0x3da0cbcc00000000, 0xfa368a8300000000, 0xbb07919a00000000, 0x7854bcb100000000, 0x3965a7a800000000, 0x4b98833b00000000, 0x0aa9982200000000, 0xc9fab50900000000, 0x88cbae1000000000, 0x4f5def5f00000000, 0x0e6cf44600000000, 0xcd3fd96d00000000, 0x8c0ec27400000000, 0x43125af300000000, 0x022341ea00000000, 0xc1706cc100000000, 0x804177d800000000, 0x47d7369700000000, 0x06e62d8e00000000, 0xc5b500a500000000, 0x84841bbc00000000, 0x1a8a417100000000, 0x5bbb5a6800000000, 0x98e8774300000000, 0xd9d96c5a00000000, 0x1e4f2d1500000000, 0x5f7e360c00000000, 0x9c2d1b2700000000, 0xdd1c003e00000000, 0x120098b900000000, 0x533183a000000000, 0x9062ae8b00000000, 0xd153b59200000000, 0x16c5f4dd00000000, 0x57f4efc400000000, 0x94a7c2ef00000000, 0xd596d9f600000000, 0xe9bc07ae00000000, 0xa88d1cb700000000, 0x6bde319c00000000, 0x2aef2a8500000000, 0xed796bca00000000, 0xac4870d300000000, 0x6f1b5df800000000, 0x2e2a46e100000000, 0xe136de6600000000, 0xa007c57f00000000, 0x6354e85400000000, 0x2265f34d00000000, 0xe5f3b20200000000, 0xa4c2a91b00000000, 0x6791843000000000, 0x26a09f2900000000, 0xb8aec5e400000000, 0xf99fdefd00000000, 0x3accf3d600000000, 0x7bfde8cf00000000, 0xbc6ba98000000000, 0xfd5ab29900000000, 0x3e099fb200000000, 0x7f3884ab00000000, 0xb0241c2c00000000, 0xf115073500000000, 0x32462a1e00000000, 0x7377310700000000, 0xb4e1704800000000, 0xf5d06b5100000000, 0x3683467a00000000, 0x77b25d6300000000, 0x4ed7facb00000000, 0x0fe6e1d200000000, 0xccb5ccf900000000, 0x8d84d7e000000000, 0x4a1296af00000000, 0x0b238db600000000, 0xc870a09d00000000, 0x8941bb8400000000, 0x465d230300000000, 0x076c381a00000000, 0xc43f153100000000, 0x850e0e2800000000, 0x42984f6700000000, 0x03a9547e00000000, 0xc0fa795500000000, 0x81cb624c00000000, 0x1fc5388100000000, 0x5ef4239800000000, 0x9da70eb300000000, 0xdc9615aa00000000, 0x1b0054e500000000, 0x5a314ffc00000000, 0x996262d700000000, 0xd85379ce00000000, 0x174fe14900000000, 0x567efa5000000000, 0x952dd77b00000000, 0xd41ccc6200000000, 0x138a8d2d00000000, 0x52bb963400000000, 0x91e8bb1f00000000, 0xd0d9a00600000000, 0xecf37e5e00000000, 0xadc2654700000000, 0x6e91486c00000000, 0x2fa0537500000000, 0xe836123a00000000, 0xa907092300000000, 0x6a54240800000000, 0x2b653f1100000000, 0xe479a79600000000, 0xa548bc8f00000000, 0x661b91a400000000, 0x272a8abd00000000, 0xe0bccbf200000000, 0xa18dd0eb00000000, 0x62defdc000000000, 0x23efe6d900000000, 0xbde1bc1400000000, 0xfcd0a70d00000000, 0x3f838a2600000000, 0x7eb2913f00000000, 0xb924d07000000000, 0xf815cb6900000000, 0x3b46e64200000000, 0x7a77fd5b00000000, 0xb56b65dc00000000, 0xf45a7ec500000000, 0x370953ee00000000, 0x763848f700000000, 0xb1ae09b800000000, 0xf09f12a100000000, 0x33cc3f8a00000000, 0x72fd249300000000}, {0x0000000000000000, 0x376ac20100000000, 0x6ed4840300000000, 0x59be460200000000, 0xdca8090700000000, 0xebc2cb0600000000, 0xb27c8d0400000000, 0x85164f0500000000, 0xb851130e00000000, 0x8f3bd10f00000000, 0xd685970d00000000, 0xe1ef550c00000000, 0x64f91a0900000000, 0x5393d80800000000, 0x0a2d9e0a00000000, 0x3d475c0b00000000, 0x70a3261c00000000, 0x47c9e41d00000000, 0x1e77a21f00000000, 0x291d601e00000000, 0xac0b2f1b00000000, 0x9b61ed1a00000000, 0xc2dfab1800000000, 0xf5b5691900000000, 0xc8f2351200000000, 0xff98f71300000000, 0xa626b11100000000, 0x914c731000000000, 0x145a3c1500000000, 0x2330fe1400000000, 0x7a8eb81600000000, 0x4de47a1700000000, 0xe0464d3800000000, 0xd72c8f3900000000, 0x8e92c93b00000000, 0xb9f80b3a00000000, 0x3cee443f00000000, 0x0b84863e00000000, 0x523ac03c00000000, 0x6550023d00000000, 0x58175e3600000000, 0x6f7d9c3700000000, 0x36c3da3500000000, 0x01a9183400000000, 0x84bf573100000000, 0xb3d5953000000000, 0xea6bd33200000000, 0xdd01113300000000, 0x90e56b2400000000, 0xa78fa92500000000, 0xfe31ef2700000000, 0xc95b2d2600000000, 0x4c4d622300000000, 0x7b27a02200000000, 0x2299e62000000000, 0x15f3242100000000, 0x28b4782a00000000, 0x1fdeba2b00000000, 0x4660fc2900000000, 0x710a3e2800000000, 0xf41c712d00000000, 0xc376b32c00000000, 0x9ac8f52e00000000, 0xada2372f00000000, 0xc08d9a7000000000, 0xf7e7587100000000, 0xae591e7300000000, 0x9933dc7200000000, 0x1c25937700000000, 0x2b4f517600000000, 0x72f1177400000000, 0x459bd57500000000, 0x78dc897e00000000, 0x4fb64b7f00000000, 0x16080d7d00000000, 0x2162cf7c00000000, 0xa474807900000000, 0x931e427800000000, 0xcaa0047a00000000, 0xfdcac67b00000000, 0xb02ebc6c00000000, 0x87447e6d00000000, 0xdefa386f00000000, 0xe990fa6e00000000, 0x6c86b56b00000000, 0x5bec776a00000000, 0x0252316800000000, 0x3538f36900000000, 0x087faf6200000000, 0x3f156d6300000000, 0x66ab2b6100000000, 0x51c1e96000000000, 0xd4d7a66500000000, 0xe3bd646400000000, 0xba03226600000000, 0x8d69e06700000000, 0x20cbd74800000000, 0x17a1154900000000, 0x4e1f534b00000000, 0x7975914a00000000, 0xfc63de4f00000000, 0xcb091c4e00000000, 0x92b75a4c00000000, 0xa5dd984d00000000, 0x989ac44600000000, 0xaff0064700000000, 0xf64e404500000000, 0xc124824400000000, 0x4432cd4100000000, 0x73580f4000000000, 0x2ae6494200000000, 0x1d8c8b4300000000, 0x5068f15400000000, 0x6702335500000000, 0x3ebc755700000000, 0x09d6b75600000000, 0x8cc0f85300000000, 0xbbaa3a5200000000, 0xe2147c5000000000, 0xd57ebe5100000000, 0xe839e25a00000000, 0xdf53205b00000000, 0x86ed665900000000, 0xb187a45800000000, 0x3491eb5d00000000, 0x03fb295c00000000, 0x5a456f5e00000000, 0x6d2fad5f00000000, 0x801b35e100000000, 0xb771f7e000000000, 0xeecfb1e200000000, 0xd9a573e300000000, 0x5cb33ce600000000, 0x6bd9fee700000000, 0x3267b8e500000000, 0x050d7ae400000000, 0x384a26ef00000000, 0x0f20e4ee00000000, 0x569ea2ec00000000, 0x61f460ed00000000, 0xe4e22fe800000000, 0xd388ede900000000, 0x8a36abeb00000000, 0xbd5c69ea00000000, 0xf0b813fd00000000, 0xc7d2d1fc00000000, 0x9e6c97fe00000000, 0xa90655ff00000000, 0x2c101afa00000000, 0x1b7ad8fb00000000, 0x42c49ef900000000, 0x75ae5cf800000000, 0x48e900f300000000, 0x7f83c2f200000000, 0x263d84f000000000, 0x115746f100000000, 0x944109f400000000, 0xa32bcbf500000000, 0xfa958df700000000, 0xcdff4ff600000000, 0x605d78d900000000, 0x5737bad800000000, 0x0e89fcda00000000, 0x39e33edb00000000, 0xbcf571de00000000, 0x8b9fb3df00000000, 0xd221f5dd00000000, 0xe54b37dc00000000, 0xd80c6bd700000000, 0xef66a9d600000000, 0xb6d8efd400000000, 0x81b22dd500000000, 0x04a462d000000000, 0x33cea0d100000000, 0x6a70e6d300000000, 0x5d1a24d200000000, 0x10fe5ec500000000, 0x27949cc400000000, 0x7e2adac600000000, 0x494018c700000000, 0xcc5657c200000000, 0xfb3c95c300000000, 0xa282d3c100000000, 0x95e811c000000000, 0xa8af4dcb00000000, 0x9fc58fca00000000, 0xc67bc9c800000000, 0xf1110bc900000000, 0x740744cc00000000, 0x436d86cd00000000, 0x1ad3c0cf00000000, 0x2db902ce00000000, 0x4096af9100000000, 0x77fc6d9000000000, 0x2e422b9200000000, 0x1928e99300000000, 0x9c3ea69600000000, 0xab54649700000000, 0xf2ea229500000000, 0xc580e09400000000, 0xf8c7bc9f00000000, 0xcfad7e9e00000000, 0x9613389c00000000, 0xa179fa9d00000000, 0x246fb59800000000, 0x1305779900000000, 0x4abb319b00000000, 0x7dd1f39a00000000, 0x3035898d00000000, 0x075f4b8c00000000, 0x5ee10d8e00000000, 0x698bcf8f00000000, 0xec9d808a00000000, 0xdbf7428b00000000, 0x8249048900000000, 0xb523c68800000000, 0x88649a8300000000, 0xbf0e588200000000, 0xe6b01e8000000000, 0xd1dadc8100000000, 0x54cc938400000000, 0x63a6518500000000, 0x3a18178700000000, 0x0d72d58600000000, 0xa0d0e2a900000000, 0x97ba20a800000000, 0xce0466aa00000000, 0xf96ea4ab00000000, 0x7c78ebae00000000, 0x4b1229af00000000, 0x12ac6fad00000000, 0x25c6adac00000000, 0x1881f1a700000000, 0x2feb33a600000000, 0x765575a400000000, 0x413fb7a500000000, 0xc429f8a000000000, 0xf3433aa100000000, 0xaafd7ca300000000, 0x9d97bea200000000, 0xd073c4b500000000, 0xe71906b400000000, 0xbea740b600000000, 0x89cd82b700000000, 0x0cdbcdb200000000, 0x3bb10fb300000000, 0x620f49b100000000, 0x55658bb000000000, 0x6822d7bb00000000, 0x5f4815ba00000000, 0x06f653b800000000, 0x319c91b900000000, 0xb48adebc00000000, 0x83e01cbd00000000, 0xda5e5abf00000000, 0xed3498be00000000}, {0x0000000000000000, 0x6567bcb800000000, 0x8bc809aa00000000, 0xeeafb51200000000, 0x5797628f00000000, 0x32f0de3700000000, 0xdc5f6b2500000000, 0xb938d79d00000000, 0xef28b4c500000000, 0x8a4f087d00000000, 0x64e0bd6f00000000, 0x018701d700000000, 0xb8bfd64a00000000, 0xddd86af200000000, 0x3377dfe000000000, 0x5610635800000000, 0x9f57195000000000, 0xfa30a5e800000000, 0x149f10fa00000000, 0x71f8ac4200000000, 0xc8c07bdf00000000, 0xada7c76700000000, 0x4308727500000000, 0x266fcecd00000000, 0x707fad9500000000, 0x1518112d00000000, 0xfbb7a43f00000000, 0x9ed0188700000000, 0x27e8cf1a00000000, 0x428f73a200000000, 0xac20c6b000000000, 0xc9477a0800000000, 0x3eaf32a000000000, 0x5bc88e1800000000, 0xb5673b0a00000000, 0xd00087b200000000, 0x6938502f00000000, 0x0c5fec9700000000, 0xe2f0598500000000, 0x8797e53d00000000, 0xd187866500000000, 0xb4e03add00000000, 0x5a4f8fcf00000000, 0x3f28337700000000, 0x8610e4ea00000000, 0xe377585200000000, 0x0dd8ed4000000000, 0x68bf51f800000000, 0xa1f82bf000000000, 0xc49f974800000000, 0x2a30225a00000000, 0x4f579ee200000000, 0xf66f497f00000000, 0x9308f5c700000000, 0x7da740d500000000, 0x18c0fc6d00000000, 0x4ed09f3500000000, 0x2bb7238d00000000, 0xc518969f00000000, 0xa07f2a2700000000, 0x1947fdba00000000, 0x7c20410200000000, 0x928ff41000000000, 0xf7e848a800000000, 0x3d58149b00000000, 0x583fa82300000000, 0xb6901d3100000000, 0xd3f7a18900000000, 0x6acf761400000000, 0x0fa8caac00000000, 0xe1077fbe00000000, 0x8460c30600000000, 0xd270a05e00000000, 0xb7171ce600000000, 0x59b8a9f400000000, 0x3cdf154c00000000, 0x85e7c2d100000000, 0xe0807e6900000000, 0x0e2fcb7b00000000, 0x6b4877c300000000, 0xa20f0dcb00000000, 0xc768b17300000000, 0x29c7046100000000, 0x4ca0b8d900000000, 0xf5986f4400000000, 0x90ffd3fc00000000, 0x7e5066ee00000000, 0x1b37da5600000000, 0x4d27b90e00000000, 0x284005b600000000, 0xc6efb0a400000000, 0xa3880c1c00000000, 0x1ab0db8100000000, 0x7fd7673900000000, 0x9178d22b00000000, 0xf41f6e9300000000, 0x03f7263b00000000, 0x66909a8300000000, 0x883f2f9100000000, 0xed58932900000000, 0x546044b400000000, 0x3107f80c00000000, 0xdfa84d1e00000000, 0xbacff1a600000000, 0xecdf92fe00000000, 0x89b82e4600000000, 0x67179b5400000000, 0x027027ec00000000, 0xbb48f07100000000, 0xde2f4cc900000000, 0x3080f9db00000000, 0x55e7456300000000, 0x9ca03f6b00000000, 0xf9c783d300000000, 0x176836c100000000, 0x720f8a7900000000, 0xcb375de400000000, 0xae50e15c00000000, 0x40ff544e00000000, 0x2598e8f600000000, 0x73888bae00000000, 0x16ef371600000000, 0xf840820400000000, 0x9d273ebc00000000, 0x241fe92100000000, 0x4178559900000000, 0xafd7e08b00000000, 0xcab05c3300000000, 0x3bb659ed00000000, 0x5ed1e55500000000, 0xb07e504700000000, 0xd519ecff00000000, 0x6c213b6200000000, 0x094687da00000000, 0xe7e932c800000000, 0x828e8e7000000000, 0xd49eed2800000000, 0xb1f9519000000000, 0x5f56e48200000000, 0x3a31583a00000000, 0x83098fa700000000, 0xe66e331f00000000, 0x08c1860d00000000, 0x6da63ab500000000, 0xa4e140bd00000000, 0xc186fc0500000000, 0x2f29491700000000, 0x4a4ef5af00000000, 0xf376223200000000, 0x96119e8a00000000, 0x78be2b9800000000, 0x1dd9972000000000, 0x4bc9f47800000000, 0x2eae48c000000000, 0xc001fdd200000000, 0xa566416a00000000, 0x1c5e96f700000000, 0x79392a4f00000000, 0x97969f5d00000000, 0xf2f123e500000000, 0x05196b4d00000000, 0x607ed7f500000000, 0x8ed162e700000000, 0xebb6de5f00000000, 0x528e09c200000000, 0x37e9b57a00000000, 0xd946006800000000, 0xbc21bcd000000000, 0xea31df8800000000, 0x8f56633000000000, 0x61f9d62200000000, 0x049e6a9a00000000, 0xbda6bd0700000000, 0xd8c101bf00000000, 0x366eb4ad00000000, 0x5309081500000000, 0x9a4e721d00000000, 0xff29cea500000000, 0x11867bb700000000, 0x74e1c70f00000000, 0xcdd9109200000000, 0xa8beac2a00000000, 0x4611193800000000, 0x2376a58000000000, 0x7566c6d800000000, 0x10017a6000000000, 0xfeaecf7200000000, 0x9bc973ca00000000, 0x22f1a45700000000, 0x479618ef00000000, 0xa939adfd00000000, 0xcc5e114500000000, 0x06ee4d7600000000, 0x6389f1ce00000000, 0x8d2644dc00000000, 0xe841f86400000000, 0x51792ff900000000, 0x341e934100000000, 0xdab1265300000000, 0xbfd69aeb00000000, 0xe9c6f9b300000000, 0x8ca1450b00000000, 0x620ef01900000000, 0x07694ca100000000, 0xbe519b3c00000000, 0xdb36278400000000, 0x3599929600000000, 0x50fe2e2e00000000, 0x99b9542600000000, 0xfcdee89e00000000, 0x12715d8c00000000, 0x7716e13400000000, 0xce2e36a900000000, 0xab498a1100000000, 0x45e63f0300000000, 0x208183bb00000000, 0x7691e0e300000000, 0x13f65c5b00000000, 0xfd59e94900000000, 0x983e55f100000000, 0x2106826c00000000, 0x44613ed400000000, 0xaace8bc600000000, 0xcfa9377e00000000, 0x38417fd600000000, 0x5d26c36e00000000, 0xb389767c00000000, 0xd6eecac400000000, 0x6fd61d5900000000, 0x0ab1a1e100000000, 0xe41e14f300000000, 0x8179a84b00000000, 0xd769cb1300000000, 0xb20e77ab00000000, 0x5ca1c2b900000000, 0x39c67e0100000000, 0x80fea99c00000000, 0xe599152400000000, 0x0b36a03600000000, 0x6e511c8e00000000, 0xa716668600000000, 0xc271da3e00000000, 0x2cde6f2c00000000, 0x49b9d39400000000, 0xf081040900000000, 0x95e6b8b100000000, 0x7b490da300000000, 0x1e2eb11b00000000, 0x483ed24300000000, 0x2d596efb00000000, 0xc3f6dbe900000000, 0xa691675100000000, 0x1fa9b0cc00000000, 0x7ace0c7400000000, 0x9461b96600000000, 0xf10605de00000000}, {0x0000000000000000, 0xb029603d00000000, 0x6053c07a00000000, 0xd07aa04700000000, 0xc0a680f500000000, 0x708fe0c800000000, 0xa0f5408f00000000, 0x10dc20b200000000, 0xc14b703000000000, 0x7162100d00000000, 0xa118b04a00000000, 0x1131d07700000000, 0x01edf0c500000000, 0xb1c490f800000000, 0x61be30bf00000000, 0xd197508200000000, 0x8297e06000000000, 0x32be805d00000000, 0xe2c4201a00000000, 0x52ed402700000000, 0x4231609500000000, 0xf21800a800000000, 0x2262a0ef00000000, 0x924bc0d200000000, 0x43dc905000000000, 0xf3f5f06d00000000, 0x238f502a00000000, 0x93a6301700000000, 0x837a10a500000000, 0x3353709800000000, 0xe329d0df00000000, 0x5300b0e200000000, 0x042fc1c100000000, 0xb406a1fc00000000, 0x647c01bb00000000, 0xd455618600000000, 0xc489413400000000, 0x74a0210900000000, 0xa4da814e00000000, 0x14f3e17300000000, 0xc564b1f100000000, 0x754dd1cc00000000, 0xa537718b00000000, 0x151e11b600000000, 0x05c2310400000000, 0xb5eb513900000000, 0x6591f17e00000000, 0xd5b8914300000000, 0x86b821a100000000, 0x3691419c00000000, 0xe6ebe1db00000000, 0x56c281e600000000, 0x461ea15400000000, 0xf637c16900000000, 0x264d612e00000000, 0x9664011300000000, 0x47f3519100000000, 0xf7da31ac00000000, 0x27a091eb00000000, 0x9789f1d600000000, 0x8755d16400000000, 0x377cb15900000000, 0xe706111e00000000, 0x572f712300000000, 0x4958f35800000000, 0xf971936500000000, 0x290b332200000000, 0x9922531f00000000, 0x89fe73ad00000000, 0x39d7139000000000, 0xe9adb3d700000000, 0x5984d3ea00000000, 0x8813836800000000, 0x383ae35500000000, 0xe840431200000000, 0x5869232f00000000, 0x48b5039d00000000, 0xf89c63a000000000, 0x28e6c3e700000000, 0x98cfa3da00000000, 0xcbcf133800000000, 0x7be6730500000000, 0xab9cd34200000000, 0x1bb5b37f00000000, 0x0b6993cd00000000, 0xbb40f3f000000000, 0x6b3a53b700000000, 0xdb13338a00000000, 0x0a84630800000000, 0xbaad033500000000, 0x6ad7a37200000000, 0xdafec34f00000000, 0xca22e3fd00000000, 0x7a0b83c000000000, 0xaa71238700000000, 0x1a5843ba00000000, 0x4d77329900000000, 0xfd5e52a400000000, 0x2d24f2e300000000, 0x9d0d92de00000000, 0x8dd1b26c00000000, 0x3df8d25100000000, 0xed82721600000000, 0x5dab122b00000000, 0x8c3c42a900000000, 0x3c15229400000000, 0xec6f82d300000000, 0x5c46e2ee00000000, 0x4c9ac25c00000000, 0xfcb3a26100000000, 0x2cc9022600000000, 0x9ce0621b00000000, 0xcfe0d2f900000000, 0x7fc9b2c400000000, 0xafb3128300000000, 0x1f9a72be00000000, 0x0f46520c00000000, 0xbf6f323100000000, 0x6f15927600000000, 0xdf3cf24b00000000, 0x0eaba2c900000000, 0xbe82c2f400000000, 0x6ef862b300000000, 0xded1028e00000000, 0xce0d223c00000000, 0x7e24420100000000, 0xae5ee24600000000, 0x1e77827b00000000, 0x92b0e6b100000000, 0x2299868c00000000, 0xf2e326cb00000000, 0x42ca46f600000000, 0x5216664400000000, 0xe23f067900000000, 0x3245a63e00000000, 0x826cc60300000000, 0x53fb968100000000, 0xe3d2f6bc00000000, 0x33a856fb00000000, 0x838136c600000000, 0x935d167400000000, 0x2374764900000000, 0xf30ed60e00000000, 0x4327b63300000000, 0x102706d100000000, 0xa00e66ec00000000, 0x7074c6ab00000000, 0xc05da69600000000, 0xd081862400000000, 0x60a8e61900000000, 0xb0d2465e00000000, 0x00fb266300000000, 0xd16c76e100000000, 0x614516dc00000000, 0xb13fb69b00000000, 0x0116d6a600000000, 0x11caf61400000000, 0xa1e3962900000000, 0x7199366e00000000, 0xc1b0565300000000, 0x969f277000000000, 0x26b6474d00000000, 0xf6cce70a00000000, 0x46e5873700000000, 0x5639a78500000000, 0xe610c7b800000000, 0x366a67ff00000000, 0x864307c200000000, 0x57d4574000000000, 0xe7fd377d00000000, 0x3787973a00000000, 0x87aef70700000000, 0x9772d7b500000000, 0x275bb78800000000, 0xf72117cf00000000, 0x470877f200000000, 0x1408c71000000000, 0xa421a72d00000000, 0x745b076a00000000, 0xc472675700000000, 0xd4ae47e500000000, 0x648727d800000000, 0xb4fd879f00000000, 0x04d4e7a200000000, 0xd543b72000000000, 0x656ad71d00000000, 0xb510775a00000000, 0x0539176700000000, 0x15e537d500000000, 0xa5cc57e800000000, 0x75b6f7af00000000, 0xc59f979200000000, 0xdbe815e900000000, 0x6bc175d400000000, 0xbbbbd59300000000, 0x0b92b5ae00000000, 0x1b4e951c00000000, 0xab67f52100000000, 0x7b1d556600000000, 0xcb34355b00000000, 0x1aa365d900000000, 0xaa8a05e400000000, 0x7af0a5a300000000, 0xcad9c59e00000000, 0xda05e52c00000000, 0x6a2c851100000000, 0xba56255600000000, 0x0a7f456b00000000, 0x597ff58900000000, 0xe95695b400000000, 0x392c35f300000000, 0x890555ce00000000, 0x99d9757c00000000, 0x29f0154100000000, 0xf98ab50600000000, 0x49a3d53b00000000, 0x983485b900000000, 0x281de58400000000, 0xf86745c300000000, 0x484e25fe00000000, 0x5892054c00000000, 0xe8bb657100000000, 0x38c1c53600000000, 0x88e8a50b00000000, 0xdfc7d42800000000, 0x6feeb41500000000, 0xbf94145200000000, 0x0fbd746f00000000, 0x1f6154dd00000000, 0xaf4834e000000000, 0x7f3294a700000000, 0xcf1bf49a00000000, 0x1e8ca41800000000, 0xaea5c42500000000, 0x7edf646200000000, 0xcef6045f00000000, 0xde2a24ed00000000, 0x6e0344d000000000, 0xbe79e49700000000, 0x0e5084aa00000000, 0x5d50344800000000, 0xed79547500000000, 0x3d03f43200000000, 0x8d2a940f00000000, 0x9df6b4bd00000000, 0x2ddfd48000000000, 0xfda574c700000000, 0x4d8c14fa00000000, 0x9c1b447800000000, 0x2c32244500000000, 0xfc48840200000000, 0x4c61e43f00000000, 0x5cbdc48d00000000, 0xec94a4b000000000, 0x3cee04f700000000, 0x8cc764ca00000000}, {0x0000000000000000, 0xa5d35ccb00000000, 0x0ba1c84d00000000, 0xae72948600000000, 0x1642919b00000000, 0xb391cd5000000000, 0x1de359d600000000, 0xb830051d00000000, 0x6d8253ec00000000, 0xc8510f2700000000, 0x66239ba100000000, 0xc3f0c76a00000000, 0x7bc0c27700000000, 0xde139ebc00000000, 0x70610a3a00000000, 0xd5b256f100000000, 0x9b02d60300000000, 0x3ed18ac800000000, 0x90a31e4e00000000, 0x3570428500000000, 0x8d40479800000000, 0x28931b5300000000, 0x86e18fd500000000, 0x2332d31e00000000, 0xf68085ef00000000, 0x5353d92400000000, 0xfd214da200000000, 0x58f2116900000000, 0xe0c2147400000000, 0x451148bf00000000, 0xeb63dc3900000000, 0x4eb080f200000000, 0x3605ac0700000000, 0x93d6f0cc00000000, 0x3da4644a00000000, 0x9877388100000000, 0x20473d9c00000000, 0x8594615700000000, 0x2be6f5d100000000, 0x8e35a91a00000000, 0x5b87ffeb00000000, 0xfe54a32000000000, 0x502637a600000000, 0xf5f56b6d00000000, 0x4dc56e7000000000, 0xe81632bb00000000, 0x4664a63d00000000, 0xe3b7faf600000000, 0xad077a0400000000, 0x08d426cf00000000, 0xa6a6b24900000000, 0x0375ee8200000000, 0xbb45eb9f00000000, 0x1e96b75400000000, 0xb0e423d200000000, 0x15377f1900000000, 0xc08529e800000000, 0x6556752300000000, 0xcb24e1a500000000, 0x6ef7bd6e00000000, 0xd6c7b87300000000, 0x7314e4b800000000, 0xdd66703e00000000, 0x78b52cf500000000, 0x6c0a580f00000000, 0xc9d904c400000000, 0x67ab904200000000, 0xc278cc8900000000, 0x7a48c99400000000, 0xdf9b955f00000000, 0x71e901d900000000, 0xd43a5d1200000000, 0x01880be300000000, 0xa45b572800000000, 0x0a29c3ae00000000, 0xaffa9f6500000000, 0x17ca9a7800000000, 0xb219c6b300000000, 0x1c6b523500000000, 0xb9b80efe00000000, 0xf7088e0c00000000, 0x52dbd2c700000000, 0xfca9464100000000, 0x597a1a8a00000000, 0xe14a1f9700000000, 0x4499435c00000000, 0xeaebd7da00000000, 0x4f388b1100000000, 0x9a8adde000000000, 0x3f59812b00000000, 0x912b15ad00000000, 0x34f8496600000000, 0x8cc84c7b00000000, 0x291b10b000000000, 0x8769843600000000, 0x22bad8fd00000000, 0x5a0ff40800000000, 0xffdca8c300000000, 0x51ae3c4500000000, 0xf47d608e00000000, 0x4c4d659300000000, 0xe99e395800000000, 0x47ecadde00000000, 0xe23ff11500000000, 0x378da7e400000000, 0x925efb2f00000000, 0x3c2c6fa900000000, 0x99ff336200000000, 0x21cf367f00000000, 0x841c6ab400000000, 0x2a6efe3200000000, 0x8fbda2f900000000, 0xc10d220b00000000, 0x64de7ec000000000, 0xcaacea4600000000, 0x6f7fb68d00000000, 0xd74fb39000000000, 0x729cef5b00000000, 0xdcee7bdd00000000, 0x793d271600000000, 0xac8f71e700000000, 0x095c2d2c00000000, 0xa72eb9aa00000000, 0x02fde56100000000, 0xbacde07c00000000, 0x1f1ebcb700000000, 0xb16c283100000000, 0x14bf74fa00000000, 0xd814b01e00000000, 0x7dc7ecd500000000, 0xd3b5785300000000, 0x7666249800000000, 0xce56218500000000, 0x6b857d4e00000000, 0xc5f7e9c800000000, 0x6024b50300000000, 0xb596e3f200000000, 0x1045bf3900000000, 0xbe372bbf00000000, 0x1be4777400000000, 0xa3d4726900000000, 0x06072ea200000000, 0xa875ba2400000000, 0x0da6e6ef00000000, 0x4316661d00000000, 0xe6c53ad600000000, 0x48b7ae5000000000, 0xed64f29b00000000, 0x5554f78600000000, 0xf087ab4d00000000, 0x5ef53fcb00000000, 0xfb26630000000000, 0x2e9435f100000000, 0x8b47693a00000000, 0x2535fdbc00000000, 0x80e6a17700000000, 0x38d6a46a00000000, 0x9d05f8a100000000, 0x33776c2700000000, 0x96a430ec00000000, 0xee111c1900000000, 0x4bc240d200000000, 0xe5b0d45400000000, 0x4063889f00000000, 0xf8538d8200000000, 0x5d80d14900000000, 0xf3f245cf00000000, 0x5621190400000000, 0x83934ff500000000, 0x2640133e00000000, 0x883287b800000000, 0x2de1db7300000000, 0x95d1de6e00000000, 0x300282a500000000, 0x9e70162300000000, 0x3ba34ae800000000, 0x7513ca1a00000000, 0xd0c096d100000000, 0x7eb2025700000000, 0xdb615e9c00000000, 0x63515b8100000000, 0xc682074a00000000, 0x68f093cc00000000, 0xcd23cf0700000000, 0x189199f600000000, 0xbd42c53d00000000, 0x133051bb00000000, 0xb6e30d7000000000, 0x0ed3086d00000000, 0xab0054a600000000, 0x0572c02000000000, 0xa0a19ceb00000000, 0xb41ee81100000000, 0x11cdb4da00000000, 0xbfbf205c00000000, 0x1a6c7c9700000000, 0xa25c798a00000000, 0x078f254100000000, 0xa9fdb1c700000000, 0x0c2eed0c00000000, 0xd99cbbfd00000000, 0x7c4fe73600000000, 0xd23d73b000000000, 0x77ee2f7b00000000, 0xcfde2a6600000000, 0x6a0d76ad00000000, 0xc47fe22b00000000, 0x61acbee000000000, 0x2f1c3e1200000000, 0x8acf62d900000000, 0x24bdf65f00000000, 0x816eaa9400000000, 0x395eaf8900000000, 0x9c8df34200000000, 0x32ff67c400000000, 0x972c3b0f00000000, 0x429e6dfe00000000, 0xe74d313500000000, 0x493fa5b300000000, 0xececf97800000000, 0x54dcfc6500000000, 0xf10fa0ae00000000, 0x5f7d342800000000, 0xfaae68e300000000, 0x821b441600000000, 0x27c818dd00000000, 0x89ba8c5b00000000, 0x2c69d09000000000, 0x9459d58d00000000, 0x318a894600000000, 0x9ff81dc000000000, 0x3a2b410b00000000, 0xef9917fa00000000, 0x4a4a4b3100000000, 0xe438dfb700000000, 0x41eb837c00000000, 0xf9db866100000000, 0x5c08daaa00000000, 0xf27a4e2c00000000, 0x57a912e700000000, 0x1919921500000000, 0xbccacede00000000, 0x12b85a5800000000, 0xb76b069300000000, 0x0f5b038e00000000, 0xaa885f4500000000, 0x04facbc300000000, 0xa129970800000000, 0x749bc1f900000000, 0xd1489d3200000000, 0x7f3a09b400000000, 0xdae9557f00000000, 0x62d9506200000000, 0xc70a0ca900000000, 0x6978982f00000000, 0xccabc4e400000000}, {0x0000000000000000, 0xb40b77a600000000, 0x29119f9700000000, 0x9d1ae83100000000, 0x13244ff400000000, 0xa72f385200000000, 0x3a35d06300000000, 0x8e3ea7c500000000, 0x674eef3300000000, 0xd345989500000000, 0x4e5f70a400000000, 0xfa54070200000000, 0x746aa0c700000000, 0xc061d76100000000, 0x5d7b3f5000000000, 0xe97048f600000000, 0xce9cde6700000000, 0x7a97a9c100000000, 0xe78d41f000000000, 0x5386365600000000, 0xddb8919300000000, 0x69b3e63500000000, 0xf4a90e0400000000, 0x40a279a200000000, 0xa9d2315400000000, 0x1dd946f200000000, 0x80c3aec300000000, 0x34c8d96500000000, 0xbaf67ea000000000, 0x0efd090600000000, 0x93e7e13700000000, 0x27ec969100000000, 0x9c39bdcf00000000, 0x2832ca6900000000, 0xb528225800000000, 0x012355fe00000000, 0x8f1df23b00000000, 0x3b16859d00000000, 0xa60c6dac00000000, 0x12071a0a00000000, 0xfb7752fc00000000, 0x4f7c255a00000000, 0xd266cd6b00000000, 0x666dbacd00000000, 0xe8531d0800000000, 0x5c586aae00000000, 0xc142829f00000000, 0x7549f53900000000, 0x52a563a800000000, 0xe6ae140e00000000, 0x7bb4fc3f00000000, 0xcfbf8b9900000000, 0x41812c5c00000000, 0xf58a5bfa00000000, 0x6890b3cb00000000, 0xdc9bc46d00000000, 0x35eb8c9b00000000, 0x81e0fb3d00000000, 0x1cfa130c00000000, 0xa8f164aa00000000, 0x26cfc36f00000000, 0x92c4b4c900000000, 0x0fde5cf800000000, 0xbbd52b5e00000000, 0x79750b4400000000, 0xcd7e7ce200000000, 0x506494d300000000, 0xe46fe37500000000, 0x6a5144b000000000, 0xde5a331600000000, 0x4340db2700000000, 0xf74bac8100000000, 0x1e3be47700000000, 0xaa3093d100000000, 0x372a7be000000000, 0x83210c4600000000, 0x0d1fab8300000000, 0xb914dc2500000000, 0x240e341400000000, 0x900543b200000000, 0xb7e9d52300000000, 0x03e2a28500000000, 0x9ef84ab400000000, 0x2af33d1200000000, 0xa4cd9ad700000000, 0x10c6ed7100000000, 0x8ddc054000000000, 0x39d772e600000000, 0xd0a73a1000000000, 0x64ac4db600000000, 0xf9b6a58700000000, 0x4dbdd22100000000, 0xc38375e400000000, 0x7788024200000000, 0xea92ea7300000000, 0x5e999dd500000000, 0xe54cb68b00000000, 0x5147c12d00000000, 0xcc5d291c00000000, 0x78565eba00000000, 0xf668f97f00000000, 0x42638ed900000000, 0xdf7966e800000000, 0x6b72114e00000000, 0x820259b800000000, 0x36092e1e00000000, 0xab13c62f00000000, 0x1f18b18900000000, 0x9126164c00000000, 0x252d61ea00000000, 0xb83789db00000000, 0x0c3cfe7d00000000, 0x2bd068ec00000000, 0x9fdb1f4a00000000, 0x02c1f77b00000000, 0xb6ca80dd00000000, 0x38f4271800000000, 0x8cff50be00000000, 0x11e5b88f00000000, 0xa5eecf2900000000, 0x4c9e87df00000000, 0xf895f07900000000, 0x658f184800000000, 0xd1846fee00000000, 0x5fbac82b00000000, 0xebb1bf8d00000000, 0x76ab57bc00000000, 0xc2a0201a00000000, 0xf2ea168800000000, 0x46e1612e00000000, 0xdbfb891f00000000, 0x6ff0feb900000000, 0xe1ce597c00000000, 0x55c52eda00000000, 0xc8dfc6eb00000000, 0x7cd4b14d00000000, 0x95a4f9bb00000000, 0x21af8e1d00000000, 0xbcb5662c00000000, 0x08be118a00000000, 0x8680b64f00000000, 0x328bc1e900000000, 0xaf9129d800000000, 0x1b9a5e7e00000000, 0x3c76c8ef00000000, 0x887dbf4900000000, 0x1567577800000000, 0xa16c20de00000000, 0x2f52871b00000000, 0x9b59f0bd00000000, 0x0643188c00000000, 0xb2486f2a00000000, 0x5b3827dc00000000, 0xef33507a00000000, 0x7229b84b00000000, 0xc622cfed00000000, 0x481c682800000000, 0xfc171f8e00000000, 0x610df7bf00000000, 0xd506801900000000, 0x6ed3ab4700000000, 0xdad8dce100000000, 0x47c234d000000000, 0xf3c9437600000000, 0x7df7e4b300000000, 0xc9fc931500000000, 0x54e67b2400000000, 0xe0ed0c8200000000, 0x099d447400000000, 0xbd9633d200000000, 0x208cdbe300000000, 0x9487ac4500000000, 0x1ab90b8000000000, 0xaeb27c2600000000, 0x33a8941700000000, 0x87a3e3b100000000, 0xa04f752000000000, 0x1444028600000000, 0x895eeab700000000, 0x3d559d1100000000, 0xb36b3ad400000000, 0x07604d7200000000, 0x9a7aa54300000000, 0x2e71d2e500000000, 0xc7019a1300000000, 0x730aedb500000000, 0xee10058400000000, 0x5a1b722200000000, 0xd425d5e700000000, 0x602ea24100000000, 0xfd344a7000000000, 0x493f3dd600000000, 0x8b9f1dcc00000000, 0x3f946a6a00000000, 0xa28e825b00000000, 0x1685f5fd00000000, 0x98bb523800000000, 0x2cb0259e00000000, 0xb1aacdaf00000000, 0x05a1ba0900000000, 0xecd1f2ff00000000, 0x58da855900000000, 0xc5c06d6800000000, 0x71cb1ace00000000, 0xfff5bd0b00000000, 0x4bfecaad00000000, 0xd6e4229c00000000, 0x62ef553a00000000, 0x4503c3ab00000000, 0xf108b40d00000000, 0x6c125c3c00000000, 0xd8192b9a00000000, 0x56278c5f00000000, 0xe22cfbf900000000, 0x7f3613c800000000, 0xcb3d646e00000000, 0x224d2c9800000000, 0x96465b3e00000000, 0x0b5cb30f00000000, 0xbf57c4a900000000, 0x3169636c00000000, 0x856214ca00000000, 0x1878fcfb00000000, 0xac738b5d00000000, 0x17a6a00300000000, 0xa3add7a500000000, 0x3eb73f9400000000, 0x8abc483200000000, 0x0482eff700000000, 0xb089985100000000, 0x2d93706000000000, 0x999807c600000000, 0x70e84f3000000000, 0xc4e3389600000000, 0x59f9d0a700000000, 0xedf2a70100000000, 0x63cc00c400000000, 0xd7c7776200000000, 0x4add9f5300000000, 0xfed6e8f500000000, 0xd93a7e6400000000, 0x6d3109c200000000, 0xf02be1f300000000, 0x4420965500000000, 0xca1e319000000000, 0x7e15463600000000, 0xe30fae0700000000, 0x5704d9a100000000, 0xbe74915700000000, 0x0a7fe6f100000000, 0x97650ec000000000, 0x236e796600000000, 0xad50dea300000000, 0x195ba90500000000, 0x8441413400000000, 0x304a369200000000}, {0x0000000000000000, 0x9e00aacc00000000, 0x7d07254200000000, 0xe3078f8e00000000, 0xfa0e4a8400000000, 0x640ee04800000000, 0x87096fc600000000, 0x1909c50a00000000, 0xb51be5d300000000, 0x2b1b4f1f00000000, 0xc81cc09100000000, 0x561c6a5d00000000, 0x4f15af5700000000, 0xd115059b00000000, 0x32128a1500000000, 0xac1220d900000000, 0x2b31bb7c00000000, 0xb53111b000000000, 0x56369e3e00000000, 0xc83634f200000000, 0xd13ff1f800000000, 0x4f3f5b3400000000, 0xac38d4ba00000000, 0x32387e7600000000, 0x9e2a5eaf00000000, 0x002af46300000000, 0xe32d7bed00000000, 0x7d2dd12100000000, 0x6424142b00000000, 0xfa24bee700000000, 0x1923316900000000, 0x87239ba500000000, 0x566276f900000000, 0xc862dc3500000000, 0x2b6553bb00000000, 0xb565f97700000000, 0xac6c3c7d00000000, 0x326c96b100000000, 0xd16b193f00000000, 0x4f6bb3f300000000, 0xe379932a00000000, 0x7d7939e600000000, 0x9e7eb66800000000, 0x007e1ca400000000, 0x1977d9ae00000000, 0x8777736200000000, 0x6470fcec00000000, 0xfa70562000000000, 0x7d53cd8500000000, 0xe353674900000000, 0x0054e8c700000000, 0x9e54420b00000000, 0x875d870100000000, 0x195d2dcd00000000, 0xfa5aa24300000000, 0x645a088f00000000, 0xc848285600000000, 0x5648829a00000000, 0xb54f0d1400000000, 0x2b4fa7d800000000, 0x324662d200000000, 0xac46c81e00000000, 0x4f41479000000000, 0xd141ed5c00000000, 0xedc29d2900000000, 0x73c237e500000000, 0x90c5b86b00000000, 0x0ec512a700000000, 0x17ccd7ad00000000, 0x89cc7d6100000000, 0x6acbf2ef00000000, 0xf4cb582300000000, 0x58d978fa00000000, 0xc6d9d23600000000, 0x25de5db800000000, 0xbbdef77400000000, 0xa2d7327e00000000, 0x3cd798b200000000, 0xdfd0173c00000000, 0x41d0bdf000000000, 0xc6f3265500000000, 0x58f38c9900000000, 0xbbf4031700000000, 0x25f4a9db00000000, 0x3cfd6cd100000000, 0xa2fdc61d00000000, 0x41fa499300000000, 0xdffae35f00000000, 0x73e8c38600000000, 0xede8694a00000000, 0x0eefe6c400000000, 0x90ef4c0800000000, 0x89e6890200000000, 0x17e623ce00000000, 0xf4e1ac4000000000, 0x6ae1068c00000000, 0xbba0ebd000000000, 0x25a0411c00000000, 0xc6a7ce9200000000, 0x58a7645e00000000, 0x41aea15400000000, 0xdfae0b9800000000, 0x3ca9841600000000, 0xa2a92eda00000000, 0x0ebb0e0300000000, 0x90bba4cf00000000, 0x73bc2b4100000000, 0xedbc818d00000000, 0xf4b5448700000000, 0x6ab5ee4b00000000, 0x89b261c500000000, 0x17b2cb0900000000, 0x909150ac00000000, 0x0e91fa6000000000, 0xed9675ee00000000, 0x7396df2200000000, 0x6a9f1a2800000000, 0xf49fb0e400000000, 0x17983f6a00000000, 0x899895a600000000, 0x258ab57f00000000, 0xbb8a1fb300000000, 0x588d903d00000000, 0xc68d3af100000000, 0xdf84fffb00000000, 0x4184553700000000, 0xa283dab900000000, 0x3c83707500000000, 0xda853b5300000000, 0x4485919f00000000, 0xa7821e1100000000, 0x3982b4dd00000000, 0x208b71d700000000, 0xbe8bdb1b00000000, 0x5d8c549500000000, 0xc38cfe5900000000, 0x6f9ede8000000000, 0xf19e744c00000000, 0x1299fbc200000000, 0x8c99510e00000000, 0x9590940400000000, 0x0b903ec800000000, 0xe897b14600000000, 0x76971b8a00000000, 0xf1b4802f00000000, 0x6fb42ae300000000, 0x8cb3a56d00000000, 0x12b30fa100000000, 0x0bbacaab00000000, 0x95ba606700000000, 0x76bdefe900000000, 0xe8bd452500000000, 0x44af65fc00000000, 0xdaafcf3000000000, 0x39a840be00000000, 0xa7a8ea7200000000, 0xbea12f7800000000, 0x20a185b400000000, 0xc3a60a3a00000000, 0x5da6a0f600000000, 0x8ce74daa00000000, 0x12e7e76600000000, 0xf1e068e800000000, 0x6fe0c22400000000, 0x76e9072e00000000, 0xe8e9ade200000000, 0x0bee226c00000000, 0x95ee88a000000000, 0x39fca87900000000, 0xa7fc02b500000000, 0x44fb8d3b00000000, 0xdafb27f700000000, 0xc3f2e2fd00000000, 0x5df2483100000000, 0xbef5c7bf00000000, 0x20f56d7300000000, 0xa7d6f6d600000000, 0x39d65c1a00000000, 0xdad1d39400000000, 0x44d1795800000000, 0x5dd8bc5200000000, 0xc3d8169e00000000, 0x20df991000000000, 0xbedf33dc00000000, 0x12cd130500000000, 0x8ccdb9c900000000, 0x6fca364700000000, 0xf1ca9c8b00000000, 0xe8c3598100000000, 0x76c3f34d00000000, 0x95c47cc300000000, 0x0bc4d60f00000000, 0x3747a67a00000000, 0xa9470cb600000000, 0x4a40833800000000, 0xd44029f400000000, 0xcd49ecfe00000000, 0x5349463200000000, 0xb04ec9bc00000000, 0x2e4e637000000000, 0x825c43a900000000, 0x1c5ce96500000000, 0xff5b66eb00000000, 0x615bcc2700000000, 0x7852092d00000000, 0xe652a3e100000000, 0x05552c6f00000000, 0x9b5586a300000000, 0x1c761d0600000000, 0x8276b7ca00000000, 0x6171384400000000, 0xff71928800000000, 0xe678578200000000, 0x7878fd4e00000000, 0x9b7f72c000000000, 0x057fd80c00000000, 0xa96df8d500000000, 0x376d521900000000, 0xd46add9700000000, 0x4a6a775b00000000, 0x5363b25100000000, 0xcd63189d00000000, 0x2e64971300000000, 0xb0643ddf00000000, 0x6125d08300000000, 0xff257a4f00000000, 0x1c22f5c100000000, 0x82225f0d00000000, 0x9b2b9a0700000000, 0x052b30cb00000000, 0xe62cbf4500000000, 0x782c158900000000, 0xd43e355000000000, 0x4a3e9f9c00000000, 0xa939101200000000, 0x3739bade00000000, 0x2e307fd400000000, 0xb030d51800000000, 0x53375a9600000000, 0xcd37f05a00000000, 0x4a146bff00000000, 0xd414c13300000000, 0x37134ebd00000000, 0xa913e47100000000, 0xb01a217b00000000, 0x2e1a8bb700000000, 0xcd1d043900000000, 0x531daef500000000, 0xff0f8e2c00000000, 0x610f24e000000000, 0x8208ab6e00000000, 0x1c0801a200000000, 0x0501c4a800000000, 0x9b016e6400000000, 0x7806e1ea00000000, 0xe6064b2600000000}}; #else /* W == 4 */ local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, 0x8f629757, 0x37def032, 0x256b5fdc, 0x9dd738b9, 0xc5b428ef, 0x7d084f8a, 0x6fbde064, 0xd7018701, 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, 0x58631056, 0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26, 0x95ad7f70, 0x2d111815, 0x3fa4b7fb, 0x8718d09e, 0x1acfe827, 0xa2738f42, 0xb0c620ac, 0x087a47c9, 0xa032af3e, 0x188ec85b, 0x0a3b67b5, 0xb28700d0, 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787, 0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, 0xeae41086, 0x525877e3, 0x40edd80d, 0xf851bf68, 0xf02bf8a1, 0x48979fc4, 0x5a22302a, 0xe29e574f, 0x7f496ff6, 0xc7f50893, 0xd540a77d, 0x6dfcc018, 0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7, 0x9b14583d, 0x23a83f58, 0x311d90b6, 0x89a1f7d3, 0x1476cf6a, 0xaccaa80f, 0xbe7f07e1, 0x06c36084, 0x5ea070d2, 0xe61c17b7, 0xf4a9b859, 0x4c15df3c, 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b, 0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, 0x446f98f5, 0xfcd3ff90, 0xee66507e, 0x56da371b, 0x0eb9274d, 0xb6054028, 0xa4b0efc6, 0x1c0c88a3, 0x81dbb01a, 0x3967d77f, 0x2bd27891, 0x936e1ff4, 0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba, 0xfe92dfec, 0x462eb889, 0x549b1767, 0xec277002, 0x71f048bb, 0xc94c2fde, 0xdbf98030, 0x6345e755, 0x6b3fa09c, 0xd383c7f9, 0xc1366817, 0x798a0f72, 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825, 0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, 0x21e91f24, 0x99557841, 0x8be0d7af, 0x335cb0ca, 0xed59b63b, 0x55e5d15e, 0x47507eb0, 0xffec19d5, 0x623b216c, 0xda874609, 0xc832e9e7, 0x708e8e82, 0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d, 0xbd40e1a4, 0x05fc86c1, 0x1749292f, 0xaff54e4a, 0x322276f3, 0x8a9e1196, 0x982bbe78, 0x2097d91d, 0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, 0x6a4166a5, 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2, 0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, 0xc2098e52, 0x7ab5e937, 0x680046d9, 0xd0bc21bc, 0x88df31ea, 0x3063568f, 0x22d6f961, 0x9a6a9e04, 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, 0x15080953, 0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623, 0xd8c66675, 0x607a0110, 0x72cfaefe, 0xca73c99b, 0x57a4f122, 0xef189647, 0xfdad39a9, 0x45115ecc, 0x764dee06, 0xcef18963, 0xdc44268d, 0x64f841e8, 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf, 0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, 0x3c9b51be, 0x842736db, 0x96929935, 0x2e2efe50, 0x2654b999, 0x9ee8defc, 0x8c5d7112, 0x34e11677, 0xa9362ece, 0x118a49ab, 0x033fe645, 0xbb838120, 0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf, 0xd67f4138, 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, 0x591dd66f, 0xe1a1b10a, 0xf3141ee4, 0x4ba87981, 0x13cb69d7, 0xab770eb2, 0xb9c2a15c, 0x017ec639, 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e, 0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, 0x090481f0, 0xb1b8e695, 0xa30d497b, 0x1bb12e1e, 0x43d23e48, 0xfb6e592d, 0xe9dbf6c3, 0x516791a6, 0xccb0a91f, 0x740cce7a, 0x66b96194, 0xde0506f1}, {0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, 0x0709a8dc, 0x06cbc2eb, 0x048d7cb2, 0x054f1685, 0x0e1351b8, 0x0fd13b8f, 0x0d9785d6, 0x0c55efe1, 0x091af964, 0x08d89353, 0x0a9e2d0a, 0x0b5c473d, 0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5, 0x1235f2c8, 0x13f798ff, 0x11b126a6, 0x10734c91, 0x153c5a14, 0x14fe3023, 0x16b88e7a, 0x177ae44d, 0x384d46e0, 0x398f2cd7, 0x3bc9928e, 0x3a0bf8b9, 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065, 0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, 0x3157bf84, 0x3095d5b3, 0x32d36bea, 0x331101dd, 0x246be590, 0x25a98fa7, 0x27ef31fe, 0x262d5bc9, 0x23624d4c, 0x22a0277b, 0x20e69922, 0x2124f315, 0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad, 0x709a8dc0, 0x7158e7f7, 0x731e59ae, 0x72dc3399, 0x7793251c, 0x76514f2b, 0x7417f172, 0x75d59b45, 0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, 0x7ccf6221, 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd, 0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, 0x6bb5866c, 0x6a77ec5b, 0x68315202, 0x69f33835, 0x62af7f08, 0x636d153f, 0x612bab66, 0x60e9c151, 0x65a6d7d4, 0x6464bde3, 0x662203ba, 0x67e0698d, 0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5, 0x46c49a98, 0x4706f0af, 0x45404ef6, 0x448224c1, 0x41cd3244, 0x400f5873, 0x4249e62a, 0x438b8c1d, 0x54f16850, 0x55330267, 0x5775bc3e, 0x56b7d609, 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5, 0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, 0x5deb9134, 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d, 0xe1351b80, 0xe0f771b7, 0xe2b1cfee, 0xe373a5d9, 0xe63cb35c, 0xe7fed96b, 0xe5b86732, 0xe47a0d05, 0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd, 0xfd13b8f0, 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, 0xfa1a102c, 0xfbd87a1b, 0xf99ec442, 0xf85cae75, 0xf300e948, 0xf2c2837f, 0xf0843d26, 0xf1465711, 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd, 0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, 0xde71f5bc, 0xdfb39f8b, 0xddf521d2, 0xdc374be5, 0xd76b0cd8, 0xd6a966ef, 0xd4efd8b6, 0xd52db281, 0xd062a404, 0xd1a0ce33, 0xd3e6706a, 0xd2241a5d, 0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895, 0xcb4dafa8, 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, 0xcc440774, 0xcd866d43, 0xcfc0d31a, 0xce02b92d, 0x91af9640, 0x906dfc77, 0x922b422e, 0x93e92819, 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5, 0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, 0x98b56f24, 0x99770513, 0x9b31bb4a, 0x9af3d17d, 0x8d893530, 0x8c4b5f07, 0x8e0de15e, 0x8fcf8b69, 0x8a809dec, 0x8b42f7db, 0x89044982, 0x88c623b5, 0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d, 0xa9e2d0a0, 0xa820ba97, 0xaa6604ce, 0xaba46ef9, 0xaeeb787c, 0xaf29124b, 0xad6fac12, 0xacadc625, 0xa7f18118, 0xa633eb2f, 0xa4755576, 0xa5b73f41, 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d, 0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, 0xb2cddb0c, 0xb30fb13b, 0xb1490f62, 0xb08b6555, 0xbbd72268, 0xba15485f, 0xb853f606, 0xb9919c31, 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, 0xbe9834ed}, {0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, 0x646cc504, 0x7d77f445, 0x565aa786, 0x4f4196c7, 0xc8d98a08, 0xd1c2bb49, 0xfaefe88a, 0xe3f4d9cb, 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, 0x87981ccf, 0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496, 0x821b9859, 0x9b00a918, 0xb02dfadb, 0xa936cb9a, 0xe6775d5d, 0xff6c6c1c, 0xd4413fdf, 0xcd5a0e9e, 0x958424a2, 0x8c9f15e3, 0xa7b24620, 0xbea97761, 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265, 0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, 0x39316bae, 0x202a5aef, 0x0b07092c, 0x121c386d, 0xdf4636f3, 0xc65d07b2, 0xed705471, 0xf46b6530, 0xbb2af3f7, 0xa231c2b6, 0x891c9175, 0x9007a034, 0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c, 0xf0794f05, 0xe9627e44, 0xc24f2d87, 0xdb541cc6, 0x94158a01, 0x8d0ebb40, 0xa623e883, 0xbf38d9c2, 0x38a0c50d, 0x21bbf44c, 0x0a96a78f, 0x138d96ce, 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca, 0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, 0xded79850, 0xc7cca911, 0xece1fad2, 0xf5facb93, 0x7262d75c, 0x6b79e61d, 0x4054b5de, 0x594f849f, 0x160e1258, 0x0f152319, 0x243870da, 0x3d23419b, 0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60, 0xad24e1af, 0xb43fd0ee, 0x9f12832d, 0x8609b26c, 0xc94824ab, 0xd05315ea, 0xfb7e4629, 0xe2657768, 0x2f3f79f6, 0x362448b7, 0x1d091b74, 0x04122a35, 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31, 0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, 0x838a36fa, 0x9a9107bb, 0xb1bc5478, 0xa8a76539, 0x3b83984b, 0x2298a90a, 0x09b5fac9, 0x10aecb88, 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, 0x74c20e8c, 0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484, 0x71418a1a, 0x685abb5b, 0x4377e898, 0x5a6cd9d9, 0x152d4f1e, 0x0c367e5f, 0x271b2d9c, 0x3e001cdd, 0xb9980012, 0xa0833153, 0x8bae6290, 0x92b553d1, 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5, 0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, 0xca6b79ed, 0xd37048ac, 0xf85d1b6f, 0xe1462a2e, 0x66de36e1, 0x7fc507a0, 0x54e85463, 0x4df36522, 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, 0x299fa026, 0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f, 0x2c1c24b0, 0x350715f1, 0x1e2a4632, 0x07317773, 0x4870e1b4, 0x516bd0f5, 0x7a468336, 0x635db277, 0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, 0xe0d7848d, 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189, 0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, 0x674f9842, 0x7e54a903, 0x5579fac0, 0x4c62cb81, 0x8138c51f, 0x9823f45e, 0xb30ea79d, 0xaa1596dc, 0xe554001b, 0xfc4f315a, 0xd7626299, 0xce7953d8, 0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0, 0x5e7ef3ec, 0x4765c2ad, 0x6c48916e, 0x7553a02f, 0x3a1236e8, 0x230907a9, 0x0824546a, 0x113f652b, 0x96a779e4, 0x8fbc48a5, 0xa4911b66, 0xbd8a2a27, 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23, 0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, 0x70d024b9, 0x69cb15f8, 0x42e6463b, 0x5bfd777a, 0xdc656bb5, 0xc57e5af4, 0xee530937, 0xf7483876, 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, 0x9324fd72}, {0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x00000000, 0x96300777, 0x2c610eee, 0xba510999, 0x19c46d07, 0x8ff46a70, 0x35a563e9, 0xa395649e, 0x3288db0e, 0xa4b8dc79, 0x1ee9d5e0, 0x88d9d297, 0x2b4cb609, 0xbd7cb17e, 0x072db8e7, 0x911dbf90, 0x6410b71d, 0xf220b06a, 0x4871b9f3, 0xde41be84, 0x7dd4da1a, 0xebe4dd6d, 0x51b5d4f4, 0xc785d383, 0x56986c13, 0xc0a86b64, 0x7af962fd, 0xecc9658a, 0x4f5c0114, 0xd96c0663, 0x633d0ffa, 0xf50d088d, 0xc8206e3b, 0x5e10694c, 0xe44160d5, 0x727167a2, 0xd1e4033c, 0x47d4044b, 0xfd850dd2, 0x6bb50aa5, 0xfaa8b535, 0x6c98b242, 0xd6c9bbdb, 0x40f9bcac, 0xe36cd832, 0x755cdf45, 0xcf0dd6dc, 0x593dd1ab, 0xac30d926, 0x3a00de51, 0x8051d7c8, 0x1661d0bf, 0xb5f4b421, 0x23c4b356, 0x9995bacf, 0x0fa5bdb8, 0x9eb80228, 0x0888055f, 0xb2d90cc6, 0x24e90bb1, 0x877c6f2f, 0x114c6858, 0xab1d61c1, 0x3d2d66b6, 0x9041dc76, 0x0671db01, 0xbc20d298, 0x2a10d5ef, 0x8985b171, 0x1fb5b606, 0xa5e4bf9f, 0x33d4b8e8, 0xa2c90778, 0x34f9000f, 0x8ea80996, 0x18980ee1, 0xbb0d6a7f, 0x2d3d6d08, 0x976c6491, 0x015c63e6, 0xf4516b6b, 0x62616c1c, 0xd8306585, 0x4e0062f2, 0xed95066c, 0x7ba5011b, 0xc1f40882, 0x57c40ff5, 0xc6d9b065, 0x50e9b712, 0xeab8be8b, 0x7c88b9fc, 0xdf1ddd62, 0x492dda15, 0xf37cd38c, 0x654cd4fb, 0x5861b24d, 0xce51b53a, 0x7400bca3, 0xe230bbd4, 0x41a5df4a, 0xd795d83d, 0x6dc4d1a4, 0xfbf4d6d3, 0x6ae96943, 0xfcd96e34, 0x468867ad, 0xd0b860da, 0x732d0444, 0xe51d0333, 0x5f4c0aaa, 0xc97c0ddd, 0x3c710550, 0xaa410227, 0x10100bbe, 0x86200cc9, 0x25b56857, 0xb3856f20, 0x09d466b9, 0x9fe461ce, 0x0ef9de5e, 0x98c9d929, 0x2298d0b0, 0xb4a8d7c7, 0x173db359, 0x810db42e, 0x3b5cbdb7, 0xad6cbac0, 0x2083b8ed, 0xb6b3bf9a, 0x0ce2b603, 0x9ad2b174, 0x3947d5ea, 0xaf77d29d, 0x1526db04, 0x8316dc73, 0x120b63e3, 0x843b6494, 0x3e6a6d0d, 0xa85a6a7a, 0x0bcf0ee4, 0x9dff0993, 0x27ae000a, 0xb19e077d, 0x44930ff0, 0xd2a30887, 0x68f2011e, 0xfec20669, 0x5d5762f7, 0xcb676580, 0x71366c19, 0xe7066b6e, 0x761bd4fe, 0xe02bd389, 0x5a7ada10, 0xcc4add67, 0x6fdfb9f9, 0xf9efbe8e, 0x43beb717, 0xd58eb060, 0xe8a3d6d6, 0x7e93d1a1, 0xc4c2d838, 0x52f2df4f, 0xf167bbd1, 0x6757bca6, 0xdd06b53f, 0x4b36b248, 0xda2b0dd8, 0x4c1b0aaf, 0xf64a0336, 0x607a0441, 0xc3ef60df, 0x55df67a8, 0xef8e6e31, 0x79be6946, 0x8cb361cb, 0x1a8366bc, 0xa0d26f25, 0x36e26852, 0x95770ccc, 0x03470bbb, 0xb9160222, 0x2f260555, 0xbe3bbac5, 0x280bbdb2, 0x925ab42b, 0x046ab35c, 0xa7ffd7c2, 0x31cfd0b5, 0x8b9ed92c, 0x1daede5b, 0xb0c2649b, 0x26f263ec, 0x9ca36a75, 0x0a936d02, 0xa906099c, 0x3f360eeb, 0x85670772, 0x13570005, 0x824abf95, 0x147ab8e2, 0xae2bb17b, 0x381bb60c, 0x9b8ed292, 0x0dbed5e5, 0xb7efdc7c, 0x21dfdb0b, 0xd4d2d386, 0x42e2d4f1, 0xf8b3dd68, 0x6e83da1f, 0xcd16be81, 0x5b26b9f6, 0xe177b06f, 0x7747b718, 0xe65a0888, 0x706a0fff, 0xca3b0666, 0x5c0b0111, 0xff9e658f, 0x69ae62f8, 0xd3ff6b61, 0x45cf6c16, 0x78e20aa0, 0xeed20dd7, 0x5483044e, 0xc2b30339, 0x612667a7, 0xf71660d0, 0x4d476949, 0xdb776e3e, 0x4a6ad1ae, 0xdc5ad6d9, 0x660bdf40, 0xf03bd837, 0x53aebca9, 0xc59ebbde, 0x7fcfb247, 0xe9ffb530, 0x1cf2bdbd, 0x8ac2baca, 0x3093b353, 0xa6a3b424, 0x0536d0ba, 0x9306d7cd, 0x2957de54, 0xbf67d923, 0x2e7a66b3, 0xb84a61c4, 0x021b685d, 0x942b6f2a, 0x37be0bb4, 0xa18e0cc3, 0x1bdf055a, 0x8def022d}, {0x00000000, 0x41311b19, 0x82623632, 0xc3532d2b, 0x04c56c64, 0x45f4777d, 0x86a75a56, 0xc796414f, 0x088ad9c8, 0x49bbc2d1, 0x8ae8effa, 0xcbd9f4e3, 0x0c4fb5ac, 0x4d7eaeb5, 0x8e2d839e, 0xcf1c9887, 0x5112c24a, 0x1023d953, 0xd370f478, 0x9241ef61, 0x55d7ae2e, 0x14e6b537, 0xd7b5981c, 0x96848305, 0x59981b82, 0x18a9009b, 0xdbfa2db0, 0x9acb36a9, 0x5d5d77e6, 0x1c6c6cff, 0xdf3f41d4, 0x9e0e5acd, 0xa2248495, 0xe3159f8c, 0x2046b2a7, 0x6177a9be, 0xa6e1e8f1, 0xe7d0f3e8, 0x2483dec3, 0x65b2c5da, 0xaaae5d5d, 0xeb9f4644, 0x28cc6b6f, 0x69fd7076, 0xae6b3139, 0xef5a2a20, 0x2c09070b, 0x6d381c12, 0xf33646df, 0xb2075dc6, 0x715470ed, 0x30656bf4, 0xf7f32abb, 0xb6c231a2, 0x75911c89, 0x34a00790, 0xfbbc9f17, 0xba8d840e, 0x79dea925, 0x38efb23c, 0xff79f373, 0xbe48e86a, 0x7d1bc541, 0x3c2ade58, 0x054f79f0, 0x447e62e9, 0x872d4fc2, 0xc61c54db, 0x018a1594, 0x40bb0e8d, 0x83e823a6, 0xc2d938bf, 0x0dc5a038, 0x4cf4bb21, 0x8fa7960a, 0xce968d13, 0x0900cc5c, 0x4831d745, 0x8b62fa6e, 0xca53e177, 0x545dbbba, 0x156ca0a3, 0xd63f8d88, 0x970e9691, 0x5098d7de, 0x11a9ccc7, 0xd2fae1ec, 0x93cbfaf5, 0x5cd76272, 0x1de6796b, 0xdeb55440, 0x9f844f59, 0x58120e16, 0x1923150f, 0xda703824, 0x9b41233d, 0xa76bfd65, 0xe65ae67c, 0x2509cb57, 0x6438d04e, 0xa3ae9101, 0xe29f8a18, 0x21cca733, 0x60fdbc2a, 0xafe124ad, 0xeed03fb4, 0x2d83129f, 0x6cb20986, 0xab2448c9, 0xea1553d0, 0x29467efb, 0x687765e2, 0xf6793f2f, 0xb7482436, 0x741b091d, 0x352a1204, 0xf2bc534b, 0xb38d4852, 0x70de6579, 0x31ef7e60, 0xfef3e6e7, 0xbfc2fdfe, 0x7c91d0d5, 0x3da0cbcc, 0xfa368a83, 0xbb07919a, 0x7854bcb1, 0x3965a7a8, 0x4b98833b, 0x0aa99822, 0xc9fab509, 0x88cbae10, 0x4f5def5f, 0x0e6cf446, 0xcd3fd96d, 0x8c0ec274, 0x43125af3, 0x022341ea, 0xc1706cc1, 0x804177d8, 0x47d73697, 0x06e62d8e, 0xc5b500a5, 0x84841bbc, 0x1a8a4171, 0x5bbb5a68, 0x98e87743, 0xd9d96c5a, 0x1e4f2d15, 0x5f7e360c, 0x9c2d1b27, 0xdd1c003e, 0x120098b9, 0x533183a0, 0x9062ae8b, 0xd153b592, 0x16c5f4dd, 0x57f4efc4, 0x94a7c2ef, 0xd596d9f6, 0xe9bc07ae, 0xa88d1cb7, 0x6bde319c, 0x2aef2a85, 0xed796bca, 0xac4870d3, 0x6f1b5df8, 0x2e2a46e1, 0xe136de66, 0xa007c57f, 0x6354e854, 0x2265f34d, 0xe5f3b202, 0xa4c2a91b, 0x67918430, 0x26a09f29, 0xb8aec5e4, 0xf99fdefd, 0x3accf3d6, 0x7bfde8cf, 0xbc6ba980, 0xfd5ab299, 0x3e099fb2, 0x7f3884ab, 0xb0241c2c, 0xf1150735, 0x32462a1e, 0x73773107, 0xb4e17048, 0xf5d06b51, 0x3683467a, 0x77b25d63, 0x4ed7facb, 0x0fe6e1d2, 0xccb5ccf9, 0x8d84d7e0, 0x4a1296af, 0x0b238db6, 0xc870a09d, 0x8941bb84, 0x465d2303, 0x076c381a, 0xc43f1531, 0x850e0e28, 0x42984f67, 0x03a9547e, 0xc0fa7955, 0x81cb624c, 0x1fc53881, 0x5ef42398, 0x9da70eb3, 0xdc9615aa, 0x1b0054e5, 0x5a314ffc, 0x996262d7, 0xd85379ce, 0x174fe149, 0x567efa50, 0x952dd77b, 0xd41ccc62, 0x138a8d2d, 0x52bb9634, 0x91e8bb1f, 0xd0d9a006, 0xecf37e5e, 0xadc26547, 0x6e91486c, 0x2fa05375, 0xe836123a, 0xa9070923, 0x6a542408, 0x2b653f11, 0xe479a796, 0xa548bc8f, 0x661b91a4, 0x272a8abd, 0xe0bccbf2, 0xa18dd0eb, 0x62defdc0, 0x23efe6d9, 0xbde1bc14, 0xfcd0a70d, 0x3f838a26, 0x7eb2913f, 0xb924d070, 0xf815cb69, 0x3b46e642, 0x7a77fd5b, 0xb56b65dc, 0xf45a7ec5, 0x370953ee, 0x763848f7, 0xb1ae09b8, 0xf09f12a1, 0x33cc3f8a, 0x72fd2493}, {0x00000000, 0x376ac201, 0x6ed48403, 0x59be4602, 0xdca80907, 0xebc2cb06, 0xb27c8d04, 0x85164f05, 0xb851130e, 0x8f3bd10f, 0xd685970d, 0xe1ef550c, 0x64f91a09, 0x5393d808, 0x0a2d9e0a, 0x3d475c0b, 0x70a3261c, 0x47c9e41d, 0x1e77a21f, 0x291d601e, 0xac0b2f1b, 0x9b61ed1a, 0xc2dfab18, 0xf5b56919, 0xc8f23512, 0xff98f713, 0xa626b111, 0x914c7310, 0x145a3c15, 0x2330fe14, 0x7a8eb816, 0x4de47a17, 0xe0464d38, 0xd72c8f39, 0x8e92c93b, 0xb9f80b3a, 0x3cee443f, 0x0b84863e, 0x523ac03c, 0x6550023d, 0x58175e36, 0x6f7d9c37, 0x36c3da35, 0x01a91834, 0x84bf5731, 0xb3d59530, 0xea6bd332, 0xdd011133, 0x90e56b24, 0xa78fa925, 0xfe31ef27, 0xc95b2d26, 0x4c4d6223, 0x7b27a022, 0x2299e620, 0x15f32421, 0x28b4782a, 0x1fdeba2b, 0x4660fc29, 0x710a3e28, 0xf41c712d, 0xc376b32c, 0x9ac8f52e, 0xada2372f, 0xc08d9a70, 0xf7e75871, 0xae591e73, 0x9933dc72, 0x1c259377, 0x2b4f5176, 0x72f11774, 0x459bd575, 0x78dc897e, 0x4fb64b7f, 0x16080d7d, 0x2162cf7c, 0xa4748079, 0x931e4278, 0xcaa0047a, 0xfdcac67b, 0xb02ebc6c, 0x87447e6d, 0xdefa386f, 0xe990fa6e, 0x6c86b56b, 0x5bec776a, 0x02523168, 0x3538f369, 0x087faf62, 0x3f156d63, 0x66ab2b61, 0x51c1e960, 0xd4d7a665, 0xe3bd6464, 0xba032266, 0x8d69e067, 0x20cbd748, 0x17a11549, 0x4e1f534b, 0x7975914a, 0xfc63de4f, 0xcb091c4e, 0x92b75a4c, 0xa5dd984d, 0x989ac446, 0xaff00647, 0xf64e4045, 0xc1248244, 0x4432cd41, 0x73580f40, 0x2ae64942, 0x1d8c8b43, 0x5068f154, 0x67023355, 0x3ebc7557, 0x09d6b756, 0x8cc0f853, 0xbbaa3a52, 0xe2147c50, 0xd57ebe51, 0xe839e25a, 0xdf53205b, 0x86ed6659, 0xb187a458, 0x3491eb5d, 0x03fb295c, 0x5a456f5e, 0x6d2fad5f, 0x801b35e1, 0xb771f7e0, 0xeecfb1e2, 0xd9a573e3, 0x5cb33ce6, 0x6bd9fee7, 0x3267b8e5, 0x050d7ae4, 0x384a26ef, 0x0f20e4ee, 0x569ea2ec, 0x61f460ed, 0xe4e22fe8, 0xd388ede9, 0x8a36abeb, 0xbd5c69ea, 0xf0b813fd, 0xc7d2d1fc, 0x9e6c97fe, 0xa90655ff, 0x2c101afa, 0x1b7ad8fb, 0x42c49ef9, 0x75ae5cf8, 0x48e900f3, 0x7f83c2f2, 0x263d84f0, 0x115746f1, 0x944109f4, 0xa32bcbf5, 0xfa958df7, 0xcdff4ff6, 0x605d78d9, 0x5737bad8, 0x0e89fcda, 0x39e33edb, 0xbcf571de, 0x8b9fb3df, 0xd221f5dd, 0xe54b37dc, 0xd80c6bd7, 0xef66a9d6, 0xb6d8efd4, 0x81b22dd5, 0x04a462d0, 0x33cea0d1, 0x6a70e6d3, 0x5d1a24d2, 0x10fe5ec5, 0x27949cc4, 0x7e2adac6, 0x494018c7, 0xcc5657c2, 0xfb3c95c3, 0xa282d3c1, 0x95e811c0, 0xa8af4dcb, 0x9fc58fca, 0xc67bc9c8, 0xf1110bc9, 0x740744cc, 0x436d86cd, 0x1ad3c0cf, 0x2db902ce, 0x4096af91, 0x77fc6d90, 0x2e422b92, 0x1928e993, 0x9c3ea696, 0xab546497, 0xf2ea2295, 0xc580e094, 0xf8c7bc9f, 0xcfad7e9e, 0x9613389c, 0xa179fa9d, 0x246fb598, 0x13057799, 0x4abb319b, 0x7dd1f39a, 0x3035898d, 0x075f4b8c, 0x5ee10d8e, 0x698bcf8f, 0xec9d808a, 0xdbf7428b, 0x82490489, 0xb523c688, 0x88649a83, 0xbf0e5882, 0xe6b01e80, 0xd1dadc81, 0x54cc9384, 0x63a65185, 0x3a181787, 0x0d72d586, 0xa0d0e2a9, 0x97ba20a8, 0xce0466aa, 0xf96ea4ab, 0x7c78ebae, 0x4b1229af, 0x12ac6fad, 0x25c6adac, 0x1881f1a7, 0x2feb33a6, 0x765575a4, 0x413fb7a5, 0xc429f8a0, 0xf3433aa1, 0xaafd7ca3, 0x9d97bea2, 0xd073c4b5, 0xe71906b4, 0xbea740b6, 0x89cd82b7, 0x0cdbcdb2, 0x3bb10fb3, 0x620f49b1, 0x55658bb0, 0x6822d7bb, 0x5f4815ba, 0x06f653b8, 0x319c91b9, 0xb48adebc, 0x83e01cbd, 0xda5e5abf, 0xed3498be}, {0x00000000, 0x6567bcb8, 0x8bc809aa, 0xeeafb512, 0x5797628f, 0x32f0de37, 0xdc5f6b25, 0xb938d79d, 0xef28b4c5, 0x8a4f087d, 0x64e0bd6f, 0x018701d7, 0xb8bfd64a, 0xddd86af2, 0x3377dfe0, 0x56106358, 0x9f571950, 0xfa30a5e8, 0x149f10fa, 0x71f8ac42, 0xc8c07bdf, 0xada7c767, 0x43087275, 0x266fcecd, 0x707fad95, 0x1518112d, 0xfbb7a43f, 0x9ed01887, 0x27e8cf1a, 0x428f73a2, 0xac20c6b0, 0xc9477a08, 0x3eaf32a0, 0x5bc88e18, 0xb5673b0a, 0xd00087b2, 0x6938502f, 0x0c5fec97, 0xe2f05985, 0x8797e53d, 0xd1878665, 0xb4e03add, 0x5a4f8fcf, 0x3f283377, 0x8610e4ea, 0xe3775852, 0x0dd8ed40, 0x68bf51f8, 0xa1f82bf0, 0xc49f9748, 0x2a30225a, 0x4f579ee2, 0xf66f497f, 0x9308f5c7, 0x7da740d5, 0x18c0fc6d, 0x4ed09f35, 0x2bb7238d, 0xc518969f, 0xa07f2a27, 0x1947fdba, 0x7c204102, 0x928ff410, 0xf7e848a8, 0x3d58149b, 0x583fa823, 0xb6901d31, 0xd3f7a189, 0x6acf7614, 0x0fa8caac, 0xe1077fbe, 0x8460c306, 0xd270a05e, 0xb7171ce6, 0x59b8a9f4, 0x3cdf154c, 0x85e7c2d1, 0xe0807e69, 0x0e2fcb7b, 0x6b4877c3, 0xa20f0dcb, 0xc768b173, 0x29c70461, 0x4ca0b8d9, 0xf5986f44, 0x90ffd3fc, 0x7e5066ee, 0x1b37da56, 0x4d27b90e, 0x284005b6, 0xc6efb0a4, 0xa3880c1c, 0x1ab0db81, 0x7fd76739, 0x9178d22b, 0xf41f6e93, 0x03f7263b, 0x66909a83, 0x883f2f91, 0xed589329, 0x546044b4, 0x3107f80c, 0xdfa84d1e, 0xbacff1a6, 0xecdf92fe, 0x89b82e46, 0x67179b54, 0x027027ec, 0xbb48f071, 0xde2f4cc9, 0x3080f9db, 0x55e74563, 0x9ca03f6b, 0xf9c783d3, 0x176836c1, 0x720f8a79, 0xcb375de4, 0xae50e15c, 0x40ff544e, 0x2598e8f6, 0x73888bae, 0x16ef3716, 0xf8408204, 0x9d273ebc, 0x241fe921, 0x41785599, 0xafd7e08b, 0xcab05c33, 0x3bb659ed, 0x5ed1e555, 0xb07e5047, 0xd519ecff, 0x6c213b62, 0x094687da, 0xe7e932c8, 0x828e8e70, 0xd49eed28, 0xb1f95190, 0x5f56e482, 0x3a31583a, 0x83098fa7, 0xe66e331f, 0x08c1860d, 0x6da63ab5, 0xa4e140bd, 0xc186fc05, 0x2f294917, 0x4a4ef5af, 0xf3762232, 0x96119e8a, 0x78be2b98, 0x1dd99720, 0x4bc9f478, 0x2eae48c0, 0xc001fdd2, 0xa566416a, 0x1c5e96f7, 0x79392a4f, 0x97969f5d, 0xf2f123e5, 0x05196b4d, 0x607ed7f5, 0x8ed162e7, 0xebb6de5f, 0x528e09c2, 0x37e9b57a, 0xd9460068, 0xbc21bcd0, 0xea31df88, 0x8f566330, 0x61f9d622, 0x049e6a9a, 0xbda6bd07, 0xd8c101bf, 0x366eb4ad, 0x53090815, 0x9a4e721d, 0xff29cea5, 0x11867bb7, 0x74e1c70f, 0xcdd91092, 0xa8beac2a, 0x46111938, 0x2376a580, 0x7566c6d8, 0x10017a60, 0xfeaecf72, 0x9bc973ca, 0x22f1a457, 0x479618ef, 0xa939adfd, 0xcc5e1145, 0x06ee4d76, 0x6389f1ce, 0x8d2644dc, 0xe841f864, 0x51792ff9, 0x341e9341, 0xdab12653, 0xbfd69aeb, 0xe9c6f9b3, 0x8ca1450b, 0x620ef019, 0x07694ca1, 0xbe519b3c, 0xdb362784, 0x35999296, 0x50fe2e2e, 0x99b95426, 0xfcdee89e, 0x12715d8c, 0x7716e134, 0xce2e36a9, 0xab498a11, 0x45e63f03, 0x208183bb, 0x7691e0e3, 0x13f65c5b, 0xfd59e949, 0x983e55f1, 0x2106826c, 0x44613ed4, 0xaace8bc6, 0xcfa9377e, 0x38417fd6, 0x5d26c36e, 0xb389767c, 0xd6eecac4, 0x6fd61d59, 0x0ab1a1e1, 0xe41e14f3, 0x8179a84b, 0xd769cb13, 0xb20e77ab, 0x5ca1c2b9, 0x39c67e01, 0x80fea99c, 0xe5991524, 0x0b36a036, 0x6e511c8e, 0xa7166686, 0xc271da3e, 0x2cde6f2c, 0x49b9d394, 0xf0810409, 0x95e6b8b1, 0x7b490da3, 0x1e2eb11b, 0x483ed243, 0x2d596efb, 0xc3f6dbe9, 0xa6916751, 0x1fa9b0cc, 0x7ace0c74, 0x9461b966, 0xf10605de}}; #endif #endif #if N == 2 #if W == 8 local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0xae689191, 0x87a02563, 0x29c8b4f2, 0xd4314c87, 0x7a59dd16, 0x539169e4, 0xfdf9f875, 0x73139f4f, 0xdd7b0ede, 0xf4b3ba2c, 0x5adb2bbd, 0xa722d3c8, 0x094a4259, 0x2082f6ab, 0x8eea673a, 0xe6273e9e, 0x484faf0f, 0x61871bfd, 0xcfef8a6c, 0x32167219, 0x9c7ee388, 0xb5b6577a, 0x1bdec6eb, 0x9534a1d1, 0x3b5c3040, 0x129484b2, 0xbcfc1523, 0x4105ed56, 0xef6d7cc7, 0xc6a5c835, 0x68cd59a4, 0x173f7b7d, 0xb957eaec, 0x909f5e1e, 0x3ef7cf8f, 0xc30e37fa, 0x6d66a66b, 0x44ae1299, 0xeac68308, 0x642ce432, 0xca4475a3, 0xe38cc151, 0x4de450c0, 0xb01da8b5, 0x1e753924, 0x37bd8dd6, 0x99d51c47, 0xf11845e3, 0x5f70d472, 0x76b86080, 0xd8d0f111, 0x25290964, 0x8b4198f5, 0xa2892c07, 0x0ce1bd96, 0x820bdaac, 0x2c634b3d, 0x05abffcf, 0xabc36e5e, 0x563a962b, 0xf85207ba, 0xd19ab348, 0x7ff222d9, 0x2e7ef6fa, 0x8016676b, 0xa9ded399, 0x07b64208, 0xfa4fba7d, 0x54272bec, 0x7def9f1e, 0xd3870e8f, 0x5d6d69b5, 0xf305f824, 0xdacd4cd6, 0x74a5dd47, 0x895c2532, 0x2734b4a3, 0x0efc0051, 0xa09491c0, 0xc859c864, 0x663159f5, 0x4ff9ed07, 0xe1917c96, 0x1c6884e3, 0xb2001572, 0x9bc8a180, 0x35a03011, 0xbb4a572b, 0x1522c6ba, 0x3cea7248, 0x9282e3d9, 0x6f7b1bac, 0xc1138a3d, 0xe8db3ecf, 0x46b3af5e, 0x39418d87, 0x97291c16, 0xbee1a8e4, 0x10893975, 0xed70c100, 0x43185091, 0x6ad0e463, 0xc4b875f2, 0x4a5212c8, 0xe43a8359, 0xcdf237ab, 0x639aa63a, 0x9e635e4f, 0x300bcfde, 0x19c37b2c, 0xb7abeabd, 0xdf66b319, 0x710e2288, 0x58c6967a, 0xf6ae07eb, 0x0b57ff9e, 0xa53f6e0f, 0x8cf7dafd, 0x229f4b6c, 0xac752c56, 0x021dbdc7, 0x2bd50935, 0x85bd98a4, 0x784460d1, 0xd62cf140, 0xffe445b2, 0x518cd423, 0x5cfdedf4, 0xf2957c65, 0xdb5dc897, 0x75355906, 0x88cca173, 0x26a430e2, 0x0f6c8410, 0xa1041581, 0x2fee72bb, 0x8186e32a, 0xa84e57d8, 0x0626c649, 0xfbdf3e3c, 0x55b7afad, 0x7c7f1b5f, 0xd2178ace, 0xbadad36a, 0x14b242fb, 0x3d7af609, 0x93126798, 0x6eeb9fed, 0xc0830e7c, 0xe94bba8e, 0x47232b1f, 0xc9c94c25, 0x67a1ddb4, 0x4e696946, 0xe001f8d7, 0x1df800a2, 0xb3909133, 0x9a5825c1, 0x3430b450, 0x4bc29689, 0xe5aa0718, 0xcc62b3ea, 0x620a227b, 0x9ff3da0e, 0x319b4b9f, 0x1853ff6d, 0xb63b6efc, 0x38d109c6, 0x96b99857, 0xbf712ca5, 0x1119bd34, 0xece04541, 0x4288d4d0, 0x6b406022, 0xc528f1b3, 0xade5a817, 0x038d3986, 0x2a458d74, 0x842d1ce5, 0x79d4e490, 0xd7bc7501, 0xfe74c1f3, 0x501c5062, 0xdef63758, 0x709ea6c9, 0x5956123b, 0xf73e83aa, 0x0ac77bdf, 0xa4afea4e, 0x8d675ebc, 0x230fcf2d, 0x72831b0e, 0xdceb8a9f, 0xf5233e6d, 0x5b4baffc, 0xa6b25789, 0x08dac618, 0x211272ea, 0x8f7ae37b, 0x01908441, 0xaff815d0, 0x8630a122, 0x285830b3, 0xd5a1c8c6, 0x7bc95957, 0x5201eda5, 0xfc697c34, 0x94a42590, 0x3accb401, 0x130400f3, 0xbd6c9162, 0x40956917, 0xeefdf886, 0xc7354c74, 0x695ddde5, 0xe7b7badf, 0x49df2b4e, 0x60179fbc, 0xce7f0e2d, 0x3386f658, 0x9dee67c9, 0xb426d33b, 0x1a4e42aa, 0x65bc6073, 0xcbd4f1e2, 0xe21c4510, 0x4c74d481, 0xb18d2cf4, 0x1fe5bd65, 0x362d0997, 0x98459806, 0x16afff3c, 0xb8c76ead, 0x910fda5f, 0x3f674bce, 0xc29eb3bb, 0x6cf6222a, 0x453e96d8, 0xeb560749, 0x839b5eed, 0x2df3cf7c, 0x043b7b8e, 0xaa53ea1f, 0x57aa126a, 0xf9c283fb, 0xd00a3709, 0x7e62a698, 0xf088c1a2, 0x5ee05033, 0x7728e4c1, 0xd9407550, 0x24b98d25, 0x8ad11cb4, 0xa319a846, 0x0d7139d7}, {0x00000000, 0xb9fbdbe8, 0xa886b191, 0x117d6a79, 0x8a7c6563, 0x3387be8b, 0x22fad4f2, 0x9b010f1a, 0xcf89cc87, 0x7672176f, 0x670f7d16, 0xdef4a6fe, 0x45f5a9e4, 0xfc0e720c, 0xed731875, 0x5488c39d, 0x44629f4f, 0xfd9944a7, 0xece42ede, 0x551ff536, 0xce1efa2c, 0x77e521c4, 0x66984bbd, 0xdf639055, 0x8beb53c8, 0x32108820, 0x236de259, 0x9a9639b1, 0x019736ab, 0xb86ced43, 0xa911873a, 0x10ea5cd2, 0x88c53e9e, 0x313ee576, 0x20438f0f, 0x99b854e7, 0x02b95bfd, 0xbb428015, 0xaa3fea6c, 0x13c43184, 0x474cf219, 0xfeb729f1, 0xefca4388, 0x56319860, 0xcd30977a, 0x74cb4c92, 0x65b626eb, 0xdc4dfd03, 0xcca7a1d1, 0x755c7a39, 0x64211040, 0xdddacba8, 0x46dbc4b2, 0xff201f5a, 0xee5d7523, 0x57a6aecb, 0x032e6d56, 0xbad5b6be, 0xaba8dcc7, 0x1253072f, 0x89520835, 0x30a9d3dd, 0x21d4b9a4, 0x982f624c, 0xcafb7b7d, 0x7300a095, 0x627dcaec, 0xdb861104, 0x40871e1e, 0xf97cc5f6, 0xe801af8f, 0x51fa7467, 0x0572b7fa, 0xbc896c12, 0xadf4066b, 0x140fdd83, 0x8f0ed299, 0x36f50971, 0x27886308, 0x9e73b8e0, 0x8e99e432, 0x37623fda, 0x261f55a3, 0x9fe48e4b, 0x04e58151, 0xbd1e5ab9, 0xac6330c0, 0x1598eb28, 0x411028b5, 0xf8ebf35d, 0xe9969924, 0x506d42cc, 0xcb6c4dd6, 0x7297963e, 0x63eafc47, 0xda1127af, 0x423e45e3, 0xfbc59e0b, 0xeab8f472, 0x53432f9a, 0xc8422080, 0x71b9fb68, 0x60c49111, 0xd93f4af9, 0x8db78964, 0x344c528c, 0x253138f5, 0x9ccae31d, 0x07cbec07, 0xbe3037ef, 0xaf4d5d96, 0x16b6867e, 0x065cdaac, 0xbfa70144, 0xaeda6b3d, 0x1721b0d5, 0x8c20bfcf, 0x35db6427, 0x24a60e5e, 0x9d5dd5b6, 0xc9d5162b, 0x702ecdc3, 0x6153a7ba, 0xd8a87c52, 0x43a97348, 0xfa52a8a0, 0xeb2fc2d9, 0x52d41931, 0x4e87f0bb, 0xf77c2b53, 0xe601412a, 0x5ffa9ac2, 0xc4fb95d8, 0x7d004e30, 0x6c7d2449, 0xd586ffa1, 0x810e3c3c, 0x38f5e7d4, 0x29888dad, 0x90735645, 0x0b72595f, 0xb28982b7, 0xa3f4e8ce, 0x1a0f3326, 0x0ae56ff4, 0xb31eb41c, 0xa263de65, 0x1b98058d, 0x80990a97, 0x3962d17f, 0x281fbb06, 0x91e460ee, 0xc56ca373, 0x7c97789b, 0x6dea12e2, 0xd411c90a, 0x4f10c610, 0xf6eb1df8, 0xe7967781, 0x5e6dac69, 0xc642ce25, 0x7fb915cd, 0x6ec47fb4, 0xd73fa45c, 0x4c3eab46, 0xf5c570ae, 0xe4b81ad7, 0x5d43c13f, 0x09cb02a2, 0xb030d94a, 0xa14db333, 0x18b668db, 0x83b767c1, 0x3a4cbc29, 0x2b31d650, 0x92ca0db8, 0x8220516a, 0x3bdb8a82, 0x2aa6e0fb, 0x935d3b13, 0x085c3409, 0xb1a7efe1, 0xa0da8598, 0x19215e70, 0x4da99ded, 0xf4524605, 0xe52f2c7c, 0x5cd4f794, 0xc7d5f88e, 0x7e2e2366, 0x6f53491f, 0xd6a892f7, 0x847c8bc6, 0x3d87502e, 0x2cfa3a57, 0x9501e1bf, 0x0e00eea5, 0xb7fb354d, 0xa6865f34, 0x1f7d84dc, 0x4bf54741, 0xf20e9ca9, 0xe373f6d0, 0x5a882d38, 0xc1892222, 0x7872f9ca, 0x690f93b3, 0xd0f4485b, 0xc01e1489, 0x79e5cf61, 0x6898a518, 0xd1637ef0, 0x4a6271ea, 0xf399aa02, 0xe2e4c07b, 0x5b1f1b93, 0x0f97d80e, 0xb66c03e6, 0xa711699f, 0x1eeab277, 0x85ebbd6d, 0x3c106685, 0x2d6d0cfc, 0x9496d714, 0x0cb9b558, 0xb5426eb0, 0xa43f04c9, 0x1dc4df21, 0x86c5d03b, 0x3f3e0bd3, 0x2e4361aa, 0x97b8ba42, 0xc33079df, 0x7acba237, 0x6bb6c84e, 0xd24d13a6, 0x494c1cbc, 0xf0b7c754, 0xe1caad2d, 0x583176c5, 0x48db2a17, 0xf120f1ff, 0xe05d9b86, 0x59a6406e, 0xc2a74f74, 0x7b5c949c, 0x6a21fee5, 0xd3da250d, 0x8752e690, 0x3ea93d78, 0x2fd45701, 0x962f8ce9, 0x0d2e83f3, 0xb4d5581b, 0xa5a83262, 0x1c53e98a}, {0x00000000, 0x9d0fe176, 0xe16ec4ad, 0x7c6125db, 0x19ac8f1b, 0x84a36e6d, 0xf8c24bb6, 0x65cdaac0, 0x33591e36, 0xae56ff40, 0xd237da9b, 0x4f383bed, 0x2af5912d, 0xb7fa705b, 0xcb9b5580, 0x5694b4f6, 0x66b23c6c, 0xfbbddd1a, 0x87dcf8c1, 0x1ad319b7, 0x7f1eb377, 0xe2115201, 0x9e7077da, 0x037f96ac, 0x55eb225a, 0xc8e4c32c, 0xb485e6f7, 0x298a0781, 0x4c47ad41, 0xd1484c37, 0xad2969ec, 0x3026889a, 0xcd6478d8, 0x506b99ae, 0x2c0abc75, 0xb1055d03, 0xd4c8f7c3, 0x49c716b5, 0x35a6336e, 0xa8a9d218, 0xfe3d66ee, 0x63328798, 0x1f53a243, 0x825c4335, 0xe791e9f5, 0x7a9e0883, 0x06ff2d58, 0x9bf0cc2e, 0xabd644b4, 0x36d9a5c2, 0x4ab88019, 0xd7b7616f, 0xb27acbaf, 0x2f752ad9, 0x53140f02, 0xce1bee74, 0x988f5a82, 0x0580bbf4, 0x79e19e2f, 0xe4ee7f59, 0x8123d599, 0x1c2c34ef, 0x604d1134, 0xfd42f042, 0x41b9f7f1, 0xdcb61687, 0xa0d7335c, 0x3dd8d22a, 0x581578ea, 0xc51a999c, 0xb97bbc47, 0x24745d31, 0x72e0e9c7, 0xefef08b1, 0x938e2d6a, 0x0e81cc1c, 0x6b4c66dc, 0xf64387aa, 0x8a22a271, 0x172d4307, 0x270bcb9d, 0xba042aeb, 0xc6650f30, 0x5b6aee46, 0x3ea74486, 0xa3a8a5f0, 0xdfc9802b, 0x42c6615d, 0x1452d5ab, 0x895d34dd, 0xf53c1106, 0x6833f070, 0x0dfe5ab0, 0x90f1bbc6, 0xec909e1d, 0x719f7f6b, 0x8cdd8f29, 0x11d26e5f, 0x6db34b84, 0xf0bcaaf2, 0x95710032, 0x087ee144, 0x741fc49f, 0xe91025e9, 0xbf84911f, 0x228b7069, 0x5eea55b2, 0xc3e5b4c4, 0xa6281e04, 0x3b27ff72, 0x4746daa9, 0xda493bdf, 0xea6fb345, 0x77605233, 0x0b0177e8, 0x960e969e, 0xf3c33c5e, 0x6eccdd28, 0x12adf8f3, 0x8fa21985, 0xd936ad73, 0x44394c05, 0x385869de, 0xa55788a8, 0xc09a2268, 0x5d95c31e, 0x21f4e6c5, 0xbcfb07b3, 0x8373efe2, 0x1e7c0e94, 0x621d2b4f, 0xff12ca39, 0x9adf60f9, 0x07d0818f, 0x7bb1a454, 0xe6be4522, 0xb02af1d4, 0x2d2510a2, 0x51443579, 0xcc4bd40f, 0xa9867ecf, 0x34899fb9, 0x48e8ba62, 0xd5e75b14, 0xe5c1d38e, 0x78ce32f8, 0x04af1723, 0x99a0f655, 0xfc6d5c95, 0x6162bde3, 0x1d039838, 0x800c794e, 0xd698cdb8, 0x4b972cce, 0x37f60915, 0xaaf9e863, 0xcf3442a3, 0x523ba3d5, 0x2e5a860e, 0xb3556778, 0x4e17973a, 0xd318764c, 0xaf795397, 0x3276b2e1, 0x57bb1821, 0xcab4f957, 0xb6d5dc8c, 0x2bda3dfa, 0x7d4e890c, 0xe041687a, 0x9c204da1, 0x012facd7, 0x64e20617, 0xf9ede761, 0x858cc2ba, 0x188323cc, 0x28a5ab56, 0xb5aa4a20, 0xc9cb6ffb, 0x54c48e8d, 0x3109244d, 0xac06c53b, 0xd067e0e0, 0x4d680196, 0x1bfcb560, 0x86f35416, 0xfa9271cd, 0x679d90bb, 0x02503a7b, 0x9f5fdb0d, 0xe33efed6, 0x7e311fa0, 0xc2ca1813, 0x5fc5f965, 0x23a4dcbe, 0xbeab3dc8, 0xdb669708, 0x4669767e, 0x3a0853a5, 0xa707b2d3, 0xf1930625, 0x6c9ce753, 0x10fdc288, 0x8df223fe, 0xe83f893e, 0x75306848, 0x09514d93, 0x945eace5, 0xa478247f, 0x3977c509, 0x4516e0d2, 0xd81901a4, 0xbdd4ab64, 0x20db4a12, 0x5cba6fc9, 0xc1b58ebf, 0x97213a49, 0x0a2edb3f, 0x764ffee4, 0xeb401f92, 0x8e8db552, 0x13825424, 0x6fe371ff, 0xf2ec9089, 0x0fae60cb, 0x92a181bd, 0xeec0a466, 0x73cf4510, 0x1602efd0, 0x8b0d0ea6, 0xf76c2b7d, 0x6a63ca0b, 0x3cf77efd, 0xa1f89f8b, 0xdd99ba50, 0x40965b26, 0x255bf1e6, 0xb8541090, 0xc435354b, 0x593ad43d, 0x691c5ca7, 0xf413bdd1, 0x8872980a, 0x157d797c, 0x70b0d3bc, 0xedbf32ca, 0x91de1711, 0x0cd1f667, 0x5a454291, 0xc74aa3e7, 0xbb2b863c, 0x2624674a, 0x43e9cd8a, 0xdee62cfc, 0xa2870927, 0x3f88e851}, {0x00000000, 0xdd96d985, 0x605cb54b, 0xbdca6cce, 0xc0b96a96, 0x1d2fb313, 0xa0e5dfdd, 0x7d730658, 0x5a03d36d, 0x87950ae8, 0x3a5f6626, 0xe7c9bfa3, 0x9abab9fb, 0x472c607e, 0xfae60cb0, 0x2770d535, 0xb407a6da, 0x69917f5f, 0xd45b1391, 0x09cdca14, 0x74becc4c, 0xa92815c9, 0x14e27907, 0xc974a082, 0xee0475b7, 0x3392ac32, 0x8e58c0fc, 0x53ce1979, 0x2ebd1f21, 0xf32bc6a4, 0x4ee1aa6a, 0x937773ef, 0xb37e4bf5, 0x6ee89270, 0xd322febe, 0x0eb4273b, 0x73c72163, 0xae51f8e6, 0x139b9428, 0xce0d4dad, 0xe97d9898, 0x34eb411d, 0x89212dd3, 0x54b7f456, 0x29c4f20e, 0xf4522b8b, 0x49984745, 0x940e9ec0, 0x0779ed2f, 0xdaef34aa, 0x67255864, 0xbab381e1, 0xc7c087b9, 0x1a565e3c, 0xa79c32f2, 0x7a0aeb77, 0x5d7a3e42, 0x80ece7c7, 0x3d268b09, 0xe0b0528c, 0x9dc354d4, 0x40558d51, 0xfd9fe19f, 0x2009381a, 0xbd8d91ab, 0x601b482e, 0xddd124e0, 0x0047fd65, 0x7d34fb3d, 0xa0a222b8, 0x1d684e76, 0xc0fe97f3, 0xe78e42c6, 0x3a189b43, 0x87d2f78d, 0x5a442e08, 0x27372850, 0xfaa1f1d5, 0x476b9d1b, 0x9afd449e, 0x098a3771, 0xd41ceef4, 0x69d6823a, 0xb4405bbf, 0xc9335de7, 0x14a58462, 0xa96fe8ac, 0x74f93129, 0x5389e41c, 0x8e1f3d99, 0x33d55157, 0xee4388d2, 0x93308e8a, 0x4ea6570f, 0xf36c3bc1, 0x2efae244, 0x0ef3da5e, 0xd36503db, 0x6eaf6f15, 0xb339b690, 0xce4ab0c8, 0x13dc694d, 0xae160583, 0x7380dc06, 0x54f00933, 0x8966d0b6, 0x34acbc78, 0xe93a65fd, 0x944963a5, 0x49dfba20, 0xf415d6ee, 0x29830f6b, 0xbaf47c84, 0x6762a501, 0xdaa8c9cf, 0x073e104a, 0x7a4d1612, 0xa7dbcf97, 0x1a11a359, 0xc7877adc, 0xe0f7afe9, 0x3d61766c, 0x80ab1aa2, 0x5d3dc327, 0x204ec57f, 0xfdd81cfa, 0x40127034, 0x9d84a9b1, 0xa06a2517, 0x7dfcfc92, 0xc036905c, 0x1da049d9, 0x60d34f81, 0xbd459604, 0x008ffaca, 0xdd19234f, 0xfa69f67a, 0x27ff2fff, 0x9a354331, 0x47a39ab4, 0x3ad09cec, 0xe7464569, 0x5a8c29a7, 0x871af022, 0x146d83cd, 0xc9fb5a48, 0x74313686, 0xa9a7ef03, 0xd4d4e95b, 0x094230de, 0xb4885c10, 0x691e8595, 0x4e6e50a0, 0x93f88925, 0x2e32e5eb, 0xf3a43c6e, 0x8ed73a36, 0x5341e3b3, 0xee8b8f7d, 0x331d56f8, 0x13146ee2, 0xce82b767, 0x7348dba9, 0xaede022c, 0xd3ad0474, 0x0e3bddf1, 0xb3f1b13f, 0x6e6768ba, 0x4917bd8f, 0x9481640a, 0x294b08c4, 0xf4ddd141, 0x89aed719, 0x54380e9c, 0xe9f26252, 0x3464bbd7, 0xa713c838, 0x7a8511bd, 0xc74f7d73, 0x1ad9a4f6, 0x67aaa2ae, 0xba3c7b2b, 0x07f617e5, 0xda60ce60, 0xfd101b55, 0x2086c2d0, 0x9d4cae1e, 0x40da779b, 0x3da971c3, 0xe03fa846, 0x5df5c488, 0x80631d0d, 0x1de7b4bc, 0xc0716d39, 0x7dbb01f7, 0xa02dd872, 0xdd5ede2a, 0x00c807af, 0xbd026b61, 0x6094b2e4, 0x47e467d1, 0x9a72be54, 0x27b8d29a, 0xfa2e0b1f, 0x875d0d47, 0x5acbd4c2, 0xe701b80c, 0x3a976189, 0xa9e01266, 0x7476cbe3, 0xc9bca72d, 0x142a7ea8, 0x695978f0, 0xb4cfa175, 0x0905cdbb, 0xd493143e, 0xf3e3c10b, 0x2e75188e, 0x93bf7440, 0x4e29adc5, 0x335aab9d, 0xeecc7218, 0x53061ed6, 0x8e90c753, 0xae99ff49, 0x730f26cc, 0xcec54a02, 0x13539387, 0x6e2095df, 0xb3b64c5a, 0x0e7c2094, 0xd3eaf911, 0xf49a2c24, 0x290cf5a1, 0x94c6996f, 0x495040ea, 0x342346b2, 0xe9b59f37, 0x547ff3f9, 0x89e92a7c, 0x1a9e5993, 0xc7088016, 0x7ac2ecd8, 0xa754355d, 0xda273305, 0x07b1ea80, 0xba7b864e, 0x67ed5fcb, 0x409d8afe, 0x9d0b537b, 0x20c13fb5, 0xfd57e630, 0x8024e068, 0x5db239ed, 0xe0785523, 0x3dee8ca6}, {0x00000000, 0x9ba54c6f, 0xec3b9e9f, 0x779ed2f0, 0x03063b7f, 0x98a37710, 0xef3da5e0, 0x7498e98f, 0x060c76fe, 0x9da93a91, 0xea37e861, 0x7192a40e, 0x050a4d81, 0x9eaf01ee, 0xe931d31e, 0x72949f71, 0x0c18edfc, 0x97bda193, 0xe0237363, 0x7b863f0c, 0x0f1ed683, 0x94bb9aec, 0xe325481c, 0x78800473, 0x0a149b02, 0x91b1d76d, 0xe62f059d, 0x7d8a49f2, 0x0912a07d, 0x92b7ec12, 0xe5293ee2, 0x7e8c728d, 0x1831dbf8, 0x83949797, 0xf40a4567, 0x6faf0908, 0x1b37e087, 0x8092ace8, 0xf70c7e18, 0x6ca93277, 0x1e3dad06, 0x8598e169, 0xf2063399, 0x69a37ff6, 0x1d3b9679, 0x869eda16, 0xf10008e6, 0x6aa54489, 0x14293604, 0x8f8c7a6b, 0xf812a89b, 0x63b7e4f4, 0x172f0d7b, 0x8c8a4114, 0xfb1493e4, 0x60b1df8b, 0x122540fa, 0x89800c95, 0xfe1ede65, 0x65bb920a, 0x11237b85, 0x8a8637ea, 0xfd18e51a, 0x66bda975, 0x3063b7f0, 0xabc6fb9f, 0xdc58296f, 0x47fd6500, 0x33658c8f, 0xa8c0c0e0, 0xdf5e1210, 0x44fb5e7f, 0x366fc10e, 0xadca8d61, 0xda545f91, 0x41f113fe, 0x3569fa71, 0xaeccb61e, 0xd95264ee, 0x42f72881, 0x3c7b5a0c, 0xa7de1663, 0xd040c493, 0x4be588fc, 0x3f7d6173, 0xa4d82d1c, 0xd346ffec, 0x48e3b383, 0x3a772cf2, 0xa1d2609d, 0xd64cb26d, 0x4de9fe02, 0x3971178d, 0xa2d45be2, 0xd54a8912, 0x4eefc57d, 0x28526c08, 0xb3f72067, 0xc469f297, 0x5fccbef8, 0x2b545777, 0xb0f11b18, 0xc76fc9e8, 0x5cca8587, 0x2e5e1af6, 0xb5fb5699, 0xc2658469, 0x59c0c806, 0x2d582189, 0xb6fd6de6, 0xc163bf16, 0x5ac6f379, 0x244a81f4, 0xbfefcd9b, 0xc8711f6b, 0x53d45304, 0x274cba8b, 0xbce9f6e4, 0xcb772414, 0x50d2687b, 0x2246f70a, 0xb9e3bb65, 0xce7d6995, 0x55d825fa, 0x2140cc75, 0xbae5801a, 0xcd7b52ea, 0x56de1e85, 0x60c76fe0, 0xfb62238f, 0x8cfcf17f, 0x1759bd10, 0x63c1549f, 0xf86418f0, 0x8ffaca00, 0x145f866f, 0x66cb191e, 0xfd6e5571, 0x8af08781, 0x1155cbee, 0x65cd2261, 0xfe686e0e, 0x89f6bcfe, 0x1253f091, 0x6cdf821c, 0xf77ace73, 0x80e41c83, 0x1b4150ec, 0x6fd9b963, 0xf47cf50c, 0x83e227fc, 0x18476b93, 0x6ad3f4e2, 0xf176b88d, 0x86e86a7d, 0x1d4d2612, 0x69d5cf9d, 0xf27083f2, 0x85ee5102, 0x1e4b1d6d, 0x78f6b418, 0xe353f877, 0x94cd2a87, 0x0f6866e8, 0x7bf08f67, 0xe055c308, 0x97cb11f8, 0x0c6e5d97, 0x7efac2e6, 0xe55f8e89, 0x92c15c79, 0x09641016, 0x7dfcf999, 0xe659b5f6, 0x91c76706, 0x0a622b69, 0x74ee59e4, 0xef4b158b, 0x98d5c77b, 0x03708b14, 0x77e8629b, 0xec4d2ef4, 0x9bd3fc04, 0x0076b06b, 0x72e22f1a, 0xe9476375, 0x9ed9b185, 0x057cfdea, 0x71e41465, 0xea41580a, 0x9ddf8afa, 0x067ac695, 0x50a4d810, 0xcb01947f, 0xbc9f468f, 0x273a0ae0, 0x53a2e36f, 0xc807af00, 0xbf997df0, 0x243c319f, 0x56a8aeee, 0xcd0de281, 0xba933071, 0x21367c1e, 0x55ae9591, 0xce0bd9fe, 0xb9950b0e, 0x22304761, 0x5cbc35ec, 0xc7197983, 0xb087ab73, 0x2b22e71c, 0x5fba0e93, 0xc41f42fc, 0xb381900c, 0x2824dc63, 0x5ab04312, 0xc1150f7d, 0xb68bdd8d, 0x2d2e91e2, 0x59b6786d, 0xc2133402, 0xb58de6f2, 0x2e28aa9d, 0x489503e8, 0xd3304f87, 0xa4ae9d77, 0x3f0bd118, 0x4b933897, 0xd03674f8, 0xa7a8a608, 0x3c0dea67, 0x4e997516, 0xd53c3979, 0xa2a2eb89, 0x3907a7e6, 0x4d9f4e69, 0xd63a0206, 0xa1a4d0f6, 0x3a019c99, 0x448dee14, 0xdf28a27b, 0xa8b6708b, 0x33133ce4, 0x478bd56b, 0xdc2e9904, 0xabb04bf4, 0x3015079b, 0x428198ea, 0xd924d485, 0xaeba0675, 0x351f4a1a, 0x4187a395, 0xda22effa, 0xadbc3d0a, 0x36197165}, {0x00000000, 0xc18edfc0, 0x586cb9c1, 0x99e26601, 0xb0d97382, 0x7157ac42, 0xe8b5ca43, 0x293b1583, 0xbac3e145, 0x7b4d3e85, 0xe2af5884, 0x23218744, 0x0a1a92c7, 0xcb944d07, 0x52762b06, 0x93f8f4c6, 0xaef6c4cb, 0x6f781b0b, 0xf69a7d0a, 0x3714a2ca, 0x1e2fb749, 0xdfa16889, 0x46430e88, 0x87cdd148, 0x1435258e, 0xd5bbfa4e, 0x4c599c4f, 0x8dd7438f, 0xa4ec560c, 0x656289cc, 0xfc80efcd, 0x3d0e300d, 0x869c8fd7, 0x47125017, 0xdef03616, 0x1f7ee9d6, 0x3645fc55, 0xf7cb2395, 0x6e294594, 0xafa79a54, 0x3c5f6e92, 0xfdd1b152, 0x6433d753, 0xa5bd0893, 0x8c861d10, 0x4d08c2d0, 0xd4eaa4d1, 0x15647b11, 0x286a4b1c, 0xe9e494dc, 0x7006f2dd, 0xb1882d1d, 0x98b3389e, 0x593de75e, 0xc0df815f, 0x01515e9f, 0x92a9aa59, 0x53277599, 0xcac51398, 0x0b4bcc58, 0x2270d9db, 0xe3fe061b, 0x7a1c601a, 0xbb92bfda, 0xd64819ef, 0x17c6c62f, 0x8e24a02e, 0x4faa7fee, 0x66916a6d, 0xa71fb5ad, 0x3efdd3ac, 0xff730c6c, 0x6c8bf8aa, 0xad05276a, 0x34e7416b, 0xf5699eab, 0xdc528b28, 0x1ddc54e8, 0x843e32e9, 0x45b0ed29, 0x78bedd24, 0xb93002e4, 0x20d264e5, 0xe15cbb25, 0xc867aea6, 0x09e97166, 0x900b1767, 0x5185c8a7, 0xc27d3c61, 0x03f3e3a1, 0x9a1185a0, 0x5b9f5a60, 0x72a44fe3, 0xb32a9023, 0x2ac8f622, 0xeb4629e2, 0x50d49638, 0x915a49f8, 0x08b82ff9, 0xc936f039, 0xe00de5ba, 0x21833a7a, 0xb8615c7b, 0x79ef83bb, 0xea17777d, 0x2b99a8bd, 0xb27bcebc, 0x73f5117c, 0x5ace04ff, 0x9b40db3f, 0x02a2bd3e, 0xc32c62fe, 0xfe2252f3, 0x3fac8d33, 0xa64eeb32, 0x67c034f2, 0x4efb2171, 0x8f75feb1, 0x169798b0, 0xd7194770, 0x44e1b3b6, 0x856f6c76, 0x1c8d0a77, 0xdd03d5b7, 0xf438c034, 0x35b61ff4, 0xac5479f5, 0x6ddaa635, 0x77e1359f, 0xb66fea5f, 0x2f8d8c5e, 0xee03539e, 0xc738461d, 0x06b699dd, 0x9f54ffdc, 0x5eda201c, 0xcd22d4da, 0x0cac0b1a, 0x954e6d1b, 0x54c0b2db, 0x7dfba758, 0xbc757898, 0x25971e99, 0xe419c159, 0xd917f154, 0x18992e94, 0x817b4895, 0x40f59755, 0x69ce82d6, 0xa8405d16, 0x31a23b17, 0xf02ce4d7, 0x63d41011, 0xa25acfd1, 0x3bb8a9d0, 0xfa367610, 0xd30d6393, 0x1283bc53, 0x8b61da52, 0x4aef0592, 0xf17dba48, 0x30f36588, 0xa9110389, 0x689fdc49, 0x41a4c9ca, 0x802a160a, 0x19c8700b, 0xd846afcb, 0x4bbe5b0d, 0x8a3084cd, 0x13d2e2cc, 0xd25c3d0c, 0xfb67288f, 0x3ae9f74f, 0xa30b914e, 0x62854e8e, 0x5f8b7e83, 0x9e05a143, 0x07e7c742, 0xc6691882, 0xef520d01, 0x2edcd2c1, 0xb73eb4c0, 0x76b06b00, 0xe5489fc6, 0x24c64006, 0xbd242607, 0x7caaf9c7, 0x5591ec44, 0x941f3384, 0x0dfd5585, 0xcc738a45, 0xa1a92c70, 0x6027f3b0, 0xf9c595b1, 0x384b4a71, 0x11705ff2, 0xd0fe8032, 0x491ce633, 0x889239f3, 0x1b6acd35, 0xdae412f5, 0x430674f4, 0x8288ab34, 0xabb3beb7, 0x6a3d6177, 0xf3df0776, 0x3251d8b6, 0x0f5fe8bb, 0xced1377b, 0x5733517a, 0x96bd8eba, 0xbf869b39, 0x7e0844f9, 0xe7ea22f8, 0x2664fd38, 0xb59c09fe, 0x7412d63e, 0xedf0b03f, 0x2c7e6fff, 0x05457a7c, 0xc4cba5bc, 0x5d29c3bd, 0x9ca71c7d, 0x2735a3a7, 0xe6bb7c67, 0x7f591a66, 0xbed7c5a6, 0x97ecd025, 0x56620fe5, 0xcf8069e4, 0x0e0eb624, 0x9df642e2, 0x5c789d22, 0xc59afb23, 0x041424e3, 0x2d2f3160, 0xeca1eea0, 0x754388a1, 0xb4cd5761, 0x89c3676c, 0x484db8ac, 0xd1afdead, 0x1021016d, 0x391a14ee, 0xf894cb2e, 0x6176ad2f, 0xa0f872ef, 0x33008629, 0xf28e59e9, 0x6b6c3fe8, 0xaae2e028, 0x83d9f5ab, 0x42572a6b, 0xdbb54c6a, 0x1a3b93aa}, {0x00000000, 0xefc26b3e, 0x04f5d03d, 0xeb37bb03, 0x09eba07a, 0xe629cb44, 0x0d1e7047, 0xe2dc1b79, 0x13d740f4, 0xfc152bca, 0x172290c9, 0xf8e0fbf7, 0x1a3ce08e, 0xf5fe8bb0, 0x1ec930b3, 0xf10b5b8d, 0x27ae81e8, 0xc86cead6, 0x235b51d5, 0xcc993aeb, 0x2e452192, 0xc1874aac, 0x2ab0f1af, 0xc5729a91, 0x3479c11c, 0xdbbbaa22, 0x308c1121, 0xdf4e7a1f, 0x3d926166, 0xd2500a58, 0x3967b15b, 0xd6a5da65, 0x4f5d03d0, 0xa09f68ee, 0x4ba8d3ed, 0xa46ab8d3, 0x46b6a3aa, 0xa974c894, 0x42437397, 0xad8118a9, 0x5c8a4324, 0xb348281a, 0x587f9319, 0xb7bdf827, 0x5561e35e, 0xbaa38860, 0x51943363, 0xbe56585d, 0x68f38238, 0x8731e906, 0x6c065205, 0x83c4393b, 0x61182242, 0x8eda497c, 0x65edf27f, 0x8a2f9941, 0x7b24c2cc, 0x94e6a9f2, 0x7fd112f1, 0x901379cf, 0x72cf62b6, 0x9d0d0988, 0x763ab28b, 0x99f8d9b5, 0x9eba07a0, 0x71786c9e, 0x9a4fd79d, 0x758dbca3, 0x9751a7da, 0x7893cce4, 0x93a477e7, 0x7c661cd9, 0x8d6d4754, 0x62af2c6a, 0x89989769, 0x665afc57, 0x8486e72e, 0x6b448c10, 0x80733713, 0x6fb15c2d, 0xb9148648, 0x56d6ed76, 0xbde15675, 0x52233d4b, 0xb0ff2632, 0x5f3d4d0c, 0xb40af60f, 0x5bc89d31, 0xaac3c6bc, 0x4501ad82, 0xae361681, 0x41f47dbf, 0xa32866c6, 0x4cea0df8, 0xa7ddb6fb, 0x481fddc5, 0xd1e70470, 0x3e256f4e, 0xd512d44d, 0x3ad0bf73, 0xd80ca40a, 0x37cecf34, 0xdcf97437, 0x333b1f09, 0xc2304484, 0x2df22fba, 0xc6c594b9, 0x2907ff87, 0xcbdbe4fe, 0x24198fc0, 0xcf2e34c3, 0x20ec5ffd, 0xf6498598, 0x198beea6, 0xf2bc55a5, 0x1d7e3e9b, 0xffa225e2, 0x10604edc, 0xfb57f5df, 0x14959ee1, 0xe59ec56c, 0x0a5cae52, 0xe16b1551, 0x0ea97e6f, 0xec756516, 0x03b70e28, 0xe880b52b, 0x0742de15, 0xe6050901, 0x09c7623f, 0xe2f0d93c, 0x0d32b202, 0xefeea97b, 0x002cc245, 0xeb1b7946, 0x04d91278, 0xf5d249f5, 0x1a1022cb, 0xf12799c8, 0x1ee5f2f6, 0xfc39e98f, 0x13fb82b1, 0xf8cc39b2, 0x170e528c, 0xc1ab88e9, 0x2e69e3d7, 0xc55e58d4, 0x2a9c33ea, 0xc8402893, 0x278243ad, 0xccb5f8ae, 0x23779390, 0xd27cc81d, 0x3dbea323, 0xd6891820, 0x394b731e, 0xdb976867, 0x34550359, 0xdf62b85a, 0x30a0d364, 0xa9580ad1, 0x469a61ef, 0xadaddaec, 0x426fb1d2, 0xa0b3aaab, 0x4f71c195, 0xa4467a96, 0x4b8411a8, 0xba8f4a25, 0x554d211b, 0xbe7a9a18, 0x51b8f126, 0xb364ea5f, 0x5ca68161, 0xb7913a62, 0x5853515c, 0x8ef68b39, 0x6134e007, 0x8a035b04, 0x65c1303a, 0x871d2b43, 0x68df407d, 0x83e8fb7e, 0x6c2a9040, 0x9d21cbcd, 0x72e3a0f3, 0x99d41bf0, 0x761670ce, 0x94ca6bb7, 0x7b080089, 0x903fbb8a, 0x7ffdd0b4, 0x78bf0ea1, 0x977d659f, 0x7c4ade9c, 0x9388b5a2, 0x7154aedb, 0x9e96c5e5, 0x75a17ee6, 0x9a6315d8, 0x6b684e55, 0x84aa256b, 0x6f9d9e68, 0x805ff556, 0x6283ee2f, 0x8d418511, 0x66763e12, 0x89b4552c, 0x5f118f49, 0xb0d3e477, 0x5be45f74, 0xb426344a, 0x56fa2f33, 0xb938440d, 0x520fff0e, 0xbdcd9430, 0x4cc6cfbd, 0xa304a483, 0x48331f80, 0xa7f174be, 0x452d6fc7, 0xaaef04f9, 0x41d8bffa, 0xae1ad4c4, 0x37e20d71, 0xd820664f, 0x3317dd4c, 0xdcd5b672, 0x3e09ad0b, 0xd1cbc635, 0x3afc7d36, 0xd53e1608, 0x24354d85, 0xcbf726bb, 0x20c09db8, 0xcf02f686, 0x2ddeedff, 0xc21c86c1, 0x292b3dc2, 0xc6e956fc, 0x104c8c99, 0xff8ee7a7, 0x14b95ca4, 0xfb7b379a, 0x19a72ce3, 0xf66547dd, 0x1d52fcde, 0xf29097e0, 0x039bcc6d, 0xec59a753, 0x076e1c50, 0xe8ac776e, 0x0a706c17, 0xe5b20729, 0x0e85bc2a, 0xe147d714}, {0x00000000, 0x177b1443, 0x2ef62886, 0x398d3cc5, 0x5dec510c, 0x4a97454f, 0x731a798a, 0x64616dc9, 0xbbd8a218, 0xaca3b65b, 0x952e8a9e, 0x82559edd, 0xe634f314, 0xf14fe757, 0xc8c2db92, 0xdfb9cfd1, 0xacc04271, 0xbbbb5632, 0x82366af7, 0x954d7eb4, 0xf12c137d, 0xe657073e, 0xdfda3bfb, 0xc8a12fb8, 0x1718e069, 0x0063f42a, 0x39eec8ef, 0x2e95dcac, 0x4af4b165, 0x5d8fa526, 0x640299e3, 0x73798da0, 0x82f182a3, 0x958a96e0, 0xac07aa25, 0xbb7cbe66, 0xdf1dd3af, 0xc866c7ec, 0xf1ebfb29, 0xe690ef6a, 0x392920bb, 0x2e5234f8, 0x17df083d, 0x00a41c7e, 0x64c571b7, 0x73be65f4, 0x4a335931, 0x5d484d72, 0x2e31c0d2, 0x394ad491, 0x00c7e854, 0x17bcfc17, 0x73dd91de, 0x64a6859d, 0x5d2bb958, 0x4a50ad1b, 0x95e962ca, 0x82927689, 0xbb1f4a4c, 0xac645e0f, 0xc80533c6, 0xdf7e2785, 0xe6f31b40, 0xf1880f03, 0xde920307, 0xc9e91744, 0xf0642b81, 0xe71f3fc2, 0x837e520b, 0x94054648, 0xad887a8d, 0xbaf36ece, 0x654aa11f, 0x7231b55c, 0x4bbc8999, 0x5cc79dda, 0x38a6f013, 0x2fdde450, 0x1650d895, 0x012bccd6, 0x72524176, 0x65295535, 0x5ca469f0, 0x4bdf7db3, 0x2fbe107a, 0x38c50439, 0x014838fc, 0x16332cbf, 0xc98ae36e, 0xdef1f72d, 0xe77ccbe8, 0xf007dfab, 0x9466b262, 0x831da621, 0xba909ae4, 0xadeb8ea7, 0x5c6381a4, 0x4b1895e7, 0x7295a922, 0x65eebd61, 0x018fd0a8, 0x16f4c4eb, 0x2f79f82e, 0x3802ec6d, 0xe7bb23bc, 0xf0c037ff, 0xc94d0b3a, 0xde361f79, 0xba5772b0, 0xad2c66f3, 0x94a15a36, 0x83da4e75, 0xf0a3c3d5, 0xe7d8d796, 0xde55eb53, 0xc92eff10, 0xad4f92d9, 0xba34869a, 0x83b9ba5f, 0x94c2ae1c, 0x4b7b61cd, 0x5c00758e, 0x658d494b, 0x72f65d08, 0x169730c1, 0x01ec2482, 0x38611847, 0x2f1a0c04, 0x6655004f, 0x712e140c, 0x48a328c9, 0x5fd83c8a, 0x3bb95143, 0x2cc24500, 0x154f79c5, 0x02346d86, 0xdd8da257, 0xcaf6b614, 0xf37b8ad1, 0xe4009e92, 0x8061f35b, 0x971ae718, 0xae97dbdd, 0xb9eccf9e, 0xca95423e, 0xddee567d, 0xe4636ab8, 0xf3187efb, 0x97791332, 0x80020771, 0xb98f3bb4, 0xaef42ff7, 0x714de026, 0x6636f465, 0x5fbbc8a0, 0x48c0dce3, 0x2ca1b12a, 0x3bdaa569, 0x025799ac, 0x152c8def, 0xe4a482ec, 0xf3df96af, 0xca52aa6a, 0xdd29be29, 0xb948d3e0, 0xae33c7a3, 0x97befb66, 0x80c5ef25, 0x5f7c20f4, 0x480734b7, 0x718a0872, 0x66f11c31, 0x029071f8, 0x15eb65bb, 0x2c66597e, 0x3b1d4d3d, 0x4864c09d, 0x5f1fd4de, 0x6692e81b, 0x71e9fc58, 0x15889191, 0x02f385d2, 0x3b7eb917, 0x2c05ad54, 0xf3bc6285, 0xe4c776c6, 0xdd4a4a03, 0xca315e40, 0xae503389, 0xb92b27ca, 0x80a61b0f, 0x97dd0f4c, 0xb8c70348, 0xafbc170b, 0x96312bce, 0x814a3f8d, 0xe52b5244, 0xf2504607, 0xcbdd7ac2, 0xdca66e81, 0x031fa150, 0x1464b513, 0x2de989d6, 0x3a929d95, 0x5ef3f05c, 0x4988e41f, 0x7005d8da, 0x677ecc99, 0x14074139, 0x037c557a, 0x3af169bf, 0x2d8a7dfc, 0x49eb1035, 0x5e900476, 0x671d38b3, 0x70662cf0, 0xafdfe321, 0xb8a4f762, 0x8129cba7, 0x9652dfe4, 0xf233b22d, 0xe548a66e, 0xdcc59aab, 0xcbbe8ee8, 0x3a3681eb, 0x2d4d95a8, 0x14c0a96d, 0x03bbbd2e, 0x67dad0e7, 0x70a1c4a4, 0x492cf861, 0x5e57ec22, 0x81ee23f3, 0x969537b0, 0xaf180b75, 0xb8631f36, 0xdc0272ff, 0xcb7966bc, 0xf2f45a79, 0xe58f4e3a, 0x96f6c39a, 0x818dd7d9, 0xb800eb1c, 0xaf7bff5f, 0xcb1a9296, 0xdc6186d5, 0xe5ecba10, 0xf297ae53, 0x2d2e6182, 0x3a5575c1, 0x03d84904, 0x14a35d47, 0x70c2308e, 0x67b924cd, 0x5e341808, 0x494f0c4b}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x0000000000000000, 0x43147b1700000000, 0x8628f62e00000000, 0xc53c8d3900000000, 0x0c51ec5d00000000, 0x4f45974a00000000, 0x8a791a7300000000, 0xc96d616400000000, 0x18a2d8bb00000000, 0x5bb6a3ac00000000, 0x9e8a2e9500000000, 0xdd9e558200000000, 0x14f334e600000000, 0x57e74ff100000000, 0x92dbc2c800000000, 0xd1cfb9df00000000, 0x7142c0ac00000000, 0x3256bbbb00000000, 0xf76a368200000000, 0xb47e4d9500000000, 0x7d132cf100000000, 0x3e0757e600000000, 0xfb3bdadf00000000, 0xb82fa1c800000000, 0x69e0181700000000, 0x2af4630000000000, 0xefc8ee3900000000, 0xacdc952e00000000, 0x65b1f44a00000000, 0x26a58f5d00000000, 0xe399026400000000, 0xa08d797300000000, 0xa382f18200000000, 0xe0968a9500000000, 0x25aa07ac00000000, 0x66be7cbb00000000, 0xafd31ddf00000000, 0xecc766c800000000, 0x29fbebf100000000, 0x6aef90e600000000, 0xbb20293900000000, 0xf834522e00000000, 0x3d08df1700000000, 0x7e1ca40000000000, 0xb771c56400000000, 0xf465be7300000000, 0x3159334a00000000, 0x724d485d00000000, 0xd2c0312e00000000, 0x91d44a3900000000, 0x54e8c70000000000, 0x17fcbc1700000000, 0xde91dd7300000000, 0x9d85a66400000000, 0x58b92b5d00000000, 0x1bad504a00000000, 0xca62e99500000000, 0x8976928200000000, 0x4c4a1fbb00000000, 0x0f5e64ac00000000, 0xc63305c800000000, 0x85277edf00000000, 0x401bf3e600000000, 0x030f88f100000000, 0x070392de00000000, 0x4417e9c900000000, 0x812b64f000000000, 0xc23f1fe700000000, 0x0b527e8300000000, 0x4846059400000000, 0x8d7a88ad00000000, 0xce6ef3ba00000000, 0x1fa14a6500000000, 0x5cb5317200000000, 0x9989bc4b00000000, 0xda9dc75c00000000, 0x13f0a63800000000, 0x50e4dd2f00000000, 0x95d8501600000000, 0xd6cc2b0100000000, 0x7641527200000000, 0x3555296500000000, 0xf069a45c00000000, 0xb37ddf4b00000000, 0x7a10be2f00000000, 0x3904c53800000000, 0xfc38480100000000, 0xbf2c331600000000, 0x6ee38ac900000000, 0x2df7f1de00000000, 0xe8cb7ce700000000, 0xabdf07f000000000, 0x62b2669400000000, 0x21a61d8300000000, 0xe49a90ba00000000, 0xa78eebad00000000, 0xa481635c00000000, 0xe795184b00000000, 0x22a9957200000000, 0x61bdee6500000000, 0xa8d08f0100000000, 0xebc4f41600000000, 0x2ef8792f00000000, 0x6dec023800000000, 0xbc23bbe700000000, 0xff37c0f000000000, 0x3a0b4dc900000000, 0x791f36de00000000, 0xb07257ba00000000, 0xf3662cad00000000, 0x365aa19400000000, 0x754eda8300000000, 0xd5c3a3f000000000, 0x96d7d8e700000000, 0x53eb55de00000000, 0x10ff2ec900000000, 0xd9924fad00000000, 0x9a8634ba00000000, 0x5fbab98300000000, 0x1caec29400000000, 0xcd617b4b00000000, 0x8e75005c00000000, 0x4b498d6500000000, 0x085df67200000000, 0xc130971600000000, 0x8224ec0100000000, 0x4718613800000000, 0x040c1a2f00000000, 0x4f00556600000000, 0x0c142e7100000000, 0xc928a34800000000, 0x8a3cd85f00000000, 0x4351b93b00000000, 0x0045c22c00000000, 0xc5794f1500000000, 0x866d340200000000, 0x57a28ddd00000000, 0x14b6f6ca00000000, 0xd18a7bf300000000, 0x929e00e400000000, 0x5bf3618000000000, 0x18e71a9700000000, 0xdddb97ae00000000, 0x9ecfecb900000000, 0x3e4295ca00000000, 0x7d56eedd00000000, 0xb86a63e400000000, 0xfb7e18f300000000, 0x3213799700000000, 0x7107028000000000, 0xb43b8fb900000000, 0xf72ff4ae00000000, 0x26e04d7100000000, 0x65f4366600000000, 0xa0c8bb5f00000000, 0xe3dcc04800000000, 0x2ab1a12c00000000, 0x69a5da3b00000000, 0xac99570200000000, 0xef8d2c1500000000, 0xec82a4e400000000, 0xaf96dff300000000, 0x6aaa52ca00000000, 0x29be29dd00000000, 0xe0d348b900000000, 0xa3c733ae00000000, 0x66fbbe9700000000, 0x25efc58000000000, 0xf4207c5f00000000, 0xb734074800000000, 0x72088a7100000000, 0x311cf16600000000, 0xf871900200000000, 0xbb65eb1500000000, 0x7e59662c00000000, 0x3d4d1d3b00000000, 0x9dc0644800000000, 0xded41f5f00000000, 0x1be8926600000000, 0x58fce97100000000, 0x9191881500000000, 0xd285f30200000000, 0x17b97e3b00000000, 0x54ad052c00000000, 0x8562bcf300000000, 0xc676c7e400000000, 0x034a4add00000000, 0x405e31ca00000000, 0x893350ae00000000, 0xca272bb900000000, 0x0f1ba68000000000, 0x4c0fdd9700000000, 0x4803c7b800000000, 0x0b17bcaf00000000, 0xce2b319600000000, 0x8d3f4a8100000000, 0x44522be500000000, 0x074650f200000000, 0xc27addcb00000000, 0x816ea6dc00000000, 0x50a11f0300000000, 0x13b5641400000000, 0xd689e92d00000000, 0x959d923a00000000, 0x5cf0f35e00000000, 0x1fe4884900000000, 0xdad8057000000000, 0x99cc7e6700000000, 0x3941071400000000, 0x7a557c0300000000, 0xbf69f13a00000000, 0xfc7d8a2d00000000, 0x3510eb4900000000, 0x7604905e00000000, 0xb3381d6700000000, 0xf02c667000000000, 0x21e3dfaf00000000, 0x62f7a4b800000000, 0xa7cb298100000000, 0xe4df529600000000, 0x2db233f200000000, 0x6ea648e500000000, 0xab9ac5dc00000000, 0xe88ebecb00000000, 0xeb81363a00000000, 0xa8954d2d00000000, 0x6da9c01400000000, 0x2ebdbb0300000000, 0xe7d0da6700000000, 0xa4c4a17000000000, 0x61f82c4900000000, 0x22ec575e00000000, 0xf323ee8100000000, 0xb037959600000000, 0x750b18af00000000, 0x361f63b800000000, 0xff7202dc00000000, 0xbc6679cb00000000, 0x795af4f200000000, 0x3a4e8fe500000000, 0x9ac3f69600000000, 0xd9d78d8100000000, 0x1ceb00b800000000, 0x5fff7baf00000000, 0x96921acb00000000, 0xd58661dc00000000, 0x10baece500000000, 0x53ae97f200000000, 0x82612e2d00000000, 0xc175553a00000000, 0x0449d80300000000, 0x475da31400000000, 0x8e30c27000000000, 0xcd24b96700000000, 0x0818345e00000000, 0x4b0c4f4900000000}, {0x0000000000000000, 0x3e6bc2ef00000000, 0x3dd0f50400000000, 0x03bb37eb00000000, 0x7aa0eb0900000000, 0x44cb29e600000000, 0x47701e0d00000000, 0x791bdce200000000, 0xf440d71300000000, 0xca2b15fc00000000, 0xc990221700000000, 0xf7fbe0f800000000, 0x8ee03c1a00000000, 0xb08bfef500000000, 0xb330c91e00000000, 0x8d5b0bf100000000, 0xe881ae2700000000, 0xd6ea6cc800000000, 0xd5515b2300000000, 0xeb3a99cc00000000, 0x9221452e00000000, 0xac4a87c100000000, 0xaff1b02a00000000, 0x919a72c500000000, 0x1cc1793400000000, 0x22aabbdb00000000, 0x21118c3000000000, 0x1f7a4edf00000000, 0x6661923d00000000, 0x580a50d200000000, 0x5bb1673900000000, 0x65daa5d600000000, 0xd0035d4f00000000, 0xee689fa000000000, 0xedd3a84b00000000, 0xd3b86aa400000000, 0xaaa3b64600000000, 0x94c874a900000000, 0x9773434200000000, 0xa91881ad00000000, 0x24438a5c00000000, 0x1a2848b300000000, 0x19937f5800000000, 0x27f8bdb700000000, 0x5ee3615500000000, 0x6088a3ba00000000, 0x6333945100000000, 0x5d5856be00000000, 0x3882f36800000000, 0x06e9318700000000, 0x0552066c00000000, 0x3b39c48300000000, 0x4222186100000000, 0x7c49da8e00000000, 0x7ff2ed6500000000, 0x41992f8a00000000, 0xccc2247b00000000, 0xf2a9e69400000000, 0xf112d17f00000000, 0xcf79139000000000, 0xb662cf7200000000, 0x88090d9d00000000, 0x8bb23a7600000000, 0xb5d9f89900000000, 0xa007ba9e00000000, 0x9e6c787100000000, 0x9dd74f9a00000000, 0xa3bc8d7500000000, 0xdaa7519700000000, 0xe4cc937800000000, 0xe777a49300000000, 0xd91c667c00000000, 0x54476d8d00000000, 0x6a2caf6200000000, 0x6997988900000000, 0x57fc5a6600000000, 0x2ee7868400000000, 0x108c446b00000000, 0x1337738000000000, 0x2d5cb16f00000000, 0x488614b900000000, 0x76edd65600000000, 0x7556e1bd00000000, 0x4b3d235200000000, 0x3226ffb000000000, 0x0c4d3d5f00000000, 0x0ff60ab400000000, 0x319dc85b00000000, 0xbcc6c3aa00000000, 0x82ad014500000000, 0x811636ae00000000, 0xbf7df44100000000, 0xc66628a300000000, 0xf80dea4c00000000, 0xfbb6dda700000000, 0xc5dd1f4800000000, 0x7004e7d100000000, 0x4e6f253e00000000, 0x4dd412d500000000, 0x73bfd03a00000000, 0x0aa40cd800000000, 0x34cfce3700000000, 0x3774f9dc00000000, 0x091f3b3300000000, 0x844430c200000000, 0xba2ff22d00000000, 0xb994c5c600000000, 0x87ff072900000000, 0xfee4dbcb00000000, 0xc08f192400000000, 0xc3342ecf00000000, 0xfd5fec2000000000, 0x988549f600000000, 0xa6ee8b1900000000, 0xa555bcf200000000, 0x9b3e7e1d00000000, 0xe225a2ff00000000, 0xdc4e601000000000, 0xdff557fb00000000, 0xe19e951400000000, 0x6cc59ee500000000, 0x52ae5c0a00000000, 0x51156be100000000, 0x6f7ea90e00000000, 0x166575ec00000000, 0x280eb70300000000, 0x2bb580e800000000, 0x15de420700000000, 0x010905e600000000, 0x3f62c70900000000, 0x3cd9f0e200000000, 0x02b2320d00000000, 0x7ba9eeef00000000, 0x45c22c0000000000, 0x46791beb00000000, 0x7812d90400000000, 0xf549d2f500000000, 0xcb22101a00000000, 0xc89927f100000000, 0xf6f2e51e00000000, 0x8fe939fc00000000, 0xb182fb1300000000, 0xb239ccf800000000, 0x8c520e1700000000, 0xe988abc100000000, 0xd7e3692e00000000, 0xd4585ec500000000, 0xea339c2a00000000, 0x932840c800000000, 0xad43822700000000, 0xaef8b5cc00000000, 0x9093772300000000, 0x1dc87cd200000000, 0x23a3be3d00000000, 0x201889d600000000, 0x1e734b3900000000, 0x676897db00000000, 0x5903553400000000, 0x5ab862df00000000, 0x64d3a03000000000, 0xd10a58a900000000, 0xef619a4600000000, 0xecdaadad00000000, 0xd2b16f4200000000, 0xabaab3a000000000, 0x95c1714f00000000, 0x967a46a400000000, 0xa811844b00000000, 0x254a8fba00000000, 0x1b214d5500000000, 0x189a7abe00000000, 0x26f1b85100000000, 0x5fea64b300000000, 0x6181a65c00000000, 0x623a91b700000000, 0x5c51535800000000, 0x398bf68e00000000, 0x07e0346100000000, 0x045b038a00000000, 0x3a30c16500000000, 0x432b1d8700000000, 0x7d40df6800000000, 0x7efbe88300000000, 0x40902a6c00000000, 0xcdcb219d00000000, 0xf3a0e37200000000, 0xf01bd49900000000, 0xce70167600000000, 0xb76bca9400000000, 0x8900087b00000000, 0x8abb3f9000000000, 0xb4d0fd7f00000000, 0xa10ebf7800000000, 0x9f657d9700000000, 0x9cde4a7c00000000, 0xa2b5889300000000, 0xdbae547100000000, 0xe5c5969e00000000, 0xe67ea17500000000, 0xd815639a00000000, 0x554e686b00000000, 0x6b25aa8400000000, 0x689e9d6f00000000, 0x56f55f8000000000, 0x2fee836200000000, 0x1185418d00000000, 0x123e766600000000, 0x2c55b48900000000, 0x498f115f00000000, 0x77e4d3b000000000, 0x745fe45b00000000, 0x4a3426b400000000, 0x332ffa5600000000, 0x0d4438b900000000, 0x0eff0f5200000000, 0x3094cdbd00000000, 0xbdcfc64c00000000, 0x83a404a300000000, 0x801f334800000000, 0xbe74f1a700000000, 0xc76f2d4500000000, 0xf904efaa00000000, 0xfabfd84100000000, 0xc4d41aae00000000, 0x710de23700000000, 0x4f6620d800000000, 0x4cdd173300000000, 0x72b6d5dc00000000, 0x0bad093e00000000, 0x35c6cbd100000000, 0x367dfc3a00000000, 0x08163ed500000000, 0x854d352400000000, 0xbb26f7cb00000000, 0xb89dc02000000000, 0x86f602cf00000000, 0xffedde2d00000000, 0xc1861cc200000000, 0xc23d2b2900000000, 0xfc56e9c600000000, 0x998c4c1000000000, 0xa7e78eff00000000, 0xa45cb91400000000, 0x9a377bfb00000000, 0xe32ca71900000000, 0xdd4765f600000000, 0xdefc521d00000000, 0xe09790f200000000, 0x6dcc9b0300000000, 0x53a759ec00000000, 0x501c6e0700000000, 0x6e77ace800000000, 0x176c700a00000000, 0x2907b2e500000000, 0x2abc850e00000000, 0x14d747e100000000}, {0x0000000000000000, 0xc0df8ec100000000, 0xc1b96c5800000000, 0x0166e29900000000, 0x8273d9b000000000, 0x42ac577100000000, 0x43cab5e800000000, 0x83153b2900000000, 0x45e1c3ba00000000, 0x853e4d7b00000000, 0x8458afe200000000, 0x4487212300000000, 0xc7921a0a00000000, 0x074d94cb00000000, 0x062b765200000000, 0xc6f4f89300000000, 0xcbc4f6ae00000000, 0x0b1b786f00000000, 0x0a7d9af600000000, 0xcaa2143700000000, 0x49b72f1e00000000, 0x8968a1df00000000, 0x880e434600000000, 0x48d1cd8700000000, 0x8e25351400000000, 0x4efabbd500000000, 0x4f9c594c00000000, 0x8f43d78d00000000, 0x0c56eca400000000, 0xcc89626500000000, 0xcdef80fc00000000, 0x0d300e3d00000000, 0xd78f9c8600000000, 0x1750124700000000, 0x1636f0de00000000, 0xd6e97e1f00000000, 0x55fc453600000000, 0x9523cbf700000000, 0x9445296e00000000, 0x549aa7af00000000, 0x926e5f3c00000000, 0x52b1d1fd00000000, 0x53d7336400000000, 0x9308bda500000000, 0x101d868c00000000, 0xd0c2084d00000000, 0xd1a4ead400000000, 0x117b641500000000, 0x1c4b6a2800000000, 0xdc94e4e900000000, 0xddf2067000000000, 0x1d2d88b100000000, 0x9e38b39800000000, 0x5ee73d5900000000, 0x5f81dfc000000000, 0x9f5e510100000000, 0x59aaa99200000000, 0x9975275300000000, 0x9813c5ca00000000, 0x58cc4b0b00000000, 0xdbd9702200000000, 0x1b06fee300000000, 0x1a601c7a00000000, 0xdabf92bb00000000, 0xef1948d600000000, 0x2fc6c61700000000, 0x2ea0248e00000000, 0xee7faa4f00000000, 0x6d6a916600000000, 0xadb51fa700000000, 0xacd3fd3e00000000, 0x6c0c73ff00000000, 0xaaf88b6c00000000, 0x6a2705ad00000000, 0x6b41e73400000000, 0xab9e69f500000000, 0x288b52dc00000000, 0xe854dc1d00000000, 0xe9323e8400000000, 0x29edb04500000000, 0x24ddbe7800000000, 0xe40230b900000000, 0xe564d22000000000, 0x25bb5ce100000000, 0xa6ae67c800000000, 0x6671e90900000000, 0x67170b9000000000, 0xa7c8855100000000, 0x613c7dc200000000, 0xa1e3f30300000000, 0xa085119a00000000, 0x605a9f5b00000000, 0xe34fa47200000000, 0x23902ab300000000, 0x22f6c82a00000000, 0xe22946eb00000000, 0x3896d45000000000, 0xf8495a9100000000, 0xf92fb80800000000, 0x39f036c900000000, 0xbae50de000000000, 0x7a3a832100000000, 0x7b5c61b800000000, 0xbb83ef7900000000, 0x7d7717ea00000000, 0xbda8992b00000000, 0xbcce7bb200000000, 0x7c11f57300000000, 0xff04ce5a00000000, 0x3fdb409b00000000, 0x3ebda20200000000, 0xfe622cc300000000, 0xf35222fe00000000, 0x338dac3f00000000, 0x32eb4ea600000000, 0xf234c06700000000, 0x7121fb4e00000000, 0xb1fe758f00000000, 0xb098971600000000, 0x704719d700000000, 0xb6b3e14400000000, 0x766c6f8500000000, 0x770a8d1c00000000, 0xb7d503dd00000000, 0x34c038f400000000, 0xf41fb63500000000, 0xf57954ac00000000, 0x35a6da6d00000000, 0x9f35e17700000000, 0x5fea6fb600000000, 0x5e8c8d2f00000000, 0x9e5303ee00000000, 0x1d4638c700000000, 0xdd99b60600000000, 0xdcff549f00000000, 0x1c20da5e00000000, 0xdad422cd00000000, 0x1a0bac0c00000000, 0x1b6d4e9500000000, 0xdbb2c05400000000, 0x58a7fb7d00000000, 0x987875bc00000000, 0x991e972500000000, 0x59c119e400000000, 0x54f117d900000000, 0x942e991800000000, 0x95487b8100000000, 0x5597f54000000000, 0xd682ce6900000000, 0x165d40a800000000, 0x173ba23100000000, 0xd7e42cf000000000, 0x1110d46300000000, 0xd1cf5aa200000000, 0xd0a9b83b00000000, 0x107636fa00000000, 0x93630dd300000000, 0x53bc831200000000, 0x52da618b00000000, 0x9205ef4a00000000, 0x48ba7df100000000, 0x8865f33000000000, 0x890311a900000000, 0x49dc9f6800000000, 0xcac9a44100000000, 0x0a162a8000000000, 0x0b70c81900000000, 0xcbaf46d800000000, 0x0d5bbe4b00000000, 0xcd84308a00000000, 0xcce2d21300000000, 0x0c3d5cd200000000, 0x8f2867fb00000000, 0x4ff7e93a00000000, 0x4e910ba300000000, 0x8e4e856200000000, 0x837e8b5f00000000, 0x43a1059e00000000, 0x42c7e70700000000, 0x821869c600000000, 0x010d52ef00000000, 0xc1d2dc2e00000000, 0xc0b43eb700000000, 0x006bb07600000000, 0xc69f48e500000000, 0x0640c62400000000, 0x072624bd00000000, 0xc7f9aa7c00000000, 0x44ec915500000000, 0x84331f9400000000, 0x8555fd0d00000000, 0x458a73cc00000000, 0x702ca9a100000000, 0xb0f3276000000000, 0xb195c5f900000000, 0x714a4b3800000000, 0xf25f701100000000, 0x3280fed000000000, 0x33e61c4900000000, 0xf339928800000000, 0x35cd6a1b00000000, 0xf512e4da00000000, 0xf474064300000000, 0x34ab888200000000, 0xb7beb3ab00000000, 0x77613d6a00000000, 0x7607dff300000000, 0xb6d8513200000000, 0xbbe85f0f00000000, 0x7b37d1ce00000000, 0x7a51335700000000, 0xba8ebd9600000000, 0x399b86bf00000000, 0xf944087e00000000, 0xf822eae700000000, 0x38fd642600000000, 0xfe099cb500000000, 0x3ed6127400000000, 0x3fb0f0ed00000000, 0xff6f7e2c00000000, 0x7c7a450500000000, 0xbca5cbc400000000, 0xbdc3295d00000000, 0x7d1ca79c00000000, 0xa7a3352700000000, 0x677cbbe600000000, 0x661a597f00000000, 0xa6c5d7be00000000, 0x25d0ec9700000000, 0xe50f625600000000, 0xe46980cf00000000, 0x24b60e0e00000000, 0xe242f69d00000000, 0x229d785c00000000, 0x23fb9ac500000000, 0xe324140400000000, 0x60312f2d00000000, 0xa0eea1ec00000000, 0xa188437500000000, 0x6157cdb400000000, 0x6c67c38900000000, 0xacb84d4800000000, 0xaddeafd100000000, 0x6d01211000000000, 0xee141a3900000000, 0x2ecb94f800000000, 0x2fad766100000000, 0xef72f8a000000000, 0x2986003300000000, 0xe9598ef200000000, 0xe83f6c6b00000000, 0x28e0e2aa00000000, 0xabf5d98300000000, 0x6b2a574200000000, 0x6a4cb5db00000000, 0xaa933b1a00000000}, {0x0000000000000000, 0x6f4ca59b00000000, 0x9f9e3bec00000000, 0xf0d29e7700000000, 0x7f3b060300000000, 0x1077a39800000000, 0xe0a53def00000000, 0x8fe9987400000000, 0xfe760c0600000000, 0x913aa99d00000000, 0x61e837ea00000000, 0x0ea4927100000000, 0x814d0a0500000000, 0xee01af9e00000000, 0x1ed331e900000000, 0x719f947200000000, 0xfced180c00000000, 0x93a1bd9700000000, 0x637323e000000000, 0x0c3f867b00000000, 0x83d61e0f00000000, 0xec9abb9400000000, 0x1c4825e300000000, 0x7304807800000000, 0x029b140a00000000, 0x6dd7b19100000000, 0x9d052fe600000000, 0xf2498a7d00000000, 0x7da0120900000000, 0x12ecb79200000000, 0xe23e29e500000000, 0x8d728c7e00000000, 0xf8db311800000000, 0x9797948300000000, 0x67450af400000000, 0x0809af6f00000000, 0x87e0371b00000000, 0xe8ac928000000000, 0x187e0cf700000000, 0x7732a96c00000000, 0x06ad3d1e00000000, 0x69e1988500000000, 0x993306f200000000, 0xf67fa36900000000, 0x79963b1d00000000, 0x16da9e8600000000, 0xe60800f100000000, 0x8944a56a00000000, 0x0436291400000000, 0x6b7a8c8f00000000, 0x9ba812f800000000, 0xf4e4b76300000000, 0x7b0d2f1700000000, 0x14418a8c00000000, 0xe49314fb00000000, 0x8bdfb16000000000, 0xfa40251200000000, 0x950c808900000000, 0x65de1efe00000000, 0x0a92bb6500000000, 0x857b231100000000, 0xea37868a00000000, 0x1ae518fd00000000, 0x75a9bd6600000000, 0xf0b7633000000000, 0x9ffbc6ab00000000, 0x6f2958dc00000000, 0x0065fd4700000000, 0x8f8c653300000000, 0xe0c0c0a800000000, 0x10125edf00000000, 0x7f5efb4400000000, 0x0ec16f3600000000, 0x618dcaad00000000, 0x915f54da00000000, 0xfe13f14100000000, 0x71fa693500000000, 0x1eb6ccae00000000, 0xee6452d900000000, 0x8128f74200000000, 0x0c5a7b3c00000000, 0x6316dea700000000, 0x93c440d000000000, 0xfc88e54b00000000, 0x73617d3f00000000, 0x1c2dd8a400000000, 0xecff46d300000000, 0x83b3e34800000000, 0xf22c773a00000000, 0x9d60d2a100000000, 0x6db24cd600000000, 0x02fee94d00000000, 0x8d17713900000000, 0xe25bd4a200000000, 0x12894ad500000000, 0x7dc5ef4e00000000, 0x086c522800000000, 0x6720f7b300000000, 0x97f269c400000000, 0xf8becc5f00000000, 0x7757542b00000000, 0x181bf1b000000000, 0xe8c96fc700000000, 0x8785ca5c00000000, 0xf61a5e2e00000000, 0x9956fbb500000000, 0x698465c200000000, 0x06c8c05900000000, 0x8921582d00000000, 0xe66dfdb600000000, 0x16bf63c100000000, 0x79f3c65a00000000, 0xf4814a2400000000, 0x9bcdefbf00000000, 0x6b1f71c800000000, 0x0453d45300000000, 0x8bba4c2700000000, 0xe4f6e9bc00000000, 0x142477cb00000000, 0x7b68d25000000000, 0x0af7462200000000, 0x65bbe3b900000000, 0x95697dce00000000, 0xfa25d85500000000, 0x75cc402100000000, 0x1a80e5ba00000000, 0xea527bcd00000000, 0x851ede5600000000, 0xe06fc76000000000, 0x8f2362fb00000000, 0x7ff1fc8c00000000, 0x10bd591700000000, 0x9f54c16300000000, 0xf01864f800000000, 0x00cafa8f00000000, 0x6f865f1400000000, 0x1e19cb6600000000, 0x71556efd00000000, 0x8187f08a00000000, 0xeecb551100000000, 0x6122cd6500000000, 0x0e6e68fe00000000, 0xfebcf68900000000, 0x91f0531200000000, 0x1c82df6c00000000, 0x73ce7af700000000, 0x831ce48000000000, 0xec50411b00000000, 0x63b9d96f00000000, 0x0cf57cf400000000, 0xfc27e28300000000, 0x936b471800000000, 0xe2f4d36a00000000, 0x8db876f100000000, 0x7d6ae88600000000, 0x12264d1d00000000, 0x9dcfd56900000000, 0xf28370f200000000, 0x0251ee8500000000, 0x6d1d4b1e00000000, 0x18b4f67800000000, 0x77f853e300000000, 0x872acd9400000000, 0xe866680f00000000, 0x678ff07b00000000, 0x08c355e000000000, 0xf811cb9700000000, 0x975d6e0c00000000, 0xe6c2fa7e00000000, 0x898e5fe500000000, 0x795cc19200000000, 0x1610640900000000, 0x99f9fc7d00000000, 0xf6b559e600000000, 0x0667c79100000000, 0x692b620a00000000, 0xe459ee7400000000, 0x8b154bef00000000, 0x7bc7d59800000000, 0x148b700300000000, 0x9b62e87700000000, 0xf42e4dec00000000, 0x04fcd39b00000000, 0x6bb0760000000000, 0x1a2fe27200000000, 0x756347e900000000, 0x85b1d99e00000000, 0xeafd7c0500000000, 0x6514e47100000000, 0x0a5841ea00000000, 0xfa8adf9d00000000, 0x95c67a0600000000, 0x10d8a45000000000, 0x7f9401cb00000000, 0x8f469fbc00000000, 0xe00a3a2700000000, 0x6fe3a25300000000, 0x00af07c800000000, 0xf07d99bf00000000, 0x9f313c2400000000, 0xeeaea85600000000, 0x81e20dcd00000000, 0x713093ba00000000, 0x1e7c362100000000, 0x9195ae5500000000, 0xfed90bce00000000, 0x0e0b95b900000000, 0x6147302200000000, 0xec35bc5c00000000, 0x837919c700000000, 0x73ab87b000000000, 0x1ce7222b00000000, 0x930eba5f00000000, 0xfc421fc400000000, 0x0c9081b300000000, 0x63dc242800000000, 0x1243b05a00000000, 0x7d0f15c100000000, 0x8ddd8bb600000000, 0xe2912e2d00000000, 0x6d78b65900000000, 0x023413c200000000, 0xf2e68db500000000, 0x9daa282e00000000, 0xe803954800000000, 0x874f30d300000000, 0x779daea400000000, 0x18d10b3f00000000, 0x9738934b00000000, 0xf87436d000000000, 0x08a6a8a700000000, 0x67ea0d3c00000000, 0x1675994e00000000, 0x79393cd500000000, 0x89eba2a200000000, 0xe6a7073900000000, 0x694e9f4d00000000, 0x06023ad600000000, 0xf6d0a4a100000000, 0x999c013a00000000, 0x14ee8d4400000000, 0x7ba228df00000000, 0x8b70b6a800000000, 0xe43c133300000000, 0x6bd58b4700000000, 0x04992edc00000000, 0xf44bb0ab00000000, 0x9b07153000000000, 0xea98814200000000, 0x85d424d900000000, 0x7506baae00000000, 0x1a4a1f3500000000, 0x95a3874100000000, 0xfaef22da00000000, 0x0a3dbcad00000000, 0x6571193600000000}, {0x0000000000000000, 0x85d996dd00000000, 0x4bb55c6000000000, 0xce6ccabd00000000, 0x966ab9c000000000, 0x13b32f1d00000000, 0xdddfe5a000000000, 0x5806737d00000000, 0x6dd3035a00000000, 0xe80a958700000000, 0x26665f3a00000000, 0xa3bfc9e700000000, 0xfbb9ba9a00000000, 0x7e602c4700000000, 0xb00ce6fa00000000, 0x35d5702700000000, 0xdaa607b400000000, 0x5f7f916900000000, 0x91135bd400000000, 0x14cacd0900000000, 0x4cccbe7400000000, 0xc91528a900000000, 0x0779e21400000000, 0x82a074c900000000, 0xb77504ee00000000, 0x32ac923300000000, 0xfcc0588e00000000, 0x7919ce5300000000, 0x211fbd2e00000000, 0xa4c62bf300000000, 0x6aaae14e00000000, 0xef73779300000000, 0xf54b7eb300000000, 0x7092e86e00000000, 0xbefe22d300000000, 0x3b27b40e00000000, 0x6321c77300000000, 0xe6f851ae00000000, 0x28949b1300000000, 0xad4d0dce00000000, 0x98987de900000000, 0x1d41eb3400000000, 0xd32d218900000000, 0x56f4b75400000000, 0x0ef2c42900000000, 0x8b2b52f400000000, 0x4547984900000000, 0xc09e0e9400000000, 0x2fed790700000000, 0xaa34efda00000000, 0x6458256700000000, 0xe181b3ba00000000, 0xb987c0c700000000, 0x3c5e561a00000000, 0xf2329ca700000000, 0x77eb0a7a00000000, 0x423e7a5d00000000, 0xc7e7ec8000000000, 0x098b263d00000000, 0x8c52b0e000000000, 0xd454c39d00000000, 0x518d554000000000, 0x9fe19ffd00000000, 0x1a38092000000000, 0xab918dbd00000000, 0x2e481b6000000000, 0xe024d1dd00000000, 0x65fd470000000000, 0x3dfb347d00000000, 0xb822a2a000000000, 0x764e681d00000000, 0xf397fec000000000, 0xc6428ee700000000, 0x439b183a00000000, 0x8df7d28700000000, 0x082e445a00000000, 0x5028372700000000, 0xd5f1a1fa00000000, 0x1b9d6b4700000000, 0x9e44fd9a00000000, 0x71378a0900000000, 0xf4ee1cd400000000, 0x3a82d66900000000, 0xbf5b40b400000000, 0xe75d33c900000000, 0x6284a51400000000, 0xace86fa900000000, 0x2931f97400000000, 0x1ce4895300000000, 0x993d1f8e00000000, 0x5751d53300000000, 0xd28843ee00000000, 0x8a8e309300000000, 0x0f57a64e00000000, 0xc13b6cf300000000, 0x44e2fa2e00000000, 0x5edaf30e00000000, 0xdb0365d300000000, 0x156faf6e00000000, 0x90b639b300000000, 0xc8b04ace00000000, 0x4d69dc1300000000, 0x830516ae00000000, 0x06dc807300000000, 0x3309f05400000000, 0xb6d0668900000000, 0x78bcac3400000000, 0xfd653ae900000000, 0xa563499400000000, 0x20badf4900000000, 0xeed615f400000000, 0x6b0f832900000000, 0x847cf4ba00000000, 0x01a5626700000000, 0xcfc9a8da00000000, 0x4a103e0700000000, 0x12164d7a00000000, 0x97cfdba700000000, 0x59a3111a00000000, 0xdc7a87c700000000, 0xe9aff7e000000000, 0x6c76613d00000000, 0xa21aab8000000000, 0x27c33d5d00000000, 0x7fc54e2000000000, 0xfa1cd8fd00000000, 0x3470124000000000, 0xb1a9849d00000000, 0x17256aa000000000, 0x92fcfc7d00000000, 0x5c9036c000000000, 0xd949a01d00000000, 0x814fd36000000000, 0x049645bd00000000, 0xcafa8f0000000000, 0x4f2319dd00000000, 0x7af669fa00000000, 0xff2fff2700000000, 0x3143359a00000000, 0xb49aa34700000000, 0xec9cd03a00000000, 0x694546e700000000, 0xa7298c5a00000000, 0x22f01a8700000000, 0xcd836d1400000000, 0x485afbc900000000, 0x8636317400000000, 0x03efa7a900000000, 0x5be9d4d400000000, 0xde30420900000000, 0x105c88b400000000, 0x95851e6900000000, 0xa0506e4e00000000, 0x2589f89300000000, 0xebe5322e00000000, 0x6e3ca4f300000000, 0x363ad78e00000000, 0xb3e3415300000000, 0x7d8f8bee00000000, 0xf8561d3300000000, 0xe26e141300000000, 0x67b782ce00000000, 0xa9db487300000000, 0x2c02deae00000000, 0x7404add300000000, 0xf1dd3b0e00000000, 0x3fb1f1b300000000, 0xba68676e00000000, 0x8fbd174900000000, 0x0a64819400000000, 0xc4084b2900000000, 0x41d1ddf400000000, 0x19d7ae8900000000, 0x9c0e385400000000, 0x5262f2e900000000, 0xd7bb643400000000, 0x38c813a700000000, 0xbd11857a00000000, 0x737d4fc700000000, 0xf6a4d91a00000000, 0xaea2aa6700000000, 0x2b7b3cba00000000, 0xe517f60700000000, 0x60ce60da00000000, 0x551b10fd00000000, 0xd0c2862000000000, 0x1eae4c9d00000000, 0x9b77da4000000000, 0xc371a93d00000000, 0x46a83fe000000000, 0x88c4f55d00000000, 0x0d1d638000000000, 0xbcb4e71d00000000, 0x396d71c000000000, 0xf701bb7d00000000, 0x72d82da000000000, 0x2ade5edd00000000, 0xaf07c80000000000, 0x616b02bd00000000, 0xe4b2946000000000, 0xd167e44700000000, 0x54be729a00000000, 0x9ad2b82700000000, 0x1f0b2efa00000000, 0x470d5d8700000000, 0xc2d4cb5a00000000, 0x0cb801e700000000, 0x8961973a00000000, 0x6612e0a900000000, 0xe3cb767400000000, 0x2da7bcc900000000, 0xa87e2a1400000000, 0xf078596900000000, 0x75a1cfb400000000, 0xbbcd050900000000, 0x3e1493d400000000, 0x0bc1e3f300000000, 0x8e18752e00000000, 0x4074bf9300000000, 0xc5ad294e00000000, 0x9dab5a3300000000, 0x1872ccee00000000, 0xd61e065300000000, 0x53c7908e00000000, 0x49ff99ae00000000, 0xcc260f7300000000, 0x024ac5ce00000000, 0x8793531300000000, 0xdf95206e00000000, 0x5a4cb6b300000000, 0x94207c0e00000000, 0x11f9ead300000000, 0x242c9af400000000, 0xa1f50c2900000000, 0x6f99c69400000000, 0xea40504900000000, 0xb246233400000000, 0x379fb5e900000000, 0xf9f37f5400000000, 0x7c2ae98900000000, 0x93599e1a00000000, 0x168008c700000000, 0xd8ecc27a00000000, 0x5d3554a700000000, 0x053327da00000000, 0x80eab10700000000, 0x4e867bba00000000, 0xcb5fed6700000000, 0xfe8a9d4000000000, 0x7b530b9d00000000, 0xb53fc12000000000, 0x30e657fd00000000, 0x68e0248000000000, 0xed39b25d00000000, 0x235578e000000000, 0xa68cee3d00000000}, {0x0000000000000000, 0x76e10f9d00000000, 0xadc46ee100000000, 0xdb25617c00000000, 0x1b8fac1900000000, 0x6d6ea38400000000, 0xb64bc2f800000000, 0xc0aacd6500000000, 0x361e593300000000, 0x40ff56ae00000000, 0x9bda37d200000000, 0xed3b384f00000000, 0x2d91f52a00000000, 0x5b70fab700000000, 0x80559bcb00000000, 0xf6b4945600000000, 0x6c3cb26600000000, 0x1addbdfb00000000, 0xc1f8dc8700000000, 0xb719d31a00000000, 0x77b31e7f00000000, 0x015211e200000000, 0xda77709e00000000, 0xac967f0300000000, 0x5a22eb5500000000, 0x2cc3e4c800000000, 0xf7e685b400000000, 0x81078a2900000000, 0x41ad474c00000000, 0x374c48d100000000, 0xec6929ad00000000, 0x9a88263000000000, 0xd87864cd00000000, 0xae996b5000000000, 0x75bc0a2c00000000, 0x035d05b100000000, 0xc3f7c8d400000000, 0xb516c74900000000, 0x6e33a63500000000, 0x18d2a9a800000000, 0xee663dfe00000000, 0x9887326300000000, 0x43a2531f00000000, 0x35435c8200000000, 0xf5e991e700000000, 0x83089e7a00000000, 0x582dff0600000000, 0x2eccf09b00000000, 0xb444d6ab00000000, 0xc2a5d93600000000, 0x1980b84a00000000, 0x6f61b7d700000000, 0xafcb7ab200000000, 0xd92a752f00000000, 0x020f145300000000, 0x74ee1bce00000000, 0x825a8f9800000000, 0xf4bb800500000000, 0x2f9ee17900000000, 0x597feee400000000, 0x99d5238100000000, 0xef342c1c00000000, 0x34114d6000000000, 0x42f042fd00000000, 0xf1f7b94100000000, 0x8716b6dc00000000, 0x5c33d7a000000000, 0x2ad2d83d00000000, 0xea78155800000000, 0x9c991ac500000000, 0x47bc7bb900000000, 0x315d742400000000, 0xc7e9e07200000000, 0xb108efef00000000, 0x6a2d8e9300000000, 0x1ccc810e00000000, 0xdc664c6b00000000, 0xaa8743f600000000, 0x71a2228a00000000, 0x07432d1700000000, 0x9dcb0b2700000000, 0xeb2a04ba00000000, 0x300f65c600000000, 0x46ee6a5b00000000, 0x8644a73e00000000, 0xf0a5a8a300000000, 0x2b80c9df00000000, 0x5d61c64200000000, 0xabd5521400000000, 0xdd345d8900000000, 0x06113cf500000000, 0x70f0336800000000, 0xb05afe0d00000000, 0xc6bbf19000000000, 0x1d9e90ec00000000, 0x6b7f9f7100000000, 0x298fdd8c00000000, 0x5f6ed21100000000, 0x844bb36d00000000, 0xf2aabcf000000000, 0x3200719500000000, 0x44e17e0800000000, 0x9fc41f7400000000, 0xe92510e900000000, 0x1f9184bf00000000, 0x69708b2200000000, 0xb255ea5e00000000, 0xc4b4e5c300000000, 0x041e28a600000000, 0x72ff273b00000000, 0xa9da464700000000, 0xdf3b49da00000000, 0x45b36fea00000000, 0x3352607700000000, 0xe877010b00000000, 0x9e960e9600000000, 0x5e3cc3f300000000, 0x28ddcc6e00000000, 0xf3f8ad1200000000, 0x8519a28f00000000, 0x73ad36d900000000, 0x054c394400000000, 0xde69583800000000, 0xa88857a500000000, 0x68229ac000000000, 0x1ec3955d00000000, 0xc5e6f42100000000, 0xb307fbbc00000000, 0xe2ef738300000000, 0x940e7c1e00000000, 0x4f2b1d6200000000, 0x39ca12ff00000000, 0xf960df9a00000000, 0x8f81d00700000000, 0x54a4b17b00000000, 0x2245bee600000000, 0xd4f12ab000000000, 0xa210252d00000000, 0x7935445100000000, 0x0fd44bcc00000000, 0xcf7e86a900000000, 0xb99f893400000000, 0x62bae84800000000, 0x145be7d500000000, 0x8ed3c1e500000000, 0xf832ce7800000000, 0x2317af0400000000, 0x55f6a09900000000, 0x955c6dfc00000000, 0xe3bd626100000000, 0x3898031d00000000, 0x4e790c8000000000, 0xb8cd98d600000000, 0xce2c974b00000000, 0x1509f63700000000, 0x63e8f9aa00000000, 0xa34234cf00000000, 0xd5a33b5200000000, 0x0e865a2e00000000, 0x786755b300000000, 0x3a97174e00000000, 0x4c7618d300000000, 0x975379af00000000, 0xe1b2763200000000, 0x2118bb5700000000, 0x57f9b4ca00000000, 0x8cdcd5b600000000, 0xfa3dda2b00000000, 0x0c894e7d00000000, 0x7a6841e000000000, 0xa14d209c00000000, 0xd7ac2f0100000000, 0x1706e26400000000, 0x61e7edf900000000, 0xbac28c8500000000, 0xcc23831800000000, 0x56aba52800000000, 0x204aaab500000000, 0xfb6fcbc900000000, 0x8d8ec45400000000, 0x4d24093100000000, 0x3bc506ac00000000, 0xe0e067d000000000, 0x9601684d00000000, 0x60b5fc1b00000000, 0x1654f38600000000, 0xcd7192fa00000000, 0xbb909d6700000000, 0x7b3a500200000000, 0x0ddb5f9f00000000, 0xd6fe3ee300000000, 0xa01f317e00000000, 0x1318cac200000000, 0x65f9c55f00000000, 0xbedca42300000000, 0xc83dabbe00000000, 0x089766db00000000, 0x7e76694600000000, 0xa553083a00000000, 0xd3b207a700000000, 0x250693f100000000, 0x53e79c6c00000000, 0x88c2fd1000000000, 0xfe23f28d00000000, 0x3e893fe800000000, 0x4868307500000000, 0x934d510900000000, 0xe5ac5e9400000000, 0x7f2478a400000000, 0x09c5773900000000, 0xd2e0164500000000, 0xa40119d800000000, 0x64abd4bd00000000, 0x124adb2000000000, 0xc96fba5c00000000, 0xbf8eb5c100000000, 0x493a219700000000, 0x3fdb2e0a00000000, 0xe4fe4f7600000000, 0x921f40eb00000000, 0x52b58d8e00000000, 0x2454821300000000, 0xff71e36f00000000, 0x8990ecf200000000, 0xcb60ae0f00000000, 0xbd81a19200000000, 0x66a4c0ee00000000, 0x1045cf7300000000, 0xd0ef021600000000, 0xa60e0d8b00000000, 0x7d2b6cf700000000, 0x0bca636a00000000, 0xfd7ef73c00000000, 0x8b9ff8a100000000, 0x50ba99dd00000000, 0x265b964000000000, 0xe6f15b2500000000, 0x901054b800000000, 0x4b3535c400000000, 0x3dd43a5900000000, 0xa75c1c6900000000, 0xd1bd13f400000000, 0x0a98728800000000, 0x7c797d1500000000, 0xbcd3b07000000000, 0xca32bfed00000000, 0x1117de9100000000, 0x67f6d10c00000000, 0x9142455a00000000, 0xe7a34ac700000000, 0x3c862bbb00000000, 0x4a67242600000000, 0x8acde94300000000, 0xfc2ce6de00000000, 0x270987a200000000, 0x51e8883f00000000}, {0x0000000000000000, 0xe8dbfbb900000000, 0x91b186a800000000, 0x796a7d1100000000, 0x63657c8a00000000, 0x8bbe873300000000, 0xf2d4fa2200000000, 0x1a0f019b00000000, 0x87cc89cf00000000, 0x6f17727600000000, 0x167d0f6700000000, 0xfea6f4de00000000, 0xe4a9f54500000000, 0x0c720efc00000000, 0x751873ed00000000, 0x9dc3885400000000, 0x4f9f624400000000, 0xa74499fd00000000, 0xde2ee4ec00000000, 0x36f51f5500000000, 0x2cfa1ece00000000, 0xc421e57700000000, 0xbd4b986600000000, 0x559063df00000000, 0xc853eb8b00000000, 0x2088103200000000, 0x59e26d2300000000, 0xb139969a00000000, 0xab36970100000000, 0x43ed6cb800000000, 0x3a8711a900000000, 0xd25cea1000000000, 0x9e3ec58800000000, 0x76e53e3100000000, 0x0f8f432000000000, 0xe754b89900000000, 0xfd5bb90200000000, 0x158042bb00000000, 0x6cea3faa00000000, 0x8431c41300000000, 0x19f24c4700000000, 0xf129b7fe00000000, 0x8843caef00000000, 0x6098315600000000, 0x7a9730cd00000000, 0x924ccb7400000000, 0xeb26b66500000000, 0x03fd4ddc00000000, 0xd1a1a7cc00000000, 0x397a5c7500000000, 0x4010216400000000, 0xa8cbdadd00000000, 0xb2c4db4600000000, 0x5a1f20ff00000000, 0x23755dee00000000, 0xcbaea65700000000, 0x566d2e0300000000, 0xbeb6d5ba00000000, 0xc7dca8ab00000000, 0x2f07531200000000, 0x3508528900000000, 0xddd3a93000000000, 0xa4b9d42100000000, 0x4c622f9800000000, 0x7d7bfbca00000000, 0x95a0007300000000, 0xecca7d6200000000, 0x041186db00000000, 0x1e1e874000000000, 0xf6c57cf900000000, 0x8faf01e800000000, 0x6774fa5100000000, 0xfab7720500000000, 0x126c89bc00000000, 0x6b06f4ad00000000, 0x83dd0f1400000000, 0x99d20e8f00000000, 0x7109f53600000000, 0x0863882700000000, 0xe0b8739e00000000, 0x32e4998e00000000, 0xda3f623700000000, 0xa3551f2600000000, 0x4b8ee49f00000000, 0x5181e50400000000, 0xb95a1ebd00000000, 0xc03063ac00000000, 0x28eb981500000000, 0xb528104100000000, 0x5df3ebf800000000, 0x249996e900000000, 0xcc426d5000000000, 0xd64d6ccb00000000, 0x3e96977200000000, 0x47fcea6300000000, 0xaf2711da00000000, 0xe3453e4200000000, 0x0b9ec5fb00000000, 0x72f4b8ea00000000, 0x9a2f435300000000, 0x802042c800000000, 0x68fbb97100000000, 0x1191c46000000000, 0xf94a3fd900000000, 0x6489b78d00000000, 0x8c524c3400000000, 0xf538312500000000, 0x1de3ca9c00000000, 0x07eccb0700000000, 0xef3730be00000000, 0x965d4daf00000000, 0x7e86b61600000000, 0xacda5c0600000000, 0x4401a7bf00000000, 0x3d6bdaae00000000, 0xd5b0211700000000, 0xcfbf208c00000000, 0x2764db3500000000, 0x5e0ea62400000000, 0xb6d55d9d00000000, 0x2b16d5c900000000, 0xc3cd2e7000000000, 0xbaa7536100000000, 0x527ca8d800000000, 0x4873a94300000000, 0xa0a852fa00000000, 0xd9c22feb00000000, 0x3119d45200000000, 0xbbf0874e00000000, 0x532b7cf700000000, 0x2a4101e600000000, 0xc29afa5f00000000, 0xd895fbc400000000, 0x304e007d00000000, 0x49247d6c00000000, 0xa1ff86d500000000, 0x3c3c0e8100000000, 0xd4e7f53800000000, 0xad8d882900000000, 0x4556739000000000, 0x5f59720b00000000, 0xb78289b200000000, 0xcee8f4a300000000, 0x26330f1a00000000, 0xf46fe50a00000000, 0x1cb41eb300000000, 0x65de63a200000000, 0x8d05981b00000000, 0x970a998000000000, 0x7fd1623900000000, 0x06bb1f2800000000, 0xee60e49100000000, 0x73a36cc500000000, 0x9b78977c00000000, 0xe212ea6d00000000, 0x0ac911d400000000, 0x10c6104f00000000, 0xf81debf600000000, 0x817796e700000000, 0x69ac6d5e00000000, 0x25ce42c600000000, 0xcd15b97f00000000, 0xb47fc46e00000000, 0x5ca43fd700000000, 0x46ab3e4c00000000, 0xae70c5f500000000, 0xd71ab8e400000000, 0x3fc1435d00000000, 0xa202cb0900000000, 0x4ad930b000000000, 0x33b34da100000000, 0xdb68b61800000000, 0xc167b78300000000, 0x29bc4c3a00000000, 0x50d6312b00000000, 0xb80dca9200000000, 0x6a51208200000000, 0x828adb3b00000000, 0xfbe0a62a00000000, 0x133b5d9300000000, 0x09345c0800000000, 0xe1efa7b100000000, 0x9885daa000000000, 0x705e211900000000, 0xed9da94d00000000, 0x054652f400000000, 0x7c2c2fe500000000, 0x94f7d45c00000000, 0x8ef8d5c700000000, 0x66232e7e00000000, 0x1f49536f00000000, 0xf792a8d600000000, 0xc68b7c8400000000, 0x2e50873d00000000, 0x573afa2c00000000, 0xbfe1019500000000, 0xa5ee000e00000000, 0x4d35fbb700000000, 0x345f86a600000000, 0xdc847d1f00000000, 0x4147f54b00000000, 0xa99c0ef200000000, 0xd0f673e300000000, 0x382d885a00000000, 0x222289c100000000, 0xcaf9727800000000, 0xb3930f6900000000, 0x5b48f4d000000000, 0x89141ec000000000, 0x61cfe57900000000, 0x18a5986800000000, 0xf07e63d100000000, 0xea71624a00000000, 0x02aa99f300000000, 0x7bc0e4e200000000, 0x931b1f5b00000000, 0x0ed8970f00000000, 0xe6036cb600000000, 0x9f6911a700000000, 0x77b2ea1e00000000, 0x6dbdeb8500000000, 0x8566103c00000000, 0xfc0c6d2d00000000, 0x14d7969400000000, 0x58b5b90c00000000, 0xb06e42b500000000, 0xc9043fa400000000, 0x21dfc41d00000000, 0x3bd0c58600000000, 0xd30b3e3f00000000, 0xaa61432e00000000, 0x42bab89700000000, 0xdf7930c300000000, 0x37a2cb7a00000000, 0x4ec8b66b00000000, 0xa6134dd200000000, 0xbc1c4c4900000000, 0x54c7b7f000000000, 0x2dadcae100000000, 0xc576315800000000, 0x172adb4800000000, 0xfff120f100000000, 0x869b5de000000000, 0x6e40a65900000000, 0x744fa7c200000000, 0x9c945c7b00000000, 0xe5fe216a00000000, 0x0d25dad300000000, 0x90e6528700000000, 0x783da93e00000000, 0x0157d42f00000000, 0xe98c2f9600000000, 0xf3832e0d00000000, 0x1b58d5b400000000, 0x6232a8a500000000, 0x8ae9531c00000000}, {0x0000000000000000, 0x919168ae00000000, 0x6325a08700000000, 0xf2b4c82900000000, 0x874c31d400000000, 0x16dd597a00000000, 0xe469915300000000, 0x75f8f9fd00000000, 0x4f9f137300000000, 0xde0e7bdd00000000, 0x2cbab3f400000000, 0xbd2bdb5a00000000, 0xc8d322a700000000, 0x59424a0900000000, 0xabf6822000000000, 0x3a67ea8e00000000, 0x9e3e27e600000000, 0x0faf4f4800000000, 0xfd1b876100000000, 0x6c8aefcf00000000, 0x1972163200000000, 0x88e37e9c00000000, 0x7a57b6b500000000, 0xebc6de1b00000000, 0xd1a1349500000000, 0x40305c3b00000000, 0xb284941200000000, 0x2315fcbc00000000, 0x56ed054100000000, 0xc77c6def00000000, 0x35c8a5c600000000, 0xa459cd6800000000, 0x7d7b3f1700000000, 0xecea57b900000000, 0x1e5e9f9000000000, 0x8fcff73e00000000, 0xfa370ec300000000, 0x6ba6666d00000000, 0x9912ae4400000000, 0x0883c6ea00000000, 0x32e42c6400000000, 0xa37544ca00000000, 0x51c18ce300000000, 0xc050e44d00000000, 0xb5a81db000000000, 0x2439751e00000000, 0xd68dbd3700000000, 0x471cd59900000000, 0xe34518f100000000, 0x72d4705f00000000, 0x8060b87600000000, 0x11f1d0d800000000, 0x6409292500000000, 0xf598418b00000000, 0x072c89a200000000, 0x96bde10c00000000, 0xacda0b8200000000, 0x3d4b632c00000000, 0xcfffab0500000000, 0x5e6ec3ab00000000, 0x2b963a5600000000, 0xba0752f800000000, 0x48b39ad100000000, 0xd922f27f00000000, 0xfaf67e2e00000000, 0x6b67168000000000, 0x99d3dea900000000, 0x0842b60700000000, 0x7dba4ffa00000000, 0xec2b275400000000, 0x1e9fef7d00000000, 0x8f0e87d300000000, 0xb5696d5d00000000, 0x24f805f300000000, 0xd64ccdda00000000, 0x47dda57400000000, 0x32255c8900000000, 0xa3b4342700000000, 0x5100fc0e00000000, 0xc09194a000000000, 0x64c859c800000000, 0xf559316600000000, 0x07edf94f00000000, 0x967c91e100000000, 0xe384681c00000000, 0x721500b200000000, 0x80a1c89b00000000, 0x1130a03500000000, 0x2b574abb00000000, 0xbac6221500000000, 0x4872ea3c00000000, 0xd9e3829200000000, 0xac1b7b6f00000000, 0x3d8a13c100000000, 0xcf3edbe800000000, 0x5eafb34600000000, 0x878d413900000000, 0x161c299700000000, 0xe4a8e1be00000000, 0x7539891000000000, 0x00c170ed00000000, 0x9150184300000000, 0x63e4d06a00000000, 0xf275b8c400000000, 0xc812524a00000000, 0x59833ae400000000, 0xab37f2cd00000000, 0x3aa69a6300000000, 0x4f5e639e00000000, 0xdecf0b3000000000, 0x2c7bc31900000000, 0xbdeaabb700000000, 0x19b366df00000000, 0x88220e7100000000, 0x7a96c65800000000, 0xeb07aef600000000, 0x9eff570b00000000, 0x0f6e3fa500000000, 0xfddaf78c00000000, 0x6c4b9f2200000000, 0x562c75ac00000000, 0xc7bd1d0200000000, 0x3509d52b00000000, 0xa498bd8500000000, 0xd160447800000000, 0x40f12cd600000000, 0xb245e4ff00000000, 0x23d48c5100000000, 0xf4edfd5c00000000, 0x657c95f200000000, 0x97c85ddb00000000, 0x0659357500000000, 0x73a1cc8800000000, 0xe230a42600000000, 0x10846c0f00000000, 0x811504a100000000, 0xbb72ee2f00000000, 0x2ae3868100000000, 0xd8574ea800000000, 0x49c6260600000000, 0x3c3edffb00000000, 0xadafb75500000000, 0x5f1b7f7c00000000, 0xce8a17d200000000, 0x6ad3daba00000000, 0xfb42b21400000000, 0x09f67a3d00000000, 0x9867129300000000, 0xed9feb6e00000000, 0x7c0e83c000000000, 0x8eba4be900000000, 0x1f2b234700000000, 0x254cc9c900000000, 0xb4dda16700000000, 0x4669694e00000000, 0xd7f801e000000000, 0xa200f81d00000000, 0x339190b300000000, 0xc125589a00000000, 0x50b4303400000000, 0x8996c24b00000000, 0x1807aae500000000, 0xeab362cc00000000, 0x7b220a6200000000, 0x0edaf39f00000000, 0x9f4b9b3100000000, 0x6dff531800000000, 0xfc6e3bb600000000, 0xc609d13800000000, 0x5798b99600000000, 0xa52c71bf00000000, 0x34bd191100000000, 0x4145e0ec00000000, 0xd0d4884200000000, 0x2260406b00000000, 0xb3f128c500000000, 0x17a8e5ad00000000, 0x86398d0300000000, 0x748d452a00000000, 0xe51c2d8400000000, 0x90e4d47900000000, 0x0175bcd700000000, 0xf3c174fe00000000, 0x62501c5000000000, 0x5837f6de00000000, 0xc9a69e7000000000, 0x3b12565900000000, 0xaa833ef700000000, 0xdf7bc70a00000000, 0x4eeaafa400000000, 0xbc5e678d00000000, 0x2dcf0f2300000000, 0x0e1b837200000000, 0x9f8aebdc00000000, 0x6d3e23f500000000, 0xfcaf4b5b00000000, 0x8957b2a600000000, 0x18c6da0800000000, 0xea72122100000000, 0x7be37a8f00000000, 0x4184900100000000, 0xd015f8af00000000, 0x22a1308600000000, 0xb330582800000000, 0xc6c8a1d500000000, 0x5759c97b00000000, 0xa5ed015200000000, 0x347c69fc00000000, 0x9025a49400000000, 0x01b4cc3a00000000, 0xf300041300000000, 0x62916cbd00000000, 0x1769954000000000, 0x86f8fdee00000000, 0x744c35c700000000, 0xe5dd5d6900000000, 0xdfbab7e700000000, 0x4e2bdf4900000000, 0xbc9f176000000000, 0x2d0e7fce00000000, 0x58f6863300000000, 0xc967ee9d00000000, 0x3bd326b400000000, 0xaa424e1a00000000, 0x7360bc6500000000, 0xe2f1d4cb00000000, 0x10451ce200000000, 0x81d4744c00000000, 0xf42c8db100000000, 0x65bde51f00000000, 0x97092d3600000000, 0x0698459800000000, 0x3cffaf1600000000, 0xad6ec7b800000000, 0x5fda0f9100000000, 0xce4b673f00000000, 0xbbb39ec200000000, 0x2a22f66c00000000, 0xd8963e4500000000, 0x490756eb00000000, 0xed5e9b8300000000, 0x7ccff32d00000000, 0x8e7b3b0400000000, 0x1fea53aa00000000, 0x6a12aa5700000000, 0xfb83c2f900000000, 0x09370ad000000000, 0x98a6627e00000000, 0xa2c188f000000000, 0x3350e05e00000000, 0xc1e4287700000000, 0x507540d900000000, 0x258db92400000000, 0xb41cd18a00000000, 0x46a819a300000000, 0xd739710d00000000}}; #else /* W == 4 */ local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0xccaa009e, 0x4225077d, 0x8e8f07e3, 0x844a0efa, 0x48e00e64, 0xc66f0987, 0x0ac50919, 0xd3e51bb5, 0x1f4f1b2b, 0x91c01cc8, 0x5d6a1c56, 0x57af154f, 0x9b0515d1, 0x158a1232, 0xd92012ac, 0x7cbb312b, 0xb01131b5, 0x3e9e3656, 0xf23436c8, 0xf8f13fd1, 0x345b3f4f, 0xbad438ac, 0x767e3832, 0xaf5e2a9e, 0x63f42a00, 0xed7b2de3, 0x21d12d7d, 0x2b142464, 0xe7be24fa, 0x69312319, 0xa59b2387, 0xf9766256, 0x35dc62c8, 0xbb53652b, 0x77f965b5, 0x7d3c6cac, 0xb1966c32, 0x3f196bd1, 0xf3b36b4f, 0x2a9379e3, 0xe639797d, 0x68b67e9e, 0xa41c7e00, 0xaed97719, 0x62737787, 0xecfc7064, 0x205670fa, 0x85cd537d, 0x496753e3, 0xc7e85400, 0x0b42549e, 0x01875d87, 0xcd2d5d19, 0x43a25afa, 0x8f085a64, 0x562848c8, 0x9a824856, 0x140d4fb5, 0xd8a74f2b, 0xd2624632, 0x1ec846ac, 0x9047414f, 0x5ced41d1, 0x299dc2ed, 0xe537c273, 0x6bb8c590, 0xa712c50e, 0xadd7cc17, 0x617dcc89, 0xeff2cb6a, 0x2358cbf4, 0xfa78d958, 0x36d2d9c6, 0xb85dde25, 0x74f7debb, 0x7e32d7a2, 0xb298d73c, 0x3c17d0df, 0xf0bdd041, 0x5526f3c6, 0x998cf358, 0x1703f4bb, 0xdba9f425, 0xd16cfd3c, 0x1dc6fda2, 0x9349fa41, 0x5fe3fadf, 0x86c3e873, 0x4a69e8ed, 0xc4e6ef0e, 0x084cef90, 0x0289e689, 0xce23e617, 0x40ace1f4, 0x8c06e16a, 0xd0eba0bb, 0x1c41a025, 0x92cea7c6, 0x5e64a758, 0x54a1ae41, 0x980baedf, 0x1684a93c, 0xda2ea9a2, 0x030ebb0e, 0xcfa4bb90, 0x412bbc73, 0x8d81bced, 0x8744b5f4, 0x4beeb56a, 0xc561b289, 0x09cbb217, 0xac509190, 0x60fa910e, 0xee7596ed, 0x22df9673, 0x281a9f6a, 0xe4b09ff4, 0x6a3f9817, 0xa6959889, 0x7fb58a25, 0xb31f8abb, 0x3d908d58, 0xf13a8dc6, 0xfbff84df, 0x37558441, 0xb9da83a2, 0x7570833c, 0x533b85da, 0x9f918544, 0x111e82a7, 0xddb48239, 0xd7718b20, 0x1bdb8bbe, 0x95548c5d, 0x59fe8cc3, 0x80de9e6f, 0x4c749ef1, 0xc2fb9912, 0x0e51998c, 0x04949095, 0xc83e900b, 0x46b197e8, 0x8a1b9776, 0x2f80b4f1, 0xe32ab46f, 0x6da5b38c, 0xa10fb312, 0xabcaba0b, 0x6760ba95, 0xe9efbd76, 0x2545bde8, 0xfc65af44, 0x30cfafda, 0xbe40a839, 0x72eaa8a7, 0x782fa1be, 0xb485a120, 0x3a0aa6c3, 0xf6a0a65d, 0xaa4de78c, 0x66e7e712, 0xe868e0f1, 0x24c2e06f, 0x2e07e976, 0xe2ade9e8, 0x6c22ee0b, 0xa088ee95, 0x79a8fc39, 0xb502fca7, 0x3b8dfb44, 0xf727fbda, 0xfde2f2c3, 0x3148f25d, 0xbfc7f5be, 0x736df520, 0xd6f6d6a7, 0x1a5cd639, 0x94d3d1da, 0x5879d144, 0x52bcd85d, 0x9e16d8c3, 0x1099df20, 0xdc33dfbe, 0x0513cd12, 0xc9b9cd8c, 0x4736ca6f, 0x8b9ccaf1, 0x8159c3e8, 0x4df3c376, 0xc37cc495, 0x0fd6c40b, 0x7aa64737, 0xb60c47a9, 0x3883404a, 0xf42940d4, 0xfeec49cd, 0x32464953, 0xbcc94eb0, 0x70634e2e, 0xa9435c82, 0x65e95c1c, 0xeb665bff, 0x27cc5b61, 0x2d095278, 0xe1a352e6, 0x6f2c5505, 0xa386559b, 0x061d761c, 0xcab77682, 0x44387161, 0x889271ff, 0x825778e6, 0x4efd7878, 0xc0727f9b, 0x0cd87f05, 0xd5f86da9, 0x19526d37, 0x97dd6ad4, 0x5b776a4a, 0x51b26353, 0x9d1863cd, 0x1397642e, 0xdf3d64b0, 0x83d02561, 0x4f7a25ff, 0xc1f5221c, 0x0d5f2282, 0x079a2b9b, 0xcb302b05, 0x45bf2ce6, 0x89152c78, 0x50353ed4, 0x9c9f3e4a, 0x121039a9, 0xdeba3937, 0xd47f302e, 0x18d530b0, 0x965a3753, 0x5af037cd, 0xff6b144a, 0x33c114d4, 0xbd4e1337, 0x71e413a9, 0x7b211ab0, 0xb78b1a2e, 0x39041dcd, 0xf5ae1d53, 0x2c8e0fff, 0xe0240f61, 0x6eab0882, 0xa201081c, 0xa8c40105, 0x646e019b, 0xeae10678, 0x264b06e6}, {0x00000000, 0xa6770bb4, 0x979f1129, 0x31e81a9d, 0xf44f2413, 0x52382fa7, 0x63d0353a, 0xc5a73e8e, 0x33ef4e67, 0x959845d3, 0xa4705f4e, 0x020754fa, 0xc7a06a74, 0x61d761c0, 0x503f7b5d, 0xf64870e9, 0x67de9cce, 0xc1a9977a, 0xf0418de7, 0x56368653, 0x9391b8dd, 0x35e6b369, 0x040ea9f4, 0xa279a240, 0x5431d2a9, 0xf246d91d, 0xc3aec380, 0x65d9c834, 0xa07ef6ba, 0x0609fd0e, 0x37e1e793, 0x9196ec27, 0xcfbd399c, 0x69ca3228, 0x582228b5, 0xfe552301, 0x3bf21d8f, 0x9d85163b, 0xac6d0ca6, 0x0a1a0712, 0xfc5277fb, 0x5a257c4f, 0x6bcd66d2, 0xcdba6d66, 0x081d53e8, 0xae6a585c, 0x9f8242c1, 0x39f54975, 0xa863a552, 0x0e14aee6, 0x3ffcb47b, 0x998bbfcf, 0x5c2c8141, 0xfa5b8af5, 0xcbb39068, 0x6dc49bdc, 0x9b8ceb35, 0x3dfbe081, 0x0c13fa1c, 0xaa64f1a8, 0x6fc3cf26, 0xc9b4c492, 0xf85cde0f, 0x5e2bd5bb, 0x440b7579, 0xe27c7ecd, 0xd3946450, 0x75e36fe4, 0xb044516a, 0x16335ade, 0x27db4043, 0x81ac4bf7, 0x77e43b1e, 0xd19330aa, 0xe07b2a37, 0x460c2183, 0x83ab1f0d, 0x25dc14b9, 0x14340e24, 0xb2430590, 0x23d5e9b7, 0x85a2e203, 0xb44af89e, 0x123df32a, 0xd79acda4, 0x71edc610, 0x4005dc8d, 0xe672d739, 0x103aa7d0, 0xb64dac64, 0x87a5b6f9, 0x21d2bd4d, 0xe47583c3, 0x42028877, 0x73ea92ea, 0xd59d995e, 0x8bb64ce5, 0x2dc14751, 0x1c295dcc, 0xba5e5678, 0x7ff968f6, 0xd98e6342, 0xe86679df, 0x4e11726b, 0xb8590282, 0x1e2e0936, 0x2fc613ab, 0x89b1181f, 0x4c162691, 0xea612d25, 0xdb8937b8, 0x7dfe3c0c, 0xec68d02b, 0x4a1fdb9f, 0x7bf7c102, 0xdd80cab6, 0x1827f438, 0xbe50ff8c, 0x8fb8e511, 0x29cfeea5, 0xdf879e4c, 0x79f095f8, 0x48188f65, 0xee6f84d1, 0x2bc8ba5f, 0x8dbfb1eb, 0xbc57ab76, 0x1a20a0c2, 0x8816eaf2, 0x2e61e146, 0x1f89fbdb, 0xb9fef06f, 0x7c59cee1, 0xda2ec555, 0xebc6dfc8, 0x4db1d47c, 0xbbf9a495, 0x1d8eaf21, 0x2c66b5bc, 0x8a11be08, 0x4fb68086, 0xe9c18b32, 0xd82991af, 0x7e5e9a1b, 0xefc8763c, 0x49bf7d88, 0x78576715, 0xde206ca1, 0x1b87522f, 0xbdf0599b, 0x8c184306, 0x2a6f48b2, 0xdc27385b, 0x7a5033ef, 0x4bb82972, 0xedcf22c6, 0x28681c48, 0x8e1f17fc, 0xbff70d61, 0x198006d5, 0x47abd36e, 0xe1dcd8da, 0xd034c247, 0x7643c9f3, 0xb3e4f77d, 0x1593fcc9, 0x247be654, 0x820cede0, 0x74449d09, 0xd23396bd, 0xe3db8c20, 0x45ac8794, 0x800bb91a, 0x267cb2ae, 0x1794a833, 0xb1e3a387, 0x20754fa0, 0x86024414, 0xb7ea5e89, 0x119d553d, 0xd43a6bb3, 0x724d6007, 0x43a57a9a, 0xe5d2712e, 0x139a01c7, 0xb5ed0a73, 0x840510ee, 0x22721b5a, 0xe7d525d4, 0x41a22e60, 0x704a34fd, 0xd63d3f49, 0xcc1d9f8b, 0x6a6a943f, 0x5b828ea2, 0xfdf58516, 0x3852bb98, 0x9e25b02c, 0xafcdaab1, 0x09baa105, 0xfff2d1ec, 0x5985da58, 0x686dc0c5, 0xce1acb71, 0x0bbdf5ff, 0xadcafe4b, 0x9c22e4d6, 0x3a55ef62, 0xabc30345, 0x0db408f1, 0x3c5c126c, 0x9a2b19d8, 0x5f8c2756, 0xf9fb2ce2, 0xc813367f, 0x6e643dcb, 0x982c4d22, 0x3e5b4696, 0x0fb35c0b, 0xa9c457bf, 0x6c636931, 0xca146285, 0xfbfc7818, 0x5d8b73ac, 0x03a0a617, 0xa5d7ada3, 0x943fb73e, 0x3248bc8a, 0xf7ef8204, 0x519889b0, 0x6070932d, 0xc6079899, 0x304fe870, 0x9638e3c4, 0xa7d0f959, 0x01a7f2ed, 0xc400cc63, 0x6277c7d7, 0x539fdd4a, 0xf5e8d6fe, 0x647e3ad9, 0xc209316d, 0xf3e12bf0, 0x55962044, 0x90311eca, 0x3646157e, 0x07ae0fe3, 0xa1d90457, 0x579174be, 0xf1e67f0a, 0xc00e6597, 0x66796e23, 0xa3de50ad, 0x05a95b19, 0x34414184, 0x92364a30}, {0x00000000, 0xcb5cd3a5, 0x4dc8a10b, 0x869472ae, 0x9b914216, 0x50cd91b3, 0xd659e31d, 0x1d0530b8, 0xec53826d, 0x270f51c8, 0xa19b2366, 0x6ac7f0c3, 0x77c2c07b, 0xbc9e13de, 0x3a0a6170, 0xf156b2d5, 0x03d6029b, 0xc88ad13e, 0x4e1ea390, 0x85427035, 0x9847408d, 0x531b9328, 0xd58fe186, 0x1ed33223, 0xef8580f6, 0x24d95353, 0xa24d21fd, 0x6911f258, 0x7414c2e0, 0xbf481145, 0x39dc63eb, 0xf280b04e, 0x07ac0536, 0xccf0d693, 0x4a64a43d, 0x81387798, 0x9c3d4720, 0x57619485, 0xd1f5e62b, 0x1aa9358e, 0xebff875b, 0x20a354fe, 0xa6372650, 0x6d6bf5f5, 0x706ec54d, 0xbb3216e8, 0x3da66446, 0xf6fab7e3, 0x047a07ad, 0xcf26d408, 0x49b2a6a6, 0x82ee7503, 0x9feb45bb, 0x54b7961e, 0xd223e4b0, 0x197f3715, 0xe82985c0, 0x23755665, 0xa5e124cb, 0x6ebdf76e, 0x73b8c7d6, 0xb8e41473, 0x3e7066dd, 0xf52cb578, 0x0f580a6c, 0xc404d9c9, 0x4290ab67, 0x89cc78c2, 0x94c9487a, 0x5f959bdf, 0xd901e971, 0x125d3ad4, 0xe30b8801, 0x28575ba4, 0xaec3290a, 0x659ffaaf, 0x789aca17, 0xb3c619b2, 0x35526b1c, 0xfe0eb8b9, 0x0c8e08f7, 0xc7d2db52, 0x4146a9fc, 0x8a1a7a59, 0x971f4ae1, 0x5c439944, 0xdad7ebea, 0x118b384f, 0xe0dd8a9a, 0x2b81593f, 0xad152b91, 0x6649f834, 0x7b4cc88c, 0xb0101b29, 0x36846987, 0xfdd8ba22, 0x08f40f5a, 0xc3a8dcff, 0x453cae51, 0x8e607df4, 0x93654d4c, 0x58399ee9, 0xdeadec47, 0x15f13fe2, 0xe4a78d37, 0x2ffb5e92, 0xa96f2c3c, 0x6233ff99, 0x7f36cf21, 0xb46a1c84, 0x32fe6e2a, 0xf9a2bd8f, 0x0b220dc1, 0xc07ede64, 0x46eaacca, 0x8db67f6f, 0x90b34fd7, 0x5bef9c72, 0xdd7beedc, 0x16273d79, 0xe7718fac, 0x2c2d5c09, 0xaab92ea7, 0x61e5fd02, 0x7ce0cdba, 0xb7bc1e1f, 0x31286cb1, 0xfa74bf14, 0x1eb014d8, 0xd5ecc77d, 0x5378b5d3, 0x98246676, 0x852156ce, 0x4e7d856b, 0xc8e9f7c5, 0x03b52460, 0xf2e396b5, 0x39bf4510, 0xbf2b37be, 0x7477e41b, 0x6972d4a3, 0xa22e0706, 0x24ba75a8, 0xefe6a60d, 0x1d661643, 0xd63ac5e6, 0x50aeb748, 0x9bf264ed, 0x86f75455, 0x4dab87f0, 0xcb3ff55e, 0x006326fb, 0xf135942e, 0x3a69478b, 0xbcfd3525, 0x77a1e680, 0x6aa4d638, 0xa1f8059d, 0x276c7733, 0xec30a496, 0x191c11ee, 0xd240c24b, 0x54d4b0e5, 0x9f886340, 0x828d53f8, 0x49d1805d, 0xcf45f2f3, 0x04192156, 0xf54f9383, 0x3e134026, 0xb8873288, 0x73dbe12d, 0x6eded195, 0xa5820230, 0x2316709e, 0xe84aa33b, 0x1aca1375, 0xd196c0d0, 0x5702b27e, 0x9c5e61db, 0x815b5163, 0x4a0782c6, 0xcc93f068, 0x07cf23cd, 0xf6999118, 0x3dc542bd, 0xbb513013, 0x700de3b6, 0x6d08d30e, 0xa65400ab, 0x20c07205, 0xeb9ca1a0, 0x11e81eb4, 0xdab4cd11, 0x5c20bfbf, 0x977c6c1a, 0x8a795ca2, 0x41258f07, 0xc7b1fda9, 0x0ced2e0c, 0xfdbb9cd9, 0x36e74f7c, 0xb0733dd2, 0x7b2fee77, 0x662adecf, 0xad760d6a, 0x2be27fc4, 0xe0beac61, 0x123e1c2f, 0xd962cf8a, 0x5ff6bd24, 0x94aa6e81, 0x89af5e39, 0x42f38d9c, 0xc467ff32, 0x0f3b2c97, 0xfe6d9e42, 0x35314de7, 0xb3a53f49, 0x78f9ecec, 0x65fcdc54, 0xaea00ff1, 0x28347d5f, 0xe368aefa, 0x16441b82, 0xdd18c827, 0x5b8cba89, 0x90d0692c, 0x8dd55994, 0x46898a31, 0xc01df89f, 0x0b412b3a, 0xfa1799ef, 0x314b4a4a, 0xb7df38e4, 0x7c83eb41, 0x6186dbf9, 0xaada085c, 0x2c4e7af2, 0xe712a957, 0x15921919, 0xdececabc, 0x585ab812, 0x93066bb7, 0x8e035b0f, 0x455f88aa, 0xc3cbfa04, 0x089729a1, 0xf9c19b74, 0x329d48d1, 0xb4093a7f, 0x7f55e9da, 0x6250d962, 0xa90c0ac7, 0x2f987869, 0xe4c4abcc}, {0x00000000, 0x3d6029b0, 0x7ac05360, 0x47a07ad0, 0xf580a6c0, 0xc8e08f70, 0x8f40f5a0, 0xb220dc10, 0x30704bc1, 0x0d106271, 0x4ab018a1, 0x77d03111, 0xc5f0ed01, 0xf890c4b1, 0xbf30be61, 0x825097d1, 0x60e09782, 0x5d80be32, 0x1a20c4e2, 0x2740ed52, 0x95603142, 0xa80018f2, 0xefa06222, 0xd2c04b92, 0x5090dc43, 0x6df0f5f3, 0x2a508f23, 0x1730a693, 0xa5107a83, 0x98705333, 0xdfd029e3, 0xe2b00053, 0xc1c12f04, 0xfca106b4, 0xbb017c64, 0x866155d4, 0x344189c4, 0x0921a074, 0x4e81daa4, 0x73e1f314, 0xf1b164c5, 0xccd14d75, 0x8b7137a5, 0xb6111e15, 0x0431c205, 0x3951ebb5, 0x7ef19165, 0x4391b8d5, 0xa121b886, 0x9c419136, 0xdbe1ebe6, 0xe681c256, 0x54a11e46, 0x69c137f6, 0x2e614d26, 0x13016496, 0x9151f347, 0xac31daf7, 0xeb91a027, 0xd6f18997, 0x64d15587, 0x59b17c37, 0x1e1106e7, 0x23712f57, 0x58f35849, 0x659371f9, 0x22330b29, 0x1f532299, 0xad73fe89, 0x9013d739, 0xd7b3ade9, 0xead38459, 0x68831388, 0x55e33a38, 0x124340e8, 0x2f236958, 0x9d03b548, 0xa0639cf8, 0xe7c3e628, 0xdaa3cf98, 0x3813cfcb, 0x0573e67b, 0x42d39cab, 0x7fb3b51b, 0xcd93690b, 0xf0f340bb, 0xb7533a6b, 0x8a3313db, 0x0863840a, 0x3503adba, 0x72a3d76a, 0x4fc3feda, 0xfde322ca, 0xc0830b7a, 0x872371aa, 0xba43581a, 0x9932774d, 0xa4525efd, 0xe3f2242d, 0xde920d9d, 0x6cb2d18d, 0x51d2f83d, 0x167282ed, 0x2b12ab5d, 0xa9423c8c, 0x9422153c, 0xd3826fec, 0xeee2465c, 0x5cc29a4c, 0x61a2b3fc, 0x2602c92c, 0x1b62e09c, 0xf9d2e0cf, 0xc4b2c97f, 0x8312b3af, 0xbe729a1f, 0x0c52460f, 0x31326fbf, 0x7692156f, 0x4bf23cdf, 0xc9a2ab0e, 0xf4c282be, 0xb362f86e, 0x8e02d1de, 0x3c220dce, 0x0142247e, 0x46e25eae, 0x7b82771e, 0xb1e6b092, 0x8c869922, 0xcb26e3f2, 0xf646ca42, 0x44661652, 0x79063fe2, 0x3ea64532, 0x03c66c82, 0x8196fb53, 0xbcf6d2e3, 0xfb56a833, 0xc6368183, 0x74165d93, 0x49767423, 0x0ed60ef3, 0x33b62743, 0xd1062710, 0xec660ea0, 0xabc67470, 0x96a65dc0, 0x248681d0, 0x19e6a860, 0x5e46d2b0, 0x6326fb00, 0xe1766cd1, 0xdc164561, 0x9bb63fb1, 0xa6d61601, 0x14f6ca11, 0x2996e3a1, 0x6e369971, 0x5356b0c1, 0x70279f96, 0x4d47b626, 0x0ae7ccf6, 0x3787e546, 0x85a73956, 0xb8c710e6, 0xff676a36, 0xc2074386, 0x4057d457, 0x7d37fde7, 0x3a978737, 0x07f7ae87, 0xb5d77297, 0x88b75b27, 0xcf1721f7, 0xf2770847, 0x10c70814, 0x2da721a4, 0x6a075b74, 0x576772c4, 0xe547aed4, 0xd8278764, 0x9f87fdb4, 0xa2e7d404, 0x20b743d5, 0x1dd76a65, 0x5a7710b5, 0x67173905, 0xd537e515, 0xe857cca5, 0xaff7b675, 0x92979fc5, 0xe915e8db, 0xd475c16b, 0x93d5bbbb, 0xaeb5920b, 0x1c954e1b, 0x21f567ab, 0x66551d7b, 0x5b3534cb, 0xd965a31a, 0xe4058aaa, 0xa3a5f07a, 0x9ec5d9ca, 0x2ce505da, 0x11852c6a, 0x562556ba, 0x6b457f0a, 0x89f57f59, 0xb49556e9, 0xf3352c39, 0xce550589, 0x7c75d999, 0x4115f029, 0x06b58af9, 0x3bd5a349, 0xb9853498, 0x84e51d28, 0xc34567f8, 0xfe254e48, 0x4c059258, 0x7165bbe8, 0x36c5c138, 0x0ba5e888, 0x28d4c7df, 0x15b4ee6f, 0x521494bf, 0x6f74bd0f, 0xdd54611f, 0xe03448af, 0xa794327f, 0x9af41bcf, 0x18a48c1e, 0x25c4a5ae, 0x6264df7e, 0x5f04f6ce, 0xed242ade, 0xd044036e, 0x97e479be, 0xaa84500e, 0x4834505d, 0x755479ed, 0x32f4033d, 0x0f942a8d, 0xbdb4f69d, 0x80d4df2d, 0xc774a5fd, 0xfa148c4d, 0x78441b9c, 0x4524322c, 0x028448fc, 0x3fe4614c, 0x8dc4bd5c, 0xb0a494ec, 0xf704ee3c, 0xca64c78c}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x00000000, 0xb029603d, 0x6053c07a, 0xd07aa047, 0xc0a680f5, 0x708fe0c8, 0xa0f5408f, 0x10dc20b2, 0xc14b7030, 0x7162100d, 0xa118b04a, 0x1131d077, 0x01edf0c5, 0xb1c490f8, 0x61be30bf, 0xd1975082, 0x8297e060, 0x32be805d, 0xe2c4201a, 0x52ed4027, 0x42316095, 0xf21800a8, 0x2262a0ef, 0x924bc0d2, 0x43dc9050, 0xf3f5f06d, 0x238f502a, 0x93a63017, 0x837a10a5, 0x33537098, 0xe329d0df, 0x5300b0e2, 0x042fc1c1, 0xb406a1fc, 0x647c01bb, 0xd4556186, 0xc4894134, 0x74a02109, 0xa4da814e, 0x14f3e173, 0xc564b1f1, 0x754dd1cc, 0xa537718b, 0x151e11b6, 0x05c23104, 0xb5eb5139, 0x6591f17e, 0xd5b89143, 0x86b821a1, 0x3691419c, 0xe6ebe1db, 0x56c281e6, 0x461ea154, 0xf637c169, 0x264d612e, 0x96640113, 0x47f35191, 0xf7da31ac, 0x27a091eb, 0x9789f1d6, 0x8755d164, 0x377cb159, 0xe706111e, 0x572f7123, 0x4958f358, 0xf9719365, 0x290b3322, 0x9922531f, 0x89fe73ad, 0x39d71390, 0xe9adb3d7, 0x5984d3ea, 0x88138368, 0x383ae355, 0xe8404312, 0x5869232f, 0x48b5039d, 0xf89c63a0, 0x28e6c3e7, 0x98cfa3da, 0xcbcf1338, 0x7be67305, 0xab9cd342, 0x1bb5b37f, 0x0b6993cd, 0xbb40f3f0, 0x6b3a53b7, 0xdb13338a, 0x0a846308, 0xbaad0335, 0x6ad7a372, 0xdafec34f, 0xca22e3fd, 0x7a0b83c0, 0xaa712387, 0x1a5843ba, 0x4d773299, 0xfd5e52a4, 0x2d24f2e3, 0x9d0d92de, 0x8dd1b26c, 0x3df8d251, 0xed827216, 0x5dab122b, 0x8c3c42a9, 0x3c152294, 0xec6f82d3, 0x5c46e2ee, 0x4c9ac25c, 0xfcb3a261, 0x2cc90226, 0x9ce0621b, 0xcfe0d2f9, 0x7fc9b2c4, 0xafb31283, 0x1f9a72be, 0x0f46520c, 0xbf6f3231, 0x6f159276, 0xdf3cf24b, 0x0eaba2c9, 0xbe82c2f4, 0x6ef862b3, 0xded1028e, 0xce0d223c, 0x7e244201, 0xae5ee246, 0x1e77827b, 0x92b0e6b1, 0x2299868c, 0xf2e326cb, 0x42ca46f6, 0x52166644, 0xe23f0679, 0x3245a63e, 0x826cc603, 0x53fb9681, 0xe3d2f6bc, 0x33a856fb, 0x838136c6, 0x935d1674, 0x23747649, 0xf30ed60e, 0x4327b633, 0x102706d1, 0xa00e66ec, 0x7074c6ab, 0xc05da696, 0xd0818624, 0x60a8e619, 0xb0d2465e, 0x00fb2663, 0xd16c76e1, 0x614516dc, 0xb13fb69b, 0x0116d6a6, 0x11caf614, 0xa1e39629, 0x7199366e, 0xc1b05653, 0x969f2770, 0x26b6474d, 0xf6cce70a, 0x46e58737, 0x5639a785, 0xe610c7b8, 0x366a67ff, 0x864307c2, 0x57d45740, 0xe7fd377d, 0x3787973a, 0x87aef707, 0x9772d7b5, 0x275bb788, 0xf72117cf, 0x470877f2, 0x1408c710, 0xa421a72d, 0x745b076a, 0xc4726757, 0xd4ae47e5, 0x648727d8, 0xb4fd879f, 0x04d4e7a2, 0xd543b720, 0x656ad71d, 0xb510775a, 0x05391767, 0x15e537d5, 0xa5cc57e8, 0x75b6f7af, 0xc59f9792, 0xdbe815e9, 0x6bc175d4, 0xbbbbd593, 0x0b92b5ae, 0x1b4e951c, 0xab67f521, 0x7b1d5566, 0xcb34355b, 0x1aa365d9, 0xaa8a05e4, 0x7af0a5a3, 0xcad9c59e, 0xda05e52c, 0x6a2c8511, 0xba562556, 0x0a7f456b, 0x597ff589, 0xe95695b4, 0x392c35f3, 0x890555ce, 0x99d9757c, 0x29f01541, 0xf98ab506, 0x49a3d53b, 0x983485b9, 0x281de584, 0xf86745c3, 0x484e25fe, 0x5892054c, 0xe8bb6571, 0x38c1c536, 0x88e8a50b, 0xdfc7d428, 0x6feeb415, 0xbf941452, 0x0fbd746f, 0x1f6154dd, 0xaf4834e0, 0x7f3294a7, 0xcf1bf49a, 0x1e8ca418, 0xaea5c425, 0x7edf6462, 0xcef6045f, 0xde2a24ed, 0x6e0344d0, 0xbe79e497, 0x0e5084aa, 0x5d503448, 0xed795475, 0x3d03f432, 0x8d2a940f, 0x9df6b4bd, 0x2ddfd480, 0xfda574c7, 0x4d8c14fa, 0x9c1b4478, 0x2c322445, 0xfc488402, 0x4c61e43f, 0x5cbdc48d, 0xec94a4b0, 0x3cee04f7, 0x8cc764ca}, {0x00000000, 0xa5d35ccb, 0x0ba1c84d, 0xae729486, 0x1642919b, 0xb391cd50, 0x1de359d6, 0xb830051d, 0x6d8253ec, 0xc8510f27, 0x66239ba1, 0xc3f0c76a, 0x7bc0c277, 0xde139ebc, 0x70610a3a, 0xd5b256f1, 0x9b02d603, 0x3ed18ac8, 0x90a31e4e, 0x35704285, 0x8d404798, 0x28931b53, 0x86e18fd5, 0x2332d31e, 0xf68085ef, 0x5353d924, 0xfd214da2, 0x58f21169, 0xe0c21474, 0x451148bf, 0xeb63dc39, 0x4eb080f2, 0x3605ac07, 0x93d6f0cc, 0x3da4644a, 0x98773881, 0x20473d9c, 0x85946157, 0x2be6f5d1, 0x8e35a91a, 0x5b87ffeb, 0xfe54a320, 0x502637a6, 0xf5f56b6d, 0x4dc56e70, 0xe81632bb, 0x4664a63d, 0xe3b7faf6, 0xad077a04, 0x08d426cf, 0xa6a6b249, 0x0375ee82, 0xbb45eb9f, 0x1e96b754, 0xb0e423d2, 0x15377f19, 0xc08529e8, 0x65567523, 0xcb24e1a5, 0x6ef7bd6e, 0xd6c7b873, 0x7314e4b8, 0xdd66703e, 0x78b52cf5, 0x6c0a580f, 0xc9d904c4, 0x67ab9042, 0xc278cc89, 0x7a48c994, 0xdf9b955f, 0x71e901d9, 0xd43a5d12, 0x01880be3, 0xa45b5728, 0x0a29c3ae, 0xaffa9f65, 0x17ca9a78, 0xb219c6b3, 0x1c6b5235, 0xb9b80efe, 0xf7088e0c, 0x52dbd2c7, 0xfca94641, 0x597a1a8a, 0xe14a1f97, 0x4499435c, 0xeaebd7da, 0x4f388b11, 0x9a8adde0, 0x3f59812b, 0x912b15ad, 0x34f84966, 0x8cc84c7b, 0x291b10b0, 0x87698436, 0x22bad8fd, 0x5a0ff408, 0xffdca8c3, 0x51ae3c45, 0xf47d608e, 0x4c4d6593, 0xe99e3958, 0x47ecadde, 0xe23ff115, 0x378da7e4, 0x925efb2f, 0x3c2c6fa9, 0x99ff3362, 0x21cf367f, 0x841c6ab4, 0x2a6efe32, 0x8fbda2f9, 0xc10d220b, 0x64de7ec0, 0xcaacea46, 0x6f7fb68d, 0xd74fb390, 0x729cef5b, 0xdcee7bdd, 0x793d2716, 0xac8f71e7, 0x095c2d2c, 0xa72eb9aa, 0x02fde561, 0xbacde07c, 0x1f1ebcb7, 0xb16c2831, 0x14bf74fa, 0xd814b01e, 0x7dc7ecd5, 0xd3b57853, 0x76662498, 0xce562185, 0x6b857d4e, 0xc5f7e9c8, 0x6024b503, 0xb596e3f2, 0x1045bf39, 0xbe372bbf, 0x1be47774, 0xa3d47269, 0x06072ea2, 0xa875ba24, 0x0da6e6ef, 0x4316661d, 0xe6c53ad6, 0x48b7ae50, 0xed64f29b, 0x5554f786, 0xf087ab4d, 0x5ef53fcb, 0xfb266300, 0x2e9435f1, 0x8b47693a, 0x2535fdbc, 0x80e6a177, 0x38d6a46a, 0x9d05f8a1, 0x33776c27, 0x96a430ec, 0xee111c19, 0x4bc240d2, 0xe5b0d454, 0x4063889f, 0xf8538d82, 0x5d80d149, 0xf3f245cf, 0x56211904, 0x83934ff5, 0x2640133e, 0x883287b8, 0x2de1db73, 0x95d1de6e, 0x300282a5, 0x9e701623, 0x3ba34ae8, 0x7513ca1a, 0xd0c096d1, 0x7eb20257, 0xdb615e9c, 0x63515b81, 0xc682074a, 0x68f093cc, 0xcd23cf07, 0x189199f6, 0xbd42c53d, 0x133051bb, 0xb6e30d70, 0x0ed3086d, 0xab0054a6, 0x0572c020, 0xa0a19ceb, 0xb41ee811, 0x11cdb4da, 0xbfbf205c, 0x1a6c7c97, 0xa25c798a, 0x078f2541, 0xa9fdb1c7, 0x0c2eed0c, 0xd99cbbfd, 0x7c4fe736, 0xd23d73b0, 0x77ee2f7b, 0xcfde2a66, 0x6a0d76ad, 0xc47fe22b, 0x61acbee0, 0x2f1c3e12, 0x8acf62d9, 0x24bdf65f, 0x816eaa94, 0x395eaf89, 0x9c8df342, 0x32ff67c4, 0x972c3b0f, 0x429e6dfe, 0xe74d3135, 0x493fa5b3, 0xececf978, 0x54dcfc65, 0xf10fa0ae, 0x5f7d3428, 0xfaae68e3, 0x821b4416, 0x27c818dd, 0x89ba8c5b, 0x2c69d090, 0x9459d58d, 0x318a8946, 0x9ff81dc0, 0x3a2b410b, 0xef9917fa, 0x4a4a4b31, 0xe438dfb7, 0x41eb837c, 0xf9db8661, 0x5c08daaa, 0xf27a4e2c, 0x57a912e7, 0x19199215, 0xbccacede, 0x12b85a58, 0xb76b0693, 0x0f5b038e, 0xaa885f45, 0x04facbc3, 0xa1299708, 0x749bc1f9, 0xd1489d32, 0x7f3a09b4, 0xdae9557f, 0x62d95062, 0xc70a0ca9, 0x6978982f, 0xccabc4e4}, {0x00000000, 0xb40b77a6, 0x29119f97, 0x9d1ae831, 0x13244ff4, 0xa72f3852, 0x3a35d063, 0x8e3ea7c5, 0x674eef33, 0xd3459895, 0x4e5f70a4, 0xfa540702, 0x746aa0c7, 0xc061d761, 0x5d7b3f50, 0xe97048f6, 0xce9cde67, 0x7a97a9c1, 0xe78d41f0, 0x53863656, 0xddb89193, 0x69b3e635, 0xf4a90e04, 0x40a279a2, 0xa9d23154, 0x1dd946f2, 0x80c3aec3, 0x34c8d965, 0xbaf67ea0, 0x0efd0906, 0x93e7e137, 0x27ec9691, 0x9c39bdcf, 0x2832ca69, 0xb5282258, 0x012355fe, 0x8f1df23b, 0x3b16859d, 0xa60c6dac, 0x12071a0a, 0xfb7752fc, 0x4f7c255a, 0xd266cd6b, 0x666dbacd, 0xe8531d08, 0x5c586aae, 0xc142829f, 0x7549f539, 0x52a563a8, 0xe6ae140e, 0x7bb4fc3f, 0xcfbf8b99, 0x41812c5c, 0xf58a5bfa, 0x6890b3cb, 0xdc9bc46d, 0x35eb8c9b, 0x81e0fb3d, 0x1cfa130c, 0xa8f164aa, 0x26cfc36f, 0x92c4b4c9, 0x0fde5cf8, 0xbbd52b5e, 0x79750b44, 0xcd7e7ce2, 0x506494d3, 0xe46fe375, 0x6a5144b0, 0xde5a3316, 0x4340db27, 0xf74bac81, 0x1e3be477, 0xaa3093d1, 0x372a7be0, 0x83210c46, 0x0d1fab83, 0xb914dc25, 0x240e3414, 0x900543b2, 0xb7e9d523, 0x03e2a285, 0x9ef84ab4, 0x2af33d12, 0xa4cd9ad7, 0x10c6ed71, 0x8ddc0540, 0x39d772e6, 0xd0a73a10, 0x64ac4db6, 0xf9b6a587, 0x4dbdd221, 0xc38375e4, 0x77880242, 0xea92ea73, 0x5e999dd5, 0xe54cb68b, 0x5147c12d, 0xcc5d291c, 0x78565eba, 0xf668f97f, 0x42638ed9, 0xdf7966e8, 0x6b72114e, 0x820259b8, 0x36092e1e, 0xab13c62f, 0x1f18b189, 0x9126164c, 0x252d61ea, 0xb83789db, 0x0c3cfe7d, 0x2bd068ec, 0x9fdb1f4a, 0x02c1f77b, 0xb6ca80dd, 0x38f42718, 0x8cff50be, 0x11e5b88f, 0xa5eecf29, 0x4c9e87df, 0xf895f079, 0x658f1848, 0xd1846fee, 0x5fbac82b, 0xebb1bf8d, 0x76ab57bc, 0xc2a0201a, 0xf2ea1688, 0x46e1612e, 0xdbfb891f, 0x6ff0feb9, 0xe1ce597c, 0x55c52eda, 0xc8dfc6eb, 0x7cd4b14d, 0x95a4f9bb, 0x21af8e1d, 0xbcb5662c, 0x08be118a, 0x8680b64f, 0x328bc1e9, 0xaf9129d8, 0x1b9a5e7e, 0x3c76c8ef, 0x887dbf49, 0x15675778, 0xa16c20de, 0x2f52871b, 0x9b59f0bd, 0x0643188c, 0xb2486f2a, 0x5b3827dc, 0xef33507a, 0x7229b84b, 0xc622cfed, 0x481c6828, 0xfc171f8e, 0x610df7bf, 0xd5068019, 0x6ed3ab47, 0xdad8dce1, 0x47c234d0, 0xf3c94376, 0x7df7e4b3, 0xc9fc9315, 0x54e67b24, 0xe0ed0c82, 0x099d4474, 0xbd9633d2, 0x208cdbe3, 0x9487ac45, 0x1ab90b80, 0xaeb27c26, 0x33a89417, 0x87a3e3b1, 0xa04f7520, 0x14440286, 0x895eeab7, 0x3d559d11, 0xb36b3ad4, 0x07604d72, 0x9a7aa543, 0x2e71d2e5, 0xc7019a13, 0x730aedb5, 0xee100584, 0x5a1b7222, 0xd425d5e7, 0x602ea241, 0xfd344a70, 0x493f3dd6, 0x8b9f1dcc, 0x3f946a6a, 0xa28e825b, 0x1685f5fd, 0x98bb5238, 0x2cb0259e, 0xb1aacdaf, 0x05a1ba09, 0xecd1f2ff, 0x58da8559, 0xc5c06d68, 0x71cb1ace, 0xfff5bd0b, 0x4bfecaad, 0xd6e4229c, 0x62ef553a, 0x4503c3ab, 0xf108b40d, 0x6c125c3c, 0xd8192b9a, 0x56278c5f, 0xe22cfbf9, 0x7f3613c8, 0xcb3d646e, 0x224d2c98, 0x96465b3e, 0x0b5cb30f, 0xbf57c4a9, 0x3169636c, 0x856214ca, 0x1878fcfb, 0xac738b5d, 0x17a6a003, 0xa3add7a5, 0x3eb73f94, 0x8abc4832, 0x0482eff7, 0xb0899851, 0x2d937060, 0x999807c6, 0x70e84f30, 0xc4e33896, 0x59f9d0a7, 0xedf2a701, 0x63cc00c4, 0xd7c77762, 0x4add9f53, 0xfed6e8f5, 0xd93a7e64, 0x6d3109c2, 0xf02be1f3, 0x44209655, 0xca1e3190, 0x7e154636, 0xe30fae07, 0x5704d9a1, 0xbe749157, 0x0a7fe6f1, 0x97650ec0, 0x236e7966, 0xad50dea3, 0x195ba905, 0x84414134, 0x304a3692}, {0x00000000, 0x9e00aacc, 0x7d072542, 0xe3078f8e, 0xfa0e4a84, 0x640ee048, 0x87096fc6, 0x1909c50a, 0xb51be5d3, 0x2b1b4f1f, 0xc81cc091, 0x561c6a5d, 0x4f15af57, 0xd115059b, 0x32128a15, 0xac1220d9, 0x2b31bb7c, 0xb53111b0, 0x56369e3e, 0xc83634f2, 0xd13ff1f8, 0x4f3f5b34, 0xac38d4ba, 0x32387e76, 0x9e2a5eaf, 0x002af463, 0xe32d7bed, 0x7d2dd121, 0x6424142b, 0xfa24bee7, 0x19233169, 0x87239ba5, 0x566276f9, 0xc862dc35, 0x2b6553bb, 0xb565f977, 0xac6c3c7d, 0x326c96b1, 0xd16b193f, 0x4f6bb3f3, 0xe379932a, 0x7d7939e6, 0x9e7eb668, 0x007e1ca4, 0x1977d9ae, 0x87777362, 0x6470fcec, 0xfa705620, 0x7d53cd85, 0xe3536749, 0x0054e8c7, 0x9e54420b, 0x875d8701, 0x195d2dcd, 0xfa5aa243, 0x645a088f, 0xc8482856, 0x5648829a, 0xb54f0d14, 0x2b4fa7d8, 0x324662d2, 0xac46c81e, 0x4f414790, 0xd141ed5c, 0xedc29d29, 0x73c237e5, 0x90c5b86b, 0x0ec512a7, 0x17ccd7ad, 0x89cc7d61, 0x6acbf2ef, 0xf4cb5823, 0x58d978fa, 0xc6d9d236, 0x25de5db8, 0xbbdef774, 0xa2d7327e, 0x3cd798b2, 0xdfd0173c, 0x41d0bdf0, 0xc6f32655, 0x58f38c99, 0xbbf40317, 0x25f4a9db, 0x3cfd6cd1, 0xa2fdc61d, 0x41fa4993, 0xdffae35f, 0x73e8c386, 0xede8694a, 0x0eefe6c4, 0x90ef4c08, 0x89e68902, 0x17e623ce, 0xf4e1ac40, 0x6ae1068c, 0xbba0ebd0, 0x25a0411c, 0xc6a7ce92, 0x58a7645e, 0x41aea154, 0xdfae0b98, 0x3ca98416, 0xa2a92eda, 0x0ebb0e03, 0x90bba4cf, 0x73bc2b41, 0xedbc818d, 0xf4b54487, 0x6ab5ee4b, 0x89b261c5, 0x17b2cb09, 0x909150ac, 0x0e91fa60, 0xed9675ee, 0x7396df22, 0x6a9f1a28, 0xf49fb0e4, 0x17983f6a, 0x899895a6, 0x258ab57f, 0xbb8a1fb3, 0x588d903d, 0xc68d3af1, 0xdf84fffb, 0x41845537, 0xa283dab9, 0x3c837075, 0xda853b53, 0x4485919f, 0xa7821e11, 0x3982b4dd, 0x208b71d7, 0xbe8bdb1b, 0x5d8c5495, 0xc38cfe59, 0x6f9ede80, 0xf19e744c, 0x1299fbc2, 0x8c99510e, 0x95909404, 0x0b903ec8, 0xe897b146, 0x76971b8a, 0xf1b4802f, 0x6fb42ae3, 0x8cb3a56d, 0x12b30fa1, 0x0bbacaab, 0x95ba6067, 0x76bdefe9, 0xe8bd4525, 0x44af65fc, 0xdaafcf30, 0x39a840be, 0xa7a8ea72, 0xbea12f78, 0x20a185b4, 0xc3a60a3a, 0x5da6a0f6, 0x8ce74daa, 0x12e7e766, 0xf1e068e8, 0x6fe0c224, 0x76e9072e, 0xe8e9ade2, 0x0bee226c, 0x95ee88a0, 0x39fca879, 0xa7fc02b5, 0x44fb8d3b, 0xdafb27f7, 0xc3f2e2fd, 0x5df24831, 0xbef5c7bf, 0x20f56d73, 0xa7d6f6d6, 0x39d65c1a, 0xdad1d394, 0x44d17958, 0x5dd8bc52, 0xc3d8169e, 0x20df9910, 0xbedf33dc, 0x12cd1305, 0x8ccdb9c9, 0x6fca3647, 0xf1ca9c8b, 0xe8c35981, 0x76c3f34d, 0x95c47cc3, 0x0bc4d60f, 0x3747a67a, 0xa9470cb6, 0x4a408338, 0xd44029f4, 0xcd49ecfe, 0x53494632, 0xb04ec9bc, 0x2e4e6370, 0x825c43a9, 0x1c5ce965, 0xff5b66eb, 0x615bcc27, 0x7852092d, 0xe652a3e1, 0x05552c6f, 0x9b5586a3, 0x1c761d06, 0x8276b7ca, 0x61713844, 0xff719288, 0xe6785782, 0x7878fd4e, 0x9b7f72c0, 0x057fd80c, 0xa96df8d5, 0x376d5219, 0xd46add97, 0x4a6a775b, 0x5363b251, 0xcd63189d, 0x2e649713, 0xb0643ddf, 0x6125d083, 0xff257a4f, 0x1c22f5c1, 0x82225f0d, 0x9b2b9a07, 0x052b30cb, 0xe62cbf45, 0x782c1589, 0xd43e3550, 0x4a3e9f9c, 0xa9391012, 0x3739bade, 0x2e307fd4, 0xb030d518, 0x53375a96, 0xcd37f05a, 0x4a146bff, 0xd414c133, 0x37134ebd, 0xa913e471, 0xb01a217b, 0x2e1a8bb7, 0xcd1d0439, 0x531daef5, 0xff0f8e2c, 0x610f24e0, 0x8208ab6e, 0x1c0801a2, 0x0501c4a8, 0x9b016e64, 0x7806e1ea, 0xe6064b26}}; #endif #endif #if N == 3 #if W == 8 local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0x81256527, 0xd93bcc0f, 0x581ea928, 0x69069e5f, 0xe823fb78, 0xb03d5250, 0x31183777, 0xd20d3cbe, 0x53285999, 0x0b36f0b1, 0x8a139596, 0xbb0ba2e1, 0x3a2ec7c6, 0x62306eee, 0xe3150bc9, 0x7f6b7f3d, 0xfe4e1a1a, 0xa650b332, 0x2775d615, 0x166de162, 0x97488445, 0xcf562d6d, 0x4e73484a, 0xad664383, 0x2c4326a4, 0x745d8f8c, 0xf578eaab, 0xc460dddc, 0x4545b8fb, 0x1d5b11d3, 0x9c7e74f4, 0xfed6fe7a, 0x7ff39b5d, 0x27ed3275, 0xa6c85752, 0x97d06025, 0x16f50502, 0x4eebac2a, 0xcfcec90d, 0x2cdbc2c4, 0xadfea7e3, 0xf5e00ecb, 0x74c56bec, 0x45dd5c9b, 0xc4f839bc, 0x9ce69094, 0x1dc3f5b3, 0x81bd8147, 0x0098e460, 0x58864d48, 0xd9a3286f, 0xe8bb1f18, 0x699e7a3f, 0x3180d317, 0xb0a5b630, 0x53b0bdf9, 0xd295d8de, 0x8a8b71f6, 0x0bae14d1, 0x3ab623a6, 0xbb934681, 0xe38defa9, 0x62a88a8e, 0x26dcfab5, 0xa7f99f92, 0xffe736ba, 0x7ec2539d, 0x4fda64ea, 0xceff01cd, 0x96e1a8e5, 0x17c4cdc2, 0xf4d1c60b, 0x75f4a32c, 0x2dea0a04, 0xaccf6f23, 0x9dd75854, 0x1cf23d73, 0x44ec945b, 0xc5c9f17c, 0x59b78588, 0xd892e0af, 0x808c4987, 0x01a92ca0, 0x30b11bd7, 0xb1947ef0, 0xe98ad7d8, 0x68afb2ff, 0x8bbab936, 0x0a9fdc11, 0x52817539, 0xd3a4101e, 0xe2bc2769, 0x6399424e, 0x3b87eb66, 0xbaa28e41, 0xd80a04cf, 0x592f61e8, 0x0131c8c0, 0x8014ade7, 0xb10c9a90, 0x3029ffb7, 0x6837569f, 0xe91233b8, 0x0a073871, 0x8b225d56, 0xd33cf47e, 0x52199159, 0x6301a62e, 0xe224c309, 0xba3a6a21, 0x3b1f0f06, 0xa7617bf2, 0x26441ed5, 0x7e5ab7fd, 0xff7fd2da, 0xce67e5ad, 0x4f42808a, 0x175c29a2, 0x96794c85, 0x756c474c, 0xf449226b, 0xac578b43, 0x2d72ee64, 0x1c6ad913, 0x9d4fbc34, 0xc551151c, 0x4474703b, 0x4db9f56a, 0xcc9c904d, 0x94823965, 0x15a75c42, 0x24bf6b35, 0xa59a0e12, 0xfd84a73a, 0x7ca1c21d, 0x9fb4c9d4, 0x1e91acf3, 0x468f05db, 0xc7aa60fc, 0xf6b2578b, 0x779732ac, 0x2f899b84, 0xaeacfea3, 0x32d28a57, 0xb3f7ef70, 0xebe94658, 0x6acc237f, 0x5bd41408, 0xdaf1712f, 0x82efd807, 0x03cabd20, 0xe0dfb6e9, 0x61fad3ce, 0x39e47ae6, 0xb8c11fc1, 0x89d928b6, 0x08fc4d91, 0x50e2e4b9, 0xd1c7819e, 0xb36f0b10, 0x324a6e37, 0x6a54c71f, 0xeb71a238, 0xda69954f, 0x5b4cf068, 0x03525940, 0x82773c67, 0x616237ae, 0xe0475289, 0xb859fba1, 0x397c9e86, 0x0864a9f1, 0x8941ccd6, 0xd15f65fe, 0x507a00d9, 0xcc04742d, 0x4d21110a, 0x153fb822, 0x941add05, 0xa502ea72, 0x24278f55, 0x7c39267d, 0xfd1c435a, 0x1e094893, 0x9f2c2db4, 0xc732849c, 0x4617e1bb, 0x770fd6cc, 0xf62ab3eb, 0xae341ac3, 0x2f117fe4, 0x6b650fdf, 0xea406af8, 0xb25ec3d0, 0x337ba6f7, 0x02639180, 0x8346f4a7, 0xdb585d8f, 0x5a7d38a8, 0xb9683361, 0x384d5646, 0x6053ff6e, 0xe1769a49, 0xd06ead3e, 0x514bc819, 0x09556131, 0x88700416, 0x140e70e2, 0x952b15c5, 0xcd35bced, 0x4c10d9ca, 0x7d08eebd, 0xfc2d8b9a, 0xa43322b2, 0x25164795, 0xc6034c5c, 0x4726297b, 0x1f388053, 0x9e1de574, 0xaf05d203, 0x2e20b724, 0x763e1e0c, 0xf71b7b2b, 0x95b3f1a5, 0x14969482, 0x4c883daa, 0xcdad588d, 0xfcb56ffa, 0x7d900add, 0x258ea3f5, 0xa4abc6d2, 0x47becd1b, 0xc69ba83c, 0x9e850114, 0x1fa06433, 0x2eb85344, 0xaf9d3663, 0xf7839f4b, 0x76a6fa6c, 0xead88e98, 0x6bfdebbf, 0x33e34297, 0xb2c627b0, 0x83de10c7, 0x02fb75e0, 0x5ae5dcc8, 0xdbc0b9ef, 0x38d5b226, 0xb9f0d701, 0xe1ee7e29, 0x60cb1b0e, 0x51d32c79, 0xd0f6495e, 0x88e8e076, 0x09cd8551}, {0x00000000, 0x9b73ead4, 0xed96d3e9, 0x76e5393d, 0x005ca193, 0x9b2f4b47, 0xedca727a, 0x76b998ae, 0x00b94326, 0x9bcaa9f2, 0xed2f90cf, 0x765c7a1b, 0x00e5e2b5, 0x9b960861, 0xed73315c, 0x7600db88, 0x0172864c, 0x9a016c98, 0xece455a5, 0x7797bf71, 0x012e27df, 0x9a5dcd0b, 0xecb8f436, 0x77cb1ee2, 0x01cbc56a, 0x9ab82fbe, 0xec5d1683, 0x772efc57, 0x019764f9, 0x9ae48e2d, 0xec01b710, 0x77725dc4, 0x02e50c98, 0x9996e64c, 0xef73df71, 0x740035a5, 0x02b9ad0b, 0x99ca47df, 0xef2f7ee2, 0x745c9436, 0x025c4fbe, 0x992fa56a, 0xefca9c57, 0x74b97683, 0x0200ee2d, 0x997304f9, 0xef963dc4, 0x74e5d710, 0x03978ad4, 0x98e46000, 0xee01593d, 0x7572b3e9, 0x03cb2b47, 0x98b8c193, 0xee5df8ae, 0x752e127a, 0x032ec9f2, 0x985d2326, 0xeeb81a1b, 0x75cbf0cf, 0x03726861, 0x980182b5, 0xeee4bb88, 0x7597515c, 0x05ca1930, 0x9eb9f3e4, 0xe85ccad9, 0x732f200d, 0x0596b8a3, 0x9ee55277, 0xe8006b4a, 0x7373819e, 0x05735a16, 0x9e00b0c2, 0xe8e589ff, 0x7396632b, 0x052ffb85, 0x9e5c1151, 0xe8b9286c, 0x73cac2b8, 0x04b89f7c, 0x9fcb75a8, 0xe92e4c95, 0x725da641, 0x04e43eef, 0x9f97d43b, 0xe972ed06, 0x720107d2, 0x0401dc5a, 0x9f72368e, 0xe9970fb3, 0x72e4e567, 0x045d7dc9, 0x9f2e971d, 0xe9cbae20, 0x72b844f4, 0x072f15a8, 0x9c5cff7c, 0xeab9c641, 0x71ca2c95, 0x0773b43b, 0x9c005eef, 0xeae567d2, 0x71968d06, 0x0796568e, 0x9ce5bc5a, 0xea008567, 0x71736fb3, 0x07caf71d, 0x9cb91dc9, 0xea5c24f4, 0x712fce20, 0x065d93e4, 0x9d2e7930, 0xebcb400d, 0x70b8aad9, 0x06013277, 0x9d72d8a3, 0xeb97e19e, 0x70e40b4a, 0x06e4d0c2, 0x9d973a16, 0xeb72032b, 0x7001e9ff, 0x06b87151, 0x9dcb9b85, 0xeb2ea2b8, 0x705d486c, 0x0b943260, 0x90e7d8b4, 0xe602e189, 0x7d710b5d, 0x0bc893f3, 0x90bb7927, 0xe65e401a, 0x7d2daace, 0x0b2d7146, 0x905e9b92, 0xe6bba2af, 0x7dc8487b, 0x0b71d0d5, 0x90023a01, 0xe6e7033c, 0x7d94e9e8, 0x0ae6b42c, 0x91955ef8, 0xe77067c5, 0x7c038d11, 0x0aba15bf, 0x91c9ff6b, 0xe72cc656, 0x7c5f2c82, 0x0a5ff70a, 0x912c1dde, 0xe7c924e3, 0x7cbace37, 0x0a035699, 0x9170bc4d, 0xe7958570, 0x7ce66fa4, 0x09713ef8, 0x9202d42c, 0xe4e7ed11, 0x7f9407c5, 0x092d9f6b, 0x925e75bf, 0xe4bb4c82, 0x7fc8a656, 0x09c87dde, 0x92bb970a, 0xe45eae37, 0x7f2d44e3, 0x0994dc4d, 0x92e73699, 0xe4020fa4, 0x7f71e570, 0x0803b8b4, 0x93705260, 0xe5956b5d, 0x7ee68189, 0x085f1927, 0x932cf3f3, 0xe5c9cace, 0x7eba201a, 0x08bafb92, 0x93c91146, 0xe52c287b, 0x7e5fc2af, 0x08e65a01, 0x9395b0d5, 0xe57089e8, 0x7e03633c, 0x0e5e2b50, 0x952dc184, 0xe3c8f8b9, 0x78bb126d, 0x0e028ac3, 0x95716017, 0xe394592a, 0x78e7b3fe, 0x0ee76876, 0x959482a2, 0xe371bb9f, 0x7802514b, 0x0ebbc9e5, 0x95c82331, 0xe32d1a0c, 0x785ef0d8, 0x0f2cad1c, 0x945f47c8, 0xe2ba7ef5, 0x79c99421, 0x0f700c8f, 0x9403e65b, 0xe2e6df66, 0x799535b2, 0x0f95ee3a, 0x94e604ee, 0xe2033dd3, 0x7970d707, 0x0fc94fa9, 0x94baa57d, 0xe25f9c40, 0x792c7694, 0x0cbb27c8, 0x97c8cd1c, 0xe12df421, 0x7a5e1ef5, 0x0ce7865b, 0x97946c8f, 0xe17155b2, 0x7a02bf66, 0x0c0264ee, 0x97718e3a, 0xe194b707, 0x7ae75dd3, 0x0c5ec57d, 0x972d2fa9, 0xe1c81694, 0x7abbfc40, 0x0dc9a184, 0x96ba4b50, 0xe05f726d, 0x7b2c98b9, 0x0d950017, 0x96e6eac3, 0xe003d3fe, 0x7b70392a, 0x0d70e2a2, 0x96030876, 0xe0e6314b, 0x7b95db9f, 0x0d2c4331, 0x965fa9e5, 0xe0ba90d8, 0x7bc97a0c}, {0x00000000, 0x172864c0, 0x2e50c980, 0x3978ad40, 0x5ca19300, 0x4b89f7c0, 0x72f15a80, 0x65d93e40, 0xb9432600, 0xae6b42c0, 0x9713ef80, 0x803b8b40, 0xe5e2b500, 0xf2cad1c0, 0xcbb27c80, 0xdc9a1840, 0xa9f74a41, 0xbedf2e81, 0x87a783c1, 0x908fe701, 0xf556d941, 0xe27ebd81, 0xdb0610c1, 0xcc2e7401, 0x10b46c41, 0x079c0881, 0x3ee4a5c1, 0x29ccc101, 0x4c15ff41, 0x5b3d9b81, 0x624536c1, 0x756d5201, 0x889f92c3, 0x9fb7f603, 0xa6cf5b43, 0xb1e73f83, 0xd43e01c3, 0xc3166503, 0xfa6ec843, 0xed46ac83, 0x31dcb4c3, 0x26f4d003, 0x1f8c7d43, 0x08a41983, 0x6d7d27c3, 0x7a554303, 0x432dee43, 0x54058a83, 0x2168d882, 0x3640bc42, 0x0f381102, 0x181075c2, 0x7dc94b82, 0x6ae12f42, 0x53998202, 0x44b1e6c2, 0x982bfe82, 0x8f039a42, 0xb67b3702, 0xa15353c2, 0xc48a6d82, 0xd3a20942, 0xeadaa402, 0xfdf2c0c2, 0xca4e23c7, 0xdd664707, 0xe41eea47, 0xf3368e87, 0x96efb0c7, 0x81c7d407, 0xb8bf7947, 0xaf971d87, 0x730d05c7, 0x64256107, 0x5d5dcc47, 0x4a75a887, 0x2fac96c7, 0x3884f207, 0x01fc5f47, 0x16d43b87, 0x63b96986, 0x74910d46, 0x4de9a006, 0x5ac1c4c6, 0x3f18fa86, 0x28309e46, 0x11483306, 0x066057c6, 0xdafa4f86, 0xcdd22b46, 0xf4aa8606, 0xe382e2c6, 0x865bdc86, 0x9173b846, 0xa80b1506, 0xbf2371c6, 0x42d1b104, 0x55f9d5c4, 0x6c817884, 0x7ba91c44, 0x1e702204, 0x095846c4, 0x3020eb84, 0x27088f44, 0xfb929704, 0xecbaf3c4, 0xd5c25e84, 0xc2ea3a44, 0xa7330404, 0xb01b60c4, 0x8963cd84, 0x9e4ba944, 0xeb26fb45, 0xfc0e9f85, 0xc57632c5, 0xd25e5605, 0xb7876845, 0xa0af0c85, 0x99d7a1c5, 0x8effc505, 0x5265dd45, 0x454db985, 0x7c3514c5, 0x6b1d7005, 0x0ec44e45, 0x19ec2a85, 0x209487c5, 0x37bce305, 0x4fed41cf, 0x58c5250f, 0x61bd884f, 0x7695ec8f, 0x134cd2cf, 0x0464b60f, 0x3d1c1b4f, 0x2a347f8f, 0xf6ae67cf, 0xe186030f, 0xd8feae4f, 0xcfd6ca8f, 0xaa0ff4cf, 0xbd27900f, 0x845f3d4f, 0x9377598f, 0xe61a0b8e, 0xf1326f4e, 0xc84ac20e, 0xdf62a6ce, 0xbabb988e, 0xad93fc4e, 0x94eb510e, 0x83c335ce, 0x5f592d8e, 0x4871494e, 0x7109e40e, 0x662180ce, 0x03f8be8e, 0x14d0da4e, 0x2da8770e, 0x3a8013ce, 0xc772d30c, 0xd05ab7cc, 0xe9221a8c, 0xfe0a7e4c, 0x9bd3400c, 0x8cfb24cc, 0xb583898c, 0xa2abed4c, 0x7e31f50c, 0x691991cc, 0x50613c8c, 0x4749584c, 0x2290660c, 0x35b802cc, 0x0cc0af8c, 0x1be8cb4c, 0x6e85994d, 0x79adfd8d, 0x40d550cd, 0x57fd340d, 0x32240a4d, 0x250c6e8d, 0x1c74c3cd, 0x0b5ca70d, 0xd7c6bf4d, 0xc0eedb8d, 0xf99676cd, 0xeebe120d, 0x8b672c4d, 0x9c4f488d, 0xa537e5cd, 0xb21f810d, 0x85a36208, 0x928b06c8, 0xabf3ab88, 0xbcdbcf48, 0xd902f108, 0xce2a95c8, 0xf7523888, 0xe07a5c48, 0x3ce04408, 0x2bc820c8, 0x12b08d88, 0x0598e948, 0x6041d708, 0x7769b3c8, 0x4e111e88, 0x59397a48, 0x2c542849, 0x3b7c4c89, 0x0204e1c9, 0x152c8509, 0x70f5bb49, 0x67dddf89, 0x5ea572c9, 0x498d1609, 0x95170e49, 0x823f6a89, 0xbb47c7c9, 0xac6fa309, 0xc9b69d49, 0xde9ef989, 0xe7e654c9, 0xf0ce3009, 0x0d3cf0cb, 0x1a14940b, 0x236c394b, 0x34445d8b, 0x519d63cb, 0x46b5070b, 0x7fcdaa4b, 0x68e5ce8b, 0xb47fd6cb, 0xa357b20b, 0x9a2f1f4b, 0x8d077b8b, 0xe8de45cb, 0xfff6210b, 0xc68e8c4b, 0xd1a6e88b, 0xa4cbba8a, 0xb3e3de4a, 0x8a9b730a, 0x9db317ca, 0xf86a298a, 0xef424d4a, 0xd63ae00a, 0xc11284ca, 0x1d889c8a, 0x0aa0f84a, 0x33d8550a, 0x24f031ca, 0x41290f8a, 0x56016b4a, 0x6f79c60a, 0x7851a2ca}, {0x00000000, 0x9fda839e, 0xe4c4017d, 0x7b1e82e3, 0x12f904bb, 0x8d238725, 0xf63d05c6, 0x69e78658, 0x25f20976, 0xba288ae8, 0xc136080b, 0x5eec8b95, 0x370b0dcd, 0xa8d18e53, 0xd3cf0cb0, 0x4c158f2e, 0x4be412ec, 0xd43e9172, 0xaf201391, 0x30fa900f, 0x591d1657, 0xc6c795c9, 0xbdd9172a, 0x220394b4, 0x6e161b9a, 0xf1cc9804, 0x8ad21ae7, 0x15089979, 0x7cef1f21, 0xe3359cbf, 0x982b1e5c, 0x07f19dc2, 0x97c825d8, 0x0812a646, 0x730c24a5, 0xecd6a73b, 0x85312163, 0x1aeba2fd, 0x61f5201e, 0xfe2fa380, 0xb23a2cae, 0x2de0af30, 0x56fe2dd3, 0xc924ae4d, 0xa0c32815, 0x3f19ab8b, 0x44072968, 0xdbddaaf6, 0xdc2c3734, 0x43f6b4aa, 0x38e83649, 0xa732b5d7, 0xced5338f, 0x510fb011, 0x2a1132f2, 0xb5cbb16c, 0xf9de3e42, 0x6604bddc, 0x1d1a3f3f, 0x82c0bca1, 0xeb273af9, 0x74fdb967, 0x0fe33b84, 0x9039b81a, 0xf4e14df1, 0x6b3bce6f, 0x10254c8c, 0x8fffcf12, 0xe618494a, 0x79c2cad4, 0x02dc4837, 0x9d06cba9, 0xd1134487, 0x4ec9c719, 0x35d745fa, 0xaa0dc664, 0xc3ea403c, 0x5c30c3a2, 0x272e4141, 0xb8f4c2df, 0xbf055f1d, 0x20dfdc83, 0x5bc15e60, 0xc41bddfe, 0xadfc5ba6, 0x3226d838, 0x49385adb, 0xd6e2d945, 0x9af7566b, 0x052dd5f5, 0x7e335716, 0xe1e9d488, 0x880e52d0, 0x17d4d14e, 0x6cca53ad, 0xf310d033, 0x63296829, 0xfcf3ebb7, 0x87ed6954, 0x1837eaca, 0x71d06c92, 0xee0aef0c, 0x95146def, 0x0aceee71, 0x46db615f, 0xd901e2c1, 0xa21f6022, 0x3dc5e3bc, 0x542265e4, 0xcbf8e67a, 0xb0e66499, 0x2f3ce707, 0x28cd7ac5, 0xb717f95b, 0xcc097bb8, 0x53d3f826, 0x3a347e7e, 0xa5eefde0, 0xdef07f03, 0x412afc9d, 0x0d3f73b3, 0x92e5f02d, 0xe9fb72ce, 0x7621f150, 0x1fc67708, 0x801cf496, 0xfb027675, 0x64d8f5eb, 0x32b39da3, 0xad691e3d, 0xd6779cde, 0x49ad1f40, 0x204a9918, 0xbf901a86, 0xc48e9865, 0x5b541bfb, 0x174194d5, 0x889b174b, 0xf38595a8, 0x6c5f1636, 0x05b8906e, 0x9a6213f0, 0xe17c9113, 0x7ea6128d, 0x79578f4f, 0xe68d0cd1, 0x9d938e32, 0x02490dac, 0x6bae8bf4, 0xf474086a, 0x8f6a8a89, 0x10b00917, 0x5ca58639, 0xc37f05a7, 0xb8618744, 0x27bb04da, 0x4e5c8282, 0xd186011c, 0xaa9883ff, 0x35420061, 0xa57bb87b, 0x3aa13be5, 0x41bfb906, 0xde653a98, 0xb782bcc0, 0x28583f5e, 0x5346bdbd, 0xcc9c3e23, 0x8089b10d, 0x1f533293, 0x644db070, 0xfb9733ee, 0x9270b5b6, 0x0daa3628, 0x76b4b4cb, 0xe96e3755, 0xee9faa97, 0x71452909, 0x0a5babea, 0x95812874, 0xfc66ae2c, 0x63bc2db2, 0x18a2af51, 0x87782ccf, 0xcb6da3e1, 0x54b7207f, 0x2fa9a29c, 0xb0732102, 0xd994a75a, 0x464e24c4, 0x3d50a627, 0xa28a25b9, 0xc652d052, 0x598853cc, 0x2296d12f, 0xbd4c52b1, 0xd4abd4e9, 0x4b715777, 0x306fd594, 0xafb5560a, 0xe3a0d924, 0x7c7a5aba, 0x0764d859, 0x98be5bc7, 0xf159dd9f, 0x6e835e01, 0x159ddce2, 0x8a475f7c, 0x8db6c2be, 0x126c4120, 0x6972c3c3, 0xf6a8405d, 0x9f4fc605, 0x0095459b, 0x7b8bc778, 0xe45144e6, 0xa844cbc8, 0x379e4856, 0x4c80cab5, 0xd35a492b, 0xbabdcf73, 0x25674ced, 0x5e79ce0e, 0xc1a34d90, 0x519af58a, 0xce407614, 0xb55ef4f7, 0x2a847769, 0x4363f131, 0xdcb972af, 0xa7a7f04c, 0x387d73d2, 0x7468fcfc, 0xebb27f62, 0x90acfd81, 0x0f767e1f, 0x6691f847, 0xf94b7bd9, 0x8255f93a, 0x1d8f7aa4, 0x1a7ee766, 0x85a464f8, 0xfebae61b, 0x61606585, 0x0887e3dd, 0x975d6043, 0xec43e2a0, 0x7399613e, 0x3f8cee10, 0xa0566d8e, 0xdb48ef6d, 0x44926cf3, 0x2d75eaab, 0xb2af6935, 0xc9b1ebd6, 0x566b6848}, {0x00000000, 0x65673b46, 0xcace768c, 0xafa94dca, 0x4eedeb59, 0x2b8ad01f, 0x84239dd5, 0xe144a693, 0x9ddbd6b2, 0xf8bcedf4, 0x5715a03e, 0x32729b78, 0xd3363deb, 0xb65106ad, 0x19f84b67, 0x7c9f7021, 0xe0c6ab25, 0x85a19063, 0x2a08dda9, 0x4f6fe6ef, 0xae2b407c, 0xcb4c7b3a, 0x64e536f0, 0x01820db6, 0x7d1d7d97, 0x187a46d1, 0xb7d30b1b, 0xd2b4305d, 0x33f096ce, 0x5697ad88, 0xf93ee042, 0x9c59db04, 0x1afc500b, 0x7f9b6b4d, 0xd0322687, 0xb5551dc1, 0x5411bb52, 0x31768014, 0x9edfcdde, 0xfbb8f698, 0x872786b9, 0xe240bdff, 0x4de9f035, 0x288ecb73, 0xc9ca6de0, 0xacad56a6, 0x03041b6c, 0x6663202a, 0xfa3afb2e, 0x9f5dc068, 0x30f48da2, 0x5593b6e4, 0xb4d71077, 0xd1b02b31, 0x7e1966fb, 0x1b7e5dbd, 0x67e12d9c, 0x028616da, 0xad2f5b10, 0xc8486056, 0x290cc6c5, 0x4c6bfd83, 0xe3c2b049, 0x86a58b0f, 0x35f8a016, 0x509f9b50, 0xff36d69a, 0x9a51eddc, 0x7b154b4f, 0x1e727009, 0xb1db3dc3, 0xd4bc0685, 0xa82376a4, 0xcd444de2, 0x62ed0028, 0x078a3b6e, 0xe6ce9dfd, 0x83a9a6bb, 0x2c00eb71, 0x4967d037, 0xd53e0b33, 0xb0593075, 0x1ff07dbf, 0x7a9746f9, 0x9bd3e06a, 0xfeb4db2c, 0x511d96e6, 0x347aada0, 0x48e5dd81, 0x2d82e6c7, 0x822bab0d, 0xe74c904b, 0x060836d8, 0x636f0d9e, 0xccc64054, 0xa9a17b12, 0x2f04f01d, 0x4a63cb5b, 0xe5ca8691, 0x80adbdd7, 0x61e91b44, 0x048e2002, 0xab276dc8, 0xce40568e, 0xb2df26af, 0xd7b81de9, 0x78115023, 0x1d766b65, 0xfc32cdf6, 0x9955f6b0, 0x36fcbb7a, 0x539b803c, 0xcfc25b38, 0xaaa5607e, 0x050c2db4, 0x606b16f2, 0x812fb061, 0xe4488b27, 0x4be1c6ed, 0x2e86fdab, 0x52198d8a, 0x377eb6cc, 0x98d7fb06, 0xfdb0c040, 0x1cf466d3, 0x79935d95, 0xd63a105f, 0xb35d2b19, 0x6bf1402c, 0x0e967b6a, 0xa13f36a0, 0xc4580de6, 0x251cab75, 0x407b9033, 0xefd2ddf9, 0x8ab5e6bf, 0xf62a969e, 0x934dadd8, 0x3ce4e012, 0x5983db54, 0xb8c77dc7, 0xdda04681, 0x72090b4b, 0x176e300d, 0x8b37eb09, 0xee50d04f, 0x41f99d85, 0x249ea6c3, 0xc5da0050, 0xa0bd3b16, 0x0f1476dc, 0x6a734d9a, 0x16ec3dbb, 0x738b06fd, 0xdc224b37, 0xb9457071, 0x5801d6e2, 0x3d66eda4, 0x92cfa06e, 0xf7a89b28, 0x710d1027, 0x146a2b61, 0xbbc366ab, 0xdea45ded, 0x3fe0fb7e, 0x5a87c038, 0xf52e8df2, 0x9049b6b4, 0xecd6c695, 0x89b1fdd3, 0x2618b019, 0x437f8b5f, 0xa23b2dcc, 0xc75c168a, 0x68f55b40, 0x0d926006, 0x91cbbb02, 0xf4ac8044, 0x5b05cd8e, 0x3e62f6c8, 0xdf26505b, 0xba416b1d, 0x15e826d7, 0x708f1d91, 0x0c106db0, 0x697756f6, 0xc6de1b3c, 0xa3b9207a, 0x42fd86e9, 0x279abdaf, 0x8833f065, 0xed54cb23, 0x5e09e03a, 0x3b6edb7c, 0x94c796b6, 0xf1a0adf0, 0x10e40b63, 0x75833025, 0xda2a7def, 0xbf4d46a9, 0xc3d23688, 0xa6b50dce, 0x091c4004, 0x6c7b7b42, 0x8d3fddd1, 0xe858e697, 0x47f1ab5d, 0x2296901b, 0xbecf4b1f, 0xdba87059, 0x74013d93, 0x116606d5, 0xf022a046, 0x95459b00, 0x3aecd6ca, 0x5f8bed8c, 0x23149dad, 0x4673a6eb, 0xe9daeb21, 0x8cbdd067, 0x6df976f4, 0x089e4db2, 0xa7370078, 0xc2503b3e, 0x44f5b031, 0x21928b77, 0x8e3bc6bd, 0xeb5cfdfb, 0x0a185b68, 0x6f7f602e, 0xc0d62de4, 0xa5b116a2, 0xd92e6683, 0xbc495dc5, 0x13e0100f, 0x76872b49, 0x97c38dda, 0xf2a4b69c, 0x5d0dfb56, 0x386ac010, 0xa4331b14, 0xc1542052, 0x6efd6d98, 0x0b9a56de, 0xeadef04d, 0x8fb9cb0b, 0x201086c1, 0x4577bd87, 0x39e8cda6, 0x5c8ff6e0, 0xf326bb2a, 0x9641806c, 0x770526ff, 0x12621db9, 0xbdcb5073, 0xd8ac6b35}, {0x00000000, 0xd7e28058, 0x74b406f1, 0xa35686a9, 0xe9680de2, 0x3e8a8dba, 0x9ddc0b13, 0x4a3e8b4b, 0x09a11d85, 0xde439ddd, 0x7d151b74, 0xaaf79b2c, 0xe0c91067, 0x372b903f, 0x947d1696, 0x439f96ce, 0x13423b0a, 0xc4a0bb52, 0x67f63dfb, 0xb014bda3, 0xfa2a36e8, 0x2dc8b6b0, 0x8e9e3019, 0x597cb041, 0x1ae3268f, 0xcd01a6d7, 0x6e57207e, 0xb9b5a026, 0xf38b2b6d, 0x2469ab35, 0x873f2d9c, 0x50ddadc4, 0x26847614, 0xf166f64c, 0x523070e5, 0x85d2f0bd, 0xcfec7bf6, 0x180efbae, 0xbb587d07, 0x6cbafd5f, 0x2f256b91, 0xf8c7ebc9, 0x5b916d60, 0x8c73ed38, 0xc64d6673, 0x11afe62b, 0xb2f96082, 0x651be0da, 0x35c64d1e, 0xe224cd46, 0x41724bef, 0x9690cbb7, 0xdcae40fc, 0x0b4cc0a4, 0xa81a460d, 0x7ff8c655, 0x3c67509b, 0xeb85d0c3, 0x48d3566a, 0x9f31d632, 0xd50f5d79, 0x02eddd21, 0xa1bb5b88, 0x7659dbd0, 0x4d08ec28, 0x9aea6c70, 0x39bcead9, 0xee5e6a81, 0xa460e1ca, 0x73826192, 0xd0d4e73b, 0x07366763, 0x44a9f1ad, 0x934b71f5, 0x301df75c, 0xe7ff7704, 0xadc1fc4f, 0x7a237c17, 0xd975fabe, 0x0e977ae6, 0x5e4ad722, 0x89a8577a, 0x2afed1d3, 0xfd1c518b, 0xb722dac0, 0x60c05a98, 0xc396dc31, 0x14745c69, 0x57ebcaa7, 0x80094aff, 0x235fcc56, 0xf4bd4c0e, 0xbe83c745, 0x6961471d, 0xca37c1b4, 0x1dd541ec, 0x6b8c9a3c, 0xbc6e1a64, 0x1f389ccd, 0xc8da1c95, 0x82e497de, 0x55061786, 0xf650912f, 0x21b21177, 0x622d87b9, 0xb5cf07e1, 0x16998148, 0xc17b0110, 0x8b458a5b, 0x5ca70a03, 0xfff18caa, 0x28130cf2, 0x78cea136, 0xaf2c216e, 0x0c7aa7c7, 0xdb98279f, 0x91a6acd4, 0x46442c8c, 0xe512aa25, 0x32f02a7d, 0x716fbcb3, 0xa68d3ceb, 0x05dbba42, 0xd2393a1a, 0x9807b151, 0x4fe53109, 0xecb3b7a0, 0x3b5137f8, 0x9a11d850, 0x4df35808, 0xeea5dea1, 0x39475ef9, 0x7379d5b2, 0xa49b55ea, 0x07cdd343, 0xd02f531b, 0x93b0c5d5, 0x4452458d, 0xe704c324, 0x30e6437c, 0x7ad8c837, 0xad3a486f, 0x0e6ccec6, 0xd98e4e9e, 0x8953e35a, 0x5eb16302, 0xfde7e5ab, 0x2a0565f3, 0x603beeb8, 0xb7d96ee0, 0x148fe849, 0xc36d6811, 0x80f2fedf, 0x57107e87, 0xf446f82e, 0x23a47876, 0x699af33d, 0xbe787365, 0x1d2ef5cc, 0xcacc7594, 0xbc95ae44, 0x6b772e1c, 0xc821a8b5, 0x1fc328ed, 0x55fda3a6, 0x821f23fe, 0x2149a557, 0xf6ab250f, 0xb534b3c1, 0x62d63399, 0xc180b530, 0x16623568, 0x5c5cbe23, 0x8bbe3e7b, 0x28e8b8d2, 0xff0a388a, 0xafd7954e, 0x78351516, 0xdb6393bf, 0x0c8113e7, 0x46bf98ac, 0x915d18f4, 0x320b9e5d, 0xe5e91e05, 0xa67688cb, 0x71940893, 0xd2c28e3a, 0x05200e62, 0x4f1e8529, 0x98fc0571, 0x3baa83d8, 0xec480380, 0xd7193478, 0x00fbb420, 0xa3ad3289, 0x744fb2d1, 0x3e71399a, 0xe993b9c2, 0x4ac53f6b, 0x9d27bf33, 0xdeb829fd, 0x095aa9a5, 0xaa0c2f0c, 0x7deeaf54, 0x37d0241f, 0xe032a447, 0x436422ee, 0x9486a2b6, 0xc45b0f72, 0x13b98f2a, 0xb0ef0983, 0x670d89db, 0x2d330290, 0xfad182c8, 0x59870461, 0x8e658439, 0xcdfa12f7, 0x1a1892af, 0xb94e1406, 0x6eac945e, 0x24921f15, 0xf3709f4d, 0x502619e4, 0x87c499bc, 0xf19d426c, 0x267fc234, 0x8529449d, 0x52cbc4c5, 0x18f54f8e, 0xcf17cfd6, 0x6c41497f, 0xbba3c927, 0xf83c5fe9, 0x2fdedfb1, 0x8c885918, 0x5b6ad940, 0x1154520b, 0xc6b6d253, 0x65e054fa, 0xb202d4a2, 0xe2df7966, 0x353df93e, 0x966b7f97, 0x4189ffcf, 0x0bb77484, 0xdc55f4dc, 0x7f037275, 0xa8e1f22d, 0xeb7e64e3, 0x3c9ce4bb, 0x9fca6212, 0x4828e24a, 0x02166901, 0xd5f4e959, 0x76a26ff0, 0xa140efa8}, {0x00000000, 0xef52b6e1, 0x05d46b83, 0xea86dd62, 0x0ba8d706, 0xe4fa61e7, 0x0e7cbc85, 0xe12e0a64, 0x1751ae0c, 0xf80318ed, 0x1285c58f, 0xfdd7736e, 0x1cf9790a, 0xf3abcfeb, 0x192d1289, 0xf67fa468, 0x2ea35c18, 0xc1f1eaf9, 0x2b77379b, 0xc425817a, 0x250b8b1e, 0xca593dff, 0x20dfe09d, 0xcf8d567c, 0x39f2f214, 0xd6a044f5, 0x3c269997, 0xd3742f76, 0x325a2512, 0xdd0893f3, 0x378e4e91, 0xd8dcf870, 0x5d46b830, 0xb2140ed1, 0x5892d3b3, 0xb7c06552, 0x56ee6f36, 0xb9bcd9d7, 0x533a04b5, 0xbc68b254, 0x4a17163c, 0xa545a0dd, 0x4fc37dbf, 0xa091cb5e, 0x41bfc13a, 0xaeed77db, 0x446baab9, 0xab391c58, 0x73e5e428, 0x9cb752c9, 0x76318fab, 0x9963394a, 0x784d332e, 0x971f85cf, 0x7d9958ad, 0x92cbee4c, 0x64b44a24, 0x8be6fcc5, 0x616021a7, 0x8e329746, 0x6f1c9d22, 0x804e2bc3, 0x6ac8f6a1, 0x859a4040, 0xba8d7060, 0x55dfc681, 0xbf591be3, 0x500bad02, 0xb125a766, 0x5e771187, 0xb4f1cce5, 0x5ba37a04, 0xaddcde6c, 0x428e688d, 0xa808b5ef, 0x475a030e, 0xa674096a, 0x4926bf8b, 0xa3a062e9, 0x4cf2d408, 0x942e2c78, 0x7b7c9a99, 0x91fa47fb, 0x7ea8f11a, 0x9f86fb7e, 0x70d44d9f, 0x9a5290fd, 0x7500261c, 0x837f8274, 0x6c2d3495, 0x86abe9f7, 0x69f95f16, 0x88d75572, 0x6785e393, 0x8d033ef1, 0x62518810, 0xe7cbc850, 0x08997eb1, 0xe21fa3d3, 0x0d4d1532, 0xec631f56, 0x0331a9b7, 0xe9b774d5, 0x06e5c234, 0xf09a665c, 0x1fc8d0bd, 0xf54e0ddf, 0x1a1cbb3e, 0xfb32b15a, 0x146007bb, 0xfee6dad9, 0x11b46c38, 0xc9689448, 0x263a22a9, 0xccbcffcb, 0x23ee492a, 0xc2c0434e, 0x2d92f5af, 0xc71428cd, 0x28469e2c, 0xde393a44, 0x316b8ca5, 0xdbed51c7, 0x34bfe726, 0xd591ed42, 0x3ac35ba3, 0xd04586c1, 0x3f173020, 0xae6be681, 0x41395060, 0xabbf8d02, 0x44ed3be3, 0xa5c33187, 0x4a918766, 0xa0175a04, 0x4f45ece5, 0xb93a488d, 0x5668fe6c, 0xbcee230e, 0x53bc95ef, 0xb2929f8b, 0x5dc0296a, 0xb746f408, 0x581442e9, 0x80c8ba99, 0x6f9a0c78, 0x851cd11a, 0x6a4e67fb, 0x8b606d9f, 0x6432db7e, 0x8eb4061c, 0x61e6b0fd, 0x97991495, 0x78cba274, 0x924d7f16, 0x7d1fc9f7, 0x9c31c393, 0x73637572, 0x99e5a810, 0x76b71ef1, 0xf32d5eb1, 0x1c7fe850, 0xf6f93532, 0x19ab83d3, 0xf88589b7, 0x17d73f56, 0xfd51e234, 0x120354d5, 0xe47cf0bd, 0x0b2e465c, 0xe1a89b3e, 0x0efa2ddf, 0xefd427bb, 0x0086915a, 0xea004c38, 0x0552fad9, 0xdd8e02a9, 0x32dcb448, 0xd85a692a, 0x3708dfcb, 0xd626d5af, 0x3974634e, 0xd3f2be2c, 0x3ca008cd, 0xcadfaca5, 0x258d1a44, 0xcf0bc726, 0x205971c7, 0xc1777ba3, 0x2e25cd42, 0xc4a31020, 0x2bf1a6c1, 0x14e696e1, 0xfbb42000, 0x1132fd62, 0xfe604b83, 0x1f4e41e7, 0xf01cf706, 0x1a9a2a64, 0xf5c89c85, 0x03b738ed, 0xece58e0c, 0x0663536e, 0xe931e58f, 0x081fefeb, 0xe74d590a, 0x0dcb8468, 0xe2993289, 0x3a45caf9, 0xd5177c18, 0x3f91a17a, 0xd0c3179b, 0x31ed1dff, 0xdebfab1e, 0x3439767c, 0xdb6bc09d, 0x2d1464f5, 0xc246d214, 0x28c00f76, 0xc792b997, 0x26bcb3f3, 0xc9ee0512, 0x2368d870, 0xcc3a6e91, 0x49a02ed1, 0xa6f29830, 0x4c744552, 0xa326f3b3, 0x4208f9d7, 0xad5a4f36, 0x47dc9254, 0xa88e24b5, 0x5ef180dd, 0xb1a3363c, 0x5b25eb5e, 0xb4775dbf, 0x555957db, 0xba0be13a, 0x508d3c58, 0xbfdf8ab9, 0x670372c9, 0x8851c428, 0x62d7194a, 0x8d85afab, 0x6caba5cf, 0x83f9132e, 0x697fce4c, 0x862d78ad, 0x7052dcc5, 0x9f006a24, 0x7586b746, 0x9ad401a7, 0x7bfa0bc3, 0x94a8bd22, 0x7e2e6040, 0x917cd6a1}, {0x00000000, 0x87a6cb43, 0xd43c90c7, 0x539a5b84, 0x730827cf, 0xf4aeec8c, 0xa734b708, 0x20927c4b, 0xe6104f9e, 0x61b684dd, 0x322cdf59, 0xb58a141a, 0x95186851, 0x12bea312, 0x4124f896, 0xc68233d5, 0x1751997d, 0x90f7523e, 0xc36d09ba, 0x44cbc2f9, 0x6459beb2, 0xe3ff75f1, 0xb0652e75, 0x37c3e536, 0xf141d6e3, 0x76e71da0, 0x257d4624, 0xa2db8d67, 0x8249f12c, 0x05ef3a6f, 0x567561eb, 0xd1d3aaa8, 0x2ea332fa, 0xa905f9b9, 0xfa9fa23d, 0x7d39697e, 0x5dab1535, 0xda0dde76, 0x899785f2, 0x0e314eb1, 0xc8b37d64, 0x4f15b627, 0x1c8feda3, 0x9b2926e0, 0xbbbb5aab, 0x3c1d91e8, 0x6f87ca6c, 0xe821012f, 0x39f2ab87, 0xbe5460c4, 0xedce3b40, 0x6a68f003, 0x4afa8c48, 0xcd5c470b, 0x9ec61c8f, 0x1960d7cc, 0xdfe2e419, 0x58442f5a, 0x0bde74de, 0x8c78bf9d, 0xaceac3d6, 0x2b4c0895, 0x78d65311, 0xff709852, 0x5d4665f4, 0xdae0aeb7, 0x897af533, 0x0edc3e70, 0x2e4e423b, 0xa9e88978, 0xfa72d2fc, 0x7dd419bf, 0xbb562a6a, 0x3cf0e129, 0x6f6abaad, 0xe8cc71ee, 0xc85e0da5, 0x4ff8c6e6, 0x1c629d62, 0x9bc45621, 0x4a17fc89, 0xcdb137ca, 0x9e2b6c4e, 0x198da70d, 0x391fdb46, 0xbeb91005, 0xed234b81, 0x6a8580c2, 0xac07b317, 0x2ba17854, 0x783b23d0, 0xff9de893, 0xdf0f94d8, 0x58a95f9b, 0x0b33041f, 0x8c95cf5c, 0x73e5570e, 0xf4439c4d, 0xa7d9c7c9, 0x207f0c8a, 0x00ed70c1, 0x874bbb82, 0xd4d1e006, 0x53772b45, 0x95f51890, 0x1253d3d3, 0x41c98857, 0xc66f4314, 0xe6fd3f5f, 0x615bf41c, 0x32c1af98, 0xb56764db, 0x64b4ce73, 0xe3120530, 0xb0885eb4, 0x372e95f7, 0x17bce9bc, 0x901a22ff, 0xc380797b, 0x4426b238, 0x82a481ed, 0x05024aae, 0x5698112a, 0xd13eda69, 0xf1aca622, 0x760a6d61, 0x259036e5, 0xa236fda6, 0xba8ccbe8, 0x3d2a00ab, 0x6eb05b2f, 0xe916906c, 0xc984ec27, 0x4e222764, 0x1db87ce0, 0x9a1eb7a3, 0x5c9c8476, 0xdb3a4f35, 0x88a014b1, 0x0f06dff2, 0x2f94a3b9, 0xa83268fa, 0xfba8337e, 0x7c0ef83d, 0xaddd5295, 0x2a7b99d6, 0x79e1c252, 0xfe470911, 0xded5755a, 0x5973be19, 0x0ae9e59d, 0x8d4f2ede, 0x4bcd1d0b, 0xcc6bd648, 0x9ff18dcc, 0x1857468f, 0x38c53ac4, 0xbf63f187, 0xecf9aa03, 0x6b5f6140, 0x942ff912, 0x13893251, 0x401369d5, 0xc7b5a296, 0xe727dedd, 0x6081159e, 0x331b4e1a, 0xb4bd8559, 0x723fb68c, 0xf5997dcf, 0xa603264b, 0x21a5ed08, 0x01379143, 0x86915a00, 0xd50b0184, 0x52adcac7, 0x837e606f, 0x04d8ab2c, 0x5742f0a8, 0xd0e43beb, 0xf07647a0, 0x77d08ce3, 0x244ad767, 0xa3ec1c24, 0x656e2ff1, 0xe2c8e4b2, 0xb152bf36, 0x36f47475, 0x1666083e, 0x91c0c37d, 0xc25a98f9, 0x45fc53ba, 0xe7caae1c, 0x606c655f, 0x33f63edb, 0xb450f598, 0x94c289d3, 0x13644290, 0x40fe1914, 0xc758d257, 0x01dae182, 0x867c2ac1, 0xd5e67145, 0x5240ba06, 0x72d2c64d, 0xf5740d0e, 0xa6ee568a, 0x21489dc9, 0xf09b3761, 0x773dfc22, 0x24a7a7a6, 0xa3016ce5, 0x839310ae, 0x0435dbed, 0x57af8069, 0xd0094b2a, 0x168b78ff, 0x912db3bc, 0xc2b7e838, 0x4511237b, 0x65835f30, 0xe2259473, 0xb1bfcff7, 0x361904b4, 0xc9699ce6, 0x4ecf57a5, 0x1d550c21, 0x9af3c762, 0xba61bb29, 0x3dc7706a, 0x6e5d2bee, 0xe9fbe0ad, 0x2f79d378, 0xa8df183b, 0xfb4543bf, 0x7ce388fc, 0x5c71f4b7, 0xdbd73ff4, 0x884d6470, 0x0febaf33, 0xde38059b, 0x599eced8, 0x0a04955c, 0x8da25e1f, 0xad302254, 0x2a96e917, 0x790cb293, 0xfeaa79d0, 0x38284a05, 0xbf8e8146, 0xec14dac2, 0x6bb21181, 0x4b206dca, 0xcc86a689, 0x9f1cfd0d, 0x18ba364e}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x0000000000000000, 0x43cba68700000000, 0xc7903cd400000000, 0x845b9a5300000000, 0xcf27087300000000, 0x8cecaef400000000, 0x08b734a700000000, 0x4b7c922000000000, 0x9e4f10e600000000, 0xdd84b66100000000, 0x59df2c3200000000, 0x1a148ab500000000, 0x5168189500000000, 0x12a3be1200000000, 0x96f8244100000000, 0xd53382c600000000, 0x7d99511700000000, 0x3e52f79000000000, 0xba096dc300000000, 0xf9c2cb4400000000, 0xb2be596400000000, 0xf175ffe300000000, 0x752e65b000000000, 0x36e5c33700000000, 0xe3d641f100000000, 0xa01de77600000000, 0x24467d2500000000, 0x678ddba200000000, 0x2cf1498200000000, 0x6f3aef0500000000, 0xeb61755600000000, 0xa8aad3d100000000, 0xfa32a32e00000000, 0xb9f905a900000000, 0x3da29ffa00000000, 0x7e69397d00000000, 0x3515ab5d00000000, 0x76de0dda00000000, 0xf285978900000000, 0xb14e310e00000000, 0x647db3c800000000, 0x27b6154f00000000, 0xa3ed8f1c00000000, 0xe026299b00000000, 0xab5abbbb00000000, 0xe8911d3c00000000, 0x6cca876f00000000, 0x2f0121e800000000, 0x87abf23900000000, 0xc46054be00000000, 0x403bceed00000000, 0x03f0686a00000000, 0x488cfa4a00000000, 0x0b475ccd00000000, 0x8f1cc69e00000000, 0xccd7601900000000, 0x19e4e2df00000000, 0x5a2f445800000000, 0xde74de0b00000000, 0x9dbf788c00000000, 0xd6c3eaac00000000, 0x95084c2b00000000, 0x1153d67800000000, 0x529870ff00000000, 0xf465465d00000000, 0xb7aee0da00000000, 0x33f57a8900000000, 0x703edc0e00000000, 0x3b424e2e00000000, 0x7889e8a900000000, 0xfcd272fa00000000, 0xbf19d47d00000000, 0x6a2a56bb00000000, 0x29e1f03c00000000, 0xadba6a6f00000000, 0xee71cce800000000, 0xa50d5ec800000000, 0xe6c6f84f00000000, 0x629d621c00000000, 0x2156c49b00000000, 0x89fc174a00000000, 0xca37b1cd00000000, 0x4e6c2b9e00000000, 0x0da78d1900000000, 0x46db1f3900000000, 0x0510b9be00000000, 0x814b23ed00000000, 0xc280856a00000000, 0x17b307ac00000000, 0x5478a12b00000000, 0xd0233b7800000000, 0x93e89dff00000000, 0xd8940fdf00000000, 0x9b5fa95800000000, 0x1f04330b00000000, 0x5ccf958c00000000, 0x0e57e57300000000, 0x4d9c43f400000000, 0xc9c7d9a700000000, 0x8a0c7f2000000000, 0xc170ed0000000000, 0x82bb4b8700000000, 0x06e0d1d400000000, 0x452b775300000000, 0x9018f59500000000, 0xd3d3531200000000, 0x5788c94100000000, 0x14436fc600000000, 0x5f3ffde600000000, 0x1cf45b6100000000, 0x98afc13200000000, 0xdb6467b500000000, 0x73ceb46400000000, 0x300512e300000000, 0xb45e88b000000000, 0xf7952e3700000000, 0xbce9bc1700000000, 0xff221a9000000000, 0x7b7980c300000000, 0x38b2264400000000, 0xed81a48200000000, 0xae4a020500000000, 0x2a11985600000000, 0x69da3ed100000000, 0x22a6acf100000000, 0x616d0a7600000000, 0xe536902500000000, 0xa6fd36a200000000, 0xe8cb8cba00000000, 0xab002a3d00000000, 0x2f5bb06e00000000, 0x6c9016e900000000, 0x27ec84c900000000, 0x6427224e00000000, 0xe07cb81d00000000, 0xa3b71e9a00000000, 0x76849c5c00000000, 0x354f3adb00000000, 0xb114a08800000000, 0xf2df060f00000000, 0xb9a3942f00000000, 0xfa6832a800000000, 0x7e33a8fb00000000, 0x3df80e7c00000000, 0x9552ddad00000000, 0xd6997b2a00000000, 0x52c2e17900000000, 0x110947fe00000000, 0x5a75d5de00000000, 0x19be735900000000, 0x9de5e90a00000000, 0xde2e4f8d00000000, 0x0b1dcd4b00000000, 0x48d66bcc00000000, 0xcc8df19f00000000, 0x8f46571800000000, 0xc43ac53800000000, 0x87f163bf00000000, 0x03aaf9ec00000000, 0x40615f6b00000000, 0x12f92f9400000000, 0x5132891300000000, 0xd569134000000000, 0x96a2b5c700000000, 0xddde27e700000000, 0x9e15816000000000, 0x1a4e1b3300000000, 0x5985bdb400000000, 0x8cb63f7200000000, 0xcf7d99f500000000, 0x4b2603a600000000, 0x08eda52100000000, 0x4391370100000000, 0x005a918600000000, 0x84010bd500000000, 0xc7caad5200000000, 0x6f607e8300000000, 0x2cabd80400000000, 0xa8f0425700000000, 0xeb3be4d000000000, 0xa04776f000000000, 0xe38cd07700000000, 0x67d74a2400000000, 0x241ceca300000000, 0xf12f6e6500000000, 0xb2e4c8e200000000, 0x36bf52b100000000, 0x7574f43600000000, 0x3e08661600000000, 0x7dc3c09100000000, 0xf9985ac200000000, 0xba53fc4500000000, 0x1caecae700000000, 0x5f656c6000000000, 0xdb3ef63300000000, 0x98f550b400000000, 0xd389c29400000000, 0x9042641300000000, 0x1419fe4000000000, 0x57d258c700000000, 0x82e1da0100000000, 0xc12a7c8600000000, 0x4571e6d500000000, 0x06ba405200000000, 0x4dc6d27200000000, 0x0e0d74f500000000, 0x8a56eea600000000, 0xc99d482100000000, 0x61379bf000000000, 0x22fc3d7700000000, 0xa6a7a72400000000, 0xe56c01a300000000, 0xae10938300000000, 0xeddb350400000000, 0x6980af5700000000, 0x2a4b09d000000000, 0xff788b1600000000, 0xbcb32d9100000000, 0x38e8b7c200000000, 0x7b23114500000000, 0x305f836500000000, 0x739425e200000000, 0xf7cfbfb100000000, 0xb404193600000000, 0xe69c69c900000000, 0xa557cf4e00000000, 0x210c551d00000000, 0x62c7f39a00000000, 0x29bb61ba00000000, 0x6a70c73d00000000, 0xee2b5d6e00000000, 0xade0fbe900000000, 0x78d3792f00000000, 0x3b18dfa800000000, 0xbf4345fb00000000, 0xfc88e37c00000000, 0xb7f4715c00000000, 0xf43fd7db00000000, 0x70644d8800000000, 0x33afeb0f00000000, 0x9b0538de00000000, 0xd8ce9e5900000000, 0x5c95040a00000000, 0x1f5ea28d00000000, 0x542230ad00000000, 0x17e9962a00000000, 0x93b20c7900000000, 0xd079aafe00000000, 0x054a283800000000, 0x46818ebf00000000, 0xc2da14ec00000000, 0x8111b26b00000000, 0xca6d204b00000000, 0x89a686cc00000000, 0x0dfd1c9f00000000, 0x4e36ba1800000000}, {0x0000000000000000, 0xe1b652ef00000000, 0x836bd40500000000, 0x62dd86ea00000000, 0x06d7a80b00000000, 0xe761fae400000000, 0x85bc7c0e00000000, 0x640a2ee100000000, 0x0cae511700000000, 0xed1803f800000000, 0x8fc5851200000000, 0x6e73d7fd00000000, 0x0a79f91c00000000, 0xebcfabf300000000, 0x89122d1900000000, 0x68a47ff600000000, 0x185ca32e00000000, 0xf9eaf1c100000000, 0x9b37772b00000000, 0x7a8125c400000000, 0x1e8b0b2500000000, 0xff3d59ca00000000, 0x9de0df2000000000, 0x7c568dcf00000000, 0x14f2f23900000000, 0xf544a0d600000000, 0x9799263c00000000, 0x762f74d300000000, 0x12255a3200000000, 0xf39308dd00000000, 0x914e8e3700000000, 0x70f8dcd800000000, 0x30b8465d00000000, 0xd10e14b200000000, 0xb3d3925800000000, 0x5265c0b700000000, 0x366fee5600000000, 0xd7d9bcb900000000, 0xb5043a5300000000, 0x54b268bc00000000, 0x3c16174a00000000, 0xdda045a500000000, 0xbf7dc34f00000000, 0x5ecb91a000000000, 0x3ac1bf4100000000, 0xdb77edae00000000, 0xb9aa6b4400000000, 0x581c39ab00000000, 0x28e4e57300000000, 0xc952b79c00000000, 0xab8f317600000000, 0x4a39639900000000, 0x2e334d7800000000, 0xcf851f9700000000, 0xad58997d00000000, 0x4ceecb9200000000, 0x244ab46400000000, 0xc5fce68b00000000, 0xa721606100000000, 0x4697328e00000000, 0x229d1c6f00000000, 0xc32b4e8000000000, 0xa1f6c86a00000000, 0x40409a8500000000, 0x60708dba00000000, 0x81c6df5500000000, 0xe31b59bf00000000, 0x02ad0b5000000000, 0x66a725b100000000, 0x8711775e00000000, 0xe5ccf1b400000000, 0x047aa35b00000000, 0x6cdedcad00000000, 0x8d688e4200000000, 0xefb508a800000000, 0x0e035a4700000000, 0x6a0974a600000000, 0x8bbf264900000000, 0xe962a0a300000000, 0x08d4f24c00000000, 0x782c2e9400000000, 0x999a7c7b00000000, 0xfb47fa9100000000, 0x1af1a87e00000000, 0x7efb869f00000000, 0x9f4dd47000000000, 0xfd90529a00000000, 0x1c26007500000000, 0x74827f8300000000, 0x95342d6c00000000, 0xf7e9ab8600000000, 0x165ff96900000000, 0x7255d78800000000, 0x93e3856700000000, 0xf13e038d00000000, 0x1088516200000000, 0x50c8cbe700000000, 0xb17e990800000000, 0xd3a31fe200000000, 0x32154d0d00000000, 0x561f63ec00000000, 0xb7a9310300000000, 0xd574b7e900000000, 0x34c2e50600000000, 0x5c669af000000000, 0xbdd0c81f00000000, 0xdf0d4ef500000000, 0x3ebb1c1a00000000, 0x5ab132fb00000000, 0xbb07601400000000, 0xd9dae6fe00000000, 0x386cb41100000000, 0x489468c900000000, 0xa9223a2600000000, 0xcbffbccc00000000, 0x2a49ee2300000000, 0x4e43c0c200000000, 0xaff5922d00000000, 0xcd2814c700000000, 0x2c9e462800000000, 0x443a39de00000000, 0xa58c6b3100000000, 0xc751eddb00000000, 0x26e7bf3400000000, 0x42ed91d500000000, 0xa35bc33a00000000, 0xc18645d000000000, 0x2030173f00000000, 0x81e66bae00000000, 0x6050394100000000, 0x028dbfab00000000, 0xe33bed4400000000, 0x8731c3a500000000, 0x6687914a00000000, 0x045a17a000000000, 0xe5ec454f00000000, 0x8d483ab900000000, 0x6cfe685600000000, 0x0e23eebc00000000, 0xef95bc5300000000, 0x8b9f92b200000000, 0x6a29c05d00000000, 0x08f446b700000000, 0xe942145800000000, 0x99bac88000000000, 0x780c9a6f00000000, 0x1ad11c8500000000, 0xfb674e6a00000000, 0x9f6d608b00000000, 0x7edb326400000000, 0x1c06b48e00000000, 0xfdb0e66100000000, 0x9514999700000000, 0x74a2cb7800000000, 0x167f4d9200000000, 0xf7c91f7d00000000, 0x93c3319c00000000, 0x7275637300000000, 0x10a8e59900000000, 0xf11eb77600000000, 0xb15e2df300000000, 0x50e87f1c00000000, 0x3235f9f600000000, 0xd383ab1900000000, 0xb78985f800000000, 0x563fd71700000000, 0x34e251fd00000000, 0xd554031200000000, 0xbdf07ce400000000, 0x5c462e0b00000000, 0x3e9ba8e100000000, 0xdf2dfa0e00000000, 0xbb27d4ef00000000, 0x5a91860000000000, 0x384c00ea00000000, 0xd9fa520500000000, 0xa9028edd00000000, 0x48b4dc3200000000, 0x2a695ad800000000, 0xcbdf083700000000, 0xafd526d600000000, 0x4e63743900000000, 0x2cbef2d300000000, 0xcd08a03c00000000, 0xa5acdfca00000000, 0x441a8d2500000000, 0x26c70bcf00000000, 0xc771592000000000, 0xa37b77c100000000, 0x42cd252e00000000, 0x2010a3c400000000, 0xc1a6f12b00000000, 0xe196e61400000000, 0x0020b4fb00000000, 0x62fd321100000000, 0x834b60fe00000000, 0xe7414e1f00000000, 0x06f71cf000000000, 0x642a9a1a00000000, 0x859cc8f500000000, 0xed38b70300000000, 0x0c8ee5ec00000000, 0x6e53630600000000, 0x8fe531e900000000, 0xebef1f0800000000, 0x0a594de700000000, 0x6884cb0d00000000, 0x893299e200000000, 0xf9ca453a00000000, 0x187c17d500000000, 0x7aa1913f00000000, 0x9b17c3d000000000, 0xff1ded3100000000, 0x1eabbfde00000000, 0x7c76393400000000, 0x9dc06bdb00000000, 0xf564142d00000000, 0x14d246c200000000, 0x760fc02800000000, 0x97b992c700000000, 0xf3b3bc2600000000, 0x1205eec900000000, 0x70d8682300000000, 0x916e3acc00000000, 0xd12ea04900000000, 0x3098f2a600000000, 0x5245744c00000000, 0xb3f326a300000000, 0xd7f9084200000000, 0x364f5aad00000000, 0x5492dc4700000000, 0xb5248ea800000000, 0xdd80f15e00000000, 0x3c36a3b100000000, 0x5eeb255b00000000, 0xbf5d77b400000000, 0xdb57595500000000, 0x3ae10bba00000000, 0x583c8d5000000000, 0xb98adfbf00000000, 0xc972036700000000, 0x28c4518800000000, 0x4a19d76200000000, 0xabaf858d00000000, 0xcfa5ab6c00000000, 0x2e13f98300000000, 0x4cce7f6900000000, 0xad782d8600000000, 0xc5dc527000000000, 0x246a009f00000000, 0x46b7867500000000, 0xa701d49a00000000, 0xc30bfa7b00000000, 0x22bda89400000000, 0x40602e7e00000000, 0xa1d67c9100000000}, {0x0000000000000000, 0x5880e2d700000000, 0xf106b47400000000, 0xa98656a300000000, 0xe20d68e900000000, 0xba8d8a3e00000000, 0x130bdc9d00000000, 0x4b8b3e4a00000000, 0x851da10900000000, 0xdd9d43de00000000, 0x741b157d00000000, 0x2c9bf7aa00000000, 0x6710c9e000000000, 0x3f902b3700000000, 0x96167d9400000000, 0xce969f4300000000, 0x0a3b421300000000, 0x52bba0c400000000, 0xfb3df66700000000, 0xa3bd14b000000000, 0xe8362afa00000000, 0xb0b6c82d00000000, 0x19309e8e00000000, 0x41b07c5900000000, 0x8f26e31a00000000, 0xd7a601cd00000000, 0x7e20576e00000000, 0x26a0b5b900000000, 0x6d2b8bf300000000, 0x35ab692400000000, 0x9c2d3f8700000000, 0xc4addd5000000000, 0x1476842600000000, 0x4cf666f100000000, 0xe570305200000000, 0xbdf0d28500000000, 0xf67beccf00000000, 0xaefb0e1800000000, 0x077d58bb00000000, 0x5ffdba6c00000000, 0x916b252f00000000, 0xc9ebc7f800000000, 0x606d915b00000000, 0x38ed738c00000000, 0x73664dc600000000, 0x2be6af1100000000, 0x8260f9b200000000, 0xdae01b6500000000, 0x1e4dc63500000000, 0x46cd24e200000000, 0xef4b724100000000, 0xb7cb909600000000, 0xfc40aedc00000000, 0xa4c04c0b00000000, 0x0d461aa800000000, 0x55c6f87f00000000, 0x9b50673c00000000, 0xc3d085eb00000000, 0x6a56d34800000000, 0x32d6319f00000000, 0x795d0fd500000000, 0x21dded0200000000, 0x885bbba100000000, 0xd0db597600000000, 0x28ec084d00000000, 0x706cea9a00000000, 0xd9eabc3900000000, 0x816a5eee00000000, 0xcae160a400000000, 0x9261827300000000, 0x3be7d4d000000000, 0x6367360700000000, 0xadf1a94400000000, 0xf5714b9300000000, 0x5cf71d3000000000, 0x0477ffe700000000, 0x4ffcc1ad00000000, 0x177c237a00000000, 0xbefa75d900000000, 0xe67a970e00000000, 0x22d74a5e00000000, 0x7a57a88900000000, 0xd3d1fe2a00000000, 0x8b511cfd00000000, 0xc0da22b700000000, 0x985ac06000000000, 0x31dc96c300000000, 0x695c741400000000, 0xa7caeb5700000000, 0xff4a098000000000, 0x56cc5f2300000000, 0x0e4cbdf400000000, 0x45c783be00000000, 0x1d47616900000000, 0xb4c137ca00000000, 0xec41d51d00000000, 0x3c9a8c6b00000000, 0x641a6ebc00000000, 0xcd9c381f00000000, 0x951cdac800000000, 0xde97e48200000000, 0x8617065500000000, 0x2f9150f600000000, 0x7711b22100000000, 0xb9872d6200000000, 0xe107cfb500000000, 0x4881991600000000, 0x10017bc100000000, 0x5b8a458b00000000, 0x030aa75c00000000, 0xaa8cf1ff00000000, 0xf20c132800000000, 0x36a1ce7800000000, 0x6e212caf00000000, 0xc7a77a0c00000000, 0x9f2798db00000000, 0xd4aca69100000000, 0x8c2c444600000000, 0x25aa12e500000000, 0x7d2af03200000000, 0xb3bc6f7100000000, 0xeb3c8da600000000, 0x42badb0500000000, 0x1a3a39d200000000, 0x51b1079800000000, 0x0931e54f00000000, 0xa0b7b3ec00000000, 0xf837513b00000000, 0x50d8119a00000000, 0x0858f34d00000000, 0xa1dea5ee00000000, 0xf95e473900000000, 0xb2d5797300000000, 0xea559ba400000000, 0x43d3cd0700000000, 0x1b532fd000000000, 0xd5c5b09300000000, 0x8d45524400000000, 0x24c304e700000000, 0x7c43e63000000000, 0x37c8d87a00000000, 0x6f483aad00000000, 0xc6ce6c0e00000000, 0x9e4e8ed900000000, 0x5ae3538900000000, 0x0263b15e00000000, 0xabe5e7fd00000000, 0xf365052a00000000, 0xb8ee3b6000000000, 0xe06ed9b700000000, 0x49e88f1400000000, 0x11686dc300000000, 0xdffef28000000000, 0x877e105700000000, 0x2ef846f400000000, 0x7678a42300000000, 0x3df39a6900000000, 0x657378be00000000, 0xccf52e1d00000000, 0x9475ccca00000000, 0x44ae95bc00000000, 0x1c2e776b00000000, 0xb5a821c800000000, 0xed28c31f00000000, 0xa6a3fd5500000000, 0xfe231f8200000000, 0x57a5492100000000, 0x0f25abf600000000, 0xc1b334b500000000, 0x9933d66200000000, 0x30b580c100000000, 0x6835621600000000, 0x23be5c5c00000000, 0x7b3ebe8b00000000, 0xd2b8e82800000000, 0x8a380aff00000000, 0x4e95d7af00000000, 0x1615357800000000, 0xbf9363db00000000, 0xe713810c00000000, 0xac98bf4600000000, 0xf4185d9100000000, 0x5d9e0b3200000000, 0x051ee9e500000000, 0xcb8876a600000000, 0x9308947100000000, 0x3a8ec2d200000000, 0x620e200500000000, 0x29851e4f00000000, 0x7105fc9800000000, 0xd883aa3b00000000, 0x800348ec00000000, 0x783419d700000000, 0x20b4fb0000000000, 0x8932ada300000000, 0xd1b24f7400000000, 0x9a39713e00000000, 0xc2b993e900000000, 0x6b3fc54a00000000, 0x33bf279d00000000, 0xfd29b8de00000000, 0xa5a95a0900000000, 0x0c2f0caa00000000, 0x54afee7d00000000, 0x1f24d03700000000, 0x47a432e000000000, 0xee22644300000000, 0xb6a2869400000000, 0x720f5bc400000000, 0x2a8fb91300000000, 0x8309efb000000000, 0xdb890d6700000000, 0x9002332d00000000, 0xc882d1fa00000000, 0x6104875900000000, 0x3984658e00000000, 0xf712facd00000000, 0xaf92181a00000000, 0x06144eb900000000, 0x5e94ac6e00000000, 0x151f922400000000, 0x4d9f70f300000000, 0xe419265000000000, 0xbc99c48700000000, 0x6c429df100000000, 0x34c27f2600000000, 0x9d44298500000000, 0xc5c4cb5200000000, 0x8e4ff51800000000, 0xd6cf17cf00000000, 0x7f49416c00000000, 0x27c9a3bb00000000, 0xe95f3cf800000000, 0xb1dfde2f00000000, 0x1859888c00000000, 0x40d96a5b00000000, 0x0b52541100000000, 0x53d2b6c600000000, 0xfa54e06500000000, 0xa2d402b200000000, 0x6679dfe200000000, 0x3ef93d3500000000, 0x977f6b9600000000, 0xcfff894100000000, 0x8474b70b00000000, 0xdcf455dc00000000, 0x7572037f00000000, 0x2df2e1a800000000, 0xe3647eeb00000000, 0xbbe49c3c00000000, 0x1262ca9f00000000, 0x4ae2284800000000, 0x0169160200000000, 0x59e9f4d500000000, 0xf06fa27600000000, 0xa8ef40a100000000}, {0x0000000000000000, 0x463b676500000000, 0x8c76ceca00000000, 0xca4da9af00000000, 0x59ebed4e00000000, 0x1fd08a2b00000000, 0xd59d238400000000, 0x93a644e100000000, 0xb2d6db9d00000000, 0xf4edbcf800000000, 0x3ea0155700000000, 0x789b723200000000, 0xeb3d36d300000000, 0xad0651b600000000, 0x674bf81900000000, 0x21709f7c00000000, 0x25abc6e000000000, 0x6390a18500000000, 0xa9dd082a00000000, 0xefe66f4f00000000, 0x7c402bae00000000, 0x3a7b4ccb00000000, 0xf036e56400000000, 0xb60d820100000000, 0x977d1d7d00000000, 0xd1467a1800000000, 0x1b0bd3b700000000, 0x5d30b4d200000000, 0xce96f03300000000, 0x88ad975600000000, 0x42e03ef900000000, 0x04db599c00000000, 0x0b50fc1a00000000, 0x4d6b9b7f00000000, 0x872632d000000000, 0xc11d55b500000000, 0x52bb115400000000, 0x1480763100000000, 0xdecddf9e00000000, 0x98f6b8fb00000000, 0xb986278700000000, 0xffbd40e200000000, 0x35f0e94d00000000, 0x73cb8e2800000000, 0xe06dcac900000000, 0xa656adac00000000, 0x6c1b040300000000, 0x2a20636600000000, 0x2efb3afa00000000, 0x68c05d9f00000000, 0xa28df43000000000, 0xe4b6935500000000, 0x7710d7b400000000, 0x312bb0d100000000, 0xfb66197e00000000, 0xbd5d7e1b00000000, 0x9c2de16700000000, 0xda16860200000000, 0x105b2fad00000000, 0x566048c800000000, 0xc5c60c2900000000, 0x83fd6b4c00000000, 0x49b0c2e300000000, 0x0f8ba58600000000, 0x16a0f83500000000, 0x509b9f5000000000, 0x9ad636ff00000000, 0xdced519a00000000, 0x4f4b157b00000000, 0x0970721e00000000, 0xc33ddbb100000000, 0x8506bcd400000000, 0xa47623a800000000, 0xe24d44cd00000000, 0x2800ed6200000000, 0x6e3b8a0700000000, 0xfd9dcee600000000, 0xbba6a98300000000, 0x71eb002c00000000, 0x37d0674900000000, 0x330b3ed500000000, 0x753059b000000000, 0xbf7df01f00000000, 0xf946977a00000000, 0x6ae0d39b00000000, 0x2cdbb4fe00000000, 0xe6961d5100000000, 0xa0ad7a3400000000, 0x81dde54800000000, 0xc7e6822d00000000, 0x0dab2b8200000000, 0x4b904ce700000000, 0xd836080600000000, 0x9e0d6f6300000000, 0x5440c6cc00000000, 0x127ba1a900000000, 0x1df0042f00000000, 0x5bcb634a00000000, 0x9186cae500000000, 0xd7bdad8000000000, 0x441be96100000000, 0x02208e0400000000, 0xc86d27ab00000000, 0x8e5640ce00000000, 0xaf26dfb200000000, 0xe91db8d700000000, 0x2350117800000000, 0x656b761d00000000, 0xf6cd32fc00000000, 0xb0f6559900000000, 0x7abbfc3600000000, 0x3c809b5300000000, 0x385bc2cf00000000, 0x7e60a5aa00000000, 0xb42d0c0500000000, 0xf2166b6000000000, 0x61b02f8100000000, 0x278b48e400000000, 0xedc6e14b00000000, 0xabfd862e00000000, 0x8a8d195200000000, 0xccb67e3700000000, 0x06fbd79800000000, 0x40c0b0fd00000000, 0xd366f41c00000000, 0x955d937900000000, 0x5f103ad600000000, 0x192b5db300000000, 0x2c40f16b00000000, 0x6a7b960e00000000, 0xa0363fa100000000, 0xe60d58c400000000, 0x75ab1c2500000000, 0x33907b4000000000, 0xf9ddd2ef00000000, 0xbfe6b58a00000000, 0x9e962af600000000, 0xd8ad4d9300000000, 0x12e0e43c00000000, 0x54db835900000000, 0xc77dc7b800000000, 0x8146a0dd00000000, 0x4b0b097200000000, 0x0d306e1700000000, 0x09eb378b00000000, 0x4fd050ee00000000, 0x859df94100000000, 0xc3a69e2400000000, 0x5000dac500000000, 0x163bbda000000000, 0xdc76140f00000000, 0x9a4d736a00000000, 0xbb3dec1600000000, 0xfd068b7300000000, 0x374b22dc00000000, 0x717045b900000000, 0xe2d6015800000000, 0xa4ed663d00000000, 0x6ea0cf9200000000, 0x289ba8f700000000, 0x27100d7100000000, 0x612b6a1400000000, 0xab66c3bb00000000, 0xed5da4de00000000, 0x7efbe03f00000000, 0x38c0875a00000000, 0xf28d2ef500000000, 0xb4b6499000000000, 0x95c6d6ec00000000, 0xd3fdb18900000000, 0x19b0182600000000, 0x5f8b7f4300000000, 0xcc2d3ba200000000, 0x8a165cc700000000, 0x405bf56800000000, 0x0660920d00000000, 0x02bbcb9100000000, 0x4480acf400000000, 0x8ecd055b00000000, 0xc8f6623e00000000, 0x5b5026df00000000, 0x1d6b41ba00000000, 0xd726e81500000000, 0x911d8f7000000000, 0xb06d100c00000000, 0xf656776900000000, 0x3c1bdec600000000, 0x7a20b9a300000000, 0xe986fd4200000000, 0xafbd9a2700000000, 0x65f0338800000000, 0x23cb54ed00000000, 0x3ae0095e00000000, 0x7cdb6e3b00000000, 0xb696c79400000000, 0xf0ada0f100000000, 0x630be41000000000, 0x2530837500000000, 0xef7d2ada00000000, 0xa9464dbf00000000, 0x8836d2c300000000, 0xce0db5a600000000, 0x04401c0900000000, 0x427b7b6c00000000, 0xd1dd3f8d00000000, 0x97e658e800000000, 0x5dabf14700000000, 0x1b90962200000000, 0x1f4bcfbe00000000, 0x5970a8db00000000, 0x933d017400000000, 0xd506661100000000, 0x46a022f000000000, 0x009b459500000000, 0xcad6ec3a00000000, 0x8ced8b5f00000000, 0xad9d142300000000, 0xeba6734600000000, 0x21ebdae900000000, 0x67d0bd8c00000000, 0xf476f96d00000000, 0xb24d9e0800000000, 0x780037a700000000, 0x3e3b50c200000000, 0x31b0f54400000000, 0x778b922100000000, 0xbdc63b8e00000000, 0xfbfd5ceb00000000, 0x685b180a00000000, 0x2e607f6f00000000, 0xe42dd6c000000000, 0xa216b1a500000000, 0x83662ed900000000, 0xc55d49bc00000000, 0x0f10e01300000000, 0x492b877600000000, 0xda8dc39700000000, 0x9cb6a4f200000000, 0x56fb0d5d00000000, 0x10c06a3800000000, 0x141b33a400000000, 0x522054c100000000, 0x986dfd6e00000000, 0xde569a0b00000000, 0x4df0deea00000000, 0x0bcbb98f00000000, 0xc186102000000000, 0x87bd774500000000, 0xa6cde83900000000, 0xe0f68f5c00000000, 0x2abb26f300000000, 0x6c80419600000000, 0xff26057700000000, 0xb91d621200000000, 0x7350cbbd00000000, 0x356bacd800000000}, {0x0000000000000000, 0x9e83da9f00000000, 0x7d01c4e400000000, 0xe3821e7b00000000, 0xbb04f91200000000, 0x2587238d00000000, 0xc6053df600000000, 0x5886e76900000000, 0x7609f22500000000, 0xe88a28ba00000000, 0x0b0836c100000000, 0x958bec5e00000000, 0xcd0d0b3700000000, 0x538ed1a800000000, 0xb00ccfd300000000, 0x2e8f154c00000000, 0xec12e44b00000000, 0x72913ed400000000, 0x911320af00000000, 0x0f90fa3000000000, 0x57161d5900000000, 0xc995c7c600000000, 0x2a17d9bd00000000, 0xb494032200000000, 0x9a1b166e00000000, 0x0498ccf100000000, 0xe71ad28a00000000, 0x7999081500000000, 0x211fef7c00000000, 0xbf9c35e300000000, 0x5c1e2b9800000000, 0xc29df10700000000, 0xd825c89700000000, 0x46a6120800000000, 0xa5240c7300000000, 0x3ba7d6ec00000000, 0x6321318500000000, 0xfda2eb1a00000000, 0x1e20f56100000000, 0x80a32ffe00000000, 0xae2c3ab200000000, 0x30afe02d00000000, 0xd32dfe5600000000, 0x4dae24c900000000, 0x1528c3a000000000, 0x8bab193f00000000, 0x6829074400000000, 0xf6aadddb00000000, 0x34372cdc00000000, 0xaab4f64300000000, 0x4936e83800000000, 0xd7b532a700000000, 0x8f33d5ce00000000, 0x11b00f5100000000, 0xf232112a00000000, 0x6cb1cbb500000000, 0x423edef900000000, 0xdcbd046600000000, 0x3f3f1a1d00000000, 0xa1bcc08200000000, 0xf93a27eb00000000, 0x67b9fd7400000000, 0x843be30f00000000, 0x1ab8399000000000, 0xf14de1f400000000, 0x6fce3b6b00000000, 0x8c4c251000000000, 0x12cfff8f00000000, 0x4a4918e600000000, 0xd4cac27900000000, 0x3748dc0200000000, 0xa9cb069d00000000, 0x874413d100000000, 0x19c7c94e00000000, 0xfa45d73500000000, 0x64c60daa00000000, 0x3c40eac300000000, 0xa2c3305c00000000, 0x41412e2700000000, 0xdfc2f4b800000000, 0x1d5f05bf00000000, 0x83dcdf2000000000, 0x605ec15b00000000, 0xfedd1bc400000000, 0xa65bfcad00000000, 0x38d8263200000000, 0xdb5a384900000000, 0x45d9e2d600000000, 0x6b56f79a00000000, 0xf5d52d0500000000, 0x1657337e00000000, 0x88d4e9e100000000, 0xd0520e8800000000, 0x4ed1d41700000000, 0xad53ca6c00000000, 0x33d010f300000000, 0x2968296300000000, 0xb7ebf3fc00000000, 0x5469ed8700000000, 0xcaea371800000000, 0x926cd07100000000, 0x0cef0aee00000000, 0xef6d149500000000, 0x71eece0a00000000, 0x5f61db4600000000, 0xc1e201d900000000, 0x22601fa200000000, 0xbce3c53d00000000, 0xe465225400000000, 0x7ae6f8cb00000000, 0x9964e6b000000000, 0x07e73c2f00000000, 0xc57acd2800000000, 0x5bf917b700000000, 0xb87b09cc00000000, 0x26f8d35300000000, 0x7e7e343a00000000, 0xe0fdeea500000000, 0x037ff0de00000000, 0x9dfc2a4100000000, 0xb3733f0d00000000, 0x2df0e59200000000, 0xce72fbe900000000, 0x50f1217600000000, 0x0877c61f00000000, 0x96f41c8000000000, 0x757602fb00000000, 0xebf5d86400000000, 0xa39db33200000000, 0x3d1e69ad00000000, 0xde9c77d600000000, 0x401fad4900000000, 0x18994a2000000000, 0x861a90bf00000000, 0x65988ec400000000, 0xfb1b545b00000000, 0xd594411700000000, 0x4b179b8800000000, 0xa89585f300000000, 0x36165f6c00000000, 0x6e90b80500000000, 0xf013629a00000000, 0x13917ce100000000, 0x8d12a67e00000000, 0x4f8f577900000000, 0xd10c8de600000000, 0x328e939d00000000, 0xac0d490200000000, 0xf48bae6b00000000, 0x6a0874f400000000, 0x898a6a8f00000000, 0x1709b01000000000, 0x3986a55c00000000, 0xa7057fc300000000, 0x448761b800000000, 0xda04bb2700000000, 0x82825c4e00000000, 0x1c0186d100000000, 0xff8398aa00000000, 0x6100423500000000, 0x7bb87ba500000000, 0xe53ba13a00000000, 0x06b9bf4100000000, 0x983a65de00000000, 0xc0bc82b700000000, 0x5e3f582800000000, 0xbdbd465300000000, 0x233e9ccc00000000, 0x0db1898000000000, 0x9332531f00000000, 0x70b04d6400000000, 0xee3397fb00000000, 0xb6b5709200000000, 0x2836aa0d00000000, 0xcbb4b47600000000, 0x55376ee900000000, 0x97aa9fee00000000, 0x0929457100000000, 0xeaab5b0a00000000, 0x7428819500000000, 0x2cae66fc00000000, 0xb22dbc6300000000, 0x51afa21800000000, 0xcf2c788700000000, 0xe1a36dcb00000000, 0x7f20b75400000000, 0x9ca2a92f00000000, 0x022173b000000000, 0x5aa794d900000000, 0xc4244e4600000000, 0x27a6503d00000000, 0xb9258aa200000000, 0x52d052c600000000, 0xcc53885900000000, 0x2fd1962200000000, 0xb1524cbd00000000, 0xe9d4abd400000000, 0x7757714b00000000, 0x94d56f3000000000, 0x0a56b5af00000000, 0x24d9a0e300000000, 0xba5a7a7c00000000, 0x59d8640700000000, 0xc75bbe9800000000, 0x9fdd59f100000000, 0x015e836e00000000, 0xe2dc9d1500000000, 0x7c5f478a00000000, 0xbec2b68d00000000, 0x20416c1200000000, 0xc3c3726900000000, 0x5d40a8f600000000, 0x05c64f9f00000000, 0x9b45950000000000, 0x78c78b7b00000000, 0xe64451e400000000, 0xc8cb44a800000000, 0x56489e3700000000, 0xb5ca804c00000000, 0x2b495ad300000000, 0x73cfbdba00000000, 0xed4c672500000000, 0x0ece795e00000000, 0x904da3c100000000, 0x8af59a5100000000, 0x147640ce00000000, 0xf7f45eb500000000, 0x6977842a00000000, 0x31f1634300000000, 0xaf72b9dc00000000, 0x4cf0a7a700000000, 0xd2737d3800000000, 0xfcfc687400000000, 0x627fb2eb00000000, 0x81fdac9000000000, 0x1f7e760f00000000, 0x47f8916600000000, 0xd97b4bf900000000, 0x3af9558200000000, 0xa47a8f1d00000000, 0x66e77e1a00000000, 0xf864a48500000000, 0x1be6bafe00000000, 0x8565606100000000, 0xdde3870800000000, 0x43605d9700000000, 0xa0e243ec00000000, 0x3e61997300000000, 0x10ee8c3f00000000, 0x8e6d56a000000000, 0x6def48db00000000, 0xf36c924400000000, 0xabea752d00000000, 0x3569afb200000000, 0xd6ebb1c900000000, 0x48686b5600000000}, {0x0000000000000000, 0xc064281700000000, 0x80c9502e00000000, 0x40ad783900000000, 0x0093a15c00000000, 0xc0f7894b00000000, 0x805af17200000000, 0x403ed96500000000, 0x002643b900000000, 0xc0426bae00000000, 0x80ef139700000000, 0x408b3b8000000000, 0x00b5e2e500000000, 0xc0d1caf200000000, 0x807cb2cb00000000, 0x40189adc00000000, 0x414af7a900000000, 0x812edfbe00000000, 0xc183a78700000000, 0x01e78f9000000000, 0x41d956f500000000, 0x81bd7ee200000000, 0xc11006db00000000, 0x01742ecc00000000, 0x416cb41000000000, 0x81089c0700000000, 0xc1a5e43e00000000, 0x01c1cc2900000000, 0x41ff154c00000000, 0x819b3d5b00000000, 0xc136456200000000, 0x01526d7500000000, 0xc3929f8800000000, 0x03f6b79f00000000, 0x435bcfa600000000, 0x833fe7b100000000, 0xc3013ed400000000, 0x036516c300000000, 0x43c86efa00000000, 0x83ac46ed00000000, 0xc3b4dc3100000000, 0x03d0f42600000000, 0x437d8c1f00000000, 0x8319a40800000000, 0xc3277d6d00000000, 0x0343557a00000000, 0x43ee2d4300000000, 0x838a055400000000, 0x82d8682100000000, 0x42bc403600000000, 0x0211380f00000000, 0xc275101800000000, 0x824bc97d00000000, 0x422fe16a00000000, 0x0282995300000000, 0xc2e6b14400000000, 0x82fe2b9800000000, 0x429a038f00000000, 0x02377bb600000000, 0xc25353a100000000, 0x826d8ac400000000, 0x4209a2d300000000, 0x02a4daea00000000, 0xc2c0f2fd00000000, 0xc7234eca00000000, 0x074766dd00000000, 0x47ea1ee400000000, 0x878e36f300000000, 0xc7b0ef9600000000, 0x07d4c78100000000, 0x4779bfb800000000, 0x871d97af00000000, 0xc7050d7300000000, 0x0761256400000000, 0x47cc5d5d00000000, 0x87a8754a00000000, 0xc796ac2f00000000, 0x07f2843800000000, 0x475ffc0100000000, 0x873bd41600000000, 0x8669b96300000000, 0x460d917400000000, 0x06a0e94d00000000, 0xc6c4c15a00000000, 0x86fa183f00000000, 0x469e302800000000, 0x0633481100000000, 0xc657600600000000, 0x864ffada00000000, 0x462bd2cd00000000, 0x0686aaf400000000, 0xc6e282e300000000, 0x86dc5b8600000000, 0x46b8739100000000, 0x06150ba800000000, 0xc67123bf00000000, 0x04b1d14200000000, 0xc4d5f95500000000, 0x8478816c00000000, 0x441ca97b00000000, 0x0422701e00000000, 0xc446580900000000, 0x84eb203000000000, 0x448f082700000000, 0x049792fb00000000, 0xc4f3baec00000000, 0x845ec2d500000000, 0x443aeac200000000, 0x040433a700000000, 0xc4601bb000000000, 0x84cd638900000000, 0x44a94b9e00000000, 0x45fb26eb00000000, 0x859f0efc00000000, 0xc53276c500000000, 0x05565ed200000000, 0x456887b700000000, 0x850cafa000000000, 0xc5a1d79900000000, 0x05c5ff8e00000000, 0x45dd655200000000, 0x85b94d4500000000, 0xc514357c00000000, 0x05701d6b00000000, 0x454ec40e00000000, 0x852aec1900000000, 0xc587942000000000, 0x05e3bc3700000000, 0xcf41ed4f00000000, 0x0f25c55800000000, 0x4f88bd6100000000, 0x8fec957600000000, 0xcfd24c1300000000, 0x0fb6640400000000, 0x4f1b1c3d00000000, 0x8f7f342a00000000, 0xcf67aef600000000, 0x0f0386e100000000, 0x4faefed800000000, 0x8fcad6cf00000000, 0xcff40faa00000000, 0x0f9027bd00000000, 0x4f3d5f8400000000, 0x8f59779300000000, 0x8e0b1ae600000000, 0x4e6f32f100000000, 0x0ec24ac800000000, 0xcea662df00000000, 0x8e98bbba00000000, 0x4efc93ad00000000, 0x0e51eb9400000000, 0xce35c38300000000, 0x8e2d595f00000000, 0x4e49714800000000, 0x0ee4097100000000, 0xce80216600000000, 0x8ebef80300000000, 0x4edad01400000000, 0x0e77a82d00000000, 0xce13803a00000000, 0x0cd372c700000000, 0xccb75ad000000000, 0x8c1a22e900000000, 0x4c7e0afe00000000, 0x0c40d39b00000000, 0xcc24fb8c00000000, 0x8c8983b500000000, 0x4cedaba200000000, 0x0cf5317e00000000, 0xcc91196900000000, 0x8c3c615000000000, 0x4c58494700000000, 0x0c66902200000000, 0xcc02b83500000000, 0x8cafc00c00000000, 0x4ccbe81b00000000, 0x4d99856e00000000, 0x8dfdad7900000000, 0xcd50d54000000000, 0x0d34fd5700000000, 0x4d0a243200000000, 0x8d6e0c2500000000, 0xcdc3741c00000000, 0x0da75c0b00000000, 0x4dbfc6d700000000, 0x8ddbeec000000000, 0xcd7696f900000000, 0x0d12beee00000000, 0x4d2c678b00000000, 0x8d484f9c00000000, 0xcde537a500000000, 0x0d811fb200000000, 0x0862a38500000000, 0xc8068b9200000000, 0x88abf3ab00000000, 0x48cfdbbc00000000, 0x08f102d900000000, 0xc8952ace00000000, 0x883852f700000000, 0x485c7ae000000000, 0x0844e03c00000000, 0xc820c82b00000000, 0x888db01200000000, 0x48e9980500000000, 0x08d7416000000000, 0xc8b3697700000000, 0x881e114e00000000, 0x487a395900000000, 0x4928542c00000000, 0x894c7c3b00000000, 0xc9e1040200000000, 0x09852c1500000000, 0x49bbf57000000000, 0x89dfdd6700000000, 0xc972a55e00000000, 0x09168d4900000000, 0x490e179500000000, 0x896a3f8200000000, 0xc9c747bb00000000, 0x09a36fac00000000, 0x499db6c900000000, 0x89f99ede00000000, 0xc954e6e700000000, 0x0930cef000000000, 0xcbf03c0d00000000, 0x0b94141a00000000, 0x4b396c2300000000, 0x8b5d443400000000, 0xcb639d5100000000, 0x0b07b54600000000, 0x4baacd7f00000000, 0x8bcee56800000000, 0xcbd67fb400000000, 0x0bb257a300000000, 0x4b1f2f9a00000000, 0x8b7b078d00000000, 0xcb45dee800000000, 0x0b21f6ff00000000, 0x4b8c8ec600000000, 0x8be8a6d100000000, 0x8abacba400000000, 0x4adee3b300000000, 0x0a739b8a00000000, 0xca17b39d00000000, 0x8a296af800000000, 0x4a4d42ef00000000, 0x0ae03ad600000000, 0xca8412c100000000, 0x8a9c881d00000000, 0x4af8a00a00000000, 0x0a55d83300000000, 0xca31f02400000000, 0x8a0f294100000000, 0x4a6b015600000000, 0x0ac6796f00000000, 0xcaa2517800000000}, {0x0000000000000000, 0xd4ea739b00000000, 0xe9d396ed00000000, 0x3d39e57600000000, 0x93a15c0000000000, 0x474b2f9b00000000, 0x7a72caed00000000, 0xae98b97600000000, 0x2643b90000000000, 0xf2a9ca9b00000000, 0xcf902fed00000000, 0x1b7a5c7600000000, 0xb5e2e50000000000, 0x6108969b00000000, 0x5c3173ed00000000, 0x88db007600000000, 0x4c86720100000000, 0x986c019a00000000, 0xa555e4ec00000000, 0x71bf977700000000, 0xdf272e0100000000, 0x0bcd5d9a00000000, 0x36f4b8ec00000000, 0xe21ecb7700000000, 0x6ac5cb0100000000, 0xbe2fb89a00000000, 0x83165dec00000000, 0x57fc2e7700000000, 0xf964970100000000, 0x2d8ee49a00000000, 0x10b701ec00000000, 0xc45d727700000000, 0x980ce50200000000, 0x4ce6969900000000, 0x71df73ef00000000, 0xa535007400000000, 0x0badb90200000000, 0xdf47ca9900000000, 0xe27e2fef00000000, 0x36945c7400000000, 0xbe4f5c0200000000, 0x6aa52f9900000000, 0x579ccaef00000000, 0x8376b97400000000, 0x2dee000200000000, 0xf904739900000000, 0xc43d96ef00000000, 0x10d7e57400000000, 0xd48a970300000000, 0x0060e49800000000, 0x3d5901ee00000000, 0xe9b3727500000000, 0x472bcb0300000000, 0x93c1b89800000000, 0xaef85dee00000000, 0x7a122e7500000000, 0xf2c92e0300000000, 0x26235d9800000000, 0x1b1ab8ee00000000, 0xcff0cb7500000000, 0x6168720300000000, 0xb582019800000000, 0x88bbe4ee00000000, 0x5c51977500000000, 0x3019ca0500000000, 0xe4f3b99e00000000, 0xd9ca5ce800000000, 0x0d202f7300000000, 0xa3b8960500000000, 0x7752e59e00000000, 0x4a6b00e800000000, 0x9e81737300000000, 0x165a730500000000, 0xc2b0009e00000000, 0xff89e5e800000000, 0x2b63967300000000, 0x85fb2f0500000000, 0x51115c9e00000000, 0x6c28b9e800000000, 0xb8c2ca7300000000, 0x7c9fb80400000000, 0xa875cb9f00000000, 0x954c2ee900000000, 0x41a65d7200000000, 0xef3ee40400000000, 0x3bd4979f00000000, 0x06ed72e900000000, 0xd207017200000000, 0x5adc010400000000, 0x8e36729f00000000, 0xb30f97e900000000, 0x67e5e47200000000, 0xc97d5d0400000000, 0x1d972e9f00000000, 0x20aecbe900000000, 0xf444b87200000000, 0xa8152f0700000000, 0x7cff5c9c00000000, 0x41c6b9ea00000000, 0x952cca7100000000, 0x3bb4730700000000, 0xef5e009c00000000, 0xd267e5ea00000000, 0x068d967100000000, 0x8e56960700000000, 0x5abce59c00000000, 0x678500ea00000000, 0xb36f737100000000, 0x1df7ca0700000000, 0xc91db99c00000000, 0xf4245cea00000000, 0x20ce2f7100000000, 0xe4935d0600000000, 0x30792e9d00000000, 0x0d40cbeb00000000, 0xd9aab87000000000, 0x7732010600000000, 0xa3d8729d00000000, 0x9ee197eb00000000, 0x4a0be47000000000, 0xc2d0e40600000000, 0x163a979d00000000, 0x2b0372eb00000000, 0xffe9017000000000, 0x5171b80600000000, 0x859bcb9d00000000, 0xb8a22eeb00000000, 0x6c485d7000000000, 0x6032940b00000000, 0xb4d8e79000000000, 0x89e102e600000000, 0x5d0b717d00000000, 0xf393c80b00000000, 0x2779bb9000000000, 0x1a405ee600000000, 0xceaa2d7d00000000, 0x46712d0b00000000, 0x929b5e9000000000, 0xafa2bbe600000000, 0x7b48c87d00000000, 0xd5d0710b00000000, 0x013a029000000000, 0x3c03e7e600000000, 0xe8e9947d00000000, 0x2cb4e60a00000000, 0xf85e959100000000, 0xc56770e700000000, 0x118d037c00000000, 0xbf15ba0a00000000, 0x6bffc99100000000, 0x56c62ce700000000, 0x822c5f7c00000000, 0x0af75f0a00000000, 0xde1d2c9100000000, 0xe324c9e700000000, 0x37ceba7c00000000, 0x9956030a00000000, 0x4dbc709100000000, 0x708595e700000000, 0xa46fe67c00000000, 0xf83e710900000000, 0x2cd4029200000000, 0x11ede7e400000000, 0xc507947f00000000, 0x6b9f2d0900000000, 0xbf755e9200000000, 0x824cbbe400000000, 0x56a6c87f00000000, 0xde7dc80900000000, 0x0a97bb9200000000, 0x37ae5ee400000000, 0xe3442d7f00000000, 0x4ddc940900000000, 0x9936e79200000000, 0xa40f02e400000000, 0x70e5717f00000000, 0xb4b8030800000000, 0x6052709300000000, 0x5d6b95e500000000, 0x8981e67e00000000, 0x27195f0800000000, 0xf3f32c9300000000, 0xcecac9e500000000, 0x1a20ba7e00000000, 0x92fbba0800000000, 0x4611c99300000000, 0x7b282ce500000000, 0xafc25f7e00000000, 0x015ae60800000000, 0xd5b0959300000000, 0xe88970e500000000, 0x3c63037e00000000, 0x502b5e0e00000000, 0x84c12d9500000000, 0xb9f8c8e300000000, 0x6d12bb7800000000, 0xc38a020e00000000, 0x1760719500000000, 0x2a5994e300000000, 0xfeb3e77800000000, 0x7668e70e00000000, 0xa282949500000000, 0x9fbb71e300000000, 0x4b51027800000000, 0xe5c9bb0e00000000, 0x3123c89500000000, 0x0c1a2de300000000, 0xd8f05e7800000000, 0x1cad2c0f00000000, 0xc8475f9400000000, 0xf57ebae200000000, 0x2194c97900000000, 0x8f0c700f00000000, 0x5be6039400000000, 0x66dfe6e200000000, 0xb235957900000000, 0x3aee950f00000000, 0xee04e69400000000, 0xd33d03e200000000, 0x07d7707900000000, 0xa94fc90f00000000, 0x7da5ba9400000000, 0x409c5fe200000000, 0x94762c7900000000, 0xc827bb0c00000000, 0x1ccdc89700000000, 0x21f42de100000000, 0xf51e5e7a00000000, 0x5b86e70c00000000, 0x8f6c949700000000, 0xb25571e100000000, 0x66bf027a00000000, 0xee64020c00000000, 0x3a8e719700000000, 0x07b794e100000000, 0xd35de77a00000000, 0x7dc55e0c00000000, 0xa92f2d9700000000, 0x9416c8e100000000, 0x40fcbb7a00000000, 0x84a1c90d00000000, 0x504bba9600000000, 0x6d725fe000000000, 0xb9982c7b00000000, 0x1700950d00000000, 0xc3eae69600000000, 0xfed303e000000000, 0x2a39707b00000000, 0xa2e2700d00000000, 0x7608039600000000, 0x4b31e6e000000000, 0x9fdb957b00000000, 0x31432c0d00000000, 0xe5a95f9600000000, 0xd890bae000000000, 0x0c7ac97b00000000}, {0x0000000000000000, 0x2765258100000000, 0x0fcc3bd900000000, 0x28a91e5800000000, 0x5f9e066900000000, 0x78fb23e800000000, 0x50523db000000000, 0x7737183100000000, 0xbe3c0dd200000000, 0x9959285300000000, 0xb1f0360b00000000, 0x9695138a00000000, 0xe1a20bbb00000000, 0xc6c72e3a00000000, 0xee6e306200000000, 0xc90b15e300000000, 0x3d7f6b7f00000000, 0x1a1a4efe00000000, 0x32b350a600000000, 0x15d6752700000000, 0x62e16d1600000000, 0x4584489700000000, 0x6d2d56cf00000000, 0x4a48734e00000000, 0x834366ad00000000, 0xa426432c00000000, 0x8c8f5d7400000000, 0xabea78f500000000, 0xdcdd60c400000000, 0xfbb8454500000000, 0xd3115b1d00000000, 0xf4747e9c00000000, 0x7afed6fe00000000, 0x5d9bf37f00000000, 0x7532ed2700000000, 0x5257c8a600000000, 0x2560d09700000000, 0x0205f51600000000, 0x2aaceb4e00000000, 0x0dc9cecf00000000, 0xc4c2db2c00000000, 0xe3a7fead00000000, 0xcb0ee0f500000000, 0xec6bc57400000000, 0x9b5cdd4500000000, 0xbc39f8c400000000, 0x9490e69c00000000, 0xb3f5c31d00000000, 0x4781bd8100000000, 0x60e4980000000000, 0x484d865800000000, 0x6f28a3d900000000, 0x181fbbe800000000, 0x3f7a9e6900000000, 0x17d3803100000000, 0x30b6a5b000000000, 0xf9bdb05300000000, 0xded895d200000000, 0xf6718b8a00000000, 0xd114ae0b00000000, 0xa623b63a00000000, 0x814693bb00000000, 0xa9ef8de300000000, 0x8e8aa86200000000, 0xb5fadc2600000000, 0x929ff9a700000000, 0xba36e7ff00000000, 0x9d53c27e00000000, 0xea64da4f00000000, 0xcd01ffce00000000, 0xe5a8e19600000000, 0xc2cdc41700000000, 0x0bc6d1f400000000, 0x2ca3f47500000000, 0x040aea2d00000000, 0x236fcfac00000000, 0x5458d79d00000000, 0x733df21c00000000, 0x5b94ec4400000000, 0x7cf1c9c500000000, 0x8885b75900000000, 0xafe092d800000000, 0x87498c8000000000, 0xa02ca90100000000, 0xd71bb13000000000, 0xf07e94b100000000, 0xd8d78ae900000000, 0xffb2af6800000000, 0x36b9ba8b00000000, 0x11dc9f0a00000000, 0x3975815200000000, 0x1e10a4d300000000, 0x6927bce200000000, 0x4e42996300000000, 0x66eb873b00000000, 0x418ea2ba00000000, 0xcf040ad800000000, 0xe8612f5900000000, 0xc0c8310100000000, 0xe7ad148000000000, 0x909a0cb100000000, 0xb7ff293000000000, 0x9f56376800000000, 0xb83312e900000000, 0x7138070a00000000, 0x565d228b00000000, 0x7ef43cd300000000, 0x5991195200000000, 0x2ea6016300000000, 0x09c324e200000000, 0x216a3aba00000000, 0x060f1f3b00000000, 0xf27b61a700000000, 0xd51e442600000000, 0xfdb75a7e00000000, 0xdad27fff00000000, 0xade567ce00000000, 0x8a80424f00000000, 0xa2295c1700000000, 0x854c799600000000, 0x4c476c7500000000, 0x6b2249f400000000, 0x438b57ac00000000, 0x64ee722d00000000, 0x13d96a1c00000000, 0x34bc4f9d00000000, 0x1c1551c500000000, 0x3b70744400000000, 0x6af5b94d00000000, 0x4d909ccc00000000, 0x6539829400000000, 0x425ca71500000000, 0x356bbf2400000000, 0x120e9aa500000000, 0x3aa784fd00000000, 0x1dc2a17c00000000, 0xd4c9b49f00000000, 0xf3ac911e00000000, 0xdb058f4600000000, 0xfc60aac700000000, 0x8b57b2f600000000, 0xac32977700000000, 0x849b892f00000000, 0xa3feacae00000000, 0x578ad23200000000, 0x70eff7b300000000, 0x5846e9eb00000000, 0x7f23cc6a00000000, 0x0814d45b00000000, 0x2f71f1da00000000, 0x07d8ef8200000000, 0x20bdca0300000000, 0xe9b6dfe000000000, 0xced3fa6100000000, 0xe67ae43900000000, 0xc11fc1b800000000, 0xb628d98900000000, 0x914dfc0800000000, 0xb9e4e25000000000, 0x9e81c7d100000000, 0x100b6fb300000000, 0x376e4a3200000000, 0x1fc7546a00000000, 0x38a271eb00000000, 0x4f9569da00000000, 0x68f04c5b00000000, 0x4059520300000000, 0x673c778200000000, 0xae37626100000000, 0x895247e000000000, 0xa1fb59b800000000, 0x869e7c3900000000, 0xf1a9640800000000, 0xd6cc418900000000, 0xfe655fd100000000, 0xd9007a5000000000, 0x2d7404cc00000000, 0x0a11214d00000000, 0x22b83f1500000000, 0x05dd1a9400000000, 0x72ea02a500000000, 0x558f272400000000, 0x7d26397c00000000, 0x5a431cfd00000000, 0x9348091e00000000, 0xb42d2c9f00000000, 0x9c8432c700000000, 0xbbe1174600000000, 0xccd60f7700000000, 0xebb32af600000000, 0xc31a34ae00000000, 0xe47f112f00000000, 0xdf0f656b00000000, 0xf86a40ea00000000, 0xd0c35eb200000000, 0xf7a67b3300000000, 0x8091630200000000, 0xa7f4468300000000, 0x8f5d58db00000000, 0xa8387d5a00000000, 0x613368b900000000, 0x46564d3800000000, 0x6eff536000000000, 0x499a76e100000000, 0x3ead6ed000000000, 0x19c84b5100000000, 0x3161550900000000, 0x1604708800000000, 0xe2700e1400000000, 0xc5152b9500000000, 0xedbc35cd00000000, 0xcad9104c00000000, 0xbdee087d00000000, 0x9a8b2dfc00000000, 0xb22233a400000000, 0x9547162500000000, 0x5c4c03c600000000, 0x7b29264700000000, 0x5380381f00000000, 0x74e51d9e00000000, 0x03d205af00000000, 0x24b7202e00000000, 0x0c1e3e7600000000, 0x2b7b1bf700000000, 0xa5f1b39500000000, 0x8294961400000000, 0xaa3d884c00000000, 0x8d58adcd00000000, 0xfa6fb5fc00000000, 0xdd0a907d00000000, 0xf5a38e2500000000, 0xd2c6aba400000000, 0x1bcdbe4700000000, 0x3ca89bc600000000, 0x1401859e00000000, 0x3364a01f00000000, 0x4453b82e00000000, 0x63369daf00000000, 0x4b9f83f700000000, 0x6cfaa67600000000, 0x988ed8ea00000000, 0xbfebfd6b00000000, 0x9742e33300000000, 0xb027c6b200000000, 0xc710de8300000000, 0xe075fb0200000000, 0xc8dce55a00000000, 0xefb9c0db00000000, 0x26b2d53800000000, 0x01d7f0b900000000, 0x297eeee100000000, 0x0e1bcb6000000000, 0x792cd35100000000, 0x5e49f6d000000000, 0x76e0e88800000000, 0x5185cd0900000000}}; #else /* W == 4 */ local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0x9ba54c6f, 0xec3b9e9f, 0x779ed2f0, 0x03063b7f, 0x98a37710, 0xef3da5e0, 0x7498e98f, 0x060c76fe, 0x9da93a91, 0xea37e861, 0x7192a40e, 0x050a4d81, 0x9eaf01ee, 0xe931d31e, 0x72949f71, 0x0c18edfc, 0x97bda193, 0xe0237363, 0x7b863f0c, 0x0f1ed683, 0x94bb9aec, 0xe325481c, 0x78800473, 0x0a149b02, 0x91b1d76d, 0xe62f059d, 0x7d8a49f2, 0x0912a07d, 0x92b7ec12, 0xe5293ee2, 0x7e8c728d, 0x1831dbf8, 0x83949797, 0xf40a4567, 0x6faf0908, 0x1b37e087, 0x8092ace8, 0xf70c7e18, 0x6ca93277, 0x1e3dad06, 0x8598e169, 0xf2063399, 0x69a37ff6, 0x1d3b9679, 0x869eda16, 0xf10008e6, 0x6aa54489, 0x14293604, 0x8f8c7a6b, 0xf812a89b, 0x63b7e4f4, 0x172f0d7b, 0x8c8a4114, 0xfb1493e4, 0x60b1df8b, 0x122540fa, 0x89800c95, 0xfe1ede65, 0x65bb920a, 0x11237b85, 0x8a8637ea, 0xfd18e51a, 0x66bda975, 0x3063b7f0, 0xabc6fb9f, 0xdc58296f, 0x47fd6500, 0x33658c8f, 0xa8c0c0e0, 0xdf5e1210, 0x44fb5e7f, 0x366fc10e, 0xadca8d61, 0xda545f91, 0x41f113fe, 0x3569fa71, 0xaeccb61e, 0xd95264ee, 0x42f72881, 0x3c7b5a0c, 0xa7de1663, 0xd040c493, 0x4be588fc, 0x3f7d6173, 0xa4d82d1c, 0xd346ffec, 0x48e3b383, 0x3a772cf2, 0xa1d2609d, 0xd64cb26d, 0x4de9fe02, 0x3971178d, 0xa2d45be2, 0xd54a8912, 0x4eefc57d, 0x28526c08, 0xb3f72067, 0xc469f297, 0x5fccbef8, 0x2b545777, 0xb0f11b18, 0xc76fc9e8, 0x5cca8587, 0x2e5e1af6, 0xb5fb5699, 0xc2658469, 0x59c0c806, 0x2d582189, 0xb6fd6de6, 0xc163bf16, 0x5ac6f379, 0x244a81f4, 0xbfefcd9b, 0xc8711f6b, 0x53d45304, 0x274cba8b, 0xbce9f6e4, 0xcb772414, 0x50d2687b, 0x2246f70a, 0xb9e3bb65, 0xce7d6995, 0x55d825fa, 0x2140cc75, 0xbae5801a, 0xcd7b52ea, 0x56de1e85, 0x60c76fe0, 0xfb62238f, 0x8cfcf17f, 0x1759bd10, 0x63c1549f, 0xf86418f0, 0x8ffaca00, 0x145f866f, 0x66cb191e, 0xfd6e5571, 0x8af08781, 0x1155cbee, 0x65cd2261, 0xfe686e0e, 0x89f6bcfe, 0x1253f091, 0x6cdf821c, 0xf77ace73, 0x80e41c83, 0x1b4150ec, 0x6fd9b963, 0xf47cf50c, 0x83e227fc, 0x18476b93, 0x6ad3f4e2, 0xf176b88d, 0x86e86a7d, 0x1d4d2612, 0x69d5cf9d, 0xf27083f2, 0x85ee5102, 0x1e4b1d6d, 0x78f6b418, 0xe353f877, 0x94cd2a87, 0x0f6866e8, 0x7bf08f67, 0xe055c308, 0x97cb11f8, 0x0c6e5d97, 0x7efac2e6, 0xe55f8e89, 0x92c15c79, 0x09641016, 0x7dfcf999, 0xe659b5f6, 0x91c76706, 0x0a622b69, 0x74ee59e4, 0xef4b158b, 0x98d5c77b, 0x03708b14, 0x77e8629b, 0xec4d2ef4, 0x9bd3fc04, 0x0076b06b, 0x72e22f1a, 0xe9476375, 0x9ed9b185, 0x057cfdea, 0x71e41465, 0xea41580a, 0x9ddf8afa, 0x067ac695, 0x50a4d810, 0xcb01947f, 0xbc9f468f, 0x273a0ae0, 0x53a2e36f, 0xc807af00, 0xbf997df0, 0x243c319f, 0x56a8aeee, 0xcd0de281, 0xba933071, 0x21367c1e, 0x55ae9591, 0xce0bd9fe, 0xb9950b0e, 0x22304761, 0x5cbc35ec, 0xc7197983, 0xb087ab73, 0x2b22e71c, 0x5fba0e93, 0xc41f42fc, 0xb381900c, 0x2824dc63, 0x5ab04312, 0xc1150f7d, 0xb68bdd8d, 0x2d2e91e2, 0x59b6786d, 0xc2133402, 0xb58de6f2, 0x2e28aa9d, 0x489503e8, 0xd3304f87, 0xa4ae9d77, 0x3f0bd118, 0x4b933897, 0xd03674f8, 0xa7a8a608, 0x3c0dea67, 0x4e997516, 0xd53c3979, 0xa2a2eb89, 0x3907a7e6, 0x4d9f4e69, 0xd63a0206, 0xa1a4d0f6, 0x3a019c99, 0x448dee14, 0xdf28a27b, 0xa8b6708b, 0x33133ce4, 0x478bd56b, 0xdc2e9904, 0xabb04bf4, 0x3015079b, 0x428198ea, 0xd924d485, 0xaeba0675, 0x351f4a1a, 0x4187a395, 0xda22effa, 0xadbc3d0a, 0x36197165}, {0x00000000, 0xc18edfc0, 0x586cb9c1, 0x99e26601, 0xb0d97382, 0x7157ac42, 0xe8b5ca43, 0x293b1583, 0xbac3e145, 0x7b4d3e85, 0xe2af5884, 0x23218744, 0x0a1a92c7, 0xcb944d07, 0x52762b06, 0x93f8f4c6, 0xaef6c4cb, 0x6f781b0b, 0xf69a7d0a, 0x3714a2ca, 0x1e2fb749, 0xdfa16889, 0x46430e88, 0x87cdd148, 0x1435258e, 0xd5bbfa4e, 0x4c599c4f, 0x8dd7438f, 0xa4ec560c, 0x656289cc, 0xfc80efcd, 0x3d0e300d, 0x869c8fd7, 0x47125017, 0xdef03616, 0x1f7ee9d6, 0x3645fc55, 0xf7cb2395, 0x6e294594, 0xafa79a54, 0x3c5f6e92, 0xfdd1b152, 0x6433d753, 0xa5bd0893, 0x8c861d10, 0x4d08c2d0, 0xd4eaa4d1, 0x15647b11, 0x286a4b1c, 0xe9e494dc, 0x7006f2dd, 0xb1882d1d, 0x98b3389e, 0x593de75e, 0xc0df815f, 0x01515e9f, 0x92a9aa59, 0x53277599, 0xcac51398, 0x0b4bcc58, 0x2270d9db, 0xe3fe061b, 0x7a1c601a, 0xbb92bfda, 0xd64819ef, 0x17c6c62f, 0x8e24a02e, 0x4faa7fee, 0x66916a6d, 0xa71fb5ad, 0x3efdd3ac, 0xff730c6c, 0x6c8bf8aa, 0xad05276a, 0x34e7416b, 0xf5699eab, 0xdc528b28, 0x1ddc54e8, 0x843e32e9, 0x45b0ed29, 0x78bedd24, 0xb93002e4, 0x20d264e5, 0xe15cbb25, 0xc867aea6, 0x09e97166, 0x900b1767, 0x5185c8a7, 0xc27d3c61, 0x03f3e3a1, 0x9a1185a0, 0x5b9f5a60, 0x72a44fe3, 0xb32a9023, 0x2ac8f622, 0xeb4629e2, 0x50d49638, 0x915a49f8, 0x08b82ff9, 0xc936f039, 0xe00de5ba, 0x21833a7a, 0xb8615c7b, 0x79ef83bb, 0xea17777d, 0x2b99a8bd, 0xb27bcebc, 0x73f5117c, 0x5ace04ff, 0x9b40db3f, 0x02a2bd3e, 0xc32c62fe, 0xfe2252f3, 0x3fac8d33, 0xa64eeb32, 0x67c034f2, 0x4efb2171, 0x8f75feb1, 0x169798b0, 0xd7194770, 0x44e1b3b6, 0x856f6c76, 0x1c8d0a77, 0xdd03d5b7, 0xf438c034, 0x35b61ff4, 0xac5479f5, 0x6ddaa635, 0x77e1359f, 0xb66fea5f, 0x2f8d8c5e, 0xee03539e, 0xc738461d, 0x06b699dd, 0x9f54ffdc, 0x5eda201c, 0xcd22d4da, 0x0cac0b1a, 0x954e6d1b, 0x54c0b2db, 0x7dfba758, 0xbc757898, 0x25971e99, 0xe419c159, 0xd917f154, 0x18992e94, 0x817b4895, 0x40f59755, 0x69ce82d6, 0xa8405d16, 0x31a23b17, 0xf02ce4d7, 0x63d41011, 0xa25acfd1, 0x3bb8a9d0, 0xfa367610, 0xd30d6393, 0x1283bc53, 0x8b61da52, 0x4aef0592, 0xf17dba48, 0x30f36588, 0xa9110389, 0x689fdc49, 0x41a4c9ca, 0x802a160a, 0x19c8700b, 0xd846afcb, 0x4bbe5b0d, 0x8a3084cd, 0x13d2e2cc, 0xd25c3d0c, 0xfb67288f, 0x3ae9f74f, 0xa30b914e, 0x62854e8e, 0x5f8b7e83, 0x9e05a143, 0x07e7c742, 0xc6691882, 0xef520d01, 0x2edcd2c1, 0xb73eb4c0, 0x76b06b00, 0xe5489fc6, 0x24c64006, 0xbd242607, 0x7caaf9c7, 0x5591ec44, 0x941f3384, 0x0dfd5585, 0xcc738a45, 0xa1a92c70, 0x6027f3b0, 0xf9c595b1, 0x384b4a71, 0x11705ff2, 0xd0fe8032, 0x491ce633, 0x889239f3, 0x1b6acd35, 0xdae412f5, 0x430674f4, 0x8288ab34, 0xabb3beb7, 0x6a3d6177, 0xf3df0776, 0x3251d8b6, 0x0f5fe8bb, 0xced1377b, 0x5733517a, 0x96bd8eba, 0xbf869b39, 0x7e0844f9, 0xe7ea22f8, 0x2664fd38, 0xb59c09fe, 0x7412d63e, 0xedf0b03f, 0x2c7e6fff, 0x05457a7c, 0xc4cba5bc, 0x5d29c3bd, 0x9ca71c7d, 0x2735a3a7, 0xe6bb7c67, 0x7f591a66, 0xbed7c5a6, 0x97ecd025, 0x56620fe5, 0xcf8069e4, 0x0e0eb624, 0x9df642e2, 0x5c789d22, 0xc59afb23, 0x041424e3, 0x2d2f3160, 0xeca1eea0, 0x754388a1, 0xb4cd5761, 0x89c3676c, 0x484db8ac, 0xd1afdead, 0x1021016d, 0x391a14ee, 0xf894cb2e, 0x6176ad2f, 0xa0f872ef, 0x33008629, 0xf28e59e9, 0x6b6c3fe8, 0xaae2e028, 0x83d9f5ab, 0x42572a6b, 0xdbb54c6a, 0x1a3b93aa}, {0x00000000, 0xefc26b3e, 0x04f5d03d, 0xeb37bb03, 0x09eba07a, 0xe629cb44, 0x0d1e7047, 0xe2dc1b79, 0x13d740f4, 0xfc152bca, 0x172290c9, 0xf8e0fbf7, 0x1a3ce08e, 0xf5fe8bb0, 0x1ec930b3, 0xf10b5b8d, 0x27ae81e8, 0xc86cead6, 0x235b51d5, 0xcc993aeb, 0x2e452192, 0xc1874aac, 0x2ab0f1af, 0xc5729a91, 0x3479c11c, 0xdbbbaa22, 0x308c1121, 0xdf4e7a1f, 0x3d926166, 0xd2500a58, 0x3967b15b, 0xd6a5da65, 0x4f5d03d0, 0xa09f68ee, 0x4ba8d3ed, 0xa46ab8d3, 0x46b6a3aa, 0xa974c894, 0x42437397, 0xad8118a9, 0x5c8a4324, 0xb348281a, 0x587f9319, 0xb7bdf827, 0x5561e35e, 0xbaa38860, 0x51943363, 0xbe56585d, 0x68f38238, 0x8731e906, 0x6c065205, 0x83c4393b, 0x61182242, 0x8eda497c, 0x65edf27f, 0x8a2f9941, 0x7b24c2cc, 0x94e6a9f2, 0x7fd112f1, 0x901379cf, 0x72cf62b6, 0x9d0d0988, 0x763ab28b, 0x99f8d9b5, 0x9eba07a0, 0x71786c9e, 0x9a4fd79d, 0x758dbca3, 0x9751a7da, 0x7893cce4, 0x93a477e7, 0x7c661cd9, 0x8d6d4754, 0x62af2c6a, 0x89989769, 0x665afc57, 0x8486e72e, 0x6b448c10, 0x80733713, 0x6fb15c2d, 0xb9148648, 0x56d6ed76, 0xbde15675, 0x52233d4b, 0xb0ff2632, 0x5f3d4d0c, 0xb40af60f, 0x5bc89d31, 0xaac3c6bc, 0x4501ad82, 0xae361681, 0x41f47dbf, 0xa32866c6, 0x4cea0df8, 0xa7ddb6fb, 0x481fddc5, 0xd1e70470, 0x3e256f4e, 0xd512d44d, 0x3ad0bf73, 0xd80ca40a, 0x37cecf34, 0xdcf97437, 0x333b1f09, 0xc2304484, 0x2df22fba, 0xc6c594b9, 0x2907ff87, 0xcbdbe4fe, 0x24198fc0, 0xcf2e34c3, 0x20ec5ffd, 0xf6498598, 0x198beea6, 0xf2bc55a5, 0x1d7e3e9b, 0xffa225e2, 0x10604edc, 0xfb57f5df, 0x14959ee1, 0xe59ec56c, 0x0a5cae52, 0xe16b1551, 0x0ea97e6f, 0xec756516, 0x03b70e28, 0xe880b52b, 0x0742de15, 0xe6050901, 0x09c7623f, 0xe2f0d93c, 0x0d32b202, 0xefeea97b, 0x002cc245, 0xeb1b7946, 0x04d91278, 0xf5d249f5, 0x1a1022cb, 0xf12799c8, 0x1ee5f2f6, 0xfc39e98f, 0x13fb82b1, 0xf8cc39b2, 0x170e528c, 0xc1ab88e9, 0x2e69e3d7, 0xc55e58d4, 0x2a9c33ea, 0xc8402893, 0x278243ad, 0xccb5f8ae, 0x23779390, 0xd27cc81d, 0x3dbea323, 0xd6891820, 0x394b731e, 0xdb976867, 0x34550359, 0xdf62b85a, 0x30a0d364, 0xa9580ad1, 0x469a61ef, 0xadaddaec, 0x426fb1d2, 0xa0b3aaab, 0x4f71c195, 0xa4467a96, 0x4b8411a8, 0xba8f4a25, 0x554d211b, 0xbe7a9a18, 0x51b8f126, 0xb364ea5f, 0x5ca68161, 0xb7913a62, 0x5853515c, 0x8ef68b39, 0x6134e007, 0x8a035b04, 0x65c1303a, 0x871d2b43, 0x68df407d, 0x83e8fb7e, 0x6c2a9040, 0x9d21cbcd, 0x72e3a0f3, 0x99d41bf0, 0x761670ce, 0x94ca6bb7, 0x7b080089, 0x903fbb8a, 0x7ffdd0b4, 0x78bf0ea1, 0x977d659f, 0x7c4ade9c, 0x9388b5a2, 0x7154aedb, 0x9e96c5e5, 0x75a17ee6, 0x9a6315d8, 0x6b684e55, 0x84aa256b, 0x6f9d9e68, 0x805ff556, 0x6283ee2f, 0x8d418511, 0x66763e12, 0x89b4552c, 0x5f118f49, 0xb0d3e477, 0x5be45f74, 0xb426344a, 0x56fa2f33, 0xb938440d, 0x520fff0e, 0xbdcd9430, 0x4cc6cfbd, 0xa304a483, 0x48331f80, 0xa7f174be, 0x452d6fc7, 0xaaef04f9, 0x41d8bffa, 0xae1ad4c4, 0x37e20d71, 0xd820664f, 0x3317dd4c, 0xdcd5b672, 0x3e09ad0b, 0xd1cbc635, 0x3afc7d36, 0xd53e1608, 0x24354d85, 0xcbf726bb, 0x20c09db8, 0xcf02f686, 0x2ddeedff, 0xc21c86c1, 0x292b3dc2, 0xc6e956fc, 0x104c8c99, 0xff8ee7a7, 0x14b95ca4, 0xfb7b379a, 0x19a72ce3, 0xf66547dd, 0x1d52fcde, 0xf29097e0, 0x039bcc6d, 0xec59a753, 0x076e1c50, 0xe8ac776e, 0x0a706c17, 0xe5b20729, 0x0e85bc2a, 0xe147d714}, {0x00000000, 0x177b1443, 0x2ef62886, 0x398d3cc5, 0x5dec510c, 0x4a97454f, 0x731a798a, 0x64616dc9, 0xbbd8a218, 0xaca3b65b, 0x952e8a9e, 0x82559edd, 0xe634f314, 0xf14fe757, 0xc8c2db92, 0xdfb9cfd1, 0xacc04271, 0xbbbb5632, 0x82366af7, 0x954d7eb4, 0xf12c137d, 0xe657073e, 0xdfda3bfb, 0xc8a12fb8, 0x1718e069, 0x0063f42a, 0x39eec8ef, 0x2e95dcac, 0x4af4b165, 0x5d8fa526, 0x640299e3, 0x73798da0, 0x82f182a3, 0x958a96e0, 0xac07aa25, 0xbb7cbe66, 0xdf1dd3af, 0xc866c7ec, 0xf1ebfb29, 0xe690ef6a, 0x392920bb, 0x2e5234f8, 0x17df083d, 0x00a41c7e, 0x64c571b7, 0x73be65f4, 0x4a335931, 0x5d484d72, 0x2e31c0d2, 0x394ad491, 0x00c7e854, 0x17bcfc17, 0x73dd91de, 0x64a6859d, 0x5d2bb958, 0x4a50ad1b, 0x95e962ca, 0x82927689, 0xbb1f4a4c, 0xac645e0f, 0xc80533c6, 0xdf7e2785, 0xe6f31b40, 0xf1880f03, 0xde920307, 0xc9e91744, 0xf0642b81, 0xe71f3fc2, 0x837e520b, 0x94054648, 0xad887a8d, 0xbaf36ece, 0x654aa11f, 0x7231b55c, 0x4bbc8999, 0x5cc79dda, 0x38a6f013, 0x2fdde450, 0x1650d895, 0x012bccd6, 0x72524176, 0x65295535, 0x5ca469f0, 0x4bdf7db3, 0x2fbe107a, 0x38c50439, 0x014838fc, 0x16332cbf, 0xc98ae36e, 0xdef1f72d, 0xe77ccbe8, 0xf007dfab, 0x9466b262, 0x831da621, 0xba909ae4, 0xadeb8ea7, 0x5c6381a4, 0x4b1895e7, 0x7295a922, 0x65eebd61, 0x018fd0a8, 0x16f4c4eb, 0x2f79f82e, 0x3802ec6d, 0xe7bb23bc, 0xf0c037ff, 0xc94d0b3a, 0xde361f79, 0xba5772b0, 0xad2c66f3, 0x94a15a36, 0x83da4e75, 0xf0a3c3d5, 0xe7d8d796, 0xde55eb53, 0xc92eff10, 0xad4f92d9, 0xba34869a, 0x83b9ba5f, 0x94c2ae1c, 0x4b7b61cd, 0x5c00758e, 0x658d494b, 0x72f65d08, 0x169730c1, 0x01ec2482, 0x38611847, 0x2f1a0c04, 0x6655004f, 0x712e140c, 0x48a328c9, 0x5fd83c8a, 0x3bb95143, 0x2cc24500, 0x154f79c5, 0x02346d86, 0xdd8da257, 0xcaf6b614, 0xf37b8ad1, 0xe4009e92, 0x8061f35b, 0x971ae718, 0xae97dbdd, 0xb9eccf9e, 0xca95423e, 0xddee567d, 0xe4636ab8, 0xf3187efb, 0x97791332, 0x80020771, 0xb98f3bb4, 0xaef42ff7, 0x714de026, 0x6636f465, 0x5fbbc8a0, 0x48c0dce3, 0x2ca1b12a, 0x3bdaa569, 0x025799ac, 0x152c8def, 0xe4a482ec, 0xf3df96af, 0xca52aa6a, 0xdd29be29, 0xb948d3e0, 0xae33c7a3, 0x97befb66, 0x80c5ef25, 0x5f7c20f4, 0x480734b7, 0x718a0872, 0x66f11c31, 0x029071f8, 0x15eb65bb, 0x2c66597e, 0x3b1d4d3d, 0x4864c09d, 0x5f1fd4de, 0x6692e81b, 0x71e9fc58, 0x15889191, 0x02f385d2, 0x3b7eb917, 0x2c05ad54, 0xf3bc6285, 0xe4c776c6, 0xdd4a4a03, 0xca315e40, 0xae503389, 0xb92b27ca, 0x80a61b0f, 0x97dd0f4c, 0xb8c70348, 0xafbc170b, 0x96312bce, 0x814a3f8d, 0xe52b5244, 0xf2504607, 0xcbdd7ac2, 0xdca66e81, 0x031fa150, 0x1464b513, 0x2de989d6, 0x3a929d95, 0x5ef3f05c, 0x4988e41f, 0x7005d8da, 0x677ecc99, 0x14074139, 0x037c557a, 0x3af169bf, 0x2d8a7dfc, 0x49eb1035, 0x5e900476, 0x671d38b3, 0x70662cf0, 0xafdfe321, 0xb8a4f762, 0x8129cba7, 0x9652dfe4, 0xf233b22d, 0xe548a66e, 0xdcc59aab, 0xcbbe8ee8, 0x3a3681eb, 0x2d4d95a8, 0x14c0a96d, 0x03bbbd2e, 0x67dad0e7, 0x70a1c4a4, 0x492cf861, 0x5e57ec22, 0x81ee23f3, 0x969537b0, 0xaf180b75, 0xb8631f36, 0xdc0272ff, 0xcb7966bc, 0xf2f45a79, 0xe58f4e3a, 0x96f6c39a, 0x818dd7d9, 0xb800eb1c, 0xaf7bff5f, 0xcb1a9296, 0xdc6186d5, 0xe5ecba10, 0xf297ae53, 0x2d2e6182, 0x3a5575c1, 0x03d84904, 0x14a35d47, 0x70c2308e, 0x67b924cd, 0x5e341808, 0x494f0c4b}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x00000000, 0x43147b17, 0x8628f62e, 0xc53c8d39, 0x0c51ec5d, 0x4f45974a, 0x8a791a73, 0xc96d6164, 0x18a2d8bb, 0x5bb6a3ac, 0x9e8a2e95, 0xdd9e5582, 0x14f334e6, 0x57e74ff1, 0x92dbc2c8, 0xd1cfb9df, 0x7142c0ac, 0x3256bbbb, 0xf76a3682, 0xb47e4d95, 0x7d132cf1, 0x3e0757e6, 0xfb3bdadf, 0xb82fa1c8, 0x69e01817, 0x2af46300, 0xefc8ee39, 0xacdc952e, 0x65b1f44a, 0x26a58f5d, 0xe3990264, 0xa08d7973, 0xa382f182, 0xe0968a95, 0x25aa07ac, 0x66be7cbb, 0xafd31ddf, 0xecc766c8, 0x29fbebf1, 0x6aef90e6, 0xbb202939, 0xf834522e, 0x3d08df17, 0x7e1ca400, 0xb771c564, 0xf465be73, 0x3159334a, 0x724d485d, 0xd2c0312e, 0x91d44a39, 0x54e8c700, 0x17fcbc17, 0xde91dd73, 0x9d85a664, 0x58b92b5d, 0x1bad504a, 0xca62e995, 0x89769282, 0x4c4a1fbb, 0x0f5e64ac, 0xc63305c8, 0x85277edf, 0x401bf3e6, 0x030f88f1, 0x070392de, 0x4417e9c9, 0x812b64f0, 0xc23f1fe7, 0x0b527e83, 0x48460594, 0x8d7a88ad, 0xce6ef3ba, 0x1fa14a65, 0x5cb53172, 0x9989bc4b, 0xda9dc75c, 0x13f0a638, 0x50e4dd2f, 0x95d85016, 0xd6cc2b01, 0x76415272, 0x35552965, 0xf069a45c, 0xb37ddf4b, 0x7a10be2f, 0x3904c538, 0xfc384801, 0xbf2c3316, 0x6ee38ac9, 0x2df7f1de, 0xe8cb7ce7, 0xabdf07f0, 0x62b26694, 0x21a61d83, 0xe49a90ba, 0xa78eebad, 0xa481635c, 0xe795184b, 0x22a99572, 0x61bdee65, 0xa8d08f01, 0xebc4f416, 0x2ef8792f, 0x6dec0238, 0xbc23bbe7, 0xff37c0f0, 0x3a0b4dc9, 0x791f36de, 0xb07257ba, 0xf3662cad, 0x365aa194, 0x754eda83, 0xd5c3a3f0, 0x96d7d8e7, 0x53eb55de, 0x10ff2ec9, 0xd9924fad, 0x9a8634ba, 0x5fbab983, 0x1caec294, 0xcd617b4b, 0x8e75005c, 0x4b498d65, 0x085df672, 0xc1309716, 0x8224ec01, 0x47186138, 0x040c1a2f, 0x4f005566, 0x0c142e71, 0xc928a348, 0x8a3cd85f, 0x4351b93b, 0x0045c22c, 0xc5794f15, 0x866d3402, 0x57a28ddd, 0x14b6f6ca, 0xd18a7bf3, 0x929e00e4, 0x5bf36180, 0x18e71a97, 0xdddb97ae, 0x9ecfecb9, 0x3e4295ca, 0x7d56eedd, 0xb86a63e4, 0xfb7e18f3, 0x32137997, 0x71070280, 0xb43b8fb9, 0xf72ff4ae, 0x26e04d71, 0x65f43666, 0xa0c8bb5f, 0xe3dcc048, 0x2ab1a12c, 0x69a5da3b, 0xac995702, 0xef8d2c15, 0xec82a4e4, 0xaf96dff3, 0x6aaa52ca, 0x29be29dd, 0xe0d348b9, 0xa3c733ae, 0x66fbbe97, 0x25efc580, 0xf4207c5f, 0xb7340748, 0x72088a71, 0x311cf166, 0xf8719002, 0xbb65eb15, 0x7e59662c, 0x3d4d1d3b, 0x9dc06448, 0xded41f5f, 0x1be89266, 0x58fce971, 0x91918815, 0xd285f302, 0x17b97e3b, 0x54ad052c, 0x8562bcf3, 0xc676c7e4, 0x034a4add, 0x405e31ca, 0x893350ae, 0xca272bb9, 0x0f1ba680, 0x4c0fdd97, 0x4803c7b8, 0x0b17bcaf, 0xce2b3196, 0x8d3f4a81, 0x44522be5, 0x074650f2, 0xc27addcb, 0x816ea6dc, 0x50a11f03, 0x13b56414, 0xd689e92d, 0x959d923a, 0x5cf0f35e, 0x1fe48849, 0xdad80570, 0x99cc7e67, 0x39410714, 0x7a557c03, 0xbf69f13a, 0xfc7d8a2d, 0x3510eb49, 0x7604905e, 0xb3381d67, 0xf02c6670, 0x21e3dfaf, 0x62f7a4b8, 0xa7cb2981, 0xe4df5296, 0x2db233f2, 0x6ea648e5, 0xab9ac5dc, 0xe88ebecb, 0xeb81363a, 0xa8954d2d, 0x6da9c014, 0x2ebdbb03, 0xe7d0da67, 0xa4c4a170, 0x61f82c49, 0x22ec575e, 0xf323ee81, 0xb0379596, 0x750b18af, 0x361f63b8, 0xff7202dc, 0xbc6679cb, 0x795af4f2, 0x3a4e8fe5, 0x9ac3f696, 0xd9d78d81, 0x1ceb00b8, 0x5fff7baf, 0x96921acb, 0xd58661dc, 0x10baece5, 0x53ae97f2, 0x82612e2d, 0xc175553a, 0x0449d803, 0x475da314, 0x8e30c270, 0xcd24b967, 0x0818345e, 0x4b0c4f49}, {0x00000000, 0x3e6bc2ef, 0x3dd0f504, 0x03bb37eb, 0x7aa0eb09, 0x44cb29e6, 0x47701e0d, 0x791bdce2, 0xf440d713, 0xca2b15fc, 0xc9902217, 0xf7fbe0f8, 0x8ee03c1a, 0xb08bfef5, 0xb330c91e, 0x8d5b0bf1, 0xe881ae27, 0xd6ea6cc8, 0xd5515b23, 0xeb3a99cc, 0x9221452e, 0xac4a87c1, 0xaff1b02a, 0x919a72c5, 0x1cc17934, 0x22aabbdb, 0x21118c30, 0x1f7a4edf, 0x6661923d, 0x580a50d2, 0x5bb16739, 0x65daa5d6, 0xd0035d4f, 0xee689fa0, 0xedd3a84b, 0xd3b86aa4, 0xaaa3b646, 0x94c874a9, 0x97734342, 0xa91881ad, 0x24438a5c, 0x1a2848b3, 0x19937f58, 0x27f8bdb7, 0x5ee36155, 0x6088a3ba, 0x63339451, 0x5d5856be, 0x3882f368, 0x06e93187, 0x0552066c, 0x3b39c483, 0x42221861, 0x7c49da8e, 0x7ff2ed65, 0x41992f8a, 0xccc2247b, 0xf2a9e694, 0xf112d17f, 0xcf791390, 0xb662cf72, 0x88090d9d, 0x8bb23a76, 0xb5d9f899, 0xa007ba9e, 0x9e6c7871, 0x9dd74f9a, 0xa3bc8d75, 0xdaa75197, 0xe4cc9378, 0xe777a493, 0xd91c667c, 0x54476d8d, 0x6a2caf62, 0x69979889, 0x57fc5a66, 0x2ee78684, 0x108c446b, 0x13377380, 0x2d5cb16f, 0x488614b9, 0x76edd656, 0x7556e1bd, 0x4b3d2352, 0x3226ffb0, 0x0c4d3d5f, 0x0ff60ab4, 0x319dc85b, 0xbcc6c3aa, 0x82ad0145, 0x811636ae, 0xbf7df441, 0xc66628a3, 0xf80dea4c, 0xfbb6dda7, 0xc5dd1f48, 0x7004e7d1, 0x4e6f253e, 0x4dd412d5, 0x73bfd03a, 0x0aa40cd8, 0x34cfce37, 0x3774f9dc, 0x091f3b33, 0x844430c2, 0xba2ff22d, 0xb994c5c6, 0x87ff0729, 0xfee4dbcb, 0xc08f1924, 0xc3342ecf, 0xfd5fec20, 0x988549f6, 0xa6ee8b19, 0xa555bcf2, 0x9b3e7e1d, 0xe225a2ff, 0xdc4e6010, 0xdff557fb, 0xe19e9514, 0x6cc59ee5, 0x52ae5c0a, 0x51156be1, 0x6f7ea90e, 0x166575ec, 0x280eb703, 0x2bb580e8, 0x15de4207, 0x010905e6, 0x3f62c709, 0x3cd9f0e2, 0x02b2320d, 0x7ba9eeef, 0x45c22c00, 0x46791beb, 0x7812d904, 0xf549d2f5, 0xcb22101a, 0xc89927f1, 0xf6f2e51e, 0x8fe939fc, 0xb182fb13, 0xb239ccf8, 0x8c520e17, 0xe988abc1, 0xd7e3692e, 0xd4585ec5, 0xea339c2a, 0x932840c8, 0xad438227, 0xaef8b5cc, 0x90937723, 0x1dc87cd2, 0x23a3be3d, 0x201889d6, 0x1e734b39, 0x676897db, 0x59035534, 0x5ab862df, 0x64d3a030, 0xd10a58a9, 0xef619a46, 0xecdaadad, 0xd2b16f42, 0xabaab3a0, 0x95c1714f, 0x967a46a4, 0xa811844b, 0x254a8fba, 0x1b214d55, 0x189a7abe, 0x26f1b851, 0x5fea64b3, 0x6181a65c, 0x623a91b7, 0x5c515358, 0x398bf68e, 0x07e03461, 0x045b038a, 0x3a30c165, 0x432b1d87, 0x7d40df68, 0x7efbe883, 0x40902a6c, 0xcdcb219d, 0xf3a0e372, 0xf01bd499, 0xce701676, 0xb76bca94, 0x8900087b, 0x8abb3f90, 0xb4d0fd7f, 0xa10ebf78, 0x9f657d97, 0x9cde4a7c, 0xa2b58893, 0xdbae5471, 0xe5c5969e, 0xe67ea175, 0xd815639a, 0x554e686b, 0x6b25aa84, 0x689e9d6f, 0x56f55f80, 0x2fee8362, 0x1185418d, 0x123e7666, 0x2c55b489, 0x498f115f, 0x77e4d3b0, 0x745fe45b, 0x4a3426b4, 0x332ffa56, 0x0d4438b9, 0x0eff0f52, 0x3094cdbd, 0xbdcfc64c, 0x83a404a3, 0x801f3348, 0xbe74f1a7, 0xc76f2d45, 0xf904efaa, 0xfabfd841, 0xc4d41aae, 0x710de237, 0x4f6620d8, 0x4cdd1733, 0x72b6d5dc, 0x0bad093e, 0x35c6cbd1, 0x367dfc3a, 0x08163ed5, 0x854d3524, 0xbb26f7cb, 0xb89dc020, 0x86f602cf, 0xffedde2d, 0xc1861cc2, 0xc23d2b29, 0xfc56e9c6, 0x998c4c10, 0xa7e78eff, 0xa45cb914, 0x9a377bfb, 0xe32ca719, 0xdd4765f6, 0xdefc521d, 0xe09790f2, 0x6dcc9b03, 0x53a759ec, 0x501c6e07, 0x6e77ace8, 0x176c700a, 0x2907b2e5, 0x2abc850e, 0x14d747e1}, {0x00000000, 0xc0df8ec1, 0xc1b96c58, 0x0166e299, 0x8273d9b0, 0x42ac5771, 0x43cab5e8, 0x83153b29, 0x45e1c3ba, 0x853e4d7b, 0x8458afe2, 0x44872123, 0xc7921a0a, 0x074d94cb, 0x062b7652, 0xc6f4f893, 0xcbc4f6ae, 0x0b1b786f, 0x0a7d9af6, 0xcaa21437, 0x49b72f1e, 0x8968a1df, 0x880e4346, 0x48d1cd87, 0x8e253514, 0x4efabbd5, 0x4f9c594c, 0x8f43d78d, 0x0c56eca4, 0xcc896265, 0xcdef80fc, 0x0d300e3d, 0xd78f9c86, 0x17501247, 0x1636f0de, 0xd6e97e1f, 0x55fc4536, 0x9523cbf7, 0x9445296e, 0x549aa7af, 0x926e5f3c, 0x52b1d1fd, 0x53d73364, 0x9308bda5, 0x101d868c, 0xd0c2084d, 0xd1a4ead4, 0x117b6415, 0x1c4b6a28, 0xdc94e4e9, 0xddf20670, 0x1d2d88b1, 0x9e38b398, 0x5ee73d59, 0x5f81dfc0, 0x9f5e5101, 0x59aaa992, 0x99752753, 0x9813c5ca, 0x58cc4b0b, 0xdbd97022, 0x1b06fee3, 0x1a601c7a, 0xdabf92bb, 0xef1948d6, 0x2fc6c617, 0x2ea0248e, 0xee7faa4f, 0x6d6a9166, 0xadb51fa7, 0xacd3fd3e, 0x6c0c73ff, 0xaaf88b6c, 0x6a2705ad, 0x6b41e734, 0xab9e69f5, 0x288b52dc, 0xe854dc1d, 0xe9323e84, 0x29edb045, 0x24ddbe78, 0xe40230b9, 0xe564d220, 0x25bb5ce1, 0xa6ae67c8, 0x6671e909, 0x67170b90, 0xa7c88551, 0x613c7dc2, 0xa1e3f303, 0xa085119a, 0x605a9f5b, 0xe34fa472, 0x23902ab3, 0x22f6c82a, 0xe22946eb, 0x3896d450, 0xf8495a91, 0xf92fb808, 0x39f036c9, 0xbae50de0, 0x7a3a8321, 0x7b5c61b8, 0xbb83ef79, 0x7d7717ea, 0xbda8992b, 0xbcce7bb2, 0x7c11f573, 0xff04ce5a, 0x3fdb409b, 0x3ebda202, 0xfe622cc3, 0xf35222fe, 0x338dac3f, 0x32eb4ea6, 0xf234c067, 0x7121fb4e, 0xb1fe758f, 0xb0989716, 0x704719d7, 0xb6b3e144, 0x766c6f85, 0x770a8d1c, 0xb7d503dd, 0x34c038f4, 0xf41fb635, 0xf57954ac, 0x35a6da6d, 0x9f35e177, 0x5fea6fb6, 0x5e8c8d2f, 0x9e5303ee, 0x1d4638c7, 0xdd99b606, 0xdcff549f, 0x1c20da5e, 0xdad422cd, 0x1a0bac0c, 0x1b6d4e95, 0xdbb2c054, 0x58a7fb7d, 0x987875bc, 0x991e9725, 0x59c119e4, 0x54f117d9, 0x942e9918, 0x95487b81, 0x5597f540, 0xd682ce69, 0x165d40a8, 0x173ba231, 0xd7e42cf0, 0x1110d463, 0xd1cf5aa2, 0xd0a9b83b, 0x107636fa, 0x93630dd3, 0x53bc8312, 0x52da618b, 0x9205ef4a, 0x48ba7df1, 0x8865f330, 0x890311a9, 0x49dc9f68, 0xcac9a441, 0x0a162a80, 0x0b70c819, 0xcbaf46d8, 0x0d5bbe4b, 0xcd84308a, 0xcce2d213, 0x0c3d5cd2, 0x8f2867fb, 0x4ff7e93a, 0x4e910ba3, 0x8e4e8562, 0x837e8b5f, 0x43a1059e, 0x42c7e707, 0x821869c6, 0x010d52ef, 0xc1d2dc2e, 0xc0b43eb7, 0x006bb076, 0xc69f48e5, 0x0640c624, 0x072624bd, 0xc7f9aa7c, 0x44ec9155, 0x84331f94, 0x8555fd0d, 0x458a73cc, 0x702ca9a1, 0xb0f32760, 0xb195c5f9, 0x714a4b38, 0xf25f7011, 0x3280fed0, 0x33e61c49, 0xf3399288, 0x35cd6a1b, 0xf512e4da, 0xf4740643, 0x34ab8882, 0xb7beb3ab, 0x77613d6a, 0x7607dff3, 0xb6d85132, 0xbbe85f0f, 0x7b37d1ce, 0x7a513357, 0xba8ebd96, 0x399b86bf, 0xf944087e, 0xf822eae7, 0x38fd6426, 0xfe099cb5, 0x3ed61274, 0x3fb0f0ed, 0xff6f7e2c, 0x7c7a4505, 0xbca5cbc4, 0xbdc3295d, 0x7d1ca79c, 0xa7a33527, 0x677cbbe6, 0x661a597f, 0xa6c5d7be, 0x25d0ec97, 0xe50f6256, 0xe46980cf, 0x24b60e0e, 0xe242f69d, 0x229d785c, 0x23fb9ac5, 0xe3241404, 0x60312f2d, 0xa0eea1ec, 0xa1884375, 0x6157cdb4, 0x6c67c389, 0xacb84d48, 0xaddeafd1, 0x6d012110, 0xee141a39, 0x2ecb94f8, 0x2fad7661, 0xef72f8a0, 0x29860033, 0xe9598ef2, 0xe83f6c6b, 0x28e0e2aa, 0xabf5d983, 0x6b2a5742, 0x6a4cb5db, 0xaa933b1a}, {0x00000000, 0x6f4ca59b, 0x9f9e3bec, 0xf0d29e77, 0x7f3b0603, 0x1077a398, 0xe0a53def, 0x8fe99874, 0xfe760c06, 0x913aa99d, 0x61e837ea, 0x0ea49271, 0x814d0a05, 0xee01af9e, 0x1ed331e9, 0x719f9472, 0xfced180c, 0x93a1bd97, 0x637323e0, 0x0c3f867b, 0x83d61e0f, 0xec9abb94, 0x1c4825e3, 0x73048078, 0x029b140a, 0x6dd7b191, 0x9d052fe6, 0xf2498a7d, 0x7da01209, 0x12ecb792, 0xe23e29e5, 0x8d728c7e, 0xf8db3118, 0x97979483, 0x67450af4, 0x0809af6f, 0x87e0371b, 0xe8ac9280, 0x187e0cf7, 0x7732a96c, 0x06ad3d1e, 0x69e19885, 0x993306f2, 0xf67fa369, 0x79963b1d, 0x16da9e86, 0xe60800f1, 0x8944a56a, 0x04362914, 0x6b7a8c8f, 0x9ba812f8, 0xf4e4b763, 0x7b0d2f17, 0x14418a8c, 0xe49314fb, 0x8bdfb160, 0xfa402512, 0x950c8089, 0x65de1efe, 0x0a92bb65, 0x857b2311, 0xea37868a, 0x1ae518fd, 0x75a9bd66, 0xf0b76330, 0x9ffbc6ab, 0x6f2958dc, 0x0065fd47, 0x8f8c6533, 0xe0c0c0a8, 0x10125edf, 0x7f5efb44, 0x0ec16f36, 0x618dcaad, 0x915f54da, 0xfe13f141, 0x71fa6935, 0x1eb6ccae, 0xee6452d9, 0x8128f742, 0x0c5a7b3c, 0x6316dea7, 0x93c440d0, 0xfc88e54b, 0x73617d3f, 0x1c2dd8a4, 0xecff46d3, 0x83b3e348, 0xf22c773a, 0x9d60d2a1, 0x6db24cd6, 0x02fee94d, 0x8d177139, 0xe25bd4a2, 0x12894ad5, 0x7dc5ef4e, 0x086c5228, 0x6720f7b3, 0x97f269c4, 0xf8becc5f, 0x7757542b, 0x181bf1b0, 0xe8c96fc7, 0x8785ca5c, 0xf61a5e2e, 0x9956fbb5, 0x698465c2, 0x06c8c059, 0x8921582d, 0xe66dfdb6, 0x16bf63c1, 0x79f3c65a, 0xf4814a24, 0x9bcdefbf, 0x6b1f71c8, 0x0453d453, 0x8bba4c27, 0xe4f6e9bc, 0x142477cb, 0x7b68d250, 0x0af74622, 0x65bbe3b9, 0x95697dce, 0xfa25d855, 0x75cc4021, 0x1a80e5ba, 0xea527bcd, 0x851ede56, 0xe06fc760, 0x8f2362fb, 0x7ff1fc8c, 0x10bd5917, 0x9f54c163, 0xf01864f8, 0x00cafa8f, 0x6f865f14, 0x1e19cb66, 0x71556efd, 0x8187f08a, 0xeecb5511, 0x6122cd65, 0x0e6e68fe, 0xfebcf689, 0x91f05312, 0x1c82df6c, 0x73ce7af7, 0x831ce480, 0xec50411b, 0x63b9d96f, 0x0cf57cf4, 0xfc27e283, 0x936b4718, 0xe2f4d36a, 0x8db876f1, 0x7d6ae886, 0x12264d1d, 0x9dcfd569, 0xf28370f2, 0x0251ee85, 0x6d1d4b1e, 0x18b4f678, 0x77f853e3, 0x872acd94, 0xe866680f, 0x678ff07b, 0x08c355e0, 0xf811cb97, 0x975d6e0c, 0xe6c2fa7e, 0x898e5fe5, 0x795cc192, 0x16106409, 0x99f9fc7d, 0xf6b559e6, 0x0667c791, 0x692b620a, 0xe459ee74, 0x8b154bef, 0x7bc7d598, 0x148b7003, 0x9b62e877, 0xf42e4dec, 0x04fcd39b, 0x6bb07600, 0x1a2fe272, 0x756347e9, 0x85b1d99e, 0xeafd7c05, 0x6514e471, 0x0a5841ea, 0xfa8adf9d, 0x95c67a06, 0x10d8a450, 0x7f9401cb, 0x8f469fbc, 0xe00a3a27, 0x6fe3a253, 0x00af07c8, 0xf07d99bf, 0x9f313c24, 0xeeaea856, 0x81e20dcd, 0x713093ba, 0x1e7c3621, 0x9195ae55, 0xfed90bce, 0x0e0b95b9, 0x61473022, 0xec35bc5c, 0x837919c7, 0x73ab87b0, 0x1ce7222b, 0x930eba5f, 0xfc421fc4, 0x0c9081b3, 0x63dc2428, 0x1243b05a, 0x7d0f15c1, 0x8ddd8bb6, 0xe2912e2d, 0x6d78b659, 0x023413c2, 0xf2e68db5, 0x9daa282e, 0xe8039548, 0x874f30d3, 0x779daea4, 0x18d10b3f, 0x9738934b, 0xf87436d0, 0x08a6a8a7, 0x67ea0d3c, 0x1675994e, 0x79393cd5, 0x89eba2a2, 0xe6a70739, 0x694e9f4d, 0x06023ad6, 0xf6d0a4a1, 0x999c013a, 0x14ee8d44, 0x7ba228df, 0x8b70b6a8, 0xe43c1333, 0x6bd58b47, 0x04992edc, 0xf44bb0ab, 0x9b071530, 0xea988142, 0x85d424d9, 0x7506baae, 0x1a4a1f35, 0x95a38741, 0xfaef22da, 0x0a3dbcad, 0x65711936}}; #endif #endif #if N == 4 #if W == 8 local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0xf1da05aa, 0x38c50d15, 0xc91f08bf, 0x718a1a2a, 0x80501f80, 0x494f173f, 0xb8951295, 0xe3143454, 0x12ce31fe, 0xdbd13941, 0x2a0b3ceb, 0x929e2e7e, 0x63442bd4, 0xaa5b236b, 0x5b8126c1, 0x1d596ee9, 0xec836b43, 0x259c63fc, 0xd4466656, 0x6cd374c3, 0x9d097169, 0x541679d6, 0xa5cc7c7c, 0xfe4d5abd, 0x0f975f17, 0xc68857a8, 0x37525202, 0x8fc74097, 0x7e1d453d, 0xb7024d82, 0x46d84828, 0x3ab2ddd2, 0xcb68d878, 0x0277d0c7, 0xf3add56d, 0x4b38c7f8, 0xbae2c252, 0x73fdcaed, 0x8227cf47, 0xd9a6e986, 0x287cec2c, 0xe163e493, 0x10b9e139, 0xa82cf3ac, 0x59f6f606, 0x90e9feb9, 0x6133fb13, 0x27ebb33b, 0xd631b691, 0x1f2ebe2e, 0xeef4bb84, 0x5661a911, 0xa7bbacbb, 0x6ea4a404, 0x9f7ea1ae, 0xc4ff876f, 0x352582c5, 0xfc3a8a7a, 0x0de08fd0, 0xb5759d45, 0x44af98ef, 0x8db09050, 0x7c6a95fa, 0x7565bba4, 0x84bfbe0e, 0x4da0b6b1, 0xbc7ab31b, 0x04efa18e, 0xf535a424, 0x3c2aac9b, 0xcdf0a931, 0x96718ff0, 0x67ab8a5a, 0xaeb482e5, 0x5f6e874f, 0xe7fb95da, 0x16219070, 0xdf3e98cf, 0x2ee49d65, 0x683cd54d, 0x99e6d0e7, 0x50f9d858, 0xa123ddf2, 0x19b6cf67, 0xe86ccacd, 0x2173c272, 0xd0a9c7d8, 0x8b28e119, 0x7af2e4b3, 0xb3edec0c, 0x4237e9a6, 0xfaa2fb33, 0x0b78fe99, 0xc267f626, 0x33bdf38c, 0x4fd76676, 0xbe0d63dc, 0x77126b63, 0x86c86ec9, 0x3e5d7c5c, 0xcf8779f6, 0x06987149, 0xf74274e3, 0xacc35222, 0x5d195788, 0x94065f37, 0x65dc5a9d, 0xdd494808, 0x2c934da2, 0xe58c451d, 0x145640b7, 0x528e089f, 0xa3540d35, 0x6a4b058a, 0x9b910020, 0x230412b5, 0xd2de171f, 0x1bc11fa0, 0xea1b1a0a, 0xb19a3ccb, 0x40403961, 0x895f31de, 0x78853474, 0xc01026e1, 0x31ca234b, 0xf8d52bf4, 0x090f2e5e, 0xeacb7748, 0x1b1172e2, 0xd20e7a5d, 0x23d47ff7, 0x9b416d62, 0x6a9b68c8, 0xa3846077, 0x525e65dd, 0x09df431c, 0xf80546b6, 0x311a4e09, 0xc0c04ba3, 0x78555936, 0x898f5c9c, 0x40905423, 0xb14a5189, 0xf79219a1, 0x06481c0b, 0xcf5714b4, 0x3e8d111e, 0x8618038b, 0x77c20621, 0xbedd0e9e, 0x4f070b34, 0x14862df5, 0xe55c285f, 0x2c4320e0, 0xdd99254a, 0x650c37df, 0x94d63275, 0x5dc93aca, 0xac133f60, 0xd079aa9a, 0x21a3af30, 0xe8bca78f, 0x1966a225, 0xa1f3b0b0, 0x5029b51a, 0x9936bda5, 0x68ecb80f, 0x336d9ece, 0xc2b79b64, 0x0ba893db, 0xfa729671, 0x42e784e4, 0xb33d814e, 0x7a2289f1, 0x8bf88c5b, 0xcd20c473, 0x3cfac1d9, 0xf5e5c966, 0x043fcccc, 0xbcaade59, 0x4d70dbf3, 0x846fd34c, 0x75b5d6e6, 0x2e34f027, 0xdfeef58d, 0x16f1fd32, 0xe72bf898, 0x5fbeea0d, 0xae64efa7, 0x677be718, 0x96a1e2b2, 0x9faeccec, 0x6e74c946, 0xa76bc1f9, 0x56b1c453, 0xee24d6c6, 0x1ffed36c, 0xd6e1dbd3, 0x273bde79, 0x7cbaf8b8, 0x8d60fd12, 0x447ff5ad, 0xb5a5f007, 0x0d30e292, 0xfceae738, 0x35f5ef87, 0xc42fea2d, 0x82f7a205, 0x732da7af, 0xba32af10, 0x4be8aaba, 0xf37db82f, 0x02a7bd85, 0xcbb8b53a, 0x3a62b090, 0x61e39651, 0x903993fb, 0x59269b44, 0xa8fc9eee, 0x10698c7b, 0xe1b389d1, 0x28ac816e, 0xd97684c4, 0xa51c113e, 0x54c61494, 0x9dd91c2b, 0x6c031981, 0xd4960b14, 0x254c0ebe, 0xec530601, 0x1d8903ab, 0x4608256a, 0xb7d220c0, 0x7ecd287f, 0x8f172dd5, 0x37823f40, 0xc6583aea, 0x0f473255, 0xfe9d37ff, 0xb8457fd7, 0x499f7a7d, 0x808072c2, 0x715a7768, 0xc9cf65fd, 0x38156057, 0xf10a68e8, 0x00d06d42, 0x5b514b83, 0xaa8b4e29, 0x63944696, 0x924e433c, 0x2adb51a9, 0xdb015403, 0x121e5cbc, 0xe3c45916}, {0x00000000, 0x0ee7e8d1, 0x1dcfd1a2, 0x13283973, 0x3b9fa344, 0x35784b95, 0x265072e6, 0x28b79a37, 0x773f4688, 0x79d8ae59, 0x6af0972a, 0x64177ffb, 0x4ca0e5cc, 0x42470d1d, 0x516f346e, 0x5f88dcbf, 0xee7e8d10, 0xe09965c1, 0xf3b15cb2, 0xfd56b463, 0xd5e12e54, 0xdb06c685, 0xc82efff6, 0xc6c91727, 0x9941cb98, 0x97a62349, 0x848e1a3a, 0x8a69f2eb, 0xa2de68dc, 0xac39800d, 0xbf11b97e, 0xb1f651af, 0x078c1c61, 0x096bf4b0, 0x1a43cdc3, 0x14a42512, 0x3c13bf25, 0x32f457f4, 0x21dc6e87, 0x2f3b8656, 0x70b35ae9, 0x7e54b238, 0x6d7c8b4b, 0x639b639a, 0x4b2cf9ad, 0x45cb117c, 0x56e3280f, 0x5804c0de, 0xe9f29171, 0xe71579a0, 0xf43d40d3, 0xfadaa802, 0xd26d3235, 0xdc8adae4, 0xcfa2e397, 0xc1450b46, 0x9ecdd7f9, 0x902a3f28, 0x8302065b, 0x8de5ee8a, 0xa55274bd, 0xabb59c6c, 0xb89da51f, 0xb67a4dce, 0x0f1838c2, 0x01ffd013, 0x12d7e960, 0x1c3001b1, 0x34879b86, 0x3a607357, 0x29484a24, 0x27afa2f5, 0x78277e4a, 0x76c0969b, 0x65e8afe8, 0x6b0f4739, 0x43b8dd0e, 0x4d5f35df, 0x5e770cac, 0x5090e47d, 0xe166b5d2, 0xef815d03, 0xfca96470, 0xf24e8ca1, 0xdaf91696, 0xd41efe47, 0xc736c734, 0xc9d12fe5, 0x9659f35a, 0x98be1b8b, 0x8b9622f8, 0x8571ca29, 0xadc6501e, 0xa321b8cf, 0xb00981bc, 0xbeee696d, 0x089424a3, 0x0673cc72, 0x155bf501, 0x1bbc1dd0, 0x330b87e7, 0x3dec6f36, 0x2ec45645, 0x2023be94, 0x7fab622b, 0x714c8afa, 0x6264b389, 0x6c835b58, 0x4434c16f, 0x4ad329be, 0x59fb10cd, 0x571cf81c, 0xe6eaa9b3, 0xe80d4162, 0xfb257811, 0xf5c290c0, 0xdd750af7, 0xd392e226, 0xc0badb55, 0xce5d3384, 0x91d5ef3b, 0x9f3207ea, 0x8c1a3e99, 0x82fdd648, 0xaa4a4c7f, 0xa4ada4ae, 0xb7859ddd, 0xb962750c, 0x1e307184, 0x10d79955, 0x03ffa026, 0x0d1848f7, 0x25afd2c0, 0x2b483a11, 0x38600362, 0x3687ebb3, 0x690f370c, 0x67e8dfdd, 0x74c0e6ae, 0x7a270e7f, 0x52909448, 0x5c777c99, 0x4f5f45ea, 0x41b8ad3b, 0xf04efc94, 0xfea91445, 0xed812d36, 0xe366c5e7, 0xcbd15fd0, 0xc536b701, 0xd61e8e72, 0xd8f966a3, 0x8771ba1c, 0x899652cd, 0x9abe6bbe, 0x9459836f, 0xbcee1958, 0xb209f189, 0xa121c8fa, 0xafc6202b, 0x19bc6de5, 0x175b8534, 0x0473bc47, 0x0a945496, 0x2223cea1, 0x2cc42670, 0x3fec1f03, 0x310bf7d2, 0x6e832b6d, 0x6064c3bc, 0x734cfacf, 0x7dab121e, 0x551c8829, 0x5bfb60f8, 0x48d3598b, 0x4634b15a, 0xf7c2e0f5, 0xf9250824, 0xea0d3157, 0xe4ead986, 0xcc5d43b1, 0xc2baab60, 0xd1929213, 0xdf757ac2, 0x80fda67d, 0x8e1a4eac, 0x9d3277df, 0x93d59f0e, 0xbb620539, 0xb585ede8, 0xa6add49b, 0xa84a3c4a, 0x11284946, 0x1fcfa197, 0x0ce798e4, 0x02007035, 0x2ab7ea02, 0x245002d3, 0x37783ba0, 0x399fd371, 0x66170fce, 0x68f0e71f, 0x7bd8de6c, 0x753f36bd, 0x5d88ac8a, 0x536f445b, 0x40477d28, 0x4ea095f9, 0xff56c456, 0xf1b12c87, 0xe29915f4, 0xec7efd25, 0xc4c96712, 0xca2e8fc3, 0xd906b6b0, 0xd7e15e61, 0x886982de, 0x868e6a0f, 0x95a6537c, 0x9b41bbad, 0xb3f6219a, 0xbd11c94b, 0xae39f038, 0xa0de18e9, 0x16a45527, 0x1843bdf6, 0x0b6b8485, 0x058c6c54, 0x2d3bf663, 0x23dc1eb2, 0x30f427c1, 0x3e13cf10, 0x619b13af, 0x6f7cfb7e, 0x7c54c20d, 0x72b32adc, 0x5a04b0eb, 0x54e3583a, 0x47cb6149, 0x492c8998, 0xf8dad837, 0xf63d30e6, 0xe5150995, 0xebf2e144, 0xc3457b73, 0xcda293a2, 0xde8aaad1, 0xd06d4200, 0x8fe59ebf, 0x8102766e, 0x922a4f1d, 0x9ccda7cc, 0xb47a3dfb, 0xba9dd52a, 0xa9b5ec59, 0xa7520488}, {0x00000000, 0x3c60e308, 0x78c1c610, 0x44a12518, 0xf1838c20, 0xcde36f28, 0x89424a30, 0xb522a938, 0x38761e01, 0x0416fd09, 0x40b7d811, 0x7cd73b19, 0xc9f59221, 0xf5957129, 0xb1345431, 0x8d54b739, 0x70ec3c02, 0x4c8cdf0a, 0x082dfa12, 0x344d191a, 0x816fb022, 0xbd0f532a, 0xf9ae7632, 0xc5ce953a, 0x489a2203, 0x74fac10b, 0x305be413, 0x0c3b071b, 0xb919ae23, 0x85794d2b, 0xc1d86833, 0xfdb88b3b, 0xe1d87804, 0xddb89b0c, 0x9919be14, 0xa5795d1c, 0x105bf424, 0x2c3b172c, 0x689a3234, 0x54fad13c, 0xd9ae6605, 0xe5ce850d, 0xa16fa015, 0x9d0f431d, 0x282dea25, 0x144d092d, 0x50ec2c35, 0x6c8ccf3d, 0x91344406, 0xad54a70e, 0xe9f58216, 0xd595611e, 0x60b7c826, 0x5cd72b2e, 0x18760e36, 0x2416ed3e, 0xa9425a07, 0x9522b90f, 0xd1839c17, 0xede37f1f, 0x58c1d627, 0x64a1352f, 0x20001037, 0x1c60f33f, 0x18c1f649, 0x24a11541, 0x60003059, 0x5c60d351, 0xe9427a69, 0xd5229961, 0x9183bc79, 0xade35f71, 0x20b7e848, 0x1cd70b40, 0x58762e58, 0x6416cd50, 0xd1346468, 0xed548760, 0xa9f5a278, 0x95954170, 0x682dca4b, 0x544d2943, 0x10ec0c5b, 0x2c8cef53, 0x99ae466b, 0xa5cea563, 0xe16f807b, 0xdd0f6373, 0x505bd44a, 0x6c3b3742, 0x289a125a, 0x14faf152, 0xa1d8586a, 0x9db8bb62, 0xd9199e7a, 0xe5797d72, 0xf9198e4d, 0xc5796d45, 0x81d8485d, 0xbdb8ab55, 0x089a026d, 0x34fae165, 0x705bc47d, 0x4c3b2775, 0xc16f904c, 0xfd0f7344, 0xb9ae565c, 0x85ceb554, 0x30ec1c6c, 0x0c8cff64, 0x482dda7c, 0x744d3974, 0x89f5b24f, 0xb5955147, 0xf134745f, 0xcd549757, 0x78763e6f, 0x4416dd67, 0x00b7f87f, 0x3cd71b77, 0xb183ac4e, 0x8de34f46, 0xc9426a5e, 0xf5228956, 0x4000206e, 0x7c60c366, 0x38c1e67e, 0x04a10576, 0x3183ec92, 0x0de30f9a, 0x49422a82, 0x7522c98a, 0xc00060b2, 0xfc6083ba, 0xb8c1a6a2, 0x84a145aa, 0x09f5f293, 0x3595119b, 0x71343483, 0x4d54d78b, 0xf8767eb3, 0xc4169dbb, 0x80b7b8a3, 0xbcd75bab, 0x416fd090, 0x7d0f3398, 0x39ae1680, 0x05cef588, 0xb0ec5cb0, 0x8c8cbfb8, 0xc82d9aa0, 0xf44d79a8, 0x7919ce91, 0x45792d99, 0x01d80881, 0x3db8eb89, 0x889a42b1, 0xb4faa1b9, 0xf05b84a1, 0xcc3b67a9, 0xd05b9496, 0xec3b779e, 0xa89a5286, 0x94fab18e, 0x21d818b6, 0x1db8fbbe, 0x5919dea6, 0x65793dae, 0xe82d8a97, 0xd44d699f, 0x90ec4c87, 0xac8caf8f, 0x19ae06b7, 0x25cee5bf, 0x616fc0a7, 0x5d0f23af, 0xa0b7a894, 0x9cd74b9c, 0xd8766e84, 0xe4168d8c, 0x513424b4, 0x6d54c7bc, 0x29f5e2a4, 0x159501ac, 0x98c1b695, 0xa4a1559d, 0xe0007085, 0xdc60938d, 0x69423ab5, 0x5522d9bd, 0x1183fca5, 0x2de31fad, 0x29421adb, 0x1522f9d3, 0x5183dccb, 0x6de33fc3, 0xd8c196fb, 0xe4a175f3, 0xa00050eb, 0x9c60b3e3, 0x113404da, 0x2d54e7d2, 0x69f5c2ca, 0x559521c2, 0xe0b788fa, 0xdcd76bf2, 0x98764eea, 0xa416ade2, 0x59ae26d9, 0x65cec5d1, 0x216fe0c9, 0x1d0f03c1, 0xa82daaf9, 0x944d49f1, 0xd0ec6ce9, 0xec8c8fe1, 0x61d838d8, 0x5db8dbd0, 0x1919fec8, 0x25791dc0, 0x905bb4f8, 0xac3b57f0, 0xe89a72e8, 0xd4fa91e0, 0xc89a62df, 0xf4fa81d7, 0xb05ba4cf, 0x8c3b47c7, 0x3919eeff, 0x05790df7, 0x41d828ef, 0x7db8cbe7, 0xf0ec7cde, 0xcc8c9fd6, 0x882dbace, 0xb44d59c6, 0x016ff0fe, 0x3d0f13f6, 0x79ae36ee, 0x45ced5e6, 0xb8765edd, 0x8416bdd5, 0xc0b798cd, 0xfcd77bc5, 0x49f5d2fd, 0x759531f5, 0x313414ed, 0x0d54f7e5, 0x800040dc, 0xbc60a3d4, 0xf8c186cc, 0xc4a165c4, 0x7183ccfc, 0x4de32ff4, 0x09420aec, 0x3522e9e4}, {0x00000000, 0x6307d924, 0xc60fb248, 0xa5086b6c, 0x576e62d1, 0x3469bbf5, 0x9161d099, 0xf26609bd, 0xaedcc5a2, 0xcddb1c86, 0x68d377ea, 0x0bd4aece, 0xf9b2a773, 0x9ab57e57, 0x3fbd153b, 0x5cbacc1f, 0x86c88d05, 0xe5cf5421, 0x40c73f4d, 0x23c0e669, 0xd1a6efd4, 0xb2a136f0, 0x17a95d9c, 0x74ae84b8, 0x281448a7, 0x4b139183, 0xee1bfaef, 0x8d1c23cb, 0x7f7a2a76, 0x1c7df352, 0xb975983e, 0xda72411a, 0xd6e01c4b, 0xb5e7c56f, 0x10efae03, 0x73e87727, 0x818e7e9a, 0xe289a7be, 0x4781ccd2, 0x248615f6, 0x783cd9e9, 0x1b3b00cd, 0xbe336ba1, 0xdd34b285, 0x2f52bb38, 0x4c55621c, 0xe95d0970, 0x8a5ad054, 0x5028914e, 0x332f486a, 0x96272306, 0xf520fa22, 0x0746f39f, 0x64412abb, 0xc14941d7, 0xa24e98f3, 0xfef454ec, 0x9df38dc8, 0x38fbe6a4, 0x5bfc3f80, 0xa99a363d, 0xca9def19, 0x6f958475, 0x0c925d51, 0x76b13ed7, 0x15b6e7f3, 0xb0be8c9f, 0xd3b955bb, 0x21df5c06, 0x42d88522, 0xe7d0ee4e, 0x84d7376a, 0xd86dfb75, 0xbb6a2251, 0x1e62493d, 0x7d659019, 0x8f0399a4, 0xec044080, 0x490c2bec, 0x2a0bf2c8, 0xf079b3d2, 0x937e6af6, 0x3676019a, 0x5571d8be, 0xa717d103, 0xc4100827, 0x6118634b, 0x021fba6f, 0x5ea57670, 0x3da2af54, 0x98aac438, 0xfbad1d1c, 0x09cb14a1, 0x6acccd85, 0xcfc4a6e9, 0xacc37fcd, 0xa051229c, 0xc356fbb8, 0x665e90d4, 0x055949f0, 0xf73f404d, 0x94389969, 0x3130f205, 0x52372b21, 0x0e8de73e, 0x6d8a3e1a, 0xc8825576, 0xab858c52, 0x59e385ef, 0x3ae45ccb, 0x9fec37a7, 0xfcebee83, 0x2699af99, 0x459e76bd, 0xe0961dd1, 0x8391c4f5, 0x71f7cd48, 0x12f0146c, 0xb7f87f00, 0xd4ffa624, 0x88456a3b, 0xeb42b31f, 0x4e4ad873, 0x2d4d0157, 0xdf2b08ea, 0xbc2cd1ce, 0x1924baa2, 0x7a236386, 0xed627dae, 0x8e65a48a, 0x2b6dcfe6, 0x486a16c2, 0xba0c1f7f, 0xd90bc65b, 0x7c03ad37, 0x1f047413, 0x43beb80c, 0x20b96128, 0x85b10a44, 0xe6b6d360, 0x14d0dadd, 0x77d703f9, 0xd2df6895, 0xb1d8b1b1, 0x6baaf0ab, 0x08ad298f, 0xada542e3, 0xcea29bc7, 0x3cc4927a, 0x5fc34b5e, 0xfacb2032, 0x99ccf916, 0xc5763509, 0xa671ec2d, 0x03798741, 0x607e5e65, 0x921857d8, 0xf11f8efc, 0x5417e590, 0x37103cb4, 0x3b8261e5, 0x5885b8c1, 0xfd8dd3ad, 0x9e8a0a89, 0x6cec0334, 0x0febda10, 0xaae3b17c, 0xc9e46858, 0x955ea447, 0xf6597d63, 0x5351160f, 0x3056cf2b, 0xc230c696, 0xa1371fb2, 0x043f74de, 0x6738adfa, 0xbd4aece0, 0xde4d35c4, 0x7b455ea8, 0x1842878c, 0xea248e31, 0x89235715, 0x2c2b3c79, 0x4f2ce55d, 0x13962942, 0x7091f066, 0xd5999b0a, 0xb69e422e, 0x44f84b93, 0x27ff92b7, 0x82f7f9db, 0xe1f020ff, 0x9bd34379, 0xf8d49a5d, 0x5ddcf131, 0x3edb2815, 0xccbd21a8, 0xafbaf88c, 0x0ab293e0, 0x69b54ac4, 0x350f86db, 0x56085fff, 0xf3003493, 0x9007edb7, 0x6261e40a, 0x01663d2e, 0xa46e5642, 0xc7698f66, 0x1d1bce7c, 0x7e1c1758, 0xdb147c34, 0xb813a510, 0x4a75acad, 0x29727589, 0x8c7a1ee5, 0xef7dc7c1, 0xb3c70bde, 0xd0c0d2fa, 0x75c8b996, 0x16cf60b2, 0xe4a9690f, 0x87aeb02b, 0x22a6db47, 0x41a10263, 0x4d335f32, 0x2e348616, 0x8b3ced7a, 0xe83b345e, 0x1a5d3de3, 0x795ae4c7, 0xdc528fab, 0xbf55568f, 0xe3ef9a90, 0x80e843b4, 0x25e028d8, 0x46e7f1fc, 0xb481f841, 0xd7862165, 0x728e4a09, 0x1189932d, 0xcbfbd237, 0xa8fc0b13, 0x0df4607f, 0x6ef3b95b, 0x9c95b0e6, 0xff9269c2, 0x5a9a02ae, 0x399ddb8a, 0x65271795, 0x0620ceb1, 0xa328a5dd, 0xc02f7cf9, 0x32497544, 0x514eac60, 0xf446c70c, 0x97411e28}, {0x00000000, 0x01b5fd1d, 0x036bfa3a, 0x02de0727, 0x06d7f474, 0x07620969, 0x05bc0e4e, 0x0409f353, 0x0dafe8e8, 0x0c1a15f5, 0x0ec412d2, 0x0f71efcf, 0x0b781c9c, 0x0acde181, 0x0813e6a6, 0x09a61bbb, 0x1b5fd1d0, 0x1aea2ccd, 0x18342bea, 0x1981d6f7, 0x1d8825a4, 0x1c3dd8b9, 0x1ee3df9e, 0x1f562283, 0x16f03938, 0x1745c425, 0x159bc302, 0x142e3e1f, 0x1027cd4c, 0x11923051, 0x134c3776, 0x12f9ca6b, 0x36bfa3a0, 0x370a5ebd, 0x35d4599a, 0x3461a487, 0x306857d4, 0x31ddaac9, 0x3303adee, 0x32b650f3, 0x3b104b48, 0x3aa5b655, 0x387bb172, 0x39ce4c6f, 0x3dc7bf3c, 0x3c724221, 0x3eac4506, 0x3f19b81b, 0x2de07270, 0x2c558f6d, 0x2e8b884a, 0x2f3e7557, 0x2b378604, 0x2a827b19, 0x285c7c3e, 0x29e98123, 0x204f9a98, 0x21fa6785, 0x232460a2, 0x22919dbf, 0x26986eec, 0x272d93f1, 0x25f394d6, 0x244669cb, 0x6d7f4740, 0x6ccaba5d, 0x6e14bd7a, 0x6fa14067, 0x6ba8b334, 0x6a1d4e29, 0x68c3490e, 0x6976b413, 0x60d0afa8, 0x616552b5, 0x63bb5592, 0x620ea88f, 0x66075bdc, 0x67b2a6c1, 0x656ca1e6, 0x64d95cfb, 0x76209690, 0x77956b8d, 0x754b6caa, 0x74fe91b7, 0x70f762e4, 0x71429ff9, 0x739c98de, 0x722965c3, 0x7b8f7e78, 0x7a3a8365, 0x78e48442, 0x7951795f, 0x7d588a0c, 0x7ced7711, 0x7e337036, 0x7f868d2b, 0x5bc0e4e0, 0x5a7519fd, 0x58ab1eda, 0x591ee3c7, 0x5d171094, 0x5ca2ed89, 0x5e7ceaae, 0x5fc917b3, 0x566f0c08, 0x57daf115, 0x5504f632, 0x54b10b2f, 0x50b8f87c, 0x510d0561, 0x53d30246, 0x5266ff5b, 0x409f3530, 0x412ac82d, 0x43f4cf0a, 0x42413217, 0x4648c144, 0x47fd3c59, 0x45233b7e, 0x4496c663, 0x4d30ddd8, 0x4c8520c5, 0x4e5b27e2, 0x4feedaff, 0x4be729ac, 0x4a52d4b1, 0x488cd396, 0x49392e8b, 0xdafe8e80, 0xdb4b739d, 0xd99574ba, 0xd82089a7, 0xdc297af4, 0xdd9c87e9, 0xdf4280ce, 0xdef77dd3, 0xd7516668, 0xd6e49b75, 0xd43a9c52, 0xd58f614f, 0xd186921c, 0xd0336f01, 0xd2ed6826, 0xd358953b, 0xc1a15f50, 0xc014a24d, 0xc2caa56a, 0xc37f5877, 0xc776ab24, 0xc6c35639, 0xc41d511e, 0xc5a8ac03, 0xcc0eb7b8, 0xcdbb4aa5, 0xcf654d82, 0xced0b09f, 0xcad943cc, 0xcb6cbed1, 0xc9b2b9f6, 0xc80744eb, 0xec412d20, 0xedf4d03d, 0xef2ad71a, 0xee9f2a07, 0xea96d954, 0xeb232449, 0xe9fd236e, 0xe848de73, 0xe1eec5c8, 0xe05b38d5, 0xe2853ff2, 0xe330c2ef, 0xe73931bc, 0xe68ccca1, 0xe452cb86, 0xe5e7369b, 0xf71efcf0, 0xf6ab01ed, 0xf47506ca, 0xf5c0fbd7, 0xf1c90884, 0xf07cf599, 0xf2a2f2be, 0xf3170fa3, 0xfab11418, 0xfb04e905, 0xf9daee22, 0xf86f133f, 0xfc66e06c, 0xfdd31d71, 0xff0d1a56, 0xfeb8e74b, 0xb781c9c0, 0xb63434dd, 0xb4ea33fa, 0xb55fcee7, 0xb1563db4, 0xb0e3c0a9, 0xb23dc78e, 0xb3883a93, 0xba2e2128, 0xbb9bdc35, 0xb945db12, 0xb8f0260f, 0xbcf9d55c, 0xbd4c2841, 0xbf922f66, 0xbe27d27b, 0xacde1810, 0xad6be50d, 0xafb5e22a, 0xae001f37, 0xaa09ec64, 0xabbc1179, 0xa962165e, 0xa8d7eb43, 0xa171f0f8, 0xa0c40de5, 0xa21a0ac2, 0xa3aff7df, 0xa7a6048c, 0xa613f991, 0xa4cdfeb6, 0xa57803ab, 0x813e6a60, 0x808b977d, 0x8255905a, 0x83e06d47, 0x87e99e14, 0x865c6309, 0x8482642e, 0x85379933, 0x8c918288, 0x8d247f95, 0x8ffa78b2, 0x8e4f85af, 0x8a4676fc, 0x8bf38be1, 0x892d8cc6, 0x889871db, 0x9a61bbb0, 0x9bd446ad, 0x990a418a, 0x98bfbc97, 0x9cb64fc4, 0x9d03b2d9, 0x9fddb5fe, 0x9e6848e3, 0x97ce5358, 0x967bae45, 0x94a5a962, 0x9510547f, 0x9119a72c, 0x90ac5a31, 0x92725d16, 0x93c7a00b}, {0x00000000, 0x6e8c1b41, 0xdd183682, 0xb3942dc3, 0x61416b45, 0x0fcd7004, 0xbc595dc7, 0xd2d54686, 0xc282d68a, 0xac0ecdcb, 0x1f9ae008, 0x7116fb49, 0xa3c3bdcf, 0xcd4fa68e, 0x7edb8b4d, 0x1057900c, 0x5e74ab55, 0x30f8b014, 0x836c9dd7, 0xede08696, 0x3f35c010, 0x51b9db51, 0xe22df692, 0x8ca1edd3, 0x9cf67ddf, 0xf27a669e, 0x41ee4b5d, 0x2f62501c, 0xfdb7169a, 0x933b0ddb, 0x20af2018, 0x4e233b59, 0xbce956aa, 0xd2654deb, 0x61f16028, 0x0f7d7b69, 0xdda83def, 0xb32426ae, 0x00b00b6d, 0x6e3c102c, 0x7e6b8020, 0x10e79b61, 0xa373b6a2, 0xcdffade3, 0x1f2aeb65, 0x71a6f024, 0xc232dde7, 0xacbec6a6, 0xe29dfdff, 0x8c11e6be, 0x3f85cb7d, 0x5109d03c, 0x83dc96ba, 0xed508dfb, 0x5ec4a038, 0x3048bb79, 0x201f2b75, 0x4e933034, 0xfd071df7, 0x938b06b6, 0x415e4030, 0x2fd25b71, 0x9c4676b2, 0xf2ca6df3, 0xa2a3ab15, 0xcc2fb054, 0x7fbb9d97, 0x113786d6, 0xc3e2c050, 0xad6edb11, 0x1efaf6d2, 0x7076ed93, 0x60217d9f, 0x0ead66de, 0xbd394b1d, 0xd3b5505c, 0x016016da, 0x6fec0d9b, 0xdc782058, 0xb2f43b19, 0xfcd70040, 0x925b1b01, 0x21cf36c2, 0x4f432d83, 0x9d966b05, 0xf31a7044, 0x408e5d87, 0x2e0246c6, 0x3e55d6ca, 0x50d9cd8b, 0xe34de048, 0x8dc1fb09, 0x5f14bd8f, 0x3198a6ce, 0x820c8b0d, 0xec80904c, 0x1e4afdbf, 0x70c6e6fe, 0xc352cb3d, 0xadded07c, 0x7f0b96fa, 0x11878dbb, 0xa213a078, 0xcc9fbb39, 0xdcc82b35, 0xb2443074, 0x01d01db7, 0x6f5c06f6, 0xbd894070, 0xd3055b31, 0x609176f2, 0x0e1d6db3, 0x403e56ea, 0x2eb24dab, 0x9d266068, 0xf3aa7b29, 0x217f3daf, 0x4ff326ee, 0xfc670b2d, 0x92eb106c, 0x82bc8060, 0xec309b21, 0x5fa4b6e2, 0x3128ada3, 0xe3fdeb25, 0x8d71f064, 0x3ee5dda7, 0x5069c6e6, 0x9e36506b, 0xf0ba4b2a, 0x432e66e9, 0x2da27da8, 0xff773b2e, 0x91fb206f, 0x226f0dac, 0x4ce316ed, 0x5cb486e1, 0x32389da0, 0x81acb063, 0xef20ab22, 0x3df5eda4, 0x5379f6e5, 0xe0eddb26, 0x8e61c067, 0xc042fb3e, 0xaecee07f, 0x1d5acdbc, 0x73d6d6fd, 0xa103907b, 0xcf8f8b3a, 0x7c1ba6f9, 0x1297bdb8, 0x02c02db4, 0x6c4c36f5, 0xdfd81b36, 0xb1540077, 0x638146f1, 0x0d0d5db0, 0xbe997073, 0xd0156b32, 0x22df06c1, 0x4c531d80, 0xffc73043, 0x914b2b02, 0x439e6d84, 0x2d1276c5, 0x9e865b06, 0xf00a4047, 0xe05dd04b, 0x8ed1cb0a, 0x3d45e6c9, 0x53c9fd88, 0x811cbb0e, 0xef90a04f, 0x5c048d8c, 0x328896cd, 0x7cabad94, 0x1227b6d5, 0xa1b39b16, 0xcf3f8057, 0x1deac6d1, 0x7366dd90, 0xc0f2f053, 0xae7eeb12, 0xbe297b1e, 0xd0a5605f, 0x63314d9c, 0x0dbd56dd, 0xdf68105b, 0xb1e40b1a, 0x027026d9, 0x6cfc3d98, 0x3c95fb7e, 0x5219e03f, 0xe18dcdfc, 0x8f01d6bd, 0x5dd4903b, 0x33588b7a, 0x80cca6b9, 0xee40bdf8, 0xfe172df4, 0x909b36b5, 0x230f1b76, 0x4d830037, 0x9f5646b1, 0xf1da5df0, 0x424e7033, 0x2cc26b72, 0x62e1502b, 0x0c6d4b6a, 0xbff966a9, 0xd1757de8, 0x03a03b6e, 0x6d2c202f, 0xdeb80dec, 0xb03416ad, 0xa06386a1, 0xceef9de0, 0x7d7bb023, 0x13f7ab62, 0xc122ede4, 0xafaef6a5, 0x1c3adb66, 0x72b6c027, 0x807cadd4, 0xeef0b695, 0x5d649b56, 0x33e88017, 0xe13dc691, 0x8fb1ddd0, 0x3c25f013, 0x52a9eb52, 0x42fe7b5e, 0x2c72601f, 0x9fe64ddc, 0xf16a569d, 0x23bf101b, 0x4d330b5a, 0xfea72699, 0x902b3dd8, 0xde080681, 0xb0841dc0, 0x03103003, 0x6d9c2b42, 0xbf496dc4, 0xd1c57685, 0x62515b46, 0x0cdd4007, 0x1c8ad00b, 0x7206cb4a, 0xc192e689, 0xaf1efdc8, 0x7dcbbb4e, 0x1347a00f, 0xa0d38dcc, 0xce5f968d}, {0x00000000, 0xe71da697, 0x154a4b6f, 0xf257edf8, 0x2a9496de, 0xcd893049, 0x3fdeddb1, 0xd8c37b26, 0x55292dbc, 0xb2348b2b, 0x406366d3, 0xa77ec044, 0x7fbdbb62, 0x98a01df5, 0x6af7f00d, 0x8dea569a, 0xaa525b78, 0x4d4ffdef, 0xbf181017, 0x5805b680, 0x80c6cda6, 0x67db6b31, 0x958c86c9, 0x7291205e, 0xff7b76c4, 0x1866d053, 0xea313dab, 0x0d2c9b3c, 0xd5efe01a, 0x32f2468d, 0xc0a5ab75, 0x27b80de2, 0x8fd5b0b1, 0x68c81626, 0x9a9ffbde, 0x7d825d49, 0xa541266f, 0x425c80f8, 0xb00b6d00, 0x5716cb97, 0xdafc9d0d, 0x3de13b9a, 0xcfb6d662, 0x28ab70f5, 0xf0680bd3, 0x1775ad44, 0xe52240bc, 0x023fe62b, 0x2587ebc9, 0xc29a4d5e, 0x30cda0a6, 0xd7d00631, 0x0f137d17, 0xe80edb80, 0x1a593678, 0xfd4490ef, 0x70aec675, 0x97b360e2, 0x65e48d1a, 0x82f92b8d, 0x5a3a50ab, 0xbd27f63c, 0x4f701bc4, 0xa86dbd53, 0xc4da6723, 0x23c7c1b4, 0xd1902c4c, 0x368d8adb, 0xee4ef1fd, 0x0953576a, 0xfb04ba92, 0x1c191c05, 0x91f34a9f, 0x76eeec08, 0x84b901f0, 0x63a4a767, 0xbb67dc41, 0x5c7a7ad6, 0xae2d972e, 0x493031b9, 0x6e883c5b, 0x89959acc, 0x7bc27734, 0x9cdfd1a3, 0x441caa85, 0xa3010c12, 0x5156e1ea, 0xb64b477d, 0x3ba111e7, 0xdcbcb770, 0x2eeb5a88, 0xc9f6fc1f, 0x11358739, 0xf62821ae, 0x047fcc56, 0xe3626ac1, 0x4b0fd792, 0xac127105, 0x5e459cfd, 0xb9583a6a, 0x619b414c, 0x8686e7db, 0x74d10a23, 0x93ccacb4, 0x1e26fa2e, 0xf93b5cb9, 0x0b6cb141, 0xec7117d6, 0x34b26cf0, 0xd3afca67, 0x21f8279f, 0xc6e58108, 0xe15d8cea, 0x06402a7d, 0xf417c785, 0x130a6112, 0xcbc91a34, 0x2cd4bca3, 0xde83515b, 0x399ef7cc, 0xb474a156, 0x536907c1, 0xa13eea39, 0x46234cae, 0x9ee03788, 0x79fd911f, 0x8baa7ce7, 0x6cb7da70, 0x52c5c807, 0xb5d86e90, 0x478f8368, 0xa09225ff, 0x78515ed9, 0x9f4cf84e, 0x6d1b15b6, 0x8a06b321, 0x07ece5bb, 0xe0f1432c, 0x12a6aed4, 0xf5bb0843, 0x2d787365, 0xca65d5f2, 0x3832380a, 0xdf2f9e9d, 0xf897937f, 0x1f8a35e8, 0xedddd810, 0x0ac07e87, 0xd20305a1, 0x351ea336, 0xc7494ece, 0x2054e859, 0xadbebec3, 0x4aa31854, 0xb8f4f5ac, 0x5fe9533b, 0x872a281d, 0x60378e8a, 0x92606372, 0x757dc5e5, 0xdd1078b6, 0x3a0dde21, 0xc85a33d9, 0x2f47954e, 0xf784ee68, 0x109948ff, 0xe2cea507, 0x05d30390, 0x8839550a, 0x6f24f39d, 0x9d731e65, 0x7a6eb8f2, 0xa2adc3d4, 0x45b06543, 0xb7e788bb, 0x50fa2e2c, 0x774223ce, 0x905f8559, 0x620868a1, 0x8515ce36, 0x5dd6b510, 0xbacb1387, 0x489cfe7f, 0xaf8158e8, 0x226b0e72, 0xc576a8e5, 0x3721451d, 0xd03ce38a, 0x08ff98ac, 0xefe23e3b, 0x1db5d3c3, 0xfaa87554, 0x961faf24, 0x710209b3, 0x8355e44b, 0x644842dc, 0xbc8b39fa, 0x5b969f6d, 0xa9c17295, 0x4edcd402, 0xc3368298, 0x242b240f, 0xd67cc9f7, 0x31616f60, 0xe9a21446, 0x0ebfb2d1, 0xfce85f29, 0x1bf5f9be, 0x3c4df45c, 0xdb5052cb, 0x2907bf33, 0xce1a19a4, 0x16d96282, 0xf1c4c415, 0x039329ed, 0xe48e8f7a, 0x6964d9e0, 0x8e797f77, 0x7c2e928f, 0x9b333418, 0x43f04f3e, 0xa4ede9a9, 0x56ba0451, 0xb1a7a2c6, 0x19ca1f95, 0xfed7b902, 0x0c8054fa, 0xeb9df26d, 0x335e894b, 0xd4432fdc, 0x2614c224, 0xc10964b3, 0x4ce33229, 0xabfe94be, 0x59a97946, 0xbeb4dfd1, 0x6677a4f7, 0x816a0260, 0x733def98, 0x9420490f, 0xb39844ed, 0x5485e27a, 0xa6d20f82, 0x41cfa915, 0x990cd233, 0x7e1174a4, 0x8c46995c, 0x6b5b3fcb, 0xe6b16951, 0x01accfc6, 0xf3fb223e, 0x14e684a9, 0xcc25ff8f, 0x2b385918, 0xd96fb4e0, 0x3e721277}, {0x00000000, 0xa58b900e, 0x9066265d, 0x35edb653, 0xfbbd4afb, 0x5e36daf5, 0x6bdb6ca6, 0xce50fca8, 0x2c0b93b7, 0x898003b9, 0xbc6db5ea, 0x19e625e4, 0xd7b6d94c, 0x723d4942, 0x47d0ff11, 0xe25b6f1f, 0x5817276e, 0xfd9cb760, 0xc8710133, 0x6dfa913d, 0xa3aa6d95, 0x0621fd9b, 0x33cc4bc8, 0x9647dbc6, 0x741cb4d9, 0xd19724d7, 0xe47a9284, 0x41f1028a, 0x8fa1fe22, 0x2a2a6e2c, 0x1fc7d87f, 0xba4c4871, 0xb02e4edc, 0x15a5ded2, 0x20486881, 0x85c3f88f, 0x4b930427, 0xee189429, 0xdbf5227a, 0x7e7eb274, 0x9c25dd6b, 0x39ae4d65, 0x0c43fb36, 0xa9c86b38, 0x67989790, 0xc213079e, 0xf7feb1cd, 0x527521c3, 0xe83969b2, 0x4db2f9bc, 0x785f4fef, 0xddd4dfe1, 0x13842349, 0xb60fb347, 0x83e20514, 0x2669951a, 0xc432fa05, 0x61b96a0b, 0x5454dc58, 0xf1df4c56, 0x3f8fb0fe, 0x9a0420f0, 0xafe996a3, 0x0a6206ad, 0xbb2d9bf9, 0x1ea60bf7, 0x2b4bbda4, 0x8ec02daa, 0x4090d102, 0xe51b410c, 0xd0f6f75f, 0x757d6751, 0x9726084e, 0x32ad9840, 0x07402e13, 0xa2cbbe1d, 0x6c9b42b5, 0xc910d2bb, 0xfcfd64e8, 0x5976f4e6, 0xe33abc97, 0x46b12c99, 0x735c9aca, 0xd6d70ac4, 0x1887f66c, 0xbd0c6662, 0x88e1d031, 0x2d6a403f, 0xcf312f20, 0x6ababf2e, 0x5f57097d, 0xfadc9973, 0x348c65db, 0x9107f5d5, 0xa4ea4386, 0x0161d388, 0x0b03d525, 0xae88452b, 0x9b65f378, 0x3eee6376, 0xf0be9fde, 0x55350fd0, 0x60d8b983, 0xc553298d, 0x27084692, 0x8283d69c, 0xb76e60cf, 0x12e5f0c1, 0xdcb50c69, 0x793e9c67, 0x4cd32a34, 0xe958ba3a, 0x5314f24b, 0xf69f6245, 0xc372d416, 0x66f94418, 0xa8a9b8b0, 0x0d2228be, 0x38cf9eed, 0x9d440ee3, 0x7f1f61fc, 0xda94f1f2, 0xef7947a1, 0x4af2d7af, 0x84a22b07, 0x2129bb09, 0x14c40d5a, 0xb14f9d54, 0xad2a31b3, 0x08a1a1bd, 0x3d4c17ee, 0x98c787e0, 0x56977b48, 0xf31ceb46, 0xc6f15d15, 0x637acd1b, 0x8121a204, 0x24aa320a, 0x11478459, 0xb4cc1457, 0x7a9ce8ff, 0xdf1778f1, 0xeafacea2, 0x4f715eac, 0xf53d16dd, 0x50b686d3, 0x655b3080, 0xc0d0a08e, 0x0e805c26, 0xab0bcc28, 0x9ee67a7b, 0x3b6dea75, 0xd936856a, 0x7cbd1564, 0x4950a337, 0xecdb3339, 0x228bcf91, 0x87005f9f, 0xb2ede9cc, 0x176679c2, 0x1d047f6f, 0xb88fef61, 0x8d625932, 0x28e9c93c, 0xe6b93594, 0x4332a59a, 0x76df13c9, 0xd35483c7, 0x310fecd8, 0x94847cd6, 0xa169ca85, 0x04e25a8b, 0xcab2a623, 0x6f39362d, 0x5ad4807e, 0xff5f1070, 0x45135801, 0xe098c80f, 0xd5757e5c, 0x70feee52, 0xbeae12fa, 0x1b2582f4, 0x2ec834a7, 0x8b43a4a9, 0x6918cbb6, 0xcc935bb8, 0xf97eedeb, 0x5cf57de5, 0x92a5814d, 0x372e1143, 0x02c3a710, 0xa748371e, 0x1607aa4a, 0xb38c3a44, 0x86618c17, 0x23ea1c19, 0xedbae0b1, 0x483170bf, 0x7ddcc6ec, 0xd85756e2, 0x3a0c39fd, 0x9f87a9f3, 0xaa6a1fa0, 0x0fe18fae, 0xc1b17306, 0x643ae308, 0x51d7555b, 0xf45cc555, 0x4e108d24, 0xeb9b1d2a, 0xde76ab79, 0x7bfd3b77, 0xb5adc7df, 0x102657d1, 0x25cbe182, 0x8040718c, 0x621b1e93, 0xc7908e9d, 0xf27d38ce, 0x57f6a8c0, 0x99a65468, 0x3c2dc466, 0x09c07235, 0xac4be23b, 0xa629e496, 0x03a27498, 0x364fc2cb, 0x93c452c5, 0x5d94ae6d, 0xf81f3e63, 0xcdf28830, 0x6879183e, 0x8a227721, 0x2fa9e72f, 0x1a44517c, 0xbfcfc172, 0x719f3dda, 0xd414add4, 0xe1f91b87, 0x44728b89, 0xfe3ec3f8, 0x5bb553f6, 0x6e58e5a5, 0xcbd375ab, 0x05838903, 0xa008190d, 0x95e5af5e, 0x306e3f50, 0xd235504f, 0x77bec041, 0x42537612, 0xe7d8e61c, 0x29881ab4, 0x8c038aba, 0xb9ee3ce9, 0x1c65ace7}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x0000000000000000, 0x0e908ba500000000, 0x5d26669000000000, 0x53b6ed3500000000, 0xfb4abdfb00000000, 0xf5da365e00000000, 0xa66cdb6b00000000, 0xa8fc50ce00000000, 0xb7930b2c00000000, 0xb903808900000000, 0xeab56dbc00000000, 0xe425e61900000000, 0x4cd9b6d700000000, 0x42493d7200000000, 0x11ffd04700000000, 0x1f6f5be200000000, 0x6e27175800000000, 0x60b79cfd00000000, 0x330171c800000000, 0x3d91fa6d00000000, 0x956daaa300000000, 0x9bfd210600000000, 0xc84bcc3300000000, 0xc6db479600000000, 0xd9b41c7400000000, 0xd72497d100000000, 0x84927ae400000000, 0x8a02f14100000000, 0x22fea18f00000000, 0x2c6e2a2a00000000, 0x7fd8c71f00000000, 0x71484cba00000000, 0xdc4e2eb000000000, 0xd2dea51500000000, 0x8168482000000000, 0x8ff8c38500000000, 0x2704934b00000000, 0x299418ee00000000, 0x7a22f5db00000000, 0x74b27e7e00000000, 0x6bdd259c00000000, 0x654dae3900000000, 0x36fb430c00000000, 0x386bc8a900000000, 0x9097986700000000, 0x9e0713c200000000, 0xcdb1fef700000000, 0xc321755200000000, 0xb26939e800000000, 0xbcf9b24d00000000, 0xef4f5f7800000000, 0xe1dfd4dd00000000, 0x4923841300000000, 0x47b30fb600000000, 0x1405e28300000000, 0x1a95692600000000, 0x05fa32c400000000, 0x0b6ab96100000000, 0x58dc545400000000, 0x564cdff100000000, 0xfeb08f3f00000000, 0xf020049a00000000, 0xa396e9af00000000, 0xad06620a00000000, 0xf99b2dbb00000000, 0xf70ba61e00000000, 0xa4bd4b2b00000000, 0xaa2dc08e00000000, 0x02d1904000000000, 0x0c411be500000000, 0x5ff7f6d000000000, 0x51677d7500000000, 0x4e08269700000000, 0x4098ad3200000000, 0x132e400700000000, 0x1dbecba200000000, 0xb5429b6c00000000, 0xbbd210c900000000, 0xe864fdfc00000000, 0xe6f4765900000000, 0x97bc3ae300000000, 0x992cb14600000000, 0xca9a5c7300000000, 0xc40ad7d600000000, 0x6cf6871800000000, 0x62660cbd00000000, 0x31d0e18800000000, 0x3f406a2d00000000, 0x202f31cf00000000, 0x2ebfba6a00000000, 0x7d09575f00000000, 0x7399dcfa00000000, 0xdb658c3400000000, 0xd5f5079100000000, 0x8643eaa400000000, 0x88d3610100000000, 0x25d5030b00000000, 0x2b4588ae00000000, 0x78f3659b00000000, 0x7663ee3e00000000, 0xde9fbef000000000, 0xd00f355500000000, 0x83b9d86000000000, 0x8d2953c500000000, 0x9246082700000000, 0x9cd6838200000000, 0xcf606eb700000000, 0xc1f0e51200000000, 0x690cb5dc00000000, 0x679c3e7900000000, 0x342ad34c00000000, 0x3aba58e900000000, 0x4bf2145300000000, 0x45629ff600000000, 0x16d472c300000000, 0x1844f96600000000, 0xb0b8a9a800000000, 0xbe28220d00000000, 0xed9ecf3800000000, 0xe30e449d00000000, 0xfc611f7f00000000, 0xf2f194da00000000, 0xa14779ef00000000, 0xafd7f24a00000000, 0x072ba28400000000, 0x09bb292100000000, 0x5a0dc41400000000, 0x549d4fb100000000, 0xb3312aad00000000, 0xbda1a10800000000, 0xee174c3d00000000, 0xe087c79800000000, 0x487b975600000000, 0x46eb1cf300000000, 0x155df1c600000000, 0x1bcd7a6300000000, 0x04a2218100000000, 0x0a32aa2400000000, 0x5984471100000000, 0x5714ccb400000000, 0xffe89c7a00000000, 0xf17817df00000000, 0xa2cefaea00000000, 0xac5e714f00000000, 0xdd163df500000000, 0xd386b65000000000, 0x80305b6500000000, 0x8ea0d0c000000000, 0x265c800e00000000, 0x28cc0bab00000000, 0x7b7ae69e00000000, 0x75ea6d3b00000000, 0x6a8536d900000000, 0x6415bd7c00000000, 0x37a3504900000000, 0x3933dbec00000000, 0x91cf8b2200000000, 0x9f5f008700000000, 0xcce9edb200000000, 0xc279661700000000, 0x6f7f041d00000000, 0x61ef8fb800000000, 0x3259628d00000000, 0x3cc9e92800000000, 0x9435b9e600000000, 0x9aa5324300000000, 0xc913df7600000000, 0xc78354d300000000, 0xd8ec0f3100000000, 0xd67c849400000000, 0x85ca69a100000000, 0x8b5ae20400000000, 0x23a6b2ca00000000, 0x2d36396f00000000, 0x7e80d45a00000000, 0x70105fff00000000, 0x0158134500000000, 0x0fc898e000000000, 0x5c7e75d500000000, 0x52eefe7000000000, 0xfa12aebe00000000, 0xf482251b00000000, 0xa734c82e00000000, 0xa9a4438b00000000, 0xb6cb186900000000, 0xb85b93cc00000000, 0xebed7ef900000000, 0xe57df55c00000000, 0x4d81a59200000000, 0x43112e3700000000, 0x10a7c30200000000, 0x1e3748a700000000, 0x4aaa071600000000, 0x443a8cb300000000, 0x178c618600000000, 0x191cea2300000000, 0xb1e0baed00000000, 0xbf70314800000000, 0xecc6dc7d00000000, 0xe25657d800000000, 0xfd390c3a00000000, 0xf3a9879f00000000, 0xa01f6aaa00000000, 0xae8fe10f00000000, 0x0673b1c100000000, 0x08e33a6400000000, 0x5b55d75100000000, 0x55c55cf400000000, 0x248d104e00000000, 0x2a1d9beb00000000, 0x79ab76de00000000, 0x773bfd7b00000000, 0xdfc7adb500000000, 0xd157261000000000, 0x82e1cb2500000000, 0x8c71408000000000, 0x931e1b6200000000, 0x9d8e90c700000000, 0xce387df200000000, 0xc0a8f65700000000, 0x6854a69900000000, 0x66c42d3c00000000, 0x3572c00900000000, 0x3be24bac00000000, 0x96e429a600000000, 0x9874a20300000000, 0xcbc24f3600000000, 0xc552c49300000000, 0x6dae945d00000000, 0x633e1ff800000000, 0x3088f2cd00000000, 0x3e18796800000000, 0x2177228a00000000, 0x2fe7a92f00000000, 0x7c51441a00000000, 0x72c1cfbf00000000, 0xda3d9f7100000000, 0xd4ad14d400000000, 0x871bf9e100000000, 0x898b724400000000, 0xf8c33efe00000000, 0xf653b55b00000000, 0xa5e5586e00000000, 0xab75d3cb00000000, 0x0389830500000000, 0x0d1908a000000000, 0x5eafe59500000000, 0x503f6e3000000000, 0x4f5035d200000000, 0x41c0be7700000000, 0x1276534200000000, 0x1ce6d8e700000000, 0xb41a882900000000, 0xba8a038c00000000, 0xe93ceeb900000000, 0xe7ac651c00000000}, {0x0000000000000000, 0x97a61de700000000, 0x6f4b4a1500000000, 0xf8ed57f200000000, 0xde96942a00000000, 0x493089cd00000000, 0xb1ddde3f00000000, 0x267bc3d800000000, 0xbc2d295500000000, 0x2b8b34b200000000, 0xd366634000000000, 0x44c07ea700000000, 0x62bbbd7f00000000, 0xf51da09800000000, 0x0df0f76a00000000, 0x9a56ea8d00000000, 0x785b52aa00000000, 0xeffd4f4d00000000, 0x171018bf00000000, 0x80b6055800000000, 0xa6cdc68000000000, 0x316bdb6700000000, 0xc9868c9500000000, 0x5e20917200000000, 0xc4767bff00000000, 0x53d0661800000000, 0xab3d31ea00000000, 0x3c9b2c0d00000000, 0x1ae0efd500000000, 0x8d46f23200000000, 0x75aba5c000000000, 0xe20db82700000000, 0xb1b0d58f00000000, 0x2616c86800000000, 0xdefb9f9a00000000, 0x495d827d00000000, 0x6f2641a500000000, 0xf8805c4200000000, 0x006d0bb000000000, 0x97cb165700000000, 0x0d9dfcda00000000, 0x9a3be13d00000000, 0x62d6b6cf00000000, 0xf570ab2800000000, 0xd30b68f000000000, 0x44ad751700000000, 0xbc4022e500000000, 0x2be63f0200000000, 0xc9eb872500000000, 0x5e4d9ac200000000, 0xa6a0cd3000000000, 0x3106d0d700000000, 0x177d130f00000000, 0x80db0ee800000000, 0x7836591a00000000, 0xef9044fd00000000, 0x75c6ae7000000000, 0xe260b39700000000, 0x1a8de46500000000, 0x8d2bf98200000000, 0xab503a5a00000000, 0x3cf627bd00000000, 0xc41b704f00000000, 0x53bd6da800000000, 0x2367dac400000000, 0xb4c1c72300000000, 0x4c2c90d100000000, 0xdb8a8d3600000000, 0xfdf14eee00000000, 0x6a57530900000000, 0x92ba04fb00000000, 0x051c191c00000000, 0x9f4af39100000000, 0x08ecee7600000000, 0xf001b98400000000, 0x67a7a46300000000, 0x41dc67bb00000000, 0xd67a7a5c00000000, 0x2e972dae00000000, 0xb931304900000000, 0x5b3c886e00000000, 0xcc9a958900000000, 0x3477c27b00000000, 0xa3d1df9c00000000, 0x85aa1c4400000000, 0x120c01a300000000, 0xeae1565100000000, 0x7d474bb600000000, 0xe711a13b00000000, 0x70b7bcdc00000000, 0x885aeb2e00000000, 0x1ffcf6c900000000, 0x3987351100000000, 0xae2128f600000000, 0x56cc7f0400000000, 0xc16a62e300000000, 0x92d70f4b00000000, 0x057112ac00000000, 0xfd9c455e00000000, 0x6a3a58b900000000, 0x4c419b6100000000, 0xdbe7868600000000, 0x230ad17400000000, 0xb4accc9300000000, 0x2efa261e00000000, 0xb95c3bf900000000, 0x41b16c0b00000000, 0xd61771ec00000000, 0xf06cb23400000000, 0x67caafd300000000, 0x9f27f82100000000, 0x0881e5c600000000, 0xea8c5de100000000, 0x7d2a400600000000, 0x85c717f400000000, 0x12610a1300000000, 0x341ac9cb00000000, 0xa3bcd42c00000000, 0x5b5183de00000000, 0xccf79e3900000000, 0x56a174b400000000, 0xc107695300000000, 0x39ea3ea100000000, 0xae4c234600000000, 0x8837e09e00000000, 0x1f91fd7900000000, 0xe77caa8b00000000, 0x70dab76c00000000, 0x07c8c55200000000, 0x906ed8b500000000, 0x68838f4700000000, 0xff2592a000000000, 0xd95e517800000000, 0x4ef84c9f00000000, 0xb6151b6d00000000, 0x21b3068a00000000, 0xbbe5ec0700000000, 0x2c43f1e000000000, 0xd4aea61200000000, 0x4308bbf500000000, 0x6573782d00000000, 0xf2d565ca00000000, 0x0a38323800000000, 0x9d9e2fdf00000000, 0x7f9397f800000000, 0xe8358a1f00000000, 0x10d8dded00000000, 0x877ec00a00000000, 0xa10503d200000000, 0x36a31e3500000000, 0xce4e49c700000000, 0x59e8542000000000, 0xc3bebead00000000, 0x5418a34a00000000, 0xacf5f4b800000000, 0x3b53e95f00000000, 0x1d282a8700000000, 0x8a8e376000000000, 0x7263609200000000, 0xe5c57d7500000000, 0xb67810dd00000000, 0x21de0d3a00000000, 0xd9335ac800000000, 0x4e95472f00000000, 0x68ee84f700000000, 0xff48991000000000, 0x07a5cee200000000, 0x9003d30500000000, 0x0a55398800000000, 0x9df3246f00000000, 0x651e739d00000000, 0xf2b86e7a00000000, 0xd4c3ada200000000, 0x4365b04500000000, 0xbb88e7b700000000, 0x2c2efa5000000000, 0xce23427700000000, 0x59855f9000000000, 0xa168086200000000, 0x36ce158500000000, 0x10b5d65d00000000, 0x8713cbba00000000, 0x7ffe9c4800000000, 0xe85881af00000000, 0x720e6b2200000000, 0xe5a876c500000000, 0x1d45213700000000, 0x8ae33cd000000000, 0xac98ff0800000000, 0x3b3ee2ef00000000, 0xc3d3b51d00000000, 0x5475a8fa00000000, 0x24af1f9600000000, 0xb309027100000000, 0x4be4558300000000, 0xdc42486400000000, 0xfa398bbc00000000, 0x6d9f965b00000000, 0x9572c1a900000000, 0x02d4dc4e00000000, 0x988236c300000000, 0x0f242b2400000000, 0xf7c97cd600000000, 0x606f613100000000, 0x4614a2e900000000, 0xd1b2bf0e00000000, 0x295fe8fc00000000, 0xbef9f51b00000000, 0x5cf44d3c00000000, 0xcb5250db00000000, 0x33bf072900000000, 0xa4191ace00000000, 0x8262d91600000000, 0x15c4c4f100000000, 0xed29930300000000, 0x7a8f8ee400000000, 0xe0d9646900000000, 0x777f798e00000000, 0x8f922e7c00000000, 0x1834339b00000000, 0x3e4ff04300000000, 0xa9e9eda400000000, 0x5104ba5600000000, 0xc6a2a7b100000000, 0x951fca1900000000, 0x02b9d7fe00000000, 0xfa54800c00000000, 0x6df29deb00000000, 0x4b895e3300000000, 0xdc2f43d400000000, 0x24c2142600000000, 0xb36409c100000000, 0x2932e34c00000000, 0xbe94feab00000000, 0x4679a95900000000, 0xd1dfb4be00000000, 0xf7a4776600000000, 0x60026a8100000000, 0x98ef3d7300000000, 0x0f49209400000000, 0xed4498b300000000, 0x7ae2855400000000, 0x820fd2a600000000, 0x15a9cf4100000000, 0x33d20c9900000000, 0xa474117e00000000, 0x5c99468c00000000, 0xcb3f5b6b00000000, 0x5169b1e600000000, 0xc6cfac0100000000, 0x3e22fbf300000000, 0xa984e61400000000, 0x8fff25cc00000000, 0x1859382b00000000, 0xe0b46fd900000000, 0x7712723e00000000}, {0x0000000000000000, 0x411b8c6e00000000, 0x823618dd00000000, 0xc32d94b300000000, 0x456b416100000000, 0x0470cd0f00000000, 0xc75d59bc00000000, 0x8646d5d200000000, 0x8ad682c200000000, 0xcbcd0eac00000000, 0x08e09a1f00000000, 0x49fb167100000000, 0xcfbdc3a300000000, 0x8ea64fcd00000000, 0x4d8bdb7e00000000, 0x0c90571000000000, 0x55ab745e00000000, 0x14b0f83000000000, 0xd79d6c8300000000, 0x9686e0ed00000000, 0x10c0353f00000000, 0x51dbb95100000000, 0x92f62de200000000, 0xd3eda18c00000000, 0xdf7df69c00000000, 0x9e667af200000000, 0x5d4bee4100000000, 0x1c50622f00000000, 0x9a16b7fd00000000, 0xdb0d3b9300000000, 0x1820af2000000000, 0x593b234e00000000, 0xaa56e9bc00000000, 0xeb4d65d200000000, 0x2860f16100000000, 0x697b7d0f00000000, 0xef3da8dd00000000, 0xae2624b300000000, 0x6d0bb00000000000, 0x2c103c6e00000000, 0x20806b7e00000000, 0x619be71000000000, 0xa2b673a300000000, 0xe3adffcd00000000, 0x65eb2a1f00000000, 0x24f0a67100000000, 0xe7dd32c200000000, 0xa6c6beac00000000, 0xfffd9de200000000, 0xbee6118c00000000, 0x7dcb853f00000000, 0x3cd0095100000000, 0xba96dc8300000000, 0xfb8d50ed00000000, 0x38a0c45e00000000, 0x79bb483000000000, 0x752b1f2000000000, 0x3430934e00000000, 0xf71d07fd00000000, 0xb6068b9300000000, 0x30405e4100000000, 0x715bd22f00000000, 0xb276469c00000000, 0xf36dcaf200000000, 0x15aba3a200000000, 0x54b02fcc00000000, 0x979dbb7f00000000, 0xd686371100000000, 0x50c0e2c300000000, 0x11db6ead00000000, 0xd2f6fa1e00000000, 0x93ed767000000000, 0x9f7d216000000000, 0xde66ad0e00000000, 0x1d4b39bd00000000, 0x5c50b5d300000000, 0xda16600100000000, 0x9b0dec6f00000000, 0x582078dc00000000, 0x193bf4b200000000, 0x4000d7fc00000000, 0x011b5b9200000000, 0xc236cf2100000000, 0x832d434f00000000, 0x056b969d00000000, 0x44701af300000000, 0x875d8e4000000000, 0xc646022e00000000, 0xcad6553e00000000, 0x8bcdd95000000000, 0x48e04de300000000, 0x09fbc18d00000000, 0x8fbd145f00000000, 0xcea6983100000000, 0x0d8b0c8200000000, 0x4c9080ec00000000, 0xbffd4a1e00000000, 0xfee6c67000000000, 0x3dcb52c300000000, 0x7cd0dead00000000, 0xfa960b7f00000000, 0xbb8d871100000000, 0x78a013a200000000, 0x39bb9fcc00000000, 0x352bc8dc00000000, 0x743044b200000000, 0xb71dd00100000000, 0xf6065c6f00000000, 0x704089bd00000000, 0x315b05d300000000, 0xf276916000000000, 0xb36d1d0e00000000, 0xea563e4000000000, 0xab4db22e00000000, 0x6860269d00000000, 0x297baaf300000000, 0xaf3d7f2100000000, 0xee26f34f00000000, 0x2d0b67fc00000000, 0x6c10eb9200000000, 0x6080bc8200000000, 0x219b30ec00000000, 0xe2b6a45f00000000, 0xa3ad283100000000, 0x25ebfde300000000, 0x64f0718d00000000, 0xa7dde53e00000000, 0xe6c6695000000000, 0x6b50369e00000000, 0x2a4bbaf000000000, 0xe9662e4300000000, 0xa87da22d00000000, 0x2e3b77ff00000000, 0x6f20fb9100000000, 0xac0d6f2200000000, 0xed16e34c00000000, 0xe186b45c00000000, 0xa09d383200000000, 0x63b0ac8100000000, 0x22ab20ef00000000, 0xa4edf53d00000000, 0xe5f6795300000000, 0x26dbede000000000, 0x67c0618e00000000, 0x3efb42c000000000, 0x7fe0ceae00000000, 0xbccd5a1d00000000, 0xfdd6d67300000000, 0x7b9003a100000000, 0x3a8b8fcf00000000, 0xf9a61b7c00000000, 0xb8bd971200000000, 0xb42dc00200000000, 0xf5364c6c00000000, 0x361bd8df00000000, 0x770054b100000000, 0xf146816300000000, 0xb05d0d0d00000000, 0x737099be00000000, 0x326b15d000000000, 0xc106df2200000000, 0x801d534c00000000, 0x4330c7ff00000000, 0x022b4b9100000000, 0x846d9e4300000000, 0xc576122d00000000, 0x065b869e00000000, 0x47400af000000000, 0x4bd05de000000000, 0x0acbd18e00000000, 0xc9e6453d00000000, 0x88fdc95300000000, 0x0ebb1c8100000000, 0x4fa090ef00000000, 0x8c8d045c00000000, 0xcd96883200000000, 0x94adab7c00000000, 0xd5b6271200000000, 0x169bb3a100000000, 0x57803fcf00000000, 0xd1c6ea1d00000000, 0x90dd667300000000, 0x53f0f2c000000000, 0x12eb7eae00000000, 0x1e7b29be00000000, 0x5f60a5d000000000, 0x9c4d316300000000, 0xdd56bd0d00000000, 0x5b1068df00000000, 0x1a0be4b100000000, 0xd926700200000000, 0x983dfc6c00000000, 0x7efb953c00000000, 0x3fe0195200000000, 0xfccd8de100000000, 0xbdd6018f00000000, 0x3b90d45d00000000, 0x7a8b583300000000, 0xb9a6cc8000000000, 0xf8bd40ee00000000, 0xf42d17fe00000000, 0xb5369b9000000000, 0x761b0f2300000000, 0x3700834d00000000, 0xb146569f00000000, 0xf05ddaf100000000, 0x33704e4200000000, 0x726bc22c00000000, 0x2b50e16200000000, 0x6a4b6d0c00000000, 0xa966f9bf00000000, 0xe87d75d100000000, 0x6e3ba00300000000, 0x2f202c6d00000000, 0xec0db8de00000000, 0xad1634b000000000, 0xa18663a000000000, 0xe09defce00000000, 0x23b07b7d00000000, 0x62abf71300000000, 0xe4ed22c100000000, 0xa5f6aeaf00000000, 0x66db3a1c00000000, 0x27c0b67200000000, 0xd4ad7c8000000000, 0x95b6f0ee00000000, 0x569b645d00000000, 0x1780e83300000000, 0x91c63de100000000, 0xd0ddb18f00000000, 0x13f0253c00000000, 0x52eba95200000000, 0x5e7bfe4200000000, 0x1f60722c00000000, 0xdc4de69f00000000, 0x9d566af100000000, 0x1b10bf2300000000, 0x5a0b334d00000000, 0x9926a7fe00000000, 0xd83d2b9000000000, 0x810608de00000000, 0xc01d84b000000000, 0x0330100300000000, 0x422b9c6d00000000, 0xc46d49bf00000000, 0x8576c5d100000000, 0x465b516200000000, 0x0740dd0c00000000, 0x0bd08a1c00000000, 0x4acb067200000000, 0x89e692c100000000, 0xc8fd1eaf00000000, 0x4ebbcb7d00000000, 0x0fa0471300000000, 0xcc8dd3a000000000, 0x8d965fce00000000}, {0x0000000000000000, 0x1dfdb50100000000, 0x3afa6b0300000000, 0x2707de0200000000, 0x74f4d70600000000, 0x6909620700000000, 0x4e0ebc0500000000, 0x53f3090400000000, 0xe8e8af0d00000000, 0xf5151a0c00000000, 0xd212c40e00000000, 0xcfef710f00000000, 0x9c1c780b00000000, 0x81e1cd0a00000000, 0xa6e6130800000000, 0xbb1ba60900000000, 0xd0d15f1b00000000, 0xcd2cea1a00000000, 0xea2b341800000000, 0xf7d6811900000000, 0xa425881d00000000, 0xb9d83d1c00000000, 0x9edfe31e00000000, 0x8322561f00000000, 0x3839f01600000000, 0x25c4451700000000, 0x02c39b1500000000, 0x1f3e2e1400000000, 0x4ccd271000000000, 0x5130921100000000, 0x76374c1300000000, 0x6bcaf91200000000, 0xa0a3bf3600000000, 0xbd5e0a3700000000, 0x9a59d43500000000, 0x87a4613400000000, 0xd457683000000000, 0xc9aadd3100000000, 0xeead033300000000, 0xf350b63200000000, 0x484b103b00000000, 0x55b6a53a00000000, 0x72b17b3800000000, 0x6f4cce3900000000, 0x3cbfc73d00000000, 0x2142723c00000000, 0x0645ac3e00000000, 0x1bb8193f00000000, 0x7072e02d00000000, 0x6d8f552c00000000, 0x4a888b2e00000000, 0x57753e2f00000000, 0x0486372b00000000, 0x197b822a00000000, 0x3e7c5c2800000000, 0x2381e92900000000, 0x989a4f2000000000, 0x8567fa2100000000, 0xa260242300000000, 0xbf9d912200000000, 0xec6e982600000000, 0xf1932d2700000000, 0xd694f32500000000, 0xcb69462400000000, 0x40477f6d00000000, 0x5dbaca6c00000000, 0x7abd146e00000000, 0x6740a16f00000000, 0x34b3a86b00000000, 0x294e1d6a00000000, 0x0e49c36800000000, 0x13b4766900000000, 0xa8afd06000000000, 0xb552656100000000, 0x9255bb6300000000, 0x8fa80e6200000000, 0xdc5b076600000000, 0xc1a6b26700000000, 0xe6a16c6500000000, 0xfb5cd96400000000, 0x9096207600000000, 0x8d6b957700000000, 0xaa6c4b7500000000, 0xb791fe7400000000, 0xe462f77000000000, 0xf99f427100000000, 0xde989c7300000000, 0xc365297200000000, 0x787e8f7b00000000, 0x65833a7a00000000, 0x4284e47800000000, 0x5f79517900000000, 0x0c8a587d00000000, 0x1177ed7c00000000, 0x3670337e00000000, 0x2b8d867f00000000, 0xe0e4c05b00000000, 0xfd19755a00000000, 0xda1eab5800000000, 0xc7e31e5900000000, 0x9410175d00000000, 0x89eda25c00000000, 0xaeea7c5e00000000, 0xb317c95f00000000, 0x080c6f5600000000, 0x15f1da5700000000, 0x32f6045500000000, 0x2f0bb15400000000, 0x7cf8b85000000000, 0x61050d5100000000, 0x4602d35300000000, 0x5bff665200000000, 0x30359f4000000000, 0x2dc82a4100000000, 0x0acff44300000000, 0x1732414200000000, 0x44c1484600000000, 0x593cfd4700000000, 0x7e3b234500000000, 0x63c6964400000000, 0xd8dd304d00000000, 0xc520854c00000000, 0xe2275b4e00000000, 0xffdaee4f00000000, 0xac29e74b00000000, 0xb1d4524a00000000, 0x96d38c4800000000, 0x8b2e394900000000, 0x808efeda00000000, 0x9d734bdb00000000, 0xba7495d900000000, 0xa78920d800000000, 0xf47a29dc00000000, 0xe9879cdd00000000, 0xce8042df00000000, 0xd37df7de00000000, 0x686651d700000000, 0x759be4d600000000, 0x529c3ad400000000, 0x4f618fd500000000, 0x1c9286d100000000, 0x016f33d000000000, 0x2668edd200000000, 0x3b9558d300000000, 0x505fa1c100000000, 0x4da214c000000000, 0x6aa5cac200000000, 0x77587fc300000000, 0x24ab76c700000000, 0x3956c3c600000000, 0x1e511dc400000000, 0x03aca8c500000000, 0xb8b70ecc00000000, 0xa54abbcd00000000, 0x824d65cf00000000, 0x9fb0d0ce00000000, 0xcc43d9ca00000000, 0xd1be6ccb00000000, 0xf6b9b2c900000000, 0xeb4407c800000000, 0x202d41ec00000000, 0x3dd0f4ed00000000, 0x1ad72aef00000000, 0x072a9fee00000000, 0x54d996ea00000000, 0x492423eb00000000, 0x6e23fde900000000, 0x73de48e800000000, 0xc8c5eee100000000, 0xd5385be000000000, 0xf23f85e200000000, 0xefc230e300000000, 0xbc3139e700000000, 0xa1cc8ce600000000, 0x86cb52e400000000, 0x9b36e7e500000000, 0xf0fc1ef700000000, 0xed01abf600000000, 0xca0675f400000000, 0xd7fbc0f500000000, 0x8408c9f100000000, 0x99f57cf000000000, 0xbef2a2f200000000, 0xa30f17f300000000, 0x1814b1fa00000000, 0x05e904fb00000000, 0x22eedaf900000000, 0x3f136ff800000000, 0x6ce066fc00000000, 0x711dd3fd00000000, 0x561a0dff00000000, 0x4be7b8fe00000000, 0xc0c981b700000000, 0xdd3434b600000000, 0xfa33eab400000000, 0xe7ce5fb500000000, 0xb43d56b100000000, 0xa9c0e3b000000000, 0x8ec73db200000000, 0x933a88b300000000, 0x28212eba00000000, 0x35dc9bbb00000000, 0x12db45b900000000, 0x0f26f0b800000000, 0x5cd5f9bc00000000, 0x41284cbd00000000, 0x662f92bf00000000, 0x7bd227be00000000, 0x1018deac00000000, 0x0de56bad00000000, 0x2ae2b5af00000000, 0x371f00ae00000000, 0x64ec09aa00000000, 0x7911bcab00000000, 0x5e1662a900000000, 0x43ebd7a800000000, 0xf8f071a100000000, 0xe50dc4a000000000, 0xc20a1aa200000000, 0xdff7afa300000000, 0x8c04a6a700000000, 0x91f913a600000000, 0xb6fecda400000000, 0xab0378a500000000, 0x606a3e8100000000, 0x7d978b8000000000, 0x5a90558200000000, 0x476de08300000000, 0x149ee98700000000, 0x09635c8600000000, 0x2e64828400000000, 0x3399378500000000, 0x8882918c00000000, 0x957f248d00000000, 0xb278fa8f00000000, 0xaf854f8e00000000, 0xfc76468a00000000, 0xe18bf38b00000000, 0xc68c2d8900000000, 0xdb71988800000000, 0xb0bb619a00000000, 0xad46d49b00000000, 0x8a410a9900000000, 0x97bcbf9800000000, 0xc44fb69c00000000, 0xd9b2039d00000000, 0xfeb5dd9f00000000, 0xe348689e00000000, 0x5853ce9700000000, 0x45ae7b9600000000, 0x62a9a59400000000, 0x7f54109500000000, 0x2ca7199100000000, 0x315aac9000000000, 0x165d729200000000, 0x0ba0c79300000000}, {0x0000000000000000, 0x24d9076300000000, 0x48b20fc600000000, 0x6c6b08a500000000, 0xd1626e5700000000, 0xf5bb693400000000, 0x99d0619100000000, 0xbd0966f200000000, 0xa2c5dcae00000000, 0x861cdbcd00000000, 0xea77d36800000000, 0xceaed40b00000000, 0x73a7b2f900000000, 0x577eb59a00000000, 0x3b15bd3f00000000, 0x1fccba5c00000000, 0x058dc88600000000, 0x2154cfe500000000, 0x4d3fc74000000000, 0x69e6c02300000000, 0xd4efa6d100000000, 0xf036a1b200000000, 0x9c5da91700000000, 0xb884ae7400000000, 0xa748142800000000, 0x8391134b00000000, 0xeffa1bee00000000, 0xcb231c8d00000000, 0x762a7a7f00000000, 0x52f37d1c00000000, 0x3e9875b900000000, 0x1a4172da00000000, 0x4b1ce0d600000000, 0x6fc5e7b500000000, 0x03aeef1000000000, 0x2777e87300000000, 0x9a7e8e8100000000, 0xbea789e200000000, 0xd2cc814700000000, 0xf615862400000000, 0xe9d93c7800000000, 0xcd003b1b00000000, 0xa16b33be00000000, 0x85b234dd00000000, 0x38bb522f00000000, 0x1c62554c00000000, 0x70095de900000000, 0x54d05a8a00000000, 0x4e91285000000000, 0x6a482f3300000000, 0x0623279600000000, 0x22fa20f500000000, 0x9ff3460700000000, 0xbb2a416400000000, 0xd74149c100000000, 0xf3984ea200000000, 0xec54f4fe00000000, 0xc88df39d00000000, 0xa4e6fb3800000000, 0x803ffc5b00000000, 0x3d369aa900000000, 0x19ef9dca00000000, 0x7584956f00000000, 0x515d920c00000000, 0xd73eb17600000000, 0xf3e7b61500000000, 0x9f8cbeb000000000, 0xbb55b9d300000000, 0x065cdf2100000000, 0x2285d84200000000, 0x4eeed0e700000000, 0x6a37d78400000000, 0x75fb6dd800000000, 0x51226abb00000000, 0x3d49621e00000000, 0x1990657d00000000, 0xa499038f00000000, 0x804004ec00000000, 0xec2b0c4900000000, 0xc8f20b2a00000000, 0xd2b379f000000000, 0xf66a7e9300000000, 0x9a01763600000000, 0xbed8715500000000, 0x03d117a700000000, 0x270810c400000000, 0x4b63186100000000, 0x6fba1f0200000000, 0x7076a55e00000000, 0x54afa23d00000000, 0x38c4aa9800000000, 0x1c1dadfb00000000, 0xa114cb0900000000, 0x85cdcc6a00000000, 0xe9a6c4cf00000000, 0xcd7fc3ac00000000, 0x9c2251a000000000, 0xb8fb56c300000000, 0xd4905e6600000000, 0xf049590500000000, 0x4d403ff700000000, 0x6999389400000000, 0x05f2303100000000, 0x212b375200000000, 0x3ee78d0e00000000, 0x1a3e8a6d00000000, 0x765582c800000000, 0x528c85ab00000000, 0xef85e35900000000, 0xcb5ce43a00000000, 0xa737ec9f00000000, 0x83eeebfc00000000, 0x99af992600000000, 0xbd769e4500000000, 0xd11d96e000000000, 0xf5c4918300000000, 0x48cdf77100000000, 0x6c14f01200000000, 0x007ff8b700000000, 0x24a6ffd400000000, 0x3b6a458800000000, 0x1fb342eb00000000, 0x73d84a4e00000000, 0x57014d2d00000000, 0xea082bdf00000000, 0xced12cbc00000000, 0xa2ba241900000000, 0x8663237a00000000, 0xae7d62ed00000000, 0x8aa4658e00000000, 0xe6cf6d2b00000000, 0xc2166a4800000000, 0x7f1f0cba00000000, 0x5bc60bd900000000, 0x37ad037c00000000, 0x1374041f00000000, 0x0cb8be4300000000, 0x2861b92000000000, 0x440ab18500000000, 0x60d3b6e600000000, 0xdddad01400000000, 0xf903d77700000000, 0x9568dfd200000000, 0xb1b1d8b100000000, 0xabf0aa6b00000000, 0x8f29ad0800000000, 0xe342a5ad00000000, 0xc79ba2ce00000000, 0x7a92c43c00000000, 0x5e4bc35f00000000, 0x3220cbfa00000000, 0x16f9cc9900000000, 0x093576c500000000, 0x2dec71a600000000, 0x4187790300000000, 0x655e7e6000000000, 0xd857189200000000, 0xfc8e1ff100000000, 0x90e5175400000000, 0xb43c103700000000, 0xe561823b00000000, 0xc1b8855800000000, 0xadd38dfd00000000, 0x890a8a9e00000000, 0x3403ec6c00000000, 0x10daeb0f00000000, 0x7cb1e3aa00000000, 0x5868e4c900000000, 0x47a45e9500000000, 0x637d59f600000000, 0x0f16515300000000, 0x2bcf563000000000, 0x96c630c200000000, 0xb21f37a100000000, 0xde743f0400000000, 0xfaad386700000000, 0xe0ec4abd00000000, 0xc4354dde00000000, 0xa85e457b00000000, 0x8c87421800000000, 0x318e24ea00000000, 0x1557238900000000, 0x793c2b2c00000000, 0x5de52c4f00000000, 0x4229961300000000, 0x66f0917000000000, 0x0a9b99d500000000, 0x2e429eb600000000, 0x934bf84400000000, 0xb792ff2700000000, 0xdbf9f78200000000, 0xff20f0e100000000, 0x7943d39b00000000, 0x5d9ad4f800000000, 0x31f1dc5d00000000, 0x1528db3e00000000, 0xa821bdcc00000000, 0x8cf8baaf00000000, 0xe093b20a00000000, 0xc44ab56900000000, 0xdb860f3500000000, 0xff5f085600000000, 0x933400f300000000, 0xb7ed079000000000, 0x0ae4616200000000, 0x2e3d660100000000, 0x42566ea400000000, 0x668f69c700000000, 0x7cce1b1d00000000, 0x58171c7e00000000, 0x347c14db00000000, 0x10a513b800000000, 0xadac754a00000000, 0x8975722900000000, 0xe51e7a8c00000000, 0xc1c77def00000000, 0xde0bc7b300000000, 0xfad2c0d000000000, 0x96b9c87500000000, 0xb260cf1600000000, 0x0f69a9e400000000, 0x2bb0ae8700000000, 0x47dba62200000000, 0x6302a14100000000, 0x325f334d00000000, 0x1686342e00000000, 0x7aed3c8b00000000, 0x5e343be800000000, 0xe33d5d1a00000000, 0xc7e45a7900000000, 0xab8f52dc00000000, 0x8f5655bf00000000, 0x909aefe300000000, 0xb443e88000000000, 0xd828e02500000000, 0xfcf1e74600000000, 0x41f881b400000000, 0x652186d700000000, 0x094a8e7200000000, 0x2d93891100000000, 0x37d2fbcb00000000, 0x130bfca800000000, 0x7f60f40d00000000, 0x5bb9f36e00000000, 0xe6b0959c00000000, 0xc26992ff00000000, 0xae029a5a00000000, 0x8adb9d3900000000, 0x9517276500000000, 0xb1ce200600000000, 0xdda528a300000000, 0xf97c2fc000000000, 0x4475493200000000, 0x60ac4e5100000000, 0x0cc746f400000000, 0x281e419700000000}, {0x0000000000000000, 0x08e3603c00000000, 0x10c6c17800000000, 0x1825a14400000000, 0x208c83f100000000, 0x286fe3cd00000000, 0x304a428900000000, 0x38a922b500000000, 0x011e763800000000, 0x09fd160400000000, 0x11d8b74000000000, 0x193bd77c00000000, 0x2192f5c900000000, 0x297195f500000000, 0x315434b100000000, 0x39b7548d00000000, 0x023cec7000000000, 0x0adf8c4c00000000, 0x12fa2d0800000000, 0x1a194d3400000000, 0x22b06f8100000000, 0x2a530fbd00000000, 0x3276aef900000000, 0x3a95cec500000000, 0x03229a4800000000, 0x0bc1fa7400000000, 0x13e45b3000000000, 0x1b073b0c00000000, 0x23ae19b900000000, 0x2b4d798500000000, 0x3368d8c100000000, 0x3b8bb8fd00000000, 0x0478d8e100000000, 0x0c9bb8dd00000000, 0x14be199900000000, 0x1c5d79a500000000, 0x24f45b1000000000, 0x2c173b2c00000000, 0x34329a6800000000, 0x3cd1fa5400000000, 0x0566aed900000000, 0x0d85cee500000000, 0x15a06fa100000000, 0x1d430f9d00000000, 0x25ea2d2800000000, 0x2d094d1400000000, 0x352cec5000000000, 0x3dcf8c6c00000000, 0x0644349100000000, 0x0ea754ad00000000, 0x1682f5e900000000, 0x1e6195d500000000, 0x26c8b76000000000, 0x2e2bd75c00000000, 0x360e761800000000, 0x3eed162400000000, 0x075a42a900000000, 0x0fb9229500000000, 0x179c83d100000000, 0x1f7fe3ed00000000, 0x27d6c15800000000, 0x2f35a16400000000, 0x3710002000000000, 0x3ff3601c00000000, 0x49f6c11800000000, 0x4115a12400000000, 0x5930006000000000, 0x51d3605c00000000, 0x697a42e900000000, 0x619922d500000000, 0x79bc839100000000, 0x715fe3ad00000000, 0x48e8b72000000000, 0x400bd71c00000000, 0x582e765800000000, 0x50cd166400000000, 0x686434d100000000, 0x608754ed00000000, 0x78a2f5a900000000, 0x7041959500000000, 0x4bca2d6800000000, 0x43294d5400000000, 0x5b0cec1000000000, 0x53ef8c2c00000000, 0x6b46ae9900000000, 0x63a5cea500000000, 0x7b806fe100000000, 0x73630fdd00000000, 0x4ad45b5000000000, 0x42373b6c00000000, 0x5a129a2800000000, 0x52f1fa1400000000, 0x6a58d8a100000000, 0x62bbb89d00000000, 0x7a9e19d900000000, 0x727d79e500000000, 0x4d8e19f900000000, 0x456d79c500000000, 0x5d48d88100000000, 0x55abb8bd00000000, 0x6d029a0800000000, 0x65e1fa3400000000, 0x7dc45b7000000000, 0x75273b4c00000000, 0x4c906fc100000000, 0x44730ffd00000000, 0x5c56aeb900000000, 0x54b5ce8500000000, 0x6c1cec3000000000, 0x64ff8c0c00000000, 0x7cda2d4800000000, 0x74394d7400000000, 0x4fb2f58900000000, 0x475195b500000000, 0x5f7434f100000000, 0x579754cd00000000, 0x6f3e767800000000, 0x67dd164400000000, 0x7ff8b70000000000, 0x771bd73c00000000, 0x4eac83b100000000, 0x464fe38d00000000, 0x5e6a42c900000000, 0x568922f500000000, 0x6e20004000000000, 0x66c3607c00000000, 0x7ee6c13800000000, 0x7605a10400000000, 0x92ec833100000000, 0x9a0fe30d00000000, 0x822a424900000000, 0x8ac9227500000000, 0xb26000c000000000, 0xba8360fc00000000, 0xa2a6c1b800000000, 0xaa45a18400000000, 0x93f2f50900000000, 0x9b11953500000000, 0x8334347100000000, 0x8bd7544d00000000, 0xb37e76f800000000, 0xbb9d16c400000000, 0xa3b8b78000000000, 0xab5bd7bc00000000, 0x90d06f4100000000, 0x98330f7d00000000, 0x8016ae3900000000, 0x88f5ce0500000000, 0xb05cecb000000000, 0xb8bf8c8c00000000, 0xa09a2dc800000000, 0xa8794df400000000, 0x91ce197900000000, 0x992d794500000000, 0x8108d80100000000, 0x89ebb83d00000000, 0xb1429a8800000000, 0xb9a1fab400000000, 0xa1845bf000000000, 0xa9673bcc00000000, 0x96945bd000000000, 0x9e773bec00000000, 0x86529aa800000000, 0x8eb1fa9400000000, 0xb618d82100000000, 0xbefbb81d00000000, 0xa6de195900000000, 0xae3d796500000000, 0x978a2de800000000, 0x9f694dd400000000, 0x874cec9000000000, 0x8faf8cac00000000, 0xb706ae1900000000, 0xbfe5ce2500000000, 0xa7c06f6100000000, 0xaf230f5d00000000, 0x94a8b7a000000000, 0x9c4bd79c00000000, 0x846e76d800000000, 0x8c8d16e400000000, 0xb424345100000000, 0xbcc7546d00000000, 0xa4e2f52900000000, 0xac01951500000000, 0x95b6c19800000000, 0x9d55a1a400000000, 0x857000e000000000, 0x8d9360dc00000000, 0xb53a426900000000, 0xbdd9225500000000, 0xa5fc831100000000, 0xad1fe32d00000000, 0xdb1a422900000000, 0xd3f9221500000000, 0xcbdc835100000000, 0xc33fe36d00000000, 0xfb96c1d800000000, 0xf375a1e400000000, 0xeb5000a000000000, 0xe3b3609c00000000, 0xda04341100000000, 0xd2e7542d00000000, 0xcac2f56900000000, 0xc221955500000000, 0xfa88b7e000000000, 0xf26bd7dc00000000, 0xea4e769800000000, 0xe2ad16a400000000, 0xd926ae5900000000, 0xd1c5ce6500000000, 0xc9e06f2100000000, 0xc1030f1d00000000, 0xf9aa2da800000000, 0xf1494d9400000000, 0xe96cecd000000000, 0xe18f8cec00000000, 0xd838d86100000000, 0xd0dbb85d00000000, 0xc8fe191900000000, 0xc01d792500000000, 0xf8b45b9000000000, 0xf0573bac00000000, 0xe8729ae800000000, 0xe091fad400000000, 0xdf629ac800000000, 0xd781faf400000000, 0xcfa45bb000000000, 0xc7473b8c00000000, 0xffee193900000000, 0xf70d790500000000, 0xef28d84100000000, 0xe7cbb87d00000000, 0xde7cecf000000000, 0xd69f8ccc00000000, 0xceba2d8800000000, 0xc6594db400000000, 0xfef06f0100000000, 0xf6130f3d00000000, 0xee36ae7900000000, 0xe6d5ce4500000000, 0xdd5e76b800000000, 0xd5bd168400000000, 0xcd98b7c000000000, 0xc57bd7fc00000000, 0xfdd2f54900000000, 0xf531957500000000, 0xed14343100000000, 0xe5f7540d00000000, 0xdc40008000000000, 0xd4a360bc00000000, 0xcc86c1f800000000, 0xc465a1c400000000, 0xfccc837100000000, 0xf42fe34d00000000, 0xec0a420900000000, 0xe4e9223500000000}, {0x0000000000000000, 0xd1e8e70e00000000, 0xa2d1cf1d00000000, 0x7339281300000000, 0x44a39f3b00000000, 0x954b783500000000, 0xe672502600000000, 0x379ab72800000000, 0x88463f7700000000, 0x59aed87900000000, 0x2a97f06a00000000, 0xfb7f176400000000, 0xcce5a04c00000000, 0x1d0d474200000000, 0x6e346f5100000000, 0xbfdc885f00000000, 0x108d7eee00000000, 0xc16599e000000000, 0xb25cb1f300000000, 0x63b456fd00000000, 0x542ee1d500000000, 0x85c606db00000000, 0xf6ff2ec800000000, 0x2717c9c600000000, 0x98cb419900000000, 0x4923a69700000000, 0x3a1a8e8400000000, 0xebf2698a00000000, 0xdc68dea200000000, 0x0d8039ac00000000, 0x7eb911bf00000000, 0xaf51f6b100000000, 0x611c8c0700000000, 0xb0f46b0900000000, 0xc3cd431a00000000, 0x1225a41400000000, 0x25bf133c00000000, 0xf457f43200000000, 0x876edc2100000000, 0x56863b2f00000000, 0xe95ab37000000000, 0x38b2547e00000000, 0x4b8b7c6d00000000, 0x9a639b6300000000, 0xadf92c4b00000000, 0x7c11cb4500000000, 0x0f28e35600000000, 0xdec0045800000000, 0x7191f2e900000000, 0xa07915e700000000, 0xd3403df400000000, 0x02a8dafa00000000, 0x35326dd200000000, 0xe4da8adc00000000, 0x97e3a2cf00000000, 0x460b45c100000000, 0xf9d7cd9e00000000, 0x283f2a9000000000, 0x5b06028300000000, 0x8aeee58d00000000, 0xbd7452a500000000, 0x6c9cb5ab00000000, 0x1fa59db800000000, 0xce4d7ab600000000, 0xc238180f00000000, 0x13d0ff0100000000, 0x60e9d71200000000, 0xb101301c00000000, 0x869b873400000000, 0x5773603a00000000, 0x244a482900000000, 0xf5a2af2700000000, 0x4a7e277800000000, 0x9b96c07600000000, 0xe8afe86500000000, 0x39470f6b00000000, 0x0eddb84300000000, 0xdf355f4d00000000, 0xac0c775e00000000, 0x7de4905000000000, 0xd2b566e100000000, 0x035d81ef00000000, 0x7064a9fc00000000, 0xa18c4ef200000000, 0x9616f9da00000000, 0x47fe1ed400000000, 0x34c736c700000000, 0xe52fd1c900000000, 0x5af3599600000000, 0x8b1bbe9800000000, 0xf822968b00000000, 0x29ca718500000000, 0x1e50c6ad00000000, 0xcfb821a300000000, 0xbc8109b000000000, 0x6d69eebe00000000, 0xa324940800000000, 0x72cc730600000000, 0x01f55b1500000000, 0xd01dbc1b00000000, 0xe7870b3300000000, 0x366fec3d00000000, 0x4556c42e00000000, 0x94be232000000000, 0x2b62ab7f00000000, 0xfa8a4c7100000000, 0x89b3646200000000, 0x585b836c00000000, 0x6fc1344400000000, 0xbe29d34a00000000, 0xcd10fb5900000000, 0x1cf81c5700000000, 0xb3a9eae600000000, 0x62410de800000000, 0x117825fb00000000, 0xc090c2f500000000, 0xf70a75dd00000000, 0x26e292d300000000, 0x55dbbac000000000, 0x84335dce00000000, 0x3befd59100000000, 0xea07329f00000000, 0x993e1a8c00000000, 0x48d6fd8200000000, 0x7f4c4aaa00000000, 0xaea4ada400000000, 0xdd9d85b700000000, 0x0c7562b900000000, 0x8471301e00000000, 0x5599d71000000000, 0x26a0ff0300000000, 0xf748180d00000000, 0xc0d2af2500000000, 0x113a482b00000000, 0x6203603800000000, 0xb3eb873600000000, 0x0c370f6900000000, 0xdddfe86700000000, 0xaee6c07400000000, 0x7f0e277a00000000, 0x4894905200000000, 0x997c775c00000000, 0xea455f4f00000000, 0x3badb84100000000, 0x94fc4ef000000000, 0x4514a9fe00000000, 0x362d81ed00000000, 0xe7c566e300000000, 0xd05fd1cb00000000, 0x01b736c500000000, 0x728e1ed600000000, 0xa366f9d800000000, 0x1cba718700000000, 0xcd52968900000000, 0xbe6bbe9a00000000, 0x6f83599400000000, 0x5819eebc00000000, 0x89f109b200000000, 0xfac821a100000000, 0x2b20c6af00000000, 0xe56dbc1900000000, 0x34855b1700000000, 0x47bc730400000000, 0x9654940a00000000, 0xa1ce232200000000, 0x7026c42c00000000, 0x031fec3f00000000, 0xd2f70b3100000000, 0x6d2b836e00000000, 0xbcc3646000000000, 0xcffa4c7300000000, 0x1e12ab7d00000000, 0x29881c5500000000, 0xf860fb5b00000000, 0x8b59d34800000000, 0x5ab1344600000000, 0xf5e0c2f700000000, 0x240825f900000000, 0x57310dea00000000, 0x86d9eae400000000, 0xb1435dcc00000000, 0x60abbac200000000, 0x139292d100000000, 0xc27a75df00000000, 0x7da6fd8000000000, 0xac4e1a8e00000000, 0xdf77329d00000000, 0x0e9fd59300000000, 0x390562bb00000000, 0xe8ed85b500000000, 0x9bd4ada600000000, 0x4a3c4aa800000000, 0x4649281100000000, 0x97a1cf1f00000000, 0xe498e70c00000000, 0x3570000200000000, 0x02eab72a00000000, 0xd302502400000000, 0xa03b783700000000, 0x71d39f3900000000, 0xce0f176600000000, 0x1fe7f06800000000, 0x6cded87b00000000, 0xbd363f7500000000, 0x8aac885d00000000, 0x5b446f5300000000, 0x287d474000000000, 0xf995a04e00000000, 0x56c456ff00000000, 0x872cb1f100000000, 0xf41599e200000000, 0x25fd7eec00000000, 0x1267c9c400000000, 0xc38f2eca00000000, 0xb0b606d900000000, 0x615ee1d700000000, 0xde82698800000000, 0x0f6a8e8600000000, 0x7c53a69500000000, 0xadbb419b00000000, 0x9a21f6b300000000, 0x4bc911bd00000000, 0x38f039ae00000000, 0xe918dea000000000, 0x2755a41600000000, 0xf6bd431800000000, 0x85846b0b00000000, 0x546c8c0500000000, 0x63f63b2d00000000, 0xb21edc2300000000, 0xc127f43000000000, 0x10cf133e00000000, 0xaf139b6100000000, 0x7efb7c6f00000000, 0x0dc2547c00000000, 0xdc2ab37200000000, 0xebb0045a00000000, 0x3a58e35400000000, 0x4961cb4700000000, 0x98892c4900000000, 0x37d8daf800000000, 0xe6303df600000000, 0x950915e500000000, 0x44e1f2eb00000000, 0x737b45c300000000, 0xa293a2cd00000000, 0xd1aa8ade00000000, 0x00426dd000000000, 0xbf9ee58f00000000, 0x6e76028100000000, 0x1d4f2a9200000000, 0xcca7cd9c00000000, 0xfb3d7ab400000000, 0x2ad59dba00000000, 0x59ecb5a900000000, 0x880452a700000000}, {0x0000000000000000, 0xaa05daf100000000, 0x150dc53800000000, 0xbf081fc900000000, 0x2a1a8a7100000000, 0x801f508000000000, 0x3f174f4900000000, 0x951295b800000000, 0x543414e300000000, 0xfe31ce1200000000, 0x4139d1db00000000, 0xeb3c0b2a00000000, 0x7e2e9e9200000000, 0xd42b446300000000, 0x6b235baa00000000, 0xc126815b00000000, 0xe96e591d00000000, 0x436b83ec00000000, 0xfc639c2500000000, 0x566646d400000000, 0xc374d36c00000000, 0x6971099d00000000, 0xd679165400000000, 0x7c7ccca500000000, 0xbd5a4dfe00000000, 0x175f970f00000000, 0xa85788c600000000, 0x0252523700000000, 0x9740c78f00000000, 0x3d451d7e00000000, 0x824d02b700000000, 0x2848d84600000000, 0xd2ddb23a00000000, 0x78d868cb00000000, 0xc7d0770200000000, 0x6dd5adf300000000, 0xf8c7384b00000000, 0x52c2e2ba00000000, 0xedcafd7300000000, 0x47cf278200000000, 0x86e9a6d900000000, 0x2cec7c2800000000, 0x93e463e100000000, 0x39e1b91000000000, 0xacf32ca800000000, 0x06f6f65900000000, 0xb9fee99000000000, 0x13fb336100000000, 0x3bb3eb2700000000, 0x91b631d600000000, 0x2ebe2e1f00000000, 0x84bbf4ee00000000, 0x11a9615600000000, 0xbbacbba700000000, 0x04a4a46e00000000, 0xaea17e9f00000000, 0x6f87ffc400000000, 0xc582253500000000, 0x7a8a3afc00000000, 0xd08fe00d00000000, 0x459d75b500000000, 0xef98af4400000000, 0x5090b08d00000000, 0xfa956a7c00000000, 0xa4bb657500000000, 0x0ebebf8400000000, 0xb1b6a04d00000000, 0x1bb37abc00000000, 0x8ea1ef0400000000, 0x24a435f500000000, 0x9bac2a3c00000000, 0x31a9f0cd00000000, 0xf08f719600000000, 0x5a8aab6700000000, 0xe582b4ae00000000, 0x4f876e5f00000000, 0xda95fbe700000000, 0x7090211600000000, 0xcf983edf00000000, 0x659de42e00000000, 0x4dd53c6800000000, 0xe7d0e69900000000, 0x58d8f95000000000, 0xf2dd23a100000000, 0x67cfb61900000000, 0xcdca6ce800000000, 0x72c2732100000000, 0xd8c7a9d000000000, 0x19e1288b00000000, 0xb3e4f27a00000000, 0x0cecedb300000000, 0xa6e9374200000000, 0x33fba2fa00000000, 0x99fe780b00000000, 0x26f667c200000000, 0x8cf3bd3300000000, 0x7666d74f00000000, 0xdc630dbe00000000, 0x636b127700000000, 0xc96ec88600000000, 0x5c7c5d3e00000000, 0xf67987cf00000000, 0x4971980600000000, 0xe37442f700000000, 0x2252c3ac00000000, 0x8857195d00000000, 0x375f069400000000, 0x9d5adc6500000000, 0x084849dd00000000, 0xa24d932c00000000, 0x1d458ce500000000, 0xb740561400000000, 0x9f088e5200000000, 0x350d54a300000000, 0x8a054b6a00000000, 0x2000919b00000000, 0xb512042300000000, 0x1f17ded200000000, 0xa01fc11b00000000, 0x0a1a1bea00000000, 0xcb3c9ab100000000, 0x6139404000000000, 0xde315f8900000000, 0x7434857800000000, 0xe12610c000000000, 0x4b23ca3100000000, 0xf42bd5f800000000, 0x5e2e0f0900000000, 0x4877cbea00000000, 0xe272111b00000000, 0x5d7a0ed200000000, 0xf77fd42300000000, 0x626d419b00000000, 0xc8689b6a00000000, 0x776084a300000000, 0xdd655e5200000000, 0x1c43df0900000000, 0xb64605f800000000, 0x094e1a3100000000, 0xa34bc0c000000000, 0x3659557800000000, 0x9c5c8f8900000000, 0x2354904000000000, 0x89514ab100000000, 0xa11992f700000000, 0x0b1c480600000000, 0xb41457cf00000000, 0x1e118d3e00000000, 0x8b03188600000000, 0x2106c27700000000, 0x9e0eddbe00000000, 0x340b074f00000000, 0xf52d861400000000, 0x5f285ce500000000, 0xe020432c00000000, 0x4a2599dd00000000, 0xdf370c6500000000, 0x7532d69400000000, 0xca3ac95d00000000, 0x603f13ac00000000, 0x9aaa79d000000000, 0x30afa32100000000, 0x8fa7bce800000000, 0x25a2661900000000, 0xb0b0f3a100000000, 0x1ab5295000000000, 0xa5bd369900000000, 0x0fb8ec6800000000, 0xce9e6d3300000000, 0x649bb7c200000000, 0xdb93a80b00000000, 0x719672fa00000000, 0xe484e74200000000, 0x4e813db300000000, 0xf189227a00000000, 0x5b8cf88b00000000, 0x73c420cd00000000, 0xd9c1fa3c00000000, 0x66c9e5f500000000, 0xcccc3f0400000000, 0x59deaabc00000000, 0xf3db704d00000000, 0x4cd36f8400000000, 0xe6d6b57500000000, 0x27f0342e00000000, 0x8df5eedf00000000, 0x32fdf11600000000, 0x98f82be700000000, 0x0deabe5f00000000, 0xa7ef64ae00000000, 0x18e77b6700000000, 0xb2e2a19600000000, 0xecccae9f00000000, 0x46c9746e00000000, 0xf9c16ba700000000, 0x53c4b15600000000, 0xc6d624ee00000000, 0x6cd3fe1f00000000, 0xd3dbe1d600000000, 0x79de3b2700000000, 0xb8f8ba7c00000000, 0x12fd608d00000000, 0xadf57f4400000000, 0x07f0a5b500000000, 0x92e2300d00000000, 0x38e7eafc00000000, 0x87eff53500000000, 0x2dea2fc400000000, 0x05a2f78200000000, 0xafa72d7300000000, 0x10af32ba00000000, 0xbaaae84b00000000, 0x2fb87df300000000, 0x85bda70200000000, 0x3ab5b8cb00000000, 0x90b0623a00000000, 0x5196e36100000000, 0xfb93399000000000, 0x449b265900000000, 0xee9efca800000000, 0x7b8c691000000000, 0xd189b3e100000000, 0x6e81ac2800000000, 0xc48476d900000000, 0x3e111ca500000000, 0x9414c65400000000, 0x2b1cd99d00000000, 0x8119036c00000000, 0x140b96d400000000, 0xbe0e4c2500000000, 0x010653ec00000000, 0xab03891d00000000, 0x6a25084600000000, 0xc020d2b700000000, 0x7f28cd7e00000000, 0xd52d178f00000000, 0x403f823700000000, 0xea3a58c600000000, 0x5532470f00000000, 0xff379dfe00000000, 0xd77f45b800000000, 0x7d7a9f4900000000, 0xc272808000000000, 0x68775a7100000000, 0xfd65cfc900000000, 0x5760153800000000, 0xe8680af100000000, 0x426dd00000000000, 0x834b515b00000000, 0x294e8baa00000000, 0x9646946300000000, 0x3c434e9200000000, 0xa951db2a00000000, 0x035401db00000000, 0xbc5c1e1200000000, 0x1659c4e300000000}}; #else /* W == 4 */ local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0xae689191, 0x87a02563, 0x29c8b4f2, 0xd4314c87, 0x7a59dd16, 0x539169e4, 0xfdf9f875, 0x73139f4f, 0xdd7b0ede, 0xf4b3ba2c, 0x5adb2bbd, 0xa722d3c8, 0x094a4259, 0x2082f6ab, 0x8eea673a, 0xe6273e9e, 0x484faf0f, 0x61871bfd, 0xcfef8a6c, 0x32167219, 0x9c7ee388, 0xb5b6577a, 0x1bdec6eb, 0x9534a1d1, 0x3b5c3040, 0x129484b2, 0xbcfc1523, 0x4105ed56, 0xef6d7cc7, 0xc6a5c835, 0x68cd59a4, 0x173f7b7d, 0xb957eaec, 0x909f5e1e, 0x3ef7cf8f, 0xc30e37fa, 0x6d66a66b, 0x44ae1299, 0xeac68308, 0x642ce432, 0xca4475a3, 0xe38cc151, 0x4de450c0, 0xb01da8b5, 0x1e753924, 0x37bd8dd6, 0x99d51c47, 0xf11845e3, 0x5f70d472, 0x76b86080, 0xd8d0f111, 0x25290964, 0x8b4198f5, 0xa2892c07, 0x0ce1bd96, 0x820bdaac, 0x2c634b3d, 0x05abffcf, 0xabc36e5e, 0x563a962b, 0xf85207ba, 0xd19ab348, 0x7ff222d9, 0x2e7ef6fa, 0x8016676b, 0xa9ded399, 0x07b64208, 0xfa4fba7d, 0x54272bec, 0x7def9f1e, 0xd3870e8f, 0x5d6d69b5, 0xf305f824, 0xdacd4cd6, 0x74a5dd47, 0x895c2532, 0x2734b4a3, 0x0efc0051, 0xa09491c0, 0xc859c864, 0x663159f5, 0x4ff9ed07, 0xe1917c96, 0x1c6884e3, 0xb2001572, 0x9bc8a180, 0x35a03011, 0xbb4a572b, 0x1522c6ba, 0x3cea7248, 0x9282e3d9, 0x6f7b1bac, 0xc1138a3d, 0xe8db3ecf, 0x46b3af5e, 0x39418d87, 0x97291c16, 0xbee1a8e4, 0x10893975, 0xed70c100, 0x43185091, 0x6ad0e463, 0xc4b875f2, 0x4a5212c8, 0xe43a8359, 0xcdf237ab, 0x639aa63a, 0x9e635e4f, 0x300bcfde, 0x19c37b2c, 0xb7abeabd, 0xdf66b319, 0x710e2288, 0x58c6967a, 0xf6ae07eb, 0x0b57ff9e, 0xa53f6e0f, 0x8cf7dafd, 0x229f4b6c, 0xac752c56, 0x021dbdc7, 0x2bd50935, 0x85bd98a4, 0x784460d1, 0xd62cf140, 0xffe445b2, 0x518cd423, 0x5cfdedf4, 0xf2957c65, 0xdb5dc897, 0x75355906, 0x88cca173, 0x26a430e2, 0x0f6c8410, 0xa1041581, 0x2fee72bb, 0x8186e32a, 0xa84e57d8, 0x0626c649, 0xfbdf3e3c, 0x55b7afad, 0x7c7f1b5f, 0xd2178ace, 0xbadad36a, 0x14b242fb, 0x3d7af609, 0x93126798, 0x6eeb9fed, 0xc0830e7c, 0xe94bba8e, 0x47232b1f, 0xc9c94c25, 0x67a1ddb4, 0x4e696946, 0xe001f8d7, 0x1df800a2, 0xb3909133, 0x9a5825c1, 0x3430b450, 0x4bc29689, 0xe5aa0718, 0xcc62b3ea, 0x620a227b, 0x9ff3da0e, 0x319b4b9f, 0x1853ff6d, 0xb63b6efc, 0x38d109c6, 0x96b99857, 0xbf712ca5, 0x1119bd34, 0xece04541, 0x4288d4d0, 0x6b406022, 0xc528f1b3, 0xade5a817, 0x038d3986, 0x2a458d74, 0x842d1ce5, 0x79d4e490, 0xd7bc7501, 0xfe74c1f3, 0x501c5062, 0xdef63758, 0x709ea6c9, 0x5956123b, 0xf73e83aa, 0x0ac77bdf, 0xa4afea4e, 0x8d675ebc, 0x230fcf2d, 0x72831b0e, 0xdceb8a9f, 0xf5233e6d, 0x5b4baffc, 0xa6b25789, 0x08dac618, 0x211272ea, 0x8f7ae37b, 0x01908441, 0xaff815d0, 0x8630a122, 0x285830b3, 0xd5a1c8c6, 0x7bc95957, 0x5201eda5, 0xfc697c34, 0x94a42590, 0x3accb401, 0x130400f3, 0xbd6c9162, 0x40956917, 0xeefdf886, 0xc7354c74, 0x695ddde5, 0xe7b7badf, 0x49df2b4e, 0x60179fbc, 0xce7f0e2d, 0x3386f658, 0x9dee67c9, 0xb426d33b, 0x1a4e42aa, 0x65bc6073, 0xcbd4f1e2, 0xe21c4510, 0x4c74d481, 0xb18d2cf4, 0x1fe5bd65, 0x362d0997, 0x98459806, 0x16afff3c, 0xb8c76ead, 0x910fda5f, 0x3f674bce, 0xc29eb3bb, 0x6cf6222a, 0x453e96d8, 0xeb560749, 0x839b5eed, 0x2df3cf7c, 0x043b7b8e, 0xaa53ea1f, 0x57aa126a, 0xf9c283fb, 0xd00a3709, 0x7e62a698, 0xf088c1a2, 0x5ee05033, 0x7728e4c1, 0xd9407550, 0x24b98d25, 0x8ad11cb4, 0xa319a846, 0x0d7139d7}, {0x00000000, 0xb9fbdbe8, 0xa886b191, 0x117d6a79, 0x8a7c6563, 0x3387be8b, 0x22fad4f2, 0x9b010f1a, 0xcf89cc87, 0x7672176f, 0x670f7d16, 0xdef4a6fe, 0x45f5a9e4, 0xfc0e720c, 0xed731875, 0x5488c39d, 0x44629f4f, 0xfd9944a7, 0xece42ede, 0x551ff536, 0xce1efa2c, 0x77e521c4, 0x66984bbd, 0xdf639055, 0x8beb53c8, 0x32108820, 0x236de259, 0x9a9639b1, 0x019736ab, 0xb86ced43, 0xa911873a, 0x10ea5cd2, 0x88c53e9e, 0x313ee576, 0x20438f0f, 0x99b854e7, 0x02b95bfd, 0xbb428015, 0xaa3fea6c, 0x13c43184, 0x474cf219, 0xfeb729f1, 0xefca4388, 0x56319860, 0xcd30977a, 0x74cb4c92, 0x65b626eb, 0xdc4dfd03, 0xcca7a1d1, 0x755c7a39, 0x64211040, 0xdddacba8, 0x46dbc4b2, 0xff201f5a, 0xee5d7523, 0x57a6aecb, 0x032e6d56, 0xbad5b6be, 0xaba8dcc7, 0x1253072f, 0x89520835, 0x30a9d3dd, 0x21d4b9a4, 0x982f624c, 0xcafb7b7d, 0x7300a095, 0x627dcaec, 0xdb861104, 0x40871e1e, 0xf97cc5f6, 0xe801af8f, 0x51fa7467, 0x0572b7fa, 0xbc896c12, 0xadf4066b, 0x140fdd83, 0x8f0ed299, 0x36f50971, 0x27886308, 0x9e73b8e0, 0x8e99e432, 0x37623fda, 0x261f55a3, 0x9fe48e4b, 0x04e58151, 0xbd1e5ab9, 0xac6330c0, 0x1598eb28, 0x411028b5, 0xf8ebf35d, 0xe9969924, 0x506d42cc, 0xcb6c4dd6, 0x7297963e, 0x63eafc47, 0xda1127af, 0x423e45e3, 0xfbc59e0b, 0xeab8f472, 0x53432f9a, 0xc8422080, 0x71b9fb68, 0x60c49111, 0xd93f4af9, 0x8db78964, 0x344c528c, 0x253138f5, 0x9ccae31d, 0x07cbec07, 0xbe3037ef, 0xaf4d5d96, 0x16b6867e, 0x065cdaac, 0xbfa70144, 0xaeda6b3d, 0x1721b0d5, 0x8c20bfcf, 0x35db6427, 0x24a60e5e, 0x9d5dd5b6, 0xc9d5162b, 0x702ecdc3, 0x6153a7ba, 0xd8a87c52, 0x43a97348, 0xfa52a8a0, 0xeb2fc2d9, 0x52d41931, 0x4e87f0bb, 0xf77c2b53, 0xe601412a, 0x5ffa9ac2, 0xc4fb95d8, 0x7d004e30, 0x6c7d2449, 0xd586ffa1, 0x810e3c3c, 0x38f5e7d4, 0x29888dad, 0x90735645, 0x0b72595f, 0xb28982b7, 0xa3f4e8ce, 0x1a0f3326, 0x0ae56ff4, 0xb31eb41c, 0xa263de65, 0x1b98058d, 0x80990a97, 0x3962d17f, 0x281fbb06, 0x91e460ee, 0xc56ca373, 0x7c97789b, 0x6dea12e2, 0xd411c90a, 0x4f10c610, 0xf6eb1df8, 0xe7967781, 0x5e6dac69, 0xc642ce25, 0x7fb915cd, 0x6ec47fb4, 0xd73fa45c, 0x4c3eab46, 0xf5c570ae, 0xe4b81ad7, 0x5d43c13f, 0x09cb02a2, 0xb030d94a, 0xa14db333, 0x18b668db, 0x83b767c1, 0x3a4cbc29, 0x2b31d650, 0x92ca0db8, 0x8220516a, 0x3bdb8a82, 0x2aa6e0fb, 0x935d3b13, 0x085c3409, 0xb1a7efe1, 0xa0da8598, 0x19215e70, 0x4da99ded, 0xf4524605, 0xe52f2c7c, 0x5cd4f794, 0xc7d5f88e, 0x7e2e2366, 0x6f53491f, 0xd6a892f7, 0x847c8bc6, 0x3d87502e, 0x2cfa3a57, 0x9501e1bf, 0x0e00eea5, 0xb7fb354d, 0xa6865f34, 0x1f7d84dc, 0x4bf54741, 0xf20e9ca9, 0xe373f6d0, 0x5a882d38, 0xc1892222, 0x7872f9ca, 0x690f93b3, 0xd0f4485b, 0xc01e1489, 0x79e5cf61, 0x6898a518, 0xd1637ef0, 0x4a6271ea, 0xf399aa02, 0xe2e4c07b, 0x5b1f1b93, 0x0f97d80e, 0xb66c03e6, 0xa711699f, 0x1eeab277, 0x85ebbd6d, 0x3c106685, 0x2d6d0cfc, 0x9496d714, 0x0cb9b558, 0xb5426eb0, 0xa43f04c9, 0x1dc4df21, 0x86c5d03b, 0x3f3e0bd3, 0x2e4361aa, 0x97b8ba42, 0xc33079df, 0x7acba237, 0x6bb6c84e, 0xd24d13a6, 0x494c1cbc, 0xf0b7c754, 0xe1caad2d, 0x583176c5, 0x48db2a17, 0xf120f1ff, 0xe05d9b86, 0x59a6406e, 0xc2a74f74, 0x7b5c949c, 0x6a21fee5, 0xd3da250d, 0x8752e690, 0x3ea93d78, 0x2fd45701, 0x962f8ce9, 0x0d2e83f3, 0xb4d5581b, 0xa5a83262, 0x1c53e98a}, {0x00000000, 0x9d0fe176, 0xe16ec4ad, 0x7c6125db, 0x19ac8f1b, 0x84a36e6d, 0xf8c24bb6, 0x65cdaac0, 0x33591e36, 0xae56ff40, 0xd237da9b, 0x4f383bed, 0x2af5912d, 0xb7fa705b, 0xcb9b5580, 0x5694b4f6, 0x66b23c6c, 0xfbbddd1a, 0x87dcf8c1, 0x1ad319b7, 0x7f1eb377, 0xe2115201, 0x9e7077da, 0x037f96ac, 0x55eb225a, 0xc8e4c32c, 0xb485e6f7, 0x298a0781, 0x4c47ad41, 0xd1484c37, 0xad2969ec, 0x3026889a, 0xcd6478d8, 0x506b99ae, 0x2c0abc75, 0xb1055d03, 0xd4c8f7c3, 0x49c716b5, 0x35a6336e, 0xa8a9d218, 0xfe3d66ee, 0x63328798, 0x1f53a243, 0x825c4335, 0xe791e9f5, 0x7a9e0883, 0x06ff2d58, 0x9bf0cc2e, 0xabd644b4, 0x36d9a5c2, 0x4ab88019, 0xd7b7616f, 0xb27acbaf, 0x2f752ad9, 0x53140f02, 0xce1bee74, 0x988f5a82, 0x0580bbf4, 0x79e19e2f, 0xe4ee7f59, 0x8123d599, 0x1c2c34ef, 0x604d1134, 0xfd42f042, 0x41b9f7f1, 0xdcb61687, 0xa0d7335c, 0x3dd8d22a, 0x581578ea, 0xc51a999c, 0xb97bbc47, 0x24745d31, 0x72e0e9c7, 0xefef08b1, 0x938e2d6a, 0x0e81cc1c, 0x6b4c66dc, 0xf64387aa, 0x8a22a271, 0x172d4307, 0x270bcb9d, 0xba042aeb, 0xc6650f30, 0x5b6aee46, 0x3ea74486, 0xa3a8a5f0, 0xdfc9802b, 0x42c6615d, 0x1452d5ab, 0x895d34dd, 0xf53c1106, 0x6833f070, 0x0dfe5ab0, 0x90f1bbc6, 0xec909e1d, 0x719f7f6b, 0x8cdd8f29, 0x11d26e5f, 0x6db34b84, 0xf0bcaaf2, 0x95710032, 0x087ee144, 0x741fc49f, 0xe91025e9, 0xbf84911f, 0x228b7069, 0x5eea55b2, 0xc3e5b4c4, 0xa6281e04, 0x3b27ff72, 0x4746daa9, 0xda493bdf, 0xea6fb345, 0x77605233, 0x0b0177e8, 0x960e969e, 0xf3c33c5e, 0x6eccdd28, 0x12adf8f3, 0x8fa21985, 0xd936ad73, 0x44394c05, 0x385869de, 0xa55788a8, 0xc09a2268, 0x5d95c31e, 0x21f4e6c5, 0xbcfb07b3, 0x8373efe2, 0x1e7c0e94, 0x621d2b4f, 0xff12ca39, 0x9adf60f9, 0x07d0818f, 0x7bb1a454, 0xe6be4522, 0xb02af1d4, 0x2d2510a2, 0x51443579, 0xcc4bd40f, 0xa9867ecf, 0x34899fb9, 0x48e8ba62, 0xd5e75b14, 0xe5c1d38e, 0x78ce32f8, 0x04af1723, 0x99a0f655, 0xfc6d5c95, 0x6162bde3, 0x1d039838, 0x800c794e, 0xd698cdb8, 0x4b972cce, 0x37f60915, 0xaaf9e863, 0xcf3442a3, 0x523ba3d5, 0x2e5a860e, 0xb3556778, 0x4e17973a, 0xd318764c, 0xaf795397, 0x3276b2e1, 0x57bb1821, 0xcab4f957, 0xb6d5dc8c, 0x2bda3dfa, 0x7d4e890c, 0xe041687a, 0x9c204da1, 0x012facd7, 0x64e20617, 0xf9ede761, 0x858cc2ba, 0x188323cc, 0x28a5ab56, 0xb5aa4a20, 0xc9cb6ffb, 0x54c48e8d, 0x3109244d, 0xac06c53b, 0xd067e0e0, 0x4d680196, 0x1bfcb560, 0x86f35416, 0xfa9271cd, 0x679d90bb, 0x02503a7b, 0x9f5fdb0d, 0xe33efed6, 0x7e311fa0, 0xc2ca1813, 0x5fc5f965, 0x23a4dcbe, 0xbeab3dc8, 0xdb669708, 0x4669767e, 0x3a0853a5, 0xa707b2d3, 0xf1930625, 0x6c9ce753, 0x10fdc288, 0x8df223fe, 0xe83f893e, 0x75306848, 0x09514d93, 0x945eace5, 0xa478247f, 0x3977c509, 0x4516e0d2, 0xd81901a4, 0xbdd4ab64, 0x20db4a12, 0x5cba6fc9, 0xc1b58ebf, 0x97213a49, 0x0a2edb3f, 0x764ffee4, 0xeb401f92, 0x8e8db552, 0x13825424, 0x6fe371ff, 0xf2ec9089, 0x0fae60cb, 0x92a181bd, 0xeec0a466, 0x73cf4510, 0x1602efd0, 0x8b0d0ea6, 0xf76c2b7d, 0x6a63ca0b, 0x3cf77efd, 0xa1f89f8b, 0xdd99ba50, 0x40965b26, 0x255bf1e6, 0xb8541090, 0xc435354b, 0x593ad43d, 0x691c5ca7, 0xf413bdd1, 0x8872980a, 0x157d797c, 0x70b0d3bc, 0xedbf32ca, 0x91de1711, 0x0cd1f667, 0x5a454291, 0xc74aa3e7, 0xbb2b863c, 0x2624674a, 0x43e9cd8a, 0xdee62cfc, 0xa2870927, 0x3f88e851}, {0x00000000, 0xdd96d985, 0x605cb54b, 0xbdca6cce, 0xc0b96a96, 0x1d2fb313, 0xa0e5dfdd, 0x7d730658, 0x5a03d36d, 0x87950ae8, 0x3a5f6626, 0xe7c9bfa3, 0x9abab9fb, 0x472c607e, 0xfae60cb0, 0x2770d535, 0xb407a6da, 0x69917f5f, 0xd45b1391, 0x09cdca14, 0x74becc4c, 0xa92815c9, 0x14e27907, 0xc974a082, 0xee0475b7, 0x3392ac32, 0x8e58c0fc, 0x53ce1979, 0x2ebd1f21, 0xf32bc6a4, 0x4ee1aa6a, 0x937773ef, 0xb37e4bf5, 0x6ee89270, 0xd322febe, 0x0eb4273b, 0x73c72163, 0xae51f8e6, 0x139b9428, 0xce0d4dad, 0xe97d9898, 0x34eb411d, 0x89212dd3, 0x54b7f456, 0x29c4f20e, 0xf4522b8b, 0x49984745, 0x940e9ec0, 0x0779ed2f, 0xdaef34aa, 0x67255864, 0xbab381e1, 0xc7c087b9, 0x1a565e3c, 0xa79c32f2, 0x7a0aeb77, 0x5d7a3e42, 0x80ece7c7, 0x3d268b09, 0xe0b0528c, 0x9dc354d4, 0x40558d51, 0xfd9fe19f, 0x2009381a, 0xbd8d91ab, 0x601b482e, 0xddd124e0, 0x0047fd65, 0x7d34fb3d, 0xa0a222b8, 0x1d684e76, 0xc0fe97f3, 0xe78e42c6, 0x3a189b43, 0x87d2f78d, 0x5a442e08, 0x27372850, 0xfaa1f1d5, 0x476b9d1b, 0x9afd449e, 0x098a3771, 0xd41ceef4, 0x69d6823a, 0xb4405bbf, 0xc9335de7, 0x14a58462, 0xa96fe8ac, 0x74f93129, 0x5389e41c, 0x8e1f3d99, 0x33d55157, 0xee4388d2, 0x93308e8a, 0x4ea6570f, 0xf36c3bc1, 0x2efae244, 0x0ef3da5e, 0xd36503db, 0x6eaf6f15, 0xb339b690, 0xce4ab0c8, 0x13dc694d, 0xae160583, 0x7380dc06, 0x54f00933, 0x8966d0b6, 0x34acbc78, 0xe93a65fd, 0x944963a5, 0x49dfba20, 0xf415d6ee, 0x29830f6b, 0xbaf47c84, 0x6762a501, 0xdaa8c9cf, 0x073e104a, 0x7a4d1612, 0xa7dbcf97, 0x1a11a359, 0xc7877adc, 0xe0f7afe9, 0x3d61766c, 0x80ab1aa2, 0x5d3dc327, 0x204ec57f, 0xfdd81cfa, 0x40127034, 0x9d84a9b1, 0xa06a2517, 0x7dfcfc92, 0xc036905c, 0x1da049d9, 0x60d34f81, 0xbd459604, 0x008ffaca, 0xdd19234f, 0xfa69f67a, 0x27ff2fff, 0x9a354331, 0x47a39ab4, 0x3ad09cec, 0xe7464569, 0x5a8c29a7, 0x871af022, 0x146d83cd, 0xc9fb5a48, 0x74313686, 0xa9a7ef03, 0xd4d4e95b, 0x094230de, 0xb4885c10, 0x691e8595, 0x4e6e50a0, 0x93f88925, 0x2e32e5eb, 0xf3a43c6e, 0x8ed73a36, 0x5341e3b3, 0xee8b8f7d, 0x331d56f8, 0x13146ee2, 0xce82b767, 0x7348dba9, 0xaede022c, 0xd3ad0474, 0x0e3bddf1, 0xb3f1b13f, 0x6e6768ba, 0x4917bd8f, 0x9481640a, 0x294b08c4, 0xf4ddd141, 0x89aed719, 0x54380e9c, 0xe9f26252, 0x3464bbd7, 0xa713c838, 0x7a8511bd, 0xc74f7d73, 0x1ad9a4f6, 0x67aaa2ae, 0xba3c7b2b, 0x07f617e5, 0xda60ce60, 0xfd101b55, 0x2086c2d0, 0x9d4cae1e, 0x40da779b, 0x3da971c3, 0xe03fa846, 0x5df5c488, 0x80631d0d, 0x1de7b4bc, 0xc0716d39, 0x7dbb01f7, 0xa02dd872, 0xdd5ede2a, 0x00c807af, 0xbd026b61, 0x6094b2e4, 0x47e467d1, 0x9a72be54, 0x27b8d29a, 0xfa2e0b1f, 0x875d0d47, 0x5acbd4c2, 0xe701b80c, 0x3a976189, 0xa9e01266, 0x7476cbe3, 0xc9bca72d, 0x142a7ea8, 0x695978f0, 0xb4cfa175, 0x0905cdbb, 0xd493143e, 0xf3e3c10b, 0x2e75188e, 0x93bf7440, 0x4e29adc5, 0x335aab9d, 0xeecc7218, 0x53061ed6, 0x8e90c753, 0xae99ff49, 0x730f26cc, 0xcec54a02, 0x13539387, 0x6e2095df, 0xb3b64c5a, 0x0e7c2094, 0xd3eaf911, 0xf49a2c24, 0x290cf5a1, 0x94c6996f, 0x495040ea, 0x342346b2, 0xe9b59f37, 0x547ff3f9, 0x89e92a7c, 0x1a9e5993, 0xc7088016, 0x7ac2ecd8, 0xa754355d, 0xda273305, 0x07b1ea80, 0xba7b864e, 0x67ed5fcb, 0x409d8afe, 0x9d0b537b, 0x20c13fb5, 0xfd57e630, 0x8024e068, 0x5db239ed, 0xe0785523, 0x3dee8ca6}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x00000000, 0x85d996dd, 0x4bb55c60, 0xce6ccabd, 0x966ab9c0, 0x13b32f1d, 0xdddfe5a0, 0x5806737d, 0x6dd3035a, 0xe80a9587, 0x26665f3a, 0xa3bfc9e7, 0xfbb9ba9a, 0x7e602c47, 0xb00ce6fa, 0x35d57027, 0xdaa607b4, 0x5f7f9169, 0x91135bd4, 0x14cacd09, 0x4cccbe74, 0xc91528a9, 0x0779e214, 0x82a074c9, 0xb77504ee, 0x32ac9233, 0xfcc0588e, 0x7919ce53, 0x211fbd2e, 0xa4c62bf3, 0x6aaae14e, 0xef737793, 0xf54b7eb3, 0x7092e86e, 0xbefe22d3, 0x3b27b40e, 0x6321c773, 0xe6f851ae, 0x28949b13, 0xad4d0dce, 0x98987de9, 0x1d41eb34, 0xd32d2189, 0x56f4b754, 0x0ef2c429, 0x8b2b52f4, 0x45479849, 0xc09e0e94, 0x2fed7907, 0xaa34efda, 0x64582567, 0xe181b3ba, 0xb987c0c7, 0x3c5e561a, 0xf2329ca7, 0x77eb0a7a, 0x423e7a5d, 0xc7e7ec80, 0x098b263d, 0x8c52b0e0, 0xd454c39d, 0x518d5540, 0x9fe19ffd, 0x1a380920, 0xab918dbd, 0x2e481b60, 0xe024d1dd, 0x65fd4700, 0x3dfb347d, 0xb822a2a0, 0x764e681d, 0xf397fec0, 0xc6428ee7, 0x439b183a, 0x8df7d287, 0x082e445a, 0x50283727, 0xd5f1a1fa, 0x1b9d6b47, 0x9e44fd9a, 0x71378a09, 0xf4ee1cd4, 0x3a82d669, 0xbf5b40b4, 0xe75d33c9, 0x6284a514, 0xace86fa9, 0x2931f974, 0x1ce48953, 0x993d1f8e, 0x5751d533, 0xd28843ee, 0x8a8e3093, 0x0f57a64e, 0xc13b6cf3, 0x44e2fa2e, 0x5edaf30e, 0xdb0365d3, 0x156faf6e, 0x90b639b3, 0xc8b04ace, 0x4d69dc13, 0x830516ae, 0x06dc8073, 0x3309f054, 0xb6d06689, 0x78bcac34, 0xfd653ae9, 0xa5634994, 0x20badf49, 0xeed615f4, 0x6b0f8329, 0x847cf4ba, 0x01a56267, 0xcfc9a8da, 0x4a103e07, 0x12164d7a, 0x97cfdba7, 0x59a3111a, 0xdc7a87c7, 0xe9aff7e0, 0x6c76613d, 0xa21aab80, 0x27c33d5d, 0x7fc54e20, 0xfa1cd8fd, 0x34701240, 0xb1a9849d, 0x17256aa0, 0x92fcfc7d, 0x5c9036c0, 0xd949a01d, 0x814fd360, 0x049645bd, 0xcafa8f00, 0x4f2319dd, 0x7af669fa, 0xff2fff27, 0x3143359a, 0xb49aa347, 0xec9cd03a, 0x694546e7, 0xa7298c5a, 0x22f01a87, 0xcd836d14, 0x485afbc9, 0x86363174, 0x03efa7a9, 0x5be9d4d4, 0xde304209, 0x105c88b4, 0x95851e69, 0xa0506e4e, 0x2589f893, 0xebe5322e, 0x6e3ca4f3, 0x363ad78e, 0xb3e34153, 0x7d8f8bee, 0xf8561d33, 0xe26e1413, 0x67b782ce, 0xa9db4873, 0x2c02deae, 0x7404add3, 0xf1dd3b0e, 0x3fb1f1b3, 0xba68676e, 0x8fbd1749, 0x0a648194, 0xc4084b29, 0x41d1ddf4, 0x19d7ae89, 0x9c0e3854, 0x5262f2e9, 0xd7bb6434, 0x38c813a7, 0xbd11857a, 0x737d4fc7, 0xf6a4d91a, 0xaea2aa67, 0x2b7b3cba, 0xe517f607, 0x60ce60da, 0x551b10fd, 0xd0c28620, 0x1eae4c9d, 0x9b77da40, 0xc371a93d, 0x46a83fe0, 0x88c4f55d, 0x0d1d6380, 0xbcb4e71d, 0x396d71c0, 0xf701bb7d, 0x72d82da0, 0x2ade5edd, 0xaf07c800, 0x616b02bd, 0xe4b29460, 0xd167e447, 0x54be729a, 0x9ad2b827, 0x1f0b2efa, 0x470d5d87, 0xc2d4cb5a, 0x0cb801e7, 0x8961973a, 0x6612e0a9, 0xe3cb7674, 0x2da7bcc9, 0xa87e2a14, 0xf0785969, 0x75a1cfb4, 0xbbcd0509, 0x3e1493d4, 0x0bc1e3f3, 0x8e18752e, 0x4074bf93, 0xc5ad294e, 0x9dab5a33, 0x1872ccee, 0xd61e0653, 0x53c7908e, 0x49ff99ae, 0xcc260f73, 0x024ac5ce, 0x87935313, 0xdf95206e, 0x5a4cb6b3, 0x94207c0e, 0x11f9ead3, 0x242c9af4, 0xa1f50c29, 0x6f99c694, 0xea405049, 0xb2462334, 0x379fb5e9, 0xf9f37f54, 0x7c2ae989, 0x93599e1a, 0x168008c7, 0xd8ecc27a, 0x5d3554a7, 0x053327da, 0x80eab107, 0x4e867bba, 0xcb5fed67, 0xfe8a9d40, 0x7b530b9d, 0xb53fc120, 0x30e657fd, 0x68e02480, 0xed39b25d, 0x235578e0, 0xa68cee3d}, {0x00000000, 0x76e10f9d, 0xadc46ee1, 0xdb25617c, 0x1b8fac19, 0x6d6ea384, 0xb64bc2f8, 0xc0aacd65, 0x361e5933, 0x40ff56ae, 0x9bda37d2, 0xed3b384f, 0x2d91f52a, 0x5b70fab7, 0x80559bcb, 0xf6b49456, 0x6c3cb266, 0x1addbdfb, 0xc1f8dc87, 0xb719d31a, 0x77b31e7f, 0x015211e2, 0xda77709e, 0xac967f03, 0x5a22eb55, 0x2cc3e4c8, 0xf7e685b4, 0x81078a29, 0x41ad474c, 0x374c48d1, 0xec6929ad, 0x9a882630, 0xd87864cd, 0xae996b50, 0x75bc0a2c, 0x035d05b1, 0xc3f7c8d4, 0xb516c749, 0x6e33a635, 0x18d2a9a8, 0xee663dfe, 0x98873263, 0x43a2531f, 0x35435c82, 0xf5e991e7, 0x83089e7a, 0x582dff06, 0x2eccf09b, 0xb444d6ab, 0xc2a5d936, 0x1980b84a, 0x6f61b7d7, 0xafcb7ab2, 0xd92a752f, 0x020f1453, 0x74ee1bce, 0x825a8f98, 0xf4bb8005, 0x2f9ee179, 0x597feee4, 0x99d52381, 0xef342c1c, 0x34114d60, 0x42f042fd, 0xf1f7b941, 0x8716b6dc, 0x5c33d7a0, 0x2ad2d83d, 0xea781558, 0x9c991ac5, 0x47bc7bb9, 0x315d7424, 0xc7e9e072, 0xb108efef, 0x6a2d8e93, 0x1ccc810e, 0xdc664c6b, 0xaa8743f6, 0x71a2228a, 0x07432d17, 0x9dcb0b27, 0xeb2a04ba, 0x300f65c6, 0x46ee6a5b, 0x8644a73e, 0xf0a5a8a3, 0x2b80c9df, 0x5d61c642, 0xabd55214, 0xdd345d89, 0x06113cf5, 0x70f03368, 0xb05afe0d, 0xc6bbf190, 0x1d9e90ec, 0x6b7f9f71, 0x298fdd8c, 0x5f6ed211, 0x844bb36d, 0xf2aabcf0, 0x32007195, 0x44e17e08, 0x9fc41f74, 0xe92510e9, 0x1f9184bf, 0x69708b22, 0xb255ea5e, 0xc4b4e5c3, 0x041e28a6, 0x72ff273b, 0xa9da4647, 0xdf3b49da, 0x45b36fea, 0x33526077, 0xe877010b, 0x9e960e96, 0x5e3cc3f3, 0x28ddcc6e, 0xf3f8ad12, 0x8519a28f, 0x73ad36d9, 0x054c3944, 0xde695838, 0xa88857a5, 0x68229ac0, 0x1ec3955d, 0xc5e6f421, 0xb307fbbc, 0xe2ef7383, 0x940e7c1e, 0x4f2b1d62, 0x39ca12ff, 0xf960df9a, 0x8f81d007, 0x54a4b17b, 0x2245bee6, 0xd4f12ab0, 0xa210252d, 0x79354451, 0x0fd44bcc, 0xcf7e86a9, 0xb99f8934, 0x62bae848, 0x145be7d5, 0x8ed3c1e5, 0xf832ce78, 0x2317af04, 0x55f6a099, 0x955c6dfc, 0xe3bd6261, 0x3898031d, 0x4e790c80, 0xb8cd98d6, 0xce2c974b, 0x1509f637, 0x63e8f9aa, 0xa34234cf, 0xd5a33b52, 0x0e865a2e, 0x786755b3, 0x3a97174e, 0x4c7618d3, 0x975379af, 0xe1b27632, 0x2118bb57, 0x57f9b4ca, 0x8cdcd5b6, 0xfa3dda2b, 0x0c894e7d, 0x7a6841e0, 0xa14d209c, 0xd7ac2f01, 0x1706e264, 0x61e7edf9, 0xbac28c85, 0xcc238318, 0x56aba528, 0x204aaab5, 0xfb6fcbc9, 0x8d8ec454, 0x4d240931, 0x3bc506ac, 0xe0e067d0, 0x9601684d, 0x60b5fc1b, 0x1654f386, 0xcd7192fa, 0xbb909d67, 0x7b3a5002, 0x0ddb5f9f, 0xd6fe3ee3, 0xa01f317e, 0x1318cac2, 0x65f9c55f, 0xbedca423, 0xc83dabbe, 0x089766db, 0x7e766946, 0xa553083a, 0xd3b207a7, 0x250693f1, 0x53e79c6c, 0x88c2fd10, 0xfe23f28d, 0x3e893fe8, 0x48683075, 0x934d5109, 0xe5ac5e94, 0x7f2478a4, 0x09c57739, 0xd2e01645, 0xa40119d8, 0x64abd4bd, 0x124adb20, 0xc96fba5c, 0xbf8eb5c1, 0x493a2197, 0x3fdb2e0a, 0xe4fe4f76, 0x921f40eb, 0x52b58d8e, 0x24548213, 0xff71e36f, 0x8990ecf2, 0xcb60ae0f, 0xbd81a192, 0x66a4c0ee, 0x1045cf73, 0xd0ef0216, 0xa60e0d8b, 0x7d2b6cf7, 0x0bca636a, 0xfd7ef73c, 0x8b9ff8a1, 0x50ba99dd, 0x265b9640, 0xe6f15b25, 0x901054b8, 0x4b3535c4, 0x3dd43a59, 0xa75c1c69, 0xd1bd13f4, 0x0a987288, 0x7c797d15, 0xbcd3b070, 0xca32bfed, 0x1117de91, 0x67f6d10c, 0x9142455a, 0xe7a34ac7, 0x3c862bbb, 0x4a672426, 0x8acde943, 0xfc2ce6de, 0x270987a2, 0x51e8883f}, {0x00000000, 0xe8dbfbb9, 0x91b186a8, 0x796a7d11, 0x63657c8a, 0x8bbe8733, 0xf2d4fa22, 0x1a0f019b, 0x87cc89cf, 0x6f177276, 0x167d0f67, 0xfea6f4de, 0xe4a9f545, 0x0c720efc, 0x751873ed, 0x9dc38854, 0x4f9f6244, 0xa74499fd, 0xde2ee4ec, 0x36f51f55, 0x2cfa1ece, 0xc421e577, 0xbd4b9866, 0x559063df, 0xc853eb8b, 0x20881032, 0x59e26d23, 0xb139969a, 0xab369701, 0x43ed6cb8, 0x3a8711a9, 0xd25cea10, 0x9e3ec588, 0x76e53e31, 0x0f8f4320, 0xe754b899, 0xfd5bb902, 0x158042bb, 0x6cea3faa, 0x8431c413, 0x19f24c47, 0xf129b7fe, 0x8843caef, 0x60983156, 0x7a9730cd, 0x924ccb74, 0xeb26b665, 0x03fd4ddc, 0xd1a1a7cc, 0x397a5c75, 0x40102164, 0xa8cbdadd, 0xb2c4db46, 0x5a1f20ff, 0x23755dee, 0xcbaea657, 0x566d2e03, 0xbeb6d5ba, 0xc7dca8ab, 0x2f075312, 0x35085289, 0xddd3a930, 0xa4b9d421, 0x4c622f98, 0x7d7bfbca, 0x95a00073, 0xecca7d62, 0x041186db, 0x1e1e8740, 0xf6c57cf9, 0x8faf01e8, 0x6774fa51, 0xfab77205, 0x126c89bc, 0x6b06f4ad, 0x83dd0f14, 0x99d20e8f, 0x7109f536, 0x08638827, 0xe0b8739e, 0x32e4998e, 0xda3f6237, 0xa3551f26, 0x4b8ee49f, 0x5181e504, 0xb95a1ebd, 0xc03063ac, 0x28eb9815, 0xb5281041, 0x5df3ebf8, 0x249996e9, 0xcc426d50, 0xd64d6ccb, 0x3e969772, 0x47fcea63, 0xaf2711da, 0xe3453e42, 0x0b9ec5fb, 0x72f4b8ea, 0x9a2f4353, 0x802042c8, 0x68fbb971, 0x1191c460, 0xf94a3fd9, 0x6489b78d, 0x8c524c34, 0xf5383125, 0x1de3ca9c, 0x07eccb07, 0xef3730be, 0x965d4daf, 0x7e86b616, 0xacda5c06, 0x4401a7bf, 0x3d6bdaae, 0xd5b02117, 0xcfbf208c, 0x2764db35, 0x5e0ea624, 0xb6d55d9d, 0x2b16d5c9, 0xc3cd2e70, 0xbaa75361, 0x527ca8d8, 0x4873a943, 0xa0a852fa, 0xd9c22feb, 0x3119d452, 0xbbf0874e, 0x532b7cf7, 0x2a4101e6, 0xc29afa5f, 0xd895fbc4, 0x304e007d, 0x49247d6c, 0xa1ff86d5, 0x3c3c0e81, 0xd4e7f538, 0xad8d8829, 0x45567390, 0x5f59720b, 0xb78289b2, 0xcee8f4a3, 0x26330f1a, 0xf46fe50a, 0x1cb41eb3, 0x65de63a2, 0x8d05981b, 0x970a9980, 0x7fd16239, 0x06bb1f28, 0xee60e491, 0x73a36cc5, 0x9b78977c, 0xe212ea6d, 0x0ac911d4, 0x10c6104f, 0xf81debf6, 0x817796e7, 0x69ac6d5e, 0x25ce42c6, 0xcd15b97f, 0xb47fc46e, 0x5ca43fd7, 0x46ab3e4c, 0xae70c5f5, 0xd71ab8e4, 0x3fc1435d, 0xa202cb09, 0x4ad930b0, 0x33b34da1, 0xdb68b618, 0xc167b783, 0x29bc4c3a, 0x50d6312b, 0xb80dca92, 0x6a512082, 0x828adb3b, 0xfbe0a62a, 0x133b5d93, 0x09345c08, 0xe1efa7b1, 0x9885daa0, 0x705e2119, 0xed9da94d, 0x054652f4, 0x7c2c2fe5, 0x94f7d45c, 0x8ef8d5c7, 0x66232e7e, 0x1f49536f, 0xf792a8d6, 0xc68b7c84, 0x2e50873d, 0x573afa2c, 0xbfe10195, 0xa5ee000e, 0x4d35fbb7, 0x345f86a6, 0xdc847d1f, 0x4147f54b, 0xa99c0ef2, 0xd0f673e3, 0x382d885a, 0x222289c1, 0xcaf97278, 0xb3930f69, 0x5b48f4d0, 0x89141ec0, 0x61cfe579, 0x18a59868, 0xf07e63d1, 0xea71624a, 0x02aa99f3, 0x7bc0e4e2, 0x931b1f5b, 0x0ed8970f, 0xe6036cb6, 0x9f6911a7, 0x77b2ea1e, 0x6dbdeb85, 0x8566103c, 0xfc0c6d2d, 0x14d79694, 0x58b5b90c, 0xb06e42b5, 0xc9043fa4, 0x21dfc41d, 0x3bd0c586, 0xd30b3e3f, 0xaa61432e, 0x42bab897, 0xdf7930c3, 0x37a2cb7a, 0x4ec8b66b, 0xa6134dd2, 0xbc1c4c49, 0x54c7b7f0, 0x2dadcae1, 0xc5763158, 0x172adb48, 0xfff120f1, 0x869b5de0, 0x6e40a659, 0x744fa7c2, 0x9c945c7b, 0xe5fe216a, 0x0d25dad3, 0x90e65287, 0x783da93e, 0x0157d42f, 0xe98c2f96, 0xf3832e0d, 0x1b58d5b4, 0x6232a8a5, 0x8ae9531c}, {0x00000000, 0x919168ae, 0x6325a087, 0xf2b4c829, 0x874c31d4, 0x16dd597a, 0xe4699153, 0x75f8f9fd, 0x4f9f1373, 0xde0e7bdd, 0x2cbab3f4, 0xbd2bdb5a, 0xc8d322a7, 0x59424a09, 0xabf68220, 0x3a67ea8e, 0x9e3e27e6, 0x0faf4f48, 0xfd1b8761, 0x6c8aefcf, 0x19721632, 0x88e37e9c, 0x7a57b6b5, 0xebc6de1b, 0xd1a13495, 0x40305c3b, 0xb2849412, 0x2315fcbc, 0x56ed0541, 0xc77c6def, 0x35c8a5c6, 0xa459cd68, 0x7d7b3f17, 0xecea57b9, 0x1e5e9f90, 0x8fcff73e, 0xfa370ec3, 0x6ba6666d, 0x9912ae44, 0x0883c6ea, 0x32e42c64, 0xa37544ca, 0x51c18ce3, 0xc050e44d, 0xb5a81db0, 0x2439751e, 0xd68dbd37, 0x471cd599, 0xe34518f1, 0x72d4705f, 0x8060b876, 0x11f1d0d8, 0x64092925, 0xf598418b, 0x072c89a2, 0x96bde10c, 0xacda0b82, 0x3d4b632c, 0xcfffab05, 0x5e6ec3ab, 0x2b963a56, 0xba0752f8, 0x48b39ad1, 0xd922f27f, 0xfaf67e2e, 0x6b671680, 0x99d3dea9, 0x0842b607, 0x7dba4ffa, 0xec2b2754, 0x1e9fef7d, 0x8f0e87d3, 0xb5696d5d, 0x24f805f3, 0xd64ccdda, 0x47dda574, 0x32255c89, 0xa3b43427, 0x5100fc0e, 0xc09194a0, 0x64c859c8, 0xf5593166, 0x07edf94f, 0x967c91e1, 0xe384681c, 0x721500b2, 0x80a1c89b, 0x1130a035, 0x2b574abb, 0xbac62215, 0x4872ea3c, 0xd9e38292, 0xac1b7b6f, 0x3d8a13c1, 0xcf3edbe8, 0x5eafb346, 0x878d4139, 0x161c2997, 0xe4a8e1be, 0x75398910, 0x00c170ed, 0x91501843, 0x63e4d06a, 0xf275b8c4, 0xc812524a, 0x59833ae4, 0xab37f2cd, 0x3aa69a63, 0x4f5e639e, 0xdecf0b30, 0x2c7bc319, 0xbdeaabb7, 0x19b366df, 0x88220e71, 0x7a96c658, 0xeb07aef6, 0x9eff570b, 0x0f6e3fa5, 0xfddaf78c, 0x6c4b9f22, 0x562c75ac, 0xc7bd1d02, 0x3509d52b, 0xa498bd85, 0xd1604478, 0x40f12cd6, 0xb245e4ff, 0x23d48c51, 0xf4edfd5c, 0x657c95f2, 0x97c85ddb, 0x06593575, 0x73a1cc88, 0xe230a426, 0x10846c0f, 0x811504a1, 0xbb72ee2f, 0x2ae38681, 0xd8574ea8, 0x49c62606, 0x3c3edffb, 0xadafb755, 0x5f1b7f7c, 0xce8a17d2, 0x6ad3daba, 0xfb42b214, 0x09f67a3d, 0x98671293, 0xed9feb6e, 0x7c0e83c0, 0x8eba4be9, 0x1f2b2347, 0x254cc9c9, 0xb4dda167, 0x4669694e, 0xd7f801e0, 0xa200f81d, 0x339190b3, 0xc125589a, 0x50b43034, 0x8996c24b, 0x1807aae5, 0xeab362cc, 0x7b220a62, 0x0edaf39f, 0x9f4b9b31, 0x6dff5318, 0xfc6e3bb6, 0xc609d138, 0x5798b996, 0xa52c71bf, 0x34bd1911, 0x4145e0ec, 0xd0d48842, 0x2260406b, 0xb3f128c5, 0x17a8e5ad, 0x86398d03, 0x748d452a, 0xe51c2d84, 0x90e4d479, 0x0175bcd7, 0xf3c174fe, 0x62501c50, 0x5837f6de, 0xc9a69e70, 0x3b125659, 0xaa833ef7, 0xdf7bc70a, 0x4eeaafa4, 0xbc5e678d, 0x2dcf0f23, 0x0e1b8372, 0x9f8aebdc, 0x6d3e23f5, 0xfcaf4b5b, 0x8957b2a6, 0x18c6da08, 0xea721221, 0x7be37a8f, 0x41849001, 0xd015f8af, 0x22a13086, 0xb3305828, 0xc6c8a1d5, 0x5759c97b, 0xa5ed0152, 0x347c69fc, 0x9025a494, 0x01b4cc3a, 0xf3000413, 0x62916cbd, 0x17699540, 0x86f8fdee, 0x744c35c7, 0xe5dd5d69, 0xdfbab7e7, 0x4e2bdf49, 0xbc9f1760, 0x2d0e7fce, 0x58f68633, 0xc967ee9d, 0x3bd326b4, 0xaa424e1a, 0x7360bc65, 0xe2f1d4cb, 0x10451ce2, 0x81d4744c, 0xf42c8db1, 0x65bde51f, 0x97092d36, 0x06984598, 0x3cffaf16, 0xad6ec7b8, 0x5fda0f91, 0xce4b673f, 0xbbb39ec2, 0x2a22f66c, 0xd8963e45, 0x490756eb, 0xed5e9b83, 0x7ccff32d, 0x8e7b3b04, 0x1fea53aa, 0x6a12aa57, 0xfb83c2f9, 0x09370ad0, 0x98a6627e, 0xa2c188f0, 0x3350e05e, 0xc1e42877, 0x507540d9, 0x258db924, 0xb41cd18a, 0x46a819a3, 0xd739710d}}; #endif #endif #if N == 5 #if W == 8 local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0xaf449247, 0x85f822cf, 0x2abcb088, 0xd08143df, 0x7fc5d198, 0x55796110, 0xfa3df357, 0x7a7381ff, 0xd53713b8, 0xff8ba330, 0x50cf3177, 0xaaf2c220, 0x05b65067, 0x2f0ae0ef, 0x804e72a8, 0xf4e703fe, 0x5ba391b9, 0x711f2131, 0xde5bb376, 0x24664021, 0x8b22d266, 0xa19e62ee, 0x0edaf0a9, 0x8e948201, 0x21d01046, 0x0b6ca0ce, 0xa4283289, 0x5e15c1de, 0xf1515399, 0xdbede311, 0x74a97156, 0x32bf01bd, 0x9dfb93fa, 0xb7472372, 0x1803b135, 0xe23e4262, 0x4d7ad025, 0x67c660ad, 0xc882f2ea, 0x48cc8042, 0xe7881205, 0xcd34a28d, 0x627030ca, 0x984dc39d, 0x370951da, 0x1db5e152, 0xb2f17315, 0xc6580243, 0x691c9004, 0x43a0208c, 0xece4b2cb, 0x16d9419c, 0xb99dd3db, 0x93216353, 0x3c65f114, 0xbc2b83bc, 0x136f11fb, 0x39d3a173, 0x96973334, 0x6caac063, 0xc3ee5224, 0xe952e2ac, 0x461670eb, 0x657e037a, 0xca3a913d, 0xe08621b5, 0x4fc2b3f2, 0xb5ff40a5, 0x1abbd2e2, 0x3007626a, 0x9f43f02d, 0x1f0d8285, 0xb04910c2, 0x9af5a04a, 0x35b1320d, 0xcf8cc15a, 0x60c8531d, 0x4a74e395, 0xe53071d2, 0x91990084, 0x3edd92c3, 0x1461224b, 0xbb25b00c, 0x4118435b, 0xee5cd11c, 0xc4e06194, 0x6ba4f3d3, 0xebea817b, 0x44ae133c, 0x6e12a3b4, 0xc15631f3, 0x3b6bc2a4, 0x942f50e3, 0xbe93e06b, 0x11d7722c, 0x57c102c7, 0xf8859080, 0xd2392008, 0x7d7db24f, 0x87404118, 0x2804d35f, 0x02b863d7, 0xadfcf190, 0x2db28338, 0x82f6117f, 0xa84aa1f7, 0x070e33b0, 0xfd33c0e7, 0x527752a0, 0x78cbe228, 0xd78f706f, 0xa3260139, 0x0c62937e, 0x26de23f6, 0x899ab1b1, 0x73a742e6, 0xdce3d0a1, 0xf65f6029, 0x591bf26e, 0xd95580c6, 0x76111281, 0x5cada209, 0xf3e9304e, 0x09d4c319, 0xa690515e, 0x8c2ce1d6, 0x23687391, 0xcafc06f4, 0x65b894b3, 0x4f04243b, 0xe040b67c, 0x1a7d452b, 0xb539d76c, 0x9f8567e4, 0x30c1f5a3, 0xb08f870b, 0x1fcb154c, 0x3577a5c4, 0x9a333783, 0x600ec4d4, 0xcf4a5693, 0xe5f6e61b, 0x4ab2745c, 0x3e1b050a, 0x915f974d, 0xbbe327c5, 0x14a7b582, 0xee9a46d5, 0x41ded492, 0x6b62641a, 0xc426f65d, 0x446884f5, 0xeb2c16b2, 0xc190a63a, 0x6ed4347d, 0x94e9c72a, 0x3bad556d, 0x1111e5e5, 0xbe5577a2, 0xf8430749, 0x5707950e, 0x7dbb2586, 0xd2ffb7c1, 0x28c24496, 0x8786d6d1, 0xad3a6659, 0x027ef41e, 0x823086b6, 0x2d7414f1, 0x07c8a479, 0xa88c363e, 0x52b1c569, 0xfdf5572e, 0xd749e7a6, 0x780d75e1, 0x0ca404b7, 0xa3e096f0, 0x895c2678, 0x2618b43f, 0xdc254768, 0x7361d52f, 0x59dd65a7, 0xf699f7e0, 0x76d78548, 0xd993170f, 0xf32fa787, 0x5c6b35c0, 0xa656c697, 0x091254d0, 0x23aee458, 0x8cea761f, 0xaf82058e, 0x00c697c9, 0x2a7a2741, 0x853eb506, 0x7f034651, 0xd047d416, 0xfafb649e, 0x55bff6d9, 0xd5f18471, 0x7ab51636, 0x5009a6be, 0xff4d34f9, 0x0570c7ae, 0xaa3455e9, 0x8088e561, 0x2fcc7726, 0x5b650670, 0xf4219437, 0xde9d24bf, 0x71d9b6f8, 0x8be445af, 0x24a0d7e8, 0x0e1c6760, 0xa158f527, 0x2116878f, 0x8e5215c8, 0xa4eea540, 0x0baa3707, 0xf197c450, 0x5ed35617, 0x746fe69f, 0xdb2b74d8, 0x9d3d0433, 0x32799674, 0x18c526fc, 0xb781b4bb, 0x4dbc47ec, 0xe2f8d5ab, 0xc8446523, 0x6700f764, 0xe74e85cc, 0x480a178b, 0x62b6a703, 0xcdf23544, 0x37cfc613, 0x988b5454, 0xb237e4dc, 0x1d73769b, 0x69da07cd, 0xc69e958a, 0xec222502, 0x4366b745, 0xb95b4412, 0x161fd655, 0x3ca366dd, 0x93e7f49a, 0x13a98632, 0xbced1475, 0x9651a4fd, 0x391536ba, 0xc328c5ed, 0x6c6c57aa, 0x46d0e722, 0xe9947565}, {0x00000000, 0x4e890ba9, 0x9d121752, 0xd39b1cfb, 0xe15528e5, 0xafdc234c, 0x7c473fb7, 0x32ce341e, 0x19db578b, 0x57525c22, 0x84c940d9, 0xca404b70, 0xf88e7f6e, 0xb60774c7, 0x659c683c, 0x2b156395, 0x33b6af16, 0x7d3fa4bf, 0xaea4b844, 0xe02db3ed, 0xd2e387f3, 0x9c6a8c5a, 0x4ff190a1, 0x01789b08, 0x2a6df89d, 0x64e4f334, 0xb77fefcf, 0xf9f6e466, 0xcb38d078, 0x85b1dbd1, 0x562ac72a, 0x18a3cc83, 0x676d5e2c, 0x29e45585, 0xfa7f497e, 0xb4f642d7, 0x863876c9, 0xc8b17d60, 0x1b2a619b, 0x55a36a32, 0x7eb609a7, 0x303f020e, 0xe3a41ef5, 0xad2d155c, 0x9fe32142, 0xd16a2aeb, 0x02f13610, 0x4c783db9, 0x54dbf13a, 0x1a52fa93, 0xc9c9e668, 0x8740edc1, 0xb58ed9df, 0xfb07d276, 0x289cce8d, 0x6615c524, 0x4d00a6b1, 0x0389ad18, 0xd012b1e3, 0x9e9bba4a, 0xac558e54, 0xe2dc85fd, 0x31479906, 0x7fce92af, 0xcedabc58, 0x8053b7f1, 0x53c8ab0a, 0x1d41a0a3, 0x2f8f94bd, 0x61069f14, 0xb29d83ef, 0xfc148846, 0xd701ebd3, 0x9988e07a, 0x4a13fc81, 0x049af728, 0x3654c336, 0x78ddc89f, 0xab46d464, 0xe5cfdfcd, 0xfd6c134e, 0xb3e518e7, 0x607e041c, 0x2ef70fb5, 0x1c393bab, 0x52b03002, 0x812b2cf9, 0xcfa22750, 0xe4b744c5, 0xaa3e4f6c, 0x79a55397, 0x372c583e, 0x05e26c20, 0x4b6b6789, 0x98f07b72, 0xd67970db, 0xa9b7e274, 0xe73ee9dd, 0x34a5f526, 0x7a2cfe8f, 0x48e2ca91, 0x066bc138, 0xd5f0ddc3, 0x9b79d66a, 0xb06cb5ff, 0xfee5be56, 0x2d7ea2ad, 0x63f7a904, 0x51399d1a, 0x1fb096b3, 0xcc2b8a48, 0x82a281e1, 0x9a014d62, 0xd48846cb, 0x07135a30, 0x499a5199, 0x7b546587, 0x35dd6e2e, 0xe64672d5, 0xa8cf797c, 0x83da1ae9, 0xcd531140, 0x1ec80dbb, 0x50410612, 0x628f320c, 0x2c0639a5, 0xff9d255e, 0xb1142ef7, 0x46c47ef1, 0x084d7558, 0xdbd669a3, 0x955f620a, 0xa7915614, 0xe9185dbd, 0x3a834146, 0x740a4aef, 0x5f1f297a, 0x119622d3, 0xc20d3e28, 0x8c843581, 0xbe4a019f, 0xf0c30a36, 0x235816cd, 0x6dd11d64, 0x7572d1e7, 0x3bfbda4e, 0xe860c6b5, 0xa6e9cd1c, 0x9427f902, 0xdaaef2ab, 0x0935ee50, 0x47bce5f9, 0x6ca9866c, 0x22208dc5, 0xf1bb913e, 0xbf329a97, 0x8dfcae89, 0xc375a520, 0x10eeb9db, 0x5e67b272, 0x21a920dd, 0x6f202b74, 0xbcbb378f, 0xf2323c26, 0xc0fc0838, 0x8e750391, 0x5dee1f6a, 0x136714c3, 0x38727756, 0x76fb7cff, 0xa5606004, 0xebe96bad, 0xd9275fb3, 0x97ae541a, 0x443548e1, 0x0abc4348, 0x121f8fcb, 0x5c968462, 0x8f0d9899, 0xc1849330, 0xf34aa72e, 0xbdc3ac87, 0x6e58b07c, 0x20d1bbd5, 0x0bc4d840, 0x454dd3e9, 0x96d6cf12, 0xd85fc4bb, 0xea91f0a5, 0xa418fb0c, 0x7783e7f7, 0x390aec5e, 0x881ec2a9, 0xc697c900, 0x150cd5fb, 0x5b85de52, 0x694bea4c, 0x27c2e1e5, 0xf459fd1e, 0xbad0f6b7, 0x91c59522, 0xdf4c9e8b, 0x0cd78270, 0x425e89d9, 0x7090bdc7, 0x3e19b66e, 0xed82aa95, 0xa30ba13c, 0xbba86dbf, 0xf5216616, 0x26ba7aed, 0x68337144, 0x5afd455a, 0x14744ef3, 0xc7ef5208, 0x896659a1, 0xa2733a34, 0xecfa319d, 0x3f612d66, 0x71e826cf, 0x432612d1, 0x0daf1978, 0xde340583, 0x90bd0e2a, 0xef739c85, 0xa1fa972c, 0x72618bd7, 0x3ce8807e, 0x0e26b460, 0x40afbfc9, 0x9334a332, 0xddbda89b, 0xf6a8cb0e, 0xb821c0a7, 0x6bbadc5c, 0x2533d7f5, 0x17fde3eb, 0x5974e842, 0x8aeff4b9, 0xc466ff10, 0xdcc53393, 0x924c383a, 0x41d724c1, 0x0f5e2f68, 0x3d901b76, 0x731910df, 0xa0820c24, 0xee0b078d, 0xc51e6418, 0x8b976fb1, 0x580c734a, 0x168578e3, 0x244b4cfd, 0x6ac24754, 0xb9595baf, 0xf7d05006}, {0x00000000, 0x8d88fde2, 0xc060fd85, 0x4de80067, 0x5bb0fd4b, 0xd63800a9, 0x9bd000ce, 0x1658fd2c, 0xb761fa96, 0x3ae90774, 0x77010713, 0xfa89faf1, 0xecd107dd, 0x6159fa3f, 0x2cb1fa58, 0xa13907ba, 0xb5b2f36d, 0x383a0e8f, 0x75d20ee8, 0xf85af30a, 0xee020e26, 0x638af3c4, 0x2e62f3a3, 0xa3ea0e41, 0x02d309fb, 0x8f5bf419, 0xc2b3f47e, 0x4f3b099c, 0x5963f4b0, 0xd4eb0952, 0x99030935, 0x148bf4d7, 0xb014e09b, 0x3d9c1d79, 0x70741d1e, 0xfdfce0fc, 0xeba41dd0, 0x662ce032, 0x2bc4e055, 0xa64c1db7, 0x07751a0d, 0x8afde7ef, 0xc715e788, 0x4a9d1a6a, 0x5cc5e746, 0xd14d1aa4, 0x9ca51ac3, 0x112de721, 0x05a613f6, 0x882eee14, 0xc5c6ee73, 0x484e1391, 0x5e16eebd, 0xd39e135f, 0x9e761338, 0x13feeeda, 0xb2c7e960, 0x3f4f1482, 0x72a714e5, 0xff2fe907, 0xe977142b, 0x64ffe9c9, 0x2917e9ae, 0xa49f144c, 0xbb58c777, 0x36d03a95, 0x7b383af2, 0xf6b0c710, 0xe0e83a3c, 0x6d60c7de, 0x2088c7b9, 0xad003a5b, 0x0c393de1, 0x81b1c003, 0xcc59c064, 0x41d13d86, 0x5789c0aa, 0xda013d48, 0x97e93d2f, 0x1a61c0cd, 0x0eea341a, 0x8362c9f8, 0xce8ac99f, 0x4302347d, 0x555ac951, 0xd8d234b3, 0x953a34d4, 0x18b2c936, 0xb98bce8c, 0x3403336e, 0x79eb3309, 0xf463ceeb, 0xe23b33c7, 0x6fb3ce25, 0x225bce42, 0xafd333a0, 0x0b4c27ec, 0x86c4da0e, 0xcb2cda69, 0x46a4278b, 0x50fcdaa7, 0xdd742745, 0x909c2722, 0x1d14dac0, 0xbc2ddd7a, 0x31a52098, 0x7c4d20ff, 0xf1c5dd1d, 0xe79d2031, 0x6a15ddd3, 0x27fdddb4, 0xaa752056, 0xbefed481, 0x33762963, 0x7e9e2904, 0xf316d4e6, 0xe54e29ca, 0x68c6d428, 0x252ed44f, 0xa8a629ad, 0x099f2e17, 0x8417d3f5, 0xc9ffd392, 0x44772e70, 0x522fd35c, 0xdfa72ebe, 0x924f2ed9, 0x1fc7d33b, 0xadc088af, 0x2048754d, 0x6da0752a, 0xe02888c8, 0xf67075e4, 0x7bf88806, 0x36108861, 0xbb987583, 0x1aa17239, 0x97298fdb, 0xdac18fbc, 0x5749725e, 0x41118f72, 0xcc997290, 0x817172f7, 0x0cf98f15, 0x18727bc2, 0x95fa8620, 0xd8128647, 0x559a7ba5, 0x43c28689, 0xce4a7b6b, 0x83a27b0c, 0x0e2a86ee, 0xaf138154, 0x229b7cb6, 0x6f737cd1, 0xe2fb8133, 0xf4a37c1f, 0x792b81fd, 0x34c3819a, 0xb94b7c78, 0x1dd46834, 0x905c95d6, 0xddb495b1, 0x503c6853, 0x4664957f, 0xcbec689d, 0x860468fa, 0x0b8c9518, 0xaab592a2, 0x273d6f40, 0x6ad56f27, 0xe75d92c5, 0xf1056fe9, 0x7c8d920b, 0x3165926c, 0xbced6f8e, 0xa8669b59, 0x25ee66bb, 0x680666dc, 0xe58e9b3e, 0xf3d66612, 0x7e5e9bf0, 0x33b69b97, 0xbe3e6675, 0x1f0761cf, 0x928f9c2d, 0xdf679c4a, 0x52ef61a8, 0x44b79c84, 0xc93f6166, 0x84d76101, 0x095f9ce3, 0x16984fd8, 0x9b10b23a, 0xd6f8b25d, 0x5b704fbf, 0x4d28b293, 0xc0a04f71, 0x8d484f16, 0x00c0b2f4, 0xa1f9b54e, 0x2c7148ac, 0x619948cb, 0xec11b529, 0xfa494805, 0x77c1b5e7, 0x3a29b580, 0xb7a14862, 0xa32abcb5, 0x2ea24157, 0x634a4130, 0xeec2bcd2, 0xf89a41fe, 0x7512bc1c, 0x38fabc7b, 0xb5724199, 0x144b4623, 0x99c3bbc1, 0xd42bbba6, 0x59a34644, 0x4ffbbb68, 0xc273468a, 0x8f9b46ed, 0x0213bb0f, 0xa68caf43, 0x2b0452a1, 0x66ec52c6, 0xeb64af24, 0xfd3c5208, 0x70b4afea, 0x3d5caf8d, 0xb0d4526f, 0x11ed55d5, 0x9c65a837, 0xd18da850, 0x5c0555b2, 0x4a5da89e, 0xc7d5557c, 0x8a3d551b, 0x07b5a8f9, 0x133e5c2e, 0x9eb6a1cc, 0xd35ea1ab, 0x5ed65c49, 0x488ea165, 0xc5065c87, 0x88ee5ce0, 0x0566a102, 0xa45fa6b8, 0x29d75b5a, 0x643f5b3d, 0xe9b7a6df, 0xffef5bf3, 0x7267a611, 0x3f8fa676, 0xb2075b94}, {0x00000000, 0x80f0171f, 0xda91287f, 0x5a613f60, 0x6e5356bf, 0xeea341a0, 0xb4c27ec0, 0x343269df, 0xdca6ad7e, 0x5c56ba61, 0x06378501, 0x86c7921e, 0xb2f5fbc1, 0x3205ecde, 0x6864d3be, 0xe894c4a1, 0x623c5cbd, 0xe2cc4ba2, 0xb8ad74c2, 0x385d63dd, 0x0c6f0a02, 0x8c9f1d1d, 0xd6fe227d, 0x560e3562, 0xbe9af1c3, 0x3e6ae6dc, 0x640bd9bc, 0xe4fbcea3, 0xd0c9a77c, 0x5039b063, 0x0a588f03, 0x8aa8981c, 0xc478b97a, 0x4488ae65, 0x1ee99105, 0x9e19861a, 0xaa2befc5, 0x2adbf8da, 0x70bac7ba, 0xf04ad0a5, 0x18de1404, 0x982e031b, 0xc24f3c7b, 0x42bf2b64, 0x768d42bb, 0xf67d55a4, 0xac1c6ac4, 0x2cec7ddb, 0xa644e5c7, 0x26b4f2d8, 0x7cd5cdb8, 0xfc25daa7, 0xc817b378, 0x48e7a467, 0x12869b07, 0x92768c18, 0x7ae248b9, 0xfa125fa6, 0xa07360c6, 0x208377d9, 0x14b11e06, 0x94410919, 0xce203679, 0x4ed02166, 0x538074b5, 0xd37063aa, 0x89115cca, 0x09e14bd5, 0x3dd3220a, 0xbd233515, 0xe7420a75, 0x67b21d6a, 0x8f26d9cb, 0x0fd6ced4, 0x55b7f1b4, 0xd547e6ab, 0xe1758f74, 0x6185986b, 0x3be4a70b, 0xbb14b014, 0x31bc2808, 0xb14c3f17, 0xeb2d0077, 0x6bdd1768, 0x5fef7eb7, 0xdf1f69a8, 0x857e56c8, 0x058e41d7, 0xed1a8576, 0x6dea9269, 0x378bad09, 0xb77bba16, 0x8349d3c9, 0x03b9c4d6, 0x59d8fbb6, 0xd928eca9, 0x97f8cdcf, 0x1708dad0, 0x4d69e5b0, 0xcd99f2af, 0xf9ab9b70, 0x795b8c6f, 0x233ab30f, 0xa3caa410, 0x4b5e60b1, 0xcbae77ae, 0x91cf48ce, 0x113f5fd1, 0x250d360e, 0xa5fd2111, 0xff9c1e71, 0x7f6c096e, 0xf5c49172, 0x7534866d, 0x2f55b90d, 0xafa5ae12, 0x9b97c7cd, 0x1b67d0d2, 0x4106efb2, 0xc1f6f8ad, 0x29623c0c, 0xa9922b13, 0xf3f31473, 0x7303036c, 0x47316ab3, 0xc7c17dac, 0x9da042cc, 0x1d5055d3, 0xa700e96a, 0x27f0fe75, 0x7d91c115, 0xfd61d60a, 0xc953bfd5, 0x49a3a8ca, 0x13c297aa, 0x933280b5, 0x7ba64414, 0xfb56530b, 0xa1376c6b, 0x21c77b74, 0x15f512ab, 0x950505b4, 0xcf643ad4, 0x4f942dcb, 0xc53cb5d7, 0x45cca2c8, 0x1fad9da8, 0x9f5d8ab7, 0xab6fe368, 0x2b9ff477, 0x71fecb17, 0xf10edc08, 0x199a18a9, 0x996a0fb6, 0xc30b30d6, 0x43fb27c9, 0x77c94e16, 0xf7395909, 0xad586669, 0x2da87176, 0x63785010, 0xe388470f, 0xb9e9786f, 0x39196f70, 0x0d2b06af, 0x8ddb11b0, 0xd7ba2ed0, 0x574a39cf, 0xbfdefd6e, 0x3f2eea71, 0x654fd511, 0xe5bfc20e, 0xd18dabd1, 0x517dbcce, 0x0b1c83ae, 0x8bec94b1, 0x01440cad, 0x81b41bb2, 0xdbd524d2, 0x5b2533cd, 0x6f175a12, 0xefe74d0d, 0xb586726d, 0x35766572, 0xdde2a1d3, 0x5d12b6cc, 0x077389ac, 0x87839eb3, 0xb3b1f76c, 0x3341e073, 0x6920df13, 0xe9d0c80c, 0xf4809ddf, 0x74708ac0, 0x2e11b5a0, 0xaee1a2bf, 0x9ad3cb60, 0x1a23dc7f, 0x4042e31f, 0xc0b2f400, 0x282630a1, 0xa8d627be, 0xf2b718de, 0x72470fc1, 0x4675661e, 0xc6857101, 0x9ce44e61, 0x1c14597e, 0x96bcc162, 0x164cd67d, 0x4c2de91d, 0xccddfe02, 0xf8ef97dd, 0x781f80c2, 0x227ebfa2, 0xa28ea8bd, 0x4a1a6c1c, 0xcaea7b03, 0x908b4463, 0x107b537c, 0x24493aa3, 0xa4b92dbc, 0xfed812dc, 0x7e2805c3, 0x30f824a5, 0xb00833ba, 0xea690cda, 0x6a991bc5, 0x5eab721a, 0xde5b6505, 0x843a5a65, 0x04ca4d7a, 0xec5e89db, 0x6cae9ec4, 0x36cfa1a4, 0xb63fb6bb, 0x820ddf64, 0x02fdc87b, 0x589cf71b, 0xd86ce004, 0x52c47818, 0xd2346f07, 0x88555067, 0x08a54778, 0x3c972ea7, 0xbc6739b8, 0xe60606d8, 0x66f611c7, 0x8e62d566, 0x0e92c279, 0x54f3fd19, 0xd403ea06, 0xe03183d9, 0x60c194c6, 0x3aa0aba6, 0xba50bcb9}, {0x00000000, 0x9570d495, 0xf190af6b, 0x64e07bfe, 0x38505897, 0xad208c02, 0xc9c0f7fc, 0x5cb02369, 0x70a0b12e, 0xe5d065bb, 0x81301e45, 0x1440cad0, 0x48f0e9b9, 0xdd803d2c, 0xb96046d2, 0x2c109247, 0xe141625c, 0x7431b6c9, 0x10d1cd37, 0x85a119a2, 0xd9113acb, 0x4c61ee5e, 0x288195a0, 0xbdf14135, 0x91e1d372, 0x049107e7, 0x60717c19, 0xf501a88c, 0xa9b18be5, 0x3cc15f70, 0x5821248e, 0xcd51f01b, 0x19f3c2f9, 0x8c83166c, 0xe8636d92, 0x7d13b907, 0x21a39a6e, 0xb4d34efb, 0xd0333505, 0x4543e190, 0x695373d7, 0xfc23a742, 0x98c3dcbc, 0x0db30829, 0x51032b40, 0xc473ffd5, 0xa093842b, 0x35e350be, 0xf8b2a0a5, 0x6dc27430, 0x09220fce, 0x9c52db5b, 0xc0e2f832, 0x55922ca7, 0x31725759, 0xa40283cc, 0x8812118b, 0x1d62c51e, 0x7982bee0, 0xecf26a75, 0xb042491c, 0x25329d89, 0x41d2e677, 0xd4a232e2, 0x33e785f2, 0xa6975167, 0xc2772a99, 0x5707fe0c, 0x0bb7dd65, 0x9ec709f0, 0xfa27720e, 0x6f57a69b, 0x434734dc, 0xd637e049, 0xb2d79bb7, 0x27a74f22, 0x7b176c4b, 0xee67b8de, 0x8a87c320, 0x1ff717b5, 0xd2a6e7ae, 0x47d6333b, 0x233648c5, 0xb6469c50, 0xeaf6bf39, 0x7f866bac, 0x1b661052, 0x8e16c4c7, 0xa2065680, 0x37768215, 0x5396f9eb, 0xc6e62d7e, 0x9a560e17, 0x0f26da82, 0x6bc6a17c, 0xfeb675e9, 0x2a14470b, 0xbf64939e, 0xdb84e860, 0x4ef43cf5, 0x12441f9c, 0x8734cb09, 0xe3d4b0f7, 0x76a46462, 0x5ab4f625, 0xcfc422b0, 0xab24594e, 0x3e548ddb, 0x62e4aeb2, 0xf7947a27, 0x937401d9, 0x0604d54c, 0xcb552557, 0x5e25f1c2, 0x3ac58a3c, 0xafb55ea9, 0xf3057dc0, 0x6675a955, 0x0295d2ab, 0x97e5063e, 0xbbf59479, 0x2e8540ec, 0x4a653b12, 0xdf15ef87, 0x83a5ccee, 0x16d5187b, 0x72356385, 0xe745b710, 0x67cf0be4, 0xf2bfdf71, 0x965fa48f, 0x032f701a, 0x5f9f5373, 0xcaef87e6, 0xae0ffc18, 0x3b7f288d, 0x176fbaca, 0x821f6e5f, 0xe6ff15a1, 0x738fc134, 0x2f3fe25d, 0xba4f36c8, 0xdeaf4d36, 0x4bdf99a3, 0x868e69b8, 0x13febd2d, 0x771ec6d3, 0xe26e1246, 0xbede312f, 0x2baee5ba, 0x4f4e9e44, 0xda3e4ad1, 0xf62ed896, 0x635e0c03, 0x07be77fd, 0x92cea368, 0xce7e8001, 0x5b0e5494, 0x3fee2f6a, 0xaa9efbff, 0x7e3cc91d, 0xeb4c1d88, 0x8fac6676, 0x1adcb2e3, 0x466c918a, 0xd31c451f, 0xb7fc3ee1, 0x228cea74, 0x0e9c7833, 0x9becaca6, 0xff0cd758, 0x6a7c03cd, 0x36cc20a4, 0xa3bcf431, 0xc75c8fcf, 0x522c5b5a, 0x9f7dab41, 0x0a0d7fd4, 0x6eed042a, 0xfb9dd0bf, 0xa72df3d6, 0x325d2743, 0x56bd5cbd, 0xc3cd8828, 0xefdd1a6f, 0x7aadcefa, 0x1e4db504, 0x8b3d6191, 0xd78d42f8, 0x42fd966d, 0x261ded93, 0xb36d3906, 0x54288e16, 0xc1585a83, 0xa5b8217d, 0x30c8f5e8, 0x6c78d681, 0xf9080214, 0x9de879ea, 0x0898ad7f, 0x24883f38, 0xb1f8ebad, 0xd5189053, 0x406844c6, 0x1cd867af, 0x89a8b33a, 0xed48c8c4, 0x78381c51, 0xb569ec4a, 0x201938df, 0x44f94321, 0xd18997b4, 0x8d39b4dd, 0x18496048, 0x7ca91bb6, 0xe9d9cf23, 0xc5c95d64, 0x50b989f1, 0x3459f20f, 0xa129269a, 0xfd9905f3, 0x68e9d166, 0x0c09aa98, 0x99797e0d, 0x4ddb4cef, 0xd8ab987a, 0xbc4be384, 0x293b3711, 0x758b1478, 0xe0fbc0ed, 0x841bbb13, 0x116b6f86, 0x3d7bfdc1, 0xa80b2954, 0xcceb52aa, 0x599b863f, 0x052ba556, 0x905b71c3, 0xf4bb0a3d, 0x61cbdea8, 0xac9a2eb3, 0x39eafa26, 0x5d0a81d8, 0xc87a554d, 0x94ca7624, 0x01baa2b1, 0x655ad94f, 0xf02a0dda, 0xdc3a9f9d, 0x494a4b08, 0x2daa30f6, 0xb8dae463, 0xe46ac70a, 0x711a139f, 0x15fa6861, 0x808abcf4}, {0x00000000, 0xcf9e17c8, 0x444d29d1, 0x8bd33e19, 0x889a53a2, 0x4704446a, 0xccd77a73, 0x03496dbb, 0xca45a105, 0x05dbb6cd, 0x8e0888d4, 0x41969f1c, 0x42dff2a7, 0x8d41e56f, 0x0692db76, 0xc90cccbe, 0x4ffa444b, 0x80645383, 0x0bb76d9a, 0xc4297a52, 0xc76017e9, 0x08fe0021, 0x832d3e38, 0x4cb329f0, 0x85bfe54e, 0x4a21f286, 0xc1f2cc9f, 0x0e6cdb57, 0x0d25b6ec, 0xc2bba124, 0x49689f3d, 0x86f688f5, 0x9ff48896, 0x506a9f5e, 0xdbb9a147, 0x1427b68f, 0x176edb34, 0xd8f0ccfc, 0x5323f2e5, 0x9cbde52d, 0x55b12993, 0x9a2f3e5b, 0x11fc0042, 0xde62178a, 0xdd2b7a31, 0x12b56df9, 0x996653e0, 0x56f84428, 0xd00eccdd, 0x1f90db15, 0x9443e50c, 0x5bddf2c4, 0x58949f7f, 0x970a88b7, 0x1cd9b6ae, 0xd347a166, 0x1a4b6dd8, 0xd5d57a10, 0x5e064409, 0x919853c1, 0x92d13e7a, 0x5d4f29b2, 0xd69c17ab, 0x19020063, 0xe498176d, 0x2b0600a5, 0xa0d53ebc, 0x6f4b2974, 0x6c0244cf, 0xa39c5307, 0x284f6d1e, 0xe7d17ad6, 0x2eddb668, 0xe143a1a0, 0x6a909fb9, 0xa50e8871, 0xa647e5ca, 0x69d9f202, 0xe20acc1b, 0x2d94dbd3, 0xab625326, 0x64fc44ee, 0xef2f7af7, 0x20b16d3f, 0x23f80084, 0xec66174c, 0x67b52955, 0xa82b3e9d, 0x6127f223, 0xaeb9e5eb, 0x256adbf2, 0xeaf4cc3a, 0xe9bda181, 0x2623b649, 0xadf08850, 0x626e9f98, 0x7b6c9ffb, 0xb4f28833, 0x3f21b62a, 0xf0bfa1e2, 0xf3f6cc59, 0x3c68db91, 0xb7bbe588, 0x7825f240, 0xb1293efe, 0x7eb72936, 0xf564172f, 0x3afa00e7, 0x39b36d5c, 0xf62d7a94, 0x7dfe448d, 0xb2605345, 0x3496dbb0, 0xfb08cc78, 0x70dbf261, 0xbf45e5a9, 0xbc0c8812, 0x73929fda, 0xf841a1c3, 0x37dfb60b, 0xfed37ab5, 0x314d6d7d, 0xba9e5364, 0x750044ac, 0x76492917, 0xb9d73edf, 0x320400c6, 0xfd9a170e, 0x1241289b, 0xdddf3f53, 0x560c014a, 0x99921682, 0x9adb7b39, 0x55456cf1, 0xde9652e8, 0x11084520, 0xd804899e, 0x179a9e56, 0x9c49a04f, 0x53d7b787, 0x509eda3c, 0x9f00cdf4, 0x14d3f3ed, 0xdb4de425, 0x5dbb6cd0, 0x92257b18, 0x19f64501, 0xd66852c9, 0xd5213f72, 0x1abf28ba, 0x916c16a3, 0x5ef2016b, 0x97fecdd5, 0x5860da1d, 0xd3b3e404, 0x1c2df3cc, 0x1f649e77, 0xd0fa89bf, 0x5b29b7a6, 0x94b7a06e, 0x8db5a00d, 0x422bb7c5, 0xc9f889dc, 0x06669e14, 0x052ff3af, 0xcab1e467, 0x4162da7e, 0x8efccdb6, 0x47f00108, 0x886e16c0, 0x03bd28d9, 0xcc233f11, 0xcf6a52aa, 0x00f44562, 0x8b277b7b, 0x44b96cb3, 0xc24fe446, 0x0dd1f38e, 0x8602cd97, 0x499cda5f, 0x4ad5b7e4, 0x854ba02c, 0x0e989e35, 0xc10689fd, 0x080a4543, 0xc794528b, 0x4c476c92, 0x83d97b5a, 0x809016e1, 0x4f0e0129, 0xc4dd3f30, 0x0b4328f8, 0xf6d93ff6, 0x3947283e, 0xb2941627, 0x7d0a01ef, 0x7e436c54, 0xb1dd7b9c, 0x3a0e4585, 0xf590524d, 0x3c9c9ef3, 0xf302893b, 0x78d1b722, 0xb74fa0ea, 0xb406cd51, 0x7b98da99, 0xf04be480, 0x3fd5f348, 0xb9237bbd, 0x76bd6c75, 0xfd6e526c, 0x32f045a4, 0x31b9281f, 0xfe273fd7, 0x75f401ce, 0xba6a1606, 0x7366dab8, 0xbcf8cd70, 0x372bf369, 0xf8b5e4a1, 0xfbfc891a, 0x34629ed2, 0xbfb1a0cb, 0x702fb703, 0x692db760, 0xa6b3a0a8, 0x2d609eb1, 0xe2fe8979, 0xe1b7e4c2, 0x2e29f30a, 0xa5facd13, 0x6a64dadb, 0xa3681665, 0x6cf601ad, 0xe7253fb4, 0x28bb287c, 0x2bf245c7, 0xe46c520f, 0x6fbf6c16, 0xa0217bde, 0x26d7f32b, 0xe949e4e3, 0x629adafa, 0xad04cd32, 0xae4da089, 0x61d3b741, 0xea008958, 0x259e9e90, 0xec92522e, 0x230c45e6, 0xa8df7bff, 0x67416c37, 0x6408018c, 0xab961644, 0x2045285d, 0xefdb3f95}, {0x00000000, 0x24825136, 0x4904a26c, 0x6d86f35a, 0x920944d8, 0xb68b15ee, 0xdb0de6b4, 0xff8fb782, 0xff638ff1, 0xdbe1dec7, 0xb6672d9d, 0x92e57cab, 0x6d6acb29, 0x49e89a1f, 0x246e6945, 0x00ec3873, 0x25b619a3, 0x01344895, 0x6cb2bbcf, 0x4830eaf9, 0xb7bf5d7b, 0x933d0c4d, 0xfebbff17, 0xda39ae21, 0xdad59652, 0xfe57c764, 0x93d1343e, 0xb7536508, 0x48dcd28a, 0x6c5e83bc, 0x01d870e6, 0x255a21d0, 0x4b6c3346, 0x6fee6270, 0x0268912a, 0x26eac01c, 0xd965779e, 0xfde726a8, 0x9061d5f2, 0xb4e384c4, 0xb40fbcb7, 0x908ded81, 0xfd0b1edb, 0xd9894fed, 0x2606f86f, 0x0284a959, 0x6f025a03, 0x4b800b35, 0x6eda2ae5, 0x4a587bd3, 0x27de8889, 0x035cd9bf, 0xfcd36e3d, 0xd8513f0b, 0xb5d7cc51, 0x91559d67, 0x91b9a514, 0xb53bf422, 0xd8bd0778, 0xfc3f564e, 0x03b0e1cc, 0x2732b0fa, 0x4ab443a0, 0x6e361296, 0x96d8668c, 0xb25a37ba, 0xdfdcc4e0, 0xfb5e95d6, 0x04d12254, 0x20537362, 0x4dd58038, 0x6957d10e, 0x69bbe97d, 0x4d39b84b, 0x20bf4b11, 0x043d1a27, 0xfbb2ada5, 0xdf30fc93, 0xb2b60fc9, 0x96345eff, 0xb36e7f2f, 0x97ec2e19, 0xfa6add43, 0xdee88c75, 0x21673bf7, 0x05e56ac1, 0x6863999b, 0x4ce1c8ad, 0x4c0df0de, 0x688fa1e8, 0x050952b2, 0x218b0384, 0xde04b406, 0xfa86e530, 0x9700166a, 0xb382475c, 0xddb455ca, 0xf93604fc, 0x94b0f7a6, 0xb032a690, 0x4fbd1112, 0x6b3f4024, 0x06b9b37e, 0x223be248, 0x22d7da3b, 0x06558b0d, 0x6bd37857, 0x4f512961, 0xb0de9ee3, 0x945ccfd5, 0xf9da3c8f, 0xdd586db9, 0xf8024c69, 0xdc801d5f, 0xb106ee05, 0x9584bf33, 0x6a0b08b1, 0x4e895987, 0x230faadd, 0x078dfbeb, 0x0761c398, 0x23e392ae, 0x4e6561f4, 0x6ae730c2, 0x95688740, 0xb1ead676, 0xdc6c252c, 0xf8ee741a, 0xf6c1cb59, 0xd2439a6f, 0xbfc56935, 0x9b473803, 0x64c88f81, 0x404adeb7, 0x2dcc2ded, 0x094e7cdb, 0x09a244a8, 0x2d20159e, 0x40a6e6c4, 0x6424b7f2, 0x9bab0070, 0xbf295146, 0xd2afa21c, 0xf62df32a, 0xd377d2fa, 0xf7f583cc, 0x9a737096, 0xbef121a0, 0x417e9622, 0x65fcc714, 0x087a344e, 0x2cf86578, 0x2c145d0b, 0x08960c3d, 0x6510ff67, 0x4192ae51, 0xbe1d19d3, 0x9a9f48e5, 0xf719bbbf, 0xd39bea89, 0xbdadf81f, 0x992fa929, 0xf4a95a73, 0xd02b0b45, 0x2fa4bcc7, 0x0b26edf1, 0x66a01eab, 0x42224f9d, 0x42ce77ee, 0x664c26d8, 0x0bcad582, 0x2f4884b4, 0xd0c73336, 0xf4456200, 0x99c3915a, 0xbd41c06c, 0x981be1bc, 0xbc99b08a, 0xd11f43d0, 0xf59d12e6, 0x0a12a564, 0x2e90f452, 0x43160708, 0x6794563e, 0x67786e4d, 0x43fa3f7b, 0x2e7ccc21, 0x0afe9d17, 0xf5712a95, 0xd1f37ba3, 0xbc7588f9, 0x98f7d9cf, 0x6019add5, 0x449bfce3, 0x291d0fb9, 0x0d9f5e8f, 0xf210e90d, 0xd692b83b, 0xbb144b61, 0x9f961a57, 0x9f7a2224, 0xbbf87312, 0xd67e8048, 0xf2fcd17e, 0x0d7366fc, 0x29f137ca, 0x4477c490, 0x60f595a6, 0x45afb476, 0x612de540, 0x0cab161a, 0x2829472c, 0xd7a6f0ae, 0xf324a198, 0x9ea252c2, 0xba2003f4, 0xbacc3b87, 0x9e4e6ab1, 0xf3c899eb, 0xd74ac8dd, 0x28c57f5f, 0x0c472e69, 0x61c1dd33, 0x45438c05, 0x2b759e93, 0x0ff7cfa5, 0x62713cff, 0x46f36dc9, 0xb97cda4b, 0x9dfe8b7d, 0xf0787827, 0xd4fa2911, 0xd4161162, 0xf0944054, 0x9d12b30e, 0xb990e238, 0x461f55ba, 0x629d048c, 0x0f1bf7d6, 0x2b99a6e0, 0x0ec38730, 0x2a41d606, 0x47c7255c, 0x6345746a, 0x9ccac3e8, 0xb84892de, 0xd5ce6184, 0xf14c30b2, 0xf1a008c1, 0xd52259f7, 0xb8a4aaad, 0x9c26fb9b, 0x63a94c19, 0x472b1d2f, 0x2aadee75, 0x0e2fbf43}, {0x00000000, 0x36f290f3, 0x6de521e6, 0x5b17b115, 0xdbca43cc, 0xed38d33f, 0xb62f622a, 0x80ddf2d9, 0x6ce581d9, 0x5a17112a, 0x0100a03f, 0x37f230cc, 0xb72fc215, 0x81dd52e6, 0xdacae3f3, 0xec387300, 0xd9cb03b2, 0xef399341, 0xb42e2254, 0x82dcb2a7, 0x0201407e, 0x34f3d08d, 0x6fe46198, 0x5916f16b, 0xb52e826b, 0x83dc1298, 0xd8cba38d, 0xee39337e, 0x6ee4c1a7, 0x58165154, 0x0301e041, 0x35f370b2, 0x68e70125, 0x5e1591d6, 0x050220c3, 0x33f0b030, 0xb32d42e9, 0x85dfd21a, 0xdec8630f, 0xe83af3fc, 0x040280fc, 0x32f0100f, 0x69e7a11a, 0x5f1531e9, 0xdfc8c330, 0xe93a53c3, 0xb22de2d6, 0x84df7225, 0xb12c0297, 0x87de9264, 0xdcc92371, 0xea3bb382, 0x6ae6415b, 0x5c14d1a8, 0x070360bd, 0x31f1f04e, 0xddc9834e, 0xeb3b13bd, 0xb02ca2a8, 0x86de325b, 0x0603c082, 0x30f15071, 0x6be6e164, 0x5d147197, 0xd1ce024a, 0xe73c92b9, 0xbc2b23ac, 0x8ad9b35f, 0x0a044186, 0x3cf6d175, 0x67e16060, 0x5113f093, 0xbd2b8393, 0x8bd91360, 0xd0cea275, 0xe63c3286, 0x66e1c05f, 0x501350ac, 0x0b04e1b9, 0x3df6714a, 0x080501f8, 0x3ef7910b, 0x65e0201e, 0x5312b0ed, 0xd3cf4234, 0xe53dd2c7, 0xbe2a63d2, 0x88d8f321, 0x64e08021, 0x521210d2, 0x0905a1c7, 0x3ff73134, 0xbf2ac3ed, 0x89d8531e, 0xd2cfe20b, 0xe43d72f8, 0xb929036f, 0x8fdb939c, 0xd4cc2289, 0xe23eb27a, 0x62e340a3, 0x5411d050, 0x0f066145, 0x39f4f1b6, 0xd5cc82b6, 0xe33e1245, 0xb829a350, 0x8edb33a3, 0x0e06c17a, 0x38f45189, 0x63e3e09c, 0x5511706f, 0x60e200dd, 0x5610902e, 0x0d07213b, 0x3bf5b1c8, 0xbb284311, 0x8ddad3e2, 0xd6cd62f7, 0xe03ff204, 0x0c078104, 0x3af511f7, 0x61e2a0e2, 0x57103011, 0xd7cdc2c8, 0xe13f523b, 0xba28e32e, 0x8cda73dd, 0x78ed02d5, 0x4e1f9226, 0x15082333, 0x23fab3c0, 0xa3274119, 0x95d5d1ea, 0xcec260ff, 0xf830f00c, 0x1408830c, 0x22fa13ff, 0x79eda2ea, 0x4f1f3219, 0xcfc2c0c0, 0xf9305033, 0xa227e126, 0x94d571d5, 0xa1260167, 0x97d49194, 0xccc32081, 0xfa31b072, 0x7aec42ab, 0x4c1ed258, 0x1709634d, 0x21fbf3be, 0xcdc380be, 0xfb31104d, 0xa026a158, 0x96d431ab, 0x1609c372, 0x20fb5381, 0x7bece294, 0x4d1e7267, 0x100a03f0, 0x26f89303, 0x7def2216, 0x4b1db2e5, 0xcbc0403c, 0xfd32d0cf, 0xa62561da, 0x90d7f129, 0x7cef8229, 0x4a1d12da, 0x110aa3cf, 0x27f8333c, 0xa725c1e5, 0x91d75116, 0xcac0e003, 0xfc3270f0, 0xc9c10042, 0xff3390b1, 0xa42421a4, 0x92d6b157, 0x120b438e, 0x24f9d37d, 0x7fee6268, 0x491cf29b, 0xa524819b, 0x93d61168, 0xc8c1a07d, 0xfe33308e, 0x7eeec257, 0x481c52a4, 0x130be3b1, 0x25f97342, 0xa923009f, 0x9fd1906c, 0xc4c62179, 0xf234b18a, 0x72e94353, 0x441bd3a0, 0x1f0c62b5, 0x29fef246, 0xc5c68146, 0xf33411b5, 0xa823a0a0, 0x9ed13053, 0x1e0cc28a, 0x28fe5279, 0x73e9e36c, 0x451b739f, 0x70e8032d, 0x461a93de, 0x1d0d22cb, 0x2bffb238, 0xab2240e1, 0x9dd0d012, 0xc6c76107, 0xf035f1f4, 0x1c0d82f4, 0x2aff1207, 0x71e8a312, 0x471a33e1, 0xc7c7c138, 0xf13551cb, 0xaa22e0de, 0x9cd0702d, 0xc1c401ba, 0xf7369149, 0xac21205c, 0x9ad3b0af, 0x1a0e4276, 0x2cfcd285, 0x77eb6390, 0x4119f363, 0xad218063, 0x9bd31090, 0xc0c4a185, 0xf6363176, 0x76ebc3af, 0x4019535c, 0x1b0ee249, 0x2dfc72ba, 0x180f0208, 0x2efd92fb, 0x75ea23ee, 0x4318b31d, 0xc3c541c4, 0xf537d137, 0xae206022, 0x98d2f0d1, 0x74ea83d1, 0x42181322, 0x190fa237, 0x2ffd32c4, 0xaf20c01d, 0x99d250ee, 0xc2c5e1fb, 0xf4377108}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x0000000000000000, 0xf390f23600000000, 0xe621e56d00000000, 0x15b1175b00000000, 0xcc43cadb00000000, 0x3fd338ed00000000, 0x2a622fb600000000, 0xd9f2dd8000000000, 0xd981e56c00000000, 0x2a11175a00000000, 0x3fa0000100000000, 0xcc30f23700000000, 0x15c22fb700000000, 0xe652dd8100000000, 0xf3e3cada00000000, 0x007338ec00000000, 0xb203cbd900000000, 0x419339ef00000000, 0x54222eb400000000, 0xa7b2dc8200000000, 0x7e40010200000000, 0x8dd0f33400000000, 0x9861e46f00000000, 0x6bf1165900000000, 0x6b822eb500000000, 0x9812dc8300000000, 0x8da3cbd800000000, 0x7e3339ee00000000, 0xa7c1e46e00000000, 0x5451165800000000, 0x41e0010300000000, 0xb270f33500000000, 0x2501e76800000000, 0xd691155e00000000, 0xc320020500000000, 0x30b0f03300000000, 0xe9422db300000000, 0x1ad2df8500000000, 0x0f63c8de00000000, 0xfcf33ae800000000, 0xfc80020400000000, 0x0f10f03200000000, 0x1aa1e76900000000, 0xe931155f00000000, 0x30c3c8df00000000, 0xc3533ae900000000, 0xd6e22db200000000, 0x2572df8400000000, 0x97022cb100000000, 0x6492de8700000000, 0x7123c9dc00000000, 0x82b33bea00000000, 0x5b41e66a00000000, 0xa8d1145c00000000, 0xbd60030700000000, 0x4ef0f13100000000, 0x4e83c9dd00000000, 0xbd133beb00000000, 0xa8a22cb000000000, 0x5b32de8600000000, 0x82c0030600000000, 0x7150f13000000000, 0x64e1e66b00000000, 0x9771145d00000000, 0x4a02ced100000000, 0xb9923ce700000000, 0xac232bbc00000000, 0x5fb3d98a00000000, 0x8641040a00000000, 0x75d1f63c00000000, 0x6060e16700000000, 0x93f0135100000000, 0x93832bbd00000000, 0x6013d98b00000000, 0x75a2ced000000000, 0x86323ce600000000, 0x5fc0e16600000000, 0xac50135000000000, 0xb9e1040b00000000, 0x4a71f63d00000000, 0xf801050800000000, 0x0b91f73e00000000, 0x1e20e06500000000, 0xedb0125300000000, 0x3442cfd300000000, 0xc7d23de500000000, 0xd2632abe00000000, 0x21f3d88800000000, 0x2180e06400000000, 0xd210125200000000, 0xc7a1050900000000, 0x3431f73f00000000, 0xedc32abf00000000, 0x1e53d88900000000, 0x0be2cfd200000000, 0xf8723de400000000, 0x6f0329b900000000, 0x9c93db8f00000000, 0x8922ccd400000000, 0x7ab23ee200000000, 0xa340e36200000000, 0x50d0115400000000, 0x4561060f00000000, 0xb6f1f43900000000, 0xb682ccd500000000, 0x45123ee300000000, 0x50a329b800000000, 0xa333db8e00000000, 0x7ac1060e00000000, 0x8951f43800000000, 0x9ce0e36300000000, 0x6f70115500000000, 0xdd00e26000000000, 0x2e90105600000000, 0x3b21070d00000000, 0xc8b1f53b00000000, 0x114328bb00000000, 0xe2d3da8d00000000, 0xf762cdd600000000, 0x04f23fe000000000, 0x0481070c00000000, 0xf711f53a00000000, 0xe2a0e26100000000, 0x1130105700000000, 0xc8c2cdd700000000, 0x3b523fe100000000, 0x2ee328ba00000000, 0xdd73da8c00000000, 0xd502ed7800000000, 0x26921f4e00000000, 0x3323081500000000, 0xc0b3fa2300000000, 0x194127a300000000, 0xead1d59500000000, 0xff60c2ce00000000, 0x0cf030f800000000, 0x0c83081400000000, 0xff13fa2200000000, 0xeaa2ed7900000000, 0x19321f4f00000000, 0xc0c0c2cf00000000, 0x335030f900000000, 0x26e127a200000000, 0xd571d59400000000, 0x670126a100000000, 0x9491d49700000000, 0x8120c3cc00000000, 0x72b031fa00000000, 0xab42ec7a00000000, 0x58d21e4c00000000, 0x4d63091700000000, 0xbef3fb2100000000, 0xbe80c3cd00000000, 0x4d1031fb00000000, 0x58a126a000000000, 0xab31d49600000000, 0x72c3091600000000, 0x8153fb2000000000, 0x94e2ec7b00000000, 0x67721e4d00000000, 0xf0030a1000000000, 0x0393f82600000000, 0x1622ef7d00000000, 0xe5b21d4b00000000, 0x3c40c0cb00000000, 0xcfd032fd00000000, 0xda6125a600000000, 0x29f1d79000000000, 0x2982ef7c00000000, 0xda121d4a00000000, 0xcfa30a1100000000, 0x3c33f82700000000, 0xe5c125a700000000, 0x1651d79100000000, 0x03e0c0ca00000000, 0xf07032fc00000000, 0x4200c1c900000000, 0xb19033ff00000000, 0xa42124a400000000, 0x57b1d69200000000, 0x8e430b1200000000, 0x7dd3f92400000000, 0x6862ee7f00000000, 0x9bf21c4900000000, 0x9b8124a500000000, 0x6811d69300000000, 0x7da0c1c800000000, 0x8e3033fe00000000, 0x57c2ee7e00000000, 0xa4521c4800000000, 0xb1e30b1300000000, 0x4273f92500000000, 0x9f0023a900000000, 0x6c90d19f00000000, 0x7921c6c400000000, 0x8ab134f200000000, 0x5343e97200000000, 0xa0d31b4400000000, 0xb5620c1f00000000, 0x46f2fe2900000000, 0x4681c6c500000000, 0xb51134f300000000, 0xa0a023a800000000, 0x5330d19e00000000, 0x8ac20c1e00000000, 0x7952fe2800000000, 0x6ce3e97300000000, 0x9f731b4500000000, 0x2d03e87000000000, 0xde931a4600000000, 0xcb220d1d00000000, 0x38b2ff2b00000000, 0xe14022ab00000000, 0x12d0d09d00000000, 0x0761c7c600000000, 0xf4f135f000000000, 0xf4820d1c00000000, 0x0712ff2a00000000, 0x12a3e87100000000, 0xe1331a4700000000, 0x38c1c7c700000000, 0xcb5135f100000000, 0xdee022aa00000000, 0x2d70d09c00000000, 0xba01c4c100000000, 0x499136f700000000, 0x5c2021ac00000000, 0xafb0d39a00000000, 0x76420e1a00000000, 0x85d2fc2c00000000, 0x9063eb7700000000, 0x63f3194100000000, 0x638021ad00000000, 0x9010d39b00000000, 0x85a1c4c000000000, 0x763136f600000000, 0xafc3eb7600000000, 0x5c53194000000000, 0x49e20e1b00000000, 0xba72fc2d00000000, 0x08020f1800000000, 0xfb92fd2e00000000, 0xee23ea7500000000, 0x1db3184300000000, 0xc441c5c300000000, 0x37d137f500000000, 0x226020ae00000000, 0xd1f0d29800000000, 0xd183ea7400000000, 0x2213184200000000, 0x37a20f1900000000, 0xc432fd2f00000000, 0x1dc020af00000000, 0xee50d29900000000, 0xfbe1c5c200000000, 0x087137f400000000}, {0x0000000000000000, 0x3651822400000000, 0x6ca2044900000000, 0x5af3866d00000000, 0xd844099200000000, 0xee158bb600000000, 0xb4e60ddb00000000, 0x82b78fff00000000, 0xf18f63ff00000000, 0xc7dee1db00000000, 0x9d2d67b600000000, 0xab7ce59200000000, 0x29cb6a6d00000000, 0x1f9ae84900000000, 0x45696e2400000000, 0x7338ec0000000000, 0xa319b62500000000, 0x9548340100000000, 0xcfbbb26c00000000, 0xf9ea304800000000, 0x7b5dbfb700000000, 0x4d0c3d9300000000, 0x17ffbbfe00000000, 0x21ae39da00000000, 0x5296d5da00000000, 0x64c757fe00000000, 0x3e34d19300000000, 0x086553b700000000, 0x8ad2dc4800000000, 0xbc835e6c00000000, 0xe670d80100000000, 0xd0215a2500000000, 0x46336c4b00000000, 0x7062ee6f00000000, 0x2a91680200000000, 0x1cc0ea2600000000, 0x9e7765d900000000, 0xa826e7fd00000000, 0xf2d5619000000000, 0xc484e3b400000000, 0xb7bc0fb400000000, 0x81ed8d9000000000, 0xdb1e0bfd00000000, 0xed4f89d900000000, 0x6ff8062600000000, 0x59a9840200000000, 0x035a026f00000000, 0x350b804b00000000, 0xe52ada6e00000000, 0xd37b584a00000000, 0x8988de2700000000, 0xbfd95c0300000000, 0x3d6ed3fc00000000, 0x0b3f51d800000000, 0x51ccd7b500000000, 0x679d559100000000, 0x14a5b99100000000, 0x22f43bb500000000, 0x7807bdd800000000, 0x4e563ffc00000000, 0xcce1b00300000000, 0xfab0322700000000, 0xa043b44a00000000, 0x9612366e00000000, 0x8c66d89600000000, 0xba375ab200000000, 0xe0c4dcdf00000000, 0xd6955efb00000000, 0x5422d10400000000, 0x6273532000000000, 0x3880d54d00000000, 0x0ed1576900000000, 0x7de9bb6900000000, 0x4bb8394d00000000, 0x114bbf2000000000, 0x271a3d0400000000, 0xa5adb2fb00000000, 0x93fc30df00000000, 0xc90fb6b200000000, 0xff5e349600000000, 0x2f7f6eb300000000, 0x192eec9700000000, 0x43dd6afa00000000, 0x758ce8de00000000, 0xf73b672100000000, 0xc16ae50500000000, 0x9b99636800000000, 0xadc8e14c00000000, 0xdef00d4c00000000, 0xe8a18f6800000000, 0xb252090500000000, 0x84038b2100000000, 0x06b404de00000000, 0x30e586fa00000000, 0x6a16009700000000, 0x5c4782b300000000, 0xca55b4dd00000000, 0xfc0436f900000000, 0xa6f7b09400000000, 0x90a632b000000000, 0x1211bd4f00000000, 0x24403f6b00000000, 0x7eb3b90600000000, 0x48e23b2200000000, 0x3bdad72200000000, 0x0d8b550600000000, 0x5778d36b00000000, 0x6129514f00000000, 0xe39edeb000000000, 0xd5cf5c9400000000, 0x8f3cdaf900000000, 0xb96d58dd00000000, 0x694c02f800000000, 0x5f1d80dc00000000, 0x05ee06b100000000, 0x33bf849500000000, 0xb1080b6a00000000, 0x8759894e00000000, 0xddaa0f2300000000, 0xebfb8d0700000000, 0x98c3610700000000, 0xae92e32300000000, 0xf461654e00000000, 0xc230e76a00000000, 0x4087689500000000, 0x76d6eab100000000, 0x2c256cdc00000000, 0x1a74eef800000000, 0x59cbc1f600000000, 0x6f9a43d200000000, 0x3569c5bf00000000, 0x0338479b00000000, 0x818fc86400000000, 0xb7de4a4000000000, 0xed2dcc2d00000000, 0xdb7c4e0900000000, 0xa844a20900000000, 0x9e15202d00000000, 0xc4e6a64000000000, 0xf2b7246400000000, 0x7000ab9b00000000, 0x465129bf00000000, 0x1ca2afd200000000, 0x2af32df600000000, 0xfad277d300000000, 0xcc83f5f700000000, 0x9670739a00000000, 0xa021f1be00000000, 0x22967e4100000000, 0x14c7fc6500000000, 0x4e347a0800000000, 0x7865f82c00000000, 0x0b5d142c00000000, 0x3d0c960800000000, 0x67ff106500000000, 0x51ae924100000000, 0xd3191dbe00000000, 0xe5489f9a00000000, 0xbfbb19f700000000, 0x89ea9bd300000000, 0x1ff8adbd00000000, 0x29a92f9900000000, 0x735aa9f400000000, 0x450b2bd000000000, 0xc7bca42f00000000, 0xf1ed260b00000000, 0xab1ea06600000000, 0x9d4f224200000000, 0xee77ce4200000000, 0xd8264c6600000000, 0x82d5ca0b00000000, 0xb484482f00000000, 0x3633c7d000000000, 0x006245f400000000, 0x5a91c39900000000, 0x6cc041bd00000000, 0xbce11b9800000000, 0x8ab099bc00000000, 0xd0431fd100000000, 0xe6129df500000000, 0x64a5120a00000000, 0x52f4902e00000000, 0x0807164300000000, 0x3e56946700000000, 0x4d6e786700000000, 0x7b3ffa4300000000, 0x21cc7c2e00000000, 0x179dfe0a00000000, 0x952a71f500000000, 0xa37bf3d100000000, 0xf98875bc00000000, 0xcfd9f79800000000, 0xd5ad196000000000, 0xe3fc9b4400000000, 0xb90f1d2900000000, 0x8f5e9f0d00000000, 0x0de910f200000000, 0x3bb892d600000000, 0x614b14bb00000000, 0x571a969f00000000, 0x24227a9f00000000, 0x1273f8bb00000000, 0x48807ed600000000, 0x7ed1fcf200000000, 0xfc66730d00000000, 0xca37f12900000000, 0x90c4774400000000, 0xa695f56000000000, 0x76b4af4500000000, 0x40e52d6100000000, 0x1a16ab0c00000000, 0x2c47292800000000, 0xaef0a6d700000000, 0x98a124f300000000, 0xc252a29e00000000, 0xf40320ba00000000, 0x873bccba00000000, 0xb16a4e9e00000000, 0xeb99c8f300000000, 0xddc84ad700000000, 0x5f7fc52800000000, 0x692e470c00000000, 0x33ddc16100000000, 0x058c434500000000, 0x939e752b00000000, 0xa5cff70f00000000, 0xff3c716200000000, 0xc96df34600000000, 0x4bda7cb900000000, 0x7d8bfe9d00000000, 0x277878f000000000, 0x1129fad400000000, 0x621116d400000000, 0x544094f000000000, 0x0eb3129d00000000, 0x38e290b900000000, 0xba551f4600000000, 0x8c049d6200000000, 0xd6f71b0f00000000, 0xe0a6992b00000000, 0x3087c30e00000000, 0x06d6412a00000000, 0x5c25c74700000000, 0x6a74456300000000, 0xe8c3ca9c00000000, 0xde9248b800000000, 0x8461ced500000000, 0xb2304cf100000000, 0xc108a0f100000000, 0xf75922d500000000, 0xadaaa4b800000000, 0x9bfb269c00000000, 0x194ca96300000000, 0x2f1d2b4700000000, 0x75eead2a00000000, 0x43bf2f0e00000000}, {0x0000000000000000, 0xc8179ecf00000000, 0xd1294d4400000000, 0x193ed38b00000000, 0xa2539a8800000000, 0x6a44044700000000, 0x737ad7cc00000000, 0xbb6d490300000000, 0x05a145ca00000000, 0xcdb6db0500000000, 0xd488088e00000000, 0x1c9f964100000000, 0xa7f2df4200000000, 0x6fe5418d00000000, 0x76db920600000000, 0xbecc0cc900000000, 0x4b44fa4f00000000, 0x8353648000000000, 0x9a6db70b00000000, 0x527a29c400000000, 0xe91760c700000000, 0x2100fe0800000000, 0x383e2d8300000000, 0xf029b34c00000000, 0x4ee5bf8500000000, 0x86f2214a00000000, 0x9fccf2c100000000, 0x57db6c0e00000000, 0xecb6250d00000000, 0x24a1bbc200000000, 0x3d9f684900000000, 0xf588f68600000000, 0x9688f49f00000000, 0x5e9f6a5000000000, 0x47a1b9db00000000, 0x8fb6271400000000, 0x34db6e1700000000, 0xfcccf0d800000000, 0xe5f2235300000000, 0x2de5bd9c00000000, 0x9329b15500000000, 0x5b3e2f9a00000000, 0x4200fc1100000000, 0x8a1762de00000000, 0x317a2bdd00000000, 0xf96db51200000000, 0xe053669900000000, 0x2844f85600000000, 0xddcc0ed000000000, 0x15db901f00000000, 0x0ce5439400000000, 0xc4f2dd5b00000000, 0x7f9f945800000000, 0xb7880a9700000000, 0xaeb6d91c00000000, 0x66a147d300000000, 0xd86d4b1a00000000, 0x107ad5d500000000, 0x0944065e00000000, 0xc153989100000000, 0x7a3ed19200000000, 0xb2294f5d00000000, 0xab179cd600000000, 0x6300021900000000, 0x6d1798e400000000, 0xa500062b00000000, 0xbc3ed5a000000000, 0x74294b6f00000000, 0xcf44026c00000000, 0x07539ca300000000, 0x1e6d4f2800000000, 0xd67ad1e700000000, 0x68b6dd2e00000000, 0xa0a143e100000000, 0xb99f906a00000000, 0x71880ea500000000, 0xcae547a600000000, 0x02f2d96900000000, 0x1bcc0ae200000000, 0xd3db942d00000000, 0x265362ab00000000, 0xee44fc6400000000, 0xf77a2fef00000000, 0x3f6db12000000000, 0x8400f82300000000, 0x4c1766ec00000000, 0x5529b56700000000, 0x9d3e2ba800000000, 0x23f2276100000000, 0xebe5b9ae00000000, 0xf2db6a2500000000, 0x3accf4ea00000000, 0x81a1bde900000000, 0x49b6232600000000, 0x5088f0ad00000000, 0x989f6e6200000000, 0xfb9f6c7b00000000, 0x3388f2b400000000, 0x2ab6213f00000000, 0xe2a1bff000000000, 0x59ccf6f300000000, 0x91db683c00000000, 0x88e5bbb700000000, 0x40f2257800000000, 0xfe3e29b100000000, 0x3629b77e00000000, 0x2f1764f500000000, 0xe700fa3a00000000, 0x5c6db33900000000, 0x947a2df600000000, 0x8d44fe7d00000000, 0x455360b200000000, 0xb0db963400000000, 0x78cc08fb00000000, 0x61f2db7000000000, 0xa9e545bf00000000, 0x12880cbc00000000, 0xda9f927300000000, 0xc3a141f800000000, 0x0bb6df3700000000, 0xb57ad3fe00000000, 0x7d6d4d3100000000, 0x64539eba00000000, 0xac44007500000000, 0x1729497600000000, 0xdf3ed7b900000000, 0xc600043200000000, 0x0e179afd00000000, 0x9b28411200000000, 0x533fdfdd00000000, 0x4a010c5600000000, 0x8216929900000000, 0x397bdb9a00000000, 0xf16c455500000000, 0xe85296de00000000, 0x2045081100000000, 0x9e8904d800000000, 0x569e9a1700000000, 0x4fa0499c00000000, 0x87b7d75300000000, 0x3cda9e5000000000, 0xf4cd009f00000000, 0xedf3d31400000000, 0x25e44ddb00000000, 0xd06cbb5d00000000, 0x187b259200000000, 0x0145f61900000000, 0xc95268d600000000, 0x723f21d500000000, 0xba28bf1a00000000, 0xa3166c9100000000, 0x6b01f25e00000000, 0xd5cdfe9700000000, 0x1dda605800000000, 0x04e4b3d300000000, 0xccf32d1c00000000, 0x779e641f00000000, 0xbf89fad000000000, 0xa6b7295b00000000, 0x6ea0b79400000000, 0x0da0b58d00000000, 0xc5b72b4200000000, 0xdc89f8c900000000, 0x149e660600000000, 0xaff32f0500000000, 0x67e4b1ca00000000, 0x7eda624100000000, 0xb6cdfc8e00000000, 0x0801f04700000000, 0xc0166e8800000000, 0xd928bd0300000000, 0x113f23cc00000000, 0xaa526acf00000000, 0x6245f40000000000, 0x7b7b278b00000000, 0xb36cb94400000000, 0x46e44fc200000000, 0x8ef3d10d00000000, 0x97cd028600000000, 0x5fda9c4900000000, 0xe4b7d54a00000000, 0x2ca04b8500000000, 0x359e980e00000000, 0xfd8906c100000000, 0x43450a0800000000, 0x8b5294c700000000, 0x926c474c00000000, 0x5a7bd98300000000, 0xe116908000000000, 0x29010e4f00000000, 0x303fddc400000000, 0xf828430b00000000, 0xf63fd9f600000000, 0x3e28473900000000, 0x271694b200000000, 0xef010a7d00000000, 0x546c437e00000000, 0x9c7bddb100000000, 0x85450e3a00000000, 0x4d5290f500000000, 0xf39e9c3c00000000, 0x3b8902f300000000, 0x22b7d17800000000, 0xeaa04fb700000000, 0x51cd06b400000000, 0x99da987b00000000, 0x80e44bf000000000, 0x48f3d53f00000000, 0xbd7b23b900000000, 0x756cbd7600000000, 0x6c526efd00000000, 0xa445f03200000000, 0x1f28b93100000000, 0xd73f27fe00000000, 0xce01f47500000000, 0x06166aba00000000, 0xb8da667300000000, 0x70cdf8bc00000000, 0x69f32b3700000000, 0xa1e4b5f800000000, 0x1a89fcfb00000000, 0xd29e623400000000, 0xcba0b1bf00000000, 0x03b72f7000000000, 0x60b72d6900000000, 0xa8a0b3a600000000, 0xb19e602d00000000, 0x7989fee200000000, 0xc2e4b7e100000000, 0x0af3292e00000000, 0x13cdfaa500000000, 0xdbda646a00000000, 0x651668a300000000, 0xad01f66c00000000, 0xb43f25e700000000, 0x7c28bb2800000000, 0xc745f22b00000000, 0x0f526ce400000000, 0x166cbf6f00000000, 0xde7b21a000000000, 0x2bf3d72600000000, 0xe3e449e900000000, 0xfada9a6200000000, 0x32cd04ad00000000, 0x89a04dae00000000, 0x41b7d36100000000, 0x588900ea00000000, 0x909e9e2500000000, 0x2e5292ec00000000, 0xe6450c2300000000, 0xff7bdfa800000000, 0x376c416700000000, 0x8c01086400000000, 0x441696ab00000000, 0x5d28452000000000, 0x953fdbef00000000}, {0x0000000000000000, 0x95d4709500000000, 0x6baf90f100000000, 0xfe7be06400000000, 0x9758503800000000, 0x028c20ad00000000, 0xfcf7c0c900000000, 0x6923b05c00000000, 0x2eb1a07000000000, 0xbb65d0e500000000, 0x451e308100000000, 0xd0ca401400000000, 0xb9e9f04800000000, 0x2c3d80dd00000000, 0xd24660b900000000, 0x4792102c00000000, 0x5c6241e100000000, 0xc9b6317400000000, 0x37cdd11000000000, 0xa219a18500000000, 0xcb3a11d900000000, 0x5eee614c00000000, 0xa095812800000000, 0x3541f1bd00000000, 0x72d3e19100000000, 0xe707910400000000, 0x197c716000000000, 0x8ca801f500000000, 0xe58bb1a900000000, 0x705fc13c00000000, 0x8e24215800000000, 0x1bf051cd00000000, 0xf9c2f31900000000, 0x6c16838c00000000, 0x926d63e800000000, 0x07b9137d00000000, 0x6e9aa32100000000, 0xfb4ed3b400000000, 0x053533d000000000, 0x90e1434500000000, 0xd773536900000000, 0x42a723fc00000000, 0xbcdcc39800000000, 0x2908b30d00000000, 0x402b035100000000, 0xd5ff73c400000000, 0x2b8493a000000000, 0xbe50e33500000000, 0xa5a0b2f800000000, 0x3074c26d00000000, 0xce0f220900000000, 0x5bdb529c00000000, 0x32f8e2c000000000, 0xa72c925500000000, 0x5957723100000000, 0xcc8302a400000000, 0x8b11128800000000, 0x1ec5621d00000000, 0xe0be827900000000, 0x756af2ec00000000, 0x1c4942b000000000, 0x899d322500000000, 0x77e6d24100000000, 0xe232a2d400000000, 0xf285e73300000000, 0x675197a600000000, 0x992a77c200000000, 0x0cfe075700000000, 0x65ddb70b00000000, 0xf009c79e00000000, 0x0e7227fa00000000, 0x9ba6576f00000000, 0xdc34474300000000, 0x49e037d600000000, 0xb79bd7b200000000, 0x224fa72700000000, 0x4b6c177b00000000, 0xdeb867ee00000000, 0x20c3878a00000000, 0xb517f71f00000000, 0xaee7a6d200000000, 0x3b33d64700000000, 0xc548362300000000, 0x509c46b600000000, 0x39bff6ea00000000, 0xac6b867f00000000, 0x5210661b00000000, 0xc7c4168e00000000, 0x805606a200000000, 0x1582763700000000, 0xebf9965300000000, 0x7e2de6c600000000, 0x170e569a00000000, 0x82da260f00000000, 0x7ca1c66b00000000, 0xe975b6fe00000000, 0x0b47142a00000000, 0x9e9364bf00000000, 0x60e884db00000000, 0xf53cf44e00000000, 0x9c1f441200000000, 0x09cb348700000000, 0xf7b0d4e300000000, 0x6264a47600000000, 0x25f6b45a00000000, 0xb022c4cf00000000, 0x4e5924ab00000000, 0xdb8d543e00000000, 0xb2aee46200000000, 0x277a94f700000000, 0xd901749300000000, 0x4cd5040600000000, 0x572555cb00000000, 0xc2f1255e00000000, 0x3c8ac53a00000000, 0xa95eb5af00000000, 0xc07d05f300000000, 0x55a9756600000000, 0xabd2950200000000, 0x3e06e59700000000, 0x7994f5bb00000000, 0xec40852e00000000, 0x123b654a00000000, 0x87ef15df00000000, 0xeecca58300000000, 0x7b18d51600000000, 0x8563357200000000, 0x10b745e700000000, 0xe40bcf6700000000, 0x71dfbff200000000, 0x8fa45f9600000000, 0x1a702f0300000000, 0x73539f5f00000000, 0xe687efca00000000, 0x18fc0fae00000000, 0x8d287f3b00000000, 0xcaba6f1700000000, 0x5f6e1f8200000000, 0xa115ffe600000000, 0x34c18f7300000000, 0x5de23f2f00000000, 0xc8364fba00000000, 0x364dafde00000000, 0xa399df4b00000000, 0xb8698e8600000000, 0x2dbdfe1300000000, 0xd3c61e7700000000, 0x46126ee200000000, 0x2f31debe00000000, 0xbae5ae2b00000000, 0x449e4e4f00000000, 0xd14a3eda00000000, 0x96d82ef600000000, 0x030c5e6300000000, 0xfd77be0700000000, 0x68a3ce9200000000, 0x01807ece00000000, 0x94540e5b00000000, 0x6a2fee3f00000000, 0xfffb9eaa00000000, 0x1dc93c7e00000000, 0x881d4ceb00000000, 0x7666ac8f00000000, 0xe3b2dc1a00000000, 0x8a916c4600000000, 0x1f451cd300000000, 0xe13efcb700000000, 0x74ea8c2200000000, 0x33789c0e00000000, 0xa6acec9b00000000, 0x58d70cff00000000, 0xcd037c6a00000000, 0xa420cc3600000000, 0x31f4bca300000000, 0xcf8f5cc700000000, 0x5a5b2c5200000000, 0x41ab7d9f00000000, 0xd47f0d0a00000000, 0x2a04ed6e00000000, 0xbfd09dfb00000000, 0xd6f32da700000000, 0x43275d3200000000, 0xbd5cbd5600000000, 0x2888cdc300000000, 0x6f1addef00000000, 0xfacead7a00000000, 0x04b54d1e00000000, 0x91613d8b00000000, 0xf8428dd700000000, 0x6d96fd4200000000, 0x93ed1d2600000000, 0x06396db300000000, 0x168e285400000000, 0x835a58c100000000, 0x7d21b8a500000000, 0xe8f5c83000000000, 0x81d6786c00000000, 0x140208f900000000, 0xea79e89d00000000, 0x7fad980800000000, 0x383f882400000000, 0xadebf8b100000000, 0x539018d500000000, 0xc644684000000000, 0xaf67d81c00000000, 0x3ab3a88900000000, 0xc4c848ed00000000, 0x511c387800000000, 0x4aec69b500000000, 0xdf38192000000000, 0x2143f94400000000, 0xb49789d100000000, 0xddb4398d00000000, 0x4860491800000000, 0xb61ba97c00000000, 0x23cfd9e900000000, 0x645dc9c500000000, 0xf189b95000000000, 0x0ff2593400000000, 0x9a2629a100000000, 0xf30599fd00000000, 0x66d1e96800000000, 0x98aa090c00000000, 0x0d7e799900000000, 0xef4cdb4d00000000, 0x7a98abd800000000, 0x84e34bbc00000000, 0x11373b2900000000, 0x78148b7500000000, 0xedc0fbe000000000, 0x13bb1b8400000000, 0x866f6b1100000000, 0xc1fd7b3d00000000, 0x54290ba800000000, 0xaa52ebcc00000000, 0x3f869b5900000000, 0x56a52b0500000000, 0xc3715b9000000000, 0x3d0abbf400000000, 0xa8decb6100000000, 0xb32e9aac00000000, 0x26faea3900000000, 0xd8810a5d00000000, 0x4d557ac800000000, 0x2476ca9400000000, 0xb1a2ba0100000000, 0x4fd95a6500000000, 0xda0d2af000000000, 0x9d9f3adc00000000, 0x084b4a4900000000, 0xf630aa2d00000000, 0x63e4dab800000000, 0x0ac76ae400000000, 0x9f131a7100000000, 0x6168fa1500000000, 0xf4bc8a8000000000}, {0x0000000000000000, 0x1f17f08000000000, 0x7f2891da00000000, 0x603f615a00000000, 0xbf56536e00000000, 0xa041a3ee00000000, 0xc07ec2b400000000, 0xdf69323400000000, 0x7eada6dc00000000, 0x61ba565c00000000, 0x0185370600000000, 0x1e92c78600000000, 0xc1fbf5b200000000, 0xdeec053200000000, 0xbed3646800000000, 0xa1c494e800000000, 0xbd5c3c6200000000, 0xa24bcce200000000, 0xc274adb800000000, 0xdd635d3800000000, 0x020a6f0c00000000, 0x1d1d9f8c00000000, 0x7d22fed600000000, 0x62350e5600000000, 0xc3f19abe00000000, 0xdce66a3e00000000, 0xbcd90b6400000000, 0xa3cefbe400000000, 0x7ca7c9d000000000, 0x63b0395000000000, 0x038f580a00000000, 0x1c98a88a00000000, 0x7ab978c400000000, 0x65ae884400000000, 0x0591e91e00000000, 0x1a86199e00000000, 0xc5ef2baa00000000, 0xdaf8db2a00000000, 0xbac7ba7000000000, 0xa5d04af000000000, 0x0414de1800000000, 0x1b032e9800000000, 0x7b3c4fc200000000, 0x642bbf4200000000, 0xbb428d7600000000, 0xa4557df600000000, 0xc46a1cac00000000, 0xdb7dec2c00000000, 0xc7e544a600000000, 0xd8f2b42600000000, 0xb8cdd57c00000000, 0xa7da25fc00000000, 0x78b317c800000000, 0x67a4e74800000000, 0x079b861200000000, 0x188c769200000000, 0xb948e27a00000000, 0xa65f12fa00000000, 0xc66073a000000000, 0xd977832000000000, 0x061eb11400000000, 0x1909419400000000, 0x793620ce00000000, 0x6621d04e00000000, 0xb574805300000000, 0xaa6370d300000000, 0xca5c118900000000, 0xd54be10900000000, 0x0a22d33d00000000, 0x153523bd00000000, 0x750a42e700000000, 0x6a1db26700000000, 0xcbd9268f00000000, 0xd4ced60f00000000, 0xb4f1b75500000000, 0xabe647d500000000, 0x748f75e100000000, 0x6b98856100000000, 0x0ba7e43b00000000, 0x14b014bb00000000, 0x0828bc3100000000, 0x173f4cb100000000, 0x77002deb00000000, 0x6817dd6b00000000, 0xb77eef5f00000000, 0xa8691fdf00000000, 0xc8567e8500000000, 0xd7418e0500000000, 0x76851aed00000000, 0x6992ea6d00000000, 0x09ad8b3700000000, 0x16ba7bb700000000, 0xc9d3498300000000, 0xd6c4b90300000000, 0xb6fbd85900000000, 0xa9ec28d900000000, 0xcfcdf89700000000, 0xd0da081700000000, 0xb0e5694d00000000, 0xaff299cd00000000, 0x709babf900000000, 0x6f8c5b7900000000, 0x0fb33a2300000000, 0x10a4caa300000000, 0xb1605e4b00000000, 0xae77aecb00000000, 0xce48cf9100000000, 0xd15f3f1100000000, 0x0e360d2500000000, 0x1121fda500000000, 0x711e9cff00000000, 0x6e096c7f00000000, 0x7291c4f500000000, 0x6d86347500000000, 0x0db9552f00000000, 0x12aea5af00000000, 0xcdc7979b00000000, 0xd2d0671b00000000, 0xb2ef064100000000, 0xadf8f6c100000000, 0x0c3c622900000000, 0x132b92a900000000, 0x7314f3f300000000, 0x6c03037300000000, 0xb36a314700000000, 0xac7dc1c700000000, 0xcc42a09d00000000, 0xd355501d00000000, 0x6ae900a700000000, 0x75fef02700000000, 0x15c1917d00000000, 0x0ad661fd00000000, 0xd5bf53c900000000, 0xcaa8a34900000000, 0xaa97c21300000000, 0xb580329300000000, 0x1444a67b00000000, 0x0b5356fb00000000, 0x6b6c37a100000000, 0x747bc72100000000, 0xab12f51500000000, 0xb405059500000000, 0xd43a64cf00000000, 0xcb2d944f00000000, 0xd7b53cc500000000, 0xc8a2cc4500000000, 0xa89dad1f00000000, 0xb78a5d9f00000000, 0x68e36fab00000000, 0x77f49f2b00000000, 0x17cbfe7100000000, 0x08dc0ef100000000, 0xa9189a1900000000, 0xb60f6a9900000000, 0xd6300bc300000000, 0xc927fb4300000000, 0x164ec97700000000, 0x095939f700000000, 0x696658ad00000000, 0x7671a82d00000000, 0x1050786300000000, 0x0f4788e300000000, 0x6f78e9b900000000, 0x706f193900000000, 0xaf062b0d00000000, 0xb011db8d00000000, 0xd02ebad700000000, 0xcf394a5700000000, 0x6efddebf00000000, 0x71ea2e3f00000000, 0x11d54f6500000000, 0x0ec2bfe500000000, 0xd1ab8dd100000000, 0xcebc7d5100000000, 0xae831c0b00000000, 0xb194ec8b00000000, 0xad0c440100000000, 0xb21bb48100000000, 0xd224d5db00000000, 0xcd33255b00000000, 0x125a176f00000000, 0x0d4de7ef00000000, 0x6d7286b500000000, 0x7265763500000000, 0xd3a1e2dd00000000, 0xccb6125d00000000, 0xac89730700000000, 0xb39e838700000000, 0x6cf7b1b300000000, 0x73e0413300000000, 0x13df206900000000, 0x0cc8d0e900000000, 0xdf9d80f400000000, 0xc08a707400000000, 0xa0b5112e00000000, 0xbfa2e1ae00000000, 0x60cbd39a00000000, 0x7fdc231a00000000, 0x1fe3424000000000, 0x00f4b2c000000000, 0xa130262800000000, 0xbe27d6a800000000, 0xde18b7f200000000, 0xc10f477200000000, 0x1e66754600000000, 0x017185c600000000, 0x614ee49c00000000, 0x7e59141c00000000, 0x62c1bc9600000000, 0x7dd64c1600000000, 0x1de92d4c00000000, 0x02feddcc00000000, 0xdd97eff800000000, 0xc2801f7800000000, 0xa2bf7e2200000000, 0xbda88ea200000000, 0x1c6c1a4a00000000, 0x037beaca00000000, 0x63448b9000000000, 0x7c537b1000000000, 0xa33a492400000000, 0xbc2db9a400000000, 0xdc12d8fe00000000, 0xc305287e00000000, 0xa524f83000000000, 0xba3308b000000000, 0xda0c69ea00000000, 0xc51b996a00000000, 0x1a72ab5e00000000, 0x05655bde00000000, 0x655a3a8400000000, 0x7a4dca0400000000, 0xdb895eec00000000, 0xc49eae6c00000000, 0xa4a1cf3600000000, 0xbbb63fb600000000, 0x64df0d8200000000, 0x7bc8fd0200000000, 0x1bf79c5800000000, 0x04e06cd800000000, 0x1878c45200000000, 0x076f34d200000000, 0x6750558800000000, 0x7847a50800000000, 0xa72e973c00000000, 0xb83967bc00000000, 0xd80606e600000000, 0xc711f66600000000, 0x66d5628e00000000, 0x79c2920e00000000, 0x19fdf35400000000, 0x06ea03d400000000, 0xd98331e000000000, 0xc694c16000000000, 0xa6aba03a00000000, 0xb9bc50ba00000000}, {0x0000000000000000, 0xe2fd888d00000000, 0x85fd60c000000000, 0x6700e84d00000000, 0x4bfdb05b00000000, 0xa90038d600000000, 0xce00d09b00000000, 0x2cfd581600000000, 0x96fa61b700000000, 0x7407e93a00000000, 0x1307017700000000, 0xf1fa89fa00000000, 0xdd07d1ec00000000, 0x3ffa596100000000, 0x58fab12c00000000, 0xba0739a100000000, 0x6df3b2b500000000, 0x8f0e3a3800000000, 0xe80ed27500000000, 0x0af35af800000000, 0x260e02ee00000000, 0xc4f38a6300000000, 0xa3f3622e00000000, 0x410eeaa300000000, 0xfb09d30200000000, 0x19f45b8f00000000, 0x7ef4b3c200000000, 0x9c093b4f00000000, 0xb0f4635900000000, 0x5209ebd400000000, 0x3509039900000000, 0xd7f48b1400000000, 0x9be014b000000000, 0x791d9c3d00000000, 0x1e1d747000000000, 0xfce0fcfd00000000, 0xd01da4eb00000000, 0x32e02c6600000000, 0x55e0c42b00000000, 0xb71d4ca600000000, 0x0d1a750700000000, 0xefe7fd8a00000000, 0x88e715c700000000, 0x6a1a9d4a00000000, 0x46e7c55c00000000, 0xa41a4dd100000000, 0xc31aa59c00000000, 0x21e72d1100000000, 0xf613a60500000000, 0x14ee2e8800000000, 0x73eec6c500000000, 0x91134e4800000000, 0xbdee165e00000000, 0x5f139ed300000000, 0x3813769e00000000, 0xdaeefe1300000000, 0x60e9c7b200000000, 0x82144f3f00000000, 0xe514a77200000000, 0x07e92fff00000000, 0x2b1477e900000000, 0xc9e9ff6400000000, 0xaee9172900000000, 0x4c149fa400000000, 0x77c758bb00000000, 0x953ad03600000000, 0xf23a387b00000000, 0x10c7b0f600000000, 0x3c3ae8e000000000, 0xdec7606d00000000, 0xb9c7882000000000, 0x5b3a00ad00000000, 0xe13d390c00000000, 0x03c0b18100000000, 0x64c059cc00000000, 0x863dd14100000000, 0xaac0895700000000, 0x483d01da00000000, 0x2f3de99700000000, 0xcdc0611a00000000, 0x1a34ea0e00000000, 0xf8c9628300000000, 0x9fc98ace00000000, 0x7d34024300000000, 0x51c95a5500000000, 0xb334d2d800000000, 0xd4343a9500000000, 0x36c9b21800000000, 0x8cce8bb900000000, 0x6e33033400000000, 0x0933eb7900000000, 0xebce63f400000000, 0xc7333be200000000, 0x25ceb36f00000000, 0x42ce5b2200000000, 0xa033d3af00000000, 0xec274c0b00000000, 0x0edac48600000000, 0x69da2ccb00000000, 0x8b27a44600000000, 0xa7dafc5000000000, 0x452774dd00000000, 0x22279c9000000000, 0xc0da141d00000000, 0x7add2dbc00000000, 0x9820a53100000000, 0xff204d7c00000000, 0x1dddc5f100000000, 0x31209de700000000, 0xd3dd156a00000000, 0xb4ddfd2700000000, 0x562075aa00000000, 0x81d4febe00000000, 0x6329763300000000, 0x04299e7e00000000, 0xe6d416f300000000, 0xca294ee500000000, 0x28d4c66800000000, 0x4fd42e2500000000, 0xad29a6a800000000, 0x172e9f0900000000, 0xf5d3178400000000, 0x92d3ffc900000000, 0x702e774400000000, 0x5cd32f5200000000, 0xbe2ea7df00000000, 0xd92e4f9200000000, 0x3bd3c71f00000000, 0xaf88c0ad00000000, 0x4d75482000000000, 0x2a75a06d00000000, 0xc88828e000000000, 0xe47570f600000000, 0x0688f87b00000000, 0x6188103600000000, 0x837598bb00000000, 0x3972a11a00000000, 0xdb8f299700000000, 0xbc8fc1da00000000, 0x5e72495700000000, 0x728f114100000000, 0x907299cc00000000, 0xf772718100000000, 0x158ff90c00000000, 0xc27b721800000000, 0x2086fa9500000000, 0x478612d800000000, 0xa57b9a5500000000, 0x8986c24300000000, 0x6b7b4ace00000000, 0x0c7ba28300000000, 0xee862a0e00000000, 0x548113af00000000, 0xb67c9b2200000000, 0xd17c736f00000000, 0x3381fbe200000000, 0x1f7ca3f400000000, 0xfd812b7900000000, 0x9a81c33400000000, 0x787c4bb900000000, 0x3468d41d00000000, 0xd6955c9000000000, 0xb195b4dd00000000, 0x53683c5000000000, 0x7f95644600000000, 0x9d68eccb00000000, 0xfa68048600000000, 0x18958c0b00000000, 0xa292b5aa00000000, 0x406f3d2700000000, 0x276fd56a00000000, 0xc5925de700000000, 0xe96f05f100000000, 0x0b928d7c00000000, 0x6c92653100000000, 0x8e6fedbc00000000, 0x599b66a800000000, 0xbb66ee2500000000, 0xdc66066800000000, 0x3e9b8ee500000000, 0x1266d6f300000000, 0xf09b5e7e00000000, 0x979bb63300000000, 0x75663ebe00000000, 0xcf61071f00000000, 0x2d9c8f9200000000, 0x4a9c67df00000000, 0xa861ef5200000000, 0x849cb74400000000, 0x66613fc900000000, 0x0161d78400000000, 0xe39c5f0900000000, 0xd84f981600000000, 0x3ab2109b00000000, 0x5db2f8d600000000, 0xbf4f705b00000000, 0x93b2284d00000000, 0x714fa0c000000000, 0x164f488d00000000, 0xf4b2c00000000000, 0x4eb5f9a100000000, 0xac48712c00000000, 0xcb48996100000000, 0x29b511ec00000000, 0x054849fa00000000, 0xe7b5c17700000000, 0x80b5293a00000000, 0x6248a1b700000000, 0xb5bc2aa300000000, 0x5741a22e00000000, 0x30414a6300000000, 0xd2bcc2ee00000000, 0xfe419af800000000, 0x1cbc127500000000, 0x7bbcfa3800000000, 0x994172b500000000, 0x23464b1400000000, 0xc1bbc39900000000, 0xa6bb2bd400000000, 0x4446a35900000000, 0x68bbfb4f00000000, 0x8a4673c200000000, 0xed469b8f00000000, 0x0fbb130200000000, 0x43af8ca600000000, 0xa152042b00000000, 0xc652ec6600000000, 0x24af64eb00000000, 0x08523cfd00000000, 0xeaafb47000000000, 0x8daf5c3d00000000, 0x6f52d4b000000000, 0xd555ed1100000000, 0x37a8659c00000000, 0x50a88dd100000000, 0xb255055c00000000, 0x9ea85d4a00000000, 0x7c55d5c700000000, 0x1b553d8a00000000, 0xf9a8b50700000000, 0x2e5c3e1300000000, 0xcca1b69e00000000, 0xaba15ed300000000, 0x495cd65e00000000, 0x65a18e4800000000, 0x875c06c500000000, 0xe05cee8800000000, 0x02a1660500000000, 0xb8a65fa400000000, 0x5a5bd72900000000, 0x3d5b3f6400000000, 0xdfa6b7e900000000, 0xf35befff00000000, 0x11a6677200000000, 0x76a68f3f00000000, 0x945b07b200000000}, {0x0000000000000000, 0xa90b894e00000000, 0x5217129d00000000, 0xfb1c9bd300000000, 0xe52855e100000000, 0x4c23dcaf00000000, 0xb73f477c00000000, 0x1e34ce3200000000, 0x8b57db1900000000, 0x225c525700000000, 0xd940c98400000000, 0x704b40ca00000000, 0x6e7f8ef800000000, 0xc77407b600000000, 0x3c689c6500000000, 0x9563152b00000000, 0x16afb63300000000, 0xbfa43f7d00000000, 0x44b8a4ae00000000, 0xedb32de000000000, 0xf387e3d200000000, 0x5a8c6a9c00000000, 0xa190f14f00000000, 0x089b780100000000, 0x9df86d2a00000000, 0x34f3e46400000000, 0xcfef7fb700000000, 0x66e4f6f900000000, 0x78d038cb00000000, 0xd1dbb18500000000, 0x2ac72a5600000000, 0x83cca31800000000, 0x2c5e6d6700000000, 0x8555e42900000000, 0x7e497ffa00000000, 0xd742f6b400000000, 0xc976388600000000, 0x607db1c800000000, 0x9b612a1b00000000, 0x326aa35500000000, 0xa709b67e00000000, 0x0e023f3000000000, 0xf51ea4e300000000, 0x5c152dad00000000, 0x4221e39f00000000, 0xeb2a6ad100000000, 0x1036f10200000000, 0xb93d784c00000000, 0x3af1db5400000000, 0x93fa521a00000000, 0x68e6c9c900000000, 0xc1ed408700000000, 0xdfd98eb500000000, 0x76d207fb00000000, 0x8dce9c2800000000, 0x24c5156600000000, 0xb1a6004d00000000, 0x18ad890300000000, 0xe3b112d000000000, 0x4aba9b9e00000000, 0x548e55ac00000000, 0xfd85dce200000000, 0x0699473100000000, 0xaf92ce7f00000000, 0x58bcdace00000000, 0xf1b7538000000000, 0x0aabc85300000000, 0xa3a0411d00000000, 0xbd948f2f00000000, 0x149f066100000000, 0xef839db200000000, 0x468814fc00000000, 0xd3eb01d700000000, 0x7ae0889900000000, 0x81fc134a00000000, 0x28f79a0400000000, 0x36c3543600000000, 0x9fc8dd7800000000, 0x64d446ab00000000, 0xcddfcfe500000000, 0x4e136cfd00000000, 0xe718e5b300000000, 0x1c047e6000000000, 0xb50ff72e00000000, 0xab3b391c00000000, 0x0230b05200000000, 0xf92c2b8100000000, 0x5027a2cf00000000, 0xc544b7e400000000, 0x6c4f3eaa00000000, 0x9753a57900000000, 0x3e582c3700000000, 0x206ce20500000000, 0x89676b4b00000000, 0x727bf09800000000, 0xdb7079d600000000, 0x74e2b7a900000000, 0xdde93ee700000000, 0x26f5a53400000000, 0x8ffe2c7a00000000, 0x91cae24800000000, 0x38c16b0600000000, 0xc3ddf0d500000000, 0x6ad6799b00000000, 0xffb56cb000000000, 0x56bee5fe00000000, 0xada27e2d00000000, 0x04a9f76300000000, 0x1a9d395100000000, 0xb396b01f00000000, 0x488a2bcc00000000, 0xe181a28200000000, 0x624d019a00000000, 0xcb4688d400000000, 0x305a130700000000, 0x99519a4900000000, 0x8765547b00000000, 0x2e6edd3500000000, 0xd57246e600000000, 0x7c79cfa800000000, 0xe91ada8300000000, 0x401153cd00000000, 0xbb0dc81e00000000, 0x1206415000000000, 0x0c328f6200000000, 0xa539062c00000000, 0x5e259dff00000000, 0xf72e14b100000000, 0xf17ec44600000000, 0x58754d0800000000, 0xa369d6db00000000, 0x0a625f9500000000, 0x145691a700000000, 0xbd5d18e900000000, 0x4641833a00000000, 0xef4a0a7400000000, 0x7a291f5f00000000, 0xd322961100000000, 0x283e0dc200000000, 0x8135848c00000000, 0x9f014abe00000000, 0x360ac3f000000000, 0xcd16582300000000, 0x641dd16d00000000, 0xe7d1727500000000, 0x4edafb3b00000000, 0xb5c660e800000000, 0x1ccde9a600000000, 0x02f9279400000000, 0xabf2aeda00000000, 0x50ee350900000000, 0xf9e5bc4700000000, 0x6c86a96c00000000, 0xc58d202200000000, 0x3e91bbf100000000, 0x979a32bf00000000, 0x89aefc8d00000000, 0x20a575c300000000, 0xdbb9ee1000000000, 0x72b2675e00000000, 0xdd20a92100000000, 0x742b206f00000000, 0x8f37bbbc00000000, 0x263c32f200000000, 0x3808fcc000000000, 0x9103758e00000000, 0x6a1fee5d00000000, 0xc314671300000000, 0x5677723800000000, 0xff7cfb7600000000, 0x046060a500000000, 0xad6be9eb00000000, 0xb35f27d900000000, 0x1a54ae9700000000, 0xe148354400000000, 0x4843bc0a00000000, 0xcb8f1f1200000000, 0x6284965c00000000, 0x99980d8f00000000, 0x309384c100000000, 0x2ea74af300000000, 0x87acc3bd00000000, 0x7cb0586e00000000, 0xd5bbd12000000000, 0x40d8c40b00000000, 0xe9d34d4500000000, 0x12cfd69600000000, 0xbbc45fd800000000, 0xa5f091ea00000000, 0x0cfb18a400000000, 0xf7e7837700000000, 0x5eec0a3900000000, 0xa9c21e8800000000, 0x00c997c600000000, 0xfbd50c1500000000, 0x52de855b00000000, 0x4cea4b6900000000, 0xe5e1c22700000000, 0x1efd59f400000000, 0xb7f6d0ba00000000, 0x2295c59100000000, 0x8b9e4cdf00000000, 0x7082d70c00000000, 0xd9895e4200000000, 0xc7bd907000000000, 0x6eb6193e00000000, 0x95aa82ed00000000, 0x3ca10ba300000000, 0xbf6da8bb00000000, 0x166621f500000000, 0xed7aba2600000000, 0x4471336800000000, 0x5a45fd5a00000000, 0xf34e741400000000, 0x0852efc700000000, 0xa159668900000000, 0x343a73a200000000, 0x9d31faec00000000, 0x662d613f00000000, 0xcf26e87100000000, 0xd112264300000000, 0x7819af0d00000000, 0x830534de00000000, 0x2a0ebd9000000000, 0x859c73ef00000000, 0x2c97faa100000000, 0xd78b617200000000, 0x7e80e83c00000000, 0x60b4260e00000000, 0xc9bfaf4000000000, 0x32a3349300000000, 0x9ba8bddd00000000, 0x0ecba8f600000000, 0xa7c021b800000000, 0x5cdcba6b00000000, 0xf5d7332500000000, 0xebe3fd1700000000, 0x42e8745900000000, 0xb9f4ef8a00000000, 0x10ff66c400000000, 0x9333c5dc00000000, 0x3a384c9200000000, 0xc124d74100000000, 0x682f5e0f00000000, 0x761b903d00000000, 0xdf10197300000000, 0x240c82a000000000, 0x8d070bee00000000, 0x18641ec500000000, 0xb16f978b00000000, 0x4a730c5800000000, 0xe378851600000000, 0xfd4c4b2400000000, 0x5447c26a00000000, 0xaf5b59b900000000, 0x0650d0f700000000}, {0x0000000000000000, 0x479244af00000000, 0xcf22f88500000000, 0x88b0bc2a00000000, 0xdf4381d000000000, 0x98d1c57f00000000, 0x1061795500000000, 0x57f33dfa00000000, 0xff81737a00000000, 0xb81337d500000000, 0x30a38bff00000000, 0x7731cf5000000000, 0x20c2f2aa00000000, 0x6750b60500000000, 0xefe00a2f00000000, 0xa8724e8000000000, 0xfe03e7f400000000, 0xb991a35b00000000, 0x31211f7100000000, 0x76b35bde00000000, 0x2140662400000000, 0x66d2228b00000000, 0xee629ea100000000, 0xa9f0da0e00000000, 0x0182948e00000000, 0x4610d02100000000, 0xcea06c0b00000000, 0x893228a400000000, 0xdec1155e00000000, 0x995351f100000000, 0x11e3eddb00000000, 0x5671a97400000000, 0xbd01bf3200000000, 0xfa93fb9d00000000, 0x722347b700000000, 0x35b1031800000000, 0x62423ee200000000, 0x25d07a4d00000000, 0xad60c66700000000, 0xeaf282c800000000, 0x4280cc4800000000, 0x051288e700000000, 0x8da234cd00000000, 0xca30706200000000, 0x9dc34d9800000000, 0xda51093700000000, 0x52e1b51d00000000, 0x1573f1b200000000, 0x430258c600000000, 0x04901c6900000000, 0x8c20a04300000000, 0xcbb2e4ec00000000, 0x9c41d91600000000, 0xdbd39db900000000, 0x5363219300000000, 0x14f1653c00000000, 0xbc832bbc00000000, 0xfb116f1300000000, 0x73a1d33900000000, 0x3433979600000000, 0x63c0aa6c00000000, 0x2452eec300000000, 0xace252e900000000, 0xeb70164600000000, 0x7a037e6500000000, 0x3d913aca00000000, 0xb52186e000000000, 0xf2b3c24f00000000, 0xa540ffb500000000, 0xe2d2bb1a00000000, 0x6a62073000000000, 0x2df0439f00000000, 0x85820d1f00000000, 0xc21049b000000000, 0x4aa0f59a00000000, 0x0d32b13500000000, 0x5ac18ccf00000000, 0x1d53c86000000000, 0x95e3744a00000000, 0xd27130e500000000, 0x8400999100000000, 0xc392dd3e00000000, 0x4b22611400000000, 0x0cb025bb00000000, 0x5b43184100000000, 0x1cd15cee00000000, 0x9461e0c400000000, 0xd3f3a46b00000000, 0x7b81eaeb00000000, 0x3c13ae4400000000, 0xb4a3126e00000000, 0xf33156c100000000, 0xa4c26b3b00000000, 0xe3502f9400000000, 0x6be093be00000000, 0x2c72d71100000000, 0xc702c15700000000, 0x809085f800000000, 0x082039d200000000, 0x4fb27d7d00000000, 0x1841408700000000, 0x5fd3042800000000, 0xd763b80200000000, 0x90f1fcad00000000, 0x3883b22d00000000, 0x7f11f68200000000, 0xf7a14aa800000000, 0xb0330e0700000000, 0xe7c033fd00000000, 0xa052775200000000, 0x28e2cb7800000000, 0x6f708fd700000000, 0x390126a300000000, 0x7e93620c00000000, 0xf623de2600000000, 0xb1b19a8900000000, 0xe642a77300000000, 0xa1d0e3dc00000000, 0x29605ff600000000, 0x6ef21b5900000000, 0xc68055d900000000, 0x8112117600000000, 0x09a2ad5c00000000, 0x4e30e9f300000000, 0x19c3d40900000000, 0x5e5190a600000000, 0xd6e12c8c00000000, 0x9173682300000000, 0xf406fcca00000000, 0xb394b86500000000, 0x3b24044f00000000, 0x7cb640e000000000, 0x2b457d1a00000000, 0x6cd739b500000000, 0xe467859f00000000, 0xa3f5c13000000000, 0x0b878fb000000000, 0x4c15cb1f00000000, 0xc4a5773500000000, 0x8337339a00000000, 0xd4c40e6000000000, 0x93564acf00000000, 0x1be6f6e500000000, 0x5c74b24a00000000, 0x0a051b3e00000000, 0x4d975f9100000000, 0xc527e3bb00000000, 0x82b5a71400000000, 0xd5469aee00000000, 0x92d4de4100000000, 0x1a64626b00000000, 0x5df626c400000000, 0xf584684400000000, 0xb2162ceb00000000, 0x3aa690c100000000, 0x7d34d46e00000000, 0x2ac7e99400000000, 0x6d55ad3b00000000, 0xe5e5111100000000, 0xa27755be00000000, 0x490743f800000000, 0x0e95075700000000, 0x8625bb7d00000000, 0xc1b7ffd200000000, 0x9644c22800000000, 0xd1d6868700000000, 0x59663aad00000000, 0x1ef47e0200000000, 0xb686308200000000, 0xf114742d00000000, 0x79a4c80700000000, 0x3e368ca800000000, 0x69c5b15200000000, 0x2e57f5fd00000000, 0xa6e749d700000000, 0xe1750d7800000000, 0xb704a40c00000000, 0xf096e0a300000000, 0x78265c8900000000, 0x3fb4182600000000, 0x684725dc00000000, 0x2fd5617300000000, 0xa765dd5900000000, 0xe0f799f600000000, 0x4885d77600000000, 0x0f1793d900000000, 0x87a72ff300000000, 0xc0356b5c00000000, 0x97c656a600000000, 0xd054120900000000, 0x58e4ae2300000000, 0x1f76ea8c00000000, 0x8e0582af00000000, 0xc997c60000000000, 0x41277a2a00000000, 0x06b53e8500000000, 0x5146037f00000000, 0x16d447d000000000, 0x9e64fbfa00000000, 0xd9f6bf5500000000, 0x7184f1d500000000, 0x3616b57a00000000, 0xbea6095000000000, 0xf9344dff00000000, 0xaec7700500000000, 0xe95534aa00000000, 0x61e5888000000000, 0x2677cc2f00000000, 0x7006655b00000000, 0x379421f400000000, 0xbf249dde00000000, 0xf8b6d97100000000, 0xaf45e48b00000000, 0xe8d7a02400000000, 0x60671c0e00000000, 0x27f558a100000000, 0x8f87162100000000, 0xc815528e00000000, 0x40a5eea400000000, 0x0737aa0b00000000, 0x50c497f100000000, 0x1756d35e00000000, 0x9fe66f7400000000, 0xd8742bdb00000000, 0x33043d9d00000000, 0x7496793200000000, 0xfc26c51800000000, 0xbbb481b700000000, 0xec47bc4d00000000, 0xabd5f8e200000000, 0x236544c800000000, 0x64f7006700000000, 0xcc854ee700000000, 0x8b170a4800000000, 0x03a7b66200000000, 0x4435f2cd00000000, 0x13c6cf3700000000, 0x54548b9800000000, 0xdce437b200000000, 0x9b76731d00000000, 0xcd07da6900000000, 0x8a959ec600000000, 0x022522ec00000000, 0x45b7664300000000, 0x12445bb900000000, 0x55d61f1600000000, 0xdd66a33c00000000, 0x9af4e79300000000, 0x3286a91300000000, 0x7514edbc00000000, 0xfda4519600000000, 0xba36153900000000, 0xedc528c300000000, 0xaa576c6c00000000, 0x22e7d04600000000, 0x657594e900000000}}; #else /* W == 4 */ local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0x65673b46, 0xcace768c, 0xafa94dca, 0x4eedeb59, 0x2b8ad01f, 0x84239dd5, 0xe144a693, 0x9ddbd6b2, 0xf8bcedf4, 0x5715a03e, 0x32729b78, 0xd3363deb, 0xb65106ad, 0x19f84b67, 0x7c9f7021, 0xe0c6ab25, 0x85a19063, 0x2a08dda9, 0x4f6fe6ef, 0xae2b407c, 0xcb4c7b3a, 0x64e536f0, 0x01820db6, 0x7d1d7d97, 0x187a46d1, 0xb7d30b1b, 0xd2b4305d, 0x33f096ce, 0x5697ad88, 0xf93ee042, 0x9c59db04, 0x1afc500b, 0x7f9b6b4d, 0xd0322687, 0xb5551dc1, 0x5411bb52, 0x31768014, 0x9edfcdde, 0xfbb8f698, 0x872786b9, 0xe240bdff, 0x4de9f035, 0x288ecb73, 0xc9ca6de0, 0xacad56a6, 0x03041b6c, 0x6663202a, 0xfa3afb2e, 0x9f5dc068, 0x30f48da2, 0x5593b6e4, 0xb4d71077, 0xd1b02b31, 0x7e1966fb, 0x1b7e5dbd, 0x67e12d9c, 0x028616da, 0xad2f5b10, 0xc8486056, 0x290cc6c5, 0x4c6bfd83, 0xe3c2b049, 0x86a58b0f, 0x35f8a016, 0x509f9b50, 0xff36d69a, 0x9a51eddc, 0x7b154b4f, 0x1e727009, 0xb1db3dc3, 0xd4bc0685, 0xa82376a4, 0xcd444de2, 0x62ed0028, 0x078a3b6e, 0xe6ce9dfd, 0x83a9a6bb, 0x2c00eb71, 0x4967d037, 0xd53e0b33, 0xb0593075, 0x1ff07dbf, 0x7a9746f9, 0x9bd3e06a, 0xfeb4db2c, 0x511d96e6, 0x347aada0, 0x48e5dd81, 0x2d82e6c7, 0x822bab0d, 0xe74c904b, 0x060836d8, 0x636f0d9e, 0xccc64054, 0xa9a17b12, 0x2f04f01d, 0x4a63cb5b, 0xe5ca8691, 0x80adbdd7, 0x61e91b44, 0x048e2002, 0xab276dc8, 0xce40568e, 0xb2df26af, 0xd7b81de9, 0x78115023, 0x1d766b65, 0xfc32cdf6, 0x9955f6b0, 0x36fcbb7a, 0x539b803c, 0xcfc25b38, 0xaaa5607e, 0x050c2db4, 0x606b16f2, 0x812fb061, 0xe4488b27, 0x4be1c6ed, 0x2e86fdab, 0x52198d8a, 0x377eb6cc, 0x98d7fb06, 0xfdb0c040, 0x1cf466d3, 0x79935d95, 0xd63a105f, 0xb35d2b19, 0x6bf1402c, 0x0e967b6a, 0xa13f36a0, 0xc4580de6, 0x251cab75, 0x407b9033, 0xefd2ddf9, 0x8ab5e6bf, 0xf62a969e, 0x934dadd8, 0x3ce4e012, 0x5983db54, 0xb8c77dc7, 0xdda04681, 0x72090b4b, 0x176e300d, 0x8b37eb09, 0xee50d04f, 0x41f99d85, 0x249ea6c3, 0xc5da0050, 0xa0bd3b16, 0x0f1476dc, 0x6a734d9a, 0x16ec3dbb, 0x738b06fd, 0xdc224b37, 0xb9457071, 0x5801d6e2, 0x3d66eda4, 0x92cfa06e, 0xf7a89b28, 0x710d1027, 0x146a2b61, 0xbbc366ab, 0xdea45ded, 0x3fe0fb7e, 0x5a87c038, 0xf52e8df2, 0x9049b6b4, 0xecd6c695, 0x89b1fdd3, 0x2618b019, 0x437f8b5f, 0xa23b2dcc, 0xc75c168a, 0x68f55b40, 0x0d926006, 0x91cbbb02, 0xf4ac8044, 0x5b05cd8e, 0x3e62f6c8, 0xdf26505b, 0xba416b1d, 0x15e826d7, 0x708f1d91, 0x0c106db0, 0x697756f6, 0xc6de1b3c, 0xa3b9207a, 0x42fd86e9, 0x279abdaf, 0x8833f065, 0xed54cb23, 0x5e09e03a, 0x3b6edb7c, 0x94c796b6, 0xf1a0adf0, 0x10e40b63, 0x75833025, 0xda2a7def, 0xbf4d46a9, 0xc3d23688, 0xa6b50dce, 0x091c4004, 0x6c7b7b42, 0x8d3fddd1, 0xe858e697, 0x47f1ab5d, 0x2296901b, 0xbecf4b1f, 0xdba87059, 0x74013d93, 0x116606d5, 0xf022a046, 0x95459b00, 0x3aecd6ca, 0x5f8bed8c, 0x23149dad, 0x4673a6eb, 0xe9daeb21, 0x8cbdd067, 0x6df976f4, 0x089e4db2, 0xa7370078, 0xc2503b3e, 0x44f5b031, 0x21928b77, 0x8e3bc6bd, 0xeb5cfdfb, 0x0a185b68, 0x6f7f602e, 0xc0d62de4, 0xa5b116a2, 0xd92e6683, 0xbc495dc5, 0x13e0100f, 0x76872b49, 0x97c38dda, 0xf2a4b69c, 0x5d0dfb56, 0x386ac010, 0xa4331b14, 0xc1542052, 0x6efd6d98, 0x0b9a56de, 0xeadef04d, 0x8fb9cb0b, 0x201086c1, 0x4577bd87, 0x39e8cda6, 0x5c8ff6e0, 0xf326bb2a, 0x9641806c, 0x770526ff, 0x12621db9, 0xbdcb5073, 0xd8ac6b35}, {0x00000000, 0xd7e28058, 0x74b406f1, 0xa35686a9, 0xe9680de2, 0x3e8a8dba, 0x9ddc0b13, 0x4a3e8b4b, 0x09a11d85, 0xde439ddd, 0x7d151b74, 0xaaf79b2c, 0xe0c91067, 0x372b903f, 0x947d1696, 0x439f96ce, 0x13423b0a, 0xc4a0bb52, 0x67f63dfb, 0xb014bda3, 0xfa2a36e8, 0x2dc8b6b0, 0x8e9e3019, 0x597cb041, 0x1ae3268f, 0xcd01a6d7, 0x6e57207e, 0xb9b5a026, 0xf38b2b6d, 0x2469ab35, 0x873f2d9c, 0x50ddadc4, 0x26847614, 0xf166f64c, 0x523070e5, 0x85d2f0bd, 0xcfec7bf6, 0x180efbae, 0xbb587d07, 0x6cbafd5f, 0x2f256b91, 0xf8c7ebc9, 0x5b916d60, 0x8c73ed38, 0xc64d6673, 0x11afe62b, 0xb2f96082, 0x651be0da, 0x35c64d1e, 0xe224cd46, 0x41724bef, 0x9690cbb7, 0xdcae40fc, 0x0b4cc0a4, 0xa81a460d, 0x7ff8c655, 0x3c67509b, 0xeb85d0c3, 0x48d3566a, 0x9f31d632, 0xd50f5d79, 0x02eddd21, 0xa1bb5b88, 0x7659dbd0, 0x4d08ec28, 0x9aea6c70, 0x39bcead9, 0xee5e6a81, 0xa460e1ca, 0x73826192, 0xd0d4e73b, 0x07366763, 0x44a9f1ad, 0x934b71f5, 0x301df75c, 0xe7ff7704, 0xadc1fc4f, 0x7a237c17, 0xd975fabe, 0x0e977ae6, 0x5e4ad722, 0x89a8577a, 0x2afed1d3, 0xfd1c518b, 0xb722dac0, 0x60c05a98, 0xc396dc31, 0x14745c69, 0x57ebcaa7, 0x80094aff, 0x235fcc56, 0xf4bd4c0e, 0xbe83c745, 0x6961471d, 0xca37c1b4, 0x1dd541ec, 0x6b8c9a3c, 0xbc6e1a64, 0x1f389ccd, 0xc8da1c95, 0x82e497de, 0x55061786, 0xf650912f, 0x21b21177, 0x622d87b9, 0xb5cf07e1, 0x16998148, 0xc17b0110, 0x8b458a5b, 0x5ca70a03, 0xfff18caa, 0x28130cf2, 0x78cea136, 0xaf2c216e, 0x0c7aa7c7, 0xdb98279f, 0x91a6acd4, 0x46442c8c, 0xe512aa25, 0x32f02a7d, 0x716fbcb3, 0xa68d3ceb, 0x05dbba42, 0xd2393a1a, 0x9807b151, 0x4fe53109, 0xecb3b7a0, 0x3b5137f8, 0x9a11d850, 0x4df35808, 0xeea5dea1, 0x39475ef9, 0x7379d5b2, 0xa49b55ea, 0x07cdd343, 0xd02f531b, 0x93b0c5d5, 0x4452458d, 0xe704c324, 0x30e6437c, 0x7ad8c837, 0xad3a486f, 0x0e6ccec6, 0xd98e4e9e, 0x8953e35a, 0x5eb16302, 0xfde7e5ab, 0x2a0565f3, 0x603beeb8, 0xb7d96ee0, 0x148fe849, 0xc36d6811, 0x80f2fedf, 0x57107e87, 0xf446f82e, 0x23a47876, 0x699af33d, 0xbe787365, 0x1d2ef5cc, 0xcacc7594, 0xbc95ae44, 0x6b772e1c, 0xc821a8b5, 0x1fc328ed, 0x55fda3a6, 0x821f23fe, 0x2149a557, 0xf6ab250f, 0xb534b3c1, 0x62d63399, 0xc180b530, 0x16623568, 0x5c5cbe23, 0x8bbe3e7b, 0x28e8b8d2, 0xff0a388a, 0xafd7954e, 0x78351516, 0xdb6393bf, 0x0c8113e7, 0x46bf98ac, 0x915d18f4, 0x320b9e5d, 0xe5e91e05, 0xa67688cb, 0x71940893, 0xd2c28e3a, 0x05200e62, 0x4f1e8529, 0x98fc0571, 0x3baa83d8, 0xec480380, 0xd7193478, 0x00fbb420, 0xa3ad3289, 0x744fb2d1, 0x3e71399a, 0xe993b9c2, 0x4ac53f6b, 0x9d27bf33, 0xdeb829fd, 0x095aa9a5, 0xaa0c2f0c, 0x7deeaf54, 0x37d0241f, 0xe032a447, 0x436422ee, 0x9486a2b6, 0xc45b0f72, 0x13b98f2a, 0xb0ef0983, 0x670d89db, 0x2d330290, 0xfad182c8, 0x59870461, 0x8e658439, 0xcdfa12f7, 0x1a1892af, 0xb94e1406, 0x6eac945e, 0x24921f15, 0xf3709f4d, 0x502619e4, 0x87c499bc, 0xf19d426c, 0x267fc234, 0x8529449d, 0x52cbc4c5, 0x18f54f8e, 0xcf17cfd6, 0x6c41497f, 0xbba3c927, 0xf83c5fe9, 0x2fdedfb1, 0x8c885918, 0x5b6ad940, 0x1154520b, 0xc6b6d253, 0x65e054fa, 0xb202d4a2, 0xe2df7966, 0x353df93e, 0x966b7f97, 0x4189ffcf, 0x0bb77484, 0xdc55f4dc, 0x7f037275, 0xa8e1f22d, 0xeb7e64e3, 0x3c9ce4bb, 0x9fca6212, 0x4828e24a, 0x02166901, 0xd5f4e959, 0x76a26ff0, 0xa140efa8}, {0x00000000, 0xef52b6e1, 0x05d46b83, 0xea86dd62, 0x0ba8d706, 0xe4fa61e7, 0x0e7cbc85, 0xe12e0a64, 0x1751ae0c, 0xf80318ed, 0x1285c58f, 0xfdd7736e, 0x1cf9790a, 0xf3abcfeb, 0x192d1289, 0xf67fa468, 0x2ea35c18, 0xc1f1eaf9, 0x2b77379b, 0xc425817a, 0x250b8b1e, 0xca593dff, 0x20dfe09d, 0xcf8d567c, 0x39f2f214, 0xd6a044f5, 0x3c269997, 0xd3742f76, 0x325a2512, 0xdd0893f3, 0x378e4e91, 0xd8dcf870, 0x5d46b830, 0xb2140ed1, 0x5892d3b3, 0xb7c06552, 0x56ee6f36, 0xb9bcd9d7, 0x533a04b5, 0xbc68b254, 0x4a17163c, 0xa545a0dd, 0x4fc37dbf, 0xa091cb5e, 0x41bfc13a, 0xaeed77db, 0x446baab9, 0xab391c58, 0x73e5e428, 0x9cb752c9, 0x76318fab, 0x9963394a, 0x784d332e, 0x971f85cf, 0x7d9958ad, 0x92cbee4c, 0x64b44a24, 0x8be6fcc5, 0x616021a7, 0x8e329746, 0x6f1c9d22, 0x804e2bc3, 0x6ac8f6a1, 0x859a4040, 0xba8d7060, 0x55dfc681, 0xbf591be3, 0x500bad02, 0xb125a766, 0x5e771187, 0xb4f1cce5, 0x5ba37a04, 0xaddcde6c, 0x428e688d, 0xa808b5ef, 0x475a030e, 0xa674096a, 0x4926bf8b, 0xa3a062e9, 0x4cf2d408, 0x942e2c78, 0x7b7c9a99, 0x91fa47fb, 0x7ea8f11a, 0x9f86fb7e, 0x70d44d9f, 0x9a5290fd, 0x7500261c, 0x837f8274, 0x6c2d3495, 0x86abe9f7, 0x69f95f16, 0x88d75572, 0x6785e393, 0x8d033ef1, 0x62518810, 0xe7cbc850, 0x08997eb1, 0xe21fa3d3, 0x0d4d1532, 0xec631f56, 0x0331a9b7, 0xe9b774d5, 0x06e5c234, 0xf09a665c, 0x1fc8d0bd, 0xf54e0ddf, 0x1a1cbb3e, 0xfb32b15a, 0x146007bb, 0xfee6dad9, 0x11b46c38, 0xc9689448, 0x263a22a9, 0xccbcffcb, 0x23ee492a, 0xc2c0434e, 0x2d92f5af, 0xc71428cd, 0x28469e2c, 0xde393a44, 0x316b8ca5, 0xdbed51c7, 0x34bfe726, 0xd591ed42, 0x3ac35ba3, 0xd04586c1, 0x3f173020, 0xae6be681, 0x41395060, 0xabbf8d02, 0x44ed3be3, 0xa5c33187, 0x4a918766, 0xa0175a04, 0x4f45ece5, 0xb93a488d, 0x5668fe6c, 0xbcee230e, 0x53bc95ef, 0xb2929f8b, 0x5dc0296a, 0xb746f408, 0x581442e9, 0x80c8ba99, 0x6f9a0c78, 0x851cd11a, 0x6a4e67fb, 0x8b606d9f, 0x6432db7e, 0x8eb4061c, 0x61e6b0fd, 0x97991495, 0x78cba274, 0x924d7f16, 0x7d1fc9f7, 0x9c31c393, 0x73637572, 0x99e5a810, 0x76b71ef1, 0xf32d5eb1, 0x1c7fe850, 0xf6f93532, 0x19ab83d3, 0xf88589b7, 0x17d73f56, 0xfd51e234, 0x120354d5, 0xe47cf0bd, 0x0b2e465c, 0xe1a89b3e, 0x0efa2ddf, 0xefd427bb, 0x0086915a, 0xea004c38, 0x0552fad9, 0xdd8e02a9, 0x32dcb448, 0xd85a692a, 0x3708dfcb, 0xd626d5af, 0x3974634e, 0xd3f2be2c, 0x3ca008cd, 0xcadfaca5, 0x258d1a44, 0xcf0bc726, 0x205971c7, 0xc1777ba3, 0x2e25cd42, 0xc4a31020, 0x2bf1a6c1, 0x14e696e1, 0xfbb42000, 0x1132fd62, 0xfe604b83, 0x1f4e41e7, 0xf01cf706, 0x1a9a2a64, 0xf5c89c85, 0x03b738ed, 0xece58e0c, 0x0663536e, 0xe931e58f, 0x081fefeb, 0xe74d590a, 0x0dcb8468, 0xe2993289, 0x3a45caf9, 0xd5177c18, 0x3f91a17a, 0xd0c3179b, 0x31ed1dff, 0xdebfab1e, 0x3439767c, 0xdb6bc09d, 0x2d1464f5, 0xc246d214, 0x28c00f76, 0xc792b997, 0x26bcb3f3, 0xc9ee0512, 0x2368d870, 0xcc3a6e91, 0x49a02ed1, 0xa6f29830, 0x4c744552, 0xa326f3b3, 0x4208f9d7, 0xad5a4f36, 0x47dc9254, 0xa88e24b5, 0x5ef180dd, 0xb1a3363c, 0x5b25eb5e, 0xb4775dbf, 0x555957db, 0xba0be13a, 0x508d3c58, 0xbfdf8ab9, 0x670372c9, 0x8851c428, 0x62d7194a, 0x8d85afab, 0x6caba5cf, 0x83f9132e, 0x697fce4c, 0x862d78ad, 0x7052dcc5, 0x9f006a24, 0x7586b746, 0x9ad401a7, 0x7bfa0bc3, 0x94a8bd22, 0x7e2e6040, 0x917cd6a1}, {0x00000000, 0x87a6cb43, 0xd43c90c7, 0x539a5b84, 0x730827cf, 0xf4aeec8c, 0xa734b708, 0x20927c4b, 0xe6104f9e, 0x61b684dd, 0x322cdf59, 0xb58a141a, 0x95186851, 0x12bea312, 0x4124f896, 0xc68233d5, 0x1751997d, 0x90f7523e, 0xc36d09ba, 0x44cbc2f9, 0x6459beb2, 0xe3ff75f1, 0xb0652e75, 0x37c3e536, 0xf141d6e3, 0x76e71da0, 0x257d4624, 0xa2db8d67, 0x8249f12c, 0x05ef3a6f, 0x567561eb, 0xd1d3aaa8, 0x2ea332fa, 0xa905f9b9, 0xfa9fa23d, 0x7d39697e, 0x5dab1535, 0xda0dde76, 0x899785f2, 0x0e314eb1, 0xc8b37d64, 0x4f15b627, 0x1c8feda3, 0x9b2926e0, 0xbbbb5aab, 0x3c1d91e8, 0x6f87ca6c, 0xe821012f, 0x39f2ab87, 0xbe5460c4, 0xedce3b40, 0x6a68f003, 0x4afa8c48, 0xcd5c470b, 0x9ec61c8f, 0x1960d7cc, 0xdfe2e419, 0x58442f5a, 0x0bde74de, 0x8c78bf9d, 0xaceac3d6, 0x2b4c0895, 0x78d65311, 0xff709852, 0x5d4665f4, 0xdae0aeb7, 0x897af533, 0x0edc3e70, 0x2e4e423b, 0xa9e88978, 0xfa72d2fc, 0x7dd419bf, 0xbb562a6a, 0x3cf0e129, 0x6f6abaad, 0xe8cc71ee, 0xc85e0da5, 0x4ff8c6e6, 0x1c629d62, 0x9bc45621, 0x4a17fc89, 0xcdb137ca, 0x9e2b6c4e, 0x198da70d, 0x391fdb46, 0xbeb91005, 0xed234b81, 0x6a8580c2, 0xac07b317, 0x2ba17854, 0x783b23d0, 0xff9de893, 0xdf0f94d8, 0x58a95f9b, 0x0b33041f, 0x8c95cf5c, 0x73e5570e, 0xf4439c4d, 0xa7d9c7c9, 0x207f0c8a, 0x00ed70c1, 0x874bbb82, 0xd4d1e006, 0x53772b45, 0x95f51890, 0x1253d3d3, 0x41c98857, 0xc66f4314, 0xe6fd3f5f, 0x615bf41c, 0x32c1af98, 0xb56764db, 0x64b4ce73, 0xe3120530, 0xb0885eb4, 0x372e95f7, 0x17bce9bc, 0x901a22ff, 0xc380797b, 0x4426b238, 0x82a481ed, 0x05024aae, 0x5698112a, 0xd13eda69, 0xf1aca622, 0x760a6d61, 0x259036e5, 0xa236fda6, 0xba8ccbe8, 0x3d2a00ab, 0x6eb05b2f, 0xe916906c, 0xc984ec27, 0x4e222764, 0x1db87ce0, 0x9a1eb7a3, 0x5c9c8476, 0xdb3a4f35, 0x88a014b1, 0x0f06dff2, 0x2f94a3b9, 0xa83268fa, 0xfba8337e, 0x7c0ef83d, 0xaddd5295, 0x2a7b99d6, 0x79e1c252, 0xfe470911, 0xded5755a, 0x5973be19, 0x0ae9e59d, 0x8d4f2ede, 0x4bcd1d0b, 0xcc6bd648, 0x9ff18dcc, 0x1857468f, 0x38c53ac4, 0xbf63f187, 0xecf9aa03, 0x6b5f6140, 0x942ff912, 0x13893251, 0x401369d5, 0xc7b5a296, 0xe727dedd, 0x6081159e, 0x331b4e1a, 0xb4bd8559, 0x723fb68c, 0xf5997dcf, 0xa603264b, 0x21a5ed08, 0x01379143, 0x86915a00, 0xd50b0184, 0x52adcac7, 0x837e606f, 0x04d8ab2c, 0x5742f0a8, 0xd0e43beb, 0xf07647a0, 0x77d08ce3, 0x244ad767, 0xa3ec1c24, 0x656e2ff1, 0xe2c8e4b2, 0xb152bf36, 0x36f47475, 0x1666083e, 0x91c0c37d, 0xc25a98f9, 0x45fc53ba, 0xe7caae1c, 0x606c655f, 0x33f63edb, 0xb450f598, 0x94c289d3, 0x13644290, 0x40fe1914, 0xc758d257, 0x01dae182, 0x867c2ac1, 0xd5e67145, 0x5240ba06, 0x72d2c64d, 0xf5740d0e, 0xa6ee568a, 0x21489dc9, 0xf09b3761, 0x773dfc22, 0x24a7a7a6, 0xa3016ce5, 0x839310ae, 0x0435dbed, 0x57af8069, 0xd0094b2a, 0x168b78ff, 0x912db3bc, 0xc2b7e838, 0x4511237b, 0x65835f30, 0xe2259473, 0xb1bfcff7, 0x361904b4, 0xc9699ce6, 0x4ecf57a5, 0x1d550c21, 0x9af3c762, 0xba61bb29, 0x3dc7706a, 0x6e5d2bee, 0xe9fbe0ad, 0x2f79d378, 0xa8df183b, 0xfb4543bf, 0x7ce388fc, 0x5c71f4b7, 0xdbd73ff4, 0x884d6470, 0x0febaf33, 0xde38059b, 0x599eced8, 0x0a04955c, 0x8da25e1f, 0xad302254, 0x2a96e917, 0x790cb293, 0xfeaa79d0, 0x38284a05, 0xbf8e8146, 0xec14dac2, 0x6bb21181, 0x4b206dca, 0xcc86a689, 0x9f1cfd0d, 0x18ba364e}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x00000000, 0x43cba687, 0xc7903cd4, 0x845b9a53, 0xcf270873, 0x8cecaef4, 0x08b734a7, 0x4b7c9220, 0x9e4f10e6, 0xdd84b661, 0x59df2c32, 0x1a148ab5, 0x51681895, 0x12a3be12, 0x96f82441, 0xd53382c6, 0x7d995117, 0x3e52f790, 0xba096dc3, 0xf9c2cb44, 0xb2be5964, 0xf175ffe3, 0x752e65b0, 0x36e5c337, 0xe3d641f1, 0xa01de776, 0x24467d25, 0x678ddba2, 0x2cf14982, 0x6f3aef05, 0xeb617556, 0xa8aad3d1, 0xfa32a32e, 0xb9f905a9, 0x3da29ffa, 0x7e69397d, 0x3515ab5d, 0x76de0dda, 0xf2859789, 0xb14e310e, 0x647db3c8, 0x27b6154f, 0xa3ed8f1c, 0xe026299b, 0xab5abbbb, 0xe8911d3c, 0x6cca876f, 0x2f0121e8, 0x87abf239, 0xc46054be, 0x403bceed, 0x03f0686a, 0x488cfa4a, 0x0b475ccd, 0x8f1cc69e, 0xccd76019, 0x19e4e2df, 0x5a2f4458, 0xde74de0b, 0x9dbf788c, 0xd6c3eaac, 0x95084c2b, 0x1153d678, 0x529870ff, 0xf465465d, 0xb7aee0da, 0x33f57a89, 0x703edc0e, 0x3b424e2e, 0x7889e8a9, 0xfcd272fa, 0xbf19d47d, 0x6a2a56bb, 0x29e1f03c, 0xadba6a6f, 0xee71cce8, 0xa50d5ec8, 0xe6c6f84f, 0x629d621c, 0x2156c49b, 0x89fc174a, 0xca37b1cd, 0x4e6c2b9e, 0x0da78d19, 0x46db1f39, 0x0510b9be, 0x814b23ed, 0xc280856a, 0x17b307ac, 0x5478a12b, 0xd0233b78, 0x93e89dff, 0xd8940fdf, 0x9b5fa958, 0x1f04330b, 0x5ccf958c, 0x0e57e573, 0x4d9c43f4, 0xc9c7d9a7, 0x8a0c7f20, 0xc170ed00, 0x82bb4b87, 0x06e0d1d4, 0x452b7753, 0x9018f595, 0xd3d35312, 0x5788c941, 0x14436fc6, 0x5f3ffde6, 0x1cf45b61, 0x98afc132, 0xdb6467b5, 0x73ceb464, 0x300512e3, 0xb45e88b0, 0xf7952e37, 0xbce9bc17, 0xff221a90, 0x7b7980c3, 0x38b22644, 0xed81a482, 0xae4a0205, 0x2a119856, 0x69da3ed1, 0x22a6acf1, 0x616d0a76, 0xe5369025, 0xa6fd36a2, 0xe8cb8cba, 0xab002a3d, 0x2f5bb06e, 0x6c9016e9, 0x27ec84c9, 0x6427224e, 0xe07cb81d, 0xa3b71e9a, 0x76849c5c, 0x354f3adb, 0xb114a088, 0xf2df060f, 0xb9a3942f, 0xfa6832a8, 0x7e33a8fb, 0x3df80e7c, 0x9552ddad, 0xd6997b2a, 0x52c2e179, 0x110947fe, 0x5a75d5de, 0x19be7359, 0x9de5e90a, 0xde2e4f8d, 0x0b1dcd4b, 0x48d66bcc, 0xcc8df19f, 0x8f465718, 0xc43ac538, 0x87f163bf, 0x03aaf9ec, 0x40615f6b, 0x12f92f94, 0x51328913, 0xd5691340, 0x96a2b5c7, 0xddde27e7, 0x9e158160, 0x1a4e1b33, 0x5985bdb4, 0x8cb63f72, 0xcf7d99f5, 0x4b2603a6, 0x08eda521, 0x43913701, 0x005a9186, 0x84010bd5, 0xc7caad52, 0x6f607e83, 0x2cabd804, 0xa8f04257, 0xeb3be4d0, 0xa04776f0, 0xe38cd077, 0x67d74a24, 0x241ceca3, 0xf12f6e65, 0xb2e4c8e2, 0x36bf52b1, 0x7574f436, 0x3e086616, 0x7dc3c091, 0xf9985ac2, 0xba53fc45, 0x1caecae7, 0x5f656c60, 0xdb3ef633, 0x98f550b4, 0xd389c294, 0x90426413, 0x1419fe40, 0x57d258c7, 0x82e1da01, 0xc12a7c86, 0x4571e6d5, 0x06ba4052, 0x4dc6d272, 0x0e0d74f5, 0x8a56eea6, 0xc99d4821, 0x61379bf0, 0x22fc3d77, 0xa6a7a724, 0xe56c01a3, 0xae109383, 0xeddb3504, 0x6980af57, 0x2a4b09d0, 0xff788b16, 0xbcb32d91, 0x38e8b7c2, 0x7b231145, 0x305f8365, 0x739425e2, 0xf7cfbfb1, 0xb4041936, 0xe69c69c9, 0xa557cf4e, 0x210c551d, 0x62c7f39a, 0x29bb61ba, 0x6a70c73d, 0xee2b5d6e, 0xade0fbe9, 0x78d3792f, 0x3b18dfa8, 0xbf4345fb, 0xfc88e37c, 0xb7f4715c, 0xf43fd7db, 0x70644d88, 0x33afeb0f, 0x9b0538de, 0xd8ce9e59, 0x5c95040a, 0x1f5ea28d, 0x542230ad, 0x17e9962a, 0x93b20c79, 0xd079aafe, 0x054a2838, 0x46818ebf, 0xc2da14ec, 0x8111b26b, 0xca6d204b, 0x89a686cc, 0x0dfd1c9f, 0x4e36ba18}, {0x00000000, 0xe1b652ef, 0x836bd405, 0x62dd86ea, 0x06d7a80b, 0xe761fae4, 0x85bc7c0e, 0x640a2ee1, 0x0cae5117, 0xed1803f8, 0x8fc58512, 0x6e73d7fd, 0x0a79f91c, 0xebcfabf3, 0x89122d19, 0x68a47ff6, 0x185ca32e, 0xf9eaf1c1, 0x9b37772b, 0x7a8125c4, 0x1e8b0b25, 0xff3d59ca, 0x9de0df20, 0x7c568dcf, 0x14f2f239, 0xf544a0d6, 0x9799263c, 0x762f74d3, 0x12255a32, 0xf39308dd, 0x914e8e37, 0x70f8dcd8, 0x30b8465d, 0xd10e14b2, 0xb3d39258, 0x5265c0b7, 0x366fee56, 0xd7d9bcb9, 0xb5043a53, 0x54b268bc, 0x3c16174a, 0xdda045a5, 0xbf7dc34f, 0x5ecb91a0, 0x3ac1bf41, 0xdb77edae, 0xb9aa6b44, 0x581c39ab, 0x28e4e573, 0xc952b79c, 0xab8f3176, 0x4a396399, 0x2e334d78, 0xcf851f97, 0xad58997d, 0x4ceecb92, 0x244ab464, 0xc5fce68b, 0xa7216061, 0x4697328e, 0x229d1c6f, 0xc32b4e80, 0xa1f6c86a, 0x40409a85, 0x60708dba, 0x81c6df55, 0xe31b59bf, 0x02ad0b50, 0x66a725b1, 0x8711775e, 0xe5ccf1b4, 0x047aa35b, 0x6cdedcad, 0x8d688e42, 0xefb508a8, 0x0e035a47, 0x6a0974a6, 0x8bbf2649, 0xe962a0a3, 0x08d4f24c, 0x782c2e94, 0x999a7c7b, 0xfb47fa91, 0x1af1a87e, 0x7efb869f, 0x9f4dd470, 0xfd90529a, 0x1c260075, 0x74827f83, 0x95342d6c, 0xf7e9ab86, 0x165ff969, 0x7255d788, 0x93e38567, 0xf13e038d, 0x10885162, 0x50c8cbe7, 0xb17e9908, 0xd3a31fe2, 0x32154d0d, 0x561f63ec, 0xb7a93103, 0xd574b7e9, 0x34c2e506, 0x5c669af0, 0xbdd0c81f, 0xdf0d4ef5, 0x3ebb1c1a, 0x5ab132fb, 0xbb076014, 0xd9dae6fe, 0x386cb411, 0x489468c9, 0xa9223a26, 0xcbffbccc, 0x2a49ee23, 0x4e43c0c2, 0xaff5922d, 0xcd2814c7, 0x2c9e4628, 0x443a39de, 0xa58c6b31, 0xc751eddb, 0x26e7bf34, 0x42ed91d5, 0xa35bc33a, 0xc18645d0, 0x2030173f, 0x81e66bae, 0x60503941, 0x028dbfab, 0xe33bed44, 0x8731c3a5, 0x6687914a, 0x045a17a0, 0xe5ec454f, 0x8d483ab9, 0x6cfe6856, 0x0e23eebc, 0xef95bc53, 0x8b9f92b2, 0x6a29c05d, 0x08f446b7, 0xe9421458, 0x99bac880, 0x780c9a6f, 0x1ad11c85, 0xfb674e6a, 0x9f6d608b, 0x7edb3264, 0x1c06b48e, 0xfdb0e661, 0x95149997, 0x74a2cb78, 0x167f4d92, 0xf7c91f7d, 0x93c3319c, 0x72756373, 0x10a8e599, 0xf11eb776, 0xb15e2df3, 0x50e87f1c, 0x3235f9f6, 0xd383ab19, 0xb78985f8, 0x563fd717, 0x34e251fd, 0xd5540312, 0xbdf07ce4, 0x5c462e0b, 0x3e9ba8e1, 0xdf2dfa0e, 0xbb27d4ef, 0x5a918600, 0x384c00ea, 0xd9fa5205, 0xa9028edd, 0x48b4dc32, 0x2a695ad8, 0xcbdf0837, 0xafd526d6, 0x4e637439, 0x2cbef2d3, 0xcd08a03c, 0xa5acdfca, 0x441a8d25, 0x26c70bcf, 0xc7715920, 0xa37b77c1, 0x42cd252e, 0x2010a3c4, 0xc1a6f12b, 0xe196e614, 0x0020b4fb, 0x62fd3211, 0x834b60fe, 0xe7414e1f, 0x06f71cf0, 0x642a9a1a, 0x859cc8f5, 0xed38b703, 0x0c8ee5ec, 0x6e536306, 0x8fe531e9, 0xebef1f08, 0x0a594de7, 0x6884cb0d, 0x893299e2, 0xf9ca453a, 0x187c17d5, 0x7aa1913f, 0x9b17c3d0, 0xff1ded31, 0x1eabbfde, 0x7c763934, 0x9dc06bdb, 0xf564142d, 0x14d246c2, 0x760fc028, 0x97b992c7, 0xf3b3bc26, 0x1205eec9, 0x70d86823, 0x916e3acc, 0xd12ea049, 0x3098f2a6, 0x5245744c, 0xb3f326a3, 0xd7f90842, 0x364f5aad, 0x5492dc47, 0xb5248ea8, 0xdd80f15e, 0x3c36a3b1, 0x5eeb255b, 0xbf5d77b4, 0xdb575955, 0x3ae10bba, 0x583c8d50, 0xb98adfbf, 0xc9720367, 0x28c45188, 0x4a19d762, 0xabaf858d, 0xcfa5ab6c, 0x2e13f983, 0x4cce7f69, 0xad782d86, 0xc5dc5270, 0x246a009f, 0x46b78675, 0xa701d49a, 0xc30bfa7b, 0x22bda894, 0x40602e7e, 0xa1d67c91}, {0x00000000, 0x5880e2d7, 0xf106b474, 0xa98656a3, 0xe20d68e9, 0xba8d8a3e, 0x130bdc9d, 0x4b8b3e4a, 0x851da109, 0xdd9d43de, 0x741b157d, 0x2c9bf7aa, 0x6710c9e0, 0x3f902b37, 0x96167d94, 0xce969f43, 0x0a3b4213, 0x52bba0c4, 0xfb3df667, 0xa3bd14b0, 0xe8362afa, 0xb0b6c82d, 0x19309e8e, 0x41b07c59, 0x8f26e31a, 0xd7a601cd, 0x7e20576e, 0x26a0b5b9, 0x6d2b8bf3, 0x35ab6924, 0x9c2d3f87, 0xc4addd50, 0x14768426, 0x4cf666f1, 0xe5703052, 0xbdf0d285, 0xf67beccf, 0xaefb0e18, 0x077d58bb, 0x5ffdba6c, 0x916b252f, 0xc9ebc7f8, 0x606d915b, 0x38ed738c, 0x73664dc6, 0x2be6af11, 0x8260f9b2, 0xdae01b65, 0x1e4dc635, 0x46cd24e2, 0xef4b7241, 0xb7cb9096, 0xfc40aedc, 0xa4c04c0b, 0x0d461aa8, 0x55c6f87f, 0x9b50673c, 0xc3d085eb, 0x6a56d348, 0x32d6319f, 0x795d0fd5, 0x21dded02, 0x885bbba1, 0xd0db5976, 0x28ec084d, 0x706cea9a, 0xd9eabc39, 0x816a5eee, 0xcae160a4, 0x92618273, 0x3be7d4d0, 0x63673607, 0xadf1a944, 0xf5714b93, 0x5cf71d30, 0x0477ffe7, 0x4ffcc1ad, 0x177c237a, 0xbefa75d9, 0xe67a970e, 0x22d74a5e, 0x7a57a889, 0xd3d1fe2a, 0x8b511cfd, 0xc0da22b7, 0x985ac060, 0x31dc96c3, 0x695c7414, 0xa7caeb57, 0xff4a0980, 0x56cc5f23, 0x0e4cbdf4, 0x45c783be, 0x1d476169, 0xb4c137ca, 0xec41d51d, 0x3c9a8c6b, 0x641a6ebc, 0xcd9c381f, 0x951cdac8, 0xde97e482, 0x86170655, 0x2f9150f6, 0x7711b221, 0xb9872d62, 0xe107cfb5, 0x48819916, 0x10017bc1, 0x5b8a458b, 0x030aa75c, 0xaa8cf1ff, 0xf20c1328, 0x36a1ce78, 0x6e212caf, 0xc7a77a0c, 0x9f2798db, 0xd4aca691, 0x8c2c4446, 0x25aa12e5, 0x7d2af032, 0xb3bc6f71, 0xeb3c8da6, 0x42badb05, 0x1a3a39d2, 0x51b10798, 0x0931e54f, 0xa0b7b3ec, 0xf837513b, 0x50d8119a, 0x0858f34d, 0xa1dea5ee, 0xf95e4739, 0xb2d57973, 0xea559ba4, 0x43d3cd07, 0x1b532fd0, 0xd5c5b093, 0x8d455244, 0x24c304e7, 0x7c43e630, 0x37c8d87a, 0x6f483aad, 0xc6ce6c0e, 0x9e4e8ed9, 0x5ae35389, 0x0263b15e, 0xabe5e7fd, 0xf365052a, 0xb8ee3b60, 0xe06ed9b7, 0x49e88f14, 0x11686dc3, 0xdffef280, 0x877e1057, 0x2ef846f4, 0x7678a423, 0x3df39a69, 0x657378be, 0xccf52e1d, 0x9475ccca, 0x44ae95bc, 0x1c2e776b, 0xb5a821c8, 0xed28c31f, 0xa6a3fd55, 0xfe231f82, 0x57a54921, 0x0f25abf6, 0xc1b334b5, 0x9933d662, 0x30b580c1, 0x68356216, 0x23be5c5c, 0x7b3ebe8b, 0xd2b8e828, 0x8a380aff, 0x4e95d7af, 0x16153578, 0xbf9363db, 0xe713810c, 0xac98bf46, 0xf4185d91, 0x5d9e0b32, 0x051ee9e5, 0xcb8876a6, 0x93089471, 0x3a8ec2d2, 0x620e2005, 0x29851e4f, 0x7105fc98, 0xd883aa3b, 0x800348ec, 0x783419d7, 0x20b4fb00, 0x8932ada3, 0xd1b24f74, 0x9a39713e, 0xc2b993e9, 0x6b3fc54a, 0x33bf279d, 0xfd29b8de, 0xa5a95a09, 0x0c2f0caa, 0x54afee7d, 0x1f24d037, 0x47a432e0, 0xee226443, 0xb6a28694, 0x720f5bc4, 0x2a8fb913, 0x8309efb0, 0xdb890d67, 0x9002332d, 0xc882d1fa, 0x61048759, 0x3984658e, 0xf712facd, 0xaf92181a, 0x06144eb9, 0x5e94ac6e, 0x151f9224, 0x4d9f70f3, 0xe4192650, 0xbc99c487, 0x6c429df1, 0x34c27f26, 0x9d442985, 0xc5c4cb52, 0x8e4ff518, 0xd6cf17cf, 0x7f49416c, 0x27c9a3bb, 0xe95f3cf8, 0xb1dfde2f, 0x1859888c, 0x40d96a5b, 0x0b525411, 0x53d2b6c6, 0xfa54e065, 0xa2d402b2, 0x6679dfe2, 0x3ef93d35, 0x977f6b96, 0xcfff8941, 0x8474b70b, 0xdcf455dc, 0x7572037f, 0x2df2e1a8, 0xe3647eeb, 0xbbe49c3c, 0x1262ca9f, 0x4ae22848, 0x01691602, 0x59e9f4d5, 0xf06fa276, 0xa8ef40a1}, {0x00000000, 0x463b6765, 0x8c76ceca, 0xca4da9af, 0x59ebed4e, 0x1fd08a2b, 0xd59d2384, 0x93a644e1, 0xb2d6db9d, 0xf4edbcf8, 0x3ea01557, 0x789b7232, 0xeb3d36d3, 0xad0651b6, 0x674bf819, 0x21709f7c, 0x25abc6e0, 0x6390a185, 0xa9dd082a, 0xefe66f4f, 0x7c402bae, 0x3a7b4ccb, 0xf036e564, 0xb60d8201, 0x977d1d7d, 0xd1467a18, 0x1b0bd3b7, 0x5d30b4d2, 0xce96f033, 0x88ad9756, 0x42e03ef9, 0x04db599c, 0x0b50fc1a, 0x4d6b9b7f, 0x872632d0, 0xc11d55b5, 0x52bb1154, 0x14807631, 0xdecddf9e, 0x98f6b8fb, 0xb9862787, 0xffbd40e2, 0x35f0e94d, 0x73cb8e28, 0xe06dcac9, 0xa656adac, 0x6c1b0403, 0x2a206366, 0x2efb3afa, 0x68c05d9f, 0xa28df430, 0xe4b69355, 0x7710d7b4, 0x312bb0d1, 0xfb66197e, 0xbd5d7e1b, 0x9c2de167, 0xda168602, 0x105b2fad, 0x566048c8, 0xc5c60c29, 0x83fd6b4c, 0x49b0c2e3, 0x0f8ba586, 0x16a0f835, 0x509b9f50, 0x9ad636ff, 0xdced519a, 0x4f4b157b, 0x0970721e, 0xc33ddbb1, 0x8506bcd4, 0xa47623a8, 0xe24d44cd, 0x2800ed62, 0x6e3b8a07, 0xfd9dcee6, 0xbba6a983, 0x71eb002c, 0x37d06749, 0x330b3ed5, 0x753059b0, 0xbf7df01f, 0xf946977a, 0x6ae0d39b, 0x2cdbb4fe, 0xe6961d51, 0xa0ad7a34, 0x81dde548, 0xc7e6822d, 0x0dab2b82, 0x4b904ce7, 0xd8360806, 0x9e0d6f63, 0x5440c6cc, 0x127ba1a9, 0x1df0042f, 0x5bcb634a, 0x9186cae5, 0xd7bdad80, 0x441be961, 0x02208e04, 0xc86d27ab, 0x8e5640ce, 0xaf26dfb2, 0xe91db8d7, 0x23501178, 0x656b761d, 0xf6cd32fc, 0xb0f65599, 0x7abbfc36, 0x3c809b53, 0x385bc2cf, 0x7e60a5aa, 0xb42d0c05, 0xf2166b60, 0x61b02f81, 0x278b48e4, 0xedc6e14b, 0xabfd862e, 0x8a8d1952, 0xccb67e37, 0x06fbd798, 0x40c0b0fd, 0xd366f41c, 0x955d9379, 0x5f103ad6, 0x192b5db3, 0x2c40f16b, 0x6a7b960e, 0xa0363fa1, 0xe60d58c4, 0x75ab1c25, 0x33907b40, 0xf9ddd2ef, 0xbfe6b58a, 0x9e962af6, 0xd8ad4d93, 0x12e0e43c, 0x54db8359, 0xc77dc7b8, 0x8146a0dd, 0x4b0b0972, 0x0d306e17, 0x09eb378b, 0x4fd050ee, 0x859df941, 0xc3a69e24, 0x5000dac5, 0x163bbda0, 0xdc76140f, 0x9a4d736a, 0xbb3dec16, 0xfd068b73, 0x374b22dc, 0x717045b9, 0xe2d60158, 0xa4ed663d, 0x6ea0cf92, 0x289ba8f7, 0x27100d71, 0x612b6a14, 0xab66c3bb, 0xed5da4de, 0x7efbe03f, 0x38c0875a, 0xf28d2ef5, 0xb4b64990, 0x95c6d6ec, 0xd3fdb189, 0x19b01826, 0x5f8b7f43, 0xcc2d3ba2, 0x8a165cc7, 0x405bf568, 0x0660920d, 0x02bbcb91, 0x4480acf4, 0x8ecd055b, 0xc8f6623e, 0x5b5026df, 0x1d6b41ba, 0xd726e815, 0x911d8f70, 0xb06d100c, 0xf6567769, 0x3c1bdec6, 0x7a20b9a3, 0xe986fd42, 0xafbd9a27, 0x65f03388, 0x23cb54ed, 0x3ae0095e, 0x7cdb6e3b, 0xb696c794, 0xf0ada0f1, 0x630be410, 0x25308375, 0xef7d2ada, 0xa9464dbf, 0x8836d2c3, 0xce0db5a6, 0x04401c09, 0x427b7b6c, 0xd1dd3f8d, 0x97e658e8, 0x5dabf147, 0x1b909622, 0x1f4bcfbe, 0x5970a8db, 0x933d0174, 0xd5066611, 0x46a022f0, 0x009b4595, 0xcad6ec3a, 0x8ced8b5f, 0xad9d1423, 0xeba67346, 0x21ebdae9, 0x67d0bd8c, 0xf476f96d, 0xb24d9e08, 0x780037a7, 0x3e3b50c2, 0x31b0f544, 0x778b9221, 0xbdc63b8e, 0xfbfd5ceb, 0x685b180a, 0x2e607f6f, 0xe42dd6c0, 0xa216b1a5, 0x83662ed9, 0xc55d49bc, 0x0f10e013, 0x492b8776, 0xda8dc397, 0x9cb6a4f2, 0x56fb0d5d, 0x10c06a38, 0x141b33a4, 0x522054c1, 0x986dfd6e, 0xde569a0b, 0x4df0deea, 0x0bcbb98f, 0xc1861020, 0x87bd7745, 0xa6cde839, 0xe0f68f5c, 0x2abb26f3, 0x6c804196, 0xff260577, 0xb91d6212, 0x7350cbbd, 0x356bacd8}}; #endif #endif #if N == 6 #if W == 8 local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0x3db1ecdc, 0x7b63d9b8, 0x46d23564, 0xf6c7b370, 0xcb765fac, 0x8da46ac8, 0xb0158614, 0x36fe60a1, 0x0b4f8c7d, 0x4d9db919, 0x702c55c5, 0xc039d3d1, 0xfd883f0d, 0xbb5a0a69, 0x86ebe6b5, 0x6dfcc142, 0x504d2d9e, 0x169f18fa, 0x2b2ef426, 0x9b3b7232, 0xa68a9eee, 0xe058ab8a, 0xdde94756, 0x5b02a1e3, 0x66b34d3f, 0x2061785b, 0x1dd09487, 0xadc51293, 0x9074fe4f, 0xd6a6cb2b, 0xeb1727f7, 0xdbf98284, 0xe6486e58, 0xa09a5b3c, 0x9d2bb7e0, 0x2d3e31f4, 0x108fdd28, 0x565de84c, 0x6bec0490, 0xed07e225, 0xd0b60ef9, 0x96643b9d, 0xabd5d741, 0x1bc05155, 0x2671bd89, 0x60a388ed, 0x5d126431, 0xb60543c6, 0x8bb4af1a, 0xcd669a7e, 0xf0d776a2, 0x40c2f0b6, 0x7d731c6a, 0x3ba1290e, 0x0610c5d2, 0x80fb2367, 0xbd4acfbb, 0xfb98fadf, 0xc6291603, 0x763c9017, 0x4b8d7ccb, 0x0d5f49af, 0x30eea573, 0x6c820349, 0x5133ef95, 0x17e1daf1, 0x2a50362d, 0x9a45b039, 0xa7f45ce5, 0xe1266981, 0xdc97855d, 0x5a7c63e8, 0x67cd8f34, 0x211fba50, 0x1cae568c, 0xacbbd098, 0x910a3c44, 0xd7d80920, 0xea69e5fc, 0x017ec20b, 0x3ccf2ed7, 0x7a1d1bb3, 0x47acf76f, 0xf7b9717b, 0xca089da7, 0x8cdaa8c3, 0xb16b441f, 0x3780a2aa, 0x0a314e76, 0x4ce37b12, 0x715297ce, 0xc14711da, 0xfcf6fd06, 0xba24c862, 0x879524be, 0xb77b81cd, 0x8aca6d11, 0xcc185875, 0xf1a9b4a9, 0x41bc32bd, 0x7c0dde61, 0x3adfeb05, 0x076e07d9, 0x8185e16c, 0xbc340db0, 0xfae638d4, 0xc757d408, 0x7742521c, 0x4af3bec0, 0x0c218ba4, 0x31906778, 0xda87408f, 0xe736ac53, 0xa1e49937, 0x9c5575eb, 0x2c40f3ff, 0x11f11f23, 0x57232a47, 0x6a92c69b, 0xec79202e, 0xd1c8ccf2, 0x971af996, 0xaaab154a, 0x1abe935e, 0x270f7f82, 0x61dd4ae6, 0x5c6ca63a, 0xd9040692, 0xe4b5ea4e, 0xa267df2a, 0x9fd633f6, 0x2fc3b5e2, 0x1272593e, 0x54a06c5a, 0x69118086, 0xeffa6633, 0xd24b8aef, 0x9499bf8b, 0xa9285357, 0x193dd543, 0x248c399f, 0x625e0cfb, 0x5fefe027, 0xb4f8c7d0, 0x89492b0c, 0xcf9b1e68, 0xf22af2b4, 0x423f74a0, 0x7f8e987c, 0x395cad18, 0x04ed41c4, 0x8206a771, 0xbfb74bad, 0xf9657ec9, 0xc4d49215, 0x74c11401, 0x4970f8dd, 0x0fa2cdb9, 0x32132165, 0x02fd8416, 0x3f4c68ca, 0x799e5dae, 0x442fb172, 0xf43a3766, 0xc98bdbba, 0x8f59eede, 0xb2e80202, 0x3403e4b7, 0x09b2086b, 0x4f603d0f, 0x72d1d1d3, 0xc2c457c7, 0xff75bb1b, 0xb9a78e7f, 0x841662a3, 0x6f014554, 0x52b0a988, 0x14629cec, 0x29d37030, 0x99c6f624, 0xa4771af8, 0xe2a52f9c, 0xdf14c340, 0x59ff25f5, 0x644ec929, 0x229cfc4d, 0x1f2d1091, 0xaf389685, 0x92897a59, 0xd45b4f3d, 0xe9eaa3e1, 0xb58605db, 0x8837e907, 0xcee5dc63, 0xf35430bf, 0x4341b6ab, 0x7ef05a77, 0x38226f13, 0x059383cf, 0x8378657a, 0xbec989a6, 0xf81bbcc2, 0xc5aa501e, 0x75bfd60a, 0x480e3ad6, 0x0edc0fb2, 0x336de36e, 0xd87ac499, 0xe5cb2845, 0xa3191d21, 0x9ea8f1fd, 0x2ebd77e9, 0x130c9b35, 0x55deae51, 0x686f428d, 0xee84a438, 0xd33548e4, 0x95e77d80, 0xa856915c, 0x18431748, 0x25f2fb94, 0x6320cef0, 0x5e91222c, 0x6e7f875f, 0x53ce6b83, 0x151c5ee7, 0x28adb23b, 0x98b8342f, 0xa509d8f3, 0xe3dbed97, 0xde6a014b, 0x5881e7fe, 0x65300b22, 0x23e23e46, 0x1e53d29a, 0xae46548e, 0x93f7b852, 0xd5258d36, 0xe89461ea, 0x0383461d, 0x3e32aac1, 0x78e09fa5, 0x45517379, 0xf544f56d, 0xc8f519b1, 0x8e272cd5, 0xb396c009, 0x357d26bc, 0x08ccca60, 0x4e1eff04, 0x73af13d8, 0xc3ba95cc, 0xfe0b7910, 0xb8d94c74, 0x8568a0a8}, {0x00000000, 0x69790b65, 0xd2f216ca, 0xbb8b1daf, 0x7e952bd5, 0x17ec20b0, 0xac673d1f, 0xc51e367a, 0xfd2a57aa, 0x94535ccf, 0x2fd84160, 0x46a14a05, 0x83bf7c7f, 0xeac6771a, 0x514d6ab5, 0x383461d0, 0x2125a915, 0x485ca270, 0xf3d7bfdf, 0x9aaeb4ba, 0x5fb082c0, 0x36c989a5, 0x8d42940a, 0xe43b9f6f, 0xdc0ffebf, 0xb576f5da, 0x0efde875, 0x6784e310, 0xa29ad56a, 0xcbe3de0f, 0x7068c3a0, 0x1911c8c5, 0x424b522a, 0x2b32594f, 0x90b944e0, 0xf9c04f85, 0x3cde79ff, 0x55a7729a, 0xee2c6f35, 0x87556450, 0xbf610580, 0xd6180ee5, 0x6d93134a, 0x04ea182f, 0xc1f42e55, 0xa88d2530, 0x1306389f, 0x7a7f33fa, 0x636efb3f, 0x0a17f05a, 0xb19cedf5, 0xd8e5e690, 0x1dfbd0ea, 0x7482db8f, 0xcf09c620, 0xa670cd45, 0x9e44ac95, 0xf73da7f0, 0x4cb6ba5f, 0x25cfb13a, 0xe0d18740, 0x89a88c25, 0x3223918a, 0x5b5a9aef, 0x8496a454, 0xedefaf31, 0x5664b29e, 0x3f1db9fb, 0xfa038f81, 0x937a84e4, 0x28f1994b, 0x4188922e, 0x79bcf3fe, 0x10c5f89b, 0xab4ee534, 0xc237ee51, 0x0729d82b, 0x6e50d34e, 0xd5dbcee1, 0xbca2c584, 0xa5b30d41, 0xccca0624, 0x77411b8b, 0x1e3810ee, 0xdb262694, 0xb25f2df1, 0x09d4305e, 0x60ad3b3b, 0x58995aeb, 0x31e0518e, 0x8a6b4c21, 0xe3124744, 0x260c713e, 0x4f757a5b, 0xf4fe67f4, 0x9d876c91, 0xc6ddf67e, 0xafa4fd1b, 0x142fe0b4, 0x7d56ebd1, 0xb848ddab, 0xd131d6ce, 0x6abacb61, 0x03c3c004, 0x3bf7a1d4, 0x528eaab1, 0xe905b71e, 0x807cbc7b, 0x45628a01, 0x2c1b8164, 0x97909ccb, 0xfee997ae, 0xe7f85f6b, 0x8e81540e, 0x350a49a1, 0x5c7342c4, 0x996d74be, 0xf0147fdb, 0x4b9f6274, 0x22e66911, 0x1ad208c1, 0x73ab03a4, 0xc8201e0b, 0xa159156e, 0x64472314, 0x0d3e2871, 0xb6b535de, 0xdfcc3ebb, 0xd25c4ee9, 0xbb25458c, 0x00ae5823, 0x69d75346, 0xacc9653c, 0xc5b06e59, 0x7e3b73f6, 0x17427893, 0x2f761943, 0x460f1226, 0xfd840f89, 0x94fd04ec, 0x51e33296, 0x389a39f3, 0x8311245c, 0xea682f39, 0xf379e7fc, 0x9a00ec99, 0x218bf136, 0x48f2fa53, 0x8deccc29, 0xe495c74c, 0x5f1edae3, 0x3667d186, 0x0e53b056, 0x672abb33, 0xdca1a69c, 0xb5d8adf9, 0x70c69b83, 0x19bf90e6, 0xa2348d49, 0xcb4d862c, 0x90171cc3, 0xf96e17a6, 0x42e50a09, 0x2b9c016c, 0xee823716, 0x87fb3c73, 0x3c7021dc, 0x55092ab9, 0x6d3d4b69, 0x0444400c, 0xbfcf5da3, 0xd6b656c6, 0x13a860bc, 0x7ad16bd9, 0xc15a7676, 0xa8237d13, 0xb132b5d6, 0xd84bbeb3, 0x63c0a31c, 0x0ab9a879, 0xcfa79e03, 0xa6de9566, 0x1d5588c9, 0x742c83ac, 0x4c18e27c, 0x2561e919, 0x9eeaf4b6, 0xf793ffd3, 0x328dc9a9, 0x5bf4c2cc, 0xe07fdf63, 0x8906d406, 0x56caeabd, 0x3fb3e1d8, 0x8438fc77, 0xed41f712, 0x285fc168, 0x4126ca0d, 0xfaadd7a2, 0x93d4dcc7, 0xabe0bd17, 0xc299b672, 0x7912abdd, 0x106ba0b8, 0xd57596c2, 0xbc0c9da7, 0x07878008, 0x6efe8b6d, 0x77ef43a8, 0x1e9648cd, 0xa51d5562, 0xcc645e07, 0x097a687d, 0x60036318, 0xdb887eb7, 0xb2f175d2, 0x8ac51402, 0xe3bc1f67, 0x583702c8, 0x314e09ad, 0xf4503fd7, 0x9d2934b2, 0x26a2291d, 0x4fdb2278, 0x1481b897, 0x7df8b3f2, 0xc673ae5d, 0xaf0aa538, 0x6a149342, 0x036d9827, 0xb8e68588, 0xd19f8eed, 0xe9abef3d, 0x80d2e458, 0x3b59f9f7, 0x5220f292, 0x973ec4e8, 0xfe47cf8d, 0x45ccd222, 0x2cb5d947, 0x35a41182, 0x5cdd1ae7, 0xe7560748, 0x8e2f0c2d, 0x4b313a57, 0x22483132, 0x99c32c9d, 0xf0ba27f8, 0xc88e4628, 0xa1f74d4d, 0x1a7c50e2, 0x73055b87, 0xb61b6dfd, 0xdf626698, 0x64e97b37, 0x0d907052}, {0x00000000, 0x7fc99b93, 0xff933726, 0x805aacb5, 0x2457680d, 0x5b9ef39e, 0xdbc45f2b, 0xa40dc4b8, 0x48aed01a, 0x37674b89, 0xb73de73c, 0xc8f47caf, 0x6cf9b817, 0x13302384, 0x936a8f31, 0xeca314a2, 0x915da034, 0xee943ba7, 0x6ece9712, 0x11070c81, 0xb50ac839, 0xcac353aa, 0x4a99ff1f, 0x3550648c, 0xd9f3702e, 0xa63aebbd, 0x26604708, 0x59a9dc9b, 0xfda41823, 0x826d83b0, 0x02372f05, 0x7dfeb496, 0xf9ca4629, 0x8603ddba, 0x0659710f, 0x7990ea9c, 0xdd9d2e24, 0xa254b5b7, 0x220e1902, 0x5dc78291, 0xb1649633, 0xcead0da0, 0x4ef7a115, 0x313e3a86, 0x9533fe3e, 0xeafa65ad, 0x6aa0c918, 0x1569528b, 0x6897e61d, 0x175e7d8e, 0x9704d13b, 0xe8cd4aa8, 0x4cc08e10, 0x33091583, 0xb353b936, 0xcc9a22a5, 0x20393607, 0x5ff0ad94, 0xdfaa0121, 0xa0639ab2, 0x046e5e0a, 0x7ba7c599, 0xfbfd692c, 0x8434f2bf, 0x28e58a13, 0x572c1180, 0xd776bd35, 0xa8bf26a6, 0x0cb2e21e, 0x737b798d, 0xf321d538, 0x8ce84eab, 0x604b5a09, 0x1f82c19a, 0x9fd86d2f, 0xe011f6bc, 0x441c3204, 0x3bd5a997, 0xbb8f0522, 0xc4469eb1, 0xb9b82a27, 0xc671b1b4, 0x462b1d01, 0x39e28692, 0x9def422a, 0xe226d9b9, 0x627c750c, 0x1db5ee9f, 0xf116fa3d, 0x8edf61ae, 0x0e85cd1b, 0x714c5688, 0xd5419230, 0xaa8809a3, 0x2ad2a516, 0x551b3e85, 0xd12fcc3a, 0xaee657a9, 0x2ebcfb1c, 0x5175608f, 0xf578a437, 0x8ab13fa4, 0x0aeb9311, 0x75220882, 0x99811c20, 0xe64887b3, 0x66122b06, 0x19dbb095, 0xbdd6742d, 0xc21fefbe, 0x4245430b, 0x3d8cd898, 0x40726c0e, 0x3fbbf79d, 0xbfe15b28, 0xc028c0bb, 0x64250403, 0x1bec9f90, 0x9bb63325, 0xe47fa8b6, 0x08dcbc14, 0x77152787, 0xf74f8b32, 0x888610a1, 0x2c8bd419, 0x53424f8a, 0xd318e33f, 0xacd178ac, 0x51cb1426, 0x2e028fb5, 0xae582300, 0xd191b893, 0x759c7c2b, 0x0a55e7b8, 0x8a0f4b0d, 0xf5c6d09e, 0x1965c43c, 0x66ac5faf, 0xe6f6f31a, 0x993f6889, 0x3d32ac31, 0x42fb37a2, 0xc2a19b17, 0xbd680084, 0xc096b412, 0xbf5f2f81, 0x3f058334, 0x40cc18a7, 0xe4c1dc1f, 0x9b08478c, 0x1b52eb39, 0x649b70aa, 0x88386408, 0xf7f1ff9b, 0x77ab532e, 0x0862c8bd, 0xac6f0c05, 0xd3a69796, 0x53fc3b23, 0x2c35a0b0, 0xa801520f, 0xd7c8c99c, 0x57926529, 0x285bfeba, 0x8c563a02, 0xf39fa191, 0x73c50d24, 0x0c0c96b7, 0xe0af8215, 0x9f661986, 0x1f3cb533, 0x60f52ea0, 0xc4f8ea18, 0xbb31718b, 0x3b6bdd3e, 0x44a246ad, 0x395cf23b, 0x469569a8, 0xc6cfc51d, 0xb9065e8e, 0x1d0b9a36, 0x62c201a5, 0xe298ad10, 0x9d513683, 0x71f22221, 0x0e3bb9b2, 0x8e611507, 0xf1a88e94, 0x55a54a2c, 0x2a6cd1bf, 0xaa367d0a, 0xd5ffe699, 0x792e9e35, 0x06e705a6, 0x86bda913, 0xf9743280, 0x5d79f638, 0x22b06dab, 0xa2eac11e, 0xdd235a8d, 0x31804e2f, 0x4e49d5bc, 0xce137909, 0xb1dae29a, 0x15d72622, 0x6a1ebdb1, 0xea441104, 0x958d8a97, 0xe8733e01, 0x97baa592, 0x17e00927, 0x682992b4, 0xcc24560c, 0xb3edcd9f, 0x33b7612a, 0x4c7efab9, 0xa0ddee1b, 0xdf147588, 0x5f4ed93d, 0x208742ae, 0x848a8616, 0xfb431d85, 0x7b19b130, 0x04d02aa3, 0x80e4d81c, 0xff2d438f, 0x7f77ef3a, 0x00be74a9, 0xa4b3b011, 0xdb7a2b82, 0x5b208737, 0x24e91ca4, 0xc84a0806, 0xb7839395, 0x37d93f20, 0x4810a4b3, 0xec1d600b, 0x93d4fb98, 0x138e572d, 0x6c47ccbe, 0x11b97828, 0x6e70e3bb, 0xee2a4f0e, 0x91e3d49d, 0x35ee1025, 0x4a278bb6, 0xca7d2703, 0xb5b4bc90, 0x5917a832, 0x26de33a1, 0xa6849f14, 0xd94d0487, 0x7d40c03f, 0x02895bac, 0x82d3f719, 0xfd1a6c8a}, {0x00000000, 0xa396284c, 0x9c5d56d9, 0x3fcb7e95, 0xe3cbabf3, 0x405d83bf, 0x7f96fd2a, 0xdc00d566, 0x1ce651a7, 0xbf7079eb, 0x80bb077e, 0x232d2f32, 0xff2dfa54, 0x5cbbd218, 0x6370ac8d, 0xc0e684c1, 0x39cca34e, 0x9a5a8b02, 0xa591f597, 0x0607dddb, 0xda0708bd, 0x799120f1, 0x465a5e64, 0xe5cc7628, 0x252af2e9, 0x86bcdaa5, 0xb977a430, 0x1ae18c7c, 0xc6e1591a, 0x65777156, 0x5abc0fc3, 0xf92a278f, 0x7399469c, 0xd00f6ed0, 0xefc41045, 0x4c523809, 0x9052ed6f, 0x33c4c523, 0x0c0fbbb6, 0xaf9993fa, 0x6f7f173b, 0xcce93f77, 0xf32241e2, 0x50b469ae, 0x8cb4bcc8, 0x2f229484, 0x10e9ea11, 0xb37fc25d, 0x4a55e5d2, 0xe9c3cd9e, 0xd608b30b, 0x759e9b47, 0xa99e4e21, 0x0a08666d, 0x35c318f8, 0x965530b4, 0x56b3b475, 0xf5259c39, 0xcaeee2ac, 0x6978cae0, 0xb5781f86, 0x16ee37ca, 0x2925495f, 0x8ab36113, 0xe7328d38, 0x44a4a574, 0x7b6fdbe1, 0xd8f9f3ad, 0x04f926cb, 0xa76f0e87, 0x98a47012, 0x3b32585e, 0xfbd4dc9f, 0x5842f4d3, 0x67898a46, 0xc41fa20a, 0x181f776c, 0xbb895f20, 0x844221b5, 0x27d409f9, 0xdefe2e76, 0x7d68063a, 0x42a378af, 0xe13550e3, 0x3d358585, 0x9ea3adc9, 0xa168d35c, 0x02fefb10, 0xc2187fd1, 0x618e579d, 0x5e452908, 0xfdd30144, 0x21d3d422, 0x8245fc6e, 0xbd8e82fb, 0x1e18aab7, 0x94abcba4, 0x373de3e8, 0x08f69d7d, 0xab60b531, 0x77606057, 0xd4f6481b, 0xeb3d368e, 0x48ab1ec2, 0x884d9a03, 0x2bdbb24f, 0x1410ccda, 0xb786e496, 0x6b8631f0, 0xc81019bc, 0xf7db6729, 0x544d4f65, 0xad6768ea, 0x0ef140a6, 0x313a3e33, 0x92ac167f, 0x4eacc319, 0xed3aeb55, 0xd2f195c0, 0x7167bd8c, 0xb181394d, 0x12171101, 0x2ddc6f94, 0x8e4a47d8, 0x524a92be, 0xf1dcbaf2, 0xce17c467, 0x6d81ec2b, 0x15141c31, 0xb682347d, 0x89494ae8, 0x2adf62a4, 0xf6dfb7c2, 0x55499f8e, 0x6a82e11b, 0xc914c957, 0x09f24d96, 0xaa6465da, 0x95af1b4f, 0x36393303, 0xea39e665, 0x49afce29, 0x7664b0bc, 0xd5f298f0, 0x2cd8bf7f, 0x8f4e9733, 0xb085e9a6, 0x1313c1ea, 0xcf13148c, 0x6c853cc0, 0x534e4255, 0xf0d86a19, 0x303eeed8, 0x93a8c694, 0xac63b801, 0x0ff5904d, 0xd3f5452b, 0x70636d67, 0x4fa813f2, 0xec3e3bbe, 0x668d5aad, 0xc51b72e1, 0xfad00c74, 0x59462438, 0x8546f15e, 0x26d0d912, 0x191ba787, 0xba8d8fcb, 0x7a6b0b0a, 0xd9fd2346, 0xe6365dd3, 0x45a0759f, 0x99a0a0f9, 0x3a3688b5, 0x05fdf620, 0xa66bde6c, 0x5f41f9e3, 0xfcd7d1af, 0xc31caf3a, 0x608a8776, 0xbc8a5210, 0x1f1c7a5c, 0x20d704c9, 0x83412c85, 0x43a7a844, 0xe0318008, 0xdffafe9d, 0x7c6cd6d1, 0xa06c03b7, 0x03fa2bfb, 0x3c31556e, 0x9fa77d22, 0xf2269109, 0x51b0b945, 0x6e7bc7d0, 0xcdedef9c, 0x11ed3afa, 0xb27b12b6, 0x8db06c23, 0x2e26446f, 0xeec0c0ae, 0x4d56e8e2, 0x729d9677, 0xd10bbe3b, 0x0d0b6b5d, 0xae9d4311, 0x91563d84, 0x32c015c8, 0xcbea3247, 0x687c1a0b, 0x57b7649e, 0xf4214cd2, 0x282199b4, 0x8bb7b1f8, 0xb47ccf6d, 0x17eae721, 0xd70c63e0, 0x749a4bac, 0x4b513539, 0xe8c71d75, 0x34c7c813, 0x9751e05f, 0xa89a9eca, 0x0b0cb686, 0x81bfd795, 0x2229ffd9, 0x1de2814c, 0xbe74a900, 0x62747c66, 0xc1e2542a, 0xfe292abf, 0x5dbf02f3, 0x9d598632, 0x3ecfae7e, 0x0104d0eb, 0xa292f8a7, 0x7e922dc1, 0xdd04058d, 0xe2cf7b18, 0x41595354, 0xb87374db, 0x1be55c97, 0x242e2202, 0x87b80a4e, 0x5bb8df28, 0xf82ef764, 0xc7e589f1, 0x6473a1bd, 0xa495257c, 0x07030d30, 0x38c873a5, 0x9b5e5be9, 0x475e8e8f, 0xe4c8a6c3, 0xdb03d856, 0x7895f01a}, {0x00000000, 0x2a283862, 0x545070c4, 0x7e7848a6, 0xa8a0e188, 0x8288d9ea, 0xfcf0914c, 0xd6d8a92e, 0x8a30c551, 0xa018fd33, 0xde60b595, 0xf4488df7, 0x229024d9, 0x08b81cbb, 0x76c0541d, 0x5ce86c7f, 0xcf108ce3, 0xe538b481, 0x9b40fc27, 0xb168c445, 0x67b06d6b, 0x4d985509, 0x33e01daf, 0x19c825cd, 0x452049b2, 0x6f0871d0, 0x11703976, 0x3b580114, 0xed80a83a, 0xc7a89058, 0xb9d0d8fe, 0x93f8e09c, 0x45501f87, 0x6f7827e5, 0x11006f43, 0x3b285721, 0xedf0fe0f, 0xc7d8c66d, 0xb9a08ecb, 0x9388b6a9, 0xcf60dad6, 0xe548e2b4, 0x9b30aa12, 0xb1189270, 0x67c03b5e, 0x4de8033c, 0x33904b9a, 0x19b873f8, 0x8a409364, 0xa068ab06, 0xde10e3a0, 0xf438dbc2, 0x22e072ec, 0x08c84a8e, 0x76b00228, 0x5c983a4a, 0x00705635, 0x2a586e57, 0x542026f1, 0x7e081e93, 0xa8d0b7bd, 0x82f88fdf, 0xfc80c779, 0xd6a8ff1b, 0x8aa03f0e, 0xa088076c, 0xdef04fca, 0xf4d877a8, 0x2200de86, 0x0828e6e4, 0x7650ae42, 0x5c789620, 0x0090fa5f, 0x2ab8c23d, 0x54c08a9b, 0x7ee8b2f9, 0xa8301bd7, 0x821823b5, 0xfc606b13, 0xd6485371, 0x45b0b3ed, 0x6f988b8f, 0x11e0c329, 0x3bc8fb4b, 0xed105265, 0xc7386a07, 0xb94022a1, 0x93681ac3, 0xcf8076bc, 0xe5a84ede, 0x9bd00678, 0xb1f83e1a, 0x67209734, 0x4d08af56, 0x3370e7f0, 0x1958df92, 0xcff02089, 0xe5d818eb, 0x9ba0504d, 0xb188682f, 0x6750c101, 0x4d78f963, 0x3300b1c5, 0x192889a7, 0x45c0e5d8, 0x6fe8ddba, 0x1190951c, 0x3bb8ad7e, 0xed600450, 0xc7483c32, 0xb9307494, 0x93184cf6, 0x00e0ac6a, 0x2ac89408, 0x54b0dcae, 0x7e98e4cc, 0xa8404de2, 0x82687580, 0xfc103d26, 0xd6380544, 0x8ad0693b, 0xa0f85159, 0xde8019ff, 0xf4a8219d, 0x227088b3, 0x0858b0d1, 0x7620f877, 0x5c08c015, 0xce31785d, 0xe419403f, 0x9a610899, 0xb04930fb, 0x669199d5, 0x4cb9a1b7, 0x32c1e911, 0x18e9d173, 0x4401bd0c, 0x6e29856e, 0x1051cdc8, 0x3a79f5aa, 0xeca15c84, 0xc68964e6, 0xb8f12c40, 0x92d91422, 0x0121f4be, 0x2b09ccdc, 0x5571847a, 0x7f59bc18, 0xa9811536, 0x83a92d54, 0xfdd165f2, 0xd7f95d90, 0x8b1131ef, 0xa139098d, 0xdf41412b, 0xf5697949, 0x23b1d067, 0x0999e805, 0x77e1a0a3, 0x5dc998c1, 0x8b6167da, 0xa1495fb8, 0xdf31171e, 0xf5192f7c, 0x23c18652, 0x09e9be30, 0x7791f696, 0x5db9cef4, 0x0151a28b, 0x2b799ae9, 0x5501d24f, 0x7f29ea2d, 0xa9f14303, 0x83d97b61, 0xfda133c7, 0xd7890ba5, 0x4471eb39, 0x6e59d35b, 0x10219bfd, 0x3a09a39f, 0xecd10ab1, 0xc6f932d3, 0xb8817a75, 0x92a94217, 0xce412e68, 0xe469160a, 0x9a115eac, 0xb03966ce, 0x66e1cfe0, 0x4cc9f782, 0x32b1bf24, 0x18998746, 0x44914753, 0x6eb97f31, 0x10c13797, 0x3ae90ff5, 0xec31a6db, 0xc6199eb9, 0xb861d61f, 0x9249ee7d, 0xcea18202, 0xe489ba60, 0x9af1f2c6, 0xb0d9caa4, 0x6601638a, 0x4c295be8, 0x3251134e, 0x18792b2c, 0x8b81cbb0, 0xa1a9f3d2, 0xdfd1bb74, 0xf5f98316, 0x23212a38, 0x0909125a, 0x77715afc, 0x5d59629e, 0x01b10ee1, 0x2b993683, 0x55e17e25, 0x7fc94647, 0xa911ef69, 0x8339d70b, 0xfd419fad, 0xd769a7cf, 0x01c158d4, 0x2be960b6, 0x55912810, 0x7fb91072, 0xa961b95c, 0x8349813e, 0xfd31c998, 0xd719f1fa, 0x8bf19d85, 0xa1d9a5e7, 0xdfa1ed41, 0xf589d523, 0x23517c0d, 0x0979446f, 0x77010cc9, 0x5d2934ab, 0xced1d437, 0xe4f9ec55, 0x9a81a4f3, 0xb0a99c91, 0x667135bf, 0x4c590ddd, 0x3221457b, 0x18097d19, 0x44e11166, 0x6ec92904, 0x10b161a2, 0x3a9959c0, 0xec41f0ee, 0xc669c88c, 0xb811802a, 0x9239b848}, {0x00000000, 0x4713f6fb, 0x8e27edf6, 0xc9341b0d, 0xc73eddad, 0x802d2b56, 0x4919305b, 0x0e0ac6a0, 0x550cbd1b, 0x121f4be0, 0xdb2b50ed, 0x9c38a616, 0x923260b6, 0xd521964d, 0x1c158d40, 0x5b067bbb, 0xaa197a36, 0xed0a8ccd, 0x243e97c0, 0x632d613b, 0x6d27a79b, 0x2a345160, 0xe3004a6d, 0xa413bc96, 0xff15c72d, 0xb80631d6, 0x71322adb, 0x3621dc20, 0x382b1a80, 0x7f38ec7b, 0xb60cf776, 0xf11f018d, 0x8f43f22d, 0xc85004d6, 0x01641fdb, 0x4677e920, 0x487d2f80, 0x0f6ed97b, 0xc65ac276, 0x8149348d, 0xda4f4f36, 0x9d5cb9cd, 0x5468a2c0, 0x137b543b, 0x1d71929b, 0x5a626460, 0x93567f6d, 0xd4458996, 0x255a881b, 0x62497ee0, 0xab7d65ed, 0xec6e9316, 0xe26455b6, 0xa577a34d, 0x6c43b840, 0x2b504ebb, 0x70563500, 0x3745c3fb, 0xfe71d8f6, 0xb9622e0d, 0xb768e8ad, 0xf07b1e56, 0x394f055b, 0x7e5cf3a0, 0xc5f6e21b, 0x82e514e0, 0x4bd10fed, 0x0cc2f916, 0x02c83fb6, 0x45dbc94d, 0x8cefd240, 0xcbfc24bb, 0x90fa5f00, 0xd7e9a9fb, 0x1eddb2f6, 0x59ce440d, 0x57c482ad, 0x10d77456, 0xd9e36f5b, 0x9ef099a0, 0x6fef982d, 0x28fc6ed6, 0xe1c875db, 0xa6db8320, 0xa8d14580, 0xefc2b37b, 0x26f6a876, 0x61e55e8d, 0x3ae32536, 0x7df0d3cd, 0xb4c4c8c0, 0xf3d73e3b, 0xfdddf89b, 0xbace0e60, 0x73fa156d, 0x34e9e396, 0x4ab51036, 0x0da6e6cd, 0xc492fdc0, 0x83810b3b, 0x8d8bcd9b, 0xca983b60, 0x03ac206d, 0x44bfd696, 0x1fb9ad2d, 0x58aa5bd6, 0x919e40db, 0xd68db620, 0xd8877080, 0x9f94867b, 0x56a09d76, 0x11b36b8d, 0xe0ac6a00, 0xa7bf9cfb, 0x6e8b87f6, 0x2998710d, 0x2792b7ad, 0x60814156, 0xa9b55a5b, 0xeea6aca0, 0xb5a0d71b, 0xf2b321e0, 0x3b873aed, 0x7c94cc16, 0x729e0ab6, 0x358dfc4d, 0xfcb9e740, 0xbbaa11bb, 0x509cc277, 0x178f348c, 0xdebb2f81, 0x99a8d97a, 0x97a21fda, 0xd0b1e921, 0x1985f22c, 0x5e9604d7, 0x05907f6c, 0x42838997, 0x8bb7929a, 0xcca46461, 0xc2aea2c1, 0x85bd543a, 0x4c894f37, 0x0b9ab9cc, 0xfa85b841, 0xbd964eba, 0x74a255b7, 0x33b1a34c, 0x3dbb65ec, 0x7aa89317, 0xb39c881a, 0xf48f7ee1, 0xaf89055a, 0xe89af3a1, 0x21aee8ac, 0x66bd1e57, 0x68b7d8f7, 0x2fa42e0c, 0xe6903501, 0xa183c3fa, 0xdfdf305a, 0x98ccc6a1, 0x51f8ddac, 0x16eb2b57, 0x18e1edf7, 0x5ff21b0c, 0x96c60001, 0xd1d5f6fa, 0x8ad38d41, 0xcdc07bba, 0x04f460b7, 0x43e7964c, 0x4ded50ec, 0x0afea617, 0xc3cabd1a, 0x84d94be1, 0x75c64a6c, 0x32d5bc97, 0xfbe1a79a, 0xbcf25161, 0xb2f897c1, 0xf5eb613a, 0x3cdf7a37, 0x7bcc8ccc, 0x20caf777, 0x67d9018c, 0xaeed1a81, 0xe9feec7a, 0xe7f42ada, 0xa0e7dc21, 0x69d3c72c, 0x2ec031d7, 0x956a206c, 0xd279d697, 0x1b4dcd9a, 0x5c5e3b61, 0x5254fdc1, 0x15470b3a, 0xdc731037, 0x9b60e6cc, 0xc0669d77, 0x87756b8c, 0x4e417081, 0x0952867a, 0x075840da, 0x404bb621, 0x897fad2c, 0xce6c5bd7, 0x3f735a5a, 0x7860aca1, 0xb154b7ac, 0xf6474157, 0xf84d87f7, 0xbf5e710c, 0x766a6a01, 0x31799cfa, 0x6a7fe741, 0x2d6c11ba, 0xe4580ab7, 0xa34bfc4c, 0xad413aec, 0xea52cc17, 0x2366d71a, 0x647521e1, 0x1a29d241, 0x5d3a24ba, 0x940e3fb7, 0xd31dc94c, 0xdd170fec, 0x9a04f917, 0x5330e21a, 0x142314e1, 0x4f256f5a, 0x083699a1, 0xc10282ac, 0x86117457, 0x881bb2f7, 0xcf08440c, 0x063c5f01, 0x412fa9fa, 0xb030a877, 0xf7235e8c, 0x3e174581, 0x7904b37a, 0x770e75da, 0x301d8321, 0xf929982c, 0xbe3a6ed7, 0xe53c156c, 0xa22fe397, 0x6b1bf89a, 0x2c080e61, 0x2202c8c1, 0x65113e3a, 0xac252537, 0xeb36d3cc}, {0x00000000, 0xa13984ee, 0x99020f9d, 0x383b8b73, 0xe975197b, 0x484c9d95, 0x707716e6, 0xd14e9208, 0x099b34b7, 0xa8a2b059, 0x90993b2a, 0x31a0bfc4, 0xe0ee2dcc, 0x41d7a922, 0x79ec2251, 0xd8d5a6bf, 0x1336696e, 0xb20fed80, 0x8a3466f3, 0x2b0de21d, 0xfa437015, 0x5b7af4fb, 0x63417f88, 0xc278fb66, 0x1aad5dd9, 0xbb94d937, 0x83af5244, 0x2296d6aa, 0xf3d844a2, 0x52e1c04c, 0x6ada4b3f, 0xcbe3cfd1, 0x266cd2dc, 0x87555632, 0xbf6edd41, 0x1e5759af, 0xcf19cba7, 0x6e204f49, 0x561bc43a, 0xf72240d4, 0x2ff7e66b, 0x8ece6285, 0xb6f5e9f6, 0x17cc6d18, 0xc682ff10, 0x67bb7bfe, 0x5f80f08d, 0xfeb97463, 0x355abbb2, 0x94633f5c, 0xac58b42f, 0x0d6130c1, 0xdc2fa2c9, 0x7d162627, 0x452dad54, 0xe41429ba, 0x3cc18f05, 0x9df80beb, 0xa5c38098, 0x04fa0476, 0xd5b4967e, 0x748d1290, 0x4cb699e3, 0xed8f1d0d, 0x4cd9a5b8, 0xede02156, 0xd5dbaa25, 0x74e22ecb, 0xa5acbcc3, 0x0495382d, 0x3caeb35e, 0x9d9737b0, 0x4542910f, 0xe47b15e1, 0xdc409e92, 0x7d791a7c, 0xac378874, 0x0d0e0c9a, 0x353587e9, 0x940c0307, 0x5fefccd6, 0xfed64838, 0xc6edc34b, 0x67d447a5, 0xb69ad5ad, 0x17a35143, 0x2f98da30, 0x8ea15ede, 0x5674f861, 0xf74d7c8f, 0xcf76f7fc, 0x6e4f7312, 0xbf01e11a, 0x1e3865f4, 0x2603ee87, 0x873a6a69, 0x6ab57764, 0xcb8cf38a, 0xf3b778f9, 0x528efc17, 0x83c06e1f, 0x22f9eaf1, 0x1ac26182, 0xbbfbe56c, 0x632e43d3, 0xc217c73d, 0xfa2c4c4e, 0x5b15c8a0, 0x8a5b5aa8, 0x2b62de46, 0x13595535, 0xb260d1db, 0x79831e0a, 0xd8ba9ae4, 0xe0811197, 0x41b89579, 0x90f60771, 0x31cf839f, 0x09f408ec, 0xa8cd8c02, 0x70182abd, 0xd121ae53, 0xe91a2520, 0x4823a1ce, 0x996d33c6, 0x3854b728, 0x006f3c5b, 0xa156b8b5, 0x99b34b70, 0x388acf9e, 0x00b144ed, 0xa188c003, 0x70c6520b, 0xd1ffd6e5, 0xe9c45d96, 0x48fdd978, 0x90287fc7, 0x3111fb29, 0x092a705a, 0xa813f4b4, 0x795d66bc, 0xd864e252, 0xe05f6921, 0x4166edcf, 0x8a85221e, 0x2bbca6f0, 0x13872d83, 0xb2bea96d, 0x63f03b65, 0xc2c9bf8b, 0xfaf234f8, 0x5bcbb016, 0x831e16a9, 0x22279247, 0x1a1c1934, 0xbb259dda, 0x6a6b0fd2, 0xcb528b3c, 0xf369004f, 0x525084a1, 0xbfdf99ac, 0x1ee61d42, 0x26dd9631, 0x87e412df, 0x56aa80d7, 0xf7930439, 0xcfa88f4a, 0x6e910ba4, 0xb644ad1b, 0x177d29f5, 0x2f46a286, 0x8e7f2668, 0x5f31b460, 0xfe08308e, 0xc633bbfd, 0x670a3f13, 0xace9f0c2, 0x0dd0742c, 0x35ebff5f, 0x94d27bb1, 0x459ce9b9, 0xe4a56d57, 0xdc9ee624, 0x7da762ca, 0xa572c475, 0x044b409b, 0x3c70cbe8, 0x9d494f06, 0x4c07dd0e, 0xed3e59e0, 0xd505d293, 0x743c567d, 0xd56aeec8, 0x74536a26, 0x4c68e155, 0xed5165bb, 0x3c1ff7b3, 0x9d26735d, 0xa51df82e, 0x04247cc0, 0xdcf1da7f, 0x7dc85e91, 0x45f3d5e2, 0xe4ca510c, 0x3584c304, 0x94bd47ea, 0xac86cc99, 0x0dbf4877, 0xc65c87a6, 0x67650348, 0x5f5e883b, 0xfe670cd5, 0x2f299edd, 0x8e101a33, 0xb62b9140, 0x171215ae, 0xcfc7b311, 0x6efe37ff, 0x56c5bc8c, 0xf7fc3862, 0x26b2aa6a, 0x878b2e84, 0xbfb0a5f7, 0x1e892119, 0xf3063c14, 0x523fb8fa, 0x6a043389, 0xcb3db767, 0x1a73256f, 0xbb4aa181, 0x83712af2, 0x2248ae1c, 0xfa9d08a3, 0x5ba48c4d, 0x639f073e, 0xc2a683d0, 0x13e811d8, 0xb2d19536, 0x8aea1e45, 0x2bd39aab, 0xe030557a, 0x4109d194, 0x79325ae7, 0xd80bde09, 0x09454c01, 0xa87cc8ef, 0x9047439c, 0x317ec772, 0xe9ab61cd, 0x4892e523, 0x70a96e50, 0xd190eabe, 0x00de78b6, 0xa1e7fc58, 0x99dc772b, 0x38e5f3c5}, {0x00000000, 0xe81790a1, 0x0b5e2703, 0xe349b7a2, 0x16bc4e06, 0xfeabdea7, 0x1de26905, 0xf5f5f9a4, 0x2d789c0c, 0xc56f0cad, 0x2626bb0f, 0xce312bae, 0x3bc4d20a, 0xd3d342ab, 0x309af509, 0xd88d65a8, 0x5af13818, 0xb2e6a8b9, 0x51af1f1b, 0xb9b88fba, 0x4c4d761e, 0xa45ae6bf, 0x4713511d, 0xaf04c1bc, 0x7789a414, 0x9f9e34b5, 0x7cd78317, 0x94c013b6, 0x6135ea12, 0x89227ab3, 0x6a6bcd11, 0x827c5db0, 0xb5e27030, 0x5df5e091, 0xbebc5733, 0x56abc792, 0xa35e3e36, 0x4b49ae97, 0xa8001935, 0x40178994, 0x989aec3c, 0x708d7c9d, 0x93c4cb3f, 0x7bd35b9e, 0x8e26a23a, 0x6631329b, 0x85788539, 0x6d6f1598, 0xef134828, 0x0704d889, 0xe44d6f2b, 0x0c5aff8a, 0xf9af062e, 0x11b8968f, 0xf2f1212d, 0x1ae6b18c, 0xc26bd424, 0x2a7c4485, 0xc935f327, 0x21226386, 0xd4d79a22, 0x3cc00a83, 0xdf89bd21, 0x379e2d80, 0xb0b5e621, 0x58a27680, 0xbbebc122, 0x53fc5183, 0xa609a827, 0x4e1e3886, 0xad578f24, 0x45401f85, 0x9dcd7a2d, 0x75daea8c, 0x96935d2e, 0x7e84cd8f, 0x8b71342b, 0x6366a48a, 0x802f1328, 0x68388389, 0xea44de39, 0x02534e98, 0xe11af93a, 0x090d699b, 0xfcf8903f, 0x14ef009e, 0xf7a6b73c, 0x1fb1279d, 0xc73c4235, 0x2f2bd294, 0xcc626536, 0x2475f597, 0xd1800c33, 0x39979c92, 0xdade2b30, 0x32c9bb91, 0x05579611, 0xed4006b0, 0x0e09b112, 0xe61e21b3, 0x13ebd817, 0xfbfc48b6, 0x18b5ff14, 0xf0a26fb5, 0x282f0a1d, 0xc0389abc, 0x23712d1e, 0xcb66bdbf, 0x3e93441b, 0xd684d4ba, 0x35cd6318, 0xdddaf3b9, 0x5fa6ae09, 0xb7b13ea8, 0x54f8890a, 0xbcef19ab, 0x491ae00f, 0xa10d70ae, 0x4244c70c, 0xaa5357ad, 0x72de3205, 0x9ac9a2a4, 0x79801506, 0x919785a7, 0x64627c03, 0x8c75eca2, 0x6f3c5b00, 0x872bcba1, 0xba1aca03, 0x520d5aa2, 0xb144ed00, 0x59537da1, 0xaca68405, 0x44b114a4, 0xa7f8a306, 0x4fef33a7, 0x9762560f, 0x7f75c6ae, 0x9c3c710c, 0x742be1ad, 0x81de1809, 0x69c988a8, 0x8a803f0a, 0x6297afab, 0xe0ebf21b, 0x08fc62ba, 0xebb5d518, 0x03a245b9, 0xf657bc1d, 0x1e402cbc, 0xfd099b1e, 0x151e0bbf, 0xcd936e17, 0x2584feb6, 0xc6cd4914, 0x2edad9b5, 0xdb2f2011, 0x3338b0b0, 0xd0710712, 0x386697b3, 0x0ff8ba33, 0xe7ef2a92, 0x04a69d30, 0xecb10d91, 0x1944f435, 0xf1536494, 0x121ad336, 0xfa0d4397, 0x2280263f, 0xca97b69e, 0x29de013c, 0xc1c9919d, 0x343c6839, 0xdc2bf898, 0x3f624f3a, 0xd775df9b, 0x5509822b, 0xbd1e128a, 0x5e57a528, 0xb6403589, 0x43b5cc2d, 0xaba25c8c, 0x48ebeb2e, 0xa0fc7b8f, 0x78711e27, 0x90668e86, 0x732f3924, 0x9b38a985, 0x6ecd5021, 0x86dac080, 0x65937722, 0x8d84e783, 0x0aaf2c22, 0xe2b8bc83, 0x01f10b21, 0xe9e69b80, 0x1c136224, 0xf404f285, 0x174d4527, 0xff5ad586, 0x27d7b02e, 0xcfc0208f, 0x2c89972d, 0xc49e078c, 0x316bfe28, 0xd97c6e89, 0x3a35d92b, 0xd222498a, 0x505e143a, 0xb849849b, 0x5b003339, 0xb317a398, 0x46e25a3c, 0xaef5ca9d, 0x4dbc7d3f, 0xa5abed9e, 0x7d268836, 0x95311897, 0x7678af35, 0x9e6f3f94, 0x6b9ac630, 0x838d5691, 0x60c4e133, 0x88d37192, 0xbf4d5c12, 0x575accb3, 0xb4137b11, 0x5c04ebb0, 0xa9f11214, 0x41e682b5, 0xa2af3517, 0x4ab8a5b6, 0x9235c01e, 0x7a2250bf, 0x996be71d, 0x717c77bc, 0x84898e18, 0x6c9e1eb9, 0x8fd7a91b, 0x67c039ba, 0xe5bc640a, 0x0dabf4ab, 0xeee24309, 0x06f5d3a8, 0xf3002a0c, 0x1b17baad, 0xf85e0d0f, 0x10499dae, 0xc8c4f806, 0x20d368a7, 0xc39adf05, 0x2b8d4fa4, 0xde78b600, 0x366f26a1, 0xd5269103, 0x3d3101a2}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x0000000000000000, 0xa19017e800000000, 0x03275e0b00000000, 0xa2b749e300000000, 0x064ebc1600000000, 0xa7deabfe00000000, 0x0569e21d00000000, 0xa4f9f5f500000000, 0x0c9c782d00000000, 0xad0c6fc500000000, 0x0fbb262600000000, 0xae2b31ce00000000, 0x0ad2c43b00000000, 0xab42d3d300000000, 0x09f59a3000000000, 0xa8658dd800000000, 0x1838f15a00000000, 0xb9a8e6b200000000, 0x1b1faf5100000000, 0xba8fb8b900000000, 0x1e764d4c00000000, 0xbfe65aa400000000, 0x1d51134700000000, 0xbcc104af00000000, 0x14a4897700000000, 0xb5349e9f00000000, 0x1783d77c00000000, 0xb613c09400000000, 0x12ea356100000000, 0xb37a228900000000, 0x11cd6b6a00000000, 0xb05d7c8200000000, 0x3070e2b500000000, 0x91e0f55d00000000, 0x3357bcbe00000000, 0x92c7ab5600000000, 0x363e5ea300000000, 0x97ae494b00000000, 0x351900a800000000, 0x9489174000000000, 0x3cec9a9800000000, 0x9d7c8d7000000000, 0x3fcbc49300000000, 0x9e5bd37b00000000, 0x3aa2268e00000000, 0x9b32316600000000, 0x3985788500000000, 0x98156f6d00000000, 0x284813ef00000000, 0x89d8040700000000, 0x2b6f4de400000000, 0x8aff5a0c00000000, 0x2e06aff900000000, 0x8f96b81100000000, 0x2d21f1f200000000, 0x8cb1e61a00000000, 0x24d46bc200000000, 0x85447c2a00000000, 0x27f335c900000000, 0x8663222100000000, 0x229ad7d400000000, 0x830ac03c00000000, 0x21bd89df00000000, 0x802d9e3700000000, 0x21e6b5b000000000, 0x8076a25800000000, 0x22c1ebbb00000000, 0x8351fc5300000000, 0x27a809a600000000, 0x86381e4e00000000, 0x248f57ad00000000, 0x851f404500000000, 0x2d7acd9d00000000, 0x8ceada7500000000, 0x2e5d939600000000, 0x8fcd847e00000000, 0x2b34718b00000000, 0x8aa4666300000000, 0x28132f8000000000, 0x8983386800000000, 0x39de44ea00000000, 0x984e530200000000, 0x3af91ae100000000, 0x9b690d0900000000, 0x3f90f8fc00000000, 0x9e00ef1400000000, 0x3cb7a6f700000000, 0x9d27b11f00000000, 0x35423cc700000000, 0x94d22b2f00000000, 0x366562cc00000000, 0x97f5752400000000, 0x330c80d100000000, 0x929c973900000000, 0x302bdeda00000000, 0x91bbc93200000000, 0x1196570500000000, 0xb00640ed00000000, 0x12b1090e00000000, 0xb3211ee600000000, 0x17d8eb1300000000, 0xb648fcfb00000000, 0x14ffb51800000000, 0xb56fa2f000000000, 0x1d0a2f2800000000, 0xbc9a38c000000000, 0x1e2d712300000000, 0xbfbd66cb00000000, 0x1b44933e00000000, 0xbad484d600000000, 0x1863cd3500000000, 0xb9f3dadd00000000, 0x09aea65f00000000, 0xa83eb1b700000000, 0x0a89f85400000000, 0xab19efbc00000000, 0x0fe01a4900000000, 0xae700da100000000, 0x0cc7444200000000, 0xad5753aa00000000, 0x0532de7200000000, 0xa4a2c99a00000000, 0x0615807900000000, 0xa785979100000000, 0x037c626400000000, 0xa2ec758c00000000, 0x005b3c6f00000000, 0xa1cb2b8700000000, 0x03ca1aba00000000, 0xa25a0d5200000000, 0x00ed44b100000000, 0xa17d535900000000, 0x0584a6ac00000000, 0xa414b14400000000, 0x06a3f8a700000000, 0xa733ef4f00000000, 0x0f56629700000000, 0xaec6757f00000000, 0x0c713c9c00000000, 0xade12b7400000000, 0x0918de8100000000, 0xa888c96900000000, 0x0a3f808a00000000, 0xabaf976200000000, 0x1bf2ebe000000000, 0xba62fc0800000000, 0x18d5b5eb00000000, 0xb945a20300000000, 0x1dbc57f600000000, 0xbc2c401e00000000, 0x1e9b09fd00000000, 0xbf0b1e1500000000, 0x176e93cd00000000, 0xb6fe842500000000, 0x1449cdc600000000, 0xb5d9da2e00000000, 0x11202fdb00000000, 0xb0b0383300000000, 0x120771d000000000, 0xb397663800000000, 0x33baf80f00000000, 0x922aefe700000000, 0x309da60400000000, 0x910db1ec00000000, 0x35f4441900000000, 0x946453f100000000, 0x36d31a1200000000, 0x97430dfa00000000, 0x3f26802200000000, 0x9eb697ca00000000, 0x3c01de2900000000, 0x9d91c9c100000000, 0x39683c3400000000, 0x98f82bdc00000000, 0x3a4f623f00000000, 0x9bdf75d700000000, 0x2b82095500000000, 0x8a121ebd00000000, 0x28a5575e00000000, 0x893540b600000000, 0x2dccb54300000000, 0x8c5ca2ab00000000, 0x2eebeb4800000000, 0x8f7bfca000000000, 0x271e717800000000, 0x868e669000000000, 0x24392f7300000000, 0x85a9389b00000000, 0x2150cd6e00000000, 0x80c0da8600000000, 0x2277936500000000, 0x83e7848d00000000, 0x222caf0a00000000, 0x83bcb8e200000000, 0x210bf10100000000, 0x809be6e900000000, 0x2462131c00000000, 0x85f204f400000000, 0x27454d1700000000, 0x86d55aff00000000, 0x2eb0d72700000000, 0x8f20c0cf00000000, 0x2d97892c00000000, 0x8c079ec400000000, 0x28fe6b3100000000, 0x896e7cd900000000, 0x2bd9353a00000000, 0x8a4922d200000000, 0x3a145e5000000000, 0x9b8449b800000000, 0x3933005b00000000, 0x98a317b300000000, 0x3c5ae24600000000, 0x9dcaf5ae00000000, 0x3f7dbc4d00000000, 0x9eedaba500000000, 0x3688267d00000000, 0x9718319500000000, 0x35af787600000000, 0x943f6f9e00000000, 0x30c69a6b00000000, 0x91568d8300000000, 0x33e1c46000000000, 0x9271d38800000000, 0x125c4dbf00000000, 0xb3cc5a5700000000, 0x117b13b400000000, 0xb0eb045c00000000, 0x1412f1a900000000, 0xb582e64100000000, 0x1735afa200000000, 0xb6a5b84a00000000, 0x1ec0359200000000, 0xbf50227a00000000, 0x1de76b9900000000, 0xbc777c7100000000, 0x188e898400000000, 0xb91e9e6c00000000, 0x1ba9d78f00000000, 0xba39c06700000000, 0x0a64bce500000000, 0xabf4ab0d00000000, 0x0943e2ee00000000, 0xa8d3f50600000000, 0x0c2a00f300000000, 0xadba171b00000000, 0x0f0d5ef800000000, 0xae9d491000000000, 0x06f8c4c800000000, 0xa768d32000000000, 0x05df9ac300000000, 0xa44f8d2b00000000, 0x00b678de00000000, 0xa1266f3600000000, 0x039126d500000000, 0xa201313d00000000}, {0x0000000000000000, 0xee8439a100000000, 0x9d0f029900000000, 0x738b3b3800000000, 0x7b1975e900000000, 0x959d4c4800000000, 0xe616777000000000, 0x08924ed100000000, 0xb7349b0900000000, 0x59b0a2a800000000, 0x2a3b999000000000, 0xc4bfa03100000000, 0xcc2deee000000000, 0x22a9d74100000000, 0x5122ec7900000000, 0xbfa6d5d800000000, 0x6e69361300000000, 0x80ed0fb200000000, 0xf366348a00000000, 0x1de20d2b00000000, 0x157043fa00000000, 0xfbf47a5b00000000, 0x887f416300000000, 0x66fb78c200000000, 0xd95dad1a00000000, 0x37d994bb00000000, 0x4452af8300000000, 0xaad6962200000000, 0xa244d8f300000000, 0x4cc0e15200000000, 0x3f4bda6a00000000, 0xd1cfe3cb00000000, 0xdcd26c2600000000, 0x3256558700000000, 0x41dd6ebf00000000, 0xaf59571e00000000, 0xa7cb19cf00000000, 0x494f206e00000000, 0x3ac41b5600000000, 0xd44022f700000000, 0x6be6f72f00000000, 0x8562ce8e00000000, 0xf6e9f5b600000000, 0x186dcc1700000000, 0x10ff82c600000000, 0xfe7bbb6700000000, 0x8df0805f00000000, 0x6374b9fe00000000, 0xb2bb5a3500000000, 0x5c3f639400000000, 0x2fb458ac00000000, 0xc130610d00000000, 0xc9a22fdc00000000, 0x2726167d00000000, 0x54ad2d4500000000, 0xba2914e400000000, 0x058fc13c00000000, 0xeb0bf89d00000000, 0x9880c3a500000000, 0x7604fa0400000000, 0x7e96b4d500000000, 0x90128d7400000000, 0xe399b64c00000000, 0x0d1d8fed00000000, 0xb8a5d94c00000000, 0x5621e0ed00000000, 0x25aadbd500000000, 0xcb2ee27400000000, 0xc3bcaca500000000, 0x2d38950400000000, 0x5eb3ae3c00000000, 0xb037979d00000000, 0x0f91424500000000, 0xe1157be400000000, 0x929e40dc00000000, 0x7c1a797d00000000, 0x748837ac00000000, 0x9a0c0e0d00000000, 0xe987353500000000, 0x07030c9400000000, 0xd6ccef5f00000000, 0x3848d6fe00000000, 0x4bc3edc600000000, 0xa547d46700000000, 0xadd59ab600000000, 0x4351a31700000000, 0x30da982f00000000, 0xde5ea18e00000000, 0x61f8745600000000, 0x8f7c4df700000000, 0xfcf776cf00000000, 0x12734f6e00000000, 0x1ae101bf00000000, 0xf465381e00000000, 0x87ee032600000000, 0x696a3a8700000000, 0x6477b56a00000000, 0x8af38ccb00000000, 0xf978b7f300000000, 0x17fc8e5200000000, 0x1f6ec08300000000, 0xf1eaf92200000000, 0x8261c21a00000000, 0x6ce5fbbb00000000, 0xd3432e6300000000, 0x3dc717c200000000, 0x4e4c2cfa00000000, 0xa0c8155b00000000, 0xa85a5b8a00000000, 0x46de622b00000000, 0x3555591300000000, 0xdbd160b200000000, 0x0a1e837900000000, 0xe49abad800000000, 0x971181e000000000, 0x7995b84100000000, 0x7107f69000000000, 0x9f83cf3100000000, 0xec08f40900000000, 0x028ccda800000000, 0xbd2a187000000000, 0x53ae21d100000000, 0x20251ae900000000, 0xcea1234800000000, 0xc6336d9900000000, 0x28b7543800000000, 0x5b3c6f0000000000, 0xb5b856a100000000, 0x704bb39900000000, 0x9ecf8a3800000000, 0xed44b10000000000, 0x03c088a100000000, 0x0b52c67000000000, 0xe5d6ffd100000000, 0x965dc4e900000000, 0x78d9fd4800000000, 0xc77f289000000000, 0x29fb113100000000, 0x5a702a0900000000, 0xb4f413a800000000, 0xbc665d7900000000, 0x52e264d800000000, 0x21695fe000000000, 0xcfed664100000000, 0x1e22858a00000000, 0xf0a6bc2b00000000, 0x832d871300000000, 0x6da9beb200000000, 0x653bf06300000000, 0x8bbfc9c200000000, 0xf834f2fa00000000, 0x16b0cb5b00000000, 0xa9161e8300000000, 0x4792272200000000, 0x34191c1a00000000, 0xda9d25bb00000000, 0xd20f6b6a00000000, 0x3c8b52cb00000000, 0x4f0069f300000000, 0xa184505200000000, 0xac99dfbf00000000, 0x421de61e00000000, 0x3196dd2600000000, 0xdf12e48700000000, 0xd780aa5600000000, 0x390493f700000000, 0x4a8fa8cf00000000, 0xa40b916e00000000, 0x1bad44b600000000, 0xf5297d1700000000, 0x86a2462f00000000, 0x68267f8e00000000, 0x60b4315f00000000, 0x8e3008fe00000000, 0xfdbb33c600000000, 0x133f0a6700000000, 0xc2f0e9ac00000000, 0x2c74d00d00000000, 0x5fffeb3500000000, 0xb17bd29400000000, 0xb9e99c4500000000, 0x576da5e400000000, 0x24e69edc00000000, 0xca62a77d00000000, 0x75c472a500000000, 0x9b404b0400000000, 0xe8cb703c00000000, 0x064f499d00000000, 0x0edd074c00000000, 0xe0593eed00000000, 0x93d205d500000000, 0x7d563c7400000000, 0xc8ee6ad500000000, 0x266a537400000000, 0x55e1684c00000000, 0xbb6551ed00000000, 0xb3f71f3c00000000, 0x5d73269d00000000, 0x2ef81da500000000, 0xc07c240400000000, 0x7fdaf1dc00000000, 0x915ec87d00000000, 0xe2d5f34500000000, 0x0c51cae400000000, 0x04c3843500000000, 0xea47bd9400000000, 0x99cc86ac00000000, 0x7748bf0d00000000, 0xa6875cc600000000, 0x4803656700000000, 0x3b885e5f00000000, 0xd50c67fe00000000, 0xdd9e292f00000000, 0x331a108e00000000, 0x40912bb600000000, 0xae15121700000000, 0x11b3c7cf00000000, 0xff37fe6e00000000, 0x8cbcc55600000000, 0x6238fcf700000000, 0x6aaab22600000000, 0x842e8b8700000000, 0xf7a5b0bf00000000, 0x1921891e00000000, 0x143c06f300000000, 0xfab83f5200000000, 0x8933046a00000000, 0x67b73dcb00000000, 0x6f25731a00000000, 0x81a14abb00000000, 0xf22a718300000000, 0x1cae482200000000, 0xa3089dfa00000000, 0x4d8ca45b00000000, 0x3e079f6300000000, 0xd083a6c200000000, 0xd811e81300000000, 0x3695d1b200000000, 0x451eea8a00000000, 0xab9ad32b00000000, 0x7a5530e000000000, 0x94d1094100000000, 0xe75a327900000000, 0x09de0bd800000000, 0x014c450900000000, 0xefc87ca800000000, 0x9c43479000000000, 0x72c77e3100000000, 0xcd61abe900000000, 0x23e5924800000000, 0x506ea97000000000, 0xbeea90d100000000, 0xb678de0000000000, 0x58fce7a100000000, 0x2b77dc9900000000, 0xc5f3e53800000000}, {0x0000000000000000, 0xfbf6134700000000, 0xf6ed278e00000000, 0x0d1b34c900000000, 0xaddd3ec700000000, 0x562b2d8000000000, 0x5b30194900000000, 0xa0c60a0e00000000, 0x1bbd0c5500000000, 0xe04b1f1200000000, 0xed502bdb00000000, 0x16a6389c00000000, 0xb660329200000000, 0x4d9621d500000000, 0x408d151c00000000, 0xbb7b065b00000000, 0x367a19aa00000000, 0xcd8c0aed00000000, 0xc0973e2400000000, 0x3b612d6300000000, 0x9ba7276d00000000, 0x6051342a00000000, 0x6d4a00e300000000, 0x96bc13a400000000, 0x2dc715ff00000000, 0xd63106b800000000, 0xdb2a327100000000, 0x20dc213600000000, 0x801a2b3800000000, 0x7bec387f00000000, 0x76f70cb600000000, 0x8d011ff100000000, 0x2df2438f00000000, 0xd60450c800000000, 0xdb1f640100000000, 0x20e9774600000000, 0x802f7d4800000000, 0x7bd96e0f00000000, 0x76c25ac600000000, 0x8d34498100000000, 0x364f4fda00000000, 0xcdb95c9d00000000, 0xc0a2685400000000, 0x3b547b1300000000, 0x9b92711d00000000, 0x6064625a00000000, 0x6d7f569300000000, 0x968945d400000000, 0x1b885a2500000000, 0xe07e496200000000, 0xed657dab00000000, 0x16936eec00000000, 0xb65564e200000000, 0x4da377a500000000, 0x40b8436c00000000, 0xbb4e502b00000000, 0x0035567000000000, 0xfbc3453700000000, 0xf6d871fe00000000, 0x0d2e62b900000000, 0xade868b700000000, 0x561e7bf000000000, 0x5b054f3900000000, 0xa0f35c7e00000000, 0x1be2f6c500000000, 0xe014e58200000000, 0xed0fd14b00000000, 0x16f9c20c00000000, 0xb63fc80200000000, 0x4dc9db4500000000, 0x40d2ef8c00000000, 0xbb24fccb00000000, 0x005ffa9000000000, 0xfba9e9d700000000, 0xf6b2dd1e00000000, 0x0d44ce5900000000, 0xad82c45700000000, 0x5674d71000000000, 0x5b6fe3d900000000, 0xa099f09e00000000, 0x2d98ef6f00000000, 0xd66efc2800000000, 0xdb75c8e100000000, 0x2083dba600000000, 0x8045d1a800000000, 0x7bb3c2ef00000000, 0x76a8f62600000000, 0x8d5ee56100000000, 0x3625e33a00000000, 0xcdd3f07d00000000, 0xc0c8c4b400000000, 0x3b3ed7f300000000, 0x9bf8ddfd00000000, 0x600eceba00000000, 0x6d15fa7300000000, 0x96e3e93400000000, 0x3610b54a00000000, 0xcde6a60d00000000, 0xc0fd92c400000000, 0x3b0b818300000000, 0x9bcd8b8d00000000, 0x603b98ca00000000, 0x6d20ac0300000000, 0x96d6bf4400000000, 0x2dadb91f00000000, 0xd65baa5800000000, 0xdb409e9100000000, 0x20b68dd600000000, 0x807087d800000000, 0x7b86949f00000000, 0x769da05600000000, 0x8d6bb31100000000, 0x006aace000000000, 0xfb9cbfa700000000, 0xf6878b6e00000000, 0x0d71982900000000, 0xadb7922700000000, 0x5641816000000000, 0x5b5ab5a900000000, 0xa0aca6ee00000000, 0x1bd7a0b500000000, 0xe021b3f200000000, 0xed3a873b00000000, 0x16cc947c00000000, 0xb60a9e7200000000, 0x4dfc8d3500000000, 0x40e7b9fc00000000, 0xbb11aabb00000000, 0x77c29c5000000000, 0x8c348f1700000000, 0x812fbbde00000000, 0x7ad9a89900000000, 0xda1fa29700000000, 0x21e9b1d000000000, 0x2cf2851900000000, 0xd704965e00000000, 0x6c7f900500000000, 0x9789834200000000, 0x9a92b78b00000000, 0x6164a4cc00000000, 0xc1a2aec200000000, 0x3a54bd8500000000, 0x374f894c00000000, 0xccb99a0b00000000, 0x41b885fa00000000, 0xba4e96bd00000000, 0xb755a27400000000, 0x4ca3b13300000000, 0xec65bb3d00000000, 0x1793a87a00000000, 0x1a889cb300000000, 0xe17e8ff400000000, 0x5a0589af00000000, 0xa1f39ae800000000, 0xace8ae2100000000, 0x571ebd6600000000, 0xf7d8b76800000000, 0x0c2ea42f00000000, 0x013590e600000000, 0xfac383a100000000, 0x5a30dfdf00000000, 0xa1c6cc9800000000, 0xacddf85100000000, 0x572beb1600000000, 0xf7ede11800000000, 0x0c1bf25f00000000, 0x0100c69600000000, 0xfaf6d5d100000000, 0x418dd38a00000000, 0xba7bc0cd00000000, 0xb760f40400000000, 0x4c96e74300000000, 0xec50ed4d00000000, 0x17a6fe0a00000000, 0x1abdcac300000000, 0xe14bd98400000000, 0x6c4ac67500000000, 0x97bcd53200000000, 0x9aa7e1fb00000000, 0x6151f2bc00000000, 0xc197f8b200000000, 0x3a61ebf500000000, 0x377adf3c00000000, 0xcc8ccc7b00000000, 0x77f7ca2000000000, 0x8c01d96700000000, 0x811aedae00000000, 0x7aecfee900000000, 0xda2af4e700000000, 0x21dce7a000000000, 0x2cc7d36900000000, 0xd731c02e00000000, 0x6c206a9500000000, 0x97d679d200000000, 0x9acd4d1b00000000, 0x613b5e5c00000000, 0xc1fd545200000000, 0x3a0b471500000000, 0x371073dc00000000, 0xcce6609b00000000, 0x779d66c000000000, 0x8c6b758700000000, 0x8170414e00000000, 0x7a86520900000000, 0xda40580700000000, 0x21b64b4000000000, 0x2cad7f8900000000, 0xd75b6cce00000000, 0x5a5a733f00000000, 0xa1ac607800000000, 0xacb754b100000000, 0x574147f600000000, 0xf7874df800000000, 0x0c715ebf00000000, 0x016a6a7600000000, 0xfa9c793100000000, 0x41e77f6a00000000, 0xba116c2d00000000, 0xb70a58e400000000, 0x4cfc4ba300000000, 0xec3a41ad00000000, 0x17cc52ea00000000, 0x1ad7662300000000, 0xe121756400000000, 0x41d2291a00000000, 0xba243a5d00000000, 0xb73f0e9400000000, 0x4cc91dd300000000, 0xec0f17dd00000000, 0x17f9049a00000000, 0x1ae2305300000000, 0xe114231400000000, 0x5a6f254f00000000, 0xa199360800000000, 0xac8202c100000000, 0x5774118600000000, 0xf7b21b8800000000, 0x0c4408cf00000000, 0x015f3c0600000000, 0xfaa92f4100000000, 0x77a830b000000000, 0x8c5e23f700000000, 0x8145173e00000000, 0x7ab3047900000000, 0xda750e7700000000, 0x21831d3000000000, 0x2c9829f900000000, 0xd76e3abe00000000, 0x6c153ce500000000, 0x97e32fa200000000, 0x9af81b6b00000000, 0x610e082c00000000, 0xc1c8022200000000, 0x3a3e116500000000, 0x372525ac00000000, 0xccd336eb00000000}, {0x0000000000000000, 0x6238282a00000000, 0xc470505400000000, 0xa648787e00000000, 0x88e1a0a800000000, 0xead9888200000000, 0x4c91f0fc00000000, 0x2ea9d8d600000000, 0x51c5308a00000000, 0x33fd18a000000000, 0x95b560de00000000, 0xf78d48f400000000, 0xd924902200000000, 0xbb1cb80800000000, 0x1d54c07600000000, 0x7f6ce85c00000000, 0xe38c10cf00000000, 0x81b438e500000000, 0x27fc409b00000000, 0x45c468b100000000, 0x6b6db06700000000, 0x0955984d00000000, 0xaf1de03300000000, 0xcd25c81900000000, 0xb249204500000000, 0xd071086f00000000, 0x7639701100000000, 0x1401583b00000000, 0x3aa880ed00000000, 0x5890a8c700000000, 0xfed8d0b900000000, 0x9ce0f89300000000, 0x871f504500000000, 0xe527786f00000000, 0x436f001100000000, 0x2157283b00000000, 0x0ffef0ed00000000, 0x6dc6d8c700000000, 0xcb8ea0b900000000, 0xa9b6889300000000, 0xd6da60cf00000000, 0xb4e248e500000000, 0x12aa309b00000000, 0x709218b100000000, 0x5e3bc06700000000, 0x3c03e84d00000000, 0x9a4b903300000000, 0xf873b81900000000, 0x6493408a00000000, 0x06ab68a000000000, 0xa0e310de00000000, 0xc2db38f400000000, 0xec72e02200000000, 0x8e4ac80800000000, 0x2802b07600000000, 0x4a3a985c00000000, 0x3556700000000000, 0x576e582a00000000, 0xf126205400000000, 0x931e087e00000000, 0xbdb7d0a800000000, 0xdf8ff88200000000, 0x79c780fc00000000, 0x1bffa8d600000000, 0x0e3fa08a00000000, 0x6c0788a000000000, 0xca4ff0de00000000, 0xa877d8f400000000, 0x86de002200000000, 0xe4e6280800000000, 0x42ae507600000000, 0x2096785c00000000, 0x5ffa900000000000, 0x3dc2b82a00000000, 0x9b8ac05400000000, 0xf9b2e87e00000000, 0xd71b30a800000000, 0xb523188200000000, 0x136b60fc00000000, 0x715348d600000000, 0xedb3b04500000000, 0x8f8b986f00000000, 0x29c3e01100000000, 0x4bfbc83b00000000, 0x655210ed00000000, 0x076a38c700000000, 0xa12240b900000000, 0xc31a689300000000, 0xbc7680cf00000000, 0xde4ea8e500000000, 0x7806d09b00000000, 0x1a3ef8b100000000, 0x3497206700000000, 0x56af084d00000000, 0xf0e7703300000000, 0x92df581900000000, 0x8920f0cf00000000, 0xeb18d8e500000000, 0x4d50a09b00000000, 0x2f6888b100000000, 0x01c1506700000000, 0x63f9784d00000000, 0xc5b1003300000000, 0xa789281900000000, 0xd8e5c04500000000, 0xbadde86f00000000, 0x1c95901100000000, 0x7eadb83b00000000, 0x500460ed00000000, 0x323c48c700000000, 0x947430b900000000, 0xf64c189300000000, 0x6aace00000000000, 0x0894c82a00000000, 0xaedcb05400000000, 0xcce4987e00000000, 0xe24d40a800000000, 0x8075688200000000, 0x263d10fc00000000, 0x440538d600000000, 0x3b69d08a00000000, 0x5951f8a000000000, 0xff1980de00000000, 0x9d21a8f400000000, 0xb388702200000000, 0xd1b0580800000000, 0x77f8207600000000, 0x15c0085c00000000, 0x5d7831ce00000000, 0x3f4019e400000000, 0x9908619a00000000, 0xfb3049b000000000, 0xd599916600000000, 0xb7a1b94c00000000, 0x11e9c13200000000, 0x73d1e91800000000, 0x0cbd014400000000, 0x6e85296e00000000, 0xc8cd511000000000, 0xaaf5793a00000000, 0x845ca1ec00000000, 0xe66489c600000000, 0x402cf1b800000000, 0x2214d99200000000, 0xbef4210100000000, 0xdccc092b00000000, 0x7a84715500000000, 0x18bc597f00000000, 0x361581a900000000, 0x542da98300000000, 0xf265d1fd00000000, 0x905df9d700000000, 0xef31118b00000000, 0x8d0939a100000000, 0x2b4141df00000000, 0x497969f500000000, 0x67d0b12300000000, 0x05e8990900000000, 0xa3a0e17700000000, 0xc198c95d00000000, 0xda67618b00000000, 0xb85f49a100000000, 0x1e1731df00000000, 0x7c2f19f500000000, 0x5286c12300000000, 0x30bee90900000000, 0x96f6917700000000, 0xf4ceb95d00000000, 0x8ba2510100000000, 0xe99a792b00000000, 0x4fd2015500000000, 0x2dea297f00000000, 0x0343f1a900000000, 0x617bd98300000000, 0xc733a1fd00000000, 0xa50b89d700000000, 0x39eb714400000000, 0x5bd3596e00000000, 0xfd9b211000000000, 0x9fa3093a00000000, 0xb10ad1ec00000000, 0xd332f9c600000000, 0x757a81b800000000, 0x1742a99200000000, 0x682e41ce00000000, 0x0a1669e400000000, 0xac5e119a00000000, 0xce6639b000000000, 0xe0cfe16600000000, 0x82f7c94c00000000, 0x24bfb13200000000, 0x4687991800000000, 0x5347914400000000, 0x317fb96e00000000, 0x9737c11000000000, 0xf50fe93a00000000, 0xdba631ec00000000, 0xb99e19c600000000, 0x1fd661b800000000, 0x7dee499200000000, 0x0282a1ce00000000, 0x60ba89e400000000, 0xc6f2f19a00000000, 0xa4cad9b000000000, 0x8a63016600000000, 0xe85b294c00000000, 0x4e13513200000000, 0x2c2b791800000000, 0xb0cb818b00000000, 0xd2f3a9a100000000, 0x74bbd1df00000000, 0x1683f9f500000000, 0x382a212300000000, 0x5a12090900000000, 0xfc5a717700000000, 0x9e62595d00000000, 0xe10eb10100000000, 0x8336992b00000000, 0x257ee15500000000, 0x4746c97f00000000, 0x69ef11a900000000, 0x0bd7398300000000, 0xad9f41fd00000000, 0xcfa769d700000000, 0xd458c10100000000, 0xb660e92b00000000, 0x1028915500000000, 0x7210b97f00000000, 0x5cb961a900000000, 0x3e81498300000000, 0x98c931fd00000000, 0xfaf119d700000000, 0x859df18b00000000, 0xe7a5d9a100000000, 0x41eda1df00000000, 0x23d589f500000000, 0x0d7c512300000000, 0x6f44790900000000, 0xc90c017700000000, 0xab34295d00000000, 0x37d4d1ce00000000, 0x55ecf9e400000000, 0xf3a4819a00000000, 0x919ca9b000000000, 0xbf35716600000000, 0xdd0d594c00000000, 0x7b45213200000000, 0x197d091800000000, 0x6611e14400000000, 0x0429c96e00000000, 0xa261b11000000000, 0xc059993a00000000, 0xeef041ec00000000, 0x8cc869c600000000, 0x2a8011b800000000, 0x48b8399200000000}, {0x0000000000000000, 0x4c2896a300000000, 0xd9565d9c00000000, 0x957ecb3f00000000, 0xf3abcbe300000000, 0xbf835d4000000000, 0x2afd967f00000000, 0x66d500dc00000000, 0xa751e61c00000000, 0xeb7970bf00000000, 0x7e07bb8000000000, 0x322f2d2300000000, 0x54fa2dff00000000, 0x18d2bb5c00000000, 0x8dac706300000000, 0xc184e6c000000000, 0x4ea3cc3900000000, 0x028b5a9a00000000, 0x97f591a500000000, 0xdbdd070600000000, 0xbd0807da00000000, 0xf120917900000000, 0x645e5a4600000000, 0x2876cce500000000, 0xe9f22a2500000000, 0xa5dabc8600000000, 0x30a477b900000000, 0x7c8ce11a00000000, 0x1a59e1c600000000, 0x5671776500000000, 0xc30fbc5a00000000, 0x8f272af900000000, 0x9c46997300000000, 0xd06e0fd000000000, 0x4510c4ef00000000, 0x0938524c00000000, 0x6fed529000000000, 0x23c5c43300000000, 0xb6bb0f0c00000000, 0xfa9399af00000000, 0x3b177f6f00000000, 0x773fe9cc00000000, 0xe24122f300000000, 0xae69b45000000000, 0xc8bcb48c00000000, 0x8494222f00000000, 0x11eae91000000000, 0x5dc27fb300000000, 0xd2e5554a00000000, 0x9ecdc3e900000000, 0x0bb308d600000000, 0x479b9e7500000000, 0x214e9ea900000000, 0x6d66080a00000000, 0xf818c33500000000, 0xb430559600000000, 0x75b4b35600000000, 0x399c25f500000000, 0xace2eeca00000000, 0xe0ca786900000000, 0x861f78b500000000, 0xca37ee1600000000, 0x5f49252900000000, 0x1361b38a00000000, 0x388d32e700000000, 0x74a5a44400000000, 0xe1db6f7b00000000, 0xadf3f9d800000000, 0xcb26f90400000000, 0x870e6fa700000000, 0x1270a49800000000, 0x5e58323b00000000, 0x9fdcd4fb00000000, 0xd3f4425800000000, 0x468a896700000000, 0x0aa21fc400000000, 0x6c771f1800000000, 0x205f89bb00000000, 0xb521428400000000, 0xf909d42700000000, 0x762efede00000000, 0x3a06687d00000000, 0xaf78a34200000000, 0xe35035e100000000, 0x8585353d00000000, 0xc9ada39e00000000, 0x5cd368a100000000, 0x10fbfe0200000000, 0xd17f18c200000000, 0x9d578e6100000000, 0x0829455e00000000, 0x4401d3fd00000000, 0x22d4d32100000000, 0x6efc458200000000, 0xfb828ebd00000000, 0xb7aa181e00000000, 0xa4cbab9400000000, 0xe8e33d3700000000, 0x7d9df60800000000, 0x31b560ab00000000, 0x5760607700000000, 0x1b48f6d400000000, 0x8e363deb00000000, 0xc21eab4800000000, 0x039a4d8800000000, 0x4fb2db2b00000000, 0xdacc101400000000, 0x96e486b700000000, 0xf031866b00000000, 0xbc1910c800000000, 0x2967dbf700000000, 0x654f4d5400000000, 0xea6867ad00000000, 0xa640f10e00000000, 0x333e3a3100000000, 0x7f16ac9200000000, 0x19c3ac4e00000000, 0x55eb3aed00000000, 0xc095f1d200000000, 0x8cbd677100000000, 0x4d3981b100000000, 0x0111171200000000, 0x946fdc2d00000000, 0xd8474a8e00000000, 0xbe924a5200000000, 0xf2badcf100000000, 0x67c417ce00000000, 0x2bec816d00000000, 0x311c141500000000, 0x7d3482b600000000, 0xe84a498900000000, 0xa462df2a00000000, 0xc2b7dff600000000, 0x8e9f495500000000, 0x1be1826a00000000, 0x57c914c900000000, 0x964df20900000000, 0xda6564aa00000000, 0x4f1baf9500000000, 0x0333393600000000, 0x65e639ea00000000, 0x29ceaf4900000000, 0xbcb0647600000000, 0xf098f2d500000000, 0x7fbfd82c00000000, 0x33974e8f00000000, 0xa6e985b000000000, 0xeac1131300000000, 0x8c1413cf00000000, 0xc03c856c00000000, 0x55424e5300000000, 0x196ad8f000000000, 0xd8ee3e3000000000, 0x94c6a89300000000, 0x01b863ac00000000, 0x4d90f50f00000000, 0x2b45f5d300000000, 0x676d637000000000, 0xf213a84f00000000, 0xbe3b3eec00000000, 0xad5a8d6600000000, 0xe1721bc500000000, 0x740cd0fa00000000, 0x3824465900000000, 0x5ef1468500000000, 0x12d9d02600000000, 0x87a71b1900000000, 0xcb8f8dba00000000, 0x0a0b6b7a00000000, 0x4623fdd900000000, 0xd35d36e600000000, 0x9f75a04500000000, 0xf9a0a09900000000, 0xb588363a00000000, 0x20f6fd0500000000, 0x6cde6ba600000000, 0xe3f9415f00000000, 0xafd1d7fc00000000, 0x3aaf1cc300000000, 0x76878a6000000000, 0x10528abc00000000, 0x5c7a1c1f00000000, 0xc904d72000000000, 0x852c418300000000, 0x44a8a74300000000, 0x088031e000000000, 0x9dfefadf00000000, 0xd1d66c7c00000000, 0xb7036ca000000000, 0xfb2bfa0300000000, 0x6e55313c00000000, 0x227da79f00000000, 0x099126f200000000, 0x45b9b05100000000, 0xd0c77b6e00000000, 0x9cefedcd00000000, 0xfa3aed1100000000, 0xb6127bb200000000, 0x236cb08d00000000, 0x6f44262e00000000, 0xaec0c0ee00000000, 0xe2e8564d00000000, 0x77969d7200000000, 0x3bbe0bd100000000, 0x5d6b0b0d00000000, 0x11439dae00000000, 0x843d569100000000, 0xc815c03200000000, 0x4732eacb00000000, 0x0b1a7c6800000000, 0x9e64b75700000000, 0xd24c21f400000000, 0xb499212800000000, 0xf8b1b78b00000000, 0x6dcf7cb400000000, 0x21e7ea1700000000, 0xe0630cd700000000, 0xac4b9a7400000000, 0x3935514b00000000, 0x751dc7e800000000, 0x13c8c73400000000, 0x5fe0519700000000, 0xca9e9aa800000000, 0x86b60c0b00000000, 0x95d7bf8100000000, 0xd9ff292200000000, 0x4c81e21d00000000, 0x00a974be00000000, 0x667c746200000000, 0x2a54e2c100000000, 0xbf2a29fe00000000, 0xf302bf5d00000000, 0x3286599d00000000, 0x7eaecf3e00000000, 0xebd0040100000000, 0xa7f892a200000000, 0xc12d927e00000000, 0x8d0504dd00000000, 0x187bcfe200000000, 0x5453594100000000, 0xdb7473b800000000, 0x975ce51b00000000, 0x02222e2400000000, 0x4e0ab88700000000, 0x28dfb85b00000000, 0x64f72ef800000000, 0xf189e5c700000000, 0xbda1736400000000, 0x7c2595a400000000, 0x300d030700000000, 0xa573c83800000000, 0xe95b5e9b00000000, 0x8f8e5e4700000000, 0xc3a6c8e400000000, 0x56d803db00000000, 0x1af0957800000000}, {0x0000000000000000, 0x939bc97f00000000, 0x263793ff00000000, 0xb5ac5a8000000000, 0x0d68572400000000, 0x9ef39e5b00000000, 0x2b5fc4db00000000, 0xb8c40da400000000, 0x1ad0ae4800000000, 0x894b673700000000, 0x3ce73db700000000, 0xaf7cf4c800000000, 0x17b8f96c00000000, 0x8423301300000000, 0x318f6a9300000000, 0xa214a3ec00000000, 0x34a05d9100000000, 0xa73b94ee00000000, 0x1297ce6e00000000, 0x810c071100000000, 0x39c80ab500000000, 0xaa53c3ca00000000, 0x1fff994a00000000, 0x8c64503500000000, 0x2e70f3d900000000, 0xbdeb3aa600000000, 0x0847602600000000, 0x9bdca95900000000, 0x2318a4fd00000000, 0xb0836d8200000000, 0x052f370200000000, 0x96b4fe7d00000000, 0x2946caf900000000, 0xbadd038600000000, 0x0f71590600000000, 0x9cea907900000000, 0x242e9ddd00000000, 0xb7b554a200000000, 0x02190e2200000000, 0x9182c75d00000000, 0x339664b100000000, 0xa00dadce00000000, 0x15a1f74e00000000, 0x863a3e3100000000, 0x3efe339500000000, 0xad65faea00000000, 0x18c9a06a00000000, 0x8b52691500000000, 0x1de6976800000000, 0x8e7d5e1700000000, 0x3bd1049700000000, 0xa84acde800000000, 0x108ec04c00000000, 0x8315093300000000, 0x36b953b300000000, 0xa5229acc00000000, 0x0736392000000000, 0x94adf05f00000000, 0x2101aadf00000000, 0xb29a63a000000000, 0x0a5e6e0400000000, 0x99c5a77b00000000, 0x2c69fdfb00000000, 0xbff2348400000000, 0x138ae52800000000, 0x80112c5700000000, 0x35bd76d700000000, 0xa626bfa800000000, 0x1ee2b20c00000000, 0x8d797b7300000000, 0x38d521f300000000, 0xab4ee88c00000000, 0x095a4b6000000000, 0x9ac1821f00000000, 0x2f6dd89f00000000, 0xbcf611e000000000, 0x04321c4400000000, 0x97a9d53b00000000, 0x22058fbb00000000, 0xb19e46c400000000, 0x272ab8b900000000, 0xb4b171c600000000, 0x011d2b4600000000, 0x9286e23900000000, 0x2a42ef9d00000000, 0xb9d926e200000000, 0x0c757c6200000000, 0x9feeb51d00000000, 0x3dfa16f100000000, 0xae61df8e00000000, 0x1bcd850e00000000, 0x88564c7100000000, 0x309241d500000000, 0xa30988aa00000000, 0x16a5d22a00000000, 0x853e1b5500000000, 0x3acc2fd100000000, 0xa957e6ae00000000, 0x1cfbbc2e00000000, 0x8f60755100000000, 0x37a478f500000000, 0xa43fb18a00000000, 0x1193eb0a00000000, 0x8208227500000000, 0x201c819900000000, 0xb38748e600000000, 0x062b126600000000, 0x95b0db1900000000, 0x2d74d6bd00000000, 0xbeef1fc200000000, 0x0b43454200000000, 0x98d88c3d00000000, 0x0e6c724000000000, 0x9df7bb3f00000000, 0x285be1bf00000000, 0xbbc028c000000000, 0x0304256400000000, 0x909fec1b00000000, 0x2533b69b00000000, 0xb6a87fe400000000, 0x14bcdc0800000000, 0x8727157700000000, 0x328b4ff700000000, 0xa110868800000000, 0x19d48b2c00000000, 0x8a4f425300000000, 0x3fe318d300000000, 0xac78d1ac00000000, 0x2614cb5100000000, 0xb58f022e00000000, 0x002358ae00000000, 0x93b891d100000000, 0x2b7c9c7500000000, 0xb8e7550a00000000, 0x0d4b0f8a00000000, 0x9ed0c6f500000000, 0x3cc4651900000000, 0xaf5fac6600000000, 0x1af3f6e600000000, 0x89683f9900000000, 0x31ac323d00000000, 0xa237fb4200000000, 0x179ba1c200000000, 0x840068bd00000000, 0x12b496c000000000, 0x812f5fbf00000000, 0x3483053f00000000, 0xa718cc4000000000, 0x1fdcc1e400000000, 0x8c47089b00000000, 0x39eb521b00000000, 0xaa709b6400000000, 0x0864388800000000, 0x9bfff1f700000000, 0x2e53ab7700000000, 0xbdc8620800000000, 0x050c6fac00000000, 0x9697a6d300000000, 0x233bfc5300000000, 0xb0a0352c00000000, 0x0f5201a800000000, 0x9cc9c8d700000000, 0x2965925700000000, 0xbafe5b2800000000, 0x023a568c00000000, 0x91a19ff300000000, 0x240dc57300000000, 0xb7960c0c00000000, 0x1582afe000000000, 0x8619669f00000000, 0x33b53c1f00000000, 0xa02ef56000000000, 0x18eaf8c400000000, 0x8b7131bb00000000, 0x3edd6b3b00000000, 0xad46a24400000000, 0x3bf25c3900000000, 0xa869954600000000, 0x1dc5cfc600000000, 0x8e5e06b900000000, 0x369a0b1d00000000, 0xa501c26200000000, 0x10ad98e200000000, 0x8336519d00000000, 0x2122f27100000000, 0xb2b93b0e00000000, 0x0715618e00000000, 0x948ea8f100000000, 0x2c4aa55500000000, 0xbfd16c2a00000000, 0x0a7d36aa00000000, 0x99e6ffd500000000, 0x359e2e7900000000, 0xa605e70600000000, 0x13a9bd8600000000, 0x803274f900000000, 0x38f6795d00000000, 0xab6db02200000000, 0x1ec1eaa200000000, 0x8d5a23dd00000000, 0x2f4e803100000000, 0xbcd5494e00000000, 0x097913ce00000000, 0x9ae2dab100000000, 0x2226d71500000000, 0xb1bd1e6a00000000, 0x041144ea00000000, 0x978a8d9500000000, 0x013e73e800000000, 0x92a5ba9700000000, 0x2709e01700000000, 0xb492296800000000, 0x0c5624cc00000000, 0x9fcdedb300000000, 0x2a61b73300000000, 0xb9fa7e4c00000000, 0x1beedda000000000, 0x887514df00000000, 0x3dd94e5f00000000, 0xae42872000000000, 0x16868a8400000000, 0x851d43fb00000000, 0x30b1197b00000000, 0xa32ad00400000000, 0x1cd8e48000000000, 0x8f432dff00000000, 0x3aef777f00000000, 0xa974be0000000000, 0x11b0b3a400000000, 0x822b7adb00000000, 0x3787205b00000000, 0xa41ce92400000000, 0x06084ac800000000, 0x959383b700000000, 0x203fd93700000000, 0xb3a4104800000000, 0x0b601dec00000000, 0x98fbd49300000000, 0x2d578e1300000000, 0xbecc476c00000000, 0x2878b91100000000, 0xbbe3706e00000000, 0x0e4f2aee00000000, 0x9dd4e39100000000, 0x2510ee3500000000, 0xb68b274a00000000, 0x03277dca00000000, 0x90bcb4b500000000, 0x32a8175900000000, 0xa133de2600000000, 0x149f84a600000000, 0x87044dd900000000, 0x3fc0407d00000000, 0xac5b890200000000, 0x19f7d38200000000, 0x8a6c1afd00000000}, {0x0000000000000000, 0x650b796900000000, 0xca16f2d200000000, 0xaf1d8bbb00000000, 0xd52b957e00000000, 0xb020ec1700000000, 0x1f3d67ac00000000, 0x7a361ec500000000, 0xaa572afd00000000, 0xcf5c539400000000, 0x6041d82f00000000, 0x054aa14600000000, 0x7f7cbf8300000000, 0x1a77c6ea00000000, 0xb56a4d5100000000, 0xd061343800000000, 0x15a9252100000000, 0x70a25c4800000000, 0xdfbfd7f300000000, 0xbab4ae9a00000000, 0xc082b05f00000000, 0xa589c93600000000, 0x0a94428d00000000, 0x6f9f3be400000000, 0xbffe0fdc00000000, 0xdaf576b500000000, 0x75e8fd0e00000000, 0x10e3846700000000, 0x6ad59aa200000000, 0x0fdee3cb00000000, 0xa0c3687000000000, 0xc5c8111900000000, 0x2a524b4200000000, 0x4f59322b00000000, 0xe044b99000000000, 0x854fc0f900000000, 0xff79de3c00000000, 0x9a72a75500000000, 0x356f2cee00000000, 0x5064558700000000, 0x800561bf00000000, 0xe50e18d600000000, 0x4a13936d00000000, 0x2f18ea0400000000, 0x552ef4c100000000, 0x30258da800000000, 0x9f38061300000000, 0xfa337f7a00000000, 0x3ffb6e6300000000, 0x5af0170a00000000, 0xf5ed9cb100000000, 0x90e6e5d800000000, 0xead0fb1d00000000, 0x8fdb827400000000, 0x20c609cf00000000, 0x45cd70a600000000, 0x95ac449e00000000, 0xf0a73df700000000, 0x5fbab64c00000000, 0x3ab1cf2500000000, 0x4087d1e000000000, 0x258ca88900000000, 0x8a91233200000000, 0xef9a5a5b00000000, 0x54a4968400000000, 0x31afefed00000000, 0x9eb2645600000000, 0xfbb91d3f00000000, 0x818f03fa00000000, 0xe4847a9300000000, 0x4b99f12800000000, 0x2e92884100000000, 0xfef3bc7900000000, 0x9bf8c51000000000, 0x34e54eab00000000, 0x51ee37c200000000, 0x2bd8290700000000, 0x4ed3506e00000000, 0xe1cedbd500000000, 0x84c5a2bc00000000, 0x410db3a500000000, 0x2406cacc00000000, 0x8b1b417700000000, 0xee10381e00000000, 0x942626db00000000, 0xf12d5fb200000000, 0x5e30d40900000000, 0x3b3bad6000000000, 0xeb5a995800000000, 0x8e51e03100000000, 0x214c6b8a00000000, 0x444712e300000000, 0x3e710c2600000000, 0x5b7a754f00000000, 0xf467fef400000000, 0x916c879d00000000, 0x7ef6ddc600000000, 0x1bfda4af00000000, 0xb4e02f1400000000, 0xd1eb567d00000000, 0xabdd48b800000000, 0xced631d100000000, 0x61cbba6a00000000, 0x04c0c30300000000, 0xd4a1f73b00000000, 0xb1aa8e5200000000, 0x1eb705e900000000, 0x7bbc7c8000000000, 0x018a624500000000, 0x64811b2c00000000, 0xcb9c909700000000, 0xae97e9fe00000000, 0x6b5ff8e700000000, 0x0e54818e00000000, 0xa1490a3500000000, 0xc442735c00000000, 0xbe746d9900000000, 0xdb7f14f000000000, 0x74629f4b00000000, 0x1169e62200000000, 0xc108d21a00000000, 0xa403ab7300000000, 0x0b1e20c800000000, 0x6e1559a100000000, 0x1423476400000000, 0x71283e0d00000000, 0xde35b5b600000000, 0xbb3eccdf00000000, 0xe94e5cd200000000, 0x8c4525bb00000000, 0x2358ae0000000000, 0x4653d76900000000, 0x3c65c9ac00000000, 0x596eb0c500000000, 0xf6733b7e00000000, 0x9378421700000000, 0x4319762f00000000, 0x26120f4600000000, 0x890f84fd00000000, 0xec04fd9400000000, 0x9632e35100000000, 0xf3399a3800000000, 0x5c24118300000000, 0x392f68ea00000000, 0xfce779f300000000, 0x99ec009a00000000, 0x36f18b2100000000, 0x53faf24800000000, 0x29ccec8d00000000, 0x4cc795e400000000, 0xe3da1e5f00000000, 0x86d1673600000000, 0x56b0530e00000000, 0x33bb2a6700000000, 0x9ca6a1dc00000000, 0xf9add8b500000000, 0x839bc67000000000, 0xe690bf1900000000, 0x498d34a200000000, 0x2c864dcb00000000, 0xc31c179000000000, 0xa6176ef900000000, 0x090ae54200000000, 0x6c019c2b00000000, 0x163782ee00000000, 0x733cfb8700000000, 0xdc21703c00000000, 0xb92a095500000000, 0x694b3d6d00000000, 0x0c40440400000000, 0xa35dcfbf00000000, 0xc656b6d600000000, 0xbc60a81300000000, 0xd96bd17a00000000, 0x76765ac100000000, 0x137d23a800000000, 0xd6b532b100000000, 0xb3be4bd800000000, 0x1ca3c06300000000, 0x79a8b90a00000000, 0x039ea7cf00000000, 0x6695dea600000000, 0xc988551d00000000, 0xac832c7400000000, 0x7ce2184c00000000, 0x19e9612500000000, 0xb6f4ea9e00000000, 0xd3ff93f700000000, 0xa9c98d3200000000, 0xccc2f45b00000000, 0x63df7fe000000000, 0x06d4068900000000, 0xbdeaca5600000000, 0xd8e1b33f00000000, 0x77fc388400000000, 0x12f741ed00000000, 0x68c15f2800000000, 0x0dca264100000000, 0xa2d7adfa00000000, 0xc7dcd49300000000, 0x17bde0ab00000000, 0x72b699c200000000, 0xddab127900000000, 0xb8a06b1000000000, 0xc29675d500000000, 0xa79d0cbc00000000, 0x0880870700000000, 0x6d8bfe6e00000000, 0xa843ef7700000000, 0xcd48961e00000000, 0x62551da500000000, 0x075e64cc00000000, 0x7d687a0900000000, 0x1863036000000000, 0xb77e88db00000000, 0xd275f1b200000000, 0x0214c58a00000000, 0x671fbce300000000, 0xc802375800000000, 0xad094e3100000000, 0xd73f50f400000000, 0xb234299d00000000, 0x1d29a22600000000, 0x7822db4f00000000, 0x97b8811400000000, 0xf2b3f87d00000000, 0x5dae73c600000000, 0x38a50aaf00000000, 0x4293146a00000000, 0x27986d0300000000, 0x8885e6b800000000, 0xed8e9fd100000000, 0x3defabe900000000, 0x58e4d28000000000, 0xf7f9593b00000000, 0x92f2205200000000, 0xe8c43e9700000000, 0x8dcf47fe00000000, 0x22d2cc4500000000, 0x47d9b52c00000000, 0x8211a43500000000, 0xe71add5c00000000, 0x480756e700000000, 0x2d0c2f8e00000000, 0x573a314b00000000, 0x3231482200000000, 0x9d2cc39900000000, 0xf827baf000000000, 0x28468ec800000000, 0x4d4df7a100000000, 0xe2507c1a00000000, 0x875b057300000000, 0xfd6d1bb600000000, 0x986662df00000000, 0x377be96400000000, 0x5270900d00000000}, {0x0000000000000000, 0xdcecb13d00000000, 0xb8d9637b00000000, 0x6435d24600000000, 0x70b3c7f600000000, 0xac5f76cb00000000, 0xc86aa48d00000000, 0x148615b000000000, 0xa160fe3600000000, 0x7d8c4f0b00000000, 0x19b99d4d00000000, 0xc5552c7000000000, 0xd1d339c000000000, 0x0d3f88fd00000000, 0x690a5abb00000000, 0xb5e6eb8600000000, 0x42c1fc6d00000000, 0x9e2d4d5000000000, 0xfa189f1600000000, 0x26f42e2b00000000, 0x32723b9b00000000, 0xee9e8aa600000000, 0x8aab58e000000000, 0x5647e9dd00000000, 0xe3a1025b00000000, 0x3f4db36600000000, 0x5b78612000000000, 0x8794d01d00000000, 0x9312c5ad00000000, 0x4ffe749000000000, 0x2bcba6d600000000, 0xf72717eb00000000, 0x8482f9db00000000, 0x586e48e600000000, 0x3c5b9aa000000000, 0xe0b72b9d00000000, 0xf4313e2d00000000, 0x28dd8f1000000000, 0x4ce85d5600000000, 0x9004ec6b00000000, 0x25e207ed00000000, 0xf90eb6d000000000, 0x9d3b649600000000, 0x41d7d5ab00000000, 0x5551c01b00000000, 0x89bd712600000000, 0xed88a36000000000, 0x3164125d00000000, 0xc64305b600000000, 0x1aafb48b00000000, 0x7e9a66cd00000000, 0xa276d7f000000000, 0xb6f0c24000000000, 0x6a1c737d00000000, 0x0e29a13b00000000, 0xd2c5100600000000, 0x6723fb8000000000, 0xbbcf4abd00000000, 0xdffa98fb00000000, 0x031629c600000000, 0x17903c7600000000, 0xcb7c8d4b00000000, 0xaf495f0d00000000, 0x73a5ee3000000000, 0x4903826c00000000, 0x95ef335100000000, 0xf1dae11700000000, 0x2d36502a00000000, 0x39b0459a00000000, 0xe55cf4a700000000, 0x816926e100000000, 0x5d8597dc00000000, 0xe8637c5a00000000, 0x348fcd6700000000, 0x50ba1f2100000000, 0x8c56ae1c00000000, 0x98d0bbac00000000, 0x443c0a9100000000, 0x2009d8d700000000, 0xfce569ea00000000, 0x0bc27e0100000000, 0xd72ecf3c00000000, 0xb31b1d7a00000000, 0x6ff7ac4700000000, 0x7b71b9f700000000, 0xa79d08ca00000000, 0xc3a8da8c00000000, 0x1f446bb100000000, 0xaaa2803700000000, 0x764e310a00000000, 0x127be34c00000000, 0xce97527100000000, 0xda1147c100000000, 0x06fdf6fc00000000, 0x62c824ba00000000, 0xbe24958700000000, 0xcd817bb700000000, 0x116dca8a00000000, 0x755818cc00000000, 0xa9b4a9f100000000, 0xbd32bc4100000000, 0x61de0d7c00000000, 0x05ebdf3a00000000, 0xd9076e0700000000, 0x6ce1858100000000, 0xb00d34bc00000000, 0xd438e6fa00000000, 0x08d457c700000000, 0x1c52427700000000, 0xc0bef34a00000000, 0xa48b210c00000000, 0x7867903100000000, 0x8f4087da00000000, 0x53ac36e700000000, 0x3799e4a100000000, 0xeb75559c00000000, 0xfff3402c00000000, 0x231ff11100000000, 0x472a235700000000, 0x9bc6926a00000000, 0x2e2079ec00000000, 0xf2ccc8d100000000, 0x96f91a9700000000, 0x4a15abaa00000000, 0x5e93be1a00000000, 0x827f0f2700000000, 0xe64add6100000000, 0x3aa66c5c00000000, 0x920604d900000000, 0x4eeab5e400000000, 0x2adf67a200000000, 0xf633d69f00000000, 0xe2b5c32f00000000, 0x3e59721200000000, 0x5a6ca05400000000, 0x8680116900000000, 0x3366faef00000000, 0xef8a4bd200000000, 0x8bbf999400000000, 0x575328a900000000, 0x43d53d1900000000, 0x9f398c2400000000, 0xfb0c5e6200000000, 0x27e0ef5f00000000, 0xd0c7f8b400000000, 0x0c2b498900000000, 0x681e9bcf00000000, 0xb4f22af200000000, 0xa0743f4200000000, 0x7c988e7f00000000, 0x18ad5c3900000000, 0xc441ed0400000000, 0x71a7068200000000, 0xad4bb7bf00000000, 0xc97e65f900000000, 0x1592d4c400000000, 0x0114c17400000000, 0xddf8704900000000, 0xb9cda20f00000000, 0x6521133200000000, 0x1684fd0200000000, 0xca684c3f00000000, 0xae5d9e7900000000, 0x72b12f4400000000, 0x66373af400000000, 0xbadb8bc900000000, 0xdeee598f00000000, 0x0202e8b200000000, 0xb7e4033400000000, 0x6b08b20900000000, 0x0f3d604f00000000, 0xd3d1d17200000000, 0xc757c4c200000000, 0x1bbb75ff00000000, 0x7f8ea7b900000000, 0xa362168400000000, 0x5445016f00000000, 0x88a9b05200000000, 0xec9c621400000000, 0x3070d32900000000, 0x24f6c69900000000, 0xf81a77a400000000, 0x9c2fa5e200000000, 0x40c314df00000000, 0xf525ff5900000000, 0x29c94e6400000000, 0x4dfc9c2200000000, 0x91102d1f00000000, 0x859638af00000000, 0x597a899200000000, 0x3d4f5bd400000000, 0xe1a3eae900000000, 0xdb0586b500000000, 0x07e9378800000000, 0x63dce5ce00000000, 0xbf3054f300000000, 0xabb6414300000000, 0x775af07e00000000, 0x136f223800000000, 0xcf83930500000000, 0x7a65788300000000, 0xa689c9be00000000, 0xc2bc1bf800000000, 0x1e50aac500000000, 0x0ad6bf7500000000, 0xd63a0e4800000000, 0xb20fdc0e00000000, 0x6ee36d3300000000, 0x99c47ad800000000, 0x4528cbe500000000, 0x211d19a300000000, 0xfdf1a89e00000000, 0xe977bd2e00000000, 0x359b0c1300000000, 0x51aede5500000000, 0x8d426f6800000000, 0x38a484ee00000000, 0xe44835d300000000, 0x807de79500000000, 0x5c9156a800000000, 0x4817431800000000, 0x94fbf22500000000, 0xf0ce206300000000, 0x2c22915e00000000, 0x5f877f6e00000000, 0x836bce5300000000, 0xe75e1c1500000000, 0x3bb2ad2800000000, 0x2f34b89800000000, 0xf3d809a500000000, 0x97eddbe300000000, 0x4b016ade00000000, 0xfee7815800000000, 0x220b306500000000, 0x463ee22300000000, 0x9ad2531e00000000, 0x8e5446ae00000000, 0x52b8f79300000000, 0x368d25d500000000, 0xea6194e800000000, 0x1d46830300000000, 0xc1aa323e00000000, 0xa59fe07800000000, 0x7973514500000000, 0x6df544f500000000, 0xb119f5c800000000, 0xd52c278e00000000, 0x09c096b300000000, 0xbc267d3500000000, 0x60cacc0800000000, 0x04ff1e4e00000000, 0xd813af7300000000, 0xcc95bac300000000, 0x10790bfe00000000, 0x744cd9b800000000, 0xa8a0688500000000}}; #else /* W == 4 */ local const z_crc_t FAR crc_braid_table[][256] = { {0x00000000, 0x81256527, 0xd93bcc0f, 0x581ea928, 0x69069e5f, 0xe823fb78, 0xb03d5250, 0x31183777, 0xd20d3cbe, 0x53285999, 0x0b36f0b1, 0x8a139596, 0xbb0ba2e1, 0x3a2ec7c6, 0x62306eee, 0xe3150bc9, 0x7f6b7f3d, 0xfe4e1a1a, 0xa650b332, 0x2775d615, 0x166de162, 0x97488445, 0xcf562d6d, 0x4e73484a, 0xad664383, 0x2c4326a4, 0x745d8f8c, 0xf578eaab, 0xc460dddc, 0x4545b8fb, 0x1d5b11d3, 0x9c7e74f4, 0xfed6fe7a, 0x7ff39b5d, 0x27ed3275, 0xa6c85752, 0x97d06025, 0x16f50502, 0x4eebac2a, 0xcfcec90d, 0x2cdbc2c4, 0xadfea7e3, 0xf5e00ecb, 0x74c56bec, 0x45dd5c9b, 0xc4f839bc, 0x9ce69094, 0x1dc3f5b3, 0x81bd8147, 0x0098e460, 0x58864d48, 0xd9a3286f, 0xe8bb1f18, 0x699e7a3f, 0x3180d317, 0xb0a5b630, 0x53b0bdf9, 0xd295d8de, 0x8a8b71f6, 0x0bae14d1, 0x3ab623a6, 0xbb934681, 0xe38defa9, 0x62a88a8e, 0x26dcfab5, 0xa7f99f92, 0xffe736ba, 0x7ec2539d, 0x4fda64ea, 0xceff01cd, 0x96e1a8e5, 0x17c4cdc2, 0xf4d1c60b, 0x75f4a32c, 0x2dea0a04, 0xaccf6f23, 0x9dd75854, 0x1cf23d73, 0x44ec945b, 0xc5c9f17c, 0x59b78588, 0xd892e0af, 0x808c4987, 0x01a92ca0, 0x30b11bd7, 0xb1947ef0, 0xe98ad7d8, 0x68afb2ff, 0x8bbab936, 0x0a9fdc11, 0x52817539, 0xd3a4101e, 0xe2bc2769, 0x6399424e, 0x3b87eb66, 0xbaa28e41, 0xd80a04cf, 0x592f61e8, 0x0131c8c0, 0x8014ade7, 0xb10c9a90, 0x3029ffb7, 0x6837569f, 0xe91233b8, 0x0a073871, 0x8b225d56, 0xd33cf47e, 0x52199159, 0x6301a62e, 0xe224c309, 0xba3a6a21, 0x3b1f0f06, 0xa7617bf2, 0x26441ed5, 0x7e5ab7fd, 0xff7fd2da, 0xce67e5ad, 0x4f42808a, 0x175c29a2, 0x96794c85, 0x756c474c, 0xf449226b, 0xac578b43, 0x2d72ee64, 0x1c6ad913, 0x9d4fbc34, 0xc551151c, 0x4474703b, 0x4db9f56a, 0xcc9c904d, 0x94823965, 0x15a75c42, 0x24bf6b35, 0xa59a0e12, 0xfd84a73a, 0x7ca1c21d, 0x9fb4c9d4, 0x1e91acf3, 0x468f05db, 0xc7aa60fc, 0xf6b2578b, 0x779732ac, 0x2f899b84, 0xaeacfea3, 0x32d28a57, 0xb3f7ef70, 0xebe94658, 0x6acc237f, 0x5bd41408, 0xdaf1712f, 0x82efd807, 0x03cabd20, 0xe0dfb6e9, 0x61fad3ce, 0x39e47ae6, 0xb8c11fc1, 0x89d928b6, 0x08fc4d91, 0x50e2e4b9, 0xd1c7819e, 0xb36f0b10, 0x324a6e37, 0x6a54c71f, 0xeb71a238, 0xda69954f, 0x5b4cf068, 0x03525940, 0x82773c67, 0x616237ae, 0xe0475289, 0xb859fba1, 0x397c9e86, 0x0864a9f1, 0x8941ccd6, 0xd15f65fe, 0x507a00d9, 0xcc04742d, 0x4d21110a, 0x153fb822, 0x941add05, 0xa502ea72, 0x24278f55, 0x7c39267d, 0xfd1c435a, 0x1e094893, 0x9f2c2db4, 0xc732849c, 0x4617e1bb, 0x770fd6cc, 0xf62ab3eb, 0xae341ac3, 0x2f117fe4, 0x6b650fdf, 0xea406af8, 0xb25ec3d0, 0x337ba6f7, 0x02639180, 0x8346f4a7, 0xdb585d8f, 0x5a7d38a8, 0xb9683361, 0x384d5646, 0x6053ff6e, 0xe1769a49, 0xd06ead3e, 0x514bc819, 0x09556131, 0x88700416, 0x140e70e2, 0x952b15c5, 0xcd35bced, 0x4c10d9ca, 0x7d08eebd, 0xfc2d8b9a, 0xa43322b2, 0x25164795, 0xc6034c5c, 0x4726297b, 0x1f388053, 0x9e1de574, 0xaf05d203, 0x2e20b724, 0x763e1e0c, 0xf71b7b2b, 0x95b3f1a5, 0x14969482, 0x4c883daa, 0xcdad588d, 0xfcb56ffa, 0x7d900add, 0x258ea3f5, 0xa4abc6d2, 0x47becd1b, 0xc69ba83c, 0x9e850114, 0x1fa06433, 0x2eb85344, 0xaf9d3663, 0xf7839f4b, 0x76a6fa6c, 0xead88e98, 0x6bfdebbf, 0x33e34297, 0xb2c627b0, 0x83de10c7, 0x02fb75e0, 0x5ae5dcc8, 0xdbc0b9ef, 0x38d5b226, 0xb9f0d701, 0xe1ee7e29, 0x60cb1b0e, 0x51d32c79, 0xd0f6495e, 0x88e8e076, 0x09cd8551}, {0x00000000, 0x9b73ead4, 0xed96d3e9, 0x76e5393d, 0x005ca193, 0x9b2f4b47, 0xedca727a, 0x76b998ae, 0x00b94326, 0x9bcaa9f2, 0xed2f90cf, 0x765c7a1b, 0x00e5e2b5, 0x9b960861, 0xed73315c, 0x7600db88, 0x0172864c, 0x9a016c98, 0xece455a5, 0x7797bf71, 0x012e27df, 0x9a5dcd0b, 0xecb8f436, 0x77cb1ee2, 0x01cbc56a, 0x9ab82fbe, 0xec5d1683, 0x772efc57, 0x019764f9, 0x9ae48e2d, 0xec01b710, 0x77725dc4, 0x02e50c98, 0x9996e64c, 0xef73df71, 0x740035a5, 0x02b9ad0b, 0x99ca47df, 0xef2f7ee2, 0x745c9436, 0x025c4fbe, 0x992fa56a, 0xefca9c57, 0x74b97683, 0x0200ee2d, 0x997304f9, 0xef963dc4, 0x74e5d710, 0x03978ad4, 0x98e46000, 0xee01593d, 0x7572b3e9, 0x03cb2b47, 0x98b8c193, 0xee5df8ae, 0x752e127a, 0x032ec9f2, 0x985d2326, 0xeeb81a1b, 0x75cbf0cf, 0x03726861, 0x980182b5, 0xeee4bb88, 0x7597515c, 0x05ca1930, 0x9eb9f3e4, 0xe85ccad9, 0x732f200d, 0x0596b8a3, 0x9ee55277, 0xe8006b4a, 0x7373819e, 0x05735a16, 0x9e00b0c2, 0xe8e589ff, 0x7396632b, 0x052ffb85, 0x9e5c1151, 0xe8b9286c, 0x73cac2b8, 0x04b89f7c, 0x9fcb75a8, 0xe92e4c95, 0x725da641, 0x04e43eef, 0x9f97d43b, 0xe972ed06, 0x720107d2, 0x0401dc5a, 0x9f72368e, 0xe9970fb3, 0x72e4e567, 0x045d7dc9, 0x9f2e971d, 0xe9cbae20, 0x72b844f4, 0x072f15a8, 0x9c5cff7c, 0xeab9c641, 0x71ca2c95, 0x0773b43b, 0x9c005eef, 0xeae567d2, 0x71968d06, 0x0796568e, 0x9ce5bc5a, 0xea008567, 0x71736fb3, 0x07caf71d, 0x9cb91dc9, 0xea5c24f4, 0x712fce20, 0x065d93e4, 0x9d2e7930, 0xebcb400d, 0x70b8aad9, 0x06013277, 0x9d72d8a3, 0xeb97e19e, 0x70e40b4a, 0x06e4d0c2, 0x9d973a16, 0xeb72032b, 0x7001e9ff, 0x06b87151, 0x9dcb9b85, 0xeb2ea2b8, 0x705d486c, 0x0b943260, 0x90e7d8b4, 0xe602e189, 0x7d710b5d, 0x0bc893f3, 0x90bb7927, 0xe65e401a, 0x7d2daace, 0x0b2d7146, 0x905e9b92, 0xe6bba2af, 0x7dc8487b, 0x0b71d0d5, 0x90023a01, 0xe6e7033c, 0x7d94e9e8, 0x0ae6b42c, 0x91955ef8, 0xe77067c5, 0x7c038d11, 0x0aba15bf, 0x91c9ff6b, 0xe72cc656, 0x7c5f2c82, 0x0a5ff70a, 0x912c1dde, 0xe7c924e3, 0x7cbace37, 0x0a035699, 0x9170bc4d, 0xe7958570, 0x7ce66fa4, 0x09713ef8, 0x9202d42c, 0xe4e7ed11, 0x7f9407c5, 0x092d9f6b, 0x925e75bf, 0xe4bb4c82, 0x7fc8a656, 0x09c87dde, 0x92bb970a, 0xe45eae37, 0x7f2d44e3, 0x0994dc4d, 0x92e73699, 0xe4020fa4, 0x7f71e570, 0x0803b8b4, 0x93705260, 0xe5956b5d, 0x7ee68189, 0x085f1927, 0x932cf3f3, 0xe5c9cace, 0x7eba201a, 0x08bafb92, 0x93c91146, 0xe52c287b, 0x7e5fc2af, 0x08e65a01, 0x9395b0d5, 0xe57089e8, 0x7e03633c, 0x0e5e2b50, 0x952dc184, 0xe3c8f8b9, 0x78bb126d, 0x0e028ac3, 0x95716017, 0xe394592a, 0x78e7b3fe, 0x0ee76876, 0x959482a2, 0xe371bb9f, 0x7802514b, 0x0ebbc9e5, 0x95c82331, 0xe32d1a0c, 0x785ef0d8, 0x0f2cad1c, 0x945f47c8, 0xe2ba7ef5, 0x79c99421, 0x0f700c8f, 0x9403e65b, 0xe2e6df66, 0x799535b2, 0x0f95ee3a, 0x94e604ee, 0xe2033dd3, 0x7970d707, 0x0fc94fa9, 0x94baa57d, 0xe25f9c40, 0x792c7694, 0x0cbb27c8, 0x97c8cd1c, 0xe12df421, 0x7a5e1ef5, 0x0ce7865b, 0x97946c8f, 0xe17155b2, 0x7a02bf66, 0x0c0264ee, 0x97718e3a, 0xe194b707, 0x7ae75dd3, 0x0c5ec57d, 0x972d2fa9, 0xe1c81694, 0x7abbfc40, 0x0dc9a184, 0x96ba4b50, 0xe05f726d, 0x7b2c98b9, 0x0d950017, 0x96e6eac3, 0xe003d3fe, 0x7b70392a, 0x0d70e2a2, 0x96030876, 0xe0e6314b, 0x7b95db9f, 0x0d2c4331, 0x965fa9e5, 0xe0ba90d8, 0x7bc97a0c}, {0x00000000, 0x172864c0, 0x2e50c980, 0x3978ad40, 0x5ca19300, 0x4b89f7c0, 0x72f15a80, 0x65d93e40, 0xb9432600, 0xae6b42c0, 0x9713ef80, 0x803b8b40, 0xe5e2b500, 0xf2cad1c0, 0xcbb27c80, 0xdc9a1840, 0xa9f74a41, 0xbedf2e81, 0x87a783c1, 0x908fe701, 0xf556d941, 0xe27ebd81, 0xdb0610c1, 0xcc2e7401, 0x10b46c41, 0x079c0881, 0x3ee4a5c1, 0x29ccc101, 0x4c15ff41, 0x5b3d9b81, 0x624536c1, 0x756d5201, 0x889f92c3, 0x9fb7f603, 0xa6cf5b43, 0xb1e73f83, 0xd43e01c3, 0xc3166503, 0xfa6ec843, 0xed46ac83, 0x31dcb4c3, 0x26f4d003, 0x1f8c7d43, 0x08a41983, 0x6d7d27c3, 0x7a554303, 0x432dee43, 0x54058a83, 0x2168d882, 0x3640bc42, 0x0f381102, 0x181075c2, 0x7dc94b82, 0x6ae12f42, 0x53998202, 0x44b1e6c2, 0x982bfe82, 0x8f039a42, 0xb67b3702, 0xa15353c2, 0xc48a6d82, 0xd3a20942, 0xeadaa402, 0xfdf2c0c2, 0xca4e23c7, 0xdd664707, 0xe41eea47, 0xf3368e87, 0x96efb0c7, 0x81c7d407, 0xb8bf7947, 0xaf971d87, 0x730d05c7, 0x64256107, 0x5d5dcc47, 0x4a75a887, 0x2fac96c7, 0x3884f207, 0x01fc5f47, 0x16d43b87, 0x63b96986, 0x74910d46, 0x4de9a006, 0x5ac1c4c6, 0x3f18fa86, 0x28309e46, 0x11483306, 0x066057c6, 0xdafa4f86, 0xcdd22b46, 0xf4aa8606, 0xe382e2c6, 0x865bdc86, 0x9173b846, 0xa80b1506, 0xbf2371c6, 0x42d1b104, 0x55f9d5c4, 0x6c817884, 0x7ba91c44, 0x1e702204, 0x095846c4, 0x3020eb84, 0x27088f44, 0xfb929704, 0xecbaf3c4, 0xd5c25e84, 0xc2ea3a44, 0xa7330404, 0xb01b60c4, 0x8963cd84, 0x9e4ba944, 0xeb26fb45, 0xfc0e9f85, 0xc57632c5, 0xd25e5605, 0xb7876845, 0xa0af0c85, 0x99d7a1c5, 0x8effc505, 0x5265dd45, 0x454db985, 0x7c3514c5, 0x6b1d7005, 0x0ec44e45, 0x19ec2a85, 0x209487c5, 0x37bce305, 0x4fed41cf, 0x58c5250f, 0x61bd884f, 0x7695ec8f, 0x134cd2cf, 0x0464b60f, 0x3d1c1b4f, 0x2a347f8f, 0xf6ae67cf, 0xe186030f, 0xd8feae4f, 0xcfd6ca8f, 0xaa0ff4cf, 0xbd27900f, 0x845f3d4f, 0x9377598f, 0xe61a0b8e, 0xf1326f4e, 0xc84ac20e, 0xdf62a6ce, 0xbabb988e, 0xad93fc4e, 0x94eb510e, 0x83c335ce, 0x5f592d8e, 0x4871494e, 0x7109e40e, 0x662180ce, 0x03f8be8e, 0x14d0da4e, 0x2da8770e, 0x3a8013ce, 0xc772d30c, 0xd05ab7cc, 0xe9221a8c, 0xfe0a7e4c, 0x9bd3400c, 0x8cfb24cc, 0xb583898c, 0xa2abed4c, 0x7e31f50c, 0x691991cc, 0x50613c8c, 0x4749584c, 0x2290660c, 0x35b802cc, 0x0cc0af8c, 0x1be8cb4c, 0x6e85994d, 0x79adfd8d, 0x40d550cd, 0x57fd340d, 0x32240a4d, 0x250c6e8d, 0x1c74c3cd, 0x0b5ca70d, 0xd7c6bf4d, 0xc0eedb8d, 0xf99676cd, 0xeebe120d, 0x8b672c4d, 0x9c4f488d, 0xa537e5cd, 0xb21f810d, 0x85a36208, 0x928b06c8, 0xabf3ab88, 0xbcdbcf48, 0xd902f108, 0xce2a95c8, 0xf7523888, 0xe07a5c48, 0x3ce04408, 0x2bc820c8, 0x12b08d88, 0x0598e948, 0x6041d708, 0x7769b3c8, 0x4e111e88, 0x59397a48, 0x2c542849, 0x3b7c4c89, 0x0204e1c9, 0x152c8509, 0x70f5bb49, 0x67dddf89, 0x5ea572c9, 0x498d1609, 0x95170e49, 0x823f6a89, 0xbb47c7c9, 0xac6fa309, 0xc9b69d49, 0xde9ef989, 0xe7e654c9, 0xf0ce3009, 0x0d3cf0cb, 0x1a14940b, 0x236c394b, 0x34445d8b, 0x519d63cb, 0x46b5070b, 0x7fcdaa4b, 0x68e5ce8b, 0xb47fd6cb, 0xa357b20b, 0x9a2f1f4b, 0x8d077b8b, 0xe8de45cb, 0xfff6210b, 0xc68e8c4b, 0xd1a6e88b, 0xa4cbba8a, 0xb3e3de4a, 0x8a9b730a, 0x9db317ca, 0xf86a298a, 0xef424d4a, 0xd63ae00a, 0xc11284ca, 0x1d889c8a, 0x0aa0f84a, 0x33d8550a, 0x24f031ca, 0x41290f8a, 0x56016b4a, 0x6f79c60a, 0x7851a2ca}, {0x00000000, 0x9fda839e, 0xe4c4017d, 0x7b1e82e3, 0x12f904bb, 0x8d238725, 0xf63d05c6, 0x69e78658, 0x25f20976, 0xba288ae8, 0xc136080b, 0x5eec8b95, 0x370b0dcd, 0xa8d18e53, 0xd3cf0cb0, 0x4c158f2e, 0x4be412ec, 0xd43e9172, 0xaf201391, 0x30fa900f, 0x591d1657, 0xc6c795c9, 0xbdd9172a, 0x220394b4, 0x6e161b9a, 0xf1cc9804, 0x8ad21ae7, 0x15089979, 0x7cef1f21, 0xe3359cbf, 0x982b1e5c, 0x07f19dc2, 0x97c825d8, 0x0812a646, 0x730c24a5, 0xecd6a73b, 0x85312163, 0x1aeba2fd, 0x61f5201e, 0xfe2fa380, 0xb23a2cae, 0x2de0af30, 0x56fe2dd3, 0xc924ae4d, 0xa0c32815, 0x3f19ab8b, 0x44072968, 0xdbddaaf6, 0xdc2c3734, 0x43f6b4aa, 0x38e83649, 0xa732b5d7, 0xced5338f, 0x510fb011, 0x2a1132f2, 0xb5cbb16c, 0xf9de3e42, 0x6604bddc, 0x1d1a3f3f, 0x82c0bca1, 0xeb273af9, 0x74fdb967, 0x0fe33b84, 0x9039b81a, 0xf4e14df1, 0x6b3bce6f, 0x10254c8c, 0x8fffcf12, 0xe618494a, 0x79c2cad4, 0x02dc4837, 0x9d06cba9, 0xd1134487, 0x4ec9c719, 0x35d745fa, 0xaa0dc664, 0xc3ea403c, 0x5c30c3a2, 0x272e4141, 0xb8f4c2df, 0xbf055f1d, 0x20dfdc83, 0x5bc15e60, 0xc41bddfe, 0xadfc5ba6, 0x3226d838, 0x49385adb, 0xd6e2d945, 0x9af7566b, 0x052dd5f5, 0x7e335716, 0xe1e9d488, 0x880e52d0, 0x17d4d14e, 0x6cca53ad, 0xf310d033, 0x63296829, 0xfcf3ebb7, 0x87ed6954, 0x1837eaca, 0x71d06c92, 0xee0aef0c, 0x95146def, 0x0aceee71, 0x46db615f, 0xd901e2c1, 0xa21f6022, 0x3dc5e3bc, 0x542265e4, 0xcbf8e67a, 0xb0e66499, 0x2f3ce707, 0x28cd7ac5, 0xb717f95b, 0xcc097bb8, 0x53d3f826, 0x3a347e7e, 0xa5eefde0, 0xdef07f03, 0x412afc9d, 0x0d3f73b3, 0x92e5f02d, 0xe9fb72ce, 0x7621f150, 0x1fc67708, 0x801cf496, 0xfb027675, 0x64d8f5eb, 0x32b39da3, 0xad691e3d, 0xd6779cde, 0x49ad1f40, 0x204a9918, 0xbf901a86, 0xc48e9865, 0x5b541bfb, 0x174194d5, 0x889b174b, 0xf38595a8, 0x6c5f1636, 0x05b8906e, 0x9a6213f0, 0xe17c9113, 0x7ea6128d, 0x79578f4f, 0xe68d0cd1, 0x9d938e32, 0x02490dac, 0x6bae8bf4, 0xf474086a, 0x8f6a8a89, 0x10b00917, 0x5ca58639, 0xc37f05a7, 0xb8618744, 0x27bb04da, 0x4e5c8282, 0xd186011c, 0xaa9883ff, 0x35420061, 0xa57bb87b, 0x3aa13be5, 0x41bfb906, 0xde653a98, 0xb782bcc0, 0x28583f5e, 0x5346bdbd, 0xcc9c3e23, 0x8089b10d, 0x1f533293, 0x644db070, 0xfb9733ee, 0x9270b5b6, 0x0daa3628, 0x76b4b4cb, 0xe96e3755, 0xee9faa97, 0x71452909, 0x0a5babea, 0x95812874, 0xfc66ae2c, 0x63bc2db2, 0x18a2af51, 0x87782ccf, 0xcb6da3e1, 0x54b7207f, 0x2fa9a29c, 0xb0732102, 0xd994a75a, 0x464e24c4, 0x3d50a627, 0xa28a25b9, 0xc652d052, 0x598853cc, 0x2296d12f, 0xbd4c52b1, 0xd4abd4e9, 0x4b715777, 0x306fd594, 0xafb5560a, 0xe3a0d924, 0x7c7a5aba, 0x0764d859, 0x98be5bc7, 0xf159dd9f, 0x6e835e01, 0x159ddce2, 0x8a475f7c, 0x8db6c2be, 0x126c4120, 0x6972c3c3, 0xf6a8405d, 0x9f4fc605, 0x0095459b, 0x7b8bc778, 0xe45144e6, 0xa844cbc8, 0x379e4856, 0x4c80cab5, 0xd35a492b, 0xbabdcf73, 0x25674ced, 0x5e79ce0e, 0xc1a34d90, 0x519af58a, 0xce407614, 0xb55ef4f7, 0x2a847769, 0x4363f131, 0xdcb972af, 0xa7a7f04c, 0x387d73d2, 0x7468fcfc, 0xebb27f62, 0x90acfd81, 0x0f767e1f, 0x6691f847, 0xf94b7bd9, 0x8255f93a, 0x1d8f7aa4, 0x1a7ee766, 0x85a464f8, 0xfebae61b, 0x61606585, 0x0887e3dd, 0x975d6043, 0xec43e2a0, 0x7399613e, 0x3f8cee10, 0xa0566d8e, 0xdb48ef6d, 0x44926cf3, 0x2d75eaab, 0xb2af6935, 0xc9b1ebd6, 0x566b6848}}; local const z_word_t FAR crc_braid_big_table[][256] = { {0x00000000, 0x9e83da9f, 0x7d01c4e4, 0xe3821e7b, 0xbb04f912, 0x2587238d, 0xc6053df6, 0x5886e769, 0x7609f225, 0xe88a28ba, 0x0b0836c1, 0x958bec5e, 0xcd0d0b37, 0x538ed1a8, 0xb00ccfd3, 0x2e8f154c, 0xec12e44b, 0x72913ed4, 0x911320af, 0x0f90fa30, 0x57161d59, 0xc995c7c6, 0x2a17d9bd, 0xb4940322, 0x9a1b166e, 0x0498ccf1, 0xe71ad28a, 0x79990815, 0x211fef7c, 0xbf9c35e3, 0x5c1e2b98, 0xc29df107, 0xd825c897, 0x46a61208, 0xa5240c73, 0x3ba7d6ec, 0x63213185, 0xfda2eb1a, 0x1e20f561, 0x80a32ffe, 0xae2c3ab2, 0x30afe02d, 0xd32dfe56, 0x4dae24c9, 0x1528c3a0, 0x8bab193f, 0x68290744, 0xf6aadddb, 0x34372cdc, 0xaab4f643, 0x4936e838, 0xd7b532a7, 0x8f33d5ce, 0x11b00f51, 0xf232112a, 0x6cb1cbb5, 0x423edef9, 0xdcbd0466, 0x3f3f1a1d, 0xa1bcc082, 0xf93a27eb, 0x67b9fd74, 0x843be30f, 0x1ab83990, 0xf14de1f4, 0x6fce3b6b, 0x8c4c2510, 0x12cfff8f, 0x4a4918e6, 0xd4cac279, 0x3748dc02, 0xa9cb069d, 0x874413d1, 0x19c7c94e, 0xfa45d735, 0x64c60daa, 0x3c40eac3, 0xa2c3305c, 0x41412e27, 0xdfc2f4b8, 0x1d5f05bf, 0x83dcdf20, 0x605ec15b, 0xfedd1bc4, 0xa65bfcad, 0x38d82632, 0xdb5a3849, 0x45d9e2d6, 0x6b56f79a, 0xf5d52d05, 0x1657337e, 0x88d4e9e1, 0xd0520e88, 0x4ed1d417, 0xad53ca6c, 0x33d010f3, 0x29682963, 0xb7ebf3fc, 0x5469ed87, 0xcaea3718, 0x926cd071, 0x0cef0aee, 0xef6d1495, 0x71eece0a, 0x5f61db46, 0xc1e201d9, 0x22601fa2, 0xbce3c53d, 0xe4652254, 0x7ae6f8cb, 0x9964e6b0, 0x07e73c2f, 0xc57acd28, 0x5bf917b7, 0xb87b09cc, 0x26f8d353, 0x7e7e343a, 0xe0fdeea5, 0x037ff0de, 0x9dfc2a41, 0xb3733f0d, 0x2df0e592, 0xce72fbe9, 0x50f12176, 0x0877c61f, 0x96f41c80, 0x757602fb, 0xebf5d864, 0xa39db332, 0x3d1e69ad, 0xde9c77d6, 0x401fad49, 0x18994a20, 0x861a90bf, 0x65988ec4, 0xfb1b545b, 0xd5944117, 0x4b179b88, 0xa89585f3, 0x36165f6c, 0x6e90b805, 0xf013629a, 0x13917ce1, 0x8d12a67e, 0x4f8f5779, 0xd10c8de6, 0x328e939d, 0xac0d4902, 0xf48bae6b, 0x6a0874f4, 0x898a6a8f, 0x1709b010, 0x3986a55c, 0xa7057fc3, 0x448761b8, 0xda04bb27, 0x82825c4e, 0x1c0186d1, 0xff8398aa, 0x61004235, 0x7bb87ba5, 0xe53ba13a, 0x06b9bf41, 0x983a65de, 0xc0bc82b7, 0x5e3f5828, 0xbdbd4653, 0x233e9ccc, 0x0db18980, 0x9332531f, 0x70b04d64, 0xee3397fb, 0xb6b57092, 0x2836aa0d, 0xcbb4b476, 0x55376ee9, 0x97aa9fee, 0x09294571, 0xeaab5b0a, 0x74288195, 0x2cae66fc, 0xb22dbc63, 0x51afa218, 0xcf2c7887, 0xe1a36dcb, 0x7f20b754, 0x9ca2a92f, 0x022173b0, 0x5aa794d9, 0xc4244e46, 0x27a6503d, 0xb9258aa2, 0x52d052c6, 0xcc538859, 0x2fd19622, 0xb1524cbd, 0xe9d4abd4, 0x7757714b, 0x94d56f30, 0x0a56b5af, 0x24d9a0e3, 0xba5a7a7c, 0x59d86407, 0xc75bbe98, 0x9fdd59f1, 0x015e836e, 0xe2dc9d15, 0x7c5f478a, 0xbec2b68d, 0x20416c12, 0xc3c37269, 0x5d40a8f6, 0x05c64f9f, 0x9b459500, 0x78c78b7b, 0xe64451e4, 0xc8cb44a8, 0x56489e37, 0xb5ca804c, 0x2b495ad3, 0x73cfbdba, 0xed4c6725, 0x0ece795e, 0x904da3c1, 0x8af59a51, 0x147640ce, 0xf7f45eb5, 0x6977842a, 0x31f16343, 0xaf72b9dc, 0x4cf0a7a7, 0xd2737d38, 0xfcfc6874, 0x627fb2eb, 0x81fdac90, 0x1f7e760f, 0x47f89166, 0xd97b4bf9, 0x3af95582, 0xa47a8f1d, 0x66e77e1a, 0xf864a485, 0x1be6bafe, 0x85656061, 0xdde38708, 0x43605d97, 0xa0e243ec, 0x3e619973, 0x10ee8c3f, 0x8e6d56a0, 0x6def48db, 0xf36c9244, 0xabea752d, 0x3569afb2, 0xd6ebb1c9, 0x48686b56}, {0x00000000, 0xc0642817, 0x80c9502e, 0x40ad7839, 0x0093a15c, 0xc0f7894b, 0x805af172, 0x403ed965, 0x002643b9, 0xc0426bae, 0x80ef1397, 0x408b3b80, 0x00b5e2e5, 0xc0d1caf2, 0x807cb2cb, 0x40189adc, 0x414af7a9, 0x812edfbe, 0xc183a787, 0x01e78f90, 0x41d956f5, 0x81bd7ee2, 0xc11006db, 0x01742ecc, 0x416cb410, 0x81089c07, 0xc1a5e43e, 0x01c1cc29, 0x41ff154c, 0x819b3d5b, 0xc1364562, 0x01526d75, 0xc3929f88, 0x03f6b79f, 0x435bcfa6, 0x833fe7b1, 0xc3013ed4, 0x036516c3, 0x43c86efa, 0x83ac46ed, 0xc3b4dc31, 0x03d0f426, 0x437d8c1f, 0x8319a408, 0xc3277d6d, 0x0343557a, 0x43ee2d43, 0x838a0554, 0x82d86821, 0x42bc4036, 0x0211380f, 0xc2751018, 0x824bc97d, 0x422fe16a, 0x02829953, 0xc2e6b144, 0x82fe2b98, 0x429a038f, 0x02377bb6, 0xc25353a1, 0x826d8ac4, 0x4209a2d3, 0x02a4daea, 0xc2c0f2fd, 0xc7234eca, 0x074766dd, 0x47ea1ee4, 0x878e36f3, 0xc7b0ef96, 0x07d4c781, 0x4779bfb8, 0x871d97af, 0xc7050d73, 0x07612564, 0x47cc5d5d, 0x87a8754a, 0xc796ac2f, 0x07f28438, 0x475ffc01, 0x873bd416, 0x8669b963, 0x460d9174, 0x06a0e94d, 0xc6c4c15a, 0x86fa183f, 0x469e3028, 0x06334811, 0xc6576006, 0x864ffada, 0x462bd2cd, 0x0686aaf4, 0xc6e282e3, 0x86dc5b86, 0x46b87391, 0x06150ba8, 0xc67123bf, 0x04b1d142, 0xc4d5f955, 0x8478816c, 0x441ca97b, 0x0422701e, 0xc4465809, 0x84eb2030, 0x448f0827, 0x049792fb, 0xc4f3baec, 0x845ec2d5, 0x443aeac2, 0x040433a7, 0xc4601bb0, 0x84cd6389, 0x44a94b9e, 0x45fb26eb, 0x859f0efc, 0xc53276c5, 0x05565ed2, 0x456887b7, 0x850cafa0, 0xc5a1d799, 0x05c5ff8e, 0x45dd6552, 0x85b94d45, 0xc514357c, 0x05701d6b, 0x454ec40e, 0x852aec19, 0xc5879420, 0x05e3bc37, 0xcf41ed4f, 0x0f25c558, 0x4f88bd61, 0x8fec9576, 0xcfd24c13, 0x0fb66404, 0x4f1b1c3d, 0x8f7f342a, 0xcf67aef6, 0x0f0386e1, 0x4faefed8, 0x8fcad6cf, 0xcff40faa, 0x0f9027bd, 0x4f3d5f84, 0x8f597793, 0x8e0b1ae6, 0x4e6f32f1, 0x0ec24ac8, 0xcea662df, 0x8e98bbba, 0x4efc93ad, 0x0e51eb94, 0xce35c383, 0x8e2d595f, 0x4e497148, 0x0ee40971, 0xce802166, 0x8ebef803, 0x4edad014, 0x0e77a82d, 0xce13803a, 0x0cd372c7, 0xccb75ad0, 0x8c1a22e9, 0x4c7e0afe, 0x0c40d39b, 0xcc24fb8c, 0x8c8983b5, 0x4cedaba2, 0x0cf5317e, 0xcc911969, 0x8c3c6150, 0x4c584947, 0x0c669022, 0xcc02b835, 0x8cafc00c, 0x4ccbe81b, 0x4d99856e, 0x8dfdad79, 0xcd50d540, 0x0d34fd57, 0x4d0a2432, 0x8d6e0c25, 0xcdc3741c, 0x0da75c0b, 0x4dbfc6d7, 0x8ddbeec0, 0xcd7696f9, 0x0d12beee, 0x4d2c678b, 0x8d484f9c, 0xcde537a5, 0x0d811fb2, 0x0862a385, 0xc8068b92, 0x88abf3ab, 0x48cfdbbc, 0x08f102d9, 0xc8952ace, 0x883852f7, 0x485c7ae0, 0x0844e03c, 0xc820c82b, 0x888db012, 0x48e99805, 0x08d74160, 0xc8b36977, 0x881e114e, 0x487a3959, 0x4928542c, 0x894c7c3b, 0xc9e10402, 0x09852c15, 0x49bbf570, 0x89dfdd67, 0xc972a55e, 0x09168d49, 0x490e1795, 0x896a3f82, 0xc9c747bb, 0x09a36fac, 0x499db6c9, 0x89f99ede, 0xc954e6e7, 0x0930cef0, 0xcbf03c0d, 0x0b94141a, 0x4b396c23, 0x8b5d4434, 0xcb639d51, 0x0b07b546, 0x4baacd7f, 0x8bcee568, 0xcbd67fb4, 0x0bb257a3, 0x4b1f2f9a, 0x8b7b078d, 0xcb45dee8, 0x0b21f6ff, 0x4b8c8ec6, 0x8be8a6d1, 0x8abacba4, 0x4adee3b3, 0x0a739b8a, 0xca17b39d, 0x8a296af8, 0x4a4d42ef, 0x0ae03ad6, 0xca8412c1, 0x8a9c881d, 0x4af8a00a, 0x0a55d833, 0xca31f024, 0x8a0f2941, 0x4a6b0156, 0x0ac6796f, 0xcaa25178}, {0x00000000, 0xd4ea739b, 0xe9d396ed, 0x3d39e576, 0x93a15c00, 0x474b2f9b, 0x7a72caed, 0xae98b976, 0x2643b900, 0xf2a9ca9b, 0xcf902fed, 0x1b7a5c76, 0xb5e2e500, 0x6108969b, 0x5c3173ed, 0x88db0076, 0x4c867201, 0x986c019a, 0xa555e4ec, 0x71bf9777, 0xdf272e01, 0x0bcd5d9a, 0x36f4b8ec, 0xe21ecb77, 0x6ac5cb01, 0xbe2fb89a, 0x83165dec, 0x57fc2e77, 0xf9649701, 0x2d8ee49a, 0x10b701ec, 0xc45d7277, 0x980ce502, 0x4ce69699, 0x71df73ef, 0xa5350074, 0x0badb902, 0xdf47ca99, 0xe27e2fef, 0x36945c74, 0xbe4f5c02, 0x6aa52f99, 0x579ccaef, 0x8376b974, 0x2dee0002, 0xf9047399, 0xc43d96ef, 0x10d7e574, 0xd48a9703, 0x0060e498, 0x3d5901ee, 0xe9b37275, 0x472bcb03, 0x93c1b898, 0xaef85dee, 0x7a122e75, 0xf2c92e03, 0x26235d98, 0x1b1ab8ee, 0xcff0cb75, 0x61687203, 0xb5820198, 0x88bbe4ee, 0x5c519775, 0x3019ca05, 0xe4f3b99e, 0xd9ca5ce8, 0x0d202f73, 0xa3b89605, 0x7752e59e, 0x4a6b00e8, 0x9e817373, 0x165a7305, 0xc2b0009e, 0xff89e5e8, 0x2b639673, 0x85fb2f05, 0x51115c9e, 0x6c28b9e8, 0xb8c2ca73, 0x7c9fb804, 0xa875cb9f, 0x954c2ee9, 0x41a65d72, 0xef3ee404, 0x3bd4979f, 0x06ed72e9, 0xd2070172, 0x5adc0104, 0x8e36729f, 0xb30f97e9, 0x67e5e472, 0xc97d5d04, 0x1d972e9f, 0x20aecbe9, 0xf444b872, 0xa8152f07, 0x7cff5c9c, 0x41c6b9ea, 0x952cca71, 0x3bb47307, 0xef5e009c, 0xd267e5ea, 0x068d9671, 0x8e569607, 0x5abce59c, 0x678500ea, 0xb36f7371, 0x1df7ca07, 0xc91db99c, 0xf4245cea, 0x20ce2f71, 0xe4935d06, 0x30792e9d, 0x0d40cbeb, 0xd9aab870, 0x77320106, 0xa3d8729d, 0x9ee197eb, 0x4a0be470, 0xc2d0e406, 0x163a979d, 0x2b0372eb, 0xffe90170, 0x5171b806, 0x859bcb9d, 0xb8a22eeb, 0x6c485d70, 0x6032940b, 0xb4d8e790, 0x89e102e6, 0x5d0b717d, 0xf393c80b, 0x2779bb90, 0x1a405ee6, 0xceaa2d7d, 0x46712d0b, 0x929b5e90, 0xafa2bbe6, 0x7b48c87d, 0xd5d0710b, 0x013a0290, 0x3c03e7e6, 0xe8e9947d, 0x2cb4e60a, 0xf85e9591, 0xc56770e7, 0x118d037c, 0xbf15ba0a, 0x6bffc991, 0x56c62ce7, 0x822c5f7c, 0x0af75f0a, 0xde1d2c91, 0xe324c9e7, 0x37ceba7c, 0x9956030a, 0x4dbc7091, 0x708595e7, 0xa46fe67c, 0xf83e7109, 0x2cd40292, 0x11ede7e4, 0xc507947f, 0x6b9f2d09, 0xbf755e92, 0x824cbbe4, 0x56a6c87f, 0xde7dc809, 0x0a97bb92, 0x37ae5ee4, 0xe3442d7f, 0x4ddc9409, 0x9936e792, 0xa40f02e4, 0x70e5717f, 0xb4b80308, 0x60527093, 0x5d6b95e5, 0x8981e67e, 0x27195f08, 0xf3f32c93, 0xcecac9e5, 0x1a20ba7e, 0x92fbba08, 0x4611c993, 0x7b282ce5, 0xafc25f7e, 0x015ae608, 0xd5b09593, 0xe88970e5, 0x3c63037e, 0x502b5e0e, 0x84c12d95, 0xb9f8c8e3, 0x6d12bb78, 0xc38a020e, 0x17607195, 0x2a5994e3, 0xfeb3e778, 0x7668e70e, 0xa2829495, 0x9fbb71e3, 0x4b510278, 0xe5c9bb0e, 0x3123c895, 0x0c1a2de3, 0xd8f05e78, 0x1cad2c0f, 0xc8475f94, 0xf57ebae2, 0x2194c979, 0x8f0c700f, 0x5be60394, 0x66dfe6e2, 0xb2359579, 0x3aee950f, 0xee04e694, 0xd33d03e2, 0x07d77079, 0xa94fc90f, 0x7da5ba94, 0x409c5fe2, 0x94762c79, 0xc827bb0c, 0x1ccdc897, 0x21f42de1, 0xf51e5e7a, 0x5b86e70c, 0x8f6c9497, 0xb25571e1, 0x66bf027a, 0xee64020c, 0x3a8e7197, 0x07b794e1, 0xd35de77a, 0x7dc55e0c, 0xa92f2d97, 0x9416c8e1, 0x40fcbb7a, 0x84a1c90d, 0x504bba96, 0x6d725fe0, 0xb9982c7b, 0x1700950d, 0xc3eae696, 0xfed303e0, 0x2a39707b, 0xa2e2700d, 0x76080396, 0x4b31e6e0, 0x9fdb957b, 0x31432c0d, 0xe5a95f96, 0xd890bae0, 0x0c7ac97b}, {0x00000000, 0x27652581, 0x0fcc3bd9, 0x28a91e58, 0x5f9e0669, 0x78fb23e8, 0x50523db0, 0x77371831, 0xbe3c0dd2, 0x99592853, 0xb1f0360b, 0x9695138a, 0xe1a20bbb, 0xc6c72e3a, 0xee6e3062, 0xc90b15e3, 0x3d7f6b7f, 0x1a1a4efe, 0x32b350a6, 0x15d67527, 0x62e16d16, 0x45844897, 0x6d2d56cf, 0x4a48734e, 0x834366ad, 0xa426432c, 0x8c8f5d74, 0xabea78f5, 0xdcdd60c4, 0xfbb84545, 0xd3115b1d, 0xf4747e9c, 0x7afed6fe, 0x5d9bf37f, 0x7532ed27, 0x5257c8a6, 0x2560d097, 0x0205f516, 0x2aaceb4e, 0x0dc9cecf, 0xc4c2db2c, 0xe3a7fead, 0xcb0ee0f5, 0xec6bc574, 0x9b5cdd45, 0xbc39f8c4, 0x9490e69c, 0xb3f5c31d, 0x4781bd81, 0x60e49800, 0x484d8658, 0x6f28a3d9, 0x181fbbe8, 0x3f7a9e69, 0x17d38031, 0x30b6a5b0, 0xf9bdb053, 0xded895d2, 0xf6718b8a, 0xd114ae0b, 0xa623b63a, 0x814693bb, 0xa9ef8de3, 0x8e8aa862, 0xb5fadc26, 0x929ff9a7, 0xba36e7ff, 0x9d53c27e, 0xea64da4f, 0xcd01ffce, 0xe5a8e196, 0xc2cdc417, 0x0bc6d1f4, 0x2ca3f475, 0x040aea2d, 0x236fcfac, 0x5458d79d, 0x733df21c, 0x5b94ec44, 0x7cf1c9c5, 0x8885b759, 0xafe092d8, 0x87498c80, 0xa02ca901, 0xd71bb130, 0xf07e94b1, 0xd8d78ae9, 0xffb2af68, 0x36b9ba8b, 0x11dc9f0a, 0x39758152, 0x1e10a4d3, 0x6927bce2, 0x4e429963, 0x66eb873b, 0x418ea2ba, 0xcf040ad8, 0xe8612f59, 0xc0c83101, 0xe7ad1480, 0x909a0cb1, 0xb7ff2930, 0x9f563768, 0xb83312e9, 0x7138070a, 0x565d228b, 0x7ef43cd3, 0x59911952, 0x2ea60163, 0x09c324e2, 0x216a3aba, 0x060f1f3b, 0xf27b61a7, 0xd51e4426, 0xfdb75a7e, 0xdad27fff, 0xade567ce, 0x8a80424f, 0xa2295c17, 0x854c7996, 0x4c476c75, 0x6b2249f4, 0x438b57ac, 0x64ee722d, 0x13d96a1c, 0x34bc4f9d, 0x1c1551c5, 0x3b707444, 0x6af5b94d, 0x4d909ccc, 0x65398294, 0x425ca715, 0x356bbf24, 0x120e9aa5, 0x3aa784fd, 0x1dc2a17c, 0xd4c9b49f, 0xf3ac911e, 0xdb058f46, 0xfc60aac7, 0x8b57b2f6, 0xac329777, 0x849b892f, 0xa3feacae, 0x578ad232, 0x70eff7b3, 0x5846e9eb, 0x7f23cc6a, 0x0814d45b, 0x2f71f1da, 0x07d8ef82, 0x20bdca03, 0xe9b6dfe0, 0xced3fa61, 0xe67ae439, 0xc11fc1b8, 0xb628d989, 0x914dfc08, 0xb9e4e250, 0x9e81c7d1, 0x100b6fb3, 0x376e4a32, 0x1fc7546a, 0x38a271eb, 0x4f9569da, 0x68f04c5b, 0x40595203, 0x673c7782, 0xae376261, 0x895247e0, 0xa1fb59b8, 0x869e7c39, 0xf1a96408, 0xd6cc4189, 0xfe655fd1, 0xd9007a50, 0x2d7404cc, 0x0a11214d, 0x22b83f15, 0x05dd1a94, 0x72ea02a5, 0x558f2724, 0x7d26397c, 0x5a431cfd, 0x9348091e, 0xb42d2c9f, 0x9c8432c7, 0xbbe11746, 0xccd60f77, 0xebb32af6, 0xc31a34ae, 0xe47f112f, 0xdf0f656b, 0xf86a40ea, 0xd0c35eb2, 0xf7a67b33, 0x80916302, 0xa7f44683, 0x8f5d58db, 0xa8387d5a, 0x613368b9, 0x46564d38, 0x6eff5360, 0x499a76e1, 0x3ead6ed0, 0x19c84b51, 0x31615509, 0x16047088, 0xe2700e14, 0xc5152b95, 0xedbc35cd, 0xcad9104c, 0xbdee087d, 0x9a8b2dfc, 0xb22233a4, 0x95471625, 0x5c4c03c6, 0x7b292647, 0x5380381f, 0x74e51d9e, 0x03d205af, 0x24b7202e, 0x0c1e3e76, 0x2b7b1bf7, 0xa5f1b395, 0x82949614, 0xaa3d884c, 0x8d58adcd, 0xfa6fb5fc, 0xdd0a907d, 0xf5a38e25, 0xd2c6aba4, 0x1bcdbe47, 0x3ca89bc6, 0x1401859e, 0x3364a01f, 0x4453b82e, 0x63369daf, 0x4b9f83f7, 0x6cfaa676, 0x988ed8ea, 0xbfebfd6b, 0x9742e333, 0xb027c6b2, 0xc710de83, 0xe075fb02, 0xc8dce55a, 0xefb9c0db, 0x26b2d538, 0x01d7f0b9, 0x297eeee1, 0x0e1bcb60, 0x792cd351, 0x5e49f6d0, 0x76e0e888, 0x5185cd09}}; #endif #endif #endif local const z_crc_t FAR x2n_table[] = { 0x40000000, 0x20000000, 0x08000000, 0x00800000, 0x00008000, 0xedb88320, 0xb1e6b092, 0xa06a2517, 0xed627dae, 0x88d14467, 0xd7bbfe6a, 0xec447f11, 0x8e7ea170, 0x6427800e, 0x4d47bae0, 0x09fe548f, 0x83852d0f, 0x30362f1a, 0x7b5a9cc3, 0x31fec169, 0x9fec022a, 0x6c8dedc4, 0x15d6874d, 0x5fde7a4e, 0xbad90e37, 0x2e4e5eef, 0x4eaba214, 0xa8a472c0, 0x429a969e, 0x148d302a, 0xc40ba6d0, 0xc4e22c3c}; |
Added compat/zlib/deflate.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 | /* deflate.c -- compress data using the deflation algorithm * Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process depends on being able to identify portions * of the input text which are identical to earlier input (within a * sliding window trailing behind the input currently being processed). * * The most straightforward technique turns out to be the fastest for * most input files: try all possible matches and select the longest. * The key feature of this algorithm is that insertions into the string * dictionary are very simple and thus fast, and deletions are avoided * completely. Insertions are performed at each input character, whereas * string matches are performed only when the previous match ends. So it * is preferable to spend more time in matches to allow very fast string * insertions and avoid deletions. The matching algorithm for small * strings is inspired from that of Rabin & Karp. A brute force approach * is used to find longer strings when a small match has been found. * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze * (by Leonid Broukhis). * A previous version of this file used a more sophisticated algorithm * (by Fiala and Greene) which is guaranteed to run in linear amortized * time, but has a larger average cost, uses more memory and is patented. * However the F&G algorithm may be faster for some highly redundant * files if the parameter max_chain_length (described below) is too large. * * ACKNOWLEDGEMENTS * * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and * I found it in 'freeze' written by Leonid Broukhis. * Thanks to many people for bug reports and testing. * * REFERENCES * * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". * Available in http://tools.ietf.org/html/rfc1951 * * A description of the Rabin and Karp algorithm is given in the book * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. * * Fiala,E.R., and Greene,D.H. * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 * */ /* @(#) $Id$ */ #include "deflate.h" const char deflate_copyright[] = " deflate 1.3.1 Copyright 1995-2024 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ typedef enum { need_more, /* block not completed, need more input or more output */ block_done, /* block flush performed */ finish_started, /* finish started, need only more output at next deflate */ finish_done /* finish done, accept no more input or output */ } block_state; typedef block_state (*compress_func)(deflate_state *s, int flush); /* Compression function. Returns the block state after the call. */ local block_state deflate_stored(deflate_state *s, int flush); local block_state deflate_fast(deflate_state *s, int flush); #ifndef FASTEST local block_state deflate_slow(deflate_state *s, int flush); #endif local block_state deflate_rle(deflate_state *s, int flush); local block_state deflate_huff(deflate_state *s, int flush); /* =========================================================================== * Local data */ #define NIL 0 /* Tail of hash chains */ #ifndef TOO_FAR # define TOO_FAR 4096 #endif /* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be * found for specific files. */ typedef struct config_s { ush good_length; /* reduce lazy search above this match length */ ush max_lazy; /* do not perform lazy search above this match length */ ush nice_length; /* quit search above this match length */ ush max_chain; compress_func func; } config; #ifdef FASTEST local const config configuration_table[2] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ #else local const config configuration_table[10] = { /* good lazy nice chain */ /* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ /* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ /* 2 */ {4, 5, 16, 8, deflate_fast}, /* 3 */ {4, 6, 32, 32, deflate_fast}, /* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ /* 5 */ {8, 16, 32, 32, deflate_slow}, /* 6 */ {8, 16, 128, 128, deflate_slow}, /* 7 */ {8, 32, 128, 256, deflate_slow}, /* 8 */ {32, 128, 258, 1024, deflate_slow}, /* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ #endif /* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 * For deflate_fast() (levels <= 3) good is ignored and lazy has a different * meaning. */ /* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ #define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0)) /* =========================================================================== * Update a hash value with the given input byte * IN assertion: all calls to UPDATE_HASH are made with consecutive input * characters, so that a running hash key can be computed from the previous * key instead of complete recalculation each time. */ #define UPDATE_HASH(s,h,c) (h = (((h) << s->hash_shift) ^ (c)) & s->hash_mask) /* =========================================================================== * Insert string str in the dictionary and set match_head to the previous head * of the hash chain (the most recent string with same hash key). Return * the previous length of the hash chain. * If this file is compiled with -DFASTEST, the compression level is forced * to 1, and no hash chains are maintained. * IN assertion: all calls to INSERT_STRING are made with consecutive input * characters and the first MIN_MATCH bytes of str are valid (except for * the last MIN_MATCH-1 bytes of the input file). */ #ifdef FASTEST #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ match_head = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) #else #define INSERT_STRING(s, str, match_head) \ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ s->head[s->ins_h] = (Pos)(str)) #endif /* =========================================================================== * Initialize the hash table (avoiding 64K overflow for 16 bit systems). * prev[] will be initialized on the fly. */ #define CLEAR_HASH(s) \ do { \ s->head[s->hash_size - 1] = NIL; \ zmemzero((Bytef *)s->head, \ (unsigned)(s->hash_size - 1)*sizeof(*s->head)); \ } while (0) /* =========================================================================== * Slide the hash table when sliding the window down (could be avoided with 32 * bit values at the expense of memory usage). We slide even when level == 0 to * keep the hash table consistent if we switch back to level > 0 later. */ #if defined(__has_feature) # if __has_feature(memory_sanitizer) __attribute__((no_sanitize("memory"))) # endif #endif local void slide_hash(deflate_state *s) { unsigned n, m; Posf *p; uInt wsize = s->w_size; n = s->hash_size; p = &s->head[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m - wsize : NIL); } while (--n); n = wsize; #ifndef FASTEST p = &s->prev[n]; do { m = *--p; *p = (Pos)(m >= wsize ? m - wsize : NIL); /* If n is not on any hash chain, prev[n] is garbage but * its value will never be used. */ } while (--n); #endif } /* =========================================================================== * Read a new buffer from the current input stream, update the adler32 * and total number of bytes read. All deflate() input goes through * this function so some applications may wish to modify it to avoid * allocating a large strm->next_in buffer and copying from it. * (See also flush_pending()). */ local unsigned read_buf(z_streamp strm, Bytef *buf, unsigned size) { unsigned len = strm->avail_in; if (len > size) len = size; if (len == 0) return 0; strm->avail_in -= len; zmemcpy(buf, strm->next_in, len); if (strm->state->wrap == 1) { strm->adler = adler32(strm->adler, buf, len); } #ifdef GZIP else if (strm->state->wrap == 2) { strm->adler = crc32(strm->adler, buf, len); } #endif strm->next_in += len; strm->total_in += len; return len; } /* =========================================================================== * Fill the window when the lookahead becomes insufficient. * Updates strstart and lookahead. * * IN assertion: lookahead < MIN_LOOKAHEAD * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD * At least one byte has been read, or avail_in == 0; reads are * performed for at least two bytes (required for the zip translate_eol * option -- not supported here). */ local void fill_window(deflate_state *s) { unsigned n; unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); do { more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); /* Deal with !@#$% 64K limit: */ if (sizeof(int) <= 2) { if (more == 0 && s->strstart == 0 && s->lookahead == 0) { more = wsize; } else if (more == (unsigned)(-1)) { /* Very unlikely, but possible on 16 bit machine if * strstart == 0 && lookahead == 1 (input done a byte at time) */ more--; } } /* If the window is almost full and there is insufficient lookahead, * move the upper half to the lower one to make room in the upper half. */ if (s->strstart >= wsize + MAX_DIST(s)) { zmemcpy(s->window, s->window + wsize, (unsigned)wsize - more); s->match_start -= wsize; s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ s->block_start -= (long) wsize; if (s->insert > s->strstart) s->insert = s->strstart; slide_hash(s); more += wsize; } if (s->strm->avail_in == 0) break; /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && * more == window_size - lookahead - strstart * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) * => more >= window_size - 2*WSIZE + 2 * In the BIG_MEM or MMAP case (not yet supported), * window_size == input_size + MIN_LOOKAHEAD && * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. * Otherwise, window_size == 2*WSIZE so more >= 2. * If there was sliding, more >= WSIZE. So in all cases, more >= 2. */ Assert(more >= 2, "more < 2"); n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); s->lookahead += n; /* Initialize the hash value now that we have some input: */ if (s->lookahead + s->insert >= MIN_MATCH) { uInt str = s->strstart - s->insert; s->ins_h = s->window[str]; UPDATE_HASH(s, s->ins_h, s->window[str + 1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif while (s->insert) { UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); #ifndef FASTEST s->prev[str & s->w_mask] = s->head[s->ins_h]; #endif s->head[s->ins_h] = (Pos)str; str++; s->insert--; if (s->lookahead + s->insert < MIN_MATCH) break; } } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. */ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); /* If the WIN_INIT bytes after the end of the current data have never been * written, then zero those bytes in order to avoid memory check reports of * the use of uninitialized (or uninitialised as Julian writes) bytes by * the longest match routines. Update the high water mark for the next * time through here. WIN_INIT is set to MAX_MATCH since the longest match * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. */ if (s->high_water < s->window_size) { ulg curr = s->strstart + (ulg)(s->lookahead); ulg init; if (s->high_water < curr) { /* Previous high water mark below current data -- zero WIN_INIT * bytes or up to end of window, whichever is less. */ init = s->window_size - curr; if (init > WIN_INIT) init = WIN_INIT; zmemzero(s->window + curr, (unsigned)init); s->high_water = curr + init; } else if (s->high_water < (ulg)curr + WIN_INIT) { /* High water mark at or above current data, but below current data * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up * to end of window, whichever is less. */ init = (ulg)curr + WIN_INIT - s->high_water; if (init > s->window_size - s->high_water) init = s->window_size - s->high_water; zmemzero(s->window + s->high_water, (unsigned)init); s->high_water += init; } } Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, "not enough room for search"); } /* ========================================================================= */ int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version, int stream_size) { return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, version, stream_size); /* To do: ignore strm->next_in if we use it as window */ } /* ========================================================================= */ int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size) { deflate_state *s; int wrap = 1; static const char my_version[] = ZLIB_VERSION; if (version == Z_NULL || version[0] != my_version[0] || stream_size != sizeof(z_stream)) { return Z_VERSION_ERROR; } if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; if (strm->zalloc == (alloc_func)0) { #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; #endif } if (strm->zfree == (free_func)0) #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zfree = zcfree; #endif #ifdef FASTEST if (level != 0) level = 1; #else if (level == Z_DEFAULT_COMPRESSION) level = 6; #endif if (windowBits < 0) { /* suppress zlib wrapper */ wrap = 0; if (windowBits < -15) return Z_STREAM_ERROR; windowBits = -windowBits; } #ifdef GZIP else if (windowBits > 15) { wrap = 2; /* write gzip wrapper instead */ windowBits -= 16; } #endif if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) { return Z_STREAM_ERROR; } if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); if (s == Z_NULL) return Z_MEM_ERROR; strm->state = (struct internal_state FAR *)s; s->strm = strm; s->status = INIT_STATE; /* to pass state test in deflateReset() */ s->wrap = wrap; s->gzhead = Z_NULL; s->w_bits = (uInt)windowBits; s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; s->hash_bits = (uInt)memLevel + 7; s->hash_size = 1 << s->hash_bits; s->hash_mask = s->hash_size - 1; s->hash_shift = ((s->hash_bits + MIN_MATCH-1) / MIN_MATCH); s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); s->high_water = 0; /* nothing written to s->window yet */ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ /* We overlay pending_buf and sym_buf. This works since the average size * for length/distance pairs over any compressed block is assured to be 31 * bits or less. * * Analysis: The longest fixed codes are a length code of 8 bits plus 5 * extra bits, for lengths 131 to 257. The longest fixed distance codes are * 5 bits plus 13 extra bits, for distances 16385 to 32768. The longest * possible fixed-codes length/distance pair is then 31 bits total. * * sym_buf starts one-fourth of the way into pending_buf. So there are * three bytes in sym_buf for every four bytes in pending_buf. Each symbol * in sym_buf is three bytes -- two for the distance and one for the * literal/length. As each symbol is consumed, the pointer to the next * sym_buf value to read moves forward three bytes. From that symbol, up to * 31 bits are written to pending_buf. The closest the written pending_buf * bits gets to the next sym_buf symbol to read is just before the last * code is written. At that time, 31*(n - 2) bits have been written, just * after 24*(n - 2) bits have been consumed from sym_buf. sym_buf starts at * 8*n bits into pending_buf. (Note that the symbol buffer fills when n - 1 * symbols are written.) The closest the writing gets to what is unread is * then n + 14 bits. Here n is lit_bufsize, which is 16384 by default, and * can range from 128 to 32768. * * Therefore, at a minimum, there are 142 bits of space between what is * written and what is read in the overlain buffers, so the symbols cannot * be overwritten by the compressed data. That space is actually 139 bits, * due to the three-bit fixed-code block header. * * That covers the case where either Z_FIXED is specified, forcing fixed * codes, or when the use of fixed codes is chosen, because that choice * results in a smaller compressed block than dynamic codes. That latter * condition then assures that the above analysis also covers all dynamic * blocks. A dynamic-code block will only be chosen to be emitted if it has * fewer bits than a fixed-code block would for the same set of symbols. * Therefore its average symbol length is assured to be less than 31. So * the compressed data for a dynamic block also cannot overwrite the * symbols from which it is being constructed. */ s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, LIT_BUFS); s->pending_buf_size = (ulg)s->lit_bufsize * 4; if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || s->pending_buf == Z_NULL) { s->status = FINISH_STATE; strm->msg = ERR_MSG(Z_MEM_ERROR); deflateEnd (strm); return Z_MEM_ERROR; } #ifdef LIT_MEM s->d_buf = (ushf *)(s->pending_buf + (s->lit_bufsize << 1)); s->l_buf = s->pending_buf + (s->lit_bufsize << 2); s->sym_end = s->lit_bufsize - 1; #else s->sym_buf = s->pending_buf + s->lit_bufsize; s->sym_end = (s->lit_bufsize - 1) * 3; #endif /* We avoid equality with lit_bufsize*3 because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. */ s->level = level; s->strategy = strategy; s->method = (Byte)method; return deflateReset(strm); } /* ========================================================================= * Check for a valid deflate stream state. Return 0 if ok, 1 if not. */ local int deflateStateCheck(z_streamp strm) { deflate_state *s; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) return 1; s = strm->state; if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE && #ifdef GZIP s->status != GZIP_STATE && #endif s->status != EXTRA_STATE && s->status != NAME_STATE && s->status != COMMENT_STATE && s->status != HCRC_STATE && s->status != BUSY_STATE && s->status != FINISH_STATE)) return 1; return 0; } /* ========================================================================= */ int ZEXPORT deflateSetDictionary(z_streamp strm, const Bytef *dictionary, uInt dictLength) { deflate_state *s; uInt str, n; int wrap; unsigned avail; z_const unsigned char *next; if (deflateStateCheck(strm) || dictionary == Z_NULL) return Z_STREAM_ERROR; s = strm->state; wrap = s->wrap; if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) return Z_STREAM_ERROR; /* when using zlib wrappers, compute Adler-32 for provided dictionary */ if (wrap == 1) strm->adler = adler32(strm->adler, dictionary, dictLength); s->wrap = 0; /* avoid computing Adler-32 in read_buf */ /* if dictionary would fill window, just replace the history */ if (dictLength >= s->w_size) { if (wrap == 0) { /* already empty otherwise */ CLEAR_HASH(s); s->strstart = 0; s->block_start = 0L; s->insert = 0; } dictionary += dictLength - s->w_size; /* use the tail */ dictLength = s->w_size; } /* insert dictionary into window and hash */ avail = strm->avail_in; next = strm->next_in; strm->avail_in = dictLength; strm->next_in = (z_const Bytef *)dictionary; fill_window(s); while (s->lookahead >= MIN_MATCH) { str = s->strstart; n = s->lookahead - (MIN_MATCH-1); do { UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); #ifndef FASTEST s->prev[str & s->w_mask] = s->head[s->ins_h]; #endif s->head[s->ins_h] = (Pos)str; str++; } while (--n); s->strstart = str; s->lookahead = MIN_MATCH-1; fill_window(s); } s->strstart += s->lookahead; s->block_start = (long)s->strstart; s->insert = s->lookahead; s->lookahead = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; strm->next_in = next; strm->avail_in = avail; s->wrap = wrap; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateGetDictionary(z_streamp strm, Bytef *dictionary, uInt *dictLength) { deflate_state *s; uInt len; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; len = s->strstart + s->lookahead; if (len > s->w_size) len = s->w_size; if (dictionary != Z_NULL && len) zmemcpy(dictionary, s->window + s->strstart + s->lookahead - len, len); if (dictLength != Z_NULL) *dictLength = len; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateResetKeep(z_streamp strm) { deflate_state *s; if (deflateStateCheck(strm)) { return Z_STREAM_ERROR; } strm->total_in = strm->total_out = 0; strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ strm->data_type = Z_UNKNOWN; s = (deflate_state *)strm->state; s->pending = 0; s->pending_out = s->pending_buf; if (s->wrap < 0) { s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ } s->status = #ifdef GZIP s->wrap == 2 ? GZIP_STATE : #endif INIT_STATE; strm->adler = #ifdef GZIP s->wrap == 2 ? crc32(0L, Z_NULL, 0) : #endif adler32(0L, Z_NULL, 0); s->last_flush = -2; _tr_init(s); return Z_OK; } /* =========================================================================== * Initialize the "longest match" routines for a new zlib stream */ local void lm_init(deflate_state *s) { s->window_size = (ulg)2L*s->w_size; CLEAR_HASH(s); /* Set the default configuration parameters: */ s->max_lazy_match = configuration_table[s->level].max_lazy; s->good_match = configuration_table[s->level].good_length; s->nice_match = configuration_table[s->level].nice_length; s->max_chain_length = configuration_table[s->level].max_chain; s->strstart = 0; s->block_start = 0L; s->lookahead = 0; s->insert = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; s->ins_h = 0; } /* ========================================================================= */ int ZEXPORT deflateReset(z_streamp strm) { int ret; ret = deflateResetKeep(strm); if (ret == Z_OK) lm_init(strm->state); return ret; } /* ========================================================================= */ int ZEXPORT deflateSetHeader(z_streamp strm, gz_headerp head) { if (deflateStateCheck(strm) || strm->state->wrap != 2) return Z_STREAM_ERROR; strm->state->gzhead = head; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflatePending(z_streamp strm, unsigned *pending, int *bits) { if (deflateStateCheck(strm)) return Z_STREAM_ERROR; if (pending != Z_NULL) *pending = strm->state->pending; if (bits != Z_NULL) *bits = strm->state->bi_valid; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflatePrime(z_streamp strm, int bits, int value) { deflate_state *s; int put; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; #ifdef LIT_MEM if (bits < 0 || bits > 16 || (uchf *)s->d_buf < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; #else if (bits < 0 || bits > 16 || s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; #endif do { put = Buf_size - s->bi_valid; if (put > bits) put = bits; s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); s->bi_valid += put; _tr_flush_bits(s); value >>= put; bits -= put; } while (bits); return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateParams(z_streamp strm, int level, int strategy) { deflate_state *s; compress_func func; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; #ifdef FASTEST if (level != 0) level = 1; #else if (level == Z_DEFAULT_COMPRESSION) level = 6; #endif if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { return Z_STREAM_ERROR; } func = configuration_table[s->level].func; if ((strategy != s->strategy || func != configuration_table[level].func) && s->last_flush != -2) { /* Flush the last buffer: */ int err = deflate(strm, Z_BLOCK); if (err == Z_STREAM_ERROR) return err; if (strm->avail_in || (s->strstart - s->block_start) + s->lookahead) return Z_BUF_ERROR; } if (s->level != level) { if (s->level == 0 && s->matches != 0) { if (s->matches == 1) slide_hash(s); else CLEAR_HASH(s); s->matches = 0; } s->level = level; s->max_lazy_match = configuration_table[level].max_lazy; s->good_match = configuration_table[level].good_length; s->nice_match = configuration_table[level].nice_length; s->max_chain_length = configuration_table[level].max_chain; } s->strategy = strategy; return Z_OK; } /* ========================================================================= */ int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy, int nice_length, int max_chain) { deflate_state *s; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; s->good_match = (uInt)good_length; s->max_lazy_match = (uInt)max_lazy; s->nice_match = nice_length; s->max_chain_length = (uInt)max_chain; return Z_OK; } /* ========================================================================= * For the default windowBits of 15 and memLevel of 8, this function returns a * close to exact, as well as small, upper bound on the compressed size. This * is an expansion of ~0.03%, plus a small constant. * * For any setting other than those defaults for windowBits and memLevel, one * of two worst case bounds is returned. This is at most an expansion of ~4% or * ~13%, plus a small constant. * * Both the 0.03% and 4% derive from the overhead of stored blocks. The first * one is for stored blocks of 16383 bytes (memLevel == 8), whereas the second * is for stored blocks of 127 bytes (the worst case memLevel == 1). The * expansion results from five bytes of header for each stored block. * * The larger expansion of 13% results from a window size less than or equal to * the symbols buffer size (windowBits <= memLevel + 7). In that case some of * the data being compressed may have slid out of the sliding window, impeding * a stored block from being emitted. Then the only choice is a fixed or * dynamic block, where a fixed block limits the maximum expansion to 9 bits * per 8-bit byte, plus 10 bits for every block. The smallest block size for * which this can occur is 255 (memLevel == 2). * * Shifts are used to approximate divisions, for speed. */ uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) { deflate_state *s; uLong fixedlen, storelen, wraplen; /* upper bound for fixed blocks with 9-bit literals and length 255 (memLevel == 2, which is the lowest that may not use stored blocks) -- ~13% overhead plus a small constant */ fixedlen = sourceLen + (sourceLen >> 3) + (sourceLen >> 8) + (sourceLen >> 9) + 4; /* upper bound for stored blocks with length 127 (memLevel == 1) -- ~4% overhead plus a small constant */ storelen = sourceLen + (sourceLen >> 5) + (sourceLen >> 7) + (sourceLen >> 11) + 7; /* if can't get parameters, return larger bound plus a zlib wrapper */ if (deflateStateCheck(strm)) return (fixedlen > storelen ? fixedlen : storelen) + 6; /* compute wrapper length */ s = strm->state; switch (s->wrap) { case 0: /* raw deflate */ wraplen = 0; break; case 1: /* zlib wrapper */ wraplen = 6 + (s->strstart ? 4 : 0); break; #ifdef GZIP case 2: /* gzip wrapper */ wraplen = 18; if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ Bytef *str; if (s->gzhead->extra != Z_NULL) wraplen += 2 + s->gzhead->extra_len; str = s->gzhead->name; if (str != Z_NULL) do { wraplen++; } while (*str++); str = s->gzhead->comment; if (str != Z_NULL) do { wraplen++; } while (*str++); if (s->gzhead->hcrc) wraplen += 2; } break; #endif default: /* for compiler happiness */ wraplen = 6; } /* if not default parameters, return one of the conservative bounds */ if (s->w_bits != 15 || s->hash_bits != 8 + 7) return (s->w_bits <= s->hash_bits && s->level ? fixedlen : storelen) + wraplen; /* default settings: return tight bound for that case -- ~0.03% overhead plus a small constant */ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13 - 6 + wraplen; } /* ========================================================================= * Put a short in the pending buffer. The 16-bit value is put in MSB order. * IN assertion: the stream state is correct and there is enough room in * pending_buf. */ local void putShortMSB(deflate_state *s, uInt b) { put_byte(s, (Byte)(b >> 8)); put_byte(s, (Byte)(b & 0xff)); } /* ========================================================================= * Flush as much pending output as possible. All deflate() output, except for * some deflate_stored() output, goes through this function so some * applications may wish to modify it to avoid allocating a large * strm->next_out buffer and copying into it. (See also read_buf()). */ local void flush_pending(z_streamp strm) { unsigned len; deflate_state *s = strm->state; _tr_flush_bits(s); len = s->pending; if (len > strm->avail_out) len = strm->avail_out; if (len == 0) return; zmemcpy(strm->next_out, s->pending_out, len); strm->next_out += len; s->pending_out += len; strm->total_out += len; strm->avail_out -= len; s->pending -= len; if (s->pending == 0) { s->pending_out = s->pending_buf; } } /* =========================================================================== * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1]. */ #define HCRC_UPDATE(beg) \ do { \ if (s->gzhead->hcrc && s->pending > (beg)) \ strm->adler = crc32(strm->adler, s->pending_buf + (beg), \ s->pending - (beg)); \ } while (0) /* ========================================================================= */ int ZEXPORT deflate(z_streamp strm, int flush) { int old_flush; /* value of flush param for previous deflate call */ deflate_state *s; if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) { return Z_STREAM_ERROR; } s = strm->state; if (strm->next_out == Z_NULL || (strm->avail_in != 0 && strm->next_in == Z_NULL) || (s->status == FINISH_STATE && flush != Z_FINISH)) { ERR_RETURN(strm, Z_STREAM_ERROR); } if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); old_flush = s->last_flush; s->last_flush = flush; /* Flush as much pending output as possible */ if (s->pending != 0) { flush_pending(strm); if (strm->avail_out == 0) { /* Since avail_out is 0, deflate will be called again with * more output space, but possibly with both pending and * avail_in equal to zero. There won't be anything to do, * but this is not an error situation so make sure we * return OK instead of BUF_ERROR at next call of deflate: */ s->last_flush = -1; return Z_OK; } /* Make sure there is something to do and avoid duplicate consecutive * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUF_ERROR. */ } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && flush != Z_FINISH) { ERR_RETURN(strm, Z_BUF_ERROR); } /* User must not provide more input after the first FINISH: */ if (s->status == FINISH_STATE && strm->avail_in != 0) { ERR_RETURN(strm, Z_BUF_ERROR); } /* Write the header */ if (s->status == INIT_STATE && s->wrap == 0) s->status = BUSY_STATE; if (s->status == INIT_STATE) { /* zlib header */ uInt header = (Z_DEFLATED + ((s->w_bits - 8) << 4)) << 8; uInt level_flags; if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) level_flags = 0; else if (s->level < 6) level_flags = 1; else if (s->level == 6) level_flags = 2; else level_flags = 3; header |= (level_flags << 6); if (s->strstart != 0) header |= PRESET_DICT; header += 31 - (header % 31); putShortMSB(s, header); /* Save the adler32 of the preset dictionary: */ if (s->strstart != 0) { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } strm->adler = adler32(0L, Z_NULL, 0); s->status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } #ifdef GZIP if (s->status == GZIP_STATE) { /* gzip header */ strm->adler = crc32(0L, Z_NULL, 0); put_byte(s, 31); put_byte(s, 139); put_byte(s, 8); if (s->gzhead == Z_NULL) { put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); put_byte(s, s->level == 9 ? 2 : (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, OS_CODE); s->status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } else { put_byte(s, (s->gzhead->text ? 1 : 0) + (s->gzhead->hcrc ? 2 : 0) + (s->gzhead->extra == Z_NULL ? 0 : 4) + (s->gzhead->name == Z_NULL ? 0 : 8) + (s->gzhead->comment == Z_NULL ? 0 : 16) ); put_byte(s, (Byte)(s->gzhead->time & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); put_byte(s, s->level == 9 ? 2 : (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, s->gzhead->os & 0xff); if (s->gzhead->extra != Z_NULL) { put_byte(s, s->gzhead->extra_len & 0xff); put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); } if (s->gzhead->hcrc) strm->adler = crc32(strm->adler, s->pending_buf, s->pending); s->gzindex = 0; s->status = EXTRA_STATE; } } if (s->status == EXTRA_STATE) { if (s->gzhead->extra != Z_NULL) { ulg beg = s->pending; /* start of bytes to update crc */ uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex; while (s->pending + left > s->pending_buf_size) { uInt copy = s->pending_buf_size - s->pending; zmemcpy(s->pending_buf + s->pending, s->gzhead->extra + s->gzindex, copy); s->pending = s->pending_buf_size; HCRC_UPDATE(beg); s->gzindex += copy; flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } beg = 0; left -= copy; } zmemcpy(s->pending_buf + s->pending, s->gzhead->extra + s->gzindex, left); s->pending += left; HCRC_UPDATE(beg); s->gzindex = 0; } s->status = NAME_STATE; } if (s->status == NAME_STATE) { if (s->gzhead->name != Z_NULL) { ulg beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { HCRC_UPDATE(beg); flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } beg = 0; } val = s->gzhead->name[s->gzindex++]; put_byte(s, val); } while (val != 0); HCRC_UPDATE(beg); s->gzindex = 0; } s->status = COMMENT_STATE; } if (s->status == COMMENT_STATE) { if (s->gzhead->comment != Z_NULL) { ulg beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { HCRC_UPDATE(beg); flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } beg = 0; } val = s->gzhead->comment[s->gzindex++]; put_byte(s, val); } while (val != 0); HCRC_UPDATE(beg); } s->status = HCRC_STATE; } if (s->status == HCRC_STATE) { if (s->gzhead->hcrc) { if (s->pending + 2 > s->pending_buf_size) { flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } put_byte(s, (Byte)(strm->adler & 0xff)); put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); strm->adler = crc32(0L, Z_NULL, 0); } s->status = BUSY_STATE; /* Compression must start with an empty pending buffer */ flush_pending(strm); if (s->pending != 0) { s->last_flush = -1; return Z_OK; } } #endif /* Start a new block or continue the current one. */ if (strm->avail_in != 0 || s->lookahead != 0 || (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { block_state bstate; bstate = s->level == 0 ? deflate_stored(s, flush) : s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : s->strategy == Z_RLE ? deflate_rle(s, flush) : (*(configuration_table[s->level].func))(s, flush); if (bstate == finish_started || bstate == finish_done) { s->status = FINISH_STATE; } if (bstate == need_more || bstate == finish_started) { if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ } return Z_OK; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don't have to output an * empty block here, this will be done at next call. This also * ensures that for a very small output buffer, we emit at most * one empty block. */ } if (bstate == block_done) { if (flush == Z_PARTIAL_FLUSH) { _tr_align(s); } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ _tr_stored_block(s, (char*)0, 0L, 0); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush == Z_FULL_FLUSH) { CLEAR_HASH(s); /* forget history */ if (s->lookahead == 0) { s->strstart = 0; s->block_start = 0L; s->insert = 0; } } } flush_pending(strm); if (strm->avail_out == 0) { s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ return Z_OK; } } } if (flush != Z_FINISH) return Z_OK; if (s->wrap <= 0) return Z_STREAM_END; /* Write the trailer */ #ifdef GZIP if (s->wrap == 2) { put_byte(s, (Byte)(strm->adler & 0xff)); put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); put_byte(s, (Byte)(strm->total_in & 0xff)); put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); } else #endif { putShortMSB(s, (uInt)(strm->adler >> 16)); putShortMSB(s, (uInt)(strm->adler & 0xffff)); } flush_pending(strm); /* If avail_out is zero, the application will call deflate again * to flush the rest. */ if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ return s->pending != 0 ? Z_OK : Z_STREAM_END; } /* ========================================================================= */ int ZEXPORT deflateEnd(z_streamp strm) { int status; if (deflateStateCheck(strm)) return Z_STREAM_ERROR; status = strm->state->status; /* Deallocate in reverse order of allocations: */ TRY_FREE(strm, strm->state->pending_buf); TRY_FREE(strm, strm->state->head); TRY_FREE(strm, strm->state->prev); TRY_FREE(strm, strm->state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; } /* ========================================================================= * Copy the source state to the destination state. * To simplify the source, this is not supported for 16-bit MSDOS (which * doesn't have enough memory anyway to duplicate compression states). */ int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) { #ifdef MAXSEG_64K (void)dest; (void)source; return Z_STREAM_ERROR; #else deflate_state *ds; deflate_state *ss; if (deflateStateCheck(source) || dest == Z_NULL) { return Z_STREAM_ERROR; } ss = source->state; zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); if (ds == Z_NULL) return Z_MEM_ERROR; dest->state = (struct internal_state FAR *) ds; zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); ds->strm = dest; ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, LIT_BUFS); if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || ds->pending_buf == Z_NULL) { deflateEnd (dest); return Z_MEM_ERROR; } /* following zmemcpy do not work for 16-bit MSDOS */ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); zmemcpy(ds->pending_buf, ss->pending_buf, ds->lit_bufsize * LIT_BUFS); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); #ifdef LIT_MEM ds->d_buf = (ushf *)(ds->pending_buf + (ds->lit_bufsize << 1)); ds->l_buf = ds->pending_buf + (ds->lit_bufsize << 2); #else ds->sym_buf = ds->pending_buf + ds->lit_bufsize; #endif ds->l_desc.dyn_tree = ds->dyn_ltree; ds->d_desc.dyn_tree = ds->dyn_dtree; ds->bl_desc.dyn_tree = ds->bl_tree; return Z_OK; #endif /* MAXSEG_64K */ } #ifndef FASTEST /* =========================================================================== * Set match_start to the longest match starting at the given string and * return its length. Matches shorter or equal to prev_length are discarded, * in which case the result is equal to prev_length and match_start is * garbage. * IN assertions: cur_match is the head of the hash chain for the current * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 * OUT assertion: the match length is not greater than s->lookahead. */ local uInt longest_match(deflate_state *s, IPos cur_match) { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ int best_len = (int)s->prev_length; /* best match length so far */ int nice_match = s->nice_match; /* stop if match long enough */ IPos limit = s->strstart > (IPos)MAX_DIST(s) ? s->strstart - (IPos)MAX_DIST(s) : NIL; /* Stop when cur_match becomes <= limit. To simplify the code, * we prevent matches with the string of window index 0. */ Posf *prev = s->prev; uInt wmask = s->w_mask; #ifdef UNALIGNED_OK /* Compare two bytes at a time. Note: this is not always beneficial. * Try with and without -DUNALIGNED_OK to check. */ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; register ush scan_start = *(ushf*)scan; register ush scan_end = *(ushf*)(scan + best_len - 1); #else register Bytef *strend = s->window + s->strstart + MAX_MATCH; register Byte scan_end1 = scan[best_len - 1]; register Byte scan_end = scan[best_len]; #endif /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); /* Do not waste too much time if we already have a good match: */ if (s->prev_length >= s->good_match) { chain_length >>= 2; } /* Do not look for matches beyond the end of the input. This is necessary * to make deflate deterministic. */ if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead; Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, "need lookahead"); do { Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Skip to next match if the match length cannot increase * or if the match length is less than 2. Note that the checks below * for insufficient lookahead only occur occasionally for performance * reasons. Therefore uninitialized memory will be accessed, and * conditional jumps will be made that depend on those values. * However the length of the match is limited to the lookahead, so * the output of deflate is not affected by the uninitialized values. */ #if (defined(UNALIGNED_OK) && MAX_MATCH == 258) /* This code assumes sizeof(unsigned short) == 2. Do not use * UNALIGNED_OK if your compiler uses a different size. */ if (*(ushf*)(match + best_len - 1) != scan_end || *(ushf*)match != scan_start) continue; /* It is not necessary to compare scan[2] and match[2] since they are * always equal when the other bytes match, given that the hash keys * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at * strstart + 3, + 5, up to strstart + 257. We check for insufficient * lookahead only every 4th comparison; the 128th check will be made * at strstart + 257. If MAX_MATCH-2 is not a multiple of 8, it is * necessary to put more guard bytes at the end of the window, or * to check more often for insufficient lookahead. */ Assert(scan[2] == match[2], "scan[2]?"); scan++, match++; do { } while (*(ushf*)(scan += 2) == *(ushf*)(match += 2) && *(ushf*)(scan += 2) == *(ushf*)(match += 2) && *(ushf*)(scan += 2) == *(ushf*)(match += 2) && *(ushf*)(scan += 2) == *(ushf*)(match += 2) && scan < strend); /* The funny "do {}" generates better code on most compilers */ /* Here, scan <= window + strstart + 257 */ Assert(scan <= s->window + (unsigned)(s->window_size - 1), "wild scan"); if (*scan == *match) scan++; len = (MAX_MATCH - 1) - (int)(strend - scan); scan = strend - (MAX_MATCH-1); #else /* UNALIGNED_OK */ if (match[best_len] != scan_end || match[best_len - 1] != scan_end1 || *match != *scan || *++match != scan[1]) continue; /* The check at best_len - 1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match++; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart + 258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window + (unsigned)(s->window_size - 1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); scan = strend - MAX_MATCH; #endif /* UNALIGNED_OK */ if (len > best_len) { s->match_start = cur_match; best_len = len; if (len >= nice_match) break; #ifdef UNALIGNED_OK scan_end = *(ushf*)(scan + best_len - 1); #else scan_end1 = scan[best_len - 1]; scan_end = scan[best_len]; #endif } } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length != 0); if ((uInt)best_len <= s->lookahead) return (uInt)best_len; return s->lookahead; } #else /* FASTEST */ /* --------------------------------------------------------------------------- * Optimized version for FASTEST only */ local uInt longest_match(deflate_state *s, IPos cur_match) { register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ register Bytef *strend = s->window + s->strstart + MAX_MATCH; /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, "need lookahead"); Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; /* Return failure if the match length is less than 2: */ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; /* The check at best_len - 1 can be removed because it will be made * again later. (This heuristic is not always a win.) * It is not necessary to compare scan[2] and match[2] since they * are always equal when the other bytes match, given that * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match += 2; Assert(*scan == *match, "match[2]?"); /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart + 258. */ do { } while (*++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && *++scan == *++match && scan < strend); Assert(scan <= s->window + (unsigned)(s->window_size - 1), "wild scan"); len = MAX_MATCH - (int)(strend - scan); if (len < MIN_MATCH) return MIN_MATCH - 1; s->match_start = cur_match; return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; } #endif /* FASTEST */ #ifdef ZLIB_DEBUG #define EQUAL 0 /* result of memcmp for equal strings */ /* =========================================================================== * Check that the match at match_start is indeed a match. */ local void check_match(deflate_state *s, IPos start, IPos match, int length) { /* check that the match is indeed a match */ Bytef *back = s->window + (int)match, *here = s->window + start; IPos len = length; if (match == (IPos)-1) { /* match starts one byte before the current window -- just compare the subsequent length-1 bytes */ back++; here++; len--; } if (zmemcmp(back, here, len) != EQUAL) { fprintf(stderr, " start %u, match %d, length %d\n", start, (int)match, length); do { fprintf(stderr, "(%02x %02x)", *back++, *here++); } while (--len != 0); z_error("invalid match"); } if (z_verbose > 1) { fprintf(stderr,"\\[%d,%d]", start - match, length); do { putc(s->window[start++], stderr); } while (--length != 0); } } #else # define check_match(s, start, match, length) #endif /* ZLIB_DEBUG */ /* =========================================================================== * Flush the current block, with given end-of-file flag. * IN assertion: strstart is set to the end of the current match. */ #define FLUSH_BLOCK_ONLY(s, last) { \ _tr_flush_block(s, (s->block_start >= 0L ? \ (charf *)&s->window[(unsigned)s->block_start] : \ (charf *)Z_NULL), \ (ulg)((long)s->strstart - s->block_start), \ (last)); \ s->block_start = s->strstart; \ flush_pending(s->strm); \ Tracev((stderr,"[FLUSH]")); \ } /* Same but force premature exit if necessary. */ #define FLUSH_BLOCK(s, last) { \ FLUSH_BLOCK_ONLY(s, last); \ if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ } /* Maximum stored block length in deflate format (not including header). */ #define MAX_STORED 65535 /* Minimum of a and b. */ #define MIN(a, b) ((a) > (b) ? (b) : (a)) /* =========================================================================== * Copy without compression as much as possible from the input stream, return * the current block state. * * In case deflateParams() is used to later switch to a non-zero compression * level, s->matches (otherwise unused when storing) keeps track of the number * of hash table slides to perform. If s->matches is 1, then one hash table * slide will be done when switching. If s->matches is 2, the maximum value * allowed here, then the hash table will be cleared, since two or more slides * is the same as a clear. * * deflate_stored() is written to minimize the number of times an input byte is * copied. It is most efficient with large input and output buffers, which * maximizes the opportunities to have a single copy from next_in to next_out. */ local block_state deflate_stored(deflate_state *s, int flush) { /* Smallest worthy block size when not flushing or finishing. By default * this is 32K. This can be as small as 507 bytes for memLevel == 1. For * large input and output buffers, the stored block size will be larger. */ unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size); /* Copy as many min_block or larger stored blocks directly to next_out as * possible. If flushing, copy the remaining available input to next_out as * stored blocks, if there is enough space. */ unsigned len, left, have, last = 0; unsigned used = s->strm->avail_in; do { /* Set len to the maximum size block that we can copy directly with the * available input data and output space. Set left to how much of that * would be copied from what's left in the window. */ len = MAX_STORED; /* maximum deflate stored block length */ have = (s->bi_valid + 42) >> 3; /* number of header bytes */ if (s->strm->avail_out < have) /* need room for header */ break; /* maximum stored block length that will fit in avail_out: */ have = s->strm->avail_out - have; left = s->strstart - s->block_start; /* bytes left in window */ if (len > (ulg)left + s->strm->avail_in) len = left + s->strm->avail_in; /* limit len to the input */ if (len > have) len = have; /* limit len to the output */ /* If the stored block would be less than min_block in length, or if * unable to copy all of the available input when flushing, then try * copying to the window and the pending buffer instead. Also don't * write an empty block when flushing -- deflate() does that. */ if (len < min_block && ((len == 0 && flush != Z_FINISH) || flush == Z_NO_FLUSH || len != left + s->strm->avail_in)) break; /* Make a dummy stored block in pending to get the header bytes, * including any pending bits. This also updates the debugging counts. */ last = flush == Z_FINISH && len == left + s->strm->avail_in ? 1 : 0; _tr_stored_block(s, (char *)0, 0L, last); /* Replace the lengths in the dummy stored block with len. */ s->pending_buf[s->pending - 4] = len; s->pending_buf[s->pending - 3] = len >> 8; s->pending_buf[s->pending - 2] = ~len; s->pending_buf[s->pending - 1] = ~len >> 8; /* Write the stored block header bytes. */ flush_pending(s->strm); #ifdef ZLIB_DEBUG /* Update debugging counts for the data about to be copied. */ s->compressed_len += len << 3; s->bits_sent += len << 3; #endif /* Copy uncompressed bytes from the window to next_out. */ if (left) { if (left > len) left = len; zmemcpy(s->strm->next_out, s->window + s->block_start, left); s->strm->next_out += left; s->strm->avail_out -= left; s->strm->total_out += left; s->block_start += left; len -= left; } /* Copy uncompressed bytes directly from next_in to next_out, updating * the check value. */ if (len) { read_buf(s->strm, s->strm->next_out, len); s->strm->next_out += len; s->strm->avail_out -= len; s->strm->total_out += len; } } while (last == 0); /* Update the sliding window with the last s->w_size bytes of the copied * data, or append all of the copied data to the existing window if less * than s->w_size bytes were copied. Also update the number of bytes to * insert in the hash tables, in the event that deflateParams() switches to * a non-zero compression level. */ used -= s->strm->avail_in; /* number of input bytes directly copied */ if (used) { /* If any input was used, then no unused input remains in the window, * therefore s->block_start == s->strstart. */ if (used >= s->w_size) { /* supplant the previous history */ s->matches = 2; /* clear hash */ zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size); s->strstart = s->w_size; s->insert = s->strstart; } else { if (s->window_size - s->strstart <= used) { /* Slide the window down. */ s->strstart -= s->w_size; zmemcpy(s->window, s->window + s->w_size, s->strstart); if (s->matches < 2) s->matches++; /* add a pending slide_hash() */ if (s->insert > s->strstart) s->insert = s->strstart; } zmemcpy(s->window + s->strstart, s->strm->next_in - used, used); s->strstart += used; s->insert += MIN(used, s->w_size - s->insert); } s->block_start = s->strstart; } if (s->high_water < s->strstart) s->high_water = s->strstart; /* If the last block was written to next_out, then done. */ if (last) return finish_done; /* If flushing and all input has been consumed, then done. */ if (flush != Z_NO_FLUSH && flush != Z_FINISH && s->strm->avail_in == 0 && (long)s->strstart == s->block_start) return block_done; /* Fill the window with any remaining input. */ have = s->window_size - s->strstart; if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) { /* Slide the window down. */ s->block_start -= s->w_size; s->strstart -= s->w_size; zmemcpy(s->window, s->window + s->w_size, s->strstart); if (s->matches < 2) s->matches++; /* add a pending slide_hash() */ have += s->w_size; /* more space now */ if (s->insert > s->strstart) s->insert = s->strstart; } if (have > s->strm->avail_in) have = s->strm->avail_in; if (have) { read_buf(s->strm, s->window + s->strstart, have); s->strstart += have; s->insert += MIN(have, s->w_size - s->insert); } if (s->high_water < s->strstart) s->high_water = s->strstart; /* There was not enough avail_out to write a complete worthy or flushed * stored block to next_out. Write a stored block to pending instead, if we * have enough input for a worthy block, or if flushing and there is enough * room for the remaining input as a stored block in the pending buffer. */ have = (s->bi_valid + 42) >> 3; /* number of header bytes */ /* maximum stored block length that will fit in pending: */ have = MIN(s->pending_buf_size - have, MAX_STORED); min_block = MIN(have, s->w_size); left = s->strstart - s->block_start; if (left >= min_block || ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH && s->strm->avail_in == 0 && left <= have)) { len = MIN(left, have); last = flush == Z_FINISH && s->strm->avail_in == 0 && len == left ? 1 : 0; _tr_stored_block(s, (charf *)s->window + s->block_start, len, last); s->block_start += len; flush_pending(s->strm); } /* We've done all we can with the available input and output. */ return last ? finish_started : need_more; } /* =========================================================================== * Compress as much as possible from the input stream, return the current * block state. * This function does not perform lazy evaluation of matches and inserts * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ local block_state deflate_fast(deflate_state *s, int flush) { IPos hash_head; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart + 2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. * At this point we have always match_length < MIN_MATCH */ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s->match_length = longest_match (s, hash_head); /* longest_match() sets match_start */ } if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->match_start, s->match_length); _tr_tally_dist(s, s->strstart - s->match_start, s->match_length - MIN_MATCH, bflush); s->lookahead -= s->match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ #ifndef FASTEST if (s->match_length <= s->max_insert_length && s->lookahead >= MIN_MATCH) { s->match_length--; /* string at strstart already in table */ do { s->strstart++; INSERT_STRING(s, s->strstart, hash_head); /* strstart never exceeds WSIZE-MAX_MATCH, so there are * always MIN_MATCH bytes ahead. */ } while (--s->match_length != 0); s->strstart++; } else #endif { s->strstart += s->match_length; s->match_length = 0; s->ins_h = s->window[s->strstart]; UPDATE_HASH(s, s->ins_h, s->window[s->strstart + 1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not * matter since it will be recomputed at next deflate call. */ } } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit(s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->sym_next) FLUSH_BLOCK(s, 0); return block_done; } #ifndef FASTEST /* =========================================================================== * Same as above, but achieves better compression. We use a lazy * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ local block_state deflate_slow(deflate_state *s, int flush) { IPos hash_head; /* head of hash chain */ int bflush; /* set if current block must be flushed */ /* Process the input block. */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the next match, plus MIN_MATCH bytes to insert the * string following the next match. */ if (s->lookahead < MIN_LOOKAHEAD) { fill_window(s); if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* Insert the string window[strstart .. strstart + 2] in the * dictionary, and set hash_head to the head of the hash chain: */ hash_head = NIL; if (s->lookahead >= MIN_MATCH) { INSERT_STRING(s, s->strstart, hash_head); } /* Find the longest match, discarding those <= prev_length. */ s->prev_length = s->match_length, s->prev_match = s->match_start; s->match_length = MIN_MATCH-1; if (hash_head != NIL && s->prev_length < s->max_lazy_match && s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ s->match_length = longest_match (s, hash_head); /* longest_match() sets match_start */ if (s->match_length <= 5 && (s->strategy == Z_FILTERED #if TOO_FAR <= 32767 || (s->match_length == MIN_MATCH && s->strstart - s->match_start > TOO_FAR) #endif )) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ s->match_length = MIN_MATCH-1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ check_match(s, s->strstart - 1, s->prev_match, s->prev_length); _tr_tally_dist(s, s->strstart - 1 - s->prev_match, s->prev_length - MIN_MATCH, bflush); /* Insert in hash table all strings up to the end of the match. * strstart - 1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in * the hash table. */ s->lookahead -= s->prev_length - 1; s->prev_length -= 2; do { if (++s->strstart <= max_insert) { INSERT_STRING(s, s->strstart, hash_head); } } while (--s->prev_length != 0); s->match_available = 0; s->match_length = MIN_MATCH-1; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } else if (s->match_available) { /* If there was no match at the previous position, output a * single literal. If there was a match but the current match * is longer, truncate the previous match to a single literal. */ Tracevv((stderr,"%c", s->window[s->strstart - 1])); _tr_tally_lit(s, s->window[s->strstart - 1], bflush); if (bflush) { FLUSH_BLOCK_ONLY(s, 0); } s->strstart++; s->lookahead--; if (s->strm->avail_out == 0) return need_more; } else { /* There is no previous match to compare with, wait for * the next step to decide. */ s->match_available = 1; s->strstart++; s->lookahead--; } } Assert (flush != Z_NO_FLUSH, "no flush?"); if (s->match_available) { Tracevv((stderr,"%c", s->window[s->strstart - 1])); _tr_tally_lit(s, s->window[s->strstart - 1], bflush); s->match_available = 0; } s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->sym_next) FLUSH_BLOCK(s, 0); return block_done; } #endif /* FASTEST */ /* =========================================================================== * For Z_RLE, simply look for runs of bytes, generate matches only of distance * one. Do not maintain a hash table. (It will be regenerated if this run of * deflate switches away from Z_RLE.) */ local block_state deflate_rle(deflate_state *s, int flush) { int bflush; /* set if current block must be flushed */ uInt prev; /* byte at distance one to match */ Bytef *scan, *strend; /* scan goes up to strend for length of run */ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes * for the longest run, plus one for the unrolled loop. */ if (s->lookahead <= MAX_MATCH) { fill_window(s); if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ } /* See how many times the previous byte repeats */ s->match_length = 0; if (s->lookahead >= MIN_MATCH && s->strstart > 0) { scan = s->window + s->strstart - 1; prev = *scan; if (prev == *++scan && prev == *++scan && prev == *++scan) { strend = s->window + s->strstart + MAX_MATCH; do { } while (prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && prev == *++scan && scan < strend); s->match_length = MAX_MATCH - (uInt)(strend - scan); if (s->match_length > s->lookahead) s->match_length = s->lookahead; } Assert(scan <= s->window + (uInt)(s->window_size - 1), "wild scan"); } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ if (s->match_length >= MIN_MATCH) { check_match(s, s->strstart, s->strstart - 1, s->match_length); _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); s->lookahead -= s->match_length; s->strstart += s->match_length; s->match_length = 0; } else { /* No match, output a literal byte */ Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit(s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; } if (bflush) FLUSH_BLOCK(s, 0); } s->insert = 0; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->sym_next) FLUSH_BLOCK(s, 0); return block_done; } /* =========================================================================== * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. * (It will be regenerated if this run of deflate switches away from Huffman.) */ local block_state deflate_huff(deflate_state *s, int flush) { int bflush; /* set if current block must be flushed */ for (;;) { /* Make sure that we have a literal to write. */ if (s->lookahead == 0) { fill_window(s); if (s->lookahead == 0) { if (flush == Z_NO_FLUSH) return need_more; break; /* flush the current block */ } } /* Output a literal byte */ s->match_length = 0; Tracevv((stderr,"%c", s->window[s->strstart])); _tr_tally_lit(s, s->window[s->strstart], bflush); s->lookahead--; s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } s->insert = 0; if (flush == Z_FINISH) { FLUSH_BLOCK(s, 1); return finish_done; } if (s->sym_next) FLUSH_BLOCK(s, 0); return block_done; } |
Added compat/zlib/deflate.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* deflate.h -- internal compression state * Copyright (C) 1995-2024 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id$ */ #ifndef DEFLATE_H #define DEFLATE_H #include "zutil.h" /* define NO_GZIP when compiling if you want to disable gzip header and trailer creation by deflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip encoding should be left enabled. */ #ifndef NO_GZIP # define GZIP #endif /* define LIT_MEM to slightly increase the speed of deflate (order 1% to 2%) at the cost of a larger memory footprint */ /* #define LIT_MEM */ /* =========================================================================== * Internal compression state. */ #define LENGTH_CODES 29 /* number of length codes, not counting the special END_BLOCK code */ #define LITERALS 256 /* number of literal bytes 0..255 */ #define L_CODES (LITERALS+1+LENGTH_CODES) /* number of Literal or Length codes, including the END_BLOCK code */ #define D_CODES 30 /* number of distance codes */ #define BL_CODES 19 /* number of codes used to transfer the bit lengths */ #define HEAP_SIZE (2*L_CODES+1) /* maximum heap size */ #define MAX_BITS 15 /* All codes must not exceed MAX_BITS bits */ #define Buf_size 16 /* size of bit buffer in bi_buf */ #define INIT_STATE 42 /* zlib header -> BUSY_STATE */ #ifdef GZIP # define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ #endif #define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ #define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ #define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ #define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ #define BUSY_STATE 113 /* deflate -> FINISH_STATE */ #define FINISH_STATE 666 /* stream complete */ /* Stream status */ /* Data structure describing a single value and its code string. */ typedef struct ct_data_s { union { ush freq; /* frequency count */ ush code; /* bit string */ } fc; union { ush dad; /* father node in Huffman tree */ ush len; /* length of bit string */ } dl; } FAR ct_data; #define Freq fc.freq #define Code fc.code #define Dad dl.dad #define Len dl.len typedef struct static_tree_desc_s static_tree_desc; typedef struct tree_desc_s { ct_data *dyn_tree; /* the dynamic tree */ int max_code; /* largest code with non zero frequency */ const static_tree_desc *stat_desc; /* the corresponding static tree */ } FAR tree_desc; typedef ush Pos; typedef Pos FAR Posf; typedef unsigned IPos; /* A Pos is an index in the character window. We use short instead of int to * save space in the various tables. IPos is used only for parameter passing. */ typedef struct internal_state { z_streamp strm; /* pointer back to this zlib stream */ int status; /* as the name implies */ Bytef *pending_buf; /* output still pending */ ulg pending_buf_size; /* size of pending_buf */ Bytef *pending_out; /* next pending byte to output to the stream */ ulg pending; /* nb of bytes in the pending buffer */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ gz_headerp gzhead; /* gzip header information to write */ ulg gzindex; /* where in extra, name, or comment */ Byte method; /* can only be DEFLATED */ int last_flush; /* value of flush param for previous deflate call */ /* used by deflate.c: */ uInt w_size; /* LZ77 window size (32K by default) */ uInt w_bits; /* log2(w_size) (8..16) */ uInt w_mask; /* w_size - 1 */ Bytef *window; /* Sliding window. Input bytes are read into the second half of the window, * and move to the first half later to keep a dictionary of at least wSize * bytes. With this organization, matches are limited to a distance of * wSize-MAX_MATCH bytes, but this ensures that IO is always * performed with a length multiple of the block size. Also, it limits * the window size to 64K, which is quite useful on MSDOS. * To do: use the user input buffer as sliding window. */ ulg window_size; /* Actual size of window: 2*wSize, except when the user input buffer * is directly used as sliding window. */ Posf *prev; /* Link to older string with same hash index. To limit the size of this * array to 64K, this link is maintained only for the last 32K strings. * An index in this array is thus a window index modulo 32K. */ Posf *head; /* Heads of the hash chains or NIL. */ uInt ins_h; /* hash index of string to be inserted */ uInt hash_size; /* number of elements in hash table */ uInt hash_bits; /* log2(hash_size) */ uInt hash_mask; /* hash_size-1 */ uInt hash_shift; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: * hash_shift * MIN_MATCH >= hash_bits */ long block_start; /* Window position at the beginning of the current output block. Gets * negative when the window is moved backwards. */ uInt match_length; /* length of best match */ IPos prev_match; /* previous match */ int match_available; /* set if previous match exists */ uInt strstart; /* start of string to insert */ uInt match_start; /* start of matching string */ uInt lookahead; /* number of valid bytes ahead in window */ uInt prev_length; /* Length of the best match at previous step. Matches not greater than this * are discarded. This is used in the lazy match evaluation. */ uInt max_chain_length; /* To speed up deflation, hash chains are never searched beyond this * length. A higher limit improves compression ratio but degrades the * speed. */ uInt max_lazy_match; /* Attempt to find a better match only when the current match is strictly * smaller than this value. This mechanism is used only for compression * levels >= 4. */ # define max_insert_length max_lazy_match /* Insert new strings in the hash table only if the match length is not * greater than this length. This saves time but degrades compression. * max_insert_length is used only for compression levels <= 3. */ int level; /* compression level (1..9) */ int strategy; /* favor or force Huffman coding*/ uInt good_match; /* Use a faster search when the previous match is longer than this */ int nice_match; /* Stop searching when current match exceeds this */ /* used by trees.c: */ /* Didn't use ct_data typedef below to suppress compiler warning */ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ struct tree_desc_s l_desc; /* desc. for literal tree */ struct tree_desc_s d_desc; /* desc. for distance tree */ struct tree_desc_s bl_desc; /* desc. for bit length tree */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ int heap_len; /* number of elements in the heap */ int heap_max; /* element of largest frequency */ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. * The same heap array is used to build all trees. */ uch depth[2*L_CODES+1]; /* Depth of each subtree used as tie breaker for trees of equal frequency */ #ifdef LIT_MEM # define LIT_BUFS 5 ushf *d_buf; /* buffer for distances */ uchf *l_buf; /* buffer for literals/lengths */ #else # define LIT_BUFS 4 uchf *sym_buf; /* buffer for distances and literals/lengths */ #endif uInt lit_bufsize; /* Size of match buffer for literals/lengths. There are 4 reasons for * limiting lit_bufsize to 64K: * - frequencies can be kept in 16 bit counters * - if compression is not successful for the first block, all input * data is still in the window so we can still emit a stored block even * when input comes from standard input. (This can also be done for * all blocks if lit_bufsize is not greater than 32K.) * - if compression is not successful for a file smaller than 64K, we can * even emit a stored file instead of a stored block (saving 5 bytes). * This is applicable only for zip (not gzip or zlib). * - creating new Huffman trees less frequently may not provide fast * adaptation to changes in the input data statistics. (Take for * example a binary file with poorly compressible code followed by * a highly compressible string table.) Smaller buffer sizes give * fast adaptation but have of course the overhead of transmitting * trees more frequently. * - I can't count above 4 */ uInt sym_next; /* running index in symbol buffer */ uInt sym_end; /* symbol table full when sym_next reaches this */ ulg opt_len; /* bit length of current block with optimal trees */ ulg static_len; /* bit length of current block with static trees */ uInt matches; /* number of string matches in current block */ uInt insert; /* bytes at end of window left to insert */ #ifdef ZLIB_DEBUG ulg compressed_len; /* total bit length of compressed file mod 2^32 */ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ #endif ush bi_buf; /* Output buffer. bits are inserted starting at the bottom (least * significant bits). */ int bi_valid; /* Number of valid bits in bi_buf. All bits above the last valid bit * are always zero. */ ulg high_water; /* High water mark offset in window for initialized bytes -- bytes above * this are set to zero in order to avoid memory check warnings when * longest match routines access bytes past the input. This is then * updated to the new high water mark. */ } FAR deflate_state; /* Output a byte on the stream. * IN assertion: there is enough room in pending_buf. */ #define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);} #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) /* Minimum amount of lookahead, except at the end of the input file. * See deflate.c for comments about the MIN_MATCH+1. */ #define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) /* In order to simplify the code, particularly on 16 bit machines, match * distances are limited to MAX_DIST instead of WSIZE. */ #define WIN_INIT MAX_MATCH /* Number of bytes after end of data in window to initialize in order to avoid memory checker errors from longest match routines */ /* in trees.c */ void ZLIB_INTERNAL _tr_init(deflate_state *s); int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc); void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, ulg stored_len, int last); void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s); void ZLIB_INTERNAL _tr_align(deflate_state *s); void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, ulg stored_len, int last); #define d_code(dist) \ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) /* Mapping from a distance to a distance code. dist is the distance - 1 and * must not have side effects. _dist_code[256] and _dist_code[257] are never * used. */ #ifndef ZLIB_DEBUG /* Inline versions of _tr_tally for speed: */ #if defined(GEN_TREES_H) || !defined(STDC) extern uch ZLIB_INTERNAL _length_code[]; extern uch ZLIB_INTERNAL _dist_code[]; #else extern const uch ZLIB_INTERNAL _length_code[]; extern const uch ZLIB_INTERNAL _dist_code[]; #endif #ifdef LIT_MEM # define _tr_tally_lit(s, c, flush) \ { uch cc = (c); \ s->d_buf[s->sym_next] = 0; \ s->l_buf[s->sym_next++] = cc; \ s->dyn_ltree[cc].Freq++; \ flush = (s->sym_next == s->sym_end); \ } # define _tr_tally_dist(s, distance, length, flush) \ { uch len = (uch)(length); \ ush dist = (ush)(distance); \ s->d_buf[s->sym_next] = dist; \ s->l_buf[s->sym_next++] = len; \ dist--; \ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ s->dyn_dtree[d_code(dist)].Freq++; \ flush = (s->sym_next == s->sym_end); \ } #else # define _tr_tally_lit(s, c, flush) \ { uch cc = (c); \ s->sym_buf[s->sym_next++] = 0; \ s->sym_buf[s->sym_next++] = 0; \ s->sym_buf[s->sym_next++] = cc; \ s->dyn_ltree[cc].Freq++; \ flush = (s->sym_next == s->sym_end); \ } # define _tr_tally_dist(s, distance, length, flush) \ { uch len = (uch)(length); \ ush dist = (ush)(distance); \ s->sym_buf[s->sym_next++] = (uch)dist; \ s->sym_buf[s->sym_next++] = (uch)(dist >> 8); \ s->sym_buf[s->sym_next++] = len; \ dist--; \ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ s->dyn_dtree[d_code(dist)].Freq++; \ flush = (s->sym_next == s->sym_end); \ } #endif #else # define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) # define _tr_tally_dist(s, distance, length, flush) \ flush = _tr_tally(s, distance, length) #endif #endif /* DEFLATE_H */ |
Added compat/zlib/examples/README.examples.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | This directory contains examples of the use of zlib and other relevant programs and documentation. enough.c calculation and justification of ENOUGH parameter in inftrees.h - calculates the maximum table space used in inflate tree construction over all possible Huffman codes fitblk.c compress just enough input to nearly fill a requested output size - zlib isn't designed to do this, but fitblk does it anyway gun.c uncompress a gzip file - illustrates the use of inflateBack() for high speed file-to-file decompression using call-back functions - is approximately twice as fast as gzip -d - also provides Unix uncompress functionality, again twice as fast gzappend.c append to a gzip file - illustrates the use of the Z_BLOCK flush parameter for inflate() - illustrates the use of deflatePrime() to start at any bit gzjoin.c join gzip files without recalculating the crc or recompressing - illustrates the use of the Z_BLOCK flush parameter for inflate() - illustrates the use of crc32_combine() gzlog.c gzlog.h efficiently and robustly maintain a message log file in gzip format - illustrates use of raw deflate, Z_PARTIAL_FLUSH, deflatePrime(), and deflateSetDictionary() - illustrates use of a gzip header extra field gznorm.c normalize a gzip file by combining members into a single member - demonstrates how to concatenate deflate streams using Z_BLOCK zlib_how.html painfully comprehensive description of zpipe.c (see below) - describes in excruciating detail the use of deflate() and inflate() zpipe.c reads and writes zlib streams from stdin to stdout - illustrates the proper use of deflate() and inflate() - deeply commented in zlib_how.html (see above) zran.c zran.h index a zlib or gzip stream and randomly access it - illustrates the use of Z_BLOCK, inflatePrime(), and inflateSetDictionary() to provide random access |
Added compat/zlib/examples/enough.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 | /* enough.c -- determine the maximum size of inflate's Huffman code tables over * all possible valid and complete prefix codes, subject to a length limit. * Copyright (C) 2007, 2008, 2012, 2018 Mark Adler * Version 1.5 5 August 2018 Mark Adler */ /* Version history: 1.0 3 Jan 2007 First version (derived from codecount.c version 1.4) 1.1 4 Jan 2007 Use faster incremental table usage computation Prune examine() search on previously visited states 1.2 5 Jan 2007 Comments clean up As inflate does, decrease root for short codes Refuse cases where inflate would increase root 1.3 17 Feb 2008 Add argument for initial root table size Fix bug for initial root table size == max - 1 Use a macro to compute the history index 1.4 18 Aug 2012 Avoid shifts more than bits in type (caused endless loop!) Clean up comparisons of different types Clean up code indentation 1.5 5 Aug 2018 Clean up code style, formatting, and comments Show all the codes for the maximum, and only the maximum */ /* Examine all possible prefix codes for a given number of symbols and a maximum code length in bits to determine the maximum table size for zlib's inflate. Only complete prefix codes are counted. Two codes are considered distinct if the vectors of the number of codes per length are not identical. So permutations of the symbol assignments result in the same code for the counting, as do permutations of the assignments of the bit values to the codes (i.e. only canonical codes are counted). We build a code from shorter to longer lengths, determining how many symbols are coded at each length. At each step, we have how many symbols remain to be coded, what the last code length used was, and how many bit patterns of that length remain unused. Then we add one to the code length and double the number of unused patterns to graduate to the next code length. We then assign all portions of the remaining symbols to that code length that preserve the properties of a correct and eventually complete code. Those properties are: we cannot use more bit patterns than are available; and when all the symbols are used, there are exactly zero possible bit patterns left unused. The inflate Huffman decoding algorithm uses two-level lookup tables for speed. There is a single first-level table to decode codes up to root bits in length (root == 9 for literal/length codes and root == 6 for distance codes, in the current inflate implementation). The base table has 1 << root entries and is indexed by the next root bits of input. Codes shorter than root bits have replicated table entries, so that the correct entry is pointed to regardless of the bits that follow the short code. If the code is longer than root bits, then the table entry points to a second-level table. The size of that table is determined by the longest code with that root-bit prefix. If that longest code has length len, then the table has size 1 << (len - root), to index the remaining bits in that set of codes. Each subsequent root-bit prefix then has its own sub-table. The total number of table entries required by the code is calculated incrementally as the number of codes at each bit length is populated. When all of the codes are shorter than root bits, then root is reduced to the longest code length, resulting in a single, smaller, one-level table. The inflate algorithm also provides for small values of root (relative to the log2 of the number of symbols), where the shortest code has more bits than root. In that case, root is increased to the length of the shortest code. This program, by design, does not handle that case, so it is verified that the number of symbols is less than 1 << (root + 1). In order to speed up the examination (by about ten orders of magnitude for the default arguments), the intermediate states in the build-up of a code are remembered and previously visited branches are pruned. The memory required for this will increase rapidly with the total number of symbols and the maximum code length in bits. However this is a very small price to pay for the vast speedup. First, all of the possible prefix codes are counted, and reachable intermediate states are noted by a non-zero count in a saved-results array. Second, the intermediate states that lead to (root + 1) bit or longer codes are used to look at all sub-codes from those junctures for their inflate memory usage. (The amount of memory used is not affected by the number of codes of root bits or less in length.) Third, the visited states in the construction of those sub-codes and the associated calculation of the table size is recalled in order to avoid recalculating from the same juncture. Beginning the code examination at (root + 1) bit codes, which is enabled by identifying the reachable nodes, accounts for about six of the orders of magnitude of improvement for the default arguments. About another four orders of magnitude come from not revisiting previous states. Out of approximately 2x10^16 possible prefix codes, only about 2x10^6 sub-codes need to be examined to cover all of the possible table memory usage cases for the default arguments of 286 symbols limited to 15-bit codes. Note that the uintmax_t type is used for counting. It is quite easy to exceed the capacity of an eight-byte integer with a large number of symbols and a large maximum code length, so multiple-precision arithmetic would need to replace the integer arithmetic in that case. This program will abort if an overflow occurs. The big_t type identifies where the counting takes place. The uintmax_t type is also used for calculating the number of possible codes remaining at the maximum length. This limits the maximum code length to the number of bits in a long long minus the number of bits needed to represent the symbols in a flat code. The code_t type identifies where the bit-pattern counting takes place. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <stdint.h> #include <assert.h> #define local static // Special data types. typedef uintmax_t big_t; // type for code counting #define PRIbig "ju" // printf format for big_t typedef uintmax_t code_t; // type for bit pattern counting struct tab { // type for been-here check size_t len; // allocated length of bit vector in octets char *vec; // allocated bit vector }; /* The array for saving results, num[], is indexed with this triplet: syms: number of symbols remaining to code left: number of available bit patterns at length len len: number of bits in the codes currently being assigned Those indices are constrained thusly when saving results: syms: 3..totsym (totsym == total symbols to code) left: 2..syms - 1, but only the evens (so syms == 8 -> 2, 4, 6) len: 1..max - 1 (max == maximum code length in bits) syms == 2 is not saved since that immediately leads to a single code. left must be even, since it represents the number of available bit patterns at the current length, which is double the number at the previous length. left ends at syms-1 since left == syms immediately results in a single code. (left > sym is not allowed since that would result in an incomplete code.) len is less than max, since the code completes immediately when len == max. The offset into the array is calculated for the three indices with the first one (syms) being outermost, and the last one (len) being innermost. We build the array with length max-1 lists for the len index, with syms-3 of those for each symbol. There are totsym-2 of those, with each one varying in length as a function of sym. See the calculation of index in map() for the index, and the calculation of size in main() for the size of the array. For the deflate example of 286 symbols limited to 15-bit codes, the array has 284,284 entries, taking up 2.17 MB for an 8-byte big_t. More than half of the space allocated for saved results is actually used -- not all possible triplets are reached in the generation of valid prefix codes. */ /* The array for tracking visited states, done[], is itself indexed identically to the num[] array as described above for the (syms, left, len) triplet. Each element in the array is further indexed by the (mem, rem) doublet, where mem is the amount of inflate table space used so far, and rem is the remaining unused entries in the current inflate sub-table. Each indexed element is simply one bit indicating whether the state has been visited or not. Since the ranges for mem and rem are not known a priori, each bit vector is of a variable size, and grows as needed to accommodate the visited states. mem and rem are used to calculate a single index in a triangular array. Since the range of mem is expected in the default case to be about ten times larger than the range of rem, the array is skewed to reduce the memory usage, with eight times the range for mem than for rem. See the calculations for offset and bit in been_here() for the details. For the deflate example of 286 symbols limited to 15-bit codes, the bit vectors grow to total 5.5 MB, in addition to the 4.3 MB done array itself. */ // Type for a variable-length, allocated string. typedef struct { char *str; // pointer to allocated string size_t size; // size of allocation size_t len; // length of string, not including terminating zero } string_t; // Clear a string_t. local void string_clear(string_t *s) { s->str[0] = 0; s->len = 0; } // Initialize a string_t. local void string_init(string_t *s) { s->size = 16; s->str = malloc(s->size); assert(s->str != NULL && "out of memory"); string_clear(s); } // Release the allocation of a string_t. local void string_free(string_t *s) { free(s->str); s->str = NULL; s->size = 0; s->len = 0; } // Save the results of printf with fmt and the subsequent argument list to s. // Each call appends to s. The allocated space for s is increased as needed. local void string_printf(string_t *s, char *fmt, ...) { va_list ap; va_start(ap, fmt); size_t len = s->len; int ret = vsnprintf(s->str + len, s->size - len, fmt, ap); assert(ret >= 0 && "out of memory"); s->len += ret; if (s->size < s->len + 1) { do { s->size <<= 1; assert(s->size != 0 && "overflow"); } while (s->size < s->len + 1); s->str = realloc(s->str, s->size); assert(s->str != NULL && "out of memory"); vsnprintf(s->str + len, s->size - len, fmt, ap); } va_end(ap); } // Globals to avoid propagating constants or constant pointers recursively. struct { int max; // maximum allowed bit length for the codes int root; // size of base code table in bits int large; // largest code table so far size_t size; // number of elements in num and done big_t tot; // total number of codes with maximum tables size string_t out; // display of subcodes for maximum tables size int *code; // number of symbols assigned to each bit length big_t *num; // saved results array for code counting struct tab *done; // states already evaluated array } g; // Index function for num[] and done[]. local inline size_t map(int syms, int left, int len) { return ((size_t)((syms - 1) >> 1) * ((syms - 2) >> 1) + (left >> 1) - 1) * (g.max - 1) + len - 1; } // Free allocated space in globals. local void cleanup(void) { if (g.done != NULL) { for (size_t n = 0; n < g.size; n++) if (g.done[n].len) free(g.done[n].vec); g.size = 0; free(g.done); g.done = NULL; } free(g.num); g.num = NULL; free(g.code); g.code = NULL; string_free(&g.out); } // Return the number of possible prefix codes using bit patterns of lengths len // through max inclusive, coding syms symbols, with left bit patterns of length // len unused -- return -1 if there is an overflow in the counting. Keep a // record of previous results in num to prevent repeating the same calculation. local big_t count(int syms, int left, int len) { // see if only one possible code if (syms == left) return 1; // note and verify the expected state assert(syms > left && left > 0 && len < g.max); // see if we've done this one already size_t index = map(syms, left, len); big_t got = g.num[index]; if (got) return got; // we have -- return the saved result // we need to use at least this many bit patterns so that the code won't be // incomplete at the next length (more bit patterns than symbols) int least = (left << 1) - syms; if (least < 0) least = 0; // we can use at most this many bit patterns, lest there not be enough // available for the remaining symbols at the maximum length (if there were // no limit to the code length, this would become: most = left - 1) int most = (((code_t)left << (g.max - len)) - syms) / (((code_t)1 << (g.max - len)) - 1); // count all possible codes from this juncture and add them up big_t sum = 0; for (int use = least; use <= most; use++) { got = count(syms - use, (left - use) << 1, len + 1); sum += got; if (got == (big_t)-1 || sum < got) // overflow return (big_t)-1; } // verify that all recursive calls are productive assert(sum != 0); // save the result and return it g.num[index] = sum; return sum; } // Return true if we've been here before, set to true if not. Set a bit in a // bit vector to indicate visiting this state. Each (syms,len,left) state has a // variable size bit vector indexed by (mem,rem). The bit vector is lengthened // as needed to allow setting the (mem,rem) bit. local int been_here(int syms, int left, int len, int mem, int rem) { // point to vector for (syms,left,len), bit in vector for (mem,rem) size_t index = map(syms, left, len); mem -= 1 << g.root; // mem always includes the root table mem >>= 1; // mem and rem are always even rem >>= 1; size_t offset = (mem >> 3) + rem; offset = ((offset * (offset + 1)) >> 1) + rem; int bit = 1 << (mem & 7); // see if we've been here size_t length = g.done[index].len; if (offset < length && (g.done[index].vec[offset] & bit) != 0) return 1; // done this! // we haven't been here before -- set the bit to show we have now // see if we need to lengthen the vector in order to set the bit if (length <= offset) { // if we have one already, enlarge it, zero out the appended space char *vector; if (length) { do { length <<= 1; } while (length <= offset); vector = realloc(g.done[index].vec, length); assert(vector != NULL && "out of memory"); memset(vector + g.done[index].len, 0, length - g.done[index].len); } // otherwise we need to make a new vector and zero it out else { length = 16; while (length <= offset) length <<= 1; vector = calloc(length, 1); assert(vector != NULL && "out of memory"); } // install the new vector g.done[index].len = length; g.done[index].vec = vector; } // set the bit g.done[index].vec[offset] |= bit; return 0; } // Examine all possible codes from the given node (syms, len, left). Compute // the amount of memory required to build inflate's decoding tables, where the // number of code structures used so far is mem, and the number remaining in // the current sub-table is rem. local void examine(int syms, int left, int len, int mem, int rem) { // see if we have a complete code if (syms == left) { // set the last code entry g.code[len] = left; // complete computation of memory used by this code while (rem < left) { left -= rem; rem = 1 << (len - g.root); mem += rem; } assert(rem == left); // if this is at the maximum, show the sub-code if (mem >= g.large) { // if this is a new maximum, update the maximum and clear out the // printed sub-codes from the previous maximum if (mem > g.large) { g.large = mem; string_clear(&g.out); } // compute the starting state for this sub-code syms = 0; left = 1 << g.max; for (int bits = g.max; bits > g.root; bits--) { syms += g.code[bits]; left -= g.code[bits]; assert((left & 1) == 0); left >>= 1; } // print the starting state and the resulting sub-code to g.out string_printf(&g.out, "<%u, %u, %u>:", syms, g.root + 1, ((1 << g.root) - left) << 1); for (int bits = g.root + 1; bits <= g.max; bits++) if (g.code[bits]) string_printf(&g.out, " %d[%d]", g.code[bits], bits); string_printf(&g.out, "\n"); } // remove entries as we drop back down in the recursion g.code[len] = 0; return; } // prune the tree if we can if (been_here(syms, left, len, mem, rem)) return; // we need to use at least this many bit patterns so that the code won't be // incomplete at the next length (more bit patterns than symbols) int least = (left << 1) - syms; if (least < 0) least = 0; // we can use at most this many bit patterns, lest there not be enough // available for the remaining symbols at the maximum length (if there were // no limit to the code length, this would become: most = left - 1) int most = (((code_t)left << (g.max - len)) - syms) / (((code_t)1 << (g.max - len)) - 1); // occupy least table spaces, creating new sub-tables as needed int use = least; while (rem < use) { use -= rem; rem = 1 << (len - g.root); mem += rem; } rem -= use; // examine codes from here, updating table space as we go for (use = least; use <= most; use++) { g.code[len] = use; examine(syms - use, (left - use) << 1, len + 1, mem + (rem ? 1 << (len - g.root) : 0), rem << 1); if (rem == 0) { rem = 1 << (len - g.root); mem += rem; } rem--; } // remove entries as we drop back down in the recursion g.code[len] = 0; } // Look at all sub-codes starting with root + 1 bits. Look at only the valid // intermediate code states (syms, left, len). For each completed code, // calculate the amount of memory required by inflate to build the decoding // tables. Find the maximum amount of memory required and show the codes that // require that maximum. local void enough(int syms) { // clear code for (int n = 0; n <= g.max; n++) g.code[n] = 0; // look at all (root + 1) bit and longer codes string_clear(&g.out); // empty saved results g.large = 1 << g.root; // base table if (g.root < g.max) // otherwise, there's only a base table for (int n = 3; n <= syms; n++) for (int left = 2; left < n; left += 2) { // look at all reachable (root + 1) bit nodes, and the // resulting codes (complete at root + 2 or more) size_t index = map(n, left, g.root + 1); if (g.root + 1 < g.max && g.num[index]) // reachable node examine(n, left, g.root + 1, 1 << g.root, 0); // also look at root bit codes with completions at root + 1 // bits (not saved in num, since complete), just in case if (g.num[index - 1] && n <= left << 1) examine((n - left) << 1, (n - left) << 1, g.root + 1, 1 << g.root, 0); } // done printf("maximum of %d table entries for root = %d\n", g.large, g.root); fputs(g.out.str, stdout); } // Examine and show the total number of possible prefix codes for a given // maximum number of symbols, initial root table size, and maximum code length // in bits -- those are the command arguments in that order. The default values // are 286, 9, and 15 respectively, for the deflate literal/length code. The // possible codes are counted for each number of coded symbols from two to the // maximum. The counts for each of those and the total number of codes are // shown. The maximum number of inflate table entries is then calculated across // all possible codes. Each new maximum number of table entries and the // associated sub-code (starting at root + 1 == 10 bits) is shown. // // To count and examine prefix codes that are not length-limited, provide a // maximum length equal to the number of symbols minus one. // // For the deflate literal/length code, use "enough". For the deflate distance // code, use "enough 30 6". int main(int argc, char **argv) { // set up globals for cleanup() g.code = NULL; g.num = NULL; g.done = NULL; string_init(&g.out); // get arguments -- default to the deflate literal/length code int syms = 286; g.root = 9; g.max = 15; if (argc > 1) { syms = atoi(argv[1]); if (argc > 2) { g.root = atoi(argv[2]); if (argc > 3) g.max = atoi(argv[3]); } } if (argc > 4 || syms < 2 || g.root < 1 || g.max < 1) { fputs("invalid arguments, need: [sym >= 2 [root >= 1 [max >= 1]]]\n", stderr); return 1; } // if not restricting the code length, the longest is syms - 1 if (g.max > syms - 1) g.max = syms - 1; // determine the number of bits in a code_t int bits = 0; for (code_t word = 1; word; word <<= 1) bits++; // make sure that the calculation of most will not overflow if (g.max > bits || (code_t)(syms - 2) >= ((code_t)-1 >> (g.max - 1))) { fputs("abort: code length too long for internal types\n", stderr); return 1; } // reject impossible code requests if ((code_t)(syms - 1) > ((code_t)1 << g.max) - 1) { fprintf(stderr, "%d symbols cannot be coded in %d bits\n", syms, g.max); return 1; } // allocate code vector g.code = calloc(g.max + 1, sizeof(int)); assert(g.code != NULL && "out of memory"); // determine size of saved results array, checking for overflows, // allocate and clear the array (set all to zero with calloc()) if (syms == 2) // iff max == 1 g.num = NULL; // won't be saving any results else { g.size = syms >> 1; int n = (syms - 1) >> 1; assert(g.size <= (size_t)-1 / n && "overflow"); g.size *= n; n = g.max - 1; assert(g.size <= (size_t)-1 / n && "overflow"); g.size *= n; g.num = calloc(g.size, sizeof(big_t)); assert(g.num != NULL && "out of memory"); } // count possible codes for all numbers of symbols, add up counts big_t sum = 0; for (int n = 2; n <= syms; n++) { big_t got = count(n, 2, 1); sum += got; assert(got != (big_t)-1 && sum >= got && "overflow"); } printf("%"PRIbig" total codes for 2 to %d symbols", sum, syms); if (g.max < syms - 1) printf(" (%d-bit length limit)\n", g.max); else puts(" (no length limit)"); // allocate and clear done array for been_here() if (syms == 2) g.done = NULL; else { g.done = calloc(g.size, sizeof(struct tab)); assert(g.done != NULL && "out of memory"); } // find and show maximum inflate table usage if (g.root > g.max) // reduce root to max length g.root = g.max; if ((code_t)syms < ((code_t)1 << (g.root + 1))) enough(syms); else fputs("cannot handle minimum code lengths > root", stderr); // done cleanup(); return 0; } |
Added compat/zlib/examples/fitblk.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 | /* fitblk.c: example of fitting compressed output to a specified size Not copyrighted -- provided to the public domain Version 1.1 25 November 2004 Mark Adler */ /* Version history: 1.0 24 Nov 2004 First version 1.1 25 Nov 2004 Change deflateInit2() to deflateInit() Use fixed-size, stack-allocated raw buffers Simplify code moving compression to subroutines Use assert() for internal errors Add detailed description of approach */ /* Approach to just fitting a requested compressed size: fitblk performs three compression passes on a portion of the input data in order to determine how much of that input will compress to nearly the requested output block size. The first pass generates enough deflate blocks to produce output to fill the requested output size plus a specified excess amount (see the EXCESS define below). The last deflate block may go quite a bit past that, but is discarded. The second pass decompresses and recompresses just the compressed data that fit in the requested plus excess sized buffer. The deflate process is terminated after that amount of input, which is less than the amount consumed on the first pass. The last deflate block of the result will be of a comparable size to the final product, so that the header for that deflate block and the compression ratio for that block will be about the same as in the final product. The third compression pass decompresses the result of the second step, but only the compressed data up to the requested size minus an amount to allow the compressed stream to complete (see the MARGIN define below). That will result in a final compressed stream whose length is less than or equal to the requested size. Assuming sufficient input and a requested size greater than a few hundred bytes, the shortfall will typically be less than ten bytes. If the input is short enough that the first compression completes before filling the requested output size, then that compressed stream is return with no recompression. EXCESS is chosen to be just greater than the shortfall seen in a two pass approach similar to the above. That shortfall is due to the last deflate block compressing more efficiently with a smaller header on the second pass. EXCESS is set to be large enough so that there is enough uncompressed data for the second pass to fill out the requested size, and small enough so that the final deflate block of the second pass will be close in size to the final deflate block of the third and final pass. MARGIN is chosen to be just large enough to assure that the final compression has enough room to complete in all cases. */ #include <stdio.h> #include <stdlib.h> #include <assert.h> #include "zlib.h" #define local static /* print nastygram and leave */ local void quit(char *why) { fprintf(stderr, "fitblk abort: %s\n", why); exit(1); } #define RAWLEN 4096 /* intermediate uncompressed buffer size */ /* compress from file to def until provided buffer is full or end of input reached; return last deflate() return value, or Z_ERRNO if there was read error on the file */ local int partcompress(FILE *in, z_streamp def) { int ret, flush; unsigned char raw[RAWLEN]; flush = Z_NO_FLUSH; do { def->avail_in = fread(raw, 1, RAWLEN, in); if (ferror(in)) return Z_ERRNO; def->next_in = raw; if (feof(in)) flush = Z_FINISH; ret = deflate(def, flush); assert(ret != Z_STREAM_ERROR); } while (def->avail_out != 0 && flush == Z_NO_FLUSH); return ret; } /* recompress from inf's input to def's output; the input for inf and the output for def are set in those structures before calling; return last deflate() return value, or Z_MEM_ERROR if inflate() was not able to allocate enough memory when it needed to */ local int recompress(z_streamp inf, z_streamp def) { int ret, flush; unsigned char raw[RAWLEN]; flush = Z_NO_FLUSH; do { /* decompress */ inf->avail_out = RAWLEN; inf->next_out = raw; ret = inflate(inf, Z_NO_FLUSH); assert(ret != Z_STREAM_ERROR && ret != Z_DATA_ERROR && ret != Z_NEED_DICT); if (ret == Z_MEM_ERROR) return ret; /* compress what was decompressed until done or no room */ def->avail_in = RAWLEN - inf->avail_out; def->next_in = raw; if (inf->avail_out != 0) flush = Z_FINISH; ret = deflate(def, flush); assert(ret != Z_STREAM_ERROR); } while (ret != Z_STREAM_END && def->avail_out != 0); return ret; } #define EXCESS 256 /* empirically determined stream overage */ #define MARGIN 8 /* amount to back off for completion */ /* compress from stdin to fixed-size block on stdout */ int main(int argc, char **argv) { int ret; /* return code */ unsigned size; /* requested fixed output block size */ unsigned have; /* bytes written by deflate() call */ unsigned char *blk; /* intermediate and final stream */ unsigned char *tmp; /* close to desired size stream */ z_stream def, inf; /* zlib deflate and inflate states */ /* get requested output size */ if (argc != 2) quit("need one argument: size of output block"); ret = strtol(argv[1], argv + 1, 10); if (argv[1][0] != 0) quit("argument must be a number"); if (ret < 8) /* 8 is minimum zlib stream size */ quit("need positive size of 8 or greater"); size = (unsigned)ret; /* allocate memory for buffers and compression engine */ blk = malloc(size + EXCESS); def.zalloc = Z_NULL; def.zfree = Z_NULL; def.opaque = Z_NULL; ret = deflateInit(&def, Z_DEFAULT_COMPRESSION); if (ret != Z_OK || blk == NULL) quit("out of memory"); /* compress from stdin until output full, or no more input */ def.avail_out = size + EXCESS; def.next_out = blk; ret = partcompress(stdin, &def); if (ret == Z_ERRNO) quit("error reading input"); /* if it all fit, then size was undersubscribed -- done! */ if (ret == Z_STREAM_END && def.avail_out >= EXCESS) { /* write block to stdout */ have = size + EXCESS - def.avail_out; if (fwrite(blk, 1, have, stdout) != have || ferror(stdout)) quit("error writing output"); /* clean up and print results to stderr */ ret = deflateEnd(&def); assert(ret != Z_STREAM_ERROR); free(blk); fprintf(stderr, "%u bytes unused out of %u requested (all input)\n", size - have, size); return 0; } /* it didn't all fit -- set up for recompression */ inf.zalloc = Z_NULL; inf.zfree = Z_NULL; inf.opaque = Z_NULL; inf.avail_in = 0; inf.next_in = Z_NULL; ret = inflateInit(&inf); tmp = malloc(size + EXCESS); if (ret != Z_OK || tmp == NULL) quit("out of memory"); ret = deflateReset(&def); assert(ret != Z_STREAM_ERROR); /* do first recompression close to the right amount */ inf.avail_in = size + EXCESS; inf.next_in = blk; def.avail_out = size + EXCESS; def.next_out = tmp; ret = recompress(&inf, &def); if (ret == Z_MEM_ERROR) quit("out of memory"); /* set up for next recompression */ ret = inflateReset(&inf); assert(ret != Z_STREAM_ERROR); ret = deflateReset(&def); assert(ret != Z_STREAM_ERROR); /* do second and final recompression (third compression) */ inf.avail_in = size - MARGIN; /* assure stream will complete */ inf.next_in = tmp; def.avail_out = size; def.next_out = blk; ret = recompress(&inf, &def); if (ret == Z_MEM_ERROR) quit("out of memory"); assert(ret == Z_STREAM_END); /* otherwise MARGIN too small */ /* done -- write block to stdout */ have = size - def.avail_out; if (fwrite(blk, 1, have, stdout) != have || ferror(stdout)) quit("error writing output"); /* clean up and print results to stderr */ free(tmp); ret = inflateEnd(&inf); assert(ret != Z_STREAM_ERROR); ret = deflateEnd(&def); assert(ret != Z_STREAM_ERROR); free(blk); fprintf(stderr, "%u bytes unused out of %u requested (%lu input)\n", size - have, size, def.total_in); return 0; } |
Added compat/zlib/examples/gun.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 | /* gun.c -- simple gunzip to give an example of the use of inflateBack() * Copyright (C) 2003, 2005, 2008, 2010, 2012 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h Version 1.7 12 August 2012 Mark Adler */ /* Version history: 1.0 16 Feb 2003 First version for testing of inflateBack() 1.1 21 Feb 2005 Decompress concatenated gzip streams Remove use of "this" variable (C++ keyword) Fix return value for in() Improve allocation failure checking Add typecasting for void * structures Add -h option for command version and usage Add a bunch of comments 1.2 20 Mar 2005 Add Unix compress (LZW) decompression Copy file attributes from input file to output file 1.3 12 Jun 2005 Add casts for error messages [Oberhumer] 1.4 8 Dec 2006 LZW decompression speed improvements 1.5 9 Feb 2008 Avoid warning in latest version of gcc 1.6 17 Jan 2010 Avoid signed/unsigned comparison warnings 1.7 12 Aug 2012 Update for z_const usage in zlib 1.2.8 */ /* gun [ -t ] [ name ... ] decompresses the data in the named gzip files. If no arguments are given, gun will decompress from stdin to stdout. The names must end in .gz, -gz, .z, -z, _z, or .Z. The uncompressed data will be written to a file name with the suffix stripped. On success, the original file is deleted. On failure, the output file is deleted. For most failures, the command will continue to process the remaining names on the command line. A memory allocation failure will abort the command. If -t is specified, then the listed files or stdin will be tested as gzip files for integrity (without checking for a proper suffix), no output will be written, and no files will be deleted. Like gzip, gun allows concatenated gzip streams and will decompress them, writing all of the uncompressed data to the output. Unlike gzip, gun allows an empty file on input, and will produce no error writing an empty output file. gun will also decompress files made by Unix compress, which uses LZW compression. These files are automatically detected by virtue of their magic header bytes. Since the end of Unix compress stream is marked by the end-of-file, they cannot be concatenated. If a Unix compress stream is encountered in an input file, it is the last stream in that file. Like gunzip and uncompress, the file attributes of the original compressed file are maintained in the final uncompressed file, to the extent that the user permissions allow it. On my Mac OS X PowerPC G4, gun is almost twice as fast as gunzip (version 1.2.4) is on the same file, when gun is linked with zlib 1.2.2. Also the LZW decompression provided by gun is about twice as fast as the standard Unix uncompress command. */ /* external functions and related types and constants */ #include <stdio.h> /* fprintf() */ #include <stdlib.h> /* malloc(), free() */ #include <string.h> /* strerror(), strcmp(), strlen(), memcpy() */ #include <errno.h> /* errno */ #include <fcntl.h> /* open() */ #include <unistd.h> /* read(), write(), close(), chown(), unlink() */ #include <sys/types.h> #include <sys/stat.h> /* stat(), chmod() */ #include <utime.h> /* utime() */ #include "zlib.h" /* inflateBackInit(), inflateBack(), */ /* inflateBackEnd(), crc32() */ /* function declaration */ #define local static /* buffer constants */ #define SIZE 32768U /* input and output buffer sizes */ #define PIECE 16384 /* limits i/o chunks for 16-bit int case */ /* structure for infback() to pass to input function in() -- it maintains the input file and a buffer of size SIZE */ struct ind { int infile; unsigned char *inbuf; }; /* Load input buffer, assumed to be empty, and return bytes loaded and a pointer to them. read() is called until the buffer is full, or until it returns end-of-file or error. Return 0 on error. */ local unsigned in(void *in_desc, z_const unsigned char **buf) { int ret; unsigned len; unsigned char *next; struct ind *me = (struct ind *)in_desc; next = me->inbuf; *buf = next; len = 0; do { ret = PIECE; if ((unsigned)ret > SIZE - len) ret = (int)(SIZE - len); ret = (int)read(me->infile, next, ret); if (ret == -1) { len = 0; break; } next += ret; len += ret; } while (ret != 0 && len < SIZE); return len; } /* structure for infback() to pass to output function out() -- it maintains the output file, a running CRC-32 check on the output and the total number of bytes output, both for checking against the gzip trailer. (The length in the gzip trailer is stored modulo 2^32, so it's ok if a long is 32 bits and the output is greater than 4 GB.) */ struct outd { int outfile; int check; /* true if checking crc and total */ unsigned long crc; unsigned long total; }; /* Write output buffer and update the CRC-32 and total bytes written. write() is called until all of the output is written or an error is encountered. On success out() returns 0. For a write failure, out() returns 1. If the output file descriptor is -1, then nothing is written. */ local int out(void *out_desc, unsigned char *buf, unsigned len) { int ret; struct outd *me = (struct outd *)out_desc; if (me->check) { me->crc = crc32(me->crc, buf, len); me->total += len; } if (me->outfile != -1) do { ret = PIECE; if ((unsigned)ret > len) ret = (int)len; ret = (int)write(me->outfile, buf, ret); if (ret == -1) return 1; buf += ret; len -= ret; } while (len != 0); return 0; } /* next input byte macro for use inside lunpipe() and gunpipe() */ #define NEXT() (have ? 0 : (have = in(indp, &next)), \ last = have ? (have--, (int)(*next++)) : -1) /* memory for gunpipe() and lunpipe() -- the first 256 entries of prefix[] and suffix[] are never used, could have offset the index, but it's faster to waste the memory */ unsigned char inbuf[SIZE]; /* input buffer */ unsigned char outbuf[SIZE]; /* output buffer */ unsigned short prefix[65536]; /* index to LZW prefix string */ unsigned char suffix[65536]; /* one-character LZW suffix */ unsigned char match[65280 + 2]; /* buffer for reversed match or gzip 32K sliding window */ /* throw out what's left in the current bits byte buffer (this is a vestigial aspect of the compressed data format derived from an implementation that made use of a special VAX machine instruction!) */ #define FLUSHCODE() \ do { \ left = 0; \ rem = 0; \ if (chunk > have) { \ chunk -= have; \ have = 0; \ if (NEXT() == -1) \ break; \ chunk--; \ if (chunk > have) { \ chunk = have = 0; \ break; \ } \ } \ have -= chunk; \ next += chunk; \ chunk = 0; \ } while (0) /* Decompress a compress (LZW) file from indp to outfile. The compress magic header (two bytes) has already been read and verified. There are have bytes of buffered input at next. strm is used for passing error information back to gunpipe(). lunpipe() will return Z_OK on success, Z_BUF_ERROR for an unexpected end of file, read error, or write error (a write error indicated by strm->next_in not equal to Z_NULL), or Z_DATA_ERROR for invalid input. */ local int lunpipe(unsigned have, z_const unsigned char *next, struct ind *indp, int outfile, z_stream *strm) { int last; /* last byte read by NEXT(), or -1 if EOF */ unsigned chunk; /* bytes left in current chunk */ int left; /* bits left in rem */ unsigned rem; /* unused bits from input */ int bits; /* current bits per code */ unsigned code; /* code, table traversal index */ unsigned mask; /* mask for current bits codes */ int max; /* maximum bits per code for this stream */ unsigned flags; /* compress flags, then block compress flag */ unsigned end; /* last valid entry in prefix/suffix tables */ unsigned temp; /* current code */ unsigned prev; /* previous code */ unsigned final; /* last character written for previous code */ unsigned stack; /* next position for reversed string */ unsigned outcnt; /* bytes in output buffer */ struct outd outd; /* output structure */ unsigned char *p; /* set up output */ outd.outfile = outfile; outd.check = 0; /* process remainder of compress header -- a flags byte */ flags = NEXT(); if (last == -1) return Z_BUF_ERROR; if (flags & 0x60) { strm->msg = (char *)"unknown lzw flags set"; return Z_DATA_ERROR; } max = flags & 0x1f; if (max < 9 || max > 16) { strm->msg = (char *)"lzw bits out of range"; return Z_DATA_ERROR; } if (max == 9) /* 9 doesn't really mean 9 */ max = 10; flags &= 0x80; /* true if block compress */ /* clear table */ bits = 9; mask = 0x1ff; end = flags ? 256 : 255; /* set up: get first 9-bit code, which is the first decompressed byte, but don't create a table entry until the next code */ if (NEXT() == -1) /* no compressed data is ok */ return Z_OK; final = prev = (unsigned)last; /* low 8 bits of code */ if (NEXT() == -1) /* missing a bit */ return Z_BUF_ERROR; if (last & 1) { /* code must be < 256 */ strm->msg = (char *)"invalid lzw code"; return Z_DATA_ERROR; } rem = (unsigned)last >> 1; /* remaining 7 bits */ left = 7; chunk = bits - 2; /* 7 bytes left in this chunk */ outbuf[0] = (unsigned char)final; /* write first decompressed byte */ outcnt = 1; /* decode codes */ stack = 0; for (;;) { /* if the table will be full after this, increment the code size */ if (end >= mask && bits < max) { FLUSHCODE(); bits++; mask <<= 1; mask++; } /* get a code of length bits */ if (chunk == 0) /* decrement chunk modulo bits */ chunk = bits; code = rem; /* low bits of code */ if (NEXT() == -1) { /* EOF is end of compressed data */ /* write remaining buffered output */ if (outcnt && out(&outd, outbuf, outcnt)) { strm->next_in = outbuf; /* signal write error */ return Z_BUF_ERROR; } return Z_OK; } code += (unsigned)last << left; /* middle (or high) bits of code */ left += 8; chunk--; if (bits > left) { /* need more bits */ if (NEXT() == -1) /* can't end in middle of code */ return Z_BUF_ERROR; code += (unsigned)last << left; /* high bits of code */ left += 8; chunk--; } code &= mask; /* mask to current code length */ left -= bits; /* number of unused bits */ rem = (unsigned)last >> (8 - left); /* unused bits from last byte */ /* process clear code (256) */ if (code == 256 && flags) { FLUSHCODE(); bits = 9; /* initialize bits and mask */ mask = 0x1ff; end = 255; /* empty table */ continue; /* get next code */ } /* special code to reuse last match */ temp = code; /* save the current code */ if (code > end) { /* Be picky on the allowed code here, and make sure that the code we drop through (prev) will be a valid index so that random input does not cause an exception. The code != end + 1 check is empirically derived, and not checked in the original uncompress code. If this ever causes a problem, that check could be safely removed. Leaving this check in greatly improves gun's ability to detect random or corrupted input after a compress header. In any case, the prev > end check must be retained. */ if (code != end + 1 || prev > end) { strm->msg = (char *)"invalid lzw code"; return Z_DATA_ERROR; } match[stack++] = (unsigned char)final; code = prev; } /* walk through linked list to generate output in reverse order */ p = match + stack; while (code >= 256) { *p++ = suffix[code]; code = prefix[code]; } stack = p - match; match[stack++] = (unsigned char)code; final = code; /* link new table entry */ if (end < mask) { end++; prefix[end] = (unsigned short)prev; suffix[end] = (unsigned char)final; } /* set previous code for next iteration */ prev = temp; /* write output in forward order */ while (stack > SIZE - outcnt) { while (outcnt < SIZE) outbuf[outcnt++] = match[--stack]; if (out(&outd, outbuf, outcnt)) { strm->next_in = outbuf; /* signal write error */ return Z_BUF_ERROR; } outcnt = 0; } p = match + stack; do { outbuf[outcnt++] = *--p; } while (p > match); stack = 0; /* loop for next code with final and prev as the last match, rem and left provide the first 0..7 bits of the next code, end is the last valid table entry */ } } /* Decompress a gzip file from infile to outfile. strm is assumed to have been successfully initialized with inflateBackInit(). The input file may consist of a series of gzip streams, in which case all of them will be decompressed to the output file. If outfile is -1, then the gzip stream(s) integrity is checked and nothing is written. The return value is a zlib error code: Z_MEM_ERROR if out of memory, Z_DATA_ERROR if the header or the compressed data is invalid, or if the trailer CRC-32 check or length doesn't match, Z_BUF_ERROR if the input ends prematurely or a write error occurs, or Z_ERRNO if junk (not a another gzip stream) follows a valid gzip stream. */ local int gunpipe(z_stream *strm, int infile, int outfile) { int ret, first, last; unsigned have, flags, len; z_const unsigned char *next = NULL; struct ind ind, *indp; struct outd outd; /* setup input buffer */ ind.infile = infile; ind.inbuf = inbuf; indp = &ind; /* decompress concatenated gzip streams */ have = 0; /* no input data read in yet */ first = 1; /* looking for first gzip header */ strm->next_in = Z_NULL; /* so Z_BUF_ERROR means EOF */ for (;;) { /* look for the two magic header bytes for a gzip stream */ if (NEXT() == -1) { ret = Z_OK; break; /* empty gzip stream is ok */ } if (last != 31 || (NEXT() != 139 && last != 157)) { strm->msg = (char *)"incorrect header check"; ret = first ? Z_DATA_ERROR : Z_ERRNO; break; /* not a gzip or compress header */ } first = 0; /* next non-header is junk */ /* process a compress (LZW) file -- can't be concatenated after this */ if (last == 157) { ret = lunpipe(have, next, indp, outfile, strm); break; } /* process remainder of gzip header */ ret = Z_BUF_ERROR; if (NEXT() != 8) { /* only deflate method allowed */ if (last == -1) break; strm->msg = (char *)"unknown compression method"; ret = Z_DATA_ERROR; break; } flags = NEXT(); /* header flags */ NEXT(); /* discard mod time, xflgs, os */ NEXT(); NEXT(); NEXT(); NEXT(); NEXT(); if (last == -1) break; if (flags & 0xe0) { strm->msg = (char *)"unknown header flags set"; ret = Z_DATA_ERROR; break; } if (flags & 4) { /* extra field */ len = NEXT(); len += (unsigned)(NEXT()) << 8; if (last == -1) break; while (len > have) { len -= have; have = 0; if (NEXT() == -1) break; len--; } if (last == -1) break; have -= len; next += len; } if (flags & 8) /* file name */ while (NEXT() != 0 && last != -1) ; if (flags & 16) /* comment */ while (NEXT() != 0 && last != -1) ; if (flags & 2) { /* header crc */ NEXT(); NEXT(); } if (last == -1) break; /* set up output */ outd.outfile = outfile; outd.check = 1; outd.crc = crc32(0L, Z_NULL, 0); outd.total = 0; /* decompress data to output */ strm->next_in = next; strm->avail_in = have; ret = inflateBack(strm, in, indp, out, &outd); if (ret != Z_STREAM_END) break; next = strm->next_in; have = strm->avail_in; strm->next_in = Z_NULL; /* so Z_BUF_ERROR means EOF */ /* check trailer */ ret = Z_BUF_ERROR; if (NEXT() != (int)(outd.crc & 0xff) || NEXT() != (int)((outd.crc >> 8) & 0xff) || NEXT() != (int)((outd.crc >> 16) & 0xff) || NEXT() != (int)((outd.crc >> 24) & 0xff)) { /* crc error */ if (last != -1) { strm->msg = (char *)"incorrect data check"; ret = Z_DATA_ERROR; } break; } if (NEXT() != (int)(outd.total & 0xff) || NEXT() != (int)((outd.total >> 8) & 0xff) || NEXT() != (int)((outd.total >> 16) & 0xff) || NEXT() != (int)((outd.total >> 24) & 0xff)) { /* length error */ if (last != -1) { strm->msg = (char *)"incorrect length check"; ret = Z_DATA_ERROR; } break; } /* go back and look for another gzip stream */ } /* clean up and return */ return ret; } /* Copy file attributes, from -> to, as best we can. This is best effort, so no errors are reported. The mode bits, including suid, sgid, and the sticky bit are copied (if allowed), the owner's user id and group id are copied (again if allowed), and the access and modify times are copied. */ local void copymeta(char *from, char *to) { struct stat was; struct utimbuf when; /* get all of from's Unix meta data, return if not a regular file */ if (stat(from, &was) != 0 || (was.st_mode & S_IFMT) != S_IFREG) return; /* set to's mode bits, ignore errors */ (void)chmod(to, was.st_mode & 07777); /* copy owner's user and group, ignore errors */ (void)chown(to, was.st_uid, was.st_gid); /* copy access and modify times, ignore errors */ when.actime = was.st_atime; when.modtime = was.st_mtime; (void)utime(to, &when); } /* Decompress the file inname to the file outnname, of if test is true, just decompress without writing and check the gzip trailer for integrity. If inname is NULL or an empty string, read from stdin. If outname is NULL or an empty string, write to stdout. strm is a pre-initialized inflateBack structure. When appropriate, copy the file attributes from inname to outname. gunzip() returns 1 if there is an out-of-memory error or an unexpected return code from gunpipe(). Otherwise it returns 0. */ local int gunzip(z_stream *strm, char *inname, char *outname, int test) { int ret; int infile, outfile; /* open files */ if (inname == NULL || *inname == 0) { inname = "-"; infile = 0; /* stdin */ } else { infile = open(inname, O_RDONLY, 0); if (infile == -1) { fprintf(stderr, "gun cannot open %s\n", inname); return 0; } } if (test) outfile = -1; else if (outname == NULL || *outname == 0) { outname = "-"; outfile = 1; /* stdout */ } else { outfile = open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0666); if (outfile == -1) { close(infile); fprintf(stderr, "gun cannot create %s\n", outname); return 0; } } errno = 0; /* decompress */ ret = gunpipe(strm, infile, outfile); if (outfile > 2) close(outfile); if (infile > 2) close(infile); /* interpret result */ switch (ret) { case Z_OK: case Z_ERRNO: if (infile > 2 && outfile > 2) { copymeta(inname, outname); /* copy attributes */ unlink(inname); } if (ret == Z_ERRNO) fprintf(stderr, "gun warning: trailing garbage ignored in %s\n", inname); break; case Z_DATA_ERROR: if (outfile > 2) unlink(outname); fprintf(stderr, "gun data error on %s: %s\n", inname, strm->msg); break; case Z_MEM_ERROR: if (outfile > 2) unlink(outname); fprintf(stderr, "gun out of memory error--aborting\n"); return 1; case Z_BUF_ERROR: if (outfile > 2) unlink(outname); if (strm->next_in != Z_NULL) { fprintf(stderr, "gun write error on %s: %s\n", outname, strerror(errno)); } else if (errno) { fprintf(stderr, "gun read error on %s: %s\n", inname, strerror(errno)); } else { fprintf(stderr, "gun unexpected end of file on %s\n", inname); } break; default: if (outfile > 2) unlink(outname); fprintf(stderr, "gun internal error--aborting\n"); return 1; } return 0; } /* Process the gun command line arguments. See the command syntax near the beginning of this source file. */ int main(int argc, char **argv) { int ret, len, test; char *outname; unsigned char *window; z_stream strm; /* initialize inflateBack state for repeated use */ window = match; /* reuse LZW match buffer */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; ret = inflateBackInit(&strm, 15, window); if (ret != Z_OK) { fprintf(stderr, "gun out of memory error--aborting\n"); return 1; } /* decompress each file to the same name with the suffix removed */ argc--; argv++; test = 0; if (argc && strcmp(*argv, "-h") == 0) { fprintf(stderr, "gun 1.6 (17 Jan 2010)\n"); fprintf(stderr, "Copyright (C) 2003-2010 Mark Adler\n"); fprintf(stderr, "usage: gun [-t] [file1.gz [file2.Z ...]]\n"); return 0; } if (argc && strcmp(*argv, "-t") == 0) { test = 1; argc--; argv++; } if (argc) do { if (test) outname = NULL; else { len = (int)strlen(*argv); if (strcmp(*argv + len - 3, ".gz") == 0 || strcmp(*argv + len - 3, "-gz") == 0) len -= 3; else if (strcmp(*argv + len - 2, ".z") == 0 || strcmp(*argv + len - 2, "-z") == 0 || strcmp(*argv + len - 2, "_z") == 0 || strcmp(*argv + len - 2, ".Z") == 0) len -= 2; else { fprintf(stderr, "gun error: no gz type on %s--skipping\n", *argv); continue; } outname = malloc(len + 1); if (outname == NULL) { fprintf(stderr, "gun out of memory error--aborting\n"); ret = 1; break; } memcpy(outname, *argv, len); outname[len] = 0; } ret = gunzip(&strm, *argv, outname, test); if (outname != NULL) free(outname); if (ret) break; } while (argv++, --argc); else ret = gunzip(&strm, NULL, NULL, test); /* clean up */ inflateBackEnd(&strm); return ret; } |
Added compat/zlib/examples/gzappend.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 | /* gzappend -- command to append to a gzip file Copyright (C) 2003, 2012 Mark Adler, all rights reserved version 1.2, 11 Oct 2012 This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Mark Adler madler@alumni.caltech.edu */ /* * Change history: * * 1.0 19 Oct 2003 - First version * 1.1 4 Nov 2003 - Expand and clarify some comments and notes * - Add version and copyright to help * - Send help to stdout instead of stderr * - Add some preemptive typecasts * - Add L to constants in lseek() calls * - Remove some debugging information in error messages * - Use new data_type definition for zlib 1.2.1 * - Simplify and unify file operations * - Finish off gzip file in gztack() * - Use deflatePrime() instead of adding empty blocks * - Keep gzip file clean on appended file read errors * - Use in-place rotate instead of auxiliary buffer * (Why you ask? Because it was fun to write!) * 1.2 11 Oct 2012 - Fix for proper z_const usage * - Check for input buffer malloc failure */ /* gzappend takes a gzip file and appends to it, compressing files from the command line or data from stdin. The gzip file is written to directly, to avoid copying that file, in case it's large. Note that this results in the unfriendly behavior that if gzappend fails, the gzip file is corrupted. This program was written to illustrate the use of the new Z_BLOCK option of zlib 1.2.x's inflate() function. This option returns from inflate() at each block boundary to facilitate locating and modifying the last block bit at the start of the final deflate block. Also whether using Z_BLOCK or not, another required feature of zlib 1.2.x is that inflate() now provides the number of unused bits in the last input byte used. gzappend will not work with versions of zlib earlier than 1.2.1. gzappend first decompresses the gzip file internally, discarding all but the last 32K of uncompressed data, and noting the location of the last block bit and the number of unused bits in the last byte of the compressed data. The gzip trailer containing the CRC-32 and length of the uncompressed data is verified. This trailer will be later overwritten. Then the last block bit is cleared by seeking back in the file and rewriting the byte that contains it. Seeking forward, the last byte of the compressed data is saved along with the number of unused bits to initialize deflate. A deflate process is initialized, using the last 32K of the uncompressed data from the gzip file to initialize the dictionary. If the total uncompressed data was less than 32K, then all of it is used to initialize the dictionary. The deflate output bit buffer is also initialized with the last bits from the original deflate stream. From here on, the data to append is simply compressed using deflate, and written to the gzip file. When that is complete, the new CRC-32 and uncompressed length are written as the trailer of the gzip file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include "zlib.h" #define local static #define LGCHUNK 14 #define CHUNK (1U << LGCHUNK) #define DSIZE 32768U /* print an error message and terminate with extreme prejudice */ local void bye(char *msg1, char *msg2) { fprintf(stderr, "gzappend error: %s%s\n", msg1, msg2); exit(1); } /* return the greatest common divisor of a and b using Euclid's algorithm, modified to be fast when one argument much greater than the other, and coded to avoid unnecessary swapping */ local unsigned gcd(unsigned a, unsigned b) { unsigned c; while (a && b) if (a > b) { c = b; while (a - c >= c) c <<= 1; a -= c; } else { c = a; while (b - c >= c) c <<= 1; b -= c; } return a + b; } /* rotate list[0..len-1] left by rot positions, in place */ local void rotate(unsigned char *list, unsigned len, unsigned rot) { unsigned char tmp; unsigned cycles; unsigned char *start, *last, *to, *from; /* normalize rot and handle degenerate cases */ if (len < 2) return; if (rot >= len) rot %= len; if (rot == 0) return; /* pointer to last entry in list */ last = list + (len - 1); /* do simple left shift by one */ if (rot == 1) { tmp = *list; memmove(list, list + 1, len - 1); *last = tmp; return; } /* do simple right shift by one */ if (rot == len - 1) { tmp = *last; memmove(list + 1, list, len - 1); *list = tmp; return; } /* otherwise do rotate as a set of cycles in place */ cycles = gcd(len, rot); /* number of cycles */ do { start = from = list + cycles; /* start index is arbitrary */ tmp = *from; /* save entry to be overwritten */ for (;;) { to = from; /* next step in cycle */ from += rot; /* go right rot positions */ if (from > last) from -= len; /* (pointer better not wrap) */ if (from == start) break; /* all but one shifted */ *to = *from; /* shift left */ } *to = tmp; /* complete the circle */ } while (--cycles); } /* structure for gzip file read operations */ typedef struct { int fd; /* file descriptor */ int size; /* 1 << size is bytes in buf */ unsigned left; /* bytes available at next */ unsigned char *buf; /* buffer */ z_const unsigned char *next; /* next byte in buffer */ char *name; /* file name for error messages */ } file; /* reload buffer */ local int readin(file *in) { int len; len = read(in->fd, in->buf, 1 << in->size); if (len == -1) bye("error reading ", in->name); in->left = (unsigned)len; in->next = in->buf; return len; } /* read from file in, exit if end-of-file */ local int readmore(file *in) { if (readin(in) == 0) bye("unexpected end of ", in->name); return 0; } #define read1(in) (in->left == 0 ? readmore(in) : 0, \ in->left--, *(in->next)++) /* skip over n bytes of in */ local void skip(file *in, unsigned n) { unsigned bypass; if (n > in->left) { n -= in->left; bypass = n & ~((1U << in->size) - 1); if (bypass) { if (lseek(in->fd, (off_t)bypass, SEEK_CUR) == -1) bye("seeking ", in->name); n -= bypass; } readmore(in); if (n > in->left) bye("unexpected end of ", in->name); } in->left -= n; in->next += n; } /* read a four-byte unsigned integer, little-endian, from in */ unsigned long read4(file *in) { unsigned long val; val = read1(in); val += (unsigned)read1(in) << 8; val += (unsigned long)read1(in) << 16; val += (unsigned long)read1(in) << 24; return val; } /* skip over gzip header */ local void gzheader(file *in) { int flags; unsigned n; if (read1(in) != 31 || read1(in) != 139) bye(in->name, " not a gzip file"); if (read1(in) != 8) bye("unknown compression method in", in->name); flags = read1(in); if (flags & 0xe0) bye("unknown header flags set in", in->name); skip(in, 6); if (flags & 4) { n = read1(in); n += (unsigned)(read1(in)) << 8; skip(in, n); } if (flags & 8) while (read1(in) != 0) ; if (flags & 16) while (read1(in) != 0) ; if (flags & 2) skip(in, 2); } /* decompress gzip file "name", return strm with a deflate stream ready to continue compression of the data in the gzip file, and return a file descriptor pointing to where to write the compressed data -- the deflate stream is initialized to compress using level "level" */ local int gzscan(char *name, z_stream *strm, int level) { int ret, lastbit, left, full; unsigned have; unsigned long crc, tot; unsigned char *window; off_t lastoff, end; file gz; /* open gzip file */ gz.name = name; gz.fd = open(name, O_RDWR, 0); if (gz.fd == -1) bye("cannot open ", name); gz.buf = malloc(CHUNK); if (gz.buf == NULL) bye("out of memory", ""); gz.size = LGCHUNK; gz.left = 0; /* skip gzip header */ gzheader(&gz); /* prepare to decompress */ window = malloc(DSIZE); if (window == NULL) bye("out of memory", ""); strm->zalloc = Z_NULL; strm->zfree = Z_NULL; strm->opaque = Z_NULL; ret = inflateInit2(strm, -15); if (ret != Z_OK) bye("out of memory", " or library mismatch"); /* decompress the deflate stream, saving append information */ lastbit = 0; lastoff = lseek(gz.fd, 0L, SEEK_CUR) - gz.left; left = 0; strm->avail_in = gz.left; strm->next_in = gz.next; crc = crc32(0L, Z_NULL, 0); have = full = 0; do { /* if needed, get more input */ if (strm->avail_in == 0) { readmore(&gz); strm->avail_in = gz.left; strm->next_in = gz.next; } /* set up output to next available section of sliding window */ strm->avail_out = DSIZE - have; strm->next_out = window + have; /* inflate and check for errors */ ret = inflate(strm, Z_BLOCK); if (ret == Z_STREAM_ERROR) bye("internal stream error!", ""); if (ret == Z_MEM_ERROR) bye("out of memory", ""); if (ret == Z_DATA_ERROR) bye("invalid compressed data--format violated in", name); /* update crc and sliding window pointer */ crc = crc32(crc, window + have, DSIZE - have - strm->avail_out); if (strm->avail_out) have = DSIZE - strm->avail_out; else { have = 0; full = 1; } /* process end of block */ if (strm->data_type & 128) { if (strm->data_type & 64) left = strm->data_type & 0x1f; else { lastbit = strm->data_type & 0x1f; lastoff = lseek(gz.fd, 0L, SEEK_CUR) - strm->avail_in; } } } while (ret != Z_STREAM_END); inflateEnd(strm); gz.left = strm->avail_in; gz.next = strm->next_in; /* save the location of the end of the compressed data */ end = lseek(gz.fd, 0L, SEEK_CUR) - gz.left; /* check gzip trailer and save total for deflate */ if (crc != read4(&gz)) bye("invalid compressed data--crc mismatch in ", name); tot = strm->total_out; if ((tot & 0xffffffffUL) != read4(&gz)) bye("invalid compressed data--length mismatch in", name); /* if not at end of file, warn */ if (gz.left || readin(&gz)) fprintf(stderr, "gzappend warning: junk at end of gzip file overwritten\n"); /* clear last block bit */ lseek(gz.fd, lastoff - (lastbit != 0), SEEK_SET); if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name); *gz.buf = (unsigned char)(*gz.buf ^ (1 << ((8 - lastbit) & 7))); lseek(gz.fd, -1L, SEEK_CUR); if (write(gz.fd, gz.buf, 1) != 1) bye("writing after seek to ", name); /* if window wrapped, build dictionary from window by rotating */ if (full) { rotate(window, DSIZE, have); have = DSIZE; } /* set up deflate stream with window, crc, total_in, and leftover bits */ ret = deflateInit2(strm, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); if (ret != Z_OK) bye("out of memory", ""); deflateSetDictionary(strm, window, have); strm->adler = crc; strm->total_in = tot; if (left) { lseek(gz.fd, --end, SEEK_SET); if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name); deflatePrime(strm, 8 - left, *gz.buf); } lseek(gz.fd, end, SEEK_SET); /* clean up and return */ free(window); free(gz.buf); return gz.fd; } /* append file "name" to gzip file gd using deflate stream strm -- if last is true, then finish off the deflate stream at the end */ local void gztack(char *name, int gd, z_stream *strm, int last) { int fd, len, ret; unsigned left; unsigned char *in, *out; /* open file to compress and append */ fd = 0; if (name != NULL) { fd = open(name, O_RDONLY, 0); if (fd == -1) fprintf(stderr, "gzappend warning: %s not found, skipping ...\n", name); } /* allocate buffers */ in = malloc(CHUNK); out = malloc(CHUNK); if (in == NULL || out == NULL) bye("out of memory", ""); /* compress input file and append to gzip file */ do { /* get more input */ len = read(fd, in, CHUNK); if (len == -1) { fprintf(stderr, "gzappend warning: error reading %s, skipping rest ...\n", name); len = 0; } strm->avail_in = (unsigned)len; strm->next_in = in; if (len) strm->adler = crc32(strm->adler, in, (unsigned)len); /* compress and write all available output */ do { strm->avail_out = CHUNK; strm->next_out = out; ret = deflate(strm, last && len == 0 ? Z_FINISH : Z_NO_FLUSH); left = CHUNK - strm->avail_out; while (left) { len = write(gd, out + CHUNK - strm->avail_out - left, left); if (len == -1) bye("writing gzip file", ""); left -= (unsigned)len; } } while (strm->avail_out == 0 && ret != Z_STREAM_END); } while (len != 0); /* write trailer after last entry */ if (last) { deflateEnd(strm); out[0] = (unsigned char)(strm->adler); out[1] = (unsigned char)(strm->adler >> 8); out[2] = (unsigned char)(strm->adler >> 16); out[3] = (unsigned char)(strm->adler >> 24); out[4] = (unsigned char)(strm->total_in); out[5] = (unsigned char)(strm->total_in >> 8); out[6] = (unsigned char)(strm->total_in >> 16); out[7] = (unsigned char)(strm->total_in >> 24); len = 8; do { ret = write(gd, out + 8 - len, len); if (ret == -1) bye("writing gzip file", ""); len -= ret; } while (len); close(gd); } /* clean up and return */ free(out); free(in); if (fd > 0) close(fd); } /* process the compression level option if present, scan the gzip file, and append the specified files, or append the data from stdin if no other file names are provided on the command line -- the gzip file must be writable and seekable */ int main(int argc, char **argv) { int gd, level; z_stream strm; /* ignore command name */ argc--; argv++; /* provide usage if no arguments */ if (*argv == NULL) { printf( "gzappend 1.2 (11 Oct 2012) Copyright (C) 2003, 2012 Mark Adler\n" ); printf( "usage: gzappend [-level] file.gz [ addthis [ andthis ... ]]\n"); return 0; } /* set compression level */ level = Z_DEFAULT_COMPRESSION; if (argv[0][0] == '-') { if (argv[0][1] < '0' || argv[0][1] > '9' || argv[0][2] != 0) bye("invalid compression level", ""); level = argv[0][1] - '0'; if (*++argv == NULL) bye("no gzip file name after options", ""); } /* prepare to append to gzip file */ gd = gzscan(*argv++, &strm, level); /* append files on command line, or from stdin if none */ if (*argv == NULL) gztack(NULL, gd, &strm, 1); else do { gztack(*argv, gd, &strm, argv[1] == NULL); } while (*++argv != NULL); return 0; } |
Added compat/zlib/examples/gzjoin.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 | /* gzjoin -- command to join gzip files into one gzip file Copyright (C) 2004, 2005, 2012 Mark Adler, all rights reserved version 1.2, 14 Aug 2012 This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Mark Adler madler@alumni.caltech.edu */ /* * Change history: * * 1.0 11 Dec 2004 - First version * 1.1 12 Jun 2005 - Changed ssize_t to long for portability * 1.2 14 Aug 2012 - Clean up for z_const usage */ /* gzjoin takes one or more gzip files on the command line and writes out a single gzip file that will uncompress to the concatenation of the uncompressed data from the individual gzip files. gzjoin does this without having to recompress any of the data and without having to calculate a new crc32 for the concatenated uncompressed data. gzjoin does however have to decompress all of the input data in order to find the bits in the compressed data that need to be modified to concatenate the streams. gzjoin does not do an integrity check on the input gzip files other than checking the gzip header and decompressing the compressed data. They are otherwise assumed to be complete and correct. Each joint between gzip files removes at least 18 bytes of previous trailer and subsequent header, and inserts an average of about three bytes to the compressed data in order to connect the streams. The output gzip file has a minimal ten-byte gzip header with no file name or modification time. This program was written to illustrate the use of the Z_BLOCK option of inflate() and the crc32_combine() function. gzjoin will not compile with versions of zlib earlier than 1.2.3. */ #include <stdio.h> /* fputs(), fprintf(), fwrite(), putc() */ #include <stdlib.h> /* exit(), malloc(), free() */ #include <fcntl.h> /* open() */ #include <unistd.h> /* close(), read(), lseek() */ #include "zlib.h" /* crc32(), crc32_combine(), inflateInit2(), inflate(), inflateEnd() */ #define local static /* exit with an error (return a value to allow use in an expression) */ local int bail(char *why1, char *why2) { fprintf(stderr, "gzjoin error: %s%s, output incomplete\n", why1, why2); exit(1); return 0; } /* -- simple buffered file input with access to the buffer -- */ #define CHUNK 32768 /* must be a power of two and fit in unsigned */ /* bin buffered input file type */ typedef struct { char *name; /* name of file for error messages */ int fd; /* file descriptor */ unsigned left; /* bytes remaining at next */ unsigned char *next; /* next byte to read */ unsigned char *buf; /* allocated buffer of length CHUNK */ } bin; /* close a buffered file and free allocated memory */ local void bclose(bin *in) { if (in != NULL) { if (in->fd != -1) close(in->fd); if (in->buf != NULL) free(in->buf); free(in); } } /* open a buffered file for input, return a pointer to type bin, or NULL on failure */ local bin *bopen(char *name) { bin *in; in = malloc(sizeof(bin)); if (in == NULL) return NULL; in->buf = malloc(CHUNK); in->fd = open(name, O_RDONLY, 0); if (in->buf == NULL || in->fd == -1) { bclose(in); return NULL; } in->left = 0; in->next = in->buf; in->name = name; return in; } /* load buffer from file, return -1 on read error, 0 or 1 on success, with 1 indicating that end-of-file was reached */ local int bload(bin *in) { long len; if (in == NULL) return -1; if (in->left != 0) return 0; in->next = in->buf; do { len = (long)read(in->fd, in->buf + in->left, CHUNK - in->left); if (len < 0) return -1; in->left += (unsigned)len; } while (len != 0 && in->left < CHUNK); return len == 0 ? 1 : 0; } /* get a byte from the file, bail if end of file */ #define bget(in) (in->left ? 0 : bload(in), \ in->left ? (in->left--, *(in->next)++) : \ bail("unexpected end of file on ", in->name)) /* get a four-byte little-endian unsigned integer from file */ local unsigned long bget4(bin *in) { unsigned long val; val = bget(in); val += (unsigned long)(bget(in)) << 8; val += (unsigned long)(bget(in)) << 16; val += (unsigned long)(bget(in)) << 24; return val; } /* skip bytes in file */ local void bskip(bin *in, unsigned skip) { /* check pointer */ if (in == NULL) return; /* easy case -- skip bytes in buffer */ if (skip <= in->left) { in->left -= skip; in->next += skip; return; } /* skip what's in buffer, discard buffer contents */ skip -= in->left; in->left = 0; /* seek past multiples of CHUNK bytes */ if (skip > CHUNK) { unsigned left; left = skip & (CHUNK - 1); if (left == 0) { /* exact number of chunks: seek all the way minus one byte to check for end-of-file with a read */ lseek(in->fd, skip - 1, SEEK_CUR); if (read(in->fd, in->buf, 1) != 1) bail("unexpected end of file on ", in->name); return; } /* skip the integral chunks, update skip with remainder */ lseek(in->fd, skip - left, SEEK_CUR); skip = left; } /* read more input and skip remainder */ bload(in); if (skip > in->left) bail("unexpected end of file on ", in->name); in->left -= skip; in->next += skip; } /* -- end of buffered input functions -- */ /* skip the gzip header from file in */ local void gzhead(bin *in) { int flags; /* verify gzip magic header and compression method */ if (bget(in) != 0x1f || bget(in) != 0x8b || bget(in) != 8) bail(in->name, " is not a valid gzip file"); /* get and verify flags */ flags = bget(in); if ((flags & 0xe0) != 0) bail("unknown reserved bits set in ", in->name); /* skip modification time, extra flags, and os */ bskip(in, 6); /* skip extra field if present */ if (flags & 4) { unsigned len; len = bget(in); len += (unsigned)(bget(in)) << 8; bskip(in, len); } /* skip file name if present */ if (flags & 8) while (bget(in) != 0) ; /* skip comment if present */ if (flags & 16) while (bget(in) != 0) ; /* skip header crc if present */ if (flags & 2) bskip(in, 2); } /* write a four-byte little-endian unsigned integer to out */ local void put4(unsigned long val, FILE *out) { putc(val & 0xff, out); putc((val >> 8) & 0xff, out); putc((val >> 16) & 0xff, out); putc((val >> 24) & 0xff, out); } /* Load up zlib stream from buffered input, bail if end of file */ local void zpull(z_streamp strm, bin *in) { if (in->left == 0) bload(in); if (in->left == 0) bail("unexpected end of file on ", in->name); strm->avail_in = in->left; strm->next_in = in->next; } /* Write header for gzip file to out and initialize trailer. */ local void gzinit(unsigned long *crc, unsigned long *tot, FILE *out) { fwrite("\x1f\x8b\x08\0\0\0\0\0\0\xff", 1, 10, out); *crc = crc32(0L, Z_NULL, 0); *tot = 0; } /* Copy the compressed data from name, zeroing the last block bit of the last block if clr is true, and adding empty blocks as needed to get to a byte boundary. If clr is false, then the last block becomes the last block of the output, and the gzip trailer is written. crc and tot maintains the crc and length (modulo 2^32) of the output for the trailer. The resulting gzip file is written to out. gzinit() must be called before the first call of gzcopy() to write the gzip header and to initialize crc and tot. */ local void gzcopy(char *name, int clr, unsigned long *crc, unsigned long *tot, FILE *out) { int ret; /* return value from zlib functions */ int pos; /* where the "last block" bit is in byte */ int last; /* true if processing the last block */ bin *in; /* buffered input file */ unsigned char *start; /* start of compressed data in buffer */ unsigned char *junk; /* buffer for uncompressed data -- discarded */ z_off_t len; /* length of uncompressed data (support > 4 GB) */ z_stream strm; /* zlib inflate stream */ /* open gzip file and skip header */ in = bopen(name); if (in == NULL) bail("could not open ", name); gzhead(in); /* allocate buffer for uncompressed data and initialize raw inflate stream */ junk = malloc(CHUNK); strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit2(&strm, -15); if (junk == NULL || ret != Z_OK) bail("out of memory", ""); /* inflate and copy compressed data, clear last-block bit if requested */ len = 0; zpull(&strm, in); start = in->next; last = start[0] & 1; if (last && clr) start[0] &= ~1; strm.avail_out = 0; for (;;) { /* if input used and output done, write used input and get more */ if (strm.avail_in == 0 && strm.avail_out != 0) { fwrite(start, 1, strm.next_in - start, out); start = in->buf; in->left = 0; zpull(&strm, in); } /* decompress -- return early when end-of-block reached */ strm.avail_out = CHUNK; strm.next_out = junk; ret = inflate(&strm, Z_BLOCK); switch (ret) { case Z_MEM_ERROR: bail("out of memory", ""); case Z_DATA_ERROR: bail("invalid compressed data in ", in->name); } /* update length of uncompressed data */ len += CHUNK - strm.avail_out; /* check for block boundary (only get this when block copied out) */ if (strm.data_type & 128) { /* if that was the last block, then done */ if (last) break; /* number of unused bits in last byte */ pos = strm.data_type & 7; /* find the next last-block bit */ if (pos != 0) { /* next last-block bit is in last used byte */ pos = 0x100 >> pos; last = strm.next_in[-1] & pos; if (last && clr) in->buf[strm.next_in - in->buf - 1] &= ~pos; } else { /* next last-block bit is in next unused byte */ if (strm.avail_in == 0) { /* don't have that byte yet -- get it */ fwrite(start, 1, strm.next_in - start, out); start = in->buf; in->left = 0; zpull(&strm, in); } last = strm.next_in[0] & 1; if (last && clr) in->buf[strm.next_in - in->buf] &= ~1; } } } /* update buffer with unused input */ in->left = strm.avail_in; in->next = in->buf + (strm.next_in - in->buf); /* copy used input, write empty blocks to get to byte boundary */ pos = strm.data_type & 7; fwrite(start, 1, in->next - start - 1, out); last = in->next[-1]; if (pos == 0 || !clr) /* already at byte boundary, or last file: write last byte */ putc(last, out); else { /* append empty blocks to last byte */ last &= ((0x100 >> pos) - 1); /* assure unused bits are zero */ if (pos & 1) { /* odd -- append an empty stored block */ putc(last, out); if (pos == 1) putc(0, out); /* two more bits in block header */ fwrite("\0\0\xff\xff", 1, 4, out); } else { /* even -- append 1, 2, or 3 empty fixed blocks */ switch (pos) { case 6: putc(last | 8, out); last = 0; case 4: putc(last | 0x20, out); last = 0; case 2: putc(last | 0x80, out); putc(0, out); } } } /* update crc and tot */ *crc = crc32_combine(*crc, bget4(in), len); *tot += (unsigned long)len; /* clean up */ inflateEnd(&strm); free(junk); bclose(in); /* write trailer if this is the last gzip file */ if (!clr) { put4(*crc, out); put4(*tot, out); } } /* join the gzip files on the command line, write result to stdout */ int main(int argc, char **argv) { unsigned long crc, tot; /* running crc and total uncompressed length */ /* skip command name */ argc--; argv++; /* show usage if no arguments */ if (argc == 0) { fputs("gzjoin usage: gzjoin f1.gz [f2.gz [f3.gz ...]] > fjoin.gz\n", stderr); return 0; } /* join gzip files on command line and write to stdout */ gzinit(&crc, &tot, stdout); while (argc--) gzcopy(*argv++, argc, &crc, &tot, stdout); /* done */ return 0; } |
Added compat/zlib/examples/gzlog.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 | /* * gzlog.c * Copyright (C) 2004, 2008, 2012, 2016, 2019 Mark Adler, all rights reserved * For conditions of distribution and use, see copyright notice in gzlog.h * version 2.3, 25 May 2019 */ /* gzlog provides a mechanism for frequently appending short strings to a gzip file that is efficient both in execution time and compression ratio. The strategy is to write the short strings in an uncompressed form to the end of the gzip file, only compressing when the amount of uncompressed data has reached a given threshold. gzlog also provides protection against interruptions in the process due to system crashes. The status of the operation is recorded in an extra field in the gzip file, and is only updated once the gzip file is brought to a valid state. The last data to be appended or compressed is saved in an auxiliary file, so that if the operation is interrupted, it can be completed the next time an append operation is attempted. gzlog maintains another auxiliary file with the last 32K of data from the compressed portion, which is preloaded for the compression of the subsequent data. This minimizes the impact to the compression ratio of appending. */ /* Operations Concept: Files (log name "foo"): foo.gz -- gzip file with the complete log foo.add -- last message to append or last data to compress foo.dict -- dictionary of the last 32K of data for next compression foo.temp -- temporary dictionary file for compression after this one foo.lock -- lock file for reading and writing the other files foo.repairs -- log file for log file recovery operations (not compressed) gzip file structure: - fixed-length (no file name) header with extra field (see below) - compressed data ending initially with empty stored block - uncompressed data filling out originally empty stored block and subsequent stored blocks as needed (16K max each) - gzip trailer - no junk at end (no other gzip streams) When appending data, the information in the first three items above plus the foo.add file are sufficient to recover an interrupted append operation. The extra field has the necessary information to restore the start of the last stored block and determine where to append the data in the foo.add file, as well as the crc and length of the gzip data before the append operation. The foo.add file is created before the gzip file is marked for append, and deleted after the gzip file is marked as complete. So if the append operation is interrupted, the data to add will still be there. If due to some external force, the foo.add file gets deleted between when the append operation was interrupted and when recovery is attempted, the gzip file will still be restored, but without the appended data. When compressing data, the information in the first two items above plus the foo.add file are sufficient to recover an interrupted compress operation. The extra field has the necessary information to find the end of the compressed data, and contains both the crc and length of just the compressed data and of the complete set of data including the contents of the foo.add file. Again, the foo.add file is maintained during the compress operation in case of an interruption. If in the unlikely event the foo.add file with the data to be compressed is missing due to some external force, a gzip file with just the previous compressed data will be reconstructed. In this case, all of the data that was to be compressed is lost (approximately one megabyte). This will not occur if all that happened was an interruption of the compress operation. The third state that is marked is the replacement of the old dictionary with the new dictionary after a compress operation. Once compression is complete, the gzip file is marked as being in the replace state. This completes the gzip file, so an interrupt after being so marked does not result in recompression. Then the dictionary file is replaced, and the gzip file is marked as completed. This state prevents the possibility of restarting compression with the wrong dictionary file. All three operations are wrapped by a lock/unlock procedure. In order to gain exclusive access to the log files, first a foo.lock file must be exclusively created. When all operations are complete, the lock is released by deleting the foo.lock file. If when attempting to create the lock file, it already exists and the modify time of the lock file is more than five minutes old (set by the PATIENCE define below), then the old lock file is considered stale and deleted, and the exclusive creation of the lock file is retried. To assure that there are no false assessments of the staleness of the lock file, the operations periodically touch the lock file to update the modified date. Following is the definition of the extra field with all of the information required to enable the above append and compress operations and their recovery if interrupted. Multi-byte values are stored little endian (consistent with the gzip format). File pointers are eight bytes long. The crc's and lengths for the gzip trailer are four bytes long. (Note that the length at the end of a gzip file is used for error checking only, and for large files is actually the length modulo 2^32.) The stored block length is two bytes long. The gzip extra field two-byte identification is "ap" for append. It is assumed that writing the extra field to the file is an "atomic" operation. That is, either all of the extra field is written to the file, or none of it is, if the operation is interrupted right at the point of updating the extra field. This is a reasonable assumption, since the extra field is within the first 52 bytes of the file, which is smaller than any expected block size for a mass storage device (usually 512 bytes or larger). Extra field (35 bytes): - Pointer to first stored block length -- this points to the two-byte length of the first stored block, which is followed by the two-byte, one's complement of that length. The stored block length is preceded by the three-bit header of the stored block, which is the actual start of the stored block in the deflate format. See the bit offset field below. - Pointer to the last stored block length. This is the same as above, but for the last stored block of the uncompressed data in the gzip file. Initially this is the same as the first stored block length pointer. When the stored block gets to 16K (see the MAX_STORE define), then a new stored block as added, at which point the last stored block length pointer is different from the first stored block length pointer. When they are different, the first bit of the last stored block header is eight bits, or one byte back from the block length. - Compressed data crc and length. This is the crc and length of the data that is in the compressed portion of the deflate stream. These are used only in the event that the foo.add file containing the data to compress is lost after a compress operation is interrupted. - Total data crc and length. This is the crc and length of all of the data stored in the gzip file, compressed and uncompressed. It is used to reconstruct the gzip trailer when compressing, as well as when recovering interrupted operations. - Final stored block length. This is used to quickly find where to append, and allows the restoration of the original final stored block state when an append operation is interrupted. - First stored block start as the number of bits back from the final stored block first length byte. This value is in the range of 3..10, and is stored as the low three bits of the final byte of the extra field after subtracting three (0..7). This allows the last-block bit of the stored block header to be updated when a new stored block is added, for the case when the first stored block and the last stored block are the same. (When they are different, the numbers of bits back is known to be eight.) This also allows for new compressed data to be appended to the old compressed data in the compress operation, overwriting the previous first stored block, or for the compressed data to be terminated and a valid gzip file reconstructed on the off chance that a compression operation was interrupted and the data to compress in the foo.add file was deleted. - The operation in process. This is the next two bits in the last byte (the bits under the mask 0x18). The are interpreted as 0: nothing in process, 1: append in process, 2: compress in process, 3: replace in process. - The top three bits of the last byte in the extra field are reserved and are currently set to zero. Main procedure: - Exclusively create the foo.lock file using the O_CREAT and O_EXCL modes of the system open() call. If the modify time of an existing lock file is more than PATIENCE seconds old, then the lock file is deleted and the exclusive create is retried. - Load the extra field from the foo.gz file, and see if an operation was in progress but not completed. If so, apply the recovery procedure below. - Perform the append procedure with the provided data. - If the uncompressed data in the foo.gz file is 1MB or more, apply the compress procedure. - Delete the foo.lock file. Append procedure: - Put what to append in the foo.add file so that the operation can be restarted if this procedure is interrupted. - Mark the foo.gz extra field with the append operation in progress. + Restore the original last-block bit and stored block length of the last stored block from the information in the extra field, in case a previous append operation was interrupted. - Append the provided data to the last stored block, creating new stored blocks as needed and updating the stored blocks last-block bits and lengths. - Update the crc and length with the new data, and write the gzip trailer. - Write over the extra field (with a single write operation) with the new pointers, lengths, and crc's, and mark the gzip file as not in process. Though there is still a foo.add file, it will be ignored since nothing is in process. If a foo.add file is leftover from a previously completed operation, it is truncated when writing new data to it. - Delete the foo.add file. Compress and replace procedures: - Read all of the uncompressed data in the stored blocks in foo.gz and write it to foo.add. Also write foo.temp with the last 32K of that data to provide a dictionary for the next invocation of this procedure. - Rewrite the extra field marking foo.gz with a compression in process. * If there is no data provided to compress (due to a missing foo.add file when recovering), reconstruct and truncate the foo.gz file to contain only the previous compressed data and proceed to the step after the next one. Otherwise ... - Compress the data with the dictionary in foo.dict, and write to the foo.gz file starting at the bit immediately following the last previously compressed block. If there is no foo.dict, proceed anyway with the compression at slightly reduced efficiency. (For the foo.dict file to be missing requires some external failure beyond simply the interruption of a compress operation.) During this process, the foo.lock file is periodically touched to assure that that file is not considered stale by another process before we're done. The deflation is terminated with a non-last empty static block (10 bits long), that is then located and written over by a last-bit-set empty stored block. - Append the crc and length of the data in the gzip file (previously calculated during the append operations). - Write over the extra field with the updated stored block offsets, bits back, crc's, and lengths, and mark foo.gz as in process for a replacement of the dictionary. @ Delete the foo.add file. - Replace foo.dict with foo.temp. - Write over the extra field, marking foo.gz as complete. Recovery procedure: - If not a replace recovery, read in the foo.add file, and provide that data to the appropriate recovery below. If there is no foo.add file, provide a zero data length to the recovery. In that case, the append recovery restores the foo.gz to the previous compressed + uncompressed data state. For the compress recovery, a missing foo.add file results in foo.gz being restored to the previous compressed-only data state. - Append recovery: - Pick up append at + step above - Compress recovery: - Pick up compress at * step above - Replace recovery: - Pick up compress at @ step above - Log the repair with a date stamp in foo.repairs */ #include <sys/types.h> #include <stdio.h> /* rename, fopen, fprintf, fclose */ #include <stdlib.h> /* malloc, free */ #include <string.h> /* strlen, strrchr, strcpy, strncpy, strcmp */ #include <fcntl.h> /* open */ #include <unistd.h> /* lseek, read, write, close, unlink, sleep, */ /* ftruncate, fsync */ #include <errno.h> /* errno */ #include <time.h> /* time, ctime */ #include <sys/stat.h> /* stat */ #include <sys/time.h> /* utimes */ #include "zlib.h" /* crc32 */ #include "gzlog.h" /* header for external access */ #define local static typedef unsigned int uint; typedef unsigned long ulong; /* Macro for debugging to deterministically force recovery operations */ #ifdef GZLOG_DEBUG #include <setjmp.h> /* longjmp */ jmp_buf gzlog_jump; /* where to go back to */ int gzlog_bail = 0; /* which point to bail at (1..8) */ int gzlog_count = -1; /* number of times through to wait */ # define BAIL(n) do { if (n == gzlog_bail && gzlog_count-- == 0) \ longjmp(gzlog_jump, gzlog_bail); } while (0) #else # define BAIL(n) #endif /* how old the lock file can be in seconds before considering it stale */ #define PATIENCE 300 /* maximum stored block size in Kbytes -- must be in 1..63 */ #define MAX_STORE 16 /* number of stored Kbytes to trigger compression (must be >= 32 to allow dictionary construction, and <= 204 * MAX_STORE, in order for >> 10 to discard the stored block headers contribution of five bytes each) */ #define TRIGGER 1024 /* size of a deflate dictionary (this cannot be changed) */ #define DICT 32768U /* values for the operation (2 bits) */ #define NO_OP 0 #define APPEND_OP 1 #define COMPRESS_OP 2 #define REPLACE_OP 3 /* macros to extract little-endian integers from an unsigned byte buffer */ #define PULL2(p) ((p)[0]+((uint)((p)[1])<<8)) #define PULL4(p) (PULL2(p)+((ulong)PULL2(p+2)<<16)) #define PULL8(p) (PULL4(p)+((off_t)PULL4(p+4)<<32)) /* macros to store integers into a byte buffer in little-endian order */ #define PUT2(p,a) do {(p)[0]=a;(p)[1]=(a)>>8;} while(0) #define PUT4(p,a) do {PUT2(p,a);PUT2(p+2,a>>16);} while(0) #define PUT8(p,a) do {PUT4(p,a);PUT4(p+4,a>>32);} while(0) /* internal structure for log information */ #define LOGID "\106\035\172" /* should be three non-zero characters */ struct log { char id[4]; /* contains LOGID to detect inadvertent overwrites */ int fd; /* file descriptor for .gz file, opened read/write */ char *path; /* allocated path, e.g. "/var/log/foo" or "foo" */ char *end; /* end of path, for appending suffices such as ".gz" */ off_t first; /* offset of first stored block first length byte */ int back; /* location of first block id in bits back from first */ uint stored; /* bytes currently in last stored block */ off_t last; /* offset of last stored block first length byte */ ulong ccrc; /* crc of compressed data */ ulong clen; /* length (modulo 2^32) of compressed data */ ulong tcrc; /* crc of total data */ ulong tlen; /* length (modulo 2^32) of total data */ time_t lock; /* last modify time of our lock file */ }; /* gzip header for gzlog */ local unsigned char log_gzhead[] = { 0x1f, 0x8b, /* magic gzip id */ 8, /* compression method is deflate */ 4, /* there is an extra field (no file name) */ 0, 0, 0, 0, /* no modification time provided */ 0, 0xff, /* no extra flags, no OS specified */ 39, 0, 'a', 'p', 35, 0 /* extra field with "ap" subfield */ /* 35 is EXTRA, 39 is EXTRA + 4 */ }; #define HEAD sizeof(log_gzhead) /* should be 16 */ /* initial gzip extra field content (52 == HEAD + EXTRA + 1) */ local unsigned char log_gzext[] = { 52, 0, 0, 0, 0, 0, 0, 0, /* offset of first stored block length */ 52, 0, 0, 0, 0, 0, 0, 0, /* offset of last stored block length */ 0, 0, 0, 0, 0, 0, 0, 0, /* compressed data crc and length */ 0, 0, 0, 0, 0, 0, 0, 0, /* total data crc and length */ 0, 0, /* final stored block data length */ 5 /* op is NO_OP, last bit 8 bits back */ }; #define EXTRA sizeof(log_gzext) /* should be 35 */ /* initial gzip data and trailer */ local unsigned char log_gzbody[] = { 1, 0, 0, 0xff, 0xff, /* empty stored block (last) */ 0, 0, 0, 0, /* crc */ 0, 0, 0, 0 /* uncompressed length */ }; #define BODY sizeof(log_gzbody) /* Exclusively create foo.lock in order to negotiate exclusive access to the foo.* files. If the modify time of an existing lock file is greater than PATIENCE seconds in the past, then consider the lock file to have been abandoned, delete it, and try the exclusive create again. Save the lock file modify time for verification of ownership. Return 0 on success, or -1 on failure, usually due to an access restriction or invalid path. Note that if stat() or unlink() fails, it may be due to another process noticing the abandoned lock file a smidge sooner and deleting it, so those are not flagged as an error. */ local int log_lock(struct log *log) { int fd; struct stat st; strcpy(log->end, ".lock"); while ((fd = open(log->path, O_CREAT | O_EXCL, 0644)) < 0) { if (errno != EEXIST) return -1; if (stat(log->path, &st) == 0 && time(NULL) - st.st_mtime > PATIENCE) { unlink(log->path); continue; } sleep(2); /* relinquish the CPU for two seconds while waiting */ } close(fd); if (stat(log->path, &st) == 0) log->lock = st.st_mtime; return 0; } /* Update the modify time of the lock file to now, in order to prevent another task from thinking that the lock is stale. Save the lock file modify time for verification of ownership. */ local void log_touch(struct log *log) { struct stat st; strcpy(log->end, ".lock"); utimes(log->path, NULL); if (stat(log->path, &st) == 0) log->lock = st.st_mtime; } /* Check the log file modify time against what is expected. Return true if this is not our lock. If it is our lock, touch it to keep it. */ local int log_check(struct log *log) { struct stat st; strcpy(log->end, ".lock"); if (stat(log->path, &st) || st.st_mtime != log->lock) return 1; log_touch(log); return 0; } /* Unlock a previously acquired lock, but only if it's ours. */ local void log_unlock(struct log *log) { if (log_check(log)) return; strcpy(log->end, ".lock"); unlink(log->path); log->lock = 0; } /* Check the gzip header and read in the extra field, filling in the values in the log structure. Return op on success or -1 if the gzip header was not as expected. op is the current operation in progress last written to the extra field. This assumes that the gzip file has already been opened, with the file descriptor log->fd. */ local int log_head(struct log *log) { int op; unsigned char buf[HEAD + EXTRA]; if (lseek(log->fd, 0, SEEK_SET) < 0 || read(log->fd, buf, HEAD + EXTRA) != HEAD + EXTRA || memcmp(buf, log_gzhead, HEAD)) { return -1; } log->first = PULL8(buf + HEAD); log->last = PULL8(buf + HEAD + 8); log->ccrc = PULL4(buf + HEAD + 16); log->clen = PULL4(buf + HEAD + 20); log->tcrc = PULL4(buf + HEAD + 24); log->tlen = PULL4(buf + HEAD + 28); log->stored = PULL2(buf + HEAD + 32); log->back = 3 + (buf[HEAD + 34] & 7); op = (buf[HEAD + 34] >> 3) & 3; return op; } /* Write over the extra field contents, marking the operation as op. Use fsync to assure that the device is written to, and in the requested order. This operation, and only this operation, is assumed to be atomic in order to assure that the log is recoverable in the event of an interruption at any point in the process. Return -1 if the write to foo.gz failed. */ local int log_mark(struct log *log, int op) { int ret; unsigned char ext[EXTRA]; PUT8(ext, log->first); PUT8(ext + 8, log->last); PUT4(ext + 16, log->ccrc); PUT4(ext + 20, log->clen); PUT4(ext + 24, log->tcrc); PUT4(ext + 28, log->tlen); PUT2(ext + 32, log->stored); ext[34] = log->back - 3 + (op << 3); fsync(log->fd); ret = lseek(log->fd, HEAD, SEEK_SET) < 0 || write(log->fd, ext, EXTRA) != EXTRA ? -1 : 0; fsync(log->fd); return ret; } /* Rewrite the last block header bits and subsequent zero bits to get to a byte boundary, setting the last block bit if last is true, and then write the remainder of the stored block header (length and one's complement). Leave the file pointer after the end of the last stored block data. Return -1 if there is a read or write failure on the foo.gz file */ local int log_last(struct log *log, int last) { int back, len, mask; unsigned char buf[6]; /* determine the locations of the bytes and bits to modify */ back = log->last == log->first ? log->back : 8; len = back > 8 ? 2 : 1; /* bytes back from log->last */ mask = 0x80 >> ((back - 1) & 7); /* mask for block last-bit */ /* get the byte to modify (one or two back) into buf[0] -- don't need to read the byte if the last-bit is eight bits back, since in that case the entire byte will be modified */ buf[0] = 0; if (back != 8 && (lseek(log->fd, log->last - len, SEEK_SET) < 0 || read(log->fd, buf, 1) != 1)) return -1; /* change the last-bit of the last stored block as requested -- note that all bits above the last-bit are set to zero, per the type bits of a stored block being 00 and per the convention that the bits to bring the stream to a byte boundary are also zeros */ buf[1] = 0; buf[2 - len] = (*buf & (mask - 1)) + (last ? mask : 0); /* write the modified stored block header and lengths, move the file pointer to after the last stored block data */ PUT2(buf + 2, log->stored); PUT2(buf + 4, log->stored ^ 0xffff); return lseek(log->fd, log->last - len, SEEK_SET) < 0 || write(log->fd, buf + 2 - len, len + 4) != len + 4 || lseek(log->fd, log->stored, SEEK_CUR) < 0 ? -1 : 0; } /* Append len bytes from data to the locked and open log file. len may be zero if recovering and no .add file was found. In that case, the previous state of the foo.gz file is restored. The data is appended uncompressed in deflate stored blocks. Return -1 if there was an error reading or writing the foo.gz file. */ local int log_append(struct log *log, unsigned char *data, size_t len) { uint put; off_t end; unsigned char buf[8]; /* set the last block last-bit and length, in case recovering an interrupted append, then position the file pointer to append to the block */ if (log_last(log, 1)) return -1; /* append, adding stored blocks and updating the offset of the last stored block as needed, and update the total crc and length */ while (len) { /* append as much as we can to the last block */ put = (MAX_STORE << 10) - log->stored; if (put > len) put = (uint)len; if (put) { if (write(log->fd, data, put) != put) return -1; BAIL(1); log->tcrc = crc32(log->tcrc, data, put); log->tlen += put; log->stored += put; data += put; len -= put; } /* if we need to, add a new empty stored block */ if (len) { /* mark current block as not last */ if (log_last(log, 0)) return -1; /* point to new, empty stored block */ log->last += 4 + log->stored + 1; log->stored = 0; } /* mark last block as last, update its length */ if (log_last(log, 1)) return -1; BAIL(2); } /* write the new crc and length trailer, and truncate just in case (could be recovering from partial append with a missing foo.add file) */ PUT4(buf, log->tcrc); PUT4(buf + 4, log->tlen); if (write(log->fd, buf, 8) != 8 || (end = lseek(log->fd, 0, SEEK_CUR)) < 0 || ftruncate(log->fd, end)) return -1; /* write the extra field, marking the log file as done, delete .add file */ if (log_mark(log, NO_OP)) return -1; strcpy(log->end, ".add"); unlink(log->path); /* ignore error, since may not exist */ return 0; } /* Replace the foo.dict file with the foo.temp file. Also delete the foo.add file, since the compress operation may have been interrupted before that was done. Returns 1 if memory could not be allocated, or -1 if reading or writing foo.gz fails, or if the rename fails for some reason other than foo.temp not existing. foo.temp not existing is a permitted error, since the replace operation may have been interrupted after the rename is done, but before foo.gz is marked as complete. */ local int log_replace(struct log *log) { int ret; char *dest; /* delete foo.add file */ strcpy(log->end, ".add"); unlink(log->path); /* ignore error, since may not exist */ BAIL(3); /* rename foo.name to foo.dict, replacing foo.dict if it exists */ strcpy(log->end, ".dict"); dest = malloc(strlen(log->path) + 1); if (dest == NULL) return -2; strcpy(dest, log->path); strcpy(log->end, ".temp"); ret = rename(log->path, dest); free(dest); if (ret && errno != ENOENT) return -1; BAIL(4); /* mark the foo.gz file as done */ return log_mark(log, NO_OP); } /* Compress the len bytes at data and append the compressed data to the foo.gz deflate data immediately after the previous compressed data. This overwrites the previous uncompressed data, which was stored in foo.add and is the data provided in data[0..len-1]. If this operation is interrupted, it picks up at the start of this routine, with the foo.add file read in again. If there is no data to compress (len == 0), then we simply terminate the foo.gz file after the previously compressed data, appending a final empty stored block and the gzip trailer. Return -1 if reading or writing the log.gz file failed, or -2 if there was a memory allocation failure. */ local int log_compress(struct log *log, unsigned char *data, size_t len) { int fd; uint got, max; ssize_t dict; off_t end; z_stream strm; unsigned char buf[DICT]; /* compress and append compressed data */ if (len) { /* set up for deflate, allocating memory */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) return -2; /* read in dictionary (last 32K of data that was compressed) */ strcpy(log->end, ".dict"); fd = open(log->path, O_RDONLY, 0); if (fd >= 0) { dict = read(fd, buf, DICT); close(fd); if (dict < 0) { deflateEnd(&strm); return -1; } if (dict) deflateSetDictionary(&strm, buf, (uint)dict); } log_touch(log); /* prime deflate with last bits of previous block, position write pointer to write those bits and overwrite what follows */ if (lseek(log->fd, log->first - (log->back > 8 ? 2 : 1), SEEK_SET) < 0 || read(log->fd, buf, 1) != 1 || lseek(log->fd, -1, SEEK_CUR) < 0) { deflateEnd(&strm); return -1; } deflatePrime(&strm, (8 - log->back) & 7, *buf); /* compress, finishing with a partial non-last empty static block */ strm.next_in = data; max = (((uint)0 - 1) >> 1) + 1; /* in case int smaller than size_t */ do { strm.avail_in = len > max ? max : (uint)len; len -= strm.avail_in; do { strm.avail_out = DICT; strm.next_out = buf; deflate(&strm, len ? Z_NO_FLUSH : Z_PARTIAL_FLUSH); got = DICT - strm.avail_out; if (got && write(log->fd, buf, got) != got) { deflateEnd(&strm); return -1; } log_touch(log); } while (strm.avail_out == 0); } while (len); deflateEnd(&strm); BAIL(5); /* find start of empty static block -- scanning backwards the first one bit is the second bit of the block, if the last byte is zero, then we know the byte before that has a one in the top bit, since an empty static block is ten bits long */ if ((log->first = lseek(log->fd, -1, SEEK_CUR)) < 0 || read(log->fd, buf, 1) != 1) return -1; log->first++; if (*buf) { log->back = 1; while ((*buf & ((uint)1 << (8 - log->back++))) == 0) ; /* guaranteed to terminate, since *buf != 0 */ } else log->back = 10; /* update compressed crc and length */ log->ccrc = log->tcrc; log->clen = log->tlen; } else { /* no data to compress -- fix up existing gzip stream */ log->tcrc = log->ccrc; log->tlen = log->clen; } /* complete and truncate gzip stream */ log->last = log->first; log->stored = 0; PUT4(buf, log->tcrc); PUT4(buf + 4, log->tlen); if (log_last(log, 1) || write(log->fd, buf, 8) != 8 || (end = lseek(log->fd, 0, SEEK_CUR)) < 0 || ftruncate(log->fd, end)) return -1; BAIL(6); /* mark as being in the replace operation */ if (log_mark(log, REPLACE_OP)) return -1; /* execute the replace operation and mark the file as done */ return log_replace(log); } /* log a repair record to the .repairs file */ local void log_log(struct log *log, int op, char *record) { time_t now; FILE *rec; now = time(NULL); strcpy(log->end, ".repairs"); rec = fopen(log->path, "a"); if (rec == NULL) return; fprintf(rec, "%.24s %s recovery: %s\n", ctime(&now), op == APPEND_OP ? "append" : (op == COMPRESS_OP ? "compress" : "replace"), record); fclose(rec); return; } /* Recover the interrupted operation op. First read foo.add for recovering an append or compress operation. Return -1 if there was an error reading or writing foo.gz or reading an existing foo.add, or -2 if there was a memory allocation failure. */ local int log_recover(struct log *log, int op) { int fd, ret = 0; unsigned char *data = NULL; size_t len = 0; struct stat st; /* log recovery */ log_log(log, op, "start"); /* load foo.add file if expected and present */ if (op == APPEND_OP || op == COMPRESS_OP) { strcpy(log->end, ".add"); if (stat(log->path, &st) == 0 && st.st_size) { len = (size_t)(st.st_size); if ((off_t)len != st.st_size || (data = malloc(st.st_size)) == NULL) { log_log(log, op, "allocation failure"); return -2; } if ((fd = open(log->path, O_RDONLY, 0)) < 0) { free(data); log_log(log, op, ".add file read failure"); return -1; } ret = (size_t)read(fd, data, len) != len; close(fd); if (ret) { free(data); log_log(log, op, ".add file read failure"); return -1; } log_log(log, op, "loaded .add file"); } else log_log(log, op, "missing .add file!"); } /* recover the interrupted operation */ switch (op) { case APPEND_OP: ret = log_append(log, data, len); break; case COMPRESS_OP: ret = log_compress(log, data, len); break; case REPLACE_OP: ret = log_replace(log); } /* log status */ log_log(log, op, ret ? "failure" : "complete"); /* clean up */ if (data != NULL) free(data); return ret; } /* Close the foo.gz file (if open) and release the lock. */ local void log_close(struct log *log) { if (log->fd >= 0) close(log->fd); log->fd = -1; log_unlock(log); } /* Open foo.gz, verify the header, and load the extra field contents, after first creating the foo.lock file to gain exclusive access to the foo.* files. If foo.gz does not exist or is empty, then write the initial header, extra, and body content of an empty foo.gz log file. If there is an error creating the lock file due to access restrictions, or an error reading or writing the foo.gz file, or if the foo.gz file is not a proper log file for this object (e.g. not a gzip file or does not contain the expected extra field), then return true. If there is an error, the lock is released. Otherwise, the lock is left in place. */ local int log_open(struct log *log) { int op; /* release open file resource if left over -- can occur if lock lost between gzlog_open() and gzlog_write() */ if (log->fd >= 0) close(log->fd); log->fd = -1; /* negotiate exclusive access */ if (log_lock(log) < 0) return -1; /* open the log file, foo.gz */ strcpy(log->end, ".gz"); log->fd = open(log->path, O_RDWR | O_CREAT, 0644); if (log->fd < 0) { log_close(log); return -1; } /* if new, initialize foo.gz with an empty log, delete old dictionary */ if (lseek(log->fd, 0, SEEK_END) == 0) { if (write(log->fd, log_gzhead, HEAD) != HEAD || write(log->fd, log_gzext, EXTRA) != EXTRA || write(log->fd, log_gzbody, BODY) != BODY) { log_close(log); return -1; } strcpy(log->end, ".dict"); unlink(log->path); } /* verify log file and load extra field information */ if ((op = log_head(log)) < 0) { log_close(log); return -1; } /* check for interrupted process and if so, recover */ if (op != NO_OP && log_recover(log, op)) { log_close(log); return -1; } /* touch the lock file to prevent another process from grabbing it */ log_touch(log); return 0; } /* See gzlog.h for the description of the external methods below */ gzlog *gzlog_open(char *path) { size_t n; struct log *log; /* check arguments */ if (path == NULL || *path == 0) return NULL; /* allocate and initialize log structure */ log = malloc(sizeof(struct log)); if (log == NULL) return NULL; strcpy(log->id, LOGID); log->fd = -1; /* save path and end of path for name construction */ n = strlen(path); log->path = malloc(n + 9); /* allow for ".repairs" */ if (log->path == NULL) { free(log); return NULL; } strcpy(log->path, path); log->end = log->path + n; /* gain exclusive access and verify log file -- may perform a recovery operation if needed */ if (log_open(log)) { free(log->path); free(log); return NULL; } /* return pointer to log structure */ return log; } /* gzlog_compress() return values: 0: all good -1: file i/o error (usually access issue) -2: memory allocation failure -3: invalid log pointer argument */ int gzlog_compress(gzlog *logd) { int fd, ret; uint block; size_t len, next; unsigned char *data, buf[5]; struct log *log = logd; /* check arguments */ if (log == NULL || strcmp(log->id, LOGID)) return -3; /* see if we lost the lock -- if so get it again and reload the extra field information (it probably changed), recover last operation if necessary */ if (log_check(log) && log_open(log)) return -1; /* create space for uncompressed data */ len = ((size_t)(log->last - log->first) & ~(((size_t)1 << 10) - 1)) + log->stored; if ((data = malloc(len)) == NULL) return -2; /* do statement here is just a cheap trick for error handling */ do { /* read in the uncompressed data */ if (lseek(log->fd, log->first - 1, SEEK_SET) < 0) break; next = 0; while (next < len) { if (read(log->fd, buf, 5) != 5) break; block = PULL2(buf + 1); if (next + block > len || read(log->fd, (char *)data + next, block) != block) break; next += block; } if (lseek(log->fd, 0, SEEK_CUR) != log->last + 4 + log->stored) break; log_touch(log); /* write the uncompressed data to the .add file */ strcpy(log->end, ".add"); fd = open(log->path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) break; ret = (size_t)write(fd, data, len) != len; if (ret | close(fd)) break; log_touch(log); /* write the dictionary for the next compress to the .temp file */ strcpy(log->end, ".temp"); fd = open(log->path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) break; next = DICT > len ? len : DICT; ret = (size_t)write(fd, (char *)data + len - next, next) != next; if (ret | close(fd)) break; log_touch(log); /* roll back to compressed data, mark the compress in progress */ log->last = log->first; log->stored = 0; if (log_mark(log, COMPRESS_OP)) break; BAIL(7); /* compress and append the data (clears mark) */ ret = log_compress(log, data, len); free(data); return ret; } while (0); /* broke out of do above on i/o error */ free(data); return -1; } /* gzlog_write() return values: 0: all good -1: file i/o error (usually access issue) -2: memory allocation failure -3: invalid log pointer argument */ int gzlog_write(gzlog *logd, void *data, size_t len) { int fd, ret; struct log *log = logd; /* check arguments */ if (log == NULL || strcmp(log->id, LOGID)) return -3; if (data == NULL || len <= 0) return 0; /* see if we lost the lock -- if so get it again and reload the extra field information (it probably changed), recover last operation if necessary */ if (log_check(log) && log_open(log)) return -1; /* create and write .add file */ strcpy(log->end, ".add"); fd = open(log->path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) return -1; ret = (size_t)write(fd, data, len) != len; if (ret | close(fd)) return -1; log_touch(log); /* mark log file with append in progress */ if (log_mark(log, APPEND_OP)) return -1; BAIL(8); /* append data (clears mark) */ if (log_append(log, data, len)) return -1; /* check to see if it's time to compress -- if not, then done */ if (((log->last - log->first) >> 10) + (log->stored >> 10) < TRIGGER) return 0; /* time to compress */ return gzlog_compress(log); } /* gzlog_close() return values: 0: ok -3: invalid log pointer argument */ int gzlog_close(gzlog *logd) { struct log *log = logd; /* check arguments */ if (log == NULL || strcmp(log->id, LOGID)) return -3; /* close the log file and release the lock */ log_close(log); /* free structure and return */ if (log->path != NULL) free(log->path); strcpy(log->id, "bad"); free(log); return 0; } |
Added compat/zlib/examples/gzlog.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* gzlog.h Copyright (C) 2004, 2008, 2012 Mark Adler, all rights reserved version 2.2, 14 Aug 2012 This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Mark Adler madler@alumni.caltech.edu */ /* Version History: 1.0 26 Nov 2004 First version 2.0 25 Apr 2008 Complete redesign for recovery of interrupted operations Interface changed slightly in that now path is a prefix Compression now occurs as needed during gzlog_write() gzlog_write() now always leaves the log file as valid gzip 2.1 8 Jul 2012 Fix argument checks in gzlog_compress() and gzlog_write() 2.2 14 Aug 2012 Clean up signed comparisons */ /* The gzlog object allows writing short messages to a gzipped log file, opening the log file locked for small bursts, and then closing it. The log object works by appending stored (uncompressed) data to the gzip file until 1 MB has been accumulated. At that time, the stored data is compressed, and replaces the uncompressed data in the file. The log file is truncated to its new size at that time. After each write operation, the log file is a valid gzip file that can decompressed to recover what was written. The gzlog operations can be interrupted at any point due to an application or system crash, and the log file will be recovered the next time the log is opened with gzlog_open(). */ #ifndef GZLOG_H #define GZLOG_H /* gzlog object type */ typedef void gzlog; /* Open a gzlog object, creating the log file if it does not exist. Return NULL on error. Note that gzlog_open() could take a while to complete if it has to wait to verify that a lock is stale (possibly for five minutes), or if there is significant contention with other instantiations of this object when locking the resource. path is the prefix of the file names created by this object. If path is "foo", then the log file will be "foo.gz", and other auxiliary files will be created and destroyed during the process: "foo.dict" for a compression dictionary, "foo.temp" for a temporary (next) dictionary, "foo.add" for data being added or compressed, "foo.lock" for the lock file, and "foo.repairs" to log recovery operations performed due to interrupted gzlog operations. A gzlog_open() followed by a gzlog_close() will recover a previously interrupted operation, if any. */ gzlog *gzlog_open(char *path); /* Write to a gzlog object. Return zero on success, -1 if there is a file i/o error on any of the gzlog files (this should not happen if gzlog_open() succeeded, unless the device has run out of space or leftover auxiliary files have permissions or ownership that prevent their use), -2 if there is a memory allocation failure, or -3 if the log argument is invalid (e.g. if it was not created by gzlog_open()). This function will write data to the file uncompressed, until 1 MB has been accumulated, at which time that data will be compressed. The log file will be a valid gzip file upon successful return. */ int gzlog_write(gzlog *log, void *data, size_t len); /* Force compression of any uncompressed data in the log. This should be used sparingly, if at all. The main application would be when a log file will not be appended to again. If this is used to compress frequently while appending, it will both significantly increase the execution time and reduce the compression ratio. The return codes are the same as for gzlog_write(). */ int gzlog_compress(gzlog *log); /* Close a gzlog object. Return zero on success, -3 if the log argument is invalid. The log object is freed, and so cannot be referenced again. */ int gzlog_close(gzlog *log); #endif |
Added compat/zlib/examples/gznorm.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 | /* gznorm.c -- normalize a gzip stream * Copyright (C) 2018 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * Version 1.0 7 Oct 2018 Mark Adler */ // gznorm takes a gzip stream, potentially containing multiple members, and // converts it to a gzip stream with a single member. In addition the gzip // header is normalized, removing the file name and time stamp, and setting the // other header contents (XFL, OS) to fixed values. gznorm does not recompress // the data, so it is fast, but no advantage is gained from the history that // could be available across member boundaries. #include <stdio.h> // fread, fwrite, putc, fflush, ferror, fprintf, // vsnprintf, stdout, stderr, NULL, FILE #include <stdlib.h> // malloc, free #include <string.h> // strerror #include <errno.h> // errno #include <stdarg.h> // va_list, va_start, va_end #include "zlib.h" // inflateInit2, inflate, inflateReset, inflateEnd, // z_stream, z_off_t, crc32_combine, Z_NULL, Z_BLOCK, // Z_OK, Z_STREAM_END, Z_BUF_ERROR, Z_DATA_ERROR, // Z_MEM_ERROR #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) # include <fcntl.h> # include <io.h> # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) #else # define SET_BINARY_MODE(file) #endif #define local static // printf to an allocated string. Return the string, or NULL if the printf or // allocation fails. local char *aprintf(char *fmt, ...) { // Get the length of the result of the printf. va_list args; va_start(args, fmt); int len = vsnprintf(NULL, 0, fmt, args); va_end(args); if (len < 0) return NULL; // Allocate the required space and printf to it. char *str = malloc(len + 1); if (str == NULL) return NULL; va_start(args, fmt); vsnprintf(str, len + 1, fmt, args); va_end(args); return str; } // Return with an error, putting an allocated error message in *err. Doing an // inflateEnd() on an already ended state, or one with state set to Z_NULL, is // permitted. #define BYE(...) \ do { \ inflateEnd(&strm); \ *err = aprintf(__VA_ARGS__); \ return 1; \ } while (0) // Chunk size for buffered reads and for decompression. Twice this many bytes // will be allocated on the stack by gzip_normalize(). Must fit in an unsigned. #define CHUNK 16384 // Read a gzip stream from in and write an equivalent normalized gzip stream to // out. If given no input, an empty gzip stream will be written. If successful, // 0 is returned, and *err is set to NULL. On error, 1 is returned, where the // details of the error are returned in *err, a pointer to an allocated string. // // The input may be a stream with multiple gzip members, which is converted to // a single gzip member on the output. Each gzip member is decompressed at the // level of deflate blocks. This enables clearing the last-block bit, shifting // the compressed data to concatenate to the previous member's compressed data, // which can end at an arbitrary bit boundary, and identifying stored blocks in // order to resynchronize those to byte boundaries. The deflate compressed data // is terminated with a 10-bit empty fixed block. If any members on the input // end with a 10-bit empty fixed block, then that block is excised from the // stream. This avoids appending empty fixed blocks for every normalization, // and assures that gzip_normalize applied a second time will not change the // input. The pad bits after stored block headers and after the final deflate // block are all forced to zeros. local int gzip_normalize(FILE *in, FILE *out, char **err) { // initialize the inflate engine to process a gzip member z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; if (inflateInit2(&strm, 15 + 16) != Z_OK) BYE("out of memory"); // State while processing the input gzip stream. enum { // BETWEEN -> HEAD -> BLOCK -> TAIL -> BETWEEN -> ... BETWEEN, // between gzip members (must end in this state) HEAD, // reading a gzip header BLOCK, // reading deflate blocks TAIL // reading a gzip trailer } state = BETWEEN; // current component being processed unsigned long crc = 0; // accumulated CRC of uncompressed data unsigned long len = 0; // accumulated length of uncompressed data unsigned long buf = 0; // deflate stream bit buffer of num bits int num = 0; // number of bits in buf (at bottom) // Write a canonical gzip header (no mod time, file name, comment, extra // block, or extra flags, and OS is marked as unknown). fwrite("\x1f\x8b\x08\0\0\0\0\0\0\xff", 1, 10, out); // Process the gzip stream from in until reaching the end of the input, // encountering invalid input, or experiencing an i/o error. int more; // true if not at the end of the input do { // State inside this loop. unsigned char *put; // next input buffer location to process int prev; // number of bits from previous block in // the bit buffer, or -1 if not at the // start of a block unsigned long long memb; // uncompressed length of member size_t tail; // number of trailer bytes read (0..8) unsigned long part; // accumulated trailer component // Get the next chunk of input from in. unsigned char dat[CHUNK]; strm.avail_in = fread(dat, 1, CHUNK, in); if (strm.avail_in == 0) break; more = strm.avail_in == CHUNK; strm.next_in = put = dat; // Run that chunk of input through the inflate engine to exhaustion. do { // At this point it is assured that strm.avail_in > 0. // Inflate until the end of a gzip component (header, deflate // block, trailer) is reached, or until all of the chunk is // consumed. The resulting decompressed data is discarded, though // the total size of the decompressed data in each member is // tracked, for the calculation of the total CRC. do { // inflate and handle any errors unsigned char scrap[CHUNK]; strm.avail_out = CHUNK; strm.next_out = scrap; int ret = inflate(&strm, Z_BLOCK); if (ret == Z_MEM_ERROR) BYE("out of memory"); if (ret == Z_DATA_ERROR) BYE("input invalid: %s", strm.msg); if (ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_STREAM_END) BYE("internal error"); // Update the number of uncompressed bytes generated in this // member. The actual count (not modulo 2^32) is required to // correctly compute the total CRC. unsigned got = CHUNK - strm.avail_out; memb += got; if (memb < got) BYE("overflow error"); // Continue to process this chunk until it is consumed, or // until the end of a component (header, deflate block, or // trailer) is reached. } while (strm.avail_out == 0 && (strm.data_type & 0x80) == 0); // Since strm.avail_in was > 0 for the inflate call, some input was // just consumed. It is therefore assured that put < strm.next_in. // Disposition the consumed component or part of a component. switch (state) { case BETWEEN: state = HEAD; // Fall through to HEAD when some or all of the header is // processed. case HEAD: // Discard the header. if (strm.data_type & 0x80) { // End of header reached -- deflate blocks follow. put = strm.next_in; prev = num; memb = 0; state = BLOCK; } break; case BLOCK: // Copy the deflate stream to the output, but with the // last-block-bit cleared. Re-synchronize stored block // headers to the output byte boundaries. The bytes at // put..strm.next_in-1 is the compressed data that has been // processed and is ready to be copied to the output. // At this point, it is assured that new compressed data is // available, i.e., put < strm.next_in. If prev is -1, then // that compressed data starts in the middle of a deflate // block. If prev is not -1, then the bits in the bit // buffer, possibly combined with the bits in *put, contain // the three-bit header of the new deflate block. In that // case, prev is the number of bits from the previous block // that remain in the bit buffer. Since num is the number // of bits in the bit buffer, we have that num - prev is // the number of bits from the new block currently in the // bit buffer. // If strm.data_type & 0xc0 is 0x80, then the last byte of // the available compressed data includes the last bits of // the end of a deflate block. In that case, that last byte // also has strm.data_type & 0x1f bits of the next deflate // block, in the range 0..7. If strm.data_type & 0xc0 is // 0xc0, then the last byte of the compressed data is the // end of the deflate stream, followed by strm.data_type & // 0x1f pad bits, also in the range 0..7. // Set bits to the number of bits not yet consumed from the // last byte. If we are at the end of the block, bits is // either the number of bits in the last byte belonging to // the next block, or the number of pad bits after the // final block. In either of those cases, bits is in the // range 0..7. ; // (required due to C syntax oddity) int bits = strm.data_type & 0x1f; if (prev != -1) { // We are at the start of a new block. Clear the last // block bit, and check for special cases. If it is a // stored block, then emit the header and pad to the // next byte boundary. If it is a final, empty fixed // block, then excise it. // Some or all of the three header bits for this block // may already be in the bit buffer. Load any remaining // header bits into the bit buffer. if (num - prev < 3) { buf += (unsigned long)*put++ << num; num += 8; } // Set last to have a 1 in the position of the last // block bit in the bit buffer. unsigned long last = (unsigned long)1 << prev; if (((buf >> prev) & 7) == 3) { // This is a final fixed block. Load at least ten // bits from this block, including the header, into // the bit buffer. We already have at least three, // so at most one more byte needs to be loaded. if (num - prev < 10) { if (put == strm.next_in) // Need to go get and process more input. // We'll end up back here to finish this. break; buf += (unsigned long)*put++ << num; num += 8; } if (((buf >> prev) & 0x3ff) == 3) { // That final fixed block is empty. Delete it // to avoid adding an empty block every time a // gzip stream is normalized. num = prev; buf &= last - 1; // zero the pad bits } } else if (((buf >> prev) & 6) == 0) { // This is a stored block. Flush to the next // byte boundary after the three-bit header. num = (prev + 10) & ~7; buf &= last - 1; // zero the pad bits } // Clear the last block bit. buf &= ~last; // Write out complete bytes in the bit buffer. while (num >= 8) { putc(buf, out); buf >>= 8; num -= 8; } // If no more bytes left to process, then we have // consumed the byte that had bits from the next block. if (put == strm.next_in) bits = 0; } // We are done handling the deflate block header. Now copy // all or almost all of the remaining compressed data that // has been processed so far. Don't copy one byte at the // end if it contains bits from the next deflate block or // pad bits at the end of a deflate block. // mix is 1 if we are at the end of a deflate block, and if // some of the bits in the last byte follow this block. mix // is 0 if we are in the middle of a deflate block, if the // deflate block ended on a byte boundary, or if all of the // compressed data processed so far has been consumed. int mix = (strm.data_type & 0x80) && bits; // Copy all of the processed compressed data to the output, // except for the last byte if it contains bits from the // next deflate block or pad bits at the end of the deflate // stream. Copy the data after shifting in num bits from // buf in front of it, leaving num bits from the end of the // compressed data in buf when done. unsigned char *end = strm.next_in - mix; if (put < end) { if (num) // Insert num bits from buf before the data being // copied. do { buf += (unsigned)(*put++) << num; putc(buf, out); buf >>= 8; } while (put < end); else { // No shifting needed -- write directly. fwrite(put, 1, end - put, out); put = end; } } // Process the last processed byte if it wasn't written. if (mix) { // Load the last byte into the bit buffer. buf += (unsigned)(*put++) << num; num += 8; if (strm.data_type & 0x40) { // We are at the end of the deflate stream and // there are bits pad bits. Discard the pad bits // and write a byte to the output, if available. // Leave the num bits left over in buf to prepend // to the next deflate stream. num -= bits; if (num >= 8) { putc(buf, out); num -= 8; buf >>= 8; } // Force the pad bits in the bit buffer to zeros. buf &= ((unsigned long)1 << num) - 1; // Don't need to set prev here since going to TAIL. } else // At the end of an internal deflate block. Leave // the last byte in the bit buffer to examine on // the next entry to BLOCK, when more bits from the // next block will be available. prev = num - bits; // number of bits in buffer // from current block } // Don't have a byte left over, so we are in the middle of // a deflate block, or the deflate block ended on a byte // boundary. Set prev appropriately for the next entry into // BLOCK. else if (strm.data_type & 0x80) // The block ended on a byte boundary, so no header // bits are in the bit buffer. prev = num; else // In the middle of a deflate block, so no header here. prev = -1; // Check for the end of the deflate stream. if ((strm.data_type & 0xc0) == 0xc0) { // That ends the deflate stream on the input side, the // pad bits were discarded, and any remaining bits from // the last block in the stream are saved in the bit // buffer to prepend to the next stream. Process the // gzip trailer next. tail = 0; part = 0; state = TAIL; } break; case TAIL: // Accumulate available trailer bytes to update the total // CRC and the total uncompressed length. do { part = (part >> 8) + ((unsigned long)(*put++) << 24); tail++; if (tail == 4) { // Update the total CRC. z_off_t len2 = memb; if (len2 < 0 || (unsigned long long)len2 != memb) BYE("overflow error"); crc = crc ? crc32_combine(crc, part, len2) : part; part = 0; } else if (tail == 8) { // Update the total uncompressed length. (It's ok // if this sum is done modulo 2^32.) len += part; // At the end of a member. Set up to inflate an // immediately following gzip member. (If we made // it this far, then the trailer was valid.) if (inflateReset(&strm) != Z_OK) BYE("internal error"); state = BETWEEN; break; } } while (put < strm.next_in); break; } // Process the input buffer until completely consumed. } while (strm.avail_in > 0); // Process input until end of file, invalid input, or i/o error. } while (more); // Done with the inflate engine. inflateEnd(&strm); // Verify the validity of the input. if (state != BETWEEN) BYE("input invalid: incomplete gzip stream"); // Write the remaining deflate stream bits, followed by a terminating // deflate fixed block. buf += (unsigned long)3 << num; putc(buf, out); putc(buf >> 8, out); if (num > 6) putc(0, out); // Write the gzip trailer, which is the CRC and the uncompressed length // modulo 2^32, both in little-endian order. putc(crc, out); putc(crc >> 8, out); putc(crc >> 16, out); putc(crc >> 24, out); putc(len, out); putc(len >> 8, out); putc(len >> 16, out); putc(len >> 24, out); fflush(out); // Check for any i/o errors. if (ferror(in) || ferror(out)) BYE("i/o error: %s", strerror(errno)); // All good! *err = NULL; return 0; } // Normalize the gzip stream on stdin, writing the result to stdout. int main(void) { // Avoid end-of-line conversions on evil operating systems. SET_BINARY_MODE(stdin); SET_BINARY_MODE(stdout); // Normalize from stdin to stdout, returning 1 on error, 0 if ok. char *err; int ret = gzip_normalize(stdin, stdout, &err); if (ret) fprintf(stderr, "gznorm error: %s\n", err); free(err); return ret; } |
Added compat/zlib/examples/zlib_how.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 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 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>zlib Usage Example</title> <!-- Copyright (c) 2004-2023 Mark Adler. --> </head> <body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#00A000"> <h2 align="center"> zlib Usage Example </h2> We often get questions about how the <tt>deflate()</tt> and <tt>inflate()</tt> functions should be used. Users wonder when they should provide more input, when they should use more output, what to do with a <tt>Z_BUF_ERROR</tt>, how to make sure the process terminates properly, and so on. So for those who have read <tt>zlib.h</tt> (a few times), and would like further edification, below is an annotated example in C of simple routines to compress and decompress from an input file to an output file using <tt>deflate()</tt> and <tt>inflate()</tt> respectively. The annotations are interspersed between lines of the code. So please read between the lines. We hope this helps explain some of the intricacies of <em>zlib</em>. <p> Without further ado, here is the program <a href="zpipe.c"><tt>zpipe.c</tt></a>: <pre><b> /* zpipe.c: example of proper use of zlib's inflate() and deflate() Not copyrighted -- provided to the public domain Version 1.4 11 December 2005 Mark Adler */ /* Version history: 1.0 30 Oct 2004 First version 1.1 8 Nov 2004 Add void casting for unused return values Use switch statement for inflate() return values 1.2 9 Nov 2004 Add assertions to document zlib guarantees 1.3 6 Apr 2005 Remove incorrect assertion in inf() 1.4 11 Dec 2005 Add hack to avoid MSDOS end-of-line conversions Avoid some compiler warnings for input and output buffers */ </b></pre><!-- --> We now include the header files for the required definitions. From <tt>stdio.h</tt> we use <tt>fopen()</tt>, <tt>fread()</tt>, <tt>fwrite()</tt>, <tt>feof()</tt>, <tt>ferror()</tt>, and <tt>fclose()</tt> for file i/o, and <tt>fputs()</tt> for error messages. From <tt>string.h</tt> we use <tt>strcmp()</tt> for command line argument processing. From <tt>assert.h</tt> we use the <tt>assert()</tt> macro. From <tt>zlib.h</tt> we use the basic compression functions <tt>deflateInit()</tt>, <tt>deflate()</tt>, and <tt>deflateEnd()</tt>, and the basic decompression functions <tt>inflateInit()</tt>, <tt>inflate()</tt>, and <tt>inflateEnd()</tt>. <pre><b> #include <stdio.h> #include <string.h> #include <assert.h> #include "zlib.h" </b></pre><!-- --> This is an ugly hack required to avoid corruption of the input and output data on Windows/MS-DOS systems. Without this, those systems would assume that the input and output files are text, and try to convert the end-of-line characters from one standard to another. That would corrupt binary data, and in particular would render the compressed data unusable. This sets the input and output to binary which suppresses the end-of-line conversions. <tt>SET_BINARY_MODE()</tt> will be used later on <tt>stdin</tt> and <tt>stdout</tt>, at the beginning of <tt>main()</tt>. <pre><b> #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) # include <fcntl.h> # include <io.h> # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) #else # define SET_BINARY_MODE(file) #endif </b></pre><!-- --> <tt>CHUNK</tt> is simply the buffer size for feeding data to and pulling data from the <em>zlib</em> routines. Larger buffer sizes would be more efficient, especially for <tt>inflate()</tt>. If the memory is available, buffers sizes on the order of 128K or 256K bytes should be used. <pre><b> #define CHUNK 16384 </b></pre><!-- --> The <tt>def()</tt> routine compresses data from an input file to an output file. The output data will be in the <em>zlib</em> format, which is different from the <em>gzip</em> or <em>zip</em> formats. The <em>zlib</em> format has a very small header of only two bytes to identify it as a <em>zlib</em> stream and to provide decoding information, and a four-byte trailer with a fast check value to verify the integrity of the uncompressed data after decoding. <pre><b> /* Compress from file source to file dest until EOF on source. def() returns Z_OK on success, Z_MEM_ERROR if memory could not be allocated for processing, Z_STREAM_ERROR if an invalid compression level is supplied, Z_VERSION_ERROR if the version of zlib.h and the version of the library linked do not match, or Z_ERRNO if there is an error reading or writing the files. */ int def(FILE *source, FILE *dest, int level) { </b></pre> Here are the local variables for <tt>def()</tt>. <tt>ret</tt> will be used for <em>zlib</em> return codes. <tt>flush</tt> will keep track of the current flushing state for <tt>deflate()</tt>, which is either no flushing, or flush to completion after the end of the input file is reached. <tt>have</tt> is the amount of data returned from <tt>deflate()</tt>. The <tt>strm</tt> structure is used to pass information to and from the <em>zlib</em> routines, and to maintain the <tt>deflate()</tt> state. <tt>in</tt> and <tt>out</tt> are the input and output buffers for <tt>deflate()</tt>. <pre><b> int ret, flush; unsigned have; z_stream strm; unsigned char in[CHUNK]; unsigned char out[CHUNK]; </b></pre><!-- --> The first thing we do is to initialize the <em>zlib</em> state for compression using <tt>deflateInit()</tt>. This must be done before the first use of <tt>deflate()</tt>. The <tt>zalloc</tt>, <tt>zfree</tt>, and <tt>opaque</tt> fields in the <tt>strm</tt> structure must be initialized before calling <tt>deflateInit()</tt>. Here they are set to the <em>zlib</em> constant <tt>Z_NULL</tt> to request that <em>zlib</em> use the default memory allocation routines. An application may also choose to provide custom memory allocation routines here. <tt>deflateInit()</tt> will allocate on the order of 256K bytes for the internal state. (See <a href="zlib_tech.html"><em>zlib Technical Details</em></a>.) <p> <tt>deflateInit()</tt> is called with a pointer to the structure to be initialized and the compression level, which is an integer in the range of -1 to 9. Lower compression levels result in faster execution, but less compression. Higher levels result in greater compression, but slower execution. The <em>zlib</em> constant Z_DEFAULT_COMPRESSION, equal to -1, provides a good compromise between compression and speed and is equivalent to level 6. Level 0 actually does no compression at all, and in fact expands the data slightly to produce the <em>zlib</em> format (it is not a byte-for-byte copy of the input). More advanced applications of <em>zlib</em> may use <tt>deflateInit2()</tt> here instead. Such an application may want to reduce how much memory will be used, at some price in compression. Or it may need to request a <em>gzip</em> header and trailer instead of a <em>zlib</em> header and trailer, or raw encoding with no header or trailer at all. <p> We must check the return value of <tt>deflateInit()</tt> against the <em>zlib</em> constant <tt>Z_OK</tt> to make sure that it was able to allocate memory for the internal state, and that the provided arguments were valid. <tt>deflateInit()</tt> will also check that the version of <em>zlib</em> that the <tt>zlib.h</tt> file came from matches the version of <em>zlib</em> actually linked with the program. This is especially important for environments in which <em>zlib</em> is a shared library. <p> Note that an application can initialize multiple, independent <em>zlib</em> streams, which can operate in parallel. The state information maintained in the structure allows the <em>zlib</em> routines to be reentrant. <pre><b> /* allocate deflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; ret = deflateInit(&strm, level); if (ret != Z_OK) return ret; </b></pre><!-- --> With the pleasantries out of the way, now we can get down to business. The outer <tt>do</tt>-loop reads all of the input file and exits at the bottom of the loop once end-of-file is reached. This loop contains the only call of <tt>deflate()</tt>. So we must make sure that all of the input data has been processed and that all of the output data has been generated and consumed before we fall out of the loop at the bottom. <pre><b> /* compress until end of file */ do { </b></pre> We start off by reading data from the input file. The number of bytes read is put directly into <tt>avail_in</tt>, and a pointer to those bytes is put into <tt>next_in</tt>. We also check to see if end-of-file on the input has been reached using feof(). If we are at the end of file, then <tt>flush</tt> is set to the <em>zlib</em> constant <tt>Z_FINISH</tt>, which is later passed to <tt>deflate()</tt> to indicate that this is the last chunk of input data to compress. If we are not yet at the end of the input, then the <em>zlib</em> constant <tt>Z_NO_FLUSH</tt> will be passed to <tt>deflate</tt> to indicate that we are still in the middle of the uncompressed data. <p> If there is an error in reading from the input file, the process is aborted with <tt>deflateEnd()</tt> being called to free the allocated <em>zlib</em> state before returning the error. We wouldn't want a memory leak, now would we? <tt>deflateEnd()</tt> can be called at any time after the state has been initialized. Once that's done, <tt>deflateInit()</tt> (or <tt>deflateInit2()</tt>) would have to be called to start a new compression process. There is no point here in checking the <tt>deflateEnd()</tt> return code. The deallocation can't fail. <pre><b> strm.avail_in = fread(in, 1, CHUNK, source); if (ferror(source)) { (void)deflateEnd(&strm); return Z_ERRNO; } flush = feof(source) ? Z_FINISH : Z_NO_FLUSH; strm.next_in = in; </b></pre><!-- --> The inner <tt>do</tt>-loop passes our chunk of input data to <tt>deflate()</tt>, and then keeps calling <tt>deflate()</tt> until it is done producing output. Once there is no more new output, <tt>deflate()</tt> is guaranteed to have consumed all of the input, i.e., <tt>avail_in</tt> will be zero. <pre><b> /* run deflate() on input until output buffer not full, finish compression if all of source has been read in */ do { </b></pre> Output space is provided to <tt>deflate()</tt> by setting <tt>avail_out</tt> to the number of available output bytes and <tt>next_out</tt> to a pointer to that space. <pre><b> strm.avail_out = CHUNK; strm.next_out = out; </b></pre> Now we call the compression engine itself, <tt>deflate()</tt>. It takes as many of the <tt>avail_in</tt> bytes at <tt>next_in</tt> as it can process, and writes as many as <tt>avail_out</tt> bytes to <tt>next_out</tt>. Those counters and pointers are then updated past the input data consumed and the output data written. It is the amount of output space available that may limit how much input is consumed. Hence the inner loop to make sure that all of the input is consumed by providing more output space each time. Since <tt>avail_in</tt> and <tt>next_in</tt> are updated by <tt>deflate()</tt>, we don't have to mess with those between <tt>deflate()</tt> calls until it's all used up. <p> The parameters to <tt>deflate()</tt> are a pointer to the <tt>strm</tt> structure containing the input and output information and the internal compression engine state, and a parameter indicating whether and how to flush data to the output. Normally <tt>deflate</tt> will consume several K bytes of input data before producing any output (except for the header), in order to accumulate statistics on the data for optimum compression. It will then put out a burst of compressed data, and proceed to consume more input before the next burst. Eventually, <tt>deflate()</tt> must be told to terminate the stream, complete the compression with provided input data, and write out the trailer check value. <tt>deflate()</tt> will continue to compress normally as long as the flush parameter is <tt>Z_NO_FLUSH</tt>. Once the <tt>Z_FINISH</tt> parameter is provided, <tt>deflate()</tt> will begin to complete the compressed output stream. However depending on how much output space is provided, <tt>deflate()</tt> may have to be called several times until it has provided the complete compressed stream, even after it has consumed all of the input. The flush parameter must continue to be <tt>Z_FINISH</tt> for those subsequent calls. <p> There are other values of the flush parameter that are used in more advanced applications. You can force <tt>deflate()</tt> to produce a burst of output that encodes all of the input data provided so far, even if it wouldn't have otherwise, for example to control data latency on a link with compressed data. You can also ask that <tt>deflate()</tt> do that as well as erase any history up to that point so that what follows can be decompressed independently, for example for random access applications. Both requests will degrade compression by an amount depending on how often such requests are made. <p> <tt>deflate()</tt> has a return value that can indicate errors, yet we do not check it here. Why not? Well, it turns out that <tt>deflate()</tt> can do no wrong here. Let's go through <tt>deflate()</tt>'s return values and dispense with them one by one. The possible values are <tt>Z_OK</tt>, <tt>Z_STREAM_END</tt>, <tt>Z_STREAM_ERROR</tt>, or <tt>Z_BUF_ERROR</tt>. <tt>Z_OK</tt> is, well, ok. <tt>Z_STREAM_END</tt> is also ok and will be returned for the last call of <tt>deflate()</tt>. This is already guaranteed by calling <tt>deflate()</tt> with <tt>Z_FINISH</tt> until it has no more output. <tt>Z_STREAM_ERROR</tt> is only possible if the stream is not initialized properly, but we did initialize it properly. There is no harm in checking for <tt>Z_STREAM_ERROR</tt> here, for example to check for the possibility that some other part of the application inadvertently clobbered the memory containing the <em>zlib</em> state. <tt>Z_BUF_ERROR</tt> will be explained further below, but suffice it to say that this is simply an indication that <tt>deflate()</tt> could not consume more input or produce more output. <tt>deflate()</tt> can be called again with more output space or more available input, which it will be in this code. <pre><b> ret = deflate(&strm, flush); /* no bad return value */ assert(ret != Z_STREAM_ERROR); /* state not clobbered */ </b></pre> Now we compute how much output <tt>deflate()</tt> provided on the last call, which is the difference between how much space was provided before the call, and how much output space is still available after the call. Then that data, if any, is written to the output file. We can then reuse the output buffer for the next call of <tt>deflate()</tt>. Again if there is a file i/o error, we call <tt>deflateEnd()</tt> before returning to avoid a memory leak. <pre><b> have = CHUNK - strm.avail_out; if (fwrite(out, 1, have, dest) != have || ferror(dest)) { (void)deflateEnd(&strm); return Z_ERRNO; } </b></pre> The inner <tt>do</tt>-loop is repeated until the last <tt>deflate()</tt> call fails to fill the provided output buffer. Then we know that <tt>deflate()</tt> has done as much as it can with the provided input, and that all of that input has been consumed. We can then fall out of this loop and reuse the input buffer. <p> The way we tell that <tt>deflate()</tt> has no more output is by seeing that it did not fill the output buffer, leaving <tt>avail_out</tt> greater than zero. However suppose that <tt>deflate()</tt> has no more output, but just so happened to exactly fill the output buffer! <tt>avail_out</tt> is zero, and we can't tell that <tt>deflate()</tt> has done all it can. As far as we know, <tt>deflate()</tt> has more output for us. So we call it again. But now <tt>deflate()</tt> produces no output at all, and <tt>avail_out</tt> remains unchanged as <tt>CHUNK</tt>. That <tt>deflate()</tt> call wasn't able to do anything, either consume input or produce output, and so it returns <tt>Z_BUF_ERROR</tt>. (See, I told you I'd cover this later.) However this is not a problem at all. Now we finally have the desired indication that <tt>deflate()</tt> is really done, and so we drop out of the inner loop to provide more input to <tt>deflate()</tt>. <p> With <tt>flush</tt> set to <tt>Z_FINISH</tt>, this final set of <tt>deflate()</tt> calls will complete the output stream. Once that is done, subsequent calls of <tt>deflate()</tt> would return <tt>Z_STREAM_ERROR</tt> if the flush parameter is not <tt>Z_FINISH</tt>, and do no more processing until the state is reinitialized. <p> Some applications of <em>zlib</em> have two loops that call <tt>deflate()</tt> instead of the single inner loop we have here. The first loop would call without flushing and feed all of the data to <tt>deflate()</tt>. The second loop would call <tt>deflate()</tt> with no more data and the <tt>Z_FINISH</tt> parameter to complete the process. As you can see from this example, that can be avoided by simply keeping track of the current flush state. <pre><b> } while (strm.avail_out == 0); assert(strm.avail_in == 0); /* all input will be used */ </b></pre><!-- --> Now we check to see if we have already processed all of the input file. That information was saved in the <tt>flush</tt> variable, so we see if that was set to <tt>Z_FINISH</tt>. If so, then we're done and we fall out of the outer loop. We're guaranteed to get <tt>Z_STREAM_END</tt> from the last <tt>deflate()</tt> call, since we ran it until the last chunk of input was consumed and all of the output was generated. <pre><b> /* done when last data in file processed */ } while (flush != Z_FINISH); assert(ret == Z_STREAM_END); /* stream will be complete */ </b></pre><!-- --> The process is complete, but we still need to deallocate the state to avoid a memory leak (or rather more like a memory hemorrhage if you didn't do this). Then finally we can return with a happy return value. <pre><b> /* clean up and return */ (void)deflateEnd(&strm); return Z_OK; } </b></pre><!-- --> Now we do the same thing for decompression in the <tt>inf()</tt> routine. <tt>inf()</tt> decompresses what is hopefully a valid <em>zlib</em> stream from the input file and writes the uncompressed data to the output file. Much of the discussion above for <tt>def()</tt> applies to <tt>inf()</tt> as well, so the discussion here will focus on the differences between the two. <pre><b> /* Decompress from file source to file dest until stream ends or EOF. inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be allocated for processing, Z_DATA_ERROR if the deflate data is invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and the version of the library linked do not match, or Z_ERRNO if there is an error reading or writing the files. */ int inf(FILE *source, FILE *dest) { </b></pre> The local variables have the same functionality as they do for <tt>def()</tt>. The only difference is that there is no <tt>flush</tt> variable, since <tt>inflate()</tt> can tell from the <em>zlib</em> stream itself when the stream is complete. <pre><b> int ret; unsigned have; z_stream strm; unsigned char in[CHUNK]; unsigned char out[CHUNK]; </b></pre><!-- --> The initialization of the state is the same, except that there is no compression level, of course, and two more elements of the structure are initialized. <tt>avail_in</tt> and <tt>next_in</tt> must be initialized before calling <tt>inflateInit()</tt>. This is because the application has the option to provide the start of the zlib stream in order for <tt>inflateInit()</tt> to have access to information about the compression method to aid in memory allocation. In the current implementation of <em>zlib</em> (up through versions 1.2.x), the method-dependent memory allocations are deferred to the first call of <tt>inflate()</tt> anyway. However those fields must be initialized since later versions of <em>zlib</em> that provide more compression methods may take advantage of this interface. In any case, no decompression is performed by <tt>inflateInit()</tt>, so the <tt>avail_out</tt> and <tt>next_out</tt> fields do not need to be initialized before calling. <p> Here <tt>avail_in</tt> is set to zero and <tt>next_in</tt> is set to <tt>Z_NULL</tt> to indicate that no input data is being provided. <pre><b> /* allocate inflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit(&strm); if (ret != Z_OK) return ret; </b></pre><!-- --> The outer <tt>do</tt>-loop decompresses input until <tt>inflate()</tt> indicates that it has reached the end of the compressed data and has produced all of the uncompressed output. This is in contrast to <tt>def()</tt> which processes all of the input file. If end-of-file is reached before the compressed data self-terminates, then the compressed data is incomplete and an error is returned. <pre><b> /* decompress until deflate stream ends or end of file */ do { </b></pre> We read input data and set the <tt>strm</tt> structure accordingly. If we've reached the end of the input file, then we leave the outer loop and report an error, since the compressed data is incomplete. Note that we may read more data than is eventually consumed by <tt>inflate()</tt>, if the input file continues past the <em>zlib</em> stream. For applications where <em>zlib</em> streams are embedded in other data, this routine would need to be modified to return the unused data, or at least indicate how much of the input data was not used, so the application would know where to pick up after the <em>zlib</em> stream. <pre><b> strm.avail_in = fread(in, 1, CHUNK, source); if (ferror(source)) { (void)inflateEnd(&strm); return Z_ERRNO; } if (strm.avail_in == 0) break; strm.next_in = in; </b></pre><!-- --> The inner <tt>do</tt>-loop has the same function it did in <tt>def()</tt>, which is to keep calling <tt>inflate()</tt> until has generated all of the output it can with the provided input. <pre><b> /* run inflate() on input until output buffer not full */ do { </b></pre> Just like in <tt>def()</tt>, the same output space is provided for each call of <tt>inflate()</tt>. <pre><b> strm.avail_out = CHUNK; strm.next_out = out; </b></pre> Now we run the decompression engine itself. There is no need to adjust the flush parameter, since the <em>zlib</em> format is self-terminating. The main difference here is that there are return values that we need to pay attention to. <tt>Z_DATA_ERROR</tt> indicates that <tt>inflate()</tt> detected an error in the <em>zlib</em> compressed data format, which means that either the data is not a <em>zlib</em> stream to begin with, or that the data was corrupted somewhere along the way since it was compressed. The other error to be processed is <tt>Z_MEM_ERROR</tt>, which can occur since memory allocation is deferred until <tt>inflate()</tt> needs it, unlike <tt>deflate()</tt>, whose memory is allocated at the start by <tt>deflateInit()</tt>. <p> Advanced applications may use <tt>deflateSetDictionary()</tt> to prime <tt>deflate()</tt> with a set of likely data to improve the first 32K or so of compression. This is noted in the <em>zlib</em> header, so <tt>inflate()</tt> requests that that dictionary be provided before it can start to decompress. Without the dictionary, correct decompression is not possible. For this routine, we have no idea what the dictionary is, so the <tt>Z_NEED_DICT</tt> indication is converted to a <tt>Z_DATA_ERROR</tt>. <p> <tt>inflate()</tt> can also return <tt>Z_STREAM_ERROR</tt>, which should not be possible here, but could be checked for as noted above for <tt>def()</tt>. <tt>Z_BUF_ERROR</tt> does not need to be checked for here, for the same reasons noted for <tt>def()</tt>. <tt>Z_STREAM_END</tt> will be checked for later. <pre><b> ret = inflate(&strm, Z_NO_FLUSH); assert(ret != Z_STREAM_ERROR); /* state not clobbered */ switch (ret) { case Z_NEED_DICT: ret = Z_DATA_ERROR; /* and fall through */ case Z_DATA_ERROR: case Z_MEM_ERROR: (void)inflateEnd(&strm); return ret; } </b></pre> The output of <tt>inflate()</tt> is handled identically to that of <tt>deflate()</tt>. <pre><b> have = CHUNK - strm.avail_out; if (fwrite(out, 1, have, dest) != have || ferror(dest)) { (void)inflateEnd(&strm); return Z_ERRNO; } </b></pre> The inner <tt>do</tt>-loop ends when <tt>inflate()</tt> has no more output as indicated by not filling the output buffer, just as for <tt>deflate()</tt>. In this case, we cannot assert that <tt>strm.avail_in</tt> will be zero, since the deflate stream may end before the file does. <pre><b> } while (strm.avail_out == 0); </b></pre><!-- --> The outer <tt>do</tt>-loop ends when <tt>inflate()</tt> reports that it has reached the end of the input <em>zlib</em> stream, has completed the decompression and integrity check, and has provided all of the output. This is indicated by the <tt>inflate()</tt> return value <tt>Z_STREAM_END</tt>. The inner loop is guaranteed to leave <tt>ret</tt> equal to <tt>Z_STREAM_END</tt> if the last chunk of the input file read contained the end of the <em>zlib</em> stream. So if the return value is not <tt>Z_STREAM_END</tt>, the loop continues to read more input. <pre><b> /* done when inflate() says it's done */ } while (ret != Z_STREAM_END); </b></pre><!-- --> At this point, decompression successfully completed, or we broke out of the loop due to no more data being available from the input file. If the last <tt>inflate()</tt> return value is not <tt>Z_STREAM_END</tt>, then the <em>zlib</em> stream was incomplete and a data error is returned. Otherwise, we return with a happy return value. Of course, <tt>inflateEnd()</tt> is called first to avoid a memory leak. <pre><b> /* clean up and return */ (void)inflateEnd(&strm); return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; } </b></pre><!-- --> That ends the routines that directly use <em>zlib</em>. The following routines make this a command-line program by running data through the above routines from <tt>stdin</tt> to <tt>stdout</tt>, and handling any errors reported by <tt>def()</tt> or <tt>inf()</tt>. <p> <tt>zerr()</tt> is used to interpret the possible error codes from <tt>def()</tt> and <tt>inf()</tt>, as detailed in their comments above, and print out an error message. Note that these are only a subset of the possible return values from <tt>deflate()</tt> and <tt>inflate()</tt>. <pre><b> /* report a zlib or i/o error */ void zerr(int ret) { fputs("zpipe: ", stderr); switch (ret) { case Z_ERRNO: if (ferror(stdin)) fputs("error reading stdin\n", stderr); if (ferror(stdout)) fputs("error writing stdout\n", stderr); break; case Z_STREAM_ERROR: fputs("invalid compression level\n", stderr); break; case Z_DATA_ERROR: fputs("invalid or incomplete deflate data\n", stderr); break; case Z_MEM_ERROR: fputs("out of memory\n", stderr); break; case Z_VERSION_ERROR: fputs("zlib version mismatch!\n", stderr); } } </b></pre><!-- --> Here is the <tt>main()</tt> routine used to test <tt>def()</tt> and <tt>inf()</tt>. The <tt>zpipe</tt> command is simply a compression pipe from <tt>stdin</tt> to <tt>stdout</tt>, if no arguments are given, or it is a decompression pipe if <tt>zpipe -d</tt> is used. If any other arguments are provided, no compression or decompression is performed. Instead a usage message is displayed. Examples are <tt>zpipe < foo.txt > foo.txt.z</tt> to compress, and <tt>zpipe -d < foo.txt.z > foo.txt</tt> to decompress. <pre><b> /* compress or decompress from stdin to stdout */ int main(int argc, char **argv) { int ret; /* avoid end-of-line conversions */ SET_BINARY_MODE(stdin); SET_BINARY_MODE(stdout); /* do compression if no arguments */ if (argc == 1) { ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION); if (ret != Z_OK) zerr(ret); return ret; } /* do decompression if -d specified */ else if (argc == 2 && strcmp(argv[1], "-d") == 0) { ret = inf(stdin, stdout); if (ret != Z_OK) zerr(ret); return ret; } /* otherwise, report usage */ else { fputs("zpipe usage: zpipe [-d] < source > dest\n", stderr); return 1; } } </b></pre> <hr> <i>Last modified 24 January 2023<br> Copyright © 2004-2023 Mark Adler</i><br> <a rel="license" href="http://creativecommons.org/licenses/by-nd/4.0/"> <img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nd/4.0/88x31.png"></a> <a rel="license" href="http://creativecommons.org/licenses/by-nd/4.0/"> Creative Commons Attribution-NoDerivatives 4.0 International License</a>. </body> </html> |
Added compat/zlib/examples/zpipe.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 | /* zpipe.c: example of proper use of zlib's inflate() and deflate() Not copyrighted -- provided to the public domain Version 1.4 11 December 2005 Mark Adler */ /* Version history: 1.0 30 Oct 2004 First version 1.1 8 Nov 2004 Add void casting for unused return values Use switch statement for inflate() return values 1.2 9 Nov 2004 Add assertions to document zlib guarantees 1.3 6 Apr 2005 Remove incorrect assertion in inf() 1.4 11 Dec 2005 Add hack to avoid MSDOS end-of-line conversions Avoid some compiler warnings for input and output buffers */ #include <stdio.h> #include <string.h> #include <assert.h> #include "zlib.h" #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) # include <fcntl.h> # include <io.h> # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) #else # define SET_BINARY_MODE(file) #endif #define CHUNK 16384 /* Compress from file source to file dest until EOF on source. def() returns Z_OK on success, Z_MEM_ERROR if memory could not be allocated for processing, Z_STREAM_ERROR if an invalid compression level is supplied, Z_VERSION_ERROR if the version of zlib.h and the version of the library linked do not match, or Z_ERRNO if there is an error reading or writing the files. */ int def(FILE *source, FILE *dest, int level) { int ret, flush; unsigned have; z_stream strm; unsigned char in[CHUNK]; unsigned char out[CHUNK]; /* allocate deflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; ret = deflateInit(&strm, level); if (ret != Z_OK) return ret; /* compress until end of file */ do { strm.avail_in = fread(in, 1, CHUNK, source); if (ferror(source)) { (void)deflateEnd(&strm); return Z_ERRNO; } flush = feof(source) ? Z_FINISH : Z_NO_FLUSH; strm.next_in = in; /* run deflate() on input until output buffer not full, finish compression if all of source has been read in */ do { strm.avail_out = CHUNK; strm.next_out = out; ret = deflate(&strm, flush); /* no bad return value */ assert(ret != Z_STREAM_ERROR); /* state not clobbered */ have = CHUNK - strm.avail_out; if (fwrite(out, 1, have, dest) != have || ferror(dest)) { (void)deflateEnd(&strm); return Z_ERRNO; } } while (strm.avail_out == 0); assert(strm.avail_in == 0); /* all input will be used */ /* done when last data in file processed */ } while (flush != Z_FINISH); assert(ret == Z_STREAM_END); /* stream will be complete */ /* clean up and return */ (void)deflateEnd(&strm); return Z_OK; } /* Decompress from file source to file dest until stream ends or EOF. inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be allocated for processing, Z_DATA_ERROR if the deflate data is invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and the version of the library linked do not match, or Z_ERRNO if there is an error reading or writing the files. */ int inf(FILE *source, FILE *dest) { int ret; unsigned have; z_stream strm; unsigned char in[CHUNK]; unsigned char out[CHUNK]; /* allocate inflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit(&strm); if (ret != Z_OK) return ret; /* decompress until deflate stream ends or end of file */ do { strm.avail_in = fread(in, 1, CHUNK, source); if (ferror(source)) { (void)inflateEnd(&strm); return Z_ERRNO; } if (strm.avail_in == 0) break; strm.next_in = in; /* run inflate() on input until output buffer not full */ do { strm.avail_out = CHUNK; strm.next_out = out; ret = inflate(&strm, Z_NO_FLUSH); assert(ret != Z_STREAM_ERROR); /* state not clobbered */ switch (ret) { case Z_NEED_DICT: ret = Z_DATA_ERROR; /* and fall through */ case Z_DATA_ERROR: case Z_MEM_ERROR: (void)inflateEnd(&strm); return ret; } have = CHUNK - strm.avail_out; if (fwrite(out, 1, have, dest) != have || ferror(dest)) { (void)inflateEnd(&strm); return Z_ERRNO; } } while (strm.avail_out == 0); /* done when inflate() says it's done */ } while (ret != Z_STREAM_END); /* clean up and return */ (void)inflateEnd(&strm); return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; } /* report a zlib or i/o error */ void zerr(int ret) { fputs("zpipe: ", stderr); switch (ret) { case Z_ERRNO: if (ferror(stdin)) fputs("error reading stdin\n", stderr); if (ferror(stdout)) fputs("error writing stdout\n", stderr); break; case Z_STREAM_ERROR: fputs("invalid compression level\n", stderr); break; case Z_DATA_ERROR: fputs("invalid or incomplete deflate data\n", stderr); break; case Z_MEM_ERROR: fputs("out of memory\n", stderr); break; case Z_VERSION_ERROR: fputs("zlib version mismatch!\n", stderr); } } /* compress or decompress from stdin to stdout */ int main(int argc, char **argv) { int ret; /* avoid end-of-line conversions */ SET_BINARY_MODE(stdin); SET_BINARY_MODE(stdout); /* do compression if no arguments */ if (argc == 1) { ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION); if (ret != Z_OK) zerr(ret); return ret; } /* do decompression if -d specified */ else if (argc == 2 && strcmp(argv[1], "-d") == 0) { ret = inf(stdin, stdout); if (ret != Z_OK) zerr(ret); return ret; } /* otherwise, report usage */ else { fputs("zpipe usage: zpipe [-d] < source > dest\n", stderr); return 1; } } |
Added compat/zlib/examples/zran.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 | /* zran.c -- example of deflate stream indexing and random access * Copyright (C) 2005, 2012, 2018, 2023 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * Version 1.4 13 Apr 2023 Mark Adler */ /* Version History: 1.0 29 May 2005 First version 1.1 29 Sep 2012 Fix memory reallocation error 1.2 14 Oct 2018 Handle gzip streams with multiple members Add a header file to facilitate usage in applications 1.3 18 Feb 2023 Permit raw deflate streams as well as zlib and gzip Permit crossing gzip member boundaries when extracting Support a size_t size when extracting (was an int) Do a binary search over the index for an access point Expose the access point type to enable save and load 1.4 13 Apr 2023 Add a NOPRIME define to not use inflatePrime() */ // Illustrate the use of Z_BLOCK, inflatePrime(), and inflateSetDictionary() // for random access of a compressed file. A file containing a raw deflate // stream is provided on the command line. The compressed stream is decoded in // its entirety, and an index built with access points about every SPAN bytes // in the uncompressed output. The compressed file is left open, and can then // be read randomly, having to decompress on the average SPAN/2 uncompressed // bytes before getting to the desired block of data. // // An access point can be created at the start of any deflate block, by saving // the starting file offset and bit of that block, and the 32K bytes of // uncompressed data that precede that block. Also the uncompressed offset of // that block is saved to provide a reference for locating a desired starting // point in the uncompressed stream. deflate_index_build() decompresses the // input raw deflate stream a block at a time, and at the end of each block // decides if enough uncompressed data has gone by to justify the creation of a // new access point. If so, that point is saved in a data structure that grows // as needed to accommodate the points. // // To use the index, an offset in the uncompressed data is provided, for which // the latest access point at or preceding that offset is located in the index. // The input file is positioned to the specified location in the index, and if // necessary the first few bits of the compressed data is read from the file. // inflate is initialized with those bits and the 32K of uncompressed data, and // decompression then proceeds until the desired offset in the file is reached. // Then decompression continues to read the requested uncompressed data from // the file. // // There is some fair bit of overhead to starting inflation for the random // access, mainly copying the 32K byte dictionary. If small pieces of the file // are being accessed, it would make sense to implement a cache to hold some // lookahead to avoid many calls to deflate_index_extract() for small lengths. // // Another way to build an index would be to use inflateCopy(). That would not // be constrained to have access points at block boundaries, but would require // more memory per access point, and could not be saved to a file due to the // use of pointers in the state. The approach here allows for storage of the // index in a file. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include "zlib.h" #include "zran.h" #define WINSIZE 32768U // sliding window size #define CHUNK 16384 // file input buffer size // See comments in zran.h. void deflate_index_free(struct deflate_index *index) { if (index != NULL) { free(index->list); free(index); } } // Add an access point to the list. If out of memory, deallocate the existing // list and return NULL. index->mode is temporarily the allocated number of // access points, until it is time for deflate_index_build() to return. Then // index->mode is set to the mode of inflation. static struct deflate_index *add_point(struct deflate_index *index, int bits, off_t in, off_t out, unsigned left, unsigned char *window) { if (index == NULL) { // The list is empty. Create it, starting with eight access points. index = malloc(sizeof(struct deflate_index)); if (index == NULL) return NULL; index->have = 0; index->mode = 8; index->list = malloc(sizeof(point_t) * index->mode); if (index->list == NULL) { free(index); return NULL; } } else if (index->have == index->mode) { // The list is full. Make it bigger. index->mode <<= 1; point_t *next = realloc(index->list, sizeof(point_t) * index->mode); if (next == NULL) { deflate_index_free(index); return NULL; } index->list = next; } // Fill in the access point and increment how many we have. point_t *next = (point_t *)(index->list) + index->have++; if (index->have < 0) { // Overflowed the int! deflate_index_free(index); return NULL; } next->out = out; next->in = in; next->bits = bits; if (left) memcpy(next->window, window + WINSIZE - left, left); if (left < WINSIZE) memcpy(next->window + left, window, WINSIZE - left); // Return the index, which may have been newly allocated or destroyed. return index; } // Decompression modes. These are the inflateInit2() windowBits parameter. #define RAW -15 #define ZLIB 15 #define GZIP 31 // See comments in zran.h. int deflate_index_build(FILE *in, off_t span, struct deflate_index **built) { // Set up inflation state. z_stream strm = {0}; // inflate engine (gets fired up later) unsigned char buf[CHUNK]; // input buffer unsigned char win[WINSIZE] = {0}; // output sliding window off_t totin = 0; // total bytes read from input off_t totout = 0; // total bytes uncompressed int mode = 0; // mode: RAW, ZLIB, or GZIP (0 => not set yet) // Decompress from in, generating access points along the way. int ret; // the return value from zlib, or Z_ERRNO off_t last; // last access point uncompressed offset struct deflate_index *index = NULL; // list of access points do { // Assure available input, at least until reaching EOF. if (strm.avail_in == 0) { strm.avail_in = fread(buf, 1, sizeof(buf), in); totin += strm.avail_in; strm.next_in = buf; if (strm.avail_in < sizeof(buf) && ferror(in)) { ret = Z_ERRNO; break; } if (mode == 0) { // At the start of the input -- determine the type. Assume raw // if it is neither zlib nor gzip. This could in theory result // in a false positive for zlib, but in practice the fill bits // after a stored block are always zeros, so a raw stream won't // start with an 8 in the low nybble. mode = strm.avail_in == 0 ? RAW : // empty -- will fail (strm.next_in[0] & 0xf) == 8 ? ZLIB : strm.next_in[0] == 0x1f ? GZIP : /* else */ RAW; ret = inflateInit2(&strm, mode); if (ret != Z_OK) break; } } // Assure available output. This rotates the output through, for use as // a sliding window on the uncompressed data. if (strm.avail_out == 0) { strm.avail_out = sizeof(win); strm.next_out = win; } if (mode == RAW && index == NULL) // We skip the inflate() call at the start of raw deflate data in // order generate an access point there. Set data_type to imitate // the end of a header. strm.data_type = 0x80; else { // Inflate and update the number of uncompressed bytes. unsigned before = strm.avail_out; ret = inflate(&strm, Z_BLOCK); totout += before - strm.avail_out; } if ((strm.data_type & 0xc0) == 0x80 && (index == NULL || totout - last >= span)) { // We are at the end of a header or a non-last deflate block, so we // can add an access point here. Furthermore, we are either at the // very start for the first access point, or there has been span or // more uncompressed bytes since the last access point, so we want // to add an access point here. index = add_point(index, strm.data_type & 7, totin - strm.avail_in, totout, strm.avail_out, win); if (index == NULL) { ret = Z_MEM_ERROR; break; } last = totout; } if (ret == Z_STREAM_END && mode == GZIP && (strm.avail_in || ungetc(getc(in), in) != EOF)) // There is more input after the end of a gzip member. Reset the // inflate state to read another gzip member. On success, this will // set ret to Z_OK to continue decompressing. ret = inflateReset2(&strm, GZIP); // Keep going until Z_STREAM_END or error. If the compressed data ends // prematurely without a file read error, Z_BUF_ERROR is returned. } while (ret == Z_OK); inflateEnd(&strm); if (ret != Z_STREAM_END) { // An error was encountered. Discard the index and return a negative // error code. deflate_index_free(index); return ret == Z_NEED_DICT ? Z_DATA_ERROR : ret; } // Shrink the index to only the occupied access points and return it. index->mode = mode; index->length = totout; point_t *list = realloc(index->list, sizeof(point_t) * index->have); if (list == NULL) { // Seems like a realloc() to make something smaller should always work, // but just in case. deflate_index_free(index); return Z_MEM_ERROR; } index->list = list; *built = index; return index->have; } #ifdef NOPRIME // Support zlib versions before 1.2.3 (July 2005), or incomplete zlib clones // that do not have inflatePrime(). # define INFLATEPRIME inflatePreface // Append the low bits bits of value to in[] at bit position *have, updating // *have. value must be zero above its low bits bits. bits must be positive. // This assumes that any bits above the *have bits in the last byte are zeros. // That assumption is preserved on return, as any bits above *have + bits in // the last byte written will be set to zeros. static inline void append_bits(unsigned value, int bits, unsigned char *in, int *have) { in += *have >> 3; // where the first bits from value will go int k = *have & 7; // the number of bits already there *have += bits; if (k) *in |= value << k; // write value above the low k bits else *in = value; k = 8 - k; // the number of bits just appended while (bits > k) { value >>= k; // drop the bits appended bits -= k; k = 8; // now at a byte boundary *++in = value; } } // Insert enough bits in the form of empty deflate blocks in front of the // low bits bits of value, in order to bring the sequence to a byte boundary. // Then feed that to inflate(). This does what inflatePrime() does, except that // a negative value of bits is not supported. bits must be in 0..16. If the // arguments are invalid, Z_STREAM_ERROR is returned. Otherwise the return // value from inflate() is returned. static int inflatePreface(z_stream *strm, int bits, int value) { // Check input. if (strm == Z_NULL || bits < 0 || bits > 16) return Z_STREAM_ERROR; if (bits == 0) return Z_OK; value &= (2 << (bits - 1)) - 1; // An empty dynamic block with an odd number of bits (95). The high bit of // the last byte is unused. static const unsigned char dyn[] = { 4, 0xe0, 0x81, 8, 0, 0, 0, 0, 0x20, 0xa8, 0xab, 0x1f }; const int dynlen = 95; // number of bits in the block // Build an input buffer for inflate that is a multiple of eight bits in // length, and that ends with the low bits bits of value. unsigned char in[(dynlen + 3 * 10 + 16 + 7) / 8]; int have = 0; if (bits & 1) { // Insert an empty dynamic block to get to an odd number of bits, so // when bits bits from value are appended, we are at an even number of // bits. memcpy(in, dyn, sizeof(dyn)); have = dynlen; } while ((have + bits) & 7) // Insert empty fixed blocks until appending bits bits would put us on // a byte boundary. This will insert at most three fixed blocks. append_bits(2, 10, in, &have); // Append the bits bits from value, which takes us to a byte boundary. append_bits(value, bits, in, &have); // Deliver the input to inflate(). There is no output space provided, but // inflate() can't get stuck waiting on output not ingesting all of the // provided input. The reason is that there will be at most 16 bits of // input from value after the empty deflate blocks (which themselves // generate no output). At least ten bits are needed to generate the first // output byte from a fixed block. The last two bytes of the buffer have to // be ingested in order to get ten bits, which is the most that value can // occupy. strm->avail_in = have >> 3; strm->next_in = in; strm->avail_out = 0; strm->next_out = in; // not used, but can't be NULL return inflate(strm, Z_NO_FLUSH); } #else # define INFLATEPRIME inflatePrime #endif // See comments in zran.h. ptrdiff_t deflate_index_extract(FILE *in, struct deflate_index *index, off_t offset, unsigned char *buf, size_t len) { // Do a quick sanity check on the index. if (index == NULL || index->have < 1 || index->list[0].out != 0) return Z_STREAM_ERROR; // If nothing to extract, return zero bytes extracted. if (len == 0 || offset < 0 || offset >= index->length) return 0; // Find the access point closest to but not after offset. int lo = -1, hi = index->have; point_t *point = index->list; while (hi - lo > 1) { int mid = (lo + hi) >> 1; if (offset < point[mid].out) hi = mid; else lo = mid; } point += lo; // Initialize the input file and prime the inflate engine to start there. int ret = fseeko(in, point->in - (point->bits ? 1 : 0), SEEK_SET); if (ret == -1) return Z_ERRNO; int ch = 0; if (point->bits && (ch = getc(in)) == EOF) return ferror(in) ? Z_ERRNO : Z_BUF_ERROR; z_stream strm = {0}; ret = inflateInit2(&strm, RAW); if (ret != Z_OK) return ret; if (point->bits) INFLATEPRIME(&strm, point->bits, ch >> (8 - point->bits)); inflateSetDictionary(&strm, point->window, WINSIZE); // Skip uncompressed bytes until offset reached, then satisfy request. unsigned char input[CHUNK]; unsigned char discard[WINSIZE]; offset -= point->out; // number of bytes to skip to get to offset size_t left = len; // number of bytes left to read after offset do { if (offset) { // Discard up to offset uncompressed bytes. strm.avail_out = offset < WINSIZE ? (unsigned)offset : WINSIZE; strm.next_out = discard; } else { // Uncompress up to left bytes into buf. strm.avail_out = left < UINT_MAX ? (unsigned)left : UINT_MAX; strm.next_out = buf + len - left; } // Uncompress, setting got to the number of bytes uncompressed. if (strm.avail_in == 0) { // Assure available input. strm.avail_in = fread(input, 1, CHUNK, in); if (strm.avail_in < CHUNK && ferror(in)) { ret = Z_ERRNO; break; } strm.next_in = input; } unsigned got = strm.avail_out; ret = inflate(&strm, Z_NO_FLUSH); got -= strm.avail_out; // Update the appropriate count. if (offset) offset -= got; else left -= got; // If we're at the end of a gzip member and there's more to read, // continue to the next gzip member. if (ret == Z_STREAM_END && index->mode == GZIP) { // Discard the gzip trailer. unsigned drop = 8; // length of gzip trailer if (strm.avail_in >= drop) { strm.avail_in -= drop; strm.next_in += drop; } else { // Read and discard the remainder of the gzip trailer. drop -= strm.avail_in; strm.avail_in = 0; do { if (getc(in) == EOF) // The input does not have a complete trailer. return ferror(in) ? Z_ERRNO : Z_BUF_ERROR; } while (--drop); } if (strm.avail_in || ungetc(getc(in), in) != EOF) { // There's more after the gzip trailer. Use inflate to skip the // gzip header and resume the raw inflate there. inflateReset2(&strm, GZIP); do { if (strm.avail_in == 0) { strm.avail_in = fread(input, 1, CHUNK, in); if (strm.avail_in < CHUNK && ferror(in)) { ret = Z_ERRNO; break; } strm.next_in = input; } strm.avail_out = WINSIZE; strm.next_out = discard; ret = inflate(&strm, Z_BLOCK); // stop at end of header } while (ret == Z_OK && (strm.data_type & 0x80) == 0); if (ret != Z_OK) break; inflateReset2(&strm, RAW); } } // Continue until we have the requested data, the deflate data has // ended, or an error is encountered. } while (ret == Z_OK && left); inflateEnd(&strm); // Return the number of uncompressed bytes read into buf, or the error. return ret == Z_OK || ret == Z_STREAM_END ? len - left : ret; } #ifdef TEST #define SPAN 1048576L // desired distance between access points #define LEN 16384 // number of bytes to extract // Demonstrate the use of deflate_index_build() and deflate_index_extract() by // processing the file provided on the command line, and extracting LEN bytes // from 2/3rds of the way through the uncompressed output, writing that to // stdout. An offset can be provided as the second argument, in which case the // data is extracted from there instead. int main(int argc, char **argv) { // Open the input file. if (argc < 2 || argc > 3) { fprintf(stderr, "usage: zran file.raw [offset]\n"); return 1; } FILE *in = fopen(argv[1], "rb"); if (in == NULL) { fprintf(stderr, "zran: could not open %s for reading\n", argv[1]); return 1; } // Get optional offset. off_t offset = -1; if (argc == 3) { char *end; offset = strtoll(argv[2], &end, 10); if (*end || offset < 0) { fprintf(stderr, "zran: %s is not a valid offset\n", argv[2]); return 1; } } // Build index. struct deflate_index *index = NULL; int len = deflate_index_build(in, SPAN, &index); if (len < 0) { fclose(in); switch (len) { case Z_MEM_ERROR: fprintf(stderr, "zran: out of memory\n"); break; case Z_BUF_ERROR: fprintf(stderr, "zran: %s ended prematurely\n", argv[1]); break; case Z_DATA_ERROR: fprintf(stderr, "zran: compressed data error in %s\n", argv[1]); break; case Z_ERRNO: fprintf(stderr, "zran: read error on %s\n", argv[1]); break; default: fprintf(stderr, "zran: error %d while building index\n", len); } return 1; } fprintf(stderr, "zran: built index with %d access points\n", len); // Use index by reading some bytes from an arbitrary offset. unsigned char buf[LEN]; if (offset == -1) offset = ((index->length + 1) << 1) / 3; ptrdiff_t got = deflate_index_extract(in, index, offset, buf, LEN); if (got < 0) fprintf(stderr, "zran: extraction failed: %s error\n", got == Z_MEM_ERROR ? "out of memory" : "input corrupted"); else { fwrite(buf, 1, got, stdout); fprintf(stderr, "zran: extracted %ld bytes at %lld\n", got, offset); } // Clean up and exit. deflate_index_free(index); fclose(in); return 0; } #endif |
Added compat/zlib/examples/zran.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* zran.h -- example of deflated stream indexing and random access * Copyright (C) 2005, 2012, 2018, 2023 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * Version 1.3 18 Feb 2023 Mark Adler */ #include <stdio.h> #include "zlib.h" // Access point. typedef struct point { off_t out; // offset in uncompressed data off_t in; // offset in compressed file of first full byte int bits; // 0, or number of bits (1-7) from byte at in-1 unsigned char window[32768]; // preceding 32K of uncompressed data } point_t; // Access point list. struct deflate_index { int have; // number of access points in list int mode; // -15 for raw, 15 for zlib, or 31 for gzip off_t length; // total length of uncompressed data point_t *list; // allocated list of access points }; // Make one pass through a zlib, gzip, or raw deflate compressed stream and // build an index, with access points about every span bytes of uncompressed // output. gzip files with multiple members are fully indexed. span should be // chosen to balance the speed of random access against the memory requirements // of the list, which is about 32K bytes per access point. The return value is // the number of access points on success (>= 1), Z_MEM_ERROR for out of // memory, Z_BUF_ERROR for a premature end of input, Z_DATA_ERROR for a format // or verification error in the input file, or Z_ERRNO for a file read error. // On success, *built points to the resulting index. int deflate_index_build(FILE *in, off_t span, struct deflate_index **built); // Use the index to read len bytes from offset into buf. Return the number of // bytes read or a negative error code. If data is requested past the end of // the uncompressed data, then deflate_index_extract() will return a value less // than len, indicating how much was actually read into buf. If given a valid // index, this function should not return an error unless the file was modified // somehow since the index was generated, given that deflate_index_build() had // validated all of the input. If nevertheless there is a failure, Z_BUF_ERROR // is returned if the compressed data ends prematurely, Z_DATA_ERROR if the // deflate compressed data is not valid, Z_MEM_ERROR if out of memory, // Z_STREAM_ERROR if the index is not valid, or Z_ERRNO if there is an error // reading or seeking on the input file. ptrdiff_t deflate_index_extract(FILE *in, struct deflate_index *index, off_t offset, unsigned char *buf, size_t len); // Deallocate an index built by deflate_index_build(). void deflate_index_free(struct deflate_index *index); |
Added compat/zlib/gzclose.c.
> > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /* gzclose.c -- zlib gzclose() function * Copyright (C) 2004, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" /* gzclose() is in a separate file so that it is linked in only if it is used. That way the other gzclose functions can be used instead to avoid linking in unneeded compression or decompression routines. */ int ZEXPORT gzclose(gzFile file) { #ifndef NO_GZCOMPRESS gz_statep state; if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); #else return gzclose_r(file); #endif } |
Added compat/zlib/gzguts.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* gzguts.h -- zlib internal header definitions for gz* operations * Copyright (C) 2004-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #ifdef _LARGEFILE64_SOURCE # ifndef _LARGEFILE_SOURCE # define _LARGEFILE_SOURCE 1 # endif # undef _FILE_OFFSET_BITS # undef _TIME_BITS #endif #ifdef HAVE_HIDDEN # define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) #else # define ZLIB_INTERNAL #endif #include <stdio.h> #include "zlib.h" #ifdef STDC # include <string.h> # include <stdlib.h> # include <limits.h> #endif #ifndef _POSIX_SOURCE # define _POSIX_SOURCE #endif #include <fcntl.h> #ifdef _WIN32 # include <stddef.h> #endif #if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) # include <io.h> #endif #if defined(_WIN32) # define WIDECHAR #endif #ifdef WINAPI_FAMILY # define open _open # define read _read # define write _write # define close _close #endif #ifdef NO_DEFLATE /* for compatibility with old definition */ # define NO_GZCOMPRESS #endif #if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #if defined(__CYGWIN__) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) # ifndef HAVE_VSNPRINTF # define HAVE_VSNPRINTF # endif #endif #ifndef HAVE_VSNPRINTF # ifdef MSDOS /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), but for now we just assume it doesn't. */ # define NO_vsnprintf # endif # ifdef __TURBOC__ # define NO_vsnprintf # endif # ifdef WIN32 /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ # if !defined(vsnprintf) && !defined(NO_vsnprintf) # if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) # define vsnprintf _vsnprintf # endif # endif # endif # ifdef __SASC # define NO_vsnprintf # endif # ifdef VMS # define NO_vsnprintf # endif # ifdef __OS400__ # define NO_vsnprintf # endif # ifdef __MVS__ # define NO_vsnprintf # endif #endif /* unlike snprintf (which is required in C99), _snprintf does not guarantee null termination of the result -- however this is only used in gzlib.c where the result is assured to fit in the space provided */ #if defined(_MSC_VER) && _MSC_VER < 1900 # define snprintf _snprintf #endif #ifndef local # define local static #endif /* since "static" is used to mean two completely different things in C, we define "local" for the non-static meaning of "static", for readability (compile with -Dlocal if your debugger can't find static symbols) */ /* gz* functions always use library allocation functions */ #ifndef STDC extern voidp malloc(uInt size); extern void free(voidpf ptr); #endif /* get errno and strerror definition */ #if defined UNDER_CE # include <windows.h> # define zstrerror() gz_strwinerror((DWORD)GetLastError()) #else # ifndef NO_STRERROR # include <errno.h> # define zstrerror() strerror(errno) # else # define zstrerror() "stdio error (consult errno)" # endif #endif /* provide prototypes for these when building zlib without LFS */ #if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); #endif /* default memLevel */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default i/o buffer size -- double this for output when reading (this and twice this must be able to fit in an unsigned type) */ #define GZBUFSIZE 8192 /* gzip modes, also provide a little integrity check on the passed structure */ #define GZ_NONE 0 #define GZ_READ 7247 #define GZ_WRITE 31153 #define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ /* values for gz_state how */ #define LOOK 0 /* look for a gzip header */ #define COPY 1 /* copy input directly */ #define GZIP 2 /* decompress a gzip stream */ /* internal gzip file state data structure */ typedef struct { /* exposed contents for gzgetc() macro */ struct gzFile_s x; /* "x" for exposed */ /* x.have: number of bytes available at x.next */ /* x.next: next output data to deliver or write */ /* x.pos: current position in uncompressed data */ /* used for both reading and writing */ int mode; /* see gzip modes above */ int fd; /* file descriptor */ char *path; /* path or fd for error messages */ unsigned size; /* buffer size, zero if not allocated yet */ unsigned want; /* requested buffer size, default is GZBUFSIZE */ unsigned char *in; /* input buffer (double-sized when writing) */ unsigned char *out; /* output buffer (double-sized when reading) */ int direct; /* 0 if processing gzip, 1 if transparent */ /* just for reading */ int how; /* 0: get header, 1: copy, 2: decompress */ z_off64_t start; /* where the gzip data started, for rewinding */ int eof; /* true if end of input file reached */ int past; /* true if read requested past end */ /* just for writing */ int level; /* compression level */ int strategy; /* compression strategy */ int reset; /* true if a reset is pending after a Z_FINISH */ /* seek request */ z_off64_t skip; /* amount to skip (already rewound if backwards) */ int seek; /* true if seek request pending */ /* error information */ int err; /* error code */ char *msg; /* error message */ /* zlib inflate or deflate stream */ z_stream strm; /* stream structure in-place (not a pointer) */ } gz_state; typedef gz_state FAR *gz_statep; /* shared functions */ void ZLIB_INTERNAL gz_error(gz_statep, int, const char *); #if defined UNDER_CE char ZLIB_INTERNAL *gz_strwinerror(DWORD error); #endif /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t value -- needed when comparing unsigned to z_off64_t, which is signed (possible z_off64_t types off_t, off64_t, and long are all signed) */ unsigned ZLIB_INTERNAL gz_intmax(void); #define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) |
Added compat/zlib/gzlib.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 | /* gzlib.c -- zlib functions common to reading and writing gzip files * Copyright (C) 2004-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" #if defined(_WIN32) && !defined(__BORLANDC__) # define LSEEK _lseeki64 #else #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 # define LSEEK lseek64 #else # define LSEEK lseek #endif #endif #if defined UNDER_CE /* Map the Windows error number in ERROR to a locale-dependent error message string and return a pointer to it. Typically, the values for ERROR come from GetLastError. The string pointed to shall not be modified by the application, but may be overwritten by a subsequent call to gz_strwinerror The gz_strwinerror function does not change the current setting of GetLastError. */ char ZLIB_INTERNAL *gz_strwinerror(DWORD error) { static char buf[1024]; wchar_t *msgbuf; DWORD lasterr = GetLastError(); DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error, 0, /* Default language */ (LPVOID)&msgbuf, 0, NULL); if (chars != 0) { /* If there is an \r\n appended, zap it. */ if (chars >= 2 && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { chars -= 2; msgbuf[chars] = 0; } if (chars > sizeof (buf) - 1) { chars = sizeof (buf) - 1; msgbuf[chars] = 0; } wcstombs(buf, msgbuf, chars + 1); LocalFree(msgbuf); } else { sprintf(buf, "unknown win32 error (%ld)", error); } SetLastError(lasterr); return buf; } #endif /* UNDER_CE */ /* Reset gzip file state */ local void gz_reset(gz_statep state) { state->x.have = 0; /* no output data available */ if (state->mode == GZ_READ) { /* for reading ... */ state->eof = 0; /* not at end of file */ state->past = 0; /* have not read past end yet */ state->how = LOOK; /* look for gzip header */ } else /* for writing ... */ state->reset = 0; /* no deflateReset pending */ state->seek = 0; /* no seek request pending */ gz_error(state, Z_OK, NULL); /* clear error */ state->x.pos = 0; /* no uncompressed data yet */ state->strm.avail_in = 0; /* no input data yet */ } /* Open a gzip file either by name or file descriptor. */ local gzFile gz_open(const void *path, int fd, const char *mode) { gz_statep state; z_size_t len; int oflag; #ifdef O_CLOEXEC int cloexec = 0; #endif #ifdef O_EXCL int exclusive = 0; #endif /* check input */ if (path == NULL) return NULL; /* allocate gzFile structure to return */ state = (gz_statep)malloc(sizeof(gz_state)); if (state == NULL) return NULL; state->size = 0; /* no buffers allocated yet */ state->want = GZBUFSIZE; /* requested buffer size */ state->msg = NULL; /* no error message yet */ /* interpret mode */ state->mode = GZ_NONE; state->level = Z_DEFAULT_COMPRESSION; state->strategy = Z_DEFAULT_STRATEGY; state->direct = 0; while (*mode) { if (*mode >= '0' && *mode <= '9') state->level = *mode - '0'; else switch (*mode) { case 'r': state->mode = GZ_READ; break; #ifndef NO_GZCOMPRESS case 'w': state->mode = GZ_WRITE; break; case 'a': state->mode = GZ_APPEND; break; #endif case '+': /* can't read and write at the same time */ free(state); return NULL; case 'b': /* ignore -- will request binary anyway */ break; #ifdef O_CLOEXEC case 'e': cloexec = 1; break; #endif #ifdef O_EXCL case 'x': exclusive = 1; break; #endif case 'f': state->strategy = Z_FILTERED; break; case 'h': state->strategy = Z_HUFFMAN_ONLY; break; case 'R': state->strategy = Z_RLE; break; case 'F': state->strategy = Z_FIXED; break; case 'T': state->direct = 1; break; default: /* could consider as an error, but just ignore */ ; } mode++; } /* must provide an "r", "w", or "a" */ if (state->mode == GZ_NONE) { free(state); return NULL; } /* can't force transparent read */ if (state->mode == GZ_READ) { if (state->direct) { free(state); return NULL; } state->direct = 1; /* for empty file */ } /* save the path name for error messages */ #ifdef WIDECHAR if (fd == -2) { len = wcstombs(NULL, path, 0); if (len == (z_size_t)-1) len = 0; } else #endif len = strlen((const char *)path); state->path = (char *)malloc(len + 1); if (state->path == NULL) { free(state); return NULL; } #ifdef WIDECHAR if (fd == -2) if (len) wcstombs(state->path, path, len + 1); else *(state->path) = 0; else #endif #if !defined(NO_snprintf) && !defined(NO_vsnprintf) (void)snprintf(state->path, len + 1, "%s", (const char *)path); #else strcpy(state->path, path); #endif /* compute the flags for open() */ oflag = #ifdef O_LARGEFILE O_LARGEFILE | #endif #ifdef O_BINARY O_BINARY | #endif #ifdef O_CLOEXEC (cloexec ? O_CLOEXEC : 0) | #endif (state->mode == GZ_READ ? O_RDONLY : (O_WRONLY | O_CREAT | #ifdef O_EXCL (exclusive ? O_EXCL : 0) | #endif (state->mode == GZ_WRITE ? O_TRUNC : O_APPEND))); /* open the file with the appropriate flags (or just use fd) */ state->fd = fd > -1 ? fd : ( #ifdef WIDECHAR fd == -2 ? _wopen(path, oflag, 0666) : #endif open((const char *)path, oflag, 0666)); if (state->fd == -1) { free(state->path); free(state); return NULL; } if (state->mode == GZ_APPEND) { LSEEK(state->fd, 0, SEEK_END); /* so gzoffset() is correct */ state->mode = GZ_WRITE; /* simplify later checks */ } /* save the current position for rewinding (only if reading) */ if (state->mode == GZ_READ) { state->start = LSEEK(state->fd, 0, SEEK_CUR); if (state->start == -1) state->start = 0; } /* initialize stream */ gz_reset(state); /* return stream */ return (gzFile)state; } /* -- see zlib.h -- */ gzFile ZEXPORT gzopen(const char *path, const char *mode) { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ gzFile ZEXPORT gzopen64(const char *path, const char *mode) { return gz_open(path, -1, mode); } /* -- see zlib.h -- */ gzFile ZEXPORT gzdopen(int fd, const char *mode) { char *path; /* identifier for error messages */ gzFile gz; if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) return NULL; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) (void)snprintf(path, 7 + 3 * sizeof(int), "<fd:%d>", fd); #else sprintf(path, "<fd:%d>", fd); /* for debugging */ #endif gz = gz_open(path, fd, mode); free(path); return gz; } /* -- see zlib.h -- */ #ifdef WIDECHAR gzFile ZEXPORT gzopen_w(const wchar_t *path, const char *mode) { return gz_open(path, -2, mode); } #endif /* -- see zlib.h -- */ int ZEXPORT gzbuffer(gzFile file, unsigned size) { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* make sure we haven't already allocated memory */ if (state->size != 0) return -1; /* check and set requested size */ if ((size << 1) < size) return -1; /* need to be able to double it */ if (size < 8) size = 8; /* needed to behave well with flushing */ state->want = size; return 0; } /* -- see zlib.h -- */ int ZEXPORT gzrewind(gzFile file) { gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're reading and that there's no error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return -1; /* back up and start over */ if (LSEEK(state->fd, state->start, SEEK_SET) == -1) return -1; gz_reset(state); return 0; } /* -- see zlib.h -- */ z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) { unsigned n; z_off64_t ret; gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* check that there's no error */ if (state->err != Z_OK && state->err != Z_BUF_ERROR) return -1; /* can only seek from start or relative to current position */ if (whence != SEEK_SET && whence != SEEK_CUR) return -1; /* normalize offset to a SEEK_CUR specification */ if (whence == SEEK_SET) offset -= state->x.pos; else if (state->seek) offset += state->skip; state->seek = 0; /* if within raw area while reading, just go there */ if (state->mode == GZ_READ && state->how == COPY && state->x.pos + offset >= 0) { ret = LSEEK(state->fd, offset - (z_off64_t)state->x.have, SEEK_CUR); if (ret == -1) return -1; state->x.have = 0; state->eof = 0; state->past = 0; state->seek = 0; gz_error(state, Z_OK, NULL); state->strm.avail_in = 0; state->x.pos += offset; return state->x.pos; } /* calculate skip amount, rewinding if needed for back seek when reading */ if (offset < 0) { if (state->mode != GZ_READ) /* writing -- can't go backwards */ return -1; offset += state->x.pos; if (offset < 0) /* before start of file! */ return -1; if (gzrewind(file) == -1) /* rewind, then skip to offset */ return -1; } /* if reading, skip what's in output buffer (one less gzgetc() check) */ if (state->mode == GZ_READ) { n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ? (unsigned)offset : state->x.have; state->x.have -= n; state->x.next += n; state->x.pos += n; offset -= n; } /* request skip (if not zero) */ if (offset) { state->seek = 1; state->skip = offset; } return state->x.pos + offset; } /* -- see zlib.h -- */ z_off_t ZEXPORT gzseek(gzFile file, z_off_t offset, int whence) { z_off64_t ret; ret = gzseek64(file, (z_off64_t)offset, whence); return ret == (z_off_t)ret ? (z_off_t)ret : -1; } /* -- see zlib.h -- */ z_off64_t ZEXPORT gztell64(gzFile file) { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* return position */ return state->x.pos + (state->seek ? state->skip : 0); } /* -- see zlib.h -- */ z_off_t ZEXPORT gztell(gzFile file) { z_off64_t ret; ret = gztell64(file); return ret == (z_off_t)ret ? (z_off_t)ret : -1; } /* -- see zlib.h -- */ z_off64_t ZEXPORT gzoffset64(gzFile file) { z_off64_t offset; gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return -1; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return -1; /* compute and return effective offset in file */ offset = LSEEK(state->fd, 0, SEEK_CUR); if (offset == -1) return -1; if (state->mode == GZ_READ) /* reading */ offset -= state->strm.avail_in; /* don't count buffered input */ return offset; } /* -- see zlib.h -- */ z_off_t ZEXPORT gzoffset(gzFile file) { z_off64_t ret; ret = gzoffset64(file); return ret == (z_off_t)ret ? (z_off_t)ret : -1; } /* -- see zlib.h -- */ int ZEXPORT gzeof(gzFile file) { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return 0; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return 0; /* return end-of-file state */ return state->mode == GZ_READ ? state->past : 0; } /* -- see zlib.h -- */ const char * ZEXPORT gzerror(gzFile file, int *errnum) { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return NULL; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return NULL; /* return error information */ if (errnum != NULL) *errnum = state->err; return state->err == Z_MEM_ERROR ? "out of memory" : (state->msg == NULL ? "" : state->msg); } /* -- see zlib.h -- */ void ZEXPORT gzclearerr(gzFile file) { gz_statep state; /* get internal structure and check integrity */ if (file == NULL) return; state = (gz_statep)file; if (state->mode != GZ_READ && state->mode != GZ_WRITE) return; /* clear error and end-of-file */ if (state->mode == GZ_READ) { state->eof = 0; state->past = 0; } gz_error(state, Z_OK, NULL); } /* Create an error message in allocated memory and set state->err and state->msg accordingly. Free any previous error message already there. Do not try to free or allocate space if the error is Z_MEM_ERROR (out of memory). Simply save the error message as a static string. If there is an allocation failure constructing the error message, then convert the error to out of memory. */ void ZLIB_INTERNAL gz_error(gz_statep state, int err, const char *msg) { /* free previously allocated message and clear */ if (state->msg != NULL) { if (state->err != Z_MEM_ERROR) free(state->msg); state->msg = NULL; } /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */ if (err != Z_OK && err != Z_BUF_ERROR) state->x.have = 0; /* set error code, and if no message, then done */ state->err = err; if (msg == NULL) return; /* for an out of memory error, return literal string when requested */ if (err == Z_MEM_ERROR) return; /* construct error message with path */ if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) { state->err = Z_MEM_ERROR; return; } #if !defined(NO_snprintf) && !defined(NO_vsnprintf) (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, "%s%s%s", state->path, ": ", msg); #else strcpy(state->msg, state->path); strcat(state->msg, ": "); strcat(state->msg, msg); #endif } /* portably return maximum value for an int (when limits.h presumed not available) -- we need to do this to cover cases where 2's complement not used, since C standard permits 1's complement and sign-bit representations, otherwise we could just use ((unsigned)-1) >> 1 */ unsigned ZLIB_INTERNAL gz_intmax(void) { #ifdef INT_MAX return INT_MAX; #else unsigned p = 1, q; do { q = p; p <<= 1; p++; } while (p > q); return q >> 1; #endif } |
Added compat/zlib/gzread.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 | /* gzread.c -- zlib functions for reading gzip files * Copyright (C) 2004-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" /* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from state->fd, and update state->eof, state->err, and state->msg as appropriate. This function needs to loop on read(), since read() is not guaranteed to read the number of bytes requested, depending on the type of descriptor. */ local int gz_load(gz_statep state, unsigned char *buf, unsigned len, unsigned *have) { int ret; unsigned get, max = ((unsigned)-1 >> 2) + 1; *have = 0; do { get = len - *have; if (get > max) get = max; ret = read(state->fd, buf + *have, get); if (ret <= 0) break; *have += (unsigned)ret; } while (*have < len); if (ret < 0) { gz_error(state, Z_ERRNO, zstrerror()); return -1; } if (ret == 0) state->eof = 1; return 0; } /* Load up input buffer and set eof flag if last data loaded -- return -1 on error, 0 otherwise. Note that the eof flag is set when the end of the input file is reached, even though there may be unused data in the buffer. Once that data has been used, no more attempts will be made to read the file. If strm->avail_in != 0, then the current data is moved to the beginning of the input buffer, and then the remainder of the buffer is loaded with the available data from the input file. */ local int gz_avail(gz_statep state) { unsigned got; z_streamp strm = &(state->strm); if (state->err != Z_OK && state->err != Z_BUF_ERROR) return -1; if (state->eof == 0) { if (strm->avail_in) { /* copy what's there to the start */ unsigned char *p = state->in; unsigned const char *q = strm->next_in; unsigned n = strm->avail_in; do { *p++ = *q++; } while (--n); } if (gz_load(state, state->in + strm->avail_in, state->size - strm->avail_in, &got) == -1) return -1; strm->avail_in += got; strm->next_in = state->in; } return 0; } /* Look for gzip header, set up for inflate or copy. state->x.have must be 0. If this is the first time in, allocate required memory. state->how will be left unchanged if there is no more input data available, will be set to COPY if there is no gzip header and direct copying will be performed, or it will be set to GZIP for decompression. If direct copying, then leftover input data from the input buffer will be copied to the output buffer. In that case, all further file reads will be directly to either the output buffer or a user buffer. If decompressing, the inflate state will be initialized. gz_look() will return 0 on success or -1 on failure. */ local int gz_look(gz_statep state) { z_streamp strm = &(state->strm); /* allocate read buffers and inflate memory */ if (state->size == 0) { /* allocate buffers */ state->in = (unsigned char *)malloc(state->want); state->out = (unsigned char *)malloc(state->want << 1); if (state->in == NULL || state->out == NULL) { free(state->out); free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } state->size = state->want; /* allocate inflate memory */ state->strm.zalloc = Z_NULL; state->strm.zfree = Z_NULL; state->strm.opaque = Z_NULL; state->strm.avail_in = 0; state->strm.next_in = Z_NULL; if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */ free(state->out); free(state->in); state->size = 0; gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } } /* get at least the magic bytes in the input buffer */ if (strm->avail_in < 2) { if (gz_avail(state) == -1) return -1; if (strm->avail_in == 0) return 0; } /* look for gzip magic bytes -- if there, do gzip decoding (note: there is a logical dilemma here when considering the case of a partially written gzip file, to wit, if a single 31 byte is written, then we cannot tell whether this is a single-byte file, or just a partially written gzip file -- for here we assume that if a gzip file is being written, then the header will be written in a single operation, so that reading a single byte is sufficient indication that it is not a gzip file) */ if (strm->avail_in > 1 && strm->next_in[0] == 31 && strm->next_in[1] == 139) { inflateReset(strm); state->how = GZIP; state->direct = 0; return 0; } /* no gzip header -- if we were decoding gzip before, then this is trailing garbage. Ignore the trailing garbage and finish. */ if (state->direct == 0) { strm->avail_in = 0; state->eof = 1; state->x.have = 0; return 0; } /* doing raw i/o, copy any leftover input to output -- this assumes that the output buffer is larger than the input buffer, which also assures space for gzungetc() */ state->x.next = state->out; memcpy(state->x.next, strm->next_in, strm->avail_in); state->x.have = strm->avail_in; strm->avail_in = 0; state->how = COPY; state->direct = 1; return 0; } /* Decompress from input to the provided next_out and avail_out in the state. On return, state->x.have and state->x.next point to the just decompressed data. If the gzip stream completes, state->how is reset to LOOK to look for the next gzip stream or raw data, once state->x.have is depleted. Returns 0 on success, -1 on failure. */ local int gz_decomp(gz_statep state) { int ret = Z_OK; unsigned had; z_streamp strm = &(state->strm); /* fill output buffer up to end of deflate stream */ had = strm->avail_out; do { /* get more input for inflate() */ if (strm->avail_in == 0 && gz_avail(state) == -1) return -1; if (strm->avail_in == 0) { gz_error(state, Z_BUF_ERROR, "unexpected end of file"); break; } /* decompress and handle errors */ ret = inflate(strm, Z_NO_FLUSH); if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { gz_error(state, Z_STREAM_ERROR, "internal error: inflate stream corrupt"); return -1; } if (ret == Z_MEM_ERROR) { gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ gz_error(state, Z_DATA_ERROR, strm->msg == NULL ? "compressed data error" : strm->msg); return -1; } } while (strm->avail_out && ret != Z_STREAM_END); /* update available output */ state->x.have = had - strm->avail_out; state->x.next = strm->next_out - state->x.have; /* if the gzip stream completed successfully, look for another */ if (ret == Z_STREAM_END) state->how = LOOK; /* good decompression */ return 0; } /* Fetch data and put it in the output buffer. Assumes state->x.have is 0. Data is either copied from the input file or decompressed from the input file depending on state->how. If state->how is LOOK, then a gzip header is looked for to determine whether to copy or decompress. Returns -1 on error, otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the end of the input file has been reached and all data has been processed. */ local int gz_fetch(gz_statep state) { z_streamp strm = &(state->strm); do { switch(state->how) { case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */ if (gz_look(state) == -1) return -1; if (state->how == LOOK) return 0; break; case COPY: /* -> COPY */ if (gz_load(state, state->out, state->size << 1, &(state->x.have)) == -1) return -1; state->x.next = state->out; return 0; case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */ strm->avail_out = state->size << 1; strm->next_out = state->out; if (gz_decomp(state) == -1) return -1; } } while (state->x.have == 0 && (!state->eof || strm->avail_in)); return 0; } /* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ local int gz_skip(gz_statep state, z_off64_t len) { unsigned n; /* skip over len bytes or reach end-of-file, whichever comes first */ while (len) /* skip over whatever is in output buffer */ if (state->x.have) { n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ? (unsigned)len : state->x.have; state->x.have -= n; state->x.next += n; state->x.pos += n; len -= n; } /* output buffer empty -- return if we're at the end of the input */ else if (state->eof && state->strm.avail_in == 0) break; /* need more data to skip -- load up output buffer */ else { /* get more output, looking for header if required */ if (gz_fetch(state) == -1) return -1; } return 0; } /* Read len bytes into buf from file, or less than len up to the end of the input. Return the number of bytes read. If zero is returned, either the end of file was reached, or there was an error. state->err must be consulted in that case to determine which. */ local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { z_size_t got; unsigned n; /* if len is zero, avoid unnecessary operations */ if (len == 0) return 0; /* process a skip request */ if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) return 0; } /* get len bytes to buf, or less than len if at the end */ got = 0; do { /* set n to the maximum amount of len that fits in an unsigned int */ n = (unsigned)-1; if (n > len) n = (unsigned)len; /* first just try copying data from the output buffer */ if (state->x.have) { if (state->x.have < n) n = state->x.have; memcpy(buf, state->x.next, n); state->x.next += n; state->x.have -= n; } /* output buffer empty -- return if we're at the end of the input */ else if (state->eof && state->strm.avail_in == 0) { state->past = 1; /* tried to read past end */ break; } /* need output data -- for small len or new stream load up our output buffer */ else if (state->how == LOOK || n < (state->size << 1)) { /* get more output, looking for header if required */ if (gz_fetch(state) == -1) return 0; continue; /* no progress yet -- go back to copy above */ /* the copy above assures that we will leave with space in the output buffer, allowing at least one gzungetc() to succeed */ } /* large len -- read directly into user buffer */ else if (state->how == COPY) { /* read directly */ if (gz_load(state, (unsigned char *)buf, n, &n) == -1) return 0; } /* large len -- decompress directly into user buffer */ else { /* state->how == GZIP */ state->strm.avail_out = n; state->strm.next_out = (unsigned char *)buf; if (gz_decomp(state) == -1) return 0; n = state->x.have; state->x.have = 0; } /* update progress */ len -= n; buf = (char *)buf + n; got += n; state->x.pos += n; } while (len); /* return number of bytes read into user buffer */ return got; } /* -- see zlib.h -- */ int ZEXPORT gzread(gzFile file, voidp buf, unsigned len) { gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return -1; /* since an int is returned, make sure len fits in one, otherwise return with an error (this avoids a flaw in the interface) */ if ((int)len < 0) { gz_error(state, Z_STREAM_ERROR, "request does not fit in an int"); return -1; } /* read len or fewer bytes to buf */ len = (unsigned)gz_read(state, buf, len); /* check for an error */ if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR) return -1; /* return the number of bytes read (this is assured to fit in an int) */ return (int)len; } /* -- see zlib.h -- */ z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file) { z_size_t len; gz_statep state; /* get internal structure */ if (file == NULL) return 0; state = (gz_statep)file; /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return 0; /* compute bytes to read -- error on overflow */ len = nitems * size; if (size && len / size != nitems) { gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); return 0; } /* read len or fewer bytes to buf, return the number of full items read */ return len ? gz_read(state, buf, len) / size : 0; } /* -- see zlib.h -- */ #ifdef Z_PREFIX_SET # undef z_gzgetc #else # undef gzgetc #endif int ZEXPORT gzgetc(gzFile file) { unsigned char buf[1]; gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return -1; /* try output buffer (no need to check for skip request) */ if (state->x.have) { state->x.have--; state->x.pos++; return *(state->x.next)++; } /* nothing there -- try gz_read() */ return gz_read(state, buf, 1) < 1 ? -1 : buf[0]; } int ZEXPORT gzgetc_(gzFile file) { return gzgetc(file); } /* -- see zlib.h -- */ int ZEXPORT gzungetc(int c, gzFile file) { gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* in case this was just opened, set up the input buffer */ if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) (void)gz_look(state); /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return -1; /* process a skip request */ if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) return -1; } /* can't push EOF */ if (c < 0) return -1; /* if output buffer empty, put byte at end (allows more pushing) */ if (state->x.have == 0) { state->x.have = 1; state->x.next = state->out + (state->size << 1) - 1; state->x.next[0] = (unsigned char)c; state->x.pos--; state->past = 0; return c; } /* if no room, give up (must have already done a gzungetc()) */ if (state->x.have == (state->size << 1)) { gz_error(state, Z_DATA_ERROR, "out of room to push characters"); return -1; } /* slide output data if needed and insert byte before existing data */ if (state->x.next == state->out) { unsigned char *src = state->out + state->x.have; unsigned char *dest = state->out + (state->size << 1); while (src > state->out) *--dest = *--src; state->x.next = dest; } state->x.have++; state->x.next--; state->x.next[0] = (unsigned char)c; state->x.pos--; state->past = 0; return c; } /* -- see zlib.h -- */ char * ZEXPORT gzgets(gzFile file, char *buf, int len) { unsigned left, n; char *str; unsigned char *eol; gz_statep state; /* check parameters and get internal structure */ if (file == NULL || buf == NULL || len < 1) return NULL; state = (gz_statep)file; /* check that we're reading and that there's no (serious) error */ if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR)) return NULL; /* process a skip request */ if (state->seek) { state->seek = 0; if (gz_skip(state, state->skip) == -1) return NULL; } /* copy output bytes up to new line or len - 1, whichever comes first -- append a terminating zero to the string (we don't check for a zero in the contents, let the user worry about that) */ str = buf; left = (unsigned)len - 1; if (left) do { /* assure that something is in the output buffer */ if (state->x.have == 0 && gz_fetch(state) == -1) return NULL; /* error */ if (state->x.have == 0) { /* end of file */ state->past = 1; /* read past end */ break; /* return what we have */ } /* look for end-of-line in current output buffer */ n = state->x.have > left ? left : state->x.have; eol = (unsigned char *)memchr(state->x.next, '\n', n); if (eol != NULL) n = (unsigned)(eol - state->x.next) + 1; /* copy through end-of-line, or remainder if not found */ memcpy(buf, state->x.next, n); state->x.have -= n; state->x.next += n; state->x.pos += n; left -= n; buf += n; } while (left && eol == NULL); /* return terminated string, or if nothing, end of file */ if (buf == str) return NULL; buf[0] = 0; return str; } /* -- see zlib.h -- */ int ZEXPORT gzdirect(gzFile file) { gz_statep state; /* get internal structure */ if (file == NULL) return 0; state = (gz_statep)file; /* if the state is not known, but we can find out, then do so (this is mainly for right after a gzopen() or gzdopen()) */ if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) (void)gz_look(state); /* return 1 if transparent, 0 if processing a gzip stream */ return state->direct; } /* -- see zlib.h -- */ int ZEXPORT gzclose_r(gzFile file) { int ret, err; gz_statep state; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; /* check that we're reading */ if (state->mode != GZ_READ) return Z_STREAM_ERROR; /* free memory and close file */ if (state->size) { inflateEnd(&(state->strm)); free(state->out); free(state->in); } err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK; gz_error(state, Z_OK, NULL); free(state->path); ret = close(state->fd); free(state); return ret ? Z_ERRNO : err; } |
Added compat/zlib/gzwrite.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 | /* gzwrite.c -- zlib functions for writing gzip files * Copyright (C) 2004-2019 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" /* Initialize state for writing a gzip file. Mark initialization by setting state->size to non-zero. Return -1 on a memory allocation failure, or 0 on success. */ local int gz_init(gz_statep state) { int ret; z_streamp strm = &(state->strm); /* allocate input buffer (double size for gzprintf) */ state->in = (unsigned char *)malloc(state->want << 1); if (state->in == NULL) { gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } /* only need output buffer and deflate state if compressing */ if (!state->direct) { /* allocate output buffer */ state->out = (unsigned char *)malloc(state->want); if (state->out == NULL) { free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } /* allocate deflate memory, set up for gzip compression */ strm->zalloc = Z_NULL; strm->zfree = Z_NULL; strm->opaque = Z_NULL; ret = deflateInit2(strm, state->level, Z_DEFLATED, MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy); if (ret != Z_OK) { free(state->out); free(state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } strm->next_in = NULL; } /* mark state as initialized */ state->size = state->want; /* initialize write buffer if compressing */ if (!state->direct) { strm->avail_out = state->size; strm->next_out = state->out; state->x.next = strm->next_out; } return 0; } /* Compress whatever is at avail_in and next_in and write to the output file. Return -1 if there is an error writing to the output file or if gz_init() fails to allocate memory, otherwise 0. flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, then the deflate() state is reset to start a new gzip stream. If gz->direct is true, then simply write to the output file without compressing, and ignore flush. */ local int gz_comp(gz_statep state, int flush) { int ret, writ; unsigned have, put, max = ((unsigned)-1 >> 2) + 1; z_streamp strm = &(state->strm); /* allocate memory if this is the first time through */ if (state->size == 0 && gz_init(state) == -1) return -1; /* write directly if requested */ if (state->direct) { while (strm->avail_in) { put = strm->avail_in > max ? max : strm->avail_in; writ = write(state->fd, strm->next_in, put); if (writ < 0) { gz_error(state, Z_ERRNO, zstrerror()); return -1; } strm->avail_in -= (unsigned)writ; strm->next_in += writ; } return 0; } /* check for a pending reset */ if (state->reset) { /* don't start a new gzip member unless there is data to write */ if (strm->avail_in == 0) return 0; deflateReset(strm); state->reset = 0; } /* run deflate() on provided input until it produces no more output */ ret = Z_OK; do { /* write out current buffer contents if full, or if flushing, but if doing Z_FINISH then don't write until we get to Z_STREAM_END */ if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && (flush != Z_FINISH || ret == Z_STREAM_END))) { while (strm->next_out > state->x.next) { put = strm->next_out - state->x.next > (int)max ? max : (unsigned)(strm->next_out - state->x.next); writ = write(state->fd, state->x.next, put); if (writ < 0) { gz_error(state, Z_ERRNO, zstrerror()); return -1; } state->x.next += writ; } if (strm->avail_out == 0) { strm->avail_out = state->size; strm->next_out = state->out; state->x.next = state->out; } } /* compress */ have = strm->avail_out; ret = deflate(strm, flush); if (ret == Z_STREAM_ERROR) { gz_error(state, Z_STREAM_ERROR, "internal error: deflate stream corrupt"); return -1; } have -= strm->avail_out; } while (have); /* if that completed a deflate stream, allow another to start */ if (flush == Z_FINISH) state->reset = 1; /* all done, no errors */ return 0; } /* Compress len zeros to output. Return -1 on a write error or memory allocation failure by gz_comp(), or 0 on success. */ local int gz_zero(gz_statep state, z_off64_t len) { int first; unsigned n; z_streamp strm = &(state->strm); /* consume whatever's left in the input buffer */ if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return -1; /* compress len zeros (len guaranteed > 0) */ first = 1; while (len) { n = GT_OFF(state->size) || (z_off64_t)state->size > len ? (unsigned)len : state->size; if (first) { memset(state->in, 0, n); first = 0; } strm->avail_in = n; strm->next_in = state->in; state->x.pos += n; if (gz_comp(state, Z_NO_FLUSH) == -1) return -1; len -= n; } return 0; } /* Write len bytes from buf to file. Return the number of bytes written. If the returned value is less than len, then there was an error. */ local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) { z_size_t put = len; /* if len is zero, avoid unnecessary operations */ if (len == 0) return 0; /* allocate memory if this is the first time through */ if (state->size == 0 && gz_init(state) == -1) return 0; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return 0; } /* for small len, copy to input buffer, otherwise compress directly */ if (len < state->size) { /* copy to input buffer, compress when full */ do { unsigned have, copy; if (state->strm.avail_in == 0) state->strm.next_in = state->in; have = (unsigned)((state->strm.next_in + state->strm.avail_in) - state->in); copy = state->size - have; if (copy > len) copy = (unsigned)len; memcpy(state->in + have, buf, copy); state->strm.avail_in += copy; state->x.pos += copy; buf = (const char *)buf + copy; len -= copy; if (len && gz_comp(state, Z_NO_FLUSH) == -1) return 0; } while (len); } else { /* consume whatever's left in the input buffer */ if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return 0; /* directly compress user buffer to file */ state->strm.next_in = (z_const Bytef *)buf; do { unsigned n = (unsigned)-1; if (n > len) n = (unsigned)len; state->strm.avail_in = n; state->x.pos += n; if (gz_comp(state, Z_NO_FLUSH) == -1) return 0; len -= n; } while (len); } /* input was all buffered or compressed */ return put; } /* -- see zlib.h -- */ int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len) { gz_statep state; /* get internal structure */ if (file == NULL) return 0; state = (gz_statep)file; /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return 0; /* since an int is returned, make sure len fits in one, otherwise return with an error (this avoids a flaw in the interface) */ if ((int)len < 0) { gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); return 0; } /* write len bytes from buf (the return value will fit in an int) */ return (int)gz_write(state, buf, len); } /* -- see zlib.h -- */ z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, z_size_t nitems, gzFile file) { z_size_t len; gz_statep state; /* get internal structure */ if (file == NULL) return 0; state = (gz_statep)file; /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return 0; /* compute bytes to read -- error on overflow */ len = nitems * size; if (size && len / size != nitems) { gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); return 0; } /* write len bytes to buf, return the number of full items written */ return len ? gz_write(state, buf, len) / size : 0; } /* -- see zlib.h -- */ int ZEXPORT gzputc(gzFile file, int c) { unsigned have; unsigned char buf[1]; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return -1; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return -1; } /* try writing to input buffer for speed (state->size == 0 if buffer not initialized) */ if (state->size) { if (strm->avail_in == 0) strm->next_in = state->in; have = (unsigned)((strm->next_in + strm->avail_in) - state->in); if (have < state->size) { state->in[have] = (unsigned char)c; strm->avail_in++; state->x.pos++; return c & 0xff; } } /* no room in buffer or not initialized, use gz_write() */ buf[0] = (unsigned char)c; if (gz_write(state, buf, 1) != 1) return -1; return c & 0xff; } /* -- see zlib.h -- */ int ZEXPORT gzputs(gzFile file, const char *s) { z_size_t len, put; gz_statep state; /* get internal structure */ if (file == NULL) return -1; state = (gz_statep)file; /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return -1; /* write string */ len = strlen(s); if ((int)len < 0 || (unsigned)len != len) { gz_error(state, Z_STREAM_ERROR, "string length does not fit in int"); return -1; } put = gz_write(state, s, len); return put < len ? -1 : (int)len; } #if defined(STDC) || defined(Z_HAVE_STDARG_H) #include <stdarg.h> /* -- see zlib.h -- */ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { int len; unsigned left; char *next; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return Z_STREAM_ERROR; /* make sure we have some buffer space */ if (state->size == 0 && gz_init(state) == -1) return state->err; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return state->err; } /* do the printf() into the input buffer, put length in len -- the input buffer is double-sized just for this function, so there is guaranteed to be state->size bytes available after the current contents */ if (strm->avail_in == 0) strm->next_in = state->in; next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in); next[state->size - 1] = 0; #ifdef NO_vsnprintf # ifdef HAS_vsprintf_void (void)vsprintf(next, format, va); for (len = 0; len < state->size; len++) if (next[len] == 0) break; # else len = vsprintf(next, format, va); # endif #else # ifdef HAS_vsnprintf_void (void)vsnprintf(next, state->size, format, va); len = strlen(next); # else len = vsnprintf(next, state->size, format, va); # endif #endif /* check that printf() results fit in buffer */ if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0) return 0; /* update buffer and position, compress first half if past that */ strm->avail_in += (unsigned)len; state->x.pos += len; if (strm->avail_in >= state->size) { left = strm->avail_in - state->size; strm->avail_in = state->size; if (gz_comp(state, Z_NO_FLUSH) == -1) return state->err; memmove(state->in, state->in + state->size, left); strm->next_in = state->in; strm->avail_in = left; } return len; } int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) { va_list va; int ret; va_start(va, format); ret = gzvprintf(file, format, va); va_end(va); return ret; } #else /* !STDC && !Z_HAVE_STDARG_H */ /* -- see zlib.h -- */ int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18, int a19, int a20) { unsigned len, left; char *next; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state->strm); /* check that can really pass pointer in ints */ if (sizeof(int) != sizeof(void *)) return Z_STREAM_ERROR; /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return Z_STREAM_ERROR; /* make sure we have some buffer space */ if (state->size == 0 && gz_init(state) == -1) return state->error; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return state->error; } /* do the printf() into the input buffer, put length in len -- the input buffer is double-sized just for this function, so there is guaranteed to be state->size bytes available after the current contents */ if (strm->avail_in == 0) strm->next_in = state->in; next = (char *)(strm->next_in + strm->avail_in); next[state->size - 1] = 0; #ifdef NO_snprintf # ifdef HAS_sprintf_void sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); for (len = 0; len < size; len++) if (next[len] == 0) break; # else len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #else # ifdef HAS_snprintf_void snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); len = strlen(next); # else len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #endif /* check that printf() results fit in buffer */ if (len == 0 || len >= state->size || next[state->size - 1] != 0) return 0; /* update buffer and position, compress first half if past that */ strm->avail_in += len; state->x.pos += len; if (strm->avail_in >= state->size) { left = strm->avail_in - state->size; strm->avail_in = state->size; if (gz_comp(state, Z_NO_FLUSH) == -1) return state->err; memmove(state->in, state->in + state->size, left); strm->next_in = state->in; strm->avail_in = left; } return (int)len; } #endif /* -- see zlib.h -- */ int ZEXPORT gzflush(gzFile file, int flush) { gz_statep state; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK) return Z_STREAM_ERROR; /* check flush parameter */ if (flush < 0 || flush > Z_FINISH) return Z_STREAM_ERROR; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return state->err; } /* compress remaining data with requested flush */ (void)gz_comp(state, flush); return state->err; } /* -- see zlib.h -- */ int ZEXPORT gzsetparams(gzFile file, int level, int strategy) { gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state->strm); /* check that we're writing and that there's no error */ if (state->mode != GZ_WRITE || state->err != Z_OK || state->direct) return Z_STREAM_ERROR; /* if no change is requested, then do nothing */ if (level == state->level && strategy == state->strategy) return Z_OK; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) return state->err; } /* change compression parameters for subsequent input */ if (state->size) { /* flush previous input with previous parameters before changing */ if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1) return state->err; deflateParams(strm, level, strategy); } state->level = level; state->strategy = strategy; return Z_OK; } /* -- see zlib.h -- */ int ZEXPORT gzclose_w(gzFile file) { int ret = Z_OK; gz_statep state; /* get internal structure */ if (file == NULL) return Z_STREAM_ERROR; state = (gz_statep)file; /* check that we're writing */ if (state->mode != GZ_WRITE) return Z_STREAM_ERROR; /* check for seek request */ if (state->seek) { state->seek = 0; if (gz_zero(state, state->skip) == -1) ret = state->err; } /* flush, free memory, and close file */ if (gz_comp(state, Z_FINISH) == -1) ret = state->err; if (state->size) { if (!state->direct) { (void)deflateEnd(&(state->strm)); free(state->out); } free(state->in); } gz_error(state, Z_OK, NULL); free(state->path); if (close(state->fd) == -1) ret = Z_ERRNO; free(state); return ret; } |
Added compat/zlib/infback.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 | /* infback.c -- inflate using a call-back interface * Copyright (C) 1995-2022 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* This code is largely copied from inflate.c. Normally either infback.o or inflate.o would be linked into an application--not both. The interface with inffast.c is retained so that optimized assembler-coded versions of inflate_fast() can be used with either inflate.c or infback.c. */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" /* strm provides memory allocation functions in zalloc and zfree, or Z_NULL to use the library memory allocation functions. windowBits is in the range 8..15, and window is a user-supplied window and output buffer that is 2**windowBits bytes. */ int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size) { struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != (int)(sizeof(z_stream))) return Z_VERSION_ERROR; if (strm == Z_NULL || window == Z_NULL || windowBits < 8 || windowBits > 15) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; #endif } if (strm->zfree == (free_func)0) #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zfree = zcfree; #endif state = (struct inflate_state FAR *)ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; state->dmax = 32768U; state->wbits = (uInt)windowBits; state->wsize = 1U << windowBits; state->window = window; state->wnext = 0; state->whave = 0; state->sane = 1; return Z_OK; } /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it's called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ local void fixedtables(struct inflate_state FAR *state) { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; static code fixed[544]; /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { unsigned sym, bits; static code *next; /* literal/length table */ sym = 0; while (sym < 144) state->lens[sym++] = 8; while (sym < 256) state->lens[sym++] = 9; while (sym < 280) state->lens[sym++] = 7; while (sym < 288) state->lens[sym++] = 8; next = fixed; lenfix = next; bits = 9; inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); /* distance table */ sym = 0; while (sym < 32) state->lens[sym++] = 5; distfix = next; bits = 5; inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); /* do this just once */ virgin = 0; } #else /* !BUILDFIXED */ # include "inffixed.h" #endif /* BUILDFIXED */ state->lencode = lenfix; state->lenbits = 9; state->distcode = distfix; state->distbits = 5; } /* Macros for inflateBack(): */ /* Load returned state from inflate_fast() */ #define LOAD() \ do { \ put = strm->next_out; \ left = strm->avail_out; \ next = strm->next_in; \ have = strm->avail_in; \ hold = state->hold; \ bits = state->bits; \ } while (0) /* Set state from registers for inflate_fast() */ #define RESTORE() \ do { \ strm->next_out = put; \ strm->avail_out = left; \ strm->next_in = next; \ strm->avail_in = have; \ state->hold = hold; \ state->bits = bits; \ } while (0) /* Clear the input bit accumulator */ #define INITBITS() \ do { \ hold = 0; \ bits = 0; \ } while (0) /* Assure that some input is available. If input is requested, but denied, then return a Z_BUF_ERROR from inflateBack(). */ #define PULL() \ do { \ if (have == 0) { \ have = in(in_desc, &next); \ if (have == 0) { \ next = Z_NULL; \ ret = Z_BUF_ERROR; \ goto inf_leave; \ } \ } \ } while (0) /* Get a byte of input into the bit accumulator, or return from inflateBack() with an error if there is no input available. */ #define PULLBYTE() \ do { \ PULL(); \ have--; \ hold += (unsigned long)(*next++) << bits; \ bits += 8; \ } while (0) /* Assure that there are at least n bits in the bit accumulator. If there is not enough available input to do that, then return from inflateBack() with an error. */ #define NEEDBITS(n) \ do { \ while (bits < (unsigned)(n)) \ PULLBYTE(); \ } while (0) /* Return the low n bits of the bit accumulator (n < 16) */ #define BITS(n) \ ((unsigned)hold & ((1U << (n)) - 1)) /* Remove n bits from the bit accumulator */ #define DROPBITS(n) \ do { \ hold >>= (n); \ bits -= (unsigned)(n); \ } while (0) /* Remove zero to seven bits as needed to go to a byte boundary */ #define BYTEBITS() \ do { \ hold >>= bits & 7; \ bits -= bits & 7; \ } while (0) /* Assure that some output space is available, by writing out the window if it's full. If the write fails, return from inflateBack() with a Z_BUF_ERROR. */ #define ROOM() \ do { \ if (left == 0) { \ put = state->window; \ left = state->wsize; \ state->whave = left; \ if (out(out_desc, put, left)) { \ ret = Z_BUF_ERROR; \ goto inf_leave; \ } \ } \ } while (0) /* strm provides the memory allocation functions and window buffer on input, and provides information on the unused input on return. For Z_DATA_ERROR returns, strm will also provide an error message. in() and out() are the call-back input and output functions. When inflateBack() needs more input, it calls in(). When inflateBack() has filled the window with output, or when it completes with data in the window, it calls out() to write out the data. The application must not change the provided input until in() is called again or inflateBack() returns. The application must not change the window/output buffer until inflateBack() returns. in() and out() are called with a descriptor parameter provided in the inflateBack() call. This parameter can be a structure that provides the information required to do the read or write, as well as accumulated information on the input and output such as totals and check values. in() should return zero on failure. out() should return non-zero on failure. If either in() or out() fails, than inflateBack() returns a Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it was in() or out() that caused in the error. Otherwise, inflateBack() returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format error, or Z_MEM_ERROR if it could not allocate memory for the state. inflateBack() can also return Z_STREAM_ERROR if the input parameters are not correct, i.e. strm is Z_NULL or the state was not initialized. */ int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc) { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ unsigned bits; /* bits in bit buffer */ unsigned copy; /* number of stored or match bytes to copy */ unsigned char FAR *from; /* where to copy match bytes from */ code here; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* Check that the strm exists and that the state was initialized */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* Reset the state */ strm->msg = Z_NULL; state->mode = TYPE; state->last = 0; state->whave = 0; next = strm->next_in; have = next != Z_NULL ? strm->avail_in : 0; hold = 0; bits = 0; put = state->window; left = state->wsize; /* Inflate until end of block marked as last */ for (;;) switch (state->mode) { case TYPE: /* determine and dispatch block type */ if (state->last) { BYTEBITS(); state->mode = DONE; break; } NEEDBITS(3); state->last = BITS(1); DROPBITS(1); switch (BITS(2)) { case 0: /* stored block */ Tracev((stderr, "inflate: stored block%s\n", state->last ? " (last)" : "")); state->mode = STORED; break; case 1: /* fixed block */ fixedtables(state); Tracev((stderr, "inflate: fixed codes block%s\n", state->last ? " (last)" : "")); state->mode = LEN; /* decode codes */ break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", state->last ? " (last)" : "")); state->mode = TABLE; break; case 3: strm->msg = (char *)"invalid block type"; state->mode = BAD; } DROPBITS(2); break; case STORED: /* get and verify stored block length */ BYTEBITS(); /* go to byte boundary */ NEEDBITS(32); if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { strm->msg = (char *)"invalid stored block lengths"; state->mode = BAD; break; } state->length = (unsigned)hold & 0xffff; Tracev((stderr, "inflate: stored length %u\n", state->length)); INITBITS(); /* copy stored block from input to output */ while (state->length != 0) { copy = state->length; PULL(); ROOM(); if (copy > have) copy = have; if (copy > left) copy = left; zmemcpy(put, next, copy); have -= copy; next += copy; left -= copy; put += copy; state->length -= copy; } Tracev((stderr, "inflate: stored end\n")); state->mode = TYPE; break; case TABLE: /* get dynamic table entries descriptor */ NEEDBITS(14); state->nlen = BITS(5) + 257; DROPBITS(5); state->ndist = BITS(5) + 1; DROPBITS(5); state->ncode = BITS(4) + 4; DROPBITS(4); #ifndef PKZIP_BUG_WORKAROUND if (state->nlen > 286 || state->ndist > 30) { strm->msg = (char *)"too many length or distance symbols"; state->mode = BAD; break; } #endif Tracev((stderr, "inflate: table sizes ok\n")); /* get code length code lengths (not a typo) */ state->have = 0; while (state->have < state->ncode) { NEEDBITS(3); state->lens[order[state->have++]] = (unsigned short)BITS(3); DROPBITS(3); } while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; state->lencode = (code const FAR *)(state->next); state->lenbits = 7; ret = inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid code lengths set"; state->mode = BAD; break; } Tracev((stderr, "inflate: code lengths ok\n")); /* get length and distance code code lengths */ state->have = 0; while (state->have < state->nlen + state->ndist) { for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.val < 16) { DROPBITS(here.bits); state->lens[state->have++] = here.val; } else { if (here.val == 16) { NEEDBITS(here.bits + 2); DROPBITS(here.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } len = (unsigned)(state->lens[state->have - 1]); copy = 3 + BITS(2); DROPBITS(2); } else if (here.val == 17) { NEEDBITS(here.bits + 3); DROPBITS(here.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { NEEDBITS(here.bits + 7); DROPBITS(here.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); } if (state->have + copy > state->nlen + state->ndist) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } while (copy--) state->lens[state->have++] = (unsigned short)len; } } /* handle error breaks in while */ if (state->mode == BAD) break; /* check for end-of-block code (better have one) */ if (state->lens[256] == 0) { strm->msg = (char *)"invalid code -- missing end-of-block"; state->mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; state->lencode = (code const FAR *)(state->next); state->lenbits = 9; ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid literal/lengths set"; state->mode = BAD; break; } state->distcode = (code const FAR *)(state->next); state->distbits = 6; ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); if (ret) { strm->msg = (char *)"invalid distances set"; state->mode = BAD; break; } Tracev((stderr, "inflate: codes ok\n")); state->mode = LEN; /* fallthrough */ case LEN: /* use inflate_fast() if we have enough input and output */ if (have >= 6 && left >= 258) { RESTORE(); if (state->whave < state->wsize) state->whave = state->wsize - left; inflate_fast(strm, state->wsize); LOAD(); break; } /* get a literal, length, or end-of-block code */ for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.op && (here.op & 0xf0) == 0) { last = here; for (;;) { here = state->lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); } DROPBITS(here.bits); state->length = (unsigned)here.val; /* process literal */ if (here.op == 0) { Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); ROOM(); *put++ = (unsigned char)(state->length); left--; state->mode = LEN; break; } /* process end of block */ if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); state->mode = TYPE; break; } /* invalid code */ if (here.op & 64) { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } /* length code -- get extra bits, if any */ state->extra = (unsigned)(here.op) & 15; if (state->extra != 0) { NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); } Tracevv((stderr, "inflate: length %u\n", state->length)); /* get distance code */ for (;;) { here = state->distcode[BITS(state->distbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if ((here.op & 0xf0) == 0) { last = here; for (;;) { here = state->distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); } DROPBITS(here.bits); if (here.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } state->offset = (unsigned)here.val; /* get distance extra bits, if any */ state->extra = (unsigned)(here.op) & 15; if (state->extra != 0) { NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); } if (state->offset > state->wsize - (state->whave < state->wsize ? left : 0)) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } Tracevv((stderr, "inflate: distance %u\n", state->offset)); /* copy match from window to output */ do { ROOM(); copy = state->wsize - state->offset; if (copy < left) { from = put + copy; copy = left - copy; } else { from = put - state->offset; copy = left; } if (copy > state->length) copy = state->length; state->length -= copy; left -= copy; do { *put++ = *from++; } while (--copy); } while (state->length != 0); break; case DONE: /* inflate stream terminated properly */ ret = Z_STREAM_END; goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; default: /* can't happen, but makes compilers happy */ ret = Z_STREAM_ERROR; goto inf_leave; } /* Write leftover output and return unused input */ inf_leave: if (left < state->wsize) { if (out(out_desc, state->window, state->wsize - left) && ret == Z_STREAM_END) ret = Z_BUF_ERROR; } strm->next_in = next; strm->avail_in = have; return ret; } int ZEXPORT inflateBackEnd(z_streamp strm) { if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) return Z_STREAM_ERROR; ZFREE(strm, strm->state); strm->state = Z_NULL; Tracev((stderr, "inflate: end\n")); return Z_OK; } |
Added compat/zlib/inffast.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 | /* inffast.c -- fast decoding * Copyright (C) 1995-2017 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" #ifdef ASMINF # pragma message("Assembler code may have bugs -- use at your own risk") #else /* Decode literal, length, and distance codes and write out the resulting literal and match bytes until either not enough input or output is available, an end-of-block is encountered, or a data error is encountered. When large enough input and output buffers are supplied to inflate(), for example, a 16K input buffer and a 64K output buffer, more than 95% of the inflate execution time is spent in this routine. Entry assumptions: state->mode == LEN strm->avail_in >= 6 strm->avail_out >= 258 start >= strm->avail_out state->bits < 8 On return, state->mode is one of: LEN -- ran out of enough output space or enough available input TYPE -- reached end of block code, inflate() to interpret next block BAD -- error in block data Notes: - The maximum input bits used by a length/distance pair is 15 bits for the length code, 5 bits for the length extra, 15 bits for the distance code, and 13 bits for the distance extra. This totals 48 bits, or six bytes. Therefore if strm->avail_in >= 6, then there is enough input to avoid checking for available input while decoding. - The maximum bytes that a single length/distance pair can output is 258 bytes, which is the maximum length that can be coded. inflate_fast() requires strm->avail_out >= 258 for each loop to avoid checking for output space. */ void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) { struct inflate_state FAR *state; z_const unsigned char FAR *in; /* local strm->next_in */ z_const unsigned char FAR *last; /* have enough input while in < last */ unsigned char FAR *out; /* local strm->next_out */ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ unsigned char FAR *end; /* while out < end, enough space available */ #ifdef INFLATE_STRICT unsigned dmax; /* maximum distance from zlib header */ #endif unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ unsigned long hold; /* local strm->hold */ unsigned bits; /* local strm->bits */ code const FAR *lcode; /* local strm->lencode */ code const FAR *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ code const *here; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ unsigned dist; /* match distance */ unsigned char FAR *from; /* where to copy match from */ /* copy state to local variables */ state = (struct inflate_state FAR *)strm->state; in = strm->next_in; last = in + (strm->avail_in - 5); out = strm->next_out; beg = out - (start - strm->avail_out); end = out + (strm->avail_out - 257); #ifdef INFLATE_STRICT dmax = state->dmax; #endif wsize = state->wsize; whave = state->whave; wnext = state->wnext; window = state->window; hold = state->hold; bits = state->bits; lcode = state->lencode; dcode = state->distcode; lmask = (1U << state->lenbits) - 1; dmask = (1U << state->distbits) - 1; /* decode literals and length/distances until end-of-block or not enough input data or output space */ do { if (bits < 15) { hold += (unsigned long)(*in++) << bits; bits += 8; hold += (unsigned long)(*in++) << bits; bits += 8; } here = lcode + (hold & lmask); dolen: op = (unsigned)(here->bits); hold >>= op; bits -= op; op = (unsigned)(here->op); if (op == 0) { /* literal */ Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here->val)); *out++ = (unsigned char)(here->val); } else if (op & 16) { /* length base */ len = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { hold += (unsigned long)(*in++) << bits; bits += 8; } len += (unsigned)hold & ((1U << op) - 1); hold >>= op; bits -= op; } Tracevv((stderr, "inflate: length %u\n", len)); if (bits < 15) { hold += (unsigned long)(*in++) << bits; bits += 8; hold += (unsigned long)(*in++) << bits; bits += 8; } here = dcode + (hold & dmask); dodist: op = (unsigned)(here->bits); hold >>= op; bits -= op; op = (unsigned)(here->op); if (op & 16) { /* distance base */ dist = (unsigned)(here->val); op &= 15; /* number of extra bits */ if (bits < op) { hold += (unsigned long)(*in++) << bits; bits += 8; if (bits < op) { hold += (unsigned long)(*in++) << bits; bits += 8; } } dist += (unsigned)hold & ((1U << op) - 1); #ifdef INFLATE_STRICT if (dist > dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif hold >>= op; bits -= op; Tracevv((stderr, "inflate: distance %u\n", dist)); op = (unsigned)(out - beg); /* max distance in output */ if (dist > op) { /* see if copy from window */ op = dist - op; /* distance back in window */ if (op > whave) { if (state->sane) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR if (len <= op - whave) { do { *out++ = 0; } while (--len); continue; } len -= op - whave; do { *out++ = 0; } while (--op > whave); if (op == 0) { from = out - dist; do { *out++ = *from++; } while (--len); continue; } #endif } from = window; if (wnext == 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } else if (wnext < op) { /* wrap around window */ from += wsize + wnext - op; op -= wnext; if (op < len) { /* some from end of window */ len -= op; do { *out++ = *from++; } while (--op); from = window; if (wnext < len) { /* some from start of window */ op = wnext; len -= op; do { *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } } else { /* contiguous in window */ from += wnext - op; if (op < len) { /* some from window */ len -= op; do { *out++ = *from++; } while (--op); from = out - dist; /* rest from output */ } } while (len > 2) { *out++ = *from++; *out++ = *from++; *out++ = *from++; len -= 3; } if (len) { *out++ = *from++; if (len > 1) *out++ = *from++; } } else { from = out - dist; /* copy direct from output */ do { /* minimum length is three */ *out++ = *from++; *out++ = *from++; *out++ = *from++; len -= 3; } while (len > 2); if (len) { *out++ = *from++; if (len > 1) *out++ = *from++; } } } else if ((op & 64) == 0) { /* 2nd level distance code */ here = dcode + here->val + (hold & ((1U << op) - 1)); goto dodist; } else { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } } else if ((op & 64) == 0) { /* 2nd level length code */ here = lcode + here->val + (hold & ((1U << op) - 1)); goto dolen; } else if (op & 32) { /* end-of-block */ Tracevv((stderr, "inflate: end of block\n")); state->mode = TYPE; break; } else { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } } while (in < last && out < end); /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ len = bits >> 3; in -= len; bits -= len << 3; hold &= (1U << bits) - 1; /* update state and return */ strm->next_in = in; strm->next_out = out; strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); strm->avail_out = (unsigned)(out < end ? 257 + (end - out) : 257 - (out - end)); state->hold = hold; state->bits = bits; return; } /* inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): - Using bit fields for code structure - Different op definition to avoid & for extra bits (do & for table bits) - Three separate decoding do-loops for direct, window, and wnext == 0 - Special case for distance > 1 copies to do overlapped load and store copy - Explicit branch predictions (based on measured branch probabilities) - Deferring match copy and interspersed it with decoding subsequent codes - Swapping literal/length else - Swapping window/direct else - Larger unrolled copy loops (three is about right) - Moving len -= 3 statement into middle of loop */ #endif /* !ASMINF */ |
Added compat/zlib/inffast.h.
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | /* inffast.h -- header to use inffast.c * Copyright (C) 1995-2003, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start); |
Added compat/zlib/inffixed.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* inffixed.h -- table for decoding fixed codes * Generated automatically by makefixed(). */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of this library and is subject to change. Applications should only use zlib.h. */ static const code lenfix[512] = { {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, {0,9,255} }; static const code distfix[32] = { {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, {22,5,193},{64,5,0} }; |
Added compat/zlib/inflate.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 | /* inflate.c -- zlib decompression * Copyright (C) 1995-2022 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * Change history: * * 1.2.beta0 24 Nov 2002 * - First version -- complete rewrite of inflate to simplify code, avoid * creation of window when not needed, minimize use of window when it is * needed, make inffast.c even faster, implement gzip decoding, and to * improve code readability and style over the previous zlib inflate code * * 1.2.beta1 25 Nov 2002 * - Use pointers for available input and output checking in inffast.c * - Remove input and output counters in inffast.c * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 * - Remove unnecessary second byte pull from length extra in inffast.c * - Unroll direct copy to three copies per loop in inffast.c * * 1.2.beta2 4 Dec 2002 * - Change external routine names to reduce potential conflicts * - Correct filename to inffixed.h for fixed tables in inflate.c * - Make hbuf[] unsigned char to match parameter type in inflate.c * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) * to avoid negation problem on Alphas (64 bit) in inflate.c * * 1.2.beta3 22 Dec 2002 * - Add comments on state->bits assertion in inffast.c * - Add comments on op field in inftrees.h * - Fix bug in reuse of allocated window after inflateReset() * - Remove bit fields--back to byte structure for speed * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths * - Change post-increments to pre-increments in inflate_fast(), PPC biased? * - Add compile time option, POSTINC, to use post-increments instead (Intel?) * - Make MATCH copy in inflate() much faster for when inflate_fast() not used * - Use local copies of stream next and avail values, as well as local bit * buffer and bit count in inflate()--for speed when inflate_fast() not used * * 1.2.beta4 1 Jan 2003 * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings * - Move a comment on output buffer sizes from inffast.c to inflate.c * - Add comments in inffast.c to introduce the inflate_fast() routine * - Rearrange window copies in inflate_fast() for speed and simplification * - Unroll last copy for window match in inflate_fast() * - Use local copies of window variables in inflate_fast() for speed * - Pull out common wnext == 0 case for speed in inflate_fast() * - Make op and len in inflate_fast() unsigned for consistency * - Add FAR to lcode and dcode declarations in inflate_fast() * - Simplified bad distance check in inflate_fast() * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new * source file infback.c to provide a call-back interface to inflate for * programs like gzip and unzip -- uses window as output buffer to avoid * window copying * * 1.2.beta5 1 Jan 2003 * - Improved inflateBack() interface to allow the caller to provide initial * input in strm. * - Fixed stored blocks bug in inflateBack() * * 1.2.beta6 4 Jan 2003 * - Added comments in inffast.c on effectiveness of POSTINC * - Typecasting all around to reduce compiler warnings * - Changed loops from while (1) or do {} while (1) to for (;;), again to * make compilers happy * - Changed type of window in inflateBackInit() to unsigned char * * * 1.2.beta7 27 Jan 2003 * - Changed many types to unsigned or unsigned short to avoid warnings * - Added inflateCopy() function * * 1.2.0 9 Mar 2003 * - Changed inflateBack() interface to provide separate opaque descriptors * for the in() and out() functions * - Changed inflateBack() argument and in_func typedef to swap the length * and buffer address return values for the input function * - Check next_in and next_out for Z_NULL on entry to inflate() * * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. */ #include "zutil.h" #include "inftrees.h" #include "inflate.h" #include "inffast.h" #ifdef MAKEFIXED # ifndef BUILDFIXED # define BUILDFIXED # endif #endif local int inflateStateCheck(z_streamp strm) { struct inflate_state FAR *state; if (strm == Z_NULL || strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) return 1; state = (struct inflate_state FAR *)strm->state; if (state == Z_NULL || state->strm != strm || state->mode < HEAD || state->mode > SYNC) return 1; return 0; } int ZEXPORT inflateResetKeep(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; strm->total_in = strm->total_out = state->total = 0; strm->msg = Z_NULL; if (state->wrap) /* to support ill-conceived Java test suite */ strm->adler = state->wrap & 1; state->mode = HEAD; state->last = 0; state->havedict = 0; state->flags = -1; state->dmax = 32768U; state->head = Z_NULL; state->hold = 0; state->bits = 0; state->lencode = state->distcode = state->next = state->codes; state->sane = 1; state->back = -1; Tracev((stderr, "inflate: reset\n")); return Z_OK; } int ZEXPORT inflateReset(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; state->wsize = 0; state->whave = 0; state->wnext = 0; return inflateResetKeep(strm); } int ZEXPORT inflateReset2(z_streamp strm, int windowBits) { int wrap; struct inflate_state FAR *state; /* get the state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* extract wrap request from windowBits parameter */ if (windowBits < 0) { if (windowBits < -15) return Z_STREAM_ERROR; wrap = 0; windowBits = -windowBits; } else { wrap = (windowBits >> 4) + 5; #ifdef GUNZIP if (windowBits < 48) windowBits &= 15; #endif } /* set number of window bits, free window if different */ if (windowBits && (windowBits < 8 || windowBits > 15)) return Z_STREAM_ERROR; if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { ZFREE(strm, state->window); state->window = Z_NULL; } /* update state and reset the rest of it */ state->wrap = wrap; state->wbits = (unsigned)windowBits; return inflateReset(strm); } int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, const char *version, int stream_size) { int ret; struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != (int)(sizeof(z_stream))) return Z_VERSION_ERROR; if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; #endif } if (strm->zfree == (free_func)0) #ifdef Z_SOLO return Z_STREAM_ERROR; #else strm->zfree = zcfree; #endif state = (struct inflate_state FAR *) ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; state->strm = strm; state->window = Z_NULL; state->mode = HEAD; /* to pass state test in inflateReset2() */ ret = inflateReset2(strm, windowBits); if (ret != Z_OK) { ZFREE(strm, state); strm->state = Z_NULL; } return ret; } int ZEXPORT inflateInit_(z_streamp strm, const char *version, int stream_size) { return inflateInit2_(strm, DEF_WBITS, version, stream_size); } int ZEXPORT inflatePrime(z_streamp strm, int bits, int value) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; if (bits == 0) return Z_OK; state = (struct inflate_state FAR *)strm->state; if (bits < 0) { state->hold = 0; state->bits = 0; return Z_OK; } if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR; value &= (1L << bits) - 1; state->hold += (unsigned)value << state->bits; state->bits += (uInt)bits; return Z_OK; } /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. If BUILDFIXED is defined, then instead this routine builds the tables the first time it's called, and returns those tables the first time and thereafter. This reduces the size of the code by about 2K bytes, in exchange for a little execution time. However, BUILDFIXED should not be used for threaded applications, since the rewriting of the tables and virgin may not be thread-safe. */ local void fixedtables(struct inflate_state FAR *state) { #ifdef BUILDFIXED static int virgin = 1; static code *lenfix, *distfix; static code fixed[544]; /* build fixed huffman tables if first call (may not be thread safe) */ if (virgin) { unsigned sym, bits; static code *next; /* literal/length table */ sym = 0; while (sym < 144) state->lens[sym++] = 8; while (sym < 256) state->lens[sym++] = 9; while (sym < 280) state->lens[sym++] = 7; while (sym < 288) state->lens[sym++] = 8; next = fixed; lenfix = next; bits = 9; inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); /* distance table */ sym = 0; while (sym < 32) state->lens[sym++] = 5; distfix = next; bits = 5; inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); /* do this just once */ virgin = 0; } #else /* !BUILDFIXED */ # include "inffixed.h" #endif /* BUILDFIXED */ state->lencode = lenfix; state->lenbits = 9; state->distcode = distfix; state->distbits = 5; } #ifdef MAKEFIXED #include <stdio.h> /* Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also defines BUILDFIXED, so the tables are built on the fly. makefixed() writes those tables to stdout, which would be piped to inffixed.h. A small program can simply call makefixed to do this: void makefixed(void); int main(void) { makefixed(); return 0; } Then that can be linked with zlib built with MAKEFIXED defined and run: a.out > inffixed.h */ void makefixed(void) { unsigned low, size; struct inflate_state state; fixedtables(&state); puts(" /* inffixed.h -- table for decoding fixed codes"); puts(" * Generated automatically by makefixed()."); puts(" */"); puts(""); puts(" /* WARNING: this file should *not* be used by applications."); puts(" It is part of the implementation of this library and is"); puts(" subject to change. Applications should only use zlib.h."); puts(" */"); puts(""); size = 1U << 9; printf(" static const code lenfix[%u] = {", size); low = 0; for (;;) { if ((low % 7) == 0) printf("\n "); printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, state.lencode[low].bits, state.lencode[low].val); if (++low == size) break; putchar(','); } puts("\n };"); size = 1U << 5; printf("\n static const code distfix[%u] = {", size); low = 0; for (;;) { if ((low % 6) == 0) printf("\n "); printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, state.distcode[low].val); if (++low == size) break; putchar(','); } puts("\n };"); } #endif /* MAKEFIXED */ /* Update the window with the last wsize (normally 32K) bytes written before returning. If window does not exist yet, create it. This is only called when a window is already in use, or when output has been written during this inflate call, but the end of the deflate stream has not been reached yet. It is also called to create a window for dictionary data when a dictionary is loaded. Providing output buffers larger than 32K to inflate() should provide a speed advantage, since only the last 32K of output is copied to the sliding window upon return from inflate(), and since all distances after the first 32K of output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy) { struct inflate_state FAR *state; unsigned dist; state = (struct inflate_state FAR *)strm->state; /* if it hasn't been done already, allocate space for the window */ if (state->window == Z_NULL) { state->window = (unsigned char FAR *) ZALLOC(strm, 1U << state->wbits, sizeof(unsigned char)); if (state->window == Z_NULL) return 1; } /* if window not in use yet, initialize */ if (state->wsize == 0) { state->wsize = 1U << state->wbits; state->wnext = 0; state->whave = 0; } /* copy state->wsize or less output bytes into the circular window */ if (copy >= state->wsize) { zmemcpy(state->window, end - state->wsize, state->wsize); state->wnext = 0; state->whave = state->wsize; } else { dist = state->wsize - state->wnext; if (dist > copy) dist = copy; zmemcpy(state->window + state->wnext, end - copy, dist); copy -= dist; if (copy) { zmemcpy(state->window, end - copy, copy); state->wnext = copy; state->whave = state->wsize; } else { state->wnext += dist; if (state->wnext == state->wsize) state->wnext = 0; if (state->whave < state->wsize) state->whave += dist; } } return 0; } /* Macros for inflate(): */ /* check function to use adler32() for zlib or crc32() for gzip */ #ifdef GUNZIP # define UPDATE_CHECK(check, buf, len) \ (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) #else # define UPDATE_CHECK(check, buf, len) adler32(check, buf, len) #endif /* check macros for header crc */ #ifdef GUNZIP # define CRC2(check, word) \ do { \ hbuf[0] = (unsigned char)(word); \ hbuf[1] = (unsigned char)((word) >> 8); \ check = crc32(check, hbuf, 2); \ } while (0) # define CRC4(check, word) \ do { \ hbuf[0] = (unsigned char)(word); \ hbuf[1] = (unsigned char)((word) >> 8); \ hbuf[2] = (unsigned char)((word) >> 16); \ hbuf[3] = (unsigned char)((word) >> 24); \ check = crc32(check, hbuf, 4); \ } while (0) #endif /* Load registers with state in inflate() for speed */ #define LOAD() \ do { \ put = strm->next_out; \ left = strm->avail_out; \ next = strm->next_in; \ have = strm->avail_in; \ hold = state->hold; \ bits = state->bits; \ } while (0) /* Restore state from registers in inflate() */ #define RESTORE() \ do { \ strm->next_out = put; \ strm->avail_out = left; \ strm->next_in = next; \ strm->avail_in = have; \ state->hold = hold; \ state->bits = bits; \ } while (0) /* Clear the input bit accumulator */ #define INITBITS() \ do { \ hold = 0; \ bits = 0; \ } while (0) /* Get a byte of input into the bit accumulator, or return from inflate() if there is no input available. */ #define PULLBYTE() \ do { \ if (have == 0) goto inf_leave; \ have--; \ hold += (unsigned long)(*next++) << bits; \ bits += 8; \ } while (0) /* Assure that there are at least n bits in the bit accumulator. If there is not enough available input to do that, then return from inflate(). */ #define NEEDBITS(n) \ do { \ while (bits < (unsigned)(n)) \ PULLBYTE(); \ } while (0) /* Return the low n bits of the bit accumulator (n < 16) */ #define BITS(n) \ ((unsigned)hold & ((1U << (n)) - 1)) /* Remove n bits from the bit accumulator */ #define DROPBITS(n) \ do { \ hold >>= (n); \ bits -= (unsigned)(n); \ } while (0) /* Remove zero to seven bits as needed to go to a byte boundary */ #define BYTEBITS() \ do { \ hold >>= bits & 7; \ bits -= bits & 7; \ } while (0) /* inflate() uses a state machine to process as much input data and generate as much output data as possible before returning. The state machine is structured roughly as follows: for (;;) switch (state) { ... case STATEn: if (not enough input data or output space to make progress) return; ... make progress ... state = STATEm; break; ... } so when inflate() is called again, the same case is attempted again, and if the appropriate resources are provided, the machine proceeds to the next state. The NEEDBITS() macro is usually the way the state evaluates whether it can proceed or should return. NEEDBITS() does the return if the requested bits are not available. The typical use of the BITS macros is: NEEDBITS(n); ... do something with BITS(n) ... DROPBITS(n); where NEEDBITS(n) either returns from inflate() if there isn't enough input left to load n bits into the accumulator, or it continues. BITS(n) gives the low n bits in the accumulator. When done, DROPBITS(n) drops the low n bits off the accumulator. INITBITS() clears the accumulator and sets the number of available bits to zero. BYTEBITS() discards just enough bits to put the accumulator on a byte boundary. After BYTEBITS() and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return if there is no input available. The decoding of variable length codes uses PULLBYTE() directly in order to pull just enough bytes to decode the next code, and no more. Some states loop until they get enough input, making sure that enough state information is maintained to continue the loop where it left off if NEEDBITS() returns in the loop. For example, want, need, and keep would all have to actually be part of the saved state in case NEEDBITS() returns: case STATEw: while (want < need) { NEEDBITS(n); keep[want++] = BITS(n); DROPBITS(n); } state = STATEx; case STATEx: As shown above, if the next state is also the next case, then the break is omitted. A state may also return if there is not enough output space available to complete that state. Those states are copying stored data, writing a literal byte, and copying a matching string. When returning, a "goto inf_leave" is used to update the total counters, update the check value, and determine whether any progress has been made during that inflate() call in order to return the proper return code. Progress is defined as a change in either strm->avail_in or strm->avail_out. When there is a window, goto inf_leave will update the window with the last output written. If a goto inf_leave occurs in the middle of decompression and there is no window currently, goto inf_leave will create one and copy output to the window for the next call of inflate(). In this implementation, the flush parameter of inflate() only affects the return code (per zlib.h). inflate() always writes as much as possible to strm->next_out, given the space available and the provided input--the effect documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers the allocation of and copying into a sliding window until necessary, which provides the effect documented in zlib.h for Z_FINISH when the entire input stream available. So the only thing the flush parameter actually does is: when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it will return Z_BUF_ERROR if it has not reached the end of the stream. */ int ZEXPORT inflate(z_streamp strm, int flush) { struct inflate_state FAR *state; z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ unsigned bits; /* bits in bit buffer */ unsigned in, out; /* save starting available input and output */ unsigned copy; /* number of stored or match bytes to copy */ unsigned char FAR *from; /* where to copy match bytes from */ code here; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ #ifdef GUNZIP unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ #endif static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; if (inflateStateCheck(strm) || strm->next_out == Z_NULL || (strm->next_in == Z_NULL && strm->avail_in != 0)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ LOAD(); in = have; out = left; ret = Z_OK; for (;;) switch (state->mode) { case HEAD: if (state->wrap == 0) { state->mode = TYPEDO; break; } NEEDBITS(16); #ifdef GUNZIP if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ if (state->wbits == 0) state->wbits = 15; state->check = crc32(0L, Z_NULL, 0); CRC2(state->check, hold); INITBITS(); state->mode = FLAGS; break; } if (state->head != Z_NULL) state->head->done = -1; if (!(state->wrap & 1) || /* check if zlib header allowed */ #else if ( #endif ((BITS(8) << 8) + (hold >> 8)) % 31) { strm->msg = (char *)"incorrect header check"; state->mode = BAD; break; } if (BITS(4) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } DROPBITS(4); len = BITS(4) + 8; if (state->wbits == 0) state->wbits = len; if (len > 15 || len > state->wbits) { strm->msg = (char *)"invalid window size"; state->mode = BAD; break; } state->dmax = 1U << len; state->flags = 0; /* indicate zlib header */ Tracev((stderr, "inflate: zlib header ok\n")); strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = hold & 0x200 ? DICTID : TYPE; INITBITS(); break; #ifdef GUNZIP case FLAGS: NEEDBITS(16); state->flags = (int)(hold); if ((state->flags & 0xff) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } if (state->flags & 0xe000) { strm->msg = (char *)"unknown header flags set"; state->mode = BAD; break; } if (state->head != Z_NULL) state->head->text = (int)((hold >> 8) & 1); if ((state->flags & 0x0200) && (state->wrap & 4)) CRC2(state->check, hold); INITBITS(); state->mode = TIME; /* fallthrough */ case TIME: NEEDBITS(32); if (state->head != Z_NULL) state->head->time = hold; if ((state->flags & 0x0200) && (state->wrap & 4)) CRC4(state->check, hold); INITBITS(); state->mode = OS; /* fallthrough */ case OS: NEEDBITS(16); if (state->head != Z_NULL) { state->head->xflags = (int)(hold & 0xff); state->head->os = (int)(hold >> 8); } if ((state->flags & 0x0200) && (state->wrap & 4)) CRC2(state->check, hold); INITBITS(); state->mode = EXLEN; /* fallthrough */ case EXLEN: if (state->flags & 0x0400) { NEEDBITS(16); state->length = (unsigned)(hold); if (state->head != Z_NULL) state->head->extra_len = (unsigned)hold; if ((state->flags & 0x0200) && (state->wrap & 4)) CRC2(state->check, hold); INITBITS(); } else if (state->head != Z_NULL) state->head->extra = Z_NULL; state->mode = EXTRA; /* fallthrough */ case EXTRA: if (state->flags & 0x0400) { copy = state->length; if (copy > have) copy = have; if (copy) { if (state->head != Z_NULL && state->head->extra != Z_NULL && (len = state->head->extra_len - state->length) < state->head->extra_max) { zmemcpy(state->head->extra + len, next, len + copy > state->head->extra_max ? state->head->extra_max - len : copy); } if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; state->length -= copy; } if (state->length) goto inf_leave; } state->length = 0; state->mode = NAME; /* fallthrough */ case NAME: if (state->flags & 0x0800) { if (have == 0) goto inf_leave; copy = 0; do { len = (unsigned)(next[copy++]); if (state->head != Z_NULL && state->head->name != Z_NULL && state->length < state->head->name_max) state->head->name[state->length++] = (Bytef)len; } while (len && copy < have); if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; if (len) goto inf_leave; } else if (state->head != Z_NULL) state->head->name = Z_NULL; state->length = 0; state->mode = COMMENT; /* fallthrough */ case COMMENT: if (state->flags & 0x1000) { if (have == 0) goto inf_leave; copy = 0; do { len = (unsigned)(next[copy++]); if (state->head != Z_NULL && state->head->comment != Z_NULL && state->length < state->head->comm_max) state->head->comment[state->length++] = (Bytef)len; } while (len && copy < have); if ((state->flags & 0x0200) && (state->wrap & 4)) state->check = crc32(state->check, next, copy); have -= copy; next += copy; if (len) goto inf_leave; } else if (state->head != Z_NULL) state->head->comment = Z_NULL; state->mode = HCRC; /* fallthrough */ case HCRC: if (state->flags & 0x0200) { NEEDBITS(16); if ((state->wrap & 4) && hold != (state->check & 0xffff)) { strm->msg = (char *)"header crc mismatch"; state->mode = BAD; break; } INITBITS(); } if (state->head != Z_NULL) { state->head->hcrc = (int)((state->flags >> 9) & 1); state->head->done = 1; } strm->adler = state->check = crc32(0L, Z_NULL, 0); state->mode = TYPE; break; #endif case DICTID: NEEDBITS(32); strm->adler = state->check = ZSWAP32(hold); INITBITS(); state->mode = DICT; /* fallthrough */ case DICT: if (state->havedict == 0) { RESTORE(); return Z_NEED_DICT; } strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = TYPE; /* fallthrough */ case TYPE: if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; /* fallthrough */ case TYPEDO: if (state->last) { BYTEBITS(); state->mode = CHECK; break; } NEEDBITS(3); state->last = BITS(1); DROPBITS(1); switch (BITS(2)) { case 0: /* stored block */ Tracev((stderr, "inflate: stored block%s\n", state->last ? " (last)" : "")); state->mode = STORED; break; case 1: /* fixed block */ fixedtables(state); Tracev((stderr, "inflate: fixed codes block%s\n", state->last ? " (last)" : "")); state->mode = LEN_; /* decode codes */ if (flush == Z_TREES) { DROPBITS(2); goto inf_leave; } break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", state->last ? " (last)" : "")); state->mode = TABLE; break; case 3: strm->msg = (char *)"invalid block type"; state->mode = BAD; } DROPBITS(2); break; case STORED: BYTEBITS(); /* go to byte boundary */ NEEDBITS(32); if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { strm->msg = (char *)"invalid stored block lengths"; state->mode = BAD; break; } state->length = (unsigned)hold & 0xffff; Tracev((stderr, "inflate: stored length %u\n", state->length)); INITBITS(); state->mode = COPY_; if (flush == Z_TREES) goto inf_leave; /* fallthrough */ case COPY_: state->mode = COPY; /* fallthrough */ case COPY: copy = state->length; if (copy) { if (copy > have) copy = have; if (copy > left) copy = left; if (copy == 0) goto inf_leave; zmemcpy(put, next, copy); have -= copy; next += copy; left -= copy; put += copy; state->length -= copy; break; } Tracev((stderr, "inflate: stored end\n")); state->mode = TYPE; break; case TABLE: NEEDBITS(14); state->nlen = BITS(5) + 257; DROPBITS(5); state->ndist = BITS(5) + 1; DROPBITS(5); state->ncode = BITS(4) + 4; DROPBITS(4); #ifndef PKZIP_BUG_WORKAROUND if (state->nlen > 286 || state->ndist > 30) { strm->msg = (char *)"too many length or distance symbols"; state->mode = BAD; break; } #endif Tracev((stderr, "inflate: table sizes ok\n")); state->have = 0; state->mode = LENLENS; /* fallthrough */ case LENLENS: while (state->have < state->ncode) { NEEDBITS(3); state->lens[order[state->have++]] = (unsigned short)BITS(3); DROPBITS(3); } while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; state->lencode = (const code FAR *)(state->next); state->lenbits = 7; ret = inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid code lengths set"; state->mode = BAD; break; } Tracev((stderr, "inflate: code lengths ok\n")); state->have = 0; state->mode = CODELENS; /* fallthrough */ case CODELENS: while (state->have < state->nlen + state->ndist) { for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.val < 16) { DROPBITS(here.bits); state->lens[state->have++] = here.val; } else { if (here.val == 16) { NEEDBITS(here.bits + 2); DROPBITS(here.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } len = state->lens[state->have - 1]; copy = 3 + BITS(2); DROPBITS(2); } else if (here.val == 17) { NEEDBITS(here.bits + 3); DROPBITS(here.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { NEEDBITS(here.bits + 7); DROPBITS(here.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); } if (state->have + copy > state->nlen + state->ndist) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } while (copy--) state->lens[state->have++] = (unsigned short)len; } } /* handle error breaks in while */ if (state->mode == BAD) break; /* check for end-of-block code (better have one) */ if (state->lens[256] == 0) { strm->msg = (char *)"invalid code -- missing end-of-block"; state->mode = BAD; break; } /* build code tables -- note: do not change the lenbits or distbits values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; state->lencode = (const code FAR *)(state->next); state->lenbits = 9; ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid literal/lengths set"; state->mode = BAD; break; } state->distcode = (const code FAR *)(state->next); state->distbits = 6; ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); if (ret) { strm->msg = (char *)"invalid distances set"; state->mode = BAD; break; } Tracev((stderr, "inflate: codes ok\n")); state->mode = LEN_; if (flush == Z_TREES) goto inf_leave; /* fallthrough */ case LEN_: state->mode = LEN; /* fallthrough */ case LEN: if (have >= 6 && left >= 258) { RESTORE(); inflate_fast(strm, out); LOAD(); if (state->mode == TYPE) state->back = -1; break; } state->back = 0; for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if (here.op && (here.op & 0xf0) == 0) { last = here; for (;;) { here = state->lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); state->back += last.bits; } DROPBITS(here.bits); state->back += here.bits; state->length = (unsigned)here.val; if ((int)(here.op) == 0) { Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? "inflate: literal '%c'\n" : "inflate: literal 0x%02x\n", here.val)); state->mode = LIT; break; } if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); state->back = -1; state->mode = TYPE; break; } if (here.op & 64) { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } state->extra = (unsigned)(here.op) & 15; state->mode = LENEXT; /* fallthrough */ case LENEXT: if (state->extra) { NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); state->back += state->extra; } Tracevv((stderr, "inflate: length %u\n", state->length)); state->was = state->length; state->mode = DIST; /* fallthrough */ case DIST: for (;;) { here = state->distcode[BITS(state->distbits)]; if ((unsigned)(here.bits) <= bits) break; PULLBYTE(); } if ((here.op & 0xf0) == 0) { last = here; for (;;) { here = state->distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + here.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); state->back += last.bits; } DROPBITS(here.bits); state->back += here.bits; if (here.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } state->offset = (unsigned)here.val; state->extra = (unsigned)(here.op) & 15; state->mode = DISTEXT; /* fallthrough */ case DISTEXT: if (state->extra) { NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); state->back += state->extra; } #ifdef INFLATE_STRICT if (state->offset > state->dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif Tracevv((stderr, "inflate: distance %u\n", state->offset)); state->mode = MATCH; /* fallthrough */ case MATCH: if (left == 0) goto inf_leave; copy = out - left; if (state->offset > copy) { /* copy from window */ copy = state->offset - copy; if (copy > state->whave) { if (state->sane) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR Trace((stderr, "inflate.c too far\n")); copy -= state->whave; if (copy > state->length) copy = state->length; if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = 0; } while (--copy); if (state->length == 0) state->mode = LEN; break; #endif } if (copy > state->wnext) { copy -= state->wnext; from = state->window + (state->wsize - copy); } else from = state->window + (state->wnext - copy); if (copy > state->length) copy = state->length; } else { /* copy from output */ from = put - state->offset; copy = state->length; } if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = *from++; } while (--copy); if (state->length == 0) state->mode = LEN; break; case LIT: if (left == 0) goto inf_leave; *put++ = (unsigned char)(state->length); left--; state->mode = LEN; break; case CHECK: if (state->wrap) { NEEDBITS(32); out -= left; strm->total_out += out; state->total += out; if ((state->wrap & 4) && out) strm->adler = state->check = UPDATE_CHECK(state->check, put - out, out); out = left; if ((state->wrap & 4) && ( #ifdef GUNZIP state->flags ? hold : #endif ZSWAP32(hold)) != state->check) { strm->msg = (char *)"incorrect data check"; state->mode = BAD; break; } INITBITS(); Tracev((stderr, "inflate: check matches trailer\n")); } #ifdef GUNZIP state->mode = LENGTH; /* fallthrough */ case LENGTH: if (state->wrap && state->flags) { NEEDBITS(32); if ((state->wrap & 4) && hold != (state->total & 0xffffffff)) { strm->msg = (char *)"incorrect length check"; state->mode = BAD; break; } INITBITS(); Tracev((stderr, "inflate: length matches trailer\n")); } #endif state->mode = DONE; /* fallthrough */ case DONE: ret = Z_STREAM_END; goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; case MEM: return Z_MEM_ERROR; case SYNC: /* fallthrough */ default: return Z_STREAM_ERROR; } /* Return from inflate(), updating the total counts and the check value. If there was no progress during the inflate() call, return a buffer error. Call updatewindow() to create and/or update the window state. Note: a memory error from inflate() is non-recoverable. */ inf_leave: RESTORE(); if (state->wsize || (out != strm->avail_out && state->mode < BAD && (state->mode < CHECK || flush != Z_FINISH))) if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { state->mode = MEM; return Z_MEM_ERROR; } in -= strm->avail_in; out -= strm->avail_out; strm->total_in += in; strm->total_out += out; state->total += out; if ((state->wrap & 4) && out) strm->adler = state->check = UPDATE_CHECK(state->check, strm->next_out - out, out); strm->data_type = (int)state->bits + (state->last ? 64 : 0) + (state->mode == TYPE ? 128 : 0) + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) ret = Z_BUF_ERROR; return ret; } int ZEXPORT inflateEnd(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->window != Z_NULL) ZFREE(strm, state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; Tracev((stderr, "inflate: end\n")); return Z_OK; } int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary, uInt *dictLength) { struct inflate_state FAR *state; /* check state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; /* copy dictionary */ if (state->whave && dictionary != Z_NULL) { zmemcpy(dictionary, state->window + state->wnext, state->whave - state->wnext); zmemcpy(dictionary + state->whave - state->wnext, state->window, state->wnext); } if (dictLength != Z_NULL) *dictLength = state->whave; return Z_OK; } int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary, uInt dictLength) { struct inflate_state FAR *state; unsigned long dictid; int ret; /* check state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (state->wrap != 0 && state->mode != DICT) return Z_STREAM_ERROR; /* check for correct dictionary identifier */ if (state->mode == DICT) { dictid = adler32(0L, Z_NULL, 0); dictid = adler32(dictid, dictionary, dictLength); if (dictid != state->check) return Z_DATA_ERROR; } /* copy dictionary to window using updatewindow(), which will amend the existing dictionary if appropriate */ ret = updatewindow(strm, dictionary + dictLength, dictLength); if (ret) { state->mode = MEM; return Z_MEM_ERROR; } state->havedict = 1; Tracev((stderr, "inflate: dictionary set\n")); return Z_OK; } int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head) { struct inflate_state FAR *state; /* check state */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; /* save header structure */ state->head = head; head->done = 0; return Z_OK; } /* Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found or when out of input. When called, *have is the number of pattern bytes found in order so far, in 0..3. On return *have is updated to the new state. If on return *have equals four, then the pattern was found and the return value is how many bytes were read including the last byte of the pattern. If *have is less than four, then the pattern has not been found yet and the return value is len. In the latter case, syncsearch() can be called again with more data and the *have state. *have is initialized to zero for the first call. */ local unsigned syncsearch(unsigned FAR *have, const unsigned char FAR *buf, unsigned len) { unsigned got; unsigned next; got = *have; next = 0; while (next < len && got < 4) { if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) got++; else if (buf[next]) got = 0; else got = 4 - got; next++; } *have = got; return next; } int ZEXPORT inflateSync(z_streamp strm) { unsigned len; /* number of bytes to look at or looked at */ int flags; /* temporary to save header status */ unsigned long in, out; /* temporary to save total_in and total_out */ unsigned char buf[4]; /* to restore bit buffer to byte string */ struct inflate_state FAR *state; /* check parameters */ if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; /* if first time, start search in bit buffer */ if (state->mode != SYNC) { state->mode = SYNC; state->hold >>= state->bits & 7; state->bits -= state->bits & 7; len = 0; while (state->bits >= 8) { buf[len++] = (unsigned char)(state->hold); state->hold >>= 8; state->bits -= 8; } state->have = 0; syncsearch(&(state->have), buf, len); } /* search available input */ len = syncsearch(&(state->have), strm->next_in, strm->avail_in); strm->avail_in -= len; strm->next_in += len; strm->total_in += len; /* return no joy or set up to restart inflate() on a new block */ if (state->have != 4) return Z_DATA_ERROR; if (state->flags == -1) state->wrap = 0; /* if no header yet, treat as raw */ else state->wrap &= ~4; /* no point in computing a check value now */ flags = state->flags; in = strm->total_in; out = strm->total_out; inflateReset(strm); strm->total_in = in; strm->total_out = out; state->flags = flags; state->mode = TYPE; return Z_OK; } /* Returns true if inflate is currently at the end of a block generated by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored block. When decompressing, PPP checks that at the end of input packet, inflate is waiting for these length bytes. */ int ZEXPORT inflateSyncPoint(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; return state->mode == STORED && state->bits == 0; } int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) { struct inflate_state FAR *state; struct inflate_state FAR *copy; unsigned char FAR *window; unsigned wsize; /* check input */ if (inflateStateCheck(source) || dest == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)source->state; /* allocate space */ copy = (struct inflate_state FAR *) ZALLOC(source, 1, sizeof(struct inflate_state)); if (copy == Z_NULL) return Z_MEM_ERROR; window = Z_NULL; if (state->window != Z_NULL) { window = (unsigned char FAR *) ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); if (window == Z_NULL) { ZFREE(source, copy); return Z_MEM_ERROR; } } /* copy state */ zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); copy->strm = dest; if (state->lencode >= state->codes && state->lencode <= state->codes + ENOUGH - 1) { copy->lencode = copy->codes + (state->lencode - state->codes); copy->distcode = copy->codes + (state->distcode - state->codes); } copy->next = copy->codes + (state->next - state->codes); if (window != Z_NULL) { wsize = 1U << state->wbits; zmemcpy(window, state->window, wsize); } copy->window = window; dest->state = (struct internal_state FAR *)copy; return Z_OK; } int ZEXPORT inflateUndermine(z_streamp strm, int subvert) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR state->sane = !subvert; return Z_OK; #else (void)subvert; state->sane = 1; return Z_DATA_ERROR; #endif } int ZEXPORT inflateValidate(z_streamp strm, int check) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (check && state->wrap) state->wrap |= 4; else state->wrap &= ~4; return Z_OK; } long ZEXPORT inflateMark(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return -(1L << 16); state = (struct inflate_state FAR *)strm->state; return (long)(((unsigned long)((long)state->back)) << 16) + (state->mode == COPY ? state->length : (state->mode == MATCH ? state->was - state->length : 0)); } unsigned long ZEXPORT inflateCodesUsed(z_streamp strm) { struct inflate_state FAR *state; if (inflateStateCheck(strm)) return (unsigned long)-1; state = (struct inflate_state FAR *)strm->state; return (unsigned long)(state->next - state->codes); } |
Added compat/zlib/inflate.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* inflate.h -- internal inflate state definition * Copyright (C) 1995-2019 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* define NO_GZIP when compiling if you want to disable gzip header and trailer decoding by inflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip decoding should be left enabled. */ #ifndef NO_GZIP # define GUNZIP #endif /* Possible inflate modes between inflate() calls */ typedef enum { HEAD = 16180, /* i: waiting for magic header */ FLAGS, /* i: waiting for method and flags (gzip) */ TIME, /* i: waiting for modification time (gzip) */ OS, /* i: waiting for extra flags and operating system (gzip) */ EXLEN, /* i: waiting for extra length (gzip) */ EXTRA, /* i: waiting for extra bytes (gzip) */ NAME, /* i: waiting for end of file name (gzip) */ COMMENT, /* i: waiting for end of comment (gzip) */ HCRC, /* i: waiting for header crc (gzip) */ DICTID, /* i: waiting for dictionary check value */ DICT, /* waiting for inflateSetDictionary() call */ TYPE, /* i: waiting for type bits, including last-flag bit */ TYPEDO, /* i: same, but skip check to exit inflate on new block */ STORED, /* i: waiting for stored size (length and complement) */ COPY_, /* i/o: same as COPY below, but only first time in */ COPY, /* i/o: waiting for input or output to copy stored block */ TABLE, /* i: waiting for dynamic block table lengths */ LENLENS, /* i: waiting for code length code lengths */ CODELENS, /* i: waiting for length/lit and distance code lengths */ LEN_, /* i: same as LEN below, but only first time in */ LEN, /* i: waiting for length/lit/eob code */ LENEXT, /* i: waiting for length extra bits */ DIST, /* i: waiting for distance code */ DISTEXT, /* i: waiting for distance extra bits */ MATCH, /* o: waiting for output space to copy string */ LIT, /* o: waiting for output space to write literal */ CHECK, /* i: waiting for 32-bit check value */ LENGTH, /* i: waiting for 32-bit length (gzip) */ DONE, /* finished check, done -- remain here until reset */ BAD, /* got a data error -- remain here until reset */ MEM, /* got an inflate() memory error -- remain here until reset */ SYNC /* looking for synchronization bytes to restart inflate() */ } inflate_mode; /* State transitions between above modes - (most modes can go to BAD or MEM on error -- not shown for clarity) Process header: HEAD -> (gzip) or (zlib) or (raw) (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> HCRC -> TYPE (zlib) -> DICTID or TYPE DICTID -> DICT -> TYPE (raw) -> TYPEDO Read deflate blocks: TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK STORED -> COPY_ -> COPY -> TYPE TABLE -> LENLENS -> CODELENS -> LEN_ LEN_ -> LEN Read deflate codes in fixed or dynamic block: LEN -> LENEXT or LIT or TYPE LENEXT -> DIST -> DISTEXT -> MATCH -> LEN LIT -> LEN Process trailer: CHECK -> LENGTH -> DONE */ /* State maintained between inflate() calls -- approximately 7K bytes, not including the allocated sliding window, which is up to 32K bytes. */ struct inflate_state { z_streamp strm; /* pointer back to this zlib stream */ inflate_mode mode; /* current inflate mode */ int last; /* true if processing last block */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip, bit 2 true to validate check value */ int havedict; /* true if dictionary provided */ int flags; /* gzip header method and flags, 0 if zlib, or -1 if raw or no header yet */ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ unsigned long check; /* protected copy of check value */ unsigned long total; /* protected copy of output count */ gz_headerp head; /* where to save gzip header information */ /* sliding window */ unsigned wbits; /* log base 2 of requested window size */ unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned wnext; /* window write index */ unsigned char FAR *window; /* allocated sliding window, if needed */ /* bit accumulator */ unsigned long hold; /* input bit accumulator */ unsigned bits; /* number of bits in "in" */ /* for string and stored block copying */ unsigned length; /* literal or length of data to copy */ unsigned offset; /* distance back to copy string from */ /* for table and code decoding */ unsigned extra; /* extra bits needed */ /* fixed and dynamic code tables */ code const FAR *lencode; /* starting table for length/literal codes */ code const FAR *distcode; /* starting table for distance codes */ unsigned lenbits; /* index bits for lencode */ unsigned distbits; /* index bits for distcode */ /* dynamic table building */ unsigned ncode; /* number of code length code lengths */ unsigned nlen; /* number of length code lengths */ unsigned ndist; /* number of distance code lengths */ unsigned have; /* number of code lengths in lens[] */ code FAR *next; /* next available space in codes[] */ unsigned short lens[320]; /* temporary storage for code lengths */ unsigned short work[288]; /* work area for code table building */ code codes[ENOUGH]; /* space for code tables */ int sane; /* if false, allow invalid distance too far */ int back; /* bits back of last unprocessed length/lit */ unsigned was; /* initial length of match */ }; |
Added compat/zlib/inftrees.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 | /* inftrees.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #define MAXBITS 15 const char inflate_copyright[] = " inflate 1.3.1 Copyright 1995-2024 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ /* Build a set of tables to decode the provided canonical Huffman code. The code lengths are lens[0..codes-1]. The result starts at *table, whose indices are 0..2^bits-1. work is a writable array of at least lens shorts, which is used as a work area. type is the type of code to be generated, CODES, LENS, or DISTS. On return, zero is success, -1 is an invalid code, and +1 means that ENOUGH isn't enough. table on return points to the next available entry's address. bits is the requested root table index bits, and on return it is the actual root table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, unsigned codes, code FAR * FAR *table, unsigned FAR *bits, unsigned short FAR *work) { unsigned len; /* a code's length in bits */ unsigned sym; /* index of code symbols */ unsigned min, max; /* minimum and maximum code lengths */ unsigned root; /* number of index bits for root table */ unsigned curr; /* number of index bits for current table */ unsigned drop; /* code bits to drop for sub-table */ int left; /* number of prefix codes available */ unsigned used; /* code entries in table used */ unsigned huff; /* Huffman code */ unsigned incr; /* for incrementing code, index */ unsigned fill; /* index for replicating entries */ unsigned low; /* low bits for current root entry */ unsigned mask; /* mask for low root bits */ code here; /* table entry for duplication */ code FAR *next; /* next available space in table */ const unsigned short FAR *base; /* base value table to use */ const unsigned short FAR *extra; /* extra bits table to use */ unsigned match; /* use base and extra for symbol >= match */ unsigned short count[MAXBITS+1]; /* number of codes of each length */ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ static const unsigned short lbase[31] = { /* Length codes 257..285 base */ 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 unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 203, 77}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 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 unsigned short dext[32] = { /* Distance codes 0..29 extra */ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64}; /* Process a set of code lengths to create a canonical Huffman code. The code lengths are lens[0..codes-1]. Each length corresponds to the symbols 0..codes-1. The Huffman code is generated by first sorting the symbols by length from short to long, and retaining the symbol order for codes with equal lengths. Then the code starts with all zero bits for the first code of the shortest length, and the codes are integer increments for the same length, and zeros are appended as the length increases. For the deflate format, these bits are stored backwards from their more natural integer increment ordering, and so when the decoding tables are built in the large loop below, the integer codes are incremented backwards. This routine assumes, but does not check, that all of the entries in lens[] are in the range 0..MAXBITS. The caller must assure this. 1..MAXBITS is interpreted as that code length. zero means that that symbol does not occur in this code. The codes are sorted by computing a count of codes for each length, creating from that a table of starting indices for each length in the sorted table, and then entering the symbols in order in the sorted table. The sorted table is work[], with that space being provided by the caller. The length counts are used for other purposes as well, i.e. finding the minimum and maximum length codes, determining if there are any codes at all, checking for a valid set of lengths, and looking ahead at length counts to determine sub-table sizes when building the decoding tables. */ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ for (len = 0; len <= MAXBITS; len++) count[len] = 0; for (sym = 0; sym < codes; sym++) count[lens[sym]]++; /* bound code lengths, force root to be within code lengths */ root = *bits; for (max = MAXBITS; max >= 1; max--) if (count[max] != 0) break; if (root > max) root = max; if (max == 0) { /* no symbols to code at all */ here.op = (unsigned char)64; /* invalid code marker */ here.bits = (unsigned char)1; here.val = (unsigned short)0; *(*table)++ = here; /* make a table to force an error */ *(*table)++ = here; *bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } for (min = 1; min < max; min++) if (count[min] != 0) break; if (root < min) root = min; /* check for an over-subscribed or incomplete set of lengths */ left = 1; for (len = 1; len <= MAXBITS; len++) { left <<= 1; left -= count[len]; if (left < 0) return -1; /* over-subscribed */ } if (left > 0 && (type == CODES || max != 1)) return -1; /* incomplete set */ /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + count[len]; /* sort symbols by length, by symbol order within each length */ for (sym = 0; sym < codes; sym++) if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; /* Create and fill in decoding tables. In this loop, the table being filled is at next and has curr index bits. The code being used is huff with length len. That code is converted to an index by dropping drop bits off of the bottom. For codes where len is less than drop + curr, those top drop + curr - len bits are incremented through all values to fill the table with replicated entries. root is the number of index bits for the root table. When len exceeds root, sub-tables are created pointed to by the root entry with an index of the low root bits of huff. This is saved in low to check for when a new sub-table should be started. drop is zero when the root table is being filled, and drop is root when sub-tables are being filled. When a new sub-table is needed, it is necessary to look ahead in the code lengths to determine what size sub-table is needed. The length counts are used for this, and so count[] is decremented as codes are entered in the tables. used keeps track of how many table entries have been allocated from the provided *table space. It is checked for LENS and DIST tables against the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in the initial root table size constants. See the comments in inftrees.h for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This routine permits incomplete codes, so another loop after this one fills in the rest of the decoding tables with invalid code markers. */ /* set up for code type */ switch (type) { case CODES: base = extra = work; /* dummy value--not used */ match = 20; break; case LENS: base = lbase; extra = lext; match = 257; break; default: /* DISTS */ base = dbase; extra = dext; match = 0; } /* initialize state for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ next = *table; /* current table to fill in */ curr = root; /* current table index bits */ drop = 0; /* current bits to drop from code for index */ low = (unsigned)(-1); /* trigger new sub-table when len > root */ used = 1U << root; /* use root table entries */ mask = used - 1; /* mask for comparing low */ /* check available table space */ if ((type == LENS && used > ENOUGH_LENS) || (type == DISTS && used > ENOUGH_DISTS)) return 1; /* process all codes and make table entries */ for (;;) { /* create table entry */ here.bits = (unsigned char)(len - drop); if (work[sym] + 1U < match) { here.op = (unsigned char)0; here.val = work[sym]; } else if (work[sym] >= match) { here.op = (unsigned char)(extra[work[sym] - match]); here.val = base[work[sym] - match]; } else { here.op = (unsigned char)(32 + 64); /* end of block */ here.val = 0; } /* replicate for those indices with low len bits equal to huff */ incr = 1U << (len - drop); fill = 1U << curr; min = fill; /* save offset to next table */ do { fill -= incr; next[(huff >> drop) + fill] = here; } while (fill != 0); /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; if (incr != 0) { huff &= incr - 1; huff += incr; } else huff = 0; /* go to next symbol, update count, len */ sym++; if (--(count[len]) == 0) { if (len == max) break; len = lens[work[sym]]; } /* create new sub-table if needed */ if (len > root && (huff & mask) != low) { /* if first time, transition to sub-tables */ if (drop == 0) drop = root; /* increment past last table */ next += min; /* here min is 1 << curr */ /* determine length of next table */ curr = len - drop; left = (int)(1 << curr); while (curr + drop < max) { left -= count[curr + drop]; if (left <= 0) break; curr++; left <<= 1; } /* check for enough space */ used += 1U << curr; if ((type == LENS && used > ENOUGH_LENS) || (type == DISTS && used > ENOUGH_DISTS)) return 1; /* point entry in root table to sub-table */ low = huff & mask; (*table)[low].op = (unsigned char)curr; (*table)[low].bits = (unsigned char)root; (*table)[low].val = (unsigned short)(next - *table); } } /* fill in remaining table entry if code is incomplete (guaranteed to have at most one remaining entry, since if the code is incomplete, the maximum code length that was allowed to get this far is one bit) */ if (huff != 0) { here.op = (unsigned char)64; /* invalid code marker */ here.bits = (unsigned char)(len - drop); here.val = (unsigned short)0; next[huff] = here; } /* set return parameters */ *table += used; *bits = root; return 0; } |
Added compat/zlib/inftrees.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* inftrees.h -- header to use inftrees.c * Copyright (C) 1995-2005, 2010 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Structure for decoding tables. Each entry provides either the information needed to do the operation requested by the code that indexed that table entry, or it provides a pointer to another table that indexes more bits of the code. op indicates whether the entry is a pointer to another table, a literal, a length or distance, an end-of-block, or an invalid code. For a table pointer, the low four bits of op is the number of index bits of that table. For a length or distance, the low four bits of op is the number of extra bits to get after the code. bits is the number of bits in this code or part of the code to drop off of the bit buffer. val is the actual byte to output in the case of a literal, the base length or distance, or the offset from the current table to the next table. Each entry is four bytes. */ typedef struct { unsigned char op; /* operation, extra bits, table bits */ unsigned char bits; /* bits in this part of the code */ unsigned short val; /* offset in table or code value */ } code; /* op values as set by inflate_table(): 00000000 - literal 0000tttt - table link, tttt != 0 is the number of table index bits 0001eeee - length or distance, eeee is the number of extra bits 01100000 - end of block 01000000 - invalid code */ /* Maximum size of the dynamic table. The maximum number of code structures is 1444, which is the sum of 852 for literal/length codes and 592 for distance codes. These values were found by exhaustive searches using the program examples/enough.c found in the zlib distribution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes returns 852, and "enough 30 6 15" for distance codes returns 592. The initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in inflate.c and infback.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ #define ENOUGH_LENS 852 #define ENOUGH_DISTS 592 #define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) /* Type of code to build for inflate_table() */ typedef enum { CODES, LENS, DISTS } codetype; int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, unsigned codes, code FAR * FAR *table, unsigned FAR *bits, unsigned short FAR *work); |
Added compat/zlib/make_vms.com.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | $! make libz under VMS written by $! Martin P.J. Zinser $! $! In case of problems with the install you might contact me at $! zinser@zinser.no-ip.info(preferred) or $! martin.zinser@eurexchange.com (work) $! $! Make procedure history for Zlib $! $!------------------------------------------------------------------------------ $! Version history $! 0.01 20060120 First version to receive a number $! 0.02 20061008 Adapt to new Makefile.in $! 0.03 20091224 Add support for large file check $! 0.04 20100110 Add new gzclose, gzlib, gzread, gzwrite $! 0.05 20100221 Exchange zlibdefs.h by zconf.h.in $! 0.06 20120111 Fix missing amiss_err, update zconf_h.in, fix new examples $! subdir path, update module search in makefile.in $! 0.07 20120115 Triggered by work done by Alexey Chupahin completely redesigned $! shared image creation $! 0.08 20120219 Make it work on VAX again, pre-load missing symbols to shared $! image $! 0.09 20120305 SMS. P1 sets builder ("MMK", "MMS", " " (built-in)). $! "" -> automatic, preference: MMK, MMS, built-in. $! $ on error then goto err_exit $! $ true = 1 $ false = 0 $ tmpnam = "temp_" + f$getjpi("","pid") $ tt = tmpnam + ".txt" $ tc = tmpnam + ".c" $ th = tmpnam + ".h" $ define/nolog tconfig 'th' $ its_decc = false $ its_vaxc = false $ its_gnuc = false $ s_case = False $! $! Setup variables holding "config" information $! $ Make = "''p1'" $ name = "Zlib" $ version = "?.?.?" $ v_string = "ZLIB_VERSION" $ v_file = "zlib.h" $ ccopt = "/include = []" $ lopts = "" $ dnsrl = "" $ aconf_in_file = "zconf.h.in#zconf.h_in#zconf_h.in" $ conf_check_string = "" $ linkonly = false $ optfile = name + ".opt" $ mapfile = name + ".map" $ libdefs = "" $ vax = f$getsyi("HW_MODEL").lt.1024 $ axp = f$getsyi("HW_MODEL").ge.1024 .and. f$getsyi("HW_MODEL").lt.4096 $ ia64 = f$getsyi("HW_MODEL").ge.4096 $! $! 2012-03-05 SMS. $! Why is this needed? And if it is needed, why not simply ".not. vax"? $! $!!! if axp .or. ia64 then set proc/parse=extended $! $ whoami = f$parse(f$environment("Procedure"),,,,"NO_CONCEAL") $ mydef = F$parse(whoami,,,"DEVICE") $ mydir = f$parse(whoami,,,"DIRECTORY") - "][" $ myproc = f$parse(whoami,,,"Name") + f$parse(whoami,,,"type") $! $! Check for MMK/MMS $! $ if (Make .eqs. "") $ then $ If F$Search ("Sys$System:MMS.EXE") .nes. "" Then Make = "MMS" $ If F$Type (MMK) .eqs. "STRING" Then Make = "MMK" $ else $ Make = f$edit( Make, "trim") $ endif $! $ gosub find_version $! $ open/write topt tmp.opt $ open/write optf 'optfile' $! $ gosub check_opts $! $! Look for the compiler used $! $ gosub check_compiler $ close topt $ close optf $! $ if its_decc $ then $ ccopt = "/prefix=all" + ccopt $ if f$trnlnm("SYS") .eqs. "" $ then $ if axp $ then $ define sys sys$library: $ else $ ccopt = "/decc" + ccopt $ define sys decc$library_include: $ endif $ endif $! $! 2012-03-05 SMS. $! Why /NAMES = AS_IS? Why not simply ".not. vax"? And why not on VAX? $! $ if axp .or. ia64 $ then $ ccopt = ccopt + "/name=as_is/opt=(inline=speed)" $ s_case = true $ endif $ endif $ if its_vaxc .or. its_gnuc $ then $ if f$trnlnm("SYS").eqs."" then define sys sys$library: $ endif $! $! Build a fake configure input header $! $ open/write conf_hin config.hin $ write conf_hin "#undef _LARGEFILE64_SOURCE" $ close conf_hin $! $! $ i = 0 $FIND_ACONF: $ fname = f$element(i,"#",aconf_in_file) $ if fname .eqs. "#" then goto AMISS_ERR $ if f$search(fname) .eqs. "" $ then $ i = i + 1 $ goto find_aconf $ endif $ open/read/err=aconf_err aconf_in 'fname' $ open/write aconf zconf.h $ACONF_LOOP: $ read/end_of_file=aconf_exit aconf_in line $ work = f$edit(line, "compress,trim") $ if f$extract(0,6,work) .nes. "#undef" $ then $ if f$extract(0,12,work) .nes. "#cmakedefine" $ then $ write aconf line $ endif $ else $ cdef = f$element(1," ",work) $ gosub check_config $ endif $ goto aconf_loop $ACONF_EXIT: $ write aconf "" $ write aconf "/* VMS specifics added by make_vms.com: */" $ write aconf "#define VMS 1" $ write aconf "#include <unistd.h>" $ write aconf "#include <unixio.h>" $ write aconf "#ifdef _LARGEFILE" $ write aconf "# define off64_t __off64_t" $ write aconf "# define fopen64 fopen" $ write aconf "# define fseeko64 fseeko" $ write aconf "# define lseek64 lseek" $ write aconf "# define ftello64 ftell" $ write aconf "#endif" $ write aconf "#if !defined( __VAX) && (__CRTL_VER >= 70312000)" $ write aconf "# define HAVE_VSNPRINTF" $ write aconf "#endif" $ close aconf_in $ close aconf $ if f$search("''th'") .nes. "" then delete 'th';* $! Build the thing plain or with mms $! $ write sys$output "Compiling Zlib sources ..." $ if make.eqs."" $ then $ if (f$search( "example.obj;*") .nes. "") then delete example.obj;* $ if (f$search( "minigzip.obj;*") .nes. "") then delete minigzip.obj;* $ CALL MAKE adler32.OBJ "CC ''CCOPT' adler32" - adler32.c zlib.h zconf.h $ CALL MAKE compress.OBJ "CC ''CCOPT' compress" - compress.c zlib.h zconf.h $ CALL MAKE crc32.OBJ "CC ''CCOPT' crc32" - crc32.c zlib.h zconf.h $ CALL MAKE deflate.OBJ "CC ''CCOPT' deflate" - deflate.c deflate.h zutil.h zlib.h zconf.h $ CALL MAKE gzclose.OBJ "CC ''CCOPT' gzclose" - gzclose.c zutil.h zlib.h zconf.h $ CALL MAKE gzlib.OBJ "CC ''CCOPT' gzlib" - gzlib.c zutil.h zlib.h zconf.h $ CALL MAKE gzread.OBJ "CC ''CCOPT' gzread" - gzread.c zutil.h zlib.h zconf.h $ CALL MAKE gzwrite.OBJ "CC ''CCOPT' gzwrite" - gzwrite.c zutil.h zlib.h zconf.h $ CALL MAKE infback.OBJ "CC ''CCOPT' infback" - infback.c zutil.h inftrees.h inflate.h inffast.h inffixed.h $ CALL MAKE inffast.OBJ "CC ''CCOPT' inffast" - inffast.c zutil.h zlib.h zconf.h inffast.h $ CALL MAKE inflate.OBJ "CC ''CCOPT' inflate" - inflate.c zutil.h zlib.h zconf.h infblock.h $ CALL MAKE inftrees.OBJ "CC ''CCOPT' inftrees" - inftrees.c zutil.h zlib.h zconf.h inftrees.h $ CALL MAKE trees.OBJ "CC ''CCOPT' trees" - trees.c deflate.h zutil.h zlib.h zconf.h $ CALL MAKE uncompr.OBJ "CC ''CCOPT' uncompr" - uncompr.c zlib.h zconf.h $ CALL MAKE zutil.OBJ "CC ''CCOPT' zutil" - zutil.c zutil.h zlib.h zconf.h $ write sys$output "Building Zlib ..." $ CALL MAKE libz.OLB "lib/crea libz.olb *.obj" *.OBJ $ write sys$output "Building example..." $ CALL MAKE example.OBJ "CC ''CCOPT' [.test]example" - [.test]example.c zlib.h zconf.h $ call make example.exe "LINK example,libz.olb/lib" example.obj libz.olb $ write sys$output "Building minigzip..." $ CALL MAKE minigzip.OBJ "CC ''CCOPT' [.test]minigzip" - [.test]minigzip.c zlib.h zconf.h $ call make minigzip.exe - "LINK minigzip,libz.olb/lib" - minigzip.obj libz.olb $ else $ gosub crea_mms $ write sys$output "Make ''name' ''version' with ''Make' " $ 'make' $ endif $! $! Create shareable image $! $ gosub crea_olist $ write sys$output "Creating libzshr.exe" $ call map_2_shopt 'mapfile' 'optfile' $ LINK_'lopts'/SHARE=libzshr.exe modules.opt/opt,'optfile'/opt $ write sys$output "Zlib build completed" $ delete/nolog tmp.opt;* $ exit $AMISS_ERR: $ write sys$output "No source for config.hin found." $ write sys$output "Tried any of ''aconf_in_file'" $ goto err_exit $CC_ERR: $ write sys$output "C compiler required to build ''name'" $ goto err_exit $ERR_EXIT: $ set message/facil/ident/sever/text $ close/nolog optf $ close/nolog topt $ close/nolog aconf_in $ close/nolog aconf $ close/nolog out $ close/nolog min $ close/nolog mod $ close/nolog h_in $ write sys$output "Exiting..." $ exit 2 $! $! $MAKE: SUBROUTINE !SUBROUTINE TO CHECK DEPENDENCIES $ V = 'F$Verify(0) $! P1 = What we are trying to make $! P2 = Command to make it $! P3 - P8 What it depends on $ $ If F$Search(P1) .Eqs. "" Then Goto Makeit $ Time = F$CvTime(F$File(P1,"RDT")) $arg=3 $Loop: $ Argument = P'arg $ If Argument .Eqs. "" Then Goto Exit $ El=0 $Loop2: $ File = F$Element(El," ",Argument) $ If File .Eqs. " " Then Goto Endl $ AFile = "" $Loop3: $ OFile = AFile $ AFile = F$Search(File) $ If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl $ If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit $ Goto Loop3 $NextEL: $ El = El + 1 $ Goto Loop2 $EndL: $ arg=arg+1 $ If arg .Le. 8 Then Goto Loop $ Goto Exit $ $Makeit: $ VV=F$VERIFY(0) $ write sys$output P2 $ 'P2 $ VV='F$Verify(VV) $Exit: $ If V Then Set Verify $ENDSUBROUTINE $!------------------------------------------------------------------------------ $! $! Check command line options and set symbols accordingly $! $!------------------------------------------------------------------------------ $! Version history $! 0.01 20041206 First version to receive a number $! 0.02 20060126 Add new "HELP" target $ CHECK_OPTS: $ i = 1 $ OPT_LOOP: $ if i .lt. 9 $ then $ cparm = f$edit(p'i',"upcase") $! $! Check if parameter actually contains something $! $ if f$edit(cparm,"trim") .nes. "" $ then $ if cparm .eqs. "DEBUG" $ then $ ccopt = ccopt + "/noopt/deb" $ lopts = lopts + "/deb" $ endif $ if f$locate("CCOPT=",cparm) .lt. f$length(cparm) $ then $ start = f$locate("=",cparm) + 1 $ len = f$length(cparm) - start $ ccopt = ccopt + f$extract(start,len,cparm) $ if f$locate("AS_IS",f$edit(ccopt,"UPCASE")) .lt. f$length(ccopt) - then s_case = true $ endif $ if cparm .eqs. "LINK" then linkonly = true $ if f$locate("LOPTS=",cparm) .lt. f$length(cparm) $ then $ start = f$locate("=",cparm) + 1 $ len = f$length(cparm) - start $ lopts = lopts + f$extract(start,len,cparm) $ endif $ if f$locate("CC=",cparm) .lt. f$length(cparm) $ then $ start = f$locate("=",cparm) + 1 $ len = f$length(cparm) - start $ cc_com = f$extract(start,len,cparm) if (cc_com .nes. "DECC") .and. - (cc_com .nes. "VAXC") .and. - (cc_com .nes. "GNUC") $ then $ write sys$output "Unsupported compiler choice ''cc_com' ignored" $ write sys$output "Use DECC, VAXC, or GNUC instead" $ else $ if cc_com .eqs. "DECC" then its_decc = true $ if cc_com .eqs. "VAXC" then its_vaxc = true $ if cc_com .eqs. "GNUC" then its_gnuc = true $ endif $ endif $ if f$locate("MAKE=",cparm) .lt. f$length(cparm) $ then $ start = f$locate("=",cparm) + 1 $ len = f$length(cparm) - start $ mmks = f$extract(start,len,cparm) $ if (mmks .eqs. "MMK") .or. (mmks .eqs. "MMS") $ then $ make = mmks $ else $ write sys$output "Unsupported make choice ''mmks' ignored" $ write sys$output "Use MMK or MMS instead" $ endif $ endif $ if cparm .eqs. "HELP" then gosub bhelp $ endif $ i = i + 1 $ goto opt_loop $ endif $ return $!------------------------------------------------------------------------------ $! $! Look for the compiler used $! $! Version history $! 0.01 20040223 First version to receive a number $! 0.02 20040229 Save/set value of decc$no_rooted_search_lists $! 0.03 20060202 Extend handling of GNU C $! 0.04 20090402 Compaq -> hp $CHECK_COMPILER: $ if (.not. (its_decc .or. its_vaxc .or. its_gnuc)) $ then $ its_decc = (f$search("SYS$SYSTEM:DECC$COMPILER.EXE") .nes. "") $ its_vaxc = .not. its_decc .and. (F$Search("SYS$System:VAXC.Exe") .nes. "") $ its_gnuc = .not. (its_decc .or. its_vaxc) .and. (f$trnlnm("gnu_cc") .nes. "") $ endif $! $! Exit if no compiler available $! $ if (.not. (its_decc .or. its_vaxc .or. its_gnuc)) $ then goto CC_ERR $ else $ if its_decc $ then $ write sys$output "CC compiler check ... hp C" $ if f$trnlnm("decc$no_rooted_search_lists") .nes. "" $ then $ dnrsl = f$trnlnm("decc$no_rooted_search_lists") $ endif $ define/nolog decc$no_rooted_search_lists 1 $ else $ if its_vaxc then write sys$output "CC compiler check ... VAX C" $ if its_gnuc $ then $ write sys$output "CC compiler check ... GNU C" $ if f$trnlnm(topt) then write topt "gnu_cc:[000000]gcclib.olb/lib" $ if f$trnlnm(optf) then write optf "gnu_cc:[000000]gcclib.olb/lib" $ cc = "gcc" $ endif $ if f$trnlnm(topt) then write topt "sys$share:vaxcrtl.exe/share" $ if f$trnlnm(optf) then write optf "sys$share:vaxcrtl.exe/share" $ endif $ endif $ return $!------------------------------------------------------------------------------ $! $! If MMS/MMK are available dump out the descrip.mms if required $! $CREA_MMS: $ write sys$output "Creating descrip.mms..." $ create descrip.mms $ open/append out descrip.mms $ copy sys$input: out $ deck # descrip.mms: MMS description file for building zlib on VMS # written by Martin P.J. Zinser # <zinser@zinser.no-ip.info or martin.zinser@eurexchange.com> OBJS = adler32.obj, compress.obj, crc32.obj, gzclose.obj, gzlib.obj\ gzread.obj, gzwrite.obj, uncompr.obj, infback.obj\ deflate.obj, trees.obj, zutil.obj, inflate.obj, \ inftrees.obj, inffast.obj $ eod $ write out "CFLAGS=", ccopt $ write out "LOPTS=", lopts $ write out "all : example.exe minigzip.exe libz.olb" $ copy sys$input: out $ deck @ write sys$output " Example applications available" libz.olb : libz.olb($(OBJS)) @ write sys$output " libz available" example.exe : example.obj libz.olb link $(LOPTS) example,libz.olb/lib minigzip.exe : minigzip.obj libz.olb link $(LOPTS) minigzip,libz.olb/lib clean : delete *.obj;*,libz.olb;*,*.opt;*,*.exe;* # Other dependencies. adler32.obj : adler32.c zutil.h zlib.h zconf.h compress.obj : compress.c zlib.h zconf.h crc32.obj : crc32.c zutil.h zlib.h zconf.h deflate.obj : deflate.c deflate.h zutil.h zlib.h zconf.h example.obj : [.test]example.c zlib.h zconf.h gzclose.obj : gzclose.c zutil.h zlib.h zconf.h gzlib.obj : gzlib.c zutil.h zlib.h zconf.h gzread.obj : gzread.c zutil.h zlib.h zconf.h gzwrite.obj : gzwrite.c zutil.h zlib.h zconf.h inffast.obj : inffast.c zutil.h zlib.h zconf.h inftrees.h inffast.h inflate.obj : inflate.c zutil.h zlib.h zconf.h inftrees.obj : inftrees.c zutil.h zlib.h zconf.h inftrees.h minigzip.obj : [.test]minigzip.c zlib.h zconf.h trees.obj : trees.c deflate.h zutil.h zlib.h zconf.h uncompr.obj : uncompr.c zlib.h zconf.h zutil.obj : zutil.c zutil.h zlib.h zconf.h infback.obj : infback.c zutil.h inftrees.h inflate.h inffast.h inffixed.h $ eod $ close out $ return $!------------------------------------------------------------------------------ $! $! Read list of core library sources from makefile.in and create options $! needed to build shareable image $! $CREA_OLIST: $ open/read min makefile.in $ open/write mod modules.opt $ src_check_list = "OBJZ =#OBJG =" $MRLOOP: $ read/end=mrdone min rec $ i = 0 $SRC_CHECK_LOOP: $ src_check = f$element(i, "#", src_check_list) $ i = i+1 $ if src_check .eqs. "#" then goto mrloop $ if (f$extract(0,6,rec) .nes. src_check) then goto src_check_loop $ rec = rec - src_check $ gosub extra_filnam $ if (f$element(1,"\",rec) .eqs. "\") then goto mrloop $MRSLOOP: $ read/end=mrdone min rec $ gosub extra_filnam $ if (f$element(1,"\",rec) .nes. "\") then goto mrsloop $MRDONE: $ close min $ close mod $ return $!------------------------------------------------------------------------------ $! $! Take record extracted in crea_olist and split it into single filenames $! $EXTRA_FILNAM: $ myrec = f$edit(rec - "\", "trim,compress") $ i = 0 $FELOOP: $ srcfil = f$element(i," ", myrec) $ if (srcfil .nes. " ") $ then $ write mod f$parse(srcfil,,,"NAME"), ".obj" $ i = i + 1 $ goto feloop $ endif $ return $!------------------------------------------------------------------------------ $! $! Find current Zlib version number $! $FIND_VERSION: $ open/read h_in 'v_file' $hloop: $ read/end=hdone h_in rec $ rec = f$edit(rec,"TRIM") $ if (f$extract(0,1,rec) .nes. "#") then goto hloop $ rec = f$edit(rec - "#", "TRIM") $ if f$element(0," ",rec) .nes. "define" then goto hloop $ if f$element(1," ",rec) .eqs. v_string $ then $ version = 'f$element(2," ",rec)' $ goto hdone $ endif $ goto hloop $hdone: $ close h_in $ return $!------------------------------------------------------------------------------ $! $CHECK_CONFIG: $! $ in_ldef = f$locate(cdef,libdefs) $ if (in_ldef .lt. f$length(libdefs)) $ then $ write aconf "#define ''cdef' 1" $ libdefs = f$extract(0,in_ldef,libdefs) + - f$extract(in_ldef + f$length(cdef) + 1, - f$length(libdefs) - in_ldef - f$length(cdef) - 1, - libdefs) $ else $ if (f$type('cdef') .eqs. "INTEGER") $ then $ write aconf "#define ''cdef' ", 'cdef' $ else $ if (f$type('cdef') .eqs. "STRING") $ then $ write aconf "#define ''cdef' ", """", '''cdef'', """" $ else $ gosub check_cc_def $ endif $ endif $ endif $ return $!------------------------------------------------------------------------------ $! $! Check if this is a define relating to the properties of the C/C++ $! compiler $! $ CHECK_CC_DEF: $ if (cdef .eqs. "_LARGEFILE64_SOURCE") $ then $ copy sys$input: 'tc' $ deck #include "tconfig" #define _LARGEFILE #include <stdio.h> int main(){ FILE *fp; fp = fopen("temp.txt","r"); fseeko(fp,1,SEEK_SET); fclose(fp); } $ eod $ test_inv = false $ comm_h = false $ gosub cc_prop_check $ return $ endif $ write aconf "/* ", line, " */" $ return $!------------------------------------------------------------------------------ $! $! Check for properties of C/C++ compiler $! $! Version history $! 0.01 20031020 First version to receive a number $! 0.02 20031022 Added logic for defines with value $! 0.03 20040309 Make sure local config file gets not deleted $! 0.04 20041230 Also write include for configure run $! 0.05 20050103 Add processing of "comment defines" $CC_PROP_CHECK: $ cc_prop = true $ is_need = false $ is_need = (f$extract(0,4,cdef) .eqs. "NEED") .or. (test_inv .eq. true) $ if f$search(th) .eqs. "" then create 'th' $ set message/nofac/noident/nosever/notext $ on error then continue $ cc 'tmpnam' $ if .not. ($status) then cc_prop = false $ on error then continue $! The headers might lie about the capabilities of the RTL $ link 'tmpnam',tmp.opt/opt $ if .not. ($status) then cc_prop = false $ set message/fac/ident/sever/text $ on error then goto err_exit $ delete/nolog 'tmpnam'.*;*/exclude='th' $ if (cc_prop .and. .not. is_need) .or. - (.not. cc_prop .and. is_need) $ then $ write sys$output "Checking for ''cdef'... yes" $ if f$type('cdef_val'_yes) .nes. "" $ then $ if f$type('cdef_val'_yes) .eqs. "INTEGER" - then call write_config f$fao("#define !AS !UL",cdef,'cdef_val'_yes) $ if f$type('cdef_val'_yes) .eqs. "STRING" - then call write_config f$fao("#define !AS !AS",cdef,'cdef_val'_yes) $ else $ call write_config f$fao("#define !AS 1",cdef) $ endif $ if (cdef .eqs. "HAVE_FSEEKO") .or. (cdef .eqs. "_LARGE_FILES") .or. - (cdef .eqs. "_LARGEFILE64_SOURCE") then - call write_config f$string("#define _LARGEFILE 1") $ else $ write sys$output "Checking for ''cdef'... no" $ if (comm_h) $ then call write_config f$fao("/* !AS */",line) $ else $ if f$type('cdef_val'_no) .nes. "" $ then $ if f$type('cdef_val'_no) .eqs. "INTEGER" - then call write_config f$fao("#define !AS !UL",cdef,'cdef_val'_no) $ if f$type('cdef_val'_no) .eqs. "STRING" - then call write_config f$fao("#define !AS !AS",cdef,'cdef_val'_no) $ else $ call write_config f$fao("#undef !AS",cdef) $ endif $ endif $ endif $ return $!------------------------------------------------------------------------------ $! $! Check for properties of C/C++ compiler with multiple result values $! $! Version history $! 0.01 20040127 First version $! 0.02 20050103 Reconcile changes from cc_prop up to version 0.05 $CC_MPROP_CHECK: $ cc_prop = true $ i = 1 $ idel = 1 $ MT_LOOP: $ if f$type(result_'i') .eqs. "STRING" $ then $ set message/nofac/noident/nosever/notext $ on error then continue $ cc 'tmpnam'_'i' $ if .not. ($status) then cc_prop = false $ on error then continue $! The headers might lie about the capabilities of the RTL $ link 'tmpnam'_'i',tmp.opt/opt $ if .not. ($status) then cc_prop = false $ set message/fac/ident/sever/text $ on error then goto err_exit $ delete/nolog 'tmpnam'_'i'.*;* $ if (cc_prop) $ then $ write sys$output "Checking for ''cdef'... ", mdef_'i' $ if f$type(mdef_'i') .eqs. "INTEGER" - then call write_config f$fao("#define !AS !UL",cdef,mdef_'i') $ if f$type('cdef_val'_yes) .eqs. "STRING" - then call write_config f$fao("#define !AS !AS",cdef,mdef_'i') $ goto msym_clean $ else $ i = i + 1 $ goto mt_loop $ endif $ endif $ write sys$output "Checking for ''cdef'... no" $ call write_config f$fao("#undef !AS",cdef) $ MSYM_CLEAN: $ if (idel .le. msym_max) $ then $ delete/sym mdef_'idel' $ idel = idel + 1 $ goto msym_clean $ endif $ return $!------------------------------------------------------------------------------ $! $! Write configuration to both permanent and temporary config file $! $! Version history $! 0.01 20031029 First version to receive a number $! $WRITE_CONFIG: SUBROUTINE $ write aconf 'p1' $ open/append confh 'th' $ write confh 'p1' $ close confh $ENDSUBROUTINE $!------------------------------------------------------------------------------ $! $! Analyze the project map file and create the symbol vector for a shareable $! image from it $! $! Version history $! 0.01 20120128 First version $! 0.02 20120226 Add pre-load logic $! $ MAP_2_SHOPT: Subroutine $! $ SAY := "WRITE_ SYS$OUTPUT" $! $ IF F$SEARCH("''P1'") .EQS. "" $ THEN $ SAY "MAP_2_SHOPT-E-NOSUCHFILE: Error, inputfile ''p1' not available" $ goto exit_m2s $ ENDIF $ IF "''P2'" .EQS. "" $ THEN $ SAY "MAP_2_SHOPT: Error, no output file provided" $ goto exit_m2s $ ENDIF $! $ module1 = "deflate#deflateEnd#deflateInit_#deflateParams#deflateSetDictionary" $ module2 = "gzclose#gzerror#gzgetc#gzgets#gzopen#gzprintf#gzputc#gzputs#gzread" $ module3 = "gzseek#gztell#inflate#inflateEnd#inflateInit_#inflateSetDictionary" $ module4 = "inflateSync#uncompress#zlibVersion#compress" $ open/read map 'p1 $ if axp .or. ia64 $ then $ open/write aopt a.opt $ open/write bopt b.opt $ write aopt " CASE_SENSITIVE=YES" $ write bopt "SYMBOL_VECTOR= (-" $ mod_sym_num = 1 $ MOD_SYM_LOOP: $ if f$type(module'mod_sym_num') .nes. "" $ then $ mod_in = 0 $ MOD_SYM_IN: $ shared_proc = f$element(mod_in, "#", module'mod_sym_num') $ if shared_proc .nes. "#" $ then $ write aopt f$fao(" symbol_vector=(!AS/!AS=PROCEDURE)",- f$edit(shared_proc,"upcase"),shared_proc) $ write bopt f$fao("!AS=PROCEDURE,-",shared_proc) $ mod_in = mod_in + 1 $ goto mod_sym_in $ endif $ mod_sym_num = mod_sym_num + 1 $ goto mod_sym_loop $ endif $MAP_LOOP: $ read/end=map_end map line $ if (f$locate("{",line).lt. f$length(line)) .or. - (f$locate("global:", line) .lt. f$length(line)) $ then $ proc = true $ goto map_loop $ endif $ if f$locate("}",line).lt. f$length(line) then proc = false $ if f$locate("local:", line) .lt. f$length(line) then proc = false $ if proc $ then $ shared_proc = f$edit(line,"collapse") $ chop_semi = f$locate(";", shared_proc) $ if chop_semi .lt. f$length(shared_proc) then - shared_proc = f$extract(0, chop_semi, shared_proc) $ write aopt f$fao(" symbol_vector=(!AS/!AS=PROCEDURE)",- f$edit(shared_proc,"upcase"),shared_proc) $ write bopt f$fao("!AS=PROCEDURE,-",shared_proc) $ endif $ goto map_loop $MAP_END: $ close/nolog aopt $ close/nolog bopt $ open/append libopt 'p2' $ open/read aopt a.opt $ open/read bopt b.opt $ALOOP: $ read/end=aloop_end aopt line $ write libopt line $ goto aloop $ALOOP_END: $ close/nolog aopt $ sv = "" $BLOOP: $ read/end=bloop_end bopt svn $ if (svn.nes."") $ then $ if (sv.nes."") then write libopt sv $ sv = svn $ endif $ goto bloop $BLOOP_END: $ write libopt f$extract(0,f$length(sv)-2,sv), "-" $ write libopt ")" $ close/nolog bopt $ delete/nolog/noconf a.opt;*,b.opt;* $ else $ if vax $ then $ open/append libopt 'p2' $ mod_sym_num = 1 $ VMOD_SYM_LOOP: $ if f$type(module'mod_sym_num') .nes. "" $ then $ mod_in = 0 $ VMOD_SYM_IN: $ shared_proc = f$element(mod_in, "#", module'mod_sym_num') $ if shared_proc .nes. "#" $ then $ write libopt f$fao("UNIVERSAL=!AS",- f$edit(shared_proc,"upcase")) $ mod_in = mod_in + 1 $ goto vmod_sym_in $ endif $ mod_sym_num = mod_sym_num + 1 $ goto vmod_sym_loop $ endif $VMAP_LOOP: $ read/end=vmap_end map line $ if (f$locate("{",line).lt. f$length(line)) .or. - (f$locate("global:", line) .lt. f$length(line)) $ then $ proc = true $ goto vmap_loop $ endif $ if f$locate("}",line).lt. f$length(line) then proc = false $ if f$locate("local:", line) .lt. f$length(line) then proc = false $ if proc $ then $ shared_proc = f$edit(line,"collapse") $ chop_semi = f$locate(";", shared_proc) $ if chop_semi .lt. f$length(shared_proc) then - shared_proc = f$extract(0, chop_semi, shared_proc) $ write libopt f$fao("UNIVERSAL=!AS",- f$edit(shared_proc,"upcase")) $ endif $ goto vmap_loop $VMAP_END: $ else $ write sys$output "Unknown Architecture (Not VAX, AXP, or IA64)" $ write sys$output "No options file created" $ endif $ endif $ EXIT_M2S: $ close/nolog map $ close/nolog libopt $ endsubroutine |
Added compat/zlib/msdos/Makefile.bor.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib # Borland C++ # Last updated: 15-Mar-2003 # To use, do "make -fmakefile.bor" # To compile in small model, set below: MODEL=s # WARNING: the small model is supported but only for small values of # MAX_WBITS and MAX_MEM_LEVEL. For example: # -DMAX_WBITS=11 -DDEF_WBITS=11 -DMAX_MEM_LEVEL=3 # If you wish to reduce the memory requirements (default 256K for big # objects plus a few K), you can add to the LOC macro below: # -DMAX_MEM_LEVEL=7 -DMAX_WBITS=14 # See zconf.h for details about the memory requirements. # ------------ Turbo C++, Borland C++ ------------ # Optional nonstandard preprocessor flags (e.g. -DMAX_MEM_LEVEL=7) # should be added to the environment via "set LOCAL_ZLIB=-DFOO" or added # to the declaration of LOC here: LOC = $(LOCAL_ZLIB) # type for CPU required: 0: 8086, 1: 80186, 2: 80286, 3: 80386, etc. CPU_TYP = 0 # memory model: one of s, m, c, l (small, medium, compact, large) MODEL=l # replace bcc with tcc for Turbo C++ 1.0, with bcc32 for the 32 bit version CC=bcc LD=bcc AR=tlib # compiler flags # replace "-O2" by "-O -G -a -d" for Turbo C++ 1.0 CFLAGS=-O2 -Z -m$(MODEL) $(LOC) LDFLAGS=-m$(MODEL) -f- # variables ZLIB_LIB = zlib_$(MODEL).lib OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj # targets all: $(ZLIB_LIB) example.exe minigzip.exe .c.obj: $(CC) -c $(CFLAGS) $*.c adler32.obj: adler32.c zlib.h zconf.h compress.obj: compress.c zlib.h zconf.h crc32.obj: crc32.c zlib.h zconf.h crc32.h deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h gzread.obj: gzread.c zlib.h zconf.h gzguts.h gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h uncompr.obj: uncompr.c zlib.h zconf.h zutil.obj: zutil.c zutil.h zlib.h zconf.h example.obj: test/example.c zlib.h zconf.h minigzip.obj: test/minigzip.c zlib.h zconf.h # the command line is cut to fit in the MS-DOS 128 byte limit: $(ZLIB_LIB): $(OBJ1) $(OBJ2) -del $(ZLIB_LIB) $(AR) $(ZLIB_LIB) $(OBJP1) $(AR) $(ZLIB_LIB) $(OBJP2) example.exe: example.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) example.obj $(ZLIB_LIB) minigzip.exe: minigzip.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB) test: example.exe minigzip.exe example echo hello world | minigzip | minigzip -d clean: -del *.obj -del *.lib -del *.exe -del zlib_*.bak -del foo.gz |
Added compat/zlib/msdos/Makefile.dj2.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib. Modified for djgpp v2.0 by F. J. Donahoe, 3/15/96. # Copyright (C) 1995-1998 Jean-loup Gailly. # For conditions of distribution and use, see copyright notice in zlib.h # To compile, or to compile and test, type: # # make -fmakefile.dj2; make test -fmakefile.dj2 # # To install libz.a, zconf.h and zlib.h in the djgpp directories, type: # # make install -fmakefile.dj2 # # after first defining LIBRARY_PATH and INCLUDE_PATH in djgpp.env as # in the sample below if the pattern of the DJGPP distribution is to # be followed. Remember that, while <sp>'es around <=> are ignored in # makefiles, they are *not* in batch files or in djgpp.env. # - - - - - # [make] # INCLUDE_PATH=%\>;INCLUDE_PATH%%\DJDIR%\include # LIBRARY_PATH=%\>;LIBRARY_PATH%%\DJDIR%\lib # BUTT=-m486 # - - - - - # Alternately, these variables may be defined below, overriding the values # in djgpp.env, as # INCLUDE_PATH=c:\usr\include # LIBRARY_PATH=c:\usr\lib CC=gcc #CFLAGS=-MMD -O #CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 #CFLAGS=-MMD -g -DZLIB_DEBUG CFLAGS=-MMD -O3 $(BUTT) -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ -Wstrict-prototypes -Wmissing-prototypes # If cp.exe is available, replace "copy /Y" with "cp -fp" . CP=copy /Y # If gnu install.exe is available, replace $(CP) with ginstall. INSTALL=$(CP) # The default value of RM is "rm -f." If "rm.exe" is found, comment out: RM=del LDLIBS=-L. -lz LD=$(CC) -s -o LDSHARED=$(CC) INCL=zlib.h zconf.h LIBS=libz.a AR=ar rcs prefix=/usr/local exec_prefix = $(prefix) OBJS = adler32.o compress.o crc32.o gzclose.o gzlib.o gzread.o gzwrite.o \ uncompr.o deflate.o trees.o zutil.o inflate.o infback.o inftrees.o inffast.o OBJA = # to use the asm code: make OBJA=match.o TEST_OBJS = example.o minigzip.o all: example.exe minigzip.exe check: test test: all ./example echo hello world | .\minigzip | .\minigzip -d %.o : %.c $(CC) $(CFLAGS) -c $< -o $@ libz.a: $(OBJS) $(OBJA) $(AR) $@ $(OBJS) $(OBJA) %.exe : %.o $(LIBS) $(LD) $@ $< $(LDLIBS) # INCLUDE_PATH and LIBRARY_PATH were set for [make] in djgpp.env . .PHONY : uninstall clean install: $(INCL) $(LIBS) -@if not exist $(INCLUDE_PATH)\nul mkdir $(INCLUDE_PATH) -@if not exist $(LIBRARY_PATH)\nul mkdir $(LIBRARY_PATH) $(INSTALL) zlib.h $(INCLUDE_PATH) $(INSTALL) zconf.h $(INCLUDE_PATH) $(INSTALL) libz.a $(LIBRARY_PATH) uninstall: $(RM) $(INCLUDE_PATH)\zlib.h $(RM) $(INCLUDE_PATH)\zconf.h $(RM) $(LIBRARY_PATH)\libz.a clean: $(RM) *.d $(RM) *.o $(RM) *.exe $(RM) libz.a $(RM) foo.gz DEPS := $(wildcard *.d) ifneq ($(DEPS),) include $(DEPS) endif |
Added compat/zlib/msdos/Makefile.emx.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib. Modified for emx 0.9c by Chr. Spieler, 6/17/98. # Copyright (C) 1995-1998 Jean-loup Gailly. # For conditions of distribution and use, see copyright notice in zlib.h # To compile, or to compile and test, type: # # make -fmakefile.emx; make test -fmakefile.emx # CC=gcc #CFLAGS=-MMD -O #CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 #CFLAGS=-MMD -g -DZLIB_DEBUG CFLAGS=-MMD -O3 $(BUTT) -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ -Wstrict-prototypes -Wmissing-prototypes # If cp.exe is available, replace "copy /Y" with "cp -fp" . CP=copy /Y # If gnu install.exe is available, replace $(CP) with ginstall. INSTALL=$(CP) # The default value of RM is "rm -f." If "rm.exe" is found, comment out: RM=del LDLIBS=-L. -lzlib LD=$(CC) -s -o LDSHARED=$(CC) INCL=zlib.h zconf.h LIBS=zlib.a AR=ar rcs prefix=/usr/local exec_prefix = $(prefix) OBJS = adler32.o compress.o crc32.o gzclose.o gzlib.o gzread.o gzwrite.o \ uncompr.o deflate.o trees.o zutil.o inflate.o infback.o inftrees.o inffast.o TEST_OBJS = example.o minigzip.o all: example.exe minigzip.exe test: all ./example echo hello world | .\minigzip | .\minigzip -d %.o : %.c $(CC) $(CFLAGS) -c $< -o $@ zlib.a: $(OBJS) $(AR) $@ $(OBJS) %.exe : %.o $(LIBS) $(LD) $@ $< $(LDLIBS) .PHONY : clean clean: $(RM) *.d $(RM) *.o $(RM) *.exe $(RM) zlib.a $(RM) foo.gz DEPS := $(wildcard *.d) ifneq ($(DEPS),) include $(DEPS) endif |
Added compat/zlib/msdos/Makefile.msc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib # Microsoft C 5.1 or later # Last updated: 19-Mar-2003 # To use, do "make makefile.msc" # To compile in small model, set below: MODEL=S # If you wish to reduce the memory requirements (default 256K for big # objects plus a few K), you can add to the LOC macro below: # -DMAX_MEM_LEVEL=7 -DMAX_WBITS=14 # See zconf.h for details about the memory requirements. # ------------- Microsoft C 5.1 and later ------------- # Optional nonstandard preprocessor flags (e.g. -DMAX_MEM_LEVEL=7) # should be added to the environment via "set LOCAL_ZLIB=-DFOO" or added # to the declaration of LOC here: LOC = $(LOCAL_ZLIB) # Type for CPU required: 0: 8086, 1: 80186, 2: 80286, 3: 80386, etc. CPU_TYP = 0 # Memory model: one of S, M, C, L (small, medium, compact, large) MODEL=L CC=cl CFLAGS=-nologo -A$(MODEL) -G$(CPU_TYP) -W3 -Oait -Gs $(LOC) #-Ox generates bad code with MSC 5.1 LIB_CFLAGS=-Zl $(CFLAGS) LD=link LDFLAGS=/noi/e/st:0x1500/noe/farcall/packcode # "/farcall/packcode" are only useful for `large code' memory models # but should be a "no-op" for small code models. # variables ZLIB_LIB = zlib_$(MODEL).lib OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj # targets all: $(ZLIB_LIB) example.exe minigzip.exe .c.obj: $(CC) -c $(LIB_CFLAGS) $*.c adler32.obj: adler32.c zlib.h zconf.h compress.obj: compress.c zlib.h zconf.h crc32.obj: crc32.c zlib.h zconf.h crc32.h deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h gzread.obj: gzread.c zlib.h zconf.h gzguts.h gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h uncompr.obj: uncompr.c zlib.h zconf.h zutil.obj: zutil.c zutil.h zlib.h zconf.h example.obj: test/example.c zlib.h zconf.h $(CC) -c $(CFLAGS) $*.c minigzip.obj: test/minigzip.c zlib.h zconf.h $(CC) -c $(CFLAGS) $*.c # the command line is cut to fit in the MS-DOS 128 byte limit: $(ZLIB_LIB): $(OBJ1) $(OBJ2) if exist $(ZLIB_LIB) del $(ZLIB_LIB) lib $(ZLIB_LIB) $(OBJ1); lib $(ZLIB_LIB) $(OBJ2); example.exe: example.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) example.obj,,,$(ZLIB_LIB); minigzip.exe: minigzip.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) minigzip.obj,,,$(ZLIB_LIB); test: example.exe minigzip.exe example echo hello world | minigzip | minigzip -d clean: -del *.obj -del *.lib -del *.exe -del *.map -del zlib_*.bak -del foo.gz |
Added compat/zlib/msdos/Makefile.tc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib # Turbo C 2.01, Turbo C++ 1.01 # Last updated: 15-Mar-2003 # To use, do "make -fmakefile.tc" # To compile in small model, set below: MODEL=s # WARNING: the small model is supported but only for small values of # MAX_WBITS and MAX_MEM_LEVEL. For example: # -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3 # If you wish to reduce the memory requirements (default 256K for big # objects plus a few K), you can add to CFLAGS below: # -DMAX_MEM_LEVEL=7 -DMAX_WBITS=14 # See zconf.h for details about the memory requirements. # ------------ Turbo C 2.01, Turbo C++ 1.01 ------------ MODEL=l CC=tcc LD=tcc AR=tlib # CFLAGS=-O2 -G -Z -m$(MODEL) -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3 CFLAGS=-O2 -G -Z -m$(MODEL) LDFLAGS=-m$(MODEL) -f- # variables ZLIB_LIB = zlib_$(MODEL).lib OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj # targets all: $(ZLIB_LIB) example.exe minigzip.exe .c.obj: $(CC) -c $(CFLAGS) $*.c adler32.obj: adler32.c zlib.h zconf.h compress.obj: compress.c zlib.h zconf.h crc32.obj: crc32.c zlib.h zconf.h crc32.h deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h gzread.obj: gzread.c zlib.h zconf.h gzguts.h gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h uncompr.obj: uncompr.c zlib.h zconf.h zutil.obj: zutil.c zutil.h zlib.h zconf.h example.obj: test/example.c zlib.h zconf.h minigzip.obj: test/minigzip.c zlib.h zconf.h # the command line is cut to fit in the MS-DOS 128 byte limit: $(ZLIB_LIB): $(OBJ1) $(OBJ2) -del $(ZLIB_LIB) $(AR) $(ZLIB_LIB) $(OBJP1) $(AR) $(ZLIB_LIB) $(OBJP2) example.exe: example.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) example.obj $(ZLIB_LIB) minigzip.exe: minigzip.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB) test: example.exe minigzip.exe example echo hello world | minigzip | minigzip -d clean: -del *.obj -del *.lib -del *.exe -del zlib_*.bak -del foo.gz |
Added compat/zlib/nintendods/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 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 | #--------------------------------------------------------------------------------- .SUFFIXES: #--------------------------------------------------------------------------------- ifeq ($(strip $(DEVKITARM)),) $(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM") endif include $(DEVKITARM)/ds_rules #--------------------------------------------------------------------------------- # TARGET is the name of the output # BUILD is the directory where object files & intermediate files will be placed # SOURCES is a list of directories containing source code # DATA is a list of directories containing data files # INCLUDES is a list of directories containing header files #--------------------------------------------------------------------------------- TARGET := $(shell basename $(CURDIR)) BUILD := build SOURCES := ../../ DATA := data INCLUDES := include #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- ARCH := -mthumb -mthumb-interwork CFLAGS := -Wall -O2\ -march=armv5te -mtune=arm946e-s \ -fomit-frame-pointer -ffast-math \ $(ARCH) CFLAGS += $(INCLUDE) -DARM9 CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions ASFLAGS := $(ARCH) -march=armv5te -mtune=arm946e-s LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing # include and lib #--------------------------------------------------------------------------------- LIBDIRS := $(LIBNDS) #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional # rules for different file extensions #--------------------------------------------------------------------------------- ifneq ($(BUILD),$(notdir $(CURDIR))) #--------------------------------------------------------------------------------- export OUTPUT := $(CURDIR)/lib/libz.a export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) export DEPSDIR := $(CURDIR)/$(BUILD) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C #--------------------------------------------------------------------------------- ifeq ($(strip $(CPPFILES)),) #--------------------------------------------------------------------------------- export LD := $(CC) #--------------------------------------------------------------------------------- else #--------------------------------------------------------------------------------- export LD := $(CXX) #--------------------------------------------------------------------------------- endif #--------------------------------------------------------------------------------- export OFILES := $(addsuffix .o,$(BINFILES)) \ $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I$(CURDIR)/$(BUILD) .PHONY: $(BUILD) clean all #--------------------------------------------------------------------------------- all: $(BUILD) @[ -d $@ ] || mkdir -p include @cp ../../*.h include lib: @[ -d $@ ] || mkdir -p $@ $(BUILD): lib @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile #--------------------------------------------------------------------------------- clean: @echo clean ... @rm -fr $(BUILD) lib #--------------------------------------------------------------------------------- else DEPENDS := $(OFILES:.o=.d) #--------------------------------------------------------------------------------- # main targets #--------------------------------------------------------------------------------- $(OUTPUT) : $(OFILES) #--------------------------------------------------------------------------------- %.bin.o : %.bin #--------------------------------------------------------------------------------- @echo $(notdir $<) @$(bin2o) -include $(DEPENDS) #--------------------------------------------------------------------------------------- endif #--------------------------------------------------------------------------------------- |
Added compat/zlib/nintendods/README.
> > > > > | 1 2 3 4 5 | This Makefile requires devkitARM (http://www.devkitpro.org/category/devkitarm/) and works inside "contrib/nds". It is based on a devkitARM template. Eduardo Costa <eduardo.m.costa@gmail.com> January 3, 2009 |
Added compat/zlib/old/Makefile.emx.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib. Modified for emx/rsxnt by Chr. Spieler, 6/16/98. # Copyright (C) 1995-1998 Jean-loup Gailly. # For conditions of distribution and use, see copyright notice in zlib.h # To compile, or to compile and test, type: # # make -fmakefile.emx; make test -fmakefile.emx # CC=gcc -Zwin32 #CFLAGS=-MMD -O #CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 #CFLAGS=-MMD -g -DZLIB_DEBUG CFLAGS=-MMD -O3 $(BUTT) -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ -Wstrict-prototypes -Wmissing-prototypes # If cp.exe is available, replace "copy /Y" with "cp -fp" . CP=copy /Y # If gnu install.exe is available, replace $(CP) with ginstall. INSTALL=$(CP) # The default value of RM is "rm -f." If "rm.exe" is found, comment out: RM=del LDLIBS=-L. -lzlib LD=$(CC) -s -o LDSHARED=$(CC) INCL=zlib.h zconf.h LIBS=zlib.a AR=ar rcs prefix=/usr/local exec_prefix = $(prefix) OBJS = adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o \ gzwrite.o infback.o inffast.o inflate.o inftrees.o trees.o uncompr.o zutil.o TEST_OBJS = example.o minigzip.o all: example.exe minigzip.exe test: all ./example echo hello world | .\minigzip | .\minigzip -d %.o : %.c $(CC) $(CFLAGS) -c $< -o $@ zlib.a: $(OBJS) $(AR) $@ $(OBJS) %.exe : %.o $(LIBS) $(LD) $@ $< $(LDLIBS) .PHONY : clean clean: $(RM) *.d $(RM) *.o $(RM) *.exe $(RM) zlib.a $(RM) foo.gz DEPS := $(wildcard *.d) ifneq ($(DEPS),) include $(DEPS) endif |
Added compat/zlib/old/Makefile.riscos.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Project: zlib_1_03 # Patched for zlib 1.1.2 rw@shadow.org.uk 19980430 # test works out-of-the-box, installs `somewhere' on demand # Toolflags: CCflags = -c -depend !Depend -IC: -g -throwback -DRISCOS -fah C++flags = -c -depend !Depend -IC: -throwback Linkflags = -aif -c++ -o $@ ObjAsmflags = -throwback -NoCache -depend !Depend CMHGflags = LibFileflags = -c -l -o $@ Squeezeflags = -o $@ # change the line below to where _you_ want the library installed. libdest = lib:zlib # Final targets: @.lib: @.o.adler32 @.o.compress @.o.crc32 @.o.deflate @.o.gzio \ @.o.infblock @.o.infcodes @.o.inffast @.o.inflate @.o.inftrees @.o.infutil @.o.trees \ @.o.uncompr @.o.zutil LibFile $(LibFileflags) @.o.adler32 @.o.compress @.o.crc32 @.o.deflate \ @.o.gzio @.o.infblock @.o.infcodes @.o.inffast @.o.inflate @.o.inftrees @.o.infutil \ @.o.trees @.o.uncompr @.o.zutil test: @.minigzip @.example @.lib @copy @.lib @.libc A~C~DF~L~N~P~Q~RS~TV @echo running tests: hang on. @/@.minigzip -f -9 libc @/@.minigzip -d libc-gz @/@.minigzip -f -1 libc @/@.minigzip -d libc-gz @/@.minigzip -h -9 libc @/@.minigzip -d libc-gz @/@.minigzip -h -1 libc @/@.minigzip -d libc-gz @/@.minigzip -9 libc @/@.minigzip -d libc-gz @/@.minigzip -1 libc @/@.minigzip -d libc-gz @diff @.lib @.libc @echo that should have reported '@.lib and @.libc identical' if you have diff. @/@.example @.fred @.fred @echo that will have given lots of hello!'s. @.minigzip: @.o.minigzip @.lib C:o.Stubs Link $(Linkflags) @.o.minigzip @.lib C:o.Stubs @.example: @.o.example @.lib C:o.Stubs Link $(Linkflags) @.o.example @.lib C:o.Stubs install: @.lib cdir $(libdest) cdir $(libdest).h @copy @.h.zlib $(libdest).h.zlib A~C~DF~L~N~P~Q~RS~TV @copy @.h.zconf $(libdest).h.zconf A~C~DF~L~N~P~Q~RS~TV @copy @.lib $(libdest).lib A~C~DF~L~N~P~Q~RS~TV @echo okay, installed zlib in $(libdest) clean:; remove @.minigzip remove @.example remove @.libc -wipe @.o.* F~r~cV remove @.fred # User-editable dependencies: .c.o: cc $(ccflags) -o $@ $< # Static dependencies: # Dynamic dependencies: o.example: c.example o.example: h.zlib o.example: h.zconf o.minigzip: c.minigzip o.minigzip: h.zlib o.minigzip: h.zconf o.adler32: c.adler32 o.adler32: h.zlib o.adler32: h.zconf o.compress: c.compress o.compress: h.zlib o.compress: h.zconf o.crc32: c.crc32 o.crc32: h.zlib o.crc32: h.zconf o.deflate: c.deflate o.deflate: h.deflate o.deflate: h.zutil o.deflate: h.zlib o.deflate: h.zconf o.gzio: c.gzio o.gzio: h.zutil o.gzio: h.zlib o.gzio: h.zconf o.infblock: c.infblock o.infblock: h.zutil o.infblock: h.zlib o.infblock: h.zconf o.infblock: h.infblock o.infblock: h.inftrees o.infblock: h.infcodes o.infblock: h.infutil o.infcodes: c.infcodes o.infcodes: h.zutil o.infcodes: h.zlib o.infcodes: h.zconf o.infcodes: h.inftrees o.infcodes: h.infblock o.infcodes: h.infcodes o.infcodes: h.infutil o.infcodes: h.inffast o.inffast: c.inffast o.inffast: h.zutil o.inffast: h.zlib o.inffast: h.zconf o.inffast: h.inftrees o.inffast: h.infblock o.inffast: h.infcodes o.inffast: h.infutil o.inffast: h.inffast o.inflate: c.inflate o.inflate: h.zutil o.inflate: h.zlib o.inflate: h.zconf o.inflate: h.infblock o.inftrees: c.inftrees o.inftrees: h.zutil o.inftrees: h.zlib o.inftrees: h.zconf o.inftrees: h.inftrees o.inftrees: h.inffixed o.infutil: c.infutil o.infutil: h.zutil o.infutil: h.zlib o.infutil: h.zconf o.infutil: h.infblock o.infutil: h.inftrees o.infutil: h.infcodes o.infutil: h.infutil o.trees: c.trees o.trees: h.deflate o.trees: h.zutil o.trees: h.zlib o.trees: h.zconf o.trees: h.trees o.uncompr: c.uncompr o.uncompr: h.zlib o.uncompr: h.zconf o.zutil: c.zutil o.zutil: h.zutil o.zutil: h.zlib o.zutil: h.zconf |
Added compat/zlib/old/README.
> > > | 1 2 3 | This directory contains files that have not been updated for zlib 1.2.x (Volunteers are encouraged to help clean this up. Thanks.) |
Added compat/zlib/old/descrip.mms.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # descrip.mms: MMS description file for building zlib on VMS # written by Martin P.J. Zinser <m.zinser@gsi.de> cc_defs = c_deb = .ifdef __DECC__ pref = /prefix=all .endif OBJS = adler32.obj, compress.obj, crc32.obj, gzio.obj, uncompr.obj,\ deflate.obj, trees.obj, zutil.obj, inflate.obj, infblock.obj,\ inftrees.obj, infcodes.obj, infutil.obj, inffast.obj CFLAGS= $(C_DEB) $(CC_DEFS) $(PREF) all : example.exe minigzip.exe @ write sys$output " Example applications available" libz.olb : libz.olb($(OBJS)) @ write sys$output " libz available" example.exe : example.obj libz.olb link example,libz.olb/lib minigzip.exe : minigzip.obj libz.olb link minigzip,libz.olb/lib,x11vms:xvmsutils.olb/lib clean : delete *.obj;*,libz.olb;* # Other dependencies. adler32.obj : zutil.h zlib.h zconf.h compress.obj : zlib.h zconf.h crc32.obj : zutil.h zlib.h zconf.h deflate.obj : deflate.h zutil.h zlib.h zconf.h example.obj : zlib.h zconf.h gzio.obj : zutil.h zlib.h zconf.h infblock.obj : zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h infcodes.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h infcodes.h inffast.h inffast.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h inffast.h inflate.obj : zutil.h zlib.h zconf.h infblock.h inftrees.obj : zutil.h zlib.h zconf.h inftrees.h infutil.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h minigzip.obj : zlib.h zconf.h trees.obj : deflate.h zutil.h zlib.h zconf.h uncompr.obj : zlib.h zconf.h zutil.obj : zutil.h zlib.h zconf.h |
Added compat/zlib/old/os2/Makefile.os2.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib under OS/2 using GCC (PGCC) # For conditions of distribution and use, see copyright notice in zlib.h # To compile and test, type: # cp Makefile.os2 .. # cd .. # make -f Makefile.os2 test # This makefile will build a static library z.lib, a shared library # z.dll and a import library zdll.lib. You can use either z.lib or # zdll.lib by specifying either -lz or -lzdll on gcc's command line CC=gcc -Zomf -s CFLAGS=-O6 -Wall #CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 #CFLAGS=-g -DZLIB_DEBUG #CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ # -Wstrict-prototypes -Wmissing-prototypes #################### BUG WARNING: ##################### ## infcodes.c hits a bug in pgcc-1.0, so you have to use either ## -O# where # <= 4 or one of (-fno-ommit-frame-pointer or -fno-force-mem) ## This bug is reportedly fixed in pgcc >1.0, but this was not tested CFLAGS+=-fno-force-mem LDFLAGS=-s -L. -lzdll -Zcrtdll LDSHARED=$(CC) -s -Zomf -Zdll -Zcrtdll VER=1.1.0 ZLIB=z.lib SHAREDLIB=z.dll SHAREDLIBIMP=zdll.lib LIBS=$(ZLIB) $(SHAREDLIB) $(SHAREDLIBIMP) AR=emxomfar cr IMPLIB=emximp RANLIB=echo TAR=tar SHELL=bash prefix=/usr/local exec_prefix = $(prefix) OBJS = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \ zutil.o inflate.o infblock.o inftrees.o infcodes.o infutil.o inffast.o TEST_OBJS = example.o minigzip.o DISTFILES = README INDEX ChangeLog configure Make*[a-z0-9] *.[ch] descrip.mms \ algorithm.txt zlib.3 msdos/Make*[a-z0-9] msdos/zlib.def msdos/zlib.rc \ nt/Makefile.nt nt/zlib.dnt contrib/README.contrib contrib/*.txt \ contrib/asm386/*.asm contrib/asm386/*.c \ contrib/asm386/*.bat contrib/asm386/zlibvc.d?? contrib/iostream/*.cpp \ contrib/iostream/*.h contrib/iostream2/*.h contrib/iostream2/*.cpp \ contrib/untgz/Makefile contrib/untgz/*.c contrib/untgz/*.w32 all: example.exe minigzip.exe test: all @LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \ echo hello world | ./minigzip | ./minigzip -d || \ echo ' *** minigzip test FAILED ***' ; \ if ./example; then \ echo ' *** zlib test OK ***'; \ else \ echo ' *** zlib test FAILED ***'; \ fi $(ZLIB): $(OBJS) $(AR) $@ $(OBJS) -@ ($(RANLIB) $@ || true) >/dev/null 2>&1 $(SHAREDLIB): $(OBJS) os2/z.def $(LDSHARED) -o $@ $^ $(SHAREDLIBIMP): os2/z.def $(IMPLIB) -o $@ $^ example.exe: example.o $(LIBS) $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) minigzip.exe: minigzip.o $(LIBS) $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS) clean: rm -f *.o *~ example minigzip libz.a libz.so* foo.gz distclean: clean zip: mv Makefile Makefile~; cp -p Makefile.in Makefile rm -f test.c ztest*.c v=`sed -n -e 's/\.//g' -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`;\ zip -ul9 zlib$$v $(DISTFILES) mv Makefile~ Makefile dist: mv Makefile Makefile~; cp -p Makefile.in Makefile rm -f test.c ztest*.c d=zlib-`sed -n '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`;\ rm -f $$d.tar.gz; \ if test ! -d ../$$d; then rm -f ../$$d; ln -s `pwd` ../$$d; fi; \ files=""; \ for f in $(DISTFILES); do files="$$files $$d/$$f"; done; \ cd ..; \ GZIP=-9 $(TAR) chofz $$d/$$d.tar.gz $$files; \ if test ! -d $$d; then rm -f $$d; fi mv Makefile~ Makefile tags: etags *.[ch] depend: makedepend -- $(CFLAGS) -- *.[ch] # DO NOT DELETE THIS LINE -- make depend depends on it. adler32.o: zlib.h zconf.h compress.o: zlib.h zconf.h crc32.o: zlib.h zconf.h deflate.o: deflate.h zutil.h zlib.h zconf.h example.o: zlib.h zconf.h gzio.o: zutil.h zlib.h zconf.h infblock.o: infblock.h inftrees.h infcodes.h infutil.h zutil.h zlib.h zconf.h infcodes.o: zutil.h zlib.h zconf.h infcodes.o: inftrees.h infblock.h infcodes.h infutil.h inffast.h inffast.o: zutil.h zlib.h zconf.h inftrees.h inffast.o: infblock.h infcodes.h infutil.h inffast.h inflate.o: zutil.h zlib.h zconf.h infblock.h inftrees.o: zutil.h zlib.h zconf.h inftrees.h infutil.o: zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h minigzip.o: zlib.h zconf.h trees.o: deflate.h zutil.h zlib.h zconf.h trees.h uncompr.o: zlib.h zconf.h zutil.o: zutil.h zlib.h zconf.h |
Added compat/zlib/old/os2/zlib.def.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ; ; Slightly modified version of ../nt/zlib.dnt :-) ; LIBRARY Z DESCRIPTION "Zlib compression library for OS/2" CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE MULTIPLE EXPORTS adler32 compress crc32 deflate deflateCopy deflateEnd deflateInit2_ deflateInit_ deflateParams deflateReset deflateSetDictionary gzclose gzdopen gzerror gzflush gzopen gzread gzwrite inflate inflateEnd inflateInit2_ inflateInit_ inflateReset inflateSetDictionary inflateSync uncompress zlibVersion gzprintf gzputc gzgetc gzseek gzrewind gztell gzeof gzsetparams zError inflateSyncPoint get_crc_table compress2 gzputs gzgets |
Added compat/zlib/old/visual-basic.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 | See below some functions declarations for Visual Basic. Frequently Asked Question: Q: Each time I use the compress function I get the -5 error (not enough room in the output buffer). A: Make sure that the length of the compressed buffer is passed by reference ("as any"), not by value ("as long"). Also check that before the call of compress this length is equal to the total size of the compressed buffer and not zero. From: "Jon Caruana" <jon-net@usa.net> Subject: Re: How to port zlib declares to vb? Date: Mon, 28 Oct 1996 18:33:03 -0600 Got the answer! (I haven't had time to check this but it's what I got, and looks correct): He has the following routines working: compress uncompress gzopen gzwrite gzread gzclose Declares follow: (Quoted from Carlos Rios <c_rios@sonda.cl>, in Vb4 form) #If Win16 Then 'Use Win16 calls. Declare Function compress Lib "ZLIB.DLL" (ByVal compr As String, comprLen As Any, ByVal buf As String, ByVal buflen As Long) As Integer Declare Function uncompress Lib "ZLIB.DLL" (ByVal uncompr As String, uncomprLen As Any, ByVal compr As String, ByVal lcompr As Long) As Integer Declare Function gzopen Lib "ZLIB.DLL" (ByVal filePath As String, ByVal mode As String) As Long Declare Function gzread Lib "ZLIB.DLL" (ByVal file As Long, ByVal uncompr As String, ByVal uncomprLen As Integer) As Integer Declare Function gzwrite Lib "ZLIB.DLL" (ByVal file As Long, ByVal uncompr As String, ByVal uncomprLen As Integer) As Integer Declare Function gzclose Lib "ZLIB.DLL" (ByVal file As Long) As Integer #Else Declare Function compress Lib "ZLIB32.DLL" (ByVal compr As String, comprLen As Any, ByVal buf As String, ByVal buflen As Long) As Integer Declare Function uncompress Lib "ZLIB32.DLL" (ByVal uncompr As String, uncomprLen As Any, ByVal compr As String, ByVal lcompr As Long) As Long Declare Function gzopen Lib "ZLIB32.DLL" (ByVal file As String, ByVal mode As String) As Long Declare Function gzread Lib "ZLIB32.DLL" (ByVal file As Long, ByVal uncompr As String, ByVal uncomprLen As Long) As Long Declare Function gzwrite Lib "ZLIB32.DLL" (ByVal file As Long, ByVal uncompr As String, ByVal uncomprLen As Long) As Long Declare Function gzclose Lib "ZLIB32.DLL" (ByVal file As Long) As Long #End If -Jon Caruana jon-net@usa.net Microsoft Sitebuilder Network Level 1 Member - HTML Writer's Guild Member Here is another example from Michael <michael_borgsys@hotmail.com> that he says conforms to the VB guidelines, and that solves the problem of not knowing the uncompressed size by storing it at the end of the file: 'Calling the functions: 'bracket meaning: <parameter> [optional] {Range of possible values} 'Call subCompressFile(<path with filename to compress> [, <path with filename to write to>, [level of compression {1..9}]]) 'Call subUncompressFile(<path with filename to compress>) Option Explicit Private lngpvtPcnSml As Long 'Stores value for 'lngPercentSmaller' Private Const SUCCESS As Long = 0 Private Const strFilExt As String = ".cpr" Private Declare Function lngfncCpr Lib "zlib.dll" Alias "compress2" (ByRef dest As Any, ByRef destLen As Any, ByRef src As Any, ByVal srcLen As Long, ByVal level As Integer) As Long Private Declare Function lngfncUcp Lib "zlib.dll" Alias "uncompress" (ByRef dest As Any, ByRef destLen As Any, ByRef src As Any, ByVal srcLen As Long) As Long Public Sub subCompressFile(ByVal strargOriFilPth As String, Optional ByVal strargCprFilPth As String, Optional ByVal intLvl As Integer = 9) Dim strCprPth As String Dim lngOriSiz As Long Dim lngCprSiz As Long Dim bytaryOri() As Byte Dim bytaryCpr() As Byte lngOriSiz = FileLen(strargOriFilPth) ReDim bytaryOri(lngOriSiz - 1) Open strargOriFilPth For Binary Access Read As #1 Get #1, , bytaryOri() Close #1 strCprPth = IIf(strargCprFilPth = "", strargOriFilPth, strargCprFilPth) 'Select file path and name strCprPth = strCprPth & IIf(Right(strCprPth, Len(strFilExt)) = strFilExt, "", strFilExt) 'Add file extension if not exists lngCprSiz = (lngOriSiz * 1.01) + 12 'Compression needs temporary a bit more space then original file size ReDim bytaryCpr(lngCprSiz - 1) If lngfncCpr(bytaryCpr(0), lngCprSiz, bytaryOri(0), lngOriSiz, intLvl) = SUCCESS Then lngpvtPcnSml = (1# - (lngCprSiz / lngOriSiz)) * 100 ReDim Preserve bytaryCpr(lngCprSiz - 1) Open strCprPth For Binary Access Write As #1 Put #1, , bytaryCpr() Put #1, , lngOriSiz 'Add the original size value to the end (last 4 bytes) Close #1 Else MsgBox "Compression error" End If Erase bytaryCpr Erase bytaryOri End Sub Public Sub subUncompressFile(ByVal strargFilPth As String) Dim bytaryCpr() As Byte Dim bytaryOri() As Byte Dim lngOriSiz As Long Dim lngCprSiz As Long Dim strOriPth As String lngCprSiz = FileLen(strargFilPth) ReDim bytaryCpr(lngCprSiz - 1) Open strargFilPth For Binary Access Read As #1 Get #1, , bytaryCpr() Close #1 'Read the original file size value: lngOriSiz = bytaryCpr(lngCprSiz - 1) * (2 ^ 24) _ + bytaryCpr(lngCprSiz - 2) * (2 ^ 16) _ + bytaryCpr(lngCprSiz - 3) * (2 ^ 8) _ + bytaryCpr(lngCprSiz - 4) ReDim Preserve bytaryCpr(lngCprSiz - 5) 'Cut of the original size value ReDim bytaryOri(lngOriSiz - 1) If lngfncUcp(bytaryOri(0), lngOriSiz, bytaryCpr(0), lngCprSiz) = SUCCESS Then strOriPth = Left(strargFilPth, Len(strargFilPth) - Len(strFilExt)) Open strOriPth For Binary Access Write As #1 Put #1, , bytaryOri() Close #1 Else MsgBox "Uncompression error" End If Erase bytaryCpr Erase bytaryOri End Sub Public Property Get lngPercentSmaller() As Long lngPercentSmaller = lngpvtPcnSml End Property |
Added compat/zlib/os400/README400.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ZLIB version 1.3.1 for OS/400 installation instructions 1) Download and unpack the zlib tarball to some IFS directory. (i.e.: /path/to/the/zlib/ifs/source/directory) If the installed IFS command supports gzip format, this is straightforward, else you have to unpack first to some directory on a system supporting it, then move the whole directory to the IFS via the network (via SMB or FTP). 2) Edit the configuration parameters in the compilation script. EDTF STMF('/path/to/the/zlib/ifs/source/directory/os400/make.sh') Tune the parameters according to your needs if not matching the defaults. Save the file and exit after edition. 3) Enter qshell, then work in the zlib OS/400 specific directory. QSH cd /path/to/the/zlib/ifs/source/directory/os400 4) Compile and install sh make.sh The script will: - create the libraries, objects and IFS directories for the zlib environment, - compile all modules, - create a service program, - create a static and a dynamic binding directory, - install header files for C/C++ and for ILE/RPG, both for compilation in DB2 and IFS environments. That's all. Notes: For OS/400 ILE RPG programmers, a /copy member defining the ZLIB API prototypes for ILE RPG can be found in ZLIB/H(ZLIB.INC). In the ILE environment, the same definitions are available from file zlib.inc located in the same IFS include directory as the C/C++ header files. Please read comments in this member for more information. Remember that most foreign textual data are ASCII coded: this implementation does not handle conversion from/to ASCII, so text data code conversions must be done explicitly. Mainly for the reason above, always open zipped files in binary mode. |
Added compat/zlib/os400/bndsrc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('ZLIB') /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ /* Version 1.1.3 entry points. */ /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ EXPORT SYMBOL("adler32") EXPORT SYMBOL("compress") EXPORT SYMBOL("compress2") EXPORT SYMBOL("crc32") EXPORT SYMBOL("get_crc_table") EXPORT SYMBOL("deflate") EXPORT SYMBOL("deflateEnd") EXPORT SYMBOL("deflateSetDictionary") EXPORT SYMBOL("deflateCopy") EXPORT SYMBOL("deflateReset") EXPORT SYMBOL("deflateParams") EXPORT SYMBOL("deflatePrime") EXPORT SYMBOL("deflateInit_") EXPORT SYMBOL("deflateInit2_") EXPORT SYMBOL("gzopen") EXPORT SYMBOL("gzdopen") EXPORT SYMBOL("gzsetparams") EXPORT SYMBOL("gzread") EXPORT SYMBOL("gzwrite") EXPORT SYMBOL("gzprintf") EXPORT SYMBOL("gzputs") EXPORT SYMBOL("gzgets") EXPORT SYMBOL("gzputc") EXPORT SYMBOL("gzgetc") EXPORT SYMBOL("gzflush") EXPORT SYMBOL("gzseek") EXPORT SYMBOL("gzrewind") EXPORT SYMBOL("gztell") EXPORT SYMBOL("gzeof") EXPORT SYMBOL("gzclose") EXPORT SYMBOL("gzerror") EXPORT SYMBOL("inflate") EXPORT SYMBOL("inflateEnd") EXPORT SYMBOL("inflateSetDictionary") EXPORT SYMBOL("inflateSync") EXPORT SYMBOL("inflateReset") EXPORT SYMBOL("inflateInit_") EXPORT SYMBOL("inflateInit2_") EXPORT SYMBOL("inflateSyncPoint") EXPORT SYMBOL("uncompress") EXPORT SYMBOL("zlibVersion") EXPORT SYMBOL("zError") EXPORT SYMBOL("z_errmsg") /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ /* Version 1.2.1 additional entry points. */ /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ EXPORT SYMBOL("compressBound") EXPORT SYMBOL("deflateBound") EXPORT SYMBOL("deflatePending") EXPORT SYMBOL("gzungetc") EXPORT SYMBOL("gzclearerr") EXPORT SYMBOL("inflateBack") EXPORT SYMBOL("inflateBackEnd") EXPORT SYMBOL("inflateBackInit_") EXPORT SYMBOL("inflateCopy") EXPORT SYMBOL("zlibCompileFlags") /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ /* Version 1.2.4 additional entry points. */ /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ EXPORT SYMBOL("adler32_combine") EXPORT SYMBOL("adler32_combine64") EXPORT SYMBOL("crc32_combine") EXPORT SYMBOL("crc32_combine64") EXPORT SYMBOL("deflateSetHeader") EXPORT SYMBOL("deflateTune") EXPORT SYMBOL("gzbuffer") EXPORT SYMBOL("gzclose_r") EXPORT SYMBOL("gzclose_w") EXPORT SYMBOL("gzdirect") EXPORT SYMBOL("gzoffset") EXPORT SYMBOL("gzoffset64") EXPORT SYMBOL("gzopen64") EXPORT SYMBOL("gzseek64") EXPORT SYMBOL("gztell64") EXPORT SYMBOL("inflateGetHeader") EXPORT SYMBOL("inflateMark") EXPORT SYMBOL("inflatePrime") EXPORT SYMBOL("inflateReset2") EXPORT SYMBOL("inflateUndermine") /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ /* Version 1.2.6 additional entry points. */ /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ EXPORT SYMBOL("deflateResetKeep") EXPORT SYMBOL("gzgetc_") EXPORT SYMBOL("inflateResetKeep") /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ /* Version 1.2.8 additional entry points. */ /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ EXPORT SYMBOL("gzvprintf") EXPORT SYMBOL("inflateGetDictionary") /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ /* Version 1.2.9 additional entry points. */ /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ EXPORT SYMBOL("adler32_z") EXPORT SYMBOL("crc32_z") EXPORT SYMBOL("deflateGetDictionary") EXPORT SYMBOL("gzfread") EXPORT SYMBOL("gzfwrite") EXPORT SYMBOL("inflateCodesUsed") EXPORT SYMBOL("inflateValidate") EXPORT SYMBOL("uncompress2") /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ /* Version 1.2.12 additional entry points. */ /*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ EXPORT SYMBOL("crc32_combine_gen64") EXPORT SYMBOL("crc32_combine_gen") EXPORT SYMBOL("crc32_combine_op") ENDPGMEXP |
Added compat/zlib/os400/make.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 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 | #!/bin/sh # # ZLIB compilation script for the OS/400. # # # This is a shell script since make is not a standard component of OS/400. ################################################################################ # # Tunable configuration parameters. # ################################################################################ TARGETLIB='ZLIB' # Target OS/400 program library STATBNDDIR='ZLIB_A' # Static binding directory. DYNBNDDIR='ZLIB' # Dynamic binding directory. SRVPGM="ZLIB" # Service program. IFSDIR='/zlib' # IFS support base directory. TGTCCSID='500' # Target CCSID of objects DEBUG='*NONE' # Debug level OPTIMIZE='40' # Optimisation level OUTPUT='*NONE' # Compilation output option. TGTRLS='V6R1M0' # Target OS release export TARGETLIB STATBNDDIR DYNBNDDIR SRVPGM IFSDIR export TGTCCSID DEBUG OPTIMIZE OUTPUT TGTRLS ################################################################################ # # OS/400 specific definitions. # ################################################################################ LIBIFSNAME="/QSYS.LIB/${TARGETLIB}.LIB" ################################################################################ # # Procedures. # ################################################################################ # action_needed dest [src] # # dest is an object to build # if specified, src is an object on which dest depends. # # exit 0 (succeeds) if some action has to be taken, else 1. action_needed() { [ ! -e "${1}" ] && return 0 [ "${2}" ] || return 1 [ "${1}" -ot "${2}" ] && return 0 return 1 } # make_module module_name source_name [additional_definitions] # # Compile source name into module if needed. # As side effect, append the module name to variable MODULES. # Set LINK to "YES" if the module has been compiled. make_module() { MODULES="${MODULES} ${1}" MODIFSNAME="${LIBIFSNAME}/${1}.MODULE" CSRC="`basename \"${2}\"`" if action_needed "${MODIFSNAME}" "${2}" then : elif [ ! "`sed -e \"/<source name=\\\"${CSRC}\\\">/,/<\\\\/source>/!d\" \ -e '/<depend /!d' \ -e 's/.* name=\"\\([^\"]*\\)\".*/\\1/' < \"${TOPDIR}/treebuild.xml\" | while read HDR do if action_needed \"${MODIFSNAME}\" \"${IFSDIR}/include/${HDR}\" then echo recompile break fi done`" ] then return 0 fi CMD="CRTCMOD MODULE(${TARGETLIB}/${1}) SRCSTMF('${2}')" CMD="${CMD} SYSIFCOPT(*IFS64IO) OPTION(*INCDIRFIRST)" CMD="${CMD} LOCALETYPE(*LOCALE) FLAG(10)" CMD="${CMD} INCDIR('${IFSDIR}/include' ${INCLUDES})" CMD="${CMD} TGTCCSID(${TGTCCSID}) TGTRLS(${TGTRLS})" CMD="${CMD} OUTPUT(${OUTPUT})" CMD="${CMD} OPTIMIZE(${OPTIMIZE})" CMD="${CMD} DBGVIEW(${DEBUG})" system "${CMD}" LINK=YES } # Determine DB2 object name from IFS name. db2_name() { basename "${1}" | tr 'a-z-' 'A-Z_' | sed -e 's/\..*//' \ -e 's/^\(.\).*\(.........\)$/\1\2/' } # Force enumeration types to be the same size as integers. copy_hfile() { sed -e '1i\ #pragma enum(int)\ ' "${@}" -e '$a\ #pragma enum(pop)\ ' } ################################################################################ # # Script initialization. # ################################################################################ SCRIPTDIR=`dirname "${0}"` case "${SCRIPTDIR}" in /*) ;; *) SCRIPTDIR="`pwd`/${SCRIPTDIR}" esac while true do case "${SCRIPTDIR}" in */.) SCRIPTDIR="${SCRIPTDIR%/.}";; *) break;; esac done # The script directory is supposed to be in ${TOPDIR}/os400. TOPDIR=`dirname "${SCRIPTDIR}"` export SCRIPTDIR TOPDIR cd "${TOPDIR}" # Extract the version from the master compilation XML file. VERSION=`sed -e '/^<package /!d' \ -e 's/^.* version="\([0-9.]*\)".*$/\1/' -e 'q' \ < treebuild.xml` export VERSION ################################################################################ # Create the OS/400 library if it does not exist. if action_needed "${LIBIFSNAME}" then CMD="CRTLIB LIB(${TARGETLIB}) TEXT('ZLIB: Data compression API')" system "${CMD}" fi # Create the DOCS source file if it does not exist. if action_needed "${LIBIFSNAME}/DOCS.FILE" then CMD="CRTSRCPF FILE(${TARGETLIB}/DOCS) RCDLEN(112)" CMD="${CMD} CCSID(${TGTCCSID}) TEXT('Documentation texts')" system "${CMD}" fi # Copy some documentation files if needed. for TEXT in "${TOPDIR}/ChangeLog" "${TOPDIR}/FAQ" \ "${TOPDIR}/README" "${SCRIPTDIR}/README400" do MEMBER="${LIBIFSNAME}/DOCS.FILE/`db2_name \"${TEXT}\"`.MBR" if action_needed "${MEMBER}" "${TEXT}" then CMD="CPY OBJ('${TEXT}') TOOBJ('${MEMBER}') TOCCSID(${TGTCCSID})" CMD="${CMD} DTAFMT(*TEXT) REPLACE(*YES)" system "${CMD}" fi done # Create the OS/400 source program file for the C header files. SRCPF="${LIBIFSNAME}/H.FILE" if action_needed "${SRCPF}" then CMD="CRTSRCPF FILE(${TARGETLIB}/H) RCDLEN(112)" CMD="${CMD} CCSID(${TGTCCSID}) TEXT('ZLIB: C/C++ header files')" system "${CMD}" fi # Create the IFS directory for the C header files. if action_needed "${IFSDIR}/include" then mkdir -p "${IFSDIR}/include" fi # Copy the header files to DB2 library. Link from IFS include directory. for HFILE in "${TOPDIR}/"*.h do DEST="${SRCPF}/`db2_name \"${HFILE}\"`.MBR" if action_needed "${DEST}" "${HFILE}" then copy_hfile < "${HFILE}" > tmphdrfile # Need to translate to target CCSID. CMD="CPY OBJ('`pwd`/tmphdrfile') TOOBJ('${DEST}')" CMD="${CMD} TOCCSID(${TGTCCSID}) DTAFMT(*TEXT) REPLACE(*YES)" system "${CMD}" # touch -r "${HFILE}" "${DEST}" rm -f tmphdrfile fi IFSFILE="${IFSDIR}/include/`basename \"${HFILE}\"`" if action_needed "${IFSFILE}" "${DEST}" then rm -f "${IFSFILE}" ln -s "${DEST}" "${IFSFILE}" fi done # Install the ILE/RPG header file. HFILE="${SCRIPTDIR}/zlib.inc" DEST="${SRCPF}/ZLIB.INC.MBR" if action_needed "${DEST}" "${HFILE}" then CMD="CPY OBJ('${HFILE}') TOOBJ('${DEST}')" CMD="${CMD} TOCCSID(${TGTCCSID}) DTAFMT(*TEXT) REPLACE(*YES)" system "${CMD}" # touch -r "${HFILE}" "${DEST}" fi IFSFILE="${IFSDIR}/include/`basename \"${HFILE}\"`" if action_needed "${IFSFILE}" "${DEST}" then rm -f "${IFSFILE}" ln -s "${DEST}" "${IFSFILE}" fi # Create and compile the identification source file. echo '#pragma comment(user, "ZLIB version '"${VERSION}"'")' > os400.c echo '#pragma comment(user, __DATE__)' >> os400.c echo '#pragma comment(user, __TIME__)' >> os400.c echo '#pragma comment(copyright, "Copyright (C) 1995-2017 Jean-Loup Gailly, Mark Adler. OS/400 version by P. Monnerat.")' >> os400.c make_module OS400 os400.c LINK= # No need to rebuild service program yet. MODULES= # Get source list. CSOURCES=`sed -e '/<source name="/!d' \ -e 's/.* name="\([^"]*\)".*/\1/' < treebuild.xml` # Compile the sources into modules. for SRC in ${CSOURCES} do MODULE=`db2_name "${SRC}"` make_module "${MODULE}" "${SRC}" done # If needed, (re)create the static binding directory. if action_needed "${LIBIFSNAME}/${STATBNDDIR}.BNDDIR" then LINK=YES fi if [ "${LINK}" ] then rm -rf "${LIBIFSNAME}/${STATBNDDIR}.BNDDIR" CMD="CRTBNDDIR BNDDIR(${TARGETLIB}/${STATBNDDIR})" CMD="${CMD} TEXT('ZLIB static binding directory')" system "${CMD}" for MODULE in ${MODULES} do CMD="ADDBNDDIRE BNDDIR(${TARGETLIB}/${STATBNDDIR})" CMD="${CMD} OBJ((${TARGETLIB}/${MODULE} *MODULE))" system "${CMD}" done fi # The exportation file for service program creation must be in a DB2 # source file, so make sure it exists. if action_needed "${LIBIFSNAME}/TOOLS.FILE" then CMD="CRTSRCPF FILE(${TARGETLIB}/TOOLS) RCDLEN(112)" CMD="${CMD} CCSID(${TGTCCSID}) TEXT('ZLIB: build tools')" system "${CMD}" fi DEST="${LIBIFSNAME}/TOOLS.FILE/BNDSRC.MBR" if action_needed "${SCRIPTDIR}/bndsrc" "${DEST}" then CMD="CPY OBJ('${SCRIPTDIR}/bndsrc') TOOBJ('${DEST}')" CMD="${CMD} TOCCSID(${TGTCCSID}) DTAFMT(*TEXT) REPLACE(*YES)" system "${CMD}" # touch -r "${SCRIPTDIR}/bndsrc" "${DEST}" LINK=YES fi # Build the service program if needed. if action_needed "${LIBIFSNAME}/${SRVPGM}.SRVPGM" then LINK=YES fi if [ "${LINK}" ] then CMD="CRTSRVPGM SRVPGM(${TARGETLIB}/${SRVPGM})" CMD="${CMD} SRCFILE(${TARGETLIB}/TOOLS) SRCMBR(BNDSRC)" CMD="${CMD} MODULE(${TARGETLIB}/OS400)" CMD="${CMD} BNDDIR(${TARGETLIB}/${STATBNDDIR})" CMD="${CMD} TEXT('ZLIB ${VERSION} dynamic library')" CMD="${CMD} TGTRLS(${TGTRLS})" system "${CMD}" LINK=YES # Duplicate the service program for a versioned backup. BACKUP=`echo "${SRVPGM}${VERSION}" | sed -e 's/.*\(..........\)$/\1/' -e 's/\./_/g'` BACKUP="`db2_name \"${BACKUP}\"`" BKUPIFSNAME="${LIBIFSNAME}/${BACKUP}.SRVPGM" rm -f "${BKUPIFSNAME}" CMD="CRTDUPOBJ OBJ(${SRVPGM}) FROMLIB(${TARGETLIB})" CMD="${CMD} OBJTYPE(*SRVPGM) NEWOBJ(${BACKUP})" system "${CMD}" fi # If needed, (re)create the dynamic binding directory. if action_needed "${LIBIFSNAME}/${DYNBNDDIR}.BNDDIR" then LINK=YES fi if [ "${LINK}" ] then rm -rf "${LIBIFSNAME}/${DYNBNDDIR}.BNDDIR" CMD="CRTBNDDIR BNDDIR(${TARGETLIB}/${DYNBNDDIR})" CMD="${CMD} TEXT('ZLIB dynamic binding directory')" system "${CMD}" CMD="ADDBNDDIRE BNDDIR(${TARGETLIB}/${DYNBNDDIR})" CMD="${CMD} OBJ((*LIBL/${SRVPGM} *SRVPGM))" system "${CMD}" fi |
Added compat/zlib/os400/zlib.inc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | * ZLIB.INC - Interface to the general purpose compression library * * ILE RPG400 version by Patrick Monnerat, DATASPHERE. * Version 1.3.1 * * * WARNING: * Procedures inflateInit(), inflateInit2(), deflateInit(), * deflateInit2() and inflateBackInit() need to be called with * two additional arguments: * the package version string and the stream control structure. * size. This is needed because RPG lacks some macro feature. * Call these procedures as: * inflateInit(...: ZLIB_VERSION: %size(z_stream)) * /if not defined(ZLIB_H_) /define ZLIB_H_ * ************************************************************************** * Constants ************************************************************************** * * Versioning information. * D ZLIB_VERSION C '1.3.1' D ZLIB_VERNUM C X'12a0' D ZLIB_VER_MAJOR C 1 D ZLIB_VER_MINOR C 3 D ZLIB_VER_REVISION... D C 1 D ZLIB_VER_SUBREVISION... D C 0 * * Other equates. * D Z_NO_FLUSH C 0 D Z_PARTIAL_FLUSH... D C 1 D Z_SYNC_FLUSH C 2 D Z_FULL_FLUSH C 3 D Z_FINISH C 4 D Z_BLOCK C 5 D Z_TREES C 6 * D Z_OK C 0 D Z_STREAM_END C 1 D Z_NEED_DICT C 2 D Z_ERRNO C -1 D Z_STREAM_ERROR C -2 D Z_DATA_ERROR C -3 D Z_MEM_ERROR C -4 D Z_BUF_ERROR C -5 D Z_VERSION_ERROR C -6 * D Z_NO_COMPRESSION... D C 0 D Z_BEST_SPEED C 1 D Z_BEST_COMPRESSION... D C 9 D Z_DEFAULT_COMPRESSION... D C -1 * D Z_FILTERED C 1 D Z_HUFFMAN_ONLY C 2 D Z_RLE C 3 D Z_DEFAULT_STRATEGY... D C 0 * D Z_BINARY C 0 D Z_ASCII C 1 D Z_UNKNOWN C 2 * D Z_DEFLATED C 8 * D Z_NULL C 0 * ************************************************************************** * Types ************************************************************************** * D z_streamp S * Stream struct ptr D gzFile S * File pointer D gz_headerp S * D z_off_t S 10i 0 Stream offsets D z_off64_t S 20i 0 Stream offsets * ************************************************************************** * Structures ************************************************************************** * * The GZIP encode/decode stream support structure. * D z_stream DS align based(z_streamp) D zs_next_in * Next input byte D zs_avail_in 10U 0 Byte cnt at next_in D zs_total_in 10U 0 Total bytes read D zs_next_out * Output buffer ptr D zs_avail_out 10U 0 Room left @ next_out D zs_total_out 10U 0 Total bytes written D zs_msg * Last errmsg or null D zs_state * Internal state D zs_zalloc * procptr Int. state allocator D zs_free * procptr Int. state dealloc. D zs_opaque * Private alloc. data D zs_data_type 10i 0 ASC/BIN best guess D zs_adler 10u 0 Uncompr. adler32 val D 10U 0 Reserved D 10U 0 Ptr. alignment * ************************************************************************** * Utility function prototypes ************************************************************************** * D compress PR 10I 0 extproc('compress') D dest 65535 options(*varsize) Destination buffer D destLen 10U 0 Destination length D source 65535 const options(*varsize) Source buffer D sourceLen 10u 0 value Source length * D compress2 PR 10I 0 extproc('compress2') D dest 65535 options(*varsize) Destination buffer D destLen 10U 0 Destination length D source 65535 const options(*varsize) Source buffer D sourceLen 10U 0 value Source length D level 10I 0 value Compression level * D compressBound PR 10U 0 extproc('compressBound') D sourceLen 10U 0 value * D uncompress PR 10I 0 extproc('uncompress') D dest 65535 options(*varsize) Destination buffer D destLen 10U 0 Destination length D source 65535 const options(*varsize) Source buffer D sourceLen 10U 0 value Source length * D uncompress2 PR 10I 0 extproc('uncompress2') D dest 65535 options(*varsize) Destination buffer D destLen 10U 0 Destination length D source 65535 const options(*varsize) Source buffer D sourceLen 10U 0 Source length * /if not defined(LARGE_FILES) D gzopen PR extproc('gzopen') D like(gzFile) D path * value options(*string) File pathname D mode * value options(*string) Open mode /else D gzopen PR extproc('gzopen64') D like(gzFile) D path * value options(*string) File pathname D mode * value options(*string) Open mode * D gzopen64 PR extproc('gzopen64') D like(gzFile) D path * value options(*string) File pathname D mode * value options(*string) Open mode /endif * D gzdopen PR extproc('gzdopen') D like(gzFile) D fd 10I 0 value File descriptor D mode * value options(*string) Open mode * D gzbuffer PR 10I 0 extproc('gzbuffer') D file value like(gzFile) File pointer D size 10U 0 value * D gzsetparams PR 10I 0 extproc('gzsetparams') D file value like(gzFile) File pointer D level 10I 0 value D strategy 10I 0 value * D gzread PR 10I 0 extproc('gzread') D file value like(gzFile) File pointer D buf 65535 options(*varsize) Buffer D len 10u 0 value Buffer length * D gzfread PR 20I 0 extproc('gzfread') D buf 65535 options(*varsize) Buffer D size 20u 0 value Buffer length D nitems 20u 0 value Buffer length D file value like(gzFile) File pointer * D gzwrite PR 10I 0 extproc('gzwrite') D file value like(gzFile) File pointer D buf 65535 const options(*varsize) Buffer D len 10u 0 value Buffer length * D gzfwrite PR 20I 0 extproc('gzfwrite') D buf 65535 options(*varsize) Buffer D size 20u 0 value Buffer length D nitems 20u 0 value Buffer length D file value like(gzFile) File pointer * D gzputs PR 10I 0 extproc('gzputs') D file value like(gzFile) File pointer D s * value options(*string) String to output * D gzgets PR * extproc('gzgets') D file value like(gzFile) File pointer D buf 65535 options(*varsize) Read buffer D len 10i 0 value Buffer length * D gzputc PR 10i 0 extproc('gzputc') D file value like(gzFile) File pointer D c 10I 0 value Character to write * D gzgetc PR 10i 0 extproc('gzgetc') D file value like(gzFile) File pointer * D gzgetc_ PR 10i 0 extproc('gzgetc_') D file value like(gzFile) File pointer * D gzungetc PR 10i 0 extproc('gzungetc') D c 10I 0 value Character to push D file value like(gzFile) File pointer * D gzflush PR 10i 0 extproc('gzflush') D file value like(gzFile) File pointer D flush 10I 0 value Type of flush * /if not defined(LARGE_FILES) D gzseek PR extproc('gzseek') D like(z_off_t) D file value like(gzFile) File pointer D offset value like(z_off_t) Offset D whence 10i 0 value Origin /else D gzseek PR extproc('gzseek64') D like(z_off_t) D file value like(gzFile) File pointer D offset value like(z_off_t) Offset D whence 10i 0 value Origin * D gzseek64 PR extproc('gzseek64') D like(z_off64_t) D file value like(gzFile) File pointer D offset value like(z_off64_t) Offset D whence 10i 0 value Origin /endif * D gzrewind PR 10i 0 extproc('gzrewind') D file value like(gzFile) File pointer * /if not defined(LARGE_FILES) D gztell PR extproc('gztell') D like(z_off_t) D file value like(gzFile) File pointer /else D gztell PR extproc('gztell64') D like(z_off_t) D file value like(gzFile) File pointer * D gztell64 PR extproc('gztell64') D like(z_off64_t) D file value like(gzFile) File pointer /endif * /if not defined(LARGE_FILES) D gzoffset PR extproc('gzoffset') D like(z_off_t) D file value like(gzFile) File pointer /else D gzoffset PR extproc('gzoffset64') D like(z_off_t) D file value like(gzFile) File pointer * D gzoffset64 PR extproc('gzoffset64') D like(z_off64_t) D file value like(gzFile) File pointer /endif * D gzeof PR 10i 0 extproc('gzeof') D file value like(gzFile) File pointer * D gzdirect PR 10i 0 extproc('gzdirect') D file value like(gzFile) File pointer * D gzclose_r PR 10i 0 extproc('gzclose_r') D file value like(gzFile) File pointer * D gzclose_w PR 10i 0 extproc('gzclose_w') D file value like(gzFile) File pointer * D gzclose PR 10i 0 extproc('gzclose') D file value like(gzFile) File pointer * D gzerror PR * extproc('gzerror') Error string D file value like(gzFile) File pointer D errnum 10I 0 Error code * D gzclearerr PR extproc('gzclearerr') D file value like(gzFile) File pointer * ************************************************************************** * Basic function prototypes ************************************************************************** * D zlibVersion PR * extproc('zlibVersion') Version string * D deflateInit PR 10I 0 extproc('deflateInit_') Init. compression D strm like(z_stream) Compression stream D level 10I 0 value Compression level D version * value options(*string) Version string D stream_size 10i 0 value Stream struct. size * D deflate PR 10I 0 extproc('deflate') Compress data D strm like(z_stream) Compression stream D flush 10I 0 value Flush type required * D deflateEnd PR 10I 0 extproc('deflateEnd') Termin. compression D strm like(z_stream) Compression stream * D inflateInit PR 10I 0 extproc('inflateInit_') Init. expansion D strm like(z_stream) Expansion stream D version * value options(*string) Version string D stream_size 10i 0 value Stream struct. size * D inflate PR 10I 0 extproc('inflate') Expand data D strm like(z_stream) Expansion stream D flush 10I 0 value Flush type required * D inflateEnd PR 10I 0 extproc('inflateEnd') Termin. expansion D strm like(z_stream) Expansion stream * ************************************************************************** * Advanced function prototypes ************************************************************************** * D deflateInit2 PR 10I 0 extproc('deflateInit2_') Init. compression D strm like(z_stream) Compression stream D level 10I 0 value Compression level D method 10I 0 value Compression method D windowBits 10I 0 value log2(window size) D memLevel 10I 0 value Mem/cmpress tradeoff D strategy 10I 0 value Compression strategy D version * value options(*string) Version string D stream_size 10i 0 value Stream struct. size * D deflateSetDictionary... D PR 10I 0 extproc('deflateSetDictionary') Init. dictionary D strm like(z_stream) Compression stream D dictionary 65535 const options(*varsize) Dictionary bytes D dictLength 10U 0 value Dictionary length * D deflateCopy PR 10I 0 extproc('deflateCopy') Compress strm 2 strm D dest like(z_stream) Destination stream D source like(z_stream) Source stream * D deflateReset PR 10I 0 extproc('deflateReset') End and init. stream D strm like(z_stream) Compression stream * D deflateParams PR 10I 0 extproc('deflateParams') Change level & strat D strm like(z_stream) Compression stream D level 10I 0 value Compression level D strategy 10I 0 value Compression strategy * D deflateTune PR 10I 0 extproc('deflateTune') D strm like(z_stream) Compression stream D good 10I 0 value D lazy 10I 0 value D nice 10I 0 value D chain 10I 0 value * D deflateBound PR 10U 0 extproc('deflateBound') Change level & strat D strm like(z_stream) Compression stream D sourcelen 10U 0 value Compression level * D deflatePending PR 10I 0 extproc('deflatePending') Change level & strat D strm like(z_stream) Compression stream D pending 10U 0 Pending bytes D bits 10I 0 Pending bits * D deflatePrime PR 10I 0 extproc('deflatePrime') Change level & strat D strm like(z_stream) Compression stream D bits 10I 0 value # of bits to insert D value 10I 0 value Bits to insert * D inflateInit2 PR 10I 0 extproc('inflateInit2_') Init. expansion D strm like(z_stream) Expansion stream D windowBits 10I 0 value log2(window size) D version * value options(*string) Version string D stream_size 10i 0 value Stream struct. size * D inflateSetDictionary... D PR 10I 0 extproc('inflateSetDictionary') Init. dictionary D strm like(z_stream) Expansion stream D dictionary 65535 const options(*varsize) Dictionary bytes D dictLength 10U 0 value Dictionary length * D inflateGetDictionary... D PR 10I 0 extproc('inflateGetDictionary') Get dictionary D strm like(z_stream) Expansion stream D dictionary 65535 options(*varsize) Dictionary bytes D dictLength 10U 0 Dictionary length * D deflateGetDictionary... D PR 10I 0 extproc('deflateGetDictionary') Get dictionary D strm like(z_stream) Expansion stream D dictionary 65535 options(*varsize) Dictionary bytes D dictLength 10U 0 Dictionary length * D inflateSync PR 10I 0 extproc('inflateSync') Sync. expansion D strm like(z_stream) Expansion stream * D inflateCopy PR 10I 0 extproc('inflateCopy') D dest like(z_stream) Destination stream D source like(z_stream) Source stream * D inflateReset PR 10I 0 extproc('inflateReset') End and init. stream D strm like(z_stream) Expansion stream * D inflateReset2 PR 10I 0 extproc('inflateReset2') End and init. stream D strm like(z_stream) Expansion stream D windowBits 10I 0 value Log2(buffer size) * D inflatePrime PR 10I 0 extproc('inflatePrime') Insert bits D strm like(z_stream) Expansion stream D bits 10I 0 value Bit count D value 10I 0 value Bits to insert * D inflateMark PR 10I 0 extproc('inflateMark') Get inflate info D strm like(z_stream) Expansion stream * D inflateCodesUsed... PR 20U 0 extproc('inflateCodesUsed') D strm like(z_stream) Expansion stream * D inflateValidate... PR 20U 0 extproc('inflateValidate') D strm like(z_stream) Expansion stream D check 10I 0 value * D inflateGetHeader... PR 10U 0 extproc('inflateGetHeader') D strm like(z_stream) Expansion stream D head like(gz_headerp) * D deflateSetHeader... PR 10U 0 extproc('deflateSetHeader') D strm like(z_stream) Expansion stream D head like(gz_headerp) * D inflateBackInit... D PR 10I 0 extproc('inflateBackInit_') D strm like(z_stream) Expansion stream D windowBits 10I 0 value Log2(buffer size) D window 65535 options(*varsize) Buffer D version * value options(*string) Version string D stream_size 10i 0 value Stream struct. size * D inflateBack PR 10I 0 extproc('inflateBack') D strm like(z_stream) Expansion stream D in * value procptr Input function D in_desc * value Input descriptor D out * value procptr Output function D out_desc * value Output descriptor * D inflateBackEnd PR 10I 0 extproc('inflateBackEnd') D strm like(z_stream) Expansion stream * D zlibCompileFlags... D PR 10U 0 extproc('zlibCompileFlags') * ************************************************************************** * Checksum function prototypes ************************************************************************** * D adler32 PR 10U 0 extproc('adler32') New checksum D adler 10U 0 value Old checksum D buf 65535 const options(*varsize) Bytes to accumulate D len 10U 0 value Buffer length * D adler32_combine... PR 10U 0 extproc('adler32_combine') New checksum D adler1 10U 0 value Old checksum D adler2 10U 0 value Old checksum D len2 20U 0 value Buffer length * D adler32_z PR 10U 0 extproc('adler32_z') New checksum D adler 10U 0 value Old checksum D buf 65535 const options(*varsize) Bytes to accumulate D len 20U 0 value Buffer length * D crc32 PR 10U 0 extproc('crc32') New checksum D crc 10U 0 value Old checksum D buf 65535 const options(*varsize) Bytes to accumulate D len 10U 0 value Buffer length * D crc32_combine... PR 10U 0 extproc('crc32_combine') New checksum D crc1 10U 0 value Old checksum D crc2 10U 0 value Old checksum D len2 20U 0 value Buffer length * D crc32_z PR 10U 0 extproc('crc32_z') New checksum D crc 10U 0 value Old checksum D buf 65535 const options(*varsize) Bytes to accumulate D len 20U 0 value Buffer length * ************************************************************************** * Miscellaneous function prototypes ************************************************************************** * D zError PR * extproc('zError') Error string D err 10I 0 value Error code * D inflateSyncPoint... D PR 10I 0 extproc('inflateSyncPoint') D strm like(z_stream) Expansion stream * D get_crc_table PR * extproc('get_crc_table') Ptr to ulongs * D inflateUndermine... D PR 10I 0 extproc('inflateUndermine') D strm like(z_stream) Expansion stream D arg 10I 0 value Error code * D inflateResetKeep... D PR 10I 0 extproc('inflateResetKeep') End and init. stream D strm like(z_stream) Expansion stream * D deflateResetKeep... D PR 10I 0 extproc('deflateResetKeep') End and init. stream D strm like(z_stream) Expansion stream * /endif |
Added compat/zlib/qnx/package.qpg.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <QPG:Generation> <QPG:Options> <QPG:User unattended="no" verbosity="2" listfiles="yes"/> <QPG:Defaults type="qnx_package"/> <QPG:Source></QPG:Source> <QPG:Release number="+"/> <QPG:Build></QPG:Build> <QPG:FileSorting strip="yes"/> <QPG:Package targets="combine"/> <QPG:Repository generate="yes"/> <QPG:FinalDir></QPG:FinalDir> <QPG:Cleanup></QPG:Cleanup> </QPG:Options> <QPG:Responsible> <QPG:Company></QPG:Company> <QPG:Department></QPG:Department> <QPG:Group></QPG:Group> <QPG:Team></QPG:Team> <QPG:Employee></QPG:Employee> <QPG:EmailAddress></QPG:EmailAddress> </QPG:Responsible> <QPG:Values> <QPG:Files> <QPG:Add file="../zconf.h" install="/opt/include/" user="root:sys" permission="644"/> <QPG:Add file="../zlib.h" install="/opt/include/" user="root:sys" permission="644"/> <QPG:Add file="../libz.so.1.3.1" install="/opt/lib/" user="root:bin" permission="644"/> <QPG:Add file="libz.so" install="/opt/lib/" component="dev" filetype="symlink" linkto="libz.so.1.3.1"/> <QPG:Add file="libz.so.1" install="/opt/lib/" filetype="symlink" linkto="libz.so.1.3.1"/> <QPG:Add file="../libz.so.1.3.1" install="/opt/lib/" component="slib"/> </QPG:Files> <QPG:PackageFilter> <QPM:PackageManifest> <QPM:PackageDescription> <QPM:PackageType>Library</QPM:PackageType> <QPM:PackageReleaseNotes></QPM:PackageReleaseNotes> <QPM:PackageReleaseUrgency>Medium</QPM:PackageReleaseUrgency> <QPM:PackageRepository></QPM:PackageRepository> <QPM:FileVersion>2.0</QPM:FileVersion> </QPM:PackageDescription> <QPM:ProductDescription> <QPM:ProductName>zlib</QPM:ProductName> <QPM:ProductIdentifier>zlib</QPM:ProductIdentifier> <QPM:ProductEmail>alain.bonnefoy@icbt.com</QPM:ProductEmail> <QPM:VendorName>Public</QPM:VendorName> <QPM:VendorInstallName>public</QPM:VendorInstallName> <QPM:VendorURL>www.gzip.org/zlib</QPM:VendorURL> <QPM:VendorEmbedURL></QPM:VendorEmbedURL> <QPM:VendorEmail></QPM:VendorEmail> <QPM:AuthorName>Jean-Loup Gailly,Mark Adler</QPM:AuthorName> <QPM:AuthorURL>www.gzip.org/zlib</QPM:AuthorURL> <QPM:AuthorEmbedURL></QPM:AuthorEmbedURL> <QPM:AuthorEmail>zlib@gzip.org</QPM:AuthorEmail> <QPM:ProductIconSmall></QPM:ProductIconSmall> <QPM:ProductIconLarge></QPM:ProductIconLarge> <QPM:ProductDescriptionShort>A massively spiffy yet delicately unobtrusive compression library.</QPM:ProductDescriptionShort> <QPM:ProductDescriptionLong>zlib is designed to be a free, general-purpose, legally unencumbered, lossless data compression library for use on virtually any computer hardware and operating system.</QPM:ProductDescriptionLong> <QPM:ProductDescriptionURL>http://www.gzip.org/zlib</QPM:ProductDescriptionURL> <QPM:ProductDescriptionEmbedURL></QPM:ProductDescriptionEmbedURL> </QPM:ProductDescription> <QPM:ReleaseDescription> <QPM:ReleaseVersion>1.3.1</QPM:ReleaseVersion> <QPM:ReleaseUrgency>Medium</QPM:ReleaseUrgency> <QPM:ReleaseStability>Stable</QPM:ReleaseStability> <QPM:ReleaseNoteMinor></QPM:ReleaseNoteMinor> <QPM:ReleaseNoteMajor></QPM:ReleaseNoteMajor> <QPM:ExcludeCountries> <QPM:Country></QPM:Country> </QPM:ExcludeCountries> <QPM:ReleaseCopyright>No License</QPM:ReleaseCopyright> </QPM:ReleaseDescription> <QPM:ContentDescription> <QPM:ContentTopic xmlmultiple="true">Software Development/Libraries and Extensions/C Libraries</QPM:ContentTopic> <QPM:ContentKeyword>zlib,compression</QPM:ContentKeyword> <QPM:TargetOS>qnx6</QPM:TargetOS> <QPM:HostOS>qnx6</QPM:HostOS> <QPM:DisplayEnvironment xmlmultiple="true">None</QPM:DisplayEnvironment> <QPM:TargetAudience xmlmultiple="true">Developer</QPM:TargetAudience> </QPM:ContentDescription> </QPM:PackageManifest> </QPG:PackageFilter> <QPG:PackageFilter proc="none" target="none"> <QPM:PackageManifest> <QPM:ProductInstallationDependencies> <QPM:ProductRequirements></QPM:ProductRequirements> </QPM:ProductInstallationDependencies> <QPM:ProductInstallationProcedure> <QPM:Script xmlmultiple="true"> <QPM:ScriptName></QPM:ScriptName> <QPM:ScriptType>Install</QPM:ScriptType> <QPM:ScriptTiming>Post</QPM:ScriptTiming> <QPM:ScriptBlocking>No</QPM:ScriptBlocking> <QPM:ScriptResult>Ignore</QPM:ScriptResult> <QPM:ShortDescription></QPM:ShortDescription> <QPM:UseBinaries>No</QPM:UseBinaries> <QPM:Priority>Optional</QPM:Priority> </QPM:Script> </QPM:ProductInstallationProcedure> </QPM:PackageManifest> <QPM:Launch> </QPM:Launch> </QPG:PackageFilter> <QPG:PackageFilter type="core" component="none"> <QPM:PackageManifest> <QPM:ProductInstallationProcedure> <QPM:OrderDependency xmlmultiple="true"> <QPM:Order>InstallOver</QPM:Order> <QPM:Product>zlib</QPM:Product> </QPM:OrderDependency> </QPM:ProductInstallationProcedure> </QPM:PackageManifest> <QPM:Launch> </QPM:Launch> </QPG:PackageFilter> <QPG:PackageFilter type="core" component="dev"> <QPM:PackageManifest> <QPM:ProductInstallationProcedure> <QPM:OrderDependency xmlmultiple="true"> <QPM:Order>InstallOver</QPM:Order> <QPM:Product>zlib-dev</QPM:Product> </QPM:OrderDependency> </QPM:ProductInstallationProcedure> </QPM:PackageManifest> <QPM:Launch> </QPM:Launch> </QPG:PackageFilter> </QPG:Values> </QPG:Generation> |
Added compat/zlib/test/example.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 | /* example.c -- usage example of the zlib compression library * Copyright (C) 1995-2006, 2011, 2016 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zlib.h" #include <stdio.h> #ifdef STDC # include <string.h> # include <stdlib.h> #endif #if defined(VMS) || defined(RISCOS) # define TESTFILE "foo-gz" #else # define TESTFILE "foo.gz" #endif #define CHECK_ERR(err, msg) { \ if (err != Z_OK) { \ fprintf(stderr, "%s error: %d\n", msg, err); \ exit(1); \ } \ } static z_const char hello[] = "hello, hello!"; /* "hello world" would be more standard, but the repeated "hello" * stresses the compression code better, sorry... */ static const char dictionary[] = "hello"; static uLong dictId; /* Adler32 value of the dictionary */ #ifdef Z_SOLO static void *myalloc(void *q, unsigned n, unsigned m) { (void)q; return calloc(n, m); } static void myfree(void *q, void *p) { (void)q; free(p); } static alloc_func zalloc = myalloc; static free_func zfree = myfree; #else /* !Z_SOLO */ static alloc_func zalloc = (alloc_func)0; static free_func zfree = (free_func)0; /* =========================================================================== * Test compress() and uncompress() */ static void test_compress(Byte *compr, uLong comprLen, Byte *uncompr, uLong uncomprLen) { int err; uLong len = (uLong)strlen(hello)+1; err = compress(compr, &comprLen, (const Bytef*)hello, len); CHECK_ERR(err, "compress"); strcpy((char*)uncompr, "garbage"); err = uncompress(uncompr, &uncomprLen, compr, comprLen); CHECK_ERR(err, "uncompress"); if (strcmp((char*)uncompr, hello)) { fprintf(stderr, "bad uncompress\n"); exit(1); } else { printf("uncompress(): %s\n", (char *)uncompr); } } /* =========================================================================== * Test read/write of .gz files */ static void test_gzio(const char *fname, Byte *uncompr, uLong uncomprLen) { #ifdef NO_GZCOMPRESS fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n"); #else int err; int len = (int)strlen(hello)+1; gzFile file; z_off_t pos; file = gzopen(fname, "wb"); if (file == NULL) { fprintf(stderr, "gzopen error\n"); exit(1); } gzputc(file, 'h'); if (gzputs(file, "ello") != 4) { fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err)); exit(1); } if (gzprintf(file, ", %s!", "hello") != 8) { fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err)); exit(1); } gzseek(file, 1L, SEEK_CUR); /* add one zero byte */ gzclose(file); file = gzopen(fname, "rb"); if (file == NULL) { fprintf(stderr, "gzopen error\n"); exit(1); } strcpy((char*)uncompr, "garbage"); if (gzread(file, uncompr, (unsigned)uncomprLen) != len) { fprintf(stderr, "gzread err: %s\n", gzerror(file, &err)); exit(1); } if (strcmp((char*)uncompr, hello)) { fprintf(stderr, "bad gzread: %s\n", (char*)uncompr); exit(1); } else { printf("gzread(): %s\n", (char*)uncompr); } pos = gzseek(file, -8L, SEEK_CUR); if (pos != 6 || gztell(file) != pos) { fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)gztell(file)); exit(1); } if (gzgetc(file) != ' ') { fprintf(stderr, "gzgetc error\n"); exit(1); } if (gzungetc(' ', file) != ' ') { fprintf(stderr, "gzungetc error\n"); exit(1); } gzgets(file, (char*)uncompr, (int)uncomprLen); if (strlen((char*)uncompr) != 7) { /* " hello!" */ fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err)); exit(1); } if (strcmp((char*)uncompr, hello + 6)) { fprintf(stderr, "bad gzgets after gzseek\n"); exit(1); } else { printf("gzgets() after gzseek: %s\n", (char*)uncompr); } gzclose(file); #endif } #endif /* Z_SOLO */ /* =========================================================================== * Test deflate() with small buffers */ static void test_deflate(Byte *compr, uLong comprLen) { z_stream c_stream; /* compression stream */ int err; uLong len = (uLong)strlen(hello)+1; c_stream.zalloc = zalloc; c_stream.zfree = zfree; c_stream.opaque = (voidpf)0; err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); CHECK_ERR(err, "deflateInit"); c_stream.next_in = (z_const unsigned char *)hello; c_stream.next_out = compr; while (c_stream.total_in != len && c_stream.total_out < comprLen) { c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */ err = deflate(&c_stream, Z_NO_FLUSH); CHECK_ERR(err, "deflate"); } /* Finish the stream, still forcing small buffers: */ for (;;) { c_stream.avail_out = 1; err = deflate(&c_stream, Z_FINISH); if (err == Z_STREAM_END) break; CHECK_ERR(err, "deflate"); } err = deflateEnd(&c_stream); CHECK_ERR(err, "deflateEnd"); } /* =========================================================================== * Test inflate() with small buffers */ static void test_inflate(Byte *compr, uLong comprLen, Byte *uncompr, uLong uncomprLen) { int err; z_stream d_stream; /* decompression stream */ strcpy((char*)uncompr, "garbage"); d_stream.zalloc = zalloc; d_stream.zfree = zfree; d_stream.opaque = (voidpf)0; d_stream.next_in = compr; d_stream.avail_in = 0; d_stream.next_out = uncompr; err = inflateInit(&d_stream); CHECK_ERR(err, "inflateInit"); while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) { d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ err = inflate(&d_stream, Z_NO_FLUSH); if (err == Z_STREAM_END) break; CHECK_ERR(err, "inflate"); } err = inflateEnd(&d_stream); CHECK_ERR(err, "inflateEnd"); if (strcmp((char*)uncompr, hello)) { fprintf(stderr, "bad inflate\n"); exit(1); } else { printf("inflate(): %s\n", (char *)uncompr); } } /* =========================================================================== * Test deflate() with large buffers and dynamic change of compression level */ static void test_large_deflate(Byte *compr, uLong comprLen, Byte *uncompr, uLong uncomprLen) { z_stream c_stream; /* compression stream */ int err; c_stream.zalloc = zalloc; c_stream.zfree = zfree; c_stream.opaque = (voidpf)0; err = deflateInit(&c_stream, Z_BEST_SPEED); CHECK_ERR(err, "deflateInit"); c_stream.next_out = compr; c_stream.avail_out = (uInt)comprLen; /* At this point, uncompr is still mostly zeroes, so it should compress * very well: */ c_stream.next_in = uncompr; c_stream.avail_in = (uInt)uncomprLen; err = deflate(&c_stream, Z_NO_FLUSH); CHECK_ERR(err, "deflate"); if (c_stream.avail_in != 0) { fprintf(stderr, "deflate not greedy\n"); exit(1); } /* Feed in already compressed data and switch to no compression: */ deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); c_stream.next_in = compr; c_stream.avail_in = (uInt)uncomprLen/2; err = deflate(&c_stream, Z_NO_FLUSH); CHECK_ERR(err, "deflate"); /* Switch back to compressing mode: */ deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); c_stream.next_in = uncompr; c_stream.avail_in = (uInt)uncomprLen; err = deflate(&c_stream, Z_NO_FLUSH); CHECK_ERR(err, "deflate"); err = deflate(&c_stream, Z_FINISH); if (err != Z_STREAM_END) { fprintf(stderr, "deflate should report Z_STREAM_END\n"); exit(1); } err = deflateEnd(&c_stream); CHECK_ERR(err, "deflateEnd"); } /* =========================================================================== * Test inflate() with large buffers */ static void test_large_inflate(Byte *compr, uLong comprLen, Byte *uncompr, uLong uncomprLen) { int err; z_stream d_stream; /* decompression stream */ strcpy((char*)uncompr, "garbage"); d_stream.zalloc = zalloc; d_stream.zfree = zfree; d_stream.opaque = (voidpf)0; d_stream.next_in = compr; d_stream.avail_in = (uInt)comprLen; err = inflateInit(&d_stream); CHECK_ERR(err, "inflateInit"); for (;;) { d_stream.next_out = uncompr; /* discard the output */ d_stream.avail_out = (uInt)uncomprLen; err = inflate(&d_stream, Z_NO_FLUSH); if (err == Z_STREAM_END) break; CHECK_ERR(err, "large inflate"); } err = inflateEnd(&d_stream); CHECK_ERR(err, "inflateEnd"); if (d_stream.total_out != 2*uncomprLen + uncomprLen/2) { fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out); exit(1); } else { printf("large_inflate(): OK\n"); } } /* =========================================================================== * Test deflate() with full flush */ static void test_flush(Byte *compr, uLong *comprLen) { z_stream c_stream; /* compression stream */ int err; uInt len = (uInt)strlen(hello)+1; c_stream.zalloc = zalloc; c_stream.zfree = zfree; c_stream.opaque = (voidpf)0; err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); CHECK_ERR(err, "deflateInit"); c_stream.next_in = (z_const unsigned char *)hello; c_stream.next_out = compr; c_stream.avail_in = 3; c_stream.avail_out = (uInt)*comprLen; err = deflate(&c_stream, Z_FULL_FLUSH); CHECK_ERR(err, "deflate"); compr[3]++; /* force an error in first compressed block */ c_stream.avail_in = len - 3; err = deflate(&c_stream, Z_FINISH); if (err != Z_STREAM_END) { CHECK_ERR(err, "deflate"); } err = deflateEnd(&c_stream); CHECK_ERR(err, "deflateEnd"); *comprLen = c_stream.total_out; } /* =========================================================================== * Test inflateSync() */ static void test_sync(Byte *compr, uLong comprLen, Byte *uncompr, uLong uncomprLen) { int err; z_stream d_stream; /* decompression stream */ strcpy((char*)uncompr, "garbage"); d_stream.zalloc = zalloc; d_stream.zfree = zfree; d_stream.opaque = (voidpf)0; d_stream.next_in = compr; d_stream.avail_in = 2; /* just read the zlib header */ err = inflateInit(&d_stream); CHECK_ERR(err, "inflateInit"); d_stream.next_out = uncompr; d_stream.avail_out = (uInt)uncomprLen; err = inflate(&d_stream, Z_NO_FLUSH); CHECK_ERR(err, "inflate"); d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */ err = inflateSync(&d_stream); /* but skip the damaged part */ CHECK_ERR(err, "inflateSync"); err = inflate(&d_stream, Z_FINISH); if (err != Z_STREAM_END) { fprintf(stderr, "inflate should report Z_STREAM_END\n"); exit(1); } err = inflateEnd(&d_stream); CHECK_ERR(err, "inflateEnd"); printf("after inflateSync(): hel%s\n", (char *)uncompr); } /* =========================================================================== * Test deflate() with preset dictionary */ static void test_dict_deflate(Byte *compr, uLong comprLen) { z_stream c_stream; /* compression stream */ int err; c_stream.zalloc = zalloc; c_stream.zfree = zfree; c_stream.opaque = (voidpf)0; err = deflateInit(&c_stream, Z_BEST_COMPRESSION); CHECK_ERR(err, "deflateInit"); err = deflateSetDictionary(&c_stream, (const Bytef*)dictionary, (int)sizeof(dictionary)); CHECK_ERR(err, "deflateSetDictionary"); dictId = c_stream.adler; c_stream.next_out = compr; c_stream.avail_out = (uInt)comprLen; c_stream.next_in = (z_const unsigned char *)hello; c_stream.avail_in = (uInt)strlen(hello)+1; err = deflate(&c_stream, Z_FINISH); if (err != Z_STREAM_END) { fprintf(stderr, "deflate should report Z_STREAM_END\n"); exit(1); } err = deflateEnd(&c_stream); CHECK_ERR(err, "deflateEnd"); } /* =========================================================================== * Test inflate() with a preset dictionary */ static void test_dict_inflate(Byte *compr, uLong comprLen, Byte *uncompr, uLong uncomprLen) { int err; z_stream d_stream; /* decompression stream */ strcpy((char*)uncompr, "garbage"); d_stream.zalloc = zalloc; d_stream.zfree = zfree; d_stream.opaque = (voidpf)0; d_stream.next_in = compr; d_stream.avail_in = (uInt)comprLen; err = inflateInit(&d_stream); CHECK_ERR(err, "inflateInit"); d_stream.next_out = uncompr; d_stream.avail_out = (uInt)uncomprLen; for (;;) { err = inflate(&d_stream, Z_NO_FLUSH); if (err == Z_STREAM_END) break; if (err == Z_NEED_DICT) { if (d_stream.adler != dictId) { fprintf(stderr, "unexpected dictionary"); exit(1); } err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary, (int)sizeof(dictionary)); } CHECK_ERR(err, "inflate with dict"); } err = inflateEnd(&d_stream); CHECK_ERR(err, "inflateEnd"); if (strcmp((char*)uncompr, hello)) { fprintf(stderr, "bad inflate with dict\n"); exit(1); } else { printf("inflate with dictionary: %s\n", (char *)uncompr); } } /* =========================================================================== * Usage: example [output.gz [input.gz]] */ int main(int argc, char *argv[]) { Byte *compr, *uncompr; uLong uncomprLen = 20000; uLong comprLen = 3 * uncomprLen; static const char* myVersion = ZLIB_VERSION; if (zlibVersion()[0] != myVersion[0]) { fprintf(stderr, "incompatible zlib version\n"); exit(1); } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) { fprintf(stderr, "warning: different zlib version linked: %s\n", zlibVersion()); } printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n", ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags()); compr = (Byte*)calloc((uInt)comprLen, 1); uncompr = (Byte*)calloc((uInt)uncomprLen, 1); /* compr and uncompr are cleared to avoid reading uninitialized * data and to ensure that uncompr compresses well. */ if (compr == Z_NULL || uncompr == Z_NULL) { printf("out of memory\n"); exit(1); } #ifdef Z_SOLO (void)argc; (void)argv; #else test_compress(compr, comprLen, uncompr, uncomprLen); test_gzio((argc > 1 ? argv[1] : TESTFILE), uncompr, uncomprLen); #endif test_deflate(compr, comprLen); test_inflate(compr, comprLen, uncompr, uncomprLen); test_large_deflate(compr, comprLen, uncompr, uncomprLen); test_large_inflate(compr, comprLen, uncompr, uncomprLen); test_flush(compr, &comprLen); test_sync(compr, comprLen, uncompr, uncomprLen); comprLen = 3 * uncomprLen; test_dict_deflate(compr, comprLen); test_dict_inflate(compr, comprLen, uncompr, uncomprLen); free(compr); free(uncompr); return 0; } |
Added compat/zlib/test/infcover.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 | /* infcover.c -- test zlib's inflate routines with full code coverage * Copyright (C) 2011, 2016 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* to use, do: ./configure --cover && make cover */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include "zlib.h" /* get definition of internal structure so we can mess with it (see pull()), and so we can call inflate_trees() (see cover5()) */ #define ZLIB_INTERNAL #include "inftrees.h" #include "inflate.h" #define local static /* -- memory tracking routines -- */ /* These memory tracking routines are provided to zlib and track all of zlib's allocations and deallocations, check for LIFO operations, keep a current and high water mark of total bytes requested, optionally set a limit on the total memory that can be allocated, and when done check for memory leaks. They are used as follows: z_stream strm; mem_setup(&strm) initializes the memory tracking and sets the zalloc, zfree, and opaque members of strm to use memory tracking for all zlib operations on strm mem_limit(&strm, limit) sets a limit on the total bytes requested -- a request that exceeds this limit will result in an allocation failure (returns NULL) -- setting the limit to zero means no limit, which is the default after mem_setup() mem_used(&strm, "msg") prints to stderr "msg" and the total bytes used mem_high(&strm, "msg") prints to stderr "msg" and the high water mark mem_done(&strm, "msg") ends memory tracking, releases all allocations for the tracking as well as leaked zlib blocks, if any. If there was anything unusual, such as leaked blocks, non-FIFO frees, or frees of addresses not allocated, then "msg" and information about the problem is printed to stderr. If everything is normal, nothing is printed. mem_done resets the strm members to Z_NULL to use the default memory allocation routines on the next zlib initialization using strm. */ /* these items are strung together in a linked list, one for each allocation */ struct mem_item { void *ptr; /* pointer to allocated memory */ size_t size; /* requested size of allocation */ struct mem_item *next; /* pointer to next item in list, or NULL */ }; /* this structure is at the root of the linked list, and tracks statistics */ struct mem_zone { struct mem_item *first; /* pointer to first item in list, or NULL */ size_t total, highwater; /* total allocations, and largest total */ size_t limit; /* memory allocation limit, or 0 if no limit */ int notlifo, rogue; /* counts of non-LIFO frees and rogue frees */ }; /* memory allocation routine to pass to zlib */ local void *mem_alloc(void *mem, unsigned count, unsigned size) { void *ptr; struct mem_item *item; struct mem_zone *zone = mem; size_t len = count * (size_t)size; /* induced allocation failure */ if (zone == NULL || (zone->limit && zone->total + len > zone->limit)) return NULL; /* perform allocation using the standard library, fill memory with a non-zero value to make sure that the code isn't depending on zeros */ ptr = malloc(len); if (ptr == NULL) return NULL; memset(ptr, 0xa5, len); /* create a new item for the list */ item = malloc(sizeof(struct mem_item)); if (item == NULL) { free(ptr); return NULL; } item->ptr = ptr; item->size = len; /* insert item at the beginning of the list */ item->next = zone->first; zone->first = item; /* update the statistics */ zone->total += item->size; if (zone->total > zone->highwater) zone->highwater = zone->total; /* return the allocated memory */ return ptr; } /* memory free routine to pass to zlib */ local void mem_free(void *mem, void *ptr) { struct mem_item *item, *next; struct mem_zone *zone = mem; /* if no zone, just do a free */ if (zone == NULL) { free(ptr); return; } /* point next to the item that matches ptr, or NULL if not found -- remove the item from the linked list if found */ next = zone->first; if (next) { if (next->ptr == ptr) zone->first = next->next; /* first one is it, remove from list */ else { do { /* search the linked list */ item = next; next = item->next; } while (next != NULL && next->ptr != ptr); if (next) { /* if found, remove from linked list */ item->next = next->next; zone->notlifo++; /* not a LIFO free */ } } } /* if found, update the statistics and free the item */ if (next) { zone->total -= next->size; free(next); } /* if not found, update the rogue count */ else zone->rogue++; /* in any case, do the requested free with the standard library function */ free(ptr); } /* set up a controlled memory allocation space for monitoring, set the stream parameters to the controlled routines, with opaque pointing to the space */ local void mem_setup(z_stream *strm) { struct mem_zone *zone; zone = malloc(sizeof(struct mem_zone)); assert(zone != NULL); zone->first = NULL; zone->total = 0; zone->highwater = 0; zone->limit = 0; zone->notlifo = 0; zone->rogue = 0; strm->opaque = zone; strm->zalloc = mem_alloc; strm->zfree = mem_free; } /* set a limit on the total memory allocation, or 0 to remove the limit */ local void mem_limit(z_stream *strm, size_t limit) { struct mem_zone *zone = strm->opaque; zone->limit = limit; } /* show the current total requested allocations in bytes */ local void mem_used(z_stream *strm, char *prefix) { struct mem_zone *zone = strm->opaque; fprintf(stderr, "%s: %lu allocated\n", prefix, zone->total); } /* show the high water allocation in bytes */ local void mem_high(z_stream *strm, char *prefix) { struct mem_zone *zone = strm->opaque; fprintf(stderr, "%s: %lu high water mark\n", prefix, zone->highwater); } /* release the memory allocation zone -- if there are any surprises, notify */ local void mem_done(z_stream *strm, char *prefix) { int count = 0; struct mem_item *item, *next; struct mem_zone *zone = strm->opaque; /* show high water mark */ mem_high(strm, prefix); /* free leftover allocations and item structures, if any */ item = zone->first; while (item != NULL) { free(item->ptr); next = item->next; free(item); item = next; count++; } /* issue alerts about anything unexpected */ if (count || zone->total) fprintf(stderr, "** %s: %lu bytes in %d blocks not freed\n", prefix, zone->total, count); if (zone->notlifo) fprintf(stderr, "** %s: %d frees not LIFO\n", prefix, zone->notlifo); if (zone->rogue) fprintf(stderr, "** %s: %d frees not recognized\n", prefix, zone->rogue); /* free the zone and delete from the stream */ free(zone); strm->opaque = Z_NULL; strm->zalloc = Z_NULL; strm->zfree = Z_NULL; } /* -- inflate test routines -- */ /* Decode a hexadecimal string, set *len to length, in[] to the bytes. This decodes liberally, in that hex digits can be adjacent, in which case two in a row writes a byte. Or they can be delimited by any non-hex character, where the delimiters are ignored except when a single hex digit is followed by a delimiter, where that single digit writes a byte. The returned data is allocated and must eventually be freed. NULL is returned if out of memory. If the length is not needed, then len can be NULL. */ local unsigned char *h2b(const char *hex, unsigned *len) { unsigned char *in, *re; unsigned next, val; in = malloc((strlen(hex) + 1) >> 1); if (in == NULL) return NULL; next = 0; val = 1; do { if (*hex >= '0' && *hex <= '9') val = (val << 4) + *hex - '0'; else if (*hex >= 'A' && *hex <= 'F') val = (val << 4) + *hex - 'A' + 10; else if (*hex >= 'a' && *hex <= 'f') val = (val << 4) + *hex - 'a' + 10; else if (val != 1 && val < 32) /* one digit followed by delimiter */ val += 240; /* make it look like two digits */ if (val > 255) { /* have two digits */ in[next++] = val & 0xff; /* save the decoded byte */ val = 1; /* start over */ } } while (*hex++); /* go through the loop with the terminating null */ if (len != NULL) *len = next; re = realloc(in, next); return re == NULL ? in : re; } /* generic inflate() run, where hex is the hexadecimal input data, what is the text to include in an error message, step is how much input data to feed inflate() on each call, or zero to feed it all, win is the window bits parameter to inflateInit2(), len is the size of the output buffer, and err is the error code expected from the first inflate() call (the second inflate() call is expected to return Z_STREAM_END). If win is 47, then header information is collected with inflateGetHeader(). If a zlib stream is looking for a dictionary, then an empty dictionary is provided. inflate() is run until all of the input data is consumed. */ local void inf(char *hex, char *what, unsigned step, int win, unsigned len, int err) { int ret; unsigned have; unsigned char *in, *out; z_stream strm, copy; gz_header head; mem_setup(&strm); strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit2(&strm, win); if (ret != Z_OK) { mem_done(&strm, what); return; } out = malloc(len); assert(out != NULL); if (win == 47) { head.extra = out; head.extra_max = len; head.name = out; head.name_max = len; head.comment = out; head.comm_max = len; ret = inflateGetHeader(&strm, &head); assert(ret == Z_OK); } in = h2b(hex, &have); assert(in != NULL); if (step == 0 || step > have) step = have; strm.avail_in = step; have -= step; strm.next_in = in; do { strm.avail_out = len; strm.next_out = out; ret = inflate(&strm, Z_NO_FLUSH); assert(err == 9 || ret == err); if (ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_NEED_DICT) break; if (ret == Z_NEED_DICT) { ret = inflateSetDictionary(&strm, in, 1); assert(ret == Z_DATA_ERROR); mem_limit(&strm, 1); ret = inflateSetDictionary(&strm, out, 0); assert(ret == Z_MEM_ERROR); mem_limit(&strm, 0); ((struct inflate_state *)strm.state)->mode = DICT; ret = inflateSetDictionary(&strm, out, 0); assert(ret == Z_OK); ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_BUF_ERROR); } ret = inflateCopy(©, &strm); assert(ret == Z_OK); ret = inflateEnd(©); assert(ret == Z_OK); err = 9; /* don't care next time around */ have += strm.avail_in; strm.avail_in = step > have ? have : step; have -= strm.avail_in; } while (strm.avail_in); free(in); free(out); ret = inflateReset2(&strm, -8); assert(ret == Z_OK); ret = inflateEnd(&strm); assert(ret == Z_OK); mem_done(&strm, what); } /* cover all of the lines in inflate.c up to inflate() */ local void cover_support(void) { int ret; z_stream strm; mem_setup(&strm); strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit(&strm); assert(ret == Z_OK); mem_used(&strm, "inflate init"); ret = inflatePrime(&strm, 5, 31); assert(ret == Z_OK); ret = inflatePrime(&strm, -1, 0); assert(ret == Z_OK); ret = inflateSetDictionary(&strm, Z_NULL, 0); assert(ret == Z_STREAM_ERROR); ret = inflateEnd(&strm); assert(ret == Z_OK); mem_done(&strm, "prime"); inf("63 0", "force window allocation", 0, -15, 1, Z_OK); inf("63 18 5", "force window replacement", 0, -8, 259, Z_OK); inf("63 18 68 30 d0 0 0", "force split window update", 4, -8, 259, Z_OK); inf("3 0", "use fixed blocks", 0, -15, 1, Z_STREAM_END); inf("", "bad window size", 0, 1, 0, Z_STREAM_ERROR); mem_setup(&strm); strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit_(&strm, "!", (int)sizeof(z_stream)); assert(ret == Z_VERSION_ERROR); mem_done(&strm, "wrong version"); strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit(&strm); assert(ret == Z_OK); ret = inflateEnd(&strm); assert(ret == Z_OK); fputs("inflate built-in memory routines\n", stderr); } /* cover all inflate() header and trailer cases and code after inflate() */ local void cover_wrap(void) { int ret; z_stream strm, copy; unsigned char dict[257]; ret = inflate(Z_NULL, 0); assert(ret == Z_STREAM_ERROR); ret = inflateEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); ret = inflateCopy(Z_NULL, Z_NULL); assert(ret == Z_STREAM_ERROR); fputs("inflate bad parameters\n", stderr); inf("1f 8b 0 0", "bad gzip method", 0, 31, 0, Z_DATA_ERROR); inf("1f 8b 8 80", "bad gzip flags", 0, 31, 0, Z_DATA_ERROR); inf("77 85", "bad zlib method", 0, 15, 0, Z_DATA_ERROR); inf("8 99", "set window size from header", 0, 0, 0, Z_OK); inf("78 9c", "bad zlib window size", 0, 8, 0, Z_DATA_ERROR); inf("78 9c 63 0 0 0 1 0 1", "check adler32", 0, 15, 1, Z_STREAM_END); inf("1f 8b 8 1e 0 0 0 0 0 0 1 0 0 0 0 0 0", "bad header crc", 0, 47, 1, Z_DATA_ERROR); inf("1f 8b 8 2 0 0 0 0 0 0 1d 26 3 0 0 0 0 0 0 0 0 0", "check gzip length", 0, 47, 0, Z_STREAM_END); inf("78 90", "bad zlib header check", 0, 47, 0, Z_DATA_ERROR); inf("8 b8 0 0 0 1", "need dictionary", 0, 8, 0, Z_NEED_DICT); inf("78 9c 63 0", "compute adler32", 0, 15, 1, Z_OK); mem_setup(&strm); strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit2(&strm, -8); strm.avail_in = 2; strm.next_in = (void *)"\x63"; strm.avail_out = 1; strm.next_out = (void *)&ret; mem_limit(&strm, 1); ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); mem_limit(&strm, 0); memset(dict, 0, 257); ret = inflateSetDictionary(&strm, dict, 257); assert(ret == Z_OK); mem_limit(&strm, (sizeof(struct inflate_state) << 1) + 256); ret = inflatePrime(&strm, 16, 0); assert(ret == Z_OK); strm.avail_in = 2; strm.next_in = (void *)"\x80"; ret = inflateSync(&strm); assert(ret == Z_DATA_ERROR); ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_STREAM_ERROR); strm.avail_in = 4; strm.next_in = (void *)"\0\0\xff\xff"; ret = inflateSync(&strm); assert(ret == Z_OK); (void)inflateSyncPoint(&strm); ret = inflateCopy(©, &strm); assert(ret == Z_MEM_ERROR); mem_limit(&strm, 0); ret = inflateUndermine(&strm, 1); assert(ret == Z_DATA_ERROR); (void)inflateMark(&strm); ret = inflateEnd(&strm); assert(ret == Z_OK); mem_done(&strm, "miscellaneous, force memory errors"); } /* input and output functions for inflateBack() */ local unsigned pull(void *desc, unsigned char **buf) { static unsigned int next = 0; static unsigned char dat[] = {0x63, 0, 2, 0}; struct inflate_state *state; if (desc == Z_NULL) { next = 0; return 0; /* no input (already provided at next_in) */ } state = (void *)((z_stream *)desc)->state; if (state != Z_NULL) state->mode = SYNC; /* force an otherwise impossible situation */ return next < sizeof(dat) ? (*buf = dat + next++, 1) : 0; } local int push(void *desc, unsigned char *buf, unsigned len) { (void)buf; (void)len; return desc != Z_NULL; /* force error if desc not null */ } /* cover inflateBack() up to common deflate data cases and after those */ local void cover_back(void) { int ret; z_stream strm; unsigned char win[32768]; ret = inflateBackInit_(Z_NULL, 0, win, 0, 0); assert(ret == Z_VERSION_ERROR); ret = inflateBackInit(Z_NULL, 0, win); assert(ret == Z_STREAM_ERROR); ret = inflateBack(Z_NULL, Z_NULL, Z_NULL, Z_NULL, Z_NULL); assert(ret == Z_STREAM_ERROR); ret = inflateBackEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); fputs("inflateBack bad parameters\n", stderr); mem_setup(&strm); ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); strm.avail_in = 2; strm.next_in = (void *)"\x03"; ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL); assert(ret == Z_STREAM_END); /* force output error */ strm.avail_in = 3; strm.next_in = (void *)"\x63\x00"; ret = inflateBack(&strm, pull, Z_NULL, push, &strm); assert(ret == Z_BUF_ERROR); /* force mode error by mucking with state */ ret = inflateBack(&strm, pull, &strm, push, Z_NULL); assert(ret == Z_STREAM_ERROR); ret = inflateBackEnd(&strm); assert(ret == Z_OK); mem_done(&strm, "inflateBack bad state"); ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); ret = inflateBackEnd(&strm); assert(ret == Z_OK); fputs("inflateBack built-in memory routines\n", stderr); } /* do a raw inflate of data in hexadecimal with both inflate and inflateBack */ local int try(char *hex, char *id, int err) { int ret; unsigned len, size; unsigned char *in, *out, *win; char *prefix; z_stream strm; /* convert to hex */ in = h2b(hex, &len); assert(in != NULL); /* allocate work areas */ size = len << 3; out = malloc(size); assert(out != NULL); win = malloc(32768); assert(win != NULL); prefix = malloc(strlen(id) + 6); assert(prefix != NULL); /* first with inflate */ strcpy(prefix, id); strcat(prefix, "-late"); mem_setup(&strm); strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit2(&strm, err < 0 ? 47 : -15); assert(ret == Z_OK); strm.avail_in = len; strm.next_in = in; do { strm.avail_out = size; strm.next_out = out; ret = inflate(&strm, Z_TREES); assert(ret != Z_STREAM_ERROR && ret != Z_MEM_ERROR); if (ret == Z_DATA_ERROR || ret == Z_NEED_DICT) break; } while (strm.avail_in || strm.avail_out == 0); if (err) { assert(ret == Z_DATA_ERROR); assert(strcmp(id, strm.msg) == 0); } inflateEnd(&strm); mem_done(&strm, prefix); /* then with inflateBack */ if (err >= 0) { strcpy(prefix, id); strcat(prefix, "-back"); mem_setup(&strm); ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); strm.avail_in = len; strm.next_in = in; ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL); assert(ret != Z_STREAM_ERROR); if (err) { assert(ret == Z_DATA_ERROR); assert(strcmp(id, strm.msg) == 0); } inflateBackEnd(&strm); mem_done(&strm, prefix); } /* clean up */ free(prefix); free(win); free(out); free(in); return ret; } /* cover deflate data cases in both inflate() and inflateBack() */ local void cover_inflate(void) { try("0 0 0 0 0", "invalid stored block lengths", 1); try("3 0", "fixed", 0); try("6", "invalid block type", 1); try("1 1 0 fe ff 0", "stored", 0); try("fc 0 0", "too many length or distance symbols", 1); try("4 0 fe ff", "invalid code lengths set", 1); try("4 0 24 49 0", "invalid bit length repeat", 1); try("4 0 24 e9 ff ff", "invalid bit length repeat", 1); try("4 0 24 e9 ff 6d", "invalid code -- missing end-of-block", 1); try("4 80 49 92 24 49 92 24 71 ff ff 93 11 0", "invalid literal/lengths set", 1); try("4 80 49 92 24 49 92 24 f b4 ff ff c3 84", "invalid distances set", 1); try("4 c0 81 8 0 0 0 0 20 7f eb b 0 0", "invalid literal/length code", 1); try("2 7e ff ff", "invalid distance code", 1); try("c c0 81 0 0 0 0 0 90 ff 6b 4 0", "invalid distance too far back", 1); /* also trailer mismatch just in inflate() */ try("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 1", "incorrect data check", -1); try("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 1", "incorrect length check", -1); try("5 c0 21 d 0 0 0 80 b0 fe 6d 2f 91 6c", "pull 17", 0); try("5 e0 81 91 24 cb b2 2c 49 e2 f 2e 8b 9a 47 56 9f fb fe ec d2 ff 1f", "long code", 0); try("ed c0 1 1 0 0 0 40 20 ff 57 1b 42 2c 4f", "length extra", 0); try("ed cf c1 b1 2c 47 10 c4 30 fa 6f 35 1d 1 82 59 3d fb be 2e 2a fc f c", "long distance and extra", 0); try("ed c0 81 0 0 0 0 80 a0 fd a9 17 a9 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 6", "window end", 0); inf("2 8 20 80 0 3 0", "inflate_fast TYPE return", 0, -15, 258, Z_STREAM_END); inf("63 18 5 40 c 0", "window wrap", 3, -8, 300, Z_OK); } /* cover remaining lines in inftrees.c */ local void cover_trees(void) { int ret; unsigned bits; unsigned short lens[16], work[16]; code *next, table[ENOUGH_DISTS]; /* we need to call inflate_table() directly in order to manifest not- enough errors, since zlib insures that enough is always enough */ for (bits = 0; bits < 15; bits++) lens[bits] = (unsigned short)(bits + 1); lens[15] = 15; next = table; bits = 15; ret = inflate_table(DISTS, lens, 16, &next, &bits, work); assert(ret == 1); next = table; bits = 1; ret = inflate_table(DISTS, lens, 16, &next, &bits, work); assert(ret == 1); fputs("inflate_table not enough errors\n", stderr); } /* cover remaining inffast.c decoding and window copying */ local void cover_fast(void) { inf("e5 e0 81 ad 6d cb b2 2c c9 01 1e 59 63 ae 7d ee fb 4d fd b5 35 41 68" " ff 7f 0f 0 0 0", "fast length extra bits", 0, -8, 258, Z_DATA_ERROR); inf("25 fd 81 b5 6d 59 b6 6a 49 ea af 35 6 34 eb 8c b9 f6 b9 1e ef 67 49" " 50 fe ff ff 3f 0 0", "fast distance extra bits", 0, -8, 258, Z_DATA_ERROR); inf("3 7e 0 0 0 0 0", "fast invalid distance code", 0, -8, 258, Z_DATA_ERROR); inf("1b 7 0 0 0 0 0", "fast invalid literal/length code", 0, -8, 258, Z_DATA_ERROR); inf("d c7 1 ae eb 38 c 4 41 a0 87 72 de df fb 1f b8 36 b1 38 5d ff ff 0", "fast 2nd level codes and too far back", 0, -8, 258, Z_DATA_ERROR); inf("63 18 5 8c 10 8 0 0 0 0", "very common case", 0, -8, 259, Z_OK); inf("63 60 60 18 c9 0 8 18 18 18 26 c0 28 0 29 0 0 0", "contiguous and wrap around window", 6, -8, 259, Z_OK); inf("63 0 3 0 0 0 0 0", "copy direct from output", 0, -8, 259, Z_STREAM_END); } int main(void) { fprintf(stderr, "%s\n", zlibVersion()); cover_support(); cover_wrap(); cover_back(); cover_inflate(); cover_trees(); cover_fast(); return 0; } |
Added compat/zlib/test/minigzip.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 | /* minigzip.c -- simulate gzip using the zlib compression library * Copyright (C) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* * minigzip is a minimal implementation of the gzip utility. This is * only an example of using zlib and isn't meant to replace the * full-featured gzip. No attempt is made to deal with file systems * limiting names to 14 or 8+3 characters, etc... Error checking is * very limited. So use minigzip only for testing; use gzip for the * real thing. On MSDOS, use only on file names without extension * or in pipe mode. */ /* @(#) $Id$ */ #include "zlib.h" #include <stdio.h> #ifdef STDC # include <string.h> # include <stdlib.h> #endif #ifdef USE_MMAP # include <sys/types.h> # include <sys/mman.h> # include <sys/stat.h> #endif #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) # include <fcntl.h> # include <io.h> # ifdef UNDER_CE # include <stdlib.h> # endif # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) #else # define SET_BINARY_MODE(file) #endif #if defined(_MSC_VER) && _MSC_VER < 1900 # define snprintf _snprintf #endif #ifdef VMS # define unlink delete # define GZ_SUFFIX "-gz" #endif #ifdef RISCOS # define unlink remove # define GZ_SUFFIX "-gz" # define fileno(file) file->__file #endif #if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os # include <unix.h> /* for fileno */ #endif #if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE) #ifndef WIN32 /* unlink already in stdio.h for WIN32 */ extern int unlink(const char *); #endif #endif #if defined(UNDER_CE) # include <windows.h> # define perror(s) pwinerror(s) /* Map the Windows error number in ERROR to a locale-dependent error message string and return a pointer to it. Typically, the values for ERROR come from GetLastError. The string pointed to shall not be modified by the application, but may be overwritten by a subsequent call to strwinerror The strwinerror function does not change the current setting of GetLastError. */ static char *strwinerror (error) DWORD error; { static char buf[1024]; wchar_t *msgbuf; DWORD lasterr = GetLastError(); DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error, 0, /* Default language */ (LPVOID)&msgbuf, 0, NULL); if (chars != 0) { /* If there is an \r\n appended, zap it. */ if (chars >= 2 && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { chars -= 2; msgbuf[chars] = 0; } if (chars > sizeof (buf) - 1) { chars = sizeof (buf) - 1; msgbuf[chars] = 0; } wcstombs(buf, msgbuf, chars + 1); LocalFree(msgbuf); } else { sprintf(buf, "unknown win32 error (%ld)", error); } SetLastError(lasterr); return buf; } static void pwinerror (s) const char *s; { if (s && *s) fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ())); else fprintf(stderr, "%s\n", strwinerror(GetLastError ())); } #endif /* UNDER_CE */ #ifndef GZ_SUFFIX # define GZ_SUFFIX ".gz" #endif #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1) #define BUFLEN 16384 #define MAX_NAME_LEN 1024 #ifdef MAXSEG_64K # define local static /* Needed for systems with limitation on stack size. */ #else # define local #endif #ifdef Z_SOLO /* for Z_SOLO, create simplified gz* functions using deflate and inflate */ #if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE) # include <unistd.h> /* for unlink() */ #endif static void *myalloc(void *q, unsigned n, unsigned m) { (void)q; return calloc(n, m); } static void myfree(void *q, void *p) { (void)q; free(p); } typedef struct gzFile_s { FILE *file; int write; int err; char *msg; z_stream strm; } *gzFile; static gzFile gz_open(const char *path, int fd, const char *mode) { gzFile gz; int ret; gz = malloc(sizeof(struct gzFile_s)); if (gz == NULL) return NULL; gz->write = strchr(mode, 'w') != NULL; gz->strm.zalloc = myalloc; gz->strm.zfree = myfree; gz->strm.opaque = Z_NULL; if (gz->write) ret = deflateInit2(&(gz->strm), -1, 8, 15 + 16, 8, 0); else { gz->strm.next_in = 0; gz->strm.avail_in = Z_NULL; ret = inflateInit2(&(gz->strm), 15 + 16); } if (ret != Z_OK) { free(gz); return NULL; } gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") : fopen(path, gz->write ? "wb" : "rb"); if (gz->file == NULL) { gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm)); free(gz); return NULL; } gz->err = 0; gz->msg = ""; return gz; } static gzFile gzopen(const char *path, const char *mode) { return gz_open(path, -1, mode); } static gzFile gzdopen(int fd, const char *mode) { return gz_open(NULL, fd, mode); } static int gzwrite(gzFile gz, const void *buf, unsigned len) { z_stream *strm; unsigned char out[BUFLEN]; if (gz == NULL || !gz->write) return 0; strm = &(gz->strm); strm->next_in = (void *)buf; strm->avail_in = len; do { strm->next_out = out; strm->avail_out = BUFLEN; (void)deflate(strm, Z_NO_FLUSH); fwrite(out, 1, BUFLEN - strm->avail_out, gz->file); } while (strm->avail_out == 0); return len; } static int gzread(gzFile gz, void *buf, unsigned len) { int ret; unsigned got; unsigned char in[1]; z_stream *strm; if (gz == NULL || gz->write) return 0; if (gz->err) return 0; strm = &(gz->strm); strm->next_out = (void *)buf; strm->avail_out = len; do { got = fread(in, 1, 1, gz->file); if (got == 0) break; strm->next_in = in; strm->avail_in = 1; ret = inflate(strm, Z_NO_FLUSH); if (ret == Z_DATA_ERROR) { gz->err = Z_DATA_ERROR; gz->msg = strm->msg; return 0; } if (ret == Z_STREAM_END) inflateReset(strm); } while (strm->avail_out); return len - strm->avail_out; } static int gzclose(gzFile gz) { z_stream *strm; unsigned char out[BUFLEN]; if (gz == NULL) return Z_STREAM_ERROR; strm = &(gz->strm); if (gz->write) { strm->next_in = Z_NULL; strm->avail_in = 0; do { strm->next_out = out; strm->avail_out = BUFLEN; (void)deflate(strm, Z_FINISH); fwrite(out, 1, BUFLEN - strm->avail_out, gz->file); } while (strm->avail_out == 0); deflateEnd(strm); } else inflateEnd(strm); fclose(gz->file); free(gz); return Z_OK; } static const char *gzerror(gzFile gz, int *err) { *err = gz->err; return gz->msg; } #endif static char *prog; /* =========================================================================== * Display error message and exit */ static void error(const char *msg) { fprintf(stderr, "%s: %s\n", prog, msg); exit(1); } #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */ /* Try compressing the input file at once using mmap. Return Z_OK if * success, Z_ERRNO otherwise. */ static int gz_compress_mmap(FILE *in, gzFile out) { int len; int err; int ifd = fileno(in); caddr_t buf; /* mmap'ed buffer for the entire input file */ off_t buf_len; /* length of the input file */ struct stat sb; /* Determine the size of the file, needed for mmap: */ if (fstat(ifd, &sb) < 0) return Z_ERRNO; buf_len = sb.st_size; if (buf_len <= 0) return Z_ERRNO; /* Now do the actual mmap: */ buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0); if (buf == (caddr_t)(-1)) return Z_ERRNO; /* Compress the whole file at once: */ len = gzwrite(out, (char *)buf, (unsigned)buf_len); if (len != (int)buf_len) error(gzerror(out, &err)); munmap(buf, buf_len); fclose(in); if (gzclose(out) != Z_OK) error("failed gzclose"); return Z_OK; } #endif /* USE_MMAP */ /* =========================================================================== * Compress input to output then close both files. */ static void gz_compress(FILE *in, gzFile out) { local char buf[BUFLEN]; int len; int err; #ifdef USE_MMAP /* Try first compressing with mmap. If mmap fails (minigzip used in a * pipe), use the normal fread loop. */ if (gz_compress_mmap(in, out) == Z_OK) return; #endif for (;;) { len = (int)fread(buf, 1, sizeof(buf), in); if (ferror(in)) { perror("fread"); exit(1); } if (len == 0) break; if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err)); } fclose(in); if (gzclose(out) != Z_OK) error("failed gzclose"); } /* =========================================================================== * Uncompress input to output then close both files. */ static void gz_uncompress(gzFile in, FILE *out) { local char buf[BUFLEN]; int len; int err; for (;;) { len = gzread(in, buf, sizeof(buf)); if (len < 0) error (gzerror(in, &err)); if (len == 0) break; if ((int)fwrite(buf, 1, (unsigned)len, out) != len) { error("failed fwrite"); } } if (fclose(out)) error("failed fclose"); if (gzclose(in) != Z_OK) error("failed gzclose"); } /* =========================================================================== * Compress the given file: create a corresponding .gz file and remove the * original. */ static void file_compress(char *file, char *mode) { local char outfile[MAX_NAME_LEN]; FILE *in; gzFile out; if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) { fprintf(stderr, "%s: filename too long\n", prog); exit(1); } #if !defined(NO_snprintf) && !defined(NO_vsnprintf) snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX); #else strcpy(outfile, file); strcat(outfile, GZ_SUFFIX); #endif in = fopen(file, "rb"); if (in == NULL) { perror(file); exit(1); } out = gzopen(outfile, mode); if (out == NULL) { fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile); exit(1); } gz_compress(in, out); unlink(file); } /* =========================================================================== * Uncompress the given file and remove the original. */ static void file_uncompress(char *file) { local char buf[MAX_NAME_LEN]; char *infile, *outfile; FILE *out; gzFile in; z_size_t len = strlen(file); if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) { fprintf(stderr, "%s: filename too long\n", prog); exit(1); } #if !defined(NO_snprintf) && !defined(NO_vsnprintf) snprintf(buf, sizeof(buf), "%s", file); #else strcpy(buf, file); #endif if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) { infile = file; outfile = buf; outfile[len-3] = '\0'; } else { outfile = file; infile = buf; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX); #else strcat(infile, GZ_SUFFIX); #endif } in = gzopen(infile, "rb"); if (in == NULL) { fprintf(stderr, "%s: can't gzopen %s\n", prog, infile); exit(1); } out = fopen(outfile, "wb"); if (out == NULL) { perror(file); exit(1); } gz_uncompress(in, out); unlink(infile); } /* =========================================================================== * Usage: minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...] * -c : write to standard output * -d : decompress * -f : compress with Z_FILTERED * -h : compress with Z_HUFFMAN_ONLY * -r : compress with Z_RLE * -1 to -9 : compression level */ int main(int argc, char *argv[]) { int copyout = 0; int uncompr = 0; gzFile file; char *bname, outmode[20]; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) snprintf(outmode, sizeof(outmode), "%s", "wb6 "); #else strcpy(outmode, "wb6 "); #endif prog = argv[0]; bname = strrchr(argv[0], '/'); if (bname) bname++; else bname = argv[0]; argc--, argv++; if (!strcmp(bname, "gunzip")) uncompr = 1; else if (!strcmp(bname, "zcat")) copyout = uncompr = 1; while (argc > 0) { if (strcmp(*argv, "-c") == 0) copyout = 1; else if (strcmp(*argv, "-d") == 0) uncompr = 1; else if (strcmp(*argv, "-f") == 0) outmode[3] = 'f'; else if (strcmp(*argv, "-h") == 0) outmode[3] = 'h'; else if (strcmp(*argv, "-r") == 0) outmode[3] = 'R'; else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' && (*argv)[2] == 0) outmode[2] = (*argv)[1]; else break; argc--, argv++; } if (outmode[3] == ' ') outmode[3] = 0; if (argc == 0) { SET_BINARY_MODE(stdin); SET_BINARY_MODE(stdout); if (uncompr) { file = gzdopen(fileno(stdin), "rb"); if (file == NULL) error("can't gzdopen stdin"); gz_uncompress(file, stdout); } else { file = gzdopen(fileno(stdout), outmode); if (file == NULL) error("can't gzdopen stdout"); gz_compress(stdin, file); } } else { if (copyout) { SET_BINARY_MODE(stdout); } do { if (uncompr) { if (copyout) { file = gzopen(*argv, "rb"); if (file == NULL) fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv); else gz_uncompress(file, stdout); } else { file_uncompress(*argv); } } else { if (copyout) { FILE * in = fopen(*argv, "rb"); if (in == NULL) { perror(*argv); } else { file = gzdopen(fileno(stdout), outmode); if (file == NULL) error("can't gzdopen stdout"); gz_compress(in, file); } } else { file_compress(*argv, outmode); } } } while (argv++, --argc); } return 0; } |
Added compat/zlib/treebuild.xml.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" ?> <package name="zlib" version="1.3.1"> <library name="zlib" dlversion="1.3.1" dlname="z"> <property name="description"> zip compression library </property> <property name="include-target-dir" value="$(@PACKAGE/install-includedir)" /> <!-- fixme: not implemented yet --> <property name="compiler/c/inline" value="yes" /> <include-file name="zlib.h" scope="public" mode="644" /> <include-file name="zconf.h" scope="public" mode="644" /> <source name="adler32.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> </source> <source name="compress.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> </source> <source name="crc32.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="crc32.h" /> </source> <source name="gzclose.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="gzguts.h" /> </source> <source name="gzlib.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="gzguts.h" /> </source> <source name="gzread.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="gzguts.h" /> </source> <source name="gzwrite.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="gzguts.h" /> </source> <source name="uncompr.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> </source> <source name="deflate.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="zutil.h" /> <depend name="deflate.h" /> </source> <source name="trees.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="zutil.h" /> <depend name="deflate.h" /> <depend name="trees.h" /> </source> <source name="zutil.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="zutil.h" /> </source> <source name="inflate.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="zutil.h" /> <depend name="inftrees.h" /> <depend name="inflate.h" /> <depend name="inffast.h" /> </source> <source name="infback.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="zutil.h" /> <depend name="inftrees.h" /> <depend name="inflate.h" /> <depend name="inffast.h" /> </source> <source name="inftrees.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="zutil.h" /> <depend name="inftrees.h" /> </source> <source name="inffast.c"> <depend name="zlib.h" /> <depend name="zconf.h" /> <depend name="zutil.h" /> <depend name="inftrees.h" /> <depend name="inflate.h" /> <depend name="inffast.h" /> </source> </library> </package> <!-- CFLAGS=-O #CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 #CFLAGS=-g -DZLIB_DEBUG #CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ # -Wstrict-prototypes -Wmissing-prototypes # OBJA = # to use the asm code: make OBJA=match.o # match.o: match.S $(CPP) match.S > _match.s $(CC) -c _match.s mv _match.o match.o rm -f _match.s --> |
Added compat/zlib/trees.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 | /* trees.c -- output deflated data using Huffman coding * Copyright (C) 1995-2024 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process uses several Huffman trees. The more * common source values are represented by shorter bit sequences. * * Each code tree is stored in a compressed form which is itself * a Huffman encoding of the lengths of all the code strings (in * ascending order by source values). The actual code strings are * reconstructed from the lengths in the inflate process, as described * in the deflate specification. * * REFERENCES * * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc * * Storer, James A. * Data Compression: Methods and Theory, pp. 49-50. * Computer Science Press, 1988. ISBN 0-7167-8156-5. * * Sedgewick, R. * Algorithms, p290. * Addison-Wesley, 1983. ISBN 0-201-06672-6. */ /* @(#) $Id$ */ /* #define GEN_TREES_H */ #include "deflate.h" #ifdef ZLIB_DEBUG # include <ctype.h> #endif /* =========================================================================== * Constants */ #define MAX_BL_BITS 7 /* Bit length codes must not exceed MAX_BL_BITS bits */ #define END_BLOCK 256 /* end of block literal code */ #define REP_3_6 16 /* repeat previous bit length 3-6 times (2 bits of repeat count) */ #define REPZ_3_10 17 /* repeat a zero length 3-10 times (3 bits of repeat count) */ #define REPZ_11_138 18 /* repeat a zero length 11-138 times (7 bits of repeat count) */ local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ = {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}; local const int extra_dbits[D_CODES] /* extra bits for each distance code */ = {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}; local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; local const uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; /* The lengths of the bit length codes are sent in order of decreasing * probability, to avoid transmitting the lengths for unused bit length codes. */ /* =========================================================================== * Local data. These are initialized only once. */ #define DIST_CODE_LEN 512 /* see definition of array dist_code below */ #if defined(GEN_TREES_H) || !defined(STDC) /* non ANSI compilers may not accept trees.h */ local ct_data static_ltree[L_CODES+2]; /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see _tr_init * below). */ local ct_data static_dtree[D_CODES]; /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ uch _dist_code[DIST_CODE_LEN]; /* Distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ uch _length_code[MAX_MATCH-MIN_MATCH+1]; /* length code for each normalized match length (0 == MIN_MATCH) */ local int base_length[LENGTH_CODES]; /* First normalized length for each code (0 = MIN_MATCH) */ local int base_dist[D_CODES]; /* First normalized distance for each code (0 = distance of 1) */ #else # include "trees.h" #endif /* GEN_TREES_H */ struct static_tree_desc_s { const ct_data *static_tree; /* static tree or NULL */ const intf *extra_bits; /* extra bits for each code or NULL */ int extra_base; /* base index for extra_bits */ int elems; /* max number of elements in the tree */ int max_length; /* max bit length for the codes */ }; #ifdef NO_INIT_GLOBAL_POINTERS # define TCONST #else # define TCONST const #endif local TCONST static_tree_desc static_l_desc = {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; local TCONST static_tree_desc static_d_desc = {static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; local TCONST static_tree_desc static_bl_desc = {(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; /* =========================================================================== * Output a short LSB first on the stream. * IN assertion: there is enough room in pendingBuf. */ #define put_short(s, w) { \ put_byte(s, (uch)((w) & 0xff)); \ put_byte(s, (uch)((ush)(w) >> 8)); \ } /* =========================================================================== * Reverse the first len bits of a code, using straightforward code (a faster * method would use a table) * IN assertion: 1 <= len <= 15 */ local unsigned bi_reverse(unsigned code, int len) { register unsigned res = 0; do { res |= code & 1; code >>= 1, res <<= 1; } while (--len > 0); return res >> 1; } /* =========================================================================== * Flush the bit buffer, keeping at most 7 bits in it. */ local void bi_flush(deflate_state *s) { if (s->bi_valid == 16) { put_short(s, s->bi_buf); s->bi_buf = 0; s->bi_valid = 0; } else if (s->bi_valid >= 8) { put_byte(s, (Byte)s->bi_buf); s->bi_buf >>= 8; s->bi_valid -= 8; } } /* =========================================================================== * Flush the bit buffer and align the output on a byte boundary */ local void bi_windup(deflate_state *s) { if (s->bi_valid > 8) { put_short(s, s->bi_buf); } else if (s->bi_valid > 0) { put_byte(s, (Byte)s->bi_buf); } s->bi_buf = 0; s->bi_valid = 0; #ifdef ZLIB_DEBUG s->bits_sent = (s->bits_sent + 7) & ~7; #endif } /* =========================================================================== * Generate the codes for a given tree and bit counts (which need not be * optimal). * IN assertion: the array bl_count contains the bit length statistics for * the given tree and the field len is set for all tree elements. * OUT assertion: the field code is set for all tree elements of non * zero code length. */ local void gen_codes(ct_data *tree, int max_code, ushf *bl_count) { ush next_code[MAX_BITS+1]; /* next code value for each bit length */ unsigned code = 0; /* running code value */ int bits; /* bit index */ int n; /* code index */ /* The distribution counts are first used to generate the code values * without bit reversal. */ for (bits = 1; bits <= MAX_BITS; bits++) { code = (code + bl_count[bits - 1]) << 1; next_code[bits] = (ush)code; } /* Check that the bit counts in bl_count are consistent. The last code * must be all ones. */ Assert (code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, "inconsistent bit counts"); Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); for (n = 0; n <= max_code; n++) { int len = tree[n].Len; if (len == 0) continue; /* Now reverse the bits */ tree[n].Code = (ush)bi_reverse(next_code[len]++, len); Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len] - 1)); } } #ifdef GEN_TREES_H local void gen_trees_header(void); #endif #ifndef ZLIB_DEBUG # define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) /* Send a code of the given tree. c and tree must not have side effects */ #else /* !ZLIB_DEBUG */ # define send_code(s, c, tree) \ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ send_bits(s, tree[c].Code, tree[c].Len); } #endif /* =========================================================================== * Send a value on a given number of bits. * IN assertion: length <= 16 and value fits in length bits. */ #ifdef ZLIB_DEBUG local void send_bits(deflate_state *s, int value, int length) { Tracevv((stderr," l %2d v %4x ", length, value)); Assert(length > 0 && length <= 15, "invalid length"); s->bits_sent += (ulg)length; /* If not enough room in bi_buf, use (valid) bits from bi_buf and * (16 - bi_valid) bits from value, leaving (width - (16 - bi_valid)) * unused bits in value. */ if (s->bi_valid > (int)Buf_size - length) { s->bi_buf |= (ush)value << s->bi_valid; put_short(s, s->bi_buf); s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); s->bi_valid += length - Buf_size; } else { s->bi_buf |= (ush)value << s->bi_valid; s->bi_valid += length; } } #else /* !ZLIB_DEBUG */ #define send_bits(s, value, length) \ { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ int val = (int)value;\ s->bi_buf |= (ush)val << s->bi_valid;\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ s->bi_valid += len - Buf_size;\ } else {\ s->bi_buf |= (ush)(value) << s->bi_valid;\ s->bi_valid += len;\ }\ } #endif /* ZLIB_DEBUG */ /* the arguments must not have side effects */ /* =========================================================================== * Initialize the various 'constant' tables. */ local void tr_static_init(void) { #if defined(GEN_TREES_H) || !defined(STDC) static int static_init_done = 0; int n; /* iterates over tree elements */ int bits; /* bit counter */ int length; /* length value */ int code; /* code value */ int dist; /* distance index */ ush bl_count[MAX_BITS+1]; /* number of codes at each bit length for an optimal tree */ if (static_init_done) return; /* For some embedded targets, global variables are not initialized: */ #ifdef NO_INIT_GLOBAL_POINTERS static_l_desc.static_tree = static_ltree; static_l_desc.extra_bits = extra_lbits; static_d_desc.static_tree = static_dtree; static_d_desc.extra_bits = extra_dbits; static_bl_desc.extra_bits = extra_blbits; #endif /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; for (code = 0; code < LENGTH_CODES-1; code++) { base_length[code] = length; for (n = 0; n < (1 << extra_lbits[code]); n++) { _length_code[length++] = (uch)code; } } Assert (length == 256, "tr_static_init: length != 256"); /* Note that the length 255 (match length 258) can be represented * in two different ways: code 284 + 5 bits or code 285, so we * overwrite length_code[255] to use the best encoding: */ _length_code[length - 1] = (uch)code; /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ dist = 0; for (code = 0 ; code < 16; code++) { base_dist[code] = dist; for (n = 0; n < (1 << extra_dbits[code]); n++) { _dist_code[dist++] = (uch)code; } } Assert (dist == 256, "tr_static_init: dist != 256"); dist >>= 7; /* from now on, all distances are divided by 128 */ for ( ; code < D_CODES; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { _dist_code[256 + dist++] = (uch)code; } } Assert (dist == 256, "tr_static_init: 256 + dist != 512"); /* Construct the codes of the static literal tree */ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; n = 0; while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; /* Codes 286 and 287 do not exist, but we must include them in the * tree construction to get a canonical Huffman tree (longest code * all ones) */ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); /* The static distance tree is trivial: */ for (n = 0; n < D_CODES; n++) { static_dtree[n].Len = 5; static_dtree[n].Code = bi_reverse((unsigned)n, 5); } static_init_done = 1; # ifdef GEN_TREES_H gen_trees_header(); # endif #endif /* defined(GEN_TREES_H) || !defined(STDC) */ } /* =========================================================================== * Generate the file trees.h describing the static trees. */ #ifdef GEN_TREES_H # ifndef ZLIB_DEBUG # include <stdio.h> # endif # define SEPARATOR(i, last, width) \ ((i) == (last)? "\n};\n\n" : \ ((i) % (width) == (width) - 1 ? ",\n" : ", ")) void gen_trees_header(void) { FILE *header = fopen("trees.h", "w"); int i; Assert (header != NULL, "Can't open trees.h"); fprintf(header, "/* header created automatically with -DGEN_TREES_H */\n\n"); fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); for (i = 0; i < L_CODES+2; i++) { fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); } fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); for (i = 0; i < D_CODES; i++) { fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); } fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); for (i = 0; i < DIST_CODE_LEN; i++) { fprintf(header, "%2u%s", _dist_code[i], SEPARATOR(i, DIST_CODE_LEN-1, 20)); } fprintf(header, "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { fprintf(header, "%2u%s", _length_code[i], SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); } fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); for (i = 0; i < LENGTH_CODES; i++) { fprintf(header, "%1u%s", base_length[i], SEPARATOR(i, LENGTH_CODES-1, 20)); } fprintf(header, "local const int base_dist[D_CODES] = {\n"); for (i = 0; i < D_CODES; i++) { fprintf(header, "%5u%s", base_dist[i], SEPARATOR(i, D_CODES-1, 10)); } fclose(header); } #endif /* GEN_TREES_H */ /* =========================================================================== * Initialize a new block. */ local void init_block(deflate_state *s) { int n; /* iterates over tree elements */ /* Initialize the trees. */ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; s->dyn_ltree[END_BLOCK].Freq = 1; s->opt_len = s->static_len = 0L; s->sym_next = s->matches = 0; } /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ void ZLIB_INTERNAL _tr_init(deflate_state *s) { tr_static_init(); s->l_desc.dyn_tree = s->dyn_ltree; s->l_desc.stat_desc = &static_l_desc; s->d_desc.dyn_tree = s->dyn_dtree; s->d_desc.stat_desc = &static_d_desc; s->bl_desc.dyn_tree = s->bl_tree; s->bl_desc.stat_desc = &static_bl_desc; s->bi_buf = 0; s->bi_valid = 0; #ifdef ZLIB_DEBUG s->compressed_len = 0L; s->bits_sent = 0L; #endif /* Initialize the first block of the first file: */ init_block(s); } #define SMALLEST 1 /* Index within the heap array of least frequent node in the Huffman tree */ /* =========================================================================== * Remove the smallest element from the heap and recreate the heap with * one less element. Updates heap and heap_len. */ #define pqremove(s, tree, top) \ {\ top = s->heap[SMALLEST]; \ s->heap[SMALLEST] = s->heap[s->heap_len--]; \ pqdownheap(s, tree, SMALLEST); \ } /* =========================================================================== * Compares to subtrees, using the tree depth as tie breaker when * the subtrees have equal frequency. This minimizes the worst case length. */ #define smaller(tree, n, m, depth) \ (tree[n].Freq < tree[m].Freq || \ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) /* =========================================================================== * Restore the heap property by moving down the tree starting at node k, * exchanging a node with the smallest of its two sons if necessary, stopping * when the heap property is re-established (each father smaller than its * two sons). */ local void pqdownheap(deflate_state *s, ct_data *tree, int k) { int v = s->heap[k]; int j = k << 1; /* left son of k */ while (j <= s->heap_len) { /* Set j to the smallest of the two sons: */ if (j < s->heap_len && smaller(tree, s->heap[j + 1], s->heap[j], s->depth)) { j++; } /* Exit if v is smaller than both sons */ if (smaller(tree, v, s->heap[j], s->depth)) break; /* Exchange v with the smallest son */ s->heap[k] = s->heap[j]; k = j; /* And continue down the tree, setting j to the left son of k */ j <<= 1; } s->heap[k] = v; } /* =========================================================================== * Compute the optimal bit lengths for a tree and update the total bit length * for the current block. * IN assertion: the fields freq and dad are set, heap[heap_max] and * above are the tree nodes sorted by increasing frequency. * OUT assertions: the field len is set to the optimal bit length, the * array bl_count contains the frequencies for each bit length. * The length opt_len is updated; static_len is also updated if stree is * not null. */ local void gen_bitlen(deflate_state *s, tree_desc *desc) { ct_data *tree = desc->dyn_tree; int max_code = desc->max_code; const ct_data *stree = desc->stat_desc->static_tree; const intf *extra = desc->stat_desc->extra_bits; int base = desc->stat_desc->extra_base; int max_length = desc->stat_desc->max_length; int h; /* heap index */ int n, m; /* iterate over the tree elements */ int bits; /* bit length */ int xbits; /* extra bits */ ush f; /* frequency */ int overflow = 0; /* number of elements with bit length too large */ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; /* In a first pass, compute the optimal bit lengths (which may * overflow in the case of the bit length tree). */ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ for (h = s->heap_max + 1; h < HEAP_SIZE; h++) { n = s->heap[h]; bits = tree[tree[n].Dad].Len + 1; if (bits > max_length) bits = max_length, overflow++; tree[n].Len = (ush)bits; /* We overwrite tree[n].Dad which is no longer needed */ if (n > max_code) continue; /* not a leaf node */ s->bl_count[bits]++; xbits = 0; if (n >= base) xbits = extra[n - base]; f = tree[n].Freq; s->opt_len += (ulg)f * (unsigned)(bits + xbits); if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits); } if (overflow == 0) return; Tracev((stderr,"\nbit length overflow\n")); /* This happens for example on obj2 and pic of the Calgary corpus */ /* Find the first bit length which could increase: */ do { bits = max_length - 1; while (s->bl_count[bits] == 0) bits--; s->bl_count[bits]--; /* move one leaf down the tree */ s->bl_count[bits + 1] += 2; /* move one overflow item as its brother */ s->bl_count[max_length]--; /* The brother of the overflow item also moves one step up, * but this does not affect bl_count[max_length] */ overflow -= 2; } while (overflow > 0); /* Now recompute all bit lengths, scanning in increasing frequency. * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all * lengths instead of fixing only the wrong ones. This idea is taken * from 'ar' written by Haruhiko Okumura.) */ for (bits = max_length; bits != 0; bits--) { n = s->bl_count[bits]; while (n != 0) { m = s->heap[--h]; if (m > max_code) continue; if ((unsigned) tree[m].Len != (unsigned) bits) { Tracev((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); s->opt_len += ((ulg)bits - tree[m].Len) * tree[m].Freq; tree[m].Len = (ush)bits; } n--; } } } #ifdef DUMP_BL_TREE # include <stdio.h> #endif /* =========================================================================== * Construct one Huffman tree and assigns the code bit strings and lengths. * Update the total bit length for the current block. * IN assertion: the field freq is set for all tree elements. * OUT assertions: the fields len and code are set to the optimal bit length * and corresponding code. The length opt_len is updated; static_len is * also updated if stree is not null. The field max_code is set. */ local void build_tree(deflate_state *s, tree_desc *desc) { ct_data *tree = desc->dyn_tree; const ct_data *stree = desc->stat_desc->static_tree; int elems = desc->stat_desc->elems; int n, m; /* iterate over heap elements */ int max_code = -1; /* largest code with non zero frequency */ int node; /* new node being created */ /* Construct the initial heap, with least frequent element in * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n + 1]. * heap[0] is not used. */ s->heap_len = 0, s->heap_max = HEAP_SIZE; for (n = 0; n < elems; n++) { if (tree[n].Freq != 0) { s->heap[++(s->heap_len)] = max_code = n; s->depth[n] = 0; } else { tree[n].Len = 0; } } /* The pkzip format requires that at least one distance code exists, * and that at least one bit should be sent even if there is only one * possible code. So to avoid special checks later on we force at least * two codes of non zero frequency. */ while (s->heap_len < 2) { node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); tree[node].Freq = 1; s->depth[node] = 0; s->opt_len--; if (stree) s->static_len -= stree[node].Len; /* node is 0 or 1 so it does not have extra bits */ } desc->max_code = max_code; /* The elements heap[heap_len/2 + 1 .. heap_len] are leaves of the tree, * establish sub-heaps of increasing lengths: */ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); /* Construct the Huffman tree by repeatedly combining the least two * frequent nodes. */ node = elems; /* next internal node of the tree */ do { pqremove(s, tree, n); /* n = node of least frequency */ m = s->heap[SMALLEST]; /* m = node of next least frequency */ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ s->heap[--(s->heap_max)] = m; /* Create a new node father of n and m */ tree[node].Freq = tree[n].Freq + tree[m].Freq; s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? s->depth[n] : s->depth[m]) + 1); tree[n].Dad = tree[m].Dad = (ush)node; #ifdef DUMP_BL_TREE if (tree == s->bl_tree) { fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); } #endif /* and insert the new node in the heap */ s->heap[SMALLEST] = node++; pqdownheap(s, tree, SMALLEST); } while (s->heap_len >= 2); s->heap[--(s->heap_max)] = s->heap[SMALLEST]; /* At this point, the fields freq and dad are set. We can now * generate the bit lengths. */ gen_bitlen(s, (tree_desc *)desc); /* The field len is now set, we can generate the bit codes */ gen_codes ((ct_data *)tree, max_code, s->bl_count); } /* =========================================================================== * Scan a literal or distance tree to determine the frequencies of the codes * in the bit length tree. */ local void scan_tree(deflate_state *s, ct_data *tree, int max_code) { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ if (nextlen == 0) max_count = 138, min_count = 3; tree[max_code + 1].Len = (ush)0xffff; /* guard */ for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n + 1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { s->bl_tree[curlen].Freq += count; } else if (curlen != 0) { if (curlen != prevlen) s->bl_tree[curlen].Freq++; s->bl_tree[REP_3_6].Freq++; } else if (count <= 10) { s->bl_tree[REPZ_3_10].Freq++; } else { s->bl_tree[REPZ_11_138].Freq++; } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Send a literal or distance tree in compressed form, using the codes in * bl_tree. */ local void send_tree(deflate_state *s, ct_data *tree, int max_code) { int n; /* iterates over all tree elements */ int prevlen = -1; /* last emitted length */ int curlen; /* length of current code */ int nextlen = tree[0].Len; /* length of next code */ int count = 0; /* repeat count of the current code */ int max_count = 7; /* max repeat count */ int min_count = 4; /* min repeat count */ /* tree[max_code + 1].Len = -1; */ /* guard already set */ if (nextlen == 0) max_count = 138, min_count = 3; for (n = 0; n <= max_code; n++) { curlen = nextlen; nextlen = tree[n + 1].Len; if (++count < max_count && curlen == nextlen) { continue; } else if (count < min_count) { do { send_code(s, curlen, s->bl_tree); } while (--count != 0); } else if (curlen != 0) { if (curlen != prevlen) { send_code(s, curlen, s->bl_tree); count--; } Assert(count >= 3 && count <= 6, " 3_6?"); send_code(s, REP_3_6, s->bl_tree); send_bits(s, count - 3, 2); } else if (count <= 10) { send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count - 3, 3); } else { send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count - 11, 7); } count = 0; prevlen = curlen; if (nextlen == 0) { max_count = 138, min_count = 3; } else if (curlen == nextlen) { max_count = 6, min_count = 3; } else { max_count = 7, min_count = 4; } } } /* =========================================================================== * Construct the Huffman tree for the bit lengths and return the index in * bl_order of the last bit length code to send. */ local int build_bl_tree(deflate_state *s) { int max_blindex; /* index of last bit length code of non zero freq */ /* Determine the bit length frequencies for literal and distance trees */ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); /* Build the bit length tree: */ build_tree(s, (tree_desc *)(&(s->bl_desc))); /* opt_len now includes the length of the tree representations, except the * lengths of the bit lengths codes and the 5 + 5 + 4 bits for the counts. */ /* Determine the number of bit length codes to send. The pkzip format * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; } /* Update opt_len to include the bit length tree and counts */ s->opt_len += 3*((ulg)max_blindex + 1) + 5 + 5 + 4; Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", s->opt_len, s->static_len)); return max_blindex; } /* =========================================================================== * Send the header for a block using dynamic Huffman trees: the counts, the * lengths of the bit length codes, the literal tree and the distance tree. * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. */ local void send_all_trees(deflate_state *s, int lcodes, int dcodes, int blcodes) { int rank; /* index in bl_order */ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, "too many codes"); Tracev((stderr, "\nbl counts: ")); send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ send_bits(s, dcodes - 1, 5); send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ for (rank = 0; rank < blcodes; rank++) { Tracev((stderr, "\nbl code %2d ", bl_order[rank])); send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); } Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_ltree, lcodes - 1); /* literal tree */ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); send_tree(s, (ct_data *)s->dyn_dtree, dcodes - 1); /* distance tree */ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); } /* =========================================================================== * Send a stored block */ void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, ulg stored_len, int last) { send_bits(s, (STORED_BLOCK<<1) + last, 3); /* send block type */ bi_windup(s); /* align on byte boundary */ put_short(s, (ush)stored_len); put_short(s, (ush)~stored_len); if (stored_len) zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len); s->pending += stored_len; #ifdef ZLIB_DEBUG s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; s->compressed_len += (stored_len + 4) << 3; s->bits_sent += 2*16; s->bits_sent += stored_len << 3; #endif } /* =========================================================================== * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) */ void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s) { bi_flush(s); } /* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ void ZLIB_INTERNAL _tr_align(deflate_state *s) { send_bits(s, STATIC_TREES<<1, 3); send_code(s, END_BLOCK, static_ltree); #ifdef ZLIB_DEBUG s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ #endif bi_flush(s); } /* =========================================================================== * Send the block data compressed using the given Huffman trees */ local void compress_block(deflate_state *s, const ct_data *ltree, const ct_data *dtree) { unsigned dist; /* distance of matched string */ int lc; /* match length or unmatched char (if dist == 0) */ unsigned sx = 0; /* running index in symbol buffers */ unsigned code; /* the code to send */ int extra; /* number of extra bits to send */ if (s->sym_next != 0) do { #ifdef LIT_MEM dist = s->d_buf[sx]; lc = s->l_buf[sx++]; #else dist = s->sym_buf[sx++] & 0xff; dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8; lc = s->sym_buf[sx++]; #endif if (dist == 0) { send_code(s, lc, ltree); /* send a literal byte */ Tracecv(isgraph(lc), (stderr," '%c' ", lc)); } else { /* Here, lc is the match length - MIN_MATCH */ code = _length_code[lc]; send_code(s, code + LITERALS + 1, ltree); /* send length code */ extra = extra_lbits[code]; if (extra != 0) { lc -= base_length[code]; send_bits(s, lc, extra); /* send the extra length bits */ } dist--; /* dist is now the match distance - 1 */ code = d_code(dist); Assert (code < D_CODES, "bad d_code"); send_code(s, code, dtree); /* send the distance code */ extra = extra_dbits[code]; if (extra != 0) { dist -= (unsigned)base_dist[code]; send_bits(s, dist, extra); /* send the extra distance bits */ } } /* literal or match pair ? */ /* Check for no overlay of pending_buf on needed symbols */ #ifdef LIT_MEM Assert(s->pending < 2 * (s->lit_bufsize + sx), "pendingBuf overflow"); #else Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); #endif } while (sx < s->sym_next); send_code(s, END_BLOCK, ltree); } /* =========================================================================== * Check if the data type is TEXT or BINARY, using the following algorithm: * - TEXT if the two conditions below are satisfied: * a) There are no non-portable control characters belonging to the * "block list" (0..6, 14..25, 28..31). * b) There is at least one printable character belonging to the * "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). * - BINARY otherwise. * - The following partially-portable control characters form a * "gray list" that is ignored in this detection algorithm: * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). * IN assertion: the fields Freq of dyn_ltree are set. */ local int detect_data_type(deflate_state *s) { /* block_mask is the bit mask of block-listed bytes * set bits 0..6, 14..25, and 28..31 * 0xf3ffc07f = binary 11110011111111111100000001111111 */ unsigned long block_mask = 0xf3ffc07fUL; int n; /* Check for non-textual ("block-listed") bytes. */ for (n = 0; n <= 31; n++, block_mask >>= 1) if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0)) return Z_BINARY; /* Check for textual ("allow-listed") bytes. */ if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 || s->dyn_ltree[13].Freq != 0) return Z_TEXT; for (n = 32; n < LITERALS; n++) if (s->dyn_ltree[n].Freq != 0) return Z_TEXT; /* There are no "block-listed" or "allow-listed" bytes: * this stream either is empty or has tolerated ("gray-listed") bytes only. */ return Z_BINARY; } /* =========================================================================== * Determine the best encoding for the current block: dynamic trees, static * trees or store, and write out the encoded block. */ void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, ulg stored_len, int last) { ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ int max_blindex = 0; /* index of last bit length code of non zero freq */ /* Build the Huffman trees unless a stored block is forced */ if (s->level > 0) { /* Check if the file is binary or text */ if (s->strm->data_type == Z_UNKNOWN) s->strm->data_type = detect_data_type(s); /* Construct the literal and distance trees */ build_tree(s, (tree_desc *)(&(s->l_desc))); Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, s->static_len)); build_tree(s, (tree_desc *)(&(s->d_desc))); Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, s->static_len)); /* At this point, opt_len and static_len are the total bit lengths of * the compressed block data, excluding the tree representations. */ /* Build the bit length tree for the above two trees, and get the index * in bl_order of the last bit length code to send. */ max_blindex = build_bl_tree(s); /* Determine the best encoding. Compute the block lengths in bytes. */ opt_lenb = (s->opt_len + 3 + 7) >> 3; static_lenb = (s->static_len + 3 + 7) >> 3; Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, s->sym_next / 3)); #ifndef FORCE_STATIC if (static_lenb <= opt_lenb || s->strategy == Z_FIXED) #endif opt_lenb = static_lenb; } else { Assert(buf != (char*)0, "lost buf"); opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ } #ifdef FORCE_STORED if (buf != (char*)0) { /* force stored block */ #else if (stored_len + 4 <= opt_lenb && buf != (char*)0) { /* 4: two words for the lengths */ #endif /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. * Otherwise we can't have processed more than WSIZE input bytes since * the last block flush, because compression would have been * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ _tr_stored_block(s, buf, stored_len, last); } else if (static_lenb == opt_lenb) { send_bits(s, (STATIC_TREES<<1) + last, 3); compress_block(s, (const ct_data *)static_ltree, (const ct_data *)static_dtree); #ifdef ZLIB_DEBUG s->compressed_len += 3 + s->static_len; #endif } else { send_bits(s, (DYN_TREES<<1) + last, 3); send_all_trees(s, s->l_desc.max_code + 1, s->d_desc.max_code + 1, max_blindex + 1); compress_block(s, (const ct_data *)s->dyn_ltree, (const ct_data *)s->dyn_dtree); #ifdef ZLIB_DEBUG s->compressed_len += 3 + s->opt_len; #endif } Assert (s->compressed_len == s->bits_sent, "bad compressed size"); /* The above check is made mod 2^32, for files larger than 512 MB * and uLong implemented on 32 bits. */ init_block(s); if (last) { bi_windup(s); #ifdef ZLIB_DEBUG s->compressed_len += 7; /* align on byte boundary */ #endif } Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len >> 3, s->compressed_len - 7*last)); } /* =========================================================================== * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc) { #ifdef LIT_MEM s->d_buf[s->sym_next] = (ush)dist; s->l_buf[s->sym_next++] = (uch)lc; #else s->sym_buf[s->sym_next++] = (uch)dist; s->sym_buf[s->sym_next++] = (uch)(dist >> 8); s->sym_buf[s->sym_next++] = (uch)lc; #endif if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; } else { s->matches++; /* Here, lc is the match length - MIN_MATCH */ dist--; /* dist = match distance - 1 */ Assert((ush)dist < (ush)MAX_DIST(s) && (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); s->dyn_ltree[_length_code[lc] + LITERALS + 1].Freq++; s->dyn_dtree[d_code(dist)].Freq++; } return (s->sym_next == s->sym_end); } |
Added compat/zlib/trees.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* header created automatically with -DGEN_TREES_H */ local const ct_data static_ltree[L_CODES+2] = { {{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, {{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, {{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, {{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, {{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, {{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, {{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, {{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, {{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, {{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, {{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, {{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, {{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, {{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, {{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, {{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, {{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, {{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, {{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, {{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, {{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, {{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, {{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, {{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, {{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, {{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, {{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, {{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, {{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, {{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, {{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, {{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, {{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, {{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, {{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, {{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, {{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, {{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, {{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, {{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, {{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, {{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, {{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, {{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, {{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, {{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, {{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, {{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, {{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, {{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, {{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, {{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, {{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, {{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, {{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, {{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, {{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, {{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} }; local const ct_data static_dtree[D_CODES] = { {{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, {{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, {{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, {{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, {{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, {{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} }; const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { 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, 0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 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, 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, 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, 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 }; const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 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, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 }; local const int base_length[LENGTH_CODES] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0 }; local const int base_dist[D_CODES] = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 }; |
Added compat/zlib/uncompr.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 | /* uncompr.c -- decompress a memory buffer * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #define ZLIB_INTERNAL #include "zlib.h" /* =========================================================================== Decompresses the source buffer into the destination buffer. *sourceLen is the byte length of the source buffer. Upon entry, *destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, *destLen is the size of the decompressed data and *sourceLen is the number of source bytes consumed. Upon return, source + *sourceLen points to the first unused input byte. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, or Z_DATA_ERROR if the input data was corrupted, including if the input data is an incomplete zlib stream. */ int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source, uLong *sourceLen) { z_stream stream; int err; const uInt max = (uInt)-1; uLong len, left; Byte buf[1]; /* for detection of incomplete stream when *destLen == 0 */ len = *sourceLen; if (*destLen) { left = *destLen; *destLen = 0; } else { left = 1; dest = buf; } stream.next_in = (z_const Bytef *)source; stream.avail_in = 0; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; stream.opaque = (voidpf)0; err = inflateInit(&stream); if (err != Z_OK) return err; stream.next_out = dest; stream.avail_out = 0; do { if (stream.avail_out == 0) { stream.avail_out = left > (uLong)max ? max : (uInt)left; left -= stream.avail_out; } if (stream.avail_in == 0) { stream.avail_in = len > (uLong)max ? max : (uInt)len; len -= stream.avail_in; } err = inflate(&stream, Z_NO_FLUSH); } while (err == Z_OK); *sourceLen -= len + stream.avail_in; if (dest != buf) *destLen = stream.total_out; else if (stream.total_out && err == Z_BUF_ERROR) left = 1; inflateEnd(&stream); return err == Z_STREAM_END ? Z_OK : err == Z_NEED_DICT ? Z_DATA_ERROR : err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR : err; } int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen) { return uncompress2(dest, destLen, source, &sourceLen); } |
Added compat/zlib/watcom/watcom_f.mak.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib # OpenWatcom flat model # Last updated: 28-Dec-2005 # To use, do "wmake -f watcom_f.mak" C_SOURCE = adler32.c compress.c crc32.c deflate.c & gzclose.c gzlib.c gzread.c gzwrite.c & infback.c inffast.c inflate.c inftrees.c & trees.c uncompr.c zutil.c OBJS = adler32.obj compress.obj crc32.obj deflate.obj & gzclose.obj gzlib.obj gzread.obj gzwrite.obj & infback.obj inffast.obj inflate.obj inftrees.obj & trees.obj uncompr.obj zutil.obj CC = wcc386 LINKER = wcl386 CFLAGS = -zq -mf -3r -fp3 -s -bt=dos -oilrtfm -fr=nul -wx ZLIB_LIB = zlib_f.lib .C.OBJ: $(CC) $(CFLAGS) $[@ all: $(ZLIB_LIB) example.exe minigzip.exe $(ZLIB_LIB): $(OBJS) wlib -b -c $(ZLIB_LIB) -+adler32.obj -+compress.obj -+crc32.obj wlib -b -c $(ZLIB_LIB) -+gzclose.obj -+gzlib.obj -+gzread.obj -+gzwrite.obj wlib -b -c $(ZLIB_LIB) -+deflate.obj -+infback.obj wlib -b -c $(ZLIB_LIB) -+inffast.obj -+inflate.obj -+inftrees.obj wlib -b -c $(ZLIB_LIB) -+trees.obj -+uncompr.obj -+zutil.obj example.exe: $(ZLIB_LIB) example.obj $(LINKER) -ldos32a -fe=example.exe example.obj $(ZLIB_LIB) minigzip.exe: $(ZLIB_LIB) minigzip.obj $(LINKER) -ldos32a -fe=minigzip.exe minigzip.obj $(ZLIB_LIB) clean: .SYMBOLIC del *.obj del $(ZLIB_LIB) @echo Cleaning done |
Added compat/zlib/watcom/watcom_l.mak.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib # OpenWatcom large model # Last updated: 28-Dec-2005 # To use, do "wmake -f watcom_l.mak" C_SOURCE = adler32.c compress.c crc32.c deflate.c & gzclose.c gzlib.c gzread.c gzwrite.c & infback.c inffast.c inflate.c inftrees.c & trees.c uncompr.c zutil.c OBJS = adler32.obj compress.obj crc32.obj deflate.obj & gzclose.obj gzlib.obj gzread.obj gzwrite.obj & infback.obj inffast.obj inflate.obj inftrees.obj & trees.obj uncompr.obj zutil.obj CC = wcc LINKER = wcl CFLAGS = -zq -ml -s -bt=dos -oilrtfm -fr=nul -wx ZLIB_LIB = zlib_l.lib .C.OBJ: $(CC) $(CFLAGS) $[@ all: $(ZLIB_LIB) example.exe minigzip.exe $(ZLIB_LIB): $(OBJS) wlib -b -c $(ZLIB_LIB) -+adler32.obj -+compress.obj -+crc32.obj wlib -b -c $(ZLIB_LIB) -+gzclose.obj -+gzlib.obj -+gzread.obj -+gzwrite.obj wlib -b -c $(ZLIB_LIB) -+deflate.obj -+infback.obj wlib -b -c $(ZLIB_LIB) -+inffast.obj -+inflate.obj -+inftrees.obj wlib -b -c $(ZLIB_LIB) -+trees.obj -+uncompr.obj -+zutil.obj example.exe: $(ZLIB_LIB) example.obj $(LINKER) -fe=example.exe example.obj $(ZLIB_LIB) minigzip.exe: $(ZLIB_LIB) minigzip.obj $(LINKER) -fe=minigzip.exe minigzip.obj $(ZLIB_LIB) clean: .SYMBOLIC del *.obj del $(ZLIB_LIB) @echo Cleaning done |
Added compat/zlib/win32/DLL_FAQ.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 | Frequently Asked Questions about ZLIB1.DLL This document describes the design, the rationale, and the usage of the common DLL build of zlib, named ZLIB1.DLL. If you have general questions about zlib, you should see the file "FAQ" found in the zlib distribution, or at the following location: http://www.gzip.org/zlib/zlib_faq.html 1. What is ZLIB1.DLL, and how can I get it? - ZLIB1.DLL is the common build of zlib as a DLL. (Please remark the character '1' in the name.) Applications that link to ZLIB1.DLL can rely on the following specification: * The exported symbols are exclusively defined in the source files "zlib.h" and "zlib.def", found in an official zlib source distribution. * The symbols are exported by name, not by ordinal. * The exported names are undecorated. * The calling convention of functions is "C" (CDECL). * The ZLIB1.DLL binary is linked to MSVCRT.DLL. The archive in which ZLIB1.DLL is bundled contains compiled test programs that must run with a valid build of ZLIB1.DLL. It is recommended to download the prebuilt DLL from the zlib web site, instead of building it yourself, to avoid potential incompatibilities that could be introduced by your compiler and build settings. If you do build the DLL yourself, please make sure that it complies with all the above requirements, and it runs with the precompiled test programs, bundled with the original ZLIB1.DLL distribution. If, for any reason, you need to build an incompatible DLL, please use a different file name. 2. Why did you change the name of the DLL to ZLIB1.DLL? What happened to the old ZLIB.DLL? - The old ZLIB.DLL, built from zlib-1.1.4 or earlier, required compilation settings that were incompatible to those used by a static build. The DLL settings were supposed to be enabled by defining the macro ZLIB_DLL, before including "zlib.h". Incorrect handling of this macro was silently accepted at build time, resulting in two major problems: * ZLIB_DLL was missing from the old makefile. When building the DLL, not all people added it to the build options. In consequence, incompatible incarnations of ZLIB.DLL started to circulate around the net. * When switching from using the static library to using the DLL, applications had to define the ZLIB_DLL macro and to recompile all the sources that contained calls to zlib functions. Failure to do so resulted in creating binaries that were unable to run with the official ZLIB.DLL build. The only possible solution that we could foresee was to make a binary-incompatible change in the DLL interface, in order to remove the dependency on the ZLIB_DLL macro, and to release the new DLL under a different name. We chose the name ZLIB1.DLL, where '1' indicates the major zlib version number. We hope that we will not have to break the binary compatibility again, at least not as long as the zlib-1.x series will last. There is still a ZLIB_DLL macro, that can trigger a more efficient build and use of the DLL, but compatibility no longer dependents on it. 3. Can I build ZLIB.DLL from the new zlib sources, and replace an old ZLIB.DLL, that was built from zlib-1.1.4 or earlier? - In principle, you can do it by assigning calling convention keywords to the macros ZEXPORT and ZEXPORTVA. In practice, it depends on what you mean by "an old ZLIB.DLL", because the old DLL exists in several mutually-incompatible versions. You have to find out first what kind of calling convention is being used in your particular ZLIB.DLL build, and to use the same one in the new build. If you don't know what this is all about, you might be better off if you would just leave the old DLL intact. 4. Can I compile my application using the new zlib interface, and link it to an old ZLIB.DLL, that was built from zlib-1.1.4 or earlier? - The official answer is "no"; the real answer depends again on what kind of ZLIB.DLL you have. Even if you are lucky, this course of action is unreliable. If you rebuild your application and you intend to use a newer version of zlib (post- 1.1.4), it is strongly recommended to link it to the new ZLIB1.DLL. 5. Why are the zlib symbols exported by name, and not by ordinal? - Although exporting symbols by ordinal is a little faster, it is risky. Any single glitch in the maintenance or use of the DEF file that contains the ordinals can result in incompatible builds and frustrating crashes. Simply put, the benefits of exporting symbols by ordinal do not justify the risks. Technically, it should be possible to maintain ordinals in the DEF file, and still export the symbols by name. Ordinals exist in every DLL, and even if the dynamic linking performed at the DLL startup is searching for names, ordinals serve as hints, for a faster name lookup. However, if the DEF file contains ordinals, the Microsoft linker automatically builds an implib that will cause the executables linked to it to use those ordinals, and not the names. It is interesting to notice that the GNU linker for Win32 does not suffer from this problem. It is possible to avoid the DEF file if the exported symbols are accompanied by a "__declspec(dllexport)" attribute in the source files. You can do this in zlib by predefining the ZLIB_DLL macro. 6. I see that the ZLIB1.DLL functions use the "C" (CDECL) calling convention. Why not use the STDCALL convention? STDCALL is the standard convention in Win32, and I need it in my Visual Basic project! (For readability, we use CDECL to refer to the convention triggered by the "__cdecl" keyword, STDCALL to refer to the convention triggered by "__stdcall", and FASTCALL to refer to the convention triggered by "__fastcall".) - Most of the native Windows API functions (without varargs) use indeed the WINAPI convention (which translates to STDCALL in Win32), but the standard C functions use CDECL. If a user application is intrinsically tied to the Windows API (e.g. it calls native Windows API functions such as CreateFile()), sometimes it makes sense to decorate its own functions with WINAPI. But if ANSI C or POSIX portability is a goal (e.g. it calls standard C functions such as fopen()), it is not a sound decision to request the inclusion of <windows.h>, or to use non-ANSI constructs, for the sole purpose to make the user functions STDCALL-able. The functionality offered by zlib is not in the category of "Windows functionality", but is more like "C functionality". Technically, STDCALL is not bad; in fact, it is slightly faster than CDECL, and it works with variable-argument functions, just like CDECL. It is unfortunate that, in spite of using STDCALL in the Windows API, it is not the default convention used by the C compilers that run under Windows. The roots of the problem reside deep inside the unsafety of the K&R-style function prototypes, where the argument types are not specified; but that is another story for another day. The remaining fact is that CDECL is the default convention. Even if an explicit convention is hard-coded into the function prototypes inside C headers, problems may appear. The necessity to expose the convention in users' callbacks is one of these problems. The calling convention issues are also important when using zlib in other programming languages. Some of them, like Ada (GNAT) and Fortran (GNU G77), have C bindings implemented initially on Unix, and relying on the C calling convention. On the other hand, the pre- .NET versions of Microsoft Visual Basic require STDCALL, while Borland Delphi prefers, although it does not require, FASTCALL. In fairness to all possible uses of zlib outside the C programming language, we choose the default "C" convention. Anyone interested in different bindings or conventions is encouraged to maintain specialized projects. The "contrib/" directory from the zlib distribution already holds a couple of foreign bindings, such as Ada, C++, and Delphi. 7. I need a DLL for my Visual Basic project. What can I do? - Define the ZLIB_WINAPI macro before including "zlib.h", when building both the DLL and the user application (except that you don't need to define anything when using the DLL in Visual Basic). The ZLIB_WINAPI macro will switch on the WINAPI (STDCALL) convention. The name of this DLL must be different than the official ZLIB1.DLL. Gilles Vollant has contributed a build named ZLIBWAPI.DLL, with the ZLIB_WINAPI macro turned on, and with the minizip functionality built in. For more information, please read the notes inside "contrib/vstudio/readme.txt", found in the zlib distribution. 8. I need to use zlib in my Microsoft .NET project. What can I do? - Henrik Ravn has contributed a .NET wrapper around zlib. Look into contrib/dotzlib/, inside the zlib distribution. 9. If my application uses ZLIB1.DLL, should I link it to MSVCRT.DLL? Why? - It is not required, but it is recommended to link your application to MSVCRT.DLL, if it uses ZLIB1.DLL. The executables (.EXE, .DLL, etc.) that are involved in the same process and are using the C run-time library (i.e. they are calling standard C functions), must link to the same library. There are several libraries in the Win32 system: CRTDLL.DLL, MSVCRT.DLL, the static C libraries, etc. Since ZLIB1.DLL is linked to MSVCRT.DLL, the executables that depend on it should also be linked to MSVCRT.DLL. 10. Why are you saying that ZLIB1.DLL and my application should be linked to the same C run-time (CRT) library? I linked my application and my DLLs to different C libraries (e.g. my application to a static library, and my DLLs to MSVCRT.DLL), and everything works fine. - If a user library invokes only pure Win32 API (accessible via <windows.h> and the related headers), its DLL build will work in any context. But if this library invokes standard C API, things get more complicated. There is a single Win32 library in a Win32 system. Every function in this library resides in a single DLL module, that is safe to call from anywhere. On the other hand, there are multiple versions of the C library, and each of them has its own separate internal state. Standalone executables and user DLLs that call standard C functions must link to a C run-time (CRT) library, be it static or shared (DLL). Intermixing occurs when an executable (not necessarily standalone) and a DLL are linked to different CRTs, and both are running in the same process. Intermixing multiple CRTs is possible, as long as their internal states are kept intact. The Microsoft Knowledge Base articles KB94248 "HOWTO: Use the C Run-Time" and KB140584 "HOWTO: Link with the Correct C Run-Time (CRT) Library" mention the potential problems raised by intermixing. If intermixing works for you, it's because your application and DLLs are avoiding the corruption of each of the CRTs' internal states, maybe by careful design, or maybe by fortune. Also note that linking ZLIB1.DLL to non-Microsoft CRTs, such as those provided by Borland, raises similar problems. 11. Why are you linking ZLIB1.DLL to MSVCRT.DLL? - MSVCRT.DLL exists on every Windows 95 with a new service pack installed, or with Microsoft Internet Explorer 4 or later, and on all other Windows 4.x or later (Windows 98, Windows NT 4, or later). It is freely distributable; if not present in the system, it can be downloaded from Microsoft or from other software provider for free. The fact that MSVCRT.DLL does not exist on a virgin Windows 95 is not so problematic. Windows 95 is scarcely found nowadays, Microsoft ended its support a long time ago, and many recent applications from various vendors, including Microsoft, do not even run on it. Furthermore, no serious user should run Windows 95 without a proper update installed. 12. Why are you not linking ZLIB1.DLL to <<my favorite C run-time library>> ? - We considered and abandoned the following alternatives: * Linking ZLIB1.DLL to a static C library (LIBC.LIB, or LIBCMT.LIB) is not a good option. People are using the DLL mainly to save disk space. If you are linking your program to a static C library, you may as well consider linking zlib in statically, too. * Linking ZLIB1.DLL to CRTDLL.DLL looks appealing, because CRTDLL.DLL is present on every Win32 installation. Unfortunately, it has a series of problems: it does not work properly with Microsoft's C++ libraries, it does not provide support for 64-bit file offsets, (and so on...), and Microsoft discontinued its support a long time ago. * Linking ZLIB1.DLL to MSVCR70.DLL or MSVCR71.DLL, supplied with the Microsoft .NET platform, and Visual C++ 7.0/7.1, raises problems related to the status of ZLIB1.DLL as a system component. According to the Microsoft Knowledge Base article KB326922 "INFO: Redistribution of the Shared C Runtime Component in Visual C++ .NET", MSVCR70.DLL and MSVCR71.DLL are not supposed to function as system DLLs, because they may clash with MSVCRT.DLL. Instead, the application's installer is supposed to put these DLLs (if needed) in the application's private directory. If ZLIB1.DLL depends on a non-system runtime, it cannot function as a redistributable system component. * Linking ZLIB1.DLL to non-Microsoft runtimes, such as Borland's, or Cygwin's, raises problems related to the reliable presence of these runtimes on Win32 systems. It's easier to let the DLL build of zlib up to the people who distribute these runtimes, and who may proceed as explained in the answer to Question 14. 13. If ZLIB1.DLL cannot be linked to MSVCR70.DLL or MSVCR71.DLL, how can I build/use ZLIB1.DLL in Microsoft Visual C++ 7.0 (Visual Studio .NET) or newer? - Due to the problems explained in the Microsoft Knowledge Base article KB326922 (see the previous answer), the C runtime that comes with the VC7 environment is no longer considered a system component. That is, it should not be assumed that this runtime exists, or may be installed in a system directory. Since ZLIB1.DLL is supposed to be a system component, it may not depend on a non-system component. In order to link ZLIB1.DLL and your application to MSVCRT.DLL in VC7, you need the library of Visual C++ 6.0 or older. If you don't have this library at hand, it's probably best not to use ZLIB1.DLL. We are hoping that, in the future, Microsoft will provide a way to build applications linked to a proper system runtime, from the Visual C++ environment. Until then, you have a couple of alternatives, such as linking zlib in statically. If your application requires dynamic linking, you may proceed as explained in the answer to Question 14. 14. I need to link my own DLL build to a CRT different than MSVCRT.DLL. What can I do? - Feel free to rebuild the DLL from the zlib sources, and link it the way you want. You should, however, clearly state that your build is unofficial. You should give it a different file name, and/or install it in a private directory that can be accessed by your application only, and is not visible to the others (i.e. it's neither in the PATH, nor in the SYSTEM or SYSTEM32 directories). Otherwise, your build may clash with applications that link to the official build. For example, in Cygwin, zlib is linked to the Cygwin runtime CYGWIN1.DLL, and it is distributed under the name CYGZ.DLL. 15. May I include additional pieces of code that I find useful, link them in ZLIB1.DLL, and export them? - No. A legitimate build of ZLIB1.DLL must not include code that does not originate from the official zlib source code. But you can make your own private DLL build, under a different file name, as suggested in the previous answer. For example, zlib is a part of the VCL library, distributed with Borland Delphi and C++ Builder. The DLL build of VCL is a redistributable file, named VCLxx.DLL. 16. May I remove some functionality out of ZLIB1.DLL, by enabling macros like NO_GZCOMPRESS or NO_GZIP at compile time? - No. A legitimate build of ZLIB1.DLL must provide the complete zlib functionality, as implemented in the official zlib source code. But you can make your own private DLL build, under a different file name, as suggested in the previous answer. ** This document is written and maintained by Cosmin Truta <cosmint@cs.ubbcluj.ro> |
Added compat/zlib/win32/Makefile.bor.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib # Borland C++ for Win32 # # Usage: # make -f win32/Makefile.bor # ------------ Borland C++ ------------ # Optional nonstandard preprocessor flags (e.g. -DMAX_MEM_LEVEL=7) # should be added to the environment via "set LOCAL_ZLIB=-DFOO" or # added to the declaration of LOC here: LOC = $(LOCAL_ZLIB) CC = bcc32 AS = bcc32 LD = bcc32 AR = tlib CFLAGS = -a -d -k- -O2 $(LOC) ASFLAGS = $(LOC) LDFLAGS = $(LOC) # variables ZLIB_LIB = zlib.lib OBJ1 = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj OBJ2 = gzwrite.obj infback.obj inffast.obj inflate.obj inftrees.obj trees.obj uncompr.obj zutil.obj #OBJA = OBJP1 = +adler32.obj+compress.obj+crc32.obj+deflate.obj+gzclose.obj+gzlib.obj+gzread.obj OBJP2 = +gzwrite.obj+infback.obj+inffast.obj+inflate.obj+inftrees.obj+trees.obj+uncompr.obj+zutil.obj #OBJPA= # targets all: $(ZLIB_LIB) example.exe minigzip.exe .c.obj: $(CC) -c $(CFLAGS) $< .asm.obj: $(AS) -c $(ASFLAGS) $< adler32.obj: adler32.c zlib.h zconf.h compress.obj: compress.c zlib.h zconf.h crc32.obj: crc32.c zlib.h zconf.h crc32.h deflate.obj: deflate.c deflate.h zutil.h zlib.h zconf.h gzclose.obj: gzclose.c zlib.h zconf.h gzguts.h gzlib.obj: gzlib.c zlib.h zconf.h gzguts.h gzread.obj: gzread.c zlib.h zconf.h gzguts.h gzwrite.obj: gzwrite.c zlib.h zconf.h gzguts.h infback.obj: infback.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inffast.obj: inffast.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inflate.obj: inflate.c zutil.h zlib.h zconf.h inftrees.h inflate.h \ inffast.h inffixed.h inftrees.obj: inftrees.c zutil.h zlib.h zconf.h inftrees.h trees.obj: trees.c zutil.h zlib.h zconf.h deflate.h trees.h uncompr.obj: uncompr.c zlib.h zconf.h zutil.obj: zutil.c zutil.h zlib.h zconf.h example.obj: test/example.c zlib.h zconf.h minigzip.obj: test/minigzip.c zlib.h zconf.h # For the sake of the old Borland make, # the command line is cut to fit in the MS-DOS 128 byte limit: $(ZLIB_LIB): $(OBJ1) $(OBJ2) $(OBJA) -del $(ZLIB_LIB) $(AR) $(ZLIB_LIB) $(OBJP1) $(AR) $(ZLIB_LIB) $(OBJP2) $(AR) $(ZLIB_LIB) $(OBJPA) # testing test: example.exe minigzip.exe example echo hello world | minigzip | minigzip -d example.exe: example.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) example.obj $(ZLIB_LIB) minigzip.exe: minigzip.obj $(ZLIB_LIB) $(LD) $(LDFLAGS) minigzip.obj $(ZLIB_LIB) # cleanup clean: -del $(ZLIB_LIB) -del *.obj -del *.exe -del *.tds -del zlib.bak -del foo.gz |
Added compat/zlib/win32/Makefile.gcc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib, derived from Makefile.dj2. # Modified for mingw32 by C. Spieler, 6/16/98. # Updated for zlib 1.2.x by Christian Spieler and Cosmin Truta, Mar-2003. # Last updated: Mar 2012. # Tested under Cygwin and MinGW. # Copyright (C) 1995-2003 Jean-loup Gailly. # For conditions of distribution and use, see copyright notice in zlib.h # To compile, or to compile and test, type from the top level zlib directory: # # make -fwin32/Makefile.gcc; make test testdll -fwin32/Makefile.gcc # # To install libz.a, zconf.h and zlib.h in the system directories, type: # # make install -fwin32/Makefile.gcc # # BINARY_PATH, INCLUDE_PATH and LIBRARY_PATH must be set. # # To install the shared lib, append SHARED_MODE=1 to the make command : # # make install -fwin32/Makefile.gcc SHARED_MODE=1 # Note: # If the platform is *not* MinGW (e.g. it is Cygwin or UWIN), # the DLL name should be changed from "zlib1.dll". STATICLIB = libz.a SHAREDLIB = zlib1.dll IMPLIB = libz.dll.a # # Set to 1 if shared object needs to be installed # SHARED_MODE=0 #LOC = -DZLIB_DEBUG -g PREFIX = CC = $(PREFIX)gcc CFLAGS = $(LOC) -O3 -Wall AS = $(CC) ASFLAGS = $(LOC) -Wall LD = $(CC) LDFLAGS = $(LOC) AR = $(PREFIX)ar ARFLAGS = rcs RC = $(PREFIX)windres RCFLAGS = --define GCC_WINDRES STRIP = $(PREFIX)strip CP = cp -fp # If GNU install is available, replace $(CP) with install. INSTALL = $(CP) RM = rm -f prefix ?= /usr/local exec_prefix = $(prefix) OBJS = adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o \ gzwrite.o infback.o inffast.o inflate.o inftrees.o trees.o uncompr.o zutil.o OBJA = all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) example.exe minigzip.exe example_d.exe minigzip_d.exe test: example.exe minigzip.exe ./example echo hello world | ./minigzip | ./minigzip -d testdll: example_d.exe minigzip_d.exe ./example_d echo hello world | ./minigzip_d | ./minigzip_d -d .c.o: $(CC) $(CFLAGS) -c -o $@ $< .S.o: $(AS) $(ASFLAGS) -c -o $@ $< $(STATICLIB): $(OBJS) $(OBJA) $(AR) $(ARFLAGS) $@ $(OBJS) $(OBJA) $(IMPLIB): $(SHAREDLIB) $(SHAREDLIB): win32/zlib.def $(OBJS) $(OBJA) zlibrc.o $(CC) -shared -Wl,--out-implib,$(IMPLIB) $(LDFLAGS) \ -o $@ win32/zlib.def $(OBJS) $(OBJA) zlibrc.o $(STRIP) $@ example.exe: example.o $(STATICLIB) $(LD) $(LDFLAGS) -o $@ example.o $(STATICLIB) $(STRIP) $@ minigzip.exe: minigzip.o $(STATICLIB) $(LD) $(LDFLAGS) -o $@ minigzip.o $(STATICLIB) $(STRIP) $@ example_d.exe: example.o $(IMPLIB) $(LD) $(LDFLAGS) -o $@ example.o $(IMPLIB) $(STRIP) $@ minigzip_d.exe: minigzip.o $(IMPLIB) $(LD) $(LDFLAGS) -o $@ minigzip.o $(IMPLIB) $(STRIP) $@ example.o: test/example.c zlib.h zconf.h $(CC) $(CFLAGS) -I. -c -o $@ test/example.c minigzip.o: test/minigzip.c zlib.h zconf.h $(CC) $(CFLAGS) -I. -c -o $@ test/minigzip.c zlibrc.o: win32/zlib1.rc $(RC) $(RCFLAGS) -o $@ win32/zlib1.rc .PHONY: install uninstall clean install: zlib.h zconf.h $(STATICLIB) $(IMPLIB) @if test -z "$(DESTDIR)$(INCLUDE_PATH)" -o -z "$(DESTDIR)$(LIBRARY_PATH)" -o -z "$(DESTDIR)$(BINARY_PATH)"; then \ echo INCLUDE_PATH, LIBRARY_PATH, and BINARY_PATH must be specified; \ exit 1; \ fi -@mkdir -p '$(DESTDIR)$(INCLUDE_PATH)' -@mkdir -p '$(DESTDIR)$(LIBRARY_PATH)' '$(DESTDIR)$(LIBRARY_PATH)'/pkgconfig -if [ "$(SHARED_MODE)" = "1" ]; then \ mkdir -p '$(DESTDIR)$(BINARY_PATH)'; \ $(INSTALL) $(SHAREDLIB) '$(DESTDIR)$(BINARY_PATH)'; \ $(INSTALL) $(IMPLIB) '$(DESTDIR)$(LIBRARY_PATH)'; \ fi -$(INSTALL) zlib.h '$(DESTDIR)$(INCLUDE_PATH)' -$(INSTALL) zconf.h '$(DESTDIR)$(INCLUDE_PATH)' -$(INSTALL) $(STATICLIB) '$(DESTDIR)$(LIBRARY_PATH)' sed \ -e 's|@prefix@|${prefix}|g' \ -e 's|@exec_prefix@|${exec_prefix}|g' \ -e 's|@libdir@|$(LIBRARY_PATH)|g' \ -e 's|@sharedlibdir@|$(LIBRARY_PATH)|g' \ -e 's|@includedir@|$(INCLUDE_PATH)|g' \ -e 's|@VERSION@|'`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' zlib.h`'|g' \ zlib.pc.in > '$(DESTDIR)$(LIBRARY_PATH)'/pkgconfig/zlib.pc uninstall: -if [ "$(SHARED_MODE)" = "1" ]; then \ $(RM) '$(DESTDIR)$(BINARY_PATH)'/$(SHAREDLIB); \ $(RM) '$(DESTDIR)$(LIBRARY_PATH)'/$(IMPLIB); \ fi -$(RM) '$(DESTDIR)$(INCLUDE_PATH)'/zlib.h -$(RM) '$(DESTDIR)$(INCLUDE_PATH)'/zconf.h -$(RM) '$(DESTDIR)$(LIBRARY_PATH)'/$(STATICLIB) clean: -$(RM) $(STATICLIB) -$(RM) $(SHAREDLIB) -$(RM) $(IMPLIB) -$(RM) *.o -$(RM) *.exe -$(RM) foo.gz adler32.o: zlib.h zconf.h compress.o: zlib.h zconf.h crc32.o: crc32.h zlib.h zconf.h deflate.o: deflate.h zutil.h zlib.h zconf.h gzclose.o: zlib.h zconf.h gzguts.h gzlib.o: zlib.h zconf.h gzguts.h gzread.o: zlib.h zconf.h gzguts.h gzwrite.o: zlib.h zconf.h gzguts.h inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h inftrees.o: zutil.h zlib.h zconf.h inftrees.h trees.o: deflate.h zutil.h zlib.h zconf.h trees.h uncompr.o: zlib.h zconf.h zutil.o: zutil.h zlib.h zconf.h |
Added compat/zlib/win32/Makefile.msc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # Makefile for zlib using Microsoft (Visual) C # zlib is copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler # # Usage: # nmake -f win32/Makefile.msc (standard build) # nmake -f win32/Makefile.msc LOC=-DFOO (nonstandard build) # The toplevel directory of the source tree. # TOP = . # optional build flags LOC = # variables STATICLIB = zlib.lib SHAREDLIB = zlib1.dll IMPLIB = zdll.lib CC = cl AS = ml LD = link AR = lib RC = rc CFLAGS = -nologo -MT -W3 -O2 -Oy- -Zi -Fd"zlib" $(LOC) WFLAGS = -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE ASFLAGS = -coff -Zi $(LOC) LDFLAGS = -nologo -debug -incremental:no -opt:ref ARFLAGS = -nologo RCFLAGS = /dWIN32 /r OBJS = adler32.obj compress.obj crc32.obj deflate.obj gzclose.obj gzlib.obj gzread.obj \ gzwrite.obj infback.obj inflate.obj inftrees.obj inffast.obj trees.obj uncompr.obj zutil.obj OBJA = # targets all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) \ example.exe minigzip.exe example_d.exe minigzip_d.exe $(STATICLIB): $(OBJS) $(OBJA) $(AR) $(ARFLAGS) -out:$@ $(OBJS) $(OBJA) $(IMPLIB): $(SHAREDLIB) $(SHAREDLIB): $(TOP)/win32/zlib.def $(OBJS) $(OBJA) zlib1.res $(LD) $(LDFLAGS) -def:$(TOP)/win32/zlib.def -dll -implib:$(IMPLIB) \ -out:$@ -base:0x5A4C0000 $(OBJS) $(OBJA) zlib1.res if exist $@.manifest \ mt -nologo -manifest $@.manifest -outputresource:$@;2 example.exe: example.obj $(STATICLIB) $(LD) $(LDFLAGS) example.obj $(STATICLIB) if exist $@.manifest \ mt -nologo -manifest $@.manifest -outputresource:$@;1 minigzip.exe: minigzip.obj $(STATICLIB) $(LD) $(LDFLAGS) minigzip.obj $(STATICLIB) if exist $@.manifest \ mt -nologo -manifest $@.manifest -outputresource:$@;1 example_d.exe: example.obj $(IMPLIB) $(LD) $(LDFLAGS) -out:$@ example.obj $(IMPLIB) if exist $@.manifest \ mt -nologo -manifest $@.manifest -outputresource:$@;1 minigzip_d.exe: minigzip.obj $(IMPLIB) $(LD) $(LDFLAGS) -out:$@ minigzip.obj $(IMPLIB) if exist $@.manifest \ mt -nologo -manifest $@.manifest -outputresource:$@;1 {$(TOP)}.c.obj: $(CC) -c $(WFLAGS) $(CFLAGS) $< {$(TOP)/test}.c.obj: $(CC) -c -I$(TOP) $(WFLAGS) $(CFLAGS) $< {$(TOP)/contrib/masmx64}.c.obj: $(CC) -c $(WFLAGS) $(CFLAGS) $< {$(TOP)/contrib/masmx64}.asm.obj: $(AS) -c $(ASFLAGS) $< {$(TOP)/contrib/masmx86}.asm.obj: $(AS) -c $(ASFLAGS) $< adler32.obj: $(TOP)/adler32.c $(TOP)/zlib.h $(TOP)/zconf.h compress.obj: $(TOP)/compress.c $(TOP)/zlib.h $(TOP)/zconf.h crc32.obj: $(TOP)/crc32.c $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/crc32.h deflate.obj: $(TOP)/deflate.c $(TOP)/deflate.h $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h gzclose.obj: $(TOP)/gzclose.c $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/gzguts.h gzlib.obj: $(TOP)/gzlib.c $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/gzguts.h gzread.obj: $(TOP)/gzread.c $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/gzguts.h gzwrite.obj: $(TOP)/gzwrite.c $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/gzguts.h infback.obj: $(TOP)/infback.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/inftrees.h $(TOP)/inflate.h \ $(TOP)/inffast.h $(TOP)/inffixed.h inffast.obj: $(TOP)/inffast.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/inftrees.h $(TOP)/inflate.h \ $(TOP)/inffast.h inflate.obj: $(TOP)/inflate.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/inftrees.h $(TOP)/inflate.h \ $(TOP)/inffast.h $(TOP)/inffixed.h inftrees.obj: $(TOP)/inftrees.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/inftrees.h trees.obj: $(TOP)/trees.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h $(TOP)/deflate.h $(TOP)/trees.h uncompr.obj: $(TOP)/uncompr.c $(TOP)/zlib.h $(TOP)/zconf.h zutil.obj: $(TOP)/zutil.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h gvmat64.obj: $(TOP)/contrib\masmx64\gvmat64.asm inffasx64.obj: $(TOP)/contrib\masmx64\inffasx64.asm inffas8664.obj: $(TOP)/contrib\masmx64\inffas8664.c $(TOP)/zutil.h $(TOP)/zlib.h $(TOP)/zconf.h \ $(TOP)/inftrees.h $(TOP)/inflate.h $(TOP)/inffast.h inffas32.obj: $(TOP)/contrib\masmx86\inffas32.asm match686.obj: $(TOP)/contrib\masmx86\match686.asm example.obj: $(TOP)/test/example.c $(TOP)/zlib.h $(TOP)/zconf.h minigzip.obj: $(TOP)/test/minigzip.c $(TOP)/zlib.h $(TOP)/zconf.h zlib1.res: $(TOP)/win32/zlib1.rc $(RC) $(RCFLAGS) /fo$@ $(TOP)/win32/zlib1.rc # testing test: example.exe minigzip.exe example echo hello world | minigzip | minigzip -d testdll: example_d.exe minigzip_d.exe example_d echo hello world | minigzip_d | minigzip_d -d # cleanup clean: -del $(STATICLIB) -del $(SHAREDLIB) -del $(IMPLIB) -del *.obj -del *.res -del *.exp -del *.exe -del *.pdb -del *.manifest -del foo.gz |
Added compat/zlib/win32/README-WIN32.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 | ZLIB DATA COMPRESSION LIBRARY zlib 1.3.1 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). All functions of the compression library are documented in the file zlib.h (volunteer to write man pages welcome, contact zlib@gzip.org). Two compiled examples are distributed in this package, example and minigzip. The example_d and minigzip_d flavors validate that the zlib1.dll file is working correctly. Questions about zlib should be sent to <zlib@gzip.org>. The zlib home page is http://zlib.net/ . Before reporting a problem, please check this site to verify that you have the latest version of zlib; otherwise get the latest version and check whether the problem still exists or not. PLEASE read DLL_FAQ.txt, and the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. Manifest: The package zlib-1.3.1-win32-x86.zip will contain the following files: README-WIN32.txt This document ChangeLog Changes since previous zlib packages DLL_FAQ.txt Frequently asked questions about zlib1.dll zlib.3.pdf Documentation of this library in Adobe Acrobat format example.exe A statically-bound example (using zlib.lib, not the dll) example.pdb Symbolic information for debugging example.exe example_d.exe A zlib1.dll bound example (using zdll.lib) example_d.pdb Symbolic information for debugging example_d.exe minigzip.exe A statically-bound test program (using zlib.lib, not the dll) minigzip.pdb Symbolic information for debugging minigzip.exe minigzip_d.exe A zlib1.dll bound test program (using zdll.lib) minigzip_d.pdb Symbolic information for debugging minigzip_d.exe zlib.h Install these files into the compilers' INCLUDE path to zconf.h compile programs which use zlib.lib or zdll.lib zdll.lib Install these files into the compilers' LIB path if linking zdll.exp a compiled program to the zlib1.dll binary zlib.lib Install these files into the compilers' LIB path to link zlib zlib.pdb into compiled programs, without zlib1.dll runtime dependency (zlib.pdb provides debugging info to the compile time linker) zlib1.dll Install this binary shared library into the system PATH, or the program's runtime directory (where the .exe resides) zlib1.pdb Install in the same directory as zlib1.dll, in order to debug an application crash using WinDbg or similar tools. All .pdb files above are entirely optional, but are very useful to a developer attempting to diagnose program misbehavior or a crash. Many additional important files for developers can be found in the zlib127.zip source package available from http://zlib.net/ - review that package's README file for details. Acknowledgments: The deflate format used by zlib was defined by Phil Katz. The deflate and zlib specifications were written by L. Peter Deutsch. Thanks to all the people who reported problems and suggested various improvements in zlib; they are too numerous to cite here. Copyright notice: (C) 1995-2017 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu If you use the zlib library in a product, we would appreciate *not* receiving lengthy legal documents to sign. The sources are provided for free but without warranty of any kind. The library has been entirely written by Jean-loup Gailly and Mark Adler; it does not include third-party code. If you redistribute modified sources, we would appreciate that you include in the file ChangeLog history information documenting your changes. Please read the FAQ for more information on the distribution of modified source versions. |
Added compat/zlib/win32/VisualC.txt.
> > > | 1 2 3 | To build zlib using the Microsoft Visual C++ environment, use the appropriate project from the contrib/vstudio/ directory. |
Added compat/zlib/win32/zlib.def.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ; zlib data compression library EXPORTS ; basic functions zlibVersion deflate deflateEnd inflate inflateEnd ; advanced functions deflateSetDictionary deflateGetDictionary deflateCopy deflateReset deflateParams deflateTune deflateBound deflatePending deflatePrime deflateSetHeader inflateSetDictionary inflateGetDictionary inflateSync inflateCopy inflateReset inflateReset2 inflatePrime inflateMark inflateGetHeader inflateBack inflateBackEnd zlibCompileFlags ; utility functions compress compress2 compressBound uncompress uncompress2 gzopen gzdopen gzbuffer gzsetparams gzread gzfread gzwrite gzfwrite gzprintf gzvprintf gzputs gzgets gzputc gzgetc gzungetc gzflush gzseek gzrewind gztell gzoffset gzeof gzdirect gzclose gzclose_r gzclose_w gzerror gzclearerr ; large file functions gzopen64 gzseek64 gztell64 gzoffset64 adler32_combine64 crc32_combine64 crc32_combine_gen64 ; checksum functions adler32 adler32_z crc32 crc32_z adler32_combine crc32_combine crc32_combine_gen crc32_combine_op ; various hacks, don't look :) deflateInit_ deflateInit2_ inflateInit_ inflateInit2_ inflateBackInit_ gzgetc_ zError inflateSyncPoint get_crc_table inflateUndermine inflateValidate inflateCodesUsed inflateResetKeep deflateResetKeep gzopen_w |
Added compat/zlib/win32/zlib1.rc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include <winver.h> #include "../zlib.h" #ifdef GCC_WINDRES VS_VERSION_INFO VERSIONINFO #else VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE #endif FILEVERSION ZLIB_VER_MAJOR,ZLIB_VER_MINOR,ZLIB_VER_REVISION,0 PRODUCTVERSION ZLIB_VER_MAJOR,ZLIB_VER_MINOR,ZLIB_VER_REVISION,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #ifdef _DEBUG FILEFLAGS 1 #else FILEFLAGS 0 #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE 0 // not used BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" //language ID = U.S. English, char set = Windows, Multilingual BEGIN VALUE "FileDescription", "zlib data compression library\0" VALUE "FileVersion", ZLIB_VERSION "\0" VALUE "InternalName", "zlib1.dll\0" VALUE "LegalCopyright", "(C) 1995-2022 Jean-loup Gailly & Mark Adler\0" VALUE "OriginalFilename", "zlib1.dll\0" VALUE "ProductName", "zlib\0" VALUE "ProductVersion", ZLIB_VERSION "\0" VALUE "Comments", "For more information visit http://www.zlib.net/\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 1252 END END |
Added compat/zlib/zconf.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. * Even better than compiling with -DZ_PREFIX would be to use configure to set * this permanently in zconf.h using "./configure --zprefix". */ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET /* all linked symbols and init macros */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align # define _tr_flush_bits z__tr_flush_bits # define _tr_flush_block z__tr_flush_block # define _tr_init z__tr_init # define _tr_stored_block z__tr_stored_block # define _tr_tally z__tr_tally # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 # define adler32_z z_adler32_z # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 # define compressBound z_compressBound # endif # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 # define crc32_combine_gen z_crc32_combine_gen # define crc32_combine_gen64 z_crc32_combine_gen64 # define crc32_combine_op z_crc32_combine_op # define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd # define deflateGetDictionary z_deflateGetDictionary # define deflateInit z_deflateInit # define deflateInit2 z_deflateInit2 # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams # define deflatePending z_deflatePending # define deflatePrime z_deflatePrime # define deflateReset z_deflateReset # define deflateResetKeep z_deflateResetKeep # define deflateSetDictionary z_deflateSetDictionary # define deflateSetHeader z_deflateSetHeader # define deflateTune z_deflateTune # define deflate_copyright z_deflate_copyright # define get_crc_table z_get_crc_table # ifndef Z_SOLO # define gz_error z_gz_error # define gz_intmax z_gz_intmax # define gz_strwinerror z_gz_strwinerror # define gzbuffer z_gzbuffer # define gzclearerr z_gzclearerr # define gzclose z_gzclose # define gzclose_r z_gzclose_r # define gzclose_w z_gzclose_w # define gzdirect z_gzdirect # define gzdopen z_gzdopen # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush # define gzfread z_gzfread # define gzfwrite z_gzfwrite # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets # define gzoffset z_gzoffset # define gzoffset64 z_gzoffset64 # define gzopen z_gzopen # define gzopen64 z_gzopen64 # ifdef _WIN32 # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread # define gzrewind z_gzrewind # define gzseek z_gzseek # define gzseek64 z_gzseek64 # define gzsetparams z_gzsetparams # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc # define gzvprintf z_gzvprintf # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd # define inflateBackInit z_inflateBackInit # define inflateBackInit_ z_inflateBackInit_ # define inflateCodesUsed z_inflateCodesUsed # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd # define inflateGetDictionary z_inflateGetDictionary # define inflateGetHeader z_inflateGetHeader # define inflateInit z_inflateInit # define inflateInit2 z_inflateInit2 # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 # define inflateResetKeep z_inflateResetKeep # define inflateSetDictionary z_inflateSetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine # define inflateValidate z_inflateValidate # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress # define uncompress2 z_uncompress2 # endif # define zError z_zError # ifndef Z_SOLO # define zcalloc z_zcalloc # define zcfree z_zcfree # endif # define zlibCompileFlags z_zlibCompileFlags # define zlibVersion z_zlibVersion /* all zlib typedefs in zlib.h and zconf.h */ # define Byte z_Byte # define Bytef z_Bytef # define alloc_func z_alloc_func # define charf z_charf # define free_func z_free_func # ifndef Z_SOLO # define gzFile z_gzFile # endif # define gz_header z_gz_header # define gz_headerp z_gz_headerp # define in_func z_in_func # define intf z_intf # define out_func z_out_func # define uInt z_uInt # define uIntf z_uIntf # define uLong z_uLong # define uLongf z_uLongf # define voidp z_voidp # define voidpc z_voidpc # define voidpf z_voidpf /* all zlib structs in zlib.h and zconf.h */ # define gz_header_s z_gz_header_s # define internal_state z_internal_state #endif #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif #if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) # define OS2 #endif #if defined(_WINDOWS) && !defined(WINDOWS) # define WINDOWS #endif #if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) # ifndef WIN32 # define WIN32 # endif #endif #if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) # if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) # ifndef SYS16BIT # define SYS16BIT # endif # endif #endif /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). */ #ifdef SYS16BIT # define MAXSEG_64K #endif #ifdef MSDOS # define UNALIGNED_OK #endif #ifdef __STDC_VERSION__ # ifndef STDC # define STDC # endif # if __STDC_VERSION__ >= 199901L # ifndef STDC99 # define STDC99 # endif # endif #endif #if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) # define STDC #endif #if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) # define STDC #endif #if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) # define STDC #endif #if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) # define STDC #endif #if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ # define STDC #endif #ifndef STDC # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ # define const /* note: need a more gentle solution here */ # endif #endif #if defined(ZLIB_CONST) && !defined(z_const) # define z_const const #else # define z_const #endif #ifdef Z_SOLO # ifdef _WIN64 typedef unsigned long long z_size_t; # else typedef unsigned long z_size_t; # endif #else # define z_longlong long long # if defined(NO_SIZE_T) typedef unsigned NO_SIZE_T z_size_t; # elif defined(STDC) # include <stddef.h> typedef size_t z_size_t; # else typedef unsigned long z_size_t; # endif # undef z_longlong #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus about 7 kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT # if defined(M_I86SM) || defined(M_I86MM) /* MSC small or medium model */ # define SMALL_MEDIUM # ifdef _MSC_VER # define FAR _far # else # define FAR far # endif # endif # if (defined(__SMALL__) || defined(__MEDIUM__)) /* Turbo C small or medium model */ # define SMALL_MEDIUM # ifdef __BORLANDC__ # define FAR _far # else # define FAR far # endif # endif #endif #if defined(WINDOWS) || defined(WIN32) /* If building or using zlib as a DLL, define ZLIB_DLL. * This is not mandatory, but it offers a little performance increase. */ # ifdef ZLIB_DLL # if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) # ifdef ZLIB_INTERNAL # define ZEXTERN extern __declspec(dllexport) # else # define ZEXTERN extern __declspec(dllimport) # endif # endif # endif /* ZLIB_DLL */ /* If building or using zlib with the WINAPI/WINAPIV calling convention, * define ZLIB_WINAPI. * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. */ # ifdef ZLIB_WINAPI # ifdef FAR # undef FAR # endif # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include <windows.h> /* No need for _export, use ZLIB.DEF instead. */ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ # define ZEXPORT WINAPI # ifdef WIN32 # define ZEXPORTVA WINAPIV # else # define ZEXPORTVA FAR CDECL # endif # endif #endif #if defined (__BEOS__) # ifdef ZLIB_DLL # ifdef ZLIB_INTERNAL # define ZEXPORT __declspec(dllexport) # define ZEXPORTVA __declspec(dllexport) # else # define ZEXPORT __declspec(dllimport) # define ZEXPORTVA __declspec(dllimport) # endif # endif #endif #ifndef ZEXTERN # define ZEXTERN extern #endif #ifndef ZEXPORT # define ZEXPORT #endif #ifndef ZEXPORTVA # define ZEXPORTVA #endif #ifndef FAR # define FAR #endif #if !defined(__MACTYPES__) typedef unsigned char Byte; /* 8 bits */ #endif typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ #ifdef SMALL_MEDIUM /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ # define Bytef Byte FAR #else typedef Byte FAR Bytef; #endif typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void const *voidpc; typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte const *voidpc; typedef Byte FAR *voidpf; typedef Byte *voidp; #endif #if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) # include <limits.h> # if (UINT_MAX == 0xffffffffUL) # define Z_U4 unsigned # elif (ULONG_MAX == 0xffffffffUL) # define Z_U4 unsigned long # elif (USHRT_MAX == 0xffffffffUL) # define Z_U4 unsigned short # endif #endif #ifdef Z_U4 typedef Z_U4 z_crc_t; #else typedef unsigned long z_crc_t; #endif #ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_UNISTD_H #endif #ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_STDARG_H #endif #ifdef STDC # ifndef Z_SOLO # include <sys/types.h> /* for off_t */ # endif #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO # include <stdarg.h> /* for va_list */ # endif #endif #ifdef _WIN32 # ifndef Z_SOLO # include <stddef.h> /* for wchar_t */ # endif #endif /* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even * though the former does not conform to the LFS document), but considering * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as * equivalently requesting no 64-bit operations */ #if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 # undef _LARGEFILE64_SOURCE #endif #ifndef Z_HAVE_UNISTD_H # ifdef __WATCOMC__ # define Z_HAVE_UNISTD_H # endif #endif #ifndef Z_HAVE_UNISTD_H # if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32) # define Z_HAVE_UNISTD_H # endif #endif #ifndef Z_SOLO # if defined(Z_HAVE_UNISTD_H) # include <unistd.h> /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ # ifdef VMS # include <unixio.h> /* for off_t */ # endif # ifndef z_off_t # define z_off_t off_t # endif # endif #endif #if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 # define Z_LFS64 #endif #if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) # define Z_LARGE64 #endif #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) # define Z_WANT64 #endif #if !defined(SEEK_SET) && !defined(Z_SOLO) # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif #ifndef z_off_t # define z_off_t long #endif #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else # if defined(_WIN32) && !defined(__GNUC__) # define z_off64_t __int64 # else # define z_off64_t z_off_t # endif #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) #pragma map(deflateInit_,"DEIN") #pragma map(deflateInit2_,"DEIN2") #pragma map(deflateEnd,"DEEND") #pragma map(deflateBound,"DEBND") #pragma map(inflateInit_,"ININ") #pragma map(inflateInit2_,"ININ2") #pragma map(inflateEnd,"INEND") #pragma map(inflateSync,"INSY") #pragma map(inflateSetDictionary,"INSEDI") #pragma map(compressBound,"CMBND") #pragma map(inflate_table,"INTABL") #pragma map(inflate_fast,"INFA") #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ |
Added compat/zlib/zconf.h.cmakein.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H #cmakedefine Z_PREFIX #cmakedefine Z_HAVE_UNISTD_H /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. * Even better than compiling with -DZ_PREFIX would be to use configure to set * this permanently in zconf.h using "./configure --zprefix". */ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET /* all linked symbols and init macros */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align # define _tr_flush_bits z__tr_flush_bits # define _tr_flush_block z__tr_flush_block # define _tr_init z__tr_init # define _tr_stored_block z__tr_stored_block # define _tr_tally z__tr_tally # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 # define adler32_z z_adler32_z # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 # define compressBound z_compressBound # endif # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 # define crc32_combine_gen z_crc32_combine_gen # define crc32_combine_gen64 z_crc32_combine_gen64 # define crc32_combine_op z_crc32_combine_op # define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd # define deflateGetDictionary z_deflateGetDictionary # define deflateInit z_deflateInit # define deflateInit2 z_deflateInit2 # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams # define deflatePending z_deflatePending # define deflatePrime z_deflatePrime # define deflateReset z_deflateReset # define deflateResetKeep z_deflateResetKeep # define deflateSetDictionary z_deflateSetDictionary # define deflateSetHeader z_deflateSetHeader # define deflateTune z_deflateTune # define deflate_copyright z_deflate_copyright # define get_crc_table z_get_crc_table # ifndef Z_SOLO # define gz_error z_gz_error # define gz_intmax z_gz_intmax # define gz_strwinerror z_gz_strwinerror # define gzbuffer z_gzbuffer # define gzclearerr z_gzclearerr # define gzclose z_gzclose # define gzclose_r z_gzclose_r # define gzclose_w z_gzclose_w # define gzdirect z_gzdirect # define gzdopen z_gzdopen # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush # define gzfread z_gzfread # define gzfwrite z_gzfwrite # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets # define gzoffset z_gzoffset # define gzoffset64 z_gzoffset64 # define gzopen z_gzopen # define gzopen64 z_gzopen64 # ifdef _WIN32 # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread # define gzrewind z_gzrewind # define gzseek z_gzseek # define gzseek64 z_gzseek64 # define gzsetparams z_gzsetparams # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc # define gzvprintf z_gzvprintf # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd # define inflateBackInit z_inflateBackInit # define inflateBackInit_ z_inflateBackInit_ # define inflateCodesUsed z_inflateCodesUsed # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd # define inflateGetDictionary z_inflateGetDictionary # define inflateGetHeader z_inflateGetHeader # define inflateInit z_inflateInit # define inflateInit2 z_inflateInit2 # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 # define inflateResetKeep z_inflateResetKeep # define inflateSetDictionary z_inflateSetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine # define inflateValidate z_inflateValidate # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress # define uncompress2 z_uncompress2 # endif # define zError z_zError # ifndef Z_SOLO # define zcalloc z_zcalloc # define zcfree z_zcfree # endif # define zlibCompileFlags z_zlibCompileFlags # define zlibVersion z_zlibVersion /* all zlib typedefs in zlib.h and zconf.h */ # define Byte z_Byte # define Bytef z_Bytef # define alloc_func z_alloc_func # define charf z_charf # define free_func z_free_func # ifndef Z_SOLO # define gzFile z_gzFile # endif # define gz_header z_gz_header # define gz_headerp z_gz_headerp # define in_func z_in_func # define intf z_intf # define out_func z_out_func # define uInt z_uInt # define uIntf z_uIntf # define uLong z_uLong # define uLongf z_uLongf # define voidp z_voidp # define voidpc z_voidpc # define voidpf z_voidpf /* all zlib structs in zlib.h and zconf.h */ # define gz_header_s z_gz_header_s # define internal_state z_internal_state #endif #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif #if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) # define OS2 #endif #if defined(_WINDOWS) && !defined(WINDOWS) # define WINDOWS #endif #if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) # ifndef WIN32 # define WIN32 # endif #endif #if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) # if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) # ifndef SYS16BIT # define SYS16BIT # endif # endif #endif /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). */ #ifdef SYS16BIT # define MAXSEG_64K #endif #ifdef MSDOS # define UNALIGNED_OK #endif #ifdef __STDC_VERSION__ # ifndef STDC # define STDC # endif # if __STDC_VERSION__ >= 199901L # ifndef STDC99 # define STDC99 # endif # endif #endif #if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) # define STDC #endif #if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) # define STDC #endif #if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) # define STDC #endif #if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) # define STDC #endif #if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ # define STDC #endif #ifndef STDC # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ # define const /* note: need a more gentle solution here */ # endif #endif #if defined(ZLIB_CONST) && !defined(z_const) # define z_const const #else # define z_const #endif #ifdef Z_SOLO # ifdef _WIN64 typedef unsigned long long z_size_t; # else typedef unsigned long z_size_t; # endif #else # define z_longlong long long # if defined(NO_SIZE_T) typedef unsigned NO_SIZE_T z_size_t; # elif defined(STDC) # include <stddef.h> typedef size_t z_size_t; # else typedef unsigned long z_size_t; # endif # undef z_longlong #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus about 7 kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT # if defined(M_I86SM) || defined(M_I86MM) /* MSC small or medium model */ # define SMALL_MEDIUM # ifdef _MSC_VER # define FAR _far # else # define FAR far # endif # endif # if (defined(__SMALL__) || defined(__MEDIUM__)) /* Turbo C small or medium model */ # define SMALL_MEDIUM # ifdef __BORLANDC__ # define FAR _far # else # define FAR far # endif # endif #endif #if defined(WINDOWS) || defined(WIN32) /* If building or using zlib as a DLL, define ZLIB_DLL. * This is not mandatory, but it offers a little performance increase. */ # ifdef ZLIB_DLL # if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) # ifdef ZLIB_INTERNAL # define ZEXTERN extern __declspec(dllexport) # else # define ZEXTERN extern __declspec(dllimport) # endif # endif # endif /* ZLIB_DLL */ /* If building or using zlib with the WINAPI/WINAPIV calling convention, * define ZLIB_WINAPI. * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. */ # ifdef ZLIB_WINAPI # ifdef FAR # undef FAR # endif # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include <windows.h> /* No need for _export, use ZLIB.DEF instead. */ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ # define ZEXPORT WINAPI # ifdef WIN32 # define ZEXPORTVA WINAPIV # else # define ZEXPORTVA FAR CDECL # endif # endif #endif #if defined (__BEOS__) # ifdef ZLIB_DLL # ifdef ZLIB_INTERNAL # define ZEXPORT __declspec(dllexport) # define ZEXPORTVA __declspec(dllexport) # else # define ZEXPORT __declspec(dllimport) # define ZEXPORTVA __declspec(dllimport) # endif # endif #endif #ifndef ZEXTERN # define ZEXTERN extern #endif #ifndef ZEXPORT # define ZEXPORT #endif #ifndef ZEXPORTVA # define ZEXPORTVA #endif #ifndef FAR # define FAR #endif #if !defined(__MACTYPES__) typedef unsigned char Byte; /* 8 bits */ #endif typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ #ifdef SMALL_MEDIUM /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ # define Bytef Byte FAR #else typedef Byte FAR Bytef; #endif typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void const *voidpc; typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte const *voidpc; typedef Byte FAR *voidpf; typedef Byte *voidp; #endif #if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) # include <limits.h> # if (UINT_MAX == 0xffffffffUL) # define Z_U4 unsigned # elif (ULONG_MAX == 0xffffffffUL) # define Z_U4 unsigned long # elif (USHRT_MAX == 0xffffffffUL) # define Z_U4 unsigned short # endif #endif #ifdef Z_U4 typedef Z_U4 z_crc_t; #else typedef unsigned long z_crc_t; #endif #ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_UNISTD_H #endif #ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_STDARG_H #endif #ifdef STDC # ifndef Z_SOLO # include <sys/types.h> /* for off_t */ # endif #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO # include <stdarg.h> /* for va_list */ # endif #endif #ifdef _WIN32 # ifndef Z_SOLO # include <stddef.h> /* for wchar_t */ # endif #endif /* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even * though the former does not conform to the LFS document), but considering * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as * equivalently requesting no 64-bit operations */ #if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 # undef _LARGEFILE64_SOURCE #endif #ifndef Z_HAVE_UNISTD_H # ifdef __WATCOMC__ # define Z_HAVE_UNISTD_H # endif #endif #ifndef Z_HAVE_UNISTD_H # if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32) # define Z_HAVE_UNISTD_H # endif #endif #ifndef Z_SOLO # if defined(Z_HAVE_UNISTD_H) # include <unistd.h> /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ # ifdef VMS # include <unixio.h> /* for off_t */ # endif # ifndef z_off_t # define z_off_t off_t # endif # endif #endif #if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 # define Z_LFS64 #endif #if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) # define Z_LARGE64 #endif #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) # define Z_WANT64 #endif #if !defined(SEEK_SET) && !defined(Z_SOLO) # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif #ifndef z_off_t # define z_off_t long #endif #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else # if defined(_WIN32) && !defined(__GNUC__) # define z_off64_t __int64 # else # define z_off64_t z_off_t # endif #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) #pragma map(deflateInit_,"DEIN") #pragma map(deflateInit2_,"DEIN2") #pragma map(deflateEnd,"DEEND") #pragma map(deflateBound,"DEBND") #pragma map(inflateInit_,"ININ") #pragma map(inflateInit2_,"ININ2") #pragma map(inflateEnd,"INEND") #pragma map(inflateSync,"INSY") #pragma map(inflateSetDictionary,"INSEDI") #pragma map(compressBound,"CMBND") #pragma map(inflate_table,"INTABL") #pragma map(inflate_fast,"INFA") #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ |
Added compat/zlib/zconf.h.in.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H /* * If you *really* need a unique prefix for all types and library functions, * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. * Even better than compiling with -DZ_PREFIX would be to use configure to set * this permanently in zconf.h using "./configure --zprefix". */ #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ # define Z_PREFIX_SET /* all linked symbols and init macros */ # define _dist_code z__dist_code # define _length_code z__length_code # define _tr_align z__tr_align # define _tr_flush_bits z__tr_flush_bits # define _tr_flush_block z__tr_flush_block # define _tr_init z__tr_init # define _tr_stored_block z__tr_stored_block # define _tr_tally z__tr_tally # define adler32 z_adler32 # define adler32_combine z_adler32_combine # define adler32_combine64 z_adler32_combine64 # define adler32_z z_adler32_z # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 # define compressBound z_compressBound # endif # define crc32 z_crc32 # define crc32_combine z_crc32_combine # define crc32_combine64 z_crc32_combine64 # define crc32_combine_gen z_crc32_combine_gen # define crc32_combine_gen64 z_crc32_combine_gen64 # define crc32_combine_op z_crc32_combine_op # define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd # define deflateGetDictionary z_deflateGetDictionary # define deflateInit z_deflateInit # define deflateInit2 z_deflateInit2 # define deflateInit2_ z_deflateInit2_ # define deflateInit_ z_deflateInit_ # define deflateParams z_deflateParams # define deflatePending z_deflatePending # define deflatePrime z_deflatePrime # define deflateReset z_deflateReset # define deflateResetKeep z_deflateResetKeep # define deflateSetDictionary z_deflateSetDictionary # define deflateSetHeader z_deflateSetHeader # define deflateTune z_deflateTune # define deflate_copyright z_deflate_copyright # define get_crc_table z_get_crc_table # ifndef Z_SOLO # define gz_error z_gz_error # define gz_intmax z_gz_intmax # define gz_strwinerror z_gz_strwinerror # define gzbuffer z_gzbuffer # define gzclearerr z_gzclearerr # define gzclose z_gzclose # define gzclose_r z_gzclose_r # define gzclose_w z_gzclose_w # define gzdirect z_gzdirect # define gzdopen z_gzdopen # define gzeof z_gzeof # define gzerror z_gzerror # define gzflush z_gzflush # define gzfread z_gzfread # define gzfwrite z_gzfwrite # define gzgetc z_gzgetc # define gzgetc_ z_gzgetc_ # define gzgets z_gzgets # define gzoffset z_gzoffset # define gzoffset64 z_gzoffset64 # define gzopen z_gzopen # define gzopen64 z_gzopen64 # ifdef _WIN32 # define gzopen_w z_gzopen_w # endif # define gzprintf z_gzprintf # define gzputc z_gzputc # define gzputs z_gzputs # define gzread z_gzread # define gzrewind z_gzrewind # define gzseek z_gzseek # define gzseek64 z_gzseek64 # define gzsetparams z_gzsetparams # define gztell z_gztell # define gztell64 z_gztell64 # define gzungetc z_gzungetc # define gzvprintf z_gzvprintf # define gzwrite z_gzwrite # endif # define inflate z_inflate # define inflateBack z_inflateBack # define inflateBackEnd z_inflateBackEnd # define inflateBackInit z_inflateBackInit # define inflateBackInit_ z_inflateBackInit_ # define inflateCodesUsed z_inflateCodesUsed # define inflateCopy z_inflateCopy # define inflateEnd z_inflateEnd # define inflateGetDictionary z_inflateGetDictionary # define inflateGetHeader z_inflateGetHeader # define inflateInit z_inflateInit # define inflateInit2 z_inflateInit2 # define inflateInit2_ z_inflateInit2_ # define inflateInit_ z_inflateInit_ # define inflateMark z_inflateMark # define inflatePrime z_inflatePrime # define inflateReset z_inflateReset # define inflateReset2 z_inflateReset2 # define inflateResetKeep z_inflateResetKeep # define inflateSetDictionary z_inflateSetDictionary # define inflateSync z_inflateSync # define inflateSyncPoint z_inflateSyncPoint # define inflateUndermine z_inflateUndermine # define inflateValidate z_inflateValidate # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table # ifndef Z_SOLO # define uncompress z_uncompress # define uncompress2 z_uncompress2 # endif # define zError z_zError # ifndef Z_SOLO # define zcalloc z_zcalloc # define zcfree z_zcfree # endif # define zlibCompileFlags z_zlibCompileFlags # define zlibVersion z_zlibVersion /* all zlib typedefs in zlib.h and zconf.h */ # define Byte z_Byte # define Bytef z_Bytef # define alloc_func z_alloc_func # define charf z_charf # define free_func z_free_func # ifndef Z_SOLO # define gzFile z_gzFile # endif # define gz_header z_gz_header # define gz_headerp z_gz_headerp # define in_func z_in_func # define intf z_intf # define out_func z_out_func # define uInt z_uInt # define uIntf z_uIntf # define uLong z_uLong # define uLongf z_uLongf # define voidp z_voidp # define voidpc z_voidpc # define voidpf z_voidpf /* all zlib structs in zlib.h and zconf.h */ # define gz_header_s z_gz_header_s # define internal_state z_internal_state #endif #if defined(__MSDOS__) && !defined(MSDOS) # define MSDOS #endif #if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) # define OS2 #endif #if defined(_WINDOWS) && !defined(WINDOWS) # define WINDOWS #endif #if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) # ifndef WIN32 # define WIN32 # endif #endif #if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) # if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) # ifndef SYS16BIT # define SYS16BIT # endif # endif #endif /* * Compile with -DMAXSEG_64K if the alloc function cannot allocate more * than 64k bytes at a time (needed on systems with 16-bit int). */ #ifdef SYS16BIT # define MAXSEG_64K #endif #ifdef MSDOS # define UNALIGNED_OK #endif #ifdef __STDC_VERSION__ # ifndef STDC # define STDC # endif # if __STDC_VERSION__ >= 199901L # ifndef STDC99 # define STDC99 # endif # endif #endif #if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) # define STDC #endif #if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) # define STDC #endif #if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) # define STDC #endif #if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) # define STDC #endif #if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ # define STDC #endif #ifndef STDC # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ # define const /* note: need a more gentle solution here */ # endif #endif #if defined(ZLIB_CONST) && !defined(z_const) # define z_const const #else # define z_const #endif #ifdef Z_SOLO # ifdef _WIN64 typedef unsigned long long z_size_t; # else typedef unsigned long z_size_t; # endif #else # define z_longlong long long # if defined(NO_SIZE_T) typedef unsigned NO_SIZE_T z_size_t; # elif defined(STDC) # include <stddef.h> typedef size_t z_size_t; # else typedef unsigned long z_size_t; # endif # undef z_longlong #endif /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # ifdef MAXSEG_64K # define MAX_MEM_LEVEL 8 # else # define MAX_MEM_LEVEL 9 # endif #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus about 7 kilobytes for small objects. */ /* Type declarations */ #ifndef OF /* function prototypes */ # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT # if defined(M_I86SM) || defined(M_I86MM) /* MSC small or medium model */ # define SMALL_MEDIUM # ifdef _MSC_VER # define FAR _far # else # define FAR far # endif # endif # if (defined(__SMALL__) || defined(__MEDIUM__)) /* Turbo C small or medium model */ # define SMALL_MEDIUM # ifdef __BORLANDC__ # define FAR _far # else # define FAR far # endif # endif #endif #if defined(WINDOWS) || defined(WIN32) /* If building or using zlib as a DLL, define ZLIB_DLL. * This is not mandatory, but it offers a little performance increase. */ # ifdef ZLIB_DLL # if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) # ifdef ZLIB_INTERNAL # define ZEXTERN extern __declspec(dllexport) # else # define ZEXTERN extern __declspec(dllimport) # endif # endif # endif /* ZLIB_DLL */ /* If building or using zlib with the WINAPI/WINAPIV calling convention, * define ZLIB_WINAPI. * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. */ # ifdef ZLIB_WINAPI # ifdef FAR # undef FAR # endif # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include <windows.h> /* No need for _export, use ZLIB.DEF instead. */ /* For complete Windows compatibility, use WINAPI, not __stdcall. */ # define ZEXPORT WINAPI # ifdef WIN32 # define ZEXPORTVA WINAPIV # else # define ZEXPORTVA FAR CDECL # endif # endif #endif #if defined (__BEOS__) # ifdef ZLIB_DLL # ifdef ZLIB_INTERNAL # define ZEXPORT __declspec(dllexport) # define ZEXPORTVA __declspec(dllexport) # else # define ZEXPORT __declspec(dllimport) # define ZEXPORTVA __declspec(dllimport) # endif # endif #endif #ifndef ZEXTERN # define ZEXTERN extern #endif #ifndef ZEXPORT # define ZEXPORT #endif #ifndef ZEXPORTVA # define ZEXPORTVA #endif #ifndef FAR # define FAR #endif #if !defined(__MACTYPES__) typedef unsigned char Byte; /* 8 bits */ #endif typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ #ifdef SMALL_MEDIUM /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ # define Bytef Byte FAR #else typedef Byte FAR Bytef; #endif typedef char FAR charf; typedef int FAR intf; typedef uInt FAR uIntf; typedef uLong FAR uLongf; #ifdef STDC typedef void const *voidpc; typedef void FAR *voidpf; typedef void *voidp; #else typedef Byte const *voidpc; typedef Byte FAR *voidpf; typedef Byte *voidp; #endif #if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) # include <limits.h> # if (UINT_MAX == 0xffffffffUL) # define Z_U4 unsigned # elif (ULONG_MAX == 0xffffffffUL) # define Z_U4 unsigned long # elif (USHRT_MAX == 0xffffffffUL) # define Z_U4 unsigned short # endif #endif #ifdef Z_U4 typedef Z_U4 z_crc_t; #else typedef unsigned long z_crc_t; #endif #ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_UNISTD_H #endif #ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ # define Z_HAVE_STDARG_H #endif #ifdef STDC # ifndef Z_SOLO # include <sys/types.h> /* for off_t */ # endif #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO # include <stdarg.h> /* for va_list */ # endif #endif #ifdef _WIN32 # ifndef Z_SOLO # include <stddef.h> /* for wchar_t */ # endif #endif /* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even * though the former does not conform to the LFS document), but considering * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as * equivalently requesting no 64-bit operations */ #if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 # undef _LARGEFILE64_SOURCE #endif #ifndef Z_HAVE_UNISTD_H # ifdef __WATCOMC__ # define Z_HAVE_UNISTD_H # endif #endif #ifndef Z_HAVE_UNISTD_H # if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32) # define Z_HAVE_UNISTD_H # endif #endif #ifndef Z_SOLO # if defined(Z_HAVE_UNISTD_H) # include <unistd.h> /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ # ifdef VMS # include <unixio.h> /* for off_t */ # endif # ifndef z_off_t # define z_off_t off_t # endif # endif #endif #if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 # define Z_LFS64 #endif #if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) # define Z_LARGE64 #endif #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) # define Z_WANT64 #endif #if !defined(SEEK_SET) && !defined(Z_SOLO) # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif #ifndef z_off_t # define z_off_t long #endif #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else # if defined(_WIN32) && !defined(__GNUC__) # define z_off64_t __int64 # else # define z_off64_t z_off_t # endif #endif /* MVS linker does not support external names larger than 8 bytes */ #if defined(__MVS__) #pragma map(deflateInit_,"DEIN") #pragma map(deflateInit2_,"DEIN2") #pragma map(deflateEnd,"DEEND") #pragma map(deflateBound,"DEBND") #pragma map(inflateInit_,"ININ") #pragma map(inflateInit2_,"ININ2") #pragma map(inflateEnd,"INEND") #pragma map(inflateSync,"INSY") #pragma map(inflateSetDictionary,"INSEDI") #pragma map(compressBound,"CMBND") #pragma map(inflate_table,"INTABL") #pragma map(inflate_fast,"INFA") #pragma map(inflate_copyright,"INCOPY") #endif #endif /* ZCONF_H */ |
Added compat/zlib/zlib.3.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | .TH ZLIB 3 "22 Jan 2024" .SH NAME zlib \- compression/decompression library .SH SYNOPSIS [see .I zlib.h for full description] .SH DESCRIPTION The .I zlib library is a general purpose data compression library. The code is thread safe, assuming that the standard library functions used are thread safe, such as memory allocation routines. It provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms may be added later with the same stream interface. .LP Compression can be done in a single step if the buffers are large enough or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. .LP The library also supports reading and writing files in .IR gzip (1) (.gz) format with an interface similar to that of stdio. .LP The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash even in the case of corrupted input. .LP All functions of the compression library are documented in the file .IR zlib.h . The distribution source includes examples of use of the library in the files .I test/example.c and .IR test/minigzip.c, as well as other examples in the .IR examples/ directory. .LP Changes to this version are documented in the file .I ChangeLog that accompanies the source. .LP .I zlib is built in to many languages and operating systems, including but not limited to Java, Python, .NET, PHP, Perl, Ruby, Swift, and Go. .LP An experimental package to read and write files in the .zip format, written on top of .I zlib by Gilles Vollant (info@winimage.com), is available at: .IP http://www.winimage.com/zLibDll/minizip.html and also in the .I contrib/minizip directory of the main .I zlib source distribution. .SH "SEE ALSO" The .I zlib web site can be found at: .IP http://zlib.net/ .LP The data format used by the .I zlib library is described by RFC (Request for Comments) 1950 to 1952 in the files: .IP http://tools.ietf.org/html/rfc1950 (for the zlib header and trailer format) .br http://tools.ietf.org/html/rfc1951 (for the deflate compressed data format) .br http://tools.ietf.org/html/rfc1952 (for the gzip header and trailer format) .LP Mark Nelson wrote an article about .I zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at: .IP http://marknelson.us/1997/01/01/zlib-engine/ .SH "REPORTING PROBLEMS" Before reporting a problem, please check the .I zlib web site to verify that you have the latest version of .IR zlib ; otherwise, obtain the latest version and see if the problem still exists. Please read the .I zlib FAQ at: .IP http://zlib.net/zlib_faq.html .LP before asking for help. Send questions and/or comments to zlib@gzip.org, or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). .SH AUTHORS AND LICENSE Version 1.3.1 .LP Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler .LP This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. .LP Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: .LP .nr step 1 1 .IP \n[step]. 3 The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. .IP \n+[step]. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. .IP \n+[step]. This notice may not be removed or altered from any source distribution. .LP Jean-loup Gailly Mark Adler .br jloup@gzip.org madler@alumni.caltech.edu .LP The deflate format used by .I zlib was defined by Phil Katz. The deflate and .I zlib specifications were written by L. Peter Deutsch. Thanks to all the people who reported problems and suggested various improvements in .IR zlib ; who are too numerous to cite here. .LP UNIX manual page by R. P. C. Rodgers, U.S. National Library of Medicine (rodgers@nlm.nih.gov). .\" end of man page |
Added compat/zlib/zlib.3.pdf.
cannot compute difference between binary files
Added compat/zlib/zlib.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* zlib.h -- interface of the 'zlib' general purpose compression library version 1.3.1, January 22nd, 2024 Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ #ifndef ZLIB_H #define ZLIB_H #include "zconf.h" #ifdef __cplusplus extern "C" { #endif #define ZLIB_VERSION "1.3.1" #define ZLIB_VERNUM 0x1310 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 3 #define ZLIB_VER_REVISION 1 #define ZLIB_VER_SUBREVISION 0 /* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms will be added later and will have the same stream interface. Compression can be done in a single step if the buffers are large enough, or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. The compressed data format used by default by the in-memory functions is the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped around a deflate stream, which is itself documented in RFC 1951. The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. This library can optionally read and write gzip and raw deflate streams in memory as well. The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- file compression on file systems, has a larger header than zlib to maintain directory information, and uses a different, slower check method than zlib. The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash even in the case of corrupted input. */ typedef voidpf (*alloc_func)(voidpf opaque, uInt items, uInt size); typedef void (*free_func)(voidpf opaque, voidpf address); struct internal_state; typedef struct z_stream_s { z_const Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ uLong total_in; /* total number of input bytes read so far */ Bytef *next_out; /* next output byte will go here */ uInt avail_out; /* remaining free space at next_out */ uLong total_out; /* total number of bytes output so far */ z_const char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ voidpf opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: binary or text for deflate, or the decoding state for inflate */ uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; typedef z_stream FAR *z_streamp; /* gzip header information passed to and from zlib routines. See RFC 1952 for more details on the meanings of these fields. */ typedef struct gz_header_s { int text; /* true if compressed data believed to be text */ uLong time; /* modification time */ int xflags; /* extra flags (not used when writing a gzip file) */ int os; /* operating system */ Bytef *extra; /* pointer to extra field or Z_NULL if none */ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ uInt extra_max; /* space at extra (only when reading header) */ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ uInt name_max; /* space at name (only when reading header) */ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ uInt comm_max; /* space at comment (only when reading header) */ int hcrc; /* true if there was or will be a header crc */ int done; /* true when done reading gzip header (not used when writing a gzip file) */ } gz_header; typedef gz_header FAR *gz_headerp; /* The application must update next_in and avail_in when avail_in has dropped to zero. It must update next_out and avail_out when avail_out has dropped to zero. The application must initialize zalloc, zfree and opaque before calling the init function. All other fields are set by the compression library and must not be updated by the application. The opaque value provided by the application will be passed as the first parameter for calls of zalloc and zfree. This can be useful for custom memory management. The compression library attaches no meaning to the opaque value. zalloc must return Z_NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be thread safe. In that case, zlib is thread-safe. When zalloc and zfree are Z_NULL on entry to the initialization function, they are set to internal routines that use the standard library functions malloc() and free(). On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers returned by zalloc for objects of exactly 65536 bytes *must* have their offset normalized to zero. The default allocation function provided by this library ensures this (see zutil.c). To reduce memory requirements and avoid any allocation of 64K objects, at the expense of compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the uncompressed data and may be saved for use by the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 #define Z_BLOCK 5 #define Z_TREES 6 /* Allowed flush values; see deflate() and inflate() below for details */ #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) /* Return codes for the compression/decompression functions. Negative values * are errors, positive values are used for special but normal events. */ #define Z_NO_COMPRESSION 0 #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) /* compression levels */ #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_RLE 3 #define Z_FIXED 4 #define Z_DEFAULT_STRATEGY 0 /* compression strategy; see deflateInit2() below for details */ #define Z_BINARY 0 #define Z_TEXT 1 #define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ #define Z_UNKNOWN 2 /* Possible values of the data_type field for deflate() */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ #define zlib_version zlibVersion() /* for compatibility with versions < 1.0.2 */ /* basic functions */ ZEXTERN const char * ZEXPORT zlibVersion(void); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. This check is automatically made by deflateInit and inflateInit. */ /* ZEXTERN int ZEXPORT deflateInit(z_streamp strm, int level); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to Z_NULL, deflateInit updates them to use default allocation functions. total_in, total_out, adler, and msg are initialized. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if level is not a valid compression level, or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflate(z_streamp strm, int flush); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. deflate performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Generate more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter should be set only when necessary. Some output may be provided even if flush is zero. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating avail_in or avail_out accordingly; avail_out should never be zero before the call. The application can consume the compressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. See deflatePending(), which can be used if desired to determine whether or not there is more output in that case. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to decide how much data to accumulate before producing output, in order to maximize compression. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so that the decompressor can get all input data available so far. (In particular avail_in is zero after the call if enough output space has been provided before the call.) Flushing may degrade compression for some compression algorithms and so it should be used only when necessary. This completes the current deflate block and follows it with an empty stored block that is three bits plus filler bits to the next byte, followed by four bytes (00 00 ff ff). If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the output buffer, but the output is not aligned to a byte boundary. All of the input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. This completes the current deflate block and follows it with an empty fixed codes block that is 10 bits long. This assures that enough bytes are output in order for the decompressor to finish the block before the empty fixed codes block. If flush is set to Z_BLOCK, a deflate block is completed and emitted, as for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to seven bits of the current block are held to be written as the next byte after the next deflate block is completed. In this case, the decompressor may not be provided enough bits at this point in order to complete decompression of the data provided so far to the compressor. It may need to wait for the next block to be emitted. This is for advanced applications that need to control the emission of deflate blocks. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if random access is desired. Using Z_FULL_FLUSH too often can seriously degrade compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six when the flush marker begins, in order to avoid repeated flush markers upon calling deflate() again when avail_out == 0. If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. Z_FINISH can be used in the first deflate call after deflateInit if all the compression is to be done in a single step. In order to complete in one call, avail_out must be at least the value returned by deflateBound (see below). Then deflate is guaranteed to return Z_STREAM_END. If not enough output space is provided, deflate will not return Z_STREAM_END, and it must be called again as described above. deflate() sets strm->adler to the Adler-32 checksum of all input read so far (that is, total_in bytes). If a gzip stream is being generated, then strm->adler will be the CRC-32 checksum of the input read so far. (See deflateInit2 below.) deflate() may update strm->data_type if it can make a good guess about the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is considered binary. This field is only for information purposes and does not affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example if next_in or next_out was Z_NULL or the state was inadvertently written over by the application), or Z_BUF_ERROR if no progress is possible (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and deflate() can be called again with more input and more output space to continue compressing. */ ZEXTERN int ZEXPORT deflateEnd(z_streamp strm); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed prematurely (some input or output was discarded). In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* ZEXTERN int ZEXPORT inflateInit(z_streamp strm); Initializes the internal stream state for decompression. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. In the current version of inflate, the provided input is not read or consumed. The allocation of a sliding window will be deferred to the first call of inflate (if the decompression does not complete on the first call). If zalloc and zfree are set to Z_NULL, inflateInit updates them to use default allocation functions. total_in, total_out, adler, and msg are initialized. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit does not perform any decompression. Actual decompression will be done by inflate(). So next_in, and avail_in, next_out, and avail_out are unused and unchanged. The current implementation of inflateInit() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflate(z_streamp strm, int flush); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. inflate performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), then next_in and avail_in are updated accordingly, and processing will resume at this point for the next call of inflate(). - Generate more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating the next_* and avail_* values accordingly. If the caller of inflate() does not provide both available input and available output space, it is possible that there will be no progress made. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much output as possible to the output buffer. Z_BLOCK requests that inflate() stop if and when it gets to the next deflate block boundary. When decoding the zlib or gzip format, this will cause inflate() to return immediately after the header and before the first block. When doing a raw inflate, inflate() will go ahead and process the first block, and will return when it gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. To assist in this, on return inflate() always sets strm->data_type to the number of unused bits in the last byte taken from strm->next_in, plus 64 if inflate() is currently decoding the last block in the deflate stream, plus 128 if inflate() returned immediately after decoding an end-of-block code or decoding the complete header up to just before the first byte of the deflate stream. The end-of-block will not be indicated until all of the uncompressed data from that block has been written to strm->next_out. The number of unused bits may in general be greater than seven, except when bit 7 of data_type is set, in which case the number of unused bits will be less than eight. data_type is set as noted here every time inflate() returns for all flush options, and so can be used to determine the amount of currently consumed input in bits. The Z_TREES option behaves as Z_BLOCK does, but it also returns when the end of each deflate block header is reached, before any actual data in that block is decoded. This allows the caller to determine the length of the deflate block header for later use in random access within a deflate block. 256 is added to the value of strm->data_type when inflate() returns immediately after reaching the end of the deflate block header. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all of the uncompressed data for the operation to complete. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The use of Z_FINISH is not required to perform an inflation in one step. However it may be used to inform inflate that a faster approach can be used for the single inflate() call. Z_FINISH also informs inflate to not maintain a sliding window if the stream completes, which reduces inflate's memory footprint. If the stream does not complete, either because not all of the stream is provided or not enough output space is provided, then a sliding window will be allocated and inflate() can be called again to continue the operation as if Z_NO_FLUSH had been used. In this implementation, inflate() always flushes as much output as possible to the output buffer, and always uses the faster approach on the first call. So the effects of the flush parameter in this implementation are on the return value of inflate() as noted below, when inflate() returns early when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of memory for a sliding window when Z_FINISH is used. If a preset dictionary is needed after this call (see inflateSetDictionary below), inflate sets strm->adler to the Adler-32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets strm->adler to the Adler-32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described below. At the end of the stream, inflate() checks that its computed Adler-32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. inflate() can decompress and check either zlib-wrapped or gzip-wrapped deflate data. The header type is detected automatically, if requested when initializing with inflateInit2(). Any information contained in the gzip header is not retained unless inflateGetHeader() is used. When processing gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output produced so far. The CRC-32 is checked against the gzip trailer, as is the uncompressed length, modulo 2^32. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check value, in which case strm->msg points to a string with a more specific error), Z_STREAM_ERROR if the stream structure was inconsistent (for example next_in or next_out was Z_NULL, or the state was inadvertently written over by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress was possible or if there was not enough room in the output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing. If Z_DATA_ERROR is returned, the application may then call inflateSync() to look for a good compression block if a partial recovery of the data is to be attempted. */ ZEXTERN int ZEXPORT inflateEnd(z_streamp strm); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state was inconsistent. */ /* Advanced functions */ /* The following functions are needed only in some special applications. */ /* ZEXTERN int ZEXPORT deflateInit2(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy); This is another version of deflateInit with more compression options. The fields zalloc, zfree and opaque must be initialized before by the caller. The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. For the current implementation of deflate(), a windowBits value of 8 (a window size of 256 bytes) is not supported. As a result, a request for 8 will result in 9 (a 512-byte window). In that case, providing 8 to inflateInit2() will result in an error when the zlib header with 9 is checked against the initialization of inflate(). The remedy is to not use 8 with deflateInit2() with this initialization, or at least in that case use 9 with inflateInit2(). windowBits can also be -8..-15 for raw deflate. In this case, -windowBits determines the window size. deflate() will then generate raw deflate data with no zlib header or trailer, and will not compute a check value. windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. The gzip header will have no file name, no extra data, no comment, no modification time (set to zero), no header crc, and the operating system will be set to the appropriate value, if the operating system was determined at compile time. If a gzip stream is being written, strm->adler is a CRC-32 instead of an Adler-32. For raw deflate or gzip encoding, a request for a 256-byte window is rejected as invalid, since only the zlib header provides a means of transmitting the window size to the decompressor. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no string match), or Z_RLE to limit match distances to one (run-length encoding). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit2 does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateSetDictionary(z_streamp strm, const Bytef *dictionary, uInt dictLength); /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. When using the zlib format, this function must be called immediately after deflateInit, deflateInit2 or deflateReset, and before any call of deflate. When doing raw deflate, this function must be called either before any call of deflate, or immediately after the completion of a deflate block, i.e. after all input has been consumed and all output has been delivered when using any of the flush options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The compressor and decompressor must use exactly the same dictionary (see inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly used strings preferably put towards the end of the dictionary. Using a dictionary is most useful when the data to be compressed is short and can be predicted with good accuracy; the data can then be compressed better than with the default empty dictionary. Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be discarded, for example if the dictionary is larger than the window size provided in deflateInit or deflateInit2. Thus the strings most likely to be useful should be put at the end of the dictionary, not at the front. In addition, the current implementation of deflate will use at most the window size minus 262 bytes of the provided dictionary. Upon return of this function, strm->adler is set to the Adler-32 value of the dictionary; the decompressor may later use this value to determine which dictionary has been used by the compressor. (The Adler-32 value applies to the whole dictionary even if only a subset of the dictionary is actually used by the compressor.) If a raw deflate was requested, then the Adler-32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent (for example if deflate has already been called for this stream or if not at a block boundary for raw deflate). deflateSetDictionary does not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateGetDictionary(z_streamp strm, Bytef *dictionary, uInt *dictLength); /* Returns the sliding dictionary being maintained by deflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If deflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. Similarly, if dictLength is Z_NULL, then it is not set. deflateGetDictionary() may return a length less than the window size, even when more than the window size in input has been provided. It may return up to 258 bytes less in that case, due to how zlib's implementation of deflate manages the sliding window and lookahead for matches, where matches can be up to 258 bytes long. If the application needs the last window-size bytes of input, then that would need to be saved by the application outside of zlib. deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the stream state is inconsistent. */ ZEXTERN int ZEXPORT deflateCopy(z_streamp dest, z_streamp source); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when several compression strategies will be tried, for example when there are several ways of pre-processing the input data with a filter. The streams that will be discarded should then be freed by calling deflateEnd. Note that deflateCopy duplicates the internal compression state which can be quite large, so this strategy is slow and can consume lots of memory. deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT deflateReset(z_streamp strm); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate the internal compression state. The stream will leave the compression level and any other attributes that may have been set unchanged. total_in, total_out, adler, and msg are initialized. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT deflateParams(z_streamp strm, int level, int strategy); /* Dynamically update the compression level and compression strategy. The interpretation of level and strategy is as in deflateInit2(). This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression approach (which is a function of the level) or the strategy is changed, and if there have been any deflate() calls since the state was initialized or reset, then the input available so far is compressed with the old level and strategy using deflate(strm, Z_BLOCK). There are three approaches for the compression levels 0, 1..3, and 4..9 respectively. The new level and strategy will take effect at the next call of deflate(). If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does not have enough output space to complete, then the parameter change will not take effect. In this case, deflateParams() can be called again with the same parameters and more output space to try again. In order to assure a change in the parameters on the first try, the deflate stream should be flushed using deflate() with Z_BLOCK or other flush request until strm.avail_out is not zero, before calling deflateParams(). Then no more input data should be provided before the deflateParams() call. If this is done, the old level and strategy will be applied to the data compressed before deflateParams(), and the new level and strategy will be applied to the data compressed after deflateParams(). deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if there was not enough output space to complete the compression of the available input data before a change in the strategy or approach. Note that in the case of a Z_BUF_ERROR, the parameters are not changed. A return value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be retried with more output space. */ ZEXTERN int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy, int nice_length, int max_chain); /* Fine tune deflate's internal compression parameters. This should only be used by someone who understands the algorithm used by zlib's deflate for searching for the best matching string, and even then only by the most fanatic optimizer trying to squeeze out the last compressed bit for their specific input data. Read the deflate.c source code for the meaning of the max_lazy, good_length, nice_length, and max_chain parameters. deflateTune() can be called after deflateInit() or deflateInit2(), and returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen); /* deflateBound() returns an upper bound on the compressed size after deflation of sourceLen bytes. It must be called after deflateInit() or deflateInit2(), and after deflateSetHeader(), if used. This would be used to allocate an output buffer for deflation in a single pass, and so would be called before deflate(). If that first deflate() call is provided the sourceLen input bytes, an output buffer allocated to the size returned by deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed to return Z_STREAM_END. Note that it is possible for the compressed size to be larger than the value returned by deflateBound() if flush options other than Z_FINISH or Z_NO_FLUSH are used. */ ZEXTERN int ZEXPORT deflatePending(z_streamp strm, unsigned *pending, int *bits); /* deflatePending() returns the number of bytes and bits of output that have been generated, but not yet provided in the available output. The bytes not provided would be due to the available output space having being consumed. The number of bits of output not provided are between 0 and 7, where they await more bits to join them in order to fill out a full byte. If pending or bits are Z_NULL, then those values are not set. deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflatePrime(z_streamp strm, int bits, int value); /* deflatePrime() inserts bits in the deflate output stream. The intent is that this function is used to start off the deflate output with the bits leftover from a previous deflate stream when appending to it. As such, this function can only be used for raw deflate, and must be used before the first deflate() call after a deflateInit2() or deflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the output. deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflateSetHeader(z_streamp strm, gz_headerp head); /* deflateSetHeader() provides gzip header information for when a gzip stream is requested by deflateInit2(). deflateSetHeader() may be called after deflateInit2() or deflateReset() and before the first call of deflate(). The text, time, os, extra field, name, and comment information in the provided gz_header structure are written to the gzip header (xflag is ignored -- the extra flags are set according to the compression level). The caller must assure that, if not Z_NULL, name and comment are terminated with a zero byte, and that if extra is not Z_NULL, that extra_len bytes are available there. If hcrc is true, a gzip header crc is included. Note that the current versions of the command-line version of gzip (up through version 1.3.x) do not support header crc's, and will report that it is a "multi-part gzip file" and give up. If deflateSetHeader is not used, the default gzip header has text false, the time set to zero, and os set to the current operating system, with no extra, name, or comment fields. The gzip header is returned to the default state by deflateReset(). deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateInit2(z_streamp strm, int windowBits); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. The default value is 15 if inflateInit is used instead. windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing, or it must be equal to 15 if deflateInit2() was not used. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. windowBits can also be zero to request that inflate use the window size in the zlib header of the compressed stream. windowBits can also be -8..-15 for raw inflate. In this case, -windowBits determines the window size. inflate() will then process raw deflate data, not looking for a zlib or gzip header, not generating a check value, and not looking for any check values for comparison at the end of the stream. This is for use with other formats that use the deflate compressed data format such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is recommended that a check value such as an Adler-32 or a CRC-32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. windowBits can also be greater than 15 for optional gzip decoding. Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see below), inflate() will *not* automatically decode concatenated gzip members. inflate() will return Z_STREAM_END at the end of the gzip member. The state would need to be reset to continue decoding a subsequent gzip member. This *must* be done if there is more data after a gzip member, in order for the decompression to be compliant with the gzip standard (RFC 1952). inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller, or Z_STREAM_ERROR if the parameters are invalid, such as a null pointer to the structure. msg is set to null if there is no error message. inflateInit2 does not perform any decompression apart from possibly reading the zlib header if present: actual decompression will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unused and unchanged.) The current implementation of inflateInit2() does not process any header information -- that is deferred until inflate() is called. */ ZEXTERN int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary, uInt dictLength); /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, if that call returned Z_NEED_DICT. The dictionary chosen by the compressor can be determined from the Adler-32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see deflateSetDictionary). For raw inflate, this function can be called at any time to set the dictionary. If the provided dictionary is smaller than the window and there is already data in the window, then the provided dictionary will amend what's there. The application must insure that the dictionary that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the expected one (incorrect Adler-32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ ZEXTERN int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary, uInt *dictLength); /* Returns the sliding dictionary being maintained by inflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied to dictionary. dictionary must have enough space, where 32768 bytes is always enough. If inflateGetDictionary() is called with dictionary equal to Z_NULL, then only the dictionary length is returned, and nothing is copied. Similarly, if dictLength is Z_NULL, then it is not set. inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the stream state is inconsistent. */ ZEXTERN int ZEXPORT inflateSync(z_streamp strm); /* Skips invalid compressed data until a possible full flush point (see above for the description of deflate with Z_FULL_FLUSH) can be found, or until all available input is skipped. No output is provided. inflateSync searches for a 00 00 FF FF pattern in the compressed data. All full flush points have this pattern, but not all occurrences of this pattern are full flush points. inflateSync returns Z_OK if a possible full flush point has been found, Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the success case, the application may save the current value of total_in which indicates where valid compressed data was found. In the error case, the application may repeatedly call inflateSync, providing more input each time, until success or end of the input data. */ ZEXTERN int ZEXPORT inflateCopy(z_streamp dest, z_streamp source); /* Sets the destination stream as a complete copy of the source stream. This function can be useful when randomly accessing a large stream. The first pass through the stream can periodically record the inflate state, allowing restarting inflate at those points when randomly accessing the stream. inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ ZEXTERN int ZEXPORT inflateReset(z_streamp strm); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. total_in, total_out, adler, and msg are initialized. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT inflateReset2(z_streamp strm, int windowBits); /* This function is the same as inflateReset, but it also permits changing the wrap and window size requests. The windowBits parameter is interpreted the same as it is for inflateInit2. If the window size is changed, then the memory allocated for the window is freed, and the window will be reallocated by inflate() if needed. inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being Z_NULL), or if the windowBits parameter is invalid. */ ZEXTERN int ZEXPORT inflatePrime(z_streamp strm, int bits, int value); /* This function inserts bits in the inflate input stream. The intent is that this function is used to start inflating at a bit position in the middle of a byte. The provided bits will be used before any bytes are used from next_in. This function should only be used with raw inflate, and should be used before the first inflate() call after inflateInit2() or inflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the input. If bits is negative, then the input stream bit buffer is emptied. Then inflatePrime() can be called again to put bits in the buffer. This is used to clear out bits leftover after feeding inflate a block description prior to feeding inflate codes. inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ ZEXTERN long ZEXPORT inflateMark(z_streamp strm); /* This function returns two values, one in the lower 16 bits of the return value, and the other in the remaining upper bits, obtained by shifting the return value down 16 bits. If the upper value is -1 and the lower value is zero, then inflate() is currently decoding information outside of a block. If the upper value is -1 and the lower value is non-zero, then inflate is in the middle of a stored block, with the lower value equaling the number of bytes from the input remaining to copy. If the upper value is not -1, then it is the number of bits back from the current bit position in the input of the code (literal or length/distance pair) currently being processed. In that case the lower value is the number of bytes already emitted for that code. A code is being processed if inflate is waiting for more input to complete decoding of the code, or if it has completed decoding but is waiting for more output space to write the literal or match data. inflateMark() is used to mark locations in the input data for random access, which may be at bit positions, and to note those cases where the output of a code may span boundaries of random access blocks. The current location in the input stream can be determined from avail_in and data_type as noted in the description for the Z_BLOCK flush parameter for inflate. inflateMark returns the value noted above, or -65536 if the provided source stream state was inconsistent. */ ZEXTERN int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head); /* inflateGetHeader() requests that gzip header information be stored in the provided gz_header structure. inflateGetHeader() may be called after inflateInit2() or inflateReset(), and before the first call of inflate(). As inflate() processes the gzip stream, head->done is zero until the header is completed, at which time head->done is set to one. If a zlib stream is being decoded, then head->done is set to -1 to indicate that there will be no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be used to force inflate() to return immediately after header processing is complete and before any actual data is decompressed. The text, time, xflags, and os fields are filled in with the gzip header contents. hcrc is set to true if there is a header CRC. (The header CRC was valid if done is set to one.) If extra is not Z_NULL, then extra_max contains the maximum number of bytes to write to extra. Once done is true, extra_len contains the actual extra field length, and extra contains the extra field, or that field truncated if extra_max is less than extra_len. If name is not Z_NULL, then up to name_max characters are written there, terminated with a zero unless the length is greater than name_max. If comment is not Z_NULL, then up to comm_max characters are written there, terminated with a zero unless the length is greater than comm_max. When any of extra, name, or comment are not Z_NULL and the respective field is not present in the header, then that field is set to Z_NULL to signal its absence. This allows the use of deflateSetHeader() with the returned structure to duplicate the header. However if those fields are set to allocated memory, then the application will need to save those pointers elsewhere so that they can be eventually freed. If inflateGetHeader is not used, then the header information is simply discarded. The header is always checked for validity, including the header CRC if present. inflateReset() will reset the process to discard the header information. The application would need to call inflateGetHeader() again to retrieve the header from the next gzip stream. inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ /* ZEXTERN int ZEXPORT inflateBackInit(z_streamp strm, int windowBits, unsigned char FAR *window); Initialize the internal stream state for decompression using inflateBack() calls. The fields zalloc, zfree and opaque in strm must be initialized before the call. If zalloc and zfree are Z_NULL, then the default library- derived memory allocation routines are used. windowBits is the base two logarithm of the window size, in the range 8..15. window is a caller supplied buffer of that size. Except for special applications where it is assured that deflate was used with small window sizes, windowBits must be 15 and a 32K byte window must be supplied to be able to decompress general deflate streams. See inflateBack() for the usage of these routines. inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of the parameters are invalid, Z_MEM_ERROR if the internal state could not be allocated, or Z_VERSION_ERROR if the version of the library does not match the version of the header file. */ typedef unsigned (*in_func)(void FAR *, z_const unsigned char FAR * FAR *); typedef int (*out_func)(void FAR *, unsigned char FAR *, unsigned); ZEXTERN int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc); /* inflateBack() does a raw inflate with a single call using a call-back interface for input and output. This is potentially more efficient than inflate() for file i/o applications, in that it avoids copying between the output and the sliding window by simply making the window itself the output buffer. inflate() can be faster on modern CPUs when used with large buffers. inflateBack() trusts the application to not change the output buffer passed by the output function, at least until inflateBack() returns. inflateBackInit() must be called first to allocate the internal state and to initialize the state with the user-provided window buffer. inflateBack() may then be used multiple times to inflate a complete, raw deflate stream with each call. inflateBackEnd() is then called to free the allocated state. A raw deflate stream is one with no zlib or gzip header or trailer. This routine would normally be used in a utility that reads zip or gzip files and writes out uncompressed files. The utility would decode the header and process the trailer on its own, hence this routine expects only the raw deflate stream to decompress. This is different from the default behavior of inflate(), which expects a zlib header and trailer around the deflate stream. inflateBack() uses two subroutines supplied by the caller that are then called by inflateBack() for input and output. inflateBack() calls those routines until it reads a complete deflate stream and writes out all of the uncompressed data, or until it encounters an error. The function's parameters and return types are defined above in the in_func and out_func typedefs. inflateBack() will call in(in_desc, &buf) which should return the number of bytes of provided input, and a pointer to that input in buf. If there is no input available, in() must return zero -- buf is ignored in that case -- and inflateBack() will return a buffer error. inflateBack() will call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() should return zero on success, or non-zero on failure. If out() returns non-zero, inflateBack() will return with an error. Neither in() nor out() are permitted to change the contents of the window provided to inflateBackInit(), which is also the buffer that out() uses to write from. The length written by out() will be at most the window size. Any non-zero amount of input may be provided by in(). For convenience, inflateBack() can be provided input on the first call by setting strm->next_in and strm->avail_in. If that input is exhausted, then in() will be called. Therefore strm->next_in must be initialized before calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in must also be initialized, and then if strm->avail_in is not zero, input will initially be taken from strm->next_in[0 .. strm->avail_in - 1]. The in_desc and out_desc parameters of inflateBack() is passed as the first parameter of in() and out() respectively when they are called. These descriptors can be optionally used to pass any information that the caller- supplied in() and out() functions need to do their job. On return, inflateBack() will set strm->next_in and strm->avail_in to pass back any unused input that was provided by the last in() call. The return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR if in() or out() returned an error, Z_DATA_ERROR if there was a format error in the deflate stream (in which case strm->msg is set to indicate the nature of the error), or Z_STREAM_ERROR if the stream was not properly initialized. In the case of Z_BUF_ERROR, an input or output error can be distinguished using strm->next_in which will be Z_NULL only if in() returned an error. If strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning non-zero. (in() will always be called before out(), so strm->next_in is assured to be defined if out() returns non-zero.) Note that inflateBack() cannot return Z_OK. */ ZEXTERN int ZEXPORT inflateBackEnd(z_streamp strm); /* All memory allocated by inflateBackInit() is freed. inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream state was inconsistent. */ ZEXTERN uLong ZEXPORT zlibCompileFlags(void); /* Return flags indicating compile-time options. Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: 1.0: size of uInt 3.2: size of uLong 5.4: size of voidpf (pointer) 7.6: size of z_off_t Compiler, assembler, and debug options: 8: ZLIB_DEBUG 9: ASMV or ASMINF -- use ASM code 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention 11: 0 (reserved) One-time table building (smaller code, but not thread-safe if true): 12: BUILDFIXED -- build static block decoding tables when needed 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed 14,15: 0 (reserved) Library content (indicates missing functionality): 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking deflate code when not needed) 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect and decode gzip streams (to avoid linking crc code) 18-19: 0 (reserved) Operation variations (changes in library functionality): 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate 21: FASTEST -- deflate algorithm with only one, lowest compression level 22,23: 0 (reserved) The sprintf variant used by gzprintf (zero is best): 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! 26: 0 = returns value, 1 = void -- 1 means inferred string length returned Remainder: 27-31: 0 (reserved) */ #ifndef Z_SOLO /* utility functions */ /* The following utility functions are implemented on top of the basic stream-oriented functions. To simplify the interface, some default options are assumed (compression level and memory usage, standard memory allocation functions). The source code of these utility functions can be modified if you need special options. */ ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen); /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed data. compress() is equivalent to compress2() with a level parameter of Z_DEFAULT_COMPRESSION. compress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer. */ ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level); /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be at least the value returned by compressBound(sourceLen). Upon exit, destLen is the actual size of the compressed data. compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, Z_STREAM_ERROR if the level parameter is invalid. */ ZEXTERN uLong ZEXPORT compressBound(uLong sourceLen); /* compressBound() returns an upper bound on the compressed size after compress() or compress2() on sourceLen bytes. It would be used before a compress() or compress2() call to allocate the destination buffer. */ ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen); /* Decompresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) Upon exit, destLen is the actual size of the uncompressed data. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In the case where there is not enough room, uncompress() will fill the output buffer with the uncompressed data up to that point. */ ZEXTERN int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source, uLong *sourceLen); /* Same as uncompress, except that sourceLen is a pointer, where the length of the source is *sourceLen. On return, *sourceLen is the number of source bytes consumed. */ /* gzip file access functions */ /* This library supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio, using the functions that start with "gz". The gzip format is different from the zlib format. gzip is a gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. */ typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ /* ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode); Open the gzip (.gz) file at path for reading and decompressing, or compressing and writing. The mode parameter is as in fopen ("rb" or "wb") but can also include a compression level ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression as in "wb9F". (See the description of deflateInit2 for more information about the strategy parameter.) 'T' will request transparent writing or appending with no compression and not using the gzip format. "a" can be used instead of "w" to request that the gzip stream that will be written be appended to the file. "+" will result in an error, since reading and writing to the same gzip file is not supported. The addition of "x" when writing will create the file exclusively, which fails if the file already exists. On systems that support it, the addition of "e" when reading or writing will set the flag to close the file on an execve() call. These functions, as well as gzip, will read and decode a sequence of gzip streams in a file. The append function of gzopen() can be used to create such a file. (Also see gzflush() for another way to do this.) When appending, gzopen does not test whether the file begins with a gzip stream, nor does it look for the end of the gzip streams to begin appending. gzopen will simply append a gzip stream to the existing file. gzopen can be used to read a file which is not in gzip format; in this case gzread will directly read from the file without decompression. When reading, this will be detected automatically by looking for the magic two- byte gzip header. gzopen returns NULL if the file could not be opened, if there was insufficient memory to allocate the gzFile state, or if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). errno can be checked to determine if the reason gzopen failed was that the file could not be opened. */ ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode); /* Associate a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (if the file has been previously opened with fopen). The mode parameter is as in gzopen. The next call of gzclose on the returned gzFile will also close the file descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, mode);. The duplicated descriptor should be saved to avoid a leak, since gzdopen does not close fd if it fails. If you are using fileno() to get the file descriptor from a FILE *, then you will have to use dup() to avoid double-close()ing the file descriptor. Both gzclose() and fclose() will close the associated file descriptor, so they need to have different file descriptors. gzdopen returns NULL if there was insufficient memory to allocate the gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided), or if fd is -1. The file descriptor is not used until the next gz* read, write, seek, or close operation, so gzdopen will not detect if fd is invalid (unless fd is -1). */ ZEXTERN int ZEXPORT gzbuffer(gzFile file, unsigned size); /* Set the internal buffer size used by this library's functions for file to size. The default buffer size is 8192 bytes. This function must be called after gzopen() or gzdopen(), and before any other calls that read or write the file. The buffer memory allocation is always deferred to the first read or write. Three times that size in buffer space is allocated. A larger buffer size of, for example, 64K or 128K bytes will noticeably increase the speed of decompression (reading). The new buffer size also affects the maximum length for gzprintf(). gzbuffer() returns 0 on success, or -1 on failure, such as being called too late. */ ZEXTERN int ZEXPORT gzsetparams(gzFile file, int level, int strategy); /* Dynamically update the compression level and strategy for file. See the description of deflateInit2 for the meaning of these parameters. Previously provided data is flushed before applying the parameter changes. gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not opened for writing, Z_ERRNO if there is an error writing the flushed data, or Z_MEM_ERROR if there is a memory allocation error. */ ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len); /* Read and decompress up to len uncompressed bytes from file into buf. If the input file is not in gzip format, gzread copies the given number of bytes into the buffer directly from the file. After reaching the end of a gzip stream in the input, gzread will continue to read, looking for another gzip stream. Any number of gzip streams may be concatenated in the input file, and will all be decompressed by gzread(). If something other than a gzip stream is encountered after a gzip stream, that remaining trailing garbage is ignored (and no error is returned). gzread can be used to read a gzip file that is being concurrently written. Upon reaching the end of the input, gzread will return with the available data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then gzclearerr can be used to clear the end of file indicator in order to permit gzread to be tried again. Z_OK indicates that a gzip stream was completed on the last gzread. Z_BUF_ERROR indicates that the input file ended in the middle of a gzip stream. Note that gzread does not return -1 in the event of an incomplete gzip stream. This error is deferred until gzclose(), which will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip stream. Alternatively, gzerror can be used before gzclose to detect this case. gzread returns the number of uncompressed bytes actually read, less than len for end of file, or -1 for error. If len is too large to fit in an int, then nothing is read, -1 is returned, and the error state is set to Z_STREAM_ERROR. */ ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file); /* Read and decompress up to nitems items of size size from file into buf, otherwise operating as gzread() does. This duplicates the interface of stdio's fread(), with size_t request and return types. If the library defines size_t, then z_size_t is identical to size_t. If not, then z_size_t is an unsigned integer type that can contain a pointer. gzfread() returns the number of full items read of size size, or zero if the end of the file was reached and a full item could not be read, or if there was an error. gzerror() must be consulted if zero is returned in order to determine if there was an error. If the multiplication of size and nitems overflows, i.e. the product does not fit in a z_size_t, then nothing is read, zero is returned, and the error state is set to Z_STREAM_ERROR. In the event that the end of file is reached and only a partial item is available at the end, i.e. the remaining uncompressed data length is not a multiple of size, then the final partial item is nevertheless read into buf and the end-of-file flag is set. The length of the partial item read is not provided, but could be inferred from the result of gztell(). This behavior is the same as the behavior of fread() implementations in common libraries, but it prevents the direct use of gzfread() to read a concurrently written file, resetting and retrying on end-of-file, when size is not 1. */ ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len); /* Compress and write the len uncompressed bytes at buf to file. gzwrite returns the number of uncompressed bytes written or 0 in case of error. */ ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, z_size_t nitems, gzFile file); /* Compress and write nitems items of size size from buf to file, duplicating the interface of stdio's fwrite(), with size_t request and return types. If the library defines size_t, then z_size_t is identical to size_t. If not, then z_size_t is an unsigned integer type that can contain a pointer. gzfwrite() returns the number of full items written of size size, or zero if there was an error. If the multiplication of size and nitems overflows, i.e. the product does not fit in a z_size_t, then nothing is written, zero is returned, and the error state is set to Z_STREAM_ERROR. */ ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); /* Convert, format, compress, and write the arguments (...) to file under control of the string format, as in fprintf. gzprintf returns the number of uncompressed bytes actually written, or a negative zlib error code in case of error. The number of uncompressed bytes written is limited to 8191, or one less than the buffer size given to gzbuffer(). The caller should assure that this limit is not exceeded. If it is exceeded, then gzprintf() will return an error (0) with nothing written. In this case, there may also be a buffer overflow with unpredictable consequences, which is possible only if zlib was compiled with the insecure functions sprintf() or vsprintf(), because the secure snprintf() or vsnprintf() functions were not available. This can be determined using zlibCompileFlags(). */ ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s); /* Compress and write the given null-terminated string s to file, excluding the terminating null character. gzputs returns the number of characters written, or -1 in case of error. */ ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len); /* Read and decompress bytes from file into buf, until len-1 characters are read, or until a newline character is read and transferred to buf, or an end-of-file condition is encountered. If any characters are read or if len is one, the string is terminated with a null character. If no characters are read due to an end-of-file or len is less than one, then the buffer is left untouched. gzgets returns buf which is a null-terminated string, or it returns NULL for end-of-file or in case of error. If there was an error, the contents at buf are indeterminate. */ ZEXTERN int ZEXPORT gzputc(gzFile file, int c); /* Compress and write c, converted to an unsigned char, into file. gzputc returns the value that was written, or -1 in case of error. */ ZEXTERN int ZEXPORT gzgetc(gzFile file); /* Read and decompress one byte from file. gzgetc returns this byte or -1 in case of end of file or error. This is implemented as a macro for speed. As such, it does not do all of the checking the other functions do. I.e. it does not check to see if file is NULL, nor whether the structure file points to has been clobbered or not. */ ZEXTERN int ZEXPORT gzungetc(int c, gzFile file); /* Push c back onto the stream for file to be read as the first character on the next read. At least one character of push-back is always allowed. gzungetc() returns the character pushed, or -1 on failure. gzungetc() will fail if c is -1, and may fail if a character has been pushed but not read yet. If gzungetc is used immediately after gzopen or gzdopen, at least the output buffer size of pushed characters is allowed. (See gzbuffer above.) The pushed character will be discarded if the stream is repositioned with gzseek() or gzrewind(). */ ZEXTERN int ZEXPORT gzflush(gzFile file, int flush); /* Flush all pending output to file. The parameter flush is as in the deflate() function. The return value is the zlib error number (see function gzerror below). gzflush is only permitted when writing. If the flush parameter is Z_FINISH, the remaining data is written and the gzip stream is completed in the output. If gzwrite() is called again, a new gzip stream will be started in the output. gzread() is able to read such concatenated gzip streams. gzflush should be called only when strictly necessary because it will degrade compression if called too often. */ /* ZEXTERN z_off_t ZEXPORT gzseek(gzFile file, z_off_t offset, int whence); Set the starting position to offset relative to whence for the next gzread or gzwrite on file. The offset represents a number of bytes in the uncompressed data stream. The whence parameter is defined as in lseek(2); the value SEEK_END is not supported. If the file is opened for reading, this function is emulated but can be extremely slow. If the file is opened for writing, only forward seeks are supported; gzseek then compresses a sequence of zeroes up to the new starting position. gzseek returns the resulting offset location as measured in bytes from the beginning of the uncompressed stream, or -1 in case of error, in particular if the file is opened for writing and the new starting position would be before the current position. */ ZEXTERN int ZEXPORT gzrewind(gzFile file); /* Rewind file. This function is supported only for reading. gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET). */ /* ZEXTERN z_off_t ZEXPORT gztell(gzFile file); Return the starting position for the next gzread or gzwrite on file. This position represents a number of bytes in the uncompressed data stream, and is zero when starting, even if appending or reading a gzip stream from the middle of a file using gzdopen(). gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) */ /* ZEXTERN z_off_t ZEXPORT gzoffset(gzFile file); Return the current compressed (actual) read or write offset of file. This offset includes the count of bytes that precede the gzip stream, for example when appending or when using gzdopen() for reading. When reading, the offset does not include as yet unused buffered input. This information can be used for a progress indicator. On error, gzoffset() returns -1. */ ZEXTERN int ZEXPORT gzeof(gzFile file); /* Return true (1) if the end-of-file indicator for file has been set while reading, false (0) otherwise. Note that the end-of-file indicator is set only if the read tried to go past the end of the input, but came up short. Therefore, just like feof(), gzeof() may return false even if there is no more data to read, in the event that the last read request was for the exact number of bytes remaining in the input file. This will happen if the input file size is an exact multiple of the buffer size. If gzeof() returns true, then the read functions will return no more data, unless the end-of-file indicator is reset by gzclearerr() and the input file has grown since the previous end of file was detected. */ ZEXTERN int ZEXPORT gzdirect(gzFile file); /* Return true (1) if file is being copied directly while reading, or false (0) if file is a gzip stream being decompressed. If the input file is empty, gzdirect() will return true, since the input does not contain a gzip stream. If gzdirect() is used immediately after gzopen() or gzdopen() it will cause buffers to be allocated to allow reading the file to determine if it is a gzip file. Therefore if gzbuffer() is used, it should be called before gzdirect(). When writing, gzdirect() returns true (1) if transparent writing was requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: gzdirect() is not needed when writing. Transparent writing must be explicitly requested, so the application already knows the answer. When linking statically, using gzdirect() will include all of the zlib code for gzip file reading and decompression, which may not be desired.) */ ZEXTERN int ZEXPORT gzclose(gzFile file); /* Flush all pending output for file, if necessary, close file and deallocate the (de)compression state. Note that once file is closed, you cannot call gzerror with file, since its structures have been deallocated. gzclose must not be called more than once on the same file, just as free must not be called more than once on the same allocation. gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the last read ended in the middle of a gzip stream, or Z_OK on success. */ ZEXTERN int ZEXPORT gzclose_r(gzFile file); ZEXTERN int ZEXPORT gzclose_w(gzFile file); /* Same as gzclose(), but gzclose_r() is only for use when reading, and gzclose_w() is only for use when writing or appending. The advantage to using these instead of gzclose() is that they avoid linking in zlib compression or decompression code that is not used when only reading or only writing respectively. If gzclose() is used, then both compression and decompression code will be included the application when linking to a static zlib library. */ ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum); /* Return the error message for the last error which occurred on file. errnum is set to zlib error number. If an error occurred in the file system and not in the compression library, errnum is set to Z_ERRNO and the application may consult errno to get the exact error code. The application must not modify the returned string. Future calls to this function may invalidate the previously returned string. If file is closed, then the string previously returned by gzerror will no longer be available. gzerror() should be used to distinguish errors from end-of-file for those functions above that do not distinguish those cases in their return values. */ ZEXTERN void ZEXPORT gzclearerr(gzFile file); /* Clear the error and end-of-file flags for file. This is analogous to the clearerr() function in stdio. This is useful for continuing to read a gzip file that is being written concurrently. */ #endif /* !Z_SOLO */ /* checksum functions */ /* These functions are not related to compression but are exported anyway because they might be useful in applications using the compression library. */ ZEXTERN uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. An Adler-32 value is in the range of a 32-bit unsigned integer. If buf is Z_NULL, this function returns the required initial value for the checksum. An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed much faster. Usage example: uLong adler = adler32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { adler = adler32(adler, buffer, length); } if (adler != original_adler) error(); */ ZEXTERN uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, z_size_t len); /* Same as adler32(), but with a size_t length. */ /* ZEXTERN uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2); Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note that the z_off_t type (like off_t) is a signed integer. If len2 is negative, the result has no meaning or utility. */ ZEXTERN uLong ZEXPORT crc32(uLong crc, const Bytef *buf, uInt len); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer. If buf is Z_NULL, this function returns the required initial value for the crc. Pre- and post-conditioning (one's complement) is performed within this function so it shouldn't be done by the application. Usage example: uLong crc = crc32(0L, Z_NULL, 0); while (read_buffer(buffer, length) != EOF) { crc = crc32(crc, buffer, length); } if (crc != original_crc) error(); */ ZEXTERN uLong ZEXPORT crc32_z(uLong crc, const Bytef *buf, z_size_t len); /* Same as crc32(), but with a size_t length. */ /* ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2); Combine two CRC-32 check values into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and len2. len2 must be non-negative. */ /* ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2); Return the operator corresponding to length len2, to be used with crc32_combine_op(). len2 must be non-negative. */ ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op); /* Give the same result as crc32_combine(), using op in place of len2. op is is generated from len2 by crc32_combine_gen(). This will be faster than crc32_combine() if the generated op is used more than once. */ /* various hacks, don't look :) */ /* deflateInit and inflateInit are macros to allow checking the zlib version * and the compiler's view of z_stream: */ ZEXTERN int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version, int stream_size); ZEXTERN int ZEXPORT inflateInit_(z_streamp strm, const char *version, int stream_size); ZEXTERN int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size); ZEXTERN int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, const char *version, int stream_size); ZEXTERN int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size); #ifdef Z_PREFIX_SET # define z_deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) # define z_inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ (int)sizeof(z_stream)) # define z_inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ ZLIB_VERSION, (int)sizeof(z_stream)) #else # define deflateInit(strm, level) \ deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) # define inflateInit(strm) \ inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) # define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) # define inflateInit2(strm, windowBits) \ inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ (int)sizeof(z_stream)) # define inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ ZLIB_VERSION, (int)sizeof(z_stream)) #endif #ifndef Z_SOLO /* gzgetc() macro and its supporting function and exposed data structure. Note * that the real internal state is much larger than the exposed structure. * This abbreviated structure exposes just enough for the gzgetc() macro. The * user should not mess with these exposed elements, since their names or * behavior could change in the future, perhaps even capriciously. They can * only be used by the gzgetc() macro. You have been warned. */ struct gzFile_s { unsigned have; unsigned char *next; z_off64_t pos; }; ZEXTERN int ZEXPORT gzgetc_(gzFile file); /* backward compatibility */ #ifdef Z_PREFIX_SET # undef z_gzgetc # define z_gzgetc(g) \ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #else # define gzgetc(g) \ ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) #endif /* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if * both are true, the application gets the *64 functions, and the regular * functions are changed to 64 bits) -- in case these are set on systems * without large file support, _LFS64_LARGEFILE must also be true */ #ifdef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int); ZEXTERN z_off64_t ZEXPORT gztell64(gzFile); ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile); ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); #endif #if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) # ifdef Z_PREFIX_SET # define z_gzopen z_gzopen64 # define z_gzseek z_gzseek64 # define z_gztell z_gztell64 # define z_gzoffset z_gzoffset64 # define z_adler32_combine z_adler32_combine64 # define z_crc32_combine z_crc32_combine64 # define z_crc32_combine_gen z_crc32_combine_gen64 # else # define gzopen gzopen64 # define gzseek gzseek64 # define gztell gztell64 # define gzoffset gzoffset64 # define adler32_combine adler32_combine64 # define crc32_combine crc32_combine64 # define crc32_combine_gen crc32_combine_gen64 # endif # ifndef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *); ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int); ZEXTERN z_off_t ZEXPORT gztell64(gzFile); ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile); ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); # endif #else ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *); ZEXTERN z_off_t ZEXPORT gzseek(gzFile, z_off_t, int); ZEXTERN z_off_t ZEXPORT gztell(gzFile); ZEXTERN z_off_t ZEXPORT gzoffset(gzFile); ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); #endif #else /* Z_SOLO */ ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t); ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t); ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t); #endif /* !Z_SOLO */ /* undocumented functions */ ZEXTERN const char * ZEXPORT zError(int); ZEXTERN int ZEXPORT inflateSyncPoint(z_streamp); ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table(void); ZEXTERN int ZEXPORT inflateUndermine(z_streamp, int); ZEXTERN int ZEXPORT inflateValidate(z_streamp, int); ZEXTERN unsigned long ZEXPORT inflateCodesUsed(z_streamp); ZEXTERN int ZEXPORT inflateResetKeep(z_streamp); ZEXTERN int ZEXPORT deflateResetKeep(z_streamp); #if defined(_WIN32) && !defined(Z_SOLO) ZEXTERN gzFile ZEXPORT gzopen_w(const wchar_t *path, const char *mode); #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO ZEXTERN int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va); # endif #endif #ifdef __cplusplus } #endif #endif /* ZLIB_H */ |
Added compat/zlib/zlib.map.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ZLIB_1.2.0 { global: compressBound; deflateBound; inflateBack; inflateBackEnd; inflateBackInit_; inflateCopy; local: deflate_copyright; inflate_copyright; inflate_fast; inflate_table; zcalloc; zcfree; z_errmsg; gz_error; gz_intmax; _*; }; ZLIB_1.2.0.2 { gzclearerr; gzungetc; zlibCompileFlags; } ZLIB_1.2.0; ZLIB_1.2.0.8 { deflatePrime; } ZLIB_1.2.0.2; ZLIB_1.2.2 { adler32_combine; crc32_combine; deflateSetHeader; inflateGetHeader; } ZLIB_1.2.0.8; ZLIB_1.2.2.3 { deflateTune; gzdirect; } ZLIB_1.2.2; ZLIB_1.2.2.4 { inflatePrime; } ZLIB_1.2.2.3; ZLIB_1.2.3.3 { adler32_combine64; crc32_combine64; gzopen64; gzseek64; gztell64; inflateUndermine; } ZLIB_1.2.2.4; ZLIB_1.2.3.4 { inflateReset2; inflateMark; } ZLIB_1.2.3.3; ZLIB_1.2.3.5 { gzbuffer; gzoffset; gzoffset64; gzclose_r; gzclose_w; } ZLIB_1.2.3.4; ZLIB_1.2.5.1 { deflatePending; } ZLIB_1.2.3.5; ZLIB_1.2.5.2 { deflateResetKeep; gzgetc_; inflateResetKeep; } ZLIB_1.2.5.1; ZLIB_1.2.7.1 { inflateGetDictionary; gzvprintf; } ZLIB_1.2.5.2; ZLIB_1.2.9 { inflateCodesUsed; inflateValidate; uncompress2; gzfread; gzfwrite; deflateGetDictionary; adler32_z; crc32_z; } ZLIB_1.2.7.1; ZLIB_1.2.12 { crc32_combine_gen; crc32_combine_gen64; crc32_combine_op; } ZLIB_1.2.9; |
Added compat/zlib/zlib.pc.cmakein.
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=@INSTALL_LIB_DIR@ sharedlibdir=@INSTALL_LIB_DIR@ includedir=@INSTALL_INC_DIR@ Name: zlib Description: zlib compression library Version: @VERSION@ Requires: Libs: -L${libdir} -L${sharedlibdir} -lz Cflags: -I${includedir} |
Added compat/zlib/zlib.pc.in.
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ sharedlibdir=@sharedlibdir@ includedir=@includedir@ Name: zlib Description: zlib compression library Version: @VERSION@ Requires: Libs: -L${libdir} -L${sharedlibdir} -lz Cflags: -I${includedir} |
Added compat/zlib/zutil.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 | /* zutil.c -- target dependent utility functions for the compression library * Copyright (C) 1995-2017 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #include "zutil.h" #ifndef Z_SOLO # include "gzguts.h" #endif z_const char * const z_errmsg[10] = { (z_const char *)"need dictionary", /* Z_NEED_DICT 2 */ (z_const char *)"stream end", /* Z_STREAM_END 1 */ (z_const char *)"", /* Z_OK 0 */ (z_const char *)"file error", /* Z_ERRNO (-1) */ (z_const char *)"stream error", /* Z_STREAM_ERROR (-2) */ (z_const char *)"data error", /* Z_DATA_ERROR (-3) */ (z_const char *)"insufficient memory", /* Z_MEM_ERROR (-4) */ (z_const char *)"buffer error", /* Z_BUF_ERROR (-5) */ (z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */ (z_const char *)"" }; const char * ZEXPORT zlibVersion(void) { return ZLIB_VERSION; } uLong ZEXPORT zlibCompileFlags(void) { uLong flags; flags = 0; switch ((int)(sizeof(uInt))) { case 2: break; case 4: flags += 1; break; case 8: flags += 2; break; default: flags += 3; } switch ((int)(sizeof(uLong))) { case 2: break; case 4: flags += 1 << 2; break; case 8: flags += 2 << 2; break; default: flags += 3 << 2; } switch ((int)(sizeof(voidpf))) { case 2: break; case 4: flags += 1 << 4; break; case 8: flags += 2 << 4; break; default: flags += 3 << 4; } switch ((int)(sizeof(z_off_t))) { case 2: break; case 4: flags += 1 << 6; break; case 8: flags += 2 << 6; break; default: flags += 3 << 6; } #ifdef ZLIB_DEBUG flags += 1 << 8; #endif /* #if defined(ASMV) || defined(ASMINF) flags += 1 << 9; #endif */ #ifdef ZLIB_WINAPI flags += 1 << 10; #endif #ifdef BUILDFIXED flags += 1 << 12; #endif #ifdef DYNAMIC_CRC_TABLE flags += 1 << 13; #endif #ifdef NO_GZCOMPRESS flags += 1L << 16; #endif #ifdef NO_GZIP flags += 1L << 17; #endif #ifdef PKZIP_BUG_WORKAROUND flags += 1L << 20; #endif #ifdef FASTEST flags += 1L << 21; #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifdef NO_vsnprintf flags += 1L << 25; # ifdef HAS_vsprintf_void flags += 1L << 26; # endif # else # ifdef HAS_vsnprintf_void flags += 1L << 26; # endif # endif #else flags += 1L << 24; # ifdef NO_snprintf flags += 1L << 25; # ifdef HAS_sprintf_void flags += 1L << 26; # endif # else # ifdef HAS_snprintf_void flags += 1L << 26; # endif # endif #endif return flags; } #ifdef ZLIB_DEBUG #include <stdlib.h> # ifndef verbose # define verbose 0 # endif int ZLIB_INTERNAL z_verbose = verbose; void ZLIB_INTERNAL z_error(char *m) { fprintf(stderr, "%s\n", m); exit(1); } #endif /* exported to allow conversion of error code to string for compress() and * uncompress() */ const char * ZEXPORT zError(int err) { return ERR_MSG(err); } #if defined(_WIN32_WCE) && _WIN32_WCE < 0x800 /* The older Microsoft C Run-Time Library for Windows CE doesn't have * errno. We define it as a global variable to simplify porting. * Its value is always 0 and should not be used. */ int errno = 0; #endif #ifndef HAVE_MEMCPY void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len) { if (len == 0) return; do { *dest++ = *source++; /* ??? to be unrolled */ } while (--len != 0); } int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len) { uInt j; for (j = 0; j < len; j++) { if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; } return 0; } void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len) { if (len == 0) return; do { *dest++ = 0; /* ??? to be unrolled */ } while (--len != 0); } #endif #ifndef Z_SOLO #ifdef SYS16BIT #ifdef __TURBOC__ /* Turbo C in 16-bit mode */ # define MY_ZCALLOC /* Turbo C malloc() does not allow dynamic allocation of 64K bytes * and farmalloc(64K) returns a pointer with an offset of 8, so we * must fix the pointer. Warning: the pointer must be put back to its * original form in order to free it, use zcfree(). */ #define MAX_PTR 10 /* 10*64K = 640K */ local int next_ptr = 0; typedef struct ptr_table_s { voidpf org_ptr; voidpf new_ptr; } ptr_table; local ptr_table table[MAX_PTR]; /* This table is used to remember the original form of pointers * to large buffers (64K). Such pointers are normalized with a zero offset. * Since MSDOS is not a preemptive multitasking OS, this table is not * protected from concurrent access. This hack doesn't work anyway on * a protected system like OS/2. Use Microsoft C instead. */ voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { voidpf buf; ulg bsize = (ulg)items*size; (void)opaque; /* If we allocate less than 65520 bytes, we assume that farmalloc * will return a usable pointer which doesn't have to be normalized. */ if (bsize < 65520L) { buf = farmalloc(bsize); if (*(ush*)&buf != 0) return buf; } else { buf = farmalloc(bsize + 16L); } if (buf == NULL || next_ptr >= MAX_PTR) return NULL; table[next_ptr].org_ptr = buf; /* Normalize the pointer to seg:0 */ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; *(ush*)&buf = 0; table[next_ptr++].new_ptr = buf; return buf; } void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { int n; (void)opaque; if (*(ush*)&ptr != 0) { /* object < 64K */ farfree(ptr); return; } /* Find the original pointer */ for (n = 0; n < next_ptr; n++) { if (ptr != table[n].new_ptr) continue; farfree(table[n].org_ptr); while (++n < next_ptr) { table[n-1] = table[n]; } next_ptr--; return; } Assert(0, "zcfree: ptr not found"); } #endif /* __TURBOC__ */ #ifdef M_I86 /* Microsoft C in 16-bit mode */ # define MY_ZCALLOC #if (!defined(_MSC_VER) || (_MSC_VER <= 600)) # define _halloc halloc # define _hfree hfree #endif voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, uInt items, uInt size) { (void)opaque; return _halloc((long)items, size); } void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { (void)opaque; _hfree(ptr); } #endif /* M_I86 */ #endif /* SYS16BIT */ #ifndef MY_ZCALLOC /* Any system without a special alloc function */ #ifndef STDC extern voidp malloc(uInt size); extern voidp calloc(uInt items, uInt size); extern void free(voidpf ptr); #endif voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) { (void)opaque; return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : (voidpf)calloc(items, size); } void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { (void)opaque; free(ptr); } #endif /* MY_ZCALLOC */ #endif /* !Z_SOLO */ |
Added compat/zlib/zutil.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* zutil.h -- internal interface and configuration of the compression library * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id$ */ #ifndef ZUTIL_H #define ZUTIL_H #ifdef HAVE_HIDDEN # define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) #else # define ZLIB_INTERNAL #endif #include "zlib.h" #if defined(STDC) && !defined(Z_SOLO) # if !(defined(_WIN32_WCE) && defined(_MSC_VER)) # include <stddef.h> # endif # include <string.h> # include <stdlib.h> #endif #ifndef local # define local static #endif /* since "static" is used to mean two completely different things in C, we define "local" for the non-static meaning of "static", for readability (compile with -Dlocal if your debugger can't find static symbols) */ typedef unsigned char uch; typedef uch FAR uchf; typedef unsigned short ush; typedef ush FAR ushf; typedef unsigned long ulg; #if !defined(Z_U8) && !defined(Z_SOLO) && defined(STDC) # include <limits.h> # if (ULONG_MAX == 0xffffffffffffffff) # define Z_U8 unsigned long # elif (ULLONG_MAX == 0xffffffffffffffff) # define Z_U8 unsigned long long # elif (UINT_MAX == 0xffffffffffffffff) # define Z_U8 unsigned # endif #endif extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ #define ERR_MSG(err) z_errmsg[(err) < -6 || (err) > 2 ? 9 : 2 - (err)] #define ERR_RETURN(strm,err) \ return (strm->msg = ERR_MSG(err), (err)) /* To be used only when the state is known to be valid */ /* common constants */ #ifndef DEF_WBITS # define DEF_WBITS MAX_WBITS #endif /* default windowBits for decompression. MAX_WBITS is for compression only */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* default memLevel */ #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 /* The three kinds of block type */ #define MIN_MATCH 3 #define MAX_MATCH 258 /* The minimum and maximum match lengths */ #define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ /* target dependencies */ #if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) # define OS_CODE 0x00 # ifndef Z_SOLO # if defined(__TURBOC__) || defined(__BORLANDC__) # if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) /* Allow compilation with ANSI keywords only enabled */ void _Cdecl farfree( void *block ); void *_Cdecl farmalloc( unsigned long nbytes ); # else # include <alloc.h> # endif # else /* MSC or DJGPP */ # include <malloc.h> # endif # endif #endif #ifdef AMIGA # define OS_CODE 1 #endif #if defined(VAXC) || defined(VMS) # define OS_CODE 2 # define F_OPEN(name, mode) \ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") #endif #ifdef __370__ # if __TARGET_LIB__ < 0x20000000 # define OS_CODE 4 # elif __TARGET_LIB__ < 0x40000000 # define OS_CODE 11 # else # define OS_CODE 8 # endif #endif #if defined(ATARI) || defined(atarist) # define OS_CODE 5 #endif #ifdef OS2 # define OS_CODE 6 # if defined(M_I86) && !defined(Z_SOLO) # include <malloc.h> # endif #endif #if defined(MACOS) # define OS_CODE 7 #endif #ifdef __acorn # define OS_CODE 13 #endif #if defined(WIN32) && !defined(__CYGWIN__) # define OS_CODE 10 #endif #ifdef _BEOS_ # define OS_CODE 16 #endif #ifdef __TOS_OS400__ # define OS_CODE 18 #endif #ifdef __APPLE__ # define OS_CODE 19 #endif #if defined(__BORLANDC__) && !defined(MSDOS) #pragma warn -8004 #pragma warn -8008 #pragma warn -8066 #endif /* provide prototypes for these when building zlib without LFS */ #if !defined(_WIN32) && \ (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); #endif /* common defaults */ #ifndef OS_CODE # define OS_CODE 3 /* assume Unix */ #endif #ifndef F_OPEN # define F_OPEN(name, mode) fopen((name), (mode)) #endif /* functions */ #if defined(pyr) || defined(Z_SOLO) # define NO_MEMCPY #endif #if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) /* Use our own functions for small and medium model with MSC <= 5.0. * You may have to use the same strategy for Borland C (untested). * The __SC__ check is for Symantec. */ # define NO_MEMCPY #endif #if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) # define HAVE_MEMCPY #endif #ifdef HAVE_MEMCPY # ifdef SMALL_MEDIUM /* MSDOS small or medium model */ # define zmemcpy _fmemcpy # define zmemcmp _fmemcmp # define zmemzero(dest, len) _fmemset(dest, 0, len) # else # define zmemcpy memcpy # define zmemcmp memcmp # define zmemzero(dest, len) memset(dest, 0, len) # endif #else void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len); int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len); void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len); #endif /* Diagnostic functions */ #ifdef ZLIB_DEBUG # include <stdio.h> extern int ZLIB_INTERNAL z_verbose; extern void ZLIB_INTERNAL z_error(char *m); # define Assert(cond,msg) {if(!(cond)) z_error(msg);} # define Trace(x) {if (z_verbose>=0) fprintf x ;} # define Tracev(x) {if (z_verbose>0) fprintf x ;} # define Tracevv(x) {if (z_verbose>1) fprintf x ;} # define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} # define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} #else # define Assert(cond,msg) # define Trace(x) # define Tracev(x) # define Tracevv(x) # define Tracec(c,x) # define Tracecv(c,x) #endif #ifndef Z_SOLO voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size); void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr); #endif #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) #define TRY_FREE(s, p) {if (p) ZFREE(s, p);} /* Reverse the bytes in a 32-bit value */ #define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) #endif /* ZUTIL_H */ |
Added configure.
> > > | 1 2 3 | #!/bin/sh dir="`dirname "$0"`/autosetup" WRAPPER="$0"; export WRAPPER; exec "`"$dir/autosetup-find-tclsh"`" "$dir/autosetup" "$@" |
Deleted cvs2fossil.txt.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to debian/makedeb.sh.
1 2 3 4 5 | #!/bin/bash # A quick hack to generate a Debian package of fossil. i took most of this # from Martin Krafft's "The Debian System" book. DEB_REV=${1-1} # .deb package build/revision number. | | < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #!/bin/bash # A quick hack to generate a Debian package of fossil. i took most of this # from Martin Krafft's "The Debian System" book. DEB_REV=${1-1} # .deb package build/revision number. PACKAGE_DEBNAME=fossil THISDIR=${PWD} if uname -a | grep -i nexenta &>/dev/null; then # Assume NexentaOS/GnuSolaris: DEB_ARCH_NAME=solaris-i386 DEB_ARCH_PKGDEPENDS="sunwcsl" # for -lsocket else DEB_ARCH_NAME=$(dpkg --print-architecture) fi SRCDIR=$(cd ..; pwd) test -e ${SRCDIR}/fossil || { echo "This script must be run from a BUILT copy of the source tree." exit 1 } |
︙ | ︙ | |||
39 40 41 42 43 44 45 | rm -fr DEBIAN mkdir DEBIAN PACKAGE_VERSION=$(date +%Y.%m.%d) PACKAGE_DEB_VERSION=${PACKAGE_VERSION}-${DEB_REV} | | | | | | | | | | | 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 | rm -fr DEBIAN mkdir DEBIAN PACKAGE_VERSION=$(date +%Y.%m.%d) PACKAGE_DEB_VERSION=${PACKAGE_VERSION}-${DEB_REV} DEBFILE=${THISDIR}/${PACKAGE_DEBNAME}-${PACKAGE_DEB_VERSION}-dev-${DEB_ARCH_NAME}.deb PACKAGE_TIME=$(/bin/date) rm -f ${DEBFILE} echo "Creating .deb package [${DEBFILE}]..." echo "Generating md5 sums..." find ${DEBLOCALPREFIX} -type f -exec md5sum {} \; > DEBIAN/md5sums true && { echo "Generating Debian-specific files..." COPYRIGHT=${DEBLOCALPREFIX}/share/doc/${PACKAGE_DEBNAME}/copyright cat <<EOF > ${COPYRIGHT} This package was created by fossil-scm <fossil-dev@lists.fossil-scm.org> on ${PACKAGE_TIME}. The original sources for fossil can be downloaded for free from: http://fossil-scm.org/ fossil is released under the terms of the 2-clause BSD License. EOF } true && { CHANGELOG=${DEBLOCALPREFIX}/share/doc/${PACKAGE_DEBNAME}/changelog.gz cat <<EOF | gzip -c > ${CHANGELOG} ${PACKAGE_DEBNAME} ${PACKAGE_DEB_VERSION}; urgency=low This release has no changes over the core source distribution. It has simply been Debianized. Packaged by fossil-dev <fossil-dev@lists.fossil-scm.org> on ${PACKAGE_TIME}. EOF } true && { CONTROL=DEBIAN/control echo "Generating ${CONTROL}..." cat <<EOF > ${CONTROL} Package: ${PACKAGE_DEBNAME} Section: vcs Priority: optional Maintainer: fossil-dev <fossil-dev@lists.fossil-scm.org> Architecture: ${DEB_ARCH_NAME} Depends: libc6 ${DEB_ARCH_PKGDEPENDS+, }${DEB_ARCH_PKGDEPENDS} Version: ${PACKAGE_DEB_VERSION} Description: Fossil is a unique SCM (Software Configuration Management) system. This package contains the Fossil binary for *buntu/Debian systems. Fossil is a unique SCM program which supports distributed source control management using local repositories, access over HTTP CGI, or using the built-in HTTP server. It has a built-in wiki, file browsing, etc. Fossil home page: http://fossil-scm.org Fossil author: D. Richard Hipp License: 2-clause BSD EOF } true && { # GZ_CONTROL=control.tar.gz |
︙ | ︙ |
Added extsrc/cson_amalgamation.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 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 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 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 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 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 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 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 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 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 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 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 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 5433 5434 5435 5436 5437 5438 5439 5440 5441 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 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 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 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 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 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 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 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 | #ifdef FOSSIL_ENABLE_JSON /* auto-generated! Do not edit! */ #include "cson_amalgamation.h" /* begin file parser/JSON_parser.h */ /* See JSON_parser.c for copyright information and licensing. */ #ifndef JSON_PARSER_H #define JSON_PARSER_H /* JSON_parser.h */ #include <stddef.h> /* Windows DLL stuff */ #ifdef JSON_PARSER_DLL # ifdef _MSC_VER # ifdef JSON_PARSER_DLL_EXPORTS # define JSON_PARSER_DLL_API __declspec(dllexport) # else # define JSON_PARSER_DLL_API __declspec(dllimport) # endif # else # define JSON_PARSER_DLL_API # endif #else # define JSON_PARSER_DLL_API #endif /* Determine the integer type use to parse non-floating point numbers */ #ifdef _WIN32 typedef __int64 JSON_int_t; #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%I64d" #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%I64d" #elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1) typedef long long JSON_int_t; #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld" #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld" #else typedef long JSON_int_t; #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld" #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld" #endif #ifdef __cplusplus extern "C" { #endif typedef enum { JSON_E_NONE = 0, JSON_E_INVALID_CHAR, JSON_E_INVALID_KEYWORD, JSON_E_INVALID_ESCAPE_SEQUENCE, JSON_E_INVALID_UNICODE_SEQUENCE, JSON_E_INVALID_NUMBER, JSON_E_NESTING_DEPTH_REACHED, JSON_E_UNBALANCED_COLLECTION, JSON_E_EXPECTED_KEY, JSON_E_EXPECTED_COLON, JSON_E_OUT_OF_MEMORY } JSON_error; typedef enum { JSON_T_NONE = 0, JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END, JSON_T_INTEGER, JSON_T_FLOAT, JSON_T_NULL, JSON_T_TRUE, JSON_T_FALSE, JSON_T_STRING, JSON_T_KEY, JSON_T_MAX } JSON_type; typedef struct JSON_value_struct { union { JSON_int_t integer_value; double float_value; struct { const char* value; size_t length; } str; } vu; } JSON_value; typedef struct JSON_parser_struct* JSON_parser; /*! \brief JSON parser callback \param ctx The pointer passed to new_JSON_parser. \param type An element of JSON_type but not JSON_T_NONE. \param value A representation of the parsed value. This parameter is NULL for JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END, JSON_T_NULL, JSON_T_TRUE, and JSON_T_FALSE. String values are always returned as zero-terminated C strings. \return Non-zero if parsing should continue, else zero. */ typedef int (*JSON_parser_callback)(void* ctx, int type, const JSON_value* value); /** A typedef for allocator functions semantically compatible with malloc(). */ typedef void* (*JSON_malloc_t)(size_t n); /** A typedef for deallocator functions semantically compatible with free(). */ typedef void (*JSON_free_t)(void* mem); /*! \brief The structure used to configure a JSON parser object */ typedef struct { /** Pointer to a callback, called when the parser has something to tell the user. This parameter may be NULL. In this case the input is merely checked for validity. */ JSON_parser_callback callback; /** Callback context - client-specified data to pass to the callback function. This parameter may be NULL. */ void* callback_ctx; /** Specifies the levels of nested JSON to allow. Negative numbers yield unlimited nesting. If negative, the parser can parse arbitrary levels of JSON, otherwise the depth is the limit. */ int depth; /** To allow C style comments in JSON, set to non-zero. */ int allow_comments; /** To decode floating point numbers manually set this parameter to non-zero. */ int handle_floats_manually; /** The memory allocation routine, which must be semantically compatible with malloc(3). If set to NULL, malloc(3) is used. If this is set to a non-NULL value then the 'free' member MUST be set to the proper deallocation counterpart for this function. Failure to do so results in undefined behaviour at deallocation time. */ JSON_malloc_t malloc; /** The memory deallocation routine, which must be semantically compatible with free(3). If set to NULL, free(3) is used. If this is set to a non-NULL value then the 'alloc' member MUST be set to the proper allocation counterpart for this function. Failure to do so results in undefined behaviour at deallocation time. */ JSON_free_t free; } JSON_config; /*! \brief Initializes the JSON parser configuration structure to default values. The default configuration is - 127 levels of nested JSON (depends on JSON_PARSER_STACK_SIZE, see json_parser.c) - no parsing, just checking for JSON syntax - no comments - Uses realloc() for memory de/allocation. \param config. Used to configure the parser. */ JSON_PARSER_DLL_API void init_JSON_config(JSON_config * config); /*! \brief Create a JSON parser object \param config. Used to configure the parser. Set to NULL to use the default configuration. See init_JSON_config. Its contents are copied by this function, so it need not outlive the returned object. \return The parser object, which is owned by the caller and must eventually be freed by calling delete_JSON_parser(). */ JSON_PARSER_DLL_API JSON_parser new_JSON_parser(JSON_config const* config); /*! \brief Destroy a previously created JSON parser object. */ JSON_PARSER_DLL_API void delete_JSON_parser(JSON_parser jc); /*! \brief Parse a character. \return Non-zero, if all characters passed to this function are part of are valid JSON. */ JSON_PARSER_DLL_API int JSON_parser_char(JSON_parser jc, int next_char); /*! \brief Finalize parsing. Call this method once after all input characters have been consumed. \return Non-zero, if all parsed characters are valid JSON, zero otherwise. */ JSON_PARSER_DLL_API int JSON_parser_done(JSON_parser jc); /*! \brief Determine if a given string is valid JSON white space \return Non-zero if the string is valid, zero otherwise. */ JSON_PARSER_DLL_API int JSON_parser_is_legal_white_space_string(const char* s); /*! \brief Gets the last error that occurred during the use of JSON_parser. \return A value from the JSON_error enum. */ JSON_PARSER_DLL_API int JSON_parser_get_last_error(JSON_parser jc); /*! \brief Re-sets the parser to prepare it for another parse run. \return True (non-zero) on success, 0 on error (e.g. !jc). */ JSON_PARSER_DLL_API int JSON_parser_reset(JSON_parser jc); #ifdef __cplusplus } #endif #endif /* JSON_PARSER_H */ /* end file parser/JSON_parser.h */ /* begin file parser/JSON_parser.c */ /* Copyright (c) 2007-2013 Jean Gressmann (jean@0x42.de) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 OR COPYRIGHT HOLDERS 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. */ /* Changelog: 2013-09-08 Updated license to to be compatible with Debian license requirements. 2012-06-06 Fix for invalid UTF16 characters and some comment fixex (thomas.h.moog@intel.com). 2010-11-25 Support for custom memory allocation (sgbeal@googlemail.com). 2010-05-07 Added error handling for memory allocation failure (sgbeal@googlemail.com). Added diagnosis errors for invalid JSON. 2010-03-25 Fixed buffer overrun in grow_parse_buffer & cleaned up code. 2009-10-19 Replaced long double in JSON_value_struct with double after reports of strtold being broken on some platforms (charles@transmissionbt.com). 2009-05-17 Incorporated benrudiak@googlemail.com fix for UTF16 decoding. 2009-05-14 Fixed float parsing bug related to a locale being set that didn't use '.' as decimal point character (charles@transmissionbt.com). 2008-10-14 Renamed states.IN to states.IT to avoid name clash which IN macro defined in windef.h (alexey.pelykh@gmail.com) 2008-07-19 Removed some duplicate code & debugging variable (charles@transmissionbt.com) 2008-05-28 Made JSON_value structure ansi C compliant. This bug was report by trisk@acm.jhu.edu 2008-05-20 Fixed bug reported by charles@transmissionbt.com where the switching from static to dynamic parse buffer did not copy the static parse buffer's content. */ #include <assert.h> #include <ctype.h> #include <float.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <locale.h> #ifdef _MSC_VER # if _MSC_VER >= 1400 /* Visual Studio 2005 and up */ # pragma warning(disable:4996) /* unsecure sscanf */ # pragma warning(disable:4127) /* conditional expression is constant */ # endif #endif #define true 1 #define false 0 #define XX -1 /* the universal error code */ /* values chosen so that the object size is approx equal to one page (4K) */ #ifndef JSON_PARSER_STACK_SIZE # define JSON_PARSER_STACK_SIZE 128 #endif #ifndef JSON_PARSER_PARSE_BUFFER_SIZE # define JSON_PARSER_PARSE_BUFFER_SIZE 3500 #endif typedef void* (*JSON_debug_malloc_t)(size_t bytes, const char* reason); #ifdef JSON_PARSER_DEBUG_MALLOC # define JSON_parser_malloc(func, bytes, reason) ((JSON_debug_malloc_t)func)(bytes, reason) #else # define JSON_parser_malloc(func, bytes, reason) func(bytes) #endif typedef unsigned short UTF16; struct JSON_parser_struct { JSON_parser_callback callback; void* ctx; signed char state, before_comment_state, type, escaped, comment, allow_comments, handle_floats_manually, error; char decimal_point; UTF16 utf16_high_surrogate; int current_char; int depth; int top; int stack_capacity; signed char* stack; char* parse_buffer; size_t parse_buffer_capacity; size_t parse_buffer_count; signed char static_stack[JSON_PARSER_STACK_SIZE]; char static_parse_buffer[JSON_PARSER_PARSE_BUFFER_SIZE]; JSON_malloc_t malloc; JSON_free_t free; }; #define COUNTOF(x) (sizeof(x)/sizeof(x[0])) /* Characters are mapped into these character classes. This allows for a significant reduction in the size of the state transition table. */ enum classes { C_SPACE, /* space */ C_WHITE, /* other whitespace */ C_LCURB, /* { */ C_RCURB, /* } */ C_LSQRB, /* [ */ C_RSQRB, /* ] */ C_COLON, /* : */ C_COMMA, /* , */ C_QUOTE, /* " */ C_BACKS, /* \ */ C_SLASH, /* / */ C_PLUS, /* + */ C_MINUS, /* - */ C_POINT, /* . */ C_ZERO , /* 0 */ C_DIGIT, /* 123456789 */ C_LOW_A, /* a */ C_LOW_B, /* b */ C_LOW_C, /* c */ C_LOW_D, /* d */ C_LOW_E, /* e */ C_LOW_F, /* f */ C_LOW_L, /* l */ C_LOW_N, /* n */ C_LOW_R, /* r */ C_LOW_S, /* s */ C_LOW_T, /* t */ C_LOW_U, /* u */ C_ABCDF, /* ABCDF */ C_E, /* E */ C_ETC, /* everything else */ C_STAR, /* * */ NR_CLASSES }; static const signed char ascii_class[128] = { /* This array maps the 128 ASCII characters into character classes. The remaining Unicode characters should be mapped to C_ETC. Non-whitespace control characters are errors. */ XX, XX, XX, XX, XX, XX, XX, XX, XX, C_WHITE, C_WHITE, XX, XX, C_WHITE, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH, C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC, C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC, C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC }; /* The state codes. */ enum states { GO, /* start */ OK, /* ok */ OB, /* object */ KE, /* key */ CO, /* colon */ VA, /* value */ AR, /* array */ ST, /* string */ ESC, /* escape */ U1, /* u1 */ U2, /* u2 */ U3, /* u3 */ U4, /* u4 */ MI, /* minus */ ZE, /* zero */ IT, /* integer */ FR, /* fraction */ E1, /* e */ E2, /* ex */ E3, /* exp */ T1, /* tr */ T2, /* tru */ T3, /* true */ F1, /* fa */ F2, /* fal */ F3, /* fals */ F4, /* false */ N1, /* nu */ N2, /* nul */ N3, /* null */ C1, /* / */ C2, /* / * */ C3, /* * */ FX, /* *.* *eE* */ D1, /* second UTF-16 character decoding started by \ */ D2, /* second UTF-16 character proceeded by u */ NR_STATES }; enum actions { CB = -10, /* comment begin */ CE = -11, /* comment end */ FA = -12, /* false */ TR = -13, /* false */ NU = -14, /* null */ DE = -15, /* double detected by exponent e E */ DF = -16, /* double detected by fraction . */ SB = -17, /* string begin */ MX = -18, /* integer detected by minus */ ZX = -19, /* integer detected by zero */ IX = -20, /* integer detected by 1-9 */ EX = -21, /* next char is escaped */ UC = -22 /* Unicode character read */ }; static const signed char state_transition_table[NR_STATES][NR_CLASSES] = { /* The state transition table takes the current state and the current symbol, and returns either a new state or an action. An action is represented as a negative number. A JSON text is accepted if at the end of the text the state is OK and if the mode is MODE_DONE. white 1-9 ABCDF etc space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E | * */ /*start GO*/ {GO,GO,-6,XX,-5,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*ok OK*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*object OB*/ {OB,OB,XX,-9,XX,XX,XX,XX,SB,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*key KE*/ {KE,KE,XX,XX,XX,XX,XX,XX,SB,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*colon CO*/ {CO,CO,XX,XX,XX,XX,-2,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*value VA*/ {VA,VA,-6,XX,-5,XX,XX,XX,SB,XX,CB,XX,MX,XX,ZX,IX,XX,XX,XX,XX,XX,FA,XX,NU,XX,XX,TR,XX,XX,XX,XX,XX}, /*array AR*/ {AR,AR,-6,XX,-5,-7,XX,XX,SB,XX,CB,XX,MX,XX,ZX,IX,XX,XX,XX,XX,XX,FA,XX,NU,XX,XX,TR,XX,XX,XX,XX,XX}, /*string ST*/ {ST,XX,ST,ST,ST,ST,ST,ST,-4,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST}, /*escape ES*/ {XX,XX,XX,XX,XX,XX,XX,XX,ST,ST,ST,XX,XX,XX,XX,XX,XX,ST,XX,XX,XX,ST,XX,ST,ST,XX,ST,U1,XX,XX,XX,XX}, /*u1 U1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U2,U2,U2,U2,U2,U2,U2,U2,XX,XX,XX,XX,XX,XX,U2,U2,XX,XX}, /*u2 U2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U3,U3,U3,U3,U3,U3,U3,U3,XX,XX,XX,XX,XX,XX,U3,U3,XX,XX}, /*u3 U3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U4,U4,U4,U4,U4,U4,U4,U4,XX,XX,XX,XX,XX,XX,U4,U4,XX,XX}, /*u4 U4*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,UC,UC,UC,UC,UC,UC,UC,UC,XX,XX,XX,XX,XX,XX,UC,UC,XX,XX}, /*minus MI*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,ZE,IT,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*zero ZE*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,DF,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*int IT*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,DF,IT,IT,XX,XX,XX,XX,DE,XX,XX,XX,XX,XX,XX,XX,XX,DE,XX,XX}, /*frac FR*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,XX,FR,FR,XX,XX,XX,XX,E1,XX,XX,XX,XX,XX,XX,XX,XX,E1,XX,XX}, /*e E1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,E2,E2,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*ex E2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*exp E3*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,XX,XX,XX,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*tr T1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,T2,XX,XX,XX,XX,XX,XX,XX}, /*tru T2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,T3,XX,XX,XX,XX}, /*true T3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*fa F1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F2,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*fal F2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F3,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*fals F3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F4,XX,XX,XX,XX,XX,XX}, /*false F4*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*nu N1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,N2,XX,XX,XX,XX}, /*nul N2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,N3,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*null N3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*/ C1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,C2}, /*/star C2*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3}, /** C3*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,CE,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3}, /*_. FX*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,XX,XX,XX,XX,FR,FR,XX,XX,XX,XX,E1,XX,XX,XX,XX,XX,XX,XX,XX,E1,XX,XX}, /*\ D1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,D2,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX}, /*\ D2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U1,XX,XX,XX,XX}, }; /* These modes can be pushed on the stack. */ enum modes { MODE_ARRAY = 1, MODE_DONE = 2, MODE_KEY = 3, MODE_OBJECT = 4 }; static void set_error(JSON_parser jc) { switch (jc->state) { case GO: switch (jc->current_char) { case '{': case '}': case '[': case ']': jc->error = JSON_E_UNBALANCED_COLLECTION; break; default: jc->error = JSON_E_INVALID_CHAR; break; } break; case OB: jc->error = JSON_E_EXPECTED_KEY; break; case AR: jc->error = JSON_E_UNBALANCED_COLLECTION; break; case CO: jc->error = JSON_E_EXPECTED_COLON; break; case KE: jc->error = JSON_E_EXPECTED_KEY; break; /* \uXXXX\uYYYY */ case U1: case U2: case U3: case U4: case D1: case D2: jc->error = JSON_E_INVALID_UNICODE_SEQUENCE; break; /* true, false, null */ case T1: case T2: case T3: case F1: case F2: case F3: case F4: case N1: case N2: case N3: jc->error = JSON_E_INVALID_KEYWORD; break; /* minus, integer, fraction, exponent */ case MI: case ZE: case IT: case FR: case E1: case E2: case E3: jc->error = JSON_E_INVALID_NUMBER; break; default: jc->error = JSON_E_INVALID_CHAR; break; } } static int push(JSON_parser jc, int mode) { /* Push a mode onto the stack. Return false if there is overflow. */ assert(jc->top <= jc->stack_capacity); if (jc->depth < 0) { if (jc->top == jc->stack_capacity) { const size_t bytes_to_copy = jc->stack_capacity * sizeof(jc->stack[0]); const size_t new_capacity = jc->stack_capacity * 2; const size_t bytes_to_allocate = new_capacity * sizeof(jc->stack[0]); void* mem = JSON_parser_malloc(jc->malloc, bytes_to_allocate, "stack"); if (!mem) { jc->error = JSON_E_OUT_OF_MEMORY; return false; } jc->stack_capacity = (int)new_capacity; memcpy(mem, jc->stack, bytes_to_copy); if (jc->stack != &jc->static_stack[0]) { jc->free(jc->stack); } jc->stack = (signed char*)mem; } } else { if (jc->top == jc->depth) { jc->error = JSON_E_NESTING_DEPTH_REACHED; return false; } } jc->stack[++jc->top] = (signed char)mode; return true; } static int pop(JSON_parser jc, int mode) { /* Pop the stack, assuring that the current mode matches the expectation. Return false if there is underflow or if the modes mismatch. */ if (jc->top < 0 || jc->stack[jc->top] != mode) { return false; } jc->top -= 1; return true; } #define parse_buffer_clear(jc) \ do {\ jc->parse_buffer_count = 0;\ jc->parse_buffer[0] = 0;\ } while (0) #define parse_buffer_pop_back_char(jc)\ do {\ assert(jc->parse_buffer_count >= 1);\ --jc->parse_buffer_count;\ jc->parse_buffer[jc->parse_buffer_count] = 0;\ } while (0) void delete_JSON_parser(JSON_parser jc) { if (jc) { if (jc->stack != &jc->static_stack[0]) { jc->free((void*)jc->stack); } if (jc->parse_buffer != &jc->static_parse_buffer[0]) { jc->free((void*)jc->parse_buffer); } jc->free((void*)jc); } } int JSON_parser_reset(JSON_parser jc) { if (NULL == jc) { return false; } jc->state = GO; jc->top = -1; /* parser has been used previously? */ if (NULL == jc->parse_buffer) { /* Do we want non-bound stack? */ if (jc->depth > 0) { jc->stack_capacity = jc->depth; if (jc->depth <= (int)COUNTOF(jc->static_stack)) { jc->stack = &jc->static_stack[0]; } else { const size_t bytes_to_alloc = jc->stack_capacity * sizeof(jc->stack[0]); jc->stack = (signed char*)JSON_parser_malloc(jc->malloc, bytes_to_alloc, "stack"); if (jc->stack == NULL) { return false; } } } else { jc->stack_capacity = (int)COUNTOF(jc->static_stack); jc->depth = -1; jc->stack = &jc->static_stack[0]; } /* set up the parse buffer */ jc->parse_buffer = &jc->static_parse_buffer[0]; jc->parse_buffer_capacity = COUNTOF(jc->static_parse_buffer); } /* set parser to start */ push(jc, MODE_DONE); parse_buffer_clear(jc); return true; } JSON_parser new_JSON_parser(JSON_config const * config) { /* new_JSON_parser starts the checking process by constructing a JSON_parser object. It takes a depth parameter that restricts the level of maximum nesting. To continue the process, call JSON_parser_char for each character in the JSON text, and then call JSON_parser_done to obtain the final result. These functions are fully reentrant. */ int use_std_malloc = false; JSON_config default_config; JSON_parser jc; JSON_malloc_t alloc; /* set to default configuration if none was provided */ if (NULL == config) { /* initialize configuration */ init_JSON_config(&default_config); config = &default_config; } /* use std malloc if either the allocator or deallocator function isn't set */ use_std_malloc = NULL == config->malloc || NULL == config->free; alloc = use_std_malloc ? malloc : config->malloc; jc = (JSON_parser)JSON_parser_malloc(alloc, sizeof(*jc), "parser"); if (NULL == jc) { return NULL; } /* configure the parser */ memset(jc, 0, sizeof(*jc)); jc->malloc = alloc; jc->free = use_std_malloc ? free : config->free; jc->callback = config->callback; jc->ctx = config->callback_ctx; jc->allow_comments = (signed char)(config->allow_comments != 0); jc->handle_floats_manually = (signed char)(config->handle_floats_manually != 0); jc->decimal_point = *localeconv()->decimal_point; /* We need to be able to push at least one object */ jc->depth = config->depth == 0 ? 1 : config->depth; /* reset the parser */ if (!JSON_parser_reset(jc)) { jc->free(jc); return NULL; } return jc; } static int parse_buffer_grow(JSON_parser jc) { const size_t bytes_to_copy = jc->parse_buffer_count * sizeof(jc->parse_buffer[0]); const size_t new_capacity = jc->parse_buffer_capacity * 2; const size_t bytes_to_allocate = new_capacity * sizeof(jc->parse_buffer[0]); void* mem = JSON_parser_malloc(jc->malloc, bytes_to_allocate, "parse buffer"); if (mem == NULL) { jc->error = JSON_E_OUT_OF_MEMORY; return false; } assert(new_capacity > 0); memcpy(mem, jc->parse_buffer, bytes_to_copy); if (jc->parse_buffer != &jc->static_parse_buffer[0]) { jc->free(jc->parse_buffer); } jc->parse_buffer = (char*)mem; jc->parse_buffer_capacity = new_capacity; return true; } static int parse_buffer_reserve_for(JSON_parser jc, unsigned chars) { while (jc->parse_buffer_count + chars + 1 > jc->parse_buffer_capacity) { if (!parse_buffer_grow(jc)) { assert(jc->error == JSON_E_OUT_OF_MEMORY); return false; } } return true; } #define parse_buffer_has_space_for(jc, count) \ (jc->parse_buffer_count + (count) + 1 <= jc->parse_buffer_capacity) #define parse_buffer_push_back_char(jc, c)\ do {\ assert(parse_buffer_has_space_for(jc, 1)); \ jc->parse_buffer[jc->parse_buffer_count++] = c;\ jc->parse_buffer[jc->parse_buffer_count] = 0;\ } while (0) #define assert_is_non_container_type(jc) \ assert( \ jc->type == JSON_T_NULL || \ jc->type == JSON_T_FALSE || \ jc->type == JSON_T_TRUE || \ jc->type == JSON_T_FLOAT || \ jc->type == JSON_T_INTEGER || \ jc->type == JSON_T_STRING) static int parse_parse_buffer(JSON_parser jc) { if (jc->callback) { JSON_value value, *arg = NULL; if (jc->type != JSON_T_NONE) { assert_is_non_container_type(jc); switch(jc->type) { case JSON_T_FLOAT: arg = &value; if (jc->handle_floats_manually) { value.vu.str.value = jc->parse_buffer; value.vu.str.length = jc->parse_buffer_count; } else { /* not checking with end pointer b/c there may be trailing ws */ value.vu.float_value = strtod(jc->parse_buffer, NULL); } break; case JSON_T_INTEGER: arg = &value; sscanf(jc->parse_buffer, JSON_PARSER_INTEGER_SSCANF_TOKEN, &value.vu.integer_value); break; case JSON_T_STRING: arg = &value; value.vu.str.value = jc->parse_buffer; value.vu.str.length = jc->parse_buffer_count; break; } if (!(*jc->callback)(jc->ctx, jc->type, arg)) { return false; } } } parse_buffer_clear(jc); return true; } #define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800) #define IS_LOW_SURROGATE(uc) (((uc) & 0xFC00) == 0xDC00) #define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000) static const unsigned char utf8_lead_bits[4] = { 0x00, 0xC0, 0xE0, 0xF0 }; static int decode_unicode_char(JSON_parser jc) { int i; unsigned uc = 0; char* p; int trail_bytes; assert(jc->parse_buffer_count >= 6); p = &jc->parse_buffer[jc->parse_buffer_count - 4]; for (i = 12; i >= 0; i -= 4, ++p) { unsigned x = *p; if (x >= 'a') { x -= ('a' - 10); } else if (x >= 'A') { x -= ('A' - 10); } else { x &= ~0x30u; } assert(x < 16); uc |= x << i; } /* clear UTF-16 char from buffer */ jc->parse_buffer_count -= 6; jc->parse_buffer[jc->parse_buffer_count] = 0; if (uc == 0xffff || uc == 0xfffe) { return false; } /* attempt decoding ... */ if (jc->utf16_high_surrogate) { if (IS_LOW_SURROGATE(uc)) { uc = DECODE_SURROGATE_PAIR(jc->utf16_high_surrogate, uc); trail_bytes = 3; jc->utf16_high_surrogate = 0; } else { /* high surrogate without a following low surrogate */ return false; } } else { if (uc < 0x80) { trail_bytes = 0; } else if (uc < 0x800) { trail_bytes = 1; } else if (IS_HIGH_SURROGATE(uc)) { /* save the high surrogate and wait for the low surrogate */ jc->utf16_high_surrogate = (UTF16)uc; return true; } else if (IS_LOW_SURROGATE(uc)) { /* low surrogate without a preceding high surrogate */ return false; } else { trail_bytes = 2; } } jc->parse_buffer[jc->parse_buffer_count++] = (char) ((uc >> (trail_bytes * 6)) | utf8_lead_bits[trail_bytes]); for (i = trail_bytes * 6 - 6; i >= 0; i -= 6) { jc->parse_buffer[jc->parse_buffer_count++] = (char) (((uc >> i) & 0x3F) | 0x80); } jc->parse_buffer[jc->parse_buffer_count] = 0; return true; } static int add_escaped_char_to_parse_buffer(JSON_parser jc, int next_char) { assert(parse_buffer_has_space_for(jc, 1)); jc->escaped = 0; /* remove the backslash */ parse_buffer_pop_back_char(jc); switch(next_char) { case 'b': parse_buffer_push_back_char(jc, '\b'); break; case 'f': parse_buffer_push_back_char(jc, '\f'); break; case 'n': parse_buffer_push_back_char(jc, '\n'); break; case 'r': parse_buffer_push_back_char(jc, '\r'); break; case 't': parse_buffer_push_back_char(jc, '\t'); break; case '"': parse_buffer_push_back_char(jc, '"'); break; case '\\': parse_buffer_push_back_char(jc, '\\'); break; case '/': parse_buffer_push_back_char(jc, '/'); break; case 'u': parse_buffer_push_back_char(jc, '\\'); parse_buffer_push_back_char(jc, 'u'); break; default: return false; } return true; } static int add_char_to_parse_buffer(JSON_parser jc, int next_char, int next_class) { if (!parse_buffer_reserve_for(jc, 1)) { assert(JSON_E_OUT_OF_MEMORY == jc->error); return false; } if (jc->escaped) { if (!add_escaped_char_to_parse_buffer(jc, next_char)) { jc->error = JSON_E_INVALID_ESCAPE_SEQUENCE; return false; } } else if (!jc->comment) { if ((jc->type != JSON_T_NONE) | !((next_class == C_SPACE) | (next_class == C_WHITE)) /* non-white-space */) { parse_buffer_push_back_char(jc, (char)next_char); } } return true; } #define assert_type_isnt_string_null_or_bool(jc) \ assert(jc->type != JSON_T_FALSE); \ assert(jc->type != JSON_T_TRUE); \ assert(jc->type != JSON_T_NULL); \ assert(jc->type != JSON_T_STRING) int JSON_parser_char(JSON_parser jc, int next_char) { /* After calling new_JSON_parser, call this function for each character (or partial character) in your JSON text. It can accept UTF-8, UTF-16, or UTF-32. It returns true if things are looking ok so far. If it rejects the text, it returns false. */ int next_class, next_state; /* Store the current char for error handling */ jc->current_char = next_char; /* Determine the character's class. */ if (next_char < 0) { jc->error = JSON_E_INVALID_CHAR; return false; } if (next_char >= 128) { next_class = C_ETC; } else { next_class = ascii_class[next_char]; if (next_class <= XX) { set_error(jc); return false; } } if (!add_char_to_parse_buffer(jc, next_char, next_class)) { return false; } /* Get the next state from the state transition table. */ next_state = state_transition_table[jc->state][next_class]; if (next_state >= 0) { /* Change the state. */ jc->state = (signed char)next_state; } else { /* Or perform one of the actions. */ switch (next_state) { /* Unicode character */ case UC: if(!decode_unicode_char(jc)) { jc->error = JSON_E_INVALID_UNICODE_SEQUENCE; return false; } /* check if we need to read a second UTF-16 char */ if (jc->utf16_high_surrogate) { jc->state = D1; } else { jc->state = ST; } break; /* escaped char */ case EX: jc->escaped = 1; jc->state = ESC; break; /* integer detected by minus */ case MX: jc->type = JSON_T_INTEGER; jc->state = MI; break; /* integer detected by zero */ case ZX: jc->type = JSON_T_INTEGER; jc->state = ZE; break; /* integer detected by 1-9 */ case IX: jc->type = JSON_T_INTEGER; jc->state = IT; break; /* floating point number detected by exponent*/ case DE: assert_type_isnt_string_null_or_bool(jc); jc->type = JSON_T_FLOAT; jc->state = E1; break; /* floating point number detected by fraction */ case DF: assert_type_isnt_string_null_or_bool(jc); if (!jc->handle_floats_manually) { /* Some versions of strtod (which underlies sscanf) don't support converting C-locale formated floating point values. */ assert(jc->parse_buffer[jc->parse_buffer_count-1] == '.'); jc->parse_buffer[jc->parse_buffer_count-1] = jc->decimal_point; } jc->type = JSON_T_FLOAT; jc->state = FX; break; /* string begin " */ case SB: parse_buffer_clear(jc); assert(jc->type == JSON_T_NONE); jc->type = JSON_T_STRING; jc->state = ST; break; /* n */ case NU: assert(jc->type == JSON_T_NONE); jc->type = JSON_T_NULL; jc->state = N1; break; /* f */ case FA: assert(jc->type == JSON_T_NONE); jc->type = JSON_T_FALSE; jc->state = F1; break; /* t */ case TR: assert(jc->type == JSON_T_NONE); jc->type = JSON_T_TRUE; jc->state = T1; break; /* closing comment */ case CE: jc->comment = 0; assert(jc->parse_buffer_count == 0); assert(jc->type == JSON_T_NONE); jc->state = jc->before_comment_state; break; /* opening comment */ case CB: if (!jc->allow_comments) { return false; } parse_buffer_pop_back_char(jc); if (!parse_parse_buffer(jc)) { return false; } assert(jc->parse_buffer_count == 0); assert(jc->type != JSON_T_STRING); switch (jc->stack[jc->top]) { case MODE_ARRAY: case MODE_OBJECT: switch(jc->state) { case VA: case AR: jc->before_comment_state = jc->state; break; default: jc->before_comment_state = OK; break; } break; default: jc->before_comment_state = jc->state; break; } jc->type = JSON_T_NONE; jc->state = C1; jc->comment = 1; break; /* empty } */ case -9: parse_buffer_clear(jc); if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_END, NULL)) { return false; } if (!pop(jc, MODE_KEY)) { return false; } jc->state = OK; break; /* } */ case -8: parse_buffer_pop_back_char(jc); if (!parse_parse_buffer(jc)) { return false; } if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_END, NULL)) { return false; } if (!pop(jc, MODE_OBJECT)) { jc->error = JSON_E_UNBALANCED_COLLECTION; return false; } jc->type = JSON_T_NONE; jc->state = OK; break; /* ] */ case -7: parse_buffer_pop_back_char(jc); if (!parse_parse_buffer(jc)) { return false; } if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_ARRAY_END, NULL)) { return false; } if (!pop(jc, MODE_ARRAY)) { jc->error = JSON_E_UNBALANCED_COLLECTION; return false; } jc->type = JSON_T_NONE; jc->state = OK; break; /* { */ case -6: parse_buffer_pop_back_char(jc); if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_OBJECT_BEGIN, NULL)) { return false; } if (!push(jc, MODE_KEY)) { return false; } assert(jc->type == JSON_T_NONE); jc->state = OB; break; /* [ */ case -5: parse_buffer_pop_back_char(jc); if (jc->callback && !(*jc->callback)(jc->ctx, JSON_T_ARRAY_BEGIN, NULL)) { return false; } if (!push(jc, MODE_ARRAY)) { return false; } assert(jc->type == JSON_T_NONE); jc->state = AR; break; /* string end " */ case -4: parse_buffer_pop_back_char(jc); switch (jc->stack[jc->top]) { case MODE_KEY: assert(jc->type == JSON_T_STRING); jc->type = JSON_T_NONE; jc->state = CO; if (jc->callback) { JSON_value value; value.vu.str.value = jc->parse_buffer; value.vu.str.length = jc->parse_buffer_count; if (!(*jc->callback)(jc->ctx, JSON_T_KEY, &value)) { return false; } } parse_buffer_clear(jc); break; case MODE_ARRAY: case MODE_OBJECT: assert(jc->type == JSON_T_STRING); if (!parse_parse_buffer(jc)) { return false; } jc->type = JSON_T_NONE; jc->state = OK; break; default: return false; } break; /* , */ case -3: parse_buffer_pop_back_char(jc); if (!parse_parse_buffer(jc)) { return false; } switch (jc->stack[jc->top]) { case MODE_OBJECT: /* A comma causes a flip from object mode to key mode. */ if (!pop(jc, MODE_OBJECT) || !push(jc, MODE_KEY)) { return false; } assert(jc->type != JSON_T_STRING); jc->type = JSON_T_NONE; jc->state = KE; break; case MODE_ARRAY: assert(jc->type != JSON_T_STRING); jc->type = JSON_T_NONE; jc->state = VA; break; default: return false; } break; /* : */ case -2: /* A colon causes a flip from key mode to object mode. */ parse_buffer_pop_back_char(jc); if (!pop(jc, MODE_KEY) || !push(jc, MODE_OBJECT)) { return false; } assert(jc->type == JSON_T_NONE); jc->state = VA; break; /* Bad action. */ default: set_error(jc); return false; } } return true; } int JSON_parser_done(JSON_parser jc) { if ((jc->state == OK || jc->state == GO) && pop(jc, MODE_DONE)) { return true; } jc->error = JSON_E_UNBALANCED_COLLECTION; return false; } int JSON_parser_is_legal_white_space_string(const char* s) { int c, char_class; if (s == NULL) { return false; } for (; *s; ++s) { c = *s; if (c < 0 || c >= 128) { return false; } char_class = ascii_class[c]; if (char_class != C_SPACE && char_class != C_WHITE) { return false; } } return true; } int JSON_parser_get_last_error(JSON_parser jc) { return jc->error; } void init_JSON_config(JSON_config* config) { if (config) { memset(config, 0, sizeof(*config)); config->depth = JSON_PARSER_STACK_SIZE - 1; config->malloc = malloc; config->free = free; } } #undef XX #undef COUNTOF #undef parse_buffer_clear #undef parse_buffer_pop_back_char /* end file parser/JSON_parser.c */ /* begin file ./cson.c */ #include <assert.h> #include <stdlib.h> /* malloc()/free() */ #include <string.h> #include <errno.h> #ifdef _MSC_VER # if _MSC_VER >= 1400 /* Visual Studio 2005 and up */ # pragma warning( push ) # pragma warning(disable:4996) /* unsecure sscanf (but snscanf() isn't in c89) */ # pragma warning(disable:4244) /* complaining about data loss due to integer precision in the sqlite3 utf decoding routines */ # endif #endif #if 1 #include <stdio.h> #define MARKER if(1) printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); if(1) printf #else static void noop_printf(char const * fmt, ...) {} #define MARKER if(0) printf #endif #if defined(__cplusplus) extern "C" { #endif /** This type holds the "vtbl" for type-specific operations when working with cson_value objects. All cson_values of a given logical type share a pointer to a single library-internal instance of this class. */ struct cson_value_api { /** The logical JavaScript/JSON type associated with this object. */ const cson_type_id typeID; /** Must free any memory associated with self, but not free self. If self is NULL then this function must do nothing. */ void (*cleanup)( cson_value * self ); /** POSSIBLE TODOs: // Deep copy. int (*clone)( cson_value const * self, cson_value ** tgt ); // Using JS semantics for true/value char (*bool_value)( cson_value const * self ); // memcmp() return value semantics int (*compare)( cson_value const * self, cson_value const * other ); */ }; typedef struct cson_value_api cson_value_api; /** Empty-initialized cson_value_api object. */ #define cson_value_api_empty_m { \ CSON_TYPE_UNDEF/*typeID*/, \ NULL/*cleanup*/\ } /** Empty-initialized cson_value_api object. */ /*static const cson_value_api cson_value_api_empty = cson_value_api_empty_m;*/ typedef unsigned int cson_counter_t; struct cson_value { /** The "vtbl" of type-specific operations. All instances of a given logical value type share a single api instance. Results are undefined if this value is NULL. */ cson_value_api const * api; /** The raw value. Its interpretation depends on the value of the api member. Some value types require dynamically-allocated memory, so one must always call cson_value_free() to destroy a value when it is no longer needed. For stack-allocated values (which client could SHOULD NOT USE unless they are intimately familiar with the memory management rules and don't mind an occasional leak or crash), use cson_value_clean() instead of cson_value_free(). */ void * value; /** We use this to allow us to store cson_value instances in multiple containers or multiple times within a single container (provided no cycles are introduced). Notes about the rc implementation: - The refcount is for the cson_value instance itself, not its value pointer. - Instances start out with a refcount of 0 (not 1). Adding them to a container will increase the refcount. Cleaning up the container will decrement the count. - cson_value_free() decrements the refcount (if it is not already 0) and cleans/frees the value only when the refcount is 0. - Some places in the internals add an "extra" reference to objects to avoid a premature deletion. Don't try this at home. */ cson_counter_t refcount; }; /** Empty-initialized cson_value object. */ const cson_parse_opt cson_parse_opt_empty = cson_parse_opt_empty_m; const cson_output_opt cson_output_opt_empty = cson_output_opt_empty_m; const cson_object_iterator cson_object_iterator_empty = cson_object_iterator_empty_m; const cson_buffer cson_buffer_empty = cson_buffer_empty_m; const cson_parse_info cson_parse_info_empty = cson_parse_info_empty_m; static void cson_value_destroy_zero_it( cson_value * self ); static void cson_value_destroy_object( cson_value * self ); /** If self is-a array then this function destroys its contents, else this function does nothing. */ static void cson_value_destroy_array( cson_value * self ); static const cson_value_api cson_value_api_null = { CSON_TYPE_NULL, cson_value_destroy_zero_it }; static const cson_value_api cson_value_api_undef = { CSON_TYPE_UNDEF, cson_value_destroy_zero_it }; static const cson_value_api cson_value_api_bool = { CSON_TYPE_BOOL, cson_value_destroy_zero_it }; static const cson_value_api cson_value_api_integer = { CSON_TYPE_INTEGER, cson_value_destroy_zero_it }; static const cson_value_api cson_value_api_double = { CSON_TYPE_DOUBLE, cson_value_destroy_zero_it }; static const cson_value_api cson_value_api_string = { CSON_TYPE_STRING, cson_value_destroy_zero_it }; static const cson_value_api cson_value_api_array = { CSON_TYPE_ARRAY, cson_value_destroy_array }; static const cson_value_api cson_value_api_object = { CSON_TYPE_OBJECT, cson_value_destroy_object }; static const cson_value cson_value_undef = { &cson_value_api_undef, NULL, 0 }; static const cson_value cson_value_integer_empty = { &cson_value_api_integer, NULL, 0 }; static const cson_value cson_value_double_empty = { &cson_value_api_double, NULL, 0 }; static const cson_value cson_value_string_empty = { &cson_value_api_string, NULL, 0 }; static const cson_value cson_value_array_empty = { &cson_value_api_array, NULL, 0 }; static const cson_value cson_value_object_empty = { &cson_value_api_object, NULL, 0 }; /** Strings are allocated as an instances of this class with N+1 trailing bytes, where N is the length of the string being allocated. To convert a cson_string to c-string we simply increment the cson_string pointer. To do the opposite we use (cstr - sizeof(cson_string)). Zero-length strings are a special case handled by a couple of the cson_string functions. */ struct cson_string { unsigned int length; }; #define cson_string_empty_m {0/*length*/} static const cson_string cson_string_empty = cson_string_empty_m; /** Assumes V is a (cson_value*) ans V->value is a (T*). Returns V->value cast to a (T*). */ #define CSON_CAST(T,V) ((T*)((V)->value)) /** Assumes V is a pointer to memory which is allocated as part of a cson_value instance (the bytes immediately after that part). Returns a pointer a a cson_value by subtracting sizeof(cson_value) from that address and casting it to a (cson_value*) */ #define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value))) /** CSON_INT(V) assumes that V is a (cson_value*) of type CSON_TYPE_INTEGER. This macro returns a (cson_int_t*) representing its value (how that is stored depends on whether we are running in 32- or 64-bit mode). */ #if CSON_VOID_PTR_IS_BIG # define CSON_INT(V) ((cson_int_t*)(&((V)->value))) #else # define CSON_INT(V) ((cson_int_t*)(V)->value) #endif #define CSON_DBL(V) CSON_CAST(cson_double_t,(V)) #define CSON_STR(V) CSON_CAST(cson_string,(V)) #define CSON_OBJ(V) CSON_CAST(cson_object,(V)) #define CSON_ARRAY(V) CSON_CAST(cson_array,(V)) /** Holds special shared "constant" (though they are non-const) values. */ static struct CSON_EMPTY_HOLDER_ { char trueValue; cson_string stringValue; } CSON_EMPTY_HOLDER = { 1/*trueValue*/, cson_string_empty_m }; /** Indexes into the CSON_SPECIAL_VALUES array. If this enum changes in any way, makes damned sure that CSON_SPECIAL_VALUES is updated to match!!! */ enum CSON_INTERNAL_VALUES { CSON_VAL_UNDEF = 0, CSON_VAL_NULL = 1, CSON_VAL_TRUE = 2, CSON_VAL_FALSE = 3, CSON_VAL_INT_0 = 4, CSON_VAL_DBL_0 = 5, CSON_VAL_STR_EMPTY = 6, CSON_INTERNAL_VALUES_LENGTH }; /** Some "special" shared cson_value instances. These values MUST be initialized in the order specified by the CSON_INTERNAL_VALUES enum. Note that they are not const because they are used as shared-allocation objects in non-const contexts. However, the public API provides no way to modifying them, and clients who modify values directly are subject to The Wrath of Undefined Behaviour. */ static cson_value CSON_SPECIAL_VALUES[] = { { &cson_value_api_undef, NULL, 0 }, /* UNDEF */ { &cson_value_api_null, NULL, 0 }, /* NULL */ { &cson_value_api_bool, &CSON_EMPTY_HOLDER.trueValue, 0 }, /* TRUE */ { &cson_value_api_bool, NULL, 0 }, /* FALSE */ { &cson_value_api_integer, NULL, 0 }, /* INT_0 */ { &cson_value_api_double, NULL, 0 }, /* DBL_0 */ { &cson_value_api_string, &CSON_EMPTY_HOLDER.stringValue, 0 }, /* STR_EMPTY */ { NULL, NULL, 0 } }; /** Returns non-0 (true) if m is one of our special "built-in" values, e.g. from CSON_SPECIAL_VALUES and some "empty" values. If this returns true, m MUST NOT be free()d! */ static char cson_value_is_builtin( void const * m ) { if((m >= (void const *)&CSON_EMPTY_HOLDER) && ( m < (void const *)(&CSON_EMPTY_HOLDER+1))) return 1; else return ((m >= (void const *)&CSON_SPECIAL_VALUES[0]) && ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) ) ? 1 : 0; } char const * cson_rc_string(int rc) { if(0 == rc) return "OK"; #define CHECK(N) else if(cson_rc.N == rc ) return #N CHECK(OK); CHECK(ArgError); CHECK(RangeError); CHECK(TypeError); CHECK(IOError); CHECK(AllocError); CHECK(NYIError); CHECK(InternalError); CHECK(UnsupportedError); CHECK(NotFoundError); CHECK(UnknownError); CHECK(Parse_INVALID_CHAR); CHECK(Parse_INVALID_KEYWORD); CHECK(Parse_INVALID_ESCAPE_SEQUENCE); CHECK(Parse_INVALID_UNICODE_SEQUENCE); CHECK(Parse_INVALID_NUMBER); CHECK(Parse_NESTING_DEPTH_REACHED); CHECK(Parse_UNBALANCED_COLLECTION); CHECK(Parse_EXPECTED_KEY); CHECK(Parse_EXPECTED_COLON); else return "UnknownError"; #undef CHECK } /** If CSON_LOG_ALLOC is true then the cson_malloc/realloc/free() routines will log a message to stderr. */ #define CSON_LOG_ALLOC 0 /** CSON_FOSSIL_MODE is only for use in the Fossil source tree, so that we can plug in to its allocators. We can't do this by, e.g., defining macros for the malloc/free funcs because fossil's lack of header files means we would have to #include "main.c" here to get the declarations. */ #if defined(CSON_FOSSIL_MODE) extern void *fossil_malloc(size_t n); extern void fossil_free(void *p); extern void *fossil_realloc(void *p, size_t n); # define CSON_MALLOC_IMPL fossil_malloc # define CSON_FREE_IMPL fossil_free # define CSON_REALLOC_IMPL fossil_realloc #endif #if !defined CSON_MALLOC_IMPL # define CSON_MALLOC_IMPL malloc #endif #if !defined CSON_FREE_IMPL # define CSON_FREE_IMPL free #endif #if !defined CSON_REALLOC_IMPL # define CSON_REALLOC_IMPL realloc #endif /** A test/debug macro for simulating an OOM after the given number of bytes have been allocated. */ #define CSON_SIMULATE_OOM 0 #if CSON_SIMULATE_OOM static unsigned int cson_totalAlloced = 0; #endif /** Simple proxy for malloc(). descr is a description of the allocation. */ static void * cson_malloc( size_t n, char const * descr ) { #if CSON_LOG_ALLOC fprintf(stderr, "Allocating %u bytes [%s].\n", (unsigned int)n, descr); #endif #if CSON_SIMULATE_OOM cson_totalAlloced += n; if( cson_totalAlloced > CSON_SIMULATE_OOM ) { return NULL; } #endif return CSON_MALLOC_IMPL(n); } /** Simple proxy for free(). descr is a description of the memory being freed. */ static void cson_free( void * p, char const * descr ) { #if CSON_LOG_ALLOC fprintf(stderr, "Freeing @%p [%s].\n", p, descr); #endif if( !cson_value_is_builtin(p) ) { CSON_FREE_IMPL( p ); } } /** Simple proxy for realloc(). descr is a description of the (re)allocation. */ static void * cson_realloc( void * hint, size_t n, char const * descr ) { #if CSON_LOG_ALLOC fprintf(stderr, "%sllocating %u bytes [%s].\n", hint ? "Rea" : "A", (unsigned int)n, descr); #endif #if CSON_SIMULATE_OOM cson_totalAlloced += n; if( cson_totalAlloced > CSON_SIMULATE_OOM ) { return NULL; } #endif if( 0==n ) { cson_free(hint, descr); return NULL; } else { return CSON_REALLOC_IMPL( hint, n ); } } #undef CSON_LOG_ALLOC #undef CSON_SIMULATE_OOM /** CLIENTS CODE SHOULD NEVER USE THIS because it opens up doors to memory leaks if it is not used in very controlled circumstances. Users must be very aware of how the underlying memory management works. Frees any resources owned by val, but does not free val itself (which may be stack-allocated). If !val or val->api or val->api->cleanup are NULL then this is a no-op. If v is a container type (object or array) its children are also cleaned up, recursively. After calling this, val will have the special "undefined" type. */ static void cson_value_clean( cson_value * val ); /** Increments cv's reference count by 1. As a special case, values for which cson_value_is_builtin() returns true are not modified. assert()s if (NULL==cv). */ static void cson_refcount_incr( cson_value * cv ) { assert( NULL != cv ); if( cson_value_is_builtin( cv ) ) { /* do nothing: we do not want to modify the shared instances. */ return; } else { ++cv->refcount; } } #if 0 int cson_value_refcount_set( cson_value * cv, unsigned short rc ) { if( NULL == cv ) return cson_rc.ArgError; else { cv->refcount = rc; return 0; } } #endif int cson_value_add_reference( cson_value * cv ) { if( NULL == cv ) return cson_rc.ArgError; else if( (cv->refcount+1) < cv->refcount ) { return cson_rc.RangeError; } else { cson_refcount_incr( cv ); return 0; } } /** If cv is NULL or cson_value_is_builtin(cv) returns true then this function does nothing and returns 0, otherwise... If cv->refcount is 0 or 1 then cson_value_clean(cv) is called, cv is freed, and 0 is returned. If cv->refcount is any other value then it is decremented and the new value is returned. */ static cson_counter_t cson_refcount_decr( cson_value * cv ) { if( (NULL == cv) || cson_value_is_builtin(cv) ) return 0; else if( (0 == cv->refcount) || (0 == --cv->refcount) ) { cson_value_clean(cv); cson_free(cv,"cson_value::refcount=0"); return 0; } else return cv->refcount; } unsigned int cson_string_length_bytes( cson_string const * str ) { return str ? str->length : 0; } /** Fetches v's string value as a non-const string. cson_strings are intended to be immutable, but this form provides access to the immutable bits, which are v->length bytes long. A length-0 string is returned as NULL from here, as opposed to "". (This is a side-effect of the string allocation mechanism.) Returns NULL if !v or if v is the internal empty-string singleton. */ static char * cson_string_str(cson_string *v) { /* See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a */ #if 1 if( !v || (&CSON_EMPTY_HOLDER.stringValue == v) ) return NULL; else return (char *)((unsigned char *)( v+1 )); #else static char empty[2] = {0,0}; return ( NULL == v ) ? NULL : (v->length ? (char *) (((unsigned char *)v) + sizeof(cson_string)) : empty) ; #endif } /** Fetches v's string value as a const string. */ char const * cson_string_cstr(cson_string const *v) { /* See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2e0c0df5e8a0cd6a */ #if 1 if( ! v ) return NULL; else if( v == &CSON_EMPTY_HOLDER.stringValue ) return ""; else { assert((0 < v->length) && "How do we have a non-singleton empty string?"); return (char const *)((unsigned char const *)(v+1)); } #else return (NULL == v) ? NULL : (v->length ? (char const *) ((unsigned char const *)(v+1)) : ""); #endif } #if 0 /** Just like strndup(3), in that neither are C89/C99-standard and both are documented in detail in strndup(3). */ static char * cson_strdup( char const * src, size_t n ) { char * rc = (char *)cson_malloc(n+1, "cson_strdup"); if( ! rc ) return NULL; memset( rc, 0, n+1 ); rc[n] = 0; return strncpy( rc, src, n ); } #endif int cson_string_cmp_cstr_n( cson_string const * str, char const * other, unsigned int otherLen ) { if( ! other && !str ) return 0; else if( other && !str ) return 1; else if( str && !other ) return -1; else if( !otherLen ) return str->length ? 1 : 0; else if( !str->length ) return otherLen ? -1 : 0; else { unsigned const int max = (otherLen > str->length) ? otherLen : str->length; int const rc = strncmp( cson_string_cstr(str), other, max ); return ( (0 == rc) && (otherLen != str->length) ) ? (str->length < otherLen) ? -1 : 1 : rc; } } int cson_string_cmp_cstr( cson_string const * lhs, char const * rhs ) { return cson_string_cmp_cstr_n( lhs, rhs, (rhs&&*rhs) ? strlen(rhs) : 0 ); } int cson_string_cmp( cson_string const * lhs, cson_string const * rhs ) { return cson_string_cmp_cstr_n( lhs, cson_string_cstr(rhs), rhs ? rhs->length : 0 ); } /** If self is not NULL, *self is overwritten to have the undefined type. self is not cleaned up or freed. */ void cson_value_destroy_zero_it( cson_value * self ) { if( self ) { *self = cson_value_undef; } } /** A key/value pair collection. Each of these objects owns its key/value pointers, and they are cleaned up by cson_kvp_clean(). */ struct cson_kvp { cson_value * key; cson_value * value; }; #define cson_kvp_empty_m {NULL,NULL} static const cson_kvp cson_kvp_empty = cson_kvp_empty_m; /** @def CSON_OBJECT_PROPS_SORT Don't use this - it has not been updated to account for internal changes in cson_object. If CSON_OBJECT_PROPS_SORT is set to a true value then qsort() and bsearch() are used to sort (upon insertion) and search cson_object::kvp property lists. This costs us a re-sort on each insertion but searching is O(log n) average/worst case (and O(1) best-case). i'm not yet convinced that the overhead of the qsort() justifies the potentially decreased search times - it has not been measured. Object property lists tend to be relatively short in JSON, and a linear search which uses the cson_string::length property as a quick check is quite fast when one compares it with the sort overhead required by the bsearch() approach. */ #define CSON_OBJECT_PROPS_SORT 0 /** @def CSON_OBJECT_PROPS_SORT_USE_LENGTH Don't use this - i'm not sure that it works how i'd like. If CSON_OBJECT_PROPS_SORT_USE_LENGTH is true then we use string lengths as quick checks when sorting property keys. This leads to a non-intuitive sorting order but "should" be faster. This is ignored if CSON_OBJECT_PROPS_SORT is false. */ #define CSON_OBJECT_PROPS_SORT_USE_LENGTH 0 #if CSON_OBJECT_PROPS_SORT /** cson_kvp comparator for use with qsort(). ALMOST compares with strcmp() semantics, but it uses the strings' lengths as a quicker approach. This might give non-intuitive results, but it's faster. */ static int cson_kvp_cmp( void const * lhs, void const * rhs ) { cson_kvp const * lk = *((cson_kvp const * const*)lhs); cson_kvp const * rk = *((cson_kvp const * const*)rhs); cson_string const * l = cson_string_value(lk->key); cson_string const * r = cson_string_value(rk->key); #if CSON_OBJECT_PROPS_SORT_USE_LENGTH if( l->length < r->length ) return -1; else if( l->length > r->length ) return 1; else return strcmp( cson_string_cstr( l ), cson_string_cstr( r ) ); #else return strcmp( cson_string_cstr( l ), cson_string_cstr( r ) ); #endif /*CSON_OBJECT_PROPS_SORT_USE_LENGTH*/ } #endif /*CSON_OBJECT_PROPS_SORT*/ #if CSON_OBJECT_PROPS_SORT #error "Need to rework this for cson_string-to-cson_value refactoring" /** A bsearch() comparison function which requires that lhs be a (char const *) and rhs be-a (cson_kvp const * const *). It compares lhs to rhs->key's value, using strcmp() semantics. */ static int cson_kvp_cmp_vs_cstr( void const * lhs, void const * rhs ) { char const * lk = (char const *)lhs; cson_kvp const * rk = *((cson_kvp const * const*)rhs) ; #if CSON_OBJECT_PROPS_SORT_USE_LENGTH unsigned int llen = strlen(lk); if( llen < rk->key->length ) return -1; else if( llen > rk->key->length ) return 1; else return strcmp( lk, cson_string_cstr( rk->key ) ); #else return strcmp( lk, cson_string_cstr( rk->key ) ); #endif /*CSON_OBJECT_PROPS_SORT_USE_LENGTH*/ } #endif /*CSON_OBJECT_PROPS_SORT*/ struct cson_kvp_list { cson_kvp ** list; unsigned int count; unsigned int alloced; }; typedef struct cson_kvp_list cson_kvp_list; #define cson_kvp_list_empty_m {NULL/*list*/,0/*count*/,0/*alloced*/} /*static const cson_kvp_list cson_kvp_list_empty = cson_kvp_list_empty_m;*/ struct cson_object { cson_kvp_list kvp; }; /*typedef struct cson_object cson_object;*/ #define cson_object_empty_m { cson_kvp_list_empty_m/*kvp*/ } static const cson_object cson_object_empty = cson_object_empty_m; struct cson_value_list { cson_value ** list; unsigned int count; unsigned int alloced; }; typedef struct cson_value_list cson_value_list; #define cson_value_list_empty_m {NULL/*list*/,0/*count*/,0/*alloced*/} static const cson_value_list cson_value_list_empty = cson_value_list_empty_m; struct cson_array { cson_value_list list; }; /*typedef struct cson_array cson_array;*/ #define cson_array_empty_m { cson_value_list_empty_m/*list*/ } static const cson_array cson_array_empty = cson_array_empty_m; struct cson_parser { JSON_parser p; cson_value * root; cson_value * node; cson_array stack; cson_string * ckey; int errNo; unsigned int totalKeyCount; unsigned int totalValueCount; }; typedef struct cson_parser cson_parser; static const cson_parser cson_parser_empty = { NULL/*p*/, NULL/*root*/, NULL/*node*/, cson_array_empty_m/*stack*/, NULL/*ckey*/, 0/*errNo*/, 0/*totalKeyCount*/, 0/*totalValueCount*/ }; #if 1 /* The following funcs are declared in generated code (cson_lists.h), but we need early access to their decls for the Amalgamation build. */ static unsigned int cson_value_list_reserve( cson_value_list * self, unsigned int n ); static unsigned int cson_kvp_list_reserve( cson_kvp_list * self, unsigned int n ); static int cson_kvp_list_append( cson_kvp_list * self, cson_kvp * cp ); static void cson_kvp_list_clean( cson_kvp_list * self, void (*cleaner)(cson_kvp * obj) ); #if 0 static int cson_value_list_append( cson_value_list * self, cson_value * cp ); static void cson_value_list_clean( cson_value_list * self, void (*cleaner)(cson_value * obj)); static int cson_kvp_list_visit( cson_kvp_list * self, int (*visitor)(cson_kvp * obj, void * visitorState ), void * visitorState ); static int cson_value_list_visit( cson_value_list * self, int (*visitor)(cson_value * obj, void * visitorState ), void * visitorState ); #endif #endif #if 0 # define LIST_T cson_value_list # define VALUE_T cson_value * # define VALUE_T_IS_PTR 1 # define LIST_T cson_kvp_list # define VALUE_T cson_kvp * # define VALUE_T_IS_PTR 1 #else #endif /** Allocates a new value of the specified type. Ownership is transfered to the caller, who must eventually free it by passing it to cson_value_free() or transfering ownership to a container. extra is only valid for type CSON_TYPE_STRING, and must be the length of the string to allocate + 1 byte (for the NUL). The returned value->api member will be set appropriately and val->value will be set to point to the memory allocated to hold the native value type. Use the internal CSON_CAST() family of macros to convert the cson_values to their corresponding native representation. Returns NULL on allocation error. @see cson_value_new_array() @see cson_value_new_object() @see cson_value_new_string() @see cson_value_new_integer() @see cson_value_new_double() @see cson_value_new_bool() @see cson_value_free() */ static cson_value * cson_value_new(cson_type_id t, size_t extra) { static const size_t vsz = sizeof(cson_value); const size_t sz = vsz + extra; size_t tx = 0; cson_value def = cson_value_undef; cson_value * v = NULL; char const * reason = "cson_value_new"; switch(t) { case CSON_TYPE_ARRAY: assert( 0 == extra ); def = cson_value_array_empty; tx = sizeof(cson_array); reason = "cson_value:array"; break; case CSON_TYPE_DOUBLE: assert( 0 == extra ); def = cson_value_double_empty; tx = sizeof(cson_double_t); reason = "cson_value:double"; break; case CSON_TYPE_INTEGER: assert( 0 == extra ); def = cson_value_integer_empty; #if !CSON_VOID_PTR_IS_BIG tx = sizeof(cson_int_t); #endif reason = "cson_value:int"; break; case CSON_TYPE_STRING: assert( 0 != extra ); def = cson_value_string_empty; tx = sizeof(cson_string); reason = "cson_value:string"; break; case CSON_TYPE_OBJECT: assert( 0 == extra ); def = cson_value_object_empty; tx = sizeof(cson_object); reason = "cson_value:object"; break; default: assert(0 && "Unhandled type in cson_value_new()!"); return NULL; } assert( def.api->typeID != CSON_TYPE_UNDEF ); v = (cson_value *)cson_malloc(sz+tx, reason); if( v ) { *v = def; if(tx || extra){ memset(v+1, 0, tx + extra); v->value = (void *)(v+1); } } return v; } void cson_value_free(cson_value *v) { cson_refcount_decr( v ); } #if 0 /* we might actually want this later on. */ /** Returns true if v is not NULL and has the given type ID. */ static char cson_value_is_a( cson_value const * v, cson_type_id is ) { return (v && v->api && (v->api->typeID == is)) ? 1 : 0; } #endif cson_type_id cson_value_type_id( cson_value const * v ) { return (v && v->api) ? v->api->typeID : CSON_TYPE_UNDEF; } char cson_value_is_undef( cson_value const * v ) { return ( !v || !v->api || (v->api==&cson_value_api_undef)) ? 1 : 0; } #define ISA(T,TID) char cson_value_is_##T( cson_value const * v ) { \ /*return (v && v->api) ? cson_value_is_a(v,CSON_TYPE_##TID) : 0;*/ \ return (v && (v->api == &cson_value_api_##T)) ? 1 : 0; \ } extern char bogusPlaceHolderForEmacsIndention##TID ISA(null,NULL); ISA(bool,BOOL); ISA(integer,INTEGER); ISA(double,DOUBLE); ISA(string,STRING); ISA(array,ARRAY); ISA(object,OBJECT); #undef ISA char cson_value_is_number( cson_value const * v ) { return cson_value_is_integer(v) || cson_value_is_double(v); } void cson_value_clean( cson_value * val ) { if( val && val->api && val->api->cleanup ) { if( ! cson_value_is_builtin( val ) ) { cson_counter_t const rc = val->refcount; val->api->cleanup(val); *val = cson_value_undef; val->refcount = rc; } } } static cson_value * cson_value_array_alloc() { cson_value * v = cson_value_new(CSON_TYPE_ARRAY,0); if( NULL != v ) { cson_array * ar = CSON_ARRAY(v); assert(NULL != ar); *ar = cson_array_empty; } return v; } static cson_value * cson_value_object_alloc() { cson_value * v = cson_value_new(CSON_TYPE_OBJECT,0); if( NULL != v ) { cson_object * obj = CSON_OBJ(v); assert(NULL != obj); *obj = cson_object_empty; } return v; } cson_value * cson_value_new_object() { return cson_value_object_alloc(); } cson_object * cson_new_object() { return cson_value_get_object( cson_value_new_object() ); } cson_value * cson_value_new_array() { return cson_value_array_alloc(); } cson_array * cson_new_array() { return cson_value_get_array( cson_value_new_array() ); } /** Frees kvp->key and kvp->value and sets them to NULL, but does not free kvp. If !kvp then this is a no-op. */ static void cson_kvp_clean( cson_kvp * kvp ) { if( kvp ) { if(kvp->key) { cson_value_free(kvp->key); kvp->key = NULL; } if(kvp->value) { cson_value_free( kvp->value ); kvp->value = NULL; } } } cson_string * cson_kvp_key( cson_kvp const * kvp ) { return kvp ? cson_value_get_string(kvp->key) : NULL; } cson_value * cson_kvp_value( cson_kvp const * kvp ) { return kvp ? kvp->value : NULL; } /** Calls cson_kvp_clean(kvp) and then frees kvp. */ static void cson_kvp_free( cson_kvp * kvp ) { if( kvp ) { cson_kvp_clean(kvp); cson_free(kvp,"cson_kvp"); } } /** cson_value_api::destroy_value() impl for Object values. Cleans up self-owned memory and overwrites self to have the undefined value, but does not free self. */ static void cson_value_destroy_object( cson_value * self ) { if(self && self->value) { cson_object * obj = (cson_object *)self->value; assert( self->value == obj ); cson_kvp_list_clean( &obj->kvp, cson_kvp_free ); *self = cson_value_undef; } } /** Cleans up the contents of ar->list, but does not free ar. After calling this, ar will have a length of 0. If properlyCleanValues is 1 then cson_value_free() is called on each non-NULL item, otherwise the outer list is destroyed but the individual items are assumed to be owned by someone else and are not freed. */ static void cson_array_clean( cson_array * ar, char properlyCleanValues ) { if( ar ) { unsigned int i = 0; cson_value * val = NULL; for( ; i < ar->list.count; ++i ) { val = ar->list.list[i]; if(val) { ar->list.list[i] = NULL; if( properlyCleanValues ) { cson_value_free( val ); } } } cson_value_list_reserve(&ar->list,0); ar->list = cson_value_list_empty /* Pedantic note: reserve(0) already clears the list-specific fields, but we do this just in case we ever add new fields to cson_value_list which are not used in the reserve() impl. */ ; } } /** cson_value_api::destroy_value() impl for Array values. Cleans up self-owned memory and overwrites self to have the undefined value, but does not free self. */ static void cson_value_destroy_array( cson_value * self ) { cson_array * ar = cson_value_get_array(self); if(ar) { assert( self->value == ar ); cson_array_clean( ar, 1 ); *self = cson_value_undef; } } int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state ) { int rc; enum { BufSize = 1024 * 4 }; char rbuf[BufSize]; size_t total = 0; unsigned int rlen = 0; if( ! dest || ! src ) return cson_rc.ArgError; dest->used = 0; while(1) { rlen = BufSize; rc = src( state, rbuf, &rlen ); if( rc ) break; total += rlen; if( dest->capacity < (total+1) ) { rc = cson_buffer_reserve( dest, total + 1); if( 0 != rc ) break; } memcpy( dest->mem + dest->used, rbuf, rlen ); dest->used += rlen; if( rlen < BufSize ) break; } if( !rc && dest->used ) { assert( dest->used < dest->capacity ); dest->mem[dest->used] = 0; } return rc; } int cson_data_source_FILE( void * state, void * dest, unsigned int * n ) { FILE * f = (FILE*) state; if( ! state || ! n || !dest ) return cson_rc.ArgError; else if( !*n ) return cson_rc.RangeError; *n = (unsigned int)fread( dest, 1, *n, f ); if( !*n ) { return feof(f) ? 0 : cson_rc.IOError; } return 0; } int cson_parse_FILE( cson_value ** tgt, FILE * src, cson_parse_opt const * opt, cson_parse_info * err ) { return cson_parse( tgt, cson_data_source_FILE, src, opt, err ); } int cson_value_fetch_bool( cson_value const * val, char * v ) { /** FIXME: move the to-bool operation into cson_value_api, like we do in the C++ API. */ if( ! val || !val->api ) return cson_rc.ArgError; else { int rc = 0; char b = 0; switch( val->api->typeID ) { case CSON_TYPE_ARRAY: case CSON_TYPE_OBJECT: b = 1; break; case CSON_TYPE_STRING: { char const * str = cson_string_cstr(cson_value_get_string(val)); b = (str && *str) ? 1 : 0; break; } case CSON_TYPE_UNDEF: case CSON_TYPE_NULL: break; case CSON_TYPE_BOOL: b = (NULL==val->value) ? 0 : 1; break; case CSON_TYPE_INTEGER: { cson_int_t i = 0; cson_value_fetch_integer( val, &i ); b = i ? 1 : 0; break; } case CSON_TYPE_DOUBLE: { cson_double_t d = 0.0; cson_value_fetch_double( val, &d ); b = (0.0==d) ? 0 : 1; break; } default: rc = cson_rc.TypeError; break; } if( v ) *v = b; return rc; } } char cson_value_get_bool( cson_value const * val ) { char i = 0; cson_value_fetch_bool( val, &i ); return i; } int cson_value_fetch_integer( cson_value const * val, cson_int_t * v ) { if( ! val || !val->api ) return cson_rc.ArgError; else { cson_int_t i = 0; int rc = 0; switch(val->api->typeID) { case CSON_TYPE_UNDEF: case CSON_TYPE_NULL: i = 0; break; case CSON_TYPE_BOOL: { char b = 0; cson_value_fetch_bool( val, &b ); i = b; break; } case CSON_TYPE_INTEGER: { cson_int_t const * x = CSON_INT(val); if(!x) { assert( val == &CSON_SPECIAL_VALUES[CSON_VAL_INT_0] ); } i = x ? *x : 0; break; } case CSON_TYPE_DOUBLE: { cson_double_t d = 0.0; cson_value_fetch_double( val, &d ); i = (cson_int_t)d; break; } case CSON_TYPE_STRING: case CSON_TYPE_ARRAY: case CSON_TYPE_OBJECT: default: rc = cson_rc.TypeError; break; } if(!rc && v) *v = i; return rc; } } cson_int_t cson_value_get_integer( cson_value const * val ) { cson_int_t i = 0; cson_value_fetch_integer( val, &i ); return i; } int cson_value_fetch_double( cson_value const * val, cson_double_t * v ) { if( ! val || !val->api ) return cson_rc.ArgError; else { cson_double_t d = 0.0; int rc = 0; switch(val->api->typeID) { case CSON_TYPE_UNDEF: case CSON_TYPE_NULL: d = 0; break; case CSON_TYPE_BOOL: { char b = 0; cson_value_fetch_bool( val, &b ); d = b ? 1.0 : 0.0; break; } case CSON_TYPE_INTEGER: { cson_int_t i = 0; cson_value_fetch_integer( val, &i ); d = i; break; } case CSON_TYPE_DOUBLE: { cson_double_t const* dv = CSON_DBL(val); d = dv ? *dv : 0.0; break; } default: rc = cson_rc.TypeError; break; } if(v) *v = d; return rc; } } cson_double_t cson_value_get_double( cson_value const * val ) { cson_double_t i = 0.0; cson_value_fetch_double( val, &i ); return i; } int cson_value_fetch_string( cson_value const * val, cson_string ** dest ) { if( ! val || ! dest ) return cson_rc.ArgError; else if( ! cson_value_is_string(val) ) return cson_rc.TypeError; else { if( dest ) *dest = CSON_STR(val); return 0; } } cson_string * cson_value_get_string( cson_value const * val ) { cson_string * rc = NULL; cson_value_fetch_string( val, &rc ); return rc; } char const * cson_value_get_cstr( cson_value const * val ) { return cson_string_cstr( cson_value_get_string(val) ); } int cson_value_fetch_object( cson_value const * val, cson_object ** obj ) { if( ! val ) return cson_rc.ArgError; else if( ! cson_value_is_object(val) ) return cson_rc.TypeError; else { if(obj) *obj = CSON_OBJ(val); return 0; } } cson_object * cson_value_get_object( cson_value const * v ) { cson_object * obj = NULL; cson_value_fetch_object( v, &obj ); return obj; } int cson_value_fetch_array( cson_value const * val, cson_array ** ar) { if( ! val ) return cson_rc.ArgError; else if( !cson_value_is_array(val) ) return cson_rc.TypeError; else { if(ar) *ar = CSON_ARRAY(val); return 0; } } cson_array * cson_value_get_array( cson_value const * v ) { cson_array * ar = NULL; cson_value_fetch_array( v, &ar ); return ar; } cson_kvp * cson_kvp_alloc() { cson_kvp * kvp = (cson_kvp*)cson_malloc(sizeof(cson_kvp),"cson_kvp"); if( kvp ) { *kvp = cson_kvp_empty; } return kvp; } int cson_array_append( cson_array * ar, cson_value * v ) { if( !ar || !v ) return cson_rc.ArgError; else if( (ar->list.count+1) < ar->list.count ) return cson_rc.RangeError; else { if( !ar->list.alloced || (ar->list.count == ar->list.alloced-1)) { unsigned int const n = ar->list.count ? (ar->list.count*2) : 7; if( n > cson_value_list_reserve( &ar->list, n ) ) { return cson_rc.AllocError; } } return cson_array_set( ar, ar->list.count, v ); } } #if 0 /** Removes and returns the last value from the given array, shrinking its size by 1. Returns NULL if ar is NULL, ar->list.count is 0, or the element at that index is NULL. If removeRef is true then cson_value_free() is called to remove ar's reference count for the value. In that case NULL is returned, even if the object still has live references. If removeRef is false then the caller takes over ownership of that reference count point. If removeRef is false then the caller takes over ownership of the return value, otherwise ownership is effectively determined by any remaining references for the returned value. */ static cson_value * cson_array_pop_back( cson_array * ar, char removeRef ) { if( !ar ) return NULL; else if( ! ar->list.count ) return NULL; else { unsigned int const ndx = --ar->list.count; cson_value * v = ar->list.list[ndx]; ar->list.list[ndx] = NULL; if( removeRef ) { cson_value_free( v ); v = NULL; } return v; } } #endif cson_value * cson_value_new_bool( char v ) { return v ? &CSON_SPECIAL_VALUES[CSON_VAL_TRUE] : &CSON_SPECIAL_VALUES[CSON_VAL_FALSE]; } cson_value * cson_value_true() { return &CSON_SPECIAL_VALUES[CSON_VAL_TRUE]; } cson_value * cson_value_false() { return &CSON_SPECIAL_VALUES[CSON_VAL_FALSE]; } cson_value * cson_value_null() { return &CSON_SPECIAL_VALUES[CSON_VAL_NULL]; } cson_value * cson_new_int( cson_int_t v ) { return cson_value_new_integer(v); } cson_value * cson_value_new_integer( cson_int_t v ) { if( 0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_INT_0]; else { cson_value * c = cson_value_new(CSON_TYPE_INTEGER,0); #if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG assert( sizeof(cson_int_t) <= sizeof(void *) ); #endif if( c ) { memcpy( CSON_INT(c), &v, sizeof(v) ); } return c; } } cson_value * cson_new_double( cson_double_t v ) { return cson_value_new_double(v); } cson_value * cson_value_new_double( cson_double_t v ) { if( 0.0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_DBL_0]; else { cson_value * c = cson_value_new(CSON_TYPE_DOUBLE,0); if( c ) { memcpy( CSON_DBL(c), &v, sizeof(v) ); } return c; } } cson_string * cson_new_string(char const * str, unsigned int len) { if( !str || !*str || !len ) return &CSON_EMPTY_HOLDER.stringValue; else { cson_value * c = cson_value_new(CSON_TYPE_STRING, len + 1/*NUL byte*/); cson_string * s = NULL; if( c ) { char * dest = NULL; s = CSON_STR(c); *s = cson_string_empty; assert( NULL != s ); s->length = len; dest = cson_string_str(s); assert( NULL != dest ); memcpy( dest, str, len ); dest[len] = 0; } return s; } } cson_value * cson_value_new_string( char const * str, unsigned int len ) { return cson_string_value( cson_new_string(str, len) ); } int cson_array_value_fetch( cson_array const * ar, unsigned int pos, cson_value ** v ) { if( !ar) return cson_rc.ArgError; if( pos >= ar->list.count ) return cson_rc.RangeError; else { if(v) *v = ar->list.list[pos]; return 0; } } cson_value * cson_array_get( cson_array const * ar, unsigned int pos ) { cson_value *v = NULL; cson_array_value_fetch(ar, pos, &v); return v; } int cson_array_length_fetch( cson_array const * ar, unsigned int * v ) { if( ! ar || !v ) return cson_rc.ArgError; else { if(v) *v = ar->list.count; return 0; } } unsigned int cson_array_length_get( cson_array const * ar ) { unsigned int i = 0; cson_array_length_fetch(ar, &i); return i; } int cson_array_reserve( cson_array * ar, unsigned int size ) { if( ! ar ) return cson_rc.ArgError; else if( size <= ar->list.alloced ) { /* We don't want to introduce a can of worms by trying to handle the cleanup from here. */ return 0; } else { return (ar->list.alloced > cson_value_list_reserve( &ar->list, size )) ? cson_rc.AllocError : 0 ; } } int cson_array_set( cson_array * ar, unsigned int ndx, cson_value * v ) { if( !ar || !v ) return cson_rc.ArgError; else if( (ndx+1) < ndx) /* overflow */return cson_rc.RangeError; else { unsigned const int len = cson_value_list_reserve( &ar->list, ndx+1 ); if( len <= ndx ) return cson_rc.AllocError; else { cson_value * old = ar->list.list[ndx]; if( old ) { if(old == v) return 0; else cson_value_free(old); } cson_refcount_incr( v ); ar->list.list[ndx] = v; if( ndx >= ar->list.count ) { ar->list.count = ndx+1; } return 0; } } } /** @internal Searchs for the given key in the given object. Returns the found item on success, NULL on error. If ndx is not NULL, it is set to the index (in obj->kvp.list) of the found item. *ndx is not modified if no entry is found. */ static cson_kvp * cson_object_search_impl( cson_object const * obj, char const * key, unsigned int * ndx ) { if( obj && key && *key && obj->kvp.count) { #if CSON_OBJECT_PROPS_SORT cson_kvp ** s = (cson_kvp**) bsearch( key, obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*), cson_kvp_cmp_vs_cstr ); if( ndx && s ) { /* index of found record is required by cson_object_unset(). Calculate the offset based on s...*/ #if 0 *ndx = (((unsigned char const *)s - ((unsigned char const *)obj->kvp.list)) / sizeof(cson_kvp*)); #else *ndx = s - obj->kvp.list; #endif } return s ? *s : NULL; #else cson_kvp_list const * li = &obj->kvp; unsigned int i = 0; cson_kvp * kvp; const unsigned int klen = strlen(key); for( ; i < li->count; ++i ) { cson_string const * sKey; kvp = li->list[i]; assert( kvp && kvp->key ); sKey = cson_value_get_string(kvp->key); assert(sKey); if( sKey->length != klen ) continue; else if(0==strcmp(key,cson_string_cstr(sKey))) { if(ndx) *ndx = i; return kvp; } } #endif } return NULL; } cson_value * cson_object_get( cson_object const * obj, char const * key ) { cson_kvp * kvp = cson_object_search_impl( obj, key, NULL ); return kvp ? kvp->value : NULL; } cson_value * cson_object_get_s( cson_object const * obj, cson_string const *key ) { cson_kvp * kvp = cson_object_search_impl( obj, cson_string_cstr(key), NULL ); return kvp ? kvp->value : NULL; } #if CSON_OBJECT_PROPS_SORT static void cson_object_sort_props( cson_object * obj ) { assert( NULL != obj ); if( obj->kvp.count ) { qsort( obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*), cson_kvp_cmp ); } } #endif int cson_object_unset( cson_object * obj, char const * key ) { if( ! obj || !key || !*key ) return cson_rc.ArgError; else { unsigned int ndx = 0; cson_kvp * kvp = cson_object_search_impl( obj, key, &ndx ); if( ! kvp ) { return cson_rc.NotFoundError; } assert( obj->kvp.count > 0 ); assert( obj->kvp.list[ndx] == kvp ); cson_kvp_free( kvp ); obj->kvp.list[ndx] = NULL; { /* if my brain were bigger i'd use memmove(). */ unsigned int i = ndx; for( ; i < obj->kvp.count; ++i ) { obj->kvp.list[i] = (i < (obj->kvp.alloced-1)) ? obj->kvp.list[i+1] : NULL; } } obj->kvp.list[--obj->kvp.count] = NULL; #if CSON_OBJECT_PROPS_SORT cson_object_sort_props( obj ); #endif return 0; } } int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v ) { if( !obj || !key ) return cson_rc.ArgError; else if( NULL == v ) return cson_object_unset( obj, cson_string_cstr(key) ); else { char const * cKey; cson_value * vKey; cson_kvp * kvp; vKey = cson_string_value(key); assert(vKey && (key==CSON_STR(vKey))); if( vKey == CSON_VCAST(obj) ){ return cson_rc.ArgError; } cKey = cson_string_cstr(key); kvp = cson_object_search_impl( obj, cKey, NULL ); if( kvp ) { /* "I told 'em we've already got one!" */ if( kvp->key != vKey ){ cson_value_free( kvp->key ); cson_refcount_incr(vKey); kvp->key = vKey; } if(kvp->value != v){ cson_value_free( kvp->value ); cson_refcount_incr( v ); kvp->value = v; } return 0; } if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) { /* reserve space */ unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6; if( n > cson_kvp_list_reserve( &obj->kvp, n ) ) { return cson_rc.AllocError; } } { /* insert new item... */ int rc = 0; kvp = cson_kvp_alloc(); if( ! kvp ) { return cson_rc.AllocError; } rc = cson_kvp_list_append( &obj->kvp, kvp ); if( 0 != rc ) { cson_kvp_free(kvp); } else { cson_refcount_incr(vKey); cson_refcount_incr(v); kvp->key = vKey; kvp->value = v; #if CSON_OBJECT_PROPS_SORT cson_object_sort_props( obj ); #endif } return rc; } } } int cson_object_set( cson_object * obj, char const * key, cson_value * v ) { if( ! obj || !key || !*key ) return cson_rc.ArgError; else if( NULL == v ) { return cson_object_unset( obj, key ); } else { cson_string * cs = cson_new_string(key,strlen(key)); if(!cs) return cson_rc.AllocError; else { int const rc = cson_object_set_s(obj, cs, v); if(rc) cson_value_free(cson_string_value(cs)); return rc; } } } cson_value * cson_object_take( cson_object * obj, char const * key ) { if( ! obj || !key || !*key ) return NULL; else { /* FIXME: this is 90% identical to cson_object_unset(), only with different refcount handling. Consolidate them. */ unsigned int ndx = 0; cson_kvp * kvp = cson_object_search_impl( obj, key, &ndx ); cson_value * rc = NULL; if( ! kvp ) { return NULL; } assert( obj->kvp.count > 0 ); assert( obj->kvp.list[ndx] == kvp ); rc = kvp->value; assert( rc ); kvp->value = NULL; cson_kvp_free( kvp ); assert( rc->refcount > 0 ); --rc->refcount; obj->kvp.list[ndx] = NULL; { /* if my brain were bigger i'd use memmove(). */ unsigned int i = ndx; for( ; i < obj->kvp.count; ++i ) { obj->kvp.list[i] = (i < (obj->kvp.alloced-1)) ? obj->kvp.list[i+1] : NULL; } } obj->kvp.list[--obj->kvp.count] = NULL; #if CSON_OBJECT_PROPS_SORT cson_object_sort_props( obj ); #endif return rc; } } /** @internal If p->node is-a Object then value is inserted into the object using p->key. In any other case cson_rc.InternalError is returned. Returns cson_rc.AllocError if an allocation fails. Returns 0 on success. On error, parsing must be ceased immediately. Ownership of val is ALWAYS TRANSFERED to this function. If this function fails, val will be cleaned up and destroyed. (This simplifies error handling in the core parser.) */ static int cson_parser_set_key( cson_parser * p, cson_value * val ) { assert( p && val ); if( p->ckey && cson_value_is_object(p->node) ) { int rc; cson_object * obj = cson_value_get_object(p->node); cson_kvp * kvp = NULL; assert( obj && (p->node->value == obj) ); /** FIXME? Use cson_object_set() instead of our custom finagling with the object? We do it this way to avoid an extra alloc/strcpy of the key data. */ if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) { if( obj->kvp.alloced > cson_kvp_list_reserve( &obj->kvp, obj->kvp.count ? (obj->kvp.count*2) : 5 ) ) { cson_value_free(val); return cson_rc.AllocError; } } kvp = cson_kvp_alloc(); if( ! kvp ) { cson_value_free(val); return cson_rc.AllocError; } kvp->key = cson_string_value(p->ckey)/*transfer ownership*/; assert(0 == kvp->key->refcount); cson_refcount_incr(kvp->key); p->ckey = NULL; kvp->value = val; cson_refcount_incr( val ); rc = cson_kvp_list_append( &obj->kvp, kvp ); if( 0 != rc ) { cson_kvp_free( kvp ); } else { ++p->totalValueCount; } return rc; } else { if(val) cson_value_free(val); return p->errNo = cson_rc.InternalError; } } /** @internal Pushes val into the current object/array parent node, depending on the internal state of the parser. Ownership of val is always transfered to this function, regardless of success or failure. Returns 0 on success. On error, parsing must be ceased immediately. */ static int cson_parser_push_value( cson_parser * p, cson_value * val ) { if( p->ckey ) { /* we're in Object mode */ assert( cson_value_is_object( p->node ) ); return cson_parser_set_key( p, val ); } else if( cson_value_is_array( p->node ) ) { /* we're in Array mode */ cson_array * ar = cson_value_get_array( p->node ); int rc; assert( ar && (ar == p->node->value) ); rc = cson_array_append( ar, val ); if( 0 != rc ) { cson_value_free(val); } else { ++p->totalValueCount; } return rc; } else { /* WTF? */ assert( 0 && "Internal error in cson_parser code" ); return p->errNo = cson_rc.InternalError; } } /** Callback for JSON_parser API. Reminder: it returns 0 (meaning false) on error! */ static int cson_parse_callback( void * cx, int type, JSON_value const * value ) { cson_parser * p = (cson_parser *)cx; int rc = 0; #define ALLOC_V(T,V) cson_value * v = cson_value_new_##T(V); if( ! v ) { rc = cson_rc.AllocError; break; } switch(type) { case JSON_T_ARRAY_BEGIN: case JSON_T_OBJECT_BEGIN: { cson_value * obja = (JSON_T_ARRAY_BEGIN == type) ? cson_value_new_array() : cson_value_new_object(); if( ! obja ) { p->errNo = cson_rc.AllocError; break; } if( 0 != rc ) break; if( ! p->root ) { p->root = p->node = obja; rc = cson_array_append( &p->stack, obja ); if( 0 != rc ) { /* work around a (potential) corner case in the cleanup code. */ cson_value_free( p->root ); p->root = NULL; } else { cson_refcount_incr( p->root ) /* simplifies cleanup later on. */ ; ++p->totalValueCount; } } else { rc = cson_array_append( &p->stack, obja ); if(rc) cson_value_free( obja ); else { rc = cson_parser_push_value( p, obja ); if( 0 == rc ) p->node = obja; } } break; } case JSON_T_ARRAY_END: case JSON_T_OBJECT_END: { if( 0 == p->stack.list.count ) { rc = cson_rc.RangeError; break; } #if CSON_OBJECT_PROPS_SORT if( cson_value_is_object(p->node) ) {/* kludge: the parser uses custom cson_object property insertion as a malloc/strcpy-reduction optimization. Because of that, we have to sort the property list ourselves... */ cson_object * obj = cson_value_get_object(p->node); assert( NULL != obj ); cson_object_sort_props( obj ); } #endif #if 1 /* Reminder: do not use cson_array_pop_back( &p->stack ) because that will clean up the object, and we don't want that. We just want to forget this reference to it. The object is either the root or was pushed into an object/array in the parse tree (and is owned by that object/array). */ --p->stack.list.count; assert( p->node == p->stack.list.list[p->stack.list.count] ); cson_refcount_decr( p->node ) /* p->node might be owned by an outer object but we need to remove the list's reference. For the root node we manually add a reference to avoid a special case here. Thus when we close the root node, its refcount is still 1. */; p->stack.list.list[p->stack.list.count] = NULL; if( p->stack.list.count ) { p->node = p->stack.list.list[p->stack.list.count-1]; } else { p->node = p->root; } #else /* Causing a leak? */ cson_array_pop_back( &p->stack, 1 ); if( p->stack.list.count ) { p->node = p->stack.list.list[p->stack.list.count-1]; } else { p->node = p->root; } assert( p->node && (1==p->node->refcount) ); #endif break; } case JSON_T_INTEGER: { ALLOC_V(integer, value->vu.integer_value ); rc = cson_parser_push_value( p, v ); break; } case JSON_T_FLOAT: { ALLOC_V(double, value->vu.float_value ); rc = cson_parser_push_value( p, v ); break; } case JSON_T_NULL: { rc = cson_parser_push_value( p, cson_value_null() ); break; } case JSON_T_TRUE: { rc = cson_parser_push_value( p, cson_value_true() ); break; } case JSON_T_FALSE: { rc = cson_parser_push_value( p, cson_value_false() ); break; } case JSON_T_KEY: { assert(!p->ckey); p->ckey = cson_new_string( value->vu.str.value, value->vu.str.length ); if( ! p->ckey ) { rc = cson_rc.AllocError; break; } ++p->totalKeyCount; break; } case JSON_T_STRING: { cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length ); rc = ( NULL == v ) ? cson_rc.AllocError : cson_parser_push_value( p, v ); break; } default: assert(0); rc = cson_rc.InternalError; break; } #undef ALLOC_V return ((p->errNo = rc)) ? 0 : 1; } /** Converts a JSON_error code to one of the cson_rc values. */ static int cson_json_err_to_rc( JSON_error jrc ) { switch(jrc) { case JSON_E_NONE: return 0; case JSON_E_INVALID_CHAR: return cson_rc.Parse_INVALID_CHAR; case JSON_E_INVALID_KEYWORD: return cson_rc.Parse_INVALID_KEYWORD; case JSON_E_INVALID_ESCAPE_SEQUENCE: return cson_rc.Parse_INVALID_ESCAPE_SEQUENCE; case JSON_E_INVALID_UNICODE_SEQUENCE: return cson_rc.Parse_INVALID_UNICODE_SEQUENCE; case JSON_E_INVALID_NUMBER: return cson_rc.Parse_INVALID_NUMBER; case JSON_E_NESTING_DEPTH_REACHED: return cson_rc.Parse_NESTING_DEPTH_REACHED; case JSON_E_UNBALANCED_COLLECTION: return cson_rc.Parse_UNBALANCED_COLLECTION; case JSON_E_EXPECTED_KEY: return cson_rc.Parse_EXPECTED_KEY; case JSON_E_EXPECTED_COLON: return cson_rc.Parse_EXPECTED_COLON; case JSON_E_OUT_OF_MEMORY: return cson_rc.AllocError; default: return cson_rc.InternalError; } } /** @internal Cleans up all contents of p but does not free p. To properly take over ownership of the parser's root node on a successful parse: - Copy p->root's pointer and set p->root to NULL. - Eventually free up p->root with cson_value_free(). If you do not set p->root to NULL, p->root will be freed along with any other items inserted into it (or under it) during the parsing process. */ static int cson_parser_clean( cson_parser * p ) { if( ! p ) return cson_rc.ArgError; else { if( p->p ) { delete_JSON_parser(p->p); p->p = NULL; } if( p->ckey ){ cson_value_free(cson_string_value(p->ckey)); } cson_array_clean( &p->stack, 1 ); if( p->root ) { cson_value_free( p->root ); } *p = cson_parser_empty; return 0; } } int cson_parse( cson_value ** tgt, cson_data_source_f src, void * state, cson_parse_opt const * opt_, cson_parse_info * info_ ) { unsigned char ch[2] = {0,0}; cson_parse_opt const opt = opt_ ? *opt_ : cson_parse_opt_empty; int rc = 0; unsigned int len = 1; cson_parse_info info = info_ ? *info_ : cson_parse_info_empty; cson_parser p = cson_parser_empty; if( ! tgt || ! src ) return cson_rc.ArgError; { JSON_config jopt = {0}; init_JSON_config( &jopt ); jopt.allow_comments = opt.allowComments; jopt.depth = opt.maxDepth; jopt.callback_ctx = &p; jopt.handle_floats_manually = 0; jopt.callback = cson_parse_callback; p.p = new_JSON_parser(&jopt); if( ! p.p ) { return cson_rc.AllocError; } } do { /* FIXME: buffer the input in multi-kb chunks. */ len = 1; ch[0] = 0; rc = src( state, ch, &len ); if( 0 != rc ) break; else if( !len /* EOF */ ) break; ++info.length; if('\n' == ch[0]) { ++info.line; info.col = 0; } if( ! JSON_parser_char(p.p, ch[0]) ) { rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) ); if(0==rc) rc = p.errNo; if(0==rc) rc = cson_rc.InternalError; info.errorCode = rc; break; } if( '\n' != ch[0]) ++info.col; } while(1); if( info_ ) { info.totalKeyCount = p.totalKeyCount; info.totalValueCount = p.totalValueCount; *info_ = info; } if( 0 != rc ) { cson_parser_clean(&p); return rc; } if( ! JSON_parser_done(p.p) ) { rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) ); cson_parser_clean(&p); if(0==rc) rc = p.errNo; if(0==rc) rc = cson_rc.InternalError; } else { cson_value * root = p.root; p.root = NULL; cson_parser_clean(&p); if( root ) { assert( (1 == root->refcount) && "Detected memory mismanagement in the parser." ); root->refcount = 0 /* HUGE KLUDGE! Avoids having one too many references in some client code, leading to a leak. Here we're accommodating a memory management workaround in the parser code which manually adds a reference to the root node to keep it from being cleaned up prematurely. */; *tgt = root; } else { /* then can happen on empty input. */ rc = cson_rc.UnknownError; } } return rc; } /** The UTF code was originally taken from sqlite3's public-domain source code (http://sqlite.org), modified only slightly for use here. This code generates some "possible data loss" warnings on MSVC, but if this code is good enough for sqlite3 then it's damned well good enough for me, so we disable that warning for Windows builds. */ /* ** This lookup table is used to help decode the first byte of ** a multi-byte UTF8 character. */ static const unsigned char cson_utfTrans1[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00 }; /* ** Translate a single UTF-8 character. Return the unicode value. ** ** During translation, assume that the byte that zTerm points ** is a 0x00. ** ** Write a pointer to the next unread byte back into *pzNext. ** ** Notes On Invalid UTF-8: ** ** * This routine never allows a 7-bit character (0x00 through 0x7f) to ** be encoded as a multi-byte character. Any multi-byte character that ** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd. ** ** * This routine never allows a UTF16 surrogate value to be encoded. ** If a multi-byte character attempts to encode a value between ** 0xd800 and 0xe000 then it is rendered as 0xfffd. ** ** * Bytes in the range of 0x80 through 0xbf which occur as the first ** byte of a character are interpreted as single-byte characters ** and rendered as themselves even though they are technically ** invalid characters. ** ** * This routine accepts an infinite number of different UTF8 encodings ** for unicode values 0x80 and greater. It do not change over-length ** encodings to 0xfffd as some systems recommend. */ #define READ_UTF8(zIn, zTerm, c) \ c = *(zIn++); \ if( c>=0xc0 ){ \ c = cson_utfTrans1[c-0xc0]; \ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ c = (c<<6) + (0x3f & *(zIn++)); \ } \ if( c<0x80 \ || (c&0xFFFFF800)==0xD800 \ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ } static int cson_utf8Read( const unsigned char *z, /* First byte of UTF-8 character */ const unsigned char *zTerm, /* Pretend this byte is 0x00 */ const unsigned char **pzNext /* Write first byte past UTF-8 char here */ ){ int c; READ_UTF8(z, zTerm, c); *pzNext = z; return c; } #undef READ_UTF8 #ifdef _MSC_VER # if _MSC_VER >= 1400 /* Visual Studio 2005 and up */ # pragma warning( pop ) # endif #endif unsigned int cson_string_length_utf8( cson_string const * str ) { if( ! str ) return 0; else { char unsigned const * pos = (char unsigned const *)cson_string_cstr(str); char unsigned const * end = pos + str->length; unsigned int rc = 0; for( ; (pos < end) && cson_utf8Read(pos, end, &pos); ++rc ) { }; return rc; } } /** Escapes the first len bytes of the given string as JSON and sends it to the given output function (which will be called often - once for each logical character). The output is also surrounded by double-quotes. A NULL str will be escaped as an empty string, though we should arguably export it as "null" (without quotes). We do this because in JavaScript (typeof null === "object"), and by outputing null here we would effectively change the data type from string to object. */ static int cson_str_to_json( char const * str, unsigned int len, char escapeFwdSlash, cson_data_dest_f f, void * state ) { if( NULL == f ) return cson_rc.ArgError; else if( !str || !*str || (0 == len) ) { /* special case for 0-length strings. */ return f( state, "\"\"", 2 ); } else { unsigned char const * pos = (unsigned char const *)str; unsigned char const * end = (unsigned char const *)(str ? (str + len) : NULL); unsigned char const * next = NULL; int ch; unsigned char clen = 0; char escChar[3] = {'\\',0,0}; enum { UBLen = 20 }; char ubuf[UBLen]; int rc = 0; rc = f(state, "\"", 1 ); for( ; (pos < end) && (0 == rc); pos += clen ) { ch = cson_utf8Read(pos, end, &next); if( 0 == ch ) break; assert( next > pos ); clen = next - pos; assert( clen ); if( 1 == clen ) { /* ASCII */ #if defined(CSON_FOSSIL_MODE) /* Workaround for fossil repo artifact f460839cff85d4e4f1360b366bb2858cef1411ea, which has what appears to be latin1-encoded text. file(1) thinks it's a FORTRAN program. */ if(0xfffd==ch){ assert(*pos != ch); /* MARKER("ch=%04x, *pos=%04x\n", ch, *pos); */ ch = *pos /* We should arguably translate to '?', and will if this problem ever comes up with a non-latin1 encoding. For latin1 this workaround incidentally corrects the output to proper UTF8-escaped characters, and only for that reason is it being kept around. */; goto assume_latin1; } #endif assert( (*pos == ch) && "Invalid UTF8" ); escChar[1] = 0; switch(ch) { case '\t': escChar[1] = 't'; break; case '\r': escChar[1] = 'r'; break; case '\n': escChar[1] = 'n'; break; case '\f': escChar[1] = 'f'; break; case '\b': escChar[1] = 'b'; break; case '/': /* Regarding escaping of forward-slashes. See the main exchange below... -------------- From: Douglas Crockford <douglas@crockford.com> To: Stephan Beal <sgbeal@googlemail.com> Subject: Re: Is escaping of forward slashes required? It is allowed, not required. It is allowed so that JSON can be safely embedded in HTML, which can freak out when seeing strings containing "</". JSON tolerates "<\/" for this reason. On 4/8/2011 2:09 PM, Stephan Beal wrote: > Hello, Jsonites, > > i'm a bit confused on a small grammatic detail of JSON: > > if i'm reading the grammar chart on http://www.json.org/ correctly, > forward slashes (/) are supposed to be escaped in JSON. However, the > JSON class provided with my browsers (Chrome and FF, both of which i > assume are fairly standards/RFC-compliant) do not escape such characters. > > Is backslash-escaping forward slashes required? If so, what is the > justification for it? (i ask because i find it unnecessary and hard to > look at.) -------------- */ if( escapeFwdSlash ) escChar[1] = '/'; break; case '\\': escChar[1] = '\\'; break; case '"': escChar[1] = '"'; break; default: break; } if( escChar[1]) { rc = f(state, escChar, 2); } else { rc = f(state, (char const *)pos, clen); } continue; } else { /* UTF: transform it to \uXXXX */ #if defined(CSON_FOSSIL_MODE) assume_latin1: #endif memset(ubuf,0,UBLen); if(ch <= 0xFFFF){ rc = sprintf(ubuf, "\\u%04x",ch); if( rc != 6 ) { rc = cson_rc.RangeError; break; } rc = f( state, ubuf, 6 ); }else{ /* encode as a UTF16 surrogate pair */ /* http://unicodebook.readthedocs.org/en/latest/unicode_encodings.html#surrogates */ ch -= 0x10000; rc = sprintf(ubuf, "\\u%04x\\u%04x", (0xd800 | (ch>>10)), (0xdc00 | (ch & 0x3ff))); if( rc != 12 ) { rc = cson_rc.RangeError; break; } rc = f( state, ubuf, 12 ); } continue; } } if( 0 == rc ) { rc = f(state, "\"", 1 ); } return rc; } } int cson_object_iter_init( cson_object const * obj, cson_object_iterator * iter ) { if( ! obj || !iter ) return cson_rc.ArgError; else { iter->obj = obj; iter->pos = 0; return 0; } } cson_kvp * cson_object_iter_next( cson_object_iterator * iter ) { if( ! iter || !iter->obj ) return NULL; else if( iter->pos >= iter->obj->kvp.count ) return NULL; else { cson_kvp * rc = iter->obj->kvp.list[iter->pos++]; while( (NULL==rc) && (iter->pos < iter->obj->kvp.count)) { rc = iter->obj->kvp.list[iter->pos++]; } return rc; } } static int cson_output_null( cson_data_dest_f f, void * state ) { if( !f ) return cson_rc.ArgError; else { return f(state, "null", 4); } } static int cson_output_bool( cson_value const * src, cson_data_dest_f f, void * state ) { if( !f ) return cson_rc.ArgError; else { char const v = cson_value_get_bool(src); return f(state, v ? "true" : "false", v ? 4 : 5); } } static int cson_output_integer( cson_value const * src, cson_data_dest_f f, void * state ) { if( !f ) return cson_rc.ArgError; else if( !cson_value_is_integer(src) ) return cson_rc.TypeError; else { enum { BufLen = 100 }; char b[BufLen]; int rc; memset( b, 0, BufLen ); rc = sprintf( b, "%"CSON_INT_T_PFMT, cson_value_get_integer(src) ) /* Reminder: snprintf() is C99 */ ; return ( rc<=0 ) ? cson_rc.RangeError : f( state, b, (unsigned int)rc ) ; } } static int cson_output_double( cson_value const * src, cson_data_dest_f f, void * state ) { if( !f ) return cson_rc.ArgError; else if( !cson_value_is_double(src) ) return cson_rc.TypeError; else { enum { BufLen = 128 /* this must be relatively large or huge doubles can cause us to overrun here, resulting in stack-smashing errors. */}; char b[BufLen]; int rc; memset( b, 0, BufLen ); rc = sprintf( b, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(src) ) /* Reminder: snprintf() is C99 */ ; if( rc<=0 ) return cson_rc.RangeError; else if(1) { /* Strip trailing zeroes before passing it on... */ unsigned int urc = (unsigned int)rc; char * pos = b + urc - 1; for( ; ('0' == *pos) && urc && (*(pos-1) != '.'); --pos, --urc ) { *pos = 0; } assert(urc && *pos); return f( state, b, urc ); } else { unsigned int urc = (unsigned int)rc; return f( state, b, urc ); } return 0; } } static int cson_output_string( cson_value const * src, char escapeFwdSlash, cson_data_dest_f f, void * state ) { if( !f ) return cson_rc.ArgError; else if( ! cson_value_is_string(src) ) return cson_rc.TypeError; else { cson_string const * str = cson_value_get_string(src); assert( NULL != str ); return cson_str_to_json(cson_string_cstr(str), str->length, escapeFwdSlash, f, state); } } /** Outputs indention spacing to f(). blanks: (0)=no indentation, (1)=1 TAB per/level, (>1)=n spaces/level depth is the current depth of the output tree, and determines how much indentation to generate. If blanks is 0 this is a no-op. Returns non-0 on error, and the error code will always come from f(). */ static int cson_output_indent( cson_data_dest_f f, void * state, unsigned char blanks, unsigned int depth ) { if( 0 == blanks ) return 0; else { #if 0 /* FIXME: stuff the indention into the buffer and make a single call to f(). */ enum { BufLen = 200 }; char buf[BufLen]; #endif unsigned int i; unsigned int x; char const ch = (1==blanks) ? '\t' : ' '; int rc = f(state, "\n", 1 ); for( i = 0; (i < depth) && (0 == rc); ++i ) { for( x = 0; (x < blanks) && (0 == rc); ++x ) { rc = f(state, &ch, 1); } } return rc; } } static int cson_output_array( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt, unsigned int level ); static int cson_output_object( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt, unsigned int level ); /** Main cson_output() implementation. Dispatches to a different impl depending on src->api->typeID. Returns 0 on success. */ static int cson_output_impl( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt, unsigned int level ) { if( ! src || !f || !src->api ) return cson_rc.ArgError; else { int rc = 0; assert(fmt); switch( src->api->typeID ) { case CSON_TYPE_UNDEF: case CSON_TYPE_NULL: rc = cson_output_null(f, state); break; case CSON_TYPE_BOOL: rc = cson_output_bool(src, f, state); break; case CSON_TYPE_INTEGER: rc = cson_output_integer(src, f, state); break; case CSON_TYPE_DOUBLE: rc = cson_output_double(src, f, state); break; case CSON_TYPE_STRING: rc = cson_output_string(src, fmt->escapeForwardSlashes, f, state); break; case CSON_TYPE_ARRAY: rc = cson_output_array( src, f, state, fmt, level ); break; case CSON_TYPE_OBJECT: rc = cson_output_object( src, f, state, fmt, level ); break; default: rc = cson_rc.TypeError; break; } return rc; } } static int cson_output_array( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt, unsigned int level ) { if( !src || !f || !fmt ) return cson_rc.ArgError; else if( ! cson_value_is_array(src) ) return cson_rc.TypeError; else if( level > fmt->maxDepth ) return cson_rc.RangeError; else { int rc; unsigned int i; cson_value const * v; char doIndent = fmt->indentation ? 1 : 0; cson_array const * ar = cson_value_get_array(src); assert( NULL != ar ); if( 0 == ar->list.count ) { return f(state, "[]", 2 ); } else if( (1 == ar->list.count) && !fmt->indentSingleMemberValues ) doIndent = 0; rc = f(state, "[", 1); ++level; if( doIndent ) { rc = cson_output_indent( f, state, fmt->indentation, level ); } for( i = 0; (i < ar->list.count) && (0 == rc); ++i ) { v = ar->list.list[i]; if( v ) { rc = cson_output_impl( v, f, state, fmt, level ); } else { rc = cson_output_null( f, state ); } if( 0 == rc ) { if(i < (ar->list.count-1)) { rc = f(state, ",", 1); if( 0 == rc ) { rc = doIndent ? cson_output_indent( f, state, fmt->indentation, level ) : 0 /*f( state, " ", 1 )*/; } } } } --level; if( doIndent && (0 == rc) ) { rc = cson_output_indent( f, state, fmt->indentation, level ); } return (0 == rc) ? f(state, "]", 1) : rc; } } static int cson_output_object( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt, unsigned int level ) { if( !src || !f || !fmt ) return cson_rc.ArgError; else if( ! cson_value_is_object(src) ) return cson_rc.TypeError; else if( level > fmt->maxDepth ) return cson_rc.RangeError; else { int rc; unsigned int i; cson_kvp const * kvp; char doIndent = fmt->indentation ? 1 : 0; cson_object const * obj = cson_value_get_object(src); assert( (NULL != obj) && (NULL != fmt)); if( 0 == obj->kvp.count ) { return f(state, "{}", 2 ); } else if( (1 == obj->kvp.count) && !fmt->indentSingleMemberValues ) doIndent = 0; rc = f(state, "{", 1); ++level; if( doIndent ) { rc = cson_output_indent( f, state, fmt->indentation, level ); } for( i = 0; (i < obj->kvp.count) && (0 == rc); ++i ) { kvp = obj->kvp.list[i]; if( kvp && kvp->key ) { cson_string const * sKey = cson_value_get_string(kvp->key); char const * cKey = cson_string_cstr(sKey); rc = cson_str_to_json(cKey, sKey->length, fmt->escapeForwardSlashes, f, state); if( 0 == rc ) { rc = fmt->addSpaceAfterColon ? f(state, ": ", 2 ) : f(state, ":", 1 ) ; } if( 0 == rc) { rc = ( kvp->value ) ? cson_output_impl( kvp->value, f, state, fmt, level ) : cson_output_null( f, state ); } } else { assert( 0 && "Possible internal error." ); continue /* internal error? */; } if( 0 == rc ) { if(i < (obj->kvp.count-1)) { rc = f(state, ",", 1); if( 0 == rc ) { rc = doIndent ? cson_output_indent( f, state, fmt->indentation, level ) : 0 /*f( state, " ", 1 )*/; } } } } --level; if( doIndent && (0 == rc) ) { rc = cson_output_indent( f, state, fmt->indentation, level ); } return (0 == rc) ? f(state, "}", 1) : rc; } } int cson_output( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt ) { int rc; if(! fmt ) fmt = &cson_output_opt_empty; rc = cson_output_impl(src, f, state, fmt, 0 ); if( (0 == rc) && fmt->addNewline ) { rc = f(state, "\n", 1); } return rc; } int cson_data_dest_FILE( void * state, void const * src, unsigned int n ) { if( ! state ) return cson_rc.ArgError; else if( !src || !n ) return 0; else { return ( 1 == fwrite( src, n, 1, (FILE*) state ) ) ? 0 : cson_rc.IOError; } } int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * fmt ) { int rc = 0; if( fmt ) { rc = cson_output( src, cson_data_dest_FILE, dest, fmt ); } else { /* We normally want a newline on FILE output. */ cson_output_opt opt = cson_output_opt_empty; opt.addNewline = 1; rc = cson_output( src, cson_data_dest_FILE, dest, &opt ); } if( 0 == rc ) { fflush( dest ); } return rc; } int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt ) { if( !src || !dest ) return cson_rc.ArgError; else { FILE * f = fopen(dest,"wb"); if( !f ) return cson_rc.IOError; else { int const rc = cson_output_FILE( src, f, fmt ); fclose(f); return rc; } } } int cson_parse_filename( cson_value ** tgt, char const * src, cson_parse_opt const * opt, cson_parse_info * err ) { if( !src || !tgt ) return cson_rc.ArgError; else { FILE * f = fopen(src, "r"); if( !f ) return cson_rc.IOError; else { int const rc = cson_parse_FILE( tgt, f, opt, err ); fclose(f); return rc; } } } /** Internal type to hold state for a JSON input string. */ typedef struct cson_data_source_StringSource_ { /** Start of input string. */ char const * str; /** Current iteration position. Must initially be == str. */ char const * pos; /** Logical EOF, one-past-the-end of str. */ char const * end; } cson_data_source_StringSource_t; /** A cson_data_source_f() implementation which requires the state argument to be a properly populated (cson_data_source_StringSource_t*). */ static int cson_data_source_StringSource( void * state, void * dest, unsigned int * n ) { if( !state || !n || !dest ) return cson_rc.ArgError; else if( !*n ) return 0 /* ignore this */; else { unsigned int i; cson_data_source_StringSource_t * ss = (cson_data_source_StringSource_t*) state; unsigned char * tgt = (unsigned char *)dest; for( i = 0; (i < *n) && (ss->pos < ss->end); ++i, ++ss->pos, ++tgt ) { *tgt = *ss->pos; } *n = i; return 0; } } int cson_parse_string( cson_value ** tgt, char const * src, unsigned int len, cson_parse_opt const * opt, cson_parse_info * err ) { if( ! tgt || !src ) return cson_rc.ArgError; else if( !*src || (len<2/*2==len of {} and []*/) ) return cson_rc.RangeError; else { cson_data_source_StringSource_t ss; ss.str = ss.pos = src; ss.end = src + len; return cson_parse( tgt, cson_data_source_StringSource, &ss, opt, err ); } } int cson_parse_buffer( cson_value ** tgt, cson_buffer const * buf, cson_parse_opt const * opt, cson_parse_info * err ) { return ( !tgt || !buf || !buf->mem || !buf->used ) ? cson_rc.ArgError : cson_parse_string( tgt, (char const *)buf->mem, buf->used, opt, err ); } int cson_buffer_reserve( cson_buffer * buf, cson_size_t n ) { if( ! buf ) return cson_rc.ArgError; else if( 0 == n ) { cson_free(buf->mem, "cson_buffer::mem"); *buf = cson_buffer_empty; return 0; } else if( buf->capacity >= n ) { return 0; } else { unsigned char * x = (unsigned char *)cson_realloc( buf->mem, n, "cson_buffer::mem" ); if( ! x ) return cson_rc.AllocError; memset( x + buf->used, 0, n - buf->used ); buf->mem = x; buf->capacity = n; ++buf->timesExpanded; return 0; } } cson_size_t cson_buffer_fill( cson_buffer * buf, char c ) { if( !buf || !buf->capacity || !buf->mem ) return 0; else { memset( buf->mem, c, buf->capacity ); return buf->capacity; } } /** cson_data_dest_f() implementation, used by cson_output_buffer(). arg MUST be a (cson_buffer*). This function appends n bytes at position arg->used, expanding the buffer as necessary. */ static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n ) { if( !arg ) return cson_rc.ArgError; else if( ! n ) return 0; else { cson_buffer * sb = (cson_buffer*)arg; char const * data = (char const *)data_; cson_size_t npos = sb->used + n; unsigned int i; if( npos >= sb->capacity ) { const cson_size_t oldCap = sb->capacity; const cson_size_t asz = npos * 2; if( asz < npos ) return cson_rc.ArgError; /* overflow */ else if( 0 != cson_buffer_reserve( sb, asz ) ) return cson_rc.AllocError; assert( (sb->capacity > oldCap) && "Internal error in memory buffer management!" ); /* make sure it gets NUL terminated. */ memset( sb->mem + oldCap, 0, (sb->capacity - oldCap) ); } for( i = 0; i < n; ++i, ++sb->used ) { sb->mem[sb->used] = data[i]; } return 0; } } int cson_output_buffer( cson_value const * v, cson_buffer * buf, cson_output_opt const * opt ) { int rc = cson_output( v, cson_data_dest_cson_buffer, buf, opt ); if( 0 == rc ) { /* Ensure that the buffer is null-terminated. */ rc = cson_buffer_reserve( buf, buf->used + 1 ); if( 0 == rc ) { buf->mem[buf->used] = 0; } } return rc; } /** @internal Tokenizes an input string on a given separator. Inputs are: - (inp) = is a pointer to the pointer to the start of the input. - (separator) = the separator character - (end) = a pointer to NULL. i.e. (*end == NULL) This function scans *inp for the given separator char or a NUL char. Successive separators at the start of *inp are skipped. The effect is that, when this function is called in a loop, all neighboring separators are ignored. e.g. the string "aa.bb...cc" will tokenize to the list (aa,bb,cc) if the separator is '.' and to (aa.,...cc) if the separator is 'b'. Returns 0 (false) if it finds no token, else non-0 (true). Output: - (*inp) will be set to the first character of the next token. - (*end) will point to the one-past-the-end point of the token. If (*inp == *end) then the end of the string has been reached without finding a token. Post-conditions: - (*end == *inp) if no token is found. - (*end > *inp) if a token is found. It is intolerant of NULL values for (inp, end), and will assert() in debug builds if passed NULL as either parameter. */ static char cson_next_token( char const ** inp, char separator, char const ** end ) { char const * pos = NULL; assert( inp && end && *inp ); if( *inp == *end ) return 0; pos = *inp; if( !*pos ) { *end = pos; return 0; } for( ; *pos && (*pos == separator); ++pos) { /* skip preceeding splitters */ } *inp = pos; for( ; *pos && (*pos != separator); ++pos) { /* find next splitter */ } *end = pos; return (pos > *inp) ? 1 : 0; } int cson_object_fetch_sub2( cson_object const * obj, cson_value ** tgt, char const * path ) { if( ! obj || !path ) return cson_rc.ArgError; else if( !*path || !*(1+path) ) return cson_rc.RangeError; else return cson_object_fetch_sub(obj, tgt, path+1, *path); } int cson_object_fetch_sub( cson_object const * obj, cson_value ** tgt, char const * path, char sep ) { if( ! obj || !path ) return cson_rc.ArgError; else if( !*path || !sep ) return cson_rc.RangeError; else { char const * beg = path; char const * end = NULL; int rc; unsigned int i, len; unsigned int tokenCount = 0; cson_value * cv = NULL; cson_object const * curObj = obj; enum { BufSize = 128 }; char buf[BufSize]; memset( buf, 0, BufSize ); while( cson_next_token( &beg, sep, &end ) ) { if( beg == end ) break; else { ++tokenCount; beg = end; end = NULL; } } if( 0 == tokenCount ) return cson_rc.RangeError; beg = path; end = NULL; for( i = 0; i < tokenCount; ++i, beg=end, end=NULL ) { rc = cson_next_token( &beg, sep, &end ); assert( 1 == rc ); assert( beg != end ); assert( end > beg ); len = end - beg; if( len > (BufSize-1) ) return cson_rc.RangeError; memset( buf, 0, len + 1 ); memcpy( buf, beg, len ); buf[len] = 0; cv = cson_object_get( curObj, buf ); if( NULL == cv ) return cson_rc.NotFoundError; else if( i == (tokenCount-1) ) { if(tgt) *tgt = cv; return 0; } else if( cson_value_is_object(cv) ) { curObj = cson_value_get_object(cv); assert((NULL != curObj) && "Detected mis-management of internal memory!"); } /* TODO: arrays. Requires numeric parsing for the index. */ else { return cson_rc.NotFoundError; } } assert( i == tokenCount ); return cson_rc.NotFoundError; } } cson_value * cson_object_get_sub( cson_object const * obj, char const * path, char sep ) { cson_value * v = NULL; cson_object_fetch_sub( obj, &v, path, sep ); return v; } cson_value * cson_object_get_sub2( cson_object const * obj, char const * path ) { cson_value * v = NULL; cson_object_fetch_sub2( obj, &v, path ); return v; } /** If v is-a Object or Array then this function returns a deep clone, otherwise it returns v. In either case, the refcount of the returned value is increased by 1 by this call. */ static cson_value * cson_value_clone_ref( cson_value * v ) { cson_value * rc = NULL; #define TRY_SHARING 1 #if TRY_SHARING if(!v ) return rc; else if( cson_value_is_object(v) || cson_value_is_array(v)) { rc = cson_value_clone( v ); } else { rc = v; } #else rc = cson_value_clone(v); #endif #undef TRY_SHARING cson_value_add_reference(rc); return rc; } static cson_value * cson_value_clone_array( cson_value const * orig ) { unsigned int i = 0; cson_array const * asrc = cson_value_get_array( orig ); unsigned int alen = cson_array_length_get( asrc ); cson_value * destV = NULL; cson_array * destA = NULL; assert( orig && asrc ); destV = cson_value_new_array(); if( NULL == destV ) return NULL; destA = cson_value_get_array( destV ); assert( destA ); if( 0 != cson_array_reserve( destA, alen ) ) { cson_value_free( destV ); return NULL; } for( ; i < alen; ++i ) { cson_value * ch = cson_array_get( asrc, i ); if( NULL != ch ) { cson_value * cl = cson_value_clone_ref( ch ); if( NULL == cl ) { cson_value_free( destV ); return NULL; } if( 0 != cson_array_set( destA, i, cl ) ) { cson_value_free( cl ); cson_value_free( destV ); return NULL; } cson_value_free(cl)/*remove our artificial reference */; } } return destV; } static cson_value * cson_value_clone_object( cson_value const * orig ) { cson_object const * src = cson_value_get_object( orig ); cson_value * destV = NULL; cson_object * dest = NULL; cson_kvp const * kvp = NULL; cson_object_iterator iter = cson_object_iterator_empty; assert( orig && src ); if( 0 != cson_object_iter_init( src, &iter ) ) { return NULL; } destV = cson_value_new_object(); if( NULL == destV ) return NULL; dest = cson_value_get_object( destV ); assert( dest ); if( src->kvp.count > cson_kvp_list_reserve( &dest->kvp, src->kvp.count ) ){ cson_value_free( destV ); return NULL; } while( (kvp = cson_object_iter_next( &iter )) ) { cson_value * key = NULL; cson_value * val = NULL; assert( kvp->key && (kvp->key->refcount>0) ); key = cson_value_clone_ref(kvp->key); val = key ? cson_value_clone_ref(kvp->value) : NULL; if( ! key || !val ){ goto error; } assert( CSON_STR(key) ); if( 0 != cson_object_set_s( dest, CSON_STR(key), val ) ) { goto error; } /* remove our references */ cson_value_free(key); cson_value_free(val); continue; error: cson_value_free(key); cson_value_free(val); cson_value_free(destV); destV = NULL; break; } return destV; } cson_value * cson_value_clone( cson_value const * orig ) { if( NULL == orig ) return NULL; else { switch( orig->api->typeID ) { case CSON_TYPE_UNDEF: assert(0 && "This should never happen."); return NULL; case CSON_TYPE_NULL: return cson_value_null(); case CSON_TYPE_BOOL: return cson_value_new_bool( cson_value_get_bool( orig ) ); case CSON_TYPE_INTEGER: return cson_value_new_integer( cson_value_get_integer( orig ) ); break; case CSON_TYPE_DOUBLE: return cson_value_new_double( cson_value_get_double( orig ) ); break; case CSON_TYPE_STRING: { cson_string const * str = cson_value_get_string( orig ); return cson_value_new_string( cson_string_cstr( str ), cson_string_length_bytes( str ) ); } case CSON_TYPE_ARRAY: return cson_value_clone_array( orig ); case CSON_TYPE_OBJECT: return cson_value_clone_object( orig ); } assert( 0 && "We can't get this far." ); return NULL; } } cson_value * cson_string_value(cson_string const * s) { #define MT CSON_SPECIAL_VALUES[CSON_VAL_STR_EMPTY] return s ? ((s==MT.value) ? &MT : CSON_VCAST(s)) : NULL; #undef MT } cson_value * cson_object_value(cson_object const * s) { return s ? CSON_VCAST(s) : NULL; } cson_value * cson_array_value(cson_array const * s) { return s ? CSON_VCAST(s) : NULL; } void cson_free_object(cson_object *x) { if(x) cson_value_free(cson_object_value(x)); } void cson_free_array(cson_array *x) { if(x) cson_value_free(cson_array_value(x)); } void cson_free_string(cson_string *x) { if(x) cson_value_free(cson_string_value(x)); } void cson_free_value(cson_value *x) { if(x) cson_value_free(x); } #if 0 /* i'm not happy with this... */ char * cson_pod_to_string( cson_value const * orig ) { if( ! orig ) return NULL; else { enum { BufSize = 64 }; char * v = NULL; switch( orig->api->typeID ) { case CSON_TYPE_BOOL: { char const bv = cson_value_get_bool(orig); v = cson_strdup( bv ? "true" : "false", bv ? 4 : 5 ); break; } case CSON_TYPE_UNDEF: case CSON_TYPE_NULL: { v = cson_strdup( "null", 4 ); break; } case CSON_TYPE_STRING: { cson_string const * jstr = cson_value_get_string(orig); unsigned const int slen = cson_string_length_bytes( jstr ); assert( NULL != jstr ); v = cson_strdup( cson_string_cstr( jstr ), slen ); break; } case CSON_TYPE_INTEGER: { char buf[BufSize] = {0}; if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) ) { v = cson_strdup( buf, strlen(buf) ); } break; } case CSON_TYPE_DOUBLE: { char buf[BufSize] = {0}; if( 0 < sprintf( v, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(orig)) ) { v = cson_strdup( buf, strlen(buf) ); } break; } default: break; } return v; } } #endif #if 0 /* i'm not happy with this... */ char * cson_pod_to_string( cson_value const * orig ) { if( ! orig ) return NULL; else { enum { BufSize = 64 }; char * v = NULL; switch( orig->api->typeID ) { case CSON_TYPE_BOOL: { char const bv = cson_value_get_bool(orig); v = cson_strdup( bv ? "true" : "false", bv ? 4 : 5 ); break; } case CSON_TYPE_UNDEF: case CSON_TYPE_NULL: { v = cson_strdup( "null", 4 ); break; } case CSON_TYPE_STRING: { cson_string const * jstr = cson_value_get_string(orig); unsigned const int slen = cson_string_length_bytes( jstr ); assert( NULL != jstr ); v = cson_strdup( cson_string_cstr( jstr ), slen ); break; } case CSON_TYPE_INTEGER: { char buf[BufSize] = {0}; if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) ) { v = cson_strdup( buf, strlen(buf) ); } break; } case CSON_TYPE_DOUBLE: { char buf[BufSize] = {0}; if( 0 < sprintf( v, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(orig)) ) { v = cson_strdup( buf, strlen(buf) ); } break; } default: break; } return v; } } #endif unsigned int cson_value_msize(cson_value const * v) { if(!v) return 0; else if( cson_value_is_builtin(v) ) return 0; else { unsigned int rc = sizeof(cson_value); assert(NULL != v->api); switch(v->api->typeID){ case CSON_TYPE_INTEGER: assert( v != &CSON_SPECIAL_VALUES[CSON_VAL_INT_0]); rc += sizeof(cson_int_t); break; case CSON_TYPE_DOUBLE: assert( v != &CSON_SPECIAL_VALUES[CSON_VAL_DBL_0]); rc += sizeof(cson_double_t); break; case CSON_TYPE_STRING: rc += sizeof(cson_string) + CSON_STR(v)->length + 1/*NUL*/; break; case CSON_TYPE_ARRAY:{ cson_array const * ar = CSON_ARRAY(v); cson_value_list const * li; unsigned int i = 0; assert( NULL != ar ); li = &ar->list; rc += sizeof(cson_array) + (li->alloced * sizeof(cson_value *)); for( ; i < li->count; ++i ){ cson_value const * e = ar->list.list[i]; if( e ) rc += cson_value_msize( e ); } break; } case CSON_TYPE_OBJECT:{ cson_object const * obj = CSON_OBJ(v); unsigned int i = 0; cson_kvp_list const * kl; assert(NULL != obj); kl = &obj->kvp; rc += sizeof(cson_object) + (kl->alloced * sizeof(cson_kvp*)); for( ; i < kl->count; ++i ){ cson_kvp const * kvp = kl->list[i]; assert(NULL != kvp); rc += cson_value_msize(kvp->key); rc += cson_value_msize(kvp->value); } break; } case CSON_TYPE_UNDEF: case CSON_TYPE_NULL: case CSON_TYPE_BOOL: assert( 0 && "Should have been caught by is-builtin check!" ); break; default: assert(0 && "Invalid typeID!"); return 0; #undef RCCHECK } return rc; } } int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){ cson_object_iterator iter = cson_object_iterator_empty; int rc; char const replace = (flags & CSON_MERGE_REPLACE); char const recurse = !(flags & CSON_MERGE_NO_RECURSE); cson_kvp const * kvp; if((!dest || !src) || (dest==src)) return cson_rc.ArgError; rc = cson_object_iter_init( src, &iter ); if(rc) return rc; while( (kvp = cson_object_iter_next(&iter) ) ) { cson_string * key = cson_kvp_key(kvp); cson_value * val = cson_kvp_value(kvp); cson_value * check = cson_object_get_s( dest, key ); if(!check){ cson_object_set_s( dest, key, val ); continue; } else if(!replace && !recurse) continue; else if(replace && !recurse){ cson_object_set_s( dest, key, val ); continue; } else if( recurse ){ if( cson_value_is_object(check) && cson_value_is_object(val) ){ rc = cson_object_merge( cson_value_get_object(check), cson_value_get_object(val), flags ); if(rc) return rc; else continue; } else continue; } else continue; } return 0; } static cson_value * cson_guess_arg_type(char const *arg){ char * end = NULL; if(!arg || !*arg) return cson_value_null(); else if(('0'>*arg) || ('9'<*arg)){ goto do_string; } else{ /* try numbers... */ long const val = strtol(arg, &end, 10); if(!*end){ return cson_value_new_integer( (cson_int_t)val); } else if( '.' != *end ) { goto do_string; } else { double const val = strtod(arg, &end); if(!*end){ return cson_value_new_double(val); } } } do_string: return cson_value_new_string(arg, strlen(arg)); } int cson_parse_argv_flags( int argc, char const * const * argv, cson_object ** tgt, unsigned int * count ){ cson_object * o = NULL; int rc = 0; int i = 0; if(argc<1 || !argc || !tgt) return cson_rc.ArgError; o = *tgt ? *tgt : cson_new_object(); if(count) *count = 0; for( i = 0; i < argc; ++i ){ char const * arg = argv[i]; char const * key = arg; char const * pos; cson_string * k = NULL; cson_value * v = NULL; if('-' != *arg) continue; while('-'==*key) ++key; if(!*key) continue; pos = key; while( *pos && ('=' != *pos)) ++pos; k = cson_new_string(key, pos-key); if(!k){ rc = cson_rc.AllocError; break; } if(!*pos){ /** --key */ v = cson_value_true(); }else{ /** --key=...*/ assert('=' == *pos); ++pos /*skip '='*/; v = cson_guess_arg_type(pos); } if(0 != (rc=cson_object_set_s(o, k, v))){ cson_free_string(k); cson_value_free(v); break; } else if(count) ++*count; } if(o != *tgt){ if(rc) cson_free_object(o); else *tgt = o; } return rc; } #if defined(__cplusplus) } /*extern "C"*/ #endif #undef MARKER #undef CSON_OBJECT_PROPS_SORT #undef CSON_OBJECT_PROPS_SORT_USE_LENGTH #undef CSON_CAST #undef CSON_INT #undef CSON_DBL #undef CSON_STR #undef CSON_OBJ #undef CSON_ARRAY #undef CSON_VCAST #undef CSON_MALLOC_IMPL #undef CSON_FREE_IMPL #undef CSON_REALLOC_IMPL /* end file ./cson.c */ /* begin file ./cson_lists.h */ /* Auto-generated from cson_list.h. Edit at your own risk! */ unsigned int cson_value_list_reserve( cson_value_list * self, unsigned int n ) { if( !self ) return 0; else if(0 == n) { if(0 == self->alloced) return 0; cson_free(self->list, "cson_value_list_reserve"); self->list = NULL; self->alloced = self->count = 0; return 0; } else if( self->alloced >= n ) { return self->alloced; } else { size_t const sz = sizeof(cson_value *) * n; cson_value * * m = (cson_value **)cson_realloc( self->list, sz, "cson_value_list_reserve" ); if( ! m ) return self->alloced; memset( m + self->alloced, 0, (sizeof(cson_value *)*(n-self->alloced))); self->alloced = n; self->list = m; return n; } } int cson_value_list_append( cson_value_list * self, cson_value * cp ) { if( !self || !cp ) return cson_rc.ArgError; else if( self->alloced > cson_value_list_reserve(self, self->count+1) ) { return cson_rc.AllocError; } else { self->list[self->count++] = cp; return 0; } } int cson_value_list_visit( cson_value_list * self, int (*visitor)(cson_value * obj, void * visitorState ), void * visitorState ) { int rc = cson_rc.ArgError; if( self && visitor ) { unsigned int i = 0; for( rc = 0; (i < self->count) && (0 == rc); ++i ) { cson_value * obj = self->list[i]; if(obj) rc = visitor( obj, visitorState ); } } return rc; } void cson_value_list_clean( cson_value_list * self, void (*cleaner)(cson_value * obj) ) { if( self && cleaner && self->count ) { unsigned int i = 0; for( ; i < self->count; ++i ) { cson_value * obj = self->list[i]; if(obj) cleaner(obj); } } cson_value_list_reserve(self,0); } unsigned int cson_kvp_list_reserve( cson_kvp_list * self, unsigned int n ) { if( !self ) return 0; else if(0 == n) { if(0 == self->alloced) return 0; cson_free(self->list, "cson_kvp_list_reserve"); self->list = NULL; self->alloced = self->count = 0; return 0; } else if( self->alloced >= n ) { return self->alloced; } else { size_t const sz = sizeof(cson_kvp *) * n; cson_kvp * * m = (cson_kvp **)cson_realloc( self->list, sz, "cson_kvp_list_reserve" ); if( ! m ) return self->alloced; memset( m + self->alloced, 0, (sizeof(cson_kvp *)*(n-self->alloced))); self->alloced = n; self->list = m; return n; } } int cson_kvp_list_append( cson_kvp_list * self, cson_kvp * cp ) { if( !self || !cp ) return cson_rc.ArgError; else if( self->alloced > cson_kvp_list_reserve(self, self->count+1) ) { return cson_rc.AllocError; } else { self->list[self->count++] = cp; return 0; } } int cson_kvp_list_visit( cson_kvp_list * self, int (*visitor)(cson_kvp * obj, void * visitorState ), void * visitorState ) { int rc = cson_rc.ArgError; if( self && visitor ) { unsigned int i = 0; for( rc = 0; (i < self->count) && (0 == rc); ++i ) { cson_kvp * obj = self->list[i]; if(obj) rc = visitor( obj, visitorState ); } } return rc; } void cson_kvp_list_clean( cson_kvp_list * self, void (*cleaner)(cson_kvp * obj) ) { if( self && cleaner && self->count ) { unsigned int i = 0; for( ; i < self->count; ++i ) { cson_kvp * obj = self->list[i]; if(obj) cleaner(obj); } } cson_kvp_list_reserve(self,0); } /* end file ./cson_lists.h */ /* begin file ./cson_sqlite3.c */ /** @file cson_sqlite3.c This file contains the implementation code for the cson sqlite3-to-JSON API. License: the same as the cson core library. Author: Stephan Beal (http://wanderinghorse.net/home/stephan) */ #if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */ #include <assert.h> #include <string.h> /* strlen() */ #if 0 #include <stdio.h> #define MARKER if(1) printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); if(1) printf #else #define MARKER if(0) printf #endif #if defined(__cplusplus) extern "C" { #endif cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col ) { if( ! st ) return NULL; else { #if 0 sqlite3_value * val = sqlite3_column_type(st,col); int const vtype = val ? sqlite3_value_type(val) : -1; if( ! val ) return cson_value_null(); #else int const vtype = sqlite3_column_type(st,col); #endif switch( vtype ) { case SQLITE_NULL: return cson_value_null(); case SQLITE_INTEGER: /* FIXME: for large integers fall back to Double instead. */ return cson_value_new_integer( (cson_int_t) sqlite3_column_int64(st, col) ); case SQLITE_FLOAT: return cson_value_new_double( sqlite3_column_double(st, col) ); case SQLITE_BLOB: /* arguably fall through... */ case SQLITE_TEXT: { char const * str = (char const *)sqlite3_column_text(st,col); return cson_value_new_string(str, str ? strlen(str) : 0); } default: return NULL; } } } cson_value * cson_sqlite3_column_names( sqlite3_stmt * st ) { cson_value * aryV = NULL; cson_array * ary = NULL; char const * colName = NULL; int i = 0; int rc = 0; int colCount = 0; assert(st); colCount = sqlite3_column_count(st); if( colCount <= 0 ) return NULL; aryV = cson_value_new_array(); if( ! aryV ) return NULL; ary = cson_value_get_array(aryV); assert(ary); for( i = 0; (0 ==rc) && (i < colCount); ++i ) { colName = sqlite3_column_name( st, i ); if( ! colName ) rc = cson_rc.AllocError; else { rc = cson_array_set( ary, (unsigned int)i, cson_value_new_string(colName, strlen(colName)) ); } } if( 0 == rc ) return aryV; else { cson_value_free(aryV); return NULL; } } cson_value * cson_sqlite3_row_to_object2( sqlite3_stmt * st, cson_array * colNames ) { cson_value * rootV = NULL; cson_object * root = NULL; cson_string * colName = NULL; int i = 0; int rc = 0; cson_value * currentValue = NULL; int const colCount = sqlite3_column_count(st); if( !colCount || (colCount>(int)cson_array_length_get(colNames)) ) { return NULL; } rootV = cson_value_new_object(); if(!rootV) return NULL; root = cson_value_get_object(rootV); for( i = 0; i < colCount; ++i ) { colName = cson_value_get_string( cson_array_get( colNames, i ) ); if( ! colName ) goto error; currentValue = cson_sqlite3_column_to_value(st,i); if( ! currentValue ) currentValue = cson_value_null(); rc = cson_object_set_s( root, colName, currentValue ); if( 0 != rc ) { cson_value_free( currentValue ); goto error; } } goto end; error: cson_value_free( rootV ); rootV = NULL; end: return rootV; } cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st ) { #if 0 cson_value * arV = cson_sqlite3_column_names(st); cson_array * ar = NULL; cson_value * rc = NULL; if(!arV) return NULL; ar = cson_value_get_array(arV); assert( NULL != ar ); rc = cson_sqlite3_row_to_object2(st, ar); cson_value_free(arV); return rc; #else cson_value * rootV = NULL; cson_object * root = NULL; char const * colName = NULL; int i = 0; int rc = 0; cson_value * currentValue = NULL; int const colCount = sqlite3_column_count(st); if( !colCount ) return NULL; rootV = cson_value_new_object(); if(!rootV) return NULL; root = cson_value_get_object(rootV); for( i = 0; i < colCount; ++i ) { colName = sqlite3_column_name( st, i ); if( ! colName ) goto error; currentValue = cson_sqlite3_column_to_value(st,i); if( ! currentValue ) currentValue = cson_value_null(); rc = cson_object_set( root, colName, currentValue ); if( 0 != rc ) { cson_value_free( currentValue ); goto error; } } goto end; error: cson_value_free( rootV ); rootV = NULL; end: return rootV; #endif } cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st ) { cson_value * aryV = NULL; cson_array * ary = NULL; int i = 0; int rc = 0; int const colCount = sqlite3_column_count(st); if( ! colCount ) return NULL; aryV = cson_value_new_array(); if( ! aryV ) return NULL; ary = cson_value_get_array(aryV); rc = cson_array_reserve(ary, (unsigned int) colCount ); if( 0 != rc ) goto error; for( i = 0; i < colCount; ++i ){ cson_value * elem = cson_sqlite3_column_to_value(st,i); if( ! elem ) goto error; rc = cson_array_append(ary,elem); if(0!=rc) { cson_value_free( elem ); goto end; } } goto end; error: cson_value_free(aryV); aryV = NULL; end: return aryV; } /** Internal impl of cson_sqlite3_stmt_to_json() when the 'fat' parameter is non-0. */ static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt ) { #define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; } if( ! tgt || !st ) return cson_rc.ArgError; else { cson_value * rootV = NULL; cson_object * root = NULL; cson_value * colsV = NULL; cson_array * cols = NULL; cson_value * rowsV = NULL; cson_array * rows = NULL; cson_value * objV = NULL; int rc = 0; int const colCount = sqlite3_column_count(st); if( colCount <= 0 ) return cson_rc.ArgError; rootV = cson_value_new_object(); if( ! rootV ) return cson_rc.AllocError; colsV = cson_sqlite3_column_names(st); if( ! colsV ) { cson_value_free( rootV ); RETURN(cson_rc.AllocError); } cols = cson_value_get_array(colsV); assert(NULL != cols); root = cson_value_get_object(rootV); rc = cson_object_set( root, "columns", colsV ); if( rc ) { cson_value_free( colsV ); RETURN(rc); } rowsV = cson_value_new_array(); if( ! rowsV ) RETURN(cson_rc.AllocError); rc = cson_object_set( root, "rows", rowsV ); if( rc ) { cson_value_free( rowsV ); RETURN(rc); } rows = cson_value_get_array(rowsV); assert(rows); while( SQLITE_ROW == sqlite3_step(st) ) { objV = cson_sqlite3_row_to_object2(st, cols); if( ! objV ) RETURN(cson_rc.UnknownError); rc = cson_array_append( rows, objV ); if( rc ) { cson_value_free( objV ); RETURN(rc); } } *tgt = rootV; return 0; } #undef RETURN } /** Internal impl of cson_sqlite3_stmt_to_json() when the 'fat' parameter is 0. */ static int cson_sqlite3_stmt_to_json_slim( sqlite3_stmt * st, cson_value ** tgt ) { #define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; } if( ! tgt || !st ) return cson_rc.ArgError; else { cson_value * rootV = NULL; cson_object * root = NULL; cson_value * aryV = NULL; cson_value * rowsV = NULL; cson_array * rows = NULL; int rc = 0; int const colCount = sqlite3_column_count(st); if( colCount <= 0 ) return cson_rc.ArgError; rootV = cson_value_new_object(); if( ! rootV ) return cson_rc.AllocError; aryV = cson_sqlite3_column_names(st); if( ! aryV ) { cson_value_free( rootV ); RETURN(cson_rc.AllocError); } root = cson_value_get_object(rootV); rc = cson_object_set( root, "columns", aryV ); if( rc ) { cson_value_free( aryV ); RETURN(rc); } aryV = NULL; rowsV = cson_value_new_array(); if( ! rowsV ) RETURN(cson_rc.AllocError); rc = cson_object_set( root, "rows", rowsV ); if( 0 != rc ) { cson_value_free( rowsV ); RETURN(rc); } rows = cson_value_get_array(rowsV); assert(rows); while( SQLITE_ROW == sqlite3_step(st) ) { aryV = cson_sqlite3_row_to_array(st); if( ! aryV ) RETURN(cson_rc.UnknownError); rc = cson_array_append( rows, aryV ); if( 0 != rc ) { cson_value_free( aryV ); RETURN(rc); } } *tgt = rootV; return 0; } #undef RETURN } int cson_sqlite3_stmt_to_json( sqlite3_stmt * st, cson_value ** tgt, char fat ) { return fat ? cson_sqlite3_stmt_to_json_fat(st,tgt) : cson_sqlite3_stmt_to_json_slim(st,tgt) ; } int cson_sqlite3_sql_to_json( sqlite3 * db, cson_value ** tgt, char const * sql, char fat ) { if( !db || !tgt || !sql || !*sql ) return cson_rc.ArgError; else { sqlite3_stmt * st = NULL; int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL ); if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */; rc = cson_sqlite3_stmt_to_json( st, tgt, fat ); sqlite3_finalize( st ); return rc; } } int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v ) { int rc = 0; char convertErr = 0; if(!st) return cson_rc.ArgError; else if( ndx < 1 ) { rc = cson_rc.RangeError; } else if( cson_value_is_array(v) ){ cson_array * ar = cson_value_get_array(v); unsigned int len = cson_array_length_get(ar); unsigned int i; assert(NULL != ar); for( i = 0; !rc && (i < len); ++i ){ rc = cson_sqlite3_bind_value( st, (int)i+ndx, cson_array_get(ar, i)); } } else if(!v || cson_value_is_null(v)){ rc = sqlite3_bind_null(st,ndx); convertErr = 1; } else if( cson_value_is_double(v) ){ rc = sqlite3_bind_double( st, ndx, cson_value_get_double(v) ); convertErr = 1; } else if( cson_value_is_bool(v) ){ rc = sqlite3_bind_int( st, ndx, cson_value_get_bool(v) ? 1 : 0 ); convertErr = 1; } else if( cson_value_is_integer(v) ){ rc = sqlite3_bind_int64( st, ndx, cson_value_get_integer(v) ); convertErr = 1; } else if( cson_value_is_string(v) ){ cson_string const * s = cson_value_get_string(v); rc = sqlite3_bind_text( st, ndx, cson_string_cstr(s), cson_string_length_bytes(s), SQLITE_TRANSIENT); convertErr = 1; } else { rc = cson_rc.TypeError; } if(convertErr && rc) switch(rc){ case SQLITE_TOOBIG: case SQLITE_RANGE: rc = cson_rc.RangeError; break; case SQLITE_NOMEM: rc = cson_rc.AllocError; break; case SQLITE_IOERR: rc = cson_rc.IOError; break; default: rc = cson_rc.UnknownError; break; }; return rc; } #if defined(__cplusplus) } /*extern "C"*/ #endif #undef MARKER #endif /* CSON_ENABLE_SQLITE3 */ /* end file ./cson_sqlite3.c */ #endif /* FOSSIL_ENABLE_JSON */ |
Added extsrc/cson_amalgamation.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #ifdef FOSSIL_ENABLE_JSON #ifndef CSON_FOSSIL_MODE #define CSON_FOSSIL_MODE #endif /* auto-generated! Do not edit! */ /* begin file include/wh/cson/cson.h */ #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 /*#include <stdint.h> C99: fixed-size int types. */ #include <stdio.h> /* FILE decl */ /** @page page_cson cson JSON API cson (pronounced "season") is an object-oriented C API for generating and consuming JSON (http://www.json.org) data. Its main claim to fame is that it can parse JSON from, and output it to, damned near anywhere. The i/o routines use a callback function to fetch/emit JSON data, allowing clients to easily plug in their own implementations. Implementations are provided for string- and FILE-based i/o. Project home page: http://fossil.wanderinghorse.net/repos/cson Author: Stephan Beal (http://www.wanderinghorse.net/home/stephan/) License: Dual Public Domain/MIT The full license text is at the bottom of the main header file (cson.h). Examples of how to use the library are scattered throughout the API documentation, in the test.c file in the source repo, and in the wiki on the project's home page. */ #if defined(__cplusplus) extern "C" { #endif #if defined(_WIN32) || defined(_WIN64) # define CSON_ENABLE_UNIX 0 #else # define CSON_ENABLE_UNIX 1 #endif /** @typedef some_long_int_type cson_int_t Typedef for JSON-like integer types. This is (long long) where feasible, otherwise (long). */ #ifdef _WIN32 typedef __int64 cson_int_t; #define CSON_INT_T_SFMT "I64d" #define CSON_INT_T_PFMT "I64d" #elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1) typedef long long cson_int_t; #define CSON_INT_T_SFMT "lld" #define CSON_INT_T_PFMT "lld" #else typedef long cson_int_t; #define CSON_INT_T_SFMT "ld" #define CSON_INT_T_PFMT "ld" #endif /** @typedef double_or_long_double cson_double_t This is the type of double value used by the library. It is only lightly tested with long double, and when using long double the memory requirements for such values goes up. Note that by default cson uses C-API defaults for numeric precision. To use a custom precision throughout the library, one needs to define the macros CSON_DOUBLE_T_SFMT and/or CSON_DOUBLE_T_PFMT macros to include their desired precision, and must build BOTH cson AND the client using these same values. For example: @code #define CSON_DOUBLE_T_PFMT ".8Lf" // for Modified Julian Day values #define HAVE_LONG_DOUBLE @endcode (Only CSON_DOUBLE_T_PFTM should be needed for most purposes.) */ #if defined(HAVE_LONG_DOUBLE) typedef long double cson_double_t; # ifndef CSON_DOUBLE_T_SFMT # define CSON_DOUBLE_T_SFMT "Lf" # endif # ifndef CSON_DOUBLE_T_PFMT # define CSON_DOUBLE_T_PFMT "Lf" # endif #else typedef double cson_double_t; # ifndef CSON_DOUBLE_T_SFMT # define CSON_DOUBLE_T_SFMT "f" # endif # ifndef CSON_DOUBLE_T_PFMT # define CSON_DOUBLE_T_PFMT "f" # endif #endif /** @def CSON_VOID_PTR_IS_BIG ONLY define this to a true value if you know that (sizeof(cson_int_t) <= sizeof(void*)) If that is the case, cson does not need to dynamically allocate integers. However, enabling this may cause compilation warnings in 32-bit builds even though the code being warned about cannot ever be called. To get around such warnings, when building on a 64-bit environment you can define this to 1 to get "big" integer support. HOWEVER, all clients must also use the same value for this macro. If i knew a halfway reliable way to determine this automatically at preprocessor-time, i would automate this. We might be able to do halfway reliably by looking for a large INT_MAX value? */ #if !defined(CSON_VOID_PTR_IS_BIG) /* Largely taken from http://predef.sourceforge.net/prearch.html See also: http://poshlib.hookatooka.com/poshlib/trac.cgi/browser/posh.h */ # if defined(_WIN64) || defined(__LP64__)/*gcc*/ \ || defined(_M_X64) || defined(__amd64__) || defined(__amd64) \ || defined(__x86_64__) || defined(__x86_64) \ || defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64__) \ || defined(_M_IA64) \ || defined(__sparc_v9__) || defined(__sparcv9) || defined(_ADDR64) \ || defined(__64BIT__) # define CSON_VOID_PTR_IS_BIG 1 # else # define CSON_VOID_PTR_IS_BIG 0 # endif #endif /** @def CSON_INT_T_SFMT scanf()-compatible format token for cson_int_t. */ /** @def CSON_INT_T_PFMT printf()-compatible format token for cson_int_t. */ /** @def CSON_DOUBLE_T_SFMT scanf()-compatible format token for cson_double_t. */ /** @def CSON_DOUBLE_T_PFMT printf()-compatible format token for cson_double_t. */ /** Type IDs corresponding to JavaScript/JSON types. These are only in the public API to allow O(1) client-side dispatching based on cson_value types. */ enum cson_type_id { /** The special "undefined" value constant. Its value must be 0 for internal reasons. */ CSON_TYPE_UNDEF = 0, /** The special "null" value constant. */ CSON_TYPE_NULL = 1, /** The bool value type. */ CSON_TYPE_BOOL = 2, /** The integer value type, represented in this library by cson_int_t. */ CSON_TYPE_INTEGER = 3, /** The double value type, represented in this library by cson_double_t. */ CSON_TYPE_DOUBLE = 4, /** The immutable string type. This library stores strings as immutable UTF8. */ CSON_TYPE_STRING = 5, /** The "Array" type. */ CSON_TYPE_ARRAY = 6, /** The "Object" type. */ CSON_TYPE_OBJECT = 7 }; /** Convenience typedef. */ typedef enum cson_type_id cson_type_id; /** Convenience typedef. */ typedef struct cson_value cson_value; /** @struct cson_value The core value type of this API. It is opaque to clients, and only the cson public API should be used for setting or inspecting their values. This class is opaque because stack-based usage can easily cause leaks if one does not intimately understand the underlying internal memory management (which sometimes changes). It is (as of 20110323) legal to insert a given value instance into multiple containers (they will share ownership using reference counting) as long as those insertions do not cause cycles. However, be very aware that such value re-use uses a reference to the original copy, meaning that if its value is changed once, it is changed everywhere. Also beware that multi-threaded write operations on such references leads to undefined behaviour. PLEASE read the ACHTUNGEN below... ACHTUNG #1: cson_values MUST NOT form cycles (e.g. via object or array entries). Not abiding th Holy Law Of No Cycles will lead to double-frees and the like (i.e. undefined behaviour, likely crashes due to infinite recursion or stepping on invalid (freed) pointers). ACHTUNG #2: ALL cson_values returned as non-const cson_value pointers from any public functions in the cson API are to be treated as if they are heap-allocated, and MUST be freed by client by doing ONE of: - Passing it to cson_value_free(). - Adding it to an Object or Array, in which case the object/array takes over ownership. As of 20110323, a value may be inserted into a single container multiple times, or into multiple containers, in which case they all share ownership (via reference counting) of the original value (meaning any changes to it are visible in all references to it). Each call to cson_value_new_xxx() MUST eventually be followed up by one of those options. Some cson_value_new_XXX() implementations do not actually allocate memory, but this is an internal implementation detail. Client code MUST NOT rely on this behaviour and MUST treat each object returned by such a function as if it was a freshly-allocated copy (even if their pointer addresses are the same). ACHTUNG #3: Note that ACHTUNG #2 tells us that we must always free (or transfer ownership of) all pointers returned bycson_value_new_xxx(), but that two calls to (e.g.) cson_value_new_bool(1) will (or might) return the same address. The client must not rely on the "non-allocation" policy of such special cases, and must pass each returned value to cson_value_free(), even if two of them have the same address. Some special values (e.g. null, true, false, integer 0, double 0.0, and empty strings) use shared copies and in other places reference counting is used internally to figure out when it is safe to destroy an object. @see cson_value_new_array() @see cson_value_new_object() @see cson_value_new_string() @see cson_value_new_integer() @see cson_value_new_double() @see cson_value_new_bool() @see cson_value_true() @see cson_value_false() @see cson_value_null() @see cson_value_free() @see cson_value_type_id() */ /** @var cson_rc This object defines the error codes used by cson. Library routines which return int values almost always return a value from this structure. None of the members in this struct have published values except for the OK member, which has the value 0. All other values might be incidentally defined where clients can see them, but the numbers might change from release to release, so clients should only use the symbolic names. Client code is expected to access these values via the shared cson_rc object, and use them as demonstrated here: @code int rc = cson_some_func(...); if( 0 == rc ) {...success...} else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... } else if( cson_rc.AllocError == rc ) { ... allocation error ... } ... @endcode The entries named Parse_XXX are generally only returned by cson_parse() and friends. */ /** @struct cson_rc_ See \ref cson_rc for details. */ static const struct cson_rc_ { /** The generic success value. Guaranteed to be 0. */ const int OK; /** Signifies an error in one or more arguments (e.g. NULL where it is not allowed). */ const int ArgError; /** Signifies that some argument is not in a valid range. */ const int RangeError; /** Signifies that some argument is not of the correct logical cson type. */ const int TypeError; /** Signifies an input/ouput error. */ const int IOError; /** Signifies an out-of-memory error. */ const int AllocError; /** Signifies that the called code is "NYI" (Not Yet Implemented). */ const int NYIError; /** Signifies that an internal error was triggered. If it happens, please report this as a bug! */ const int InternalError; /** Signifies that the called operation is not supported in the current environment. e.g. missing support from 3rd-party or platform-specific code. */ const int UnsupportedError; /** Signifies that the request resource could not be found. */ const int NotFoundError; /** Signifies an unknown error, possibly because an underlying 3rd-party API produced an error and we have no other reasonable error code to convert it to. */ const int UnknownError; /** Signifies that the parser found an unexpected character. */ const int Parse_INVALID_CHAR; /** Signifies that the parser found an invalid keyword (possibly an unquoted string). */ const int Parse_INVALID_KEYWORD; /** Signifies that the parser found an invalid escape sequence. */ const int Parse_INVALID_ESCAPE_SEQUENCE; /** Signifies that the parser found an invalid Unicode character sequence. */ const int Parse_INVALID_UNICODE_SEQUENCE; /** Signifies that the parser found an invalid numeric token. */ const int Parse_INVALID_NUMBER; /** Signifies that the parser reached its maximum defined parsing depth before finishing the input. */ const int Parse_NESTING_DEPTH_REACHED; /** Signifies that the parser found an unclosed object or array. */ const int Parse_UNBALANCED_COLLECTION; /** Signifies that the parser found an key in an unexpected place. */ const int Parse_EXPECTED_KEY; /** Signifies that the parser expected to find a colon but found none (e.g. between keys and values in an object). */ const int Parse_EXPECTED_COLON; } cson_rc = { 0/*OK*/, 1/*ArgError*/, 2/*RangeError*/, 3/*TypeError*/, 4/*IOError*/, 5/*AllocError*/, 6/*NYIError*/, 7/*InternalError*/, 8/*UnsupportedError*/, 9/*NotFoundError*/, 10/*UnknownError*/, 11/*Parse_INVALID_CHAR*/, 12/*Parse_INVALID_KEYWORD*/, 13/*Parse_INVALID_ESCAPE_SEQUENCE*/, 14/*Parse_INVALID_UNICODE_SEQUENCE*/, 15/*Parse_INVALID_NUMBER*/, 16/*Parse_NESTING_DEPTH_REACHED*/, 17/*Parse_UNBALANCED_COLLECTION*/, 18/*Parse_EXPECTED_KEY*/, 19/*Parse_EXPECTED_COLON*/ }; /** Returns the string form of the cson_rc code corresponding to rc, or some unspecified, non-NULL string if it is an unknown code. The returned bytes are static and do not changing during the lifetime of the application. */ char const * cson_rc_string(int rc); /** @struct cson_parse_opt Client-configurable options for the cson_parse() family of functions. */ struct cson_parse_opt { /** Maximum object/array depth to traverse. */ unsigned short maxDepth; /** Whether or not to allow C-style comments. Do not rely on this option being available. If the underlying parser is replaced, this option might no longer be supported. */ char allowComments; }; typedef struct cson_parse_opt cson_parse_opt; /** Empty-initialized cson_parse_opt object. */ #define cson_parse_opt_empty_m { 25/*maxDepth*/, 0/*allowComments*/} /** A class for holding JSON parser information. It is primarily intended for finding the position of a parse error. */ struct cson_parse_info { /** 1-based line number. */ unsigned int line; /** 0-based column number. */ unsigned int col; /** Length, in bytes. */ unsigned int length; /** Error code of the parse run (0 for no error). */ int errorCode; /** The total number of object keys successfully processed by the parser. */ unsigned int totalKeyCount; /** The total number of object/array values successfully processed by the parser, including the root node. */ unsigned int totalValueCount; }; typedef struct cson_parse_info cson_parse_info; /** Empty-initialized cson_parse_info object. */ #define cson_parse_info_empty_m {1/*line*/,\ 0/*col*/, \ 0/*length*/, \ 0/*errorCode*/, \ 0/*totalKeyCount*/, \ 0/*totalValueCount*/ \ } /** Empty-initialized cson_parse_info object. */ extern const cson_parse_info cson_parse_info_empty; /** Empty-initialized cson_parse_opt object. */ extern const cson_parse_opt cson_parse_opt_empty; /** Client-configurable options for the cson_output() family of functions. */ struct cson_output_opt { /** Specifies how to indent (or not) output. The values are: (0) == no extra indentation. (1) == 1 TAB character for each level. (>1) == that number of SPACES for each level. */ unsigned char indentation; /** Maximum object/array depth to traverse. Traversing deeply can be indicative of cycles in the object/array tree, and this value is used to figure out when to abort the traversal. */ unsigned short maxDepth; /** If true, a newline will be added to generated output, else not. */ char addNewline; /** If true, a space will be added after the colon operator in objects' key/value pairs. */ char addSpaceAfterColon; /** If set to 1 then objects/arrays containing only a single value will not indent an extra level for that value (but will indent on subsequent levels if that value contains multiple values). */ char indentSingleMemberValues; /** The JSON format allows, but does not require, JSON generators to backslash-escape forward slashes. This option enables/disables that feature. According to JSON's inventor, Douglas Crockford: <quote> It is allowed, not required. It is allowed so that JSON can be safely embedded in HTML, which can freak out when seeing strings containing "</". JSON tolerates "<\/" for this reason. </quote> (from an email on 2011-04-08) The default value is 0 (because it's just damned ugly). */ char escapeForwardSlashes; }; typedef struct cson_output_opt cson_output_opt; /** Empty-initialized cson_output_opt object. */ #define cson_output_opt_empty_m { 0/*indentation*/,\ 25/*maxDepth*/, \ 0/*addNewline*/, \ 0/*addSpaceAfterColon*/, \ 0/*indentSingleMemberValues*/, \ 0/*escapeForwardSlashes*/ \ } /** Empty-initialized cson_output_opt object. */ extern const cson_output_opt cson_output_opt_empty; /** Typedef for functions which act as an input source for the cson JSON parser. The arguments are: - state: implementation-specific state needed by the function. - n: when called, *n will be the number of bytes the function should read and copy to dest. The function MUST NOT copy more than *n bytes to dest. Before returning, *n must be set to the number of bytes actually copied to dest. If that number is smaller than the original *n value, the input is assumed to be completed (thus this is not useful with non-blocking readers). - dest: the destination memory to copy the data do. Must return 0 on success, non-0 on error (preferably a value from cson_rc). The parser allows this routine to return a partial character from a UTF multi-byte character. The input routine does not need to concern itself with character boundaries. */ typedef int (*cson_data_source_f)( void * state, void * dest, unsigned int * n ); /** Typedef for functions which act as an output destination for generated JSON. The arguments are: - state: implementation-specific state needed by the function. - n: the length, in bytes, of src. - src: the source bytes which the output function should consume. The src pointer will be invalidated shortly after this function returns, so the implementation must copy or ignore the data, but not hold a copy of the src pointer. Must return 0 on success, non-0 on error (preferably a value from cson_rc). These functions are called relatively often during the JSON-output process, and should try to be fast. */ typedef int (*cson_data_dest_f)( void * state, void const * src, unsigned int n ); /** Reads JSON-formatted string data (in ASCII, UTF8, or UTF16), using the src function to fetch all input. This function fetches each input character from the source function, which is calls like src(srcState, buffer, bufferSize), and processes them. If anything is not JSON-kosher then this function fails and returns one of the non-0 cson_rc codes. This function is only intended to read root nodes of a JSON tree, either a single object or a single array, containing any number of child elements. On success, *tgt is assigned the value of the root node of the JSON input, and the caller takes over ownership of that memory. On error, *tgt is not modified and the caller need not do any special cleanup, except possibly for the input source. The opt argument may point to an initialized cson_parse_opt object which contains any settings the caller wants. If it is NULL then default settings (the values defined in cson_parse_opt_empty) are used. The info argument may be NULL. If it is not NULL then the parser populates it with information which is useful in error reporting. Namely, it contains the line/column of parse errors. The srcState argument is ignored by this function but is passed on to src, so any output-destination-specific state can be stored there and accessed via the src callback. Non-parse error conditions include: - (!tgt) or !src: cson_rc.ArgError - cson_rc.AllocError can happen at any time during the input phase Here's a complete example of using a custom input source: @code // Internal type to hold state for a JSON input string. typedef struct { char const * str; // start of input string char const * pos; // current internal cursor position char const * end; // logical EOF (one-past-the-end) } StringSource; // cson_data_source_f() impl which uses StringSource. static int cson_data_source_StringSource( void * state, void * dest, unsigned int * n ) { StringSource * ss = (StringSource*) state; unsigned int i; unsigned char * tgt = (unsigned char *)dest; if( ! ss || ! n || !dest ) return cson_rc.ArgError; else if( !*n ) return cson_rc.RangeError; for( i = 0; (i < *n) && (ss->pos < ss->end); ++i, ++ss->pos, ++tgt ) { *tgt = *ss->pos; } *n = i; return 0; } ... // Now use StringSource together with cson_parse() StringSource ss; cson_value * root = NULL; char const * json = "{\"k1\":123}"; ss.str = ss.pos = json; ss.end = json + strlen(json); int rc = cson_parse( &root, cson_data_source_StringSource, &ss, NULL, NULL ); @endcode It is recommended that clients wrap such utility code into type-safe wrapper functions which also initialize the internal state object and check the user-provided parameters for legality before passing them on to cson_parse(). For examples of this, see cson_parse_FILE() or cson_parse_string(). TODOs: - Buffer the input in larger chunks. We currently read byte-by-byte, but i'm too tired to write/test the looping code for the buffering. @see cson_parse_FILE() @see cson_parse_string() */ int cson_parse( cson_value ** tgt, cson_data_source_f src, void * srcState, cson_parse_opt const * opt, cson_parse_info * info ); /** A cson_data_source_f() implementation which requires the state argument to be a readable (FILE*) handle. */ int cson_data_source_FILE( void * state, void * dest, unsigned int * n ); /** Equivalent to cson_parse( tgt, cson_data_source_FILE, src, opt ). @see cson_parse_filename() */ int cson_parse_FILE( cson_value ** tgt, FILE * src, cson_parse_opt const * opt, cson_parse_info * info ); /** Convenience wrapper around cson_parse_FILE() which opens the given filename. Returns cson_rc.IOError if the file cannot be opened. @see cson_parse_FILE() */ int cson_parse_filename( cson_value ** tgt, char const * src, cson_parse_opt const * opt, cson_parse_info * info ); /** Uses an internal helper class to pass src through cson_parse(). See that function for the return value and argument semantics. src must be a string containing JSON code, at least len bytes long, and the parser will attempt to parse exactly len bytes from src. If len is less than 2 (the minimum length of a legal top-node JSON object) then cson_rc.RangeError is returned. */ int cson_parse_string( cson_value ** tgt, char const * src, unsigned int len, cson_parse_opt const * opt, cson_parse_info * info ); /** Outputs the given value as a JSON-formatted string, sending all output to the given callback function. It is intended for top-level objects or arrays, but can be used with any cson_value. If opt is NULL then default options (the values defined in cson_output_opt_empty) are used. If opt->maxDepth is exceeded while traversing the value tree, cson_rc.RangeError is returned. The destState parameter is ignored by this function and is passed on to the dest function. Returns 0 on success. On error, any amount of output might have been generated before the error was triggered. Example: @code int rc = cson_output( myValue, cson_data_dest_FILE, stdout, NULL ); // basically equivalent to: cson_output_FILE( myValue, stdout, NULL ); // but note that cson_output_FILE() actually uses different defaults // for the output options. @endcode */ int cson_output( cson_value const * src, cson_data_dest_f dest, void * destState, cson_output_opt const * opt ); /** A cson_data_dest_f() implementation which requires the state argument to be a writable (FILE*) handle. */ int cson_data_dest_FILE( void * state, void const * src, unsigned int n ); /** Almost equivalent to cson_output( src, cson_data_dest_FILE, dest, opt ), with one minor difference: if opt is NULL then the default options always include the addNewline option, since that is normally desired for FILE output. @see cson_output_filename() */ int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * opt ); /** Convenience wrapper around cson_output_FILE() which writes to the given filename, destroying any existing contents. Returns cson_rc.IOError if the file cannot be opened. @see cson_output_FILE() */ int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt ); /** Returns the virtual type of v, or CSON_TYPE_UNDEF if !v. */ cson_type_id cson_value_type_id( cson_value const * v ); /** Returns true if v is null, v->api is NULL, or v holds the special undefined value. */ char cson_value_is_undef( cson_value const * v ); /** Returns true if v contains a null value. */ char cson_value_is_null( cson_value const * v ); /** Returns true if v contains a bool value. */ char cson_value_is_bool( cson_value const * v ); /** Returns true if v contains an integer value. */ char cson_value_is_integer( cson_value const * v ); /** Returns true if v contains a double value. */ char cson_value_is_double( cson_value const * v ); /** Returns true if v contains a number (double, integer) value. */ char cson_value_is_number( cson_value const * v ); /** Returns true if v contains a string value. */ char cson_value_is_string( cson_value const * v ); /** Returns true if v contains an array value. */ char cson_value_is_array( cson_value const * v ); /** Returns true if v contains an object value. */ char cson_value_is_object( cson_value const * v ); /** @struct cson_object cson_object is an opaque handle to an Object value. They are used like: @code cson_object * obj = cson_value_get_object(myValue); ... @endcode They can be created like: @code cson_value * objV = cson_value_new_object(); cson_object * obj = cson_value_get_object(objV); // obj is owned by objV and objV must eventually be freed // using cson_value_free() or added to a container // object/array (which transfers ownership to that container). @endcode @see cson_value_new_object() @see cson_value_get_object() @see cson_value_free() */ typedef struct cson_object cson_object; /** @struct cson_array cson_array is an opaque handle to an Array value. They are used like: @code cson_array * obj = cson_value_get_array(myValue); ... @endcode They can be created like: @code cson_value * arV = cson_value_new_array(); cson_array * ar = cson_value_get_array(arV); // ar is owned by arV and arV must eventually be freed // using cson_value_free() or added to a container // object/array (which transfers ownership to that container). @endcode @see cson_value_new_array() @see cson_value_get_array() @see cson_value_free() */ typedef struct cson_array cson_array; /** @struct cson_string cson-internal string type, opaque to client code. Strings in cson are immutable and allocated only by library internals, never directly by client code. The actual string bytes are to be allocated together in the same memory chunk as the cson_string object, which saves us 1 malloc() and 1 pointer member in this type (because we no longer have a direct pointer to the memory). Potential TODOs: @see cson_string_cstr() */ typedef struct cson_string cson_string; /** Converts the given value to a boolean, using JavaScript semantics depending on the concrete type of val: undef or null: false boolean: same integer, double: 0 or 0.0 == false, else true object, array: true string: length-0 string is false, else true. Returns 0 on success and assigns *v (if v is not NULL) to either 0 or 1. On error (val is NULL) then v is not modified. */ int cson_value_fetch_bool( cson_value const * val, char * v ); /** Similar to cson_value_fetch_bool(), but fetches an integer value. The conversion, if any, depends on the concrete type of val: NULL, null, undefined: *v is set to 0 and 0 is returned. string, object, array: *v is set to 0 and cson_rc.TypeError is returned. The error may normally be safely ignored, but it is provided for those wanted to know whether a direct conversion was possible. integer: *v is set to the int value and 0 is returned. double: *v is set to the value truncated to int and 0 is returned. */ int cson_value_fetch_integer( cson_value const * val, cson_int_t * v ); /** The same conversions and return values as cson_value_fetch_integer(), except that the roles of int/double are swapped. */ int cson_value_fetch_double( cson_value const * val, cson_double_t * v ); /** If cson_value_is_string(val) then this function assigns *str to the contents of the string. str may be NULL, in which case this function functions like cson_value_is_string() but returns 0 on success. Returns 0 if val is-a string, else non-0, in which case *str is not modified. The bytes are owned by the given value and may be invalidated in any of the following ways: - The value is cleaned up or freed. - An array or object containing the value peforms a re-allocation (it shrinks or grows). And thus the bytes should be consumed before any further operations on val or any container which holds it. Note that this routine does not convert non-String values to their string representations. (Adding that ability would add more overhead to every cson_value instance.) */ int cson_value_fetch_string( cson_value const * val, cson_string ** str ); /** If cson_value_is_object(val) then this function assigns *obj to the underlying object value and returns 0, otherwise non-0 is returned and *obj is not modified. obj may be NULL, in which case this function works like cson_value_is_object() but with inverse return value semantics (0==success) (and it's a few CPU cycles slower). The *obj pointer is owned by val, and will be invalidated when val is cleaned up. Achtung: for best results, ALWAYS pass a pointer to NULL as the second argument, e.g.: @code cson_object * obj = NULL; int rc = cson_value_fetch_object( val, &obj ); // Or, more simply: obj = cson_value_get_object( val ); @endcode @see cson_value_get_object() */ int cson_value_fetch_object( cson_value const * val, cson_object ** obj ); /** Identical to cson_value_fetch_object(), but works on array values. @see cson_value_get_array() */ int cson_value_fetch_array( cson_value const * val, cson_array ** tgt ); /** Simplified form of cson_value_fetch_bool(). Returns 0 if val is NULL. */ char cson_value_get_bool( cson_value const * val ); /** Simplified form of cson_value_fetch_integer(). Returns 0 if val is NULL. */ cson_int_t cson_value_get_integer( cson_value const * val ); /** Simplified form of cson_value_fetch_double(). Returns 0.0 if val is NULL. */ cson_double_t cson_value_get_double( cson_value const * val ); /** Simplified form of cson_value_fetch_string(). Returns NULL if val is-not-a string value. */ cson_string * cson_value_get_string( cson_value const * val ); /** Returns a pointer to the NULL-terminated string bytes of str. The bytes are owned by string and will be invalided when it is cleaned up. If str is NULL then NULL is returned. If the string has a length of 0 then "" is returned. @see cson_string_length_bytes() @see cson_value_get_string() */ char const * cson_string_cstr( cson_string const * str ); /** Convenience function which returns the string bytes of the given value if it is-a string, otherwise it returns NULL. Note that this does no conversion of non-string types to strings. Equivalent to cson_string_cstr(cson_value_get_string(val)). */ char const * cson_value_get_cstr( cson_value const * val ); /** Equivalent to cson_string_cmp_cstr_n(lhs, cson_string_cstr(rhs), cson_string_length_bytes(rhs)). */ int cson_string_cmp( cson_string const * lhs, cson_string const * rhs ); /** Compares lhs to rhs using memcmp()/strcmp() semantics. Generically speaking it returns a negative number if lhs is less-than rhs, 0 if they are equivalent, or a positive number if lhs is greater-than rhs. It has the following rules for equivalence: - The maximum number of bytes compared is the lesser of rhsLen and the length of lhs. If the strings do not match, but compare equal up to the just-described comparison length, the shorter string is considered to be less-than the longer one. - If lhs and rhs are both NULL, or both have a length of 0 then they will compare equal. - If lhs is null/length-0 but rhs is not then lhs is considered to be less-than rhs. - If rhs is null/length-0 but lhs is not then rhs is considered to be less-than rhs. - i have no clue if the results are exactly correct for UTF strings. */ int cson_string_cmp_cstr_n( cson_string const * lhs, char const * rhs, unsigned int rhsLen ); /** Equivalent to cson_string_cmp_cstr_n( lhs, rhs, (rhs&&*rhs)?strlen(rhs):0 ). */ int cson_string_cmp_cstr( cson_string const * lhs, char const * rhs ); /** Returns the length, in bytes, of str, or 0 if str is NULL. This is an O(1) operation. TODO: add cson_string_length_chars() (is O(N) unless we add another member to store the char length). @see cson_string_cstr() */ unsigned int cson_string_length_bytes( cson_string const * str ); /** Returns the number of UTF8 characters in str. This value will be at most as long as cson_string_length_bytes() for the same string, and less if it has multi-byte characters. Returns 0 if str is NULL. */ unsigned int cson_string_length_utf8( cson_string const * str ); /** Like cson_value_get_string(), but returns a copy of the underying string bytes, which the caller owns and must eventually free using free(). */ char * cson_value_get_string_copy( cson_value const * val ); /** Simplified form of cson_value_fetch_object(). Returns NULL if val is-not-a object value. */ cson_object * cson_value_get_object( cson_value const * val ); /** Simplified form of cson_value_fetch_array(). Returns NULL if val is-not-a array value. */ cson_array * cson_value_get_array( cson_value const * val ); /** Const-correct form of cson_value_get_array(). */ cson_array const * cson_value_get_array_c( cson_value const * val ); /** If ar is-a array and is at least (pos+1) entries long then *v (if v is not NULL) is assigned to the value at that position (which may be NULL). Ownership of the *v return value is unchanged by this call. (The containing array may share ownership of the value with other containers.) If pos is out of range, non-0 is returned and *v is not modified. If v is NULL then this function returns 0 if pos is in bounds, but does not otherwise return a value to the caller. */ int cson_array_value_fetch( cson_array const * ar, unsigned int pos, cson_value ** v ); /** Simplified form of cson_array_value_fetch() which returns NULL if ar is NULL, pos is out of bounds or if ar has no element at that position. */ cson_value * cson_array_get( cson_array const * ar, unsigned int pos ); /** Ensures that ar has allocated space for at least the given number of entries. This never shrinks the array and never changes its logical size, but may pre-allocate space in the array for storing new (as-yet-unassigned) values. Returns 0 on success, or non-zero on error: - If ar is NULL: cson_rc.ArgError - If allocation fails: cson_rc.AllocError */ int cson_array_reserve( cson_array * ar, unsigned int size ); /** If ar is not NULL, sets *v (if v is not NULL) to the length of the array and returns 0. Returns cson_rc.ArgError if ar is NULL. */ int cson_array_length_fetch( cson_array const * ar, unsigned int * v ); /** Simplified form of cson_array_length_fetch() which returns 0 if ar is NULL. */ unsigned int cson_array_length_get( cson_array const * ar ); /** Sets the given index of the given array to the given value. If ar already has an item at that index then it is cleaned up and freed before inserting the new item. ar is expanded, if needed, to be able to hold at least (ndx+1) items, and any new entries created by that expansion are empty (NULL values). On success, 0 is returned and ownership of v is transfered to ar. On error ownership of v is NOT modified, and the caller may still need to clean it up. For example, the following code will introduce a leak if this function fails: @code cson_array_append( myArray, cson_value_new_integer(42) ); @endcode Because the value created by cson_value_new_integer() has no owner and is not cleaned up. The "more correct" way to do this is: @code cson_value * v = cson_value_new_integer(42); int rc = cson_array_append( myArray, v ); if( 0 != rc ) { cson_value_free( v ); ... handle error ... } @endcode */ int cson_array_set( cson_array * ar, unsigned int ndx, cson_value * v ); /** Appends the given value to the given array, transfering ownership of v to ar. On error, ownership of v is not modified. Ownership of ar is never changed by this function. This is functionally equivalent to cson_array_set(ar,cson_array_length_get(ar),v), but this implementation has slightly different array-preallocation policy (it grows more eagerly). Returns 0 on success, non-zero on error. Error cases include: - ar or v are NULL: cson_rc.ArgError - Array cannot be expanded to hold enough elements: cson_rc.AllocError. - Appending would cause a numeric overlow in the array's size: cson_rc.RangeError. (However, you'll get an AllocError long before that happens!) On error ownership of v is NOT modified, and the caller may still need to clean it up. See cson_array_set() for the details. */ int cson_array_append( cson_array * ar, cson_value * v ); /** Creates a new cson_value from the given boolean value. Ownership of the new value is passed to the caller, who must eventually either free the value using cson_value_free() or inserting it into a container (array or object), which transfers ownership to the container. See the cson_value class documentation for more details. Semantically speaking this function Returns NULL on allocation error, but the implementation never actually allocates for this case. Nonetheless, it must be treated as if it were an allocated value. */ cson_value * cson_value_new_bool( char v ); /** Alias for cson_value_new_bool(v). */ cson_value * cson_new_bool(char v); /** Returns the special JSON "null" value. When outputing JSON, its string representation is "null" (without the quotes). See cson_value_new_bool() for notes regarding the returned value's memory. */ cson_value * cson_value_null( void ); /** Equivalent to cson_value_new_bool(1). */ cson_value * cson_value_true( void ); /** Equivalent to cson_value_new_bool(0). */ cson_value * cson_value_false( void ); /** Semantically the same as cson_value_new_bool(), but for integers. */ cson_value * cson_value_new_integer( cson_int_t v ); /** Alias for cson_value_new_integer(v). */ cson_value * cson_new_int(cson_int_t v); /** Semantically the same as cson_value_new_bool(), but for doubles. */ cson_value * cson_value_new_double( cson_double_t v ); /** Alias for cson_value_new_double(v). */ cson_value * cson_new_double(cson_double_t v); /** Semantically the same as cson_value_new_bool(), but for strings. This creates a JSON value which copies the first n bytes of str. The string will automatically be NUL-terminated. Note that if str is NULL or n is 0, this function still returns non-NULL value representing that empty string. Returns NULL on allocation error. See cson_value_new_bool() for important information about the returned memory. */ cson_value * cson_value_new_string( char const * str, unsigned int n ); /** Allocates a new "object" value and transfers ownership of it to the caller. It must eventually be destroyed, by the caller or its owning container, by passing it to cson_value_free(). Returns NULL on allocation error. Post-conditions: cson_value_is_object(value) will return true. @see cson_value_new_array() @see cson_value_free() */ cson_value * cson_value_new_object( void ); /** This works like cson_value_new_object() but returns an Object handle directly. The value handle for the returned object can be fetched with cson_object_value(theObject). Ownership is transfered to the caller, who must eventually free it by passing the Value handle (NOT the Object handle) to cson_value_free() or passing ownership to a parent container. Returns NULL on error (out of memory). */ cson_object * cson_new_object( void ); /** Identical to cson_new_object() except that it creates an Array. */ cson_array * cson_new_array( void ); /** Identical to cson_new_object() except that it creates a String. */ cson_string * cson_new_string(char const * val, unsigned int len); /** Equivalent to cson_value_free(cson_object_value(x)). */ void cson_free_object(cson_object *x); /** Equivalent to cson_value_free(cson_array_value(x)). */ void cson_free_array(cson_array *x); /** Equivalent to cson_value_free(cson_string_value(x)). */ void cson_free_string(cson_string *x); /** Allocates a new "array" value and transfers ownership of it to the caller. It must eventually be destroyed, by the caller or its owning container, by passing it to cson_value_free(). Returns NULL on allocation error. Post-conditions: cson_value_is_array(value) will return true. @see cson_value_new_object() @see cson_value_free() */ cson_value * cson_value_new_array( void ); /** Frees any resources owned by v, then frees v. If v is a container type (object or array) its children are also freed (recursively). If v is NULL, this is a no-op. This function decrements a reference count and only destroys the value if its reference count drops to 0. Reference counts are increased by either inserting the value into a container or via cson_value_add_reference(). Even if this function does not immediately destroy the value, the value must be considered, from the perspective of that client code, to have been destroyed/invalidated by this call. @see cson_value_new_object() @see cson_value_new_array() @see cson_value_add_reference() */ void cson_value_free(cson_value * v); /** Alias for cson_value_free(). */ void cson_free_value(cson_value * v); /** Functionally similar to cson_array_set(), but uses a string key as an index. Like arrays, if a value already exists for the given key, it is destroyed by this function before inserting the new value. If v is NULL then this call is equivalent to cson_object_unset(obj,key). Note that (v==NULL) is treated differently from v having the special null value. In the latter case, the key is set to the special null value. The key may be encoded as ASCII or UTF8. Results are undefined with other encodings, and the errors won't show up here, but may show up later, e.g. during output. Returns 0 on success, non-0 on error. It has the following error cases: - cson_rc.ArgError: obj or key are NULL or strlen(key) is 0. - cson_rc.AllocError: an out-of-memory error On error ownership of v is NOT modified, and the caller may still need to clean it up. For example, the following code will introduce a leak if this function fails: @code cson_object_set( myObj, "foo", cson_value_new_integer(42) ); @endcode Because the value created by cson_value_new_integer() has no owner and is not cleaned up. The "more correct" way to do this is: @code cson_value * v = cson_value_new_integer(42); int rc = cson_object_set( myObj, "foo", v ); if( 0 != rc ) { cson_value_free( v ); ... handle error ... } @endcode Potential TODOs: - Add an overload which takes a cson_value key instead. To get any value out of that we first need to be able to convert arbitrary value types to strings. We could simply to-JSON them and use those as keys. */ int cson_object_set( cson_object * obj, char const * key, cson_value * v ); /** Functionaly equivalent to cson_object_set(), but takes a cson_string() as its KEY type. The string will be reference-counted like any other values, and the key may legally be used within this same container (as a value) or others (as a key or value) at the same time. Returns 0 on success. On error, ownership (i.e. refcounts) of key and value are not modified. On success key and value will get increased refcounts unless they are replacing themselves (which is a harmless no-op). */ int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v ); /** Removes a property from an object. If obj contains the given key, it is removed and 0 is returned. If it is not found, cson_rc.NotFoundError is returned (which can normally be ignored by client code). cson_rc.ArgError is returned if obj or key are NULL or key has a length of 0. Returns 0 if the given key is found and removed. This is functionally equivalent calling cson_object_set(obj,key,NULL). */ int cson_object_unset( cson_object * obj, char const * key ); /** Searches the given object for a property with the given key. If found, it is returned. If no match is found, or any arguments are NULL, NULL is returned. The returned object is owned by obj, and may be invalidated by ANY operations which change obj's property list (i.e. add or remove properties). FIXME: allocate the key/value pairs like we do for cson_array, to get improve the lifetimes of fetched values. @see cson_object_fetch_sub() @see cson_object_get_sub() */ cson_value * cson_object_get( cson_object const * obj, char const * key ); /** Equivalent to cson_object_get() but takes a cson_string argument instead of a C-style string. */ cson_value * cson_object_get_s( cson_object const * obj, cson_string const *key ); /** Similar to cson_object_get(), but removes the value from the parent object's ownership. If no item is found then NULL is returned, else the object (now owned by the caller or possibly shared with other containers) is returned. Returns NULL if either obj or key are NULL or key has a length of 0. This function reduces the returned value's reference count but has the specific property that it does not treat refcounts 0 and 1 identically, meaning that the returned object may have a refcount of 0. This behaviour works around a corner-case where we want to extract a child element from its parent and then destroy the parent (which leaves us in an undesireable (normally) reference count state). */ cson_value * cson_object_take( cson_object * obj, char const * key ); /** Fetches a property from a child (or [great-]*grand-child) object. obj is the object to search. path is a delimited string, where the delimiter is the given separator character. This function searches for the given path, starting at the given object and traversing its properties as the path specifies. If a given part of the path is not found, then this function fails with cson_rc.NotFoundError. If it finds the given path, it returns the value by assiging *tgt to it. If tgt is NULL then this function has no side-effects but will return 0 if the given path is found within the object, so it can be used to test for existence without fetching it. Returns 0 if it finds an entry, cson_rc.NotFoundError if it finds no item, and any other non-zero error code on a "real" error. Errors include: - obj or path are NULL: cson_rc.ArgError - separator is 0, or path is an empty string or contains only separator characters: cson_rc.RangeError - There is an upper limit on how long a single path component may be (some "reasonable" internal size), and cson_rc.RangeError is returned if that length is violated. Limitations: - It has no way to fetch data from arrays this way. i could imagine, e.g., a path of "subobj.subArray.0" for subobj.subArray[0], or "0.3.1" for [0][3][1]. But i'm too lazy/tired to add this. Example usage: Assume we have a JSON structure which abstractly looks like: @code {"subobj":{"subsubobj":{"myValue":[1,2,3]}}} @endcode Out goal is to get the value of myValue. We can do that with: @code cson_value * v = NULL; int rc = cson_object_fetch_sub( object, &v, "subobj.subsubobj.myValue", '.' ); @endcode Note that because keys in JSON may legally contain a '.', the separator must be specified by the caller. e.g. the path "subobj/subsubobj/myValue" with separator='/' is equivalent the path "subobj.subsubobj.myValue" with separator='.'. The value of 0 is not legal as a separator character because we cannot distinguish that use from the real end-of-string without requiring the caller to also pass in the length of the string. Multiple successive separators in the list are collapsed into a single separator for parsing purposes. e.g. the path "a...b...c" (separator='.') is equivalent to "a.b.c". @see cson_object_get_sub() @see cson_object_get_sub2() */ int cson_object_fetch_sub( cson_object const * obj, cson_value ** tgt, char const * path, char separator ); /** Similar to cson_object_fetch_sub(), but derives the path separator character from the first byte of the path argument. e.g. the following arg equivalent: @code cson_object_fetch_sub( obj, &tgt, "foo.bar.baz", '.' ); cson_object_fetch_sub2( obj, &tgt, ".foo.bar.baz" ); @endcode */ int cson_object_fetch_sub2( cson_object const * obj, cson_value ** tgt, char const * path ); /** Convenience form of cson_object_fetch_sub() which returns NULL if the given item is not found. */ cson_value * cson_object_get_sub( cson_object const * obj, char const * path, char sep ); /** Convenience form of cson_object_fetch_sub2() which returns NULL if the given item is not found. */ cson_value * cson_object_get_sub2( cson_object const * obj, char const * path ); /** @enum CSON_MERGE_FLAGS Flags for cson_object_merge(). */ enum CSON_MERGE_FLAGS { CSON_MERGE_DEFAULT = 0, CSON_MERGE_REPLACE = 0x01, CSON_MERGE_NO_RECURSE = 0x02 }; /** "Merges" the src object's properties into dest. Each property in src is copied (using reference counting, not cloning) into dest. If dest already has the given property then behaviour depends on the flags argument: If flag has the CSON_MERGE_REPLACE bit set then this function will by default replace non-object properties with the src property. If src and dest both have the property AND it is an Object then this function operates recursively on those objects. If CSON_MERGE_NO_RECURSE is set then objects are not recursed in this manner, and will be completely replaced if CSON_MERGE_REPLACE is set. Array properties in dest are NOT recursed for merging - they are either replaced or left as-is, depending on whether flags contains he CSON_MERGE_REPLACE bit. Returns 0 on success. The error conditions are: - dest or src are NULL or (dest==src) returns cson_rc.ArgError. - dest or src contain cyclic references - this will likely cause a crash due to endless recursion. Potential TODOs: - Add a flag to copy clones, not the original values. */ int cson_object_merge( cson_object * dest, cson_object const * src, int flags ); /** An iterator type for traversing object properties. Its values must be considered private, not to be touched by client code. @see cson_object_iter_init() @see cson_object_iter_next() */ struct cson_object_iterator { /** @internal The underlying object. */ cson_object const * obj; /** @internal Current position in the property list. */ unsigned int pos; }; typedef struct cson_object_iterator cson_object_iterator; /** Empty-initialized cson_object_iterator object. */ #define cson_object_iterator_empty_m {NULL/*obj*/,0/*pos*/} /** Empty-initialized cson_object_iterator object. */ extern const cson_object_iterator cson_object_iterator_empty; /** Initializes the given iterator to point at the start of obj's properties. Returns 0 on success or cson_rc.ArgError if !obj or !iter. obj must outlive iter, or results are undefined. Results are also undefined if obj is modified while the iterator is active. @see cson_object_iter_next() */ int cson_object_iter_init( cson_object const * obj, cson_object_iterator * iter ); /** @struct cson_kvp This class represents a key/value pair and is used for storing object properties. It is opaque to client code, and the public API only uses this type for purposes of iterating over cson_object properties using the cson_object_iterator interfaces. */ typedef struct cson_kvp cson_kvp; /** Returns the next property from the given iterator's object, or NULL if the end of the property list as been reached. Note that the order of object properties is undefined by the API, and may change from version to version. The returned memory belongs to the underlying object and may be invalidated by any changes to that object. Example usage: @code cson_object_iterator it; cson_object_iter_init( myObject, &it ); // only fails if either arg is 0 cson_kvp * kvp; cson_string const * key; cson_value const * val; while( (kvp = cson_object_iter_next(&it) ) ) { key = cson_kvp_key(kvp); val = cson_kvp_value(kvp); ... } @endcode There is no need to clean up an iterator, as it holds no dynamic resources. @see cson_kvp_key() @see cson_kvp_value() */ cson_kvp * cson_object_iter_next( cson_object_iterator * iter ); /** Returns the key associated with the given key/value pair, or NULL if !kvp. The memory is owned by the object which contains the key/value pair, and may be invalidated by any modifications to that object. */ cson_string * cson_kvp_key( cson_kvp const * kvp ); /** Returns the value associated with the given key/value pair, or NULL if !kvp. The memory is owned by the object which contains the key/value pair, and may be invalidated by any modifications to that object. */ cson_value * cson_kvp_value( cson_kvp const * kvp ); /** @typedef some unsigned int type cson_size_t */ typedef unsigned int cson_size_t; /** A generic buffer class. They can be used like this: @code cson_buffer b = cson_buffer_empty; int rc = cson_buffer_reserve( &buf, 100 ); if( 0 != rc ) { ... allocation error ... } ... use buf.mem ... ... then free it up ... cson_buffer_reserve( &buf, 0 ); @endcode To take over ownership of a buffer's memory: @code void * mem = b.mem; // mem is b.capacity bytes long, but only b.used // bytes of it has been "used" by the API. b = cson_buffer_empty; @endcode The memory now belongs to the caller and must eventually be free()d. */ struct cson_buffer { /** The number of bytes allocated for this object. Use cson_buffer_reserve() to change its value. */ cson_size_t capacity; /** The number of bytes "used" by this object. It is not needed for all use cases, and management of this value (if needed) is up to the client. The cson_buffer public API does not use this member. The intention is that this can be used to track the length of strings which are allocated via cson_buffer, since they need an explicit length and/or null terminator. */ cson_size_t used; /** This is a debugging/metric-counting value intended to help certain malloc()-conscious clients tweak their memory reservation sizes. Each time cson_buffer_reserve() expands the buffer, it increments this value by 1. */ cson_size_t timesExpanded; /** The memory allocated for and owned by this buffer. Use cson_buffer_reserve() to change its size or free it. To take over ownership, do: @code void * myptr = buf.mem; buf = cson_buffer_empty; @endcode (You might also need to store buf.used and buf.capacity, depending on what you want to do with the memory.) When doing so, the memory must eventually be passed to free() to deallocate it. */ unsigned char * mem; }; /** Convenience typedef. */ typedef struct cson_buffer cson_buffer; /** An empty-initialized cson_buffer object. */ #define cson_buffer_empty_m {0/*capacity*/,0/*used*/,0/*timesExpanded*/,NULL/*mem*/} /** An empty-initialized cson_buffer object. */ extern const cson_buffer cson_buffer_empty; /** Uses cson_output() to append all JSON output to the given buffer object. The semantics for the (v, opt) parameters, and the return value, are as documented for cson_output(). buf must be a non-NULL pointer to a properly initialized buffer (see example below). Ownership of buf is not changed by calling this. On success 0 is returned and the contents of buf.mem are guaranteed to be NULL-terminated. On error the buffer might contain partial contents, and it should not be used except to free its contents. On error non-zero is returned. Errors include: - Invalid arguments: cson_rc.ArgError - Buffer cannot be expanded (runs out of memory): cson_rc.AllocError Example usage: @code cson_buffer buf = cson_buffer_empty; // optional: cson_buffer_reserve(&buf, 1024 * 10); int rc = cson_output_buffer( myValue, &buf, NULL ); if( 0 != rc ) { ... error! ... } else { ... use buffer ... puts((char const*)buf.mem); } // In both cases, we eventually need to clean up the buffer: cson_buffer_reserve( &buf, 0 ); // Or take over ownership of its memory: { char * mem = (char *)buf.mem; buf = cson_buffer_empty; ... free(mem); } @endcode @see cson_output() */ int cson_output_buffer( cson_value const * v, cson_buffer * buf, cson_output_opt const * opt ); /** This works identically to cson_parse_string(), but takes a cson_buffer object as its input. buf->used bytes of buf->mem are assumed to be valid JSON input, but it need not be NUL-terminated (we only read up to buf->used bytes). The value of buf->used is assumed to be the "string length" of buf->mem, i.e. not including the NUL terminator. Returns 0 on success, non-0 on error. See cson_parse() for the semantics of the tgt, opt, and err parameters. */ int cson_parse_buffer( cson_value ** tgt, cson_buffer const * buf, cson_parse_opt const * opt, cson_parse_info * err ); /** Reserves the given amount of memory for the given buffer object. If n is 0 then buf->mem is freed and its state is set to NULL/0 values. If buf->capacity is less than or equal to n then 0 is returned and buf is not modified. If n is larger than buf->capacity then buf->mem is (re)allocated and buf->capacity contains the new length. Newly-allocated bytes are filled with zeroes. On success 0 is returned. On error non-0 is returned and buf is not modified. buf->mem is owned by buf and must eventually be freed by passing an n value of 0 to this function. buf->used is never modified by this function unless n is 0, in which case it is reset. */ int cson_buffer_reserve( cson_buffer * buf, cson_size_t n ); /** Fills all bytes of the given buffer with the given character. Returns the number of bytes set (buf->capacity), or 0 if !buf or buf has no memory allocated to it. */ cson_size_t cson_buffer_fill( cson_buffer * buf, char c ); /** Uses a cson_data_source_f() function to buffer input into a cson_buffer. dest must be a non-NULL, initialized (though possibly empty) cson_buffer object. Its contents, if any, will be overwritten by this function, and any memory it holds might be re-used. The src function is called, and passed the state parameter, to fetch the input. If it returns non-0, this function returns that error code. src() is called, possibly repeatedly, until it reports that there is no more data. Whether or not this function succeeds, dest still owns any memory pointed to by dest->mem, and the client must eventually free it by calling cson_buffer_reserve(dest,0). dest->mem might (and possibly will) be (re)allocated by this function, so any pointers to it held from before this call might be invalidated by this call. On error non-0 is returned and dest has almost certainly been modified but its state must be considered incomplete. Errors include: - dest or src are NULL (cson_rc.ArgError) - Allocation error (cson_rc.AllocError) - src() returns an error code Whether or not the state parameter may be NULL depends on the src implementation requirements. On success dest will contain the contents read from the input source. dest->used will be the length of the read-in data, and dest->mem will point to the memory. dest->mem is automatically NUL-terminated if this function succeeds, but dest->used does not count that terminator. On error the state of dest->mem must be considered incomplete, and is not guaranteed to be NUL-terminated. Example usage: @code cson_buffer buf = cson_buffer_empty; int rc = cson_buffer_fill_from( &buf, cson_data_source_FILE, stdin ); if( rc ) { fprintf(stderr,"Error %d (%s) while filling buffer.\n", rc, cson_rc_string(rc)); cson_buffer_reserve( &buf, 0 ); return ...; } ... use the buf->mem ... ... clean up the buffer ... cson_buffer_reserve( &buf, 0 ); @endcode To take over ownership of the buffer's memory, do: @code void * mem = buf.mem; buf = cson_buffer_empty; @endcode In which case the memory must eventually be passed to free() to free it. */ int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state ); /** Increments the reference count for the given value. This is a low-level operation and should not normally be used by client code without understanding exactly what side-effects it introduces. Mis-use can lead to premature destruction or cause a value instance to never be properly destructed (i.e. a memory leak). This function is probably only useful for the following cases: - You want to hold a reference to a value which is itself contained in one or more containers, and you need to be sure that your reference outlives the container(s) and/or that you can free your copy of the reference without invaliding any references to the same value held in containers. - You want to implement "value sharing" behaviour without using an object or array to contain the shared value. This can be used to ensure the lifetime of the shared value instance. Each sharing point adds a reference and simply passed the value to cson_value_free() when they're done. The object will be kept alive for other sharing points which added a reference. Normally any such value handles would be invalidated when the parent container(s) is/are cleaned up, but this function can be used to effectively delay the cleanup. This function, at its lowest level, increments the value's reference count by 1. To decrement the reference count, pass the value to cson_value_free(), after which the value must be considered, from the perspective of that client code, to be destroyed (though it will not be if there are still other live references to it). cson_value_free() will not _actually_ destroy the value until its reference count drops to 0. Returns 0 on success. The only error conditions are if v is NULL (cson_rc.ArgError) or if the reference increment would overflow (cson_rc.RangeError). In theory a client would get allocation errors long before the reference count could overflow (assuming those reference counts come from container insertions, as opposed to via this function). Insider notes which clients really need to know: For shared/constant value instances, such as those returned by cson_value_true() and cson_value_null(), this function has no side effects - it does not actually modify the reference count because (A) those instances are shared across all client code and (B) those objects are static and never get cleaned up. However, that is an implementation detail which client code should not rely on. In other words, if you call cson_value_add_reference() 3 times using the value returned by cson_value_true() (which is incidentally a shared cson_value instance), you must eventually call cson_value_free() 3 times to (semantically) remove those references. However, internally the reference count for that specific cson_value instance will not be modified and those objects will never be freed (they're stack-allocated). It might be interesting to note that newly-created objects have a reference count of 0 instead of 1. This is partly because if the initial reference is counted then it makes ownership problematic when inserting values into containers. e.g. consider the following code: @code // ACHTUNG: this code is hypothetical and does not reflect // what actually happens! cson_value * v = cson_value_new_integer( 42 ); // v's refcount = 1 cson_array_append( myArray, v ); // v's refcount = 2 @endcode If that were the case, the client would be forced to free his own reference after inserting it into the container (which is a bit counter-intuitive as well as intrusive). It would look a bit like the following and would have to be done after every create/insert operation: @code // ACHTUNG: this code is hypothetical and does not reflect // what actually happens! cson_array_append( myArray, v ); // v's refcount = 2 cson_value_free( v ); // v's refcount = 1 @endcode (As i said: it's counter-intuitive and intrusive.) Instead, values start with a refcount of 0 and it is only increased when the value is added to an object/array container or when this function is used to manually increment it. cson_value_free() treats a refcount of 0 or 1 equivalently, destroying the value instance. The only semantic difference between 0 and 1, for purposes of cleaning up, is that a value with a non-0 refcount has been had its refcount adjusted, whereas a 0 refcount indicates a fresh, "unowned" reference. */ int cson_value_add_reference( cson_value * v ); #if 0 /** DO NOT use this unless you know EXACTLY what you're doing. It is only in the public API to work around a couple corner cases involving extracting child elements and discarding their parents. This function sets v's reference count to the given value. It does not clean up the object if rc is 0. Returns 0 on success, non-0 on error. */ int cson_value_refcount_set( cson_value * v, unsigned short rc ); #endif /** Deeply copies a JSON value, be it an object/array or a "plain" value (e.g. number/string/boolean). If cv is not NULL then this function makes a deep clone of it and returns that clone. Ownership of the clone is identical t transfered to the caller, who must eventually free the value using cson_value_free() or add it to a container object/array to transfer ownership to the container. The returned object will be of the same logical type as orig. ACHTUNG: if orig contains any cyclic references at any depth level this function will endlessly recurse. (Having _any_ cyclic references violates this library's requirements.) Returns NULL if orig is NULL or if cloning fails. Assuming that orig is in a valid state, the only "likely" error case is that an allocation fails while constructing the clone. In other words, if cloning fails due to something other than an allocation error then either orig is in an invalid state or there is a bug. When this function clones Objects or Arrays it shares any immutable values (including object keys) between the parent and the clone. Mutable values (Objects and Arrays) are copied, however. For example, if we clone: @code { a: 1, b: 2, c:["hi"] } @endcode The cloned object and the array "c" would be a new Object/Array instances but the object keys (a,b,b) and the values of (a,b), as well as the string value within the "c" array, would be shared between the original and the clone. The "c" array itself would be deeply cloned, such that future changes to the clone are not visible to the parent, and vice versa, but immutable values within the array are shared (in this case the string "hi"). The justification for this heuristic is that immutable values can never be changed, so there is no harm in sharing them across clones. Additionally, such types can never contribute to cycles in a JSON tree, so they are safe to share this way. Objects and Arrays, on the other hand, can be modified later and can contribute to cycles, and thus the clone needs to be an independent instance. Note, however, that if this function directly passed a non-Object/Array, that value is deeply cloned. The sharing behaviour only applies when traversing Objects/Arrays. */ cson_value * cson_value_clone( cson_value const * orig ); /** Returns the value handle associated with s. The handle itself owns s, and ownership of the handle is not changed by calling this function. If the returned handle is part of a container, calling cson_value_free() on the returned handle invoked undefined behaviour (quite possibly downstream when the container tries to use it). This function only returns NULL if s is NULL. The length of the returned string is cson_string_length_bytes(). */ cson_value * cson_string_value(cson_string const * s); /** The Object form of cson_string_value(). See that function for full details. */ cson_value * cson_object_value(cson_object const * s); /** The Array form of cson_string_value(). See that function for full details. */ cson_value * cson_array_value(cson_array const * s); /** Calculates the approximate in-memory-allocated size of v, recursively if it is a container type, with the following caveats and limitations: If a given value is reference counted then it is only and multiple times within a traversed container, each reference is counted at full cost. We have no way of knowing if a given reference has been visited already and whether it should or should not be counted, so we pessimistically count them even though the _might_ not really count for the given object tree (it depends on where the other open references live). This function returns 0 if any of the following are true: - v is NULL - v is one of the special singleton values (null, bools, empty string, int 0, double 0.0) All other values require an allocation, and this will return their total memory cost, including the cson-specific internals and the native value(s). Note that because arrays and objects might have more internal slots allocated than used, the alloced size of a container does not necessarily increase when a new item is inserted into it. An interesting side-effect of this is that when cson_clone()ing an array or object, the size of the clone can actually be less than the original. */ unsigned int cson_value_msize(cson_value const * v); /** Parses command-line-style arguments into a JSON object. It expects arguments to be in any of these forms, and any number of leading dashes are treated identically: --key : Treats key as a boolean with a true value. --key=VAL : Treats VAL as either a double, integer, or string. --key= : Treats key as a JSON null (not literal NULL) value. Arguments not starting with a dash are skipped. Each key/value pair is inserted into an object. If a given key appears more than once then only the final entry is actually stored. argc and argv are expected to be values from main() (or similar, possibly adjusted to remove argv[0]). tgt must be either a pointer to NULL or a pointer to a client-provided Object. If (NULL==*tgt) then this function allocates a new object and on success it stores the new object in *tgt (it is owned by the caller). If (NULL!=*tgt) then it is assumed to be a properly allocated object. DO NOT pass a pointer to an unitialized pointer, as that will fool this function into thinking it is a valid object and Undefined Behaviour will ensue. If count is not NULL then the number of arugments parsed by this function are assigned to it. On error, count will be the number of options successfully parsed before the error was encountered. On success: - 0 is returned. - If (*tgt==NULL) then *tgt is assigned to a newly-allocated object, owned by the caller. Note that even if no arguments are parsed, the object is still created. On error: - non-0 is returned - If (*tgt==NULL) then it is not modified. - If (*tgt!=NULL) (i.e., the caller provides his own object) then it might contain partial results. */ int cson_parse_argv_flags( int argc, char const * const * argv, cson_object ** tgt, unsigned int * count ); /* LICENSE This software's source code, including accompanying documentation and demonstration applications, are licensed under the following conditions... Certain files are imported from external projects and have their own licensing terms. Namely, the JSON_parser.* files. See their files for their official licenses, but the summary is "do what you want [with them] but leave the license text and copyright in place." The author (Stephan G. Beal [http://wanderinghorse.net/home/stephan/]) explicitly disclaims copyright in all jurisdictions which recognize such a disclaimer. In such jurisdictions, this software is released into the Public Domain. In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 2011), this software is Copyright (c) 2011 by Stephan G. Beal, and is released under the terms of the MIT License (see below). In jurisdictions which recognize Public Domain property, the user of this software may choose to accept it either as 1) Public Domain, 2) under the conditions of the MIT License (see below), or 3) under the terms of dual Public Domain/MIT License conditions described here, as they choose. The MIT License is about as close to Public Domain as a license can get, and is described in clear, concise terms at: http://en.wikipedia.org/wiki/MIT_License The full text of the MIT License follows: -- Copyright (c) 2011 Stephan G. Beal (http://wanderinghorse.net/home/stephan/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 OR COPYRIGHT HOLDERS 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. --END OF MIT LICENSE-- For purposes of the above license, the term "Software" includes documentation and demonstration source code which accompanies this software. ("Accompanies" = is contained in the Software's primary public source code repository.) */ #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* WANDERINGHORSE_NET_CSON_H_INCLUDED */ /* end file include/wh/cson/cson.h */ /* begin file include/wh/cson/cson_sqlite3.h */ /** @file cson_sqlite3.h This file contains cson's public sqlite3-to-JSON API declarations and API documentation. If CSON_ENABLE_SQLITE3 is not defined, or is defined to 0, then including this file will have no side-effects other than defining CSON_ENABLE_SQLITE3 (if it was not defined) to 0 and defining a few include guard macros. i.e. if CSON_ENABLE_SQLITE3 is not set to a true value then the API is not visible. This API requires that <sqlite3.h> be in the INCLUDES path and that the client eventually link to (or directly embed) the sqlite3 library. */ #if !defined(WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED) #define WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED 1 #if !defined(CSON_ENABLE_SQLITE3) # if defined(DOXYGEN) #define CSON_ENABLE_SQLITE3 1 # else #define CSON_ENABLE_SQLITE3 1 # endif #endif #if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */ #include <sqlite3.h> #if defined(__cplusplus) extern "C" { #endif /** Converts a single value from a single 0-based column index to its JSON equivalent. On success it returns a new JSON value, which will have a different concrete type depending on the field type reported by sqlite3_column_type(st,col): Integer, double, null, or string (TEXT and BLOB data, though not all blob data is legal for a JSON string). st must be a sqlite3_step()'d row and col must be a 0-based column index within that result row. */ cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col ); /** Creates a JSON Array object containing the names of all columns of the given prepared statement handle. Returns a new array value on success, which the caller owns. Its elements are in the same order as in the underlying query. On error NULL is returned. st is not traversed or freed by this function - only the column count and names are read. */ cson_value * cson_sqlite3_column_names( sqlite3_stmt * st ); /** Creates a JSON Object containing key/value pairs corresponding to the result columns in the current row of the given statement handle. st must be a sqlite3_step()'d row result. On success a new Object is returned which is owned by the caller. On error NULL is returned. cson_sqlite3_column_to_value() is used to convert each column to a JSON value, and the column names are taken from sqlite3_column_name(). */ cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st ); /** Functionally almost identical to cson_sqlite3_row_to_object(), the only difference being how the result objects gets its column names. st must be a freshly-step()'d handle holding a result row. colNames must be an Array with at least the same number of columns as st. If it has fewer, NULL is returned and this function has no side-effects. For each column in the result set, the colNames entry at the same index is used for the column key. If a given entry is-not-a String then conversion will fail and NULL will be returned. The one reason to prefer this over cson_sqlite3_row_to_object() is that this one can share the keys across multiple rows (or even other JSON containers), whereas the former makes fresh copies of the column names for each row. */ cson_value * cson_sqlite3_row_to_object2( sqlite3_stmt * st, cson_array * colNames ); /** Similar to cson_sqlite3_row_to_object(), but creates an Array value which contains the JSON-form values of the given result set row. */ cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st ); /** Converts the results of an sqlite3 SELECT statement to JSON, in the form of a cson_value object tree. st must be a prepared, but not yet traversed, SELECT query. tgt must be a pointer to NULL (see the example below). If either of those arguments are NULL, cson_rc.ArgError is returned. This walks the query results and returns a JSON object which has a different structure depending on the value of the 'fat' argument. If 'fat' is 0 then the structure is: @code { "columns":["colName1",..."colNameN"], "rows":[ [colVal0, ... colValN], [colVal0, ... colValN], ... ] } @endcode In the "non-fat" format the order of the columns and row values is guaranteed to be the same as that of the underlying query. If 'fat' is not 0 then the structure is: @code { "columns":["colName1",..."colNameN"], "rows":[ {"colName1":value1,..."colNameN":valueN}, {"colName1":value1,..."colNameN":valueN}, ... ] } @endcode In the "fat" format, the order of the "columns" entries is guaranteed to be the same as the underlying query fields, but the order of the keys in the "rows" might be different and might in fact change when passed through different JSON implementations, depending on how they implement object key/value pairs. On success it returns 0 and assigns *tgt to a newly-allocated JSON object tree (using the above structure), which the caller owns. If the query returns no rows, the "rows" value will be an empty array, as opposed to null. On error non-0 is returned and *tgt is not modified. The error code cson_rc.IOError is used to indicate a db-level error, and cson_rc.TypeError is returned if sqlite3_column_count(st) returns 0 or less (indicating an invalid or non-SELECT statement). The JSON data types are determined by the column type as reported by sqlite3_column_type(): SQLITE_INTEGER: integer SQLITE_FLOAT: double SQLITE_TEXT or SQLITE_BLOB: string, and this will only work if the data is UTF8 compatible. If the db returns a literal or SQL NULL for a value it is converted to a JSON null. If it somehow finds a column type it cannot handle, the value is also converted to a NULL in the output. Example @code cson_value * json = NULL; int rc = cson_sqlite3_stmt_to_json( myStatement, &json, 1 ); if( 0 != rc ) { ... error ... } else { cson_output_FILE( json, stdout, NULL ); cson_value_free( json ); } @endcode */ int cson_sqlite3_stmt_to_json( sqlite3_stmt * st, cson_value ** tgt, char fat ); /** A convenience wrapper around cson_sqlite3_stmt_to_json(), which takes SQL instead of a sqlite3_stmt object. It has the same return value and argument semantics as that function. */ int cson_sqlite3_sql_to_json( sqlite3 * db, cson_value ** tgt, char const * sql, char fat ); /** Binds a JSON value to a 1-based parameter index in a prepared SQL statement. v must be NULL or one of one of the types (null, string, integer, double, boolean, array). Booleans are bound as integer 0 or 1. NULL or null are bound as SQL NULL. Integers are bound as 64-bit ints. Strings are bound using sqlite3_bind_text() (as opposed to text16), but we could/should arguably bind them as blobs. If v is an Array then ndx is is used as a starting position (1-based) and each item in the array is bound to the next parameter position (starting and ndx, though the array uses 0-based offsets). TODO: add Object support for named parameters. Returns 0 on success, non-0 on error. */ int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v ); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* CSON_ENABLE_SQLITE3 */ #endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */ /* end file include/wh/cson/cson_sqlite3.h */ #endif /* FOSSIL_ENABLE_JSON */ |
Added extsrc/linenoise.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 | /* linenoise.c -- guerrilla line editing library against the idea that a * line editing lib needs to be 20,000 lines of C code. * * You can find the latest source code at: * * http://github.com/antirez/linenoise * * Does a number of crazy assumptions that happen to be true in 99.9999% of * the 2010 UNIX computers around. * * ------------------------------------------------------------------------ * * Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * HOLDER 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. * * ------------------------------------------------------------------------ * * References: * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html * * Todo list: * - Filter bogus Ctrl+<char> combinations. * - Win32 support * * Bloat: * - History search like Ctrl+r in readline? * * List of escape sequences used by this program, we do everything just * with three sequences. In order to be so cheap we may have some * flickering effect with some slow terminal, but the lesser sequences * the more compatible. * * EL (Erase Line) * Sequence: ESC [ n K * Effect: if n is 0 or missing, clear from cursor to end of line * Effect: if n is 1, clear from beginning of line to cursor * Effect: if n is 2, clear entire line * * CUF (CUrsor Forward) * Sequence: ESC [ n C * Effect: moves cursor forward n chars * * CUB (CUrsor Backward) * Sequence: ESC [ n D * Effect: moves cursor backward n chars * * The following is used to get the terminal width if getting * the width with the TIOCGWINSZ ioctl fails * * DSR (Device Status Report) * Sequence: ESC [ 6 n * Effect: reports the current cusor position as ESC [ n ; m R * where n is the row and m is the column * * When multi line mode is enabled, we also use an additional escape * sequence. However multi line editing is disabled by default. * * CUU (Cursor Up) * Sequence: ESC [ n A * Effect: moves cursor up of n chars. * * CUD (Cursor Down) * Sequence: ESC [ n B * Effect: moves cursor down of n chars. * * When linenoiseClearScreen() is called, two additional escape sequences * are used in order to clear the screen and position the cursor at home * position. * * CUP (Cursor position) * Sequence: ESC [ H * Effect: moves the cursor to upper left corner * * ED (Erase display) * Sequence: ESC [ 2 J * Effect: clear the whole screen * */ #include <termios.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/ioctl.h> #include <unistd.h> #include "linenoise.h" #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 #define LINENOISE_MAX_LINE 4096 static char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; static linenoiseCompletionCallback *completionCallback = NULL; static linenoiseHintsCallback *hintsCallback = NULL; static linenoiseFreeHintsCallback *freeHintsCallback = NULL; static struct termios orig_termios; /* In order to restore at exit.*/ static int maskmode = 0; /* Show "***" instead of input. For passwords. */ static int rawmode = 0; /* For atexit() function to check if restore is needed*/ static int mlmode = 0; /* Multi line mode. Default is single line. */ static int atexit_registered = 0; /* Register atexit just 1 time. */ static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; static int history_len = 0; static char **history = NULL; /* The linenoiseState structure represents the state during line editing. * We pass this state to functions implementing specific editing * functionalities. */ struct linenoiseState { int ifd; /* Terminal stdin file descriptor. */ int ofd; /* Terminal stdout file descriptor. */ char *buf; /* Edited line buffer. */ size_t buflen; /* Edited line buffer size. */ const char *prompt; /* Prompt to display. */ size_t plen; /* Prompt length. */ size_t pos; /* Current cursor position. */ size_t oldpos; /* Previous refresh cursor position. */ size_t len; /* Current edited line length. */ size_t cols; /* Number of columns in terminal. */ size_t maxrows; /* Maximum num of rows used so far (multiline mode) */ int history_index; /* The history index we are currently editing. */ }; enum KEY_ACTION{ KEY_NULL = 0, /* NULL */ CTRL_A = 1, /* Ctrl+a */ CTRL_B = 2, /* Ctrl-b */ CTRL_C = 3, /* Ctrl-c */ CTRL_D = 4, /* Ctrl-d */ CTRL_E = 5, /* Ctrl-e */ CTRL_F = 6, /* Ctrl-f */ CTRL_H = 8, /* Ctrl-h */ TAB = 9, /* Tab */ CTRL_K = 11, /* Ctrl+k */ CTRL_L = 12, /* Ctrl+l */ ENTER = 13, /* Enter */ CTRL_N = 14, /* Ctrl-n */ CTRL_P = 16, /* Ctrl-p */ CTRL_T = 20, /* Ctrl-t */ CTRL_U = 21, /* Ctrl+u */ CTRL_W = 23, /* Ctrl+w */ ESC = 27, /* Escape */ BACKSPACE = 127 /* Backspace */ }; static void linenoiseAtExit(void); int linenoiseHistoryAdd(const char *line); static void refreshLine(struct linenoiseState *l); /* Debugging macro. */ #if 0 FILE *lndebug_fp = NULL; #define lndebug(...) \ do { \ if (lndebug_fp == NULL) { \ lndebug_fp = fopen("/tmp/lndebug.txt","a"); \ fprintf(lndebug_fp, \ "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \ (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \ (int)l->maxrows,old_rows); \ } \ fprintf(lndebug_fp, ", " __VA_ARGS__); \ fflush(lndebug_fp); \ } while (0) #else #define lndebug(fmt, ...) #endif /* ======================= Low level terminal handling ====================== */ /* Enable "mask mode". When it is enabled, instead of the input that * the user is typing, the terminal will just display a corresponding * number of asterisks, like "****". This is useful for passwords and other * secrets that should not be displayed. */ void linenoiseMaskModeEnable(void) { maskmode = 1; } /* Disable mask mode. */ void linenoiseMaskModeDisable(void) { maskmode = 0; } /* Set if to use or not the multi line mode. */ void linenoiseSetMultiLine(int ml) { mlmode = ml; } /* Return true if the terminal name is in the list of terminals we know are * not able to understand basic escape sequences. */ static int isUnsupportedTerm(void) { char *term = getenv("TERM"); int j; if (term == NULL) return 0; for (j = 0; unsupported_term[j]; j++) if (!strcasecmp(term,unsupported_term[j])) return 1; return 0; } /* Raw mode: 1960 magic shit. */ static int enableRawMode(int fd) { struct termios raw; if (!isatty(STDIN_FILENO)) goto fatal; if (!atexit_registered) { atexit(linenoiseAtExit); atexit_registered = 1; } if (tcgetattr(fd,&orig_termios) == -1) goto fatal; raw = orig_termios; /* modify the original mode */ /* input modes: no break, no CR to NL, no parity check, no strip char, * no start/stop output control. */ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); /* output modes - disable post processing */ raw.c_oflag &= ~(OPOST); /* control modes - set 8 bit chars */ raw.c_cflag |= (CS8); /* local modes - choing off, canonical off, no extended functions, * no signal chars (^Z,^C) */ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* control chars - set return condition: min number of bytes and timer. * We want read to return every single byte, without timeout. */ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ /* put terminal in raw mode after flushing */ if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; rawmode = 1; return 0; fatal: errno = ENOTTY; return -1; } static void disableRawMode(int fd) { /* Don't even check the return value as it's too late. */ if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) rawmode = 0; } /* Use the ESC [6n escape sequence to query the horizontal cursor position * and return it. On error -1 is returned, on success the position of the * cursor. */ static int getCursorPosition(int ifd, int ofd) { char buf[32]; int cols, rows; unsigned int i = 0; /* Report cursor location */ if (write(ofd, "\x1b[6n", 4) != 4) return -1; /* Read the response: ESC [ rows ; cols R */ while (i < sizeof(buf)-1) { if (read(ifd,buf+i,1) != 1) break; if (buf[i] == 'R') break; i++; } buf[i] = '\0'; /* Parse it. */ if (buf[0] != ESC || buf[1] != '[') return -1; if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; return cols; } /* Try to get the number of columns in the current terminal, or assume 80 * if it fails. */ static int getColumns(int ifd, int ofd) { struct winsize ws; if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { /* ioctl() failed. Try to query the terminal itself. */ int start, cols; /* Get the initial position so we can restore it later. */ start = getCursorPosition(ifd,ofd); if (start == -1) goto failed; /* Go to right margin and get position. */ if (write(ofd,"\x1b[999C",6) != 6) goto failed; cols = getCursorPosition(ifd,ofd); if (cols == -1) goto failed; /* Restore position. */ if (cols > start) { char seq[32]; snprintf(seq,32,"\x1b[%dD",cols-start); if (write(ofd,seq,strlen(seq)) == -1) { /* Can't recover... */ } } return cols; } else { return ws.ws_col; } failed: return 80; } /* Clear the screen. Used to handle ctrl+l */ void linenoiseClearScreen(void) { if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) { /* nothing to do, just to avoid warning. */ } } /* Beep, used for completion when there is nothing to complete or when all * the choices were already shown. */ static void linenoiseBeep(void) { fprintf(stderr, "\x7"); fflush(stderr); } /* ============================== Completion ================================ */ /* Free a list of completion option populated by linenoiseAddCompletion(). */ static void freeCompletions(linenoiseCompletions *lc) { size_t i; for (i = 0; i < lc->len; i++) free(lc->cvec[i]); if (lc->cvec != NULL) free(lc->cvec); } /* This is an helper function for linenoiseEdit() and is called when the * user types the <tab> key in order to complete the string currently in the * input. * * The state of the editing is encapsulated into the pointed linenoiseState * structure as described in the structure definition. */ static int completeLine(struct linenoiseState *ls) { linenoiseCompletions lc = { 0, NULL }; int nread, nwritten; char c = 0; completionCallback(ls->buf,&lc); if (lc.len == 0) { linenoiseBeep(); } else { size_t stop = 0, i = 0; while(!stop) { /* Show completion or original buffer */ if (i < lc.len) { struct linenoiseState saved = *ls; ls->len = ls->pos = strlen(lc.cvec[i]); ls->buf = lc.cvec[i]; refreshLine(ls); ls->len = saved.len; ls->pos = saved.pos; ls->buf = saved.buf; } else { refreshLine(ls); } nread = read(ls->ifd,&c,1); if (nread <= 0) { freeCompletions(&lc); return -1; } switch(c) { case 9: /* tab */ i = (i+1) % (lc.len+1); if (i == lc.len) linenoiseBeep(); break; case 27: /* escape */ /* Re-show original buffer */ if (i < lc.len) refreshLine(ls); stop = 1; break; default: /* Update buffer and return */ if (i < lc.len) { nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]); ls->len = ls->pos = nwritten; } stop = 1; break; } } } freeCompletions(&lc); return c; /* Return last read character */ } /* Register a callback function to be called for tab-completion. */ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { completionCallback = fn; } /* Register a hits function to be called to show hits to the user at the * right of the prompt. */ void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) { hintsCallback = fn; } /* Register a function to free the hints returned by the hints callback * registered with linenoiseSetHintsCallback(). */ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { freeHintsCallback = fn; } /* This function is used by the callback function registered by the user * in order to add completion options given the input string when the * user typed <tab>. See the example.c source code for a very easy to * understand example. */ void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { size_t len = strlen(str); char *copy, **cvec; copy = malloc(len+1); if (copy == NULL) return; memcpy(copy,str,len+1); cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); if (cvec == NULL) { free(copy); return; } lc->cvec = cvec; lc->cvec[lc->len++] = copy; } /* =========================== Line editing ================================= */ /* We define a very simple "append buffer" structure, that is an heap * allocated string where we can append to. This is useful in order to * write all the escape sequences in a buffer and flush them to the standard * output in a single call, to avoid flickering effects. */ struct abuf { char *b; int len; }; static void abInit(struct abuf *ab) { ab->b = NULL; ab->len = 0; } static void abAppend(struct abuf *ab, const char *s, int len) { char *new = realloc(ab->b,ab->len+len); if (new == NULL) return; memcpy(new+ab->len,s,len); ab->b = new; ab->len += len; } static void abFree(struct abuf *ab) { free(ab->b); } /* Helper of refreshSingleLine() and refreshMultiLine() to show hints * to the right of the prompt. */ void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) { char seq[64]; if (hintsCallback && plen+l->len < l->cols) { int color = -1, bold = 0; char *hint = hintsCallback(l->buf,&color,&bold); if (hint) { int hintlen = strlen(hint); int hintmaxlen = l->cols-(plen+l->len); if (hintlen > hintmaxlen) hintlen = hintmaxlen; if (bold == 1 && color == -1) color = 37; if (color != -1 || bold != 0) snprintf(seq,64,"\033[%d;%d;49m",bold,color); else seq[0] = '\0'; abAppend(ab,seq,strlen(seq)); abAppend(ab,hint,hintlen); if (color != -1 || bold != 0) abAppend(ab,"\033[0m",4); /* Call the function to free the hint returned. */ if (freeHintsCallback) freeHintsCallback(hint); } } } /* Single line low level line refresh. * * Rewrite the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static void refreshSingleLine(struct linenoiseState *l) { char seq[64]; size_t plen = strlen(l->prompt); int fd = l->ofd; char *buf = l->buf; size_t len = l->len; size_t pos = l->pos; struct abuf ab; while((plen+pos) >= l->cols) { buf++; len--; pos--; } while (plen+len > l->cols) { len--; } abInit(&ab); /* Cursor to left edge */ snprintf(seq,64,"\r"); abAppend(&ab,seq,strlen(seq)); /* Write the prompt and the current buffer content */ abAppend(&ab,l->prompt,strlen(l->prompt)); if (maskmode == 1) { while (len--) abAppend(&ab,"*",1); } else { abAppend(&ab,buf,len); } /* Show hits if any. */ refreshShowHints(&ab,l,plen); /* Erase to right */ snprintf(seq,64,"\x1b[0K"); abAppend(&ab,seq,strlen(seq)); /* Move cursor to original position. */ snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen)); abAppend(&ab,seq,strlen(seq)); if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ abFree(&ab); } /* Multi line low level line refresh. * * Rewrite the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static void refreshMultiLine(struct linenoiseState *l) { char seq[64]; int plen = strlen(l->prompt); int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */ int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */ int rpos2; /* rpos after refresh. */ int col; /* colum position, zero-based. */ int old_rows = l->maxrows; int fd = l->ofd, j; struct abuf ab; /* Update maxrows if needed. */ if (rows > (int)l->maxrows) l->maxrows = rows; /* First step: clear all the lines used before. To do so start by * going to the last row. */ abInit(&ab); if (old_rows-rpos > 0) { lndebug("go down %d", old_rows-rpos); snprintf(seq,64,"\x1b[%dB", old_rows-rpos); abAppend(&ab,seq,strlen(seq)); } /* Now for every row clear it, go up. */ for (j = 0; j < old_rows-1; j++) { lndebug("clear+up"); snprintf(seq,64,"\r\x1b[0K\x1b[1A"); abAppend(&ab,seq,strlen(seq)); } /* Clean the top line. */ lndebug("clear"); snprintf(seq,64,"\r\x1b[0K"); abAppend(&ab,seq,strlen(seq)); /* Write the prompt and the current buffer content */ abAppend(&ab,l->prompt,strlen(l->prompt)); if (maskmode == 1) { unsigned int i; for (i = 0; i < l->len; i++) abAppend(&ab,"*",1); } else { abAppend(&ab,l->buf,l->len); } /* Show hits if any. */ refreshShowHints(&ab,l,plen); /* If we are at the very end of the screen with our prompt, we need to * emit a newline and move the prompt to the first column. */ if (l->pos && l->pos == l->len && (l->pos+plen) % l->cols == 0) { lndebug("<newline>"); abAppend(&ab,"\n",1); snprintf(seq,64,"\r"); abAppend(&ab,seq,strlen(seq)); rows++; if (rows > (int)l->maxrows) l->maxrows = rows; } /* Move cursor to right position. */ rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */ lndebug("rpos2 %d", rpos2); /* Go up till we reach the expected positon. */ if (rows-rpos2 > 0) { lndebug("go-up %d", rows-rpos2); snprintf(seq,64,"\x1b[%dA", rows-rpos2); abAppend(&ab,seq,strlen(seq)); } /* Set column. */ col = (plen+(int)l->pos) % (int)l->cols; lndebug("set col %d", 1+col); if (col) snprintf(seq,64,"\r\x1b[%dC", col); else snprintf(seq,64,"\r"); abAppend(&ab,seq,strlen(seq)); lndebug("\n"); l->oldpos = l->pos; if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ abFree(&ab); } /* Calls the two low level functions refreshSingleLine() or * refreshMultiLine() according to the selected mode. */ static void refreshLine(struct linenoiseState *l) { if (mlmode) refreshMultiLine(l); else refreshSingleLine(l); } /* Insert the character 'c' at cursor current position. * * On error writing to the terminal -1 is returned, otherwise 0. */ int linenoiseEditInsert(struct linenoiseState *l, char c) { if (l->len < l->buflen) { if (l->len == l->pos) { l->buf[l->pos] = c; l->pos++; l->len++; l->buf[l->len] = '\0'; if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) { /* Avoid a full update of the line in the * trivial case. */ char d = (maskmode==1) ? '*' : c; if (write(l->ofd,&d,1) == -1) return -1; } else { refreshLine(l); } } else { memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos); l->buf[l->pos] = c; l->len++; l->pos++; l->buf[l->len] = '\0'; refreshLine(l); } } return 0; } /* Move cursor on the left. */ void linenoiseEditMoveLeft(struct linenoiseState *l) { if (l->pos > 0) { l->pos--; refreshLine(l); } } /* Move cursor on the right. */ void linenoiseEditMoveRight(struct linenoiseState *l) { if (l->pos != l->len) { l->pos++; refreshLine(l); } } /* Move cursor to the start of the line. */ void linenoiseEditMoveHome(struct linenoiseState *l) { if (l->pos != 0) { l->pos = 0; refreshLine(l); } } /* Move cursor to the end of the line. */ void linenoiseEditMoveEnd(struct linenoiseState *l) { if (l->pos != l->len) { l->pos = l->len; refreshLine(l); } } /* Substitute the currently edited line with the next or previous history * entry as specified by 'dir'. */ #define LINENOISE_HISTORY_NEXT 0 #define LINENOISE_HISTORY_PREV 1 void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) { if (history_len > 1) { /* Update the current history entry before to * overwrite it with the next one. */ free(history[history_len - 1 - l->history_index]); history[history_len - 1 - l->history_index] = strdup(l->buf); /* Show the new entry */ l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; if (l->history_index < 0) { l->history_index = 0; return; } else if (l->history_index >= history_len) { l->history_index = history_len-1; return; } strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen); l->buf[l->buflen-1] = '\0'; l->len = l->pos = strlen(l->buf); refreshLine(l); } } /* Delete the character at the right of the cursor without altering the cursor * position. Basically this is what happens with the "Delete" keyboard key. */ void linenoiseEditDelete(struct linenoiseState *l) { if (l->len > 0 && l->pos < l->len) { memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1); l->len--; l->buf[l->len] = '\0'; refreshLine(l); } } /* Backspace implementation. */ void linenoiseEditBackspace(struct linenoiseState *l) { if (l->pos > 0 && l->len > 0) { memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos); l->pos--; l->len--; l->buf[l->len] = '\0'; refreshLine(l); } } /* Delete the previosu word, maintaining the cursor at the start of the * current word. */ void linenoiseEditDeletePrevWord(struct linenoiseState *l) { size_t old_pos = l->pos; size_t diff; while (l->pos > 0 && l->buf[l->pos-1] == ' ') l->pos--; while (l->pos > 0 && l->buf[l->pos-1] != ' ') l->pos--; diff = old_pos - l->pos; memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); l->len -= diff; refreshLine(l); } /* This function is the core of the line editing capability of linenoise. * It expects 'fd' to be already in "raw mode" so that every key pressed * will be returned ASAP to read(). * * The resulting string is put into 'buf' when the user type enter, or * when ctrl+d is typed. * * The function returns the length of the current buffer. */ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) { struct linenoiseState l; /* Populate the linenoise state that we pass to functions implementing * specific editing functionalities. */ l.ifd = stdin_fd; l.ofd = stdout_fd; l.buf = buf; l.buflen = buflen; l.prompt = prompt; l.plen = strlen(prompt); l.oldpos = l.pos = 0; l.len = 0; l.cols = getColumns(stdin_fd, stdout_fd); l.maxrows = 0; l.history_index = 0; /* Buffer starts empty. */ l.buf[0] = '\0'; l.buflen--; /* Make sure there is always space for the nulterm */ /* The latest history entry is always our current buffer, that * initially is just an empty string. */ linenoiseHistoryAdd(""); if (write(l.ofd,prompt,l.plen) == -1) return -1; while(1) { char c; int nread; char seq[3]; nread = read(l.ifd,&c,1); if (nread <= 0) return l.len; /* Only autocomplete when the callback is set. It returns < 0 when * there was an error reading from fd. Otherwise it will return the * character that should be handled next. */ if (c == 9 && completionCallback != NULL) { c = completeLine(&l); /* Return on errors */ if (c < 0) return l.len; /* Read next character when 0 */ if (c == 0) continue; } switch(c) { case ENTER: /* enter */ history_len--; free(history[history_len]); if (mlmode) linenoiseEditMoveEnd(&l); if (hintsCallback) { /* Force a refresh without hints to leave the previous * line as the user typed it after a newline. */ linenoiseHintsCallback *hc = hintsCallback; hintsCallback = NULL; refreshLine(&l); hintsCallback = hc; } return (int)l.len; case CTRL_C: /* ctrl-c */ errno = EAGAIN; return -1; case BACKSPACE: /* backspace */ case 8: /* ctrl-h */ linenoiseEditBackspace(&l); break; case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the line is empty, act as end-of-file. */ if (l.len > 0) { linenoiseEditDelete(&l); } else { history_len--; free(history[history_len]); return -1; } break; case CTRL_T: /* ctrl-t, swaps current character with previous. */ if (l.pos > 0 && l.pos < l.len) { int aux = buf[l.pos-1]; buf[l.pos-1] = buf[l.pos]; buf[l.pos] = aux; if (l.pos != l.len-1) l.pos++; refreshLine(&l); } break; case CTRL_B: /* ctrl-b */ linenoiseEditMoveLeft(&l); break; case CTRL_F: /* ctrl-f */ linenoiseEditMoveRight(&l); break; case CTRL_P: /* ctrl-p */ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); break; case CTRL_N: /* ctrl-n */ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); break; case ESC: /* escape sequence */ /* Read the next two bytes representing the escape sequence. * Use two calls to handle slow terminals returning the two * chars at different times. */ if (read(l.ifd,seq,1) == -1) break; if (read(l.ifd,seq+1,1) == -1) break; /* ESC [ sequences. */ if (seq[0] == '[') { if (seq[1] >= '0' && seq[1] <= '9') { /* Extended escape, read additional byte. */ if (read(l.ifd,seq+2,1) == -1) break; if (seq[2] == '~') { switch(seq[1]) { case '3': /* Delete key. */ linenoiseEditDelete(&l); break; } } } else { switch(seq[1]) { case 'A': /* Up */ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); break; case 'B': /* Down */ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); break; case 'C': /* Right */ linenoiseEditMoveRight(&l); break; case 'D': /* Left */ linenoiseEditMoveLeft(&l); break; case 'H': /* Home */ linenoiseEditMoveHome(&l); break; case 'F': /* End*/ linenoiseEditMoveEnd(&l); break; } } } /* ESC O sequences. */ else if (seq[0] == 'O') { switch(seq[1]) { case 'H': /* Home */ linenoiseEditMoveHome(&l); break; case 'F': /* End*/ linenoiseEditMoveEnd(&l); break; } } break; default: if (linenoiseEditInsert(&l,c)) return -1; break; case CTRL_U: /* Ctrl+u, delete the whole line. */ buf[0] = '\0'; l.pos = l.len = 0; refreshLine(&l); break; case CTRL_K: /* Ctrl+k, delete from current to end of line. */ buf[l.pos] = '\0'; l.len = l.pos; refreshLine(&l); break; case CTRL_A: /* Ctrl+a, go to the start of the line */ linenoiseEditMoveHome(&l); break; case CTRL_E: /* ctrl+e, go to the end of the line */ linenoiseEditMoveEnd(&l); break; case CTRL_L: /* ctrl+l, clear screen */ linenoiseClearScreen(); refreshLine(&l); break; case CTRL_W: /* ctrl+w, delete previous word */ linenoiseEditDeletePrevWord(&l); break; } } return l.len; } /* This special mode is used by linenoise in order to print scan codes * on screen for debugging / development purposes. It is implemented * by the linenoise_example program using the --keycodes option. */ void linenoisePrintKeyCodes(void) { char quit[4]; printf("Linenoise key codes debugging mode.\n" "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); if (enableRawMode(STDIN_FILENO) == -1) return; memset(quit,' ',4); while(1) { char c; int nread; nread = read(STDIN_FILENO,&c,1); if (nread <= 0) continue; memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */ quit[sizeof(quit)-1] = c; /* Insert current char on the right. */ if (memcmp(quit,"quit",sizeof(quit)) == 0) break; printf("'%c' %02x (%d) (type quit to exit)\n", isprint(c) ? c : '?', (int)c, (int)c); printf("\r"); /* Go left edge manually, we are in raw mode. */ fflush(stdout); } disableRawMode(STDIN_FILENO); } /* This function calls the line editing function linenoiseEdit() using * the STDIN file descriptor set in raw mode. */ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { int count; if (buflen == 0) { errno = EINVAL; return -1; } if (enableRawMode(STDIN_FILENO) == -1) return -1; count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); disableRawMode(STDIN_FILENO); printf("\n"); return count; } /* This function is called when linenoise() is called with the standard * input file descriptor not attached to a TTY. So for example when the * program using linenoise is called in pipe or with a file redirected * to its standard input. In this case, we want to be able to return the * line regardless of its length (by default we are limited to 4k). */ static char *linenoiseNoTTY(void) { char *line = NULL; size_t len = 0, maxlen = 0; while(1) { int c; if (len == maxlen) { char *oldval = line; if (maxlen == 0) maxlen = 16; maxlen *= 2; line = realloc(line,maxlen); if (line == NULL) { if (oldval) free(oldval); return NULL; } } c = fgetc(stdin); if (c == EOF || c == '\n') { if (c == EOF && len == 0) { free(line); return NULL; } else { line[len] = '\0'; return line; } } else { line[len] = c; len++; } } } /* The high level function that is the main API of the linenoise library. * This function checks if the terminal has basic capabilities, just checking * for a blacklist of stupid terminals, and later either calls the line * editing function or uses dummy fgets() so that you will be able to type * something even in the most desperate of the conditions. */ char *linenoise(const char *prompt) { char buf[LINENOISE_MAX_LINE]; int count; if (!isatty(STDIN_FILENO)) { /* Not a tty: read from file / pipe. In this mode we don't want any * limit to the line size, so we call a function to handle that. */ return linenoiseNoTTY(); } else if (isUnsupportedTerm()) { size_t len; printf("%s",prompt); fflush(stdout); if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; len = strlen(buf); while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { len--; buf[len] = '\0'; } return strdup(buf); } else { count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); if (count == -1) return NULL; return strdup(buf); } } /* This is just a wrapper the user may want to call in order to make sure * the linenoise returned buffer is freed with the same allocator it was * created with. Useful when the main program is using an alternative * allocator. */ void linenoiseFree(void *ptr) { free(ptr); } /* ================================ History ================================= */ /* Free the history, but does not reset it. Only used when we have to * exit() to avoid memory leaks are reported by valgrind & co. */ static void freeHistory(void) { if (history) { int j; for (j = 0; j < history_len; j++) free(history[j]); free(history); } } /* At exit we'll try to fix the terminal to the initial conditions. */ static void linenoiseAtExit(void) { disableRawMode(STDIN_FILENO); freeHistory(); } /* This is the API call to add a new entry in the linenoise history. * It uses a fixed array of char pointers that are shifted (memmoved) * when the history max length is reached in order to remove the older * entry and make room for the new one, so it is not exactly suitable for huge * histories, but will work well for a few hundred of entries. * * Using a circular buffer is smarter, but a bit more complex to handle. */ int linenoiseHistoryAdd(const char *line) { char *linecopy; if (history_max_len == 0) return 0; /* Initialization on first call. */ if (history == NULL) { history = malloc(sizeof(char*)*history_max_len); if (history == NULL) return 0; memset(history,0,(sizeof(char*)*history_max_len)); } /* Don't add duplicated lines. */ if (history_len && !strcmp(history[history_len-1], line)) return 0; /* Add an heap allocated copy of the line in the history. * If we reached the max length, remove the older line. */ linecopy = strdup(line); if (!linecopy) return 0; if (history_len == history_max_len) { free(history[0]); memmove(history,history+1,sizeof(char*)*(history_max_len-1)); history_len--; } history[history_len] = linecopy; history_len++; return 1; } /* Set the maximum length for the history. This function can be called even * if there is already some history, the function will make sure to retain * just the latest 'len' elements if the new history length value is smaller * than the amount of items already inside the history. */ int linenoiseHistorySetMaxLen(int len) { char **new; if (len < 1) return 0; if (history) { int tocopy = history_len; new = malloc(sizeof(char*)*len); if (new == NULL) return 0; /* If we can't copy everything, free the elements we'll not use. */ if (len < tocopy) { int j; for (j = 0; j < tocopy-len; j++) free(history[j]); tocopy = len; } memset(new,0,sizeof(char*)*len); memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy); free(history); history = new; } history_max_len = len; if (history_len > history_max_len) history_len = history_max_len; return 1; } /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ int linenoiseHistorySave(const char *filename) { mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO); FILE *fp; int j; fp = fopen(filename,"w"); umask(old_umask); if (fp == NULL) return -1; chmod(filename,S_IRUSR|S_IWUSR); for (j = 0; j < history_len; j++) fprintf(fp,"%s\n",history[j]); fclose(fp); return 0; } /* Load the history from the specified file. If the file does not exist * zero is returned and no operation is performed. * * If the file exists and the operation succeeded 0 is returned, otherwise * on error -1 is returned. */ int linenoiseHistoryLoad(const char *filename) { FILE *fp = fopen(filename,"r"); char buf[LINENOISE_MAX_LINE]; if (fp == NULL) return -1; while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { char *p; p = strchr(buf,'\r'); if (!p) p = strchr(buf,'\n'); if (p) *p = '\0'; linenoiseHistoryAdd(buf); } fclose(fp); return 0; } |
Added extsrc/linenoise.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* linenoise.h -- VERSION 1.0 * * Guerrilla line editing library against the idea that a line editing lib * needs to be 20,000 lines of C code. * * See linenoise.c for more information. * * ------------------------------------------------------------------------ * * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * HOLDER 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. */ #ifndef __LINENOISE_H #define __LINENOISE_H #ifdef __cplusplus extern "C" { #endif typedef struct linenoiseCompletions { size_t len; char **cvec; } linenoiseCompletions; typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); typedef void(linenoiseFreeHintsCallback)(void *); void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); void linenoiseSetHintsCallback(linenoiseHintsCallback *); void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); void linenoiseAddCompletion(linenoiseCompletions *, const char *); char *linenoise(const char *prompt); void linenoiseFree(void *ptr); int linenoiseHistoryAdd(const char *line); int linenoiseHistorySetMaxLen(int len); int linenoiseHistorySave(const char *filename); int linenoiseHistoryLoad(const char *filename); void linenoiseClearScreen(void); void linenoiseSetMultiLine(int ml); void linenoisePrintKeyCodes(void); void linenoiseMaskModeEnable(void); void linenoiseMaskModeDisable(void); #ifdef __cplusplus } #endif #endif /* __LINENOISE_H */ |
Added extsrc/pikchr-worker.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* 2022-05-20 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: * May you do good and not evil. * May you find forgiveness for yourself and forgive others. * May you share freely, never taking more than you give. *********************************************************************** This is a JS Worker file for use with the pikchr wasm build. It loads the pikchr wasm module and offers access to it via the Worker message-passing interface. Because we can have only a single message handler, as opposed to an arbitrary number of discrete event listeners like with DOM elements, we have to define a lower-level message API. Messages abstractly look like: { type: string, data: type-specific value } Where 'type' is used for dispatching and 'data' is a 'type'-dependent value. The 'type' values expected by each side of the main/worker connection vary. The types are described below but subject to change at any time as this experiment evolves. Main-to-Worker message types: - pikchr: data=pikchr-format text to render or an object: { pikchr: source code for the pikchr, darkMode: boolean true to adjust colors for a dark color scheme, cssClass: CSS class name to add to the SVG } Workers-to-Main types - stdout, stderr: indicate stdout/stderr output from the wasm layer. The data property is the string of the output, noting that the emscripten binding emits these one line at a time. Thus, if a C-side puts() emits multiple lines in a single call, the JS side will see that as multiple calls. Example: {type:'stdout', data: 'Hi, world.'} - module: Status text. This is intended to alert the main thread about module loading status so that, e.g., the main thread can update a progress widget and DTRT when the module is finished loading and available for work. Status messages come in the form {type:'module', data:{ type:'status', data: {text:string|null, step:1-based-integer} } with an incrementing step value for each subsequent message. When the module loading is complete, a message with a text value of null is posted. - pikchr: {type: 'pikchr', data:{ pikchr: input text, result: rendered result (SVG on success, HTML on error), isError: bool, true if .pikchr holds an error report, flags: integer: flags used to configure the pikchr rendering, width: if !isError, width (integer pixels) of the SVG, height: if !isError, height (integer pixels) of the SVG } } */ "use strict"; (function(){ /** Posts a message in the form {type,data} unless passed more than 2 args, in which case it posts {type, data:[arg1...argN]}. */ const wMsg = function(type,data){ postMessage({ type, data: arguments.length<3 ? data : Array.prototype.slice.call(arguments,1) }); }; const stderr = function(){wMsg('stderr', Array.prototype.slice.call(arguments));}; self.onerror = function(/*message, source, lineno, colno, error*/) { const err = arguments[4]; if(err && 'ExitStatus'==err.name){ /* This "cannot happen" for this wasm binding, but just in case... */ pikchrModule.isDead = true; stderr("FATAL ERROR:", err.message); stderr("Restarting the app requires reloading the page."); wMsg('error', err); } pikchrModule.setStatus('Exception thrown, see JavaScript console: '+err); }; self.onmessage = function f(ev){ ev = ev.data; switch(ev.type){ /** Runs the given text through pikchr and emits a 'pikchr' message result (output format documented above). Fires a working/start event before it starts and working/end event when it finishes. */ case 'pikchr': if(pikchrModule.isDead){ stderr("wasm module has exit()ed. Cannot pikchr."); return; } if(!f._){ f._ = pikchrModule.cwrap('pikchr', 'string', [ 'string'/*script*/, 'string'/*CSS class*/, 'number'/*flags*/, 'number'/*output: SVG width*/, 'number'/*output: SVG height*/ ]); } wMsg('working','start'); const stack = pikchrModule.stackSave(); try { const pnWidth = pikchrModule.stackAlloc(4), pnHeight = pikchrModule.stackAlloc(4); let script = '', flags = 0, cssClass = null; if('string'===typeof ev.data){ script = ev.data; }else if(ev.data && 'object'===typeof ev.data){ script = ev.data.pikchr; flags = ev.data.darkMode ? 0x02 : 0; if(ev.data.cssClass) cssClass = ev.data.cssClass; } pikchrModule.setValue(pnWidth, 0, "i32"); pikchrModule.setValue(pnHeight, 0, "i32"); const msg = { pikchr: script, result: (f._(script, cssClass, flags, pnWidth, pnHeight) || "").trim(), flags: flags }; msg.isError = !!(msg.result && msg.result.startsWith('<div')); if(msg.isError){ msg.width = msg.height = null; }else{ msg.width = pikchrModule.getValue(pnWidth, "i32"); msg.height = pikchrModule.getValue(pnHeight, "i32"); } wMsg('pikchr', msg); } finally { pikchrModule.stackRestore(stack); wMsg('working','end'); } return; }; console.warn("Unknown pikchr-worker message type:",ev); }; /** emscripten module for use with build mode -sMODULARIZE. */ const pikchrModule = { print: function(){wMsg('stdout', Array.prototype.slice.call(arguments));}, printErr: stderr, /** Intercepts status updates from the emscripting module init and fires worker events with a type of 'status' and a payload of: { text: string | null, // null at end of load process step: integer // starts at 1, increments 1 per call } We have no way of knowing in advance how many steps will be processed/posted, so creating a "percentage done" view is not really practical. One can be approximated by giving it a current value of message.step and max value of message.step+1, though. When work is finished, a message with a text value of null is submitted. After a message with text==null is posted, the module may later post messages about fatal problems, e.g. an exit() being triggered, so it is recommended that UI elements for posting status messages not be outright removed from the DOM when text==null, and that they instead be hidden until/unless text!=null. */ setStatus: function f(text){ if(!f.last) f.last = { step: 0, text: '' }; else if(text === f.last.text) return; f.last.text = text; wMsg('module',{ type:'status', data:{step: ++f.last.step, text: text||null} }); } }; importScripts('pikchr.js'); /** initPikchrModule() is installed via pikchr.js due to building with: emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule */ initPikchrModule(pikchrModule).then(function(thisModule){ wMsg('pikchr-ready'); }); })(); |
Added extsrc/pikchr.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 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 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 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 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 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 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 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 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 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 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 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 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 5433 5434 5435 5436 5437 5438 5439 5440 5441 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 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 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 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 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 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 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 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 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 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 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 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 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 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 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 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 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 6189 6190 6191 6192 6193 6194 6195 6196 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 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 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 6278 6279 6280 6281 6282 6283 6284 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 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 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 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 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 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 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 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 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 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 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 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 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 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 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 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 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 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 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 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 7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 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 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 | /* This file is automatically generated by Lemon from input grammar ** source file "pikchr.y". */ /* ** Zero-Clause BSD license: ** ** Copyright (C) 2020-09-01 by D. Richard Hipp <drh@sqlite.org> ** ** Permission to use, copy, modify, and/or distribute this software for ** any purpose with or without fee is hereby granted. ** **************************************************************************** ** ** This software translates a PIC-inspired diagram language into SVG. ** ** PIKCHR (pronounced like "picture") is *mostly* backwards compatible ** with legacy PIC, though some features of legacy PIC are removed ** (for example, the "sh" command is removed for security) and ** many enhancements are added. ** ** PIKCHR is designed for use in an internet facing web environment. ** In particular, PIKCHR is designed to safely generate benign SVG from ** source text that provided by a hostile agent. ** ** This code was originally written by D. Richard Hipp using documentation ** from prior PIC implementations but without reference to prior code. ** All of the code in this project is original. ** ** This file implements a C-language subroutine that accepts a string ** of PIKCHR language text and generates a second string of SVG output that ** renders the drawing defined by the input. Space to hold the returned ** string is obtained from malloc() and should be freed by the caller. ** NULL might be returned if there is a memory allocation error. ** ** If there are errors in the PIKCHR input, the output will consist of an ** error message and the original PIKCHR input text (inside of <pre>...</pre>). ** ** The subroutine implemented by this file is intended to be stand-alone. ** It uses no external routines other than routines commonly found in ** the standard C library. ** **************************************************************************** ** COMPILING: ** ** The original source text is a mixture of C99 and "Lemon" ** (See https://sqlite.org/src/file/doc/lemon.html). Lemon is an LALR(1) ** parser generator program, similar to Yacc. The grammar of the ** input language is specified in Lemon. C-code is attached. Lemon ** runs to generate a single output file ("pikchr.c") which is then ** compiled to generate the Pikchr library. This header comment is ** preserved in the Lemon output, so you might be reading this in either ** the generated "pikchr.c" file that is output by Lemon, or in the ** "pikchr.y" source file that is input into Lemon. If you make changes, ** you should change the input source file "pikchr.y", not the ** Lemon-generated output file. ** ** Basic compilation steps: ** ** lemon pikchr.y ** cc pikchr.c -o pikchr.o ** ** Add -DPIKCHR_SHELL to add a main() routine that reads input files ** and sends them through Pikchr, for testing. Add -DPIKCHR_FUZZ for ** -fsanitizer=fuzzer testing. ** **************************************************************************** ** IMPLEMENTATION NOTES (for people who want to understand the internal ** operation of this software, perhaps to extend the code or to fix bugs): ** ** Each call to pikchr() uses a single instance of the Pik structure to ** track its internal state. The Pik structure lives for the duration ** of the pikchr() call. ** ** The input is a sequence of objects or "statements". Each statement is ** parsed into a PObj object. These are stored on an extensible array ** called PList. All parameters to each PObj are computed as the ** object is parsed. (Hence, the parameters to a PObj may only refer ** to prior statements.) Once the PObj is completely assembled, it is ** added to the end of a PList and never changes thereafter - except, ** PObj objects that are part of a "[...]" block might have their ** absolute position shifted when the outer [...] block is positioned. ** But apart from this repositioning, PObj objects are unchanged once ** they are added to the list. The order of statements on a PList does ** not change. ** ** After all input has been parsed, the top-level PList is walked to ** generate output. Sub-lists resulting from [...] blocks are scanned ** as they are encountered. All input must be collected and parsed ahead ** of output generation because the size and position of statements must be ** known in order to compute a bounding box on the output. ** ** Each PObj is on a "layer". (The common case is that all PObj's are ** on a single layer, but multiple layers are possible.) A separate pass ** is made through the list for each layer. ** ** After all output is generated, the Pik object and all the PList ** and PObj objects are deallocated and the generated output string is ** returned. Upon any error, the Pik.nErr flag is set, processing quickly ** stops, and the stack unwinds. No attempt is made to continue reading ** input after an error. ** ** Most statements begin with a class name like "box" or "arrow" or "move". ** There is a class named "text" which is used for statements that begin ** with a string literal. You can also specify the "text" class. ** A Sublist ("[...]") is a single object that contains a pointer to ** its substatements, all gathered onto a separate PList object. ** ** Variables go into PVar objects that form a linked list. ** ** Each PObj has zero or one names. Input constructs that attempt ** to assign a new name from an older name, for example: ** ** Abc: Abc + (0.5cm, 0) ** ** Statements like these generate a new "noop" object at the specified ** place and with the given name. As place-names are searched by scanning ** the list in reverse order, this has the effect of overriding the "Abc" ** name when referenced by subsequent objects. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <math.h> #include <assert.h> #define count(X) (sizeof(X)/sizeof(X[0])) #ifndef M_PI # define M_PI 3.1415926535897932385 #endif /* Limit the number of tokens in a single script to avoid run-away ** macro expansion attacks. See forum post ** https://pikchr.org/home/forumpost/ef8684c6955a411a */ #ifndef PIKCHR_TOKEN_LIMIT # define PIKCHR_TOKEN_LIMIT 100000 #endif /* Tag intentionally unused parameters with this macro to prevent ** compiler warnings with -Wextra */ #define UNUSED_PARAMETER(X) (void)(X) typedef struct Pik Pik; /* Complete parsing context */ typedef struct PToken PToken; /* A single token */ typedef struct PObj PObj; /* A single diagram object */ typedef struct PList PList; /* A list of diagram objects */ typedef struct PClass PClass; /* Description of statements types */ typedef double PNum; /* Numeric value */ typedef struct PRel PRel; /* Absolute or percentage value */ typedef struct PPoint PPoint; /* A position in 2-D space */ typedef struct PVar PVar; /* script-defined variable */ typedef struct PBox PBox; /* A bounding box */ typedef struct PMacro PMacro; /* A "define" macro */ /* Compass points */ #define CP_N 1 #define CP_NE 2 #define CP_E 3 #define CP_SE 4 #define CP_S 5 #define CP_SW 6 #define CP_W 7 #define CP_NW 8 #define CP_C 9 /* .center or .c */ #define CP_END 10 /* .end */ #define CP_START 11 /* .start */ /* Heading angles corresponding to compass points */ static const PNum pik_hdg_angle[] = { /* none */ 0.0, /* N */ 0.0, /* NE */ 45.0, /* E */ 90.0, /* SE */ 135.0, /* S */ 180.0, /* SW */ 225.0, /* W */ 270.0, /* NW */ 315.0, /* C */ 0.0, }; /* Built-in functions */ #define FN_ABS 0 #define FN_COS 1 #define FN_INT 2 #define FN_MAX 3 #define FN_MIN 4 #define FN_SIN 5 #define FN_SQRT 6 /* Text position and style flags. Stored in PToken.eCode so limited ** to 15 bits. */ #define TP_LJUST 0x0001 /* left justify...... */ #define TP_RJUST 0x0002 /* ...Right justify */ #define TP_JMASK 0x0003 /* Mask for justification bits */ #define TP_ABOVE2 0x0004 /* Position text way above PObj.ptAt */ #define TP_ABOVE 0x0008 /* Position text above PObj.ptAt */ #define TP_CENTER 0x0010 /* On the line */ #define TP_BELOW 0x0020 /* Position text below PObj.ptAt */ #define TP_BELOW2 0x0040 /* Position text way below PObj.ptAt */ #define TP_VMASK 0x007c /* Mask for text positioning flags */ #define TP_BIG 0x0100 /* Larger font */ #define TP_SMALL 0x0200 /* Smaller font */ #define TP_XTRA 0x0400 /* Amplify TP_BIG or TP_SMALL */ #define TP_SZMASK 0x0700 /* Font size mask */ #define TP_ITALIC 0x1000 /* Italic font */ #define TP_BOLD 0x2000 /* Bold font */ #define TP_MONO 0x4000 /* Monospace font family */ #define TP_FMASK 0x7000 /* Mask for font style */ #define TP_ALIGN 0x8000 /* Rotate to align with the line */ /* An object to hold a position in 2-D space */ struct PPoint { PNum x, y; /* X and Y coordinates */ }; static const PPoint cZeroPoint = {0.0,0.0}; /* A bounding box */ struct PBox { PPoint sw, ne; /* Lower-left and top-right corners */ }; /* An Absolute or a relative distance. The absolute distance ** is stored in rAbs and the relative distance is stored in rRel. ** Usually, one or the other will be 0.0. When using a PRel to ** update an existing value, the computation is usually something ** like this: ** ** value = PRel.rAbs + value*PRel.rRel ** */ struct PRel { PNum rAbs; /* Absolute value */ PNum rRel; /* Value relative to current value */ }; /* A variable created by the ID = EXPR construct of the PIKCHR script ** ** PIKCHR (and PIC) scripts do not use many varaibles, so it is reasonable ** to store them all on a linked list. */ struct PVar { const char *zName; /* Name of the variable */ PNum val; /* Value of the variable */ PVar *pNext; /* Next variable in a list of them all */ }; /* A single token in the parser input stream */ struct PToken { const char *z; /* Pointer to the token text */ unsigned int n; /* Length of the token in bytes */ short int eCode; /* Auxiliary code */ unsigned char eType; /* The numeric parser code */ unsigned char eEdge; /* Corner value for corner keywords */ }; /* Return negative, zero, or positive if pToken is less than, equal to ** or greater than the zero-terminated string z[] */ static int pik_token_eq(PToken *pToken, const char *z){ int c = strncmp(pToken->z,z,pToken->n); if( c==0 && z[pToken->n]!=0 ) c = -1; return c; } /* Extra token types not generated by LEMON but needed by the ** tokenizer */ #define T_PARAMETER 253 /* $1, $2, ..., $9 */ #define T_WHITESPACE 254 /* Whitespace of comments */ #define T_ERROR 255 /* Any text that is not a valid token */ /* Directions of movement */ #define DIR_RIGHT 0 #define DIR_DOWN 1 #define DIR_LEFT 2 #define DIR_UP 3 #define ValidDir(X) ((X)>=0 && (X)<=3) #define IsUpDown(X) (((X)&1)==1) #define IsLeftRight(X) (((X)&1)==0) /* Bitmask for the various attributes for PObj. These bits are ** collected in PObj.mProp and PObj.mCalc to check for constraint ** errors. */ #define A_WIDTH 0x0001 #define A_HEIGHT 0x0002 #define A_RADIUS 0x0004 #define A_THICKNESS 0x0008 #define A_DASHED 0x0010 /* Includes "dotted" */ #define A_FILL 0x0020 #define A_COLOR 0x0040 #define A_ARROW 0x0080 #define A_FROM 0x0100 #define A_CW 0x0200 #define A_AT 0x0400 #define A_TO 0x0800 /* one or more movement attributes */ #define A_FIT 0x1000 /* A single graphics object */ struct PObj { const PClass *type; /* Object type or class */ PToken errTok; /* Reference token for error messages */ PPoint ptAt; /* Reference point for the object */ PPoint ptEnter, ptExit; /* Entry and exit points */ PList *pSublist; /* Substructure for [...] objects */ char *zName; /* Name assigned to this statement */ PNum w; /* "width" property */ PNum h; /* "height" property */ PNum rad; /* "radius" property */ PNum sw; /* "thickness" property. (Mnemonic: "stroke width")*/ PNum dotted; /* "dotted" property. <=0.0 for off */ PNum dashed; /* "dashed" property. <=0.0 for off */ PNum fill; /* "fill" property. Negative for off */ PNum color; /* "color" property */ PPoint with; /* Position constraint from WITH clause */ char eWith; /* Type of heading point on WITH clause */ char cw; /* True for clockwise arc */ char larrow; /* Arrow at beginning (<- or <->) */ char rarrow; /* Arrow at end (-> or <->) */ char bClose; /* True if "close" is seen */ char bChop; /* True if "chop" is seen */ char bAltAutoFit; /* Always send both h and w into xFit() */ unsigned char nTxt; /* Number of text values */ unsigned mProp; /* Masks of properties set so far */ unsigned mCalc; /* Values computed from other constraints */ PToken aTxt[5]; /* Text with .eCode holding TP flags */ int iLayer; /* Rendering order */ int inDir, outDir; /* Entry and exit directions */ int nPath; /* Number of path points */ PPoint *aPath; /* Array of path points */ PObj *pFrom, *pTo; /* End-point objects of a path */ PBox bbox; /* Bounding box */ }; /* A list of graphics objects */ struct PList { int n; /* Number of statements in the list */ int nAlloc; /* Allocated slots in a[] */ PObj **a; /* Pointers to individual objects */ }; /* A macro definition */ struct PMacro { PMacro *pNext; /* Next in the list */ PToken macroName; /* Name of the macro */ PToken macroBody; /* Body of the macro */ int inUse; /* Do not allow recursion */ }; /* Each call to the pikchr() subroutine uses an instance of the following ** object to pass around context to all of its subroutines. */ struct Pik { unsigned nErr; /* Number of errors seen */ unsigned nToken; /* Number of tokens parsed */ PToken sIn; /* Input Pikchr-language text */ char *zOut; /* Result accumulates here */ unsigned int nOut; /* Bytes written to zOut[] so far */ unsigned int nOutAlloc; /* Space allocated to zOut[] */ unsigned char eDir; /* Current direction */ unsigned int mFlags; /* Flags passed to pikchr() */ PObj *cur; /* Object under construction */ PObj *lastRef; /* Last object references by name */ PList *list; /* Object list under construction */ PMacro *pMacros; /* List of all defined macros */ PVar *pVar; /* Application-defined variables */ PBox bbox; /* Bounding box around all statements */ /* Cache of layout values. <=0.0 for unknown... */ PNum rScale; /* Multiply to convert inches to pixels */ PNum fontScale; /* Scale fonts by this percent */ PNum charWidth; /* Character width */ PNum charHeight; /* Character height */ PNum wArrow; /* Width of arrowhead at the fat end */ PNum hArrow; /* Ht of arrowhead - dist from tip to fat end */ char bLayoutVars; /* True if cache is valid */ char thenFlag; /* True if "then" seen */ char samePath; /* aTPath copied by "same" */ const char *zClass; /* Class name for the <svg> */ int wSVG, hSVG; /* Width and height of the <svg> */ int fgcolor; /* foreground color value, or -1 for none */ int bgcolor; /* background color value, or -1 for none */ /* Paths for lines are constructed here first, then transferred into ** the PObj object at the end: */ int nTPath; /* Number of entries on aTPath[] */ int mTPath; /* For last entry, 1: x set, 2: y set */ PPoint aTPath[1000]; /* Path under construction */ /* Error contexts */ unsigned int nCtx; /* Number of error contexts */ PToken aCtx[10]; /* Nested error contexts */ }; /* Include PIKCHR_PLAINTEXT_ERRORS among the bits of mFlags on the 3rd ** argument to pikchr() in order to cause error message text to come out ** as text/plain instead of as text/html */ #define PIKCHR_PLAINTEXT_ERRORS 0x0001 /* Include PIKCHR_DARK_MODE among the mFlag bits to invert colors. */ #define PIKCHR_DARK_MODE 0x0002 /* ** The behavior of an object class is defined by an instance of ** this structure. This is the "virtual method" table. */ struct PClass { const char *zName; /* Name of class */ char isLine; /* True if a line class */ char eJust; /* Use box-style text justification */ void (*xInit)(Pik*,PObj*); /* Initializer */ void (*xNumProp)(Pik*,PObj*,PToken*); /* Value change notification */ void (*xCheck)(Pik*,PObj*); /* Checks to do after parsing */ PPoint (*xChop)(Pik*,PObj*,PPoint*); /* Chopper */ PPoint (*xOffset)(Pik*,PObj*,int); /* Offset from .c to edge point */ void (*xFit)(Pik*,PObj*,PNum w,PNum h); /* Size to fit text */ void (*xRender)(Pik*,PObj*); /* Render */ }; /* Forward declarations */ static void pik_append(Pik*, const char*,int); static void pik_append_text(Pik*,const char*,int,int); static void pik_append_num(Pik*,const char*,PNum); static void pik_append_point(Pik*,const char*,PPoint*); static void pik_append_x(Pik*,const char*,PNum,const char*); static void pik_append_y(Pik*,const char*,PNum,const char*); static void pik_append_xy(Pik*,const char*,PNum,PNum); static void pik_append_dis(Pik*,const char*,PNum,const char*); static void pik_append_arc(Pik*,PNum,PNum,PNum,PNum); static void pik_append_clr(Pik*,const char*,PNum,const char*,int); static void pik_append_style(Pik*,PObj*,int); static void pik_append_txt(Pik*,PObj*, PBox*); static void pik_draw_arrowhead(Pik*,PPoint*pFrom,PPoint*pTo,PObj*); static void pik_chop(PPoint*pFrom,PPoint*pTo,PNum); static void pik_error(Pik*,PToken*,const char*); static void pik_elist_free(Pik*,PList*); static void pik_elem_free(Pik*,PObj*); static void pik_render(Pik*,PList*); static PList *pik_elist_append(Pik*,PList*,PObj*); static PObj *pik_elem_new(Pik*,PToken*,PToken*,PList*); static void pik_set_direction(Pik*,int); static void pik_elem_setname(Pik*,PObj*,PToken*); static int pik_round(PNum); static void pik_set_var(Pik*,PToken*,PNum,PToken*); static PNum pik_value(Pik*,const char*,int,int*); static int pik_value_int(Pik*,const char*,int,int*); static PNum pik_lookup_color(Pik*,PToken*); static PNum pik_get_var(Pik*,PToken*); static PNum pik_atof(PToken*); static void pik_after_adding_attributes(Pik*,PObj*); static void pik_elem_move(PObj*,PNum dx, PNum dy); static void pik_elist_move(PList*,PNum dx, PNum dy); static void pik_set_numprop(Pik*,PToken*,PRel*); static void pik_set_clrprop(Pik*,PToken*,PNum); static void pik_set_dashed(Pik*,PToken*,PNum*); static void pik_then(Pik*,PToken*,PObj*); static void pik_add_direction(Pik*,PToken*,PRel*); static void pik_move_hdg(Pik*,PRel*,PToken*,PNum,PToken*,PToken*); static void pik_evenwith(Pik*,PToken*,PPoint*); static void pik_set_from(Pik*,PObj*,PToken*,PPoint*); static void pik_add_to(Pik*,PObj*,PToken*,PPoint*); static void pik_close_path(Pik*,PToken*); static void pik_set_at(Pik*,PToken*,PPoint*,PToken*); static short int pik_nth_value(Pik*,PToken*); static PObj *pik_find_nth(Pik*,PObj*,PToken*); static PObj *pik_find_byname(Pik*,PObj*,PToken*); static PPoint pik_place_of_elem(Pik*,PObj*,PToken*); static int pik_bbox_isempty(PBox*); static int pik_bbox_contains_point(PBox*,PPoint*); static void pik_bbox_init(PBox*); static void pik_bbox_addbox(PBox*,PBox*); static void pik_bbox_add_xy(PBox*,PNum,PNum); static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); static void pik_add_txt(Pik*,PToken*,int); static int pik_text_length(const PToken *pToken, const int isMonospace); static void pik_size_to_fit(Pik*,PToken*,int); static int pik_text_position(int,PToken*); static PNum pik_property_of(PObj*,PToken*); static PNum pik_func(Pik*,PToken*,PNum,PNum); static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt); static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt); static void pik_same(Pik *p, PObj*, PToken*); static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj); static PToken pik_next_semantic_token(PToken *pThis); static void pik_compute_layout_settings(Pik*); static void pik_behind(Pik*,PObj*); static PObj *pik_assert(Pik*,PNum,PToken*,PNum); static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); static PNum pik_dist(PPoint*,PPoint*); static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); #line 523 "pikchr.c" /**************** End of %include directives **********************************/ /* These constants specify the various numeric values for terminal symbols. ***************** Begin token definitions *************************************/ #ifndef T_ID #define T_ID 1 #define T_EDGEPT 2 #define T_OF 3 #define T_PLUS 4 #define T_MINUS 5 #define T_STAR 6 #define T_SLASH 7 #define T_PERCENT 8 #define T_UMINUS 9 #define T_EOL 10 #define T_ASSIGN 11 #define T_PLACENAME 12 #define T_COLON 13 #define T_ASSERT 14 #define T_LP 15 #define T_EQ 16 #define T_RP 17 #define T_DEFINE 18 #define T_CODEBLOCK 19 #define T_FILL 20 #define T_COLOR 21 #define T_THICKNESS 22 #define T_PRINT 23 #define T_STRING 24 #define T_COMMA 25 #define T_CLASSNAME 26 #define T_LB 27 #define T_RB 28 #define T_UP 29 #define T_DOWN 30 #define T_LEFT 31 #define T_RIGHT 32 #define T_CLOSE 33 #define T_CHOP 34 #define T_FROM 35 #define T_TO 36 #define T_THEN 37 #define T_HEADING 38 #define T_GO 39 #define T_AT 40 #define T_WITH 41 #define T_SAME 42 #define T_AS 43 #define T_FIT 44 #define T_BEHIND 45 #define T_UNTIL 46 #define T_EVEN 47 #define T_DOT_E 48 #define T_HEIGHT 49 #define T_WIDTH 50 #define T_RADIUS 51 #define T_DIAMETER 52 #define T_DOTTED 53 #define T_DASHED 54 #define T_CW 55 #define T_CCW 56 #define T_LARROW 57 #define T_RARROW 58 #define T_LRARROW 59 #define T_INVIS 60 #define T_THICK 61 #define T_THIN 62 #define T_SOLID 63 #define T_CENTER 64 #define T_LJUST 65 #define T_RJUST 66 #define T_ABOVE 67 #define T_BELOW 68 #define T_ITALIC 69 #define T_BOLD 70 #define T_MONO 71 #define T_ALIGNED 72 #define T_BIG 73 #define T_SMALL 74 #define T_AND 75 #define T_LT 76 #define T_GT 77 #define T_ON 78 #define T_WAY 79 #define T_BETWEEN 80 #define T_THE 81 #define T_NTH 82 #define T_VERTEX 83 #define T_TOP 84 #define T_BOTTOM 85 #define T_START 86 #define T_END 87 #define T_IN 88 #define T_THIS 89 #define T_DOT_U 90 #define T_LAST 91 #define T_NUMBER 92 #define T_FUNC1 93 #define T_FUNC2 94 #define T_DIST 95 #define T_DOT_XY 96 #define T_X 97 #define T_Y 98 #define T_DOT_L 99 #endif /**************** End token definitions ***************************************/ /* The next sections is a series of control #defines. ** various aspects of the generated parser. ** YYCODETYPE is the data type used to store the integer codes ** that represent terminal and non-terminal symbols. ** "unsigned char" is used if there are fewer than ** 256 symbols. Larger types otherwise. ** YYNOCODE is a number of type YYCODETYPE that is not used for ** any terminal or nonterminal symbol. ** YYFALLBACK If defined, this indicates that one or more tokens ** (also known as: "terminal symbols") have fall-back ** values which should be used if the original symbol ** would not parse. This permits keywords to sometimes ** be used as identifiers, for example. ** YYACTIONTYPE is the data type used for "action codes" - numbers ** that indicate what to do in response to the next ** token. ** pik_parserTOKENTYPE is the data type used for minor type for terminal ** symbols. Background: A "minor type" is a semantic ** value associated with a terminal or non-terminal ** symbols. For example, for an "ID" terminal symbol, ** the minor type might be the name of the identifier. ** Each non-terminal can have a different minor type. ** Terminal symbols all have the same minor type, though. ** This macros defines the minor type for terminal ** symbols. ** YYMINORTYPE is the data type used for all minor types. ** This is typically a union of many types, one of ** which is pik_parserTOKENTYPE. The entry in the union ** for terminal symbols is called "yy0". ** YYSTACKDEPTH is the maximum depth of the parser's stack. If ** zero the stack is dynamically sized using realloc() ** pik_parserARG_SDECL A static variable declaration for the %extra_argument ** pik_parserARG_PDECL A parameter declaration for the %extra_argument ** pik_parserARG_PARAM Code to pass %extra_argument as a subroutine parameter ** pik_parserARG_STORE Code to store %extra_argument into yypParser ** pik_parserARG_FETCH Code to extract %extra_argument from yypParser ** pik_parserCTX_* As pik_parserARG_ except for %extra_context ** YYREALLOC Name of the realloc() function to use ** YYFREE Name of the free() function to use ** YYDYNSTACK True if stack space should be extended on heap ** YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** YYNSTATE the combined number of states. ** YYNRULE the number of rules in the grammar ** YYNTOKEN Number of terminal symbols ** YY_MAX_SHIFT Maximum value for shift actions ** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions ** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions ** YY_ERROR_ACTION The yy_action[] code for syntax error ** YY_ACCEPT_ACTION The yy_action[] code for accept ** YY_NO_ACTION The yy_action[] code for no-op ** YY_MIN_REDUCE Minimum value for reduce actions ** YY_MAX_REDUCE Maximum value for reduce actions ** YY_MIN_DSTRCTR Minimum symbol value that has a destructor ** YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned char #define YYNOCODE 136 #define YYACTIONTYPE unsigned short int #define pik_parserTOKENTYPE PToken typedef union { int yyinit; pik_parserTOKENTYPE yy0; PNum yy21; PPoint yy63; PRel yy72; PObj* yy162; short int yy188; PList* yy235; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 #endif #define pik_parserARG_SDECL #define pik_parserARG_PDECL #define pik_parserARG_PARAM #define pik_parserARG_FETCH #define pik_parserARG_STORE #define YYREALLOC realloc #define YYFREE free #define YYDYNSTACK 0 #define pik_parserCTX_SDECL Pik *p; #define pik_parserCTX_PDECL ,Pik *p #define pik_parserCTX_PARAM ,p #define pik_parserCTX_FETCH Pik *p=yypParser->p; #define pik_parserCTX_STORE yypParser->p=p; #define YYFALLBACK 1 #define YYNSTATE 164 #define YYNRULE 156 #define YYNRULE_WITH_ACTION 116 #define YYNTOKEN 100 #define YY_MAX_SHIFT 163 #define YY_MIN_SHIFTREDUCE 287 #define YY_MAX_SHIFTREDUCE 442 #define YY_ERROR_ACTION 443 #define YY_ACCEPT_ACTION 444 #define YY_NO_ACTION 445 #define YY_MIN_REDUCE 446 #define YY_MAX_REDUCE 601 #define YY_MIN_DSTRCTR 100 #define YY_MAX_DSTRCTR 103 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) /* Define the yytestcase() macro to be a no-op if is not already defined ** otherwise. ** ** Applications can choose to define yytestcase() in the %include section ** to a macro that can assist in verifying code coverage. For production ** code the yytestcase() macro should be turned off. But it is useful ** for testing. */ #ifndef yytestcase # define yytestcase(X) #endif /* Macro to determine if stack space has the ability to grow using ** heap memory. */ #if YYSTACKDEPTH<=0 || YYDYNSTACK # define YYGROWABLESTACK 1 #else # define YYGROWABLESTACK 0 #endif /* Guarantee a minimum number of initial stack slots. */ #if YYSTACKDEPTH<=0 # undef YYSTACKDEPTH # define YYSTACKDEPTH 2 /* Need a minimum stack size */ #endif /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an ** action integer. ** ** Suppose the action integer is N. Then the action is determined as ** follows ** ** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead ** token onto the stack and goto state N. ** ** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then ** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE. ** ** N == YY_ERROR_ACTION A syntax error has occurred. ** ** N == YY_ACCEPT_ACTION The parser accepts its input. ** ** N == YY_NO_ACTION No such action. Denotes unused ** slots in the yy_action[] table. ** ** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE ** and YY_MAX_REDUCE ** ** The action table is constructed as a single large table named yy_action[]. ** Given state S and lookahead X, the action is computed as either: ** ** (A) N = yy_action[ yy_shift_ofst[S] + X ] ** (B) N = yy_default[S] ** ** The (A) formula is preferred. The B formula is used instead if ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X. ** ** The formulas above are for computing the action when the lookahead is ** a terminal symbol. If the lookahead is a non-terminal (as occurs after ** a reduce action) then the yy_reduce_ofst[] array is used in place of ** the yy_shift_ofst[] array. ** ** The following are the tables generated in this section: ** ** yy_action[] A single table containing all actions. ** yy_lookahead[] A table containing the lookahead for each entry in ** yy_action. Used to detect hash collisions. ** yy_shift_ofst[] For each state, the offset into yy_action for ** shifting terminals. ** yy_reduce_ofst[] For each state, the offset into yy_action for ** shifting non-terminals after a reduce. ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ #define YY_ACTTAB_COUNT (1313) static const YYACTIONTYPE yy_action[] = { /* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148, /* 10 */ 575, 492, 161, 119, 453, 113, 120, 161, 119, 530, /* 20 */ 427, 428, 339, 559, 81, 30, 560, 561, 575, 64, /* 30 */ 63, 62, 61, 322, 323, 9, 8, 33, 149, 32, /* 40 */ 7, 71, 127, 38, 335, 66, 48, 37, 28, 339, /* 50 */ 339, 339, 339, 425, 426, 340, 341, 342, 343, 344, /* 60 */ 345, 346, 347, 348, 474, 528, 161, 119, 577, 77, /* 70 */ 577, 73, 306, 148, 474, 533, 161, 119, 112, 113, /* 80 */ 120, 161, 119, 128, 427, 428, 339, 31, 81, 531, /* 90 */ 161, 119, 474, 35, 330, 378, 158, 322, 323, 9, /* 100 */ 8, 33, 149, 32, 7, 71, 127, 328, 335, 66, /* 110 */ 579, 378, 158, 339, 339, 339, 339, 425, 426, 340, /* 120 */ 341, 342, 343, 344, 345, 346, 347, 348, 394, 435, /* 130 */ 46, 59, 60, 64, 63, 62, 61, 357, 36, 376, /* 140 */ 54, 51, 2, 47, 403, 13, 297, 411, 412, 413, /* 150 */ 414, 80, 162, 308, 79, 133, 310, 126, 441, 440, /* 160 */ 118, 123, 83, 404, 405, 406, 408, 80, 84, 308, /* 170 */ 79, 299, 411, 412, 413, 414, 118, 69, 350, 350, /* 180 */ 350, 350, 350, 350, 350, 350, 350, 350, 350, 62, /* 190 */ 61, 434, 64, 63, 62, 61, 313, 398, 399, 427, /* 200 */ 428, 339, 380, 157, 64, 63, 62, 61, 122, 106, /* 210 */ 535, 436, 437, 438, 439, 298, 375, 391, 117, 393, /* 220 */ 155, 154, 153, 394, 435, 49, 59, 60, 339, 339, /* 230 */ 339, 339, 425, 426, 376, 3, 4, 2, 64, 63, /* 240 */ 62, 61, 156, 156, 156, 394, 379, 159, 59, 60, /* 250 */ 76, 67, 535, 441, 440, 5, 102, 6, 535, 42, /* 260 */ 131, 535, 69, 107, 301, 302, 303, 394, 305, 15, /* 270 */ 59, 60, 120, 161, 119, 446, 463, 424, 376, 423, /* 280 */ 1, 42, 397, 78, 78, 36, 434, 11, 394, 435, /* 290 */ 356, 59, 60, 12, 152, 139, 432, 14, 16, 376, /* 300 */ 18, 65, 2, 138, 106, 430, 436, 437, 438, 439, /* 310 */ 44, 375, 19, 117, 393, 155, 154, 153, 441, 440, /* 320 */ 142, 140, 64, 63, 62, 61, 106, 20, 68, 376, /* 330 */ 359, 107, 23, 375, 45, 117, 393, 155, 154, 153, /* 340 */ 120, 161, 119, 55, 463, 114, 26, 57, 106, 147, /* 350 */ 146, 434, 569, 58, 392, 375, 43, 117, 393, 155, /* 360 */ 154, 153, 152, 384, 64, 63, 62, 61, 382, 106, /* 370 */ 383, 436, 437, 438, 439, 377, 375, 70, 117, 393, /* 380 */ 155, 154, 153, 160, 39, 22, 21, 445, 142, 140, /* 390 */ 64, 63, 62, 61, 24, 17, 145, 141, 431, 108, /* 400 */ 445, 445, 445, 391, 445, 445, 375, 445, 117, 445, /* 410 */ 445, 55, 74, 445, 148, 445, 445, 147, 146, 124, /* 420 */ 113, 120, 161, 119, 43, 445, 445, 142, 140, 64, /* 430 */ 63, 62, 61, 445, 394, 445, 445, 59, 60, 64, /* 440 */ 63, 62, 61, 149, 445, 376, 445, 445, 42, 445, /* 450 */ 55, 445, 391, 22, 21, 445, 147, 146, 445, 445, /* 460 */ 52, 445, 24, 43, 145, 141, 431, 394, 445, 445, /* 470 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 132, /* 480 */ 130, 42, 445, 445, 445, 355, 156, 156, 156, 445, /* 490 */ 445, 445, 22, 21, 445, 394, 473, 445, 59, 60, /* 500 */ 445, 24, 445, 145, 141, 431, 376, 445, 107, 42, /* 510 */ 64, 63, 62, 61, 445, 106, 445, 120, 161, 119, /* 520 */ 445, 478, 375, 354, 117, 393, 155, 154, 153, 445, /* 530 */ 394, 143, 473, 59, 60, 64, 63, 62, 61, 152, /* 540 */ 445, 376, 445, 445, 42, 445, 445, 445, 106, 64, /* 550 */ 63, 62, 61, 445, 445, 375, 50, 117, 393, 155, /* 560 */ 154, 153, 445, 394, 144, 445, 59, 60, 445, 445, /* 570 */ 53, 72, 445, 148, 376, 445, 106, 42, 125, 113, /* 580 */ 120, 161, 119, 375, 445, 117, 393, 155, 154, 153, /* 590 */ 394, 445, 445, 59, 60, 445, 445, 445, 445, 445, /* 600 */ 445, 102, 149, 445, 42, 445, 74, 445, 148, 445, /* 610 */ 445, 106, 445, 497, 113, 120, 161, 119, 375, 445, /* 620 */ 117, 393, 155, 154, 153, 394, 445, 445, 59, 60, /* 630 */ 445, 445, 88, 445, 445, 445, 376, 149, 445, 40, /* 640 */ 445, 120, 161, 119, 106, 445, 445, 435, 110, 110, /* 650 */ 445, 375, 445, 117, 393, 155, 154, 153, 394, 445, /* 660 */ 445, 59, 60, 152, 85, 445, 445, 445, 445, 376, /* 670 */ 445, 106, 41, 120, 161, 119, 441, 440, 375, 445, /* 680 */ 117, 393, 155, 154, 153, 448, 454, 29, 445, 445, /* 690 */ 74, 450, 148, 75, 88, 152, 445, 496, 113, 120, /* 700 */ 161, 119, 163, 120, 161, 119, 106, 27, 445, 434, /* 710 */ 111, 111, 445, 375, 445, 117, 393, 155, 154, 153, /* 720 */ 445, 149, 445, 445, 445, 152, 74, 445, 148, 436, /* 730 */ 437, 438, 439, 490, 113, 120, 161, 119, 445, 106, /* 740 */ 121, 447, 454, 29, 445, 445, 375, 450, 117, 393, /* 750 */ 155, 154, 153, 445, 445, 445, 445, 149, 163, 74, /* 760 */ 445, 148, 444, 27, 445, 445, 484, 113, 120, 161, /* 770 */ 119, 445, 445, 445, 74, 445, 148, 445, 445, 445, /* 780 */ 445, 483, 113, 120, 161, 119, 74, 445, 148, 86, /* 790 */ 149, 445, 445, 480, 113, 120, 161, 119, 120, 161, /* 800 */ 119, 445, 74, 445, 148, 149, 445, 445, 445, 134, /* 810 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, /* 820 */ 152, 517, 113, 120, 161, 119, 88, 64, 63, 62, /* 830 */ 61, 445, 445, 149, 445, 120, 161, 119, 445, 74, /* 840 */ 396, 148, 475, 445, 445, 149, 137, 113, 120, 161, /* 850 */ 119, 74, 445, 148, 445, 445, 445, 152, 525, 113, /* 860 */ 120, 161, 119, 445, 74, 445, 148, 445, 445, 445, /* 870 */ 149, 527, 113, 120, 161, 119, 445, 445, 445, 74, /* 880 */ 445, 148, 149, 445, 445, 445, 524, 113, 120, 161, /* 890 */ 119, 74, 445, 148, 98, 149, 445, 445, 526, 113, /* 900 */ 120, 161, 119, 120, 161, 119, 445, 74, 445, 148, /* 910 */ 149, 445, 445, 445, 523, 113, 120, 161, 119, 74, /* 920 */ 445, 148, 149, 445, 445, 152, 522, 113, 120, 161, /* 930 */ 119, 89, 64, 63, 62, 61, 445, 445, 149, 445, /* 940 */ 120, 161, 119, 445, 74, 395, 148, 445, 445, 445, /* 950 */ 149, 521, 113, 120, 161, 119, 74, 445, 148, 445, /* 960 */ 445, 445, 152, 520, 113, 120, 161, 119, 445, 74, /* 970 */ 445, 148, 445, 445, 445, 149, 519, 113, 120, 161, /* 980 */ 119, 445, 445, 445, 74, 445, 148, 149, 445, 445, /* 990 */ 445, 150, 113, 120, 161, 119, 74, 445, 148, 90, /* 1000 */ 149, 445, 445, 151, 113, 120, 161, 119, 120, 161, /* 1010 */ 119, 445, 74, 445, 148, 149, 445, 435, 445, 136, /* 1020 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, /* 1030 */ 152, 135, 113, 120, 161, 119, 64, 63, 62, 61, /* 1040 */ 445, 445, 445, 149, 445, 445, 441, 440, 445, 88, /* 1050 */ 445, 445, 445, 445, 445, 149, 445, 56, 120, 161, /* 1060 */ 119, 88, 445, 445, 10, 479, 479, 445, 445, 445, /* 1070 */ 120, 161, 119, 445, 445, 445, 445, 82, 445, 434, /* 1080 */ 152, 445, 445, 445, 466, 445, 34, 109, 447, 454, /* 1090 */ 29, 445, 152, 445, 450, 445, 445, 445, 107, 436, /* 1100 */ 437, 438, 439, 87, 445, 163, 445, 120, 161, 119, /* 1110 */ 27, 451, 120, 161, 119, 99, 445, 64, 63, 62, /* 1120 */ 61, 445, 100, 445, 120, 161, 119, 101, 445, 152, /* 1130 */ 391, 120, 161, 119, 152, 445, 120, 161, 119, 91, /* 1140 */ 445, 445, 445, 445, 445, 445, 152, 445, 120, 161, /* 1150 */ 119, 103, 445, 152, 92, 445, 445, 445, 152, 445, /* 1160 */ 120, 161, 119, 120, 161, 119, 93, 445, 445, 104, /* 1170 */ 152, 445, 445, 445, 445, 120, 161, 119, 120, 161, /* 1180 */ 119, 445, 152, 445, 94, 152, 445, 445, 445, 445, /* 1190 */ 445, 445, 105, 120, 161, 119, 445, 152, 445, 95, /* 1200 */ 152, 120, 161, 119, 445, 445, 445, 96, 120, 161, /* 1210 */ 119, 445, 445, 445, 445, 152, 120, 161, 119, 445, /* 1220 */ 445, 445, 445, 152, 445, 445, 445, 445, 445, 445, /* 1230 */ 152, 97, 445, 445, 549, 445, 445, 548, 152, 445, /* 1240 */ 120, 161, 119, 120, 161, 119, 120, 161, 119, 445, /* 1250 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, /* 1260 */ 445, 445, 152, 547, 445, 152, 546, 445, 152, 115, /* 1270 */ 445, 445, 120, 161, 119, 120, 161, 119, 120, 161, /* 1280 */ 119, 116, 445, 445, 445, 445, 445, 445, 445, 445, /* 1290 */ 120, 161, 119, 445, 152, 445, 445, 152, 445, 445, /* 1300 */ 152, 445, 445, 445, 445, 445, 445, 445, 445, 445, /* 1310 */ 445, 445, 152, }; static const YYCODETYPE yy_lookahead[] = { /* 0 */ 0, 113, 114, 115, 134, 102, 103, 104, 106, 106, /* 10 */ 10, 113, 114, 115, 111, 112, 113, 114, 115, 106, /* 20 */ 20, 21, 22, 105, 24, 126, 108, 109, 28, 4, /* 30 */ 5, 6, 7, 33, 34, 35, 36, 37, 135, 39, /* 40 */ 40, 41, 42, 105, 44, 45, 108, 109, 107, 49, /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, /* 60 */ 60, 61, 62, 63, 0, 113, 114, 115, 130, 131, /* 70 */ 132, 104, 25, 106, 10, 113, 114, 115, 111, 112, /* 80 */ 113, 114, 115, 106, 20, 21, 22, 128, 24, 113, /* 90 */ 114, 115, 28, 129, 2, 26, 27, 33, 34, 35, /* 100 */ 36, 37, 135, 39, 40, 41, 42, 2, 44, 45, /* 110 */ 133, 26, 27, 49, 50, 51, 52, 53, 54, 55, /* 120 */ 56, 57, 58, 59, 60, 61, 62, 63, 1, 2, /* 130 */ 38, 4, 5, 4, 5, 6, 7, 17, 10, 12, /* 140 */ 4, 5, 15, 38, 1, 25, 17, 29, 30, 31, /* 150 */ 32, 24, 83, 26, 27, 12, 28, 14, 31, 32, /* 160 */ 91, 18, 116, 20, 21, 22, 23, 24, 116, 26, /* 170 */ 27, 19, 29, 30, 31, 32, 91, 3, 64, 65, /* 180 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 6, /* 190 */ 7, 64, 4, 5, 6, 7, 8, 97, 98, 20, /* 200 */ 21, 22, 26, 27, 4, 5, 6, 7, 1, 82, /* 210 */ 48, 84, 85, 86, 87, 17, 89, 17, 91, 92, /* 220 */ 93, 94, 95, 1, 2, 25, 4, 5, 49, 50, /* 230 */ 51, 52, 53, 54, 12, 16, 15, 15, 4, 5, /* 240 */ 6, 7, 20, 21, 22, 1, 26, 27, 4, 5, /* 250 */ 48, 43, 90, 31, 32, 40, 12, 40, 96, 15, /* 260 */ 47, 99, 88, 104, 20, 21, 22, 1, 24, 35, /* 270 */ 4, 5, 113, 114, 115, 0, 117, 41, 12, 41, /* 280 */ 13, 15, 17, 124, 125, 10, 64, 25, 1, 2, /* 290 */ 17, 4, 5, 75, 135, 81, 80, 3, 3, 12, /* 300 */ 3, 99, 15, 79, 82, 80, 84, 85, 86, 87, /* 310 */ 38, 89, 3, 91, 92, 93, 94, 95, 31, 32, /* 320 */ 2, 3, 4, 5, 6, 7, 82, 3, 3, 12, /* 330 */ 77, 104, 25, 89, 16, 91, 92, 93, 94, 95, /* 340 */ 113, 114, 115, 25, 117, 96, 15, 15, 82, 31, /* 350 */ 32, 64, 125, 15, 17, 89, 38, 91, 92, 93, /* 360 */ 94, 95, 135, 28, 4, 5, 6, 7, 28, 82, /* 370 */ 28, 84, 85, 86, 87, 12, 89, 3, 91, 92, /* 380 */ 93, 94, 95, 90, 11, 67, 68, 136, 2, 3, /* 390 */ 4, 5, 6, 7, 76, 35, 78, 79, 80, 82, /* 400 */ 136, 136, 136, 17, 136, 136, 89, 136, 91, 136, /* 410 */ 136, 25, 104, 136, 106, 136, 136, 31, 32, 111, /* 420 */ 112, 113, 114, 115, 38, 136, 136, 2, 3, 4, /* 430 */ 5, 6, 7, 136, 1, 136, 136, 4, 5, 4, /* 440 */ 5, 6, 7, 135, 136, 12, 136, 136, 15, 136, /* 450 */ 25, 136, 17, 67, 68, 136, 31, 32, 136, 136, /* 460 */ 25, 136, 76, 38, 78, 79, 80, 1, 136, 136, /* 470 */ 4, 5, 4, 5, 6, 7, 136, 136, 12, 46, /* 480 */ 47, 15, 136, 136, 136, 17, 20, 21, 22, 136, /* 490 */ 136, 136, 67, 68, 136, 1, 2, 136, 4, 5, /* 500 */ 136, 76, 136, 78, 79, 80, 12, 136, 104, 15, /* 510 */ 4, 5, 6, 7, 136, 82, 136, 113, 114, 115, /* 520 */ 136, 117, 89, 17, 91, 92, 93, 94, 95, 136, /* 530 */ 1, 2, 38, 4, 5, 4, 5, 6, 7, 135, /* 540 */ 136, 12, 136, 136, 15, 136, 136, 136, 82, 4, /* 550 */ 5, 6, 7, 136, 136, 89, 25, 91, 92, 93, /* 560 */ 94, 95, 136, 1, 2, 136, 4, 5, 136, 136, /* 570 */ 25, 104, 136, 106, 12, 136, 82, 15, 111, 112, /* 580 */ 113, 114, 115, 89, 136, 91, 92, 93, 94, 95, /* 590 */ 1, 136, 136, 4, 5, 136, 136, 136, 136, 136, /* 600 */ 136, 12, 135, 136, 15, 136, 104, 136, 106, 136, /* 610 */ 136, 82, 136, 111, 112, 113, 114, 115, 89, 136, /* 620 */ 91, 92, 93, 94, 95, 1, 136, 136, 4, 5, /* 630 */ 136, 136, 104, 136, 136, 136, 12, 135, 136, 15, /* 640 */ 136, 113, 114, 115, 82, 136, 136, 2, 120, 121, /* 650 */ 136, 89, 136, 91, 92, 93, 94, 95, 1, 136, /* 660 */ 136, 4, 5, 135, 104, 136, 136, 136, 136, 12, /* 670 */ 136, 82, 15, 113, 114, 115, 31, 32, 89, 136, /* 680 */ 91, 92, 93, 94, 95, 101, 102, 103, 136, 136, /* 690 */ 104, 107, 106, 48, 104, 135, 136, 111, 112, 113, /* 700 */ 114, 115, 118, 113, 114, 115, 82, 123, 136, 64, /* 710 */ 120, 121, 136, 89, 136, 91, 92, 93, 94, 95, /* 720 */ 136, 135, 136, 136, 136, 135, 104, 136, 106, 84, /* 730 */ 85, 86, 87, 111, 112, 113, 114, 115, 136, 82, /* 740 */ 100, 101, 102, 103, 136, 136, 89, 107, 91, 92, /* 750 */ 93, 94, 95, 136, 136, 136, 136, 135, 118, 104, /* 760 */ 136, 106, 122, 123, 136, 136, 111, 112, 113, 114, /* 770 */ 115, 136, 136, 136, 104, 136, 106, 136, 136, 136, /* 780 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, /* 790 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, /* 800 */ 115, 136, 104, 136, 106, 135, 136, 136, 136, 111, /* 810 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, /* 820 */ 135, 111, 112, 113, 114, 115, 104, 4, 5, 6, /* 830 */ 7, 136, 136, 135, 136, 113, 114, 115, 136, 104, /* 840 */ 17, 106, 120, 136, 136, 135, 111, 112, 113, 114, /* 850 */ 115, 104, 136, 106, 136, 136, 136, 135, 111, 112, /* 860 */ 113, 114, 115, 136, 104, 136, 106, 136, 136, 136, /* 870 */ 135, 111, 112, 113, 114, 115, 136, 136, 136, 104, /* 880 */ 136, 106, 135, 136, 136, 136, 111, 112, 113, 114, /* 890 */ 115, 104, 136, 106, 104, 135, 136, 136, 111, 112, /* 900 */ 113, 114, 115, 113, 114, 115, 136, 104, 136, 106, /* 910 */ 135, 136, 136, 136, 111, 112, 113, 114, 115, 104, /* 920 */ 136, 106, 135, 136, 136, 135, 111, 112, 113, 114, /* 930 */ 115, 104, 4, 5, 6, 7, 136, 136, 135, 136, /* 940 */ 113, 114, 115, 136, 104, 17, 106, 136, 136, 136, /* 950 */ 135, 111, 112, 113, 114, 115, 104, 136, 106, 136, /* 960 */ 136, 136, 135, 111, 112, 113, 114, 115, 136, 104, /* 970 */ 136, 106, 136, 136, 136, 135, 111, 112, 113, 114, /* 980 */ 115, 136, 136, 136, 104, 136, 106, 135, 136, 136, /* 990 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, /* 1000 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, /* 1010 */ 115, 136, 104, 136, 106, 135, 136, 2, 136, 111, /* 1020 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, /* 1030 */ 135, 111, 112, 113, 114, 115, 4, 5, 6, 7, /* 1040 */ 136, 136, 136, 135, 136, 136, 31, 32, 136, 104, /* 1050 */ 136, 136, 136, 136, 136, 135, 136, 25, 113, 114, /* 1060 */ 115, 104, 136, 136, 119, 120, 121, 136, 136, 136, /* 1070 */ 113, 114, 115, 136, 136, 136, 136, 120, 136, 64, /* 1080 */ 135, 136, 136, 136, 127, 136, 129, 100, 101, 102, /* 1090 */ 103, 136, 135, 136, 107, 136, 136, 136, 104, 84, /* 1100 */ 85, 86, 87, 104, 136, 118, 136, 113, 114, 115, /* 1110 */ 123, 117, 113, 114, 115, 104, 136, 4, 5, 6, /* 1120 */ 7, 136, 104, 136, 113, 114, 115, 104, 136, 135, /* 1130 */ 17, 113, 114, 115, 135, 136, 113, 114, 115, 104, /* 1140 */ 136, 136, 136, 136, 136, 136, 135, 136, 113, 114, /* 1150 */ 115, 104, 136, 135, 104, 136, 136, 136, 135, 136, /* 1160 */ 113, 114, 115, 113, 114, 115, 104, 136, 136, 104, /* 1170 */ 135, 136, 136, 136, 136, 113, 114, 115, 113, 114, /* 1180 */ 115, 136, 135, 136, 104, 135, 136, 136, 136, 136, /* 1190 */ 136, 136, 104, 113, 114, 115, 136, 135, 136, 104, /* 1200 */ 135, 113, 114, 115, 136, 136, 136, 104, 113, 114, /* 1210 */ 115, 136, 136, 136, 136, 135, 113, 114, 115, 136, /* 1220 */ 136, 136, 136, 135, 136, 136, 136, 136, 136, 136, /* 1230 */ 135, 104, 136, 136, 104, 136, 136, 104, 135, 136, /* 1240 */ 113, 114, 115, 113, 114, 115, 113, 114, 115, 136, /* 1250 */ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, /* 1260 */ 136, 136, 135, 104, 136, 135, 104, 136, 135, 104, /* 1270 */ 136, 136, 113, 114, 115, 113, 114, 115, 113, 114, /* 1280 */ 115, 104, 136, 136, 136, 136, 136, 136, 136, 136, /* 1290 */ 113, 114, 115, 136, 135, 136, 136, 135, 136, 136, /* 1300 */ 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, /* 1310 */ 136, 136, 135, 100, 100, 100, 100, 100, 100, 100, /* 1320 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1330 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1340 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1350 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1360 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1370 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1380 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1390 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1400 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, /* 1410 */ 100, 100, 100, }; #define YY_SHIFT_COUNT (163) #define YY_SHIFT_MIN (0) #define YY_SHIFT_MAX (1113) static const unsigned short int yy_shift_ofst[] = { /* 0 */ 143, 127, 222, 287, 287, 287, 287, 287, 287, 287, /* 10 */ 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, /* 20 */ 287, 287, 287, 287, 287, 287, 287, 244, 433, 266, /* 30 */ 244, 143, 494, 494, 0, 64, 143, 589, 266, 589, /* 40 */ 466, 466, 466, 529, 562, 266, 266, 266, 266, 266, /* 50 */ 266, 624, 266, 266, 657, 266, 266, 266, 266, 266, /* 60 */ 266, 266, 266, 266, 266, 179, 317, 317, 317, 317, /* 70 */ 317, 645, 318, 386, 425, 1015, 1015, 118, 47, 1313, /* 80 */ 1313, 1313, 1313, 114, 114, 200, 435, 129, 188, 234, /* 90 */ 360, 468, 531, 506, 545, 823, 1032, 928, 1113, 25, /* 100 */ 25, 25, 162, 25, 25, 25, 69, 25, 85, 128, /* 110 */ 92, 105, 120, 136, 100, 183, 183, 176, 220, 174, /* 120 */ 202, 275, 152, 207, 198, 219, 221, 208, 215, 217, /* 130 */ 236, 238, 213, 267, 265, 262, 218, 273, 216, 224, /* 140 */ 214, 225, 294, 295, 297, 272, 309, 324, 325, 249, /* 150 */ 253, 307, 249, 331, 332, 338, 337, 335, 340, 342, /* 160 */ 363, 293, 374, 373, }; #define YY_REDUCE_COUNT (82) #define YY_REDUCE_MIN (-130) #define YY_REDUCE_MAX (1177) static const short yy_reduce_ofst[] = { /* 0 */ 640, -97, -33, 308, 467, 502, 586, 622, 655, 670, /* 10 */ 682, 698, 710, 735, 747, 760, 775, 787, 803, 815, /* 20 */ 840, 852, 865, 880, 892, 908, 920, 159, 945, 957, /* 30 */ 227, 987, 528, 590, -62, -62, 584, 404, 722, 994, /* 40 */ 560, 685, 790, 827, 895, 999, 1011, 1018, 1023, 1035, /* 50 */ 1047, 1050, 1062, 1065, 1080, 1088, 1095, 1103, 1127, 1130, /* 60 */ 1133, 1159, 1162, 1165, 1177, -82, -112, -102, -48, -38, /* 70 */ -24, -23, -130, -130, -130, -98, -87, -59, -101, -41, /* 80 */ 46, 52, -36, }; static const YYACTIONTYPE yy_default[] = { /* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576, /* 30 */ 443, 449, 580, 485, 581, 581, 449, 443, 443, 443, /* 40 */ 443, 443, 443, 443, 443, 443, 443, 443, 477, 443, /* 50 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 60 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 70 */ 443, 443, 443, 443, 443, 443, 443, 443, 455, 470, /* 80 */ 508, 508, 576, 468, 493, 443, 443, 443, 471, 443, /* 90 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 488, /* 100 */ 486, 476, 459, 512, 511, 510, 443, 566, 443, 443, /* 110 */ 443, 443, 443, 588, 443, 545, 544, 540, 443, 532, /* 120 */ 529, 443, 443, 443, 443, 443, 443, 491, 443, 443, /* 130 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 140 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 592, /* 150 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, /* 160 */ 443, 601, 443, 443, }; /********** End of lemon-generated parsing tables *****************************/ /* The next table maps tokens (terminal symbols) into fallback tokens. ** If a construct like the following: ** ** %fallback ID X Y Z. ** ** appears in the grammar, then ID becomes a fallback token for X, Y, ** and Z. Whenever one of the tokens X, Y, or Z is input to the parser ** but it does not parse, the type of the token is changed to ID and ** the parse is retried before an error is thrown. ** ** This feature can be used, for example, to cause some keywords in a language ** to revert to identifiers if they keyword does not apply in the context where ** it appears. */ #ifdef YYFALLBACK static const YYCODETYPE yyFallback[] = { 0, /* $ => nothing */ 0, /* ID => nothing */ 1, /* EDGEPT => ID */ 0, /* OF => nothing */ 0, /* PLUS => nothing */ 0, /* MINUS => nothing */ 0, /* STAR => nothing */ 0, /* SLASH => nothing */ 0, /* PERCENT => nothing */ 0, /* UMINUS => nothing */ 0, /* EOL => nothing */ 0, /* ASSIGN => nothing */ 0, /* PLACENAME => nothing */ 0, /* COLON => nothing */ 0, /* ASSERT => nothing */ 0, /* LP => nothing */ 0, /* EQ => nothing */ 0, /* RP => nothing */ 0, /* DEFINE => nothing */ 0, /* CODEBLOCK => nothing */ 0, /* FILL => nothing */ 0, /* COLOR => nothing */ 0, /* THICKNESS => nothing */ 0, /* PRINT => nothing */ 0, /* STRING => nothing */ 0, /* COMMA => nothing */ 0, /* CLASSNAME => nothing */ 0, /* LB => nothing */ 0, /* RB => nothing */ 0, /* UP => nothing */ 0, /* DOWN => nothing */ 0, /* LEFT => nothing */ 0, /* RIGHT => nothing */ 0, /* CLOSE => nothing */ 0, /* CHOP => nothing */ 0, /* FROM => nothing */ 0, /* TO => nothing */ 0, /* THEN => nothing */ 0, /* HEADING => nothing */ 0, /* GO => nothing */ 0, /* AT => nothing */ 0, /* WITH => nothing */ 0, /* SAME => nothing */ 0, /* AS => nothing */ 0, /* FIT => nothing */ 0, /* BEHIND => nothing */ 0, /* UNTIL => nothing */ 0, /* EVEN => nothing */ 0, /* DOT_E => nothing */ 0, /* HEIGHT => nothing */ 0, /* WIDTH => nothing */ 0, /* RADIUS => nothing */ 0, /* DIAMETER => nothing */ 0, /* DOTTED => nothing */ 0, /* DASHED => nothing */ 0, /* CW => nothing */ 0, /* CCW => nothing */ 0, /* LARROW => nothing */ 0, /* RARROW => nothing */ 0, /* LRARROW => nothing */ 0, /* INVIS => nothing */ 0, /* THICK => nothing */ 0, /* THIN => nothing */ 0, /* SOLID => nothing */ 0, /* CENTER => nothing */ 0, /* LJUST => nothing */ 0, /* RJUST => nothing */ 0, /* ABOVE => nothing */ 0, /* BELOW => nothing */ 0, /* ITALIC => nothing */ 0, /* BOLD => nothing */ 0, /* MONO => nothing */ 0, /* ALIGNED => nothing */ 0, /* BIG => nothing */ 0, /* SMALL => nothing */ 0, /* AND => nothing */ 0, /* LT => nothing */ 0, /* GT => nothing */ 0, /* ON => nothing */ 0, /* WAY => nothing */ 0, /* BETWEEN => nothing */ 0, /* THE => nothing */ 0, /* NTH => nothing */ 0, /* VERTEX => nothing */ 0, /* TOP => nothing */ 0, /* BOTTOM => nothing */ 0, /* START => nothing */ 0, /* END => nothing */ 0, /* IN => nothing */ 0, /* THIS => nothing */ 0, /* DOT_U => nothing */ 0, /* LAST => nothing */ 0, /* NUMBER => nothing */ 0, /* FUNC1 => nothing */ 0, /* FUNC2 => nothing */ 0, /* DIST => nothing */ 0, /* DOT_XY => nothing */ 0, /* X => nothing */ 0, /* Y => nothing */ 0, /* DOT_L => nothing */ }; #endif /* YYFALLBACK */ /* The following structure represents a single element of the ** parser's stack. Information stored includes: ** ** + The state number for the parser at this level of the stack. ** ** + The value of the token stored at this level of the stack. ** (In other words, the "major" token.) ** ** + The semantic value stored at this level of the stack. This is ** the information used by the action routines in the grammar. ** It is sometimes called the "minor" token. ** ** After the "shift" half of a SHIFTREDUCE action, the stateno field ** actually contains the reduce action for the second half of the ** SHIFTREDUCE. */ struct yyStackEntry { YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */ YYCODETYPE major; /* The major token value. This is the code ** number for the token at this stack level */ YYMINORTYPE minor; /* The user-supplied minor token value. This ** is the value of the token */ }; typedef struct yyStackEntry yyStackEntry; /* The state of the parser is completely contained in an instance of ** the following structure */ struct yyParser { yyStackEntry *yytos; /* Pointer to top element of the stack */ #ifdef YYTRACKMAXSTACKDEPTH int yyhwm; /* High-water mark of the stack */ #endif #ifndef YYNOERRORRECOVERY int yyerrcnt; /* Shifts left before out of the error */ #endif pik_parserARG_SDECL /* A place to hold %extra_argument */ pik_parserCTX_SDECL /* A place to hold %extra_context */ yyStackEntry *yystackEnd; /* Last entry in the stack */ yyStackEntry *yystack; /* The parser stack */ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ }; typedef struct yyParser yyParser; #include <assert.h> #ifndef NDEBUG #include <stdio.h> static FILE *yyTraceFILE = 0; static char *yyTracePrompt = 0; #endif /* NDEBUG */ #ifndef NDEBUG /* ** Turn parser tracing on by giving a stream to which to write the trace ** and a prompt to preface each trace message. Tracing is turned off ** by making either argument NULL ** ** Inputs: ** <ul> ** <li> A FILE* to which trace output should be written. ** If NULL, then tracing is turned off. ** <li> A prefix string written at the beginning of every ** line of trace output. If NULL, then tracing is ** turned off. ** </ul> ** ** Outputs: ** None. */ void pik_parserTrace(FILE *TraceFILE, char *zTracePrompt){ yyTraceFILE = TraceFILE; yyTracePrompt = zTracePrompt; if( yyTraceFILE==0 ) yyTracePrompt = 0; else if( yyTracePrompt==0 ) yyTraceFILE = 0; } #endif /* NDEBUG */ #if defined(YYCOVERAGE) || !defined(NDEBUG) /* For tracing shifts, the names of all terminals and nonterminals ** are required. The following table supplies these names */ static const char *const yyTokenName[] = { /* 0 */ "$", /* 1 */ "ID", /* 2 */ "EDGEPT", /* 3 */ "OF", /* 4 */ "PLUS", /* 5 */ "MINUS", /* 6 */ "STAR", /* 7 */ "SLASH", /* 8 */ "PERCENT", /* 9 */ "UMINUS", /* 10 */ "EOL", /* 11 */ "ASSIGN", /* 12 */ "PLACENAME", /* 13 */ "COLON", /* 14 */ "ASSERT", /* 15 */ "LP", /* 16 */ "EQ", /* 17 */ "RP", /* 18 */ "DEFINE", /* 19 */ "CODEBLOCK", /* 20 */ "FILL", /* 21 */ "COLOR", /* 22 */ "THICKNESS", /* 23 */ "PRINT", /* 24 */ "STRING", /* 25 */ "COMMA", /* 26 */ "CLASSNAME", /* 27 */ "LB", /* 28 */ "RB", /* 29 */ "UP", /* 30 */ "DOWN", /* 31 */ "LEFT", /* 32 */ "RIGHT", /* 33 */ "CLOSE", /* 34 */ "CHOP", /* 35 */ "FROM", /* 36 */ "TO", /* 37 */ "THEN", /* 38 */ "HEADING", /* 39 */ "GO", /* 40 */ "AT", /* 41 */ "WITH", /* 42 */ "SAME", /* 43 */ "AS", /* 44 */ "FIT", /* 45 */ "BEHIND", /* 46 */ "UNTIL", /* 47 */ "EVEN", /* 48 */ "DOT_E", /* 49 */ "HEIGHT", /* 50 */ "WIDTH", /* 51 */ "RADIUS", /* 52 */ "DIAMETER", /* 53 */ "DOTTED", /* 54 */ "DASHED", /* 55 */ "CW", /* 56 */ "CCW", /* 57 */ "LARROW", /* 58 */ "RARROW", /* 59 */ "LRARROW", /* 60 */ "INVIS", /* 61 */ "THICK", /* 62 */ "THIN", /* 63 */ "SOLID", /* 64 */ "CENTER", /* 65 */ "LJUST", /* 66 */ "RJUST", /* 67 */ "ABOVE", /* 68 */ "BELOW", /* 69 */ "ITALIC", /* 70 */ "BOLD", /* 71 */ "MONO", /* 72 */ "ALIGNED", /* 73 */ "BIG", /* 74 */ "SMALL", /* 75 */ "AND", /* 76 */ "LT", /* 77 */ "GT", /* 78 */ "ON", /* 79 */ "WAY", /* 80 */ "BETWEEN", /* 81 */ "THE", /* 82 */ "NTH", /* 83 */ "VERTEX", /* 84 */ "TOP", /* 85 */ "BOTTOM", /* 86 */ "START", /* 87 */ "END", /* 88 */ "IN", /* 89 */ "THIS", /* 90 */ "DOT_U", /* 91 */ "LAST", /* 92 */ "NUMBER", /* 93 */ "FUNC1", /* 94 */ "FUNC2", /* 95 */ "DIST", /* 96 */ "DOT_XY", /* 97 */ "X", /* 98 */ "Y", /* 99 */ "DOT_L", /* 100 */ "statement_list", /* 101 */ "statement", /* 102 */ "unnamed_statement", /* 103 */ "basetype", /* 104 */ "expr", /* 105 */ "numproperty", /* 106 */ "edge", /* 107 */ "direction", /* 108 */ "dashproperty", /* 109 */ "colorproperty", /* 110 */ "locproperty", /* 111 */ "position", /* 112 */ "place", /* 113 */ "object", /* 114 */ "objectname", /* 115 */ "nth", /* 116 */ "textposition", /* 117 */ "rvalue", /* 118 */ "lvalue", /* 119 */ "even", /* 120 */ "relexpr", /* 121 */ "optrelexpr", /* 122 */ "document", /* 123 */ "print", /* 124 */ "prlist", /* 125 */ "pritem", /* 126 */ "prsep", /* 127 */ "attribute_list", /* 128 */ "savelist", /* 129 */ "alist", /* 130 */ "attribute", /* 131 */ "go", /* 132 */ "boolproperty", /* 133 */ "withclause", /* 134 */ "between", /* 135 */ "place2", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ #ifndef NDEBUG /* For tracing reduce actions, the names of all rules are required. */ static const char *const yyRuleName[] = { /* 0 */ "document ::= statement_list", /* 1 */ "statement_list ::= statement", /* 2 */ "statement_list ::= statement_list EOL statement", /* 3 */ "statement ::=", /* 4 */ "statement ::= direction", /* 5 */ "statement ::= lvalue ASSIGN rvalue", /* 6 */ "statement ::= PLACENAME COLON unnamed_statement", /* 7 */ "statement ::= PLACENAME COLON position", /* 8 */ "statement ::= unnamed_statement", /* 9 */ "statement ::= print prlist", /* 10 */ "statement ::= ASSERT LP expr EQ expr RP", /* 11 */ "statement ::= ASSERT LP position EQ position RP", /* 12 */ "statement ::= DEFINE ID CODEBLOCK", /* 13 */ "rvalue ::= PLACENAME", /* 14 */ "pritem ::= FILL", /* 15 */ "pritem ::= COLOR", /* 16 */ "pritem ::= THICKNESS", /* 17 */ "pritem ::= rvalue", /* 18 */ "pritem ::= STRING", /* 19 */ "prsep ::= COMMA", /* 20 */ "unnamed_statement ::= basetype attribute_list", /* 21 */ "basetype ::= CLASSNAME", /* 22 */ "basetype ::= STRING textposition", /* 23 */ "basetype ::= LB savelist statement_list RB", /* 24 */ "savelist ::=", /* 25 */ "relexpr ::= expr", /* 26 */ "relexpr ::= expr PERCENT", /* 27 */ "optrelexpr ::=", /* 28 */ "attribute_list ::= relexpr alist", /* 29 */ "attribute ::= numproperty relexpr", /* 30 */ "attribute ::= dashproperty expr", /* 31 */ "attribute ::= dashproperty", /* 32 */ "attribute ::= colorproperty rvalue", /* 33 */ "attribute ::= go direction optrelexpr", /* 34 */ "attribute ::= go direction even position", /* 35 */ "attribute ::= CLOSE", /* 36 */ "attribute ::= CHOP", /* 37 */ "attribute ::= FROM position", /* 38 */ "attribute ::= TO position", /* 39 */ "attribute ::= THEN", /* 40 */ "attribute ::= THEN optrelexpr HEADING expr", /* 41 */ "attribute ::= THEN optrelexpr EDGEPT", /* 42 */ "attribute ::= GO optrelexpr HEADING expr", /* 43 */ "attribute ::= GO optrelexpr EDGEPT", /* 44 */ "attribute ::= AT position", /* 45 */ "attribute ::= SAME", /* 46 */ "attribute ::= SAME AS object", /* 47 */ "attribute ::= STRING textposition", /* 48 */ "attribute ::= FIT", /* 49 */ "attribute ::= BEHIND object", /* 50 */ "withclause ::= DOT_E edge AT position", /* 51 */ "withclause ::= edge AT position", /* 52 */ "numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS", /* 53 */ "boolproperty ::= CW", /* 54 */ "boolproperty ::= CCW", /* 55 */ "boolproperty ::= LARROW", /* 56 */ "boolproperty ::= RARROW", /* 57 */ "boolproperty ::= LRARROW", /* 58 */ "boolproperty ::= INVIS", /* 59 */ "boolproperty ::= THICK", /* 60 */ "boolproperty ::= THIN", /* 61 */ "boolproperty ::= SOLID", /* 62 */ "textposition ::=", /* 63 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL", /* 64 */ "position ::= expr COMMA expr", /* 65 */ "position ::= place PLUS expr COMMA expr", /* 66 */ "position ::= place MINUS expr COMMA expr", /* 67 */ "position ::= place PLUS LP expr COMMA expr RP", /* 68 */ "position ::= place MINUS LP expr COMMA expr RP", /* 69 */ "position ::= LP position COMMA position RP", /* 70 */ "position ::= LP position RP", /* 71 */ "position ::= expr between position AND position", /* 72 */ "position ::= expr LT position COMMA position GT", /* 73 */ "position ::= expr ABOVE position", /* 74 */ "position ::= expr BELOW position", /* 75 */ "position ::= expr LEFT OF position", /* 76 */ "position ::= expr RIGHT OF position", /* 77 */ "position ::= expr ON HEADING EDGEPT OF position", /* 78 */ "position ::= expr HEADING EDGEPT OF position", /* 79 */ "position ::= expr EDGEPT OF position", /* 80 */ "position ::= expr ON HEADING expr FROM position", /* 81 */ "position ::= expr HEADING expr FROM position", /* 82 */ "place ::= edge OF object", /* 83 */ "place2 ::= object", /* 84 */ "place2 ::= object DOT_E edge", /* 85 */ "place2 ::= NTH VERTEX OF object", /* 86 */ "object ::= nth", /* 87 */ "object ::= nth OF|IN object", /* 88 */ "objectname ::= THIS", /* 89 */ "objectname ::= PLACENAME", /* 90 */ "objectname ::= objectname DOT_U PLACENAME", /* 91 */ "nth ::= NTH CLASSNAME", /* 92 */ "nth ::= NTH LAST CLASSNAME", /* 93 */ "nth ::= LAST CLASSNAME", /* 94 */ "nth ::= LAST", /* 95 */ "nth ::= NTH LB RB", /* 96 */ "nth ::= NTH LAST LB RB", /* 97 */ "nth ::= LAST LB RB", /* 98 */ "expr ::= expr PLUS expr", /* 99 */ "expr ::= expr MINUS expr", /* 100 */ "expr ::= expr STAR expr", /* 101 */ "expr ::= expr SLASH expr", /* 102 */ "expr ::= MINUS expr", /* 103 */ "expr ::= PLUS expr", /* 104 */ "expr ::= LP expr RP", /* 105 */ "expr ::= LP FILL|COLOR|THICKNESS RP", /* 106 */ "expr ::= NUMBER", /* 107 */ "expr ::= ID", /* 108 */ "expr ::= FUNC1 LP expr RP", /* 109 */ "expr ::= FUNC2 LP expr COMMA expr RP", /* 110 */ "expr ::= DIST LP position COMMA position RP", /* 111 */ "expr ::= place2 DOT_XY X", /* 112 */ "expr ::= place2 DOT_XY Y", /* 113 */ "expr ::= object DOT_L numproperty", /* 114 */ "expr ::= object DOT_L dashproperty", /* 115 */ "expr ::= object DOT_L colorproperty", /* 116 */ "lvalue ::= ID", /* 117 */ "lvalue ::= FILL", /* 118 */ "lvalue ::= COLOR", /* 119 */ "lvalue ::= THICKNESS", /* 120 */ "rvalue ::= expr", /* 121 */ "print ::= PRINT", /* 122 */ "prlist ::= pritem", /* 123 */ "prlist ::= prlist prsep pritem", /* 124 */ "direction ::= UP", /* 125 */ "direction ::= DOWN", /* 126 */ "direction ::= LEFT", /* 127 */ "direction ::= RIGHT", /* 128 */ "optrelexpr ::= relexpr", /* 129 */ "attribute_list ::= alist", /* 130 */ "alist ::=", /* 131 */ "alist ::= alist attribute", /* 132 */ "attribute ::= boolproperty", /* 133 */ "attribute ::= WITH withclause", /* 134 */ "go ::= GO", /* 135 */ "go ::=", /* 136 */ "even ::= UNTIL EVEN WITH", /* 137 */ "even ::= EVEN WITH", /* 138 */ "dashproperty ::= DOTTED", /* 139 */ "dashproperty ::= DASHED", /* 140 */ "colorproperty ::= FILL", /* 141 */ "colorproperty ::= COLOR", /* 142 */ "position ::= place", /* 143 */ "between ::= WAY BETWEEN", /* 144 */ "between ::= BETWEEN", /* 145 */ "between ::= OF THE WAY BETWEEN", /* 146 */ "place ::= place2", /* 147 */ "edge ::= CENTER", /* 148 */ "edge ::= EDGEPT", /* 149 */ "edge ::= TOP", /* 150 */ "edge ::= BOTTOM", /* 151 */ "edge ::= START", /* 152 */ "edge ::= END", /* 153 */ "edge ::= RIGHT", /* 154 */ "edge ::= LEFT", /* 155 */ "object ::= objectname", }; #endif /* NDEBUG */ #if YYGROWABLESTACK /* ** Try to increase the size of the parser stack. Return the number ** of errors. Return 0 on success. */ static int yyGrowStack(yyParser *p){ int oldSize = 1 + (int)(p->yystackEnd - p->yystack); int newSize; int idx; yyStackEntry *pNew; newSize = oldSize*2 + 100; idx = (int)(p->yytos - p->yystack); if( p->yystack==p->yystk0 ){ pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); if( pNew==0 ) return 1; memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); }else{ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); if( pNew==0 ) return 1; } p->yystack = pNew; p->yytos = &p->yystack[idx]; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", yyTracePrompt, oldSize, newSize); } #endif p->yystackEnd = &p->yystack[newSize-1]; return 0; } #endif /* YYGROWABLESTACK */ #if !YYGROWABLESTACK /* For builds that do no have a growable stack, yyGrowStack always ** returns an error. */ # define yyGrowStack(X) 1 #endif /* Datatype of the argument to the memory allocated passed as the ** second argument to pik_parserAlloc() below. This can be changed by ** putting an appropriate #define in the %include section of the input ** grammar. */ #ifndef YYMALLOCARGTYPE # define YYMALLOCARGTYPE size_t #endif /* Initialize a new parser that has already been allocated. */ void pik_parserInit(void *yypRawParser pik_parserCTX_PDECL){ yyParser *yypParser = (yyParser*)yypRawParser; pik_parserCTX_STORE #ifdef YYTRACKMAXSTACKDEPTH yypParser->yyhwm = 0; #endif yypParser->yystack = yypParser->yystk0; yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif yypParser->yytos = yypParser->yystack; yypParser->yystack[0].stateno = 0; yypParser->yystack[0].major = 0; } #ifndef pik_parser_ENGINEALWAYSONSTACK /* ** This function allocates a new parser. ** The only argument is a pointer to a function which works like ** malloc. ** ** Inputs: ** A pointer to the function used to allocate memory. ** ** Outputs: ** A pointer to a parser. This pointer is used in subsequent calls ** to pik_parser and pik_parserFree. */ void *pik_parserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) pik_parserCTX_PDECL){ yyParser *yypParser; yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) ); if( yypParser ){ pik_parserCTX_STORE pik_parserInit(yypParser pik_parserCTX_PARAM); } return (void*)yypParser; } #endif /* pik_parser_ENGINEALWAYSONSTACK */ /* The following function deletes the "minor type" or semantic value ** associated with a symbol. The symbol can be either a terminal ** or nonterminal. "yymajor" is the symbol code, and "yypminor" is ** a pointer to the value to be deleted. The code used to do the ** deletions is derived from the %destructor and/or %token_destructor ** directives of the input grammar. */ static void yy_destructor( yyParser *yypParser, /* The parser */ YYCODETYPE yymajor, /* Type code for object to destroy */ YYMINORTYPE *yypminor /* The object to be destroyed */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH switch( yymajor ){ /* Here is inserted the actions which take place when a ** terminal or non-terminal is destroyed. This can happen ** when the symbol is popped from the stack during a ** reduce or during error processing or when a parser is ** being destroyed before it is finished parsing. ** ** Note: during a reduce, the only symbols destroyed are those ** which appear on the RHS of the rule, but which are *not* used ** inside the C code. */ /********* Begin destructor definitions ***************************************/ case 100: /* statement_list */ { #line 511 "pikchr.y" pik_elist_free(p,(yypminor->yy235)); #line 1777 "pikchr.c" } break; case 101: /* statement */ case 102: /* unnamed_statement */ case 103: /* basetype */ { #line 513 "pikchr.y" pik_elem_free(p,(yypminor->yy162)); #line 1786 "pikchr.c" } break; /********* End destructor definitions *****************************************/ default: break; /* If no destructor action specified: do nothing */ } } /* ** Pop the parser's stack once. ** ** If there is a destructor routine associated with the token which ** is popped from the stack, then call it. */ static void yy_pop_parser_stack(yyParser *pParser){ yyStackEntry *yytos; assert( pParser->yytos!=0 ); assert( pParser->yytos > pParser->yystack ); yytos = pParser->yytos--; #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sPopping %s\n", yyTracePrompt, yyTokenName[yytos->major]); } #endif yy_destructor(pParser, yytos->major, &yytos->minor); } /* ** Clear all secondary memory allocations from the parser */ void pik_parserFinalize(void *p){ yyParser *pParser = (yyParser*)p; /* In-lined version of calling yy_pop_parser_stack() for each ** element left in the stack */ yyStackEntry *yytos = pParser->yytos; while( yytos>pParser->yystack ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sPopping %s\n", yyTracePrompt, yyTokenName[yytos->major]); } #endif if( yytos->major>=YY_MIN_DSTRCTR ){ yy_destructor(pParser, yytos->major, &yytos->minor); } yytos--; } #if YYGROWABLESTACK if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); #endif } #ifndef pik_parser_ENGINEALWAYSONSTACK /* ** Deallocate and destroy a parser. Destructors are called for ** all stack elements before shutting the parser down. ** ** If the YYPARSEFREENEVERNULL macro exists (for example because it ** is defined in a %include section of the input grammar) then it is ** assumed that the input pointer is never NULL. */ void pik_parserFree( void *p, /* The parser to be deleted */ void (*freeProc)(void*) /* Function used to reclaim memory */ ){ #ifndef YYPARSEFREENEVERNULL if( p==0 ) return; #endif pik_parserFinalize(p); (*freeProc)(p); } #endif /* pik_parser_ENGINEALWAYSONSTACK */ /* ** Return the peak depth of the stack for a parser. */ #ifdef YYTRACKMAXSTACKDEPTH int pik_parserStackPeak(void *p){ yyParser *pParser = (yyParser*)p; return pParser->yyhwm; } #endif /* This array of booleans keeps track of the parser statement ** coverage. The element yycoverage[X][Y] is set when the parser ** is in state X and has a lookahead token Y. In a well-tested ** systems, every element of this matrix should end up being set. */ #if defined(YYCOVERAGE) static unsigned char yycoverage[YYNSTATE][YYNTOKEN]; #endif /* ** Write into out a description of every state/lookahead combination that ** ** (1) has not been used by the parser, and ** (2) is not a syntax error. ** ** Return the number of missed state/lookahead combinations. */ #if defined(YYCOVERAGE) int pik_parserCoverage(FILE *out){ int stateno, iLookAhead, i; int nMissed = 0; for(stateno=0; stateno<YYNSTATE; stateno++){ i = yy_shift_ofst[stateno]; for(iLookAhead=0; iLookAhead<YYNTOKEN; iLookAhead++){ if( yy_lookahead[i+iLookAhead]!=iLookAhead ) continue; if( yycoverage[stateno][iLookAhead]==0 ) nMissed++; if( out ){ fprintf(out,"State %d lookahead %s %s\n", stateno, yyTokenName[iLookAhead], yycoverage[stateno][iLookAhead] ? "ok" : "missed"); } } } return nMissed; } #endif /* ** Find the appropriate action for a parser given the terminal ** look-ahead token iLookAhead. */ static YYACTIONTYPE yy_find_shift_action( YYCODETYPE iLookAhead, /* The look-ahead token */ YYACTIONTYPE stateno /* Current state number */ ){ int i; if( stateno>YY_MAX_SHIFT ) return stateno; assert( stateno <= YY_SHIFT_COUNT ); #if defined(YYCOVERAGE) yycoverage[stateno][iLookAhead] = 1; #endif do{ i = yy_shift_ofst[stateno]; assert( i>=0 ); assert( i<=YY_ACTTAB_COUNT ); assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD ); assert( iLookAhead!=YYNOCODE ); assert( iLookAhead < YYNTOKEN ); i += iLookAhead; assert( i<(int)YY_NLOOKAHEAD ); if( yy_lookahead[i]!=iLookAhead ){ #ifdef YYFALLBACK YYCODETYPE iFallback; /* Fallback token */ assert( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) ); iFallback = yyFallback[iLookAhead]; if( iFallback!=0 ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n", yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); } #endif assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */ iLookAhead = iFallback; continue; } #endif #ifdef YYWILDCARD { int j = i - iLookAhead + YYWILDCARD; assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) ); if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n", yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); } #endif /* NDEBUG */ return yy_action[j]; } } #endif /* YYWILDCARD */ return yy_default[stateno]; }else{ assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) ); return yy_action[i]; } }while(1); } /* ** Find the appropriate action for a parser given the non-terminal ** look-ahead token iLookAhead. */ static YYACTIONTYPE yy_find_reduce_action( YYACTIONTYPE stateno, /* Current state number */ YYCODETYPE iLookAhead /* The look-ahead token */ ){ int i; #ifdef YYERRORSYMBOL if( stateno>YY_REDUCE_COUNT ){ return yy_default[stateno]; } #else assert( stateno<=YY_REDUCE_COUNT ); #endif i = yy_reduce_ofst[stateno]; assert( iLookAhead!=YYNOCODE ); i += iLookAhead; #ifdef YYERRORSYMBOL if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ return yy_default[stateno]; } #else assert( i>=0 && i<YY_ACTTAB_COUNT ); assert( yy_lookahead[i]==iLookAhead ); #endif return yy_action[i]; } /* ** The following routine is called if the stack overflows. */ static void yyStackOverflow(yyParser *yypParser){ pik_parserARG_FETCH pik_parserCTX_FETCH #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); } #endif while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will execute if the parser ** stack every overflows */ /******** Begin %stack_overflow code ******************************************/ #line 545 "pikchr.y" pik_error(p, 0, "parser stack overflow"); #line 2024 "pikchr.c" /******** End %stack_overflow code ********************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */ pik_parserCTX_STORE } /* ** Print tracing information for a SHIFT action */ #ifndef NDEBUG static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){ if( yyTraceFILE ){ if( yyNewState<YYNSTATE ){ fprintf(yyTraceFILE,"%s%s '%s', go to state %d\n", yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major], yyNewState); }else{ fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n", yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major], yyNewState - YY_MIN_REDUCE); } } } #else # define yyTraceShift(X,Y,Z) #endif /* ** Perform a shift action. */ static void yy_shift( yyParser *yypParser, /* The parser to be shifted */ YYACTIONTYPE yyNewState, /* The new state to shift in */ YYCODETYPE yyMajor, /* The major token to shift in */ pik_parserTOKENTYPE yyMinor /* The minor token to shift in */ ){ yyStackEntry *yytos; yypParser->yytos++; #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif yytos = yypParser->yytos; if( yytos>yypParser->yystackEnd ){ if( yyGrowStack(yypParser) ){ yypParser->yytos--; yyStackOverflow(yypParser); return; } yytos = yypParser->yytos; assert( yytos <= yypParser->yystackEnd ); } if( yyNewState > YY_MAX_SHIFT ){ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; } yytos->stateno = yyNewState; yytos->major = yyMajor; yytos->minor.yy0 = yyMinor; yyTraceShift(yypParser, yyNewState, "Shift"); } /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side ** of that rule */ static const YYCODETYPE yyRuleInfoLhs[] = { 122, /* (0) document ::= statement_list */ 100, /* (1) statement_list ::= statement */ 100, /* (2) statement_list ::= statement_list EOL statement */ 101, /* (3) statement ::= */ 101, /* (4) statement ::= direction */ 101, /* (5) statement ::= lvalue ASSIGN rvalue */ 101, /* (6) statement ::= PLACENAME COLON unnamed_statement */ 101, /* (7) statement ::= PLACENAME COLON position */ 101, /* (8) statement ::= unnamed_statement */ 101, /* (9) statement ::= print prlist */ 101, /* (10) statement ::= ASSERT LP expr EQ expr RP */ 101, /* (11) statement ::= ASSERT LP position EQ position RP */ 101, /* (12) statement ::= DEFINE ID CODEBLOCK */ 117, /* (13) rvalue ::= PLACENAME */ 125, /* (14) pritem ::= FILL */ 125, /* (15) pritem ::= COLOR */ 125, /* (16) pritem ::= THICKNESS */ 125, /* (17) pritem ::= rvalue */ 125, /* (18) pritem ::= STRING */ 126, /* (19) prsep ::= COMMA */ 102, /* (20) unnamed_statement ::= basetype attribute_list */ 103, /* (21) basetype ::= CLASSNAME */ 103, /* (22) basetype ::= STRING textposition */ 103, /* (23) basetype ::= LB savelist statement_list RB */ 128, /* (24) savelist ::= */ 120, /* (25) relexpr ::= expr */ 120, /* (26) relexpr ::= expr PERCENT */ 121, /* (27) optrelexpr ::= */ 127, /* (28) attribute_list ::= relexpr alist */ 130, /* (29) attribute ::= numproperty relexpr */ 130, /* (30) attribute ::= dashproperty expr */ 130, /* (31) attribute ::= dashproperty */ 130, /* (32) attribute ::= colorproperty rvalue */ 130, /* (33) attribute ::= go direction optrelexpr */ 130, /* (34) attribute ::= go direction even position */ 130, /* (35) attribute ::= CLOSE */ 130, /* (36) attribute ::= CHOP */ 130, /* (37) attribute ::= FROM position */ 130, /* (38) attribute ::= TO position */ 130, /* (39) attribute ::= THEN */ 130, /* (40) attribute ::= THEN optrelexpr HEADING expr */ 130, /* (41) attribute ::= THEN optrelexpr EDGEPT */ 130, /* (42) attribute ::= GO optrelexpr HEADING expr */ 130, /* (43) attribute ::= GO optrelexpr EDGEPT */ 130, /* (44) attribute ::= AT position */ 130, /* (45) attribute ::= SAME */ 130, /* (46) attribute ::= SAME AS object */ 130, /* (47) attribute ::= STRING textposition */ 130, /* (48) attribute ::= FIT */ 130, /* (49) attribute ::= BEHIND object */ 133, /* (50) withclause ::= DOT_E edge AT position */ 133, /* (51) withclause ::= edge AT position */ 105, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ 132, /* (53) boolproperty ::= CW */ 132, /* (54) boolproperty ::= CCW */ 132, /* (55) boolproperty ::= LARROW */ 132, /* (56) boolproperty ::= RARROW */ 132, /* (57) boolproperty ::= LRARROW */ 132, /* (58) boolproperty ::= INVIS */ 132, /* (59) boolproperty ::= THICK */ 132, /* (60) boolproperty ::= THIN */ 132, /* (61) boolproperty ::= SOLID */ 116, /* (62) textposition ::= */ 116, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ 111, /* (64) position ::= expr COMMA expr */ 111, /* (65) position ::= place PLUS expr COMMA expr */ 111, /* (66) position ::= place MINUS expr COMMA expr */ 111, /* (67) position ::= place PLUS LP expr COMMA expr RP */ 111, /* (68) position ::= place MINUS LP expr COMMA expr RP */ 111, /* (69) position ::= LP position COMMA position RP */ 111, /* (70) position ::= LP position RP */ 111, /* (71) position ::= expr between position AND position */ 111, /* (72) position ::= expr LT position COMMA position GT */ 111, /* (73) position ::= expr ABOVE position */ 111, /* (74) position ::= expr BELOW position */ 111, /* (75) position ::= expr LEFT OF position */ 111, /* (76) position ::= expr RIGHT OF position */ 111, /* (77) position ::= expr ON HEADING EDGEPT OF position */ 111, /* (78) position ::= expr HEADING EDGEPT OF position */ 111, /* (79) position ::= expr EDGEPT OF position */ 111, /* (80) position ::= expr ON HEADING expr FROM position */ 111, /* (81) position ::= expr HEADING expr FROM position */ 112, /* (82) place ::= edge OF object */ 135, /* (83) place2 ::= object */ 135, /* (84) place2 ::= object DOT_E edge */ 135, /* (85) place2 ::= NTH VERTEX OF object */ 113, /* (86) object ::= nth */ 113, /* (87) object ::= nth OF|IN object */ 114, /* (88) objectname ::= THIS */ 114, /* (89) objectname ::= PLACENAME */ 114, /* (90) objectname ::= objectname DOT_U PLACENAME */ 115, /* (91) nth ::= NTH CLASSNAME */ 115, /* (92) nth ::= NTH LAST CLASSNAME */ 115, /* (93) nth ::= LAST CLASSNAME */ 115, /* (94) nth ::= LAST */ 115, /* (95) nth ::= NTH LB RB */ 115, /* (96) nth ::= NTH LAST LB RB */ 115, /* (97) nth ::= LAST LB RB */ 104, /* (98) expr ::= expr PLUS expr */ 104, /* (99) expr ::= expr MINUS expr */ 104, /* (100) expr ::= expr STAR expr */ 104, /* (101) expr ::= expr SLASH expr */ 104, /* (102) expr ::= MINUS expr */ 104, /* (103) expr ::= PLUS expr */ 104, /* (104) expr ::= LP expr RP */ 104, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ 104, /* (106) expr ::= NUMBER */ 104, /* (107) expr ::= ID */ 104, /* (108) expr ::= FUNC1 LP expr RP */ 104, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ 104, /* (110) expr ::= DIST LP position COMMA position RP */ 104, /* (111) expr ::= place2 DOT_XY X */ 104, /* (112) expr ::= place2 DOT_XY Y */ 104, /* (113) expr ::= object DOT_L numproperty */ 104, /* (114) expr ::= object DOT_L dashproperty */ 104, /* (115) expr ::= object DOT_L colorproperty */ 118, /* (116) lvalue ::= ID */ 118, /* (117) lvalue ::= FILL */ 118, /* (118) lvalue ::= COLOR */ 118, /* (119) lvalue ::= THICKNESS */ 117, /* (120) rvalue ::= expr */ 123, /* (121) print ::= PRINT */ 124, /* (122) prlist ::= pritem */ 124, /* (123) prlist ::= prlist prsep pritem */ 107, /* (124) direction ::= UP */ 107, /* (125) direction ::= DOWN */ 107, /* (126) direction ::= LEFT */ 107, /* (127) direction ::= RIGHT */ 121, /* (128) optrelexpr ::= relexpr */ 127, /* (129) attribute_list ::= alist */ 129, /* (130) alist ::= */ 129, /* (131) alist ::= alist attribute */ 130, /* (132) attribute ::= boolproperty */ 130, /* (133) attribute ::= WITH withclause */ 131, /* (134) go ::= GO */ 131, /* (135) go ::= */ 119, /* (136) even ::= UNTIL EVEN WITH */ 119, /* (137) even ::= EVEN WITH */ 108, /* (138) dashproperty ::= DOTTED */ 108, /* (139) dashproperty ::= DASHED */ 109, /* (140) colorproperty ::= FILL */ 109, /* (141) colorproperty ::= COLOR */ 111, /* (142) position ::= place */ 134, /* (143) between ::= WAY BETWEEN */ 134, /* (144) between ::= BETWEEN */ 134, /* (145) between ::= OF THE WAY BETWEEN */ 112, /* (146) place ::= place2 */ 106, /* (147) edge ::= CENTER */ 106, /* (148) edge ::= EDGEPT */ 106, /* (149) edge ::= TOP */ 106, /* (150) edge ::= BOTTOM */ 106, /* (151) edge ::= START */ 106, /* (152) edge ::= END */ 106, /* (153) edge ::= RIGHT */ 106, /* (154) edge ::= LEFT */ 113, /* (155) object ::= objectname */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number ** of symbols on the right-hand side of that rule. */ static const signed char yyRuleInfoNRhs[] = { -1, /* (0) document ::= statement_list */ -1, /* (1) statement_list ::= statement */ -3, /* (2) statement_list ::= statement_list EOL statement */ 0, /* (3) statement ::= */ -1, /* (4) statement ::= direction */ -3, /* (5) statement ::= lvalue ASSIGN rvalue */ -3, /* (6) statement ::= PLACENAME COLON unnamed_statement */ -3, /* (7) statement ::= PLACENAME COLON position */ -1, /* (8) statement ::= unnamed_statement */ -2, /* (9) statement ::= print prlist */ -6, /* (10) statement ::= ASSERT LP expr EQ expr RP */ -6, /* (11) statement ::= ASSERT LP position EQ position RP */ -3, /* (12) statement ::= DEFINE ID CODEBLOCK */ -1, /* (13) rvalue ::= PLACENAME */ -1, /* (14) pritem ::= FILL */ -1, /* (15) pritem ::= COLOR */ -1, /* (16) pritem ::= THICKNESS */ -1, /* (17) pritem ::= rvalue */ -1, /* (18) pritem ::= STRING */ -1, /* (19) prsep ::= COMMA */ -2, /* (20) unnamed_statement ::= basetype attribute_list */ -1, /* (21) basetype ::= CLASSNAME */ -2, /* (22) basetype ::= STRING textposition */ -4, /* (23) basetype ::= LB savelist statement_list RB */ 0, /* (24) savelist ::= */ -1, /* (25) relexpr ::= expr */ -2, /* (26) relexpr ::= expr PERCENT */ 0, /* (27) optrelexpr ::= */ -2, /* (28) attribute_list ::= relexpr alist */ -2, /* (29) attribute ::= numproperty relexpr */ -2, /* (30) attribute ::= dashproperty expr */ -1, /* (31) attribute ::= dashproperty */ -2, /* (32) attribute ::= colorproperty rvalue */ -3, /* (33) attribute ::= go direction optrelexpr */ -4, /* (34) attribute ::= go direction even position */ -1, /* (35) attribute ::= CLOSE */ -1, /* (36) attribute ::= CHOP */ -2, /* (37) attribute ::= FROM position */ -2, /* (38) attribute ::= TO position */ -1, /* (39) attribute ::= THEN */ -4, /* (40) attribute ::= THEN optrelexpr HEADING expr */ -3, /* (41) attribute ::= THEN optrelexpr EDGEPT */ -4, /* (42) attribute ::= GO optrelexpr HEADING expr */ -3, /* (43) attribute ::= GO optrelexpr EDGEPT */ -2, /* (44) attribute ::= AT position */ -1, /* (45) attribute ::= SAME */ -3, /* (46) attribute ::= SAME AS object */ -2, /* (47) attribute ::= STRING textposition */ -1, /* (48) attribute ::= FIT */ -2, /* (49) attribute ::= BEHIND object */ -4, /* (50) withclause ::= DOT_E edge AT position */ -3, /* (51) withclause ::= edge AT position */ -1, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ -1, /* (53) boolproperty ::= CW */ -1, /* (54) boolproperty ::= CCW */ -1, /* (55) boolproperty ::= LARROW */ -1, /* (56) boolproperty ::= RARROW */ -1, /* (57) boolproperty ::= LRARROW */ -1, /* (58) boolproperty ::= INVIS */ -1, /* (59) boolproperty ::= THICK */ -1, /* (60) boolproperty ::= THIN */ -1, /* (61) boolproperty ::= SOLID */ 0, /* (62) textposition ::= */ -2, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ -3, /* (64) position ::= expr COMMA expr */ -5, /* (65) position ::= place PLUS expr COMMA expr */ -5, /* (66) position ::= place MINUS expr COMMA expr */ -7, /* (67) position ::= place PLUS LP expr COMMA expr RP */ -7, /* (68) position ::= place MINUS LP expr COMMA expr RP */ -5, /* (69) position ::= LP position COMMA position RP */ -3, /* (70) position ::= LP position RP */ -5, /* (71) position ::= expr between position AND position */ -6, /* (72) position ::= expr LT position COMMA position GT */ -3, /* (73) position ::= expr ABOVE position */ -3, /* (74) position ::= expr BELOW position */ -4, /* (75) position ::= expr LEFT OF position */ -4, /* (76) position ::= expr RIGHT OF position */ -6, /* (77) position ::= expr ON HEADING EDGEPT OF position */ -5, /* (78) position ::= expr HEADING EDGEPT OF position */ -4, /* (79) position ::= expr EDGEPT OF position */ -6, /* (80) position ::= expr ON HEADING expr FROM position */ -5, /* (81) position ::= expr HEADING expr FROM position */ -3, /* (82) place ::= edge OF object */ -1, /* (83) place2 ::= object */ -3, /* (84) place2 ::= object DOT_E edge */ -4, /* (85) place2 ::= NTH VERTEX OF object */ -1, /* (86) object ::= nth */ -3, /* (87) object ::= nth OF|IN object */ -1, /* (88) objectname ::= THIS */ -1, /* (89) objectname ::= PLACENAME */ -3, /* (90) objectname ::= objectname DOT_U PLACENAME */ -2, /* (91) nth ::= NTH CLASSNAME */ -3, /* (92) nth ::= NTH LAST CLASSNAME */ -2, /* (93) nth ::= LAST CLASSNAME */ -1, /* (94) nth ::= LAST */ -3, /* (95) nth ::= NTH LB RB */ -4, /* (96) nth ::= NTH LAST LB RB */ -3, /* (97) nth ::= LAST LB RB */ -3, /* (98) expr ::= expr PLUS expr */ -3, /* (99) expr ::= expr MINUS expr */ -3, /* (100) expr ::= expr STAR expr */ -3, /* (101) expr ::= expr SLASH expr */ -2, /* (102) expr ::= MINUS expr */ -2, /* (103) expr ::= PLUS expr */ -3, /* (104) expr ::= LP expr RP */ -3, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ -1, /* (106) expr ::= NUMBER */ -1, /* (107) expr ::= ID */ -4, /* (108) expr ::= FUNC1 LP expr RP */ -6, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ -6, /* (110) expr ::= DIST LP position COMMA position RP */ -3, /* (111) expr ::= place2 DOT_XY X */ -3, /* (112) expr ::= place2 DOT_XY Y */ -3, /* (113) expr ::= object DOT_L numproperty */ -3, /* (114) expr ::= object DOT_L dashproperty */ -3, /* (115) expr ::= object DOT_L colorproperty */ -1, /* (116) lvalue ::= ID */ -1, /* (117) lvalue ::= FILL */ -1, /* (118) lvalue ::= COLOR */ -1, /* (119) lvalue ::= THICKNESS */ -1, /* (120) rvalue ::= expr */ -1, /* (121) print ::= PRINT */ -1, /* (122) prlist ::= pritem */ -3, /* (123) prlist ::= prlist prsep pritem */ -1, /* (124) direction ::= UP */ -1, /* (125) direction ::= DOWN */ -1, /* (126) direction ::= LEFT */ -1, /* (127) direction ::= RIGHT */ -1, /* (128) optrelexpr ::= relexpr */ -1, /* (129) attribute_list ::= alist */ 0, /* (130) alist ::= */ -2, /* (131) alist ::= alist attribute */ -1, /* (132) attribute ::= boolproperty */ -2, /* (133) attribute ::= WITH withclause */ -1, /* (134) go ::= GO */ 0, /* (135) go ::= */ -3, /* (136) even ::= UNTIL EVEN WITH */ -2, /* (137) even ::= EVEN WITH */ -1, /* (138) dashproperty ::= DOTTED */ -1, /* (139) dashproperty ::= DASHED */ -1, /* (140) colorproperty ::= FILL */ -1, /* (141) colorproperty ::= COLOR */ -1, /* (142) position ::= place */ -2, /* (143) between ::= WAY BETWEEN */ -1, /* (144) between ::= BETWEEN */ -4, /* (145) between ::= OF THE WAY BETWEEN */ -1, /* (146) place ::= place2 */ -1, /* (147) edge ::= CENTER */ -1, /* (148) edge ::= EDGEPT */ -1, /* (149) edge ::= TOP */ -1, /* (150) edge ::= BOTTOM */ -1, /* (151) edge ::= START */ -1, /* (152) edge ::= END */ -1, /* (153) edge ::= RIGHT */ -1, /* (154) edge ::= LEFT */ -1, /* (155) object ::= objectname */ }; static void yy_accept(yyParser*); /* Forward Declaration */ /* ** Perform a reduce action and the shift that must immediately ** follow the reduce. ** ** The yyLookahead and yyLookaheadToken parameters provide reduce actions ** access to the lookahead token (if any). The yyLookahead will be YYNOCODE ** if the lookahead token has already been consumed. As this procedure is ** only called from one place, optimizing compilers will in-line it, which ** means that the extra parameters have no performance impact. */ static YYACTIONTYPE yy_reduce( yyParser *yypParser, /* The parser */ unsigned int yyruleno, /* Number of the rule by which to reduce */ int yyLookahead, /* Lookahead token, or YYNOCODE if none */ pik_parserTOKENTYPE yyLookaheadToken /* Value of the lookahead token */ pik_parserCTX_PDECL /* %extra_context */ ){ int yygoto; /* The next state */ YYACTIONTYPE yyact; /* The next action */ yyStackEntry *yymsp; /* The top of the parser's stack */ int yysize; /* Amount to pop the stack */ pik_parserARG_FETCH (void)yyLookahead; (void)yyLookaheadToken; yymsp = yypParser->yytos; switch( yyruleno ){ /* Beginning here are the reduction cases. A typical example ** follows: ** case 0: ** #line <lineno> <grammarfile> ** { ... } // User supplied code ** #line <lineno> <thisfile> ** break; */ /********** Begin reduce actions **********************************************/ YYMINORTYPE yylhsminor; case 0: /* document ::= statement_list */ #line 549 "pikchr.y" {pik_render(p,yymsp[0].minor.yy235);} #line 2451 "pikchr.c" break; case 1: /* statement_list ::= statement */ #line 552 "pikchr.y" { yylhsminor.yy235 = pik_elist_append(p,0,yymsp[0].minor.yy162); } #line 2456 "pikchr.c" yymsp[0].minor.yy235 = yylhsminor.yy235; break; case 2: /* statement_list ::= statement_list EOL statement */ #line 554 "pikchr.y" { yylhsminor.yy235 = pik_elist_append(p,yymsp[-2].minor.yy235,yymsp[0].minor.yy162); } #line 2462 "pikchr.c" yymsp[-2].minor.yy235 = yylhsminor.yy235; break; case 3: /* statement ::= */ #line 557 "pikchr.y" { yymsp[1].minor.yy162 = 0; } #line 2468 "pikchr.c" break; case 4: /* statement ::= direction */ #line 558 "pikchr.y" { pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy162=0; } #line 2473 "pikchr.c" yymsp[0].minor.yy162 = yylhsminor.yy162; break; case 5: /* statement ::= lvalue ASSIGN rvalue */ #line 559 "pikchr.y" {pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy21,&yymsp[-1].minor.yy0); yylhsminor.yy162=0;} #line 2479 "pikchr.c" yymsp[-2].minor.yy162 = yylhsminor.yy162; break; case 6: /* statement ::= PLACENAME COLON unnamed_statement */ #line 561 "pikchr.y" { yylhsminor.yy162 = yymsp[0].minor.yy162; pik_elem_setname(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0); } #line 2485 "pikchr.c" yymsp[-2].minor.yy162 = yylhsminor.yy162; break; case 7: /* statement ::= PLACENAME COLON position */ #line 563 "pikchr.y" { yylhsminor.yy162 = pik_elem_new(p,0,0,0); if(yylhsminor.yy162){ yylhsminor.yy162->ptAt = yymsp[0].minor.yy63; pik_elem_setname(p,yylhsminor.yy162,&yymsp[-2].minor.yy0); }} #line 2492 "pikchr.c" yymsp[-2].minor.yy162 = yylhsminor.yy162; break; case 8: /* statement ::= unnamed_statement */ #line 565 "pikchr.y" {yylhsminor.yy162 = yymsp[0].minor.yy162;} #line 2498 "pikchr.c" yymsp[0].minor.yy162 = yylhsminor.yy162; break; case 9: /* statement ::= print prlist */ #line 566 "pikchr.y" {pik_append(p,"<br>\n",5); yymsp[-1].minor.yy162=0;} #line 2504 "pikchr.c" break; case 10: /* statement ::= ASSERT LP expr EQ expr RP */ #line 571 "pikchr.y" {yymsp[-5].minor.yy162=pik_assert(p,yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy21);} #line 2509 "pikchr.c" break; case 11: /* statement ::= ASSERT LP position EQ position RP */ #line 573 "pikchr.y" {yymsp[-5].minor.yy162=pik_position_assert(p,&yymsp[-3].minor.yy63,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy63);} #line 2514 "pikchr.c" break; case 12: /* statement ::= DEFINE ID CODEBLOCK */ #line 574 "pikchr.y" {yymsp[-2].minor.yy162=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} #line 2519 "pikchr.c" break; case 13: /* rvalue ::= PLACENAME */ #line 585 "pikchr.y" {yylhsminor.yy21 = pik_lookup_color(p,&yymsp[0].minor.yy0);} #line 2524 "pikchr.c" yymsp[0].minor.yy21 = yylhsminor.yy21; break; case 14: /* pritem ::= FILL */ case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15); case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16); #line 590 "pikchr.y" {pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));} #line 2532 "pikchr.c" break; case 17: /* pritem ::= rvalue */ #line 593 "pikchr.y" {pik_append_num(p,"",yymsp[0].minor.yy21);} #line 2537 "pikchr.c" break; case 18: /* pritem ::= STRING */ #line 594 "pikchr.y" {pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);} #line 2542 "pikchr.c" break; case 19: /* prsep ::= COMMA */ #line 595 "pikchr.y" {pik_append(p, " ", 1);} #line 2547 "pikchr.c" break; case 20: /* unnamed_statement ::= basetype attribute_list */ #line 598 "pikchr.y" {yylhsminor.yy162 = yymsp[-1].minor.yy162; pik_after_adding_attributes(p,yylhsminor.yy162);} #line 2552 "pikchr.c" yymsp[-1].minor.yy162 = yylhsminor.yy162; break; case 21: /* basetype ::= CLASSNAME */ #line 600 "pikchr.y" {yylhsminor.yy162 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } #line 2558 "pikchr.c" yymsp[0].minor.yy162 = yylhsminor.yy162; break; case 22: /* basetype ::= STRING textposition */ #line 602 "pikchr.y" {yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy188; yylhsminor.yy162 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } #line 2564 "pikchr.c" yymsp[-1].minor.yy162 = yylhsminor.yy162; break; case 23: /* basetype ::= LB savelist statement_list RB */ #line 604 "pikchr.y" { p->list = yymsp[-2].minor.yy235; yymsp[-3].minor.yy162 = pik_elem_new(p,0,0,yymsp[-1].minor.yy235); if(yymsp[-3].minor.yy162) yymsp[-3].minor.yy162->errTok = yymsp[0].minor.yy0; } #line 2570 "pikchr.c" break; case 24: /* savelist ::= */ #line 609 "pikchr.y" {yymsp[1].minor.yy235 = p->list; p->list = 0;} #line 2575 "pikchr.c" break; case 25: /* relexpr ::= expr */ #line 616 "pikchr.y" {yylhsminor.yy72.rAbs = yymsp[0].minor.yy21; yylhsminor.yy72.rRel = 0;} #line 2580 "pikchr.c" yymsp[0].minor.yy72 = yylhsminor.yy72; break; case 26: /* relexpr ::= expr PERCENT */ #line 617 "pikchr.y" {yylhsminor.yy72.rAbs = 0; yylhsminor.yy72.rRel = yymsp[-1].minor.yy21/100;} #line 2586 "pikchr.c" yymsp[-1].minor.yy72 = yylhsminor.yy72; break; case 27: /* optrelexpr ::= */ #line 619 "pikchr.y" {yymsp[1].minor.yy72.rAbs = 0; yymsp[1].minor.yy72.rRel = 1.0;} #line 2592 "pikchr.c" break; case 28: /* attribute_list ::= relexpr alist */ #line 621 "pikchr.y" {pik_add_direction(p,0,&yymsp[-1].minor.yy72);} #line 2597 "pikchr.c" break; case 29: /* attribute ::= numproperty relexpr */ #line 625 "pikchr.y" { pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72); } #line 2602 "pikchr.c" break; case 30: /* attribute ::= dashproperty expr */ #line 626 "pikchr.y" { pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy21); } #line 2607 "pikchr.c" break; case 31: /* attribute ::= dashproperty */ #line 627 "pikchr.y" { pik_set_dashed(p,&yymsp[0].minor.yy0,0); } #line 2612 "pikchr.c" break; case 32: /* attribute ::= colorproperty rvalue */ #line 628 "pikchr.y" { pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21); } #line 2617 "pikchr.c" break; case 33: /* attribute ::= go direction optrelexpr */ #line 629 "pikchr.y" { pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72);} #line 2622 "pikchr.c" break; case 34: /* attribute ::= go direction even position */ #line 630 "pikchr.y" {pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63);} #line 2627 "pikchr.c" break; case 35: /* attribute ::= CLOSE */ #line 631 "pikchr.y" { pik_close_path(p,&yymsp[0].minor.yy0); } #line 2632 "pikchr.c" break; case 36: /* attribute ::= CHOP */ #line 632 "pikchr.y" { p->cur->bChop = 1; } #line 2637 "pikchr.c" break; case 37: /* attribute ::= FROM position */ #line 633 "pikchr.y" { pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } #line 2642 "pikchr.c" break; case 38: /* attribute ::= TO position */ #line 634 "pikchr.y" { pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } #line 2647 "pikchr.c" break; case 39: /* attribute ::= THEN */ #line 635 "pikchr.y" { pik_then(p, &yymsp[0].minor.yy0, p->cur); } #line 2652 "pikchr.c" break; case 40: /* attribute ::= THEN optrelexpr HEADING expr */ case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42); #line 637 "pikchr.y" {pik_move_hdg(p,&yymsp[-2].minor.yy72,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21,0,&yymsp[-3].minor.yy0);} #line 2658 "pikchr.c" break; case 41: /* attribute ::= THEN optrelexpr EDGEPT */ case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43); #line 638 "pikchr.y" {pik_move_hdg(p,&yymsp[-1].minor.yy72,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} #line 2664 "pikchr.c" break; case 44: /* attribute ::= AT position */ #line 643 "pikchr.y" { pik_set_at(p,0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } #line 2669 "pikchr.c" break; case 45: /* attribute ::= SAME */ #line 645 "pikchr.y" {pik_same(p,0,&yymsp[0].minor.yy0);} #line 2674 "pikchr.c" break; case 46: /* attribute ::= SAME AS object */ #line 646 "pikchr.y" {pik_same(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} #line 2679 "pikchr.c" break; case 47: /* attribute ::= STRING textposition */ #line 647 "pikchr.y" {pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy188);} #line 2684 "pikchr.c" break; case 48: /* attribute ::= FIT */ #line 648 "pikchr.y" {pik_size_to_fit(p,&yymsp[0].minor.yy0,3); } #line 2689 "pikchr.c" break; case 49: /* attribute ::= BEHIND object */ #line 649 "pikchr.y" {pik_behind(p,yymsp[0].minor.yy162);} #line 2694 "pikchr.c" break; case 50: /* withclause ::= DOT_E edge AT position */ case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51); #line 657 "pikchr.y" { pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } #line 2700 "pikchr.c" break; case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ #line 661 "pikchr.y" {yylhsminor.yy0 = yymsp[0].minor.yy0;} #line 2705 "pikchr.c" yymsp[0].minor.yy0 = yylhsminor.yy0; break; case 53: /* boolproperty ::= CW */ #line 672 "pikchr.y" {p->cur->cw = 1;} #line 2711 "pikchr.c" break; case 54: /* boolproperty ::= CCW */ #line 673 "pikchr.y" {p->cur->cw = 0;} #line 2716 "pikchr.c" break; case 55: /* boolproperty ::= LARROW */ #line 674 "pikchr.y" {p->cur->larrow=1; p->cur->rarrow=0; } #line 2721 "pikchr.c" break; case 56: /* boolproperty ::= RARROW */ #line 675 "pikchr.y" {p->cur->larrow=0; p->cur->rarrow=1; } #line 2726 "pikchr.c" break; case 57: /* boolproperty ::= LRARROW */ #line 676 "pikchr.y" {p->cur->larrow=1; p->cur->rarrow=1; } #line 2731 "pikchr.c" break; case 58: /* boolproperty ::= INVIS */ #line 677 "pikchr.y" {p->cur->sw = -0.00001;} #line 2736 "pikchr.c" break; case 59: /* boolproperty ::= THICK */ #line 678 "pikchr.y" {p->cur->sw *= 1.5;} #line 2741 "pikchr.c" break; case 60: /* boolproperty ::= THIN */ #line 679 "pikchr.y" {p->cur->sw *= 0.67;} #line 2746 "pikchr.c" break; case 61: /* boolproperty ::= SOLID */ #line 680 "pikchr.y" {p->cur->sw = pik_value(p,"thickness",9,0); p->cur->dotted = p->cur->dashed = 0.0;} #line 2752 "pikchr.c" break; case 62: /* textposition ::= */ #line 683 "pikchr.y" {yymsp[1].minor.yy188 = 0;} #line 2757 "pikchr.c" break; case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ #line 686 "pikchr.y" {yylhsminor.yy188 = (short int)pik_text_position(yymsp[-1].minor.yy188,&yymsp[0].minor.yy0);} #line 2762 "pikchr.c" yymsp[-1].minor.yy188 = yylhsminor.yy188; break; case 64: /* position ::= expr COMMA expr */ #line 689 "pikchr.y" {yylhsminor.yy63.x=yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[0].minor.yy21;} #line 2768 "pikchr.c" yymsp[-2].minor.yy63 = yylhsminor.yy63; break; case 65: /* position ::= place PLUS expr COMMA expr */ #line 691 "pikchr.y" {yylhsminor.yy63.x=yymsp[-4].minor.yy63.x+yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y+yymsp[0].minor.yy21;} #line 2774 "pikchr.c" yymsp[-4].minor.yy63 = yylhsminor.yy63; break; case 66: /* position ::= place MINUS expr COMMA expr */ #line 692 "pikchr.y" {yylhsminor.yy63.x=yymsp[-4].minor.yy63.x-yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y-yymsp[0].minor.yy21;} #line 2780 "pikchr.c" yymsp[-4].minor.yy63 = yylhsminor.yy63; break; case 67: /* position ::= place PLUS LP expr COMMA expr RP */ #line 694 "pikchr.y" {yylhsminor.yy63.x=yymsp[-6].minor.yy63.x+yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y+yymsp[-1].minor.yy21;} #line 2786 "pikchr.c" yymsp[-6].minor.yy63 = yylhsminor.yy63; break; case 68: /* position ::= place MINUS LP expr COMMA expr RP */ #line 696 "pikchr.y" {yylhsminor.yy63.x=yymsp[-6].minor.yy63.x-yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y-yymsp[-1].minor.yy21;} #line 2792 "pikchr.c" yymsp[-6].minor.yy63 = yylhsminor.yy63; break; case 69: /* position ::= LP position COMMA position RP */ #line 697 "pikchr.y" {yymsp[-4].minor.yy63.x=yymsp[-3].minor.yy63.x; yymsp[-4].minor.yy63.y=yymsp[-1].minor.yy63.y;} #line 2798 "pikchr.c" break; case 70: /* position ::= LP position RP */ #line 698 "pikchr.y" {yymsp[-2].minor.yy63=yymsp[-1].minor.yy63;} #line 2803 "pikchr.c" break; case 71: /* position ::= expr between position AND position */ #line 700 "pikchr.y" {yylhsminor.yy63 = pik_position_between(yymsp[-4].minor.yy21,yymsp[-2].minor.yy63,yymsp[0].minor.yy63);} #line 2808 "pikchr.c" yymsp[-4].minor.yy63 = yylhsminor.yy63; break; case 72: /* position ::= expr LT position COMMA position GT */ #line 702 "pikchr.y" {yylhsminor.yy63 = pik_position_between(yymsp[-5].minor.yy21,yymsp[-3].minor.yy63,yymsp[-1].minor.yy63);} #line 2814 "pikchr.c" yymsp[-5].minor.yy63 = yylhsminor.yy63; break; case 73: /* position ::= expr ABOVE position */ #line 703 "pikchr.y" {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y += yymsp[-2].minor.yy21;} #line 2820 "pikchr.c" yymsp[-2].minor.yy63 = yylhsminor.yy63; break; case 74: /* position ::= expr BELOW position */ #line 704 "pikchr.y" {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y -= yymsp[-2].minor.yy21;} #line 2826 "pikchr.c" yymsp[-2].minor.yy63 = yylhsminor.yy63; break; case 75: /* position ::= expr LEFT OF position */ #line 705 "pikchr.y" {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x -= yymsp[-3].minor.yy21;} #line 2832 "pikchr.c" yymsp[-3].minor.yy63 = yylhsminor.yy63; break; case 76: /* position ::= expr RIGHT OF position */ #line 706 "pikchr.y" {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x += yymsp[-3].minor.yy21;} #line 2838 "pikchr.c" yymsp[-3].minor.yy63 = yylhsminor.yy63; break; case 77: /* position ::= expr ON HEADING EDGEPT OF position */ #line 708 "pikchr.y" {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-5].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} #line 2844 "pikchr.c" yymsp[-5].minor.yy63 = yylhsminor.yy63; break; case 78: /* position ::= expr HEADING EDGEPT OF position */ #line 710 "pikchr.y" {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-4].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} #line 2850 "pikchr.c" yymsp[-4].minor.yy63 = yylhsminor.yy63; break; case 79: /* position ::= expr EDGEPT OF position */ #line 712 "pikchr.y" {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} #line 2856 "pikchr.c" yymsp[-3].minor.yy63 = yylhsminor.yy63; break; case 80: /* position ::= expr ON HEADING expr FROM position */ #line 714 "pikchr.y" {yylhsminor.yy63 = pik_position_at_angle(yymsp[-5].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} #line 2862 "pikchr.c" yymsp[-5].minor.yy63 = yylhsminor.yy63; break; case 81: /* position ::= expr HEADING expr FROM position */ #line 716 "pikchr.y" {yylhsminor.yy63 = pik_position_at_angle(yymsp[-4].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} #line 2868 "pikchr.c" yymsp[-4].minor.yy63 = yylhsminor.yy63; break; case 82: /* place ::= edge OF object */ #line 728 "pikchr.y" {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} #line 2874 "pikchr.c" yymsp[-2].minor.yy63 = yylhsminor.yy63; break; case 83: /* place2 ::= object */ #line 729 "pikchr.y" {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,0);} #line 2880 "pikchr.c" yymsp[0].minor.yy63 = yylhsminor.yy63; break; case 84: /* place2 ::= object DOT_E edge */ #line 730 "pikchr.y" {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} #line 2886 "pikchr.c" yymsp[-2].minor.yy63 = yylhsminor.yy63; break; case 85: /* place2 ::= NTH VERTEX OF object */ #line 731 "pikchr.y" {yylhsminor.yy63 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy162);} #line 2892 "pikchr.c" yymsp[-3].minor.yy63 = yylhsminor.yy63; break; case 86: /* object ::= nth */ #line 743 "pikchr.y" {yylhsminor.yy162 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} #line 2898 "pikchr.c" yymsp[0].minor.yy162 = yylhsminor.yy162; break; case 87: /* object ::= nth OF|IN object */ #line 744 "pikchr.y" {yylhsminor.yy162 = pik_find_nth(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} #line 2904 "pikchr.c" yymsp[-2].minor.yy162 = yylhsminor.yy162; break; case 88: /* objectname ::= THIS */ #line 746 "pikchr.y" {yymsp[0].minor.yy162 = p->cur;} #line 2910 "pikchr.c" break; case 89: /* objectname ::= PLACENAME */ #line 747 "pikchr.y" {yylhsminor.yy162 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} #line 2915 "pikchr.c" yymsp[0].minor.yy162 = yylhsminor.yy162; break; case 90: /* objectname ::= objectname DOT_U PLACENAME */ #line 749 "pikchr.y" {yylhsminor.yy162 = pik_find_byname(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} #line 2921 "pikchr.c" yymsp[-2].minor.yy162 = yylhsminor.yy162; break; case 91: /* nth ::= NTH CLASSNAME */ #line 751 "pikchr.y" {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); } #line 2927 "pikchr.c" yymsp[-1].minor.yy0 = yylhsminor.yy0; break; case 92: /* nth ::= NTH LAST CLASSNAME */ #line 752 "pikchr.y" {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); } #line 2933 "pikchr.c" yymsp[-2].minor.yy0 = yylhsminor.yy0; break; case 93: /* nth ::= LAST CLASSNAME */ #line 753 "pikchr.y" {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;} #line 2939 "pikchr.c" break; case 94: /* nth ::= LAST */ #line 754 "pikchr.y" {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;} #line 2944 "pikchr.c" yymsp[0].minor.yy0 = yylhsminor.yy0; break; case 95: /* nth ::= NTH LB RB */ #line 755 "pikchr.y" {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);} #line 2950 "pikchr.c" yymsp[-2].minor.yy0 = yylhsminor.yy0; break; case 96: /* nth ::= NTH LAST LB RB */ #line 756 "pikchr.y" {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);} #line 2956 "pikchr.c" yymsp[-3].minor.yy0 = yylhsminor.yy0; break; case 97: /* nth ::= LAST LB RB */ #line 757 "pikchr.y" {yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; } #line 2962 "pikchr.c" break; case 98: /* expr ::= expr PLUS expr */ #line 759 "pikchr.y" {yylhsminor.yy21=yymsp[-2].minor.yy21+yymsp[0].minor.yy21;} #line 2967 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 99: /* expr ::= expr MINUS expr */ #line 760 "pikchr.y" {yylhsminor.yy21=yymsp[-2].minor.yy21-yymsp[0].minor.yy21;} #line 2973 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 100: /* expr ::= expr STAR expr */ #line 761 "pikchr.y" {yylhsminor.yy21=yymsp[-2].minor.yy21*yymsp[0].minor.yy21;} #line 2979 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 101: /* expr ::= expr SLASH expr */ #line 762 "pikchr.y" { if( yymsp[0].minor.yy21==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy21 = 0.0; } else{ yylhsminor.yy21 = yymsp[-2].minor.yy21/yymsp[0].minor.yy21; } } #line 2988 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 102: /* expr ::= MINUS expr */ #line 766 "pikchr.y" {yymsp[-1].minor.yy21=-yymsp[0].minor.yy21;} #line 2994 "pikchr.c" break; case 103: /* expr ::= PLUS expr */ #line 767 "pikchr.y" {yymsp[-1].minor.yy21=yymsp[0].minor.yy21;} #line 2999 "pikchr.c" break; case 104: /* expr ::= LP expr RP */ #line 768 "pikchr.y" {yymsp[-2].minor.yy21=yymsp[-1].minor.yy21;} #line 3004 "pikchr.c" break; case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */ #line 769 "pikchr.y" {yymsp[-2].minor.yy21=pik_get_var(p,&yymsp[-1].minor.yy0);} #line 3009 "pikchr.c" break; case 106: /* expr ::= NUMBER */ #line 770 "pikchr.y" {yylhsminor.yy21=pik_atof(&yymsp[0].minor.yy0);} #line 3014 "pikchr.c" yymsp[0].minor.yy21 = yylhsminor.yy21; break; case 107: /* expr ::= ID */ #line 771 "pikchr.y" {yylhsminor.yy21=pik_get_var(p,&yymsp[0].minor.yy0);} #line 3020 "pikchr.c" yymsp[0].minor.yy21 = yylhsminor.yy21; break; case 108: /* expr ::= FUNC1 LP expr RP */ #line 772 "pikchr.y" {yylhsminor.yy21 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy21,0.0);} #line 3026 "pikchr.c" yymsp[-3].minor.yy21 = yylhsminor.yy21; break; case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */ #line 773 "pikchr.y" {yylhsminor.yy21 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy21,yymsp[-1].minor.yy21);} #line 3032 "pikchr.c" yymsp[-5].minor.yy21 = yylhsminor.yy21; break; case 110: /* expr ::= DIST LP position COMMA position RP */ #line 774 "pikchr.y" {yymsp[-5].minor.yy21 = pik_dist(&yymsp[-3].minor.yy63,&yymsp[-1].minor.yy63);} #line 3038 "pikchr.c" break; case 111: /* expr ::= place2 DOT_XY X */ #line 775 "pikchr.y" {yylhsminor.yy21 = yymsp[-2].minor.yy63.x;} #line 3043 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 112: /* expr ::= place2 DOT_XY Y */ #line 776 "pikchr.y" {yylhsminor.yy21 = yymsp[-2].minor.yy63.y;} #line 3049 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; case 113: /* expr ::= object DOT_L numproperty */ case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114); case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115); #line 777 "pikchr.y" {yylhsminor.yy21=pik_property_of(yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} #line 3057 "pikchr.c" yymsp[-2].minor.yy21 = yylhsminor.yy21; break; default: /* (116) lvalue ::= ID */ yytestcase(yyruleno==116); /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117); /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118); /* (119) lvalue ::= THICKNESS */ yytestcase(yyruleno==119); /* (120) rvalue ::= expr */ yytestcase(yyruleno==120); /* (121) print ::= PRINT */ yytestcase(yyruleno==121); /* (122) prlist ::= pritem (OPTIMIZED OUT) */ assert(yyruleno!=122); /* (123) prlist ::= prlist prsep pritem */ yytestcase(yyruleno==123); /* (124) direction ::= UP */ yytestcase(yyruleno==124); /* (125) direction ::= DOWN */ yytestcase(yyruleno==125); /* (126) direction ::= LEFT */ yytestcase(yyruleno==126); /* (127) direction ::= RIGHT */ yytestcase(yyruleno==127); /* (128) optrelexpr ::= relexpr (OPTIMIZED OUT) */ assert(yyruleno!=128); /* (129) attribute_list ::= alist */ yytestcase(yyruleno==129); /* (130) alist ::= */ yytestcase(yyruleno==130); /* (131) alist ::= alist attribute */ yytestcase(yyruleno==131); /* (132) attribute ::= boolproperty (OPTIMIZED OUT) */ assert(yyruleno!=132); /* (133) attribute ::= WITH withclause */ yytestcase(yyruleno==133); /* (134) go ::= GO */ yytestcase(yyruleno==134); /* (135) go ::= */ yytestcase(yyruleno==135); /* (136) even ::= UNTIL EVEN WITH */ yytestcase(yyruleno==136); /* (137) even ::= EVEN WITH */ yytestcase(yyruleno==137); /* (138) dashproperty ::= DOTTED */ yytestcase(yyruleno==138); /* (139) dashproperty ::= DASHED */ yytestcase(yyruleno==139); /* (140) colorproperty ::= FILL */ yytestcase(yyruleno==140); /* (141) colorproperty ::= COLOR */ yytestcase(yyruleno==141); /* (142) position ::= place */ yytestcase(yyruleno==142); /* (143) between ::= WAY BETWEEN */ yytestcase(yyruleno==143); /* (144) between ::= BETWEEN */ yytestcase(yyruleno==144); /* (145) between ::= OF THE WAY BETWEEN */ yytestcase(yyruleno==145); /* (146) place ::= place2 */ yytestcase(yyruleno==146); /* (147) edge ::= CENTER */ yytestcase(yyruleno==147); /* (148) edge ::= EDGEPT */ yytestcase(yyruleno==148); /* (149) edge ::= TOP */ yytestcase(yyruleno==149); /* (150) edge ::= BOTTOM */ yytestcase(yyruleno==150); /* (151) edge ::= START */ yytestcase(yyruleno==151); /* (152) edge ::= END */ yytestcase(yyruleno==152); /* (153) edge ::= RIGHT */ yytestcase(yyruleno==153); /* (154) edge ::= LEFT */ yytestcase(yyruleno==154); /* (155) object ::= objectname */ yytestcase(yyruleno==155); break; /********** End reduce actions ************************************************/ }; assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) ); yygoto = yyRuleInfoLhs[yyruleno]; yysize = yyRuleInfoNRhs[yyruleno]; yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto); /* There are no SHIFTREDUCE actions on nonterminals because the table ** generator has simplified them to pure REDUCE actions. */ assert( !(yyact>YY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) ); /* It is not possible for a REDUCE to be followed by an error */ assert( yyact!=YY_ERROR_ACTION ); yymsp += yysize+1; yypParser->yytos = yymsp; yymsp->stateno = (YYACTIONTYPE)yyact; yymsp->major = (YYCODETYPE)yygoto; yyTraceShift(yypParser, yyact, "... then shift"); return yyact; } /* ** The following code executes when the parse fails */ #ifndef YYNOERRORRECOVERY static void yy_parse_failed( yyParser *yypParser /* The parser */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); } #endif while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); /* Here code is inserted which will be executed whenever the ** parser fails */ /************ Begin %parse_failure code ***************************************/ /************ End %parse_failure code *****************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ pik_parserCTX_STORE } #endif /* YYNOERRORRECOVERY */ /* ** The following code executes when a syntax error first occurs. */ static void yy_syntax_error( yyParser *yypParser, /* The parser */ int yymajor, /* The major type of the error token */ pik_parserTOKENTYPE yyminor /* The minor type of the error token */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH #define TOKEN yyminor /************ Begin %syntax_error code ****************************************/ #line 537 "pikchr.y" if( TOKEN.z && TOKEN.z[0] ){ pik_error(p, &TOKEN, "syntax error"); }else{ pik_error(p, 0, "syntax error"); } UNUSED_PARAMETER(yymajor); #line 3168 "pikchr.c" /************ End %syntax_error code ******************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ pik_parserCTX_STORE } /* ** The following is executed when the parser accepts */ static void yy_accept( yyParser *yypParser /* The parser */ ){ pik_parserARG_FETCH pik_parserCTX_FETCH #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); } #endif #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif assert( yypParser->yytos==yypParser->yystack ); /* Here code is inserted which will be executed whenever the ** parser accepts */ /*********** Begin %parse_accept code *****************************************/ /*********** End %parse_accept code *******************************************/ pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ pik_parserCTX_STORE } /* The main parser program. ** The first argument is a pointer to a structure obtained from ** "pik_parserAlloc" which describes the current state of the parser. ** The second argument is the major token number. The third is ** the minor token. The fourth optional argument is whatever the ** user wants (and specified in the grammar) and is available for ** use by the action routines. ** ** Inputs: ** <ul> ** <li> A pointer to the parser (an opaque structure.) ** <li> The major token number. ** <li> The minor token number. ** <li> An option argument of a grammar-specified type. ** </ul> ** ** Outputs: ** None. */ void pik_parser( void *yyp, /* The parser */ int yymajor, /* The major token code number */ pik_parserTOKENTYPE yyminor /* The value for the token */ pik_parserARG_PDECL /* Optional %extra_argument parameter */ ){ YYMINORTYPE yyminorunion; YYACTIONTYPE yyact; /* The parser action. */ #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) int yyendofinput; /* True if we are at the end of input */ #endif #ifdef YYERRORSYMBOL int yyerrorhit = 0; /* True if yymajor has invoked an error */ #endif yyParser *yypParser = (yyParser*)yyp; /* The parser */ pik_parserCTX_FETCH pik_parserARG_STORE assert( yypParser->yytos!=0 ); #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) yyendofinput = (yymajor==0); #endif yyact = yypParser->yytos->stateno; #ifndef NDEBUG if( yyTraceFILE ){ if( yyact < YY_MIN_REDUCE ){ fprintf(yyTraceFILE,"%sInput '%s' in state %d\n", yyTracePrompt,yyTokenName[yymajor],yyact); }else{ fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n", yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE); } } #endif while(1){ /* Exit by "break" */ assert( yypParser->yytos>=yypParser->yystack ); assert( yyact==yypParser->yytos->stateno ); yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact); if( yyact >= YY_MIN_REDUCE ){ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */ #ifndef NDEBUG assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); if( yyTraceFILE ){ int yysize = yyRuleInfoNRhs[yyruleno]; if( yysize ){ fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", yyTracePrompt, yyruleno, yyRuleName[yyruleno], yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action", yypParser->yytos[yysize].stateno); }else{ fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n", yyTracePrompt, yyruleno, yyRuleName[yyruleno], yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action"); } } #endif /* NDEBUG */ /* Check that the stack is large enough to grow by a single entry ** if the RHS of the rule is empty. This ensures that there is room ** enough on the stack to push the LHS value */ if( yyRuleInfoNRhs[yyruleno]==0 ){ #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); } #endif if( yypParser->yytos>=yypParser->yystackEnd ){ if( yyGrowStack(yypParser) ){ yyStackOverflow(yypParser); break; } } } yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor pik_parserCTX_PARAM); }else if( yyact <= YY_MAX_SHIFTREDUCE ){ yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt--; #endif break; }else if( yyact==YY_ACCEPT_ACTION ){ yypParser->yytos--; yy_accept(yypParser); return; }else{ assert( yyact == YY_ERROR_ACTION ); yyminorunion.yy0 = yyminor; #ifdef YYERRORSYMBOL int yymx; #endif #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); } #endif #ifdef YYERRORSYMBOL /* A syntax error has occurred. ** The response to an error depends upon whether or not the ** grammar defines an error token "ERROR". ** ** This is what we do if the grammar does define ERROR: ** ** * Call the %syntax_error function. ** ** * Begin popping the stack until we enter a state where ** it is legal to shift the error symbol, then shift ** the error symbol. ** ** * Set the error count to three. ** ** * Begin accepting and shifting new tokens. No new error ** processing will occur until three tokens have been ** shifted successfully. ** */ if( yypParser->yyerrcnt<0 ){ yy_syntax_error(yypParser,yymajor,yyminor); } yymx = yypParser->yytos->major; if( yymx==YYERRORSYMBOL || yyerrorhit ){ #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sDiscard input token %s\n", yyTracePrompt,yyTokenName[yymajor]); } #endif yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); yymajor = YYNOCODE; }else{ while( yypParser->yytos > yypParser->yystack ){ yyact = yy_find_reduce_action(yypParser->yytos->stateno, YYERRORSYMBOL); if( yyact<=YY_MAX_SHIFTREDUCE ) break; yy_pop_parser_stack(yypParser); } if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); yy_parse_failed(yypParser); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif yymajor = YYNOCODE; }else if( yymx!=YYERRORSYMBOL ){ yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor); } } yypParser->yyerrcnt = 3; yyerrorhit = 1; if( yymajor==YYNOCODE ) break; yyact = yypParser->yytos->stateno; #elif defined(YYNOERRORRECOVERY) /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to ** do any kind of error recovery. Instead, simply invoke the syntax ** error routine and continue going as if nothing had happened. ** ** Applications can set this macro (for example inside %include) if ** they intend to abandon the parse upon the first syntax error seen. */ yy_syntax_error(yypParser,yymajor, yyminor); yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); break; #else /* YYERRORSYMBOL is not defined */ /* This is what we do if the grammar does not define ERROR: ** ** * Report an error message, and throw away the input token. ** ** * If the input token is $, then fail the parse. ** ** As before, subsequent error messages are suppressed until ** three input tokens have been successfully shifted. */ if( yypParser->yyerrcnt<=0 ){ yy_syntax_error(yypParser,yymajor, yyminor); } yypParser->yyerrcnt = 3; yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); if( yyendofinput ){ yy_parse_failed(yypParser); #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif } break; #endif } } #ifndef NDEBUG if( yyTraceFILE ){ yyStackEntry *i; char cDiv = '['; fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt); for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){ fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]); cDiv = ' '; } fprintf(yyTraceFILE,"]\n"); } #endif return; } /* ** Return the fallback token corresponding to canonical token iToken, or ** 0 if iToken has no fallback. */ int pik_parserFallback(int iToken){ #ifdef YYFALLBACK assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); return yyFallback[iToken]; #else (void)iToken; return 0; #endif } #line 782 "pikchr.y" /* Chart of the 148 official CSS color names with their ** corresponding RGB values thru Color Module Level 4: ** https://developer.mozilla.org/en-US/docs/Web/CSS/color_value ** ** Two new names "None" and "Off" are added with a value ** of -1. */ static const struct { const char *zName; /* Name of the color */ int val; /* RGB value */ } aColor[] = { { "AliceBlue", 0xf0f8ff }, { "AntiqueWhite", 0xfaebd7 }, { "Aqua", 0x00ffff }, { "Aquamarine", 0x7fffd4 }, { "Azure", 0xf0ffff }, { "Beige", 0xf5f5dc }, { "Bisque", 0xffe4c4 }, { "Black", 0x000000 }, { "BlanchedAlmond", 0xffebcd }, { "Blue", 0x0000ff }, { "BlueViolet", 0x8a2be2 }, { "Brown", 0xa52a2a }, { "BurlyWood", 0xdeb887 }, { "CadetBlue", 0x5f9ea0 }, { "Chartreuse", 0x7fff00 }, { "Chocolate", 0xd2691e }, { "Coral", 0xff7f50 }, { "CornflowerBlue", 0x6495ed }, { "Cornsilk", 0xfff8dc }, { "Crimson", 0xdc143c }, { "Cyan", 0x00ffff }, { "DarkBlue", 0x00008b }, { "DarkCyan", 0x008b8b }, { "DarkGoldenrod", 0xb8860b }, { "DarkGray", 0xa9a9a9 }, { "DarkGreen", 0x006400 }, { "DarkGrey", 0xa9a9a9 }, { "DarkKhaki", 0xbdb76b }, { "DarkMagenta", 0x8b008b }, { "DarkOliveGreen", 0x556b2f }, { "DarkOrange", 0xff8c00 }, { "DarkOrchid", 0x9932cc }, { "DarkRed", 0x8b0000 }, { "DarkSalmon", 0xe9967a }, { "DarkSeaGreen", 0x8fbc8f }, { "DarkSlateBlue", 0x483d8b }, { "DarkSlateGray", 0x2f4f4f }, { "DarkSlateGrey", 0x2f4f4f }, { "DarkTurquoise", 0x00ced1 }, { "DarkViolet", 0x9400d3 }, { "DeepPink", 0xff1493 }, { "DeepSkyBlue", 0x00bfff }, { "DimGray", 0x696969 }, { "DimGrey", 0x696969 }, { "DodgerBlue", 0x1e90ff }, { "Firebrick", 0xb22222 }, { "FloralWhite", 0xfffaf0 }, { "ForestGreen", 0x228b22 }, { "Fuchsia", 0xff00ff }, { "Gainsboro", 0xdcdcdc }, { "GhostWhite", 0xf8f8ff }, { "Gold", 0xffd700 }, { "Goldenrod", 0xdaa520 }, { "Gray", 0x808080 }, { "Green", 0x008000 }, { "GreenYellow", 0xadff2f }, { "Grey", 0x808080 }, { "Honeydew", 0xf0fff0 }, { "HotPink", 0xff69b4 }, { "IndianRed", 0xcd5c5c }, { "Indigo", 0x4b0082 }, { "Ivory", 0xfffff0 }, { "Khaki", 0xf0e68c }, { "Lavender", 0xe6e6fa }, { "LavenderBlush", 0xfff0f5 }, { "LawnGreen", 0x7cfc00 }, { "LemonChiffon", 0xfffacd }, { "LightBlue", 0xadd8e6 }, { "LightCoral", 0xf08080 }, { "LightCyan", 0xe0ffff }, { "LightGoldenrodYellow", 0xfafad2 }, { "LightGray", 0xd3d3d3 }, { "LightGreen", 0x90ee90 }, { "LightGrey", 0xd3d3d3 }, { "LightPink", 0xffb6c1 }, { "LightSalmon", 0xffa07a }, { "LightSeaGreen", 0x20b2aa }, { "LightSkyBlue", 0x87cefa }, { "LightSlateGray", 0x778899 }, { "LightSlateGrey", 0x778899 }, { "LightSteelBlue", 0xb0c4de }, { "LightYellow", 0xffffe0 }, { "Lime", 0x00ff00 }, { "LimeGreen", 0x32cd32 }, { "Linen", 0xfaf0e6 }, { "Magenta", 0xff00ff }, { "Maroon", 0x800000 }, { "MediumAquamarine", 0x66cdaa }, { "MediumBlue", 0x0000cd }, { "MediumOrchid", 0xba55d3 }, { "MediumPurple", 0x9370db }, { "MediumSeaGreen", 0x3cb371 }, { "MediumSlateBlue", 0x7b68ee }, { "MediumSpringGreen", 0x00fa9a }, { "MediumTurquoise", 0x48d1cc }, { "MediumVioletRed", 0xc71585 }, { "MidnightBlue", 0x191970 }, { "MintCream", 0xf5fffa }, { "MistyRose", 0xffe4e1 }, { "Moccasin", 0xffe4b5 }, { "NavajoWhite", 0xffdead }, { "Navy", 0x000080 }, { "None", -1 }, /* Non-standard addition */ { "Off", -1 }, /* Non-standard addition */ { "OldLace", 0xfdf5e6 }, { "Olive", 0x808000 }, { "OliveDrab", 0x6b8e23 }, { "Orange", 0xffa500 }, { "OrangeRed", 0xff4500 }, { "Orchid", 0xda70d6 }, { "PaleGoldenrod", 0xeee8aa }, { "PaleGreen", 0x98fb98 }, { "PaleTurquoise", 0xafeeee }, { "PaleVioletRed", 0xdb7093 }, { "PapayaWhip", 0xffefd5 }, { "PeachPuff", 0xffdab9 }, { "Peru", 0xcd853f }, { "Pink", 0xffc0cb }, { "Plum", 0xdda0dd }, { "PowderBlue", 0xb0e0e6 }, { "Purple", 0x800080 }, { "RebeccaPurple", 0x663399 }, { "Red", 0xff0000 }, { "RosyBrown", 0xbc8f8f }, { "RoyalBlue", 0x4169e1 }, { "SaddleBrown", 0x8b4513 }, { "Salmon", 0xfa8072 }, { "SandyBrown", 0xf4a460 }, { "SeaGreen", 0x2e8b57 }, { "Seashell", 0xfff5ee }, { "Sienna", 0xa0522d }, { "Silver", 0xc0c0c0 }, { "SkyBlue", 0x87ceeb }, { "SlateBlue", 0x6a5acd }, { "SlateGray", 0x708090 }, { "SlateGrey", 0x708090 }, { "Snow", 0xfffafa }, { "SpringGreen", 0x00ff7f }, { "SteelBlue", 0x4682b4 }, { "Tan", 0xd2b48c }, { "Teal", 0x008080 }, { "Thistle", 0xd8bfd8 }, { "Tomato", 0xff6347 }, { "Turquoise", 0x40e0d0 }, { "Violet", 0xee82ee }, { "Wheat", 0xf5deb3 }, { "White", 0xffffff }, { "WhiteSmoke", 0xf5f5f5 }, { "Yellow", 0xffff00 }, { "YellowGreen", 0x9acd32 }, }; /* Built-in variable names. ** ** This array is constant. When a script changes the value of one of ** these built-ins, a new PVar record is added at the head of ** the Pik.pVar list, which is searched first. Thus the new PVar entry ** will override this default value. ** ** Units are in inches, except for "color" and "fill" which are ** interpreted as 24-bit RGB values. ** ** Binary search used. Must be kept in sorted order. */ static const struct { const char *zName; PNum val; } aBuiltin[] = { { "arcrad", 0.25 }, { "arrowhead", 2.0 }, { "arrowht", 0.08 }, { "arrowwid", 0.06 }, { "boxht", 0.5 }, { "boxrad", 0.0 }, { "boxwid", 0.75 }, { "charht", 0.14 }, { "charwid", 0.08 }, { "circlerad", 0.25 }, { "color", 0.0 }, { "cylht", 0.5 }, { "cylrad", 0.075 }, { "cylwid", 0.75 }, { "dashwid", 0.05 }, { "diamondht", 0.75 }, { "diamondwid", 1.0 }, { "dotrad", 0.015 }, { "ellipseht", 0.5 }, { "ellipsewid", 0.75 }, { "fileht", 0.75 }, { "filerad", 0.15 }, { "filewid", 0.5 }, { "fill", -1.0 }, { "lineht", 0.5 }, { "linewid", 0.5 }, { "movewid", 0.5 }, { "ovalht", 0.5 }, { "ovalwid", 1.0 }, { "scale", 1.0 }, { "textht", 0.5 }, { "textwid", 0.75 }, { "thickness", 0.015 }, }; /* Methods for the "arc" class */ static void arcInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "arcrad",6,0); pObj->h = pObj->w; } /* Hack: Arcs are here rendered as quadratic Bezier curves rather ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters ** that control arcs are obscure and I could not figure out what they ** mean based on available documentation. (2) Arcs are rarely used, ** and so do not seem that important. */ static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){ PPoint m; PNum dx, dy; m.x = 0.5*(f.x+t.x); m.y = 0.5*(f.y+t.y); dx = t.x - f.x; dy = t.y - f.y; if( cw ){ m.x -= 0.5*rScale*dy; m.y += 0.5*rScale*dx; }else{ m.x += 0.5*rScale*dy; m.y -= 0.5*rScale*dx; } return m; } static void arcCheck(Pik *p, PObj *pObj){ PPoint m; if( p->nTPath>2 ){ pik_error(p, &pObj->errTok, "arc geometry error"); return; } m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5); pik_bbox_add_xy(&pObj->bbox, m.x, m.y); } static void arcRender(Pik *p, PObj *pObj){ PPoint f, m, t; if( pObj->nPath<2 ) return; if( pObj->sw<0.0 ) return; f = pObj->aPath[0]; t = pObj->aPath[1]; m = arcControlPoint(pObj->cw,f,t,1.0); if( pObj->larrow ){ pik_draw_arrowhead(p,&m,&f,pObj); } if( pObj->rarrow ){ pik_draw_arrowhead(p,&m,&t,pObj); } pik_append_xy(p,"<path d=\"M", f.x, f.y); pik_append_xy(p,"Q", m.x, m.y); pik_append_xy(p," ", t.x, t.y); pik_append(p,"\" ",2); pik_append_style(p,pObj,0); pik_append(p,"\" />\n", -1); pik_append_txt(p, pObj, 0); } /* Methods for the "arrow" class */ static void arrowInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "linewid",7,0); pObj->h = pik_value(p, "lineht",6,0); pObj->rad = pik_value(p, "linerad",7,0); pObj->rarrow = 1; } /* Methods for the "box" class */ static void boxInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "boxwid",6,0); pObj->h = pik_value(p, "boxht",5,0); pObj->rad = pik_value(p, "boxrad",6,0); } /* Return offset from the center of the box to the compass point ** given by parameter cp */ static PPoint boxOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PNum rx; if( rad<=0.0 ){ rx = 0.0; }else{ if( rad>w2 ) rad = w2; if( rad>h2 ) rad = h2; rx = 0.29289321881345252392*rad; } switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h2; break; case CP_NE: pt.x = w2-rx; pt.y = h2-rx; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w2-rx; pt.y = rx-h2; break; case CP_S: pt.x = 0.0; pt.y = -h2; break; case CP_SW: pt.x = rx-w2; pt.y = rx-h2; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = rx-w2; pt.y = h2-rx; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static PPoint boxChop(Pik *p, PObj *pObj, PPoint *pPt){ PNum dx, dy; int cp = CP_C; PPoint chop = pObj->ptAt; if( pObj->w<=0.0 ) return chop; if( pObj->h<=0.0 ) return chop; dx = (pPt->x - pObj->ptAt.x)*pObj->h/pObj->w; dy = (pPt->y - pObj->ptAt.y); if( dx>0.0 ){ if( dy>=2.414*dx ){ cp = CP_N; }else if( dy>=0.414*dx ){ cp = CP_NE; }else if( dy>=-0.414*dx ){ cp = CP_E; }else if( dy>-2.414*dx ){ cp = CP_SE; }else{ cp = CP_S; } }else{ if( dy>=-2.414*dx ){ cp = CP_N; }else if( dy>=-0.414*dx ){ cp = CP_NW; }else if( dy>=0.414*dx ){ cp = CP_W; }else if( dy>2.414*dx ){ cp = CP_SW; }else{ cp = CP_S; } } chop = pObj->type->xOffset(p,pObj,cp); chop.x += pObj->ptAt.x; chop.y += pObj->ptAt.y; return chop; } static void boxFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h; UNUSED_PARAMETER(p); } static void boxRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ if( rad<=0.0 ){ pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y-h2); pik_append_xy(p,"L", pt.x+w2,pt.y-h2); pik_append_xy(p,"L", pt.x+w2,pt.y+h2); pik_append_xy(p,"L", pt.x-w2,pt.y+h2); pik_append(p,"Z\" ",-1); }else{ /* ** ---- - y3 ** / \ ** / \ _ y2 ** | | ** | | _ y1 ** \ / ** \ / ** ---- _ y0 ** ** ' ' ' ' ** x0 x1 x2 x3 */ PNum x0,x1,x2,x3,y0,y1,y2,y3; if( rad>w2 ) rad = w2; if( rad>h2 ) rad = h2; x0 = pt.x - w2; x1 = x0 + rad; x3 = pt.x + w2; x2 = x3 - rad; y0 = pt.y - h2; y1 = y0 + rad; y3 = pt.y + h2; y2 = y3 - rad; pik_append_xy(p,"<path d=\"M", x1, y0); if( x2>x1 ) pik_append_xy(p, "L", x2, y0); pik_append_arc(p, rad, rad, x3, y1); if( y2>y1 ) pik_append_xy(p, "L", x3, y2); pik_append_arc(p, rad, rad, x2, y3); if( x2>x1 ) pik_append_xy(p, "L", x1, y3); pik_append_arc(p, rad, rad, x0, y2); if( y2>y1 ) pik_append_xy(p, "L", x0, y1); pik_append_arc(p, rad, rad, x1, y0); pik_append(p,"Z\" ",-1); } pik_append_style(p,pObj,3); pik_append(p,"\" />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "circle" class */ static void circleInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "circlerad",9,0)*2; pObj->h = pObj->w; pObj->rad = 0.5*pObj->w; } static void circleNumProp(Pik *p, PObj *pObj, PToken *pId){ /* For a circle, the width must equal the height and both must ** be twice the radius. Enforce those constraints. */ switch( pId->eType ){ case T_DIAMETER: case T_RADIUS: pObj->w = pObj->h = 2.0*pObj->rad; break; case T_WIDTH: pObj->h = pObj->w; pObj->rad = 0.5*pObj->w; break; case T_HEIGHT: pObj->w = pObj->h; pObj->rad = 0.5*pObj->w; break; } UNUSED_PARAMETER(p); } static PPoint circleChop(Pik *p, PObj *pObj, PPoint *pPt){ PPoint chop; PNum dx = pPt->x - pObj->ptAt.x; PNum dy = pPt->y - pObj->ptAt.y; PNum dist = hypot(dx,dy); if( dist<pObj->rad || dist<=0 ) return pObj->ptAt; chop.x = pObj->ptAt.x + dx*pObj->rad/dist; chop.y = pObj->ptAt.y + dy*pObj->rad/dist; UNUSED_PARAMETER(p); return chop; } static void circleFit(Pik *p, PObj *pObj, PNum w, PNum h){ PNum mx = 0.0; if( w>0 ) mx = w; if( h>mx ) mx = h; if( w*h>0 && (w*w + h*h) > mx*mx ){ mx = hypot(w,h); } if( mx>0.0 ){ pObj->rad = 0.5*mx; pObj->w = pObj->h = mx; } UNUSED_PARAMETER(p); } static void circleRender(Pik *p, PObj *pObj){ PNum r = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_x(p,"<circle cx=\"", pt.x, "\""); pik_append_y(p," cy=\"", pt.y, "\""); pik_append_dis(p," r=\"", r, "\" "); pik_append_style(p,pObj,3); pik_append(p,"\" />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "cylinder" class */ static void cylinderInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "cylwid",6,0); pObj->h = pik_value(p, "cylht",5,0); pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */ } static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h + 0.25*pObj->rad + pObj->sw; UNUSED_PARAMETER(p); } static void cylinderRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ if( rad>h2 ){ rad = h2; }else if( rad<0 ){ rad = 0; } pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y+h2-rad); pik_append_xy(p,"L", pt.x-w2,pt.y-h2+rad); pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad); pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad); pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad); pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad); pik_append(p,"\" ",-1); pik_append_style(p,pObj,3); pik_append(p,"\" />\n", -1); } pik_append_txt(p, pObj, 0); } static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = pObj->w*0.5; PNum h1 = pObj->h*0.5; PNum h2 = h1 - pObj->rad; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h1; break; case CP_NE: pt.x = w2; pt.y = h2; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w2; pt.y = -h2; break; case CP_S: pt.x = 0.0; pt.y = -h1; break; case CP_SW: pt.x = -w2; pt.y = -h2; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = -w2; pt.y = h2; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } /* Methods for the "dot" class */ static void dotInit(Pik *p, PObj *pObj){ pObj->rad = pik_value(p, "dotrad",6,0); pObj->h = pObj->w = pObj->rad*6; pObj->fill = pObj->color; } static void dotNumProp(Pik *p, PObj *pObj, PToken *pId){ switch( pId->eType ){ case T_COLOR: pObj->fill = pObj->color; break; case T_FILL: pObj->color = pObj->fill; break; } UNUSED_PARAMETER(p); } static void dotCheck(Pik *p, PObj *pObj){ pObj->w = pObj->h = 0; pik_bbox_addellipse(&pObj->bbox, pObj->ptAt.x, pObj->ptAt.y, pObj->rad, pObj->rad); UNUSED_PARAMETER(p); } static PPoint dotOffset(Pik *p, PObj *pObj, int cp){ UNUSED_PARAMETER(p); UNUSED_PARAMETER(pObj); UNUSED_PARAMETER(cp); return cZeroPoint; } static void dotRender(Pik *p, PObj *pObj){ PNum r = pObj->rad; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_x(p,"<circle cx=\"", pt.x, "\""); pik_append_y(p," cy=\"", pt.y, "\""); pik_append_dis(p," r=\"", r, "\""); pik_append_style(p,pObj,2); pik_append(p,"\" />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "diamond" class */ static void diamondInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "diamondwid",10,0); pObj->h = pik_value(p, "diamondht",9,0); pObj->bAltAutoFit = 1; } /* Return offset from the center of the box to the compass point ** given by parameter cp */ static PPoint diamondOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = 0.5*pObj->w; PNum w4 = 0.25*pObj->w; PNum h2 = 0.5*pObj->h; PNum h4 = 0.25*pObj->h; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h2; break; case CP_NE: pt.x = w4; pt.y = h4; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w4; pt.y = -h4; break; case CP_S: pt.x = 0.0; pt.y = -h2; break; case CP_SW: pt.x = -w4; pt.y = -h4; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = -w4; pt.y = h4; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static void diamondFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( pObj->w<=0 ) pObj->w = w*1.5; if( pObj->h<=0 ) pObj->h = h*1.5; if( pObj->w>0 && pObj->h>0 ){ PNum x = pObj->w*h/pObj->h + w; PNum y = pObj->h*x/pObj->w; pObj->w = x; pObj->h = y; } UNUSED_PARAMETER(p); } static void diamondRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y); pik_append_xy(p,"L", pt.x,pt.y-h2); pik_append_xy(p,"L", pt.x+w2,pt.y); pik_append_xy(p,"L", pt.x,pt.y+h2); pik_append(p,"Z\" ",-1); pik_append_style(p,pObj,3); pik_append(p,"\" />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "ellipse" class */ static void ellipseInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "ellipsewid",10,0); pObj->h = pik_value(p, "ellipseht",9,0); } static PPoint ellipseChop(Pik *p, PObj *pObj, PPoint *pPt){ PPoint chop; PNum s, dq, dist; PNum dx = pPt->x - pObj->ptAt.x; PNum dy = pPt->y - pObj->ptAt.y; if( pObj->w<=0.0 ) return pObj->ptAt; if( pObj->h<=0.0 ) return pObj->ptAt; s = pObj->h/pObj->w; dq = dx*s; dist = hypot(dq,dy); if( dist<pObj->h ) return pObj->ptAt; chop.x = pObj->ptAt.x + 0.5*dq*pObj->h/(dist*s); chop.y = pObj->ptAt.y + 0.5*dy*pObj->h/dist; UNUSED_PARAMETER(p); return chop; } static PPoint ellipseOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w = pObj->w*0.5; PNum w2 = w*0.70710678118654747608; PNum h = pObj->h*0.5; PNum h2 = h*0.70710678118654747608; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h; break; case CP_NE: pt.x = w2; pt.y = h2; break; case CP_E: pt.x = w; pt.y = 0.0; break; case CP_SE: pt.x = w2; pt.y = -h2; break; case CP_S: pt.x = 0.0; pt.y = -h; break; case CP_SW: pt.x = -w2; pt.y = -h2; break; case CP_W: pt.x = -w; pt.y = 0.0; break; case CP_NW: pt.x = -w2; pt.y = h2; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static void ellipseRender(Pik *p, PObj *pObj){ PNum w = pObj->w; PNum h = pObj->h; PPoint pt = pObj->ptAt; if( pObj->sw>=0.0 ){ pik_append_x(p,"<ellipse cx=\"", pt.x, "\""); pik_append_y(p," cy=\"", pt.y, "\""); pik_append_dis(p," rx=\"", w/2.0, "\""); pik_append_dis(p," ry=\"", h/2.0, "\" "); pik_append_style(p,pObj,3); pik_append(p,"\" />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "file" object */ static void fileInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "filewid",7,0); pObj->h = pik_value(p, "fileht",6,0); pObj->rad = pik_value(p, "filerad",7,0); } /* Return offset from the center of the file to the compass point ** given by parameter cp */ static PPoint fileOffset(Pik *p, PObj *pObj, int cp){ PPoint pt = cZeroPoint; PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rx = pObj->rad; PNum mn = w2<h2 ? w2 : h2; if( rx>mn ) rx = mn; if( rx<mn*0.25 ) rx = mn*0.25; pt.x = pt.y = 0.0; rx *= 0.5; switch( cp ){ case CP_C: break; case CP_N: pt.x = 0.0; pt.y = h2; break; case CP_NE: pt.x = w2-rx; pt.y = h2-rx; break; case CP_E: pt.x = w2; pt.y = 0.0; break; case CP_SE: pt.x = w2; pt.y = -h2; break; case CP_S: pt.x = 0.0; pt.y = -h2; break; case CP_SW: pt.x = -w2; pt.y = -h2; break; case CP_W: pt.x = -w2; pt.y = 0.0; break; case CP_NW: pt.x = -w2; pt.y = h2; break; default: assert(0); } UNUSED_PARAMETER(p); return pt; } static void fileFit(Pik *p, PObj *pObj, PNum w, PNum h){ if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h + 2*pObj->rad; UNUSED_PARAMETER(p); } static void fileRender(Pik *p, PObj *pObj){ PNum w2 = 0.5*pObj->w; PNum h2 = 0.5*pObj->h; PNum rad = pObj->rad; PPoint pt = pObj->ptAt; PNum mn = w2<h2 ? w2 : h2; if( rad>mn ) rad = mn; if( rad<mn*0.25 ) rad = mn*0.25; if( pObj->sw>=0.0 ){ pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y-h2); pik_append_xy(p,"L", pt.x+w2,pt.y-h2); pik_append_xy(p,"L", pt.x+w2,pt.y+(h2-rad)); pik_append_xy(p,"L", pt.x+(w2-rad),pt.y+h2); pik_append_xy(p,"L", pt.x-w2,pt.y+h2); pik_append(p,"Z\" ",-1); pik_append_style(p,pObj,1); pik_append(p,"\" />\n",-1); pik_append_xy(p,"<path d=\"M", pt.x+(w2-rad), pt.y+h2); pik_append_xy(p,"L", pt.x+(w2-rad),pt.y+(h2-rad)); pik_append_xy(p,"L", pt.x+w2, pt.y+(h2-rad)); pik_append(p,"\" ",-1); pik_append_style(p,pObj,0); pik_append(p,"\" />\n",-1); } pik_append_txt(p, pObj, 0); } /* Methods for the "line" class */ static void lineInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "linewid",7,0); pObj->h = pik_value(p, "lineht",6,0); pObj->rad = pik_value(p, "linerad",7,0); } static PPoint lineOffset(Pik *p, PObj *pObj, int cp){ #if 0 /* In legacy PIC, the .center of an unclosed line is half way between ** its .start and .end. */ if( cp==CP_C && !pObj->bClose ){ PPoint out; out.x = 0.5*(pObj->ptEnter.x + pObj->ptExit.x) - pObj->ptAt.x; out.y = 0.5*(pObj->ptEnter.x + pObj->ptExit.y) - pObj->ptAt.y; return out; } #endif return boxOffset(p,pObj,cp); } static void lineRender(Pik *p, PObj *pObj){ int i; if( pObj->sw>0.0 ){ const char *z = "<path d=\"M"; int n = pObj->nPath; if( pObj->larrow ){ pik_draw_arrowhead(p,&pObj->aPath[1],&pObj->aPath[0],pObj); } if( pObj->rarrow ){ pik_draw_arrowhead(p,&pObj->aPath[n-2],&pObj->aPath[n-1],pObj); } for(i=0; i<pObj->nPath; i++){ pik_append_xy(p,z,pObj->aPath[i].x,pObj->aPath[i].y); z = "L"; } if( pObj->bClose ){ pik_append(p,"Z",1); }else{ pObj->fill = -1.0; } pik_append(p,"\" ",-1); pik_append_style(p,pObj,pObj->bClose?3:0); pik_append(p,"\" />\n", -1); } pik_append_txt(p, pObj, 0); } /* Methods for the "move" class */ static void moveInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "movewid",7,0); pObj->h = pObj->w; pObj->fill = -1.0; pObj->color = -1.0; pObj->sw = -1.0; } static void moveRender(Pik *p, PObj *pObj){ /* No-op */ UNUSED_PARAMETER(p); UNUSED_PARAMETER(pObj); } /* Methods for the "oval" class */ static void ovalInit(Pik *p, PObj *pObj){ pObj->h = pik_value(p, "ovalht",6,0); pObj->w = pik_value(p, "ovalwid",7,0); pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w); } static void ovalNumProp(Pik *p, PObj *pObj, PToken *pId){ UNUSED_PARAMETER(p); UNUSED_PARAMETER(pId); /* Always adjust the radius to be half of the smaller of ** the width and height. */ pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w); } static void ovalFit(Pik *p, PObj *pObj, PNum w, PNum h){ UNUSED_PARAMETER(p); if( w>0 ) pObj->w = w; if( h>0 ) pObj->h = h; if( pObj->w<pObj->h ) pObj->w = pObj->h; pObj->rad = 0.5*(pObj->h<pObj->w?pObj->h:pObj->w); } /* Methods for the "spline" class */ static void splineInit(Pik *p, PObj *pObj){ pObj->w = pik_value(p, "linewid",7,0); pObj->h = pik_value(p, "lineht",6,0); pObj->rad = 1000; } /* Return a point along the path from "f" to "t" that is r units ** prior to reaching "t", except if the path is less than 2*r total, ** return the midpoint. */ static PPoint radiusMidpoint(PPoint f, PPoint t, PNum r, int *pbMid){ PNum dx = t.x - f.x; PNum dy = t.y - f.y; PNum dist = hypot(dx,dy); PPoint m; if( dist<=0.0 ) return t; dx /= dist; dy /= dist; if( r > 0.5*dist ){ r = 0.5*dist; *pbMid = 1; }else{ *pbMid = 0; } m.x = t.x - r*dx; m.y = t.y - r*dy; return m; } static void radiusPath(Pik *p, PObj *pObj, PNum r){ int i; int n = pObj->nPath; const PPoint *a = pObj->aPath; PPoint m; PPoint an = a[n-1]; int isMid = 0; int iLast = pObj->bClose ? n : n-1; pik_append_xy(p,"<path d=\"M", a[0].x, a[0].y); m = radiusMidpoint(a[0], a[1], r, &isMid); pik_append_xy(p," L ",m.x,m.y); for(i=1; i<iLast; i++){ an = i<n-1 ? a[i+1] : a[0]; m = radiusMidpoint(an,a[i],r, &isMid); pik_append_xy(p," Q ",a[i].x,a[i].y); pik_append_xy(p," ",m.x,m.y); if( !isMid ){ m = radiusMidpoint(a[i],an,r, &isMid); pik_append_xy(p," L ",m.x,m.y); } } pik_append_xy(p," L ",an.x,an.y); if( pObj->bClose ){ pik_append(p,"Z",1); }else{ pObj->fill = -1.0; } pik_append(p,"\" ",-1); pik_append_style(p,pObj,pObj->bClose?3:0); pik_append(p,"\" />\n", -1); } static void splineRender(Pik *p, PObj *pObj){ if( pObj->sw>0.0 ){ int n = pObj->nPath; PNum r = pObj->rad; if( n<3 || r<=0.0 ){ lineRender(p,pObj); return; } if( pObj->larrow ){ pik_draw_arrowhead(p,&pObj->aPath[1],&pObj->aPath[0],pObj); } if( pObj->rarrow ){ pik_draw_arrowhead(p,&pObj->aPath[n-2],&pObj->aPath[n-1],pObj); } radiusPath(p,pObj,pObj->rad); } pik_append_txt(p, pObj, 0); } /* Methods for the "text" class */ static void textInit(Pik *p, PObj *pObj){ pik_value(p, "textwid",7,0); pik_value(p, "textht",6,0); pObj->sw = 0.0; } static PPoint textOffset(Pik *p, PObj *pObj, int cp){ /* Automatically slim-down the width and height of text ** statements so that the bounding box tightly encloses the text, ** then get boxOffset() to do the offset computation. */ pik_size_to_fit(p, &pObj->errTok,3); return boxOffset(p, pObj, cp); } static void textRender(Pik *p, PObj *pObj){ pik_append_txt(p, pObj, 0); } /* Methods for the "sublist" class */ static void sublistInit(Pik *p, PObj *pObj){ PList *pList = pObj->pSublist; int i; UNUSED_PARAMETER(p); pik_bbox_init(&pObj->bbox); for(i=0; i<pList->n; i++){ pik_bbox_addbox(&pObj->bbox, &pList->a[i]->bbox); } pObj->w = pObj->bbox.ne.x - pObj->bbox.sw.x; pObj->h = pObj->bbox.ne.y - pObj->bbox.sw.y; pObj->ptAt.x = 0.5*(pObj->bbox.ne.x + pObj->bbox.sw.x); pObj->ptAt.y = 0.5*(pObj->bbox.ne.y + pObj->bbox.sw.y); pObj->mCalc |= A_WIDTH|A_HEIGHT|A_RADIUS; } /* ** The following array holds all the different kinds of objects. ** The special [] object is separate. */ static const PClass aClass[] = { { /* name */ "arc", /* isline */ 1, /* eJust */ 0, /* xInit */ arcInit, /* xNumProp */ 0, /* xCheck */ arcCheck, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ arcRender }, { /* name */ "arrow", /* isline */ 1, /* eJust */ 0, /* xInit */ arrowInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ lineOffset, /* xFit */ 0, /* xRender */ splineRender }, { /* name */ "box", /* isline */ 0, /* eJust */ 1, /* xInit */ boxInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ boxOffset, /* xFit */ boxFit, /* xRender */ boxRender }, { /* name */ "circle", /* isline */ 0, /* eJust */ 0, /* xInit */ circleInit, /* xNumProp */ circleNumProp, /* xCheck */ 0, /* xChop */ circleChop, /* xOffset */ ellipseOffset, /* xFit */ circleFit, /* xRender */ circleRender }, { /* name */ "cylinder", /* isline */ 0, /* eJust */ 1, /* xInit */ cylinderInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ cylinderOffset, /* xFit */ cylinderFit, /* xRender */ cylinderRender }, { /* name */ "diamond", /* isline */ 0, /* eJust */ 0, /* xInit */ diamondInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ diamondOffset, /* xFit */ diamondFit, /* xRender */ diamondRender }, { /* name */ "dot", /* isline */ 0, /* eJust */ 0, /* xInit */ dotInit, /* xNumProp */ dotNumProp, /* xCheck */ dotCheck, /* xChop */ circleChop, /* xOffset */ dotOffset, /* xFit */ 0, /* xRender */ dotRender }, { /* name */ "ellipse", /* isline */ 0, /* eJust */ 0, /* xInit */ ellipseInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ ellipseChop, /* xOffset */ ellipseOffset, /* xFit */ boxFit, /* xRender */ ellipseRender }, { /* name */ "file", /* isline */ 0, /* eJust */ 1, /* xInit */ fileInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ fileOffset, /* xFit */ fileFit, /* xRender */ fileRender }, { /* name */ "line", /* isline */ 1, /* eJust */ 0, /* xInit */ lineInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ lineOffset, /* xFit */ 0, /* xRender */ splineRender }, { /* name */ "move", /* isline */ 1, /* eJust */ 0, /* xInit */ moveInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ moveRender }, { /* name */ "oval", /* isline */ 0, /* eJust */ 1, /* xInit */ ovalInit, /* xNumProp */ ovalNumProp, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ boxOffset, /* xFit */ ovalFit, /* xRender */ boxRender }, { /* name */ "spline", /* isline */ 1, /* eJust */ 0, /* xInit */ splineInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ lineOffset, /* xFit */ 0, /* xRender */ splineRender }, { /* name */ "text", /* isline */ 0, /* eJust */ 0, /* xInit */ textInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ boxChop, /* xOffset */ textOffset, /* xFit */ boxFit, /* xRender */ textRender }, }; static const PClass sublistClass = { /* name */ "[]", /* isline */ 0, /* eJust */ 0, /* xInit */ sublistInit, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ 0 }; static const PClass noopClass = { /* name */ "noop", /* isline */ 0, /* eJust */ 0, /* xInit */ 0, /* xNumProp */ 0, /* xCheck */ 0, /* xChop */ 0, /* xOffset */ boxOffset, /* xFit */ 0, /* xRender */ 0 }; /* ** Reduce the length of the line segment by amt (if possible) by ** modifying the location of *t. */ static void pik_chop(PPoint *f, PPoint *t, PNum amt){ PNum dx = t->x - f->x; PNum dy = t->y - f->y; PNum dist = hypot(dx,dy); PNum r; if( dist<=amt ){ *t = *f; return; } r = 1.0 - amt/dist; t->x = f->x + r*dx; t->y = f->y + r*dy; } /* ** Draw an arrowhead on the end of the line segment from pFrom to pTo. ** Also, shorten the line segment (by changing the value of pTo) so that ** the shaft of the arrow does not extend into the arrowhead. */ static void pik_draw_arrowhead(Pik *p, PPoint *f, PPoint *t, PObj *pObj){ PNum dx = t->x - f->x; PNum dy = t->y - f->y; PNum dist = hypot(dx,dy); PNum h = p->hArrow * pObj->sw; PNum w = p->wArrow * pObj->sw; PNum e1, ddx, ddy; PNum bx, by; if( pObj->color<0.0 ) return; if( pObj->sw<=0.0 ) return; if( dist<=0.0 ) return; /* Unable */ dx /= dist; dy /= dist; e1 = dist - h; if( e1<0.0 ){ e1 = 0.0; h = dist; } ddx = -w*dy; ddy = w*dx; bx = f->x + e1*dx; by = f->y + e1*dy; pik_append_xy(p,"<polygon points=\"", t->x, t->y); pik_append_xy(p," ",bx-ddx, by-ddy); pik_append_xy(p," ",bx+ddx, by+ddy); pik_append_clr(p,"\" style=\"fill:",pObj->color,"\"/>\n",0); pik_chop(f,t,h/2); } /* ** Compute the relative offset to an edge location from the reference for a ** an statement. */ static PPoint pik_elem_offset(Pik *p, PObj *pObj, int cp){ return pObj->type->xOffset(p, pObj, cp); } /* ** Append raw text to zOut */ static void pik_append(Pik *p, const char *zText, int n){ if( n<0 ) n = (int)strlen(zText); if( p->nOut+n>=p->nOutAlloc ){ int nNew = (p->nOut+n)*2 + 1; char *z = realloc(p->zOut, nNew); if( z==0 ){ pik_error(p, 0, 0); return; } p->zOut = z; p->nOutAlloc = nNew; } memcpy(p->zOut+p->nOut, zText, n); p->nOut += n; p->zOut[p->nOut] = 0; } /* ** Given a string and its length, returns true if the string begins ** with a construct which syntactically matches an HTML entity escape ** sequence (without checking for whether it's a known entity). Always ** returns false if zText[0] is false or n<4. Entities match the ** equivalent of the regexes `&#[0-9]{2,};` and ** `&[a-zA-Z][a-zA-Z0-9]+;`. */ static int pik_isentity(char const * zText, int n){ int i = 0; if( n<4 || '&'!=zText[0] ) return 0; n--; zText++; if( '#'==zText[0] ){ zText++; n--; for(i=0; i<n; i++){ if( i>1 && ';'==zText[i] ) return 1; else if( zText[i]<'0' || zText[i]>'9' ) return 0; /* Note that &#nn; values nn<32d are not legal entities. */ } }else{ for(i=0; i<n; i++){ if( i>1 && ';'==zText[i] ) return 1; else if( i>0 && zText[i]>='0' && zText[i]<='9' ){ continue; }else if( zText[i]<'A' || zText[i]>'z' || (zText[i]>'Z' && zText[i]<'a') ) return 0; } } return 0; } /* ** Append text to zOut with HTML characters escaped. ** ** * The space character is changed into non-breaking space (U+00a0) ** if mFlags has the 0x01 bit set. This is needed when outputting ** text to preserve leading and trailing whitespace. Turns out we ** cannot use as that is an HTML-ism and is not valid in XML. ** ** * The "&" character is changed into "&" if mFlags has the ** 0x02 bit set. This is needed when generating error message text. ** ** * Except for the above, only "<" and ">" are escaped. */ static void pik_append_text(Pik *p, const char *zText, int n, int mFlags){ int i; char c = 0; int bQSpace = mFlags & 1; int bQAmp = mFlags & 2; if( n<0 ) n = (int)strlen(zText); while( n>0 ){ for(i=0; i<n; i++){ c = zText[i]; if( c=='<' || c=='>' ) break; if( c==' ' && bQSpace ) break; if( c=='&' && bQAmp ) break; } if( i ) pik_append(p, zText, i); if( i==n ) break; switch( c ){ case '<': { pik_append(p, "<", 4); break; } case '>': { pik_append(p, ">", 4); break; } case ' ': { pik_append(p, "\302\240;", 2); break; } case '&': if( pik_isentity(zText+i, n-i) ){ pik_append(p, "&", 1); } else { pik_append(p, "&", 5); } } i++; n -= i; zText += i; i = 0; } } /* ** Append error message text. This is either a raw append, or an append ** with HTML escapes, depending on whether the PIKCHR_PLAINTEXT_ERRORS flag ** is set. */ static void pik_append_errtxt(Pik *p, const char *zText, int n){ if( p->mFlags & PIKCHR_PLAINTEXT_ERRORS ){ pik_append(p, zText, n); }else{ pik_append_text(p, zText, n, 0); } } /* Append a PNum value */ static void pik_append_num(Pik *p, const char *z,PNum v){ char buf[100]; snprintf(buf, sizeof(buf)-1, "%.10g", (double)v); buf[sizeof(buf)-1] = 0; pik_append(p, z, -1); pik_append(p, buf, -1); } /* Append a PPoint value (Used for debugging only) */ static void pik_append_point(Pik *p, const char *z, PPoint *pPt){ char buf[100]; snprintf(buf, sizeof(buf)-1, "%.10g,%.10g", (double)pPt->x, (double)pPt->y); buf[sizeof(buf)-1] = 0; pik_append(p, z, -1); pik_append(p, buf, -1); } /* ** Invert the RGB color so that it is appropriate for dark mode. ** Variable x hold the initial color. The color is intended for use ** as a background color if isBg is true, and as a foreground color ** if isBg is false. */ static int pik_color_to_dark_mode(int x, int isBg){ int r, g, b; int mn, mx; x = 0xffffff - x; r = (x>>16) & 0xff; g = (x>>8) & 0xff; b = x & 0xff; mx = r; if( g>mx ) mx = g; if( b>mx ) mx = b; mn = r; if( g<mn ) mn = g; if( b<mn ) mn = b; r = mn + (mx-r); g = mn + (mx-g); b = mn + (mx-b); if( isBg ){ if( mx>127 ){ r = (127*r)/mx; g = (127*g)/mx; b = (127*b)/mx; } }else{ if( mn<128 && mx>mn ){ r = 127 + ((r-mn)*128)/(mx-mn); g = 127 + ((g-mn)*128)/(mx-mn); b = 127 + ((b-mn)*128)/(mx-mn); } } return r*0x10000 + g*0x100 + b; } /* Append a PNum value surrounded by text. Do coordinate transformations ** on the value. */ static void pik_append_x(Pik *p, const char *z1, PNum v, const char *z2){ char buf[200]; v -= p->bbox.sw.x; snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } static void pik_append_y(Pik *p, const char *z1, PNum v, const char *z2){ char buf[200]; v = p->bbox.ne.y - v; snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } static void pik_append_xy(Pik *p, const char *z1, PNum x, PNum y){ char buf[200]; x = x - p->bbox.sw.x; y = p->bbox.ne.y - y; snprintf(buf, sizeof(buf)-1, "%s%g,%g", z1, p->rScale*x, p->rScale*y); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } static void pik_append_dis(Pik *p, const char *z1, PNum v, const char *z2){ char buf[200]; snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } /* Append a color specification to the output. ** ** In PIKCHR_DARK_MODE, the color is inverted. The "bg" flags indicates that ** the color is intended for use as a background color if true, or as a ** foreground color if false. The distinction only matters for color ** inversions in PIKCHR_DARK_MODE. */ static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){ char buf[200]; int x = pik_round(v); int r, g, b; if( x==0 && p->fgcolor>0 && !bg ){ x = p->fgcolor; }else if( bg && x>=0xffffff && p->bgcolor>0 ){ x = p->bgcolor; }else if( p->mFlags & PIKCHR_DARK_MODE ){ x = pik_color_to_dark_mode(x,bg); } r = (x>>16) & 0xff; g = (x>>8) & 0xff; b = x & 0xff; snprintf(buf, sizeof(buf)-1, "%srgb(%d,%d,%d)%s", z1, r, g, b, z2); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } /* Append an SVG path A record: ** ** A r1 r2 0 0 0 x y */ static void pik_append_arc(Pik *p, PNum r1, PNum r2, PNum x, PNum y){ char buf[200]; x = x - p->bbox.sw.x; y = p->bbox.ne.y - y; snprintf(buf, sizeof(buf)-1, "A%g %g 0 0 0 %g %g", p->rScale*r1, p->rScale*r2, p->rScale*x, p->rScale*y); buf[sizeof(buf)-1] = 0; pik_append(p, buf, -1); } /* Append a style="..." text. But, leave the quote unterminated, in case ** the caller wants to add some more. ** ** eFill is non-zero to fill in the background, or 0 if no fill should ** occur. Non-zero values of eFill determine the "bg" flag to pik_append_clr() ** for cases when pObj->fill==pObj->color ** ** 1 fill is background, and color is foreground. ** 2 fill and color are both foreground. (Used by "dot" objects) ** 3 fill and color are both background. (Used by most other objs) */ static void pik_append_style(Pik *p, PObj *pObj, int eFill){ int clrIsBg = 0; pik_append(p, " style=\"", -1); if( pObj->fill>=0 && eFill ){ int fillIsBg = 1; if( pObj->fill==pObj->color ){ if( eFill==2 ) fillIsBg = 0; if( eFill==3 ) clrIsBg = 1; } pik_append_clr(p, "fill:", pObj->fill, ";", fillIsBg); }else{ pik_append(p,"fill:none;",-1); } if( pObj->sw>=0.0 && pObj->color>=0.0 ){ PNum sw = pObj->sw; pik_append_dis(p, "stroke-width:", sw, ";"); if( pObj->nPath>2 && pObj->rad<=pObj->sw ){ pik_append(p, "stroke-linejoin:round;", -1); } pik_append_clr(p, "stroke:",pObj->color,";",clrIsBg); if( pObj->dotted>0.0 ){ PNum v = pObj->dotted; if( sw<2.1/p->rScale ) sw = 2.1/p->rScale; pik_append_dis(p,"stroke-dasharray:",sw,""); pik_append_dis(p,",",v,";"); }else if( pObj->dashed>0.0 ){ PNum v = pObj->dashed; pik_append_dis(p,"stroke-dasharray:",v,""); pik_append_dis(p,",",v,";"); } } } /* ** Compute the vertical locations for all text items in the ** object pObj. In other words, set every pObj->aTxt[*].eCode ** value to contain exactly one of: TP_ABOVE2, TP_ABOVE, TP_CENTER, ** TP_BELOW, or TP_BELOW2 is set. */ static void pik_txt_vertical_layout(PObj *pObj){ int n, i; PToken *aTxt; n = pObj->nTxt; if( n==0 ) return; aTxt = pObj->aTxt; if( n==1 ){ if( (aTxt[0].eCode & TP_VMASK)==0 ){ aTxt[0].eCode |= TP_CENTER; } }else{ int allSlots = 0; int aFree[5]; int iSlot; int j, mJust; /* If there is more than one TP_ABOVE, change the first to TP_ABOVE2. */ for(j=mJust=0, i=n-1; i>=0; i--){ if( aTxt[i].eCode & TP_ABOVE ){ if( j==0 ){ j++; mJust = aTxt[i].eCode & TP_JMASK; }else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){ j++; }else{ aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_ABOVE2; break; } } } /* If there is more than one TP_BELOW, change the last to TP_BELOW2 */ for(j=mJust=0, i=0; i<n; i++){ if( aTxt[i].eCode & TP_BELOW ){ if( j==0 ){ j++; mJust = aTxt[i].eCode & TP_JMASK; }else if( j==1 && mJust!=0 && (aTxt[i].eCode & mJust)==0 ){ j++; }else{ aTxt[i].eCode = (aTxt[i].eCode & ~TP_VMASK) | TP_BELOW2; break; } } } /* Compute a mask of all slots used */ for(i=0; i<n; i++) allSlots |= aTxt[i].eCode & TP_VMASK; /* Set of an array of available slots */ if( n==2 && ((aTxt[0].eCode|aTxt[1].eCode)&TP_JMASK)==(TP_LJUST|TP_RJUST) ){ /* Special case of two texts that have opposite justification: ** Allow them both to float to center. */ iSlot = 2; aFree[0] = aFree[1] = TP_CENTER; }else{ /* Set up the arrow so that available slots are filled from top to ** bottom */ iSlot = 0; if( n>=4 && (allSlots & TP_ABOVE2)==0 ) aFree[iSlot++] = TP_ABOVE2; if( (allSlots & TP_ABOVE)==0 ) aFree[iSlot++] = TP_ABOVE; if( (n&1)!=0 ) aFree[iSlot++] = TP_CENTER; if( (allSlots & TP_BELOW)==0 ) aFree[iSlot++] = TP_BELOW; if( n>=4 && (allSlots & TP_BELOW2)==0 ) aFree[iSlot++] = TP_BELOW2; } /* Set the VMASK for all unassigned texts */ for(i=iSlot=0; i<n; i++){ if( (aTxt[i].eCode & TP_VMASK)==0 ){ aTxt[i].eCode |= aFree[iSlot++]; } } } } /* Return the font scaling factor associated with the input text attribute. */ static PNum pik_font_scale(PToken *t){ PNum scale = 1.0; if( t->eCode & TP_BIG ) scale *= 1.25; if( t->eCode & TP_SMALL ) scale *= 0.8; if( t->eCode & TP_XTRA ) scale *= scale; return scale; } /* Append multiple <text> SVG elements for the text fields of the PObj. ** Parameters: ** ** p The Pik object into which we are rendering ** ** pObj Object containing the text to be rendered ** ** pBox If not NULL, do no rendering at all. Instead ** expand the box object so that it will include all ** of the text. */ static void pik_append_txt(Pik *p, PObj *pObj, PBox *pBox){ PNum jw; /* Justification margin relative to center */ PNum ha2 = 0.0; /* Height of the top row of text */ PNum ha1 = 0.0; /* Height of the second "above" row */ PNum hc = 0.0; /* Height of the center row */ PNum hb1 = 0.0; /* Height of the first "below" row of text */ PNum hb2 = 0.0; /* Height of the second "below" row */ PNum yBase = 0.0; PNum sw = pObj->sw>=0.0 ? pObj->sw : 0; int n, i, nz; PNum x, y, orig_y, s; const char *z; PToken *aTxt; unsigned allMask = 0; if( p->nErr ) return; if( pObj->nTxt==0 ) return; aTxt = pObj->aTxt; n = pObj->nTxt; pik_txt_vertical_layout(pObj); x = pObj->ptAt.x; for(i=0; i<n; i++) allMask |= pObj->aTxt[i].eCode; if( pObj->type->isLine ){ hc = sw*1.5; }else if( pObj->rad>0.0 && pObj->type->xInit==cylinderInit ){ yBase = -0.75*pObj->rad; } if( allMask & TP_CENTER ){ for(i=0; i<n; i++){ if( pObj->aTxt[i].eCode & TP_CENTER ){ s = pik_font_scale(pObj->aTxt+i); if( hc<s*p->charHeight ) hc = s*p->charHeight; } } } if( allMask & TP_ABOVE ){ for(i=0; i<n; i++){ if( pObj->aTxt[i].eCode & TP_ABOVE ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( ha1<s ) ha1 = s; } } if( allMask & TP_ABOVE2 ){ for(i=0; i<n; i++){ if( pObj->aTxt[i].eCode & TP_ABOVE2 ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( ha2<s ) ha2 = s; } } } } if( allMask & TP_BELOW ){ for(i=0; i<n; i++){ if( pObj->aTxt[i].eCode & TP_BELOW ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( hb1<s ) hb1 = s; } } if( allMask & TP_BELOW2 ){ for(i=0; i<n; i++){ if( pObj->aTxt[i].eCode & TP_BELOW2 ){ s = pik_font_scale(pObj->aTxt+i)*p->charHeight; if( hb2<s ) hb2 = s; } } } } if( pObj->type->eJust==1 ){ jw = 0.5*(pObj->w - 0.5*(p->charWidth + sw)); }else{ jw = 0.0; } for(i=0; i<n; i++){ PToken *t = &aTxt[i]; PNum xtraFontScale = pik_font_scale(t); PNum nx = 0; orig_y = pObj->ptAt.y; y = yBase; if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2; if( t->eCode & TP_ABOVE ) y += 0.5*hc + 0.5*ha1; if( t->eCode & TP_BELOW ) y -= 0.5*hc + 0.5*hb1; if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2; if( t->eCode & TP_LJUST ) nx -= jw; if( t->eCode & TP_RJUST ) nx += jw; if( pBox!=0 ){ /* If pBox is not NULL, do not draw any <text>. Instead, just expand ** pBox to include the text */ PNum cw = pik_text_length(t, t->eCode & TP_MONO)*p->charWidth*xtraFontScale*0.01; PNum ch = p->charHeight*0.5*xtraFontScale; PNum x0, y0, x1, y1; /* Boundary of text relative to pObj->ptAt */ if( (t->eCode & (TP_BOLD|TP_MONO))==TP_BOLD ){ cw *= 1.1; } if( t->eCode & TP_RJUST ){ x0 = nx; y0 = y-ch; x1 = nx-cw; y1 = y+ch; }else if( t->eCode & TP_LJUST ){ x0 = nx; y0 = y-ch; x1 = nx+cw; y1 = y+ch; }else{ x0 = nx+cw/2; y0 = y+ch; x1 = nx-cw/2; y1 = y-ch; } if( (t->eCode & TP_ALIGN)!=0 && pObj->nPath>=2 ){ int nn = pObj->nPath; PNum dx = pObj->aPath[nn-1].x - pObj->aPath[0].x; PNum dy = pObj->aPath[nn-1].y - pObj->aPath[0].y; if( dx!=0 || dy!=0 ){ PNum dist = hypot(dx,dy); PNum tt; dx /= dist; dy /= dist; tt = dx*x0 - dy*y0; y0 = dy*x0 - dx*y0; x0 = tt; tt = dx*x1 - dy*y1; y1 = dy*x1 - dx*y1; x1 = tt; } } pik_bbox_add_xy(pBox, x+x0, orig_y+y0); pik_bbox_add_xy(pBox, x+x1, orig_y+y1); continue; } nx += x; y += orig_y; pik_append_x(p, "<text x=\"", nx, "\""); pik_append_y(p, " y=\"", y, "\""); if( t->eCode & TP_RJUST ){ pik_append(p, " text-anchor=\"end\"", -1); }else if( t->eCode & TP_LJUST ){ pik_append(p, " text-anchor=\"start\"", -1); }else{ pik_append(p, " text-anchor=\"middle\"", -1); } if( t->eCode & TP_ITALIC ){ pik_append(p, " font-style=\"italic\"", -1); } if( t->eCode & TP_BOLD ){ pik_append(p, " font-weight=\"bold\"", -1); } if( t->eCode & TP_MONO ){ pik_append(p, " font-family=\"monospace\"", -1); } if( pObj->color>=0.0 ){ pik_append_clr(p, " fill=\"", pObj->color, "\"",0); } xtraFontScale *= p->fontScale; if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){ pik_append_num(p, " font-size=\"", xtraFontScale*100.0); pik_append(p, "%\"", 2); } if( (t->eCode & TP_ALIGN)!=0 && pObj->nPath>=2 ){ int nn = pObj->nPath; PNum dx = pObj->aPath[nn-1].x - pObj->aPath[0].x; PNum dy = pObj->aPath[nn-1].y - pObj->aPath[0].y; if( dx!=0 || dy!=0 ){ PNum ang = atan2(dy,dx)*-180/M_PI; pik_append_num(p, " transform=\"rotate(", ang); pik_append_xy(p, " ", x, orig_y); pik_append(p,")\"",2); } } pik_append(p," dominant-baseline=\"central\">",-1); if( t->n>=2 && t->z[0]=='"' ){ z = t->z+1; nz = t->n-2; }else{ z = t->z; nz = t->n; } while( nz>0 ){ int j; for(j=0; j<nz && z[j]!='\\'; j++){} if( j ) pik_append_text(p, z, j, 0x3); if( j<nz && (j+1==nz || z[j+1]=='\\') ){ pik_append(p, "\", -1); j++; } nz -= j+1; z += j+1; } pik_append(p, "</text>\n", -1); } } /* ** Append text (that will go inside of a <pre>...</pre>) that ** shows the context of an error token. */ static void pik_error_context(Pik *p, PToken *pErr, int nContext){ int iErrPt; /* Index of first byte of error from start of input */ int iErrCol; /* Column of the error token on its line */ int iStart; /* Start position of the error context */ int iEnd; /* End position of the error context */ int iLineno; /* Line number of the error */ int iFirstLineno; /* Line number of start of error context */ int i; /* Loop counter */ int iBump = 0; /* Bump the location of the error cursor */ char zLineno[24]; /* Buffer in which to generate line numbers */ iErrPt = (int)(pErr->z - p->sIn.z); if( iErrPt>=(int)p->sIn.n ){ iErrPt = p->sIn.n-1; iBump = 1; }else{ while( iErrPt>0 && (p->sIn.z[iErrPt]=='\n' || p->sIn.z[iErrPt]=='\r') ){ iErrPt--; iBump = 1; } } iLineno = 1; for(i=0; i<iErrPt; i++){ if( p->sIn.z[i]=='\n' ){ iLineno++; } } iStart = 0; iFirstLineno = 1; while( iFirstLineno+nContext<iLineno ){ while( p->sIn.z[iStart]!='\n' ){ iStart++; } iStart++; iFirstLineno++; } for(iEnd=iErrPt; p->sIn.z[iEnd]!=0 && p->sIn.z[iEnd]!='\n'; iEnd++){} i = iStart; while( iFirstLineno<=iLineno ){ snprintf(zLineno,sizeof(zLineno)-1,"/* %4d */ ", iFirstLineno++); zLineno[sizeof(zLineno)-1] = 0; pik_append(p, zLineno, -1); for(i=iStart; p->sIn.z[i]!=0 && p->sIn.z[i]!='\n'; i++){} pik_append_errtxt(p, p->sIn.z+iStart, i-iStart); iStart = i+1; pik_append(p, "\n", 1); } for(iErrCol=0, i=iErrPt; i>0 && p->sIn.z[i]!='\n'; iErrCol++, i--){} for(i=0; i<iErrCol+11+iBump; i++){ pik_append(p, " ", 1); } for(i=0; i<(int)pErr->n; i++) pik_append(p, "^", 1); pik_append(p, "\n", 1); } /* ** Generate an error message for the output. pErr is the token at which ** the error should point. zMsg is the text of the error message. If ** either pErr or zMsg is NULL, generate an out-of-memory error message. ** ** This routine is a no-op if there has already been an error reported. */ static void pik_error(Pik *p, PToken *pErr, const char *zMsg){ int i; if( p==0 ) return; if( p->nErr ) return; p->nErr++; if( zMsg==0 ){ if( p->mFlags & PIKCHR_PLAINTEXT_ERRORS ){ pik_append(p, "\nOut of memory\n", -1); }else{ pik_append(p, "\n<div><p>Out of memory</p></div>\n", -1); } return; } if( pErr==0 ){ pik_append(p, "\n", 1); pik_append_errtxt(p, zMsg, -1); return; } if( (p->mFlags & PIKCHR_PLAINTEXT_ERRORS)==0 ){ pik_append(p, "<div><pre>\n", -1); } pik_error_context(p, pErr, 5); pik_append(p, "ERROR: ", -1); pik_append_errtxt(p, zMsg, -1); pik_append(p, "\n", 1); for(i=p->nCtx-1; i>=0; i--){ pik_append(p, "Called from:\n", -1); pik_error_context(p, &p->aCtx[i], 0); } if( (p->mFlags & PIKCHR_PLAINTEXT_ERRORS)==0 ){ pik_append(p, "</pre></div>\n", -1); } } /* ** Process an "assert( e1 == e2 )" statement. Always return NULL. */ static PObj *pik_assert(Pik *p, PNum e1, PToken *pEq, PNum e2){ char zE1[100], zE2[100], zMsg[300]; /* Convert the numbers to strings using %g for comparison. This ** limits the precision of the comparison to account for rounding error. */ snprintf(zE1, sizeof(zE1), "%g", e1); zE1[sizeof(zE1)-1] = 0; snprintf(zE2, sizeof(zE2), "%g", e2); zE1[sizeof(zE2)-1] = 0; if( strcmp(zE1,zE2)!=0 ){ snprintf(zMsg, sizeof(zMsg), "%.50s != %.50s", zE1, zE2); pik_error(p, pEq, zMsg); } return 0; } /* ** Process an "assert( place1 == place2 )" statement. Always return NULL. */ static PObj *pik_position_assert(Pik *p, PPoint *e1, PToken *pEq, PPoint *e2){ char zE1[100], zE2[100], zMsg[210]; /* Convert the numbers to strings using %g for comparison. This ** limits the precision of the comparison to account for rounding error. */ snprintf(zE1, sizeof(zE1), "(%g,%g)", e1->x, e1->y); zE1[sizeof(zE1)-1] = 0; snprintf(zE2, sizeof(zE2), "(%g,%g)", e2->x, e2->y); zE1[sizeof(zE2)-1] = 0; if( strcmp(zE1,zE2)!=0 ){ snprintf(zMsg, sizeof(zMsg), "%s != %s", zE1, zE2); pik_error(p, pEq, zMsg); } return 0; } /* Free a complete list of objects */ static void pik_elist_free(Pik *p, PList *pList){ int i; if( pList==0 ) return; for(i=0; i<pList->n; i++){ pik_elem_free(p, pList->a[i]); } free(pList->a); free(pList); return; } /* Free a single object, and its substructure */ static void pik_elem_free(Pik *p, PObj *pObj){ if( pObj==0 ) return; free(pObj->zName); pik_elist_free(p, pObj->pSublist); free(pObj->aPath); free(pObj); } /* Convert a numeric literal into a number. Return that number. ** There is no error handling because the tokenizer has already ** assured us that the numeric literal is valid. ** ** Allowed number forms: ** ** (1) Floating point literal ** (2) Same as (1) but followed by a unit: "cm", "mm", "in", ** "px", "pt", or "pc". ** (3) Hex integers: 0x000000 ** ** This routine returns the result in inches. If a different unit ** is specified, the conversion happens automatically. */ PNum pik_atof(PToken *num){ char *endptr; PNum ans; if( num->n>=3 && num->z[0]=='0' && (num->z[1]=='x'||num->z[1]=='X') ){ return (PNum)strtol(num->z+2, 0, 16); } ans = strtod(num->z, &endptr); if( (int)(endptr - num->z)==(int)num->n-2 ){ char c1 = endptr[0]; char c2 = endptr[1]; if( c1=='c' && c2=='m' ){ ans /= 2.54; }else if( c1=='m' && c2=='m' ){ ans /= 25.4; }else if( c1=='p' && c2=='x' ){ ans /= 96; }else if( c1=='p' && c2=='t' ){ ans /= 72; }else if( c1=='p' && c2=='c' ){ ans /= 6; } } return ans; } /* ** Compute the distance between two points */ static PNum pik_dist(PPoint *pA, PPoint *pB){ PNum dx, dy; dx = pB->x - pA->x; dy = pB->y - pA->y; return hypot(dx,dy); } /* Return true if a bounding box is empty. */ static int pik_bbox_isempty(PBox *p){ return p->sw.x>p->ne.x; } /* Return true if point pPt is contained within the bounding box pBox */ static int pik_bbox_contains_point(PBox *pBox, PPoint *pPt){ if( pik_bbox_isempty(pBox) ) return 0; if( pPt->x < pBox->sw.x ) return 0; if( pPt->x > pBox->ne.x ) return 0; if( pPt->y < pBox->sw.y ) return 0; if( pPt->y > pBox->ne.y ) return 0; return 1; } /* Initialize a bounding box to an empty container */ static void pik_bbox_init(PBox *p){ p->sw.x = 1.0; p->sw.y = 1.0; p->ne.x = 0.0; p->ne.y = 0.0; } /* Enlarge the PBox of the first argument so that it fully ** covers the second PBox */ static void pik_bbox_addbox(PBox *pA, PBox *pB){ if( pik_bbox_isempty(pA) ){ *pA = *pB; } if( pik_bbox_isempty(pB) ) return; if( pA->sw.x>pB->sw.x ) pA->sw.x = pB->sw.x; if( pA->sw.y>pB->sw.y ) pA->sw.y = pB->sw.y; if( pA->ne.x<pB->ne.x ) pA->ne.x = pB->ne.x; if( pA->ne.y<pB->ne.y ) pA->ne.y = pB->ne.y; } /* Enlarge the PBox of the first argument, if necessary, so that ** it contains the point described by the 2nd and 3rd arguments. */ static void pik_bbox_add_xy(PBox *pA, PNum x, PNum y){ if( pik_bbox_isempty(pA) ){ pA->ne.x = x; pA->ne.y = y; pA->sw.x = x; pA->sw.y = y; return; } if( pA->sw.x>x ) pA->sw.x = x; if( pA->sw.y>y ) pA->sw.y = y; if( pA->ne.x<x ) pA->ne.x = x; if( pA->ne.y<y ) pA->ne.y = y; } /* Enlarge the PBox so that it is able to contain an ellipse ** centered at x,y and with radiuses rx and ry. */ static void pik_bbox_addellipse(PBox *pA, PNum x, PNum y, PNum rx, PNum ry){ if( pik_bbox_isempty(pA) ){ pA->ne.x = x+rx; pA->ne.y = y+ry; pA->sw.x = x-rx; pA->sw.y = y-ry; return; } if( pA->sw.x>x-rx ) pA->sw.x = x-rx; if( pA->sw.y>y-ry ) pA->sw.y = y-ry; if( pA->ne.x<x+rx ) pA->ne.x = x+rx; if( pA->ne.y<y+ry ) pA->ne.y = y+ry; } /* Append a new object onto the end of an object list. The ** object list is created if it does not already exist. Return ** the new object list. */ static PList *pik_elist_append(Pik *p, PList *pList, PObj *pObj){ if( pObj==0 ) return pList; if( pList==0 ){ pList = malloc(sizeof(*pList)); if( pList==0 ){ pik_error(p, 0, 0); pik_elem_free(p, pObj); return 0; } memset(pList, 0, sizeof(*pList)); } if( pList->n>=pList->nAlloc ){ int nNew = (pList->n+5)*2; PObj **pNew = realloc(pList->a, sizeof(PObj*)*nNew); if( pNew==0 ){ pik_error(p, 0, 0); pik_elem_free(p, pObj); return pList; } pList->nAlloc = nNew; pList->a = pNew; } pList->a[pList->n++] = pObj; p->list = pList; return pList; } /* Convert an object class name into a PClass pointer */ static const PClass *pik_find_class(PToken *pId){ int first = 0; int last = count(aClass) - 1; do{ int mid = (first+last)/2; int c = strncmp(aClass[mid].zName, pId->z, pId->n); if( c==0 ){ c = aClass[mid].zName[pId->n]!=0; if( c==0 ) return &aClass[mid]; } if( c<0 ){ first = mid + 1; }else{ last = mid - 1; } }while( first<=last ); return 0; } /* Allocate and return a new PObj object. ** ** If pId!=0 then pId is an identifier that defines the object class. ** If pStr!=0 then it is a STRING literal that defines a text object. ** If pSublist!=0 then this is a [...] object. If all three parameters ** are NULL then this is a no-op object used to define a PLACENAME. */ static PObj *pik_elem_new(Pik *p, PToken *pId, PToken *pStr,PList *pSublist){ PObj *pNew; int miss = 0; if( p->nErr ) return 0; pNew = malloc( sizeof(*pNew) ); if( pNew==0 ){ pik_error(p,0,0); pik_elist_free(p, pSublist); return 0; } memset(pNew, 0, sizeof(*pNew)); p->cur = pNew; p->nTPath = 1; p->thenFlag = 0; if( p->list==0 || p->list->n==0 ){ pNew->ptAt.x = pNew->ptAt.y = 0.0; pNew->eWith = CP_C; }else{ PObj *pPrior = p->list->a[p->list->n-1]; pNew->ptAt = pPrior->ptExit; switch( p->eDir ){ default: pNew->eWith = CP_W; break; case DIR_LEFT: pNew->eWith = CP_E; break; case DIR_UP: pNew->eWith = CP_S; break; case DIR_DOWN: pNew->eWith = CP_N; break; } } p->aTPath[0] = pNew->ptAt; pNew->with = pNew->ptAt; pNew->outDir = pNew->inDir = p->eDir; pNew->iLayer = pik_value_int(p, "layer", 5, &miss); if( miss ) pNew->iLayer = 1000; if( pNew->iLayer<0 ) pNew->iLayer = 0; if( pSublist ){ pNew->type = &sublistClass; pNew->pSublist = pSublist; sublistClass.xInit(p,pNew); return pNew; } if( pStr ){ PToken n; n.z = "text"; n.n = 4; pNew->type = pik_find_class(&n); assert( pNew->type!=0 ); pNew->errTok = *pStr; pNew->type->xInit(p, pNew); pik_add_txt(p, pStr, pStr->eCode); return pNew; } if( pId ){ const PClass *pClass; pNew->errTok = *pId; pClass = pik_find_class(pId); if( pClass ){ pNew->type = pClass; pNew->sw = pik_value(p, "thickness",9,0); pNew->fill = pik_value(p, "fill",4,0); pNew->color = pik_value(p, "color",5,0); pClass->xInit(p, pNew); return pNew; } pik_error(p, pId, "unknown object type"); pik_elem_free(p, pNew); return 0; } pNew->type = &noopClass; pNew->ptExit = pNew->ptEnter = pNew->ptAt; return pNew; } /* ** If the ID token in the argument is the name of a macro, return ** the PMacro object for that macro */ static PMacro *pik_find_macro(Pik *p, PToken *pId){ PMacro *pMac; for(pMac = p->pMacros; pMac; pMac=pMac->pNext){ if( pMac->macroName.n==pId->n && strncmp(pMac->macroName.z,pId->z,pId->n)==0 ){ return pMac; } } return 0; } /* Add a new macro */ static void pik_add_macro( Pik *p, /* Current Pikchr diagram */ PToken *pId, /* The ID token that defines the macro name */ PToken *pCode /* Macro body inside of {...} */ ){ PMacro *pNew = pik_find_macro(p, pId); if( pNew==0 ){ pNew = malloc( sizeof(*pNew) ); if( pNew==0 ){ pik_error(p, 0, 0); return; } pNew->pNext = p->pMacros; p->pMacros = pNew; pNew->macroName = *pId; } pNew->macroBody.z = pCode->z+1; pNew->macroBody.n = pCode->n-2; pNew->inUse = 0; } /* ** Set the output direction and exit point for an object */ static void pik_elem_set_exit(PObj *pObj, int eDir){ assert( ValidDir(eDir) ); pObj->outDir = eDir; if( !pObj->type->isLine || pObj->bClose ){ pObj->ptExit = pObj->ptAt; switch( pObj->outDir ){ default: pObj->ptExit.x += pObj->w*0.5; break; case DIR_LEFT: pObj->ptExit.x -= pObj->w*0.5; break; case DIR_UP: pObj->ptExit.y += pObj->h*0.5; break; case DIR_DOWN: pObj->ptExit.y -= pObj->h*0.5; break; } } } /* Change the layout direction. */ static void pik_set_direction(Pik *p, int eDir){ assert( ValidDir(eDir) ); p->eDir = (unsigned char)eDir; /* It seems to make sense to reach back into the last object and ** change its exit point (its ".end") to correspond to the new ** direction. Things just seem to work better this way. However, ** legacy PIC does *not* do this. ** ** The difference can be seen in a script like this: ** ** arrow; circle; down; arrow ** ** You can make pikchr render the above exactly like PIC ** by deleting the following three lines. But I (drh) think ** it works better with those lines in place. */ if( p->list && p->list->n ){ pik_elem_set_exit(p->list->a[p->list->n-1], eDir); } } /* Move all coordinates contained within an object (and within its ** substructure) by dx, dy */ static void pik_elem_move(PObj *pObj, PNum dx, PNum dy){ int i; pObj->ptAt.x += dx; pObj->ptAt.y += dy; pObj->ptEnter.x += dx; pObj->ptEnter.y += dy; pObj->ptExit.x += dx; pObj->ptExit.y += dy; pObj->bbox.ne.x += dx; pObj->bbox.ne.y += dy; pObj->bbox.sw.x += dx; pObj->bbox.sw.y += dy; for(i=0; i<pObj->nPath; i++){ pObj->aPath[i].x += dx; pObj->aPath[i].y += dy; } if( pObj->pSublist ){ pik_elist_move(pObj->pSublist, dx, dy); } } static void pik_elist_move(PList *pList, PNum dx, PNum dy){ int i; for(i=0; i<pList->n; i++){ pik_elem_move(pList->a[i], dx, dy); } } /* ** Check to see if it is ok to set the value of paraemeter mThis. ** Return 0 if it is ok. If it not ok, generate an appropriate ** error message and return non-zero. ** ** Flags are set in pObj so that the same object or conflicting ** objects may not be set again. ** ** To be ok, bit mThis must be clear and no more than one of ** the bits identified by mBlockers may be set. */ static int pik_param_ok( Pik *p, /* For storing the error message (if any) */ PObj *pObj, /* The object under construction */ PToken *pId, /* Make the error point to this token */ int mThis /* Value we are trying to set */ ){ if( pObj->mProp & mThis ){ pik_error(p, pId, "value is already set"); return 1; } if( pObj->mCalc & mThis ){ pik_error(p, pId, "value already fixed by prior constraints"); return 1; } pObj->mProp |= mThis; return 0; } /* ** Set a numeric property like "width 7" or "radius 200%". ** ** The rAbs term is an absolute value to add in. rRel is ** a relative value by which to change the current value. */ void pik_set_numprop(Pik *p, PToken *pId, PRel *pVal){ PObj *pObj = p->cur; switch( pId->eType ){ case T_HEIGHT: if( pik_param_ok(p, pObj, pId, A_HEIGHT) ) return; pObj->h = pObj->h*pVal->rRel + pVal->rAbs; break; case T_WIDTH: if( pik_param_ok(p, pObj, pId, A_WIDTH) ) return; pObj->w = pObj->w*pVal->rRel + pVal->rAbs; break; case T_RADIUS: if( pik_param_ok(p, pObj, pId, A_RADIUS) ) return; pObj->rad = pObj->rad*pVal->rRel + pVal->rAbs; break; case T_DIAMETER: if( pik_param_ok(p, pObj, pId, A_RADIUS) ) return; pObj->rad = pObj->rad*pVal->rRel + 0.5*pVal->rAbs; /* diam it 2x rad */ break; case T_THICKNESS: if( pik_param_ok(p, pObj, pId, A_THICKNESS) ) return; pObj->sw = pObj->sw*pVal->rRel + pVal->rAbs; break; } if( pObj->type->xNumProp ){ pObj->type->xNumProp(p, pObj, pId); } return; } /* ** Set a color property. The argument is an RGB value. */ void pik_set_clrprop(Pik *p, PToken *pId, PNum rClr){ PObj *pObj = p->cur; switch( pId->eType ){ case T_FILL: if( pik_param_ok(p, pObj, pId, A_FILL) ) return; pObj->fill = rClr; break; case T_COLOR: if( pik_param_ok(p, pObj, pId, A_COLOR) ) return; pObj->color = rClr; break; } if( pObj->type->xNumProp ){ pObj->type->xNumProp(p, pObj, pId); } return; } /* ** Set a "dashed" property like "dash 0.05" ** ** Use the value supplied by pVal if available. If pVal==0, use ** a default. */ void pik_set_dashed(Pik *p, PToken *pId, PNum *pVal){ PObj *pObj = p->cur; PNum v; switch( pId->eType ){ case T_DOTTED: { v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal; pObj->dotted = v; pObj->dashed = 0.0; break; } case T_DASHED: { v = pVal==0 ? pik_value(p,"dashwid",7,0) : *pVal; pObj->dashed = v; pObj->dotted = 0.0; break; } } } /* ** If the current path information came from a "same" or "same as" ** reset it. */ static void pik_reset_samepath(Pik *p){ if( p->samePath ){ p->samePath = 0; p->nTPath = 1; } } /* Add a new term to the path for a line-oriented object by transferring ** the information in the ptTo field over onto the path and into ptFrom ** resetting the ptTo. */ static void pik_then(Pik *p, PToken *pToken, PObj *pObj){ int n; if( !pObj->type->isLine ){ pik_error(p, pToken, "use with line-oriented objects only"); return; } n = p->nTPath - 1; if( n<1 && (pObj->mProp & A_FROM)==0 ){ pik_error(p, pToken, "no prior path points"); return; } p->thenFlag = 1; } /* Advance to the next entry in p->aTPath. Return its index. */ static int pik_next_rpath(Pik *p, PToken *pErr){ int n = p->nTPath - 1; if( n+1>=(int)count(p->aTPath) ){ pik_error(0, pErr, "too many path elements"); return n; } n++; p->nTPath++; p->aTPath[n] = p->aTPath[n-1]; p->mTPath = 0; return n; } /* Add a direction term to an object. "up 0.5", or "left 3", or "down" ** or "down 50%". */ static void pik_add_direction(Pik *p, PToken *pDir, PRel *pVal){ PObj *pObj = p->cur; int n; int dir; if( !pObj->type->isLine ){ if( pDir ){ pik_error(p, pDir, "use with line-oriented objects only"); }else{ PToken x = pik_next_semantic_token(&pObj->errTok); pik_error(p, &x, "syntax error"); } return; } pik_reset_samepath(p); n = p->nTPath - 1; if( p->thenFlag || p->mTPath==3 || n==0 ){ n = pik_next_rpath(p, pDir); p->thenFlag = 0; } dir = pDir ? pDir->eCode : p->eDir; switch( dir ){ case DIR_UP: if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir); p->aTPath[n].y += pVal->rAbs + pObj->h*pVal->rRel; p->mTPath |= 2; break; case DIR_DOWN: if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir); p->aTPath[n].y -= pVal->rAbs + pObj->h*pVal->rRel; p->mTPath |= 2; break; case DIR_RIGHT: if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir); p->aTPath[n].x += pVal->rAbs + pObj->w*pVal->rRel; p->mTPath |= 1; break; case DIR_LEFT: if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir); p->aTPath[n].x -= pVal->rAbs + pObj->w*pVal->rRel; p->mTPath |= 1; break; } pObj->outDir = dir; } /* Process a movement attribute of one of these forms: ** ** pDist pHdgKW rHdg pEdgept ** GO distance HEADING angle ** GO distance compasspoint */ static void pik_move_hdg( Pik *p, /* The Pikchr context */ PRel *pDist, /* Distance to move */ PToken *pHeading, /* "heading" keyword if present */ PNum rHdg, /* Angle argument to "heading" keyword */ PToken *pEdgept, /* EDGEPT keyword "ne", "sw", etc... */ PToken *pErr /* Token to use for error messages */ ){ PObj *pObj = p->cur; int n; PNum rDist = pDist->rAbs + pik_value(p,"linewid",7,0)*pDist->rRel; if( !pObj->type->isLine ){ pik_error(p, pErr, "use with line-oriented objects only"); return; } pik_reset_samepath(p); do{ n = pik_next_rpath(p, pErr); }while( n<1 ); if( pHeading ){ rHdg = fmod(rHdg,360.0); }else if( pEdgept->eEdge==CP_C ){ pik_error(p, pEdgept, "syntax error"); return; }else{ rHdg = pik_hdg_angle[pEdgept->eEdge]; } if( rHdg<=45.0 ){ pObj->outDir = DIR_UP; }else if( rHdg<=135.0 ){ pObj->outDir = DIR_RIGHT; }else if( rHdg<=225.0 ){ pObj->outDir = DIR_DOWN; }else if( rHdg<=315.0 ){ pObj->outDir = DIR_LEFT; }else{ pObj->outDir = DIR_UP; } rHdg *= 0.017453292519943295769; /* degrees to radians */ p->aTPath[n].x += rDist*sin(rHdg); p->aTPath[n].y += rDist*cos(rHdg); p->mTPath = 2; } /* Process a movement attribute of the form "right until even with ..." ** ** pDir is the first keyword, "right" or "left" or "up" or "down". ** The movement is in that direction until its closest approach to ** the point specified by pPoint. */ static void pik_evenwith(Pik *p, PToken *pDir, PPoint *pPlace){ PObj *pObj = p->cur; int n; if( !pObj->type->isLine ){ pik_error(p, pDir, "use with line-oriented objects only"); return; } pik_reset_samepath(p); n = p->nTPath - 1; if( p->thenFlag || p->mTPath==3 || n==0 ){ n = pik_next_rpath(p, pDir); p->thenFlag = 0; } switch( pDir->eCode ){ case DIR_DOWN: case DIR_UP: if( p->mTPath & 2 ) n = pik_next_rpath(p, pDir); p->aTPath[n].y = pPlace->y; p->mTPath |= 2; break; case DIR_RIGHT: case DIR_LEFT: if( p->mTPath & 1 ) n = pik_next_rpath(p, pDir); p->aTPath[n].x = pPlace->x; p->mTPath |= 1; break; } pObj->outDir = pDir->eCode; } /* If the last referenced object is centered at point pPt then return ** a pointer to that object. If there is no prior object reference, ** or if the points are not the same, return NULL. ** ** This is a side-channel hack used to find the objects at which a ** line begins and ends. For example, in ** ** arrow from OBJ1 to OBJ2 chop ** ** The arrow object is normally just handed the coordinates of the ** centers for OBJ1 and OBJ2. But we also want to know the specific ** object named in case there are multiple objects centered at the ** same point. ** ** See forum post 1d46e3a0bc */ static PObj *pik_last_ref_object(Pik *p, PPoint *pPt){ PObj *pRes = 0; if( p->lastRef==0 ) return 0; if( p->lastRef->ptAt.x==pPt->x && p->lastRef->ptAt.y==pPt->y ){ pRes = p->lastRef; } p->lastRef = 0; return pRes; } /* Set the "from" of an object */ static void pik_set_from(Pik *p, PObj *pObj, PToken *pTk, PPoint *pPt){ if( !pObj->type->isLine ){ pik_error(p, pTk, "use \"at\" to position this object"); return; } if( pObj->mProp & A_FROM ){ pik_error(p, pTk, "line start location already fixed"); return; } if( pObj->bClose ){ pik_error(p, pTk, "polygon is closed"); return; } if( p->nTPath>1 ){ PNum dx = pPt->x - p->aTPath[0].x; PNum dy = pPt->y - p->aTPath[0].y; int i; for(i=1; i<p->nTPath; i++){ p->aTPath[i].x += dx; p->aTPath[i].y += dy; } } p->aTPath[0] = *pPt; p->mTPath = 3; pObj->mProp |= A_FROM; pObj->pFrom = pik_last_ref_object(p, pPt); } /* Set the "to" of an object */ static void pik_add_to(Pik *p, PObj *pObj, PToken *pTk, PPoint *pPt){ int n = p->nTPath-1; if( !pObj->type->isLine ){ pik_error(p, pTk, "use \"at\" to position this object"); return; } if( pObj->bClose ){ pik_error(p, pTk, "polygon is closed"); return; } pik_reset_samepath(p); if( n==0 || p->mTPath==3 || p->thenFlag ){ n = pik_next_rpath(p, pTk); } p->aTPath[n] = *pPt; p->mTPath = 3; pObj->pTo = pik_last_ref_object(p, pPt); } static void pik_close_path(Pik *p, PToken *pErr){ PObj *pObj = p->cur; if( p->nTPath<3 ){ pik_error(p, pErr, "need at least 3 vertexes in order to close the polygon"); return; } if( pObj->bClose ){ pik_error(p, pErr, "polygon already closed"); return; } pObj->bClose = 1; } /* Lower the layer of the current object so that it is behind the ** given object. */ static void pik_behind(Pik *p, PObj *pOther){ PObj *pObj = p->cur; if( p->nErr==0 && pObj->iLayer>=pOther->iLayer ){ pObj->iLayer = pOther->iLayer - 1; } } /* Set the "at" of an object */ static void pik_set_at(Pik *p, PToken *pEdge, PPoint *pAt, PToken *pErrTok){ PObj *pObj; static unsigned char eDirToCp[] = { CP_E, CP_S, CP_W, CP_N }; if( p->nErr ) return; pObj = p->cur; if( pObj->type->isLine ){ pik_error(p, pErrTok, "use \"from\" and \"to\" to position this object"); return; } if( pObj->mProp & A_AT ){ pik_error(p, pErrTok, "location fixed by prior \"at\""); return; } pObj->mProp |= A_AT; pObj->eWith = pEdge ? pEdge->eEdge : CP_C; if( pObj->eWith>=CP_END ){ int dir = pObj->eWith==CP_END ? pObj->outDir : pObj->inDir; pObj->eWith = eDirToCp[dir]; } pObj->with = *pAt; } /* ** Try to add a text attribute to an object */ static void pik_add_txt(Pik *p, PToken *pTxt, int iPos){ PObj *pObj = p->cur; PToken *pT; if( pObj->nTxt >= count(pObj->aTxt) ){ pik_error(p, pTxt, "too many text terms"); return; } pT = &pObj->aTxt[pObj->nTxt++]; *pT = *pTxt; pT->eCode = (short)iPos; } /* Merge "text-position" flags */ static int pik_text_position(int iPrev, PToken *pFlag){ int iRes = iPrev; switch( pFlag->eType ){ case T_LJUST: iRes = (iRes&~TP_JMASK) | TP_LJUST; break; case T_RJUST: iRes = (iRes&~TP_JMASK) | TP_RJUST; break; case T_ABOVE: iRes = (iRes&~TP_VMASK) | TP_ABOVE; break; case T_CENTER: iRes = (iRes&~TP_VMASK) | TP_CENTER; break; case T_BELOW: iRes = (iRes&~TP_VMASK) | TP_BELOW; break; case T_ITALIC: iRes |= TP_ITALIC; break; case T_BOLD: iRes |= TP_BOLD; break; case T_MONO: iRes |= TP_MONO; break; case T_ALIGNED: iRes |= TP_ALIGN; break; case T_BIG: if( iRes & TP_BIG ) iRes |= TP_XTRA; else iRes = (iRes &~TP_SZMASK)|TP_BIG; break; case T_SMALL: if( iRes & TP_SMALL ) iRes |= TP_XTRA; else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break; } return iRes; } /* ** Table of scale-factor estimates for variable-width characters. ** Actual character widths vary by font. These numbers are only ** guesses. And this table only provides data for ASCII. ** ** 100 means normal width. */ static const unsigned char awChar[] = { /* Skip initial 32 control characters */ /* ' ' */ 45, /* '!' */ 55, /* '"' */ 62, /* '#' */ 115, /* '$' */ 90, /* '%' */ 132, /* '&' */ 125, /* '\''*/ 40, /* '(' */ 55, /* ')' */ 55, /* '*' */ 71, /* '+' */ 115, /* ',' */ 45, /* '-' */ 48, /* '.' */ 45, /* '/' */ 50, /* '0' */ 91, /* '1' */ 91, /* '2' */ 91, /* '3' */ 91, /* '4' */ 91, /* '5' */ 91, /* '6' */ 91, /* '7' */ 91, /* '8' */ 91, /* '9' */ 91, /* ':' */ 50, /* ';' */ 50, /* '<' */ 120, /* '=' */ 120, /* '>' */ 120, /* '?' */ 78, /* '@' */ 142, /* 'A' */ 102, /* 'B' */ 105, /* 'C' */ 110, /* 'D' */ 115, /* 'E' */ 105, /* 'F' */ 98, /* 'G' */ 105, /* 'H' */ 125, /* 'I' */ 58, /* 'J' */ 58, /* 'K' */ 107, /* 'L' */ 95, /* 'M' */ 145, /* 'N' */ 125, /* 'O' */ 115, /* 'P' */ 95, /* 'Q' */ 115, /* 'R' */ 107, /* 'S' */ 95, /* 'T' */ 97, /* 'U' */ 118, /* 'V' */ 102, /* 'W' */ 150, /* 'X' */ 100, /* 'Y' */ 93, /* 'Z' */ 100, /* '[' */ 58, /* '\\'*/ 50, /* ']' */ 58, /* '^' */ 119, /* '_' */ 72, /* '`' */ 72, /* 'a' */ 86, /* 'b' */ 92, /* 'c' */ 80, /* 'd' */ 92, /* 'e' */ 85, /* 'f' */ 52, /* 'g' */ 92, /* 'h' */ 92, /* 'i' */ 47, /* 'j' */ 47, /* 'k' */ 88, /* 'l' */ 48, /* 'm' */ 135, /* 'n' */ 92, /* 'o' */ 86, /* 'p' */ 92, /* 'q' */ 92, /* 'r' */ 69, /* 's' */ 75, /* 't' */ 58, /* 'u' */ 92, /* 'v' */ 80, /* 'w' */ 121, /* 'x' */ 81, /* 'y' */ 80, /* 'z' */ 76, /* '{' */ 91, /* '|'*/ 49, /* '}' */ 91, /* '~' */ 118, }; /* Return an estimate of the width of the displayed characters ** in a character string. The returned value is 100 times the ** average character width. ** ** Omit "\" used to escape characters. And count entities like ** "<" as a single character. Multi-byte UTF8 characters count ** as a single character. ** ** Unless using a monospaced font, attempt to scale the answer by ** the actual characters seen. Wide characters count more than ** narrow characters. But the widths are only guesses. ** */ static int pik_text_length(const PToken *pToken, const int isMonospace){ const int stdAvg=100, monoAvg=82; int n = pToken->n; const char *z = pToken->z; int cnt, j; for(j=1, cnt=0; j<n-1; j++){ char c = z[j]; if( c=='\\' && z[j+1]!='&' ){ c = z[++j]; }else if( c=='&' ){ int k; for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){} if( z[k]==';' ) j = k; cnt += (isMonospace ? monoAvg : stdAvg) * 3 / 2; continue; } if( (c & 0xc0)==0xc0 ){ while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; } cnt += isMonospace ? monoAvg : stdAvg; continue; } if( isMonospace ){ cnt += monoAvg; }else if( c >= 0x20 && c <= 0x7e ){ cnt += awChar[c-0x20]; }else{ cnt += stdAvg; } } return cnt; } /* Adjust the width, height, and/or radius of the object so that ** it fits around the text that has been added so far. ** ** (1) Only text specified prior to this attribute is considered. ** (2) The text size is estimated based on the charht and charwid ** variable settings. ** (3) The fitted attributes can be changed again after this ** attribute, for example using "width 110%" if this auto-fit ** underestimates the text size. ** (4) Previously set attributes will not be altered. In other words, ** "width 1in fit" might cause the height to change, but the ** width is now set. ** (5) This only works for attributes that have an xFit method. ** ** The eWhich parameter is: ** ** 1: Fit horizontally only ** 2: Fit vertically only ** 3: Fit both ways */ static void pik_size_to_fit(Pik *p, PToken *pFit, int eWhich){ PObj *pObj; PNum w, h; PBox bbox; if( p->nErr ) return; pObj = p->cur; if( pObj->nTxt==0 ){ pik_error(0, pFit, "no text to fit to"); return; } if( pObj->type->xFit==0 ) return; pik_bbox_init(&bbox); pik_compute_layout_settings(p); pik_append_txt(p, pObj, &bbox); if( (eWhich & 1)!=0 || pObj->bAltAutoFit ){ w = (bbox.ne.x - bbox.sw.x) + p->charWidth; }else{ w = 0; } if( (eWhich & 2)!=0 || pObj->bAltAutoFit ){ PNum h1, h2; h1 = (bbox.ne.y - pObj->ptAt.y); h2 = (pObj->ptAt.y - bbox.sw.y); h = 2.0*( h1<h2 ? h2 : h1 ) + 0.5*p->charHeight; }else{ h = 0; } pObj->type->xFit(p, pObj, w, h); pObj->mProp |= A_FIT; } /* Set a local variable name to "val". ** ** The name might be a built-in variable or a color name. In either case, ** a new application-defined variable is set. Since app-defined variables ** are searched first, this will override any built-in variables. */ static void pik_set_var(Pik *p, PToken *pId, PNum val, PToken *pOp){ PVar *pVar = p->pVar; while( pVar ){ if( pik_token_eq(pId,pVar->zName)==0 ) break; pVar = pVar->pNext; } if( pVar==0 ){ char *z; pVar = malloc( pId->n+1 + sizeof(*pVar) ); if( pVar==0 ){ pik_error(p, 0, 0); return; } pVar->zName = z = (char*)&pVar[1]; memcpy(z, pId->z, pId->n); z[pId->n] = 0; pVar->pNext = p->pVar; pVar->val = pik_value(p, pId->z, pId->n, 0); p->pVar = pVar; } switch( pOp->eCode ){ case T_PLUS: pVar->val += val; break; case T_STAR: pVar->val *= val; break; case T_MINUS: pVar->val -= val; break; case T_SLASH: if( val==0.0 ){ pik_error(p, pOp, "division by zero"); }else{ pVar->val /= val; } break; default: pVar->val = val; break; } p->bLayoutVars = 0; /* Clear the layout setting cache */ } /* ** Round a PNum into the nearest integer */ static int pik_round(PNum v){ if( isnan(v) ) return 0; if( v < -2147483647 ) return (-2147483647-1); if( v >= 2147483647 ) return 2147483647; return (int)v; } /* ** Search for the variable named z[0..n-1] in: ** ** * Application defined variables ** * Built-in variables ** ** Return the value of the variable if found. If not found ** return 0.0. Also if pMiss is not NULL, then set it to 1 ** if not found. ** ** This routine is a subroutine to pik_get_var(). But it is also ** used by object implementations to look up (possibly overwritten) ** values for built-in variables like "boxwid". */ static PNum pik_value(Pik *p, const char *z, int n, int *pMiss){ PVar *pVar; int first, last, mid, c; for(pVar=p->pVar; pVar; pVar=pVar->pNext){ if( strncmp(pVar->zName,z,n)==0 && pVar->zName[n]==0 ){ return pVar->val; } } first = 0; last = count(aBuiltin)-1; while( first<=last ){ mid = (first+last)/2; c = strncmp(z,aBuiltin[mid].zName,n); if( c==0 && aBuiltin[mid].zName[n] ) c = 1; if( c==0 ) return aBuiltin[mid].val; if( c>0 ){ first = mid+1; }else{ last = mid-1; } } if( pMiss ) *pMiss = 1; return 0.0; } static int pik_value_int(Pik *p, const char *z, int n, int *pMiss){ return pik_round(pik_value(p,z,n,pMiss)); } /* ** Look up a color-name. Unlike other names in this program, the ** color-names are not case sensitive. So "DarkBlue" and "darkblue" ** and "DARKBLUE" all find the same value (139). ** ** If not found, return -99.0. Also post an error if p!=NULL. ** ** Special color names "None" and "Off" return -1.0 without causing ** an error. */ static PNum pik_lookup_color(Pik *p, PToken *pId){ int first, last, mid, c = 0; first = 0; last = count(aColor)-1; while( first<=last ){ const char *zClr; int c1, c2; unsigned int i; mid = (first+last)/2; zClr = aColor[mid].zName; for(i=0; i<pId->n; i++){ c1 = zClr[i]&0x7f; if( isupper(c1) ) c1 = tolower(c1); c2 = pId->z[i]&0x7f; if( isupper(c2) ) c2 = tolower(c2); c = c2 - c1; if( c ) break; } if( c==0 && aColor[mid].zName[pId->n] ) c = -1; if( c==0 ) return (double)aColor[mid].val; if( c>0 ){ first = mid+1; }else{ last = mid-1; } } if( p ) pik_error(p, pId, "not a known color name"); return -99.0; } /* Get the value of a variable. ** ** Search in order: ** ** * Application defined variables ** * Built-in variables ** * Color names ** ** If no such variable is found, throw an error. */ static PNum pik_get_var(Pik *p, PToken *pId){ int miss = 0; PNum v = pik_value(p, pId->z, pId->n, &miss); if( miss==0 ) return v; v = pik_lookup_color(0, pId); if( v>-90.0 ) return v; pik_error(p,pId,"no such variable"); return 0.0; } /* Convert a T_NTH token (ex: "2nd", "5th"} into a numeric value and ** return that value. Throw an error if the value is too big. */ static short int pik_nth_value(Pik *p, PToken *pNth){ int i = atoi(pNth->z); if( i>1000 ){ pik_error(p, pNth, "value too big - max '1000th'"); i = 1; } if( i==0 && pik_token_eq(pNth,"first")==0 ) i = 1; return (short int)i; } /* Search for the NTH object. ** ** If pBasis is not NULL then it should be a [] object. Use the ** sublist of that [] object for the search. If pBasis is not a [] ** object, then throw an error. ** ** The pNth token describes the N-th search. The pNth->eCode value ** is one more than the number of items to skip. It is negative ** to search backwards. If pNth->eType==T_ID, then it is the name ** of a class to search for. If pNth->eType==T_LB, then ** search for a [] object. If pNth->eType==T_LAST, then search for ** any type. ** ** Raise an error if the item is not found. */ static PObj *pik_find_nth(Pik *p, PObj *pBasis, PToken *pNth){ PList *pList; int i, n; const PClass *pClass; if( pBasis==0 ){ pList = p->list; }else{ pList = pBasis->pSublist; } if( pList==0 ){ pik_error(p, pNth, "no such object"); return 0; } if( pNth->eType==T_LAST ){ pClass = 0; }else if( pNth->eType==T_LB ){ pClass = &sublistClass; }else{ pClass = pik_find_class(pNth); if( pClass==0 ){ pik_error(0, pNth, "no such object type"); return 0; } } n = pNth->eCode; if( n<0 ){ for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; if( pClass && pObj->type!=pClass ) continue; n++; if( n==0 ){ return pObj; } } }else{ for(i=0; i<pList->n; i++){ PObj *pObj = pList->a[i]; if( pClass && pObj->type!=pClass ) continue; n--; if( n==0 ){ return pObj; } } } pik_error(p, pNth, "no such object"); return 0; } /* Search for an object by name. ** ** Search in pBasis->pSublist if pBasis is not NULL. If pBasis is NULL ** then search in p->list. */ static PObj *pik_find_byname(Pik *p, PObj *pBasis, PToken *pName){ PList *pList; int i, j; if( pBasis==0 ){ pList = p->list; }else{ pList = pBasis->pSublist; } if( pList==0 ){ pik_error(p, pName, "no such object"); return 0; } /* First look explicitly tagged objects */ for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; if( pObj->zName && pik_token_eq(pName,pObj->zName)==0 ){ p->lastRef = pObj; return pObj; } } /* If not found, do a second pass looking for any object containing ** text which exactly matches pName */ for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; for(j=0; j<pObj->nTxt; j++){ if( pObj->aTxt[j].n==pName->n+2 && memcmp(pObj->aTxt[j].z+1,pName->z,pName->n)==0 ){ p->lastRef = pObj; return pObj; } } } pik_error(p, pName, "no such object"); return 0; } /* Change most of the settings for the current object to be the ** same as the pOther object, or the most recent object of the same ** type if pOther is NULL. */ static void pik_same(Pik *p, PObj *pOther, PToken *pErrTok){ PObj *pObj = p->cur; if( p->nErr ) return; if( pOther==0 ){ int i; for(i=(p->list ? p->list->n : 0)-1; i>=0; i--){ pOther = p->list->a[i]; if( pOther->type==pObj->type ) break; } if( i<0 ){ pik_error(p, pErrTok, "no prior objects of the same type"); return; } } if( pOther->nPath && pObj->type->isLine ){ PNum dx, dy; int i; dx = p->aTPath[0].x - pOther->aPath[0].x; dy = p->aTPath[0].y - pOther->aPath[0].y; for(i=1; i<pOther->nPath; i++){ p->aTPath[i].x = pOther->aPath[i].x + dx; p->aTPath[i].y = pOther->aPath[i].y + dy; } p->nTPath = pOther->nPath; p->mTPath = 3; p->samePath = 1; } if( !pObj->type->isLine ){ pObj->w = pOther->w; pObj->h = pOther->h; } pObj->rad = pOther->rad; pObj->sw = pOther->sw; pObj->dashed = pOther->dashed; pObj->dotted = pOther->dotted; pObj->fill = pOther->fill; pObj->color = pOther->color; pObj->cw = pOther->cw; pObj->larrow = pOther->larrow; pObj->rarrow = pOther->rarrow; pObj->bClose = pOther->bClose; pObj->bChop = pOther->bChop; pObj->iLayer = pOther->iLayer; } /* Return a "Place" associated with object pObj. If pEdge is NULL ** return the center of the object. Otherwise, return the corner ** described by pEdge. */ static PPoint pik_place_of_elem(Pik *p, PObj *pObj, PToken *pEdge){ PPoint pt = cZeroPoint; const PClass *pClass; if( pObj==0 ) return pt; if( pEdge==0 ){ return pObj->ptAt; } pClass = pObj->type; if( pEdge->eType==T_EDGEPT || (pEdge->eEdge>0 && pEdge->eEdge<CP_END) ){ pt = pClass->xOffset(p, pObj, pEdge->eEdge); pt.x += pObj->ptAt.x; pt.y += pObj->ptAt.y; return pt; } if( pEdge->eType==T_START ){ return pObj->ptEnter; }else{ return pObj->ptExit; } } /* Do a linear interpolation of two positions. */ static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2){ PPoint out; out.x = p2.x*x + p1.x*(1.0 - x); out.y = p2.y*x + p1.y*(1.0 - x); return out; } /* Compute the position that is dist away from pt at an heading angle of r ** ** The angle is a compass heading in degrees. North is 0 (or 360). ** East is 90. South is 180. West is 270. And so forth. */ static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt){ r *= 0.017453292519943295769; /* degrees to radians */ pt.x += dist*sin(r); pt.y += dist*cos(r); return pt; } /* Compute the position that is dist away at a compass point */ static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt){ return pik_position_at_angle(dist, pik_hdg_angle[pD->eEdge], pt); } /* Return the coordinates for the n-th vertex of a line. */ static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj){ static const PPoint zero = {0, 0}; int n; if( p->nErr || pObj==0 ) return p->aTPath[0]; if( !pObj->type->isLine ){ pik_error(p, pErr, "object is not a line"); return zero; } n = atoi(pNth->z); if( n<1 || n>pObj->nPath ){ pik_error(p, pNth, "no such vertex"); return zero; } return pObj->aPath[n-1]; } /* Return the value of a property of an object. */ static PNum pik_property_of(PObj *pObj, PToken *pProp){ PNum v = 0.0; if( pObj ){ switch( pProp->eType ){ case T_HEIGHT: v = pObj->h; break; case T_WIDTH: v = pObj->w; break; case T_RADIUS: v = pObj->rad; break; case T_DIAMETER: v = pObj->rad*2.0; break; case T_THICKNESS: v = pObj->sw; break; case T_DASHED: v = pObj->dashed; break; case T_DOTTED: v = pObj->dotted; break; case T_FILL: v = pObj->fill; break; case T_COLOR: v = pObj->color; break; case T_X: v = pObj->ptAt.x; break; case T_Y: v = pObj->ptAt.y; break; case T_TOP: v = pObj->bbox.ne.y; break; case T_BOTTOM: v = pObj->bbox.sw.y; break; case T_LEFT: v = pObj->bbox.sw.x; break; case T_RIGHT: v = pObj->bbox.ne.x; break; } } return v; } /* Compute one of the built-in functions */ static PNum pik_func(Pik *p, PToken *pFunc, PNum x, PNum y){ PNum v = 0.0; switch( pFunc->eCode ){ case FN_ABS: v = x<0.0 ? -x : x; break; case FN_COS: v = cos(x); break; case FN_INT: v = rint(x); break; case FN_SIN: v = sin(x); break; case FN_SQRT: if( x<0.0 ){ pik_error(p, pFunc, "sqrt of negative value"); v = 0.0; }else{ v = sqrt(x); } break; case FN_MAX: v = x>y ? x : y; break; case FN_MIN: v = x<y ? x : y; break; default: v = 0.0; } return v; } /* Attach a name to an object */ static void pik_elem_setname(Pik *p, PObj *pObj, PToken *pName){ if( pObj==0 ) return; if( pName==0 ) return; free(pObj->zName); pObj->zName = malloc(pName->n+1); if( pObj->zName==0 ){ pik_error(p,0,0); }else{ memcpy(pObj->zName,pName->z,pName->n); pObj->zName[pName->n] = 0; } return; } /* ** Search for object located at *pCenter that has an xChop method and ** that does not enclose point pOther. ** ** Return a pointer to the object, or NULL if not found. */ static PObj *pik_find_chopper(PList *pList, PPoint *pCenter, PPoint *pOther){ int i; if( pList==0 ) return 0; for(i=pList->n-1; i>=0; i--){ PObj *pObj = pList->a[i]; if( pObj->type->xChop!=0 && pObj->ptAt.x==pCenter->x && pObj->ptAt.y==pCenter->y && !pik_bbox_contains_point(&pObj->bbox, pOther) ){ return pObj; }else if( pObj->pSublist ){ pObj = pik_find_chopper(pObj->pSublist,pCenter,pOther); if( pObj ) return pObj; } } return 0; } /* ** There is a line traveling from pFrom to pTo. ** ** If pObj is not null and is a choppable object, then chop at ** the boundary of pObj - where the line crosses the boundary ** of pObj. ** ** If pObj is NULL or has no xChop method, then search for some ** other object centered at pTo that is choppable and use it ** instead. */ static void pik_autochop(Pik *p, PPoint *pFrom, PPoint *pTo, PObj *pObj){ if( pObj==0 || pObj->type->xChop==0 ){ pObj = pik_find_chopper(p->list, pTo, pFrom); } if( pObj ){ *pTo = pObj->type->xChop(p, pObj, pFrom); } } /* This routine runs after all attributes have been received ** on an object. */ static void pik_after_adding_attributes(Pik *p, PObj *pObj){ int i; PPoint ofst; PNum dx, dy; if( p->nErr ) return; /* Position block objects */ if( pObj->type->isLine==0 ){ /* A height or width less than or equal to zero means "autofit". ** Change the height or width to be big enough to contain the text, */ if( pObj->h<=0.0 ){ if( pObj->nTxt==0 ){ pObj->h = 0.0; }else if( pObj->w<=0.0 ){ pik_size_to_fit(p, &pObj->errTok, 3); }else{ pik_size_to_fit(p, &pObj->errTok, 2); } } if( pObj->w<=0.0 ){ if( pObj->nTxt==0 ){ pObj->w = 0.0; }else{ pik_size_to_fit(p, &pObj->errTok, 1); } } ofst = pik_elem_offset(p, pObj, pObj->eWith); dx = (pObj->with.x - ofst.x) - pObj->ptAt.x; dy = (pObj->with.y - ofst.y) - pObj->ptAt.y; if( dx!=0 || dy!=0 ){ pik_elem_move(pObj, dx, dy); } } /* For a line object with no movement specified, a single movement ** of the default length in the current direction */ if( pObj->type->isLine && p->nTPath<2 ){ pik_next_rpath(p, 0); assert( p->nTPath==2 ); switch( pObj->inDir ){ default: p->aTPath[1].x += pObj->w; break; case DIR_DOWN: p->aTPath[1].y -= pObj->h; break; case DIR_LEFT: p->aTPath[1].x -= pObj->w; break; case DIR_UP: p->aTPath[1].y += pObj->h; break; } if( pObj->type->xInit==arcInit ){ pObj->outDir = (pObj->inDir + (pObj->cw ? 1 : 3))%4; p->eDir = (unsigned char)pObj->outDir; switch( pObj->outDir ){ default: p->aTPath[1].x += pObj->w; break; case DIR_DOWN: p->aTPath[1].y -= pObj->h; break; case DIR_LEFT: p->aTPath[1].x -= pObj->w; break; case DIR_UP: p->aTPath[1].y += pObj->h; break; } } } /* Initialize the bounding box prior to running xCheck */ pik_bbox_init(&pObj->bbox); /* Run object-specific code */ if( pObj->type->xCheck!=0 ){ pObj->type->xCheck(p,pObj); if( p->nErr ) return; } /* Compute final bounding box, entry and exit points, center ** point (ptAt) and path for the object */ if( pObj->type->isLine ){ pObj->aPath = malloc( sizeof(PPoint)*p->nTPath ); if( pObj->aPath==0 ){ pik_error(p, 0, 0); return; }else{ pObj->nPath = p->nTPath; for(i=0; i<p->nTPath; i++){ pObj->aPath[i] = p->aTPath[i]; } } /* "chop" processing: ** If the line goes to the center of an object with an ** xChop method, then use the xChop method to trim the line. */ if( pObj->bChop && pObj->nPath>=2 ){ int n = pObj->nPath; pik_autochop(p, &pObj->aPath[n-2], &pObj->aPath[n-1], pObj->pTo); pik_autochop(p, &pObj->aPath[1], &pObj->aPath[0], pObj->pFrom); } pObj->ptEnter = pObj->aPath[0]; pObj->ptExit = pObj->aPath[pObj->nPath-1]; /* Compute the center of the line based on the bounding box over ** the vertexes. This is a difference from PIC. In Pikchr, the ** center of a line is the center of its bounding box. In PIC, the ** center of a line is halfway between its .start and .end. For ** straight lines, this is the same point, but for multi-segment ** lines the result is usually diferent */ for(i=0; i<pObj->nPath; i++){ pik_bbox_add_xy(&pObj->bbox, pObj->aPath[i].x, pObj->aPath[i].y); } pObj->ptAt.x = (pObj->bbox.ne.x + pObj->bbox.sw.x)/2.0; pObj->ptAt.y = (pObj->bbox.ne.y + pObj->bbox.sw.y)/2.0; /* Reset the width and height of the object to be the width and height ** of the bounding box over vertexes */ pObj->w = pObj->bbox.ne.x - pObj->bbox.sw.x; pObj->h = pObj->bbox.ne.y - pObj->bbox.sw.y; /* If this is a polygon (if it has the "close" attribute), then ** adjust the exit point */ if( pObj->bClose ){ /* For "closed" lines, the .end is one of the .e, .s, .w, or .n ** points of the bounding box, as with block objects. */ pik_elem_set_exit(pObj, pObj->inDir); } }else{ PNum w2 = pObj->w/2.0; PNum h2 = pObj->h/2.0; pObj->ptEnter = pObj->ptAt; pObj->ptExit = pObj->ptAt; switch( pObj->inDir ){ default: pObj->ptEnter.x -= w2; break; case DIR_LEFT: pObj->ptEnter.x += w2; break; case DIR_UP: pObj->ptEnter.y -= h2; break; case DIR_DOWN: pObj->ptEnter.y += h2; break; } switch( pObj->outDir ){ default: pObj->ptExit.x += w2; break; case DIR_LEFT: pObj->ptExit.x -= w2; break; case DIR_UP: pObj->ptExit.y += h2; break; case DIR_DOWN: pObj->ptExit.y -= h2; break; } pik_bbox_add_xy(&pObj->bbox, pObj->ptAt.x - w2, pObj->ptAt.y - h2); pik_bbox_add_xy(&pObj->bbox, pObj->ptAt.x + w2, pObj->ptAt.y + h2); } p->eDir = (unsigned char)pObj->outDir; } /* Show basic information about each object as a comment in the ** generated HTML. Used for testing and debugging. Activated ** by the (undocumented) "debug = 1;" ** command. */ static void pik_elem_render(Pik *p, PObj *pObj){ char *zDir; if( pObj==0 ) return; pik_append(p,"<!-- ", -1); if( pObj->zName ){ pik_append_text(p, pObj->zName, -1, 0); pik_append(p, ": ", 2); } pik_append_text(p, pObj->type->zName, -1, 0); if( pObj->nTxt ){ pik_append(p, " \"", 2); pik_append_text(p, pObj->aTxt[0].z+1, pObj->aTxt[0].n-2, 1); pik_append(p, "\"", 1); } pik_append_num(p, " w=", pObj->w); pik_append_num(p, " h=", pObj->h); pik_append_point(p, " center=", &pObj->ptAt); pik_append_point(p, " enter=", &pObj->ptEnter); switch( pObj->outDir ){ default: zDir = " right"; break; case DIR_LEFT: zDir = " left"; break; case DIR_UP: zDir = " up"; break; case DIR_DOWN: zDir = " down"; break; } pik_append_point(p, " exit=", &pObj->ptExit); pik_append(p, zDir, -1); pik_append(p, " -->\n", -1); } /* Render a list of objects */ void pik_elist_render(Pik *p, PList *pList){ int i; int iNextLayer = 0; int iThisLayer; int bMoreToDo; int miss = 0; int mDebug = pik_value_int(p, "debug", 5, 0); PNum colorLabel; do{ bMoreToDo = 0; iThisLayer = iNextLayer; iNextLayer = 0x7fffffff; for(i=0; i<pList->n; i++){ PObj *pObj = pList->a[i]; void (*xRender)(Pik*,PObj*); if( pObj->iLayer>iThisLayer ){ if( pObj->iLayer<iNextLayer ) iNextLayer = pObj->iLayer; bMoreToDo = 1; continue; /* Defer until another round */ }else if( pObj->iLayer<iThisLayer ){ continue; } if( mDebug & 1 ) pik_elem_render(p, pObj); xRender = pObj->type->xRender; if( xRender ){ xRender(p, pObj); } if( pObj->pSublist ){ pik_elist_render(p, pObj->pSublist); } } }while( bMoreToDo ); /* If the color_debug_label value is defined, then go through ** and paint a dot at every label location */ colorLabel = pik_value(p, "debug_label_color", 17, &miss); if( miss==0 && colorLabel>=0.0 ){ PObj dot; memset(&dot, 0, sizeof(dot)); dot.type = &noopClass; dot.rad = 0.015; dot.sw = 0.015; dot.fill = colorLabel; dot.color = colorLabel; dot.nTxt = 1; dot.aTxt[0].eCode = TP_ABOVE; for(i=0; i<pList->n; i++){ PObj *pObj = pList->a[i]; if( pObj->zName==0 ) continue; dot.ptAt = pObj->ptAt; dot.aTxt[0].z = pObj->zName; dot.aTxt[0].n = (int)strlen(pObj->zName); dotRender(p, &dot); } } } /* Add all objects of the list pList to the bounding box */ static void pik_bbox_add_elist(Pik *p, PList *pList, PNum wArrow){ int i; for(i=0; i<pList->n; i++){ PObj *pObj = pList->a[i]; if( pObj->sw>=0.0 ) pik_bbox_addbox(&p->bbox, &pObj->bbox); pik_append_txt(p, pObj, &p->bbox); if( pObj->pSublist ) pik_bbox_add_elist(p, pObj->pSublist, wArrow); /* Expand the bounding box to account for arrowheads on lines */ if( pObj->type->isLine && pObj->nPath>0 ){ if( pObj->larrow ){ pik_bbox_addellipse(&p->bbox, pObj->aPath[0].x, pObj->aPath[0].y, wArrow, wArrow); } if( pObj->rarrow ){ int j = pObj->nPath-1; pik_bbox_addellipse(&p->bbox, pObj->aPath[j].x, pObj->aPath[j].y, wArrow, wArrow); } } } } /* Recompute key layout parameters from variables. */ static void pik_compute_layout_settings(Pik *p){ PNum thickness; /* Line thickness */ PNum wArrow; /* Width of arrowheads */ /* Set up rendering parameters */ if( p->bLayoutVars ) return; thickness = pik_value(p,"thickness",9,0); if( thickness<=0.01 ) thickness = 0.01; wArrow = 0.5*pik_value(p,"arrowwid",8,0); p->wArrow = wArrow/thickness; p->hArrow = pik_value(p,"arrowht",7,0)/thickness; p->fontScale = pik_value(p,"fontscale",9,0); if( p->fontScale<=0.0 ) p->fontScale = 1.0; p->rScale = 144.0; p->charWidth = pik_value(p,"charwid",7,0)*p->fontScale; p->charHeight = pik_value(p,"charht",6,0)*p->fontScale; p->bLayoutVars = 1; } /* Render a list of objects. Write the SVG into p->zOut. ** Delete the input object_list before returnning. */ static void pik_render(Pik *p, PList *pList){ if( pList==0 ) return; if( p->nErr==0 ){ PNum thickness; /* Stroke width */ PNum margin; /* Extra bounding box margin */ PNum w, h; /* Drawing width and height */ PNum wArrow; PNum pikScale; /* Value of the "scale" variable */ int miss = 0; /* Set up rendering parameters */ pik_compute_layout_settings(p); thickness = pik_value(p,"thickness",9,0); if( thickness<=0.01 ) thickness = 0.01; margin = pik_value(p,"margin",6,0); margin += thickness; wArrow = p->wArrow*thickness; miss = 0; p->fgcolor = pik_value_int(p,"fgcolor",7,&miss); if( miss ){ PToken t; t.z = "fgcolor"; t.n = 7; p->fgcolor = pik_round(pik_lookup_color(0, &t)); } miss = 0; p->bgcolor = pik_value_int(p,"bgcolor",7,&miss); if( miss ){ PToken t; t.z = "bgcolor"; t.n = 7; p->bgcolor = pik_round(pik_lookup_color(0, &t)); } /* Compute a bounding box over all objects so that we can know ** how big to declare the SVG canvas */ pik_bbox_init(&p->bbox); pik_bbox_add_elist(p, pList, wArrow); /* Expand the bounding box slightly to account for line thickness ** and the optional "margin = EXPR" setting. */ p->bbox.ne.x += margin + pik_value(p,"rightmargin",11,0); p->bbox.ne.y += margin + pik_value(p,"topmargin",9,0); p->bbox.sw.x -= margin + pik_value(p,"leftmargin",10,0); p->bbox.sw.y -= margin + pik_value(p,"bottommargin",12,0); /* Output the SVG */ pik_append(p, "<svg xmlns='http://www.w3.org/2000/svg'",-1); if( p->zClass ){ pik_append(p, " class=\"", -1); pik_append(p, p->zClass, -1); pik_append(p, "\"", 1); } w = p->bbox.ne.x - p->bbox.sw.x; h = p->bbox.ne.y - p->bbox.sw.y; p->wSVG = pik_round(p->rScale*w); p->hSVG = pik_round(p->rScale*h); pikScale = pik_value(p,"scale",5,0); if( pikScale>=0.001 && pikScale<=1000.0 && (pikScale<0.99 || pikScale>1.01) ){ p->wSVG = pik_round(p->wSVG*pikScale); p->hSVG = pik_round(p->hSVG*pikScale); pik_append_num(p, " width=\"", p->wSVG); pik_append_num(p, "\" height=\"", p->hSVG); pik_append(p, "\"", 1); } pik_append_dis(p, " viewBox=\"0 0 ",w,""); pik_append_dis(p, " ",h,"\">\n"); pik_elist_render(p, pList); pik_append(p,"</svg>\n", -1); }else{ p->wSVG = -1; p->hSVG = -1; } pik_elist_free(p, pList); } /* ** An array of this structure defines a list of keywords. */ typedef struct PikWord { char *zWord; /* Text of the keyword */ unsigned char nChar; /* Length of keyword text in bytes */ unsigned char eType; /* Token code */ unsigned char eCode; /* Extra code for the token */ unsigned char eEdge; /* CP_* code for corner/edge keywords */ } PikWord; /* ** Keywords */ static const PikWord pik_keywords[] = { { "above", 5, T_ABOVE, 0, 0 }, { "abs", 3, T_FUNC1, FN_ABS, 0 }, { "aligned", 7, T_ALIGNED, 0, 0 }, { "and", 3, T_AND, 0, 0 }, { "as", 2, T_AS, 0, 0 }, { "assert", 6, T_ASSERT, 0, 0 }, { "at", 2, T_AT, 0, 0 }, { "behind", 6, T_BEHIND, 0, 0 }, { "below", 5, T_BELOW, 0, 0 }, { "between", 7, T_BETWEEN, 0, 0 }, { "big", 3, T_BIG, 0, 0 }, { "bold", 4, T_BOLD, 0, 0 }, { "bot", 3, T_EDGEPT, 0, CP_S }, { "bottom", 6, T_BOTTOM, 0, CP_S }, { "c", 1, T_EDGEPT, 0, CP_C }, { "ccw", 3, T_CCW, 0, 0 }, { "center", 6, T_CENTER, 0, CP_C }, { "chop", 4, T_CHOP, 0, 0 }, { "close", 5, T_CLOSE, 0, 0 }, { "color", 5, T_COLOR, 0, 0 }, { "cos", 3, T_FUNC1, FN_COS, 0 }, { "cw", 2, T_CW, 0, 0 }, { "dashed", 6, T_DASHED, 0, 0 }, { "define", 6, T_DEFINE, 0, 0 }, { "diameter", 8, T_DIAMETER, 0, 0 }, { "dist", 4, T_DIST, 0, 0 }, { "dotted", 6, T_DOTTED, 0, 0 }, { "down", 4, T_DOWN, DIR_DOWN, 0 }, { "e", 1, T_EDGEPT, 0, CP_E }, { "east", 4, T_EDGEPT, 0, CP_E }, { "end", 3, T_END, 0, CP_END }, { "even", 4, T_EVEN, 0, 0 }, { "fill", 4, T_FILL, 0, 0 }, { "first", 5, T_NTH, 0, 0 }, { "fit", 3, T_FIT, 0, 0 }, { "from", 4, T_FROM, 0, 0 }, { "go", 2, T_GO, 0, 0 }, { "heading", 7, T_HEADING, 0, 0 }, { "height", 6, T_HEIGHT, 0, 0 }, { "ht", 2, T_HEIGHT, 0, 0 }, { "in", 2, T_IN, 0, 0 }, { "int", 3, T_FUNC1, FN_INT, 0 }, { "invis", 5, T_INVIS, 0, 0 }, { "invisible", 9, T_INVIS, 0, 0 }, { "italic", 6, T_ITALIC, 0, 0 }, { "last", 4, T_LAST, 0, 0 }, { "left", 4, T_LEFT, DIR_LEFT, CP_W }, { "ljust", 5, T_LJUST, 0, 0 }, { "max", 3, T_FUNC2, FN_MAX, 0 }, { "min", 3, T_FUNC2, FN_MIN, 0 }, { "mono", 4, T_MONO, 0, 0 }, { "monospace", 9, T_MONO, 0, 0 }, { "n", 1, T_EDGEPT, 0, CP_N }, { "ne", 2, T_EDGEPT, 0, CP_NE }, { "north", 5, T_EDGEPT, 0, CP_N }, { "nw", 2, T_EDGEPT, 0, CP_NW }, { "of", 2, T_OF, 0, 0 }, { "previous", 8, T_LAST, 0, 0, }, { "print", 5, T_PRINT, 0, 0 }, { "rad", 3, T_RADIUS, 0, 0 }, { "radius", 6, T_RADIUS, 0, 0 }, { "right", 5, T_RIGHT, DIR_RIGHT, CP_E }, { "rjust", 5, T_RJUST, 0, 0 }, { "s", 1, T_EDGEPT, 0, CP_S }, { "same", 4, T_SAME, 0, 0 }, { "se", 2, T_EDGEPT, 0, CP_SE }, { "sin", 3, T_FUNC1, FN_SIN, 0 }, { "small", 5, T_SMALL, 0, 0 }, { "solid", 5, T_SOLID, 0, 0 }, { "south", 5, T_EDGEPT, 0, CP_S }, { "sqrt", 4, T_FUNC1, FN_SQRT, 0 }, { "start", 5, T_START, 0, CP_START }, { "sw", 2, T_EDGEPT, 0, CP_SW }, { "t", 1, T_TOP, 0, CP_N }, { "the", 3, T_THE, 0, 0 }, { "then", 4, T_THEN, 0, 0 }, { "thick", 5, T_THICK, 0, 0 }, { "thickness", 9, T_THICKNESS, 0, 0 }, { "thin", 4, T_THIN, 0, 0 }, { "this", 4, T_THIS, 0, 0 }, { "to", 2, T_TO, 0, 0 }, { "top", 3, T_TOP, 0, CP_N }, { "until", 5, T_UNTIL, 0, 0 }, { "up", 2, T_UP, DIR_UP, 0 }, { "vertex", 6, T_VERTEX, 0, 0 }, { "w", 1, T_EDGEPT, 0, CP_W }, { "way", 3, T_WAY, 0, 0 }, { "west", 4, T_EDGEPT, 0, CP_W }, { "wid", 3, T_WIDTH, 0, 0 }, { "width", 5, T_WIDTH, 0, 0 }, { "with", 4, T_WITH, 0, 0 }, { "x", 1, T_X, 0, 0 }, { "y", 1, T_Y, 0, 0 }, }; /* ** Search a PikWordlist for the given keyword. Return a pointer to the ** keyword entry found. Or return 0 if not found. */ static const PikWord *pik_find_word( const char *zIn, /* Word to search for */ int n, /* Length of zIn */ const PikWord *aList, /* List to search */ int nList /* Number of entries in aList */ ){ int first = 0; int last = nList-1; while( first<=last ){ int mid = (first + last)/2; int sz = aList[mid].nChar; int c = strncmp(zIn, aList[mid].zWord, sz<n ? sz : n); if( c==0 ){ c = n - sz; if( c==0 ) return &aList[mid]; } if( c<0 ){ last = mid-1; }else{ first = mid+1; } } return 0; } /* ** Set a symbolic debugger breakpoint on this routine to receive a ** breakpoint when the "#breakpoint" token is parsed. */ static void pik_breakpoint(const unsigned char *z){ /* Prevent C compilers from optimizing out this routine. */ if( z[2]=='X' ) exit(1); } /* ** Return the length of next token. The token starts on ** the pToken->z character. Fill in other fields of the ** pToken object as appropriate. */ static int pik_token_length(PToken *pToken, int bAllowCodeBlock){ const unsigned char *z = (const unsigned char*)pToken->z; int i; unsigned char c, c2; switch( z[0] ){ case '\\': { pToken->eType = T_WHITESPACE; for(i=1; z[i]=='\r' || z[i]==' ' || z[i]=='\t'; i++){} if( z[i]=='\n' ) return i+1; pToken->eType = T_ERROR; return 1; } case ';': case '\n': { pToken->eType = T_EOL; return 1; } case '"': { for(i=1; (c = z[i])!=0; i++){ if( c=='\\' ){ if( z[i+1]==0 ) break; i++; continue; } if( c=='"' ){ pToken->eType = T_STRING; return i+1; } } pToken->eType = T_ERROR; return i; } case ' ': case '\t': case '\f': case '\r': { for(i=1; (c = z[i])==' ' || c=='\t' || c=='\r' || c=='\t'; i++){} pToken->eType = T_WHITESPACE; return i; } case '#': { for(i=1; (c = z[i])!=0 && c!='\n'; i++){} pToken->eType = T_WHITESPACE; /* If the comment is "#breakpoint" then invoke the pik_breakpoint() ** routine. The pik_breakpoint() routie is a no-op that serves as ** a convenient place to set a gdb breakpoint when debugging. */ if( strncmp((const char*)z,"#breakpoint",11)==0 ) pik_breakpoint(z); return i; } case '/': { if( z[1]=='*' ){ for(i=2; z[i]!=0 && (z[i]!='*' || z[i+1]!='/'); i++){} if( z[i]=='*' ){ pToken->eType = T_WHITESPACE; return i+2; }else{ pToken->eType = T_ERROR; return i; } }else if( z[1]=='/' ){ for(i=2; z[i]!=0 && z[i]!='\n'; i++){} pToken->eType = T_WHITESPACE; return i; }else if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_SLASH; return 2; }else{ pToken->eType = T_SLASH; return 1; } } case '+': { if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_PLUS; return 2; } pToken->eType = T_PLUS; return 1; } case '*': { if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_STAR; return 2; } pToken->eType = T_STAR; return 1; } case '%': { pToken->eType = T_PERCENT; return 1; } case '(': { pToken->eType = T_LP; return 1; } case ')': { pToken->eType = T_RP; return 1; } case '[': { pToken->eType = T_LB; return 1; } case ']': { pToken->eType = T_RB; return 1; } case ',': { pToken->eType = T_COMMA; return 1; } case ':': { pToken->eType = T_COLON; return 1; } case '>': { pToken->eType = T_GT; return 1; } case '=': { if( z[1]=='=' ){ pToken->eType = T_EQ; return 2; } pToken->eType = T_ASSIGN; pToken->eCode = T_ASSIGN; return 1; } case '-': { if( z[1]=='>' ){ pToken->eType = T_RARROW; return 2; }else if( z[1]=='=' ){ pToken->eType = T_ASSIGN; pToken->eCode = T_MINUS; return 2; }else{ pToken->eType = T_MINUS; return 1; } } case '<': { if( z[1]=='-' ){ if( z[2]=='>' ){ pToken->eType = T_LRARROW; return 3; }else{ pToken->eType = T_LARROW; return 2; } }else{ pToken->eType = T_LT; return 1; } } case 0xe2: { if( z[1]==0x86 ){ if( z[2]==0x90 ){ pToken->eType = T_LARROW; /* <- */ return 3; } if( z[2]==0x92 ){ pToken->eType = T_RARROW; /* -> */ return 3; } if( z[2]==0x94 ){ pToken->eType = T_LRARROW; /* <-> */ return 3; } } pToken->eType = T_ERROR; return 1; } case '{': { int len, depth; i = 1; if( bAllowCodeBlock ){ depth = 1; while( z[i] && depth>0 ){ PToken x; x.z = (char*)(z+i); len = pik_token_length(&x, 0); if( len==1 ){ if( z[i]=='{' ) depth++; if( z[i]=='}' ) depth--; } i += len; } }else{ depth = 0; } if( depth ){ pToken->eType = T_ERROR; return 1; } pToken->eType = T_CODEBLOCK; return i; } case '&': { static const struct { int nByte; /* Number of bytes in zEntity */ int eCode; /* Corresponding token code */ const char *zEntity; /* Name of the HTML entity */ } aEntity[] = { /* 123456789 1234567 */ { 6, T_RARROW, "→" }, /* Same as -> */ { 12, T_RARROW, "→" }, /* Same as -> */ { 6, T_LARROW, "←" }, /* Same as <- */ { 11, T_LARROW, "←" }, /* Same as <- */ { 16, T_LRARROW, "↔" }, /* Same as <-> */ }; unsigned int i; for(i=0; i<sizeof(aEntity)/sizeof(aEntity[0]); i++){ if( strncmp((const char*)z,aEntity[i].zEntity,aEntity[i].nByte)==0 ){ pToken->eType = aEntity[i].eCode; return aEntity[i].nByte; } } pToken->eType = T_ERROR; return 1; } default: { c = z[0]; if( c=='.' ){ unsigned char c1 = z[1]; if( islower(c1) ){ const PikWord *pFound; for(i=2; (c = z[i])>='a' && c<='z'; i++){} pFound = pik_find_word((const char*)z+1, i-1, pik_keywords, count(pik_keywords)); if( pFound && (pFound->eEdge>0 || pFound->eType==T_EDGEPT || pFound->eType==T_START || pFound->eType==T_END ) ){ /* Dot followed by something that is a 2-D place value */ pToken->eType = T_DOT_E; }else if( pFound && (pFound->eType==T_X || pFound->eType==T_Y) ){ /* Dot followed by "x" or "y" */ pToken->eType = T_DOT_XY; }else{ /* Any other "dot" */ pToken->eType = T_DOT_L; } return 1; }else if( isdigit(c1) ){ i = 0; /* no-op. Fall through to number handling */ }else if( isupper(c1) ){ for(i=2; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} pToken->eType = T_DOT_U; return 1; }else{ pToken->eType = T_ERROR; return 1; } } if( (c>='0' && c<='9') || c=='.' ){ int nDigit; int isInt = 1; if( c!='.' ){ nDigit = 1; for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; } if( i==1 && (c=='x' || c=='X') ){ for(i=2; (c = z[i])!=0 && isxdigit(c); i++){} pToken->eType = T_NUMBER; return i; } }else{ isInt = 0; nDigit = 0; i = 0; } if( c=='.' ){ isInt = 0; for(i++; (c = z[i])>='0' && c<='9'; i++){ nDigit++; } } if( nDigit==0 ){ pToken->eType = T_ERROR; return i; } if( c=='e' || c=='E' ){ int iBefore = i; i++; c2 = z[i]; if( c2=='+' || c2=='-' ){ i++; c2 = z[i]; } if( c2<'0' || c>'9' ){ /* This is not an exp */ i = iBefore; }else{ i++; isInt = 0; while( (c = z[i])>='0' && c<='9' ){ i++; } } } c2 = c ? z[i+1] : 0; if( isInt ){ if( (c=='t' && c2=='h') || (c=='r' && c2=='d') || (c=='n' && c2=='d') || (c=='s' && c2=='t') ){ pToken->eType = T_NTH; return i+2; } } if( (c=='i' && c2=='n') || (c=='c' && c2=='m') || (c=='m' && c2=='m') || (c=='p' && c2=='t') || (c=='p' && c2=='x') || (c=='p' && c2=='c') ){ i += 2; } pToken->eType = T_NUMBER; return i; }else if( islower(c) ){ const PikWord *pFound; for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} pFound = pik_find_word((const char*)z, i, pik_keywords, count(pik_keywords)); if( pFound ){ pToken->eType = pFound->eType; pToken->eCode = pFound->eCode; pToken->eEdge = pFound->eEdge; return i; } pToken->n = i; if( pik_find_class(pToken)!=0 ){ pToken->eType = T_CLASSNAME; }else{ pToken->eType = T_ID; } return i; }else if( c>='A' && c<='Z' ){ for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} pToken->eType = T_PLACENAME; return i; }else if( c=='$' && z[1]>='1' && z[1]<='9' && !isdigit(z[2]) ){ pToken->eType = T_PARAMETER; pToken->eCode = z[1] - '1'; return 2; }else if( c=='_' || c=='$' || c=='@' ){ for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} pToken->eType = T_ID; return i; }else{ pToken->eType = T_ERROR; return 1; } } } } /* ** Return a pointer to the next non-whitespace token after pThis. ** This is used to help form error messages. */ static PToken pik_next_semantic_token(PToken *pThis){ PToken x; int sz; int i = pThis->n; memset(&x, 0, sizeof(x)); x.z = pThis->z; while(1){ x.z = pThis->z + i; sz = pik_token_length(&x, 1); if( x.eType!=T_WHITESPACE ){ x.n = sz; return x; } i += sz; } } /* Parser arguments to a macro invocation ** ** (arg1, arg2, ...) ** ** Arguments are comma-separated, except that commas within string ** literals or with (...), {...}, or [...] do not count. The argument ** list begins and ends with parentheses. There can be at most 9 ** arguments. ** ** Return the number of bytes in the argument list. */ static unsigned int pik_parse_macro_args( Pik *p, const char *z, /* Start of the argument list */ int n, /* Available bytes */ PToken *args, /* Fill in with the arguments */ PToken *pOuter /* Arguments of the next outer context, or NULL */ ){ int nArg = 0; int i, j, sz; int iStart; int depth = 0; PToken x; if( z[0]!='(' ) return 0; args[0].z = z+1; iStart = 1; for(i=1; i<n && z[i]!=')'; i+=sz){ x.z = z+i; sz = pik_token_length(&x, 0); if( sz!=1 ) continue; if( z[i]==',' && depth<=0 ){ args[nArg].n = i - iStart; if( nArg==8 ){ x.z = z; x.n = 1; pik_error(p, &x, "too many macro arguments - max 9"); return 0; } nArg++; args[nArg].z = z+i+1; iStart = i+1; depth = 0; }else if( z[i]=='(' || z[i]=='{' || z[i]=='[' ){ depth++; }else if( z[i]==')' || z[i]=='}' || z[i]==']' ){ depth--; } } if( z[i]==')' ){ args[nArg].n = i - iStart; /* Remove leading and trailing whitespace from each argument. ** If what remains is one of $1, $2, ... $9 then transfer the ** corresponding argument from the outer context */ for(j=0; j<=nArg; j++){ PToken *t = &args[j]; while( t->n>0 && isspace(t->z[0]) ){ t->n--; t->z++; } while( t->n>0 && isspace(t->z[t->n-1]) ){ t->n--; } if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){ if( pOuter ) *t = pOuter[t->z[1]-'1']; else t->n = 0; } } return i+1; } x.z = z; x.n = 1; pik_error(p, &x, "unterminated macro argument list"); return 0; } /* ** Split up the content of a PToken into multiple tokens and ** send each to the parser. */ void pik_tokenize(Pik *p, PToken *pIn, yyParser *pParser, PToken *aParam){ unsigned int i; int sz = 0; PToken token; PMacro *pMac; for(i=0; i<pIn->n && pIn->z[i] && p->nErr==0; i+=sz){ token.eCode = 0; token.eEdge = 0; token.z = pIn->z + i; sz = pik_token_length(&token, 1); if( token.eType==T_WHITESPACE ){ /* no-op */ }else if( sz>50000 ){ token.n = 1; pik_error(p, &token, "token is too long - max length 50000 bytes"); break; }else if( token.eType==T_ERROR ){ token.n = (unsigned short)(sz & 0xffff); pik_error(p, &token, "unrecognized token"); break; }else if( sz+i>pIn->n ){ token.n = pIn->n - i; pik_error(p, &token, "syntax error"); break; }else if( token.eType==T_PARAMETER ){ /* Substitute a parameter into the input stream */ if( aParam==0 || aParam[token.eCode].n==0 ){ continue; } token.n = (unsigned short)(sz & 0xffff); if( p->nCtx>=count(p->aCtx) ){ pik_error(p, &token, "macros nested too deep"); }else{ p->aCtx[p->nCtx++] = token; pik_tokenize(p, &aParam[token.eCode], pParser, 0); p->nCtx--; } }else if( token.eType==T_ID && (token.n = (unsigned short)(sz & 0xffff), (pMac = pik_find_macro(p,&token))!=0) ){ PToken args[9]; unsigned int j = i+sz; if( pMac->inUse ){ pik_error(p, &pMac->macroName, "recursive macro definition"); break; } token.n = (short int)(sz & 0xffff); if( p->nCtx>=count(p->aCtx) ){ pik_error(p, &token, "macros nested too deep"); break; } pMac->inUse = 1; memset(args, 0, sizeof(args)); p->aCtx[p->nCtx++] = token; sz += pik_parse_macro_args(p, pIn->z+j, pIn->n-j, args, aParam); pik_tokenize(p, &pMac->macroBody, pParser, args); p->nCtx--; pMac->inUse = 0; }else{ #if 0 printf("******** Token %s (%d): \"%.*s\" **************\n", yyTokenName[token.eType], token.eType, (int)(isspace(token.z[0]) ? 0 : sz), token.z); #endif token.n = (unsigned short)(sz & 0xffff); if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){ pik_error(p, &token, "script is too complex"); break; } pik_parser(pParser, token.eType, token); } } } /* ** Parse the PIKCHR script contained in zText[]. Return a rendering. Or ** if an error is encountered, return the error text. The error message ** is HTML formatted. So regardless of what happens, the return text ** is safe to be insertd into an HTML output stream. ** ** If pnWidth and pnHeight are not NULL, then this routine writes the ** width and height of the <SVG> object into the integers that they ** point to. A value of -1 is written if an error is seen. ** ** If zClass is not NULL, then it is a class name to be included in ** the <SVG> markup. ** ** The returned string is contained in memory obtained from malloc() ** and should be released by the caller. */ char *pikchr( const char *zText, /* Input PIKCHR source text. zero-terminated */ const char *zClass, /* Add class="%s" to <svg> markup */ unsigned int mFlags, /* Flags used to influence rendering behavior */ int *pnWidth, /* Write width of <svg> here, if not NULL */ int *pnHeight /* Write height here, if not NULL */ ){ Pik s; yyParser sParse; memset(&s, 0, sizeof(s)); s.sIn.z = zText; s.sIn.n = (unsigned int)strlen(zText); s.eDir = DIR_RIGHT; s.zClass = zClass; s.mFlags = mFlags; pik_parserInit(&sParse, &s); #if 0 pik_parserTrace(stdout, "parser: "); #endif pik_tokenize(&s, &s.sIn, &sParse, 0); if( s.nErr==0 ){ PToken token; memset(&token,0,sizeof(token)); token.z = zText + (s.sIn.n>0 ? s.sIn.n-1 : 0); token.n = 1; pik_parser(&sParse, 0, token); } pik_parserFinalize(&sParse); if( s.zOut==0 && s.nErr==0 ){ pik_append(&s, "<!-- empty pikchr diagram -->\n", -1); } while( s.pVar ){ PVar *pNext = s.pVar->pNext; free(s.pVar); s.pVar = pNext; } while( s.pMacros ){ PMacro *pNext = s.pMacros->pNext; free(s.pMacros); s.pMacros = pNext; } if( pnWidth ) *pnWidth = s.nErr ? -1 : s.wSVG; if( pnHeight ) *pnHeight = s.nErr ? -1 : s.hSVG; if( s.zOut ){ s.zOut[s.nOut] = 0; s.zOut = realloc(s.zOut, s.nOut+1); } return s.zOut; } #if defined(PIKCHR_FUZZ) #include <stdint.h> int LLVMFuzzerTestOneInput(const uint8_t *aData, size_t nByte){ int w,h; char *zIn, *zOut; unsigned int mFlags = nByte & 3; zIn = malloc( nByte + 1 ); if( zIn==0 ) return 0; memcpy(zIn, aData, nByte); zIn[nByte] = 0; zOut = pikchr(zIn, "pikchr", mFlags, &w, &h); free(zIn); free(zOut); return 0; } #endif /* PIKCHR_FUZZ */ #if defined(PIKCHR_SHELL) /* Print a usage comment for the shell and exit. */ static void usage(const char *argv0){ fprintf(stderr, "usage: %s [OPTIONS] FILE ...\n", argv0); fprintf(stderr, "Convert Pikchr input files into SVG. Filename \"-\" means stdin.\n" "All output goes to stdout.\n" "Options:\n" " --dont-stop Process all files even if earlier files have errors\n" " --svg-only Emit raw SVG without the HTML wrapper\n" ); exit(1); } /* Send text to standard output, but escape HTML markup */ static void print_escape_html(const char *z){ int j; char c; while( z[0]!=0 ){ for(j=0; (c = z[j])!=0 && c!='<' && c!='>' && c!='&'; j++){} if( j ) printf("%.*s", j, z); z += j+1; j = -1; if( c=='<' ){ printf("<"); }else if( c=='>' ){ printf(">"); }else if( c=='&' ){ printf("&"); }else if( c==0 ){ break; } } } /* Read the content of file zFilename into memory obtained from malloc() ** Return the memory. If something goes wrong (ex: the file does not exist ** or cannot be opened) put an error message on stderr and return NULL. ** ** If the filename is "-" read stdin. */ static char *readFile(const char *zFilename){ FILE *in; size_t n; size_t nUsed = 0; size_t nAlloc = 0; char *z = 0, *zNew = 0; in = strcmp(zFilename,"-")==0 ? stdin : fopen(zFilename, "rb"); if( in==0 ){ fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename); return 0; } while(1){ if( nUsed+2>=nAlloc ){ nAlloc = nAlloc*2 + 4000; zNew = realloc(z, nAlloc); } if( zNew==0 ){ free(z); fprintf(stderr, "out of memory trying to allocate %lld bytes\n", (long long int)nAlloc); exit(1); } z = zNew; n = fread(z+nUsed, 1, nAlloc-nUsed-1, in); if( n<=0 ){ break; } nUsed += n; } if( in!=stdin ) fclose(in); z[nUsed] = 0; return z; } /* Testing interface ** ** Generate HTML on standard output that displays both the original ** input text and the rendered SVG for all files named on the command ** line. */ int main(int argc, char **argv){ int i; int bSvgOnly = 0; /* Output SVG only. No HTML wrapper */ int bDontStop = 0; /* Continue in spite of errors */ int exitCode = 0; /* What to return */ int mFlags = 0; /* mFlags argument to pikchr() */ const char *zStyle = ""; /* Extra styling */ const char *zHtmlHdr = "<!DOCTYPE html>\n" "<html lang=\"en-US\">\n" "<head>\n<title>PIKCHR Test</title>\n" "<style>\n" " .hidden {\n" " position: absolute !important;\n" " opacity: 0 !important;\n" " pointer-events: none !important;\n" " display: none !important;\n" " }\n" "</style>\n" "<script>\n" " function toggleHidden(id){\n" " for(var c of document.getElementById(id).children){\n" " c.classList.toggle('hidden');\n" " }\n" " }\n" "</script>\n" "<meta charset=\"utf-8\">\n" "</head>\n" "<body>\n" ; if( argc<2 ) usage(argv[0]); for(i=1; i<argc; i++){ char *zIn; char *zOut; int w, h; if( argv[i][0]=='-' && argv[i][1]!=0 ){ char *z = argv[i]; z++; if( z[0]=='-' ) z++; if( strcmp(z,"dont-stop")==0 ){ bDontStop = 1; }else if( strcmp(z,"dark-mode")==0 ){ zStyle = "color:white;background-color:black;"; mFlags |= PIKCHR_DARK_MODE; }else if( strcmp(z,"svg-only")==0 ){ if( zHtmlHdr==0 ){ fprintf(stderr, "the \"%s\" option must come first\n",argv[i]); exit(1); } bSvgOnly = 1; mFlags |= PIKCHR_PLAINTEXT_ERRORS; }else { fprintf(stderr,"unknown option: \"%s\"\n", argv[i]); usage(argv[0]); } continue; } zIn = readFile(argv[i]); if( zIn==0 ) continue; zOut = pikchr(zIn, "pikchr", mFlags, &w, &h); if( w<0 ) exitCode = 1; if( zOut==0 ){ fprintf(stderr, "pikchr() returns NULL. Out of memory?\n"); if( !bDontStop ) exit(1); }else if( bSvgOnly ){ printf("%s\n", zOut); }else{ if( zHtmlHdr ){ printf("%s", zHtmlHdr); zHtmlHdr = 0; } printf("<h1>File %s</h1>\n", argv[i]); if( w<0 ){ printf("<p>ERROR</p>\n%s\n", zOut); }else{ printf("<div id=\"svg-%d\" onclick=\"toggleHidden('svg-%d')\">\n",i,i); printf("<div style='border:3px solid lightgray;max-width:%dpx;%s'>\n", w,zStyle); printf("%s</div>\n", zOut); printf("<pre class='hidden'>"); print_escape_html(zIn); printf("</pre>\n</div>\n"); } } free(zOut); free(zIn); } if( !bSvgOnly ){ printf("</body></html>\n"); } return exitCode ? EXIT_FAILURE : EXIT_SUCCESS; } #endif /* PIKCHR_SHELL */ #ifdef PIKCHR_TCL #include <tcl.h> /* ** An interface to TCL ** ** TCL command: pikchr $INPUTTEXT ** ** Returns a list of 3 elements which are the output text, the width, and ** the height. ** ** Register the "pikchr" command by invoking Pikchr_Init(Tcl_Interp*). Or ** compile this source file as a shared library and load it using the ** "load" command of Tcl. ** ** Compile this source-code file into a shared library using a command ** similar to this: ** ** gcc -c pikchr.so -DPIKCHR_TCL -shared pikchr.c */ static int pik_tcl_command( ClientData clientData, /* Not Used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ int w, h; /* Width and height of the pikchr */ const char *zIn; /* Source text input */ char *zOut; /* SVG output text */ Tcl_Obj *pRes; /* The result TCL object */ (void)clientData; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "PIKCHR_SOURCE_TEXT"); return TCL_ERROR; } zIn = Tcl_GetString(objv[1]); w = h = 0; zOut = pikchr(zIn, "pikchr", 0, &w, &h); if( zOut==0 ){ return TCL_ERROR; /* Out of memory */ } pRes = Tcl_NewObj(); Tcl_ListObjAppendElement(0, pRes, Tcl_NewStringObj(zOut, -1)); free(zOut); Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(w)); Tcl_ListObjAppendElement(0, pRes, Tcl_NewIntObj(h)); Tcl_SetObjResult(interp, pRes); return TCL_OK; } #ifndef PACKAGE_NAME # define PACKAGE_NAME "pikchr" #endif #ifndef PACKAGE_VERSION # define PACKAGE_VERSION "1.0" #endif /* Invoke this routine to register the "pikchr" command with the interpreter ** given in the argument */ int Pikchr_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "pikchr", pik_tcl_command, 0, 0); Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION); return TCL_OK; } #endif /* PIKCHR_TCL */ #line 8270 "pikchr.c" |
Added extsrc/pikchr.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | var initPikchrModule = (() => { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; return ( function(moduleArg = {}) { var Module = moduleArg; var readyPromiseResolve, readyPromiseReject; Module["ready"] = new Promise((resolve, reject) => { readyPromiseResolve = resolve; readyPromiseReject = reject; }); var moduleOverrides = Object.assign({}, Module); var arguments_ = []; var thisProgram = "./this.program"; var quit_ = (status, toThrow) => { throw toThrow; }; var ENVIRONMENT_IS_WEB = true; var ENVIRONMENT_IS_WORKER = false; var scriptDirectory = ""; function locateFile(path) { if (Module["locateFile"]) { return Module["locateFile"](path, scriptDirectory); } return scriptDirectory + path; } var read_, readAsync, readBinary; if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = self.location.href; } else if (typeof document != "undefined" && document.currentScript) { scriptDirectory = document.currentScript.src; } if (_scriptDir) { scriptDirectory = _scriptDir; } if (scriptDirectory.startsWith("blob:")) { scriptDirectory = ""; } else { scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1); } { read_ = url => { var xhr = new XMLHttpRequest; xhr.open("GET", url, false); xhr.send(null); return xhr.responseText; }; if (ENVIRONMENT_IS_WORKER) { readBinary = url => { var xhr = new XMLHttpRequest; xhr.open("GET", url, false); xhr.responseType = "arraybuffer"; xhr.send(null); return new Uint8Array(/** @type{!ArrayBuffer} */ (xhr.response)); }; } readAsync = (url, onload, onerror) => { var xhr = new XMLHttpRequest; xhr.open("GET", url, true); xhr.responseType = "arraybuffer"; xhr.onload = () => { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { onload(xhr.response); return; } onerror(); }; xhr.onerror = onerror; xhr.send(null); }; } } else {} var out = Module["print"] || console.log.bind(console); var err = Module["printErr"] || console.error.bind(console); Object.assign(Module, moduleOverrides); moduleOverrides = null; if (Module["arguments"]) arguments_ = Module["arguments"]; if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; if (Module["quit"]) quit_ = Module["quit"]; var wasmBinary; if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; if (typeof WebAssembly != "object") { abort("no native wasm support detected"); } var wasmMemory; var ABORT = false; var EXITSTATUS; var /** @type {!Int8Array} */ HEAP8, /** @type {!Uint8Array} */ HEAPU8, /** @type {!Int16Array} */ HEAP16, /** @type {!Uint16Array} */ HEAPU16, /** @type {!Int32Array} */ HEAP32, /** @type {!Uint32Array} */ HEAPU32, /** @type {!Float32Array} */ HEAPF32, /** @type {!Float64Array} */ HEAPF64; function updateMemoryViews() { var b = wasmMemory.buffer; Module["HEAP8"] = HEAP8 = new Int8Array(b); Module["HEAP16"] = HEAP16 = new Int16Array(b); Module["HEAPU8"] = HEAPU8 = new Uint8Array(b); Module["HEAPU16"] = HEAPU16 = new Uint16Array(b); Module["HEAP32"] = HEAP32 = new Int32Array(b); Module["HEAPU32"] = HEAPU32 = new Uint32Array(b); Module["HEAPF32"] = HEAPF32 = new Float32Array(b); Module["HEAPF64"] = HEAPF64 = new Float64Array(b); } var __ATPRERUN__ = []; var __ATINIT__ = []; var __ATPOSTRUN__ = []; var runtimeInitialized = false; function preRun() { if (Module["preRun"]) { if (typeof Module["preRun"] == "function") Module["preRun"] = [ Module["preRun"] ]; while (Module["preRun"].length) { addOnPreRun(Module["preRun"].shift()); } } callRuntimeCallbacks(__ATPRERUN__); } function initRuntime() { runtimeInitialized = true; callRuntimeCallbacks(__ATINIT__); } function postRun() { if (Module["postRun"]) { if (typeof Module["postRun"] == "function") Module["postRun"] = [ Module["postRun"] ]; while (Module["postRun"].length) { addOnPostRun(Module["postRun"].shift()); } } callRuntimeCallbacks(__ATPOSTRUN__); } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb); } function addOnInit(cb) { __ATINIT__.unshift(cb); } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb); } var runDependencies = 0; var runDependencyWatcher = null; var dependenciesFulfilled = null; function addRunDependency(id) { runDependencies++; Module["monitorRunDependencies"]?.(runDependencies); } function removeRunDependency(id) { runDependencies--; Module["monitorRunDependencies"]?.(runDependencies); if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher); runDependencyWatcher = null; } if (dependenciesFulfilled) { var callback = dependenciesFulfilled; dependenciesFulfilled = null; callback(); } } } /** @param {string|number=} what */ function abort(what) { Module["onAbort"]?.(what); what = "Aborted(" + what + ")"; err(what); ABORT = true; EXITSTATUS = 1; what += ". Build with -sASSERTIONS for more info."; /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what); readyPromiseReject(e); throw e; } var dataURIPrefix = "data:application/octet-stream;base64,"; /** * Indicates whether filename is a base64 data URI. * @noinline */ var isDataURI = filename => filename.startsWith(dataURIPrefix); var wasmBinaryFile; wasmBinaryFile = "pikchr.wasm"; if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile); } function getBinarySync(file) { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary); } if (readBinary) { return readBinary(file); } throw "both async and sync fetching of the wasm failed"; } function getBinaryPromise(binaryFile) { if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == "function") { return fetch(binaryFile, { credentials: "same-origin" }).then(response => { if (!response["ok"]) { throw `failed to load wasm binary file at '${binaryFile}'`; } return response["arrayBuffer"](); }).catch(() => getBinarySync(binaryFile)); } } return Promise.resolve().then(() => getBinarySync(binaryFile)); } function instantiateArrayBuffer(binaryFile, imports, receiver) { return getBinaryPromise(binaryFile).then(binary => WebAssembly.instantiate(binary, imports)).then(instance => instance).then(receiver, reason => { err(`failed to asynchronously prepare wasm: ${reason}`); abort(reason); }); } function instantiateAsync(binary, binaryFile, imports, callback) { if (!binary && typeof WebAssembly.instantiateStreaming == "function" && !isDataURI(binaryFile) && typeof fetch == "function") { return fetch(binaryFile, { credentials: "same-origin" }).then(response => { /** @suppress {checkTypes} */ var result = WebAssembly.instantiateStreaming(response, imports); return result.then(callback, function(reason) { err(`wasm streaming compile failed: ${reason}`); err("falling back to ArrayBuffer instantiation"); return instantiateArrayBuffer(binaryFile, imports, callback); }); }); } return instantiateArrayBuffer(binaryFile, imports, callback); } function createWasm() { var info = { "a": wasmImports }; /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) { wasmExports = instance.exports; wasmMemory = wasmExports["d"]; updateMemoryViews(); addOnInit(wasmExports["e"]); removeRunDependency("wasm-instantiate"); return wasmExports; } addRunDependency("wasm-instantiate"); function receiveInstantiationResult(result) { receiveInstance(result["instance"]); } if (Module["instantiateWasm"]) { try { return Module["instantiateWasm"](info, receiveInstance); } catch (e) { err(`Module.instantiateWasm callback failed with error: ${e}`); readyPromiseReject(e); } } instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult).catch(readyPromiseReject); return {}; } /** @constructor */ function ExitStatus(status) { this.name = "ExitStatus"; this.message = `Program terminated with exit(${status})`; this.status = status; } var callRuntimeCallbacks = callbacks => { while (callbacks.length > 0) { callbacks.shift()(Module); } }; /** * @param {number} ptr * @param {string} type */ function getValue(ptr, type = "i8") { if (type.endsWith("*")) type = "*"; switch (type) { case "i1": return HEAP8[((ptr) >> 0)]; case "i8": return HEAP8[((ptr) >> 0)]; case "i16": return HEAP16[((ptr) >> 1)]; case "i32": return HEAP32[((ptr) >> 2)]; case "i64": abort("to do getValue(i64) use WASM_BIGINT"); case "float": return HEAPF32[((ptr) >> 2)]; case "double": return HEAPF64[((ptr) >> 3)]; case "*": return HEAPU32[((ptr) >> 2)]; default: abort(`invalid type for getValue: ${type}`); } } var noExitRuntime = Module["noExitRuntime"] || true; /** * @param {number} ptr * @param {number} value * @param {string} type */ function setValue(ptr, value, type = "i8") { if (type.endsWith("*")) type = "*"; switch (type) { case "i1": HEAP8[((ptr) >> 0)] = value; break; case "i8": HEAP8[((ptr) >> 0)] = value; break; case "i16": HEAP16[((ptr) >> 1)] = value; break; case "i32": HEAP32[((ptr) >> 2)] = value; break; case "i64": abort("to do setValue(i64) use WASM_BIGINT"); case "float": HEAPF32[((ptr) >> 2)] = value; break; case "double": HEAPF64[((ptr) >> 3)] = value; break; case "*": HEAPU32[((ptr) >> 2)] = value; break; default: abort(`invalid type for setValue: ${type}`); } } var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder("utf8") : undefined; /** * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given * array that contains uint8 values, returns a copy of that string as a * Javascript String object. * heapOrArray is either a regular array, or a JavaScript typed array view. * @param {number} idx * @param {number=} maxBytesToRead * @return {string} */ var UTF8ArrayToString = (heapOrArray, idx, maxBytesToRead) => { var endIdx = idx + maxBytesToRead; var endPtr = idx; while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); } var str = ""; while (idx < endPtr) { var u0 = heapOrArray[idx++]; if (!(u0 & 128)) { str += String.fromCharCode(u0); continue; } var u1 = heapOrArray[idx++] & 63; if ((u0 & 224) == 192) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } var u2 = heapOrArray[idx++] & 63; if ((u0 & 240) == 224) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; } else { u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); } if (u0 < 65536) { str += String.fromCharCode(u0); } else { var ch = u0 - 65536; str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)); } } return str; }; /** * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the * emscripten HEAP, returns a copy of that string as a Javascript String object. * * @param {number} ptr * @param {number=} maxBytesToRead - An optional length that specifies the * maximum number of bytes to read. You can omit this parameter to scan the * string until the first 0 byte. If maxBytesToRead is passed, and the string * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the * string will cut short at that byte index (i.e. maxBytesToRead will not * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing * frequent uses of UTF8ToString() with and without maxBytesToRead may throw * JS JIT optimizations off, so it is worth to consider consistently using one * @return {string} */ var UTF8ToString = (ptr, maxBytesToRead) => ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""; var ___assert_fail = (condition, filename, line, func) => { abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [ filename ? UTF8ToString(filename) : "unknown filename", line, func ? UTF8ToString(func) : "unknown function" ]); }; var abortOnCannotGrowMemory = requestedSize => { abort("OOM"); }; var _emscripten_resize_heap = requestedSize => { var oldSize = HEAPU8.length; requestedSize >>>= 0; abortOnCannotGrowMemory(requestedSize); }; var runtimeKeepaliveCounter = 0; var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0; var _proc_exit = code => { EXITSTATUS = code; if (!keepRuntimeAlive()) { Module["onExit"]?.(code); ABORT = true; } quit_(code, new ExitStatus(code)); }; /** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => { EXITSTATUS = status; _proc_exit(status); }; var _exit = exitJS; var getCFunc = ident => { var func = Module["_" + ident]; return func; }; var writeArrayToMemory = (array, buffer) => { HEAP8.set(array, buffer); }; var lengthBytesUTF8 = str => { var len = 0; for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i); if (c <= 127) { len++; } else if (c <= 2047) { len += 2; } else if (c >= 55296 && c <= 57343) { len += 4; ++i; } else { len += 3; } } return len; }; var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { if (!(maxBytesToWrite > 0)) return 0; var startIdx = outIdx; var endIdx = outIdx + maxBytesToWrite - 1; for (var i = 0; i < str.length; ++i) { var u = str.charCodeAt(i); if (u >= 55296 && u <= 57343) { var u1 = str.charCodeAt(++i); u = 65536 + ((u & 1023) << 10) | (u1 & 1023); } if (u <= 127) { if (outIdx >= endIdx) break; heap[outIdx++] = u; } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break; heap[outIdx++] = 192 | (u >> 6); heap[outIdx++] = 128 | (u & 63); } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break; heap[outIdx++] = 224 | (u >> 12); heap[outIdx++] = 128 | ((u >> 6) & 63); heap[outIdx++] = 128 | (u & 63); } else { if (outIdx + 3 >= endIdx) break; heap[outIdx++] = 240 | (u >> 18); heap[outIdx++] = 128 | ((u >> 12) & 63); heap[outIdx++] = 128 | ((u >> 6) & 63); heap[outIdx++] = 128 | (u & 63); } } heap[outIdx] = 0; return outIdx - startIdx; }; var stringToUTF8 = (str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); var stringToUTF8OnStack = str => { var size = lengthBytesUTF8(str) + 1; var ret = stackAlloc(size); stringToUTF8(str, ret, size); return ret; }; /** * @param {string|null=} returnType * @param {Array=} argTypes * @param {Arguments|Array=} args * @param {Object=} opts */ var ccall = (ident, returnType, argTypes, args, opts) => { var toC = { "string": str => { var ret = 0; if (str !== null && str !== undefined && str !== 0) { ret = stringToUTF8OnStack(str); } return ret; }, "array": arr => { var ret = stackAlloc(arr.length); writeArrayToMemory(arr, ret); return ret; } }; function convertReturnValue(ret) { if (returnType === "string") { return UTF8ToString(ret); } if (returnType === "boolean") return Boolean(ret); return ret; } var func = getCFunc(ident); var cArgs = []; var stack = 0; if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]]; if (converter) { if (stack === 0) stack = stackSave(); cArgs[i] = converter(args[i]); } else { cArgs[i] = args[i]; } } } var ret = func.apply(null, cArgs); function onDone(ret) { if (stack !== 0) stackRestore(stack); return convertReturnValue(ret); } ret = onDone(ret); return ret; }; /** * @param {string=} returnType * @param {Array=} argTypes * @param {Object=} opts */ var cwrap = (ident, returnType, argTypes, opts) => { var numericArgs = !argTypes || argTypes.every(type => type === "number" || type === "boolean"); var numericRet = returnType !== "string"; if (numericRet && numericArgs && !opts) { return getCFunc(ident); } return function() { return ccall(ident, returnType, argTypes, arguments, opts); }; }; var wasmImports = { /** @export */ a: ___assert_fail, /** @export */ b: _emscripten_resize_heap, /** @export */ c: _exit }; var wasmExports = createWasm(); var ___wasm_call_ctors = () => (___wasm_call_ctors = wasmExports["e"])(); var _pikchr = Module["_pikchr"] = (a0, a1, a2, a3, a4) => (_pikchr = Module["_pikchr"] = wasmExports["f"])(a0, a1, a2, a3, a4); var stackSave = () => (stackSave = wasmExports["h"])(); var stackRestore = a0 => (stackRestore = wasmExports["i"])(a0); var stackAlloc = a0 => (stackAlloc = wasmExports["j"])(a0); Module["stackAlloc"] = stackAlloc; Module["stackSave"] = stackSave; Module["stackRestore"] = stackRestore; Module["cwrap"] = cwrap; Module["setValue"] = setValue; Module["getValue"] = getValue; var calledRun; dependenciesFulfilled = function runCaller() { if (!calledRun) run(); if (!calledRun) dependenciesFulfilled = runCaller; }; function run() { if (runDependencies > 0) { return; } preRun(); if (runDependencies > 0) { return; } function doRun() { if (calledRun) return; calledRun = true; Module["calledRun"] = true; if (ABORT) return; initRuntime(); readyPromiseResolve(Module); if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"](); postRun(); } if (Module["setStatus"]) { Module["setStatus"]("Running..."); setTimeout(function() { setTimeout(function() { Module["setStatus"](""); }, 1); doRun(); }, 1); } else { doRun(); } } if (Module["preInit"]) { if (typeof Module["preInit"] == "function") Module["preInit"] = [ Module["preInit"] ]; while (Module["preInit"].length > 0) { Module["preInit"].pop()(); } } run(); return moduleArg.ready } ); })(); if (typeof exports === 'object' && typeof module === 'object') module.exports = initPikchrModule; else if (typeof define === 'function' && define['amd']) define([], () => initPikchrModule); |
Added extsrc/pikchr.wasm.
cannot compute difference between binary files
Added extsrc/shell.c.
more than 10,000 changes
Added extsrc/sqlite3.c.
more than 10,000 changes
Added extsrc/sqlite3.h.
more than 10,000 changes
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 101 102 | .TH FOSSIL "1" "July 2021" "https://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 forum, wiki, ticket tracker, CGI/HTTP interface, and HTTP server. .SH Common COMMANDs: add cat diff ls revert timeline .br addremove changes extras merge rm ui .br all chat finfo mv settings undo .br amend clean gdiff open sql unversioned .br annotate clone grep pull stash update .br bisect commit help push status version .br blame dbstat info rebuild sync .br branch delete init remote tag .br .SH FEATURES Features as described on the fossil home page. .HP 1. .B Integrated Bug Tracking, Wiki, Forum, and Technotes - In addition to doing distributed version control like Git and Mercurial, Fossil also supports bug tracking, wiki, forum, 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 or over the exceedingly slow Wifi on airliners. .HP 5. .B CGI/SCGI Enabled - No server is required, but if you want to set one up, Fossil supports four easy 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. .HP 8. .B Free and Open-Source - Uses the 2-clause BSD license. .SH DOCUMENTATION https://fossil-scm.org/ .br .B fossil \fIui\fR |
Deleted fossil.nsi.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted kktodo.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted rse-notes.txt.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
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://fossil-scm.org/ AppSupportURL=https://fossil-scm.org/ AppUpdatesURL=https://fossil-scm.org/ AppVerName=Fossil v{#AppVersion} AppVersion={#AppVersion} AppComments=Simple, high-reliability, distributed software configuration management system. AppReadmeFile=https://fossil-scm.org/home/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 46 47 48 49 50 51 52 53 | Built-in Skins ============== Each subdirectory under this folder describes a built-in "skin". There are five key files in each subdirectory: * `css.txt` → The CSS for the skin * `details.txt` → Skin-specific settings * `footer.txt` → Text of the Content Footer for each page * `header.txt` → Text of the Content Header for each page * `js.txt` → Javascript included in the Content Footer 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, skins/newskin/header.txt, and skins/newskin/js.txt. Be sure to "fossil add" these files. 3. Go to the tools/ 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. See the [custom skin documentation](/doc/$CURRENT/www/customskin.md) for more information. Development Hints ----------------- One way to develop a new skin is to copy the baseline files (css.txt, details.txt, footer.txt, header.txt, and js.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/*.txt files using your favorite text editor, then press Reload on your browser to see immediate results. |
Added skins/ardoise/README.md.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ## Ardoise theme A black and grey skin ("Ardoise" is the french word for slate). The skin includes custom icons for the file browser and the WYSIWYG editor, which are embedded directly in the css as base64 blobs. For convenience, they are also provided as standalone files in the images subdirectory. This skin was contributed by Antoine Chavasse. This theme is loosely based upon, and still contains some elements from the Blitz theme by James Moger. This theme embeds & uses a modified copy of [Normalize 3.0.2](https://necolas.github.io/normalize.css/) which is distributed under an [MIT license](https://github.com/necolas/normalize.css/blob/master/LICENSE.md). This theme embeds & uses a modified copy of [Skeleton](http://getskeleton.com) which is distributed under an [MIT license](https://github.com/dhg/Skeleton/blob/master/LICENSE.md). The sass version of Skeleton used in this project was made by [Seth Coelen](https://github.com/whatsnewsaes). |
Added skins/ardoise/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 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 | @charset "UTF-8"; /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ hr, input[type=search] { box-sizing: content-box } img, legend, table.login_out, table.login_out td, tr.timelineCurrent, tr.timelineCurrent td.timelineTableCell, tr.timelineSelected { background-color: initial; border: 0 } ol, p, ul { margin-top: 0 } article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, pre > code, section, summary { display: block } ul.browser li.dir, ul.browser li.file { background-position: 0 center; padding-left: 22px; padding-top: 2px } .container, .filetree a, .filetree li, .filetree ul ul, .mainmenu ul, sub, sup { position: relative } .filetree .dir > div.filetreeline > a, ul.browser li.dir > a { background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDUuMjkyIDQuMjMzIj48cGF0aCBkPSJNLjc5NC41M3YzLjE3NGgzLjcwNFYxLjMyM0gyLjkxVi41Mjl6IiBmaWxsPSIjMWQyMDIxIiBzdHJva2U9IiNmZjgwMDAiIHN0cm9rZS13aWR0aD0iLjUyOSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PC9zdmc+) } dfn, span.modpending { font-style: italic } html { font-family: sans-serif; } audio, canvas, progress, video { display: inline-block; vertical-align: baseline } audio:not([controls]) { display: none; height: 0 } .filetree li.last>ul:before, .filetree ul.collapsed, [hidden], template { display: none } a { background-color: transparent; color: #ff8000; text-decoration: unset } a:active, a:hover, abbr[title] { border-bottom: 1px dotted } b, optgroup, strong, td.usetupEditLabel { font-weight: 700 } mark { background: #ff0 } small { font-size: 80% } sub, sup { font-size: 75%; line-height: 0; vertical-align: baseline } sup { top: -.5em } sub { bottom: -.25em } svg:not(:root) { overflow: hidden } figure { margin: 1em 40px } hr { height: 0; margin-top: 3rem; margin-bottom: 3.5rem; border-width: 0; border-top: 1px solid #626262 } pre { overflow: auto } code, kbd, pre, samp { font-family: monospace,monospace; font-size: 1em } button, input, optgroup, select, textarea { color: inherit; font: inherit; margin: 0 } body, h5 { line-height: 1.5 } button { overflow: visible } button, select { text-transform: none } button, html input[type=button], input[type=reset], input[type=submit] { cursor: pointer } button[disabled], html input[disabled] { cursor: default } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0 } input { line-height: normal } input[type=checkbox], input[type=radio] { box-sizing: border-box; padding: 0; display: inline } input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button { height: auto } input[type=search]::-webkit-search-cancel-button, fieldset { border: 1px solid silver; margin: 0 2px } legend { padding: 0 } table { border-spacing: 0; width: 100% } html { font-size: 62.5% } body { margin: 0; font-size: 1.4em; font-weight: 400; font-family: HelveticaNeue,"Helvetica Neue",Helvetica,Arial,sans-serif; color: #ddd; background-color: #303536 } a:hover { color: #e67300 } .full-width, .u-full-width { width: 100%; box-sizing: border-box } .max-full-width, .u-max-full-width { max-width: 100%; box-sizing: border-box } .pull-right, .u-pull-right { float: right } .pull-left, .u-pull-left { float: left } h1, h2, h3, h4, h5, h6 { margin: 1rem 0; font-weight: 700 } h1 { font-size: 3rem; 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: 2rem; line-height: 1.35 } h5 { font-size: 1.6rem } h6 { font-size: 1.4rem; line-height: 1.6 } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { font-size: .75em; font-weight: 400; color: #ccc } p { display: flow-root } .container { width: 100%; max-width: 1200px; margin: 0 auto; box-sizing: border-box } .column, .columns { width: 100%; /*float: left; can break README.md in /dir view*/ box-sizing: border-box } @media (min-width:400px) { .container { width: 95%; padding: 0 } } .button, button, input[type=button], input[type=reset], input[type=submit] { padding: 0 30px; font-size: 11px; line-height: 32px; letter-spacing: .1rem; text-transform: uppercase; height: 32px; font-weight: 600; display: inline-block; box-sizing: border-box; text-decoration: none; text-align: center; white-space: nowrap; cursor: pointer } input[type=submit]:disabled { color: rgb(70,70,70); background-color: rgb(153,153,153); } @media (min-width:550px) { .container { width: 95% } .column, .columns { margin-left: 4% } .column:first-child, .columns:first-child { margin-left: 0 } .one.column, .one.columns { width: 4.66667% } .two.columns { width: 13.33333% } .three.columns { width: 22% } .four.columns, .one-third.column { width: 30.66667% } .five.columns { width: 39.33333% } .one-half.column, .six.columns { width: 48% } .seven.columns { width: 56.66667% } .eight.columns, .two-thirds.column { width: 65.33333% } .nine.columns { width: 74% } .ten.columns { width: 82.66667% } .eleven.columns { width: 91.33333% } .twelve.columns { width: 100%; margin-left: 0 } .offset-by-one.column, .offset-by-one.columns { margin-left: 8.66667% } .offset-by-two.column, .offset-by-two.columns { margin-left: 17.33333% } .offset-by-three.column, .offset-by-three.columns { margin-left: 26% } .offset-by-four.column, .offset-by-four.columns, .offset-by-one-third.column, .offset-by-one-third.columns { margin-left: 34.66667% } .offset-by-five.column, .offset-by-five.columns { margin-left: 43.33333% } .offset-by-one-half.column, .offset-by-six.column, .offset-by-six.columns { margin-left: 52% } .offset-by-seven.column, .offset-by-seven.columns { margin-left: 60.66667% } .offset-by-eight.column, .offset-by-eight.columns, .offset-by-two-thirds.column, .offset-by-two-thirds.columns { margin-left: 69.33333% } .offset-by-nine.column, .offset-by-nine.columns { margin-left: 78% } .offset-by-ten.column, .offset-by-ten.columns { margin-left: 86.66667% } .offset-by-eleven.column, .offset-by-eleven.columns { margin-left: 95.33333% } } .button, button { color: #aaa; background-color: #444; border-radius: 5px; border: 0 } input[type=button], input[type=reset], input[type=submit] { color: #ddd; background-color: #446979; border: 0; border-radius: 5px } .button:hover, button:hover { color: #444; background-color: #aaa; outline: 0 } input[type=button]:hover, input[type=reset]:hover, input[type=submit]:hover { color: #446979; background-color: #ddd; outline: 0 } .button:focus, button:focus, input[type=button]:focus, input[type=reset]:focus, input[type=submit]:focus { color: #333; border-color: #888; outline: 0 } .button.button-primary, .button.button-primary:focus, .button.button-primary:hover, button.button-primary, button.button-primary:focus, button.button-primary:hover, input[type=button].button-primary, input[type=button].button-primary:focus, input[type=button].button-primary:hover, input[type=reset].button-primary, input[type=reset].button-primary:focus, input[type=reset].button-primary:hover, input[type=submit].button-primary, input[type=submit].button-primary:focus, input[type=submit].button-primary:hover { color: #303536; background-color: #ff8000; border-color: #ff8000 } input[type=email], input[type=number], input[type=password], input[type=search], input[type=tel], input[type=text], input[type=url] { box-shadow: none; box-sizing: border-box; } input[type=email], input[type=number], input[type=password], input[type=search], input[type=tel], input[type=text], input[type=url], select, textarea { height: 32px; padding: 6px 10px; color: #bbb; background-color: #303536; border: 0; border-radius: 5px; box-shadow: none; box-sizing: border-box } textarea, select { height: initial; } input[type=email]:hover, input[type=number]:hover, input[type=password]:hover, input[type=search]:hover, input[type=tel]:hover, input[type=text]:hover, input[type=url]:hover, select:hover, textarea:hover { color: #eef8ff; background-color: #555 } textarea { overflow: auto; min-height: 65px; padding-top: 6px; padding-bottom: 6px } input[type=email]:focus, input[type=number]:focus, input[type=password]:focus, input[type=search]:focus, input[type=tel]:focus, input[type=text]:focus, input[type=url]:focus, select:focus, textarea:focus { border: 1px solid #ff8000; outline: 0 } label, legend { margin-bottom: .5rem; font-weight: 600 } fieldset { padding: 0; border-width: 0 } label > .label-body { display: inline-block; margin-left: .5rem; font-weight: 400 } ul { list-style: square } ol { list-style: decimal } ol, ul { padding-left: 3rem } li { margin-bottom: 0 } ol ol, ol ul, ul ol, ul ul { margin: 1rem 0 1rem 2rem } code { padding: .2rem .5rem; margin: 0 .2rem; font-size: 90%; white-space: nowrap; background: #000; border: 2px solid #bbb; border-radius: 5px } table.numbered-lines td.file-content > pre { margin-top: -2px/*offset CODE tag border*/; } table.numbered-lines { font-family: monospace; } table.numbered-lines pre { font-family: inherit; } pre > code { padding: 1rem 1.5rem; white-space: pre } td, th { padding: 1px 5px; text-align: left } td:first-child, th:first-child { padding-left: 0 } .button, button { margin-bottom: 1rem } fieldset, input, select, textarea { margin-bottom: .5rem } blockquote, dl, figure, ol, p, pre, table, ul { margin-bottom: 1.5rem } header { color: #888; font-weight: 400; padding-top: 10px; border-width: 0 } .filetree li > ul:before, .filetree li li:before { border-left: 2px solid #888; content: ''; position: absolute } .filetree>ul, header .logo, header .logo h1 { display: inline-block } header .login { padding-top: 2px; text-align: right } header .login .button { margin: 0 } header h1 { margin: 0; color: #888; display: inline-block } header .title h1 { padding-bottom: 10px } header .login, header h1 small, header h2 small { color: #777 } .middle { background-color: #1d2021; padding-bottom: 20px; max-width: 100%; box-sizing: border-box } .content { padding-top: 8px; padding-left: 8px; padding-right: 8px } #hbdrop a, .content a { color: #8cf } #hbdrop a:hover, .content a:hover, .submenu a:hover, .submenu label:hover { color: #fff } .artifact_content hr:first-of-type { margin: 0; border: 0 } .artifact_content blockquote:first-of-type { padding: 1px 20px; margin: 0 0 20px; background: #000; border-radius: 5px } footer { padding: 10px 0 60px; border-top: 0; color: #888 } footer a { color: #527b8f; background-repeat: no-repeat; background-position: center top 10px } footer a:hover { color: #eef8ff } .mainmenu { background-color: #161819; border-top-right-radius: 15px; border-top-left-radius: 15px; clear: both z-index: 21; } .mainmenu ul { list-style: none; border-top: 1px solid transparent; padding: 0 } .mainmenu li { outline: 0; float: left; margin: 0 } .mainmenu li.active { background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNyIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgNC40OTggMi4zODEiPjxwYXRoIGQ9Ik00LjIzMyAyLjM4MUguMjY1bC45OTgtMS4wNTguOTg2LTEuMDU4Ljk5OCAxLjA2MnoiIGZpbGw9IiNmZjgwMDAiLz48L3N2Zz4=); background-repeat: no-repeat; background-position: center bottom } .mainmenu li a { color: #66a8c7; padding: 10px 15px } .mainmenu li.active a { text-shadow: 0 0 1px #b1d2e2 } .mainmenu li:hover { background-color: #ff8000; border-radius: 5px } .mainmenu li:hover a { color: #000 } nav#hbdrop { background-color: #161819; border-radius: 15px; display: none; width: 100%; position: absolute; z-index: 20; } .submenu { padding: 4px 0; background-color: #000; border-bottom-right-radius: 15px; border-bottom-left-radius: 15px; line-height: 2.5 } .section, .sortable thead, .userTable thead { background-color: #404040 } .submenu input, .submenu select { margin: 0 0 0 5px } .submenu a, .submenu label { display: inline; font-weight: 400; color: #5e9ab6; padding: 25px 15px; text-decoration: none; border-radius: 5px } .section { font-weight: 700; padding: 9px 10px 10px; margin: 10px 0; border-radius: 5px } .sectionmenu { border-top: 0; margin-top: -10px; margin-bottom: 10px; padding: 5px; text-align: center; background: #000; border-bottom-right-radius: 15px; border-bottom-left-radius: 15px } .sectionmenu a { display: inline-block; margin-top: 5px; margin-right: 1em } ul.browser { list-style: none; line-height: 1.6 } ul.browser li.dir { background-repeat: no-repeat } .filetree a, ul.browser li.file > a{ background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDUuMjkyIDQuMjMzIj48cGF0aCBkPSJNMS4zMjMuMjY1djMuNzA0aDIuNjQ2VjEuMzIzTDIuOTEuMjY1eiIgZmlsbD0iIzFkMjAyMSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMi42NDYuMjY1aC4yNjR2MS4zMjNoMS4wNiIgZmlsbD0iIzFkMjAyMSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4=); background-repeat: no-repeat } div.filetreeline:hover *, ul.browser li.dir:hover, ul.browser li.dir:hover *, ul.browser li.file:hover, ul.browser li.file:hover * { background-color: #333 } td.browser, td.tktDescLabel { vertical-align: top } td.tktTlOpen { color: #ffa0a0; } td.tktTlClosed { color: #555; } div.filetreeline { display: table; width: 100%; white-space: nowrap } .filetree { margin: 1em 0; line-height: 1.6 } .filetree ul { margin: 0; padding: 0; list-style: none } .filetree ul ul { margin: 0 0 0 21px } .filetree li { margin: 0; padding: 0 } .filetree li li:before { top: -.8em; left: -14px; width: 16px; height: 1.5em; border-bottom: 2px solid #888 } .filetree li > ul:before { top: -1.5em; bottom: 0; left: -35px } .filetree a { z-index: 1; display: table-cell; min-height: 16px; padding-left: 22px; background-position: center left } div.filetreeage { display: table-cell; padding-left: 10em; text-align: right } .fileage tr:first-child { background-color: #404040!important } .fileage tr:nth-child(odd), .sortable tbody tr:nth-child(even), .userTable tbody tr:nth-child(even) { background-color: #2c2c2c } .fileage tr:nth-child(even):hover, .fileage tr:nth-child(odd):hover, .sortable thead:hover { background-color: #555 } .fileage tr:nth-child(even), .sortable tbody tr:nth-child(odd), .userTable tbody tr:nth-child(odd) { background-color: #181818 } .fileage td, .sortable td, .userTable td { vertical-align: top; text-align: left; padding-top: 3px; border-left: 1px solid #333 } .fileage td:first-child, .sortable td:first-child, .userTable td:first-child { border-left: transparent } table.label-value th { vertical-align: middle } .brlist table td { padding: 5px } .sortable, .userTable { border-color: transparent; width: 75% } td.timelineTime, tr.timelineBottom td { border-bottom: 0 } .sortable tbody tr:nth-child(even):hover, .sortable tbody tr:nth-child(odd):hover, .userTable tbody tr:nth-child(even):hover, .userTable tbody tr:nth-child(odd):hover { background-color: #444 } div.timelineDate { font-weight: 700; white-space: nowrap } td.timelineTime { vertical-align: top; text-align: right; white-space: nowrap; padding-top: .75em } td.timelineGraph { width: 20px; text-align: left; vertical-align: top; border-bottom: 0 } a.timelineHistLink { text-transform: lowercase } span.timelineComment { padding: 0 5px } .report th, span.timelineEllipsis { cursor: pointer } table.timelineTable { border-spacing: 2px 3px } .timelineModernCell, .timelineColumnarCell, .timelineDetailCell, .timelineCompactCell, .timelineVerboseCell { vertical-align: top; text-align: left; padding: .75em; border-radius: 5px; background: #000 } .timelineSelected > .timelineColumnarCell, .timelineSelected > .timelineCompactCell, .timelineSelected > .timelineDetailCell, .timelineSelected > .timelineModernCell, .timelineSelected > .timelineVerboseCell { padding: .75em; border-radius: 5px; border: solid #ff8000; vertical-align: top; text-align: left; background: #442800 } span.timelineSelected { border-radius: 5px; border: solid #ff8000; vertical-align: top; text-align: left; background-color: #442800; } .timelineSelected { box-shadow: none; } .timelineSecondary {} .timelineSecondary > .timelineColumnarCell, .timelineSecondary > .timelineCompactCell, .timelineSecondary > .timelineDetailCell, .timelineSecondary > .timelineModernCell, .timelineSecondary > .timelineVerboseCell { padding: .75em; border-radius: 5px; border: solid #0080ff; vertical-align: top; text-align: left; background: #002844 } span.timelineSecondary { border-radius: 5px; border: solid #0080ff; vertical-align: top; text-align: left; background: #002844 } .timelineCurrent > .timelineColumnarCell, .timelineCurrent > .timelineCompactCell, .timelineCurrent > .timelineDetailCell, .timelineCurrent > .timelineModernCell, .timelineCurrent > .timelineVerboseCell { vertical-align: top; text-align: left; padding: .75em; border-radius: 5px; border: dashed #ff8000 } .timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] { background-color: #000 } .tl-canvas { margin: 0 6px 0 10px } .tl-rail { width: 18px } .tl-mergeoffset { width: 2px } .tl-nodemark { margin-top: .8em } .tl-node { width: 10px; height: 10px; border: 2px solid #bbb; background: #111; cursor: pointer } .tl-node.leaf:after { content: ''; position: absolute; top: 3px; left: 3px; width: 4px; height: 4px; background: #bbb } .tl-node.closed-leaf svg { position: absolute; top: 0px; left: 0px; width: 10px; height: 10px; color: #bbb; } .tl-node.sel:after { content: ''; position: absolute; top: 1px; left: 1px; width: 8px; height: 8px; background: #ff8000 } .tl-arrow { width: 0; height: 0; transform: scale(.999); border: 0 solid transparent } .tl-arrow.u { margin-top: -1px; border-width: 0 3px; border-bottom: 7px solid } .tl-arrow.u.sm { border-bottom: 5px solid #bbb } .tl-line { background: #bbb; width: 2px } .tl-arrow.merge { height: 1px; border-width: 2px 0 } .tl-arrow.merge.l { border-right: 3px solid #bbb } .tl-arrow.merge.r { border-left: 3px solid #bbb } .tl-line.merge { width: 1px } .tl-arrow.cherrypick { height: 1px; border-width: 2px 0; } .tl-arrow.cherrypick.l { border-right: 3px solid #bbb; } .tl-arrow.cherrypick.r { border-left: 3px solid #bbb; } .tl-line.cherrypick.h { width: 0px; border-top: 1px dashed #bbb; border-left: 0px dashed #bbb; background: rgba(255,255,255,0); } .tl-line.cherrypick.v { width: 0px; border-top: 0px dashed #bbb; border-left: 1px dashed #bbb; background: rgba(255,255,255,0); } .intLink[title="Add indentation"], .intLink[title="Center align"], .intLink[title="Dotted list"], .intLink[title="Left align"], .intLink[title="Numbered list"], .intLink[title="Remove formatting"], .intLink[title="Right align"], .intLink[title=Bold], .intLink[title=Hyperlink], .intLink[title=Italic], .intLink[title=Quote], .intLink[title=Redo], .intLink[title=Underline], .intLink[title=Undo] { width: 0; height: 0; padding: 11px } .tl-arrow.warp { margin-left: 1px; border-width: 3px 0; border-left: 7px solid #600000 } .tl-line.warp { background: #600000 } table.login_out .login_out_label { font-weight: 700; text-align: right } table.diff { width: 100%; overflow: auto; font-size: 1rem; background: #000; border-radius: 5px; } table.diff pre { font-size: 1.15rem; scrollbar-color: black #999; } table.udiff pre { padding: 0 0 } td.difftxt { width: 52rem; } td.diffln ins { background-color: #559855; color: #000; text-decoration: none; } td.diffln del { background-color: #c55; color: #000; text-decoration: none; } td.difftxt del { background-color: inherit; text-decoration: none; } td.difftxt del > del { background-color: #c55; color: #000; text-decoration: none; } td.difftxt ins { background-color: inherit; text-decoration: none; } td.difftxt ins > ins { background-color: #559855; color: #000; text-decoration: none; } tr.diffskip.jchunk { background-color: #404040; } tr.diffskip > td.chunkctrl .jcbutton { background-color: black; } table.report { width: 100%; cursor: auto; margin: 0 0 1em; color: #000 } table.report thead { color: #ddd } table.report a { color: #0374ca } .report td, .report th { border: 0; font-size: .9em; padding: 5px } .report thead + tbody tr:hover { background-color: #ff8000!important } tbody tr:nth-child(odd) td.tktDescValue, tbody tr:nth-child(odd) td.tktDspValue { text-align: left; vertical-align: top; background: #181818; padding: 10px } tbody tr:nth-child(odd) td.tktDescLabel, tbody tr:nth-child(odd) td.tktDspLabel { width: 70px; text-align: right; overflow: hidden; font-weight: 700; padding: 10px; background: #484848 } tbody tr:nth-child(even) td.tktDescValue, tbody tr:nth-child(even) td.tktDspValue { text-align: left; vertical-align: top; background: #2c2c2c; padding: 10px } tbody tr:nth-child(even) td.tktDescLabel, tbody tr:nth-child(even) td.tktDspLabel { width: 70px; text-align: right; overflow: hidden; font-weight: 700; padding: 10px; margin: 2px; background: #555 } td.tktDescLabel, td.tktDspLabel { width: 70px; text-align: right; overflow: hidden; font-weight: 700; padding: 10px; background-color: #404040 } td.tktDescValue code, td.tktDescValue pre, td.tktDspValue code, td.tktDspValue pre { white-space: pre-wrap } div.tktComments { width: 100%; margin: 30px 0 10px } div.tktCommentHeader { border: 1px solid #ccc; background-color: #f8f8f8; padding: 10px; margin-bottom: 10px } span.tktCommentLogin { display: inline-block; font-weight: 700; color: #002060 } div.tktCommentBody { margin: 10px 40px 30px } span.ueditInheritNobody { color: #72d472; padding: .2em } span.ueditInheritDeveloper { color: #ff5d5d; padding: .2em } span.ueditInheritReader { color: #f0b850; padding: .2em } span.ueditInheritAnonymous { color: #7d7dff; padding: .2em } #wysiwygBox { padding: 12px; color: #bbb; background-color: #000; border: transparent!important; border-radius: 5px } [id=toolBar2] { cursor: pointer; display: inline-block } .intLink[title=Undo] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS44NTIgMi4wMDdjLjMxNC0uNDE4Ljg0Ny0uNjU1IDEuMzIzLS41MjlzLjk0Ni41MjggMS4wNTggMS4wNThjLjExMy41MzEtLjE1NCAxLjIzNy0uNTI5IDEuNTg4IiBmaWxsPSJub25lIiBzdHJva2U9IiM2NmE4YzciIHN0cm9rZS13aWR0aD0iLjUyOSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJiZXZlbCIvPjxwYXRoIGQ9Ik0xLjA1OCAxLjE5bDEuNTg4IDEuNzJIMS4wNTh6IiBmaWxsPSIjNjZhOGM3Ii8+PC9zdmc+) } .intLink[title=Undo]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS44NTIgMi4wMDZjLjMxNC0uNDE3Ljg0Ny0uNjU1IDEuMzIzLS41MjlzLjk0Ni41MjggMS4wNTggMS4wNTljLjExMy41My0uMTU0IDEuMjM2LS41MjkgMS41ODciIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmODAwMCIgc3Ryb2tlLXdpZHRoPSIuNTI5IiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIiBzdHJva2UtbGluZWpvaW49ImJldmVsIi8+PHBhdGggZD0iTTEuMDU4IDEuMTlsMS41ODggMS43MkgxLjA1OHoiIGZpbGw9IiNmZjgwMDAiLz48L3N2Zz4=) } .intLink[title=Redo] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMy43MzQgMi4wMDdjLS4zMTQtLjQxOC0uODQ3LS42NTUtMS4zMjMtLjUyOXMtLjk0Ni41MjgtMS4wNTggMS4wNThjLS4xMTMuNTMxLjE1NCAxLjIzNy41MjkgMS41ODgiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzY2YThjNyIgc3Ryb2tlLXdpZHRoPSIuNTI5IiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIiBzdHJva2UtbGluZWpvaW49ImJldmVsIi8+PHBhdGggZD0iTTQuNTI4IDEuMTlMMi45NCAyLjkxaDEuNTg4eiIgZmlsbD0iIzY2YThjNyIvPjwvc3ZnPg==) } .intLink[title=Redo]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMy43MzQgMi4wMDZjLS4zMTQtLjQxNy0uODQ3LS42NTUtMS4zMjMtLjUyOXMtLjk0Ni41MjgtMS4wNTggMS4wNTljLS4xMTMuNTMuMTU0IDEuMjM2LjUyOSAxLjU4NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmY4MDAwIiBzdHJva2Utd2lkdGg9Ii41MjkiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1saW5lam9pbj0iYmV2ZWwiLz48cGF0aCBkPSJNNC41MjggMS4xOUwyLjk0IDIuOTFoMS41ODh6IiBmaWxsPSIjZmY4MDAwIi8+PC9zdmc+) } .intLink[title="Remove formatting"] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS43MiAyLjM0NmwtLjE3Mi4zMTdxLS4wMzQuMDY0LS4wNTEuMTEyLS4wMTYuMDQ5LS4wMTYuMDg2IDAgLjA2Ni4wNDguMDk3LjA0OS4wMy4xNS4wM2guMDdsLS4wMzkuMTg3SC43OTRsLjAzOC0uMTg4aC4wNzRxLjA0OCAwIC4wODQtLjAxLjAzOC0uMDEyLjA3Mi0uMDQzLjAzNS0uMDMxLjA3MS0uMDg1LjAzOC0uMDUzLjA4Ni0uMTM2TDIuNDE2LjY1N0gyLjlsLjI5NiAyLjA3N3EuMDA5LjA1NS4wMjIuMTAyLjAxNi4wNDYuMDQuMDguMDI0LjAzMy4wNi4wNTIuMDM4LjAyLjA5My4wMmguMDY0bC0uMDM4LjE4N0gyLjI3MmwuMDM4LS4xODhoLjA4cS4xMTQgMCAuMTgxLS4wMzQuMDY4LS4wMzYuMDY4LS4xMTQgMC0uMDI5LS4wMDItLjA1N2wtLjAwNS0uMDU1LS4wNTQtLjM4em0uNzctLjgwN3EtLjAwOC0uMDc4LS4wMTctLjE0NUwyLjQ2IDEuMjZsLS4wMS0uMTI0LS4wMDUtLjEyNHEtLjAyNi4wNjQtLjA1Mi4xMmwtLjA1NS4xMS0uMDY1LjExOC0uMDgxLjE0NS0uMzUuNjI1aC43MTN6bTIuMjM4IDMuNzUzbC0uMTI4LS40MmgtLjY0MmwtLjEyOC40MmgtLjQwM2wuNjIyLTEuNzdoLjQ1N2wuNjI1IDEuNzd6bS0uMjE3LS43MzNsLS4yLS42NDZxLS4wMjItLjA3NC0uMDMxLS4xMTgtLjA0LjE1NS0uMjI4Ljc2NHoiIGZpbGw9IiM2NmE4YzciLz48cGF0aCBkPSJNMS4zMjMgMy43MDRjMCAxLjA1OSAxLjMyMy43NjQgMS4zMjMuNzY0IiBmaWxsPSJub25lIiBzdHJva2U9IiNhYWEiIHN0cm9rZS13aWR0aD0iLjUyOSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPjxwYXRoIGQ9Ik0yLjM4MSAzLjcwNHYxLjU4OGwxLjA1OS0uNzk0eiIgZmlsbD0iI2FhYSIvPjwvc3ZnPg==) } .intLink[title="Remove formatting"]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1LjgyMSA1LjgyMSIgaGVpZ2h0PSIyMiIgd2lkdGg9IjIyIj48cmVjdCBmaWxsPSIjNTU1IiB3aWR0aD0iNS44MjEiIGhlaWdodD0iNS44MjEiIHk9Ii0uMDAxIiByeD0iLjI2NSIgcnk9Ii4yNjUiLz48cGF0aCBmaWxsPSIjZmY4MDAwIiBkPSJNMS43MiAyLjM0NmwtLjE3Mi4zMTdxLS4wMzQuMDYzLS4wNTEuMTEyLS4wMTYuMDQ4LS4wMTYuMDg2IDAgLjA2NS4wNDguMDk2LjA0OS4wMy4xNS4wM2guMDdsLS4wMzkuMTg3SC43OTRsLjAzOC0uMTg4aC4wNzRxLjA0OCAwIC4wODQtLjAxLjAzOC0uMDEyLjA3Mi0uMDQzLjAzNS0uMDMxLjA3MS0uMDg0LjAzOC0uMDU0LjA4Ni0uMTM2TDIuNDE2LjY1NkgyLjlsLjI5NiAyLjA3N3EuMDA5LjA1NS4wMjIuMTAyLjAxNi4wNDYuMDQuMDh0LjA2LjA1M3EuMDM4LjAxOS4wOTMuMDE5aC4wNjRsLS4wMzguMTg3SDIuMjcybC4wMzgtLjE4N2guMDhxLjExNCAwIC4xODEtLjAzNS4wNjgtLjAzNi4wNjgtLjExNCAwLS4wMjktLjAwMi0uMDU2bC0uMDA1LS4wNTUtLjA1NC0uMzgxem0uNzctLjgwOHEtLjAwOC0uMDc4LS4wMTctLjE0NWwtLjAxNC0uMTMzLS4wMS0uMTI0LS4wMDUtLjEyNHEtLjAyNi4wNjQtLjA1Mi4xMmwtLjA1NS4xMS0uMDY1LjExOC0uMDgxLjE0NS0uMzUuNjI1aC43MTN6TTQuNzI4IDUuMjlMNC42IDQuODcxaC0uNjQybC0uMTI4LjQyaC0uNDAzbC42MjItMS43N2guNDU3bC42MjUgMS43N3ptLS4yMTctLjczMmwtLjItLjY0NXEtLjAyMi0uMDc1LS4wMzEtLjExOS0uMDQuMTU1LS4yMjguNzY0eiIvPjxwYXRoIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS13aWR0aD0iLjUyOSIgc3Ryb2tlPSIjZGRkIiBmaWxsPSJub25lIiBkPSJNMS4zMjMgMy43MDNjMCAxLjA1OSAxLjMyMy43NjQgMS4zMjMuNzY0Ii8+PHBhdGggZmlsbD0iI2RkZCIgZD0iTTIuMzgxIDMuNzAzdjEuNTg4bDEuMDU5LS43OTR6Ii8+PC9zdmc+) } .intLink[title=Bold] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS44NjYgMS4zNDloLjk4cS42NyAwIC45NzEuMTkxLjMwNC4xOS4zMDQuNjA2IDAgLjI4Mi0uMTM0LjQ2My0uMTMxLjE4LS4zNS4yMTd2LjAyMnEuMjk5LjA2Ni40My4yNS4xMzMuMTgyLjEzMy40ODYgMCAuNDMtLjMxMi42NzItLjMxLjI0MS0uODQ0LjI0MUgxLjg2NnptLjY2OCAxLjI0N2guMzg3cS4yNzIgMCAuMzkyLS4wODQuMTIzLS4wODQuMTIzLS4yNzggMC0uMTgxLS4xMzQtLjI1OS0uMTMtLjA4LS40MTctLjA4aC0uMzUxem0wIC41M3YuODJoLjQzNXEuMjc1IDAgLjQwNy0uMTA2LjEzMS0uMTA1LjEzMS0uMzIzIDAtLjM5Mi0uNTYtLjM5MnoiIGZpbGw9IiNhYWEiLz48L3N2Zz4=) } .intLink[title=Bold]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS44NjYgMS4zNDloLjk4cS42NyAwIC45NzEuMTkxLjMwNC4xOS4zMDQuNjA2IDAgLjI4Mi0uMTM0LjQ2My0uMTMxLjE4LS4zNS4yMTd2LjAyMnEuMjk5LjA2Ni40My4yNS4xMzMuMTgyLjEzMy40ODYgMCAuNDMtLjMxMi42NzItLjMxLjI0MS0uODQ0LjI0MUgxLjg2NnptLjY2OCAxLjI0N2guMzg3cS4yNzIgMCAuMzkyLS4wODQuMTIzLS4wODQuMTIzLS4yNzggMC0uMTgxLS4xMzQtLjI1OS0uMTMtLjA4LS40MTctLjA4aC0uMzUxem0wIC41M3YuODJoLjQzNXEuMjc1IDAgLjQwNy0uMTA2LjEzMS0uMTA1LjEzMS0uMzIzIDAtLjM5Mi0uNTYtLjM5MnoiIGZpbGw9IiNkZGQiLz48L3N2Zz4=) } .intLink[title=Italic] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMy4yNDUgNC40OTdIMS44NTJsLjA3Ny0uMzc5LjQwMy0uMTc3LjQzMy0yLjAzNy0uMzI3LS4xNzYuMDgtLjM4SDMuOTFsLS4wOC4zOC0uNDEuMTc2LS40MzIgMi4wMzcuMzM2LjE3N3oiIGZpbGw9IiNhYWEiLz48L3N2Zz4=) } .intLink[title=Italic]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMy4yNDUgNC40OTdIMS44NTJsLjA3Ny0uMzc5LjQwMy0uMTc3LjQzMy0yLjAzNy0uMzI3LS4xNzYuMDgtLjM4SDMuOTFsLS4wOC4zOC0uNDEuMTc2LS40MzIgMi4wMzcuMzM2LjE3N3oiIGZpbGw9IiNkZGQiLz48L3N2Zz4=) } .intLink[title=Underline] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNNC4xNzIgMS4wNHYyLjAzOHEwIC4zNDktLjE1OC42MTEtLjE1NS4yNjMtLjQ1LjQwMy0uMjk1LjE0LS42OTcuMTQtLjYwNyAwLS45NDMtLjMxLS4zMzYtLjMxMi0uMzM2LS44NTNWMS4wNDFoLjY2NXYxLjkyN3EwIC4zNjQuMTQ2LjUzNC4xNDcuMTcuNDg1LjE3LjMyNyAwIC40NzQtLjE3LjE0OC0uMTcyLjE0OC0uNTM4VjEuMDR6IiBmaWxsPSIjYWFhIi8+PHBhdGggZD0iTTEuMzIzIDQuNzYyaDMuMTc1IiBmaWxsPSJub25lIiBzdHJva2U9IiNhYWEiIHN0cm9rZS13aWR0aD0iLjUyOSIvPjwvc3ZnPg==) } .intLink[title=Underline]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNC4xNzIgMS4wNHYyLjAzOHEwIC4zNDktLjE1OC42MTEtLjE1NS4yNjMtLjQ1LjQwMy0uMjk1LjE0LS42OTcuMTQtLjYwNyAwLS45NDMtLjMxLS4zMzYtLjMxMi0uMzM2LS44NTNWMS4wNDFoLjY2NXYxLjkyN3EwIC4zNjQuMTQ2LjUzNC4xNDcuMTcuNDg1LjE3LjMyNyAwIC40NzQtLjE3LjE0OC0uMTcyLjE0OC0uNTM4VjEuMDR6IiBmaWxsPSIjZGRkIi8+PHBhdGggZD0iTTEuMzIzIDQuNzYyaDMuMTc1IiBmaWxsPSJub25lIiBzdHJva2U9IiNkZGQiIHN0cm9rZS13aWR0aD0iLjUyOSIvPjwvc3ZnPg==) } .intLink[title="Left align"] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS4wNTggMS4zMjNoMy43MDRNMS4wNTggMi4zODFoMi45MU0xLjA1OCAzLjQ0aDMuNzA0TTEuMDU4IDQuNDk4aDIuOTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2FhYSIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+) } .intLink[title="Left align"]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS4wNTggMS4zMjJoMy43MDRNMS4wNTggMi4zOGgyLjkxbS0yLjkxIDEuMDU5aDMuNzA0TTEuMDU4IDQuNDk3aDIuOTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2RkZCIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+) } .intLink[title="Center align"] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS4wNTggMS4zMjNoMy43MDRtLTIuOTEgMS4wNThIMy45N00xLjA1OCAzLjQ0aDMuNzA0bS0yLjkxIDEuMDU4SDMuOTciIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2FhYSIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+) } .intLink[title="Center align"]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS4wNTggMS4zMjJoMy43MDRNMS44NTIgMi4zOEgzLjk3TTEuMDU4IDMuNDM5aDMuNzA0bS0yLjkxIDEuMDU4SDMuOTciIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2RkZCIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+) } .intLink[title="Right align"] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS4wNTggMS4zMjNoMy43MDRtLTIuOTEgMS4wNThoMi45MU0xLjA1OCAzLjQ0aDMuNzA0bS0yLjkxIDEuMDU4aDIuOTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2FhYSIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+) } .intLink[title="Right align"]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS4wNTggMS4zMjJoMy43MDRNMS44NTIgMi4zOGgyLjkxTTEuMDU4IDMuNDM5aDMuNzA0bS0yLjkxIDEuMDU4aDIuOTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2RkZCIgc3Ryb2tlLXdpZHRoPSIuNTI5Ii8+PC9zdmc+) } .intLink[title="Numbered list"] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMi4xMTcgMS4zMjNoMi45MU0yLjExNyAyLjkxaDIuOTFtLTIuOTEgMS41ODhoMi45MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjYWFhIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNMS41MjMgMS44NTFoLS4yNjZ2LS43MjlsLjAwMi0uMTIuMDA0LS4xM3EtLjA2Ni4wNjYtLjA5Mi4wODdsLS4xNDQuMTE2LS4xMjktLjE2LjQwNi0uMzIzaC4yMTl6bS4yMjIgMS41ODhoLS44OHYtLjE4NWwuMzE2LS4zMnEuMTQtLjE0NC4xODMtLjE5OS4wNDMtLjA1Ni4wNjItLjEwMy4wMi0uMDQ3LjAyLS4wOTggMC0uMDc2LS4wNDMtLjExMy0uMDQxLS4wMzctLjExMS0uMDM3LS4wNzMgMC0uMTQyLjAzMy0uMDcuMDM0LS4xNDQuMDk2TC44NiAyLjM0MnEuMDkzLS4wOC4xNTQtLjExMi4wNjItLjAzMy4xMzQtLjA1LjA3Mi0uMDE4LjE2Mi0uMDE4LjExOCAwIC4yMDguMDQzdC4xNC4xMnEuMDUuMDc4LjA1LjE3OCAwIC4wODctLjAzLjE2My0uMDMuMDc2LS4wOTUuMTU2LS4wNjQuMDgtLjIyNi4yMjlsLS4xNjIuMTUydi4wMTJoLjU0OXptLS4wNDkuNjA5cTAgLjExOC0uMDcyLjIwMS0uMDcxLjA4My0uMi4xMTR2LjAwNXEuMTUyLjAxOS4yMy4wOTMuMDc5LjA3My4wNzkuMTk4IDAgLjE4Mi0uMTMyLjI4My0uMTMyLjEwMS0uMzc2LjEwMS0uMjA1IDAtLjM2NC0uMDY4di0uMjI2cS4wNzMuMDM3LjE2MS4wNi4wODguMDIzLjE3NC4wMjMuMTMyIDAgLjE5NS0uMDQ1dC4wNjMtLjE0NHEwLS4wODgtLjA3My0uMTI1LS4wNzItLjAzNy0uMjMtLjAzN2gtLjA5NnYtLjIwNWguMDk3cS4xNDcgMCAuMjE0LS4wMzcuMDY4LS4wNC4wNjgtLjEzMiAwLS4xNDMtLjE4LS4xNDMtLjA2MSAwLS4xMjYuMDItLjA2NC4wMjEtLjE0Mi4wNzJsLS4xMjMtLjE4M3EuMTcyLS4xMjQuNDEtLjEyNC4xOTYgMCAuMzEuMDc5LjExMy4wNzkuMTEzLjIyeiIgZmlsbD0iIzY2YThjNyIvPjwvc3ZnPg==) } .intLink[title="Numbered list"]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMi4xMTcgMS4zMjJoMi45MU0yLjExNyAyLjkxaDIuOTFtLTIuOTEgMS41ODdoMi45MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNMS41MjMgMS44NTFoLS4yNjZ2LS43MjlsLjAwMi0uMTIuMDA0LS4xM3EtLjA2Ni4wNjYtLjA5Mi4wODdsLS4xNDQuMTE2LS4xMjktLjE2LjQwNi0uMzIzaC4yMTl6bS4yMjIgMS41ODhoLS44OHYtLjE4NWwuMzE2LS4zMnEuMTQtLjE0NC4xODMtLjE5OS4wNDMtLjA1Ni4wNjItLjEwMy4wMi0uMDQ3LjAyLS4wOTggMC0uMDc2LS4wNDMtLjExMy0uMDQxLS4wMzctLjExMS0uMDM3LS4wNzMgMC0uMTQyLjAzMy0uMDcuMDM0LS4xNDQuMDk2TC44NiAyLjM0MnEuMDkzLS4wOC4xNTQtLjExMi4wNjItLjAzMy4xMzQtLjA1LjA3Mi0uMDE4LjE2Mi0uMDE4LjExOCAwIC4yMDguMDQzdC4xNC4xMnEuMDUuMDc4LjA1LjE3OCAwIC4wODctLjAzLjE2My0uMDMuMDc2LS4wOTUuMTU2LS4wNjQuMDgtLjIyNi4yMjlsLS4xNjIuMTUydi4wMTJoLjU0OXptLS4wNDkuNjA5cTAgLjExOC0uMDcyLjIwMS0uMDcxLjA4My0uMi4xMTR2LjAwNXEuMTUyLjAxOS4yMy4wOTMuMDc5LjA3My4wNzkuMTk4IDAgLjE4Mi0uMTMyLjI4My0uMTMyLjEwMS0uMzc2LjEwMS0uMjA1IDAtLjM2NC0uMDY4di0uMjI2cS4wNzMuMDM3LjE2MS4wNi4wODguMDIzLjE3NC4wMjMuMTMyIDAgLjE5NS0uMDQ1dC4wNjMtLjE0NHEwLS4wODgtLjA3My0uMTI1LS4wNzItLjAzNy0uMjMtLjAzN2gtLjA5NnYtLjIwNWguMDk3cS4xNDcgMCAuMjE0LS4wMzcuMDY4LS4wNC4wNjgtLjEzMiAwLS4xNDMtLjE4LS4xNDMtLjA2MSAwLS4xMjYuMDItLjA2NC4wMjEtLjE0Mi4wNzJsLS4xMjMtLjE4M3EuMTcyLS4xMjQuNDEtLjEyNC4xOTYgMCAuMzEuMDc5LjExMy4wNzkuMTEzLjIyeiIgZmlsbD0iI2ZmODAwMCIvPjwvc3ZnPg==) } .intLink[title="Dotted list"] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMi4xMTcgMS4zMjJoMi45MU0yLjExNyAyLjkxaDIuOTFtLTIuOTEgMS41ODdoMi45MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjYWFhIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48Y2lyY2xlIHI9Ii4yNjUiIGN5PSIxLjMyMiIgY3g9IjEuMzIzIiBmaWxsPSIjNjZhOGM3Ii8+PGNpcmNsZSByPSIuMjY1IiBjeT0iMi45MSIgY3g9IjEuMzIzIiBmaWxsPSIjNjZhOGM3Ii8+PGNpcmNsZSByPSIuMjY1IiBjeT0iNC40OTciIGN4PSIxLjMyMyIgZmlsbD0iIzY2YThjNyIvPjwvc3ZnPg==) } .intLink[title="Dotted list"]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMi4xMTcgMS4zMjJoMi45MU0yLjExNyAyLjkxaDIuOTFtLTIuOTEgMS41ODdoMi45MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48Y2lyY2xlIHI9Ii4yNjUiIGN5PSIxLjMyMiIgY3g9IjEuMzIzIiBmaWxsPSIjZmY4MDAwIi8+PGNpcmNsZSByPSIuMjY1IiBjeT0iMi45MSIgY3g9IjEuMzIzIiBmaWxsPSIjZmY4MDAwIi8+PGNpcmNsZSByPSIuMjY1IiBjeT0iNC40OTciIGN4PSIxLjMyMyIgZmlsbD0iI2ZmODAwMCIvPjwvc3ZnPg==) } .intLink[title=Quote] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMy4wOTIgMi41MDlxMC0uMjcuMDgzLS41MjEuMDgyLS4yNTMuMjYtLjQ2OC4xODItLjIxNS40NjctLjM4NC4yOS0uMTc0LjcwMy0uMjg1di40MzhxLS4xODIuMDY2LS4zMjIuMTMyLS4xNDEuMDY2LS4yMzYuMTQ1LS4wOTEuMDc0LS4xNC4xNy0uMDQ2LjA5LS4wNDYuMjE0IDAgLjA4My4wNDUuMTMzLjA0Ni4wNS4xMTYuMDk1bC4xNDkuMDk1cS4wNzkuMDQ1LjE0OS4xMi4wNy4wNy4xMTYuMTgyLjA0NS4xMTEuMDQ1LjI4IDAgLjI4Ni0uMTc4LjQzNS0uMTczLjE0OS0uNDM0LjE0OS0uMzYgMC0uNTctLjI0OC0uMjA3LS4yNDgtLjIwNy0uNjgyem0tMS43NyAwcTAtLjI3LjA4My0uNTIxLjA4My0uMjUzLjI2LS40NjguMTgyLS4yMTUuNDY4LS4zODQuMjktLjE3NC43MDMtLjI4NXYuNDM4cS0uMTg2LjA2Ni0uMzI3LjEzMi0uMTM2LjA2Ni0uMjMyLjE0NS0uMDkuMDc0LS4xNC4xNy0uMDQ2LjA5LS4wNDYuMjE0IDAgLjA4My4wNDYuMTMzLjA0NS4wNS4xMTYuMDk1bC4xNDguMDk1cS4wNzkuMDQ1LjE1LjEyLjA3LjA3LjExNS4xODIuMDQ2LjExMS4wNDYuMjggMCAuMjg2LS4xNzguNDM1LS4xNzQuMTQ5LS40MzQuMTQ5LS4zNiAwLS41Ny0uMjQ4LS4yMDgtLjI0OC0uMjA4LS42ODJ6IiBmaWxsPSIjNjZhOGM3Ii8+PC9zdmc+) } .intLink[title=Quote]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMy4wOTIgMi41MDlxMC0uMjcuMDgzLS41MjEuMDgyLS4yNTMuMjYtLjQ2OC4xODItLjIxNS40NjctLjM4NC4yOS0uMTc0LjcwMy0uMjg1di40MzhxLS4xODIuMDY2LS4zMjIuMTMyLS4xNDEuMDY2LS4yMzYuMTQ1LS4wOTEuMDc0LS4xNC4xNy0uMDQ2LjA5LS4wNDYuMjE0IDAgLjA4My4wNDUuMTMzLjA0Ni4wNS4xMTYuMDk1bC4xNDkuMDk1cS4wNzkuMDQ1LjE0OS4xMi4wNy4wNy4xMTYuMTgyLjA0NS4xMTEuMDQ1LjI4IDAgLjI4Ni0uMTc4LjQzNS0uMTczLjE0OS0uNDM0LjE0OS0uMzYgMC0uNTctLjI0OC0uMjA3LS4yNDgtLjIwNy0uNjgyem0tMS43NyAwcTAtLjI3LjA4My0uNTIxLjA4My0uMjUzLjI2LS40NjguMTgyLS4yMTUuNDY4LS4zODQuMjktLjE3NC43MDMtLjI4NXYuNDM4cS0uMTg2LjA2Ni0uMzI3LjEzMi0uMTM2LjA2Ni0uMjMyLjE0NS0uMDkuMDc0LS4xNC4xNy0uMDQ2LjA5LS4wNDYuMjE0IDAgLjA4My4wNDYuMTMzLjA0NS4wNS4xMTYuMDk1bC4xNDguMDk1cS4wNzkuMDQ1LjE1LjEyLjA3LjA3LjExNS4xODIuMDQ2LjExMS4wNDYuMjggMCAuMjg2LS4xNzguNDM1LS4xNzQuMTQ5LS40MzQuMTQ5LS4zNiAwLS41Ny0uMjQ4LS4yMDgtLjI0OC0uMjA4LS42ODJ6IiBmaWxsPSIjZmY4MDAwIi8+PC9zdmc+) } .intLink[title="Delete indentation"] { width: 0; height: 0; padding: 11px; background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS4wNTggMS4zMjNoMy43MDRNMi4zODEgMi4zODFoMi4zODJNMi4zODEgMy40NGgyLjM4Mk0xLjA1OCA0LjQ5OGgzLjcwNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjYWFhIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNLjUzIDIuOTFsMS4zMjItLjc5M3YxLjU4N3oiIGZpbGw9IiM2NmE4YzciLz48L3N2Zz4=) } .intLink[title="Delete indentation"]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS4wNTggMS4zMjJoMy43MDRNMi4zODEgMi4zOGgyLjM4Mk0yLjM4MSAzLjQzOWgyLjM4Mk0xLjA1OCA0LjQ5N2gzLjcwNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNLjUzIDIuOTFsMS4zMjItLjc5NHYxLjU4N3oiIGZpbGw9IiNmZjgwMDAiLz48L3N2Zz4=) } .intLink[title="Add indentation"] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS4wNTggMS4zMjNoMy43MDRNMi4zODEgMi4zODFoMi4zODJNMi4zODEgMy40NGgyLjM4Mk0xLjA1OCA0LjQ5OGgzLjcwNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjYWFhIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNMi4xMTcgMi45MUwuNzk0IDIuMTE3djEuNTg3eiIgZmlsbD0iIzY2YThjNyIvPjwvc3ZnPg==) } .intLink[title="Add indentation"]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1LjgyMSA1LjgyMSIgaGVpZ2h0PSIyMiIgd2lkdGg9IjIyIj48cmVjdCB3aWR0aD0iNS44MjEiIGhlaWdodD0iNS44MjEiIHk9Ii0uMDAxIiByeD0iLjI2NSIgcnk9Ii4yNjUiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS4wNTggMS4zMjJoMy43MDRNMi4zODEgMi4zOGgyLjM4Mk0yLjM4MSAzLjQzOWgyLjM4Mk0xLjA1OCA0LjQ5N2gzLjcwNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48cGF0aCBkPSJNMi4xMTcgMi45MUwuNzk0IDIuMTE2djEuNTg3eiIgZmlsbD0iI2ZmODAwMCIvPjwvc3ZnPg==) } .intLink[title=Hyperlink] { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS43NTIgMy45NjloLjc5M20tLjc5My0yLjExN2guNzkzTTEuNzUyIDMuOTdjLS4zNDMgMC0uNjU5LS4xMTktLjgzLS40MTUtLjE3MS0uMjk3LS4xNzEtLjk5IDAtMS4yODcuMTcxLS4yOTYuNDg3LS40MTUuODMtLjQxNW0yLjIxNyAyLjExNmgtLjc5NG0uNzk0LTIuMTE3aC0uNzk0bS43OTQgMi4xMTdjLjM0MiAwIC42NTgtLjExOS44My0uNDE1LjE3LS4yOTcuMTctLjk5IDAtMS4yODctLjE3Mi0uMjk2LS40ODgtLjQxNS0uODMtLjQxNU0yLjExNyAyLjkxaDEuNTg3IiBmaWxsPSJub25lIiBzdHJva2U9IiNhYWEiIHN0cm9rZS13aWR0aD0iLjUyOSIvPjwvc3ZnPg==) } .intLink[title=Hyperlink]:hover { background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS43NTIgMy45NjhoLjc5M20tLjc5My0yLjExN2guNzkzbS0uNzkzIDIuMTE3Yy0uMzQzIDAtLjY1OS0uMTE5LS44My0uNDE1LS4xNzEtLjI5Ny0uMTcxLS45OSAwLTEuMjg3LjE3MS0uMjk2LjQ4Ny0uNDE1LjgzLS40MTVtMi4yMTcgMi4xMTdoLS43OTRtLjc5NC0yLjExN2gtLjc5NG0uNzk0IDIuMTE3Yy4zNDIgMCAuNjU4LS4xMTkuODMtLjQxNS4xNy0uMjk3LjE3LS45OSAwLTEuMjg3LS4xNzItLjI5Ni0uNDg4LS40MTUtLjgzLS40MTVNMi4xMTcgMi45MWgxLjU4NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48L3N2Zz4=) } .statistics-report-graph-line { border: 2px solid #ff8000; background-color: #ff8000; } .statistics-report-graph-extra { border: 2px dashed #446979; border-left-style: none; } mark, p.noMoreShun, p.shunned, span.modpending { color: #ff8000 } table.captcha { margin: auto; padding: 10px; background-color: #000; border-radius: 5px } .container:after, .mainmenu:after, .row:after, .u-cf { content: ""; display: table; clear: both } div.forumSel { background-color: #3a3a3a; } body.forum .forumPosts.fileage a:visited { color: rgb(72, 144, 224); } .debug { background-color: #330; border: 2px solid #aa0; } .capsumOff { background-color: #222; } .capsumRead { background-color: #262; } .capsumWrite { background-color: #662; } body.branch .brlist > table > tbody > tr:hover:not(.selected), body.branch .brlist > table > tbody > tr.selected { background-color: #442800; } |
Added skins/ardoise/details.txt.
> > > > > | 1 2 3 4 5 | timeline-arrowheads: 0 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 1 pikchr-background: 0x1d2021 |
Added skins/ardoise/footer.txt.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <th1> if {[string first artifact $current_page] == 0 || [string first hexdump $current_page] == 0} { html "</div>" } </th1> </div> <!-- end div container --> </div> <!-- end div middle max-full-width --> <footer> <div class="container"> <div class="pull-right"> <a href="https://fossil-scm.org/">Fossil $release_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> </footer> |
Added skins/ardoise/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 | <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 --> <nav class="mainmenu" title="Main Menu"> <ul> <th1> html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a></li>\n" builtin_request_js hbmenu.js set once 1 foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {$once && [string match $url\[/?#\]* /$current_page/]} { set class "$class active" set once 0 } html "<li class='$class'>" if {[string match /* $url]} {set url $home$url} html "<a href='$url'>$name</a></li>\n" } </th1> </ul> </nav> <nav id="hbdrop" class='hbdrop' title="sitemap"></nav> </div> <!-- end div container --> </header> <div class="middle max-full-width"> <div class="container"> <th1> if {[string first artifact $current_page] == 0 || [string first hexdump $current_page] == 0} { html "<div class=\"artifact_content\">" } </th1> |
Added skins/ardoise/images/active.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="17" height="9" viewBox="0 0 4.498 2.381"><path d="M4.233 2.381H.265l.998-1.058.986-1.058.998 1.062z" fill="#ff8000"/></svg> |
Added skins/ardoise/images/addindent.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M2.381 2.381h2.382M2.381 3.44h2.382M1.058 4.498h3.704" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M2.117 2.91L.794 2.117v1.587z" fill="#66a8c7"/></svg> |
Added skins/ardoise/images/addindent_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5.821 5.821" height="22" width="22"><rect width="5.821" height="5.821" y="-.001" rx=".265" ry=".265" fill="#555"/><path d="M1.058 1.322h3.704M2.381 2.38h2.382M2.381 3.439h2.382M1.058 4.497h3.704" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M2.117 2.91L.794 2.116v1.587z" fill="#ff8000"/></svg> |
Added skins/ardoise/images/blist.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#aaa" stroke-width=".529"/><circle r=".265" cy="1.322" cx="1.323" fill="#66a8c7"/><circle r=".265" cy="2.91" cx="1.323" fill="#66a8c7"/><circle r=".265" cy="4.497" cx="1.323" fill="#66a8c7"/></svg> |
Added skins/ardoise/images/blist_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#ddd" stroke-width=".529"/><circle r=".265" cy="1.322" cx="1.323" fill="#ff8000"/><circle r=".265" cy="2.91" cx="1.323" fill="#ff8000"/><circle r=".265" cy="4.497" cx="1.323" fill="#ff8000"/></svg> |
Added skins/ardoise/images/bold.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.866 1.349h.98q.67 0 .971.191.304.19.304.606 0 .282-.134.463-.131.18-.35.217v.022q.299.066.43.25.133.182.133.486 0 .43-.312.672-.31.241-.844.241H1.866zm.668 1.247h.387q.272 0 .392-.084.123-.084.123-.278 0-.181-.134-.259-.13-.08-.417-.08h-.351zm0 .53v.82h.435q.275 0 .407-.106.131-.105.131-.323 0-.392-.56-.392z" fill="#aaa"/></svg> |
Added skins/ardoise/images/bold_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.866 1.349h.98q.67 0 .971.191.304.19.304.606 0 .282-.134.463-.131.18-.35.217v.022q.299.066.43.25.133.182.133.486 0 .43-.312.672-.31.241-.844.241H1.866zm.668 1.247h.387q.272 0 .392-.084.123-.084.123-.278 0-.181-.134-.259-.13-.08-.417-.08h-.351zm0 .53v.82h.435q.275 0 .407-.106.131-.105.131-.323 0-.392-.56-.392z" fill="#ddd"/></svg> |
Added skins/ardoise/images/calign.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704m-2.91 1.058H3.97M1.058 3.44h3.704m-2.91 1.058H3.97" fill="none" stroke="#aaa" stroke-width=".529"/></svg> |
Added skins/ardoise/images/calign_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.852 2.38H3.97M1.058 3.439h3.704m-2.91 1.058H3.97" fill="none" stroke="#ddd" stroke-width=".529"/></svg> |
Added skins/ardoise/images/clrfmt.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.72 2.346l-.172.317q-.034.064-.051.112-.016.049-.016.086 0 .066.048.097.049.03.15.03h.07l-.039.187H.794l.038-.188h.074q.048 0 .084-.01.038-.012.072-.043.035-.031.071-.085.038-.053.086-.136L2.416.657H2.9l.296 2.077q.009.055.022.102.016.046.04.08.024.033.06.052.038.02.093.02h.064l-.038.187H2.272l.038-.188h.08q.114 0 .181-.034.068-.036.068-.114 0-.029-.002-.057l-.005-.055-.054-.38zm.77-.807q-.008-.078-.017-.145L2.46 1.26l-.01-.124-.005-.124q-.026.064-.052.12l-.055.11-.065.118-.081.145-.35.625h.713zm2.238 3.753l-.128-.42h-.642l-.128.42h-.403l.622-1.77h.457l.625 1.77zm-.217-.733l-.2-.646q-.022-.074-.031-.118-.04.155-.228.764z" fill="#66a8c7"/><path d="M1.323 3.704c0 1.059 1.323.764 1.323.764" fill="none" stroke="#aaa" stroke-width=".529" stroke-linecap="square"/><path d="M2.381 3.704v1.588l1.059-.794z" fill="#aaa"/></svg> |
Added skins/ardoise/images/clrfmt_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5.821 5.821" height="22" width="22"><rect fill="#555" width="5.821" height="5.821" y="-.001" rx=".265" ry=".265"/><path fill="#ff8000" d="M1.72 2.346l-.172.317q-.034.063-.051.112-.016.048-.016.086 0 .065.048.096.049.03.15.03h.07l-.039.187H.794l.038-.188h.074q.048 0 .084-.01.038-.012.072-.043.035-.031.071-.084.038-.054.086-.136L2.416.656H2.9l.296 2.077q.009.055.022.102.016.046.04.08t.06.053q.038.019.093.019h.064l-.038.187H2.272l.038-.187h.08q.114 0 .181-.035.068-.036.068-.114 0-.029-.002-.056l-.005-.055-.054-.381zm.77-.808q-.008-.078-.017-.145l-.014-.133-.01-.124-.005-.124q-.026.064-.052.12l-.055.11-.065.118-.081.145-.35.625h.713zM4.728 5.29L4.6 4.871h-.642l-.128.42h-.403l.622-1.77h.457l.625 1.77zm-.217-.732l-.2-.645q-.022-.075-.031-.119-.04.155-.228.764z"/><path stroke-linecap="square" stroke-width=".529" stroke="#ddd" fill="none" d="M1.323 3.703c0 1.059 1.323.764 1.323.764"/><path fill="#ddd" d="M2.381 3.703v1.588l1.059-.794z"/></svg> |
Added skins/ardoise/images/delindent.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M2.381 2.381h2.382M2.381 3.44h2.382M1.058 4.498h3.704" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M.53 2.91l1.322-.793v1.587z" fill="#66a8c7"/></svg> |
Added skins/ardoise/images/delindent_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M2.381 2.38h2.382M2.381 3.439h2.382M1.058 4.497h3.704" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M.53 2.91l1.322-.794v1.587z" fill="#ff8000"/></svg> |
Added skins/ardoise/images/dir.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 5.292 4.233"><path d="M.794.53v3.174h3.704V1.323H2.91V.529z" fill="#1d2021" stroke="#ff8000" stroke-width=".529" stroke-linecap="round" stroke-linejoin="round"/></svg> |
Added skins/ardoise/images/file.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 5.292 4.233"><path d="M1.323.265v3.704h2.646V1.323L2.91.265z" fill="#1d2021" stroke="#ddd" stroke-width=".529" stroke-linejoin="round"/><path d="M2.646.265h.264v1.323h1.06" fill="#1d2021" stroke="#ddd" stroke-width=".529" stroke-linejoin="round"/></svg> |
Added skins/ardoise/images/italic.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.245 4.497H1.852l.077-.379.403-.177.433-2.037-.327-.176.08-.38H3.91l-.08.38-.41.176-.432 2.037.336.177z" fill="#aaa"/></svg> |
Added skins/ardoise/images/italic_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.245 4.497H1.852l.077-.379.403-.177.433-2.037-.327-.176.08-.38H3.91l-.08.38-.41.176-.432 2.037.336.177z" fill="#ddd"/></svg> |
Added skins/ardoise/images/lalign.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M1.058 2.381h2.91M1.058 3.44h3.704M1.058 4.498h2.91" fill="none" stroke="#aaa" stroke-width=".529"/></svg> |
Added skins/ardoise/images/lalign_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.058 2.38h2.91m-2.91 1.059h3.704M1.058 4.497h2.91" fill="none" stroke="#ddd" stroke-width=".529"/></svg> |
Added skins/ardoise/images/link.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.752 3.969h.793m-.793-2.117h.793M1.752 3.97c-.343 0-.659-.119-.83-.415-.171-.297-.171-.99 0-1.287.171-.296.487-.415.83-.415m2.217 2.116h-.794m.794-2.117h-.794m.794 2.117c.342 0 .658-.119.83-.415.17-.297.17-.99 0-1.287-.172-.296-.488-.415-.83-.415M2.117 2.91h1.587" fill="none" stroke="#aaa" stroke-width=".529"/></svg> |
Added skins/ardoise/images/link_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.752 3.968h.793m-.793-2.117h.793m-.793 2.117c-.343 0-.659-.119-.83-.415-.171-.297-.171-.99 0-1.287.171-.296.487-.415.83-.415m2.217 2.117h-.794m.794-2.117h-.794m.794 2.117c.342 0 .658-.119.83-.415.17-.297.17-.99 0-1.287-.172-.296-.488-.415-.83-.415M2.117 2.91h1.587" fill="none" stroke="#ddd" stroke-width=".529"/></svg> |
Added skins/ardoise/images/nlist.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M2.117 1.323h2.91M2.117 2.91h2.91m-2.91 1.588h2.91" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M1.523 1.851h-.266v-.729l.002-.12.004-.13q-.066.066-.092.087l-.144.116-.129-.16.406-.323h.219zm.222 1.588h-.88v-.185l.316-.32q.14-.144.183-.199.043-.056.062-.103.02-.047.02-.098 0-.076-.043-.113-.041-.037-.111-.037-.073 0-.142.033-.07.034-.144.096L.86 2.342q.093-.08.154-.112.062-.033.134-.05.072-.018.162-.018.118 0 .208.043t.14.12q.05.078.05.178 0 .087-.03.163-.03.076-.095.156-.064.08-.226.229l-.162.152v.012h.549zm-.049.609q0 .118-.072.201-.071.083-.2.114v.005q.152.019.23.093.079.073.079.198 0 .182-.132.283-.132.101-.376.101-.205 0-.364-.068v-.226q.073.037.161.06.088.023.174.023.132 0 .195-.045t.063-.144q0-.088-.073-.125-.072-.037-.23-.037h-.096v-.205h.097q.147 0 .214-.037.068-.04.068-.132 0-.143-.18-.143-.061 0-.126.02-.064.021-.142.072l-.123-.183q.172-.124.41-.124.196 0 .31.079.113.079.113.22z" fill="#66a8c7"/></svg> |
Added skins/ardoise/images/nlist_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M1.523 1.851h-.266v-.729l.002-.12.004-.13q-.066.066-.092.087l-.144.116-.129-.16.406-.323h.219zm.222 1.588h-.88v-.185l.316-.32q.14-.144.183-.199.043-.056.062-.103.02-.047.02-.098 0-.076-.043-.113-.041-.037-.111-.037-.073 0-.142.033-.07.034-.144.096L.86 2.342q.093-.08.154-.112.062-.033.134-.05.072-.018.162-.018.118 0 .208.043t.14.12q.05.078.05.178 0 .087-.03.163-.03.076-.095.156-.064.08-.226.229l-.162.152v.012h.549zm-.049.609q0 .118-.072.201-.071.083-.2.114v.005q.152.019.23.093.079.073.079.198 0 .182-.132.283-.132.101-.376.101-.205 0-.364-.068v-.226q.073.037.161.06.088.023.174.023.132 0 .195-.045t.063-.144q0-.088-.073-.125-.072-.037-.23-.037h-.096v-.205h.097q.147 0 .214-.037.068-.04.068-.132 0-.143-.18-.143-.061 0-.126.02-.064.021-.142.072l-.123-.183q.172-.124.41-.124.196 0 .31.079.113.079.113.22z" fill="#ff8000"/></svg> |
Added skins/ardoise/images/quote.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.092 2.509q0-.27.083-.521.082-.253.26-.468.182-.215.467-.384.29-.174.703-.285v.438q-.182.066-.322.132-.141.066-.236.145-.091.074-.14.17-.046.09-.046.214 0 .083.045.133.046.05.116.095l.149.095q.079.045.149.12.07.07.116.182.045.111.045.28 0 .286-.178.435-.173.149-.434.149-.36 0-.57-.248-.207-.248-.207-.682zm-1.77 0q0-.27.083-.521.083-.253.26-.468.182-.215.468-.384.29-.174.703-.285v.438q-.186.066-.327.132-.136.066-.232.145-.09.074-.14.17-.046.09-.046.214 0 .083.046.133.045.05.116.095l.148.095q.079.045.15.12.07.07.115.182.046.111.046.28 0 .286-.178.435-.174.149-.434.149-.36 0-.57-.248-.208-.248-.208-.682z" fill="#66a8c7"/></svg> |
Added skins/ardoise/images/quote_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.092 2.509q0-.27.083-.521.082-.253.26-.468.182-.215.467-.384.29-.174.703-.285v.438q-.182.066-.322.132-.141.066-.236.145-.091.074-.14.17-.046.09-.046.214 0 .083.045.133.046.05.116.095l.149.095q.079.045.149.12.07.07.116.182.045.111.045.28 0 .286-.178.435-.173.149-.434.149-.36 0-.57-.248-.207-.248-.207-.682zm-1.77 0q0-.27.083-.521.083-.253.26-.468.182-.215.468-.384.29-.174.703-.285v.438q-.186.066-.327.132-.136.066-.232.145-.09.074-.14.17-.046.09-.046.214 0 .083.046.133.045.05.116.095l.148.095q.079.045.15.12.07.07.115.182.046.111.046.28 0 .286-.178.435-.174.149-.434.149-.36 0-.57-.248-.208-.248-.208-.682z" fill="#ff8000"/></svg> |
Added skins/ardoise/images/ralign.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704m-2.91 1.058h2.91M1.058 3.44h3.704m-2.91 1.058h2.91" fill="none" stroke="#aaa" stroke-width=".529"/></svg> |
Added skins/ardoise/images/ralign_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.852 2.38h2.91M1.058 3.439h3.704m-2.91 1.058h2.91" fill="none" stroke="#ddd" stroke-width=".529"/></svg> |
Added skins/ardoise/images/redo.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.734 2.007c-.314-.418-.847-.655-1.323-.529s-.946.528-1.058 1.058c-.113.531.154 1.237.529 1.588" fill="none" stroke="#66a8c7" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M4.528 1.19L2.94 2.91h1.588z" fill="#66a8c7"/></svg> |
Added skins/ardoise/images/redo_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.734 2.006c-.314-.417-.847-.655-1.323-.529s-.946.528-1.058 1.059c-.113.53.154 1.236.529 1.587" fill="none" stroke="#ff8000" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M4.528 1.19L2.94 2.91h1.588z" fill="#ff8000"/></svg> |
Added skins/ardoise/images/underline.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M4.172 1.04v2.038q0 .349-.158.611-.155.263-.45.403-.295.14-.697.14-.607 0-.943-.31-.336-.312-.336-.853V1.041h.665v1.927q0 .364.146.534.147.17.485.17.327 0 .474-.17.148-.172.148-.538V1.04z" fill="#aaa"/><path d="M1.323 4.762h3.175" fill="none" stroke="#aaa" stroke-width=".529"/></svg> |
Added skins/ardoise/images/underline_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M4.172 1.04v2.038q0 .349-.158.611-.155.263-.45.403-.295.14-.697.14-.607 0-.943-.31-.336-.312-.336-.853V1.041h.665v1.927q0 .364.146.534.147.17.485.17.327 0 .474-.17.148-.172.148-.538V1.04z" fill="#ddd"/><path d="M1.323 4.762h3.175" fill="none" stroke="#ddd" stroke-width=".529"/></svg> |
Added skins/ardoise/images/undo.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.852 2.007c.314-.418.847-.655 1.323-.529s.946.528 1.058 1.058c.113.531-.154 1.237-.529 1.588" fill="none" stroke="#66a8c7" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M1.058 1.19l1.588 1.72H1.058z" fill="#66a8c7"/></svg> |
Added skins/ardoise/images/undo_h.svg.
> | 1 | <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.852 2.006c.314-.417.847-.655 1.323-.529s.946.528 1.058 1.059c.113.53-.154 1.236-.529 1.587" fill="none" stroke="#ff8000" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M1.058 1.19l1.588 1.72H1.058z" fill="#ff8000"/></svg> |
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 | /* 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; 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 */ 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 */ nav.mainmenu { float: left; margin-left: 10px; margin-right: 20px; font-size: 0.9em; font-weight: bold; padding:5px; background-color:#eee; border:1px solid #999; width:6em; } /* Main menu is now a list */ nav.mainmenu ul { padding: 0; list-style:none; } nav.mainmenu a, nav.mainmenu a:visited{ padding: 1px 10px 1px 10px; color: #333; text-decoration: none; } nav.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, div.submenu label { padding: 3px 10px 3px 10px; color: #333; text-decoration: none; } div.submenu a:hover, div.sectionmenu>a.button:hover, div.submenu label: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 */ 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 | <footer> Fossil $release_version $manifest_version $manifest_date </footer> |
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 | <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> </header> <nav class="mainmenu" title="Main Menu"> <th1> set sitemap 0 foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {[string match /* $url]} {set url $home$url} html "<a href='$url'>$name</a><br/>\n" if {[string match /sitemap $url]} {set sitemap 1} } if {!$sitemap} { html "<a href='$home/sitemap'>Sitemap</a>\n" } </th1> </nav> |
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 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 | /*! 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; } input[type="submit"]:disabled { color: rgb(128,128,128); background-color: rgb(153,153,153); } /* 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; position: relative; border-top: 1px solid #ccc; padding: 0; } .mainmenu li { outline: 0; 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, nav#hbdrop a { color: #3b5c6b; padding: 10px 15px; } .mainmenu li.active a { font-weight: bold; } .mainmenu li:hover nav#hbdrop a:hover { background-color: #eee; } nav#hbdrop { background-color: white; border: 2px solid #ccc; display: none; width: 100%; position: absolute; z-index: 20; } /* 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, .submenu label { display: inline; font-weight: normal; color: #3b5c6b; padding: 5px 15px; text-decoration: none; border: 1px solid transparent; border-radius: 5px; } .submenu a:hover, .submenu label: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 { padding-top: 2px; } ul.browser li.dir > a { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExABnLjGZQAAAEFJREFUOMtjYKAQMIaGhv4npGj16tWMuORYGBgYGOZW+eDUnNy2Ba/hLMQ4E58rCRpAyHVMlAbiqAGjBhCdmWgKAHp4Dh0ZusP3AAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: 0px center; padding-left: 22px; } ul.browser li.file { padding-top: 2px; } ul.browser li.file > a { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExMaPfBcSgAAAFNJREFUOMvtkzEOwDAIA02VL5pHwiOTJZFQmkqFOTex+CwPCCYkAaDjB+4u65ZdYGafQVV9SR4kWQUke0mwS1o2HGcAQKs0R1lpQuQKruD4jVnBAG/cGRqf0U66AAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: 0px center; padding-left: 22px; } 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; border-bottom: 1px solid #ddd; border-right: 1px solid #ddd; } .timelineSelected { border-left: 2px solid orange; background-color: #ffffe8; border-bottom: 1px solid #ddd; border-right: 1px solid #ddd; } .timelineSecondary { background-color: #e8ffff; } tr.timelineCurrent td.timelineTableCell { } tr.timelineBottom td { border-bottom: 0; } div.timelineDate { font-weight: bold; white-space: nowrap; } td.timelineTime { vertical-align: top; text-align: right; white-space: nowrap; border-bottom: 0; } td.timelineGraph { width: 20px; text-align: left; vertical-align: top; border-bottom: 0; } a.timelineHistLink { text-transform: lowercase; } span.timelineComment { padding: 0px 5px; } /* Login/Logout ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ table.login_out { } table.login_out .login_out_label { font-weight: 700; text-align: right; } table.login_out td { border: 0; } /* Diff displays ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ table.diff { width: 100%; overflow: auto; border: 1px solid #ccc; padding: 5px; font-size: 1rem; } table.diff: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; } body.forum .forumPosts.fileage a:visited { color: #648999; } |
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 | </div> <!-- end div container --> </div> <!-- end div middle max-full-width --> <footer> <div class="container"> <div class="pull-right"> <a href="https://www.fossil-scm.org/">Fossil $release_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> </footer> |
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 | <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> <div class='logo'> <h1>$<project_name> <th1> if {[anycap jor]} { html "<a class='rss' href='$home/timeline.rss'></a>" } </th1> <small> $<title></small></h1> </div> <!-- Main Menu --> <nav class="mainmenu" title="Main Menu"> <ul> <th1> html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a></li>\n" builtin_request_js hbmenu.js set once 1 foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {$once && [string match $url\[/?#\]* /$current_page/]} { set class "active $class" set once 0 } html "<li class='$class'>" if {[string match /* $url]} {set url $home$url} html "<a href='$url'>$name</a></li>\n" } </th1> </ul> </nav> <nav id="hbdrop" class='hbdrop' title="sitemap"></nav> </div> <!-- end div container --> </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 Hash</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/darkmode/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 | /* General settings for the entire page */ body { margin: 0ex 1ex; padding: 0; background-color: #1f1f1f; color: #ffffffe0; font-family: sans-serif; } /* The page title centered at the top of each page */ div.title { display: table-cell; font-size: 2em; font-weight: bold; text-align: center; 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: #ddddddc9; font-size: 0.8em; font-weight: bold; white-space: nowrap; } /* The leftoftitle is a <div> to the left of the title <div> ** that contains the same text as the status div. But we want ** the area to show as blank. The purpose is to cause the ** title to be exactly centered. */ div.leftoftitle { visibility: hidden; } /* The header across the top of the page */ header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ nav.mainmenu { padding: 0.25em 0.5em; font-size: 0.9em; font-weight: bold; text-align: center; border-top-left-radius: 0.5em; border-top-right-radius: 0.5em; border-bottom: 1px dotted rgba(200,200,200,0.3); z-index: 21; /* just above hbdrop */ } nav#hbdrop { background-color: #1f1f1f; border: 2px solid #303536; border-radius: 0 0 0.5em 0.5em; display: none; left: 2em; width: calc(100% - 4em); position: absolute; z-index: 20; /* just below mainmenu, but above timeline bubbles */ } nav.mainmenu, div.submenu, div.sectionmenu { color: #ffffffcc; background-color: #303536/*#0000ff60*/; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu, div.sectionmenu { padding: 0.15em 0.5em 0.15em 0; font-size: 0.9em; text-align: center; border-bottom-left-radius: 0.5em; border-bottom-right-radius: 0.5em; } a, a:visited { color: rgba(127, 201, 255, 0.9); display: inline; text-decoration: none; } a:visited {opacity: 0.8} nav.mainmenu a, div.submenu a, div.sectionmenu>a.button, div.submenu label, footer a { padding: 0.15em 0.5em; } nav.mainmenu a.active { border-bottom: 1px solid #FF4500f0; } a:hover, a:visited:hover { background-color: #FF4500f0; color: rgba(24,24,24,0.8); border-radius: 0.1em; } .fileage tr:hover, div.filetreeline:hover { background-color: #333; } .button, button { color: #aaa; background-color: #444; border-radius: 5px; border: 0 } .button:hover, button:hover { background-color: #FF4500f0; color: rgba(24,24,24,0.8); outline: 0 } input[type=button], input[type=reset], input[type=submit] { color: #ddd; background-color: #446979; border: 0; border-radius: 5px } input[type=button]:hover, input[type=reset]:hover, input[type=submit]:hover { background-color: #FF4500f0; color: rgba(24,24,24,0.8); outline: 0 } input[type=submit]:disabled { color: #363636; background-color: #707070; } .button:focus, button:focus, input[type=button]:focus, input[type=reset]:focus, input[type=submit]:focus { outline: 2px outset #333; border-color: #888; } /* All page content from the bottom of the menu or submenu down to ** the footer */ div.content { padding: 0ex 1ex 1ex 1ex; } /* Some pages have section dividers */ div.section { margin-bottom: 0; margin-top: 1em; padding: 0.1em; font-size: 1.2em; font-weight: bold; background-color: #303536/*#0000ff60*/; white-space: nowrap; border-top-left-radius: 0.5em; border-top-right-radius: 0.5em; border-bottom: 1px dotted rgba(200,200,200,0.3); } /* The "Date" that occurs on the left hand side of timelines */ div.divider { background: #303536; border: 1px #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 */ footer { clear: both; font-size: 0.8em; padding: 0.15em 0.5em; text-align: right; background-color: #303536/*#0000ff60*/; border-top: 1px dotted rgba(200,200,200,0.3); border-bottom-left-radius: 0.5em; border-bottom-right-radius: 0.5em; } /* Hyperlink colors in the footer */ pre { border-radius: 0.25em; } pre > code { display: block; } /* verbatim blocks */ pre.verbatim { padding: 0.12em; white-space: pre-wrap; } pre:not(.verbatim) { margin-left: 1rem; margin-right: 1rem; background-color: rgba(200,200,200, 0.1); padding: 0.5em 1em; } /* The label/value pairs on (for example) the ci page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } h1 {margin: 0.6em 0} h2 {margin: 0.5em 0} h3 {margin: 0.5em 0} h4 {margin: 0.5em 0} h5 {margin: 0.5em 0} /********** td.timelineTime, tr.timelineBottom td { border-bottom: 0 } table.timelineTable { border-spacing: 0.3em 0.3em; } table.timelineTable tr td { padding: 0.5em 1em; } .timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] { background-color: #ffffff40; } table.timelineTable tr td:nth-of-type(2) { background-color: #ffffffc0; } div.tl-canvas { } */ .fossil-tooltip, .fossil-toast-message { background-color: rgba(251, 106, 0, 1); border-color: rgba(127, 201, 255, 0.9); color: black; } /************************************************************************ timeline... ************************************************************************/ table.timelineTable tr:not(.timelineDateRow){ background-color: #ffffff17; } table.timelineTable tr:not(.timelineDateRow):hover{ background-color: #FF450080; } table.timelineTable tr td:first-of-type { vertical-align: middle; padding: 0.2em 0.5em; } div.timelineDate { font-weight: 700; white-space: nowrap; border-radius: 0.2em; } td.timelineTime { text-align: right; white-space: nowrap; } td.timelineGraph { width: 20px; text-align: left; border-bottom: 0 } a.timelineHistLink { /*text-transform: lowercase*/ } span.timelineComment { padding: 0 5px } .report th, span.timelineEllipsis { cursor: pointer } table.timelineTable { border-spacing: 0 0.2em; } .timelineModernCell, .timelineColumnarCell, .timelineDetailCell, .timelineCompactCell, .timelineVerboseCell { vertical-align: top; text-align: left; padding: .75em; border-radius: 0.25em; background: inherit /*#000*/; } .timelineSelected > .timelineColumnarCell, .timelineSelected > .timelineCompactCell, .timelineSelected > .timelineDetailCell, .timelineSelected > .timelineModernCell, .timelineSelected > .timelineVerboseCell { padding: .75em; border-radius: 0.2em; border: 1px solid #ff8000; vertical-align: top; text-align: left; background: #442800 } /* Timeline has a blank line at the bottom. Apparently it's to provide the graph with a good starting place. Hiding it causes a slight graph unsightliness, but we can change its bg color. */ table.timelineTable tr.timelineBottom, table.timelineTable tr.timelineBottom:hover { background: inherit; } span.timelineSelected { border-radius: 0.2em; border: 1px solid #ff8000; /*vertical-align: top; text-align: left;*/ background: #442800 } .timelineSelected { background-color: #ffffff40; } .timelineSecondary {} .timelineSecondary > .timelineColumnarCell, .timelineSecondary > .timelineCompactCell, .timelineSecondary > .timelineDetailCell, .timelineSecondary > .timelineModernCell, .timelineSecondary > .timelineVerboseCell { padding: .75em; border-radius: 5px; border: solid #0080ff; /*vertical-align: top; text-align: left;*/ background: #002844 } span.timelineSecondary { border-radius: 5px; border: solid #0080ff; /*vertical-align: top; text-align: left;*/ background: #002844 } .timelineCurrent > .timelineColumnarCell, .timelineCurrent > .timelineCompactCell, .timelineCurrent > .timelineDetailCell, .timelineCurrent > .timelineModernCell, .timelineCurrent > .timelineVerboseCell { /*vertical-align: top; text-align: left;*/ padding: .75em; border-radius: 5px; border: dashed #ff8000 } .timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] { background-color: inherit;/*#000*/ } .tl-canvas { margin: 0 6px 0 10px } .tl-rail { width: 18px } .tl-mergeoffset { width: 2px } .tl-nodemark { margin-top: .8em } .tl-node { width: 10px; height: 10px; border: 2px solid #bbb; background: #111; cursor: pointer } .tl-node.leaf:after { content: ''; position: absolute; top: 3px; left: 3px; width: 4px; height: 4px; background: #bbb } .tl-node.closed-leaf svg { position: absolute; top: 0px; left: 0px; width: 10px; height: 10px; color: #bbb; } .tl-node.sel:after { content: ''; position: absolute; top: 1px; left: 1px; width: 8px; height: 8px; background: #ff8000 } .tl-arrow { width: 0; height: 0; transform: scale(.999); border: 0 solid transparent } .tl-arrow.u { margin-top: -1px; border-width: 0 3px; border-bottom: 7px solid } .tl-arrow.u.sm { border-bottom: 5px solid #bbb } .tl-line { background: #bbb; width: 2px } .tl-arrow.merge { height: 1px; border-width: 2px 0 } .tl-arrow.merge.l { border-right: 3px solid #bbb } .tl-arrow.merge.r { border-left: 3px solid #bbb } .tl-line.merge { width: 1px } .tl-arrow.cherrypick { height: 1px; border-width: 2px 0; } .tl-arrow.cherrypick.l { border-right: 3px solid #bbb; } .tl-arrow.cherrypick.r { border-left: 3px solid #bbb; } .tl-line.cherrypick.h { width: 0px; border-top: 1px dashed #bbb; border-left: 0px dashed #bbb; background: rgba(255,255,255,0); } .tl-line.cherrypick.v { width: 0px; border-top: 0px dashed #bbb; border-left: 1px dashed #bbb; background: rgba(255,255,255,0); } /************************************************************************ diffs... ************************************************************************/ td.diffln ins { background-color: #559855; color: #000; text-decoration: none; } td.diffln del { background-color: #c55; color: #000; text-decoration: none; } td.difftxt del { background-color: inherit; text-decoration: none; } td.difftxt del > del { background-color: #c55; color: #000; text-decoration: none; } td.difftxt ins { background-color: inherit; text-decoration: none; } td.difftxt ins > ins { background-color: #559855; color: #000; text-decoration: none; } tr.diffskip.jchunk { background-color: black; } tr.diffskip > td.chunkctrl .jcbutton { background-color: #303536; } /************************************************************************ ************************************************************************/ body.wikiedit #fossil-status-bar, body.fileedit #fossil-status-bar{ border-radius: 0.25em 0.25em 0 0; } .tab-container > .tabs { border-radius: 0.25em; } blockquote.file-content { margin: 0; } blockquote.file-content > pre { padding: 0; } blockquote.file-content > pre > code { padding: 0 0.5em; } svg.pikchr { /* swap the pikchr svg colors around so they're readable in this dark theme. 2020-02: changes in fossil have made this obsolete. */ /*filter: invert(1) hue-rotate(180deg);*/ } span.snippet>mark { color: white; font-weight: bold; } button, input, optgroup, select, textarea { background: inherit; color: inherit; font: inherit; margin: 0 } button { background-color: rgba(45,45,45,0.75); } input, textarea, select { border: 1px solid rgba(127, 201, 255, 0.9); padding: 1px; } select { color: #1f1f1f; background: #ffffffe0; } .capsumOff { background-color: #222; } .capsumRead { background-color: #262; } .capsumWrite { background-color: #662; } body.forum div.forumSel { background: inherit; border-left-width: 0.5em; border-left-style: double; } body.forum .debug { background-color: #FF4500f0; color: rgba(24,24,24,0.8); } body.forum .forumPosts.fileage tr:hover { background-color: #333; color: rgba(24,24,24,0.8); } body.forum .forumPosts.fileage tr:hover { background-color: #333; color: rgba(24,24,24,0.8); } body.forum .forumPosts.fileage tr:hover > td:nth-child(1), body.forum .forumPosts.fileage tr:hover > td:nth-child(3) { color: #ffffffe0; } body.forum .forumPostBody > div blockquote { border: 1px inset; padding: 0 0.5em; } body.forum .forumPosts.fileage a:visited { color: rgba(98, 150, 205, 0.9); } body.report table.report tr td { color: black } body.report table.report a { color: blue } body.tkt td.tktDspValue { color: black } body.tkt td.tktDspValue a { color: blue } body.branch .brlist > table > tbody > tr:hover:not(.selected), body.branch .brlist > table > tbody > tr.selected { background-color: #442800; } |
Added skins/darkmode/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 0 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 1 |
Added skins/darkmode/footer.txt.
> > > > > > > > | 1 2 3 4 5 6 7 8 | <footer> <div class="container"> <div class="pull-right"> <a href="https://www.fossil-scm.org/">Fossil $release_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> </footer> |
Added skins/darkmode/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 | <header> <div class="status leftoftitle"><th1> if {[info exists login]} { set logintext "<a href='$home/login'>$login</a>\n" } else { set logintext "<a href='$home/login'>Login</a>\n" } html $logintext </th1></div> <div class="title">$<title></div> <div class="status"><nobr><th1> html $logintext </th1></nobr></div> </header> <nav class="mainmenu" title="Main Menu"> <th1> html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" builtin_request_js hbmenu.js foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {[string match /* $url]} { if {[string match $url\[/?#\]* /$current_page/]} { set class "active $class" } set url $home$url } html "<a href='$url' class='$class'>$name</a>\n" } </th1> </nav> <nav id="hbdrop" class='hbdrop' title="sitemap"></nav> |
Added skins/default/README.md.
> > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 | This skin was originally contributed by Étienne Deparis on 2015-02-22, promoted to the default on 2015-03-14, and subsequently changed by many: https://fossil-scm.org/home/finfo/skins/default/css.txt https://fossil-scm.org/home/blame?filename=skins/default/css.txt&checkin=trunk In February 2024, a sufficiently large set of changes were made to the skin that we forked the old version for the benefit of those who needed to reference the old one — as when migrating custom skin changes to work atop the new default — or who simply preferred it. See ../etienne. |
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 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 | /* Overall page style; vi: filetype=css */ body { margin: 0 auto; background-color: white; font-family: sans-serif; } a { /* Unvisited links are a lightness-adjusted version of this skin's * header blue, balancing contrast between the body text and the * background in order to meet the goals specified by the WCAG 2 * accessbility standard, earning us an "AA" grade according to * the calculator result here: * * https://webaim.org/resources/linkcontrastchecker/?fcolor=2E2E2E&bcolor=FFFFFF&lcolor=3779BF * * It is for this same reason that our not-quite-black body text * color is the shade of dark gray that it is. It can't be any * lighter and still allow us to meet both targets. */ color: #3779BF; text-decoration: none; } a:hover { color: #4183C4; text-decoration: underline; } /* Page title, above menu bars */ .title { color: #4183C4; float: left; } h1.page-title { font-size: 1.60em; /* match content > h1 */ margin-bottom: 0; /* div.content top margin suffices */ display: none; /* don't use body-area h1 except… */ } .artifact h1.page-title, .dir h1.page-title, .doc h1.page-title, .wiki h1.page-title { display: block; /* …for potentially long doc titles… */ } .artifact .title > .page-title, .dir .title > .page-title, .doc .title > .page-title, .wiki .title > .page-title { display: none; /* …where we suppress the title area h1 instead */ } .title h1 { display: inline; font-size: 2.20em; } .title h1:after { content: " / "; color: #777; font-weight: normal; } .artifact .title h1:after, .dir .title h1:after, .doc .title h1:after, .wiki .title h1:after { content: ""; /* hide solidus for docs along with title h1 */ } .status { float: right; font-size: 0.8em; } div.logo { float: left; padding-right: 10px; } div.logo img { max-height: 2em; /* smaller than title to keep it above the baseline */ } /* Main menu and optional sub-menu */ .mainmenu { clear: both; background: #eaeaea linear-gradient(#fafafa, #eaeaea) repeat-x; border: 1px solid #eaeaea; border-radius: 5px; overflow-x: auto; overflow-y: hidden; white-space: nowrap; z-index: 21; /* just above hbdrop */ } .mainmenu a { text-decoration: none; color: #777; border-right: 1px solid #eaeaea; } .mainmenu a.active, .mainmenu a:hover { color: #000; border-bottom: 2px solid #D26911; } nav#hbdrop { background-color: white; border: 1px solid black; border-top: white; border-radius: 0 0 0.5em 0.5em; display: none; font-size: 80%; left: 2em; width: 90%; padding-right: 1em; position: absolute; z-index: 20; /* just below mainmenu, but above timeline bubbles */ } .submenu { font-size: 0.8em; padding: 10px; border-bottom: 1px solid #ccc; } .submenu a, .submenu label { padding: 10px 11px; text-decoration: none; color: #777; } .submenu label { white-space: nowrap; } .submenu a:hover, .submenu label:hover { padding: 6px 10px; border: 1px solid #ccc; border-radius: 5px; color: #000; } span.submenuctrl, span.submenuctrl input, select.submenuctrl { color: #777; } span.submenuctrl { white-space: nowrap; } /* Main document area; elements common to most pages. */ .content { padding: 1ex; color: #2e2e2e; /* justified above in "WCAG 2" comment */ } .content h1 { font-size: 1.60em; color: #444; } .content h2 { font-size: 1.45em; color: #444; } .content h3 { font-size: 1.15em; color: #444; } .content h4 { font-size: 1.05em; color: #444; } .content h5 { font-size: 1.00em; color: #444; } .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; } hr { color: #eee; } /* Page footer */ footer { border-top: 1px solid #ccc; padding: 10px; font-size: 0.8em; margin-top: 10px; color: #ccc; } /* Forum */ .forum a:visited { color: #6A7F94; } div.forumSel { animation: 1s linear 0s sel-fade; background-color: white; /* animation end state */ border-left: 4px solid black; /* after-animation selection indicator */ } @keyframes sel-fade { from { background-color: #cef; } to { background-color: white; } } .forum form input { margin: 0.5em 0; } /* Markdown and Wiki-formatted pages: /wiki, /doc, /file... */ .markdown blockquote, p.blockquote, .sidebar { /* Override default.css version with our accent colors. Background is * the solid version of rgba(65, 131, 196, 0.1) on white, needed to * avoid tinting pre block backgrounds going "under" them. */ background-color: #ebf2f9; border-left-color: #4183c4; } div.sidebar { /* Add extra whitespace between sidebar and content, both for spacing * and to put a gap between it and any <pre> blocks that happen to run * up against it. */ outline: 1em solid white; } /* Mark inline code fragments in the near-universal manner pioneered by * Stack Overflow, then picked up by approximately everyone, including * us, now. * * This combinatorial selector explosion results from a need to apply * these stylings inside multiple page container types, multiplied by * the surprisingly large number of tags HTML defines for semantically * differentiated monospaced inline markup. If we do not target the * elements we want to affect carefully, we'll end up overreaching, * styling Fossil UI elements that use these tags for local purposes. * * HTML generated and emitted by Fossil UI does not always fall under * the skin's generic rules; we must avoid intruding on its domain. * Our limited intent here is to style user content only, where it is * unreasonable to expect its author to take the time to hand-craft * per-document styling. Contrast Fossil UI, which often does exactly * that in order to get particular results. * * Its rough equivalent in Sass syntax is far more compact, thus clearer: * * .artifact, .dir, .doc, .forum, .wiki // the page types we target * > .content // hands off header & footer * &, > .fossil-doc, > .markdown // wiki, HTML & MD emb docs * > p // in top-level paras only * > code, > kbd, > samp, > tt, > var // monospaced tag types * background-color: #f4f4f4 // pale gray box which… * padding: 0 4px // …extends around the sides * * We then need something similar for the block-level pre elements. * * The CSS below is based on feeding that Sass code through this: * * $ sassc code.sass | sed -e 's/, /,\n/g' * * …then hand-cleansing it to make it _somewhat_ more understandable. * That largely amounts to whitespace tweaks, but we've also done things * like trim back the forum-specific styling to apply to the default MD * markup only; direct HTML formatting isn't even an option there, and * while wiki markup _is_ supported, MD was the default from day 1. * Another quirk of the forum post handling is that the .markdown class * gets applied per-post, not up at the top level as with the wiki, * embedded docs, etc. */ .artifact > .content > p > code, .artifact > .content > p > kbd, .artifact > .content > p > samp, .artifact > .content > p > tt, .artifact > .content > p > var, .artifact > .content > .fossil-doc > p > code, .artifact > .content > .fossil-doc > p > kbd, .artifact > .content > .fossil-doc > p > samp, .artifact > .content > .fossil-doc > p > tt, .artifact > .content > .fossil-doc > p > var, .artifact > .content > .markdown > p > code, .artifact > .content > .markdown > p > kbd, .artifact > .content > .markdown > p > samp, .artifact > .content > .markdown > p > tt, .artifact > .content > .markdown > p > var, .dir > .content > p > code, .dir > .content > p > kbd, .dir > .content > p > samp, .dir > .content > p > tt, .dir > .content > p > var, .dir > .content > .fossil-doc > p > code, .dir > .content > .fossil-doc > p > kbd, .dir > .content > .fossil-doc > p > samp, .dir > .content > .fossil-doc > p > tt, .dir > .content > .fossil-doc > p > var, .dir > .content > .markdown > p > code, .dir > .content > .markdown > p > kbd, .dir > .content > .markdown > p > samp, .dir > .content > .markdown > p > tt, .dir > .content > .markdown > p > var, .doc > .content > p > code, .doc > .content > p > kbd, .doc > .content > p > samp, .doc > .content > p > tt, .doc > .content > p > var, .doc > .content > .fossil-doc > p > code, .doc > .content > .fossil-doc > p > kbd, .doc > .content > .fossil-doc > p > samp, .doc > .content > .fossil-doc > p > tt, .doc > .content > .fossil-doc > p > var, .doc > .content > .markdown > p > code, .doc > .content > .markdown > p > kbd, .doc > .content > .markdown > p > samp, .doc > .content > .markdown > p > tt, .doc > .content > .markdown > p > var, .forum > .content .markdown > p > code, .forum > .content .markdown > p > kbd, .forum > .content .markdown > p > samp, .forum > .content .markdown > p > tt, .forum > .content .markdown > p > var, .wiki > .content > p > code, .wiki > .content > p > kbd, .wiki > .content > p > samp, .wiki > .content > p > tt, .wiki > .content > p > var, .wiki > .content > .fossil-doc > p > code, .wiki > .content > .fossil-doc > p > kbd, .wiki > .content > .fossil-doc > p > samp, .wiki > .content > .fossil-doc > p > tt, .wiki > .content > .fossil-doc > p > var, .wiki > .content > .markdown > p > code, .wiki > .content > .markdown > p > kbd, .wiki > .content > .markdown > p > samp, .wiki > .content > .markdown > p > tt, .wiki > .content > .markdown > p > var, .artifact > .content > pre, .artifact > .content > .fossil-doc > pre, .artifact > .content > .markdown > pre, .dir > .content > pre, .dir > .content > .fossil-doc > pre, .dir > .content > .markdown > pre, .doc > .content > pre, .doc > .content > .fossil-doc > pre, .doc > .content > .markdown > pre, .forum > .content .markdown > pre, .wiki > .content > pre, .wiki > .content > .fossil-doc > pre, .wiki > .content > .markdown > pre { background-color: #f4f4f4; padding: 0 4px; } .content pre, table.numbered-lines > tbody > tr { hyphens: none; line-height: 1.25; } .content ul li { list-style-type: disc; } .artifact > .content table, .dir > .content table, .doc > .content table { background-color: #f0f5f9; border: 1px solid #a7c2dc; border-radius: 0.5em; border-spacing: 0; padding: 6px; } .artifact > .content th, .dir > .content th, .doc > .content th { border-bottom: 1px solid #dee8f2; padding-bottom: 4px; padding-right: 6px; text-align: left; } .artifact > .content tr > th, .dir > .content tr > th, .doc > .content tr > th { background-color: #dee8f0; } .artifact > .content tr:nth-child(odd), .dir > .content tr:nth-child(odd), .doc > .content tr:nth-child(odd) { background-color: #e0e8ee; } .artifact > .content td, .dir > .content td, .doc > .content td { padding-bottom: 4px; padding-right: 6px; text-align: left; } /* Wiki adjustments */ pre.verbatim { /* keep code examples from crashing into sidebars, etc. */ white-space: pre-wrap; } textarea.wikiedit { /* Monospace fonts tend to have smaller x-heights; compensate. * Can't do this generally because not all fonts have this problem. * A textarea stands alone, whereas inline <code> has to work with * the browser's choice of sans-serif proportional font. */ font-size: 1.1em; } /* Tickets */ table.report { cursor: auto; border: 1px solid #ccc; border-radius: 0.5em; 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; } /* Timeline */ span.timelineDetail { font-size: 90%; } div.timelineDate { font-weight: bold; white-space: nowrap; } /* Extend default.css comment cell rounding to the whole row for the * various types of "selected" rows, making them "hang" into the left * margin, distinguishing them from the coloring used for branch cells. * Care must be taken to avoid having the box-shadow rounded but the * background squared-off. */ table.timelineTable { padding: 0 3px; /* leave space to sides for box shadow; can clip otherwise */ } table.timelineTable tr { border-radius: 1em; } tr.timelineSelected, tr.timelineSecondary { background-color: unset; } tr.timelineSelected td, span.timelineSelected { background-color: #fbe8d5; } tr.timelineSecondary td, span.timelineSecondary { background-color: #d5e8fb; } tr.timelineCurrent td:first-child, tr.timelineSecondary td:first-child, tr.timelineSelected td:first-child { border-top-left-radius: 1em; border-bottom-left-radius: 1em; } tr.timelineCurrent td:last-child, tr.timelineSecondary td:last-child, tr.timelineSelected td:last-child { border-top-right-radius: 1em; border-bottom-right-radius: 1em; } tr.timelineCurrent td { border-top: 1px dashed #446979; border-bottom: 1px dashed #446979; } tr.timelineCurrent td:first-child { border-left: 1px dashed #446979; } tr.timelineCurrent td:last-child { border-right: 1px dashed #446979; } /* Miscellaneous UI elements */ .fossil-tooltip.help-buttonlet-content { background-color: lightyellow; } /* Exceptions for specific screen sizes */ @media screen and (max-width: 600px) { /* Spacing for mobile */ body { padding-left: 4px; padding-right: 4px; } .content { font-size: 0.9em; } .title { padding-top: 0px; padding-bottom: 0px; } .title > .page-title { display: inline; /* show page titles above menu bar… */ } .artifact .title > .page-title, .dir .title > .page-title, .doc .title > .page-title, .wiki .title > .page-title { display: none; /* …except for docs, where it may force wrapping */ } .status {padding-top: 0px;} .mainmenu a { padding: 8px 10px; } .mainmenu { padding: 10px; } } @media screen and (min-width: 600px) { /* Spacing for desktop */ body { padding-left: 20px; padding-right: 20px; } .title { padding-top: 10px; padding-bottom: 10px; } span.page-title { font-size: 18px; } div.logo { padding-top: 10px; } .status {padding-top: 30px;} .mainmenu a { padding: 8px 20px; } .mainmenu { padding: 10px; } /* Wide screens mean long lines. Add extra leading to give the eye a * "gutter" to follow from the end of one to the start of the next. */ .content dd, .content dt, .content div, .content li, .content p, .content table { line-height: 1.4em; } /* This horror show has the same cause that informed our handling of * <code> and friends above; see "combinatorial selector explosion." * Without this careful targeting, we'd not only overreach into areas * of Fossil UI where our meddling is not wanted, we would mistakenly * apply double indents to nested formatting in MD forum posts, p * within td tags, and more. * * Rather than give the equivalent Sass code here, see the SCSS file * that the [Inskinerator](https://tangentsoft.com/inskinerator/) * project ships as override/modern/media.scss. Rendering that * through sassc gives substantially identical output, modulo the * hand-polishing we've done here. */ .artifact > .content > p, .artifact > .content > .markdown > p, .artifact > .content > .fossil-doc > p, .artifact > .content > ol, .artifact > .content > ul, .artifact > .content > .markdown > ol, .artifact > .content > .markdown > ul, .artifact > .content > .fossil-doc > ol, .artifact > .content > .fossil-doc > ul, .artifact > .content > table, .artifact > .content > .markdown > table, .artifact > .content > .fossil-doc > table, .dir > .content > p, .dir > .content > .markdown > p, .dir > .content > .fossil-doc > p, .dir > .content > ol, .dir > .content > ul, .dir > .content > .markdown > ol, .dir > .content > .markdown > ul, .dir > .content > .fossil-doc > ol, .dir > .content > .fossil-doc > ul, .dir > .content > table, .dir > .content > .markdown > table, .dir > .content > .fossil-doc > table, .doc > .content > p, .doc > .content > .markdown > p, .doc > .content > .fossil-doc > p, .doc > .content > ol, .doc > .content > ul, .doc > .content > .markdown > ol, .doc > .content > .markdown > ul, .doc > .content > .fossil-doc > ol, .doc > .content > .fossil-doc > ul, .doc > .content > table, .doc > .content > .markdown > table, .doc > .content > .fossil-doc > table, .wiki > .content > p, .wiki > .content > .markdown > p, .wiki > .content > .fossil-doc > p, .wiki > .content > ol, .wiki > .content > ul, .wiki > .content > .markdown > ol, .wiki > .content > .markdown > ul, .wiki > .content > .fossil-doc > ol, .wiki > .content > .fossil-doc > ul, .wiki > .content > table, .wiki > .content > .markdown > table, .wiki > .content > .fossil-doc > table, #fileedit-tab-preview-wrapper > p, #fileedit-tab-preview-wrapper > ol, #fileedit-tab-preview-wrapper > ul, #fileedit-tab-preview-wrapper > table, #fileedit-tab-preview-wrapper > .markdown > p, #fileedit-tab-preview-wrapper > .markdown > ol, #fileedit-tab-preview-wrapper > .markdown > ul, #fileedit-tab-preview-wrapper > .markdown > table, #wikiedit-tab-preview-wrapper > p, #wikiedit-tab-preview-wrapper > ol, #wikiedit-tab-preview-wrapper > ul, #wikiedit-tab-preview-wrapper > table, #wikiedit-tab-preview-wrapper > .markdown > p, #wikiedit-tab-preview-wrapper > .markdown > ol, #wikiedit-tab-preview-wrapper > .markdown > ul, #wikiedit-tab-preview-wrapper > .markdown > table { margin-left: 50pt; margin-right: 50pt; } /* Code blocks get extra indenting. We need a selector explosion * equally powerful to the one above for inline <code> fragments and * similar elements, for essentially the same reason: Fossil UI also * uses <pre>, and we want to affect user content only. * * The equivalent Sass code is: * * .artifact, .dir, .doc, .wiki // doc types we target * > .content // hands off header & footer * @import 'pre-doc-margins.sass' * * #fileedit-tab-preview-wrapper, // include /fileedit previews * #wikiedit-tab-preview-wrapper // ditto /wikiedit * @import 'pre-doc-margins.sass' * * …where pre-doc-margins.sass contains the elements common to both: * * &, > .fossil-doc, > .markdown // wiki, HTML & MD doc types * > pre // direct pre descendants only * margin-left: 70pt; * margin-right: 50pt; * * This is a technical overreach since /wiki & /wikiedit lack support * for Fossil's HTML embedded doc markup capability, but we prefer to * draw the /fileedit parallel in our Sass example over the dubious * pleasure of being nit-picky on this point. Instead, we've chosen * to back that overreach out by hand below. */ .artifact > .content > pre, .artifact > .content > .fossil-doc > pre, .artifact > .content > .markdown > pre, .dir > .content > pre, .dir > .content > .fossil-doc > pre, .dir > .content > .markdown > pre, .doc > .content > pre, .doc > .content > .fossil-doc > pre, .doc > .content > .markdown > pre, .wiki > .content > pre, .wiki > .content > .markdown > pre { margin-left: 70pt; margin-right: 50pt; } #fileedit-tab-preview-wrapper > pre, #wikiedit-tab-preview-wrapper > pre, #fileedit-tab-preview-wrapper > .fossil-doc > pre, #fileedit-tab-preview-wrapper > .markdown > pre, #wikiedit-tab-preview-wrapper > .markdown > pre { margin-left: 70pt; margin-right: 50pt; } .forum > .content .markdown > pre { margin-left: 20pt; /* special case for MD in forum; need less indent */ } /* Fossil UI uses these, but in sufficiently constrained ways that we * don't have to be nearly as careful to avoid an overreach. */ .doc > .content h1, .artifact h1, .dir h1, .fileedit h1, .wiki h1 { margin-left: 10pt; } .doc > .content h2, .artifact h2, .dir h2, .fileedit h2, .wiki h2 { margin-left: 20pt; } .doc > .content h3, .artifact h3, .dir h3, .fileedit h3, .wiki h3 { margin-left: 30pt; } .doc > .content h4, .artifact h4, .dir h4, .fileedit h4, .wiki h4 { margin-left: 40pt; } .doc > .content h5, .artifact h5, .dir h5, .fileedit h5, .wiki h5 { margin-left: 50pt; } .doc > .content hr, .artifact hr, .dir hr, .fileedit hr, .wiki hr { margin-left: 10pt; } /* Don't need to be nearly as careful with tags Fossil UI doesn't use. */ .doc dd, .artifact dd, .dir dd, .fileedit dd, .wikiedit dd { margin-left: 30pt; margin-bottom: 1em; } .doc dl, .artifact dl, .dir dl, .fileedit dl, .wikiedit dl { margin-left: 60pt; } .doc dt, .artifact dt, .dir dt, .fileedit dt, .wikiedit dt { margin-left: 10pt; } /* Fossil UI doesn't use Pikchr at all (yet?) so we can be quite loose * with these selectors. */ .content .pikchr-wrapper { margin-left: 70pt; } div.pikchr-wrapper.indent:not(.source) { /* Selector naming scheme mismatch is intentional: it must match the * way it's given in default.css exactly if it is to override it. */ margin-left: 70pt; margin-right: 50pt; } div.pikchr-wrapper.center:not(.source), div.pikchr-wrapper.float-right:not(.source) { margin-left: 0; } /* Special treatment for backward compatibility. */ .indent, /* clean alternative to misusing <blockquote> */ .artifact > .content > blockquote:not(.file-content), .dir > .content > blockquote, .doc > .content > blockquote, .fileedit > .content > blockquote, .wiki > .content > blockquote { /* We must apply extra indent relative to "p" since Fossil's wiki * generator misuses the blockquote tag against HTML and MD norms * to mean "indented paragraph." Skip it for file content retrieved * by /dir URLs. */ margin-left: 80pt; } .artifact > .content > .markdown > blockquote, .dir > .content > .markdown > blockquote, .doc > .content > .markdown > blockquote, .fileedit > .content > .markdown > blockquote, .wiki > .content > .markdown > blockquote { /* Fossil MD didn't inherit that bug; its HTML generator emits * blockquote tags only for _block quotes_! A moderate indent * suffices due to the visual styling applied above. */ margin-left: 60pt; } /* Alternative to BLOCK.indent when wrapped in something that is * itself indented. The value is the delta between p and blockquote * above, expressed as padding instead of margin so it adds to the * outer margin instead of forcing the browser into picking one. */ .local-indent { padding-left: 30pt; } } |
Added skins/default/details.txt.
> > > > > > | 1 2 3 4 5 6 | pikchr-fontscale: "0.9" pikchr-scale: "1.1" timeline-arrowheads: 1 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 |
Added skins/default/footer.txt.
> > > > > | 1 2 3 4 5 | <footer> This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by Fossil $release_version $manifest_version $manifest_date </footer> |
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 57 58 59 60 61 62 63 64 65 66 67 | <header> <div class="logo"> <th1> ## See skins/original/header.txt for commentary; not repeated here. proc getLogoUrl { baseurl } { set idx(first) [string first // $baseurl] if {$idx(first) != -1} { set idx(first+1) [expr {$idx(first) + 2}] set idx(nextRange) [string range $baseurl $idx(first+1) end] set idx(next) [string first / $idx(nextRange)] if {$idx(next) != -1} { set idx(next) [expr {$idx(next) + $idx(first+1)}] set idx(next-1) [expr {$idx(next) - 1}] set scheme [string range $baseurl 0 $idx(first)] set host [string range $baseurl $idx(first+1) $idx(next-1)] 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"> <h1>$<project_name></h1> <span class="page-title">$<title></span> </div> <div class="status"> <th1> if {[info exists login]} { html "<a href='$home/login'>$login</a>\n" } else { html "<a href='$home/login'>Login</a>\n" } </th1> </div> </header> <nav class="mainmenu" title="Main Menu"> <th1> html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" builtin_request_js hbmenu.js foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {[string match /* $url]} { if {[string match $url\[/?#\]* /$current_page/]} { set class "active $class" } set url $home$url } html "<a href='$url' class='$class'>$name</a>\n" } </th1> </nav> <nav id="hbdrop" class='hbdrop' title="sitemap"></nav> <h1 class="page-title">$<title></h1> |
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 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 | /* General settings for the entire page */ body { margin: 0ex 1ex; padding: 0px; background-color: #485D7B; font-family: sans-serif; color: white; } /* 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; min-width: 50px; white-space: nowrap; position: relative; filter: drop-shadow(2px 4px 6px rgba(0,0,0,0.75)); top: 0.5em; right: -0.5em; } div.logo img{ border-radius: 2mm; } /* 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; white-space: nowrap; } /* The header across the top of the page */ header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ nav.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; } nav#hbdrop { background-color: #485D7B; border-radius: 0 0 15px 15px; border-left: 0.5em solid #76869d; border-bottom: 1.2em solid #76869d; display: none; width: 98%; position: absolute; z-index: 20; } /* 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; } nav.mainmenu a, nav.mainmenu a:visited, div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited, div.submenu label { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } nav.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover, div.submenu label: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 */ 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-spacing: 0px 2px; // border-collapse: collapse; } .timelineSelected { background-color: #7EA2D9; } .timelineSecondary { background-color: #7EA27E; } /* commit node */ .tl-node { width: 10px; height: 10px; border: 1px solid #fff; background: #485D7B; cursor: pointer; } /* leaf commit marker */ .tl-node.leaf:after { content: ''; position: absolute; top: 3px; left: 3px; width: 4px; height: 4px; background: #fff; } /* closed leaf commit marker */ .tl-node.closed-leaf svg { position: absolute; top: 0px; left: 0px; width: 10px; height: 10px; color: #fff; } /* up arrow */ .tl-arrow.u { margin-top: -1px; border-width: 0 3px; border-bottom: 7px solid #fff; } /* small up arrow */ .tl-arrow.u.sm { border-bottom: 5px solid #fff; } /* line */ .tl-line { background: #fff; width: 2px; } /* left merge arrow */ .tl-arrow.merge.l { border-right: 3px solid #fff; } /* right merge arrow */ .tl-arrow.merge.r { border-left: 3px solid #fff; } .tl-arrow.cherrypick { height: 1px; border-width: 2px 0; } .tl-arrow.cherrypick.l { border-right: 3px solid #fff; } .tl-arrow.cherrypick.r { border-left: 3px solid #fff; } .tl-line.cherrypick.h { width: 0px; border-top: 1px dashed #fff; border-left: 0px dashed #fff; background: rgba(255,255,255,0); } .tl-line.cherrypick.v { width: 0px; border-top: 0px dashed #fff; border-left: 1px dashed #fff; background: rgba(255,255,255,0); } /* Side-by-side diff */ table.splitdiff { background-color: #485D7B; font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace; font-size: 8pt; border-collapse:collapse; white-space: pre; 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; } /* Ticket display on timelines */ td.tktTlOpen { color: #ffc0c0; } td.tktTlClose { color: #c0c0c0; } /* 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.splitdiff { font-family: monospace; font-size: smaller; white-space: pre; } /* context diff display */ table.udiff { font-family: monospace; white-space: pre; } /* added code in a diff */ td.difftxt ins > ins, td.diffln ins { background-color: rgb(100, 200, 100); } td.difftxt ins { background-color: inherit; } /* deleted in a diff */ td.difftxt del > del, td.diffln del { background-color: rgb(230, 110, 110); } td.difftxt del { background-color: inherit; } tr.diffskip.jchunk { background-color: #7EA2D9; } tr.diffskip > td.chunkctrl .jcbutton{ color: white; background-color: #485D7B; } .fileage tr:hover { background-color: #7EA2D9; } span.modpending { color: #c0c0c0; font-style: italic; } span.forum_author { color: white; font-size: 75%; } span.forum_age { color: white; font-size: 85%; } span.forum_npost { color: white; font-size: 75%; } .debug { background-color: #808080; border: 2px solid white; } div.forumEdit { border: 1px solid white; } div.forumTimeline { border: 1px solid white; } div.forumTime { border: 1px solid white; } div.forumSel { background-color: #808080; } div.forumObs { color: white; } body.forum .forumPosts.fileage a:visited { color: rgba(176,176,176,1.0); } .fileage td { font-family: "courier new"; } div.filetreeline:hover { background-color: #7EA2D9; } table.numbered-lines td.line-numbers span.selected-line { background-color: #7EA2D9; } .statistics-report-graph-line { border: 2px solid #7EA2D9; background-color: #7EA2D9; } .statistics-report-graph-extra { border: 2px solid #7EA2D9; border-left-style: none; } .timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] { background-color: #455978; } .capsumOff { background-color: #bbbbbb; } .capsumRead { background-color: #006d00; } .capsumWrite { background-color: #e5e500; } body.branch .brlist > table > tbody > tr:hover:not(.selected), body.branch .brlist > table > tbody > tr.selected { background-color: #7EA2D9; } |
Added skins/eagle/details.txt.
> > > > > | 1 2 3 4 5 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 1 pikchr-background: 0x485d7b |
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 | <footer> <th1> proc getTclVersion {} { if {[catch {tclEval info patchlevel} tclVersion] == 0} { return "<a href=\"https://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> </footer> |
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 | <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 } if {1} { # Link logo to the top of the current domain set logourl [getLogoUrl $baseurl] } else { # Link logo to the top of the current repo set logourl $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> </header> <th1>html "<script nonce='$nonce'>"</th1> (function updateClock(){ var e = document.getElementById("clock"); if(!e) return; if(!updateClock.fmt){ updateClock.fmt = function(n){ return n < 10 ? '0' + n : n; }; } var d = new Date(); e.innerHTML = d.getUTCFullYear()+ '-' + updateClock.fmt(d.getUTCMonth() + 1) + '-' + updateClock.fmt(d.getUTCDate()) + ' ' + updateClock.fmt(d.getUTCHours()) + ':' + updateClock.fmt(d.getUTCMinutes()); setTimeout(updateClock,(60-d.getUTCSeconds())*1000); })(); </script> <nav class="mainmenu" title="Main Menu"> <th1> html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>\n" builtin_request_js hbmenu.js foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {[string match /* $url]} {set url $home$url} html "<a href='$url' class='$class'>$name</a>\n" } </th1> </nav> <nav id="hbdrop" class='hbdrop' title="sitemap"></nav> |
Added skins/etienne/README.md.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | This skin was contributed by Étienne Deparis. It was promoted to the default from 2015-03-14 until February 2024, when it was forked into this location for use by those who do not want the large number of changes merged into trunk at that time. Even if you agree with us that the changes improve readability, you may prefer to pack more information onto the screen at the expense of readability. Other reasons to choose this fork are to migrate custom skin changes to work atop the new base, or to make a comparative design evaluation. A bare minimum of changes have been made to this fork, primarily to allow this skin to render the Fossil documentation in a readable fashion. The intent is that you be able to toggle between these two skins at will. |
Added skins/etienne/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 | /* Overall page style; vi: filetype=css */ body { margin: 0 auto; background-color: white; font-family: sans-serif; font-size: 14pt; } a { color: #4183C4; text-decoration: none; } a:hover { color: #4183C4; text-decoration: underline; } /* Page title, above menu bars */ .title { color: #4183C4; float: left; } .title h1 { display: inline; } .title h1:after { content: " / "; color: #777; font-weight: normal; } .status { float: right; font-size: 0.7em; } /* Main menu and optional sub-menu */ .mainmenu { font-size: 0.8em; clear: both; background: #eaeaea linear-gradient(#fafafa, #eaeaea) repeat-x; border: 1px solid #eaeaea; border-radius: 5px; overflow-x: auto; overflow-y: hidden; white-space: nowrap; z-index: 21; /* just above hbdrop */ } .mainmenu a { text-decoration: none; color: #777; border-right: 1px solid #eaeaea; } .mainmenu a.active, .mainmenu a:hover { color: #000; border-bottom: 2px solid #D26911; } nav#hbdrop { background-color: white; border: 1px solid black; border-top: white; border-radius: 0 0 0.5em 0.5em; display: none; font-size: 80%; left: 2em; width: 90%; padding-right: 1em; position: absolute; z-index: 20; /* just below mainmenu, but above timeline bubbles */ } .submenu { font-size: .7em; padding: 10px; border-bottom: 1px solid #ccc; } .submenu a, .submenu label { padding: 10px 11px; text-decoration: none; color: #777; } .submenu label { white-space: nowrap; } .submenu a:hover, .submenu label:hover { padding: 6px 10px; border: 1px solid #ccc; border-radius: 5px; color: #000; } span.submenuctrl, span.submenuctrl input, select.submenuctrl { color: #777; } span.submenuctrl { white-space: nowrap; } /* Main document area; elements common to most pages. */ .content { padding-top: 10px; font-size: 0.8em; color: #444; } .content blockquote { padding: 0 15px; } .content h1 { font-size: 1.25em; } .content h2 { font-size: 1.15em; } .content h3 { font-size: 1.05em; } .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; } hr { color: #eee; } /* Page footer */ footer { border-top: 1px solid #ccc; padding: 10px; font-size: 0.7em; margin-top: 10px; color: #ccc; } /* Forum */ .forum a:visited { color: #6A7F94; } .forum blockquote { background-color: rgba(65, 131, 196, 0.1); border-left: 3px solid #254769; padding: .1em 1em; } /* Markdown and Wiki-formatted pages: /wiki, /doc, /file... */ .doc > .content table { background-color: rgba(0, 0, 0, 0.05); border: 1px solid #aaa; border-radius: 0.5em; border-spacing: 0; padding: 6px; } .doc > .content th { border-bottom: 1px solid #ddd; padding-bottom: 4px; padding-right: 6px; text-align: left; } .doc > .content tr > th { background-color: #eee; } .doc > .content tr:nth-child(odd) { background-color: #e8e8e8; } .doc > .content td { padding-bottom: 4px; padding-right: 6px; text-align: left; } /* Tickets */ 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; } /* Timeline */ span.timelineDetail { font-size: 90%; } div.timelineDate { font-weight: bold; white-space: nowrap; } /* Miscellaneous UI elements */ .fossil-tooltip.help-buttonlet-content { background-color: lightyellow; } /* Exceptions for specific screen sizes */ @media screen and (max-width: 600px) { /* Spacing for mobile */ body { padding-left: 4px; padding-right: 4px; } .title { padding-top: 0px; padding-bottom: 0px; } .status {padding-top: 0px;} .mainmenu a { padding: 8px 10px; } .mainmenu { padding: 10px; } } @media screen and (min-width: 600px) { /* Spacing for desktop */ body { padding-left: 20px; padding-right: 20px; } .title { padding-top: 10px; padding-bottom: 10px; } .status {padding-top: 30px;} .mainmenu a { padding: 8px 20px; } .mainmenu { padding: 10px; } } |
Added skins/etienne/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 |
Added skins/etienne/footer.txt.
> > > > > | 1 2 3 4 5 | <footer> This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by Fossil $release_version $manifest_version $manifest_date </footer> |
Added skins/etienne/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 | <header> <div class="title"><h1>$<project_name></h1>$<title></div> <div class="status"> <th1> if {[info exists login]} { html "<a href='$home/login'>$login</a>\n" } else { html "<a href='$home/login'>Login</a>\n" } </th1> </div> </header> <nav class="mainmenu" title="Main Menu"> <th1> html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" builtin_request_js hbmenu.js foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {[string match /* $url]} { if {[string match $url\[/?#\]* /$current_page/]} { set class "active $class" } set url $home$url } html "<a href='$url' class='$class'>$name</a>\n" } </th1> </nav> <nav id="hbdrop" class='hbdrop' title="sitemap"></nav> |
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 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 | /* General settings for the entire page */ body { margin: 0ex 0ex; padding: 0px; background-color: #fef3bc; font-family: sans-serif; 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 */ header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ nav.mainmenu { padding: 5px 10px 5px 10px; font-size: 0.9em; font-weight: bold; text-align: center; letter-spacing: 1px; background-color: #a09048; color: black; z-index: 21; /* just above hbdrop */ } nav#hbdrop { background-color: #fef3bc; border: 2px solid #a09048; border-radius: 0 0 0.5em 0.5em; display: none; left: 2em; width: 90%; padding-right: 1em; position: absolute; z-index: 20; /* just below mainmenu, but above timeline bubbles */ } /* 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; } nav.mainmenu a, nav.mainmenu a:visited, div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited, div.submenu label { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } nav.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover, div.submenu label:hover, nav#hbdrop a: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, nav#hbdrop a { color: #706532; } div.content a:link, nav#hbdrop a:link { color: #706532; } div.content a:visited, nav#hbdrop a:visited { color: #704032; } div.content a:hover, nav#hbdrop a:hover { background-color: white; color: #706532; } a, a:visited { text-decoration: none; } /* 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 */ footer { font-size: 0.8em; margin-top: 12px; padding: 5px 10px 5px 10px; text-align: right; background-color: #a09048; color: white; } /* Hyperlink colors */ footer a { color: white; } footer a:link { color: white; } footer a:visited { color: white; } 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; } div.forumPostBody blockquote { border-width: 1pt; border-radius: 0.25em; border-style: solid; padding: 0 0.5em; } tr.diffskip > td.chunkctrl .jcbutton { color: white; background-color: #a09048; } tr.diffskip.jchunk { background-color: #c0af58; } |
Added skins/khaki/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 |
Added skins/khaki/footer.txt.
> > > | 1 2 3 | <footer> Fossil $release_version $manifest_version $manifest_date </footer> |
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 | <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> </header> <nav class="mainmenu" title="Main Menu"> <th1> html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" builtin_request_js hbmenu.js foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {[string match /* $url]} {set url $home$url} html "<a href='$url' class='$class'>$name</a>\n" } </th1> </nav> <nav id="hbdrop" class='hbdrop' title="sitemap"></nav> |
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 149 | /* 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: 50px; 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; white-space: nowrap; } /* The header across the top of the page */ header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ nav.mainmenu { padding: 5px; 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; } nav.mainmenu a, nav.mainmenu a:visited, div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited, div.submenu label { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } nav.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover, div.submenu label: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 */ 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 */ footer a { color: white; } footer a:link { color: white; } footer a:visited { color: white; } 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 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <footer> <th1> proc getTclVersion {} { if {[catch {tclEval info patchlevel} tclVersion] == 0} { return "<a href=\"https://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> </footer> |
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 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 | <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> </header> <th1>html "<script nonce='$nonce'>"</th1> 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> <nav class="mainmenu" title="Main Menu"> <th1> set sitemap 0 foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {[string match /* $url]} {set url $home$url} html "<a href='$url' class='$class'>$name</a>\n" if {[string match */sitemap $url]} {set sitemap 1} } if {!$sitemap} { html "<a href='$home/sitemap'>...</a>" } </th1> </nav> |
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 143 144 145 146 147 148 149 150 | /* General settings for the entire page */ body { margin: 0ex 1ex; padding: 0px; background-color: white; font-family: sans-serif; text-size-adjust: none; } /* 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-weight: bold; white-space: nowrap; } /* The header across the top of the page */ header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ nav.mainmenu { padding: 5px 10px 5px 10px; font-size: 0.9em; font-weight: bold; text-align: center; letter-spacing: 1px; background-color: #404040; color: white; z-index: 21; /* just above hbdrop */ } .hbdrop { background-color: white; border: 1px solid black; border-radius: 0.5em; display: none; width: 95%; position: absolute; z-index: 20; /* just below mainmenu, but above timeline bubbles */ } div.hbdrop a { color: #604000; } div.hbdrop a:link { color: #604000;} div.hbdrop a:visited { color: #600000; } /* 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; } nav.mainmenu a, nav.mainmenu a:visited, div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited, div.submenu label { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } nav.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover, div.submenu label:hover { color: #404040; background-color: white; } a, a:visited { text-decoration: none; } /* 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 */ 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: 1 timeline-color-graph-lines: 0 white-foreground: 0 |
Added skins/plain_gray/footer.txt.
> > > | 1 2 3 | <footer> Fossil $release_version $manifest_version $manifest_date </footer> |
Added skins/plain_gray/header.txt.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <header> <div class="title">$<project_name>: $<title></div> </header> <nav class="mainmenu" title="Main Menu"> <th1> html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>" builtin_request_js hbmenu.js foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {[string match /* $url]} {set url $home$url} html "<a href='$url' class='$class'>$name</a>\n" } </th1> </nav> <nav id="hbdrop" class='hbdrop' title="sitemap"></nav> |
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 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 | /****************************************************************************** * 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; text-size-adjust: none; } a { color: #40a0ff; } 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; } /************************************** * Main Area */ header, nav.mainmenu, div.submenu, div.content, footer { clear: both; margin: 0 auto; max-width: 90%; padding: 0.25rem 1rem; } /************************************** * Main Area: Header */ header { margin: 0.5rem auto 0 auto; display: flex; flex-direction: row; align-items: center; flex-wrap: wrap; } div.logo { display: inline; max-height: 4em; max-width: 4em; flex: 0 1 auto; } div.logo img { padding: 0; box-shadow: 2px 4px 6px rgba(180,180,180,0.70); border-radius: 2mm; } 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: #3297f9; font-family: Verdana, sans-serif; font-weight: bold; font-size: 2.5rem; padding: 0.5rem; text-align: center; text-shadow: 3px 3px 1px #000; flex: 10 0 auto; } div.status { color: #ee0; font-size: 1rem; padding: 0.25rem; text-align: right; text-shadow: 2px 2px 1px #000; flex: 0 1 auto; } /************************************** * Main Area: Global Menu */ nav.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; } nav.mainmenu { padding-top: 0.33rem; padding-bottom: 0.25rem; } div.submenu { border-top: 1px solid #0a0; border-radius: 0; display: block; } nav.mainmenu a, div.submenu a, div.submenu label { color: #000; padding: 0 0.75rem; text-decoration: none; } nav.mainmenu a:hover, div.submenu a:hover, div.submenu label: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; } .piechartLabel { fill: white; } .piechartLine { stroke: white; } /************************************** * Main Area: Footer */ footer { color: #ee0; font-size: 0.75rem; padding: 0; text-align: right; width: 75%; } footer div { background-color: #222; box-shadow: 3px 3px 1px #000; border-radius: 0 0 1rem 1rem; margin: 0 0 10px 0; padding: 0.25rem 0.75rem; } footer div.page-time { float: left; } footer div.fossil-info { float: right; } footer a, footer a:link, footer a:visited { color: #ee0; } 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 */ tr.diffskip.jchunk { background-color: black; } tr.diffskip > td.chunkctrl .jcbutton { background-color: #303536; } /* Code Added */ td.diffln ins, td.difftxt ins > ins { background-color: #7f7; color: #000; } td.difftxt ins { background-color: inherit; } /* Code Deleted */ td.diffln del, td.difftxt del > del { background-color: #f77; color: #000; } td.difftxt del { background-color: inherit; } /************************************** * Diffs : Side-By-Side */ /* display (column-based) */ table.splitdiff { border-spacing: 0; font-size: 0.85rem; } table.splitdiff pre { border: 0; margin: 0 0.5em; padding: 0; } table.splitdiff td { padding: 0; vertical-align: top; } /* line number column */ td.diffln { color: #ee0; padding-right: 0.75em; text-align: right; } /* diff text column */ td.difftxt { background-color: #111; overflow-x: auto; width: 45em; } /* diff marker column */ td.diffsep { padding: 0 0.5em; } /************************************** * Diffs : Unified */ table.udiff pre { background-color: #111; } /************************************** * 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 { 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 { border: 2px solid #22e; background-color: #22e; } .statistics-report-graph-extra { border: 2px dashed #22e; border-left-style: none; } .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: auto; 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; } /* Tickets on timelines */ td.tktTlOpen { color: #ffa0a0; } /* format for ticket error messages */ span.tktError { color: #f00; font-weight: bold; } /************************************** * Timeline */ /* The suppressed duplicates lines in timeline, .. */ .timelineDisabled { font-size: 0.5rem; font-style: italic; } /* the format for the timeline version display(no history permission!) */ .timelineHistDsp { font-weight: bold; } .content .timelineTable { border: 0; border-spacing: 0 0.5rem; } .content .timelineTable tr { background: #222; border: 0; padding: 0; box-shadow: none; } .timelineTable .timelineDate { color: #ee0; font-size: 1.2rem; font-weight: bold; margin-top: 1rem; white-space: nowrap; } .timelineTable .timelineTime { border-radius: 0; border-width: 0; padding: 0.25rem 0.5rem 0.5rem 0.5rem; white-space: nowrap; } .timelineGraph { text-align: left; vertical-align: top; width: 20px; } .timelineTable .timelineModernCell , .timelineTable .timelineCompactCell , .timelineTable .timelineVerboseCell , .timelineTable .timelineDetailCell { /* background: linear-gradient(to bottom, #222 0%, #333 16%, #222 100%); */ border-radius: 0; border-width: 0; padding: 0.25rem 0.5rem 0.5rem 0.5rem; } .timelineTable .timelineColumnarCell { /* background: linear-gradient(to bottom, #222 0%, #333 16%, #222 100%); */ border-radius: 0; border-width: 0; padding: 0.25rem 0.5rem 0.5rem 0.5rem; } .timelineTable .timelineModernCell[id] , .timelineTable .timelineCompactCell[id] , .timelineTable .timelineVerboseCell[id] , .timelineTable .timelineColumnarCell[id] , .timelineTable .timelineDetailCell[id] { background: #272727; } .timelineTable .timelineCurrent .timelineTime { background: #333; border-radius: 1rem 0 0 1rem; border-width: 0; } .timelineTable .timelineCurrent .timelineColumnarCell { background: #333; } .timelineTable .timelineCurrent .timelineModernCell , .timelineTable .timelineCurrent .timelineCompactCell , .timelineTable .timelineCurrent .timelineVerboseCell , .timelineTable .timelineCurrent .timelineDetailCell { background: #333; border-radius: 0 1rem 1rem 0; } .timelineTable .timelineSelected { background: #222; border: 0; box-shadow: none; } .timelineSelected {} .timelineSecondary {} .timelineTable .timelineSelected .timelineTime { background: #333; border-radius: 1rem 0 0 1rem; box-shadow: 2px 2px 1px #000; } .timelineTable .timelineSelected .timelineColumnarCell { background: #333; box-shadow: 2px 2px 1px #000; } .timelineTable .timelineSelected .timelineModernCell , .timelineTable .timelineSelected .timelineCompactCell , .timelineTable .timelineSelected .timelineVerboseCell , .timelineTable .timelineSelected .timelineDetailCell { background: #333; border-radius: 0 1rem 1rem 0; box-shadow: 2px 2px 1px #000; } span.timelineSelected { padding: 0 1em 0 1em; border-radius: 1rem; background: #333; box-shadow: 2px 2px 1px #000; } .timelineTable .timelineModernCell .timelineModernComment , .timelineTable .timelineModernCell .timelineModernDetail , .timelineTable .timelineCompactCell .timelineCompactComment , .timelineTable .timelineCompactCell .timelineCompactDetail , .timelineTable .timelineVerboseCell .timelineVerboseComment , .timelineTable .timelineVerboseCell .timelineVerboseDetail { } .timelineTable .timelineModernCell .timelineLeaf , .timelineTable .timelineCompactCell .timelineLeaf , .timelineTable .timelineVerboseCell .timelineLeaf , .timelineTable .timelineVerboseComment .timelineLeaf { font-weight: bold; } .timelineTable .timelineModernCell .timelineModernDetail , .timelineTable .timelineDetailCell { font-size: 85%; } .timelineTable .timelineDetailCell .timelineColumnarDetail { white-space: pre-line; } .timelineTable .timelineDetailCell ul.filelist::before { content: "files:"; } .timelineTable .timelineDetailCell ul.filelist { margin-left: 0; padding-left: 0; } .timelineTable .timelineDetailCell ul.filelist li { margin-left: 1.5rem; padding-left: 0; white-space: nowrap; } /* the format for the timeline version links */ a.timelineHistLink { } /************************************** * 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: #ee0; } /* 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 */ table.numbered-lines td.line-numbers span.selected-line { font-weight: bold; color: #00f; background-color: #d5d5ff; border-color: #00f; } /* 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 definition 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: yellow; } /* format for artifact lines being shunned */ p.shunned { color: yellow; } /* 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 */ } .fossil-PopupWidget, .fossil-tooltip.help-buttonlet-content { background-color: #111; border: 1px solid rgba(255,255,255,0.5); } .fossil-PopupWidget a, .fossil-PopupWidget a:visited { color: white; } div.forumSel { background-color: #663399; } div.forumPostBody blockquote { border-width: 1pt; border-style: solid; padding: 0 0.5em; border-radius: 0.25em; } body.forum .forumPosts.fileage a { color: #60c0ff; } body.forum .forumPosts.fileage a:visited { color: #40a0ff; } .debug { color: black; } body.branch .brlist > table > tbody > tr:hover:not(.selected), body.branch .brlist > table > tbody > tr.selected { background-color: #444; } body.chat header, body.chat footer, body.chat nav.mainmenu, body.chat div.submenu, body.chat div.content { margin-left: 0.5em; margin-right: 0.5em; margin-top: auto/*eliminates unnecessary scrollbars*/; } body.chat.chat-only-mode div.content { max-width: revert; } body.chat #chat-user-list .chat-user{ color: white; } |
Added skins/xekri/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 1 white-foreground: 1 |
Added skins/xekri/footer.txt.
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | </div> <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> </footer> |
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 | <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 } if {1} { # Link logo to the top of the current domain set logourl [getLogoUrl $baseurl] } else { # Link logo to the top of the current repo set logourl $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> </header> <th1>html "<script nonce='$nonce'>"</th1> 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> <nav class="mainmenu" title="Main Menu"> <th1> set sitemap 0 foreach {name url expr class} $mainmenu { if {![capexpr $expr]} continue if {[string match /* $url]} { if {[string match $url\[/?#\]* /$current_page/]} { set class "active $class" } set url $home$url } html "<a href='$url' class='$class'>$name</a>\n" if {[string match */sitemap $url]} {set sitemap 1} } if {!$sitemap} { html "<a href='$home/sitemap'>...</a>\n" } </th1> </nav> |
Added src/Makefile.
> > > | 1 2 3 | all clean: $(MAKE) -C .. $(MAKECMDGOALS) |
Added src/accordion.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Attach appropriate javascript to each ".accordion" button so that it expands ** and contracts when clicked. ** ** The uncompressed source code for the SVG icons can be found on the wiki page ** "branch/accordion-experiments" in the Fossil repository. ** ** Implementation notes: ** ** The `maxHeight' CSS property is quite restrictive for vertical resizing of ** elements, especially for dynamic-content areas like the diff panels. That's ** why `maxHeight' is set only during animation, to prevent truncated elements. ** (The diff panels may get truncated right after page loading, and other ** elements may get truncated when resizing the browser window to a smaller ** width, causing vertical growth.) ** ** Another problem is that `scrollHeight' used to calculate the expanded height ** while still in the contracted state may return values with small errors on ** some browsers, especially for large elements, presumably due to omitting the ** space required by the vertical scrollbar that may become necessary, causing ** additional horizontal shrinking and consequently more vertical growth than ** calculated. That's why setting `maxHeight' to `scrollHeight' is considered ** "good enough" only during animation, but cleared afterwards. ** ** https://fossil-scm.org/forum/forumpost/66d7075f40 ** https://fossil-scm.org/home/timeline?r=accordion-fix */ var acc_svgdata = ["data:image/svg+xml,"+ "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E"+ "%3Cpath style='fill:black;opacity:0' d='M16,16H0V0h16v16z'/%3E"+ "%3Cpath style='fill:rgb(240,240,240)' d='M14,14H2V2h12v12z'/%3E"+ "%3Cpath style='fill:rgb(64,64,64)' d='M13,13H3V3h10v10z'/%3E"+ "%3Cpath style='fill:rgb(248,248,248)' d='M12,12H4V4h8v8z'/%3E"+ "%3Cpath style='fill:rgb(80,128,208)' d='", "'/%3E%3C/svg%3E", "M5,7h2v-2h2v2h2v2h-2v2h-2v-2h-2z", "M11,9H5V7h6v6z"]; var a = document.getElementsByClassName("accordion"); for(var i=0; i<a.length; i++){ var img = document.createElement("img"); img.src = acc_svgdata[0]+acc_svgdata[2]+acc_svgdata[1]; img.className = "accordion_btn accordion_btn_plus"; a[i].insertBefore(img,a[i].firstChild); img = document.createElement("img"); img.src = acc_svgdata[0]+acc_svgdata[3]+acc_svgdata[1]; img.className = "accordion_btn accordion_btn_minus"; a[i].insertBefore(img,a[i].firstChild); a[i].addEventListener("click",function(){ var x = this.nextElementSibling; if( this.classList.contains("accordion_closed") ){ x.style.maxHeight = x.scrollHeight + "px"; setTimeout(function(){ x.style.maxHeight = ""; },250); // default.css: .accordion_panel { transition-duration } }else{ x.style.maxHeight = x.scrollHeight + "px"; setTimeout(function(){ x.style.maxHeight = "0"; },1); } this.classList.toggle("accordion_closed"); }); } |
Changes to src/add.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 | ** This file contains code used to check-out versions of the project ** from the local repository. */ #include "config.h" #include "add.h" #include <assert.h> #include <dirent.h> /* | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > > | | > > > > > > > > | < > | > | > > > > > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | | | | | | > > > | > > > | > > > > | < < > | | > | < | | | | < > | > > > > | < < | | | | > > > > | > > > > < < > > > > > > > > > | < > > | | > | > > > > > > > > | > | | > > > > > | < < | | < < | < < < | | < | | > < > > | | > > > > > > | > > > > > > > > > > > > > > | > | | > | > > | > > > > > > | > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | | > > > > > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > | > > > > > > > | > > > > | > > > > > < | < > < | | > > > | < | | | | | > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | | | | > > > > > | | > > > > > > | > | < > | < | > > > > | > | < | < < < < < < < < | | < > | | | | > | > > > | < < < > | < | > > > > > | < | | | | > | < < < | > | < | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > | > > | > | > > | > > | > > > > > > > > > > > > > | | | | < < > | | | < | < < < > > | < | | | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | < < | > > | | > > > > > > > > | | | | > > > | | > > > > > > > > > > > | 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 | ** This file contains code used to check-out versions of the project ** from the local repository. */ #include "config.h" #include "add.h" #include <assert.h> #include <dirent.h> #include "cygsup.h" /* ** This routine returns the names of files in a working check-out 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-check-out 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 check-out 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", }; /* Possible names of auxiliary files generated when the "manifest" property ** is used */ static const struct { const char *fname; int flg; }aManifestflags[] = { { "manifest", MFESTFLG_RAW }, { "manifest.uuid", MFESTFLG_UUID }, { "manifest.tags", MFESTFLG_TAGS } }; static const char *azManifests[3]; /* ** Names of repository files, if they exist in the check-out. */ static const char *azRepo[4] = { 0, 0, 0, 0 }; /* Cached setting "manifest" */ static int cachedManifest = -1; static int numManifests; if( cachedManifest == -1 ){ int i; Blob repo; cachedManifest = db_get_manifest_setting(); numManifests = 0; for(i=0; i<count(aManifestflags); i++){ if( cachedManifest&aManifestflags[i].flg ) { azManifests[numManifests++] = aManifestflags[i].fname; } } blob_zero(&repo); if( file_tree_name(g.zRepositoryName, &repo, 0, 0) ){ const char *zRepo = blob_str(&repo); azRepo[0] = zRepo; azRepo[1] = mprintf("%s-journal", zRepo); azRepo[2] = mprintf("%s-wal", zRepo); azRepo[3] = mprintf("%s-shm", zRepo); } } if( N<0 ) return 0; if( N<count(azName) ) return azName[N]; N -= count(azName); if( cachedManifest ){ if( N<numManifests ) return azManifests[N]; N -= numManifests; } if( !omitRepo && N<count(azRepo) ) return azRepo[N]; return 0; } /* ** Return a list of all reserved filenames as an SQL list. */ const char *fossil_all_reserved_names(int omitRepo){ static char *zAll = 0; if( zAll==0 ){ Blob x; int i; const char *z; blob_zero(&x); for(i=0; (z = fossil_reserved_name(i, omitRepo))!=0; i++){ if( i>0 ) blob_append(&x, ",", 1); blob_appendf(&x, "'%q'", z); } zAll = blob_str(&x); } return zAll; } /* ** COMMAND: test-reserved-names ** ** Usage: %fossil test-reserved-names [-omitrepo] ** ** 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)); } /* ** Add a single file named zName to the VFILE table with vid. ** ** Omit any file whose name is pOmit. */ static int add_one_file( const char *zPath, /* Tree-name of file to add. */ int vid /* Add to this VFILE */ ){ int doSkip = 0; if( !file_is_simple_pathname(zPath, 1) ){ fossil_warning("filename contains illegal characters: %s", zPath); return 0; } 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 AND deleted", zPath, filename_collation()); }else{ char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath); int isExe = file_isexe(zFullname, RepoFILE); int isLink = file_islink(0); if( file_nondir_objects_on_path(g.zLocalRoot, zFullname) ){ /* Do not add unsafe files to the vfile */ doSkip = 1; }else{ db_multi_exec( "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink,mhash)" "VALUES(%d,0,0,0,%Q,%d,%d,NULL)", vid, zPath, isExe, isLink); } fossil_free(zFullname); } if( db_changes() && !doSkip ){ fossil_print("ADDED %s\n", zPath); return 1; }else{ fossil_print("SKIP %s\n", zPath); return 0; } } /* ** Add all files in the sfile temp table. ** ** Automatically exclude the repository file and any other files ** with reserved names. Also exclude files that are beneath an ** existing symlink. */ static int add_files_in_sfile(int vid){ 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, 0) ){ blob_zero(&repoName); zRepo = ""; }else{ zRepo = blob_str(&repoName); } if( filenames_are_case_sensitive() ){ xCmp = fossil_strcmp; }else{ xCmp = fossil_stricmp; } db_prepare(&loop, "SELECT pathname FROM sfile" " WHERE pathname NOT IN (" "SELECT sfile.pathname FROM vfile, sfile" " WHERE vfile.islink" " AND NOT vfile.deleted" " AND sfile.pathname>(vfile.pathname||'/')" " AND sfile.pathname<(vfile.pathname||'0'))" " ORDER BY pathname"); while( db_step(&loop)==SQLITE_ROW ){ const char *zToAdd = db_column_text(&loop, 0); if( fossil_strcmp(zToAdd, zRepo)==0 ) continue; if( strchr(zToAdd,'/') ){ if( file_is_reserved_name(zToAdd, -1) ) continue; }else{ for(i=0; (zReserved = fossil_reserved_name(i, 0))!=0; i++){ if( xCmp(zToAdd, zReserved)==0 ) break; } if( zReserved ) continue; } nAdd += add_one_file(zToAdd, vid); } db_finalize(&loop); blob_reset(&repoName); return nAdd; } /* ** Resets the ADDED/DELETED state of a check-out, such that all ** newly-added (but not yet committed) files are no longer added and ** newly-removed (but not yet committed) files are no longer ** removed. If bIsAdd is true, it operates on the "add" state, else it ** operates on the "rm" state. ** ** If bDryRun is true it outputs what it would have done, but does not ** actually do it. In this case it rolls back the transaction it ** starts (so don't start a transaction before calling this). ** ** If bVerbose is true it outputs the name of each reset entry. ** ** This is intended to be called only in the context of the ** add/rm/addremove commands, after a call to verify_all_options(). ** ** Un-added files are not modified but any un-rm'd files which are ** missing from the check-out are restored from the repo. un-rm'd files ** which exist in the check-out are left as-is, rather than restoring ** them using vfile_to_disk(), to avoid overwriting any local changes ** made to those files. */ static void addremove_reset(int bIsAdd, int bDryRun, int bVerbose){ int nReset = 0; /* # of entries which get reset */ Stmt stmt; /* vfile loop query */ db_begin_transaction(); db_prepare(&stmt, "SELECT id, pathname FROM vfile " "WHERE %s ORDER BY pathname", bIsAdd==0 ? "deleted<>0" : "rid=0"/*safe-for-%s*/); while( db_step(&stmt)==SQLITE_ROW ){ /* This loop exists only so we can restore the contents of un-rm'd ** files and support verbose mode. All manipulation of vfile's ** contents happens after the loop. For the ADD case in non-verbose ** mode we "could" skip this loop entirely. */ int const id = db_column_int(&stmt, 0); char const * zPathname = db_column_text(&stmt, 1); Blob relName = empty_blob; if(bIsAdd==0 || bVerbose!=0){ /* Make filename relative... */ char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); file_relative_name(zFullName, &relName, 0); fossil_free(zFullName); } if(bIsAdd==0){ /* Restore contents of missing un-rm'd files. We don't do this ** unconditionally because we might cause data loss if a file ** is modified, rm'd, then un-rm'd. */ ++nReset; if(!file_isfile_or_link(blob_str(&relName))){ if(bDryRun==0){ vfile_to_disk(0, id, 0, 0); if(bVerbose){ fossil_print("Restored missing file: %b\n", &relName); } }else{ fossil_print("Dry-run: not restoring missing file: %b\n", &relName); } } if(bVerbose){ fossil_print("Un-removed: %b\n", &relName); } }else{ /* un-add... */ ++nReset; if(bVerbose){ fossil_print("Un-added: %b\n", &relName); } } blob_reset(&relName); } db_finalize(&stmt); if(nReset>0){ if(bIsAdd==0){ if(bDryRun==0){ db_exec_sql("UPDATE vfile SET deleted=0 WHERE deleted<>0"); } fossil_print("Un-removed %d file(s).\n", nReset); }else{ if(bDryRun==0){ db_exec_sql("DELETE FROM vfile WHERE rid=0"); } fossil_print("Un-added %d file(s).\n", nReset); } } db_end_transaction(bDryRun ? 1 : 0); } /* ** COMMAND: add ** ** Usage: %fossil add ?OPTIONS? FILE1 ?FILE2 ...? ** ** Make arrangements to add one or more files or directories to the ** current check-out at the next [[commit]]. ** ** When adding files or directories recursively, filenames that begin ** with "." are excluded by default. To include such files, add ** the "--dotfiles" option to the command-line. ** ** The --ignore and --clean options are comma-separated 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 unmanaged files matching patterns from ** the Comma Separated Glob (CSG) pattern list ** --clean CSG Also ignore files matching patterns from ** the Comma Separated Glob (CSG) list ** --reset Reset the ADDED state of a check-out, such ** that all newly-added (but not yet committed) ** files are no longer added. No flags other ** than --verbose and --dry-run may be used ** with --reset. ** --allow-reserved Permit filenames which are reserved on ** Windows platforms. Such files cannot be ** checked out on Windows, so use with care. ** ** The following options are only valid with --reset: ** -v|--verbose Output information about each --reset file ** -n|--dry-run Display instead of run actions ** ** 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; int allowReservedFlag = 0; /* --allow-reserved flag */ if(0!=find_option("reset",0,0)){ int const verboseFlag = find_option("verbose","v",0)!=0; int const dryRunFlag = find_option("dry-run","n",0)!=0; db_must_be_within_tree(); verify_all_options(); addremove_reset(1, dryRunFlag, verboseFlag); return; } 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; allowReservedFlag = find_option("allow-reserved",0,0)!=0; /* 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(pathname 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 = empty_blob; /* file_tree_name() throws a fatal error if g.argv[i] is outside of the ** check-out. */ file_tree_name(g.argv[i], &fullName, 0, 1); blob_reset(&fullName); file_canonical_name(g.argv[i], &fullName, 0); zName = blob_str(&fullName); isDir = file_isdir(zName, RepoFILE); if( isDir==1 ){ vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE); }else if( isDir==0 ){ fossil_warning("not found: %s", zName); }else{ char *zTreeName = file_case_preferred_name(g.zLocalRoot,&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); fossil_free(prompt); 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(pathname) VALUES(%Q)", zTreeName ); fossil_free(zTreeName); } blob_reset(&fullName); } glob_free(pIgnore); glob_free(pClean); /** Check for Windows-reserved names and warn or exit, as ** appopriate. Note that the 'add' internal machinery already ** _silently_ skips over any names for which ** file_is_reserved_name() returns true or which is in the ** fossil_reserved_name() list. We do not need to warn for those, ** as they're outright verboten. */ if(db_exists("SELECT 1 FROM sfile WHERE win_reserved(pathname)")){ int reservedCount = 0; Stmt q = empty_Stmt; db_prepare(&q,"SELECT pathname FROM sfile " "WHERE win_reserved(pathname)"); while( db_step(&q)==SQLITE_ROW ){ const char * zName = db_column_text(&q, 0); ++reservedCount; if(allowReservedFlag){ fossil_warning("WARNING: Windows-reserved " "filename: %s", zName); }else{ fossil_warning("ERROR: Windows-reserved filename: %s", zName); } } db_finalize(&q); if(allowReservedFlag==0){ fossil_fatal("ERROR: %d Windows-reserved filename(s) added. " "Use --allow-reserved to permit such names.", reservedCount); } } add_files_in_sfile(vid); db_end_transaction(0); } /* ** This function adds a file to list of files to delete from disk after ** the other actions required for the parent operation have completed ** successfully. The first time it is called for the current process, ** it creates a temporary table named "fremove", to keep track of these ** files. */ static void add_file_to_remove( const char *zOldName /* The old name of the file on disk. */ ){ static int tableCreated = 0; Blob fullOldName; if( !tableCreated ){ db_multi_exec("CREATE TEMP TABLE fremove(x TEXT PRIMARY KEY %s)", filename_collation()); tableCreated = 1; } file_tree_name(zOldName, &fullOldName, 1, 1); db_multi_exec("INSERT INTO fremove VALUES('%q');", blob_str(&fullOldName)); blob_reset(&fullOldName); } /* ** This function deletes files from the check-out, using the file names ** contained in the temporary table "fremove". The temporary table is ** created on demand by the add_file_to_remove() function. ** ** If dryRunFlag is non-zero, no files will be removed; however, their ** names will still be output. ** ** The temporary table "fremove" is dropped after being processed. */ static void process_files_to_remove( int dryRunFlag /* Zero to actually operate on the file-system. */ ){ Stmt remove; if( db_table_exists("temp", "fremove") ){ db_prepare(&remove, "SELECT x FROM fremove ORDER BY x;"); while( db_step(&remove)==SQLITE_ROW ){ const char *zOldName = db_column_text(&remove, 0); if( !dryRunFlag ){ file_delete(zOldName); } fossil_print("DELETED_FILE %s\n", zOldName); } db_finalize(&remove); db_multi_exec("DROP TABLE fremove;"); } } /* ** COMMAND: rm ** COMMAND: delete ** COMMAND: forget* ** ** Usage: %fossil rm|delete|forget FILE1 ?FILE2 ...? ** ** Remove one or more files or directories from the repository. ** ** The 'rm' and 'delete' commands do NOT normally remove the files from ** disk. They just mark the files as no longer being part of the project. ** In other words, future changes to the named files will not be versioned. ** However, the default behavior of this command may be overridden via the ** command line options listed below and/or the 'mv-rm-files' setting. ** ** The 'forget' command never removes files from disk, even when the command ** line options and/or the 'mv-rm-files' setting would otherwise require it ** to do so. ** ** WARNING: If the "--hard" option is specified -OR- the "mv-rm-files" ** setting is non-zero, files WILL BE removed from disk as well. ** This does NOT apply to the 'forget' command. ** ** Options: ** --soft Skip removing files from the check-out. ** This supersedes the --hard option. ** --hard Remove files from the check-out ** --case-sensitive BOOL Override the case-sensitive setting ** -n|--dry-run If given, display instead of run actions. ** --reset Reset the DELETED state of a check-out, such ** that all newly-rm'd (but not yet committed) ** files are no longer removed. No flags other ** than --verbose or --dry-run may be used with ** --reset. ** -v|--verbose Outputs information about each --reset file. ** Only usable with --reset. ** ** See also: [[addremove]], [[add]] */ void delete_cmd(void){ int i; int removeFiles; int dryRunFlag = find_option("dry-run","n",0)!=0; int softFlag; int hardFlag; Stmt loop; if(0!=find_option("reset",0,0)){ int const verboseFlag = find_option("verbose","v",0)!=0; db_must_be_within_tree(); verify_all_options(); addremove_reset(0, dryRunFlag, verboseFlag); return; } softFlag = find_option("soft",0,0)!=0; hardFlag = find_option("hard",0,0)!=0; /* We should be done with options.. */ verify_all_options(); db_must_be_within_tree(); db_begin_transaction(); if( g.argv[1][0]=='f' ){ /* i.e. "forget" */ removeFiles = 0; }else if( softFlag ){ removeFiles = 0; }else if( hardFlag ){ removeFiles = 1; }else{ removeFiles = db_get_boolean("mv-rm-files",0); } db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s)", filename_collation()); for(i=2; i<g.argc; i++){ Blob treeName; char *zTreeName; file_tree_name(g.argv[i], &treeName, 0, 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 pathname FROM sfile"); while( db_step(&loop)==SQLITE_ROW ){ fossil_print("DELETED %s\n", db_column_text(&loop, 0)); if( removeFiles ) add_file_to_remove(db_column_text(&loop, 0)); } db_finalize(&loop); if( !dryRunFlag ){ db_multi_exec( "UPDATE vfile SET deleted=1 WHERE pathname IN sfile;" "DELETE FROM vfile WHERE rid=0 AND deleted;" ); } db_end_transaction(0); if( removeFiles ) process_files_to_remove(dryRunFlag); } /* ** Capture the command-line --case-sensitive option. */ static const char *zCaseSensitive = 0; void capture_case_sensitive_option(void){ if( zCaseSensitive==0 ){ zCaseSensitive = find_option("case-sensitive",0,1); } } /* ** This routine determines if files should be case-sensitive or not. ** In other words, this routine determines if two filenames that ** differ only in case should be considered the same name or not. ** ** The case-sensitive setting determines the default value. If ** the case-sensitive setting is undefined, then case sensitivity ** defaults off for Cygwin, Mac and Windows and on for all other unix. ** If case-sensitivity is enabled in the windows kernel, the Cygwin port ** of fossil.exe can detect that, and modifies the default to 'on'. ** ** The "--case-sensitive BOOL" command-line option overrides any ** setting. */ int filenames_are_case_sensitive(void){ static int caseSensitive; static int once = 1; if( once ){ once = 0; if( zCaseSensitive ){ caseSensitive = is_truth(zCaseSensitive); }else{ #if defined(_WIN32) || defined(__DARWIN__) || defined(__APPLE__) caseSensitive = 0; /* Mac and Windows */ #elif defined(__CYGWIN__) /* Cygwin can be configured to be case-sensitive, check this. */ void *hKey; int value = 1, length = sizeof(int); caseSensitive = 0; /* Cygwin default */ if( (RegOpenKeyExW((void *)0x80000002, L"SYSTEM\\CurrentControlSet\\" "Control\\Session Manager\\kernel", 0, 1, (void *)&hKey) == 0) && (RegQueryValueExW(hKey, L"obcaseinsensitive", 0, NULL, (void *)&value, (void *)&length) == 0) && !value ){ caseSensitive = 1; } #else caseSensitive = 1; /* Unix */ #endif caseSensitive = db_get_boolean("case-sensitive",caseSensitive); } if( !caseSensitive && g.localOpen ){ db_multi_exec( "CREATE INDEX IF NOT EXISTS localdb.vfile_nocase" " ON vfile(pathname COLLATE nocase)" ); } } return caseSensitive; } /* ** Return one of two things: ** ** "" (empty string) if filenames are case sensitive ** ** "COLLATE nocase" if filenames are not case sensitive. */ const char *filename_collation(void){ return filenames_are_case_sensitive() ? "" : "COLLATE nocase"; } /* ** COMMAND: addremove ** ** Usage: %fossil addremove ?OPTIONS? ** ** Do all necessary "[[add]]" and "[[rm]]" commands to synchronize the ** repository with the content of the working check-out: ** ** * All files in the check-out 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 check-out (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 unmanaged files matching patterns from ** the Comma Separated Glob (CSG) list ** --clean CSG Also ignore files matching patterns from ** the Comma Separated Glob (CSG) list ** -n|--dry-run If given, display instead of run actions ** --reset Reset the ADDED/DELETED state of a check-out, ** such that all newly-added (but not yet committed) ** files are no longer added and all newly-removed ** (but not yet committed) files are no longer ** removed. No flags other than --verbose and ** --dry-run may be used with --reset. ** -v|--verbose Outputs information about each --reset file. ** Only usable with --reset. ** ** See also: [[add]], [[rm]] */ void addremove_cmd(void){ Blob path; const char *zCleanFlag; const char *zIgnoreFlag; unsigned scanFlags; 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 */ } if(0!=find_option("reset",0,0)){ int const verboseFlag = find_option("verbose","v",0)!=0; db_must_be_within_tree(); verify_all_options(); addremove_reset(0, dryRunFlag, verboseFlag); addremove_reset(1, dryRunFlag, verboseFlag); return; } zCleanFlag = find_option("clean",0,1); zIgnoreFlag = find_option("ignore",0,1); scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; /* We should be done with options.. */ verify_all_options(); /* Fail if unprocessed arguments are present, in case user expect the ** addremove command to accept a list of file or directory. */ if( g.argc>2 ){ fossil_fatal( "%s: Can only work on the entire check-out, no arguments supported.", g.argv[1]); } 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(pathname TEXT PRIMARY KEY %s)", filename_collation()); n = strlen(g.zLocalRoot); blob_init(&path, g.zLocalRoot, n-1); /* now we read the complete file structure into a temp table */ pClean = glob_create(zCleanFlag); pIgnore = glob_create(zIgnoreFlag); vfile_scan(&path, blob_size(&path), scanFlags, pClean, pIgnore, RepoFILE); glob_free(pIgnore); glob_free(pClean); nAdd = add_files_in_sfile(vid); /* step 2: search for missing files */ 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_isfile_or_link(zPath) ){ if( !dryRunFlag ){ db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile); } fossil_print("DELETED %s\n", zFile); nDelete++; } } db_finalize(&q); /* show command summary */ fossil_print("added %d files, deleted %d files\n", nAdd, nDelete); if(dryRunFlag!=0){ fossil_print("Dry-run mode: no changes were made.\n"); } db_end_transaction(dryRunFlag); } /* ** 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 dryRunFlag ){ int x = db_int(-1, "SELECT deleted FROM vfile WHERE pathname=%Q %s", zNew, filename_collation()); if( x>=0 ){ if( x==0 ){ if( !filenames_are_case_sensitive() && fossil_stricmp(zOrig,zNew)==0 ){ /* Case change only */ }else{ 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); if( !dryRunFlag ){ db_multi_exec( "UPDATE vfile SET pathname='%q' WHERE pathname='%q' %s AND vid=%d", zNew, zOrig, filename_collation(), vid ); } } /* ** This function adds a file to list of files to move on disk after the ** other actions required for the parent operation have completed ** successfully. The first time it is called for the current process, ** it creates a temporary table named "fmove", to keep track of these ** files. */ static void add_file_to_move( const char *zOldName, /* The old name of the file on disk. */ const char *zNewName /* The new name of the file on disk. */ ){ static int tableCreated = 0; Blob fullOldName; Blob fullNewName; char *zOld, *zNew; if( !tableCreated ){ db_multi_exec("CREATE TEMP TABLE fmove(x TEXT PRIMARY KEY %s, y TEXT %s)", filename_collation(), filename_collation()); tableCreated = 1; } file_tree_name(zOldName, &fullOldName, 1, 1); zOld = blob_str(&fullOldName); file_tree_name(zNewName, &fullNewName, 1, 1); zNew = blob_str(&fullNewName); if( filenames_are_case_sensitive() || fossil_stricmp(zOld,zNew)!=0 ){ db_multi_exec("INSERT INTO fmove VALUES('%q','%q');", zOld, zNew); } blob_reset(&fullNewName); blob_reset(&fullOldName); } /* ** This function moves files within the check-out, using the file names ** contained in the temporary table "fmove". The temporary table is ** created on demand by the add_file_to_move() function. ** ** If dryRunFlag is non-zero, no files will be moved; however, their ** names will still be output. ** ** The temporary table "fmove" is dropped after being processed. */ static void process_files_to_move( int dryRunFlag /* Zero to actually operate on the file-system. */ ){ Stmt move; if( db_table_exists("temp", "fmove") ){ db_prepare(&move, "SELECT x, y FROM fmove ORDER BY x;"); while( db_step(&move)==SQLITE_ROW ){ const char *zOldName = db_column_text(&move, 0); const char *zNewName = db_column_text(&move, 1); if( !dryRunFlag ){ int isOldDir = file_isdir(zOldName, RepoFILE); if( isOldDir==1 ){ int isNewDir = file_isdir(zNewName, RepoFILE); if( isNewDir==0 ){ file_rename(zOldName, zNewName, isOldDir, isNewDir); } }else{ if( file_islink(zOldName) ){ symlink_copy(zOldName, zNewName); }else{ file_copy(zOldName, zNewName); } file_delete(zOldName); } } fossil_print("MOVED_FILE %s\n", zOldName); } db_finalize(&move); db_multi_exec("DROP TABLE fmove;"); } } /* ** COMMAND: mv ** COMMAND: rename* ** ** Usage: %fossil mv|rename OLDNAME NEWNAME ** 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. ** ** The 'mv' command does NOT normally rename or move the files on disk. ** This command merely records the fact that file names have changed so ** that appropriate notations can be made at the next [[commit]]. ** However, the default behavior of this command may be overridden via ** command line options listed below and/or the 'mv-rm-files' setting. ** ** The 'rename' command never renames or moves files on disk, even when the ** command line options and/or the 'mv-rm-files' setting would otherwise ** require it to do so. ** ** WARNING: If the "--hard" option is specified -OR- the "mv-rm-files" ** setting is non-zero, files WILL BE renamed or moved on disk ** as well. This does NOT apply to the 'rename' command. ** ** Options: ** --soft Skip moving files within the check-out. ** This supersedes the --hard option. ** --hard Move files within the check-out ** --case-sensitive BOOL Override the case-sensitive setting ** -n|--dry-run If given, display instead of run actions ** ** See also: [[changes]], [[status]] */ void mv_cmd(void){ int i; int vid; int moveFiles; int dryRunFlag; int softFlag; int hardFlag; int origType; int destType; char *zDest; Blob dest; Stmt q; db_must_be_within_tree(); dryRunFlag = find_option("dry-run","n",0)!=0; softFlag = find_option("soft",0,0)!=0; hardFlag = find_option("hard",0,0)!=0; /* We should be done with options.. */ verify_all_options(); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("no check-out in which to rename files"); } if( g.argc<4 ){ usage("OLDNAME NEWNAME"); } zDest = g.argv[g.argc-1]; db_begin_transaction(); if( g.argv[1][0]=='r' ){ /* i.e. "rename" */ moveFiles = 0; }else if( softFlag ){ moveFiles = 0; }else if( hardFlag ){ moveFiles = 1; }else{ moveFiles = db_get_boolean("mv-rm-files",0); } file_tree_name(zDest, &dest, 0, 1); db_multi_exec( "UPDATE vfile SET origname=pathname WHERE origname IS NULL;" ); db_multi_exec( "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);" ); if( g.argc!=4 ){ origType = -1; }else{ origType = (file_isdir(g.argv[2], RepoFILE) == 1); } destType = file_isdir(zDest, RepoFILE); if( origType==-1 && destType!=1 ){ usage("OLDNAME NEWNAME"); }else if( origType==1 && destType==2 ){ fossil_fatal("cannot rename '%s' to '%s' since another file named" " '%s' exists", g.argv[2], zDest, zDest); }else if( origType==0 && destType!=1 ){ Blob orig; file_tree_name(g.argv[2], &orig, 0, 1); db_multi_exec( "INSERT INTO mv VALUES(%B,%B)", &orig, &dest ); }else{ if( blob_eq(&dest, ".") ){ blob_reset(&dest); }else{ blob_append(&dest, "/", 1); } for(i=2; i<g.argc-1; i++){ Blob orig; char *zOrig; int nOrig; file_tree_name(g.argv[i], &orig, 0, 1); zOrig = blob_str(&orig); nOrig = blob_size(&orig); db_prepare(&q, "SELECT pathname FROM vfile" " WHERE vid=%d" " AND (pathname='%q' %s OR (pathname>'%q/' %s AND pathname<'%q0' %s))" " ORDER BY 1", vid, zOrig, filename_collation(), zOrig, filename_collation(), zOrig, filename_collation() ); while( db_step(&q)==SQLITE_ROW ){ const char *zPath = db_column_text(&q, 0); int nPath = db_column_bytes(&q, 0); const char *zTail; if( nPath==nOrig ){ zTail = file_tail(zPath); }else if( origType!=0 && destType==1 ){ zTail = &zPath[nOrig-strlen(file_tail(zOrig))]; }else{ zTail = &zPath[nOrig+1]; } db_multi_exec( "INSERT INTO mv VALUES('%q','%q%q')", zPath, blob_str(&dest), zTail ); } db_finalize(&q); } } db_prepare(&q, "SELECT f, t FROM mv ORDER BY f"); while( db_step(&q)==SQLITE_ROW ){ const char *zFrom = db_column_text(&q, 0); const char *zTo = db_column_text(&q, 1); mv_one_file(vid, zFrom, zTo, dryRunFlag); if( moveFiles ) add_file_to_move(zFrom, zTo); } db_finalize(&q); undo_reset(); db_end_transaction(0); if( moveFiles ) process_files_to_move(dryRunFlag); } /* ** 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); } |
Added src/ajax.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 | /* ** Copyright (c) 2020 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 shared Ajax-related code for /fileedit, the wiki/forum ** editors, and friends. */ #include "config.h" #include "ajax.h" #include <assert.h> #include <stdarg.h> #if INTERFACE /* enum ajax_render_preview_flags: */ #define AJAX_PREVIEW_LINE_NUMBERS 1 /* enum ajax_render_modes: */ #define AJAX_RENDER_GUESS 0 /* Guess rendering mode based on mimetype. */ /* GUESS must be 0. All others have unspecified values. */ #define AJAX_RENDER_PLAIN_TEXT 1 /* Render as plain text. */ #define AJAX_RENDER_HTML_IFRAME 2 /* Render as HTML inside an IFRAME. */ #define AJAX_RENDER_HTML_INLINE 3 /* Render as HTML without an IFRAME. */ #define AJAX_RENDER_WIKI 4 /* Render as wiki/markdown. */ #endif /* ** Emits JS code which initializes the ** fossil.page.previewModes object to a map of AJAX_RENDER_xxx values ** and symbolic names for use by client-side scripts. ** ** If addScriptTag is true then the output is wrapped in a SCRIPT tag ** with the current nonce, else no SCRIPT tag is emitted. ** ** Requires that builtin_emit_script_fossil_bootstrap() has already been ** called in order to initialize the window.fossil.page object. */ void ajax_emit_js_preview_modes(int addScriptTag){ if(addScriptTag){ style_script_begin(__FILE__,__LINE__); } CX("fossil.page.previewModes={" "guess: %d, %d: 'guess', wiki: %d, %d: 'wiki'," "htmlIframe: %d, %d: 'htmlIframe', " "htmlInline: %d, %d: 'htmlInline', " "text: %d, %d: 'text'" "};\n", AJAX_RENDER_GUESS, AJAX_RENDER_GUESS, AJAX_RENDER_WIKI, AJAX_RENDER_WIKI, AJAX_RENDER_HTML_IFRAME, AJAX_RENDER_HTML_IFRAME, AJAX_RENDER_HTML_INLINE, AJAX_RENDER_HTML_INLINE, AJAX_RENDER_PLAIN_TEXT, AJAX_RENDER_PLAIN_TEXT); if(addScriptTag){ style_script_end(); } } /* ** Returns a value from the ajax_render_modes enum, based on the ** given mimetype string (which may be NULL), defaulting to ** AJAX_RENDER_PLAIN_TEXT. */ int ajax_render_mode_for_mimetype(const char * zMimetype){ int rc = AJAX_RENDER_PLAIN_TEXT; if( zMimetype ){ if( fossil_strcmp(zMimetype, "text/html")==0 ){ rc = AJAX_RENDER_HTML_IFRAME; }else if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 || fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ rc = AJAX_RENDER_WIKI; } } return rc; } /* ** Renders text/wiki content preview for various /ajax routes. ** ** pContent is text/wiki content to preview. zName is the name of the ** content, for purposes of determining the mimetype based on the ** extension (if NULL, mimetype text/plain is assumed). flags may be a ** bitmask of values from the ajax_render_preview_flags ** enum. *renderMode must specify the render mode to use. If ** *renderMode==AJAX_RENDER_GUESS then *renderMode gets set to the ** mode which is guessed at for the rendering (based on the mimetype). ** ** nIframeHeightEm is only used for the AJAX_RENDER_HTML_IFRAME ** renderMode, and specifies the height, in EM's, of the resulting ** iframe. If passed 0, it defaults to "some sane value." */ void ajax_render_preview(Blob * pContent, const char *zName, int flags, int * renderMode, int nIframeHeightEm){ const char * zMime; zMime = zName ? mimetype_from_name(zName) : "text/plain"; if(AJAX_RENDER_GUESS==*renderMode){ *renderMode = ajax_render_mode_for_mimetype(zMime); } switch(*renderMode){ case AJAX_RENDER_HTML_IFRAME:{ char * z64 = encode64(blob_str(pContent), blob_size(pContent)); CX("<iframe width='100%%' frameborder='0' " "marginwidth='0' style='height:%dem' " "marginheight='0' sandbox='allow-same-origin' " "src='data:text/html;base64,%z'" "></iframe>", nIframeHeightEm ? nIframeHeightEm : 40, z64); break; } case AJAX_RENDER_HTML_INLINE:{ CX("%b",pContent); break; } case AJAX_RENDER_WIKI: safe_html_context(DOCSRC_FILE); wiki_render_by_mimetype(pContent, zMime); break; default:{ const char *zContent = blob_str(pContent); if(AJAX_PREVIEW_LINE_NUMBERS & flags){ output_text_with_line_numbers(zContent, blob_size(pContent), zName, "on", 0); }else{ const char *zExt = strrchr(zName,'.'); if(zExt && zExt[1]){ CX("<pre><code class='language-%s'>%h</code></pre>", zExt+1, zContent); }else{ CX("<pre>%h</pre>", zContent); } } break; } } } /* ** Renders diffs for ajax routes. pOrig is the "original" (v1) content ** and pContent is the locally-edited (v2) content. diffFlags is any ** set of flags suitable for passing to text_diff(). ** ** zOrigHash, if not NULL, must be the SCM-side hash of pOrig's ** contents. If set, additional information may be built into ** the diff output to enable dynamic loading of additional ** diff context. */ void ajax_render_diff(Blob * pOrig, const char * zOrigHash, Blob *pContent, u64 diffFlags){ Blob out = empty_blob; DiffConfig DCfg; diff_config_init(&DCfg, diffFlags); DCfg.zLeftHash = zOrigHash; text_diff(pOrig, pContent, &out, &DCfg); if(blob_size(&out)==0){ /* nothing to do */ }else{ CX("%b",&out); } blob_reset(&out); } /* ** Uses P(zKey) to fetch a CGI environment variable. If that var is ** NULL or starts with '0' or 'f' then this function returns false, ** else it returns true. */ int ajax_p_bool(char const *zKey){ const char * zVal = P(zKey); return (!zVal || '0'==*zVal || 'f'==*zVal) ? 0 : 1; } /* ** Helper for /ajax routes. Clears the CGI content buffer, sets an ** HTTP error status code, and queues up a JSON response in the form ** of an object: ** ** {error: formatted message} ** ** If httpCode<=0 then it defaults to 500. ** ** After calling this, the caller should immediately return. */ void ajax_route_error(int httpCode, const char * zFmt, ...){ Blob msg = empty_blob; Blob content = empty_blob; va_list vargs; va_start(vargs,zFmt); blob_vappendf(&msg, zFmt, vargs); va_end(vargs); blob_appendf(&content,"{\"error\":%!j}", blob_str(&msg)); blob_reset(&msg); cgi_set_content(&content); cgi_set_status(httpCode>0 ? httpCode : 500, "Error"); cgi_set_content_type("application/json"); } /* ** Performs bootstrapping common to the /ajax/xyz AJAX routes, such as ** logging in the user. ** ** Returns false (0) if bootstrapping fails, in which case it has ** reported the error and the route should immediately return. Returns ** true on success. ** ** If requireWrite is true then write permissions are required. ** If requirePost is true then the request is assumed to be using ** POST'ed data and CSRF validation is performed. ** */ int ajax_route_bootstrap(int requireWrite, int requirePost){ login_check_credentials(); if( requireWrite!=0 && g.perm.Write==0 ){ ajax_route_error(403,"Write permissions required."); return 0; }else if(0==cgi_csrf_safe(requirePost)){ ajax_route_error(403, "CSRF violation (make sure sending of HTTP " "Referer headers is enabled for XHR " "connections)."); return 0; } return 1; } /* ** Helper for collecting filename/check-in request parameters. ** ** If zFn is not NULL, it is assigned the value of the first one of ** the "filename" or "fn" CGI parameters which is set. ** ** If zCi is not NULL, it is assigned the value of the first one of ** the "checkin" or "ci" CGI parameters which is set. ** ** If a parameter is not NULL, it will be assigned NULL if the ** corresponding parameter is not set. ** ** Returns the number of non-NULL values it assigns to arguments. Thus ** if passed (&x, NULL), it returns 1 if it assigns non-NULL to *x and ** 0 if it assigns NULL to *x. */ int ajax_get_fnci_args( const char **zFn, const char **zCi ){ int rc = 0; if(zCi!=0){ *zCi = PD("checkin",P("ci")); if( *zCi ) ++rc; } if(zFn!=0){ *zFn = PD("filename",P("fn")); if (*zFn) ++rc; } return rc; } /* ** AJAX route /ajax/preview-text ** ** Required query parameters: ** ** filename=name of content, for use in determining the ** mimetype/render mode. ** ** content=text ** ** Optional query parameters: ** ** render_mode=integer (AJAX_RENDER_xxx) (default=AJAX_RENDER_GUESS) ** ** ln=0 or 1 to disable/enable line number mode in ** AJAX_RENDER_PLAIN_TEXT mode. ** ** iframe_height=integer (default=40) Height, in EMs of HTML preview ** iframe. ** ** User must have Write access to use this page. ** ** Responds with the HTML content of the preview. On error it produces ** a JSON response as documented for ajax_route_error(). ** ** Extra response headers: ** ** x-ajax-render-mode: string representing the rendering mode ** which was really used (which will differ from the requested mode ** only if mode 0 (guess) was requested). The names are documented ** below in code and match those in the emitted JS object ** fossil.page.previewModes. */ void ajax_route_preview_text(void){ const char * zFilename = 0; const char * zContent = P("content"); int renderMode = atoi(PD("render_mode","0")); int ln = atoi(PD("ln","0")); int iframeHeight = atoi(PD("iframe_height","40")); Blob content = empty_blob; const char * zRenderMode = 0; ajax_get_fnci_args( &zFilename, 0 ); if(!ajax_route_bootstrap(1,1)){ return; } if(zFilename==0){ /* The filename is only used for mimetype determination, ** so we can default it... */ zFilename = "foo.txt"; } cgi_set_content_type("text/html"); blob_init(&content, zContent, -1); ajax_render_preview(&content, zFilename, ln ? AJAX_PREVIEW_LINE_NUMBERS : 0, &renderMode, iframeHeight); /* ** Now tell the caller if we did indeed use AJAX_RENDER_WIKI, so that ** they can re-set the <base href> to an appropriate value (which ** requires knowing the content's current check-in version, which we ** don't have here). */ switch(renderMode){ /* The strings used here MUST correspond to those used in the JS-side ** fossil.page.previewModes map. */ case AJAX_RENDER_WIKI: zRenderMode = "wiki"; break; case AJAX_RENDER_HTML_INLINE: zRenderMode = "htmlInline"; break; case AJAX_RENDER_HTML_IFRAME: zRenderMode = "htmlIframe"; break; case AJAX_RENDER_PLAIN_TEXT: zRenderMode = "text"; break; case AJAX_RENDER_GUESS: assert(!"cannot happen"); } if(zRenderMode!=0){ cgi_printf_header("x-ajax-render-mode: %s\r\n", zRenderMode); } } #if INTERFACE /* ** Internal mapping of ajax sub-route names to various metadata. */ struct AjaxRoute { const char *zName; /* Name part of the route after "ajax/" */ void (*xCallback)(); /* Impl function for the route. */ int bWriteMode; /* True if requires write mode */ int bPost; /* True if requires POST (i.e. CSRF ** verification) */ }; typedef struct AjaxRoute AjaxRoute; #endif /*INTERFACE*/ /* ** Comparison function for bsearch() for searching an AjaxRoute ** list for a matching name. */ int cmp_ajax_route_name(const void *a, const void *b){ const AjaxRoute * rA = (const AjaxRoute*)a; const AjaxRoute * rB = (const AjaxRoute*)b; return fossil_strcmp(rA->zName, rB->zName); } /* ** WEBPAGE: ajax hidden ** ** The main dispatcher for shared ajax-served routes. Requires the ** 'name' parameter be the main route's name (as defined in a list in ** this function), noting that fossil automatically assigns all path ** parts after "ajax" to "name", e.g. /ajax/foo/bar assigns ** name=foo/bar. ** ** This "page" is only intended to be used by higher-level pages which ** have certain Ajax-driven features in common. It is not intended to ** be used by clients and NONE of its HTTP interfaces are considered ** documented/stable/supported - they may change on any given build of ** fossil. ** ** The exact response type depends on the route which gets called. In ** the case of an initialization error it emits a JSON-format response ** as documented for ajax_route_error(). Individual routes may emit ** errors in different formats, e.g. HTML. */ void ajax_route_dispatcher(void){ const char * zName = P("name"); AjaxRoute routeName = {0,0,0,0}; const AjaxRoute * pRoute = 0; const AjaxRoute routes[] = { /* Keep these sorted by zName (for bsearch()) */ {"preview-text", ajax_route_preview_text, 0, 1 /* Note that this does not require write permissions in the repo. ** It should arguably require write permissions but doing means ** that /chat does not work without checkin permissions: ** ** https://fossil-scm.org/forum/forumpost/ed4a762b3a557898 ** ** This particular route is used by /fileedit and /chat, whereas ** /wikiedit uses a simpler wiki-specific route. */ } }; if(zName==0 || zName[0]==0){ ajax_route_error(400,"Missing required [route] 'name' parameter."); return; } routeName.zName = zName; pRoute = (const AjaxRoute *)bsearch(&routeName, routes, count(routes), sizeof routes[0], cmp_ajax_route_name); if(pRoute==0){ ajax_route_error(404,"Ajax route not found."); return; }else if(0==ajax_route_bootstrap(pRoute->bWriteMode, pRoute->bPost)){ return; } pRoute->xCallback(); } |
Added src/alerts.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 | /* ** Copyright (c) 2018 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/ ** ******************************************************************************* ** ** Logic for email notification, also known as "alerts" or "subscriptions". ** ** Are you looking for the code that reads and writes the internet ** email protocol? That is not here. See the "smtp.c" file instead. ** Yes, the choice of source code filenames is not the greatest, but ** it is not so bad that changing them seems justified. */ #include "config.h" #include "alerts.h" #include <assert.h> #include <time.h> /* ** Maximum size of the subscriberCode blob, in bytes */ #define SUBSCRIBER_CODE_SZ 32 /* ** SQL code to implement the tables needed by the email notification ** system. */ static const char zAlertInit[] = @ DROP TABLE IF EXISTS repository.subscriber; @ -- Subscribers are distinct from users. A person can have a log-in in @ -- the USER table without being a subscriber. Or a person can be a @ -- subscriber without having a USER table entry. Or they can have both. @ -- In the last case the suname column points from the subscriber entry @ -- to the USER entry. @ -- @ -- The ssub field is a string where each character indicates a particular @ -- type of event to subscribe to. Choices: @ -- a - Announcements @ -- c - Check-ins @ -- f - Forum posts @ -- k - ** Special: Unsubscribed using /oneclickunsub @ -- n - New forum threads @ -- r - Replies to my own forum posts @ -- t - Ticket changes @ -- w - Wiki changes @ -- x - Edits to forum posts @ -- Probably different codes will be added in the future. In the future @ -- we might also add a separate table that allows subscribing to email @ -- notifications for specific branches or tags or tickets. @ -- @ CREATE TABLE repository.subscriber( @ subscriberId INTEGER PRIMARY KEY, -- numeric subscriber ID. Internal use @ subscriberCode BLOB DEFAULT (randomblob(32)) UNIQUE, -- UUID for subscriber @ semail TEXT UNIQUE COLLATE nocase,-- email address @ suname TEXT, -- corresponding USER entry @ sverified BOOLEAN DEFAULT true, -- email address verified @ sdonotcall BOOLEAN, -- true for Do Not Call @ sdigest BOOLEAN, -- true for daily digests only @ ssub TEXT, -- baseline subscriptions @ sctime INTDATE, -- When this entry was created. unixtime @ mtime INTDATE, -- Last change. unixtime @ smip TEXT, -- IP address of last change @ lastContact INT -- Last contact. days since 1970 @ ); @ CREATE INDEX repository.subscriberUname @ ON subscriber(suname) WHERE suname IS NOT NULL; @ @ DROP TABLE IF EXISTS repository.pending_alert; @ -- Email notifications that need to be sent. @ -- @ -- The first character of the eventid determines the event type. @ -- Remaining characters determine the specific event. For example, @ -- 'c4413' means check-in with rid=4413. @ -- @ CREATE TABLE repository.pending_alert( @ eventid TEXT PRIMARY KEY, -- Object that changed @ sentSep BOOLEAN DEFAULT false, -- individual alert sent @ sentDigest BOOLEAN DEFAULT false, -- digest alert sent @ sentMod BOOLEAN DEFAULT false -- pending moderation alert sent @ ) WITHOUT ROWID; @ @ -- Obsolete table. No longer used. @ DROP TABLE IF EXISTS repository.alert_bounce; ; /* ** Return true if the email notification tables exist. */ int alert_tables_exist(void){ return db_table_exists("repository", "subscriber"); } /* ** Record the fact that user zUser has made contact with the repository. ** This resets the subscription timeout on that user. */ void alert_user_contact(const char *zUser){ if( db_table_has_column("repository","subscriber","lastContact") ){ db_unprotect(PROTECT_READONLY); db_multi_exec( "UPDATE subscriber SET lastContact=now()/86400 WHERE suname=%Q", zUser ); db_protect_pop(); } } /* ** Make sure the table needed for email notification exist in the repository. ** ** If the bOnlyIfEnabled option is true, then tables are only created ** if the email-send-method is something other than "off". */ void alert_schema(int bOnlyIfEnabled){ if( !alert_tables_exist() ){ if( bOnlyIfEnabled && fossil_strcmp(db_get("email-send-method",0),"off")==0 ){ return; /* Don't create table for disabled email */ } db_exec_sql(zAlertInit); return; } if( db_table_has_column("repository","subscriber","lastContact") ){ return; } db_unprotect(PROTECT_READONLY); db_multi_exec( "DROP TABLE IF EXISTS repository.alert_bounce;\n" "ALTER TABLE repository.subscriber ADD COLUMN lastContact INT;\n" "UPDATE subscriber SET lastContact=mtime/86400;" ); db_protect_pop(); if( db_table_has_column("repository","pending_alert","sentMod") ){ return; } db_multi_exec( "ALTER TABLE repository.pending_alert" " ADD COLUMN sentMod BOOLEAN DEFAULT false;" ); } /* ** Process deferred alert events. Return the number of errors. */ static int alert_process_deferred_triggers(void){ if( db_table_exists("temp","deferred_chat_events") && db_table_exists("repository","chat") ){ const char *zChatUser = db_get("chat-timeline-user", 0); if( zChatUser && zChatUser[0] ){ db_multi_exec( "INSERT INTO chat(mtime,lmtime,xfrom,xmsg)" " SELECT julianday(), " " strftime('%%Y-%%m-%%dT%%H:%%M:%%S','now','localtime')," " %Q," " chat_msg_from_event(type, objid, user, comment)\n" " FROM deferred_chat_events;\n", zChatUser ); } } return 0; } /* ** Enable triggers that automatically populate the pending_alert ** table. (Later:) Also add triggers that automatically relay timeline ** events to chat, if chat is configured for that. */ void alert_create_trigger(void){ if( db_table_exists("repository","pending_alert") ){ db_multi_exec( "DROP TRIGGER IF EXISTS repository.alert_trigger1;\n" /* Purge legacy */ "CREATE TRIGGER temp.alert_trigger1\n" "AFTER INSERT ON repository.event BEGIN\n" " INSERT INTO pending_alert(eventid)\n" " SELECT printf('%%.1c%%d',new.type,new.objid) WHERE true\n" " ON CONFLICT(eventId) DO NOTHING;\n" "END;" ); } if( db_table_exists("repository","chat") && db_get("chat-timeline-user", "")[0]!=0 ){ /* Record events that will be relayed to chat, but do not relay ** them immediately, as the chat_msg_from_event() function requires ** that TAGXREF be up-to-date, and that has not happened yet when ** the insert into the EVENT table occurs. Make arrangements to ** invoke alert_process_deferred_triggers() when the transaction ** commits. The TAGXREF table will be ready by then. */ db_multi_exec( "CREATE TABLE temp.deferred_chat_events(\n" " type TEXT,\n" " objid INT,\n" " user TEXT,\n" " comment TEXT\n" ");\n" "CREATE TRIGGER temp.chat_trigger1\n" "AFTER INSERT ON repository.event BEGIN\n" " INSERT INTO deferred_chat_events" " VALUES(new.type,new.objid,new.user,new.comment);\n" "END;\n" ); db_commit_hook(alert_process_deferred_triggers, 1); } } /* ** Disable triggers the event_pending and chat triggers. ** ** This must be called before rebuilding the EVENT table, for example ** via the "fossil rebuild" command. */ void alert_drop_trigger(void){ db_multi_exec( "DROP TRIGGER IF EXISTS temp.alert_trigger1;\n" "DROP TRIGGER IF EXISTS repository.alert_trigger1;\n" /* Purge legacy */ "DROP TRIGGER IF EXISTS temp.chat_trigger1;\n" ); } /* ** Return true if email alerts are active. */ int alert_enabled(void){ if( !alert_tables_exist() ) return 0; if( fossil_strcmp(db_get("email-send-method",0),"off")==0 ) return 0; return 1; } /* ** If alerts are enabled, removes the pending_alert entry which ** matches (eventType || rid). Note that pending_alert entries are ** added via the manifest crosslinking process, so this has no effect ** if called before crosslinking is performed. Because alerts are sent ** asynchronously, unqueuing needs to be performed as part of the ** transaction in which crosslinking is performed in order to avoid a ** race condition. */ void alert_unqueue(char eventType, int rid){ if( alert_enabled() ){ db_multi_exec("DELETE FROM pending_alert WHERE eventid='%c%d'", eventType, rid); } } /* ** If the subscriber table does not exist, then paint an error message ** web page and return true. ** ** If the subscriber table does exist, return 0 without doing anything. */ static int alert_webpages_disabled(void){ if( alert_tables_exist() ) return 0; style_set_current_feature("alerts"); style_header("Email Alerts Are Disabled"); @ <p>Email alerts are disabled on this server</p> style_finish_page(); return 1; } /* ** Insert a "Subscriber List" submenu link if the current user ** is an administrator. */ void alert_submenu_common(void){ if( g.perm.Admin ){ if( fossil_strcmp(g.zPath,"subscribers") ){ style_submenu_element("Subscribers","%R/subscribers"); } if( fossil_strcmp(g.zPath,"subscribe") ){ style_submenu_element("Add New Subscriber","%R/subscribe"); } } } /* ** WEBPAGE: setup_notification ** ** Administrative page for configuring and controlling email notification. ** Normally accessible via the /Admin/Notification menu. */ void setup_notification(void){ static const char *const azSendMethods[] = { "off", "Disabled", "pipe", "Pipe to a command", "db", "Store in a database", "dir", "Store in a directory", "relay", "SMTP relay" }; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } db_begin_transaction(); alert_submenu_common(); style_submenu_element("Send Announcement","%R/announce"); style_set_current_feature("alerts"); style_header("Email Notification Setup"); @ <h1>Status</h1> @ <table class="label-value"> if( alert_enabled() ){ stats_for_email(); }else{ @ <th>Disabled</th> } @ </table> @ <hr> @ <h1> Configuration </h1> @ <form action="%R/setup_notification" method="post"><div> @ <input type="submit" name="submit" value="Apply Changes"><hr> login_insert_csrf_secret(); entry_attribute("Canonical Server URL", 40, "email-url", "eurl", "", 0); @ <p><b>Required.</b> @ This is URL used as the basename for hyperlinks included in @ email alert text. Omit the trailing "/". @ Suggested value: "%h(g.zBaseURL)" @ (Property: "email-url")</p> @ <hr> entry_attribute("Administrator email address", 40, "email-admin", "eadmin", "", 0); @ <p>This is the email for the human administrator for the system. @ Abuse and trouble reports and password reset requests are send here. @ (Property: "email-admin")</p> @ <hr> entry_attribute("\"Return-Path\" email address", 20, "email-self", "eself", "", 0); @ <p><b>Required.</b> @ This is the email to which email notification bounces should be sent. @ In cases where the email notification does not align with a specific @ Fossil login account (for example, digest messages), this is also @ the "From:" address of the email notification. @ The system administrator should arrange for emails sent to this address @ to be handed off to the "fossil email incoming" command so that Fossil @ can handle bounces. (Property: "email-self")</p> @ <hr> entry_attribute("List-ID", 40, "email-listid", "elistid", "", 0); @ <p> @ If this is not an empty string, then it becomes the argument to @ a "List-ID:" header on all out-bound notification emails. @ (Property: "email-listid")</p> @ <hr> entry_attribute("Repository Nickname", 16, "email-subname", "enn", "", 0); @ <p><b>Required.</b> @ This is short name used to identifies the repository in the @ Subject: line of email alerts. Traditionally this name is @ included in square brackets. Examples: "[fossil-src]", "[sqlite-src]". @ (Property: "email-subname")</p> @ <hr> entry_attribute("Subscription Renewal Interval In Days", 8, "email-renew-interval", "eri", "", 0); @ <p> @ If this value is an integer N greater than or equal to 14, then email @ notification subscriptions will be suspended N days after the last known @ interaction with the user. This prevents sending notifications @ to abandoned accounts. If a subscription comes within 7 days of expiring, @ a separate email goes out with the daily digest that prompts the @ subscriber to click on a link to the "/renew" webpage in order to @ extend their subscription. Subscriptions never expire if this setting @ is less than 14 or is an empty string. @ (Property: "email-renew-interval")</p> @ <hr> multiple_choice_attribute("Email Send Method", "email-send-method", "esm", "off", count(azSendMethods)/2, azSendMethods); @ <p>How to send email. Requires auxiliary information from the fields @ that follow. Hint: Use the <a href="%R/announce">/announce</a> page @ to send test message to debug this setting. @ (Property: "email-send-method")</p> alert_schema(1); entry_attribute("Pipe Email Text Into This Command", 60, "email-send-command", "ecmd", "sendmail -ti", 0); @ <p>When the send method is "pipe to a command", this is the command @ that is run. Email messages are piped into the standard input of this @ command. The command is expected to extract the sender address, @ recipient addresses, and subject from the header of the piped email @ text. (Property: "email-send-command")</p> entry_attribute("Store Emails In This Database", 60, "email-send-db", "esdb", "", 0); @ <p>When the send method is "store in a database", each email message is @ stored in an SQLite database file with the name given here. @ (Property: "email-send-db")</p> entry_attribute("Store Emails In This Directory", 60, "email-send-dir", "esdir", "", 0); @ <p>When the send method is "store in a directory", each email message is @ stored as a separate file in the directory shown here. @ (Property: "email-send-dir")</p> entry_attribute("SMTP Relay Host", 60, "email-send-relayhost", "esrh", "", 0); @ <p>When the send method is "SMTP relay", each email message is @ transmitted via the SMTP protocol (rfc5321) to a "Mail Submission @ Agent" or "MSA" (rfc4409) at the hostname shown here. Optionally @ append a colon and TCP port number (ex: smtp.example.com:587). @ The default TCP port number is 25. @ (Property: "email-send-relayhost")</p> @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); style_finish_page(); } #if 0 /* ** Encode pMsg as MIME base64 and append it to pOut */ static void append_base64(Blob *pOut, Blob *pMsg){ int n, i, k; char zBuf[100]; n = blob_size(pMsg); for(i=0; i<n; i+=54){ k = translateBase64(blob_buffer(pMsg)+i, i+54<n ? 54 : n-i, zBuf); blob_append(pOut, zBuf, k); blob_append(pOut, "\r\n", 2); } } #endif /* ** Encode pMsg using the quoted-printable email encoding and ** append it onto pOut */ static void append_quoted(Blob *pOut, Blob *pMsg){ char *zIn = blob_str(pMsg); char c; int iCol = 0; while( (c = *(zIn++))!=0 ){ if( (c>='!' && c<='~' && c!='=' && c!=':') || (c==' ' && zIn[0]!='\r' && zIn[0]!='\n') ){ blob_append_char(pOut, c); iCol++; if( iCol>=70 ){ blob_append(pOut, "=\r\n", 3); iCol = 0; } }else if( c=='\r' && zIn[0]=='\n' ){ zIn++; blob_append(pOut, "\r\n", 2); iCol = 0; }else if( c=='\n' ){ blob_append(pOut, "\r\n", 2); iCol = 0; }else{ char x[3]; x[0] = '='; x[1] = "0123456789ABCDEF"[(c>>4)&0xf]; x[2] = "0123456789ABCDEF"[c&0xf]; blob_append(pOut, x, 3); iCol += 3; } } } #if INTERFACE /* ** An instance of the following object is used to send emails. */ struct AlertSender { sqlite3 *db; /* Database emails are sent to */ sqlite3_stmt *pStmt; /* Stmt to insert into the database */ const char *zDest; /* How to send email. */ const char *zDb; /* Name of database file */ const char *zDir; /* Directory in which to store as email files */ const char *zCmd; /* Command to run for each email */ const char *zFrom; /* Emails come from here */ const char *zListId; /* Argument to List-ID header */ SmtpSession *pSmtp; /* SMTP relay connection */ Blob out; /* For zDest=="blob" */ char *zErr; /* Error message */ u32 mFlags; /* Flags */ int bImmediateFail; /* On any error, call fossil_fatal() */ }; /* Allowed values for mFlags to alert_sender_new(). */ #define ALERT_IMMEDIATE_FAIL 0x0001 /* Call fossil_fatal() on any error */ #define ALERT_TRACE 0x0002 /* Log sending process on console */ #endif /* INTERFACE */ /* ** Shutdown an emailer. Clear all information other than the error message. */ static void emailerShutdown(AlertSender *p){ sqlite3_finalize(p->pStmt); p->pStmt = 0; sqlite3_close(p->db); p->db = 0; p->zDb = 0; p->zDir = 0; p->zCmd = 0; p->zListId = 0; if( p->pSmtp ){ smtp_client_quit(p->pSmtp); smtp_session_free(p->pSmtp); p->pSmtp = 0; } blob_reset(&p->out); } /* ** Put the AlertSender into an error state. */ static void emailerError(AlertSender *p, const char *zFormat, ...){ va_list ap; fossil_free(p->zErr); va_start(ap, zFormat); p->zErr = vmprintf(zFormat, ap); va_end(ap); emailerShutdown(p); if( p->mFlags & ALERT_IMMEDIATE_FAIL ){ fossil_fatal("%s", p->zErr); } } /* ** Free an email sender object */ void alert_sender_free(AlertSender *p){ if( p ){ emailerShutdown(p); fossil_free(p->zErr); fossil_free(p); } } /* ** Get an email setting value. Report an error if not configured. ** Return 0 on success and one if there is an error. */ static int emailerGetSetting( AlertSender *p, /* Where to report the error */ const char **pzVal, /* Write the setting value here */ const char *zName /* Name of the setting */ ){ const char *z = db_get(zName, 0); int rc = 0; if( z==0 || z[0]==0 ){ emailerError(p, "missing \"%s\" setting", zName); rc = 1; }else{ *pzVal = z; } return rc; } /* ** Create a new AlertSender object. ** ** The method used for sending email is determined by various email-* ** settings, and especially email-send-method. The repository ** email-send-method can be overridden by the zAltDest argument to ** cause a different sending mechanism to be used. Pass "stdout" to ** zAltDest to cause all emails to be printed to the console for ** debugging purposes. ** ** The AlertSender object returned must be freed using alert_sender_free(). */ AlertSender *alert_sender_new(const char *zAltDest, u32 mFlags){ AlertSender *p; p = fossil_malloc(sizeof(*p)); memset(p, 0, sizeof(*p)); blob_init(&p->out, 0, 0); p->mFlags = mFlags; if( zAltDest ){ p->zDest = zAltDest; }else{ p->zDest = db_get("email-send-method",0); } if( fossil_strcmp(p->zDest,"off")==0 ) return p; if( emailerGetSetting(p, &p->zFrom, "email-self") ) return p; p->zListId = db_get("email-listid", 0); if( fossil_strcmp(p->zDest,"db")==0 ){ char *zErr; int rc; if( emailerGetSetting(p, &p->zDb, "email-send-db") ) return p; rc = sqlite3_open(p->zDb, &p->db); if( rc ){ emailerError(p, "unable to open output database file \"%s\": %s", p->zDb, sqlite3_errmsg(p->db)); return p; } rc = sqlite3_exec(p->db, "CREATE TABLE IF NOT EXISTS email(\n" " emailid INTEGER PRIMARY KEY,\n" " msg TEXT\n);", 0, 0, &zErr); if( zErr ){ emailerError(p, "CREATE TABLE failed with \"%s\"", zErr); sqlite3_free(zErr); return p; } rc = sqlite3_prepare_v2(p->db, "INSERT INTO email(msg) VALUES(?1)", -1, &p->pStmt, 0); if( rc ){ emailerError(p, "cannot prepare INSERT statement: %s", sqlite3_errmsg(p->db)); return p; } }else if( fossil_strcmp(p->zDest, "pipe")==0 ){ emailerGetSetting(p, &p->zCmd, "email-send-command"); }else if( fossil_strcmp(p->zDest, "dir")==0 ){ emailerGetSetting(p, &p->zDir, "email-send-dir"); }else if( fossil_strcmp(p->zDest, "blob")==0 ){ blob_init(&p->out, 0, 0); }else if( fossil_strcmp(p->zDest, "relay")==0 ){ const char *zRelay = 0; emailerGetSetting(p, &zRelay, "email-send-relayhost"); if( zRelay ){ u32 smtpFlags = SMTP_DIRECT; if( mFlags & ALERT_TRACE ) smtpFlags |= SMTP_TRACE_STDOUT; p->pSmtp = smtp_session_new(domain_of_addr(p->zFrom), zRelay, smtpFlags); smtp_client_startup(p->pSmtp); } } return p; } /* ** Scan the header of the email message in pMsg looking for the ** (first) occurrance of zField. Fill pValue with the content of ** that field. ** ** This routine initializes pValue. Any prior content of pValue is ** discarded (leaked). ** ** Return non-zero on success. Return 0 if no instance of the header ** is found. */ int email_header_value(Blob *pMsg, const char *zField, Blob *pValue){ int nField = (int)strlen(zField); Blob line; blob_rewind(pMsg); blob_init(pValue,0,0); while( blob_line(pMsg, &line) ){ int n, i; char *z; blob_trim(&line); n = blob_size(&line); if( n==0 ) return 0; if( n<nField+1 ) continue; z = blob_buffer(&line); if( sqlite3_strnicmp(z, zField, nField)==0 && z[nField]==':' ){ for(i=nField+1; i<n && fossil_isspace(z[i]); i++){} blob_init(pValue, z+i, n-i); while( blob_line(pMsg, &line) ){ blob_trim(&line); n = blob_size(&line); if( n==0 ) break; z = blob_buffer(&line); if( !fossil_isspace(z[0]) ) break; for(i=1; i<n && fossil_isspace(z[i]); i++){} blob_append(pValue, " ", 1); blob_append(pValue, z+i, n-i); } return 1; } } return 0; } /* ** Determine whether or not the input string is a valid email address. ** Only look at character up to but not including the first \000 or ** the first cTerm character, whichever comes first. ** ** Return the length of the email addresss string in bytes if the email ** address is valid. If the email address is misformed, return 0. */ int email_address_is_valid(const char *z, char cTerm){ int i; int nAt = 0; int nDot = 0; char c; if( z[0]=='.' ) return 0; /* Local part cannot begin with "." */ for(i=0; (c = z[i])!=0 && c!=cTerm; i++){ if( fossil_isalnum(c) ){ /* Alphanumerics are always ok */ }else if( c=='@' ){ if( nAt ) return 0; /* Only a single "@" allowed */ if( i>64 ) return 0; /* Local part too big */ nAt = 1; nDot = 0; if( i==0 ) return 0; /* Disallow empty local part */ if( z[i-1]=='.' ) return 0; /* Last char of local cannot be "." */ if( z[i+1]=='.' || z[i+1]=='-' ){ return 0; /* Domain cannot begin with "." or "-" */ } }else if( c=='-' ){ if( z[i+1]==cTerm ) return 0; /* Last character cannot be "-" */ }else if( c=='.' ){ if( z[i+1]=='.' ) return 0; /* Do not allow ".." */ if( z[i+1]==cTerm ) return 0; /* Domain may not end with . */ nDot++; }else if( (c=='_' || c=='+') && nAt==0 ){ /* _ and + are ok in the local part */ }else{ return 0; /* Anything else is an error */ } } if( c!=cTerm ) return 0; /* Missing terminator */ if( nAt==0 ) return 0; /* No "@" found anywhere */ if( nDot==0 ) return 0; /* No "." in the domain */ return i; } /* ** Make a copy of the input string up to but not including the ** first cTerm character. ** ** Verify that the string to be copied really is a valid ** email address. If it is not, then return NULL. ** ** This routine is more restrictive than necessary. It does not ** allow comments, IP address, quoted strings, or certain uncommon ** characters. The only non-alphanumerics allowed in the local ** part are "_", "+", "-" and "+". */ char *email_copy_addr(const char *z, char cTerm ){ int i = email_address_is_valid(z, cTerm); return i==0 ? 0 : mprintf("%.*s", i, z); } /* ** Scan the input string for a valid email address that may be ** enclosed in <...>, or delimited by ',' or ':' or '=' or ' '. ** If the string contains one or more email addresses, extract the first ** one into memory obtained from mprintf() and return a pointer to it. ** If no valid email address can be found, return NULL. */ char *alert_find_emailaddr(const char *zIn){ char *zOut = 0; do{ zOut = email_copy_addr(zIn, zIn[strcspn(zIn, ">,:= ")]); if( zOut!=0 ) break; zIn = (const char *)strpbrk(zIn, "<,:= "); if( zIn==0 ) break; zIn++; }while( zIn!=0 ); return zOut; } /* ** SQL function: find_emailaddr(X) ** ** Return the first valid email address of the form <...> in input string ** X. Or return NULL if not found. */ void alert_find_emailaddr_func( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zIn = (const char*)sqlite3_value_text(argv[0]); char *zOut = alert_find_emailaddr(zIn); if( zOut ){ sqlite3_result_text(context, zOut, -1, fossil_free); } } /* ** SQL function: display_name(X) ** ** If X is a string, search for a user name at the beginning of that ** string. The user name must be followed by an email address. If ** found, return the user name. If not found, return NULL. ** ** This routine is used to extract the display name from the USER.INFO ** field. */ void alert_display_name_func( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zIn = (const char*)sqlite3_value_text(argv[0]); int i; if( zIn==0 ) return; while( fossil_isspace(zIn[0]) ) zIn++; for(i=0; zIn[i] && zIn[i]!='<' && zIn[i]!='\n'; i++){} if( zIn[i]=='<' ){ while( i>0 && fossil_isspace(zIn[i-1]) ){ i--; } if( i>0 ){ sqlite3_result_text(context, zIn, i, SQLITE_TRANSIENT); } } } /* ** Return the hostname portion of an email address - the part following ** the @ */ char *alert_hostname(const char *zAddr){ char *z = strchr(zAddr, '@'); if( z ){ z++; }else{ z = (char*)zAddr; } return z; } /* ** Return a pointer to a fake email mailbox name that corresponds ** to human-readable name zFromName. The fake mailbox name is based ** on a hash. No huge problems arise if there is a hash collisions, ** but it is still better if collisions can be avoided. ** ** The returned string is held in a static buffer and is overwritten ** by each subsequent call to this routine. */ static char *alert_mailbox_name(const char *zFromName){ static char zHash[20]; unsigned int x = 0; int n = 0; while( zFromName[0] ){ n++; x = x*1103515245 + 12345 + ((unsigned char*)zFromName)[0]; zFromName++; } sqlite3_snprintf(sizeof(zHash), zHash, "noreply%x%08x", n, x); return zHash; } /* ** COMMAND: test-mailbox-hashname ** ** Usage: %fossil test-mailbox-hashname HUMAN-NAME ... ** ** Return the mailbox hash name corresponding to each human-readable ** name on the command line. This is a test interface for the ** alert_mailbox_name() function. */ void alert_test_mailbox_hashname(void){ int i; for(i=2; i<g.argc; i++){ fossil_print("%30s: %s\n", g.argv[i], alert_mailbox_name(g.argv[i])); } } /* ** Extract all To: header values from the email header supplied. ** Store them in the array list. */ void email_header_to(Blob *pMsg, int *pnTo, char ***pazTo){ int nTo = 0; char **azTo = 0; Blob v; char *z, *zAddr; int i; email_header_value(pMsg, "to", &v); z = blob_str(&v); for(i=0; z[i]; i++){ if( z[i]=='<' && (zAddr = email_copy_addr(&z[i+1],'>'))!=0 ){ azTo = fossil_realloc(azTo, sizeof(azTo[0])*(nTo+1) ); azTo[nTo++] = zAddr; } } *pnTo = nTo; *pazTo = azTo; } /* ** Free a list of To addresses obtained from a prior call to ** email_header_to() */ void email_header_to_free(int nTo, char **azTo){ int i; for(i=0; i<nTo; i++) fossil_free(azTo[i]); fossil_free(azTo); } /* ** Send a single email message. ** ** The recipient(s) must be specified using "To:" or "Cc:" or "Bcc:" fields ** in the header. Likewise, the header must contains a "Subject:" line. ** The header might also include fields like "Message-Id:" or ** "In-Reply-To:". ** ** This routine will add fields to the header as follows: ** ** From: ** Date: ** Message-Id: ** Content-Type: ** Content-Transfer-Encoding: ** MIME-Version: ** Sender: ** ** The caller maintains ownership of the input Blobs. This routine will ** read the Blobs and send them onward to the email system, but it will ** not free them. ** ** The Message-Id: field is added if there is not already a Message-Id ** in the pHdr parameter. ** ** If the zFromName argument is not NULL, then it should be a human-readable ** name or handle for the sender. In that case, "From:" becomes a made-up ** email address based on a hash of zFromName and the domain of email-self, ** and an additional "Sender:" field is inserted with the email-self ** address. Downstream software might use the Sender header to set ** the envelope-from address of the email. If zFromName is a NULL pointer, ** then the "From:" is set to the email-self value and Sender is ** omitted. */ void alert_send( AlertSender *p, /* Emailer context */ Blob *pHdr, /* Email header (incomplete) */ Blob *pBody, /* Email body */ const char *zFromName /* Optional human-readable name of sender */ ){ Blob all, *pOut; u64 r1, r2; if( p->mFlags & ALERT_TRACE ){ fossil_print("Sending email\n"); } if( fossil_strcmp(p->zDest, "off")==0 ){ return; } blob_init(&all, 0, 0); if( fossil_strcmp(p->zDest, "blob")==0 ){ pOut = &p->out; if( blob_size(pOut) ){ blob_appendf(pOut, "%.72c\n", '='); } }else{ pOut = &all; } blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr)); if( p->zFrom==0 || p->zFrom[0]==0 ){ return; /* email-self is not set. Error will be reported separately */ }else if( zFromName ){ blob_appendf(pOut, "From: %s <%s@%s>\r\n", zFromName, alert_mailbox_name(zFromName), alert_hostname(p->zFrom)); blob_appendf(pOut, "Sender: <%s>\r\n", p->zFrom); }else{ blob_appendf(pOut, "From: <%s>\r\n", p->zFrom); } blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0))); if( p->zListId && p->zListId[0] ){ blob_appendf(pOut, "List-Id: %s\r\n", p->zListId); } if( strstr(blob_str(pHdr), "\r\nMessage-Id:")==0 ){ /* Message-id format: "<$(date)x$(random)@$(from-host)>" where $(date) is ** the current unix-time in hex, $(random) is a 64-bit random number, ** and $(from) is the domain part of the email-self setting. */ sqlite3_randomness(sizeof(r1), &r1); r2 = time(0); blob_appendf(pOut, "Message-Id: <%llxx%016llx@%s>\r\n", r2, r1, alert_hostname(p->zFrom)); } blob_add_final_newline(pBody); blob_appendf(pOut, "MIME-Version: 1.0\r\n"); blob_appendf(pOut, "Content-Type: text/plain; charset=\"UTF-8\"\r\n"); #if 0 blob_appendf(pOut, "Content-Transfer-Encoding: base64\r\n\r\n"); append_base64(pOut, pBody); #else blob_appendf(pOut, "Content-Transfer-Encoding: quoted-printable\r\n\r\n"); append_quoted(pOut, pBody); #endif if( p->pStmt ){ int i, rc; sqlite3_bind_text(p->pStmt, 1, blob_str(&all), -1, SQLITE_TRANSIENT); for(i=0; i<100 && sqlite3_step(p->pStmt)==SQLITE_BUSY; i++){ sqlite3_sleep(10); } rc = sqlite3_reset(p->pStmt); if( rc!=SQLITE_OK ){ emailerError(p, "Failed to insert email message into output queue.\n" "%s", sqlite3_errmsg(p->db)); } }else if( p->zCmd ){ FILE *out = popen(p->zCmd, "w"); if( out ){ fwrite(blob_buffer(&all), 1, blob_size(&all), out); pclose(out); }else{ emailerError(p, "Could not open output pipe \"%s\"", p->zCmd); } }else if( p->zDir ){ char *zFile = file_time_tempname(p->zDir, ".email"); blob_write_to_file(&all, zFile); fossil_free(zFile); }else if( p->pSmtp ){ char **azTo = 0; int nTo = 0; email_header_to(pHdr, &nTo, &azTo); if( nTo>0 ){ smtp_send_msg(p->pSmtp, p->zFrom, nTo, (const char**)azTo,blob_str(&all)); email_header_to_free(nTo, azTo); } }else if( strcmp(p->zDest, "stdout")==0 ){ char **azTo = 0; int nTo = 0; int i; email_header_to(pHdr, &nTo, &azTo); for(i=0; i<nTo; i++){ fossil_print("X-To-Test-%d: [%s]\r\n", i, azTo[i]); } email_header_to_free(nTo, azTo); blob_add_final_newline(&all); fossil_print("%s", blob_str(&all)); } blob_reset(&all); } /* ** SETTING: email-url width=40 ** This is the main URL used to access the repository for cloning or ** syncing or for operating the web interface. It is also ** the basename for hyperlinks included in email alert text. ** Omit the trailing "/". If the repository is not intended to be ** a long-running server and will not be sending email notifications, ** then leave this setting blank. */ /* ** SETTING: email-admin width=40 ** This is the email address for the human administrator for the system. ** Abuse and trouble reports and password reset requests are send here. */ /* ** SETTING: email-subname width=16 ** This is a short name used to identifies the repository in the Subject: ** line of email alerts. Traditionally this name is included in square ** brackets. Examples: "[fossil-src]", "[sqlite-src]". */ /* ** SETTING: email-renew-interval width=16 ** If this setting as an integer N that is 14 or greater then email ** notification is suspended for subscriptions that have a "last contact ** time" of more than N days ago. The "last contact time" is recorded ** in the SUBSCRIBER.LASTCONTACT entry of the database. Logging in, ** sending a forum post, editing a wiki page, changing subscription settings ** at /alerts, or visiting /renew all update the last contact time. ** If this setting is not an integer value or is less than 14 or undefined, ** then subscriptions never expire. */ /* X-VARIABLE: email-renew-warning ** X-VARIABLE: email-renew-cutoff ** ** These CONFIG table entries are not considered "settings" since their ** values are computed and updated automatically. ** ** email-renew-cutoff is the lastContact cutoff for subscription. It ** is measured in days since 1970-01-01. If The lastContact time for ** a subscription is less than email-renew-cutoff, then now new emails ** are sent to the subscriber. ** ** email-renew-warning is the time (in days since 1970-01-01) when the ** last batch of "your subscription is about to expire" emails were ** sent out. ** ** email-renew-cutoff is normally 7 days behind email-renew-warning. */ /* ** SETTING: email-send-method width=5 default=off sensitive ** Determine the method used to send email. Allowed values are ** "off", "relay", "pipe", "dir", "db", and "stdout". The "off" value ** means no email is ever sent. The "relay" value means emails are sent ** to an Mail Sending Agent using SMTP located at email-send-relayhost. ** The "pipe" value means email messages are piped into a command ** determined by the email-send-command setting. The "dir" value means ** emails are written to individual files in a directory determined ** by the email-send-dir setting. The "db" value means that emails ** are added to an SQLite database named by the* email-send-db setting. ** The "stdout" value writes email text to standard output, for debugging. */ /* ** SETTING: email-send-command width=40 sensitive ** This is a command to which outbound email content is piped when the ** email-send-method is set to "pipe". The command must extract ** recipient, sender, subject, and all other relevant information ** from the email header. */ /* ** SETTING: email-send-dir width=40 sensitive ** This is a directory into which outbound emails are written as individual ** files if the email-send-method is set to "dir". */ /* ** SETTING: email-send-db width=40 sensitive ** This is an SQLite database file into which outbound emails are written ** if the email-send-method is set to "db". */ /* ** SETTING: email-self width=40 ** This is the email address for the repository. Outbound emails add ** this email address as the "From:" field. */ /* ** SETTING: email-listid width=40 ** If this setting is not an empty string, then it becomes the argument to ** a "List-ID:" header that is added to all out-bound notification emails. */ /* ** SETTING: email-send-relayhost width=40 sensitive ** This is the hostname and TCP port to which output email messages ** are sent when email-send-method is "relay". There should be an ** SMTP server configured as a Mail Submission Agent listening on the ** designated host and port and all times. */ /* ** COMMAND: alerts* ** ** Usage: %fossil alerts SUBCOMMAND ARGS... ** ** Subcommands: ** ** pending Show all pending alerts. Useful for debugging. ** ** reset Hard reset of all email notification tables ** in the repository. This erases all subscription ** information. ** Use with extreme care ** ** ** send Compose and send pending email alerts. ** Some installations may want to do this via ** a cron-job to make sure alerts are sent ** in a timely manner. ** ** Options: ** --digest Send digests ** --renewal Send subscription renewal ** notices ** --test Write to standard output ** ** settings [NAME VALUE] With no arguments, list all email settings. ** Or change the value of a single email setting. ** ** status Report on the status of the email alert ** subsystem ** ** subscribers [PATTERN] List all subscribers matching PATTERN. Either ** LIKE or GLOB wildcards can be used in PATTERN. ** ** test-message TO [OPTS] Send a single email message using whatever ** email sending mechanism is currently configured. ** Use this for testing the email notification ** configuration. ** ** Options: ** --body FILENAME Content from FILENAME ** --smtp-trace Trace SMTP processing ** --stdout Send msg to stdout ** -S|--subject SUBJECT Message "subject:" ** ** unsubscribe EMAIL Remove a single subscriber with the given EMAIL. */ void alert_cmd(void){ const char *zCmd; int nCmd; db_find_and_open_repository(0, 0); alert_schema(0); zCmd = g.argc>=3 ? g.argv[2] : "x"; nCmd = (int)strlen(zCmd); if( strncmp(zCmd, "pending", nCmd)==0 ){ Stmt q; verify_all_options(); if( g.argc!=3 ) usage("pending"); db_prepare(&q,"SELECT eventid, sentSep, sentDigest, sentMod" " FROM pending_alert"); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%10s %7s %10s %7s\n", db_column_text(&q,0), db_column_int(&q,1) ? "sentSep" : "", db_column_int(&q,2) ? "sentDigest" : "", db_column_int(&q,3) ? "sentMod" : ""); } db_finalize(&q); }else if( strncmp(zCmd, "reset", nCmd)==0 ){ int c; int bForce = find_option("force","f",0)!=0; verify_all_options(); if( bForce ){ c = 'y'; }else{ Blob yn; fossil_print( "This will erase all content in the repository tables, thus\n" "deleting all subscriber information. The information will be\n" "unrecoverable.\n"); prompt_user("Continue? (y/N) ", &yn); c = blob_str(&yn)[0]; blob_reset(&yn); } if( c=='y' ){ alert_drop_trigger(); db_multi_exec( "DROP TABLE IF EXISTS subscriber;\n" "DROP TABLE IF EXISTS pending_alert;\n" "DROP TABLE IF EXISTS alert_bounce;\n" /* Legacy */ "DROP TABLE IF EXISTS alert_pending;\n" "DROP TABLE IF EXISTS subscription;\n" ); alert_schema(0); } }else if( strncmp(zCmd, "send", nCmd)==0 ){ u32 eFlags = 0; if( find_option("digest",0,0)!=0 ) eFlags |= SENDALERT_DIGEST; if( find_option("renewal",0,0)!=0 ) eFlags |= SENDALERT_RENEWAL; if( find_option("test",0,0)!=0 ){ eFlags |= SENDALERT_PRESERVE|SENDALERT_STDOUT; } verify_all_options(); alert_send_alerts(eFlags); }else if( strncmp(zCmd, "settings", nCmd)==0 ){ int isGlobal = find_option("global",0,0)!=0; int nSetting; const Setting *pSetting = setting_info(&nSetting); db_open_config(1, 0); verify_all_options(); if( g.argc!=3 && g.argc!=5 ) usage("setting [NAME VALUE]"); if( g.argc==5 ){ const char *zLabel = g.argv[3]; if( strncmp(zLabel, "email-", 6)!=0 || (pSetting = db_find_setting(zLabel, 1))==0 ){ fossil_fatal("not a valid email setting: \"%s\"", zLabel); } db_set(pSetting->name/*works-like:""*/, g.argv[4], isGlobal); g.argc = 3; } pSetting = setting_info(&nSetting); for(; nSetting>0; nSetting--, pSetting++ ){ if( strncmp(pSetting->name,"email-",6)!=0 ) continue; print_setting(pSetting, 0); } }else if( strncmp(zCmd, "status", nCmd)==0 ){ Stmt q; int iCutoff; int nSetting, n; static const char *zFmt = "%-29s %d\n"; const Setting *pSetting = setting_info(&nSetting); db_open_config(1, 0); verify_all_options(); if( g.argc!=3 ) usage("status"); pSetting = setting_info(&nSetting); for(; nSetting>0; nSetting--, pSetting++ ){ if( strncmp(pSetting->name,"email-",6)!=0 ) continue; print_setting(pSetting, 0); } n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep"); fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n); n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest"); fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n); db_prepare(&q, "SELECT" " name," " value," " now()/86400-value," " date(value*86400,'unixepoch')" " FROM repository.config" " WHERE name in ('email-renew-warning','email-renew-cutoff');"); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%-29s %-6d (%d days ago on %s)\n", db_column_text(&q, 0), db_column_int(&q, 1), db_column_int(&q, 2), db_column_text(&q, 3)); } db_finalize(&q); n = db_int(0,"SELECT count(*) FROM subscriber"); fossil_print(zFmt/*works-like:"%s%d"*/, "total-subscribers", n); iCutoff = db_get_int("email-renew-cutoff", 0); n = db_int(0, "SELECT count(*) FROM subscriber WHERE sverified" " AND NOT sdonotcall AND length(ssub)>1" " AND lastContact>=%d", iCutoff); fossil_print(zFmt/*works-like:"%s%d"*/, "active-subscribers", n); }else if( strncmp(zCmd, "subscribers", nCmd)==0 ){ Stmt q; verify_all_options(); if( g.argc!=3 && g.argc!=4 ) usage("subscribers [PATTERN]"); if( g.argc==4 ){ char *zPattern = g.argv[3]; db_prepare(&q, "SELECT semail FROM subscriber" " WHERE semail LIKE '%%%q%%' OR suname LIKE '%%%q%%'" " OR semail GLOB '*%q*' or suname GLOB '*%q*'" " ORDER BY semail", zPattern, zPattern, zPattern, zPattern); }else{ db_prepare(&q, "SELECT semail FROM subscriber" " ORDER BY semail"); } while( db_step(&q)==SQLITE_ROW ){ fossil_print("%s\n", db_column_text(&q, 0)); } db_finalize(&q); }else if( strncmp(zCmd, "test-message", nCmd)==0 ){ Blob prompt, body, hdr; const char *zDest = find_option("stdout",0,0)!=0 ? "stdout" : 0; int i; u32 mFlags = ALERT_IMMEDIATE_FAIL; const char *zSubject = find_option("subject", "S", 1); const char *zSource = find_option("body", 0, 1); AlertSender *pSender; if( find_option("smtp-trace",0,0)!=0 ) mFlags |= ALERT_TRACE; verify_all_options(); blob_init(&prompt, 0, 0); blob_init(&body, 0, 0); blob_init(&hdr, 0, 0); blob_appendf(&hdr,"To: "); for(i=3; i<g.argc; i++){ if( i>3 ) blob_append(&hdr, ", ", 2); blob_appendf(&hdr, "<%s>", g.argv[i]); } blob_append(&hdr,"\r\n",2); if( zSubject==0 ) zSubject = "fossil alerts test-message"; blob_appendf(&hdr, "Subject: %s\r\n", zSubject); if( zSource ){ blob_read_from_file(&body, zSource, ExtFILE); }else{ prompt_for_user_comment(&body, &prompt); } blob_add_final_newline(&body); pSender = alert_sender_new(zDest, mFlags); alert_send(pSender, &hdr, &body, 0); alert_sender_free(pSender); blob_reset(&hdr); blob_reset(&body); blob_reset(&prompt); }else if( strncmp(zCmd, "unsubscribe", nCmd)==0 ){ verify_all_options(); if( g.argc!=4 ) usage("unsubscribe EMAIL"); db_multi_exec( "DELETE FROM subscriber WHERE semail=%Q", g.argv[3]); }else { usage("pending|reset|send|setting|status|" "subscribers|test-message|unsubscribe"); } } /* ** Do error checking on a submitted subscription form. Return TRUE ** if the submission is valid. Return false if any problems are seen. */ static int subscribe_error_check( int *peErr, /* Type of error */ char **pzErr, /* Error message text */ int needCaptcha /* True if captcha check needed */ ){ const char *zEAddr; int i, j, n; char c; *peErr = 0; *pzErr = 0; /* Verify the captcha first */ if( needCaptcha ){ if( !captcha_is_correct(1) ){ *peErr = 2; *pzErr = mprintf("incorrect security code"); return 0; } } /* Check the validity of the email address. ** ** (1) Exactly one '@' character. ** (2) No other characters besides [a-zA-Z0-9._+-] ** ** The local part is currently more restrictive than RFC 5322 allows: ** https://stackoverflow.com/a/2049510/142454 We will expand this as ** necessary. */ zEAddr = P("e"); if( zEAddr==0 ){ *peErr = 1; *pzErr = mprintf("required"); return 0; } for(i=j=n=0; (c = zEAddr[i])!=0; i++){ if( c=='@' ){ n = i; j++; continue; } if( !fossil_isalnum(c) && c!='.' && c!='_' && c!='-' && c!='+' ){ *peErr = 1; *pzErr = mprintf("illegal character in email address: 0x%x '%c'", c, c); return 0; } } if( j!=1 ){ *peErr = 1; *pzErr = mprintf("email address should contain exactly one '@'"); return 0; } if( n<1 ){ *peErr = 1; *pzErr = mprintf("name missing before '@' in email address"); return 0; } if( n>i-5 ){ *peErr = 1; *pzErr = mprintf("email domain too short"); return 0; } if( authorized_subscription_email(zEAddr)==0 ){ *peErr = 1; *pzErr = mprintf("not an authorized email address"); return 0; } /* Check to make sure the email address is available for reuse */ if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){ *peErr = 1; *pzErr = mprintf("this email address is used by someone else"); return 0; } /* If we reach this point, all is well */ return 1; } /* ** Text of email message sent in order to confirm a subscription. */ static const char zConfirmMsg[] = @ Someone has signed you up for email alerts on the Fossil repository @ at %s. @ @ To confirm your subscription and begin receiving alerts, click on @ the following hyperlink: @ @ %s/alerts/%s @ @ Save the hyperlink above! You can reuse this same hyperlink to @ unsubscribe or to change the kinds of alerts you receive. @ @ If you do not want to subscribe, you can simply ignore this message. @ You will not be contacted again. @ ; /* ** Append the text of an email confirmation message to the given ** Blob. The security code is in zCode. */ void alert_append_confirmation_message(Blob *pMsg, const char *zCode){ blob_appendf(pMsg, zConfirmMsg/*works-like:"%s%s%s"*/, g.zBaseURL, g.zBaseURL, zCode); } /* ** WEBPAGE: subscribe ** ** Allow users to subscribe to email notifications. ** ** This page is usually run by users who are not logged in. ** A logged-in user can add email notifications on the /alerts page. ** Access to this page by a logged in user (other than an ** administrator) results in a redirect to the /alerts page. ** ** Administrators can visit this page in order to sign up other ** users. ** ** The Alerts permission ("7") is required to access this ** page. To allow anonymous passers-by to sign up for email ** notification, set Email-Alerts on user "nobody" or "anonymous". */ void subscribe_page(void){ int needCaptcha; unsigned int uSeed = 0; const char *zDecoded; char *zCaptcha = 0; char *zErr = 0; int eErr = 0; int di; if( alert_webpages_disabled() ) return; login_check_credentials(); if( !g.perm.EmailAlert ){ login_needed(g.anon.EmailAlert); return; } if( login_is_individual() && db_exists("SELECT 1 FROM subscriber WHERE suname=%Q",g.zLogin) ){ /* This person is already signed up for email alerts. Jump ** to the screen that lets them edit their alert preferences. ** Except, administrators can create subscriptions for others so ** do not jump for them. */ if( g.perm.Admin ){ /* Admins get a link to admin their own account, but they ** stay on this page so that they can create subscriptions ** for other people. */ style_submenu_element("My Subscription","%R/alerts"); }else{ /* Everybody else jumps to the page to administer their own ** account only. */ cgi_redirectf("%R/alerts"); return; } } if( !g.perm.Admin && !db_get_boolean("anon-subscribe",1) ){ register_page(); return; } style_set_current_feature("alerts"); alert_submenu_common(); needCaptcha = !login_is_individual(); if( P("submit") && cgi_csrf_safe(2) && subscribe_error_check(&eErr,&zErr,needCaptcha) ){ /* A validated request for a new subscription has been received. */ char ssub[20]; const char *zEAddr = P("e"); const char *zCode; /* New subscriber code (in hex) */ int nsub = 0; const char *suname = PT("suname"); if( suname==0 && needCaptcha==0 && !g.perm.Admin ) suname = g.zLogin; if( suname && suname[0]==0 ) suname = 0; if( PB("sa") ) ssub[nsub++] = 'a'; if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n'; if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r'; if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x'; ssub[nsub] = 0; zCode = db_text(0, "INSERT INTO subscriber(semail,suname," " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip,lastContact)" "VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q,now()/86400)" "RETURNING hex(subscriberCode);", /* semail */ zEAddr, /* suname */ suname, /* sverified */ needCaptcha==0, /* sdigest */ PB("di"), /* ssub */ ssub, /* smip */ g.zIpAddr ); if( !needCaptcha ){ /* The new subscription has been added on behalf of a logged-in user. ** No verification is required. Jump immediately to /alerts page. */ if( g.perm.Admin ){ cgi_redirectf("%R/alerts/%.32s", zCode); }else{ cgi_redirectf("%R/alerts"); } return; }else{ /* We need to send a verification email */ Blob hdr, body; AlertSender *pSender = alert_sender_new(0,0); blob_init(&hdr,0,0); blob_init(&body,0,0); blob_appendf(&hdr, "To: <%s>\n", zEAddr); blob_appendf(&hdr, "Subject: Subscription verification\n"); alert_append_confirmation_message(&body, zCode); alert_send(pSender, &hdr, &body, 0); style_header("Email Alert Verification"); if( pSender->zErr ){ @ <h1>Internal Error</h1> @ <p>The following internal error was encountered while trying @ to send the confirmation email: @ <blockquote><pre> @ %h(pSender->zErr) @ </pre></blockquote> }else{ @ <p>An email has been sent to "%h(zEAddr)". That email contains a @ hyperlink that you must click to activate your @ subscription.</p> } alert_sender_free(pSender); style_finish_page(); } return; } style_header("Signup For Email Alerts"); if( P("submit")==0 ){ /* If this is the first visit to this page (if this HTTP request did not ** come from a prior Submit of the form) then default all of the ** subscription options to "on" */ cgi_set_parameter_nocopy("sa","1",1); if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1); if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1); if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1); if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1); if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1); if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1); } @ <p>To receive email notifications for changes to this @ repository, fill out the form below and press the "Submit" button.</p> form_begin(0, "%R/subscribe"); @ <table class="subscribe"> @ <tr> @ <td class="form_label">Email Address:</td> @ <td><input type="text" name="e" value="%h(PD("e",""))" size="30"></td> @ <tr> if( eErr==1 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> } @ </tr> if( needCaptcha ){ const char *zInit = ""; if( P("captchaseed")!=0 && eErr!=2 ){ uSeed = strtoul(P("captchaseed"),0,10); zInit = P("captcha"); }else{ uSeed = captcha_seed(); } zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); @ <tr> @ <td class="form_label">Security Code:</td> @ <td><input type="text" name="captcha" value="%h(zInit)" size="30"> captcha_speakit_button(uSeed, "Speak the code"); @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> @ </tr> if( eErr==2 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> } @ </tr> } if( g.perm.Admin ){ @ <tr> @ <td class="form_label">User:</td> @ <td><input type="text" name="suname" value="%h(PD("suname",g.zLogin))" \ @ size="30"></td> @ </tr> if( eErr==3 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> } @ </tr> } @ <tr> @ <td class="form_label">Topics:</td> @ <td><label><input type="checkbox" name="sa" %s(PCK("sa"))> \ @ Announcements</label><br> if( g.perm.Read ){ @ <label><input type="checkbox" name="sc" %s(PCK("sc"))> \ @ Check-ins</label><br> } if( g.perm.RdForum ){ @ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \ @ All Forum Posts</label><br> @ <label><input type="checkbox" name="sn" %s(PCK("sn"))> \ @ New Forum Threads</label><br> @ <label><input type="checkbox" name="sr" %s(PCK("sr"))> \ @ Replies To My Forum Posts</label><br> @ <label><input type="checkbox" name="sx" %s(PCK("sx"))> \ @ Edits To Forum Posts</label><br> } if( g.perm.RdTkt ){ @ <label><input type="checkbox" name="st" %s(PCK("st"))> \ @ Ticket changes</label><br> } if( g.perm.RdWiki ){ @ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \ @ Wiki</label><br> } di = PB("di"); @ </td></tr> @ <tr> @ <td class="form_label">Delivery:</td> @ <td><select size="1" name="di"> @ <option value="0" %s(di?"":"selected")>Individual Emails</option> @ <option value="1" %s(di?"selected":"")>Daily Digest</option> @ </select></td> @ </tr> if( g.perm.Admin ){ @ <tr> @ <td class="form_label">Admin Options:</td><td> @ <label><input type="checkbox" name="vi" %s(PCK("vi"))> \ @ Verified</label><br> @ <label><input type="checkbox" name="dnc" %s(PCK("dnc"))> \ @ Do not call</label></td></tr> } @ <tr> @ <td></td> if( needCaptcha && !alert_enabled() ){ @ <td><input type="submit" name="submit" value="Submit" disabled> @ (Email current disabled)</td> }else{ @ <td><input type="submit" name="submit" value="Submit"></td> } @ </tr> @ </table> if( needCaptcha ){ @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> @ %h(zCaptcha) @ </pre> @ Enter the 8 characters above in the "Security Code" box<br/> @ </td></tr></table></div> } @ </form> fossil_free(zErr); style_finish_page(); } /* ** Either shutdown or completely delete a subscription entry given ** by the hex value zName. Then paint a webpage that explains that ** the entry has been removed. */ static void alert_unsubscribe(int sid, int bTotal){ const char *zEmail = 0; const char *zLogin = 0; int uid = 0; Stmt q; db_prepare(&q, "SELECT semail, suname FROM subscriber" " WHERE subscriberId=%d", sid); if( db_step(&q)==SQLITE_ROW ){ zEmail = db_column_text(&q, 0); zLogin = db_column_text(&q, 1); uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin); } style_set_current_feature("alerts"); if( zEmail==0 ){ style_header("Unsubscribe Fail"); @ <p>Unable to locate a subscriber with the requested key</p> }else{ db_unprotect(PROTECT_READONLY); if( bTotal ){ /* Completely delete the subscriber */ db_multi_exec( "DELETE FROM subscriber WHERE subscriberId=%d", sid ); }else{ /* Keep the subscriber, but turn off all notifications */ db_multi_exec( "UPDATE subscriber SET ssub='k', mtime=now() WHERE subscriberId=%d", sid ); } db_protect_pop(); style_header("Unsubscribed"); @ <p>The "%h(zEmail)" email address has been unsubscribed from all @ notifications. All subscription records for "%h(zEmail)" have @ been purged. No further emails will be sent to "%h(zEmail)".</p> if( uid && g.perm.Admin ){ @ <p>You may also want to @ <a href="%R/setup_uedit?id=%d(uid)">edit or delete @ the corresponding user "%h(zLogin)"</a></p> } } db_finalize(&q); style_finish_page(); return; } /* ** WEBPAGE: alerts ** ** Edit email alert and notification settings. ** ** The subscriber is identified in several ways: ** ** * The name= query parameter contains the complete subscriberCode. ** This only happens when the user receives a verification ** email and clicks on the link in the email. When a ** compilete subscriberCode is seen on the name= query parameter, ** that constitutes verification of the email address. ** ** * The sid= query parameter contains an integer subscriberId. ** This only works for the administrator. It allows the ** administrator to edit any subscription. ** ** * The user is logged into an account other than "nobody" or ** "anonymous". In that case the notification settings ** associated with that account can be edited without needing ** to know the subscriber code. ** ** * The name= query parameter contains a 32-digit prefix of ** subscriber code. (Subscriber codes are normally 64 hex digits ** in length.) This uniquely identifies the subscriber without ** revealing the complete subscriber code, and hence without ** verifying the email address. */ void alert_page(void){ const char *zName = 0; /* Value of the name= query parameter */ Stmt q; /* For querying the database */ int sa, sc, sf, st, sw, sx; /* Types of notifications requested */ int sn, sr; int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */ int isLogin; /* True if logged in as an individual */ const char *ssub = 0; /* Subscription flags */ const char *semail = 0; /* Email address */ const char *smip; /* */ const char *suname = 0; /* Corresponding user.login value */ const char *mtime; /* */ const char *sctime; /* Time subscription created */ int eErr = 0; /* Type of error */ char *zErr = 0; /* Error message text */ int sid = 0; /* Subscriber ID */ int nName; /* Length of zName in bytes */ char *zHalfCode; /* prefix of subscriberCode */ int keepAlive = 0; /* True to update the last contact time */ db_begin_transaction(); if( alert_webpages_disabled() ){ db_commit_transaction(); return; } login_check_credentials(); isLogin = login_is_individual(); zName = P("name"); nName = zName ? (int)strlen(zName) : 0; if( g.perm.Admin && P("sid")!=0 ){ sid = atoi(P("sid")); } if( sid==0 && nName>=32 ){ sid = db_int(0, "SELECT CASE WHEN hex(subscriberCode) LIKE (%Q||'%%')" " THEN subscriberId ELSE 0 END" " FROM subscriber WHERE subscriberCode>=hextoblob(%Q)" " LIMIT 1", zName, zName); if( sid ) keepAlive = 1; } if( sid==0 && isLogin && g.perm.EmailAlert ){ sid = db_int(0, "SELECT subscriberId FROM subscriber" " WHERE suname=%Q", g.zLogin); } if( sid==0 ){ db_commit_transaction(); cgi_redirect("subscribe"); /*NOTREACHED*/ } alert_submenu_common(); if( P("submit")!=0 && cgi_csrf_safe(2) ){ char newSsub[10]; int nsub = 0; Blob update; sdonotcall = PB("sdonotcall"); sdigest = PB("sdigest"); semail = P("semail"); if( PB("sa") ) newSsub[nsub++] = 'a'; if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c'; if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f'; if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n'; if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r'; if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't'; if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w'; if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x'; newSsub[nsub] = 0; ssub = newSsub; blob_init(&update, "UPDATE subscriber SET", -1); blob_append_sql(&update, " sdonotcall=%d," " sdigest=%d," " ssub=%Q," " mtime=now()," " lastContact=now()/86400," " smip=%Q", sdonotcall, sdigest, ssub, g.zIpAddr ); if( g.perm.Admin ){ suname = PT("suname"); sverified = PB("sverified"); if( suname && suname[0]==0 ) suname = 0; blob_append_sql(&update, ", suname=%Q," " sverified=%d", suname, sverified ); } if( isLogin ){ if( semail==0 || email_address_is_valid(semail,0)==0 ){ eErr = 8; } blob_append_sql(&update, ", semail=%Q", semail); } blob_append_sql(&update," WHERE subscriberId=%d", sid); if( eErr==0 ){ db_exec_sql(blob_str(&update)); ssub = 0; } blob_reset(&update); }else if( keepAlive ){ db_unprotect(PROTECT_READONLY); db_multi_exec( "UPDATE subscriber SET lastContact=now()/86400" " WHERE subscriberId=%d", sid ); db_protect_pop(); } if( P("delete")!=0 && cgi_csrf_safe(2) ){ if( !PB("dodelete") ){ eErr = 9; zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" " unsubscribe"); }else{ alert_unsubscribe(sid, 1); db_commit_transaction(); return; } } style_set_current_feature("alerts"); style_header("Update Subscription"); db_prepare(&q, "SELECT" " semail," /* 0 */ " sverified," /* 1 */ " sdonotcall," /* 2 */ " sdigest," /* 3 */ " ssub," /* 4 */ " smip," /* 5 */ " suname," /* 6 */ " datetime(mtime,'unixepoch')," /* 7 */ " datetime(sctime,'unixepoch')," /* 8 */ " hex(subscriberCode)," /* 9 */ " date(coalesce(lastContact*86400,mtime),'unixepoch')," /* 10 */ " now()/86400 - coalesce(lastContact,mtime/86400)" /* 11 */ " FROM subscriber WHERE subscriberId=%d", sid); if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); db_commit_transaction(); cgi_redirect("subscribe"); /*NOTREACHED*/ } if( ssub==0 ){ semail = db_column_text(&q, 0); sdonotcall = db_column_int(&q, 2); sdigest = db_column_int(&q, 3); ssub = db_column_text(&q, 4); } if( suname==0 ){ suname = db_column_text(&q, 6); sverified = db_column_int(&q, 1); } sa = strchr(ssub,'a')!=0; sc = strchr(ssub,'c')!=0; sf = strchr(ssub,'f')!=0; sn = strchr(ssub,'n')!=0; sr = strchr(ssub,'r')!=0; st = strchr(ssub,'t')!=0; sw = strchr(ssub,'w')!=0; sx = strchr(ssub,'x')!=0; smip = db_column_text(&q, 5); mtime = db_column_text(&q, 7); sctime = db_column_text(&q, 8); if( !g.perm.Admin && !sverified ){ if( nName==64 ){ db_unprotect(PROTECT_READONLY); db_multi_exec( "UPDATE subscriber SET sverified=1" " WHERE subscriberCode=hextoblob(%Q)", zName); db_protect_pop(); if( db_get_boolean("selfreg-verify",0) ){ char *zNewCap = db_get("default-perms","u"); db_unprotect(PROTECT_USER); db_multi_exec( "UPDATE user" " SET cap=%Q" " WHERE cap='7' AND login=(" " SELECT suname FROM subscriber" " WHERE subscriberCode=hextoblob(%Q))", zNewCap, zName ); db_protect_pop(); login_set_capabilities(zNewCap, 0); } @ <h1>Your email alert subscription has been verified!</h1> @ <p>Use the form below to update your subscription information.</p> @ <p>Hint: Bookmark this page so that you can more easily update @ your subscription information in the future</p> }else{ @ <h2>Your email address is unverified</h2> @ <p>You should have received an email message containing a link @ that you must visit to verify your account. No email notifications @ will be sent until your email address has been verified.</p> } }else{ @ <p>Make changes to the email subscription shown below and @ press "Submit".</p> } form_begin(0, "%R/alerts"); zHalfCode = db_text("x","SELECT hex(substr(subscriberCode,1,16))" " FROM subscriber WHERE subscriberId=%d", sid); @ <input type="hidden" name="name" value="%h(zHalfCode)"> @ <table class="subscribe"> @ <tr> @ <td class="form_label">Email Address:</td> if( isLogin ){ @ <td><input type="text" name="semail" value="%h(semail)" size="30">\ if( eErr==8 ){ @ <span class='loginError'>← not a valid email address!</span> }else if( g.perm.Admin ){ @ <a href="%R/announce?to=%t(semail)">\ @ (Send a message to %h(semail))</a>\ } @ </td> }else{ @ <td>%h(semail)</td> } @ </tr> if( g.perm.Admin ){ int uid; @ <tr> @ <td class='form_label'>Created:</td> @ <td>%h(sctime)</td> @ </tr> @ <tr> @ <td class='form_label'>Last Modified:</td> @ <td>%h(mtime)</td> @ </tr> @ <tr> @ <td class='form_label'>IP Address:</td> @ <td>%h(smip)</td> @ </tr> @ <tr> @ <td class='form_label'>Subscriber Code:</td> @ <td>%h(db_column_text(&q,9))</td> @ <tr> @ <tr> @ <td class='form_label'>Last Contact:</td> @ <td>%h(db_column_text(&q,10)) ← \ @ %,d(db_column_int(&q,11)) days ago</td> @ </tr> @ <td class="form_label">User:</td> @ <td><input type="text" name="suname" value="%h(suname?suname:"")" \ @ size="30">\ uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", suname); if( uid ){ @ <a href='%R/setup_uedit?id=%d(uid)'>\ @ (login info for %h(suname))</a>\ } @ </tr> } @ <tr> @ <td class="form_label">Topics:</td> @ <td><label><input type="checkbox" name="sa" %s(sa?"checked":"")>\ @ Announcements</label><br> if( g.perm.Read ){ @ <label><input type="checkbox" name="sc" %s(sc?"checked":"")>\ @ Check-ins</label><br> } if( g.perm.RdForum ){ @ <label><input type="checkbox" name="sf" %s(sf?"checked":"")>\ @ All Forum Posts</label><br> @ <label><input type="checkbox" name="sn" %s(sn?"checked":"")>\ @ New Forum Threads</label><br> @ <label><input type="checkbox" name="sr" %s(sr?"checked":"")>\ @ Replies To My Posts</label><br> @ <label><input type="checkbox" name="sx" %s(sx?"checked":"")>\ @ Edits To Forum Posts</label><br> } if( g.perm.RdTkt ){ @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\ @ Ticket changes</label><br> } if( g.perm.RdWiki ){ @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\ @ Wiki</label> } @ </td></tr> if( strchr(ssub,'k')!=0 ){ @ <tr><td></td><td> ↑ @ Note: User did a one-click unsubscribe</td></tr> } @ <tr> @ <td class="form_label">Delivery:</td> @ <td><select size="1" name="sdigest"> @ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option> @ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option> @ </select></td> @ </tr> if( g.perm.Admin ){ @ <tr> @ <td class="form_label">Admin Options:</td><td> @ <label><input type="checkbox" name="sdonotcall" \ @ %s(sdonotcall?"checked":"")> Do not disturb</label><br> @ <label><input type="checkbox" name="sverified" \ @ %s(sverified?"checked":"")>\ @ Verified</label></td></tr> } if( eErr==9 ){ @ <tr> @ <td class="form_label">Verify:</td><td> @ <label><input type="checkbox" name="dodelete"> @ Unsubscribe</label> @ <span class="loginError">← %h(zErr)</span> @ </td></tr> } @ <tr> @ <td></td> @ <td><input type="submit" name="submit" value="Submit"> @ <input type="submit" name="delete" value="Unsubscribe"> @ </tr> @ </table> @ </form> fossil_free(zErr); db_finalize(&q); style_finish_page(); db_commit_transaction(); return; } /* ** WEBPAGE: renew ** ** Users visit this page to update the last-contact date on their ** subscription. The last-contact date is the day that the subscriber ** last interacted with the repository. If the name= query parameter ** (or POST parameter) contains a valid subscriber code, then the last-contact ** subscription associated with that subscriber code is updated to be the ** current date. */ void renewal_page(void){ const char *zName = P("name"); int iInterval = db_get_int("email-renew-interval", 0); Stmt s; int rc; style_header("Subscription Renewal"); if( zName==0 || strlen(zName)<4 ){ @ <p>No subscription specified</p> style_finish_page(); return; } if( !db_table_has_column("repository","subscriber","lastContact") || iInterval<1 ){ @ <p>This repository does not expire email notification subscriptions. @ No renewals are necessary.</p> style_finish_page(); return; } db_unprotect(PROTECT_READONLY); db_prepare(&s, "UPDATE subscriber" " SET lastContact=now()/86400" " WHERE subscriberCode=hextoblob(%Q)" " RETURNING semail, date('now','+%d days');", zName, iInterval+1 ); rc = db_step(&s); if( rc==SQLITE_ROW ){ @ <p>The email notification subscription for %h(db_column_text(&s,0)) @ has been extended until %h(db_column_text(&s,1)) UTC. }else{ @ <p>No such subscriber-id: %h(zName)</p> } db_finalize(&s); db_protect_pop(); style_finish_page(); } /* This is the message that gets sent to describe how to change ** or modify a subscription */ static const char zUnsubMsg[] = @ To changes your subscription settings at %s visit this link: @ @ %s/alerts/%s @ @ To completely unsubscribe from %s, visit the following link: @ @ %s/unsubscribe/%s ; /* ** WEBPAGE: unsubscribe ** WEBPAGE: oneclickunsub ** ** Users visit this page to be delisted from email alerts. ** ** If a valid subscriber code is supplied in the name= query parameter, ** then that subscriber is delisted. ** ** Otherwise, If the users is logged in, then they are redirected ** to the /alerts page where they have an unsubscribe button. ** ** Non-logged-in users with no name= query parameter are invited to enter ** an email address to which will be sent the unsubscribe link that ** contains the correct subscriber code. ** ** The /unsubscribe page requires comfirmation. The /oneclickunsub ** page unsubscribes immediately without any need to confirm. */ void unsubscribe_page(void){ const char *zName = P("name"); char *zErr = 0; int eErr = 0; unsigned int uSeed = 0; const char *zDecoded; char *zCaptcha = 0; int dx; int bSubmit; const char *zEAddr; char *zCode = 0; int sid = 0; if( zName==0 ) zName = P("scode"); /* If a valid subscriber code is supplied, then either present the user ** with a confirmation, or if already confirmed, unsubscribe immediately. */ if( zName && (sid = db_int(0, "SELECT subscriberId FROM subscriber" " WHERE subscriberCode=hextoblob(%Q)", zName))!=0 ){ char *zUnsubName = mprintf("confirm%04x", sid); if( P(zUnsubName)!=0 ){ alert_unsubscribe(sid, 1); }else if( sqlite3_strglob("*oneclick*",g.zPath)==0 ){ alert_unsubscribe(sid, 0); }else if( P("manage")!=0 ){ cgi_redirectf("%R/alerts/%s", zName); }else{ style_header("Unsubscribe"); form_begin(0, "%R/unsubscribe"); @ <input type="hidden" name="scode" value="%h(zName)"> @ <table border="0" cellpadding="10" width="100%%"> @ <tr><td align="right"> @ <input type="submit" name="%h(zUnsubName)" value="Unsubscribe"> @ </td><td><big><b>←</b></big></td> @ <td>Cancel your subscription to %h(g.zBaseURL) notifications @ </td><tr> @ <tr><td align="right"> @ <input type="submit" name="manage" \ @ value="Manage Subscription Settings"> @ </td><td><big><b>←</b></big></td> @ <td>Make other changes to your subscription preferences @ </td><tr> @ </table> @ </form> style_finish_page(); } return; } /* Logged in users are redirected to the /alerts page */ login_check_credentials(); if( login_is_individual() ){ cgi_redirectf("%R/alerts"); return; } style_set_current_feature("alerts"); zEAddr = PD("e",""); dx = atoi(PD("dx","0")); bSubmit = P("submit")!=0 && P("e")!=0 && cgi_csrf_safe(2); if( bSubmit ){ if( !captcha_is_correct(1) ){ eErr = 2; zErr = mprintf("enter the security code shown below"); bSubmit = 0; } } if( bSubmit ){ zCode = db_text(0,"SELECT hex(subscriberCode) FROM subscriber" " WHERE semail=%Q", zEAddr); if( zCode==0 ){ eErr = 1; zErr = mprintf("not a valid email address"); bSubmit = 0; } } if( bSubmit ){ /* If we get this far, it means that a valid unsubscribe request has ** been submitted. Send the appropriate email. */ Blob hdr, body; AlertSender *pSender = alert_sender_new(0,0); blob_init(&hdr,0,0); blob_init(&body,0,0); blob_appendf(&hdr, "To: <%s>\r\n", zEAddr); blob_appendf(&hdr, "Subject: Unsubscribe Instructions\r\n"); blob_appendf(&body, zUnsubMsg/*works-like:"%s%s%s%s%s%s"*/, g.zBaseURL, g.zBaseURL, zCode, g.zBaseURL, g.zBaseURL, zCode); alert_send(pSender, &hdr, &body, 0); style_header("Unsubscribe Instructions Sent"); if( pSender->zErr ){ @ <h1>Internal Error</h1> @ <p>The following error was encountered while trying to send an @ email to %h(zEAddr): @ <blockquote><pre> @ %h(pSender->zErr) @ </pre></blockquote> }else{ @ <p>An email has been sent to "%h(zEAddr)" that explains how to @ unsubscribe and/or modify your subscription settings</p> } alert_sender_free(pSender); style_finish_page(); return; } /* Non-logged-in users have to enter an email address to which is ** sent a message containing the unsubscribe link. */ style_header("Unsubscribe Request"); @ <p>Fill out the form below to request an email message that will @ explain how to unsubscribe and/or change your subscription settings.</p> @ form_begin(0, "%R/unsubscribe"); @ <table class="subscribe"> @ <tr> @ <td class="form_label">Email Address:</td> @ <td><input type="text" name="e" value="%h(zEAddr)" size="30"></td> if( eErr==1 ){ @ <td><span class="loginError">← %h(zErr)</span></td> } @ </tr> uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); @ <tr> @ <td class="form_label">Security Code:</td> @ <td><input type="text" name="captcha" value="" size="30"> captcha_speakit_button(uSeed, "Speak the code"); @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> if( eErr==2 ){ @ <td><span class="loginError">← %h(zErr)</span></td> } @ </tr> @ <tr> @ <td class="form_label">Options:</td> @ <td><label><input type="radio" name="dx" value="0" %s(dx?"":"checked")>\ @ Modify subscription</label><br> @ <label><input type="radio" name="dx" value="1" %s(dx?"checked":"")>\ @ Completely unsubscribe</label><br> @ <tr> @ <td></td> @ <td><input type="submit" name="submit" value="Submit"></td> @ </tr> @ </table> @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> @ %h(zCaptcha) @ </pre> @ Enter the 8 characters above in the "Security Code" box<br/> @ </td></tr></table></div> @ </form> fossil_free(zErr); style_finish_page(); } /* ** WEBPAGE: subscribers ** ** This page, accessible to administrators only, ** shows a list of subscriber email addresses. ** Clicking on an email takes one to the /alerts page ** for that email where the delivery settings can be ** modified. */ void subscriber_list_page(void){ Blob sql; Stmt q; sqlite3_int64 iNow; int nTotal; int nPending; int nDel = 0; int iCutoff = db_get_int("email-renew-cutoff",0); int iWarning = db_get_int("email-renew-warning",0); char zCutoffClr[8]; char zWarnClr[8]; if( alert_webpages_disabled() ) return; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } alert_submenu_common(); style_submenu_element("Users","setup_ulist"); style_set_current_feature("alerts"); style_header("Subscriber List"); nTotal = db_int(0, "SELECT count(*) FROM subscriber"); nPending = db_int(0, "SELECT count(*) FROM subscriber WHERE NOT sverified"); if( nPending>0 && P("purge") && cgi_csrf_safe(0) ){ int nNewPending; db_multi_exec( "DELETE FROM subscriber" " WHERE NOT sverified AND mtime<now()-86400" ); nNewPending = db_int(0, "SELECT count(*) FROM subscriber" " WHERE NOT sverified"); nDel = nPending - nNewPending; nPending = nNewPending; nTotal -= nDel; } if( nPending>0 ){ @ <h1>%,d(nTotal) Subscribers, %,d(nPending) Pending</h1> if( nDel==0 && 0<db_int(0,"SELECT count(*) FROM subscriber" " WHERE NOT sverified AND mtime<now()-86400") ){ style_submenu_element("Purge Pending","subscribers?purge"); } }else{ @ <h1>%,d(nTotal) Subscribers</h1> } if( nDel>0 ){ @ <p>*** %d(nDel) pending subscriptions deleted ***</p> } blob_init(&sql, 0, 0); blob_append_sql(&sql, "SELECT subscriberId," /* 0 */ " semail," /* 1 */ " ssub," /* 2 */ " suname," /* 3 */ " sverified," /* 4 */ " sdigest," /* 5 */ " mtime," /* 6 */ " date(sctime,'unixepoch')," /* 7 */ " (SELECT uid FROM user WHERE login=subscriber.suname)," /* 8 */ " coalesce(lastContact,mtime/86400)" /* 9 */ " FROM subscriber" ); if( P("only")!=0 ){ blob_append_sql(&sql, " WHERE ssub LIKE '%%%q%%'", P("only")); style_submenu_element("Show All","%R/subscribers"); } blob_append_sql(&sql," ORDER BY mtime DESC"); db_prepare_blob(&q, &sql); iNow = time(0); memcpy(zCutoffClr, hash_color("A"), sizeof(zCutoffClr)); memcpy(zWarnClr, hash_color("HIJ"), sizeof(zWarnClr)); @ <table border='1' class='sortable' \ @ data-init-sort='6' data-column-types='tttttKKt'> @ <thead> @ <tr> @ <th>Email @ <th>Events @ <th>Digest-Only? @ <th>User @ <th>Verified? @ <th>Last change @ <th>Last contact @ <th>Created @ </tr> @ </thead><tbody> while( db_step(&q)==SQLITE_ROW ){ sqlite3_int64 iMtime = db_column_int64(&q, 6); double rAge = (iNow - iMtime)/86400.0; int uid = db_column_int(&q, 8); const char *zUname = db_column_text(&q, 3); sqlite3_int64 iContact = db_column_int64(&q, 9); double rContact = (iNow/86400.0) - iContact; @ <tr> @ <td><a href='%R/alerts?sid=%d(db_column_int(&q,0))'>\ @ %h(db_column_text(&q,1))</a></td> @ <td>%h(db_column_text(&q,2))</td> @ <td>%s(db_column_int(&q,5)?"digest":"")</td> if( uid ){ @ <td><a href='%R/setup_uedit?id=%d(uid)'>%h(zUname)</a> }else{ @ <td>%h(zUname)</td> } @ <td>%s(db_column_int(&q,4)?"yes":"pending")</td> @ <td data-sortkey='%010llx(iMtime)'>%z(human_readable_age(rAge))</td> @ <td data-sortkey='%010llx(iContact)'>\ if( iContact>iWarning ){ @ <span>\ }else if( iContact>iCutoff ){ @ <span style='background-color:%s(zWarnClr);'>\ }else{ @ <span style='background-color:%s(zCutoffClr);'>\ } @ %z(human_readable_age(rContact))</td> @ <td>%h(db_column_text(&q,7))</td> @ </tr> } @ </tbody></table> db_finalize(&q); style_table_sorter(); style_finish_page(); } #if LOCAL_INTERFACE /* ** A single event that might appear in an alert is recorded as an ** instance of the following object. ** ** type values: ** ** c A new check-in ** f An original forum post ** n New forum threads ** r Replies to my forum posts ** x An edit to a prior forum post ** t A new ticket or a change to an existing ticket ** w A change to a wiki page ** x Edits to forum posts */ struct EmailEvent { int type; /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */ int needMod; /* Pending moderator approval */ Blob hdr; /* Header content, for forum entries */ Blob txt; /* Text description to appear in an alert */ char *zFromName; /* Human name of the sender */ char *zPriors; /* Upthread sender IDs for forum posts */ EmailEvent *pNext; /* Next in chronological order */ }; #endif /* ** Free a linked list of EmailEvent objects */ void alert_free_eventlist(EmailEvent *p){ while( p ){ EmailEvent *pNext = p->pNext; blob_reset(&p->txt); blob_reset(&p->hdr); fossil_free(p->zFromName); fossil_free(p->zPriors); fossil_free(p); p = pNext; } } /* ** Compute a string that is appropriate for the EmailEvent.zPriors field ** for a particular forum post. ** ** This string is an encode list of sender names and rids for all ancestors ** of the fpdi post - the post that fpid answer, the post that that parent ** post answers, and so forth back up to the root post. Duplicates sender ** names are omitted. ** ** The EmailEvent.zPriors field is used to screen events for people who ** only want to see replies to their own posts or to specific posts. */ static char *alert_compute_priors(int fpid){ return db_text(0, "WITH priors(rid,who) AS (" " SELECT firt, coalesce(euser,user)" " FROM forumpost LEFT JOIN event ON fpid=objid" " WHERE fpid=%d" " UNION ALL" " SELECT firt, coalesce(euser,user)" " FROM priors, forumpost LEFT JOIN event ON fpid=objid" " WHERE fpid=rid" ")" "SELECT ','||group_concat(DISTINCT 'u'||who)||" "','||group_concat(rid) FROM priors;", fpid ); } /* ** Compute and return a linked list of EmailEvent objects ** corresponding to the current content of the temp.wantalert ** table which should be defined as follows: ** ** CREATE TEMP TABLE wantalert(eventId TEXT, needMod BOOLEAN); */ EmailEvent *alert_compute_event_text(int *pnEvent, int doDigest){ Stmt q; EmailEvent *p; EmailEvent anchor; EmailEvent *pLast; const char *zUrl = db_get("email-url","http://localhost:8080"); const char *zFrom; const char *zSub; /* First do non-forum post events */ db_prepare(&q, "SELECT" " CASE WHEN event.type='t'" " THEN (SELECT substr(tagname,5) FROM tag" " WHERE tagid=event.tagid AND tagname LIKE 'tkt-%%')" " ELSE blob.uuid END," /* 0 */ " datetime(event.mtime)," /* 1 */ " 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," /* 2 */ " wantalert.eventId," /* 3 */ " wantalert.needMod" /* 4 */ " FROM temp.wantalert, event, blob" " WHERE blob.rid=event.objid" " AND event.objid=substr(wantalert.eventId,2)+0" " AND (%d OR eventId NOT GLOB 'f*')" " ORDER BY event.mtime", doDigest ); memset(&anchor, 0, sizeof(anchor)); pLast = &anchor; *pnEvent = 0; while( db_step(&q)==SQLITE_ROW ){ const char *zType = ""; const char *zComment = db_column_text(&q, 2); p = fossil_malloc_zero( sizeof(EmailEvent) ); pLast->pNext = p; pLast = p; p->type = db_column_text(&q, 3)[0]; p->needMod = db_column_int(&q, 4); p->zFromName = 0; p->pNext = 0; switch( p->type ){ case 'c': zType = "Check-In"; break; /* case 'f': -- forum posts omitted from this loop. See below */ case 't': zType = "Ticket Change"; break; case 'w': { zType = "Wiki Edit"; switch( zComment ? *zComment : 0 ){ case ':': ++zComment; break; case '+': zType = "Wiki Added"; ++zComment; break; case '-': zType = "Wiki Removed"; ++zComment; break; } break; } } blob_init(&p->hdr, 0, 0); blob_init(&p->txt, 0, 0); blob_appendf(&p->txt,"== %s %s ==\n%s\n%s/info/%.20s\n", db_column_text(&q,1), zType, zComment, zUrl, db_column_text(&q,0) ); if( p->needMod ){ blob_appendf(&p->txt, "** Pending moderator approval (%s/modreq) **\n", zUrl ); } (*pnEvent)++; } db_finalize(&q); /* Early-out if forumpost is not a table in this repository */ if( !db_table_exists("repository","forumpost") ){ return anchor.pNext; } /* For digests, the previous loop also handled forumposts already */ if( doDigest ){ return anchor.pNext; } /* If we reach this point, it means that forumposts exist and this ** is a normal email alert. Construct full-text forum post alerts ** using a format that enables them to be sent as separate emails. */ db_prepare(&q, "SELECT" " forumpost.fpid," /* 0: fpid */ " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1: hash */ " datetime(event.mtime)," /* 2: date/time */ " substr(comment,instr(comment,':')+2)," /* 3: comment */ " (WITH thread(fpid,fprev) AS (" " SELECT fpid,fprev FROM forumpost AS tx" " WHERE tx.froot=forumpost.froot)," " basepid(fpid,bpid) AS (" " SELECT fpid, fpid FROM thread WHERE fprev IS NULL" " UNION ALL" " SELECT thread.fpid, basepid.bpid FROM basepid, thread" " WHERE basepid.fpid=thread.fprev)" " SELECT uuid FROM blob, basepid" " WHERE basepid.fpid=forumpost.firt" " AND blob.rid=basepid.bpid)," /* 4: in-reply-to */ " wantalert.needMod," /* 5: moderated */ " coalesce(display_name(info),euser,user)," /* 6: user */ " forumpost.fprev IS NULL" /* 7: is an edit */ " FROM temp.wantalert, event, forumpost" " LEFT JOIN user ON (login=coalesce(euser,user))" " WHERE event.objid=substr(wantalert.eventId,2)+0" " AND eventId GLOB 'f*'" " AND forumpost.fpid=event.objid" " ORDER BY event.mtime" ); zFrom = db_get("email-self",0); zSub = db_get("email-subname",""); while( db_step(&q)==SQLITE_ROW ){ int fpid = db_column_int(&q,0); Manifest *pPost = manifest_get(fpid, CFTYPE_FORUM, 0); const char *zIrt; const char *zUuid; const char *zTitle; const char *z; if( pPost==0 ) continue; p = fossil_malloc( sizeof(EmailEvent) ); pLast->pNext = p; pLast = p; p->type = db_column_int(&q,7) ? 'f' : 'x'; p->needMod = db_column_int(&q, 5); z = db_column_text(&q,6); p->zFromName = z && z[0] ? fossil_strdup(z) : 0; p->zPriors = alert_compute_priors(fpid); p->pNext = 0; blob_init(&p->hdr, 0, 0); zUuid = db_column_text(&q, 1); zTitle = db_column_text(&q, 3); if( p->needMod ){ blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n", zSub, zTitle); }else{ blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle); blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n", zUuid, alert_hostname(zFrom)); zIrt = db_column_text(&q, 4); if( zIrt && zIrt[0] ){ blob_appendf(&p->hdr, "In-Reply-To: <%.32s@%s>\r\n", zIrt, alert_hostname(zFrom)); } } blob_init(&p->txt, 0, 0); if( p->needMod ){ blob_appendf(&p->txt, "** Pending moderator approval (%s/modreq) **\n", zUrl ); } blob_appendf(&p->txt, "Forum post by %s on %s\n", pPost->zUser, db_column_text(&q, 2)); blob_appendf(&p->txt, "%s/forumpost/%S\n\n", zUrl, zUuid); blob_append(&p->txt, pPost->zWiki, -1); manifest_destroy(pPost); (*pnEvent)++; } db_finalize(&q); return anchor.pNext; } /* ** Put a header on an alert email */ void email_header(Blob *pOut){ blob_appendf(pOut, "This is an automated email reporting changes " "on Fossil repository %s (%s/timeline)\n", db_get("email-subname","(unknown)"), db_get("email-url","http://localhost:8080")); } /* ** COMMAND: test-alert ** ** Usage: %fossil test-alert EVENTID ... ** ** Generate the text of an email alert for all of the EVENTIDs ** listed on the command-line. Or if no events are listed on the ** command line, generate text for all events named in the ** pending_alert table. The text of the email alerts appears on ** standard output. ** ** This command is intended for testing and debugging Fossil itself, ** for example when enhancing the email alert system or fixing bugs ** in the email alert system. If you are not making changes to the ** Fossil source code, this command is probably not useful to you. ** ** EVENTIDs are text. The first character is 'c', 'f', 't', or 'w' ** for check-in, forum, ticket, or wiki. The remaining text is a ** integer that references the EVENT.OBJID value for the event. ** Run /timeline?showid to see these OBJID values. ** ** Options: ** --digest Generate digest alert text ** --needmod Assume all events are pending moderator approval */ void test_alert_cmd(void){ Blob out; int nEvent; int needMod; int doDigest; EmailEvent *pEvent, *p; doDigest = find_option("digest",0,0)!=0; needMod = find_option("needmod",0,0)!=0; db_find_and_open_repository(0, 0); verify_all_options(); db_begin_transaction(); alert_schema(0); db_multi_exec("CREATE TEMP TABLE wantalert(eventid TEXT, needMod BOOLEAN)"); if( g.argc==2 ){ db_multi_exec( "INSERT INTO wantalert(eventId,needMod)" " SELECT eventid, %d FROM pending_alert", needMod); }else{ int i; for(i=2; i<g.argc; i++){ db_multi_exec("INSERT INTO wantalert(eventId,needMod) VALUES(%Q,%d)", g.argv[i], needMod); } } blob_init(&out, 0, 0); email_header(&out); pEvent = alert_compute_event_text(&nEvent, doDigest); for(p=pEvent; p; p=p->pNext){ blob_append(&out, "\n", 1); if( blob_size(&p->hdr) ){ blob_append(&out, blob_buffer(&p->hdr), blob_size(&p->hdr)); blob_append(&out, "\n", 1); } blob_append(&out, blob_buffer(&p->txt), blob_size(&p->txt)); } alert_free_eventlist(pEvent); fossil_print("%s", blob_str(&out)); blob_reset(&out); db_end_transaction(0); } /* ** COMMAND: test-add-alerts ** ** Usage: %fossil test-add-alerts [OPTIONS] EVENTID ... ** ** Add one or more events to the pending_alert queue. Use this ** command during testing to force email notifications for specific ** events. ** ** EVENTIDs are text. The first character is 'c', 'f', 't', or 'w' ** for check-in, forum, ticket, or wiki. The remaining text is a ** integer that references the EVENT.OBJID value for the event. ** Run /timeline?showid to see these OBJID values. ** ** Options: ** --backoffice Run alert_backoffice() after all alerts have ** been added. This will cause the alerts to be ** sent out with the SENDALERT_TRACE option. ** --debug Like --backoffice, but add the SENDALERT_STDOUT ** so that emails are printed to standard output ** rather than being sent. ** --digest Process emails using SENDALERT_DIGEST */ void test_add_alert_cmd(void){ int i; int doAuto = find_option("backoffice",0,0)!=0; unsigned mFlags = 0; if( find_option("debug",0,0)!=0 ){ doAuto = 1; mFlags = SENDALERT_STDOUT; } if( find_option("digest",0,0)!=0 ){ mFlags |= SENDALERT_DIGEST; } db_find_and_open_repository(0, 0); verify_all_options(); db_begin_write(); alert_schema(0); for(i=2; i<g.argc; i++){ db_multi_exec("REPLACE INTO pending_alert(eventId) VALUES(%Q)", g.argv[i]); } db_end_transaction(0); if( doAuto ){ alert_backoffice(SENDALERT_TRACE|mFlags); } } /* ** Minimum number of days between renewal messages */ #define ALERT_RENEWAL_MSG_FREQUENCY 7 /* Do renewals at most once/week */ /* ** Construct the header and body for an email message that will alert ** a subscriber that their subscriptions are about to expire. */ static void alert_renewal_msg( Blob *pHdr, /* Write email header here */ Blob *pBody, /* Write email body here */ const char *zCode, /* The subscriber code */ int lastContact, /* Last contact (days since 1970) */ const char *zEAddr, /* Subscriber email address. Send to this. */ const char *zSub, /* Subscription codes */ const char *zRepoName, /* Name of the sending Fossil repostory */ const char *zUrl /* URL for the sending Fossil repostory */ ){ blob_appendf(pHdr,"To: <%s>\r\n", zEAddr); blob_appendf(pHdr,"Subject: %s Subscription to %s expires soon\r\n", zRepoName, zUrl); blob_appendf(pBody, "\nTo renew your subscription, click the following link:\n" "\n %s/renew/%s\n\n", zUrl, zCode ); blob_appendf(pBody, "You are currently receiving email notification for the following events\n" "on the %s Fossil repository at %s:\n\n", zRepoName, zUrl ); if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n"); if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n"); if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n"); if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n"); if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n"); blob_appendf(pBody, "\n" "If you take no action, your subscription will expire and you will be\n" "unsubscribed in about %d days. To make other changes or to unsubscribe\n" "immediately, visit the following webpage:\n\n" " %s/alerts/%s\n\n", ALERT_RENEWAL_MSG_FREQUENCY, zUrl, zCode ); } /* ** If zUser is a sender of one of the ancestors of a forum post ** (if zUser appears in zPriors) then return true. */ static int alert_in_priors(const char *zUser, const char *zPriors){ int n = (int)strlen(zUser); char zBuf[200]; if( n>195 ) return 0; if( zPriors==0 || zPriors[0]==0 ) return 0; zBuf[0] = ','; zBuf[1] = 'u'; memcpy(zBuf+2, zUser, n+1); return strstr(zPriors, zBuf)!=0; } #if INTERFACE /* ** Flags for alert_send_alerts() */ #define SENDALERT_DIGEST 0x0001 /* Send a digest */ #define SENDALERT_PRESERVE 0x0002 /* Do not mark the task as done */ #define SENDALERT_STDOUT 0x0004 /* Print emails instead of sending */ #define SENDALERT_TRACE 0x0008 /* Trace operation for debugging */ #define SENDALERT_RENEWAL 0x0010 /* Send renewal notices */ #endif /* INTERFACE */ /* ** Send alert emails to subscribers. ** ** This procedure is run by either the backoffice, or in response to the ** "fossil alerts send" command. Details of operation are controlled by ** the flags parameter. ** ** Here is a summary of what happens: ** ** (1) Create a TEMP table wantalert(eventId,needMod) and fill it with ** all the events that we want to send alerts about. The needMod ** flags is set if and only if the event is still awaiting ** moderator approval. Events with the needMod flag are only ** shown to users that have moderator privileges. ** ** (2) Call alert_compute_event_text() to compute a list of EmailEvent ** objects that describe all events about which we want to send ** alerts. ** ** (3) Loop over all subscribers. Compose and send one or more email ** messages to each subscriber that describe the events for ** which the subscriber has expressed interest and has ** appropriate privileges. ** ** (4) Update the pending_alerts table to indicate that alerts have been ** sent. ** ** Update 2018-08-09: Do step (3) before step (4). Update the ** pending_alerts table *before* the emails are sent. That way, if ** the process malfunctions or crashes, some notifications may never ** be sent. But that is better than some recurring bug causing ** subscribers to be flooded with repeated notifications every 60 ** seconds! */ int alert_send_alerts(u32 flags){ EmailEvent *pEvents, *p; int nEvent = 0; int nSent = 0; Stmt q; const char *zDigest = "false"; Blob hdr, body; const char *zUrl; const char *zRepoName; const char *zFrom; const char *zDest = (flags & SENDALERT_STDOUT) ? "stdout" : 0; AlertSender *pSender = 0; u32 senderFlags = 0; int iInterval = 0; /* Subscription renewal interval */ if( g.fSqlTrace ) fossil_trace("-- BEGIN alert_send_alerts(%u)\n", flags); alert_schema(0); if( !alert_enabled() && (flags & SENDALERT_STDOUT)==0 ) goto send_alert_done; zUrl = db_get("email-url",0); if( zUrl==0 ) goto send_alert_done; zRepoName = db_get("email-subname",0); if( zRepoName==0 ) goto send_alert_done; zFrom = db_get("email-self",0); if( zFrom==0 ) goto send_alert_done; if( flags & SENDALERT_TRACE ){ senderFlags |= ALERT_TRACE; } pSender = alert_sender_new(zDest, senderFlags); /* Step (1): Compute the alerts that need sending */ db_multi_exec( "DROP TABLE IF EXISTS temp.wantalert;" "CREATE TEMP TABLE wantalert(eventId TEXT, needMod BOOLEAN, sentMod);" ); if( flags & SENDALERT_DIGEST ){ /* Unmoderated changes are never sent as part of a digest */ db_multi_exec( "INSERT INTO wantalert(eventId,needMod)" " SELECT eventid, 0" " FROM pending_alert" " WHERE sentDigest IS FALSE" " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=substr(eventid,2));" ); zDigest = "true"; }else{ /* Immediate alerts might include events that are subject to ** moderator approval */ db_multi_exec( "INSERT INTO wantalert(eventId,needMod,sentMod)" " SELECT eventid," " EXISTS(SELECT 1 FROM private WHERE rid=substr(eventid,2))," " sentMod" " FROM pending_alert" " WHERE sentSep IS FALSE;" "DELETE FROM wantalert WHERE needMod AND sentMod;" ); } if( g.fSqlTrace ){ fossil_trace("-- wantalert contains %d rows\n", db_int(0, "SELECT count(*) FROM wantalert") ); } /* Step 2: compute EmailEvent objects for every notification that ** needs sending. */ pEvents = alert_compute_event_text(&nEvent, (flags & SENDALERT_DIGEST)!=0); if( nEvent==0 ) goto send_alert_expiration_warnings; /* Step 4a: Update the pending_alerts table to designate the ** alerts as having all been sent. This is done *before* step (3) ** so that a crash will not cause alerts to be sent multiple times. ** Better a missed alert than being spammed with hundreds of alerts ** due to a bug. */ if( (flags & SENDALERT_PRESERVE)==0 ){ if( flags & SENDALERT_DIGEST ){ db_multi_exec( "UPDATE pending_alert SET sentDigest=true" " WHERE eventid IN (SELECT eventid FROM wantalert);" ); }else{ db_multi_exec( "UPDATE pending_alert SET sentSep=true" " WHERE eventid IN (SELECT eventid FROM wantalert WHERE NOT needMod);" "UPDATE pending_alert SET sentMod=true" " WHERE eventid IN (SELECT eventid FROM wantalert WHERE needMod);" ); } } /* Step 3: Loop over subscribers. Send alerts */ blob_init(&hdr, 0, 0); blob_init(&body, 0, 0); db_prepare(&q, "SELECT" " hex(subscriberCode)," /* 0 */ " semail," /* 1 */ " ssub," /* 2 */ " fullcap(user.cap)," /* 3 */ " suname" /* 4 */ " FROM subscriber LEFT JOIN user ON (login=suname)" " WHERE sverified" " AND NOT sdonotcall" " AND sdigest IS %s" " AND coalesce(subscriber.lastContact*86400,subscriber.mtime)>=%d", zDigest/*safe-for-%s*/, db_get_int("email-renew-cutoff",0) ); while( db_step(&q)==SQLITE_ROW ){ const char *zCode = db_column_text(&q, 0); const char *zSub = db_column_text(&q, 2); const char *zEmail = db_column_text(&q, 1); const char *zCap = db_column_text(&q, 3); const char *zUser = db_column_text(&q, 4); int nHit = 0; for(p=pEvents; p; p=p->pNext){ if( strchr(zSub,p->type)==0 ){ if( p->type!='f' ) continue; if( strchr(zSub,'n')!=0 && (p->zPriors==0 || p->zPriors[0]==0) ){ /* New post: accepted */ }else if( strchr(zSub,'r')!=0 && zUser!=0 && alert_in_priors(zUser, p->zPriors) ){ /* A follow-up to a post written by the user: accept */ }else{ continue; } } if( p->needMod ){ /* For events that require moderator approval, only send an alert ** if the recipient is a moderator for that type of event. Setup ** and Admin users always get notified. */ char xType = '*'; if( strpbrk(zCap,"as")==0 ){ switch( p->type ){ case 'x': case 'f': case 'n': case 'r': xType = '5'; break; case 't': xType = 'q'; break; case 'w': xType = 'l'; break; } if( strchr(zCap,xType)==0 ) continue; } }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){ /* Setup and admin users can get any notification that does not ** require moderation */ }else{ /* Other users only see the alert if they have sufficient ** privilege to view the event itself */ char xType = '*'; switch( p->type ){ case 'c': xType = 'o'; break; case 'x': case 'f': case 'n': case 'r': xType = '2'; break; case 't': xType = 'r'; break; case 'w': xType = 'j'; break; } if( strchr(zCap,xType)==0 ) continue; } if( blob_size(&p->hdr)>0 ){ /* This alert should be sent as a separate email */ Blob fhdr, fbody; blob_init(&fhdr, 0, 0); blob_appendf(&fhdr, "To: <%s>\r\n", zEmail); blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr)); blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt)); blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n", zUrl, zCode); blob_appendf(&fhdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n"); blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n", zUrl, zCode); /* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n", ** zUrl, zCode); */ alert_send(pSender,&fhdr,&fbody,p->zFromName); nSent++; blob_reset(&fhdr); blob_reset(&fbody); }else{ /* Events other than forum posts are gathered together into ** a single email message */ if( nHit==0 ){ blob_appendf(&hdr,"To: <%s>\r\n", zEmail); blob_appendf(&hdr,"Subject: %s activity alert\r\n", zRepoName); blob_appendf(&body, "This is an automated email sent by the Fossil repository " "at %s to report changes.\n", zUrl ); } nHit++; blob_append(&body, "\n", 1); blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt)); } } if( nHit==0 ) continue; blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n", zUrl, zCode); blob_appendf(&hdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n"); blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", zUrl, zCode); alert_send(pSender,&hdr,&body,0); nSent++; blob_truncate(&hdr, 0); blob_truncate(&body, 0); } blob_reset(&hdr); blob_reset(&body); db_finalize(&q); alert_free_eventlist(pEvents); /* Step 4b: Update the pending_alerts table to remove all of the ** alerts that have been completely sent. */ db_multi_exec("DELETE FROM pending_alert WHERE sentDigest AND sentSep;"); /* Send renewal messages to subscribers whose subscriptions are about ** to expire. Only do this if: ** ** (1) email-renew-interval is 14 or greater (or in other words if ** subscription expiration is enabled). ** ** (2) The SENDALERT_RENEWAL flag is set */ send_alert_expiration_warnings: if( (flags & SENDALERT_RENEWAL)!=0 && (iInterval = db_get_int("email-renew-interval",0))>=14 ){ int iNow = (int)(time(0)/86400); int iOldWarn = db_get_int("email-renew-warning",0); int iNewWarn = iNow - iInterval + ALERT_RENEWAL_MSG_FREQUENCY; if( iNewWarn >= iOldWarn + ALERT_RENEWAL_MSG_FREQUENCY ){ db_prepare(&q, "SELECT" " hex(subscriberCode)," /* 0 */ " lastContact," /* 1 */ " semail," /* 2 */ " ssub" /* 3 */ " FROM subscriber" " WHERE lastContact<=%d AND lastContact>%d" " AND NOT sdonotcall" " AND length(sdigest)>0", iNewWarn, iOldWarn ); while( db_step(&q)==SQLITE_ROW ){ Blob hdr, body; blob_init(&hdr, 0, 0); blob_init(&body, 0, 0); alert_renewal_msg(&hdr, &body, db_column_text(&q,0), db_column_int(&q,1), db_column_text(&q,2), db_column_text(&q,3), zRepoName, zUrl); alert_send(pSender,&hdr,&body,0); blob_reset(&hdr); blob_reset(&body); } db_finalize(&q); if( (flags & SENDALERT_PRESERVE)==0 ){ if( iOldWarn>0 ){ db_set_int("email-renew-cutoff", iOldWarn, 0); } db_set_int("email-renew-warning", iNewWarn, 0); } } } send_alert_done: alert_sender_free(pSender); if( g.fSqlTrace ) fossil_trace("-- END alert_send_alerts(%u)\n", flags); return nSent; } /* ** Do backoffice processing for email notifications. In other words, ** check to see if any email notifications need to occur, and then ** do them. ** ** This routine is intended to run in the background, after webpages. ** ** The mFlags option is zero or more of the SENDALERT_* flags. Normally ** this flag is zero, but the test-set-alert command sets it to ** SENDALERT_TRACE. */ int alert_backoffice(u32 mFlags){ int iJulianDay; int nSent = 0; if( !alert_tables_exist() ) return 0; nSent = alert_send_alerts(mFlags); iJulianDay = db_int(0, "SELECT julianday('now')"); if( iJulianDay>db_get_int("email-last-digest",0) ){ db_set_int("email-last-digest",iJulianDay,0); nSent += alert_send_alerts(SENDALERT_DIGEST|SENDALERT_RENEWAL|mFlags); } return nSent; } /* ** WEBPAGE: contact_admin ** ** A web-form to send an email message to the repository administrator, ** or (with appropriate permissions) to anybody. */ void contact_admin_page(void){ const char *zAdminEmail = db_get("email-admin",0); unsigned int uSeed = 0; const char *zDecoded; char *zCaptcha = 0; login_check_credentials(); style_set_current_feature("alerts"); if( zAdminEmail==0 || zAdminEmail[0]==0 ){ style_header("Outbound Email Disabled"); @ <p>Outbound email is disabled on this repository style_finish_page(); return; } if( P("submit")!=0 && P("subject")!=0 && P("msg")!=0 && P("from")!=0 && cgi_csrf_safe(2) && captcha_is_correct(0) ){ Blob hdr, body; AlertSender *pSender = alert_sender_new(0,0); blob_init(&hdr, 0, 0); blob_appendf(&hdr, "To: <%s>\r\nSubject: %s administrator message\r\n", zAdminEmail, db_get("email-subname","Fossil Repo")); blob_init(&body, 0, 0); blob_appendf(&body, "Message from [%s]\n", PT("from")/*safe-for-%s*/); blob_appendf(&body, "Subject: [%s]\n\n", PT("subject")/*safe-for-%s*/); blob_appendf(&body, "%s", PT("msg")/*safe-for-%s*/); alert_send(pSender, &hdr, &body, 0); style_header("Message Sent"); if( pSender->zErr ){ @ <h1>Internal Error</h1> @ <p>The following error was reported by the system: @ <blockquote><pre> @ %h(pSender->zErr) @ </pre></blockquote> }else{ @ <p>Your message has been sent to the repository administrator. @ Thank you for your input.</p> } alert_sender_free(pSender); style_finish_page(); return; } if( captcha_needed() ){ uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); } style_set_current_feature("alerts"); style_header("Message To Administrator"); form_begin(0, "%R/contact_admin"); @ <p>Enter a message to the repository administrator below:</p> @ <table class="subscribe"> if( zCaptcha ){ @ <tr> @ <td class="form_label">Security Code:</td> @ <td><input type="text" name="captcha" value="" size="10"> captcha_speakit_button(uSeed, "Speak the code"); @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> @ </tr> } @ <tr> @ <td class="form_label">Your Email Address:</td> @ <td><input type="text" name="from" value="%h(PT("from"))" size="30"></td> @ </tr> @ <tr> @ <td class="form_label">Subject:</td> @ <td><input type="text" name="subject" value="%h(PT("subject"))"\ @ size="80"></td> @ </tr> @ <tr> @ <td class="form_label">Message:</td> @ <td><textarea name="msg" cols="80" rows="10" wrap="virtual">\ @ %h(PT("msg"))</textarea> @ </tr> @ <tr> @ <td></td> @ <td><input type="submit" name="submit" value="Send Message"> @ </tr> @ </table> if( zCaptcha ){ @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> @ %h(zCaptcha) @ </pre> @ Enter the 8 characters above in the "Security Code" box<br/> @ </td></tr></table></div> } @ </form> style_finish_page(); } /* ** Send an annoucement message described by query parameter. ** Permission to do this has already been verified. */ static char *alert_send_announcement(void){ AlertSender *pSender; char *zErr; const char *zTo = PT("to"); char *zSubject = PT("subject"); int bAll = PB("all"); int bAA = PB("aa"); int bMods = PB("mods"); const char *zSub = db_get("email-subname", "[Fossil Repo]"); int bTest2 = fossil_strcmp(P("name"),"test2")==0; Blob hdr, body; blob_init(&body, 0, 0); blob_init(&hdr, 0, 0); blob_appendf(&body, "%s", PT("msg")/*safe-for-%s*/); pSender = alert_sender_new(bTest2 ? "blob" : 0, 0); if( zTo[0] ){ blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", zTo, zSub, zSubject); alert_send(pSender, &hdr, &body, 0); } if( bAll || bAA || bMods ){ Stmt q; int nUsed = blob_size(&body); const char *zURL = db_get("email-url",0); if( bAll ){ db_prepare(&q, "SELECT semail, hex(subscriberCode) FROM subscriber " " WHERE sverified AND NOT sdonotcall"); }else if( bAA ){ db_prepare(&q, "SELECT semail, hex(subscriberCode) FROM subscriber " " WHERE sverified AND NOT sdonotcall" " AND ssub LIKE '%%a%%'"); }else if( bMods ){ db_prepare(&q, "SELECT semail, hex(subscriberCode)" " FROM subscriber, user " " WHERE sverified AND NOT sdonotcall" " AND suname=login" " AND fullcap(cap) GLOB '*5*'"); } while( db_step(&q)==SQLITE_ROW ){ const char *zCode = db_column_text(&q, 1); zTo = db_column_text(&q, 0); blob_truncate(&hdr, 0); blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", zTo, zSub, zSubject); if( zURL ){ blob_truncate(&body, nUsed); blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", zURL, zCode); } alert_send(pSender, &hdr, &body, 0); } db_finalize(&q); } if( bTest2 ){ /* If the URL is /announce/test2 instead of just /announce, then no ** email is actually sent. Instead, the text of the email that would ** have been sent is displayed in the result window. */ @ <pre style='border: 2px solid blue; padding: 1ex'> @ %h(blob_str(&pSender->out)) @ </pre> } zErr = pSender->zErr; pSender->zErr = 0; alert_sender_free(pSender); return zErr; } /* ** WEBPAGE: announce ** ** A web-form, available to users with the "Send-Announcement" or "A" ** capability, that allows one to send announcements to whomever ** has subscribed to receive announcements. The administrator can ** also send a message to an arbitrary email address and/or to all ** subscribers regardless of whether or not they have elected to ** receive announcements. */ void announce_page(void){ const char *zAction = "announce" /* Maintenance reminder: we need an explicit action=THIS_PAGE on the ** form element to avoid that a URL arg of to=... passed to this ** page ends up overwriting the form-posted "to" value. This ** action value differs for the test1 request path. */; login_check_credentials(); if( !g.perm.Announce ){ login_needed(0); return; } style_set_current_feature("alerts"); if( fossil_strcmp(P("name"),"test1")==0 ){ /* Visit the /announce/test1 page to see the CGI variables */ zAction = "announce/test1"; @ <p style='border: 1px solid black; padding: 1ex;'> cgi_print_all(0, 0, 0); @ </p> }else if( P("submit")!=0 && cgi_csrf_safe(2) ){ char *zErr = alert_send_announcement(); style_header("Announcement Sent"); if( zErr ){ @ <h1>Internal Error</h1> @ <p>The following error was reported by the system: @ <blockquote><pre> @ %h(zErr) @ </pre></blockquote> }else{ @ <p>The announcement has been sent. @ <a href="%h(PD("REQUEST_URI","/"))">Send another</a></p> } style_finish_page(); return; } else if( !alert_enabled() ){ style_header("Cannot Send Announcement"); @ <p>Either you have no subscribers yet, or email alerts are not yet @ <a href="https://fossil-scm.org/fossil/doc/trunk/www/alerts.md">set up</a> @ for this repository.</p> return; } style_header("Send Announcement"); @ <form method="POST" action="%R/%s(zAction)"> login_insert_csrf_secret(); @ <table class="subscribe"> if( g.perm.Admin ){ int aa = PB("aa"); int all = PB("all"); int aMod = PB("mods"); const char *aack = aa ? "checked" : ""; const char *allck = all ? "checked" : ""; const char *modck = aMod ? "checked" : ""; @ <tr> @ <td class="form_label">To:</td> @ <td><input type="text" name="to" value="%h(PT("to"))" size="30"><br> @ <label><input type="checkbox" name="aa" %s(aack)> \ @ All "announcement" subscribers</label> \ @ <a href="%R/subscribers?only=a" target="_blank">(list)</a><br> @ <label><input type="checkbox" name="all" %s(allck)> \ @ All subscribers</label> \ @ <a href="%R/subscribers" target="_blank">(list)</a><br> @ <label><input type="checkbox" name="mods" %s(modck)> \ @ All moderators</label> \ @ <a href="%R/setup_ulist?with=5" target="_blank">(list)</a><br></td> @ </tr> } @ <tr> @ <td class="form_label">Subject:</td> @ <td><input type="text" name="subject" value="%h(PT("subject"))"\ @ size="80"></td> @ </tr> @ <tr> @ <td class="form_label">Message:</td> @ <td><textarea name="msg" cols="80" rows="10" wrap="virtual">\ @ %h(PT("msg"))</textarea> @ </tr> @ <tr> @ <td></td> if( fossil_strcmp(P("name"),"test2")==0 ){ @ <td><input type="submit" name="submit" value="Dry Run"> }else{ @ <td><input type="submit" name="submit" value="Send Message"> } @ </tr> @ </table> @ </form> style_finish_page(); } |
Added src/alerts/bflat2.wav.
cannot compute difference between binary files
Added src/alerts/bflat3.wav.
cannot compute difference between binary files
Added src/alerts/bloop.wav.
cannot compute difference between binary files
Added src/alerts/mkwav.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 | /* ** This C program was used to generate the "g-minor-triad.wav" file. ** A small modification generated the "b-flat.wav" file. ** ** This code is saved as an historical reference. It is not part ** of Fossil. */ #include <stdio.h> #include <math.h> #include <stdlib.h> /* ** Write a four-byte little-endian integer value to out. */ void write_int4(FILE *out, unsigned int i){ unsigned char z[4]; z[0] = i&0xff; z[1] = (i>>8)&0xff; z[2] = (i>>16)&0xff; z[3] = (i>>24)&0xff; fwrite(z, 4, 1, out); } /* ** Write out the WAV file */ void write_wave( const char *zFilename, /* The file to write */ unsigned int nData, /* Bytes of data */ unsigned char *aData /* 8000 samples/sec, 8 bit samples */ ){ const unsigned char aWavFmt[] = { 0x57, 0x41, 0x56, 0x45, /* "WAVE" */ 0x66, 0x6d, 0x74, 0x20, /* "fmt " */ 0x10, 0x00, 0x00, 0x00, /* 16 bytes in the "fmt " section */ 0x01, 0x00, /* FormatTag: WAVE_FORMAT_PCM */ 0x01, 0x00, /* 1 channel */ 0x40, 0x1f, 0x00, 0x00, /* 8000 samples/second */ 0x40, 0x1f, 0x00, 0x00, /* 8000 bytes/second */ 0x01, 0x00, /* Block alignment */ 0x08, 0x00, /* bits/sample */ 0x64, 0x61, 0x74, 0x61, /* "data" */ }; FILE *out = fopen(zFilename,"wb"); if( out==0 ){ fprintf(stderr, "cannot open \"%s\" for writing\n", zFilename); exit(1); } fwrite("RIFF", 4, 1, out); write_int4(out, nData+4+20+8); fwrite(aWavFmt, sizeof(aWavFmt), 1, out); write_int4(out, nData); fwrite(aData, nData, 1, out); fclose(out); } int main(int argc, char **argv){ int i = 0; unsigned char aBuf[800]; # define N sizeof(aBuf) # define pitch1 195.9977*2 /* G */ # define pitch2 233.0819*2 /* B-flat */ # define pitch3 293.6648*2 /* D */ while( i<N/2 ){ double v; v = 99.0*sin((2*M_PI*pitch3*i)/8000); if( i<200 ){ v = v*i/200.0; }else if( i>N-200 ){ v = v*(N-i)/200.0; } aBuf[i] = (char)(v+99.0); i++; } while( i<N ){ double v; v = 99.0*sin((2*M_PI*pitch1*i)/8000); if( i<200 ){ v = v*i/200.0; }else if( i>N-200 ){ v = v*(N-i)/200.0; } aBuf[i] = (char)(v+99.0); i++; } write_wave("out.wav", N, aBuf); return 0; } |
Added src/alerts/plunk.wav.
cannot compute difference between binary files
Changes to src/allrepo.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file contains code to implement the "all" command-line method. */ #include "config.h" #include "allrepo.h" #include <assert.h> /* | | | | | < < < < > | < < < | < < < > > > > > > | < > > | > > > > > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > | > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > < | > > > | > | > | > > > > > > > | < | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > > | | > | | | > | > > > > > > > | > > > > | < | > > > < | > > > > > > > > > | | > | > > > > > > > > | | > | | < < < | | | < < | < < | < < | | < | | < < < < | 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 | ** This file contains code to implement the "all" command-line method. */ #include "config.h" #include "allrepo.h" #include <assert.h> /* ** Build a string that contains all of the command-line options ** specified as arguments. collect_argument() is used for stand-alone ** options and collect_argument_value() is used for options that are ** followed by an argument value. */ static void collect_argument(Blob *pExtra,const char *zArg,const char *zShort){ const char *z = find_option(zArg, zShort, 0); if( z!=0 ){ blob_appendf(pExtra, " %s", z); } } static void collect_argument_value( Blob *pExtra, const char *zArg, const char *zShort ){ const char *zValue = find_option(zArg, zShort, 1); if( zValue ){ if( zValue[0] ){ blob_appendf(pExtra, " --%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: ** ** backup Backup all repositories. The argument must be the name of ** a directory into which all backup repositories are written. ** ** cache Manages the cache used for potentially expensive web ** pages. Any additional arguments are passed on verbatim ** to the cache command. ** ** changes Shows all local check-outs that have uncommitted changes. ** This operation has no additional options. ** ** clean Delete all "extra" files in all local check-outs. 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 check-outs 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. ** ** config Only the "config pull AREA" command works. ** ** dbstat Run the "dbstat" command on all repositories. ** ** extras Shows "extra" files from all local check-outs. 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. ** ** git CMD Do the "git export" or "git status" command (whichever ** is specified by CMD) on all repositories for which ** a Git mirror has been previously established. ** ** info Run the "info" command on all repositories. ** ** pull Run a "pull" operation on all repositories. Only the ** --verbose and --share-links options are 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 option ** is not supported. ** ** remote Show remote hosts for all repositories. ** ** repack Look for extra compression in all repositories. ** ** sync Run a "sync" on all repositories. Only the --verbose ** and --unversioned and --share-links options are supported. ** ** set Run the "setting" or "set" commands on all repositories. ** This command is useful for settings like "max-loadavg" which ** you usually want to be the same across all repositories ** on a server. ** ** unset Run the "unset" command on all repositories ** ** server Run the "server" commands on all repositories. ** The root URI gives a listing of all repos. ** ** ui Run the "ui" command on all repositories. Like "server" ** but bind to the loopback TCP address only, enable ** the --localauth option and automatically launch a ** web-browser ** ** whatis Run the "whatis" command on all repositories. Only ** show output for repositories that have a match. ** ** ** 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 sometimes 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 check-outs to be ignored instead. ** ** list | ls Display the location of all repositories. The -c|--ckout ** option causes all local check-outs 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: ** --dry-run If given, display instead of run actions ** --showfile Show the repository or check-out being operated upon ** --stop-on-error Halt immediately if any subprocess fails */ void all_cmd(void){ Stmt q; const char *zCmd; char *zSyscmd; Blob extra; int useCheckouts = 0; int quiet = 0; int dryRunFlag = 0; int showFile = find_option("showfile",0,0)!=0; int stopOnError; int nToDel = 0; int showLabel = 0; (void)find_option("dontstop",0,0); /* Legacy. Now the default */ stopOnError = find_option("stop-on-error",0,0)!=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 ..."); } db_open_config(1, 0); blob_zero(&extra); zCmd = g.argv[2]; if( !login_is_nobody() ) blob_appendf(&extra, " -U %s", g.zLogin); if( fossil_strcmp(zCmd, "ui")==0 || fossil_strcmp(zCmd, "server")==0 ){ g.argv[1] = g.argv[2]; g.argv[2] = "/"; cmd_webserver(); return; } if( fossil_strcmp(zCmd, "list")==0 || fossil_strcmp(zCmd,"ls")==0 ){ zCmd = "list"; useCheckouts = find_option("ckout","c",0)!=0; }else if( fossil_strcmp(zCmd, "backup")==0 ){ char *zDest; zCmd = "backup -R"; collect_argument(&extra, "overwrite",0); if( g.argc!=4 ) usage("backup DIRECTORY"); zDest = g.argv[3]; if( file_isdir(zDest, ExtFILE)!=1 ){ fossil_fatal("argument to \"fossil all backup\" must be a directory"); } blob_appendf(&extra, " %$", zDest); }else if( fossil_strcmp(zCmd, "clean")==0 ){ zCmd = "clean --chdir"; collect_argument(&extra, "allckouts",0); collect_argument_value(&extra, "case-sensitive", 0); collect_argument_value(&extra, "clean", 0); collect_argument(&extra, "dirsonly",0); collect_argument(&extra, "disable-undo",0); collect_argument(&extra, "dotfiles",0); collect_argument(&extra, "emptydirs",0); collect_argument(&extra, "force","f"); collect_argument_value(&extra, "ignore", 0); collect_argument_value(&extra, "keep", 0); collect_argument(&extra, "no-prompt",0); collect_argument(&extra, "temp",0); collect_argument(&extra, "verbose","v"); collect_argument(&extra, "whatif",0); useCheckouts = 1; }else if( fossil_strcmp(zCmd, "config")==0 ){ zCmd = "config -R"; collect_argv(&extra, 3); (void)find_option("legacy",0,0); (void)find_option("overwrite",0,0); verify_all_options(); if( g.argc!=5 || fossil_strcmp(g.argv[3],"pull")!=0 ){ usage("configure pull AREA ?OPTIONS?"); } }else if( fossil_strcmp(zCmd, "dbstat")==0 ){ zCmd = "dbstat --omit-version-info -R"; showLabel = 1; quiet = 1; collect_argument(&extra, "brief", "b"); collect_argument(&extra, "db-check", 0); collect_argument(&extra, "db-verify", 0); }else if( fossil_strcmp(zCmd, "extras")==0 ){ if( showFile ){ zCmd = "extras --chdir"; }else{ zCmd = "extras --header --chdir"; } collect_argument(&extra, "abs-paths",0); collect_argument_value(&extra, "case-sensitive", 0); collect_argument(&extra, "dotfiles",0); collect_argument_value(&extra, "ignore", 0); collect_argument(&extra, "rel-paths",0); useCheckouts = 1; stopOnError = 0; quiet = 1; }else if( fossil_strcmp(zCmd, "git")==0 ){ if( g.argc<4 ){ usage("git (export|status)"); }else{ if( fossil_strcmp(g.argv[3], "export")==0 ){ zCmd = "git export --if-mirrored -R"; }else if( fossil_strcmp(g.argv[3], "status")==0 ){ zCmd = "git status --by-all -q -R"; quiet = 1; }else{ usage("git (export|status)"); } } }else if( fossil_strcmp(zCmd, "push")==0 ){ zCmd = "push -autourl -R"; collect_argument(&extra, "verbose","v"); }else if( fossil_strcmp(zCmd, "pull")==0 ){ zCmd = "pull -autourl -R"; collect_argument(&extra, "verbose","v"); collect_argument(&extra, "share-links",0); }else if( fossil_strcmp(zCmd, "rebuild")==0 ){ zCmd = "rebuild"; collect_argument(&extra, "analyze",0); collect_argument(&extra, "cluster",0); collect_argument(&extra, "compress",0); collect_argument(&extra, "compress-only",0); collect_argument(&extra, "noverify",0); collect_argument_value(&extra, "pagesize", 0); collect_argument(&extra, "vacuum",0); collect_argument(&extra, "deanalyze",0); /* Deprecated */ 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( fossil_strcmp(zCmd, "remote")==0 ){ showLabel = 1; quiet = 1; collect_argument(&extra, "show-passwords", 0); if( g.argc==3 ){ zCmd = "remote -R"; }else if( g.argc!=4 ){ usage("remote ?config-data|list|ls?"); }else if( fossil_strcmp(g.argv[3],"ls")==0 || fossil_strcmp(g.argv[3],"list")==0 ){ zCmd = "remote ls -R"; }else if( fossil_strcmp(g.argv[3],"ls")==0 || fossil_strcmp(g.argv[3],"list")==0 ){ zCmd = "remote ls -R"; }else if( fossil_strcmp(g.argv[3],"config-data")==0 ){ zCmd = "remote config-data -R"; }else{ usage("remote ?config-data|list|ls?"); } }else if( fossil_strcmp(zCmd, "repack")==0 ){ zCmd = "repack"; }else if( fossil_strcmp(zCmd, "setting")==0 ){ zCmd = "setting -R"; collect_argv(&extra, 3); }else if( fossil_strcmp(zCmd, "unset")==0 ){ zCmd = "unset -R"; collect_argv(&extra, 3); }else if( fossil_strcmp(zCmd, "fts-config")==0 ){ zCmd = "fts-config -R"; collect_argv(&extra, 3); }else if( fossil_strcmp(zCmd, "sync")==0 ){ zCmd = "sync -autourl -R"; collect_argument(&extra, "share-links",0); collect_argument(&extra, "verbose","v"); collect_argument(&extra, "unversioned","u"); collect_argument(&extra, "all",0); }else if( fossil_strcmp(zCmd, "test-integrity")==0 ){ collect_argument(&extra, "db-only", "d"); collect_argument(&extra, "parse", 0); collect_argument(&extra, "quick", "q"); zCmd = "test-integrity"; }else if( fossil_strcmp(zCmd, "test-orphans")==0 ){ zCmd = "test-orphans -R"; }else if( fossil_strcmp(zCmd, "test-missing")==0 ){ zCmd = "test-missing -q -R"; collect_argument(&extra, "notshunned",0); }else if( fossil_strcmp(zCmd, "changes")==0 ){ zCmd = "changes --quiet --header --chdir"; useCheckouts = 1; stopOnError = 0; quiet = 1; }else if( fossil_strcmp(zCmd, "ignore")==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, useCheckouts?1: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_unprotect(PROTECT_CONFIG); db_multi_exec("%s", blob_sql_text(&sql)); db_protect_pop(); } } db_end_transaction(0); blob_reset(&sql); blob_reset(&fn); blob_reset(&extra); return; }else if( fossil_strcmp(zCmd, "add")==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, ExtFILE) ) continue; g.dbIgnoreErrors++; rc = sqlite3_open(z, &db); if( rc!=SQLITE_OK ){ sqlite3_close(db); g.dbIgnoreErrors--; continue; } rc = sqlite3_exec(db, "SELECT rcvid FROM blob, delta LIMIT 1", 0, 0, 0); sqlite3_close(db); g.dbIgnoreErrors--; if( rc!=SQLITE_OK ) continue; blob_append_sql(&sql, "INSERT OR IGNORE INTO global_config(name,value)" "VALUES('repo:%q',1)", z ); if( dryRunFlag ){ fossil_print("%s\n", blob_sql_text(&sql)); }else{ db_unprotect(PROTECT_CONFIG); db_multi_exec("%s", blob_sql_text(&sql)); db_protect_pop(); } } db_end_transaction(0); blob_reset(&sql); blob_reset(&fn); blob_reset(&extra); return; }else if( fossil_strcmp(zCmd, "info")==0 ){ zCmd = "info"; showLabel = 1; quiet = 1; }else if( fossil_strcmp(zCmd, "cache")==0 ){ zCmd = "cache -R"; showLabel = 1; collect_argv(&extra, 3); }else if( fossil_strcmp(zCmd, "whatis")==0 ){ zCmd = "whatis -q -R"; quiet = 1; collect_argument(&extra, "file", "f"); collect_argument_value(&extra, "type", 0); collect_argv(&extra, 3); }else{ fossil_fatal("\"all\" subcommand should be one of: " "add cache changes clean dbstat extras fts-config git ignore " "info list ls pull push rebuild remote " "server setting sync ui unset whatis"); } verify_all_options(); 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_multi_exec("CREATE TEMP TABLE toDel(x TEXT)"); db_prepare(&q, "SELECT name, tag FROM repolist ORDER BY 1"); while( db_step(&q)==SQLITE_ROW ){ int rc; const char *zFilename = db_column_text(&q, 0); #if !USE_SEE if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue; #endif if( file_access(zFilename, F_OK) || !file_is_canonical(zFilename) || (useCheckouts && file_isdir(zFilename, ExtFILE)!=1) ){ db_multi_exec("INSERT INTO toDel VALUES(%Q)", db_column_text(&q, 1)); nToDel++; continue; } if( zCmd[0]=='l' ){ fossil_print("%s\n", zFilename); continue; }else if( showFile ){ fossil_print("%s: %s\n", useCheckouts ? "check-out" : "repository", zFilename); } zSyscmd = mprintf("%$ %s %$%s", g.nameOfExe, zCmd, zFilename, 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, '*'); fflush(stdout); } if( !quiet || dryRunFlag ){ fossil_print("%s\n", zSyscmd); fflush(stdout); } rc = dryRunFlag ? 0 : fossil_system(zSyscmd); free(zSyscmd); if( rc ){ if( stopOnError ) break; /* If there is an error, pause briefly, but do not stop. The brief ** pause is so that if the prior command failed with Ctrl-C then there ** will be time to stop the whole thing with a second Ctrl-C. */ sqlite3_sleep(330); } } db_finalize(&q); blob_reset(&extra); /* 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_unprotect(PROTECT_CONFIG); db_multi_exec("%s", zSql /*safe-for-%s*/ ); db_protect_pop(); } } } |
Changes to src/attach.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2010 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".) | | > | > < | > | | | > > > > > | | > > > > > > > | < | | | | | > > > > > | > > > | | > > > > > > | | > > | > > | | | | | | | | | | | | | > | > > > > | > < > < > | | > > > | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < | | < < < < | | | | | < < < < > | < < < < < < < < < < < < < < < < < < < < < < > > | > > > > > > > | | | | | | | < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | > | > > > > | > < | | < < < | < < < | > > > | > > > > > > > | > | > | > > | < < < | | > > > > > > > | < | > > > > > > > > | > > > | > > > | > > > > > | < > | > > > | > > > > > | < | | | > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > | | > > > > > > > > > > > > > > > > > > | > > > > > > > > > | | > > > > | > > > > > > > > > | > > > > | 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 | /* ** Copyright (c) 2010 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 for dealing with attachments. */ #include "config.h" #include "attach.h" #include <assert.h> /* ** WEBPAGE: attachlist ** List attachments. ** ** tkt=HASH ** page=WIKIPAGE ** technote=HASH ** ** At most one of technote=, tkt= or page= may be supplied. ** ** If none are given, all attachments are listed. If one is given, only ** attachments for the designated technote, ticket or wiki page are shown. ** ** HASH may be just a prefix of the relevant technical note or ticket ** artifact hash, in which case all attachments of all technical notes or ** tickets with the prefix will be listed. */ void attachlist_page(void){ const char *zPage = P("page"); const char *zTkt = P("tkt"); const char *zTechNote = P("technote"); Blob sql; Stmt q; if( zPage && zTkt ) zTkt = 0; login_check_credentials(); style_set_current_feature("attach"); blob_zero(&sql); blob_append_sql(&sql, "SELECT datetime(mtime,toLocal()), src, target, filename," " comment, user," " (SELECT uuid FROM blob WHERE rid=attachid), attachid," " (CASE WHEN 'tkt-'||target IN (SELECT tagname FROM tag)" " THEN 1" " WHEN 'event-'||target IN (SELECT tagname FROM tag)" " THEN 2" " ELSE 0 END)" " FROM attachment" ); 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( zTechNote ){ if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; } style_header("Attachments to Tech Note %S", zTechNote); blob_append_sql(&sql, " WHERE target GLOB '%q*'", zTechNote); }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); /* type 0 is a wiki page, 1 is a ticket, 2 is a tech note */ int type = db_column_int(&q, 8); 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( type==1 ){ zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename); }else if( type==2 ){ zUrlTail = mprintf("technote=%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> moderation_pending_www(attachid); @ <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 && zTechNote==0 ){ if( zSrc==0 || zSrc[0]==0 ){ zSrc = "Deleted from"; }else { zSrc = "Added to"; } if( type==1 ){ @ %s(zSrc) ticket <a href="%R/tktview?name=%s(zTarget)"> @ %S(zTarget)</a> }else if( type==2 ){ @ %s(zSrc) tech note <a href="%R/technote/%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 } } @ by %h(zDispUser) on hyperlink_to_date(zDate, "."); free(zUrlTail); } db_finalize(&q); @ </ol> style_finish_page(); return; } /* ** WEBPAGE: attachdownload ** WEBPAGE: attachimage ** WEBPAGE: attachview ** ** Download or display an attachment. ** ** Query parameters: ** ** tkt=HASH ** page=WIKIPAGE ** technote=HASH ** file=FILENAME ** attachid=ID ** */ void attachview_page(void){ const char *zPage = P("page"); const char *zTkt = P("tkt"); const char *zTechNote = P("technote"); const char *zFile = P("file"); const char *zTarget = 0; int attachid = atoi(PD("attachid","0")); char *zUUID; if( zFile==0 ) fossil_redirect_home(); login_check_credentials(); style_set_current_feature("attach"); 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 if( zTechNote ){ if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; } zTarget = zTechNote; }else{ fossil_redirect_home(); } if( attachid>0 ){ zUUID = db_text(0, "SELECT coalesce(src,'x') FROM attachment" " WHERE target=%Q AND attachid=%d", zTarget, attachid ); }else{ zUUID = db_text(0, "SELECT coalesce(src,'x') FROM attachment" " WHERE target=%Q AND filename=%Q" " ORDER BY mtime DESC LIMIT 1", zTarget, zFile ); } if( zUUID==0 || zUUID[0]==0 ){ style_header("No Such Attachment"); @ No such attachment.... style_finish_page(); return; }else if( zUUID[0]=='x' ){ style_header("Missing"); @ Attachment has been deleted style_finish_page(); return; }else{ g.perm.Read = 1; cgi_replace_parameter("name",zUUID); if( fossil_strcmp(g.zPath,"attachview")==0 ){ artifact_page(); }else{ cgi_replace_parameter("m", mimetype_from_name(zFile)); rawartifact_page(); } } } /* ** Save an attachment control artifact into the repository */ static void attach_put( Blob *pAttach, /* Text of the Attachment record */ int attachRid, /* RID for the file that is being attached */ int needMod /* True if the attachment is subject to moderation */ ){ int rid; if( needMod ){ rid = content_put_ex(pAttach, 0, 0, 0, 1); moderation_table_create(); db_multi_exec( "INSERT INTO modreq(objid,attachRid) VALUES(%d,%d);", rid, attachRid ); }else{ rid = content_put(pAttach); db_add_unsent(rid); db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); } manifest_crosslink(rid, pAttach, MC_NONE); } /* ** Commit a new attachment into the repository */ void attach_commit( const char *zName, /* The filename of the attachment */ const char *zTarget, /* The artifact hash to attach to */ const char *aContent, /* The content of the attachment */ int szContent, /* The length of the attachment */ int needModerator, /* Moderate the attachment? */ const char *zComment /* The comment for the attachment */ ){ Blob content; Blob manifest; Blob cksum; char *zUUID; char *zDate; int rid; int i, n; int addCompress = 0; Manifest *pManifest; db_begin_transaction(); blob_init(&content, aContent, szContent); pManifest = manifest_parse(&content, 0, 0); manifest_destroy(pManifest); blob_init(&content, aContent, szContent); if( pManifest ){ blob_compress(&content, &content); addCompress = 1; } 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+1; } zName += n; if( zName[0]==0 ) zName = "unknown"; blob_appendf(&manifest, "A %F%s %F %s\n", zName, addCompress ? ".gz" : "", zTarget, zUUID); 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); } /* ** WEBPAGE: attachadd ** Add a new attachment. ** ** tkt=HASH ** page=WIKIPAGE ** technote=HASH ** from=URL ** */ void attachadd_page(void){ const char *zPage = P("page"); const char *zTkt = P("tkt"); const char *zTechNote = P("technote"); const char *zFrom = P("from"); const char *aContent = P("f"); const char *zName = PD("f:filename","unknown"); const char *zTarget; char *zTargetType; int szContent = atoi(PD("f:bytes","0")); int goodCaptcha = 1; if( P("cancel") ) cgi_redirect(zFrom); if( (zPage && zTkt) || (zPage && zTechNote) || (zTkt && zTechNote) ){ fossil_redirect_home(); } if( zPage==0 && zTkt==0 && zTechNote==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 ( zTechNote ){ if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){ login_needed(g.anon.Write && g.anon.ApndWiki && g.anon.Attach); return; } if( !db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", zTechNote) ){ zTechNote = db_text(0, "SELECT substr(tagname,7) FROM tag" " WHERE tagname GLOB 'event-%q*'", zTechNote); if( zTechNote==0) fossil_redirect_home(); } zTarget = zTechNote; zTargetType = mprintf("Tech Note <a href=\"%R/technote/%s\">%S</a>", zTechNote, zTechNote); }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("%R/home"); if( P("cancel") ){ cgi_redirect(zFrom); } if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct(0)) ){ int needModerator = (zTkt!=0 && ticket_need_moderation(0)) || (zPage!=0 && wiki_need_moderation(0)); const char *zComment = PD("comment", ""); attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment); cgi_redirect(zFrom); } style_set_current_feature("attach"); style_header("Add Attachment"); if( !goodCaptcha ){ @ <p class="generalError">Error: Incorrect security code.</p> } @ <h2>Add Attachment To %s(zTargetType)</h2> form_begin("enctype='multipart/form-data'", "%R/attachadd"); @ <div> @ File to Attach: @ <input type="file" name="f" size="60"><br> @ Description:<br> @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br> if( zTkt ){ @ <input type="hidden" name="tkt" value="%h(zTkt)"> }else if( zTechNote ){ @ <input type="hidden" name="technote" value="%h(zTechNote)"> }else{ @ <input type="hidden" name="page" value="%h(zPage)"> } @ <input type="hidden" name="from" value="%h(zFrom)"> @ <input type="submit" name="ok" value="Add Attachment"> @ <input type="submit" name="cancel" value="Cancel"> @ </div> captcha_generate(0); @ </form> style_finish_page(); fossil_free(zTargetType); } /* ** WEBPAGE: ainfo ** URL: /ainfo?name=ARTIFACTID ** ** Show the details of an attachment artifact. */ void ainfo_page(void){ int rid; /* RID for the control artifact */ int ridSrc; /* RID for the attached file */ char *zDate; /* Date attached */ const char *zUuid; /* Hash of the control artifact */ Manifest *pAttach; /* Parse of the control artifact */ const char *zTarget; /* Wiki, ticket or tech note attached to */ const char *zSrc; /* Hash of the attached file */ const char *zName; /* Name of the attached file */ const char *zDesc; /* Description of the attached file */ const char *zWikiName = 0; /* Wiki page name when attached to Wiki */ const char *zTNUuid = 0; /* Tech Note ID when attached to tech note */ 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); 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", "%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", "%R/ainfo/%s?del", zUuid); } }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",zTarget) ){ zTNUuid = zTarget; if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } if( g.perm.Write && g.perm.WrWiki ){ style_submenu_element("Delete", "%R/ainfo/%s?del", zUuid); } } zDate = db_text(0, "SELECT datetime(%.12f)", pAttach->rDate); if( P("confirm") && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki) || (zTNUuid && g.perm.Write && g.perm.WrWiki)) ){ int i, n, rid; char *zDate; Blob manifest; Blob cksum; const char *zFile = zName; db_begin_transaction(); blob_zero(&manifest); for(i=n=0; zFile[i]; i++){ 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) || (zTNUuid && g.perm.Write && 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('a', rid); } } style_set_current_feature("attach"); style_header("Attachment Details"); style_submenu_element("Raw", "%R/artifact/%s", zUuid); if(fShowContent){ style_submenu_element("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_www(rid); if( zTktUuid ){ @ <tr><th>Ticket:</th> @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr> } if( zTNUuid ){ @ <tr><th>Tech Note:</th> @ <td>%z(href("%R/technote/%s",zTNUuid))%s(zTNUuid)</a></td></tr> } if( zWikiName ){ @ <tr><th>Wiki Page:</th> @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr> } @ <tr><th>Date:</th><td> hyperlink_to_date(zDate, "</td></tr>"); @ <tr><th>User:</th><td> 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, blob_size(&attach), zName, zLn, 1); }else{ @ <pre> @ %h(z) @ </pre> } }else if( strncmp(zMime, "image/", 6)==0 ){ int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); @ <i>(file is %d(sz) bytes of image data)</i><br> @ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img> style_submenu_element("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_finish_page(); } /* ** 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,toLocal()), filename, user," " (SELECT uuid FROM blob WHERE rid=attachid), src" " FROM attachment" " WHERE isLatest AND src!='' AND target=%Q" " ORDER BY mtime DESC", 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> @ [<a href="%R/attachdownload/%t(zFile)?page=%t(zTarget)&file=%t(zFile)">download</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); } /* ** COMMAND: attachment* ** ** Usage: %fossil attachment add ?PAGENAME? FILENAME ?OPTIONS? ** ** Add an attachment to an existing wiki page or tech note. ** ** Options: ** -t|--technote DATETIME Specifies the timestamp of ** the technote to which the attachment ** is to be made. The attachment will be ** to the most recently modified tech note ** with the specified timestamp. ** -t|--technote TECHNOTE-ID Specifies the technote to be ** updated by its technote id ** ** One of PAGENAME, DATETIME or TECHNOTE-ID must be specified. ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. */ void attachment_cmd(void){ int n; db_find_and_open_repository(0, 0); if( g.argc<3 ){ goto attachment_cmd_usage; } n = strlen(g.argv[2]); if( n==0 ){ goto attachment_cmd_usage; } if( strncmp(g.argv[2],"add",n)==0 ){ const char *zPageName = 0; /* Name of the wiki page to attach to */ const char *zFile; /* Name of the file to be attached */ const char *zETime; /* The name of the technote to attach to */ Manifest *pWiki = 0; /* Parsed wiki page content */ char *zBody = 0; /* Wiki page content */ int rid; const char *zTarget; /* Target of the attachment */ Blob content; /* The content of the attachment */ zETime = find_option("technote","t",1); if( !zETime ){ if( g.argc!=5 ){ usage("add PAGENAME FILENAME"); } 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); } zTarget = zPageName; zFile = g.argv[4]; }else{ if( g.argc!=4 ){ usage("add FILENAME --technote DATETIME|TECHNOTE-ID"); } rid = wiki_technote_to_rid(zETime); if( rid<0 ){ fossil_fatal("ambiguous tech note id: %s", zETime); } if( (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0 ){ zBody = pWiki->zWiki; } if( zBody==0 ){ fossil_fatal("technote [%s] not found",zETime); } zTarget = db_text(0, "SELECT substr(tagname,7) FROM tag " " WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')", rid ); zFile = g.argv[3]; } blob_read_from_file(&content, zFile, ExtFILE); user_select(); attach_commit( zFile, /* The filename of the attachment */ zTarget, /* The artifact hash to attach to */ blob_buffer(&content), /* The content of the attachment */ blob_size(&content), /* The length of the attachment */ 0, /* No need to moderate the attachment */ "" /* Empty attachment comment */ ); if( !zETime ){ fossil_print("Attached %s to wiki page %s.\n", zFile, zPageName); }else{ fossil_print("Attached %s to tech note %s.\n", zFile, zETime); } }else{ goto attachment_cmd_usage; } return; attachment_cmd_usage: usage("add ?PAGENAME? FILENAME [-t|--technote DATETIME ]"); } /* ** COMMAND: test-list-attachments ** ** Usage: %fossil test-list-attachments ?-latest? ?TargetName(s)...? ** ** List attachments for one or more attachment targets. The target ** name arguments are glob prefixes for the attachment.target ** field. If no names are provided then a prefix of [a-zA-Z] is used, ** which will match most wiki page names and some ticket hashes. ** ** Options: ** -latest List only the latest version of a given attachment ** */ void test_list_attachments(void){ Stmt q; int i; const int fLatest = find_option("latest", 0, 0) != 0; db_find_and_open_repository(0, 0); verify_all_options(); db_prepare(&q, "SELECT datetime(mtime,toLocal()), src, target, filename," " comment, user " " FROM attachment" " WHERE target GLOB :tgtname ||'*'" " AND (isLatest OR %d)" " ORDER BY target, isLatest DESC, mtime DESC", !fLatest ); if(g.argc<3){ static char * argv[3] = {0,0,"[a-zA-Z]"}; g.argc = 3; g.argv = argv; } for(i = 2; i < g.argc; ++i){ const char *zPage = g.argv[i]; db_bind_text(&q, ":tgtname", zPage); while(SQLITE_ROW == db_step(&q)){ const char * zTime = db_column_text(&q, 0); const char * zSrc = db_column_text(&q, 1); const char * zTarget = db_column_text(&q, 2); const char * zName = db_column_text(&q, 3); printf("%-20s %s %.12s %s\n", zTarget, zTime, zSrc, zName); } db_reset(&q); } db_finalize(&q); } |
Added src/backlink.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 | /* ** Copyright (c) 2020 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 file contains code to implement for managing backlinks and ** the "backlink" table of the repository database. ** ** A backlink is a reference in Fossil-Wiki or Markdown to some other ** object in the repository. */ #include "config.h" #include "backlink.h" #include <assert.h> /* ** Show a graph of all wiki, tickets, and check-ins that refer to object zUuid. ** ** If zLabel is not NULL and the graph is not empty, then output zLabel as ** a prefix to the graph. */ void render_backlink_graph(const char *zUuid, const char *zLabel){ Blob sql; Stmt q; char *zGlob; int needEndPanel = 0; zGlob = mprintf("%.5s*", zUuid); db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);\n" "DELETE FROM ok;\n" "INSERT OR IGNORE INTO ok(rid)\n" " SELECT CASE srctype\n" " WHEN 2 THEN (SELECT rid FROM tagxref WHERE tagid=backlink.srcid\n" " ORDER BY mtime DESC LIMIT 1)\n" " ELSE srcid END\n" " FROM backlink\n" " WHERE target GLOB %Q" " AND %Q GLOB (target || '*');", zGlob, zUuid ); if( !db_exists("SELECT 1 FROM ok") ) return; if( zLabel ){ cgi_printf("%s", zLabel); if( strstr(zLabel, "accordion")!=0 ){ cgi_printf("<div class=\"accordion_panel\">\n"); needEndPanel = 1; } } blob_zero(&sql); blob_append(&sql, timeline_query_for_www(), -1); 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|TIMELINE_NOSCROLL|TIMELINE_REFS, 0, 0, 0, 0, 0, 0); db_finalize(&q); if( needEndPanel ){ cgi_printf("</div>\n"); } } /* ** WEBPAGE: test-backlink-timeline ** ** Show a timeline of all check-ins and other events that have entries ** in the backlink table. This is used for testing the rendering ** of the "References" section of the /info page. */ void backlink_timeline_page(void){ Blob sql; Stmt q; 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; } style_set_current_feature("test"); style_header("Backlink Timeline (Internal Testing Use)"); db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" "DELETE FROM ok;" "INSERT OR IGNORE INTO ok" " SELECT blob.rid FROM backlink, blob" " WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')" ); blob_zero(&sql); blob_append(&sql, timeline_query_for_www(), -1); 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|TIMELINE_NOSCROLL, 0, 0, 0, 0, 0, 0); db_finalize(&q); style_finish_page(); } /* ** WEBPAGE: test-backlinks ** ** Show a table of all backlinks. Admin access only. */ void backlink_table_page(void){ Stmt q; int n; login_check_credentials(); if( !g.perm.Admin ){ login_needed(g.anon.Admin); return; } style_set_current_feature("test"); style_header("Backlink Table (Internal Testing Use)"); n = db_int(0, "SELECT count(*) FROM backlink"); @ <p>%d(n) backlink table entries:</p> db_prepare(&q, "SELECT target, srctype, srcid, datetime(mtime)," " CASE srctype" " WHEN 2 THEN (SELECT substr(tagname,6) FROM tag" " WHERE tagid=srcid AND tagname GLOB 'wiki-*')" " ELSE null END FROM backlink" ); style_table_sorter(); @ <table border="1" cellpadding="2" cellspacing="0" \ @ class='sortable' data-column-types='ttt' data-init-sort='0'> @ <thead><tr><th> Target <th> Source <th> mtime </tr></thead> @ <tbody> while( db_step(&q)==SQLITE_ROW ){ const char *zTarget = db_column_text(&q, 0); int srctype = db_column_int(&q, 1); int srcid = db_column_int(&q, 2); const char *zMtime = db_column_text(&q, 3); @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a> switch( srctype ){ case BKLNK_COMMENT: { @ <td><a href="%R/info?name=rid:%d(srcid)">checkin-%d(srcid)</a> break; } case BKLNK_TICKET: { @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a> break; } case BKLNK_WIKI: { const char *zName = db_column_text(&q, 4); @ <td><a href="%R/wiki?name=%h(zName)&p">wiki-%d(srcid)</a> break; } case BKLNK_EVENT: { @ <td><a href="%R/info?name=rid:%d(srcid)">tecknote-%d(srcid)</a> break; } case BKLNK_FORUM: { @ <td><a href="%R/info?name=rid:%d(srcid)">forum-%d(srcid)</a> break; } default: { @ <td>unknown(%d(srctype)) - %d(srcid) break; } } @ <td>%h(zMtime)</tr> } @ </tbody> @ </table> db_finalize(&q); style_finish_page(); } /* ** Remove all prior backlinks for the wiki page given. Then ** add new backlinks for the latest version of the wiki page. */ void backlink_wiki_refresh(const char *zWikiTitle){ int tagid = wiki_tagid(zWikiTitle); int rid; Manifest *pWiki; if( tagid==0 ) return; rid = db_int(0, "SELECT rid FROM tagxref WHERE tagid=%d" " ORDER BY mtime DESC LIMIT 1", tagid); if( rid==0 ) return; pWiki = manifest_get(rid, CFTYPE_WIKI, 0); if( pWiki ){ int mimetype = parse_mimetype( pWiki->zMimetype ); backlink_extract(pWiki->zWiki, mimetype, tagid, BKLNK_WIKI, pWiki->rDate, 1); manifest_destroy(pWiki); } } /* ** Structure used to pass down state information through the ** markup formatters into the BACKLINK generator. */ #if INTERFACE struct Backlink { int srcid; /* srcid for the source document */ int srctype; /* One of BKLNK_*. 0=comment 1=ticket 2=wiki */ double mtime; /* mtime field for new BACKLINK table entries */ }; #endif /* ** zTarget is a hyperlink target in some markup format. If this ** target is a self-reference to some other object in the repository, ** then create an appropriate backlink. */ void backlink_create(Backlink *p, const char *zTarget, int nTarget){ char zLink[HNAME_MAX+4]; if( zTarget==0 ) return; if( nTarget<4 ) return; if( nTarget>=10 && strncmp(zTarget,"/info/",6)==0 ){ zTarget += 6; nTarget -= 6; } if( nTarget>HNAME_MAX ) return; if( !validate16(zTarget, nTarget) ) return; memcpy(zLink, zTarget, nTarget); zLink[nTarget] = 0; canonical16(zLink, nTarget); db_multi_exec( "REPLACE INTO backlink(target,srctype,srcid,mtime)" "VALUES(%Q,%d,%d,%.17g)", zLink, p->srctype, p->srcid, p->mtime ); } /* ** This routine is called by the markdown formatter for each hyperlink. ** If the hyperlink is a backlink, add it to the BACKLINK table. */ static int backlink_md_link( Blob *ob, /* Write output text here (not used in this case) */ Blob *target, /* The hyperlink target */ Blob *title, /* Hyperlink title */ Blob *content, /* Content of the link */ void *opaque ){ Backlink *p = (Backlink*)opaque; char *zTarget = blob_buffer(target); int nTarget = blob_size(target); backlink_create(p, zTarget, nTarget); return 1; } /* No-op routines for the rendering callbacks that we do not need */ static void mkdn_noop_prolog(Blob *b, void *v){ return; } static void (*mkdn_noop_epilog)(Blob*, void*) = mkdn_noop_prolog; static void mkdn_noop_footnotes(Blob *b1, const Blob *b2, void *v){ return; } static void mkdn_noop_blockcode(Blob *b1, Blob *b2, void *v){ return; } static void (*mkdn_noop_blockquote)(Blob*, Blob*, void*) = mkdn_noop_blockcode; static void (*mkdn_noop_blockhtml)(Blob*, Blob*, void*) = mkdn_noop_blockcode; static void mkdn_noop_header(Blob *b1, Blob *b2, int i, void *v){ return; } static void (*mkdn_noop_hrule)(Blob*, void*) = mkdn_noop_prolog; static void (*mkdn_noop_list)(Blob*, Blob*, int, void*) = mkdn_noop_header; static void (*mkdn_noop_listitem)(Blob*, Blob*, int, void*) = mkdn_noop_header; static void (*mkdn_noop_paragraph)(Blob*, Blob*, void*) = mkdn_noop_blockcode; static void mkdn_noop_table(Blob *b1, Blob *b2, Blob *b3, void *v){ return; } static void (*mkdn_noop_table_cell)(Blob*, Blob*, int, void*) = mkdn_noop_header; static void (*mkdn_noop_table_row)(Blob*, Blob*, int, void*) = mkdn_noop_header; static void mkdn_noop_footnoteitm(Blob *b1, const Blob *b2, int i1, int i2, void *v){ return; } static int mkdn_noop_autolink(Blob *b1, Blob *b2, enum mkd_autolink e, void *v){ return 1; } static int mkdn_noop_codespan(Blob *b1, Blob *b2, int i, void *v){ return 1; } static int mkdn_noop_emphasis(Blob *b1, Blob *b2, char c, void *v){ return 1; } static int (*mkdn_noop_dbl_emphas)(Blob*, Blob*, char, void*) = mkdn_noop_emphasis; static int mkdn_noop_image(Blob *b1, Blob *b2, Blob *b3, Blob *b4, void *v){ return 1; } static int mkdn_noop_linebreak(Blob *b1, void *v){ return 1; } static int mkdn_noop_r_html_tag(Blob *b1, Blob *b2, void *v){ return 1; } static int (*mkdn_noop_tri_emphas)(Blob*, Blob*, char, void*) = mkdn_noop_emphasis; static int mkdn_noop_footnoteref(Blob *b1, const Blob *b2, const Blob *b3, int i1, int i2, void *v){ return 1; } /* ** Scan markdown text and add self-hyperlinks to the BACKLINK table. */ void markdown_extract_links( char *zInputText, Backlink *p ){ struct mkd_renderer html_renderer = { /* prolog */ mkdn_noop_prolog, /* epilog */ mkdn_noop_epilog, /* footnotes */ mkdn_noop_footnotes, /* blockcode */ mkdn_noop_blockcode, /* blockquote */ mkdn_noop_blockquote, /* blockhtml */ mkdn_noop_blockhtml, /* header */ mkdn_noop_header, /* hrule */ mkdn_noop_hrule, /* list */ mkdn_noop_list, /* listitem */ mkdn_noop_listitem, /* paragraph */ mkdn_noop_paragraph, /* table */ mkdn_noop_table, /* table_cell */ mkdn_noop_table_cell, /* table_row */ mkdn_noop_table_row, /* footnoteitm*/ mkdn_noop_footnoteitm, /* autolink */ mkdn_noop_autolink, /* codespan */ mkdn_noop_codespan, /* dbl_emphas */ mkdn_noop_dbl_emphas, /* emphasis */ mkdn_noop_emphasis, /* image */ mkdn_noop_image, /* linebreak */ mkdn_noop_linebreak, /* link */ backlink_md_link, /* r_html_tag */ mkdn_noop_r_html_tag, /* tri_emphas */ mkdn_noop_tri_emphas, /* footnoteref*/ mkdn_noop_footnoteref, 0, /* entity */ 0, /* normal_text */ "*_", /* emphasis characters */ 0 /* client data */ }; Blob out, in; html_renderer.opaque = (void*)p; blob_init(&out, 0, 0); blob_init(&in, zInputText, -1); markdown(&out, &in, &html_renderer); blob_reset(&out); blob_reset(&in); } /* ** Transform mimetype string into an integer code. ** NOTE: In the sake of compatability empty string is parsed as MT_UNKNOWN; ** it is yet unclear whether it can safely be changed to MT_NONE. */ int parse_mimetype(const char* zMimetype){ if( zMimetype==0 ) return MT_NONE; if( strstr(zMimetype,"wiki")!=0 ) return MT_WIKI; if( strstr(zMimetype,"markdown")!=0 ) return MT_MARKDOWN; return MT_UNKNOWN; } /* ** Parse text looking for hyperlinks. Insert references into the ** BACKLINK table. */ void backlink_extract( char *zSrc, /* Input text from which links are extracted */ int mimetype, /* Mimetype of input. MT_NONE works as MT_WIKI */ int srcid, /* srcid for the source document */ int srctype, /* One of BKLNK_*. 0=comment 1=ticket 2=wiki */ double mtime, /* mtime field for new BACKLINK table entries */ int replaceFlag /* True to overwrite prior BACKLINK entries */ ){ Backlink bklnk; if( replaceFlag ){ db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d", srctype, srcid); } bklnk.srcid = srcid; assert( ValidBklnk(srctype) ); assert( ValidMTC(mimetype) ); bklnk.srctype = srctype; bklnk.mtime = mtime; if( mimetype==MT_NONE || mimetype==MT_WIKI ){ wiki_extract_links(zSrc, &bklnk, srctype==BKLNK_COMMENT ? WIKI_INLINE : 0); }else if( mimetype==MT_MARKDOWN ){ markdown_extract_links(zSrc, &bklnk); } } /* ** COMMAND: test-backlinks ** ** Usage: %fossil test-backlinks SRCTYPE SRCID ?OPTIONS? INPUT-FILE ** ** Read the content of INPUT-FILE and pass it into the backlink_extract() ** routine. But instead of adding backlinks to the backlink table, ** just print them on stdout. SRCID and SRCTYPE are integers. ** ** Options: ** --mtime DATETIME Use an alternative date/time. Defaults to the ** current date/time. ** --mimetype TYPE Use an alternative mimetype */ void test_backlinks_cmd(void){ const char *zMTime = find_option("mtime",0,1); const char *zMimetype = find_option("mimetype",0,1); const int mimetype = parse_mimetype(zMimetype); Blob in; int srcid; int srctype; double mtime; verify_all_options(); if( g.argc!=5 ){ usage("SRCTYPE SRCID INPUTFILE"); } srctype = atoi(g.argv[2]); if( srctype<0 || srctype>2 ){ fossil_fatal("SRCTYPE should be an integer 0, 1, or 2"); } srcid = atoi(g.argv[3]); blob_read_from_file(&in, g.argv[4], ExtFILE); sqlite3_open(":memory:",&g.db); if( zMTime==0 ) zMTime = "now"; mtime = db_double(1721059.5,"SELECT julianday(%Q)",zMTime); g.fSqlPrint = 1; sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); db_multi_exec( "CREATE TEMP TABLE backlink(target,srctype,srcid,mtime);\n" "CREATE TRIGGER backlink_insert BEFORE INSERT ON backlink BEGIN\n" " SELECT print(" " 'target='||quote(new.target)||" " ' srctype='||quote(new.srctype)||" " ' srcid='||quote(new.srcid)||" " ' mtime='||datetime(new.mtime));\n" " SELECT raise(ignore);\n" "END;" ); backlink_extract(blob_str(&in),mimetype,srcid,srctype,mtime,0); blob_reset(&in); } /* ** COMMAND: test-wiki-relink ** ** Usage: %fossil test-wiki-relink WIKI-PAGE-NAME ** ** Run the backlink_wiki_refresh() procedure on the wiki page ** named. WIKI-PAGE-NAME can be a glob pattern or a prefix ** of the wiki page. */ void test_wiki_relink_cmd(void){ Stmt q; db_find_and_open_repository(0, 0); if( g.argc!=3 ) usage("WIKI-PAGE-NAME"); db_prepare(&q, "SELECT substr(tagname,6) FROM tag WHERE tagname GLOB 'wiki-%q*'", g.argv[2] ); while( db_step(&q)==SQLITE_ROW ){ const char *zPage = db_column_text(&q,0); fossil_print("Relinking page: %s\n", zPage); backlink_wiki_refresh(zPage); } db_finalize(&q); } |
Added src/backoffice.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 | /* ** Copyright (c) 2018 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 manage a background processes that ** occur after user interaction with the repository. Examples of ** backoffice processing includes: ** ** * Sending alerts and notifications ** * Processing the email queue ** * Handling post-receive hooks ** * Automatically syncing to peer repositories ** ** Backoffice processing is automatically started whenever there are ** changes to the repository. The backoffice process dies off after ** a period of inactivity. ** ** Steps are taken to ensure that only a single backoffice process is ** running at a time. Otherwise, there could be race conditions that ** cause adverse effects such as multiple alerts for the same changes. ** ** At the same time, we do not want a backoffice process to run forever. ** Backoffice processes should die off after doing whatever work they need ** to do. In this way, we avoid having lots of idle processes in the ** process table, doing nothing on rarely accessed repositories, and ** if the Fossil binary is updated on a system, the backoffice processes ** will restart using the new binary automatically. ** ** At any point in time there should be at most two backoffice processes. ** There is a main process that is doing the actually work, and there is ** a second stand-by process that is waiting for the main process to finish ** and that will become the main process after a delay. ** ** After any successful web page reply, the backoffice_check_if_needed() ** routine is called. That routine checks to see if both one or both of ** the backoffice processes are already running. That routine remembers the ** status in a global variable. ** ** Later, after the repository database is closed, the ** backoffice_run_if_needed() routine is called. If the prior call ** to backoffice_check_if_needed() indicated that backoffice processing ** might be required, the run_if_needed() attempts to kick off a backoffice ** process. ** ** All work performance by the backoffice is in the backoffice_work() ** routine. */ #if defined(_WIN32) # if defined(_WIN32_WINNT) # undef _WIN32_WINNT # endif # define _WIN32_WINNT 0x501 #endif #include "config.h" #include "backoffice.h" #include <time.h> #if defined(_WIN32) # include <windows.h> # include <stdio.h> # include <process.h> # if defined(__MINGW32__) # include <wchar.h> # endif # define GETPID (int)GetCurrentProcessId #else # include <unistd.h> # include <sys/types.h> # include <signal.h> # include <errno.h> # include <sys/time.h> # include <sys/resource.h> # include <fcntl.h> # define GETPID getpid #endif #include <time.h> /* ** The BKOFCE_LEASE_TIME is the amount of time for which a single backoffice ** processing run is valid. Each backoffice run monopolizes the lease for ** at least this amount of time. Hopefully all backoffice processing is ** finished much faster than this - usually in less than a second. But ** regardless of how long each invocation lasts, successive backoffice runs ** must be spaced out by at least this much time. */ #define BKOFCE_LEASE_TIME 60 /* Length of lease validity in seconds */ #if LOCAL_INTERFACE /* ** An instance of the following object describes a lease on the backoffice ** processing timeslot. This lease is used to help ensure that no more than ** one process is running backoffice at a time. */ struct Lease { sqlite3_uint64 idCurrent; /* process ID for the current lease holder */ sqlite3_uint64 tmCurrent; /* Expiration of the current lease */ sqlite3_uint64 idNext; /* process ID for the next lease holder on queue */ sqlite3_uint64 tmNext; /* Expiration of the next lease */ }; #endif /*************************************************************************** ** Local state variables ** ** Set to prevent backoffice processing from ever entering sleep or ** otherwise taking a long time to complete. Set this when a user-visible ** process might need to wait for backoffice to complete. */ static int backofficeNoDelay = 0; /* This variable is set to the name of a database on which backoffice ** should run if backoffice process is needed. It is set by the ** backoffice_check_if_needed() routine which must be run while the database ** file is open. Later, after the database is closed, the ** backoffice_run_if_needed() will consult this variable to see if it ** should be a no-op. ** ** The magic string "x" in this variable means "do not run the backoffice". */ static char *backofficeDb = 0; /* ** Log backoffice activity to a file named here. If not NULL, this ** overrides the "backoffice-logfile" setting of the database. If NULL, ** the "backoffice-logfile" setting is used instead. */ static const char *backofficeLogfile = 0; /* ** Write the log message into this open file. */ static FILE *backofficeFILE = 0; /* ** Write backoffice log messages on this BLOB. to this connection: */ static Blob *backofficeBlob = 0; /* ** Non-zero for extra logging detail. */ static int backofficeLogDetail = 0; /* End of state variables ****************************************************************************/ /* ** This function emits a diagnostic message related to the processing in ** this module. */ #if defined(_WIN32) # define BKOFCE_ALWAYS_TRACE (1) extern void sqlite3_win32_write_debug(const char *, int); #else # define BKOFCE_ALWAYS_TRACE (0) #endif static void backofficeTrace(const char *zFormat, ...){ char *zMsg = 0; if( BKOFCE_ALWAYS_TRACE || g.fAnyTrace ){ va_list ap; va_start(ap, zFormat); zMsg = sqlite3_vmprintf(zFormat, ap); va_end(ap); #if defined(_WIN32) sqlite3_win32_write_debug(zMsg, -1); #endif } if( g.fAnyTrace ) fprintf(stderr, "%s", zMsg); if( zMsg ) sqlite3_free(zMsg); } /* ** Do not allow backoffice processes to sleep waiting on a timeslot. ** They must either do their work immediately or exit. ** ** In a perfect world, this interface would not exist, as there would ** never be a problem with waiting backoffice threads. But in some cases ** a backoffice will delay a UI thread, so we don't want them to run for ** longer than needed. */ void backoffice_no_delay(void){ backofficeNoDelay = 1; } /* ** Sleeps for the specified number of milliseconds -OR- until interrupted ** by another thread (if supported by the underlying platform). Non-zero ** will be returned if the sleep was interrupted. */ static int backofficeSleep(int milliseconds){ #if defined(_WIN32) assert( milliseconds>=0 ); if( SleepEx((DWORD)milliseconds, TRUE)==WAIT_IO_COMPLETION ){ return 1; } #else sqlite3_sleep(milliseconds); #endif return 0; } /* ** Parse a unsigned 64-bit integer from a string. Return a pointer ** to the character of z[] that occurs after the integer. */ static const char *backofficeParseInt(const char *z, sqlite3_uint64 *pVal){ *pVal = 0; if( z==0 ) return 0; while( fossil_isspace(z[0]) ){ z++; } while( fossil_isdigit(z[0]) ){ *pVal = (*pVal)*10 + z[0] - '0'; z++; } return z; } /* ** Read the "backoffice" property and parse it into a Lease object. ** ** The backoffice property should consist of four integers: ** ** (1) Process ID for the active backoffice process. ** (2) Time (seconds since 1970) for when the active backoffice ** lease expires. ** (3) Process ID for the on-deck backoffice process. ** (4) Time when the on-deck process should expire. ** ** No other process should start active backoffice processing until ** process (1) no longer exists and the current time exceeds (2). */ static void backofficeReadLease(Lease *pLease){ Stmt q; memset(pLease, 0, sizeof(*pLease)); db_unprotect(PROTECT_CONFIG); db_prepare(&q, "SELECT value FROM repository.config" " WHERE name='backoffice'"); if( db_step(&q)==SQLITE_ROW ){ const char *z = db_column_text(&q,0); z = backofficeParseInt(z, &pLease->idCurrent); z = backofficeParseInt(z, &pLease->tmCurrent); z = backofficeParseInt(z, &pLease->idNext); backofficeParseInt(z, &pLease->tmNext); } db_finalize(&q); db_protect_pop(); } /* ** Return a string that describes how long it has been since the ** last backoffice run. The string is obtained from fossil_malloc(). */ char *backoffice_last_run(void){ Lease x; sqlite3_uint64 tmNow; double rAge; backofficeReadLease(&x); tmNow = time(0); if( x.tmCurrent==0 ){ return fossil_strdup("never"); } if( tmNow<=(x.tmCurrent-BKOFCE_LEASE_TIME) ){ return fossil_strdup("moments ago"); } rAge = (tmNow - (x.tmCurrent-BKOFCE_LEASE_TIME))/86400.0; return mprintf("%z ago", human_readable_age(rAge)); } /* ** Write a lease to the backoffice property */ static void backofficeWriteLease(Lease *pLease){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "REPLACE INTO repository.config(name,value,mtime)" " VALUES('backoffice','%lld %lld %lld %lld',now())", pLease->idCurrent, pLease->tmCurrent, pLease->idNext, pLease->tmNext); db_protect_pop(); } /* ** Check to see if the specified Win32 process is still alive. It ** should be noted that even if this function returns non-zero, the ** process may die before another operation on it can be completed. */ #if defined(_WIN32) #ifndef PROCESS_QUERY_LIMITED_INFORMATION # define PROCESS_QUERY_LIMITED_INFORMATION (0x1000) #endif static int backofficeWin32ProcessExists(DWORD dwProcessId){ HANDLE hProcess; hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,FALSE,dwProcessId); if( hProcess==NULL ) return 0; CloseHandle(hProcess); return 1; } #endif /* ** Check to see if the process identified by pid is alive. If ** we cannot prove that the process is dead, return true. */ static int backofficeProcessExists(sqlite3_uint64 pid){ #if defined(_WIN32) return pid>0 && backofficeWin32ProcessExists((DWORD)pid)!=0; #else return pid>0 && kill((pid_t)pid, 0)==0; #endif } /* ** Check to see if the process identified by pid has finished. If ** we cannot prove that the process is still running, return true. */ static int backofficeProcessDone(sqlite3_uint64 pid){ #if defined(_WIN32) return pid<=0 || backofficeWin32ProcessExists((DWORD)pid)==0; #else return pid<=0 || kill((pid_t)pid, 0)!=0; #endif } /* ** Return a process id number for the current process */ static sqlite3_uint64 backofficeProcessId(void){ return (sqlite3_uint64)GETPID(); } /* ** COMMAND: test-process-id ** ** Usage: %fossil [--sleep N] PROCESS-ID ... ** ** Show the current process id, and also tell whether or not all other ** processes IDs on the command line are running or not. If the --sleep N ** option is provide, then sleep for N seconds before exiting. */ void test_process_id_command(void){ const char *zSleep = find_option("sleep",0,1); int i; verify_all_options(); fossil_print("ProcessID for this process: %lld\n", backofficeProcessId()); if( zSleep ) sqlite3_sleep(1000*atoi(zSleep)); for(i=2; i<g.argc; i++){ sqlite3_uint64 x = (sqlite3_uint64)atoi(g.argv[i]); fossil_print("ProcessId %lld: exists %d done %d\n", x, backofficeProcessExists(x), backofficeProcessDone(x)); } } /* ** COMMAND: test-backoffice-lease ** ** Usage: %fossil test-backoffice-lease ?--reset? ** ** Print out information about the backoffice "lease" entry in the ** config table that controls whether or not backoffice should run. ** ** If the --reset option is given, the backoffice lease is reset. ** The use of the --reset option can be disruptive. It can cause two ** or more backoffice processes to be run simultaneously. Use it with ** caution. */ void test_backoffice_lease(void){ sqlite3_int64 tmNow = time(0); Lease x; const char *zLease; db_find_and_open_repository(0,0); if( find_option("reset",0,0)!=0 ){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "DELETE FROM repository.config WHERE name='backoffice'" ); db_protect_pop(); } verify_all_options(); zLease = db_get("backoffice",""); fossil_print("now: %lld\n", tmNow); fossil_print("lease: \"%s\"\n", zLease); backofficeReadLease(&x); fossil_print("idCurrent: %-20lld", x.idCurrent); if( backofficeProcessExists(x.idCurrent) ) fossil_print(" (exists)"); if( backofficeProcessDone(x.idCurrent) ) fossil_print(" (done)"); fossil_print("\n"); fossil_print("tmCurrent: %-20lld", x.tmCurrent); if( x.tmCurrent>0 ){ fossil_print(" (now%+d)\n",x.tmCurrent-tmNow); }else{ fossil_print("\n"); } fossil_print("idNext: %-20lld", x.idNext); if( backofficeProcessExists(x.idNext) ) fossil_print(" (exists)"); if( backofficeProcessDone(x.idNext) ) fossil_print(" (done)"); fossil_print("\n"); fossil_print("tmNext: %-20lld", x.tmNext); if( x.tmNext>0 ){ fossil_print(" (now%+d)\n",x.tmNext-tmNow); }else{ fossil_print("\n"); } } /* ** If backoffice processing is needed set the backofficeDb variable to the ** name of the database file. If no backoffice processing is needed, ** this routine makes no changes to state. */ void backoffice_check_if_needed(void){ Lease x; sqlite3_uint64 tmNow; if( backofficeDb ) return; if( g.zRepositoryName==0 ) return; if( g.db==0 ) return; if( !db_table_exists("repository","config") ) return; if( db_get_boolean("backoffice-disable",0) ) return; tmNow = time(0); backofficeReadLease(&x); if( x.tmNext>=tmNow && backofficeProcessExists(x.idNext) ){ /* Another backoffice process is already queued up to run. This ** process does not need to do any backoffice work. */ return; }else{ /* We need to run backup to be (at a minimum) on-deck */ backofficeDb = fossil_strdup(g.zRepositoryName); } } /* ** Call this routine to disable backoffice */ void backoffice_disable(void){ backofficeDb = "x"; } /* ** Check for errors prior to running backoffice_thread() or backoffice_run(). */ static void backoffice_error_check_one(int *pOnce){ if( *pOnce ){ fossil_panic("multiple calls to backoffice()"); } *pOnce = 1; if( g.db==0 ){ fossil_panic("database not open for backoffice processing"); } if( db_transaction_nesting_depth()!=0 ){ fossil_panic("transaction %s not closed prior to backoffice processing", db_transaction_start_point()); } } /* This is the main loop for backoffice processing. ** ** If another process is already working as the current backoffice and ** the on-deck backoffice, then this routine returns very quickly ** without doing any work. ** ** If no backoffice processes are running at all, this routine becomes ** the main backoffice. ** ** If a primary backoffice is running, but an on-deck backoffice is ** needed, this routine becomes that on-deck backoffice. */ static void backoffice_thread(void){ Lease x; sqlite3_uint64 tmNow; sqlite3_uint64 idSelf; int lastWarning = 0; int warningDelay = 30; static int once = 0; if( sqlite3_db_readonly(g.db, 0) ) return; g.zPhase = "backoffice"; backoffice_error_check_one(&once); idSelf = backofficeProcessId(); while(1){ tmNow = time(0); db_begin_write(); backofficeReadLease(&x); if( x.tmNext>=tmNow && x.idNext!=idSelf && backofficeProcessExists(x.idNext) ){ /* Another backoffice process is already queued up to run. This ** process does not need to do any backoffice work and can stop ** immediately. */ db_end_transaction(0); backofficeTrace("/***** Backoffice Processing Not Needed In %d *****/\n", GETPID()); break; } if( x.tmCurrent<tmNow && backofficeProcessDone(x.idCurrent) ){ /* This process can start doing backoffice work immediately */ x.idCurrent = idSelf; x.tmCurrent = tmNow + BKOFCE_LEASE_TIME; x.idNext = 0; x.tmNext = 0; backofficeWriteLease(&x); db_end_transaction(0); backofficeTrace("/***** Begin Backoffice Processing %d *****/\n", GETPID()); backoffice_work(); break; } if( backofficeNoDelay || db_get_boolean("backoffice-nodelay",0) ){ /* If the no-delay flag is set, exit immediately rather than queuing ** up. Assume that some future request will come along and handle any ** necessary backoffice work. */ db_end_transaction(0); backofficeTrace( "/***** Backoffice No-Delay Exit For %d *****/\n", GETPID()); break; } /* This process needs to queue up and wait for the current lease ** to expire before continuing. */ x.idNext = idSelf; x.tmNext = (tmNow>x.tmCurrent ? tmNow : x.tmCurrent) + BKOFCE_LEASE_TIME; backofficeWriteLease(&x); db_end_transaction(0); backofficeTrace("/***** Backoffice On-deck %d *****/\n", GETPID()); if( x.tmCurrent >= tmNow ){ if( backofficeSleep(1000*(x.tmCurrent - tmNow + 1)) ){ /* The sleep was interrupted by a signal from another thread. */ backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID()); db_end_transaction(0); break; } }else{ if( (sqlite3_uint64)(lastWarning+warningDelay) < tmNow ){ fossil_warning( "backoffice process %lld still running after %d seconds", x.idCurrent, (int)(BKOFCE_LEASE_TIME + tmNow - x.tmCurrent)); lastWarning = tmNow; warningDelay *= 2; } if( backofficeSleep(1000) ){ /* The sleep was interrupted by a signal from another thread. */ backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID()); db_end_transaction(0); break; } } } return; } /* ** Append to a message to the backoffice log, if the log is open. */ void backoffice_log(const char *zFormat, ...){ va_list ap; if( backofficeBlob==0 ) return; blob_append_char(backofficeBlob, ' '); va_start(ap, zFormat); blob_vappendf(backofficeBlob, zFormat, ap); va_end(ap); } #if !defined(_WIN32) /* ** Capture routine for signals while running backoffice. */ static void backoffice_signal_handler(int sig){ const char *zSig = 0; if( sig==SIGSEGV ) zSig = "SIGSEGV"; if( sig==SIGFPE ) zSig = "SIGFPE"; if( sig==SIGABRT ) zSig = "SIGABRT"; if( sig==SIGILL ) zSig = "SIGILL"; if( zSig==0 ){ backoffice_log("signal-%d", sig); }else{ backoffice_log("%s", zSig); } fprintf(backofficeFILE, "%s\n", blob_str(backofficeBlob)); fflush(backofficeFILE); exit(1); } #endif #if !defined(_WIN32) /* ** Convert a struct timeval into an integer number of microseconds */ static long long int tvms(struct timeval *p){ return ((long long int)p->tv_sec)*1000000 + (long long int)p->tv_usec; } #endif /* ** This routine runs to do the backoffice processing. When adding new ** backoffice processing tasks, add them here. */ void backoffice_work(void){ /* Log the backoffice run for testing purposes. For production deployments ** the "backoffice-logfile" property should be unset and the following code ** should be a no-op. */ const char *zLog = backofficeLogfile; Blob log; int nThis; int nTotal = 0; #if !defined(_WIN32) struct timeval sStart, sEnd; #endif if( zLog==0 ) zLog = db_get("backoffice-logfile",0); if( zLog && zLog[0] && (backofficeFILE = fossil_fopen(zLog,"a"))!=0 ){ int i; char *zName = db_get("project-name",0); #if !defined(_WIN32) gettimeofday(&sStart, 0); signal(SIGSEGV, backoffice_signal_handler); signal(SIGABRT, backoffice_signal_handler); signal(SIGFPE, backoffice_signal_handler); signal(SIGILL, backoffice_signal_handler); #endif if( zName==0 ){ zName = (char*)file_tail(g.zRepositoryName); if( zName==0 ) zName = "(unnamed)"; }else{ /* Convert all spaces in the "project-name" into dashes */ for(i=0; zName[i]; i++){ if( zName[i]==' ' ) zName[i] = '-'; } } blob_init(&log, 0, 0); backofficeBlob = &log; blob_appendf(&log, "%s %s", db_text(0, "SELECT datetime('now')"), zName); } /* Here is where the actual work of the backoffice happens */ nThis = alert_backoffice(0); if( nThis ){ backoffice_log("%d alerts", nThis); nTotal += nThis; } nThis = hook_backoffice(); if( nThis ){ backoffice_log("%d hooks", nThis); nTotal += nThis; } /* Close the log */ if( backofficeFILE ){ if( nTotal || backofficeLogDetail ){ if( nTotal==0 ) backoffice_log("no-op"); #if !defined(_WIN32) gettimeofday(&sEnd,0); backoffice_log("elapse-time %d us", tvms(&sEnd) - tvms(&sStart)); #endif fprintf(backofficeFILE, "%s\n", blob_str(backofficeBlob)); } fclose(backofficeFILE); } } /* ** COMMAND: backoffice* ** ** Usage: %fossil backoffice [OPTIONS...] [REPOSITORIES...] ** ** Run backoffice processing on the repositories listed. If no ** repository is specified, run it on the repository of the local check-out. ** ** This might be done by a cron job or similar to make sure backoffice ** processing happens periodically. Or, the --poll option can be used ** to run this command as a daemon that will periodically invoke backoffice ** on a collection of repositories. ** ** If only a single repository is named and --poll is omitted, then the ** backoffice work is done in-process. But if there are multiple repositories ** or if --poll is used, a separate sub-process is started for each poll of ** each repository. ** ** Standard options: ** ** --debug Show what this command is doing ** ** --logfile FILE Append a log of backoffice actions onto FILE ** ** --min N When polling, invoke backoffice at least ** once every N seconds even if the repository ** never changes. 0 or negative means disable ** this feature. Default: 3600 (once per hour). ** ** --poll N Repeat backoffice calls for repositories that ** change in appoximately N-second intervals. ** N less than 1 turns polling off (the default). ** Recommended polling interval: 60 seconds. ** ** --trace Enable debugging output on stderr ** ** Options intended for internal use only which may change or be ** discontinued in future releases: ** ** --nodelay Do not queue up or wait for a backoffice job ** to complete. If no work is available or if ** backoffice has run recently, return immediately. ** ** --nolease Always run backoffice, even if there is a lease ** conflict. This option implies --nodelay. This ** option is added to secondary backoffice commands ** that are invoked by the --poll option. */ void backoffice_command(void){ int nPoll; int nMin; const char *zPoll; int bDebug = 0; int bNoLease = 0; unsigned int nCmd = 0; if( find_option("trace",0,0)!=0 ) g.fAnyTrace = 1; if( find_option("nodelay",0,0)!=0 ) backofficeNoDelay = 1; backofficeLogfile = find_option("logfile",0,1); zPoll = find_option("poll",0,1); nPoll = zPoll ? atoi(zPoll) : 0; zPoll = find_option("min",0,1); nMin = zPoll ? atoi(zPoll) : 3600; bDebug = find_option("debug",0,0)!=0; bNoLease = find_option("nolease",0,0)!=0; /* Silently consume the -R or --repository flag, leaving behind its ** argument. This is for legacy compatibility. Older versions of the ** backoffice command only ran on a single repository that was specified ** using the -R option. */ (void)find_option("repository","R",0); verify_all_options(); if( g.argc>3 || nPoll>0 ){ /* Either there are multiple repositories named on the command-line ** or we are polling. In either case, each backoffice should be run ** using a separate sub-process */ int i; time_t iNow = 0; time_t ix; i64 *aLastRun = fossil_malloc( sizeof(i64)*g.argc ); memset(aLastRun, 0, sizeof(i64)*g.argc ); while( 1 /* exit via "break;" */){ time_t iNext = time(0); for(i=2; i<g.argc; i++){ Blob cmd; if( !file_isfile(g.argv[i], ExtFILE) ){ continue; /* Repo no longer exists. Ignore it. */ } if( iNow && iNow>file_mtime(g.argv[i], ExtFILE) && (nMin<=0 || aLastRun[i]+nMin>iNow) ){ continue; /* Not yet time to run this one */ } blob_init(&cmd, 0, 0); blob_append_escaped_arg(&cmd, g.nameOfExe, 1); blob_append(&cmd, " backoffice --nodelay", -1); if( g.fAnyTrace ){ blob_append(&cmd, " --trace", -1); } if( bDebug ){ blob_append(&cmd, " --debug", -1); } if( nPoll>0 ){ blob_append(&cmd, " --nolease", -1); } if( backofficeLogfile ){ blob_append(&cmd, " --logfile", -1); blob_append_escaped_arg(&cmd, backofficeLogfile, 1); } blob_append_escaped_arg(&cmd, g.argv[i], 1); nCmd++; if( bDebug ){ fossil_print("COMMAND[%u]: %s\n", nCmd, blob_str(&cmd)); } fossil_system(blob_str(&cmd)); aLastRun[i] = iNext; blob_reset(&cmd); } if( nPoll<1 ) break; iNow = iNext; ix = time(0); if( ix < iNow+nPoll ){ sqlite3_int64 nMS = (iNow + nPoll - ix)*1000; if( bDebug )fossil_print("SLEEP: %lld\n", nMS); sqlite3_sleep((int)nMS); } } }else{ /* Not polling and only one repository named. Backoffice is run ** once by this process, which then exits */ if( g.argc==3 ){ g.zRepositoryOption = g.argv[2]; g.argc--; } db_find_and_open_repository(0,0); if( bDebug ){ backofficeLogDetail = 1; } if( bNoLease ){ backoffice_work(); }else{ backoffice_thread(); } } } /* ** This is the main interface to backoffice from the rest of the system. ** This routine launches either backoffice_thread() directly or as a ** subprocess. */ void backoffice_run_if_needed(void){ if( backofficeDb==0 ) return; if( strcmp(backofficeDb,"x")==0 ) return; if( g.db ) return; if( g.repositoryOpen ) return; #if defined(_WIN32) { int i; intptr_t x; char *argv[4]; wchar_t *ax[5]; argv[0] = g.nameOfExe; argv[1] = "backoffice"; argv[2] = "-R"; argv[3] = backofficeDb; ax[4] = 0; for(i=0; i<=3; i++) ax[i] = fossil_utf8_to_unicode(argv[i]); x = _wspawnv(_P_NOWAIT, ax[0], (const wchar_t * const *)ax); for(i=0; i<=3; i++) fossil_unicode_free(ax[i]); backofficeTrace( "/***** Subprocess %d creates backoffice child %lu *****/\n", GETPID(), GetProcessId((HANDLE)x)); if( x>=0 ) return; } #else /* unix */ { pid_t pid = fork(); if( pid>0 ){ /* This is the parent in a successful fork(). Return immediately. */ backofficeTrace( "/***** Subprocess %d creates backoffice child %d *****/\n", GETPID(), (int)pid); return; } if( pid==0 ){ /* This is the child of a successful fork(). Run backoffice. */ int i; setsid(); for(i=0; i<=2; i++){ close(i); open("/dev/null", O_RDWR); } for(i=3; i<100; i++){ close(i); } g.fDebug = 0; g.httpIn = 0; g.httpOut = 0; db_open_repository(backofficeDb); backofficeDb = "x"; backoffice_thread(); db_close(1); backofficeTrace("/***** Backoffice Child %d exits *****/\n", GETPID()); exit(0); } fossil_warning("backoffice process %d fork failed, errno %d", GETPID(), errno); } #endif /* Fork() failed or is unavailable. Run backoffice in this process, but ** do so with the no-delay setting. */ backofficeNoDelay = 1; db_open_repository(backofficeDb); backofficeDb = "x"; backoffice_thread(); db_close(1); } |
Changes to src/bag.c.
︙ | ︙ | |||
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 | */ struct Bag { int cnt; /* Number of integers in the bag */ int sz; /* Number of slots in a[] */ int used; /* Number of used slots in a[] */ int *a; /* Hash table of integers that are in the bag */ }; #endif /* ** Initialize a Bag structure */ void bag_init(Bag *p){ memset(p, 0, sizeof(*p)); } /* ** Destroy a Bag. Delete all of its content. */ void bag_clear(Bag *p){ free(p->a); bag_init(p); } /* ** The hash function */ | > > > > > > | | | | | 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 | */ struct Bag { int cnt; /* Number of integers in the bag */ int sz; /* Number of slots in a[] */ int used; /* Number of used slots in a[] */ int *a; /* Hash table of integers that are in the bag */ }; /* ** An expression for statically initializing a Bag instance, to be ** assigned to Bag instances at their declaration point. It has ** the same effect as passing the Bag to bag_init(). */ #define Bag_INIT {0,0,0,0} #endif /* ** Initialize a Bag structure */ void bag_init(Bag *p){ memset(p, 0, sizeof(*p)); } /* ** Destroy a Bag. Delete all of its content. */ void bag_clear(Bag *p){ free(p->a); bag_init(p); } /* ** The hash function */ #define bag_hash(i) (((u64)(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 */ old = *p; assert( newSize>old.cnt ); p->a = fossil_malloc( sizeof(p->a[0])*newSize ); p->sz = newSize; memset(p->a, 0, sizeof(p->a[0])*newSize ); for(i=0; i<old.sz; i++){ int e = old.a[i]; if( e>0 ){ unsigned h = bag_hash(e)%newSize; while( p->a[h] ){ h++; if( (int)h==newSize ) h = 0; } p->a[h] = e; nLive++; }else if( e<0 ){ nDel++; } } |
︙ | ︙ | |||
123 124 125 126 127 128 129 | if( p->used+1 >= p->sz/2 ){ int n = p->sz*2; bag_resize(p, n + 20 ); } h = bag_hash(e)%p->sz; while( p->a[h]>0 && p->a[h]!=e ){ h++; | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | if( p->used+1 >= p->sz/2 ){ int n = p->sz*2; bag_resize(p, n + 20 ); } h = bag_hash(e)%p->sz; while( p->a[h]>0 && p->a[h]!=e ){ h++; if( (int)h>=p->sz ) h = 0; } if( p->a[h]<=0 ){ if( p->a[h]==0 ) p->used++; p->a[h] = e; p->cnt++; rc = 1; } |
︙ | ︙ | |||
146 147 148 149 150 151 152 | assert( e>0 ); if( p->sz==0 ){ return 0; } h = bag_hash(e)%p->sz; while( p->a[h] && p->a[h]!=e ){ h++; | | | | 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 | assert( e>0 ); if( p->sz==0 ){ return 0; } h = bag_hash(e)%p->sz; while( p->a[h] && p->a[h]!=e ){ h++; if( (int)h>=p->sz ) h = 0; } return p->a[h]==e; } /* ** Remove element e from the bag if it exists in the bag. ** If e is not in the bag, this is a no-op. */ void bag_remove(Bag *p, int e){ unsigned h; assert( e>0 ); if( p->sz==0 ) return; h = bag_hash(e)%p->sz; while( p->a[h] && p->a[h]!=e ){ h++; if( (int)h>=p->sz ) h = 0; } if( p->a[h] ){ int nx = h+1; if( nx>=p->sz ) nx = 0; if( p->a[nx]==0 ){ p->a[h] = 0; p->used--; |
︙ | ︙ | |||
209 210 211 212 213 214 215 | int bag_next(Bag *p, int e){ unsigned h; assert( p->sz>0 ); assert( e>0 ); h = bag_hash(e)%p->sz; while( p->a[h] && p->a[h]!=e ){ h++; | | | | | 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | int bag_next(Bag *p, int e){ unsigned h; assert( p->sz>0 ); assert( e>0 ); h = bag_hash(e)%p->sz; while( p->a[h] && p->a[h]!=e ){ h++; if( (int)h>=p->sz ) h = 0; } assert( p->a[h] ); h++; while( (int)h<p->sz && p->a[h]<=0 ){ h++; } return (int)h<p->sz ? p->a[h] : 0; } /* ** Return the number of elements in the bag. */ int bag_count(Bag *p){ return p->cnt; } |
Added src/bisect.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 | /* ** Copyright (c) 2010 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 contains code used to implement the "bisect" command. ** ** This file also contains logic used to compute the closure of filename ** changes that have occurred across multiple check-ins. */ #include "config.h" #include "bisect.h" #include <assert.h> /* ** Local variables for this module */ static struct { int bad; /* The bad version */ int good; /* The good version */ } bisect; /* ** Find the shortest path between bad and good. */ void bisect_path(void){ PathNode *p; bisect.bad = db_lget_int("bisect-bad", 0); bisect.good = db_lget_int("bisect-good", 0); if( bisect.good>0 && bisect.bad==0 ){ path_shortest(bisect.good, bisect.good, 0, 0, 0); }else if( bisect.bad>0 && bisect.good==0 ){ path_shortest(bisect.bad, bisect.bad, 0, 0, 0); }else if( bisect.bad==0 && bisect.good==0 ){ fossil_fatal("neither \"good\" nor \"bad\" versions have been identified"); }else{ Bag skip; int bDirect = bisect_option("direct-only"); char *zLog = db_lget("bisect-log",""); Blob log, id; bag_init(&skip); blob_init(&log, zLog, -1); while( blob_token(&log, &id) ){ if( blob_str(&id)[0]=='s' ){ bag_insert(&skip, atoi(blob_str(&id)+1)); } } blob_reset(&log); p = path_shortest(bisect.good, bisect.bad, bDirect, 0, &skip); bag_clear(&skip); if( p==0 ){ char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad); char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good); fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", zGood, zBad); } } } /* ** The set of all bisect options. */ static const struct { const char *zName; const char *zDefault; const char *zDesc; } aBisectOption[] = { { "auto-next", "on", "Automatically run \"bisect next\" after each " "\"bisect good\", \"bisect bad\", or \"bisect " "skip\"" }, { "direct-only", "on", "Follow only primary parent-child links, not " "merges\n" }, { "display", "chart", "Command to run after \"next\". \"chart\", " "\"log\", \"status\", or \"none\"" }, { "linear", "off", "Do a linear scan rather than a true bisect, " "stopping at the first \"bad\" result"}, }; /* ** Return the value of a boolean bisect option. */ int bisect_option(const char *zName){ unsigned int i; int r = -1; for(i=0; i<count(aBisectOption); i++){ if( fossil_strcmp(zName, aBisectOption[i].zName)==0 ){ char *zLabel = mprintf("bisect-%s", zName); char *z; if( g.localOpen ){ z = db_lget(zLabel, (char*)aBisectOption[i].zDefault); }else{ z = (char*)aBisectOption[i].zDefault; } if( is_truth(z) ) r = 1; if( is_false(z) ) r = 0; if( r<0 ) r = is_truth(aBisectOption[i].zDefault); free(zLabel); break; } } assert( r>=0 ); return r; } /* ** List a bisect path. */ static void bisect_list(int abbreviated){ PathNode *p; int vid = db_lget_int("checkout", 0); int n; Stmt s; int nStep; int nHidden = 0; bisect_path(); db_prepare(&s, "SELECT blob.uuid, datetime(event.mtime) " " FROM blob, event" " WHERE blob.rid=:rid AND event.objid=:rid" " AND event.type='ci'"); nStep = path_length(); if( abbreviated ){ for(p=path_last(); p; p=p->pFrom) p->isHidden = 1; for(p=path_last(), n=0; p; p=p->pFrom, n++){ if( p->rid==bisect.good || p->rid==bisect.bad || p->rid==vid || (nStep>1 && n==nStep/2) ){ p->isHidden = 0; if( p->pFrom ) p->pFrom->isHidden = 0; } } for(p=path_last(); p; p=p->pFrom){ if( p->pFrom && p->pFrom->isHidden==0 ) p->isHidden = 0; } } for(p=path_last(), n=0; p; p=p->pFrom, n++){ if( p->isHidden && (nHidden || (p->pFrom && p->pFrom->isHidden)) ){ nHidden++; continue; }else if( nHidden ){ fossil_print(" ... %d other check-ins omitted\n", nHidden); nHidden = 0; } db_bind_int(&s, ":rid", p->rid); if( db_step(&s)==SQLITE_ROW ){ const char *zUuid = db_column_text(&s, 0); const char *zDate = db_column_text(&s, 1); fossil_print("%s %S", zDate, zUuid); if( p->rid==bisect.good ) fossil_print(" GOOD"); if( p->rid==bisect.bad ) fossil_print(" BAD"); if( p->rid==vid ) fossil_print(" CURRENT"); if( nStep>1 && n==nStep/2 ) fossil_print(" NEXT"); fossil_print("\n"); } db_reset(&s); } db_finalize(&s); } /* ** Append a new entry to the bisect log. Update the bisect-good or ** bisect-bad values as appropriate. ** ** The bisect-log consists of a list of token. Each token is an ** integer RID of a check-in. The RID is negative for "bad" check-ins ** and positive for "good" check-ins. */ static void bisect_append_log(int rid){ if( rid<0 ){ if( db_lget_int("bisect-bad",0)==(-rid) ) return; db_lset_int("bisect-bad", -rid); }else{ if( db_lget_int("bisect-good",0)==rid ) return; db_lset_int("bisect-good", rid); } db_multi_exec( "REPLACE INTO vvar(name,value) VALUES('bisect-log'," "COALESCE((SELECT value||' ' FROM vvar WHERE name='bisect-log'),'')" " || '%d')", rid); } /* ** Append a new skip entry to the bisect log. */ static void bisect_append_skip(int rid){ db_multi_exec( "UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid ); } /* ** Create a TEMP table named "bilog" that contains the complete history ** of the current bisect. ** ** If iCurrent>0 then it is the RID of the current check-out and is included ** in the history table. ** ** If zDesc is not NULL, then it is the bid= query parameter to /timeline ** that describes a bisect. Use the information in zDesc rather than in ** the bisect-log variable. ** ** If bDetail is true, then also include information about every node ** in between the inner-most GOOD and BAD nodes. */ int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){ char *zLog; Blob log, id; Stmt q; int cnt = 0; int lastGood = -1; int lastBad = -1; if( zDesc!=0 ){ blob_init(&log, 0, 0); while( zDesc[0]=='y' || zDesc[0]=='n' || zDesc[0]=='s' ){ int i; char c; int rid; if( blob_size(&log) ) blob_append(&log, " ", 1); if( zDesc[0]=='n' ) blob_append(&log, "-", 1); if( zDesc[0]=='s' ) blob_append(&log, "s", 1); for(i=1; ((c = zDesc[i])>='0' && c<='9') || (c>='a' && c<='f'); i++){} if( i==1 ) break; rid = db_int(0, "SELECT rid FROM blob" " WHERE uuid LIKE '%.*q%%'" " AND EXISTS(SELECT 1 FROM plink WHERE cid=rid)", i-1, zDesc+1 ); if( rid==0 ) break; blob_appendf(&log, "%d", rid); zDesc += i; while( zDesc[0]=='-' ) zDesc++; } }else{ zLog = db_lget("bisect-log",""); blob_init(&log, zLog, -1); } db_multi_exec( "CREATE TEMP TABLE bilog(" " rid INTEGER PRIMARY KEY," /* Sequence of events */ " stat TEXT," /* Type of occurrence */ " seq INTEGER UNIQUE" /* Check-in number */ ");" ); db_prepare(&q, "INSERT OR IGNORE INTO bilog(seq,stat,rid)" " VALUES(:seq,:stat,:rid)"); while( blob_token(&log, &id) ){ int rid; db_bind_int(&q, ":seq", ++cnt); if( blob_str(&id)[0]=='s' ){ rid = atoi(blob_str(&id)+1); db_bind_text(&q, ":stat", "SKIP"); db_bind_int(&q, ":rid", rid); }else{ rid = atoi(blob_str(&id)); if( rid>0 ){ db_bind_text(&q, ":stat","GOOD"); db_bind_int(&q, ":rid", rid); lastGood = rid; }else{ db_bind_text(&q, ":stat", "BAD"); db_bind_int(&q, ":rid", -rid); lastBad = -rid; } } db_step(&q); db_reset(&q); } if( iCurrent>0 ){ db_bind_int(&q, ":seq", ++cnt); db_bind_text(&q, ":stat", "CURRENT"); db_bind_int(&q, ":rid", iCurrent); db_step(&q); db_reset(&q); } if( bDetail && lastGood>0 && lastBad>0 ){ PathNode *p; p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0); while( p ){ db_bind_null(&q, ":seq"); db_bind_null(&q, ":stat"); db_bind_int(&q, ":rid", p->rid); db_step(&q); db_reset(&q); p = p->u.pTo; } path_reset(); } db_finalize(&q); return 1; } /* Return a permalink description of a bisect. Space is obtained from ** fossil_malloc() and should be freed by the caller. ** ** A bisect description consists of characters 'y' and 'n' and lowercase ** hex digits. Each term begins with 'y' or 'n' (success or failure) and ** is followed by a hash prefix in lowercase hex. */ char *bisect_permalink(void){ char *zLog = db_lget("bisect-log",""); char *zResult; Blob log; Blob link = BLOB_INITIALIZER; Blob id; blob_init(&log, zLog, -1); while( blob_token(&log, &id) ){ const char *zUuid; int rid; char cPrefix = 'y'; if( blob_str(&id)[0]=='s' ){ rid = atoi(blob_str(&id)+1); cPrefix = 's'; }else{ rid = atoi(blob_str(&id)); if( rid<0 ){ cPrefix = 'n'; rid = -rid; } } zUuid = db_text(0,"SELECT lower(uuid) FROM blob WHERE rid=%d", rid); if( blob_size(&link)>0 ) blob_append(&link, "-", 1); blob_appendf(&link, "%c%.10s", cPrefix, zUuid); } zResult = mprintf("%s", blob_str(&link)); blob_reset(&link); blob_reset(&log); blob_reset(&id); return zResult; } /* ** Show a chart of bisect "good" and "bad" versions. The chart can be ** sorted either chronologically by bisect time, or by check-in time. */ static void bisect_chart(int sortByCkinTime){ Stmt q; int iCurrent = db_lget_int("checkout",0); bisect_create_bilog_table(iCurrent, 0, 0); db_prepare(&q, "SELECT bilog.seq, bilog.stat," " substr(blob.uuid,1,16), datetime(event.mtime)," " blob.rid==%d" " FROM bilog, blob, event" " WHERE blob.rid=bilog.rid AND event.objid=bilog.rid" " AND event.type='ci'" " ORDER BY %s bilog.rowid ASC", iCurrent, (sortByCkinTime ? "event.mtime DESC, " : "") ); while( db_step(&q)==SQLITE_ROW ){ const char *zGoodBad = db_column_text(&q, 1); fossil_print("%3d %-7s %s %s%s\n", db_column_int(&q, 0), zGoodBad, db_column_text(&q, 3), db_column_text(&q, 2), (db_column_int(&q, 4) && zGoodBad[0]!='C') ? " CURRENT" : ""); } db_finalize(&q); } /* ** Reset the bisect subsystem. */ void bisect_reset(void){ db_multi_exec( "DELETE FROM vvar WHERE name IN " " ('bisect-good', 'bisect-bad', 'bisect-log', 'bisect-complete'," " 'bisect-linear')" ); } /* ** fossil bisect run [OPTIONS] COMMAND ** ** Invoke COMMAND (with arguments) repeatedly to perform the bisect. ** ** Options: ** -i|--interactive Prompt user for decisions rather than ** using the return code from COMMAND */ static void bisect_run(void){ const char *zCmd; int isInteractive = 0; int i; if( g.argc<4 ){ fossil_fatal("Usage: fossil bisect run [OPTIONS] COMMAND\n"); } for(i=3; i<g.argc-1; i++){ const char *zArg = g.argv[i]; if( zArg[0]=='-' && zArg[1]=='-' && zArg[2]!=0 ) zArg++; if( strcmp(zArg, "-i")==0 || strcmp(zArg, "-interactive")==0 ){ isInteractive = 1; continue; } fossil_fatal("unknown command-line option: \"%s\"\n", g.argv[i]); } zCmd = g.argv[i]; if( db_int(0, "SELECT count(*) FROM vvar" " WHERE name IN ('bisect-good','bisect-bad')")!=2 ){ fossil_fatal("need good/bad boundaries to use \"fossil bisect run\""); } while( db_lget_int("bisect-complete",0)==0 ){ int rc; Blob cmd; blob_init(&cmd, 0, 0); blob_append_escaped_arg(&cmd, g.nameOfExe, 1); rc = fossil_unsafe_system(zCmd); if( isInteractive ){ Blob in; fossil_print("test-command result: %d\n", rc); while(1){ int n; char *z; prompt_user("Enter (g)ood, (b)ad, (s)kip, (a)uto, (h)alt: ", &in); n = blob_size(&in); z = blob_str(&in); if( n<1 ) continue; if( sqlite3_strnicmp("good", z, n)==0 ){ rc = 0; break; } if( sqlite3_strnicmp("bad", z, n)==0 ){ rc = 1; break; } if( sqlite3_strnicmp("skip", z, n)==0 ){ rc = 125; break; } if( sqlite3_strnicmp("auto", z, n)==0 ){ isInteractive = 0; break; } if( sqlite3_strnicmp("halt", z, n)==0 ){ return; } blob_reset(&in); } } if( rc==0 ){ blob_append(&cmd, " bisect good", -1); }else if( rc==125 ){ blob_append(&cmd, " bisect skip", -1); }else{ blob_append(&cmd, " bisect bad", -1); } fossil_print("%s\n", blob_str(&cmd)); fossil_system(blob_str(&cmd)); blob_reset(&cmd); } } /* ** COMMAND: bisect ** ** Usage: %fossil bisect SUBCOMMAND ... ** ** Run various subcommands useful for searching back through the change ** history for a particular check-in that causes or fixes a problem. ** ** > fossil bisect bad ?VERSION? ** ** Identify version VERSION as non-working. If VERSION is omitted, ** the current check-out is marked as non-working. ** ** > fossil bisect good ?VERSION? ** ** Identify version VERSION as working. If VERSION is omitted, ** the current check-out is marked as working. ** ** > fossil bisect log ** > fossil bisect chart ** ** Show a log of "good", "bad", and "skip" versions. "bisect log" ** shows the events in the order that they were tested. ** "bisect chart" shows them in order of check-in. ** ** > fossil bisect next ** ** Update to the next version that is halfway between the working and ** non-working versions. ** ** > fossil bisect options ?NAME? ?VALUE? ** ** List all bisect options, or the value of a single option, or set the ** value of a bisect option. ** ** > fossil bisect reset ** ** Reinitialize a bisect session. This cancels prior bisect history ** and allows a bisect session to start over from the beginning. ** ** > fossil bisect run [OPTIONS] COMMAND ** ** Invoke COMMAND repeatedly to run the bisect. The exit code for ** COMMAND should be 0 for "good", 125 for "skip", and any other value ** for "bad". ** ** Options: ** -i|--interactive Prompt the user for the good/bad/skip decision ** after each step, rather than using the exit ** code from COMMAND ** ** > fossil bisect skip ?VERSION? ** ** Cause VERSION (or the current check-out if VERSION is omitted) to ** be ignored for the purpose of the current bisect. This might ** be done, for example, because VERSION does not compile correctly ** or is otherwise unsuitable to participate in this bisect. ** ** > fossil bisect vlist|ls|status ?-a|--all? ** ** List the versions in between the inner-most "bad" and "good". ** ** > fossil bisect ui ** ** Like "fossil ui" except start on a timeline that shows only the ** check-ins that are part of the current bisect. ** ** > fossil bisect undo ** ** Undo the most recent "good", "bad", or "skip" command. */ void bisect_cmd(void){ int n; const char *zCmd; int foundCmd = 0; db_must_be_within_tree(); if( g.argc<3 ){ goto usage; } zCmd = g.argv[2]; n = strlen(zCmd); if( n==0 ) zCmd = "-"; if( strncmp(zCmd, "bad", n)==0 ){ int ridBad; foundCmd = 1; if( g.argc==3 ){ ridBad = db_lget_int("checkout",0); }else{ ridBad = name_to_typed_rid(g.argv[3], "ci"); } if( ridBad>0 ){ bisect_append_log(-ridBad); if( bisect_option("auto-next") && db_lget_int("bisect-good",0)>0 ){ zCmd = "next"; n = 4; } } }else if( strncmp(zCmd, "good", n)==0 ){ int ridGood; foundCmd = 1; if( g.argc==3 ){ ridGood = db_lget_int("checkout",0); }else{ ridGood = name_to_typed_rid(g.argv[3], "ci"); } if( ridGood>0 ){ bisect_append_log(ridGood); if( bisect_option("auto-next") && db_lget_int("bisect-bad",0)>0 ){ zCmd = "next"; n = 4; } } }else if( strncmp(zCmd, "skip", n)==0 ){ int ridSkip; foundCmd = 1; if( g.argc==3 ){ ridSkip = db_lget_int("checkout",0); }else{ ridSkip = name_to_typed_rid(g.argv[3], "ci"); } if( ridSkip>0 ){ bisect_append_skip(ridSkip); if( bisect_option("auto-next") && db_lget_int("bisect-bad",0)>0 && db_lget_int("bisect-good",0)>0 ){ zCmd = "next"; n = 4; } } }else if( strncmp(zCmd, "undo", n)==0 ){ char *zLog; Blob log, id; int ridBad = 0; int ridGood = 0; int cnt = 0, i; foundCmd = 1; db_begin_transaction(); zLog = db_lget("bisect-log",""); blob_init(&log, zLog, -1); while( blob_token(&log, &id) ){ cnt++; } if( cnt==0 ){ fossil_fatal("no previous bisect steps to undo"); } blob_rewind(&log); for(i=0; i<cnt-1; i++){ int rid; blob_token(&log, &id); rid = atoi(blob_str(&id)); if( rid<0 ) ridBad = -rid; else ridGood = rid; } db_multi_exec( "UPDATE vvar SET value=substr(value,1,%d) WHERE name='bisect-log'", log.iCursor-1 ); db_lset_int("bisect-bad", ridBad); db_lset_int("bisect-good", ridGood); db_end_transaction(0); if( ridBad && ridGood ){ zCmd = "next"; n = 4; } } /* No else here so that the above commands can morph themselves into ** a "next" command */ if( strncmp(zCmd, "next", n)==0 ){ PathNode *pMid; char *zDisplay = db_lget("bisect-display","chart"); int m = (int)strlen(zDisplay); bisect_path(); if( db_lget_boolean("bisect-linear",0) ){ pMid = path_next(); if( pMid && pMid->rid==db_lget_int("checkout",0) ) pMid = 0; }else{ pMid = path_midpoint(); } if( pMid==0 ){ fossil_print("bisect complete\n"); db_lset_int("bisect-complete",1); }else{ int nSpan = path_length_not_hidden(); int nStep = path_search_depth(); 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(); fossil_print("span: %d steps-remaining: %d\n", nSpan, nStep); } 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, "run", n)==0 ){ bisect_run(); }else if( strncmp(zCmd, "options", n)==0 ){ if( g.argc==3 ){ unsigned int i; for(i=0; i<count(aBisectOption); 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, get_comment_format()); } }else if( g.argc==4 || g.argc==5 ){ unsigned int i; n = strlen(g.argv[3]); for(i=0; i<count(aBisectOption); 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/*works-like:"bisect-%s"*/, g.argv[4]); } fossil_print("%s\n", db_lget(z, (char*)aBisectOption[i].zDefault)); fossil_free(z); break; } } if( i>=count(aBisectOption) ){ fossil_fatal("no such bisect option: %s", g.argv[3]); } }else{ usage("options ?NAME? ?VALUE?"); } }else if( strncmp(zCmd, "reset", n)==0 ){ bisect_reset(); }else if( strcmp(zCmd, "ui")==0 ){ char *newArgv[8]; newArgv[0] = g.argv[0]; newArgv[1] = "ui"; newArgv[2] = "--page"; newArgv[3] = "timeline?bisect"; newArgv[4] = 0; g.argv = newArgv; g.argc = 4; cmd_webserver(); }else if( strncmp(zCmd, "vlist", n)==0 || strncmp(zCmd, "ls", n)==0 || strncmp(zCmd, "status", n)==0 ){ int fAll = find_option("all", "a", 0)!=0; bisect_list(!fAll); }else if( !foundCmd ){ usage: usage("bad|good|log|chart|next|options|reset|run|skip|status|ui|undo"); } } |
Changes to src/blob.c.
1 2 3 4 5 6 | /* ** 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".) | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > > > > | < | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > > > > > > > > > > > > > > > > > | > | | | > | | | > > > > > > > > > > > > > > | > > > > > > > > > > | > | > > > > > > > > > | > > | > > > > > > > > > > | > > > | > > > > > > | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** 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/ ** ******************************************************************************* ** ** A Blob is a variable-length containers for arbitrary string ** or binary data. */ #include "config.h" #include <zlib.h> #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 */ #define blob_buffer(X) ((X)->aData) /* ** Number of elements that fits into the current blob's size */ #define blob_count(X,elType) (blob_size(X)/sizeof(elType)) /* ** Append blob contents to another */ #define blob_appendb(dest, src) \ blob_append((dest), blob_buffer(src), blob_size(src)) /* ** Append a string literal to a blob ** TODO: Consider renaming to blob_appendl() */ #define blob_append_literal(blob, literal) \ blob_append((blob), "" literal, (sizeof literal)-1) /* * The empty string in the second argument leads to a syntax error * when the macro is not used with a string literal. Unfortunately * the error is not overly explicit. */ /* ** TODO: Suggested for removal because the name seems misleading. */ #define blob_append_string blob_append_literal /* ** Seek whence parameter values */ #define BLOB_SEEK_SET 1 #define BLOB_SEEK_CUR 2 #endif /* INTERFACE */ /* ** Make sure a blob is initialized */ #define blob_is_init(x) \ assert((x)->xRealloc==blobReallocMalloc || (x)->xRealloc==blobReallocStatic) /* ** Make sure a blob does not contain malloced memory. ** ** This might fail if we are unlucky and x is uninitialized. For that ** reason it should only be used locally for debugging. Leave it turned ** off for production. */ #if 0 /* Enable for debugging only */ #define assert_blob_is_reset(x) assert(blob_is_reset(x)) #else #define assert_blob_is_reset(x) #endif /* ** We find that the built-in isspace() function does not work for ** some international character sets. So here is a substitute. */ int fossil_isspace(char c){ return c==' ' || (c<='\r' && c>='\t'); } /* ** Other replacements for ctype.h functions. */ int fossil_islower(char c){ return c>='a' && c<='z'; } int fossil_isupper(char c){ return c>='A' && c<='Z'; } int fossil_isdigit(char c){ return c>='0' && c<='9'; } int fossil_isxdigit(char c){ return (c>='0' && c<='9') || (c>='a' && c<='f'); } int fossil_tolower(char c){ return fossil_isupper(c) ? c - 'A' + 'a' : c; } int fossil_toupper(char c){ return fossil_islower(c) ? c - 'a' + 'A' : c; } int fossil_isalpha(char c){ return (c>='a' && c<='z') || (c>='A' && c<='Z'); } int fossil_isalnum(char c){ return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); } /* Return true if and only if the entire string consists of only ** alphanumeric characters. */ int fossil_no_strange_characters(const char *z){ while( z && (fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-') ) z++; return z[0]==0; } /* ** COMMAND: test-isspace ** ** Verify that the fossil_isspace() routine is working correctly by ** testing it on all possible inputs. */ void isspace_cmd(void){ int i; for(i=0; i<=255; i++){ if( i==' ' || i=='\n' || i=='\t' || i=='\v' || i=='\f' || i=='\r' ){ assert( fossil_isspace((char)i) ); }else{ assert( !fossil_isspace((char)i) ); } } fossil_print("All 256 characters OK\n"); } /* ** This routine is called if a blob operation fails because we ** have run out of memory. */ static void blob_panic(void){ static const char zErrMsg[] = "out of memory\n"; fputs(zErrMsg, stderr); fossil_exit(1); } /* ** Maximum size of a Blob's managed memory. This is ~2GB, largely for ** historical reasons. ** */ #define MAX_BLOB_SIZE 0x7fff0000 /* ** If n >= MAX_BLOB_SIZE, calls blob_panic(), ** else this is a no-op. */ static void blob_assert_safe_size(i64 n){ if( n>=(i64)MAX_BLOB_SIZE ){ blob_panic(); } } /* ** A reallocation function that assumes that aData came from malloc(). ** This function attempts to resize the buffer of the blob to hold ** newSize bytes. ** ** No attempt is made to recover from an out-of-memory error. ** If an OOM error occurs, an error message is printed on stderr ** and the program exits. */ 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+4000<pBlob->nAlloc ){ char *pNew; blob_assert_safe_size((i64)newSize); 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(). */ static void blobReallocStatic(Blob *pBlob, unsigned int newSize){ if( newSize==0 ){ *pBlob = empty_blob; }else{ char *pNew; blob_assert_safe_size((i64)newSize); pNew = fossil_malloc( newSize ); if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize; memcpy(pNew, pBlob->aData, pBlob->nUsed); pBlob->aData = pNew; pBlob->xRealloc = blobReallocMalloc; pBlob->nAlloc = newSize; } } /* ** Reset a blob to be an empty container. */ void blob_reset(Blob *pBlob){ blob_is_init(pBlob); pBlob->xRealloc(pBlob, 0); } /* ** Return true if the blob has been zeroed - in other words if it contains ** no malloced memory. This only works reliably if the blob has been ** initialized - it can return a false negative on an uninitialized blob. */ int blob_is_reset(Blob *pBlob){ if( pBlob==0 ) return 1; if( pBlob->nUsed ) return 0; if( pBlob->xRealloc==blobReallocMalloc && pBlob->nAlloc ) return 0; return 1; } /* ** Initialize a blob to a string or byte-array constant of a specified length. ** Any prior data in the blob is discarded. */ void blob_init(Blob *pBlob, const char *zData, int size){ assert_blob_is_reset(pBlob); 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. Or, if pBlob==NULL, send ** the text to standard output in terminal mode, or to standard CGI output ** in CGI mode. ** ** If nData<0 then output all of aData up to the first 0x00 byte. ** ** Use the blob_append() routine in all application code. The blob_append() ** routine is faster, but blob_append_full() handles all the corner cases. ** The blob_append() routine automatically calls blob_append_full() if ** necessary. */ static void blob_append_full(Blob *pBlob, const char *aData, int nData){ sqlite3_int64 nNew; /* assert( aData!=0 || nData==0 ); // omitted for speed */ /* blob_is_init(pBlob); // omitted for speed */ if( nData<0 ) nData = strlen(aData); if( nData==0 ) return; if( pBlob==0 ){ if( g.cgiOutput ){ pBlob = cgi_output_blob(); }else{ fossil_puts(aData, 0, nData); return; } } nNew = pBlob->nUsed; nNew += nData; if( nNew >= pBlob->nAlloc ){ nNew += pBlob->nAlloc; nNew += 100; blob_assert_safe_size(nNew); pBlob->xRealloc(pBlob, (unsigned)nNew); if( pBlob->nUsed + nData >= pBlob->nAlloc ){ blob_panic(); } } memcpy(&pBlob->aData[pBlob->nUsed], aData, nData); pBlob->nUsed += nData; pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */ } void blob_append(Blob *pBlob, const char *aData, int nData){ sqlite3_int64 nUsed; /* assert( aData!=0 || nData==0 ); // omitted for speed */ if( nData<=0 || pBlob==0 || pBlob->nUsed + nData >= pBlob->nAlloc ){ blob_append_full(pBlob, aData, nData); return; } nUsed = pBlob->nUsed; pBlob->nUsed += nData; pBlob->aData[pBlob->nUsed] = 0; memcpy(&pBlob->aData[nUsed], aData, nData); } /* ** Append a single character to the blob. If pBlob is zero then the ** character is written directly to stdout. */ void blob_append_char(Blob *pBlob, char c){ if( pBlob==0 || pBlob->nUsed+1 >= pBlob->nAlloc ){ blob_append_full(pBlob, &c, 1); }else{ pBlob->aData[pBlob->nUsed++] = c; } } /* ** Copy a blob. pTo is reinitialized to be a copy of pFrom. */ void blob_copy(Blob *pTo, Blob *pFrom){ blob_is_init(pFrom); blob_zero(pTo); blob_append(pTo, blob_buffer(pFrom), blob_size(pFrom)); } /* ** Append the second blob onto the end of the first blob and reset the ** second blob. If the first blob (pTo) is NULL, then the content ** of the second blob is written to stdout or to CGI depending on if the ** Fossil is running in terminal or CGI mode. */ void blob_append_xfer(Blob *pTo, Blob *pFrom){ blob_append(pTo, blob_buffer(pFrom), blob_size(pFrom)); blob_reset(pFrom); } /* ** Write into pOut, a string literal representation for the first n bytes ** of z[]. The string literal representation is compatible with C, TCL, ** and JSON. Double-quotes are added to both ends. Double-quote and ** backslash characters are escaped. */ void blob_append_tcl_literal(Blob *pOut, const char *z, int n){ int i; blob_append_char(pOut, '"'); for(i=0; i<n; i++){ char c = z[i]; switch( c ){ case '\r': c = 'r'; case '[': case ']': case '$': case '"': case '\\': blob_append_char(pOut, '\\'); default: blob_append_char(pOut, c); } } blob_append_char(pOut, '"'); } void blob_append_json_literal(Blob *pOut, const char *z, int n){ int i; blob_append_char(pOut, '"'); for(i=0; i<n; i++){ char c = z[i]; switch( c ){ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: c += '0' - 0x00; blob_append(pOut, "\\u000",5); break; case 0x0b: case 0x0e: case 0x0f: c += 'a' - 0x0a; blob_append(pOut, "\\u000",5); break; case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: c += '0' - 0x10; blob_append(pOut, "\\u001",5); break; case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: c += 'a' - 0x1a; blob_append(pOut, "\\u001",5); break; case '\b': c = 'b'; blob_append_char(pOut, '\\'); break; case '\t': c = 't'; blob_append_char(pOut, '\\'); break; case '\r': c = 'r'; blob_append_char(pOut, '\\'); break; case '\n': c = 'n'; blob_append_char(pOut, '\\'); break; case '\f': c = 'f'; blob_append_char(pOut, '\\'); break; case '"': blob_append_char(pOut, '\\'); break; case '\\': blob_append_char(pOut, '\\'); break; default: break; } blob_append_char(pOut, c); } blob_append_char(pOut, '"'); } /* ** Return a pointer to a null-terminated string for a blob. */ char *blob_str(Blob *p){ blob_is_init(p); if( p->nUsed==0 ){ blob_append_char(p, 0); /* NOTE: Changes nUsed. */ p->nUsed = 0; } if( p->nUsed<p->nAlloc ){ p->aData[p->nUsed] = 0; }else{ blob_materialize(p); } return p->aData; } /* ** Compute the string length of a Blob. If there are embedded ** nul characters, truncate the to blob at the first nul. */ int blob_strlen(Blob *p){ char *z = blob_str(p); if( z==0 ) return 0; p->nUsed = (int)strlen(p->aData); return p->nUsed; } /* ** 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_panic("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 |
︙ | ︙ | |||
262 263 264 265 266 267 268 | blob_is_init(p); if( p->nUsed==0 ) return ""; p->aData[p->nUsed] = 0; return p->aData; } /* | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > | | 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 | blob_is_init(p); if( p->nUsed==0 ) return ""; p->aData[p->nUsed] = 0; return p->aData; } /* ** Compare two blobs. Return negative, zero, or positive if the first ** blob is less then, equal to, or greater than the second. */ int blob_compare(const Blob *pA, const Blob *pB){ int szA, szB, sz, rc; blob_is_init(pA); blob_is_init(pB); szA = blob_size(pA); szB = blob_size(pB); sz = szA<szB ? szA : szB; rc = memcmp(blob_buffer(pA), blob_buffer(pB), sz); if( rc==0 ){ rc = szA - szB; } return rc; } /* ** Compare two blobs in constant time and return zero if they are equal. ** Constant time comparison only applies for blobs of the same length. ** If lengths are different, immediately returns 1. */ int blob_constant_time_cmp(Blob *pA, Blob *pB){ int szA, szB, i; unsigned char *buf1, *buf2; unsigned char rc = 0; blob_is_init(pA); blob_is_init(pB); szA = blob_size(pA); szB = blob_size(pB); if( szA!=szB || szA==0 ) return 1; buf1 = (unsigned char*)blob_buffer(pA); buf2 = (unsigned char*)blob_buffer(pB); for( i=0; i<szA; i++ ){ rc = rc | (buf1[i] ^ buf2[i]); } return rc; } /* ** Compare a blob to a string. Return TRUE if they are equal. */ int blob_eq_str(Blob *pBlob, const char *z, int n){ Blob t; blob_is_init(pBlob); if( n<=0 ) n = (int)strlen(z); t.aData = (char*)z; t.nUsed = n; t.xRealloc = blobReallocStatic; return blob_compare(pBlob, &t)==0; } /* ** This macro compares a blob against a string constant. We use the sizeof() ** operator on the string constant twice, so it really does need to be a ** string literal or character array - not a character pointer. */ #if INTERFACE # define blob_eq(B,S) \ ((B)->nUsed==sizeof(S"")-1 && memcmp((B)->aData,S,sizeof(S)-1)==0) #endif /* ** Attempt to resize a blob so that its internal buffer is ** nByte in size. The blob is truncated if necessary. */ void blob_resize(Blob *pBlob, unsigned int newSize){ pBlob->xRealloc(pBlob, newSize+1); pBlob->nUsed = newSize; pBlob->aData[newSize] = 0; } /* ** Ensures that the given blob has at least the given amount of memory ** allocated to it. Does not modify pBlob->nUsed nor will it reduce ** the currently-allocated amount of memory. ** ** For semantic compatibility with blob_append_full(), if newSize is ** >=MAX_BLOB_SIZE then this function will trigger blob_panic(). If it ** didn't, it would be possible to bypass that hard-coded limit via ** this function. ** ** We've had at least one report: ** https://fossil-scm.org/forum/forumpost/b7bbd28db4 ** which implies that this is unconditionally failing on mingw 32-bit ** builds. */ void blob_reserve(Blob *pBlob, unsigned int newSize){ blob_assert_safe_size( (i64)newSize ); if(newSize>pBlob->nAlloc){ pBlob->xRealloc(pBlob, newSize+1); pBlob->aData[newSize] = 0; } } /* ** Make sure a blob is nul-terminated and is not a pointer to unmanaged ** space. Return a pointer to the data. */ char *blob_materialize(Blob *pBlob){ blob_resize(pBlob, pBlob->nUsed); return pBlob->aData; } |
︙ | ︙ | |||
339 340 341 342 343 344 345 | ** Extract N bytes from blob pFrom and use it to initialize blob pTo. ** Return the actual number of bytes extracted. ** ** After this call completes, pTo will be an ephemeral blob. */ int blob_extract(Blob *pFrom, int N, Blob *pTo){ blob_is_init(pFrom); | | | 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 | ** Extract N bytes from blob pFrom and use it to initialize blob pTo. ** Return the actual number of bytes extracted. ** ** After this call completes, pTo will be an ephemeral blob. */ int blob_extract(Blob *pFrom, int N, Blob *pTo){ blob_is_init(pFrom); assert_blob_is_reset(pTo); if( pFrom->iCursor + N > pFrom->nUsed ){ N = pFrom->nUsed - pFrom->iCursor; if( N<=0 ){ blob_zero(pTo); return 0; } } |
︙ | ︙ | |||
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | /* ** Rewind the cursor on a blob back to the beginning. */ void blob_rewind(Blob *p){ p->iCursor = 0; } /* ** Seek the cursor in a blob to the indicated offset. */ int blob_seek(Blob *p, int offset, int whence){ if( whence==BLOB_SEEK_SET ){ p->iCursor = offset; }else if( whence==BLOB_SEEK_CUR ){ p->iCursor += offset; | > > > > > > > < < < < < | | 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 | /* ** Rewind the cursor on a blob back to the beginning. */ void blob_rewind(Blob *p){ p->iCursor = 0; } /* ** Truncate a blob back to zero length */ void blob_truncate(Blob *p, int sz){ if( sz>=0 && sz<(int)(p->nUsed) ) p->nUsed = sz; } /* ** Seek the cursor in a blob to the indicated offset. */ int blob_seek(Blob *p, int offset, int whence){ if( whence==BLOB_SEEK_SET ){ p->iCursor = offset; }else if( whence==BLOB_SEEK_CUR ){ p->iCursor += offset; } if( p->iCursor>p->nUsed ){ p->iCursor = p->nUsed; } return p->iCursor; } /* ** Return the current offset into the blob */ int blob_tell(Blob *p){ return p->iCursor; } /* ** Extract a single line of text from pFrom beginning at the current ** cursor location and use that line of text to initialize pTo. ** pTo will include the terminating \n. Return the number of bytes ** in the line including the \n at the end. 0 is returned at ** end-of-file. ** ** The cursor of pFrom is left pointing at the first byte past the ** \n that terminated the line. |
︙ | ︙ | |||
427 428 429 430 431 432 433 | ** ** All this does is reduce the length counter. This routine does ** not insert a new zero terminator. */ int blob_trim(Blob *p){ char *z = p->aData; int n = p->nUsed; | | | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 | ** ** All this does is reduce the length counter. This routine does ** not insert a new zero terminator. */ int blob_trim(Blob *p){ char *z = p->aData; int n = p->nUsed; while( n>0 && fossil_isspace(z[n-1]) ){ n--; } p->nUsed = n; return n; } /* ** Extract a single token from pFrom and use it to initialize pTo. ** Return the number of bytes in the token. If no token is found, |
︙ | ︙ | |||
450 451 452 453 454 455 456 | ** pTo will be an ephermeral blob. If pFrom changes, it might alter ** pTo as well. */ int blob_token(Blob *pFrom, Blob *pTo){ char *aData = pFrom->aData; int n = pFrom->nUsed; int i = pFrom->iCursor; | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | ** pTo will be an ephermeral blob. If pFrom changes, it might alter ** pTo as well. */ int blob_token(Blob *pFrom, Blob *pTo){ char *aData = pFrom->aData; int n = pFrom->nUsed; int i = pFrom->iCursor; while( i<n && fossil_isspace(aData[i]) ){ i++; } pFrom->iCursor = i; while( i<n && !fossil_isspace(aData[i]) ){ i++; } blob_extract(pFrom, i-pFrom->iCursor, pTo); while( i<n && fossil_isspace(aData[i]) ){ i++; } pFrom->iCursor = i; return pTo->nUsed; } /* ** Extract a single SQL token from pFrom and use it to initialize pTo. ** Return the number of bytes in the token. If no token is found, ** return 0. ** ** An SQL token consists of one or more non-space characters. If the ** first character is ' then the token is terminated by a matching ' ** (ignoring double '') or by the end of the string ** ** The cursor of pFrom is left pointing at the first character past ** the end of the token. ** ** pTo will be an ephermeral blob. If pFrom changes, it might alter ** pTo as well. */ int blob_sqltoken(Blob *pFrom, Blob *pTo){ char *aData = pFrom->aData; int n = pFrom->nUsed; int i = pFrom->iCursor; while( i<n && fossil_isspace(aData[i]) ){ i++; } pFrom->iCursor = i; if( aData[i]=='\'' ){ i++; while( i<n ){ if( aData[i]=='\'' ){ if( aData[++i]!='\'' ) break; } i++; } }else{ while( i<n && !fossil_isspace(aData[i]) ){ i++; } } blob_extract(pFrom, i-pFrom->iCursor, pTo); while( i<n && fossil_isspace(aData[i]) ){ i++; } pFrom->iCursor = i; return pTo->nUsed; } /* ** Extract everything from the current cursor to the end of the blob ** into a new blob. The new blob is an ephemerial reference to the |
︙ | ︙ | |||
502 503 504 505 506 507 508 | if( pTo ){ blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor); } pFrom->iCursor = i; } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | < | | > > > | > | > > > > > > > > > > > > > > > > > > > > > | 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 | if( pTo ){ blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor); } pFrom->iCursor = i; } /* ** Remove comment lines (starting with '#') from a blob pIn. ** Keep lines starting with "\#" but remove the initial backslash. ** ** Store the result in pOut. It is ok for pIn and pOut to be the same blob. ** ** pOut must either be the same as pIn or else uninitialized. */ void blob_strip_comment_lines(Blob *pIn, Blob *pOut){ char *z = pIn->aData; unsigned int i = 0; unsigned int n = pIn->nUsed; unsigned int lineStart = 0; unsigned int copyStart = 0; int doCopy = 1; Blob temp; blob_zero(&temp); while( i<n ){ if( i==lineStart && z[i]=='#' ){ copyStart = i; doCopy = 0; }else if( i==lineStart && z[i]=='\\' && z[i+1]=='#' ){ /* keep lines starting with an escaped '#' (and unescape it) */ copyStart = i + 1; } if( z[i]=='\n' ){ if( doCopy ) blob_append(&temp,&pIn->aData[copyStart], i - copyStart + 1); lineStart = copyStart = i + 1; doCopy = 1; } i++; } /* Last line */ if( doCopy ) blob_append(&temp, &pIn->aData[copyStart], i - copyStart); if( pOut==pIn ) blob_reset(pOut); *pOut = temp; } /* ** COMMAND: test-strip-comment-lines ** ** Usage: %fossil test-strip-comment-lines ?OPTIONS? INPUTFILE ** ** Read INPUTFILE and print it without comment lines (starting with '#'). ** Keep lines starting with "\\#" but remove the initial backslash. ** ** This is used to test and debug the blob_strip_comment_lines() routine. ** ** Options: ** -y|--side-by-side Show diff of INPUTFILE and output side-by-side ** -W|--width N Width of lines in side-by-side diff */ void test_strip_comment_lines_cmd(void){ Blob f, h; /* unitialized */ Blob out; DiffConfig dCfg; int sbs = 0; const char *z; int w = 0; memset(&dCfg, 0, sizeof(dCfg)); sbs = find_option("side-by-side","y",0)!=0; if( (z = find_option("width","W",1))!=0 && (w = atoi(z))>0 ){ dCfg.wColumn = w; } verify_all_options(); if( g.argc!=3 ) usage("INPUTFILE"); blob_read_from_file(&f, g.argv[2], ExtFILE); blob_strip_comment_lines(&f, &h); if ( !sbs ){ blob_write_to_file(&h, "-"); }else{ blob_zero(&out); dCfg.nContext = -1; /* whole content */ dCfg.diffFlags = DIFF_SIDEBYSIDE | DIFF_CONTEXT_EX | DIFF_STRIP_EOLCR; diff_begin(&dCfg); text_diff(&f, &h, &out, &dCfg); blob_write_to_file(&out, "-"); diff_end(&dCfg, 0); } } /* ** Ensure that the text in pBlob ends with '\n' */ void blob_add_final_newline(Blob *pBlob){ if( pBlob->nUsed<=0 ) return; if( pBlob->aData[pBlob->nUsed-1]!='\n' ){ blob_append_char(pBlob, '\n'); } } /* ** Return true if the blob contains a valid base16 identifier artifact hash. ** ** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the ** hash is valid. Both of these are non-zero and therefore "true". ** If the hash is not valid, then HNAME_ERROR is returned, which is zero or ** false. */ int blob_is_hname(Blob *pBlob){ return hname_validate(blob_buffer(pBlob), blob_size(pBlob)); } /* ** Return true if the blob contains a valid filename */ int blob_is_filename(Blob *pBlob){ return file_is_simple_pathname(blob_str(pBlob), 1); } /* ** Return true if the blob contains a valid 32-bit integer. Store ** the integer value in *pValue. */ int blob_is_int(Blob *pBlob, int *pValue){ const char *z = blob_buffer(pBlob); int i, n, c, v; n = blob_size(pBlob); v = 0; for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){ v = v*10 + c - '0'; } if( i==n ){ *pValue = v; return 1; }else{ return 0; } } /* ** Return true if the blob contains a valid 64-bit integer. Store ** the integer value in *pValue. */ int blob_is_int64(Blob *pBlob, sqlite3_int64 *pValue){ const char *z = blob_buffer(pBlob); int i, n, c; sqlite3_int64 v; n = blob_size(pBlob); v = 0; for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){ v = v*10 + c - '0'; } if( i==n ){ *pValue = v; return 1; }else{ return 0; |
︙ | ︙ | |||
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 | int i; for(i=0; i<n; i++) blob_zero(&aBlob[i]); } void blobarray_reset(Blob *aBlob, int n){ int i; for(i=0; i<n; i++) blob_reset(&aBlob[i]); } /* ** Parse a blob into space-separated tokens. Store each token in ** an element of the blobarray aToken[]. aToken[] is nToken elements in ** size. Return the number of tokens seen. */ int blob_tokenize(Blob *pIn, Blob *aToken, int nToken){ int i; for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){} return i; } /* | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | | 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 | int i; for(i=0; i<n; i++) blob_zero(&aBlob[i]); } void blobarray_reset(Blob *aBlob, int n){ int i; for(i=0; i<n; i++) blob_reset(&aBlob[i]); } /* ** Allocate array of n blobs and initialize each element with `empty_blob` */ Blob* blobarray_new(int n){ int i; Blob *aBlob = fossil_malloc(sizeof(Blob)*n); for(i=0; i<n; i++) aBlob[i] = empty_blob; return aBlob; } /* ** Free array of n blobs some of which may be empty (have NULL buffer) */ void blobarray_delete(Blob *aBlob, int n){ int i; for(i=0; i<n; i++){ if( blob_buffer(aBlob+i) ) blob_reset(aBlob+i); } fossil_free(aBlob); } /* ** Parse a blob into space-separated tokens. Store each token in ** an element of the blobarray aToken[]. aToken[] is nToken elements in ** size. Return the number of tokens seen. */ int blob_tokenize(Blob *pIn, Blob *aToken, int nToken){ 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. Or ** if pBlob==0, do printf-style string rendering directly to stdout. ** ** 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, ...){ va_list ap; va_start(ap, zFormat); vxprintf(pBlob, zFormat, ap); va_end(ap); if( pBlob ) pBlob->blobFlags |= BLOBFLAG_NotSQL; } void blob_append_sql(Blob *pBlob, const char *zFormat, ...){ 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){ vxprintf(pBlob, zFormat, ap); } /* ** Initialize a blob to the data on an input channel. Return ** the number of bytes read into the blob. Any prior content ** of the blob is discarded, not freed. */ int blob_read_from_channel(Blob *pBlob, FILE *in, int nToRead){ size_t n; blob_zero(pBlob); if( nToRead<0 ){ |
︙ | ︙ | |||
591 592 593 594 595 596 597 598 599 600 601 602 603 604 | }else{ blob_resize(pBlob, nToRead); n = fread(blob_buffer(pBlob), 1, nToRead, in); blob_resize(pBlob, n); } return blob_size(pBlob); } /* ** 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. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > | > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | > > > > | < > | > | < | | | < | | | < < < | < | < < < < < < < > | | | | < < < < < < < | > < < < | | | | | | | > | | | | > > > > > > | | | | | 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 | }else{ blob_resize(pBlob, nToRead); n = fread(blob_buffer(pBlob), 1, nToRead, in); blob_resize(pBlob, n); } return blob_size(pBlob); } /* ** Initialize a blob to the data read from HTTP input. Return ** the number of bytes read into the blob. Any prior content ** of the blob is discarded, not freed. */ int blob_read_from_cgi(Blob *pBlob, int nToRead){ size_t n; blob_zero(pBlob); if( nToRead<0 ){ char zBuf[10000]; while( !cgi_feof() ){ n = cgi_fread(zBuf, sizeof(zBuf)); if( n>0 ){ blob_append(pBlob, zBuf, n); } } }else{ blob_resize(pBlob, nToRead); n = cgi_fread(blob_buffer(pBlob), nToRead); blob_resize(pBlob, n); } return blob_size(pBlob); } /* ** Initialize a blob to be the content of a file. If the filename ** is blank or "-" then read from standard input. ** ** If zFilename is a symbolic link, behavior depends on the eFType ** parameter: ** ** * If eFType is ExtFILE or allow-symlinks is OFF, then the ** pBlob is initialized to the *content* of the object to which ** the zFilename symlink points. ** ** * If eFType is RepoFILE and allow-symlinks is ON, then the ** pBlob is initialized to the *name* of the object to which ** the zFilename symlink points. ** ** 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). */ sqlite3_int64 blob_read_from_file( Blob *pBlob, /* The blob to be initialized */ const char *zFilename, /* Extract content from this file */ int eFType /* ExtFILE or RepoFILE - see above */ ){ sqlite3_int64 size, got; FILE *in; if( zFilename==0 || zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ return blob_read_from_channel(pBlob, stdin, -1); } if( file_islink(zFilename) ){ return blob_read_link(pBlob, zFilename); } size = file_size(zFilename, eFType); blob_zero(pBlob); if( size<0 ){ fossil_fatal("no such file: %s", zFilename); } if( size==0 ){ return 0; } blob_resize(pBlob, size); in = fossil_fopen(zFilename, "rb"); if( in==0 ){ fossil_fatal("cannot open %s for reading", zFilename); } got = fread(blob_buffer(pBlob), 1, size, in); fclose(in); if( got<size ){ blob_resize(pBlob, got); } return got; } /* ** Reads symlink destination path and puts int into blob. ** Any prior content of the blob is discarded, not freed. ** ** Returns length of destination path. ** ** On windows, zeros blob and returns 0. */ int blob_read_link(Blob *pBlob, const char *zFilename){ #if !defined(_WIN32) char zBuf[1024]; ssize_t len = readlink(zFilename, zBuf, 1023); if( len < 0 ){ fossil_fatal("cannot read symbolic link %s", zFilename); } zBuf[len] = 0; /* null-terminate */ blob_zero(pBlob); blob_appendf(pBlob, "%s", zBuf); return len; #else blob_zero(pBlob); return 0; #endif } /* ** Write the content of a blob into a file. ** ** If the filename is blank or "-" then write to standard output. ** ** This routine always assumes ExtFILE. If zFilename is a symbolic link ** then the content is written into the object that symbolic link points ** to, not into the symbolic link itself. This is true regardless of ** the allow-symlinks setting. ** ** Return the number of bytes written. */ int blob_write_to_file(Blob *pBlob, const char *zFilename){ FILE *out; int nWrote; if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ blob_is_init(pBlob); #if defined(_WIN32) nWrote = fossil_utf8_to_console(blob_buffer(pBlob), blob_size(pBlob), 0); if( nWrote>=0 ) return nWrote; fflush(stdout); _setmode(_fileno(stdout), _O_BINARY); #endif nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), stdout); #if defined(_WIN32) fflush(stdout); _setmode(_fileno(stdout), _O_TEXT); #endif }else{ file_mkfolder(zFilename, ExtFILE, 1, 0); out = fossil_fopen(zFilename, "wb"); if( out==0 ){ #if defined(_WIN32) const char *zReserved = file_is_win_reserved(zFilename); if( zReserved ){ fossil_fatal("cannot open \"%s\" because \"%s\" is " "a reserved name on Windows", zFilename, zReserved); } #endif 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!=(int)blob_size(pBlob) ){ fossil_fatal_recursive("short write: %d of %d bytes to %s", nWrote, blob_size(pBlob), zFilename); } } return nWrote; } /* ** Compress a blob pIn. Store the result in pOut. It is ok for pIn and ** pOut to be the same blob. ** ** pOut must either be the same as pIn or else uninitialized. */ void blob_compress(Blob *pIn, Blob *pOut){ unsigned int nIn = blob_size(pIn); unsigned int nOut = 13 + nIn + (nIn+999)/1000; unsigned long int nOut2; unsigned char *outBuf; Blob temp; blob_zero(&temp); blob_resize(&temp, nOut+4); outBuf = (unsigned char*)blob_buffer(&temp); outBuf[0] = nIn>>24 & 0xff; outBuf[1] = nIn>>16 & 0xff; outBuf[2] = nIn>>8 & 0xff; outBuf[3] = nIn & 0xff; nOut2 = (long int)nOut; compress(&outBuf[4], &nOut2, (unsigned char*)blob_buffer(pIn), blob_size(pIn)); if( pOut==pIn ) blob_reset(pOut); assert_blob_is_reset(pOut); *pOut = temp; blob_resize(pOut, nOut2+4); } /* ** COMMAND: test-compress ** ** Usage: %fossil test-compress INPUTFILE OUTPUTFILE ** ** Run compression on INPUTFILE and write the result into OUTPUTFILE. ** ** This is used to test and debug the blob_compress() routine. */ void compress_cmd(void){ Blob f; if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE"); blob_read_from_file(&f, g.argv[2], ExtFILE); blob_compress(&f, &f); blob_write_to_file(&f, g.argv[3]); } /* ** Compress the concatenation of a blobs pIn1 and pIn2. Store the result ** in pOut. ** ** pOut must be either uninitialized or must be the same as either pIn1 or ** pIn2. */ void blob_compress2(Blob *pIn1, Blob *pIn2, Blob *pOut){ unsigned int nIn = blob_size(pIn1) + blob_size(pIn2); unsigned int nOut = 13 + nIn + (nIn+999)/1000; unsigned char *outBuf; |
︙ | ︙ | |||
770 771 772 773 774 775 776 | stream.next_in = (unsigned char*)blob_buffer(pIn2); deflate(&stream, 0); deflate(&stream, Z_FINISH); blob_resize(&temp, stream.total_out + 4); deflateEnd(&stream); if( pOut==pIn1 ) blob_reset(pOut); if( pOut==pIn2 ) blob_reset(pOut); | | > > > > > > > | | | 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 | stream.next_in = (unsigned char*)blob_buffer(pIn2); deflate(&stream, 0); deflate(&stream, Z_FINISH); blob_resize(&temp, stream.total_out + 4); deflateEnd(&stream); if( pOut==pIn1 ) blob_reset(pOut); if( pOut==pIn2 ) blob_reset(pOut); assert_blob_is_reset(pOut); *pOut = temp; } /* ** COMMAND: test-compress-2 ** ** Usage: %fossil test-compress-2 IN1 IN2 OUT ** ** Read files IN1 and IN2, concatenate the content, compress the ** content, then write results into OUT. ** ** This is used to test and debug the blob_compress2() routine. */ void compress2_cmd(void){ Blob f1, f2; if( g.argc!=5 ) usage("INPUTFILE1 INPUTFILE2 OUTPUTFILE"); blob_read_from_file(&f1, g.argv[2], ExtFILE); blob_read_from_file(&f2, g.argv[3], ExtFILE); blob_compress2(&f1, &f2, &f1); blob_write_to_file(&f1, g.argv[4]); } /* ** Uncompress blob pIn and store the result in pOut. It is ok for pIn and ** pOut to be the same blob. |
︙ | ︙ | |||
807 808 809 810 811 812 813 | return 0; } inBuf = (unsigned char*)blob_buffer(pIn); nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3]; blob_zero(&temp); blob_resize(&temp, nOut+1); nOut2 = (long int)nOut; | | | | > > > > > > | | | | < | < | > | < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | return 0; } inBuf = (unsigned char*)blob_buffer(pIn); nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3]; blob_zero(&temp); blob_resize(&temp, nOut+1); nOut2 = (long int)nOut; rc = uncompress((unsigned char*)blob_buffer(&temp), &nOut2, &inBuf[4], nIn - 4); if( rc!=Z_OK ){ blob_reset(&temp); return 1; } blob_resize(&temp, nOut2); if( pOut==pIn ) blob_reset(pOut); assert_blob_is_reset(pOut); *pOut = temp; return 0; } /* ** COMMAND: test-uncompress ** ** Usage: %fossil test-uncompress IN OUT ** ** Read the content of file IN, uncompress that content, and write the ** result into OUT. This command is intended for testing of the ** blob_compress() function. */ void uncompress_cmd(void){ Blob f; if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE"); blob_read_from_file(&f, g.argv[2], ExtFILE); blob_uncompress(&f, &f); blob_write_to_file(&f, g.argv[3]); } /* ** COMMAND: test-cycle-compress ** ** Compress and uncompress each file named on the command line. ** Verify that the original content is recovered. */ void test_cycle_compress(void){ int i; Blob b1, b2, b3; for(i=2; i<g.argc; i++){ blob_read_from_file(&b1, g.argv[i], ExtFILE); blob_compress(&b1, &b2); blob_uncompress(&b2, &b3); if( blob_compare(&b1, &b3) ){ fossil_fatal("compress/uncompress cycle failed for %s", g.argv[i]); } blob_reset(&b1); blob_reset(&b2); blob_reset(&b3); } fossil_print("ok\n"); } /* ** Convert every \n character in the given blob into \r\n. */ void blob_add_cr(Blob *p){ char *z = p->aData; int j = p->nUsed; int i, n; for(i=n=0; i<j; i++){ if( z[i]=='\n' ) n++; } j += n; if( j>=(int)(p->nAlloc) ){ blob_resize(p, j); z = p->aData; } p->nUsed = j; z[j] = 0; while( j>i ){ if( (z[--j] = z[--i]) =='\n' ){ z[--j] = '\r'; } } } /* ** Remove every \r character from the given blob, replacing each one with ** a \n character if it was not already part of a \r\n pair. */ void blob_to_lf_only(Blob *p){ int i, j; char *z = blob_materialize(p); 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>=(int)(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]; } } } /* ** ASCII (for reference): ** x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf ** 0x ^` ^a ^b ^c ^d ^e ^f ^g \b \t \n () \f \r ^n ^o ** 1x ^p ^q ^r ^s ^t ^u ^v ^w ^x ^y ^z ^{ ^| ^} ^~ ^ ** 2x () ! " # $ % & ' ( ) * + , - . / ** 3x 0 1 2 3 4 5 6 7 8 9 : ; < = > ? ** 4x @ A B C D E F G H I J K L M N O ** 5x P Q R S T U V W X Y Z [ \ ] ^ _ ** 6x ` a b c d e f g h i j k l m n o ** 7x p q r s t u v w x y z { | } ~ ^_ */ /* ** Meanings for bytes in a filename: ** ** 0 Ordinary character. No encoding required ** 1 Needs to be escaped ** 2 Illegal character. Do not allow in a filename ** 3 First byte of a 2-byte UTF-8 ** 4 First byte of a 3-byte UTF-8 ** 5 First byte of a 4-byte UTF-8 */ static const char aSafeChar[256] = { #ifdef _WIN32 /* Windows ** Prohibit: all control characters, including tab, \r and \n. ** Escape: (space) " # $ % & ' ( ) * ; < > ? [ ] ^ ` { | } */ /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 1x */ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 2x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, /* 3x */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, /* 5x */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 7x */ #else /* Unix ** Prohibit: all control characters, including tab, \r and \n ** Escape: (space) ! " # $ % & ' ( ) * ; < > ? [ \ ] ^ ` { | } */ /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 1x */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 2x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, /* 3x */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 5x */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 7x */ #endif /* all bytes 0x80 through 0xbf are unescaped, being secondary ** bytes to UTF8 characters. Bytes 0xc0 through 0xff are the ** first byte of a UTF8 character and do get escaped */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* bx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* cx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* dx */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* ex */ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 /* fx */ }; /* ** pBlob is a shell command under construction. This routine safely ** appends filename argument zIn. ** ** The argument is escaped if it contains white space or other characters ** that need to be escaped for the shell. If zIn contains characters ** that cannot be safely escaped, then throw a fatal error. ** ** If the isFilename argument is true, then the argument is expected ** to be a filename. As shell commands commonly have command-line ** options that begin with "-" and since we do not want an attacker ** to be able to invoke these switches using filenames that begin ** with "-", if zIn begins with "-", prepend an additional "./" ** (or ".\\" on Windows). */ void blob_append_escaped_arg(Blob *pBlob, const char *zIn, int isFilename){ int i; unsigned char c; int needEscape = 0; int n = blob_size(pBlob); char *z = blob_buffer(pBlob); /* Look for illegal byte-sequences and byte-sequences that require ** escaping. No control-characters are allowed. All spaces and ** non-ASCII unicode characters and some punctuation characters require ** escaping. */ for(i=0; (c = (unsigned char)zIn[i])!=0; i++){ if( aSafeChar[c] ){ unsigned char x = aSafeChar[c]; needEscape = 1; if( x==2 ){ Blob bad; blob_token(pBlob, &bad); fossil_fatal("the [%s] argument to the \"%s\" command contains " "a character (ascii 0x%02x) that is not allowed in " "filename arguments", zIn, blob_str(&bad), c); }else if( x>2 ){ if( (zIn[i+1]&0xc0)!=0x80 || (x>=4 && (zIn[i+2]&0xc0)!=0x80) || (x==5 && (zIn[i+3]&0xc0)!=0x80) ){ Blob bad; blob_token(pBlob, &bad); fossil_fatal("the [%s] argument to the \"%s\" command contains " "an illegal UTF-8 character", zIn, blob_str(&bad)); } i += x-2; } } } /* Separate from the previous argument by a space */ if( n>0 && !fossil_isspace(z[n-1]) ){ blob_append_char(pBlob, ' '); } /* Check for characters that need quoting */ if( !needEscape ){ if( isFilename && zIn[0]=='-' ){ blob_append_char(pBlob, '.'); #if defined(_WIN32) blob_append_char(pBlob, '\\'); #else blob_append_char(pBlob, '/'); #endif } blob_append(pBlob, zIn, -1); }else{ #if defined(_WIN32) /* Quoting strategy for windows: ** Put the entire name inside of "...". Any " characters within ** the name get doubled. */ blob_append_char(pBlob, '"'); if( isFilename && zIn[0]=='-' ){ blob_append_char(pBlob, '.'); blob_append_char(pBlob, '\\'); }else if( zIn[0]=='/' ){ blob_append_char(pBlob, '.'); } for(i=0; (c = (unsigned char)zIn[i])!=0; i++){ blob_append_char(pBlob, (char)c); if( c=='"' ) blob_append_char(pBlob, '"'); if( c=='\\' ) blob_append_char(pBlob, '\\'); if( c=='%' && isFilename ) blob_append(pBlob, "%cd:~,%", 7); } blob_append_char(pBlob, '"'); #else /* Quoting strategy for unix: ** If the name does not contain ', then surround the whole thing ** with '...'. If there is one or more ' characters within the ** name, then put \ before each special character. */ if( strchr(zIn,'\'') ){ if( isFilename && zIn[0]=='-' ){ blob_append_char(pBlob, '.'); blob_append_char(pBlob, '/'); } for(i=0; (c = (unsigned char)zIn[i])!=0; i++){ if( aSafeChar[c] && aSafeChar[c]!=2 ) blob_append_char(pBlob, '\\'); blob_append_char(pBlob, (char)c); } }else{ blob_append_char(pBlob, '\''); if( isFilename && zIn[0]=='-' ){ blob_append_char(pBlob, '.'); blob_append_char(pBlob, '/'); } blob_append(pBlob, zIn, -1); blob_append_char(pBlob, '\''); } #endif } } /* ** COMMAND: test-escaped-arg ** ** Usage %fossil ARGS ... ** ** Run each argument through blob_append_escaped_arg() and show the ** result. Append each argument to "fossil test-echo" and run that ** using fossil_system() to verify that it really does get escaped ** correctly. ** ** Other options: ** ** --filename-args BOOL Subsequent arguments are assumed to be ** filenames if BOOL is true, or not if BOOL ** is false. Defaults on. ** ** --hex HEX Skip the --hex flag and instead decode HEX ** into ascii. This provides a way to insert ** unusual characters as an argument for testing. ** ** --compare HEX ASCII Verify that argument ASCII is identical to ** to decoded HEX. ** ** --fuzz N Run N fuzz cases. Each cases is a call ** to "fossil test-escaped-arg --compare HEX ARG" ** where HEX and ARG are the same argument. ** The argument is chosen at random. */ void test_escaped_arg_command(void){ int i; Blob x; const char *zArg; int isFilename = 1; char zBuf[100]; blob_init(&x, 0, 0); for(i=2; i<g.argc; i++){ zArg = g.argv[i]; if( fossil_strcmp(zArg, "--hex")==0 && i+1<g.argc ){ size_t n = strlen(g.argv[++i]); if( n>=(sizeof(zBuf)-1)*2 ){ fossil_fatal("Argument to --hex is too big"); } memset(zBuf, 0, sizeof(zBuf)); decode16((const unsigned char*)g.argv[i], (unsigned char*)zBuf, (int)n); zArg = zBuf; }else if( fossil_strcmp(zArg, "--compare")==0 && i+2<g.argc ){ size_t n = strlen(g.argv[++i]); if( n>=(sizeof(zBuf)-1)*2 ){ fossil_fatal("HEX argument to --compare is too big"); } memset(zBuf, 0, sizeof(zBuf)); if( decode16((const unsigned char*)g.argv[i], (unsigned char*)zBuf, (int)n) ){ fossil_fatal("HEX decode of %s failed", g.argv[i]); } zArg = g.argv[++i]; if( zArg[0]=='-' ){ fossil_fatal("filename argument \"%s\" begins with \"-\"", zArg); } #ifdef _WIN32 if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='\\' ) zArg += 2; #else if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='/' ) zArg += 2; #endif if( strcmp(zBuf, zArg)!=0 ){ fossil_fatal("argument disagree: \"%s\" (%s) versus \"%s\"", zBuf, g.argv[i-1], zArg); } continue; }else if( fossil_strcmp(zArg, "--fuzz")==0 && i+1<g.argc ){ int n = atoi(g.argv[++i]); int j; for(j=0; j<n; j++){ unsigned char m, k; int rc; unsigned char zWord[100]; sqlite3_randomness(sizeof(m), &m); m = (m%40)+5; sqlite3_randomness(m, zWord); /* Between 5 and 45 bytes of randomness */ for(k=0; k<m; k++){ unsigned char cx = zWord[k]; if( cx<0x20 || cx>=0x7f ){ /* Translate illegal bytes into various non-ASCII unicode ** characters in order to exercise those code paths */ unsigned int u; if( cx>=0x7f ){ u = cx; }else if( cx>=0x08 ){ u = 0x800 + cx; }else{ u = 0x10000 + cx; } if( u<0x00080 ){ zWord[k] = u & 0xFF; }else if( u<0x00800 ){ zWord[k++] = 0xC0 + (u8)((u>>6)&0x1F); zWord[k] = 0x80 + (u8)(u & 0x3F); }else if( u<0x10000 ){ zWord[k++] = 0xE0 + (u8)((u>>12)&0x0F); zWord[k++] = 0x80 + (u8)((u>>6) & 0x3F); zWord[k] = 0x80 + (u8)(u & 0x3F); }else{ zWord[k++] = 0xF0 + (u8)((u>>18) & 0x07); zWord[k++] = 0x80 + (u8)((u>>12) & 0x3F); zWord[k++] = 0x80 + (u8)((u>>6) & 0x3F); zWord[k] = 0x80 + (u8)(u & 0x3F); } } } zWord[k] = 0; encode16(zWord, (unsigned char*)zBuf, (int)k); blob_appendf(&x, "%$ test-escaped-arg --compare %s %$", g.nameOfExe, zBuf,zWord); rc = fossil_system(blob_str(&x)); if( rc ) fossil_fatal("failed test (%d): %s\n", rc, blob_str(&x)); blob_reset(&x); } continue; }else if( fossil_strcmp(zArg, "--filename-args")==0 ){ if( i+1<g.argc ){ i++; isFilename = is_truth(g.argv[i]); } continue; } fossil_print("%3d [%s]: ", i, zArg); if( isFilename ){ blob_appendf(&x, "%$ test-echo %$", g.nameOfExe, zArg); }else{ blob_appendf(&x, "%$ test-echo %!$", g.nameOfExe, zArg); } fossil_print("%s\n", blob_str(&x)); fossil_system(blob_str(&x)); blob_reset(&x); } } /* ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen ** bytes from pIn, starting at position pIn->iCursor, and copies them ** to pDest (which must be valid memory at least nLen bytes long). ** ** Returns the number of bytes read/copied, which may be less than ** nLen (if end-of-blob is encountered). ** ** Updates pIn's cursor. ** ** Returns 0 if pIn contains no data. */ unsigned int blob_read(Blob *pIn, void * pDest, unsigned int nLen ){ if( !pIn->aData || (pIn->iCursor >= pIn->nUsed) ){ return 0; } else if( (pIn->iCursor + nLen) > (unsigned int)pIn->nUsed ){ nLen = (unsigned int) (pIn->nUsed - pIn->iCursor); } assert( pIn->nUsed > pIn->iCursor ); assert( (pIn->iCursor+nLen) <= pIn->nUsed ); if( nLen ){ memcpy( pDest, pIn->aData, nLen ); pIn->iCursor += nLen; } return nLen; } /* ** Swaps the contents of the given blobs. Results ** are unspecified if either value is NULL or both ** point to the same blob. */ void blob_swap( Blob *pLeft, Blob *pRight ){ Blob swap = *pLeft; *pLeft = *pRight; *pRight = swap; } /* ** Strip a possible byte-order-mark (BOM) from the blob. On Windows, if there ** 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>1 ){ /* 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, "\000\000", 3); zUtf8 = blob_str(pBlob) + bomSize; zUtf8 = fossil_unicode_to_utf8(zUtf8); blob_reset(pBlob); 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.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file contains code used to create new branches within a repository. */ #include "config.h" #include "branch.h" #include <assert.h> /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | > > > > | > > > > > > > | | | > | | < < < < | | | > > > > > > > > | < < < < < < | < | | | | < | < < | | > > > | > | > > > > > | | | | > | | | | | | > | | | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | > | > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > | > > | | | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | > > | | < > | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > | > > > > | > > > > > > > > > | > > > > > > > < > > | | > | > | | | < < < < < < < < < < < < < < < < < < < | < < < < < > < < < < < < < < < < > > > | > > > > > | | > > | | < < < < < < < < | | | | > > > > > > > > > > > > | > | > > > | | | > > > > > > > | < | > > > > > > | < < < < < < < | | 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 | ** This file contains code used to create new branches within a repository. */ #include "config.h" #include "branch.h" #include <assert.h> /* ** Return true if zBr is the branch name associated with check-in with ** blob.uuid value of zUuid */ int branch_includes_uuid(const char *zBr, const char *zUuid){ return db_exists( "SELECT 1 FROM tagxref, blob" " WHERE blob.uuid=%Q AND tagxref.rid=blob.rid" " AND tagxref.value=%Q AND tagxref.tagtype>0" " AND tagxref.tagid=%d", zUuid, zBr, TAG_BRANCH ); } /* ** If RID refers to a check-in, return the name of the branch for that ** check-in. ** ** Space to hold the returned value is obtained from fossil_malloc() ** and should be freed by the caller. */ char *branch_of_rid(int rid){ char *zBr = 0; static Stmt q; db_static_prepare(&q, "SELECT value FROM tagxref" " WHERE rid=$rid AND tagid=%d" " AND tagtype>0", TAG_BRANCH); db_bind_int(&q, "$rid", rid); if( db_step(&q)==SQLITE_ROW ){ zBr = fossil_strdup(db_column_text(&q,0)); } db_reset(&q); if( zBr==0 ){ static char *zMain = 0; if( zMain==0 ) zMain = db_get("main-branch",0); zBr = fossil_strdup(zMain); } return zBr; } /* ** fossil branch new NAME BASIS ?OPTIONS? ** argv0 argv1 argv2 argv3 argv4 */ void branch_new(void){ int rootid; /* RID of the root check-in - what we branch off of */ int brid; /* RID of the branch check-in */ int noSign; /* True if the branch is unsigned */ int i; /* Loop counter */ char *zUuid; /* Artifact ID of origin */ Stmt q; /* Generic query */ const char *zBranch; /* Name of the new branch */ char *zDate; /* Date that branch was created */ char *zComment; /* Check-in comment for the new branch */ const char *zColor; /* Color of the new branch */ Blob branch; /* manifest for the new branch */ Manifest *pParent; /* Parsed parent manifest */ Blob mcksum; /* Self-checksum on the manifest */ const char *zDateOvrd; /* Override date string */ const char *zUserOvrd; /* Override user name */ int isPrivate = 0; /* True if the branch should be private */ int bAutoColor = 0; /* Value of "--bgcolor" is "auto" */ noSign = find_option("nosign","",0)!=0; zColor = find_option("bgcolor","c",1); if( fossil_strncmp(zColor, "auto", 4)==0 ) { bAutoColor = 1; zColor = 0; } isPrivate = find_option("private",0,0)!=0; 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( branch_is_open(zBranch) ){ fossil_fatal("an open branch named \"%s\" already exists", zBranch); } user_select(); db_begin_transaction(); rootid = name_to_typed_rid(g.argv[4], "ci"); if( rootid==0 ){ fossil_fatal("unable to locate check-in off of which to branch"); } pParent = manifest_get(rootid, CFTYPE_MANIFEST, 0); if( pParent==0 ){ fossil_fatal("%s is not a valid check-in", g.argv[4]); } /* Create a manifest for the new branch */ blob_zero(&branch); if( pParent->zBaseline ){ blob_appendf(&branch, "B %s\n", pParent->zBaseline); } zComment = mprintf("Create new branch named \"%h\"", zBranch); blob_appendf(&branch, "C %F\n", zComment); zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); blob_appendf(&branch, "D %s\n", zDate); /* Copy all of the content from the parent into the branch */ for(i=0; i<pParent->nFile; ++i){ blob_appendf(&branch, "F %F", pParent->aFile[i].zName); if( pParent->aFile[i].zUuid ){ blob_appendf(&branch, " %s", pParent->aFile[i].zUuid); if( pParent->aFile[i].zPerm && pParent->aFile[i].zPerm[0] ){ blob_appendf(&branch, " %s", pParent->aFile[i].zPerm); } } blob_append(&branch, "\n", 1); } zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid); blob_appendf(&branch, "P %s\n", zUuid); if( pParent->zRepoCksum ){ blob_appendf(&branch, "R %s\n", pParent->zRepoCksum); } manifest_destroy(pParent); /* Add the symbolic branch name and the "branch" tag to identify ** this as a new branch */ if( content_is_private(rootid) ) isPrivate = 1; if( isPrivate && zColor==0 && !bAutoColor) zColor = "#fec084"; if( zColor!=0 ){ blob_appendf(&branch, "T *bgcolor * %F\n", zColor); } blob_appendf(&branch, "T *branch * %F\n", zBranch); blob_appendf(&branch, "T *sym-%F *\n", zBranch); if( isPrivate ){ noSign = 1; } /* Cancel all other symbolic tags */ db_prepare(&q, "SELECT tagname FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" " AND tagtype>0 AND tagname GLOB 'sym-*'" " ORDER BY tagname", 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_add_unsent(brid); if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ fossil_fatal("%s", g.zErrMsg); } assert( blob_is_reset(&branch) ); content_deltify(rootid, &brid, 1, 0); zUuid = rid_to_uuid(brid); fossil_print("New branch: %s\n", zUuid); if( g.argc==3 ){ fossil_print( "\n" "Note: the local check-out has not been updated to the new\n" " branch. To begin working on the new branch, do this:\n" "\n" " %s update %s\n", g.argv[0], zBranch ); } /* Commit */ db_end_transaction(0); /* Do an autosync push, if requested */ if( !isPrivate ) autosync_loop(SYNC_PUSH, 0, "branch"); } /* ** Create a TEMP table named "tmp_brlist" with 7 columns: ** ** name Name of the branch ** mtime Time of last check-in on this branch ** isclosed True if the branch is closed ** mergeto Another branch this branch was merged into ** nckin Number of checkins on this branch ** ckin Hash of the last check-in on this branch ** isprivate True if the branch is private ** bgclr Background color for this branch */ static const char createBrlistQuery[] = @ CREATE TEMP TABLE IF NOT EXISTS tmp_brlist AS @ SELECT @ tagxref.value AS name, @ max(event.mtime) AS 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) AS isclosed, @ (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) AS mergeto, @ count(*) AS nckin, @ (SELECT uuid FROM blob WHERE rid=tagxref.rid) AS ckin, @ event.bgcolor AS bgclr, @ EXISTS(SELECT 1 FROM private WHERE rid=tagxref.rid) AS isprivate @ 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; ; /* Call this routine to create the TEMP table */ static void brlist_create_temp_table(void){ db_exec_sql(createBrlistQuery); } #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_ORDERBY_MTIME 0x004 /* Sort by MTIME. (otherwise sort by name)*/ #define BRL_REVERSE 0x008 /* Reverse the sort order */ #define BRL_PRIVATE 0x010 /* Show only private branches */ #define BRL_MERGED 0x020 /* Show only merged branches */ #define BRL_UNMERGED 0x040 /* Show only unmerged branches */ #define BRL_LIST_USERS 0x080 /* Populate list of users participating */ #endif /* INTERFACE */ /* ** Prepare a query that will list branches. ** ** If the BRL_ORDERBY_MTIME flag is set and nLimitMRU ("Limit Most Recently Used ** style") is a non-zero number, the result is limited to nLimitMRU entries, and ** the BRL_REVERSE flag is applied in an outer query after processing the limit, ** so that it's possible to generate short lists with the most recently modified ** branches sorted chronologically in either direction, as does the "branch lsh" ** command. ** For other cases, the outer query is also generated, but works as a no-op. The ** code to build the outer query is marked with *//* OUTER QUERY *//* comments. */ void branch_prepare_list_query( Stmt *pQuery, int brFlags, const char *zBrNameGlob, int nLimitMRU, const char *zUser ){ Blob sql; blob_init(&sql, 0, 0); brlist_create_temp_table(); /* Ignore nLimitMRU if no chronological sort requested. */ if( (brFlags & BRL_ORDERBY_MTIME)==0 ) nLimitMRU = 0; /* Negative values for nLimitMRU also mean "no limit". */ if( nLimitMRU<0 ) nLimitMRU = 0; /* OUTER QUERY */ blob_append_sql(&sql,"SELECT name, isprivate, mergeto,"); if( brFlags & BRL_LIST_USERS ){ blob_append_sql(&sql, " (SELECT group_concat(user) FROM (" " SELECT DISTINCT * FROM (" " SELECT coalesce(euser,user) AS user" " FROM event" " WHERE type='ci' AND objid IN (" " SELECT rid FROM tagxref WHERE value=name)" " ORDER BY 1)))" ); }else{ blob_append_sql(&sql, " NULL"); } blob_append_sql(&sql," FROM ("); /* INNER QUERY */ switch( brFlags & BRL_OPEN_CLOSED_MASK ){ case BRL_CLOSED_ONLY: { blob_append_sql(&sql, "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE isclosed" ); break; } case BRL_BOTH: { blob_append_sql(&sql, "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE 1" ); break; } case BRL_OPEN_ONLY: { blob_append_sql(&sql, "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist " " WHERE NOT isclosed" ); break; } } if( brFlags & BRL_PRIVATE ) blob_append_sql(&sql, " AND isprivate"); if( brFlags & BRL_MERGED ) blob_append_sql(&sql, " AND mergeto IS NOT NULL"); if( zBrNameGlob ) blob_append_sql(&sql, " AND (name GLOB %Q)", zBrNameGlob); if( zUser && zUser[0] ) blob_append_sql(&sql, " AND EXISTS (SELECT 1 FROM event WHERE type='ci' AND (user=%Q OR euser=%Q)" " AND objid in (SELECT rid FROM tagxref WHERE value=tmp_brlist.name))", zUser, zUser ); if( brFlags & BRL_ORDERBY_MTIME ){ blob_append_sql(&sql, " ORDER BY -mtime"); }else{ blob_append_sql(&sql, " ORDER BY name COLLATE nocase"); } if( brFlags & BRL_REVERSE && !nLimitMRU ){ blob_append_sql(&sql," DESC"); } if( nLimitMRU ){ blob_append_sql(&sql," LIMIT %d",nLimitMRU); } blob_append_sql(&sql,")"); /* OUTER QUERY */ if( brFlags & BRL_REVERSE && nLimitMRU ){ blob_append_sql(&sql," ORDER BY mtime"); /* OUTER QUERY */ } db_prepare_blob(pQuery, &sql); blob_reset(&sql); } /* ** If the branch named in the argument is open, return a RID for one of ** the open leaves of that branch. If the branch does not exists or is ** closed, return 0. */ int branch_is_open(const char *zBrName){ return db_int(0, "SELECT rid FROM tagxref AS ox" " WHERE tagid=%d" " AND tagtype=2" " AND value=%Q" " AND rid IN leaf" " AND NOT EXISTS(SELECT 1 FROM tagxref AS ix" " WHERE tagid=%d" " AND tagtype=1" " AND ox.rid=ix.rid)", TAG_BRANCH, zBrName, TAG_CLOSED ); } /* ** Internal helper for branch_cmd_close() and friends. Adds a row to ** the to the brcmdtag TEMP table, initializing that table if needed, ** holding a pending tag for the given blob.rid (which is assumed to ** be valid). zTag must be a fully-formed tag name, including the ** (+,-,*) prefix character. ** */ static void branch_cmd_tag_add(int rid, const char *zTag){ static int once = 0; assert(zTag && ('+'==zTag[0] || '-'==zTag[0] || '*'==zTag[0])); if(0==once++){ db_multi_exec("CREATE TEMP TABLE brcmdtag(" "rid INTEGER UNIQUE ON CONFLICT IGNORE," "tag TEXT NOT NULL" ")"); } db_multi_exec("INSERT INTO brcmdtag(rid,tag) VALUES(%d,%Q)", rid, zTag); } /* ** Internal helper for branch_cmd_close() and friends. Creates and ** saves a control artifact of tag changes stored via ** branch_cmd_tag_add(). Fails fatally on error, returns 0 if it saves ** an artifact, and a negative value if it does not save anything ** because no tags were queued up. A positive return value is reserved ** for potential future semantics. ** ** This function asserts that a transaction is underway and it ends ** the transaction, committing or rolling back, as appropriate. */ static int branch_cmd_tag_finalize(int fDryRun /* roll back if true */, int fVerbose /* output extra info */, const char *zDateOvrd /* --date-override */, const char *zUserOvrd /* --user-override */){ int nTags = 0; Stmt q = empty_Stmt; Blob manifest = empty_blob; int doRollback = fDryRun!=0; assert(db_transaction_nesting_depth() > 0); if(!db_table_exists("temp","brcmdtag")){ fossil_warning("No tags added - nothing to do."); db_end_transaction(1); return -1; } db_prepare(&q, "SELECT b.uuid, t.tag " "FROM blob b, brcmdtag t " "WHERE b.rid=t.rid " "ORDER BY t.tag, b.uuid"); blob_appendf(&manifest, "D %z\n", date_in_standard_format( zDateOvrd ? zDateOvrd : "now")); while(SQLITE_ROW==db_step(&q)){ const char * zHash = db_column_text(&q, 0); const char * zTag = db_column_text(&q, 1); blob_appendf(&manifest, "T %s %s\n", zTag, zHash); ++nTags; } if(!nTags){ fossil_warning("No tags added - nothing to do."); db_end_transaction(1); blob_reset(&manifest); return -1; } user_select(); blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : login_name()); { /* Z-card and save artifact */ int newRid; Blob cksum = empty_blob; md5sum_blob(&manifest, &cksum); blob_appendf(&manifest, "Z %b\n", &cksum); blob_reset(&cksum); if(fDryRun && fVerbose){ fossil_print("Dry-run mode: will roll back new artifact:\n%b", &manifest); /* Run through the saving steps, though, noting that doing so ** will clear out &manifest, which is why we output it here ** instead of after saving. */ } newRid = content_put(&manifest); if(0==newRid){ fossil_fatal("Problem saving new artifact: %s\n%b", g.zErrMsg, &manifest); }else if(manifest_crosslink(newRid, &manifest, 0)==0){ fossil_fatal("Crosslinking error: %s", g.zErrMsg); } fossil_print("Saved new control artifact %z (RID %d).\n", rid_to_uuid(newRid), newRid); db_add_unsent(newRid); if(fDryRun){ fossil_print("Dry-run mode: rolling back new artifact.\n"); assert(0!=doRollback); } } db_multi_exec("DROP TABLE brcmdtag"); blob_reset(&manifest); db_end_transaction(doRollback); return 0; } /* ** Internal helper for branch_cmd_close() and friends. zName is a ** symbolic check-in name. Returns the blob.rid of the check-in or fails ** fatally if the name does not resolve unambiguously. If zUuid is ** not NULL, *zUuid is set to the resolved blob.uuid and must be freed ** by the caller via fossil_free(). */ static int branch_resolve_name(char const *zName, char **zUuid){ const int rid = name_to_uuid2(zName, "ci", zUuid); if(0==rid){ fossil_fatal("Cannot resolve name: %s", zName); }else if(rid<0){ fossil_fatal("Ambiguous name: %s", zName); } return rid; } /* ** Implementation of (branch hide/unhide) subcommands. nStartAtArg is ** the g.argv index to start reading branch/check-in names. fHide is ** true for hiding, false for unhiding. Fails fatally on error. */ static void branch_cmd_hide(int nStartAtArg, int fHide){ int argPos = nStartAtArg; /* g.argv pos with first branch/ci name */ char * zUuid = 0; /* Resolved branch UUID. */ const int fVerbose = find_option("verbose","v",0)!=0; const int fDryRun = find_option("dry-run","n",0)!=0; const char *zDateOvrd = find_option("date-override",0,1); const char *zUserOvrd = find_option("user-override",0,1); verify_all_options(); db_begin_transaction(); for( ; argPos < g.argc; fossil_free(zUuid), ++argPos ){ const char * zName = g.argv[argPos]; const int rid = branch_resolve_name(zName, &zUuid); const int isHidden = rid_has_tag(rid, TAG_HIDDEN); /* Potential TODO: check for existing 'hidden' flag and skip this ** entry if it already has (if fHide) or does not have (if !fHide) ** that tag. FWIW, /ci_edit does not do so. */ if(fHide && isHidden){ fossil_warning("Skipping hidden check-in %s: %s.", zName, zUuid); continue; }else if(!fHide && !isHidden){ fossil_warning("Skipping non-hidden check-in %s: %s.", zName, zUuid); continue; } branch_cmd_tag_add(rid, fHide ? "*hidden" : "-hidden"); if(fVerbose!=0){ fossil_print("%s check-in [%s] %s\n", fHide ? "Hiding" : "Unhiding", zName, zUuid); } } branch_cmd_tag_finalize(fDryRun, fVerbose, zDateOvrd, zUserOvrd); } /* ** Implementation of (branch close|reopen) subcommands. nStartAtArg is ** the g.argv index to start reading branch/check-in names. The given ** checkins are closed if fClose is true, else their "closed" tag (if ** any) is cancelled. Fails fatally on error. */ static void branch_cmd_close(int nStartAtArg, int fClose){ int argPos = nStartAtArg; /* g.argv pos with first branch name */ char * zUuid = 0; /* Resolved branch UUID. */ const int fVerbose = find_option("verbose","v",0)!=0; const int fDryRun = find_option("dry-run","n",0)!=0; const char *zDateOvrd = find_option("date-override",0,1); const char *zUserOvrd = find_option("user-override",0,1); verify_all_options(); db_begin_transaction(); for( ; argPos < g.argc; fossil_free(zUuid), ++argPos ){ const char * zName = g.argv[argPos]; const int rid = branch_resolve_name(zName, &zUuid); const int isClosed = leaf_is_closed(rid); if(!is_a_leaf(rid)){ /* This behaviour is different from /ci_edit closing, where ** is_a_leaf() adds a "+" tag and !is_a_leaf() adds a "*" ** tag. We might want to change this to match for consistency's ** sake, but it currently seems unnecessary to close/re-open a ** non-leaf. */ fossil_warning("Skipping non-leaf [%s] %s", zName, zUuid); continue; }else if(fClose && isClosed){ fossil_warning("Skipping closed leaf [%s] %s", zName, zUuid); continue; }else if(!fClose && !isClosed){ fossil_warning("Skipping non-closed leaf [%s] %s", zName, zUuid); continue; } branch_cmd_tag_add(rid, fClose ? "+closed" : "-closed"); if(fVerbose!=0){ fossil_print("%s branch [%s] %s\n", fClose ? "Closing" : "Re-opening", zName, zUuid); } } branch_cmd_tag_finalize(fDryRun, fVerbose, zDateOvrd, zUserOvrd); } /* ** COMMAND: branch ** ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS? ** ** Run various subcommands to manage branches of the open repository or ** of the repository identified by the -R or --repository option. ** ** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? ** ** Adds or cancels the "closed" tag to one or more branches. ** It accepts arbitrary unambiguous symbolic names but ** will only resolve check-in names and skips any which resolve ** to non-leaf check-ins. ** ** Options: ** -n|--dry-run Do not commit changes, but dump artifact ** to stdout ** -v|--verbose Output more information ** --date-override DATE DATE to use instead of 'now' ** --user-override USER USER to use instead of the current default ** ** > fossil branch current ** ** Print the name of the branch for the current check-out ** ** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? ** ** Adds or cancels the "hidden" tag for the specified branches or ** or check-in IDs. Accepts the same options as the close ** subcommand. ** ** > fossil branch info BRANCH-NAME ** ** Print information about a branch ** ** > fossil branch list|ls ?OPTIONS? ?GLOB? ** > fossil branch lsh ?OPTIONS? ?LIMIT? ** ** List all branches. ** ** Options: ** -a|--all List all branches. Default show only open branches ** -c|--closed List closed branches ** -m|--merged List branches merged into the current branch ** -M|--unmerged List branches not merged into the current branch ** -p List only private branches ** -r Reverse the sort order ** -t Show recently changed branches first ** --self List only branches where you participate ** --username USER List only branches where USER participate ** --users N List up to N users partipiating ** ** The current branch is marked with an asterisk. Private branches are ** marked with a hash sign. ** ** If GLOB is given, show only branches matching the pattern. ** ** The "lsh" variant of this subcommand shows recently changed branches, ** and accepts an optional LIMIT argument (defaults to 5) to cap output, ** but no GLOB argument. All other options are supported, with -t being ** an implied no-op. ** ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS? ** ** Create a new branch BRANCH-NAME off of check-in BASIS. ** ** Options: ** --private Branch is private (i.e., remains local) ** --bgcolor COLOR Use COLOR instead of automatic background ** ("auto" lets Fossil choose it automatically, ** even for private branches) ** --nosign Do not sign contents on this branch ** --date-override DATE DATE to use instead of 'now' ** --user-override USER USER to use instead of the current default ** ** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be ** replaced by a space, and it may also name a timezone offset ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward). ** Either no timezone suffix or "Z" means UTC. ** ** Options: ** -R|--repository REPO Run commands on repository REPO */ 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,"current",n)==0 ){ if( !g.localOpen ){ fossil_fatal("not within an open check-out"); }else{ int vid = db_lget_int("checkout", 0); char *zCurrent = db_text(0, "SELECT value FROM tagxref" " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH); fossil_print("%s\n", zCurrent); fossil_free(zCurrent); } }else if( strncmp(zCmd,"info",n)==0 ){ int i; for(i=3; i<g.argc; i++){ const char *zBrName = g.argv[i]; int rid = branch_is_open(zBrName); if( rid==0 ){ fossil_print("%s: not an open branch\n", zBrName); }else{ const char *zUuid = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",rid); const char *zDate = db_text(0, "SELECT datetime(mtime,toLocal()) FROM event" " WHERE objid=%d", rid); fossil_print("%s: open as of %s on %.16s\n", zBrName, zDate, zUuid); } } }else if( strncmp(zCmd,"list",n)==0 || strncmp(zCmd, "ls", n)==0 || strcmp(zCmd, "lsh")==0 ){ Stmt q; Blob txt = empty_blob; int vid; char *zCurrent = 0; const char *zBrNameGlob = 0; const char *zUser = find_option("username",0,1); const char *zUsersOpt = find_option("users",0,1); int nUsers = zUsersOpt ? atoi(zUsersOpt) : 0; int nLimit = 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( find_option("t",0,0)!=0 ) brFlags |= BRL_ORDERBY_MTIME; if( find_option("r",0,0)!=0 ) brFlags |= BRL_REVERSE; if( find_option("p",0,0)!=0 ) brFlags |= BRL_PRIVATE; if( find_option("merged","m",0)!=0 ) brFlags |= BRL_MERGED; if( find_option("unmerged","M",0)!=0 ) brFlags |= BRL_UNMERGED; if( find_option("self",0,0)!=0 ){ if( zUser ){ fossil_fatal("flags --username and --self are mutually exclusive"); } user_select(); zUser = login_name(); } verify_all_options(); if ( (brFlags & BRL_MERGED) && (brFlags & BRL_UNMERGED) ){ fossil_fatal("flags --merged and --unmerged are mutually exclusive"); } if( zUsersOpt ){ if( nUsers <= 0) fossil_fatal("With --users, N must be positive"); brFlags |= BRL_LIST_USERS; } if( strcmp(zCmd, "lsh")==0 ){ nLimit = 5; if( g.argc>4 || (g.argc==4 && (nLimit = atoi(g.argv[3]))==0) ){ fossil_fatal("the lsh subcommand allows one optional numeric argument"); } brFlags |= BRL_ORDERBY_MTIME; }else{ if( g.argc >= 4 ) zBrNameGlob = g.argv[3]; } 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, zBrNameGlob, nLimit, zUser); blob_init(&txt, 0, 0); while( db_step(&q)==SQLITE_ROW ){ const char *zBr = db_column_text(&q, 0); int isPriv = db_column_int(&q, 1)==1; const char *zMergeTo = db_column_text(&q, 2); int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0; const char *zUsers = db_column_text(&q, 3); if( (brFlags & BRL_MERGED) && fossil_strcmp(zCurrent,zMergeTo)!=0 ){ continue; } if( (brFlags & BRL_UNMERGED) && (fossil_strcmp(zCurrent,zMergeTo)==0 || isCur) ){ continue; } blob_appendf(&txt, "%s%s%s", ( (brFlags & BRL_PRIVATE) ? " " : ( isPriv ? "#" : " ") ), (isCur ? "* " : " "), zBr); if( nUsers ){ char c; const char *cp; const char *pComma = 0; int commas = 0; for( cp = zUsers; ( c = *cp ) != 0; cp++ ){ if( c == ',' ){ commas++; if( commas == nUsers ) pComma = cp; } } if( pComma ){ blob_appendf(&txt, " (%.*s,... %i more)", pComma - zUsers, zUsers, commas + 1 - nUsers); }else{ blob_appendf(&txt, " (%s)", zUsers); } } fossil_print("%s\n", blob_str(&txt)); blob_reset(&txt); } db_finalize(&q); }else if( strncmp(zCmd,"new",n)==0 ){ branch_new(); }else if( strncmp(zCmd,"close",5)==0 ){ if(g.argc<4){ usage("close branch-name(s)..."); } branch_cmd_close(3, 1); }else if( strncmp(zCmd,"reopen",6)==0 ){ if(g.argc<4){ usage("reopen branch-name(s)..."); } branch_cmd_close(3, 0); }else if( strncmp(zCmd,"hide",4)==0 ){ if(g.argc<4){ usage("hide branch-name(s)..."); } branch_cmd_hide(3,1); }else if( strncmp(zCmd,"unhide",6)==0 ){ if(g.argc<4){ usage("unhide branch-name(s)..."); } branch_cmd_hide(3,0); }else{ fossil_fatal("branch subcommand should be one of: " "close current hide info list ls lsh new reopen unhide"); } } /* ** 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; int show_colors = PB("colors"); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_set_current_feature("branch"); style_header("Branches"); style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_checkbox("colors", "Use Branch Colors", 0, 0); login_anonymous_available(); brlist_create_temp_table(); db_prepare(&q, "SELECT * FROM tmp_brlist ORDER BY mtime DESC"); rNow = db_double(0.0, "SELECT julianday('now')"); @ <script id="brlist-data" type="application/json">\ @ {"timelineUrl":"%R/timeline"}</script> @ <div class="brlist"> @ <table class='sortable' data-column-types='tkNtt' data-init-sort='2'> @ <thead><tr> @ <th>Branch Name</th> @ <th>Last Change</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); const char *zBgClr = db_column_text(&q, 6); char *zAge = human_readable_age(rNow - rMtime); sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0); if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0; if( zBgClr == 0 ){ if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){ zBgClr = 0; }else{ zBgClr = hash_color(zBranch); } } if( zBgClr && zBgClr[0] && show_colors ){ @ <tr style="background-color:%s(zBgClr)"> }else{ @ <tr> } @ <td>%z(href("%R/timeline?r=%T",zBranch))%h(zBranch)</a><input @ type="checkbox" disabled="disabled"/></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); builtin_request_js("fossil.page.brlist.js"); style_table_sorter(); style_finish_page(); } /* ** WEBPAGE: brlist ** Show a list of branches. With no query parameters, a sortable table ** is used to show all branches. If query parameters are present a ** fixed bullet list is shown. ** ** Query parameters: ** ** all Show all branches ** closed Show only closed branches ** open Show only open branches ** colortest Show all branches with automatic color ** ** When there are no query parameters, a new-style /brlist page shows ** all branches in a sortable table. The new-style /brlist page is ** preferred and is the default. */ 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; } cgi_check_for_malice(); if( colorTest ){ showClosed = 0; showAll = 1; } if( showAll ) brFlags = BRL_BOTH; if( showClosed ) brFlags = BRL_CLOSED_ONLY; style_set_current_feature("branch"); style_header("%s", showClosed ? "Closed Branches" : showAll ? "All Branches" : "Open Branches"); style_submenu_element("Timeline", "brtimeline"); if( showClosed ){ style_submenu_element("All", "brlist?all"); style_submenu_element("Open", "brlist?open"); }else if( showAll ){ style_submenu_element("Closed", "brlist?closed"); style_submenu_element("Open", "brlist"); }else{ style_submenu_element("All", "brlist?all"); style_submenu_element("Closed", "brlist?closed"); } if( !colorTest ){ style_submenu_element("Color-Test", "brlist?colortest"); }else{ style_submenu_element("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, 0, 0, 0); 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",zBr))%h(zBr)</a></li> } } if( cnt ){ @ </ul> } db_finalize(&q); style_finish_page(); } /* ** 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. */ static void brtimeline_extra(int rid){ Stmt q; if( !g.perm.Hyperlink ) return; db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag" " WHERE tagxref.rid=%d" " 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",zTagName))[timeline]</a> } db_finalize(&q); } /* ** WEBPAGE: brtimeline ** ** Show a timeline of all branches ** ** Query parameters: ** ** ng No graph ** nohidden Hide check-ins with "hidden" tag ** onlyhidden Show only check-ins with "hidden" tag ** brbg Background color by branch name ** ubg Background color by user name */ void brtimeline_page(void){ Blob sql = empty_blob; Stmt q; int tmFlags; /* Timeline display flags */ int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */ int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_set_current_feature("branch"); style_header("Branches"); style_submenu_element("List", "brlist"); login_anonymous_available(); timeline_ss_submenu(); cgi_check_for_malice(); @ <h2>The initial check-in for each branch:</h2> blob_append(&sql, timeline_query_for_www(), -1); blob_append_sql(&sql, "AND blob.rid IN (SELECT rid FROM tagxref" " WHERE tagtype>0 AND tagid=%d AND srcid!=0)", TAG_BRANCH); if( fNoHidden || fOnlyHidden ){ const char* zUnaryOp = fNoHidden ? "NOT" : ""; blob_append_sql(&sql, " AND %s EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n", zUnaryOp/*safe-for-%s*/, TAG_HIDDEN); } db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql)); blob_reset(&sql); /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too ** many descenders to (off-screen) parents. */ tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL; if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH; if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR; if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR; www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra); db_finalize(&q); style_finish_page(); } |
Changes to src/browse.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file contains code to implement the file browser web interface. */ #include "config.h" #include "browse.h" #include <assert.h> /* | | | | 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 to implement the file browser web interface. */ #include "config.h" #include "browse.h" #include <assert.h> /* ** This is the implementation of the "pathelement(X,N)" SQL function. ** ** If X is a unix-like pathname (with "/" separators) and N is an ** integer, then skip the initial N characters of X and return the ** name of the path component that begins on the N+1th character ** (numbered from 0). If the path component is a directory (if ** it is followed by other path components) then prepend "/". ** ** Examples: ** ** pathelement('abc/pqr/xyz', 4) -> '/pqr' ** pathelement('abc/pqr', 4) -> 'pqr' ** pathelement('abc/pqr/xyz', 0) -> '/abc' */ void pathelementFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const unsigned char *z; int len, n, i; char *zOut; |
︙ | ︙ | |||
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | sqlite3_result_text(context, (char*)&z[n], len-n, SQLITE_TRANSIENT); }else{ zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]); sqlite3_result_text(context, zOut, i-n+1, sqlite3_free); } } /* ** Given a pathname which is a relative path from the root of ** the repository to a file or directory, compute a string which ** is an HTML rendering of that path with hyperlinks on each ** directory component of the path where the hyperlink redirects ** 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. */ | > > > > > > > > | > > > > > > > | > > > > > > > > > > > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > | > < < | < | > > > > > > > > > > > | < < < > | > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > | | > > | > > | < | > > > | < > | > | > | > > | < < > > > > | | < | > | | > > > | > | | < < < < | > > > > | < | | > | | | | | | < < | | > > | > | | > > | | | | | | | | | | | > > > > > > > > > < | | > | | | < < < < < | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > > > > > > > > > > > > > > > > > | 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 | sqlite3_result_text(context, (char*)&z[n], len-n, SQLITE_TRANSIENT); }else{ zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]); sqlite3_result_text(context, zOut, i-n+1, sqlite3_free); } } /* ** Flag arguments for hyperlinked_path() */ #if INTERFACE # define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */ # define LINKPATH_FILE 0x0002 /* Link final term to /file */ #endif /* ** Given a pathname which is a relative path from the root of ** the repository to a file or directory, compute a string which ** is an HTML rendering of that path with hyperlinks on each ** directory component of the path where the hyperlink redirects ** 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 */ unsigned int mFlags /* Extra flags */ ){ int i, j; char *zSep = ""; for(i=0; zPath[i]; i=j){ for(j=i; zPath[j] && zPath[j]!='/'; j++){} if( zPath[j]==0 ){ if( mFlags & LINKPATH_FILE ){ zURI = "file"; }else if( mFlags & LINKPATH_FINFO ){ zURI = "finfo"; }else{ blob_appendf(pOut, "/%h", zPath+i); break; } } if( zCI ){ char *zLink = href("%R/%s?name=%#T%s&ci=%T", 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]); } zSep = "/"; while( zPath[j]=='/' ){ j++; } } } /* ** WEBPAGE: docdir ** ** Show the files and subdirectories within a single directory of the ** source tree. This works similarly to /dir but with the following ** differences: ** ** * Links to files go to /doc (showing the file content directly, ** depending on mimetype) rather than to /file (which always shows ** the file embedded in a standard Fossil page frame). ** ** * The submenu and the page title is now show. The page is plain. ** ** The /docdir page is a shorthand for /dir with the "dx" query parameter. ** ** Query parameters: ** ** ci=LABEL Show only files in this check-in. If omitted, the ** "trunk" directory is used. ** name=PATH Directory to display. Optional. Top-level if missing ** re=REGEXP Show only files matching REGEXP ** noreadme Do not attempt to display the README file. ** dx File links to go to /doc instead of /file or /finfo. */ void page_docdir(void){ page_dir(); } /* ** WEBPAGE: dir ** ** Show the files and subdirectories within a single directory of the ** source tree. Only files for a single check-in are shown if the ci= ** query parameter is present. If ci= is missing, the union of files ** across all check-ins is shown. ** ** Query parameters: ** ** ci=LABEL Show only files in this check-in. Optional. ** name=PATH Directory to display. Optional. Top-level if missing ** re=REGEXP Show only files matching REGEXP ** type=TYPE TYPE=flat: use this display ** TYPE=tree: use the /tree display instead ** noreadme Do not attempt to display the README file. ** dx Behave like /docdir */ void page_dir(void){ char *zD = fossil_strdup(P("name")); int nD = zD ? strlen(zD)+1 : 0; int mxLen; char *zPrefix; Stmt q; const char *zCI = P("ci"); int rid = 0; char *zUuid = 0; Manifest *pM = 0; const char *zSubdirLink; int linkTrunk = 1; int linkTip = 1; HQuery sURI; int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */ int isBranchCI = 0; /* True if ci= refers to a branch name */ char *zHeader = 0; const char *zRegexp; /* The re= query parameter */ char *zMatch; /* Extra title text describing the match */ int bDocDir = PB("dx") || strncmp(g.zPath, "docdir", 6)==0; if( zCI && strlen(zCI)==0 ){ zCI = 0; } 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; } /* 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( bDocDir && zCI==0 ) zCI = "trunk"; 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); isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0); isBranchCI = branch_includes_uuid(zCI, zUuid); if( bDocDir ) zCI = mprintf("%S", zUuid); Th_Store("current_checkin", zCI); }else{ zCI = 0; } } assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); if( zD==0 ){ if( zCI ){ zHeader = mprintf("Top-level Files of %s", zCI); }else{ zHeader = mprintf("All Top-level Files"); } }else{ if( zCI ){ zHeader = mprintf("Files in %s/ of %s", zD, zCI); }else{ zHeader = mprintf("All Files in %s/", zD); } } zRegexp = P("re"); if( zRegexp ){ zHeader = mprintf("%z matching \"%s\"", zHeader, zRegexp); zMatch = mprintf(" matching \"%h\"", zRegexp); }else{ zMatch = ""; } style_header("%s", zHeader); fossil_free(zHeader); style_adunit_config(ADUNIT_RIGHT_OK); sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, pathelementFunc, 0, 0); url_initialize(&sURI, "dir"); cgi_check_for_malice(); cgi_query_parameters_to_url(&sURI); /* Compute the title of the page */ if( bDocDir ){ zPrefix = zD ? mprintf("%s/",zD) : ""; }else if( zD ){ Blob dirname; blob_init(&dirname, 0, 0); hyperlinked_path(zD, &dirname, zCI, "dir", "", 0); @ <h2>Files in directory %s(blob_str(&dirname)) \ blob_reset(&dirname); zPrefix = mprintf("%s/", zD); style_submenu_element("Top-Level", "%s", url_render(&sURI, "name", 0, 0, 0)); }else{ @ <h2>Files in the top-level directory \ zPrefix = ""; } if( zCI ){ if( bDocDir ){ /* No header for /docdir. Just give the list of files. */ }else if( fossil_strcmp(zCI,"tip")==0 ){ @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a>\ @ %s(zMatch)</h2> }else if( isBranchCI ){ @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \ @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a>\ @ %s(zMatch)</h2> }else { @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a>\ @ %s(zMatch)</h2> } if( bDocDir ){ zSubdirLink = mprintf("%R/docdir?ci=%T&name=%T", zCI, zPrefix); }else{ zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix); } if( nD==0 && !bDocDir ){ style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); } }else{ @ in any check-in</h2> zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); } if( linkTrunk && !bDocDir ){ style_submenu_element("Trunk", "%s", url_render(&sURI, "ci", "trunk", 0, 0)); } if( linkTip && !bDocDir ){ style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); } if( zD && !bDocDir ){ style_submenu_element("History","%R/timeline?chng=%T/*", zD); } if( !bDocDir ){ style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); style_submenu_element("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. */ db_multi_exec( "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" ); if( zCI ){ /* Files in the specific checked given by zCI */ if( zD ){ db_multi_exec( "INSERT OR IGNORE INTO localfiles" " SELECT pathelement(filename,%d), uuid" " FROM files_of_checkin(%Q)" " WHERE filename GLOB '%q/*'", nD, zCI, zD ); }else{ db_multi_exec( "INSERT OR IGNORE INTO localfiles" " SELECT pathelement(filename,%d), uuid" " FROM files_of_checkin(%Q)", nD, zCI ); } }else{ /* All files across all check-ins */ if( zD ){ db_multi_exec( "INSERT OR IGNORE INTO localfiles" " SELECT pathelement(name,%d), NULL FROM filename" " WHERE name GLOB '%q/*'", nD, zD ); }else{ db_multi_exec( "INSERT OR IGNORE INTO localfiles" " SELECT pathelement(name,0), NULL FROM filename" ); } } /* If the re=REGEXP query parameter is present, filter out names that ** do not match the pattern */ if( zRegexp ){ db_multi_exec( "DELETE FROM localfiles WHERE x NOT REGEXP %Q", zRegexp ); } /* Generate a multi-column table listing the contents of zD[] ** directory. */ mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); if( mxLen<12 ) mxLen = 12; mxLen += (mxLen+9)/10; db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x COLLATE uintnocase /*scan*/"); @ <div class="columns files" style="columns: %d(mxLen)ex auto"> @ <ul class="browser"> while( db_step(&q)==SQLITE_ROW ){ const char *zFN; zFN = db_column_text(&q, 0); if( zFN[0]=='/' ){ zFN++; @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li> }else{ const char *zLink; if( bDocDir ){ zLink = href("%R/doc/%T/%T%T", zCI, zPrefix, zFN); }else if( zCI ){ zLink = href("%R/file?name=%T%T&ci=%T",zPrefix,zFN,zCI); }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></div> /* If the "noreadme" query parameter is present, do not try to ** show the content of the README file. */ if( P("noreadme")!=0 ){ style_finish_page(); return; } /* If the directory contains a readme file, then display its content below ** the list of files */ db_prepare(&q, "SELECT x, u FROM localfiles" " WHERE x COLLATE nocase IN" " ('readme','readme.txt','readme.md','readme.wiki','readme.markdown'," " 'readme.html') ORDER BY x COLLATE uintnocase LIMIT 1;" ); if( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q,0); const char *zUuid = db_column_text(&q,1); if( zUuid ){ rid = fast_uuid_to_rid(zUuid); }else{ if( zD ){ rid = db_int(0, "SELECT fid FROM filename, mlink, event" " WHERE name='%q/%q'" " AND mlink.fnid=filename.fnid" " AND event.objid=mlink.mid" " ORDER BY event.mtime DESC LIMIT 1", zD, zName ); }else{ rid = db_int(0, "SELECT fid FROM filename, mlink, event" " WHERE name='%q'" " AND mlink.fnid=filename.fnid" " AND event.objid=mlink.mid" " ORDER BY event.mtime DESC LIMIT 1", zName ); } } if( rid ){ @ <hr> if( sqlite3_strlike("readme.html", zName, 0)==0 ){ if( zUuid==0 ){ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); } @ <iframe src="%R/raw/%s(zUuid)" @ width="100%%" frameborder="0" marginwidth="0" marginheight="0" @ sandbox="allow-same-origin" @ onload="this.height=this.contentDocument.documentElement.scrollHeight;"> @ </iframe> }else{ Blob content; const char *zMime = mimetype_from_name(zName); content_get(rid, &content); safe_html_context(DOCSRC_FILE); wiki_render_by_mimetype(&content, zMime); document_emit_js(); } } } db_finalize(&q); style_finish_page(); } /* ** 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; /* Artifact hash of this file. May be NULL. */ double mtime; /* Modification time for this entry */ double sortBy; /* Either mtime or size, depending on desired sort order */ int iSize; /* Size 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, /* Hash of the file. Might be NULL. */ double mtime, /* Modification time for this entry */ int size, /* Size for this entry */ int sortOrder /* 0: filename, 1: mtime, 2: size */ ){ 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 += HNAME_MAX+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, strlen(zUuid)+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; pNew->iSize = size; if( sortOrder ){ pNew->sortBy = sortOrder==1 ? mtime : (double)size; }else{ pNew->sortBy = 0.0; } 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 ** sortBy (larger numbers first) and then by zName (smaller names first). ** ** The sortBy field will be the same as mtime in order to sort by time, ** or the same as iSize to sort by file size. ** ** Return negative if pLeft<pRight. ** Return positive if pLeft>pRight. ** Return zero if pLeft==pRight. */ static int compareNodes(FileTreeNode *pLeft, FileTreeNode *pRight){ if( pLeft->sortBy>pRight->sortBy ) return -1; if( pLeft->sortBy<pRight->sortBy ) 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 sortmtime order. */ static FileTreeNode *sortNodes(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 *sortTree(FileTreeNode *p){ FileTreeNode *pX; for(pX=p; pX; pX=pX->pSibling){ if( pX->pChild ) pX->pChild = sortTree(pX->pChild); } return sortNodes(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 ** ** Show the files using a tree-view. If the ci= query parameter is present ** then show only the files for the check-in identified. If ci= is omitted, ** then show the union of files over all check-ins. ** ** The type=tree query parameter is required or else the /dir format is ** used. ** ** Query parameters: ** ** type=tree Required to prevent use of /dir format ** 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. ** sort 0: by filename, 1: by mtime, 2: by size */ 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 sortOrder = atoi(PD("sort",useMtime?"1":"0")); 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); int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */ int isBranchCI = 0; /* ci= refers to a branch name */ char *zHeader = 0; if( zCI && strlen(zCI)==0 ){ zCI = 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; }else{ showDirOnly = 0; } 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); } cgi_check_for_malice(); /* 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,toLocal())" " FROM event WHERE objid=%d", rid); isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0); isBranchCI = branch_includes_uuid(zCI, zUuid); Th_Store("current_checkin", zCI); }else{ zCI = 0; } } if( zCI==0 ){ rNow = db_double(0.0, "SELECT max(mtime) FROM event"); zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event"); } assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) ); if( zD==0 ){ if( zCI ){ zHeader = mprintf("Top-level Files of %s", zCI); }else{ zHeader = mprintf("All Top-level Files"); } }else{ if( zCI ){ zHeader = mprintf("Files in %s/ of %s", zD, zCI); }else{ zHeader = mprintf("All Files in %s/", zD); } } style_header("%s", zHeader); fossil_free(zHeader); /* Compute the title of the page */ blob_zero(&dirname); if( zD ){ blob_append(&dirname, "within directory ", -1); hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0); if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE); style_submenu_element("Top-Level", "%s", url_render(&sURI, "name", 0, 0, 0)); }else if( zRE ){ blob_appendf(&dirname, "matching \"%s\"", zRE); } { static const char *const sort_orders[] = { "0", "Sort By Filename", "1", "Sort By Age", "2", "Sort By Size" }; style_submenu_multichoice("sort", 3, sort_orders, 0); } if( zCI ){ style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); if( nD==0 && !showDirOnly ){ style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); } } if( linkTrunk ){ style_submenu_element("Trunk", "%s", url_render(&sURI, "ci", "trunk", 0, 0)); } if( linkTip ){ style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); } style_submenu_element("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, blob.size, 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 uintnocase;" ); while( db_step(&q)==SQLITE_ROW ){ const char *zFile = db_column_text(&q,0); const char *zUuid = db_column_text(&q,1); int size = db_column_int(&q,2); double mtime = db_column_double(&q,3); 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, size, sortOrder); } db_finalize(&q); }else{ Stmt q; db_prepare(&q, "SELECT\n" " (SELECT name FROM filename WHERE filename.fnid=mlink.fnid),\n" " (SELECT uuid FROM blob WHERE blob.rid=mlink.fid),\n" " (SELECT size FROM blob WHERE blob.rid=mlink.fid),\n" " max(event.mtime)\n" " FROM mlink JOIN event ON event.objid=mlink.mid\n" " GROUP BY mlink.fnid\n" " ORDER BY 1 COLLATE uintnocase;"); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zUuid = db_column_text(&q,1); int size = db_column_int(&q,2); double mtime = db_column_double(&q,3); 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, size, sortOrder); } db_finalize(&q); } style_submenu_checkbox("nofiles", "Folders Only", 0, 0); if( showDirOnly ){ zObjType = "Folders"; }else{ zObjType = "Files"; } if( zCI && strcmp(zCI,"tip")==0 ){ @ <h2>%s(zObjType) in the %z(href("%R/info?name=tip"))latest check-in</a> }else if( isBranchCI ){ @ <h2>%s(zObjType) in the %z(href("%R/info?name=%T",zCI))latest check-in\ @ </a> for branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a> if( blob_size(&dirname) ){ @ and %s(blob_str(&dirname)) } }else if( zCI ){ @ <h2>%s(zObjType) for check-in \ @ %z(href("%R/info?name=%T",zCI))%h(zCI)</a> if( blob_size(&dirname) ){ @ and %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( sortOrder==1 ){ @ sorted by modification time</h2> }else if( sortOrder==2 ){ @ sorted by size</h2> }else{ @ sorted by filename</h2> } if( zNow ){ @ <p>File ages are expressed relative to the check-in time of @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p> } /* 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">Last Change</div> @ <div class="filetreesize">Size</div> } @ </div> @ <ul> if( sortOrder ){ p = sortTree(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 = (int)(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 class="filetreesize"></div> } @ </div> if( startExpanded || (int)(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/file?name=%T&ci=%T",p->zFullName,zCI); }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 class="filetreesize">%s(p->iSize ? mprintf("%,d",p->iSize) : "-")</div> } @ </div> } if( p->pSibling==0 ){ int nClose = p->iLevel - (p->pNext ? p->pNext->iLevel : 0); while( nClose-- > 0 ){ @ </ul> } } } @ </ul> @ </ul></div> builtin_request_js("tree.js"); style_finish_page(); /* 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) AS (VALUES(:ckin) @ UNION @ SELECT plink.pid @ FROM ckin, plink @ WHERE plink.cid=ckin.x) @ 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_exec_sql(zComputeFileAgeSetup); 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.2425); } } /* ** 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("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 ** ** Show all files in a single check-in (identified by the name= query ** parameter) in order of increasing age. ** ** 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 isBranchCI; /* name= is a branch name */ int showId = PB("showid"); Stmt q1, q2; double baseTime; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( exclude_spiders() ) 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); isBranchCI = branch_includes_uuid(zName,zUuid); baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid); zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event" " WHERE objid=%d", rid); style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName); style_header("File Ages"); zGlob = P("glob"); cgi_check_for_malice(); compute_fileage(rid,zGlob); db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); if( fossil_strcmp(zName,"tip")==0 ){ @ <h1>Files in the %z(href("%R/info?name=tip"))latest check-in</a> }else if( isBranchCI ){ @ <h1>Files in the %z(href("%R/info?name=%T",zName))latest check-in</a> @ of branch %z(href("%R/timeline?r=%T",zName))%h(zName)</a> }else{ @ <h1>Files in check-in %z(href("%R/info?name=%T",zName))%h(zName)</a> } if( zGlob && zGlob[0] ){ @ that match "%h(zGlob)" } @ ordered by age</h1> @ @ <p>File ages are expressed relative to the check-in time of @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p> @ @ <div class='fileage'><table> @ <tr><th>Age</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 filename.name, fileage.fid\n" " FROM fileage, filename\n" " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid" ); 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 *zFile = db_column_text(&q2,0); @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \ if( showId ){ int fid = db_column_int(&q2,1); @ (%d(fid))<br> }else{ @ </a><br> } } db_reset(&q2); @ </td> @ <td> @ %W(zComment) @ (check-in: %z(href("%R/info/%!S",zUuid))%S(zUuid)</a>, if( showId ){ @ id: %d(mid) } @ user: %z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>, @ branch: \ @ %z(href("%R/timeline?r=%t&c=%!S&nd",zBranch,zUuid))%h(zBranch)</a>) @ </td></tr> @ fossil_free(zAge); } @ </table></div> db_finalize(&q1); db_finalize(&q2); style_finish_page(); } /* ** WEBPAGE: files ** ** Show files as a flat table. If the ci=LABEL query parameter is provided, ** then show all the files in the specified check-in. Without the ci= query ** parameter show all files across all check-ins. ** ** 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. */ void files_page(void){ return; } |
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 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 | /* ** 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 the index in the aBuiltinFiles[] array for the file ** whose name is zFilename. Or return -1 if the file is not ** found. */ static int builtin_file_index(const char *zFilename){ int lwr, upr, i, c; lwr = 0; upr = count(aBuiltinFiles) - 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{ return i; } } return -1; } /* ** Return a pointer to built-in content */ const unsigned char *builtin_file(const char *zFilename, int *piSize){ int i = builtin_file_index(zFilename); if( i>=0 ){ if( piSize ) *piSize = aBuiltinFiles[i].nByte; return aBuiltinFiles[i].pData; }else{ if( piSize ) *piSize = 0; return 0; } } const char *builtin_text(const char *zFilename){ return (char*)builtin_file(zFilename, 0); } /* ** COMMAND: test-builtin-list ** ** If -verbose is used, it outputs a line at the end ** with the total item count and size. ** ** List the names and sizes of all built-in resources. */ void test_builtin_list(void){ int i, size = 0;; for(i=0; i<count(aBuiltinFiles); i++){ const int n = aBuiltinFiles[i].nByte; fossil_print("%3d. %-45s %6d\n", i+1, aBuiltinFiles[i].zName,n); size += n; } if(find_option("verbose","v",0)!=0){ fossil_print("%d entries totaling %d bytes\n", i, size); } } /* ** WEBPAGE: test-builtin-files ** ** Show all built-in text files. */ void test_builtin_list_page(void){ int i; style_set_current_feature("test"); style_header("Built-in Text Files"); @ <ol> for(i=0; i<count(aBuiltinFiles); i++){ const char *z = aBuiltinFiles[i].zName; char *zUrl = href("%R/builtin?name=%T&id=%.8s&mimetype=text/plain", z,fossil_exe_id()); @ <li>%z(zUrl)%h(z)</a> } @ </ol> style_finish_page(); } /* ** 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); } /* ** Input zList is a list of numeric identifiers for files in ** aBuiltinFiles[]. Return the concatenation of all of those files ** using mimetype zType, or as text/javascript if zType is 0. */ static void builtin_deliver_multiple_js_files( const char *zList, /* List of numeric identifiers */ const char *zType /* Override mimetype */ ){ Blob *pOut; if( zType==0 ) zType = "text/javascript"; cgi_set_content_type(zType); pOut = cgi_output_blob(); while( zList[0] ){ int i = atoi(zList); if( i>0 && i<=count(aBuiltinFiles) ){ blob_appendf(pOut, "/* %s */\n", aBuiltinFiles[i-1].zName); blob_append(pOut, (const char*)aBuiltinFiles[i-1].pData, aBuiltinFiles[i-1].nByte); } while( zList[0] && fossil_isdigit(zList[0]) ) zList++; while( zList[0] && !fossil_isdigit(zList[0]) ) zList++; } return; } /* ** WEBPAGE: builtin loadavg-exempt ** ** Return one of many built-in content files. Query parameters: ** ** name=FILENAME Return the single file whose name is FILENAME. ** mimetype=TYPE Override the mimetype in the returned file to ** be TYPE. If this query parameter is omitted ** (the usual case) then the mimetype is inferred ** from the suffix on FILENAME ** m=IDLIST IDLIST is a comma-separated list of integers ** that specify multiple javascript files to be ** concatenated and returned all at once. ** id=UNIQUEID Version number of the "builtin" files. Used ** for cache control only. ** ** At least one of the name= or m= query parameters must be present. ** ** If the id= query parameter is present, then Fossil assumes that the ** result is immutable and sets a very large cache retention time (1 year). */ void builtin_webpage(void){ Blob out; const char *zName = P("name"); const char *zContent = 0; int nContent = 0; const char *zId = P("id"); const char *zType = P("mimetype"); int nId; if( zName ) zContent = (const char *)builtin_file(zName, &nContent); if( zContent==0 ){ const char *zM = P("m"); if( zM ){ if( zId && (nId = (int)strlen(zId))>=8 && strncmp(zId,fossil_exe_id(),nId)==0 ){ g.isConst = 1; } etag_check(0,0); builtin_deliver_multiple_js_files(zM, zType); return; } cgi_set_status(404, "Not Found"); @ File "%h(zName)" not found return; } if( zType==0 ){ if( sqlite3_strglob("*.js", zName)==0 ){ zType = "text/javascript"; }else{ zType = mimetype_from_name(zName); } } cgi_set_content_type(zType); if( zId && (nId = (int)strlen(zId))>=8 && strncmp(zId,fossil_exe_id(),nId)==0 ){ g.isConst = 1; } etag_check(0,0); blob_init(&out, zContent, nContent); cgi_set_content(&out); } /* Variables controlling the JS cache. */ static struct { int aReq[30]; /* Indexes of all requested built-in JS files */ int nReq; /* Number of slots in aReq[] currently used */ int nSent; /* Number of slots in aReq[] fulfilled */ int eDelivery; /* Delivery mechanism */ } builtin; #if INTERFACE /* Various delivery mechanisms. The 0 option is the default. ** MAINTENANCE NOTE: Review/update the builtin_set_js_delivery_mode() and ** builtin_get_js_delivery_mode_name() functions if values are changed/added. */ #define JS_INLINE 0 /* inline, batched together at end of file */ #define JS_SEPARATE 1 /* Separate HTTP request for each JS file */ #define JS_BUNDLED 2 /* One HTTP request to load all JS files */ /* concatenated together into a bundle */ #endif /* INTERFACE */ /* ** The argument is a request to change the javascript delivery mode. ** The argument is a string which is a command-line option or CGI ** parameter. Try to match it against one of the delivery options ** and set things up accordingly. Throw an error if no match unless ** bSilent is true. */ void builtin_set_js_delivery_mode(const char *zMode, int bSilent){ if( zMode==0 ) return; if( strcmp(zMode, "inline")==0 ){ builtin.eDelivery = JS_INLINE; }else if( strcmp(zMode, "separate")==0 ){ builtin.eDelivery = JS_SEPARATE; }else if( strcmp(zMode, "bundled")==0 ){ builtin.eDelivery = JS_BUNDLED; }else if( !bSilent ){ fossil_fatal("unknown javascript delivery mode \"%s\" - should be" " one of: inline separate bundled", zMode); } } /* ** Returns the current JS delivery mode: one of JS_INLINE, ** JS_SEPARATE, JS_BUNDLED. */ int builtin_get_js_delivery_mode(void){ return builtin.eDelivery; } /* ** Returns the name of the current JS delivery mode for reuse with the --jsmode ** option, i.e. the other way around than builtin_set_js_delivery_mode(). */ const char *builtin_get_js_delivery_mode_name(void){ switch( builtin.eDelivery ){ case JS_SEPARATE: { return "separate"; } case JS_BUNDLED: { return "bundled"; } case JS_INLINE: /*FALLTHROUGH*/ default: { return "inline"; } } } /* ** The caller wants the Javascript file named by zFilename to be ** included in the generated page. Add the file to the queue of ** requested javascript resources, if it is not there already. ** ** The current implementation queues the file to be included in the ** output later. However, the caller should not depend on that ** behavior. In the future, this routine might decide to insert ** the requested javascript inline, immedaitely, or to insert ** a <script src=..> element to reference the javascript as a ** separate resource. The exact behavior might change in the future ** so pages that use this interface must not rely on any particular ** behavior. ** ** All this routine guarantees is that the named javascript file ** will be requested by the browser at some point. This routine ** does not guarantee when the javascript will be included, and it ** does not guarantee whether the javascript will be added inline or ** delivered as a separate resource. */ void builtin_request_js(const char *zFilename){ int i = builtin_file_index(zFilename); int j; if( i<0 ){ fossil_panic("unknown javascript file: \"%s\"", zFilename); } for(j=0; j<builtin.nReq; j++){ if( builtin.aReq[j]==i ) return; /* Already queued or sent */ } if( builtin.nReq>=count(builtin.aReq) ){ fossil_panic("too many javascript files requested"); } builtin.aReq[builtin.nReq++] = i; } /* ** Fulfill all pending requests for javascript files. ** ** The current implementation delivers all javascript in-line. However, ** the caller should not depend on this. Future changes to this routine ** might choose to deliver javascript as separate resources. */ void builtin_fulfill_js_requests(void){ if( builtin.nSent>=builtin.nReq ) return; /* nothing to do */ switch( builtin.eDelivery ){ case JS_INLINE: { CX("<script nonce='%h'>\n",style_nonce()); do{ int i = builtin.aReq[builtin.nSent++]; CX("/* %s %.60c*/\n", aBuiltinFiles[i].zName, '*'); cgi_append_content((const char*)aBuiltinFiles[i].pData, aBuiltinFiles[i].nByte); }while( builtin.nSent<builtin.nReq ); CX("</script>\n"); break; } case JS_BUNDLED: { if( builtin.nSent+1<builtin.nReq ){ Blob aList; blob_init(&aList,0,0); while( builtin.nSent<builtin.nReq ){ blob_appendf(&aList, ",%d", builtin.aReq[builtin.nSent++]+1); } CX("<script src='%R/builtin?m=%s&id=%.8s'></script>\n", blob_str(&aList)+1, fossil_exe_id()); blob_reset(&aList); break; } /* If there is only one JS file, fall through into the ** JS_SEPARATE case below. */ /*FALLTHROUGH*/ } case JS_SEPARATE: { /* Each JS file as a separate resource */ while( builtin.nSent<builtin.nReq ){ int i = builtin.aReq[builtin.nSent++]; CX("<script src='%R/builtin?name=%t&id=%.8s'></script>\n", aBuiltinFiles[i].zName, fossil_exe_id()); } break; } } } /***************************************************************************** ** A virtual table for accessing the information in aBuiltinFiles[]. */ /* builtinVtab_vtab is a subclass of sqlite3_vtab which is ** underlying representation of the virtual table */ typedef struct builtinVtab_vtab builtinVtab_vtab; struct builtinVtab_vtab { sqlite3_vtab base; /* Base class - must be first */ /* Add new fields here, as necessary */ }; /* builtinVtab_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct builtinVtab_cursor builtinVtab_cursor; struct builtinVtab_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ /* Insert new fields here. For this builtinVtab we only keep track ** of the rowid */ sqlite3_int64 iRowid; /* The rowid */ }; /* ** The builtinVtabConnect() method is invoked to create a new ** builtin virtual table. ** ** Think of this routine as the constructor for builtinVtab_vtab objects. ** ** All this routine needs to do is: ** ** (1) Allocate the builtinVtab_vtab object and initialize all fields. ** ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the ** result set of queries against the virtual table will look like. */ static int builtinVtabConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ builtinVtab_vtab *pNew; int rc; rc = sqlite3_declare_vtab(db, "CREATE TABLE x(name,size,data)" ); if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); } return rc; } /* ** This method is the destructor for builtinVtab_vtab objects. */ static int builtinVtabDisconnect(sqlite3_vtab *pVtab){ builtinVtab_vtab *p = (builtinVtab_vtab*)pVtab; sqlite3_free(p); return SQLITE_OK; } /* ** Constructor for a new builtinVtab_cursor object. */ static int builtinVtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ builtinVtab_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Destructor for a builtinVtab_cursor. */ static int builtinVtabClose(sqlite3_vtab_cursor *cur){ builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; sqlite3_free(pCur); return SQLITE_OK; } /* ** Advance a builtinVtab_cursor to its next row of output. */ static int builtinVtabNext(sqlite3_vtab_cursor *cur){ builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; pCur->iRowid++; return SQLITE_OK; } /* ** Return values of columns for the row at which the builtinVtab_cursor ** is currently pointing. */ static int builtinVtabColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; const struct BuiltinFileTable *pFile = aBuiltinFiles + pCur->iRowid - 1; switch( i ){ case 0: /* name */ sqlite3_result_text(ctx, pFile->zName, -1, SQLITE_STATIC); break; case 1: /* size */ sqlite3_result_int(ctx, pFile->nByte); break; case 2: /* data */ sqlite3_result_blob(ctx, pFile->pData, pFile->nByte, SQLITE_STATIC); break; } return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. */ static int builtinVtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int builtinVtabEof(sqlite3_vtab_cursor *cur){ builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; return pCur->iRowid>count(aBuiltinFiles); } /* ** This method is called to "rewind" the builtinVtab_cursor object back ** to the first row of output. This method is always called at least ** once prior to any call to builtinVtabColumn() or builtinVtabRowid() or ** builtinVtabEof(). */ static int builtinVtabFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ builtinVtab_cursor *pCur = (builtinVtab_cursor *)pVtabCursor; pCur->iRowid = 1; return SQLITE_OK; } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. */ static int builtinVtabBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ pIdxInfo->estimatedCost = (double)count(aBuiltinFiles); pIdxInfo->estimatedRows = count(aBuiltinFiles); return SQLITE_OK; } /* ** This following structure defines all the methods for the ** virtual table. */ static sqlite3_module builtinVtabModule = { /* iVersion */ 0, /* xCreate */ 0, /* The builtin vtab is eponymous and read-only */ /* xConnect */ builtinVtabConnect, /* xBestIndex */ builtinVtabBestIndex, /* xDisconnect */ builtinVtabDisconnect, /* xDestroy */ 0, /* xOpen */ builtinVtabOpen, /* xClose */ builtinVtabClose, /* xFilter */ builtinVtabFilter, /* xNext */ builtinVtabNext, /* xEof */ builtinVtabEof, /* xColumn */ builtinVtabColumn, /* xRowid */ builtinVtabRowid, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ 0, /* xIntegrity */ 0 }; /* ** Register the builtin virtual table */ int builtin_vtab_register(sqlite3 *db){ int rc = sqlite3_create_module(db, "builtin", &builtinVtabModule, 0); return rc; } /* End of the builtin virtual table ******************************************************************************/ /* ** The first time this is called, it emits code to install and ** bootstrap the window.fossil object, using the built-in file ** fossil.bootstrap.js (not to be confused with bootstrap.js). ** ** Subsequent calls are no-ops. ** ** It emits 2 parts: ** ** 1) window.fossil core object, some of which depends on C-level ** runtime data. That part of the script is always emitted inline. If ** addScriptTag is true then it is wrapped in its own SCRIPT tag, else ** it is assumed that the caller already opened a tag. ** ** 2) Emits the static fossil.bootstrap.js using builtin_request_js(). */ void builtin_emit_script_fossil_bootstrap(int addScriptTag){ static int once = 0; if(0==once++){ char * zName; /* Set up the generic/app-agnostic parts of window.fossil ** which require C-level state... */ if(addScriptTag!=0){ style_script_begin(__FILE__,__LINE__); } CX("(function(){\n"); CX(/*MSIE NodeList.forEach polyfill, courtesy of Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill */ "if(window.NodeList && !NodeList.prototype.forEach){" "NodeList.prototype.forEach = Array.prototype.forEach;" "}\n"); CX("if(!window.fossil) window.fossil={};\n" "window.fossil.version = %!j;\n" /* fossil.rootPath is the top-most CGI/server path, ** including a trailing slash. */ "window.fossil.rootPath = %!j+'/';\n", get_version(), g.zTop); /* fossil.config = {...various config-level options...} */ CX("window.fossil.config = {"); zName = db_get("project-name", ""); CX("projectName: %!j,\n", zName); fossil_free(zName); zName = db_get("short-project-name", ""); CX("shortProjectName: %!j,\n", zName); fossil_free(zName); zName = db_get("project-code", ""); CX("projectCode: %!j,\n", zName); fossil_free(zName); CX("/* Length of UUID hashes for display purposes. */"); CX("hashDigits: %d, hashDigitsUrl: %d,\n", hash_digits(0), hash_digits(1)); CX("diffContextLines: %d,\n", diff_context_lines(0)); CX("editStateMarkers: {" "/*Symbolic markers to denote certain edit states.*/" "isNew:'[+]', isModified:'[*]', isDeleted:'[-]'},\n"); CX("confirmerButtonTicks: 3 " "/*default fossil.confirmer tick count.*/,\n"); /* Inject certain info about the current skin... */ CX("skin:{"); /* can leak a local filesystem path: CX("name: %!j,", skin_in_use());*/ CX("isDark: %s" "/*true if the current skin has the 'white-foreground' detail*/", skin_detail_boolean("white-foreground") ? "true" : "false"); CX("}\n"/*fossil.config.skin*/); CX("};\n"/* fossil.config */); CX("window.fossil.user = {"); CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest"); CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false"); CX("};\n"/*fossil.user*/); CX("if(fossil.config.skin.isDark) " "document.body.classList.add('fossil-dark-style');\n"); /* ** fossil.page holds info about the current page. This is also ** where the current page "should" store any of its own ** page-specific state, and it is reserved for that purpose. */ CX("window.fossil.page = {" "name:\"%T\"" "};\n", g.zPath); CX("})();\n"); if(addScriptTag!=0){ style_script_end(); } /* The remaining window.fossil bootstrap code is not dependent on ** C-runtime state... */ builtin_request_js("fossil.bootstrap.js"); } } /* ** Given the NAME part of fossil.NAME.js, this function checks whether ** that module has been emitted by this function before. If it has, ** it returns -1 with no side effects. If it has not, it queues up ** (via builtin_request_js()) an emit of the module via and all of its ** known (by this function) fossil.XYZ.js dependencies (in their ** dependency order) and returns 1. If it does not find the given ** module name it returns 0. ** ** As a special case, if passed 0 then it queues up all known modules ** and returns -1. ** ** The very first time this is called, it unconditionally calls ** builtin_emit_script_fossil_bootstrap(). ** ** Any given module is only queued once, whether it is explicitly ** passed to the function or resolved as a dependency. Any attempts to ** re-queue them later are harmless no-ops. */ static int builtin_emit_fossil_js_once(const char * zName){ static int once = 0; int i; static struct FossilJs { const char * zName; /* NAME part of fossil.NAME.js */ int emitted; /* True if already emitted. */ const char * zDeps; /* \0-delimited list of other FossilJs ** entries: all known deps of this one. Each ** REQUIRES an EXPLICIT trailing \0, including ** the final one! */ } fjs[] = { /* This list ordering isn't strictly important. */ {"confirmer", 0, 0}, {"copybutton", 0, "dom\0"}, {"diff", 0, "dom\0fetch\0"}, {"dom", 0, 0}, {"fetch", 0, 0}, {"numbered-lines", 0, "popupwidget\0copybutton\0"}, {"pikchr", 0, "dom\0"}, {"popupwidget", 0, "dom\0"}, {"storage", 0, 0}, {"tabs", 0, "dom\0"} }; const int nFjs = sizeof(fjs) / sizeof(fjs[0]); if(0==once){ ++once; builtin_emit_script_fossil_bootstrap(1); } if(0==zName){ for( i = 0; i < nFjs; ++i ){ builtin_emit_fossil_js_once(fjs[i].zName); } return -1; } for( i = 0; i < nFjs; ++i ){ if(0==strcmp(zName, fjs[i].zName)){ if(fjs[i].emitted){ return -1; }else{ char nameBuffer[50]; if(fjs[i].zDeps){ const char * zDep = fjs[i].zDeps; while(*zDep!=0){ builtin_emit_fossil_js_once(zDep); zDep += strlen(zDep)+1/*NUL delimiter*/; } } sqlite3_snprintf(sizeof(nameBuffer)-1, nameBuffer, "fossil.%s.js", fjs[i].zName); builtin_request_js(nameBuffer); fjs[i].emitted = 1; return 1; } } } return 0; } /* ** COMMAND: test-js-once ** ** Tester for builtin_emit_fossil_js_once(). ** ** Usage: %fossil test-js-once filename */ void test_js_once(void){ int i; if(g.argc<2){ usage("?FILENAME...?"); } if(2==g.argc){ builtin_emit_fossil_js_once(0); assert(builtin.nReq>8); }else{ for(i = 2; i < g.argc; ++i){ builtin_emit_fossil_js_once(g.argv[i]); } assert(builtin.nReq>1 && "don't forget implicit fossil.bootstrap.js"); } for(i = 0; i < builtin.nReq; ++i){ fossil_print("ndx#%d = %d = %s\n", i, builtin.aReq[i], aBuiltinFiles[builtin.aReq[i]].zName); } } /* ** Convenience wrapper which calls builtin_request_js() for a series ** of builtin scripts named fossil.NAME.js. The first time it is ** called, it also calls builtin_emit_script_fossil_bootstrap() to ** initialize the window.fossil JS API. The first argument is the NAME ** part of the first API to emit. All subsequent arguments must be ** strings of the NAME part of additional fossil.NAME.js files, ** followed by a NULL argument to terminate the list. ** ** e.g. pass it ("fetch", "dom", "tabs", NULL) to load those 3 APIs (or ** pass it ("fetch","tabs",NULL), as "dom" is a dependency of "tabs", so ** it will be automatically loaded). Do not forget the trailing NULL, ** and do not pass 0 instead, since that isn't always equivalent to NULL ** in this context. ** ** If it is JS_BUNDLED then this routine queues up an emit of ALL of ** the JS fossil.XYZ.js APIs which are not strictly specific to a ** single page, and then calls builtin_fulfill_js_requests(). The idea ** is that we can get better bundle caching and reduced HTTP requests ** by including all JS, rather than creating separate bundles on a ** per-page basis. In this case, all arguments are ignored! ** ** This function has an internal mapping of the dependencies for each ** of the known fossil.XYZ.js modules and ensures that the ** dependencies also get queued (recursively) and that each module is ** queued only once. ** ** If passed a name which is not a base fossil module name then it ** will fail fatally! ** ** DO NOT use this for loading fossil.page.*.js: use ** builtin_request_js() for those. ** ** If the current JS delivery mode is *not* JS_BUNDLED then this ** function queues up a request for each given module and its known ** dependencies, but does not immediately fulfill the request, thus it ** can be called multiple times. ** ** If a given module is ever passed to this more than once, either in ** a single invocation or multiples, it is only queued for emit a ** single time (i.e. the 2nd and subsequent ones become ** no-ops). Likewise, if a module is requested but was already ** automatically queued to fulfill a dependency, the explicit request ** becomes a no-op. ** ** Bundled mode is the only mode in which this API greatly improves ** aggregate over-the-wire and HTTP request costs. For other modes, ** reducing the inclusion of fossil.XYZ APIs to their bare minimum ** provides the lowest aggregate costs. For debate and details, see ** the discussion at: ** ** https://fossil-scm.org/forum/forumpost/3fa2633f3e ** ** In practice it is normally necessary (or preferred) to call ** builtin_fulfill_js_requests() after calling this, before proceeding ** to call builtin_request_js() for page-specific JS, in order to ** improve cachability. ** ** Minor caveat: the purpose of emitting all of the fossil.XYZ JS APIs ** at once is to reduce over-the-wire transfers by enabling cross-page ** caching, but if there are other JS scripts pending via ** builtin_request_js() when this is called then they will be included ** in the JS request emitted by this routine, resulting in a different ** script URL than if they were not included. Thus, if a given page ** has its own scripts to install via builtin_request_js(), they ** should, if possible, be delayed until after this is called OR the ** page should call builtin_fulfill_js_requests() to flush the request ** queue before calling this routine. ** ** Achtung: the fossil.page.XYZ.js files are page-specific, containing ** the app-level logic for that specific page, and loading more than ** one of them in a single page will break that page. Each of those ** expects to "own" the page it is loaded in, and it should be loaded ** as late in the JS-loading process as feasible, ideally bundled (via ** builtin_request_js()) with any other app-/page-specific JS it may ** need. ** ** Example usage: ** ** builtin_fossil_js_bundle_or("dom", "fetch", NULL); ** ** In bundled mode, that will (the first time it is called) emit all ** builtin fossil JS APIs and "fulfill" the queue immediately. In ** non-bundled mode it will queue up the "dom" and "fetch" APIs to be ** emitted the next time builtin_fulfill_js_requests() is called. */ NULL_SENTINEL void builtin_fossil_js_bundle_or( const char * zApi, ... ) { static int bundled = 0; const char *zArg; va_list vargs; if(JS_BUNDLED == builtin_get_js_delivery_mode()){ if(!bundled){ bundled = 1; builtin_emit_fossil_js_once(0); builtin_fulfill_js_requests(); } return; } va_start(vargs,zApi); for( zArg = zApi; zArg!=NULL; (zArg = va_arg (vargs, const char *))){ if(0==builtin_emit_fossil_js_once(zArg)){ fossil_fatal("Unknown fossil JS module: %s\n", zArg); } } va_end(vargs); } |
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 | /* ** 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 main repository. If NULL, then ** data contains content 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, -- 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, ExtFILE)<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, octet_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 ** already 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], ExtFILE); 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','parent-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 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, 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)>=HNAME_MIN ){ 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; } if( hname_verify_hash(&c2, blob_buffer(&h1), blob_size(&h1))==0 ){ fossil_fatal("artifact hash error on %b", &h1); } 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; 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); if( hname_verify_hash(pOut, blob_buffer(&h1), blob_size(&h1))==0 ){ fossil_fatal("incorrect hash for artifact %b", &h1); } blob_reset(&h1); bag_remove(&busy, blobid); db_finalize(&q); } /* fossil bundle cat BUNDLE HASH... ** ** 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 HASH..."); bundle_attach_file(g.argv[3], "b1", 0); 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", 0); /* 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 octet_length(delta)>=%d" " AND NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.delta)", HNAME_MIN); 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 HASH... ** ** 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 to make 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". ** ** 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 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 | /* ** 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, ExtFILE); if( sz<=0 ){ fossil_free(zDbName); return 0; } } rc = sqlite3_open(zDbName, &db); fossil_free(zDbName); if( rc ){ sqlite3_close(db); return 0; } sqlite3_busy_timeout(db, 5000); if( sqlite3_table_column_metadata(db,0,"blob","key",0,0,0,0,0)!=SQLITE_OK ){ 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. ** ** The cache entry replacement algorithm is approximately LRU ** (least recently used). However, each access of an entry buys ** that entry an extra hour of grace, so that more commonly accessed ** entries are held in cache longer. The extra "grace" allotted to ** an entry is limited to 2 days worth. */ 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 + 3600*min(nRef,48)) 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); } /* ** SETTING: max-cache-entry width=10 default=10 ** ** This is the maximum number of entries to allow in the web-cache ** for tarballs, ZIP-archives, and SQL-archives. */ /* ** 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 can be: ** ** clear Remove all entries from the cache. ** ** init Create the cache file if it does not already exist. ** ** list|ls List the keys and content sizes and other stats for ** all entries currently in the cache. ** ** size ?N? Query or set the maximum number of entries in the cache. ** ** status Show a summary of the 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. The 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 || strncmp(zCmd, "status", 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 ){ if( zCmd[0]=='l' ){ 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( "Filename: %s\n" "Entries: %d\n" "max-cache-entry: %d\n" "Cache-file Size: %,lld\n", zDbName, nEntry, db_get_int("max-cache-entry",10), file_size(zDbName, ExtFILE) ); fossil_free(zDbName); } }else if( strncmp(zCmd, "size", nCmd)==0 ){ if( g.argc>=4 ){ int n = atoi(g.argv[3]); if( n>=5 ) db_set_int("max-cache-entry",n,0); } fossil_print("max-cache-entry: %d\n", db_get_int("max-cache-entry",10)); }else{ fossil_fatal("Unknown subcommand \"%s\"." " Should be one of: clear init list size status", zCmd); } } /* ** Given a cache key, find the check-in hash and return it as a separate ** string. The returned string is obtained from fossil_malloc() and must ** be freed by the caller. ** ** Return NULL if not found. ** ** The key is usually in a format like these: ** ** /tarball/HASH/NAME ** /zip/HASH/NAME ** /sqlar/HASH/NAME */ static char *cache_hash_of_key(const char *zKey){ int i; if( zKey==0 ) return 0; if( zKey[0]!='/' ) return 0; zKey++; while( zKey[0] && zKey[0]!='/' ) zKey++; if( zKey[0]==0 ) return 0; zKey++; for(i=0; zKey[i] && zKey[i]!='/'; i++){} if( !validate16(zKey, i) ) return 0; return fossil_strndup(zKey, i); } /* ** WEBPAGE: cachestat ** ** Show information about the webpage cache. Requires Setup privilege. */ void cache_page(void){ sqlite3 *db; sqlite3_stmt *pStmt; char zBuf[100]; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_set_current_feature("cache"); 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, sz, nRef, datetime(tm,'unixepoch')" " FROM cache" " ORDER BY (tm + 3600*min(nRef,48)) DESC" ); if( pStmt ){ @ <ol> while( sqlite3_step(pStmt)==SQLITE_ROW ){ const unsigned char *zName = sqlite3_column_text(pStmt,0); char *zHash = cache_hash_of_key((const char*)zName); @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br> @ size: %,lld(sqlite3_column_int64(pStmt,1)) @ hit-count: %d(sqlite3_column_int(pStmt,2)) @ last-access: %s(sqlite3_column_text(pStmt,3)) \ if( zHash ){ @ %z(href("%R/timeline?c=%S",zHash))check-in</a>\ fossil_free(zHash); } @ </p></li> } sqlite3_finalize(pStmt); @ </ol> } zDbName = cacheName(); bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName, ExtFILE)); @ <p> @ cache-file name: %h(zDbName)<br> @ cache-file size: %s(zBuf)<br> @ max-cache-entry: %d(db_get_int("max-cache-entry",10)) @ </p> @ <p> @ Use the "<a href="%R/help?cmd=cache">fossil cache</a>" command @ on the command-line to create and configure the web-cache. @ </p> fossil_free(zDbName); sqlite3_close(db); } style_finish_page(); } /* ** 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. ** Requires Admin privilege. */ 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_set_current_feature("cache"); style_header("Cache Download Error"); @ The cache does not contain any entry with this key: "%h(zKey)" style_finish_page(); return; } cgi_set_content(&content); cgi_set_content_type("application/x-compressed"); } |
Added src/capabilities.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 | /* ** Copyright (c) 2018 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 managing user capability strings. */ #include "config.h" #include "capabilities.h" #include <assert.h> #if INTERFACE /* ** A capability string object holds all defined capabilities in a ** vector format that is subject to boolean operations. */ struct CapabilityString { unsigned char x[128]; }; #endif /* ** Add capabilities to a CapabilityString. If pIn is NULL, then create ** a new capability string. ** ** Call capability_free() on the allocated CapabilityString object to ** deallocate. */ CapabilityString *capability_add(CapabilityString *pIn, const char *zCap){ int c; int i; if( pIn==0 ){ pIn = fossil_malloc( sizeof(*pIn) ); memset(pIn, 0, sizeof(*pIn)); } if( zCap ){ for(i=0; (c = zCap[i])!=0; i++){ if( c>='0' && c<='z' ) pIn->x[c] = 1; } } return pIn; } /* ** Remove capabilities from a CapabilityString. */ CapabilityString *capability_remove(CapabilityString *pIn, const char *zCap){ int c; int i; if( pIn==0 ){ pIn = fossil_malloc( sizeof(*pIn) ); memset(pIn, 0, sizeof(*pIn)); } if( zCap ){ for(i=0; (c = zCap[i])!=0; i++){ if( c>='0' && c<='z' ) pIn->x[c] = 0; } } return pIn; } /* ** Return true if any of the capabilities in zNeeded are found in pCap */ int capability_has_any(CapabilityString *p, const char *zNeeded){ if( p==0 ) return 0; if( zNeeded==0 ) return 0; while( zNeeded[0] ){ int c = zNeeded[0]; if( fossil_isalnum(c) && p->x[c] ) return 1; zNeeded++; } return 0; } /* ** Delete a CapabilityString object. */ void capability_free(CapabilityString *p){ fossil_free(p); } /* ** Expand the capability string by including all capabilities for ** special users "nobody" and "anonymous". Also include "reader" ** if "u" is present and "developer" if "v" is present. */ void capability_expand(CapabilityString *pIn){ static char *zNobody = 0; static char *zAnon = 0; static char *zReader = 0; static char *zDev = 0; static char *zAdmin = "bcdefghijklmnopqrtwz234567AD"; int doneV = 0; if( pIn==0 ){ fossil_free(zNobody); zNobody = 0; fossil_free(zAnon); zAnon = 0; fossil_free(zReader); zReader = 0; fossil_free(zDev); zDev = 0; return; } if( zNobody==0 ){ zNobody = db_text(0, "SELECT cap FROM user WHERE login='nobody'"); zAnon = db_text(0, "SELECT cap FROM user WHERE login='anonymous'"); zReader = db_text(0, "SELECT cap FROM user WHERE login='reader'"); zDev = db_text(0, "SELECT cap FROM user WHERE login='developer'"); } pIn = capability_add(pIn, zAnon); pIn = capability_add(pIn, zNobody); if( pIn->x['a'] || pIn->x['s'] ){ pIn = capability_add(pIn, zAdmin); } if( pIn->x['v'] ){ pIn = capability_add(pIn, zDev); doneV = 1; } if( pIn->x['u'] ){ pIn = capability_add(pIn, zReader); if( pIn->x['v'] && !doneV ){ pIn = capability_add(pIn, zDev); } } } /* ** Render a capability string in canonical string format. Space to hold ** the returned string is obtained from fossil_malloc() can should be freed ** by the caller. */ char *capability_string(CapabilityString *p){ Blob out; int i; int j = 0; char buf[100]; blob_init(&out, 0, 0); for(i='a'; i<='z'; i++){ if( p->x[i] ) buf[j++] = i; } for(i='0'; i<='9'; i++){ if( p->x[i] ) buf[j++] = i; } for(i='A'; i<='Z'; i++){ if( p->x[i] ) buf[j++] = i; } buf[j] = 0; return fossil_strdup(buf); } /* ** The next two routines implement an aggregate SQL function that ** takes multiple capability strings and in the end returns their ** union. Example usage: ** ** SELECT capunion(cap) FROM user WHERE login IN ('nobody','anonymous'); */ void capability_union_step( sqlite3_context *context, int argc, sqlite3_value **argv ){ CapabilityString *p; const char *zIn; zIn = (const char*)sqlite3_value_text(argv[0]); if( zIn==0 ) return; p = (CapabilityString*)sqlite3_aggregate_context(context, sizeof(*p)); p = capability_add(p, zIn); } void capability_union_finalize(sqlite3_context *context){ CapabilityString *p; p = sqlite3_aggregate_context(context, 0); if( p ){ char *zOut = capability_string(p); sqlite3_result_text(context, zOut, -1, fossil_free); } } /* ** The next routines takes the raw USER.CAP field and expands it with ** capabilities from special users. Example: ** ** SELECT fullcap(cap) FROM user WHERE login=?1 */ void capability_fullcap( sqlite3_context *context, int argc, sqlite3_value **argv ){ CapabilityString *p; const char *zIn; char *zOut; zIn = (const char*)sqlite3_value_text(argv[0]); if( zIn==0 ) zIn = ""; p = capability_add(0, zIn); capability_expand(p); zOut = capability_string(p); sqlite3_result_text(context, zOut, -1, fossil_free); capability_free(p); } #if INTERFACE /* ** Capabilities are grouped into "classes" as follows: */ #define CAPCLASS_CODE 0x0001 #define CAPCLASS_WIKI 0x0002 #define CAPCLASS_TKT 0x0004 #define CAPCLASS_FORUM 0x0008 #define CAPCLASS_DATA 0x0010 #define CAPCLASS_ALERT 0x0020 #define CAPCLASS_OTHER 0x0040 #define CAPCLASS_SUPER 0x0080 #define CAPCLASS_ALL 0xffff #endif /* INTERFACE */ /* ** The following structure holds descriptions of the various capabilities. */ static struct Caps { char cCap; /* The capability letter */ unsigned short eClass; /* The "class" for this capability */ unsigned nUser; /* Number of users with this capability */ char *zAbbrev; /* Abbreviated mnemonic name */ char *zOneLiner; /* One-line summary */ } aCap[] = { { 'a', CAPCLASS_SUPER, 0, "Admin", "Create and delete users" }, { 'b', CAPCLASS_WIKI|CAPCLASS_TKT, 0, "Attach", "Add attachments to wiki or tickets" }, { 'c', CAPCLASS_TKT, 0, "Append-Tkt", "Append to existing tickets" }, /* ** d unused since fork from CVSTrac; ** see https://fossil-scm.org/forum/forumpost/43c78f4bef */ { 'e', CAPCLASS_DATA, 0, "View-PII", "View sensitive info such as email addresses" }, { 'f', CAPCLASS_WIKI, 0, "New-Wiki", "Create new wiki pages" }, { 'g', CAPCLASS_DATA, 0, "Clone", "Clone the repository" }, { 'h', CAPCLASS_OTHER, 0, "Hyperlinks", "Show hyperlinks to detailed repository history" }, { 'i', CAPCLASS_CODE, 0, "Check-In", "Check-in code changes" }, { 'j', CAPCLASS_WIKI, 0, "Read-Wiki", "View wiki pages" }, { 'k', CAPCLASS_WIKI, 0, "Write-Wiki", "Edit wiki pages" }, { 'l', CAPCLASS_WIKI|CAPCLASS_SUPER, 0, "Mod-Wiki", "Moderator for wiki pages" }, { 'm', CAPCLASS_WIKI, 0, "Append-Wiki", "Append to wiki pages" }, { 'n', CAPCLASS_TKT, 0, "New-Tkt", "Create new tickets" }, { 'o', CAPCLASS_CODE, 0, "Check-Out", "Check out code" }, { 'p', CAPCLASS_OTHER, 0, "Password", "Change your own password" }, { 'q', CAPCLASS_TKT|CAPCLASS_SUPER, 0, "Mod-Tkt", "Moderate tickets" }, { 'r', CAPCLASS_TKT, 0, "Read-Tkt", "View tickets" }, { 's', CAPCLASS_SUPER, 0, "Superuser", "Setup and configure the repository" }, { 't', CAPCLASS_TKT, 0, "Reports", "Create new ticket report formats" }, { 'u', CAPCLASS_OTHER, 0, "Reader", "Inherit all the capabilities of the \"reader\" user" }, { 'v', CAPCLASS_OTHER, 0, "Developer", "Inherit all capabilities of the \"developer\" user" }, { 'w', CAPCLASS_TKT, 0, "Write-Tkt", "Edit tickets" }, { 'x', CAPCLASS_DATA, 0, "Private", "Push and/or pull private branches" }, { 'y', CAPCLASS_SUPER, 0, "Write-UV", "Push unversioned content" }, { 'z', CAPCLASS_CODE, 0, "Zip-Download", "Download a ZIP archive, tarball, or SQL archive" }, { '2', CAPCLASS_FORUM, 0, "Forum-Read", "Read forum posts by others" }, { '3', CAPCLASS_FORUM, 0, "Forum-Write", "Create new forum messages" }, { '4', CAPCLASS_FORUM, 0, "Forum-Trusted", "Create forum messages that bypass moderation" }, { '5', CAPCLASS_FORUM|CAPCLASS_SUPER, 0, "Forum-Mod", "Moderator for forum messages" }, { '6', CAPCLASS_FORUM|CAPCLASS_SUPER, 0, "Forum-Admin", "Grant capability '4' to other users" }, { '7', CAPCLASS_ALERT, 0, "Alerts", "Sign up for email alerts" }, { 'A', CAPCLASS_ALERT|CAPCLASS_SUPER, 0, "Announce", "Send announcements to all subscribers" }, { 'C', CAPCLASS_FORUM, 0, "Chat", "Read and/or writes messages in the chatroom" }, { 'D', CAPCLASS_OTHER, 0, "Debug", "Enable debugging features" }, }; /* ** Populate the aCap[].nUser values based on the current content ** of the USER table. */ void capabilities_count(void){ int i; static int done = 0; Stmt q; if( done ) return; db_prepare(&q, "SELECT fullcap(cap) FROM user"); while( db_step(&q)==SQLITE_ROW ){ const char *zCap = db_column_text(&q, 0); if( zCap==0 || zCap[0]==0 ) continue; for(i=0; i<(int)(sizeof(aCap)/sizeof(aCap[0])); i++){ if( strchr(zCap, aCap[i].cCap) ) aCap[i].nUser++; } } db_finalize(&q); done = 1; } /* ** Generate HTML that lists all of the capability letters together with ** a brief summary of what each letter means. */ void capabilities_table(unsigned mClass){ int i; if( g.perm.Admin ) capabilities_count(); @ <table> @ <tbody> for(i=0; i<(int)(sizeof(aCap)/sizeof(aCap[0])); i++){ int n; if( (aCap[i].eClass & mClass)==0 ) continue; @ <tr><th valign="top">%c(aCap[i].cCap)</th> @ <td>%h(aCap[i].zAbbrev)</td><td>%h(aCap[i].zOneLiner)</td>\ n = aCap[i].nUser; if( n && g.perm.Admin ){ @ <td><a href="%R/setup_ulist?with=%c(aCap[i].cCap)">\ @ %d(n) user%s(n>1?"s":"")</a></td>\ } @ </tr> } @ </tbody> @ </table> } /* ** Generate a "capability summary table" that shows the major capabilities ** against the various user categories. */ void capability_summary(void){ Stmt q; CapabilityString *pCap; char *zSelfCap; char *zPubPages = db_get("public-pages",0); int hasPubPages = zPubPages && zPubPages[0]; pCap = capability_add(0, db_get("default-perms","u")); capability_expand(pCap); zSelfCap = capability_string(pCap); capability_free(pCap); db_prepare(&q, "WITH t(id,seq) AS (VALUES('nobody',1),('anonymous',2),('reader',3)," "('developer',4))" " SELECT id, CASE WHEN user.login='nobody' THEN user.cap" " ELSE fullcap(user.cap) END,seq,1" " FROM t LEFT JOIN user ON t.id=user.login" " UNION ALL" " SELECT 'Public Pages', %Q, 100, %d" " UNION ALL" " SELECT 'New User Default', %Q, 110, 1" " UNION ALL" " SELECT 'Regular User', fullcap(capunion(cap)), 200, count(*) FROM user" " WHERE cap NOT GLOB '*[as]*' AND login NOT IN (SELECT id FROM t)" " UNION ALL" " SELECT 'Administrator', fullcap(capunion(cap)), 300, count(*) FROM user" " WHERE cap GLOB '*[as]*'" " ORDER BY 3 ASC", zSelfCap, hasPubPages, zSelfCap ); @ <table id='capabilitySummary' cellpadding="0" cellspacing="0" border="1"> @ <tr><th> <th>Code<th>Forum<th>Tickets<th>Wiki<th>Chat\ @ <th>Unversioned Content</th></tr> while( db_step(&q)==SQLITE_ROW ){ const char *zId = db_column_text(&q, 0); const char *zCap = db_column_text(&q, 1); int n = db_column_int(&q, 3); int eType; static const char *const azType[] = { "off", "read", "write" }; static const char *const azClass[] = { "capsumOff", "capsumRead", "capsumWrite" }; if( n==0 ) continue; /* Code */ if( db_column_int(&q,2)<10 ){ @ <tr><th align="right"><tt>"%h(zId)"</tt></th> }else if( n>1 ){ @ <tr><th align="right">%d(n) %h(zId)s</th> }else{ @ <tr><th align="right">%h(zId)</th> } if( sqlite3_strglob("*[asi]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*[oz]*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> /* Forum */ if( sqlite3_strglob("*[as3456]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*2*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> /* Ticket */ if( sqlite3_strglob("*[ascnqtw]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*r*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> /* Wiki */ if( sqlite3_strglob("*[asdfklm]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*j*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> /* Chat */ if( sqlite3_strglob("*C*",zCap)==0 ){ eType = 2; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> /* Unversioned */ if( sqlite3_strglob("*y*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*[ioas]*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> } db_finalize(&q); @ </table> } |
Changes to src/captcha.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 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** 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{ |
︙ | ︙ | |||
67 68 69 70 71 72 73 | /* ** Render an 8-character hexadecimal string as ascii art. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ char *captcha_render(const char *zPw){ | | | | | | | 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 | /* ** Render an 8-character hexadecimal string as ascii art. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ 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 */ " __ ", " / \\ ", "| () |", " \\__/ ", /* 1 */ |
︙ | ︙ | |||
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 */ " ___ ", "/ _ \\", |
︙ | ︙ | |||
200 201 202 203 204 205 206 | /* ** Render an 8-digit hexadecimal string as ascii arg. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ char *captcha_render(const char *zPw){ | | | | | | | | | | | | | | | | | | | | | | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Render an 8-digit hexadecimal string as ascii arg. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ char *captcha_render(const char *zPw){ 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 */ " ______ ", "| ____|", "| |__ ", "| __| ", "| | ", "|_| ", }; /* ** Render an 8-digit hexadecimal string as ascii arg. ** Space to hold the result is obtained from malloc() and should be freed ** by the caller. */ char *captcha_render(const char *zPw){ char *z = fossil_malloc( 10*6*strlen(zPw) + 7 ); int i, j, k, m; const char *zChar; 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: y = 2; break; case 0xf6: case 0xa9: case 0xa4: case 0xa1: case 0x9a: case 0x76: case 0x61: case 0x67: case 0x69: case 0x41: case 0x42: case 0x43: case 0x4a: y = 1; break; default: y = 0; break; } zChar = azFont3[6*v + i]; while( y && zChar[0]==' ' ){ y--; zChar++; } while( y && z[k-1]==' ' ){ y--; k--; } for(m=0; zChar[m]; m++){ z[k++] = zChar[m]; } } z[k++] = '\n'; } z[k] = 0; return z; } #endif /* CAPTCHA==3 */ /* ** COMMAND: test-captcha ** ** Render an ASCII-art captcha for numbers given on the command line. */ void test_captcha(void){ int i; unsigned int v; char *z; for(i=2; i<g.argc; i++){ char zHex[30]; v = (unsigned int)atoi(g.argv[i]); sqlite3_snprintf(sizeof(zHex), zHex, "%x", v); z = captcha_render(zHex); fossil_print("%s:\n%s", zHex, z); free(z); } } /* ** Compute a seed value for a captcha. The seed is public and is sent ** as a hidden parameter with the page that contains the captcha. Knowledge ** of the seed is insufficient for determining the captcha without additional ** information held only on the server and never revealed. */ unsigned int captcha_seed(void){ unsigned int x; sqlite3_randomness(sizeof(x), &x); x &= 0x7fffffff; return x; } /* ** 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 ){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "REPLACE INTO config(name,value)" " VALUES('captcha-secret', lower(hex(randomblob(20))));" ); db_protect_pop(); zSecret = db_get("captcha-secret", 0); assert( zSecret!=0 ); } blob_init(&b, 0, 0); blob_appendf(&b, "%s-%x", zSecret, seed); sha1sum_blob(&b, &b); z = blob_buffer(&b); memcpy(zRes, z, 8); zRes[8] = 0; return zRes; } /* ** 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 ** true (non-zero). ** ** The query parameters examined are "captchaseed" for the seed value and ** "captcha" for text that the user types in response to the captcha prompt. */ int captcha_is_correct(int bAlwaysNeeded){ const char *zSeed; const char *zEntered; const char *zDecode; char z[30]; int i; if( !bAlwaysNeeded && !captcha_needed() ){ return 1; /* No captcha needed */ } zSeed = P("captchaseed"); if( zSeed==0 ) return 0; zEntered = P("captcha"); if( zEntered==0 || strlen(zEntered)!=8 ) return 0; zDecode = captcha_decode((unsigned int)atoi(zSeed)); assert( strlen(zDecode)==8 ); 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. ** ** This routine is a no-op if no captcha is required. */ void captcha_generate(int showButton){ unsigned int uSeed; const char *zDecoded; char *zCaptcha; if( !captcha_needed() ) return; uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> @ %h(zCaptcha) @ </pre> @ Enter security code shown above: @ <input type="hidden" name="captchaseed" value="%u(uSeed)"> @ <input type="text" name="captcha" size=8> if( showButton ){ @ <input type="submit" value="Submit"> } @ <br/>\ captcha_speakit_button(uSeed, 0); @ </td></tr></table></div> } /* ** Add a "Speak the captcha" button. */ void captcha_speakit_button(unsigned int uSeed, const char *zMsg){ if( zMsg==0 ) zMsg = "Speak the text"; @ <input aria-label="%h(zMsg)" type="button" value="%h(zMsg)" \ @ id="speakthetext"> @ <script nonce="%h(style_nonce())">/* captcha_speakit_button() */ @ document.getElementById("speakthetext").onclick = function(){ @ var audio = window.fossilAudioCaptcha \ @ || new Audio("%R/captcha-audio/%u(uSeed)"); @ window.fossilAudioCaptcha = audio; @ audio.currentTime = 0; @ audio.play(); @ } @ </script> } /* ** WEBPAGE: test-captcha ** Test the captcha-generator by rendering the value of the name= query ** parameter using ascii-art. If name= is omitted, show a random 16-digit ** hexadecimal number. */ void captcha_test(void){ const char *zPw = P("name"); if( zPw==0 || zPw[0]==0 ){ u64 x; sqlite3_randomness(sizeof(x), &x); zPw = mprintf("%016llx", x); } style_set_current_feature("test"); style_header("Captcha Test"); @ <pre> @ %s(captcha_render(zPw)) @ </pre> style_finish_page(); } /* ** Check to see if the current request is coming from an agent that might ** be a spider. If the agent is not a spider, then return 0 without doing ** anything. But if the user agent appears to be a spider, offer ** a captcha challenge to allow the user agent to prove that it is human ** and return non-zero. */ int exclude_spiders(void){ const char *zCookieValue; char *zCookieName; if( g.isHuman ) return 0; #if 0 { const char *zReferer = P("HTTP_REFERER"); if( zReferer && strncmp(g.zBaseURL, zReferer, strlen(g.zBaseURL))==0 ){ return 0; } } #endif zCookieName = mprintf("fossil-cc-%.10s", db_get("project-code","x")); zCookieValue = P(zCookieName); if( zCookieValue && atoi(zCookieValue)==1 ) return 0; if( captcha_is_correct(0) ){ cgi_set_cookie(zCookieName, "1", login_cookie_path(), 8*3600); return 0; } /* This appears to be a spider. Offer the captcha */ style_set_current_feature("captcha"); style_header("Verification"); @ <form method='POST' action='%s(g.zPath)'> cgi_query_parameters_to_hidden(); @ <p>Please demonstrate that you are human, not a spider or robot</p> captcha_generate(1); @ </form> style_finish_page(); return 1; } /* ** Generate a WAV file that reads aloud the hex digits given by ** zHex. */ static void captcha_wav(const char *zHex, Blob *pOut){ int i; const int szWavHdr = 44; blob_init(pOut, 0, 0); blob_resize(pOut, szWavHdr); /* Space for the WAV header */ pOut->nUsed = szWavHdr; memset(pOut->aData, 0, szWavHdr); if( zHex==0 || zHex[0]==0 ) zHex = "0"; for(i=0; zHex[i]; i++){ int v = hex_digit_value(zHex[i]); int sz; int nData; const unsigned char *pData; char zSoundName[50]; sqlite3_snprintf(sizeof(zSoundName),zSoundName,"sounds/%c.wav", "0123456789abcdef"[v]); /* Extra silence in between letters */ if( i>0 ){ int nQuiet = 3000; blob_resize(pOut, pOut->nUsed+nQuiet); memset(pOut->aData+pOut->nUsed-nQuiet, 0x80, nQuiet); } pData = builtin_file(zSoundName, &sz); nData = sz - szWavHdr; blob_resize(pOut, pOut->nUsed+nData); memcpy(pOut->aData+pOut->nUsed-nData, pData+szWavHdr, nData); if( zHex[i+1]==0 ){ int len = pOut->nUsed + 36; memcpy(pOut->aData, pData, szWavHdr); pOut->aData[4] = (char)(len&0xff); pOut->aData[5] = (char)((len>>8)&0xff); pOut->aData[6] = (char)((len>>16)&0xff); pOut->aData[7] = (char)((len>>24)&0xff); len = pOut->nUsed; pOut->aData[40] = (char)(len&0xff); pOut->aData[41] = (char)((len>>8)&0xff); pOut->aData[42] = (char)((len>>16)&0xff); pOut->aData[43] = (char)((len>>24)&0xff); } } } /* ** WEBPAGE: /captcha-audio ** ** Return a WAV file that pronounces the digits of the captcha that ** is determined by the seed given in the name= query parameter. */ void captcha_wav_page(void){ const char *zSeed = PD("name","0"); const char *zDecode = captcha_decode((unsigned int)atoi(zSeed)); Blob audio; captcha_wav(zDecode, &audio); cgi_set_content_type("audio/wav"); cgi_set_content(&audio); } /* ** WEBPAGE: /test-captcha-audio ** ** Return a WAV file that pronounces the hex digits of the name= ** query parameter. */ void captcha_test_wav_page(void){ const char *zSeed = P("name"); Blob audio; captcha_wav(zSeed, &audio); cgi_set_content_type("audio/wav"); cgi_set_content(&audio); } |
Changes to src/cgi.c.
1 2 3 4 5 6 | /* ** 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".) | | | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | > > > > | > | > > > > > > > > > | < > > > | < | > > | | > > | | > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > | | 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 | /* ** 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 began as a set of C functions and procedures used intepret ** CGI environment variables for Fossil web pages that were invoked by ** CGI. That's where the file name comes from. But over the years it ** has grown to incorporate lots of related functionality, including: ** ** * Interpreting CGI environment variables when Fossil is run as ** CGI (the original purpose). ** ** * Interpreting HTTP requests received directly or via an SSH tunnel. ** ** * Interpreting SCGI requests ** ** * Generating appropriate replies to CGI, SCGI, and HTTP requests. ** ** * Listening for incoming HTTP requests and dispatching them. ** (Used by "fossil ui" and "fossil server", for example). ** ** So, even though the name of this file implies that it only deals with ** CGI, in fact, the code in this file is used to interpret webpage requests ** received by a variety of means, and to generate well-formatted replies ** to those requests. ** ** The code in this file abstracts the web-request so that downstream ** modules that generate the body of the reply (based on the requested page) ** do not need to know if the request is coming from CGI, direct HTTP, ** SCGI, or some other means. ** ** This module gathers information about web page request into a key/value ** store. Keys and values come from: ** ** * Query parameters ** * POST parameter ** * Cookies ** * Environment variables ** ** The parameters are accessed using cgi_parameter() and similar functions ** or their convenience macros P() and similar. ** ** Environment variable parameters are set as if the request were coming ** in over CGI even if the request arrived via SCGI or direct HTTP. Thus ** the downstream modules that are trying to interpret the request do not ** need to know the request protocol - they can just request the values ** of environment variables and everything will always work. ** ** This file contains routines used by Fossil when it is acting as a ** CGI client. For the code used by Fossil when it is acting as a ** CGI server (for the /ext webpage) see the "extcgi.c" source file. */ #include "config.h" #ifdef _WIN32 # if !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0501 # endif # include <winsock2.h> # include <ws2tcpip.h> #else # include <sys/socket.h> # include <netinet/in.h> # include <arpa/inet.h> # include <sys/times.h> # include <sys/time.h> # include <sys/wait.h> # include <sys/select.h> #endif #ifdef __EMX__ typedef int socklen_t; #endif #include <time.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include "cgi.h" #include "cygsup.h" #if INTERFACE /* ** Shortcuts for cgi_parameter. P("x") returns the value of query parameter ** 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) #define PCK(x) cgi_parameter_checked(x,1) #define PIF(x,y) cgi_parameter_checked(x,y) #define P_NoBot(x) cgi_parameter_nosql((x),0) #define PD_NoBot(x,y) cgi_parameter_nosql((x),(y)) /* ** Shortcut for the cgi_printf() routine. Instead of using the ** ** @ ... ** ** notation provided by the translate.c utility, you can also ** optionally use: ** ** CX(...) */ #define CX cgi_printf /* ** 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 reply content is generated in two pieces: the header and the body. ** These pieces are generated separately because they are not necessarily ** 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 ** Blob structures then output sequentially once everything has been ** built. ** ** Do not confuse the content header with the HTTP header. The content header ** is generated by downstream code. The HTTP header is generated by the ** cgi_reply() routine below. ** ** The content header and contenty body are *approximately* the <head> ** element and the <body> elements for HTML replies. However this is only ** approximate. The content header also includes parts of <body> that ** show the banner and menu bar at the top of each page. Also note that ** not all replies are HTML, but there can still be separate header and ** body sections of the content. ** ** The cgi_destination() interface switches between the buffers. */ static Blob cgiContent[2] = { BLOB_INITIALIZER, BLOB_INITIALIZER }; static Blob *pContent = &cgiContent[0]; /* ** Set the destination buffer into which to accumulate CGI content. */ |
︙ | ︙ | |||
94 95 96 97 98 99 100 | default: { cgi_panic("bad destination"); } } } /* | > > > > > > > > > > > | | | > > > > > > > | > > | | | | > > | > > > > | > > > | | | > > > > > | > | > | > > > > | > > > > > | < | | | | | | | | | | | | | < < < | < < < < | > | < | | > | < < < < < < | < < < | < < < < < < | < | | | | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | | < | | < | | | < < < | | | > > | > > > > > > > > > > > > > > > > > > > > | | | | | | > > > | < > > > | > > > | < < < < < | | | < < | | | | | | > > > > > > > > > > > > > > > > | | > > | > > > | > > > > > > | > > > | > > > > > > > > > | > | > > > > | > > > > > | | | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | > > > > | < > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < > > > > > > | > | | > > | > > > > | 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 | default: { cgi_panic("bad destination"); } } } /* ** Check to see if the content header or body 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 new reply content to what already exists. */ void cgi_append_content(const char *zData, int nAmt){ blob_append(pContent, zData, nAmt); } /* ** Reset both reply content buffers to be empty. */ void cgi_reset_content(void){ blob_reset(&cgiContent[0]); blob_reset(&cgiContent[1]); } /* ** Return a pointer to Blob that is currently accumulating reply content. */ Blob *cgi_output_blob(void){ return pContent; } /* ** Return the content header as a text string */ const char *cgi_header(void){ return blob_str(&cgiContent[0]); } /* ** Combine the header and body content all into the header buffer. ** In other words, append the body content to the end of the header ** content. */ static void cgi_combine_header_and_body(void){ int size = blob_size(&cgiContent[1]); if( size>0 ){ blob_append(&cgiContent[0], blob_buffer(&cgiContent[1]), size); blob_reset(&cgiContent[1]); } } /* ** Return a pointer to the combined header+body content. */ char *cgi_extract_content(void){ cgi_combine_header_and_body(); return blob_buffer(&cgiContent[0]); } /* ** Additional information used to form the HTTP reply */ static const char *zContentType = "text/html"; /* Content type of the reply */ static const char *zReplyStatus = "OK"; /* Reply status description */ static int iReplyStatus = 200; /* Reply status code */ static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */ static int rangeStart = 0; /* Start of Range: */ static int rangeEnd = 0; /* End of Range: plus 1 */ /* ** Set the reply content type. ** ** The reply content type defaults to "text/html". It only needs to be ** changed (by calling this routine) in the exceptional case where some ** other content type is being returned. */ void cgi_set_content_type(const char *zType){ zContentType = fossil_strdup(zType); } /* ** Erase any existing reply content. Replace is with a pNewContent. ** ** This routine erases pNewContent. In other words, it move pNewContent ** into the content buffer. */ void cgi_set_content(Blob *pNewContent){ cgi_reset_content(); cgi_destination(CGI_HEADER); cgiContent[0] = *pNewContent; blob_zero(pNewContent); } /* ** Set the reply status code */ void cgi_set_status(int iStat, const char *zStat){ zReplyStatus = fossil_strdup(zStat); iReplyStatus = iStat; } /* ** Append text to the content header buffer. */ void cgi_append_header(const char *zLine){ blob_append(&extraHeader, zLine, -1); } void cgi_printf_header(const char *zLine, ...){ va_list ap; va_start(ap, zLine); blob_vappendf(&extraHeader, zLine, ap); va_end(ap); } /* ** Set a cookie by queuing up the appropriate HTTP header output. If ** !g.isHTTP, this is a no-op. ** ** Zero lifetime implies a session cookie. A negative one expires ** the cookie immediately. */ void cgi_set_cookie( const char *zName, /* Name of the cookie */ const char *zValue, /* Value of the cookie. Automatically escaped */ const char *zPath, /* Path cookie applies to. NULL means "/" */ int lifetime /* Expiration of the cookie in seconds from now */ ){ char const *zSecure = ""; if(!g.isHTTP) return /* e.g. JSON CLI mode, where g.zTop is not set */; else if( zPath==0 ){ zPath = g.zTop; if( zPath[0]==0 ) zPath = "/"; } if( g.zBaseURL!=0 && fossil_strncmp(g.zBaseURL, "https:", 6)==0 ){ zSecure = " secure;"; } if( lifetime!=0 ){ blob_appendf(&extraHeader, "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly; %s\r\n", zName, lifetime>0 ? zValue : "null", zPath, lifetime, zSecure); }else{ blob_appendf(&extraHeader, "Set-Cookie: %s=%t; Path=%s; HttpOnly; %s\r\n", zName, zValue, zPath, zSecure); } } /* ** Return true if the response should be sent with Content-Encoding: gzip. */ static int is_gzippable(void){ if( g.fNoHttpCompress ) return 0; if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0; /* Maintenance note: this oddball structure is intended to make ** adding new mimetypes to this list less of a performance hit than ** doing a strcmp/glob over a growing set of compressible types. */ switch(zContentType ? *zContentType : 0){ case (int)'a': if(0==fossil_strncmp("application/",zContentType,12)){ const char * z = &zContentType[12]; switch(*z){ case (int)'j': return fossil_strcmp("javascript", z)==0 || fossil_strcmp("json", z)==0; case (int)'w': return fossil_strcmp("wasm", z)==0; case (int)'x': return fossil_strcmp("x-tcl", z)==0 || fossil_strcmp("x-tar", z)==0; default: return sqlite3_strglob("*xml", z)==0; } } break; case (int)'i': return fossil_strcmp(zContentType, "image/svg+xml")==0 || fossil_strcmp(zContentType, "image/vnd.microsoft.icon")==0; case (int)'t': return fossil_strncmp(zContentType, "text/", 5)==0; } return 0; } /* ** The following routines read or write content from/to the wire for ** an HTTP request. Depending on settings the content might be coming ** from or going to a socket, or a file, or it might come from or go ** to an SSL decoder/encoder. */ /* ** Works like fgets(): ** ** Read a single line of input into s[]. Ensure that s[] is zero-terminated. ** The s[] buffer is size bytes and so at most size-1 bytes will be read. ** ** Return a pointer to s[] on success, or NULL at end-of-input. */ static char *cgi_fgets(char *s, int size){ if( !g.httpUseSSL ){ return fgets(s, size, g.httpIn); } #ifdef FOSSIL_ENABLE_SSL return ssl_gets(g.httpSSLConn, s, size); #else fossil_fatal("SSL not available"); #endif } /* Works like fread(): ** ** Read as many as bytes of content as we can, up to a maximum of nmemb ** bytes. Return the number of bytes read. Return 0 if there is no ** further input or if an I/O error occurs. */ size_t cgi_fread(void *ptr, size_t nmemb){ if( !g.httpUseSSL ){ return fread(ptr, 1, nmemb, g.httpIn); } #ifdef FOSSIL_ENABLE_SSL return ssl_read_server(g.httpSSLConn, ptr, nmemb, 1); #else fossil_fatal("SSL not available"); #endif } /* Works like feof(): ** ** Return true if end-of-input has been reached. */ int cgi_feof(void){ if( !g.httpUseSSL ){ return feof(g.httpIn); } #ifdef FOSSIL_ENABLE_SSL return ssl_eof(g.httpSSLConn); #else return 1; #endif } /* Works like fwrite(): ** ** Try to output nmemb bytes of content. Return the number of ** bytes actually written. */ static size_t cgi_fwrite(void *ptr, size_t nmemb){ if( !g.httpUseSSL ){ return fwrite(ptr, 1, nmemb, g.httpOut); } #ifdef FOSSIL_ENABLE_SSL return ssl_write_server(g.httpSSLConn, ptr, nmemb); #else fossil_fatal("SSL not available"); #endif } /* Works like fflush(): ** ** Make sure I/O has completed. */ static void cgi_fflush(void){ if( !g.httpUseSSL ){ fflush(g.httpOut); } } /* ** Given a Content-Type value, returns a string suitable for appending ** to the Content-Type header for adding (or not) the "; charset=..." ** part. It returns an empty string for most types or if zContentType ** is NULL. ** ** See forum post f60dece061c364d1 for the discussions which lead to ** this. Previously we always appended the charset, but WASM loaders ** are pedantic and refuse to load any responses which have a ** charset. Also, adding a charset is not strictly appropriate for ** most types (and not required for many others which may ostensibly ** benefit from one, as detailed in that forum post). */ static const char * content_type_charset(const char *zContentType){ if(0==fossil_strncmp(zContentType,"text/",5)){ return "; charset=utf-8"; } return ""; } /* ** Generate the reply to a web request. The output might be an ** full HTTP response, or a CGI response, depending on how things have ** be set up. ** ** The reply consists of a response header (an HTTP or CGI response header) ** followed by the concatenation of the content header and content body. */ void cgi_reply(void){ Blob hdr = BLOB_INITIALIZER; int total_size; if( iReplyStatus<=0 ){ iReplyStatus = 200; zReplyStatus = "OK"; } if( g.fullHttpReply ){ if( rangeEnd>0 && iReplyStatus==200 && fossil_strcmp(P("REQUEST_METHOD"),"GET")==0 ){ iReplyStatus = 206; zReplyStatus = "Partial Content"; } blob_appendf(&hdr, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); blob_appendf(&hdr, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); blob_appendf(&hdr, "Connection: close\r\n"); blob_appendf(&hdr, "X-UA-Compatible: IE=edge\r\n"); }else{ assert( rangeEnd==0 ); blob_appendf(&hdr, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); } if( etag_tag()[0]!=0 && iReplyStatus==200 && strcmp(zContentType,"text/html")!=0 ){ /* Do not cache HTML replies as those will have been generated and ** will likely, therefore, contains a nonce and we want that nonce to ** be different every time. */ blob_appendf(&hdr, "ETag: %s\r\n", etag_tag()); blob_appendf(&hdr, "Cache-Control: max-age=%d\r\n", etag_maxage()); if( etag_mtime()>0 ){ blob_appendf(&hdr, "Last-Modified: %s\r\n", cgi_rfc822_datestamp(etag_mtime())); } }else if( g.isConst ){ /* isConst means that the reply is guaranteed to be invariant, even ** after configuration changes and/or Fossil binary recompiles. */ blob_appendf(&hdr, "Cache-Control: max-age=315360000, immutable\r\n"); }else{ blob_appendf(&hdr, "Cache-control: no-cache\r\n"); } if( blob_size(&extraHeader)>0 ){ blob_appendf(&hdr, "%s", blob_buffer(&extraHeader)); } /* Add headers to turn on useful security options in browsers. */ blob_appendf(&hdr, "X-Frame-Options: SAMEORIGIN\r\n"); /* The previous stops fossil pages appearing in frames or iframes, preventing ** click-jacking attacks on supporting browsers. ** ** Other good headers would be ** Strict-Transport-Security: max-age=62208000 ** if we're using https. However, this would break sites which serve different ** content on http and https protocols. Also, ** X-Content-Security-Policy: allow 'self' ** would help mitigate some XSS and data injection attacks, but will break ** deliberate inclusion of external resources, such as JavaScript syntax ** highlighter scripts. ** ** These headers are probably best added by the web server hosting fossil as ** a CGI script. */ if( iReplyStatus!=304 ) { blob_appendf(&hdr, "Content-Type: %s%s\r\n", zContentType, content_type_charset(zContentType)); if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ cgi_combine_header_and_body(); blob_compress(&cgiContent[0], &cgiContent[0]); } if( is_gzippable() && iReplyStatus!=206 ){ 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]); blob_appendf(&hdr, "Content-Encoding: gzip\r\n"); blob_appendf(&hdr, "Vary: Accept-Encoding\r\n"); } total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); if( iReplyStatus==206 ){ blob_appendf(&hdr, "Content-Range: bytes %d-%d/%d\r\n", rangeStart, rangeEnd-1, total_size); total_size = rangeEnd - rangeStart; } blob_appendf(&hdr, "Content-Length: %d\r\n", total_size); }else{ total_size = 0; } blob_appendf(&hdr, "\r\n"); cgi_fwrite(blob_buffer(&hdr), blob_size(&hdr)); blob_reset(&hdr); if( total_size>0 && iReplyStatus!=304 && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 ){ int i, size; for(i=0; i<2; i++){ size = blob_size(&cgiContent[i]); if( size<=rangeStart ){ rangeStart -= size; }else{ int n = size - rangeStart; if( n>total_size ){ n = total_size; } cgi_fwrite(blob_buffer(&cgiContent[i])+rangeStart, n); rangeStart = 0; total_size -= n; } } } cgi_fflush(); CGIDEBUG(("-------- END cgi ---------\n")); /* After the webpage has been sent, do any useful background ** processing. */ g.cgiOutput = 2; if( g.db!=0 && iReplyStatus==200 ){ backoffice_check_if_needed(); } } /* ** Generate an HTTP or CGI redirect response that causes a redirect ** to the URL given in the argument. ** ** The URL must be relative to the base of the fossil server. */ NORETURN void cgi_redirect_with_status( const char *zURL, int iStat, const char *zStat ){ char *zLocation; CGIDEBUG(("redirect to %s\n", zURL)); if( fossil_strncmp(zURL,"http:",5)==0 || fossil_strncmp(zURL,"https:",6)==0 ){ zLocation = mprintf("Location: %s\r\n", zURL); }else if( *zURL=='/' ){ int n1 = (int)strlen(g.zBaseURL); int n2 = (int)strlen(g.zTop); if( g.zBaseURL[n1-1]=='/' ) zURL++; zLocation = mprintf("Location: %.*s%s\r\n", n1-n2, g.zBaseURL, zURL); }else{ zLocation = mprintf("Location: %s/%s\r\n", g.zBaseURL, zURL); } cgi_append_header(zLocation); cgi_reset_content(); cgi_printf("<html>\n<p>Redirect to %h</p>\n</html>\n", zLocation); cgi_set_status(iStat, zStat); free(zLocation); cgi_reply(); fossil_exit(0); } NORETURN void cgi_redirect(const char *zURL){ cgi_redirect_with_status(zURL, 302, "Moved Temporarily"); } NORETURN void cgi_redirect_with_method(const char *zURL){ cgi_redirect_with_status(zURL, 307, "Temporary Redirect"); } NORETURN void cgi_redirectf(const char *zFormat, ...){ va_list ap; va_start(ap, zFormat); cgi_redirect(vmprintf(zFormat, ap)); va_end(ap); } /* ** Add a "Content-disposition: attachment; filename=%s" header to the reply. */ void cgi_content_disposition_filename(const char *zFilename){ char *z; int i, n; /* 0123456789 123456789 123456789 123456789 123456*/ z = mprintf("Content-Disposition: attachment; filename=\"%s\";\r\n", file_tail(zFilename)); n = (int)strlen(z); for(i=43; i<n-4; i++){ char c = z[i]; if( fossil_isalnum(c) ) continue; if( c=='.' || c=='-' || c=='/' ) continue; z[i] = '_'; } cgi_append_header(z); fossil_free(z); } /* ** Return the URL for the caller. This is obtained from either the ** "referer" CGI parameter, if it exists, or the HTTP_REFERER HTTP parameter. ** If neither exist, return zDefault. */ const char *cgi_referer(const char *zDefault){ const char *zRef = P("referer"); if( zRef==0 ){ zRef = P("HTTP_REFERER"); if( zRef==0 ) zRef = zDefault; } return zRef; } /* ** Return true if the current request is coming from the same origin. */ int cgi_same_origin(void){ const char *zRef; int nBase; if( g.zBaseURL==0 ) return 0; zRef = P("HTTP_REFERER"); if( zRef==0 ) return 0; nBase = (int)strlen(g.zBaseURL); if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0; if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0; return 1; } /* ** Return true if the current CGI request is a POST request */ static int cgi_is_post_request(void){ const char *zMethod = P("REQUEST_METHOD"); if( zMethod==0 ) return 0; if( strcmp(zMethod,"POST")!=0 ) return 0; return 1; } /* ** Return true if the current request appears to be safe from a ** Cross-Site Request Forgery (CSRF) attack. The level of checking ** is determined by the parameter. The higher the number, the more ** secure we are: ** ** 0: Request must come from the same origin ** 1: Same origin and must be a POST request ** 2: All of the above plus must have a valid CSRF token ** ** Results are cached in the g.okCsrf variable. The g.okCsrf value ** has meaning as follows: ** ** -1: Not a secure request ** 0: Status unknown ** 1: Request comes from the same origin ** 2: (1) plus it is a POST request ** 3: (2) plus there is a valid "csrf" token in the request */ int cgi_csrf_safe(int securityLevel){ if( g.okCsrf<0 ) return 0; if( g.okCsrf==0 ){ if( !cgi_same_origin() ){ g.okCsrf = -1; }else{ g.okCsrf = 1; if( cgi_is_post_request() ){ g.okCsrf = 2; if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){ g.okCsrf = 3; } } } } return g.okCsrf >= (securityLevel+1); } /* ** Verify that CSRF defenses are maximal - that the request comes from ** the same origin, that it is a POST request, and that there is a valid ** "csrf" token. If this is not the case, fail immediately. */ void cgi_csrf_verify(void){ if( !cgi_csrf_safe(2) ){ fossil_fatal("Cross-site Request Forgery detected"); } } /* ** Information about all query parameters, post parameter, cookies and ** CGI environment variables are stored in a hash table as follows: */ static int nAllocQP = 0; /* Space allocated for aParamQP[] */ 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 */ char isFetched; /* 1 if the var is requested via P/PD() */ } *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. ** ** zName and zValue are not copied and must not change or be ** deallocated after this routine returns. */ void cgi_set_parameter_nocopy(const char *zName, const char *zValue, int isQP){ if( nAllocQP<=nUsedQP ){ nAllocQP = nAllocQP*2 + 10; if( nAllocQP>1000 ){ /* Prevent a DOS service attack against the framework */ fossil_fatal("Too many query parameters"); } aParamQP = fossil_realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) ); } 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; aParamQP[nUsedQP].isFetched = 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 ** is its fully decoded value. zName will be modified to be an ** all lowercase string. ** ** zName and zValue are not copied and must not change or be ** deallocated after this routine returns. This routine changes ** all ASCII alphabetic characters in zName to lower case. The ** caller must not change them back. */ void cgi_set_parameter_nocopy_tolower( char *zName, const char *zValue, int isQP ){ int i; for(i=0; zName[i]; i++){ zName[i] = fossil_tolower(zName[i]); } cgi_set_parameter_nocopy(zName, zValue, isQP); } /* ** 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. ** ** Copies are made of both the zName and zValue parameters. */ void cgi_set_parameter(const char *zName, const char *zValue){ cgi_set_parameter_nocopy(fossil_strdup(zName),fossil_strdup(zValue), 0); } void cgi_set_query_parameter(const char *zName, const char *zValue){ cgi_set_parameter_nocopy(fossil_strdup(zName),fossil_strdup(zValue), 1); } /* ** Replace a parameter with a new value. */ void cgi_replace_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; 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); } void cgi_replace_query_parameter_tolower(char *zName, const char *zValue){ int i; for(i=0; zName[i]; i++){ zName[i] = fossil_tolower(zName[i]); } cgi_replace_query_parameter(zName, zValue); } /* ** Delete a parameter. */ void cgi_delete_parameter(const char *zName){ int i; for(i=0; i<nUsedQP; i++){ if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){ --nUsedQP; if( i<nUsedQP ){ memmove(aParamQP+i, aParamQP+i+1, sizeof(*aParamQP)*(nUsedQP-i)); } return; } } } void cgi_delete_query_parameter(const char *zName){ int i; for(i=0; i<nUsedQP; i++){ if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){ assert( aParamQP[i].isQP ); --nUsedQP; if( i<nUsedQP ){ memmove(aParamQP+i, aParamQP+i+1, sizeof(*aParamQP)*(nUsedQP-i)); } return; } } } /* ** Add an environment varaible value to the parameter set. The zName ** portion is fixed but a copy is be made of zValue. */ void cgi_setenv(const char *zName, const char *zValue){ cgi_set_parameter_nocopy(zName, fossil_strdup(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 ** special character and therefore does not decode it. ** ** If NAME begins with another other than a lower-case letter then ** the entire NAME=VALUE term is ignored. Hence: ** ** * cookies and query parameters that have uppercase names ** are ignored. ** ** * it is impossible for a cookie or query parameter to ** override the value of an environment variable since ** environment variables always have uppercase names. ** ** 2018-03-29: Also ignore the entry if NAME that contains any characters ** other than [a-zA-Z0-9_]. There are no known exploits involving unusual ** names that contain characters outside that set, but it never hurts to ** be extra cautious when sanitizing inputs. ** ** Parameters are separated by the "terminator" character. Whitespace ** before the NAME is ignored. ** ** The input string "z" is modified but no copies is made. "z" ** should not be deallocated or changed again after this routine ** returns or it will corrupt the parameter table. */ static void add_param_list(char *z, int terminator){ int isQP = terminator=='&'; while( *z ){ char *zName; char *zValue; while( fossil_isspace(*z) ){ z++; } zName = z; while( *z && *z!='=' && *z!=terminator ){ z++; } if( *z=='=' ){ *z = 0; z++; zValue = z; while( *z && *z!=terminator ){ z++; } if( *z ){ *z = 0; z++; } dehttpize(zValue); }else{ if( *z ){ *z++ = 0; } zValue = ""; } if( zName[0] && fossil_no_strange_characters(zName+1) ){ if( fossil_islower(zName[0]) ){ cgi_set_parameter_nocopy(zName, zValue, isQP); }else if( fossil_isupper(zName[0]) ){ cgi_set_parameter_nocopy_tolower(zName, zValue, isQP); } } #ifdef FOSSIL_ENABLE_JSON json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) ); #endif /* FOSSIL_ENABLE_JSON */ } } /* ** *pz is a string that consists of multiple lines of text. This ** routine finds the end of the current line of text and converts ** the "\n" or "\r\n" that ends that line into a "\000". It then |
︙ | ︙ | |||
509 510 511 512 513 514 515 | *pz = &z[i]; *pLen -= i; return z; } /* ** The input *pz points to content that is terminated by a "\r\n" | | | | | | | > | | | 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 | *pz = &z[i]; *pLen -= i; return z; } /* ** The input *pz points to content that is terminated by a "\r\n" ** followed by the boundary marker zBoundary. An extra "--" may or ** may not be appended to the boundary marker. There are *pLen characters ** in *pz. ** ** This routine adds a "\000" to the end of the content (overwriting ** the "\r\n") and returns a pointer to the content. The *pz input ** is adjusted to point to the first line following the boundary. ** The length of the content is stored in *pnContent. */ static char *get_bounded_content( char **pz, /* Content taken from here */ int *pLen, /* Number of bytes of data in (*pz)[] */ char *zBoundary, /* Boundary text marking the end of content */ int *pnContent /* Write the size of the content here */ ){ char *z = *pz; int len = *pLen; int i; int nBoundary = strlen(zBoundary); *pnContent = len; for(i=0; i<len; i++){ if( z[i]=='\n' && fossil_strncmp(zBoundary, &z[i+1], nBoundary)==0 ){ if( i>0 && z[i-1]=='\r' ) i--; z[i] = 0; *pnContent = i; i += nBoundary; 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 |
︙ | ︙ | |||
569 570 571 572 573 574 575 | ** '\000' characters are inserted in z[] at the end of each token. ** This routine returns the total number of tokens on the line, 6 ** in the example above. */ static int tokenize_line(char *z, int mxArg, char **azArg){ int i = 0; while( *z ){ | | | | 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 | ** '\000' characters are inserted in z[] at the end of each token. ** This routine returns the total number of tokens on the line, 6 ** in the example above. */ static int tokenize_line(char *z, int mxArg, char **azArg){ int i = 0; while( *z ){ while( fossil_isspace(*z) || *z==';' ){ z++; } if( *z=='"' && z[1] ){ *z = 0; z++; if( i<mxArg-1 ){ azArg[i++] = z; } while( *z && *z!='"' ){ z++; } if( *z==0 ) break; *z = 0; z++; }else{ if( i<mxArg-1 ){ azArg[i++] = z; } while( *z && !fossil_isspace(*z) && *z!=';' && *z!='"' ){ z++; } if( *z && *z!='"' ){ *z = 0; z++; } } } azArg[i] = 0; |
︙ | ︙ | |||
603 604 605 606 607 608 609 | ** not copied. The calling function must not deallocate or modify ** "z" after this routine finishes or it could corrupt the parameter ** table. */ static void process_multipart_form_data(char *z, int len){ char *zLine; int nArg, i; | | | | | | > | | | | > > > > > > > | | | > | > > > > | > | > > > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | < < > | > > | | > > > > > > | > > > > > | < < | < < | | > > > > | < < | < > | > > | | | | | > > > > > > > | < > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | ** not copied. The calling function must not deallocate or modify ** "z" after this routine finishes or it could corrupt the parameter ** table. */ static void process_multipart_form_data(char *z, int len){ char *zLine; int nArg, i; char *zBoundary; char *zValue; char *zName = 0; int showBytes = 0; char *azArg[50]; zBoundary = get_line_from_string(&z, &len); if( zBoundary==0 ) return; while( (zLine = get_line_from_string(&z, &len))!=0 ){ if( zLine[0]==0 ){ int nContent = 0; zValue = get_bounded_content(&z, &len, zBoundary, &nContent); if( zName && zValue ){ if( fossil_islower(zName[0]) ){ cgi_set_parameter_nocopy(zName, zValue, 1); if( showBytes ){ cgi_set_parameter_nocopy(mprintf("%s:bytes", zName), mprintf("%d",nContent), 1); } }else if( fossil_isupper(zName[0]) ){ cgi_set_parameter_nocopy_tolower(zName, zValue, 1); if( showBytes ){ cgi_set_parameter_nocopy_tolower(mprintf("%s:bytes", zName), mprintf("%d",nContent), 1); } } } zName = 0; showBytes = 0; }else{ nArg = tokenize_line(zLine, count(azArg), azArg); for(i=0; i<nArg; i++){ int c = fossil_tolower(azArg[i][0]); int n = strlen(azArg[i]); if( c=='c' && sqlite3_strnicmp(azArg[i],"content-disposition:",n)==0 ){ i++; }else if( c=='n' && sqlite3_strnicmp(azArg[i],"name=",n)==0 ){ zName = azArg[++i]; }else if( c=='f' && sqlite3_strnicmp(azArg[i],"filename=",n)==0 ){ char *z = azArg[++i]; if( zName && z ){ if( fossil_islower(zName[0]) ){ cgi_set_parameter_nocopy(mprintf("%s:filename",zName), z, 1); }else if( fossil_isupper(zName[0]) ){ cgi_set_parameter_nocopy_tolower(mprintf("%s:filename",zName), z, 1); } } showBytes = 1; }else if( c=='c' && sqlite3_strnicmp(azArg[i],"content-type:",n)==0 ){ char *z = azArg[++i]; if( zName && z ){ if( fossil_islower(zName[0]) ){ cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z, 1); }else if( fossil_isupper(zName[0]) ){ cgi_set_parameter_nocopy_tolower(mprintf("%s:mimetype",zName), z, 1); } } } } } } } #ifdef FOSSIL_ENABLE_JSON /* ** Reads a JSON object from the given blob, which is assumed to have ** been populated by the caller from stdin, the SSL API, or a file, as ** appropriate for the particular use case. On success g.json.post is ** updated to hold the content. On error a FSL_JSON_E_INVALID_REQUEST ** response is output and fossil_exit() is called (in HTTP mode exit ** code 0 is used). */ void cgi_parse_POST_JSON( Blob * pIn ){ cson_value * jv = NULL; cson_parse_info pinfo = cson_parse_info_empty; assert(g.json.gc.a && "json_bootstrap_early() was not called!"); jv = cson_parse_Blob(pIn, &pinfo); if( jv==NULL ){ goto invalidRequest; }else{ json_gc_add( "POST.JSON", jv ); g.json.post.v = jv; g.json.post.o = cson_value_get_object( jv ); if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */ goto invalidRequest; } } return; invalidRequest: cgi_set_content_type(json_guess_content_type()); if(0 != pinfo.errorCode){ /* fancy error message */ char * msg = mprintf("JSON parse error at line %u, column %u, " "byte offset %u: %s", pinfo.line, pinfo.col, pinfo.length, cson_rc_string(pinfo.errorCode)); json_err( FSL_JSON_E_INVALID_REQUEST, msg, 1 ); fossil_free(msg); }else if(jv && !g.json.post.o){ json_err( FSL_JSON_E_INVALID_REQUEST, "Request envelope must be a JSON Object (not array).", 1 ); }else{ /* generic error message */ json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 ); } fossil_exit( g.isHTTP ? 0 : 1); } #endif /* FOSSIL_ENABLE_JSON */ /* ** Log HTTP traffic to a file. Begin the log on first use. Close the log ** when the argument is NULL. */ void cgi_trace(const char *z){ static FILE *pLog = 0; if( g.fHttpTrace==0 ) return; if( z==0 ){ if( pLog ) fclose(pLog); pLog = 0; return; } if( pLog==0 ){ char zFile[50]; #if defined(_WIN32) unsigned r; sqlite3_randomness(sizeof(r), &r); sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%08x.txt", r); #else sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%05d.txt", getpid()); #endif pLog = fossil_fopen(zFile, "wb"); if( pLog ){ fprintf(stderr, "# open log on %s\n", zFile); }else{ fprintf(stderr, "# failed to open %s\n", zFile); return; } } fputs(z, pLog); } /* Forward declaration */ static NORETURN void malformed_request(const char *zMsg, ...); /* ** Checks the QUERY_STRING environment variable, sets it up ** via add_param_list() and, if found, applies its "skin" ** setting. Returns 0 if no QUERY_STRING is set, 1 if it is, ** and 2 if it sets the skin (in which case the cookie may ** still need flushing by the page, via cookie_render()). */ int cgi_setup_query_string(void){ int rc = 0; char * z = (char*)P("QUERY_STRING"); if( z ){ ++rc; z = fossil_strdup(z); add_param_list(z, '&'); z = (char*)P("skin"); if( z ){ char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM); ++rc; if( !zErr && P("once")==0 ){ cookie_write_parameter("skin","skin",z); /* Per /chat discussion, passing ?skin=... without "once" ** implies the "udc" argument, so we force that into the ** environment here. */ cgi_set_parameter_nocopy("udc", "1", 1); } fossil_free(zErr); } } return rc; } /* ** 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 ** ** Or if QUERY_STRING is not empty: ** ** REQUEST_URI == SCRIPT_NAME + PATH_INFO + '?' + QUERY_STRING ** ** 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. ** ** Sometimes PATH_INFO is missing and SCRIPT_NAME is not a prefix of ** REQUEST_URI. (See https://fossil-scm.org/forum/forumpost/049e8650ed) ** In that case, truncate SCRIPT_NAME so that it is a proper prefix ** of REQUEST_URI. ** ** SCGI typically omits PATH_INFO. CGI sometimes omits REQUEST_URI and ** PATH_INFO when it is empty. ** ** CGI Parameter quick reference: ** ** REQUEST_URI ** _____________|________________ ** / \ ** https://fossil-scm.org/forum/info/12736b30c072551a?t=c ** \___/ \____________/\____/\____________________/ \_/ ** | | | | | ** | HTTP_HOST | PATH_INFO QUERY_STRING ** | | ** REQUEST_SCHEMA SCRIPT_NAME ** */ void cgi_init(void){ char *z; const char *zType; char *zSemi; 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); const char *zContentLength = 0; #ifdef _WIN32 const char *zServerSoftware = cgi_parameter("SERVER_SOFTWARE",0); #endif #ifdef FOSSIL_ENABLE_JSON const int noJson = P("no_json")!=0; #endif g.isHTTP = 1; cgi_destination(CGI_BODY); /* We must have SCRIPT_NAME. If the web server did not supply it, try ** to compute it from REQUEST_URI and PATH_INFO. */ if( zScriptName==0 ){ if( zRequestUri==0 || zPathInfo==0 ){ malformed_request("missing SCRIPT_NAME"); /* Does not return */ } z = strstr(zRequestUri,zPathInfo); if( z==0 ){ malformed_request("PATH_INFO not found in REQUEST_URI"); } zScriptName = fossil_strndup(zRequestUri,(int)(z-zRequestUri)); cgi_set_parameter("SCRIPT_NAME", zScriptName); } #ifdef _WIN32 /* The Microsoft IIS web server does not define REQUEST_URI, instead it uses ** PATH_INFO for virtually the same purpose. Define REQUEST_URI the same as ** PATH_INFO and redefine PATH_INFO with SCRIPT_NAME removed from the ** beginning. */ if( zServerSoftware && strstr(zServerSoftware, "Microsoft-IIS") ){ int i, j; cgi_set_parameter("REQUEST_URI", zPathInfo); for(i=0; zPathInfo[i]==zScriptName[i] && zPathInfo[i]; i++){} for(j=i; zPathInfo[j] && zPathInfo[j]!='?'; j++){} zPathInfo = fossil_strndup(zPathInfo+i, j-i); cgi_replace_parameter("PATH_INFO", zPathInfo); } #endif if( zRequestUri==0 ){ const char *z = zPathInfo; const char *zQS = cgi_parameter("QUERY_STRING",0); if( zPathInfo==0 ){ malformed_request("missing PATH_INFO and/or REQUEST_URI"); } if( z[0]=='/' ) z++; if( zQS && zQS[0] ){ zRequestUri = mprintf("%s/%s?%s", zScriptName, z, zQS); }else{ 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++){} zPathInfo = fossil_strndup(zRequestUri+i, j-i); cgi_set_parameter_nocopy("PATH_INFO", zPathInfo, 0); if( j>i && zScriptName[i]!=0 ){ /* If SCRIPT_NAME is not a prefix of REQUEST_URI, truncate it so ** that it is. See https://fossil-scm.org/forum/forumpost/049e8650ed */ char *zNew = fossil_strndup(zScriptName, i); cgi_replace_parameter("SCRIPT_NAME", zNew); } } #ifdef FOSSIL_ENABLE_JSON if(noJson==0 && json_request_is_json_api(zPathInfo)){ /* We need to change some following behaviour depending on whether ** we are operating in JSON mode or not. We cannot, however, be ** certain whether we should/need to be in JSON mode until the ** PATH_INFO is set up. */ g.json.isJsonMode = 1; json_bootstrap_early(); }else{ assert(!g.json.isJsonMode && "Internal misconfiguration of g.json.isJsonMode"); } #endif z = (char*)P("HTTP_COOKIE"); if( z ){ z = fossil_strdup(z); add_param_list(z, ';'); z = (char*)cookie_value("skin",0); if(z){ skin_use_alternative(z, 2, SKIN_FROM_COOKIE); } } cgi_setup_query_string(); z = (char*)P("REMOTE_ADDR"); if( z ){ g.zIpAddr = fossil_strdup(z); } zContentLength = P("CONTENT_LENGTH"); if( zContentLength==0 ){ len = 0; if( sqlite3_stricmp(PD("REQUEST_METHOD",""),"POST")==0 ){ malformed_request("missing CONTENT_LENGTH on a POST method"); } }else{ len = atoi(zContentLength); } zType = P("CONTENT_TYPE"); zSemi = zType ? strchr(zType, ';') : 0; if( zSemi ){ g.zContentType = fossil_strndup(zType, (int)(zSemi-zType)); zType = g.zContentType; }else{ g.zContentType = zType; } blob_zero(&g.cgiIn); if( len>0 && zType ){ if( blob_read_from_cgi(&g.cgiIn, len)<len ){ char *zMsg = mprintf("CGI content-length mismatch: Wanted %d bytes" " but got only %d\n", len, blob_size(&g.cgiIn)); malformed_request(zMsg); } if( fossil_strcmp(zType, "application/x-fossil")==0 ){ blob_uncompress(&g.cgiIn, &g.cgiIn); } #ifdef FOSSIL_ENABLE_JSON if( noJson==0 && g.json.isJsonMode!=0 && json_can_consume_content_type(zType)!=0 ){ cgi_parse_POST_JSON(&g.cgiIn); cgi_set_content_type(json_guess_content_type()); } #endif /* FOSSIL_ENABLE_JSON */ } } /* ** Decode POST parameter information in the cgiIn content, if any. */ void cgi_decode_post_parameters(void){ int len = blob_size(&g.cgiIn); if( len==0 ) return; if( fossil_strcmp(g.zContentType,"application/x-www-form-urlencoded")==0 || fossil_strncmp(g.zContentType,"multipart/form-data",19)==0 ){ char *z = blob_str(&g.cgiIn); cgi_trace(z); if( g.zContentType[0]=='a' ){ add_param_list(z, '&'); }else{ process_multipart_form_data(z, len); } blob_init(&g.cgiIn, 0, 0); } } /* ** This is the comparison function used to sort the aParamQP[] array of ** query parameters and cookies. */ static int qparam_compare(const void *a, const void *b){ struct QParam *pA = (struct QParam*)a; struct QParam *pB = (struct QParam*)b; int c; c = fossil_strcmp(pA->zName, pB->zName); if( c==0 ){ c = pA->seq - pB->seq; } return c; } /* |
︙ | ︙ | |||
736 737 738 739 740 741 742 | qsort(aParamQP, nUsedQP, sizeof(aParamQP[0]), qparam_compare); sortQP = 0; /* After sorting, remove duplicate parameters. The secondary sort ** key is aParamQP[].seq and we keep the first entry. That means ** with duplicate calls to cgi_set_parameter() the second and ** subsequent calls are effectively no-ops. */ for(i=j=1; i<nUsedQP; i++){ | | > > > > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | qsort(aParamQP, nUsedQP, sizeof(aParamQP[0]), qparam_compare); sortQP = 0; /* After sorting, remove duplicate parameters. The secondary sort ** key is aParamQP[].seq and we keep the first entry. That means ** with duplicate calls to cgi_set_parameter() the second and ** subsequent calls are effectively no-ops. */ for(i=j=1; i<nUsedQP; i++){ if( fossil_strcmp(aParamQP[i].zName,aParamQP[i-1].zName)==0 ){ continue; } if( j<i ){ memcpy(&aParamQP[j], &aParamQP[i], sizeof(aParamQP[j])); } j++; } nUsedQP = j; } /* Invoking with a NULL zName is just a way to cause the parameters ** to be sorted. So go ahead and bail out in that case */ if( zName==0 || zName[0]==0 ) return 0; /* Do a binary search for a matching query parameter */ lo = 0; hi = nUsedQP-1; while( lo<=hi ){ mid = (lo+hi)/2; c = fossil_strcmp(aParamQP[mid].zName, zName); if( c==0 ){ CGIDEBUG(("mem-match [%s] = [%s]\n", zName, aParamQP[mid].zValue)); aParamQP[mid].isFetched = 1; return aParamQP[mid].zValue; }else if( c>0 ){ hi = mid-1; }else{ lo = mid+1; } } /* If no match is found and the name begins with an upper-case ** letter, then check to see if there is an environment variable ** with the given name. */ if( fossil_isupper(zName[0]) ){ const char *zValue = fossil_getenv(zName); if( zValue ){ cgi_set_parameter_nocopy(zName, zValue, 0); CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue)); return zValue; } } CGIDEBUG(("no-match [%s]\n", zName)); return zDefault; } /* ** Renders the "begone, spider" page and exits. */ static void cgi_begone_spider(const char *zName){ Blob content = empty_blob; cgi_set_content(&content); style_set_current_feature("test"); style_submenu_enable(0); style_header("Malicious Query Detected"); @ <h2>Begone, Knave!</h2> @ <p>This page was generated because Fossil detected an (unsuccessful) @ SQL injection attack or other nefarious content in your HTTP request. @ @ <p>If you believe you are innocent and have reached this page in error, @ contact the Fossil developers on the Fossil-SCM Forum. Type @ "fossil-scm forum" into any search engine to locate the Fossil-SCM Forum. style_finish_page(); cgi_set_status(418,"I'm a teapot"); cgi_reply(); fossil_errorlog("Xpossible hack attempt - 418 response on \"%s\"", zName); exit(0); } /* ** If looks_like_sql_injection() returns true for the given string, calls ** cgi_begone_spider() and does not return, else this function has no ** side effects. The range of checks performed by this function may ** be extended in the future. ** ** Checks are omitted for any logged-in user. ** ** This is NOT a defense against SQL injection. Fossil should easily be ** proof against SQL injection without this routine. Rather, this is an ** attempt to avoid denial-of-service caused by persistent spiders that hammer ** the server with dozens or hundreds of SQL injection attempts per second ** against pages (such as /vdiff) that are expensive to compute. In other ** words, this is an effort to reduce the CPU load imposed by malicious ** spiders. It is not an effect defense against SQL injection vulnerabilities. */ void cgi_value_spider_check(const char *zTxt, const char *zName){ if( g.zLogin==0 && looks_like_sql_injection(zTxt) ){ cgi_begone_spider(zName); } } /* ** A variant of cgi_parameter() with the same semantics except that if ** cgi_parameter(zName,zDefault) returns a value other than zDefault ** then it passes that value to cgi_value_spider_check(). */ const char *cgi_parameter_nosql(const char *zName, const char *zDefault){ const char *zTxt = cgi_parameter(zName, zDefault); if( zTxt!=zDefault ){ cgi_value_spider_check(zTxt, zName); } return zTxt; } /* ** Return the value of the first defined query parameter or cookie whose ** name appears in the list of arguments. Or if no parameter is found, ** return NULL. */ const char *cgi_coalesce(const char *zName, ...){ va_list ap; const char *z; const char *zX; if( zName==0 ) return 0; z = cgi_parameter(zName, 0); va_start(ap, zName); while( z==0 && (zX = va_arg(ap,const char*))!=0 ){ z = cgi_parameter(zX, 0); } va_end(ap); return z; } /* ** Return the value of a CGI parameter with leading and trailing ** spaces removed and with internal \r\n changed to just \n */ char *cgi_parameter_trimmed(const char *zName, const char *zDefault){ const char *zIn; char *zOut, c; int i, j; zIn = cgi_parameter(zName, 0); if( zIn==0 ) zIn = zDefault; if( zIn==0 ) return 0; while( fossil_isspace(zIn[0]) ) zIn++; zOut = fossil_strdup(zIn); for(i=j=0; (c = zOut[i])!=0; i++){ if( c=='\r' && zOut[i+1]=='\n' ) continue; zOut[j++] = c; } zOut[j] = 0; while( j>0 && fossil_isspace(zOut[j-1]) ) zOut[--j] = 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 either an empty string "" or the string "checked" depending ** on whether or not parameter zName has value iValue. If parameter ** zName does not exist, that is assumed to be the same as value 0. ** ** This routine implements the PCK(x) and PIF(x,y) macros. The PIF(x,y) ** macro generateds " checked" if the value of parameter x equals integer y. ** PCK(x) is the same as PIF(x,1). These macros are used to generate ** the "checked" attribute on checkbox and radio controls of forms. */ const char *cgi_parameter_checked(const char *zName, int iValue){ const char *zIn = cgi_parameter(zName,0); int x; if( zIn==0 ){ x = 0; }else if( !fossil_isdigit(zIn[0]) ){ x = is_truth(zIn); }else{ x = atoi(zIn); } return x==iValue ? "checked" : ""; } /* ** 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 ){ return aParamQP[i].zName; }else{ return 0; } |
︙ | ︙ | |||
837 838 839 840 841 842 843 | if( cgi_parameter(z2,0)==0 ) return 0; } va_end(ap); return 1; } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > | | | > > > > > | > > > > | > > > | < < < < < | < < < | < < < < | | | < | | < | | | > | < < < < > > > < < < < < < < < < < | < < < < < < < < > > | | | | < > > > > > > > > | | < < | < < > < < < < < | < < < < < < < < < < < < < < < < < < < < < < > > | | | > > > > | > > | < > > > | > < | < > > < < < < < < < | | > > > > | | > | < | < < < < | < < < > | < < | 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 | if( cgi_parameter(z2,0)==0 ) return 0; } va_end(ap); return 1; } /* ** Load all relevant environment variables into the parameter buffer. ** Invoke this routine prior to calling cgi_print_all() in order to see ** the full CGI environment. This routine intended for debugging purposes ** only. */ void cgi_load_environment(void){ /* The following is a list of environment variables that Fossil considers ** to be "relevant". */ static const char *const azCgiVars[] = { "COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "SCGI", "HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_AUTHENICATION", "HTTP_CONNECTION", "HTTP_HOST", "HTTP_IF_NONE_MATCH", "HTTP_IF_MODIFIED_SINCE", "HTTP_USER_AGENT", "HTTP_REFERER", "PATH_INFO", "PATH_TRANSLATED", "QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT", "REMOTE_USER", "REQUEST_METHOD", "REQUEST_SCHEME", "REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_NAME", "SERVER_PROTOCOL", "HOME", "FOSSIL_HOME", "USERNAME", "USER", "FOSSIL_USER", "SQLITE_TMPDIR", "TMPDIR", "TEMP", "TMP", "FOSSIL_VFS", "FOSSIL_FORCE_TICKET_MODERATION", "FOSSIL_FORCE_WIKI_MODERATION", "FOSSIL_TCL_PATH", "TH1_DELETE_INTERP", "TH1_ENABLE_DOCS", "TH1_ENABLE_HOOKS", "TH1_ENABLE_TCL", "REMOTE_HOST", "CONTENT_TYPE", "CONTENT_LENGTH", }; int i; for(i=0; i<count(azCgiVars); i++) (void)P(azCgiVars[i]); } /* ** Print all query parameters on standard output. ** This is used for testing and debugging. ** ** Omit the values of the cookies unless showAll is true. ** ** The eDest parameter determines where the output is shown: ** ** eDest==0: Rendering as HTML into the CGI reply ** eDest==1: Written to fossil_trace ** eDest==2: Written to cgi_debug ** eDest==3: Written to out (Used only by fossil_errorlog()) */ void cgi_print_all(int showAll, unsigned int eDest, FILE *out){ int i; cgi_parameter("",""); /* Force the parameters into sorted order */ for(i=0; i<nUsedQP; i++){ const char *zName = aParamQP[i].zName; const char *zValue = aParamQP[i].zValue; if( fossil_stricmp("HTTP_COOKIE",zName)==0 || fossil_strnicmp("fossil-",zName,7)==0 ){ if( !showAll ) continue; if( eDest==3 ) zValue = "..."; } switch( eDest ){ case 0: { cgi_printf("%h = %h <br>\n", zName, zValue); break; } case 1: { fossil_trace("%s = %s\n", zName, zValue); break; } case 2: { cgi_debug("%s = %s\n", zName, zValue); break; } case 3: { if( zValue!=0 && strlen(zValue)>100 ){ fprintf(out,"%s = %.100s...\n", zName, zValue); }else{ fprintf(out,"%s = %s\n", zName, zValue); } break; } } } } /* ** Put information about the N-th parameter into arguments. ** Return non-zero on success, and return 0 if there is no N-th parameter. */ int cgi_param_info( int N, const char **pzName, const char **pzValue, int *pbIsQP ){ if( N>=0 && N<nUsedQP ){ *pzName = aParamQP[N].zName; *pzValue = aParamQP[N].zValue; *pbIsQP = aParamQP[N].isQP; return 1; }else{ *pzName = 0; *pzValue = 0; *pbIsQP = 0; return 0; } } /* ** 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, ...){ |
︙ | ︙ | |||
1007 1008 1009 1010 1011 1012 1013 | vxprintf(pContent,zFormat,ap); } /* ** Send a reply indicating that the HTTP request was malformed */ | | > > > > > | > > > > | > | > | | | > > > > > > > > > > > > | | | | | | | | | | > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | < < > | | > > | > | | > | > | > | | < < < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > | > | > | > > > > > > > > | | > > > > > > > | > > | > > > > | | > > > > > > > > | > > > > > > > > | > > > > > > | > > | > > | > | < > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | > > > > > | | > | > > > > > > > > | > | < | > > | < > | > > > > > > > > > > | > | > > | < | > | | > | > | < > > > | > | > > > > | > | > | | > > > | > | > > > > > > > | | | > | | > > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > | < < | | | | > | < < < | < < | < < < < < < < < < < | | | | | | | | | | | < | | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | vxprintf(pContent,zFormat,ap); } /* ** Send a reply indicating that the HTTP request was malformed */ static NORETURN void malformed_request(const char *zMsg, ...){ va_list ap; char *z; va_start(ap, zMsg); z = vmprintf(zMsg, ap); va_end(ap); cgi_set_status(400, "Bad Request"); zContentType = "text/plain"; if( g.zReqType==0 ) g.zReqType = "WWW"; if( g.zReqType[0]=='C' && PD("SERVER_SOFTWARE",0)!=0 ){ const char *zServer = PD("SERVER_SOFTWARE",""); cgi_printf("Bad CGI Request from \"%s\": %s\n",zServer,z); }else{ cgi_printf("Bad %s Request: %s\n", g.zReqType, z); } fossil_free(z); cgi_reply(); fossil_exit(0); } /* ** Panic and die while processing a webpage. */ NORETURN void cgi_panic(const char *zFormat, ...){ va_list ap; cgi_reset_content(); #ifdef FOSSIL_ENABLE_JSON if( g.json.isJsonMode ){ char * zMsg; va_start(ap, zFormat); zMsg = vmprintf(zFormat,ap); va_end(ap); json_err( FSL_JSON_E_PANIC, zMsg, 1 ); free(zMsg); fossil_exit( g.isHTTP ? 0 : 1 ); }else #endif /* FOSSIL_ENABLE_JSON */ { cgi_set_status(500, "Internal Server Error"); cgi_printf( "<html><body><h1>Internal Server Error</h1>\n" "<plaintext>" ); va_start(ap, zFormat); vxprintf(pContent,zFormat,ap); va_end(ap); cgi_reply(); fossil_exit(1); } } /* z[] is the value of an X-FORWARDED-FOR: line in an HTTP header. ** 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( !cgi_is_loopback(g.zIpAddr) ){ /* Only accept X-FORWARDED-FOR if input coming from the local machine */ 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 ** a pointer to it. Add a NULL to the string to terminate the token. ** Make *zLeftOver point to the start of the next token. */ static char *extract_token(char *zInput, char **zLeftOver){ char *zResult = 0; if( zInput==0 ){ if( zLeftOver ) *zLeftOver = 0; return 0; } while( fossil_isspace(*zInput) ){ zInput++; } zResult = zInput; while( *zInput && !fossil_isspace(*zInput) ){ zInput++; } if( *zInput ){ *zInput = 0; zInput++; while( fossil_isspace(*zInput) ){ zInput++; } } if( zLeftOver ){ *zLeftOver = zInput; } return zResult; } /* ** Determine the IP address on the other side of a connection. ** Return a pointer to a string. Or return 0 if unable. ** ** The string is held in a static buffer that is overwritten on ** each call. */ char *cgi_remote_ip(int fd){ #if 0 static char zIp[100]; struct sockaddr_in6 addr; socklen_t sz = sizeof(addr); if( getpeername(fd, &addr, &sz) ) return 0; zIp[0] = 0; if( inet_ntop(AF_INET6, &addr, zIp, sizeof(zIp))==0 ){ return 0; } return zIp; #else struct sockaddr_in remoteName; socklen_t size = sizeof(struct sockaddr_in); if( getpeername(fd, (struct sockaddr*)&remoteName, &size) ) return 0; return inet_ntoa(remoteName.sin_addr); #endif } /* ** This routine handles a single HTTP request which is coming in on ** g.httpIn and which replies on g.httpOut ** ** The HTTP 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_http_request(const char *zIpAddr){ char *z, *zToken; int i; const char *zScheme = "http"; char zLine[2000]; /* A single line of input. */ g.fullHttpReply = 1; g.zReqType = "HTTP"; if( cgi_fgets(zLine, sizeof(zLine))==0 ){ malformed_request("missing header"); } blob_append(&g.httpHeader, zLine, -1); 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: \"%s\" - Fossil only supports" "GET, POST, and HEAD", zToken); } cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); cgi_setenv("REQUEST_METHOD",zToken); zToken = extract_token(z, &z); if( zToken==0 ){ malformed_request("malformed URI in the HTTP header"); } 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 ){ zIpAddr = cgi_remote_ip(fileno(g.httpIn)); } if( zIpAddr ){ cgi_setenv("REMOTE_ADDR", zIpAddr); g.zIpAddr = fossil_strdup(zIpAddr); } /* Get all the optional fields that follow the first line. */ while( cgi_fgets(zLine,sizeof(zLine)) ){ 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); zScheme = "https"; }else if( fossil_strcmp(zFieldName,"host:")==0 ){ char *z; cgi_setenv("HTTP_HOST", zVal); z = strchr(zVal, ':'); if( z ) z[0] = 0; cgi_setenv("SERVER_NAME", 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,"authorization:")==0 ){ cgi_setenv("HTTP_AUTHORIZATION", zVal); }else if( fossil_strcmp(zFieldName,"accept-language:")==0 ){ cgi_setenv("HTTP_ACCEPT_LANGUAGE", zVal); }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ const char *zIpAddr = cgi_accept_forwarded_for(zVal); if( zIpAddr!=0 ){ g.zIpAddr = fossil_strdup(zIpAddr); cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); } }else if( fossil_strcmp(zFieldName,"range:")==0 ){ int x1 = 0; int x2 = 0; if( sscanf(zVal,"bytes=%d-%d",&x1,&x2)==2 && x1>=0 && x1<=x2 ){ rangeStart = x1; rangeEnd = x2+1; } } } cgi_setenv("REQUEST_SCHEME",zScheme); 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. */ assert( !g.httpUseSSL ); #ifdef FOSSIL_ENABLE_JSON if( nCycles==0 ){ json_bootstrap_early(); } #endif if( zIpAddr ){ if( nCycles==0 ){ cgi_setenv("REMOTE_ADDR", zIpAddr); g.zIpAddr = fossil_strdup(zIpAddr); } }else{ fossil_fatal("missing SSH IP address"); } g.zReqType = "HTTP"; 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", fossil_strdup(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 = fossil_strdup(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 */ assert( !g.httpUseSSL ); 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 fossil_strdup(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. */ assert( !g.httpUseSSL ); /* 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; assert( !g.httpUseSSL ); 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 ){ for(n=0; n<nHdr && zHdr[n]; n++){} for(m=n+1; m<nHdr && zHdr[m]; m++){} if( m>=nHdr ) malformed_request("SCGI header formatting error"); cgi_set_parameter(zHdr, zHdr+n+1); zHdr += m+1; nHdr -= m+1; } 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 */ #define HTTP_SERVER_NOFORK 0x0020 /* Do not call fork() */ #endif /* INTERFACE */ /* ** Maximum number of child processes that we can have running ** at one time. Set this to 0 for "no limit". */ #ifndef FOSSIL_MAX_CONNECTIONS # define FOSSIL_MAX_CONNECTIONS 1000 #endif /* ** Implement an HTTP server daemon listening on port iPort. ** ** As new connections arrive, fork a child and let child return ** out of this procedure call. The child will handle the request. ** The parent never returns from this procedure. ** ** Return 0 to each child as it runs. If unable to establish a ** listening socket, return non-zero. */ int cgi_http_server( int mnPort, int mxPort, /* Range of TCP ports to try */ const char *zBrowser, /* Run this browser, if not NULL */ const char *zIpAddr, /* Bind to this IP address, if not null */ int flags /* HTTP_SERVER_* flags */ ){ #if defined(_WIN32) /* Use win32_http_server() instead */ fossil_exit(1); #else int listener = -1; /* The server socket */ int connection; /* A socket for each individual connection */ int nRequest = 0; /* Number of requests handled so far */ fd_set readfds; /* Set of file descriptors for select() */ socklen_t lenaddr; /* Length of the inaddr structure */ int child; /* PID of the child process */ int nchildren = 0; /* Number of child processes */ struct timeval delay; /* How long to wait inside select() */ struct sockaddr_in inaddr; /* The socket address */ int opt = 1; /* setsockopt flag */ int iPort = mnPort; while( iPort<=mxPort ){ memset(&inaddr, 0, sizeof(inaddr)); inaddr.sin_family = AF_INET; if( zIpAddr ){ inaddr.sin_addr.s_addr = inet_addr(zIpAddr); if( inaddr.sin_addr.s_addr == INADDR_NONE ){ fossil_fatal("not a valid IP address: %s", zIpAddr); } }else if( flags & HTTP_SERVER_LOCALHOST ){ inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); }else{ inaddr.sin_addr.s_addr = htonl(INADDR_ANY); } inaddr.sin_port = htons(iPort); listener = socket(AF_INET, SOCK_STREAM, 0); if( listener<0 ){ iPort++; continue; } /* if we can't terminate nicely, at least allow the socket to be reused */ setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); if( bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr))<0 ){ close(listener); iPort++; continue; } break; } if( iPort>mxPort ){ if( mnPort==mxPort ){ fossil_fatal("unable to open listening socket on port %d", mnPort); }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" : g.httpUseSSL?"TLS-encrypted HTTPS":"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( fossil_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 if( fossil_system(zBrowser)<0 ){ fossil_warning("cannot start browser: %s\n", zBrowser); } } while( 1 ){ #if FOSSIL_MAX_CONNECTIONS>0 while( nchildren>=FOSSIL_MAX_CONNECTIONS ){ if( wait(0)>=0 ) nchildren--; } #endif delay.tv_sec = 0; delay.tv_usec = 100000; FD_ZERO(&readfds); assert( listener>=0 ); FD_SET( listener, &readfds); select( listener+1, &readfds, 0, 0, &delay); if( FD_ISSET(listener, &readfds) ){ lenaddr = sizeof(inaddr); connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr); if( connection>=0 ){ if( flags & HTTP_SERVER_NOFORK ){ child = 0; }else{ child = fork(); } if( child!=0 ){ if( child>0 ){ nchildren++; nRequest++; } close(connection); }else{ int nErr = 0, fd; close(0); fd = dup(connection); if( fd!=0 ) nErr++; close(1); fd = dup(connection); if( fd!=1 ) nErr++; if( 0 && !g.fAnyTrace ){ close(2); fd = dup(connection); if( fd!=2 ) nErr++; } close(connection); g.nPendingRequest = nchildren+1; g.nRequest = nRequest+1; return nErr; } } } /* Bury dead children */ if( nchildren ){ while(1){ int iStatus = 0; pid_t x = waitpid(-1, &iStatus, WNOHANG); if( x<=0 ) break; if( WIFSIGNALED(iStatus) && g.fAnyTrace ){ fprintf(stderr, "/***** Child %d exited on signal %d (%s) *****/\n", x, WTERMSIG(iStatus), strsignal(WTERMSIG(iStatus))); } nchildren--; } } } /* NOT REACHED */ fossil_exit(1); #endif /* NOT REACHED */ return 0; } /* ** Name of days and months. */ static const char *const azDays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 0}; static const char *const azMonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0}; /* ** Returns an RFC822-formatted time string suitable for HTTP headers. ** The timezone is always GMT. The value returned is always a ** string obtained from mprintf() and must be freed using fossil_free() ** to avoid a memory leak. ** ** See http://www.faqs.org/rfcs/rfc822.html, section 5 ** and http://www.faqs.org/rfcs/rfc2616.html, section 3.3. */ char *cgi_rfc822_datestamp(time_t now){ struct tm *pTm; pTm = gmtime(&now); if( pTm==0 ){ return mprintf(""); }else{ return mprintf("%s, %d %s %02d %02d:%02d:%02d +0000", azDays[pTm->tm_wday], pTm->tm_mday, azMonths[pTm->tm_mon], pTm->tm_year+1900, pTm->tm_hour, pTm->tm_min, pTm->tm_sec); } } /* ** Returns an ISO8601-formatted time string suitable for debugging ** purposes. ** ** The value returned is always a string obtained from mprintf() and must ** be freed using fossil_free() to avoid a memory leak. */ char *cgi_iso8601_datestamp(void){ struct tm *pTm; time_t now = time(0); pTm = gmtime(&now); if( pTm==0 ){ return mprintf(""); }else{ return mprintf("%04d-%02d-%02d %02d:%02d:%02d", pTm->tm_year+1900, pTm->tm_mon+1, pTm->tm_mday, pTm->tm_hour, pTm->tm_min, pTm->tm_sec); } } /* ** COMMAND: test-date ** ** Show the current date and time in both RFC822 and ISO8601. */ void test_date(void){ fossil_print("%z = ", cgi_iso8601_datestamp()); fossil_print("%z\n", cgi_rfc822_datestamp(time(0))); } /* ** Parse an RFC822-formatted timestamp as we'd expect from HTTP and return ** a Unix epoch time. <= zero is returned on failure. ** ** Note that this won't handle all the _allowed_ HTTP formats, just the ** most popular one (the one generated by cgi_rfc822_datestamp(), actually). */ time_t cgi_rfc822_parsedate(const char *zDate){ int mday, mon, year, yday, hour, min, sec; char zIgnore[4]; char zMonth[4]; static const char *const azMonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0}; if( 7==sscanf(zDate, "%3[A-Za-z], %d %3[A-Za-z] %d %d:%d:%d", zIgnore, &mday, zMonth, &year, &hour, &min, &sec)){ if( year > 1900 ) year -= 1900; for(mon=0; azMonths[mon]; mon++){ if( !fossil_strncmp( azMonths[mon], zMonth, 3 )){ int nDay; int isLeapYr; static int priorDays[] = { 0, 31, 59, 90,120,151,181,212,243,273,304,334 }; if( mon<0 ){ int nYear = (11 - mon)/12; year -= nYear; mon += nYear*12; }else if( mon>11 ){ year += mon/12; mon %= 12; } isLeapYr = year%4==0 && (year%100!=0 || (year+300)%400==0); yday = priorDays[mon] + mday - 1; if( isLeapYr && mon>1 ) yday++; nDay = (year-70)*365 + (year-69)/4 - year/100 + (year+300)/400 + yday; return ((time_t)(nDay*24 + hour)*60 + min)*60 + sec; } } } return 0; } /* ** 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 = fossil_strdup(zSshConn); if( (zIndex = strchr(zSshClient,' '))!=0 ){ zSshClient[zIndex-zSshClient] = '\0'; return zSshClient; } } return zDefault; } /* ** Return true if information is coming from the loopback network. */ int cgi_is_loopback(const char *zIpAddr){ return fossil_strcmp(zIpAddr, "127.0.0.1")==0 || fossil_strcmp(zIpAddr, "::ffff:127.0.0.1")==0 || fossil_strcmp(zIpAddr, "::1")==0; } /* ** Return true if the HTTP request is likely to be from a small-screen ** mobile device. ** ** The returned value is a guess. Use it only for setting up defaults. */ int cgi_from_mobile(void){ const char *zAgent = P("HTTP_USER_AGENT"); if( zAgent==0 ) return 0; if( sqlite3_strglob("*iPad*", zAgent)==0 ) return 0; return sqlite3_strlike("%mobile%", zAgent, 0)==0; } /* ** Look for query or POST parameters that: ** ** (1) Have not been used ** (2) Appear to be malicious attempts to break into or otherwise ** harm the system, for example via SQL injection ** ** If any such parameters are seen, a 418 ("I'm a teapot") return is ** generated and processing aborts - this routine does not return. ** ** When Fossil is launched via CGI from althttpd, the 418 return signals ** the webserver to put the requestor IP address into "timeout", blocking ** subsequent requests for 5 minutes. ** ** Fossil is not subject to any SQL injections, as far as anybody knows. ** This routine is not necessary for the security of the system (though ** an extra layer of security never hurts). The main purpose here is ** to shutdown malicious attack spiders and prevent them from burning ** lots of CPU cycles and bogging down the website. In other words, the ** objective of this routine is to help prevent denial-of-service. ** ** Usage Hint: Put a call to this routine as late in the webpage ** implementation as possible, ideally just before it begins doing ** potentially CPU-intensive computations and after all query parameters ** have been consulted. */ void cgi_check_for_malice(void){ struct QParam * pParam; int i; for(i=0; i<nUsedQP; ++i){ pParam = &aParamQP[i]; if( 0==pParam->isFetched && pParam->zValue!=0 && pParam->zName!=0 && fossil_islower(pParam->zName[0]) ){ cgi_value_spider_check(pParam->zValue, pParam->zName); } } } |
Added src/chat.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 | /* ** Copyright (c) 2020 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 Fossil chatroom. ** ** Initial design goals: ** ** * Keep it simple. This chatroom is not intended as a competitor ** or replacement for IRC, Discord, Telegram, Slack, etc. The goal ** is zero- or near-zero-configuration, not an abundance of features. ** ** * Intended as a place for insiders to have ephemeral conversations ** about a project. This is not a public gather place. Think ** "boardroom", not "corner pub". ** ** * One chatroom per repository. ** ** * Chat content lives in a single repository. It is never synced. ** Content expires and is deleted after a set interval (a week or so). ** ** Notification is accomplished using the "hanging GET" or "long poll" design ** in which a GET request is issued but the server does not send a reply until ** new content arrives. Newer Web Sockets and Server Sent Event protocols are ** more elegant, but are not compatible with CGI, and would thus complicate ** configuration. */ #include "config.h" #include <assert.h> #include "chat.h" /* ** Outputs JS code to initialize a list of chat alert audio files for ** use by the chat front-end client. A handful of builtin files ** (from alerts/\*.wav) and all unversioned files matching ** alert-sounds/\*.{mp3,ogg,wav} are included. */ static void chat_emit_alert_list(void){ unsigned int i; const char * azBuiltins[] = { "builtin/alerts/plunk.wav", "builtin/alerts/bflat2.wav", "builtin/alerts/bflat3.wav", "builtin/alerts/bloop.wav" }; CX("window.fossil.config.chat.alerts = [\n"); for(i=0; i < sizeof(azBuiltins)/sizeof(azBuiltins[0]); ++i){ CX("%s%!j", i ? ", " : "", azBuiltins[i]); } if( db_table_exists("repository","unversioned") ){ Stmt q = empty_Stmt; db_prepare(&q, "SELECT 'uv/'||name FROM unversioned " "WHERE content IS NOT NULL " "AND (name LIKE 'alert-sounds/%%.wav' " "OR name LIKE 'alert-sounds/%%.mp3' " "OR name LIKE 'alert-sounds/%%.ogg')"); while(SQLITE_ROW==db_step(&q)){ CX(", %!j", db_column_text(&q, 0)); } db_finalize(&q); } CX("\n];\n"); } /* Settings that can be used to control chat */ /* ** SETTING: chat-initial-history width=10 default=50 ** ** If this setting has an integer value of N, then when /chat first ** starts up it initializes the screen with the N most recent chat ** messages. If N is zero, then all chat messages are loaded. */ /* ** SETTING: chat-keep-count width=10 default=50 ** ** When /chat is cleaning up older messages, it will always keep ** the most recent chat-keep-count messages, even if some of those ** messages are older than the discard threshold. If this value ** is zero, then /chat is free to delete all historic messages once ** they are old enough. */ /* ** SETTING: chat-keep-days width=10 default=7 ** ** The /chat subsystem will try to discard messages that are older then ** chat-keep-days. The value of chat-keep-days can be a floating point ** number. So, for example, if you only want to keep chat messages for ** 12 hours, set this value to 0.5. ** ** A value of 0.0 or less means that messages are retained forever. */ /* ** SETTING: chat-inline-images boolean default=on ** ** Specifies whether posted images in /chat should default to being ** displayed inline or as downloadable links. Each chat user can ** change this value for their current chat session in the UI. */ /* ** SETTING: chat-poll-timeout width=10 default=420 ** ** On an HTTP request to /chat-poll, if there is no new content available, ** the reply is delayed waiting for new content to arrive. (This is the ** "long poll" strategy of event delivery to the client.) This setting ** determines approximately how long /chat-poll will delay before giving ** up and returning an empty reply. The default value is about 7 minutes, ** which works well for Fossil behind the althttpd web server. Other ** server environments may choose a longer or shorter delay. ** ** For maximum efficiency, it is best to choose the longest delay that ** does not cause timeouts in intermediate proxies or web server. */ /* ** SETTING: chat-alert-sound width=10 ** ** This is the name of the builtin sound file to use for the alert tone. ** The value must be the name of a builtin WAV file. */ /* ** SETTING: chat-timeline-user width=10 ** ** If this setting is defined and is not an empty string, then ** timeline events are posted to the chat as they arrive. The synthesized ** chat messages appear to come from the user identified by this setting, ** not the user on the timeline event. ** ** All chat messages that come from the chat-timeline-user are ** interpreted as text/x-fossil-wiki instead of as text/x-markdown. ** For this reason, the chat-timeline-user name should probably not be ** a real user. */ /* ** WEBPAGE: chat loadavg-exempt ** ** Start up a browser-based chat session. ** ** This is the main page that humans use to access the chatroom. Simply ** point a web-browser at /chat and the screen fills with the latest ** chat messages, and waits for new one. ** ** Other /chat-OP pages are used by XHR requests from this page to ** send new chat message, delete older messages, or poll for changes. */ void chat_webpage(void){ char *zAlert; char *zProjectName; char * zInputPlaceholder0; /* Common text input placeholder value */ const char *zPaperclip = "<svg height=\"8.0\" width=\"16.0\"><path " "stroke=\"rgb(100,100,100)\" " "d=\"M 15.93452,3.2530441 " "A 4.1499493,4.1265346 0 0 0 11.804809,6.5256284e-4 H 2.8582923 A " "2.8239899,2.8080565 0 0 0 0.68965668,0.96142476 2.874599,2.8583801 " "0 0 0 0.03119302,3.2388108 2.7632589,2.7476682 0 0 0 " "0.81132923,4.7689293 3.168132,3.1502569 0 0 0 3.0300653,5.66565 l " "7.7297897,-4e-7 a 1.6802234,1.6707433 0 0 0 0.0072,-3.3377933 H " "5.6138192 v 1.0105899 l 5.1460358,-0.00712 a 0.66804062,0.66427143 " "0 0 1 0,1.3237305 l -7.7226325,0.00712 A 2.0243655,2.0129437 0 0 1 " "1.0332029,3.0964741 1.8522944,1.8418435 0 0 1 2.8511351,1.0041257 h " "8.9465169 a 3.1478884,3.1301275 0 0 1 3.134859,2.4339559 3.0365483," "3.0194156 0 0 1 -0.629835,2.4908908 3.0365483,3.0194156 0 0 1 " "-2.31178,1.0746415 l -7.5437026,-0.014233 -0.00716,1.0034736 " "7.5365456,0.00715 a 4.048731,4.0258875 0 0 0 3.957938,-4.7469259 z\"" "/></svg>"; login_check_credentials(); if( !g.perm.Chat ){ login_needed(g.anon.Chat); return; } zAlert = mprintf("%s/builtin/%s", g.zBaseURL, db_get("chat-alert-sound","alerts/plunk.wav")); zProjectName = db_get("project-name","Unnamed project"); zInputPlaceholder0 = mprintf("Type markdown-formatted message for %h.", zProjectName); style_set_current_feature("chat"); style_header("Chat"); @ <div id='chat-input-area'> @ <div id='chat-input-line-wrapper' class='compact'> @ <input type="text" id="chat-input-field-single" \ @ data-placeholder0="%h(zInputPlaceholder0)" \ @ data-placeholder="%h(zInputPlaceholder0)" \ @ class="chat-input-field"></input> @ <textarea id="chat-input-field-multi" \ @ data-placeholder0="%h(zInputPlaceholder0)" \ @ data-placeholder="%h(zInputPlaceholder0)" \ @ class="chat-input-field hidden"></textarea> @ <div contenteditable id="chat-input-field-x" \ @ data-placeholder0="%h(zInputPlaceholder0)" \ @ data-placeholder="%h(zInputPlaceholder0)" \ @ class="chat-input-field hidden"></div> @ <div id='chat-buttons-wrapper'> @ <span class='cbutton' id="chat-button-preview" \ @ title="Preview message (Shift-Enter)">👁</span> @ <span class='cbutton' id="chat-button-attach" \ @ title="Attach file to message">%s(zPaperclip)</span> @ <span class='cbutton' id="chat-button-settings" \ @ title="Configure chat">⚙</span> @ <span class='cbutton' id="chat-button-submit" \ @ title="Send message (Ctrl-Enter)">📤</span> @ </div> @ </div> @ <div id='chat-input-file-area'> @ <div class='file-selection-wrapper hidden'> @ <input type="file" name="file" id="chat-input-file"> @ </div> @ <div id="chat-drop-details"></div> @ </div> @ </div> @ <div id='chat-user-list-wrapper' class='hidden'> @ <div class='legend'> @ <span class='help-buttonlet'> @ Users who have messages in the currently-loaded list.<br><br> @ <strong>Tap a user name</strong> to filter messages @ on that user and tap again to clear the filter.<br><br> @ <strong>Tap the title</strong> of this widget to toggle @ the list on and off. @ </span> @ <span>Active users (sorted by last message time)</span> @ </div> @ <div id='chat-user-list'></div> @ </div> @ <div id='chat-preview' class='hidden chat-view'> @ <header>Preview: (<a href='%R/md_rules' target='_blank'>markdown reference</a>)</header> @ <div id='chat-preview-content' class='message-widget-content'></div> @ <div id='chat-preview-buttons'><button id='chat-preview-close'>Close Preview</button></div> @ </div> @ <div id='chat-config' class='hidden chat-view'> @ <div id='chat-config-options'></div> /* ^^^populated client-side */ @ <button>Close Settings</button> @ </div> @ <div id='chat-messages-wrapper' class='chat-view'> /* New chat messages get inserted immediately after this element */ @ <span id='message-inject-point'></span> @ </div> fossil_free(zProjectName); fossil_free(zInputPlaceholder0); builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch", "pikchr", "confirmer", "copybutton", NULL); /* Always in-line the javascript for the chat page */ @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */ /* We need an onload handler to ensure that window.fossil is initialized before the chat init code runs. */ @ window.addEventListener('load', function(){ @ document.body.classList.add('chat'); @ /*^^^for skins which add their own BODY tag */; @ window.fossil.config.chat = { @ fromcli: %h(PB("cli")?"true":"false"), @ alertSound: "%h(zAlert)", @ initSize: %d(db_get_int("chat-initial-history",50)), @ imagesInline: !!%d(db_get_boolean("chat-inline-images",1)) @ }; ajax_emit_js_preview_modes(0); chat_emit_alert_list(); @ }, false); @ </script> builtin_request_js("fossil.page.chat.js"); style_finish_page(); } /* Definition of repository tables used by chat */ static const char zChatSchema1[] = @ CREATE TABLE repository.chat( @ msgid INTEGER PRIMARY KEY AUTOINCREMENT, @ mtime JULIANDAY, -- Time for this entry - Julianday Zulu @ lmtime TEXT, -- Client YYYY-MM-DDZHH:MM:SS when message originally sent @ xfrom TEXT, -- Login of the sender @ xmsg TEXT, -- Raw, unformatted text of the message @ fname TEXT, -- Filename of the uploaded file, or NULL @ fmime TEXT, -- MIMEType of the upload file, or NULL @ mdel INT, -- msgid of another message to delete @ file BLOB -- Text of the uploaded file, or NULL @ ); ; /* ** Make sure the repository data tables used by chat exist. Create them ** if they do not. */ static void chat_create_tables(void){ if( !db_table_exists("repository","chat") ){ db_multi_exec(zChatSchema1/*works-like:""*/); }else if( !db_table_has_column("repository","chat","lmtime") ){ if( !db_table_has_column("repository","chat","mdel") ){ db_multi_exec("ALTER TABLE chat ADD COLUMN mdel INT"); } db_multi_exec("ALTER TABLE chat ADD COLUMN lmtime TEXT"); } } /* ** Delete old content from the chat table. */ static void chat_purge(void){ int mxCnt = db_get_int("chat-keep-count",50); double mxDays = atof(db_get("chat-keep-days","7")); double rAge; int msgid; rAge = db_double(0.0, "SELECT julianday('now')-mtime FROM chat" " ORDER BY msgid LIMIT 1"); if( rAge>mxDays ){ msgid = db_int(0, "SELECT msgid FROM chat" " ORDER BY msgid DESC LIMIT 1 OFFSET %d", mxCnt); if( msgid>0 ){ Stmt s; db_multi_exec("PRAGMA secure_delete=ON;"); db_prepare(&s, "DELETE FROM chat WHERE mtime<julianday('now')-:mxage" " AND msgid<%d", msgid); db_bind_double(&s, ":mxage", mxDays); db_step(&s); db_finalize(&s); } } } /* ** Sets the current CGI response type to application/json then emits a ** JSON-format error message object. If fAsMessageList is true then ** the object is output using the list format described for chat-poll, ** else it is emitted as a single object in that same format. */ static void chat_emit_permissions_error(int fAsMessageList){ char * zTime = cgi_iso8601_datestamp(); cgi_set_content_type("application/json"); if(fAsMessageList){ CX("{\"msgs\":[{"); }else{ CX("{"); } CX("\"isError\": true, \"xfrom\": null,"); CX("\"mtime\": %!j, \"lmtime\": %!j,", zTime, zTime); CX("\"xmsg\": \"Missing permissions or not logged in. " "Try <a href='%R/login?g=chat'>logging in</a>.\""); if(fAsMessageList){ CX("}]}"); }else{ CX("}"); } fossil_free(zTime); } /* ** WEBPAGE: chat-send hidden loadavg-exempt ** ** This page receives (via XHR) a new chat-message and/or a new file ** to be entered into the chat history. ** ** On success it responds with an empty response: the new message ** should be fetched via /chat-poll. On error, e.g. login expiry, ** it emits a JSON response in the same form as described for ** /chat-poll errors, but as a standalone object instead of a ** list of objects. ** ** Requests to this page should be POST, not GET. POST parameters ** include: ** ** msg The (Markdown) text of the message to be sent ** ** file The content of the file attachment ** ** lmtime ISO-8601 formatted date-time string showing the local time ** of the sender. ** ** At least one of the "msg" or "file" POST parameters must be provided. */ void chat_send_webpage(void){ int nByte; const char *zMsg; const char *zUserName; login_check_credentials(); if( 0==g.perm.Chat ) { chat_emit_permissions_error(0); return; } chat_create_tables(); zUserName = (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody"; nByte = atoi(PD("file:bytes","0")); zMsg = PD("msg",""); db_begin_write(); db_unprotect(PROTECT_READONLY); chat_purge(); if( nByte==0 ){ if( zMsg[0] ){ db_multi_exec( "INSERT INTO chat(mtime,lmtime,xfrom,xmsg)" "VALUES(julianday('now'),%Q,%Q,%Q)", P("lmtime"), zUserName, zMsg ); } }else{ Stmt q; Blob b; db_prepare(&q, "INSERT INTO chat(mtime,lmtime,xfrom,xmsg,file,fname,fmime)" "VALUES(julianday('now'),%Q,%Q,%Q,:file,%Q,%Q)", P("lmtime"), zUserName, zMsg, PD("file:filename",""), PD("file:mimetype","application/octet-stream")); blob_init(&b, P("file"), nByte); db_bind_blob(&q, ":file", &b); db_step(&q); db_finalize(&q); blob_reset(&b); } db_protect_pop(); db_commit_transaction(); } /* ** This routine receives raw (user-entered) message text and ** transforms it into HTML that is safe to insert using innerHTML. As ** of 2021-09-19, it does so by using wiki_convert() or ** markdown_to_html() to convert wiki/markdown-formatted zMsg to HTML. ** ** Space to hold the returned string is obtained from fossil_malloc() ** and must be freed by the caller. */ static char *chat_format_to_html(const char *zMsg, int isWiki){ Blob out; blob_init(&out, "", 0); if( zMsg==0 || zMsg[0]==0 ){ /* No-op */ }else if( isWiki ){ /* Used for chat-timeline-user. The zMsg is text/x-fossil-wiki. */ Blob bIn; blob_init(&bIn, zMsg, (int)strlen(zMsg)); wiki_convert(&bIn, &out, WIKI_INLINE); }else{ /* The common case: zMsg is text/x-markdown */ Blob bIn; blob_init(&bIn, zMsg, (int)strlen(zMsg)); markdown_to_html(&bIn, NULL, &out); } return blob_str(&out); } /* ** COMMAND: test-chat-formatter ** ** Usage: %fossil test-chat-formatter STRING ... ** ** Transform each argument string into HTML that will display the ** chat message. This is used to test the formatter and to verify ** that a malicious message text will not cause HTML or JS injection ** into the chat display in a browser. */ void chat_test_formatter_cmd(void){ int i; char *zOut; db_find_and_open_repository(0,0); g.perm.Hyperlink = 1; for(i=0; i<g.argc; i++){ zOut = chat_format_to_html(g.argv[i], 0); fossil_print("[%d]: %s\n", i, zOut); fossil_free(zOut); } } /* ** WEBPAGE: chat-poll hidden loadavg-exempt ** ** The chat page generated by /chat using an XHR to this page to ** request new chat content. A typical invocation is: ** ** /chat-poll/N ** /chat-poll?name=N ** ** The "name" argument should begin with an integer which is the largest ** "msgid" that the chat page currently holds. If newer content is ** available, this routine returns that content straight away. If no new ** content is available, this webpage blocks until the new content becomes ** available. In this way, the system implements "hanging-GET" or "long-poll" ** style event notification. If no new content arrives after a delay of ** approximately chat-poll-timeout seconds (default: 420), then reply is ** sent with an empty "msg": field. ** ** If N is negative, then the return value is the N most recent messages. ** Hence a request like /chat-poll/-100 can be used to initialize a new ** chat session to just the most recent messages. ** ** Some webservers (althttpd) do not allow a term of the URL path to ** begin with "-". Then /chat-poll/-100 cannot be used. Instead you ** have to say "/chat-poll?name=-100". ** ** If the integer parameter "before" is passed in, it is assumed that ** the client is requesting older messages, up to (but not including) ** that message ID, in which case the next-oldest "n" messages ** (default=chat-initial-history setting, equivalent to n=0) are ** returned (negative n fetches all older entries). The client then ** needs to take care to inject them at the end of the history rather ** than the same place new messages go. ** ** If "before" is provided, "name" is ignored. ** ** If "raw" is provided, the "xmsg" text is sent back as-is, in ** markdown format, rather than being HTML-ized. This is not used or ** supported by fossil's own chat client but is intended for 3rd-party ** clients. (Specifically, for Brad Harder's curses-based client.) ** ** The reply from this webpage is JSON that describes the new content. ** Format of the json: ** ** | { ** | "msgs":[ ** | { ** | "msgid": integer // message id ** | "mtime": text // When sent: YYYY-MM-DDTHH:MM:SSZ ** | "lmtime: text // Sender's client-side YYYY-MM-DDTHH:MM:SS ** | "xfrom": text // Login name of sender ** | "uclr": text // Color string associated with the user ** | "xmsg": text // HTML text of the message ** | "fsize": integer // file attachment size in bytes ** | "fname": text // Name of file attachment ** | "fmime": text // MIME-type of file attachment ** | "mdel": integer // message id of prior message to delete ** | } ** | ] ** | } ** ** The "fname" and "fmime" fields are only present if "fsize" is greater ** than zero. The "xmsg" field may be an empty string if "fsize" is zero. ** ** The "msgid" values will be in increasing order. ** ** The "mdel" will only exist if "xmsg" is an empty string and "fsize" is zero. ** ** The "lmtime" value might be unknown, in which case it is omitted. ** ** The messages are ordered oldest first unless "before" is provided, in which ** case they are sorted newest first (to facilitate the client-side UI update). ** ** As a special case, if this routine encounters an error, e.g. the user's ** permissions cannot be verified because their login cookie expired, the ** request returns a slightly modified structure: ** ** | { ** | "msgs":[ ** | { ** | "isError": true, ** | "xfrom": null, ** | "xmsg": "error details" ** | "mtime": as above, ** | "ltime": same as mtime ** | } ** | ] ** | } ** ** If the client gets such a response, it should display the message ** in a prominent manner and then stop polling for new messages. */ void chat_poll_webpage(void){ Blob json; /* The json to be constructed and returned */ sqlite3_int64 dataVersion; /* Data version. Used for polling. */ const int iDelay = 1000; /* Delay until next poll (milliseconds) */ int nDelay; /* Maximum delay.*/ const char *zChatUser; /* chat-timeline-user */ int isWiki = 0; /* True if chat message is x-fossil-wiki */ int msgid = atoi(PD("name","0")); const int msgBefore = atoi(PD("before","0")); int nLimit = msgBefore>0 ? atoi(PD("n","0")) : 0; const int bRaw = P("raw")!=0; Blob sql = empty_blob; Stmt q1; nDelay = db_get_int("chat-poll-timeout",420); /* Default about 7 minutes */ login_check_credentials(); if( !g.perm.Chat ) { chat_emit_permissions_error(1); return; } zChatUser = db_get("chat-timeline-user",0); chat_create_tables(); cgi_set_content_type("application/json"); dataVersion = db_int64(0, "PRAGMA data_version"); blob_append_sql(&sql, "SELECT msgid, datetime(mtime), xfrom, xmsg, octet_length(file)," " fname, fmime, %s, lmtime" " FROM chat ", msgBefore>0 ? "0 as mdel" : "mdel"); if( msgid<=0 || msgBefore>0 ){ db_begin_write(); chat_purge(); db_commit_transaction(); } if(msgBefore>0){ if(0==nLimit){ nLimit = db_get_int("chat-initial-history",50); } blob_append_sql(&sql, " WHERE msgid<%d" " ORDER BY msgid DESC " "LIMIT %d", msgBefore, nLimit>0 ? nLimit : -1 ); }else{ if( msgid<0 ){ msgid = db_int(0, "SELECT msgid FROM chat WHERE mdel IS NOT true" " ORDER BY msgid DESC LIMIT 1 OFFSET %d", -msgid); } blob_append_sql(&sql, " WHERE msgid>%d" " ORDER BY msgid", msgid ); } db_prepare(&q1, "%s", blob_sql_text(&sql)); blob_reset(&sql); blob_init(&json, "{\"msgs\":[\n", -1); while( nDelay>0 ){ int cnt = 0; while( db_step(&q1)==SQLITE_ROW ){ int id = db_column_int(&q1, 0); const char *zDate = db_column_text(&q1, 1); const char *zFrom = db_column_text(&q1, 2); const char *zRawMsg = db_column_text(&q1, 3); int nByte = db_column_int(&q1, 4); const char *zFName = db_column_text(&q1, 5); const char *zFMime = db_column_text(&q1, 6); int iToDel = db_column_int(&q1, 7); const char *zLMtime = db_column_text(&q1, 8); char *zMsg; if(cnt++){ blob_append(&json, ",\n", 2); } blob_appendf(&json, "{\"msgid\":%d,", id); blob_appendf(&json, "\"mtime\":\"%.10sT%sZ\",", zDate, zDate+11); if( zLMtime && zLMtime[0] ){ blob_appendf(&json, "\"lmtime\":%!j,", zLMtime); } blob_append(&json, "\"xfrom\":", -1); if(zFrom){ blob_appendf(&json, "%!j,", zFrom); isWiki = fossil_strcmp(zFrom,zChatUser)==0; }else{ /* see https://fossil-scm.org/forum/forumpost/e0be0eeb4c */ blob_appendf(&json, "null,"); isWiki = 0; } blob_appendf(&json, "\"uclr\":%!j,", isWiki ? "transparent" : user_color(zFrom ? zFrom : "nobody")); if(bRaw){ blob_appendf(&json, "\"xmsg\":%!j,", zRawMsg); }else{ zMsg = chat_format_to_html(zRawMsg ? zRawMsg : "", isWiki); blob_appendf(&json, "\"xmsg\":%!j,", zMsg); fossil_free(zMsg); } if( nByte==0 ){ blob_appendf(&json, "\"fsize\":0"); }else{ blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j", nByte, zFName, zFMime); } if( iToDel ){ blob_appendf(&json, ",\"mdel\":%d}", iToDel); }else{ blob_append(&json, "}", 1); } } db_reset(&q1); if( cnt || msgBefore>0 ){ break; } sqlite3_sleep(iDelay); nDelay--; while( nDelay>0 ){ sqlite3_int64 newDataVers = db_int64(0,"PRAGMA repository.data_version"); if( newDataVers!=dataVersion ){ dataVersion = newDataVers; break; } sqlite3_sleep(iDelay); nDelay--; } } /* Exit by "break" */ db_finalize(&q1); blob_append(&json, "\n]}", 3); cgi_set_content(&json); return; } /* ** WEBPAGE: chat-fetch-one hidden loadavg-exempt ** ** /chat-fetch-one/N ** ** Fetches a single message with the given ID, if available. ** ** Options: ** ** raw = the xmsg field will be returned unparsed. ** ** Response is either a single object in the format returned by ** /chat-poll (without the wrapper array) or a JSON-format error ** response, as documented for ajax_route_error(). */ void chat_fetch_one(void){ Blob json = empty_blob; /* The json to be constructed and returned */ const int fRaw = PD("raw",0)!=0; const int msgid = atoi(PD("name","0")); const char *zChatUser; int isWiki; Stmt q; login_check_credentials(); if( !g.perm.Chat ) { chat_emit_permissions_error(0); return; } zChatUser = db_get("chat-timeline-user",0); chat_create_tables(); cgi_set_content_type("application/json"); db_prepare(&q, "SELECT datetime(mtime), xfrom, xmsg, octet_length(file)," " fname, fmime, lmtime" " FROM chat WHERE msgid=%d AND mdel IS NULL", msgid); if(SQLITE_ROW==db_step(&q)){ const char *zDate = db_column_text(&q, 0); const char *zFrom = db_column_text(&q, 1); const char *zRawMsg = db_column_text(&q, 2); const int nByte = db_column_int(&q, 3); const char *zFName = db_column_text(&q, 4); const char *zFMime = db_column_text(&q, 5); const char *zLMtime = db_column_text(&q, 7); blob_appendf(&json,"{\"msgid\": %d,", msgid); blob_appendf(&json, "\"mtime\":\"%.10sT%sZ\",", zDate, zDate+11); if( zLMtime && zLMtime[0] ){ blob_appendf(&json, "\"lmtime\":%!j,", zLMtime); } blob_append(&json, "\"xfrom\":", -1); if(zFrom){ blob_appendf(&json, "%!j,", zFrom); isWiki = fossil_strcmp(zFrom, zChatUser)==0; }else{ /* see https://fossil-scm.org/forum/forumpost/e0be0eeb4c */ blob_appendf(&json, "null,"); isWiki = 0; } blob_appendf(&json, "\"uclr\":%!j,", isWiki ? "transparent" : user_color(zFrom ? zFrom : "nobody")); blob_append(&json,"\"xmsg\":", 7); if(fRaw){ blob_appendf(&json, "%!j,", zRawMsg); }else{ char * zMsg = chat_format_to_html(zRawMsg ? zRawMsg : "", isWiki); blob_appendf(&json, "%!j,", zMsg); fossil_free(zMsg); } if( nByte==0 ){ blob_appendf(&json, "\"fsize\":0"); }else{ blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j", nByte, zFName, zFMime); } blob_append(&json,"}",1); cgi_set_content(&json); }else{ ajax_route_error(404,"Chat message #%d not found.", msgid); } db_finalize(&q); } /* ** WEBPAGE: chat-download hidden loadavg-exempt ** ** Download the CHAT.FILE attachment associated with a single chat ** entry. The "name" query parameter begins with an integer that ** identifies the particular chat message. The integer may be followed ** by a / and a filename, which will (A) indicate to the browser to ** use the indicated name when saving the file and (B) be used to ** guess the mimetype in some particular cases involving the "render" ** flag. ** ** If the "render" URL parameter is provided, the blob has a size ** greater than zero, and blob meets one of the following conditions ** then the fossil-rendered form of that content is returned, rather ** than the original: ** ** - Mimetype is text/x-markdown or text/markdown: emit HTML. ** ** - Mimetype is text/x-fossil-wiki or P("name") ends with ".wiki": ** emit HTML. ** ** - Mimetype is text/x-pikchr or P("name") ends with ".pikchr": emit ** image/svg+xml if rendering succeeds or text/html if rendering ** fails. */ void chat_download_webpage(void){ int msgid; Blob r; const char *zMime; const char *zName = PD("name","0"); login_check_credentials(); if( !g.perm.Chat ){ style_header("Chat Not Authorized"); @ <h1>Not Authorized</h1> @ <p>You do not have permission to use the chatroom on this @ repository.</p> style_finish_page(); return; } chat_create_tables(); msgid = atoi(zName); blob_zero(&r); zMime = db_text(0, "SELECT fmime FROM chat wHERE msgid=%d", msgid); if( zMime==0 ) return; db_blob(&r, "SELECT file FROM chat WHERE msgid=%d", msgid); if( r.nUsed>0 && P("render")!=0 ){ /* Maybe return fossil-rendered form of the content. */ Blob r2 = BLOB_INITIALIZER; /* output target for rendering */ const char * zMime2 = 0; /* adjusted response mimetype */ if(fossil_strcmp(zMime, "text/x-markdown")==0 /* Firefox uploads md files with the mimetype text/markdown */ || fossil_strcmp(zMime, "text/markdown")==0){ markdown_to_html(&r, 0, &r2); safe_html(&r2); zMime2 = "text/html"; }else if(fossil_strcmp(zMime, "text/x-fossil-wiki")==0 || sqlite3_strglob("*.wiki", zName)==0){ /* .wiki files get uploaded as application/octet-stream */ wiki_convert(&r, &r2, 0); zMime2 = "text/html"; }else if(fossil_strcmp(zMime, "text/x-pikchr")==0 || sqlite3_strglob("*.pikchr",zName)==0){ /* .pikchr files get uploaded as application/octet-stream */ const char *zPikchr = blob_str(&r); int w = 0, h = 0; char *zOut = pikchr(zPikchr, "pikchr", 0, &w, &h); if(zOut){ blob_append(&r2, zOut, -1); } zMime2 = w>0 ? "image/svg+xml" : "text/html"; free(zOut); } if(r2.aData!=0){ blob_swap(&r, &r2); blob_reset(&r2); zMime = zMime2; } } cgi_set_content_type(zMime); cgi_set_content(&r); } /* ** WEBPAGE: chat-delete hidden loadavg-exempt ** ** Delete the chat entry identified by the name query parameter. ** Invoking fetch("chat-delete/"+msgid) from javascript in the client ** will delete a chat entry from the CHAT table. ** ** This routine both deletes the identified chat entry and also inserts ** a new entry with the current timestamp and with: ** ** * xmsg = NULL ** ** * file = NULL ** ** * mdel = The msgid of the row that was deleted ** ** This new entry will then be propagated to all listeners so that they ** will know to delete their copies of the message too. */ void chat_delete_webpage(void){ int mdel; char *zOwner; login_check_credentials(); if( !g.perm.Chat ) return; chat_create_tables(); mdel = atoi(PD("name","0")); zOwner = db_text(0, "SELECT xfrom FROM chat WHERE msgid=%d", mdel); if( zOwner==0 ) return; if( fossil_strcmp(zOwner, g.zLogin)!=0 && !g.perm.Admin ) return; db_multi_exec( "PRAGMA secure_delete=ON;\n" "BEGIN;\n" "DELETE FROM chat WHERE msgid=%d;\n" "INSERT INTO chat(mtime, xfrom, mdel)" " VALUES(julianday('now'), %Q, %d);\n" "COMMIT;", mdel, g.zLogin, mdel ); } /* ** WEBPAGE: chat-backup hidden ** ** Download an SQLite database containing all chat content with a ** message-id larger than the "msgid" query parameter. Setup ** privilege is required to use this URL. ** ** This is used to implement the "fossil chat pull" command. */ void chat_backup_webpage(void){ int msgid; unsigned char *pDb = 0; sqlite3_int64 szDb = 0; Blob chatDb; login_check_credentials(); if( !g.perm.Setup ) return; msgid = atoi(PD("msgid","0")); db_multi_exec( "ATTACH ':memory:' AS mem1;\n" "PRAGMA mem1.page_size=512;\n" "CREATE TABLE mem1.chat AS SELECT * FROM repository.chat WHERE msgid>%d;\n", msgid ); pDb = sqlite3_serialize(g.db, "mem1", &szDb, 0); if( pDb==0 ){ fossil_fatal("Out of memory"); } blob_init(&chatDb, (const char*)pDb, (int)szDb); cgi_set_content_type("application/x-sqlite3"); cgi_set_content(&chatDb); } /* ** SQL Function: chat_msg_from_event(TYPE,OBJID,USER,MSG) ** ** This function returns HTML text that describes an entry from the EVENT ** table (that is, a timeline event) for display in chat. Parameters: ** ** TYPE The event type. 'ci', 'w', 't', 'g', and so forth ** OBJID EVENT.OBJID ** USER coalesce(EVENT.EUSER,EVENT.USER) ** MSG coalesce(EVENT.ECOMMENT, EVENT.COMMENT) ** ** This function is intended to be called by the temp.chat_trigger1 trigger ** which is created by alert_create_trigger() routine. */ void chat_msg_from_event( 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 *zUser = (const char*)sqlite3_value_text(argv[2]); const char *zMsg = (const char*)sqlite3_value_text(argv[3]); char *zRes = 0; if( zType==0 || zUser==0 || zMsg==0 ) return; if( zType[0]=='c' ){ /* Check-ins */ char *zBranch; char *zUuid; zBranch = db_text(0, "SELECT value FROM tagxref" " WHERE tagxref.rid=%d" " AND tagxref.tagid=%d" " AND tagxref.tagtype>0", rid, TAG_BRANCH); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); zRes = mprintf("%W (check-in: <a href='%R/info/%S'>%S</a>, " "user: <a href='%R/timeline?u=%t&c=%S'>%h</a>, " "branch: <a href='%R/timeline?r=%t&c=%S'>%h</a>)", zMsg, zUuid, zUuid, zUser, zUuid, zUser, zBranch, zUuid, zBranch ); fossil_free(zBranch); fossil_free(zUuid); }else if( zType[0]=='w' ){ /* Wiki page changes */ char *zUuid; zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); wiki_hyperlink_override(zUuid); if( zMsg[0]=='-' ){ zRes = mprintf("Delete wiki page <a href='%R/whistory?name=%t'>%h</a>", zMsg+1, zMsg+1); }else if( zMsg[0]=='+' ){ zRes = mprintf("Added wiki page <a href='%R/whistory?name=%t'>%h</a>", zMsg+1, zMsg+1); }else if( zMsg[0]==':' ){ zRes = mprintf("<a href='%R/wdiff?id=%!S'>Changes</a> to wiki page " "<a href='%R/whistory?name=%t'>%h</a>", zUuid, zMsg+1, zMsg+1); }else{ zRes = mprintf("%W", zMsg); } wiki_hyperlink_override(0); fossil_free(zUuid); }else{ /* Anything else */ zRes = mprintf("%W", zMsg); } if( zRes ){ sqlite3_result_text(context, zRes, -1, fossil_free); } } /* ** COMMAND: chat ** ** Usage: %fossil chat [SUBCOMMAND] [--remote URL] [ARGS...] ** ** This command performs actions associated with the /chat instance ** on the default remote Fossil repository (the Fossil repository whose ** URL shows when you run the "fossil remote" command) or to the URL ** specified by the --remote option. If there is no default remote ** Fossil repository and the --remote option is omitted, then this ** command fails with an error. ** ** Subcommands: ** ** > fossil chat ** ** When there is no SUBCOMMAND (when this command is simply "fossil chat") ** the response is to bring up a web-browser window to the chatroom ** on the default system web-browser. You can accomplish the same by ** typing the appropriate URL into the web-browser yourself. This ** command is merely a convenience for command-line oriented people. ** ** > fossil chat pull ** ** Copy chat content from the server down into the local clone, ** as a backup or archive. Setup privilege is required on the server. ** ** --all Download all chat content. Normally only ** previously undownloaded content is retrieved. ** --debug Additional debugging output ** --out DATABASE Store CHAT table in separate database file ** DATABASE rather that adding to local clone ** --unsafe Allow the use of unencrypted http:// ** ** > fossil chat send [ARGUMENTS] ** ** This command sends a new message to the chatroom. The message ** to be sent is determined by arguments as follows: ** ** -f|--file FILENAME File to attach to the message ** --as FILENAME2 Causes --file FILENAME to be sent with ** the attachment name FILENAME2 ** -m|--message TEXT Text of the chat message ** --remote URL Send to this remote URL ** --unsafe Allow the use of unencrypted http:// ** ** > fossil chat url ** ** Show the default URL used to access the chat server. ** ** Additional subcommands may be added in the future. */ void chat_command(void){ const char *zUrl = find_option("remote",0,1); int urlFlags = 0; int isDefaultUrl = 0; int i; db_find_and_open_repository(0,0); if( zUrl ){ urlFlags = URL_PROMPT_PW; }else{ zUrl = db_get("last-sync-url",0); if( zUrl==0 ){ fossil_fatal("no \"remote\" repository defined"); }else{ isDefaultUrl = 1; } } url_parse(zUrl, urlFlags); if( g.url.isFile || g.url.isSsh ){ fossil_fatal("chat only works for http:// and https:// URLs"); } i = (int)strlen(g.url.path); while( i>0 && g.url.path[i-1]=='/' ) i--; if( g.url.port==g.url.dfltPort ){ zUrl = mprintf( "%s://%T%.*T", g.url.protocol, g.url.name, i, g.url.path ); }else{ zUrl = mprintf( "%s://%T:%d%.*T", g.url.protocol, g.url.name, g.url.port, i, g.url.path ); } if( g.argc==2 ){ const char *zBrowser = fossil_web_browser(); char *zCmd; verify_all_options(); if( zBrowser==0 ) return; #ifdef _WIN32 zCmd = mprintf("%s %s/chat?cli &", zBrowser, zUrl); #else zCmd = mprintf("%s \"%s/chat?cli\" &", zBrowser, zUrl); #endif fossil_system(zCmd); }else if( strcmp(g.argv[2],"send")==0 ){ const char *zFilename = find_option("file","r",1); const char *zAs = find_option("as",0,1); const char *zMsg = find_option("message","m",1); int allowUnsafe = find_option("unsafe",0,0)!=0; const int mFlags = HTTP_GENERIC | HTTP_QUIET | HTTP_NOCOMPRESS; int i; const char *zPw; char *zLMTime; Blob up, down, fcontent; char zBoundary[80]; sqlite3_uint64 r[3]; if( zFilename==0 && zMsg==0 ){ fossil_fatal("must have --message or --file or both"); } if( !g.url.isHttps && !allowUnsafe ){ fossil_fatal("URL \"%s\" is unencrypted. Use https:// instead", zUrl); } verify_all_options(); if( g.argc>3 ){ fossil_fatal("unknown extra argument: \"%s\"", g.argv[3]); } i = (int)strlen(g.url.path); while( i>0 && g.url.path[i-1]=='/' ) i--; g.url.path = mprintf("%.*s/chat-send", i, g.url.path); blob_init(&up, 0, 0); blob_init(&down, 0, 0); sqlite3_randomness(sizeof(r),r); sqlite3_snprintf(sizeof(zBoundary),zBoundary, "--------%016llu%016llu%016llu", r[0], r[1], r[2]); blob_appendf(&up, "%s", zBoundary); zLMTime = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S','now','localtime')"); if( zLMTime ){ blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"lmtime\"\r\n" "\r\n%z\r\n%s", zLMTime, zBoundary); } if( g.url.user && g.url.user[0] ){ blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"resid\"\r\n" "\r\n%z\r\n%s", obscure(g.url.user), zBoundary); } zPw = g.url.passwd; if( zPw==0 && isDefaultUrl ) zPw = unobscure(db_get("last-sync-pw", 0)); if( zPw && zPw[0] ){ blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"token\"\r\n" "\r\n%z\r\n%s", obscure(zPw), zBoundary); } if( zMsg && zMsg[0] ){ blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"msg\"\r\n" "\r\n%s\r\n%s", zMsg, zBoundary); } if( zFilename && blob_read_from_file(&fcontent, zFilename, ExtFILE)>0 ){ char *zFN = mprintf("%s", file_tail(zAs ? zAs : zFilename)); int i; const char *zMime = mimetype_from_name(zFN); for(i=0; zFN[i]; i++){ char c = zFN[i]; if( fossil_isalnum(c) ) continue; if( c=='.' ) continue; if( c=='-' ) continue; zFN[i] = '_'; } blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"file\";" " filename=\"%s\"\r\n", zFN); blob_appendf(&up,"Content-Type: %s\r\n\r\n", zMime); blob_append(&up, fcontent.aData, fcontent.nUsed); blob_appendf(&up,"\r\n%s", zBoundary); } blob_append(&up,"--\r\n", 4); http_exchange(&up, &down, mFlags, 4, "multipart/form-data"); blob_reset(&up); if( sqlite3_strglob("{\"isError\": true,*", blob_str(&down))==0 ){ if( strstr(blob_str(&down), "not logged in")!=0 ){ fossil_print("ERROR: username and/or password is incorrect\n"); }else{ fossil_print("ERROR: %s\n", blob_str(&down)); } fossil_fatal("unable to send the chat message"); } blob_reset(&down); }else if( strcmp(g.argv[2],"pull")==0 ){ /* Pull the CHAT table from the default server down into the repository ** here on the local side */ int allowUnsafe = find_option("unsafe",0,0)!=0; int bDebug = find_option("debug",0,0)!=0; const char *zOut = find_option("out",0,1); int bAll = find_option("all",0,0)!=0; int mFlags = HTTP_GENERIC | HTTP_QUIET | HTTP_NOCOMPRESS; int msgid; Blob reqUri; /* The REQUEST_URI: .../chat-backup?msgid=... */ char *zObs; const char *zPw; Blob up, down; int nChat; int rc; verify_all_options(); chat_create_tables(); msgid = bAll ? 0 : db_int(0,"SELECT max(msgid) FROM chat"); if( !g.url.isHttps && !allowUnsafe ){ fossil_fatal("URL \"%s\" is unencrypted. Use https:// instead", zUrl); } blob_init(&reqUri, g.url.path, -1); blob_appendf(&reqUri, "/chat-backup?msgid=%d", msgid); if( g.url.user && g.url.user[0] ){ zObs = obscure(g.url.user); blob_appendf(&reqUri, "&resid=%t", zObs); fossil_free(zObs); } zPw = g.url.passwd; if( zPw==0 && isDefaultUrl ){ zPw = unobscure(db_get("last-sync-pw", 0)); if( zPw==0 ){ /* Can happen if "remember password" is not used. */ g.url.flags |= URL_PROMPT_PW; url_prompt_for_password(); zPw = g.url.passwd; } } if( zPw && zPw[0] ){ zObs = obscure(zPw); blob_appendf(&reqUri, "&token=%t", zObs); fossil_free(zObs); } g.url.path = blob_str(&reqUri); if( bDebug ){ fossil_print("REQUEST_URI: %s\n", g.url.path); mFlags &= ~HTTP_QUIET; mFlags |= HTTP_VERBOSE; } blob_init(&up, 0, 0); blob_init(&down, 0, 0); http_exchange(&up, &down, mFlags, 4, 0); if( zOut ){ blob_write_to_file(&down, zOut); fossil_print("Chat database at %s is %d bytes\n", zOut, blob_size(&down)); }else{ db_multi_exec("ATTACH ':memory:' AS chatbu;"); if( g.fSqlTrace ){ fossil_trace("-- deserialize(\"chatbu\", pData, %d);\n", blob_size(&down)); } rc = sqlite3_deserialize(g.db, "chatbu", (unsigned char*)blob_buffer(&down), blob_size(&down), blob_size(&down), 0); if( rc ){ fossil_fatal("cannot open patch database: %s", sqlite3_errmsg(g.db)); } nChat = db_int(0, "SELECT count(*) FROM chatbu.chat"); fossil_print("Got %d new records, %d bytes\n", nChat, blob_size(&down)); db_multi_exec( "REPLACE INTO repository.chat(msgid,mtime,lmtime,xfrom,xmsg," "fname,fmime,mdel,file)" " SELECT msgid,mtime,lmtime,xfrom,xmsg,fname,fmime,mdel,file" " FROM chatbu.chat;" ); } }else if( strcmp(g.argv[2],"url")==0 ){ /* Show the URL to access chat. */ fossil_print("%s/chat\n", zUrl); }else{ fossil_fatal("no such subcommand \"%s\". Use --help for help", g.argv[2]); } } |
Changes to src/checkin.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2007 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".) | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < > | < < > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | | | > | > | > > | > | | > | > | | > | > | | > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | > | | > > > > > | | > | > > > > > > > > > > > > > > > > > > > | > > | > > > | | > | > > > > > > > > > | | > > > | > | | | | < < < < | < < > | | | < > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > > > > > > > | > > > > | > > > > > > > > > | > > > > > > > > > > > > | < > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > | | > | > > > > > > > > > > | > | < | | > > | | | | | | | < > > | > > > > > > > > | > | | | | > > > > > > < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | > > > > > > > > > > > > > > > > > > > | < | < < | > > > > | > > > | > | > > > | < | < < < < | | | > | > > > | | < | > | | | > > > > > > | > > | < > > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | | > > > | | > > > > > > > > > > | > > | > > > > > > | > > > > > > > > > > > > > > > > > > > > | > > | < > > | | | < | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | < > > > > > > > > > > > > > > | < > | < > > > > > > > > > > | > > > > > > > > > > > > | 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 | /* ** Copyright (c) 2007 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 check-in versions of the project ** from the local repository. */ #include "config.h" #include "checkin.h" #include <assert.h> /* ** Change filter options. */ enum { /* Zero-based bit indexes. */ CB_EDITED , CB_UPDATED , CB_CHANGED, CB_MISSING , CB_ADDED, CB_DELETED, CB_RENAMED, CB_CONFLICT, CB_META , CB_UNCHANGED, CB_EXTRA, CB_MERGE , CB_RELPATH, CB_CLASSIFY, CB_MTIME , CB_SIZE , CB_FATAL, CB_COMMENT, /* Bitmask values. */ C_EDITED = 1 << CB_EDITED, /* Edited, merged, and conflicted files. */ C_UPDATED = 1 << CB_UPDATED, /* Files updated by merge/integrate. */ C_CHANGED = 1 << CB_CHANGED, /* Treated the same as the above two. */ C_MISSING = 1 << CB_MISSING, /* Missing and non- files. */ C_ADDED = 1 << CB_ADDED, /* Added files. */ C_DELETED = 1 << CB_DELETED, /* Deleted files. */ C_RENAMED = 1 << CB_RENAMED, /* Renamed files. */ C_CONFLICT = 1 << CB_CONFLICT, /* Files having merge conflicts. */ C_META = 1 << CB_META, /* Files with metadata changes. */ C_UNCHANGED = 1 << CB_UNCHANGED, /* Unchanged files. */ C_EXTRA = 1 << CB_EXTRA, /* Unmanaged files. */ C_MERGE = 1 << CB_MERGE, /* Merge contributors. */ C_FILTER = C_EDITED | C_UPDATED | C_CHANGED | C_MISSING | C_ADDED | C_DELETED | C_RENAMED | C_CONFLICT | C_META | C_UNCHANGED | C_EXTRA | C_MERGE, /* All filter bits. */ C_ALL = C_FILTER & ~(C_EXTRA | C_MERGE),/* All managed files. */ C_DIFFER = C_FILTER & ~(C_UNCHANGED | C_MERGE),/* All differences. */ C_RELPATH = 1 << CB_RELPATH, /* Show relative paths. */ C_CLASSIFY = 1 << CB_CLASSIFY, /* Show file change types. */ C_DEFAULT = (C_ALL & ~C_UNCHANGED) | C_MERGE | C_CLASSIFY, C_MTIME = 1 << CB_MTIME, /* Show file modification time. */ C_SIZE = 1 << CB_SIZE, /* Show file size in bytes. */ C_FATAL = 1 << CB_FATAL, /* Fail on MISSING/NOT_A_FILE. */ C_COMMENT = 1 << CB_COMMENT, /* Precede each line with "# ". */ }; /* ** Create a TEMP table named SFILE and add all unmanaged files named on ** the command-line to that table. If directories are named, then add ** all unmanaged files contained underneath those directories. If there ** are no files or directories named on the command-line, then add all ** unmanaged files anywhere in the check-out. ** ** This routine never follows symlinks. It always treats symlinks as ** object unto themselves. */ static void locate_unmanaged_files( int argc, /* Number of command-line arguments to examine */ char **argv, /* values of command-line arguments */ unsigned scanFlags, /* Zero or more SCAN_xxx flags */ Glob *pIgnore /* Do not add files that match this GLOB */ ){ Blob name; /* Name of a candidate file or directory */ char *zName; /* Name of a candidate file or directory */ int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */ int i; /* Loop counter */ int nRoot; /* length of g.zLocalRoot */ db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s," " mtime INTEGER, size INTEGER)", filename_collation()); nRoot = (int)strlen(g.zLocalRoot); if( argc==0 ){ blob_init(&name, g.zLocalRoot, nRoot - 1); vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0, SymFILE); blob_reset(&name); }else{ for(i=0; i<argc; i++){ file_canonical_name(argv[i], &name, 0); zName = blob_str(&name); isDir = file_isdir(zName, SymFILE); if( isDir==1 ){ vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0, SymFILE); }else if( isDir==0 ){ fossil_warning("not found: %s", &zName[nRoot]); }else if( file_access(zName, R_OK) ){ fossil_fatal("cannot open %s", &zName[nRoot]); }else{ /* Only add unmanaged file paths specified on the command line. */ db_multi_exec( "INSERT OR IGNORE INTO sfile(pathname)" " SELECT %Q WHERE NOT EXISTS" " (SELECT 1 FROM vfile WHERE pathname=%Q)", &zName[nRoot], &zName[nRoot] ); } blob_reset(&name); } } } /* ** Generate text describing all changes. ** ** We assume that vfile_check_signature has been run. */ static void status_report( Blob *report, /* Append the status report here */ unsigned flags /* Filter and other configuration flags */ ){ Stmt q; int nErr = 0; Blob rewrittenOrigName, rewrittenPathname; Blob sql = BLOB_INITIALIZER, where = BLOB_INITIALIZER; const char *zName; int i; /* Skip the file report if no files are requested at all. */ if( !(flags & (C_ALL | C_EXTRA)) ){ goto skipFiles; } /* Assemble the path-limiting WHERE clause, if any. */ blob_zero(&where); for(i=2; i<g.argc; i++){ Blob fname; file_tree_name(g.argv[i], &fname, 0, 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() ); } /* Obtain the list of managed files if appropriate. */ blob_zero(&sql); if( flags & C_ALL ){ /* Start with a list of all managed files. */ blob_append_sql(&sql, "SELECT pathname, %s as mtime, %s as size, deleted, chnged, rid," " coalesce(origname!=pathname,0) AS renamed, 1 AS managed," " origname" " FROM vfile LEFT JOIN blob USING (rid)" " WHERE is_selected(id)%s", flags & C_MTIME ? "datetime(checkin_mtime(:vid, rid), " "'unixepoch', toLocal())" : "''" /*safe-for-%s*/, flags & C_SIZE ? "coalesce(blob.size, 0)" : "0" /*safe-for-%s*/, blob_sql_text(&where)); /* Exclude unchanged files unless requested. */ if( !(flags & C_UNCHANGED) ){ blob_append_sql(&sql, " AND (chnged OR deleted OR rid=0 OR pathname!=origname)"); } } /* If C_EXTRA, add unmanaged files to the query result too. */ if( flags & C_EXTRA ){ if( blob_size(&sql) ){ blob_append_sql(&sql, " UNION ALL"); } blob_append_sql(&sql, " SELECT pathname, %s, %s, 0, 0, 0, 0, 0, NULL" " FROM sfile WHERE pathname NOT IN (%s)%s", flags & C_MTIME ? "datetime(mtime, 'unixepoch', toLocal())" : "''", flags & C_SIZE ? "size" : "0", fossil_all_reserved_names(0), blob_sql_text(&where)); } blob_reset(&where); /* Pre-create the "ok" temporary table so the checkin_mtime() SQL function * does not lead to SQLITE_ABORT_ROLLBACK during execution of the OP_OpenRead * SQLite opcode. checkin_mtime() calls mtime_of_manifest_file() which * creates a temporary table if it doesn't already exist, thus invalidating * the prepared statement in the middle of its execution. */ db_multi_exec("CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"); /* Append an ORDER BY clause then compile the query. */ blob_append_sql(&sql, " ORDER BY pathname"); db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); /* Bind the check-out version ID to the query if needed. */ if( (flags & C_ALL) && (flags & C_MTIME) ){ db_bind_int(&q, ":vid", db_lget_int("checkout", 0)); } /* Execute the query and assemble the report. */ blob_zero(&rewrittenPathname); blob_zero(&rewrittenOrigName); while( db_step(&q)==SQLITE_ROW ){ const char *zPathname = db_column_text(&q, 0); const char *zClass = 0; int isManaged = db_column_int(&q, 7); const char *zMtime = db_column_text(&q, 1); int size = db_column_int(&q, 2); int isDeleted = db_column_int(&q, 3); int isChnged = db_column_int(&q, 4); int isNew = isManaged && !db_column_int(&q, 5); int isRenamed = db_column_int(&q, 6); const char *zOrigName = 0; char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); int isMissing = !file_isfile_or_link(zFullName); /* Determine the file change classification, if any. */ if( isDeleted ){ if( flags & C_DELETED ){ zClass = "DELETED"; } }else if( isMissing ){ if( file_access(zFullName, F_OK)==0 ){ if( flags & C_MISSING ){ zClass = "NOT_A_FILE"; } if( flags & C_FATAL ){ fossil_warning("not a file: %s", zFullName); nErr++; } }else{ if( flags & C_MISSING ){ zClass = "MISSING"; } if( flags & C_FATAL ){ fossil_warning("missing file: %s", zFullName); nErr++; } } }else if( isNew ){ if( flags & C_ADDED ){ zClass = "ADDED"; } }else if( (flags & (C_UPDATED | C_CHANGED)) && isChnged==2 ){ zClass = "UPDATED_BY_MERGE"; }else if( (flags & C_ADDED) && isChnged==3 ){ zClass = "ADDED_BY_MERGE"; }else if( (flags & (C_UPDATED | C_CHANGED)) && isChnged==4 ){ zClass = "UPDATED_BY_INTEGRATE"; }else if( (flags & C_ADDED) && isChnged==5 ){ zClass = "ADDED_BY_INTEGRATE"; }else if( (flags & C_META) && isChnged==6 ){ zClass = "EXECUTABLE"; }else if( (flags & C_META) && isChnged==7 ){ zClass = "SYMLINK"; }else if( (flags & C_META) && isChnged==8 ){ zClass = "UNEXEC"; }else if( (flags & C_META) && isChnged==9 ){ zClass = "UNLINK"; }else if( (flags & C_CONFLICT) && isChnged && !file_islink(zFullName) && file_contains_merge_marker(zFullName) ){ zClass = "CONFLICT"; }else if( (flags & (C_EDITED | C_CHANGED)) && isChnged && (isChnged<2 || isChnged>9) ){ zClass = "EDITED"; }else if( (flags & C_UNCHANGED) && isManaged && !isNew && !isChnged && !isRenamed ){ zClass = "UNCHANGED"; }else if( (flags & C_EXTRA) && !isManaged ){ zClass = "EXTRA"; } if( (flags & C_RENAMED) && isRenamed ){ zOrigName = db_column_text(&q,8); if( zClass==0 ){ zClass = "RENAMED"; } } /* Only report files for which a change classification was determined. */ if( zClass ){ if( flags & C_COMMENT ){ blob_append(report, "# ", 2); } if( flags & C_CLASSIFY ){ blob_appendf(report, "%-10s ", zClass); } if( flags & C_MTIME ){ blob_append(report, zMtime, -1); blob_append(report, " ", 2); } if( flags & C_SIZE ){ blob_appendf(report, "%7d ", size); } if( flags & C_RELPATH ){ /* If C_RELPATH, display paths relative to current directory. */ file_relative_name(zFullName, &rewrittenPathname, 0); zPathname = blob_str(&rewrittenPathname); if( zPathname[0]=='.' && zPathname[1]=='/' ){ zPathname += 2; /* no unnecessary ./ prefix */ } if( (flags & (C_FILTER ^ C_RENAMED)) && zOrigName ){ char *zOrigFullName = mprintf("%s%s", g.zLocalRoot, zOrigName); file_relative_name(zOrigFullName, &rewrittenOrigName, 0); zOrigName = blob_str(&rewrittenOrigName); fossil_free(zOrigFullName); if( zOrigName[0]=='.' && zOrigName[1]=='/' ){ zOrigName += 2; /* no unnecessary ./ prefix */ } } } if( (flags & (C_FILTER ^ C_RENAMED)) && zOrigName ){ blob_appendf(report, "%s -> ", zOrigName); } blob_appendf(report, "%s\n", zPathname); } free(zFullName); } blob_reset(&rewrittenPathname); blob_reset(&rewrittenOrigName); db_finalize(&q); /* If C_MERGE, put merge contributors at the end of the report. */ skipFiles: if( flags & C_MERGE ){ db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0" ); while( db_step(&q)==SQLITE_ROW ){ if( flags & C_COMMENT ){ blob_append(report, "# ", 2); } if( flags & C_CLASSIFY ){ const char *zClass; switch( db_column_int(&q, 1) ){ case -1: zClass = "CHERRYPICK" ; break; case -2: zClass = "BACKOUT" ; break; case -4: zClass = "INTEGRATE" ; break; default: zClass = "MERGED_WITH"; break; } blob_appendf(report, "%-10s ", zClass); } blob_append(report, db_column_text(&q, 0), -1); blob_append(report, "\n", 1); } db_finalize(&q); } if( nErr ){ fossil_fatal("aborting due to prior errors"); } } /* ** Use the "relative-paths" setting and the --abs-paths and ** --rel-paths command line options to determine whether the ** status report should be shown relative to the current ** working directory. */ static int determine_cwd_relative_option() { 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 ** COMMAND: status ** ** Usage: %fossil changes|status ?OPTIONS? ?PATHS ...? ** ** Report the change status of files in the current check-out. If one or ** more PATHS are specified, only changes among the named files and ** directories are reported. Directories are searched recursively. ** ** The status command is similar to the changes command, except it lacks ** several of the options supported by changes and it has its own header ** and footer information. The header information is a subset of that ** shown by the info command, and the footer shows if there are any forks. ** Change type classification is always enabled for the status command. ** ** Each line of output is the name of a changed file, with paths shown ** according to the "relative-paths" setting, unless overridden by the ** --abs-paths or --rel-paths options. ** ** By default, all changed files are selected for display. This behavior ** can be overridden by using one or more filter options (listed below), ** in which case only files with the specified change type(s) are shown. ** As a special case, the --no-merge option does not inhibit this default. ** This default shows exactly the set of changes that would be checked- ** in by the commit command. ** ** If no filter options are used, or if the --merge option is used, the ** artifact hash of each merge contributor check-in version is displayed at ** the end of the report. The --no-merge option is useful to display the ** default set of changed files without the merge contributors. ** ** If change type classification is enabled, each output line starts with ** a code describing the file's change type, e.g. EDITED or RENAMED. It ** is enabled by default unless exactly one change type is selected. For ** the purposes of determining the default, --changed counts as selecting ** one change type. The default can be overridden by the --classify or ** --no-classify options. ** ** --edited and --updated produce disjoint sets. --updated shows a file ** only when it is identical to that of its merge contributor, and the ** change type classification is UPDATED_BY_MERGE or UPDATED_BY_INTEGRATE. ** If the file had to be merged with any other changes, it is considered ** to be merged or conflicted and therefore will be shown by --edited, not ** --updated, with types EDITED or CONFLICT. The --changed option can be ** used to display the union of --edited and --updated. ** ** --differ is so named because it lists all the differences between the ** checked-out version and the check-out directory. In addition to the ** default changes (excluding --merge), it lists extra files which (if ** ignore-glob is set correctly) may be worth adding. Prior to doing a ** commit, it is good practice to check --differ to see not only which ** changes would be committed but also if any files should be added. ** ** If both --merge and --no-merge are used, --no-merge has priority. The ** same is true of --classify and --no-classify. ** ** The "fossil changes --extra" command is equivalent to "fossil extras". ** ** General options: ** --abs-paths Display absolute pathnames ** --rel-paths Display pathnames relative to the current working ** directory ** --hash Verify file status using hashing rather than ** relying on file mtimes ** --case-sensitive BOOL Override case-sensitive setting ** --dotfiles Include unmanaged files beginning with a dot ** --ignore <CSG> Ignore unmanaged files matching CSG glob patterns ** ** Options specific to the changes command: ** --header Identify the repository if report is non-empty ** -v|--verbose Say "(none)" if the change report is empty ** --classify Start each line with the file's change type ** --no-classify Do not print file change types ** ** Filter options: ** --edited Display edited, merged, and conflicted files ** --updated Display files updated by merge/integrate ** --changed Combination of the above two options ** --missing Display missing files ** --added Display added files ** --deleted Display deleted files ** --renamed Display renamed files ** --conflict Display files having merge conflicts ** --meta Display files with metadata changes ** --unchanged Display unchanged files ** --all Display all managed files, i.e. all of the above ** --extra Display unmanaged files ** --differ Display modified and extra files ** --merge Display merge contributors ** --no-merge Do not display merge contributors ** ** See also: [[extras]], [[ls]] */ void status_cmd(void){ /* Affirmative and negative flag option tables. */ static const struct { const char *option; /* Flag name. */ unsigned mask; /* Flag bits. */ } flagDefs[] = { {"edited" , C_EDITED }, {"updated" , C_UPDATED }, {"changed" , C_CHANGED }, {"missing" , C_MISSING }, {"added" , C_ADDED }, {"deleted" , C_DELETED }, {"renamed" , C_RENAMED }, {"conflict" , C_CONFLICT }, {"meta" , C_META }, {"unchanged" , C_UNCHANGED}, {"all" , C_ALL }, {"extra" , C_EXTRA }, {"differ" , C_DIFFER }, {"merge" , C_MERGE }, {"classify", C_CLASSIFY}, }, noFlagDefs[] = { {"no-merge", C_MERGE }, {"no-classify", C_CLASSIFY }, }; Blob report = BLOB_INITIALIZER; enum {CHANGES, STATUS} command = *g.argv[1]=='s' ? STATUS : CHANGES; /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ int useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; int showHdr = command==CHANGES && find_option("header", 0, 0); int verboseFlag = command==CHANGES && find_option("verbose", "v", 0); const char *zIgnoreFlag = find_option("ignore", 0, 1); unsigned scanFlags = 0; unsigned flags = 0; int vid, i; fossil_pledge("stdio rpath wpath cpath fattr id flock tty chown"); /* Load affirmative flag options. */ for( i=0; i<count(flagDefs); ++i ){ if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY)) && find_option(flagDefs[i].option, 0, 0) ){ flags |= flagDefs[i].mask; } } /* If no filter options are specified, enable defaults. */ if( !(flags & C_FILTER) ){ flags |= C_DEFAULT; } /* If more than one filter is enabled, enable classification. This is tricky. * Having one filter means flags masked by C_FILTER is a power of two. If a * number masked by one less than itself is zero, it's either zero or a power * of two. It's already known to not be zero because of the above defaults. * Unlike --all, --changed is a single filter, i.e. it sets only one bit. * Also force classification for the status command. */ if( command==STATUS || (flags & (flags-1) & C_FILTER) ){ flags |= C_CLASSIFY; } /* Negative flag options override defaults applied above. */ for( i=0; i<count(noFlagDefs); ++i ){ if( (command==CHANGES || !(noFlagDefs[i].mask & C_CLASSIFY)) && find_option(noFlagDefs[i].option, 0, 0) ){ flags &= ~noFlagDefs[i].mask; } } /* Confirm current working directory is within check-out. */ db_must_be_within_tree(); /* Get check-out version. l*/ vid = db_lget_int("checkout", 0); /* Relative path flag determination is done by a shared function. */ if( determine_cwd_relative_option() ){ flags |= C_RELPATH; } /* If --ignore is not specified, use the ignore-glob setting. */ if( !zIgnoreFlag ){ zIgnoreFlag = db_get("ignore-glob", 0); } /* Get the --dotfiles argument, or read it from the dotfiles setting. */ if( find_option("dotfiles", 0, 0) || db_get_boolean("dotfiles", 0) ){ scanFlags = SCAN_ALL; } /* We should be done with options. */ verify_all_options(); /* Check for changed files. */ vfile_check_signature(vid, useHash ? CKSIG_HASH : 0); /* Search for unmanaged files if requested. */ if( flags & C_EXTRA ){ Glob *pIgnore = glob_create(zIgnoreFlag); locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); glob_free(pIgnore); } /* The status command prints general information before the change list. */ if( command==STATUS ){ 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); } if( vid ){ show_common_info(vid, "checkout:", 1, 1); } db_record_repository_filename(0); } /* Find and print all requested changes. */ blob_zero(&report); status_report(&report, flags); if( blob_size(&report) ){ if( showHdr ){ fossil_print( "Changes for %s at %s:\n", db_get("project-name", "<unnamed>"), g.zLocalRoot); } blob_write_to_file(&report, "-"); }else if( verboseFlag ){ fossil_print(" (none)\n"); } blob_reset(&report); /* The status command ends with warnings about ambiguous leaves (forks). */ if( command==STATUS ){ leaf_ambiguity_warning(vid, vid); } } /* ** 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, 0, 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, toLocal()), 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? ?PATHS ...? ** ** List all files in the current check-out. If PATHS is included, only the ** named files (or their children if directories) are shown. ** ** The ls command is essentially two related commands in one, depending on ** whether or not the -r option is given. -r selects a specific check-in ** version to list, in which case -R can be used to select the repository. ** The fine behavior of the --age, -v, and -t options is altered by the -r ** option as well, as explained below. ** ** The --age option displays file commit times. Like -r, --age has the ** side effect of making -t sort by commit time, not modification time. ** ** The -v option provides extra information about each file. Without -r, ** -v displays the change status, in the manner of the changes command. ** With -r, -v shows the commit time and size of the checked-in files. ** ** The -t option changes the sort order. Without -t, files are sorted by ** path and name (case insensitive sort if -r). If neither --age nor -r ** are used, -t sorts by modification time, otherwise by commit time. ** ** 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 REPO Extract info from repository REPO ** --hash With -v, verify file status using hashing ** rather than relying on file sizes and mtimes ** ** 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; int useHash = 0; 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( verboseFlag ){ useHash = find_option("hash",0,0)!=0; } if( zRev!=0 ){ db_find_and_open_repository(0, 0); verify_all_options(); ls_cmd_rev(zRev,verboseFlag,showAge,timeOrder); return; }else if( find_option("R",0,1)!=0 ){ fossil_fatal("the -r is required in addition to -R"); } 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, 0, 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, useHash ? CKSIG_HASH : 0); if( showAge ){ db_prepare(&q, "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)," " datetime(checkin_mtime(%d,rid),'unixepoch',toLocal())" " FROM vfile %s" " ORDER BY %s", vid, blob_sql_text(&where), zOrderBy /*safe-for-%s*/ ); }else{ db_prepare(&q, "SELECT pathname, deleted, rid, chnged," " coalesce(origname!=pathname,0), islink" " 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); int isLink = db_column_int(&q,5); 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_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 "; }else if( chnged==3 ){ type = "ADDED_BY_MERGE "; }else if( chnged==4 ){ type = "UPDATED_BY_INTEGRATE "; }else if( chnged==5 ){ type = "ADDED_BY_INTEGRATE "; }else if( !isLink && file_contains_merge_marker(zFullName) ){ type = "CONFLICT "; }else{ type = "EDITED "; } }else if( renamed ){ type = "RENAMED "; }else{ type = "UNCHANGED "; } } if( showAge ){ fossil_print("%s%s %s\n", type, db_column_text(&q, 5), zPathname); }else{ fossil_print("%s%s\n", type, zPathname); } free(zFullName); } db_finalize(&q); } /* ** COMMAND: extras ** ** Usage: %fossil extras ?OPTIONS? ?PATH1 ...? ** ** Print a list of all files in the source tree that are not part of the ** current check-out. See also the "clean" command. If paths are specified, ** only files in the given directories will be listed. ** ** Files and subdirectories whose names begin with "." are normally ** ignored but can be included by adding the --dotfiles option. ** ** Files whose names match any of the glob patterns in the "ignore-glob" ** setting are ignored. This setting can be overridden by the --ignore ** option, whose CSG argument is a comma-separated list of glob patterns. ** ** 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){ Blob report = BLOB_INITIALIZER; const char *zIgnoreFlag = find_option("ignore",0,1); unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; unsigned flags = C_EXTRA; int showHdr = find_option("header",0,0)!=0; Glob *pIgnore; if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; db_must_be_within_tree(); if( determine_cwd_relative_option() ){ flags |= C_RELPATH; } 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); glob_free(pIgnore); blob_zero(&report); status_report(&report, flags); if( blob_size(&report) ){ if( showHdr ){ fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"), g.zLocalRoot); } blob_write_to_file(&report, "-"); } blob_reset(&report); } /* ** COMMAND: clean ** ** Usage: %fossil clean ?OPTIONS? ?PATH ...? ** ** Delete all "extra" files in the source tree. "Extra" files are files ** that are not officially part of the check-out. If one or more PATH ** arguments appear, then only the files named, or files contained with ** directories named, will be removed. ** ** If the --prompt option is used, prompts are issued to confirm the ** permanent removal of each file. Otherwise, files are backed up to the ** undo buffer prior to removal, and prompts are issued only for files ** whose removal cannot be undone due to their large size or due to ** --disable-undo being used. ** ** The --force option treats all prompts as having been answered yes, ** whereas --no-prompt treats them as having been answered no. ** ** Files matching any glob pattern specified by the --clean option are ** deleted without prompting, and the removal cannot be undone. ** ** No file that matches glob patterns specified by --ignore or --keep will ** ever be deleted. Files and subdirectories whose names begin with "." ** are automatically ignored unless the --dotfiles option is used. ** ** The default values for --clean, --ignore, and --keep are determined by ** the (versionable) clean-glob, ignore-glob, and keep-glob settings. ** ** The --verily option ignores the keep-glob and ignore-glob settings and ** turns on --force, --emptydirs, --dotfiles, and --disable-undo. Use the ** --verily option when you really want to clean up everything. Extreme ** care should be exercised when using the --verily option. ** ** Options: ** --allckouts Check for empty directories within any check-outs ** that may be nested within the current one. This ** option should be used with great care because the ** empty-dirs setting (and other applicable settings) ** belonging to the other repositories, if any, will ** not be checked. ** --case-sensitive BOOL Override case-sensitive setting ** --dirsonly Only remove empty directories. No files will ** be removed. Using this option will automatically ** enable the --emptydirs option as well. ** --disable-undo WARNING: This option disables use of the undo ** mechanism for this clean operation and should be ** used with extreme caution. ** --dotfiles Include files beginning with a dot (".") ** --emptydirs Remove any empty directories that are not ** 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 ** -i|--prompt Prompt before removing each file. This option ** implies the --disable-undo option. ** -x|--verily WARNING: Removes everything that is not a managed ** file or the repository itself. This option ** implies the --force, --emptydirs, --dotfiles, and ** --disable-undo options. Furthermore, it ** completely disregards the keep-glob ** and ignore-glob settings. However, it does honor ** the --ignore and --keep options. ** --clean CSG WARNING: Never prompt to delete any files matching ** this comma separated list of glob patterns. Also, ** deletions of any files matching this pattern list ** cannot be undone. ** --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 Delete nothing, but display what would have been ** deleted ** --no-prompt Do not prompt the user for input and assume an ** answer of 'No' for every question ** --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; int disableUndo, noPrompt; int alwaysPrompt = 0; unsigned scanFlags = 0; int verilyFlag = 0; const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; Glob *pIgnore, *pKeep, *pClean; int nRoot; #ifndef UNDO_SIZE_LIMIT /* TODO: Setting? */ #define UNDO_SIZE_LIMIT (10*1024*1024) /* 10MiB */ #endif undo_capture_command_line(); 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; } disableUndo = find_option("disable-undo",0,0)!=0; noPrompt = find_option("no-prompt",0,0)!=0; alwaysPrompt = find_option("prompt","i",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","x",0)!=0 ){ verilyFlag = allFileFlag = allDirFlag = 1; emptyDirsFlag = 1; disableUndo = 1; scanFlags |= SCAN_ALL; zCleanFlag = 0; } if( zIgnoreFlag==0 && !verilyFlag ){ zIgnoreFlag = db_get("ignore-glob", 0); } if( zKeepFlag==0 && !verilyFlag ){ zKeepFlag = db_get("keep-glob", 0); } if( zCleanFlag==0 && !verilyFlag ){ 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); if( !dirsOnlyFlag ){ Stmt q; Blob repo; if( !dryRunFlag && !disableUndo ) undo_begin(); locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); db_prepare(&q, "SELECT %Q || pathname FROM sfile" " WHERE pathname NOT IN (%s)" " ORDER BY 1", g.zLocalRoot, fossil_all_reserved_names(0) ); if( file_tree_name(g.zRepositoryName, &repo, 0, 0) ){ db_multi_exec("DELETE FROM sfile WHERE pathname=%B", &repo); } db_multi_exec("DELETE FROM sfile WHERE pathname 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( !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ char *zPrompt = 0; char cReply; Blob ans = empty_blob; int undoRc = UNDO_NONE; if( alwaysPrompt ){ zPrompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ", zName+nRoot); prompt_user(zPrompt, &ans); fossil_free(zPrompt); cReply = fossil_toupper(blob_str(&ans)[0]); blob_reset(&ans); if( cReply=='N' ) continue; if( cReply=='A' ){ allFileFlag = 1; alwaysPrompt = 0; }else{ undoRc = UNDO_SAVED_OK; } }else if( !disableUndo ){ undoRc = undo_maybe_save(zName+nRoot, UNDO_SIZE_LIMIT); } if( undoRc!=UNDO_SAVED_OK ){ if( allFileFlag ){ cReply = 'Y'; }else if( !noPrompt ){ Blob ans; zPrompt = mprintf("\nWARNING: Deletion of this file will " "not be undoable via the 'undo'\n" " command because %s.\n\n" "Remove unmanaged file \"%s\" (a=all/y/N)? ", undo_save_message(undoRc), zName+nRoot); prompt_user(zPrompt, &ans); fossil_free(zPrompt); cReply = blob_str(&ans)[0]; blob_reset(&ans); }else{ cReply = 'N'; } if( cReply=='a' || cReply=='A' ){ allFileFlag = 1; }else if( cReply!='y' && cReply!='Y' ){ continue; } } } if( dryRunFlag || file_delete(zName)==0 ){ if( verboseFlag || dryRunFlag ){ fossil_print("Removed unmanaged file: %s\n", zName+nRoot); } }else{ fossil_print("Could not remove file: %s\n", zName+nRoot); } } db_finalize(&q); if( !dryRunFlag && !disableUndo ) undo_finish(); } 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, RepoFILE); 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) ){ char cReply; if( !noPrompt ){ Blob ans; char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ", zName+nRoot); prompt_user(prompt, &ans); cReply = blob_str(&ans)[0]; fossil_free(prompt); blob_reset(&ans); }else{ cReply = 'N'; } if( cReply=='a' || cReply=='A' ){ allDirFlag = 1; }else if( cReply!='y' && cReply!='Y' ){ continue; } } if( dryRunFlag || file_rmdir(zName)==0 ){ if( verboseFlag || dryRunFlag ){ fossil_print("Removed unmanaged directory: %s\n", zName+nRoot); } }else if( verboseFlag ){ fossil_print("Could not remove directory: %s\n", zName+nRoot); } } db_finalize(&q); glob_free(pEmptyDirs); } glob_free(pClean); glob_free(pKeep); glob_free(pIgnore); } /* ** Prompt the user for a check-in or stash comment (given in pPrompt), ** gather the response, then return the response in pComment. ** ** Lines of the prompt that begin with # are discarded. Excess whitespace ** is removed from the reply. ** ** Appropriate encoding translations are made on windows. */ void prompt_for_user_comment(Blob *pComment, Blob *pPrompt){ const char *zEditor; char *zCmd; char *zFile; Blob reply, line; char *zComment; int i; zEditor = fossil_text_editor(); if( zEditor==0 ){ if( blob_size(pPrompt)>0 ){ blob_append(pPrompt, "#\n" "# Since no default text editor is set using EDITOR or VISUAL\n" "# environment variables or the \"fossil set editor\" command,\n" "# and because no comment was specified using the \"-m\" or \"-M\"\n" "# command-line options, you will need to enter the comment below.\n" "# Type \".\" on a line by itself when you are done:\n", -1); } zFile = mprintf("-"); }else{ Blob fname; blob_zero(&fname); if( g.zLocalRoot!=0 ){ file_relative_name(g.zLocalRoot, &fname, 1); zFile = db_text(0, "SELECT '%qci-comment-'||hex(randomblob(6))||'.txt'", blob_str(&fname)); }else{ file_tempname(&fname, "ci-comment",0); zFile = mprintf("%s", blob_str(&fname)); } blob_reset(&fname); } #if defined(_WIN32) blob_add_cr(pPrompt); #endif if( blob_size(pPrompt)>0 ) blob_write_to_file(pPrompt, zFile); if( zEditor ){ char *z, *zEnd; zCmd = mprintf("%s %$", zEditor, zFile); fossil_print("%s\n", zCmd); if( fossil_system(zCmd) ){ fossil_fatal("editor aborted: \"%s\"", zCmd); } blob_read_from_file(&reply, zFile, ExtFILE); z = blob_str(&reply); zEnd = strstr(z, "##########"); if( zEnd ){ /* Truncate the reply at any sequence of 10 or more # characters. ** The diff for the -v option occurs after such a sequence. */ blob_resize(&reply, (int)(zEnd - z)); } }else{ char zIn[300]; blob_zero(&reply); while( fgets(zIn, sizeof(zIn), stdin)!=0 ){ if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){ break; } blob_append(&reply, zIn, -1); } } blob_to_utf8_no_bom(&reply, 1); blob_to_lf_only(&reply); file_delete(zFile); free(zFile); blob_zero(pComment); while( blob_line(&reply, &line) ){ int i, n; char *z; n = blob_size(&line); z = blob_buffer(&line); for(i=0; i<n && fossil_isspace(z[i]); i++){} if( i<n && z[i]=='#' ) continue; if( i<n || blob_size(pComment)>0 ){ blob_appendf(pComment, "%b", &line); } } blob_reset(&reply); zComment = blob_str(pComment); i = strlen(zComment); while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; } blob_resize(pComment, i); } /* ** Prepare a commit comment. Let the user modify it using the ** editor specified in the global_config table or either ** the VISUAL or EDITOR environment variable. ** |
︙ | ︙ | |||
357 358 359 360 361 362 363 | ** zBranch might be NULL or an empty string if no forcing occurs. ** ** parent_rid is the recordid of the parent check-in. */ static void prepare_commit_comment( Blob *pComment, char *zInit, | | | > < < < | < > | > > > | > > > > | > | < > > > > > > | | > > > > > > > > > > > > | | > > | > > | > | < | | > > > > > | < > > | > > | > > > > > > | > | < > > > > > > | | < | > > > > > > | < > > | | > | > > > > > | > | > > > > | < < > | > | | | | > > | | | | | < > > > > > > > > | | > > > > > > > > > > | | > > > > | > > > > > > > > | > | > > > | > | > > > > | > > | | < > | > > | | > > > > > | > > > > > > > > | | > < | > > > > > | > > | | > > | | | > > | | | < < < < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | < > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > | | | < < | | | | > > > | | > > > > > > | > > > > > > > > > > > | > > > > > | > > > > > | > > > | | > > > > > > > > > > > > > > > > > > > > > > > | | | > > > | > > > > > > > | | > > | | | < | > > > > > > > | > > > > > > | > > | < < < | > > > | < > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > | > | > > | < > > > > > | | < > > > > > > > > > > > | > > > | > > > > > > > | | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > < | | | | > | | < | | > | > | < | > | | > > > > > > > > > > > > > > | < > | | < > | > > | > | | > | | > | > > > > > > | | | > | | > > | > > > > > | | | | | | > | | | > > > > > > > > | > > > > > > > > > > | > > > > > > > < > | | | | | > | > > > > > | > | > | < > > > > > > > | | > > > > > > > > > > | | | | | > > > > > > > > | > > > > > > > > > > > > > > > | > | | | | | < | | < < < < < < < < < | < < < < < < < < > > > < < < < < < < < < < < < < < < | < > > | | > > > > | | | < | | < | > | > > | > > > > > | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < | | < < > > > > | > > | < < | < < < > > | | > > > > | < < < | | < < | < | | > | | > > > | < > | < > | > | > > | | < | > | > | > < < < | < < < | < < < < | > | | | | | | | | | | | | > | > > > > > > > > > > > > > > | > > > > | | | | | | > | > > | | | | | | | | > | | | | | | | > > > > > > > > > > > > > > > > > > | | > > | 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 | ** zBranch might be NULL or an empty string if no forcing occurs. ** ** parent_rid is the recordid of the parent check-in. */ static void prepare_commit_comment( Blob *pComment, char *zInit, CheckinInfo *p, int parent_rid, int dryRunFlag ){ Blob prompt; #if defined(_WIN32) || defined(__CYGWIN__) int bomSize; const unsigned char *bom = get_utf8_bom(&bomSize); blob_init(&prompt, (const char *) bom, bomSize); if( zInit && zInit[0]){ 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 ); if( dryRunFlag ){ blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes " "will be made to the repository\n#\n"); } 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, C_DEFAULT | C_FATAL | C_COMMENT); 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 ); } if( p->verboseFlag ){ DiffConfig DCfg; blob_appendf(&prompt, "#\n%.78c\n" "# The following diff is excluded from the commit message:\n#\n", '#' ); diff_options(&DCfg, 0, 1); DCfg.diffFlags |= DIFF_VERBOSE; if( g.aCommitFile ){ Stmt q; Blob sql = BLOB_INITIALIZER; FileDirList *diffFiles; int i; for(i=0; g.aCommitFile[i]!=0; ++i){} diffFiles = fossil_malloc_zero((i+1) * sizeof(*diffFiles)); for(i=0; g.aCommitFile[i]!=0; ++i){ blob_append_sql(&sql, "SELECT pathname, deleted, rid WHERE id=%d", g.aCommitFile[i]); db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); assert( db_step(&q)==SQLITE_ROW ); diffFiles[i].zName = fossil_strdup(db_column_text(&q, 0)); DCfg.diffFlags &= (~DIFF_FILE_MASK); if( db_column_int(&q, 1) ){ DCfg.diffFlags |= DIFF_FILE_DELETED; }else if( db_column_int(&q, 2)==0 ){ DCfg.diffFlags |= DIFF_FILE_ADDED; } db_finalize(&q); if( fossil_strcmp(diffFiles[i].zName, "." )==0 ){ diffFiles[0].zName[0] = '.'; diffFiles[0].zName[1] = 0; break; } diffFiles[i].nName = strlen(diffFiles[i].zName); diffFiles[i].nUsed = 0; } diff_against_disk(0, &DCfg, diffFiles, &prompt); for( i=0; diffFiles[i].zName; ++i ){ fossil_free(diffFiles[i].zName); } fossil_free(diffFiles); }else{ diff_against_disk(0, &DCfg, 0, &prompt); } } prompt_for_user_comment(pComment, &prompt); blob_reset(&prompt); } /* ** Prepare text that describes a pending commit and write it into ** a file at the root of the check-in. Return the name of that file. ** ** Space to hold the returned filename is obtained from fossil_malloc() ** and should be freed by the caller. The caller should also unlink ** the file when it is done. */ static char *prepare_commit_description_file( CheckinInfo *p, /* Information about this commit */ int parent_rid, /* parent check-in */ Blob *pComment, /* Check-in comment */ int dryRunFlag /* True for a dry-run only */ ){ Blob *pDesc; char *zTags; char *zFilename; Blob desc; blob_init(&desc, 0, 0); pDesc = &desc; blob_appendf(pDesc, "checkout %s\n", g.zLocalRoot); blob_appendf(pDesc, "repository %s\n", g.zRepositoryName); blob_appendf(pDesc, "user %s\n", p->zUserOvrd ? p->zUserOvrd : login_name()); blob_appendf(pDesc, "branch %s\n", (p->zBranch && p->zBranch[0]) ? p->zBranch : "trunk"); zTags = info_tags_of_checkin(parent_rid, 1); if( zTags || p->azTag ){ blob_append(pDesc, "tags ", -1); if(zTags){ blob_appendf(pDesc, "%z%s", zTags, p->azTag ? ", " : ""); } if(p->azTag){ int i = 0; for( ; p->azTag[i]; ++i ){ blob_appendf(pDesc, "%s%s", p->azTag[i], p->azTag[i+1] ? ", " : ""); } } blob_appendf(pDesc, "\n"); } status_report(pDesc, C_DEFAULT | C_FATAL); if( g.markPrivate ){ blob_append(pDesc, "private-branch\n", -1); } if( p->integrateFlag ){ blob_append(pDesc, "integrate\n", -1); } if( pComment && blob_size(pComment)>0 ){ blob_appendf(pDesc, "checkin-comment\n%s\n", blob_str(pComment)); } if( dryRunFlag ){ zFilename = 0; fossil_print("******* Commit Description *******\n%s" "***** End Commit Description *****\n", blob_str(pDesc)); }else{ unsigned int r[2]; sqlite3_randomness(sizeof(r), r); zFilename = mprintf("%scommit-description-%08x%08x.txt", g.zLocalRoot, r[0], r[1]); blob_write_to_file(pDesc, zFilename); } blob_reset(pDesc); return zFilename; } /* ** Populate the Global.aCommitFile[] based on the command line arguments ** to a [commit] command. Global.aCommitFile is an array of integers ** sized at (N+1), where N is the number of arguments passed to [commit]. ** The contents are the [id] values from the vfile table corresponding ** to the filenames passed as arguments. ** ** The last element of aCommitFile[] is always 0 - indicating the end ** of the array. ** ** If there were no arguments passed to [commit], aCommitFile is not ** allocated and remains NULL. Other parts of the code interpret this ** to mean "all files". ** ** Returns 1 if there was a warning, 0 otherwise. */ 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, 0, 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]); result = 1; } blob_reset(&fname); } g.aCommitFile = fossil_malloc( (bag_count(&toCommit)+1) * sizeof(g.aCommitFile[0]) ); for(ii=bag_first(&toCommit); ii>0; ii=bag_next(&toCommit, ii)){ g.aCommitFile[jj++] = ii; } g.aCommitFile[jj] = 0; bag_clear(&toCommit); } return result; } /* ** Returns true if the check-in identified by the first parameter is ** older than the given (valid) date/time string, else returns false. ** Also returns true if rid does not refer to a check-in, but it is not ** intended to be used for that case. */ int checkin_is_younger( int rid, /* The record ID of the ancestor */ const char *zDate /* Date & time of the current check-in */ ){ return db_exists( "SELECT 1 FROM event" " WHERE datetime(mtime)>=%Q" " AND type='ci' AND objid=%d", zDate, rid ) ? 0 : 1; } /* ** Make sure the current check-in with timestamp zDate is younger than its ** ancestor identified rid and zUuid. Throw a fatal error if not. */ static void checkin_verify_younger( int rid, /* The record ID of the ancestor */ const char *zUuid, /* The artifact hash of the ancestor */ const char *zDate /* Date & time of the current check-in */ ){ #ifndef FOSSIL_ALLOW_OUT_OF_ORDER_DATES if(checkin_is_younger(rid,zDate)==0){ 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 ** format YYYY-MM-DDTHH:MM:SS. If the string is not a valid date, ** print a fatal error and quit. */ char *date_in_standard_format(const char *zInputDate){ char *zDate; if( g.perm.Setup && fossil_strcmp(zInputDate,"now")==0 ){ zInputDate = PD("date_override","now"); } zDate = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)", zInputDate); if( zDate[0]==0 ){ fossil_fatal( "unrecognized date format (%s): use \"YYYY-MM-DD HH:MM:SS.SSS\"", zInputDate ); } return zDate; } /* ** COMMAND: test-date-format ** ** Usage: %fossil test-date-format DATE-STRING... ** ** Convert the DATE-STRING into the standard format used in artifacts ** and display the result. */ void test_date_format(void){ int i; db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); for(i=2; i<g.argc; i++){ fossil_print("%s -> %s\n", g.argv[i], date_in_standard_format(g.argv[i])); } } #if INTERFACE /* ** The following structure holds some of the information needed to construct a ** 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 */ int verboseFlag; /* Show diff in editor for check-in comment */ 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, /* Hash 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; /* Hash 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 check-out/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; } if( blob_size(p->pComment)!=0 ){ blob_appendf(pOut, "C %F\n", blob_str(p->pComment)); }else{ blob_append(pOut, "C (no\\scomment)\n", 16); } zDate = date_in_standard_format(p->zDateOvrd ? p->zDateOvrd : "now"); blob_appendf(pOut, "D %s\n", zDate); zDate[10] = ' '; db_prepare(&q, "SELECT pathname, uuid, origname, blob.rid, isexe, islink," " is_selected(vfile.id)" " FROM vfile JOIN blob ON vfile.mrid=blob.rid" " WHERE (NOT deleted OR NOT is_selected(vfile.id))" " AND vfile.vid=%d" " ORDER BY if_selected(vfile.id, pathname, origname)", vid); blob_zero(&filename); blob_appendf(&filename, "%s", g.zLocalRoot); nBasename = blob_size(&filename); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zUuid = db_column_text(&q, 1); const char *zOrig = db_column_text(&q, 2); int frid = db_column_int(&q, 3); int isExe = db_column_int(&q, 4); int isLink = db_column_int(&q, 5); int isSelected = db_column_int(&q, 6); const char *zPerm; int cmp; blob_resize(&filename, nBasename); blob_append(&filename, zName, -1); #if !defined(_WIN32) /* For unix, extract the "executable" and "symlink" permissions ** directly from the filesystem. On windows, permissions are ** unchanged from the original. However, only do this if the file ** itself is actually selected to be part of this check-in. */ if( isSelected ){ int mPerm; mPerm = file_perm(blob_str(&filename), RepoFILE); isExe = ( mPerm==PERM_EXE ); isLink = ( mPerm==PERM_LNK ); } #endif if( isExe ){ zPerm = " x"; }else if( isLink ){ zPerm = " l"; /* note: symlinks don't have executable bit on unix */ }else{ zPerm = ""; } if( !g.markPrivate ) content_make_public(frid); while( pFile && fossil_strcmp(pFile->zName,zName)<0 ){ blob_appendf(pOut, "F %F\n", pFile->zName); pFile = manifest_file_next(pBaseline, 0); nFBcard++; } cmp = 1; if( pFile==0 || (cmp = fossil_strcmp(pFile->zName,zName))!=0 || fossil_strcmp(pFile->zUuid, zUuid)!=0 ){ if( zOrig && !isSelected ){ zName = zOrig; zOrig = 0; } if( zOrig==0 || fossil_strcmp(zOrig,zName)==0 ){ blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm); }else{ if( zPerm[0]==0 ){ zPerm = " w"; } blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig); } nFBcard++; } if( cmp==0 ) pFile = manifest_file_next(pBaseline,0); } blob_reset(&filename); db_finalize(&q); while( pFile ){ 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 = rid_to_uuid(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 || mhash, merge" " FROM vmerge" " WHERE (vmerge.id=-1 OR vmerge.id=-2)" " ORDER BY 1"); while( db_step(&q)==SQLITE_ROW ){ const char *zCherrypickUuid = db_column_text(&q, 0); int mid = db_column_int(&q, 1); if( (!g.markPrivate && content_is_private(mid)) || (mid == vid) ) continue; blob_appendf(pOut, "Q %s\n", zCherrypickUuid); } db_finalize(&q); if( p->pCksum ) blob_appendf(pOut, "R %b\n", p->pCksum); zColor = p->zColor; if( p->zBranch && p->zBranch[0] ){ /* Set tags for the new branch */ if( p->zBrClr && p->zBrClr[0] ){ zColor = 0; blob_appendf(pOut, "T *bgcolor * %F\n", p->zBrClr); } blob_appendf(pOut, "T *branch * %F\n", p->zBranch); blob_appendf(pOut, "T *sym-%F *\n", p->zBranch); } if( zColor && zColor[0] ){ /* 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 mhash,merge FROM vmerge" " 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)){ #if 0 /* Make sure the check-in manifest of the resulting merge child does not ** include a +close tag referring to the leaf check-in on a private ** branch, so as not to generate a missing artifact reference on ** repository clones without that private branch. The merge command ** should have dropped the --integrate option, at this point. */ assert( !content_is_private(rid) ); #endif blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid); } } db_finalize(&q); if( p->azTag ){ for(i=0; p->azTag[i]; i++){ /* Add a symbolic tag to this check-in. The tag names have already ** been sorted and converted using the %F format */ assert( i==0 || strcmp(p->azTag[i-1], p->azTag[i])<=0 ); blob_appendf(pOut, "T +sym-%s *\n", p->azTag[i]); } } if( p->zBranch && p->zBranch[0] ){ /* For a new branch, cancel all prior propagating tags */ db_prepare(&q, "SELECT tagname FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" " AND tagtype==2 AND tagname GLOB 'sym-*'" " AND tagname!='sym-'||%Q" " ORDER BY tagname", 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 ** if a Unicode (UTF-16) byte-order-mark (BOM) or a \r\n line ending ** is seen in a text file. ** ** Return 1 if the user pressed 'c'. In that case, the file will have ** been converted to UTF-8 (if it was UTF-16) with LF line-endings, ** and the original file will have been renamed to "<filename>-original". */ static int commit_warning( Blob *pContent, /* The content of the file being committed. */ int crlfOk, /* Non-zero if CR/LF warnings should be disabled. */ int binOk, /* Non-zero if binary warnings should be disabled. */ int encodingOk, /* Non-zero if encoding warnings should be disabled. */ int sizeOk, /* Non-zero if oversize warnings are disabled */ int noPrompt, /* 0 to always prompt, 1 for 'N', 2 for 'Y'. */ const char *zFilename, /* The full name of the file being committed. */ Blob *pReason /* Reason for warning, if any (non-fatal only). */ ){ 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 invalid UTF-8 */ int fHasNul; /* contains NUL chars? */ int fHasLong; /* overly long line? */ 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; if( sizeOk ){ fUnicode = could_be_utf16(pContent, &bReverse); if( fUnicode ){ lookFlags = looks_like_utf16(pContent, bReverse, LOOK_NUL); }else{ lookFlags = looks_like_utf8(pContent, LOOK_NUL); if( !(lookFlags & LOOK_BINARY) && invalid_utf8(pContent) ){ fHasInvalidUtf8 = 1; } } fHasAnyCr = (lookFlags & LOOK_CR); fBinary = (lookFlags & LOOK_BINARY); fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR); fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF); fHasNul = (lookFlags & LOOK_NUL); fHasLong = (lookFlags & LOOK_LONG); }else{ fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0; fHasLoneCrOnly = fHasCrLfOnly = fHasNul = fHasLong = 0; } if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){ const char *zWarning = 0; const char *zDisable = 0; const char *zConvert = "c=convert/"; const char *zIn = "in"; Blob ans; char cReply; if( fBinary ){ if( binOk ){ return 0; /* We don't want binary warnings for this file. */ } if( !fHasNul && fHasLong ){ zWarning = "long lines"; zConvert = ""; /* We cannot convert overlong lines. */ }else{ zWarning = "binary data"; zConvert = ""; /* We cannot convert binary files. */ } zDisable = "\"binary-glob\" setting"; }else if( fUnicode && fHasAnyCr ){ if( crlfOk && encodingOk ){ return 0; /* We don't want CR/LF and Unicode warnings for this file. */ } if( fHasLoneCrOnly ){ zWarning = "CR line endings and Unicode"; }else if( fHasCrLfOnly ){ zWarning = "CR/LF line endings and Unicode"; }else{ zWarning = "mixed line endings and Unicode"; } zDisable = "\"crlf-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( crlfOk ){ return 0; /* We don't want CR/LF warnings for this file. */ } if( fHasLoneCrOnly ){ zWarning = "CR line endings"; }else if( fHasCrLfOnly ){ zWarning = "CR/LF line endings"; }else{ zWarning = "mixed line endings"; } zDisable = "\"crlf-glob\" setting"; }else if( !sizeOk ){ zWarning = "oversize"; zIn = "file"; }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); if( !sizeOk ){ zMsg = mprintf( "%s is more than %,lld bytes in size.\n" "Commit anyhow (a=all/y/N)? ", blob_str(&fname), db_large_file_size()); }else{ 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); } if( noPrompt==0 ){ prompt_user(zMsg, &ans); cReply = blob_str(&ans)[0]; blob_reset(&ans); }else if( noPrompt==2 ){ cReply = 'Y'; }else{ cReply = 'N'; } fossil_free(zMsg); 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(pContent, 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(pContent, 0); }else if( fHasInvalidUtf8 ){ blob_cp1252_to_utf8(pContent); } if( fHasAnyCr ){ blob_to_lf_only(pContent); } fwrite(blob_buffer(pContent), 1, blob_size(pContent), f); fclose(f); } return 1; }else if( cReply!='y' && cReply!='Y' ){ fossil_fatal("Abandoning commit due to %s %s %s", zWarning, zIn, blob_str(&fname)); }else if( noPrompt==2 ){ if( pReason ){ blob_append(pReason, zWarning, -1); } return 1; } blob_reset(&fname); } return 0; } /* ** COMMAND: test-commit-warning ** ** Usage: %fossil test-commit-warning ?OPTIONS? ** ** Check each file in the check-out, including unmodified ones, using all ** the pre-commit checks. ** ** Options: ** --no-settings Do not consider any glob settings. ** -v|--verbose Show per-file results for all pre-commit checks. ** ** See also: commit, extras */ void test_commit_warning(void){ int rc = 0; int noSettings; int verboseFlag; i64 mxSize; Stmt q; noSettings = find_option("no-settings",0,0)!=0; verboseFlag = find_option("verbose","v",0)!=0; verify_all_options(); db_must_be_within_tree(); mxSize = db_large_file_size(); db_prepare(&q, "SELECT %Q || pathname, pathname, %s, %s, %s FROM vfile" " WHERE NOT deleted", g.zLocalRoot, glob_expr("pathname", noSettings ? 0 : db_get("crlf-glob", db_get("crnl-glob",""))), glob_expr("pathname", noSettings ? 0 : db_get("binary-glob","")), glob_expr("pathname", noSettings ? 0 : db_get("encoding-glob","")) ); while( db_step(&q)==SQLITE_ROW ){ const char *zFullname; const char *zName; Blob content; Blob reason; int crlfOk, binOk, encodingOk, sizeOk; int fileRc; zFullname = db_column_text(&q, 0); zName = db_column_text(&q, 1); crlfOk = db_column_int(&q, 2); binOk = db_column_int(&q, 3); encodingOk = db_column_int(&q, 4); sizeOk = mxSize<=0 || file_size(zFullname, ExtFILE)<=mxSize; blob_zero(&content); blob_read_from_file(&content, zFullname, RepoFILE); blob_zero(&reason); fileRc = commit_warning(&content, crlfOk, binOk, encodingOk, sizeOk, 2, zFullname, &reason); if( fileRc || verboseFlag ){ fossil_print("%d\t%s\t%s\n", fileRc, zName, blob_str(&reason)); } blob_reset(&reason); rc |= fileRc; } db_finalize(&q); fossil_print("%d\n", rc); } /* ** qsort() comparison routine for an array of pointers to strings. */ static int tagCmp(const void *a, const void *b){ char **pA = (char**)a; char **pB = (char**)b; return fossil_strcmp(pA[0], pB[0]); } /* ** COMMAND: ci# ** COMMAND: commit ** ** Usage: %fossil commit ?OPTIONS? ?FILE...? ** or: %fossil ci ?OPTIONS? ?FILE...? ** ** Create a new version containing all of the changes in the current ** check-out. You will be prompted to enter a check-in comment unless ** the comment has been specified on the command-line using "-m" or a ** file containing the comment using -M. The editor defined in the ** "editor" fossil option (see %fossil help set) will be used, or from ** the "VISUAL" or "EDITOR" environment variables (in that order) if ** no editor is set. ** ** All files that have changed will be committed unless some subset of ** files is specified on the command line. ** ** 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/LF 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 --hash option detects edited files by computing each file's ** artifact 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 ** ("auto" lets Fossil choose it automatically, ** even for private branches) ** --close Close the branch being committed ** --date-override DATETIME DATE to use instead of 'now' ** --delta Use a delta manifest in the commit process ** --hash Verify file status using hashing rather ** than relying on file mtimes ** --ignore-clock-skew If a clock skew is detected, ignore it and ** behave as if the user had entered 'yes' to ** the question of whether to proceed despite ** the skew. ** --ignore-oversize Do not warning the user about oversized files ** --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 ** -v|--verbose Show a diff in the commit message prompt ** --no-prompt This option disables prompting the user for ** input and assumes an answer of 'No' for every ** question. ** --no-warnings Omit all warnings about file contents ** --no-verify Do not run before-commit hooks ** --nosign Do not attempt to sign this commit with gpg ** --override-lock Allow a check-in even though parent is locked ** --private Do not sync changes and their descendants ** --tag TAG-NAME Assign given tag TAG-NAME to the check-in ** --trace Debug tracing ** --user-override USER USER to use instead of the current default ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. ** ** See also: [[branch]], [[changes]], [[update]], [[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; /* Hash of the new check-in */ int useHash = 0; /* True to verify file status using hashing */ int noSign = 0; /* True to omit signing the manifest using GPG */ int privateFlag = 0; /* True if the --private option is present */ int privateParent = 0; /* True if the parent check-in is private */ int isAMerge = 0; /* True if checking in a merge */ int noWarningFlag = 0; /* True if skipping all warnings */ int noVerify = 0; /* Do not run before-commit hooks */ int bTrace = 0; /* Debug tracing */ int noPrompt = 0; /* True if skipping all prompts */ 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 */ int allowEmpty = 0; /* Allow a commit with no changes */ int allowFork = 0; /* Allow the commit to fork */ int allowOlder = 0; /* Allow a commit older than its ancestor */ char *zManifestFile; /* Name of the manifest file */ int useCksum; /* True if checksums should be computed and verified */ int outputManifest; /* True to output "manifest" and "manifest.uuid" */ int dryRunFlag; /* True for a test run. Debugging only */ CheckinInfo sCiInfo; /* Information about this check-in */ const char *zComFile; /* Read commit message from this file */ int nTag = 0; /* Number of --tag arguments */ const char *zTag; /* A single --tag argument */ ManifestFile *pFile; /* File structure in the manifest */ Manifest *pManifest; /* Manifest structure */ Blob manifest; /* Manifest in baseline form */ Blob muuid; /* Manifest uuid */ Blob cksum1, cksum2; /* Before and after commit checksums */ Blob cksum1b; /* Checksum recorded in the manifest */ int szD; /* Size of the delta manifest */ int szB; /* Size of the baseline manifest */ int nConflict = 0; /* Number of unresolved merge conflicts */ int abortCommit = 0; /* Abort the commit due to text format conversions */ Blob ans; /* Answer to continuation prompts */ char cReply; /* First character of ans */ int bRecheck = 0; /* Repeat fork and closed-branch checks*/ int bAutoBrClr = 0; /* Value of "--branchcolor" is "auto" */ int bIgnoreSkew = 0; /* --ignore-clock-skew flag */ int mxSize; memset(&sCiInfo, 0, sizeof(sCiInfo)); url_proxy_options(); /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; noSign = find_option("nosign",0,0)!=0; privateFlag = find_option("private",0,0)!=0; forceDelta = find_option("delta",0,0)!=0; forceBaseline = find_option("baseline",0,0)!=0; db_must_be_within_tree(); if( db_get_boolean("dont-commit",0) ){ fossil_fatal("committing is prohibited: the 'dont-commit' option is set"); } if( forceDelta ){ if( forceBaseline ){ fossil_fatal("cannot use --delta and --baseline together"); } if( db_get_boolean("forbid-delta-manifests",0) ){ fossil_fatal("delta manifests are prohibited in this repository"); } } dryRunFlag = find_option("dry-run","n",0)!=0; if( !dryRunFlag ){ dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ } zComment = find_option("comment","m",1); forceFlag = find_option("force", "f", 0)!=0; allowConflict = find_option("allow-conflict",0,0)!=0; allowEmpty = find_option("allow-empty",0,0)!=0; allowFork = find_option("allow-fork",0,0)!=0; if( find_option("override-lock",0,0)!=0 ) allowFork = 1; allowOlder = find_option("allow-older",0,0)!=0; noPrompt = find_option("no-prompt", 0, 0)!=0; noWarningFlag = find_option("no-warnings", 0, 0)!=0; noVerify = find_option("no-verify",0,0)!=0; bTrace = find_option("trace",0,0)!=0; sCiInfo.zBranch = find_option("branch","b",1); sCiInfo.zColor = find_option("bgcolor",0,1); sCiInfo.zBrClr = find_option("branchcolor",0,1); if ( fossil_strncmp(sCiInfo.zBrClr, "auto", 4)==0 ) { bAutoBrClr = 1; sCiInfo.zBrClr = 0; } sCiInfo.closeFlag = find_option("close",0,0)!=0; sCiInfo.integrateFlag = find_option("integrate",0,0)!=0; sCiInfo.zMimetype = find_option("mimetype",0,1); sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0; 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; } zComFile = find_option("message-file", "M", 1); sCiInfo.zDateOvrd = find_option("date-override",0,1); sCiInfo.zUserOvrd = find_option("user-override",0,1); noSign = db_get_boolean("omitsign", 0)|noSign; if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } useCksum = db_get_boolean("repo-cksum", 1); bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0; outputManifest = db_get_manifest_setting(); mxSize = db_large_file_size(); if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0; verify_all_options(); /* Get the ID of the parent manifest artifact */ vid = db_lget_int("checkout", 0); if( vid==0 ){ useCksum = 1; if( privateFlag==0 && sCiInfo.zBranch==0 ) { sCiInfo.zBranch=db_get("main-branch", 0); } }else{ privateParent = content_is_private(vid); } /* Track the "private" status */ g.markPrivate = privateFlag || privateParent; if( privateFlag && !privateParent ){ /* Apply default branch name ("private") and color ("orange") if not ** specified otherwise on the command-line, and if the parent is not ** already private. */ if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private"; if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 && !bAutoBrClr) { sCiInfo.zBrClr = "#fec084"; } } /* Do not allow the creation of a new branch using an existing open ** branch name unless the --force flag is used */ if( sCiInfo.zBranch!=0 && !forceFlag && fossil_strcmp(sCiInfo.zBranch,"private")!=0 && branch_is_open(sCiInfo.zBranch) ){ fossil_fatal("an open branch named \"%s\" already exists - use --force" " to override", sCiInfo.zBranch); } /* Escape special characters in tags and put all tags in sorted order */ if( nTag ){ int i; for(i=0; i<nTag; i++) sCiInfo.azTag[i] = mprintf("%F", sCiInfo.azTag[i]); qsort((void*)sCiInfo.azTag, nTag, sizeof(sCiInfo.azTag[0]), tagCmp); } /* ** Autosync if autosync is enabled and this is not a private check-in. */ if( !g.markPrivate ){ int syncFlags = SYNC_PULL; if( vid!=0 && !allowFork && !forceFlag ){ syncFlags |= SYNC_CKIN_LOCK; } if( autosync_loop(syncFlags, 1, "commit") ){ fossil_exit(1); } } /* So that older versions of Fossil (that do not understand delta- ** manifest) can continue to use this repository, do not create a new ** delta-manifest unless this repository already contains one or more ** delta-manifests, or unless the delta-manifest is explicitly requested ** by the --delta option. ** ** The forbid-delta-manifests setting prevents new delta manifests. ** ** If the remote repository sent an avoid-delta-manifests pragma on ** the autosync above, then also try to avoid deltas, unless the ** --delta option is specified. The remote repo will send the ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests" ** setting is enabled. */ if( !db_get_boolean("seen-delta-manifest",0) || db_get_boolean("forbid-delta-manifests",0) || g.bAvoidDeltaManifests ){ if( !forceDelta ) forceBaseline = 1; } /* Require confirmation to continue with the check-in if there is ** clock skew */ if( g.clockSkewSeen ){ if( bIgnoreSkew!=0 ){ cReply = 'y'; fossil_warning("Clock skew ignored due to --ignore-clock-skew."); }else if( !noPrompt ){ prompt_user("continue in spite of time skew (y/N)? ", &ans); cReply = blob_str(&ans)[0]; blob_reset(&ans); }else{ fossil_print("Abandoning commit due to time skew\n"); cReply = 'N'; } 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() ){ if( !noPrompt ){ prompt_user("continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; blob_reset(&ans); }else{ cReply = 'N'; } 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" ** will generate a manifest that has two fileA entries, which is illegal. ** When you think about it, the sequence above makes no sense. So detect ** it and disallow it. Ticket [0ff64b0a5fc8]. */ if( g.aCommitFile ){ db_prepare(&q, "SELECT v1.pathname, v2.pathname" " FROM vfile AS v1, vfile AS v2" " WHERE is_selected(v1.id)" " AND v2.origname IS NOT NULL" " AND v2.origname=v1.pathname" " AND NOT is_selected(v2.id)"); if( db_step(&q)==SQLITE_ROW ){ const char *zFrom = db_column_text(&q, 0); const char *zTo = db_column_text(&q, 1); fossil_fatal("cannot do a partial commit of '%s' without '%s' because " "'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo); } db_finalize(&q); } user_select(); /* ** 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(useHash ? CKSIG_HASH : 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 ** been modified, bail out now unless the --allow-empty or --force ** flags is used. */ if( g.aCommitFile && !allowEmpty && !forceFlag && !db_exists( "SELECT 1 FROM vfile " " WHERE is_selected(id)" " AND (chnged OR deleted OR rid=0 OR pathname!=origname)") ){ fossil_fatal("none of the selected files have changed; use " "--allow-empty to override."); } /* This loop checks for potential forks and for check-ins against a ** closed branch. The checks are repeated once after interactive ** check-in comment editing. */ do{ /* ** 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( sCiInfo.zBranch==0 && allowFork==0 && forceFlag==0 && g.markPrivate==0 && (vid==0 || !is_a_leaf(vid) || g.ckinLockFail) ){ if( g.ckinLockFail ){ fossil_fatal("Might fork due to a check-in race with user \"%s\"\n" "Try \"update\" first, or --branch, or " "use --override-lock", g.ckinLockFail); }else{ fossil_fatal("Would fork. \"update\" first or use --branch or " "--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... */ leaf_is_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"); } /* Always exit the loop on the second pass */ if( bRecheck ) break; /* Get the check-in comment. This might involve prompting the ** user for the check-in comment, in which case we should resync ** to renew the check-in lock and repeat the checks for conflicts. */ if( zComment ){ blob_zero(&comment); blob_append(&comment, zComment, -1); }else if( zComFile ){ blob_zero(&comment); blob_read_from_file(&comment, zComFile, ExtFILE); blob_to_utf8_no_bom(&comment, 1); }else if( !noPrompt ){ char *zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'"); prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag); 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]; blob_reset(&ans); if( cReply!='y' && cReply!='Y' ){ fossil_fatal("Commit aborted."); } } free(zInit); db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment); db_end_transaction(0); db_begin_transaction(); if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){ /* Do another auto-pull, renewing the check-in lock. Then set ** bRecheck so that we loop back above to verify that the check-in ** is still not against a closed branch and still won't fork. */ int syncFlags = SYNC_PULL|SYNC_CKIN_LOCK; if( autosync_loop(syncFlags, 1, "commit") ){ fossil_fatal("Auto-pull failed. Commit aborted."); } bRecheck = 1; } }else{ blob_zero(&comment); } }while( bRecheck ); if( blob_size(&comment)==0 ){ if( !dryRunFlag ){ if( !noPrompt ){ prompt_user("empty check-in comment. continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; blob_reset(&ans); }else{ cReply = 'N'; } if( cReply!='y' && cReply!='Y' ){ fossil_fatal("Abandoning commit due to empty check-in comment\n"); } } } if( !noVerify && hook_exists("before-commit") ){ /* Run before-commit hooks */ char *zAuxFile; zAuxFile = prepare_commit_description_file( &sCiInfo, vid, &comment, dryRunFlag); if( zAuxFile ){ int rc = hook_run("before-commit",zAuxFile,bTrace); file_delete(zAuxFile); fossil_free(zAuxFile); if( rc ){ fossil_fatal("Before-commit hook failed\n"); } } } /* ** Step 1: Compute an aggregate MD5 checksum over the disk image ** of every file in vid. The file names are part of the checksum. ** The resulting checksum is the same as is expected on the R-card ** of a manifest. */ if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1); /* Step 2: Insert records for all modified files into the blob ** table. If there were arguments passed to this command, only ** the identified files are inserted (if they have been modified). */ db_prepare(&q, "SELECT id, %Q || pathname, mrid, %s, %s, %s FROM vfile " "WHERE chnged<>0 AND NOT deleted AND is_selected(id)", g.zLocalRoot, glob_expr("pathname", db_get("crlf-glob",db_get("crnl-glob",""))), glob_expr("pathname", db_get("binary-glob","")), glob_expr("pathname", db_get("encoding-glob","")) ); while( db_step(&q)==SQLITE_ROW ){ int id, rid; const char *zFullname; Blob content; int crlfOk, binOk, encodingOk, sizeOk; id = db_column_int(&q, 0); zFullname = db_column_text(&q, 1); rid = db_column_int(&q, 2); crlfOk = db_column_int(&q, 3); binOk = db_column_int(&q, 4); encodingOk = db_column_int(&q, 5); sizeOk = mxSize<=0 || file_size(zFullname, ExtFILE)<=mxSize; blob_zero(&content); blob_read_from_file(&content, zFullname, RepoFILE); /* Do not emit any warnings when they are disabled. */ if( !noWarningFlag ){ abortCommit |= commit_warning(&content, crlfOk, binOk, encodingOk, sizeOk, noPrompt, zFullname, 0); } if( contains_merge_marker(&content) ){ Blob fname; /* Relative pathname of the file */ nConflict++; file_relative_name(zFullname, &fname, 0); fossil_print("possible unresolved merge conflict in %s\n", blob_str(&fname)); blob_reset(&fname); } nrid = content_put(&content); blob_reset(&content); if( nrid!=rid ){ if( rid>0 ){ content_deltify(rid, &nrid, 1, 0); } db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d, mhash=NULL WHERE id=%d", nrid,nrid,id); db_add_unsent(nrid); } } db_finalize(&q); if( nConflict && !allowConflict ){ fossil_fatal("abort due to unresolved merge conflicts; " "use --allow-conflict to override"); }else if( abortCommit ){ fossil_fatal("one or more files were converted on your request; " "please re-test before committing"); } /* Create the new manifest */ sCiInfo.pComment = &comment; sCiInfo.pCksum = useCksum ? &cksum1 : 0; sCiInfo.verifyDate = !allowOlder && !forceFlag; if( forceDelta ){ blob_zero(&manifest); }else{ create_manifest(&manifest, 0, 0, vid, &sCiInfo, &szB); } /* See if a delta-manifest would be more appropriate */ if( !forceBaseline ){ const char *zBaselineUuid; Manifest *pParent; Manifest *pBaseline; pParent = manifest_get(vid, CFTYPE_MANIFEST, 0); if( pParent && pParent->zBaseline ){ zBaselineUuid = pParent->zBaseline; pBaseline = manifest_get_by_name(zBaselineUuid, 0); }else{ zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); 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 ** szD variable.) Assume that all delta manifests adds X new F-cards. ** Then to minimize the total number of F- and B-cards in the repository, ** we should use the delta manifest if and only if: ** ** D*D < B*X - X*X ** ** X is an unknown here, but for most repositories, we will not be ** far wrong if we assume X=3. */ if( forceDelta || (szD*szD)<(szB*3-9) ){ blob_reset(&manifest); manifest = delta; }else{ blob_reset(&delta); } }else if( forceDelta ){ fossil_fatal("unable to find a baseline-manifest for the delta"); } } if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){ if( !noPrompt ){ prompt_user("unable to sign manifest. continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; blob_reset(&ans); }else{ cReply = 'N'; } if( cReply!='y' && cReply!='Y' ){ fossil_fatal("Abandoning commit due to manifest signing failure\n"); } } /* If the -n|--dry-run option is specified, output the manifest file ** and rollback the transaction. */ if( dryRunFlag ){ blob_write_to_file(&manifest, ""); } if( outputManifest & MFESTFLG_RAW ){ zManifestFile = mprintf("%smanifest", g.zLocalRoot); blob_write_to_file(&manifest, zManifestFile); blob_reset(&manifest); blob_read_from_file(&manifest, zManifestFile, ExtFILE); free(zManifestFile); } nvid = content_put(&manifest); if( nvid==0 ){ fossil_fatal("trouble committing manifest: %s", g.zErrMsg); } db_add_unsent(nvid); if( manifest_crosslink(nvid, &manifest, dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){ fossil_fatal("%s", g.zErrMsg); } assert( blob_is_reset(&manifest) ); content_deltify(vid, &nvid, 1, 0); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); db_prepare(&q, "SELECT mhash,merge FROM vmerge WHERE id=-4"); while( db_step(&q)==SQLITE_ROW ){ const char *zIntegrateUuid = db_column_text(&q, 0); if( is_a_leaf(db_column_int(&q, 1)) ){ fossil_print("Closed: %s\n", zIntegrateUuid); }else{ fossil_print("Not_Closed: %s (not a leaf any more)\n", zIntegrateUuid); } } db_finalize(&q); fossil_print("New_Version: %s\n", zUuid); if( outputManifest & MFESTFLG_UUID ){ zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot); blob_zero(&muuid); blob_appendf(&muuid, "%s\n", zUuid); blob_write_to_file(&muuid, zManifestFile); free(zManifestFile); blob_reset(&muuid); } /* Update the vfile and vmerge tables */ db_multi_exec( "DELETE FROM vfile WHERE (vid!=%d OR deleted) AND is_selected(id);" "DELETE FROM vmerge;" "UPDATE vfile SET vid=%d;" "UPDATE vfile SET rid=mrid, mhash=NULL, chnged=0, deleted=0, origname=NULL" " WHERE is_selected(id);" , vid, nvid ); db_set_checkout(nvid); /* Update the isexe and islink columns of the vfile table */ db_prepare(&q, "UPDATE vfile SET isexe=:exec, islink=:link" " WHERE vid=:vid AND pathname=:path AND (isexe!=:exec OR islink!=:link)" ); db_bind_int(&q, ":vid", nvid); pManifest = manifest_get(nvid, CFTYPE_MANIFEST, 0); manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest, 0)) ){ db_bind_int(&q, ":exec", pFile->zPerm && strstr(pFile->zPerm, "x")); db_bind_int(&q, ":link", pFile->zPerm && strstr(pFile->zPerm, "l")); db_bind_text(&q, ":path", pFile->zName); db_step(&q); db_reset(&q); } db_finalize(&q); manifest_destroy(pManifest); 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 check-out does not match what would have ended " "up in the repository: %b versus %b", &cksum1, &cksum2); } /* Verify that the manifest checksum matches the expected checksum */ vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b); if( blob_compare(&cksum1, &cksum1b) ){ fossil_fatal("manifest checksum self-test failed: " "%b versus %b", &cksum1, &cksum1b); } if( blob_compare(&cksum1, &cksum2) ){ fossil_fatal( "working check-out does not match manifest after commit: " "%b versus %b", &cksum1, &cksum2); } /* Verify that the commit did not modify any disk images. */ vfile_aggregate_checksum_disk(nvid, &cksum2); if( blob_compare(&cksum1, &cksum2) ){ fossil_fatal("working check-out before and after commit does not match"); } } /* Clear the undo/redo stack */ undo_reset(); /* Commit */ db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); db_multi_exec("PRAGMA repository.application_id=252006673;"); db_multi_exec("PRAGMA localdb.application_id=252006674;"); if( dryRunFlag ){ db_end_transaction(1); return; } db_end_transaction(0); if( outputManifest & MFESTFLG_TAGS ){ Blob tagslist; zManifestFile = mprintf("%smanifest.tags", g.zLocalRoot); blob_zero(&tagslist); get_checkin_taglist(nvid, &tagslist); blob_write_to_file(&tagslist, zManifestFile); blob_reset(&tagslist); free(zManifestFile); } if( !g.markPrivate ){ int syncFlags = SYNC_PUSH | SYNC_PULL | SYNC_IFABLE; autosync_loop(syncFlags, 0, "commit"); } if( count_nonbranch_children(vid)>1 ){ fossil_print("**** warning: a fork has occurred *****\n"); }else{ leaf_ambiguity_warning(nvid,nvid); } } |
Changes to src/checkout.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | ** from the local repository. */ #include "config.h" #include "checkout.h" #include <assert.h> /* | | | | < | < | > > > > > | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | | | | | | | | < < < < < < < < | < < < < > > | > > > > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | > > > > > > > > | < | | | > | < | > > | | > > > > > > > > | < | > | | | | > > | > > > > > | | > | | < | < > > > | < | > > > | > > > > > > > > > > > > > | > > > > > > > > > > > > > > | > > | > | > > > > > > | | | | > > > > > > > > | | > | | > > | > > > > > > > | | | > | > > | > > | | | | | > | | < < < < < | > | > | | > | | | | | > > > > > > > > > | | > > > > > > > > > > | | | 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 | ** from the local repository. */ #include "config.h" #include "checkout.h" #include <assert.h> /* ** Check to see if there is an existing check-out that has been ** modified. Return values: ** ** 0: There is an existing check-out but it is unmodified ** 1: There is a modified check-out - 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. ** ** Also delete any directory that becomes empty as a result of deleting ** files due to this operation, as long as that directory is not the ** current working directory and is not on the empty-dirs list. */ void uncheckout(int vid){ char *zPwd; if( vid<=0 ) return; sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0, file_dirname_sql_function, 0, 0); sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8|SQLITE_DIRECTONLY,0, file_delete_sql_function, 0, 0); sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, file_rmdir_sql_function, 0, 0); db_multi_exec( "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID", filename_collation() ); db_multi_exec( "INSERT OR IGNORE INTO dir_to_delete(name)" " SELECT dirname(pathname) FROM vfile" " WHERE vid=%d AND mrid>0", vid ); do{ db_multi_exec( "INSERT OR IGNORE INTO dir_to_delete(name)" " SELECT dirname(name) FROM dir_to_delete;" ); }while( db_changes() ); db_multi_exec( "SELECT unlink(%Q||pathname) FROM vfile" " WHERE vid=%d AND mrid>0;", g.zLocalRoot, vid ); ensure_empty_dirs_created(1); zPwd = file_getcwd(0,0); db_multi_exec( "SELECT rmdir(%Q||name) FROM dir_to_delete" " WHERE (%Q||name)<>%Q ORDER BY name DESC", g.zLocalRoot, g.zLocalRoot, zPwd ); fossil_free(zPwd); db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid); } /* ** Given the abbreviated hash 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 check out"); }; 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){ static Stmt s; db_static_prepare(&s, "UPDATE vfile SET isexe=:isexe" " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe" ); db_bind_int(&s, ":isexe", onoff); db_bind_int(&s, ":vid", vid); db_bind_text(&s, ":path", zFilename); db_step(&s); db_reset(&s); } /* ** Set or clear the execute permission bit (as appropriate) for all ** files in the current check-out, and replace files that have ** symlink bit with actual symlinks. */ void checkout_set_all_exe(int vid){ Blob filename; int baseLen; Manifest *pManifest; ManifestFile *pFile; /* Check the EXE permission status of all files */ pManifest = manifest_get(vid, CFTYPE_MANIFEST, 0); if( pManifest==0 ) return; blob_zero(&filename); blob_appendf(&filename, "%s", g.zLocalRoot); baseLen = blob_size(&filename); manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest, 0))!=0 ){ int isExe; blob_append(&filename, pFile->zName, -1); isExe = pFile->zPerm && strstr(pFile->zPerm, "x"); file_setexe(blob_str(&filename), isExe); set_or_clear_isexe(pFile->zName, vid, isExe); blob_resize(&filename, baseLen); } blob_reset(&filename); manifest_destroy(pManifest); } /* ** If the "manifest" setting is true, then automatically generate ** files named "manifest" and "manifest.uuid" containing, respectively, ** the text of the manifest and the artifact ID of the manifest. ** If the manifest setting is set, but is not a boolean value, then treat ** each character as a flag to enable writing "manifest", "manifest.uuid" or ** "manifest.tags". */ void manifest_to_disk(int vid){ char *zManFile; Blob manifest; Blob taglist; int flg; flg = db_get_manifest_setting(); if( flg & MFESTFLG_RAW ){ blob_zero(&manifest); content_get(vid, &manifest); sterilize_manifest(&manifest, CFTYPE_MANIFEST); zManFile = mprintf("%smanifest", g.zLocalRoot); blob_write_to_file(&manifest, zManFile); free(zManFile); }else{ if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){ zManFile = mprintf("%smanifest", g.zLocalRoot); file_delete(zManFile); free(zManFile); } } if( flg & MFESTFLG_UUID ){ Blob hash; zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); blob_set_dynamic(&hash, rid_to_uuid(vid)); 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.uuid'") ){ zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); file_delete(zManFile); free(zManFile); } } if( flg & MFESTFLG_TAGS ){ blob_zero(&taglist); zManFile = mprintf("%smanifest.tags", g.zLocalRoot); get_checkin_taglist(vid, &taglist); blob_write_to_file(&taglist, zManFile); free(zManFile); blob_reset(&taglist); }else{ if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.tags'") ){ zManFile = mprintf("%smanifest.tags", g.zLocalRoot); file_delete(zManFile); free(zManFile); } } } /* ** Find the branch name and all symbolic tags for a particular check-in ** identified by "rid". ** ** The branch name is actually only extracted if this procedure is run ** from within a local check-out. And the branch name is not the branch ** name for "rid" but rather the branch name for the current check-out. ** It is unclear if the rid parameter is always the same as the current ** check-out. */ void get_checkin_taglist(int rid, Blob *pOut){ Stmt stmt; char *zCurrent; blob_reset(pOut); zCurrent = db_text(0, "SELECT value FROM tagxref" " WHERE rid=%d AND tagid=%d", rid, TAG_BRANCH); blob_appendf(pOut, "branch %s\n", zCurrent); db_prepare(&stmt, "SELECT substr(tagname, 5)" " FROM tagxref, tag" " WHERE tagxref.rid=%d" " AND tagxref.tagtype>0" " AND tag.tagid=tagxref.tagid" " AND tag.tagname GLOB 'sym-*'", rid); while( db_step(&stmt)==SQLITE_ROW ){ const char *zName; zName = db_column_text(&stmt, 0); blob_appendf(pOut, "tag %s\n", zName); } db_reset(&stmt); db_finalize(&stmt); } /* ** COMMAND: checkout* ** COMMAND: co# ** ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS? ** or: %fossil co ?VERSION | --latest? ?OPTIONS? ** ** NOTE: Most people use "fossil update" instead of "fossil checkout" for ** day-to-day operations. If you are new to Fossil and trying to learn your ** way around, it is recommended that you become familiar with the ** "fossil update" command first. ** ** This command changes the current check-out to the version specified ** as an argument. The command aborts if there are edited files in the ** current check-out unless the --force option is used. 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 check-out the ** latest version in the repository. ** ** Options: ** --force Ignore edited files in the current check-out ** --keep Only update the manifest file(s) ** --force-missing Force check-out even if content is missing ** --setmtime Set timestamps of all files to match their SCM-side ** times (the timestamp of the last check-in which modified ** them) ** ** See also: [[update]] */ void checkout_cmd(void){ int forceFlag; /* Force check-out even if edits exist */ int forceMissingFlag; /* Force check-out even if missing content */ int keepFlag; /* Do not change any files on disk */ int latestFlag; /* Check out the latest version */ char *zVers; /* Version to check out */ int promptFlag; /* True to prompt before overwriting */ int vid, prior; int setmtimeFlag; /* --setmtime. Set mtimes on files */ 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; setmtimeFlag = find_option("setmtime",0,0)!=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 check-out"); } 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 ){ db_end_transaction(0); return; } }else{ zVers = g.argv[2]; } vid = load_vfile(zVers, forceMissingFlag); if( prior==vid ){ if( setmtimeFlag ) vfile_check_signature(vid, CKSIG_SETMTIME); db_end_transaction(0); return; } if( !keepFlag ){ uncheckout(prior); } db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); if( !keepFlag ){ vfile_to_disk(vid, 0, !g.fQuiet, promptFlag); } checkout_set_all_exe(vid); manifest_to_disk(vid); ensure_empty_dirs_created(0); db_set_checkout(vid); undo_reset(); db_multi_exec("DELETE FROM vmerge"); if( !keepFlag && db_get_boolean("repo-cksum",1) ){ vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b); vfile_aggregate_checksum_disk(vid, &cksum2); if( blob_compare(&cksum1, &cksum2) ){ fossil_print("WARNING: manifest checksum does not agree with disk\n"); } if( blob_size(&cksum1b) && blob_compare(&cksum1, &cksum1b) ){ fossil_print("WARNING: manifest checksum does not agree with manifest\n"); } } if( setmtimeFlag ) vfile_check_signature(vid, CKSIG_SETMTIME); db_end_transaction(0); } /* ** Unlink the local database file */ static void unlink_local_database(int manifestOnly){ const char *zReserved; int i; for(i=0; (zReserved = fossil_reserved_name(i, 1))!=0; i++){ if( manifestOnly==0 || zReserved[0]=='m' ){ char *z; z = mprintf("%s%s", g.zLocalRoot, zReserved); file_delete(z); free(z); } } } /* ** COMMAND: close* ** ** Usage: %fossil close ?OPTIONS? ** ** The opposite of "[[open]]". Close the current database connection. ** Require a -f or --force flag if there are unsaved changes in the ** current check-out or if there is non-empty stash. ** ** Options: ** -f|--force 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 check-out"); } if( !forceFlag && db_table_exists("localdb","stash") && db_exists("SELECT 1 FROM localdb.stash") ){ fossil_fatal("closing the check-out will delete your stash"); } if( db_is_writeable("repository") ){ db_unset_mprintf(1, "ckout:%q", g.zLocalRoot); } unlink_local_database(1); db_close(1); unlink_local_database(0); } |
Added src/ci_edit.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* Javascript used to make the check-in edit screen more interactive. */ function chgcbn(){ var newbr = document.getElementById('newbr'); var brname = document.getElementById('brname'); var checked = newbr.checked; var x = brname.value.trim(); if( !x || !newbr.checked ) x = newbr.getAttribute('data-branch'); if( newbr.checked ) brname.select(); document.getElementById('hbranch').textContent = x; cidbrid = document.getElementById('cbranch'); if( cidbrid ) cidbrid.textContent = x; } function chgbn(){ var newbr = document.getElementById('newbr'); var brname = document.getElementById('brname'); var x = brname.value.trim(); var br = newbr.getAttribute('data-branch'); if( !x ) x = br; newbr.checked = (x!=br); document.getElementById('hbranch').textContent = x; cidbrid = document.getElementById('cbranch'); if( cidbrid ) cidbrid.textContent = x; } function chgtn(){ var newtag = document.getElementById('newtag'); var tagname = document.getElementById('tagname'); newtag.checked=!!tagname.value; } (function(){ document.getElementById('newbr').onchange = chgcbn; document.getElementById('brname').onkeyup = chgbn; document.getElementById('tagname').onkeyup = chgtn; }()); |
Changes to src/clearsign.c.
︙ | ︙ | |||
37 38 39 40 41 42 43 | return 0; } zRand = db_text(0, "SELECT hex(randomblob(10))"); zOut = mprintf("out-%s", zRand); zIn = mprintf("in-%z", zRand); blob_write_to_file(pIn, zOut); zCmd = mprintf("%s %s %s", zBase, zIn, zOut); | | | | | | 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 | return 0; } zRand = db_text(0, "SELECT hex(randomblob(10))"); zOut = mprintf("out-%s", zRand); zIn = mprintf("in-%z", zRand); blob_write_to_file(pIn, zOut); zCmd = mprintf("%s %s %s", zBase, zIn, zOut); rc = fossil_system(zCmd); free(zCmd); if( rc==0 ){ if( pOut==pIn ){ blob_reset(pIn); } blob_zero(pOut); blob_read_from_file(pOut, zIn, ExtFILE); }else{ if( pOut!=pIn ){ blob_copy(pOut, pIn); } } file_delete(zOut); file_delete(zIn); free(zOut); free(zIn); return rc; } |
Changes to src/clone.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 | ** ** This file contains code used to clone a repository */ #include "config.h" #include "clone.h" #include <assert.h> /* ** COMMAND: clone ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | < > > > > | > | < > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | > | < < < < < < | < < < | < > > > > | < < | > | | > | | | | > > > > > > > > > > > > > > | | > > > > | | > > > > > > > > > > > > > > | > > > > | | > > > > > > | > > > | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** ** This file contains code used to clone a repository */ #include "config.h" #include "clone.h" #include <assert.h> /* ** If there are public BLOBs that deltas from private BLOBs, then ** undeltify the public BLOBs so that the private BLOBs may be safely ** deleted. */ void fix_private_blob_dependencies(int showWarning){ Bag toUndelta; Stmt q; int rid; /* Careful: We are about to delete all BLOB entries that are private. ** So make sure that any no public BLOBs are deltas from a private BLOB. ** Otherwise after the deletion, we won't be able to recreate the public ** BLOBs. */ db_prepare(&q, "SELECT " " rid, (SELECT uuid FROM blob WHERE rid=delta.rid)," " srcid, (SELECT uuid FROM blob WHERE rid=delta.srcid)" " FROM delta" " WHERE srcid in private AND rid NOT IN private" ); bag_init(&toUndelta); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); const char *zId = db_column_text(&q, 1); int srcid = db_column_int(&q, 2); const char *zSrc = db_column_text(&q, 3); if( showWarning ){ fossil_warning( "public artifact %S (%d) is a delta from private artifact %S (%d)", zId, rid, zSrc, srcid ); } bag_insert(&toUndelta, rid); } db_finalize(&q); while( (rid = bag_first(&toUndelta))>0 ){ content_undelta(rid); bag_remove(&toUndelta, rid); } bag_clear(&toUndelta); } /* ** Delete all private content from a repository. */ void delete_private_content(void){ fix_private_blob_dependencies(1); db_multi_exec( "DELETE FROM blob WHERE rid IN private;" "DELETE FROM delta WHERE rid IN private;" "DELETE FROM private;" "DROP TABLE IF EXISTS modreq;" ); } /* ** COMMAND: clone ** ** Usage: %fossil clone ?OPTIONS? URI ?FILENAME? ** ** Make a clone of a repository specified by URI in the local ** file named FILENAME. If FILENAME is omitted, then an appropriate ** filename is deduced from last element of the path in the URL. ** ** URI may be one of the following forms ([...] denotes optional elements): ** ** * HTTP/HTTPS protocol: ** ** http[s]://[userid[:password]@]host[:port][/path] ** ** * SSH protocol: ** ** ssh://[userid@]host[:port]/path/to/repo.fossil[?fossil=path/fossil.exe] ** ** * Filesystem: ** ** [file://]path/to/repo.fossil ** ** For ssh and filesystem, path must have an extra leading ** '/' to use an absolute path. ** ** Use %HH escapes for special characters in the userid and ** password. For example "%40" in place of "@", "%2f" in place ** of "/", and "%3a" in place of ":". ** ** Note that in Fossil (in contrast to some other DVCSes) a repository ** is distinct from a check-out. Cloning a repository is not the same thing ** as opening a repository. This command always clones the repository. This ** command might also open the repository, but only if the --no-open option ** is omitted and either the --workdir option is included or the FILENAME ** argument is omitted. Use the separate [[open]] command to open a ** repository that was previously cloned and already exists on the ** local machine. ** ** By default, the current login name is used to create the default ** admin user for the new clone. This can be overridden using ** the -A|--admin-user parameter. ** ** Options: ** -A|--admin-user USERNAME Make USERNAME the administrator ** -B|--httpauth USER:PASS Add HTTP Basic Authorization to requests ** --nested Allow opening a repository inside an opened ** check-out ** --nocompress Omit extra delta compression ** --no-open Clone only. Do not open a check-out. ** --once Don't remember the URI. ** --private Also clone private branches ** --save-http-password Remember the HTTP password without asking ** -c|--ssh-command SSH Use SSH as the "ssh" command ** --ssl-identity FILENAME Use the SSL identity if requested by the server ** --transport-command CMD Use CMD to move messages to the server and back ** -u|--unversioned Also sync unversioned content ** -v|--verbose Show more statistics in output ** --workdir DIR Also open a check-out in DIR ** --xverbose Extra debugging output ** ** See also: [[init]], [[open]] */ 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; int noCompress = find_option("nocompress",0,0)!=0; int noOpen = find_option("no-open",0,0)!=0; int allowNested = find_option("nested",0,0)!=0; /* Used by open */ const char *zRepo = 0; /* Name of the new local repository file */ const char *zWorkDir = 0; /* Open in this directory, if not zero */ /* 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("save-http-password",0,0)!=0 ){ urlFlags &= ~URL_PROMPT_PW; urlFlags |= URL_REMEMBER_PW; } if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE; if( find_option("xverbose",0,0)!=0) syncFlags |= SYNC_XVERBOSE; if( find_option("unversioned","u",0)!=0 ){ syncFlags |= SYNC_UNVERSIONED; if( syncFlags & SYNC_VERBOSE ){ syncFlags |= SYNC_UV_TRACE; } } zHttpAuth = find_option("httpauth","B",1); zDefaultUser = find_option("admin-user","A",1); zWorkDir = find_option("workdir", 0, 1); clone_ssh_find_options(); url_proxy_options(); g.zHttpCmd = find_option("transport-command",0,1); /* We should be done with options.. */ verify_all_options(); if( g.argc < 3 ){ usage("?OPTIONS? FILE-OR-URL ?NEW-REPOSITORY?"); } db_open_config(0, 0); if( g.argc==4 ){ zRepo = g.argv[3]; }else{ char *zBase = url_to_repo_basename(g.argv[2]); if( zBase==0 ){ fossil_fatal( "unable to guess a repository name from the url \"%s\".\n" "give the repository filename as an additional argument.", g.argv[2]); } zRepo = mprintf("./%s.fossil", zBase); if( zWorkDir==0 ){ zWorkDir = mprintf("./%s", zBase); } fossil_free(zBase); } if( -1 != file_size(zRepo, ExtFILE) ){ fossil_fatal("file already exists: %s", zRepo); } /* Fail before clone if open will fail because inside an open check-out */ if( zWorkDir!=0 && zWorkDir[0]!=0 && !noOpen ){ if( db_open_local_v2(0, allowNested) ){ fossil_fatal("there is already an open tree at %s", g.zLocalRoot); } } 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, zRepo); db_close(1); db_open_repository(zRepo); db_open_config(1,0); db_record_repository_filename(zRepo); 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", zRepo); }else{ db_close_config(); db_create_repository(zRepo); db_open_repository(zRepo); db_open_config(0,0); db_begin_transaction(); db_record_repository_filename(zRepo); 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); db_unset("hash-policy", 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_unprotect(PROTECT_ALL); db_set("ssl-identity", blob_str(&fn), 0); db_protect_pop(); blob_reset(&fn); } db_unprotect(PROTECT_CONFIG); db_multi_exec( "REPLACE INTO config(name,value,mtime)" " VALUES('server-code', lower(hex(randomblob(20))), now());" "DELETE FROM config WHERE name='project-code';" ); db_protect_pop(); url_enable_proxy(0); clone_ssh_db_set_options(); url_get_password_if_needed(); g.xlinkClusterOnly = 1; nErr = client_sync(syncFlags,CONFIGSET_ALL,0,0,0); g.xlinkClusterOnly = 0; verify_cancel(); db_end_transaction(0); db_close(1); if( nErr ){ file_delete(zRepo); if( g.fHttpTrace ){ fossil_fatal( "server returned an error - clone aborted\n\n%s", http_last_trace_reply() ); }else{ fossil_fatal( "server returned an error - clone aborted\n" "Rerun using --httptrace for more detail" ); } } db_open_repository(zRepo); } db_begin_transaction(); if( db_exists("SELECT 1 FROM delta WHERE srcId IN phantom") ){ fossil_fatal("there are unresolved deltas -" " the clone is probably incomplete and unusable."); } fossil_print("Rebuilding repository meta-data...\n"); rebuild_db(1, 0); if( !noCompress ){ int nDelta = 0; i64 nByte; fossil_print("Extra delta compression... "); fflush(stdout); nByte = extra_deltification(&nDelta); if( nDelta==1 ){ fossil_print("1 delta saves %,lld bytes\n", nByte); }else if( nDelta>1 ){ fossil_print("%d deltas save %,lld bytes\n", nDelta, nByte); }else{ fossil_print("none found\n"); } } db_end_transaction(0); fossil_print("Vacuuming 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_unprotect(PROTECT_ALL); db_multi_exec("VACUUM"); db_protect_pop(); 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); hash_user_password(g.zLogin); if( zWorkDir!=0 && zWorkDir[0]!=0 && !noOpen ){ Blob cmd; fossil_print("opening the new %s repository in directory %s...\n", zRepo, zWorkDir); blob_init(&cmd, 0, 0); blob_append_escaped_arg(&cmd, g.nameOfExe, 1); blob_append(&cmd, " open ", -1); blob_append_escaped_arg(&cmd, zRepo, 1); blob_append(&cmd, " --nosync --workdir ", -1); blob_append_escaped_arg(&cmd, zWorkDir, 1); if( allowNested ){ blob_append(&cmd, " --nested", -1); } fossil_system(blob_str(&cmd)); blob_reset(&cmd); } } /* ** 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 */ ){ 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_mprintf(0, "http-auth:%s", g.url.canonical); }else{ g.zHttpAuth = get_httpauth(); } }else if( g.zHttpAuth==0 && zUrl==0 ){ g.zHttpAuth = get_httpauth(); } } /* ** 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){ db_set_mprintf(obscure(zHttpAuth), 0, "http-auth:%s", g.url.canonical); } /* ** 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_unprotect(PROTECT_ALL); db_set("ssh-command", g.zSshCmd, 0); db_protect_pop(); } } /* ** WEBPAGE: download ** ** Provide a simple page that enables newbies to download the latest tarball or ** ZIP archive, and provides instructions on how to clone. */ void download_page(void){ login_check_credentials(); cgi_check_for_malice(); style_header("Download Page"); if( !g.perm.Zip ){ @ <p>Bummer. You do not have permission to download. if( g.zLogin==0 || g.zLogin[0]==0 ){ @ Maybe it would work better if you @ %z(href("%R/login"))logged in</a>. }else{ @ Contact the site administrator and ask them to give @ you "Download Zip" privileges. } }else{ const char *zDLTag = db_get("download-tag","trunk"); const char *zNm = db_get("short-project-name","download"); char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm); @ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a> zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm); @ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a> zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm); @ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a> } if( !g.perm.Clone ){ @ <p>You are not authorized to clone this repository. if( g.zLogin==0 || g.zLogin[0]==0 ){ @ Maybe you would be able to clone if you @ %z(href("%R/login"))logged in</a>. }else{ @ Contact the site administrator and ask them to give @ you "Clone" privileges in order to clone. } }else{ const char *zNm = db_get("short-project-name","clone"); @ <p>Clone the repository using this command: @ <blockquote><pre> @ fossil clone %s(g.zBaseURL) %h(zNm).fossil @ </pre></blockquote> } style_finish_page(); } |
Added src/color.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 | /* ** Copyright (c) 2007 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 select colors based on branch and ** user names. ** */ #include "config.h" #include <string.h> #include "color.h" /* ** Compute a hash on a branch or user name */ static unsigned int hash_of_name(const char *z){ unsigned int h = 0; int i; for(i=0; z[i]; i++ ){ h = (h<<11) ^ (h<<1) ^ (h>>3) ^ z[i]; } return h; } /* ** Hash a string and use the hash to determine a background color. ** ** This value returned is in static space and is overwritten with ** each subsequent call. */ char *hash_color(const char *z){ 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[3] = {0,0}; /* Color chooser parameters */ if( ix[0]==0 ){ if( skin_detail_boolean("white-foreground") ){ ix[0] = 0x50; ix[1] = 0x20; }else{ ix[0] = 0xf8; ix[1] = 0x20; } } h = hash_of_name(z); h1 = h % 6; h /= 6; h3 = h % 10; h /= 10; h4 = h % 10; h /= 10; mx = ix[0] - h3; mn = mx - h4 - ix[1]; h2 = (h%(mx - mn)) + mn; switch( h1 ){ case 0: r = mx; g = h2, b = mn; break; case 1: r = h2; g = mx, b = mn; break; case 2: r = mn; g = mx, b = h2; break; case 3: r = mn; g = h2, b = mx; break; case 4: r = h2; g = mn, b = mx; break; default: r = mx; g = mn, b = h2; break; } sqlite3_snprintf(8, zColor, "#%02x%02x%02x", r,g,b); return zColor; } /* ** Determine a color for users based on their login string. ** ** SETTING: user-color-map width=40 block-text ** ** The user-color-map setting can be used to override user color choices. ** The setting is a list of space-separated words pairs. The first word ** of each pair is a login name. The second word is an alternative name ** used by the color chooser algorithm. ** ** This list is intended to be relatively short. The idea is to only use ** this map to resolve color collisions between common users. ** ** Visit /hash-color-test?rand for a list of suggested names for the ** second word of each pair in the list. */ char *user_color(const char *zLogin){ static int once = 0; static int nMap = 0; static char **azMap = 0; static int *anMap = 0; int i; if( !once ){ char *zMap = (char*)db_get("user-color-map",0); once = 1; if( zMap && zMap[0] ){ if( !g.interp ) Th_FossilInit(0); Th_SplitList(g.interp, zMap, (int)strlen(zMap), &azMap, &anMap, &nMap); for(i=0; i<nMap; i++) azMap[i][anMap[i]] = 0; } } for(i=0; i<nMap-1; i+=2){ if( strcmp(zLogin, azMap[i])==0 ) return hash_color(azMap[i+1]); } return hash_color(zLogin); } /* ** COMMAND: test-hash-color ** ** Usage: %fossil test-hash-color TAG ... ** ** Print out the color names associated with each tag. Used for ** testing the hash_color() function. */ void test_hash_color(void){ int i; for(i=2; i<g.argc; i++){ fossil_print("%20s: %s\n", g.argv[i], hash_color(g.argv[i])); } } /* ** WEBPAGE: hash-color-test ** ** Print out the color names associated with each tag. Used for ** testing the hash_color() function. */ void test_hash_color_page(void){ const char *zBr; char zNm[10]; int i, cnt; login_check_credentials(); if( P("rand")!=0 ){ int j; for(i=0; i<10; i++){ sqlite3_uint64 u; char zClr[10]; sqlite3_randomness(sizeof(u), &u); cnt = 3+(u%2); u /= 2; for(j=0; j<cnt; j++){ zClr[j] = 'a' + (u%26); u /= 26; } zClr[j] = 0; sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); cgi_replace_parameter(fossil_strdup(zNm), fossil_strdup(zClr)); } } style_set_current_feature("test"); 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));'> @ %h(zBr) - hash 0x%x(hash_of_name(zBr)) - color %s(hash_color(zBr)) - @ Omnes nos quasi oves erravimus unusquisque in viam @ suam declinavit.</p> cnt++; } } if( cnt ){ @ <hr> } @ <form method="POST"> @ <p>Enter candidate branch names below and see them displayed in their @ default background colors above.</p> for(i=0; i<10; i++){ sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); zBr = P(zNm); @ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br> } @ <input type="submit" value="Submit"> @ <input type="submit" name="rand" value="Random"> @ </form> style_finish_page(); } |
Changes to src/comformat.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2007 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".) | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < < | | > > > > | | > | | > > > > > > > > > > > > > > | | > | > > > > > > > > > > > > > > > | | | < < < > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > | > > > | 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 | /* ** Copyright (c) 2007 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 format and print comments or other ** text on a TTY. */ #include "config.h" #include "comformat.h" #include <assert.h> #if INTERFACE #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */ #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. */ #define COMMENT_PRINT_UNSET (-1) /* Not initialized. */ #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 ){ struct TerminalSize ts; if ( !terminal_get_size(&ts) ){ return 0; } if( ts.nColumns ){ *pMaxChars = ts.nColumns - indent; return 1; }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; } } /* ** This function checks the current line being printed against the original ** comment text. Upon matching, it updates the provided character and line ** counts, if applicable. The caller needs to emit a new line, if desired. */ 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 ){ 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 *distUTF8 /* [out] Distance to next space in UTF-8 sequences. */ ){ int nextIndex = index + 1; int fNonASCII=0; for(;;){ char c = zLine[nextIndex]; if( (c&0x80)==0x80 ) fNonASCII=1; if( c==0 || fossil_isspace(c) ){ if( distUTF8 ){ if( fNonASCII!=0 ){ *distUTF8 = strlen_utf8(&zLine[index], nextIndex-index); }else{ *distUTF8 = nextIndex-index; } } return nextIndex; } nextIndex++; } return 0; /* NOT REACHED */ } /* ** Count the number of UTF-8 sequences in a string. Incomplete, ill-formed and ** overlong sequences are counted as one sequence. The invalid lead bytes 0xC0 ** to 0xC1 and 0xF5 to 0xF7 are allowed to initiate (ill-formed) 2- and 4-byte ** sequences, respectively, the other invalid lead bytes 0xF8 to 0xFF are ** treated as invalid 1-byte sequences (as lone trail bytes). ** Combining characters and East Asian Wide and Fullwidth characters are counted ** as one, so this function does not calculate the effective "display width". */ int strlen_utf8(const char *zString, int lengthBytes){ int i; /* Counted bytes. */ int lengthUTF8; /* Counted UTF-8 sequences. */ #if 0 assert( lengthBytes>=0 ); #endif for(i=0, lengthUTF8=0; i<lengthBytes; i++, lengthUTF8++){ char c = zString[i]; int cchUTF8=1; /* Code units consumed. */ int maxUTF8=1; /* Expected sequence length. */ if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */ else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */ else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */ while( cchUTF8<maxUTF8 && i<lengthBytes-1 && (zString[i+1]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ cchUTF8++; i++; } } return lengthUTF8; } /* ** This function is called when printing a logical comment line to calculate ** the necessary indenting. The caller needs to emit the indenting spaces. */ static void comment_calc_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( 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, i; char zBuf[400]; int iBuf=0; /* Output buffer and counter. */ int cchUTF8, maxUTF8; /* Helper variables to count UTF-8 sequences. */ if( !zLine ) return; if( lineChars<=0 ) return; #if 0 assert( indent<sizeof(zBuf)-5 ); /* See following comments to explain */ assert( origIndent<sizeof(zBuf)-5 ); /* these limits. */ #endif if( indent>(int)sizeof(zBuf)-6 ){ /* Limit initial indent to fit output buffer. */ indent = sizeof(zBuf)-6; } comment_calc_indent(zLine, indent, trimCrLf, trimSpace, &index); if( indent>0 ){ for(i=0; i<indent; i++){ zBuf[iBuf++] = ' '; } } if( origIndent>(int)sizeof(zBuf)-6 ){ /* Limit line indent to fit output buffer. */ origIndent = sizeof(zBuf)-6; } maxChars = lineChars; for(;;){ int useChars = 1; char c = zLine[index]; /* Flush the output buffer if there's no space left for at least one more ** (potentially 4-byte) UTF-8 sequence, one level of indentation spaces, ** a new line, and a terminating NULL. */ if( iBuf>(int)sizeof(zBuf)-origIndent-6 ){ zBuf[iBuf]=0; iBuf=0; fossil_print("%s", zBuf); } if( c==0 ){ break; }else{ if( origBreak && index>0 ){ const char *zCurrent = &zLine[index]; if( comment_check_orig(zOrigText, zCurrent, &charCnt, &lineCnt) ){ zBuf[iBuf++] = '\n'; comment_calc_indent(zLine, origIndent, trimCrLf, trimSpace, &index); for( i=0; i<origIndent; i++ ){ zBuf[iBuf++] = ' '; } maxChars = lineChars; } } index++; } if( c=='\n' ){ lineCnt++; charCnt = 0; useChars = 0; }else if( c=='\t' ){ int distUTF8; int nextIndex = comment_next_space(zLine, index, &distUTF8); if( nextIndex<=0 || distUTF8>maxChars ){ break; } charCnt++; useChars = COMMENT_TAB_WIDTH; if( maxChars<useChars ){ zBuf[iBuf++] = ' '; break; } }else if( wordBreak && fossil_isspace(c) ){ int distUTF8; int nextIndex = comment_next_space(zLine, index, &distUTF8); if( nextIndex<=0 || distUTF8>=maxChars ){ break; } charCnt++; }else{ charCnt++; } assert( c!='\n' || charCnt==0 ); zBuf[iBuf++] = c; /* Skip over UTF-8 sequences, see comment on strlen_utf8() for details. */ cchUTF8=1; /* Code units consumed. */ maxUTF8=1; /* Expected sequence length. */ if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */ else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */ else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */ while( cchUTF8<maxUTF8 && (zLine[index]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ cchUTF8++; zBuf[iBuf++] = zLine[index++]; } maxChars -= useChars; if( maxChars<=0 ) break; if( c=='\n' ) break; } if( charCnt>0 ){ zBuf[iBuf++] = '\n'; lineCnt++; } /* Flush the remaining output buffer. */ if( iBuf>0 ){ zBuf[iBuf]=0; iBuf=0; fossil_print("%s", zBuf); } 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, kc; int doIndent = 0; char *zBuf; char zBuffer[400]; int lineCnt = 0; int cchUTF8, maxUTF8; /* Helper variables to count UTF-8 sequences. */ if( width<0 ){ comment_set_maxchars(indent, &maxChars); } if( zText==0 ) zText = "(NULL)"; if( maxChars<=0 ){ maxChars = strlen(zText); } /* Ensure the buffer can hold the longest-possible UTF-8 sequences. */ if( maxChars >= ((int)sizeof(zBuffer)/4-1) ){ zBuf = fossil_malloc(maxChars*4+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=kc=0; zText[i] && kc<maxChars; i++){ char c = zText[i]; kc++; /* Count complete UTF-8 sequences. */ /* Skip over UTF-8 sequences, see comment on strlen_utf8() for details. */ cchUTF8=1; /* Code units consumed. */ maxUTF8=1; /* Expected sequence length. */ if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */ else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */ else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */ if( maxUTF8>1 ){ zBuf[k++] = c; while( cchUTF8<maxUTF8 && (zText[i+1]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ cchUTF8++; zBuf[k++] = zText[++i]; } } else if( fossil_isspace(c) ){ si = i; sk = k; if( k==0 || zBuf[k-1]!=' ' ){ zBuf[k++] = ' '; } }else{ zBuf[k] = c; if( c=='-' && k>0 && fossil_isalpha(zBuf[k-1]) ){ si = i+1; sk = k+1; } k++; } } 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; } /* ** Return the "COMMENT_PRINT_*" flags specified by the following sources, ** evaluated in the following cascading order: ** ** 1. The global --comfmtflags (alias --comment-format) command-line option. ** 2. The local (per-repository) "comment-format" setting. ** 3. The global (all-repositories) "comment-format" setting. ** 4. The default value COMMENT_PRINT_DEFAULT. */ int get_comment_format(){ int comFmtFlags; /* We must cache this result, else running the timeline can end up ** querying the comment-format setting from the global db once per ** timeline entry, which brings it to a crawl if that db is ** network-mounted. Discussed in: ** https://fossil-scm.org/forum/forumpost/9aaefe4e536e01bf */ /* The global command-line option is present, or the value has been cached. */ if( g.comFmtFlags!=COMMENT_PRINT_UNSET ){ return g.comFmtFlags; } /* Load the local (per-repository) or global (all-repositories) value, and use ** g.comFmtFlags as a cache. */ comFmtFlags = db_get_int("comment-format", COMMENT_PRINT_UNSET); if( comFmtFlags!=COMMENT_PRINT_UNSET ){ g.comFmtFlags = comFmtFlags; return comFmtFlags; } /* Fallback to the default value. */ g.comFmtFlags = COMMENT_PRINT_DEFAULT; return g.comFmtFlags; } /* ** ** 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, ExtFILE); zText = mprintf("%s", blob_str(&fileData)); blob_reset(&fileData); if( zOrigText ){ blob_read_from_file(&fileData, zOrigText, ExtFILE); 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.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** A common header file used by all modules. */ /* ** System header files used by all modules */ #include <unistd.h> #include <stdio.h> #include <stdlib.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 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 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** A common header file used by all modules. */ /* The following macros are necessary for large-file support under ** 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 /* Enable the hardened SHA1 implemenation by default */ #ifndef FOSSIL_HARDENED_SHA1 # define FOSSIL_HARDENED_SHA1 1 #endif #ifndef _RC_COMPILE_ /* ** System header files used by all modules */ #include <unistd.h> #include <stdio.h> #include <stdlib.h> /* #include <ctype.h> // do not use - causes problems */ #include <string.h> #include <stdarg.h> #include <assert.h> #endif #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) # if defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) typedef int socklen_t; # endif # ifndef _WIN32 # define _WIN32 # endif # include <io.h> # include <fcntl.h> # undef popen # define popen _popen # undef pclose # define pclose _pclose #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(__clang__) # if !defined(COMPILER_VERSION) # if defined(__clang_version__) # define COMPILER_VERSION __clang_version__ # endif # endif # if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION) # define COMPILER_NAME "clang-" COMPILER_VERSION # else # define COMPILER_NAME "clang" # 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(__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 # elif defined(_WIN32) # define COMPILER_NAME "win32" # else # define COMPILER_NAME "unknown" # endif #endif #if !defined(_RC_COMPILE_) && !defined(SQLITE_AMALGAMATION) /* ** MSVC does not include the "stdint.h" header file until 2010. */ #if defined(_MSC_VER) && _MSC_VER<1600 typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #else # include <stdint.h> #endif #if USE_SEE && !defined(SQLITE_HAS_CODEC) # define SQLITE_HAS_CODEC #endif #include "sqlite3.h" /* ** On Solaris, getpass() will only return up to 8 characters. getpassphrase() returns up to 257. */ #if HAVE_GETPASSPHRASE #define getpass getpassphrase #endif /* ** Typedef for a 64-bit integer */ typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; /* ** 8-bit types */ typedef unsigned char u8; typedef signed char i8; /* In the timeline, check-in messages are truncated at the first space ** that is more than MX_CKIN_MSG from the beginning, or at the first ** paragraph break that is more than MN_CKIN_MSG from the beginning. */ #define MN_CKIN_MSG 100 #define MX_CKIN_MSG 300 /* ** 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)) #elif !defined(__GNUC__) /* Works for compilers other than LLVM */ # define FOSSIL_INT_TO_PTR(X) ((void*)&((char*)0)[X]) # define FOSSIL_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #else /* Generates a warning - but it always works */ # define FOSSIL_INT_TO_PTR(X) ((void*)(X)) # define FOSSIL_PTR_TO_INT(X) ((int)(X)) #endif /* ** A marker for functions that never return. */ #if defined(__GNUC__) || defined(__clang__) # define NORETURN __attribute__((__noreturn__)) # define NULL_SENTINEL __attribute__((sentinel)) #elif defined(_MSC_VER) && (_MSC_VER >= 1310) # define NORETURN __declspec(noreturn) # define NULL_SENTINEL #else # define NORETURN # define NULL_SENTINEL #endif /* ** Number of elements in an array */ #define count(X) (int)(sizeof(X)/sizeof(X[0])) #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) /* ** The pledge() interface is currently only available on OpenBSD 5.9 ** and later. Make calls to fossil_pledge() no-ops on all platforms ** that omit the HAVE_PLEDGE configuration parameter. */ #if !defined(HAVE_PLEDGE) # define fossil_pledge(A) #endif #endif /* _RC_COMPILE_ */ |
Changes to src/configure.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2008 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".) | | > | > | | | | | | > > > > > > | > > > > > > | | > | > | | | > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | | > | < > > > > < < < < < < < < < < < < < | < | < | < < < < | < | < < | < < < < < < > | | | | | < > | | | < < | < < < < | < < | < < < < < < | | < < < < < < < < | < < < < < < < < < | < < < < < < < < < < > > > | < < < > | | < < < < < < < > | < > > > > > | < < | < < < < | < | < < < < < > > | < | | > | | < < < < | < < < | < < < < < | | > | | > | | | > | > < | | | | < < < < < < < < < < < < | < < < | | | < | | | > > > | | | | > | | > | | > | > > | > > > > > | < > > | > > > > > > > > | | > | | > > > > | | > | > > > | > | > > > | | < < < < < < | < < | | > > | > > > > | | | | > > | > > | | > > > > > > > > > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Copyright (c) 2008 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 manage repository configurations. ** ** By "repository configure" we mean the local state of a repository ** distinct from the versioned files. */ #include "config.h" #include "configure.h" #include <assert.h> #if INTERFACE /* ** Configuration transfers occur in groups. These are the allowed ** groupings: */ #define CONFIGSET_CSS 0x000001 /* Style sheet only */ #define CONFIGSET_SKIN 0x000002 /* WWW interface appearance */ #define CONFIGSET_TKT 0x000004 /* Ticket configuration */ #define CONFIGSET_PROJ 0x000008 /* Project name */ #define CONFIGSET_SHUN 0x000010 /* Shun settings */ #define CONFIGSET_USER 0x000020 /* The USER table */ #define CONFIGSET_ADDR 0x000040 /* The CONCEALED table */ #define CONFIGSET_XFER 0x000080 /* Transfer configuration */ #define CONFIGSET_ALIAS 0x000100 /* URL Aliases */ #define CONFIGSET_SCRIBER 0x000200 /* Email subscribers */ #define CONFIGSET_IWIKI 0x000400 /* Interwiki codes */ #define CONFIGSET_ALL 0x0007ff /* Everything */ #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */ /* ** 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 */ int groupMask; /* Mask for that configuration set */ const char *zHelp; /* What it does */ } aGroupName[] = { { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" }, { "/project", CONFIGSET_PROJ, "Project name and description" }, { "/skin", CONFIGSET_SKIN | CONFIGSET_CSS, "Web interface appearance settings" }, { "/css", CONFIGSET_CSS, "Style sheet" }, { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" }, { "/ticket", CONFIGSET_TKT, "Ticket setup", }, { "/user", CONFIGSET_USER, "Users and privilege settings" }, { "/xfer", CONFIGSET_XFER, "Transfer setup", }, { "/alias", CONFIGSET_ALIAS, "URL Aliases", }, { "/subscriber", CONFIGSET_SCRIBER, "Email notification subscriber list" }, { "/interwiki", CONFIGSET_IWIKI, "Inter-wiki link prefixes" }, { "/all", CONFIGSET_ALL, "All of the above" }, }; /* ** The following is a list of settings that we are willing to ** transfer. ** ** Setting names that begin with an alphabetic characters refer to ** single entries in the CONFIG table. Setting names that begin with ** "@" are for special processing. */ 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 }, { "mainmenu", CONFIGSET_SKIN }, { "footer", CONFIGSET_SKIN }, { "details", CONFIGSET_SKIN }, { "js", CONFIGSET_SKIN }, { "default-skin", CONFIGSET_SKIN }, { "logo-mimetype", CONFIGSET_SKIN }, { "logo-image", CONFIGSET_SKIN }, { "background-mimetype", CONFIGSET_SKIN }, { "background-image", CONFIGSET_SKIN }, { "icon-mimetype", CONFIGSET_SKIN }, { "icon-image", CONFIGSET_SKIN }, { "timeline-block-markup", CONFIGSET_SKIN }, { "timeline-date-format", CONFIGSET_SKIN }, { "timeline-default-style", CONFIGSET_SKIN }, { "timeline-dwelltime", CONFIGSET_SKIN }, { "timeline-closetime", CONFIGSET_SKIN }, { "timeline-max-comment", CONFIGSET_SKIN }, { "timeline-plaintext", CONFIGSET_SKIN }, { "timeline-truncate-at-blank", CONFIGSET_SKIN }, { "timeline-tslink-info", CONFIGSET_SKIN }, { "timeline-utc", CONFIGSET_SKIN }, { "adunit", CONFIGSET_SKIN }, { "adunit-omit-if-admin", CONFIGSET_SKIN }, { "adunit-omit-if-user", CONFIGSET_SKIN }, { "default-csp", CONFIGSET_SKIN }, { "sitemap-extra", CONFIGSET_SKIN }, { "safe-html", 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 }, { "crlf-glob", CONFIGSET_PROJ }, { "crnl-glob", CONFIGSET_PROJ }, { "encoding-glob", CONFIGSET_PROJ }, { "empty-dirs", CONFIGSET_PROJ }, { "dotfiles", CONFIGSET_PROJ }, { "parent-project-code", CONFIGSET_PROJ }, { "parent-project-name", CONFIGSET_PROJ }, { "hash-policy", CONFIGSET_PROJ }, { "comment-format", CONFIGSET_PROJ }, { "mimetypes", CONFIGSET_PROJ }, { "forbid-delta-manifests", CONFIGSET_PROJ }, { "mv-rm-files", 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 }, { "ticket-reportlist", CONFIGSET_TKT }, { "ticket-report-template", CONFIGSET_TKT }, { "ticket-key-template", CONFIGSET_TKT }, { "ticket-title-expr", CONFIGSET_TKT }, { "ticket-closed-expr", CONFIGSET_TKT }, { "@reportfmt", CONFIGSET_TKT }, { "@user", CONFIGSET_USER }, { "user-color-map", CONFIGSET_USER }, { "@concealed", CONFIGSET_ADDR }, { "@shun", CONFIGSET_SHUN }, { "@alias", CONFIGSET_ALIAS }, { "@subscriber", CONFIGSET_SCRIBER }, { "@interwiki", CONFIGSET_IWIKI }, { "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. */ const char *configure_first_name(int iMask){ iConfig = 0; return configure_next_name(iMask); } const char *configure_next_name(int iMask){ if( iConfig==0 && (iMask & CONFIGSET_ALL)==CONFIGSET_ALL ){ iConfig = count(aGroupName); return "/all"; } while( iConfig<count(aGroupName)-1 ){ if( aGroupName[iConfig].groupMask & iMask ){ return aGroupName[iConfig++].zName; }else{ iConfig++; } } return 0; } /* ** Return a pointer to a string that contains the RHS of an IN operator ** that will select CONFIG table names that are part of the configuration ** that matches iMatch. */ 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 granted 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|CONFIGSET_SCRIBER); } if( !g.perm.RdAddr ){ m &= ~CONFIGSET_ADDR; } return m; } } if( strncmp(zName, "walias:/", 8)==0 ){ return CONFIGSET_ALIAS; } if( strncmp(zName, "interwiki:", 10)==0 ){ return CONFIGSET_IWIKI; } return 0; } /* ** A mask of all configuration tables that have been reset already. */ static int configHasBeenReset = 0; /* ** 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 ** * An integer literal (no floating point) ** * NULL */ static int safeSql(const char *z){ int i; if( z==0 || z[0]==0 ) return 0; if( (z[0]=='x' || z[0]=='X') && z[1]=='\'' ) z++; if( z[0]=='\'' ){ for(i=1; z[i]; i++){ if( z[i]=='\'' ){ i++; if( z[i]=='\'' ){ continue; } return z[i]==0; } } return 0; }else{ char c; for(i=0; (c = z[i])!=0; i++){ if( !fossil_isalnum(c) ) return 0; } } return 1; } /* ** Return true if z[] consists of nothing but digits */ static int safeInt(const char *z){ int i; if( z==0 || z[0]==0 ) return 0; for(i=0; fossil_isdigit(z[i]); i++){} return z[i]==0; } /* ** Process a single "config" card received from the other side of a ** sync session. ** ** Mask consists of one or more CONFIGSET_* values ORed together, to ** designate what types of configuration we are allowed to receive. ** ** NEW FORMAT: ** ** zName is one of: ** ** "/config", "/user", "/shun", "/reportfmt", "/concealed", ** "/subscriber", ** ** zName indicates the table that holds the configuration information being ** transferred. pContent is a string that consist of alternating Fossil ** and SQL tokens. The First token is a timestamp in seconds since 1970. ** The second token is a primary key for the table identified by zName. If ** The entry with the corresponding primary key exists and has a more recent ** mtime, then nothing happens. If the entry does not exist or if it has ** an older mtime, then the content described by subsequent token pairs is ** inserted. The first element of each token pair is a column name and ** the second is its value. ** ** In overview, we have: ** ** NAME CONTENT ** ------- ----------------------------------------------------------- ** /config $MTIME $NAME value $VALUE ** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE ** /shun $MTIME $UUID scom $VALUE ** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE jx $JSON ** /concealed $MTIME $HASH content $VALUE ** /subscriber $SMTIME $SEMAIL suname $V ... */ void configure_receive(const char *zName, Blob *pContent, int groupMask){ int checkMask; /* Masks for which we must first check existance of tables */ checkMask = CONFIGSET_SCRIBER; if( zName[0]=='/' ){ /* The new format */ char *azToken[24]; int nToken = 0; int ii, jj; int thisMask; Blob name, value, sql; static const struct receiveType { const char *zName; /* Configuration key for this table */ const char *zPrimKey; /* Primary key column */ int nField; /* Number of data fields */ const char *azField[6]; /* Names of the data fields */ } aType[] = { { "/config", "name", 1, { "value", 0,0,0,0,0 } }, { "@user", "login", 5, { "pw","cap","info","photo","jx",0} }, { "@shun", "uuid", 1, { "scom", 0,0,0,0,0} }, { "@reportfmt", "title", 4, { "owner","cols","sqlcode","jx",0,0}}, { "@concealed", "hash", 1, { "content", 0,0,0,0,0 } }, { "@subscriber","semail",6, { "suname","sdigest","sdonotcall","ssub","sctime","smip"} }, }; /* Locate the receiveType in aType[ii] */ for(ii=0; ii<count(aType); ii++){ if( fossil_strcmp(&aType[ii].zName[1],&zName[1])==0 ) break; } if( ii>=count(aType) ) return; while( blob_token(pContent, &name) && blob_sqltoken(pContent, &value) ){ char *z = blob_terminate(&name); if( !safeSql(z) ) return; if( nToken>0 ){ for(jj=0; jj<aType[ii].nField; jj++){ if( fossil_strcmp(aType[ii].azField[jj], z)==0 ) break; } if( jj>=aType[ii].nField ) continue; }else{ if( !safeInt(z) ) return; } azToken[nToken++] = z; azToken[nToken++] = z = blob_terminate(&value); if( !safeSql(z) ) return; if( nToken>=count(azToken)-1 ) break; } if( nToken<2 ) return; if( aType[ii].zName[0]=='/' ){ thisMask = configure_is_exportable(azToken[1]); }else{ thisMask = configure_is_exportable(aType[ii].zName); } if( (thisMask & groupMask)==0 ) return; if( (thisMask & checkMask)!=0 ){ if( (thisMask & CONFIGSET_SCRIBER)!=0 ){ alert_schema(1); } checkMask &= ~thisMask; } 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); if( fossil_stricmp(zName,"/subscriber")==0 ) alert_schema(0); 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_protect_only(PROTECT_SENSITIVE); /* Make sure tables have the "jx" column */ if( strcmp(&zName[1],"user")==0 ){ user_update_user_table(); }else if( strcmp(&zName[1],"reportfmt")==0 ){ report_update_reportfmt_table(); } 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)); } db_protect_pop(); blob_reset(&sql); rebuildMask |= thisMask; } } /* ** Process a file full of "config" cards. */ void configure_receive_all(Blob *pIn, int groupMask){ Blob line; int nToken; int size; Blob aToken[4]; configHasBeenReset = 0; while( blob_line(pIn, &line) ){ if( blob_buffer(&line)[0]=='#' ) continue; nToken = blob_tokenize(&line, aToken, count(aToken)); if( blob_eq(&aToken[0],"config") && nToken==3 && blob_is_int(&aToken[2], &size) ){ const char *zName = blob_str(&aToken[1]); Blob content; blob_zero(&content); blob_extract(pIn, size, &content); g.perm.Admin = g.perm.RdAddr = 1; configure_receive(zName, &content, groupMask); blob_reset(&content); blob_seek(pIn, 1, BLOB_SEEK_CUR); } } } /* ** Send "config" cards using the new format for all elements of a group ** that have recently changed. ** ** Output goes into pOut. The groupMask identifies the group(s) to be sent. ** Send only entries whose timestamp is later than or equal to iStart. ** ** Return the number of cards sent. */ int configure_send_group( Blob *pOut, /* Write output here */ int groupMask, /* Mask of groups to be send */ sqlite3_int64 iStart /* Only write values changed since this time */ ){ Stmt q; Blob rec; int ii; int nCard = 0; blob_zero(&rec); if( groupMask & CONFIGSET_SHUN ){ db_prepare(&q, "SELECT mtime, quote(uuid), quote(scom) FROM shun" " WHERE mtime>=%lld", iStart); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&rec,"%s %s scom %s", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2) ); blob_appendf(pOut, "config /shun %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } if( groupMask & CONFIGSET_USER ){ if( db_table_has_column("repository","user","jx") ){ db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap)," " quote(info), quote(photo), quote(jx) FROM user" " WHERE mtime>=%lld", iStart); }else{ db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap)," " quote(info), quote(photo), 'NULL' FROM user" " WHERE mtime>=%lld", iStart); } while( db_step(&q)==SQLITE_ROW ){ const char *z; blob_appendf(&rec,"%s %s", db_column_text(&q,0), db_column_text(&q,1)); z = db_column_text(&q,2); if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," pw %s", z); z = db_column_text(&q,3); if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," cap %s", z); z = db_column_text(&q,4); if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," info %s", z); z = db_column_text(&q,5); if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," photo %s", z); z = db_column_text(&q,6); if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," jx %s", z); blob_appendf(pOut, "config /user %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } if( groupMask & CONFIGSET_TKT ){ if( db_table_has_column("repository","reportfmt","jx") ){ db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols)," " quote(sqlcode), quote(jx) FROM reportfmt" " WHERE mtime>=%lld", iStart); }else{ db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols)," " quote(sqlcode), 'NULL' FROM reportfmt" " WHERE mtime>=%lld", iStart); } while( db_step(&q)==SQLITE_ROW ){ const char *z; blob_appendf(&rec,"%s %s", db_column_text(&q,0), db_column_text(&q,1)); z = db_column_text(&q,2); if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," owner %s", z); z = db_column_text(&q,3); if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," cols %s", z); z = db_column_text(&q,4); if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," sqlcode %s", z); z = db_column_text(&q,5); if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," jx %s", z); blob_appendf(pOut, "config /reportfmt %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } if( groupMask & CONFIGSET_ADDR ){ db_prepare(&q, "SELECT mtime, quote(hash), quote(content) FROM concealed" " WHERE mtime>=%lld", iStart); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&rec,"%s %s content %s", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2) ); blob_appendf(pOut, "config /concealed %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } if( groupMask & CONFIGSET_ALIAS ){ db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" " WHERE name GLOB 'walias:/*' AND mtime>=%lld", iStart); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&rec,"%s %s value %s", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2) ); blob_appendf(pOut, "config /config %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } if( groupMask & CONFIGSET_IWIKI ){ db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" " WHERE name GLOB 'interwiki:*' AND mtime>=%lld", iStart); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&rec,"%s %s value %s", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2) ); blob_appendf(pOut, "config /config %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } if( (groupMask & CONFIGSET_SCRIBER)!=0 && db_table_exists("repository","subscriber") ){ db_prepare(&q, "SELECT mtime, quote(semail)," " quote(suname), quote(sdigest)," " quote(sdonotcall), quote(ssub)," " quote(sctime), quote(smip)" " FROM subscriber WHERE sverified" " AND mtime>=%lld", iStart); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&rec, "%lld %s suname %s sdigest %s sdonotcall %s ssub %s" " sctime %s smip %s", db_column_int64(&q, 0), /* mtime */ db_column_text(&q, 1), /* semail (PK) */ db_column_text(&q, 2), /* suname */ db_column_text(&q, 3), /* sdigest */ db_column_text(&q, 4), /* sdonotcall */ db_column_text(&q, 5), /* ssub */ db_column_text(&q, 6), /* sctime */ db_column_text(&q, 7) /* smip */ ); blob_appendf(pOut, "config /subscriber %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" " WHERE name=:name AND mtime>=%lld", iStart); for(ii=0; ii<count(aConfig); ii++){ if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){ db_bind_text(&q, ":name", aConfig[ii].zName); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&rec,"%s %s value %s", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2) ); blob_appendf(pOut, "config /config %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_reset(&q); } } db_finalize(&q); return nCard; } /* ** Identify a configuration group by name. Return its mask. ** Throw an error if no match. */ int configure_name_to_mask(const char *z, int notFoundIsFatal){ int i; int n = strlen(z); for(i=0; i<count(aGroupName); i++){ if( strncmp(z, &aGroupName[i].zName[1], n)==0 ){ return aGroupName[i].groupMask; } } if( notFoundIsFatal ){ fossil_print("Available configuration areas:\n"); for(i=0; i<count(aGroupName); i++){ fossil_print(" %-13s %s\n", &aGroupName[i].zName[1], aGroupName[i].zHelp); } fossil_fatal("no such configuration area: \"%s\"", z); } return 0; } /* ** Write SQL text into file zFilename that will restore the configuration ** area identified by mask to its current state from any other state. */ static void export_config( int groupMask, /* Mask indicating which configuration to export */ const char *zMask, /* Name of the configuration */ sqlite3_int64 iStart, /* Start date */ const char *zFilename /* Write into this file */ ){ Blob out; blob_zero(&out); blob_appendf(&out, "# The \"%s\" configuration exported from\n" "# repository \"%s\"\n" "# on %s\n", zMask, g.zRepositoryName, db_text(0, "SELECT datetime('now')") ); configure_send_group(&out, groupMask, iStart); blob_write_to_file(&out, zFilename); blob_reset(&out); } /* ** COMMAND: configuration* ** ** Usage: %fossil configuration METHOD ... ?OPTIONS? ** ** Where METHOD is one of: export import merge pull push reset. ** ** > fossil configuration export AREA FILENAME ** ** Write to FILENAME exported configuration information for AREA. ** AREA can be one of: ** ** all email interwiki project shun skin ** ticket user alias subscriber ** ** > fossil configuration import FILENAME ** ** Read a configuration from FILENAME, overwriting the current ** configuration. ** ** > fossil configuration merge FILENAME ** ** Read a configuration from FILENAME and merge its values into ** the current configuration. Existing values take priority over ** values read from FILENAME. ** ** > fossil configuration pull AREA ?URL? ** ** Pull and install the configuration from a different server ** identified by URL. If no URL is specified, then the default ** server is used. Use the --overwrite flag to completely ** replace local settings with content received from URL. ** ** > 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. ** ** > fossil configuration reset AREA ** ** Restore the configuration to the default. AREA as above. ** ** > fossil configuration sync AREA ?URL? ** ** Synchronize configuration changes in the local repository with ** the remote repository at URL. ** ** Options: ** -R|--repository REPO Affect repository REPO with changes ** ** See also: [[settings]], [[unset]] */ void configuration_cmd(void){ int n; const char *zMethod; db_find_and_open_repository(0, 0); db_open_config(0, 0); if( g.argc<3 ){ usage("SUBCOMMAND ..."); } zMethod = g.argv[2]; n = strlen(zMethod); if( strncmp(zMethod, "export", n)==0 ){ int mask; const char *zSince = find_option("since",0,1); sqlite3_int64 iStart; if( g.argc!=5 ){ usage("export AREA FILENAME"); } mask = configure_name_to_mask(g.argv[3], 1); if( zSince ){ iStart = db_multi_exec( "SELECT coalesce(strftime('%%s',%Q),strftime('%%s','now',%Q))+0", zSince, zSince ); }else{ iStart = 0; } export_config(mask, g.argv[3], iStart, g.argv[4]); }else if( strncmp(zMethod, "import", n)==0 || strncmp(zMethod, "merge", n)==0 ){ Blob in; int groupMask; if( g.argc!=4 ) usage(mprintf("%s FILENAME",zMethod)); blob_read_from_file(&in, g.argv[3], ExtFILE); db_begin_transaction(); if( zMethod[0]=='i' ){ groupMask = CONFIGSET_ALL | CONFIGSET_OVERWRITE; }else{ groupMask = CONFIGSET_ALL; } db_unprotect(PROTECT_USER); configure_receive_all(&in, groupMask); db_protect_pop(); db_end_transaction(0); }else if( strncmp(zMethod, "pull", n)==0 || strncmp(zMethod, "push", n)==0 || strncmp(zMethod, "sync", n)==0 ){ int mask; const char *zServer = 0; int overwriteFlag = 0; if( strncmp(zMethod,"pull",n)==0 ){ overwriteFlag = find_option("overwrite",0,0)!=0; } url_proxy_options(); if( g.argc!=4 && g.argc!=5 ){ 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|URL_USE_CONFIG); if( g.url.protocol==0 ) fossil_fatal("no server URL specified"); user_select(); url_enable_proxy("via proxy: "); if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE; if( strncmp(zMethod, "push", n)==0 ){ client_sync(0,0,(unsigned)mask,0,0); }else if( strncmp(zMethod, "pull", n)==0 ){ if( overwriteFlag ) db_unprotect(PROTECT_USER); client_sync(0,(unsigned)mask,0,0,0); if( overwriteFlag ) db_protect_pop(); }else{ client_sync(0,(unsigned)mask,(unsigned)mask,0,0); } }else if( strncmp(zMethod, "reset", n)==0 ){ int mask, i; char *zBackup; if( g.argc!=4 ) usage("reset AREA"); mask = configure_name_to_mask(g.argv[3], 1); zBackup = db_text(0, "SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')"); db_begin_transaction(); export_config(mask, g.argv[3], 0, zBackup); for(i=0; i<count(aConfig); i++){ const char *zName = aConfig[i].zName; if( (aConfig[i].groupMask & mask)==0 ) continue; if( zName[0]!='@' ){ db_unprotect(PROTECT_CONFIG); db_multi_exec("DELETE FROM config WHERE name=%Q", zName); db_protect_pop(); }else if( fossil_strcmp(zName,"@user")==0 ){ db_unprotect(PROTECT_USER); db_multi_exec("DELETE FROM user"); db_protect_pop(); 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,"@subscriber")==0 ){ if( db_table_exists("repository","subscriber") ){ db_multi_exec("DELETE FROM subscriber"); } }else if( fossil_strcmp(zName,"@forum")==0 ){ if( db_table_exists("repository","forumpost") ){ db_multi_exec("DELETE FROM forumpost"); db_multi_exec("DELETE FROM forumthread"); } }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, ExtFILE); }else if( zBlob ){ blob_read_from_file(&x, zBlob, ExtFILE); }else{ blob_init(&x,g.argv[3],-1); } db_unprotect(PROTECT_CONFIG); 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); db_protect_pop(); blob_reset(&x); } |
Changes to src/content.c.
1 2 3 4 5 6 | /* ** 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".) | | < < < < < < < < < | < < > | > < | | > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > | | > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | < < < > | | < | | > | | 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 | /* ** 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/ ** ******************************************************************************* ** ** Procedures store and retrieve records from the repository */ #include "config.h" #include "content.h" #include <assert.h> /* ** The artifact retrieval cache */ static struct { i64 szTotal; /* Total size of all entries in the cache */ int n; /* Current number of cache entries */ int nAlloc; /* Number of slots allocated in a[] */ int nextAge; /* Age counter for implementing LRU */ struct cacheLine { /* One instance of this for each cache entry */ int rid; /* Artifact id */ int age; /* Age. Newer is larger */ Blob content; /* Content of the artifact */ } *a; /* The positive cache */ Bag inCache; /* Set of artifacts currently in cache */ /* ** The missing artifact cache. ** ** Artifacts whose record ID are in missingCache cannot be retrieved ** either because they are phantoms or because they are a delta that ** depends on a phantom. Artifacts whose content we are certain is ** available are in availableCache. If an artifact is in neither cache ** then its current availability is unknown. */ Bag missing; /* Cache of artifacts that are incomplete */ Bag available; /* Cache of artifacts that are complete */ } contentCache; /* ** Remove the oldest element from the content cache */ static void content_cache_expire_oldest(void){ int i; int mnAge = contentCache.nextAge; int mn = -1; for(i=0; i<contentCache.n; i++){ if( contentCache.a[i].age<mnAge ){ mnAge = contentCache.a[i].age; mn = i; } } if( mn>=0 ){ bag_remove(&contentCache.inCache, contentCache.a[mn].rid); contentCache.szTotal -= blob_size(&contentCache.a[mn].content); blob_reset(&contentCache.a[mn].content); contentCache.n--; contentCache.a[mn] = contentCache.a[contentCache.n]; } } /* ** Add an entry to the content cache. ** ** This routines hands responsibility for the artifact over to the cache. ** The cache will deallocate memory when it has finished with it. */ void content_cache_insert(int rid, Blob *pBlob){ struct cacheLine *p; if( contentCache.n>500 || contentCache.szTotal>50000000 ){ i64 szBefore; do{ szBefore = contentCache.szTotal; content_cache_expire_oldest(); }while( contentCache.szTotal>50000000 && contentCache.szTotal<szBefore ); } if( contentCache.n>=contentCache.nAlloc ){ contentCache.nAlloc = contentCache.nAlloc*2 + 10; contentCache.a = fossil_realloc(contentCache.a, contentCache.nAlloc*sizeof(contentCache.a[0])); } p = &contentCache.a[contentCache.n++]; p->rid = rid; p->age = contentCache.nextAge++; contentCache.szTotal += blob_size(pBlob); p->content = *pBlob; blob_zero(pBlob); bag_insert(&contentCache.inCache, rid); } /* ** Clear the content cache. If it is passed true, it ** also frees all associated memory, otherwise it may ** retain parts for future uses of the cache. */ void content_clear_cache(int bFreeIt){ int i; for(i=0; i<contentCache.n; i++){ blob_reset(&contentCache.a[i].content); } bag_clear(&contentCache.missing); bag_clear(&contentCache.available); bag_clear(&contentCache.inCache); contentCache.n = 0; contentCache.szTotal = 0; if(bFreeIt){ fossil_free(contentCache.a); contentCache.a = 0; contentCache.nAlloc = 0; } } /* ** Return the srcid associated with rid. Or return 0 if rid is ** original content and not a delta. */ int delta_source_rid(int rid){ static Stmt q; int srcid; db_static_prepare(&q, "SELECT srcid FROM delta WHERE rid=:rid"); db_bind_int(&q, ":rid", rid); if( db_step(&q)==SQLITE_ROW ){ srcid = db_column_int(&q, 0); }else{ srcid = 0; } db_reset(&q); return srcid; } /* ** Return the blob.size field given blob.rid */ int content_size(int rid, int dflt){ static Stmt q; int sz = dflt; db_static_prepare(&q, "SELECT size FROM blob WHERE rid=:r"); db_bind_int(&q, ":r", rid); if( db_step(&q)==SQLITE_ROW ){ sz = db_column_int(&q, 0); } db_reset(&q); return sz; } /* ** 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 ){ bag_insert(&contentCache.missing, rid); return 0; } srcid = delta_source_rid(rid); if( srcid==0 ){ bag_insert(&contentCache.available, rid); return 1; } rid = srcid; } fossil_panic("delta-loop in repository"); return 0; } /* ** Mark artifact rid as being available now. Update the cache to ** show that everything that was formerly unavailable because rid ** was missing is now available. */ static void content_mark_available(int rid){ Bag pending; static Stmt q; if( bag_find(&contentCache.available, rid) ) return; bag_init(&pending); bag_insert(&pending, rid); while( (rid = bag_first(&pending))!=0 ){ bag_remove(&pending, rid); bag_remove(&contentCache.missing, rid); bag_insert(&contentCache.available, rid); db_static_prepare(&q, "SELECT rid FROM delta WHERE srcid=:rid"); db_bind_int(&q, ":rid", rid); while( db_step(&q)==SQLITE_ROW ){ int nx = db_column_int(&q, 0); bag_insert(&pending, nx); } db_reset(&q); } bag_clear(&pending); } /* ** Get the blob.content value for blob.rid=rid. Return 1 on success or ** 0 on failure. |
︙ | ︙ | |||
160 161 162 163 164 165 166 | blob_uncompress(pBlob, pBlob); rc = 1; } db_reset(&q); 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 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 | blob_uncompress(pBlob, pBlob); rc = 1; } db_reset(&q); return rc; } /* ** Extract the content for ID rid and put it into the ** uninitialized blob. Return 1 on success. If the record ** is a phantom, zero pBlob and return 0. */ int content_get(int rid, Blob *pBlob){ int rc; int i; int nextRid; assert( g.repositoryOpen ); blob_zero(pBlob); if( rid==0 ) return 0; /* Early out if we know the content is not available */ if( bag_find(&contentCache.missing, rid) ){ return 0; } /* Look for the artifact in the cache first */ if( bag_find(&contentCache.inCache, rid) ){ for(i=0; i<contentCache.n; i++){ if( contentCache.a[i].rid==rid ){ blob_copy(pBlob, &contentCache.a[i].content); contentCache.a[i].age = contentCache.nextAge++; return 1; } } } nextRid = delta_source_rid(rid); if( nextRid==0 ){ rc = content_of_blob(rid, pBlob); }else{ int n = 1; int nAlloc = 10; int *a = 0; int mx; Blob delta, next; a = fossil_malloc( sizeof(a[0])*nAlloc ); a[0] = rid; a[1] = nextRid; n = 1; while( !bag_find(&contentCache.inCache, nextRid) && (nextRid = delta_source_rid(nextRid))>0 ){ n++; if( n>=nAlloc ){ if( n>db_int(0, "SELECT max(rid) FROM blob") ){ fossil_panic("infinite loop in DELTA table"); } nAlloc = nAlloc*2 + 10; a = fossil_realloc(a, nAlloc*sizeof(a[0])); } a[n] = nextRid; } mx = n; rc = content_get(a[n], pBlob); n--; while( rc && n>=0 ){ rc = content_of_blob(a[n], &delta); if( rc ){ if( blob_delta_apply(pBlob, &delta, &next)<0 ){ rc = 1; }else{ blob_reset(&delta); if( (mx-n)%8==0 ){ content_cache_insert(a[n+1], pBlob); }else{ blob_reset(pBlob); } *pBlob = next; } } n--; } free(a); if( !rc ) blob_reset(pBlob); } if( rc==0 ){ bag_insert(&contentCache.missing, rid); }else{ bag_insert(&contentCache.available, rid); } return rc; } /* ** COMMAND: artifact* ** ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? ** ** Extract an artifact by its artifact hash and write the results on ** standard output, or if the optional second argument is given, in ** the named output file. ** ** Options: ** -R|--repository REPO Extract artifacts from repository REPO ** ** See also: [[finfo]] */ void artifact_cmd(void){ int rid; Blob content; const char *zFile; db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); if( g.argc!=4 && g.argc!=3 ) usage("ARTIFACT-ID ?FILENAME? ?OPTIONS?"); zFile = g.argc==4 ? g.argv[3] : "-"; rid = name_to_rid(g.argv[2]); if( rid==0 ){ fossil_fatal("%s",g.zErrMsg); } content_get(rid, &content); blob_write_to_file(&content, zFile); } /* ** COMMAND: test-content-rawget ** ** Extract a blob from the database and write it into a file. This ** version does not expand the delta. */ void test_content_rawget_cmd(void){ int rid; Blob content; const char *zFile; if( g.argc!=4 && g.argc!=3 ) usage("RECORDID ?FILENAME?"); zFile = g.argc==4 ? g.argv[3] : "-"; db_must_be_within_tree(); rid = name_to_rid(g.argv[2]); blob_zero(&content); db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid); blob_uncompress(&content, &content); blob_write_to_file(&content, zFile); } /* ** The following flag is set to disable the automatic calls to ** manifest_crosslink() when a record is dephantomized. This ** flag can be set (for example) when doing a clone when we know ** that rebuild will be run over all records at the conclusion ** of the operation. */ static int ignoreDephantomizations = 0; /* ** When a record is converted from a phantom to a real record, ** if that record has other records that are derived by delta, ** then call manifest_crosslink() on those other records. ** ** If the formerly phantom record or any of the other records ** derived by delta from the former phantom are a baseline manifest, ** then also invoke manifest_crosslink() on the delta-manifests ** associated with that baseline. ** ** Tail recursion is used to minimize stack depth. */ void after_dephantomize(int rid, int linkFlag){ Stmt q; int nChildAlloc = 0; int *aChild = 0; Blob content; if( ignoreDephantomizations ) return; 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); if( nChildUsed>=nChildAlloc ){ nChildAlloc = nChildAlloc*2 + 10; aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild)); } aChild[nChildUsed++] = child; } db_finalize(&q); for(i=1; i<nChildUsed; i++){ after_dephantomize(aChild[i], 1); } /* Tail recursion for the common case where only a single artifact ** is derived by delta from rid... */ rid = nChildUsed>0 ? aChild[0] : 0; linkFlag = 1; } free(aChild); } /* ** Turn dephantomization processing on or off. */ void content_enable_dephantomize(int onoff){ ignoreDephantomizations = !onoff; } /* ** Make sure the g.rcvid global variable has been initialized. ** ** If the g.zIpAddr variable has not been set when this routine is ** called, use zSrc as the source of content for the rcvfrom ** table entry. */ void content_rcvid_init(const char *zSrc){ if( g.rcvid==0 ){ user_select(); if( g.zIpAddr ) zSrc = g.zIpAddr; db_multi_exec( "INSERT INTO rcvfrom(uid, mtime, nonce, ipaddr)" "VALUES(%d, julianday('now'), %Q, %Q)", g.userUid, g.zNonce, zSrc ); g.rcvid = db_last_insert_rowid(); } } /* ** 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, ** and zUuid is zero then the correct zUuid is computed from pBlob. ** ** If the record already exists but is a phantom, the pBlob content ** is inserted and the phatom becomes a real record. ** ** The original content of pBlob is not disturbed. The caller continues ** to be responsible for pBlob. This routine does *not* take over ** responsibility for freeing pBlob. */ int content_put_ex( Blob *pBlob, /* Content to add to the repository */ const char *zUuid, /* artifact hash of reconstructed pBlob */ int srcId, /* pBlob is a delta from this entry */ int nBlob, /* pBlob is compressed. Original size is this */ int isPrivate /* The content should be marked private */ ){ 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 ); db_begin_transaction(); if( zUuid==0 ){ assert( nBlob==0 ); /* First check the auxiliary hash to see if there is already an artifact ** that uses the auxiliary hash name */ hname_hash(pBlob, 1, &hash); rid = fast_uuid_to_rid(blob_str(&hash)); if( rid==0 ){ /* No existing artifact with the auxiliary hash name. Therefore, use ** the primary hash name. */ blob_reset(&hash); hname_hash(pBlob, 0, &hash); } }else{ blob_init(&hash, zUuid, -1); } if( g.eHashPolicy==HPOLICY_AUTO && blob_size(&hash)>HNAME_LEN_SHA1 ){ g.eHashPolicy = HPOLICY_SHA3; db_set_int("hash-policy", HPOLICY_SHA3, 0); } if( nBlob ){ size = nBlob; }else{ size = blob_size(pBlob); if( srcId ){ size = delta_output_size(blob_buffer(pBlob), size); } } /* Check to see if the entry already exists and if it does whether ** or not the entry is a phantom */ db_prepare(&s1, "SELECT rid, size FROM blob WHERE uuid=%B", &hash); if( db_step(&s1)==SQLITE_ROW ){ rid = db_column_int(&s1, 0); if( db_column_int(&s1, 1)>=0 ){ /* The entry is not a phantom. There is nothing for us to do ** other than return the RID. */ db_finalize(&s1); db_end_transaction(0); return rid; } }else{ rid = 0; /* No entry with the same hash currently exists */ markAsUnclustered = 1; } db_finalize(&s1); /* Construct a received-from ID if we do not already have one */ content_rcvid_init(0); if( nBlob ){ cmpr = pBlob[0]; }else{ blob_compress(pBlob, &cmpr); } if( rid>0 ){ /* We are just adding data to a phantom */ db_prepare(&s1, "UPDATE blob SET rcvid=%d, size=%d, content=:data WHERE rid=%d", g.rcvid, size, rid ); db_bind_blob(&s1, ":data", &cmpr); db_exec(&s1); db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); if( srcId==0 || content_is_available(srcId) ){ 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 OR IGNORE 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); } /* Finish the transaction and cleanup */ db_finalize(&s1); db_end_transaction(0); blob_reset(&hash); /* Make arrangements to verify that the data can be recovered ** before we commit */ verify_before_commit(rid); return rid; } /* ** This is the simple common case for inserting content into the ** repository. pBlob is the content to be inserted. ** ** pBlob is uncompressed and is not deltaed. It is exactly the content ** to be inserted. ** ** The original content of pBlob is not disturbed. The caller continues ** to be responsible for pBlob. This routine does *not* take over ** responsiblity for freeing pBlob. */ int content_put(Blob *pBlob){ return content_put_ex(pBlob, 0, 0, 0, 0); } /* ** Create a new phantom with the given hash 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, "INSERT INTO blob(rcvid,size,uuid,content)" "VALUES(0,-1,:uuid,NULL)" ); db_bind_text(&s1, ":uuid", zUuid); db_exec(&s1); rid = db_last_insert_rowid(); db_static_prepare(&s2, "INSERT INTO phantom VALUES(:rid)" ); db_bind_int(&s2, ":rid", rid); db_exec(&s2); if( g.markPrivate || isPrivate ){ db_multi_exec("INSERT INTO private VALUES(%d)", rid); }else{ db_static_prepare(&s3, "INSERT INTO unclustered VALUES(:rid)" ); db_bind_int(&s3, ":rid", rid); db_exec(&s3); } bag_insert(&contentCache.missing, rid); db_end_transaction(0); return rid; } /* ** COMMAND: test-content-put ** ** Usage: %fossil test-content-put FILE ** ** Read the content of FILE and add it to the Blob table as a new ** artifact using a direct call to content_put(). */ void test_content_put_cmd(void){ int rid; Blob content; if( g.argc!=3 ) usage("FILENAME"); db_must_be_within_tree(); user_select(); blob_read_from_file(&content, g.argv[2], ExtFILE); rid = content_put(&content); fossil_print("inserted as record %d\n", rid); } /* ** Make sure the content at rid is the original content and is not a ** delta. */ void content_undelta(int rid){ if( delta_source_rid(rid)>0 ){ Blob x; if( content_get(rid, &x) ){ Stmt s; db_prepare(&s, "UPDATE blob SET content=:c, size=%d WHERE rid=%d", blob_size(&x), rid); blob_compress(&x, &x); db_bind_blob(&s, ":c", &x); db_exec(&s); db_finalize(&s); blob_reset(&x); db_multi_exec("DELETE FROM delta WHERE rid=%d", rid); } } } /* ** COMMAND: test-content-undelta ** ** Make sure the content at RECORDID is not a delta */ void test_content_undelta_cmd(void){ int rid; if( g.argc!=3 ) usage("RECORDID"); db_must_be_within_tree(); rid = atoi(g.argv[2]); content_undelta(rid); } /* ** Return true if the given RID is marked as PRIVATE. */ int content_is_private(int rid){ static Stmt s1; 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); db_exec(&s1); } /* ** Make sure an artifact is private */ void content_make_private(int rid){ static Stmt s1; db_static_prepare(&s1, "INSERT OR IGNORE INTO private(rid) VALUES(:rid)" ); db_bind_int(&s1, ":rid", rid); db_exec(&s1); } /* ** Try to change the storage of rid so that it is a delta from one ** of the artifacts given in aSrc[0]..aSrc[nSrc-1]. The aSrc[*] that ** gives the smallest delta is choosen. ** ** If rid is already a delta from some other place then no ** conversion occurs and this is a no-op unless force==1. If force==1, ** then nSrc must also be 1. ** ** If rid refers to a phantom, no delta is created. ** ** Never generate a delta that carries a private artifact into a public ** artifact. Otherwise, when we go to send the public artifact on a ** sync operation, the other end of the sync will never be able to receive ** the source of the delta. It is OK to delta private->private and ** public->private and public->public. Just no private->public delta. ** ** If aSrc[bestSrc] is already a delta that depends on rid, then it is ** converted to undeltaed text before the aSrc[bestSrc]->rid delta is ** created, in order to prevent a delta loop. ** ** If either rid or aSrc[i] 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 the number of bytes by which the storage associated with rid ** is reduced. A return of 0 means no new deltification occurs. */ int content_deltify(int rid, int *aSrc, int nSrc, int force){ int s; Blob data; /* Content of rid */ Blob src; /* Content of aSrc[i] */ Blob delta; /* Delta from aSrc[i] to rid */ Blob bestDelta; /* Best delta seen so far */ int bestSrc = 0; /* Which aSrc is the source of the best delta */ int rc = 0; /* Value to return */ int i; /* Loop variable for aSrc[] */ /* ** Historically this routine gracefully ignored the rid 0, but the ** addition of a call to content_is_available() in [188ffef2] caused ** rid 0 to trigger an assert via bag_find(). Rather than track down ** all such calls (e.g. the one via /technoteedit), we'll continue ** to gracefully ignore rid 0 here. */ if( 0==rid ) return 0; /* If rid is already a child (a delta) of some other artifact, return ** immediately if the force flags is false */ if( !force && delta_source_rid(rid)>0 ) return 0; /* If rid refers to a phantom, skip deltification. */ if( 0==content_is_available(rid) ) return 0; /* Get the complete content of the object to be delta-ed. If the size ** is less than 50 bytes, then there really is no point in trying to do ** a delta, so return immediately */ content_get(rid, &data); if( blob_size(&data)<50 ){ /* Do not try to create a delta for objects smaller than 50 bytes */ blob_reset(&data); return 0; } blob_init(&bestDelta, 0, 0); /* Loop over all candidate delta sources */ for(i=0; i<nSrc; i++){ int srcid = aSrc[i]; if( srcid==rid ) continue; if( content_is_private(srcid) && !content_is_private(rid) ) continue; /* Compute all ancestors of srcid and make sure rid is not one of them. ** If rid is an ancestor of srcid, then making rid a decendent of srcid ** would create a delta loop. */ s = srcid; while( (s = delta_source_rid(s))>0 ){ if( s==rid ){ content_undelta(srcid); break; } } if( s!=0 ) continue; content_get(srcid, &src); if( blob_size(&src)<50 ){ /* The source is smaller then 50 bytes, so don't bother trying to use it*/ blob_reset(&src); continue; } blob_delta_create(&src, &data, &delta); if( blob_size(&delta) < blob_size(&data)*0.75 && (bestSrc<=0 || blob_size(&delta)<blob_size(&bestDelta)) ){ /* This is the best delta seen so far. Remember it */ blob_reset(&bestDelta); bestDelta = delta; bestSrc = srcid; }else{ /* This delta is not a candidate for becoming the new parent of rid */ blob_reset(&delta); } blob_reset(&src); } /* If there is a winning candidate for the new parent of rid, then ** make that candidate the new parent now */ if( bestSrc>0 ){ Stmt s1, s2; /* Statements used to create the delta */ blob_compress(&bestDelta, &bestDelta); db_prepare(&s1, "UPDATE blob SET content=:data WHERE rid=%d", rid); db_prepare(&s2, "REPLACE INTO delta(rid,srcid)VALUES(%d,%d)", rid, bestSrc); db_bind_blob(&s1, ":data", &bestDelta); db_begin_transaction(); rc = db_int(0, "SELECT octet_length(content) FROM blob WHERE rid=%d", rid); db_exec(&s1); db_exec(&s2); db_end_transaction(0); db_finalize(&s1); db_finalize(&s2); verify_before_commit(rid); rc -= blob_size(&bestDelta); } blob_reset(&data); blob_reset(&bestDelta); return rc; } /* ** COMMAND: test-content-deltify ** ** Usage: %fossil RID SRCID SRCID ... [-force] ** ** Convert the content at RID into a delta one of the from SRCIDs. */ void test_content_deltify_cmd(void){ int nSrc; int *aSrc; int i; int bForce = find_option("force",0,0)!=0; if( g.argc<3 ) usage("[--force] RID SRCID SRCID..."); aSrc = fossil_malloc( (g.argc-2)*sizeof(aSrc[0]) ); nSrc = 0; for(i=2; i<g.argc; i++) aSrc[nSrc++] = atoi(g.argv[i]); db_must_be_within_tree(); content_deltify(atoi(g.argv[2]), aSrc, nSrc, bForce); } /* ** Return true if Blob p looks like it might be a parsable control artifact. */ static int looks_like_control_artifact(Blob *p){ const char *z = blob_buffer(p); int n = blob_size(p); if( n<10 ) return 0; if( strncmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)==0 ) return 1; if( z[0]<'A' || z[0]>'Z' || z[1]!=' ' || z[0]=='I' ) return 0; if( z[n-1]!='\n' ) return 0; return 1; } /* ** COMMAND: test-integrity ** ** Verify that all content can be extracted from the BLOB table correctly. ** If the BLOB table is correct, then the repository can always be ** successfully reconstructed using "fossil rebuild". ** ** Options: ** -d|--db-only Run "PRAGMA integrity_check" on the database only. ** No other validation is performed. ** --parse Parse all manifests, wikis, tickets, events, and ** so forth, reporting any errors found. ** -q|--quick Run "PRAGMA quick_check" on the database only. ** No other validation is performed. */ void test_integrity(void){ Stmt q; Blob content; int n1 = 0; int n2 = 0; int nErr = 0; int total; int nCA = 0; int anCA[10]; int bParse = find_option("parse",0,0)!=0; int bDbOnly = find_option("db-only","d",0)!=0; int bQuick = find_option("quick","q",0)!=0; db_find_and_open_repository(OPEN_ANY_SCHEMA, 2); if( bDbOnly || bQuick ){ const char *zType = bQuick ? "quick" : "integrity"; char *zRes; zRes = db_text(0,"PRAGMA repository.%s_check", zType/*safe-for-%s*/); if( fossil_strcmp(zRes,"ok")!=0 ){ fossil_print("%s_check failed!\n", zType); exit(1); }else{ fossil_print("ok\n"); } return; } memset(anCA, 0, sizeof(anCA)); /* Make sure no public artifact is a delta from a private artifact */ db_prepare(&q, "SELECT " " rid, (SELECT uuid FROM blob WHERE rid=delta.rid)," " srcid, (SELECT uuid FROM blob WHERE rid=delta.srcid)" " FROM delta" " WHERE srcid in private AND rid NOT IN private" ); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); const char *zId = db_column_text(&q, 1); int srcid = db_column_int(&q, 2); const char *zSrc = db_column_text(&q, 3); 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 nUuid = db_column_bytes(&q, 1); int size = db_column_int(&q, 2); n1++; fossil_print(" %d/%d\r", n1, total); fflush(stdout); if( size<0 ){ fossil_print("skip phantom %d %s\n", rid, zUuid); continue; /* Ignore phantoms */ } content_get(rid, &content); if( (int)blob_size(&content)!=size ){ fossil_print("size mismatch on artifact %d: wanted %d but got %d\n", rid, size, blob_size(&content)); nErr++; } if( !hname_verify_hash(&content, zUuid, nUuid) ){ fossil_print("wrong hash on artifact %d\n",rid); nErr++; } if( bParse && looks_like_control_artifact(&content) ){ Blob err; int i, n; char *z; Manifest *p; char zFirstLine[400]; blob_zero(&err); z = blob_buffer(&content); n = blob_size(&content); for(i=0; i<n && z[i] && z[i]!='\n' && i<(int)sizeof(zFirstLine)-1; i++){} memcpy(zFirstLine, z, i); zFirstLine[i] = 0; p = manifest_parse(&content, 0, &err); if( p==0 ){ fossil_print("manifest_parse failed for %s:\n%s\n", zUuid, blob_str(&err)); if( strncmp(blob_str(&err), "line 1:", 7)==0 ){ fossil_print("\"%s\"\n", zFirstLine); } }else{ anCA[p->type]++; manifest_destroy(p); nCA++; } blob_reset(&err); }else{ blob_reset(&content); } 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]); } } fossil_print("low-level database integrity-check: "); fossil_print("%s\n", db_text(0, "PRAGMA integrity_check(10)")); } /* ** COMMAND: test-orphans ** ** Search the repository for orphaned artifacts. */ void test_orphans(void){ Stmt q; int cnt = 0; db_find_and_open_repository(0, 0); db_multi_exec( "CREATE TEMP TABLE used(id INTEGER PRIMARY KEY ON CONFLICT IGNORE);" "INSERT INTO used SELECT mid FROM mlink;" /* Manifests */ "INSERT INTO used SELECT fid FROM mlink;" /* Files */ "INSERT INTO used SELECT srcid FROM tagxref WHERE srcid>0;" /* Tags */ "INSERT INTO used SELECT rid FROM tagxref;" /* Wiki & tickets */ "INSERT INTO used SELECT rid FROM attachment JOIN blob ON src=uuid;" "INSERT INTO used SELECT attachid FROM attachment;" "INSERT INTO used SELECT objid FROM event;" ); db_prepare(&q, "SELECT rid, uuid, size FROM blob WHERE rid NOT IN used"); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%7d %s size: %d\n", db_column_int(&q, 0), db_column_text(&q, 1), db_column_int(&q,2)); cnt++; } db_finalize(&q); fossil_print("%d orphans\n", cnt); } /* Allowed flags for check_exists */ #define MISSING_SHUNNED 0x0001 /* Do not report shunned artifacts */ /* This is a helper routine for test-artifacts. ** ** Check to see that the artifact hash referenced by zUuid exists in the ** repository. If it does, return 0. If it does not, generate an error ** message and return 1. */ static int check_exists( const char *zUuid, /* Hash of the artifact we are checking for */ unsigned flags, /* Flags */ Manifest *p, /* The control artifact that references zUuid */ const char *zRole, /* Role of zUuid in p */ const char *zDetail /* Additional information, such as a filename */ ){ static Stmt q; int rc = 0; db_static_prepare(&q, "SELECT size FROM blob WHERE uuid=:uuid"); if( zUuid==0 || zUuid[0]==0 ) return 0; db_bind_text(&q, ":uuid", zUuid); if( db_step(&q)==SQLITE_ROW ){ int size = db_column_int(&q, 0); if( size<0 ) rc = 2; }else{ 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; case CFTYPE_CONTROL: zCFType = "tag"; break; case CFTYPE_WIKI: zCFType = "wiki"; break; case CFTYPE_TICKET: zCFType = "ticket"; break; case CFTYPE_ATTACHMENT: zCFType = "attachment"; break; case CFTYPE_EVENT: zCFType = "event"; break; } zSrc = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid); if( p->rDate>0.0 ){ zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate); }else{ zDate = db_text(0, "SELECT datetime(rcvfrom.mtime)" " FROM blob, rcvfrom" " WHERE blob.rcvid=rcvfrom.rcvid" " AND blob.rid=%d", p->rid); } 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 ** ** Usage: %fossil test-missing ** ** Look at every artifact in the repository and verify that ** all references are satisfied. Report any referenced artifacts ** that are missing or shunned. ** ** Options: ** --notshunned Do not report shunned artifacts ** --quiet Only show output if there are errors */ void test_missing(void){ Stmt q; Blob content; int nErr = 0; int nArtifact = 0; int i; Manifest *p; unsigned flags = 0; int quietFlag; if( find_option("notshunned", 0, 0)!=0 ) flags |= MISSING_SHUNNED; quietFlag = find_option("quiet","q",0)!=0; db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); db_prepare(&q, "SELECT mid FROM mlink UNION " "SELECT srcid FROM tagxref WHERE srcid>0 UNION " "SELECT rid FROM tagxref UNION " "SELECT rid FROM attachment JOIN blob ON src=uuid UNION " "SELECT objid FROM event"); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); 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); } } /* ** COMMAND: test-content-erase ** ** Usage: %fossil test-content-erase RID .... ** ** Remove all traces of one or more artifacts from the local repository. ** ** WARNING: This command destroys data and can cause you to lose work. ** Make sure you have a backup copy before using this command! ** ** WARNING: You must run "fossil rebuild" after this command to rebuild ** the metadata. ** ** Note that the arguments are the integer raw RID values from the BLOB table, ** not artifact hashes or labels. */ void test_content_erase(void){ int i; Blob x; char c; Stmt q; prompt_user("This command erases information from the repository and\n" "might irrecoverably damage the repository. Make sure you\n" "have a backup copy!\n" "Continue? (y/N)? ", &x); c = blob_str(&x)[0]; blob_reset(&x); if( c!='y' && c!='Y' ) return; db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); db_begin_transaction(); db_prepare(&q, "SELECT rid FROM delta WHERE srcid=:rid"); for(i=2; i<g.argc; i++){ int rid = atoi(g.argv[i]); fossil_print("Erasing artifact %d (%s)\n", rid, db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid)); db_bind_int(&q, ":rid", rid); while( db_step(&q)==SQLITE_ROW ){ content_undelta(db_column_int(&q,0)); } db_reset(&q); db_multi_exec("DELETE FROM blob WHERE rid=%d", rid); db_multi_exec("DELETE FROM delta WHERE rid=%d", rid); } db_finalize(&q); db_end_transaction(0); } |
Added src/cookies.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 | /* ** Copyright (c) 2017 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 manage a cookie that stores user-specific ** display preferences for the web interface. ** ** cookie_parse(void); ** ** Read and parse the display preferences cookie. ** ** cookie_read_parameter(zQP, zPName); ** ** If query parameter zQP does not exist but zPName does exist in ** the parsed cookie, then initialize zQP to hold the same value ** as the zPName element in the parsed cookie. ** ** cookie_write_parameter(zQP, zPName, zDefault); ** ** If query parameter zQP exists and if it has a different value from ** the zPName parameter in the parsed cookie, then replace the value of ** zPName with the value of zQP. If zQP exists but zPName does not ** exist, then zPName is created. If zQP does not exist or if it has ** the same value as zPName, then this routine is a no-op. ** ** cookie_link_parameter(zQP, zPName, zDefault); ** ** This does both cookie_read_parameter() and cookie_write_parameter() ** all at once. ** ** cookie_render(); ** ** If any prior calls to cookie_write_parameter() have changed the ** value of the user preferences cookie, this routine will cause the ** new cookie value to be included in the HTTP header for the current ** web page. This routine is a destructor for this module and should ** be called once. ** ** char *cookie_value(zPName, zDefault); ** ** Look up the value of a cookie parameter zPName. Return zDefault if ** there is no display preferences cookie or if zPName does not exist. */ #include "config.h" #include "cookies.h" #include <assert.h> #include <string.h> #if INTERFACE /* the standard name of the display settings cookie for fossil */ # define DISPLAY_SETTINGS_COOKIE "fossil_display_settings" #endif /* ** State information private to this module */ #define COOKIE_NPARAM 10 static struct { char *zCookieValue; /* Value of the user preferences cookie */ int bChanged; /* True if any value has changed */ int bIsInit; /* True after initialization */ int nParam; /* Number of parameters in the cookie */ struct { const char *zPName; /* Name of a parameter */ char *zPValue; /* Value of that parameter */ } aParam[COOKIE_NPARAM]; } cookies; /* Initialize this module by parsing the content of the cookie named ** by DISPLAY_SETTINGS_COOKIE */ void cookie_parse(void){ char *z; if( cookies.bIsInit ) return; z = (char*)P(DISPLAY_SETTINGS_COOKIE); if( z==0 ) z = ""; cookies.zCookieValue = z = mprintf("%s", z); cookies.bIsInit = 1; while( cookies.nParam<COOKIE_NPARAM ){ while( fossil_isspace(z[0]) ) z++; if( z[0]==0 ) break; cookies.aParam[cookies.nParam].zPName = z; while( *z && *z!='=' && *z!=',' ){ z++; } if( *z=='=' ){ *z = 0; z++; cookies.aParam[cookies.nParam].zPValue = z; while( *z && *z!=',' ){ z++; } if( *z ){ *z = 0; z++; } dehttpize(cookies.aParam[cookies.nParam].zPValue); }else{ if( *z ){ *z++ = 0; } cookies.aParam[cookies.nParam].zPValue = ""; } cookies.nParam++; } } #define COOKIE_READ 1 #define COOKIE_WRITE 2 static void cookie_readwrite( const char *zQP, /* Name of the query parameter */ const char *zPName, /* Name of the cooking setting */ const char *zDflt, /* Default value for the query parameter */ int flags /* READ or WRITE or both */ ){ const char *zQVal = P(zQP); int i; cookie_parse(); for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){} if( zQVal==0 && (flags & COOKIE_READ)!=0 && i<cookies.nParam ){ cgi_set_parameter_nocopy(zQP, cookies.aParam[i].zPValue, 1); return; } if( zQVal==0 ){ zQVal = zDflt; if( flags & COOKIE_WRITE ) cgi_set_parameter_nocopy(zQP, zQVal, 1); } if( (flags & COOKIE_WRITE)!=0 && i<COOKIE_NPARAM && (i==cookies.nParam || strcmp(zQVal, cookies.aParam[i].zPValue)) ){ if( i==cookies.nParam ){ cookies.aParam[i].zPName = zPName; cookies.nParam++; } cookies.aParam[i].zPValue = (char*)zQVal; cookies.bChanged = 1; } } /* If query parameter zQP is missing, initialize it using the zPName ** value from the user preferences cookie */ void cookie_read_parameter(const char *zQP, const char *zPName){ cookie_readwrite(zQP, zPName, 0, COOKIE_READ); } /* Update the zPName value of the user preference cookie to match ** the value of query parameter zQP. */ void cookie_write_parameter( const char *zQP, const char *zPName, const char *zDflt ){ cookie_readwrite(zQP, zPName, zDflt, COOKIE_WRITE); } /* Use the zPName user preference value as a default for zQP and record ** any changes to the zQP value back into the cookie. */ void cookie_link_parameter( const char *zQP, /* The query parameter */ const char *zPName, /* The name of the cookie value */ const char *zDflt /* Default value for the parameter */ ){ cookie_readwrite(zQP, zPName, zDflt, COOKIE_READ|COOKIE_WRITE); } /* Update the user preferences cookie, if necessary, and shut down ** this module. The cookie is only emitted if its value has actually ** changed since the request started and the "udc" (Update Display ** Cookie) URL argument was provided. ** ** Historical note: from 2021-03-02 [71a2d68a7a113e7c] until ** 2023-01-16, the udc was not observed (it had been prior to that), ** and that led to the unfortunate side effect that a timeline link ** from the /reports page would end up persistently setting a user's ** timeline length preference to the number of items in that ** report. In a /chat discussion it was agreed that updating the ** cookie requires explicit opt-in via the udc argument or ?skin=..., ** which implies udc. */ void cookie_render(void){ if( cookies.bChanged && P("udc")!=0 ){ Blob new; int i; blob_init(&new, 0, 0); for(i=0;i<cookies.nParam;i++){ if( i>0 ) blob_append(&new, ",", 1); blob_appendf(&new, "%s=%T", cookies.aParam[i].zPName, cookies.aParam[i].zPValue); } cgi_set_cookie(DISPLAY_SETTINGS_COOKIE, blob_str(&new), 0, 31536000); } cookies.bIsInit = 0; } /* Return the value of a preference cookie. */ const char *cookie_value(const char *zPName, const char *zDefault){ int i; assert( zPName!=0 ); cookie_parse(); for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){} return i<cookies.nParam ? cookies.aParam[i].zPValue : zDefault; } /* ** WEBPAGE: cookies ** ** Show all cookies associated with Fossil. This shows the text of the ** login cookie and is hence dangerous if an adversary is looking over ** your shoulder and is able to read and reproduce that cookie. ** ** WEBPAGE: fdscookie ** ** Show the current display settings contained in the ** "fossil_display_settings" cookie. */ void cookie_page(void){ int i; int nCookie = 0; const char *zName = 0; const char *zValue = 0; int isQP = 0; int bFDSonly = strstr(g.zPath, "fdscookie")!=0; cookie_parse(); if( bFDSonly ){ style_header("Display Preferences Cookie"); }else{ style_header("All Cookies"); } @ <form method="POST"> @ <ol> for(i=0; cgi_param_info(i, &zName, &zValue, &isQP); i++){ char *zDel; if( isQP ) continue; if( fossil_isupper(zName[0]) ) continue; if( bFDSonly && strcmp(zName, "fossil_display_settings")!=0 ) continue; zDel = mprintf("del%s",zName); if( P(zDel)!=0 ){ cgi_set_cookie(zName, "", 0, -1); cgi_redirect(g.zPath); } nCookie++; @ <li><p><b>%h(zName)</b>: %h(zValue) @ <input type="submit" name="%h(zDel)" value="Delete"> if( fossil_strcmp(zName, DISPLAY_SETTINGS_COOKIE)==0 && cookies.nParam>0 ){ int j; @ <ul> for(j=0; j<cookies.nParam; j++){ @ <li>%h(cookies.aParam[j].zPName): "%h(cookies.aParam[j].zPValue)" } @ </ul> } fossil_free(zDel); } @ </ol> @ </form> if( nCookie==0 ){ if( bFDSonly ){ @ <p><i>Your browser is not holding a "fossil_display_setting" cookie @ for this website</i></p> }else{ @ <p><i>Your browser is not holding any cookies for this website</i></p> } } style_finish_page(); } |
Added src/copybtn.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts ** thereof) of the target elements to the clipboard. ** ** Newly created buttons are <span> elements with an SVG background icon, ** defined by the "copy-button" class in the default CSS style sheet, and are ** assigned the element ID "copy-<idTarget>". ** ** To simplify customization, the only properties modified for HTML-defined ** buttons are the "onclick" handler, and the "transition" and "opacity" styles ** (used for animation). ** ** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(), ** needs to be called to attach the "onclick" handler (done automatically from ** a handler attached to the "DOMContentLoaded" event). ** ** The initialization functions do not overwrite the "data-copytarget" and ** "data-copylength" attributes with empty or null values for <idTarget> and ** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the ** previous copy length limit. ** ** HTML snippet for statically created buttons: ** ** <span class="copy-button" id="copy-<idTarget>" ** data-copytarget="<idTarget>" data-copylength="<cchLength>"></span> */ function makeCopyButton(idTarget,bFlipped,cchLength){ var elButton = document.createElement("span"); elButton.className = "copy-button"; if( bFlipped ) elButton.className += " copy-button-flipped"; elButton.id = "copy-" + idTarget; initCopyButton(elButton,idTarget,cchLength); return elButton; } function initCopyButtonById(idButton,idTarget,cchLength){ idButton = idButton || "copy-" + idTarget; var elButton = document.getElementById(idButton); if( elButton ) initCopyButton(elButton,idTarget,cchLength); return elButton; } function initCopyButton(elButton,idTarget,cchLength){ elButton.style.transition = ""; elButton.style.opacity = 1; if( idTarget ) elButton.setAttribute("data-copytarget",idTarget); if( cchLength ) elButton.setAttribute("data-copylength",cchLength); elButton.onclick = clickCopyButton; return elButton; } setTimeout(function(){ var elButtons = document.getElementsByClassName("copy-button"); for ( var i=0; i<elButtons.length; i++ ){ initCopyButton(elButtons[i],0,0); } },1); /* The onclick handler for the "Copy Button". */ function clickCopyButton(e){ e.preventDefault(); /* Mandatory for <a> and <button>. */ e.stopPropagation(); if( this.getAttribute("data-copylocked") ) return; this.setAttribute("data-copylocked","1"); this.style.transition = "opacity 400ms ease-in-out"; this.style.opacity = 0; var idTarget = this.getAttribute("data-copytarget"); var elTarget = document.getElementById(idTarget); if( elTarget ){ var text = elTarget.innerText.replace(/^\s+|\s+$/g,""); var cchLength = parseInt(this.getAttribute("data-copylength")); if( !isNaN(cchLength) && cchLength>0 ){ text = text.slice(0,cchLength); /* Assume single-byte chars. */ } copyTextToClipboard(text); } setTimeout(function(){ this.style.transition = ""; this.style.opacity = 1; this.removeAttribute("data-copylocked"); }.bind(this),400); } /* Create a temporary <textarea> element and copy the contents to clipboard. */ function copyTextToClipboard(text){ if( window.clipboardData && window.clipboardData.setData ){ window.clipboardData.setData("Text",text); }else{ var elTextarea = document.createElement("textarea"); elTextarea.style.position = "fixed"; elTextarea.value = text; document.body.appendChild(elTextarea); elTextarea.select(); try{ document.execCommand("copy"); }catch(err){ }finally{ document.body.removeChild(elTextarea); } } } |
Added src/cygsup.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Copyright (c) 2007 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 preprocessor directives used to help integrate with the ** Cygwin runtime and build environment. The intent of this file is to keep ** the Cygwin-specific preprocessor directives together. */ #if defined(__CYGWIN__) && !defined(CYGSUP_H) #define CYGSUP_H /* ******************************************************************************* ** Include any Cygwin-specific headers here. ** ******************************************************************************* */ #include <wchar.h> #include <sys/cygwin.h> /* ******************************************************************************* ** Define any Cygwin-specific preprocessor macros here. All macros defined in ** this section should be wrapped with #ifndef, in order to allow them to be ** externally overridden. ******************************************************************************* */ #ifndef CP_UTF8 # define CP_UTF8 65001 #endif #ifndef WINBASEAPI # define WINBASEAPI __declspec(dllimport) #endif #ifndef WINADVAPI # define WINADVAPI __declspec(dllimport) #endif #ifndef SHSTDAPI # define SHSTDAPI __declspec(dllimport) #endif #ifndef STDAPI # define STDAPI __stdcall #endif #ifndef WINAPI # define WINAPI __stdcall #endif /* ******************************************************************************* ** Declare any Cygwin-specific Win32 or other APIs here. Functions declared in ** this section should use the built-in ANSI C types in order to make sure this ** header file continues to work as a self-contained unit. ** ** On Cygwin64, "long" is 64-bit but in Win64 it's 32-bit. That's why in the ** signatures below "long" should not be used. They now use "int" instead. ******************************************************************************* */ WINADVAPI extern WINAPI int RegOpenKeyExW( void *, /* HKEY */ const wchar_t *, /* LPCWSTR */ unsigned int, /* DWORD */ unsigned int, /* REGSAM */ void * /* PHKEY */ ); WINADVAPI extern WINAPI int RegQueryValueExW( void *, /* HKEY */ const wchar_t *, /* LPCWSTR */ unsigned int *, /* LPDWORD */ unsigned int *, /* LPDWORD */ unsigned char *, /* LPBYTE */ unsigned int * /* LPDWORD */ ); SHSTDAPI extern STDAPI void *ShellExecuteW( void *, /* HWND */ const wchar_t *, /* LPCWSTR */ const wchar_t *, /* LPCWSTR */ const wchar_t *, /* LPCWSTR */ const wchar_t *, /* LPCWSTR */ int /* INT */ ); WINBASEAPI extern WINAPI int WideCharToMultiByte( unsigned int, /* UINT */ unsigned int, /* DWORD */ const wchar_t *, /* LPCWSTR */ int, /* int */ char *, /* LPSTR */ int, /* int */ const char *, /* LPCSTR */ int * /* LPBOOL */ ); WINBASEAPI extern WINAPI int MultiByteToWideChar( unsigned int, /* UINT */ unsigned int, /* DWORD */ const char *, /* LPCSTR */ int, /* int */ wchar_t *, /* LPWSTR */ int /* int */ ); #endif /* defined(__CYGWIN__) && !defined(CYGSUP_H) */ |
Changes to src/db.c.
1 2 3 4 5 6 | /* ** 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".) | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > < < < < > > > > > > > > > > > > | < < | < < < < | | > > | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > > > | > > | > > | > | < < < > > | > > > > > > > > > > > | | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > | | > > > > > > > > > > > > | | > > > | > > > > | | > > > > > > | < | < | < | > > > > | > > > > > | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > | > > > > | | > > > > > > > > > > > > | > > > > | > > > | > | > > > | | > > > > > > > > > > > > | 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 | /* ** 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/ ** ******************************************************************************* ** ** Code for interfacing to the various databases. ** ** There are three separate database files that fossil interacts ** with: ** ** (1) The "configdb" database in ~/.fossil or ~/.config/fossil.db ** or in %LOCALAPPDATA%/_fossil ** ** (2) The "repository" database ** ** (3) A local check-out database named "_FOSSIL_" or ".fslckout" ** and located at the root of the local copy of the source tree. ** */ #include "config.h" #if defined(_WIN32) # if USE_SEE # include <windows.h> # define GETPID (int)GetCurrentProcessId # endif #else # include <pwd.h> # if USE_SEE # define GETPID getpid # endif #endif #if USE_SEE && !defined(SQLITE_HAS_CODEC) # define SQLITE_HAS_CODEC #endif #if USE_SEE && defined(__linux__) # include <sys/uio.h> #endif #include <sqlite3.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <time.h> /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */ #if USE_SEE #if defined(_WIN32) typedef DWORD PID_T; #else typedef pid_t PID_T; #endif #endif #include "db.h" #if INTERFACE /* ** Type definitions used for handling the saved encryption key for SEE. */ #if !defined(_WIN32) typedef void *LPVOID; typedef size_t SIZE_T; #endif /* ** Operations for db_maybe_handle_saved_encryption_key_for_process, et al. */ #define SEE_KEY_READ ((int)0) #define SEE_KEY_WRITE ((int)1) #define SEE_KEY_ZERO ((int)2) /* ** 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 */ int rc; /* Error from db_vprepare() */ }; /* ** 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, 0} #endif /* INTERFACE */ const struct Stmt empty_Stmt = empty_Stmt_m; /* ** Call this routine when a database error occurs. ** This routine throws a fatal error. It does not return. */ static void db_err(const char *zFormat, ...){ va_list ap; char *z; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); #ifdef FOSSIL_ENABLE_JSON if( g.json.isJsonMode!=0 ){ /* ** Avoid calling into the JSON support subsystem if it ** has not yet been initialized, e.g. early SQLite log ** messages, etc. */ json_bootstrap_early(); json_err( 0, z, 1 ); } else #endif /* FOSSIL_ENABLE_JSON */ if( g.xferPanic && g.cgiOutput==1 ){ cgi_reset_content(); @ error Database\serror:\s%F(z) cgi_reply(); } fossil_fatal("Database error: %s", z); } /* ** Check a result code. If it is not SQLITE_OK, print the ** corresponding error message and exit. */ static void db_check_result(int rc, Stmt *pStmt){ if( rc!=SQLITE_OK ){ db_err("SQL error (%d,%d: %s) while running [%s]", rc, sqlite3_extended_errcode(g.db), sqlite3_errmsg(g.db), blob_str(&pStmt->sql)); } } /* ** All static variable that a used by only this file are gathered into ** the following structure. */ static struct DbLocalData { unsigned protectMask; /* Prevent changes to database */ int nBegin; /* Nesting depth of BEGIN */ int doRollback; /* True to force a rollback */ int nCommitHook; /* Number of commit hooks */ int wrTxn; /* Outer-most TNX is a write */ 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[6]; char *azDeleteOnFail[3]; /* Files to delete on a failure */ char *azBeforeCommit[5]; /* Commands to run prior to COMMIT */ int nBeforeCommit; /* Number of entries in azBeforeCommit */ int nPriorChanges; /* sqlite3_total_changes() at transaction start */ const char *zStartFile; /* File in which transaction was started */ int iStartLine; /* Line of zStartFile where transaction started */ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); void *pAuthArg; /* Argument to the authorizer */ const char *zAuthName; /* Name of the authorizer */ int bProtectTriggers; /* True if protection triggers already exist */ int nProtect; /* Slots of aProtect used */ unsigned aProtect[12]; /* Saved values of protectMask */ } db = { PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */ 0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}}; /* ** Arrange for the given file to be deleted on a failure. */ void db_delete_on_failure(const char *zFilename){ assert( db.nDeleteOnFail<count(db.azDeleteOnFail) ); if( zFilename==0 ) return; db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename); } /* ** Return the transaction nesting depth. 0 means we are currently ** not in a transaction. */ int db_transaction_nesting_depth(void){ return db.nBegin; } /* ** Return a pointer to a string that is the code point where the ** current transaction was started. */ char *db_transaction_start_point(void){ return mprintf("%s:%d", db.zStartFile, db.iStartLine); } /* ** This routine is called by the SQLite commit-hook mechanism ** just prior to each commit. All this routine does is verify ** that nBegin really is zero. That insures that transactions ** cannot commit by any means other than by calling db_end_transaction() ** below. ** ** This is just a safety and sanity check. */ static int db_verify_at_commit(void *notUsed){ if( db.nBegin ){ fossil_panic("illegal commit attempt"); return 1; } return 0; } /* ** Silently add the filename and line number as parameter to each ** db_begin_transaction call. */ #if INTERFACE #define db_begin_transaction() db_begin_transaction_real(__FILE__,__LINE__) #define db_begin_write() db_begin_write_real(__FILE__,__LINE__) #define db_commit_transaction() db_end_transaction(0) #define db_rollback_transaction() db_end_transaction(1) #endif /* ** Begin a nested transaction */ void db_begin_transaction_real(const char *zStartFile, int iStartLine){ if( db.nBegin==0 ){ db_multi_exec("BEGIN"); sqlite3_commit_hook(g.db, db_verify_at_commit, 0); db.nPriorChanges = sqlite3_total_changes(g.db); db.doRollback = 0; db.zStartFile = zStartFile; db.iStartLine = iStartLine; db.wrTxn = 0; } db.nBegin++; } /* ** Begin a new transaction for writing. */ void db_begin_write_real(const char *zStartFile, int iStartLine){ if( db.nBegin==0 ){ if( !db_is_writeable("repository") ){ db_multi_exec("BEGIN"); }else{ db_multi_exec("BEGIN IMMEDIATE"); sqlite3_commit_hook(g.db, db_verify_at_commit, 0); db.nPriorChanges = sqlite3_total_changes(g.db); db.doRollback = 0; db.zStartFile = zStartFile; db.iStartLine = iStartLine; db.wrTxn = 1; } }else if( !db.wrTxn ){ fossil_warning("read txn at %s:%d might cause SQLITE_BUSY " "for the write txn at %s:%d", db.zStartFile, db.iStartLine, zStartFile, iStartLine); } db.nBegin++; } /* End a transaction previously started using db_begin_transaction() ** or db_begin_write(). */ void db_end_transaction(int rollbackFlag){ if( g.db==0 ) return; if( db.nBegin<=0 ){ fossil_warning("Extra call to db_end_transaction"); return; } if( rollbackFlag ){ db.doRollback = 1; if( g.fSqlTrace ) fossil_trace("-- ROLLBACK by request\n"); } db.nBegin--; if( db.nBegin==0 ){ int i; if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){ i = 0; db_protect_only(PROTECT_SENSITIVE); 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(); db_protect_pop(); } for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){ int rc = db.aHook[i].xHook(); if( rc ){ db.doRollback = 1; if( g.fSqlTrace ) fossil_trace("-- ROLLBACK due to aHook[%d]\n", i); } } 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 */ void db_force_rollback(void){ int i; static int busy = 0; sqlite3_stmt *pStmt = 0; if( busy || g.db==0 ) return; busy = 1; undo_rollback(); while( (pStmt = sqlite3_next_stmt(g.db,pStmt))!=0 ){ sqlite3_reset(pStmt); } while( db.pAllStmt ){ db_finalize(db.pAllStmt); } if( db.nBegin ){ sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); db.nBegin = 0; } busy = 0; db_close(0); for(i=0; i<db.nDeleteOnFail; i++){ file_delete(db.azDeleteOnFail[i]); } } /* ** Install a commit hook. Hooks are installed in sequence order. ** It is an error to install the same commit hook more than once. ** ** Each commit hook is called (in order of ascending sequence) at ** each commit operation. If any commit hook returns non-zero, ** the subsequence commit hooks are omitted and the transaction ** rolls back rather than commit. It is the responsibility of the ** hooks themselves to issue any error messages. */ void db_commit_hook(int (*x)(void), int sequence){ int i; assert( db.nCommitHook < count(db.aHook) ); for(i=0; i<db.nCommitHook; i++){ assert( x!=db.aHook[i].xHook ); if( db.aHook[i].sequence>sequence ){ int s = sequence; int (*xS)(void) = x; sequence = db.aHook[i].sequence; x = db.aHook[i].xHook; db.aHook[i].sequence = s; db.aHook[i].xHook = xS; } } db.aHook[db.nCommitHook].sequence = sequence; db.aHook[db.nCommitHook].xHook = x; db.nCommitHook++; } #if INTERFACE /* ** Flag bits for db_protect() and db_unprotect() indicating which parts ** of the databases should be write protected or write enabled, respectively. */ #define PROTECT_USER 0x01 /* USER table */ #define PROTECT_CONFIG 0x02 /* CONFIG and GLOBAL_CONFIG tables */ #define PROTECT_SENSITIVE 0x04 /* Sensitive and/or global settings */ #define PROTECT_READONLY 0x08 /* everything except TEMP tables */ #define PROTECT_BASELINE 0x10 /* protection system is working */ #define PROTECT_ALL 0x1f /* All of the above */ #define PROTECT_NONE 0x00 /* Nothing. Everything is open */ #endif /* INTERFACE */ /* ** Enable or disable database write protections. ** ** db_protext(X) Add protects on X ** db_unprotect(X) Remove protections on X ** db_protect_only(X) Remove all prior protections then set ** protections to only X. ** ** Each of these routines pushes the previous protection mask onto ** a finite-size stack. Each should be followed by a call to ** db_protect_pop() to pop the stack and restore the protections that ** existed prior to the call. The protection mask stack has a limited ** depth, so take care not to nest calls too deeply. ** ** About Database Write Protection ** ------------------------------- ** ** This is *not* a primary means of defending the application from ** attack. Fossil should be secure even if this mechanism is disabled. ** The purpose of database write protection is to provide an additional ** layer of defense in case SQL injection bugs somehow slip into other ** parts of the system. In other words, database write protection is ** not the primary defense but rather defense in depth. ** ** This mechanism mostly focuses on the USER table, to prevent an ** attacker from giving themselves Admin privilegs, and on the ** CONFIG table and especially "sensitive" settings such as ** "diff-command" or "editor" that if compromised by an attacker ** could lead to an RCE. ** ** By default, the USER and CONFIG tables are read-only. Various ** subsystems that legitimately need to change those tables can ** temporarily do so using: ** ** db_unprotect(PROTECT_xxx); ** // make the legitmate changes here ** db_protect_pop(); ** ** Code that runs inside of reduced protections should be carefully ** reviewed to ensure that it is harmless and not subject to SQL ** injection. ** ** Read-only operations (such as many web pages like /timeline) ** can invoke db_protect(PROTECT_ALL) to effectively make the database ** read-only. TEMP tables (which are often used for these kinds of ** pages) are still writable, however. ** ** The PROTECT_SENSITIVE protection is a subset of PROTECT_CONFIG ** that blocks changes to all of the global_config table, but only ** "sensitive" settings in the config table. PROTECT_SENSITIVE ** relies on triggers and the protected_setting() SQL function to ** prevent changes to sensitive settings. ** ** PROTECT_READONLY is set for any HTTP request for which the HTTP_REFERER ** is not the same origin. This is an additional defense against cross-site- ** scripting attacks. As with all of these defenses, this is only an extra ** backup layer. Fossil should be proof against XSS attacks even without this. ** ** Any violation of these security restrictions results in a SECURITY message ** in the server log (if enabled). A violation of any of these restrictions ** probably indicates a bug in Fossil and should be reported to the ** developers. ** ** Additional Notes ** ---------------- ** ** Calls to routines like db_set() and db_unset() temporarily disable ** the PROTECT_CONFIG protection. The assumption is that these calls ** cannot be invoked by an SQL injection and are thus safe. Make sure ** this is the case by always using a string literal as the name argument ** to db_set() and db_unset() and friend, not a variable that might ** be compromised by an attack. */ void db_protect_only(unsigned flags){ if( db.nProtect>=count(db.aProtect)-2 ){ fossil_panic("too many db_protect() calls"); } db.aProtect[db.nProtect++] = db.protectMask; if( (flags & PROTECT_SENSITIVE)!=0 && db.bProtectTriggers==0 && g.repositoryOpen ){ /* Create the triggers needed to protect sensitive settings from ** being created or modified the first time that PROTECT_SENSITIVE ** is enabled. Deleting a sensitive setting is harmless, so there ** is not trigger to block deletes. After being created once, the ** triggers persist for the life of the database connection. */ unsigned savedProtectMask = db.protectMask; db.protectMask = 0; db_multi_exec( "CREATE TEMP TRIGGER protect_1 BEFORE INSERT ON config" " WHEN protected_setting(new.name) BEGIN" " SELECT raise(abort,'not authorized');" "END;\n" "CREATE TEMP TRIGGER protect_2 BEFORE UPDATE ON config" " WHEN protected_setting(new.name) BEGIN" " SELECT raise(abort,'not authorized');" "END;\n" ); db.bProtectTriggers = 1; db.protectMask = savedProtectMask; } db.protectMask = flags; } void db_protect(unsigned flags){ db_protect_only(db.protectMask | flags); } void db_unprotect(unsigned flags){ if( db.nProtect>=count(db.aProtect)-2 ){ fossil_panic("too many db_unprotect() calls"); } db.aProtect[db.nProtect++] = db.protectMask; db.protectMask &= ~(flags|PROTECT_READONLY); } void db_protect_pop(void){ if( db.nProtect<1 ){ fossil_panic("too many db_protect_pop() calls"); } db.protectMask = db.aProtect[--db.nProtect]; } int db_is_protected(unsigned flags){ return (db.protectMask & flags)!=0; } /* ** Verify that the desired database write protections are in place. ** Throw a fatal error if not. */ void db_assert_protected(unsigned flags){ if( (flags & db.protectMask)!=flags ){ fossil_fatal("missing database write protection bits: %02x", flags & ~db.protectMask); } } /* ** Assert that either all protections are off (including PROTECT_BASELINE ** which is usually always enabled), or the setting named in the argument ** is no a sensitive setting. ** ** This assert() is used to verify that the db_set() and db_set_int() ** interfaces do not modify a sensitive setting. */ void db_assert_protection_off_or_not_sensitive(const char *zName){ if( db.protectMask!=0 && db_setting_is_protected(zName) ){ fossil_panic("unauthorized change to protected setting \"%s\"", zName); } } /* ** Every Fossil database connection automatically registers the following ** overarching authenticator callback, and leaves it registered for the ** duration of the connection. This authenticator will call any ** sub-authenticators that are registered using db_set_authorizer(). ** ** == Testing Notes == ** ** Run Fossil as using a command like this: ** ** ./fossil sql --test --errorlog - ** ** Then enter SQL commands like one of these: ** ** SELECT db_protect('user'); ** SELECT db_protect('config'); ** SELECT db_protect('sensitive'); ** SELECT db_protect('readonly'); ** SELECT db_protect('all'); ** ** Then try to do SQL statements that would violate the constraints and ** verify that SECURITY warnings appear in the error log output. See ** also the sqlcmd_db_protect() function in sqlcmd.c. */ int db_top_authorizer( void *pNotUsed, int eCode, const char *z0, const char *z1, const char *z2, const char *z3 ){ int rc = SQLITE_OK; switch( eCode ){ case SQLITE_INSERT: case SQLITE_UPDATE: case SQLITE_DELETE: { if( (db.protectMask & PROTECT_USER)!=0 && sqlite3_stricmp(z0,"user")==0 ){ fossil_errorlog( "SECURITY: authorizer blocks DML on protected USER table\n"); rc = SQLITE_DENY; }else if( (db.protectMask & PROTECT_CONFIG)!=0 && (sqlite3_stricmp(z0,"config")==0 || sqlite3_stricmp(z0,"global_config")==0) ){ fossil_errorlog( "SECURITY: authorizer blocks DML on protected table \"%s\"\n", z0); rc = SQLITE_DENY; }else if( (db.protectMask & PROTECT_SENSITIVE)!=0 && sqlite3_stricmp(z0,"global_config")==0 ){ fossil_errorlog( "SECURITY: authorizer blocks DML on protected GLOBAL_CONFIG table\n"); rc = SQLITE_DENY; }else if( (db.protectMask & PROTECT_READONLY)!=0 && (sqlite3_stricmp(z2, "repository")==0 || sqlite3_stricmp(z2,"configdb")==0 || sqlite3_stricmp(z2,"localdb")==0) ){ /* The READONLY constraint only applies to persistent database files. ** "temp" and "mem1" and other transient databases are not ** constrained by READONLY. */ fossil_errorlog( "SECURITY: authorizer blocks DML on table \"%s\" due to the " "request coming from a different origin\n", z0); rc = SQLITE_DENY; } break; } case SQLITE_DROP_TEMP_TRIGGER: { /* Do not allow the triggers that enforce PROTECT_SENSITIVE ** to be dropped */ fossil_errorlog( "SECURITY: authorizer blocks attempt to drop a temporary trigger\n"); rc = SQLITE_DENY; break; } } if( db.xAuth && rc==SQLITE_OK ){ rc = db.xAuth(db.pAuthArg, eCode, z0, z1, z2, z3); } return rc; } /* ** Set or unset the query authorizer callback function */ void db_set_authorizer( int(*xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pArg, const char *zName /* for tracing */ ){ if( db.xAuth ){ fossil_panic("multiple active db_set_authorizer() calls"); } db.xAuth = xAuth; db.pAuthArg = pArg; db.zAuthName = zName; if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName); } void db_clear_authorizer(void){ if( db.zAuthName && g.fSqlTrace ){ fossil_trace("-- discontinue authorizer %s\n", db.zAuthName); } db.xAuth = 0; db.pAuthArg = 0; db.zAuthName = 0; } #if INTERFACE /* ** Possible flags to db_vprepare */ #define DB_PREPARE_IGNORE_ERROR 0x001 /* Suppress errors */ #define DB_PREPARE_PERSISTENT 0x002 /* Stmt will stick around for a while */ #endif /* ** Prepare a Stmt. Assume that the Stmt is previously uninitialized. ** If the input string contains multiple SQL statements, only the first ** one is processed. All statements beyond the first are silently ignored. */ int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){ int rc; int prepFlags = 0; char *zSql; const char *zExtra = 0; blob_zero(&pStmt->sql); blob_vappendf(&pStmt->sql, zFormat, ap); va_end(ap); zSql = blob_str(&pStmt->sql); db.nPrepare++; if( flags & DB_PREPARE_PERSISTENT ){ prepFlags = SQLITE_PREPARE_PERSISTENT; } rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra); if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){ db_err("%s\n%s", sqlite3_errmsg(g.db), zSql); }else if( zExtra && !fossil_all_whitespace(zExtra) ){ db_err("surplus text follows SQL: \"%s\"", zExtra); } pStmt->pNext = db.pAllStmt; pStmt->pPrev = 0; if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt; db.pAllStmt = pStmt; pStmt->nStep = 0; pStmt->rc = rc; return rc; } int db_prepare(Stmt *pStmt, const char *zFormat, ...){ int rc; va_list ap; va_start(ap, zFormat); rc = db_vprepare(pStmt, 0, zFormat, ap); va_end(ap); return rc; } int db_prepare_ignore_error(Stmt *pStmt, const char *zFormat, ...){ int rc; va_list ap; va_start(ap, zFormat); rc = db_vprepare(pStmt, DB_PREPARE_IGNORE_ERROR, zFormat, ap); va_end(ap); return rc; } /* This variant of db_prepare() checks to see if the statement has ** already been prepared, and if it has it becomes a no-op. */ int db_static_prepare(Stmt *pStmt, const char *zFormat, ...){ int rc = SQLITE_OK; if( blob_size(&pStmt->sql)==0 ){ va_list ap; va_start(ap, zFormat); rc = db_vprepare(pStmt, DB_PREPARE_PERSISTENT, zFormat, ap); va_end(ap); } return rc; } /* Return TRUE if static Stmt object pStmt has been initialized. */ int db_static_stmt_is_init(Stmt *pStmt){ return blob_size(&pStmt->sql)>0; } /* Prepare a statement using text placed inside a Blob ** using blob_append_sql(). */ int db_prepare_blob(Stmt *pStmt, Blob *pSql){ int rc; char *zSql; pStmt->sql = *pSql; blob_init(pSql, 0, 0); zSql = blob_sql_text(&pStmt->sql); db.nPrepare++; rc = sqlite3_prepare_v3(g.db, zSql, -1, 0, &pStmt->pStmt, 0); if( rc!=0 ){ db_err("%s\n%s", sqlite3_errmsg(g.db), zSql); } pStmt->pNext = pStmt->pPrev = 0; pStmt->nStep = 0; pStmt->rc = rc; return rc; } /* ** Return the index of a bind parameter */ static int paramIdx(Stmt *pStmt, const char *zParamName){ int i = sqlite3_bind_parameter_index(pStmt->pStmt, zParamName); if( i==0 ){ |
︙ | ︙ | |||
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | } 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); } /* bind_str() treats a Blob object like a TEXT string and binds it | > > > > | > | 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 | } 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); } /* bind_str() treats a Blob object like a TEXT string and binds it ** to the SQL variable. Contrast this to bind_blob() which treats ** the Blob object like an SQL BLOB. */ int db_bind_str(Stmt *pStmt, const char *zParamName, Blob *pBlob){ return sqlite3_bind_text(pStmt->pStmt, paramIdx(pStmt, zParamName), blob_buffer(pBlob), blob_size(pBlob), SQLITE_STATIC); } /* ** Step the SQL statement. Return either SQLITE_ROW or an error code ** or SQLITE_OK if the statement finishes successfully. */ int db_step(Stmt *pStmt){ int rc; if( pStmt->pStmt==0 ) return pStmt->rc; rc = sqlite3_step(pStmt->pStmt); pStmt->nStep++; return rc; } /* ** Print warnings if a query is inefficient. |
︙ | ︙ | |||
291 292 293 294 295 296 297 | } /* ** Reset or finalize a statement. */ int db_reset(Stmt *pStmt){ int rc; | | | < < < < < | | > > > > > | | > > > > > > > > > > > > > > > | > | < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | < < | < > > > > > > > > > > > > > > | | | | | | | < < < < > > > | < < < | < < < < | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | < > | | > > > > > | > > | > > > | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | > > > > > > > > > > > > > > | > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | > > | < < < < < < | < > | < < < < > | < < < < | | | | < | > > | | < < < < < < < < < > < > > > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < | | | > > | > > > > > | > > > > > | | < < | < | | | | < < | > > | < > > | < < < < < | < < | | < | > > > > > > | > > > | > > > > | | < | | | | < < < < < < | | > > > | > | > > > | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > | | > > > | > > > | < < > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > | | > > > > > > > > > > | | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > < > | | > | > | < | > | | > | > > | > > > | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | | > > | | | | | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < > > > | | | > > > > > > > > > > > > > > > > > > > < | > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > | > > > | | | > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | | | | | | > > | > > > > > | | > > | | > > | | | > > > > | > > > > > > > > > > > > > > > > > > > | | | | | | | | > | < < < < < < < < < < < < < < < < < < | | | | | | | | | | > > > > > > > > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < | > > > > > > > > > > > > > > | > > > | | | > | | > | > > > > > > > > > > > | > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | | > > > > > > > > | > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > | | | > | > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > | < | > > > > | > > > > > > > > > | > > > > > > > > > > > > > > | > > > > > > | > > > > | > | > > > > > > > > > > > > > > > > > > > | > | | > > | > > | > > > > | < > > | < > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > | | > | | > > > > > > > > > > > > | | < | > | > > > > > > > > > > > > > > > > | | > > > | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | | | < | < < < < | < < < < < < < | < < < < < < | | | | | | < < < < < < < | < > | > > < < | < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > | > | > > > > > > > > | | | < | < | > > > > > > > > > > > > > > > > > > > > | | | > | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 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 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 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 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 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 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 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 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 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 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 5433 5434 5435 5436 5437 5438 5439 5440 5441 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 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 | } /* ** Reset or finalize a statement. */ int db_reset(Stmt *pStmt){ int rc; if( g.fSqlStats ){ db_stats(pStmt); } rc = sqlite3_reset(pStmt->pStmt); db_check_result(rc, pStmt); return rc; } int db_finalize(Stmt *pStmt){ int rc; if( pStmt->pNext ){ pStmt->pNext->pPrev = pStmt->pPrev; } if( pStmt->pPrev ){ pStmt->pPrev->pNext = pStmt->pNext; }else if( db.pAllStmt==pStmt ){ db.pAllStmt = pStmt->pNext; } pStmt->pNext = 0; pStmt->pPrev = 0; if( g.fSqlStats ){ db_stats(pStmt); } blob_reset(&pStmt->sql); rc = sqlite3_finalize(pStmt->pStmt); db_check_result(rc, pStmt); pStmt->pStmt = 0; return rc; } /* ** Return the rowid of the most recent insert */ int db_last_insert_rowid(void){ i64 x = sqlite3_last_insert_rowid(g.db); if( x<0 || x>(i64)2147483647 ){ fossil_panic("rowid out of range (0..2147483647)"); } return (int)x; } /* ** Return the number of rows that were changed by the most recent ** INSERT, UPDATE, or DELETE. Auxiliary changes caused by triggers ** or other side effects are not counted. */ int db_changes(void){ 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){ return sqlite3_column_int64(pStmt->pStmt, N); } double db_column_double(Stmt *pStmt, int N){ return sqlite3_column_double(pStmt->pStmt, N); } const char *db_column_text(Stmt *pStmt, int N){ return (char*)sqlite3_column_text(pStmt->pStmt, N); } const char *db_column_raw(Stmt *pStmt, int N){ return (const char*)sqlite3_column_blob(pStmt->pStmt, N); } const char *db_column_name(Stmt *pStmt, int N){ return (char*)sqlite3_column_name(pStmt->pStmt, N); } int db_column_count(Stmt *pStmt){ return sqlite3_column_count(pStmt->pStmt); } char *db_column_malloc(Stmt *pStmt, int N){ return mprintf("%s", db_column_text(pStmt, N)); } void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){ blob_append(pBlob, sqlite3_column_blob(pStmt->pStmt, N), sqlite3_column_bytes(pStmt->pStmt, N)); } Blob db_column_text_as_blob(Stmt *pStmt, int N){ Blob x; blob_init(&x, (char*)sqlite3_column_text(pStmt->pStmt,N), sqlite3_column_bytes(pStmt->pStmt,N)); return x; } /* ** Initialize a blob to an ephemeral copy of the content of a ** column in the current row. The data in the blob will become ** invalid when the statement is stepped or reset. */ void db_ephemeral_blob(Stmt *pStmt, int N, Blob *pBlob){ blob_init(pBlob, sqlite3_column_blob(pStmt->pStmt, N), sqlite3_column_bytes(pStmt->pStmt, N)); } /* ** Execute a single prepared statement until it finishes. */ int db_exec(Stmt *pStmt){ int rc; while( (rc = db_step(pStmt))==SQLITE_ROW ){} rc = db_reset(pStmt); db_check_result(rc, pStmt); return rc; } /* ** COMMAND: test-db-exec-error ** Usage: %fossil test-db-exec-error ** ** Invoke the db_exec() interface with an erroneous SQL statement ** in order to verify the error handling logic. */ void db_test_db_exec_cmd(void){ Stmt err; db_find_and_open_repository(0,0); db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);"); db_exec(&err); } /* ** COMMAND: test-db-prepare ** Usage: %fossil test-db-prepare ?OPTIONS? SQL-STATEMENT ** ** Options: ** --auth-report Enable the ticket report query authorizer ** --auth-ticket Enable the ticket schema query authorizer ** ** Invoke db_prepare() on the SQL input. Report any errors encountered. ** This command is used to verify error detection logic in the db_prepare() ** utility routine. */ void db_test_db_prepare(void){ const int fAuthReport = find_option("auth-report",0,0)!=0; const int fAuthSchema = find_option("auth-ticket",0,0)!=0; char * zReportErr = 0; /* auth-report error string. */ int nSchemaErr = 0; /* Number of auth-ticket errors. */ Stmt err; if(fAuthReport + fAuthSchema > 1){ fossil_fatal("Only one of --auth-report or --auth-ticket " "may be used."); } db_find_and_open_repository(0,0); verify_all_options(); if( g.argc!=3 ) usage("?OPTIONS? SQL"); if(fAuthReport){ report_restrict_sql(&zReportErr); }else if(fAuthSchema){ ticket_restrict_sql(&nSchemaErr); } db_prepare(&err, "%s", g.argv[2]/*safe-for-%s*/); db_finalize(&err); if(fAuthReport){ report_unrestrict_sql(); if(zReportErr){ fossil_warning("Report authorizer error: %s\n", zReportErr); fossil_free(zReportErr); } }else if(fAuthSchema){ ticket_unrestrict_sql(); if(nSchemaErr){ fossil_warning("Ticket schema authorizer error count: %d\n", nSchemaErr); } } } /* ** 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. The input text is executed ** directly without any formatting. */ int db_exec_sql(const char *z){ int rc = SQLITE_OK; sqlite3_stmt *pStmt; const char *zEnd; 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; } return rc; } /* ** Execute multiple SQL statements using printf-style formatting. */ int db_multi_exec(const char *zSql, ...){ Blob sql; int rc; va_list ap; blob_init(&sql, 0, 0); va_start(ap, zSql); blob_vappendf(&sql, zSql, ap); va_end(ap); rc = db_exec_sql(blob_str(&sql)); blob_reset(&sql); return rc; } /* ** Optionally make the following changes to the database if feasible and ** convenient. Do not start a transaction for these changes, but only ** make these changes if other changes are also being made. */ void db_optional_sql(const char *zDb, const char *zSql, ...){ if( db_is_writeable(zDb) && db.nBeforeCommit < count(db.azBeforeCommit) ){ va_list ap; va_start(ap, zSql); db.azBeforeCommit[db.nBeforeCommit++] = sqlite3_vmprintf(zSql, ap); va_end(ap); } } /* ** Execute a query and return a single integer value. */ i64 db_int64(i64 iDflt, const char *zSql, ...){ va_list ap; Stmt s; i64 rc; va_start(ap, zSql); db_vprepare(&s, 0, zSql, ap); va_end(ap); if( db_step(&s)!=SQLITE_ROW ){ rc = iDflt; }else{ rc = db_column_int64(&s, 0); } db_finalize(&s); return rc; } int db_int(int iDflt, const char *zSql, ...){ va_list ap; Stmt s; int rc; va_start(ap, zSql); db_vprepare(&s, 0, zSql, ap); va_end(ap); if( db_step(&s)!=SQLITE_ROW ){ rc = iDflt; }else{ rc = db_column_int(&s, 0); } db_finalize(&s); return rc; } /* ** Return TRUE if the query would return 1 or more rows. Return ** FALSE if the query result would be an empty set. */ int db_exists(const char *zSql, ...){ va_list ap; Stmt s; int rc; va_start(ap, zSql); db_vprepare(&s, 0, zSql, ap); va_end(ap); if( db_step(&s)!=SQLITE_ROW ){ rc = 0; }else{ rc = 1; } db_finalize(&s); return rc; } /* ** Execute a query and return a floating-point value. */ double db_double(double rDflt, const char *zSql, ...){ va_list ap; Stmt s; double r; va_start(ap, zSql); db_vprepare(&s, 0, zSql, ap); va_end(ap); if( db_step(&s)!=SQLITE_ROW ){ r = rDflt; }else{ r = db_column_double(&s, 0); } db_finalize(&s); return r; } /* ** Execute a query and append the first column of the first row ** of the result set to blob given in the first argument. */ void db_blob(Blob *pResult, const char *zSql, ...){ va_list ap; Stmt s; va_start(ap, zSql); db_vprepare(&s, 0, zSql, ap); va_end(ap); if( db_step(&s)==SQLITE_ROW ){ blob_append(pResult, sqlite3_column_blob(s.pStmt, 0), sqlite3_column_bytes(s.pStmt, 0)); } db_finalize(&s); } /* ** 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 ){ z = mprintf("%s", sqlite3_column_text(s.pStmt, 0)); }else if( zDefault ){ z = mprintf("%s", zDefault); }else{ z = 0; } db_finalize(&s); return z; } /* ** Initialize a new database file with the given schema. If anything ** goes wrong, call db_err() to exit. ** ** If zFilename is NULL, then create an empty repository in an in-memory ** database. */ void db_init_database( const char *zFileName, /* Name of database file to create */ const char *zSchema, /* First part of schema */ ... /* Additional SQL to run. Terminate with NULL. */ ){ sqlite3 *xdb; int rc; const char *zSql; va_list ap; xdb = db_open(zFileName ? zFileName : ":memory:"); sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0); rc = sqlite3_exec(xdb, zSchema, 0, 0, 0); if( rc!=SQLITE_OK ){ db_err("%s", sqlite3_errmsg(xdb)); } va_start(ap, zSchema); while( (zSql = va_arg(ap, const char*))!=0 ){ rc = sqlite3_exec(xdb, zSql, 0, 0, 0); if( rc!=SQLITE_OK ){ db_err("%s", sqlite3_errmsg(xdb)); } } va_end(ap); sqlite3_exec(xdb, "COMMIT", 0, 0, 0); if( zFileName || g.db!=0 ){ sqlite3_close(xdb); }else{ g.db = xdb; } } /* ** Function to return the number of seconds since 1970. This is ** the same as strftime('%s','now') but is more compact. */ void db_now_function( sqlite3_context *context, int argc, 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); } } } /* ** The toLocal() SQL function returns a string that is an argument to a ** date/time function that is appropriate for modifying the time for display. ** If UTC time display is selected, no modification occurs. If local time ** display is selected, the time is adjusted appropriately. ** ** Example usage: ** ** SELECT datetime('now',toLocal()); */ void db_tolocal_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ if( g.fTimeFormat==0 ){ if( db_get_int("timeline-utc", 1) ){ g.fTimeFormat = 1; }else{ g.fTimeFormat = 2; } } if( g.fTimeFormat==1 ){ sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC); }else{ sqlite3_result_text(context, "localtime", -1, SQLITE_STATIC); } } /* ** The fromLocal() SQL function returns a string that is an argument to a ** date/time function that is appropriate to convert an input time to UTC. ** If UTC time display is selected, no modification occurs. If local time ** display is selected, the time is adjusted from local to UTC. ** ** Example usage: ** ** SELECT julianday(:user_input,fromLocal()); */ void db_fromlocal_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ if( g.fTimeFormat==0 ){ if( db_get_int("timeline-utc", 1) ){ g.fTimeFormat = 1; }else{ g.fTimeFormat = 2; } } if( g.fTimeFormat==1 ){ sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC); }else{ sqlite3_result_text(context, "utc", -1, SQLITE_STATIC); } } /* ** If the input is a hexadecimal string, convert that string into a BLOB. ** If the input is not a hexadecimal string, return NULL. */ void db_hextoblob( sqlite3_context *context, int argc, sqlite3_value **argv ){ const unsigned char *zIn = sqlite3_value_text(argv[0]); int nIn = sqlite3_value_bytes(argv[0]); unsigned char *zOut; if( zIn==0 ) return; if( nIn&1 ) return; if( !validate16((const char*)zIn, nIn) ) return; zOut = sqlite3_malloc64( nIn/2 + 1 ); if( zOut==0 ){ sqlite3_result_error_nomem(context); return; } decode16(zIn, zOut, nIn); sqlite3_result_blob(context, zOut, nIn/2, sqlite3_free); } /* ** Return the XOR-obscured version of the input text. Useful for ** updating authentication strings in Fossil settings. To change ** the password locally stored for sync, for instance: ** ** echo "UPDATE config ** SET value = obscure('monkey123') ** WHERE name = 'last-sync-pw'" | ** fossil sql ** ** Note that user.pw uses a different obscuration algorithm, but ** you don't need to use 'fossil sql' for that anyway. Just call ** ** fossil user pass monkey123 ** ** to change the local user entry's password in the same way. ** ** 2022-12-30: If the user-data pointer is not NULL, then operate ** as unobscure() rather than obscure(). The obscure() variant of ** this routine is commonly available. But unobscure is (currently) ** only registered by the "fossil remote config-data --show-passwords" ** command. */ void db_obscure( sqlite3_context *context, int argc, sqlite3_value **argv ){ const unsigned char *zIn = sqlite3_value_text(argv[0]); int nIn = sqlite3_value_bytes(argv[0]); char *zOut, *zTemp; if( 0==zIn ) return; if( 0==(zOut = sqlite3_malloc64( nIn * 2 + 3 )) ){ sqlite3_result_error_nomem(context); return; } if( sqlite3_user_data(context)==0 ){ zTemp = obscure((char*)zIn); }else{ zTemp = unobscure((char*)zIn); } strcpy(zOut, zTemp); fossil_free(zTemp); sqlite3_result_text(context, zOut, strlen(zOut), sqlite3_free); } /* ** Return True if zName is a protected (a.k.a. "sensitive") setting. */ int db_setting_is_protected(const char *zName){ const Setting *pSetting = zName ? db_find_setting(zName,0) : 0; return pSetting!=0 && pSetting->sensitive!=0; } /* ** Implement the protected_setting(X) SQL function. This function returns ** true if X is the name of a protected (security-sensitive) setting and ** the db.protectSensitive flag is enabled. It returns false otherwise. */ LOCAL void db_protected_setting_func( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zSetting; if( (db.protectMask & PROTECT_SENSITIVE)==0 ){ sqlite3_result_int(context, 0); return; } zSetting = (const char*)sqlite3_value_text(argv[0]); sqlite3_result_int(context, db_setting_is_protected(zSetting)); } /* ** Copied from SQLite ext/misc/uint.c... ** ** Compare text in lexicographic order, except strings of digits ** compare in numeric order. ** ** This version modified to also ignore case. */ static int uintNocaseCollFunc( void *notUsed, int nKey1, const void *pKey1, int nKey2, const void *pKey2 ){ const unsigned char *zA = (const unsigned char*)pKey1; const unsigned char *zB = (const unsigned char*)pKey2; int i=0, j=0, x; (void)notUsed; while( i<nKey1 && j<nKey2 ){ if( fossil_isdigit(zA[i]) && fossil_isdigit(zB[j]) ){ int k; while( i<nKey1 && zA[i]=='0' ){ i++; } while( j<nKey2 && zB[j]=='0' ){ j++; } k = 0; while( i+k<nKey1 && fossil_isdigit(zA[i+k]) && j+k<nKey2 && fossil_isdigit(zB[j+k]) ){ k++; } if( i+k<nKey1 && fossil_isdigit(zA[i+k]) ){ return +1; }else if( j+k<nKey2 && fossil_isdigit(zB[j+k]) ){ return -1; }else{ x = memcmp(zA+i, zB+j, k); if( x ) return x; i += k; j += k; } }else if( zA[i]!=zB[j] && (x = fossil_tolower(zA[i]) - fossil_tolower(zB[j]))!=0 ){ return x; }else{ i++; j++; } } return (nKey1 - i) - (nKey2 - j); } /* ** 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_collation(db, "uintnocase", SQLITE_UTF8,0,uintNocaseCollFunc); 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); sqlite3_create_function(db, "toLocal", 0, SQLITE_UTF8, 0, db_tolocal_function, 0, 0); sqlite3_create_function(db, "fromLocal", 0, SQLITE_UTF8, 0, db_fromlocal_function, 0, 0); sqlite3_create_function(db, "hextoblob", 1, SQLITE_UTF8, 0, db_hextoblob, 0, 0); sqlite3_create_function(db, "capunion", 1, SQLITE_UTF8, 0, 0, capability_union_step, capability_union_finalize); sqlite3_create_function(db, "fullcap", 1, SQLITE_UTF8, 0, capability_fullcap, 0, 0); sqlite3_create_function(db, "find_emailaddr", 1, SQLITE_UTF8, 0, alert_find_emailaddr_func, 0, 0); sqlite3_create_function(db, "display_name", 1, SQLITE_UTF8, 0, alert_display_name_func, 0, 0); sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0, db_obscure, 0, 0); sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0, db_protected_setting_func, 0, 0); sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0, db_win_reserved_func,0,0); sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0, url_nouser_func,0,0); sqlite3_create_function(db, "chat_msg_from_event", 4, SQLITE_UTF8 | SQLITE_INNOCUOUS, 0, chat_msg_from_event, 0, 0); } #if USE_SEE /* ** This is a pointer to the saved database encryption key string. */ static char *zSavedKey = 0; /* ** This is the size of the saved database encryption key, in bytes. */ static size_t savedKeySize = 0; /* ** This function returns non-zero if there is a saved database encryption ** key available. */ int db_have_saved_encryption_key(){ return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize); } /* ** This function returns non-zero if the specified database encryption key ** is valid. */ int db_is_valid_saved_encryption_key(const char *p, size_t n){ if( p==0 ) return 0; if( n==0 ) return 0; if( p[0]==0 ) return 0; return 1; } /* ** This function returns the saved database encryption key -OR- zero if ** no database encryption key is saved. */ char *db_get_saved_encryption_key(){ return zSavedKey; } /* ** This function returns the size of the saved database encryption key ** -OR- zero if no database encryption key is saved. */ size_t db_get_saved_encryption_key_size(){ return savedKeySize; } /* ** This function arranges for the saved database encryption key buffer ** to be allocated and then sets up the environment variable to allow ** a child process to initialize it with the actual database encryption ** key. */ void db_setup_for_saved_encryption_key(){ void *p = NULL; size_t n = 0; size_t pageSize = 0; Blob pidKey; assert( !db_have_saved_encryption_key() ); db_unsave_encryption_key(); fossil_get_page_size(&pageSize); assert( pageSize>0 ); p = fossil_secure_alloc_page(&n); assert( p!=NULL ); assert( n==pageSize ); blob_zero(&pidKey); blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n); fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey)); zSavedKey = p; savedKeySize = n; } /* ** This function arranges for the database encryption key to be securely ** saved in non-pagable memory (on platforms where this is possible). */ static void db_save_encryption_key( Blob *pKey ){ void *p = NULL; size_t n = 0; size_t pageSize = 0; size_t blobSize = 0; assert( !db_have_saved_encryption_key() ); blobSize = blob_size(pKey); if( blobSize==0 ) return; fossil_get_page_size(&pageSize); assert( pageSize>0 ); if( blobSize>pageSize ){ fossil_panic("key blob too large: %u versus %u", blobSize, pageSize); } p = fossil_secure_alloc_page(&n); assert( p!=NULL ); assert( n==pageSize ); assert( n>=blobSize ); memcpy(p, blob_str(pKey), blobSize); zSavedKey = p; savedKeySize = n; } /* ** This function arranges for the saved database encryption key to be ** securely zeroed, unlocked (if necessary), and freed. */ void db_unsave_encryption_key(){ fossil_secure_free_page(zSavedKey, savedKeySize); zSavedKey = NULL; savedKeySize = 0; } /* ** This function sets the saved database encryption key to the specified ** string value, allocating or freeing the underlying memory if needed. */ static void db_set_saved_encryption_key( Blob *pKey ){ if( zSavedKey!=NULL ){ size_t blobSize = blob_size(pKey); if( blobSize==0 ){ db_unsave_encryption_key(); }else{ if( blobSize>savedKeySize ){ fossil_panic("key blob too large: %u versus %u", blobSize, savedKeySize); } fossil_secure_zero(zSavedKey, savedKeySize); memcpy(zSavedKey, blob_str(pKey), blobSize); } }else{ db_save_encryption_key(pKey); } } /* ** WEBPAGE: setseekey ** ** Sets the sets the saved database encryption key to one that gets passed ** via the "key" query string parameter. If the saved database encryption ** key has already been set, does nothing. This web page does not produce ** any output on success or failure. No permissions are required and none ** are checked (partially due to lack of encrypted database access). ** ** Query parameters: ** ** key The string to set as the saved database encryption ** key. */ void db_set_see_key_page(void){ Blob key; const char *zKey; if( db_have_saved_encryption_key() ){ fossil_trace("SEE: encryption key was already set\n"); return; } zKey = P("key"); blob_init(&key, 0, 0); if( zKey!=0 ){ PID_T processId; blob_set(&key, zKey); db_set_saved_encryption_key(&key); processId = db_maybe_handle_saved_encryption_key_for_process( SEE_KEY_WRITE ); fossil_trace("SEE: set encryption key for process %lu, length %u\n", (unsigned long)processId, blob_size(&key)); }else{ fossil_trace("SEE: no encryption key specified\n"); } blob_reset(&key); } /* ** WEBPAGE: unsetseekey ** ** Sets the saved database encryption key to zeros in the current and parent ** Fossil processes. This web page does not produce any output on success ** or failure. Setup permission is required. */ void db_unset_see_key_page(void){ PID_T processId; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } processId = db_maybe_handle_saved_encryption_key_for_process( SEE_KEY_ZERO ); fossil_trace("SEE: unset encryption key for process %lu\n", (unsigned long)processId); } /* ** This function reads the saved database encryption key from the ** specified Fossil parent process. This is only necessary (or ** functional) on Windows or Linux. */ static void db_read_saved_encryption_key_from_process( PID_T processId, /* Identifier for Fossil parent process. */ LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ SIZE_T nSize /* Size of saved key buffer in the parent process. */ ){ void *p = NULL; size_t n = 0; size_t pageSize = 0; fossil_get_page_size(&pageSize); assert( pageSize>0 ); if( nSize>pageSize ){ fossil_panic("key too large: %u versus %u", nSize, pageSize); } p = fossil_secure_alloc_page(&n); assert( p!=NULL ); assert( n==pageSize ); assert( n>=nSize ); { #if defined(_WIN32) HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId); if( hProcess!=NULL ){ SIZE_T nRead = 0; if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){ CloseHandle(hProcess); if( nRead==nSize ){ db_unsave_encryption_key(); zSavedKey = p; savedKeySize = n; }else{ fossil_secure_free_page(p, n); fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu", nRead, nSize, pAddress, processId); } }else{ CloseHandle(hProcess); fossil_secure_free_page(p, n); fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize, pAddress, processId, GetLastError()); } }else{ fossil_secure_free_page(p, n); fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); } #elif defined(__linux__) ssize_t nRead; struct iovec liov = {0}; struct iovec riov = {0}; liov.iov_base = p; liov.iov_len = n; riov.iov_base = pAddress; riov.iov_len = nSize; nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0); if( nRead==nSize ){ db_unsave_encryption_key(); zSavedKey = p; savedKeySize = n; }else{ fossil_secure_free_page(p, n); fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu", nRead, nSize, pAddress, (unsigned long)processId); } #else fossil_secure_free_page(p, n); fossil_trace("db_read_saved_encryption_key_from_process unsupported"); #endif } } /* ** This function writes the saved database encryption key into the ** specified Fossil parent process. This is only necessary (or ** functional) on Windows or Linux. */ static void db_write_saved_encryption_key_to_process( PID_T processId, /* Identifier for Fossil parent process. */ LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ SIZE_T nSize /* Size of saved key buffer in the parent process. */ ){ void *p = db_get_saved_encryption_key(); size_t n = db_get_saved_encryption_key_size(); size_t pageSize = 0; fossil_get_page_size(&pageSize); assert( pageSize>0 ); if( nSize>pageSize ){ fossil_panic("key too large: %u versus %u", nSize, pageSize); } assert( p!=NULL ); assert( n==pageSize ); assert( n>=nSize ); { #if defined(_WIN32) HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE, FALSE, processId); if( hProcess!=NULL ){ SIZE_T nWrite = 0; if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){ CloseHandle(hProcess); if( nWrite!=nSize ){ fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu", nWrite, nSize, pAddress, processId); } }else{ CloseHandle(hProcess); fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize, pAddress, processId, GetLastError()); } }else{ fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); } #elif defined(__linux__) ssize_t nWrite; struct iovec liov = {0}; struct iovec riov = {0}; liov.iov_base = p; liov.iov_len = n; riov.iov_base = pAddress; riov.iov_len = nSize; nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0); if( nWrite!=nSize ){ fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu", nWrite, nSize, pAddress, (unsigned long)processId); } #else fossil_trace("db_write_saved_encryption_key_to_process unsupported"); #endif } } /* ** This function zeros the saved database encryption key in the specified ** Fossil parent process. This is only necessary (or functional) on ** Windows or Linux. */ static void db_zero_saved_encryption_key_in_process( PID_T processId, /* Identifier for Fossil parent process. */ LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ SIZE_T nSize /* Size of saved key buffer in the parent process. */ ){ void *p = NULL; size_t n = 0; size_t pageSize = 0; fossil_get_page_size(&pageSize); assert( pageSize>0 ); if( nSize>pageSize ){ fossil_panic("key too large: %u versus %u", nSize, pageSize); } p = fossil_secure_alloc_page(&n); assert( p!=NULL ); assert( n==pageSize ); assert( n>=nSize ); { #if defined(_WIN32) HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE, FALSE, processId); if( hProcess!=NULL ){ SIZE_T nWrite = 0; if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){ CloseHandle(hProcess); fossil_secure_free_page(p, n); if( nWrite!=nSize ){ fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu", nWrite, nSize, pAddress, processId); } }else{ CloseHandle(hProcess); fossil_secure_free_page(p, n); fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize, pAddress, processId, GetLastError()); } }else{ fossil_secure_free_page(p, n); fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); } #elif defined(__linux__) ssize_t nWrite; struct iovec liov = {0}; struct iovec riov = {0}; liov.iov_base = p; liov.iov_len = n; riov.iov_base = pAddress; riov.iov_len = nSize; nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0); if( nWrite!=nSize ){ fossil_secure_free_page(p, n); fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu", nWrite, nSize, pAddress, (unsigned long)processId); } #else fossil_secure_free_page(p, n); fossil_trace("db_zero_saved_encryption_key_in_process unsupported"); #endif } } /* ** This function evaluates the specified TH1 script and attempts to parse ** its result as a colon-delimited triplet containing a process identifier, ** address, and size (in bytes) of the database encryption key. This is ** only necessary (or functional) on Windows or Linux. */ static PID_T db_handle_saved_encryption_key_for_process_via_th1( const char *zConfig, /* The TH1 script to evaluate. */ int eType /* Non-zero to write key to parent process -OR- * zero to read it from the parent process. */ ){ PID_T processId = 0; int rc; char *zResult; char *zPwd = file_getcwd(0, 0); Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO); rc = Th_Eval(g.interp, 0, zConfig, -1); zResult = (char*)Th_GetResult(g.interp, 0); if( rc!=TH_OK ){ fossil_fatal("script for pid key failed: %s", zResult); } if( zResult ){ LPVOID pAddress = NULL; SIZE_T nSize = 0; parse_pid_key_value(zResult, &processId, &pAddress, &nSize); if( eType==SEE_KEY_READ ){ db_read_saved_encryption_key_from_process(processId, pAddress, nSize); }else if( eType==SEE_KEY_WRITE ){ db_write_saved_encryption_key_to_process(processId, pAddress, nSize); }else if( eType==SEE_KEY_ZERO ){ db_zero_saved_encryption_key_in_process(processId, pAddress, nSize); }else{ fossil_panic("unsupported SEE key operation %d", eType); } } file_chdir(zPwd, 0); fossil_free(zPwd); return processId; } /* ** This function sets the saved database encryption key to one that gets ** read from the specified Fossil parent process, if applicable. This is ** only necessary (or functional) on Windows or Linux. */ PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){ PID_T processId = 0; g.zPidKey = find_option("usepidkey",0,1); if( !g.zPidKey ){ g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY"); } if( g.zPidKey ){ LPVOID pAddress = NULL; SIZE_T nSize = 0; parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize); if( eType==SEE_KEY_READ ){ db_read_saved_encryption_key_from_process(processId, pAddress, nSize); }else if( eType==SEE_KEY_WRITE ){ db_write_saved_encryption_key_to_process(processId, pAddress, nSize); }else if( eType==SEE_KEY_ZERO ){ db_zero_saved_encryption_key_in_process(processId, pAddress, nSize); }else{ fossil_panic("unsupported SEE key operation %d", eType); } }else{ const char *zSeeDbConfig = find_option("seedbcfg",0,1); if( !zSeeDbConfig ){ zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG"); } if( zSeeDbConfig ){ processId = db_handle_saved_encryption_key_for_process_via_th1( zSeeDbConfig, eType ); } } return processId; } #endif /* USE_SEE */ /* ** If the database file zDbFile has a name that suggests that it is ** encrypted, then prompt for the database encryption key and return it ** in the blob *pKey. Or, if the encryption key has previously been ** requested, just return a copy of the previous result. The blob in ** *pKey must be initialized. */ static void db_maybe_obtain_encryption_key( const char *zDbFile, /* Name of the database file */ Blob *pKey /* Put the encryption key here */ ){ #if USE_SEE if( sqlite3_strglob("*.efossil", zDbFile)==0 ){ char *zKey = db_get_saved_encryption_key(); if( zKey ){ blob_set(pKey, zKey); }else{ char *zPrompt = mprintf("\rencryption key for '%s': ", zDbFile); prompt_for_password(zPrompt, pKey, 0); fossil_free(zPrompt); db_set_saved_encryption_key(pKey); } } #endif } /* ** Sets the encryption key for the database, if necessary. */ void db_maybe_set_encryption_key(sqlite3 *db, const char *zDbName){ Blob key; blob_init(&key, 0, 0); db_maybe_obtain_encryption_key(zDbName, &key); if( blob_size(&key)>0 ){ if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){ char *zCmd = sqlite3_mprintf("PRAGMA key(%Q)", blob_str(&key)); sqlite3_exec(db, zCmd, 0, 0, 0); fossil_secure_zero(zCmd, strlen(zCmd)); sqlite3_free(zCmd); #if USE_SEE }else{ sqlite3_key(db, blob_str(&key), -1); #endif } } blob_reset(&key); } /* ** 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; Blob bNameCheck = BLOB_INITIALIZER; if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); file_canonical_name(zDbName, &bNameCheck, 0) /* For purposes of the apndvfs check, g.nameOfExe and zDbName must ** both be canonicalized, else chances are very good that they ** will not match even if they're the same file. Details: ** https://fossil-scm.org/forum/forumpost/16880a28aad1a868 */; if( strcmp(blob_str(&bNameCheck), g.nameOfExe)==0 ){ extern int sqlite3_appendvfs_init( sqlite3 *, char **, const sqlite3_api_routines * ); sqlite3_appendvfs_init(0,0,0); g.zVfsName = "apndvfs"; } blob_zero(&bNameCheck); 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)); } db_maybe_set_encryption_key(db, zDbName); sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_TRIGGER, 0, &rc); sqlite3_db_config(db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, 0, &rc); sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, &rc); sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, &rc); sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, &rc); sqlite3_busy_timeout(db, 15000); 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_v2(db, SQLITE_TRACE_PROFILE, 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_set_authorizer(db, db_top_authorizer, db); db_register_fts5(db) /* in search.c */; 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){ Blob key; if( db_table_exists(zLabel,"sqlite_schema") ) return; blob_init(&key, 0, 0); db_maybe_obtain_encryption_key(zDbName, &key); if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){ char *zCmd = sqlite3_mprintf("ATTACH DATABASE %Q AS %Q KEY %Q", zDbName, zLabel, blob_str(&key)); db_exec_sql(zCmd); fossil_secure_zero(zCmd, strlen(zCmd)); sqlite3_free(zCmd); }else{ char *zCmd = sqlite3_mprintf("ATTACH DATABASE %Q AS %Q KEY ''", zDbName, zLabel); db_exec_sql(zCmd); sqlite3_free(zCmd); #if USE_SEE if( blob_size(&key)>0 ){ sqlite3_key_v2(g.db, zLabel, blob_str(&key), -1); } #endif } blob_reset(&key); } /* ** Change the schema name of the "main" database to zLabel. ** zLabel must be a static string that is unchanged for the life of ** the database connection. ** ** After calling this routine, db_database_slot(zLabel) should ** return 0. */ void db_set_main_schemaname(sqlite3 *db, const char *zLabel){ if( sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, zLabel) ){ fossil_panic("Fossil requires a version of SQLite that supports the " "SQLITE_DBCONFIG_MAINDBNAME interface."); } } /* ** Return the slot number for database zLabel. The first database ** opened is slot 0. The "temp" database is slot 1. Attached databases ** are slots 2 and higher. ** ** Return -1 if zLabel does not match any open database. */ int db_database_slot(const char *zLabel){ int iSlot = -1; int rc; Stmt q; if( g.db==0 ) return iSlot; rc = db_prepare_ignore_error(&q, "PRAGMA database_list"); if( rc==SQLITE_OK ){ while( db_step(&q)==SQLITE_ROW ){ if( fossil_strcmp(db_column_text(&q,1),zLabel)==0 ){ iSlot = db_column_int(&q, 0); break; } } } db_finalize(&q); return iSlot; } /* ** 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. */ void db_open_or_attach(const char *zDbName, const char *zLabel){ if( !g.db ){ g.db = db_open(zDbName); db_set_main_schemaname(g.db, zLabel); }else{ db_attach(zDbName, zLabel); } } /* ** Close the per-user configuration database file */ void db_close_config(){ int iSlot = db_database_slot("configdb"); if( iSlot>0 ){ db_detach("configdb"); }else if( g.dbConfig ){ sqlite3_wal_checkpoint(g.dbConfig, 0); sqlite3_close(g.dbConfig); g.dbConfig = 0; }else if( g.db && 0==iSlot ){ int rc; sqlite3_wal_checkpoint(g.db, 0); rc = sqlite3_close(g.db); if( g.fSqlTrace ) fossil_trace("-- db_close_config(%d)\n", rc); g.db = 0; g.repositoryOpen = 0; g.localOpen = 0; }else{ return; } fossil_free(g.zConfigDbName); g.zConfigDbName = 0; } /* ** Compute the name of the configuration database. If unable to find the ** database, return 0 if isOptional is true, or panic if isOptional is false. ** ** Space to hold the result comes from fossil_malloc(). */ static char *db_configdb_name(int isOptional){ char *zHome; /* Home directory */ char *zDbName; /* Name of the database file */ /* On Windows, look for these directories, in order: ** ** FOSSIL_HOME ** LOCALAPPDATA ** APPDATA ** USERPROFILE ** HOMEDRIVE HOMEPATH */ #if defined(_WIN32) || defined(__CYGWIN__) zHome = fossil_getenv("FOSSIL_HOME"); if( zHome==0 ){ zHome = fossil_getenv("LOCALAPPDATA"); if( zHome==0 ){ zHome = fossil_getenv("APPDATA"); if( zHome==0 ){ zHome = fossil_getenv("USERPROFILE"); if( zHome==0 ){ char *zDrive = fossil_getenv("HOMEDRIVE"); char *zPath = fossil_getenv("HOMEPATH"); if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath); } } } } zDbName = mprintf("%//_fossil", zHome); fossil_free(zHome); return zDbName; #else /* if unix */ char *zXdgHome; /* For unix. a 5-step algorithm is used. ** See ../www/tech_overview.wiki for discussion. ** ** Step 1: If FOSSIL_HOME exists -> $FOSSIL_HOME/.fossil */ zHome = fossil_getenv("FOSSIL_HOME"); if( zHome!=0 ) return mprintf("%s/.fossil", zHome); /* Step 2: If HOME exists and file $HOME/.fossil exists -> $HOME/.fossil */ zHome = fossil_getenv("HOME"); if( zHome ){ zDbName = mprintf("%s/.fossil", zHome); if( file_size(zDbName, ExtFILE)>1024*3 ){ return zDbName; } fossil_free(zDbName); } /* Step 3: if XDG_CONFIG_HOME exists -> $XDG_CONFIG_HOME/fossil.db */ zXdgHome = fossil_getenv("XDG_CONFIG_HOME"); if( zXdgHome!=0 ){ return mprintf("%s/fossil.db", zXdgHome); } /* The HOME variable is required in order to continue. */ if( zHome==0 ){ if( isOptional ) return 0; fossil_fatal("cannot locate home directory - please set one of the " "FOSSIL_HOME, XDG_CONFIG_HOME, or HOME environment " "variables"); } /* Step 4: If $HOME/.config is a directory -> $HOME/.config/fossil.db */ zXdgHome = mprintf("%s/.config", zHome); if( file_isdir(zXdgHome, ExtFILE)==1 ){ fossil_free(zXdgHome); return mprintf("%s/.config/fossil.db", zHome); } /* Step 5: Otherwise -> $HOME/.fossil */ return mprintf("%s/.fossil", zHome); #endif /* unix */ } /* ** Open the configuration database. Create the database anew if ** it does not already exist. ** ** If the useAttach flag is 0 (the usual case) then the configuration ** database is opened on a separate database connection g.dbConfig. ** This prevents the 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 database 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. */ int db_open_config(int useAttach, int isOptional){ char *zDbName; if( g.zConfigDbName ){ int alreadyAttached = db_database_slot("configdb")>0; if( useAttach==alreadyAttached ) return 1; /* Already open. */ db_close_config(); } zDbName = db_configdb_name(isOptional); if( zDbName==0 ) return 0; if( file_size(zDbName, ExtFILE)<1024*3 ){ char *zHome = file_dirname(zDbName); int rc; if( file_isdir(zHome, ExtFILE)==0 ){ file_mkdir(zHome, ExtFILE, 0); } rc = file_access(zHome, W_OK); if( rc ){ if( isOptional ) return 0; fossil_fatal("home directory \"%s\" must be writeable", zHome); } db_init_database(zDbName, zConfigSchema, (char*)0); fossil_free(zHome); } if( file_access(zDbName, W_OK) ){ if( isOptional ) return 0; fossil_fatal("configuration file %s must be writeable", zDbName); } if( useAttach ){ db_open_or_attach(zDbName, "configdb"); g.dbConfig = 0; }else{ g.dbConfig = db_open(zDbName); db_set_main_schemaname(g.dbConfig, "configdb"); } g.zConfigDbName = zDbName; return 1; } /* ** 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, 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, 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("localdb", zTable) && !db_table_has_column("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){ i64 lsize; if( file_access(zDbName, F_OK) ) return 0; lsize = file_size(zDbName, ExtFILE); if( lsize%1024!=0 || lsize<4096 ) return 0; db_open_or_attach(zDbName, "localdb"); /* Check to see if the check-out database has the lastest schema changes. ** The most recent schema change (2019-01-19) is the addition of the ** vmerge.mhash and vfile.mhash fields. If the schema has the vmerge.mhash ** column, assume everything else is up-to-date. */ if( db_table_has_column("localdb","vmerge","mhash") ){ return 1; /* This is a check-out database with the latest schema */ } /* If there is no vfile table, then assume we have picked up something ** that is not even close to being a valid check-out database */ if( !db_table_exists("localdb","vfile") ){ return 0; /* Not a DB */ } /* 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( !db_table_has_column("localdb","vfile","isexe") ){ 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( !db_table_has_column("localdb","vfile","isLink") ){ 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"); } if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){ db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0"); } } /* The design of the check-out database changed on 2019-01-19 adding the mhash ** column to vfile and vmerge and changing the UNIQUE index on vmerge into ** a PRIMARY KEY that includes the new mhash column. However, we must have ** the repository database at hand in order to do the migration, so that ** step is deferred. */ return 1; } /* ** Locate the root directory of the local repository tree. The root ** directory is found by searching for a file named "_FOSSIL_" or ".fslckout" ** that contains a valid repository database. ** ** For legacy, also look for ".fos". The use of ".fos" is deprecated ** since "fos" has negative connotations in Hungarian, we are told. ** ** If no valid _FOSSIL_ or .fslckout file is found, we move up one level and ** try again. Once the file is found, the g.zLocalRoot variable is set ** to the root of the repository tree and this routine returns 1. If ** no database is found, then this routine return 0. ** ** In db_open_local_v2(), if the bRootOnly flag is true, then only ** look in the CWD for the check-out database. Do not scan upwards in ** the file hierarchy. ** ** 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_v2(const char *zDbName, int bRootOnly){ 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) ){ if( db_open_config(0, 1)==0 ){ return 0; /* Configuration could not be opened */ } /* Found a valid check-out 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_repository(zDbName); return 1; } } if( bRootOnly ) break; n--; while( n>1 && zPwd[n]!='/' ){ n--; } while( n>1 && zPwd[n-1]=='/' ){ n--; } zPwd[n] = 0; } /* A check-out database file could not be found */ return 0; } int db_open_local(const char *zDbName){ return db_open_local_v2(zDbName, 0); } /* ** Get the full pathname to the repository database file. The ** local database (the _FOSSIL_ or .fslckout database) must have already ** been opened before this routine is called. */ const char *db_repository_filename(void){ static char *zRepo = 0; assert( g.localOpen ); assert( g.zLocalRoot ); if( zRepo==0 ){ zRepo = db_lget("repository", 0); if( zRepo && !file_is_absolute_path(zRepo) ){ char * zFree = zRepo; zRepo = mprintf("%s%s", g.zLocalRoot, zRepo); fossil_free(zFree); zFree = zRepo; zRepo = file_canonical_name_dup(zFree); fossil_free(zFree); } } return zRepo; } /* ** Returns non-zero if support for symlinks is currently enabled. */ int db_allow_symlinks(void){ return g.allowSymlinks; } /* ** Return TRUE if the file in the argument seems like it might be an ** SQLite database file that contains a Fossil repository schema. */ int db_looks_like_a_repository(const char *zDbName){ sqlite3 *db = 0; i64 sz; int rc; int res = 0; sqlite3_stmt *pStmt = 0; sz = file_size(zDbName, ExtFILE); if( sz<16834 ) return 0; db = db_open(zDbName); if( !db ) return 0; if( !g.zVfsName && sz%512 ) return 0; rc = sqlite3_prepare_v2(db, "SELECT count(*) FROM sqlite_schema" " WHERE name COLLATE nocase IN" "('blob','delta','rcvfrom','user','config','mlink','plink');", -1, &pStmt, 0); if( rc ) goto is_repo_end; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ) goto is_repo_end; if( sqlite3_column_int(pStmt, 0)!=7 ) goto is_repo_end; res = 1; is_repo_end: sqlite3_finalize(pStmt); sqlite3_close(db); return res; } /* ** COMMAND: test-is-repo */ void test_is_repo(void){ int i; for(i=2; i<g.argc; i++){ fossil_print("%s: %s\n", db_looks_like_a_repository(g.argv[i]) ? "yes" : " no", g.argv[i] ); } } /* ** Open the repository database given by zDbName. If zDbName==NULL then ** get the name from the already open local database. */ void db_open_repository(const char *zDbName){ if( g.repositoryOpen ) return; if( zDbName==0 ){ if( g.localOpen ){ zDbName = db_repository_filename(); } if( zDbName==0 ){ db_err("unable to find the name of a repository database"); } } if( !db_looks_like_a_repository(zDbName) ){ if( file_access(zDbName, F_OK) ){ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND; #endif fossil_fatal("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_fatal("read permission denied for repository %s", zDbName); }else{ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NOT_VALID; #endif fossil_fatal("not a valid repository: %s", zDbName); } } g.zRepositoryName = mprintf("%s", zDbName); db_open_or_attach(g.zRepositoryName, "repository"); g.repositoryOpen = 1; sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION, &g.iRepoDataVers); /* 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",""); g.eHashPolicy = db_get_int("hash-policy",-1); if( g.eHashPolicy<0 ){ g.eHashPolicy = hname_default_policy(); db_set_int("hash-policy", g.eHashPolicy, 0); } #if 0 /* No longer automatic. Need to run "fossil rebuild" to migrate */ /* Make a change to the CHECK constraint on the BLOB table for ** version 2.0 and later. */ rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */ #endif /* Additional checks that occur when opening the check-out database */ if( g.localOpen ){ /* If the repository database that was just opened has been ** eplaced by a clone of the same project, with different RID ** values, then renumber the RID values stored in various tables ** of the check-out database, so that the repository and check-out ** databases align. */ if( !db_fingerprint_ok() ){ if( find_option("no-rid-adjust",0,0)!=0 ){ /* The --no-rid-adjust command-line option bypasses the RID value ** updates. Intended for use during debugging, especially to be ** able to run "fossil sql" after a database swap. */ fossil_print( "WARNING: repository change detected, but no adjust made.\n" ); }else if( find_option("rid-renumber-dryrun",0,0)!=0 ){ /* the --rid-renumber-dryrun option shows how RID values would be ** renumbered, but does not actually perform the renumbering. ** This is a debugging-only option. */ vfile_rid_renumbering_event(1); exit(0); }else{ char *z; stash_rid_renumbering_event(); vfile_rid_renumbering_event(0); undo_reset(); bisect_reset(); z = db_fingerprint(0, 1); db_lset("fingerprint", z); fossil_free(z); fossil_print( "WARNING: The repository database has been replaced by a clone.\n" "Bisect history and undo have been lost.\n" ); } } /* Make sure the check-out database schema migration of 2019-01-20 ** has occurred. ** ** The 2019-01-19 migration is the addition of the vmerge.mhash and ** vfile.mhash columns and making the vmerge.mhash column part of the ** PRIMARY KEY for vmerge. */ if( !db_table_has_column("localdb", "vfile", "mhash") ){ db_multi_exec("ALTER TABLE vfile ADD COLUMN mhash;"); db_multi_exec( "UPDATE vfile" " SET mhash=(SELECT uuid FROM blob WHERE blob.rid=vfile.mrid)" " WHERE mrid!=rid;" ); if( !db_table_has_column("localdb", "vmerge", "mhash") ){ db_exec_sql("ALTER TABLE vmerge RENAME TO old_vmerge;"); db_exec_sql(zLocalSchemaVmerge); db_exec_sql( "INSERT OR IGNORE INTO vmerge(id,merge,mhash)" " SELECT id, merge, blob.uuid FROM old_vmerge, blob" " WHERE old_vmerge.merge=blob.rid;" "DROP TABLE old_vmerge;" ); } } } } /* ** Return true if there have been any changes to the repository ** database since it was opened. ** ** Changes to "config" and "localdb" and "temp" do not count. ** This routine only returns true if there have been changes ** to "repository". */ int db_repository_has_changed(void){ unsigned int v; if( !g.repositoryOpen ) return 0; sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION, &v); return g.iRepoDataVers != v; } /* ** 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 */ #define OPEN_SUBSTITUTE 0x004 /* Fake in-memory repo if not found */ #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 check-out 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, ExtFILE)==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; } zRep = db_repository_filename(); if( zRep==0 ){ goto rep_not_found; } } db_open_repository(zRep); if( g.repositoryOpen ){ if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema(); return; } rep_not_found: if( bFlags & OPEN_OK_NOT_FOUND ){ /* No errors if the database is not found */ if( bFlags & OPEN_SUBSTITUTE ){ db_create_repository(0); } }else{ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND; #endif if( nArgUsed==0 ){ fossil_fatal("use --repository or -R to specify the repository database"); }else{ fossil_fatal("specify the repository name as a command-line argument"); } } } /* ** 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, 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 ** ** Usage: %fossil test-move-repository PATHNAME ** ** Change the location of the repository database on a local check-out. ** Use this command to avoid having to close and reopen a check-out ** when relocating the repository database. */ void move_repo_cmd(void){ 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 check-out"); return; } db_open_or_attach(zRepo, "test_repo"); 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. */ void db_must_be_within_tree(void){ if( find_repository_option() ){ fossil_fatal("the \"%s\" command only works from within an open check-out", g.argv[1]); } if( db_open_local(0)==0 ){ fossil_fatal("current directory is not within an open check-out"); } db_open_repository(0); db_verify_schema(); } /* ** Close the database connection. ** ** Check for unfinalized statements and report errors if the reportErrors ** argument is true. Ignore unfinalized statements when false. */ void db_close(int reportErrors){ sqlite3_stmt *pStmt; if( g.db==0 ) return; sqlite3_set_authorizer(g.db, 0, 0); if( g.fSqlStats ){ int cur, hiwtr; sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0); fprintf(stderr, "-- LOOKASIDE_USED %10d %10d\n", cur, hiwtr); sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0); fprintf(stderr, "-- LOOKASIDE_HIT %10d\n", hiwtr); sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &cur,&hiwtr,0); fprintf(stderr, "-- LOOKASIDE_MISS_SIZE %10d\n", hiwtr); sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &cur,&hiwtr,0); fprintf(stderr, "-- LOOKASIDE_MISS_FULL %10d\n", hiwtr); sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiwtr, 0); fprintf(stderr, "-- CACHE_USED %10d\n", cur); sqlite3_db_status(g.db, SQLITE_DBSTATUS_SCHEMA_USED, &cur, &hiwtr, 0); fprintf(stderr, "-- SCHEMA_USED %10d\n", cur); sqlite3_db_status(g.db, SQLITE_DBSTATUS_STMT_USED, &cur, &hiwtr, 0); fprintf(stderr, "-- STMT_USED %10d\n", cur); sqlite3_status(SQLITE_STATUS_MEMORY_USED, &cur, &hiwtr, 0); fprintf(stderr, "-- MEMORY_USED %10d %10d\n", cur, hiwtr); sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &cur, &hiwtr, 0); fprintf(stderr, "-- MALLOC_SIZE %10d\n", hiwtr); sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &cur, &hiwtr, 0); fprintf(stderr, "-- MALLOC_COUNT %10d %10d\n", cur, hiwtr); sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &cur, &hiwtr, 0); fprintf(stderr, "-- PCACHE_OVFLOW %10d %10d\n", cur, hiwtr); fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare); } while( db.pAllStmt ){ db_finalize(db.pAllStmt); } if( db.nBegin ){ if( reportErrors ){ fossil_warning("Transaction started at %s:%d never commits", db.zStartFile, db.iStartLine); } db_end_transaction(1); } pStmt = 0; sqlite3_busy_timeout(g.db, 0); g.dbIgnoreErrors++; /* Stop "database locked" warnings */ sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0); g.dbIgnoreErrors--; db_close_config(); /* If the localdb has a lot of unused free space, ** then VACUUM it as we shut down. */ if( db_database_slot("localdb")>=0 ){ int nFree = db_int(0, "PRAGMA localdb.freelist_count"); int nTotal = db_int(0, "PRAGMA localdb.page_count"); if( nFree>nTotal/4 ){ db_unprotect(PROTECT_ALL); db_multi_exec("VACUUM localdb;"); db_protect_pop(); } } if( g.db ){ int rc; sqlite3_wal_checkpoint(g.db, 0); rc = sqlite3_close(g.db); if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc); 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.repositoryOpen = 0; g.localOpen = 0; db.bProtectTriggers = 0; assert( g.dbConfig==0 ); assert( g.zConfigDbName==0 ); backoffice_run_if_needed(); } /* ** Close the database as quickly as possible without unnecessary processing. */ void db_panic_close(void){ if( g.db ){ int rc; sqlite3_wal_checkpoint(g.db, 0); rc = sqlite3_close(g.db); if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc); db_clear_authorizer(); } g.db = 0; g.repositoryOpen = 0; g.localOpen = 0; } /* ** Create a new empty repository database with the given name. ** ** Only the schema is initialized. The required VAR tables entries ** are not set by this routine and must be set separately in order ** to make the new file a valid database. */ void db_create_repository(const char *zFilename){ db_init_database( zFilename, zRepositorySchema1, zRepositorySchemaDefaultReports, zRepositorySchema2, (char*)0 ); db_delete_on_failure(zFilename); } /* ** Create the default user accounts in the USER table. */ void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ const char *zUser = zDefaultUser; if( zUser==0 ){ zUser = db_get("default-user", 0); } if( zUser==0 ){ zUser = fossil_getenv("FOSSIL_USER"); } if( zUser==0 ){ zUser = fossil_getenv("USER"); } if( zUser==0 ){ zUser = fossil_getenv("LOGNAME"); } if( zUser==0 ){ zUser = fossil_getenv("USERNAME"); } if( zUser==0 ){ zUser = "root"; } db_unprotect(PROTECT_USER); db_multi_exec( "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser ); db_multi_exec( "UPDATE user SET cap='s', pw=%Q" " WHERE login=%Q", fossil_random_password(10), zUser ); if( !setupUserOnly ){ db_multi_exec( "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('anonymous',hex(randomblob(8)),'hz','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','','ei','Dev');" "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('reader','','kptw','Reader');" ); } db_protect_pop(); } /* ** 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; int nSetting; const Setting *aSetting = setting_info(&nSetting); const char *zSep = ""; blob_zero(&x); blob_append_sql(&x, "("); for(i=0; i<nSetting; 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. ** ** The zTemplate parameter determines if the settings for the repository ** should be copied from another repository. If zTemplate is 0 then the ** settings will have their normal default values. If zTemplate is ** non-zero, it is assumed that the caller of this function has already ** attached a database using the label "settingSrc". If not, the call to ** this function will fail. ** ** The zInitialDate parameter determines the date of the initial check-in ** 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_unprotect(PROTECT_ALL); db_set("content-schema", CONTENT_SCHEMA, 0); db_set("aux-schema", AUX_SCHEMA_MAX, 0); db_set("rebuilt", get_version(), 0); db_set("admin-log", "1", 0); db_set("access-log", "1", 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 OR name GLOB 'walias:/*')" " AND name NOT GLOB 'project-*'" " AND name NOT GLOB 'short-project-*';", configure_inop_rhs(CONFIGSET_ALL), db_setting_inop_rhs() ); g.eHashPolicy = db_get_int("hash-policy", g.eHashPolicy); db_multi_exec( "REPLACE INTO reportfmt SELECT * FROM settingSrc.reportfmt;" ); /* ** Copy the user permissions, contact information, last modified ** time, and photo for all the "system" users from the supplied ** template repository into the one being setup. The other columns ** are not copied because they contain security information or other ** data specific to the other repository. The list of columns copied ** by this SQL statement may need to be revised in the future. */ db_multi_exec("UPDATE user SET" " cap = (SELECT u2.cap FROM settingSrc.user u2" " WHERE u2.login = user.login)," " info = (SELECT u2.info FROM settingSrc.user u2" " WHERE u2.login = user.login)," " mtime = (SELECT u2.mtime FROM settingSrc.user u2" " WHERE u2.login = user.login)," " photo = (SELECT u2.photo FROM settingSrc.user u2" " WHERE u2.login = user.login)" " WHERE user.login IN ('anonymous','nobody','developer','reader');" ); } db_protect_pop(); if( zInitialDate ){ int rid; blob_zero(&manifest); blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n"); zDate = date_in_standard_format(zInitialDate); blob_appendf(&manifest, "D %s\n", zDate); md5sum_init(); /* The R-card is necessary here because without it * fossil versions earlier than versions 1.27 would * interpret this artifact as a "control". */ blob_appendf(&manifest, "R %s\n", md5sum_finish(0)); 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 ** ** Usage: %fossil new ?OPTIONS? FILENAME ** or: %fossil init ?OPTIONS? FILENAME ** ** Create a repository for a new project in the file named FILENAME. ** This command is distinct from "clone". The "clone" command makes ** a copy of an existing project. This command starts a new project. ** ** By default, your current login name is used to create the default ** admin user. This can be overridden using the -A|--admin-user ** parameter. ** ** By default, all settings will be initialized to their default values. ** This can be overridden using the --template parameter to specify a ** repository file from which to copy the initial settings. When a template ** repository is used, almost all of the settings accessible from the setup ** page, either directly or indirectly, will be copied. Normal users and ** 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 ** -A|--admin-user USERNAME Select given USERNAME as admin user ** --date-override DATETIME Use DATETIME as time of the initial check-in ** --sha1 Use an initial hash policy of "sha1" ** --project-name STRING The name of the project "project name in ** quotes" ** --project-desc STRING The description of the project "project ** description in quotes" ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. ** ** 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 */ const char *zProjectName; /* Optional project name of the repo */ const char *zProjectDesc; /* Optional project description "description ** of project in quotes" */ int bUseSha1 = 0; /* True to set the hash-policy to sha1 */ zTemplate = find_option("template",0,1); zDate = find_option("date-override",0,1); zDefaultUser = find_option("admin-user","A",1); bUseSha1 = find_option("sha1",0,0)!=0; zProjectName = find_option("project-name", 0, 1); zProjectDesc = find_option("project-desc", 0, 1); /* We should be done with options.. */ verify_all_options(); if( g.argc!=3 ){ usage("REPOSITORY-NAME"); } if( -1 != file_size(g.argv[2], ExtFILE) ){ 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, 0); if( zTemplate ) db_attach(zTemplate, "settingSrc"); db_begin_transaction(); if( bUseSha1 ){ g.eHashPolicy = HPOLICY_SHA1; db_set_int("hash-policy", HPOLICY_SHA1, 0); } if( zProjectName ) db_set("project-name", zProjectName, 0); if( zProjectDesc ) db_set("project-description", zProjectDesc, 0); if( zDate==0 ) zDate = "now"; db_initial_setup(zTemplate, zDate, zDefaultUser); db_end_transaction(0); if( zTemplate ) db_detach("settingSrc"); if( zProjectName ) fossil_print("project-name: %s\n", zProjectName); if( zProjectDesc ) fossil_print("project-description: %s\n", zProjectDesc); 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); hash_user_password(g.zLogin); } /* ** SQL functions for debugging. ** ** The print() function writes its arguments on stdout, but only ** if the -sqlprint command-line option is turned on. */ void db_sql_print( sqlite3_context *context, int argc, sqlite3_value **argv ){ int i; if( g.fSqlPrint ){ for(i=0; i<argc; i++){ char c = i==argc-1 ? '\n' : ' '; fossil_print("%s%c", sqlite3_value_text(argv[i]), c); } } } /* ** Callback for sqlite3_trace_v2(); */ int db_sql_trace(unsigned m, void *notUsed, void *pP, void *pX){ sqlite3_stmt *pStmt = (sqlite3_stmt*)pP; char *zSql; int n; const char *zArg = (const char*)pX; char zEnd[100]; if( m & SQLITE_TRACE_CLOSE ){ /* If we are tracking closes, that means we want to clean up static ** prepared statements. */ while( db.pAllStmt ){ db_finalize(db.pAllStmt); } return 0; } if( zArg[0]=='-' ) return 0; if( m & SQLITE_TRACE_PROFILE ){ sqlite3_int64 nNano = *(sqlite3_int64*)pX; double rMillisec = 0.000001 * nNano; int nRun = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_RUN, 0); int nVmStep = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_VM_STEP, 1); sqlite3_snprintf(sizeof(zEnd),zEnd," /* %.3fms, %r run, %d vm-steps */\n", rMillisec, nRun, nVmStep); }else{ zEnd[0] = '\n'; zEnd[1] = 0; } zSql = sqlite3_expanded_sql(pStmt); n = (int)strlen(zSql); fossil_trace("%s%s%s", zSql, (n>0 && zSql[n-1]==';') ? "" : ";", zEnd); sqlite3_free(zSql); return 0; } /* ** Implement the user() SQL function. user() takes no arguments and ** returns the user ID of the current user. */ LOCAL void db_sql_user( sqlite3_context *context, int argc, sqlite3_value **argv ){ if( g.zLogin!=0 ){ sqlite3_result_text(context, g.zLogin, -1, SQLITE_STATIC); } } /* ** Implement the cgi() SQL function. cgi() takes an argument which is ** a name of CGI query parameter. The value of that parameter is returned, ** if available. Optional second argument will be returned if the first ** doesn't exist as a CGI parameter. */ LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ const char* zP; if( argc!=1 && argc!=2 ) return; zP = P((const char*)sqlite3_value_text(argv[0])); if( zP ){ sqlite3_result_text(context, zP, -1, SQLITE_STATIC); }else if( argc==2 ){ zP = (const char*)sqlite3_value_text(argv[1]); if( zP ) sqlite3_result_text(context, zP, -1, SQLITE_TRANSIENT); } } /* ** SQL function: ** ** is_selected(id) ** if_selected(id, X, Y) ** ** On the commit command, when filenames are specified (in order to do ** a partial commit) the vfile.id values for the named files are loaded ** into the g.aCommitFile[] array. This function looks at that array ** to see if a file is named on the command-line. ** ** In the first form (1 argument) return TRUE if either no files are ** named on the command line (g.aCommitFile is NULL meaning that all ** changes are to be committed) or if id is found in g.aCommitFile[] ** (meaning that id was named on the command-line). ** ** In the second form (3 arguments) return argument X if true and Y ** if false. Except if Y is NULL then always return X. */ LOCAL void file_is_selected( sqlite3_context *context, int argc, sqlite3_value **argv ){ int rc = 0; assert(argc==1 || argc==3); if( g.aCommitFile ){ int iId = sqlite3_value_int(argv[0]); int ii; for(ii=0; g.aCommitFile[ii]; ii++){ if( iId==g.aCommitFile[ii] ){ rc = 1; break; } } }else{ rc = 1; } if( argc==1 ){ sqlite3_result_int(context, rc); }else{ assert( argc==3 ); assert( rc==0 || rc==1 ); if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc; sqlite3_result_value(context, argv[2-rc]); } } /* ** Implementation of the "win_reserved(X)" SQL function, a wrapper ** for file_is_win_reserved(X) which returns true if X is ** a Windows-reserved filename. */ LOCAL void db_win_reserved_func( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char * zName = (const char *)sqlite3_value_text(argv[0]); if( zName!=0 ){ sqlite3_result_int(context, file_is_win_reserved(zName)!=0); } } /* ** Convert the input string into an artifact hash. Make a notation in the ** CONCEALED table so that the hash can be undo using the db_reveal() ** function at some later time. ** ** The value returned is stored in static space and will be overwritten ** on subsequent calls. ** ** If zContent is already a well-formed artifact hash, then return a copy ** of that hash, not a hash of the hash. ** ** The CONCEALED table is meant to obscure email addresses. Every valid ** email address will contain a "@" character and "@" is not valid within ** a SHA1 hash so there is no chance that a valid email address will go ** unconcealed. */ char *db_conceal(const char *zContent, int n){ static char zHash[HNAME_MAX+1]; Blob out; if( hname_validate(zContent, n) ){ memcpy(zHash, zContent, n); zHash[n] = 0; }else{ sha1sum_step_text(zContent, n); sha1sum_finish(&out); sqlite3_snprintf(sizeof(zHash), zHash, "%s", blob_str(&out)); blob_reset(&out); db_multi_exec( "INSERT OR IGNORE INTO concealed(hash,content,mtime)" " VALUES(%Q,%#Q,now())", zHash, n, zContent ); } return zHash; } /* ** Attempt to look up the input in the CONCEALED table. If found, ** and if the okRdAddr permission is enabled then return the ** original value for which the input is a hash. If okRdAddr is ** false or if the lookup fails, return the original string content. ** ** In either case, the string returned is stored in space obtained ** from malloc and should be freed by the calling function. */ char *db_reveal(const char *zKey){ char *zOut; if( g.perm.RdAddr ){ zOut = db_text(0, "SELECT content FROM concealed WHERE hash=%Q", zKey); }else{ zOut = 0; } if( zOut==0 ){ zOut = mprintf("%s", zKey); } return zOut; } /* ** Return true if the string zVal represents "true" (or "false"). */ int is_truth(const char *zVal){ static const char *const azOn[] = { "on", "yes", "true", "1" }; int i; for(i=0; i<count(azOn); i++){ if( fossil_stricmp(zVal,azOn[i])==0 ) return 1; } return 0; } int is_false(const char *zVal){ static const char *const azOff[] = { "off", "no", "false", "0" }; int i; for(i=0; i<count(azOff); i++){ if( fossil_stricmp(zVal,azOff[i])==0 ) return 1; } return 0; } /* ** Swap the g.db and g.dbConfig connections so that the various db_* routines ** work on the configuration database instead of on the repository database. ** Be sure to swap them back after doing the operation. ** ** If the configuration database has already been opened as the main database ** or is attached to the main database, no connection swaps are required so ** this routine is a no-op. */ void db_swap_connections(void){ /* ** When swapping the main database connection with the config database ** connection, the config database connection must be open (not simply ** attached); otherwise, the swap would end up leaving the main database ** connection invalid, defeating the very purpose of this routine. This ** same constraint also holds true when restoring the previously swapped ** database connection; otherwise, it means that no swap was performed ** because the main database connection was already pointing to the config ** database. */ if( g.dbConfig ){ sqlite3 *dbTemp = g.db; g.db = g.dbConfig; g.dbConfig = dbTemp; } } /* ** 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 a ** 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; int found = 0; struct _cacheEntry { struct _cacheEntry *next; const char *zName, *zValue; } *cacheEntry = 0; static struct _cacheEntry *cache = 0; if( !g.localOpen && g.zOpenRevision==0 ) return zNonVersionedSetting; /* Look up name in cache */ cacheEntry = cache; while( cacheEntry!=0 ){ if( fossil_strcmp(cacheEntry->zName, zName)==0 ){ zVersionedSetting = fossil_strdup(cacheEntry->zValue); break; } cacheEntry = cacheEntry->next; } /* Attempt to read value from file in check-out if there wasn't a cache hit.*/ if( cacheEntry==0 ){ Blob versionedPathname; Blob setting; blob_zero(&versionedPathname); blob_zero(&setting); blob_appendf(&versionedPathname, "%s.fossil-settings/%s", g.zLocalRoot, zName); if( !g.localOpen ){ /* Repository is in the process of being opened, but files have not been * written to disk. Load from the database. */ Blob noWarnFile; if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname), &setting, 0) ){ found = 1; } /* See if there's a no-warn flag */ blob_append(&versionedPathname, ".no-warn", -1); blob_zero(&noWarnFile); if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname), &noWarnFile, 0) ){ noWarn = 1; } blob_reset(&noWarnFile); }else if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ /* File exists, and contains the value for this setting. Load from ** the file. */ const char *zFile = blob_str(&versionedPathname); if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){ found = 1; } /* See if there's a no-warn flag */ blob_append(&versionedPathname, ".no-warn", -1); if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ noWarn = 1; } } blob_reset(&versionedPathname); if( found ){ blob_strip_comment_lines(&setting, &setting); blob_trim(&setting); /* Avoid non-obvious problems with line endings ** on boolean properties */ zVersionedSetting = fossil_strdup(blob_str(&setting)); } blob_reset(&setting); /* Store result in cache, which can be the value or 0 if not found */ cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry)); cacheEntry->next = cache; cacheEntry->zName = zName; cacheEntry->zValue = fossil_strdup(zVersionedSetting); cache = cacheEntry; } /* Display a warning? */ if( zVersionedSetting!=0 && zNonVersionedSetting!=0 && zNonVersionedSetting[0]!='\0' && !noWarn ){ /* There's a versioned setting, and a non-versioned setting. Tell ** the user about the conflict */ fossil_warning( "setting %s has both versioned and non-versioned values: using " "versioned value from file \"%/.fossil-settings/%s\" (to silence " "this warning, either create an empty file named " "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete " "the non-versioned setting with \"fossil unset %s\")", zName, g.zLocalRoot, zName, g.zLocalRoot, zName, zName ); } /* Prefer the versioned setting */ 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, const char *zDefault){ char *z = 0; const Setting *pSetting = db_find_setting(zName, 0); if( g.repositoryOpen ){ static Stmt q1; const char *zRes; db_static_prepare(&q1, "SELECT value FROM config WHERE name=$n"); db_bind_text(&q1, "$n", zName); if( db_step(&q1)==SQLITE_ROW && (zRes = db_column_text(&q1,0))!=0 ){ z = fossil_strdup(zRes); } db_reset(&q1); } if( z==0 && g.zConfigDbName ){ static Stmt q2; const char *zRes; db_swap_connections(); db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n"); db_swap_connections(); db_bind_text(&q2, "$n", zName); if( db_step(&q2)==SQLITE_ROW && (zRes = db_column_text(&q2,0))!=0 ){ z = fossil_strdup(zRes); } db_reset(&q2); } if( pSetting!=0 && pSetting->versionable ){ /* This is a versionable setting, try and get the info from a ** checked-out file */ char * zZ = z; z = db_get_versioned(zName, z); if(zZ != z){ fossil_free(zZ); } } if( z==0 ){ if( zDefault==0 && pSetting && pSetting->def[0] ){ z = fossil_strdup(pSetting->def); }else{ z = fossil_strdup(zDefault); } } return z; } char *db_get_mtime(const char *zName, const char *zFormat, const char *zDefault){ char *z = 0; if( g.repositoryOpen ){ z = db_text(0, "SELECT mtime FROM config WHERE name=%Q", zName); } if( z==0 ){ z = fossil_strdup(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){ const CmdOrPage *pCmd = 0; db_assert_protection_off_or_not_sensitive(zName); if( zValue!=0 && zValue[0]==0 && dispatch_name_search(zName, CMDFLAG_SETTING, &pCmd)==0 && (pCmd->eCmdFlags & CMDFLAG_KEEPEMPTY)==0 ){ /* Changing a setting to an empty string is the same as unsetting it, ** unless that setting has the keep-empty flag. */ db_unset(zName/*works-like:"x"*/, globalFlag); return; } db_unprotect(PROTECT_CONFIG); db_begin_transaction(); if( globalFlag ){ db_swap_connections(); db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)", zName, zValue); db_swap_connections(); }else{ db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())", zName, zValue); } if( globalFlag && g.repositoryOpen ){ db_multi_exec("DELETE FROM config WHERE name=%Q", zName); } db_end_transaction(0); db_protect_pop(); } void db_unset(const char *zName, int globalFlag){ db_begin_transaction(); db_unprotect(PROTECT_CONFIG); if( globalFlag ){ db_swap_connections(); db_multi_exec("DELETE FROM global_config WHERE name=%Q", zName); db_swap_connections(); }else{ db_multi_exec("DELETE FROM config WHERE name=%Q", zName); } if( globalFlag && g.repositoryOpen ){ db_multi_exec("DELETE FROM config WHERE name=%Q", zName); } db_protect_pop(); db_end_transaction(0); } int db_is_global(const char *zName){ int rc = 0; if( g.zConfigDbName ){ db_swap_connections(); rc = db_exists("SELECT 1 FROM global_config WHERE name=%Q", zName); db_swap_connections(); } return rc; } int db_get_int(const char *zName, int dflt){ int v = dflt; int rc; if( g.repositoryOpen ){ static Stmt q; db_static_prepare(&q, "SELECT value FROM config WHERE name=$n"); db_bind_text(&q, "$n", zName); rc = db_step(&q); if( rc==SQLITE_ROW ){ v = db_column_int(&q, 0); } db_reset(&q); }else{ rc = SQLITE_DONE; } if( rc==SQLITE_DONE && g.zConfigDbName ){ static Stmt q2; db_swap_connections(); db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n"); db_swap_connections(); db_bind_text(&q2, "$n", zName); if( db_step(&q2)==SQLITE_ROW ){ v = db_column_int(&q2, 0); } db_reset(&q2); } return v; } i64 db_large_file_size(void){ /* Return size of the largest file that is not considered oversized */ return strtoll(db_get("large-file-size","20000000"),0,0); } void db_set_int(const char *zName, int value, int globalFlag){ db_assert_protection_off_or_not_sensitive(zName); db_unprotect(PROTECT_CONFIG); if( globalFlag ){ db_swap_connections(); db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)", zName, value); db_swap_connections(); }else{ db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%d,now())", zName, value); } if( globalFlag && g.repositoryOpen ){ db_multi_exec("DELETE FROM config WHERE name=%Q", zName); } db_protect_pop(); } int db_get_boolean(const char *zName, int dflt){ char *zVal = db_get(zName, dflt ? "on" : "off"); if( is_truth(zVal) ){ dflt = 1; }else if( is_false(zVal) ){ dflt = 0; } fossil_free(zVal); return dflt; } int db_get_versioned_boolean(const char *zName, int dflt){ char *zVal = db_get_versioned(zName, 0); if( zVal==0 ) return dflt; if( is_truth(zVal) ) return 1; if( is_false(zVal) ) return 0; return dflt; } char *db_lget(const char *zName, const char *zDefault){ return db_text(zDefault, "SELECT value FROM vvar WHERE name=%Q", zName); } void db_lset(const char *zName, const char *zValue){ db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%Q)", zName, zValue); } int db_lget_int(const char *zName, int dflt){ return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName); } int db_lget_boolean(const char *zName, int dflt){ char *zVal = db_lget(zName, dflt ? "on" : "off"); if( is_truth(zVal) ){ dflt = 1; }else if( is_false(zVal) ){ dflt = 0; } fossil_free(zVal); return dflt; } void db_lset_int(const char *zName, int value){ db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); } /* Va-args versions of db_get(), db_set(), and db_unset() ** ** codecheck1.c verifies that the format string for db_set_mprintf() ** and db_unset_mprintf() begins with an ASCII character prefix. We ** don't want that format string to begin with %s or %d as that might ** allow an injection attack to set or overwrite arbitrary settings. */ char *db_get_mprintf(const char *zDefault, const char *zFormat, ...){ va_list ap; char *zName; char *zResult; va_start(ap, zFormat); zName = vmprintf(zFormat, ap); va_end(ap); zResult = db_get(zName, zDefault); fossil_free(zName); return zResult; } void db_set_mprintf(const char *zNew, int iGlobal, const char *zFormat, ...){ va_list ap; char *zName; va_start(ap, zFormat); zName = vmprintf(zFormat, ap); va_end(ap); db_set(zName/*works-like:"x"*/, zNew, iGlobal); fossil_free(zName); } void db_unset_mprintf(int iGlobal, const char *zFormat, ...){ va_list ap; char *zName; va_start(ap, zFormat); zName = vmprintf(zFormat, ap); va_end(ap); db_unset(zName/*works-like:"x"*/, iGlobal); fossil_free(zName); } /* ** Get a setting that is tailored to subsystem. The return value is ** NULL if the setting does not exist, or a string obtained from mprintf() ** if the setting is available. ** ** The actual setting can be a comma-separated list of values of the form: ** ** * VALUE ** * SUBSYSTEM=VALUE ** ** A VALUE without the SUBSYSTEM= prefix is the default. This routine ** returns the VALUE that with the matching SUBSYSTEM, or the default ** VALUE if there is no match. */ char *db_get_for_subsystem(const char *zName, const char *zSubsys){ int nSubsys; char *zToFree = 0; char *zCopy; char *zNext; char *zResult = 0; const char *zSetting = db_get(zName, 0); if( zSetting==0 ) return 0; zCopy = zToFree = fossil_strdup(zSetting); if( zSubsys==0 ) zSubsys = ""; nSubsys = (int)strlen(zSubsys); while( zCopy ){ zNext = strchr(zCopy, ','); if( zNext ){ zNext[0] = 0; do{ zNext++; }while( fossil_isspace(zNext[0]) ); if( zNext[0]==0 ) zNext = 0; } if( strchr(zCopy,'=')==0 ){ if( zResult==0 ) zResult = zCopy; }else if( nSubsys && strncmp(zCopy, zSubsys, nSubsys)==0 && zCopy[nSubsys]=='=' ){ zResult = &zCopy[nSubsys+1]; break; } zCopy = zNext; } if( zResult ) zResult = fossil_strdup(zResult); fossil_free(zToFree); return zResult; } #if INTERFACE /* Manifest generation flags */ #define MFESTFLG_RAW 0x01 #define MFESTFLG_UUID 0x02 #define MFESTFLG_TAGS 0x04 #endif /* INTERFACE */ /* ** Get the manifest setting. For backwards compatibility first check if the ** value is a boolean. If it's not a boolean, treat each character as a flag ** to enable a manifest type. This system puts certain boundary conditions on ** which letters can be used to represent flags (any permutation of flags must ** not be able to fully form one of the boolean values). */ int db_get_manifest_setting(void){ int flg; char *zVal = db_get("manifest", 0); if( zVal==0 || is_false(zVal) ){ return 0; }else if( is_truth(zVal) ){ return MFESTFLG_RAW|MFESTFLG_UUID; } flg = 0; while( *zVal ){ switch( *zVal ){ case 'r': flg |= MFESTFLG_RAW; break; case 'u': flg |= MFESTFLG_UUID; break; case 't': flg |= MFESTFLG_TAGS; break; } zVal++; } return flg; } /* ** 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 check-out, also record the root of the check-out ** as follows: ** ** ckout:%s ** ** Where %s is the check-out 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_unprotect(PROTECT_CONFIG); 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 ); db_protect_pop(); 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_unprotect(PROTECT_CONFIG); 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 ); db_protect_pop(); fossil_free(zCkoutSetting); blob_reset(&localRoot); }else{ db_swap_connections(); } blob_reset(&full); } /* ** COMMAND: open ** ** Usage: %fossil open REPOSITORY ?VERSION? ?OPTIONS? ** ** Open a new connection to the repository name REPOSITORY. A check-out ** for the repository is created with its root at the current working ** directory, or in DIR if the "--workdir DIR" is used. If VERSION is ** specified then that version is checked out. Otherwise the most recent ** check-in on the main branch (usually "trunk") is used. ** ** REPOSITORY can be the filename for a repository that already exists on the ** local machine or it can be a URI for a remote repository. If REPOSITORY ** is a URI in one of the formats recognized by the [[clone]] command, then ** remote repo is first cloned, then the clone is opened. The clone will be ** stored in the current directory, or in DIR if the "--repodir DIR" option ** is used. The name of the clone will be taken from the last term of the URI. ** For "http:" and "https:" URIs, you can append an extra term to the end of ** the URI to get any repository name you like. For example: ** ** fossil open https://fossil-scm.org/home/new-name ** ** The base URI for cloning is "https://fossil-scm.org/home". The extra ** "new-name" term means that the cloned repository will be called ** "new-name.fossil". ** ** Options: ** --empty Initialize check-out as being empty, but still connected ** with the local repository. If you commit this check-out, ** it will become a new "initial" commit in the repository. ** -f|--force Continue with the open even if the working directory is ** not empty, or if auto-sync fails. ** --force-missing Force opening a repository with missing content ** -k|--keep Only modify the manifest file(s) ** --nested Allow opening a repository inside an opened check-out ** --nosync Do not auto-sync the repository prior to opening even ** if the autosync setting is on. ** --repodir DIR If REPOSITORY is a URI that will be cloned, store ** the clone in DIR rather than in "." ** --setmtime Set timestamps of all files to match their SCM-side ** times (the timestamp of the last check-in which modified ** them). ** --verbose If passed a URI then this flag is passed on to the clone ** operation, otherwise it has no effect ** --workdir DIR Use DIR as the working directory instead of ".". The DIR ** directory is created if it does not exist. ** ** See also: [[close]], [[clone]] */ void cmd_open(void){ int emptyFlag; int keepFlag; int forceMissingFlag; int allowNested; int setmtimeFlag; /* --setmtime. Set mtimes on files */ int bForce = 0; /* --force. Open even if non-empty dir */ static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; const char *zWorkDir; /* --workdir value */ const char *zRepo = 0; /* Name of the repository file */ const char *zRepoDir = 0; /* --repodir value */ char *zPwd; /* Initial working directory */ int isUri = 0; /* True if REPOSITORY is a URI */ int nLocal; /* Number of preexisting files in cwd */ int bVerbose = 0; /* --verbose option for clone */ url_proxy_options(); emptyFlag = find_option("empty",0,0)!=0; keepFlag = find_option("keep","k",0)!=0; forceMissingFlag = find_option("force-missing",0,0)!=0; allowNested = find_option("nested",0,0)!=0; setmtimeFlag = find_option("setmtime",0,0)!=0; zWorkDir = find_option("workdir",0,1); zRepoDir = find_option("repodir",0,1); bForce = find_option("force","f",0)!=0; if( find_option("nosync",0,0) ) g.fNoSync = 1; bVerbose = find_option("verbose",0,0)!=0; zPwd = file_getcwd(0,0); /* We should be done with options.. */ verify_all_options(); if( g.argc!=3 && g.argc!=4 ){ usage("REPOSITORY-FILENAME ?VERSION?"); } zRepo = g.argv[2]; if( sqlite3_strglob("http://*", zRepo)==0 || sqlite3_strglob("https://*", zRepo)==0 || sqlite3_strglob("ssh:*", zRepo)==0 || sqlite3_strglob("file:*", zRepo)==0 ){ isUri = 1; } /* If --workdir is specified, change to the requested working directory */ if( zWorkDir ){ if( !isUri ){ zRepo = file_canonical_name_dup(zRepo); } if( zRepoDir ){ zRepoDir = file_canonical_name_dup(zRepoDir); } if( file_isdir(zWorkDir, ExtFILE)!=1 ){ file_mkfolder(zWorkDir, ExtFILE, 0, 0); if( file_mkdir(zWorkDir, ExtFILE, 0) ){ fossil_fatal("cannot create directory %s", zWorkDir); } } if( file_chdir(zWorkDir, 0) ){ fossil_fatal("unable to make %s the working directory", zWorkDir); } } if( keepFlag==0 && bForce==0 && (nLocal = file_directory_size(".", 0, 1))>0 && (nLocal>1 || isUri || !file_in_cwd(zRepo)) ){ fossil_fatal("directory %s is not empty\n" "use the -f (--force) option to override\n" "or the -k (--keep) option to keep local files unchanged", file_getcwd(0,0)); } if( db_open_local_v2(0, allowNested) ){ fossil_fatal("there is already an open tree at %s", g.zLocalRoot); } /* If REPOSITORY looks like a URI, then try to clone it first */ if( isUri ){ char *zNewBase; /* Base name of the cloned repository file */ const char *zUri; /* URI to clone */ int rc; /* Result code from fossil_system() */ Blob cmd; /* Clone command to be run */ char *zCmd; /* String version of the clone command */ zUri = zRepo; zNewBase = url_to_repo_basename(zUri); if( zNewBase==0 ){ fossil_fatal("unable to deduce a repository name from the url \"%s\"", zUri); } if( zRepoDir==0 ) zRepoDir = zPwd; zRepo = mprintf("%s/%s.fossil", zRepoDir, zNewBase); fossil_free(zNewBase); blob_init(&cmd, 0, 0); blob_append_escaped_arg(&cmd, g.nameOfExe, 1); blob_append(&cmd, " clone", -1); if(0!=bVerbose){ blob_append(&cmd, " --verbose", -1); } blob_append_escaped_arg(&cmd, zUri, 1); blob_append_escaped_arg(&cmd, zRepo, 1); zCmd = blob_str(&cmd); fossil_print("%s\n", zCmd); if( zWorkDir ) file_chdir(zPwd, 0); rc = fossil_system(zCmd); if( rc ){ fossil_fatal("clone of %s failed", zUri); } blob_reset(&cmd); if( zWorkDir ) file_chdir(zWorkDir, 0); }else if( zRepoDir ){ fossil_fatal("the --repodir option only makes sense if the REPOSITORY " "argument is a URI that begins with http:, https:, ssh:, " "or file:"); } db_open_config(0,0); db_open_repository(zRepo); /* Figure out which revision to open. */ if( !emptyFlag ){ if( g.argc==4 ){ g.zOpenRevision = g.argv[3]; }else if( db_exists("SELECT 1 FROM event WHERE type='ci'") ){ g.zOpenRevision = db_get("main-branch", 0); } if( autosync_loop(SYNC_PULL, !bForce, "open") && !bForce ){ fossil_fatal("unable to auto-sync the repository"); } } #if defined(_WIN32) || defined(__CYGWIN__) # define LOCALDB_NAME "./_FOSSIL_" #else # define LOCALDB_NAME "./.fslckout" #endif db_init_database(LOCALDB_NAME, zLocalSchema, zLocalSchemaVmerge, #ifdef FOSSIL_LOCAL_WAL "COMMIT; PRAGMA journal_mode=WAL; BEGIN;", #endif (char*)0); db_delete_on_failure(LOCALDB_NAME); db_open_local(0); db_lset("repository", zRepo); db_record_repository_filename(zRepo); db_set_checkout(0); azNewArgv[0] = g.argv[0]; g.argv = azNewArgv; if( !emptyFlag ){ g.argc = 3; if( g.zOpenRevision ){ azNewArgv[g.argc-1] = g.zOpenRevision; }else{ azNewArgv[g.argc-1] = "--latest"; } if( keepFlag ){ azNewArgv[g.argc++] = "--keep"; } if( forceMissingFlag ){ azNewArgv[g.argc++] = "--force-missing"; } checkout_cmd(); } if( setmtimeFlag ){ int const vid = db_lget_int("checkout", 0); if(vid!=0){ vfile_check_signature(vid, CKSIG_SETMTIME); } } g.argc = 2; info_cmd(); } /* ** Print the current value of a setting identified by the pSetting ** pointer. */ void print_setting(const Setting *pSetting, int valueOnly){ Stmt q; int versioned = 0; 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), ExtFILE)>=0 ){ versioned = 1; } blob_reset(&versionedPathname); } if( valueOnly && versioned ){ fossil_print("%s\n", db_get_versioned(pSetting->name, NULL)); return; } 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 ){ if( valueOnly ){ fossil_print("%s\n", db_column_text(&q, 1)); }else{ fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0), db_column_text(&q, 1)); } }else if( valueOnly ){ fossil_print("\n"); }else{ fossil_print("%-20s\n", pSetting->name); } if( versioned ){ 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. A negative value indicates that that ** page should not render this setting. Such values may be rendered ** separately/manually on another page, e.g., /setup_access, and are ** exposed via the CLI settings command. ** ** 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 and ** negative for values which should not appear ** on the /setup_settings page. */ char versionable; /* Is this setting versionable? */ char forceTextArea; /* Force using a text area for display? */ char sensitive; /* True if this a security-sensitive setting */ const char *def; /* Default value */ }; #endif /* INTERFACE */ /* ** SETTING: access-log boolean default=off ** ** When the access-log setting is enabled, all login attempts (successful ** and unsuccessful) on the web interface are recorded in the "access" table ** of the repository. */ /* ** SETTING: admin-log boolean default=off ** ** When the admin-log setting is enabled, configuration changes are recorded ** in the "admin_log" table of the repository. */ /* ** SETTING: allow-symlinks boolean default=off sensitive ** ** When allow-symlinks is OFF, Fossil does not see symbolic links ** (a.k.a "symlinks") on disk as a separate class of object. Instead Fossil ** sees the object that the symlink points to. Fossil will only manage files ** and directories, not symlinks. When a symlink is added to a repository, ** the object that the symlink points to is added, not the symlink itself. ** ** When allow-symlinks is ON, Fossil sees symlinks on disk as a separate ** object class that is distinct from files and directories. When a symlink ** is added to a repository, Fossil stores the target filename. In other ** words, Fossil stores the symlink itself, not the object that the symlink ** points to. ** ** Symlinks are not cross-platform. They are not available on all ** operating systems and file systems. Hence the allow-symlinks setting is ** OFF by default, for portability. */ /* ** SETTING: auto-captcha boolean default=on variable=autocaptcha ** If enabled, the /login page provides a button that will automatically ** fill in the captcha password. This makes things easier for human users, ** at the expense of also making logins easier for malicious robots. */ /* ** SETTING: auto-hyperlink width=16 default=1 ** ** If non-zero, enable hyperlinks on web pages even for users that lack ** the "h" privilege as long as the UserAgent string in the HTTP request ** (The HTTP_USER_AGENT cgi variable) looks like it comes from a human and ** not a robot. Details depend on the value of the setting. ** ** (0) Off: No adjustments are made to the 'h' privilege based on ** the user agent. ** ** (1) UserAgent and Javascript: The the href= values of hyperlinks ** initially point to /honeypot and are changed to point to the ** correct target by javascript that runs after the page loads. ** The auto-hyperlink-delay and auto-hyperlink-mouseover settings ** influence that javascript. ** ** (2) UserAgent only: If the HTTP_USER_AGENT looks human ** then generate hyperlinks, otherwise do not. ** ** Better robot exclusion is obtained when this setting is 1 versus 2. ** However, a value of 1 causes the visited/unvisited colors of hyperlinks ** to stop working on Safari-derived web browsers. When this setting is 2, ** the hyperlinks work better on Safari, but more robots are able to sneak ** in. */ /* ** SETTING: auto-hyperlink-delay width=16 default=0 ** ** When the auto-hyperlink setting is 1, the javascript that runs to set ** the href= attributes of hyperlinks delays by this many milliseconds ** after the page load. Suggested values: 50 to 200. */ /* ** SETTING: auto-hyperlink-mouseover boolean default=off ** ** When the auto-hyperlink setting is 1 and this setting is on, the ** javascript that runs to set the href= attributes of hyperlinks waits ** until either a mousedown or mousemove event is seen. This helps ** to distinguish real users from robots. For maximum robot defense, ** the recommended setting is ON. */ /* ** SETTING: auto-shun boolean default=on ** If enabled, automatically pull the shunning list ** from a server to which the client autosyncs. */ /* ** SETTING: autosync width=16 default=on ** This setting determines when autosync occurs. The setting is a ** string that provides a lot of flexibility for determining when and ** when not to autosync. Examples: ** ** on Always autosync for command where autosync ** makes sense ("commit", "merge", "open", "update") ** ** off Never autosync. ** ** pullonly Only to pull autosyncs ** ** all Sync with all remotes ** ** on,open=off Autosync for most commands, but not for "open" ** ** off,commit=pullonly Do not autosync, except do a pull before each ** "commit", presumably to avoid undesirable ** forks. ** ** The syntax is a comma-separated list of VALUE and COMMAND=VALUE entries. ** A plain VALUE entry is the default that is used if no COMMAND matches. ** Otherwise, the VALUE of the matching command is used. ** ** The "all" value is special in that it applies to the "sync" command in ** addition to "commit", "merge", "open", and "update". */ /* ** SETTING: autosync-tries width=16 default=1 ** 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. */ /* ** SETTING: backoffice-nodelay boolean default=off ** If backoffice-nodelay is true, then the backoffice processing ** will never invoke sleep(). If it has nothing useful to do, ** it simply exits. */ /* ** SETTING: backoffice-disable boolean default=off ** If backoffice-disable is true, then the automatic backoffice ** processing is disabled. Automatic backoffice processing is the ** backoffice work that normally runs after each web page is ** rendered. Backoffice processing that is triggered by the ** "fossil backoffice" command is unaffected by this setting. ** ** Backoffice processing does things such as delivering ** email notifications. So if this setting is true, and if ** there is no cron job periodically running "fossil backoffice", ** email notifications and other work normally done by the ** backoffice will not occur. */ /* ** SETTING: backoffice-logfile width=40 sensitive ** If backoffice-logfile is not an empty string and is a valid ** filename, then a one-line message is appended to that file ** every time the backoffice runs. This can be used for debugging, ** to ensure that backoffice is running appropriately. */ /* ** SETTING: binary-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files ** that should be treated as "binary" for committing and merging ** purposes. Example: *.jpg,*.png The parsing rules are complex; ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__) /* ** SETTING: case-sensitive boolean default=off ** If TRUE, the files whose names differ only in case ** are considered distinct. If FALSE files whose names ** differ only in case are the same file. Defaults to ** TRUE for unix and FALSE for Cygwin, Mac and Windows. */ #endif #if !(defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__)) /* ** SETTING: case-sensitive boolean default=on ** If TRUE, the files whose names differ only in case ** are considered distinct. If FALSE files whose names ** differ only in case are the same file. Defaults to ** TRUE for unix and FALSE for Cygwin, Mac and Windows. */ #endif /* ** SETTING: clean-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files ** that the "clean" command will delete without prompting or allowing ** undo. Example: *.a,*.o,*.so The parsing rules are complex; ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ /* ** SETTING: clearsign boolean default=off ** When enabled, fossil will attempt to sign all commits ** with gpg. When disabled, commits will be unsigned. */ /* ** SETTING: comment-format width=16 default=1 ** Set the default options for printing timeline comments to the console. ** ** The global --comfmtflags command-line option (or alias --comment-format) ** overrides this setting. ** ** Possible values are: ** 1 Activate the legacy comment printing format (default). ** ** Or a bitwise combination of the following flags: ** 0 Activate the newer (non-legacy) comment printing format. ** 2 Trim leading and trailing CR and LF characters. ** 4 Trim leading and trailing white space characters. ** 8 Attempt to break lines on word boundaries. ** 16 Break lines before the original comment embedded in other text. ** ** Note: To preserve line breaks, activate the newer (non-legacy) comment ** printing format (i.e. set to "0", or a combination not including "1"). ** ** Note: The options for timeline comments displayed on the web UI can be ** configured through the /setup_timeline web page. */ /* ** SETTING: crlf-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files ** in which it is allowed to have CR, CR+LF or mixed line endings, ** suppressing Fossil's normal warning about this. Set it to "*" to ** disable CR+LF checking entirely. Example: *.md,*.txt ** The crnl-glob setting is a compatibility alias. */ /* ** SETTING: crnl-glob width=40 versionable block-text ** This is an alias for the crlf-glob setting. */ /* ** SETTING: default-perms width=16 default=u sensitive keep-empty ** Permissions given automatically to new users. For more ** information on permissions see the Users page in Server ** Administration of the HTTP UI. */ /* ** SETTING: diff-binary boolean default=on ** If enabled, permit files that may be binary ** or that match the "binary-glob" setting to be used with ** external diff programs. If disabled, skip these files. */ /* ** SETTING: diff-command width=40 sensitive ** The value is an external command to run when performing a diff. ** If undefined, the internal text diff will be used. */ /* ** SETTING: dont-commit boolean default=off ** If enabled, prevent committing to this repository, as an extra precaution ** against accidentally checking in to a repository intended to be read-only. */ /* ** SETTING: dont-push boolean default=off ** If enabled, prevent this repository from pushing from client to ** server. This can be used as an extra precaution to prevent ** accidental pushes to a public server from a private clone. */ /* ** SETTING: dotfiles boolean versionable default=off ** If enabled, include --dotfiles option for all compatible commands. */ /* ** SETTING: editor width=32 sensitive ** The value is an external command that will launch the ** text editor command used for check-in comments. */ /* ** SETTING: empty-dirs width=40 versionable block-text ** The value is a list of pathnames parsed according to the same rules as ** the *-glob settings. On update and checkout commands, if no directory ** exists with that name, an empty directory will be be created, even if ** it must create one or more parent directories. */ /* ** SETTING: encoding-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files that ** the "commit" command will ignore when issuing warnings about text files ** that may use another encoding than ASCII or UTF-8. Set to "*" to disable ** encoding checking. Example: *.md,*.txt The parsing rules are complex; ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) /* ** SETTING: exec-rel-paths boolean default=on ** When executing certain external commands (e.g. diff and ** gdiff), use relative paths. */ #endif #if !defined(FOSSIL_ENABLE_EXEC_REL_PATHS) /* ** SETTING: exec-rel-paths boolean default=off ** When executing certain external commands (e.g. diff and ** gdiff), use relative paths. */ #endif /* ** SETTING: fileedit-glob width=40 block-text ** The VALUE of this setting is a list of GLOB patterns matching files ** which are allowed to be edited using the /fileedit page. An empty list ** suppresses the feature. Example: *.md,*.txt The parsing rules are ** complex; see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax ** Note that /fileedit cannot edit binary files, so the list should not ** contain any globs for, e.g., images or PDFs. */ /* ** SETTING: forbid-delta-manifests boolean default=off ** If enabled on a client, new delta manifests are prohibited on ** commits. If enabled on a server, whenever a client attempts ** to obtain a check-in lock during auto-sync, the server will ** send the "pragma avoid-delta-manifests" statement in its reply, ** which will cause the client to avoid generating a delta ** manifest. */ /* ** SETTING: forum-close-policy boolean default=off ** If true, forum moderators may close/re-open forum posts, and reply ** to closed posts. If false, only administrators may do so. Note that ** this only affects the forum web UI, not post-closing tags which ** arrive via the command-line or from synchronization with a remote. */ /* ** SETTING: gdiff-command width=40 default=gdiff sensitive ** The value is an external command to run when performing a graphical ** diff. If undefined, text diff will be used. */ /* ** SETTING: gmerge-command width=40 sensitive ** The value is a graphical merge conflict resolver command operating ** on four files. Examples: ** ** kdiff3 "%baseline" "%original" "%merge" -o "%output" ** xxdiff "%original" "%baseline" "%merge" -M "%output" ** meld "%baseline" "%original" "%merge" --output "%output" */ /* ** SETTING: hash-digits width=5 default=10 ** The number of hexadecimal digits of the SHA3 hash to display. */ /* ** SETTING: http-port width=16 default=8080 ** The default TCP/IP port number to use by the "server" ** and "ui" commands. */ /* ** SETTING: https-login boolean default=off ** If true, then the Fossil web server will redirect unencrypted ** login screen requests to HTTPS. */ /* ** SETTING: ignore-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files that ** the "add", "addremove", "clean", and "extras" commands will ignore. ** Example: *.log,notes.txt The parsing rules are complex; see ** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ /* ** SETTING: keep-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files that ** the "clean" command must not delete. Example: build/precious.exe ** The parsing rules are complex; see ** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ /* ** SETTING: localauth boolean default=off ** If enabled, require that HTTP connections from the loopback ** address (127.0.0.1) be authenticated by password. If false, ** some HTTP requests might be granted full "Setup" user ** privileges without having to present login credentials. ** This mechanism allows the "fossil ui" command to provide ** full access to the repository without requiring the user to ** log in first. ** ** In order for full "Setup" privilege to be granted without a ** login, the following conditions must be met: ** ** (1) This setting ("localauth") must be off ** (2) The HTTP request arrive over the loopback TCP/IP ** address (127.0.01) or else via SSH. ** (3) The request must be HTTP, not HTTPS. (This ** restriction is designed to help prevent accidentally ** providing "Setup" privileges to requests arriving ** over a reverse proxy.) ** (4) The command that launched the fossil server must be ** one of the following: ** (a) "fossil ui" ** (b) "fossil server" with the --localauth option ** (c) "fossil http" with the --localauth option ** (d) CGI with the "localauth" setting in the cgi script. ** ** For maximum security, set "localauth" to 1. However, because ** of the other restrictions (2) through (4), it should be safe ** to leave "localauth" set to 0 in most installations, and ** especially on cloned repositories on workstations. Leaving ** "localauth" at 0 makes the "fossil ui" command more convenient ** to use. */ /* ** SETTING: lock-timeout width=25 default=60 ** This is the number of seconds that a check-in lock will be held on ** the server before the lock expires. The default is a 60-second delay. ** Set this value to zero to disable the check-in lock mechanism. ** ** This value should be set on the server to which users auto-sync ** their work. This setting has no effect on client repositories. The ** check-in lock mechanism is only effective if all users are auto-syncing ** to the same server. ** ** Check-in locks are an advisory mechanism designed to help prevent ** accidental forks due to a check-in race in installations where many ** users are committing to the same branch and auto-sync is enabled. ** As forks are harmless, there is no danger in disabling this mechanism. ** However, keeping check-in locks turned on can help prevent unnecessary ** confusion. */ /* ** SETTING: main-branch width=40 default=trunk ** The value is the primary branch for the project. */ /* ** SETTING: manifest width=5 versionable ** If enabled, automatically create files "manifest" and "manifest.uuid" ** in every check-out. ** ** Optionally use combinations of characters 'r' for "manifest", ** 'u' for "manifest.uuid" and 't' for "manifest.tags". The SQLite ** and Fossil repositories both require manifests. */ /* ** SETTING: max-loadavg width=25 default=0.0 ** 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. */ /* ** SETTING: max-upload width=25 default=250000 ** A limit on the size of uplink HTTP requests. */ /* ** SETTING: mimetypes width=40 versionable block-text ** A list of file extension-to-mimetype mappings, one per line. e.g. ** "foo application/x-foo". File extensions are compared ** case-insensitively in the order listed in this setting. A leading ** '.' on file extensions is permitted but not required. */ /* ** SETTING: mtime-changes boolean default=on ** Use file modification times (mtimes) to detect when ** files have been modified. If disabled, all managed files ** are hashed to detect changes, which can be slow for large ** projects. */ /* ** SETTING: mv-rm-files boolean default=off ** If enabled, the "mv" and "rename" commands will also move ** the associated files within the check-out -AND- the "rm" ** and "delete" commands will also remove the associated ** files from within the check-out. */ /* ** SETTING: pgp-command width=40 sensitive ** Command used to clear-sign manifests at check-in. ** Default value is "gpg --clearsign -o" */ /* ** SETTING: proxy width=32 default=system ** URL of the HTTP proxy. If undefined or "system", the "http_proxy" ** environment variable is consulted. If "off", a direct HTTP connection is ** used. */ /* ** SETTING: redirect-to-https default=0 width=-1 ** Specifies whether or not to redirect http:// requests to ** https:// URIs. A value of 0 (the default) means not to ** redirect, 1 means to redirect only the /login page, and 2 ** means to always redirect. */ /* ** SETTING: relative-paths boolean default=on ** When showing changes and extras, report paths relative ** to the current working directory. */ /* ** SETTING: repo-cksum boolean default=on ** Compute checksums over all files in each check-out as a double-check ** of correctness. Disable this on large repositories for a performance ** improvement. */ /* ** SETTING: repolist-skin width=2 default=0 ** If non-zero then use this repository as the skin for a repository list ** such as created by the one of: ** ** 1) fossil server DIRECTORY --repolist ** 2) fossil ui DIRECTORY --repolist ** 3) fossil http DIRECTORY --repolist ** 4) (The "repolist" option in a CGI script) ** 5) fossil all ui ** 6) fossil all server ** ** All repositories are searched (in lexicographical order) and the first ** repository with a non-zero "repolist-skin" value is used as the skin ** for the repository list page. If none of the repositories on the list ** have a non-zero "repolist-skin" setting then the repository list is ** displayed using unadorned HTML ("skinless"). ** ** If repolist-skin has a value of 2, then the repository is omitted from ** the list in use cases 1 through 4, but not for 5 and 6. */ /* ** SETTING: self-pw-reset boolean default=off sensitive ** Allow users to request that an email containing a hyperlink ** to the /resetpw page be sent to their email address of record, ** thus allowing forgetful users to reset their forgotten passwords ** without administrator involvement. */ /* ** SETTING: self-register boolean default=off sensitive ** Allow users to register themselves through the HTTP UI. ** This is useful if you want to see other names than ** "Anonymous" in e.g. ticketing system. On the other hand ** users can not be deleted. */ /* ** SETTING: ssh-command width=40 sensitive ** The command used to talk to a remote machine with the "ssh://" protocol. */ /* ** SETTING: ssl-ca-location width=40 sensitive ** The full pathname to a file containing PEM encoded ** CA root certificates, or a directory of certificates ** with filenames formed from the certificate hashes as ** required by OpenSSL. ** ** If set, this will override the OS default list of ** OpenSSL CAs. If unset, the default list will be used. ** Some platforms may add additional certificates. ** Checking your platform behaviour is required if the ** exact contents of the CA root is critical for your ** application. ** ** This setting is overridden by environment variables ** SSL_CERT_FILE and SSL_CERT_DIR. */ /* ** SETTING: ssl-identity width=40 sensitive ** The full pathname to a file containing a certificate ** and private key in PEM format. Create by concatenating ** the certificate and private key files. ** ** This identity will be presented to SSL servers to ** authenticate this client, in addition to the normal ** password authentication. */ #ifdef FOSSIL_ENABLE_TCL /* ** SETTING: tcl boolean default=off sensitive ** If enabled 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. */ /* ** SETTING: tcl-setup width=40 block-text sensitive ** This is the setup script to be evaluated after creating ** and initializing the Tcl interpreter. By default, this ** is empty and no extra setup is performed. */ #endif /* FOSSIL_ENABLE_TCL */ /* ** SETTING: tclsh width=80 default=tclsh sensitive ** Name of the external TCL interpreter used for such things ** as running the GUI diff viewer launched by the --tk option ** of the various "diff" commands. */ #ifdef FOSSIL_ENABLE_TH1_DOCS /* ** SETTING: th1-docs boolean default=off sensitive ** If enabled, 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. */ #endif #ifdef FOSSIL_ENABLE_TH1_HOOKS /* ** SETTING: th1-hooks boolean default=off ** If enabled, special TH1 commands will be called before and ** after any Fossil command or web page. */ #endif /* ** SETTING: th1-setup width=40 block-text ** This is the setup script to be evaluated after creating ** and initializing the TH1 interpreter. By default, this ** is empty and no extra setup is performed. */ /* ** SETTING: th1-uri-regexp width=40 block-text ** Specify which URI's are allowed in HTTP requests from ** TH1 scripts. If empty, no HTTP requests are allowed ** whatsoever. */ /* ** SETTING: default-csp width=40 block-text keep-empty ** ** The text of the Content Security Policy that is included ** in the Content-Security-Policy: header field of the HTTP ** reply and in the default HTML <head> section that is added when the ** skin header does not specify a <head> section. The text "$nonce" ** is replaced by the random nonce that is created for each web page. ** ** If this setting is an empty string or is omitted, then ** the following default Content Security Policy is used: ** ** default-src 'self' data:; ** script-src 'self' 'nonce-$nonce'; ** style-src 'self' 'unsafe-inline'; ** img-src * data:; ** ** The default CSP is recommended. The main reason to change ** this setting would be to add CDNs from which it is safe to ** load additional content. */ /* ** SETTING: uv-sync boolean default=off ** If true, automatically send unversioned files as part ** of a "fossil clone" or "fossil sync" command. The ** default is false, in which case the -u option is ** needed to clone or sync unversioned files. */ /* ** SETTING: web-browser width=30 sensitive ** 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. */ /* ** SETTING: large-file-size width=10 default=200000000 ** Fossil considers any file whose size is greater than this value ** to be a "large file". Fossil might issue warnings if you try to ** "add" or "commit" a "large file". Set this value to 0 or less ** to disable all such warnings. */ /* ** 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. */ Setting *db_find_setting(const char *zName, int allowPrefix){ int lwr, mid, upr, c; int n = (int)strlen(zName) + !allowPrefix; int nSetting; const Setting *aSetting = setting_info(&nSetting); lwr = 0; upr = nSetting - 1; 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 (Setting*)&aSetting[mid]; } } return 0; } /* ** COMMAND: settings ** COMMAND: unset* ** ** Usage: %fossil settings ?SETTING? ?VALUE? ?OPTIONS? ** or: %fossil unset SETTING ?OPTIONS? ** ** The "settings" command with no arguments lists all settings and their ** values. With just a SETTING name it shows the current value of that setting. ** With a VALUE argument it changes the property for the current repository. ** ** Settings marked as versionable are overridden by the contents of the ** file named .fossil-settings/PROPERTY in the check-out root, if that ** file exists. ** ** The "unset" command clears a setting. ** ** Settings can have both a "local" repository-only value and "global" value ** that applies to all repositories. The local values are stored in the ** "config" table of the repository and the global values are stored in the ** configuration database. If both a local and a global value exists for a ** setting, the local value takes precedence. This command normally operates ** on the local settings. Use the --global option to change global settings. ** ** Options: ** --global Set or unset the given property globally instead of ** setting or unsetting it for the open repository only ** --exact Only consider exact name matches ** --value Only show the value of a given property (implies --exact) ** ** See also: [[configuration]] */ void setting_cmd(void){ int i; int globalFlag = find_option("global","g",0)!=0; int exactFlag = find_option("exact",0,0)!=0; int valueFlag = find_option("value",0,0)!=0; /* Undocumented "--test-for-subsystem SUBSYS" option used to test ** the db_get_for_subsystem() interface: */ const char *zSubsys = find_option("test-for-subsystem",0,1); int unsetFlag = g.argv[1][0]=='u'; int nSetting; const Setting *aSetting = setting_info(&nSetting); find_repository_option(); verify_all_options(); db_open_config(1, 0); if( !globalFlag ){ db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0); } if( !g.repositoryOpen ){ globalFlag = 1; } if( unsetFlag && g.argc!=3 ){ usage("PROPERTY ?-global?"); } if( valueFlag ){ if( g.argc!=3 ){ fossil_fatal("--value is only supported when qurying a given property"); } exactFlag = 1; } if( g.argc==2 ){ for(i=0; i<nSetting; i++){ print_setting(&aSetting[i], 0); } }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, !exactFlag); 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!=(int)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/*works-like:"x"*/, globalFlag); }else{ db_protect_only(PROTECT_NONE); db_set(pSetting->name/*works-like:"x"*/, g.argv[3], globalFlag); db_protect_pop(); } if( isManifest && g.localOpen ){ manifest_to_disk(db_lget_int("checkout", 0)); } }else{ while( pSetting->name ){ if( exactFlag ){ if( fossil_strcmp(pSetting->name,zName)!=0 ) break; }else{ if( fossil_strncmp(pSetting->name,zName,n)!=0 ) break; } if( zSubsys ){ char *zValue = db_get_for_subsystem(pSetting->name, zSubsys); fossil_print("%s (subsystem %s) ->", pSetting->name, zSubsys); if( zValue ){ fossil_print(" [%s]", zValue); fossil_free(zValue); } fossil_print("\n"); }else{ print_setting(pSetting, valueFlag); } pSetting++; } } }else{ usage("?PROPERTY? ?VALUE? ?-global?"); } } /* ** The input in a timespan measured in days. Return a string which ** describes that timespan in units of seconds, minutes, hours, days, ** or years, depending on its duration. */ char *db_timespan_name(double rSpan){ if( rSpan<0 ) rSpan = -rSpan; rSpan *= 24.0*3600.0; /* Convert units to seconds */ if( rSpan<120.0 ){ return sqlite3_mprintf("%.1f seconds", rSpan); } rSpan /= 60.0; /* Convert units to minutes */ if( rSpan<90.0 ){ return sqlite3_mprintf("%.1f minutes", rSpan); } rSpan /= 60.0; /* Convert units to hours */ if( rSpan<=48.0 ){ return sqlite3_mprintf("%.1f hours", rSpan); } rSpan /= 24.0; /* Convert units to days */ if( rSpan<=365.0 ){ return sqlite3_mprintf("%.1f days", rSpan); } rSpan /= 356.24; /* Convert units to years */ return sqlite3_mprintf("%.1f years", rSpan); } /* ** COMMAND: test-timespan ** ** Usage: %fossil test-timespan TIMESTAMP ** ** Print the approximate span of time from now to TIMESTAMP. */ void test_timespan_cmd(void){ double rDiff; 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; g.repositoryOpen = 0; g.localOpen = 0; } /* ** COMMAND: test-without-rowid ** ** Usage: %fossil test-without-rowid FILENAME... ** ** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID ** optimization. FILENAME can also be the configuration database file ** (~/.fossil or ~/.config/fossil.db) 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: ** -n|--dry-run 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"); blob_init(&allSql, "BEGIN;\n", -1); db_prepare(&q, "SELECT name, sql FROM main.sqlite_schema " " 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; if( !db_table_exists("repository","admin_log") ){ once = 1; db_multi_exec( "CREATE TABLE repository.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" ")" ); } } /* ** 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); } /* ** COMMAND: test-database-names ** ** Print the names of the various database files: ** (1) The main repository database ** (2) The local check-out database ** (3) The global configuration database */ void test_database_name_cmd(void){ db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); fossil_print("Repository database: %s\n", g.zRepositoryName); fossil_print("Local database: %s\n", g.zLocalDbName); fossil_print("Config database: %s\n", g.zConfigDbName); } /* ** Compute a "fingerprint" on the repository. A fingerprint is used ** to verify that that the repository has not been replaced by a clone ** of the same repository. More precisely, a fingerprint are used to ** verify that the mapping between SHA3 hashes and RID values is unchanged. ** ** The check-out database ("localdb") stores RID values. When associating ** a check-out database against a repository database, it is useful to verify ** the fingerprint so that we know tha the RID values in the check-out ** database still correspond to the correct entries in the BLOB table of ** the repository. ** ** The fingerprint is based on the RCVFROM table. When constructing a ** new fingerprint, use the most recent RCVFROM entry. (Set rcvid==0 to ** accomplish this.) When verifying an old fingerprint, use the same ** RCVFROM entry that generated the fingerprint in the first place. ** ** The fingerprint consists of the rcvid, a "/", and the MD5 checksum of ** the remaining fields of the RCVFROM table entry. MD5 is used for this ** because it is 4x faster than SHA3 and 5x faster than SHA1, and there ** are no security concerns - this is just a checksum, not a security ** token. */ char *db_fingerprint(int rcvid, int iVersion){ char *z = 0; Blob sql = BLOB_INITIALIZER; Stmt q; if( iVersion==0 ){ /* The original fingerprint algorithm used "quote(mtime)". But this ** could give slightly different answers depending on how the floating- ** point hardware is configured. For example, it gave different ** answers on native Linux versus running under valgrind. */ blob_append_sql(&sql, "SELECT rcvid, quote(uid), quote(mtime), quote(nonce), quote(ipaddr)" " FROM rcvfrom" ); }else{ /* These days, we use "datetime(mtime)" for more consistent answers */ blob_append_sql(&sql, "SELECT rcvid, quote(uid), datetime(mtime), quote(nonce), quote(ipaddr)" " FROM rcvfrom" ); } if( rcvid<=0 ){ blob_append_sql(&sql, " ORDER BY rcvid DESC LIMIT 1"); }else{ blob_append_sql(&sql, " WHERE rcvid=%d", rcvid); } db_prepare_blob(&q, &sql); blob_reset(&sql); if( db_step(&q)==SQLITE_ROW ){ int i; md5sum_init(); for(i=1; i<=4; i++){ md5sum_step_text(db_column_text(&q,i),-1); } z = mprintf("%d/%s",db_column_int(&q,0),md5sum_finish(0)); } db_finalize(&q); return z; } /* ** COMMAND: test-fingerprint ** ** Usage: %fossil test-fingerprint ?RCVID? ** ** Display the repository fingerprint using the supplied RCVID or ** using the latest RCVID if not is given on the command line. ** Show both the legacy and the newer version of the fingerprint, ** and the currently stored fingerprint if there is one. */ void test_fingerprint(void){ int rcvid = 0; db_find_and_open_repository(OPEN_ANY_SCHEMA,0); if( g.argc==3 ){ rcvid = atoi(g.argv[2]); }else if( g.argc!=2 ){ fossil_fatal("wrong number of arguments"); } fossil_print("legacy: %z\n", db_fingerprint(rcvid, 0)); fossil_print("version-1: %z\n", db_fingerprint(rcvid, 1)); if( g.localOpen ){ fossil_print("localdb: %z\n", db_lget("fingerprint","(none)")); fossil_print("db_fingerprint_ok(): %d\n", db_fingerprint_ok()); } fossil_print("Fossil version: %s - %.10s %.19s\n", RELEASE_VERSION, MANIFEST_DATE, MANIFEST_UUID); } /* ** Set the value of the "checkout" entry in the VVAR table. ** ** Also set "fingerprint" and "checkout-hash". */ void db_set_checkout(int rid){ char *z; db_lset_int("checkout", rid); if (rid != 0) { z = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",rid); db_lset("checkout-hash", z); fossil_free(z); z = db_fingerprint(0, 1); db_lset("fingerprint", z); fossil_free(z); } } /* ** Verify that the fingerprint recorded in the "fingerprint" entry ** of the VVAR table matches the fingerprint on the currently ** connected repository. Return true if the fingerprint is ok, and ** return false if the fingerprint does not match. */ int db_fingerprint_ok(void){ char *zCkout; /* The fingerprint recorded in the check-out database */ char *zRepo; /* The fingerprint of the repository */ int rc; /* Result */ if( !db_lget_int("checkout", 0) ){ /* We have an empty check-out, fingerprint is still NULL. */ return 2; } zCkout = db_text(0,"SELECT value FROM localdb.vvar WHERE name='fingerprint'"); if( zCkout==0 ){ /* This is an older check-out that does not record a fingerprint. ** We have to assume everything is ok */ return 2; } zRepo = db_fingerprint(atoi(zCkout), 1); rc = fossil_strcmp(zCkout,zRepo)==0; fossil_free(zRepo); /* If the initial test fails, try again using the older fingerprint ** algorithm */ if( !rc ){ zRepo = db_fingerprint(atoi(zCkout), 0); rc = fossil_strcmp(zCkout,zRepo)==0; fossil_free(zRepo); } fossil_free(zCkout); return rc; } /* ** Adds the given rid to the UNSENT table. */ void db_add_unsent(int rid){ db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", rid); } |
Added src/default.css.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* This CSS file holds the default implementations for all of fossil's CSS classes. When /style.css is requested, the rules in this file are emitted first, followed by (1) page-specific CSS (if any) and (2) skin-specific CSS. */ div.sidebox { float: right; background-color: white; border-width: medium; border-style: double; margin: 10px; } div.sideboxTitle { display: inline; font-weight: bold; } div.sideboxDescribed { display: inline; font-weight: bold; } span.disabled { color: red; } table.timelineTable { border-spacing: 0px 2px; } .timelineDate { white-space: nowrap; } span.timelineDisabled { font-style: italic; font-size: small; } tr.timelineCurrent { padding: .1em .2em; border: 1px dashed #446979; box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5); } .timelineSelected { padding: .1em .2em; border: 2px solid lightgray; background-color: #ffc; box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5); } .timelineSecondary { background-color: #cff; } tr.timelineSelected td { border-radius: 0; border-width: 0; } tr.timelineCurrent td { border-radius: 0; border-width: 0; } span.timelineLeaf { font-weight: bold; } span.timelineHistDsp { font-weight: bold; } td.timelineTime { vertical-align: top; text-align: right; white-space: nowrap; } td.timelineGraph { width: 20px; text-align: left; vertical-align: top; } span.timelineCompactComment { cursor: pointer; } span.timelineEllipsis { cursor: pointer; } .timelineModernCell, .timelineColumnarCell, .timelineDetailCell { vertical-align: top; text-align: left; padding: 0.75em; border-radius: 1em; } .timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] { background-color: #efefef; } .timelineModernDetail { font-size: 80%; text-align: right; float: right; opacity: 0.75; margin-top: 0.5em; margin-left: 1em; } .tl-canvas { margin: 0 6px 0 10px; } .tl-rail { width: 18px; } .tl-mergeoffset { width: 2px; } .tl-nodemark { margin-top: 5px; } .tl-node { width: 10px; height: 10px; border: 1px solid #000; background: #fff; cursor: pointer; } .tl-node.leaf:after { content: ''; position: absolute; top: 3px; left: 3px; width: 4px; height: 4px; background: #000; } .tl-node.closed-leaf svg { position: absolute; top: 0px; left: 0px; width: 10px; height: 10px; color: #000; } .tl-node.sel:after { content: ''; position: absolute; top: 2px; left: 2px; width: 6px; height: 6px; background: red; } .tl-arrow { width: 0; height: 0; transform: scale(.999); border: 0 solid transparent; } .tl-arrow.u { margin-top: -1px; border-width: 0 3px; border-bottom: 7px solid #000; } .tl-arrow.u.sm { border-bottom: 5px solid #000; } .tl-line { background: #000; width: 2px; } .tl-arrow.merge { height: 1px; border-width: 2px 0; } .tl-arrow.merge.l { border-right: 3px solid #000; } .tl-arrow.merge.r { border-left: 3px solid #000; } .tl-line.merge { width: 1px; } .tl-arrow.cherrypick { height: 1px; border-width: 2px 0; } .tl-arrow.cherrypick.l { border-right: 3px solid #000; } .tl-arrow.cherrypick.r { border-left: 3px solid #000; } .tl-line.cherrypick.h { width: 0px; border-top: 1px dashed #000; border-left: 0px dashed #000; background: rgba(255,255,255,0); } .tl-line.cherrypick.v { width: 0px; border-top: 0px dashed #000; border-left: 1px dashed #000; background: rgba(255,255,255,0); } .tl-arrow.warp { margin-left: 1px; border-width: 3px 0; border-left: 7px solid #600000; } .tl-line.warp { background: #600000; } .tl-line.dotted.v { width: 0px; border-left-width: 2px; border-left-style: dotted; background: rgba(255,255,255,0); } .tl-tooltip { text-align: center; padding: 5px 1em; border: 1px solid black; border-radius: 6px; position: absolute; z-index: 100; box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.75); } span.tagDsp { font-weight: bold; } span.wikiError { font-weight: bold; color: red; } span.infoTagCancelled { font-weight: bold; text-decoration: line-through; } span.infoTag { font-weight: bold; } span.wikiTagCancelled { text-decoration: line-through; } div.columns { padding: 0 2em 0 2em; max-width: 1000px; } div.columns > ul { margin: 0; padding: 0 0 0 1em; } div.columns > ul li:first-child { margin-top:0px; } .columns li { break-inside: avoid; page-break-inside: avoid; } body.help .columns li { white-space: nowrap /* keep command name aliases from wrapping */; } .filetree { margin: 1em 0; line-height: 1.5; } .filetree > ul { display: inline-block; } .filetree ul { margin: 0; padding: 0; list-style: none; } .filetree ul.collapsed { display: none; } .filetree ul ul { position: relative; margin: 0 0 0 21px; } .filetree li { position: relative; margin: 0; padding: 0; } .filetree li li:before { 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 { content: ''; position: absolute; top: -1.5em; bottom: 0; left: -35px; border-left: 2px solid #aaa; } .filetree li.last > ul:before { display: none; } .filetree a { position: relative; z-index: 1; display: table-cell; min-height: 16px; padding-left: 21px; background-image: url("data:image/gif;base64,R0lGODlhEAAQAJEAAP\/\/\/y\ EhIf\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfmgOUvoaqDSCxrEEfF14GqFX\ ImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw=="); background-position: center left; background-repeat: no-repeat; } ul.browser { list-style-type: none; padding: 10px; margin: 0px; white-space: nowrap; } ul.browser li.file { padding-top: 2px; } ul.browser li.file > a { padding-left: 20px; background-repeat: no-repeat; background-position: 0px center; background-image: url("data:image/gif;base64,R0lGODlhEAAQAJEAAP\/\/\/\ yEhIf\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfmgOUvoaqDSCxrEEfF14Gq\ FXImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw=="); } ul.browser li.dir { padding-top: 2px; } ul.browser li.dir > a { padding-left: 20px; background-image: url("data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIiI\ v\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo+\ jUs6b5Z/K4siDu5RPUFADs="); background-repeat: no-repeat; background-position: 0px center; } div.filetreeline { display: table; width: 100%; white-space: nowrap; } .filetree .dir > div.filetreeline > a { background-image: url("data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIiI\ v\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo\ +jUs6b5Z/K4siDu5RPUFADs="); } div.filetreeage { display: table-cell; padding-left: 1.5em; text-align: right; width: 8em; } div.filetreesize { display: table-cell; padding-left: 1em; text-align: right; width: 7em; } div.filetreeline:hover { background-color: #eee; } table.login_out { text-align: left; margin-right: 10px; margin-left: 10px; margin-top: 10px; } div.captcha { text-align: center; padding: 1ex; } table.captcha { margin: auto; padding: 10px; border-width: 4px; border-style: double; border-color: black; } pre.captcha { font-size: 50%; } td.login_out_label { text-align: center; } span.loginError { color: red; } span.note { font-weight: bold; } span.textareaLabel { font-weight: bold; } table.usetupLayoutTable { outline-style: none; padding: 0; margin: 25px; } td.usetupColumnLayout { vertical-align: top } table.usetupUserList { outline-style: double; outline-width: 1px; padding: 10px; } th.usetupListUser { text-align: right; padding-right: 20px; } th.usetupListCap { text-align: center; padding-right: 15px; } th.usetupListCon { text-align: left; } td.usetupListUser { text-align: right; padding-right: 20px; white-space:nowrap; } td.usetupListCap { text-align: center; padding-right: 15px; } td.usetupListCon { text-align: left } div.ueditCapBox { margin-right: 20px; margin-bottom: 20px; } td.usetupEditLabel { text-align: right; vertical-align: top; white-space: nowrap; } span.ueditInheritNobody { color: green; padding: .2em; } span.ueditInheritDeveloper { color: red; padding: .2em; } span.ueditInheritReader { color: black; padding: .2em; } span.ueditInheritAnonymous { color: blue; padding: .2em; } span.capability { font-weight: bold; } span.usertype { font-weight: bold; } span.usertype:before { content:"'"; } span.usertype:after { content:"'"; } p.missingPriv { color: blue; } span.wikiruleHead { font-weight: bold; } td.tktDspLabel { text-align: right; } td.tktDspValue { text-align: left; vertical-align: top; background-color: #d0d0d0; } td.tktTlOpen { color: #800; } td.tktTlClosed { color: #888; } span.tktError { color: red; font-weight: bold; } table.rpteditex { float: right; margin: 0; padding: 0; width: 125px; text-align: center; border-collapse: collapse; border-spacing: 0; } table.report { border: 1px solid #999; margin: 1em 0 1em 0; cursor: pointer; } td.rpteditex { border-width: thin; border-color: #000000; border-style: solid; } div.endContent { clear: both; } p.generalError { color: red; } p.tktsetupError { color: red; font-weight: bold; } p.xfersetupError { color: red; font-weight: bold; } p.thmainError { color: red; font-weight: bold; } span.thTrace { color: red; } p.reportError { color: red; font-weight: bold; } blockquote.reportError { color: red; font-weight: bold; } p.noMoreShun { color: blue; } p.shunned { color: blue; } span.brokenlink { color: red; } ul.filelist { margin-top: 3px; line-height: 100%; } ul.filelist li { padding-top: 1px; } /* Rules governing diff layout and colors */ table.diff { width: 100%; border-spacing: 0; border-radius: 5px; border: 1px solid black; font-size: 80%; } table.diff td.diffln{ padding: 0; } table.diff td.diffln > pre{ padding: 0 0.25em 0 0.5em; margin: 0; } table.diff td { vertical-align: top; padding: 0; overflow: hidden /*work around inner PRE slight overflow/overlap*/; } table.diff pre { margin: 0 0 0 0; padding: 0 0.5em; line-height: 1.275/*for mobile: forum post e6f4ee7de98b55c0*/; text-size-adjust: none /* ^^^ attempt to keep mobile from inflating some text */; } table.diff pre > ins, table.diff pre > del { /* Fill platform-dependent color gaps caused by inflated line-height */ padding: 0.062em 0 0.062em 0; } table.diff pre > ins > *, table.diff pre > del > *{ /* Avoid odd-looking color swatches in conjunction with (table.diff pre > ins/del) padding */ padding: inherit; } table.diff td.diffln > pre { padding: 0 0.35em 0 0.5em; } table.diff td > pre { box-sizing: border-box; /* Workaround for "slight wiggle" when using mouse-wheel in some FF versions, apparently caused by the increased line-height forcing these elements to be a *tick* larger than they should be but not large enough to force a scroll bar to show up. */ overflow-y: hidden; } tr.diffskip.jchunk { /* jchunk gets added from JS to diffskip rows when they are plugged into the /jchunk route. */ background-color: aliceblue; padding: 0; } tr.diffskip.jchunk > td { padding: 0.25em 0.5em; margin: 0; } tr.diffskip.jchunk:hover { /*background-color: rgba(127,127,127,0.5); cursor: pointer;*/ } tr.diffskip > td.chunkctrl { text-align: left; } tr.diffskip > td.chunkctrl > div { display: flex; align-items: center; } tr.diffskip > td.chunkctrl > div > span.error { padding: 0.25em 0.5em; border-radius: 0.5em; } tr.diffskip > td.chunkctrl .jcbutton /* class name .button breaks w/ some skins! */ { min-width: 3.5ex; max-width: 3.5ex; text-align: center; display: inline-block; padding: 0.1em 1em; margin: 0 1em 0 0; background-color: rgba(127,127,127,0.2); border-style: outset; border-width: 0; border-radius: 0.5em; opacity: 0.7; } tr.diffskip > td.chunkctrl .jcbutton.up:not(.down){ /* Simulate an arrow pointing up */ border-radius: 3em 3em 0.25em 0.25em; } tr.diffskip > td.chunkctrl .jcbutton.down:not(.up){ /* Simulate an arrow pointing down */ border-radius: 0.25em 0.25em 3em 3em; } tr.diffskip > td.chunkctrl .jcbutton > span { /* In order to increase the glyph size w/o increasing the em-based button size or border-radius, we need an extra layer of DOM element for the glyph. */ font-size: 150%; } tr.diffskip > td.chunkctrl .jcbutton.up > span::before { content: '⇡'; } tr.diffskip > td.chunkctrl .jcbutton.down > span::before { content: '⇣'; } tr.diffskip > td.chunkctrl .jcbutton.up.down > span::before { content: '⇡⇣'; } tr.diffskip > td.chunkctrl .jcbutton:hover { cursor: pointer; opacity: 1; filter: contrast(1); } td.diffln { width: 1px; text-align: right; padding: 0 1em 0 0; } td.difflne { padding-bottom: 0.4em; } td.diffsep { width: 1px; padding: 0 0.3em 0 0.5em; } td.difftxt pre { overflow-x: auto; } td.diffln ins { background-color: #a0e4b2; text-decoration: none; } td.diffln del { background-color: #ffc0c0; text-decoration: none; } td.difftxt del { background-color: #ffe8e8; text-decoration: none; } td.difftxt del > del { background-color: #ffc0c0; text-decoration: none; font-weight: bold; } td.difftxt del > del.edit { background-color: #c0c0ff; text-decoration: none; font-weight: bold; } td.difftxt ins { background-color: #dafbe1; text-decoration: none; } td.difftxt ins > ins { background-color: #a0e4b2; text-decoration: none; font-weight: bold; } td.difftxt ins > ins.edit { background-color: #c0c0ff; text-decoration: none; font-weight: bold; } body.tkt div.content li > table.udiff { margin-left: 1.5em; margin-top: 0.5em; } body.tkt div.content ol.tkt-changes > li:target > p > span { border-bottom: 3px solid gold; } body.tkt div.content ol.tkt-changes > li:target > ol { border-left: 1px solid gold; } span.modpending { color: #b03800; font-style: italic; } pre.th1result { white-space: pre-wrap; word-wrap: break-word; } pre.th1error { white-space: pre-wrap; word-wrap: break-word; color: red; } pre.textPlain { white-space: pre-wrap; word-wrap: break-word; } .statistics-report-graph-line { border: 2px solid #446979; background-color: #446979; } .statistics-report-graph-extra { border: 2px dashed #446979; border-left-style: none; } .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 { text-align: right; font-size: 0.8em; } .statistics-report-week-of-year-list { font-size: 0.8em; } #usetupEditCapability { font-weight: bold; } table.adminLogTable { text-align: left; } .adminLogTable .adminTime { text-align: left; vertical-align: top; white-space: nowrap; } .fileage table { border-spacing: 0; } .fileage tr:hover { background-color: #eee; } .fileage td { vertical-align: top; text-align: left; border-top: 1px solid #ddd; padding-top: 3px; } .fileage td:first-child { white-space: nowrap; } .fileage td:nth-child(2) { padding-left: 1em; padding-right: 1em; } .fileage td:nth-child(3) { word-wrap: break-word; max-width: 50%; } .brlist table { border-spacing: 0; } .brlist table th { text-align: left; padding: 0px 1em 0.5ex 0px; vertical-align: bottom; } .brlist table td { padding: 0px 2em 0px 0px; white-space: nowrap; } th.sort:after { margin-left: .4em; cursor: pointer; text-shadow: 0 0 0 #000; } th.sort.none:after { content: '\2666'; } th.sort.asc:after { content: '\2193'; } th.sort.desc:after { content: '\2191'; } span.snippet>mark { background-color: inherit; font-weight: bold; } div.searchForm { text-align: center; } p.searchEmpty { font-style: italic; } .clutter { display: none; } table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 1ex; } table.forum_post { margin-top: 1ex; margin-bottom: 1ex; margin-left: 0; margin-right: 0; border-spacing: 0; } span.forum_author { color: #888; font-size: 75%; } span.forum_author::after { content: " | "; } span.forum_age { color: #888; font-size: 85%; } span.forum_buttons { font-size: 85%; } span.forum_buttons::before { color: #888; content: " | "; } span.forum_npost { color: #888; font-size: 75%; } table.forumeditform td { vertical-align: top; border-collapse: collapse; padding: 1px; } div.forum_body p { margin-top: 0; } td.form_label { vertical-align: top; text-align: right; } .debug { background-color: #ffc; border: 2px solid #ff0; } div.forumEdit { border: 1px solid black; padding-left: 1ex; padding-right: 1ex; } div.forumTimeline { border: 1px solid black; padding-left: 1ex; padding-right: 1ex; max-width: 50em; overflow: auto; } div.forumTimeline code { white-space: pre-wrap; } div.markdown code { white-space: pre-wrap; } div.forumTime { border: 1px solid black; padding-left: 1ex; padding-right: 1ex; margin-top: 1ex; display: flex; flex-direction: column; } div.forumClosed { } div.forumClosed > .forumPostBody { opacity: 0.7; } div.forumClosed > .forumPostHdr::before { content: "[CLOSED] "; } /*div.forumClosed > div.forumPostBody { filter: blur(5px); }*/ div.forumpost-closure-warning { margin-top: 1em; margin-bottom: 1em; border-style: solid; padding: 0.25em 0.5em; background: #f4f400bb; /*font-weight: bold;*/ } div.forumpost-closure-warning input[type=submit] { padding: 0.25em; } div.forumpost-single-controls { /* UI controls along the bottom of a single post ** in the thread view. */ } .forum div > form { margin: 0.5em 0; display: inline-block; } .forum-post-collapser { /* Common style for the bottom-of-post and right-of-post expand/collapse widgets. */ font-size: 0.8em; padding: 0; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 0 0 0.5em 0.5em; background-color: rgba(0, 0, 0, 0.05); opacity: 0.8; cursor: pointer; } .forum-post-collapser.bottom { margin: 0 0 0.4em 0; height: 1.75em; line-height: 1.75em; /* ^^^ Those sizes are finely tuned for the current selection of arrow characters. If those change, these should, too. Remember that FF/Chrome simply do not agree on alignment with most values :/. */ display: flex; flex-direction: row; justify-content: space-between; } .forum-post-collapser.bottom > span { margin: 0 1em 0 1em; vertical-align: middle; } .forum-post-collapser.bottom > span::before { content: "⇣⇣⇣"; } .forum-post-collapser.bottom.expanded > span::before { content: "⇡⇡⇡" /*reminder: FF/Chrome cannot agree on alignment of â®*/; } div.forumPostBody{ max-height: 50em; overflow: auto; } div.forumPostBody.with-expander { display: flex; flex-direction: row; overflow: auto; } div.forumPostBody.with-expander:not(.expanded) > :first-child { overflow-y: hidden; } div.forumPostBody.with-expander > *:first-child { /* Main content DIV/PRE */ overflow: auto; flex: 10 1 auto; } div.forumPostBody.with-expander.expanded > *:first-child { margin-bottom: 0.5em /* try to suppress scroll bar */; } div.forumPostBody.with-expander .forum-post-collapser.right { /* "Tap zone" for expansion of the post, sits to the right of the post's content. */ flex: 1 10 auto; min-width: 1.25em; max-width: 1.25em; margin: 0 0 0 0.2em; overflow: hidden; display: flex; flex-direction: column; justify-content: space-around; align-items: center; border-radius: 0.1em; cursor: pointer; border-bottom: 0; border-radius: 0 0.5em 0 0; } div.forumPostBody.with-expander .forum-post-collapser.right > span:before { content: "⇣"; } div.forumPostBody.with-expander.expanded .forum-post-collapser.right > span:before { content: "⇡"; } div.forumPostBody.expanded { max-height: initial; } div.forumPostBody.shrunken { /* When an expandable post is un-expanded, it is shrunkend down to this size instead of its original size. */ max-height: 8em; } span.forumPostReplyTitle { /* thread title part of the page header when replying to a post */ font-style: italic; } div.forumSel { background-color: #cef; } div.forumObs { color: #bbb; } div.setup_forum-column { display: flex; flex-direction: column; } body.cpage-setup_forum > .content table { margin-bottom: 1em; } body.cpage-setup_forum > .content table.bordered { border: 1px solid; border-radius: 0.25em; } body.cpage-setup_forum > .content table td, body.cpage-setup_forum > .content table th { text-align: left; } body.cpage-setup_forum table.forum-settings-list > tbody > tr > td { min-width: 2em; } #capabilitySummary { text-align: center; } #capabilitySummary td { padding-left: 3ex; padding-right: 3ex; } #capabilitySummary th { padding-left: 1ex; padding-right: 1ex; } .capsumOff { background-color: #bbb; } .capsumRead { background-color: #bfb; } .capsumWrite { background-color: #ffb; } label { white-space: nowrap; } label[for] { cursor: pointer; } .copy-button { display: inline-block; width: 14px; height: 14px; /*Note: .24em is slightly smaller than the average width of a normal space.*/ margin: -2px .24em 0 0; padding: 0; border: 0; vertical-align: middle; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ d='M3,2h3.6l2.4,2.4v5.6h-6z'/%3E%3Cpath style='fill:rgb(80,128,208)' \ d='M4,5h4v1h-4zm0,2h4v1h-4z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ d='M5,3h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ d='M10,4.4v1.6h1.6zm-4,-0.6h3v3h-3zm0,3h6v5.4h-6z'/%3E%3Cpath style='fill:rgb(80,128,208)' \ d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: center; cursor: pointer; } .copy-button.disabled { filter: grayscale(1); opacity: 0.4; } .copy-button-flipped { /*Note: .16em is suitable for element grouping.*/ margin-left: .16em; margin-right: 0; } .nobr { white-space: nowrap; } .accordion { cursor: pointer; } .accordion_btn { display: inline-block; width: 16px; height: 16px; margin-right: .5em; vertical-align: middle; } /* Note: the order of the next 3 entries should be maintained for the hierarchical cascade to work. */ .accordion > .accordion_btn_plus { display: none; } .accordion_closed > .accordion_btn_minus { display: none; } .accordion_closed > .accordion_btn_plus { display: inline-block; } .accordion_panel { overflow: hidden; transition: max-height 0.25s ease-out; } .error { color: darkred; background: yellow; } .warning { color: black; background: yellow; } .hidden, .initially-hidden { /* The framework-wide way of hiding elements is to assign them th .hidden class. To make them visible again, remove it. The !important qualifiers are unfortunate but sometimes necessary when hidden element has other classes which specify visibility-related options. The .initially-hidden class is for pages which need to show, e.g., a progress widget while a large WASM blob loads. Elements aside from that load-time widget can be made .initially-hidden and then have that class removed once the long-running startup process is done. See /pikchrshow for an example. */ position: absolute !important; opacity: 0 !important; pointer-events: none !important; display: none !important; } input { max-width: 95%; } textarea { max-width: 95%; } img { max-width: 100%; } hr { /* Needed to keep /dir README.txt from floating right in some skins */ clear: both; } /** .tab-xxx: styles for fossil.tabs.js. */ .tab-container { width: 100%; display: flex; flex-direction: column; align-items: stretch; } .tab-container > #fossil-status-bar { margin-top: 0; } .tab-container > .tabs { padding: 0.25em; margin: 0; display: flex; flex-direction: column; border-width: 1px; border-style: outset; border-color: inherit; } .tab-container > .tabs > .tab-panel { align-self: stretch; flex: 10 1 auto; display: block; border: 0; padding: 0; margin: 0; } .tab-container > .tab-bar { display: flex; flex-direction: row; flex: 1 10 auto; align-self: stretch; flex-wrap: wrap; } .tab-container > .tab-bar > .tab-button { display: inline-block; border-radius: 0.25em 0.25em 0 0; margin: 0 0.1em; padding: 0.25em 0.75em; align-self: baseline; border-color: inherit; border-width: 1px; border-bottom: none; border-top-style: inset; border-left-style: inset; border-right-style: inset; cursor: pointer; opacity: 0.6; } .tab-container > .tab-bar > .tab-button.selected { text-decoration: underline; opacity: 1.0; border-top-style: outset; border-left-style: outset; border-right-style: outset; } /** The flex-xxx classes can be used to create basic flexbox layouts through the application of classes to the containing/contained objects. */ .flex-container { display: flex; } .flex-container.flex-row { flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: center; } .flex-container .flex-grow { flex-grow: 10; flex-shrink: 0; } .flex-container .flex-shrink { flex-grow: 0; flex-shrink: 10; } .flex-container.flex-row.stretch { flex-wrap: wrap; align-items: baseline; justify-content: stretch; margin: 0; } .flex-container.flex-column { flex-direction: column; flex-wrap: wrap; justify-content: center; align-items: center; } .flex-container.flex-column.stretch { align-items: stretch; margin: 0; } .flex-container.child-gap-small > * { margin: 0.25em; } #fossil-status-bar { display: block; border-width: 1px; border-style: inset; border-color: inherit; min-height: 1.5em; font-size: 1.2em; padding: 0.2em; margin: 0.25em 0; flex: 0 0 auto; } .font-size-80 { font-size: 80%; } .font-size-100 { font-size: 100%; } .font-size-125 { font-size: 125%; } .font-size-150 { font-size: 150%; } .font-size-175 { font-size: 175%; } .font-size-200 { font-size: 200%; } /** .input-with-label is intended to be a wrapper element which contain both a LABEL tag and an INPUT or SELECT control. The wrapper is "necessary", as opposed to placing the INPUT in the LABEL, so that we can include multiple INPUT elements (e.g. a set of radio buttons). Note that these elements must sometimes be BLOCK elements (e.g. DIV) so that certain nesting constructs are legal. */ .input-with-label { border: 1px inset rgba(128, 128, 128, 0.5); border-radius: 0.25em; padding: 0.1em; margin: 0 0.5em; display: inline-block; cursor: default; white-space: nowrap; } .input-with-label > * { vertical-align: middle; } .input-with-label > label { display: inline; /* some skins set label display to block! */ cursor: pointer; } .input-with-label > input { margin: 0; } .input-with-label > button { margin: 0; } .input-with-label > select { margin: 0; } .input-with-label > input[type=text] { margin: 0; } .input-with-label > textarea { margin: 0; } /* Browsers are unfortunately inconsistent in how they align checkboxes and radio buttons, even if they're given the same vertical-align value. 'middle' seems to be the least bad option, rather than the ideal. */ .input-with-label > input[type=checkbox] { vertical-align: middle; } .input-with-label > input[type=radio] { vertical-align: middle; } .input-with-label > label { font-weight: initial; margin: 0 0.25em 0 0.25em; vertical-align: middle; } table.numbered-lines { width: 100%; table-layout: fixed /* required to keep ultra-wide code from exceeding window width, and instead force a scrollbar on them. */; } table.numbered-lines > tbody > tr { line-height: 1.35; white-space: pre; } table.numbered-lines > tbody > tr > td { font-family: inherit; font-size: inherit; line-height: inherit; white-space: inherit; margin: 0; vertical-align: top; padding: 0.25em 0 0 0 /*prevents slight overlap at top */; } table.numbered-lines td.line-numbers { width: 4.5em; } table.numbered-lines td.line-numbers > pre { margin: 0.25em/*must match top PADDING of td.file-content > pre > code*/ 0 0 0; padding: 0; } table.numbered-lines td.line-numbers span { display: inline-block; margin: 0; padding: 0; line-height: inherit; font-size: inherit; font-family: inherit; cursor: pointer; white-space: pre; margin-right: 2px/*keep selection from nudging the right column */; text-align: right; } table.numbered-lines td.line-numbers span:hover { background-color: rgba(112, 112, 112, 0.25); } table.numbered-lines td.file-content { padding-left: 0.25em; } table.numbered-lines td.file-content > pre, table.numbered-lines td.file-content > pre > code { margin: 0; padding: 0; line-height: inherit; font-size: inherit; font-family: inherit; white-space: pre; display: block/*necessary for certain skins!*/; } table.numbered-lines td.file-content > pre { } table.numbered-lines td.file-content > pre > code { overflow: auto; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.25em/*any top padding here must match the top MARGIN of td.line-numbers's first span child or the lines/code will get misaligned. */; padding-bottom: 0.25em/*prevents a slight overlap at bottom from triggering a scroller*/; } table.numbered-lines td.file-content > pre > code > * { /* Defense against syntax highlighters indirectly messing up these properties... */ line-height: inherit; font-size: inherit; font-family: inherit; } table.numbered-lines td.line-numbers span.selected-line/*replacement*/ { font-weight: bold; color: blue; background-color: #d5d5ff; border: 1px blue solid; border-top-width: 0; border-bottom-width: 0; padding: 0; margin: 0; } table.numbered-lines td.line-numbers span.selected-line.start { border-top-width: 1px; margin-top: -1px/*restore alignment*/; } table.numbered-lines td.line-numbers span.selected-line.end { border-bottom-width: 1px; margin-top: -1px/*restore alignment*/; } table.numbered-lines td.line-numbers span.selected-line.start.end { margin-top: -2px/*restore alignment*/; } .fossil-tooltip { text-align: center; padding: 0.2em 1em; border: 1px solid black; border-radius: 0.5em; position: absolute; display: inline-block; z-index: 19/*below default skin's hamburger popup*/; box-shadow: -0.15em 0.15em 0.2em rgba(0, 0, 0, 0.75); background-color: inherit; color: inherit; } .fossil-PopupWidget { /* This class is ALWAYS set on every fossil.PopupWidget instance, in addition to client/app-configured classes. It should not get any style - it is only used for DOM element selecting/filtering purposes. */ } .fossil-toast-message { /* "toast"-style popup message. See fossil.popupwidget:toast() */ position: absolute; display: block; z-index: 1001; text-align: left; padding: 0.15em 0.5em; margin: 0; font-size: 1em; border-width: 1px; border-style: solid; border-color: rgba( 127, 127, 127, 0.75 ); border-radius: 0.25em; background-color: rgba(20, 20, 20, 1) /* problem: if we inherit the color it may either be transparent or inherit translucency via the skin, leaving it unreadable. Since we set the bg color we must also set the fg color. */; color: rgba(235, 235, 235, 0.9); } .fossil-PopupWidget a, .fossil-PopupWidget a:visited { color: initial; } .fossil-toast-message.error, .fossil-toast-message.warning { background: yellow; } .fossil-toast-message.error { font-weight: bold; color: darkred; border-color: darkred; } .fossil-toast-message.warning { color: black; } blockquote.file-content { /* file content block in the /file page */ margin: 0 1em; } /* Generic sidebar styling inherited by skins that don't make their own * arrangements. */ .markdown blockquote, p.blockquote, .sidebar { background-color: rgba(0, 0, 0, 0.05); border-left: 3px solid #777; padding: 0.1em 1em; } .sidebar { /* Generic form that can be applied to any block element. */ font-size: 90%; } div.sidebar { /* Special exception for div-type sidebars, where there is no p * wrapper inside to give us the extra padding we want. */ padding: 1em; } div.sidebar:not(.no-label):before { content: "Sidebar: "; font-weight: bold; } /** Circular "help" buttons intended to be placed to the right of another element and hold text text for it. These typically get initialized automatically at page startup via fossil.popupwidget.js, and can be manually initialized/created using window.fossil.helpButtonlets.setup/create(). All of their child content (plain text and/or DOM elements) gets moved out of the DOM and shown in a singleton popup when they are clicked. They may be SPAN elements if their children are all inline elements, otherwise they must be DIVs (block elements) so that nesting of block-element content is legal. */ .help-buttonlet { display: inline-block; min-width: 1em; max-width: 1em; min-height: 1em; max-height: 1em; cursor: pointer; margin: 0 0 0 0.35em; background-image: /* white question mark on blue circular background */ url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' \ viewBox='0 0 15.867574 15.867574'%3e%3ccircle cx='7.9337869' cy='7.9337869' r='7.9337869' \ style='fill:%23f0f0f0;stroke-width:1' /%3e%3ccircle cx='7.9337869' cy='7.9337869' \ r='6.9662519' style='fill:%23404040;stroke-width:1' /%3e%3ccircle cx='7.9337869' \ cy='7.9337869' r='5.9987168' style='fill:%235080d0;stroke-width:1' /%3e%3cpath \ d='M 9.2253789,9.8629486 H 6.5997716 v -0.356384 q 0,-0.5963983 0.2400139,-1.0546067 \ 0.240014,-0.4654816 1.0109681,-1.1782504 L 8.316235,6.8518647 Q 8.7308046,6.473661 \ 8.9199065,6.1390961 9.1162816,5.8045312 9.1162816,5.4699662 q 0,-0.5091205 -0.3491113,-0.7927734 \ -0.3491111,-0.2909259 -0.9746021,-0.2909259 -0.5891252,0 -1.2728012,0.247287 \ -0.6836761,0.240014 -1.4255375,0.720042 V 3.0698267 q 0.8800513,-0.3054724 1.6073661,-0.4509353 \ 0.7273151,-0.145463 1.403718,-0.145463 1.7746486,0 2.7056104,0.727315 0.930965,0.720042 \ 0.930965,2.1092135 0,0.7127686 -0.283654,1.2800746 -0.283652,0.5600324 -0.967329,1.2073428 \ L 10.025425,8.2119439 Q 9.530851,8.6628792 9.3781148,8.9392588 9.2253789,9.2083654 \ 9.2253789,9.535657 Z M 6.5997716,10.939376 h 2.6256073 v 2.589241 H 6.5997716 Z' \ style='fill:%23f8f8f8;stroke-width:1.35412836' /%3e%3c/svg%3e "); background-repeat: no-repeat; background-position: center; /* When not using a background image, this additional style works reasonably well along with a ::before content of "?": */ /*border-width: 1px; border-style: outset; border-radius: 0.5em; font-size: 100%; font-family: monspace; font-weight: 700; overflow: hidden; background-color: rgba(54, 54, 255,1); color: rgb(255, 255, 255); text-align: center; line-height: 1; */ } /*.help-buttonlet::before { content: "?"; }*/ /** We really want to hide all help text via CSS but CSS cannot select TEXT nodes. Thus we move them out of the way programmatically during initialization. */ .help-buttonlet > *{} /** CSS class for PopupWidget which wraps .help-buttonlet content. They also have class fossil-tooltip. We need an overly-exact selector here to be certain that this class's style overrides that of fossil-tooltip. */ .fossil-tooltip.help-buttonlet-content { cursor: default; text-align: left; border-style: outset; } noscript > .error { /* Part of the style_emit_noscript_for_js_page() interface. */ padding: 1em; font-size: 150%; } /************************************************************ pikchr... DOM structure: <DIV.pikchr-wrapper> <DIV.pikchr-svg> <SVG.pikchr>...</SVG> </DIV.pikchr-svg> <PRE.pikchr-src>...</PRE> </DIV.pikchr-wrapper> ************************************************************/ div.pikchr-wrapper {/*outer wrapper elem for a pikchr construct*/} div.pikchr-svg {/*wrapper for SVG.pikchr element*/} svg.pikchr {/*pikchr SVG*/ width: 100%/*necessary for SOME SVGs for Chrome!*/; } pre.pikchr-src {/*source code view for a pikchr (see fossil.pikchr.js)*/ box-sizing: border-box; text-align: left; } /* The .source-inline class tells the .source class that the source view, when enabled, should be "inline" (same position as the graphic), else the sources are shifted to the left as if they were "plain text". */ div.pikchr-wrapper.center:not(.source), div.pikchr-wrapper.center.source.source-inline{ text-align: center; /* Reminder for The Future: this impl also works: display: grid; place-items: center; and does not require setting display:inline-block on the relevant child items, but caniuse.com/css-grid suggests that some still-seemingly-legitimate browsers don't support grid mode. */ } div.pikchr-wrapper.center > div.pikchr-svg { width: 100%/*necessary for Chrome!*/; } div.pikchr-wrapper.center:not(.source) > pre.pikchr-src, div.pikchr-wrapper.center:not(.source) > div.pikchr-svg, /* ^^^ Centered non-source-view elements */ div.pikchr-wrapper.center.source.source-inline > pre.pikchr-src, div.pikchr-wrapper.center.source.source-inline > div.pikchr-svg /* ^^^ Centered inline-source-view elements */{ display:inline-block/*allows parent text-align to do the alignment*/; /* ^^^^ Browser incompatibility: inline-block causes the centered pikchr to shrink to the point of illegiblity in Chrome. The closest match on Chrome seems to be using 'unset', which centers by virtue of stretching it to the width of the window. Similarly, using {display: grid; place-items: center} centers and sizes well on FF but Chrome shrinks it in the same way. */ } div.pikchr-wrapper.indent:not(.source), div.pikchr-wrapper.indent.source.source-inline{ margin-left: 4em; } div.pikchr-wrapper.float-left:not(.source), div.pikchr-wrapper.float-left.source.source-inline { float: left; padding: 4em; } div.pikchr-wrapper.float-right:not(.source), div.pikchr-wrapper.float-right.source.source-inline{ float: right; padding: 4em; } /* For pikchr-wrapper.source mode, toggle pre.pikchr-src and svg.pikchr visibility... */ div.pikchr-wrapper.source > pre.pikchr-src { /* Source code ^^^^^^^ is visible, else it is hidden */ } div.pikchr-wrapper:not(.source) > pre.pikchr-src { /* Hide sources when image is being shown. */ position: absolute !important; opacity: 0 !important; pointer-events: none !important; display: none !important; } div.pikchr-wrapper.source > div.pikchr-svg { /* Hide image when sources are being shown. */ position: absolute !important; opacity: 0 !important; pointer-events: none !important; display: none !important; } /* An icon element intended for use as a button/menu for accessing app-specific settings. */ .settings-icon { /* Icon source: https://de.wikipedia.org/wiki/Datei:OOjs_UI_icon_settings.svg MIT License. */ background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg \ xmlns='http://www.w3.org/2000/svg' width='24' height='24' \ viewBox='0, 0, 24, 24'%3e%3cg id='settings'%3e%3cpath id='gear' \ d='M3 4h3v2h-3zM12 4h9v2h-9zM8 3h2c.552 0 1 .448 1 1v2c0 .552-.448 1-1 1h-2c-.552 \ 0-1-.448-1-1v-2c0-.552.448-1 1-1zM3 11h9v2h-9zM18 11h3v2h-3zM14 10h2c.552 0 1 .448 \ 1 1v2c0 .552-.448 1-1 1h-2c-.552 0-1-.448-1-1v-2c0-.552.448-1 1-1zM3 18h6v2h-6zM15 \ 18h6v2h-6zM11 17h2c.552 0 1 .448 1 1v2c0 .552-.448 1-1 1h-2c-.552 \ 0-1-.448-1-1v-2c0-.552.448-1 1-1z'/%3e%3c/g%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: center; display: inline-block; min-height: 1em; max-height: 1em; min-width: 1em; max-width: 1em; margin: 0; padding: 0.2em/*needed to avoid image truncation*/; border: 1px solid rgba(0,0,0,0.0)/*avoid resize when hover style kicks in*/; cursor: pointer; border-radius: 0.25em; } .settings-icon:hover { border: 1px outset rgba(127,127,127,1); } body.fossil-dark-style .settings-icon { filter: invert(100%); } input[type="checkbox"].diff-toggle { float: right; } body.branch .brlist > table > tbody > tr:hover:not(.selected), body.branch .brlist > table > tbody > tr.selected { background-color: #ffc; } body.branch .brlist > table > tbody td:first-child > input { cursor: pointer; } body.branch .brlist > table > tbody > tr > td:nth-child(1) { display: flex; flex-direction: row; justify-content: space-between; } body.branch .submenu > a.timeline-link { display: none; } body.branch .submenu > a.timeline-link.selected { display: inline; } /* Candidate fonts for various forms of monospaced text. Collected here * to avoid repeating this long list of fonts. */ code, kbd, pre, samp, tt, var, div.markdown ol.footnotes > li.fn-joined > sup.fn-joined, table.numbered-lines > tbody > tr, tr.diffskip > td.chunkctrl, #fossil-status-bar, .monospace { font-family: Source Code Pro, Menlo, Monaco, Consolas, Andale Mono, Ubuntu Mono, Deja Vu Sans Mono, Letter Gothic, Letter Gothic Std, Prestige Elite Std, Courier, Courier New, monospace; } div.markdown > ol.footnotes { font-size: 90%; } div.markdown > ol.footnotes > li { margin-bottom: 0.5em; } div.markdown ol.footnotes > li.fn-joined > sup.fn-joined { color: gray; } div.markdown ol.footnotes > li.fn-joined > sup.fn-joined::after { content: "(joined from multiple locations) "; } div.markdown ol.footnotes > li.fn-misreference { margin-top: 0.75em; margin-bottom: 0.75em; } div.markdown ol.footnotes > li.fn-toodeep > i, div.markdown ol.footnotes > li.fn-misreference, div.markdown ol.footnotes > li.fn-unreferenced { color: gray; } div.markdown ol.footnotes > li.fn-misreference > span { color: red; } div.markdown ol.footnotes > li.fn-misreference > span::after { content: " (use of undefined label)."; } div.markdown ol.footnotes > li.fn-unreferenced { padding-left: 0.5em; } div.markdown ol.footnotes > li.fn-unreferenced > code { color: red; } div.markdown ol.footnotes > li.fn-unreferenced > i::after { content: " was defined but is not referenced"; } div.markdown ol.footnotes > li.fn-toodeep > i::after { content: " depth of nesting of inline footnotes exceeded the limit"; } div.markdown ol.footnotes > li.fn-toodeep > pre, div.markdown ol.footnotes > li.fn-unreferenced > pre { color: gray; font-size: 85%; padding-left: 0.5em; margin-top: 0.25em; border-left: 2px solid red; } div.markdown ol.footnotes > li.fn-toodeep > pre { margin-left: 0.5em; } div.markdown > ol.footnotes > li > .fn-backrefs { margin-right: 0.5em; font-weight: bold; } div.markdown > ol.footnotes > li > .fn-backrefs > a, div.markdown sup.noteref > a { padding-left: 2px; padding-right: 2px; } div.markdown sup.noteref.misref, div.markdown sup.noteref.misref > a { color: red; font-size: 90%; } div.markdown sup.noteref > a:target, div.markdown span.notescope:target > sup.noteref > a, div.markdown span.notescope:hover > sup.noteref > a, div.markdown > ol.footnotes > li > .fn-backrefs > a:target { background: gold; } div.markdown span.notescope:hover, div.markdown span.notescope:target { border-bottom: 2px solid gold; } /* Objects in the "desktoponly" class are invisible on mobile */ @media screen and (max-width: 600px) { .desktoponly { display: none; } } /* Float sidebars to the right of the main content only if there's room. */ @media screen and (min-width: 600px) { .sidebar { float: right; max-width: 33%; margin-left: 1em; } } /* Objects in the "wideonly" class are invisible only on wide-screen desktops */ @media screen and (max-width: 1200px) { .wideonly { display: none; } } |
Changes to src/delta.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This module implements the delta compress algorithm. ** ** Though developed specifically for fossil, the code in this file | | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** 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 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 | #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.) */ typedef struct hash hash; struct hash { u16 a, b; /* Hash values */ u16 i; /* Start of the hash window */ char z[NHASH]; /* The values that have been hashed */ }; /* ** Initialize the rolling hash using the first NHASH characters of z[] */ static void hash_init(hash *pHash, const char *z){ u16 a, b, i; a = b = z[0]; for(i=1; i<NHASH; i++){ a += z[i]; b += a; } memcpy(pHash->z, z, NHASH); pHash->a = a & 0xffff; pHash->b = b & 0xffff; pHash->i = 0; } /* ** Advance the rolling hash by a single character "c" |
︙ | ︙ | |||
127 128 129 130 131 132 133 134 135 136 137 138 | /* ** Return a 32-bit hash value */ static u32 hash_32bit(hash *pHash){ 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){ | > > > > > > > > > > > > > > > > > > | | 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 | /* ** Return a 32-bit hash value */ static u32 hash_32bit(hash *pHash){ return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16); } /* ** Compute a hash on NHASH bytes. ** ** This routine is intended to be equivalent to: ** hash h; ** hash_init(&h, zInput); ** return hash_32bit(&h); */ static u32 hash_once(const char *z){ u16 a, b, i; a = b = z[0]; for(i=1; i<NHASH; i++){ a += z[i]; b += a; } return a | (((u32)b)<<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; |
︙ | ︙ | |||
183 184 185 186 187 188 189 | return v; } /* ** Return the number digits in the base-64 representation of a positive integer */ static int digit_count(int v){ | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | > > > | | | > > > | | | | 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 | return v; } /* ** Return the number digits in the base-64 representation of a positive integer */ static int digit_count(int v){ unsigned int i; int x; for(i=1, x=64; v>=x; i++, x <<= 6){} return i; } #ifdef __GNUC__ # define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__) #else # define GCC_VERSION 0 #endif /* ** Compute a 32-bit big-endian checksum on the N-byte buffer. If the ** buffer is not a multiple of 4 bytes length, compute the sum that would ** have occurred if the buffer was padded with zeros to the next multiple ** of four bytes. */ static unsigned int checksum(const char *zIn, size_t N){ static const int byteOrderTest = 1; const unsigned char *z = (const unsigned char *)zIn; const unsigned char *zEnd = (const unsigned char*)&zIn[N&~3]; unsigned sum = 0; assert( (z - (const unsigned char*)0)%4==0 ); /* Four-byte alignment */ if( 0==*(char*)&byteOrderTest ){ /* This is a big-endian machine */ while( z<zEnd ){ sum += *(unsigned*)z; z += 4; } }else{ /* A little-endian machine */ #if GCC_VERSION>=4003000 while( z<zEnd ){ sum += __builtin_bswap32(*(unsigned*)z); z += 4; } #elif defined(_MSC_VER) && _MSC_VER>=1300 while( z<zEnd ){ sum += _byteswap_ulong(*(unsigned*)z); z += 4; } #else unsigned sum0 = 0; unsigned sum1 = 0; unsigned sum2 = 0; while(N >= 16){ sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]); sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]); sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]); sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]); z += 16; N -= 16; } while(N >= 4){ sum0 += z[0]; sum1 += z[1]; sum2 += z[2]; sum += z[3]; z += 4; N -= 4; } sum += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); #endif } switch(N&3){ case 3: sum += (z[2] << 8); case 2: sum += (z[1] << 16); case 1: sum += (z[0] << 24); default: ; } return sum; } /* ** 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: |
︙ | ︙ | |||
273 274 275 276 277 278 279 | ** 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 | | | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | ** 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 */ |
︙ | ︙ | |||
315 316 317 318 319 320 321 | return zDelta - zOrigDelta; } /* Compute the hash table used to locate matching sections in the ** source file. */ nHash = lenSrc/NHASH; | | | < < | | < < | | | > | > > | | | 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 | return zDelta - zOrigDelta; } /* Compute the hash table used to locate matching sections in the ** source file. */ nHash = lenSrc/NHASH; collide = fossil_malloc( nHash*2*sizeof(int) ); memset(collide, -1, nHash*2*sizeof(int)); landmark = &collide[nHash]; for(i=0; i<(int)lenSrc-NHASH; i+=NHASH){ int hv = hash_once(&zSrc[i]) % nHash; collide[i/NHASH] = landmark[hv]; landmark[hv] = i/NHASH; } /* Begin scanning the target file and generating copy commands and ** literal sections of the delta. */ base = 0; /* We have already generated everything before zOut[base] */ while( base+NHASH<(int)lenOut ){ int iSrc, iBlock; unsigned int bestCnt, bestOfst=0, bestLitsz=0; hash_init(&h, &zOut[base]); i = 0; /* Trying to match a landmark against zOut[base+i] */ bestCnt = 0; while( 1 ){ int hv; 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. ** sz will be the overhead (in bytes) needed to encode the copy ** command. Only generate copy command if the overhead of the ** copy command is less than the amount of literal text to be copied. */ int cnt, ofst, litsz; int j, k, x, y; int sz; int limitX; /* Beginning at iSrc, match forwards as far as we can. j counts ** the number of characters that match */ iSrc = iBlock*NHASH; y = base+i; limitX = ( lenSrc-iSrc <= lenOut-y ) ? lenSrc : iSrc + lenOut - y; for(x=iSrc; x<limitX; x++, y++){ if( zSrc[x]!=zOut[y] ) break; } j = x - iSrc - 1; /* Beginning at iSrc-1, match backwards as far as we can. k counts ** the number of characters that match */ for(k=1; k<iSrc && k<=i; k++){ if( zSrc[iSrc-k]!=zOut[base+i-k] ) break; } k--; /* Compute the offset and size of the matching region */ ofst = iSrc-k; cnt = j+k+1; litsz = i-k; /* Number of bytes of literal text before the copy */ DEBUG2( printf("MATCH %d bytes at %d: [%s] litsz=%d\n", cnt, ofst, print16(&zSrc[ofst]), litsz); ) /* sz will hold the number of bytes needed to encode the "insert" ** command and the copy command, not counting the "insert" text */ sz = digit_count(i-k)+digit_count(cnt)+digit_count(ofst)+3; if( cnt>=sz && cnt>(int)bestCnt ){ /* Remember this match only if it is the best so far and it ** does not increase the file size */ bestCnt = cnt; bestOfst = iSrc-k; bestLitsz = litsz; DEBUG2( printf("... BEST SO FAR\n"); ) } |
︙ | ︙ | |||
421 422 423 424 425 426 427 | } base += bestCnt; putInt(bestCnt, &zDelta); *(zDelta++) = '@'; putInt(bestOfst, &zDelta); DEBUG2( printf("copy %d bytes from %d\n", bestCnt, bestOfst); ) *(zDelta++) = ','; | | | | | | | | 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 | } base += bestCnt; putInt(bestCnt, &zDelta); *(zDelta++) = '@'; putInt(bestOfst, &zDelta); DEBUG2( printf("copy %d bytes from %d\n", bestCnt, bestOfst); ) *(zDelta++) = ','; if( (int)(bestOfst + bestCnt -1) > lastRead ){ lastRead = bestOfst + bestCnt - 1; DEBUG2( printf("lastRead becomes %d\n", lastRead); ) } bestCnt = 0; break; } /* If we reach this point, it means no match is found so far */ if( base+i+NHASH>=(int)lenOut ){ /* We have reached the end of the file and have not found any ** matches. Do an "insert" for everything that does not match */ putInt(lenOut-base, &zDelta); *(zDelta++) = ':'; memcpy(zDelta, &zOut[base], lenOut-base); zDelta += lenOut-base; base = lenOut; break; } /* Advance the hash by one character. Keep looking for a match */ hash_next(&h, zOut[base+i+NHASH]); i++; } } /* Output a final "insert" record to get all the text at the end of ** the file that does not match anything in the source file. */ if( base<(int)lenOut ){ putInt(lenOut-base, &zDelta); *(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){ |
︙ | ︙ | |||
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 | int lenSrc, /* Length of the source file */ const char *zDelta, /* Delta to apply to the pattern */ int lenDelta, /* Length of the delta */ char *zOut /* Write the output into this preallocated buffer */ ){ unsigned int limit; unsigned int total = 0; char *zOrigOut = zOut; limit = getInt(&zDelta, &lenDelta); if( *zDelta!='\n' ){ /* ERROR: size integer not terminated by "\n" */ return -1; } zDelta++; lenDelta--; while( *zDelta && lenDelta>0 ){ unsigned int cnt, ofst; cnt = getInt(&zDelta, &lenDelta); switch( zDelta[0] ){ case '@': { zDelta++; lenDelta--; ofst = getInt(&zDelta, &lenDelta); | > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | int lenSrc, /* Length of the source file */ const char *zDelta, /* Delta to apply to the pattern */ int lenDelta, /* Length of the delta */ char *zOut /* Write the output into this preallocated buffer */ ){ unsigned int limit; unsigned int total = 0; #ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST char *zOrigOut = zOut; #endif limit = getInt(&zDelta, &lenDelta); if( *zDelta!='\n' ){ /* ERROR: size integer not terminated by "\n" */ return -1; } zDelta++; lenDelta--; while( *zDelta && lenDelta>0 ){ unsigned int cnt, ofst; cnt = getInt(&zDelta, &lenDelta); switch( zDelta[0] ){ case '@': { zDelta++; lenDelta--; ofst = getInt(&zDelta, &lenDelta); if( lenDelta>0 && zDelta[0]!=',' ){ /* ERROR: copy command not terminated by ',' */ return -1; } zDelta++; lenDelta--; DEBUG1( printf("COPY %d from %d\n", cnt, ofst); ) total += cnt; if( total>limit ){ /* ERROR: copy exceeds output file size */ return -1; } if( (int)(ofst+cnt) > lenSrc ){ /* ERROR: copy extends past end of input */ return -1; } memcpy(zOut, &zSrc[ofst], cnt); zOut += cnt; break; } case ':': { zDelta++; lenDelta--; total += cnt; if( total>limit ){ /* ERROR: insert command gives an output larger than predicted */ return -1; } DEBUG1( printf("INSERT %d\n", cnt); ) if( (int)cnt>lenDelta ){ /* ERROR: insert count exceeds size of delta */ return -1; } memcpy(zOut, zDelta, cnt); zOut += cnt; zDelta += cnt; lenDelta -= cnt; break; } case ';': { zDelta++; lenDelta--; zOut[0] = 0; #ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST if( cnt!=checksum(zOrigOut, total) ){ /* ERROR: bad checksum */ return -1; } #endif if( total!=limit ){ /* ERROR: generated size does not match predicted size */ return -1; } return total; } default: { /* ERROR: unknown delta operator */ return -1; } } } /* ERROR: unterminated delta */ return -1; } /* ** Analyze a delta. Figure out the total number of bytes copied from ** source to target, and the total number of bytes inserted by the delta, ** and return both numbers. */ int delta_analyze( const char *zDelta, /* Delta to apply to the pattern */ int lenDelta, /* Length of the delta */ int *pnCopy, /* OUT: Number of bytes copied */ int *pnInsert /* OUT: Number of bytes inserted */ ){ unsigned int nInsert = 0; unsigned int nCopy = 0; (void)getInt(&zDelta, &lenDelta); if( *zDelta!='\n' ){ /* ERROR: size integer not terminated by "\n" */ return -1; } zDelta++; lenDelta--; while( *zDelta && lenDelta>0 ){ unsigned int cnt; cnt = getInt(&zDelta, &lenDelta); switch( zDelta[0] ){ case '@': { zDelta++; lenDelta--; (void)getInt(&zDelta, &lenDelta); if( lenDelta>0 && zDelta[0]!=',' ){ /* ERROR: copy command not terminated by ',' */ return -1; } zDelta++; lenDelta--; nCopy += cnt; break; } case ':': { zDelta++; lenDelta--; nInsert += cnt; if( (int)cnt>lenDelta ){ /* ERROR: insert count exceeds size of delta */ return -1; } zDelta += cnt; lenDelta -= cnt; break; } case ';': { *pnCopy = nCopy; *pnInsert = nInsert; return 0; } default: { /* ERROR: unknown delta operator */ return -1; } } } /* ERROR: unterminated delta */ return -1; } |
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 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 | ** ******************************************************************************* ** ** 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; int lenOrig, lenTarg; int len; char *zRes; blob_zero(pDelta); zOrig = blob_materialize(pOriginal); lenOrig = blob_size(pOriginal); zTarg = blob_materialize(pTarget); lenTarg = blob_size(pTarget); blob_resize(pDelta, lenTarg+16); zRes = blob_materialize(pDelta); len = delta_create(zOrig, lenOrig, zTarg, lenTarg, zRes); blob_resize(pDelta, len); return 0; } /* ** COMMAND: test-delta-create ** ** Usage: %fossil test-delta-create FILE1 FILE2 DELTA ** ** Create and output a delta that carries FILE1 into FILE2. ** Store the result in DELTA. */ void delta_create_cmd(void){ Blob orig, target, delta; if( g.argc!=5 ){ usage("ORIGIN TARGET DELTA"); } if( blob_read_from_file(&orig, g.argv[2], ExtFILE)<0 ){ fossil_fatal("cannot read %s", g.argv[2]); } if( blob_read_from_file(&target, g.argv[3], ExtFILE)<0 ){ fossil_fatal("cannot read %s", g.argv[3]); } blob_delta_create(&orig, &target, &delta); if( blob_write_to_file(&delta, g.argv[4])<(int)blob_size(&delta) ){ fossil_fatal("cannot write %s", g.argv[4]); } blob_reset(&orig); blob_reset(&target); blob_reset(&delta); } /* ** COMMAND: test-delta-analyze ** ** Usage: %fossil test-delta-analyze FILE1 FILE2 ** ** Create and a delta that carries FILE1 into FILE2. Print the ** number bytes copied and the number of bytes inserted. */ void delta_analyze_cmd(void){ Blob orig, target, delta; int nCopy = 0; int nInsert = 0; int sz1, sz2, sz3; if( g.argc!=4 ){ usage("ORIGIN TARGET"); } if( blob_read_from_file(&orig, g.argv[2], ExtFILE)<0 ){ fossil_fatal("cannot read %s", g.argv[2]); } if( blob_read_from_file(&target, g.argv[3], ExtFILE)<0 ){ fossil_fatal("cannot read %s", g.argv[3]); } blob_delta_create(&orig, &target, &delta); delta_analyze(blob_buffer(&delta), blob_size(&delta), &nCopy, &nInsert); sz1 = blob_size(&orig); sz2 = blob_size(&target); sz3 = blob_size(&delta); blob_reset(&orig); blob_reset(&target); blob_reset(&delta); fossil_print("original size: %8d\n", sz1); fossil_print("bytes copied: %8d (%.2f%% of target)\n", nCopy, (100.0*nCopy)/sz2); fossil_print("bytes inserted: %8d (%.2f%% of target)\n", nInsert, (100.0*nInsert)/sz2); fossil_print("final size: %8d\n", sz2); fossil_print("delta size: %8d\n", sz3); } /* ** Apply the delta in pDelta to the original file pOriginal to generate ** the target file pTarget. The pTarget blob is initialized by this ** routine. ** ** It works ok for pTarget and pOriginal to be the same blob. ** ** Return the length of the target. Return -1 if there is an error. */ int blob_delta_apply(Blob *pOriginal, Blob *pDelta, Blob *pTarget){ int len, n; Blob out; n = delta_output_size(blob_buffer(pDelta), blob_size(pDelta)); blob_zero(&out); if( n<0 ) return -1; blob_resize(&out, n); len = delta_apply( blob_buffer(pOriginal), blob_size(pOriginal), blob_buffer(pDelta), blob_size(pDelta), blob_buffer(&out)); if( len<0 ){ blob_reset(&out); }else if( len!=n ){ blob_resize(&out, len); } if( pTarget==pOriginal ){ blob_reset(pOriginal); } *pTarget = out; return len; } /* ** COMMAND: test-delta-apply ** ** Usage: %fossil test-delta-apply FILE1 DELTA FILE2 ** ** Apply DELTA to FILE1 and output the result in FILE2. */ void delta_apply_cmd(void){ Blob orig, target, delta; if( g.argc!=5 ){ usage("ORIGIN DELTA TARGET"); } if( blob_read_from_file(&orig, g.argv[2], ExtFILE)<0 ){ fossil_fatal("cannot read %s", g.argv[2]); } if( blob_read_from_file(&delta, g.argv[3], ExtFILE)<0 ){ fossil_fatal("cannot read %s", g.argv[3]); } blob_init(&target, 0, 0); blob_delta_apply(&orig, &delta, &target); if( blob_write_to_file(&target, g.argv[4])<(int)blob_size(&target) ){ fossil_fatal("cannot write %s", g.argv[4]); } blob_reset(&orig); blob_reset(&target); blob_reset(&delta); } /* ** COMMAND: test-delta ** ** Usage: %fossil test-delta FILE1 FILE2 ** ** Read two files named on the command-line. Create and apply deltas ** going in both directions. Verify that the original files are ** correctly recovered. */ void cmd_test_delta(void){ Blob f1, f2; /* Original file content */ Blob d12, d21; /* Deltas from f1->f2 and f2->f1 */ Blob a1, a2; /* Recovered file content */ if( g.argc!=4 ) usage("FILE1 FILE2"); blob_read_from_file(&f1, g.argv[2], ExtFILE); blob_read_from_file(&f2, g.argv[3], ExtFILE); blob_delta_create(&f1, &f2, &d12); blob_delta_create(&f2, &f1, &d21); blob_delta_apply(&f1, &d12, &a2); blob_delta_apply(&f2, &d21, &a1); if( blob_compare(&f1,&a1) || blob_compare(&f2, &a2) ){ fossil_fatal("delta test failed"); } fossil_print("ok\n"); } |
Added src/deltafunc.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 | /* ** Copyright (c) 2019 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 module implements SQL interfaces to the delta logic. The code ** here is adapted from the ext/misc/fossildelta.c extension in SQLite. */ #include "config.h" #include "deltafunc.h" /* ** SQL functions: delta_create(X,Y) ** ** Return a delta that will transform X into Y. */ static void deltaCreateFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *aOrig; int nOrig; /* old blob */ const char *aNew; int nNew; /* new blob */ char *aOut; int nOut; /* output delta */ assert( argc==2 ); if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return; nOrig = sqlite3_value_bytes(argv[0]); aOrig = (const char*)sqlite3_value_blob(argv[0]); nNew = sqlite3_value_bytes(argv[1]); aNew = (const char*)sqlite3_value_blob(argv[1]); aOut = sqlite3_malloc64(nNew+70); if( aOut==0 ){ sqlite3_result_error_nomem(context); }else{ nOut = delta_create(aOrig, nOrig, aNew, nNew, aOut); if( nOut<0 ){ sqlite3_free(aOut); sqlite3_result_error(context, "cannot create fossil delta", -1); }else{ sqlite3_result_blob(context, aOut, nOut, sqlite3_free); } } } /* ** SQL functions: delta_apply(X,D) ** ** Return the result of applying delta D to input X. */ static void deltaApplyFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *aOrig; int nOrig; /* The X input */ const char *aDelta; int nDelta; /* The input delta (D) */ char *aOut; int nOut, nOut2; /* The output */ assert( argc==2 ); if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return; nOrig = sqlite3_value_bytes(argv[0]); aOrig = (const char*)sqlite3_value_blob(argv[0]); nDelta = sqlite3_value_bytes(argv[1]); aDelta = (const char*)sqlite3_value_blob(argv[1]); /* Figure out the size of the output */ nOut = delta_output_size(aDelta, nDelta); if( nOut<0 ){ sqlite3_result_error(context, "corrupt fossil delta", -1); return; } aOut = sqlite3_malloc64((sqlite3_int64)nOut+1); if( aOut==0 ){ sqlite3_result_error_nomem(context); }else{ nOut2 = delta_apply(aOrig, nOrig, aDelta, nDelta, aOut); if( nOut2!=nOut ){ sqlite3_free(aOut); sqlite3_result_error(context, "corrupt fossil delta", -1); }else{ sqlite3_result_blob(context, aOut, nOut, sqlite3_free); } } } /* ** SQL functions: delta_output_size(D) ** ** Return the size of the output that results from applying delta D. */ static void deltaOutputSizeFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *aDelta; int nDelta; /* The input delta (D) */ int nOut; /* Size of output */ assert( argc==1 ); if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; nDelta = sqlite3_value_bytes(argv[0]); aDelta = (const char*)sqlite3_value_blob(argv[0]); /* Figure out the size of the output */ nOut = delta_output_size(aDelta, nDelta); if( nOut<0 ){ sqlite3_result_error(context, "corrupt fossil delta", -1); return; }else{ sqlite3_result_int(context, nOut); } } /***************************************************************************** ** Table-valued SQL function: delta_parse(DELTA) ** ** Schema: ** ** CREATE TABLE delta_parse( ** op TEXT, ** a1 INT, ** a2 ANY, ** delta HIDDEN BLOB ** ); ** ** Given an input DELTA, this function parses the delta and returns ** rows for each entry in the delta. The op column has one of the ** values SIZE, COPY, INSERT, CHECKSUM, ERROR. ** ** Assuming no errors, the first row has op='SIZE'. a1 is the size of ** the output in bytes and a2 is NULL. ** ** After the initial SIZE row, there are zero or more 'COPY' and/or 'INSERT' ** rows. A COPY row means content is copied from the source into the ** output. Column a1 is the number of bytes to copy and a2 is the offset ** into source from which to begin copying. An INSERT row means to ** insert text into the output stream. Column a1 is the number of bytes ** to insert and column is a BLOB that contains the text to be inserted. ** ** The last row of a well-formed delta will have an op value of 'CHECKSUM'. ** The a1 column will be the value of the checksum and a2 will be NULL. ** ** If the input delta is not well-formed, then a row with an op value ** of 'ERROR' is returned. The a1 value of the ERROR row is the offset ** into the delta where the error was encountered and a2 is NULL. */ typedef struct deltaparsevtab_vtab deltaparsevtab_vtab; typedef struct deltaparsevtab_cursor deltaparsevtab_cursor; struct deltaparsevtab_vtab { sqlite3_vtab base; /* Base class - must be first */ /* No additional information needed */ }; struct deltaparsevtab_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ char *aDelta; /* The delta being parsed */ int nDelta; /* Number of bytes in the delta */ int iCursor; /* Current cursor location */ int eOp; /* Name of current operator */ unsigned int a1, a2; /* Arguments to current operator */ int iNext; /* Next cursor value */ }; /* Operator names: */ static const char *const azOp[] = { "SIZE", "COPY", "INSERT", "CHECKSUM", "ERROR", "EOF" }; #define DELTAPARSE_OP_SIZE 0 #define DELTAPARSE_OP_COPY 1 #define DELTAPARSE_OP_INSERT 2 #define DELTAPARSE_OP_CHECKSUM 3 #define DELTAPARSE_OP_ERROR 4 #define DELTAPARSE_OP_EOF 5 /* ** Read bytes from *pz and convert them into a positive integer. When ** finished, leave *pz pointing to the first character past the end of ** the integer. The *pLen parameter holds the length of the string ** in *pz and is decremented once for each character in the integer. */ static unsigned int deltaGetInt(const char **pz, int *pLen){ static const signed char zValue[] = { -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, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, 36, -1, 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, -1, -1, -1, 63, -1, }; unsigned int v = 0; int c; unsigned char *z = (unsigned char*)*pz; unsigned char *zStart = z; while( (c = zValue[0x7f&*(z++)])>=0 ){ v = (v<<6) + c; } z--; *pLen -= z - zStart; *pz = (char*)z; return v; } /* ** The deltaparsevtabConnect() method is invoked to create a new ** deltaparse virtual table. ** ** Think of this routine as the constructor for deltaparsevtab_vtab objects. ** ** All this routine needs to do is: ** ** (1) Allocate the deltaparsevtab_vtab object and initialize all fields. ** ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the ** result set of queries against the virtual table will look like. */ static int deltaparsevtabConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ deltaparsevtab_vtab *pNew; int rc; rc = sqlite3_declare_vtab(db, "CREATE TABLE x(op,a1,a2,delta HIDDEN)" ); /* For convenience, define symbolic names for the index to each column. */ #define DELTAPARSEVTAB_OP 0 #define DELTAPARSEVTAB_A1 1 #define DELTAPARSEVTAB_A2 2 #define DELTAPARSEVTAB_DELTA 3 if( rc==SQLITE_OK ){ pNew = sqlite3_malloc64( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); } return rc; } /* ** This method is the destructor for deltaparsevtab_vtab objects. */ static int deltaparsevtabDisconnect(sqlite3_vtab *pVtab){ deltaparsevtab_vtab *p = (deltaparsevtab_vtab*)pVtab; sqlite3_free(p); return SQLITE_OK; } /* ** Constructor for a new deltaparsevtab_cursor object. */ static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ deltaparsevtab_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Destructor for a deltaparsevtab_cursor. */ static int deltaparsevtabClose(sqlite3_vtab_cursor *cur){ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur; sqlite3_free(pCur->aDelta); sqlite3_free(pCur); return SQLITE_OK; } /* ** Advance a deltaparsevtab_cursor to its next row of output. */ static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur; const char *z; int i = 0; pCur->iCursor = pCur->iNext; z = pCur->aDelta + pCur->iCursor; pCur->a1 = deltaGetInt(&z, &i); switch( z[0] ){ case '@': { z++; pCur->a2 = deltaGetInt(&z, &i); pCur->eOp = DELTAPARSE_OP_COPY; pCur->iNext = (int)(&z[1] - pCur->aDelta); break; } case ':': { z++; pCur->a2 = (unsigned int)(z - pCur->aDelta); pCur->eOp = DELTAPARSE_OP_INSERT; pCur->iNext = (int)(&z[pCur->a1] - pCur->aDelta); break; } case ';': { pCur->eOp = DELTAPARSE_OP_CHECKSUM; pCur->iNext = pCur->nDelta; break; } default: { if( pCur->iNext==pCur->nDelta ){ pCur->eOp = DELTAPARSE_OP_EOF; }else{ pCur->eOp = DELTAPARSE_OP_ERROR; pCur->iNext = pCur->nDelta; } break; } } return SQLITE_OK; } /* ** Return values of columns for the row at which the deltaparsevtab_cursor ** is currently pointing. */ static int deltaparsevtabColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur; switch( i ){ case DELTAPARSEVTAB_OP: { sqlite3_result_text(ctx, azOp[pCur->eOp], -1, SQLITE_STATIC); break; } case DELTAPARSEVTAB_A1: { sqlite3_result_int(ctx, pCur->a1); break; } case DELTAPARSEVTAB_A2: { if( pCur->eOp==DELTAPARSE_OP_COPY ){ sqlite3_result_int(ctx, pCur->a2); }else if( pCur->eOp==DELTAPARSE_OP_INSERT ){ sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1, SQLITE_TRANSIENT); } break; } case DELTAPARSEVTAB_DELTA: { sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT); break; } } return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. */ static int deltaparsevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur; *pRowid = pCur->iCursor; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur; return pCur->eOp==DELTAPARSE_OP_EOF; } /* ** This method is called to "rewind" the deltaparsevtab_cursor object back ** to the first row of output. This method is always called at least ** once prior to any call to deltaparsevtabColumn() or deltaparsevtabRowid() or ** deltaparsevtabEof(). */ static int deltaparsevtabFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor *)pVtabCursor; const char *a; int i = 0; pCur->eOp = DELTAPARSE_OP_ERROR; if( idxNum!=1 ){ return SQLITE_OK; } pCur->nDelta = sqlite3_value_bytes(argv[0]); a = (const char*)sqlite3_value_blob(argv[0]); if( pCur->nDelta==0 || a==0 ){ return SQLITE_OK; } pCur->aDelta = sqlite3_malloc64( pCur->nDelta+1 ); if( pCur->aDelta==0 ){ pCur->nDelta = 0; return SQLITE_NOMEM; } memcpy(pCur->aDelta, a, pCur->nDelta); pCur->aDelta[pCur->nDelta] = 0; a = pCur->aDelta; pCur->eOp = DELTAPARSE_OP_SIZE; pCur->a1 = deltaGetInt(&a, &i); if( a[0]!='\n' ){ pCur->eOp = DELTAPARSE_OP_ERROR; pCur->a1 = pCur->a2 = 0; pCur->iNext = pCur->nDelta; return SQLITE_OK; } a++; pCur->iNext = (unsigned int)(a - pCur->aDelta); return SQLITE_OK; } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. */ static int deltaparsevtabBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ int i; for(i=0; i<pIdxInfo->nConstraint; i++){ if( pIdxInfo->aConstraint[i].iColumn != DELTAPARSEVTAB_DELTA ) continue; if( pIdxInfo->aConstraint[i].usable==0 ) continue; if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; pIdxInfo->aConstraintUsage[i].argvIndex = 1; pIdxInfo->aConstraintUsage[i].omit = 1; pIdxInfo->estimatedCost = (double)1; pIdxInfo->estimatedRows = 10; pIdxInfo->idxNum = 1; return SQLITE_OK; } pIdxInfo->idxNum = 0; pIdxInfo->estimatedCost = (double)0x7fffffff; pIdxInfo->estimatedRows = 0x7fffffff; return SQLITE_CONSTRAINT; } /* ** This following structure defines all the methods for the ** virtual table. */ static sqlite3_module deltaparsevtabModule = { /* iVersion */ 0, /* xCreate */ 0, /* xConnect */ deltaparsevtabConnect, /* xBestIndex */ deltaparsevtabBestIndex, /* xDisconnect */ deltaparsevtabDisconnect, /* xDestroy */ 0, /* xOpen */ deltaparsevtabOpen, /* xClose */ deltaparsevtabClose, /* xFilter */ deltaparsevtabFilter, /* xNext */ deltaparsevtabNext, /* xEof */ deltaparsevtabEof, /* xColumn */ deltaparsevtabColumn, /* xRowid */ deltaparsevtabRowid, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ 0, /* xIntegrity */ 0 }; /* ** Invoke this routine to register the various delta functions. */ int deltafunc_init(sqlite3 *db){ int rc = SQLITE_OK; rc = sqlite3_create_function(db, "delta_create", 2, SQLITE_UTF8, 0, deltaCreateFunc, 0, 0); if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "delta_apply", 2, SQLITE_UTF8, 0, deltaApplyFunc, 0, 0); } if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "delta_output_size", 1, SQLITE_UTF8, 0, deltaOutputSizeFunc, 0, 0); } if( rc==SQLITE_OK ){ rc = sqlite3_create_module(db, "delta_parse", &deltaparsevtabModule, 0); } return rc; } |
Changes to src/descendants.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 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to find descendants of a version ** or leaves of a version tree. */ #include "config.h" #include "descendants.h" #include <assert.h> /* ** Create a temporary table named "leaves" if it does not ** 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. |
︙ | ︙ | |||
53 54 55 56 57 58 59 | db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS leaves(" " rid INTEGER PRIMARY KEY" ");" "DELETE FROM leaves;" ); | < < < | < < < < < < < < < < < < | | | | | | 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 | db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS leaves(" " rid INTEGER PRIMARY KEY" ");" "DELETE FROM leaves;" ); if( iBase>0 ){ Bag seen; /* Descendants seen */ Bag pending; /* Unpropagated descendants */ Stmt q1; /* Query to find children of a check-in */ Stmt isBr; /* Query to check to see if a check-in starts a new branch */ Stmt ins; /* INSERT statement for a new record */ /* Initialize the bags. */ bag_init(&seen); bag_init(&pending); bag_insert(&pending, iBase); /* This query returns all non-branch-merge children of check-in :rid. ** ** If a child is a merge of a fork within the same branch, it is ** returned. Only merge children in different branches are excluded. */ db_prepare(&q1, "SELECT cid FROM plink" " WHERE pid=:rid" " AND (isprim" " OR 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 ); /* This query returns a single row if check-in :rid is the first ** check-in of a new branch. */ db_prepare(&isBr, "SELECT 1 FROM tagxref" " WHERE rid=:rid AND tagid=%d AND tagtype=2" " AND srcid>0", TAG_BRANCH ); /* This statement inserts check-in :rid into the LEAVES table. */ db_prepare(&ins, "INSERT OR IGNORE INTO leaves VALUES(:rid)"); while( bag_count(&pending) ){ int rid = bag_first(&pending); int cnt = 0; bag_remove(&pending, rid); db_bind_int(&q1, ":rid", rid); while( db_step(&q1)==SQLITE_ROW ){ int cid = db_column_int(&q1, 0); |
︙ | ︙ | |||
141 142 143 144 145 146 147 148 149 150 151 152 153 154 | } } 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" |
︙ | ︙ | |||
164 165 166 167 168 169 170 | " AND tagxref.tagtype>0)", TAG_CLOSED ); } } /* | | | > | > | | > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > | > > | < < > | > | > > > > > > > | > | > > > > > > > > < < | < < > | | < | | > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | > < < > > > | < < > > > | < < < | | | > > > > > > | > > > > > | | > > | < | > | > > > > > > > > > > | > > > > > > > > > > > | | < < | > > > > > > > > > > > > | < < < < < < > | > > > > > > > > > > > | > > > | | | | | | > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > < | < < | < | > > > > > > > > > > > > > | > > > > > > > | > > > > > > > | > | > > > > | < < < < < < | > | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > | | > > > | | | | < > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > < > > > | > | > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > | > | | > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < | | 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 | " AND tagxref.tagtype>0)", TAG_CLOSED ); } } /* ** Load the record ID rid and up to |N|-1 closest ancestors into ** the "ok" table. If N is zero, no limit. If ridBackTo is not zero ** then stop the search upon reaching the ancestor with rid==ridBackTo. */ void compute_ancestors(int rid, int N, int directOnly, int ridBackTo){ if( !N ){ N = -1; }else if( N<0 ){ N = -N; } if( directOnly ){ /* Direct mode means to show primary parents only */ 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" " AND plink.isPrim" " ORDER BY mtime DESC LIMIT %d" " )" "INSERT INTO ok" " SELECT rid FROM ancestor;", rid, rid, N ); }else{ /* If not in directMode, also include merge parents, including ** cherrypick merges. Except, terminate searches at the cherrypick ** merge parent itself. In other words, include: ** (1) Primary parents ** (2) Merge parents ** (3) Cherrypick merge parents. ** (4) All ancestores of 1 and 2 but not of 3. */ double rLimitMtime = 0.0; if( ridBackTo ){ rLimitMtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ridBackTo); } db_multi_exec( "WITH RECURSIVE " " parent(pid,cid,isCP) AS (" " SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink" " UNION ALL" " SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude" " )," " ancestor(rid, mtime, isCP) AS (" " SELECT %d, mtime, 0 FROM event WHERE objid=%d " " UNION " " SELECT parent.pid, event.mtime, parent.isCP" " FROM ancestor, parent, event" " WHERE parent.cid=ancestor.rid" " AND event.objid=parent.pid" " AND NOT ancestor.isCP" " AND (event.mtime>=%.17g OR parent.pid=%d)" " ORDER BY mtime DESC LIMIT %d" " )" "INSERT OR IGNORE INTO ok" " SELECT rid FROM ancestor;", rid, rid, rLimitMtime, ridBackTo, N ); if( ridBackTo && db_changes()>1 ){ db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo); } } } /* ** Compute the youngest ancestor of record ID rid that is a member of ** branch zBranch. */ int compute_youngest_ancestor_in_branch(int rid, const char *zBranch){ return db_int(0, "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" " ORDER BY mtime DESC" " )" " SELECT ancestor.rid FROM ancestor" " WHERE EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagxref.rid=ancestor.rid" " AND value=%Q AND tagtype>0)" " LIMIT 1", rid, rid, TAG_BRANCH, zBranch ); } /* ** Compute all 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 ** direct ancestor as the largest generation number. */ void compute_direct_ancestors(int rid){ db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS ancestor(rid INTEGER UNIQUE NOT NULL," " generation INTEGER PRIMARY KEY);" "DELETE FROM ancestor;" "WITH RECURSIVE g(x,i) AS (" " VALUES(%d,1)" " UNION ALL" " SELECT plink.pid, g.i+1 FROM plink, g" " WHERE plink.cid=g.x AND plink.isprim)" "INSERT INTO ancestor(rid,generation) SELECT x,i FROM g;", rid ); } /* ** Compute the "mtime" of the file given whose blob.rid is "fid" that ** is part of check-in "vid". The mtime will be the mtime on vid or ** some ancestor of vid where fid first appears. */ int mtime_of_manifest_file( int vid, /* The check-in that contains fid */ int fid, /* The id of the file whose check-in time is sought */ i64 *pMTime /* Write result here */ ){ static int prevVid = -1; static Stmt q; if( prevVid!=vid ){ prevVid = vid; db_multi_exec("CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" "DELETE FROM ok;"); compute_ancestors(vid, 100000000, 1, 0); } db_static_prepare(&q, "SELECT (max(event.mtime)-2440587.5)*86400 FROM mlink, event" " WHERE mlink.mid=event.objid" " AND +mlink.mid IN ok" " AND mlink.fid=:fid"); db_bind_int(&q, ":fid", fid); if( db_step(&q)!=SQLITE_ROW ){ db_reset(&q); return 1; } *pMTime = db_column_int64(&q, 0); db_reset(&q); return 0; } /* ** Load the record ID rid and up to |N|-1 closest descendants into ** the "ok" table. If N is zero, no limit. */ void compute_descendants(int rid, int N){ if( !N ){ N = -1; }else if( N<0 ){ N = -N; } db_multi_exec( "WITH RECURSIVE" " dx(rid,mtime) AS (" " SELECT %d, 0" " UNION" " SELECT plink.cid, plink.mtime FROM dx, plink" " WHERE plink.pid=dx.rid" " ORDER BY 2" " )" "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d", rid, N ); } /* ** 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 REPO Extract info from repository REPO ** -W|--width N Width of lines (default is to auto-detect). ** Must be greater than 20 or else 0 for no ** limit, resulting in a one 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, 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 ** --bybranch Order output by branch name ** -c|--closed Show only closed leaves ** -m|--multiple Show only cases with multiple leaves on a single branch ** --recompute Recompute the "leaf" table in the repository DB ** -W|--width N Width of lines (default is to auto-detect). Must be ** more than 39 or else 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; int multipleFlag = find_option("multiple","m",0)!=0; const char *zWidth = find_option("width","W",1); char *zLastBr = 0; int n, width; char zLineNo[10]; char * const zMainBranch = db_get("main-branch","trunk"); if( multipleFlag ) byBranch = 1; 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); if( !multipleFlag ){ /* The usual case - show all leaves */ blob_append_sql(&sql, " AND blob.rid IN leaf"); }else{ /* Show only leaves where two are more occur in the same branch */ db_multi_exec( "CREATE TEMP TABLE openLeaf(rid INTEGER PRIMARY KEY);" "INSERT INTO openLeaf(rid)" " SELECT rid FROM leaf" " WHERE NOT EXISTS(" " SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=leaf.rid);", TAG_CLOSED ); db_multi_exec( "CREATE TEMP TABLE ambiguousBranch(brname TEXT);" "INSERT INTO ambiguousBranch(brname)" " SELECT (SELECT value FROM tagxref WHERE tagid=%d AND rid=openLeaf.rid)" " FROM openLeaf" " GROUP BY 1 HAVING count(*)>1;", TAG_BRANCH ); db_multi_exec( "CREATE TEMP TABLE ambiguousLeaf(rid INTEGER PRIMARY KEY);\n" "INSERT INTO ambiguousLeaf(rid)\n" " SELECT rid FROM openLeaf\n" " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=openLeaf.rid)" " IN (SELECT brname FROM ambiguousBranch);", TAG_BRANCH ); blob_append_sql(&sql, " AND blob.rid IN ambiguousLeaf"); } 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 = 0; char * zBranchPoint = 0; if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){ fossil_print("*** %s ***\n", zBr); fossil_free(zLastBr); zLastBr = fossil_strdup(zBr); if( multipleFlag ) n = 0; } n++; sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n); fossil_print("%6s ", zLineNo); if(0!=fossil_strcmp(zBr,zMainBranch)){ int ridOfRoot; z = mprintf("root:%s", zId); ridOfRoot = symbolic_name_to_rid(z, "ci"); if(ridOfRoot>0){ zBranchPoint = mprintf(" (branched from: [%.*z])", hash_digits(0), rid_to_uuid(ridOfRoot)); } fossil_free(z); } z = mprintf("%s [%S] %s%s", zDate, zId, zCom, zBranchPoint ? zBranchPoint : ""); comment_print(z, zCom, 7, width, get_comment_format()); fossil_free(z); fossil_free(zBranchPoint); } fossil_free(zMainBranch); fossil_free(zLastBr); db_finalize(&q); } /* ** WEBPAGE: leaves ** ** Show leaf check-ins in a timeline. By default only open leaves ** are listed. ** ** A "leaf" is a check-in with no children in the same branch. A ** "closed leaf" is a leaf that has a "closed" tag. An "open leaf" ** is a leaf without a "closed" tag. ** ** Query parameters: ** ** all Show all leaves ** closed Show only closed leaves ** ng No graph ** nohidden Hide check-ins with "hidden" tag ** onlyhidden Show only check-ins with "hidden" tag ** brbg Background color by branch name ** ubg Background color by user name */ void leaves_page(void){ Blob sql; Stmt q; int showAll = P("all")!=0; int showClosed = P("closed")!=0; int fNg = PB("ng")!=0; /* Flag for the "ng" query parameter */ int fNoHidden = PB("nohidden")!=0; /* "nohidden" query parameter */ int fOnlyHidden = PB("onlyhidden")!=0; /* "onlyhidden" query parameter */ int fBrBg = PB("brbg")!=0; /* Flag for the "brbg" query parameter */ int fUBg = PB("ubg")!=0; /* Flag for the "ubg" query parameter */ HQuery url; /* URL to /leaves plus query parameters */ int tmFlags; /* Timeline display flags */ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } url_initialize(&url, "leaves"); if( fNg ) url_add_parameter(&url, "ng", ""); if( fNoHidden ) url_add_parameter(&url, "nohidden", ""); if( fOnlyHidden ) url_add_parameter(&url, "onlyhidden", ""); if( fBrBg ) url_add_parameter(&url, "brbg", ""); if( fUBg ) url_add_parameter(&url, "ubg", ""); if( !showAll ){ style_submenu_element("All", "%s", url_render(&url, "all", "", 0, 0)); } if( !showClosed ){ style_submenu_element("Closed", "%s", url_render(&url, "closed", "", 0, 0)); } if( showClosed || showAll ){ style_submenu_element("Open", "%s", url_render(&url, 0, 0, 0, 0)); } url_reset(&url); cgi_check_for_malice(); style_set_current_feature("leaves"); style_header("Leaves"); login_anonymous_available(); timeline_ss_submenu(); #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")); } if( fNoHidden || fOnlyHidden ){ const char* zUnaryOp = fNoHidden ? "NOT" : ""; blob_append_sql(&sql, " AND %s EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n", zUnaryOp/*safe-for-%s*/, TAG_HIDDEN); } db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql)); blob_reset(&sql); /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too ** many descenders to (off-screen) parents. */ tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL; if( fNg==0 ) tmFlags |= TIMELINE_GRAPH; if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR; if( fUBg ) tmFlags |= TIMELINE_UCOLOR; www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); db_finalize(&q); @ <br> style_finish_page(); } #if INTERFACE /* Flag parameters to compute_uses_file() */ #define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */ #endif /* ** Add to table zTab the record ID (rid) of every check-in that contains ** the file fid. */ void compute_uses_file(const char *zTab, int fid, int usesFlags){ Bag seen; 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); db_reset(&ins); } db_finalize(&q); db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid); while( db_step(&q)==SQLITE_ROW ){ int mid = db_column_int(&q, 0); bag_insert(&seen, mid); 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; bag_insert(&seen, mid); bag_insert(&pending, mid); db_bind_int(&ins, ":rid", mid); db_step(&ins); db_reset(&ins); } db_reset(&q); } db_finalize(&q); db_finalize(&ins); bag_clear(&seen); bag_clear(&pending); } |
Changes to src/diff.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2007 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".) | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | | | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > | > > > > > | | > < < < < < | | | > > > | | < | | | < < > > > > | > | | < < < < < < > > | > > > | > > > > > > | | > > > > | > > > > > > > > > > > > > | | < > > > | | > | < | | < < < < | < < | > | < | < | | > > > > | < | | > | > > | | < > > > | | < < < < > | > > > > | < < < < | < > > > > > > > | < | > > | | < < > > > > > > | < < < < < > > > | > > > > | > > < | | > > > > | | | | > > > > > | | 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 | /* ** Copyright (c) 2007 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 compute a "diff" between two ** text files. */ #include "config.h" #include "diff.h" #include <assert.h> #include <errno.h> #if INTERFACE /* ** Flag parameters to the text_diff() routine used to control the formatting ** of the diff output. */ #define DIFF_IGNORE_EOLWS 0x00000001 /* Ignore end-of-line whitespace */ #define DIFF_IGNORE_ALLWS 0x00000003 /* Ignore all whitespace */ #define DIFF_SIDEBYSIDE 0x00000004 /* Generate a side-by-side diff */ #define DIFF_VERBOSE 0x00000008 /* Missing shown as empty files */ #define DIFF_BRIEF 0x00000010 /* Show filenames only */ #define DIFF_HTML 0x00000020 /* Render for HTML */ #define DIFF_LINENO 0x00000040 /* Show line numbers */ #define DIFF_NUMSTAT 0x00000080 /* Show line count of changes */ #define DIFF_NOOPT 0x00000100 /* Suppress optimizations (debug) */ #define DIFF_INVERT 0x00000200 /* Invert the diff (debug) */ #define DIFF_CONTEXT_EX 0x00000400 /* Use context even if zero */ #define DIFF_NOTTOOBIG 0x00000800 /* Only display if not too big */ #define DIFF_STRIP_EOLCR 0x00001000 /* Strip trailing CR */ #define DIFF_SLOW_SBS 0x00002000 /* Better but slower side-by-side */ #define DIFF_WEBPAGE 0x00004000 /* Complete webpage */ #define DIFF_BROWSER 0x00008000 /* The --browser option */ #define DIFF_JSON 0x00010000 /* JSON output */ #define DIFF_DEBUG 0x00020000 /* Debugging diff output */ #define DIFF_RAW 0x00040000 /* Raw triples - for debugging */ #define DIFF_TCL 0x00080000 /* For the --tk option */ #define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */ #define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */ #define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */ /* ** Per file information that may influence output. */ #define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */ #define DIFF_FILE_DELETED 0x80000000 /* Deleted or rename source */ #define DIFF_FILE_MASK 0xc0000000 /* Used for clearing file flags */ /* ** 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**15 = 32768 bytes) */ #define LENGTH_MASK_SZ 15 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1) /* ** An instance of this object describes the formatting and processing ** details desired of a "diff" operation. ** ** Conceptually, this object is as an encoding of the command-line options ** for the "fossil diff" command. That is not a precise description, though, ** because not all diff operations are started from the command-line. But ** the idea is sound. ** ** Information encoded by this object includes but is not limited to: ** ** * The desired output format (unified vs. side-by-side, ** TCL, JSON, HTML vs. plain-text). ** ** * Number of lines of context surrounding each difference block ** ** * Width of output columns for text side-by-side diffop */ struct DiffConfig { u64 diffFlags; /* Diff flags */ int nContext; /* Number of lines of context */ int wColumn; /* Column width in -y mode */ u32 nFile; /* Number of files diffed so far */ const char *zDiffCmd; /* External diff command to use instead of builtin */ const char *zBinGlob; /* GLOB pattern for binary files */ ReCompiled *pRe; /* Show only changes matching this pattern */ const char *zLeftHash; /* HASH-id of the left file */ }; #endif /* INTERFACE */ /* ** Initialize memory for a DiffConfig based on just a diffFlags integer. */ DiffConfig *diff_config_init(DiffConfig *pCfg, u64 diffFlags){ memset(pCfg, 0, sizeof(*pCfg)); pCfg->diffFlags = diffFlags; return pCfg; } /* ** 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 */ u64 h; /* Hash of the line */ unsigned short indent; /* Index of first non-space */ unsigned short n; /* number of bytes */ unsigned short nw; /* number of bytes without leading/trailing space */ 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) /* ** Number of diff chunks generated */ static int nChunk = 0; /* ** A context for running a raw diff. ** ** The aEdit[] array describes the raw diff. Each triple of integers in ** aEdit[] means: ** ** (1) COPY: Number of lines aFrom and aTo have in common ** (2) DELETE: Number of lines found only in aFrom ** (3) INSERT: Number of lines found only in aTo ** ** The triples repeat until all lines of both aFrom and aTo are accounted ** for. */ typedef struct DContext DContext; struct DContext { 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 (*xDiffer)(const DLine *,const DLine *); /* comparison function */ }; /* Fast isspace for use by diff */ static const char diffIsSpace[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 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, 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 diff_isspace(X) (diffIsSpace[(unsigned char)(X)]) /* ** Count the number of lines in the input string. Include the last line ** in the count even if it lacks the \n terminator. If an empty string ** is specified, the number of lines is zero. For the purposes of this ** function, a string is considered empty if it contains no characters ** -OR- it contains only NUL characters. ** ** Returns true if the input seems to be plain text input, else false. ** If it returns false, pnLine is not modified, else it is set to the ** number of lines in z. */ int count_lines( const char *z, int n, int *pnLine ){ int nLine; const char *zNL, *z2; for(nLine=0, z2=z; (zNL = strchr(z2,'\n'))!=0; z2=zNL+1, nLine++){} if( z2[0]!='\0' ){ nLine++; do{ z2++; }while( z2[0]!='\0' ); } if( n!=(int)(z2-z) ) return 0; if( pnLine ) *pnLine = nLine; return 1; } /* ** 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, k, nn, s, x; u64 h, h2; DLine *a; const char *zNL; if( count_lines(z, n, &nLine)==0 ){ return 0; } assert( nLine>0 || z[0]=='\0' ); a = fossil_malloc( sizeof(a[0])*nLine ); memset(a, 0, sizeof(a[0])*nLine); if( nLine==0 ){ *pnLine = 0; return a; } i = 0; do{ zNL = strchr(z,'\n'); if( zNL==0 ) zNL = z+n; nn = (int)(zNL - z); if( nn>LENGTH_MASK ){ fossil_free(a); return 0; } a[i].z = z; k = nn; if( diffFlags & DIFF_STRIP_EOLCR ){ if( k>0 && z[k-1]=='\r' ){ k--; } } a[i].n = k; if( diffFlags & DIFF_IGNORE_EOLWS ){ while( k>0 && diff_isspace(z[k-1]) ){ k--; } } if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){ int numws = 0; for(s=0; s<k && z[s]<=' '; s++){} a[i].indent = s; a[i].nw = k - s; for(h=0, x=s; x<k; x++){ char c = z[x]; if( diff_isspace(c) ){ ++numws; }else{ h = (h^c)*9000000000000000041LL; } } k -= numws; }else{ int k2 = k & ~0x7; u64 m; for(h=x=s=0; x<k2; x += 8){ memcpy(&m, z+x, 8); h = (h^m)*9000000000000000041LL; } m = 0; memcpy(&m, z+x, k-k2); h ^= m; } a[i].h = h = ((h%281474976710597LL)<<LENGTH_MASK_SZ) | (k-s); h2 = h % nLine; a[i].iNext = a[h2].iHash; a[h2].iHash = i+1; z += nn+1; n -= nn+1; i++; }while( zNL[0]!='\0' && zNL[1]!='\0' ); assert( i==nLine ); /* Return results */ *pnLine = nLine; return a; } /* ** Return zero if two DLine elements are identical. */ static int compare_dline(const DLine *pA, const DLine *pB){ if( pA->h!=pB->h ) return 1; return memcmp(pA->z,pB->z, pA->h&LENGTH_MASK); } /* ** Return zero 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 compare_dline_ignore_allws(const DLine *pA, const DLine *pB){ if( pA->h==pB->h ){ int a, b; if( memcmp(pA->z, pB->z, pA->h&LENGTH_MASK)==0 ) return 0; a = pA->indent; b = pB->indent; while( a<pA->n || b<pB->n ){ if( a<pA->n && b<pB->n && pA->z[a++] != pB->z[b++] ) return 1; while( a<pA->n && diff_isspace(pA->z[a])) ++a; while( b<pB->n && diff_isspace(pB->z[b])) ++b; } return pA->n-a != pB->n-b; } return 1; } /* ** Return true if the regular expression *pRe matches any of the ** N dlines */ static int re_dline_match( ReCompiled *pRe, /* The regular expression to be matched */ const DLine *aDLine, /* First of N DLines to compare against */ int N /* Number of DLines to check */ ){ while( N-- ){ if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){ return 1; } aDLine++; } return 0; } /* ** Append a single line of context-diff output to pOut. */ static void appendDiffLine( Blob *pOut, /* Where to write the line of output */ char cPrefix, /* One of " ", "+", or "-" */ const DLine *pLine /* The line to be output */ ){ blob_append_char(pOut, cPrefix); blob_append(pOut, pLine->z, pLine->n); blob_append_char(pOut, '\n'); } /* ** 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 ** to leave that number field blank. */ static void appendDiffLineno(Blob *pOut, int lnA, int lnB){ if( lnA>0 ){ blob_appendf(pOut, "%6d ", lnA); }else{ blob_append(pOut, " ", 7); } if( lnB>0 ){ blob_appendf(pOut, "%6d ", lnB); }else{ blob_append(pOut, " ", 8); } } /* ** Output a patch-style text diff. */ static void contextDiff( DContext *p, /* The difference */ Blob *pOut, /* Output a context diff to here */ DiffConfig *pCfg /* Configuration options */ ){ const DLine *A; /* Left side of the diff */ const DLine *B; /* Right side of the diff */ int a = 0; /* Index of next line in A[] */ int b = 0; /* Index of next line in B[] */ int *R; /* Array of COPY/DELETE/INSERT triples */ int r; /* Index into R[] */ int nr; /* Number of COPY/DELETE/INSERT triples to process */ int mxr; /* Maximum value for r */ int na, nb; /* Number of lines shown from A and B */ int i, j; /* Loop counters */ int m; /* Number of lines to output */ int skip; /* Number of lines to skip */ int nContext; /* Number of lines of context */ int showLn; /* Show line numbers */ int showDivider = 0; /* True to show the divider between diff blocks */ nContext = diff_context_lines(pCfg); showLn = (pCfg->diffFlags & DIFF_LINENO)!=0; A = p->aFrom; B = p->aTo; R = p->aEdit; mxr = p->nEdit; while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } for(r=0; r<mxr; r += 3*nr){ /* Figure out how many triples to show in a single block */ for(nr=1; 3*nr<mxr && R[r+nr*3]>0 && R[r+nr*3]<(int)nContext*2; nr++){} /* printf("r=%d nr=%d\n", r, nr); */ /* For the current block comprising nr triples, figure out ** how many lines of A and B are to be displayed */ if( R[r]>nContext ){ na = nb = nContext; |
︙ | ︙ | |||
234 235 236 237 238 239 240 | na += R[r+nr*3]; nb += R[r+nr*3]; } for(i=1; i<nr; i++){ na += R[r+i*3]; nb += R[r+i*3]; } | > > > > > > > > > > > > > > > > > > > | > > > > > | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < | < | | | > > > | > > > > > < < < < < < < < < | < < < < < | | | < < < < | > | | | < < > | > > > | | > | > < < | < < < > | 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 | na += R[r+nr*3]; nb += R[r+nr*3]; } for(i=1; i<nr; i++){ na += R[r+i*3]; nb += R[r+i*3]; } /* Show the header for this block, or if we are doing a modified ** context diff that contains line numbers, show the separator from ** the previous block. */ nChunk++; if( showLn ){ if( !showDivider ){ /* Do not show a top divider */ showDivider = 1; }else{ blob_appendf(pOut, "%.80c\n", '.'); } }else{ /* * If the patch changes an empty file or results in an empty file, * the block header must use 0,0 as position indicator and not 1,0. * Otherwise, patch would be confused and may reject the diff. */ blob_appendf(pOut,"@@ -%d,%d +%d,%d @@", na ? a+skip+1 : a+skip, na, nb ? b+skip+1 : b+skip, nb); blob_append(pOut, "\n", 1); } /* Show the initial common area */ a += skip; b += skip; m = R[r] - skip; for(j=0; j<m; j++){ if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1); appendDiffLine(pOut, ' ', &A[a+j]); } a += m; b += m; /* Show the differences */ for(i=0; i<nr; i++){ m = R[r+i*3+1]; for(j=0; j<m; j++){ if( showLn ) appendDiffLineno(pOut, a+j+1, 0); appendDiffLine(pOut, '-', &A[a+j]); } a += m; m = R[r+i*3+2]; for(j=0; j<m; j++){ if( showLn ) appendDiffLineno(pOut, 0, b+j+1); appendDiffLine(pOut, '+', &B[b+j]); } 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); appendDiffLine(pOut, ' ', &A[a+j]); } 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); appendDiffLine(pOut, ' ', &A[a+j]); } } } #define MX_CSN 8 /* Maximum number of change spans across a change region */ /* ** A description of zero or more (up to MX_CSN) areas of difference ** between two lines of text. */ typedef struct LineChange LineChange; struct LineChange { int n; /* Number of change spans */ struct Span { int iStart1; /* Byte offset to start of a change on the left */ int iLen1; /* Length of the left change in bytes */ int iStart2; /* Byte offset to start of a change on the right */ int iLen2; /* Length of the change on the right in bytes */ int isMin; /* True if this change is known to have no useful subdivs */ } a[MX_CSN]; /* Array of change spans, sorted order */ }; /* ** The two text segments zLeft and zRight are known to be different on ** both ends, but they might have a common segment in the middle. If ** they do not have a common segment, return 0. If they do have a large ** common segment, return 1 and before doing so set: ** ** aLCS[0] = start of the common segment in zLeft ** aLCS[1] = end of the common segment in zLeft ** aLCS[2] = start of the common segment in zLeft ** aLCS[3] = end of the common segment in zLeft ** ** This computation is for display purposes only and does not have to be ** optimal or exact. */ static int textLCS( const char *zLeft, int nA, /* String on the left */ const char *zRight, int nB, /* String on the right */ int *aLCS /* Identify bounds of LCS here */ ){ const unsigned char *zA = (const unsigned char*)zLeft; /* left string */ const unsigned char *zB = (const unsigned char*)zRight; /* right string */ int i, j, k; /* Loop counters */ int lenBest = 0; /* Match length to beat */ for(i=0; i<nA-lenBest; i++){ unsigned char cA = zA[i]; if( (cA&0xc0)==0x80 ) continue; for(j=0; j<nB-lenBest; j++ ){ if( zB[j]==cA ){ for(k=1; j+k<nB && i+k<nA && zB[j+k]==zA[i+k]; k++){} while( (zB[j+k]&0xc0)==0x80 ){ k--; } if( k>lenBest ){ lenBest = k; aLCS[0] = i; aLCS[1] = i+k; aLCS[2] = j; aLCS[3] = j+k; } } } } return lenBest>0; } /* ** Find the smallest spans that are different between two text strings that ** are known to be different on both ends. */ static int textLineChanges( const char *zLeft, int nA, /* String on the left */ const char *zRight, int nB, /* String on the right */ LineChange *p /* Write results here */ ){ p->n = 1; p->a[0].iStart1 = 0; p->a[0].iLen1 = nA; p->a[0].iStart2 = 0; p->a[0].iLen2 = nB; p->a[0].isMin = 0; while( p->n<MX_CSN-1 ){ int mxi = -1; int mxLen = -1; int x, i; int aLCS[4]; struct Span *a, *b; memset(aLCS, 0, sizeof(aLCS)); for(i=0; i<p->n; i++){ if( p->a[i].isMin ) continue; x = p->a[i].iLen1; if( p->a[i].iLen2<x ) x = p->a[i].iLen2; if( x>mxLen ){ mxLen = x; mxi = i; } } if( mxLen<6 ) break; x = textLCS(zLeft + p->a[mxi].iStart1, p->a[mxi].iLen1, zRight + p->a[mxi].iStart2, p->a[mxi].iLen2, aLCS); if( x==0 ){ p->a[mxi].isMin = 1; continue; } a = p->a+mxi; b = a+1; if( mxi<p->n-1 ){ memmove(b+1, b, sizeof(*b)*(p->n-mxi-1)); } p->n++; b->iStart1 = a->iStart1 + aLCS[1]; b->iLen1 = a->iLen1 - aLCS[1]; a->iLen1 = aLCS[0]; b->iStart2 = a->iStart2 + aLCS[3]; b->iLen2 = a->iLen2 - aLCS[3]; a->iLen2 = aLCS[2]; b->isMin = 0; } return p->n; } /* ** Return true if the string starts with n spaces */ static int allSpaces(const char *z, int n){ int i; for(i=0; i<n && diff_isspace(z[i]); i++){} return i==n; } /* ** Try to improve the human-readability of the LineChange p. ** ** (1) If the first change span shows a change of indentation, try to ** move that indentation change to the left margin. ** ** (2) Try to shift changes so that they begin or end with a space. */ static void improveReadability( const char *zA, /* Left line of the change */ const char *zB, /* Right line of the change */ LineChange *p /* The LineChange to be adjusted */ ){ int j, n, len; if( p->n<1 ) return; /* (1) Attempt to move indentation changes to the left margin */ if( p->a[0].iLen1==0 && (len = p->a[0].iLen2)>0 && (j = p->a[0].iStart2)>0 && zB[0]==zB[j] && allSpaces(zB, j) ){ for(n=1; n<len && n<j && zB[j]==zB[j+n]; n++){} if( n<len ){ memmove(&p->a[1], &p->a[0], sizeof(p->a[0])*p->n); p->n++; p->a[0] = p->a[1]; p->a[1].iStart2 += n; p->a[1].iLen2 -= n; p->a[0].iLen2 = n; } p->a[0].iStart1 = 0; p->a[0].iStart2 = 0; }else if( p->a[0].iLen2==0 && (len = p->a[0].iLen1)>0 && (j = p->a[0].iStart1)>0 && zA[0]==zA[j] && allSpaces(zA, j) ){ for(n=1; n<len && n<j && zA[j]==zA[j+n]; n++){} if( n<len ){ memmove(&p->a[1], &p->a[0], sizeof(p->a[0])*p->n); p->n++; p->a[0] = p->a[1]; p->a[1].iStart1 += n; p->a[1].iLen1 -= n; p->a[0].iLen1 = n; } p->a[0].iStart1 = 0; p->a[0].iStart2 = 0; } /* (2) Try to shift changes so that they begin or end with a ** space. (TBD) */ } /* ** Given two lines of text, pFrom and pTo, compute a set of changes ** between those two lines, for enhanced display purposes. ** ** The result is written into the LineChange object given by the ** third parameter. */ static void oneLineChange( const DLine *pLeft, /* Left line of the change */ const DLine *pRight, /* Right line of the change */ LineChange *p /* OUTPUT: Write the results here */ ){ int nLeft; /* Length of left line in bytes */ int nRight; /* Length of right line in bytes */ int nShort; /* Shortest of left and right */ int nPrefix; /* Length of common prefix */ int nSuffix; /* Length of common suffix */ int nCommon; /* Total byte length of suffix and prefix */ const char *zLeft; /* Text of the left line */ const char *zRight; /* Text of the right line */ int nLeftDiff; /* nLeft - nPrefix - nSuffix */ int nRightDiff; /* nRight - nPrefix - nSuffix */ 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++; } if( nPrefix<nShort ){ while( nPrefix>0 && (zLeft[nPrefix]&0xc0)==0x80 ) nPrefix--; } nSuffix = 0; if( nPrefix<nShort ){ while( nSuffix<nShort && zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){ nSuffix++; } if( nSuffix<nShort ){ while( nSuffix>0 && (zLeft[nLeft-nSuffix]&0xc0)==0x80 ) nSuffix--; } if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0; } nCommon = nPrefix + nSuffix; /* If the prefix and suffix overlap, that means that we are dealing with ** a pure insertion or deletion of text that can have multiple alignments. ** Try to find an alignment to begins and ends on whitespace, or on ** punctuation, rather than in the middle of a name or number. */ if( nCommon > nShort ){ int iBest = -1; int iBestVal = -1; int i; int nLong = nLeft<nRight ? nRight : nLeft; int nGap = nLong - nShort; for(i=nShort-nSuffix; i<=nPrefix; i++){ int iVal = 0; char c = zLeft[i]; if( diff_isspace(c) ){ iVal += 5; }else if( !fossil_isalnum(c) ){ iVal += 2; } c = zLeft[i+nGap-1]; if( diff_isspace(c) ){ iVal += 5; }else if( !fossil_isalnum(c) ){ iVal += 2; } if( iVal>iBestVal ){ iBestVal = iVal; iBest = i; } } nPrefix = iBest; nSuffix = nShort - nPrefix; nCommon = nPrefix + nSuffix; } /* A single chunk of text inserted */ if( nCommon==nLeft ){ p->n = 1; p->a[0].iStart1 = nPrefix; p->a[0].iLen1 = 0; p->a[0].iStart2 = nPrefix; p->a[0].iLen2 = nRight - nCommon; improveReadability(zLeft, zRight, p); return; } /* A single chunk of text deleted */ if( nCommon==nRight ){ p->n = 1; p->a[0].iStart1 = nPrefix; p->a[0].iLen1 = nLeft - nCommon; p->a[0].iStart2 = nPrefix; p->a[0].iLen2 = 0; improveReadability(zLeft, zRight, p); return; } /* At this point we know that there is a chunk of text that has ** changed between the left and the right. Check to see if there ** is a large unchanged section in the middle of that changed block. */ nLeftDiff = nLeft - nCommon; nRightDiff = nRight - nCommon; if( nLeftDiff >= 4 && nRightDiff >= 4 && textLineChanges(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, p)>1 ){ int i; for(i=0; i<p->n; i++){ p->a[i].iStart1 += nPrefix; p->a[i].iStart2 += nPrefix; } improveReadability(zLeft, zRight, p); return; } /* If all else fails, show a single big change between left and right */ p->n = 1; p->a[0].iStart1 = nPrefix; p->a[0].iLen1 = nLeft - nCommon; p->a[0].iStart2 = nPrefix; p->a[0].iLen2 = nRight - nCommon; improveReadability(zLeft, zRight, p); } /* ** COMMAND: test-line-diff ** Usage: %fossil% test-line-diff STRING1 STRING2 ** ** Show the differences between the two strings. Used for testing ** the oneLineChange() routine in the diff logic. */ void test_line_diff(void){ DLine a, b; LineChange chng; int i, j, x; if( g.argc!=4 ) usage("STRING1 STRING2"); a.z = g.argv[2]; a.n = (int)strlen(a.z); b.z = g.argv[3]; b.n = (int)strlen(b.z); oneLineChange(&a, &b, &chng); fossil_print("left: [%s]\n", a.z); for(i=x=0; i<chng.n; i++){ int ofst = chng.a[i].iStart1; int len = chng.a[i].iLen1; if( len ){ if( x==0 ){ fossil_print("%*s", 8, ""); } while( ofst > x ){ if( (a.z[x]&0xc0)!=0x80 ) fossil_print(" "); x++; } for(j=0; j<len; j++, x++){ if( (a.z[x]&0xc0)!=0x80 ) fossil_print("%d",i); } } } if( x ) fossil_print("\n"); fossil_print("right: [%s]\n", b.z); for(i=x=0; i<chng.n; i++){ int ofst = chng.a[i].iStart2; int len = chng.a[i].iLen2; if( len ){ if( x==0 ){ fossil_print("%*s", 8, ""); } while( ofst > x ){ if( (b.z[x]&0xc0)!=0x80 ) fossil_print(" "); x++; } for(j=0; j<len; j++, x++){ if( (b.z[x]&0xc0)!=0x80 ) fossil_print("%d",i); } } } if( x ) fossil_print("\n"); } /* ** Minimum of two values */ static int minInt(int a, int b){ return a<b ? a : b; } /* ** This is an abstract superclass for an object that accepts difference ** lines and formats them for display. Subclasses of this object format ** the diff output in different ways. ** ** To subclass, create an instance of the DiffBuilder object and fill ** in appropriate method implementations. */ typedef struct DiffBuilder DiffBuilder; struct DiffBuilder { void (*xSkip)(DiffBuilder*, unsigned int, int); void (*xCommon)(DiffBuilder*,const DLine*); void (*xInsert)(DiffBuilder*,const DLine*); void (*xDelete)(DiffBuilder*,const DLine*); void (*xReplace)(DiffBuilder*,const DLine*,const DLine*); void (*xEdit)(DiffBuilder*,const DLine*,const DLine*); void (*xEnd)(DiffBuilder*); unsigned int lnLeft; /* Lines seen on the left (delete) side */ unsigned int lnRight; /* Lines seen on the right (insert) side */ unsigned int nPending; /* Number of pending lines */ int eState; /* State of the output */ int width; /* Display width */ Blob *pOut; /* Output blob */ Blob aCol[5]; /* Holding blobs */ DiffConfig *pCfg; /* Configuration information */ }; /************************* DiffBuilderDebug ********************************/ /* This version of DiffBuilder is used for debugging the diff and diff ** diff formatter logic. It is accessed using the (undocumented) --debug ** option to the diff command. The output is human-readable text that ** describes the various method calls that are invoked agains the DiffBuilder ** object. */ static void dfdebugSkip(DiffBuilder *p, unsigned int n, int isFinal){ blob_appendf(p->pOut, "SKIP %d (%d..%d left and %d..%d right)%s\n", n, p->lnLeft+1, p->lnLeft+n, p->lnRight+1, p->lnRight+n, isFinal ? " FINAL" : ""); p->lnLeft += n; p->lnRight += n; } static void dfdebugCommon(DiffBuilder *p, const DLine *pLine){ p->lnLeft++; p->lnRight++; blob_appendf(p->pOut, "COMMON %8u %8u %.*s\n", p->lnLeft, p->lnRight, (int)pLine->n, pLine->z); } static void dfdebugInsert(DiffBuilder *p, const DLine *pLine){ p->lnRight++; blob_appendf(p->pOut, "INSERT %8d %.*s\n", p->lnRight, (int)pLine->n, pLine->z); } static void dfdebugDelete(DiffBuilder *p, const DLine *pLine){ p->lnLeft++; blob_appendf(p->pOut, "DELETE %8u %.*s\n", p->lnLeft, (int)pLine->n, pLine->z); } static void dfdebugReplace(DiffBuilder *p, const DLine *pX, const DLine *pY){ p->lnLeft++; p->lnRight++; blob_appendf(p->pOut, "REPLACE %8u %.*s\n", p->lnLeft, (int)pX->n, pX->z); blob_appendf(p->pOut, " %8u %.*s\n", p->lnRight, (int)pY->n, pY->z); } static void dfdebugEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){ int i, j; int x; LineChange chng; p->lnLeft++; p->lnRight++; blob_appendf(p->pOut, "EDIT %8u %.*s\n", p->lnLeft, (int)pX->n, pX->z); oneLineChange(pX, pY, &chng); for(i=x=0; i<chng.n; i++){ int ofst = chng.a[i].iStart1; int len = chng.a[i].iLen1; if( len ){ char c = '0' + i; if( x==0 ){ blob_appendf(p->pOut, "%*s", 26, ""); } while( ofst > x ){ if( (pX->z[x]&0xc0)!=0x80 ) blob_append_char(p->pOut, ' '); x++; } for(j=0; j<len; j++, x++){ if( (pX->z[x]&0xc0)!=0x80 ) blob_append_char(p->pOut, c); } } } if( x ) blob_append_char(p->pOut, '\n'); blob_appendf(p->pOut, " %8u %.*s\n", p->lnRight, (int)pY->n, pY->z); for(i=x=0; i<chng.n; i++){ int ofst = chng.a[i].iStart2; int len = chng.a[i].iLen2; if( len ){ char c = '0' + i; if( x==0 ){ blob_appendf(p->pOut, "%*s", 26, ""); } while( ofst > x ){ if( (pY->z[x]&0xc0)!=0x80 ) blob_append_char(p->pOut, ' '); x++; } for(j=0; j<len; j++, x++){ if( (pY->z[x]&0xc0)!=0x80 ) blob_append_char(p->pOut, c); } } } if( x ) blob_append_char(p->pOut, '\n'); } static void dfdebugEnd(DiffBuilder *p){ blob_appendf(p->pOut, "END with %u lines left and %u lines right\n", p->lnLeft, p->lnRight); fossil_free(p); } static DiffBuilder *dfdebugNew(Blob *pOut){ DiffBuilder *p = fossil_malloc(sizeof(*p)); p->xSkip = dfdebugSkip; p->xCommon = dfdebugCommon; p->xInsert = dfdebugInsert; p->xDelete = dfdebugDelete; p->xReplace = dfdebugReplace; p->xEdit = dfdebugEdit; p->xEnd = dfdebugEnd; p->lnLeft = p->lnRight = 0; p->pOut = pOut; return p; } /************************* DiffBuilderTcl ********************************/ /* ** This formatter outputs a description of the diff formatted as TCL, for ** use by the --tk option to "diff". See also the "diff.tcl" file. The ** output can be viewed directly using the --tcl option. ** ** There is one line per method call: ** ** SKIP n -- Skip "n" lines of input ** COM string -- "string" is an unchanged context line ** INS string -- "string" is in the right file only ** DEL string -- "string" is in the left file only ** EDIT string .... -- Complex edit between left and right ** ** The EDIT verb will be followed by 3*N or 3*N+1 strings. The triples ** each show: ** ** 1. Common text ** 2. Text from the left side ** 3. Text on the right that replaces (2) from the left ** ** For inserted text (2) will be an empty string. For deleted text, (3) ** will be an empty string. (1) might be empty for the first triple if ** the line begins with an edit. After all triples, there might be one ** additional string which is a common suffix. */ static void dftclSkip(DiffBuilder *p, unsigned int n, int isFinal){ blob_appendf(p->pOut, "SKIP %u\n", n); } static void dftclCommon(DiffBuilder *p, const DLine *pLine){ blob_appendf(p->pOut, "COM "); blob_append_tcl_literal(p->pOut, pLine->z, pLine->n); blob_append_char(p->pOut, '\n'); } static void dftclInsert(DiffBuilder *p, const DLine *pLine){ blob_append(p->pOut, "INS ", -1); blob_append_tcl_literal(p->pOut, pLine->z, pLine->n); blob_append_char(p->pOut, '\n'); } static void dftclDelete(DiffBuilder *p, const DLine *pLine){ blob_append(p->pOut, "DEL ", -1); blob_append_tcl_literal(p->pOut, pLine->z, pLine->n); blob_append_char(p->pOut, '\n'); } static void dftclReplace(DiffBuilder *p, const DLine *pX, const DLine *pY){ blob_append(p->pOut, "EDIT \"\" ", -1); blob_append_tcl_literal(p->pOut, pX->z, pX->n); blob_append_char(p->pOut, ' '); blob_append_tcl_literal(p->pOut, pY->z, pY->n); blob_append_char(p->pOut, '\n'); } static void dftclEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){ int i, x; LineChange chng; blob_append(p->pOut, "EDIT", 4); oneLineChange(pX, pY, &chng); for(i=x=0; i<chng.n; i++){ blob_append_char(p->pOut, ' '); blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x); x = chng.a[i].iStart1; blob_append_char(p->pOut, ' '); blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iLen1); x += chng.a[i].iLen1; blob_append_char(p->pOut, ' '); blob_append_tcl_literal(p->pOut, pY->z + chng.a[i].iStart2, chng.a[i].iLen2); } if( x<pX->n ){ blob_append_char(p->pOut, ' '); blob_append_tcl_literal(p->pOut, pX->z + x, pX->n - x); } blob_append_char(p->pOut, '\n'); } static void dftclEnd(DiffBuilder *p){ fossil_free(p); } static DiffBuilder *dftclNew(Blob *pOut){ DiffBuilder *p = fossil_malloc(sizeof(*p)); p->xSkip = dftclSkip; p->xCommon = dftclCommon; p->xInsert = dftclInsert; p->xDelete = dftclDelete; p->xReplace = dftclReplace; p->xEdit = dftclEdit; p->xEnd = dftclEnd; p->pOut = pOut; return p; } /************************* DiffBuilderJson ********************************/ /* ** This formatter generates a JSON array that describes the difference. ** ** The Json array consists of integer opcodes with each opcode followed ** by zero or more arguments: ** ** Syntax Mnemonic Description ** ----------- -------- -------------------------- ** 0 END This is the end of the diff ** 1 INTEGER SKIP Skip N lines from both files ** 2 STRING COMMON The line shown by STRING is in both files ** 3 STRING INSERT The line STRING is in only the right file ** 4 STRING DELETE The STRING line is in only the left file ** 5 SUBARRAY EDIT One line is different on left and right. ** ** The SUBARRAY is an array of 3*N+1 strings with N>=0. The triples ** represent common-text, left-text, and right-text. The last string ** in SUBARRAY is the common-suffix. Any string can be empty if it does ** not apply. */ static void dfjsonSkip(DiffBuilder *p, unsigned int n, int isFinal){ blob_appendf(p->pOut, "1,%u,\n", n); } static void dfjsonCommon(DiffBuilder *p, const DLine *pLine){ blob_append(p->pOut, "2,",2); blob_append_json_literal(p->pOut, pLine->z, (int)pLine->n); blob_append(p->pOut, ",\n",2); } static void dfjsonInsert(DiffBuilder *p, const DLine *pLine){ blob_append(p->pOut, "3,",2); blob_append_json_literal(p->pOut, pLine->z, (int)pLine->n); blob_append(p->pOut, ",\n",2); } static void dfjsonDelete(DiffBuilder *p, const DLine *pLine){ blob_append(p->pOut, "4,",2); blob_append_json_literal(p->pOut, pLine->z, (int)pLine->n); blob_append(p->pOut, ",\n",2); } static void dfjsonReplace(DiffBuilder *p, const DLine *pX, const DLine *pY){ blob_append(p->pOut, "5,[\"\",",-1); blob_append_json_literal(p->pOut, pX->z, (int)pX->n); blob_append(p->pOut, ",",1); blob_append_json_literal(p->pOut, pY->z, (int)pY->n); blob_append(p->pOut, ",\"\"],\n",-1); } static void dfjsonEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){ int i, x; LineChange chng; blob_append(p->pOut, "5,[", 3); oneLineChange(pX, pY, &chng); for(i=x=0; i<chng.n; i++){ if(i>0){ blob_append_char(p->pOut, ','); } blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x); x = chng.a[i].iStart1; blob_append_char(p->pOut, ','); blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iLen1); x += chng.a[i].iLen1; blob_append_char(p->pOut, ','); blob_append_json_literal(p->pOut, pY->z + chng.a[i].iStart2, chng.a[i].iLen2); } blob_append_char(p->pOut, ','); blob_append_json_literal(p->pOut, pX->z + x, pX->n - x); blob_append(p->pOut, "],\n",3); } static void dfjsonEnd(DiffBuilder *p){ blob_append(p->pOut, "0]}", 3); fossil_free(p); } static DiffBuilder *dfjsonNew(Blob *pOut){ DiffBuilder *p = fossil_malloc(sizeof(*p)); p->xSkip = dfjsonSkip; p->xCommon = dfjsonCommon; p->xInsert = dfjsonInsert; p->xDelete = dfjsonDelete; p->xReplace = dfjsonReplace; p->xEdit = dfjsonEdit; p->xEnd = dfjsonEnd; p->lnLeft = p->lnRight = 0; p->pOut = pOut; blob_append_char(pOut, '['); return p; } /************************* DiffBuilderUnified********************************/ /* This formatter generates a unified diff for HTML. ** ** The result is a <table> with four columns. The four columns hold: ** ** 1. The line numbers for the first file. ** 2. The line numbers for the second file. ** 3. The "diff mark": "+" or "-" or just a space ** 4. Text of the line ** ** Inserted lines are marked with <ins> and deleted lines are marked ** with <del>. The whole line is marked this way, not just the part that ** changed. The part that change has an additional nested <ins> or <del>. ** The CSS needs to be set up such that a single <ins> or <del> gives a ** light background and a nested <ins> or <del> gives a darker background. ** Additional attributes (like bold font) might also be added to nested ** <ins> and <del> since those are the characters that have actually ** changed. ** ** Accumulator strategy: ** ** * Delete line numbers are output directly to p->pOut ** * Insert line numbers accumulate in p->aCol[0]. ** * Separator marks accumulate in p->aCol[1]. ** * Change text accumulates in p->aCol[2]. ** * Pending insert line numbers go into p->aCol[3]. ** * Pending insert text goes into p->aCol[4]. ** ** eState is 1 if text has an open <del> */ static void dfunifiedFinishDelete(DiffBuilder *p){ if( p->eState==0 ) return; blob_append(p->pOut, "</del>", 6); blob_append(&p->aCol[2], "</del>", 6); p->eState = 0; } static void dfunifiedFinishInsert(DiffBuilder *p){ unsigned int i; if( p->nPending==0 ) return; dfunifiedFinishDelete(p); /* Blank lines for delete line numbers for each inserted line */ for(i=0; i<p->nPending; i++) blob_append_char(p->pOut, '\n'); /* Insert line numbers */ blob_append(&p->aCol[0], "<ins>", 5); blob_append_xfer(&p->aCol[0], &p->aCol[3]); blob_append(&p->aCol[0], "</ins>", 6); /* "+" marks for the separator on inserted lines */ for(i=0; i<p->nPending; i++) blob_append(&p->aCol[1], "+\n", 2); /* Text of the inserted lines */ blob_append(&p->aCol[2], "<ins>", 5); blob_append_xfer(&p->aCol[2], &p->aCol[4]); blob_append(&p->aCol[2], "</ins>", 6); p->nPending = 0; } static void dfunifiedFinishRow(DiffBuilder *p){ dfunifiedFinishDelete(p); dfunifiedFinishInsert(p); if( blob_size(&p->aCol[0])==0 ) return; blob_append(p->pOut, "</pre></td><td class=\"diffln difflnr\"><pre>\n", -1); blob_append_xfer(p->pOut, &p->aCol[0]); blob_append(p->pOut, "</pre></td><td class=\"diffsep\"><pre>\n", -1); blob_append_xfer(p->pOut, &p->aCol[1]); blob_append(p->pOut, "</pre></td><td class=\"difftxt difftxtu\"><pre>\n",-1); blob_append_xfer(p->pOut, &p->aCol[2]); blob_append(p->pOut, "</pre></td></tr>\n", -1); } static void dfunifiedStartRow(DiffBuilder *p){ if( blob_size(&p->aCol[0])>0 ) return; blob_appendf(p->pOut,"<tr id=\"chunk%d\">" "<td class=\"diffln difflnl\"><pre>\n", ++nChunk); } static void dfunifiedSkip(DiffBuilder *p, unsigned int n, int isFinal){ dfunifiedFinishRow(p); if( p->pCfg && p->pCfg->zLeftHash ){ blob_appendf(p->pOut, "<tr class=\"diffskip\" data-startln=\"%d\" data-endln=\"%d\"" " id=\"skip%xh%xi%x\">\n", p->lnLeft+1, p->lnLeft+n, nChunk, p->lnLeft, n); }else{ blob_append(p->pOut, "<tr>", 4); } blob_append(p->pOut, "<td class=\"diffln difflne\">" "︙</td><td></td><td></td></tr>\n", -1); p->lnLeft += n; p->lnRight += n; } static void dfunifiedCommon(DiffBuilder *p, const DLine *pLine){ dfunifiedStartRow(p); dfunifiedFinishDelete(p); dfunifiedFinishInsert(p); p->lnLeft++; p->lnRight++; blob_appendf(p->pOut,"%d\n", p->lnLeft); blob_appendf(&p->aCol[0],"%d\n",p->lnRight); blob_append_char(&p->aCol[1], '\n'); htmlize_to_blob(&p->aCol[2], pLine->z, (int)pLine->n); blob_append_char(&p->aCol[2], '\n'); } static void dfunifiedInsert(DiffBuilder *p, const DLine *pLine){ dfunifiedStartRow(p); p->lnRight++; blob_appendf(&p->aCol[3],"%d\n", p->lnRight); blob_append(&p->aCol[4], "<ins>", 5); htmlize_to_blob(&p->aCol[4], pLine->z, (int)pLine->n); blob_append(&p->aCol[4], "</ins>\n", 7); p->nPending++; } static void dfunifiedDelete(DiffBuilder *p, const DLine *pLine){ dfunifiedStartRow(p); dfunifiedFinishInsert(p); if( p->eState==0 ){ dfunifiedFinishInsert(p); blob_append(p->pOut, "<del>", 5); blob_append(&p->aCol[2], "<del>", 5); p->eState = 1; } p->lnLeft++; blob_appendf(p->pOut,"%d\n", p->lnLeft); blob_append_char(&p->aCol[0],'\n'); blob_append(&p->aCol[1],"-\n",2); blob_append(&p->aCol[2], "<del>", 5); htmlize_to_blob(&p->aCol[2], pLine->z, (int)pLine->n); blob_append(&p->aCol[2], "</del>\n", 7); } static void dfunifiedReplace(DiffBuilder *p, const DLine *pX, const DLine *pY){ dfunifiedStartRow(p); if( p->eState==0 ){ dfunifiedFinishInsert(p); blob_append(p->pOut, "<del>", 5); blob_append(&p->aCol[2], "<del>", 5); p->eState = 1; } p->lnLeft++; p->lnRight++; blob_appendf(p->pOut,"%d\n", p->lnLeft); blob_append_char(&p->aCol[0], '\n'); blob_append(&p->aCol[1], "-\n", 2); htmlize_to_blob(&p->aCol[2], pX->z, pX->n); blob_append_char(&p->aCol[2], '\n'); blob_appendf(&p->aCol[3],"%d\n", p->lnRight); htmlize_to_blob(&p->aCol[4], pY->z, pY->n); blob_append_char(&p->aCol[4], '\n'); p->nPending++; } static void dfunifiedEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){ int i; int x; LineChange chng; oneLineChange(pX, pY, &chng); dfunifiedStartRow(p); if( p->eState==0 ){ dfunifiedFinishInsert(p); blob_append(p->pOut, "<del>", 5); blob_append(&p->aCol[2], "<del>", 5); p->eState = 1; } p->lnLeft++; p->lnRight++; blob_appendf(p->pOut,"%d\n", p->lnLeft); blob_append_char(&p->aCol[0], '\n'); blob_append(&p->aCol[1], "-\n", 2); for(i=x=0; i<chng.n; i++){ int ofst = chng.a[i].iStart1; int len = chng.a[i].iLen1; if( len ){ htmlize_to_blob(&p->aCol[2], pX->z+x, ofst - x); x = ofst; blob_append(&p->aCol[2], "<del>", 5); htmlize_to_blob(&p->aCol[2], pX->z+x, len); x += len; blob_append(&p->aCol[2], "</del>", 6); } } htmlize_to_blob(&p->aCol[2], pX->z+x, pX->n - x); blob_append_char(&p->aCol[2], '\n'); blob_appendf(&p->aCol[3],"%d\n", p->lnRight); for(i=x=0; i<chng.n; i++){ int ofst = chng.a[i].iStart2; int len = chng.a[i].iLen2; if( len ){ htmlize_to_blob(&p->aCol[4], pY->z+x, ofst - x); x = ofst; blob_append(&p->aCol[4], "<ins>", 5); htmlize_to_blob(&p->aCol[4], pY->z+x, len); x += len; blob_append(&p->aCol[4], "</ins>", 6); } } htmlize_to_blob(&p->aCol[4], pY->z+x, pY->n - x); blob_append_char(&p->aCol[4], '\n'); p->nPending++; } static void dfunifiedEnd(DiffBuilder *p){ dfunifiedFinishRow(p); blob_append(p->pOut, "</table>\n",-1); fossil_free(p); } static DiffBuilder *dfunifiedNew(Blob *pOut, DiffConfig *pCfg){ DiffBuilder *p = fossil_malloc(sizeof(*p)); p->xSkip = dfunifiedSkip; p->xCommon = dfunifiedCommon; p->xInsert = dfunifiedInsert; p->xDelete = dfunifiedDelete; p->xReplace = dfunifiedReplace; p->xEdit = dfunifiedEdit; p->xEnd = dfunifiedEnd; p->lnLeft = p->lnRight = 0; p->eState = 0; p->nPending = 0; p->pOut = pOut; if( pCfg->zLeftHash ){ blob_appendf(pOut, "<table class=\"diff udiff\" data-lefthash=\"%s\">\n", pCfg->zLeftHash); }else{ blob_append(pOut, "<table class=\"diff udiff\">\n", -1); } blob_init(&p->aCol[0], 0, 0); blob_init(&p->aCol[1], 0, 0); blob_init(&p->aCol[2], 0, 0); blob_init(&p->aCol[3], 0, 0); blob_init(&p->aCol[4], 0, 0); p->pCfg = pCfg; return p; } /************************* DiffBuilderSplit ******************************/ /* This formatter creates a side-by-side diff in HTML. The output is a ** <table> with 5 columns: ** ** 1. Line numbers for the first file. ** 2. Text for the first file. ** 3. The difference mark. "<", ">", "|" or blank ** 4. Line numbers for the second file. ** 5. Text for the second file. ** ** The <ins> and <del> strategy is the same as for unified diff above. ** In fact, the same CSS can be used for both. ** ** Accumulator strategy: ** ** * Left line numbers are output directly to p->pOut ** * Left text accumulates in p->aCol[0]. ** * Edit marks accumulates in p->aCol[1]. ** * Right line numbers accumulate in p->aCol[2]. ** * Right text accumulates in p->aCol[3]. ** ** eState: ** 0 In common block ** 1 Have <del> on the left ** 2 Have <ins> on the right ** 3 Have <del> on left and <ins> on the right */ static void dfsplitChangeState(DiffBuilder *p, int newState){ if( p->eState == newState ) return; if( (p->eState&1)==0 && (newState & 1)!=0 ){ blob_append(p->pOut, "<del>", 5); blob_append(&p->aCol[0], "<del>", 5); p->eState |= 1; }else if( (p->eState&1)!=0 && (newState & 1)==0 ){ blob_append(p->pOut, "</del>", 6); blob_append(&p->aCol[0], "</del>", 6); p->eState &= ~1; } if( (p->eState&2)==0 && (newState & 2)!=0 ){ blob_append(&p->aCol[2], "<ins>", 5); blob_append(&p->aCol[3], "<ins>", 5); p->eState |= 2; }else if( (p->eState&2)!=0 && (newState & 2)==0 ){ blob_append(&p->aCol[2], "</ins>", 6); blob_append(&p->aCol[3], "</ins>", 6); p->eState &= ~2; } } static void dfsplitFinishRow(DiffBuilder *p){ if( blob_size(&p->aCol[0])==0 ) return; dfsplitChangeState(p, 0); blob_append(p->pOut, "</pre></td><td class=\"difftxt difftxtl\"><pre>\n",-1); blob_append_xfer(p->pOut, &p->aCol[0]); blob_append(p->pOut, "</pre></td><td class=\"diffsep\"><pre>\n", -1); blob_append_xfer(p->pOut, &p->aCol[1]); blob_append(p->pOut, "</pre></td><td class=\"diffln difflnr\"><pre>\n",-1); blob_append_xfer(p->pOut, &p->aCol[2]); blob_append(p->pOut, "</pre></td><td class=\"difftxt difftxtr\"><pre>\n",-1); blob_append_xfer(p->pOut, &p->aCol[3]); blob_append(p->pOut, "</pre></td></tr>\n", -1); } static void dfsplitStartRow(DiffBuilder *p){ if( blob_size(&p->aCol[0])>0 ) return; blob_appendf(p->pOut,"<tr id=\"chunk%d\">" "<td class=\"diffln difflnl\"><pre>\n", ++nChunk); p->eState = 0; } static void dfsplitSkip(DiffBuilder *p, unsigned int n, int isFinal){ dfsplitFinishRow(p); if( p->pCfg && p->pCfg->zLeftHash ){ blob_appendf(p->pOut, "<tr class=\"diffskip\" data-startln=\"%d\" data-endln=\"%d\"" " id=\"skip%xh%xi%x\">\n", p->lnLeft+1, p->lnLeft+n, nChunk,p->lnLeft,n); }else{ blob_append(p->pOut, "<tr>", 4); } blob_append(p->pOut, "<td class=\"diffln difflnl difflne\">︙</td>" "<td></td><td></td>" "<td class=\"diffln difflnr difflne\">︙</td>" "<td/td></tr>\n", -1); p->lnLeft += n; p->lnRight += n; } static void dfsplitCommon(DiffBuilder *p, const DLine *pLine){ dfsplitStartRow(p); dfsplitChangeState(p, 0); p->lnLeft++; p->lnRight++; blob_appendf(p->pOut,"%d\n", p->lnLeft); htmlize_to_blob(&p->aCol[0], pLine->z, (int)pLine->n); blob_append_char(&p->aCol[0], '\n'); blob_append_char(&p->aCol[1], '\n'); blob_appendf(&p->aCol[2],"%d\n",p->lnRight); htmlize_to_blob(&p->aCol[3], pLine->z, (int)pLine->n); blob_append_char(&p->aCol[3], '\n'); } static void dfsplitInsert(DiffBuilder *p, const DLine *pLine){ dfsplitStartRow(p); dfsplitChangeState(p, 2); p->lnRight++; blob_append_char(p->pOut, '\n'); blob_append_char(&p->aCol[0], '\n'); blob_append(&p->aCol[1], ">\n", -1); blob_appendf(&p->aCol[2],"%d\n", p->lnRight); blob_append(&p->aCol[3], "<ins>", 5); htmlize_to_blob(&p->aCol[3], pLine->z, (int)pLine->n); blob_append(&p->aCol[3], "</ins>\n", 7); } static void dfsplitDelete(DiffBuilder *p, const DLine *pLine){ dfsplitStartRow(p); dfsplitChangeState(p, 1); p->lnLeft++; blob_appendf(p->pOut,"%d\n", p->lnLeft); blob_append(&p->aCol[0], "<del>", 5); htmlize_to_blob(&p->aCol[0], pLine->z, (int)pLine->n); blob_append(&p->aCol[0], "</del>\n", 7); blob_append(&p->aCol[1], "<\n", -1); blob_append_char(&p->aCol[2],'\n'); blob_append_char(&p->aCol[3],'\n'); } static void dfsplitReplace(DiffBuilder *p, const DLine *pX, const DLine *pY){ dfsplitStartRow(p); dfsplitChangeState(p, 3); p->lnLeft++; p->lnRight++; blob_appendf(p->pOut,"%d\n", p->lnLeft); htmlize_to_blob(&p->aCol[0], pX->z, pX->n); blob_append_char(&p->aCol[0], '\n'); blob_append(&p->aCol[1], "|\n", 2); blob_appendf(&p->aCol[2],"%d\n", p->lnRight); htmlize_to_blob(&p->aCol[3], pY->z, pY->n); blob_append_char(&p->aCol[3], '\n'); } static void dfsplitEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){ int i; int x; LineChange chng; oneLineChange(pX, pY, &chng); dfsplitStartRow(p); dfsplitChangeState(p, 3); p->lnLeft++; p->lnRight++; blob_appendf(p->pOut,"%d\n", p->lnLeft); for(i=x=0; i<chng.n; i++){ int ofst = chng.a[i].iStart1; int len = chng.a[i].iLen1; if( len ){ htmlize_to_blob(&p->aCol[0], pX->z+x, ofst - x); x = ofst; if( chng.a[i].iLen2 ){ blob_append(&p->aCol[0], "<del class='edit'>", -1); }else{ blob_append(&p->aCol[0], "<del>", 5); } htmlize_to_blob(&p->aCol[0], pX->z+x, len); x += len; blob_append(&p->aCol[0], "</del>", 6); } } htmlize_to_blob(&p->aCol[0], pX->z+x, pX->n - x); blob_append_char(&p->aCol[0], '\n'); blob_append(&p->aCol[1], "|\n", 2); blob_appendf(&p->aCol[2],"%d\n", p->lnRight); for(i=x=0; i<chng.n; i++){ int ofst = chng.a[i].iStart2; int len = chng.a[i].iLen2; if( len ){ htmlize_to_blob(&p->aCol[3], pY->z+x, ofst - x); x = ofst; if( chng.a[i].iLen1 ){ blob_append(&p->aCol[3], "<ins class='edit'>", -1); }else{ blob_append(&p->aCol[3], "<ins>", 5); } htmlize_to_blob(&p->aCol[3], pY->z+x, len); x += len; blob_append(&p->aCol[3], "</ins>", 6); } } htmlize_to_blob(&p->aCol[3], pY->z+x, pY->n - x); blob_append_char(&p->aCol[3], '\n'); } static void dfsplitEnd(DiffBuilder *p){ dfsplitFinishRow(p); blob_append(p->pOut, "</table>\n",-1); fossil_free(p); } static DiffBuilder *dfsplitNew(Blob *pOut, DiffConfig *pCfg){ DiffBuilder *p = fossil_malloc(sizeof(*p)); p->xSkip = dfsplitSkip; p->xCommon = dfsplitCommon; p->xInsert = dfsplitInsert; p->xDelete = dfsplitDelete; p->xReplace = dfsplitReplace; p->xEdit = dfsplitEdit; p->xEnd = dfsplitEnd; p->lnLeft = p->lnRight = 0; p->eState = 0; p->pOut = pOut; if( pCfg->zLeftHash ){ blob_appendf(pOut, "<table class=\"diff splitdiff\" data-lefthash=\"%s\">\n", pCfg->zLeftHash); }else{ blob_append(pOut, "<table class=\"diff splitdiff\">\n", -1); } blob_init(&p->aCol[0], 0, 0); blob_init(&p->aCol[1], 0, 0); blob_init(&p->aCol[2], 0, 0); blob_init(&p->aCol[3], 0, 0); blob_init(&p->aCol[4], 0, 0); p->pCfg = pCfg; return p; } /************************* DiffBuilderSbs ******************************/ /* This formatter creates a side-by-side diff in text. */ static void dfsbsSkip(DiffBuilder *p, unsigned int n, int isFinal){ if( (p->lnLeft || p->lnRight) && !isFinal ){ blob_appendf(p->pOut, "%.*c\n", p->width*2 + 16, '.'); } p->lnLeft += n; p->lnRight += n; } /* ** Append at least iMin characters (not bytes) and at most iMax characters ** from pX onto the into of p. ** ** This comment contains multibyte unicode characters (ü, Æ, ð) in order ** to test the ability of the diff code to handle such characters. */ static void sbs_append_chars(Blob *p, int iMin, int iMax, const DLine *pX){ int i; const char *z = pX->z; for(i=0; i<iMax && i<pX->n; i++){ char c = z[i]; blob_append_char(p, c); if( (c&0xc0)==0x80 ){ iMin++; iMax++; } } while( i<iMin ){ blob_append_char(p, ' '); i++; } } static void dfsbsCommon(DiffBuilder *p, const DLine *pLine){ p->lnLeft++; p->lnRight++; blob_appendf(p->pOut,"%6u ", p->lnLeft); sbs_append_chars(p->pOut, p->width, p->width, pLine); blob_appendf(p->pOut," %6u ", p->lnRight); sbs_append_chars(p->pOut, 0, p->width, pLine); blob_append_char(p->pOut, '\n'); } static void dfsbsInsert(DiffBuilder *p, const DLine *pLine){ p->lnRight++; blob_appendf(p->pOut,"%6s %*s > %6u ", "", p->width, "", p->lnRight); sbs_append_chars(p->pOut, 0, p->width, pLine); blob_append_char(p->pOut, '\n'); } static void dfsbsDelete(DiffBuilder *p, const DLine *pLine){ p->lnLeft++; blob_appendf(p->pOut,"%6u ", p->lnLeft); sbs_append_chars(p->pOut, p->width, p->width, pLine); blob_append(p->pOut," <\n", 3); } static void dfsbsEdit(DiffBuilder *p, const DLine *pX, const DLine *pY){ p->lnLeft++; p->lnRight++; blob_appendf(p->pOut,"%6u ", p->lnLeft); sbs_append_chars(p->pOut, p->width, p->width, pX); blob_appendf(p->pOut, " | %6u ", p->lnRight); sbs_append_chars(p->pOut, 0, p->width, pY); blob_append_char(p->pOut, '\n'); } static void dfsbsEnd(DiffBuilder *p){ fossil_free(p); } static DiffBuilder *dfsbsNew(Blob *pOut, DiffConfig *pCfg){ DiffBuilder *p = fossil_malloc(sizeof(*p)); p->xSkip = dfsbsSkip; p->xCommon = dfsbsCommon; p->xInsert = dfsbsInsert; p->xDelete = dfsbsDelete; p->xReplace = dfsbsEdit; p->xEdit = dfsbsEdit; p->xEnd = dfsbsEnd; p->lnLeft = p->lnRight = 0; p->width = diff_width(pCfg); p->pOut = pOut; return p; } /****************************************************************************/ /* ** Return the number between 0 and 100 that is smaller the closer pA and ** pB match. Return 0 for a perfect match. Return 100 if pA and pB are ** completely different. ** ** The current algorithm is as follows: ** ** (1) Remove leading and trailing whitespace. ** (2) Truncate both strings to at most 250 characters ** (3) If the two strings have a common prefix, measure that prefix ** (4) Find the length of the longest common subsequence that is ** at least 150% longer than the common prefix. ** (5) Longer common subsequences yield lower scores. */ static int match_dline(DLine *pA, DLine *pB){ const char *zA; /* Left string */ const char *zB; /* right string */ int nA; /* Bytes in zA[] */ int nB; /* Bytes in zB[] */ int nMin; int nPrefix; int avg; /* Average length of A and B */ int i, j, k; /* Loop counters */ int best = 0; /* Longest match found so far */ 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; if( pA->nw==0 && pA->n ){ for(i=0; i<pA->n && diff_isspace(zA[i]); i++){} pA->indent = i; for(j=pA->n-1; j>i && diff_isspace(zA[j]); j--){} pA->nw = j - i + 1; } zA += pA->indent; nA = pA->nw; zB = pB->z; if( pB->nw==0 && pB->n ){ for(i=0; i<pB->n && diff_isspace(zB[i]); i++){} pB->indent = i; for(j=pB->n-1; j>i && diff_isspace(zB[j]); j--){} pB->nw = j - i + 1; } zB += pB->indent; nB = pB->nw; if( nA>250 ) nA = 250; if( nB>250 ) nB = 250; avg = (nA+nB)/2; if( avg==0 ) return 0; nMin = nA; if( nB<nMin ) nMin = nB; if( nMin==0 ) return 68; for(nPrefix=0; nPrefix<nMin && zA[nPrefix]==zB[nPrefix]; nPrefix++){} best = 0; if( nPrefix>5 && nPrefix>nMin/2 ){ best = nPrefix*3/2; if( best>=avg - 2 ) best = avg - 2; } if( nA==nB && memcmp(zA, zB, nA)==0 ) return 0; memset(aFirst, 0xff, sizeof(aFirst)); zA--; zB--; /* Make both zA[] and zB[] 1-indexed */ for(i=nB; i>0; i--){ c = (unsigned char)zB[i]; aNext[i] = aFirst[c]; aFirst[c] = i; } for(i=1; i<=nA-best; i++){ c = (unsigned char)zA[i]; for(j=aFirst[c]; j<nB-best && memcmp(&zA[i],&zB[j],best)==0; j = aNext[j]){ int limit = minInt(nA-i, nB-j); for(k=best; k<=limit && zA[k+i]==zB[k+j]; k++){} if( k>best ) best = k; } } score = 5 + ((best>=avg) ? 0 : (avg - best)*95/avg); #if 0 fprintf(stderr, "A: [%.*s]\nB: [%.*s]\nbest=%d avg=%d score=%d\n", nA, zA+1, nB, zB+1, best, avg, score); #endif /* Return the result */ return score; } /* ** COMMAND: test-line-match ** Usage: %fossil test-line-match STRING1 STRING2 ** ** Return a score from 0 to 100 that is how similar STRING1 is to ** STRING2. Smaller numbers mean more similar. 0 is an exact match. ** ** This command is used to test to match_dline() function in the ** internal Fossil diff logic. */ void test_dline_match(void){ DLine a, b; int x; if( g.argc!=4 ) usage("STRING1 STRING2"); a.z = g.argv[2]; a.n = (int)strlen(a.z); b.z = g.argv[3]; b.n = (int)strlen(b.z); x = match_dline(&a, &b); fossil_print("%d\n", x); } /* Forward declarations for recursion */ static unsigned char *diffBlockAlignment( DLine *aLeft, int nLeft, /* Text on the left */ DLine *aRight, int nRight, /* Text on the right */ DiffConfig *pCfg, /* Configuration options */ int *pNResult /* OUTPUT: Bytes of result */ ); static void longestCommonSequence( DContext *p, /* Two files being compared */ int iS1, int iE1, /* Range of lines in p->aFrom[] */ int iS2, int iE2, /* Range of lines in p->aTo[] */ int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ int *piSY, int *piEY /* Write p->aTo[] common segment here */ ); /* ** Make a copy of a list of nLine DLine objects from one array to ** another. Hash the new array to ignore whitespace. */ static void diffDLineXfer( DLine *aTo, const DLine *aFrom, int nLine ){ int i, j, k; u64 h, h2; for(i=0; i<nLine; i++) aTo[i].iHash = 0; for(i=0; i<nLine; i++){ const char *z = aFrom[i].z; int n = aFrom[i].n; for(j=0; j<n && diff_isspace(z[j]); j++){} aTo[i].z = &z[j]; for(k=aFrom[i].n; k>j && diff_isspace(z[k-1]); k--){} aTo[i].n = n = k-j; aTo[i].indent = 0; aTo[i].nw = 0; for(h=0; j<k; j++){ char c = z[j]; if( !diff_isspace(c) ){ h = (h^c)*9000000000000000041LL; } } aTo[i].h = h = ((h%281474976710597LL)<<LENGTH_MASK_SZ) | n; h2 = h % nLine; aTo[i].iNext = aTo[h2].iHash; aTo[h2].iHash = i+1; } } /* ** For a difficult diff-block alignment that was originally for ** the default consider-all-whitespace algorithm, try to find the ** longest common subsequence between the two blocks that involves ** only whitespace changes. */ static unsigned char *diffBlockAlignmentIgnoreSpace( DLine *aLeft, int nLeft, /* Text on the left */ DLine *aRight, int nRight, /* Text on the right */ DiffConfig *pCfg, /* Configuration options */ int *pNResult /* OUTPUT: Bytes of result */ ){ DContext dc; int iSX, iEX; /* Start and end of LCS on the left */ int iSY, iEY; /* Start and end of the LCS on the right */ unsigned char *a1, *a2; int n1, n2, nLCS; dc.aEdit = 0; dc.nEdit = 0; dc.nEditAlloc = 0; dc.nFrom = nLeft; dc.nTo = nRight; dc.xDiffer = compare_dline_ignore_allws; dc.aFrom = fossil_malloc( sizeof(DLine)*(nLeft+nRight) ); dc.aTo = &dc.aFrom[dc.nFrom]; diffDLineXfer(dc.aFrom, aLeft, nLeft); diffDLineXfer(dc.aTo, aRight, nRight); longestCommonSequence(&dc,0,nLeft,0,nRight,&iSX,&iEX,&iSY,&iEY); fossil_free(dc.aFrom); nLCS = iEX - iSX; if( nLCS<5 ) return 0; /* No good LCS was found */ if( pCfg->diffFlags & DIFF_DEBUG ){ fossil_print(" LCS size=%d\n" " [%.*s]\n" " [%.*s]\n", nLCS, aLeft[iSX].n, aLeft[iSX].z, aLeft[iEX-1].n, aLeft[iEX-1].z); } a1 = diffBlockAlignment(aLeft,iSX,aRight,iSY,pCfg,&n1); a2 = diffBlockAlignment(aLeft+iEX, nLeft-iEX, aRight+iEY, nRight-iEY, pCfg, &n2); a1 = fossil_realloc(a1, n1+nLCS+n2); memcpy(a1+n1+nLCS,a2,n2); memset(a1+n1,3,nLCS); fossil_free(a2); *pNResult = n1+n2+nLCS; return a1; } /* ** This is a helper route for diffBlockAlignment(). In this case, ** a very large block is encountered that might be too expensive to ** use the O(N*N) Wagner edit distance algorithm. So instead, this ** block implements a less-precise but faster O(N*logN) divide-and-conquer ** approach. */ static unsigned char *diffBlockAlignmentDivideAndConquer( DLine *aLeft, int nLeft, /* Text on the left */ DLine *aRight, int nRight, /* Text on the right */ DiffConfig *pCfg, /* Configuration options */ int *pNResult /* OUTPUT: Bytes of result */ ){ DLine *aSmall; /* The smaller of aLeft and aRight */ DLine *aBig; /* The larger of aLeft and aRight */ int nSmall, nBig; /* Size of aSmall and aBig. nSmall<=nBig */ int iDivSmall, iDivBig; /* Divider point for aSmall and aBig */ int iDivLeft, iDivRight; /* Divider point for aLeft and aRight */ unsigned char *a1, *a2; /* Results of the alignments on two halves */ int n1, n2; /* Number of entries in a1 and a2 */ int score, bestScore; /* Score and best score seen so far */ int i; /* Loop counter */ if( nLeft>nRight ){ aSmall = aRight; nSmall = nRight; aBig = aLeft; nBig = nLeft; }else{ aSmall = aLeft; nSmall = nLeft; aBig = aRight; nBig = nRight; } iDivBig = nBig/2; iDivSmall = nSmall/2; if( pCfg->diffFlags & DIFF_DEBUG ){ fossil_print(" Divide at [%.*s]\n", aBig[iDivBig].n, aBig[iDivBig].z); } bestScore = 10000; for(i=0; i<nSmall; i++){ score = match_dline(aBig+iDivBig, aSmall+i) + abs(i-nSmall/2)*2; if( score<bestScore ){ bestScore = score; iDivSmall = i; } } if( aSmall==aRight ){ iDivRight = iDivSmall; iDivLeft = iDivBig; }else{ iDivRight = iDivBig; iDivLeft = iDivSmall; } a1 = diffBlockAlignment(aLeft,iDivLeft,aRight,iDivRight,pCfg,&n1); a2 = diffBlockAlignment(aLeft+iDivLeft, nLeft-iDivLeft, aRight+iDivRight, nRight-iDivRight, pCfg, &n2); a1 = fossil_realloc(a1, n1+n2 ); memcpy(a1+n1,a2,n2); fossil_free(a2); *pNResult = n1+n2; return a1; } /* ** The threshold at which diffBlockAlignment transitions from the ** O(N*N) Wagner minimum-edit-distance algorithm to a less process ** O(NlogN) divide-and-conquer approach. */ #define DIFF_ALIGN_MX 1225 /* ** There is a change block in which nLeft lines of text on the left are ** converted into nRight lines of text on the right. This routine computes ** how the lines on the left line up with the lines on the right. ** ** The return value is a buffer of unsigned characters, obtained from ** fossil_malloc(). (The caller needs to free the return value using ** fossil_free().) Entries in the returned array have values as follows: ** ** 1. Delete the next line of pLeft. ** 2. Insert the next line of pRight. ** 3. The next line of pLeft changes into the next line of pRight. ** 4. Delete one line from pLeft and add one line to pRight. ** ** The length of the returned array will be at most nLeft+nRight bytes. ** If the first bytes is 4, that means we could not compute reasonable ** alignment between the two blocks. ** ** Algorithm: Wagner's minimum edit-distance algorithm, modified by ** adding a cost to each match based on how well the two rows match ** each other. Insertion and deletion costs are 50. Match costs ** are between 0 and 100 where 0 is a perfect match 100 is a complete ** mismatch. */ static unsigned char *diffBlockAlignment( DLine *aLeft, int nLeft, /* Text on the left */ DLine *aRight, int nRight, /* Text on the right */ DiffConfig *pCfg, /* Configuration options */ int *pNResult /* OUTPUT: Bytes of result */ ){ int i, j, k; /* Loop counters */ int *a; /* One row of the Wagner matrix */ int *pToFree; /* Space that needs to be freed */ unsigned char *aM; /* Wagner result matrix */ int aBuf[100]; /* Stack space for a[] if nRight not to big */ if( nLeft==0 ){ aM = fossil_malloc( nRight + 2 ); memset(aM, 2, nRight); *pNResult = nRight; return aM; } if( nRight==0 ){ aM = fossil_malloc( nLeft + 2 ); memset(aM, 1, nLeft); *pNResult = nLeft; return aM; } if( pCfg->diffFlags & DIFF_DEBUG ){ fossil_print("BlockAlignment:\n [%.*s] + %d\n [%.*s] + %d\n", aLeft[0].n, aLeft[0].z, nLeft, aRight[0].n, aRight[0].z, nRight); } /* For large alignments, try to use alternative algorithms that are ** faster than the O(N*N) Wagner edit distance. */ if( (i64)nLeft*(i64)nRight>DIFF_ALIGN_MX && (pCfg->diffFlags & DIFF_SLOW_SBS)==0 ){ if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==0 ){ unsigned char *aRes; aRes = diffBlockAlignmentIgnoreSpace( aLeft, nLeft,aRight, nRight,pCfg,pNResult); if( aRes ) return aRes; } return diffBlockAlignmentDivideAndConquer( aLeft, nLeft,aRight, nRight,pCfg,pNResult); } /* If we reach this point, we will be doing an O(N*N) Wagner minimum ** edit distance to compute the alignment. */ if( nRight < count(aBuf)-1 ){ pToFree = 0; a = aBuf; }else{ a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) ); } aM = fossil_malloc( (nLeft+1)*(nRight+1) ); /* Compute the best alignment */ for(i=0; i<=nRight; i++){ aM[i] = 2; a[i] = i*50; } aM[0] = 0; for(j=1; j<=nLeft; j++){ int p = a[0]; a[0] = p+50; aM[j*(nRight+1)] = 1; for(i=1; i<=nRight; i++){ int m = a[i-1]+50; int d = 2; if( m>a[i]+50 ){ m = a[i]+50; d = 1; } if( m>p ){ int score = match_dline(&aLeft[j-1], &aRight[i-1]); if( (score<=90 || (i<j+1 && i>j-1)) && m>p+score ){ m = p+score; d = 3 | score*4; } } p = a[i]; a[i] = m; aM[j*(nRight+1)+i] = d; } } /* Compute the lowest-cost path back through the matrix */ i = nRight; j = nLeft; k = (nRight+1)*(nLeft+1)-1; while( i+j>0 ){ unsigned char c = aM[k]; if( c>=3 ){ assert( i>0 && j>0 ); i--; j--; aM[k] = 3; }else if( c==2 ){ assert( i>0 ); i--; }else{ assert( j>0 ); j--; } k--; aM[k] = aM[j*(nRight+1)+i]; } k++; i = (nRight+1)*(nLeft+1) - k; memmove(aM, &aM[k], i); *pNResult = i; /* Return the result */ fossil_free(pToFree); return aM; } /* ** R[] is an array of six integer, two COPY/DELETE/INSERT triples for a ** pair of adjacent differences. Return true if the gap between these ** two differences is so small that they should be rendered as a single ** edit. */ static int smallGap(const int *R, int ma, int mb){ int m = R[3]; ma += R[4] + m; mb += R[5] + m; if( ma*mb>DIFF_ALIGN_MX ) return 0; return m<=2 || m<=(R[1]+R[2]+R[4]+R[5])/8; } /* ** Format a diff using a DiffBuilder object */ static void formatDiff( DContext *p, /* The computed diff */ DiffConfig *pCfg, /* Configuration options */ DiffBuilder *pBuilder /* The formatter object */ ){ DLine *A; /* Left side of the diff */ DLine *B; /* Right side of the diff */ unsigned int a = 0; /* Index of next line in A[] */ unsigned int b = 0; /* Index of next line in B[] */ const int *R; /* Array of COPY/DELETE/INSERT triples */ unsigned int r; /* Index into R[] */ unsigned int nr; /* Number of COPY/DELETE/INSERT triples to process */ unsigned int mxr; /* Maximum value for r */ unsigned int i, j; /* Loop counters */ unsigned int m, ma, mb;/* Number of lines to output */ signed int skip = 0; /* Number of lines to skip */ unsigned int nContext; /* Lines of context above and below each change */ nContext = diff_context_lines(pCfg); A = p->aFrom; B = p->aTo; R = p->aEdit; mxr = p->nEdit; while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } for(r=0; r<mxr; r += 3*nr){ /* Figure out how many triples to show in a single block */ for(nr=1; 3*nr<mxr && R[r+nr*3]>0 && R[r+nr*3]<(int)nContext*2; nr++){} /* If there is a regex, skip this block (generate no diff output) ** if the regex matches or does not match both insert and delete. ** Only display the block if one side matches but the other side does ** not. */ if( pCfg->pRe ){ int hideBlock = 1; int xa = a, xb = b; for(i=0; hideBlock && i<nr; i++){ int c1, c2; xa += R[r+i*3]; xb += R[r+i*3]; c1 = re_dline_match(pCfg->pRe, &A[xa], R[r+i*3+1]); c2 = re_dline_match(pCfg->pRe, &B[xb], R[r+i*3+2]); hideBlock = c1==c2; xa += R[r+i*3+1]; xb += R[r+i*3+2]; } if( hideBlock ){ a = xa; b = xb; continue; } } /* Figure out how many lines of A and B are to be displayed ** for this change block. */ if( R[r]>(int)nContext ){ skip = R[r] - nContext; }else{ skip = 0; } /* Show the initial common area */ a += skip; b += skip; m = R[r] - skip; if( r ) skip -= nContext; if( skip>0 ){ if( skip<(int)nContext ){ /* If the amount to skip is less that the context band, then ** go ahead and show the skip band as it is not worth eliding */ for(j=0; (int)j<skip; j++){ pBuilder->xCommon(pBuilder, &A[a+j-skip]); } }else{ pBuilder->xSkip(pBuilder, skip, 0); } } for(j=0; j<m; j++){ pBuilder->xCommon(pBuilder, &A[a+j]); } a += m; b += m; /* Show the differences */ for(i=0; i<nr; i++){ int nAlign; unsigned char *alignment; ma = R[r+i*3+1]; /* Lines on left but not on right */ mb = R[r+i*3+2]; /* Lines on right but not on left */ /* Try merging the current block with subsequent blocks, if the ** subsequent blocks are nearby and there result isn't too big. */ while( i<nr-1 && smallGap(&R[r+i*3],ma,mb) ){ i++; m = R[r+i*3]; ma += R[r+i*3+1] + m; mb += R[r+i*3+2] + m; } /* Try to find an alignment for the lines within this one block */ alignment = diffBlockAlignment(&A[a], ma, &B[b], mb, pCfg, &nAlign); for(j=0; ma+mb>0; j++){ assert( (int)j<nAlign ); switch( alignment[j] ){ case 1: { /* Delete one line from the left */ pBuilder->xDelete(pBuilder, &A[a]); ma--; a++; break; } case 2: { /* Insert one line on the right */ pBuilder->xInsert(pBuilder, &B[b]); assert( mb>0 ); mb--; b++; break; } case 3: { /* The left line is changed into the right line */ if( p->xDiffer(&A[a], &B[b])==0 ){ pBuilder->xCommon(pBuilder, &A[a]); }else{ pBuilder->xEdit(pBuilder, &A[a], &B[b]); } assert( ma>0 && mb>0 ); ma--; mb--; a++; b++; break; } case 4: { /* Delete from left then separately insert on the right */ pBuilder->xReplace(pBuilder, &A[a], &B[b]); ma--; a++; mb--; b++; break; } } } assert( nAlign==(int)j ); fossil_free(alignment); if( i<nr-1 ){ m = R[r+i*3+3]; for(j=0; j<m; j++){ pBuilder->xCommon(pBuilder, &A[a+j]); } 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<nContext; j++){ pBuilder->xCommon(pBuilder, &A[a+j]); } } if( R[r]>(int)nContext ){ pBuilder->xSkip(pBuilder, R[r] - nContext, 1); } pBuilder->xEnd(pBuilder); } /* ** Compute the optimal longest common subsequence (LCS) using an ** exhaustive search. This version of the LCS is only used for ** shorter input strings since runtime is O(N*N) where N is the ** input string length. */ static void optimalLCS( DContext *p, /* Two files being compared */ int iS1, int iE1, /* Range of lines in p->aFrom[] */ int iS2, int iE2, /* Range of lines in p->aTo[] */ int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ int *piSY, int *piEY /* Write p->aTo[] common segment here */ ){ int mxLength = 0; /* Length of longest common subsequence */ 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->xDiffer(&p->aFrom[i], &p->aTo[j]) ) continue; if( mxLength && p->xDiffer(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){ continue; } k = 1; while( i+k<iE1 && j+k<iE2 && p->xDiffer(&p->aFrom[i+k],&p->aTo[j+k])==0 ){ k++; } if( k>mxLength ){ iSXb = i; iSYb = j; mxLength = k; } } } *piSX = iSXb; *piEX = iSXb + mxLength; *piSY = iSYb; *piEY = iSYb + mxLength; } /* ** Compare two blocks of text on lines iS1 through iE1-1 of the aFrom[] ** file and lines iS2 through iE2-1 of the aTo[] file. Locate a sequence ** of lines in these two blocks that are exactly the same. Return ** the bounds of the matching sequence. ** ** If there are two or more possible answers of the same length, the ** returned sequence should be the one closest to the center of the ** input range. ** ** Ideally, the common sequence should be the longest possible common ** sequence. However, an exact computation of LCS is O(N*N) which is ** way too slow for larger files. So this routine uses an O(N) ** heuristic approximation based on hashing that usually works about ** as well. But if the O(N) algorithm doesn't get a good solution ** and N is not too large, we fall back to an exact solution by ** calling optimalLCS(). */ static void longestCommonSequence( DContext *p, /* Two files being compared */ int iS1, int iE1, /* Range of lines in p->aFrom[] */ int iS2, int iE2, /* Range of lines in p->aTo[] */ int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ int *piSY, int *piEY /* Write p->aTo[] common segment here */ ){ int i, j, k; /* Loop counters */ int n; /* Loop limit */ DLine *pA, *pB; /* Pointers to lines */ int iSX, iSY, iEX, iEY; /* Current match */ int skew = 0; /* How lopsided is the match */ int dist = 0; /* Distance of match from center */ int mid; /* Center of the chng */ int iSXb, iSYb, iEXb, iEYb; /* Best match so far */ int iSXp, iSYp, iEXp, iEYp; /* Previous match */ sqlite3_int64 bestScore; /* Best score so far */ sqlite3_int64 score; /* Score for current candidate LCS */ int span; /* combined width of the input sequences */ int cutoff = 4; /* Max hash chain entries to follow */ int nextCutoff = -1; /* Value of cutoff for next iteration */ span = (iE1 - iS1) + (iE2 - iS2); bestScore = -10000; score = 0; iSXb = iSXp = iS1; iEXb = iEXp = iS1; iSYb = iSYp = iS2; iEYb = iEYp = iS2; mid = (iE1 + iS1)/2; do{ nextCutoff = 0; 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->xDiffer(&p->aFrom[i], &p->aTo[j-1])) ){ if( limit++ > cutoff ){ j = 0; nextCutoff = cutoff*4; 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->xDiffer(pA,pB)==0; 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->xDiffer(pA,pB)==0; 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); if( score>bestScore ){ bestScore = score; iSXb = iSX; iSYb = iSY; iEXb = iEX; iEYb = iEY; }else if( iEX>iEXp ){ iSXp = iSX; iSYp = iSY; iEXp = iEX; iEYp = iEY; } } }while( iSXb==iEXb && nextCutoff && (cutoff=nextCutoff)<=64 ); if( iSXb==iEXb && (sqlite3_int64)(iE1-iS1)*(iE2-iS2)<2500 ){ /* If no common sequence is found using the hashing heuristic and ** the input is not too big, use the expensive exact solution */ optimalLCS(p, iS1, iE1, iS2, iE2, piSX, piEX, piSY, piEY); }else{ *piSX = iSXb; *piSY = iSYb; *piEX = iEXb; *piEY = iEYb; } } /* ** Expand the size of aEdit[] array to hold at least nEdit elements. */ static void expandEdit(DContext *p, int nEdit){ p->aEdit = fossil_realloc(p->aEdit, nEdit*sizeof(int)); p->nEditAlloc = nEdit; } /* ** Append a new COPY/DELETE/INSERT triple. */ static void appendTriple(DContext *p, int nCopy, int nDel, int nIns){ /* printf("APPEND %d/%d/%d\n", nCopy, nDel, nIns); */ if( p->nEdit>=3 ){ if( p->aEdit[p->nEdit-1]==0 ){ if( p->aEdit[p->nEdit-2]==0 ){ p->aEdit[p->nEdit-3] += nCopy; p->aEdit[p->nEdit-2] += nDel; p->aEdit[p->nEdit-1] += nIns; return; } if( nCopy==0 ){ p->aEdit[p->nEdit-2] += nDel; p->aEdit[p->nEdit-1] += nIns; return; } } if( nCopy==0 && nDel==0 ){ p->aEdit[p->nEdit-1] += nIns; return; } } if( p->nEdit+3>p->nEditAlloc ){ expandEdit(p, p->nEdit*2 + 15); if( p->aEdit==0 ) return; } p->aEdit[p->nEdit++] = nCopy; p->aEdit[p->nEdit++] = nDel; p->aEdit[p->nEdit++] = nIns; } /* ** A common subsequence between p->aFrom and p->aTo has been found. ** This routine tries to judge if the subsequence really is a valid ** match or rather is just an artifact of an indentation change. ** ** Return non-zero if the subsequence is valid. Return zero if the ** subsequence seems likely to be an editing artifact and should be ** ignored. ** ** This routine is a heuristic optimization intended to give more ** intuitive diff results following an indentation change it code that ** is formatted similarly to C/C++, Javascript, Go, TCL, and similar ** languages that use {...} for nesting. A correct diff is computed ** even if this routine always returns true (non-zero). But sometimes ** a more intuitive diff can result if this routine returns false. ** ** The subsequences consists of the rows iSX through iEX-1 (inclusive) ** in p->aFrom[]. The total sequences is iS1 through iE1-1 (inclusive) ** of p->aFrom[]. ** ** Example where this heuristic is useful, see the diff at ** https://www.sqlite.org/src/fdiff?v1=0e79dd15cbdb4f48&v2=33955a6fd874dd97 ** ** See also discussion at https://fossil-scm.org/forum/forumpost/9ba3284295 ** ** ALGORITHM (subject to change and refinement): ** ** 1. If the subsequence is larger than 1/7th of the original span, ** then consider it valid. --> return 1 ** ** 2. If no lines of the subsequence contains more than one ** non-whitespace character, --> return 0 ** ** 3. If any line of the subsequence contains more than one non-whitespace ** character and is unique across the entire sequence after ignoring ** leading and trailing whitespace --> return 1 ** ** 4. Otherwise, it is potentially an artifact of an indentation ** change. --> return 0 */ static int likelyNotIndentChngArtifact( DContext *p, /* The complete diff context */ int iS1, /* Start of the main segment */ int iSX, /* Start of the subsequence */ int iEX, /* First row past the end of the subsequence */ int iE1 /* First row past the end of the main segment */ ){ int i, j, n; /* Rule (1) */ if( (iEX-iSX)*7 >= (iE1-iS1) ) return 1; /* Compute DLine.indent and DLine.nw for all lines of the subsequence. ** If no lines contain more than one non-whitespace character return ** 0 because the subsequence could be due to an indentation change. ** Rule (2). */ n = 0; for(i=iSX; i<iEX; i++){ DLine *pA = &p->aFrom[i]; if( pA->nw==0 && pA->n ){ const char *zA = pA->z; const int nn = pA->n; int ii, jj; for(ii=0; ii<nn && diff_isspace(zA[ii]); ii++){} pA->indent = ii; for(jj=nn-1; jj>ii && diff_isspace(zA[jj]); jj--){} pA->nw = jj - ii + 1; } if( pA->nw>1 ) n++; } if( n==0 ) return 0; /* Compute DLine.indent and DLine.nw for the entire sequence */ for(i=iS1; i<iE1; i++){ DLine *pA; if( i==iSX ){ i = iEX; if( i>=iE1 ) break; } pA = &p->aFrom[i]; if( pA->nw==0 && pA->n ){ const char *zA = pA->z; const int nn = pA->n; int ii, jj; for(ii=0; ii<nn && diff_isspace(zA[ii]); ii++){} pA->indent = ii; for(jj=nn-1; jj>ii && diff_isspace(zA[jj]); jj--){} pA->nw = jj - ii + 1; } } /* Check to see if any subsequence line that has more than one ** non-whitespace character is unique across the entire sequence. ** Rule (3) */ for(i=iSX; i<iEX; i++){ const char *z = p->aFrom[i].z + p->aFrom[i].indent; const int nw = p->aFrom[i].nw; if( nw<=1 ) continue; for(j=iS1; j<iSX; j++){ if( p->aFrom[j].nw!=nw ) continue; if( memcmp(p->aFrom[j].z+p->aFrom[j].indent,z,nw)==0 ) break; } if( j<iSX ) continue; for(j=iEX; j<iE1; j++){ if( p->aFrom[j].nw!=nw ) continue; if( memcmp(p->aFrom[j].z+p->aFrom[j].indent,z,nw)==0 ) break; } if( j>=iE1 ) break; } return i<iEX; } /* ** Do a single step in the difference. Compute a sequence of ** copy/delete/insert steps that will convert lines iS1 through iE1-1 of ** the input into lines iS2 through iE2-1 of the output and write ** that sequence into the difference context. ** |
︙ | ︙ | |||
391 392 393 394 395 396 397 | appendTriple(p, 0, iE1-iS1, 0); return; } /* Find the longest matching segment between the two sequences */ longestCommonSequence(p, iS1, iE1, iS2, iE2, &iSX, &iEX, &iSY, &iEY); | | > > | | 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 | appendTriple(p, 0, iE1-iS1, 0); return; } /* Find the longest matching segment between the two sequences */ longestCommonSequence(p, iS1, iE1, iS2, iE2, &iSX, &iEX, &iSY, &iEY); if( iEX>iSX+5 || (iEX>iSX && likelyNotIndentChngArtifact(p,iS1,iSX,iEX,iE1) ) ){ /* A common segment has been found. ** Recursively diff either side of the matching segment */ diff_step(p, iS1, iSX, iS2, iSY); if( iEX>iSX ){ appendTriple(p, iEX - iSX, 0, 0); } diff_step(p, iEX, iE1, iEY, iE2); }else{ |
︙ | ︙ | |||
427 428 429 430 431 432 433 | */ static void diff_all(DContext *p){ int mnE, iS, iE1, iE2; /* Carve off the common header and footer */ iE1 = p->nFrom; iE2 = p->nTo; | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > | > | | > | > > > > > > > > > > > > > > | > | > | | | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | > > > | > > > > > > > > > > > > > > > | < | | | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | < > > | | > > > > > | < < > > > | > > > > > > > > > > | > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | | < | > > | | | | > > > > > > > > > > > > | > > > > > | > | < | | | < < | > > > > > | | | > > > > | | < > | | < > > > > > > > > > > > | > > > | > | < | < | < < > > > > | < | < < < > > | < < < > > > > > > | > | < < | < < < > > > > > > > > > > > | < < < > > | > > > | < > | | > > > > > | > > > > > > > | < > > > > > < < < < < < < < < < < < | > > > | | > | < | > | > > > > > > > > > | > > > > > > > > > > > > | | | | > > > > > > > > | > > > | < > < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > < < > > > > > > > > > > > > > > > > | > > > | > > > | > > > | > > > | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | | > > > | > > > | | | < > > > > > > > | > > > > > > > > | > > | | | > > | > | < < | < > > > > > > > | > > | > > > | < > > | | > > > | | > > | 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 | */ 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->xDiffer(&p->aFrom[iE1-1], &p->aTo[iE2-1])==0 ){ iE1--; iE2--; } mnE = iE1<iE2 ? iE1 : iE2; for(iS=0; iS<mnE && p->xDiffer(&p->aFrom[iS],&p->aTo[iS])==0; iS++){} /* do the difference */ if( iS>0 ){ appendTriple(p, iS, 0, 0); } diff_step(p, iS, iE1, iS, iE2); if( iE1<p->nFrom ){ appendTriple(p, p->nFrom - iE1, 0, 0); } /* Terminate the COPY/DELETE/INSERT triples with three zeros */ expandEdit(p, p->nEdit+3); if( p->aEdit ){ p->aEdit[p->nEdit++] = 0; p->aEdit[p->nEdit++] = 0; p->aEdit[p->nEdit++] = 0; } } /* ** Attempt to shift insertion or deletion blocks so that they begin and ** end on lines that are pure whitespace. In other words, try to transform ** this: ** ** int func1(int x){ ** return x*10; ** +} ** + ** +int func2(int x){ ** + return x*20; ** } ** ** int func3(int x){ ** return x/5; ** } ** ** Into one of these: ** ** int func1(int x){ int func1(int x){ ** return x*10; return x*10; ** } } ** + ** +int func2(int x){ +int func2(int x){ ** + return x*20; + return x*20; ** +} +} ** + ** int func3(int x){ int func3(int x){ ** return x/5; return x/5; ** } } */ static void diff_optimize(DContext *p){ int r; /* Index of current triple */ int lnFrom; /* Line number in p->aFrom */ int lnTo; /* Line number in p->aTo */ int cpy, del, ins; lnFrom = lnTo = 0; for(r=0; r<p->nEdit; r += 3){ cpy = p->aEdit[r]; del = p->aEdit[r+1]; ins = p->aEdit[r+2]; 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->xDiffer(pTop, pBtm) ) 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->xDiffer(pTop, pBtm) ) 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->xDiffer(pTop, pBtm) ) 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->xDiffer(pTop, pBtm) ) break; if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop)+LENGTH(pBtm) ) break; lnFrom++; lnTo++; p->aEdit[r]++; p->aEdit[r+3]--; cpy++; } lnFrom += del; lnTo += ins; } } /* ** Extract the number of lines of context from diffFlags. Supply an ** appropriate default if no context width is specified. If pCfg is ** NULL then the compile-time default is used (which gets propagated ** to JS-side state by certain pages). */ int diff_context_lines(DiffConfig *pCfg){ const int dflt = 5; if(pCfg!=0){ int n = pCfg->nContext; if( n==0 && (pCfg->diffFlags & DIFF_CONTEXT_EX)==0 ) n = dflt; return n<0 ? 0x7ffffff : n; }else{ return dflt; } } /* ** Extract the width of columns for side-by-side diff. Supply an ** appropriate default if no width is given. ** ** Calculate the default automatically, based on terminal's current width: ** term-width = 2*diff-col + diff-marker + 1 ** diff-col = lineno + lmargin + text-width + rmargin ** ** text-width = (term-width - diff-marker - 1)/2 - lineno - lmargin - rmargin */ int diff_width(DiffConfig *pCfg){ int w = pCfg->wColumn; if( w==0 ){ static struct { unsigned int lineno, lmargin, text, rmargin, marker; } sbsW = { 5, 2, 0, 0, 3 }; const unsigned int wMin = 24, wMax = 132; unsigned int tw = terminal_get_width(80); unsigned int twMin = (wMin + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1; unsigned int twMax = (wMax + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1; if( tw<twMin ){ tw = twMin; }else if( tw>twMax ){ tw = twMax; } sbsW.text = (tw - sbsW.marker - 1)/2 - sbsW.lineno - sbsW.lmargin - sbsW.rmargin; w = sbsW.text; } return w; } /* ** Append the error message to pOut. */ void diff_errmsg(Blob *pOut, const char *msg, int diffFlags){ if( diffFlags & DIFF_HTML ){ blob_appendf(pOut, "<p class=\"generalError\">%s</p>", msg); }else{ blob_append(pOut, msg, -1); } } /* ** Generate a report of the differences between files pA_Blob and pB_Blob. ** ** If pOut!=NULL then append text to pOut that will be the difference, ** formatted according to flags in diffFlags. The pOut Blob must have ** already been initialized. ** ** If pOut==NULL then no formatting occurs. Instead, this routine ** returns a pointer to an array of integers. The integers come in ** triples. The elements of each triple are: ** ** 1. The number of lines to copy ** 2. The number of lines to delete ** 3. The number of lines to insert ** ** The return vector is terminated by a triple of all zeros. The caller ** should free the returned vector using fossil_free(). ** ** This diff utility does not work on binary files. If a binary ** file is encountered, 0 is returned and pOut is written with ** text "cannot compute difference between binary files". */ int *text_diff( Blob *pA_Blob, /* FROM file */ Blob *pB_Blob, /* TO file */ Blob *pOut, /* Write diff here if not NULL */ DiffConfig *pCfg /* Configuration options */ ){ int ignoreWs; /* Ignore whitespace */ DContext c; if( pCfg->diffFlags & DIFF_INVERT ){ Blob *pTemp = pA_Blob; pA_Blob = pB_Blob; pB_Blob = pTemp; } ignoreWs = (pCfg->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( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){ c.xDiffer = compare_dline_ignore_allws; }else{ c.xDiffer = compare_dline; } c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), &c.nFrom, pCfg->diffFlags); c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), &c.nTo, pCfg->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, pCfg->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, pCfg->diffFlags); return 0; } if( (pCfg->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, pCfg->diffFlags); return 0; } } if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){ diff_optimize(&c); } if( pOut ){ if( pCfg->diffFlags & DIFF_NUMSTAT ){ int nDel = 0, nIns = 0, i; for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){ nDel += c.aEdit[i+1]; nIns += c.aEdit[i+2]; } g.diffCnt[1] += nIns; g.diffCnt[2] += nDel; if( nIns+nDel ){ g.diffCnt[0]++; blob_appendf(pOut, "%10d %10d", nIns, nDel); } }else if( pCfg->diffFlags & DIFF_RAW ){ const int *R = c.aEdit; unsigned int r; for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ blob_appendf(pOut, " copy %6d delete %6d insert %6d\n", R[r], R[r+1], R[r+2]); } }else if( pCfg->diffFlags & DIFF_JSON ){ DiffBuilder *pBuilder = dfjsonNew(pOut); formatDiff(&c, pCfg, pBuilder); blob_append_char(pOut, '\n'); }else if( pCfg->diffFlags & DIFF_TCL ){ DiffBuilder *pBuilder = dftclNew(pOut); formatDiff(&c, pCfg, pBuilder); }else if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){ DiffBuilder *pBuilder; if( pCfg->diffFlags & DIFF_HTML ){ pBuilder = dfsplitNew(pOut, pCfg); }else{ pBuilder = dfsbsNew(pOut, pCfg); } formatDiff(&c, pCfg, pBuilder); }else if( pCfg->diffFlags & DIFF_DEBUG ){ DiffBuilder *pBuilder = dfdebugNew(pOut); formatDiff(&c, pCfg, pBuilder); }else if( pCfg->diffFlags & DIFF_HTML ){ DiffBuilder *pBuilder = dfunifiedNew(pOut, pCfg); formatDiff(&c, pCfg, pBuilder); }else{ contextDiff(&c, pOut, pCfg); } fossil_free(c.aFrom); fossil_free(c.aTo); fossil_free(c.aEdit); return 0; }else{ /* If a context diff is not requested, then return the ** array of COPY/DELETE/INSERT triples. */ free(c.aFrom); free(c.aTo); return c.aEdit; } } /* ** Initialize the DiffConfig object using command-line options. ** ** 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. nContext ** --html Format for HTML DIFF_HTML ** --invert Invert the diff DIFF_INVERT ** -n|--linenum Show line numbers DIFF_LINENO ** --noopt Disable optimization DIFF_NOOPT ** --numstat Show change counts DIFF_NUMSTAT ** --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. wColumn ** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE ** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS */ void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){ u64 diffFlags = 0; const char *z; int f; memset(pCfg, 0, sizeof(*pCfg)); 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( !bUnifiedTextOnly ){ if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE; if( find_option("yy",0,0)!=0 ){ diffFlags |= DIFF_SIDEBYSIDE | DIFF_SLOW_SBS; } if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML; if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE; if( find_option("webpage",0,0)!=0 ){ diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO; } if( find_option("browser","b",0)!=0 ){ diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER; } if( find_option("by",0,0)!=0 ){ diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER |DIFF_SIDEBYSIDE; } if( find_option("json",0,0)!=0 ){ diffFlags |= DIFF_JSON; } if( find_option("tcl",0,0)!=0 ){ diffFlags |= DIFF_TCL; } /* Undocumented and unsupported flags used for development ** debugging and analysis: */ if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG; if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW; } if( (z = find_option("context","c",1))!=0 ){ char *zEnd; f = (int)strtol(z, &zEnd, 10); if( zEnd[0]==0 && errno!=ERANGE ){ pCfg->nContext = f; diffFlags |= DIFF_CONTEXT_EX; } } if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){ pCfg->wColumn = f; } if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO; if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT; if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT; if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS; if( find_option("dark",0,0)!=0 ) diffFlags |= DIFF_DARKMODE; if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT; if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF; if( find_option("internal","i",0)==0 && (diffFlags & (DIFF_HTML|DIFF_TCL|DIFF_DEBUG|DIFF_JSON))==0 ){ pCfg->zDiffCmd = find_option("command", 0, 1); if( pCfg->zDiffCmd==0 ) pCfg->zDiffCmd = diff_command_external(isGDiff); if( pCfg->zDiffCmd ){ const char *zDiffBinary; pCfg->zBinGlob = diff_get_binary_glob(); zDiffBinary = find_option("diff-binary", 0, 1); if( zDiffBinary ){ if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY; }else if( db_get_boolean("diff-binary", 1) ){ diffFlags |= DIFF_INCBINARY; } } } if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE; /* Deprecated, but retained for script compatibility. */ else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE; pCfg->diffFlags = diffFlags; } /* ** COMMAND: test-diff ** COMMAND: xdiff ** ** Usage: %fossil xdiff [options] FILE1 FILE2 ** ** Compute an "external diff" between two files. By "external diff" we mean ** a diff between two disk files that are not necessarily under management. ** In other words, this command provides a mechanism to use Fossil's file ** difference engine on arbitrary disk files. See the "diff" command for ** computing differences between files that are under management. ** ** This command prints the differences between the two files FILE1 and FILE2. ** all of the usual diff formatting options (--tk, --by, -c N, etc.) apply. ** See the "diff" command for a full list of command-line options. ** ** This command used to be called "test-diff". The older "test-diff" spelling ** still works, for compatibility. */ void xdiff_cmd(void){ Blob a, b, out; const char *zRe; /* Regex filter for diff output */ DiffConfig DCfg; if( find_option("tk",0,0)!=0 ){ diff_tk("xdiff", 2); return; } find_option("i",0,0); find_option("v",0,0); diff_options(&DCfg, 0, 0); zRe = find_option("regexp","e",1); if( zRe ){ const char *zErr = re_compile(&DCfg.pRe, zRe, 0); if( zErr ) fossil_fatal("regex error: %s", zErr); } verify_all_options(); if( g.argc!=4 ) usage("FILE1 FILE2"); blob_zero(&out); diff_begin(&DCfg); diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out); blob_read_from_file(&a, g.argv[2], ExtFILE); blob_read_from_file(&b, g.argv[3], ExtFILE); text_diff(&a, &b, &out, &DCfg); blob_write_to_file(&out, "-"); diff_end(&DCfg, 0); re_free(DCfg.pRe); } /************************************************************************** ** The basic difference engine is above. What follows is the annotation ** engine. Both are in the same file since they share many components. */ /* ** The status of an annotation operation is recorded by an instance ** 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 bMoreToDo; /* True if the limit was reached */ int origId; /* RID for the zOrigin version */ int showId; /* RID for the version being analyzed */ 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.xDiffer = compare_dline_ignore_allws; }else{ p->c.xDiffer = compare_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. */ 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); /* Where new lines are inserted on this difference, record the ** iVers as the source of the new line. */ for(i=lnTo=0; i<p->c.nEdit; i+=3){ int nCopy = p->c.aEdit[i]; int nIns = p->c.aEdit[i+2]; lnTo += nCopy; for(j=0; j<nIns; j++, lnTo++){ if( p->aOrig[lnTo].iVers<0 ){ p->aOrig[lnTo].iVers = iVers; } } } /* Clear out the diff results */ fossil_free(p->c.aEdit); p->c.aEdit = 0; p->c.nEdit = 0; p->c.nEditAlloc = 0; /* Clear out the from file */ free(p->c.aFrom); /* Return no errors */ return 0; } /* Return the current time as milliseconds since the Julian epoch */ static sqlite3_int64 current_time_in_milliseconds(void){ static sqlite3_vfs *clockVfs = 0; sqlite3_int64 t; if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){ clockVfs->xCurrentTimeInt64(clockVfs, &t); }else{ double r; clockVfs->xCurrentTime(clockVfs, &r); t = (sqlite3_int64)(r*86400000.0); } return t; } /* ** Compute a complete annotation on a file. The file is identified by its ** filename and check-in name (NULL for current check-in). */ static void annotate_file( Annotator *p, /* The annotator */ const char *zFilename, /* The name of the file to be annotated */ const char *zRevision, /* Use the version of the file in this check-in */ const char *zLimit, /* Limit the number of versions analyzed */ const char *zOrigin, /* The origin check-in, or NULL for root-of-tree */ 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 cid; /* Selected check-in ID */ int origid = 0; /* The origin ID or zero */ int rid; /* Artifact ID of the file being annotated */ int fnid; /* Filename ID */ Stmt q; /* Query returning all ancestor versions */ int cnt = 0; /* Number of versions analyzed */ int iLimit; /* Maximum number of versions to analyze */ sqlite3_int64 mxTime; /* Halt at this time if not already complete */ memset(p, 0, sizeof(*p)); if( zLimit ){ if( strcmp(zLimit,"none")==0 ){ iLimit = 0; mxTime = 0; }else if( sqlite3_strglob("*[0-9]s", zLimit)==0 ){ iLimit = 0; mxTime = (sqlite3_int64)(current_time_in_milliseconds() + 1000.0*atof(zLimit)); }else{ iLimit = atoi(zLimit); if( iLimit<=0 ) iLimit = 30; mxTime = 0; } }else{ /* Default limit is as much as we can do in 1.000 seconds */ iLimit = 0; mxTime = current_time_in_milliseconds()+1000; } db_begin_transaction(); /* Get the artifact ID for the check-in begin analyzed */ if( zRevision ){ cid = name_to_typed_rid(zRevision, "ci"); }else{ db_must_be_within_tree(); cid = db_lget_int("checkout", 0); } origid = zOrigin ? name_to_typed_rid(zOrigin, "ci") : 0; /* Compute all direct ancestors of the check-in being analyzed into ** the "ancestor" table. */ if( origid ){ path_shortest_stored_in_ancestor_table(origid, cid); }else{ compute_direct_ancestors(cid); } /* Get filename ID */ fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); if( fnid==0 ){ fossil_fatal("no such file: %Q", zFilename); } db_prepare(&q, "SELECT DISTINCT" " (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.fid" " FROM mlink, event, ancestor" " WHERE mlink.fnid=%d" " AND ancestor.rid=mlink.mid" " AND event.objid=mlink.mid" " AND mlink.mid!=mlink.pid" " ORDER BY ancestor.generation;", fnid ); while( db_step(&q)==SQLITE_ROW ){ if( cnt>=3 ){ /* Process at least 3 rows before imposing limits */ if( (iLimit>0 && cnt>=iLimit) || (cnt>0 && mxTime>0 && current_time_in_milliseconds()>mxTime) ){ p->bMoreToDo = 1; break; } } rid = db_column_int(&q, 4); if( cnt==0 ){ if( !content_get(rid, &toAnnotate) ){ fossil_fatal("unable to retrieve content of artifact #%d", rid); } blob_to_utf8_no_bom(&toAnnotate, 0); annotation_start(p, &toAnnotate, annFlags); p->bMoreToDo = origid!=0; p->origId = origid; p->showId = cid; } 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( cnt>0 ){ content_get(rid, &step); blob_to_utf8_no_bom(&step, 0); annotation_step(p, &step, p->nVers-1, annFlags); blob_reset(&step); } p->nVers++; cnt++; } if( p->nVers==0 ){ if( zRevision ){ fossil_fatal("file %s does not exist in check-in %s", zFilename, zRevision); }else{ fossil_fatal("no history for file: %s", zFilename); } } db_finalize(&q); db_end_transaction(0); } /* ** 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 ** ** URL: /annotate?checkin=ID&filename=FILENAME ** URL: /blame?checkin=ID&filename=FILENAME ** URL: /praise?checkin=ID&filename=FILENAME ** ** Show the most recent change to each line of a text file. /annotate shows ** the date of the changes and the check-in hash (with a link to the ** check-in). /blame and /praise also show the user who made the check-in. ** ** Reverse Annotations: Normally, these web pages look at versions of ** FILENAME moving backwards in time back toward the root check-in. However, ** if the origin= query parameter is used to specify some future check-in ** (example: "origin=trunk") then these pages show changes moving towards ** that alternative origin. Thus using "origin=trunk" on an historical ** version of the file shows the first time each line in the file was changed ** or removed by any subsequent check-in. ** ** Query parameters: ** ** checkin=ID The check-in at which to start the annotation ** filename=FILENAME The filename. ** filevers=BOOLEAN Show file versions rather than check-in versions ** limit=LIMIT Limit the amount of analysis. LIMIT can be one of: ** none No limit ** Xs As much as can be computed in X seconds ** N N versions ** log=BOOLEAN Show a log of versions analyzed ** origin=ID The origin check-in. If unspecified, the root ** check-in over the entire repository is used. ** Specify "origin=trunk" or similar for a reverse ** annotation ** w=BOOLEAN Ignore whitespace */ void annotation_page(void){ int i; const char *zLimit; /* Depth limit */ u64 annFlags = DIFF_STRIP_EOLCR; int showLog; /* True to display the log */ int fileVers; /* Show file version instead of check-in versions */ int ignoreWs; /* Ignore whitespace */ const char *zFilename; /* Name of file to annotate */ const char *zRevision; /* Name of check-in from which to start annotation */ const char *zCI; /* The check-in containing zFilename */ const char *zOrigin; /* The origin of the analysis */ int szHash; /* Number of characters in %S display */ char *zLink; 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 */ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( exclude_spiders() ) return; fossil_nice_default(); zFilename = P("filename"); zRevision = PD("checkin",0); zOrigin = P("origin"); zLimit = P("limit"); showLog = PB("log"); fileVers = PB("filevers"); ignoreWs = PB("w"); if( ignoreWs ) annFlags |= DIFF_IGNORE_ALLWS; cgi_check_for_malice(); /* compute the annotation */ annotate_file(&ann, zFilename, zRevision, zLimit, zOrigin, annFlags); zCI = ann.aVers[0].zMUuid; /* generate the web page */ style_set_current_feature("annotate"); 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( zLimit ){ url_add_parameter(&url, "limit", zLimit); } url_add_parameter(&url, "w", ignoreWs ? "1" : "0"); url_add_parameter(&url, "log", showLog ? "1" : "0"); url_add_parameter(&url, "filevers", fileVers ? "1" : "0"); style_submenu_checkbox("w", "Ignore Whitespace", 0, 0); style_submenu_checkbox("log", "Log", 0, "toggle_annotation_log"); style_submenu_checkbox("filevers", "Link to Files", 0, 0); if( ann.bMoreToDo ){ style_submenu_element("All Ancestors", "%s", url_render(&url, "limit", "none", 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); } @ <div id="annotation_log" style='display:%s(showLog?"block":"none");'> if( zOrigin ){ zLink = href("%R/finfo?name=%t&from=%!S&to=%!S",zFilename,zCI,zOrigin); }else{ zLink = href("%R/finfo?name=%t&from=%!S",zFilename,zCI); } @ <h2>Versions 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> } @ </ol> @ <hr> @ </div> if( !ann.bMoreToDo ){ assert( ann.origId==0 ); /* bMoreToDo always set for a point-to-point */ @ <h2>Origin for each line in @ %z(href("%R/finfo?name=%h&from=%!S", zFilename, zCI))%h(zFilename)</a> @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2> }else if( ann.origId>0 ){ @ <h2>Lines of @ %z(href("%R/finfo?name=%h&from=%!S", zFilename, zCI))%h(zFilename)</a> @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a> @ that are changed by the sequence of edits moving toward @ check-in %z(href("%R/info/%!S",zOrigin))%S(zOrigin)</a>:</h2> }else{ @ <h2>Lines added by the %d(ann.nVers) most recent ancestors of @ %z(href("%R/finfo?name=%h&from=%!S", zFilename, zCI))%h(zFilename)</a> @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2> } @ <pre> szHash = 10; 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( iVers<0 && !ann.bMoreToDo ) iVers = ann.nVers-1; if( bBlame ){ if( iVers>=0 ){ struct AnnVers *p = ann.aVers+iVers; const char *zUuid = fileVers ? p->zFUuid : p->zMUuid; char *zLink = xhref("target='infowindow'", "%R/info/%!S", zUuid); sqlite3_snprintf(sizeof(zPrefix), zPrefix, "<span style='background-color:%s'>" "%s%.10s</a> %s</span> %13.13s:", p->zBgColor, zLink, zUuid, p->zDate, p->zUser); fossil_free(zLink); }else{ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%*s", szHash+26, ""); } }else{ if( iVers>=0 ){ struct AnnVers *p = ann.aVers+iVers; const char *zUuid = fileVers ? p->zFUuid : p->zMUuid; char *zLink = xhref("target='infowindow'", "%R/info/%!S", zUuid); sqlite3_snprintf(sizeof(zPrefix), zPrefix, "<span style='background-color:%s'>" "%s%.10s</a> %s</span> %4d:", p->zBgColor, zLink, zUuid, p->zDate, i+1); fossil_free(zLink); }else{ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%*s%4d:",szHash+12,"",i+1); } } @ %s(zPrefix) %h(z) } @ </pre> style_finish_page(); } /* ** COMMAND: annotate ** COMMAND: blame ** COMMAND: praise* ** ** Usage: %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 version currently checked out is shown by default. ** Other versions may be specified using the -r option. The "annotate" command ** shows line numbers and omits the username. The "blame" and "praise" commands ** show the user who made each check-in. ** ** Reverse Annotations: Normally, these commands look at versions of ** FILENAME moving backwards in time back toward the root check-in, and ** thus the output shows the most recent change to each line. However, ** if the -o|--origin option is used to specify some future check-in ** (example: "-o trunk") then these commands show changes moving towards ** that alternative origin. Thus using "-o trunk" on an historical version ** of the file shows the first time each line in the file was changed or ** removed by any subsequent check-in. ** ** Options: ** --filevers Show file version numbers rather than ** check-in versions ** -r|--revision VERSION The specific check-in containing the file ** -l|--log List all versions analyzed ** -n|--limit LIMIT LIMIT can be one of: ** N Up to N versions ** Xs As much as possible in X seconds ** none No limit ** -o|--origin VERSION The origin check-in. By default this is the ** root of the repository. Set to "trunk" or ** similar for a reverse annotation. ** -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){ const char *zRevision; /* Revision name, or NULL for current check-in */ Annotator ann; /* The annotation of the file */ int i; /* Loop counter */ const char *zLimit; /* The value to the -n|--limit option */ const char *zOrig; /* The value for -o|--origin */ 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. */ int szHash; /* Display size of a version hash */ Blob treename; /* Name of file to be annotated */ char *zFilename; /* Name of file to be annotated */ bBlame = g.argv[1][0]!='a'; zRevision = find_option("r","revision",1); zLimit = find_option("limit","n",1); zOrig = find_option("origin","o",1); 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"); } annFlags |= DIFF_STRIP_EOLCR; file_tree_name(g.argv[2], &treename, 0, 1); zFilename = blob_str(&treename); annotate_file(&ann, zFilename, zRevision, zLimit, zOrig, 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"); } szHash = length_of_S_display(); 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( iVers<0 && !ann.bMoreToDo ) iVers = ann.nVers-1; if( bBlame ){ if( iVers>=0 ){ p = ann.aVers + iVers; fossil_print("%S %s %13.13s: %.*s\n", fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, n, z); }else{ fossil_print("%*s %.*s\n", szHash+26, "", n, z); } }else{ if( iVers>=0 ){ p = ann.aVers + iVers; fossil_print("%S %s %5d: %.*s\n", fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, n, z); }else{ fossil_print("%*s %5d: %.*s\n", szHash+11, "", i+1, n, z); } } } } |
Added src/diff.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* Refinements to the display of unified and side-by-side diffs. ** ** In all cases, the table columns tagged with "difftxt" are expanded, ** where possible, to fill the width of the screen. ** ** For a side-by-side diff, if either column is two wide to fit on the ** display, scrollbars are added. The scrollbars are linked, so that ** both sides scroll together. Left and right arrows also scroll. */ window.addEventListener('load',function(){ var SCROLL_LEN = 25; function initDiff(diff){ var txtCols = diff.querySelectorAll('td.difftxt'); var txtPres = diff.querySelectorAll('td.difftxt pre'); var width = 0; if(txtPres.length>=2){ width = Math.max(txtPres[0].scrollWidth, txtPres[1].scrollWidth); } var i; for(i=0; i<txtCols.length; i++){ txtCols[i].style.width = width + 'px'; txtPres[i].style.maxWidth = width + 'px'; txtPres[i].style.width = width + 'px'; txtPres[i].onscroll = function(e){ for(var j=0; j<txtPres.length; j++) txtPres[j].scrollLeft = this.scrollLeft; }; } diff.tabIndex = 0; diff.onkeydown = function(e){ e = e || event; var len = {37: -SCROLL_LEN, 39: SCROLL_LEN}[e.keyCode]; if( !len ) return; txtPres[0].scrollLeft += len; return false; }; } var i, diffs = document.querySelectorAll('table.splitdiff') for(i=0; i<diffs.length; i++){ initDiff(diffs[i]); } const checkWidth = function f(){ if(undefined === f.lastWidth){ f.lastWidth = 0; } if( document.body.clientWidth===f.lastWidth ) return; f.lastWidth = document.body.clientWidth; var w = f.lastWidth*0.5 - 100; if(!f.colsL){ f.colsL = document.querySelectorAll('td.difftxtl pre'); } for(let i=0; i<f.colsL.length; i++){ f.colsL[i].style.width = w + "px"; f.colsL[i].style.maxWidth = w + "px"; } if(!f.colsR){ f.colsR = document.querySelectorAll('td.difftxtr pre'); } for(let i=0; i<f.colsR.length; i++){ f.colsR[i].style.width = w + "px"; f.colsR[i].style.maxWidth = w + "px"; } if(!f.allDiffs){ f.allDiffs = document.querySelectorAll('table.diff'); } w = f.lastWidth; for(let i=0; i<f.allDiffs.length; i++){ f.allDiffs[i].style.width = '100%'; // setting to w causes unsightly horiz. scrollbar f.allDiffs[i].style.maxWidth = w + "px"; } }; checkWidth(); window.addEventListener('resize', checkWidth); }, false); |
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 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 | # 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_light { 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 #444444 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 } array set CFG_dark { TITLE {Fossil Diff} LN_COL_BG #dddddd LN_COL_FG #444444 TXT_COL_BG #3f3f3f TXT_COL_FG #dcdccc MKR_COL_BG #444444 MKR_COL_FG #dddddd CHNG_BG #6a6afc ADD_BG #57934c RM_BG #ef6767 HR_FG #444444 HR_PAD_TOP 4 HR_PAD_BTM 8 FN_BG #5e5e5e FN_FG #ffffff FN_PAD 5 ERR_FG #ee0000 PADX 5 WIDTH 80 HEIGHT 45 LB_HEIGHT 25 } array set CFG_arr { 0 CFG_light 1 CFG_dark } array set CFG [array get $CFG_arr($darkmode)] 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 set n1 0 set n2 0 array set widths {txt 3 ln 3 mkr 1} set fromIndex [lsearch -glob $fossilcmd *-from] set toIndex [lsearch -glob $fossilcmd *-to] set branchIndex [lsearch -glob $fossilcmd *-branch] set checkinIndex [lsearch -glob $fossilcmd *-checkin] set fA {base check-in} set fB {current check-out} if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]} if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]} if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"} if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]} while {[set line [getLine $difftxt $N ii]] != -1} { switch -- [lindex $line 0] { FILE { incr nDiffs foreach wx [list [string length $n1] [string length $n2]] { if {$wx>$widths(ln)} {set widths(ln) $wx} } .lnA insert end \n fn \n - .txtA insert end "[lindex $line 1] ($fA)\n" fn \n - .mkr insert end \n fn \n - .lnB insert end \n fn \n - .txtB insert end "[lindex $line 2] ($fB)\n" fn \n - .wfiles.lb insert end [lindex $line 2] set n1 0 set n2 0 } SKIP { set n [lindex $line 1] incr n1 $n incr n2 $n .lnA insert end ...\n hrln .txtA insert end [string repeat . 30]\n hrtxt .mkr insert end \n hrln .lnB insert end ...\n hrln .txtB insert end [string repeat . 30]\n hrtxt } COM { set x [lindex $line 1] incr n1 incr n2 .lnA insert end $n1\n - .txtA insert end $x\n - .mkr insert end \n - .lnB insert end $n2\n - .txtB insert end $x\n - } INS { set x [lindex $line 1] incr n2 .lnA insert end \n - .txtA insert end \n - .mkr insert end >\n - .lnB insert end $n2\n - .txtB insert end $x add \n - } DEL { set x [lindex $line 1] incr n1 .lnA insert end $n1\n - .txtA insert end $x rm \n - .mkr insert end <\n - .lnB insert end \n - .txtB insert end \n - } EDIT { incr n1 incr n2 .lnA insert end $n1\n - .lnB insert end $n2\n - .mkr insert end |\n - set nn [llength $line] for {set i 1} {$i<$nn} {incr i 3} { set x [lindex $line $i] if {$x ne ""} { .txtA insert end $x - .txtB insert end $x - } if {$i+2<$nn} { set x1 [lindex $line [expr {$i+1}]] set x2 [lindex $line [expr {$i+2}]] if {"$x1" eq ""} { .txtB insert end $x2 add } elseif {"$x2" eq ""} { .txtA insert end $x1 rm } else { .txtA insert end $x1 chng .txtB insert end $x2 chng } } } .txtA insert end \n - .txtB insert end \n - } "" { foreach wx [list [string length $n1] [string length $n2]] { if {$wx>$widths(ln)} {set widths(ln) $wx} } } default { .lnA insert end \n - .txtA insert end $line\n err .mkr insert end \n - .lnB insert end \n - .txtB insert end $line\n err } } } 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) # Keystroke bindings for on the top-level window for navigation and # control also fire when those same keystrokes are pressed in the # Search entry box. Disable them, to prevent the diff screen from # disappearing abruptly and unexpectedly when searching for "q". # bind . <Control-q> exit bind . <Control-p> {catch searchPrev; break} bind . <Control-n> {catch searchNext; break} bind . <Escape><Escape> exit bind . <Destroy> {after 0 exit} bind . <Tab> {cycleDiffs; break} bind . <<PrevWindow>> {cycleDiffs 1; break} bind . <Control-f> {searchOnOff; break} bind . <Control-g> {catch searchNext; 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" if {[tk windowingsystem] eq "win32"} { ::ttk::style theme use winnative .bb.files configure -padding {20 1 10 2} } 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 hrln -spacing1 $CFG(HR_PAD_TOP) -spacing3 $CFG(HR_PAD_BTM) \ -foreground $CFG(HR_FG) -justify right $c tag config hrtxt -spacing1 $CFG(HR_PAD_TOP) -spacing3 $CFG(HR_PAD_BTM) \ -foreground $CFG(HR_FG) -justify center $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) .bb.invert config -text Uninvert } 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) .bb.invert config -text Invert } .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 } proc searchOnOff {} { if {[info exists ::search]} { unset ::search .txtA tag remove search 1.0 end .txtB tag remove search 1.0 end pack forget .bb.sframe focus . } else { set ::search .txtA if {![winfo exists .bb.sframe]} { frame .bb.sframe ::ttk::entry .bb.sframe.e -width 10 pack .bb.sframe.e -side left -fill y -expand 1 bind .bb.sframe.e <Return> {searchNext; break} ::ttk::button .bb.sframe.nx -text \u2193 -width 1 -command searchNext ::ttk::button .bb.sframe.pv -text \u2191 -width 1 -command searchPrev tk_optionMenu .bb.sframe.typ ::search_type \ Exact {No Case} {RegExp} {Whole Word} .bb.sframe.typ config -width 10 set ::search_type Exact pack .bb.sframe.nx .bb.sframe.pv .bb.sframe.typ -side left } pack .bb.sframe -side left after idle {focus .bb.sframe.e} } } proc searchNext {} {searchStep -forwards +1 1.0 end} proc searchPrev {} {searchStep -backwards -1 end 1.0} proc searchStep {direction incr start stop} { set pattern [.bb.sframe.e get] if {$pattern==""} return set count 0 set w $::search if {"$w"==".txtA"} {set other .txtB} {set other .txtA} if {[lsearch [$w mark names] search]<0} { $w mark set search $start } switch $::search_type { Exact {set st -exact} {No Case} {set st -nocase} {RegExp} {set st -regexp} {Whole Word} {set st -regexp; set pattern \\y$pattern\\y} } set idx [$w search -count count $direction $st -- \ $pattern "search $incr chars" $stop] if {"$idx"==""} { set idx [$other search -count count $direction $st -- $pattern $start $stop] if {"$idx"!=""} { set this $w set w $other set other $this } else { set idx [$w search -count count $direction $st -- $pattern $start $stop] } } $w tag remove search 1.0 end $w mark unset search $other tag remove search 1.0 end $other mark unset search if {"$idx"!=""} { $w mark set search $idx $w yview -pickplace $idx $w tag add search search "$idx +$count chars" $w tag config search -background {#fcc000} } set ::search $w } ::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 ::ttk::button .bb.search -text {Search} -command searchOnOff pack .bb.quit .bb.invert -side left if {$fossilcmd!=""} {pack .bb.save -side left} pack .bb.files .bb.search -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.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 | ** ** This file contains code used to implement the "diff" command */ #include "config.h" #include "diffcmd.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 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 | ** ** This file contains code used to implement the "diff" command */ #include "config.h" #include "diffcmd.h" #include <assert.h> /* includes needed to catch interrupts */ #ifdef _WIN32 # include <windows.h> #else # include <signal.h> #endif /* ** Use the right null device for the platform. */ #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)" /* ** Use the "exec-rel-paths" setting and the --exec-abs-paths and ** --exec-rel-paths command line options to determine whether ** certain external commands are executed using relative paths. */ static int determine_exec_relative_option(int force){ static int relativePaths = -1; if( force || relativePaths==-1 ){ int relPathOption = find_option("exec-rel-paths", 0, 0)!=0; int absPathOption = find_option("exec-abs-paths", 0, 0)!=0; #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) relativePaths = db_get_boolean("exec-rel-paths", 1); #else relativePaths = db_get_boolean("exec-rel-paths", 0); #endif if( relPathOption ){ relativePaths = 1; } if( absPathOption ){ relativePaths = 0; } } return relativePaths; } #if INTERFACE /* ** An array of FileDirList objects describe the files and directories listed ** on the command line of a "diff" command. Only those objects listed are ** actually diffed. */ struct FileDirList { int nUsed; /* Number of times each entry is used */ int nName; /* Length of the entry */ char *zName; /* Text of the entry */ }; #endif /* ** Return true if zFile is a file named on the azInclude[] list or is ** a file in a directory named on the azInclude[] list. ** ** if azInclude is NULL, then always include zFile. */ static int file_dir_match(FileDirList *p, const char *zFile){ if( p==0 || strcmp(p->zName,".")==0 ) return 1; if( filenames_are_case_sensitive() ){ while( p->zName ){ if( strcmp(zFile, p->zName)==0 || (strncmp(zFile, p->zName, p->nName)==0 && zFile[p->nName]=='/') ){ break; } p++; } }else{ while( p->zName ){ if( fossil_stricmp(zFile, p->zName)==0 || (fossil_strnicmp(zFile, p->zName, p->nName)==0 && zFile[p->nName]=='/') ){ break; } p++; } } if( p->zName ){ p->nUsed++; return 1; } return 0; } /* ** Print details about the compared versions - possibly the working directory ** or the undo buffer. For check-ins, show hash and commit time. ** ** This is intended primarily to go into the "header garbage" that is ignored ** by patch(1). ** ** zFrom and zTo are interpreted as symbolic version names, unless they ** start with '(', in which case they are printed directly. */ void diff_print_versions(const char *zFrom, const char *zTo, DiffConfig *pCfg){ if( (pCfg->diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF|DIFF_NUMSTAT| DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){ fossil_print("Fossil-Diff-From: %s\n", zFrom[0]=='(' ? zFrom : mprintf("%S %s", rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")), db_text("","SELECT datetime(%f)||' UTC'", symbolic_name_to_mtime(zFrom, 0)))); fossil_print("Fossil-Diff-To: %s\n", zTo[0]=='(' ? zTo : mprintf("%S %s", rid_to_uuid(symbolic_name_to_rid(zTo, "ci")), db_text("","SELECT datetime(%f)||' UTC'", symbolic_name_to_mtime(zTo, 0)))); fossil_print("%.66c\n", '-'); } } /* ** Print the "Index:" message that patches wants to see at the top of a diff. */ void diff_print_index(const char *zFile, DiffConfig *pCfg, Blob *pOut){ if( (pCfg->diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF|DIFF_NUMSTAT|DIFF_JSON| DIFF_WEBPAGE|DIFF_TCL))==0 ){ blob_appendf(pOut, "Index: %s\n%.66c\n", zFile, '='); } } /* ** Print the +++/--- filename lines or whatever filename information ** is appropriate for the output format. ** */ void diff_print_filenames( const char *zLeft, /* Name of the left file */ const char *zRight, /* Name of the right file */ DiffConfig *pCfg, /* Diff configuration */ Blob *pOut /* Write to this blob, or stdout of this is NULL */ ){ u64 diffFlags = pCfg->diffFlags; /* Standardize on /dev/null, regardless of platform. */ if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null"; if( pCfg->diffFlags & DIFF_FILE_DELETED ) zRight = "/dev/null"; if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){ /* no-op */ }else if( diffFlags & DIFF_DEBUG ){ blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight); }else if( diffFlags & DIFF_WEBPAGE ){ if( fossil_strcmp(zLeft,zRight)==0 ){ blob_appendf(pOut,"<h1>%h</h1>\n", zLeft); }else{ blob_appendf(pOut,"<h1>%h ⇆ %h</h1>\n", zLeft, zRight); } }else if( diffFlags & (DIFF_TCL|DIFF_JSON) ){ if( diffFlags & DIFF_TCL ){ blob_append(pOut, "FILE ", 5); blob_append_tcl_literal(pOut, zLeft, (int)strlen(zLeft)); blob_append_char(pOut, ' '); blob_append_tcl_literal(pOut, zRight, (int)strlen(zRight)); blob_append_char(pOut, '\n'); }else{ if( pOut ) blob_trim(pOut); blob_append(pOut, (pCfg->nFile==0 ? "[{" : ",\n{"), -1); pCfg->nFile++; blob_append(pOut, "\n \"leftname\":", -1); blob_append_json_literal(pOut, zLeft, (int)strlen(zLeft)); blob_append(pOut, ",\n \"rightname\":", -1); blob_append_json_literal(pOut, zRight, (int)strlen(zRight)); blob_append(pOut, ",\n \"diff\":\n", -1); } }else if( diffFlags & DIFF_SIDEBYSIDE ){ int w = diff_width(pCfg); 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); blob_appendf(pOut, "%.*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; blob_appendf(pOut, "%.*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{ blob_appendf(pOut, "--- %s\n+++ %s\n", zLeft, zRight); } } /* ** Default header texts for diff with --webpage */ static const char zWebpageHdr[] = @ <!DOCTYPE html> @ <html> @ <head> @ <meta charset="UTF-8"> @ <style> @ body { @ background-color: white; @ } @ h1 { @ font-size: 150%; @ } @ @ table.diff { @ width: 100%; @ border-spacing: 0; @ border: 1px solid black; @ line-height: inherit; @ font-size: inherit; @ } @ table.diff td { @ vertical-align: top; @ line-height: inherit; @ font-size: inherit; @ } @ table.diff pre { @ margin: 0 0 0 0; @ line-height: inherit; @ font-size: inherit; @ } @ td.diffln { @ width: 1px; @ text-align: right; @ padding: 0 1em 0 0; @ } @ td.difflne { @ padding-bottom: 0.4em; @ } @ td.diffsep { @ width: 1px; @ padding: 0 0.3em 0 1em; @ line-height: inherit; @ font-size: inherit; @ } @ td.diffsep pre { @ line-height: inherit; @ font-size: inherit; @ } @ td.difftxt pre { @ overflow-x: auto; @ } @ td.diffln ins { @ background-color: #a0e4b2; @ text-decoration: none; @ line-height: inherit; @ font-size: inherit; @ } @ td.diffln del { @ background-color: #ffc0c0; @ text-decoration: none; @ line-height: inherit; @ font-size: inherit; @ } @ td.difftxt del { @ background-color: #ffe8e8; @ text-decoration: none; @ line-height: inherit; @ font-size: inherit; @ } @ td.difftxt del > del { @ background-color: #ffc0c0; @ text-decoration: none; @ font-weight: bold; @ } @ td.difftxt del > del.edit { @ background-color: #c0c0ff; @ text-decoration: none; @ font-weight: bold; @ } @ td.difftxt ins { @ background-color: #dafbe1; @ text-decoration: none; @ line-height: inherit; @ font-size: inherit; @ } @ td.difftxt ins > ins { @ background-color: #a0e4b2; @ text-decoration: none; @ font-weight: bold; @ } @ td.difftxt ins > ins.edit { @ background-color: #c0c0ff; @ text-decoration: none; @ font-weight: bold; @ } @ @media (prefers-color-scheme: dark) { @ body { @ background-color: #353535; @ color: #ffffff; @ } @ td.diffln ins { @ background-color: #559855; @ color: #000000; @ } @ td.diffln del { @ background-color: #cc5555; @ color: #000000; @ } @ td.difftxt del { @ background-color: #f9cfcf; @ color: #000000; @ } @ td.difftxt del > del { @ background-color: #cc5555; @ color: #000000; @ } @ td.difftxt ins { @ background-color: #a2dbb2; @ color: #000000; @ } @ td.difftxt ins > ins { @ background-color: #559855; @ } @ } @ @ </style> @ </head> @ <body> ; static const char zWebpageHdrDark[] = @ <!DOCTYPE html> @ <html> @ <head> @ <meta charset="UTF-8"> @ <style> @ body { @ background-color: #353535; @ color: #ffffff; @ } @ h1 { @ font-size: 150%; @ } @ @ table.diff { @ width: 100%; @ border-spacing: 0; @ border: 1px solid black; @ line-height: inherit; @ font-size: inherit; @ } @ table.diff td { @ vertical-align: top; @ line-height: inherit; @ font-size: inherit; @ } @ table.diff pre { @ margin: 0 0 0 0; @ line-height: inherit; @ font-size: inherit; @ } @ td.diffln { @ width: 1px; @ text-align: right; @ padding: 0 1em 0 0; @ } @ td.difflne { @ padding-bottom: 0.4em; @ } @ td.diffsep { @ width: 1px; @ padding: 0 0.3em 0 1em; @ line-height: inherit; @ font-size: inherit; @ } @ td.diffsep pre { @ line-height: inherit; @ font-size: inherit; @ } @ td.difftxt pre { @ overflow-x: auto; @ } @ td.diffln ins { @ background-color: #559855; @ color: #000000; @ text-decoration: none; @ line-height: inherit; @ font-size: inherit; @ } @ td.diffln del { @ background-color: #cc5555; @ color: #000000; @ text-decoration: none; @ line-height: inherit; @ font-size: inherit; @ } @ td.difftxt del { @ background-color: #f9cfcf; @ color: #000000; @ text-decoration: none; @ line-height: inherit; @ font-size: inherit; @ } @ td.difftxt del > del { @ background-color: #cc5555; @ color: #000000; @ text-decoration: none; @ font-weight: bold; @ } @ td.difftxt del > del.edit { @ background-color: #c0c0ff; @ text-decoration: none; @ font-weight: bold; @ } @ td.difftxt ins { @ background-color: #a2dbb2; @ color: #000000; @ text-decoration: none; @ line-height: inherit; @ font-size: inherit; @ } @ td.difftxt ins > ins { @ background-color: #559855; @ text-decoration: none; @ font-weight: bold; @ } @ td.difftxt ins > ins.edit { @ background-color: #c0c0ff; @ text-decoration: none; @ font-weight: bold; @ } @ @ </style> @ </head> @ <body> ; const char zWebpageEnd[] = @ </body> @ </html> ; /* ** State variables used by the --browser option for diff. These must ** be static variables, not elements of DiffConfig, since they are ** used by the interrupt handler. */ static char *tempDiffFilename; /* File holding the diff HTML */ static FILE *diffOut; /* Open to write into tempDiffFilename */ /* Amount of delay (in milliseconds) between launching the ** web browser and deleting the temporary file used by --browser */ #ifndef FOSSIL_BROWSER_DIFF_DELAY # define FOSSIL_BROWSER_DIFF_DELAY 5000 /* 5 seconds by default */ #endif /* ** If we catch a single while writing the temporary file for the --browser ** diff output, then delete the temporary file and exit. */ static void diff_www_interrupt(int NotUsed){ (void)NotUsed; if( diffOut ) fclose(diffOut); if( tempDiffFilename ) file_delete(tempDiffFilename); exit(1); } #ifdef _WIN32 static BOOL WINAPI diff_console_ctrl_handler(DWORD dwCtrlType){ if( dwCtrlType==CTRL_C_EVENT ) diff_www_interrupt(0); return FALSE; } #endif /* ** Do preliminary setup and output before computing a diff. ** ** For --browser, redirect stdout to a temporary file that will ** hold the result. Make arrangements to delete that temporary ** file if the diff is interrupted. ** ** For --browser and --webpage, output the HTML header. */ void diff_begin(DiffConfig *pCfg){ if( (pCfg->diffFlags & DIFF_BROWSER)!=0 ){ tempDiffFilename = fossil_temp_filename(); tempDiffFilename = sqlite3_mprintf("%z.html", tempDiffFilename); diffOut = fossil_freopen(tempDiffFilename,"wb",stdout); if( diffOut==0 ){ fossil_fatal("unable to create temporary file \"%s\"", tempDiffFilename); } #ifndef _WIN32 signal(SIGINT, diff_www_interrupt); #else SetConsoleCtrlHandler(diff_console_ctrl_handler, TRUE); #endif } if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){ fossil_print("%s",(pCfg->diffFlags & DIFF_DARKMODE)!=0 ? zWebpageHdrDark : zWebpageHdr); fflush(stdout); } } /* Do any final output required by a diff and complete the diff ** process. ** ** For --browser and --webpage, output any javascript required by ** the diff. (Currently JS is only needed for side-by-side diffs). ** ** For --browser, close the connection to the temporary file, then ** launch a web browser to view the file. After a delay ** of FOSSIL_BROWSER_DIFF_DELAY milliseconds, delete the temp file. */ void diff_end(DiffConfig *pCfg, int nErr){ if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){ if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){ const unsigned char *zJs = builtin_file("diff.js", 0); fossil_print("<script>\n%s</script>\n", zJs); } fossil_print("%s", zWebpageEnd); } if( (pCfg->diffFlags & DIFF_BROWSER)!=0 && nErr==0 ){ char *zCmd = mprintf("%s %$", fossil_web_browser(), tempDiffFilename); fclose(diffOut); diffOut = fossil_freopen(NULL_DEVICE, "wb", stdout); fossil_system(zCmd); fossil_free(zCmd); diffOut = 0; sqlite3_sleep(FOSSIL_BROWSER_DIFF_DELAY); file_delete(tempDiffFilename); sqlite3_free(tempDiffFilename); tempDiffFilename = 0; } if( (pCfg->diffFlags & DIFF_JSON)!=0 && pCfg->nFile>0 ){ fossil_print("]\n"); } } /* ** Show the difference between two files, one in memory and one on disk. ** ** The difference is the set of edits needed to transform pFile1 into ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk. */ void diff_file( Blob *pFile1, /* In memory content to compare from */ const char *zFile2, /* On disk content to compare to */ const char *zName, /* Display name of the file */ DiffConfig *pCfg, /* Flags to control the diff */ Blob *pOut /* Blob to store diff output */ ){ if( pCfg->zDiffCmd==0 ){ Blob out; /* Diff output text */ Blob file2; /* Content of zFile2 */ const char *zName2; /* Name of zFile2 for display */ /* Read content of zFile2 into memory */ blob_zero(&file2); if( pCfg->diffFlags & DIFF_FILE_DELETED || file_size(zFile2, ExtFILE)<0 ){ zName2 = NULL_DEVICE; }else{ blob_read_from_file(&file2, zFile2, ExtFILE); zName2 = zName; } /* Compute and output the differences */ if( pCfg->diffFlags & DIFF_BRIEF ){ if( blob_compare(pFile1, &file2) ){ fossil_print("CHANGED %s\n", zName); } }else{ blob_zero(&out); text_diff(pFile1, &file2, &out, pCfg); if( blob_size(&out) ){ if( pCfg->diffFlags & DIFF_NUMSTAT ){ blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); }else{ diff_print_filenames(zName, zName2, pCfg, pOut); blob_appendf(pOut, "%s\n", blob_str(&out)); } } blob_reset(&out); } /* Release memory resources */ blob_reset(&file2); }else{ Blob nameFile1; /* Name of temporary file to old pFile1 content */ Blob cmd; /* Text of command to run */ int useTempfile = 1; if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){ Blob file2; if( looks_like_binary(pFile1) ){ fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY); return; } if( pCfg->zBinGlob ){ Glob *pBinary = glob_create(pCfg->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_size(zFile2, ExtFILE)>=0 ){ blob_read_from_file(&file2, zFile2, ExtFILE); } 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 */ file_tempname(&nameFile1, zFile2, "orig"); #if !defined(_WIN32) /* On Unix, use /dev/null for added or deleted files. */ if( pCfg->diffFlags & DIFF_FILE_ADDED ){ blob_init(&nameFile1, NULL_DEVICE, -1); useTempfile = 0; }else if( pCfg->diffFlags & DIFF_FILE_DELETED ){ zFile2 = NULL_DEVICE; } #endif if( useTempfile ) blob_write_to_file(pFile1, blob_str(&nameFile1)); /* Construct the external diff command */ blob_zero(&cmd); blob_append(&cmd, pCfg->zDiffCmd, -1); if( pCfg->diffFlags & DIFF_INVERT ){ blob_append_escaped_arg(&cmd, zFile2, 1); blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); }else{ blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); blob_append_escaped_arg(&cmd, zFile2, 1); } /* Run the external diff command */ fossil_system(blob_str(&cmd)); /* Delete the temporary file and clean up memory used */ if( useTempfile ) file_delete(blob_str(&nameFile1)); blob_reset(&nameFile1); blob_reset(&cmd); } } /* ** Show the difference between two files, both in memory. ** ** The difference is the set of edits needed to transform pFile1 into ** pFile2. ** ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. ** ** When using an external diff program, zBinGlob contains the GLOB patterns ** for file names to treat as binary. If fIncludeBinary is zero, these files ** will be skipped in addition to files that may contain binary content. */ void diff_file_mem( Blob *pFile1, /* In memory content to compare from */ Blob *pFile2, /* In memory content to compare to */ const char *zName, /* Display name of the file */ DiffConfig *pCfg /* Diff flags */ ){ if( pCfg->diffFlags & DIFF_BRIEF ) return; if( pCfg->zDiffCmd==0 ){ Blob out; /* Diff output text */ blob_zero(&out); text_diff(pFile1, pFile2, &out, pCfg); if( pCfg->diffFlags & DIFF_NUMSTAT ){ fossil_print("%s %s\n", blob_str(&out), zName); }else{ diff_print_filenames(zName, zName, pCfg, 0); fossil_print("%s\n", blob_str(&out)); } /* Release memory resources */ blob_reset(&out); }else{ Blob cmd; Blob temp1; Blob temp2; int useTempfile1 = 1; int useTempfile2 = 1; if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){ if( looks_like_binary(pFile1) || looks_like_binary(pFile2) ){ fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY); return; } if( pCfg->zBinGlob ){ Glob *pBinary = glob_create(pCfg->zBinGlob); if( glob_match(pBinary, zName) ){ fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY); glob_free(pBinary); return; } glob_free(pBinary); } } /* Construct temporary file names */ file_tempname(&temp1, zName, "before"); file_tempname(&temp2, zName, "after"); #if !defined(_WIN32) /* On Unix, use /dev/null for added or deleted files. */ if( pCfg->diffFlags & DIFF_FILE_ADDED ){ useTempfile1 = 0; blob_init(&temp1, NULL_DEVICE, -1); }else if( pCfg->diffFlags & DIFF_FILE_DELETED ){ useTempfile2 = 0; blob_init(&temp2, NULL_DEVICE, -1); } #endif if( useTempfile1 ) blob_write_to_file(pFile1, blob_str(&temp1)); if( useTempfile2 ) blob_write_to_file(pFile2, blob_str(&temp2)); /* Construct the external diff command */ blob_zero(&cmd); blob_append(&cmd, pCfg->zDiffCmd, -1); blob_append_escaped_arg(&cmd, blob_str(&temp1), 1); blob_append_escaped_arg(&cmd, blob_str(&temp2), 1); /* Run the external diff command */ fossil_system(blob_str(&cmd)); /* Delete the temporary file and clean up memory used */ if( useTempfile1 ) file_delete(blob_str(&temp1)); if( useTempfile2 ) file_delete(blob_str(&temp2)); blob_reset(&temp1); blob_reset(&temp2); blob_reset(&cmd); } } /* ** Return true if the disk file is identical to the Blob. Return zero ** if the files differ in any way. */ static int file_same_as_blob(Blob *blob, const char *zDiskFile){ Blob file; int rc = 0; if( blob_size(blob)!=file_size(zDiskFile, ExtFILE) ) return 0; blob_zero(&file); blob_read_from_file(&file, zDiskFile, ExtFILE); if( blob_size(&file)!=blob_size(blob) ){ rc = 0; }else{ rc = memcmp(blob_buffer(&file), blob_buffer(blob), blob_size(&file))==0; } blob_reset(&file); return rc; } /* ** Run a diff between the version zFrom and files on disk. zFrom might ** be NULL which means to simply show the difference between the edited ** files on disk and the check-out on which they are based. ** ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. ** ** When using an external diff program, zBinGlob contains the GLOB patterns ** for file names to treat as binary. If fIncludeBinary is zero, these files ** will be skipped in addition to files that may contain binary content. */ void diff_against_disk( const char *zFrom, /* Version to difference from */ DiffConfig *pCfg, /* Flags controlling diff output */ FileDirList *pFileDir, /* Which files to diff */ Blob *pOut /* Blob to output diff instead of stdout */ ){ int vid; Blob sql; Stmt q; int asNewFile; /* Treat non-existant files as empty files */ int isNumStat; /* True for --numstat */ asNewFile = (pCfg->diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT|DIFF_HTML))!=0; isNumStat = (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_TCL|DIFF_HTML))!=0; vid = db_lget_int("checkout", 0); vfile_check_signature(vid, CKSIG_ENOTFILE); blob_zero(&sql); 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 ); } if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){ diff_print_versions(zFrom ? zFrom : db_lget("checkout-hash", 0), "(workdir)", pCfg); } db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&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); const char *zFullName; int showDiff = 1; Blob fname; if( !file_dir_match(pFileDir, zPathname) ) continue; if( determine_exec_relative_option(0) ){ blob_zero(&fname); file_relative_name(zPathname, &fname, 1); }else{ blob_set(&fname, g.zLocalRoot); blob_append(&fname, zPathname, -1); } zFullName = blob_str(&fname); pCfg->diffFlags &= (~DIFF_FILE_MASK); if( isDeleted ){ if( !isNumStat ){ fossil_print("DELETED %s\n", zPathname); } pCfg->diffFlags |= DIFF_FILE_DELETED; if( !asNewFile ){ showDiff = 0; zFullName = NULL_DEVICE; } }else if( file_access(zFullName, F_OK) ){ if( !isNumStat ){ fossil_print("MISSING %s\n", zPathname); } if( !asNewFile ){ showDiff = 0; } }else if( isNew ){ if( !isNumStat ){ fossil_print("ADDED %s\n", zPathname); } pCfg->diffFlags |= DIFF_FILE_ADDED; srcid = 0; if( !asNewFile ){ showDiff = 0; } }else if( isChnged==3 ){ if( !isNumStat ){ fossil_print("ADDED_BY_MERGE %s\n", zPathname); } pCfg->diffFlags |= DIFF_FILE_ADDED; srcid = 0; if( !asNewFile ){ showDiff = 0; } }else if( isChnged==5 ){ if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); } pCfg->diffFlags |= DIFF_FILE_ADDED; srcid = 0; if( !asNewFile ){ showDiff = 0; } } if( showDiff ){ Blob content; if( !isLink != !file_islink(zFullName) ){ diff_print_index(zPathname, pCfg, 0); diff_print_filenames(zPathname, zPathname, pCfg, 0); fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK); continue; } if( srcid>0 ){ content_get(srcid, &content); }else{ blob_zero(&content); } if( isChnged==0 || pCfg->diffFlags & DIFF_FILE_DELETED || !file_same_as_blob(&content, zFullName) ){ diff_print_index(zPathname, pCfg, pOut); diff_file(&content, zFullName, zPathname, pCfg, pOut); } blob_reset(&content); } blob_reset(&fname); } db_finalize(&q); db_end_transaction(1); /* ROLLBACK */ } /* ** Run a diff between the undo buffer and files on disk. ** ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. ** ** When using an external diff program, zBinGlob contains the GLOB patterns ** for file names to treat as binary. If fIncludeBinary is zero, these files ** will be skipped in addition to files that may contain binary content. */ static void diff_against_undo( DiffConfig *pCfg, /* Flags controlling diff output */ FileDirList *pFileDir /* List of files and directories to diff */ ){ Stmt q; Blob content; db_prepare(&q, "SELECT pathname, content FROM undo"); blob_init(&content, 0, 0); if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){ diff_print_versions("(undo)", "(workdir)", pCfg); } while( db_step(&q)==SQLITE_ROW ){ char *zFullName; const char *zFile = (const char*)db_column_text(&q, 0); if( !file_dir_match(pFileDir, zFile) ) continue; zFullName = mprintf("%s%s", g.zLocalRoot, zFile); db_column_blob(&q, 1, &content); diff_file(&content, zFullName, zFile, pCfg, 0); fossil_free(zFullName); blob_reset(&content); } db_finalize(&q); } /* ** Show the difference between two files identified by ManifestFile ** entries. ** ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. ** ** When using an external diff program, zBinGlob contains the GLOB patterns ** for file names to treat as binary. If fIncludeBinary is zero, these files ** will be skipped in addition to files that may contain binary content. */ static void diff_manifest_entry( struct ManifestFile *pFrom, struct ManifestFile *pTo, DiffConfig *pCfg ){ Blob f1, f2; int rid; const char *zName; if( pFrom ){ zName = pFrom->zName; }else if( pTo ){ zName = pTo->zName; }else{ zName = DIFF_NO_NAME; } if( pCfg->diffFlags & DIFF_BRIEF ) return; diff_print_index(zName, pCfg, 0); if( pFrom ){ rid = uuid_to_rid(pFrom->zUuid, 0); content_get(rid, &f1); }else{ blob_zero(&f1); } if( pTo ){ rid = uuid_to_rid(pTo->zUuid, 0); content_get(rid, &f2); }else{ blob_zero(&f2); } diff_file_mem(&f1, &f2, zName, pCfg); blob_reset(&f1); blob_reset(&f2); } /* ** Output the differences between two check-ins. ** ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. ** ** When using an external diff program, zBinGlob contains the GLOB patterns ** for file names to treat as binary. If fIncludeBinary is zero, these files ** will be skipped in addition to files that may contain binary content. */ static void diff_two_versions( const char *zFrom, const char *zTo, DiffConfig *pCfg, FileDirList *pFileDir ){ Manifest *pFrom, *pTo; ManifestFile *pFromFile, *pToFile; int asNewFlag = (pCfg->diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT))!=0 ? 1 : 0; pFrom = manifest_get_by_name(zFrom, 0); manifest_file_rewind(pFrom); pFromFile = manifest_file_next(pFrom,0); pTo = manifest_get_by_name(zTo, 0); manifest_file_rewind(pTo); pToFile = manifest_file_next(pTo,0); if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){ diff_print_versions(zFrom, zTo, pCfg); } while( pFromFile || pToFile ){ int cmp; if( pFromFile==0 ){ cmp = +1; }else if( pToFile==0 ){ cmp = -1; }else{ cmp = fossil_strcmp(pFromFile->zName, pToFile->zName); } pCfg->diffFlags &= (~DIFF_FILE_MASK); if( cmp<0 ){ if( file_dir_match(pFileDir, pFromFile->zName) ){ if( (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_HTML))==0 ){ fossil_print("DELETED %s\n", pFromFile->zName); } pCfg->diffFlags |= DIFF_FILE_DELETED; if( asNewFlag ){ diff_manifest_entry(pFromFile, 0, pCfg); } } pFromFile = manifest_file_next(pFrom,0); }else if( cmp>0 ){ if( file_dir_match(pFileDir, pToFile->zName) ){ if( (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_HTML|DIFF_TCL|DIFF_JSON))==0 ){ fossil_print("ADDED %s\n", pToFile->zName); } pCfg->diffFlags |= DIFF_FILE_ADDED; if( asNewFlag ){ diff_manifest_entry(0, pToFile, pCfg); } } pToFile = manifest_file_next(pTo,0); }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ /* No changes */ (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ pFromFile = manifest_file_next(pFrom,0); pToFile = manifest_file_next(pTo,0); }else{ if( file_dir_match(pFileDir, pToFile->zName) ){ if( pCfg->diffFlags & DIFF_BRIEF ){ fossil_print("CHANGED %s\n", pFromFile->zName); }else{ diff_manifest_entry(pFromFile, pToFile, pCfg); } } pFromFile = manifest_file_next(pFrom,0); pToFile = manifest_file_next(pTo,0); } } manifest_destroy(pFrom); manifest_destroy(pTo); } /* ** Return the name of the external diff command, or return NULL if ** no external diff command is defined. */ const char *diff_command_external(int guiDiff){ const char *zDefault; const char *zName; if( guiDiff ){ #if defined(_WIN32) zDefault = "WinDiff.exe"; #else zDefault = 0; #endif zName = "gdiff-command"; }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; const char *zTclsh; int bDarkMode = find_option("dark",0,0)!=0; blob_zero(&script); blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v", g.nameOfExe, zSubCmd); find_option("tcl",0,0); find_option("html",0,0); find_option("side-by-side","y",0); find_option("internal","i",0); find_option("verbose","v",0); zTclsh = find_option("tclsh",0,1); if( zTclsh==0 ){ zTclsh = db_get("tclsh",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, "}\nset darkmode %d\n", bDarkMode); blob_appendf(&script, "%s", builtin_file("diff.tcl", 0)); if( zTempFile ){ blob_write_to_file(&script, zTempFile); fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, 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, 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("%$ %$", zTclsh, zTempFile); fossil_system(zCmd); file_delete(zTempFile); fossil_free(zCmd); } blob_reset(&script); } /* ** Returns the GLOB pattern for file names that should be treated as binary ** by the diff subsystem, if any. */ const char *diff_get_binary_glob(void){ const char *zBinGlob = find_option("binary", 0, 1); if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); return zBinGlob; } /* ** COMMAND: diff ** COMMAND: gdiff ** ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? ** ** 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 all unsaved changes ** currently in the working check-out. ** ** The default output format is a "unified patch" (the same as the ** output of "diff -u" on most unix systems). Many alternative formats ** are available. A few of the more useful alternatives: ** ** --tk Pop up a Tcl/Tk-based GUI to show the diff ** --by Show a side-by-side diff in the default web browser ** -b Show a linear diff in the default web browser ** -y Show a text side-by-side diff ** --webpage Format output as HTML ** --webpage -y HTML output in the side-by-side format ** ** The "--from VERSION" option is used to specify 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. Similarly, the "--to VERSION" ** option 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. The "--checkin VERSION" option ** shows the changes made by check-in VERSION relative to its primary parent. ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME. ** ** The "-i" command-line option forces the use of Fossil's own internal ** diff logic rather than any external diff program that might be configured ** using the "setting" command. If no external diff program is configured, ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into ** "diff". ** ** The "--diff-binary" option enables or disables the inclusion of binary files ** 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 the external diff ** program. This option overrides the "binary-glob" setting. ** ** These command show differences between managed files. Use the "fossil xdiff" ** command to see differences in unmanaged files. ** ** 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 ** -b|--browser Show the diff output in a web-browser ** --by Shorthand for "--browser -y" ** -ci|--checkin VERSION Show diff of all changes in VERSION ** --command PROG External diff program. Overrides "diff-command" ** -c|--context N Show N lines of context around each change, ** with negative N meaning show all content ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML ** --diff-binary BOOL Include binary files with external commands ** --exec-abs-paths Force absolute path names on external commands ** --exec-rel-paths Force relative path names on external commands ** -r|--from VERSION Select VERSION as source for the diff ** -w|--ignore-all-space Ignore white space when comparing lines ** -i|--internal Use internal diff logic ** --invert Invert the diff ** --json Output formatted as JSON ** -n|--linenum Show line numbers ** -N|--new-file Alias for --verbose ** --numstat Show only the number of added and deleted lines ** -y|--side-by-side Side-by-side diff ** --strip-trailing-cr Strip trailing CR ** --tcl Tcl-formated output used internally by --tk ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") ** --tk Launch a Tcl/Tk GUI for display ** --to VERSION Select VERSION as target for the diff ** --undo Diff against the "undo" buffer ** --unified Unified diff ** -v|--verbose Output complete text of added or deleted files ** -h|--versions Show compared versions in the diff header ** --webpage Format output as a stand-alone HTML webpage ** -W|--width N 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 */ const char *zFrom; /* Source version number */ const char *zTo; /* Target version number */ const char *zCheckin; /* Check-in version number */ const char *zBranch; /* Branch to diff */ int againstUndo = 0; /* Diff against files in the undo buffer */ FileDirList *pFileDir = 0; /* Restrict the diff to these files */ DiffConfig DCfg; /* Diff configuration object */ if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ diff_tk("diff", 2); return; } isGDiff = g.argv[1][0]=='g'; zFrom = find_option("from", "r", 1); zTo = find_option("to", 0, 1); zCheckin = find_option("checkin", "ci", 1); zBranch = find_option("branch", 0, 1); againstUndo = find_option("undo",0,0)!=0; if( againstUndo && ( zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){ fossil_fatal("cannot use --undo together with --from, --to, --checkin," " or --branch"); } if( zBranch ){ if( zTo || zFrom || zCheckin ){ fossil_fatal("cannot use --from, --to, or --checkin with --branch"); } zTo = zBranch; zFrom = mprintf("root:%s", zBranch); } if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){ fossil_fatal("cannot use --checkin together with --from or --to"); } g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0; if( 0==zCheckin ){ if( zTo==0 || againstUndo ){ db_must_be_within_tree(); }else if( zFrom==0 ){ fossil_fatal("must use --from if --to is present"); }else{ db_find_and_open_repository(0, 0); } }else{ db_find_and_open_repository(0, 0); } diff_options(&DCfg, isGDiff, 0); determine_exec_relative_option(1); verify_all_options(); if( g.argc>=3 ){ int i; Blob fname; pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) ); memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1)); for(i=2; i<g.argc; i++){ file_tree_name(g.argv[i], &fname, 0, 1); pFileDir[i-2].zName = fossil_strdup(blob_str(&fname)); if( strcmp(pFileDir[i-2].zName,".")==0 ){ pFileDir[0].zName[0] = '.'; pFileDir[0].zName[1] = 0; break; } pFileDir[i-2].nName = blob_size(&fname); pFileDir[i-2].nUsed = 0; blob_reset(&fname); } } if ( zCheckin!=0 ){ int ridTo = name_to_typed_rid(zCheckin, "ci"); zTo = zCheckin; zFrom = db_text(0, "SELECT uuid FROM blob, plink" " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid", ridTo); if( zFrom==0 ){ fossil_fatal("check-in %s has no parent", zTo); } } diff_begin(&DCfg); if( againstUndo ){ if( db_lget_int("undo_available",0)==0 ){ fossil_print("No undo or redo is available\n"); return; } diff_against_undo(&DCfg, pFileDir); }else if( zTo==0 ){ diff_against_disk(zFrom, &DCfg, pFileDir, 0); }else{ diff_two_versions(zFrom, zTo, &DCfg, pFileDir); } if( pFileDir ){ int i; for(i=0; pFileDir[i].zName; i++){ if( pFileDir[i].nUsed==0 && strcmp(pFileDir[0].zName,".")!=0 && !file_isdir(g.argv[i+2], ExtFILE) ){ fossil_fatal("not found: '%s'", g.argv[i+2]); } fossil_free(pFileDir[i].zName); } fossil_free(pFileDir); } diff_end(&DCfg, 0); if ( DCfg.diffFlags & DIFF_NUMSTAT ){ fossil_print("%10d %10d TOTAL over %d changed files\n", g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]); } } /* ** WEBPAGE: vpatch ** URL: /vpatch?from=FROM&to=TO ** ** Show a patch that goes from check-in FROM to check-in TO. */ void vpatch_page(void){ const char *zFrom = P("from"); const char *zTo = P("to"); DiffConfig DCfg; cgi_check_for_malice(); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( zFrom==0 || zTo==0 ) fossil_redirect_home(); fossil_nice_default(); cgi_set_content_type("text/plain"); diff_config_init(&DCfg, DIFF_VERBOSE); diff_two_versions(zFrom, zTo, &DCfg, 0); } |
Added src/dispatch.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 | /* ** Copyright (c) 2016 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 map command names (ex: "help", "commit", ** "diff") or webpage names (ex: "/timeline", "/search") into the functions ** that implement those commands and web pages and their associated help ** text. */ #include "config.h" #include <assert.h> #include "dispatch.h" #if INTERFACE /* ** An instance of this object defines everything we need to know about an ** individual command, webpage, or setting. */ struct CmdOrPage { const char *zName; /* Name. Webpages start with "/". Commands do not */ void (*xFunc)(void); /* Implementation function, or NULL for settings */ const char *zHelp; /* Raw help text */ int iHelp; /* Index of help variable */ unsigned int eCmdFlags; /* Flags */ }; /*************************************************************************** ** These macros must match similar macros in mkindex.c ** Allowed values for CmdOrPage.eCmdFlags. */ #define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */ #define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */ #define CMDFLAG_TEST 0x0004 /* Commands for testing only */ #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ #define CMDFLAG_COMMAND 0x0010 /* A command */ #define CMDFLAG_SETTING 0x0020 /* A setting */ #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret POST content */ /* NOTE: 0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */ #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ /**************************************************************************/ /* Values for the 2nd parameter to dispatch_name_search() */ #define CMDFLAG_ANY 0x0038 /* Match anything */ #define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */ #endif /* INTERFACE */ /* ** The page_index.h file contains the definition for aCommand[] - an array ** of CmdOrPage objects that defines all available commands and webpages ** known to Fossil. ** ** The entries in aCommand[] are in sorted order by name. Since webpage names ** always begin with "/", all webpage names occur first. The page_index.h file ** also sets the FOSSIL_FIRST_CMD macro to be the *approximate* index ** in aCommand[] of the first command entry. FOSSIL_FIRST_CMD might be ** slightly too low, and so the range FOSSIL_FIRST_CMD...MX_COMMAND might ** contain a few webpage entries at the beginning. ** ** The page_index.h file is generated by the mkindex program which scans all ** source code files looking for header comments on the functions that ** implement command and webpages. */ #include "page_index.h" #define MX_COMMAND count(aCommand) #define MX_HELP_DUP 5 /* Upper bound estimate on help string duplication */ /* ** Given a command, webpage, or setting name in zName, find the corresponding ** CmdOrPage object and return a pointer to that object in *ppCmd. ** ** The eType field is CMDFLAG_COMMAND to look up commands, CMDFLAG_WEBPAGE to ** look up webpages, CMDFLAG_SETTING to look up settings, or CMDFLAG_ANY to look ** for any. If the CMDFLAG_PREFIX bit is set, then a prefix match is allowed. ** ** Return values: ** 0: Success. *ppCmd is set to the appropriate CmdOrPage ** 1: Not found. ** 2: Ambiguous. Two or more entries match. */ int dispatch_name_search( const char *zName, /* Look for this name */ unsigned eType, /* CMDFLAGS_* bits */ const CmdOrPage **ppCmd /* Write the matching CmdOrPage object here */ ){ int upr, lwr, mid; int nName = strlen(zName); lwr = 0; upr = MX_COMMAND - 1; while( lwr<=upr ){ int c; mid = (upr+lwr)/2; c = strcmp(zName, aCommand[mid].zName); if( c==0 ){ if( (aCommand[mid].eCmdFlags & eType)==0 ) return 1; *ppCmd = &aCommand[mid]; return 0; /* An exact match */ }else if( c<0 ){ upr = mid - 1; }else{ lwr = mid + 1; } } if( (eType & CMDFLAG_PREFIX)!=0 && lwr<MX_COMMAND && strncmp(zName, aCommand[lwr].zName, nName)==0 ){ /* An inexact prefix match was found. Scan the name table to try to find * exactly one entry with this prefix and the requested type. */ for( mid=-1; lwr<MX_COMMAND && strncmp(zName, aCommand[lwr].zName, nName)==0; ++lwr ){ if( aCommand[lwr].eCmdFlags & eType ){ if( mid<0 ){ mid = lwr; /* Potential ambiguous prefix */ }else{ if( aCommand[lwr].xFunc != aCommand[mid].xFunc ){ return 2; /* Confirmed ambiguous prefix */ } } } } if( mid>=0 ){ *ppCmd = &aCommand[mid]; return 0; /* Prefix match */ } } return 1; /* Not found */ } /* ** zName is the name of a webpage (eType==CMDFLAGS_WEBPAGE) that does not ** exist in the dispatch table. Check to see if this webpage name exists ** as an alias in the CONFIG table of the repository. If it is, then make ** appropriate changes to the CGI environment and set *ppCmd to point to the ** aliased command. ** ** Return 0 if the command is successfully aliased. Return 1 if there ** is not alias for zName. Any kind of error in the alias value causes a ** error to be thrown. ** ** Alias entries in the CONFIG table have a "name" value of "walias:NAME" ** where NAME is the input page name. The value is a string of the form ** "NEWNAME?QUERYPARAMS". The ?QUERYPARAMS is optional. If present (and it ** usually is), then all query parameters are added to the CGI environment. ** Except, query parameters of the form "X!" cause any CGI X variable to be ** removed. */ int dispatch_alias(const char *zName, const CmdOrPage **ppCmd){ char *z; char *zQ; int i; z = db_text(0, "SELECT value FROM config WHERE name='walias:%q'",zName); if( z==0 ) return 1; for(i=0; z[i] && z[i]!='?'; i++){} if( z[i]=='?' ){ z[i] = 0; zQ = &z[i+1]; }else{ zQ = &z[i]; } if( dispatch_name_search(z, CMDFLAG_WEBPAGE, ppCmd) ){ fossil_fatal("\"%s\" aliased to \"%s\" but \"%s\" does not exist", zName, z, z); } z = zQ; while( *z ){ char *zName = z; char *zValue = 0; while( *z && *z!='=' && *z!='&' && *z!='!' ){ z++; } if( *z=='=' ){ *z = 0; z++; zValue = z; while( *z && *z!='&' ){ z++; } if( *z ){ *z = 0; z++; } dehttpize(zValue); }else if( *z=='!' ){ *(z++) = 0; cgi_delete_query_parameter(zName); zName = ""; }else{ if( *z ){ *z++ = 0; } zValue = ""; } if( fossil_islower(zName[0]) ){ cgi_replace_query_parameter(zName, zValue); }else if( fossil_isupper(zName[0]) ){ cgi_replace_query_parameter_tolower(zName, zValue); } } return 0; } /* ** Fill Blob with a space-separated list of all command names that ** match the prefix zPrefix and the eType CMDFLAGS_ bits. */ void dispatch_matching_names( const char *zPrefix, /* name prefix */ unsigned eType, /* CMDFLAG_ bits */ Blob *pList /* space-separated list of command names */ ){ int i; int nPrefix = (int)strlen(zPrefix); for(i=FOSSIL_FIRST_CMD; i<MX_COMMAND; i++){ if( (aCommand[i].eCmdFlags & eType)==0 ) continue; if( strncmp(zPrefix, aCommand[i].zName, nPrefix)==0 ){ blob_appendf(pList, " %s", aCommand[i].zName); } } } /* ** Return the index of the first non-space character that follows ** a span of two or more spaces. Return 0 if there is not gap. */ static int hasGap(const char *z, int n){ int i; for(i=3; i<n-1; i++){ if( z[i]==' ' && z[i+1]!=' ' && z[i-1]==' ' && z[i-2]!='.' ) return i+1; } return 0 ; } /* ** Input string zIn starts with '['. If the content is a hyperlink of the ** form [[...]] then return the index of the closing ']'. Otherwise return 0. */ static int help_is_link(const char *z, int n){ int i; char c; if( n<5 ) return 0; if( z[1]!='[' ) return 0; for(i=3; i<n && (c = z[i])!=0; i++){ if( c==']' && z[i-1]==']' ) return i; } return 0; } /* ** Append text to pOut with changes: ** ** * Add hyperlink markup for [[...]] ** * Escape HTML characters: < > & and " ** * Change "%fossil" to just "fossil" */ static void appendLinked(Blob *pOut, const char *z, int n){ int i = 0; int j; while( i<n ){ char c = z[i]; if( c=='[' && (j = help_is_link(z+i, n-i))>0 ){ if( i ) blob_append(pOut, z, i); z += i+2; n -= i+2; blob_appendf(pOut, "<a href='%R/help?cmd=%.*s'>%.*s</a>", j-3, z, j-3, z); z += j-1; n -= j-1; i = 0; }else if( c=='%' && n-i>=7 && strncmp(z+i,"%fossil",7)==0 ){ if( i ) blob_append(pOut, z, i); z += i+7; n -= i+7; blob_append(pOut, "fossil", 6); i = 0; }else if( c=='<' ){ if( i ) blob_append(pOut, z, i); blob_append(pOut, "<", 4); z += i+1; n -= i+1; i = 0; }else if( c=='>' ){ if( i ) blob_append(pOut, z, i); blob_append(pOut, ">", 4); z += i+1; n -= i+1; i = 0; }else if( c=='&' ){ if( i ) blob_append(pOut, z, i); blob_append(pOut, "&", 5); z += i+1; n -= i+1; i = 0; }else{ i++; } } blob_append(pOut, z, i); } /* ** Append text to pOut, adding formatting markup. Terms that ** have all lower-case letters are within <tt>..</tt>. Terms ** that have all upper-case letters are within <i>..</i>. */ static void appendMixedFont(Blob *pOut, const char *z, int n){ const char *zEnd = ""; int i = 0; int j; while( i<n ){ if( z[i]==' ' || z[i]=='=' ){ for(j=i+1; j<n && (z[j]==' ' || z[j]=='='); j++){} appendLinked(pOut, z+i, j-i); i = j; }else{ for(j=i; j<n && z[j]!=' ' && z[j]!='=' && !fossil_isalpha(z[j]); j++){} if( j>=n || z[j]==' ' || z[j]=='=' ){ zEnd = ""; }else{ if( fossil_isupper(z[j]) && z[i]!='-' ){ blob_append(pOut, "<i>",3); zEnd = "</i>"; }else{ blob_append(pOut, "<tt>", 4); zEnd = "</tt>"; } } while( j<n && z[j]!=' ' && z[j]!='=' ){ j++; } appendLinked(pOut, z+i, j-i); if( zEnd[0] ) blob_append(pOut, zEnd, -1); i = j; } } } /* ** Attempt to reformat plain-text help into HTML for display on a webpage. ** ** The HTML output is appended to Blob pHtml, which should already be ** initialized. ** ** Formatting rules: ** ** * Bullet lists are indented from the surrounding text by ** at least one space. Each bullet begins with " * ". ** ** * Display lists are indented from the surrounding text. ** Each tag begins with "-" or occur on a line that is ** followed by two spaces and a non-space. <dd> elements can begin ** on the same line as long as they are separated by at least ** two spaces. ** ** * Indented text is show verbatim (<pre>...</pre>) ** ** * Lines that begin with "|" at the left margin are in <pre>...</pre> */ static void help_to_html(const char *zHelp, Blob *pHtml){ int i; char c; int nIndent = 0; int wantP = 0; int wantBR = 0; int aIndent[10]; const char *azEnd[10]; int iLevel = 0; int isLI = 0; int isDT = 0; int inPRE = 0; static const char *zEndDL = "</dl></blockquote>"; static const char *zEndPRE = "</pre></blockquote>"; static const char *zEndUL = "</ul>"; static const char *zEndDD = "</dd>"; aIndent[0] = 0; azEnd[0] = ""; while( zHelp[0] ){ i = 0; while( (c = zHelp[i])!=0 && c!='\n' ){ if( c=='%' && i>2 && zHelp[i-2]==':' && strncmp(zHelp+i,"%fossil",7)==0 ){ appendLinked(pHtml, zHelp, i); zHelp += i+1; i = 0; wantBR = 1; continue; } i++; } if( i>2 && (zHelp[0]=='>' || zHelp[0]=='|') && zHelp[1]==' ' ){ if( zHelp[0]=='>' ){ isDT = 1; for(nIndent=1; nIndent<i && zHelp[nIndent]==' '; nIndent++){} }else{ if( !inPRE ){ blob_append(pHtml, "<pre>\n", -1); inPRE = 1; } } }else{ if( inPRE ){ blob_append(pHtml, "</pre>\n", -1); inPRE = 0; } isDT = 0; for(nIndent=0; nIndent<i && zHelp[nIndent]==' '; nIndent++){} } if( inPRE ){ blob_append(pHtml, zHelp+1, i); zHelp += i + 1; continue; } if( nIndent==i ){ if( c==0 ) break; if( iLevel && azEnd[iLevel]==zEndPRE ){ /* Skip the newline at the end of a <pre> */ }else{ blob_append_char(pHtml, '\n'); } wantP = 1; wantBR = 0; zHelp += i+1; continue; } if( nIndent+2<i && zHelp[nIndent]=='*' && zHelp[nIndent+1]==' ' ){ nIndent += 2; while( nIndent<i && zHelp[nIndent]==' '){ nIndent++; } isLI = 1; }else{ isLI = 0; } while( iLevel>0 && aIndent[iLevel]>nIndent ){ blob_append(pHtml, azEnd[iLevel--], -1); } if( nIndent>aIndent[iLevel] ){ assert( iLevel<ArraySize(aIndent)-2 ); if( isLI ){ iLevel++; aIndent[iLevel] = nIndent; azEnd[iLevel] = zEndUL; if( wantP ){ blob_append(pHtml,"<p>", 3); wantP = 0; } blob_append(pHtml, "<ul>\n", 5); }else if( isDT || zHelp[nIndent]=='-' || hasGap(zHelp+nIndent,i-nIndent) ){ iLevel++; aIndent[iLevel] = nIndent; azEnd[iLevel] = zEndDL; wantP = 0; blob_append(pHtml, "<blockquote><dl>\n", -1); }else if( azEnd[iLevel]==zEndDL ){ iLevel++; aIndent[iLevel] = nIndent; azEnd[iLevel] = zEndDD; if( wantP ){ blob_append(pHtml,"<p>", 3); wantP = 0; } blob_append(pHtml, "<dd>", 4); }else if( wantP ){ iLevel++; aIndent[iLevel] = nIndent; azEnd[iLevel] = zEndPRE; blob_append(pHtml, "<blockquote><pre>", -1); wantP = 0; } } if( isLI ){ blob_append(pHtml, "<li> ", 5); } if( wantP ){ blob_append(pHtml, "<p> ", 4); wantP = 0; } if( azEnd[iLevel]==zEndDL ){ int iDD; blob_append(pHtml, "<dt> ", 5); iDD = hasGap(zHelp+nIndent, i-nIndent); if( iDD ){ int x; assert( iLevel<ArraySize(aIndent)-1 ); iLevel++; aIndent[iLevel] = x = nIndent+iDD; azEnd[iLevel] = zEndDD; appendMixedFont(pHtml, zHelp+nIndent, iDD-2); blob_append(pHtml, "</dt><dd>",9); appendLinked(pHtml, zHelp+x, i-x); }else{ appendMixedFont(pHtml, zHelp+nIndent, i-nIndent); } blob_append(pHtml, "</dt>\n", 6); }else if( wantBR ){ appendMixedFont(pHtml, zHelp+nIndent, i-nIndent); blob_append(pHtml, "<br>\n", 5); wantBR = 0; }else{ appendLinked(pHtml, zHelp+nIndent, i-nIndent); blob_append_char(pHtml, '\n'); } zHelp += i+1; i = 0; if( c==0 ) break; } while( iLevel>0 ){ blob_appendf(pHtml, "%s\n", azEnd[iLevel--]); } } /* ** Format help text for TTY display. */ static void help_to_text(const char *zHelp, Blob *pText){ int i, x; char c; for(i=0; (c = zHelp[i])!=0; i++){ if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){ if( i>0 ) blob_append(pText, zHelp, i); blob_append(pText, "fossil", 6); zHelp += i+7; i = -1; continue; } if( c=='\n' && (zHelp[i+1]=='>' || zHelp[i+1]=='|') && zHelp[i+2]==' ' ){ blob_append(pText, zHelp, i+1); blob_append(pText, " ", 1); zHelp += i+2; i = -1; continue; } if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){ if( i>0 ) blob_append(pText, zHelp, i); zHelp += i+2; blob_append(pText, zHelp, x-3); zHelp += x-1; i = -1; continue; } } if( i>0 ){ blob_append(pText, zHelp, i); } } /* ** Display help for all commands based on provided flags. */ static void display_all_help(int mask, int useHtml, int rawOut){ int i; unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */ int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help strings->commands*/ if( useHtml ) fossil_print("<!--\n"); fossil_print("Help text for:\n"); if( mask & CMDFLAG_1ST_TIER ) fossil_print(" * Commands\n"); if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n"); if( mask & CMDFLAG_ALIAS ) fossil_print(" * Aliases\n"); if( mask & CMDFLAG_TEST ) fossil_print(" * Test commands\n"); if( mask & CMDFLAG_WEBPAGE ) fossil_print(" * Web pages\n"); if( mask & CMDFLAG_SETTING ) fossil_print(" * Settings\n"); if( useHtml ){ fossil_print("-->\n"); fossil_print("<!-- start_all_help -->\n"); }else{ fossil_print("---\n"); } /* Fill in help string buckets */ for(i=0; i<MX_COMMAND; i++){ if( (aCommand[i].eCmdFlags & mask)==0 ) continue; else if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue; bktHelp[aCommand[i].iHelp][occHelp[aCommand[i].iHelp]++] = i; } for(i=0; i<MX_COMMAND; i++){ if( (aCommand[i].eCmdFlags & mask)==0 ) continue; else if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue; if( occHelp[aCommand[i].iHelp] > 0 ){ int j; if( useHtml ){ Blob html; blob_init(&html, 0, 0); help_to_html(aCommand[i].zHelp, &html); for(j=0; j<occHelp[aCommand[i].iHelp]; j++){ fossil_print("<h1>%h</h1>\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName); } fossil_print("%s\n<hr>\n", blob_str(&html)); blob_reset(&html); }else if( rawOut ){ for(j=0; j<occHelp[aCommand[i].iHelp]; j++) fossil_print("# %s\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName); fossil_print("%s\n\n", aCommand[i].zHelp); }else{ Blob txt; blob_init(&txt, 0, 0); help_to_text(aCommand[i].zHelp, &txt); for(j=0; j<occHelp[aCommand[i].iHelp]; j++){ fossil_print("# %s%s\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName, (aCommand[i].eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? " (versionable)" : ""); } fossil_print("%s\n\n", blob_str(&txt)); blob_reset(&txt); } occHelp[aCommand[i].iHelp] = 0; } } if( useHtml ){ fossil_print("<!-- end_all_help -->\n"); }else{ fossil_print("---\n"); } version_cmd(); } /* ** COMMAND: test-all-help ** ** Usage: %fossil test-all-help ?OPTIONS? ** ** Show help text for commands and pages. Useful for proof-reading. ** Defaults to just the CLI commands. Specify --www to see only the ** web pages, or --everything to see both commands and pages. ** ** Options: ** -a|--aliases Show aliases ** -e|--everything Show all commands and pages. Omit aliases to ** avoid duplicates. ** -h|--html Transform output to HTML ** -o|--options Show global options ** -r|--raw No output formatting ** -s|--settings Show settings ** -t|--test Include test- commands ** -w|--www Show WWW pages */ void test_all_help_cmd(void){ int mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER; int useHtml = find_option("html","h",0)!=0; int rawOut = find_option("raw","r",0)!=0; if( find_option("www","w",0) ){ mask = CMDFLAG_WEBPAGE; } if( find_option("everything","e",0) ){ mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST; } if( find_option("settings","s",0) ){ mask = CMDFLAG_SETTING; } if( find_option("aliases","a",0) ){ mask = CMDFLAG_ALIAS; } if( find_option("test","t",0) ){ mask |= CMDFLAG_TEST; } display_all_help(mask, useHtml, rawOut); } /* ** Count the number of entries in the aCommand[] table that match ** the given flag. */ static int countCmds(unsigned int eFlg){ int n = 0; int i; for(i=0; i<MX_COMMAND; i++){ if( (aCommand[i].eCmdFlags & eFlg)!=0 ) n++; } return n; } /* ** COMMAND: test-command-stats ** ** Print statistics about the built-in command dispatch table. */ void test_command_stats_cmd(void){ fossil_print("commands: %4d\n", countCmds( CMDFLAG_COMMAND )); fossil_print(" 1st tier %4d\n", countCmds( CMDFLAG_1ST_TIER )); fossil_print(" 2nd tier %4d\n", countCmds( CMDFLAG_2ND_TIER )); fossil_print(" alias %4d\n", countCmds( CMDFLAG_ALIAS )); fossil_print(" test %4d\n", countCmds( CMDFLAG_TEST )); fossil_print("web-pages: %4d\n", countCmds( CMDFLAG_WEBPAGE )); fossil_print("settings: %4d\n", countCmds( CMDFLAG_SETTING )); fossil_print("total entries: %4d\n", MX_COMMAND); } /* ** Compute an estimate of the edit-distance between to input strings. ** ** The first string is the input. The second is the pattern. Only the ** first 100 characters of the pattern are considered. */ static int edit_distance(const char *zA, const char *zB){ int nA = (int)strlen(zA); int nB = (int)strlen(zB); int i, j, m; int p0, p1, c0; int a[100] = {0}; static const int incr = 4; for(j=0; j<nB; j++) a[j] = 1; for(i=0; i<nA; i++){ p0 = i==0 ? 0 : i*incr-1; c0 = i*incr; for(j=0; j<nB; j++){ int m = 999; p1 = a[j]; if( zA[i]==zB[j] ){ m = p0; }else{ m = c0+2; if( m>p1+2 ) m = p1+2; if( m>p0+3 ) m = p0+3; } c0 = a[j]; a[j] = m; p0 = p1; } } m = a[nB-1]; for(j=0; j<nB-1; j++){ if( a[j]+1<m ) m = a[j]+1; } return m; } /* ** Fill the pointer array with names of commands that approximately ** match the input. Return the number of approximate matches. ** ** Closest matches appear first. */ int dispatch_approx_match(const char *zIn, int nArray, const char **azArray){ int i; int bestScore; int m; int n = 0; int mnScore = 0; int mxScore = 99999; int iFirst, iLast; if( zIn[0]=='/' ){ iFirst = 0; iLast = FOSSIL_FIRST_CMD-1; }else{ iFirst = FOSSIL_FIRST_CMD; iLast = MX_COMMAND-1; } while( n<nArray ){ bestScore = mxScore; for(i=iFirst; i<=iLast; i++){ m = edit_distance(zIn, aCommand[i].zName); if( m<mnScore ) continue; if( m==mnScore ){ azArray[n++] = aCommand[i].zName; if( n>=nArray ) return n; }else if( m<bestScore ){ bestScore = m; } } if( bestScore>=mxScore ) break; mnScore = bestScore; } return n; } /* ** COMMAND: test-approx-match ** ** Test the approximate match algorithm */ void test_approx_match_command(void){ int i, j, n; const char *az[20]; for(i=2; i<g.argc; i++){ fossil_print("%s:\n", g.argv[i]); n = dispatch_approx_match(g.argv[i], 20, az); for(j=0; j<n; j++){ fossil_print(" %s\n", az[j]); } } } /* ** WEBPAGE: help ** URL: /help?name=CMD ** ** Show the built-in help text for CMD. CMD can be a command-line interface ** command or a page name from the web interface or a setting. ** Query parameters: ** ** name=CMD Show help for CMD where CMD is a command name or ** webpage name or setting name. ** ** plaintext Show the help within <pre>...</pre>, as if it were ** displayed using the "fossil help" command. ** ** raw Show the raw help text without any formatting. ** (Used for debugging.) */ void help_page(void){ const char *zCmd = P("cmd"); if( zCmd==0 ) zCmd = P("name"); cgi_check_for_malice(); if( zCmd && *zCmd ){ int rc; const CmdOrPage *pCmd = 0; style_set_current_feature("tkt"); style_header("Help: %s", zCmd); style_submenu_element("Command-List", "%R/help"); rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); if( *zCmd=='/' ){ /* Some of the webpages require query parameters in order to work. ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ @ <h1>The "%h(zCmd)" page:</h1> }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ @ <h1>The "%h(pCmd->zName)" setting:</h1> }else{ @ <h1>The "%h(zCmd)" command:</h1> } if( rc==1 ){ @ unknown command: %h(zCmd) }else if( rc==2 ){ @ ambiguous command prefix: %h(zCmd) }else{ if( pCmd->zHelp[0]==0 ){ @ No help available for "%h(pCmd->zName)" }else if( P("plaintext") ){ Blob txt; blob_init(&txt, 0, 0); help_to_text(pCmd->zHelp, &txt); @ <pre class="helpPage"> @ %h(blob_str(&txt)) @ </pre> blob_reset(&txt); }else if( P("raw") ){ @ <pre class="helpPage"> @ %h(pCmd->zHelp) @ </pre> }else{ @ <div class="helpPage"> help_to_html(pCmd->zHelp, cgi_output_blob()); @ </div> } } }else{ int i; unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */ int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */ style_header("Help"); @ <a name='commands'></a> @ <h1>Available commands:</h1> @ <div class="columns" style="column-width: 12ex;"> @ <ul> /* Fill in help string buckets */ for(i=0; i<MX_COMMAND; i++){ if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue; bktHelp[aCommand[i].iHelp][occHelp[aCommand[i].iHelp]++] = i; } for(i=0; i<MX_COMMAND; i++){ const char *z = aCommand[i].zName; const char *zBoldOn = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"<b>" :""; const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":""; if( '/'==*z || strncmp(z,"test",4)==0 ) continue; if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue; else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; else if( (aCommand[i].eCmdFlags & CMDFLAG_ALIAS)!=0 ) continue; @ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a> /* Output aliases */ if( occHelp[aCommand[i].iHelp] > 1 ){ int j; int aliases[MX_HELP_DUP], nAliases=0; for(j=0; j<occHelp[aCommand[i].iHelp]; j++){ if( bktHelp[aCommand[i].iHelp][j] != i ){ if( aCommand[bktHelp[aCommand[i].iHelp][j]].eCmdFlags & CMDFLAG_ALIAS ){ aliases[nAliases++] = bktHelp[aCommand[i].iHelp][j]; } } } if( nAliases>0 ){ int k; @(\ for(k=0; k<nAliases; k++){ @<a href="%R/help?cmd=%s(aCommand[aliases[k]].zName)">\ @%s(aCommand[aliases[k]].zName)</a>%s((k<nAliases-1)?", ":"")\ } @)\ } } @ </li> } @ </ul></div> @ <a name='webpages'></a> @ <h1>Available web UI pages:</h1> @ <div class="columns" style="column-width: 18ex;"> @ <ul> for(i=0; i<MX_COMMAND; i++){ const char *z = aCommand[i].zName; if( '/'!=*z ) continue; else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; if( aCommand[i].zHelp[0] ){ @ <li><a href="%R/help?cmd=%s(z)">%s(z+1)</a></li> }else{ @ <li>%s(z+1)</li> } } @ </ul></div> @ <a name='unsupported'></a> @ <h1>Unsupported commands:</h1> @ <div class="columns" style="column-width: 20ex;"> @ <ul> for(i=0; i<MX_COMMAND; i++){ const char *z = aCommand[i].zName; if( strncmp(z,"test",4)!=0 ) continue; else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; if( aCommand[i].zHelp[0] ){ @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li> }else{ @ <li>%s(z)</li> } } @ </ul></div> @ <a name='settings'></a> @ <h1>Settings:</h1> @ <div class="columns" style="column-width: 20ex;"> @ <ul> for(i=0; i<MX_COMMAND; i++){ const char *z = aCommand[i].zName; if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue; else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue; if( aCommand[i].zHelp[0] ){ @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li> }else{ @ <li>%s(z)</li> } } @ </ul></div> } style_finish_page(); } /* ** WEBPAGE: test-all-help ** ** Show all help text on a single page. Useful for proof-reading. */ void test_all_help_page(void){ int i; unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */ int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help strings->commands*/ Blob buf; blob_init(&buf,0,0); style_set_current_feature("test"); style_header("All Help Text"); @ <dl> /* Fill in help string buckets */ for(i=0; i<MX_COMMAND; i++){ if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue; bktHelp[aCommand[i].iHelp][occHelp[aCommand[i].iHelp]++] = i; } for(i=0; i<MX_COMMAND; i++){ const char *zDesc; unsigned int e = aCommand[i].eCmdFlags; if( e & CMDFLAG_1ST_TIER ){ zDesc = "1st tier command"; }else if( e & CMDFLAG_2ND_TIER ){ zDesc = "2nd tier command"; }else if( e & CMDFLAG_ALIAS ){ zDesc = "alias"; }else if( e & CMDFLAG_TEST ){ zDesc = "test command"; }else if( e & CMDFLAG_WEBPAGE ){ if( e & CMDFLAG_RAWCONTENT ){ zDesc = "raw-content web page"; }else{ zDesc = "web page"; } }else{ blob_reset(&buf); if( e & CMDFLAG_VERSIONABLE ){ blob_appendf(&buf, "versionable "); } if( e & CMDFLAG_BLOCKTEXT ){ blob_appendf(&buf, "block-text "); } if( e & CMDFLAG_BOOLEAN ){ blob_appendf(&buf, "boolean "); } blob_appendf(&buf,"setting"); zDesc = blob_str(&buf); } if( memcmp(aCommand[i].zName, "test", 4)==0 ) continue; if( occHelp[aCommand[i].iHelp] > 0 ){ int j; for(j=0; j<occHelp[aCommand[i].iHelp]; j++){ unsigned int e = aCommand[bktHelp[aCommand[i].iHelp][j]].eCmdFlags; if( e & CMDFLAG_1ST_TIER ){ zDesc = "1st tier command"; }else if( e & CMDFLAG_2ND_TIER ){ zDesc = "2nd tier command"; }else if( e & CMDFLAG_ALIAS ){ zDesc = "alias"; }else if( e & CMDFLAG_TEST ){ zDesc = "test command"; }else if( e & CMDFLAG_WEBPAGE ){ if( e & CMDFLAG_RAWCONTENT ){ zDesc = "raw-content web page"; }else{ zDesc = "web page"; } } @ <dt><big><b>%s(aCommand[bktHelp[aCommand[i].iHelp][j]].zName)</b> @</big> (%s(zDesc))</dt> } @ <p><dd> help_to_html(aCommand[i].zHelp, cgi_output_blob()); @ </dd><p> occHelp[aCommand[i].iHelp] = 0; } } @ </dl> blob_reset(&buf); style_finish_page(); } static void multi_column_list(const char **azWord, int nWord){ int i, j, len; int mxLen = 0; int nCol; int nRow; for(i=0; i<nWord; i++){ len = strlen(azWord[i]); if( len>mxLen ) mxLen = len; } nCol = 80/(mxLen+2); if( nCol==0 ) nCol = 1; nRow = (nWord + nCol - 1)/nCol; for(i=0; i<nRow; i++){ const char *zSpacer = ""; for(j=i; j<nWord; j+=nRow){ fossil_print("%s%-*s", zSpacer, mxLen, azWord[j]); zSpacer = " "; } fossil_print("\n"); } } /* ** COMMAND: test-list-webpage ** ** List all web pages. */ void cmd_test_webpage_list(void){ int i, nCmd; const char *aCmd[MX_COMMAND]; for(i=nCmd=0; i<MX_COMMAND; i++){ if(CMDFLAG_WEBPAGE & aCommand[i].eCmdFlags){ aCmd[nCmd++] = aCommand[i].zName; } } assert(nCmd && "page list is empty?"); multi_column_list(aCmd, nCmd); } /* ** List of commands starting with zPrefix, or all commands if zPrefix is NULL. */ static void command_list(int cmdMask, int verboseFlag, int useHtml){ if( verboseFlag ){ display_all_help(cmdMask, useHtml, 0); }else{ int i, nCmd; const char *aCmd[MX_COMMAND]; for(i=nCmd=0; i<MX_COMMAND; i++){ if( (aCommand[i].eCmdFlags & cmdMask)==0 ) continue; else if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue; aCmd[nCmd++] = aCommand[i].zName; } multi_column_list(aCmd, nCmd); } } /* ** Documentation on universal command-line options. */ /* @-comment: # */ static const char zOptions[] = @ Command-line options common to all commands: @ @ --args FILENAME Read additional arguments and options from FILENAME @ --case-sensitive BOOL Set case sensitivity for file names @ --cgitrace Active CGI tracing @ --chdir PATH Change to PATH before performing any operations @ --comfmtflags VALUE Set comment formatting flags to VALUE @ --comment-format VALUE Alias for --comfmtflags @ --errorlog FILENAME Log errors to FILENAME @ --help Show help on the command rather than running it @ --httptrace Trace outbound HTTP requests @ --localtime Display times using the local timezone @ --nocgi Do not act as CGI @ --no-th-hook Do not run TH1 hooks @ --quiet Reduce the amount of output @ --sqlstats Show SQL usage statistics when done @ --sqltrace Trace all SQL commands @ --sshtrace Trace SSH activity @ --ssl-identity NAME Set the SSL identity to NAME @ --systemtrace Trace calls to system() @ -U|--user USER Make the default user be USER @ --utc Display times using UTC @ --vfs NAME Cause SQLite to use the NAME VFS ; /* ** COMMAND: help ** ** Usage: %fossil help [OPTIONS] [TOPIC] ** ** Display information on how to use TOPIC, which may be a command, webpage, or ** setting. Webpage names begin with "/". If TOPIC is omitted, a list of ** topics is returned. ** ** The following options can be used when TOPIC is omitted: ** ** -a|--all List both common and auxiliary commands ** -o|--options List command-line options common to all commands ** -s|--setting List setting names ** -t|--test List unsupported "test" commands ** -v|--verbose List both names and help text ** -x|--aux List only auxiliary commands ** -w|--www List all web pages ** -f|--full List full set of commands (including auxiliary ** and unsupported "test" commands), options, ** settings, and web pages ** -e|--everything List all help on all topics ** ** These options can be used when TOPIC is present: ** ** -h|--html Format output as HTML rather than plain text ** -c|--commands Restrict TOPIC search to commands */ void help_cmd(void){ int rc; int mask = CMDFLAG_ANY; int isPage = 0; int verboseFlag = 0; int commandsFlag = 0; const char *z; const char *zCmdOrPage; const CmdOrPage *pCmd = 0; int useHtml = 0; const char *zTopic; Blob txt; verboseFlag = find_option("verbose","v",0)!=0; commandsFlag = find_option("commands","c",0)!=0; useHtml = find_option("html","h",0)!=0; if( find_option("options","o",0) ){ fossil_print("%s", zOptions); return; } else if( find_option("all","a",0) ){ command_list(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER, verboseFlag, useHtml); return; } else if( find_option("www","w",0) ){ command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); return; } else if( find_option("aux","x",0) ){ command_list(CMDFLAG_2ND_TIER, verboseFlag, useHtml); return; } else if( find_option("test","t",0) ){ command_list(CMDFLAG_TEST, verboseFlag, useHtml); return; } else if( find_option("setting","s",0) ){ command_list(CMDFLAG_SETTING, verboseFlag, useHtml); return; } else if( find_option("full","f",0) ){ fossil_print("fossil commands:\n\n"); command_list(CMDFLAG_1ST_TIER, verboseFlag, useHtml); fossil_print("\nfossil auxiliary commands:\n\n"); command_list(CMDFLAG_2ND_TIER, verboseFlag, useHtml); fossil_print("\n%s", zOptions); fossil_print("\nfossil settings:\n\n"); command_list(CMDFLAG_SETTING, verboseFlag, useHtml); fossil_print("\nfossil web pages:\n\n"); command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); fossil_print("\nfossil test commands (unsupported):\n\n"); command_list(CMDFLAG_TEST, verboseFlag, useHtml); fossil_print("\n"); version_cmd(); return; } else if( find_option("everything","e",0) ){ display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | CMDFLAG_SETTING | CMDFLAG_TEST, useHtml, 0); return; } verify_all_options(); if( g.argc<3 ){ z = g.argv[0]; fossil_print( "Usage: %s help TOPIC\n" "Try \"%s help help\" or \"%s help -a\" for more options\n" "Frequently used commands:\n", z, z, z); command_list(CMDFLAG_1ST_TIER,verboseFlag,useHtml); if( !verboseFlag ) version_cmd(); return; } zTopic = g.argv[2]; isPage = ('/' == zTopic[0]) ? 1 : 0; if(isPage){ zCmdOrPage = "page"; }else if( commandsFlag ){ mask = CMDFLAG_COMMAND; zCmdOrPage = "command"; }else{ zCmdOrPage = "command or setting"; } rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); if( rc ){ int i, n; const char *az[5]; if( rc==1 ){ fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); }else{ fossil_print("ambiguous %s prefix: %s\n", zCmdOrPage, g.argv[2]); } fossil_print("Did you mean one of these TOPICs:\n"); n = dispatch_approx_match(g.argv[2], 5, az); for(i=0; i<n; i++){ fossil_print(" * %s\n", az[i]); } fossil_print("Also consider using:\n"); fossil_print(" fossil help TOPIC ;# show help on TOPIC\n"); fossil_print(" fossil help -a ;# show all commands\n"); fossil_print(" fossil help -w ;# show all web-pages\n"); fossil_print(" fossil help -s ;# show all settings\n"); fossil_print(" fossil help -o ;# show global options\n"); fossil_exit(1); } z = pCmd->zHelp; if( z==0 ){ fossil_fatal("no help available for the %s %s", pCmd->zName, zCmdOrPage); } if( pCmd->eCmdFlags & CMDFLAG_SETTING ){ const Setting *pSetting = db_find_setting(pCmd->zName, 0); char *zDflt = 0; if( pSetting!=0 && pSetting->def!=0 && *pSetting->def!=0 ){ zDflt = mprintf(" (default: %s)", pSetting->def); } fossil_print("Setting: \"%s\"%s%s\n\n", pCmd->zName, zDflt!=0 ? zDflt : "", (pCmd->eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? " (versionable)" : "" ); fossil_free(zDflt); } blob_init(&txt, 0, 0); if( useHtml ){ help_to_html(z, &txt); }else{ help_to_text(z, &txt); } fossil_print("%s\n", blob_str(&txt)); blob_reset(&txt); } /* ** Return a pointer to the setting information array. ** ** This routine provides access to the aSetting2[] array which is created ** by the mkindex utility program and included with <page_index.h>. */ const Setting *setting_info(int *pnCount){ if( pnCount ) *pnCount = (int)(sizeof(aSetting)/sizeof(aSetting[0])) - 1; return aSetting; } /***************************************************************************** ** A virtual table for accessing the information in aCommand[], and ** especially the help-text */ /* helptextVtab_vtab is a subclass of sqlite3_vtab which is ** underlying representation of the virtual table */ typedef struct helptextVtab_vtab helptextVtab_vtab; struct helptextVtab_vtab { sqlite3_vtab base; /* Base class - must be first */ /* Add new fields here, as necessary */ }; /* helptextVtab_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct helptextVtab_cursor helptextVtab_cursor; struct helptextVtab_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ /* Insert new fields here. For this helptextVtab we only keep track ** of the rowid */ sqlite3_int64 iRowid; /* The rowid */ }; /* ** The helptextVtabConnect() method is invoked to create a new ** helptext virtual table. ** ** Think of this routine as the constructor for helptextVtab_vtab objects. ** ** All this routine needs to do is: ** ** (1) Allocate the helptextVtab_vtab object and initialize all fields. ** ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the ** result set of queries against the virtual table will look like. */ static int helptextVtabConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ helptextVtab_vtab *pNew; int rc; rc = sqlite3_declare_vtab(db, "CREATE TABLE x(name,type,flags,helptext,formatted,html)" ); if( rc==SQLITE_OK ){ pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); } return rc; } /* ** This method is the destructor for helptextVtab_vtab objects. */ static int helptextVtabDisconnect(sqlite3_vtab *pVtab){ helptextVtab_vtab *p = (helptextVtab_vtab*)pVtab; sqlite3_free(p); return SQLITE_OK; } /* ** Constructor for a new helptextVtab_cursor object. */ static int helptextVtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ helptextVtab_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Destructor for a helptextVtab_cursor. */ static int helptextVtabClose(sqlite3_vtab_cursor *cur){ helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur; sqlite3_free(pCur); return SQLITE_OK; } /* ** Advance a helptextVtab_cursor to its next row of output. */ static int helptextVtabNext(sqlite3_vtab_cursor *cur){ helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur; pCur->iRowid++; return SQLITE_OK; } /* ** Return values of columns for the row at which the helptextVtab_cursor ** is currently pointing. */ static int helptextVtabColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur; const CmdOrPage *pPage = aCommand + pCur->iRowid; switch( i ){ case 0: /* name */ sqlite3_result_text(ctx, pPage->zName, -1, SQLITE_STATIC); break; case 1: { /* type */ const char *zType = 0; if( pPage->eCmdFlags & CMDFLAG_COMMAND ){ zType = "command"; }else if( pPage->eCmdFlags & CMDFLAG_WEBPAGE ){ zType = "webpage"; }else if( pPage->eCmdFlags & CMDFLAG_SETTING ){ zType = "setting"; } sqlite3_result_text(ctx, zType, -1, SQLITE_STATIC); break; } case 2: /* flags */ sqlite3_result_int(ctx, pPage->eCmdFlags); break; case 3: /* helptext */ sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC); break; case 4: { /* formatted */ Blob txt; blob_init(&txt, 0, 0); help_to_text(pPage->zHelp, &txt); sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); break; } case 5: { /* formatted */ Blob txt; blob_init(&txt, 0, 0); help_to_html(pPage->zHelp, &txt); sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); break; } } return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. */ static int helptextVtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur; *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int helptextVtabEof(sqlite3_vtab_cursor *cur){ helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur; return pCur->iRowid>=MX_COMMAND; } /* ** This method is called to "rewind" the helptextVtab_cursor object back ** to the first row of output. This method is always called at least ** once prior to any call to helptextVtabColumn() or helptextVtabRowid() or ** helptextVtabEof(). */ static int helptextVtabFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ helptextVtab_cursor *pCur = (helptextVtab_cursor *)pVtabCursor; pCur->iRowid = 1; return SQLITE_OK; } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. */ static int helptextVtabBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ pIdxInfo->estimatedCost = (double)MX_COMMAND; pIdxInfo->estimatedRows = MX_COMMAND; return SQLITE_OK; } /* ** This following structure defines all the methods for the ** virtual table. */ static sqlite3_module helptextVtabModule = { /* iVersion */ 0, /* xCreate */ 0, /* Helptext is eponymous and read-only */ /* xConnect */ helptextVtabConnect, /* xBestIndex */ helptextVtabBestIndex, /* xDisconnect */ helptextVtabDisconnect, /* xDestroy */ 0, /* xOpen */ helptextVtabOpen, /* xClose */ helptextVtabClose, /* xFilter */ helptextVtabFilter, /* xNext */ helptextVtabNext, /* xEof */ helptextVtabEof, /* xColumn */ helptextVtabColumn, /* xRowid */ helptextVtabRowid, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ 0, /* xIntegrity */ 0 }; /* ** Register the helptext virtual table */ int helptext_vtab_register(sqlite3 *db){ int rc = sqlite3_create_module(db, "helptext", &helptextVtabModule, 0); return rc; } /* End of the helptext virtual table ******************************************************************************/ |
Changes to src/doc.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2007 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".) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* ** Copyright (c) 2007 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/ |
︙ | ︙ | |||
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 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 | /* ** 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; /* A table of mimetypes based on file content prefixes */ static const struct { const char *z; /* Identifying file text */ const unsigned char sz1; /* Length of the prefix */ const unsigned char of2; /* Offset to the second segment */ const unsigned char sz2; /* Size of the second segment */ const unsigned char mn; /* Minimum size of input */ const char *zMimetype; /* The corresponding mimetype */ } aMime[] = { { "GIF87a", 6, 0, 0, 6, "image/gif" }, { "GIF89a", 6, 0, 0, 6, "image/gif" }, { "\211PNG\r\n\032\n", 8, 0, 0, 8, "image/png" }, { "\377\332\377", 3, 0, 0, 3, "image/jpeg" }, { "\377\330\377", 3, 0, 0, 3, "image/jpeg" }, { "RIFFWAVEfmt", 4, 8, 7, 15, "sound/wav" }, }; if( !looks_like_binary(pBlob) ) { return 0; /* Plain text */ } x = (const unsigned char*)blob_buffer(pBlob); n = blob_size(pBlob); for(i=0; i<count(aMime); i++){ if( n<aMime[i].mn ) continue; if( memcmp(x, aMime[i].z, aMime[i].sz1)!=0 ) continue; if( aMime[i].sz2 && memcmp(x+aMime[i].of2, aMime[i].z+aMime[i].sz1, aMime[i].sz2)!=0 ){ continue; } 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 mimetype. */ 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" }, { "bmp", 3, "image/bmp" }, { "bz2", 3, "application/x-bzip2" }, { "bzip", 4, "application/x-bzip" }, { "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" }, { "cs", 2, "text/plain" }, { "csh", 3, "application/x-csh" }, { "css", 3, "text/css" }, { "csv", 3, "text/csv" }, { "dcr", 3, "application/x-director" }, { "deb", 3, "application/x-debian-package" }, { "dib", 3, "image/bmp" }, { "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" }, { "ico", 3, "image/vnd.microsoft.icon" }, { "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, "text/javascript" }, /* application/javascript is commonly used for JS, but the ** spec says text/javascript is correct: ** https://html.spec.whatwg.org/multipage/scripting.html ** #scriptingLanguages:javascript-mime-type */ { "json", 4, "application/json" }, { "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, "text/plain" }, { "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" }, { "mjs", 3, "text/javascript" /*ES6 module*/ }, { "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" }, { "n", 1, "text/plain" }, { "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" }, { "pikchr", 6, "text/x-pikchr" }, { "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" }, { "vb", 2, "text/plain" }, { "vcd", 3, "application/x-cdlink" }, { "vda", 3, "application/vda" }, { "viv", 3, "video/vnd.vivo" }, { "vivo", 4, "video/vnd.vivo" }, { "vrml", 4, "model/vrml" }, { "wasm", 4, "application/wasm" }, { "wav", 3, "audio/x-wav" }, { "wax", 3, "audio/x-ms-wax" }, { "webp", 4, "image/webp" }, { "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<count(aMime); i++){ if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){ fossil_panic("mimetypes out of sequence: %s before %s", aMime[i-1].zSuffix, aMime[i].zSuffix); } } } /* ** Looks in the contents of the "mimetypes" setting for a suffix ** matching zSuffix. If found, it returns the configured value ** in memory owned by the app (i.e. do not free() it), else it ** returns 0. ** ** The mimetypes setting is expected to be a list of file extensions ** and mimetypes, with one such mapping per line. A leading '.' on ** extensions is permitted for compatibility with lists imported from ** other tools which require them. */ static const char *mimetype_from_name_custom(const char *zSuffix){ static char * zList = 0; static char const * zEnd = 0; static int once = 0; char * z; int tokenizerState /* 0=expecting a key, 1=skip next token, ** 2=accept next token */; if(once==0){ once = 1; zList = db_get("mimetypes",0); if(zList==0){ return 0; } /* Transform zList to simplify the main loop: replace non-newline spaces with NUL bytes. */ zEnd = zList + strlen(zList); for(z = zList; z<zEnd; ++z){ if('\n'==*z) continue; else if(fossil_isspace(*z)){ *z = 0; } } }else if(zList==0){ return 0; } tokenizerState = 0; z = zList; while( z<zEnd ){ if(*z==0){ ++z; continue; } else if('\n'==*z){ if(2==tokenizerState){ /* We were expecting a value for a successful match here, but got no value. Bail out. */ break; }else{ /* May happen on malformed inputs. Skip this record. */ tokenizerState = 0; ++z; continue; } } switch(tokenizerState){ case 0:{ /* This is a file extension */ static char * zCase = 0; if('.'==*z){ /*ignore an optional leading dot, for compatibility with some external mimetype lists*/; if(++z==zEnd){ break; } } if(zCase<z){ /*we have not yet case-folded this section: lower-case it*/ for(zCase = z; zCase<zEnd && *zCase!=0; ++zCase){ if(!(0x80 & *zCase)){ *zCase = (char)fossil_tolower(*zCase); } } } if(strcmp(z,zSuffix)==0){ tokenizerState = 2 /* Match: accept the next value. */; }else{ tokenizerState = 1 /* No match: skip the next value. */; } z += strlen(z); break; } case 1: /* This is a value, but not a match. Skip it. */ z += strlen(z); break; case 2: /* This is the value which matched the previous key. */; return z; default: assert(!"cannot happen - invalid tokenizerState value."); } } return 0; } /* ** Emit Javascript which applies (or optionally can apply) to both the ** /doc and /wiki pages. None of this implements required ** functionality, just nice-to-haves. Any calls after the first are ** no-ops. */ void document_emit_js(void){ static int once = 0; if(0==once++){ builtin_fossil_js_bundle_or("pikchr", NULL); style_script_begin(__FILE__,__LINE__); CX("window.addEventListener('load', " "()=>window.fossil.pikchr.addSrcView(), " "false);\n"); style_script_end(); } } /* ** Guess the mimetype 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<(int)sizeof(zSuffix)-1 ){ sqlite3_snprintf(sizeof(zSuffix), zSuffix, "%s", z); for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]); z = mimetype_from_name_custom(zSuffix); if(z!=0){ return z; } first = 0; last = count(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; }else{ first = i+1; } } } return "application/x-fossil-artifact"; } /* ** COMMAND: test-mimetype ** ** Usage: %fossil test-mimetype FILENAME... ** ** Return the deduced mimetype for each file listed. ** ** 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(); db_find_and_open_repository(0, 0); 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; char *zCustomList = 0; /* value of the mimetypes setting */ int nCustomEntries = 0; /* number of entries in the mimetypes ** setting */ mimetype_verify(); style_header("Mimetype List"); @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename @ suffixes and the following tables to guess at the appropriate mimetype @ for each document. Mimetypes may be customized and overridden using @ <a href="%R/help?cmd=mimetypes">the mimetypes config setting</a>.</p> zCustomList = db_get("mimetypes",0); if( zCustomList!=0 ){ Blob list, entry, key, val; @ <h1>Repository-specific mimetypes</h1> @ <p>The following extension-to-mimetype mappings are defined via @ the <a href="%R/help?cmd=mimetypes">mimetypes setting</a>.</p> @ <table class='sortable mimetypetable' border=1 cellpadding=0 \ @ data-column-types='tt' data-init-sort='0'> @ <thead> @ <tr><th>Suffix<th>Mimetype @ </thead> @ <tbody> blob_set(&list, zCustomList); while( blob_line(&list, &entry)>0 ){ const char *zKey; if( blob_token(&entry, &key)==0 ) continue; if( blob_token(&entry, &val)==0 ) continue; zKey = blob_str(&key); if( zKey[0]=='.' ) zKey++; @ <tr><td>%h(zKey)<td>%h(blob_str(&val))</tr> nCustomEntries++; } fossil_free(zCustomList); if( nCustomEntries==0 ){ /* This can happen if the option is set to an empty/space-only ** value. */ @ <tr><td colspan="2"><em>none</em></tr> } @ </tbody></table> } @ <h1>Default built-in mimetypes</h1> if(nCustomEntries>0){ @ <p>Entries starting with an exclamation mark <em><strong>!</strong></em> @ are overwritten by repository-specific settings.</p> } @ <table class='sortable mimetypetable' border=1 cellpadding=0 \ @ data-column-types='tt' data-init-sort='1'> @ <thead> @ <tr><th>Suffix<th>Mimetype @ </thead> @ <tbody> for(i=0; i<count(aMime); i++){ const char *zFlag = ""; if(nCustomEntries>0 && mimetype_from_name_custom(aMime[i].zSuffix)!=0){ zFlag = "<em><strong>!</strong></em> "; } @ <tr><td>%s(zFlag)%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr> } @ </tbody></table> style_table_sorter(); style_finish_page(); } /* ** 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]=='>' ) break; 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 ){ /* The text argument to data-title="" will have had any characters that ** are special to HTML encoded. We need to decode these before turning ** the text into a title, as the title text will be reencoded later */ char *zTitle = mprintf("%.*s", nValue, zValue); int i; for(i=0; fossil_isspace(zTitle[i]); i++){} html_to_plaintext(zTitle+i, pTitle); fossil_free(zTitle); 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 writable; int rid; /* The RID of the file being loaded */ if( db_is_protected(PROTECT_READONLY) || !db_is_writeable("repository") ){ writable = 0; }else{ writable = 1; } if( writable ){ db_end_transaction(0); db_begin_write(); } if( !db_table_exists("repository", "vcache") || !writable ){ db_multi_exec( "CREATE %s 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", writable ? "" : "TEMPORARY" ); } 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; } /* ** Check to verify that z[i] is contained within HTML markup. ** ** This works by looking backwards in the string for the most recent ** '<' or '>' character. If a '<' is found first, then we assume that ** z[i] is within markup. If a '>' is seen or neither character is seen, ** then z[i] is not within markup. */ static int isWithinHtmlMarkup(const char *z, int i){ while( i>=0 && z[i]!='>' && z[i]!='<' ){ i--; } return z[i]=='<'; } /* ** Check to see if z[i] is contained within an href='...' of markup. */ static int isWithinHref(const char *z, int i){ while( i>5 && !fossil_isspace(z[i]) && z[i]!='\'' && z[i]!='"' && z[i]!='>' ){ i--; } if( i<=6 ) return 0; if( z[i]!='\'' && z[i]!='\"' ) return 0; if( strncmp(&z[i-5],"href=",5)!=0 ) return 0; if( !fossil_isspace(z[i-6]) ) return 0; return 1; } /* ** Transfer content to the output. During the transfer, when text of ** the following form is seen: ** ** href="$ROOT/..." ** action="$ROOT/..." ** href=".../doc/$CURRENT/..." ** ** Convert $ROOT to the root URI of the repository, and $CURRENT to the ** version number of the /doc/ document currently being displayed (if any). ** Allow ' in place of " and any case for href or action. ** ** Efforts are made to limit this translation to cases where the text is ** fully contained with an HTML markup element. */ void convert_href_and_output(Blob *pIn){ int i, base; int n = blob_size(pIn); char *z = blob_buffer(pIn); for(base=0, i=7; i<n; i++){ if( z[i]=='$' && strncmp(&z[i],"$ROOT/", 6)==0 && (z[i-1]=='\'' || z[i-1]=='"') && i-base>=9 && ((fossil_strnicmp(&z[i-6],"href=",5)==0 && fossil_isspace(z[i-7])) || (fossil_strnicmp(&z[i-8],"action=",7)==0 && fossil_isspace(z[i-9])) ) && isWithinHtmlMarkup(z, i-6) ){ blob_append(cgi_output_blob(), &z[base], i-base); blob_appendf(cgi_output_blob(), "%R"); base = i+5; }else if( z[i]=='$' && strncmp(&z[i-5],"/doc/$CURRENT/", 11)==0 && isWithinHref(z,i-5) && isWithinHtmlMarkup(z, i-5) && strncmp(g.zPath, "doc/",4)==0 ){ int j; for(j=7; g.zPath[j] && g.zPath[j]!='/'; j++){} blob_append(cgi_output_blob(), &z[base], i-base); blob_appendf(cgi_output_blob(), "%.*s", j-4, g.zPath+4); base = i+8; } } blob_append(cgi_output_blob(), &z[base], i-base); } /* ** Render a document as the reply to the HTTP request. The body ** of the document is contained in pBody. The body might be binary. ** The mimetype is in zMimetype. */ void document_render( Blob *pBody, /* Document content */ const char *zMime, /* MIME-type */ const char *zDefaultTitle, /* Default title */ const char *zFilename /* Name of the file being rendered */ ){ Blob title; int isPopup = P("popup")!=0; blob_init(&title,0,0); if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ Blob tail; style_adunit_config(ADUNIT_RIGHT_OK); if( wiki_find_title(pBody, &title, &tail) ){ if( !isPopup ) style_header("%s", blob_str(&title)); wiki_convert(&tail, 0, WIKI_BUTTONS); }else{ if( !isPopup ) style_header("%s", zDefaultTitle); wiki_convert(pBody, 0, WIKI_BUTTONS); } if( !isPopup ){ document_emit_js(); style_finish_page(); } }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ Blob tail = BLOB_INITIALIZER; markdown_to_html(pBody, &title, &tail); if( !isPopup ){ if( blob_size(&title)>0 ){ style_header("%s", blob_str(&title)); }else{ style_header("%s", zDefaultTitle); } } convert_href_and_output(&tail); if( !isPopup ){ document_emit_js(); style_finish_page(); } }else if( fossil_strcmp(zMime, "text/plain")==0 ){ style_header("%s", zDefaultTitle); @ <blockquote><pre> @ %h(blob_str(pBody)) @ </pre></blockquote> document_emit_js(); style_finish_page(); }else if( fossil_strcmp(zMime, "text/html")==0 && doc_is_embedded_html(pBody, &title) ){ if( blob_size(&title)==0 ) blob_append(&title,zFilename,-1); if( !isPopup ) style_header("%s", blob_str(&title)); convert_href_and_output(pBody); if( !isPopup ){ document_emit_js(); style_finish_page(); } }else if( fossil_strcmp(zMime, "text/x-pikchr")==0 ){ style_adunit_config(ADUNIT_RIGHT_OK); if( !isPopup ) style_header("%s", zDefaultTitle); wiki_render_by_mimetype(pBody, zMime); if( !isPopup ) style_finish_page(); #ifdef FOSSIL_ENABLE_TH1_DOCS }else if( Th_AreDocsEnabled() && fossil_strcmp(zMime, "application/x-th1")==0 ){ int raw = P("raw")!=0; if( !raw ){ Blob tail; blob_zero(&tail); if( wiki_find_title(pBody, &title, &tail) ){ style_header("%s", blob_str(&title)); Th_Render(blob_str(&tail)); blob_reset(&tail); }else{ style_header("%h", zFilename); Th_Render(blob_str(pBody)); } }else{ Th_Render(blob_str(pBody)); } if( !raw ){ document_emit_js(); style_finish_page(); } #endif }else{ fossil_free(style_csp(1)); cgi_set_content_type(zMime); cgi_set_content(pBody); } } /* ** WEBPAGE: uv ** WEBPAGE: doc ** URL: /uv/FILE ** URL: /doc/CHECKIN/FILE ** ** CHECKIN can be either tag or hash prefix or timestamp identifying a ** particular check-in, 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" ** ** "latest" means use the most recent check-in for the document ** regardless of what branch it occurs on. ** ** 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. For /uv, FILE ** can also be the hash of the unversioned 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. Some commands like "fossil ui", "fossil server", ** and "fossil http" accept an argument "--ckout-alias NAME" when allows ** NAME to be understood as an alias for "ckout". On a site with many ** embedded hyperlinks to /doc/trunk/... one can run with "--ckout-alias trunk" ** to simulate what the pending changes will look like after they are ** checked in. The NAME alias is stored in g.zCkoutAlias. ** ** The file extension is used to decide how to render the file. ** ** If FILE ends in "/" then the names "FILE/index.html", "FILE/index.wiki", ** and "FILE/index.md" are tried in that order. If the binary was compiled ** with TH1 embedded documentation support and the "th1-docs" setting is ** enabled, the name "FILE/index.th1" is also tried. 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. ** ** If the file's mimetype is "text/x-fossil-wiki" or "text/x-markdown" ** then headers and footers are added. If the document has mimetype ** text/html then headers and footers are usually not added. However, ** if a "text/html" document begins with the following div: ** ** <div class='fossil-doc' data-title='TEXT'> ** ** then headers and footers are supplied. The optional data-title field ** specifies the title of the document in that case. ** ** For fossil-doc documents and for markdown documents, text of the ** form: "href='$ROOT/" or "action='$ROOT" has the $ROOT name expanded ** to the top-level of the repository. */ void doc_page(void){ const char *zName = 0; /* 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 */ char *zPathSuffix = ""; /* Text to append to g.zPath */ 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 */ int isUV = g.zPath[0]=='u'; /* True for /uv. False for /doc */ const char *zDfltTitle; static const char *const azSuffix[] = { "index.html", "index.wiki", "index.md" #ifdef FOSSIL_ENABLE_TH1_DOCS , "index.th1" #endif }; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_set_current_feature("doc"); blob_init(&title, 0, 0); zDfltTitle = isUV ? "" : "Documentation"; db_begin_transaction(); while( rid==0 && (++nMiss)<=count(azSuffix) ){ zName = P("name"); if( isUV ){ if( zName==0 ) zName = "index.wiki"; i = 0; }else{ if( zName==0 || zName[0]==0 ) zName = "tip/index.wiki"; for(i=0; zName[i] && zName[i]!='/'; i++){} zCheckin = mprintf("%.*s", i, zName); if( fossil_strcmp(zCheckin,"ckout")==0 && g.localOpen==0 ){ zCheckin = "tip"; }else if( fossil_strcmp(zCheckin,"latest")==0 ){ char *zNewCkin = db_text(0, "SELECT uuid FROM blob, mlink, event, filename" " WHERE filename.name=%Q" " AND mlink.fnid=filename.fnid" " AND blob.rid=mlink.mid" " AND event.objid=mlink.mid" " ORDER BY event.mtime DESC LIMIT 1", zName + i + 1); if( zNewCkin ) zCheckin = zNewCkin; } } if( nMiss==count(azSuffix) ){ zName = "404.md"; zDfltTitle = "Not Found"; }else if( zName[i]==0 ){ assert( nMiss>=0 && nMiss<count(azSuffix) ); zName = azSuffix[nMiss]; }else if( !isUV ){ zName += i; } while( zName[0]=='/' ){ zName++; } if( isUV ){ zPathSuffix = fossil_strdup(zName); }else{ zPathSuffix = mprintf("%s/%s", zCheckin, zName); } if( nMiss==0 ) zOrigName = zName; if( !file_is_simple_pathname(zName, 1) ){ if( sqlite3_strglob("*/", zName)==0 ){ assert( nMiss>=0 && nMiss<count(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( isUV ){ if( db_table_exists("repository","unversioned") ){ rid = unversioned_content(zName, &filebody); if( rid==1 ){ Stmt q; db_prepare(&q, "SELECT hash, mtime FROM unversioned" " WHERE name=%Q", zName); if( db_step(&q)==SQLITE_ROW ){ etag_check(ETAG_HASH, db_column_text(&q,0)); etag_last_modified(db_column_int64(&q,1)); } db_finalize(&q); }else if( rid==2 ){ zName = db_text(zName, "SELECT name FROM unversioned WHERE hash=%Q", zName); g.isConst = 1; } zDfltTitle = zName; } }else if( fossil_strcmp(zCheckin,"ckout")==0 || fossil_strcmp(zCheckin,g.zCkoutAlias)==0 ){ /* Read from the local check-out */ char *zFullpath; db_must_be_within_tree(); zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); if( file_isfile(zFullpath, RepoFILE) && blob_read_from_file(&filebody, zFullpath, RepoFILE)>0 ){ rid = 1; /* Fake RID just to get the loop to end */ } fossil_free(zFullpath); }else{ vid = symbolic_name_to_rid(zCheckin, "ci"); rid = vid>0 ? doc_load_content(vid, zName, &filebody) : 0; } } g.zPath = mprintf("%s/%s", g.zPath, zPathSuffix); 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); if( vid ){ 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)); } cgi_check_for_malice(); document_render(&filebody, zMime, zDfltTitle, zName); if( nMiss>=count(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); if( isUV && P("name")==0 ){ uvlist_page(); return; } 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_finish_page(); 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 ** of the header. */ void logo_page(void){ Blob logo; char *zMime; etag_check(ETAG_CONFIG, 0); zMime = db_get("logo-mimetype", "image/gif"); blob_zero(&logo); db_blob(&logo, "SELECT value FROM config WHERE name='logo-image'"); if( blob_size(&logo)==0 ){ blob_init(&logo, (char*)aLogo, sizeof(aLogo)); } cgi_set_content_type(zMime); cgi_set_content(&logo); } /* ** The default background image: a 16x16 white GIF */ static const unsigned char aBackground[] = { 71, 73, 70, 56, 57, 97, 16, 0, 16, 0, 240, 0, 0, 255, 255, 255, 0, 0, 0, 33, 254, 4, 119, 105, 115, 104, 0, 44, 0, 0, 0, 0, 16, 0, 16, 0, 0, 2, 14, 132, 143, 169, 203, 237, 15, 163, 156, 180, 218, 139, 179, 62, 5, 0, 59, }; /* ** WEBPAGE: background ** ** Return the background image. If no background image is defined, a ** built-in 16x16 pixel white GIF is returned. */ void background_page(void){ Blob bgimg; char *zMime; etag_check(ETAG_CONFIG, 0); zMime = db_get("background-mimetype", "image/gif"); blob_zero(&bgimg); db_blob(&bgimg, "SELECT value FROM config WHERE name='background-image'"); if( blob_size(&bgimg)==0 ){ blob_init(&bgimg, (char*)aBackground, sizeof(aBackground)); } cgi_set_content_type(zMime); cgi_set_content(&bgimg); } /* ** WEBPAGE: favicon.ico ** ** Return the configured "favicon.ico" image. If no "favicon.ico" image ** is defined, the returned image is for the Fossil lizard icon. ** ** The intended use case here is to supply an icon for the "fossil ui" ** command. For a permanent website, the recommended process is for ** the admin to set up a project-specific icon and reference that icon ** in the HTML header using a line like: ** ** <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/> ** */ void favicon_page(void){ Blob icon; char *zMime; etag_check(ETAG_CONFIG, 0); zMime = db_get("icon-mimetype", "image/gif"); blob_zero(&icon); db_blob(&icon, "SELECT value FROM config WHERE name='icon-image'"); if( blob_size(&icon)==0 ){ blob_init(&icon, (char*)aLogo, sizeof(aLogo)); } cgi_set_content_type(zMime); cgi_set_content(&icon); } /* ** WEBPAGE: docsrch ** ** Search for documents that match a user-supplied full-text search pattern. ** If no pattern is specified (by the s= query parameter) then the user ** is prompted to enter a search string. ** ** Query parameters: ** ** s=PATTERN Search for PATTERN */ void doc_search_page(void){ const int isSearch = P("s")!=0; login_check_credentials(); style_header("Document Search%s", isSearch ? " Results" : ""); cgi_check_for_malice(); search_screen(SRCH_DOC, 0); style_finish_page(); } |
Changes to src/encode.c.
︙ | ︙ | |||
21 22 23 24 25 26 27 | #include "encode.h" /* ** Make the given string safe for HTML by converting every "<" into "<", ** every ">" into ">" and every "&" into "&". Return a pointer ** to a new string obtained from malloc(). ** | | | | | > | | | | | | | > | | > > > | > | > | | | | > > > > > > > < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | > > < | < > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include "encode.h" /* ** Make the given string safe for HTML by converting every "<" into "<", ** every ">" into ">" and every "&" into "&". Return a pointer ** to a new string obtained from malloc(). ** ** We also encode " as " and ' as ' so they can appear as an argument ** to markup. */ char *htmlize(const char *z, int n){ unsigned char c; int i = 0; int count = 0; unsigned char *zOut; const unsigned char *zIn = (const unsigned char*)z; if( n<0 ) n = strlen(z); while( i<n ){ switch( zIn[i] ){ case '<': count += 3; break; case '>': count += 3; break; case '&': count += 4; break; case '"': count += 5; break; case '\'': count += 4; break; case 0: n = i; break; } i++; } i = 0; zOut = fossil_malloc( count+n+1 ); if( count==0 ){ memcpy(zOut, zIn, n); zOut[n] = 0; return (char*)zOut; } while( n-->0 ){ c = *(zIn++); 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; case '\'': zOut[i++] = '&'; zOut[i++] = '#'; zOut[i++] = '3'; zOut[i++] = '9'; zOut[i++] = ';'; break; default: zOut[i++] = c; break; } } zOut[i] = 0; return (char*)zOut; } /* ** Append HTML-escaped text to a Blob. */ void htmlize_to_blob(Blob *p, const char *zIn, int n){ int c, i, j; if( n<0 ) n = strlen(zIn); for(i=j=0; i<n; i++){ c = zIn[i]; switch( c ){ case '<': if( j<i ) blob_append(p, zIn+j, i-j); blob_append(p, "<", 4); j = i+1; break; case '>': if( j<i ) blob_append(p, zIn+j, i-j); blob_append(p, ">", 4); j = i+1; break; case '&': if( j<i ) blob_append(p, zIn+j, i-j); blob_append(p, "&", 5); j = i+1; break; case '"': if( j<i ) blob_append(p, zIn+j, i-j); blob_append(p, """, 6); j = i+1; break; case '\'': if( j<i ) blob_append(p, zIn+j, i-j); blob_append(p, "'", 5); j = i+1; break; case '\r': if( j<i ) blob_append(p, zIn+j, i-j); blob_append(p, " ", 1); j = i+1; break; } } if( j<i ) blob_append(p, zIn+j, i-j); } /* ** Encode a string for HTTP. This means converting lots of ** characters into the "%HH" where H is a hex digit. It also ** means converting spaces to "+". ** ** This is the opposite of DeHttpizeString below. */ static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ int c; int i = 0; int count = 0; char *zOut; # define IsSafeChar(X) \ (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ || (X)=='~' || (X)=='-' || (X)=='_' \ || (!encodeSlash && ((X)=='/' || (X)==':'))) if( zIn==0 ) return 0; if( n<0 ) n = strlen(zIn); while( i<n && (c = zIn[i])!=0 ){ if( IsSafeChar(c) || c==' ' ){ count++; }else{ count += 3; } i++; } i = 0; zOut = fossil_malloc( count+1 ); while( n-->0 && (c = *zIn)!=0 ){ if( IsSafeChar(c) ){ zOut[i++] = c; }else if( c==' ' ){ zOut[i++] = '+'; }else{ zOut[i++] = '%'; zOut[i++] = "0123456789ABCDEF"[(c>>4)&0xf]; zOut[i++] = "0123456789ABCDEF"[c&0xf]; } zIn++; } zOut[i] = 0; #undef IsSafeChar return zOut; } /* ** 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 hexadecimal ** representation of the character. The "/" character is encoded ** as "%2F". */ char *httpize(const char *z, int n){ return EncodeHttp(z, n, 1); } /* ** 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); } /* ** If input string does not contain quotes (neither ' nor ") ** then return the argument itself. Otherwise return a newly allocated ** copy of input with all quotes %-escaped. */ const char* escape_quotes(const char *zIn){ char *zRet, *zOut; size_t i, n = 0; for(i=0; zIn[i]; i++){ if( zIn[i]== '"' || zIn[i]== '\'' ) n++; } if( !n ) return zIn; zRet = zOut = fossil_malloc( i + 2*n + 1 ); for(i=0; zIn[i]; i++){ if( zIn[i]=='"' ){ *(zOut++) = '%'; *(zOut++) = '2'; *(zOut++) = '2'; }else if( zIn[i]=='\'' ){ *(zOut++) = '%'; *(zOut++) = '2'; *(zOut++) = '7'; }else{ *(zOut++) = zIn[i]; } } *zOut = 0; return zRet; } /* ** Convert a single HEX digit to an integer */ static int AsciiToHex(int c){ if( c>='a' && c<='f' ){ c += 10 - 'a'; |
︙ | ︙ | |||
231 232 233 234 235 236 237 | if( nIn<0 ) nIn = strlen(zIn); for(i=n=0; i<nIn; i++){ c = zIn[i]; if( c==0 || c==' ' || c=='\n' || c=='\t' || c=='\r' || c=='\f' || c=='\v' || c=='\\' ) n++; } n += nIn; | | | | 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 | if( nIn<0 ) nIn = strlen(zIn); for(i=n=0; i<nIn; i++){ c = zIn[i]; if( c==0 || c==' ' || c=='\n' || c=='\t' || c=='\r' || c=='\f' || c=='\v' || c=='\\' ) n++; } n += nIn; zOut = fossil_malloc( n+1 ); if( zOut ){ for(i=j=0; i<nIn; i++){ int c = zIn[i]; if( c==0 ){ zOut[j++] = '\\'; zOut[j++] = '0'; }else if( c=='\\' ){ zOut[j++] = '\\'; zOut[j++] = '\\'; }else if( fossil_isspace(c) ){ zOut[j++] = '\\'; switch( c ){ case '\n': c = 'n'; break; case ' ': c = 's'; break; case '\t': c = 't'; break; case '\r': c = 'r'; break; case '\v': c = 'v'; break; |
︙ | ︙ | |||
266 267 268 269 270 271 272 | } /* ** Decode a fossilized string in-place. */ void defossilize(char *z){ int i, j, c; | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | < < | | < < < < < < > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < | | > | | | | | | | | | | | | | | | | | | | | 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 | } /* ** Decode a fossilized string in-place. */ void defossilize(char *z){ int i, j, c; char *zSlash = strchr(z, '\\'); if( zSlash==0 ) return; i = zSlash - z; for(j=i; (c=z[i])!=0; i++){ if( c=='\\' && z[i+1] ){ i++; switch( z[i] ){ case 'n': c = '\n'; break; case 's': c = ' '; break; case 't': c = '\t'; break; case 'r': c = '\r'; break; case 'v': c = '\v'; break; case 'f': c = '\f'; break; case '0': c = 0; break; case '\\': c = '\\'; break; default: c = z[i]; break; } } z[j++] = c; } if( z[j] ) z[j] = 0; } /* ** The *pz variable points to a UTF8 string. Read the next character ** off of that string and return its codepoint value. Advance *pz to the ** next character */ u32 fossil_utf8_read( const unsigned char **pz /* Pointer to string from which to read char */ ){ unsigned int c; /* ** This lookup table is used to help decode the first byte of ** a multi-byte UTF8 character. */ static const unsigned char utf8Trans1[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, }; c = *((*pz)++); if( c>=0xc0 ){ c = utf8Trans1[c-0xc0]; while( (*(*pz) & 0xc0)==0x80 ){ c = (c<<6) + (0x3f & *((*pz)++)); } if( c<0x80 || (c&0xFFFFF800)==0xD800 || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } } return c; } /* ** Encode a UTF8 string as a JSON string literal (with or without the ** surrounding "...", depending on whether the 2nd argument is true or ** false) and return a pointer to the encoding. Space to hold the ** encoding is obtained from fossil_malloc() and must be freed by the ** caller. ** ** If nOut is not NULL then it is assigned to the length, in bytes, of ** the returned string (its strlen(), not counting the terminating ** NUL). */ char *encode_json_string_literal(const char *zStr, int fAddQuotes, int * nOut){ const unsigned char *z; char *zOut; u32 c; int n, i, j; z = (const unsigned char*)zStr; n = 0; while( (c = *(z++))!=0 ){ if( c=='\\' || c=='"' ){ n += 2; }else if( c<' ' ){ if( c=='\n' || c=='\r' ){ n += 2; }else{ n += 6; } }else{ n++; } } if(fAddQuotes){ n += 2; } zOut = fossil_malloc(n+1); if( zOut==0 ) return 0; z = (const unsigned char*)zStr; i = 0; if(fAddQuotes){ zOut[i++] = '"'; } while( (c = *(z++))!=0 ){ if( c=='\\' || c=='"' ){ zOut[i++] = '\\'; zOut[i++] = c; }else if( c<' ' ){ zOut[i++] = '\\'; if( c=='\n' ){ zOut[i++] = 'n'; }else if( c=='\r' ){ zOut[i++] = 'r'; }else{ zOut[i++] = 'u'; for(j=3; j>=0; j--){ zOut[i+j] = "0123456789abcdef"[c&0xf]; c >>= 4; } i += 4; } }else{ zOut[i++] = c; } } if(fAddQuotes){ zOut[i++] = '"'; } zOut[i] = 0; if(nOut!=0){ *nOut = i; } return zOut; } /* ** The characters used for HTTP base64 encoding. */ static unsigned char zBase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* ** Translate nData bytes of content from zData into ** ((nData+2)/3)*4) bytes of base64 encoded content and ** put the result in z64. Add a zero-terminator at the end. */ int translateBase64(const char *zData, int nData, char *z64){ int i, n; for(i=n=0; i+2<nData; i+=3){ z64[n++] = zBase[ (zData[i]>>2) & 0x3f ]; z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ]; z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) | ((zData[i+2]>>6) & 0x03) ]; z64[n++] = zBase[ zData[i+2] & 0x3f ]; } if( i+1<nData ){ z64[n++] = zBase[ (zData[i]>>2) & 0x3f ]; z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ]; z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) ]; z64[n++] = '='; }else if( i<nData ){ z64[n++] = zBase[ (zData[i]>>2) & 0x3f ]; z64[n++] = zBase[ ((zData[i]<<4) & 0x30) ]; z64[n++] = '='; z64[n++] = '='; } z64[n] = 0; return n; } /* ** 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(). */ char *encode64(const char *zData, int nData){ char *z64; if( nData<=0 ){ nData = strlen(zData); } z64 = fossil_malloc( (nData*4)/3 + 8 ); translateBase64(zData, nData, z64); 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); fossil_print("%s\n", z); free(z); } } /* Decode base64 text. Write the output into zData. The caller ** must ensure that zData is large enough. It is ok for z64 and ** zData to be the same buffer. In other words, it is ok to decode ** in-place. A zero terminator is always placed at the end of zData. */ void decodeBase64(const char *z64, int *pnByte, char *zData){ const unsigned char *zIn = (const unsigned char*)z64; int i, j, k; int x[4]; static int isInit = 0; static signed char trans[256]; if( !isInit ){ for(i=0; i<256; i++){ trans[i] = -1; } for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } isInit = 1; } for(j=k=0; zIn[0]; zIn++){ int v = trans[zIn[0]]; if( v>=0 ){ x[k++] = v; if( k==4 ){ zData[j++] = ((x[0]<<2) & 0xfc) | ((x[1]>>4) & 0x03); zData[j++] = ((x[1]<<4) & 0xf0) | ((x[2]>>2) & 0x0f); zData[j++] = ((x[2]<<6) & 0xc0) | (x[3] & 0x3f); k = 0; } } } if( k>=2 ){ zData[j++] = ((x[0]<<2) & 0xfc) | ((x[1]>>4) & 0x03); if( k==3 ){ zData[j++] = ((x[1]<<4) & 0xf0) | ((x[2]>>2) & 0x0f); } } zData[j] = 0; *pnByte = j; } /* ** This function treats its input as a base-64 string and returns the ** decoded value of that string. Characters of input that are not ** valid base-64 characters (such as spaces and newlines) are ignored. ** ** Space to hold the decoded string is obtained from malloc(). ** ** The number of bytes decoded is returned in *pnByte */ char *decode64(const char *z64, int *pnByte){ char *zData; int n64 = (int)strlen(z64); while( n64>0 && z64[n64-1]=='=' ) n64--; zData = fossil_malloc( (n64*3)/4 + 4 ); decodeBase64(z64, pnByte, zData); 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); fossil_print("%d: %s\n", n, z); fossil_free(z); } } /* ** The base-16 encoding using the following characters: ** ** 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++){ |
︙ | ︙ | |||
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 | /* ** Return true if the input string contains only valid base-16 digits. ** If any invalid characters appear in the string, return false. */ int validate16(const char *zIn, int nIn){ int i; for(i=0; i<nIn; i++, zIn++){ if( zDecode[zIn[0]&0xff]>63 ){ return zIn[0]==0; } } return 1; } /* ** The input string is a base16 value. Convert it into its canonical ** form. This means that digits are all lower case and that conversions ** like "l"->"1" and "O"->"0" occur. */ void canonical16(char *z, int n){ while( *z && n-- ){ *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; z++; } } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Return true if the input string contains only valid base-16 digits. ** If any invalid characters appear in the string, return false. */ int validate16(const char *zIn, int nIn){ int i; if( nIn<0 ) nIn = (int)strlen(zIn); if( zIn[nIn]==0 ){ return (int)strspn(zIn,"0123456789abcdefABCDEF")==nIn; } for(i=0; i<nIn; i++, zIn++){ if( zDecode[zIn[0]&0xff]>63 ){ return zIn[0]==0; } } return 1; } /* ** The input string is a base16 value. Convert it into its canonical ** form. This means that digits are all lower case and that conversions ** like "l"->"1" and "O"->"0" occur. */ void canonical16(char *z, int n){ while( *z && n-- ){ *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; z++; } } /* ** Decode a string encoded using "quoted-printable". ** ** (1) "=" followed by two hex digits becomes a single ** byte specified by the two digits ** ** The decoding is done in-place. */ void decodeQuotedPrintable(char *z, int *pnByte){ int i, j, c; for(i=j=0; (c = z[i])!=0; i++){ if( c=='=' ){ if( z[i+1]!='\r' ){ decode16((unsigned char*)&z[i+1], (unsigned char*)&z[j], 2); j++; } i += 2; }else{ z[j++] = c; } } if( pnByte ) *pnByte = j; z[j] = 0; } /* 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) ){ memcpy(zOut, zIn, n+1); }else{ n = n/2 - 1; for(i=0; i<n; i++) zOut[i] = zOut[i]^aObscurer[i&0x0f]^salt; zOut[n] = 0; } return zOut; } /* ** Command to test obscure() and unobscure(). These commands are also useful ** utilities for decoding passwords found in the database. ** ** COMMAND: test-obscure ** ** For each command-line argument X, run both obscure(X) and ** unobscure(obscure(X)) and print the results. This is used for testing ** and debugging of the obscure() and unobscure() functions. */ void test_obscure_cmd(void){ int i; char *z, *z2; for(i=2; i<g.argc; i++){ z = obscure(g.argv[i]); z2 = unobscure(z); fossil_print("OBSCURE: %s -> %s (%s)\n", g.argv[i], z, z2); free(z); free(z2); z = unobscure(g.argv[i]); fossil_print("UNOBSCURE: %s -> %s\n", g.argv[i], z); free(z); } } |
Added src/etag.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 | /* ** Copyright (c) 2018 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 ETags: cache control for Fossil ** ** An ETag is a hash that encodes attributes which must be the same for ** the page to continue to be valid. Attributes that might be contained ** in the ETag include: ** ** (1) The mtime on the Fossil executable ** (2) The last change to the CONFIG table ** (3) The last change to the EVENT table ** (4) The value of the display cookie ** (5) A hash value supplied by the page generator ** (6) The details of the request URI ** (7) The name user as determined by the login cookie ** ** Item (1) is always included in the ETag. The other elements are ** optional. Because (1) is always included as part of the ETag, all ** outstanding ETags can be invalidated by touching the fossil executable. ** ** A page generator routine invokes etag_check() exactly once, with ** arguments that indicates which of the above elements to include in the ** hash. If the request contained an If-None-Match header which matches ** the generated ETag, then a 304 Not Modified reply is generated and ** the process exits. In other words, etag_check() never returns. But ** if there is no If-None_Match header or if the ETag does not match, ** then etag_check() returns normally. Later, during reply generation, ** the cgi.c module will invoke etag_tag() to recover the generated tag ** and include it in the reply header. ** ** 2018-02-25: ** ** Also support Last-Modified: and If-Modified-Since:. The ** etag_last_modified(mtime) API records a timestamp for the page in ** seconds since 1970. This causes a Last-Modified: header to be ** issued in the reply. Or, if the request contained If-Modified-Since: ** and the new mtime is not greater than the mtime associated with ** If-Modified-Since, then a 304 Not Modified reply is generated and ** the etag_last_modified() API never returns. */ #include "config.h" #include "etag.h" #if INTERFACE /* ** Things to monitor */ #define ETAG_CONFIG 0x01 /* Output depends on the CONFIG table */ #define ETAG_DATA 0x02 /* Output depends on the EVENT table */ #define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */ #define ETAG_HASH 0x08 /* Output depends on a hash */ #define ETAG_QUERY 0x10 /* Output depends on PATH_INFO and QUERY_STRING */ /* and the g.zLogin value */ #endif static char zETag[33]; /* The generated ETag */ static int iMaxAge = 0; /* The max-age parameter in the reply */ static sqlite3_int64 iEtagMtime = 0; /* Last-Modified time */ static int etagCancelled = 0; /* Never send an etag */ /* ** Return a hash that changes every time the Fossil source code is ** rebuilt. ** ** The FOSSIL_BUILD_HASH string that is returned here gets computed by ** the mkversion utility program. The result is a hash of MANIFEST_UUID ** and the unix timestamp for when the mkversion utility program is run. ** ** During development rebuilds, if you need the source code id to change ** in order to invalidate caches, simply "touch" the "manifest" file in ** the top of the source directory prior to running "make" and a new ** FOSSIL_BUILD_HASH will be generated automatically. */ const char *fossil_exe_id(void){ return FOSSIL_BUILD_HASH; } /* ** Generate an ETag */ void etag_check(unsigned eFlags, const char *zHash){ const char *zIfNoneMatch; char zBuf[50]; assert( zETag[0]==0 ); /* Only call this routine once! */ if( etagCancelled ) return; /* By default, ETagged URLs never expire since the ETag will change * when the content changes. Approximate this policy as 10 years. */ iMaxAge = 10 * 365 * 24 * 60 * 60; md5sum_init(); /* Always include the executable ID as part of the hash */ md5sum_step_text("exe-id: ", -1); md5sum_step_text(fossil_exe_id(), -1); md5sum_step_text("\n", 1); if( (eFlags & ETAG_HASH)!=0 && zHash ){ md5sum_step_text("hash: ", -1); md5sum_step_text(zHash, -1); md5sum_step_text("\n", 1); iMaxAge = 0; } if( eFlags & ETAG_DATA ){ int iKey = db_int(0, "SELECT max(rcvid) FROM rcvfrom"); sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",iKey); md5sum_step_text("data: ", -1); md5sum_step_text(zBuf, -1); md5sum_step_text("\n", 1); iMaxAge = 60; } if( eFlags & ETAG_CONFIG ){ int iKey = db_int(0, "SELECT value FROM config WHERE name='cfgcnt'"); sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",iKey); md5sum_step_text("config: ", -1); md5sum_step_text(zBuf, -1); md5sum_step_text("\n", 1); iMaxAge = 3600; } /* Include the display cookie */ if( eFlags & ETAG_COOKIE ){ md5sum_step_text("display-cookie: ", -1); md5sum_step_text(PD(DISPLAY_SETTINGS_COOKIE,""), -1); md5sum_step_text("\n", 1); iMaxAge = 0; } /* Output depends on PATH_INFO and QUERY_STRING */ if( eFlags & ETAG_QUERY ){ const char *zQS = P("QUERY_STRING"); md5sum_step_text("query: ", -1); md5sum_step_text(PD("PATH_INFO",""), -1); if( zQS ){ md5sum_step_text("?", 1); md5sum_step_text(zQS, -1); } md5sum_step_text("\n",1); if( g.zLogin ){ md5sum_step_text("login: ", -1); md5sum_step_text(g.zLogin, -1); md5sum_step_text("\n", 1); } } /* Generate the ETag */ memcpy(zETag, md5sum_finish(0), 33); /* Check to see if the generated ETag matches If-None-Match and ** generate a 304 reply if it does. */ zIfNoneMatch = P("HTTP_IF_NONE_MATCH"); if( zIfNoneMatch==0 ) return; if( strcmp(zIfNoneMatch,zETag)!=0 ) return; /* If we get this far, it means that the content has ** not changed and we can do a 304 reply */ cgi_reset_content(); cgi_set_status(304, "Not Modified"); cgi_reply(); db_close(0); fossil_exit(0); } /* ** If the output is determined purely by hash parameter and the hash ** is long enough to be invariant, then set the g.isConst flag, indicating ** that the output will never change. */ void etag_check_for_invariant_name(const char *zHash){ size_t nHash = strlen(zHash); if( nHash<HNAME_MIN ){ return; /* Name is too short */ } if( !validate16(zHash, (int)nHash) ){ return; /* Name is not pure hex */ } g.isConst = 1; /* A long hex identifier must be a unique hash */ } /* ** Accept a new Last-Modified time. This routine should be called by ** page generators that know a valid last-modified time. This routine ** might generate a 304 Not Modified reply and exit(), never returning. ** Or, if not, it will cause a Last-Modified: header to be included in the ** reply. */ void etag_last_modified(sqlite3_int64 mtime){ const char *zIfModifiedSince; sqlite3_int64 x; assert( iEtagMtime==0 ); /* Only call this routine once */ assert( mtime>0 ); /* Only call with a valid mtime */ iEtagMtime = mtime; /* Check to see the If-Modified-Since constraint is satisfied */ zIfModifiedSince = P("HTTP_IF_MODIFIED_SINCE"); if( zIfModifiedSince==0 ) return; x = cgi_rfc822_parsedate(zIfModifiedSince); if( x<mtime ) return; #if 0 /* If the Fossil executable is more recent than If-Modified-Since, ** go ahead and regenerate the resource. */ if( file_mtime(g.nameOfExe, ExtFILE)>x ) return; #endif /* If we reach this point, it means that the resource has not changed ** and that we should generate a 304 Not Modified reply */ cgi_reset_content(); cgi_set_status(304, "Not Modified"); cgi_reply(); db_close(0); fossil_exit(0); } /* Return the ETag, if there is one. */ const char *etag_tag(void){ return g.isConst ? "" : zETag; } /* Return the recommended max-age */ int etag_maxage(void){ return iMaxAge; } /* Return the last-modified time in seconds since 1970. Or return 0 if ** there is no last-modified time. */ sqlite3_int64 etag_mtime(void){ return iEtagMtime; } /* ** COMMAND: test-etag ** ** Usage: fossil test-etag -key KEY-NUMBER -hash HASH ** ** Generate an etag given a KEY-NUMBER and/or a HASH. ** ** KEY-NUMBER is some combination of: ** ** 1 ETAG_CONFIG The config table version number ** 2 ETAG_DATA The event table version number ** 4 ETAG_COOKIE The display cookie */ void test_etag_cmd(void){ const char *zHash = 0; const char *zKey; int iKey = 0; db_find_and_open_repository(0, 0); zKey = find_option("key",0,1); zHash = find_option("hash",0,1); if( zKey ) iKey = atoi(zKey); etag_check(iKey, zHash); fossil_print("%s\n", etag_tag()); } /* ** Cancel the ETag. */ void etag_cancel(void){ etagCancelled = 1; zETag[0] = 0; } |
Added src/event.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 | /* ** Copyright (c) 2010 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 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 (formerly called an "event"). ** ** PARAMETERS: ** ** name=ID Identify the technical 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 tech-note identified by its ID, optionally at a ** specific version, and optionally with additional details. */ void event_page(void){ int rid = 0; /* rid of the event artifact */ char *zUuid; /* artifact hash 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 */ const char *zFullId; /* Full event identifier */ /* 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); style_set_current_feature("event"); if( rid==0 || (specRid!=0 && specRid!=rid) ){ style_header("No Such Tech-Note"); @ Cannot locate a technical note called <b>%h(zId)</b>. style_finish_page(); 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. */ cgi_check_for_malice(); 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", "%R/technoteedit?name=%!S", zId); if( g.perm.Attach ){ style_submenu_element("Attach", "%R/attachadd?technote=%!S&from=%R/technote/%!S", zId, zId); } } zETime = db_text(0, "SELECT datetime(%.17g)", pTNote->rEventDate); style_submenu_element("Context", "%R/timeline?c=%.20s", zId); if( g.perm.Hyperlink ){ if( verboseFlag ){ style_submenu_element("Plain", "%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", "%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", "%R/technote?name=%!S&aid=%s&v", zId, zPrev); free(zPrev); } }else{ style_submenu_element("Detail", "%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> } zFullId = db_text(0, "SELECT SUBSTR(tagname,7)" " FROM tag" " WHERE tagname GLOB 'event-%q*'", zId); attachment_list(zFullId, "<hr><h2>Attachments:</h2><ul>"); document_emit_js(); style_finish_page(); manifest_destroy(pTNote); } /* ** Add or update a new tech note to the repository. rid is id of ** the prior version of this technote, if any. ** ** returns 1 if the tech note was added or updated, 0 if the ** update failed making an invalid artifact */ int event_commit_common( int rid, /* id of the prior version of the technote */ const char *zId, /* hash label for the technote */ const char *zBody, /* content of the technote */ char *zETime, /* timestamp for the technote */ const char *zMimetype, /* mimetype for the technote N-card */ const char *zComment, /* comment shown on the timeline */ const char *zTags, /* tags associated with this technote */ const char *zClr /* Background color */ ){ Blob event; char *zDate; Blob cksum; int nrid, n; blob_init(&event, 0, 0); db_begin_transaction(); 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( zMimetype && zMimetype[0] ){ blob_appendf(&event, "N %s\n", zMimetype); } 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( 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++; } zBlob[j] = ' '; }else{ zBlob[j] = zBlob[i]; } } blob_resize(&tags, j); /* Parse out each tag and load it into a temporary table for sorting */ db_multi_exec("CREATE TEMP TABLE newtags(x);"); while( blob_token(&tags, &one) ){ db_multi_exec("INSERT INTO newtags VALUES(%B)", &one); } blob_reset(&tags); /* 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_add_unsent(nrid); if( manifest_crosslink(nrid, &event, MC_NONE)==0 ){ db_end_transaction(1); return 0; } assert( blob_is_reset(&event) ); content_deltify(rid, &nrid, 1, 0); db_end_transaction(0); return 1; } /* ** WEBPAGE: technoteedit ** WEBPAGE: eventedit ** ** Revise or create a technical note (formerly called an "event"). ** ** Required query parameter: ** ** name=ID Hex hash ID of the technote. If omitted, a new ** tech-note is created. ** ** POST parameters from the "Cancel", "Preview", or "Submit" buttons: ** ** w=TEXT Complete text of the technote. ** t=TEXT Time of the technote on the timeline (ISO 8601) ** c=TEXT Timeline comment ** g=TEXT Tags associated with this technote ** mimetype=TEXT Mimetype for w= text ** newclr Use a background color ** clr=TEXT Background color to use if newclr ** ** For GET requests, when editing an existing technote newclr and clr ** are implied if a custom color has been set on the previous version ** of the technote. */ void eventedit_page(void){ char *zTag; int rid = 0; Blob event; const char *zId; int n; const char *z; char *zBody = (char*)P("w"); /* Text of the technote */ char *zETime = (char*)P("t"); /* Date this technote appears */ const char *zComment = P("c"); /* Timeline comment */ const char *zTags = P("g"); /* Tags added to this technote */ const char *zClrFlag = ""; /* "checked" for bg color */ const char *zClr; /* Name of the background color */ const char *zMimetype = P("mimetype"); /* Mimetype of zBody */ 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)<HNAME_MIN ){ 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; } style_set_current_feature("event"); /* Figure out the color */ if( rid ){ zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid); if( zClr && zClr[0] ){ const char * zRequestMethod = P("REQUEST_METHOD"); if(zRequestMethod && 'G'==zRequestMethod[0]){ /* Apply saved color by defaut for GET requests ** (e.g., an Edit menu link). */ zClrFlag = " checked"; } } }else{ zClr = ""; isNew = 1; } if( P("newclr") ){ zClr = PD("clr",zClr); if( zClr[0] ) zClrFlag = " checked"; } /* 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) && cgi_csrf_safe(2) ){ if ( !event_commit_common(rid, zId, zBody, zETime, zMimetype, zComment, zTags, zClrFlag[0] ? zClr : 0) ){ style_header("Error"); @ Internal error: Fossil tried to make an invalid artifact for @ the edited technote. style_finish_page(); return; } cgi_redirectf("%R/technote?name=%T", zId); } if( P("cancel")!=0 ){ cgi_redirectf("%R/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( zClrFlag[0] && 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); safe_html_context(DOCSRC_WIKI); 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"> @ <input type='checkbox' name='newclr'%s(zClrFlag)> @ Use custom color: \ @ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'> @ </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">\ @ %z(href("%R/markup_help"))Markup Style</a>:</th> @ <td valign="top"> mimetype_option_menu(zMimetype, "mimetype"); @ </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="cancel" value="Cancel"> @ <input type="submit" name="preview" value="Preview"> if( P("preview") ){ @ <input type="submit" name="submit" value="Submit"> } @ </td></tr></table> @ </div></form> style_finish_page(); } /* ** Add a new tech note to the repository. The timestamp is ** given by the zETime parameter. rid must be zero to create ** a new page. If no previous page with the name zPageName exists ** and isNew is false, then this routine throws an error. */ void event_cmd_commit( char *zETime, /* timestamp */ int rid, /* Artifact id of the tech note */ Blob *pContent, /* content of the new page */ const char *zMimeType, /* mimetype of the content */ const char *zComment, /* comment to go on the timeline */ const char *zTags, /* tags */ const char *zClr /* background color */ ){ const char *zId; /* id of the tech note */ if ( rid==0 ){ zId = db_text(0, "SELECT lower(hex(randomblob(20)))"); }else{ zId = db_text(0, "SELECT substr(tagname,7) FROM tag" " WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')", rid ); } user_select(); if (event_commit_common(rid, zId, blob_str(pContent), zETime, zMimeType, zComment, zTags, zClr)==0 ){ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_ASSERT; #endif fossil_panic("Internal error: Fossil tried to make an " "invalid artifact for the technote."); } } |
Added src/export.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 | /* ** Copyright (c) 2010 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 contains code used to export the content of a Fossil ** repository in the git-fast-import format. */ #include "config.h" #include "export.h" #include <assert.h> /* ** State information common to all export types. */ static struct { const char *zTrunkName; /* Name of trunk branch */ } gexport; #if INTERFACE /* ** Each line in a git-fast-export "marK" file is an instance of ** this object. */ struct mark_t { char *name; /* Name of the mark. Also starts with ":" */ int rid; /* Corresponding object in the BLOB table */ char uuid[65]; /* The GIT hash name for this object */ }; #endif /* ** Output a "committer" record for the given user. ** NOTE: the given user name may be an email itself. */ static void print_person(const char *zUser){ static Stmt q; const char *zContact; char *zName; char *zEmail; int i, j; int isBracketed, atEmailFirst, atEmailLast; if( zUser==0 ){ printf(" <unknown>"); return; } db_static_prepare(&q, "SELECT info FROM user WHERE login=:user"); db_bind_text(&q, ":user", zUser); if( db_step(&q)!=SQLITE_ROW ){ db_reset(&q); zName = mprintf("%s", zUser); for(i=j=0; zName[i]; i++){ if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){ zName[j++] = zName[i]; } } zName[j] = 0; printf(" %s <%s>", zName, zName); free(zName); return; } /* ** We have contact information. ** It may or may not contain an email address. ** ** ASSUME: ** - General case:"Name Unicoded" <email@address.com> other info ** - If contact information contains more than an email address, ** then the email address is enclosed between <> ** - When only email address is specified, then it's stored verbatim ** - When name part is absent or all-blanks, use zUser instead */ zName = NULL; zEmail = NULL; zContact = db_column_text(&q, 0); atEmailFirst = -1; atEmailLast = -1; isBracketed = 0; for(i=0; zContact[i] && zContact[i]!='@'; i++){ if( zContact[i]=='<' ){ isBracketed = 1; atEmailFirst = i+1; } else if( zContact[i]=='>' ){ isBracketed = 0; atEmailFirst = i+1; } else if( zContact[i]==' ' && !isBracketed ){ atEmailFirst = i+1; } } if( zContact[i]==0 ){ /* No email address found. Take as user info if not empty */ zName = mprintf("%s", zContact[0] ? zContact : zUser); for(i=j=0; zName[i]; i++){ if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){ zName[j++] = zName[i]; } } zName[j] = 0; printf(" %s <%s>", zName, zName); free(zName); db_reset(&q); return; } for(j=i+1; zContact[j] && zContact[j]!=' '; j++){ if( zContact[j]=='>' ) atEmailLast = j-1; } if ( atEmailLast==-1 ) atEmailLast = j-1; if ( atEmailFirst==-1 ) atEmailFirst = 0; /* Found only email */ /* ** Found beginning and end of email address. ** Extract the address (trimmed and sanitized). */ for(j=atEmailFirst; zContact[j] && zContact[j]==' '; j++){} zEmail = mprintf("%.*s", atEmailLast-j+1, &zContact[j]); for(i=j=0; zEmail[i]; i++){ if( zEmail[i]!='<' && zEmail[i]!='>' ){ zEmail[j++] = zEmail[i]; } } zEmail[j] = 0; /* ** When bracketed email, extract the string _before_ ** email as user name (may be enquoted). ** If missing or all-blank name, use zUser. */ if( isBracketed && (atEmailFirst-1) > 0){ for(i=atEmailFirst-2; i>=0 && zContact[i] && zContact[i]==' '; i--){} if( i>=0 ){ for(j=0; j<i && zContact[j] && zContact[j]==' '; j++){} zName = mprintf("%.*s", i-j+1, &zContact[j]); } } if( zName==NULL ) zName = mprintf("%s", zUser); for(i=j=0; zName[i]; i++){ if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){ zName[j++] = zName[i]; } } zName[j] = 0; printf(" %s <%s>", zName, zEmail); free(zName); free(zEmail); db_reset(&q); } #define REFREPLACEMENT '_' /* ** Output a sanitized git named reference. ** https://git-scm.com/docs/git-check-ref-format ** This implementation assumes we are only printing ** the branch or tag part of the reference. */ static void print_ref(const char *zRef){ char *zEncoded = mprintf("%s", zRef); int i, w; if (zEncoded[0]=='@' && zEncoded[1]=='\0'){ putchar(REFREPLACEMENT); return; } for(i=0, w=0; zEncoded[i]; i++, w++){ if( i!=0 ){ /* Two letter tests */ if( (zEncoded[i-1]=='.' && zEncoded[i]=='.') || (zEncoded[i-1]=='@' && zEncoded[i]=='{') ){ zEncoded[w]=zEncoded[w-1]=REFREPLACEMENT; continue; } if( zEncoded[i-1]=='/' && zEncoded[i]=='/' ){ w--; /* Normalise to a single / by rolling back w */ continue; } } /* No control characters */ if( (unsigned)zEncoded[i]<0x20 || zEncoded[i]==0x7f ){ zEncoded[w]=REFREPLACEMENT; continue; } switch( zEncoded[i] ){ case ' ': case '^': case ':': case '?': case '*': case '[': case '\\': zEncoded[w]=REFREPLACEMENT; break; } } /* Cannot begin with a . or / */ if( zEncoded[0]=='.' || zEncoded[0] == '/' ) zEncoded[0]=REFREPLACEMENT; if( i>0 ){ i--; w--; /* Or end with a . or / */ if( zEncoded[i]=='.' || zEncoded[i] == '/' ) zEncoded[w]=REFREPLACEMENT; /* Cannot end with .lock */ if ( i>4 && strcmp((zEncoded+i)-5, ".lock")==0 ) memset((zEncoded+w)-5, REFREPLACEMENT, 5); } printf("%s", zEncoded); free(zEncoded); } #define BLOBMARK(rid) ((rid) * 2) #define COMMITMARK(rid) ((rid) * 2 + 1) /* ** insert_commit_xref() ** Insert a new (mark,rid,uuid) entry into the 'xmark' table. ** zName and zUuid must be non-null and must point to NULL-terminated strings. */ void insert_commit_xref(int rid, const char *zName, const char *zUuid){ db_multi_exec( "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" "VALUES(%Q,%d,%Q)", zName, rid, zUuid ); } /* ** create_mark() ** Create a new (mark,rid,uuid) entry for the given rid in the 'xmark' table, ** and return that information as a struct mark_t in *mark. ** *unused_mark is a value representing a mark that is free for use--that is, ** it does not appear in the marks file, and has not been used during this ** export run. Specifically, it is the supremum of the set of used marks ** plus one. ** This function returns -1 in the case where 'rid' does not exist, otherwise ** it returns 0. ** mark->name is dynamically allocated and is owned by the caller upon return. */ int create_mark(int rid, struct mark_t *mark, unsigned int *unused_mark){ char sid[13]; char *zUuid = rid_to_uuid(rid); if( !zUuid ){ fossil_trace("Undefined rid=%d\n", rid); return -1; } mark->rid = rid; sqlite3_snprintf(sizeof(sid), sid, ":%d", *unused_mark); *unused_mark += 1; mark->name = fossil_strdup(sid); sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", zUuid); free(zUuid); insert_commit_xref(mark->rid, mark->name, mark->uuid); return 0; } /* ** mark_name_from_rid() ** Find the mark associated with the given rid. Mark names always start ** with ':', and are pulled from the 'xmark' temporary table. ** If the given rid doesn't have a mark associated with it yet, one is ** created with a value of *unused_mark. ** *unused_mark functions exactly as in create_mark(). ** This function returns NULL if the rid does not have an associated UUID, ** (i.e. is not valid). Otherwise, it returns the name of the mark, which is ** dynamically allocated and is owned by the caller of this function. */ char * mark_name_from_rid(int rid, unsigned int *unused_mark){ char *zMark = db_text(0, "SELECT tname FROM xmark WHERE trid=%d", rid); if( zMark==NULL ){ struct mark_t mark; if( create_mark(rid, &mark, unused_mark)==0 ){ zMark = mark.name; }else{ return NULL; } } return zMark; } /* ** Parse a single line of the mark file. Store the result in the mark object. ** ** "line" is a single line of input. ** This function returns -1 in the case that the line is blank, malformed, or ** the rid/uuid named in 'line' does not match what is in the repository ** database. Otherwise, 0 is returned. ** ** mark->name is dynamically allocated, and owned by the caller. */ int parse_mark(char *line, struct mark_t *mark){ char *cur_tok; char type_; cur_tok = strtok(line, " \t"); if( !cur_tok || strlen(cur_tok)<2 ){ return -1; } mark->rid = atoi(&cur_tok[1]); type_ = cur_tok[0]; if( type_!='c' && type_!='b' ){ /* This is probably a blob mark */ mark->name = NULL; return 0; } cur_tok = strtok(NULL, " \t"); if( !cur_tok ){ /* This mark was generated by an older version of Fossil and doesn't ** include the mark name and uuid. create_mark() will name the new mark ** exactly as it was when exported to git, so that we should have a ** valid mapping from git hash<->mark name<->fossil hash. */ unsigned int mid; if( type_=='c' ){ mid = COMMITMARK(mark->rid); } else{ mid = BLOBMARK(mark->rid); } return create_mark(mark->rid, mark, &mid); }else{ mark->name = fossil_strdup(cur_tok); } cur_tok = strtok(NULL, "\n"); if( !cur_tok || (strlen(cur_tok)!=40 && strlen(cur_tok)!=64) ){ free(mark->name); fossil_trace("Invalid SHA-1/SHA-3 in marks file: %s\n", cur_tok); return -1; }else{ sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok); } /* make sure that rid corresponds to UUID */ if( fast_uuid_to_rid(mark->uuid)!=mark->rid ){ free(mark->name); fossil_trace("Non-existent SHA-1/SHA-3 in marks file: %s\n", mark->uuid); return -1; } /* insert a cross-ref into the 'xmark' table */ insert_commit_xref(mark->rid, mark->name, mark->uuid); return 0; } /* ** Import the marks specified in file 'f'; ** If 'blobs' is non-null, insert all blob marks into it. ** If 'vers' is non-null, insert all commit marks into it. ** If 'unused_marks' is non-null, upon return of this function, all values ** x >= *unused_marks are free to use as marks, i.e. they do not clash with ** any marks appearing in the marks file. ** ** Each line in the file must be at most 100 characters in length. This ** seems like a reasonable maximum for a 40-character uuid, and 1-13 ** character rid. ** ** The function returns -1 if any of the lines in file 'f' are malformed, ** or the rid/uuid information doesn't match what is in the repository ** database. Otherwise, 0 is returned. */ int import_marks(FILE* f, Bag *blobs, Bag *vers, unsigned int *unused_mark){ char line[101]; while(fgets(line, sizeof(line), f)){ struct mark_t mark; if( strlen(line)==100 && line[99]!='\n' ){ /* line too long */ return -1; } if( parse_mark(line, &mark)<0 ){ return -1; }else if( line[0]=='b' ){ if( blobs!=NULL ){ bag_insert(blobs, mark.rid); } }else{ if( vers!=NULL ){ bag_insert(vers, mark.rid); } } if( unused_mark!=NULL ){ unsigned int mid = atoi(mark.name + 1); if( mid>=*unused_mark ){ *unused_mark = mid + 1; } } free(mark.name); } return 0; } void export_mark(FILE* f, int rid, char obj_type) { unsigned int z = 0; char *zUuid = rid_to_uuid(rid); char *zMark; if( zUuid==NULL ){ fossil_trace("No uuid matching rid=%d when exporting marks\n", rid); return; } /* Since rid is already in the 'xmark' table, the value of z won't be ** used, but pass in a valid pointer just to be safe. */ zMark = mark_name_from_rid(rid, &z); fprintf(f, "%c%d %s %s\n", obj_type, rid, zMark, zUuid); free(zMark); free(zUuid); } /* ** If 'blobs' is non-null, it must point to a Bag of blob rids to be ** written to disk. Blob rids are written as 'b<rid>'. ** If 'vers' is non-null, it must point to a Bag of commit rids to be ** written to disk. Commit rids are written as 'c<rid> :<mark> <uuid>'. ** All commit (mark,rid,uuid) tuples are stored in 'xmark' table. ** This function does not fail, but may produce errors if a uuid cannot ** be found for an rid in 'vers'. */ void export_marks(FILE* f, Bag *blobs, Bag *vers){ int rid; if( blobs!=NULL ){ rid = bag_first(blobs); if( rid!=0 ){ do{ export_mark(f, rid, 'b'); }while( (rid = bag_next(blobs, rid))!=0 ); } } if( vers!=NULL ){ rid = bag_first(vers); if( rid!=0 ){ do{ export_mark(f, rid, 'c'); }while( (rid = bag_next(vers, rid))!=0 ); } } } /* This is the original header command (and hence documentation) for ** the "fossil export" command: ** ** 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 check-out. 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 tech notes 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 ** --rename-trunk NAME Use NAME as name of exported trunk branch ** -R|--repository REPO Export the given REPOSITORY ** ** See also: import */ /* ** COMMAND: export* ** ** This command is deprecated. Use "fossil git export" instead. */ void export_cmd(void){ Stmt q, q2, q3; Bag blobs, vers; unsigned int unused_mark = 1; const char *markfile_in; const char *markfile_out; bag_init(&blobs); bag_init(&vers); find_option("git", 0, 0); /* Ignore the --git option for now */ markfile_in = find_option("import-marks", 0, 1); markfile_out = find_option("export-marks", 0, 1); if( !(gexport.zTrunkName = find_option("rename-trunk", 0, 1)) ){ gexport.zTrunkName = "trunk"; } db_find_and_open_repository(0, 2); verify_all_options(); if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); } db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)"); db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)"); db_multi_exec("CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT," " tuuid TEXT)"); db_multi_exec("CREATE INDEX xmark_trid ON xmark(trid)"); if( markfile_in!=0 ){ Stmt qb,qc; FILE *f; int rid; f = fossil_fopen(markfile_in, "r"); if( f==0 ){ fossil_fatal("cannot open %s for reading", markfile_in); } if( import_marks(f, &blobs, &vers, &unused_mark)<0 ){ fossil_fatal("error importing marks from file: %s", markfile_in); } db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)"); db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)"); rid = bag_first(&blobs); if( rid!=0 ){ do{ db_bind_int(&qb, ":rid", rid); db_step(&qb); db_reset(&qb); }while((rid = bag_next(&blobs, rid))!=0); } rid = bag_first(&vers); if( rid!=0 ){ do{ db_bind_int(&qc, ":rid", rid); db_step(&qc); db_reset(&qc); }while((rid = bag_next(&vers, rid))!=0); } 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," " CASE WHEN EXISTS(SELECT 1 FROM delta" " WHERE rid=fid" " AND NOT EXISTS(SELECT 1 FROM oldblob" " WHERE srcid=fid))" " THEN (SELECT srcid FROM delta WHERE rid=fid)" " ELSE 0" " END" " FROM mlink" " WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)"); db_prepare(&q, "SELECT DISTINCT fid FROM mlink" " WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)"); db_prepare(&q2, "INSERT INTO oldblob VALUES (:rid)"); db_prepare(&q3, "SELECT rid FROM newblob WHERE srcid= (:srcid)"); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); Blob content; while( !bag_find(&blobs, rid) ){ char *zMark; content_get(rid, &content); db_bind_int(&q2, ":rid", rid); db_step(&q2); db_reset(&q2); zMark = mark_name_from_rid(rid, &unused_mark); printf("blob\nmark %s\ndata %d\n", zMark, blob_size(&content)); free(zMark); bag_insert(&blobs, rid); fwrite(blob_buffer(&content), 1, blob_size(&content), stdout); printf("\n"); blob_reset(&content); db_bind_int(&q3, ":srcid", rid); if( db_step(&q3) != SQLITE_ROW ){ db_reset(&q3); break; } rid = db_column_int(&q3, 0); db_reset(&q3); } } db_finalize(&q); db_finalize(&q2); db_finalize(&q3); /* Output the commit records. */ topological_sort_checkins(0); 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 toponode, event" " WHERE toponode.tid=event.objid" " AND event.type='ci'" " AND NOT EXISTS (SELECT 1 FROM oldcommit WHERE toponode.tid=rid)" " ORDER BY toponode.tseq ASC", TAG_BRANCH ); db_prepare(&q2, "INSERT INTO oldcommit VALUES (:rid)"); while( db_step(&q)==SQLITE_ROW ){ Stmt q4; const char *zSecondsSince1970 = db_column_text(&q, 0); int ckinId = db_column_int(&q, 1); const char *zComment = db_column_text(&q, 2); const char *zUser = db_column_text(&q, 3); const char *zBranch = db_column_text(&q, 4); char *zMark; bag_insert(&vers, ckinId); db_bind_int(&q2, ":rid", ckinId); db_step(&q2); db_reset(&q2); if( zBranch==0 || fossil_strcmp(zBranch, "trunk")==0 ){ zBranch = gexport.zTrunkName; } zMark = mark_name_from_rid(ckinId, &unused_mark); printf("commit refs/heads/"); print_ref(zBranch); printf("\nmark %s\n", zMark); free(zMark); printf("committer"); print_person(zUser); printf(" %s +0000\n", zSecondsSince1970); if( zComment==0 ) zComment = "null comment"; printf("data %d\n%s\n", (int)strlen(zComment), zComment); db_prepare(&q3, "SELECT pid FROM plink" " WHERE cid=%d AND isprim" " AND pid IN (SELECT objid FROM event)", ckinId ); if( db_step(&q3) == SQLITE_ROW ){ int pid = db_column_int(&q3, 0); zMark = mark_name_from_rid(pid, &unused_mark); printf("from %s\n", zMark); free(zMark); db_prepare(&q4, "SELECT pid FROM plink" " WHERE cid=%d AND NOT isprim" " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)" " ORDER BY pid", ckinId); while( db_step(&q4)==SQLITE_ROW ){ zMark = mark_name_from_rid(db_column_int(&q4, 0), &unused_mark); printf("merge %s\n", zMark); free(zMark); } db_finalize(&q4); }else{ printf("deleteall\n"); } db_prepare(&q4, "SELECT filename.name, mlink.fid, mlink.mperm FROM mlink" " JOIN filename ON filename.fnid=mlink.fnid" " WHERE mlink.mid=%d", ckinId ); while( db_step(&q4)==SQLITE_ROW ){ const char *zName = db_column_text(&q4,0); int zNew = db_column_int(&q4,1); int mPerm = db_column_int(&q4,2); if( zNew==0 ){ printf("D %s\n", zName); }else if( bag_find(&blobs, zNew) ){ const char *zPerm; zMark = mark_name_from_rid(zNew, &unused_mark); switch( mPerm ){ case PERM_LNK: zPerm = "120000"; break; case PERM_EXE: zPerm = "100755"; break; default: zPerm = "100644"; break; } printf("M %s %s %s\n", zPerm, zMark, zName); free(zMark); } } db_finalize(&q4); db_finalize(&q3); printf("\n"); } db_finalize(&q2); db_finalize(&q); manifest_cache_clear(); /* Output tags */ db_prepare(&q, "SELECT tagname, rid, strftime('%%s',mtime)," " (SELECT coalesce(euser, user) FROM event WHERE objid=rid)," " value" " FROM tagxref JOIN tag USING(tagid)" " WHERE tagtype=1 AND tagname GLOB 'sym-*'" ); while( db_step(&q)==SQLITE_ROW ){ const char *zTagname = db_column_text(&q, 0); int rid = db_column_int(&q, 1); char *zMark = mark_name_from_rid(rid, &unused_mark); const char *zSecSince1970 = db_column_text(&q, 2); const char *zUser = db_column_text(&q, 3); const char *zValue = db_column_text(&q, 4); if( rid==0 || !bag_find(&vers, rid) ) continue; zTagname += 4; printf("tag "); print_ref(zTagname); printf("\nfrom %s\n", zMark); free(zMark); printf("tagger"); print_person(zUser); printf(" %s +0000\n", zSecSince1970); printf("data %d\n", zValue==NULL?0:(int)strlen(zValue)+1); if( zValue!=NULL ) printf("%s\n",zValue); } db_finalize(&q); if( markfile_out!=0 ){ FILE *f; f = fossil_fopen(markfile_out, "w"); if( f == 0 ){ fossil_fatal("cannot open %s for writing", markfile_out); } export_marks(f, &blobs, &vers); if( ferror(f)!=0 || fclose(f)!=0 ){ fossil_fatal("error while writing %s", markfile_out); } } bag_clear(&blobs); bag_clear(&vers); } /* ** Construct the temporary table toposort as follows: ** ** CREATE TEMP TABLE toponode( ** tid INTEGER PRIMARY KEY, -- Check-in id ** tseq INT -- integer total order on check-ins. ** ); ** ** This table contains all check-ins of the repository in topological ** order. "Topological order" means that every parent check-in comes ** before all of its children. Topological order is *almost* the same ** thing as "ORDER BY event.mtime". Differences only arise when there ** are timewarps. In as much as Git hates timewarps, we have to compute ** a correct topological order when doing an export. ** ** Since mtime is a usually already nearly in topological order, the ** algorithm is to start with mtime, then make adjustments as necessary ** for timewarps. This is not a great algorithm for the general case, ** but it is very fast for the overwhelmingly common case where there ** are few timewarps. */ int topological_sort_checkins(int bVerbose){ int nChange = 0; Stmt q1; Stmt chng; db_multi_exec( "CREATE TEMP TABLE toponode(\n" " tid INTEGER PRIMARY KEY,\n" " tseq INT\n" ");\n" "INSERT INTO toponode(tid,tseq) " " SELECT objid, CAST(mtime*8640000 AS int) FROM event WHERE type='ci';\n" "CREATE TEMP TABLE topolink(\n" " tparent INT,\n" " tchild INT,\n" " PRIMARY KEY(tparent,tchild)\n" ") WITHOUT ROWID;" "INSERT INTO topolink(tparent,tchild)" " SELECT pid, cid FROM plink;\n" "CREATE INDEX topolink_child ON topolink(tchild);\n" ); /* Find a timewarp instance */ db_prepare(&q1, "SELECT P.tseq, C.tid, C.tseq\n" " FROM toponode P, toponode C, topolink X\n" " WHERE X.tparent=P.tid\n" " AND X.tchild=C.tid\n" " AND P.tseq>=C.tseq;" ); /* Update the timestamp on :tid to have value :tseq */ db_prepare(&chng, "UPDATE toponode SET tseq=:tseq WHERE tid=:tid" ); while( db_step(&q1)==SQLITE_ROW ){ i64 iParentTime = db_column_int64(&q1, 0); int iChild = db_column_int(&q1, 1); i64 iChildTime = db_column_int64(&q1, 2); nChange++; if( nChange>10000 ){ fossil_fatal("failed to fix all timewarps after 100000 attempts"); } db_reset(&q1); db_bind_int64(&chng, ":tid", iChild); db_bind_int64(&chng, ":tseq", iParentTime+1); db_step(&chng); db_reset(&chng); if( bVerbose ){ fossil_print("moving %d from %lld to %lld\n", iChild, iChildTime, iParentTime+1); } } db_finalize(&q1); db_finalize(&chng); return nChange; } /* ** COMMAND: test-topological-sort ** ** Invoke the topological_sort_checkins() interface for testing ** purposes. */ void test_topological_sort(void){ int n; db_find_and_open_repository(0, 0); n = topological_sort_checkins(1); fossil_print("%d reorderings required\n", n); } /*************************************************************************** ** Implementation of the "fossil git" command follows. We hope that the ** new code that follows will largely replace the legacy "fossil export" ** and "fossil import" code above. */ /* Verbosity level. Higher means more output. ** ** 0 print nothing at all ** 1 Errors only ** 2 Progress information (This is the default) ** 3 Extra details */ #define VERB_ERROR 1 #define VERB_NORMAL 2 #define VERB_EXTRA 3 static int gitmirror_verbosity = VERB_NORMAL; /* The main branch in the Git repository. The "trunk" branch of ** Fossil is renamed to be this branch name. */ static const char *gitmirror_mainbranch = 0; /* ** Output routine that depends on verbosity */ static void gitmirror_message(int iLevel, const char *zFormat, ...){ va_list ap; if( iLevel>gitmirror_verbosity ) return; va_start(ap, zFormat); fossil_vprint(zFormat, ap); va_end(ap); } /* ** Convert characters of z[] that are not allowed to be in branch or ** tag names into "_". */ static void gitmirror_sanitize_name(char *z){ static unsigned char aSafe[] = { /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */ 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, /* 2x */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, /* 3x */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, /* 5x */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, /* 7x */ }; unsigned char *zu = (unsigned char*)z; int i; for(i=0; zu[i]; i++){ if( zu[i]>0x7f || !aSafe[zu[i]] ){ zu[i] = '_'; }else if( zu[i]=='/' && (i==0 || zu[i+1]==0 || zu[i+1]=='/') ){ zu[i] = '_'; }else if( zu[i]=='.' && (zu[i+1]==0 || zu[i+1]=='.' || (i>0 && zu[i-1]=='.')) ){ zu[i] = '_'; } } } /* ** COMMAND: test-sanitize-name ** ** Usage: %fossil ARG... ** ** This sanitizes each argument and make it part of an "echo" command ** run by the shell. */ void test_sanitize_name_cmd(void){ sqlite3_str *pStr; int i; char *zCmd; pStr = sqlite3_str_new(0); sqlite3_str_appendall(pStr, "echo"); for(i=2; i<g.argc; i++){ char *z = fossil_strdup(g.argv[i]); gitmirror_sanitize_name(z); sqlite3_str_appendf(pStr, " \"%s\"", z); fossil_free(z); } zCmd = sqlite3_str_finish(pStr); fossil_print("Command: %s\n", zCmd); fossil_system(zCmd); sqlite3_free(zCmd); } /* ** Quote a filename as a C-style string using \\ and \" if necessary. ** If quoting is not necessary, just return a copy of the input string. ** ** The return value is a held in memory obtained from fossil_malloc() ** and must be freed by the caller. */ static char *gitmirror_quote_filename_if_needed(const char *zIn){ int i, j; char c; int nSpecial = 0; char *zOut; for(i=0; (c = zIn[i])!=0; i++){ if( c=='\\' || c=='"' || c=='\n' ){ nSpecial++; } } if( nSpecial==0 ){ return fossil_strdup(zIn); } zOut = fossil_malloc( i+nSpecial+3 ); zOut[0] = '"'; for(i=0, j=1; (c = zIn[i])!=0; i++){ if( c=='\\' || c=='"' || c=='\n' ){ zOut[j++] = '\\'; if( c=='\n' ){ zOut[j++] = 'n'; }else{ zOut[j++] = c; } }else{ zOut[j++] = c; } } zOut[j++] = '"'; zOut[j] = 0; return zOut; } /* ** Find the Git-name corresponding to the Fossil-name zUuid. ** ** If the mark does not exist and if the bCreate flag is false, then ** return NULL. If the mark does not exist and the bCreate flag is true, ** then create the mark. ** ** The string returned is obtained from fossil_malloc() and should ** be freed by the caller. */ static char *gitmirror_find_mark(const char *zUuid, int isFile, int bCreate){ static Stmt sFind, sIns; db_static_prepare(&sFind, "SELECT coalesce(githash,printf(':%%d',id))" " FROM mirror.mmark WHERE uuid=:uuid AND isfile=:isfile" ); db_bind_text(&sFind, ":uuid", zUuid); db_bind_int(&sFind, ":isfile", isFile!=0); if( db_step(&sFind)==SQLITE_ROW ){ char *zMark = fossil_strdup(db_column_text(&sFind, 0)); db_reset(&sFind); return zMark; } db_reset(&sFind); if( !bCreate ){ return 0; } db_static_prepare(&sIns, "INSERT INTO mirror.mmark(uuid,isfile) VALUES(:uuid,:isfile)" ); db_bind_text(&sIns, ":uuid", zUuid); db_bind_int(&sIns, ":isfile", isFile!=0); db_step(&sIns); db_reset(&sIns); return mprintf(":%d", db_last_insert_rowid()); } /* This is the SHA3-256 hash of an empty file */ static const char zEmptySha3[] = "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"; /* ** Export a single file named by zUuid. ** ** Return 0 on success and non-zero on any failure. ** ** If zUuid is a shunned file, then treat it as if it were any empty file. ** But files that are missing from the repository but have not been officially ** shunned cause an error return. Except, if bPhantomOk is true, then missing ** files are replaced by an empty file. */ static int gitmirror_send_file(FILE *xCmd, const char *zUuid, int bPhantomOk){ char *zMark; int rid; int rc; Blob data; rid = fast_uuid_to_rid(zUuid); if( rid<0 ){ if( bPhantomOk || uuid_is_shunned(zUuid) ){ gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid); zUuid = zEmptySha3; }else{ return 1; } }else{ rc = content_get(rid, &data); if( rc==0 ){ if( bPhantomOk ){ blob_init(&data, 0, 0); gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid); zUuid = zEmptySha3; }else{ return 1; } } } zMark = gitmirror_find_mark(zUuid, 1, 1); if( zMark[0]==':' ){ fprintf(xCmd, "blob\nmark %s\ndata %d\n", zMark, blob_size(&data)); fwrite(blob_buffer(&data), 1, blob_size(&data), xCmd); fprintf(xCmd, "\n"); } fossil_free(zMark); blob_reset(&data); return 0; } /* ** Transfer a check-in over to the mirror. "rid" is the BLOB.RID for ** the check-in to export. ** ** If any ancestor of the check-in has not yet been exported, then ** invoke this routine recursively to export the ancestor first. ** This can only happen on a timewarp, so deep nesting is unlikely. ** ** Before sending the check-in, first make sure all associated files ** have already been exported, and send "blob" records for any that ** have not been. Update the MIRROR.MMARK table so that it holds the ** marks for the exported files. ** ** Return zero on success and non-zero if the export should be stopped. */ static int gitmirror_send_checkin( FILE *xCmd, /* Write fast-import text on this pipe */ int rid, /* BLOB.RID for the check-in to export */ const char *zUuid, /* BLOB.UUID for the check-in to export */ int *pnLimit, /* Stop when the counter reaches zero */ int fManifest /* MFESTFLG_* values */ ){ Manifest *pMan; /* The check-in to be output */ int i; /* Loop counter */ int iParent; /* Which immediate ancestor is primary. -1 for none */ Stmt q; /* An SQL query */ char *zBranch; /* The branch of the check-in */ char *zMark; /* The Git-name of the check-in */ Blob sql; /* String of SQL for part of the query */ Blob comment; /* The comment text for the check-in */ int nErr = 0; /* Number of errors */ int bPhantomOk; /* True if phantom files should be ignored */ char buf[24]; char *zEmail; /* Contact info for Git committer field */ pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); if( pMan==0 ){ /* Must be a phantom. Return without doing anything, and in particular ** without creating a mark for this check-in. */ gitmirror_message(VERB_NORMAL, "missing check-in: %s\n", zUuid); return 0; } /* Check to see if any parent logins have not yet been processed, and ** if so, create them */ for(i=0; i<pMan->nParent; i++){ char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0); if( zPMark==0 ){ int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pMan->azParent[i]); int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i], pnLimit, fManifest); if( rc || *pnLimit<=0 ){ manifest_destroy(pMan); return 1; } } fossil_free(zPMark); } /* Ignore phantom files on check-ins that are over one year old */ bPhantomOk = db_int(0, "SELECT %.6f<julianday('now','-1 year')", pMan->rDate); /* Make sure all necessary files have been exported */ db_prepare(&q, "SELECT uuid FROM files_of_checkin(%Q)" " WHERE uuid NOT IN (SELECT uuid FROM mirror.mmark)", zUuid ); while( db_step(&q)==SQLITE_ROW ){ const char *zFUuid = db_column_text(&q, 0); int n = gitmirror_send_file(xCmd, zFUuid, bPhantomOk); nErr += n; if( n ) gitmirror_message(VERB_ERROR, "missing file: %s\n", zFUuid); } db_finalize(&q); /* If some required files could not be exported, abandon the check-in ** export */ if( nErr ){ gitmirror_message(VERB_ERROR, "export of %s abandoned due to missing files\n", zUuid); *pnLimit = 0; manifest_destroy(pMan); return 1; } /* Figure out which branch this check-in is a member of */ zBranch = db_text(0, "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=%d", TAG_BRANCH, rid ); if( fossil_strcmp(zBranch,"trunk")==0 ){ assert( gitmirror_mainbranch!=0 ); fossil_free(zBranch); zBranch = mprintf("%s",gitmirror_mainbranch); }else if( zBranch==0 ){ zBranch = mprintf("unknown"); }else{ gitmirror_sanitize_name(zBranch); } /* Export the check-in */ fprintf(xCmd, "commit refs/heads/%s\n", zBranch); fossil_free(zBranch); zMark = gitmirror_find_mark(zUuid,0,1); fprintf(xCmd, "mark %s\n", zMark); fossil_free(zMark); sqlite3_snprintf(sizeof(buf), buf, "%lld", (sqlite3_int64)((pMan->rDate-2440587.5)*86400.0) ); /* ** Check for 'fx_' table from previous Git import, otherwise take contact info ** from user table for <emailaddr> in committer field. If no emailaddr, check ** if username is in email form, otherwise use generic 'username@noemail.net'. */ if (db_table_exists("repository", "fx_git")) { zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser); } else { zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser); } /* Some repo 'info' fields return an empty string hence the second check */ if( zEmail==0 ){ /* If username is in emailaddr form, don't append '@noemail.net' */ if( pMan->zUser==0 || strchr(pMan->zUser, '@')==0 ){ zEmail = mprintf("%s@noemail.net", pMan->zUser); } else { zEmail = fossil_strdup(pMan->zUser); } }else{ char *zTmp = strchr(zEmail, '<'); if( zTmp ){ char *zTmpEnd = strchr(zTmp+1, '>'); char *zNew; int i; if( zTmpEnd ) *(zTmpEnd) = 0; zNew = fossil_strdup(zTmp+1); fossil_free(zEmail); zEmail = zNew; for(i=0; zEmail[i] && !fossil_isspace(zEmail[i]); i++){} zEmail[i] = 0; } } fprintf(xCmd, "# rid=%d\n", rid); fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, zEmail, buf); fossil_free(zEmail); blob_init(&comment, pMan->zComment, -1); if( blob_size(&comment)==0 ){ blob_append(&comment, "(no comment)", -1); } blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid); fprintf(xCmd, "data %d\n%s\n", blob_strlen(&comment), blob_str(&comment)); blob_reset(&comment); iParent = -1; /* Which ancestor is the primary parent */ for(i=0; i<pMan->nParent; i++){ char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0); if( zOther==0 ) continue; if( iParent<0 ){ iParent = i; fprintf(xCmd, "from %s\n", zOther); }else{ fprintf(xCmd, "merge %s\n", zOther); } fossil_free(zOther); } if( iParent>=0 ){ db_prepare(&q, "SELECT filename FROM files_of_checkin(%Q)" " EXCEPT SELECT filename FROM files_of_checkin(%Q)", pMan->azParent[iParent], zUuid ); while( db_step(&q)==SQLITE_ROW ){ fprintf(xCmd, "D %s\n", db_column_text(&q,0)); } db_finalize(&q); } blob_init(&sql, 0, 0); blob_append_sql(&sql, "SELECT filename, uuid, perm FROM files_of_checkin(%Q)", zUuid ); if( pMan->nParent ){ blob_append_sql(&sql, " EXCEPT SELECT filename, uuid, perm FROM files_of_checkin(%Q)", pMan->azParent[0]); } db_prepare(&q, "SELECT x.filename, x.perm," " coalesce(mmark.githash,printf(':%%d',mmark.id))" " FROM (%s) AS x, mirror.mmark" " WHERE mmark.uuid=x.uuid AND isfile", blob_sql_text(&sql) ); blob_reset(&sql); while( db_step(&q)==SQLITE_ROW ){ const char *zFilename = db_column_text(&q,0); const char *zMode = db_column_text(&q,1); const char *zMark = db_column_text(&q,2); const char *zGitMode = "100644"; char *zFNQuoted = 0; if( zMode ){ if( strchr(zMode,'x') ) zGitMode = "100755"; if( strchr(zMode,'l') ) zGitMode = "120000"; } zFNQuoted = gitmirror_quote_filename_if_needed(zFilename); fprintf(xCmd,"M %s %s %s\n", zGitMode, zMark, zFNQuoted); fossil_free(zFNQuoted); } db_finalize(&q); manifest_destroy(pMan); pMan = 0; /* Include Fossil-generated auxiliary files in the check-in */ if( fManifest & MFESTFLG_RAW ){ Blob manifest; content_get(rid, &manifest); sterilize_manifest(&manifest, CFTYPE_MANIFEST); fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n", blob_strlen(&manifest), blob_str(&manifest)); blob_reset(&manifest); } if( fManifest & MFESTFLG_UUID ){ int n = (int)strlen(zUuid); fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n", n, zUuid); } if( fManifest & MFESTFLG_TAGS ){ Blob tagslist; blob_init(&tagslist, 0, 0); get_checkin_taglist(rid, &tagslist); fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n", blob_strlen(&tagslist), blob_str(&tagslist)); blob_reset(&tagslist); } /* The check-in is finished, so decrement the counter */ (*pnLimit)--; return 0; } /* ** Create a new Git repository at zMirror to use as the mirror. ** Try to make zMainBr be the main branch for the new repository. ** ** A side-effect of this routine is that current-working directory ** is changed to zMirror. ** ** If zMainBr is initially NULL, then the return value will be the ** name of the default branch to be used by Git. If zMainBr is ** initially non-NULL, then the return value will be a copy of zMainBr. */ static char *gitmirror_init( const char *zMirror, char *zMainBr ){ char *zCmd; int rc; /* Create a new Git repository at zMirror */ zCmd = mprintf("git init %$", zMirror); gitmirror_message(VERB_NORMAL, "%s\n", zCmd); rc = fossil_system(zCmd); if( rc ){ fossil_fatal("cannot initialize git repository using: %s", zCmd); } fossil_free(zCmd); /* Must be in the new Git repository directory for subsequent commands */ rc = file_chdir(zMirror, 0); if( rc ){ fossil_fatal("cannot change to directory \"%s\"", zMirror); } if( zMainBr ){ /* Set the current branch to zMainBr */ zCmd = mprintf("git symbolic-ref HEAD refs/heads/%s", zMainBr); gitmirror_message(VERB_NORMAL, "%s\n", zCmd); rc = fossil_system(zCmd); if( rc ){ fossil_fatal("git command failed: %s", zCmd); } fossil_free(zCmd); }else{ /* If zMainBr is not specified, then check to see what branch ** name Git chose for itself */ char *z; char zLine[1000]; FILE *xCmd; int i; zCmd = "git symbolic-ref --short HEAD"; gitmirror_message(VERB_NORMAL, "%s\n", zCmd); xCmd = popen(zCmd, "r"); if( xCmd==0 ){ fossil_fatal("git command failed: %s", zCmd); } z = fgets(zLine, sizeof(zLine), xCmd); pclose(xCmd); if( z==0 ){ fossil_fatal("no output from \"%s\"", zCmd); } for(i=0; z[i] && !fossil_isspace(z[i]); i++){} z[i] = 0; zMainBr = fossil_strdup(z); } return zMainBr; } /* ** Implementation of the "fossil git export" command. */ void gitmirror_export_command(void){ const char *zLimit; /* Text of the --limit flag */ int nLimit = 0x7fffffff; /* Numeric value of the --limit flag */ int nTotal = 0; /* Total number of check-ins to export */ char *zMirror; /* Name of the mirror */ char *z; /* Generic string */ char *zCmd; /* git command to run as a subprocess */ const char *zDebug = 0; /* Value of the --debug flag */ const char *zAutoPush = 0; /* Value of the --autopush flag */ char *zMainBr = 0; /* Value of the --mainbranch flag */ char *zPushUrl; /* URL to sync the mirror to */ double rEnd; /* time of most recent export */ int rc; /* Result code */ int bForce; /* Do the export and sync even if no changes*/ int bNeedRepack = 0; /* True if we should run repack at the end */ int fManifest; /* Current "manifest" setting */ int bIfExists; /* The --if-mirrored flag */ FILE *xCmd; /* Pipe to the "git fast-import" command */ FILE *pMarks; /* Git mark files */ Stmt q; /* Queries */ char zLine[200]; /* One line of a mark file */ zDebug = find_option("debug",0,1); db_find_and_open_repository(0, 0); zLimit = find_option("limit", 0, 1); if( zLimit ){ nLimit = (unsigned int)atoi(zLimit); if( nLimit<=0 ) fossil_fatal("--limit must be positive"); } zAutoPush = find_option("autopush",0,1); zMainBr = (char*)find_option("mainbranch",0,1); bForce = find_option("force","f",0)!=0; bIfExists = find_option("if-mirrored",0,0)!=0; gitmirror_verbosity = VERB_NORMAL; while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } verify_all_options(); if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } if( g.argc==4 ){ Blob mirror; file_canonical_name(g.argv[3], &mirror, 0); db_set("last-git-export-repo", blob_str(&mirror), 0); blob_reset(&mirror); } zMirror = db_get("last-git-export-repo", 0); if( zMirror==0 ){ if( bIfExists ) return; fossil_fatal("no Git repository specified"); } if( zMainBr ){ z = fossil_strdup(zMainBr); gitmirror_sanitize_name(z); if( strcmp(z, zMainBr) ){ fossil_fatal("\"%s\" is not a legal branch name for Git", zMainBr); } fossil_free(z); } /* Make sure the GIT repository directory exists */ rc = file_mkdir(zMirror, ExtFILE, 0); if( rc ) fossil_fatal("cannot create directory \"%s\"", zMirror); /* Make sure GIT has been initialized */ z = mprintf("%s/.git", zMirror); if( !file_isdir(z, ExtFILE) ){ zMainBr = gitmirror_init(zMirror, zMainBr); bNeedRepack = 1; } fossil_free(z); /* Make sure the .mirror_state subdirectory exists */ z = mprintf("%s/.mirror_state", zMirror); rc = file_mkdir(z, ExtFILE, 0); if( rc ) fossil_fatal("cannot create directory \"%s\"", z); fossil_free(z); /* Attach the .mirror_state/db database */ db_multi_exec("ATTACH '%q/.mirror_state/db' AS mirror;", zMirror); db_begin_write(); db_multi_exec( "CREATE TABLE IF NOT EXISTS mirror.mconfig(\n" " key TEXT PRIMARY KEY,\n" " Value ANY\n" ") WITHOUT ROWID;\n" "CREATE TABLE IF NOT EXISTS mirror.mmark(\n" " id INTEGER PRIMARY KEY,\n" " uuid TEXT,\n" " isfile BOOLEAN,\n" " githash TEXT,\n" " UNIQUE(uuid,isfile)\n" ");" ); if( !db_table_has_column("mirror","mmark","isfile") ){ db_multi_exec( "ALTER TABLE mirror.mmark RENAME TO mmark_old;" "CREATE TABLE IF NOT EXISTS mirror.mmark(\n" " id INTEGER PRIMARY KEY,\n" " uuid TEXT,\n" " isfile BOOLEAN,\n" " githash TEXT,\n" " UNIQUE(uuid,isfile)\n" ");" "INSERT OR IGNORE INTO mirror.mmark(id,uuid,githash,isfile)" " SELECT id,uuid,githash," " NOT EXISTS(SELECT 1 FROM repository.event, repository.blob" " WHERE event.objid=blob.rid" " AND blob.uuid=mmark_old.uuid)" " FROM mirror.mmark_old;\n" "DROP TABLE mirror.mmark_old;\n" ); } /* Change the autopush setting if the --autopush flag is present */ if( zAutoPush ){ if( is_false(zAutoPush) ){ db_multi_exec("DELETE FROM mirror.mconfig WHERE key='autopush'"); }else{ db_multi_exec( "REPLACE INTO mirror.mconfig(key,value)" "VALUES('autopush',%Q)", zAutoPush ); } } /* Change the mainbranch setting if the --mainbranch flag is present */ if( zMainBr && zMainBr[0] ){ db_multi_exec( "REPLACE INTO mirror.mconfig(key,value)" "VALUES('mainbranch',%Q)", zMainBr ); gitmirror_mainbranch = fossil_strdup(zMainBr); }else{ /* Recover the saved name of the main branch */ gitmirror_mainbranch = db_text("master", "SELECT value FROM mconfig WHERE key='mainbranch'"); } /* See if there is any work to be done. Exit early if not, before starting ** the "git fast-import" command. */ if( !bForce && !db_exists("SELECT 1 FROM event WHERE type IN ('ci','t')" " AND mtime>coalesce((SELECT value FROM mconfig" " WHERE key='start'),0.0)") ){ gitmirror_message(VERB_NORMAL, "no changes\n"); db_commit_transaction(); return; } /* Do we need to include manifest files in the clone? */ fManifest = db_get_manifest_setting(); /* Change to the MIRROR directory so that the Git commands will work */ rc = file_chdir(zMirror, 0); if( rc ) fossil_fatal("cannot change the working directory to \"%s\"", zMirror); /* Start up the git fast-import command */ if( zDebug ){ if( fossil_strcmp(zDebug,"stdout")==0 ){ xCmd = stdout; }else{ xCmd = fopen(zDebug, "wb"); if( xCmd==0 ) fossil_fatal("cannot open file \"%s\" for writing", zDebug); } }else{ zCmd = mprintf("git fast-import" " --export-marks=.mirror_state/marks.txt" " --quiet --done"); gitmirror_message(VERB_NORMAL, "%s\n", zCmd); #ifdef _WIN32 xCmd = popen(zCmd, "wb"); #else xCmd = popen(zCmd, "w"); #endif if( xCmd==0 ){ fossil_fatal("cannot start the \"git fast-import\" command"); } fossil_free(zCmd); } /* Run the export */ rEnd = 0.0; db_multi_exec( "CREATE TEMP TABLE tomirror(objid,mtime,uuid);\n" "INSERT INTO tomirror " "SELECT objid, mtime, blob.uuid FROM event, blob\n" " WHERE type='ci'" " AND mtime>coalesce((SELECT value FROM mconfig WHERE key='start'),0.0)" " AND blob.rid=event.objid" " AND blob.uuid NOT IN (SELECT uuid FROM mirror.mmark WHERE NOT isfile)" " AND NOT EXISTS (SELECT 1 FROM private WHERE rid=blob.rid);" ); nTotal = db_int(0, "SELECT count(*) FROM tomirror"); if( nLimit<nTotal ){ nTotal = nLimit; }else if( nLimit>nTotal ){ nLimit = nTotal; } db_prepare(&q, "SELECT objid, mtime, uuid FROM tomirror ORDER BY mtime" ); while( nLimit && db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); double rMTime = db_column_double(&q, 1); const char *zUuid = db_column_text(&q, 2); if( rMTime>rEnd ) rEnd = rMTime; rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit, fManifest); if( rc ) break; gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal); fflush(stdout); } db_finalize(&q); fprintf(xCmd, "done\n"); if( zDebug ){ if( xCmd!=stdout ) fclose(xCmd); }else{ pclose(xCmd); } gitmirror_message(VERB_NORMAL, "%d check-ins added to the %s\n", nTotal-nLimit, zMirror); /* Read the export-marks file. Transfer the new marks over into ** the import-marks file. */ pMarks = fopen(".mirror_state/marks.txt", "rb"); if( pMarks ){ db_prepare(&q, "UPDATE mirror.mmark SET githash=:githash WHERE id=:id"); while( fgets(zLine, sizeof(zLine), pMarks) ){ int j, k; if( zLine[0]!=':' ) continue; db_bind_int(&q, ":id", atoi(zLine+1)); for(j=1; zLine[j] && zLine[j]!=' '; j++){} if( zLine[j]!=' ' ) continue; j++; if( zLine[j]==0 ) continue; for(k=j; fossil_isalnum(zLine[k]); k++){} zLine[k] = 0; db_bind_text(&q, ":githash", &zLine[j]); db_step(&q); db_reset(&q); } db_finalize(&q); fclose(pMarks); file_delete(".mirror_state/marks.txt"); }else{ fossil_fatal("git fast-import didn't generate a marks file!"); } db_multi_exec( "CREATE INDEX IF NOT EXISTS mirror.mmarkx1 ON mmark(githash);" ); /* Do any tags that have been created since the start time */ db_prepare(&q, "SELECT substr(tagname,5), githash" " FROM (SELECT tagxref.tagid AS xtagid, tagname, rid, max(mtime) AS mtime" " FROM tagxref JOIN tag ON tag.tagid=tagxref.tagid" " WHERE tag.tagname GLOB 'sym-*'" " AND tagxref.tagtype=1" " AND tagxref.mtime > coalesce((SELECT value FROM mconfig" " WHERE key='start'),0.0)" " GROUP BY tagxref.tagid) AS tx" " JOIN blob ON tx.rid=blob.rid" " JOIN mmark ON mmark.uuid=blob.uuid;" ); while( db_step(&q)==SQLITE_ROW ){ char *zTagname = fossil_strdup(db_column_text(&q,0)); const char *zObj = db_column_text(&q,1); char *zTagCmd; gitmirror_sanitize_name(zTagname); zTagCmd = mprintf("git tag -f %$ %$", zTagname, zObj); fossil_free(zTagname); gitmirror_message(VERB_NORMAL, "%s\n", zTagCmd); fossil_system(zTagCmd); fossil_free(zTagCmd); } db_finalize(&q); /* Update all references that might have changed since the start time */ db_prepare(&q, "SELECT" " tagxref.value AS name," " max(event.mtime) AS mtime," " mmark.githash AS gitckin" " FROM tagxref, tag, event, blob, mmark" " WHERE tagxref.tagid=tag.tagid" " AND tagxref.tagtype>0" " AND tag.tagname='branch'" " AND event.objid=tagxref.rid" " AND event.mtime > coalesce((SELECT value FROM mconfig" " WHERE key='start'),0.0)" " AND blob.rid=tagxref.rid" " AND mmark.uuid=blob.uuid" " GROUP BY 1" ); while( db_step(&q)==SQLITE_ROW ){ char *zBrname = fossil_strdup(db_column_text(&q,0)); const char *zObj = db_column_text(&q,2); char *zRefCmd; if( fossil_strcmp(zBrname,"trunk")==0 ){ fossil_free(zBrname); zBrname = fossil_strdup(gitmirror_mainbranch); }else{ gitmirror_sanitize_name(zBrname); } zRefCmd = mprintf("git update-ref \"refs/heads/%s\" %$", zBrname, zObj); fossil_free(zBrname); gitmirror_message(VERB_NORMAL, "%s\n", zRefCmd); fossil_system(zRefCmd); fossil_free(zRefCmd); } db_finalize(&q); /* Update the start time */ if( rEnd>0.0 ){ db_prepare(&q, "REPLACE INTO mirror.mconfig(key,value) VALUES('start',:x)"); db_bind_double(&q, ":x", rEnd); db_step(&q); db_finalize(&q); } db_commit_transaction(); /* Maybe run a git repack */ if( bNeedRepack ){ const char *zRepack = "git repack -adf"; gitmirror_message(VERB_NORMAL, "%s\n", zRepack); fossil_system(zRepack); } /* Optionally do a "git push" */ zPushUrl = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'"); if( zPushUrl ){ char *zPushCmd; UrlData url; if( sqlite3_strglob("http*", zPushUrl)==0 ){ url_parse_local(zPushUrl, 0, &url); zPushCmd = mprintf("git push --mirror %s", url.canonical); }else{ zPushCmd = mprintf("git push --mirror %s", zPushUrl); } gitmirror_message(VERB_NORMAL, "%s\n", zPushCmd); fossil_free(zPushCmd); zPushCmd = mprintf("git push --mirror %$", zPushUrl); rc = fossil_system(zPushCmd); if( rc ){ fossil_fatal("cannot push content using: %s", zPushCmd); }else if( db_is_writeable("repository") ){ db_unprotect(PROTECT_CONFIG); db_multi_exec("REPLACE INTO config(name,value,mtime)" "VALUES('gitpush:%q','{}',now())", zPushUrl); db_protect_pop(); } fossil_free(zPushCmd); } } /* ** Implementation of the "fossil git status" command. ** ** Show the status of a "git export". */ void gitmirror_status_command(void){ char *zMirror; char *z; int n, k; int rc; char *zSql; int bQuiet = 0; int bByAll = 0; /* Undocumented option meaning this command was invoked ** from "fossil all" and should modify output accordingly */ db_find_and_open_repository(0, 0); bQuiet = find_option("quiet","q",0)!=0; bByAll = find_option("by-all",0,0)!=0; verify_all_options(); zMirror = db_get("last-git-export-repo", 0); if( zMirror==0 ){ if( bQuiet ) return; if( bByAll ) return; fossil_print("Git mirror: none\n"); return; } zSql = sqlite3_mprintf("ATTACH '%q/.mirror_state/db' AS mirror", zMirror); if( zSql==0 ) fossil_fatal("out of memory"); g.dbIgnoreErrors++; rc = sqlite3_exec(g.db, zSql, 0, 0, 0); g.dbIgnoreErrors--; sqlite3_free(zSql); if( rc ){ if( bQuiet ) return; if( bByAll ) return; fossil_print("Git mirror: %s (Inactive)\n", zMirror); return; } if( bByAll ){ size_t len = strlen(g.zRepositoryName); int n; if( len>60 ) len = 60; n = (int)(65 - len); fossil_print("%.12c %s %.*c\n", '*', g.zRepositoryName, n, '*'); } fossil_print("Git mirror: %s\n", zMirror); z = db_text(0, "SELECT datetime(value) FROM mconfig WHERE key='start'"); if( z ){ double rAge = db_double(0.0, "SELECT julianday('now') - value" " FROM mconfig WHERE key='start'"); if( rAge>1.0/86400.0 ){ fossil_print("Last export: %s (%z ago)\n", z, human_readable_age(rAge)); }else{ fossil_print("Last export: %s (moments ago)\n", z); } } z = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'"); if( z==0 ){ fossil_print("Autopush: off\n"); }else{ UrlData url; if( sqlite3_strglob("http*", z)==0 ){ url_parse_local(z, 0, &url); fossil_print("Autopush: %s\n", url.canonical); }else{ fossil_print("Autopush: %s\n", z); } fossil_free(z); } n = db_int(0, "SELECT count(*) FROM event" " WHERE type='ci'" " AND mtime>coalesce((SELECT value FROM mconfig" " WHERE key='start'),0.0)" ); z = db_text("master", "SELECT value FROM mconfig WHERE key='mainbranch'"); fossil_print("Main-Branch: %s\n",z); if( n==0 ){ fossil_print("Status: up-to-date\n"); }else{ fossil_print("Status: %d check-in%s awaiting export\n", n, n==1 ? "" : "s"); } n = db_int(0, "SELECT count(*) FROM mmark WHERE isfile"); k = db_int(0, "SELECT count(*) FROM mmark WHERE NOT isfile"); fossil_print("Exported: %d check-ins and %d file blobs\n", k, n); } /* ** COMMAND: git* ** ** Usage: %fossil git SUBCOMMAND ** ** Do incremental import or export operations between Fossil and Git. ** Subcommands: ** ** > fossil git export [MIRROR] [OPTIONS] ** ** Write content from the Fossil repository into the Git repository ** in directory MIRROR. The Git repository is created if it does not ** already exist. If the Git repository does already exist, then ** new content added to fossil since the previous export is appended. ** ** Repeat this command whenever new check-ins are added to the Fossil ** repository in order to reflect those changes into the mirror. If ** the MIRROR option is omitted, the repository from the previous ** invocation is used. ** ** The MIRROR directory will contain a subdirectory named ** ".mirror_state" that contains information that Fossil needs to ** do incremental exports. Do not attempt to manage or edit the files ** in that directory since doing so can disrupt future incremental ** exports. ** ** Options: ** --autopush URL Automatically do a 'git push' to URL. The ** URL is remembered and used on subsequent exports ** to the same repository. Or if URL is "off" the ** auto-push mechanism is disabled ** --debug FILE Write fast-export text to FILE rather than ** piping it into "git fast-import" ** -f|--force Do the export even if nothing has changed ** --if-mirrored No-op if the mirror does not already exist ** --limit N Add no more than N new check-ins to MIRROR. ** Useful for debugging ** --mainbranch NAME Use NAME as the name of the main branch in Git. ** The "trunk" branch of the Fossil repository is ** mapped into this name. "master" is used if ** this option is omitted. ** -q|--quiet Reduce output. Repeat for even less output. ** -v|--verbose More output ** ** > fossil git import MIRROR ** ** TBD... ** ** > fossil git status ** ** Show the status of the current Git mirror, if there is one. ** ** -q|--quiet No output if there is nothing to report */ void gitmirror_command(void){ char *zCmd; int nCmd; if( g.argc<3 ){ usage("SUBCOMMAND ..."); } zCmd = g.argv[2]; nCmd = (int)strlen(zCmd); if( nCmd>2 && strncmp(zCmd,"export",nCmd)==0 ){ gitmirror_export_command(); }else if( nCmd>2 && strncmp(zCmd,"import",nCmd)==0 ){ fossil_fatal("not yet implemented - check back later"); }else if( nCmd>2 && strncmp(zCmd,"status",nCmd)==0 ){ gitmirror_status_command(); }else { fossil_fatal("unknown subcommand \"%s\": should be one of " "\"export\", \"import\", \"status\"", zCmd); } } |
Added src/extcgi.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 | /* ** Copyright (c) 2019 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 file contains code to invoke CGI-based extensions to the ** Fossil server via the /ext webpage. ** ** The /ext webpage acts like a recursive webserver, relaying the ** HTTP request to some other component - usually another CGI. ** ** Before doing the relay, /ext examines the login cookie to see ** if the HTTP request is coming from a validated user, and if so ** /ext sets some additional environment variables that the extension ** CGI script can use. In this way, the extension CGI scripts use the ** same login system as the main repository, and appear to be ** an integrated part of the repository. */ #include "config.h" #include "extcgi.h" #include <assert.h> /* ** These are the environment variables that should be set for CGI ** extension programs: */ static const char *azCgiEnv[] = { "AUTH_TYPE", "AUTH_CONTENT", "CONTENT_LENGTH", "CONTENT_TYPE", "DOCUMENT_ROOT", "FOSSIL_CAPABILITIES", "FOSSIL_NONCE", "FOSSIL_REPOSITORY", "FOSSIL_URI", "FOSSIL_USER", "GATEWAY_INTERFACE", "HTTPS", "HTTP_ACCEPT", /* "HTTP_ACCEPT_ENCODING", // omitted from sub-cgi */ "HTTP_ACCEPT_LANGUAGE", "HTTP_COOKIE", "HTTP_HOST", "HTTP_IF_MODIFIED_SINCE", "HTTP_IF_NONE_MATCH", "HTTP_REFERER", "HTTP_USER_AGENT", "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REMOTE_USER", "REQUEST_METHOD", "REQUEST_SCHEME", "REQUEST_URI", "SCRIPT_DIRECTORY", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_NAME", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE", }; /* ** Check a pathname to determine if it is acceptable for use as ** extension CGI. Some pathnames are excluded for security reasons. ** Return NULL on success or a static error string if there is ** a failure. */ const char *ext_pathname_ok(const char *zName){ int i; const char *zFailReason = 0; for(i=0; zName[i]; i++){ char c = zName[i]; if( (c=='.' || c=='-') && (i==0 || zName[i-1]=='/') ){ zFailReason = "path element begins with '.' or '-'"; break; } if( !fossil_isalnum(c) && c!='_' && c!='-' && c!='.' && c!='/' ){ zFailReason = "illegal character in path"; break; } } return zFailReason; } /* ** The *pzPath input is a pathname obtained from mprintf(). ** ** If ** ** (1) zPathname is the name of a directory, and ** (2) the name ends with "/", and ** (3) the directory contains a file named index.html, index.wiki, ** or index.md (in that order) ** ** then replace the input with a revised name that includes the index.* ** file and return non-zero (true). If any condition is not met, return ** zero and leave the input pathname unchanged. */ static int isDirWithIndexFile(char **pzPath){ static const char *azIndexNames[] = { "index.html", "index.wiki", "index.md" }; int i; if( file_isdir(*pzPath, ExtFILE)!=1 ) return 0; if( sqlite3_strglob("*/", *pzPath)!=0 ) return 0; for(i=0; i<(int)(sizeof(azIndexNames)/sizeof(azIndexNames[0])); i++){ char *zNew = mprintf("%s%s", *pzPath, azIndexNames[i]); if( file_isfile(zNew, ExtFILE) ){ fossil_free(*pzPath); *pzPath = zNew; return 1; } fossil_free(zNew); } return 0; } /* ** WEBPAGE: ext raw-content ** ** Relay an HTTP request to secondary CGI after first checking the ** login credentials and setting auxiliary environment variables ** so that the secondary CGI can be aware of the credentials and ** capabilities of the Fossil user. ** ** The /ext page is only functional if the "extroot: DIR" setting is ** found in the CGI script that launched Fossil, or if the "--extroot DIR" ** flag is present when Fossil is launched using the "server", "ui", or ** "http" commands. DIR must be an absolute pathname (relative to the ** chroot jail) of the root of the file hierarchy that implements the CGI ** functionality. Executable files are CGI. Non-executable files are ** static content. ** ** The path after the /ext is the path to the CGI script or static file ** relative to DIR. For security, this path may not contain characters ** other than ASCII letters or digits, ".", "-", "/", and "_". If the ** "." or "-" characters are present in the path then they may not follow ** a "/". ** ** If the path after /ext ends with "/" and is the name of a directory then ** that directory is searched for files named "index.html", "index.wiki", ** and "index.md" (in that order) and if found, those filenames are ** appended to the path. */ void ext_page(void){ const char *zName = P("name"); /* Path information after /ext */ char *zPath = 0; /* Complete path from extroot */ int nRoot; /* Number of bytes in the extroot name */ char *zScript = 0; /* Name of the CGI script */ int nScript = 0; /* Bytes in the CGI script name */ const char *zFailReason = "???";/* Reason for failure */ int i; /* Loop counter */ const char *zMime = 0; /* MIME type of the reply */ int fdFromChild = -1; /* File descriptor for reading from child */ FILE *toChild = 0; /* FILE for sending to child */ FILE *fromChild = 0; /* FILE for reading from child */ int pidChild = 0; /* Process id of the child */ int rc; /* Reply code from subroutine call */ int nContent = -1; /* Content length */ const char *zPathInfo; /* Original PATH_INFO value */ Blob reply; /* The reply */ char zLine[1000]; /* One line of the CGI reply */ const char *zSrvSw; /* SERVER_SOFTWARE */ zPathInfo = P("PATH_INFO"); login_check_credentials(); blob_init(&reply, 0, 0); if( g.zExtRoot==0 ){ zFailReason = "extroot is not set"; goto ext_not_found; } if( file_is_absolute_path(g.zExtRoot)==0 ){ zFailReason = "extroot is a relative pathname"; goto ext_not_found; } if( zName==0 ){ zFailReason = "no path beyond /ext"; goto ext_not_found; } zFailReason = ext_pathname_ok(zName); if( zFailReason ) goto ext_not_found; zFailReason = "???"; if( file_isdir(g.zExtRoot,ExtFILE)!=1 ){ zFailReason = "extroot is not a directory"; goto ext_not_found; } zPath = mprintf("%s/%s", g.zExtRoot, zName); nRoot = (int)strlen(g.zExtRoot); if( file_isfile(zPath, ExtFILE) || isDirWithIndexFile(&zPath) ){ nScript = (int)strlen(zPath); zScript = zPath; }else{ for(i=nRoot+1; zPath[i]; i++){ char c = zPath[i]; if( c=='/' ){ int isDir, isFile; zPath[i] = 0; isDir = file_isdir(zPath, ExtFILE); isFile = isDir==2 ? file_isfile(zPath, ExtFILE) : 0; zPath[i] = c; if( isDir==0 ){ zFailReason = "path does not match any file or script"; goto ext_not_found; } if( isFile!=0 ){ zScript = mprintf("%.*s", i, zPath); nScript = i; break; } } } } if( nScript==0 ){ zFailReason = "path does not match any file or script"; goto ext_not_found; } assert( nScript>=nRoot+1 ); style_set_current_page("ext/%s", &zScript[nRoot+1]); zMime = mimetype_from_name(zScript); if( zMime==0 ) zMime = "application/octet-stream"; if( !file_isexe(zScript, ExtFILE) ){ /* File is not executable. Must be a regular file. In that case, ** disallow extra path elements */ if( zPath[nScript]!=0 ){ zFailReason = "extra path elements after filename"; goto ext_not_found; } blob_read_from_file(&reply, zScript, ExtFILE); document_render(&reply, zMime, zName, zName); return; } /* If we reach this point, that means we are dealing with an executable ** file name zScript. Run that file as CGI. */ cgi_replace_parameter("DOCUMENT_ROOT", g.zExtRoot); cgi_replace_parameter("SCRIPT_FILENAME", zScript); cgi_replace_parameter("SCRIPT_NAME", mprintf("%T/ext/%T",g.zTop,zScript+nRoot+1)); cgi_replace_parameter("SCRIPT_DIRECTORY", file_dirname(zScript)); cgi_replace_parameter("PATH_INFO", zName + strlen(zScript+nRoot+1)); if( g.zLogin ){ cgi_replace_parameter("REMOTE_USER", g.zLogin); cgi_set_parameter_nocopy("FOSSIL_USER", g.zLogin, 0); } cgi_set_parameter_nocopy("FOSSIL_NONCE", style_nonce(), 0); cgi_set_parameter_nocopy("FOSSIL_REPOSITORY", g.zRepositoryName, 0); cgi_set_parameter_nocopy("FOSSIL_URI", g.zTop, 0); cgi_set_parameter_nocopy("FOSSIL_CAPABILITIES", db_text("","SELECT fullcap(cap) FROM user WHERE login=%Q", g.zLogin ? g.zLogin : "nobody"), 0); zSrvSw = P("SERVER_SOFTWARE"); if( zSrvSw==0 ){ zSrvSw = get_version(); }else{ char *z = mprintf("fossil version %s", get_version()); if( strncmp(zSrvSw,z,strlen(z)-4)!=0 ){ zSrvSw = mprintf("%z, %s", z, zSrvSw); } } cgi_replace_parameter("SERVER_SOFTWARE", zSrvSw); cgi_replace_parameter("GATEWAY_INTERFACE","CGI/1.0"); for(i=0; i<(int)(sizeof(azCgiEnv)/sizeof(azCgiEnv[0])); i++){ (void)P(azCgiEnv[i]); } fossil_clearenv(); for(i=0; i<(int)(sizeof(azCgiEnv)/sizeof(azCgiEnv[0])); i++){ const char *zVal = P(azCgiEnv[i]); if( zVal ) fossil_setenv(azCgiEnv[i], zVal); } fossil_setenv("HTTP_ACCEPT_ENCODING",""); rc = popen2(zScript, &fdFromChild, &toChild, &pidChild, 1); if( rc ){ zFailReason = "cannot exec CGI child process"; goto ext_not_found; } fromChild = fdopen(fdFromChild, "rb"); if( fromChild==0 ){ zFailReason = "cannot open FILE to read from CGI child process"; goto ext_not_found; } if( blob_size(&g.cgiIn)>0 ){ size_t nSent, toSend; unsigned char *data = (unsigned char*)blob_buffer(&g.cgiIn); toSend = (size_t)blob_size(&g.cgiIn); do{ nSent = fwrite(data, 1, toSend, toChild); if( nSent<=0 ){ zFailReason = "unable to send all content to the CGI child process"; goto ext_not_found; } toSend -= nSent; data += nSent; }while( toSend>0 ); fflush(toChild); } if( g.perm.Debug && P("fossil-ext-debug")!=0 ){ /* For users with Debug privilege, if the "fossil-ext-debug" query ** parameter exists, then show raw output from the CGI */ zMime = "text/plain"; }else{ while( fgets(zLine,sizeof(zLine),fromChild) ){ for(i=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){} zLine[i] = 0; if( i==0 ) break; if( fossil_strnicmp(zLine,"Location:",9)==0 ){ fclose(fromChild); fclose(toChild); cgi_redirect(&zLine[10]); /* no return */ }else if( fossil_strnicmp(zLine,"Status:",7)==0 ){ int j; for(i=7; fossil_isspace(zLine[i]); i++){} for(j=i; fossil_isdigit(zLine[j]); j++){} while( fossil_isspace(zLine[j]) ){ j++; } cgi_set_status(atoi(&zLine[i]), &zLine[j]); }else if( fossil_strnicmp(zLine,"Content-Length:",15)==0 ){ nContent = atoi(&zLine[15]); }else if( fossil_strnicmp(zLine,"Content-Type:",13)==0 ){ int j; for(i=13; fossil_isspace(zLine[i]); i++){} for(j=i; zLine[j] && zLine[j]!=';'; j++){} zMime = mprintf("%.*s", j-i, &zLine[i]); }else{ cgi_append_header(zLine); cgi_append_header("\r\n"); } } } blob_read_from_channel(&reply, fromChild, nContent); zFailReason = 0; /* Indicate success */ ext_not_found: fossil_free(zPath); if( fromChild ){ fclose(fromChild); }else if( fdFromChild>2 ){ close(fdFromChild); } if( toChild ) fclose(toChild); if( zFailReason==0 ){ document_render(&reply, zMime, zName, zName); }else{ cgi_set_status(404, "Not Found"); @ <h1>Not Found</h1> @ <p>Page not found: %h(zPathInfo)</p> if( g.perm.Debug ){ @ <p>Reason for failure: %h(zFailReason)</p> } } return; } /* ** Create a temporary SFILE table and fill it with one entry for each file ** in the extension document root directory (g.zExtRoot). The SFILE table ** looks like this: ** ** CREATE TEMP TABLE sfile( ** pathname TEXT PRIMARY KEY, ** isexe BOOLEAN ** ) WITHOUT ROWID; */ void ext_files(void){ Blob base; db_multi_exec( "CREATE TEMP TABLE sfile(\n" " pathname TEXT PRIMARY KEY,\n" " isexe BOOLEAN\n" ") WITHOUT ROWID;" ); blob_init(&base, g.zExtRoot, -1); vfile_scan(&base, blob_size(&base), SCAN_ALL|SCAN_ISEXE, 0, 0, ExtFILE); blob_zero(&base); } /* ** WEBPAGE: extfilelist ** ** List all files in the extension CGI document root and its subfolders. */ void ext_filelist_page(void){ Stmt q; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } ext_files(); style_set_current_feature("extcgi"); style_header("CGI Extension Filelist"); @ <table border="0" cellspacing="0" cellpadding="3"> @ <tbody> db_prepare(&q, "SELECT pathname, isexe FROM sfile" " ORDER BY pathname"); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q,0); int isExe = db_column_int(&q,1); @ <tr> if( ext_pathname_ok(zName)!=0 ){ @ <td><span style="opacity:0.5;">%h(zName)</span></td> @ <td>data file</td> }else{ @ <td><a href="%R/ext/%h(zName)">%h(zName)</a></td> if( isExe ){ @ <td>CGI</td> }else{ @ <td>static content</td> } } @ </tr> } db_finalize(&q); @ </tbody> @ </table> style_finish_page(); } |
Changes to src/file.c.
1 2 3 4 5 6 | /* ** 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".) | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | | | | | | | < | | | | > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > | > | > > > > > > > > > > > > > | > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > > > > > > > > > | > > | < | > > | > > > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > | > > > > > | > | | > > > > > > | | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | > > > | | > > > > > > > > > > > > | > > > > > > > > > > > > > > | < > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | < < < < | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | | > > > > > > > > > | | > > > > > > > > > > > | > | | > | | > | | > > > > | < | > | | | | > > > > > > > > > > | > | | > | | > > > | > | | | < < | > > > > > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | > | > > | | | | > > | < | > > > > | > | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** 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/ ** ******************************************************************************* ** ** File utilities. */ #include "config.h" #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <time.h> #include "file.h" /* ** On Windows, include the Platform SDK header file. */ #ifdef _WIN32 # include <direct.h> # include <windows.h> # include <sys/utime.h> #else # include <sys/time.h> #endif #if INTERFACE /* Many APIs take an eFType argument which must be one of ExtFILE, RepoFILE, ** or SymFILE. ** ** The difference is in the handling of symbolic links. RepoFILE should be ** used for files that are under management by a Fossil repository. ExtFILE ** should be used for files that are not under management. SymFILE is for ** a few special cases such as the "fossil test-tarball" command when we never ** want to follow symlinks. ** ** ExtFILE Symbolic links always refer to the object to which the ** link points. Symlinks are never recognized as symlinks but ** instead always appear to the the target object. ** ** SymFILE Symbolic links always appear to be files whose name is ** the target pathname of the symbolic link. ** ** RepoFILE Like SymFILE if allow-symlinks is true, or like ** ExtFILE if allow-symlinks is false. In other words, ** symbolic links are only recognized as something different ** from files or directories if allow-symlinks is true. */ #include <stdlib.h> #define ExtFILE 0 /* Always follow symlinks */ #define RepoFILE 1 /* Follow symlinks if and only if allow-symlinks is OFF */ #define SymFILE 2 /* Never follow symlinks */ #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)) /* ** File status information for windows systems. */ struct fossilStat { i64 st_size; i64 st_mtime; int st_mode; }; #endif #if defined(_WIN32) || defined(__CYGWIN__) # define fossil_isdirsep(a) (((a) == '/') || ((a) == '\\')) #else # define fossil_isdirsep(a) ((a) == '/') #endif #endif /* INTERFACE */ #if !defined(_WIN32) || !(defined(__MSVCRT__) || defined(_MSC_VER)) /* ** File status information for unix systems */ # define fossilStat stat #endif /* ** On Windows S_ISLNK always returns FALSE. */ #if !defined(S_ISLNK) # define S_ISLNK(x) (0) #endif /* ** Local state information for the file status routines */ static struct { struct fossilStat fileStat; /* File status from last fossil_stat() */ int fileStatValid; /* True if fileStat is valid */ } fx; /* ** Fill *buf with information about zFilename. ** ** If zFilename refers to a symbolic link: ** ** (A) If allow-symlinks is on and eFType is RepoFILE, then fill ** *buf with information about the symbolic link itself. ** ** (B) If allow-symlinks is off or eFType is ExtFILE, then fill ** *buf with information about the object that the symbolic link ** points to. */ static int fossil_stat( const char *zFilename, /* name of file or directory to inspect. */ struct fossilStat *buf, /* pointer to buffer where info should go. */ int eFType /* Look at symlink itself if RepoFILE and enabled. */ ){ int rc; void *zMbcs = fossil_utf8_to_path(zFilename, 0); #if !defined(_WIN32) if( (eFType==RepoFILE && db_allow_symlinks()) || eFType==SymFILE ){ /* Symlinks look like files whose content is the name of the target */ rc = lstat(zMbcs, buf); }else{ /* Symlinks look like the object to which they point */ rc = stat(zMbcs, buf); } #else rc = win32_stat(zMbcs, buf, eFType); #endif fossil_path_free(zMbcs); return rc; } /* ** Clears the fx.fileStat variable and its associated validity flag. */ static void resetStat(){ fx.fileStatValid = 0; memset(&fx.fileStat, 0, sizeof(struct fossilStat)); } /* ** Fill in the fx.fileStat variable for the file named zFilename. ** If zFilename==0, then use the previous value of fx.fileStat if ** there is a previous value. ** ** Return the number of errors. No error messages are generated. */ static int getStat(const char *zFilename, int eFType){ int rc = 0; if( zFilename==0 ){ if( fx.fileStatValid==0 ) rc = 1; }else{ if( fossil_stat(zFilename, &fx.fileStat, eFType)!=0 ){ fx.fileStatValid = 0; rc = 1; }else{ fx.fileStatValid = 1; rc = 0; } } return rc; } /* ** Return the size of a file in bytes. Return -1 if the file does not ** exist. If zFilename is NULL, return the size of the most recently ** stat-ed file. */ i64 file_size(const char *zFilename, int eFType){ return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_size; } /* ** Return the modification time for a file. Return -1 if the file ** does not exist. If zFilename is NULL return the size of the most ** recently stat-ed file. */ i64 file_mtime(const char *zFilename, int eFType){ return getStat(zFilename, eFType) ? -1 : fx.fileStat.st_mtime; } /* ** Return the mode bits for a file. Return -1 if the file does not ** exist. If zFilename is NULL return the size of the most recently ** stat-ed file. */ int file_mode(const char *zFilename, int eFType){ return getStat(zFilename, eFType) ? -1 : (int)(fx.fileStat.st_mode); } /* ** Return TRUE if either of the following are true: ** ** (1) zFilename is an ordinary file ** ** (2) allow_symlinks is on and zFilename is a symbolic link to ** a file, directory, or other object */ int file_isfile_or_link(const char *zFilename){ if( getStat(zFilename, RepoFILE) ){ return 0; /* stat() failed. Return false. */ } return S_ISREG(fx.fileStat.st_mode) || S_ISLNK(fx.fileStat.st_mode); } /* ** Return TRUE if the named file is an ordinary file. Return false ** for directories, devices, fifos, symlinks, etc. */ int file_isfile(const char *zFilename, int eFType){ return getStat(zFilename, eFType) ? 0 : S_ISREG(fx.fileStat.st_mode); } /* ** Create a symbolic link named zLinkFile that points to zTargetFile. ** ** If allow-symlinks is off, create an ordinary file named zLinkFile ** with the name of zTargetFile as its content. **/ void symlink_create(const char *zTargetFile, const char *zLinkFile){ #if !defined(_WIN32) if( db_allow_symlinks() ){ int i, nName; char *zName, zBuf[1000]; nName = strlen(zLinkFile); if( nName>=(int)sizeof(zBuf) ){ zName = mprintf("%s", zLinkFile); }else{ zName = zBuf; memcpy(zName, zLinkFile, nName+1); } nName = file_simplify_name(zName, nName, 0); for(i=1; i<nName; i++){ if( zName[i]=='/' ){ zName[i] = 0; if( file_mkdir(zName, ExtFILE, 1) ){ fossil_fatal_recursive("unable to create directory %s", zName); return; } zName[i] = '/'; } } if( symlink(zTargetFile, zName)!=0 ){ fossil_fatal_recursive("unable to create symlink \"%s\"", zName); } if( zName!=zBuf ) free(zName); }else #endif { Blob content; blob_set(&content, zTargetFile); blob_write_to_file(&content, zLinkFile); blob_reset(&content); } } /* ** Copy symbolic link from zFrom to zTo. */ void symlink_copy(const char *zFrom, const char *zTo){ Blob content; 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). ** ** If eFType is ExtFile then symbolic links are followed and so this ** routine can only return PERM_EXE and PERM_REG. ** ** On windows, this routine returns only PERM_REG. */ int file_perm(const char *zFilename, int eFType){ #if !defined(_WIN32) if( !getStat(zFilename, eFType) ){ if( S_ISREG(fx.fileStat.st_mode) && ((S_IXUSR)&fx.fileStat.st_mode)!=0 ) return PERM_EXE; else if( db_allow_symlinks() && S_ISLNK(fx.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_isexe(const char *zFilename, int eFType){ return file_perm(zFilename, eFType)==PERM_EXE; } /* ** Return TRUE if the named file is a symlink and symlinks are allowed. ** Return false for all other cases. ** ** This routines assumes RepoFILE - that zFilename is always a file ** under management. ** ** On Windows, always return False. */ int file_islink(const char *zFilename){ return file_perm(zFilename, RepoFILE)==PERM_LNK; } /* ** Check every sub-directory of zRoot along the path to zFile. ** If any sub-directory is really an ordinary file or a symbolic link, ** return an integer which is the length of the prefix of zFile which ** is the name of that object. Return 0 if all no non-directory ** objects are found along the path. ** ** Example: Given inputs ** ** zRoot = /home/alice/project1 ** zFile = /home/alice/project1/main/src/js/fileA.js ** ** Look for objects in the following order: ** ** /home/alice/project/main ** /home/alice/project/main/src ** /home/alice/project/main/src/js ** ** If any of those objects exist and are something other than a directory ** then return the length of the name of the first non-directory object ** seen. */ int file_nondir_objects_on_path(const char *zRoot, const char *zFile){ int i = (int)strlen(zRoot); char *z = fossil_strdup(zFile); assert( fossil_strnicmp(zRoot, z, i)==0 ); if( i && zRoot[i-1]=='/' ) i--; while( z[i]=='/' ){ int j, rc; for(j=i+1; z[j] && z[j]!='/'; j++){} if( z[j]!='/' ) break; z[j] = 0; rc = file_isdir(z, SymFILE); if( rc!=1 ){ if( rc==2 ){ fossil_free(z); return j; } break; } z[j] = '/'; i = j; } fossil_free(z); return 0; } /* ** The file named zFile is suppose to be an in-tree file. Check to ** ensure that it will be safe to write to this file by verifying that ** there are no symlinks or other non-directory objects in between the ** root of the check-out and zFile. ** ** If a problem is found, print a warning message (using fossil_warning()) ** and return non-zero. If everything is ok, return zero. */ int file_unsafe_in_tree_path(const char *zFile){ int n; if( !file_is_absolute_path(zFile) ){ fossil_fatal("%s is not an absolute pathname",zFile); } if( fossil_strnicmp(g.zLocalRoot, zFile, (int)strlen(g.zLocalRoot)) ){ fossil_fatal("%s is not a prefix of %s", g.zLocalRoot, zFile); } n = file_nondir_objects_on_path(g.zLocalRoot, zFile); if( n ){ fossil_warning("cannot write to %s because non-directory object %.*s" " is in the way", zFile, n, zFile); } return n; } /* ** Return 1 if zFilename is a directory. Return 0 if zFilename ** does not exist. Return 2 if zFilename exists but is something ** other than a directory. */ int file_isdir(const char *zFilename, int eFType){ int rc; char *zFN; zFN = mprintf("%s", zFilename); file_simplify_name(zFN, -1, 0); rc = getStat(zFN, eFType); if( rc ){ rc = 0; /* It does not exist at all. */ }else if( S_ISDIR(fx.fileStat.st_mode) ){ rc = 1; /* It exists and is a real directory. */ }else{ rc = 2; /* It exists and is something else. */ } free(zFN); return rc; } /* ** Return true (1) if zFilename seems like it seems like a valid ** repository database. */ int file_is_repository(const char *zFilename){ i64 sz; sqlite3 *db = 0; sqlite3_stmt *pStmt = 0; int rc; int i; static const char *azReqTab[] = { "blob", "delta", "rcvfrom", "user", "config" }; if( !file_isfile(zFilename, ExtFILE) ) return 0; sz = file_size(zFilename, ExtFILE); if( sz<35328 ) return 0; if( sz%512!=0 ) return 0; rc = sqlite3_open_v2(zFilename, &db, SQLITE_OPEN_READWRITE, 0); if( rc!=0 ) goto not_a_repo; for(i=0; i<count(azReqTab); i++){ if( sqlite3_table_column_metadata(db, "main", azReqTab[i],0,0,0,0,0,0) ){ goto not_a_repo; } } rc = sqlite3_prepare_v2(db, "SELECT 1 FROM config WHERE name='project-code'", -1, &pStmt, 0); if( rc ) goto not_a_repo; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ) goto not_a_repo; sqlite3_finalize(pStmt); sqlite3_close(db); return 1; not_a_repo: sqlite3_finalize(pStmt); sqlite3_close(db); return 0; } /* ** Wrapper around the access() system call. */ int file_access(const char *zFilename, int flags){ int rc; void *zMbcs = fossil_utf8_to_path(zFilename, 0); #ifdef _WIN32 rc = win32_access(zMbcs, flags); #else rc = access(zMbcs, flags); #endif fossil_path_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_path(zChDir, 1); #ifdef _WIN32 rc = win32_chdir(zPath, bChroot); #else rc = chdir(zPath); if( !rc && bChroot ){ rc = chroot(zPath); if( !rc ) rc = chdir("/"); g.fJail = 1; } #endif fossil_path_free(zPath); return rc; } /* ** Find an unused filename similar to zBase with zSuffix appended. ** ** Make the name relative to the working directory if relFlag is true. ** ** Space to hold the new filename is obtained form mprintf() and should ** be freed by the caller. */ char *file_newname(const char *zBase, const char *zSuffix, int relFlag){ char *z = 0; int cnt = 0; z = mprintf("%s-%s", zBase, zSuffix); while( file_size(z, ExtFILE)>=0 ){ fossil_free(z); z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++); } if( relFlag ){ Blob x; file_relative_name(z, &x, 0); fossil_free(z); z = blob_str(&x); } return z; } /* ** Return the tail of a file pathname. The tail is the last component ** of the path. For example, the tail of "/a/b/c.d" is "c.d". */ const char *file_tail(const char *z){ const char *zTail = z; if( !zTail ) return 0; while( z[0] ){ if( fossil_isdirsep(z[0]) ) zTail = &z[1]; z++; } return zTail; } /* ** Return the directory of a file path name. The directory is all components ** except the last one. For example, the directory of "/a/b/c.d" is "/a/b". ** If there is no directory, NULL is returned; otherwise, the returned memory ** should be freed via fossil_free(). */ char *file_dirname(const char *z){ const char *zTail = file_tail(z); if( zTail && zTail!=z ){ return mprintf("%.*s", (int)(zTail-z-1), z); }else{ return 0; } } /* SQL Function: file_dirname(NAME) ** ** Return the directory for NAME */ void file_dirname_sql_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zName = (const char*)sqlite3_value_text(argv[0]); char *zDir; if( zName==0 ) return; zDir = file_dirname(zName); if( zDir ){ sqlite3_result_text(context,zDir,-1,fossil_free); } } /* ** Rename a file or directory. ** Returns zero upon success. */ int file_rename( const char *zFrom, const char *zTo, int isFromDir, int isToDir ){ int rc; #if defined(_WIN32) wchar_t *zMbcsFrom = fossil_utf8_to_path(zFrom, isFromDir); wchar_t *zMbcsTo = fossil_utf8_to_path(zTo, isToDir); rc = _wrename(zMbcsFrom, zMbcsTo); #else char *zMbcsFrom = fossil_utf8_to_path(zFrom, isFromDir); char *zMbcsTo = fossil_utf8_to_path(zTo, isToDir); rc = rename(zMbcsFrom, zMbcsTo); #endif fossil_path_free(zMbcsTo); fossil_path_free(zMbcsFrom); return rc; } /* ** Copy the content of a file from one place to another. */ 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, ExtFILE, 0, 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); if( file_isexe(zFrom, ExtFILE) ) file_setexe(zTo, 1); } /* ** 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. ** ** This routine assumes RepoFILE as the eFType. In other words, if ** zFilename is a symbolic link, it is the object that zFilename points ** to that is modified. */ int file_setexe(const char *zFilename, int onoff){ int rc = 0; #if !defined(_WIN32) struct stat buf; if( fossil_stat(zFilename, &buf, RepoFILE)!=0 || S_ISLNK(buf.st_mode) || S_ISDIR(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_path(zFilename, 0); utimes(zMbcs, tv); #else struct _utimbuf tb; wchar_t *zMbcs = fossil_utf8_to_path(zFilename, 0); tb.actime = newMTime; tb.modtime = newMTime; _wutime(zMbcs, &tb); #endif fossil_path_free(zMbcs); } /* ** COMMAND: test-set-mtime ** ** Usage: %fossil test-set-mtime FILENAME DATE/TIME ** ** Sets the mtime of the named file to the date/time shown. */ void test_set_mtime(void){ const char *zFile; char *zDate; i64 iMTime; if( g.argc!=4 ){ usage("FILENAME DATE/TIME"); } db_open_or_attach(":memory:", "mem"); iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]); zFile = g.argv[2]; file_set_mtime(zFile, iMTime); iMTime = file_mtime(zFile, RepoFILE); zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime); fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime); } /* ** Delete a file. ** ** If zFilename is a symbolic link, then it is the link itself that is ** removed, not the object that zFilename points to. ** ** Returns zero upon success. */ int file_delete(const char *zFilename){ int rc; #ifdef _WIN32 wchar_t *z = fossil_utf8_to_path(zFilename, 0); rc = _wunlink(z); #else char *z = fossil_utf8_to_path(zFilename, 0); rc = unlink(zFilename); #endif fossil_path_free(z); return rc; } /* SQL Function: file_delete(NAME) ** ** Remove file NAME. Return zero on success and non-zero if anything goes ** wrong. */ void file_delete_sql_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zName = (const char*)sqlite3_value_text(argv[0]); int rc; if( zName==0 ){ rc = 1; }else{ rc = file_delete(zName); } sqlite3_result_int(context, rc); } /* ** Create a directory called zName, if it does not already exist. ** If forceFlag is 1, delete any prior non-directory object ** with the same name. ** ** Return the number of errors. */ int file_mkdir(const char *zName, int eFType, int forceFlag){ int rc = file_isdir(zName, eFType); if( rc==2 ){ if( !forceFlag ) return 1; file_delete(zName); } if( rc!=1 ){ #if defined(_WIN32) wchar_t *zMbcs = fossil_utf8_to_path(zName, 1); rc = _wmkdir(zMbcs); #else char *zMbcs = fossil_utf8_to_path(zName, 1); rc = mkdir(zMbcs, 0755); #endif fossil_path_free(zMbcs); return rc; } return 0; } /* ** Create the tree of directories in which zFilename belongs, if that sequence ** of directories does not already exist. ** ** On success, return zero. On error, return errorReturn if positive, otherwise ** print an error message and abort. */ int file_mkfolder( const char *zFilename, /* Pathname showing directories to be created */ int eFType, /* Follow symlinks if ExtFILE */ int forceFlag, /* Delete non-directory objects in the way */ int errorReturn /* What to do when an error is seen */ ){ int nName, rc = 0; char *zName; nName = strlen(zFilename); zName = mprintf("%s", zFilename); nName = file_simplify_name(zName, nName, 0); while( nName>0 && zName[nName-1]!='/' ){ nName--; } if( nName>1 ){ zName[nName-1] = 0; if( file_isdir(zName, eFType)!=1 ){ rc = file_mkfolder(zName, eFType, forceFlag, errorReturn); if( rc==0 ){ if( file_mkdir(zName, eFType, forceFlag) && file_isdir(zName, eFType)!=1 ){ if( errorReturn <= 0 ){ fossil_fatal_recursive("unable to create directory %s", zName); } rc = errorReturn; } } } } free(zName); return rc; } #if defined(_WIN32) /* ** Returns non-zero if the specified name represents a real directory, i.e. ** not a junction or symbolic link. This is important for some operations, ** e.g. removing directories via _wrmdir(), because its detection of empty ** directories will (apparently) not work right for junctions and symbolic ** links, etc. */ int file_is_normal_dir(wchar_t *zName){ /* ** Mask off attributes, applicable to directories, that are harmless for ** our purposes. This may need to be updated if other attributes should ** be ignored by this function. */ DWORD dwAttributes = GetFileAttributesW(zName); if( dwAttributes==INVALID_FILE_ATTRIBUTES ) return 0; dwAttributes &= ~( FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED ); return dwAttributes==FILE_ATTRIBUTE_DIRECTORY; } /* ** COMMAND: test-is-normal-dir ** ** Usage: %fossil test-is-normal-dir NAME... ** ** Returns non-zero if the specified names represent real directories, i.e. ** not junctions, symbolic links, etc. */ void test_is_normal_dir(void){ int i; for(i=2; i<g.argc; i++){ wchar_t *zMbcs = fossil_utf8_to_path(g.argv[i], 1); fossil_print("ATTRS \"%s\" -> %lx\n", g.argv[i], GetFileAttributesW(zMbcs)); fossil_print("ISDIR \"%s\" -> %d\n", g.argv[i], file_is_normal_dir(zMbcs)); fossil_path_free(zMbcs); } } #endif /* ** 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. */ int file_rmdir(const char *zName){ int rc = file_isdir(zName, RepoFILE); if( rc==2 ) return 1; /* cannot remove normal file */ if( rc==1 ){ #if defined(_WIN32) wchar_t *zMbcs = fossil_utf8_to_path(zName, 1); if( file_is_normal_dir(zMbcs) ){ rc = _wrmdir(zMbcs); }else{ rc = ENOTDIR; /* junction, symbolic link, etc. */ } #else char *zMbcs = fossil_utf8_to_path(zName, 1); rc = rmdir(zName); #endif fossil_path_free(zMbcs); return rc; } return 0; } /* SQL Function: rmdir(NAME) ** ** Try to remove the directory NAME. Return zero on success and non-zero ** for failure. */ void file_rmdir_sql_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zName = (const char*)sqlite3_value_text(argv[0]); int rc; if( zName==0 ){ rc = 1; }else{ rc = file_rmdir(zName); } sqlite3_result_int(context, rc); } /* ** Check the input argument to see if it looks like it has an prefix that ** indicates a remote file. If so, return the tail of the specification, ** which is the name of the file on the remote system. ** ** If the input argument does not have a prefix that makes it look like ** a remote file reference, then return NULL. ** ** Remote files look like: "HOST:PATH" or "USER@HOST:PATH". Host must ** be a valid hostname, meaning it must follow these rules: ** ** * Only characters [-.a-zA-Z0-9]. No spaces or other punctuation ** * Does not begin or end with - ** * Name is two or more characters long (otherwise it might be ** confused with a drive-letter on Windows). ** ** The USER section, if it exists, must not contain the '@' character. */ const char *file_skip_userhost(const char *zIn){ const char *zTail; int n, i; if( zIn[0]==':' ) return 0; zTail = strchr(zIn, ':'); if( zTail==0 ) return 0; if( zTail - zIn > 10000 ) return 0; n = (int)(zTail - zIn); if( n<2 ) return 0; if( zIn[n-1]=='-' || zIn[n-1]=='.' ) return 0; for(i=n-1; i>0 && zIn[i-1]!='@'; i--){ if( !fossil_isalnum(zIn[i]) && zIn[i]!='-' && zIn[i]!='.' ) return 0; } if( zIn[i]=='-' || zIn[i]=='.' || i==1 ) return 0; if( i>1 ){ i -= 2; while( i>=0 ){ if( zIn[i]=='@' ) return 0; i--; } } return zTail+1; } /* ** Return true if the filename given is a valid filename for ** a file in a repository. Valid filenames follow all of the ** following rules: ** ** * Does not begin with "/" ** * Does not contain any path element named "." or ".." ** * Does not contain any of these characters in the path: "\" ** * Does not end with "/". ** * Does not contain two or more "/" characters in a row. ** * Contains at least one character ** ** Invalid UTF8 characters result in a false return if bStrictUtf8 is ** true. If bStrictUtf8 is false, invalid UTF8 characters are silently ** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences ** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters) ** ** The bStrictUtf8 flag is true for new inputs, but is false when parsing ** legacy manifests, for backwards compatibility. */ int file_is_simple_pathname(const char *z, int bStrictUtf8){ int i; unsigned char c = (unsigned char) z[0]; char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; if( c=='/' || c==0 ) return 0; if( c=='.' ){ if( z[1]=='/' || z[1]==0 ) return 0; if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; } for(i=0; (c=(unsigned char)z[i])!=0; i++){ if( c & maskNonAscii ){ if( (z[++i]&0xc0)!=0x80 ){ /* Invalid first continuation byte */ return 0; } if( c<0xc2 ){ /* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */ return 0; }else if( (c&0xe0)==0xe0 ){ /* 3-byte or more */ int unicode; if( c&0x10 ){ /* Unicode characters > U+FFFF are not supported. * Windows XP and earlier cannot handle them. */ return 0; } /* This is a 3-byte UTF-8 character */ unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f); if( unicode <= 0x07ff ){ /* overlong form */ return 0; }else if( unicode>=0xe000 ){ /* U+E000..U+FFFF */ if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){ /* U+E000..U+F8FF are for private use. * U+FFFE..U+FFFF are noncharacters. */ return 0; } else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){ /* U+FDD0..U+FDEF are noncharacters. */ return 0; } }else if( (unicode>=0xd800) && (unicode<=0xdfff) ){ /* U+D800..U+DFFF are for surrogate pairs. */ return 0; } if( (z[++i]&0xc0)!=0x80 ){ /* Invalid second continuation byte */ return 0; } } }else if( bStrictUtf8 && (c=='\\') ){ return 0; } if( c=='/' ){ if( z[i+1]=='/' ) return 0; if( z[i+1]=='.' ){ if( z[i+2]=='/' || z[i+2]==0 ) return 0; if( z[i+2]=='.' && (z[i+3]=='/' || z[i+3]==0) ) return 0; } } } if( z[i-1]=='/' ) return 0; return 1; } int file_is_simple_pathname_nonstrict(const char *z){ unsigned char c = (unsigned char) z[0]; if( c=='/' || c==0 ) return 0; if( c=='.' ){ if( z[1]=='/' || z[1]==0 ) return 0; if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; } while( (z = strchr(z+1, '/'))!=0 ){ if( z[1]=='/' ) return 0; if( z[1]==0 ) return 0; if( z[1]=='.' ){ if( z[2]=='/' || z[2]==0 ) return 0; if( z[2]=='.' && (z[3]=='/' || z[3]==0) ) return 0; } } return 1; } /* ** If the last component of the pathname in z[0]..z[j-1] is something ** other than ".." then back it out and return true. If the last ** component is empty or if it is ".." then return false. */ static int backup_dir(const char *z, int *pJ){ int j = *pJ; int i; if( j<=0 ) return 0; for(i=j-1; i>0 && z[i-1]!='/'; i--){} if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; *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; assert( z!=0 ); if( n<0 ) n = strlen(z); if( n==0 ) return 0; /* 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){} /* Begin copying from z[i] back to z[j]... */ for(j=0; i<n; i++){ if( z[i]=='/' ){ /* Skip over internal "/." directory components */ if( z[i+1]=='.' && (i+2==n || z[i+2]=='/') ){ i += 1; continue; } /* If this is a "/.." directory component then back out the ** previous term of the directory if it is something other than ".." ** or "." */ if( z[i+1]=='.' && i+2<n && z[i+2]=='.' && (i+3==n || z[i+3]=='/') && backup_dir(z, &j) ){ 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 ** ** Usage: %fossil test-simplify-name FILENAME... ** ** Print the simplified versions of each FILENAME. This is used to test ** the file_simplify_name() routine. ** ** If FILENAME is of the form "HOST:PATH" or "USER@HOST:PATH", then remove ** and print the remote host prefix first. This is used to test the ** file_skip_userhost() interface. */ void cmd_test_simplify_name(void){ int i; char *z; const char *zTail; for(i=2; i<g.argc; i++){ zTail = file_skip_userhost(g.argv[i]); if( zTail ){ fossil_print("... ON REMOTE: %.*s\n", (int)(zTail-g.argv[i]), g.argv[i]); z = mprintf("%s", zTail); }else{ z = mprintf("%s", g.argv[i]); } fossil_print("[%s] -> ", z); file_simplify_name(z, -1, 0); fossil_print("[%s]\n", z); fossil_free(z); } } /* ** Get the current working directory. ** ** On windows, the name is converted from unicode to UTF8 and all '\\' ** characters are converted to '/'. No conversions are needed on ** unix. ** ** Store the value of the CWD in zBuf which is nBuf bytes in size. ** or if zBuf==0, allocate space to hold the result using fossil_malloc(). */ char *file_getcwd(char *zBuf, int nBuf){ if( zBuf==0 ){ char zTemp[2000]; return fossil_strdup(file_getcwd(zTemp, sizeof(zTemp))); } #ifdef _WIN32 win32_getcwd(zBuf, nBuf); #else if( getcwd(zBuf, nBuf-1)==0 ){ if( errno==ERANGE ){ fossil_fatal("pwd too big: max %d", nBuf-1); }else{ fossil_fatal("cannot find current working directory; %s", strerror(errno)); } } #endif return zBuf; } /* ** Return true if zPath is an absolute pathname. Return false ** if it is relative. */ int file_is_absolute_path(const char *zPath){ if( fossil_isdirsep(zPath[0]) #if defined(_WIN32) || defined(__CYGWIN__) || (fossil_isalpha(zPath[0]) && zPath[1]==':' && (fossil_isdirsep(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. ** ** See also: file_canonical_name_dup() */ 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)); } /* ** Compute the canonical name of a file. Store that name in ** memory obtained from fossil_malloc() and return a pointer to the ** name. ** ** See also: file_canonical_name() */ char *file_canonical_name_dup(const char *zOrigName){ Blob x; if( zOrigName==0 ) return 0; blob_init(&x, 0, 0); file_canonical_name(zOrigName, &x, 0); return blob_str(&x); } /* ** Convert zPath, which is a relative pathname rooted at zDir, into the ** case preferred by the underlying filesystem. Return the a copy ** of the converted path in memory obtained from fossil_malloc(). ** ** For case-sensitive filesystems, such as on Linux, this routine is ** just fossil_strdup(). But for case-insenstiive but "case preserving" ** filesystems, such as on MacOS or Windows, we want the filename to be ** in the preserved casing. That's what this routine does. */ char *file_case_preferred_name(const char *zDir, const char *zPath){ DIR *d; int i; char *zResult = 0; void *zNative = 0; if( filenames_are_case_sensitive() ){ return fossil_strdup(zPath); } for(i=0; zPath[i] && zPath[i]!='/' && zPath[i]!='\\'; i++){} zNative = fossil_utf8_to_path(zDir, 1); d = opendir(zNative); if( d ){ struct dirent *pEntry; while( (pEntry = readdir(d))!=0 ){ char *zUtf8 = fossil_path_to_utf8(pEntry->d_name); if( fossil_strnicmp(zUtf8, zPath, i)==0 && zUtf8[i]==0 ){ if( zPath[i]==0 ){ zResult = fossil_strdup(zUtf8); }else{ char *zSubDir = mprintf("%s/%s", zDir, zUtf8); char *zSubPath = file_case_preferred_name(zSubDir, &zPath[i+1]); zResult = mprintf("%s/%s", zUtf8, zSubPath); fossil_free(zSubPath); fossil_free(zSubDir); } fossil_path_free(zUtf8); break; } fossil_path_free(zUtf8); } closedir(d); } fossil_path_free(zNative); if( zResult==0 ) zResult = fossil_strdup(zPath); return zResult; } /* ** COMMAND: test-case-filename ** ** Usage: fossil test-case-filename DIRECTORY PATH PATH PATH .... ** ** All the PATH arguments (there must be one at least one) are pathnames ** relative to DIRECTORY. This test command prints the OS-preferred name ** for each PATH in filesystems where case is not significant. */ void test_preferred_fn(void){ int i; if( g.argc<4 ){ usage("DIRECTORY PATH ..."); } for(i=3; i<g.argc; i++){ char *z = file_case_preferred_name(g.argv[2], g.argv[i]); fossil_print("%s -> %s\n", g.argv[i], z); fossil_free(z); } } /* ** The input is the name of an executable, such as one might ** type on a command-line. This routine resolves that name into ** a full pathname. The result is obtained from fossil_malloc() ** and should be freed by the caller. */ char *file_fullexename(const char *zCmd){ #ifdef _WIN32 char *zPath; char *z = 0; const char *zExe = ""; if( sqlite3_strlike("%.exe",zCmd,0)!=0 ) zExe = ".exe"; if( file_is_absolute_path(zCmd) ){ return mprintf("%s%s", zCmd, zExe); } if( strchr(zCmd,'\\')!=0 && strchr(zCmd,'/')!=0 ){ int i; Blob out = BLOB_INITIALIZER; file_canonical_name(zCmd, &out, 0); blob_append(&out, zExe, -1); z = fossil_strdup(blob_str(&out)); blob_reset(&out); for(i=0; z[i]; i++){ if( z[i]=='/' ) z[i] = '\\'; } return z; } z = mprintf(".\\%s%s", zCmd, zExe); if( file_isfile(z, ExtFILE) ){ int i; Blob out = BLOB_INITIALIZER; file_canonical_name(zCmd, &out, 0); blob_append(&out, zExe, -1); z = fossil_strdup(blob_str(&out)); blob_reset(&out); for(i=0; z[i]; i++){ if( z[i]=='/' ) z[i] = '\\'; } return z; } fossil_free(z); zPath = fossil_getenv("PATH"); while( zPath && zPath[0] ){ int n; char *zColon; zColon = strchr(zPath, ';'); n = zColon ? (int)(zColon-zPath) : (int)strlen(zPath); while( n>0 && zPath[n-1]=='\\' ){ n--; } z = mprintf("%.*s\\%s%s", n, zPath, zCmd, zExe); if( file_isfile(z, ExtFILE) ){ return z; } fossil_free(z); if( zColon==0 ) break; zPath = zColon+1; } return fossil_strdup(zCmd); #else char *zPath; char *z; if( zCmd[0]=='/' ){ return fossil_strdup(zCmd); } if( strchr(zCmd,'/')!=0 ){ Blob out = BLOB_INITIALIZER; file_canonical_name(zCmd, &out, 0); z = fossil_strdup(blob_str(&out)); blob_reset(&out); return z; } zPath = fossil_getenv("PATH"); while( zPath && zPath[0] ){ int n; char *zColon; zColon = strchr(zPath, ':'); n = zColon ? (int)(zColon-zPath) : (int)strlen(zPath); z = mprintf("%.*s/%s", n, zPath, zCmd); if( file_isexe(z, ExtFILE) ){ return z; } fossil_free(z); if( zColon==0 ) break; zPath = zColon+1; } return fossil_strdup(zCmd); #endif } /* ** COMMAND: test-which ** ** Usage: %fossil test-which ARGS... ** ** For each argument, search the PATH for the executable with the name ** and print its full pathname. */ void test_which_cmd(void){ int i; for(i=2; i<g.argc; i++){ char *z = file_fullexename(g.argv[i]); fossil_print("%z\n", z); } } /* ** Emits the effective or raw stat() information for the specified ** file or directory, optionally preserving the trailing slash and ** resetting the cached stat() information. */ static void emitFileStat( const char *zPath, int slash, int reset ){ char zBuf[200]; char *z; Blob x; char *zFull; int rc; sqlite3_int64 iMtime; struct fossilStat testFileStat; memset(zBuf, 0, sizeof(zBuf)); blob_zero(&x); file_canonical_name(zPath, &x, slash); zFull = blob_str(&x); fossil_print("[%s] -> [%s]\n", zPath, zFull); memset(&testFileStat, 0, sizeof(struct fossilStat)); rc = fossil_stat(zPath, &testFileStat, 0); fossil_print(" stat_rc = %d\n", rc); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size); fossil_print(" stat_size = %s\n", zBuf); if( g.db==0 ) sqlite3_open(":memory:", &g.db); z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", testFileStat.st_mtime); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", testFileStat.st_mtime, z); fossil_free(z); fossil_print(" stat_mtime = %s\n", zBuf); fossil_print(" stat_mode = 0%o\n", testFileStat.st_mode); memset(&testFileStat, 0, sizeof(struct fossilStat)); rc = fossil_stat(zPath, &testFileStat, 1); fossil_print(" l_stat_rc = %d\n", rc); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size); fossil_print(" l_stat_size = %s\n", zBuf); z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", testFileStat.st_mtime); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", testFileStat.st_mtime, z); fossil_free(z); fossil_print(" l_stat_mtime = %s\n", zBuf); fossil_print(" l_stat_mode = 0%o\n", testFileStat.st_mode); if( reset ) resetStat(); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zPath,ExtFILE)); fossil_print(" file_size(ExtFILE) = %s\n", zBuf); iMtime = file_mtime(zPath, ExtFILE); z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z); fossil_free(z); fossil_print(" file_mtime(ExtFILE) = %s\n", zBuf); fossil_print(" file_mode(ExtFILE) = 0%o\n", file_mode(zPath,ExtFILE)); fossil_print(" file_isfile(ExtFILE) = %d\n", file_isfile(zPath,ExtFILE)); fossil_print(" file_isdir(ExtFILE) = %d\n", file_isdir(zPath,ExtFILE)); if( reset ) resetStat(); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zPath,RepoFILE)); fossil_print(" file_size(RepoFILE) = %s\n", zBuf); iMtime = file_mtime(zPath,RepoFILE); z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z); fossil_free(z); fossil_print(" file_mtime(RepoFILE) = %s\n", zBuf); fossil_print(" file_mode(RepoFILE) = 0%o\n", file_mode(zPath,RepoFILE)); fossil_print(" file_isfile(RepoFILE) = %d\n", file_isfile(zPath,RepoFILE)); fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zPath)); fossil_print(" file_islink = %d\n", file_islink(zPath)); fossil_print(" file_isexe(RepoFILE) = %d\n", file_isexe(zPath,RepoFILE)); fossil_print(" file_isdir(RepoFILE) = %d\n", file_isdir(zPath,RepoFILE)); fossil_print(" file_is_repository = %d\n", file_is_repository(zPath)); fossil_print(" file_is_reserved_name = %d\n", file_is_reserved_name(zFull,-1)); fossil_print(" file_in_cwd = %d\n", file_in_cwd(zPath)); blob_reset(&x); if( reset ) resetStat(); } /* ** COMMAND: test-file-environment ** ** Usage: %fossil test-file-environment FILENAME... ** ** Display the effective file handling subsystem "settings" and then ** display file system information about the files specified, if any. ** ** Options: ** --allow-symlinks BOOLEAN Temporarily turn allow-symlinks on/off ** --open-config Open the configuration database first ** --reset Reset cached stat() info for each file ** --root ROOT Use ROOT as the root of the check-out ** --slash Trailing slashes, if any, are retained */ void cmd_test_file_environment(void){ int i; int slashFlag = find_option("slash",0,0)!=0; int resetFlag = find_option("reset",0,0)!=0; const char *zRoot = find_option("root",0,1); const char *zAllow = find_option("allow-symlinks",0,1); if( find_option("open-config", 0, 0)!=0 ){ Th_OpenConfig(1); } db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0); fossil_print("filenames_are_case_sensitive() = %d\n", filenames_are_case_sensitive()); if( zAllow ){ g.allowSymlinks = !is_false(zAllow); } if( zRoot==0 ) zRoot = g.zLocalRoot; fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks()); fossil_print("local-root = [%s]\n", zRoot); for(i=2; i<g.argc; i++){ char *z; emitFileStat(g.argv[i], slashFlag, resetFlag); z = file_canonical_name_dup(g.argv[i]); fossil_print(" file_canonical_name = %s\n", z); fossil_print(" file_nondir_path = "); if( fossil_strnicmp(zRoot,z,(int)strlen(zRoot))!=0 ){ fossil_print("(--root is not a prefix of this file)\n"); }else{ int n = file_nondir_objects_on_path(zRoot, z); fossil_print("%.*s\n", n, z); } fossil_free(z); } } /* ** COMMAND: test-canonical-name ** ** Usage: %fossil test-canonical-name FILENAME... ** ** Test the operation of the canonical name generator. ** Also test Fossil's ability to measure attributes of a file. */ void cmd_test_canonical_name(void){ int i; Blob x; int slashFlag = find_option("slash",0,0)!=0; blob_zero(&x); for(i=2; i<g.argc; i++){ char zBuf[100]; const char *zName = g.argv[i]; file_canonical_name(zName, &x, slashFlag); fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x)); blob_reset(&x); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName,RepoFILE)); fossil_print(" file_size = %s\n", zBuf); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName,RepoFILE)); fossil_print(" file_mtime = %s\n", zBuf); fossil_print(" file_isfile = %d\n", file_isfile(zName,RepoFILE)); fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zName)); fossil_print(" file_islink = %d\n", file_islink(zName)); fossil_print(" file_isexe = %d\n", file_isexe(zName,RepoFILE)); fossil_print(" file_isdir = %d\n", file_isdir(zName,RepoFILE)); } } /* ** Return TRUE if the given filename is canonical. ** ** Canonical names are full pathnames using "/" not "\" and which ** contain no "/./" or "/../" terms. */ int file_is_canonical(const char *z){ int i; if( z[0]!='/' #if defined(_WIN32) || defined(__CYGWIN__) && (!fossil_isupper(z[0]) || z[1]!=':' || z[2]!='/') #endif ) return 0; for(i=0; z[i]; i++){ if( z[i]=='\\' ) return 0; if( z[i]=='/' ){ if( z[i+1]=='.' ){ if( z[i+2]=='/' || z[i+2]==0 ) return 0; if( z[i+2]=='.' && (z[i+3]=='/' || z[i+3]==0) ) return 0; } } } return 1; } /* ** Return a pointer to the first character in a pathname past the ** drive letter. This routine is a no-op on unix. */ char *file_without_drive_letter(char *zIn){ #ifdef _WIN32 if( fossil_isalpha(zIn[0]) && zIn[1]==':' ) zIn += 2; #endif return zIn; } /* ** Compute a pathname for a file or directory that is relative ** to the current directory. If the slash parameter is non-zero, ** the trailing slash, if any, is retained. */ void file_relative_name(const char *zOrigName, Blob *pOut, int slash){ char *zPath; blob_set(pOut, zOrigName); blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut), slash)); zPath = file_without_drive_letter(blob_buffer(pOut)); if( zPath[0]=='/' ){ int i, j; Blob tmp; char *zPwd; char zBuf[2000]; zPwd = zBuf; file_getcwd(zBuf, sizeof(zBuf)-20); zPwd = file_without_drive_letter(zBuf); i = 1; #if defined(_WIN32) || defined(__CYGWIN__) while( zPath[i] && fossil_tolower(zPwd[i])==fossil_tolower(zPath[i]) ) i++; #else while( zPath[i] && zPwd[i]==zPath[i] ) i++; #endif if( zPath[i]==0 ){ memcpy(&tmp, pOut, sizeof(tmp)); if( zPwd[i]==0 ){ blob_set(pOut, "."); }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); memcpy(pOut, &tmp, sizeof(tmp)); } } /* ** COMMAND: test-relative-name ** ** Test the operation of the relative name generator. */ void cmd_test_relative_name(void){ int i; Blob x; int slashFlag = find_option("slash",0,0)!=0; blob_zero(&x); for(i=2; i<g.argc; i++){ file_relative_name(g.argv[i], &x, slashFlag); fossil_print("%s\n", blob_buffer(&x)); blob_reset(&x); } } /* ** Compute a full path name for a file in the local tree. If ** the absolute flag is non-zero, the computed path will be ** absolute, starting with the root path of the local tree; ** otherwise, it will be relative to the root of the local ** tree. In both cases, the root of the local tree is defined ** by the g.zLocalRoot variable. Return TRUE on success. On ** failure, print and error message and quit if the errFatal ** flag is true. If errFatal is false, then simply return 0. */ int file_tree_name( const char *zOrigName, Blob *pOut, int absolute, int errFatal ){ Blob localRoot; int nLocalRoot; char *zLocalRoot; Blob full; int nFull; char *zFull; int (*xCmp)(const char*,const char*,int); blob_zero(pOut); if( !g.localOpen ){ if( absolute && !file_is_absolute_path(zOrigName) ){ if( errFatal ){ fossil_fatal("relative to absolute needs open check-out tree: %s", zOrigName); } return 0; }else{ /* ** The original path may be relative or absolute; however, without ** an open check-out tree, the only things we can do at this point ** is return it verbatim or generate a fatal error. The caller is ** probably expecting a tree-relative path name will be returned; ** however, most places where this function is called already check ** if the local check-out tree is open, either directly or indirectly, ** which would make this situation impossible. Alternatively, they ** could check the returned path using the file_is_absolute_path() ** function. */ 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]=='/') ){ if( absolute ){ blob_append(pOut, zLocalRoot, nLocalRoot); }else{ blob_append(pOut, ".", 1); } blob_reset(&localRoot); blob_reset(&full); return 1; } if( nFull<=nLocalRoot || xCmp(zLocalRoot, zFull, nLocalRoot) ){ blob_reset(&localRoot); blob_reset(&full); if( errFatal ){ fossil_fatal("file outside of check-out tree: %s", zOrigName); } return 0; } if( absolute ){ if( !file_is_absolute_path(zOrigName) ){ blob_append(pOut, zLocalRoot, nLocalRoot); } blob_append(pOut, zOrigName, -1); blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut), 0)); }else{ blob_append(pOut, &zFull[nLocalRoot], nFull-nLocalRoot); } blob_reset(&localRoot); blob_reset(&full); return 1; } /* ** COMMAND: test-tree-name ** ** Test the operation of the tree name generator. ** ** Options: ** --absolute Return an absolute path instead of a relative one ** --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; int absoluteFlag = find_option("absolute",0,0)!=0; 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, absoluteFlag, 1) ){ fossil_print("%s\n", blob_buffer(&x)); blob_reset(&x); } } } /* ** zFile is the name of a file. Return true if that file is in the ** current working directory (the "pwd" or file_getcwd() directory). ** Return false if the file is someplace else. */ int file_in_cwd(const char *zFile){ char *zFull = file_canonical_name_dup(zFile); char *zCwd = file_getcwd(0,0); size_t nCwd = strlen(zCwd); size_t nFull = strlen(zFull); int rc = 1; int (*xCmp)(const char*,const char*,int); if( filenames_are_case_sensitive() ){ xCmp = fossil_strncmp; }else{ xCmp = fossil_strnicmp; } if( nFull>nCwd+1 && xCmp(zFull,zCwd,nCwd)==0 && zFull[nCwd]=='/' && strchr(zFull+nCwd+1, '/')==0 ){ rc = 1; }else{ rc = 0; } fossil_free(zFull); fossil_free(zCwd); return rc; } /* ** Parse a URI into scheme, host, port, and path. */ void file_parse_uri( const char *zUri, |
︙ | ︙ | |||
478 479 480 481 482 483 484 | blob_set(pPath, &zUri[i]); }else{ blob_set(pPath, "/"); } } /* | | > > > > > > > > > > > | > > > > > > > > > > | > > > | | > | > > | > > > > > > > | > > > > > | > | > < > > > > | < > | > > > > | > > > > > > | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | blob_set(pPath, &zUri[i]); }else{ blob_set(pPath, "/"); } } /* ** Construct a random temporary filename into pBuf where the name of ** the temporary file is derived from zBasis. The suffix on the temp ** file is the same as the suffix on zBasis, and the temp file has ** the root of zBasis in its name. ** ** If zTag is not NULL, then try to create the temp-file using zTag ** as a differentiator. If that fails, or if zTag is NULL, then use ** a bunch of random characters as the tag. ** ** Dangerous characters in zBasis are changed. ** ** See also fossil_temp_filename() and file_time_tempname(); */ void file_tempname(Blob *pBuf, const char *zBasis, const char *zTag){ #if defined(_WIN32) const char *azDirs[] = { 0, /* GetTempPath */ 0, /* TEMP */ 0, /* TMP */ ".", }; #else static const char *azDirs[] = { 0, /* TMPDIR */ "/var/tmp", "/usr/tmp", "/tmp", "/temp", ".", }; #endif static const unsigned char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; unsigned int i; const char *zDir = "."; int cnt = 0; char zRand[16]; int nBasis; const char *zSuffix; char *z; #if defined(_WIN32) wchar_t zTmpPath[MAX_PATH]; if( GetTempPathW(MAX_PATH, zTmpPath) ){ azDirs[0] = fossil_path_to_utf8(zTmpPath); /* Removing trailing \ from the temp path */ z = (char*)azDirs[0]; i = (int)strlen(z)-1; if( i>0 && z[i]=='\\' ) z[i] = 0; } azDirs[1] = fossil_getenv("TEMP"); azDirs[2] = fossil_getenv("TMP"); #else azDirs[0] = fossil_getenv("TMPDIR"); #endif for(i=0; i<count(azDirs); i++){ if( azDirs[i]==0 ) continue; if( !file_isdir(azDirs[i], ExtFILE) ) continue; zDir = azDirs[i]; break; } assert( zBasis!=0 ); zSuffix = 0; for(i=0; zBasis[i]; i++){ if( zBasis[i]=='/' || zBasis[i]=='\\' ){ zBasis += i+1; i = -1; }else if( zBasis[i]=='.' ){ zSuffix = zBasis + i; } } if( zSuffix==0 || zSuffix<=zBasis ){ zSuffix = ""; nBasis = i; }else{ nBasis = (int)(zSuffix - zBasis); } if( nBasis==0 ){ nBasis = 6; zBasis = "fossil"; } do{ blob_zero(pBuf); if( cnt++>20 ) fossil_fatal("cannot generate a temporary filename"); if( zTag==0 ){ const int nRand = sizeof(zRand)-1; sqlite3_randomness(nRand, zRand); for(i=0; i<nRand; i++){ zRand[i] = (char)zChars[ ((unsigned char)zRand[i])%(sizeof(zChars)-1) ]; } zRand[nRand] = 0; zTag = zRand; } blob_appendf(pBuf, "%s/%.*s~%s%s", zDir, nBasis, zBasis, zTag, zSuffix); zTag = 0; for(z=blob_str(pBuf); z!=0 && (z=strpbrk(z,"'\"`;|$&"))!=0; z++){ z[0] = '_'; } }while( file_size(blob_str(pBuf), ExtFILE)>=0 ); #if defined(_WIN32) fossil_path_free((char *)azDirs[0]); fossil_path_free((char *)azDirs[1]); fossil_path_free((char *)azDirs[2]); /* Change all \ characters in the windows path into / so that they can ** be safely passed to a subcommand, such as by gdiff */ z = blob_buffer(pBuf); for(i=0; z[i]; i++) if( z[i]=='\\' ) z[i] = '/'; #else fossil_path_free((char *)azDirs[0]); #endif } /* ** Compute a temporary filename in zDir. The filename is based on ** the current time. ** ** See also fossil_temp_filename() and file_tempname(); */ char *file_time_tempname(const char *zDir, const char *zSuffix){ struct tm *tm; unsigned int r; static unsigned int cnt = 0; time_t t; t = time(0); tm = gmtime(&t); sqlite3_randomness(sizeof(r), &r); return mprintf("%s/%04d%02d%02d%02d%02d%02d%04d%06d%s", zDir, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, cnt++, r%1000000, zSuffix); } /* ** COMMAND: test-tempname ** Usage: fossil test-name [--time SUFFIX] [--tag NAME] BASENAME ... ** ** Generate temporary filenames derived from BASENAME. Use the --time ** option to generate temp names based on the time of day. If --tag NAME ** is specified, try to use NAME as the differentiator in the temp file. ** ** If --time is used, file_time_tempname() generates the filename. ** If BASENAME is present, file_tempname() generates the filename. ** Without --time or BASENAME, fossil_temp_filename() generates the filename. */ void file_test_tempname(void){ int i; const char *zSuffix = find_option("time",0,1); Blob x = BLOB_INITIALIZER; char *z; const char *zTag = find_option("tag",0,1); verify_all_options(); if( g.argc<=2 ){ z = fossil_temp_filename(); fossil_print("%s\n", z); sqlite3_free(z); } for(i=2; i<g.argc; i++){ if( zSuffix ){ z = file_time_tempname(g.argv[i], zSuffix); fossil_print("%s\n", z); fossil_free(z); }else{ file_tempname(&x, g.argv[i], zTag); fossil_print("%s\n", blob_str(&x)); blob_reset(&x); } } } /* ** Return true if a file named zName exists and has identical content ** to the blob pContent. If zName does not exist or if the content is ** different in any way, then return false. ** ** This routine assumes RepoFILE */ int file_is_the_same(Blob *pContent, const char *zName){ i64 iSize; int rc; Blob onDisk; iSize = file_size(zName, RepoFILE); if( iSize<0 ) return 0; if( iSize!=blob_size(pContent) ) return 0; blob_read_from_file(&onDisk, zName, RepoFILE); rc = blob_compare(&onDisk, pContent); blob_reset(&onDisk); return rc==0; } /* ** Return the value of an environment variable as UTF8. ** Use fossil_path_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_path_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; } /* ** Clear all environment variables */ int fossil_clearenv(void){ #ifdef _WIN32 int rc = 0; LPWCH zzEnv = GetEnvironmentStringsW(); if( zzEnv ){ LPCWSTR zEnv = zzEnv; /* read-only */ while( 1 ){ LPWSTR zNewEnv = _wcsdup(zEnv); /* writable */ if( zNewEnv ){ LPWSTR zEquals = wcsstr(zNewEnv, L"="); if( zEquals ){ zEquals[1] = 0; /* no value */ if( zNewEnv==zEquals || _wputenv(zNewEnv)==0 ){ /* via CRT */ /* do nothing */ }else{ zEquals[0] = 0; /* name only */ if( !SetEnvironmentVariableW(zNewEnv, NULL) ){ /* via Win32 */ rc = 1; } } if( rc==0 ){ zEnv += (lstrlenW(zEnv) + 1); /* double NUL term? */ if( zEnv[0]==0 ){ free(zNewEnv); break; /* no more vars */ } } }else{ rc = 1; } }else{ rc = 1; } free(zNewEnv); if( rc!=0 ) break; } if( !FreeEnvironmentStringsW(zzEnv) ){ rc = 2; } }else{ rc = 1; } return rc; #else extern char **environ; environ[0] = 0; return 0; #endif } /* ** Like fopen() but always takes a UTF8 argument. ** ** This function assumes ExtFILE. In other words, symbolic links ** are always followed. */ FILE *fossil_fopen(const char *zName, const char *zMode){ #ifdef _WIN32 wchar_t *uMode = fossil_utf8_to_unicode(zMode); wchar_t *uName = fossil_utf8_to_path(zName, 0); FILE *f = _wfopen(uName, uMode); fossil_path_free(uName); fossil_unicode_free(uMode); #else FILE *f = fopen(zName, zMode); #endif return f; } /* ** Wrapper for freopen() that understands UTF8 arguments. */ FILE *fossil_freopen(const char *zName, const char *zMode, FILE *stream){ #ifdef _WIN32 wchar_t *uMode = fossil_utf8_to_unicode(zMode); wchar_t *uName = fossil_utf8_to_path(zName, 0); FILE *f = _wfreopen(uName, uMode, stream); fossil_path_free(uName); fossil_unicode_free(uMode); #else FILE *f = freopen(zName, zMode, stream); #endif return f; } /* ** Works like fclose() except that: ** ** 1) is a no-op if f is 0 or if it is stdin. ** ** 2) If f is one of (stdout, stderr), it is flushed but not closed. */ void fossil_fclose(FILE *f){ if(f!=0){ if(stdout==f || stderr==f){ fflush(f); }else if(stdin!=f){ fclose(f); } } } /* ** Works like fopen(zName,"wb") except that: ** ** 1) If zName is "-", the stdout handle is returned. ** ** 2) Else file_mkfolder() is used to create all directories ** which lead up to the file before opening it. ** ** 3) It fails fatally if the file cannot be opened. */ FILE *fossil_fopen_for_output(const char *zFilename){ if(zFilename[0]=='-' && zFilename[1]==0){ return stdout; }else{ FILE * p; file_mkfolder(zFilename, ExtFILE, 1, 0); p = fossil_fopen(zFilename, "wb"); if( p==0 ){ #if defined(_WIN32) const char *zReserved = file_is_win_reserved(zFilename); if( zReserved ){ fossil_fatal("cannot open \"%s\" because \"%s\" is " "a reserved name on Windows", zFilename, zReserved); } #endif fossil_fatal("unable to open file \"%s\" for writing", zFilename); } return p; } } /* ** Return non-NULL if zFilename contains pathname elements that ** are reserved on Windows. The returned string is the disallowed ** path element. */ const char *file_is_win_reserved(const char *zPath){ static const char *const azRes[] = { "CON","PRN","AUX","NUL","COM","LPT" }; static char zReturn[5]; int i; while( zPath[0] ){ for(i=0; i<count(azRes); i++){ if( sqlite3_strnicmp(zPath, azRes[i], 3)==0 && ((i>=4 && fossil_isdigit(zPath[3]) && (zPath[4]=='/' || zPath[4]=='.' || zPath[4]==0)) || (i<4 && (zPath[3]=='/' || zPath[3]=='.' || zPath[3]==0))) ){ sqlite3_snprintf(5,zReturn,"%.*s", i>=4 ? 4 : 3, zPath); return zReturn; } } while( zPath[0] && zPath[0]!='/' ) zPath++; while( zPath[0]=='/' ) zPath++; } return 0; } /* ** COMMAND: test-valid-for-windows ** Usage: fossil test-valid-for-windows FILENAME .... ** ** Show which filenames are not valid for Windows */ void file_test_valid_for_windows(void){ int i; for(i=2; i<g.argc; i++){ fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]); } } /* ** Returns non-zero if the specified file extension belongs to a Fossil ** repository file. */ int file_is_repository_extension(const char *zPath){ if( fossil_strcmp(zPath, ".fossil")==0 ) return 1; #if USE_SEE if( fossil_strcmp(zPath, ".efossil")==0 ) return 1; #endif return 0; } /* ** Returns non-zero if the specified path appears to match a file extension ** that should belong to a Fossil repository file. */ int file_contains_repository_extension(const char *zPath){ if( sqlite3_strglob("*.fossil*",zPath)==0 ) return 1; #if USE_SEE if( sqlite3_strglob("*.efossil*",zPath)==0 ) return 1; #endif return 0; } /* ** Returns non-zero if the specified path ends with a file extension that ** should belong to a Fossil repository file. */ int file_ends_with_repository_extension(const char *zPath, int bQual){ if( bQual ){ if( sqlite3_strglob("*/*.fossil", zPath)==0 ) return 1; #if USE_SEE if( sqlite3_strglob("*/*.efossil", zPath)==0 ) return 1; #endif }else{ if( sqlite3_strglob("*.fossil", zPath)==0 ) return 1; #if USE_SEE if( sqlite3_strglob("*.efossil", zPath)==0 ) return 1; #endif } return 0; } /* ** Remove surplus "/" characters from the beginning of a full pathname. ** Extra leading "/" characters are benign on unix. But on Windows ** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil" ** into "C:/fossil/xyz.fossil". Cygwin should behave as Windows here. */ const char *file_cleanup_fullpath(const char *z){ #if defined(_WIN32) || defined(__CYGWIN__) if( z[0]=='/' && fossil_isalpha(z[1]) && z[2]==':' && z[3]=='/' ) z++; #else while( z[0]=='/' && z[1]=='/' ) z++; #endif return z; } /* ** Count the number of objects (files and subdirectories) in a given ** directory. Return the count. Return -1 if the object is not a ** directory. */ int file_directory_size(const char *zDir, const char *zGlob, int omitDotFiles){ void *zNative; DIR *d; int n = -1; zNative = fossil_utf8_to_path(zDir,1); d = opendir(zNative); if( d ){ struct dirent *pEntry; n = 0; while( (pEntry=readdir(d))!=0 ){ if( pEntry->d_name[0]==0 ) continue; if( omitDotFiles && pEntry->d_name[0]=='.' ) continue; if( zGlob ){ char *zUtf8 = fossil_path_to_utf8(pEntry->d_name); int rc = sqlite3_strglob(zGlob, zUtf8); fossil_path_free(zUtf8); if( rc ) continue; } n++; } closedir(d); } fossil_path_free(zNative); return n; } /* ** COMMAND: test-dir-size ** ** Usage: %fossil test-dir-size NAME [GLOB] [--nodots] ** ** Return the number of objects in the directory NAME. If GLOB is ** provided, then only count objects that match the GLOB pattern. ** if --nodots is specified, omit files that begin with ".". */ void test_dir_size_cmd(void){ int omitDotFiles = find_option("nodots",0,0)!=0; const char *zGlob; const char *zDir; verify_all_options(); if( g.argc!=3 && g.argc!=4 ){ usage("NAME [GLOB] [-nodots]"); } zDir = g.argv[2]; zGlob = g.argc==4 ? g.argv[3] : 0; fossil_print("%d\n", file_directory_size(zDir, zGlob, omitDotFiles)); } /* ** Internal helper for touch_cmd(). zAbsName must be resolvable as-is ** to an existing file - this function does not expand/normalize ** it. i.e. it "really should" be an absolute path. zTreeName is ** strictly cosmetic: it is used when dryRunFlag, verboseFlag, or ** quietFlag generate output, and is assumed to be a repo-relative or ** or subdir-relative filename. ** ** newMTime is the file's new timestamp (Unix epoch). ** ** Returns 1 if it sets zAbsName's mtime, 0 if it does not (indicating ** that the file already has that timestamp or a warning was emitted ** or was not found). If dryRunFlag is true then it outputs the name ** of the file it would have timestamped but does not stamp the ** file. If verboseFlag is true, it outputs a message if the file's ** timestamp is actually modified. If quietFlag is true then the ** output of non-fatal warning messages is suppressed. ** ** As a special case, if newMTime is 0 then this function emits a ** warning (unless quietFlag is true), does NOT set the timestamp, and ** returns 0. The timestamp is known to be zero when ** mtime_of_manifest_file() is asked to provide the timestamp for a ** file which is currently undergoing an uncommitted merge (though ** this may depend on exactly where that merge is happening the ** history of the project). */ static int touch_cmd_stamp_one_file(char const *zAbsName, char const *zTreeName, i64 newMtime, int dryRunFlag, int verboseFlag, int quietFlag){ i64 currentMtime; if(newMtime==0){ if( quietFlag==0 ){ fossil_print("SKIPPING timestamp of 0: %s\n", zTreeName); } return 0; } currentMtime = file_mtime(zAbsName, 0); if(currentMtime<0){ fossil_print("SKIPPING: cannot stat file: %s\n", zAbsName); return 0; }else if(currentMtime==newMtime){ return 0; }else if( dryRunFlag!=0 ){ fossil_print( "dry-run: %s\n", zTreeName ); }else{ file_set_mtime(zAbsName, newMtime); if( verboseFlag!=0 ){ fossil_print( "touched %s\n", zTreeName ); } } return 1; } /* ** Internal helper for touch_cmd(). If the given file name is found in ** the given check-out version, which MUST be the check-out version ** currently populating the vfile table, the vfile.mrid value for the ** file is returned, else 0 is returned. zName must be resolvable ** as-is from the vfile table - this function neither expands nor ** normalizes it, though it does compare using the repo's ** filename_collation() preference. */ static int touch_cmd_vfile_mrid( int vid, char const *zName ){ int mrid = 0; static Stmt q = empty_Stmt_m; db_static_prepare(&q, "SELECT vfile.mrid " "FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid " "WHERE vid=:vid AND pathname=:pathname %s", filename_collation()); db_bind_int(&q, ":vid", vid); db_bind_text(&q, ":pathname", zName); if(SQLITE_ROW==db_step(&q)){ mrid = db_column_int(&q, 0); } db_reset(&q); return mrid; } /* ** COMMAND: touch* ** ** Usage: %fossil touch ?OPTIONS? ?FILENAME...? ** ** For each file in the current check-out matching one of the provided ** list of glob patterns and/or file names, the file's mtime is ** updated to a value specified by one of the flags --checkout, ** --checkin, or --now. ** ** If neither glob patterns nor filenames are provided, it operates on ** all files managed by the currently checked-out version. ** ** This command gets its name from the conventional Unix "touch" ** command. ** ** Options: ** --now Stamp each affected file with the current time. ** This is the default behavior. ** -c|--checkin Stamp each affected file with the time of the ** most recent check-in which modified that file ** -C|--checkout Stamp each affected file with the time of the ** currently checked-out version ** -g GLOBLIST Comma-separated list of glob patterns ** -G GLOBFILE Similar to -g but reads its globs from a ** fossil-conventional glob list file ** -v|--verbose Outputs extra information about its globs ** and each file it touches ** -n|--dry-run Outputs which files would require touching, ** but does not touch them ** -q|--quiet Suppress warnings, e.g. when skipping unmanaged ** or out-of-tree files ** ** Only one of --now, --checkin, and --checkout may be used. The ** default is --now. ** ** Only one of -g or -G may be used. If neither is provided and no ** additional filenames are provided, the effect is as if a glob of ** '*' were provided, i.e. all files belonging to the ** currently checked-out version. Note that all glob patterns provided ** via these flags are always evaluated as if they are relative to the ** top of the source tree, not the current working (sub)directory. ** Filenames provided without these flags, on the other hand, are ** treated as relative to the current directory. ** ** As a special case, files currently undergoing an uncommitted merge ** might not get timestamped with --checkin because it may be ** impossible for fossil to choose between multiple potential ** timestamps. A non-fatal warning is emitted for such cases. ** */ void touch_cmd(){ const char * zGlobList; /* -g List of glob patterns */ const char * zGlobFile; /* -G File of glob patterns */ Glob * pGlob = 0; /* List of glob patterns */ int verboseFlag; int dryRunFlag; int vid; /* Check-out version */ int changeCount = 0; /* Number of files touched */ int quietFlag = 0; /* -q|--quiet */ int timeFlag; /* -1==--checkin, 1==--checkout, 0==--now */ i64 nowTime = 0; /* Timestamp of --now or --checkout */ Stmt q; Blob absBuffer = empty_blob; /* Absolute filename buffer */ verboseFlag = find_option("verbose","v",0)!=0; quietFlag = find_option("quiet","q",0)!=0 || g.fQuiet; dryRunFlag = find_option("dry-run","n",0)!=0; zGlobList = find_option("glob", "g",1); zGlobFile = find_option("globfile", "G",1); if(zGlobList && zGlobFile){ fossil_fatal("Options -g and -G may not be used together."); } { int const ci = (find_option("checkin","c",0) || find_option("check-in",0,0)) ? 1 : 0; int const co = find_option("checkout","C",0) ? 1 : 0; int const now = find_option("now",0,0) ? 1 : 0; if(ci + co + now > 1){ fossil_fatal("Options --checkin, --checkout, and --now may " "not be used together."); }else if(co){ timeFlag = 1; if(verboseFlag){ fossil_print("Timestamp = current check-out version.\n"); } }else if(ci){ timeFlag = -1; if(verboseFlag){ fossil_print("Timestamp = check-in in which each file was " "most recently modified.\n"); } }else{ timeFlag = 0; if(verboseFlag){ fossil_print("Timestamp = current system time.\n"); } } } verify_all_options(); db_must_be_within_tree(); vid = db_lget_int("checkout", 0); if(vid==0){ fossil_fatal("Cannot determine check-out version."); } if(zGlobList){ pGlob = *zGlobList ? glob_create(zGlobList) : 0; }else if(zGlobFile){ Blob globs = empty_blob; blob_read_from_file(&globs, zGlobFile, ExtFILE); pGlob = glob_create( globs.aData ); blob_reset(&globs); } if( pGlob && verboseFlag!=0 ){ int i; for(i=0; i<pGlob->nPattern; ++i){ fossil_print("glob: %s\n", pGlob->azPattern[i]); } } db_begin_transaction(); if(timeFlag==0){/*--now*/ nowTime = time(0); }else if(timeFlag>0){/*--checkout: get the check-out manifest's timestamp*/ assert(vid>0); nowTime = db_int64(-1, "SELECT CAST(strftime('%%s'," "(SELECT mtime FROM event WHERE objid=%d)" ") AS INTEGER)", vid); if(nowTime<0){ fossil_fatal("Could not determine check-out version's time!"); } }else{ /* --checkin */ assert(0 == nowTime); } if((pGlob && pGlob->nPattern>0) || g.argc<3){ /* ** We have either (1) globs or (2) no trailing filenames. If there ** are neither globs nor filenames then we operate on all managed ** files. */ db_prepare(&q, "SELECT vfile.mrid, pathname " "FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid " "WHERE vid=%d", vid); while(SQLITE_ROW==db_step(&q)){ int const fid = db_column_int(&q, 0); const char * zName = db_column_text(&q, 1); i64 newMtime = nowTime; char const * zAbs = 0; /* absolute path */ absBuffer.nUsed = 0; assert(timeFlag<0 ? newMtime==0 : newMtime>0); if(pGlob){ if(glob_match(pGlob, zName)==0) continue; } blob_appendf( &absBuffer, "%s%s", g.zLocalRoot, zName ); zAbs = blob_str(&absBuffer); if( newMtime || mtime_of_manifest_file(vid, fid, &newMtime)==0 ){ changeCount += touch_cmd_stamp_one_file( zAbs, zName, newMtime, dryRunFlag, verboseFlag, quietFlag ); } } db_finalize(&q); } glob_free(pGlob); pGlob = 0; if(g.argc>2){ /* ** Trailing filenames on the command line. These require extra ** care to avoid modifying unmanaged or out-of-tree files and ** finding an associated --checkin timestamp. */ int i; Blob treeNameBuf = empty_blob; /* Buffer for file_tree_name(). */ for( i = 2; i < g.argc; ++i, blob_reset(&treeNameBuf) ){ char const * zArg = g.argv[i]; char const * zTreeFile; /* repo-relative filename */ char const * zAbs; /* absolute filename */ i64 newMtime = nowTime; int nameCheck; int fid; /* vfile.mrid of file */ nameCheck = file_tree_name( zArg, &treeNameBuf, 0, 0 ); if(nameCheck==0){ if(quietFlag==0){ fossil_print("SKIPPING out-of-tree file: %s\n", zArg); } continue; } zTreeFile = blob_str(&treeNameBuf); fid = touch_cmd_vfile_mrid( vid, zTreeFile ); if(fid==0){ if(quietFlag==0){ fossil_print("SKIPPING unmanaged file: %s\n", zArg); } continue; } absBuffer.nUsed = 0; blob_appendf(&absBuffer, "%s%s", g.zLocalRoot, zTreeFile); zAbs = blob_str(&absBuffer); if(timeFlag<0){/*--checkin*/ if(mtime_of_manifest_file( vid, fid, &newMtime )!=0){ fossil_fatal("Could not resolve --checkin mtime of %s", zTreeFile); } }else{ assert(newMtime>0); } changeCount += touch_cmd_stamp_one_file( zAbs, zArg, newMtime, dryRunFlag, verboseFlag, quietFlag ); } } db_end_transaction(0); blob_reset(&absBuffer); if( dryRunFlag!=0 ){ fossil_print("dry-run: would have touched %d file(s)\n", changeCount); }else{ fossil_print("Touched %d file(s)\n", changeCount); } } /* ** If zFileName is not NULL and contains a '.', this returns a pointer ** to the position after the final '.', else it returns NULL. As a ** special case, if it ends with a period then a pointer to the ** terminating NUL byte is returned. */ const char * file_extension(const char *zFileName){ const char * zExt = zFileName ? strrchr(zFileName, '.') : 0; return zExt ? &zExt[1] : 0; } /* ** Returns non-zero if the specified file name ends with any reserved name, ** e.g.: _FOSSIL_ or .fslckout. Specifically, it returns 1 for exact match ** or 2 for a tail match on a longer file name. ** ** For the sake of efficiency, zFilename must be a canonical name, e.g. an ** absolute path using only forward slash ('/') as a directory separator. ** ** nFilename must be the length of zFilename. When negative, strlen() will ** be used to calculate it. */ int file_is_reserved_name(const char *zFilename, int nFilename){ const char *zEnd; /* one-after-the-end of zFilename */ int gotSuffix = 0; /* length of suffix (-wal, -shm, -journal) */ assert( zFilename && "API misuse" ); if( nFilename<0 ) nFilename = (int)strlen(zFilename); if( nFilename<8 ) return 0; /* strlen("_FOSSIL_") */ zEnd = zFilename + nFilename; if( nFilename>=12 ){ /* strlen("_FOSSIL_-(shm|wal)") */ /* Check for (-wal, -shm, -journal) suffixes, with an eye towards ** runtime speed. */ if( zEnd[-4]=='-' ){ if( fossil_strnicmp("wal", &zEnd[-3], 3) && fossil_strnicmp("shm", &zEnd[-3], 3) ){ return 0; } gotSuffix = 4; }else if( nFilename>=16 && zEnd[-8]=='-' ){ /*strlen(_FOSSIL_-journal) */ if( fossil_strnicmp("journal", &zEnd[-7], 7) ) return 0; gotSuffix = 8; } if( gotSuffix ){ assert( 4==gotSuffix || 8==gotSuffix ); zEnd -= gotSuffix; nFilename -= gotSuffix; gotSuffix = 1; } assert( nFilename>=8 && "strlen(_FOSSIL_)" ); assert( gotSuffix==0 || gotSuffix==1 ); } switch( zEnd[-1] ){ case '_':{ if( fossil_strnicmp("_FOSSIL_", &zEnd[-8], 8) ) return 0; if( 8==nFilename ) return 1; return zEnd[-9]=='/' ? 2 : gotSuffix; } case 'T': case 't':{ if( nFilename<9 || zEnd[-9]!='.' || fossil_strnicmp(".fslckout", &zEnd[-9], 9) ){ return 0; } if( 9==nFilename ) return 1; return zEnd[-10]=='/' ? 2 : gotSuffix; } default:{ return 0; } } } /* ** COMMAND: test-is-reserved-name ** ** Usage: %fossil test-is-reserved-name FILENAMES... ** ** Passes each given name to file_is_reserved_name() and outputs one ** line per file: the result value of that function followed by the ** name. */ void test_is_reserved_name_cmd(void){ int i; if(g.argc<3){ usage("FILENAME_1 [...FILENAME_N]"); } for( i = 2; i < g.argc; ++i ){ const int check = file_is_reserved_name(g.argv[i], -1); fossil_print("%d %s\n", check, g.argv[i]); } } /* ** Returns 1 if the given directory contains a file named .fslckout, 2 ** if it contains a file named _FOSSIL_, else returns 0. */ int dir_has_ckout_db(const char *zDir){ int rc = 0; char * zCkoutDb = mprintf("%//.fslckout", zDir); if(file_isfile(zCkoutDb, ExtFILE)){ rc = 1; }else{ fossil_free(zCkoutDb); zCkoutDb = mprintf("%//_FOSSIL_", zDir); if(file_isfile(zCkoutDb, ExtFILE)){ rc = 2; } } fossil_free(zCkoutDb); return rc; } |
Added src/fileedit.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 | /* ** Copyright (c) 2020 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 for the /fileedit page and related bits. */ #include "config.h" #include "fileedit.h" #include <assert.h> #include <stdarg.h> /* ** State for the "mini-checkin" infrastructure, which enables the ** ability to commit changes to a single file without a check-out ** db, e.g. for use via an HTTP request. ** ** Use CheckinMiniInfo_init() to cleanly initialize one to a known ** valid/empty default state. ** ** Memory for all non-const pointer members is owned by the ** CheckinMiniInfo instance, unless explicitly noted otherwise, and is ** freed by CheckinMiniInfo_cleanup(). Similarly, each instance owns ** any memory for its own Blob members, but NOT for its pointers to ** blobs. */ struct CheckinMiniInfo { Manifest * pParent; /* parent check-in. Memory is owned by this object. */ char *zParentUuid; /* Full UUID of pParent */ char *zFilename; /* Name of single file to commit. Must be relative to the top of the repo. */ Blob fileContent; /* Content of file referred to by zFilename. */ Blob fileHash; /* Hash of this->fileContent, using the repo's preferred hash method. */ Blob comment; /* Check-in comment text */ char *zCommentMimetype; /* Mimetype of comment. May be NULL */ char *zUser; /* User name */ char *zDate; /* Optionally force this date string (anything supported by date_in_standard_format()). Maybe be NULL. */ Blob *pMfOut; /* If not NULL, checkin_mini() will write a copy of the generated manifest here. This memory is NOT owned by CheckinMiniInfo. */ int filePerm; /* Permissions (via file_perm()) of the input file. We need to store this before calling checkin_mini() because the real input file name may differ from the repo-centric this->zFilename, and checkin_mini() requires the permissions of the original file. For web commits, set this to PERM_REG or (when editing executable scripts) PERM_EXE before calling checkin_mini(). */ int flags; /* Bitmask of fossil_cimini_flags. */ }; typedef struct CheckinMiniInfo CheckinMiniInfo; /* ** CheckinMiniInfo::flags values. */ enum fossil_cimini_flags { /* ** Must have a value of 0. All other flags have unspecified values. */ CIMINI_NONE = 0, /* ** Tells checkin_mini() to use dry-run mode. */ CIMINI_DRY_RUN = 1, /* ** Tells checkin_mini() to allow forking from a non-leaf commit. */ CIMINI_ALLOW_FORK = 1<<1, /* ** Tells checkin_mini() to dump its generated manifest to stdout. */ CIMINI_DUMP_MANIFEST = 1<<2, /* ** By default, content containing what appears to be a merge conflict ** marker is not permitted. This flag relaxes that requirement. */ CIMINI_ALLOW_MERGE_MARKER = 1<<3, /* ** By default mini-checkins are not allowed to be "older" ** than their parent. i.e. they may not have a timestamp ** which predates their parent. This flag bypasses that ** check. */ CIMINI_ALLOW_OLDER = 1<<4, /* ** Indicates that the content of the newly checked-in file is ** converted, if needed, to use the same EOL style as the previous ** version of that file. Only the in-memory/in-repo copies are ** affected, not the original file (if any). */ CIMINI_CONVERT_EOL_INHERIT = 1<<5, /* ** Indicates that the input's EOLs should be converted to Unix-style. */ CIMINI_CONVERT_EOL_UNIX = 1<<6, /* ** Indicates that the input's EOLs should be converted to Windows-style. */ CIMINI_CONVERT_EOL_WINDOWS = 1<<7, /* ** A hint to checkin_mini() to "prefer" creation of a delta manifest. ** It may decide not to for various reasons. */ CIMINI_PREFER_DELTA = 1<<8, /* ** A "stronger hint" to checkin_mini() to prefer creation of a delta ** manifest if it at all can. It will decide not to only if creation ** of a delta is not a realistic option or if it's forbitted by the ** forbid-delta-manifests repo config option. For this to work, it ** must be set together with the CIMINI_PREFER_DELTA flag, but the two ** cannot be combined in this enum. ** ** This option is ONLY INTENDED FOR TESTING, used in bypassing ** heuristics which may otherwise disable generation of a delta on the ** grounds of efficiency (e.g. not generating a delta if the parent ** non-delta only has a few F-cards). */ CIMINI_STRONGLY_PREFER_DELTA = 1<<9, /* ** Tells checkin_mini() to permit the addition of a new file. Normally ** this is disabled because there are hypothetically many cases where ** it could cause the inadvertent addition of a new file when an ** update to an existing was intended, as a side-effect of name-case ** differences. */ CIMINI_ALLOW_NEW_FILE = 1<<10 }; /* ** Initializes p to a known-valid default state. */ static void CheckinMiniInfo_init( CheckinMiniInfo * p ){ memset(p, 0, sizeof(CheckinMiniInfo)); p->flags = CIMINI_NONE; p->filePerm = -1; p->comment = p->fileContent = p->fileHash = empty_blob; } /* ** Frees all memory owned by p, but does not free p. */ static void CheckinMiniInfo_cleanup( CheckinMiniInfo * p ){ blob_reset(&p->comment); blob_reset(&p->fileContent); blob_reset(&p->fileHash); if(p->pParent){ manifest_destroy(p->pParent); } fossil_free(p->zFilename); fossil_free(p->zDate); fossil_free(p->zParentUuid); fossil_free(p->zCommentMimetype); fossil_free(p->zUser); CheckinMiniInfo_init(p); } /* ** Internal helper which returns an F-card perms string suitable for ** writing as-is into a manifest. If it's not empty, it includes a ** leading space to separate it from the F-card's hash field. */ static const char * mfile_permint_mstring(int perm){ switch(perm){ case PERM_EXE: return " x"; case PERM_LNK: return " l"; default: return ""; } } /* ** Given a ManifestFile permission string (or NULL), it returns one of ** PERM_REG, PERM_EXE, or PERM_LNK. */ static int mfile_permstr_int(const char *zPerm){ if(!zPerm || !*zPerm) return PERM_REG; else if(strstr(zPerm,"x")) return PERM_EXE; else if(strstr(zPerm,"l")) return PERM_LNK; else return PERM_REG/*???*/; } /* ** Internal helper for checkin_mini() and friends. Appends an F-card ** for p to pOut. */ static void checkin_mini_append_fcard(Blob *pOut, const ManifestFile *p){ if(p->zUuid){ assert(*p->zUuid); blob_appendf(pOut, "F %F %s%s", p->zName, p->zUuid, mfile_permint_mstring(manifest_file_mperm(p))); if(p->zPrior){ assert(*p->zPrior); blob_appendf(pOut, " %F\n", p->zPrior); }else{ blob_append(pOut, "\n", 1); } }else{ /* File was removed from parent delta. */ blob_appendf(pOut, "F %F\n", p->zName); } } /* ** Handles the F-card parts for create_manifest_mini(). ** ** If asDelta is true, F-cards will be handled as for a delta ** manifest, and the caller MUST have added a B-card to pOut before ** calling this. ** ** Returns 1 on success, 0 on error, and writes any error message to ** pErr (if it's not NULL). The only non-immediately-fatal/panic error ** is if pCI->filePerm is PERM_LNK or pCI would update a PERM_LNK ** in-repo file. */ static int create_manifest_mini_fcards( Blob * pOut, CheckinMiniInfo * pCI, int asDelta, Blob * pErr){ int wroteThisCard = 0; const ManifestFile * pFile; int (*fncmp)(char const *, char const *) = /* filename comparator */ filenames_are_case_sensitive() ? fossil_strcmp : fossil_stricmp; #define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0 #define write_this_card(NAME) \ blob_appendf(pOut, "F %F %b%s\n", (NAME), &pCI->fileHash, \ mfile_permint_mstring(pCI->filePerm)); \ wroteThisCard = 1 assert(pCI->filePerm!=PERM_LNK && "This should have been validated before."); assert(pCI->filePerm==PERM_REG || pCI->filePerm==PERM_EXE); if(PERM_LNK==pCI->filePerm){ goto err_no_symlink; } manifest_file_rewind(pCI->pParent); if(asDelta!=0 && (pCI->pParent->zBaseline==0 || pCI->pParent->nFile==0)){ /* Parent is a baseline or a delta with no F-cards, so this is ** the simplest case: create a delta with a single F-card. */ pFile = manifest_file_find(pCI->pParent, pCI->zFilename); if(pFile!=0 && manifest_file_mperm(pFile)==PERM_LNK){ goto err_no_symlink; } write_this_card(pFile ? pFile->zName : pCI->zFilename); return 1; } while(1){ int cmp; if(asDelta==0){ pFile = manifest_file_next(pCI->pParent, 0); }else{ /* Parent is a delta manifest with F-cards. Traversal of delta ** manifest file entries is normally done via ** manifest_file_next(), which takes into account the ** differences between the delta and its parent and returns ** F-cards from both. Each successive delta from the same ** baseline includes all F-card changes from the previous ** deltas, so we instead clone the parent's F-cards except for ** the one (if any) which matches the new file. */ pFile = pCI->pParent->iFile < pCI->pParent->nFile ? &pCI->pParent->aFile[pCI->pParent->iFile++] : 0; } if(0==pFile) break; cmp = fncmp(pFile->zName, pCI->zFilename); if(cmp<0){ checkin_mini_append_fcard(pOut,pFile); }else{ if(cmp==0 || 0==wroteThisCard){ assert(0==wroteThisCard); if(PERM_LNK==manifest_file_mperm(pFile)){ goto err_no_symlink; } write_this_card(cmp==0 ? pFile->zName : pCI->zFilename); } if(cmp>0){ assert(wroteThisCard!=0); checkin_mini_append_fcard(pOut,pFile); } } } if(wroteThisCard==0){ write_this_card(pCI->zFilename); } return 1; err_no_symlink: mf_err((pErr,"Cannot commit or overwrite symlinks " "via mini-checkin.")); return 0; #undef write_this_card #undef mf_err } /* ** Creates a manifest file, written to pOut, from the state in the ** fully-populated and semantically valid pCI argument. pCI is not ** *semantically* modified by this routine but cannot be const because ** blob_str() may need to NUL-terminate any given blob. ** ** Returns true on success. On error, returns 0 and, if pErr is not ** NULL, writes an error message there. ** ** Intended only to be called via checkin_mini() or routines which ** have already completely vetted pCI for semantic validity. */ static int create_manifest_mini( Blob * pOut, CheckinMiniInfo * pCI, Blob * pErr){ Blob zCard = empty_blob; /* Z-card checksum */ int asDelta = 0; #define mf_err(EXPR) if(pErr) blob_appendf EXPR; return 0 assert(blob_str(&pCI->fileHash)); assert(pCI->pParent); assert(pCI->zFilename); assert(pCI->zUser); assert(pCI->zDate); /* Potential TODOs include... ** ** - Maybe add support for tags. Those can be edited via /info page, ** and feel like YAGNI/feature creep for this purpose. */ blob_zero(pOut); manifest_file_rewind(pCI->pParent) /* force load of baseline */; /* Determine whether we want to create a delta manifest... */ if((CIMINI_PREFER_DELTA & pCI->flags) && ((CIMINI_STRONGLY_PREFER_DELTA & pCI->flags) || (pCI->pParent->pBaseline ? pCI->pParent->pBaseline : pCI->pParent)->nFile > 15 /* 15 is arbitrary: don't create a delta when there is only a ** tiny gain for doing so. That heuristic is not *quite* ** right, in that when we're deriving from another delta, we ** really should compare the F-card count between it and its ** baseline, and create a delta if the baseline has (say) ** twice or more as many F-cards as the previous delta. */) && !db_get_boolean("forbid-delta-manifests",0) ){ asDelta = 1; blob_appendf(pOut, "B %s\n", pCI->pParent->zBaseline ? pCI->pParent->zBaseline : pCI->zParentUuid); } if(blob_size(&pCI->comment)!=0){ blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment)); }else{ blob_append(pOut, "C (no\\scomment)\n", 16); } blob_appendf(pOut, "D %s\n", pCI->zDate); if(create_manifest_mini_fcards(pOut,pCI,asDelta,pErr)==0){ return 0; } if(pCI->zCommentMimetype!=0 && pCI->zCommentMimetype[0]!=0){ blob_appendf(pOut, "N %F\n", pCI->zCommentMimetype); } blob_appendf(pOut, "P %s\n", pCI->zParentUuid); blob_appendf(pOut, "U %F\n", pCI->zUser); md5sum_blob(pOut, &zCard); blob_appendf(pOut, "Z %b\n", &zCard); blob_reset(&zCard); return 1; #undef mf_err } /* ** A so-called "single-file/mini/web check-in" is a slimmed-down form ** of the check-in command which accepts only a single file and is ** intended to accept edits to a file via the web interface or from ** the CLI from outside of a check-out. ** ** Being fully non-interactive is a requirement for this function, ** thus it cannot perform autosync or similar activities (which ** includes checking for repo locks). ** ** This routine uses the state from the given fully-populated pCI ** argument to add pCI->fileContent to the database, and create and ** save a manifest for that change. Ownership of pCI and its contents ** are unchanged. ** ** This function may may modify pCI as follows: ** ** - If one of Manifest pCI->pParent or pCI->zParentUuid are NULL, ** then the other will be assigned based on its counterpart. Both ** may not be NULL. ** ** - pCI->zDate is normalized to/replaced with a valid date/time ** string. If its original value cannot be validated then ** this function fails. If pCI->zDate is NULL, the current time ** is used. ** ** - If the CIMINI_CONVERT_EOL_INHERIT flag is set, ** pCI->fileContent appears to be plain text, and its line-ending ** style differs from its previous version, it is converted to the ** same EOL style as the previous version. If this is done, the ** pCI->fileHash is re-computed. Note that only pCI->fileContent, ** not the original file, is affected by the conversion. ** ** - Else if one of the CIMINI_CONVERT_EOL_WINDOWS or ** CIMINI_CONVERT_EOL_UNIX flags are set, pCI->fileContent is ** converted, if needed, to the corresponding EOL style. ** ** - If EOL conversion takes place, pCI->fileHash is re-calculated. ** ** - If pCI->fileHash is empty, this routine populates it with the ** repository's preferred hash algorithm (after any EOL conversion). ** ** - pCI->comment may be converted to Unix-style newlines. ** ** pCI's ownership is not modified. ** ** This function validates pCI's state and fails if any validation ** fails. ** ** On error, returns false (0) and, if pErr is not NULL, writes a ** diagnostic message there. ** ** Returns true on success. If pRid is not NULL, the RID of the ** resulting manifest is written to *pRid. ** ** The check-in process is largely influenced by pCI->flags, and that ** must be populated before calling this. See the fossil_cimini_flags ** enum for the docs for each flag. */ static int checkin_mini(CheckinMiniInfo * pCI, int *pRid, Blob * pErr){ Blob mf = empty_blob; /* output manifest */ int rid = 0, frid = 0; /* various RIDs */ int isPrivate; /* whether this is private content or not */ ManifestFile * zFilePrev; /* file entry from pCI->pParent */ int prevFRid = 0; /* RID of file's prev. version */ #define ci_err(EXPR) if(pErr!=0){blob_appendf EXPR;} goto ci_error db_begin_transaction(); if(pCI->pParent==0 && pCI->zParentUuid==0){ ci_err((pErr, "Cannot determine parent version.")); } else if(pCI->pParent==0){ pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0); if(pCI->pParent==0){ ci_err((pErr,"Cannot load manifest for [%S].", pCI->zParentUuid)); } }else if(pCI->zParentUuid==0){ pCI->zParentUuid = rid_to_uuid(pCI->pParent->rid); assert(pCI->zParentUuid); } assert(pCI->pParent->rid>0); if(leaf_is_closed(pCI->pParent->rid)){ ci_err((pErr,"Cannot commit to a closed leaf.")); /* Remember that in order to override this we'd also need to ** cancel TAG_CLOSED on pCI->pParent. There would seem to be no ** reason we can't do that via the generated manifest, but the ** commit command does not offer that option, so mini-checkin ** probably shouldn't, either. */ } if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){ ci_err((pErr,"No such user: %s", pCI->zUser)); } if(!(CIMINI_ALLOW_FORK & pCI->flags) && !is_a_leaf(pCI->pParent->rid)){ ci_err((pErr,"Parent [%S] is not a leaf and forking is disabled.", pCI->zParentUuid)); } if(!(CIMINI_ALLOW_MERGE_MARKER & pCI->flags) && contains_merge_marker(&pCI->fileContent)){ ci_err((pErr,"Content appears to contain a merge conflict marker.")); } if(!file_is_simple_pathname(pCI->zFilename, 1)){ ci_err((pErr,"Invalid filename for use in a repository: %s", pCI->zFilename)); } if(!(CIMINI_ALLOW_OLDER & pCI->flags) && !checkin_is_younger(pCI->pParent->rid, pCI->zDate)){ ci_err((pErr,"Check-in time (%s) may not be older " "than its parent (%z).", pCI->zDate, db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%lf)", pCI->pParent->rDate) )); } { /* ** Normalize the timestamp. We don't use date_in_standard_format() ** because that has side-effects we don't want to trigger here. */ char * zDVal = db_text( 0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)", pCI->zDate ? pCI->zDate : "now"); if(zDVal==0 || zDVal[0]==0){ fossil_free(zDVal); ci_err((pErr,"Invalid timestamp string: %s", pCI->zDate)); } fossil_free(pCI->zDate); pCI->zDate = zDVal; } { /* Confirm that only one EOL policy is in place. */ int n = 0; if(CIMINI_CONVERT_EOL_INHERIT & pCI->flags) ++n; if(CIMINI_CONVERT_EOL_UNIX & pCI->flags) ++n; if(CIMINI_CONVERT_EOL_WINDOWS & pCI->flags) ++n; if(n>1){ ci_err((pErr,"More than 1 EOL conversion policy was specified.")); } } /* Potential TODOs include: ** ** - Commit allows an empty check-in only with a flag, but we ** currently disallow an empty check-in entirely. Conform with ** commit? ** ** Non-TODOs: ** ** - Check for a commit lock would require auto-sync, which this ** code cannot do if it's going to be run via a web page. */ /* ** Confirm that pCI->zFilename can be found in pCI->pParent. If ** not, fail unless the CIMINI_ALLOW_NEW_FILE flag is set. This is ** admittedly an artificial limitation, not strictly necessary. We ** do it to hopefully reduce the chance of an "oops" where file ** X/Y/z gets committed as X/Y/Z or X/y/z due to a typo or ** case-sensitivity mismatch between the user/repo/filesystem, or ** some such. */ manifest_file_rewind(pCI->pParent); zFilePrev = manifest_file_find(pCI->pParent, pCI->zFilename); if(!(CIMINI_ALLOW_NEW_FILE & pCI->flags) && (!zFilePrev || !zFilePrev->zUuid/*was removed from parent delta manifest*/) ){ ci_err((pErr,"File [%s] not found in manifest [%S]. " "Adding new files is currently not permitted.", pCI->zFilename, pCI->zParentUuid)); }else if(zFilePrev && manifest_file_mperm(zFilePrev)==PERM_LNK){ ci_err((pErr,"Cannot save a symlink via a mini-checkin.")); } if(zFilePrev){ prevFRid = fast_uuid_to_rid(zFilePrev->zUuid); } if(((CIMINI_CONVERT_EOL_INHERIT & pCI->flags) || (CIMINI_CONVERT_EOL_UNIX & pCI->flags) || (CIMINI_CONVERT_EOL_WINDOWS & pCI->flags)) && blob_size(&pCI->fileContent)>0 ){ /* Convert to the requested EOL style. Note that this inherently ** runs a risk of breaking content, e.g. string literals which ** contain embedded newlines. Note that HTML5 specifies that ** form-submitted TEXTAREA content gets normalized to CRLF-style: ** ** https://html.spec.whatwg.org/#the-textarea-element */ const int pseudoBinary = LOOK_LONG | LOOK_NUL; const int lookFlags = LOOK_CRLF | LOOK_LONE_LF | pseudoBinary; const int lookNew = looks_like_utf8( &pCI->fileContent, lookFlags ); if(!(pseudoBinary & lookNew)){ int rehash = 0; /*fossil_print("lookNew=%08x\n",lookNew);*/ if(CIMINI_CONVERT_EOL_INHERIT & pCI->flags){ Blob contentPrev = empty_blob; int lookOrig, nOrig; content_get(prevFRid, &contentPrev); lookOrig = looks_like_utf8(&contentPrev, lookFlags); nOrig = blob_size(&contentPrev); blob_reset(&contentPrev); /*fossil_print("lookOrig=%08x\n",lookOrig);*/ if(nOrig>0 && lookOrig!=lookNew){ /* If there is a newline-style mismatch, adjust the new ** content version to the previous style, then re-hash the ** content. Note that this means that what we insert is NOT ** what's in the filesystem. */ if(!(lookOrig & LOOK_CRLF) && (lookNew & LOOK_CRLF)){ /* Old has Unix-style, new has Windows-style. */ blob_to_lf_only(&pCI->fileContent); rehash = 1; }else if((lookOrig & LOOK_CRLF) && !(lookNew & LOOK_CRLF)){ /* Old has Windows-style, new has Unix-style. */ blob_add_cr(&pCI->fileContent); rehash = 1; } } }else{ const int oldSize = blob_size(&pCI->fileContent); if(CIMINI_CONVERT_EOL_UNIX & pCI->flags){ if(LOOK_CRLF & lookNew){ blob_to_lf_only(&pCI->fileContent); } }else{ assert(CIMINI_CONVERT_EOL_WINDOWS & pCI->flags); if(!(LOOK_CRLF & lookNew)){ blob_add_cr(&pCI->fileContent); } } if((int)blob_size(&pCI->fileContent)!=oldSize){ rehash = 1; } } if(rehash!=0){ hname_hash(&pCI->fileContent, 0, &pCI->fileHash); } } }/* end EOL conversion */ if(blob_size(&pCI->fileHash)==0){ /* Hash the content if it's not done already... */ hname_hash(&pCI->fileContent, 0, &pCI->fileHash); assert(blob_size(&pCI->fileHash)>0); } if(zFilePrev){ /* Has this file been changed since its previous commit? Note ** that we have to delay this check until after the potentially ** expensive EOL conversion. */ assert(blob_size(&pCI->fileHash)); if(0==fossil_strcmp(zFilePrev->zUuid, blob_str(&pCI->fileHash)) && manifest_file_mperm(zFilePrev)==pCI->filePerm){ ci_err((pErr,"File is unchanged. Not committing.")); } } #if 1 /* Do we really want to normalize comment EOLs? Web-posting will ** submit them in CRLF or LF format, depending on how exactly the ** content is submitted (FORM (CRLF) or textarea-to-POST (LF, at ** least in theory)). */ blob_to_lf_only(&pCI->comment); #endif /* Create, save, deltify, and crosslink the manifest... */ if(create_manifest_mini(&mf, pCI, pErr)==0){ return 0; } isPrivate = content_is_private(pCI->pParent->rid); rid = content_put_ex(&mf, 0, 0, 0, isPrivate); if(pCI->flags & CIMINI_DUMP_MANIFEST){ fossil_print("%b", &mf); } if(pCI->pMfOut!=0){ /* Cross-linking clears mf, so we have to copy it, ** instead of taking over its memory. */ blob_reset(pCI->pMfOut); blob_append(pCI->pMfOut, blob_buffer(&mf), blob_size(&mf)); } content_deltify(rid, &pCI->pParent->rid, 1, 0); manifest_crosslink(rid, &mf, 0); blob_reset(&mf); /* Save and deltify the file content... */ frid = content_put_ex(&pCI->fileContent, blob_str(&pCI->fileHash), 0, 0, isPrivate); if(zFilePrev!=0){ assert(prevFRid>0); content_deltify(frid, &prevFRid, 1, 0); } db_end_transaction((CIMINI_DRY_RUN & pCI->flags) ? 1 : 0); if(pRid!=0){ *pRid = rid; } return 1; ci_error: assert(db_transaction_nesting_depth()>0); db_end_transaction(1); return 0; #undef ci_err } /* ** COMMAND: test-ci-mini ** ** This is an on-going experiment, subject to change or removal at ** any time. ** ** Usage: %fossil test-ci-mini ?OPTIONS? FILENAME ** ** where FILENAME is a repo-relative name as it would appear in the ** vfile table. ** ** Options: ** -R|--repository REPO The repository file to commit to ** --as FILENAME The repository-side name of the input ** file, relative to the top of the ** repository. Default is the same as the ** input file name. ** -m|--comment COMMENT Required check-in comment ** -M|--comment-file FILE Reads check-in comment from the given file ** -r|--revision VERSION Commit from this version. Default is ** the check-out version (if available) or ** trunk (if used without a check-out). ** --allow-fork Allows the commit to be made against a ** non-leaf parent. Note that no autosync ** is performed beforehand. ** --allow-merge-conflict Allows check-in of a file even if it ** appears to contain a fossil merge conflict ** marker ** --user-override USER USER to use instead of the current ** default ** --date-override DATETIME DATE to use instead of 'now' ** --allow-older Allow a commit to be older than its ** ancestor ** --convert-eol-inherit Convert EOL style of the check-in to match ** the previous version's content ** --convert-eol-unix Convert the EOL style to Unix ** --convert-eol-windows Convert the EOL style to Windows. ** (Only one of the --convert-eol-X options may be used and they only ** modified the saved blob, not the input file.) ** --delta Prefer to generate a delta manifest, if ** able. The forbid-delta-manifests repo ** config option trumps this, as do certain ** heuristics. ** --allow-new-file Allow addition of a new file this way. ** Disabled by default to avoid that case- ** sensitivity errors inadvertently lead to ** adding a new file where an update is ** intended. ** -d|--dump-manifest Dumps the generated manifest to stdout ** immediately after it's generated ** --save-manifest FILE Saves the generated manifest to a file ** after successfully processing it ** --wet-run Disables the default dry-run mode ** ** Example: ** ** %fossil test-ci-mini -R REPO -m ... -r foo --as src/myfile.c myfile.c ** */ void test_ci_mini_cmd(void){ CheckinMiniInfo cimi; /* check-in state */ int newRid = 0; /* RID of new version */ const char * zFilename; /* argv[2] */ const char * zComment; /* -m comment */ const char * zCommentFile; /* -M FILE */ const char * zAsFilename; /* --as filename */ const char * zRevision; /* -r|--revision [=trunk|checkout] */ const char * zUser; /* --user-override */ const char * zDate; /* --date-override */ char const * zManifestFile = 0;/* --save-manifest FILE */ /* This function should perform only the minimal "business logic" it ** needs in order to fully/properly populate the CheckinMiniInfo and ** then pass it on to checkin_mini() to do most of the validation ** and work. The point of this is to avoid duplicate code when a web ** front-end is added for checkin_mini(). */ CheckinMiniInfo_init(&cimi); zComment = find_option("comment","m",1); zCommentFile = find_option("comment-file","M",1); zAsFilename = find_option("as",0,1); zRevision = find_option("revision","r",1); zUser = find_option("user-override",0,1); zDate = find_option("date-override",0,1); zManifestFile = find_option("save-manifest",0,1); if(find_option("wet-run",0,0)==0){ cimi.flags |= CIMINI_DRY_RUN; } if(find_option("allow-fork",0,0)!=0){ cimi.flags |= CIMINI_ALLOW_FORK; } if(find_option("dump-manifest","d",0)!=0){ cimi.flags |= CIMINI_DUMP_MANIFEST; } if(find_option("allow-merge-conflict",0,0)!=0){ cimi.flags |= CIMINI_ALLOW_MERGE_MARKER; } if(find_option("allow-older",0,0)!=0){ cimi.flags |= CIMINI_ALLOW_OLDER; } if(find_option("convert-eol-inherit",0,0)!=0){ cimi.flags |= CIMINI_CONVERT_EOL_INHERIT; }else if(find_option("convert-eol-unix",0,0)!=0){ cimi.flags |= CIMINI_CONVERT_EOL_UNIX; }else if(find_option("convert-eol-windows",0,0)!=0){ cimi.flags |= CIMINI_CONVERT_EOL_WINDOWS; } if(find_option("delta",0,0)!=0){ cimi.flags |= CIMINI_PREFER_DELTA; } if(find_option("delta2",0,0)!=0){ /* Undocumented. For testing only. */ cimi.flags |= CIMINI_PREFER_DELTA | CIMINI_STRONGLY_PREFER_DELTA; } if(find_option("allow-new-file",0,0)!=0){ cimi.flags |= CIMINI_ALLOW_NEW_FILE; } db_find_and_open_repository(0, 0); verify_all_options(); user_select(); if(g.argc!=3){ usage("INFILE"); } if(zComment && zCommentFile){ fossil_fatal("Only one of -m or -M, not both, may be used."); }else{ if(zCommentFile && *zCommentFile){ blob_read_from_file(&cimi.comment, zCommentFile, ExtFILE); }else if(zComment && *zComment){ blob_append(&cimi.comment, zComment, -1); } if(!blob_size(&cimi.comment)){ fossil_fatal("Non-empty check-in comment is required."); } } db_begin_transaction(); zFilename = g.argv[2]; cimi.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename); cimi.filePerm = file_perm(zFilename, ExtFILE); cimi.zUser = mprintf("%s", zUser ? zUser : login_name()); if(zDate){ cimi.zDate = mprintf("%s", zDate); } if(zRevision==0 || zRevision[0]==0){ if(g.localOpen/*check-out*/){ zRevision = db_lget("checkout-hash", 0)/*leak*/; }else{ zRevision = "trunk"; } } name_to_uuid2(zRevision, "ci", &cimi.zParentUuid); if(cimi.zParentUuid==0){ fossil_fatal("Cannot determine version to commit to."); } blob_read_from_file(&cimi.fileContent, zFilename, ExtFILE); { Blob theManifest = empty_blob; /* --save-manifest target */ Blob errMsg = empty_blob; int rc; if(zManifestFile){ cimi.pMfOut = &theManifest; } rc = checkin_mini(&cimi, &newRid, &errMsg); if(rc){ assert(blob_size(&errMsg)==0); }else{ assert(blob_size(&errMsg)); fossil_fatal("%b", &errMsg); } if(zManifestFile){ fossil_print("Writing manifest to: %s\n", zManifestFile); assert(blob_size(&theManifest)>0); blob_write_to_file(&theManifest, zManifestFile); blob_reset(&theManifest); } } if(newRid!=0){ fossil_print("New version%s: %z\n", (cimi.flags & CIMINI_DRY_RUN) ? " (dry run)" : "", rid_to_uuid(newRid)); } db_end_transaction(0/*checkin_mini() will have triggered it to roll ** back in dry-run mode, but we need access to ** the transaction-written db state in this ** routine.*/); if(!(cimi.flags & CIMINI_DRY_RUN) && newRid!=0 && g.localOpen!=0){ fossil_warning("The check-out state is now out of sync " "with regards to this commit. It needs to be " "'update'd or 'close'd and re-'open'ed."); } CheckinMiniInfo_cleanup(&cimi); } /* ** If the fileedit-glob setting has a value, this returns its Glob ** object (in memory owned by this function), else it returns NULL. */ Glob *fileedit_glob(void){ static Glob * pGlobs = 0; static int once = 0; if(0==pGlobs && once==0){ char * zGlobs = db_get("fileedit-glob",0); once = 1; if(0!=zGlobs && 0!=*zGlobs){ pGlobs = glob_create(zGlobs); } fossil_free(zGlobs); } return pGlobs; } /* ** Returns true if the given filename qualifies for online editing by ** the current user, else returns false. ** ** Editing requires that the user have the Write permission and that ** the filename match the glob defined by the fileedit-glob setting. ** A missing or empty value for that glob disables all editing. */ int fileedit_is_editable(const char *zFilename){ Glob * pGlobs = fileedit_glob(); if(pGlobs!=0 && zFilename!=0 && *zFilename!=0 && 0!=g.perm.Write){ return glob_match(pGlobs, zFilename); }else{ return 0; } } /* ** Given a repo-relative filename and a manifest RID, returns the UUID ** of the corresponding file entry. Returns NULL if no match is ** found. If pFilePerm is not NULL, the file's permission flag value ** is written to *pFilePerm. */ static char *fileedit_file_uuid(char const *zFilename, int vid, int *pFilePerm){ Stmt stmt = empty_Stmt; char * zFileUuid = 0; db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin " "WHERE filename=%Q %s AND checkinID=%d", zFilename, filename_collation(), vid); if(SQLITE_ROW==db_step(&stmt)){ zFileUuid = mprintf("%s",db_column_text(&stmt, 0)); if(pFilePerm){ *pFilePerm = mfile_permstr_int(db_column_text(&stmt, 1)); } } db_finalize(&stmt); return zFileUuid; } /* ** Returns true if the current user is allowed to edit the given ** filename, as determined by fileedit_is_editable(), else false, ** in which case it queues up an error response and the caller ** must return immediately. */ static int fileedit_ajax_check_filename(const char * zFilename){ if(0==fileedit_is_editable(zFilename)){ ajax_route_error(403, "File is disallowed by the " "fileedit-glob setting."); return 0; } return 1; } /* ** Passed the values of the "checkin" and "filename" request ** properties, this function verifies that they are valid and ** populates: ** ** - *zRevUuid = the fully-expanded value of zRev (owned by the ** caller). zRevUuid may be NULL. ** ** - *pVid = the RID of zRevUuid. pVid May be NULL. If the vid ** cannot be resolved or is ambiguous, pVid is not assigned. ** ** - *frid = the RID of zFilename's blob content. May not be NULL ** unless zFilename is also NULL. If BOTH of zFilename and frid are ** NULL then no confirmation is done on the filename argument - only ** zRev is checked. ** ** Returns 0 if the given file is not in the given check-in or if ** fileedit_ajax_check_filename() fails, else returns true. If it ** returns false, it queues up an error response and the caller must ** return immediately. */ static int fileedit_ajax_setup_filerev(const char * zRev, char ** zRevUuid, int * pVid, const char * zFilename, int * frid){ char * zFileUuid = 0; /* file content UUID */ const int checkFile = zFilename!=0 || frid!=0; int vid = 0; if(checkFile && !fileedit_ajax_check_filename(zFilename)){ return 0; } vid = symbolic_name_to_rid(zRev, "ci"); if(0==vid){ ajax_route_error(404,"Cannot resolve name as a check-in: %s", zRev); return 0; }else if(vid<0){ ajax_route_error(400,"Check-in name is ambiguous: %s", zRev); return 0; }else if(pVid!=0){ *pVid = vid; } if(checkFile){ zFileUuid = fileedit_file_uuid(zFilename, vid, 0); if(zFileUuid==0){ ajax_route_error(404, "Check-in does not contain file."); return 0; } } if(zRevUuid!=0){ *zRevUuid = rid_to_uuid(vid); } if(checkFile){ assert(zFileUuid!=0); if(frid!=0){ *frid = fast_uuid_to_rid(zFileUuid); } fossil_free(zFileUuid); } return 1; } /* ** AJAX route /fileedit?ajax=content ** ** Query parameters: ** ** filename=FILENAME ** checkin=CHECKIN_NAME ** ** User must have Write access to use this page. ** ** Responds with the raw content of the given page. On error it ** produces a JSON response as documented for ajax_route_error(). ** ** Extra response headers: ** ** x-fileedit-file-perm: empty or "x" or "l", representing PERM_REG, ** PERM_EXE, or PERM_LINK, respectively. ** ** x-fileedit-checkin-branch: branch name for the passed-in check-in. */ static void fileedit_ajax_content(void){ const char * zFilename = 0; const char * zRev = 0; int vid, frid; Blob content = empty_blob; const char * zMime; ajax_get_fnci_args( &zFilename, &zRev ); if(!ajax_route_bootstrap(1,0) || !fileedit_ajax_setup_filerev(zRev, 0, &vid, zFilename, &frid)){ return; } zMime = mimetype_from_name(zFilename); content_get(frid, &content); if(0==zMime){ if(looks_like_binary(&content)){ zMime = "application/octet-stream"; }else{ zMime = "text/plain"; } } { /* Send the is-exec bit via response header so that the UI can be ** updated to account for that. */ int fperm = 0; char * zFuuid = fileedit_file_uuid(zFilename, vid, &fperm); const char * zPerm = mfile_permint_mstring(fperm); assert(zFuuid); cgi_printf_header("x-fileedit-file-perm:%s\r\n", zPerm); fossil_free(zFuuid); } { /* Send branch name via response header for UI usability reasons */ char * zBranch = branch_of_rid(vid); if(zBranch!=0 && zBranch[0]!=0){ cgi_printf_header("x-fileedit-checkin-branch: %s\r\n", zBranch); } fossil_free(zBranch); } cgi_set_content_type(zMime); cgi_set_content(&content); } /* ** AJAX route /fileedit?ajax=diff ** ** Required query parameters: ** ** filename=FILENAME ** content=text ** checkin=check-in version ** ** Optional parameters: ** ** sbs=integer (1=side-by-side or 0=unified, default=0) ** ** ws=integer (0=diff whitespace, 1=ignore EOL ws, 2=ignore all ws) ** ** Reminder to self: search info.c for isPatch to see how a ** patch-style siff can be produced. ** ** User must have Write access to use this page. ** ** Responds with the HTML content of the diff. On error it produces a ** JSON response as documented for ajax_route_error(). */ static void fileedit_ajax_diff(void){ /* ** Reminder: we only need the filename to perform valdiation ** against fileedit_is_editable(), else this route could be ** abused to get diffs against content disallowed by the ** whitelist. */ const char * zFilename = 0; const char * zRev = 0; const char * zContent = P("content"); char * zRevUuid = 0; int vid, frid, iFlag; u64 diffFlags = DIFF_HTML | DIFF_NOTTOOBIG; Blob content = empty_blob; iFlag = atoi(PD("sbs","0")); if(0==iFlag){ diffFlags |= DIFF_LINENO; }else{ diffFlags |= DIFF_SIDEBYSIDE; } iFlag = atoi(PD("ws","2")); if(2==iFlag){ diffFlags |= DIFF_IGNORE_ALLWS; }else if(1==iFlag){ diffFlags |= DIFF_IGNORE_EOLWS; } diffFlags |= DIFF_STRIP_EOLCR; ajax_get_fnci_args( &zFilename, &zRev ); if(!ajax_route_bootstrap(1,1) || !fileedit_ajax_setup_filerev(zRev, &zRevUuid, &vid, zFilename, &frid)){ return; } if(!zContent){ zContent = ""; } cgi_set_content_type("text/html"); blob_init(&content, zContent, -1); { Blob orig = empty_blob; char * const zOrigUuid = rid_to_uuid(frid); content_get(frid, &orig); ajax_render_diff(&orig, zOrigUuid, &content, diffFlags); fossil_free(zOrigUuid); blob_reset(&orig); } fossil_free(zRevUuid); blob_reset(&content); } /* ** Sets up and validates most, but not all, of p's check-in-related ** state from the CGI environment. Returns 0 on success or a suggested ** HTTP result code on error, in which case a message will have been ** written to pErr. ** ** It always fails if it cannot completely resolve the 'file' and 'r' ** parameters, including verifying that the refer to a real ** file/version combination and editable by the current user. All ** others are optional (at this level, anyway, but upstream code might ** require them). ** ** If the 3rd argument is not NULL and an error is related to a ** missing arg then *bIsMissingArg is set to true. This is ** intended to allow /fileedit to squelch certain initialization ** errors. ** ** Intended to be used only by /filepage and /filepage_commit. */ static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr, int * bIsMissingArg){ char * zFileUuid = 0; /* UUID of file content */ const char * zFlag; /* generic flag */ int rc = 0, vid = 0, frid = 0; /* result code, check-in/file rids */ #define fail(EXPR) blob_appendf EXPR; goto end_fail zFlag = PD("filename",P("fn")); if(zFlag==0 || !*zFlag){ rc = 400; if(bIsMissingArg){ *bIsMissingArg = 1; } fail((pErr,"Missing required 'filename' parameter.")); } p->zFilename = mprintf("%s",zFlag); if(0==fileedit_is_editable(p->zFilename)){ rc = 403; fail((pErr,"Filename [%h] is disallowed " "by the [fileedit-glob] repository " "setting.", p->zFilename)); } zFlag = PD("checkin",P("ci")); if(!zFlag){ rc = 400; if(bIsMissingArg){ *bIsMissingArg = 1; } fail((pErr,"Missing required 'checkin' parameter.")); } vid = symbolic_name_to_rid(zFlag, "ci"); if(0==vid){ rc = 404; fail((pErr,"Could not resolve check-in version.")); }else if(vid<0){ rc = 400; fail((pErr,"Check-in name is ambiguous.")); } p->zParentUuid = rid_to_uuid(vid)/*fully expand it*/; zFileUuid = fileedit_file_uuid(p->zFilename, vid, &p->filePerm); if(!zFileUuid){ rc = 404; fail((pErr,"Check-in [%S] does not contain file: " "[%h]", p->zParentUuid, p->zFilename)); }else if(PERM_LNK==p->filePerm){ rc = 400; fail((pErr,"Editing symlinks is not permitted.")); } /* Find the repo-side file entry or fail... */ frid = fast_uuid_to_rid(zFileUuid); assert(frid); /* Read file content from submit request or repo... */ zFlag = P("content"); if(zFlag==0){ content_get(frid, &p->fileContent); }else{ blob_init(&p->fileContent,zFlag,-1); } if(looks_like_binary(&p->fileContent)){ rc = 400; fail((pErr,"File appears to be binary. Cannot edit: " "[%h]",p->zFilename)); } zFlag = PT("comment"); if(zFlag!=0 && *zFlag!=0){ blob_append(&p->comment, zFlag, -1); } zFlag = P("comment_mimetype"); if(zFlag){ p->zCommentMimetype = mprintf("%s",zFlag); zFlag = 0; } #define p_int(K) atoi(PD(K,"0")) if(p_int("dry_run")!=0){ p->flags |= CIMINI_DRY_RUN; } if(p_int("allow_fork")!=0){ p->flags |= CIMINI_ALLOW_FORK; } if(p_int("allow_older")!=0){ p->flags |= CIMINI_ALLOW_OLDER; } if(0==p_int("exec_bit")){ p->filePerm = PERM_REG; }else{ p->filePerm = PERM_EXE; } if(p_int("allow_merge_conflict")!=0){ p->flags |= CIMINI_ALLOW_MERGE_MARKER; } if(p_int("prefer_delta")!=0){ p->flags |= CIMINI_PREFER_DELTA; } /* EOL conversion policy... */ switch(p_int("eol")){ case 1: p->flags |= CIMINI_CONVERT_EOL_UNIX; break; case 2: p->flags |= CIMINI_CONVERT_EOL_WINDOWS; break; default: p->flags |= CIMINI_CONVERT_EOL_INHERIT; break; } #undef p_int /* ** TODO?: date-override date selection field. Maybe use ** an input[type=datetime-local]. */ p->zUser = mprintf("%s",g.zLogin); return 0; end_fail: #undef fail fossil_free(zFileUuid); return rc ? rc : 500; } /* ** Renders a list of all open leaves in JSON form: ** ** [ ** {checkin: UUID, branch: branchName, timestamp: string} ** ] ** ** The entries are ordered newest first. ** ** If zFirstUuid is not NULL then *zFirstUuid is set to a copy of the ** full UUID of the first (most recent) leaf, which must be freed by ** the caller. It is set to 0 if there are no leaves. */ static void fileedit_render_leaves_list(char ** zFirstUuid){ Blob sql = empty_blob; Stmt q = empty_Stmt; int i = 0; if(zFirstUuid){ *zFirstUuid = 0; } blob_append(&sql, timeline_query_for_tty(), -1); blob_append_sql(&sql, " AND blob.rid IN (SElECT rid FROM leaf " "WHERE NOT EXISTS(" "SELECT 1 from tagxref WHERE tagid=%d AND " "tagtype>0 AND rid=leaf.rid" ")) " "ORDER BY mtime DESC", TAG_CLOSED); db_prepare_blob(&q, &sql); CX("["); while( SQLITE_ROW==db_step(&q) ){ const char * zUuid = db_column_text(&q, 1); if(i++){ CX(","); }else if(zFirstUuid){ *zFirstUuid = fossil_strdup(zUuid); } CX("{"); CX("\"checkin\":%!j,", zUuid); CX("\"branch\":%!j,", db_column_text(&q, 7)); CX("\"timestamp\":%!j", db_column_text(&q, 2)); CX("}"); } CX("]"); db_finalize(&q); } /* ** For the given fully resolved UUID, renders a JSON object containing ** the fileeedit-editable files in that check-in: ** ** { ** checkin: UUID, ** editableFiles: [ filename1, ... filenameN ] ** } ** ** They are sorted by name using filename_collation(). */ static void fileedit_render_checkin_files(const char * zFullUuid){ Blob sql = empty_blob; Stmt q = empty_Stmt; int i = 0; CX("{\"checkin\":%!j," "\"editableFiles\":[", zFullUuid); blob_append_sql(&sql, "SELECT filename FROM files_of_checkin(%Q) " "ORDER BY filename %s", zFullUuid, filename_collation()); db_prepare_blob(&q, &sql); while( SQLITE_ROW==db_step(&q) ){ const char * zFilename = db_column_text(&q, 0); if(fileedit_is_editable(zFilename)){ if(i++){ CX(","); } CX("%!j", zFilename); } } db_finalize(&q); CX("]}"); } /* ** AJAX route /fileedit?ajax=filelist ** ** Fetches a JSON-format list of leaves and/or filenames for use in ** creating a file selection list in /fileedit. It has different modes ** of operation depending on its arguments: ** ** 'leaves': just fetch a list of open leaf versions, in this ** format: ** ** [ ** {checkin: UUID, branch: branchName, timestamp: string} ** ] ** ** The entries are ordered newest first. ** ** 'checkin=CHECKIN_NAME': fetch the current list of is-editable files ** for the current user and given check-in name: ** ** { ** checkin: UUID, ** editableFiles: [ filename1, ... filenameN ] // sorted by name ** } ** ** On error it produces a JSON response as documented for ** ajax_route_error(). */ static void fileedit_ajax_filelist(){ const char * zCi = PD("checkin",P("ci")); if(!ajax_route_bootstrap(1,0)){ return; } cgi_set_content_type("application/json"); if(zCi!=0){ char * zCiFull = 0; if(0==fileedit_ajax_setup_filerev(zCi, &zCiFull, 0, 0, 0)){ /* Error already reported */ return; } fileedit_render_checkin_files(zCiFull); fossil_free(zCiFull); }else if(P("leaves")!=0){ fileedit_render_leaves_list(0); }else{ ajax_route_error(500, "Unhandled URL argument."); } } /* ** AJAX route /fileedit?ajax=commit ** ** Required query parameters: ** ** filename=FILENAME ** checkin=Parent check-in UUID ** content=text ** comment=non-empty text ** ** Optional query parameters: ** ** comment_mimetype=text (NOT currently honored) ** ** dry_run=int (1 or 0) ** ** include_manifest=int (1 or 0), whether to include ** the generated manifest in the response. ** ** ** User must have Write permissions to use this page. ** ** Responds with JSON (with some state repeated ** from the input in order to avoid certain race conditions ** client-side): ** ** { ** checkin: newUUID, ** filename: theFilename, ** mimetype: string, ** branch: name of the check-in's branch, ** isExe: bool, ** dryRun: bool, ** manifest: text of manifest, ** } ** ** On error it produces a JSON response as documented for ** ajax_route_error(). */ static void fileedit_ajax_commit(void){ Blob err = empty_blob; /* Error messages */ Blob manifest = empty_blob; /* raw new manifest */ CheckinMiniInfo cimi; /* check-in state */ int rc; /* generic result code */ int newVid = 0; /* new version's RID */ char * zNewUuid = 0; /* newVid's UUID */ char const * zMimetype; char * zBranch = 0; if(!ajax_route_bootstrap(1,1)){ return; } db_begin_transaction(); CheckinMiniInfo_init(&cimi); rc = fileedit_setup_cimi_from_p(&cimi, &err, 0); if(0!=rc){ ajax_route_error(rc,"%b",&err); goto end_cleanup; } if(blob_size(&cimi.comment)==0){ ajax_route_error(400,"Empty check-in comment is not permitted."); goto end_cleanup; } if(0!=atoi(PD("include_manifest","0"))){ cimi.pMfOut = &manifest; } checkin_mini(&cimi, &newVid, &err); if(blob_size(&err)){ ajax_route_error(500,"%b",&err); goto end_cleanup; } assert(newVid>0); zNewUuid = rid_to_uuid(newVid); cgi_set_content_type("application/json"); CX("{"); CX("\"checkin\":%!j,", zNewUuid); CX("\"filename\":%!j,", cimi.zFilename); CX("\"isExe\": %s,", cimi.filePerm==PERM_EXE ? "true" : "false"); zMimetype = mimetype_from_name(cimi.zFilename); if(zMimetype!=0){ CX("\"mimetype\": %!j,", zMimetype); } zBranch = branch_of_rid(newVid); if(zBranch!=0){ CX("\"branch\": %!j,", zBranch); fossil_free(zBranch); } CX("\"dryRun\": %s", (CIMINI_DRY_RUN & cimi.flags) ? "true" : "false"); if(blob_size(&manifest)>0){ CX(",\"manifest\": %!j", blob_str(&manifest)); } CX("}"); end_cleanup: db_end_transaction(0/*noting that dry-run mode will have already ** set this to rollback mode. */); fossil_free(zNewUuid); blob_reset(&err); blob_reset(&manifest); CheckinMiniInfo_cleanup(&cimi); } /* ** WEBPAGE: fileedit ** ** Enables the online editing and committing of text files. Requires ** that the user have Write permissions and that a user with setup ** permissions has set the fileedit-glob setting to a list of glob ** patterns matching files which may be edited (e.g. "*.wiki,*.md"). ** Note that fileedit-glob, by design, is a local-only setting. ** It does not sync across repository clones, and must be explicitly ** set on any repositories where this page should be activated. ** ** Optional query parameters: ** ** filename=FILENAME Repo-relative path to the file. ** checkin=VERSION Check-in version, using any unambiguous ** symbolic version name. ** ** If passed a filename but no check-in then it will attempt to ** load that file from the most recent leaf check-in. ** ** Once the page is loaded, files may be selected from any open leaf ** version. The only way to edit files from non-leaf checkins is to ** pass both the filename and check-in as URL parameters to the page. ** Users with the proper permissions will be presented with "Edit" ** links in various file-specific contexts for files which match the ** fileedit-glob, regardless of whether they refer to leaf versions or ** not. */ void fileedit_page(void){ const char * zFileMime = 0; /* File mime type guess */ CheckinMiniInfo cimi; /* Check-in state */ int previewRenderMode = AJAX_RENDER_GUESS; /* preview mode */ Blob err = empty_blob; /* Error report */ const char *zAjax = P("name"); /* Name of AJAX route for sub-dispatching. */ /* ** Internal-use URL parameters: ** ** name=string The name of a page-specific AJAX operation. ** ** Noting that fossil internally stores all URL path components ** after the first as the "name" value. Thus /fileedit?name=blah is ** equivalent to /fileedit/blah. The latter is the preferred ** form. This means, however, that no fileedit ajax routes may make ** use of the name parameter. ** ** Which additional parameters are used by each distinct ajax route ** is an internal implementation detail and may change with any ** given build of this code. An unknown "name" value triggers an ** error, as documented for ajax_route_error(). */ /* Allow no access to this page without check-in privilege */ login_check_credentials(); if( !g.perm.Write ){ if(zAjax!=0){ ajax_route_error(403, "Write permissions required."); }else{ login_needed(g.anon.Write); } return; } /* No access to anything on this page if the fileedit-glob is empty */ if( fileedit_glob()==0 ){ if(zAjax!=0){ ajax_route_error(403, "Online editing is disabled for this " "repository."); return; } style_header("File Editor (disabled)"); CX("<h1>Online File Editing Is Disabled</h1>\n"); if( g.perm.Admin ){ CX("<p>To enable online editing, the " "<a href='%R/setup_settings'>" "<code>fileedit-glob</code> repository setting</a>\n" "must be set to a comma- and/or newine-delimited list of glob\n" "values matching files which may be edited online." "</p>\n"); }else{ CX("<p>Online editing is disabled for this repository.</p>\n"); } style_finish_page(); return; } /* Dispatch AJAX methods based tail of the request URI. ** The AJAX parts do their own permissions/CSRF check and ** fail with a JSON-format response if needed. */ if( 0!=zAjax ){ /* preview mode is handled via /ajax/preview-text */ if(0==strcmp("content",zAjax)){ fileedit_ajax_content(); }else if(0==strcmp("filelist",zAjax)){ fileedit_ajax_filelist(); }else if(0==strcmp("diff",zAjax)){ fileedit_ajax_diff(); }else if(0==strcmp("commit",zAjax)){ fileedit_ajax_commit(); }else{ ajax_route_error(500, "Unhandled ajax route name."); } return; } db_begin_transaction(); CheckinMiniInfo_init(&cimi); style_header("File Editor"); style_emit_noscript_for_js_page(); /* As of this point, don't use return or fossil_fatal(). Write any ** error in (&err) and goto end_footer instead so that we can be ** sure to emit the error message, do any cleanup, and end the ** transaction cleanly. */ { int isMissingArg = 0; if(fileedit_setup_cimi_from_p(&cimi, &err, &isMissingArg)==0){ assert(cimi.zFilename); zFileMime = mimetype_from_name(cimi.zFilename); }else if(isMissingArg!=0){ /* Squelch these startup warnings - they're non-fatal now but ** used to be fatal. */ blob_reset(&err); } } /******************************************************************** ** All errors which "could" have happened up to this point are of a ** degree which keep us from rendering the rest of the page, and ** thus have already caused us to skipped to the end of the page to ** render the errors. Any up-coming errors, barring malloc failure ** or similar, are not "that" fatal. We can/should continue ** rendering the page, then output the error message at the end. ********************************************************************/ /* The CSS for this page lives in a common file but much of it we ** don't want inadvertently being used by other pages. We don't ** have a common, page-specific container we can filter our CSS ** selectors, but we do have the BODY, which we can decorate with ** whatever CSS we wish... */ style_script_begin(__FILE__,__LINE__); CX("document.body.classList.add('fileedit');\n"); style_script_end(); /* Status bar */ CX("<div id='fossil-status-bar' " "title='Status message area. Double-click to clear them.'>" "Status messages will go here.</div>\n" /* will be moved into the tab container via JS */); CX("<div id='fileedit-edit-status'>" "<span class='name'>(no file loaded)</span>" "<span class='links'></span>" "</div>"); /* Main tab container... */ CX("<div id='fileedit-tabs' class='tab-container'></div>"); /* The .hidden class on the following tab elements is to help lessen the FOUC effect of the tabs before JS re-assembles them. */ /***** File/version info tab *****/ { CX("<div id='fileedit-tab-fileselect' " "data-tab-parent='fileedit-tabs' " "data-tab-label='File Selection' " "class='hidden'" ">"); CX("<div id='fileedit-file-selector'></div>"); CX("</div>"/*#fileedit-tab-fileselect*/); } /******* Content tab *******/ { CX("<div id='fileedit-tab-content' " "data-tab-parent='fileedit-tabs' " "data-tab-label='File Content' " "class='hidden'" ">"); CX("<div class='fileedit-options flex-container " "flex-row child-gap-small'>"); CX("<div class='input-with-label'>" "<button class='fileedit-content-reload confirmer' " ">Discard & Reload</button>" "<div class='help-buttonlet'>" "Reload the file from the server, discarding " "any local edits. To help avoid accidental loss of " "edits, it requires confirmation (a second click) within " "a few seconds or it will not reload." "</div>" "</div>"); style_select_list_int("select-font-size", "editor_font_size", "Editor font size", NULL/*tooltip*/, 100, "100%", 100, "125%", 125, "150%", 150, "175%", 175, "200%", 200, NULL); wikiedit_emit_toggle_preview(); CX("</div>"); CX("<div class='flex-container flex-column stretch'>"); CX("<textarea name='content' id='fileedit-content-editor' " "class='fileedit' rows='25'>"); CX("</textarea>"); CX("</div>"/*textarea wrapper*/); CX("</div>"/*#tab-file-content*/); } /****** Preview tab ******/ { CX("<div id='fileedit-tab-preview' " "data-tab-parent='fileedit-tabs' " "data-tab-label='Preview' " "class='hidden'" ">"); CX("<div class='fileedit-options flex-container flex-row'>"); CX("<button id='btn-preview-refresh' " "data-f-preview-from='fileContent' " /* ^^^ fossil.page[methodName]() OR text source elem ID, ** but we need a method in order to support clients swapping out ** the text editor with their own. */ "data-f-preview-via='_postPreview' " /* ^^^ fossil.page[methodName](content, callback) */ "data-f-preview-to='_previewTo' " /* ^^^ dest elem ID */ ">Refresh</button>"); /* Toggle auto-update of preview when the Preview tab is selected. */ CX("<div class='input-with-label'>" "<input type='checkbox' value='1' " "id='cb-preview-autorefresh' checked>" "<label for='cb-preview-autorefresh'>Auto-refresh?</label>" "<div class='help-buttonlet'>" "If on, the preview will automatically " "refresh (if needed) when this tab is selected." "</div>" "</div>"); /* Default preview rendering mode selection... */ previewRenderMode = zFileMime ? ajax_render_mode_for_mimetype(zFileMime) : AJAX_RENDER_GUESS; style_select_list_int("select-preview-mode", "preview_render_mode", "Preview Mode", "Preview mode format.", previewRenderMode, "Guess", AJAX_RENDER_GUESS, "Wiki/Markdown", AJAX_RENDER_WIKI, "HTML (iframe)", AJAX_RENDER_HTML_IFRAME, "HTML (inline)", AJAX_RENDER_HTML_INLINE, "Plain Text", AJAX_RENDER_PLAIN_TEXT, NULL); /* Allow selection of HTML preview iframe height */ style_select_list_int("select-preview-html-ems", "preview_html_ems", "HTML Preview IFrame Height (EMs)", "Height (in EMs) of the iframe used for " "HTML preview", 40 /*default*/, "", 20, "", 40, "", 60, "", 80, "", 100, NULL); /* Selection of line numbers for text preview */ style_labeled_checkbox("cb-line-numbers", "preview_ln", "Add line numbers to plain-text previews?", "1", P("preview_ln")!=0, "If on, plain-text files (only) will get " "line numbers added to the preview."); CX("</div>"/*.fileedit-options*/); CX("<div id='fileedit-tab-preview-wrapper'></div>"); CX("</div>"/*#fileedit-tab-preview*/); } /****** Diff tab ******/ { CX("<div id='fileedit-tab-diff' " "data-tab-parent='fileedit-tabs' " "data-tab-label='Diff' " "class='hidden'" ">"); CX("<div class='fileedit-options flex-container " "flex-row child-gap-small' " "id='fileedit-tab-diff-buttons'>"); CX("<button class='sbs'>Side-by-side</button>" "<button class='unified'>Unified</button>"); if(0){ /* For the time being let's just ignore all whitespace ** changes, as files with Windows-style EOLs always show ** more diffs than we want then they're submitted to ** ?ajax=diff because JS normalizes them to Unix EOLs. ** We can revisit this decision later. */ style_select_list_int("diff-ws-policy", "diff_ws", "Whitespace", "Whitespace handling policy.", 2, "Diff all whitespace", 0, "Ignore EOL whitespace", 1, "Ignore all whitespace", 2, NULL); } CX("</div>"); CX("<div id='fileedit-tab-diff-wrapper'>" "Diffs will be shown here." "</div>"); CX("</div>"/*#fileedit-tab-diff*/); } /****** Commit ******/ CX("<div id='fileedit-tab-commit' " "data-tab-parent='fileedit-tabs' " "data-tab-label='Commit' " "class='hidden'" ">"); { /******* Commit flags/options *******/ CX("<div class='fileedit-options flex-container flex-row'>"); style_labeled_checkbox("cb-dry-run", "dry_run", "Dry-run?", "1", 0, "In dry-run mode, the Commit button performs " "all work needed for committing changes but " "then rolls back the transaction, and thus " "does not really commit."); style_labeled_checkbox("cb-allow-fork", "allow_fork", "Allow fork?", "1", cimi.flags & CIMINI_ALLOW_FORK, "Allow committing to create a fork?"); style_labeled_checkbox("cb-allow-older", "allow_older", "Allow older?", "1", cimi.flags & CIMINI_ALLOW_OLDER, "Allow saving against a parent version " "which has a newer timestamp?"); style_labeled_checkbox("cb-exec-bit", "exec_bit", "Executable?", "1", PERM_EXE==cimi.filePerm, "Set the executable bit?"); style_labeled_checkbox("cb-allow-merge-conflict", "allow_merge_conflict", "Allow merge conflict markers?", "1", cimi.flags & CIMINI_ALLOW_MERGE_MARKER, "Allow saving even if the content contains " "what appear to be fossil merge conflict " "markers?"); style_labeled_checkbox("cb-prefer-delta", "prefer_delta", "Prefer delta manifest?", "1", db_get_boolean("forbid-delta-manifests",0) ? 0 : (db_get_boolean("seen-delta-manifest",0) || cimi.flags & CIMINI_PREFER_DELTA), "Will create a delta manifest, instead of " "baseline, if conditions are favorable to " "do so. This option is only a suggestion."); style_labeled_checkbox("cb-include-manifest", "include_manifest", "Response manifest?", "1", 0, "Include the manifest in the response? " "It's generally only useful for debug " "purposes."); style_select_list_int("select-eol-style", "eol", "EOL Style", "EOL conversion policy, noting that " "webpage-side processing may implicitly change " "the line endings of the input.", (cimi.flags & CIMINI_CONVERT_EOL_UNIX) ? 1 : (cimi.flags & CIMINI_CONVERT_EOL_WINDOWS ? 2 : 0), "Inherit", 0, "Unix", 1, "Windows", 2, NULL); CX("</div>"/*checkboxes*/); } { /******* Commit comment, button, and result manifest *******/ CX("<fieldset class='fileedit-options commit-message'>" "<legend>Message (required)</legend><div>\n"); /* We have two comment input fields, defaulting to single-line ** mode. JS code sets up the ability to toggle between single- ** and multi-line modes. */ CX("<input type='text' name='comment' " "id='fileedit-comment'></input>"); CX("<textarea name='commentBig' class='hidden' " "rows='5' id='fileedit-comment-big'></textarea>\n"); { /* comment options... */ CX("<div class='flex-container flex-column child-gap-small'>"); CX("<button id='comment-toggle' " "title='Toggle between single- and multi-line comment mode, " "noting that switching from multi- to single-line will cause " "newlines to get stripped.'" ">Toggle single-/multi-line</button> "); if(0){ /* Manifests support an N-card (comment mime type) but it has ** yet to be honored where comments are rendered, so we don't ** currently offer it as an option here: ** https://fossil-scm.org/forum/forumpost/662da045a1 ** ** If/when it's ever implemented, simply enable this block and ** adjust the container's layout accordingly (as of this ** writing, that means changing the CSS class from ** 'flex-container flex-column' to 'flex-container flex-row'). */ style_select_list_str("comment-mimetype", "comment_mimetype", "Comment style:", "Specify how fossil will interpret the " "comment string.", NULL, "Fossil", "text/x-fossil-wiki", "Markdown", "text/x-markdown", "Plain text", "text/plain", NULL); CX("</div>\n"); } CX("<div class='fileedit-hint flex-container flex-row'>" "(Warning: switching from multi- to single-line mode will " "strip out all newlines!)</div>"); } CX("</div></fieldset>\n"/*commit comment options*/); CX("<div class='flex-container flex-column' " "id='fileedit-commit-button-wrapper'>" "<button id='fileedit-btn-commit'>Commit</button>" "</div>\n"); CX("<div id='fileedit-manifest'></div>\n" /* Manifest gets rendered here after a commit. */); } CX("</div>"/*#fileedit-tab-commit*/); /****** Help/Tips ******/ CX("<div id='fileedit-tab-help' " "data-tab-parent='fileedit-tabs' " "data-tab-label='Help' " "class='hidden'" ">"); { CX("<h1>Help & Tips</h1>"); CX("<ul>"); CX("<li><strong>Only files matching the <code>fileedit-glob</code> " "repository setting</strong> can be edited online. That setting " "must be a comma- or newline-delimited list of glob patterns " "for files which may be edited online.</li>"); CX("<li>Committing edits creates a new commit record with a single " "modified file.</li>"); CX("<li>\"Delta manifests\" (see the checkbox on the Commit tab) " "make for smaller commit records, especially in repositories " "with many files.</li>"); CX("<li>The file selector allows, for usability's sake, only files " "in leaf check-ins to be selected, but files may be edited via " "non-leaf check-ins by passing them as the <code>filename</code> " "and <code>checkin</code> URL arguments to this page.</li>"); CX("<li>The editor stores some number of local edits in one of " "<code>window.fileStorage</code> or " "<code>window.sessionStorage</code>, if able, but which storage " "is unspecified and may differ across environments. When " "committing or force-reloading a file, local edits to that " "file/check-in combination are discarded.</li>"); CX("</ul>"); } CX("</div>"/*#fileedit-tab-help*/); builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", "storage", "popupwidget", "copybutton", "pikchr", NULL); /* ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is ** used for dynamically toggling certain UI components on and off. ** Must come after window.fossil has been initialized and before ** fossil.page.fileedit.js. Potential TODO: move this into the ** window.fossil bootstrapping so that we don't have to "fulfill" ** the JS multiple times. */ ajax_emit_js_preview_modes(1); builtin_fossil_js_bundle_or("diff", NULL); builtin_request_js("fossil.page.fileedit.js"); builtin_fulfill_js_requests(); { /* Dynamically populate the editor, display any error in the err ** blob, and/or switch to tab #0, where the file selector ** lives. The extra C scopes here correspond to JS-level scopes, ** to improve grokability. */ style_script_begin(__FILE__,__LINE__); CX("\n(function(){\n"); CX("try{\n"); { char * zFirstLeafUuid = 0; CX("fossil.config['fileedit-glob'] = "); glob_render_json_to_cgi(fileedit_glob()); CX(";\n"); if(blob_size(&err)>0){ CX("fossil.error(%!j);\n", blob_str(&err)); } /* Populate the page with the current leaves and, if available, the selected check-in's file list, to save 1 or 2 XHR requests at startup. That makes this page uncacheable, but compressed delivery of this page is currently less than 6k. */ CX("fossil.page.initialLeaves = "); fileedit_render_leaves_list(cimi.zParentUuid ? 0 : &zFirstLeafUuid); CX(";\n"); if(zFirstLeafUuid){ assert(!cimi.zParentUuid); cimi.zParentUuid = zFirstLeafUuid; zFirstLeafUuid = 0; } if(cimi.zParentUuid){ CX("fossil.page.initialFiles = "); fileedit_render_checkin_files(cimi.zParentUuid); CX(";\n"); } CX("fossil.onPageLoad(function(){\n"); { if(blob_size(&err)>0){ CX("fossil.error(%!j);\n", blob_str(&err)); CX("fossil.page.tabs.switchToTab(0);\n"); } if(cimi.zParentUuid && cimi.zFilename){ CX("fossil.page.loadFile(%!j,%!j);\n", cimi.zFilename, cimi.zParentUuid) /* Reminder we cannot embed the JSON-format content of the file here because if it contains a SCRIPT tag then it will break the whole page. */; } } CX("});\n")/*fossil.onPageLoad()*/; } CX("}catch(e){" "fossil.error(e); console.error('Exception:',e);" "}\n"); CX("})();")/*anonymous function*/; style_script_end(); } blob_reset(&err); CheckinMiniInfo_cleanup(&cimi); db_end_transaction(0); style_finish_page(); } |
Changes to src/finfo.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file contains code to implement the "finfo" command. */ #include "config.h" #include "finfo.h" /* ** COMMAND: finfo | | | | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | > > > > > > > > > > > > > > > | > > | | > > > > > > | > > > > > > > > > | > > | | | | | > > > > > | | < < < | | | > > | > | > | > | > | | > > > | < | | | | | | > | > > | < | < < | | < | | | < | > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > | > > > > > | > | > > > > > > > > > > > > > > > | > > > > > > > > | > > | > | | | | | | > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > < | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > | > > > | | | | > > > | > > | | > | | < < | < < > | > > > | > > > | > > > > > > > > > > > > > | | | > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | < > > > | > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** This file contains code to implement the "finfo" command. */ #include "config.h" #include "finfo.h" /* ** COMMAND: finfo ** ** Usage: %fossil finfo ?OPTIONS? FILENAME ** ** Print the complete change history for a single file going backwards ** in time. The default mode is -l. ** ** For the -l|--log mode: If "-b|--brief" is specified one line per revision ** is printed, otherwise the full comment is printed. The "-n|--limit N" ** and "--offset P" options limits the output to the first N changes ** after skipping P changes. ** ** The -i mode will print various facts about FILENAME, including its ** hash and the check-in and time when the current version of the file ** was created. Use -v for additional information. Add the -r VERSION ** option to see similar information about the same file for the check-in ** specified by VERSION. ** ** In the -s mode prints the status as <status> <revision>. This is ** a quick status and does not check for up-to-date-ness of the file. ** ** In the -p mode, there's an optional flag "-r|--revision REVISION". ** The specified version (or the latest checked-out version) is printed ** 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. ** -i|--id Print the artifact ID ** -l|--log Select log mode (the default) ** -n|--limit N Display the first N changes (default unlimited). ** N less than 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) ** -v|--verbose On the -i option, show all check-ins that use the ** file, not just the earliest check-in ** -W|--width N Width of lines (default is to auto-detect). Must be ** more than 22 or else 0 to indicate no limit. ** ** 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 check-out to finfo files in"); } vfile_check_signature(vid, CKSIG_ENOTFILE); file_tree_name(g.argv[2], &fname, 0, 1); db_prepare(&q, "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)" " FROM vfile WHERE vfile.pathname=%B %s", &fname, filename_collation()); blob_zero(&line); if( db_step(&q)==SQLITE_ROW ) { Blob uuid; 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); blob_zero(&uuid); db_blob(&uuid, "SELECT uuid FROM blob, mlink, vfile WHERE " "blob.rid = mlink.mid AND mlink.fid = vfile.rid AND " "vfile.pathname=%B %s", &fname, filename_collation() ); if( isNew ){ blob_appendf(&line, "new"); }else if( isDeleted ){ blob_appendf(&line, "deleted"); }else if( renamed ){ blob_appendf(&line, "renamed"); }else if( chnged ){ blob_appendf(&line, "edited"); }else{ blob_appendf(&line, "unchanged"); } blob_appendf(&line, " "); blob_appendf(&line, " %10.10s", blob_str(&uuid)); blob_reset(&uuid); }else{ blob_appendf(&line, "unknown 0000000000"); } db_finalize(&q); 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, 0, 1); if( zRevision ){ historical_blob(zRevision, blob_str(&fname), &record, 1); }else{ int 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); } content_get(rid, &record); } blob_write_to_file(&record, "-"); blob_reset(&record); blob_reset(&fname); }else if( find_option("id","i",0) ){ Blob fname; int rid; int whatisFlags = WHATIS_BRIEF; const char *zRevision = find_option("revision", "r", 1); if( find_option("verbose","v",0)!=0 ) whatisFlags = 0; verify_all_options(); if( zRevision==0 ) zRevision = "current"; if( g.argc!=3 ) usage("FILENAME"); file_tree_name(g.argv[2], &fname, 0, 1); rid = db_int(0, "SELECT rid FROM blob WHERE uuid =" " (SELECT uuid FROM files_of_checkin(%Q)" " WHERE filename=%B %s)", zRevision, &fname, filename_collation()); if( rid==0 ) { fossil_fatal("file not found for revision %s: %s", zRevision, blob_str(&fname)); } whatis_rid(rid,whatisFlags); blob_reset(&fname); }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, 0, 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 DISTINCT b.uuid, ci.uuid, date(event.mtime,toLocal())," " 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", TAG_BRANCH, zFilename, filename_collation(), iLimit, iOffset ); blob_zero(&line); if( iBrief ){ fossil_print("History for %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, get_comment_format()); 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, get_comment_format()); } } db_finalize(&q); blob_reset(&fname); } } /* ** COMMAND: cat ** ** Usage: %fossil cat FILENAME ... ?OPTIONS? ** ** Print on standard output the content of one or more files as they exist ** in the repository. The version currently checked out is shown by default. ** Other versions may be specified using the -r option. ** ** Options: ** -o|--out OUTFILE For exactly one given FILENAME, write to OUTFILE ** -R|--repository REPO Extract artifacts from repository REPO ** -r VERSION The specific check-in containing the file ** ** See also: [[finfo]] */ void cat_cmd(void){ int i; Blob content, fname; const char *zRev; const char *zFileName; db_find_and_open_repository(0, 0); zRev = find_option("r","r",1); zFileName = find_option("out","o",1); /* We should be done with options.. */ verify_all_options(); if ( zFileName && g.argc>3 ){ fossil_fatal("output file can only be given when retrieving a single file"); } for(i=2; i<g.argc; i++){ file_tree_name(g.argv[i], &fname, 0, 1); blob_zero(&content); historical_blob(zRev, blob_str(&fname), &content, 1); if ( g.argc==3 && zFileName ){ blob_write_to_file(&content, zFileName); }else{ blob_write_to_file(&content, "-"); } blob_reset(&fname); blob_reset(&content); } } /* Values for the debug= query parameter to finfo */ #define FINFO_DEBUG_MLINK 0x01 /* ** WEBPAGE: finfo ** Usage: ** * /finfo?name=FILENAME ** * /finfo?name=FILENAME&ci=HASH ** ** Show the change history for a single file. The name=FILENAME query ** parameter gives the filename and is a required parameter. If the ** ci=HASH parameter is also supplied, then the FILENAME,HASH combination ** identifies a particular version of a file, and in that case all changes ** to that one file version are tracked across both edits and renames. ** If only the name=FILENAME parameter is supplied (if ci=HASH is omitted) ** then the graph shows all changes to any file while it happened ** to be called FILENAME and changes are not tracked across renames. ** ** Additional query parameters: ** ** a=DATETIME Only show changes after DATETIME ** b=DATETIME Only show changes before DATETIME ** ci=HASH identify a particular version of a file and then ** track changes to that file across renames ** m=HASH Mark this particular file version. ** n=NUM Show the first NUM changes only ** name=FILENAME (Required) name of file whose history to show ** brbg Background color by branch name ** ubg Background color by user name ** from=HASH Ancestors only (not descendants) of the version of ** the file in this particular check-in. ** to=HASH If both from= and to= are supplied, only show those ** changes on the direct path between the two given ** checkins. ** showid Show RID values for debugging ** showsql Show the SQL query used to gather the data for ** the graph ** ** DATETIME may be in any of usual formats, including "now", ** "YYYY-MM-DDTHH:MM:SS.SSS", "YYYYMMDDHHMM", and others. */ void finfo_page(void){ Stmt q; const char *zFilename = PD("name",""); char zPrevDate[20]; const char *zA; const char *zB; int n; int ridFrom; int ridTo = 0; int ridCi = 0; const char *zCI = P("ci"); int fnid; 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; Stmt qparent; int iTableId = timeline_tableid(); int tmFlags = 0; /* Viewing mode */ const char *zStyle; /* Viewing mode name */ const char *zMark; /* Mark this version of the file */ int selRid = 0; /* RID of the marked file version */ int mxfnid; /* Maximum filename.fnid value */ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); ridCi = zCI ? name_to_rid_www("ci") : 0; if( fnid==0 ){ style_header("No such file"); }else if( ridCi==0 ){ style_header("All files named \"%s\"", zFilename); }else{ style_header("History of %s of %s",zFilename, zCI); } login_anonymous_available(); tmFlags = timeline_ss_submenu(); if( tmFlags & TIMELINE_COLUMNAR ){ zStyle = "Columnar"; }else if( tmFlags & TIMELINE_COMPACT ){ zStyle = "Compact"; }else if( tmFlags & TIMELINE_VERBOSE ){ zStyle = "Verbose"; }else if( tmFlags & TIMELINE_CLASSIC ){ zStyle = "Classic"; }else{ zStyle = "Modern"; } url_initialize(&url, "finfo"); if( brBg ) url_add_parameter(&url, "brbg", 0); if( uBg ) url_add_parameter(&url, "ubg", 0); ridFrom = name_to_rid_www("from"); zPrevDate[0] = 0; if( fnid==0 ){ @ No such file: %h(zFilename) style_finish_page(); return; } if( g.perm.Admin ){ style_submenu_element("MLink Table", "%R/mlink?name=%t", zFilename); } if( ridFrom ){ if( P("to")!=0 ){ ridTo = name_to_typed_rid(P("to"),"ci"); path_shortest_stored_in_ancestor_table(ridFrom,ridTo); }else{ compute_direct_ancestors(ridFrom); } } url_add_parameter(&url, "name", zFilename); cgi_check_for_malice(); blob_zero(&sql); if( ridCi ){ /* If we will be tracking changes across renames, some extra temp ** tables (implemented as CTEs) are required */ blob_append_sql(&sql, /* The clade(fid,fnid) table is the set of all (fid,fnid) pairs ** that should participate in the output. Clade is computed by ** walking the graph of mlink edges. */ "WITH RECURSIVE clade(fid,fnid) AS (\n" " SELECT blob.rid, %d FROM blob\n" /* %d is fnid */ " WHERE blob.uuid=(SELECT uuid FROM files_of_checkin(%Q)" " WHERE filename=%Q)\n" /* %Q is the filename */ " UNION\n" " SELECT mlink.fid, mlink.fnid\n" " FROM clade, mlink\n" " WHERE clade.fid=mlink.pid\n" " AND ((mlink.pfnid=0 AND mlink.fnid=clade.fnid)\n" " OR mlink.pfnid=clade.fnid)\n" " AND (mlink.fid>0 OR NOT EXISTS(SELECT 1 FROM mlink AS mx" " WHERE mx.mid=mlink.mid AND mx.pid=mlink.pid" " AND mx.fid>0 AND mx.pfnid=mlink.fnid))\n" " UNION\n" " SELECT mlink.pid," " CASE WHEN mlink.pfnid>0 THEN mlink.pfnid ELSE mlink.fnid END\n" " FROM clade, mlink\n" " WHERE mlink.pid>0\n" " AND mlink.fid=clade.fid\n" " AND mlink.fnid=clade.fnid\n" ")\n", fnid, zCI, zFilename ); }else{ /* This is the case for all files with a given name. We will still ** create a "clade(fid,fnid)" table that identifies all participates ** in the output graph, so that subsequent queries can all be the same, ** but in the case the clade table is much simplier, being just a ** single direct query against the mlink table. */ blob_append_sql(&sql, "WITH clade(fid,fnid) AS (\n" " SELECT DISTINCT fid, %d\n" " FROM mlink\n" " WHERE fnid=%d)", fnid, fnid ); } blob_append_sql(&sql, "SELECT\n" " datetime(min(event.mtime),toLocal()),\n" /* Date of change */ " coalesce(event.ecomment, event.comment),\n" /* Check-in comment */ " coalesce(event.euser, event.user),\n" /* User who made chng */ " mlink.pid,\n" /* Parent file rid */ " mlink.fid,\n" /* File rid */ " (SELECT uuid FROM blob WHERE rid=mlink.pid),\n" /* Parent file hash */ " blob.uuid,\n" /* Current file hash */ " (SELECT uuid FROM blob WHERE rid=mlink.mid),\n" /* Check-in hash */ " event.bgcolor,\n" /* Background color */ " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0" " AND tagxref.rid=mlink.mid),\n" /* Branchname */ " mlink.mid,\n" /* check-in ID */ " mlink.pfnid,\n" /* Previous filename */ " blob.size,\n" /* File size */ " mlink.fnid,\n" /* Current filename */ " filename.name\n" /* Current filename */ "FROM clade CROSS JOIN mlink, event" " LEFT JOIN blob ON blob.rid=clade.fid" " LEFT JOIN filename ON filename.fnid=clade.fnid\n" "WHERE mlink.fnid=clade.fnid AND mlink.fid=clade.fid\n" " AND event.objid=mlink.mid\n", TAG_BRANCH ); if( (zA = P("a"))!=0 ){ blob_append_sql(&sql, " AND event.mtime>=%.16g\n", symbolic_name_to_mtime(zA,0)); url_add_parameter(&url, "a", zA); } if( (zB = P("b"))!=0 ){ blob_append_sql(&sql, " AND event.mtime<=%.16g\n", symbolic_name_to_mtime(zB,0)); url_add_parameter(&url, "b", zB); } if( ridFrom ){ blob_append_sql(&sql, " AND mlink.mid IN (SELECT rid FROM ancestor)\n" "GROUP BY mlink.fid\n" ); }else{ /* 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," " mlink.fnid\n" ); } blob_append_sql(&sql, "ORDER BY event.mtime DESC"); if( (n = atoi(PD("n","0")))>0 ){ blob_append_sql(&sql, " LIMIT %d", n); url_add_parameter(&url, "n", P("n")); } blob_append_sql(&sql, " /*sort*/\n"); db_prepare(&q, "%s", blob_sql_text(&sql)); if( P("showsql")!=0 ){ @ <p>SQL: <blockquote><pre>%h(blob_str(&sql))</blockquote></pre> } zMark = P("m"); if( zMark ){ selRid = symbolic_name_to_rid(zMark, "*"); } blob_reset(&sql); blob_zero(&title); if( ridFrom ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ridFrom); char *zLink = href("%R/info/%!S", zUuid); if( ridTo ){ blob_appendf(&title, "Changes to file "); }else if( n>0 ){ blob_appendf(&title, "First %d ancestors of file ", n); }else{ blob_appendf(&title, "Ancestors of file "); } blob_appendf(&title,"%z%h</a>", href("%R/file?name=%T&ci=%!S", zFilename, zUuid), zFilename); if( fShowId ) blob_appendf(&title, " (%d)", fnid); blob_append(&title, ridTo ? " between " : " from ", -1); blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid); if( fShowId ) blob_appendf(&title, " (%d)", ridFrom); fossil_free(zUuid); if( ridTo ){ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ridTo); zLink = href("%R/info/%!S", zUuid); blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid); fossil_free(zUuid); } }else if( ridCi ){ blob_appendf(&title, "History of file "); hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE); if( fShowId ) blob_appendf(&title, " (%d)", fnid); blob_appendf(&title, " at check-in %z%h</a>", href("%R/info?name=%t",zCI), zCI); }else{ blob_appendf(&title, "History for "); hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE); if( fShowId ) blob_appendf(&title, " (%d)", fnid); } if( uBg ){ blob_append(&title, " (color-coded by user)", -1); } @ <h2>%b(&title)</h2> blob_reset(&title); pGraph = graph_init(); @ <table id="timelineTable%d(iTableId)" class="timelineTable"> mxfnid = db_int(0, "SELECT max(fnid) FROM filename"); if( ridFrom ){ db_prepare(&qparent, "SELECT DISTINCT pid*%d+CASE WHEN pfnid>0 THEN pfnid ELSE fnid END" " FROM mlink" " WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid" " AND pmid IN (SELECT rid FROM ancestor)" " ORDER BY isaux /*sort*/", mxfnid+1 ); }else{ db_prepare(&qparent, "SELECT DISTINCT pid*%d+CASE WHEN pfnid>0 THEN pfnid ELSE fnid END" " FROM mlink" " WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid" " ORDER BY isaux /*sort*/", mxfnid+1 ); } while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zCom = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); int fpid = db_column_int(&q, 3); int frid = db_column_int(&q, 4); const char *zPUuid = db_column_text(&q, 5); const char *zUuid = db_column_text(&q, 6); 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 szFile = db_column_int(&q, 12); int fnid = db_column_int(&q, 13); const char *zFName = db_column_text(&q,14); int gidx; char zTime[10]; int nParent = 0; GraphRowId aParent[GR_MAX_RAIL]; 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<count(aParent) ){ aParent[nParent] = db_column_int64(&qparent, 0); nParent++; } db_reset(&qparent); if( zBr==0 ) zBr = "trunk"; if( uBg ){ zBgClr = user_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 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000, nParent, 0, 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; if( frid==selRid ){ @ <tr class='timelineSelected'> }else{ @ <tr> } @ <td class="timelineTime">\ @ %z(href("%R/file?name=%T&ci=%!S",zFName,zCkin))%s(zTime)</a></td> @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div> @ </td> if( zBgClr && zBgClr[0] ){ @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'> }else{ @ <td class="timeline%s(zStyle)Cell"> } if( tmFlags & TIMELINE_COMPACT ){ @ <span class='timelineCompactComment' data-id='%d(frid)'> }else{ @ <span class='timeline%s(zStyle)Comment'> if( pfnid ){ char *zPrevName = db_text(0,"SELECT name FROM filename WHERE fnid=%d", pfnid); @ <b>Renamed</b> %h(zPrevName) → %h(zFName). fossil_free(zPrevName); } if( zUuid && ridTo==0 && nParent==0 ){ @ <b>Added:</b> } if( zUuid==0 ){ 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, zFName); if( zNewName ){ @ <b>Renamed</b> to @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a>. fossil_free(zNewName); }else{ @ <b>Deleted:</b> } } if( (tmFlags & TIMELINE_VERBOSE)!=0 && zUuid ){ hyperlink_to_version(zUuid); if( fShowId ){ int srcId = delta_source_rid(frid); if( srcId ){ @ (%z(href("%R/deltachain/%d",frid))%d(frid)←%d(srcId)</a>) }else{ @ (%z(href("%R/deltachain/%d",frid))%d(frid)</a>) } } @ part of check-in \ hyperlink_to_version(zCkin); } } @ %W(zCom)</span> if( (tmFlags & TIMELINE_COMPACT)!=0 ){ @ <span class='timelineEllipsis' data-id='%d(frid)' \ @ id='ellipsis-%d(frid)'>...</span> } if( tmFlags & TIMELINE_COLUMNAR ){ if( zBgClr && zBgClr[0] ){ @ <td class="timelineDetailCell" id='md%d(gidx)'> }else{ @ <td class="timelineDetailCell"> } } if( tmFlags & TIMELINE_COMPACT ){ cgi_printf("<span class='clutter' id='detail-%d'>",frid); } cgi_printf("<span class='timeline%sDetail'>", zStyle); if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("("); if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){ @ file: %z(href("%R/file?name=%T&ci=%!S",zFName,zCkin))\ @ [%S(zUuid)]</a> if( fShowId ){ int srcId = delta_source_rid(frid); if( srcId>0 ){ @ id: %z(href("%R/deltachain/%d",frid))\ @ %d(frid)←%d(srcId)</a> }else{ @ id: %z(href("%R/deltachain/%d",frid))%d(frid)</a> } } } @ check-in: \ hyperlink_to_version(zCkin); if( fShowId ){ @ (%d(fmid)) } @ user: \ hyperlink_to_user(zUser, zDate, ","); @ branch: %z(href("%R/timeline?t=%T",zBr))%h(zBr)</a>, if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ){ @ size: %d(szFile)) }else{ @ size: %d(szFile) } if( g.perm.Hyperlink && zUuid ){ const char *z = zFName; @ <span id='links-%d(frid)'><span class='timelineExtraLinks'> @ %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?uf=%!S",zUuid))[check-ins using]</a> if( fpid>0 ){ @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a> } if( fileedit_is_editable(zFName) ){ @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFName,zCkin))\ @ [edit]</a> } @ </span></span> } if( fDebug & FINFO_DEBUG_MLINK ){ int ii; char *zAncLink; @ <br>fid=%d(frid) \ @ graph-id=%lld(frid>0?(GraphRowId)frid*(mxfnid+1)+fnid:fpid+1000000000) \ @ pid=%d(fpid) mid=%d(fmid) fnid=%d(fnid) \ @ pfnid=%d(pfnid) mxfnid=%d(mxfnid) if( nParent>0 ){ @ parents=%lld(aParent[0]) for(ii=1; ii<nParent; ii++){ @ %lld(aParent[ii]) } } zAncLink = href("%R/finfo?name=%T&from=%!S&debug=1",zFName,zCkin); @ %z(zAncLink)[ancestry]</a> } tag_private_status(frid); /* End timelineDetail */ if( tmFlags & TIMELINE_COMPACT ){ @ </span></span> }else{ @ </span> } @ </td></tr> } db_finalize(&q); db_finalize(&qparent); if( pGraph ){ graph_finish(pGraph, 0, TIMELINE_DISJOINT); if( pGraph->nErr ){ graph_free(pGraph); pGraph = 0; }else{ @ <tr class="timelineBottom" id="btm-%d(iTableId)">\ @ <td></td><td></td><td></td></tr> } } @ </table> timeline_output_graph_javascript(pGraph, TIMELINE_FILEDIFF, iTableId); style_finish_page(); } /* ** WEBPAGE: mlink ** URL: /mlink?name=FILENAME ** URL: /mlink?ci=NAME ** ** Show all MLINK table entries for a particular file, or for ** a particular check-in. ** ** This screen is intended for use by Fossil developers to help ** in debugging Fossil itself. Ordinary Fossil users are not ** expected to know what the MLINK table is or why it is important. ** ** To avoid confusing ordinary users, this page is only available ** to administrators. */ void mlink_page(void){ const char *zFName = P("name"); const char *zCI = P("ci"); Stmt q; login_check_credentials(); if( !g.perm.Admin ){ login_needed(g.anon.Admin); return; } style_set_current_feature("finfo"); style_header("MLINK Table"); if( zFName==0 && zCI==0 ){ @ <span class='generalError'> @ Requires either a name= or ci= query parameter @ </span> }else if( zFName ){ int fnid = db_int(0,"SELECT fnid FROM filename WHERE name=%Q",zFName); if( fnid<=0 ) fossil_fatal("no such file: \"%s\"", zFName); db_prepare(&q, "SELECT" /* 0 */ " datetime(event.mtime,toLocal())," /* 1 */ " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* 2 */ " (SELECT uuid FROM blob WHERE rid=mlink.pmid)," /* 3 */ " isaux," /* 4 */ " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* 5 */ " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* 6 */ " mlink.pid," /* 7 */ " mperm," /* 8 */ " (SELECT name FROM filename WHERE fnid=mlink.pfnid)" " FROM mlink, event" " WHERE mlink.fnid=%d" " AND event.objid=mlink.mid" " ORDER BY 1 DESC", fnid ); style_table_sorter(); @ <h1>MLINK table for file @ <a href='%R/finfo?name=%t(zFName)'>%h(zFName)</a></h1> @ <div class='brlist'> @ <table class='sortable' data-column-types='tttxtttt' data-init-sort='1'> @ <thead><tr> @ <th>Date</th> @ <th>Check-in</th> @ <th>Parent<br>Check-in</th> @ <th>Merge?</th> @ <th>New</th> @ <th>Old</th> @ <th>Exe<br>Bit?</th> @ <th>Prior<br>Name</th> @ </tr></thead> @ <tbody> while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q,0); const char *zCkin = db_column_text(&q,1); const char *zParent = db_column_text(&q,2); int isMerge = db_column_int(&q,3); const char *zFid = db_column_text(&q,4); const char *zPid = db_column_text(&q,5); int isExe = db_column_int(&q,7); const char *zPrior = db_column_text(&q,8); @ <tr> @ <td><a href='%R/timeline?c=%!S(zCkin)'>%s(zDate)</a></td> @ <td><a href='%R/info/%!S(zCkin)'>%S(zCkin)</a></td> if( zParent ){ @ <td><a href='%R/info/%!S(zParent)'>%S(zParent)</a></td> }else{ @ <td><i>(New)</i></td> } @ <td align='center'>%s(isMerge?"✓":"")</td> if( zFid ){ @ <td><a href='%R/info/%!S(zFid)'>%S(zFid)</a></td> }else{ @ <td><i>(Deleted)</i></td> } if( zPid ){ @ <td><a href='%R/info/%!S(zPid)'>%S(zPid)</a> }else if( db_column_int(&q,6)<0 ){ @ <td><i>(Added by merge)</i></td> }else{ @ <td><i>(New)</i></td> } @ <td align='center'>%s(isExe?"✓":"")</td> if( zPrior ){ @ <td><a href='%R/finfo?name=%t(zPrior)'>%h(zPrior)</a></td> }else{ @ <td></td> } @ </tr> } db_finalize(&q); @ </tbody> @ </table> @ </div> }else{ int mid = name_to_rid_www("ci"); db_prepare(&q, "SELECT" /* 0 */ " (SELECT name FROM filename WHERE fnid=mlink.fnid)," /* 1 */ " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* 2 */ " pid," /* 3 */ " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* 4 */ " (SELECT name FROM filename WHERE fnid=mlink.pfnid)," /* 5 */ " (SELECT uuid FROM blob WHERE rid=mlink.pmid)," /* 6 */ " mperm," /* 7 */ " isaux" " FROM mlink WHERE mid=%d ORDER BY 1", mid ); @ <h1>MLINK table for check-in %h(zCI)</h1> render_checkin_context(mid, 0, 1, 0); style_table_sorter(); @ <hr> @ <div class='brlist'> @ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'> @ <thead><tr> @ <th>File</th> @ <th>Parent<br>Check-in</th> @ <th>Merge?</th> @ <th>New</th> @ <th>Old</th> @ <th>Exe<br>Bit?</th> @ <th>Prior<br>Name</th> @ </tr></thead> @ <tbody> while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q,0); const char *zFid = db_column_text(&q,1); const char *zPid = db_column_text(&q,3); const char *zPrior = db_column_text(&q,4); const char *zParent = db_column_text(&q,5); int isExec = db_column_int(&q,6); int isAux = db_column_int(&q,7); @ <tr> @ <td><a href='%R/finfo?name=%t(zName)'>%h(zName)</a></td> if( zParent ){ @ <td><a href='%R/info/%!S(zParent)'>%S(zParent)</a></td> }else{ @ <td><i>(New)</i></td> } @ <td align='center'>%s(isAux?"✓":"")</td> if( zFid ){ @ <td><a href='%R/info/%!S(zFid)'>%S(zFid)</a></td> }else{ @ <td><i>(Deleted)</i></td> } if( zPid ){ @ <td><a href='%R/info/%!S(zPid)'>%S(zPid)</a> }else if( db_column_int(&q,2)<0 ){ @ <td><i>(Added by merge)</i></td> }else{ @ <td><i>(New)</i></td> } @ <td align='center'>%s(isExec?"✓":"")</td> if( zPrior ){ @ <td><a href='%R/finfo?name=%t(zPrior)'>%h(zPrior)</a></td> }else{ @ <td></td> } @ </tr> } db_finalize(&q); @ </tbody> @ </table> @ </div> } style_finish_page(); } |
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 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 | /* ** 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 eponymous virtual table for SQLite that gives ** all of the files associated with a single check-in. The table works ** as a table-valued function. ** ** The source code filename "foci" is short for "Files of Check-in". ** ** Usage example: ** ** SELECT * FROM files_of_checkin('trunk'); ** ** 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, -- hash of the file ** previousName TEXT, -- Name of the file in previous check-in ** perm TEXT, -- Permissions on the file ** symname TEXT HIDDEN -- Symbolic name of the check-in. ** ); ** ** The hidden symname column is (optionally) used as a query parameter to ** identify the particular check-in to parse. The checkinID parameter ** (such is a unique numeric RID rather than symbolic name) can also be used ** to identify the check-in. Example: ** ** SELECT * FROM files_of_checkin ** WHERE checkinID=symbolic_name_to_rid('trunk'); ** */ #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, -- hash of the file @ previousName TEXT, -- Name of the file in previous check-in @ perm TEXT, -- Permissions on the file @ symname TEXT HIDDEN -- Symbolic name of the check-in @ ); ; #define FOCI_CHECKINID 0 #define FOCI_FILENAME 1 #define FOCI_UUID 2 #define FOCI_PREVNAME 3 #define FOCI_PERM 4 #define FOCI_SYMNAME 5 #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 specified. ** (2) symName=? visit only the single manifest specified. */ static int fociBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int i; pIdxInfo->estimatedCost = 1000000000.0; for(i=0; i<pIdxInfo->nConstraint; i++){ if( !pIdxInfo->aConstraint[i].usable ) continue; if( pIdxInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ && (pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID || pIdxInfo->aConstraint[i].iColumn==FOCI_SYMNAME) ){ if( pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID ){ pIdxInfo->idxNum = 1; }else{ pIdxInfo->idxNum = 2; } 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 ){ int rid; if( idxNum==1 ){ rid = sqlite3_value_int(argv[0]); }else{ rid = symbolic_name_to_rid((const char*)sqlite3_value_text(argv[0]),"ci"); } pCur->pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); if( pCur->pMan ){ 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 FOCI_CHECKINID: sqlite3_result_int(ctx, pCsr->pMan->rid); break; case FOCI_FILENAME: sqlite3_result_text(ctx, pCsr->pFile->zName, -1, SQLITE_TRANSIENT); break; case FOCI_UUID: sqlite3_result_text(ctx, pCsr->pFile->zUuid, -1, SQLITE_TRANSIENT); break; case FOCI_PREVNAME: sqlite3_result_text(ctx, pCsr->pFile->zPrior, -1, SQLITE_TRANSIENT); break; case FOCI_PERM: sqlite3_result_text(ctx, pCsr->pFile->zPerm, -1, SQLITE_TRANSIENT); break; case FOCI_SYMNAME: 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, /* xFindFunction */ 0, /* xRename */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ 0 /* xIntegrity */ }; sqlite3_create_module(db, "files_of_checkin", &foci_module, 0); return SQLITE_OK; } |
Added src/forum.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 | /* ** Copyright (c) 2018 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 generate the user forum. */ #include "config.h" #include <assert.h> #include "forum.h" /* ** Default to using Markdown markup */ #define DEFAULT_FORUM_MIMETYPE "text/x-markdown" #if INTERFACE /* ** Each instance of the following object represents a single message - ** either the initial post, an edit to a post, a reply, or an edit to ** a reply. */ struct ForumPost { int fpid; /* rid for this post */ int sid; /* Serial ID number */ int rev; /* Revision number */ char *zUuid; /* Artifact hash */ char *zDisplayName; /* Name of user who wrote this post */ double rDate; /* Date for this post */ ForumPost *pIrt; /* This post replies to pIrt */ ForumPost *pEditHead; /* Original, unedited post */ ForumPost *pEditTail; /* Most recent edit for this post */ ForumPost *pEditNext; /* This post is edited by pEditNext */ ForumPost *pEditPrev; /* This post is an edit of pEditPrev */ ForumPost *pNext; /* Next in chronological order */ ForumPost *pPrev; /* Previous in chronological order */ ForumPost *pDisplay; /* Next in display order */ int nEdit; /* Number of edits to this post */ int nIndent; /* Number of levels of indentation for this post */ int iClosed; /* See forum_rid_is_closed() */ }; /* ** A single instance of the following tracks all entries for a thread. */ struct ForumThread { ForumPost *pFirst; /* First post in chronological order */ ForumPost *pLast; /* Last post in chronological order */ ForumPost *pDisplay; /* Entries in display order */ ForumPost *pTail; /* Last on the display list */ int mxIndent; /* Maximum indentation level */ }; #endif /* INTERFACE */ /* ** Return true if the forum post with the given rid has been ** subsequently edited. */ int forum_rid_has_been_edited(int rid){ static Stmt q; int res; db_static_prepare(&q, "SELECT 1 FROM forumpost A, forumpost B" " WHERE A.fpid=$rid AND B.froot=A.froot AND B.fprev=$rid" ); db_bind_int(&q, "$rid", rid); res = db_step(&q)==SQLITE_ROW; db_reset(&q); return res; } /* ** Given a valid forumpost.fpid value, this function returns the first ** fpid in the chain of edits for that forum post, or rid if no prior ** versions are found. */ static int forumpost_head_rid(int rid){ Stmt q; int rcRid = rid; db_prepare(&q, "SELECT fprev FROM forumpost" " WHERE fpid=:rid AND fprev IS NOT NULL"); db_bind_int(&q, ":rid", rid); while( SQLITE_ROW==db_step(&q) ){ rcRid = db_column_int(&q, 0); db_reset(&q); db_bind_int(&q, ":rid", rcRid); } db_finalize(&q); return rcRid; } /* ** Returns true if p, or any parent of p, has a non-zero iClosed ** value. Returns 0 if !p. For an edited chain of post, the tag is ** checked on the pEditHead entry, to simplify subsequent unlocking of ** the post. ** ** If bCheckIrt is true then p's thread in-response-to parents are ** checked (recursively) for closure, else only p is checked. */ static int forumpost_is_closed(ForumPost *p, int bCheckIrt){ while(p){ if( p->pEditHead ) p = p->pEditHead; if( p->iClosed || !bCheckIrt ) return p->iClosed; p = p->pIrt; } return 0; } /* ** Given a forum post RID, this function returns true if that post has ** (or inherits) an active "closed" tag. If bCheckIrt is true then ** the post to which the given post responds is also checked ** (recursively), else they are not. When checking in-response-to ** posts, the first one which is closed ends the search. ** ** Note that this function checks _exactly_ the given rid, whereas ** forum post closure/re-opening is always applied to the head of an ** edit chain so that we get consistent implied locking beheavior for ** later versions and responses to arbitrary versions in the ** chain. Even so, the "closed" tag is applied as a propagating tag ** so will apply to all edits in a given chain. ** ** The return value is one of: ** ** - 0 if no "closed" tag is found. ** ** - The tagxref.rowid of the tagxref entry for the closure if rid is ** the forum post to which the closure applies. ** ** - (-tagxref.rowid) if the given rid inherits a "closed" tag from an ** IRT forum post. */ static int forum_rid_is_closed(int rid, int bCheckIrt){ static Stmt qIrt = empty_Stmt_m; int rc = 0, i = 0; /* TODO: this can probably be turned into a CTE by someone with ** superior SQL-fu. */ for( ; rid; i++ ){ rc = rid_has_active_tag_name(rid, "closed"); if( rc || !bCheckIrt ) break; else if( !qIrt.pStmt ) { db_static_prepare(&qIrt, "SELECT firt FROM forumpost " "WHERE fpid=$fpid ORDER BY fmtime DESC" ); } db_bind_int(&qIrt, "$fpid", rid); rid = SQLITE_ROW==db_step(&qIrt) ? db_column_int(&qIrt, 0) : 0; db_reset(&qIrt); } return i ? -rc : rc; } /* ** Closes or re-opens the given forum RID via addition of a new ** control artifact into the repository. In order to provide ** consistent behavior for implied closing of responses and later ** versions, it always acts on the first version of the given forum ** post, walking the forumpost.fprev values to find the head of the ** chain. ** ** If doClose is true then a propagating "closed" tag is added, except ** as noted below, with the given optional zReason string as the tag's ** value. If doClose is false then any active "closed" tag on frid is ** cancelled, except as noted below. zReason is ignored if doClose is ** false or if zReason is NULL or starts with a NUL byte. ** ** This function only adds a "closed" tag if forum_rid_is_closed() ** indicates that frid's head is not closed. If a parent post is ** already closed, no tag is added. Similarly, it will only remove a ** "closed" tag from a post which has its own "closed" tag, and will ** not remove an inherited one from a parent post. ** ** If doClose is true and frid is closed (directly or inherited), this ** is a no-op. Likewise, if doClose is false and frid itself is not ** closed (not accounting for an inherited closed tag), this is a ** no-op. ** ** Returns true if it actually creates a new tag, else false. Fails ** fatally on error. If it returns true then any ForumPost::iClosed ** values from previously loaded posts are invalidated if they refer ** to the amended post or a response to it. ** ** Sidebars: ** ** - Unless the caller has a transaction open, via ** db_begin_transaction(), there is a very tiny race condition ** window during which the caller's idea of whether or not the forum ** post is closed may differ from the current repository state. ** ** - This routine assumes that frid really does refer to a forum post. ** ** - This routine assumes that frid is not private or pending ** moderation. ** ** - Closure of a forum post requires a propagating "closed" tag to ** account for how edits of posts are handled. This differs from ** closure of a branch, where a non-propagating tag is used. */ static int forumpost_close(int frid, int doClose, const char *zReason){ Blob artifact = BLOB_INITIALIZER; /* Output artifact */ Blob cksum = BLOB_INITIALIZER; /* Z-card */ int iClosed; /* true if frid is closed */ int trid; /* RID of new control artifact */ char *zUuid; /* UUID of head version of post */ db_begin_transaction(); frid = forumpost_head_rid(frid); iClosed = forum_rid_is_closed(frid, 1); if( (iClosed && doClose /* Already closed, noting that in the case of (iClosed<0), it's ** actually a parent which is closed. */) || (iClosed<=0 && !doClose /* This entry is not closed, but a parent post may be. */) ){ db_end_transaction(0); return 0; } if( doClose==0 || (zReason && !zReason[0]) ){ zReason = 0; } zUuid = rid_to_uuid(frid); blob_appendf(&artifact, "D %z\n", date_in_standard_format( "now" )); blob_appendf(&artifact, "T %cclosed %s%s%F\n", doClose ? '*' : '-', zUuid, zReason ? " " : "", zReason ? zReason : ""); blob_appendf(&artifact, "U %F\n", login_name()); md5sum_blob(&artifact, &cksum); blob_appendf(&artifact, "Z %b\n", &cksum); blob_reset(&cksum); trid = content_put_ex(&artifact, 0, 0, 0, 0); if( trid==0 ){ fossil_fatal("Error saving tag artifact: %s", g.zErrMsg); } if( manifest_crosslink(trid, &artifact, MC_NONE /*MC_PERMIT_HOOKS?*/)==0 ){ fossil_fatal("%s", g.zErrMsg); } assert( blob_is_reset(&artifact) ); db_add_unsent(trid); admin_log("%s forum post %S", doClose ? "Close" : "Re-open", zUuid); fossil_free(zUuid); /* Potential TODO: if (iClosed>0) then we could find the initial tag ** artifact and content_deltify(thatRid,&trid,1,0). Given the tiny ** size of these artifacts, however, that would save little space, ** if any. */ db_end_transaction(0); return 1; } /* ** Returns true if the forum-close-policy setting is true, else false, ** caching the result for subsequent calls. */ static int forumpost_close_policy(void){ static int closePolicy = -99; if( closePolicy==-99 ){ closePolicy = db_get_boolean("forum-close-policy",0)>0; } return closePolicy; } /* ** Returns 1 if the current user is an admin, -1 if the current user ** is a forum moderator and the forum-close-policy setting is true, ** else returns 0. The value is cached for subsequent calls. */ static int forumpost_may_close(void){ static int permClose = -99; if( permClose!=-99 ){ return permClose; }else if( g.perm.Admin ){ return permClose = 1; }else if( g.perm.ModForum ){ return permClose = forumpost_close_policy()>0 ? -1 : 0; }else{ return permClose = 0; } } /* ** Emits a warning that the current forum post is CLOSED and can only ** be edited or responded to by an administrator. */ static void forumpost_error_closed(void){ @ <div class='error'>This (sub)thread is CLOSED and can only be @ edited or replied to by an admin user.</div> } /* ** Delete a complete ForumThread and all its entries. */ static void forumthread_delete(ForumThread *pThread){ ForumPost *pPost, *pNext; for(pPost=pThread->pFirst; pPost; pPost = pNext){ pNext = pPost->pNext; fossil_free(pPost->zUuid); fossil_free(pPost->zDisplayName); fossil_free(pPost); } fossil_free(pThread); } /* ** Search a ForumPost list forwards looking for the post with fpid */ static ForumPost *forumpost_forward(ForumPost *p, int fpid){ while( p && p->fpid!=fpid ) p = p->pNext; return p; } /* ** Search backwards for a ForumPost */ static ForumPost *forumpost_backward(ForumPost *p, int fpid){ while( p && p->fpid!=fpid ) p = p->pPrev; return p; } /* ** Add a post to the display list */ static void forumpost_add_to_display(ForumThread *pThread, ForumPost *p){ if( pThread->pDisplay==0 ){ pThread->pDisplay = p; }else{ pThread->pTail->pDisplay = p; } pThread->pTail = p; } /* ** Extend the display list for pThread by adding all entries that ** reference fpid. The first such post will be no earlier then ** post "p". */ static void forumthread_display_order( ForumThread *pThread, /* The complete thread */ ForumPost *pBase /* Add replies to this post */ ){ ForumPost *p; ForumPost *pPrev = 0; ForumPost *pBaseIrt; for(p=pBase->pNext; p; p=p->pNext){ if( !p->pEditPrev && p->pIrt ){ pBaseIrt = p->pIrt->pEditHead ? p->pIrt->pEditHead : p->pIrt; if( pBaseIrt==pBase ){ if( pPrev ){ pPrev->nIndent = pBase->nIndent + 1; forumpost_add_to_display(pThread, pPrev); forumthread_display_order(pThread, pPrev); } pPrev = p; } } } if( pPrev ){ pPrev->nIndent = pBase->nIndent + 1; if( pPrev->nIndent>pThread->mxIndent ) pThread->mxIndent = pPrev->nIndent; forumpost_add_to_display(pThread, pPrev); forumthread_display_order(pThread, pPrev); } } /* ** Construct a ForumThread object given the root record id. */ static ForumThread *forumthread_create(int froot, int computeHierarchy){ ForumThread *pThread; ForumPost *pPost; ForumPost *p; Stmt q; int sid = 1; int firt, fprev; pThread = fossil_malloc( sizeof(*pThread) ); memset(pThread, 0, sizeof(*pThread)); db_prepare(&q, "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid), fmtime" " FROM forumpost" " WHERE froot=%d ORDER BY fmtime", froot ); while( db_step(&q)==SQLITE_ROW ){ pPost = fossil_malloc( sizeof(*pPost) ); memset(pPost, 0, sizeof(*pPost)); pPost->fpid = db_column_int(&q, 0); firt = db_column_int(&q, 1); fprev = db_column_int(&q, 2); pPost->zUuid = fossil_strdup(db_column_text(&q,3)); pPost->rDate = db_column_double(&q,4); if( !fprev ) pPost->sid = sid++; pPost->pPrev = pThread->pLast; pPost->pNext = 0; if( pThread->pLast==0 ){ pThread->pFirst = pPost; }else{ pThread->pLast->pNext = pPost; } pThread->pLast = pPost; /* Find the in-reply-to post. Default to the topic post if the replied-to ** post cannot be found. */ if( firt ){ pPost->pIrt = pThread->pFirst; for(p=pThread->pFirst; p; p=p->pNext){ if( p->fpid==firt ){ pPost->pIrt = p; break; } } } /* Maintain the linked list of post edits. */ if( fprev ){ p = forumpost_backward(pPost->pPrev, fprev); p->pEditNext = pPost; pPost->sid = p->sid; pPost->rev = p->rev+1; pPost->nEdit = p->nEdit+1; pPost->pEditPrev = p; pPost->pEditHead = p->pEditHead ? p->pEditHead : p; for(; p; p=p->pEditPrev ){ p->nEdit = pPost->nEdit; p->pEditTail = pPost; } } pPost->iClosed = forum_rid_is_closed(pPost->pEditHead ? pPost->pEditHead->fpid : pPost->fpid, 1); } db_finalize(&q); if( computeHierarchy ){ /* Compute the hierarchical display order */ pPost = pThread->pFirst; pPost->nIndent = 1; pThread->mxIndent = 1; forumpost_add_to_display(pThread, pPost); forumthread_display_order(pThread, pPost); } /* Return the result */ return pThread; } /* ** List all forum threads to standard output. */ static void forum_thread_list(void){ Stmt q; db_prepare(&q, " SELECT" " datetime(max(fmtime))," " sum(fprev IS NULL)," " froot" " FROM forumpost" " GROUP BY froot" " ORDER BY 1;" ); fossil_print(" id cnt most recent post\n"); fossil_print("------ ---- -------------------\n"); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%6d %4d %s\n", db_column_int(&q, 2), db_column_int(&q, 1), db_column_text(&q, 0) ); } db_finalize(&q); } /* ** COMMAND: test-forumthread ** ** Usage: %fossil test-forumthread [THREADID] ** ** Display a summary of all messages on a thread THREADID. If the ** THREADID argument is omitted, then show a list of all threads. ** ** This command is intended for testing an analysis only. */ void forumthread_cmd(void){ int fpid; int froot; const char *zName; ForumThread *pThread; ForumPost *p; db_find_and_open_repository(0,0); verify_all_options(); if( g.argc==2 ){ forum_thread_list(); return; } if( g.argc!=3 ) usage("THREADID"); zName = g.argv[2]; fpid = symbolic_name_to_rid(zName, "f"); if( fpid<=0 ){ fpid = db_int(0, "SELECT rid FROM blob WHERE rid=%d", atoi(zName)); } if( fpid<=0 ){ fossil_fatal("unknown or ambiguous forum id: \"%s\"", zName); } froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); if( froot==0 ){ fossil_fatal("Not a forum post: \"%s\"", zName); } fossil_print("fpid = %d\n", fpid); fossil_print("froot = %d\n", froot); pThread = forumthread_create(froot, 1); fossil_print("Chronological:\n"); fossil_print( /* 0 1 2 3 4 5 6 7 */ /* 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123 */ " sid rev closed fpid pIrt pEditPrev pEditTail hash\n"); for(p=pThread->pFirst; p; p=p->pNext){ fossil_print("%4d %4d %7d %9d %9d %9d %9d %8.8s\n", p->sid, p->rev, p->iClosed, p->fpid, p->pIrt ? p->pIrt->fpid : 0, p->pEditPrev ? p->pEditPrev->fpid : 0, p->pEditTail ? p->pEditTail->fpid : 0, p->zUuid); } fossil_print("\nDisplay\n"); for(p=pThread->pDisplay; p; p=p->pDisplay){ fossil_print("%*s", (p->nIndent-1)*3, ""); if( p->pEditTail ){ fossil_print("%d->%d", p->fpid, p->pEditTail->fpid); }else{ fossil_print("%d", p->fpid); } if( p->iClosed ){ fossil_print(" [closed%s]", p->iClosed<0 ? " via parent" : ""); } fossil_print("\n"); } forumthread_delete(pThread); } /* ** WEBPAGE: forumthreadhashlist ** ** Usage: /forumthreadhashlist/HASH-OF-ROOT ** ** This page (accessibly only to admins) shows a list of all artifacts ** associated with a single forum thread. An admin might copy/paste this ** list into the /shun page in order to shun an entire thread. */ void forumthreadhashlist(void){ int fpid; int froot; const char *zName = P("name"); ForumThread *pThread; ForumPost *p; char *fuuid; login_check_credentials(); if( !g.perm.Admin ){ return; } if( zName==0 ){ webpage_error("Missing \"name=\" query parameter"); } fpid = symbolic_name_to_rid(zName, "f"); if( fpid<=0 ){ if( fpid==0 ){ webpage_notfound_error("Unknown forum id: \"%s\"", zName); }else{ ambiguous_page(); } return; } froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); if( froot==0 ){ webpage_notfound_error("Not a forum post: \"%s\"", zName); } fuuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", froot); style_set_current_feature("forum"); style_header("Artifacts Of Forum Thread"); @ <h2> @ Artifacts associated with the forum thread @ <a href="%R/forumthread/%S(fuuid)">%S(fuuid)</a>:</h2> @ <pre> pThread = forumthread_create(froot, 1); for(p=pThread->pFirst; p; p=p->pNext){ @ %h(p->zUuid) } forumthread_delete(pThread); @ </pre> style_finish_page(); } /* ** Render a forum post for display */ void forum_render( const char *zTitle, /* The title. Might be NULL for no title */ const char *zMimetype, /* Mimetype of the message */ const char *zContent, /* Content of the message */ const char *zClass, /* Put in a <div> if not NULL */ int bScroll /* Large message content scrolls if true */ ){ if( zClass ){ @ <div class='%s(zClass)'> } if( zTitle ){ if( zTitle[0] ){ @ <h1>%h(zTitle)</h1> }else{ @ <h1><i>Deleted</i></h1> } } if( zContent && zContent[0] ){ Blob x; const int isFossilWiki = zMimetype==0 || fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0; if( bScroll ){ @ <div class='forumPostBody'> }else{ @ <div class='forumPostFullBody'> } blob_init(&x, 0, 0); blob_append(&x, zContent, -1); safe_html_context(DOCSRC_FORUM); if( isFossilWiki ){ /* Markdown and plain-text rendering add a wrapper DIV resp. PRE ** element around the post, and some CSS relies on its existence ** in order to handle expansion/collapse of the post. Fossil ** Wiki rendering does not do so, so we must wrap those manually ** here. */ @ <div class='fossilWiki'> } wiki_render_by_mimetype(&x, zMimetype); if( isFossilWiki ){ @ </div> } blob_reset(&x); @ </div> }else{ @ <i>Deleted</i> } if( zClass ){ @ </div> } } /* ** Compute a display name from a login name. ** ** If the input login is found in the USER table, then check the USER.INFO ** field to see if it has display-name followed by an email address. ** If it does, that becomes the new display name. If not, let the display ** name just be the login. ** ** Space to hold the returned name is obtained from fossil_strdup() or ** mprintf() and should be freed by the caller. ** ** HTML markup within the reply has been property escaped. Hyperlinks ** may have been added. The result is safe for use with %s. */ static char *display_name_from_login(const char *zLogin){ static Stmt q; char *zResult; db_static_prepare(&q, "SELECT display_name(info) FROM user WHERE login=$login" ); db_bind_text(&q, "$login", zLogin); if( db_step(&q)==SQLITE_ROW && db_column_type(&q,0)==SQLITE_TEXT ){ const char *zDisplay = db_column_text(&q,0); if( fossil_strcmp(zDisplay,zLogin)==0 ){ zResult = mprintf("%z%h</a>", href("%R/timeline?ss=v&y=f&vfx&u=%t",zLogin),zLogin); }else{ zResult = mprintf("%s (%z%h</a>)", zDisplay, href("%R/timeline?ss=v&y=f&vfx&u=%t",zLogin),zLogin); } }else{ zResult = mprintf("%z%h</a>", href("%R/timeline?ss=v&y=f&vfx&u=%t",zLogin),zLogin); } db_reset(&q); return zResult; } /* ** Compute and return the display name for a ForumPost. If ** pManifest is not NULL, then it is a Manifest object for the post. ** if pManifest is NULL, this routine has to fetch and parse the ** Manifest object for itself. ** ** Memory to hold the display name is attached to p->zDisplayName ** and will be freed together with the ForumPost object p when it ** is freed. ** ** The returned text has had all HTML markup escaped and is safe for ** use within %s. */ static char *forum_post_display_name(ForumPost *p, Manifest *pManifest){ Manifest *pToFree = 0; if( p->zDisplayName ) return p->zDisplayName; if( pManifest==0 ){ pManifest = pToFree = manifest_get(p->fpid, CFTYPE_FORUM, 0); if( pManifest==0 ) return "(unknown)"; } p->zDisplayName = display_name_from_login(pManifest->zUser); if( pToFree ) manifest_destroy(pToFree); if( p->zDisplayName==0 ) return "(unknown)"; return p->zDisplayName; } /* ** Display a single post in a forum thread. */ static void forum_display_post( ForumPost *p, /* Forum post to display */ int iIndentScale, /* Indent scale factor */ int bRaw, /* True to omit the border */ int bUnf, /* True to leave the post unformatted */ int bHist, /* True if showing edit history */ int bSelect, /* True if this is the selected post */ char *zQuery /* Common query string */ ){ char *zPosterName; /* Name of user who originally made this post */ char *zEditorName; /* Name of user who provided the current edit */ char *zDate; /* The time/date string */ char *zHist; /* History query string */ Manifest *pManifest; /* Manifest comprising the current post */ int bPrivate; /* True for posts awaiting moderation */ int bSameUser; /* True if author is also the reader */ int iIndent; /* Indent level */ int iClosed; /* True if (sub)thread is closed */ const char *zMimetype;/* Formatting MIME type */ /* Get the manifest for the post. Abort if not found (e.g. shunned). */ pManifest = manifest_get(p->fpid, CFTYPE_FORUM, 0); if( !pManifest ) return; iClosed = forumpost_is_closed(p, 1); /* When not in raw mode, create the border around the post. */ if( !bRaw ){ /* Open the <div> enclosing the post. Set the class string to mark the post ** as selected and/or obsolete. */ iIndent = (p->pEditHead ? p->pEditHead->nIndent : p->nIndent)-1; @ <div id='forum%d(p->fpid)' class='forumTime\ @ %s(bSelect ? " forumSel" : "")\ @ %s(iClosed ? " forumClosed" : "")\ @ %s(p->pEditTail ? " forumObs" : "")' \ if( iIndent && iIndentScale ){ @ style='margin-left:%d(iIndent*iIndentScale)ex;'> }else{ @ > } /* If this is the first post (or an edit thereof), emit the thread title. */ if( pManifest->zThreadTitle ){ @ <h1>%h(pManifest->zThreadTitle)</h1> } /* Begin emitting the header line. The forum of the title ** varies depending on whether: ** * The post is unedited ** * The post was last edited by the original author ** * The post was last edited by a different person */ if( p->pEditHead ){ zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->pEditHead->rDate); }else{ zPosterName = forum_post_display_name(p, pManifest); zEditorName = zPosterName; } zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->rDate); if( p->pEditPrev ){ zPosterName = forum_post_display_name(p->pEditHead, 0); zEditorName = forum_post_display_name(p, pManifest); zHist = bHist ? "" : zQuery[0]==0 ? "?hist" : "&hist"; @ <h3 class='forumPostHdr'>(%d(p->sid)\ @ .%0*d(fossil_num_digits(p->nEdit))(p->rev)) if( fossil_strcmp(zPosterName, zEditorName)==0 ){ @ By %s(zPosterName) on %h(zDate) edited from \ @ %z(href("%R/forumpost/%S%s%s",p->pEditPrev->zUuid,zQuery,zHist))\ @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a> }else{ @ Originally by %s(zPosterName) \ @ with edits by %s(zEditorName) on %h(zDate) from \ @ %z(href("%R/forumpost/%S%s%s",p->pEditPrev->zUuid,zQuery,zHist))\ @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a> } }else{ zPosterName = forum_post_display_name(p, pManifest); @ <h3 class='forumPostHdr'>(%d(p->sid)) @ By %s(zPosterName) on %h(zDate) } fossil_free(zDate); /* If debugging is enabled, link to the artifact page. */ if( g.perm.Debug ){ @ <span class="debug">\ @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span> } /* If this is a reply, refer back to the parent post. */ if( p->pIrt ){ @ in reply to %z(href("%R/forumpost/%S%s",p->pIrt->zUuid,zQuery))\ @ %d(p->pIrt->sid)\ if( p->pIrt->nEdit ){ @ .%0*d(fossil_num_digits(p->pIrt->nEdit))(p->pIrt->rev)\ } @ </a> } /* If this post was later edited, refer forward to the next edit. */ if( p->pEditNext ){ @ updated by %z(href("%R/forumpost/%S%s",p->pEditNext->zUuid,zQuery))\ @ %d(p->pEditNext->sid)\ @ .%0*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a> } /* Provide a link to select the individual post. */ if( !bSelect ){ @ %z(href("%R/forumpost/%!S%s",p->zUuid,zQuery))[link]</a> } /* Provide a link to the raw source code. */ if( !bUnf ){ @ %z(href("%R/forumpost/%!S?raw",p->zUuid))[source]</a> } @ </h3> } /* Check if this post is approved, also if it's by the current user. */ bPrivate = content_is_private(p->fpid); bSameUser = login_is_individual() && fossil_strcmp(pManifest->zUser, g.zLogin)==0; /* Render the post if the user is able to see it. */ if( bPrivate && !g.perm.ModForum && !bSameUser ){ @ <p><span class="modpending">Awaiting Moderator Approval</span></p> }else{ if( bRaw || bUnf || p->pEditTail ){ zMimetype = "text/plain"; }else{ zMimetype = pManifest->zMimetype; } forum_render(0, zMimetype, pManifest->zWiki, 0, !bRaw); } /* When not in raw mode, finish creating the border around the post. */ if( !bRaw ){ /* If the user is able to write to the forum and if this post has not been ** edited, create a form with various interaction buttons. */ if( g.perm.WrForum && !p->pEditTail ){ @ <div class="forumpost-single-controls">\ @ <form action="%R/forumedit" method="POST"> @ <input type="hidden" name="fpid" value="%s(p->zUuid)"> if( !bPrivate ){ /* Reply and Edit are only available if the post has been ** approved. Closed threads can only be edited or replied to ** if forumpost_may_close() is true but a user may delete ** their own posts even if they are closed. */ if( forumpost_may_close() || !iClosed ){ @ <input type="submit" name="reply" value="Reply"> if( g.perm.Admin || (bSameUser && !iClosed) ){ @ <input type="submit" name="edit" value="Edit"> } if( g.perm.Admin || bSameUser ){ @ <input type="submit" name="nullout" value="Delete"> } } }else if( g.perm.ModForum ){ /* Allow moderators to approve or reject pending posts. Also allow ** forum supervisors to mark non-special users as trusted and therefore ** able to post unmoderated. */ @ <input type="submit" name="approve" value="Approve"> @ <input type="submit" name="reject" value="Reject"> if( g.perm.AdminForum && !login_is_special(pManifest->zUser) ){ @ <br><label><input type="checkbox" name="trust"> @ Trust user "%h(pManifest->zUser)" so that future posts by \ @ "%h(pManifest->zUser)" do not require moderation. @ </label> @ <input type="hidden" name="trustuser" value="%h(pManifest->zUser)"> } }else if( bSameUser ){ /* Allow users to delete (reject) their own pending posts. */ @ <input type="submit" name="reject" value="Delete"> } login_insert_csrf_secret(); @ </form> if( bSelect && forumpost_may_close() && iClosed>=0 ){ int iHead = forumpost_head_rid(p->fpid); @ <form method="post" \ @ action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'> login_insert_csrf_secret(); @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" /> if( moderation_pending(p->fpid)==0 ){ @ <input type="submit" value='%s(iClosed ? "Re-open" : "Close")' /> } @ </form> } @ </div> } @ </div> } /* Clean up. */ manifest_destroy(pManifest); } /* ** Possible display modes for forum_display_thread(). */ enum { FD_RAW, /* Like FD_SINGLE, but additionally omit the border, force ** unformatted mode, and inhibit history mode */ FD_SINGLE, /* Render a single post and (optionally) its edit history */ FD_CHRONO, /* Render all posts in chronological order */ FD_HIER, /* Render all posts in an indented hierarchy */ }; /* ** Display a forum thread. If mode is FD_RAW or FD_SINGLE, display only a ** single post from the thread and (optionally) its edit history. */ static void forum_display_thread( int froot, /* Forum thread root post ID */ int fpid, /* Selected forum post ID, or 0 if none selected */ int mode, /* Forum display mode, one of the FD_* enumerations */ int autoMode, /* mode was selected automatically */ int bUnf, /* True if rendering unformatted */ int bHist /* True if showing edit history, ignored for FD_RAW */ ){ ForumThread *pThread; /* Thread structure */ ForumPost *pSelect; /* Currently selected post, or NULL if none */ ForumPost *p; /* Post iterator pointer */ char zQuery[30]; /* Common query string */ int iIndentScale = 4; /* Indent scale factor, measured in "ex" units */ int sid; /* Comparison serial ID */ int i; /* In raw mode, force unformatted display and disable history. */ if( mode == FD_RAW ){ bUnf = 1; bHist = 0; } /* Thread together the posts and (optionally) compute the hierarchy. */ pThread = forumthread_create(froot, mode==FD_HIER); /* Compute the appropriate indent scaling. */ if( mode==FD_HIER ){ iIndentScale = 4; while( iIndentScale>1 && iIndentScale*pThread->mxIndent>25 ){ iIndentScale--; } }else{ iIndentScale = 0; } /* Find the selected post, or (depending on parameters) its latest edit. */ pSelect = fpid ? forumpost_forward(pThread->pFirst, fpid) : 0; if( !bHist && mode!=FD_RAW && pSelect && pSelect->pEditTail ){ pSelect = pSelect->pEditTail; } /* When displaying only a single post, abort if no post was selected or the ** selected forum post does not exist in the thread. Otherwise proceed to ** display the entire thread without marking any posts as selected. */ if( !pSelect && (mode==FD_RAW || mode==FD_SINGLE) ){ return; } /* Create the common query string to append to nearly all post links. */ i = 0; if( !autoMode ){ char m = 'a'; switch( mode ){ case FD_RAW: m = 'r'; break; case FD_CHRONO: m = 'c'; break; case FD_HIER: m = 'h'; break; case FD_SINGLE: m = 's'; break; } zQuery[i++] = '?'; zQuery[i++] = 't'; zQuery[i++] = '='; zQuery[i++] = m; } if( bUnf ){ zQuery[i] = i==0 ? '?' : '&'; i++; zQuery[i++] = 'u'; zQuery[i++] = 'n'; zQuery[i++] = 'f'; } if( bHist ){ zQuery[i] = i==0 ? '?' : '&'; i++; zQuery[i++] = 'h'; zQuery[i++] = 'i'; zQuery[i++] = 's'; zQuery[i++] = 't'; } assert( i<(int)sizeof(zQuery) ); zQuery[i] = 0; assert( zQuery[0]==0 || zQuery[0]=='?' ); /* Identify which post to display first. If history is shown, start with the ** original, unedited post. Otherwise advance to the post's latest edit. */ if( mode==FD_RAW || mode==FD_SINGLE ){ p = pSelect; if( bHist && p->pEditHead ) p = p->pEditHead; }else{ p = mode==FD_CHRONO ? pThread->pFirst : pThread->pDisplay; if( !bHist && p->pEditTail ) p = p->pEditTail; } /* Display the appropriate subset of posts in sequence. */ while( p ){ /* Display the post. */ forum_display_post(p, iIndentScale, mode==FD_RAW, bUnf, bHist, p==pSelect, zQuery); /* Advance to the next post in the thread. */ if( mode==FD_CHRONO ){ /* Chronological mode: display posts (optionally including edits) in their ** original commit order. */ if( bHist ){ p = p->pNext; }else{ sid = p->sid; if( p->pEditHead ) p = p->pEditHead; do p = p->pNext; while( p && p->sid<=sid ); if( p && p->pEditTail ) p = p->pEditTail; } }else if( bHist && p->pEditNext ){ /* Hierarchical and single mode: display each post's edits in sequence. */ p = p->pEditNext; }else if( mode==FD_HIER ){ /* Hierarchical mode: after displaying with each post (optionally ** including edits), go to the next post in computed display order. */ p = p->pEditHead ? p->pEditHead->pDisplay : p->pDisplay; if( !bHist && p && p->pEditTail ) p = p->pEditTail; }else{ /* Single and raw mode: terminate after displaying the selected post and ** (optionally) its edits. */ break; } } /* Undocumented "threadtable" query parameter causes thread table to be ** displayed for debugging purposes. */ if( PB("threadtable") ){ @ <hr> @ <table border="1" cellpadding="3" cellspacing="0"> @ <tr><th>sid<th>rev<th>fpid<th>pIrt<th>pEditHead<th>pEditTail\ @ <th>pEditNext<th>pEditPrev<th>pDisplay<th>hash for(p=pThread->pFirst; p; p=p->pNext){ @ <tr><td>%d(p->sid)<td>%d(p->rev)<td>%d(p->fpid)\ @ <td>%d(p->pIrt ? p->pIrt->fpid : 0)\ @ <td>%d(p->pEditHead ? p->pEditHead->fpid : 0)\ @ <td>%d(p->pEditTail ? p->pEditTail->fpid : 0)\ @ <td>%d(p->pEditNext ? p->pEditNext->fpid : 0)\ @ <td>%d(p->pEditPrev ? p->pEditPrev->fpid : 0)\ @ <td>%d(p->pDisplay ? p->pDisplay->fpid : 0)\ @ <td>%S(p->zUuid)</tr> } @ </table> } /* Clean up. */ forumthread_delete(pThread); } /* ** Emit Forum Javascript which applies (or optionally can apply) ** to all forum-related pages. It does not include page-specific ** code (e.g. "forum.js"). */ static void forum_emit_js(void){ builtin_fossil_js_bundle_or("copybutton", "pikchr", NULL); builtin_request_js("fossil.page.forumpost.js"); } /* ** WEBPAGE: forumpost ** ** Show a single forum posting. The posting is shown in context with ** its entire thread. The selected posting is enclosed within ** <div class='forumSel'>...</div>. Javascript is used to move the ** selected posting into view after the page loads. ** ** Query parameters: ** ** name=X REQUIRED. The hash of the post to display. ** t=a Automatic display mode, i.e. hierarchical for ** desktop and chronological for mobile. This is the ** default if the "t" query parameter is omitted. ** t=c Show posts in the order they were written. ** t=h Show posts using hierarchical indenting. ** t=s Show only the post specified by "name=X". ** t=r Alias for "t=c&unf&hist". ** t=y Alias for "t=s&unf&hist". ** raw Alias for "t=s&unf". Additionally, omit the border ** around the post, and ignore "t" and "hist". ** unf Show the original, unformatted source text. ** hist Show edit history in addition to current posts. */ void forumpost_page(void){ forumthread_page(); } /* ** WEBPAGE: forumthread ** ** Show all forum messages associated with a particular message thread. ** The result is basically the same as /forumpost except that none of ** the postings in the thread are selected. ** ** Query parameters: ** ** name=X REQUIRED. The hash of any post of the thread. ** t=a Automatic display mode, i.e. hierarchical for ** desktop and chronological for mobile. This is the ** default if the "t" query parameter is omitted. ** t=c Show posts in the order they were written. ** t=h Show posts using hierarchical indenting. ** unf Show the original, unformatted source text. ** hist Show edit history in addition to current posts. */ void forumthread_page(void){ int fpid; int froot; char *zThreadTitle; const char *zName = P("name"); const char *zMode = PD("t","a"); int bRaw = PB("raw"); int bUnf = PB("unf"); int bHist = PB("hist"); int mode = 0; int autoMode = 0; login_check_credentials(); if( !g.perm.RdForum ){ login_needed(g.anon.RdForum); return; } if( zName==0 ){ webpage_error("Missing \"name=\" query parameter"); } cgi_check_for_malice(); fpid = symbolic_name_to_rid(zName, "f"); if( fpid<=0 ){ if( fpid==0 ){ webpage_notfound_error("Unknown forum id: \"%s\"", zName); }else{ ambiguous_page(); } return; } froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); if( froot==0 ){ webpage_notfound_error("Not a forum post: \"%s\"", zName); } /* Decode the mode parameters. */ if( bRaw ){ mode = FD_RAW; bUnf = 1; bHist = 0; cgi_replace_query_parameter("unf", "on"); cgi_delete_query_parameter("hist"); cgi_delete_query_parameter("raw"); }else{ switch( *zMode ){ case 'a': mode = cgi_from_mobile() ? FD_CHRONO : FD_HIER; autoMode=1; break; case 'c': mode = FD_CHRONO; break; case 'h': mode = FD_HIER; break; case 's': mode = FD_SINGLE; break; case 'r': mode = FD_CHRONO; break; case 'y': mode = FD_SINGLE; break; default: webpage_error("Invalid thread mode: \"%s\"", zMode); } if( *zMode=='r' || *zMode=='y') { bUnf = 1; bHist = 1; cgi_replace_query_parameter("t", mode==FD_CHRONO ? "c" : "s"); cgi_replace_query_parameter("unf", "on"); cgi_replace_query_parameter("hist", "on"); } } /* Define the page header. */ zThreadTitle = db_text("", "SELECT" " substr(event.comment,instr(event.comment,':')+2)" " FROM forumpost, event" " WHERE event.objid=forumpost.fpid" " AND forumpost.fpid=%d;", fpid ); style_set_current_feature("forum"); style_header("%s%s", zThreadTitle, *zThreadTitle ? "" : "Forum"); fossil_free(zThreadTitle); if( mode!=FD_CHRONO ){ style_submenu_element("Chronological", "%R/%s/%s?t=c%s%s", g.zPath, zName, bUnf ? "&unf" : "", bHist ? "&hist" : ""); } if( mode!=FD_HIER ){ style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName, bUnf ? "&unf" : "", bHist ? "&hist" : ""); } style_submenu_checkbox("unf", "Unformatted", 0, 0); style_submenu_checkbox("hist", "History", 0, 0); if( g.perm.Admin ){ style_submenu_element("Artifacts", "%R/forumthreadhashlist/%t", zName); } /* Display the thread. */ if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist); /* Emit Forum Javascript. */ builtin_request_js("forum.js"); forum_emit_js(); /* Emit the page style. */ style_finish_page(); } /* ** Return true if a forum post should be moderated. */ static int forum_need_moderation(void){ if( P("domod") ) return 1; if( g.perm.WrTForum ) return 0; if( g.perm.ModForum ) return 0; return 1; } /* ** Return true if the string is white-space only. */ static int whitespace_only(const char *z){ if( z==0 ) return 1; while( z[0] && fossil_isspace(z[0]) ){ z++; } return z[0]==0; } /* Flags for use with forum_post() */ #define FPOST_NO_ALERT 1 /* do not send any alerts */ /* ** Return a flags value for use with the final argument to ** forum_post(), extracted from the CGI environment. */ static int forum_post_flags(void){ int iPostFlags = 0; if( g.perm.Debug && P("fpsilent")!=0 ){ iPostFlags |= FPOST_NO_ALERT; } return iPostFlags; } /* ** Add a new Forum Post artifact to the repository. ** ** Return true if a redirect occurs. */ static int forum_post( const char *zTitle, /* Title. NULL for replies */ int iInReplyTo, /* Post replying to. 0 for new threads */ int iEdit, /* Post being edited, or zero for a new post */ const char *zUser, /* Username. NULL means use login name */ const char *zMimetype, /* Mimetype of content. */ const char *zContent, /* Content */ int iFlags /* FPOST_xyz flag values */ ){ char *zDate; char *zI; char *zG; int iBasis; Blob x, cksum, formatCheck, errMsg; Manifest *pPost; int nContent = zContent ? (int)strlen(zContent) : 0; schema_forum(); if( !g.perm.Admin && (iEdit || iInReplyTo) && forum_rid_is_closed(iEdit ? iEdit : iInReplyTo, 1) ){ forumpost_error_closed(); return 0; } if( iEdit==0 && whitespace_only(zContent) ){ return 0; } if( iInReplyTo==0 && iEdit>0 ){ iBasis = iEdit; iInReplyTo = db_int(0, "SELECT firt FROM forumpost WHERE fpid=%d", iEdit); }else{ iBasis = iInReplyTo; } webpage_assert( (zTitle==0)+(iInReplyTo==0)==1 ); blob_init(&x, 0, 0); zDate = date_in_standard_format("now"); blob_appendf(&x, "D %s\n", zDate); fossil_free(zDate); zG = db_text(0, "SELECT uuid FROM blob, forumpost" " WHERE blob.rid==forumpost.froot" " AND forumpost.fpid=%d", iBasis); if( zG ){ blob_appendf(&x, "G %s\n", zG); fossil_free(zG); } if( zTitle ){ blob_appendf(&x, "H %F\n", zTitle); } zI = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", iInReplyTo); if( zI ){ blob_appendf(&x, "I %s\n", zI); fossil_free(zI); } if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")!=0 ){ blob_appendf(&x, "N %s\n", zMimetype); } if( iEdit>0 ){ char *zP = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", iEdit); if( zP==0 ) webpage_error("missing edit artifact %d", iEdit); blob_appendf(&x, "P %s\n", zP); fossil_free(zP); } if( zUser==0 ){ if( login_is_nobody() ){ zUser = "anonymous"; }else{ zUser = login_name(); } } blob_appendf(&x, "U %F\n", zUser); blob_appendf(&x, "W %d\n%s\n", nContent, zContent); md5sum_blob(&x, &cksum); blob_appendf(&x, "Z %b\n", &cksum); blob_reset(&cksum); /* Verify that the artifact we are creating is well-formed */ blob_init(&formatCheck, 0, 0); blob_init(&errMsg, 0, 0); blob_copy(&formatCheck, &x); pPost = manifest_parse(&formatCheck, 0, &errMsg); if( pPost==0 ){ webpage_error("malformed forum post artifact - %s", blob_str(&errMsg)); } webpage_assert( pPost->type==CFTYPE_FORUM ); manifest_destroy(pPost); if( P("dryrun") ){ @ <div class='debug'> @ This is the artifact that would have been generated: @ <pre>%h(blob_str(&x))</pre> @ </div> blob_reset(&x); return 0; }else{ int nrid; db_begin_transaction(); nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, forum_need_moderation()); blob_reset(&x); if( (iFlags & FPOST_NO_ALERT)!=0 ){ alert_unqueue('f', nrid); } db_commit_transaction(); cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid)); return 1; } } /* ** Paint the form elements for entering a Forum post */ static void forum_post_widget( const char *zTitle, const char *zMimetype, const char *zContent ){ if( zTitle ){ @ Title: <input type="input" name="title" value="%h(zTitle)" size="50" @ maxlength="125"><br> } @ %z(href("%R/markup_help"))Markup style</a>: mimetype_option_menu(zMimetype, "mimetype"); @ <br><textarea aria-label="Content:" name="content" class="wikiedit" \ @ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea><br> } /* ** WEBPAGE: forumpost_close hidden ** WEBPAGE: forumpost_reopen hidden ** ** fpid=X Hash of the post to be edited. REQUIRED ** reason=X Optional reason for closure. ** ** Closes or re-opens the given forum post, within the bounds of the ** API for forumpost_close(). After (perhaps) modifying the "closed" ** status of the given thread, it redirects to that post's thread ** view. Requires admin privileges. */ void forum_page_close(void){ const char *zFpid = PD("fpid",""); const char *zReason = 0; int fClose; int fpid; login_check_credentials(); if( forumpost_may_close()==0 ){ login_needed(g.anon.Admin); return; } cgi_csrf_verify(); fpid = symbolic_name_to_rid(zFpid, "f"); if( fpid<=0 ){ webpage_error("Missing or invalid fpid query parameter"); } fClose = sqlite3_strglob("*_close*", g.zPath)==0; if( fClose ) zReason = PD("reason",0); forumpost_close(fpid, fClose, zReason); cgi_redirectf("%R/forumpost/%S",zFpid); return; } /* ** WEBPAGE: forumnew ** WEBPAGE: forumedit ** ** Start a new thread on the forum or reply to an existing thread. ** But first prompt to see if the user would like to log in. */ void forum_page_init(void){ int isEdit; char *zGoto; login_check_credentials(); if( !g.perm.WrForum ){ login_needed(g.anon.WrForum); return; } if( sqlite3_strglob("*edit*", g.zPath)==0 ){ zGoto = mprintf("forume2?fpid=%S",PD("fpid","")); isEdit = 1; }else{ zGoto = mprintf("forume1"); isEdit = 0; } if( login_is_individual() ){ if( isEdit ){ forumedit_page(); }else{ forumnew_page(); } return; } style_set_current_feature("forum"); style_header("%h As Anonymous?", isEdit ? "Reply" : "Post"); @ <p>You are not logged in. @ <p><table border="0" cellpadding="10"> @ <tr><td> @ <form action="%s(zGoto)" method="POST"> @ <input type="submit" value="Remain Anonymous"> @ </form> @ <td>Post to the forum anonymously if( login_self_register_available(0) ){ @ <tr><td> @ <form action="%R/register" method="POST"> @ <input type="hidden" name="g" value="%s(zGoto)"> @ <input type="submit" value="Create An Account"> @ </form> @ <td>Create a new account and post using that new account } @ <tr><td> @ <form action="%R/login" method="POST"> @ <input type="hidden" name="g" value="%s(zGoto)"> @ <input type="hidden" name="noanon" value="1"> @ <input type="submit" value="Login"> @ </form> @ <td>Log into an existing account @ </table> forum_emit_js(); style_finish_page(); fossil_free(zGoto); } /* ** Write the "From: USER" line on the webpage. */ static void forum_from_line(void){ if( login_is_nobody() ){ @ From: anonymous<br> }else{ @ From: %h(login_name())<br> } } static void forum_render_debug_options(void){ if( g.perm.Debug ){ /* Give extra control over the post to users with the special * Debug capability, which includes Admin and Setup users */ @ <div class="debug"> @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ @ Dry run</label> @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ @ Require moderator approval</label> @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ @ Show query parameters</label> @ <br><label><input type="checkbox" name="fpsilent" %s(PCK("fpsilent"))> \ @ Do not sent notification emails</label> @ </div> } } /* ** WEBPAGE: forume1 ** ** Start a new forum thread. */ void forumnew_page(void){ const char *zTitle = PDT("title",""); const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); const char *zContent = PDT("content",""); login_check_credentials(); if( !g.perm.WrForum ){ login_needed(g.anon.WrForum); return; } if( P("submit") && cgi_csrf_safe(2) ){ if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent, forum_post_flags()) ) return; } if( P("preview") && !whitespace_only(zContent) ){ @ <h1>Preview:</h1> forum_render(zTitle, zMimetype, zContent, "forumEdit", 1); } style_set_current_feature("forum"); style_header("New Forum Thread"); @ <form action="%R/forume1" method="POST"> @ <h1>New Thread:</h1> forum_from_line(); forum_post_widget(zTitle, zMimetype, zContent); @ <input type="submit" name="preview" value="Preview"> if( P("preview") && !whitespace_only(zContent) ){ @ <input type="submit" name="submit" value="Submit"> }else{ @ <input type="submit" name="submit" value="Submit" disabled> } forum_render_debug_options(); login_insert_csrf_secret(); @ </form> forum_emit_js(); style_finish_page(); } /* ** WEBPAGE: forume2 ** ** Edit an existing forum message. ** Query parameters: ** ** fpid=X Hash of the post to be edited. REQUIRED */ void forumedit_page(void){ int fpid; int froot; Manifest *pPost = 0; Manifest *pRootPost = 0; const char *zMimetype = 0; const char *zContent = 0; const char *zTitle = 0; char *zDate = 0; const char *zFpid = PD("fpid",""); int isCsrfSafe; int isDelete = 0; int iClosed = 0; int bSameUser; /* True if author is also the reader */ int bPreview; /* True in preview mode. */ int bPrivate; /* True if post is private (not yet moderated) */ int bReply; /* True if replying to a post */ login_check_credentials(); if( !g.perm.WrForum ){ login_needed(g.anon.WrForum); return; } fpid = symbolic_name_to_rid(zFpid, "f"); if( fpid<=0 || (pPost = manifest_get(fpid, CFTYPE_FORUM, 0))==0 ){ webpage_error("Missing or invalid fpid query parameter"); } froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); if( froot==0 || (pRootPost = manifest_get(froot, CFTYPE_FORUM, 0))==0 ){ webpage_error("fpid does not appear to be a forum post: \"%d\"", fpid); } if( P("cancel") ){ cgi_redirectf("%R/forumpost/%S",zFpid); return; } bPreview = P("preview")!=0; bReply = P("reply")!=0; iClosed = forum_rid_is_closed(fpid, 1); isCsrfSafe = cgi_csrf_safe(2); bPrivate = content_is_private(fpid); bSameUser = login_is_individual() && fossil_strcmp(pPost->zUser, g.zLogin)==0; if( isCsrfSafe && (g.perm.ModForum || (bPrivate && bSameUser)) ){ if( g.perm.ModForum && P("approve") ){ const char *zUserToTrust; moderation_approve('f', fpid); if( g.perm.AdminForum && PB("trust") && (zUserToTrust = P("trustuser"))!=0 ){ db_unprotect(PROTECT_USER); db_multi_exec("UPDATE user SET cap=cap||'4' " "WHERE login=%Q AND cap NOT GLOB '*4*'", zUserToTrust); db_protect_pop(); } cgi_redirectf("%R/forumpost/%S",P("fpid")); return; } if( P("reject") ){ char *zParent = db_text(0, "SELECT uuid FROM forumpost, blob" " WHERE forumpost.fpid=%d AND blob.rid=forumpost.firt", fpid ); moderation_disapprove(fpid); if( zParent ){ cgi_redirectf("%R/forumpost/%S",zParent); }else{ cgi_redirectf("%R/forum"); } return; } } style_set_current_feature("forum"); isDelete = P("nullout")!=0; if( P("submit") && isCsrfSafe && (zContent = PDT("content",""))!=0 && (!whitespace_only(zContent) || isDelete) ){ int done = 1; const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); if( bReply ){ done = forum_post(0, fpid, 0, 0, zMimetype, zContent, forum_post_flags()); }else if( P("edit") || isDelete ){ done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent, forum_post_flags()); }else{ webpage_error("Missing 'reply' query parameter"); } if( done ) return; } if( isDelete ){ zMimetype = "text/x-fossil-wiki"; zContent = ""; if( pPost->zThreadTitle ) zTitle = ""; style_header("Delete %s", zTitle ? "Post" : "Reply"); @ <h1>Original Post:</h1> forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki, "forumEdit", 1); @ <h1>Change Into:</h1> forum_render(zTitle, zMimetype, zContent,"forumEdit", 1); @ <form action="%R/forume2" method="POST"> login_insert_csrf_secret(); @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> @ <input type="hidden" name="nullout" value="1"> @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> @ <input type="hidden" name="content" value="%h(zContent)"> if( zTitle ){ @ <input aria-label="Title" type="hidden" name="title" value="%h(zTitle)"> } }else if( P("edit") ){ /* Provide an edit to the fpid post */ zMimetype = P("mimetype"); zContent = PT("content"); zTitle = P("title"); if( zContent==0 ) zContent = fossil_strdup(pPost->zWiki); if( zMimetype==0 ) zMimetype = fossil_strdup(pPost->zMimetype); if( zTitle==0 && pPost->zThreadTitle!=0 ){ zTitle = fossil_strdup(pPost->zThreadTitle); } style_header("Edit %s", zTitle ? "Post" : "Reply"); @ <h2>Original Post:</h2> forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki, "forumEdit", 1); if( bPreview ){ @ <h2>Preview of Edited Post:</h2> forum_render(zTitle, zMimetype, zContent,"forumEdit", 1); } @ <h2>Revised Message:</h2> @ <form action="%R/forume2" method="POST"> login_insert_csrf_secret(); @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> @ <input type="hidden" name="edit" value="1"> forum_from_line(); forum_post_widget(zTitle, zMimetype, zContent); }else{ /* Reply */ char *zDisplayName; zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); zContent = PDT("content",""); style_header("Reply"); @ <h2>Replying to @ <a href="%R/forumpost/%!S(zFpid)" target="_blank">%S(zFpid)</a> if( pRootPost->zThreadTitle ){ @ in thread @ <span class="forumPostReplyTitle">%h(pRootPost->zThreadTitle)</span> } @ </h2> zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", pPost->rDate); zDisplayName = display_name_from_login(pPost->zUser); @ <h3 class='forumPostHdr'>By %s(zDisplayName) on %h(zDate)</h3> fossil_free(zDisplayName); fossil_free(zDate); forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1); if( bPreview && !whitespace_only(zContent) ){ @ <h2>Preview:</h2> forum_render(0, zMimetype,zContent, "forumEdit", 1); } @ <h2>Enter Reply:</h2> @ <form action="%R/forume2" method="POST"> @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> @ <input type="hidden" name="reply" value="1"> forum_from_line(); forum_post_widget(0, zMimetype, zContent); } if( !isDelete ){ @ <input type="submit" name="preview" value="Preview"> } @ <input type="submit" name="cancel" value="Cancel"> if( (bPreview && !whitespace_only(zContent)) || isDelete ){ if( !iClosed || g.perm.Admin ) { @ <input type="submit" name="submit" value="Submit"> } } forum_render_debug_options(); login_insert_csrf_secret(); @ </form> forum_emit_js(); style_finish_page(); } /* ** WEBPAGE: setup_forum ** ** Forum configuration and metrics. */ void forum_setup(void){ /* boolean config settings specific to the forum. */ const char * zSettingsBool[] = { "forum-close-policy", NULL /* sentinel entry */ }; login_check_credentials(); if( !g.perm.Setup ){ login_needed(g.anon.Setup); return; } style_set_current_feature("forum"); style_header("Forum Setup"); @ <h2>Metrics</h2> { int nPosts = db_int(0, "SELECT COUNT(*) FROM event WHERE type='f'"); @ <p><a href='%R/forum'>Forum posts</a>: @ <a href='%R/timeline?y=f'>%d(nPosts)</a></p> } @ <h2>Supervisors</h2> @ <p>Users with capabilities 's', 'a', or '6'.</p> { Stmt q = empty_Stmt; int nRows = 0; db_prepare(&q, "SELECT uid, login, cap FROM user " "WHERE cap GLOB '*[as6]*' ORDER BY login"); @ <table class='bordered'> @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> @ <tbody> while( SQLITE_ROW==db_step(&q) ){ const int iUid = db_column_int(&q, 0); const char *zUser = db_column_text(&q, 1); const char *zCap = db_column_text(&q, 2); ++nRows; @ <tr> @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td> @ <td>(%h(zCap))</td> @ </tr> } db_finalize(&q); @</tbody></table> if( 0==nRows ){ @ No supervisors }else{ @ %d(nRows) supervisor(s) } } @ <h2>Moderators</h2> @ <p>Users with capability '5'.</p> { Stmt q = empty_Stmt; int nRows = 0; db_prepare(&q, "SELECT uid, login, cap FROM user " "WHERE cap GLOB '*5*' ORDER BY login"); @ <table class='bordered'> @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> @ <tbody> while( SQLITE_ROW==db_step(&q) ){ const int iUid = db_column_int(&q, 0); const char *zUser = db_column_text(&q, 1); const char *zCap = db_column_text(&q, 2); ++nRows; @ <tr> @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td> @ <td>(%h(zCap))</td> @ </tr> } db_finalize(&q); @ </tbody></table> if( 0==nRows ){ @ No non-supervisor moderators }else{ @ %d(nRows) moderator(s) } } @ <h2>Settings</h2> @ <p>Configuration settings specific to the forum.</p> if( P("submit") && cgi_csrf_safe(2) ){ int i = 0; const char *zSetting; db_begin_transaction(); while( (zSetting = zSettingsBool[i++]) ){ const char *z = P(zSetting); if( !z || !z[0] ) z = "off"; db_set(zSetting/*works-like:"x"*/, z, 0); } db_end_transaction(0); @ <p><em>Settings saved.</em></p> } { int i = 0; const char *zSetting; @ <form action="%R/setup_forum" method="post"> login_insert_csrf_secret(); @ <table class='forum-settings-list'><tbody> while( (zSetting = zSettingsBool[i++]) ){ @ <tr><td> onoff_attribute("", zSetting, zSetting/*works-like:"x"*/, 0, 0); @ </td><td> @ <a href='%R/help?cmd=%h(zSetting)'>%h(zSetting)</a> @ </td></tr> } @ </tbody></table> @ <input type='submit' name='submit' value='Apply changes'> @ </form> } style_finish_page(); } /* ** WEBPAGE: forummain ** WEBPAGE: forum ** ** The main page for the forum feature. Show a list of recent forum ** threads. Also show a search box at the top if search is enabled, ** and a button for creating a new thread, if enabled. ** ** Query parameters: ** ** n=N The number of threads to show on each page ** x=X Skip the first X threads ** s=Y Search for term Y. */ void forum_main_page(void){ Stmt q; int iLimit = 0, iOfst, iCnt; int srchFlags; const int isSearch = P("s")!=0; char const *zLimit = 0; login_check_credentials(); srchFlags = search_restrict(SRCH_FORUM); if( !g.perm.RdForum ){ login_needed(g.anon.RdForum); return; } cgi_check_for_malice(); style_set_current_feature("forum"); style_header( "%s", isSearch ? "Forum Search Results" : "Forum" ); style_submenu_element("Timeline", "%R/timeline?ss=v&y=f&vfx"); if( g.perm.WrForum ){ style_submenu_element("New Thread","%R/forumnew"); }else{ /* Can't combine this with previous case using the ternary operator * because that causes an error yelling about "non-constant format" * with some compilers. I can't see it, since both expressions have * the same format, but I'm no C spec lawyer. */ style_submenu_element("New Thread","%R/login"); } if( g.perm.ModForum && moderation_needed() ){ style_submenu_element("Moderation Requests", "%R/modreq"); } if( (srchFlags & SRCH_FORUM)!=0 ){ if( search_screen(SRCH_FORUM, 0) ){ style_submenu_element("Recent Threads","%R/forum"); style_finish_page(); return; } } cookie_read_parameter("n","forum-n"); zLimit = P("n"); if( zLimit!=0 ){ iLimit = atoi(zLimit); if( iLimit>=0 && P("udc")!=0 ){ cookie_write_parameter("n","forum-n",0); } } if( iLimit<=0 ){ cgi_replace_query_parameter("n", fossil_strdup("25")) /*for the sake of Max, below*/; iLimit = 25; } style_submenu_entry("n","Max:",4,0); iOfst = atoi(PD("x","0")); iCnt = 0; if( db_table_exists("repository","forumpost") ){ db_prepare(&q, "WITH thread(age,duration,cnt,root,last) AS (" " SELECT" " julianday('now') - max(fmtime)," " max(fmtime) - min(fmtime)," " sum(fprev IS NULL)," " froot," " (SELECT fpid FROM forumpost AS y" " WHERE y.froot=x.froot %s" " ORDER BY y.fmtime DESC LIMIT 1)" " FROM forumpost AS x" " WHERE %s" " GROUP BY froot" " ORDER BY 1 LIMIT %d OFFSET %d" ")" "SELECT" " thread.age," /* 0 */ " thread.duration," /* 1 */ " thread.cnt," /* 2 */ " blob.uuid," /* 3 */ " substr(event.comment,instr(event.comment,':')+1)," /* 4 */ " thread.last" /* 5 */ " FROM thread, blob, event" " WHERE blob.rid=thread.last" " AND event.objid=thread.last" " ORDER BY 1;", g.perm.ModForum ? "" : "AND y.fpid NOT IN private" /*safe-for-%s*/, g.perm.ModForum ? "true" : "fpid NOT IN private" /*safe-for-%s*/, iLimit+1, iOfst ); while( db_step(&q)==SQLITE_ROW ){ char *zAge = human_readable_age(db_column_double(&q,0)); int nMsg = db_column_int(&q, 2); const char *zUuid = db_column_text(&q, 3); const char *zTitle = db_column_text(&q, 4); if( iCnt==0 ){ if( iOfst>0 ){ @ <h1>Threads at least %s(zAge) old</h1> }else{ @ <h1>Most recent threads</h1> } @ <div class='forumPosts fileage'><table width="100%%"> if( iOfst>0 ){ if( iOfst>iLimit ){ @ <tr><td colspan="3">\ @ %z(href("%R/forum?x=%d&n=%d",iOfst-iLimit,iLimit))\ @ ↑ Newer...</a></td></tr> }else{ @ <tr><td colspan="3">%z(href("%R/forum?n=%d",iLimit))\ @ ↑ Newer...</a></td></tr> } } } iCnt++; if( iCnt>iLimit ){ @ <tr><td colspan="3">\ @ %z(href("%R/forum?x=%d&n=%d",iOfst+iLimit,iLimit))\ @ ↓ Older...</a></td></tr> fossil_free(zAge); break; } @ <tr><td>%h(zAge) ago</td> @ <td>%z(href("%R/forumpost/%S",zUuid))%h(zTitle)</a></td> @ <td>\ if( g.perm.ModForum && moderation_pending(db_column_int(&q,5)) ){ @ <span class="modpending">\ @ Awaiting Moderator Approval</span><br> } if( nMsg<2 ){ @ no replies</td> }else{ char *zDuration = human_readable_age(db_column_double(&q,1)); @ %d(nMsg) posts spanning %h(zDuration)</td> fossil_free(zDuration); } @ </tr> fossil_free(zAge); } db_finalize(&q); } if( iCnt>0 ){ @ </table></div> }else{ @ <h1>No forum posts found</h1> } style_finish_page(); } |
Added src/forum.js.
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | (function(){ function absoluteY(obj){ var top = 0; if( obj.offsetParent ){ do{ top += obj.offsetTop; }while( obj = obj.offsetParent ); } return top; } var x = document.getElementsByClassName('forumSel'); if(x[0]){ var w = window.innerHeight; var h = x[0].scrollHeight; var y = absoluteY(x[0]); if( w>h ) y = y + (h-w)/2; if( y>0 ) window.scrollTo(0, y); } })(); |
Added src/fossil.bootstrap.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | "use strict"; (function () { /* CustomEvent polyfill, courtesy of Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent */ if(typeof window.CustomEvent === "function") return false; window.CustomEvent = function(event, params) { if(!params) params = {bubbles: false, cancelable: false, detail: null}; const evt = document.createEvent('CustomEvent'); evt.initCustomEvent( event, !!params.bubbles, !!params.cancelable, params.detail ); return evt; }; })(); (function(global){ /* Bootstrapping bits for the global.fossil object. Must be loaded after style.c:builtin_emit_script_fossil_bootstrap() has initialized that object. */ const F = global.fossil; /** Returns the current time in something approximating ISO-8601 format. */ const timestring = function f(){ if(!f.rx1){ f.rx1 = /\.\d+Z$/; } const d = new Date(); return d.toISOString().replace(f.rx1,'').split('T').join(' '); }; /** Returns the local time string of Date object d, defaulting to the current time. */ const localTimeString = function ff(d){ if(!ff.pad){ ff.pad = (x)=>(''+x).length>1 ? x : '0'+x; } d || (d = new Date()); return [ d.getFullYear(),'-',ff.pad(d.getMonth()+1/*sigh*/), '-',ff.pad(d.getDate()), ' ',ff.pad(d.getHours()),':',ff.pad(d.getMinutes()), ':',ff.pad(d.getSeconds()) ].join(''); }; /* ** By default fossil.message() sends its arguments console.debug(). If ** fossil.message.targetElement is set, it is assumed to be a DOM ** element, its innerText gets assigned to the concatenation of all ** arguments (with a space between each), and the CSS 'error' class is ** removed from the object. Pass it a falsy value to clear the target ** element. ** ** Returns this object. */ F.message = function f(msg){ const args = Array.prototype.slice.call(arguments,0); const tgt = f.targetElement; if(args.length) args.unshift( localTimeString()+':' //timestring(),'UTC:' ); if(tgt){ tgt.classList.remove('error'); tgt.innerText = args.join(' '); } else{ if(args.length){ args.unshift('Fossil status:'); console.debug.apply(console,args); } } return this; }; /* ** Set default message.targetElement to #fossil-status-bar, if found. */ F.message.targetElement = document.querySelector('#fossil-status-bar'); if(F.message.targetElement){ F.message.targetElement.addEventListener( 'dblclick', ()=>F.message(), false ); } /* ** By default fossil.error() sends its first argument to ** console.error(). If fossil.message.targetElement (yes, ** fossil.message) is set, it adds the 'error' CSS class to ** that element and sets its content as defined for message(). ** ** Returns this object. */ F.error = function f(msg){ const args = Array.prototype.slice.call(arguments,0); const tgt = F.message.targetElement; args.unshift(timestring(),'UTC:'); if(tgt){ tgt.classList.add('error'); tgt.innerText = args.join(' '); } else{ args.unshift('Fossil error:'); console.error.apply(console,args); } return this; }; /** For each property in the given object, its key/value are encoded for use as URL parameters and the combined string is returned. e.g. {a:1,b:2} encodes to "a=1&b=2". If the 2nd argument is an array, each encoded element is appended to that array and tgtArray is returned. The above object would be appended as ['a','=','1','&','b','=','2']. This form is used for building up parameter lists before join('')ing the array to create the result string. If passed a truthy 3rd argument, it does not really encode each component - it simply concatenates them together. */ F.encodeUrlArgs = function(obj,tgtArray,fakeEncode){ if(!obj) return ''; const a = (tgtArray instanceof Array) ? tgtArray : [], enc = fakeEncode ? (x)=>x : encodeURIComponent; let k, i = 0; for( k in obj ){ if(i++) a.push('&'); a.push(enc(k),'=',enc(obj[k])); } return a===tgtArray ? a : a.join(''); }; /** repoUrl( repoRelativePath [,urlParams] ) Creates a URL by prepending this.rootPath to the given path (which must be relative from the top of the site, without a leading slash). If urlParams is a string, it must be paramters encoded in the form "key=val&key2=val2..." WITHOUT a leading '?'. If it's an object, all of its properties get appended to the URL in that form. */ F.repoUrl = function(path,urlParams){ if(!urlParams) return this.rootPath+path; const url=[this.rootPath,path]; url.push('?'); if('string'===typeof urlParams) url.push(urlParams); else if(urlParams && 'object'===typeof urlParams){ this.encodeUrlArgs(urlParams, url); } return url.join(''); }; /** Returns true if v appears to be a plain object. */ F.isObject = function(v){ return v && (v instanceof Object) && ('[object Object]' === Object.prototype.toString.apply(v) ); }; /** For each object argument, this function combines their properties, using a last-one-wins policy, and returns a new object with the combined properties. If passed a single object, it effectively shallowly clones that object. */ F.mergeLastWins = function(){ var k, o, i; const n = arguments.length, rc={}; for(i = 0; i < n; ++i){ if(!F.isObject(o = arguments[i])) continue; for( k in o ){ if(o.hasOwnProperty(k)) rc[k] = o[k]; } } return rc; }; /** Expects to be passed as hash code as its first argument. It returns a "shortened" form of hash, with a length which depends on the 2nd argument: truthy = fossil.config.hashDigitsUrl, falsy = fossil.config.hashDigits, number == that many digits. The fossil.config values are derived from the 'hash-digits' repo-level config setting or the FOSSIL_HASH_DIGITS_URL/FOSSIL_HASH_DIGITS compile-time options. If its first arugment is a non-string, that value is returned as-is. */ F.hashDigits = function(hash,forUrl){ const n = ('number'===typeof forUrl) ? forUrl : F.config[forUrl ? 'hashDigitsUrl' : 'hashDigits']; return ('string'==typeof hash ? hash.substr( 0, n ) : hash); }; /** Convenience wrapper which adds an onload event listener to the window object. Returns this. */ F.onPageLoad = function(callback){ window.addEventListener('load', callback, false); return this; }; /** Convenience wrapper which adds a DOMContentLoadedevent listener to the window object. Returns this. */ F.onDOMContentLoaded = function(callback){ window.addEventListener('DOMContentLoaded', callback, false); return this; }; /** Assuming name is a repo-style filename, this function returns a shortened form of that name: .../LastDirectoryPart/FilenamePart If the name has 0-1 directory parts, it is returned as-is. Design note: in practice it is generally not helpful to elide the *last* directory part because embedded docs (in particular) often include x/y/index.md and x/z/index.md, both of which would be shortened to something like x/.../index.md. */ F.shortenFilename = function(name){ const a = name.split('/'); if(a.length<=2) return name; while(a.length>2) a.shift(); return '.../'+a.join('/'); }; /** Adds a listener for fossil-level custom events. Events are delivered to their callbacks as CustomEvent objects with a 'detail' property holding the event's app-level data. The exact events fired differ by page, and not all pages trigger events. Pedantic sidebar: the custom event's 'target' property is an unspecified DOM element. Clients must not rely on its value being anything specific or useful. Returns this object. */ F.page.addEventListener = function f(eventName, callback){ if(!f.proxy){ f.proxy = document.createElement('span'); } f.proxy.addEventListener(eventName, callback, false); return this; }; /** Internal. Dispatches a new CustomEvent to all listeners registered for the given eventName via fossil.page.addEventListener(), passing on a new CustomEvent with a 'detail' property equal to the 2nd argument. Returns this object. */ F.page.dispatchEvent = function(eventName, eventDetail){ if(this.addEventListener.proxy){ try{ this.addEventListener.proxy.dispatchEvent( new CustomEvent(eventName,{detail: eventDetail}) ); }catch(e){ console.error(eventName,"event listener threw:",e); } } return this; }; /** Sets the innerText of the page's TITLE tag to the given text and returns this object. */ F.page.setPageTitle = function(title){ const t = document.querySelector('title'); if(t) t.innerText = title; return this; }; /** Returns a function, that, as long as it continues to be invoked, will not be triggered. The function will be called after it stops being called for N milliseconds. If `immediate` is passed, call the callback immediately and hinder future invocations until at least the given time has passed. If passed only 1 argument, or passed a falsy 2nd argument, the default wait time set in this function's $defaultDelay property is used. Source: underscore.js, by way of https://davidwalsh.name/javascript-debounce-function */ F.debounce = function f(func, wait, immediate) { var timeout; if(!wait) wait = f.$defaultDelay; return function() { const context = this, args = Array.prototype.slice.call(arguments); const later = function() { timeout = undefined; if(!immediate) func.apply(context, args); }; const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if(callNow) func.apply(context, args); }; }; F.debounce.$defaultDelay = 500 /*arbitrary*/; })(window); |
Added src/fossil.confirmer.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | "use strict"; /************************************************************** Confirmer is a utility which provides an alternative to confirmation dialog boxes and "check this checkbox to confirm action" widgets. It acts by modifying a button to require two clicks within a certain time, with the second click acting as a confirmation of the first. If the second click does not come within a specified timeout then the action is not confirmed. Usage: fossil.confirmer(domElement, options); Usually: fossil.confirmer(element, { onconfirm: function(){ // this === the element. // Do whatever the element would normally do when // clicked. } }); Options: .initialText = initial text of the element. Defaults to the result of the element's .value (for INPUT tags) or innerHTML (for everything else). After the timeout/tick count expires, or if the user confirms the operation, the element's text is re-set to this value. .confirmText = text to show when in "confirm mode". Default=("Confirm: "+initialText), or something similar. .timeout = Number of milliseconds to wait for confirmation. Default=3000. Alternately, use a combination of .ticks and .ticktime. .onconfirm = function to call when clicked in confirm mode. Default = undefined. The function's "this" is the DOM element to which the countdown applies. .ontimeout = function to call when confirm is not issued. Default = undefined. The function's "this" is the DOM element to which the countdown applies. .onactivate = function to call when item is clicked, but only if the item is not currently in countdown mode. This is called (and must return) before the countdown starts. The function's "this" is the DOM element to which the countdown applies. This can be used, e.g., to change the element's text or CSS classes. .classInitial = optional CSS class string (default='') which is added to the element during its "initial" state (the state it is in when it is not waiting on a timeout). When the target is activated (waiting on a timeout) this class is removed. In the case of a timeout, this class is added *before* the .ontimeout handler is called. .classWaiting = optional CSS class string (default='') which is added to the target when it is waiting on a timeout. When the target leaves timeout-wait mode, this class is removed. When timeout-wait mode is entered, this class is added *before* the .onactivate handler is called. .ticktime = a number of ms to wait per tick (see the next item). Default = 1000. .ticks = a number of "ticks" to wait, as an alternative to .timeout. When this mode is active, the ontick callback will be triggered immediately before each tick, including the first one. If both .ticks and .timeout are set, only one will be used, but which one is unspecified. If passed a ticks value with a truncated integer value of 0 or less, it will throw an exception (e.g. that also applies if it's passed 0.5). .ontick = when using .ticks, this callback is passed the current tick number before each tick, and its "this" is the target element. On each subsequent call, the tick count will be reduced by 1, and it is passed 0 after the final tick expires or when the action has been confirmed, immediately before the onconfirm or ontimeout callback. The intention of the callback is to update the label of the target element. If .ticks is set but .ontick is not then a default implementation is used which updates the element with the .confirmText, prepending a countdown to it. .pinSize = if true AND confirmText is set, calculate the larger of the element's original and confirmed size and pin it to the larger of those sizes to avoid layout reflows when confirmation is running. The pinning is implemented by setting its minWidth and maxWidth style properties to the same value. This does not work if the element text is updated dynamically via ontick(). This ONLY works if the element is in the DOM and is not hidden (e.g. via display:none) at the time this routine is called, otherwise we cannot calculate its size. If the element needs to be hidden, hide it after initializing the confirmer. .debug = boolean. If truthy, it sends some debug output to the dev console to track what it's doing. Various notes: - To change the default option values, modify the fossil.confirmer.defaultOpts object. - Exceptions triggered via the callbacks are caught and emitted to the dev console if the debug option is enabled, but are otherwise ignored. - Due to the nature of multi-threaded code, it is potentially possible that confirmation and timeout actions BOTH happen if the user triggers the associated action at "just the right millisecond" before the timeout is triggered. TODO: - Add an invert option which activates if the timeout is reached and "times out" if the element is clicked again. e.g. a button which says "Saving..." and cancels the op if it's clicked again, else it saves after X time/ticks. - Internally we save/restore the initial text of non-INPUT elements using a relatively expensive bit of DOMParser hoop-jumping. We "should" instead move their child nodes aside (into an internal out-of-DOM element) and restore them as needed. Terse Change history: - 20200811 - Added pinSize option. - 20200507: - Add a tick-based countdown in order to more easily support updating the target element with the countdown. - 20200506: - Ported from jQuery to plain JS. - 20181112: - extended to support certain INPUT elements. - made default opts configurable. - 20070717: initial jQuery-based impl. */ (function(F/*the fossil object*/){ F.confirmer = function f(elem,opt){ const dbg = opt.debug ? function(){console.debug.apply(console,arguments)} : function(){}; dbg("confirmer opt =",opt); if(!f.Holder){ f.isInput = (e)=>/^(input|textarea)$/i.test(e.nodeName); f.Holder = function(target,opt){ const self = this; this.target = target; this.opt = opt; this.timerID = undefined; this.state = this.states.initial; const isInput = f.isInput(target); const updateText = function(msg){ if(isInput) target.value = msg; else{ /* Jump through some hoops to avoid assigning to innerHTML... */ const newNode = new DOMParser().parseFromString(msg, 'text/html'); let childs = newNode.documentElement.querySelector('body'); childs = childs ? Array.prototype.slice.call(childs.childNodes, 0) : []; target.innerText = ''; childs.forEach((e)=>target.appendChild(e)); } } const formatCountdown = (txt, number) => txt + " ["+number+"]"; if(opt.pinSize && opt.confirmText){ /* Try to pin the element's width the the greater of its current width or its waiting-on-confirmation width to avoid layout reflow when it's activated. */ const digits = (''+(opt.timeout/1000 || opt.ticks)).length; const lblLong = formatCountdown(opt.confirmText, "00000000".substr(0,digits+1)); const w1 = parseInt(target.getBoundingClientRect().width); updateText(lblLong); const w2 = parseInt(target.getBoundingClientRect().width); if(w1 || w2){ /* If target is not in visible part of the DOM, those values may be 0. */ target.style.minWidth = target.style.maxWidth = (w1>w2 ? w1 : w2)+"px"; } } updateText(this.opt.initialText); if(this.opt.ticks && !this.opt.ontick){ this.opt.ontick = function(tick){ updateText(formatCountdown(self.opt.confirmText,tick)); }; } this.setClasses(false); this.doTimeout = function() { if(this.timerID){ clearTimeout( this.timerID ); delete this.timerID; } if( this.state != this.states.waiting ) { // it was already confirmed return; } this.setClasses( false ); this.state = this.states.initial; dbg("Timeout triggered."); if( this.opt.ontick ){ try{this.opt.ontick.call(this.target, 0)} catch(e){dbg("ontick EXCEPTION:",e)} } if( this.opt.ontimeout ) { try{this.opt.ontimeout.call(this.target)} catch(e){dbg("ontimeout EXCEPTION:",e)} } updateText(this.opt.initialText); }; target.addEventListener( 'click', function(){ switch( self.state ) { case( self.states.waiting ): /* Cancel the wait on confirmation */ if( undefined !== self.timerID ){ clearTimeout( self.timerID ); delete self.timerID; } self.state = self.states.initial; self.setClasses( false ); dbg("Confirmed"); if( self.opt.ontick ){ try{self.opt.ontick.call(self.target,0)} catch(e){dbg("ontick EXCEPTION:",e)} } if( self.opt.onconfirm ){ try{self.opt.onconfirm.call(self.target)} catch(e){dbg("onconfirm EXCEPTION:",e)} } updateText(self.opt.initialText); break; case( self.states.initial ): /* Enter the waiting-on-confirmation state... */ if(self.opt.ticks) self.opt.currentTick = self.opt.ticks; self.setClasses( true ); self.state = self.states.waiting; updateText( self.opt.confirmText ); if( self.opt.onactivate ) self.opt.onactivate.call( self.target ); if( self.opt.ontick ) self.opt.ontick.call(self.target, self.opt.currentTick); if(self.opt.timeout){ dbg("Waiting "+self.opt.timeout+"ms on confirmation..."); self.timerID = setTimeout(()=>self.doTimeout(),self.opt.timeout ); }else if(self.opt.ticks){ dbg("Waiting on confirmation for "+self.opt.ticks +" ticks of "+self.opt.ticktime+"ms each..."); self.timerID = setInterval(function(){ if(0===--self.opt.currentTick) self.doTimeout(); else{ try{self.opt.ontick.call(self.target, self.opt.currentTick)} catch(e){dbg("ontick EXCEPTION:",e)} } },self.opt.ticktime); } break; default: // can't happen. break; } }, false ); }; f.Holder.prototype = { states:{initial: 0, waiting: 1}, setClasses: function(activated) { if(activated) { if( this.opt.classWaiting ) { this.target.classList.add( this.opt.classWaiting ); } if( this.opt.classInitial ) { this.target.classList.remove( this.opt.classInitial ); } }else{ if( this.opt.classInitial ) { this.target.classList.add( this.opt.classInitial ); } if( this.opt.classWaiting ) { this.target.classList.remove( this.opt.classWaiting ); } } } }; }/*static init*/ opt = F.mergeLastWins(f.defaultOpts,{ initialText: ( f.isInput(elem) ? elem.value : elem.innerHTML ) || "PLEASE SET .initialText" },opt); if(!opt.confirmText){ opt.confirmText = "Confirm: "+opt.initialText; } if(opt.ticks){ delete opt.timeout; opt.ticks = 0 | opt.ticks /* ensure it's an integer */; if(opt.ticks<=0){ throw new Error("ticks must be >0"); } if(opt.ticktime <= 0) opt.ticktime = 1000; }else{ delete opt.ontick; delete opt.ticks; } new f.Holder(elem,opt); return this; }; /** The default options for initConfirmer(). Tweak them to set the defaults. A couple of them (initialText and confirmText) are dynamically-generated, and can't reasonably be set in the defaults. Some, like ticks, cannot be set here because that would end up indirectly replacing non-tick timeouts with ticks. */ F.confirmer.defaultOpts = { timeout:undefined, ticks: 3, ticktime: 998/*not *quite* 1000*/, onconfirm: undefined, ontimeout: undefined, onactivate: undefined, classInitial: '', classWaiting: '', debug: false }; })(window.fossil); |
Added src/fossil.copybutton.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (function(F/*fossil object*/){ /** A basic API for creating and managing a copy-to-clipboard button. Requires: fossil.bootstrap, fossil.dom */ const D = F.dom; /** Initializes element e as a copy button using the given options object. The first argument may be a DOM element or a string (CSS selector suitable for use with document.querySelector()). Options: .copyFromElement: DOM element .copyFromId: DOM element ID .extractText: optional callback which is triggered when the copy button is clicked. It must return the text to copy to the clipboard. The default is to extract it from the copy-from element, using its [value] member, if it has one, else its [innerText]. A client-provided callback may use any data source it likes, so long as it's synchronous. If this function returns a falsy value then the clipboard is not modified. This function is called with the fully expanded/resolved options object as its "this" (that's a different instance than the one passed to this function!). At least one of copyFromElement, copyFromId, or extractText must be provided, but if copyFromId is not set and e.dataset.copyFromId is then that value is used in its place. extractText() trumps the other two options. .cssClass: optional CSS class, or list of classes, to apply to e. .style: optional object of properties to copy directly into e.style. .oncopy: an optional callback function which is added as an event listener for the 'text-copied' event (see below). There is functionally no difference from setting this option or adding a 'text-copied' event listener to the element, and this option is considered to be a convenience form of that. For the sake of framework-level consistency, the default value is a callback which passes the copy button to fossil.dom.flashOnce(). Note that this function's own defaultOptions object holds default values for some options. Any changes made to that object affect any future calls to this function. Be aware that clipboard functionality might or might not be available in any given environment. If this button appears to have no effect, that may be because it is not enabled/available in the current platform. The copy button emits custom event 'text-copied' after it has successfully copied text to the clipboard. The event's "detail" member is an object with a "text" property holding the copied text. Other properties may be added in the future. The event is not fired if copying to the clipboard fails (e.g. is not available in the current environment). As a special case, the copy button's click handler is suppressed (becomes a no-op) for as long as the element has the CSS class "disabled". This allows elements which cannot be disabled via HTML attributes, e.g. a SPAN, to act as a copy button while still providing a way to disable them. Returns the copy-initialized element. Example: const button = fossil.copyButton('#my-copy-button', { copyFromId: 'some-other-element-id' }); button.addEventListener('text-copied',function(ev){ fossil.dom.flashOnce(ev.target); console.debug("Copied text:",ev.detail.text); }); */ F.copyButton = function f(e, opt){ if('string'===typeof e){ e = document.querySelector(e); } opt = F.mergeLastWins(f.defaultOptions, opt); if(opt.cssClass){ D.addClass(e, opt.cssClass); } var srcId, srcElem; if(opt.copyFromElement){ srcElem = opt.copyFromElement; }else if((srcId = opt.copyFromId || e.dataset.copyFromId)){ srcElem = document.querySelector('#'+srcId); } const extract = opt.extractText || ( undefined===srcElem.value ? ()=>srcElem.innerText : ()=>srcElem.value ); D.copyStyle(e, opt.style); e.addEventListener( 'click', function(ev){ ev.preventDefault(); ev.stopPropagation(); if(e.classList.contains('disabled')) return; const txt = extract.call(opt); if(txt && D.copyTextToClipboard(txt)){ e.dispatchEvent(new CustomEvent('text-copied',{ detail: {text: txt} })); } }, false ); if('function' === typeof opt.oncopy){ e.addEventListener('text-copied', opt.oncopy, false); } return e; }; F.copyButton.defaultOptions = { cssClass: 'copy-button', oncopy: D.flashOnce.eventHandler, style: {/*properties copied as-is into element.style*/} }; })(window.fossil); |
Added src/fossil.diff.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /** diff-related JS APIs for fossil. */ "use strict"; window.fossil.onPageLoad(function(){ /** Adds toggle checkboxes to each file entry in the diff views for /info and similar pages. */ const D = window.fossil.dom; const addToggle = function(diffElem){ const sib = diffElem.previousElementSibling, btn = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; if(!sib) return; D.append(sib,btn); btn.addEventListener('click', function(){ diffElem.classList.toggle('hidden'); }, false); }; document.querySelectorAll('table.diff').forEach(addToggle); }); window.fossil.onPageLoad(function(){ const F = window.fossil, D = F.dom; const Diff = F.diff = { e:{/*certain cached DOM elements*/}, config: { chunkLoadLines: ( F.config.diffContextLines * 3 /*per /chat discussion*/ ) || 20, chunkFetch: { /* Default callack handlers for Diff.fetchArtifactChunk(), unless overridden by options passeed to that function. */ beforesend: function(){}, aftersend: function(){}, onerror: function(e){ console.error("XHR error: ",e); } } } }; /** Uses the /jchunk AJAX route to fetch specific lines of a given artifact. The argument must be an Object suitable for passing as the second argument to fossil.fetch(). Its urlParams property must be an object with these properties: { name: full hash of the target file, from: first 1-based line number of the file to fetch (inclusive), to: last 1-based line number of the file to fetch (inclusive) } The fetchOpt object is NOT cloned for use by the call: it is used as-is and may be modified by this call. Thus callers "really should" pass a temporary object, not a long-lived one. If fetchOpt does not define any of the (beforesend, aftersend, onerror) callbacks, the defaults from fossil.diff.config.chunkFetch are used, so any given client page may override those to provide page-level default handling. Note that onload callback is ostensibly optional but this function is not of much use without an onload handler. Conversely, the default onerror handler is often customized on a per-page basis to send the error output somewhere where the user can see it. The response, on success, will be an array of strings, each entry being one line from the requested artifact. If the 'to' line is greater than the length of the file, the array will be shorter than (to-from) lines. The /jchunk route reports errors via JSON objects with an "error" string property describing the problem. This is an async operation. Returns the fossil object. */ Diff.fetchArtifactChunk = function(fetchOpt){ if(!fetchOpt.beforesend) fetchOpt.beforesend = Diff.config.chunkFetch.beforesend; if(!fetchOpt.aftersend) fetchOpt.aftersend = Diff.config.chunkFetch.aftersend; if(!fetchOpt.onerror) fetchOpt.onerror = Diff.config.chunkFetch.onerror; fetchOpt.responseType = 'json'; return F.fetch('jchunk', fetchOpt); }; /** Extracts either the starting or ending line number from a line-numer column in the given tr. isSplit must be true if tr represents a split diff, else false. Expects its tr to be valid: GIGO applies. Returns the starting line number if getStart, else the ending line number. Returns the line number from the LHS file if getLHS is true, else the RHS. */ const extractLineNo = function f(getLHS, getStart, tr, isSplit){ if(!f.rx){ f.rx = { start: /^\s*(\d+)/, end: /(\d+)\n?$/ } } const td = tr.querySelector('td:nth-child('+( /* TD element with the line numbers */ getLHS ? 1 : (isSplit ? 4 : 2) )+')'); const m = f.rx[getStart ? 'start' : 'end'].exec(td.innerText); return m ? +m[1] : undefined/*"shouldn't happen"*/; }; /** Installs chunk-loading controls into TR.diffskip element tr. Each instance corresponds to a single TR.diffskip element. The goal is to base these controls roughly on github's, a good example of which, for use as a model, is: https://github.com/msteveb/autosetup/commit/235925e914a52a542 */ const ChunkLoadControls = function(tr){ this.$fetchQueue = []; this.e = {/*DOM elements*/ tr: tr, table: tr.parentElement/*TBODY*/.parentElement }; this.isSplit = this.e.table.classList.contains('splitdiff')/*else udiff*/; this.fileHash = this.e.table.dataset.lefthash; tr.$chunker = this /* keep GC from reaping this */; this.pos = { /* These line numbers correspond to the LHS file. Because the contents are common to both sides, we have the same number for the RHS, but need to extract those line numbers from the neighboring TR blocks */ startLhs: +tr.dataset.startln, endLhs: +tr.dataset.endln }; D.clearElement(tr); this.e.td = D.addClass( /* Holder for our UI controls */ D.attr(D.td(tr), 'colspan', this.isSplit ? 5 : 4), 'chunkctrl' ); this.e.msgWidget = D.addClass(D.span(), 'hidden'); this.e.btnWrapper = D.div(); D.append(this.e.td, this.e.btnWrapper); /** Depending on various factors, we need one or more of: - A single button to load the initial chunk incrementally - A single button to load all lines then remove this control - Two buttons: one to load upwards, one to load downwards - A single button to load the final chunk incrementally */ if(tr.nextElementSibling){ this.pos.next = { startLhs: extractLineNo(true, true, tr.nextElementSibling, this.isSplit), startRhs: extractLineNo(false, true, tr.nextElementSibling, this.isSplit) }; } if(tr.previousElementSibling){ this.pos.prev = { endLhs: extractLineNo(true, false, tr.previousElementSibling, this.isSplit), endRhs: extractLineNo(false, false, tr.previousElementSibling, this.isSplit) }; } let btnUp = false, btnDown = false; /** this.pos.next refers to the line numbers in the next TR's chunk. this.pos.prev refers to the line numbers in the previous TR's chunk. this.pos corresponds to the line numbers of the gap. */ if(this.pos.prev && this.pos.next && ((this.pos.endLhs - this.pos.startLhs) <= Diff.config.chunkLoadLines)){ /* Place a single button to load the whole block, rather than separate up/down buttons. */ btnDown = false; btnUp = this.createButton(this.FetchType.FillGap); }else{ /* Figure out which chunk-load buttons to add... */ if(this.pos.prev){ btnDown = this.createButton(this.FetchType.PrevDown); } if(this.pos.next){ btnUp = this.createButton(this.FetchType.NextUp); } } //this.e.btnUp = btnUp; //this.e.btnDown = btnDown; if(btnUp) D.append(this.e.btnWrapper, btnUp); if(btnDown) D.append(this.e.btnWrapper, btnDown); D.append(this.e.btnWrapper, this.e.msgWidget); /* For debugging only... */ this.e.posState = D.span(); D.append(this.e.btnWrapper, this.e.posState); this.updatePosDebug(); }; ChunkLoadControls.prototype = { /** An "enum" of values describing the types of context fetches/operations performed by this type. The values in this object must not be changed without modifying all logic which relies on their relative order. */ FetchType:{ /** Append context to the bottom of the previous diff chunk. */ PrevDown: 1, /** Fill a complete gap between the previous/next diff chunks or at the start of the next chunk or end of the previous chunks. */ FillGap: 0, /** Prepend context to the start of the next diff chunk. */ NextUp: -1, /** Process the next queued action. */ ProcessQueue: 0x7fffffff }, /** Creates and returns a button element for fetching a chunk in the given fetchType (as documented for fetchChunk()). */ createButton: function(fetchType){ let b; switch(fetchType){ case this.FetchType.PrevDown: b = D.append( D.addClass(D.span(), 'down'), D.span(/*glyph holder*/) ); break; case this.FetchType.FillGap: b = D.append( D.addClass(D.span(), 'up', 'down'), D.span(/*glyph holder*/) ); break; case this.FetchType.NextUp: b = D.append( D.addClass(D.span(), 'up'), D.span(/*glyph holder*/) ); break; default: throw new Error("Internal API misuse: unexpected fetchType value "+fetchType); } D.addClass(b, 'jcbutton'); b.addEventListener('click', ()=>this.fetchChunk(fetchType),false); return b; }, updatePosDebug: function(){ if(this.e.posState){ D.clearElement(this.e.posState); //D.append(D.clearElement(this.e.posState), JSON.stringify(this.pos)); } return this; }, /* Attempt to clean up resources and remove some circular references to that GC can do the right thing. */ destroy: function(){ delete this.$fetchQueue; D.remove(this.e.tr); delete this.e.tr.$chunker; delete this.e.tr; delete this.e; delete this.pos; }, /** If the gap between this.pos.endLhs/startLhs is less than or equal to Diff.config.chunkLoadLines then this function replaces any up/down buttons with a gap-filler button, else it's a no-op. Returns this object. As a special case, do not apply this at the start or bottom of the diff, only between two diff chunks. */ maybeReplaceButtons: function(){ if(this.pos.next && this.pos.prev && (this.pos.endLhs - this.pos.startLhs <= Diff.config.chunkLoadLines)){ D.clearElement(this.e.btnWrapper); D.append(this.e.btnWrapper, this.createButton(this.FetchType.FillGap)); if( this.$fetchQueue && this.$fetchQueue.length>1 ){ this.$fetchQueue[1] = this.FetchType.FillGap; this.$fetchQueue.length = 2; } } return this; }, /** Callack for /jchunk responses. */ injectResponse: function f(fetchType/*as for fetchChunk()*/, urlParam/*from fetchChunk()*/, lines/*response lines*/){ if(!lines.length){ /* No more data to load */ this.destroy(); return this; } this.msg(false); //console.debug("Loaded line range ", //urlParam.from,"-",urlParam.to, "fetchType ",fetchType); const lineno = [], trPrev = this.e.tr.previousElementSibling, trNext = this.e.tr.nextElementSibling, doAppend = ( !!trPrev && fetchType>=this.FetchType.FillGap ) /* true to append to previous TR, else prepend to NEXT TR */; const tr = doAppend ? trPrev : trNext; const joinTr = ( this.FetchType.FillGap===fetchType && trPrev && trNext ) ? trNext : false /* Truthy if we want to combine trPrev, the new content, and trNext into trPrev and then remove trNext. */; let i, td; if(!f.convertLines){ /* Reminder: string.replaceAll() is a relatively new JS feature, not available in some still-widely-used browser versions. */ f.rx = [[/&/g, '&'], [/</g, '<']]; f.convertLines = function(li){ var s = li.join('\n'); f.rx.forEach((a)=>s=s.replace(a[0],a[1])); return s + '\n'; }; } if(1){ // LHS line numbers... const selector = '.difflnl > pre'; td = tr.querySelector(selector); const lnTo = Math.min(urlParam.to, urlParam.from + lines.length - 1/*b/c request range is inclusive*/); for( i = urlParam.from; i <= lnTo; ++i ){ lineno.push(i); } const lineNoTxt = lineno.join('\n')+'\n'; const content = [td.innerHTML]; if(doAppend) content.push(lineNoTxt); else content.unshift(lineNoTxt); if(joinTr){ content.push(trNext.querySelector(selector).innerHTML); } td.innerHTML = content.join(''); } if(1){// code block(s)... const selector = '.difftxt > pre'; td = tr.querySelectorAll(selector); const code = f.convertLines(lines); let joinNdx = 0/*selector[X] index to join together*/; td.forEach(function(e){ const content = [e.innerHTML]; if(doAppend) content.push(code); else content.unshift(code); if(joinTr){ content.push(trNext.querySelectorAll(selector)[joinNdx++].innerHTML) } e.innerHTML = content.join(''); }); } if(1){// Add blank lines in (.diffsep>pre) const selector = '.diffsep > pre'; td = tr.querySelector(selector); for(i = 0; i < lineno.length; ++i) lineno[i] = ''; const blanks = lineno.join('\n')+'\n'; const content = [td.innerHTML]; if(doAppend) content.push(blanks); else content.unshift(blanks); if(joinTr){ content.push(trNext.querySelector(selector).innerHTML); } td.innerHTML = content.join(''); } if(this.FetchType.FillGap===fetchType){ /* Closing the whole gap between two chunks or a whole gap at the start or end of a diff. */ // RHS line numbers... let startLnR = this.pos.prev ? this.pos.prev.endRhs+1 /* Closing the whole gap between two chunks or end-of-file gap. */ : this.pos.next.startRhs - lines.length /* start-of-file gap */; lineno.length = lines.length; for( i = startLnR; i < startLnR + lines.length; ++i ){ lineno[i-startLnR] = i; } const selector = '.difflnr > pre'; td = tr.querySelector(selector); const lineNoTxt = lineno.join('\n')+'\n'; lineno.length = 0; const content = [td.innerHTML]; if(doAppend) content.push(lineNoTxt); else content.unshift(lineNoTxt); if(joinTr){ content.push(trNext.querySelector(selector).innerHTML); } td.innerHTML = content.join(''); if(joinTr) D.remove(joinTr); Diff.checkTableWidth(true); this.destroy(); return this; }else if(this.FetchType.PrevDown===fetchType){ /* Append context to previous TR. */ // RHS line numbers... let startLnR = this.pos.prev.endRhs+1; lineno.length = lines.length; for( i = startLnR; i < startLnR + lines.length; ++i ){ lineno[i-startLnR] = i; } this.pos.startLhs += lines.length; this.pos.prev.endRhs += lines.length; this.pos.prev.endLhs += lines.length; const selector = '.difflnr > pre'; td = tr.querySelector(selector); const lineNoTxt = lineno.join('\n')+'\n'; lineno.length = 0; const content = [td.innerHTML]; if(doAppend) content.push(lineNoTxt); else content.unshift(lineNoTxt); td.innerHTML = content.join(''); if(lines.length < (urlParam.to - urlParam.from)){ /* No more data. */ this.destroy(); }else{ this.maybeReplaceButtons(); this.updatePosDebug(); } Diff.checkTableWidth(true); return this; }else if(this.FetchType.NextUp===fetchType){ /* Prepend content to next TR. */ // RHS line numbers... if(doAppend){ throw new Error("Internal precondition violation: doAppend is true."); } let startLnR = this.pos.next.startRhs - lines.length; lineno.length = lines.length; for( i = startLnR; i < startLnR + lines.length; ++i ){ lineno[i-startLnR] = i; } this.pos.endLhs -= lines.length; this.pos.next.startRhs -= lines.length; this.pos.next.startLhs -= lines.length; const selector = '.difflnr > pre'; td = tr.querySelector(selector); const lineNoTxt = lineno.join('\n')+'\n'; lineno.length = 0; td.innerHTML = lineNoTxt + td.innerHTML; if(this.pos.endLhs<1 || lines.length < (urlParam.to - urlParam.from)){ /* No more data. */ this.destroy(); }else{ this.maybeReplaceButtons(); this.updatePosDebug(); } Diff.checkTableWidth(true); return this; }else{ throw new Error("Unexpected 'fetchType' value."); } }, /** Sets this widget's message to the given text. If the message represents an error, the first argument must be truthy, else it must be falsy. Returns this object. */ msg: function(isError,txt){ if(txt){ if(isError) D.addClass(this.e.msgWidget, 'error'); else D.removeClass(this.e.msgWidget, 'error'); D.append( D.removeClass(D.clearElement(this.e.msgWidget), 'hidden'), txt); }else{ D.addClass(D.clearElement(this.e.msgWidget), 'hidden'); } return this; }, /** Fetches and inserts a line chunk. fetchType is: this.FetchType.NextUp = upwards from next chunk (this.pos.next) this.FetchType.FillGap = the whole gap between this.pos.prev and this.pos.next, or the whole gap before/after the initial/final chunk in the diff. this.FetchType.PrevDown = downwards from the previous chunk (this.pos.prev) Those values are set at the time this object is initialized but one instance of this class may have 2 buttons, one each for fetchTypes NextUp and PrevDown. This is an async operation. While it is in transit, any calls to this function will have no effect except (possibly) to emit a warning. Returns this object. */ fetchChunk: function(fetchType){ if( !this.$fetchQueue ) return this; // HACKHACK: are we destroyed? if( fetchType==this.FetchType.ProcessQueue ){ this.$fetchQueue.shift(); if( this.$fetchQueue.length==0 ) return this; //console.log('fetchChunk: processing queue ...'); } else{ this.$fetchQueue.push(fetchType); if( this.$fetchQueue.length!=1 ) return this; //console.log('fetchChunk: processing user input ...'); } fetchType = this.$fetchQueue[0]; if( fetchType==this.FetchType.ProcessQueue ){ /* Unexpected! Clear queue so recovery (manual restart) is possible. */ this.$fetchQueue.length = 0; return this; } /* Forewarning, this is a bit confusing: when fetching the previous lines, we're doing so on behalf of the *next* diff chunk (this.pos.next), and vice versa. */ if(fetchType===this.FetchType.NextUp && !this.pos.next || fetchType===this.FetchType.PrevDown && !this.pos.prev){ console.error("Attempt to fetch diff lines but don't have any."); return this; } this.msg(false,"Fetching diff chunk..."); const self = this; const fOpt = { urlParams:{ name: this.fileHash, from: 0, to: 0 }, aftersend: ()=>this.msg(false), onload: function(list){ self.injectResponse(fetchType,up,list); if( !self.$fetchQueue || self.$fetchQueue.length==0 ) return; /* Keep queue length > 0, or clicks stalled during (unusually lengthy) injectResponse() may sneak in as soon as setTimeout() allows, find an empty queue, and therefore start over with queue processing. */ self.$fetchQueue[0] = self.FetchType.ProcessQueue; setTimeout(self.fetchChunk.bind(self,self.FetchType.ProcessQueue)); } }; const up = fOpt.urlParams; if(fetchType===this.FetchType.FillGap){ /* Easiest case: filling a whole gap. */ up.from = this.pos.startLhs; up.to = this.pos.endLhs; }else if(this.FetchType.PrevDown===fetchType){ /* Append to previous TR. */ if(!this.pos.prev){ console.error("Attempt to fetch next diff lines but don't have any."); return this; } up.from = this.pos.prev.endLhs + 1; up.to = up.from + Diff.config.chunkLoadLines - 1/*b/c request range is inclusive*/; if( this.pos.next && this.pos.next.startLhs <= up.to ){ up.to = this.pos.next.startLhs - 1; fetchType = this.FetchType.FillGap; } }else{ /* Prepend to next TR */ if(!this.pos.next){ console.error("Attempt to fetch previous diff lines but don't have any."); return this; } up.to = this.pos.next.startLhs - 1; up.from = Math.max(1, up.to - Diff.config.chunkLoadLines + 1); if( this.pos.prev && this.pos.prev.endLhs >= up.from ){ up.from = this.pos.prev.endLhs + 1; fetchType = this.FetchType.FillGap; } } //console.debug("fetchChunk(",fetchType,")",up); fOpt.onerror = function(err){ if(self.e/*guard against a late-stage onerror() call*/){ self.msg(true,err.message); self.$fetchQueue.length = 0; }else{ Diff.config.chunkFetch.onerror.call(this,err); } }; Diff.fetchArtifactChunk(fOpt); return this; } }; /** Adds context-loading buttons to one or more tables. The argument may be a forEach-capable list of diff table elements, a query selector string matching 0 or more diff tables, or falsy, in which case all relevant diff tables are set up. It tags each table it processes to that it will not be processed multiple times by subsequent calls to this function. Note that this only works for diffs which have been marked up with certain state, namely table.dataset.lefthash and TR entries which hold state related to browsing context. */ Diff.setupDiffContextLoad = function(tables){ if('string'===typeof tables){ tables = document.querySelectorAll(tables); }else if(!tables){ tables = document.querySelectorAll('table.diff[data-lefthash]:not(.diffskipped)'); } /* Potential performance-related TODO: instead of installing all of these at once, install them as the corresponding TR is scrolled into view. */ tables.forEach(function(table){ if(table.classList.contains('diffskipped') || !table.dataset.lefthash) return; D.addClass(table, 'diffskipped'/*avoid processing these more than once */); table.querySelectorAll('tr.diffskip[data-startln]').forEach(function(tr){ new ChunkLoadControls(D.addClass(tr, 'jchunk')); }); }); return F; }; Diff.setupDiffContextLoad(); }); /* Refinements to the display of unified and side-by-side diffs. ** ** In all cases, the table columns tagged with "difftxt" are expanded, ** where possible, to fill the width of the screen. ** ** For a side-by-side diff, if either column is two wide to fit on the ** display, scrollbars are added. The scrollbars are linked, so that ** both sides scroll together. Left and right arrows also scroll. */ window.fossil.onPageLoad(function(){ const SCROLL_LEN = 25; const F = window.fossil, D = F.dom, Diff = F.diff; var lastWidth; Diff.checkTableWidth = function f(force){ if(undefined === f.contentNode){ f.contentNode = document.querySelector('div.content'); } force = true; const parentCS = window.getComputedStyle(f.contentNode); const parentWidth = ( //document.body.clientWidth; //parentCS.width; f.contentNode.clientWidth - parseFloat(parentCS.marginLeft) - parseFloat(parentCS.marginRight) ); if( !force && parentWidth===lastWidth ) return this; lastWidth = parentWidth; let w = lastWidth*0.5 - 100; //console.debug( "w = ",w,", lastWidth =",lastWidth," body = ",document.body.clientWidth); if(force || !f.colsL){ f.colsL = document.querySelectorAll('td.difftxtl pre'); } f.colsL.forEach(function(e){ e.style.width = w + "px"; e.style.maxWidth = w + "px"; }); if(force || !f.colsR){ f.colsR = document.querySelectorAll('td.difftxtr pre'); } f.colsR.forEach(function(e){ e.style.width = w + "px"; e.style.maxWidth = w + "px"; }); if(force || !f.colsU){ f.colsU = document.querySelectorAll('td.difftxtu pre'); } f.colsU.forEach(function(e){ w = lastWidth - 3; // Outer border var k = e.parentElement/*TD*/; while(k = k.previousElementSibling/*TD*/) w -= k.scrollWidth; e.style.width = w + "px"; e.style.maxWidth = w + "px"; }); if(0){ // seems to be unnecessary if(!f.allDiffs){ f.allDiffs = document.querySelectorAll('table.diff'); } w = lastWidth; f.allDiffs.forEach(function f(e){ if(0 && !f.$){ f.$ = e.getClientRects()[0]; console.debug("diff table w =",w," f.$x",f.$); w - 2*f.$.x /* left margin (assume right==left, for simplicity) */; } e.style.maxWidth = w + "px"; }); //console.debug("checkTableWidth(",force,") lastWidth =",lastWidth); } return this; }; const scrollLeft = function(event){ //console.debug("scrollLeft",this,event); const table = this.parentElement/*TD*/.parentElement/*TR*/. parentElement/*TBODY*/.parentElement/*TABLE*/; table.$txtPres.forEach((e)=>(e===this) ? 1 : (e.scrollLeft = this.scrollLeft)); return false; }; Diff.initTableDiff = function f(diff, unifiedDiffs){ if(!diff){ let i, diffs; diffs = document.querySelectorAll('table.splitdiff'); for(i=0; i<diffs.length; ++i){ f.call(this, diffs[i], false); } diffs = document.querySelectorAll('table.udiff'); for(i=0; i<diffs.length; ++i){ f.call(this, diffs[i], true); } return this; } diff.$txtCols = diff.querySelectorAll('td.difftxt'); diff.$txtPres = diff.querySelectorAll('td.difftxt pre'); var width = 0; diff.$txtPres.forEach(function(e){ if(width < e.scrollWidth) width = e.scrollWidth; }); //console.debug("diff.$txtPres =",diff.$txtPres); diff.$txtCols.forEach((e)=>e.style.width = width + 'px'); diff.$txtPres.forEach(function(e){ e.style.maxWidth = width + 'px'; e.style.width = width + 'px'; if(!unifiedDiffs && !e.classList.contains('scroller')){ D.addClass(e, 'scroller'); e.addEventListener('scroll', scrollLeft, false); } }); if(!unifiedDiffs){ diff.tabIndex = 0; if(!diff.classList.contains('scroller')){ D.addClass(diff, 'scroller'); diff.addEventListener('keydown', function(e){ e = e || event; const len = {37: -SCROLL_LEN, 39: SCROLL_LEN}[e.keyCode]; if( !len ) return; this.$txtPres[0].scrollLeft += len; /* ^^^ bug: if there is a 2nd column and it has a scrollbar but txtPres[0] does not, no scrolling happens here. We need to find the widest of txtPres and scroll that one. Example: Checkin a7fbefee38a1c522 file diff.c */ return false; }, false); } } return this; } window.fossil.page.tweakSbsDiffs = function(){ document.querySelectorAll('table.splitdiff').forEach((e)=>Diff.initTableDiff(e)); Diff.checkTableWidth(); }; Diff.initTableDiff().checkTableWidth(); window.addEventListener('resize', F.debounce(()=>Diff.checkTableWidth())); }, false); |
Added src/fossil.dom.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | "use strict"; (function(F/*fossil object*/){ /** A collection of HTML DOM utilities to simplify, a bit, using the DOM API. It is focused on manipulation of the DOM, but one of its core mantras is "No innerHTML." Using innerHTML in this code, in particular assigning to it, is absolutely verboten. */ const argsToArray = (a)=>Array.prototype.slice.call(a,0); const isArray = (v)=>v instanceof Array; const dom = { create: function(elemType){ return document.createElement(elemType); }, createElemFactory: function(eType){ return function(){ return document.createElement(eType); }; }, remove: function(e){ if(e.forEach){ e.forEach( (x)=>x.parentNode.removeChild(x) ); }else{ e.parentNode.removeChild(e); } return e; }, /** Removes all child DOM elements from the given element and returns that element. If e has a forEach method (is an array or DOM element collection), this function instead clears each element in the collection. May be passed any number of arguments, each of which must be a DOM element or a container of DOM elements with a forEach() method. Returns its first argument. */ clearElement: function f(e){ if(!f.each){ f.each = function(e){ if(e.forEach){ e.forEach((x)=>f(x)); return e; } while(e.firstChild) e.removeChild(e.firstChild); }; } argsToArray(arguments).forEach(f.each); return arguments[0]; }, }/* dom object */; /** Returns the result of splitting the given str on a run of spaces of (\s*,\s*). */ dom.splitClassList = function f(str){ if(!f.rx){ f.rx = /(\s+|\s*,\s*)/; } return str ? str.split(f.rx) : [str]; }; dom.div = dom.createElemFactory('div'); dom.p = dom.createElemFactory('p'); dom.code = dom.createElemFactory('code'); dom.pre = dom.createElemFactory('pre'); dom.header = dom.createElemFactory('header'); dom.footer = dom.createElemFactory('footer'); dom.section = dom.createElemFactory('section'); dom.span = dom.createElemFactory('span'); dom.strong = dom.createElemFactory('strong'); dom.em = dom.createElemFactory('em'); dom.ins = dom.createElemFactory('ins'); dom.del = dom.createElemFactory('del'); /** Returns a LABEL element. If passed an argument, it must be an id or an HTMLElement with an id, and that id is set as the 'for' attribute of the label. If passed 2 arguments, the 2nd is text or a DOM element to append to the label. */ dom.label = function(forElem, text){ const rc = document.createElement('label'); if(forElem){ if(forElem instanceof HTMLElement){ forElem = this.attr(forElem, 'id'); } dom.attr(rc, 'for', forElem); } if(text) this.append(rc, text); return rc; }; /** Returns an IMG element with an optional src attribute value. */ dom.img = function(src){ const e = this.create('img'); if(src) e.setAttribute('src',src); return e; }; /** Creates and returns a new anchor element with the given optional href and label. If label===true then href is used as the label. */ dom.a = function(href,label){ const e = this.create('a'); if(href) e.setAttribute('href',href); if(label) e.appendChild(dom.text(true===label ? href : label)); return e; }; dom.hr = dom.createElemFactory('hr'); dom.br = dom.createElemFactory('br'); /** Returns a new TEXT node which contains the text of all of the arguments appended together. */ dom.text = function(/*...*/){ return document.createTextNode(argsToArray(arguments).join('')); }; /** Returns a new Button element with the given optional label and on-click event listener function. */ dom.button = function(label,callback){ const b = this.create('button'); if(label) b.appendChild(this.text(label)); if('function' === typeof callback){ b.addEventListener('click', callback, false); } return b; }; /** Returns a TEXTAREA element. Usages: ([boolean readonly = false]) (non-boolean rows[,cols[,readonly=false]]) Each of the rows/cols/readonly attributes is only set if it is truthy. */ dom.textarea = function(){ const rc = this.create('textarea'); let rows, cols, readonly; if(1===arguments.length){ if('boolean'===typeof arguments[0]){ readonly = !!arguments[0]; }else{ rows = arguments[0]; } }else if(arguments.length){ rows = arguments[0]; cols = arguments[1]; readonly = arguments[2]; } if(rows) rc.setAttribute('rows',rows); if(cols) rc.setAttribute('cols', cols); if(readonly) rc.setAttribute('readonly', true); return rc; }; /** Returns a new SELECT element. */ dom.select = dom.createElemFactory('select'); /** Returns an OPTION element with the given value and label text (which defaults to the value). Usage: (value[, label]) (selectElement [,value [,label = value]]) Any forms taking a SELECT element append the new element to the given SELECT element. If any label is falsy and the value is not then the value is used as the label. A non-falsy label value may have any type suitable for passing as the 2nd argument to dom.append(). If the value has the undefined value then it is NOT assigned as the option element's value and no label is set unless it has a non-undefined value. */ dom.option = function(value,label){ const a = arguments; var sel; if(1==a.length){ if(a[0] instanceof HTMLElement){ sel = a[0]; }else{ value = a[0]; } }else if(2==a.length){ if(a[0] instanceof HTMLElement){ sel = a[0]; value = a[1]; }else{ value = a[0]; label = a[1]; } } else if(3===a.length){ sel = a[0]; value = a[1]; label = a[2]; } const o = this.create('option'); if(undefined !== value){ o.value = value; this.append(o, this.text(label || value)); }else if(undefined !== label){ this.append(o, label); } if(sel) this.append(sel, o); return o; }; dom.h = function(level){ return this.create('h'+level); }; dom.ul = dom.createElemFactory('ul'); /** Creates and returns a new LI element, appending it to the given parent argument if it is provided. */ dom.li = function(parent){ const li = this.create('li'); if(parent) parent.appendChild(li); return li; }; /** Returns a function which creates a new DOM element of the given type and accepts an optional parent DOM element argument. If the function's argument is truthy, the new child element is appended to the given parent element. Returns the new child element. */ dom.createElemFactoryWithOptionalParent = function(childType){ return function(parent){ const e = this.create(childType); if(parent) parent.appendChild(e); return e; }; }; dom.table = dom.createElemFactory('table'); dom.thead = dom.createElemFactoryWithOptionalParent('thead'); dom.tbody = dom.createElemFactoryWithOptionalParent('tbody'); dom.tfoot = dom.createElemFactoryWithOptionalParent('tfoot'); dom.tr = dom.createElemFactoryWithOptionalParent('tr'); dom.td = dom.createElemFactoryWithOptionalParent('td'); dom.th = dom.createElemFactoryWithOptionalParent('th'); /** Creates and returns a FIELDSET element, optionaly with a LEGEND element added to it. If legendText is an HTMLElement then is is assumed to be a LEGEND and is appended as-is, else it is assumed (if truthy) to be a value suitable for passing to dom.append(aLegendElement,...). */ dom.fieldset = function(legendText){ const fs = this.create('fieldset'); if(legendText){ this.append( fs, (legendText instanceof HTMLElement) ? legendText : this.append(this.legend(legendText)) ); } return fs; }; /** Returns a new LEGEND legend element. The given argument, if not falsy, is append()ed to the element (so it may be a string or DOM element. */ dom.legend = function(legendText){ const rc = this.create('legend'); if(legendText) this.append(rc, legendText); return rc; }; /** Appends each argument after the first to the first argument (a DOM node) and returns the first argument. - If an argument is a string or number, it is transformed into a text node. - If an argument is an array or has a forEach member, this function appends each element in that list to the target by calling its forEach() method to pass it (recursively) to this function. - Else the argument assumed to be of a type legal to pass to parent.appendChild(). */ dom.append = function f(parent/*,...*/){ const a = argsToArray(arguments); a.shift(); for(let i in a) { var e = a[i]; if(isArray(e) || e.forEach){ e.forEach((x)=>f.call(this, parent,x)); continue; } if('string'===typeof e || 'number'===typeof e || 'boolean'===typeof e || e instanceof Error) e = this.text(e); parent.appendChild(e); } return parent; }; dom.input = function(type){ return this.attr(this.create('input'), 'type', type); }; /** Returns a new CHECKBOX input element. Usages: ([boolean checked = false]) (non-boolean value [,boolean checked]) */ dom.checkbox = function(value, checked){ const rc = this.input('checkbox'); if(1===arguments.length && 'boolean'===typeof value){ checked = !!value; value = undefined; } if(undefined !== value) rc.value = value; if(!!checked) rc.checked = true; return rc; }; /** Returns a new RADIO input element. ([boolean checked = false]) (string name [,boolean checked]) (string name, non-boolean value [,boolean checked]) */ dom.radio = function(){ const rc = this.input('radio'); let name, value, checked; if(1===arguments.length && 'boolean'===typeof name){ checked = arguments[0]; name = value = undefined; }else if(2===arguments.length){ name = arguments[0]; if('boolean'===typeof arguments[1]){ checked = arguments[1]; }else{ value = arguments[1]; checked = undefined; } }else if(arguments.length){ name = arguments[0]; value = arguments[1]; checked = arguments[2]; } if(name) this.attr(rc, 'name', name); if(undefined!==value) rc.value = value; if(!!checked) rc.checked = true; return rc; }; /** Internal impl for addClass(), removeClass(). */ const domAddRemoveClass = function f(action,e){ if(!f.rxSPlus){ f.rxSPlus = /\s+/; f.applyAction = function(e,a,v){ if(!e || !v /*silently skip empty strings/flasy values, for user convenience*/) return; else if(e.forEach){ e.forEach((E)=>E.classList[a](v)); }else{ e.classList[a](v); } }; } var i = 2, n = arguments.length; for( ; i < n; ++i ){ let c = arguments[i]; if(!c) continue; else if(isArray(c) || ('string'===typeof c && c.indexOf(' ')>=0 && (c = c.split(f.rxSPlus))) || c.forEach ){ c.forEach((k)=>k ? f.applyAction(e, action, k) : false); // ^^^ we could arguably call f(action,e,k) to recursively // apply constructs like ['foo bar'] or [['foo'],['bar baz']]. }else if(c){ f.applyAction(e, action, c); } } return e; }; /** Adds one or more CSS classes to one or more DOM elements. The first argument is a target DOM element or a list type of such elements which has a forEach() method. Each argument after the first may be a string or array of strings. Each string may contain spaces, in which case it is treated as a list of CSS classes. Returns e. */ dom.addClass = function(e,c){ const a = argsToArray(arguments); a.unshift('add'); return domAddRemoveClass.apply(this, a); }; /** The 'remove' counterpart of the addClass() method, taking the same arguments and returning the same thing. */ dom.removeClass = function(e,c){ const a = argsToArray(arguments); a.unshift('remove'); return domAddRemoveClass.apply(this, a); }; /** Toggles CSS class c on e (a single element for forEach-capable collection of elements). Returns its first argument. */ dom.toggleClass = function f(e,c){ if(e.forEach){ e.forEach((x)=>x.classList.toggle(c)); }else{ e.classList.toggle(c); } return e; }; /** Returns true if DOM element e contains CSS class c, else false. */ dom.hasClass = function(e,c){ return (e && e.classList) ? e.classList.contains(c) : false; }; /** Each argument after the first may be a single DOM element or a container of them with a forEach() method. All such elements are appended, in the given order, to the dest element using dom.append(dest,theElement). Thus the 2nd and susequent arguments may be any type supported as the 2nd argument to that function. Returns dest. */ dom.moveTo = function(dest,e){ const n = arguments.length; var i = 1; const self = this; for( ; i < n; ++i ){ e = arguments[i]; this.append(dest, e); } return dest; }; /** Each argument after the first may be a single DOM element or a container of them with a forEach() method. For each DOM element argument, all children of that DOM element are moved to dest (via appendChild()). For each list argument, each entry in the list is assumed to be a DOM element and is appended to dest. dest may be an Array, in which case each child is pushed into the array and removed from its current parent element. All children are appended in the given order. Returns dest. */ dom.moveChildrenTo = function f(dest,e){ if(!f.mv){ f.mv = function(d,v){ if(d instanceof Array){ d.push(v); if(v.parentNode) v.parentNode.removeChild(v); } else d.appendChild(v); }; } const n = arguments.length; var i = 1; for( ; i < n; ++i ){ e = arguments[i]; if(!e){ console.warn("Achtung: dom.moveChildrenTo() passed a falsy value at argument",i,"of", arguments,arguments[i]); continue; } if(e.forEach){ e.forEach((x)=>f.mv(dest, x)); }else{ while(e.firstChild){ f.mv(dest, e.firstChild); } } } return dest; }; /** Adds each argument (DOM Elements) after the first to the DOM immediately before the first argument (in the order provided), then removes the first argument from the DOM. Returns void. If any argument beyond the first has a forEach method, that method is used to recursively insert the collection's contents before removing the first argument from the DOM. */ dom.replaceNode = function f(old,nu){ var i = 1, n = arguments.length; ++f.counter; try { for( ; i < n; ++i ){ const e = arguments[i]; if(e.forEach){ e.forEach((x)=>f.call(this,old,e)); continue; } old.parentNode.insertBefore(e, old); } } finally{ --f.counter; } if(!f.counter){ old.parentNode.removeChild(old); } }; dom.replaceNode.counter = 0; /** Two args == getter: (e,key), returns value Three or more == setter: (e,key,val[...,keyN,valN]), returns e. If val===null or val===undefined then the attribute is removed. If (e) has a forEach method then this routine is applied to each element of that collection via that method. Each pair of keys/values is applied to all elements designated by the first argument. */ dom.attr = function f(e){ if(2===arguments.length) return e.getAttribute(arguments[1]); const a = argsToArray(arguments); if(e.forEach){ /* Apply to all elements in the collection */ e.forEach(function(x){ a[0] = x; f.apply(f,a); }); return e; } a.shift(/*element(s)*/); while(a.length){ const key = a.shift(), val = a.shift(); if(null===val || undefined===val){ e.removeAttribute(key); }else{ e.setAttribute(key,val); } } return e; }; const enableDisable = function f(enable){ var i = 1, n = arguments.length; for( ; i < n; ++i ){ let e = arguments[i]; if(e.forEach){ e.forEach((x)=>f(enable,x)); }else{ e.disabled = !enable; } } return arguments[1]; }; /** Enables (by removing the "disabled" attribute) each element (HTML DOM element or a collection with a forEach method) and returns the first argument. */ dom.enable = function(e){ const args = argsToArray(arguments); args.unshift(true); return enableDisable.apply(this,args); }; /** Disables (by setting the "disabled" attribute) each element (HTML DOM element or a collection with a forEach method) and returns the first argument. */ dom.disable = function(e){ const args = argsToArray(arguments); args.unshift(false); return enableDisable.apply(this,args); }; /** A proxy for document.querySelector() which throws if selection x is not found. It may optionally be passed an "origin" object as its 2nd argument, which restricts the search to that branch of the tree. */ dom.selectOne = function(x,origin){ var src = origin || document, e = src.querySelector(x); if(!e){ e = new Error("Cannot find DOM element: "+x); console.error(e, src); throw e; } return e; }; /** "Blinks" the given element a single time for the given number of milliseconds, defaulting (if the 2nd argument is falsy or not a number) to flashOnce.defaultTimeMs. If a 3rd argument is passed in, it must be a function, and it gets called at the end of the asynchronous flashing processes. This will only activate once per element during that timeframe - further calls will become no-ops until the blink is completed. This routine adds a dataset member to the element for the duration of the blink, to allow it to block multiple blinks. If passed 2 arguments and the 2nd is a function, it behaves as if it were called as (arg1, undefined, arg2). Returns e, noting that the flash itself is asynchronous and may still be running, or not yet started, when this function returns. */ dom.flashOnce = function f(e,howLongMs,afterFlashCallback){ if(e.dataset.isBlinking){ return; } if(2===arguments.length && 'function' ===typeof howLongMs){ afterFlashCallback = howLongMs; howLongMs = f.defaultTimeMs; } if(!howLongMs || 'number'!==typeof howLongMs){ howLongMs = f.defaultTimeMs; } e.dataset.isBlinking = true; const transition = e.style.transition; e.style.transition = "opacity "+howLongMs+"ms ease-in-out"; const opacity = e.style.opacity; e.style.opacity = 0; setTimeout(function(){ e.style.transition = transition; e.style.opacity = opacity; delete e.dataset.isBlinking; if(afterFlashCallback) afterFlashCallback(); }, howLongMs); return e; }; dom.flashOnce.defaultTimeMs = 400; /** A DOM event handler which simply passes event.target to dom.flashOnce(). */ dom.flashOnce.eventHandler = (event)=>dom.flashOnce(event.target) /** This variant of flashOnce() flashes the element e n times for a duration of howLongMs milliseconds then calls the afterFlashCallback() callback. It may also be called with 2 or 3 arguments, in which case: 2 arguments: default flash time and no callback. 3 arguments: 3rd may be a flash delay time or a callback function. Returns this object but the flashing is asynchronous. Depending on system load and related factors, a multi-flash animation might stutter and look suboptimal. */ dom.flashNTimes = function(e,n,howLongMs,afterFlashCallback){ const args = argsToArray(arguments); args.splice(1,1); if(arguments.length===3 && 'function'===typeof howLongMs){ afterFlashCallback = howLongMs; howLongMs = args[1] = this.flashOnce.defaultTimeMs; }else if(arguments.length<3){ args[1] = this.flashOnce.defaultTimeMs; } n = +n; const self = this; const cb = args[2] = function f(){ if(--n){ setTimeout(()=>self.flashOnce(e, howLongMs, f), howLongMs+(howLongMs*0.1)/*we need a slight gap here*/); }else if(afterFlashCallback){ afterFlashCallback(); } }; this.flashOnce.apply(this, args); return this; }; /** Adds the given CSS class or array of CSS classes to the given element or forEach-capable list of elements for howLongMs, then removes it. If afterCallack is a function, it is called after the CSS class is removed from all elements. If called with 3 arguments and the 3rd is a function, the 3rd is treated as a callback and the default time (addClassBriefly.defaultTimeMs) is used. If called with only 2 arguments, a time of addClassBriefly.defaultTimeMs is used. Passing a value of 0 for howLongMs causes the default value to be applied. Returns this object but the CSS removal is asynchronous. */ dom.addClassBriefly = function f(e, className, howLongMs, afterCallback){ if(arguments.length<4 && 'function'===typeof howLongMs){ afterCallback = howLongMs; howLongMs = f.defaultTimeMs; }else if(arguments.length<3 || !+howLongMs){ howLongMs = f.defaultTimeMs; } this.addClass(e, className); setTimeout(function(){ dom.removeClass(e, className); if(afterCallback) afterCallback(); }, howLongMs); return this; }; dom.addClassBriefly.defaultTimeMs = 1000; /** Attempts to copy the given text to the system clipboard. Returns true if it succeeds, else false. */ dom.copyTextToClipboard = function(text){ if( window.clipboardData && window.clipboardData.setData ){ window.clipboardData.setData('Text',text); return true; }else{ const x = document.createElement("textarea"); x.style.position = 'fixed'; x.value = text; document.body.appendChild(x); x.select(); var rc; try{ document.execCommand('copy'); rc = true; }catch(err){ rc = false; }finally{ document.body.removeChild(x); } return rc; } }; /** Copies all properties from the 2nd argument (a plain object) into the style member of the first argument (DOM element or a forEach-capable list of elements). If the 2nd argument is falsy or empty, this is a no-op. Returns its first argument. */ dom.copyStyle = function f(e, style){ if(e.forEach){ e.forEach((x)=>f(x, style)); return e; } if(style){ let k; for(k in style){ if(style.hasOwnProperty(k)) e.style[k] = style[k]; } } return e; }; /** Given a DOM element, this routine measures its "effective height", which is the bounding top/bottom range of this element and all of its children, recursively. For some DOM structure cases, a parent may have a reported height of 0 even though children have non-0 sizes. Returns 0 if !e or if the element really has no height. */ dom.effectiveHeight = function f(e){ if(!e) return 0; if(!f.measure){ f.measure = function callee(e, depth){ if(!e) return; const m = e.getBoundingClientRect(); if(0===depth){ callee.top = m.top; callee.bottom = m.bottom; }else{ callee.top = m.top ? Math.min(callee.top, m.top) : callee.top; callee.bottom = Math.max(callee.bottom, m.bottom); } Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1)); if(0===depth){ //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top)); f.extra += callee.bottom - callee.top; } return f.extra; }; } f.extra = 0; f.measure(e,0); return f.extra; }; /** Parses a string as HTML. Usages: Array (htmlString) DOMElement (DOMElement target, htmlString) The first form parses the string as HTML and returns an Array of all elements parsed from it. If string is falsy then it returns an empty array. The second form parses the HTML string and appends all elements to the given target element using dom.append(), then returns the first argument. Caveats: - It expects a partial HTML document as input, not a full HTML document with a HEAD and BODY tags. Because of how DOMParser works, only children of the parsed document's (virtual) body are acknowledged by this routine. */ dom.parseHtml = function(){ let childs, string, tgt; if(1===arguments.length){ string = arguments[0]; }else if(2==arguments.length){ tgt = arguments[0]; string = arguments[1]; } if(string){ const newNode = new DOMParser().parseFromString(string, 'text/html'); childs = newNode.documentElement.querySelector('body'); childs = childs ? Array.prototype.slice.call(childs.childNodes, 0) : []; /* ^^^ we need a clone of the list because reparenting them modifies a NodeList they're in. */ }else{ childs = []; } return tgt ? this.moveTo(tgt, childs) : childs; }; /** Sets up pseudo-automatic content preview handling between a source element (typically a TEXTAREA) and a target rendering element (typically a DIV). The selector argument must be one of: - A single DOM element - A collection of DOM elements with a forEach method. - A CSS selector Each element in the collection must have the following data attributes: - data-f-preview-from: is either a DOM element id, WITH a leading '#' prefix, or the name of a method (see below). If it's an ID, the DOM element must support .value to get the content. - data-f-preview-to: the DOM element id of the target "previewer" element, WITH a leading '#', or the name of a method (see below). - data-f-preview-via: the name of a method (see below). - OPTIONAL data-f-preview-as-text: a numeric value. Explained below. Each element gets a click handler added to it which does the following: 1) Reads the content from its data-f-preview-from element or, if that property refers to a method, calls the method without arguments and uses its result as the content. 2) Passes the content to methodNamespace[f-data-post-via](content,callback). f-data-post-via is responsible for submitting the preview HTTP request, including any parameters the request might require. When the response arrives, it must pass the content of the response to its 2nd argument, an auto-generated callback installed by this mechanism which... 3) Assigns the response text to the data-f-preview-to element or passes it to the function methodNamespace[f-data-preview-to](content), as appropriate. If data-f-preview-to is a DOM element and data-f-preview-as-text is '0' (the default) then the target elements contents are replaced with the given content as HTML, else the content is assigned to the target's textContent property. (Note that this routine uses DOMParser, rather than assignment to innerHTML, to apply HTML-format content.) The methodNamespace (2nd argument) defaults to fossil.page, and any method-name data properties, e.g. data-f-preview-via and potentially data-f-preview-from/to, must be a single method name, not a property-access-style string. e.g. "myPreview" is legal but "foo.myPreview" is not (unless, of course, the method is actually named "foo.myPreview" (which is legal but would be unconventional)). All such methods are called with methodNamespace as their "this". An example... First an input button: <button id='test-preview-connector' data-f-preview-from='#fileedit-content-editor' // elem ID or method name data-f-preview-via='myPreview' // method name data-f-preview-to='#fileedit-tab-preview-wrapper' // elem ID or method name >Preview update</button> And a sample data-f-preview-via method: fossil.page.myPreview = function(content,callback){ const fd = new FormData(); fd.append('foo', ...); fossil.fetch('preview_forumpost',{ payload: fd, onload: callback, onerror: (e)=>{ // only if app-specific handling is needed fossil.fetch.onerror(e); // default impl ... any app-specific error reporting ... } }); }; Then connect the parts with: fossil.connectPagePreviewers('#test-preview-connector'); Note that the data-f-preview-from, data-f-preview-via, and data-f-preview-to selector are not resolved until the button is actually clicked, so they need not exist in the DOM at the instant when the connection is set up, so long as they can be resolved when the preview-refreshing element is clicked. Maintenance reminder: this method is not strictly part of fossil.dom, but is in its file because it needs access to dom.parseHtml() to avoid an innerHTML assignment and all code which uses this routine also needs fossil.dom. */ F.connectPagePreviewers = function f(selector,methodNamespace){ if('string'===typeof selector){ selector = document.querySelectorAll(selector); }else if(!selector.forEach){ selector = [selector]; } if(!methodNamespace){ methodNamespace = F.page; } selector.forEach(function(e){ e.addEventListener( 'click', function(r){ const eTo = '#'===e.dataset.fPreviewTo[0] ? document.querySelector(e.dataset.fPreviewTo) : methodNamespace[e.dataset.fPreviewTo], eFrom = '#'===e.dataset.fPreviewFrom[0] ? document.querySelector(e.dataset.fPreviewFrom) : methodNamespace[e.dataset.fPreviewFrom], asText = +(e.dataset.fPreviewAsText || 0); eTo.textContent = "Fetching preview..."; methodNamespace[e.dataset.fPreviewVia]( (eFrom instanceof Function ? eFrom.call(methodNamespace) : eFrom.value), function(r){ if(eTo instanceof Function) eTo.call(methodNamespace, r||''); else if(!r){ dom.clearElement(eTo); }else if(asText){ eTo.textContent = r; }else{ dom.parseHtml(dom.clearElement(eTo), r); } } ); }, false ); }); return this; }/*F.connectPagePreviewers()*/; return F.dom = dom; })(window.fossil); |
Added src/fossil.fetch.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | "use strict"; /** Requires that window.fossil has already been set up. */ (function(namespace){ const fossil = namespace; /** fetch() is an HTTP request/response mini-framework similar (but not identical) to the not-quite-ubiquitous window.fetch(). JS usages: fossil.fetch( URI [, onLoadCallback] ); fossil.fetch( URI [, optionsObject = {}] ); Noting that URI must be relative to the top of the repository and should not start with a slash (if it does, it is stripped). It gets the equivalent of "%R/" prepended to it. The optionsObject may be an onload callback or an object with any of these properties: - onload: callback(responseData) (default = output response to the console). In the context of the callback, the options object is "this", noting that this call may have amended the options object with state other than what the caller provided. - onerror: callback(Error object) (default = output error message to console.error() and fossil.error()). Triggered if the request generates any response other than HTTP 200, suffers a connection error or timeout while awaiting a response, or if the onload() handler throws an exception. In the context of the callback, the options object is "this". Note that this function is intended to be used solely for error reporting, not error recovery. Because onerror() may be called if onload() throws, it is up to the caller to ensure that their onerror() callback references only state which is valid in such a case. - method: 'POST' | 'GET' (default = 'GET'). CASE SENSITIVE! - payload: anything acceptable by XHR2.send(ARG) (DOMString, Document, FormData, Blob, File, ArrayBuffer), or a plain object or array, either of which gets JSON.stringify()'d. If payload is set then the method is automatically set to 'POST'. By default XHR2 will set the content type based on the payload type. If an object/array is converted to JSON, the contentType option is automatically set to 'application/json', and if JSON.stringify() of that value fails then the exception is propagated to this function's caller. - contentType: Optional request content type when POSTing. Ignored if the method is not 'POST'. - responseType: optional string. One of ("text", "arraybuffer", "blob", or "document") (as specified by XHR2). Default = "text". As an extension, it supports "json", which tells it that the response is expected to be text and that it should be JSON.parse()d before passing it on to the onload() callback. If parsing of such an object fails, the onload callback is not called, and the onerror() callback is passed the exception from the parsing error. - urlParams: string|object. If a string, it is assumed to be a URI-encoded list of params in the form "key1=val1&key2=val2...", with NO leading '?'. If it is an object, all of its properties get converted to that form. Either way, the parameters get appended to the URL before submitting the request. - responseHeaders: If true, the onload() callback is passed an additional argument: a map of all of the response headers. If it's a string value, the 2nd argument passed to onload() is instead the value of that single header. If it's an array, it's treated as a list of headers to return, and the 2nd argument is a map of those header values. When a map is passed on, all of its keys are lower-cased. When a given header is requested and that header is set multiple times, their values are (per the XHR docs) concatenated together with ", " between them. - beforesend/aftersend: optional callbacks which are called without arguments immediately before the request is submitted and immediately after it is received, regardless of success or error. In the context of the callback, the options object is the "this". These can be used to, e.g., keep track of in-flight requests and update the UI accordingly, e.g. disabling/enabling DOM elements. Any exceptions thrown in an beforesend are passed to the onerror() handler and cause the fetch() to prematurely abort. Exceptions thrown in aftersend are currently silently ignored (feature or bug?). - timeout: integer in milliseconds specifying the XHR timeout duration. Default = fossil.fetch.timeout. When an options object does not provide onload/onerror/beforesend/aftersend handlers of its own, this function falls back to defaults which are member properties of this function with the same name, e.g. fossil.fetch.onload(). The default onload/onerror implementations route the data through the dev console and (for onerror()) through fossil.error(). The default beforesend/aftersend are no-ops. Individual pages may overwrite those members to provide default implementations suitable for the page's use, e.g. keeping track of how many in-flight ajax requests are pending. Note that this routine may add properties to the 2nd argument, so that instance should not be kept around for later use. Returns this object, noting that the XHR request is asynchronous, and still in transit (or has yet to be sent) when that happens. */ fossil.fetch = function f(uri,opt){ const F = fossil; if(!f.onload){ f.onload = (r)=>console.debug('fossil.fetch() XHR response:',r); } if(!f.onerror){ f.onerror = function(e/*exception*/){ console.error("fossil.fetch() XHR error:",e); if(e instanceof Error) F.error('Exception:',e); else F.error("Unknown error in handling of XHR request."); }; } if(!f.parseResponseHeaders){ f.parseResponseHeaders = function(h){ const rc = {}; if(!h) return rc; const ar = h.trim().split(/[\r\n]+/); ar.forEach(function(line) { const parts = line.split(': '); const header = parts.shift(); const value = parts.join(': '); rc[header.toLowerCase()] = value; }); return rc; }; } if('/'===uri[0]) uri = uri.substr(1); if(!opt) opt = {}; else if('function'===typeof opt) opt={onload:opt}; if(!opt.onload) opt.onload = f.onload; if(!opt.onerror) opt.onerror = f.onerror; if(!opt.beforesend) opt.beforesend = f.beforesend; if(!opt.aftersend) opt.aftersend = f.aftersend; let payload = opt.payload, jsonResponse = false; if(undefined!==payload){ opt.method = 'POST'; if(!(payload instanceof FormData) && !(payload instanceof Document) && !(payload instanceof Blob) && !(payload instanceof File) && !(payload instanceof ArrayBuffer) && ('object'===typeof payload || payload instanceof Array)){ payload = JSON.stringify(payload); opt.contentType = 'application/json'; } } const url=[f.urlTransform(uri,opt.urlParams)], x=new XMLHttpRequest(); if('json'===opt.responseType){ /* 'json' is an extension to the supported XHR.responseType list. We use it as a flag to tell us to JSON.parse() the response. */ jsonResponse = true; x.responseType = 'text'; }else{ x.responseType = opt.responseType||'text'; } x.ontimeout = function(){ try{opt.aftersend()}catch(e){/*ignore*/} opt.onerror(new Error("XHR timeout of "+x.timeout+"ms expired.")); }; x.onreadystatechange = function(){ if(XMLHttpRequest.DONE !== x.readyState) return; try{opt.aftersend()}catch(e){/*ignore*/} if(false && 0===x.status){ /* For reasons unknown, we _sometimes_ trigger x.status==0 in FF when the /chat page starts up, but not in Chrome nor in other apps. Insofar as has been determined, this happens before a request is actually sent and it appears to have no side-effects on the app other than to generate an error (i.e. no requests/responses are missing). This is a silly workaround which may or may not bite us later. If so, it can be removed at the cost of an unsightly console error message in FF. */ return; } if(200!==x.status){ let err; try{ const j = JSON.parse(x.response); if(j.error) err = new Error(j.error); }catch(ex){/*ignore*/} opt.onerror(err || new Error("HTTP response status "+x.status+".")); return; } const orh = opt.responseHeaders; let head; if(true===orh){ head = f.parseResponseHeaders(x.getAllResponseHeaders()); }else if('string'===typeof orh){ head = x.getResponseHeader(orh); }else if(orh instanceof Array){ head = {}; orh.forEach((s)=>{ if('string' === typeof s) head[s.toLowerCase()] = x.getResponseHeader(s); }); } try{ const args = [(jsonResponse && x.response) ? JSON.parse(x.response) : x.response]; if(head) args.push(head); opt.onload.apply(opt, args); }catch(e){ opt.onerror(e); } }; try{opt.beforesend()} catch(e){ opt.onerror(e); return; } x.open(opt.method||'GET', url.join(''), true); if('POST'===opt.method && 'string'===typeof opt.contentType){ x.setRequestHeader('Content-Type',opt.contentType); } x.timeout = +opt.timeout || f.timeout; if(undefined!==payload) x.send(payload); else x.send(); return this; }; /** urlTransform() must refer to a function which accepts a relative path to the same site as fetch() is served from and an optional set of URL parameters to pass with it (in the form a of a string ("a=b&c=d...") or an object of key/value pairs (which it converts to such a string), and returns the resulting URL or URI as a string. */ fossil.fetch.urlTransform = (u,p)=>fossil.repoUrl(u,p); fossil.fetch.beforesend = function(){}; fossil.fetch.aftersend = function(){}; fossil.fetch.timeout = 15000/* Default timeout, in ms. */; })(window.fossil); |
Added src/fossil.numbered-lines.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (function callee(arg){ /* JS counterpart of info.c:output_text_with_line_numbers() which ties an event handler to the line numbers to allow selection of individual lines or ranges. Requires: fossil.bootstrap, fossil.dom, fossil.popupwidget, fossil.copybutton */ var tbl = arg || document.querySelectorAll('table.numbered-lines'); if(tbl && !arg){ if(tbl.length>1){ /* multiple query results: recurse */ tbl.forEach( (t)=>callee(t) ); return; }else{/* single query result */ tbl = tbl[0]; } } if(!tbl) return /* no matching elements */; const F = window.fossil, D = F.dom; const tdLn = tbl.querySelector('td.line-numbers'); const urlArgsRaw = (window.location.search||'?') .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */ .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */ .replace('?&','?'); var urlArgsDecoded = urlArgsRaw; try{urlArgsDecoded = decodeURIComponent(urlArgsRaw);} catch{} const lineState = { urlArgs: urlArgsDecoded, start: 0, end: 0 }; const lineTip = new F.PopupWidget({ style: { cursor: 'pointer' }, refresh: function(){ const link = this.state.link; D.clearElement(link); if(lineState.start){ const ls = [lineState.start]; if(lineState.end) ls.push(lineState.end); link.dataset.url = ( window.location.toString().split('?')[0] + lineState.urlArgs + '&ln='+ls.join('-') ); D.append( D.clearElement(link), ' Copy link to '+( ls.length===1 ? 'line ' : 'lines ' )+ls.join('-') ); }else{ D.append(link, "No lines selected."); } }, init: function(){ const e = this.e; const btnCopy = D.span(), link = D.span(); this.state = {link}; F.copyButton(btnCopy,{ copyFromElement: link, extractText: ()=>link.dataset.url, oncopy: (ev)=>{ D.flashOnce(ev.target, undefined, ()=>lineTip.hide()); // arguably too snazzy: F.toast.message("Copied link to clipboard."); } }); this.e.addEventListener('click', ()=>btnCopy.click(), false); D.append(this.e, btnCopy, link) } }); tbl.addEventListener('click', ()=>lineTip.hide(), true); tdLn.addEventListener('click', function f(ev){ if('SPAN'!==ev.target.tagName) return; else if('number' !== typeof f.mode){ f.mode = 0 /*0=none selected, 1=1 selected, 2=2 selected*/; f.spans = tdLn.querySelectorAll('span'); f.selected = tdLn.querySelectorAll('span.selected-line'); f.unselect = (e)=>D.removeClass(e, 'selected-line','start','end'); } ev.stopPropagation(); const ln = +ev.target.innerText; if(2===f.mode){/*Reset selection*/ f.mode = 0; } if(0===f.mode){/*Select single line*/ lineState.end = 0; lineState.start = ln; f.mode = 1; }else if(1===f.mode){ if(ln === lineState.start){/*Unselect line*/ lineState.start = 0; f.mode = 0; }else{/*Select range*/ if(ln<lineState.start){ lineState.end = lineState.start; lineState.start = ln; }else{ lineState.end = ln; } f.mode = 2; } } if(f.selected){/*Unmark previously-selected lines.*/ f.selected.forEach(f.unselect); f.selected = undefined; } if(0===f.mode){ lineTip.hide(); }else{/*Mark selected lines*/ const rect = ev.target.getBoundingClientRect(); f.selected = []; if(f.spans.length>=lineState.start){ let i = lineState.start, end = lineState.end || lineState.start, span = f.spans[i-1]; for( ; i<=end && span; span = f.spans[i++] ){ span.classList.add('selected-line'); f.selected.push(span); if(i===lineState.start) span.classList.add('start'); if(i===end) span.classList.add('end'); } } lineTip.refresh().show(rect.right+3, rect.top-4); } }, false); })(); |
Added src/fossil.page.brlist.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* * This script adds multiselect facility for the list of branches. * * Some info on 'const': * https://caniuse.com/const * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const#browser_compatibility * * According to MDN 'const' requires Android's WebView 37, * which may not be available. * For the time being, continueing without 'const' and 'indexOf' * (but that may be reconsidered later). */ window.addEventListener( 'load', function() { var submenu = document.querySelector("div.submenu"); var anchor = document.createElement("A"); var brlistDataObj = document.getElementById("brlist-data"); var brlistDataTxt = brlistDataObj.textContent || brlistDataObj.innerText; var brlistData = JSON.parse(brlistDataTxt); anchor.classList.add("label"); anchor.classList.add("timeline-link"); anchor.href = brlistData.timelineUrl; var prefix = anchor.href.toString() + "?ms=brlist&t="; submenu.insertBefore(anchor,submenu.childNodes[0]); var amendAnchor = function( selected ){ if( selected.length == 0 ){ anchor.classList.remove('selected'); anchor.href = prefix; return; } re = selected.join(","); try{re = encodeURIComponent(re);} catch{console.log("encodeURIComponent() failed for ",re);} anchor.href = prefix + re; anchor.innerHTML = "View " + selected.length + ( selected.length > 1 ? " branches" : " branch" ); anchor.classList.add('selected'); } var onChange = function( event ){ var cbx = event.target; var tr = cbx.parentElement.parentElement; var tag = cbx.parentElement.children[0].innerText; var re = anchor.href.substr(prefix.length); try{re = decodeURIComponent(re);} catch{console.log("decodeURIComponent() failed for ",re);} var selected = ( re != "" ? re.split(",") : [] ); if( cbx.checked ){ selected.push(tag); tr.classList.add('selected'); } else { tr.classList.remove('selected'); for( var i = selected.length; --i >= 0 ;) if( selected[i] == tag ) selected.splice(i,1); } amendAnchor( selected ); } var stags = []; /* initially selected tags, not used above */ document.querySelectorAll("div.brlist > table td:first-child > input") .forEach( function( cbx ){ cbx.onchange = onChange; cbx.disabled = false; if( cbx.checked ){ stags.push(cbx.parentElement.children[0].innerText); cbx.parentElement.parentElement.classList.add('selected'); } }); amendAnchor( stags ); }); // window.addEventListener( 'load' ... |
Added src/fossil.page.chat.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /** This file contains the client-side implementation of fossil's /chat application. */ window.fossil.onPageLoad(function(){ const F = window.fossil, D = F.dom; const E1 = function(selector){ const e = document.querySelector(selector); if(!e) throw new Error("missing required DOM element: "+selector); return e; }; /** Returns true if e is entirely within the bounds of the window's viewport. */ const isEntirelyInViewport = function(e) { const rect = e.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); }; /** Returns true if e's on-screen position vertically overlaps that of element v's. Horizontal overlap is ignored (we don't need it for our case). */ const overlapsElemView = function(e,v) { const r1 = e.getBoundingClientRect(), r2 = v.getBoundingClientRect(); if(r1.top<=r2.bottom && r1.top>=r2.top) return true; else if(r1.bottom<=r2.bottom && r1.bottom>=r2.top) return true; return false; }; const addAnchorTargetBlank = (e)=>D.attr(e, 'target','_blank'); /** Returns an almost-ISO8601 form of Date object d. */ const iso8601ish = function(d){ return d.toISOString() .replace('T',' ').replace(/\.\d+/,'') .replace('Z', ' zulu'); }; const pad2 = (x)=>('0'+x).substr(-2); /** Returns the local time string of Date object d, defaulting to the current time. */ const localTimeString = function ff(d){ d || (d = new Date()); return [ d.getFullYear(),'-',pad2(d.getMonth()+1/*sigh*/), '-',pad2(d.getDate()), ' ',pad2(d.getHours()),':',pad2(d.getMinutes()), ':',pad2(d.getSeconds()) ].join(''); }; (function(){ let dbg = document.querySelector('#debugMsg'); if(dbg){ /* This can inadvertently influence our flexbox layouts, so move it out of the way. */ D.append(document.body,dbg); } })(); const GetFramingElements = function() { return document.querySelectorAll([ "body > header", "body > nav.mainmenu", "body > footer", "#debugMsg" ].join(',')); }; const ForceResizeKludge = (function(){ /* Workaround for Safari mayhem regarding use of vh CSS units.... We tried to use vh units to set the content area size for the chat layout, but Safari chokes on that, so we calculate that height here: 85% when in "normal" mode and 95% in chat-only mode. Larger than ~95% is too big for Firefox on Android, causing the input area to move off-screen. While we're here, we also use this to cap the max-height of the input field so that pasting huge text does not scroll the upper area of the input widget off-screen. */ const elemsToCount = GetFramingElements(); const contentArea = E1('div.content'); const bcl = document.body.classList; const resized = function f(){ if(f.$disabled) return; const wh = window.innerHeight, com = bcl.contains('chat-only-mode'); var ht; var extra = 0; if(com){ ht = wh; }else{ elemsToCount.forEach((e)=>e ? extra += D.effectiveHeight(e) : false); ht = wh - extra; } f.chat.e.inputX.style.maxHeight = (ht/2)+"px"; /* ^^^^ this is a middle ground between having no size cap on the input field and having a fixed arbitrary cap. */; contentArea.style.height = contentArea.style.maxHeight = [ "calc(", (ht>=100 ? ht : 100), "px", " - 0.65em"/*fudge value*/,")" /* ^^^^ hypothetically not needed, but both Chrome/FF on Linux will force scrollbars on the body if this value is too small; current value is empirically selected. */ ].join(''); if(false){ console.debug("resized.",wh, extra, ht, window.getComputedStyle(contentArea).maxHeight, contentArea); console.debug("Set input max height to: ", f.chat.e.inputX.style.maxHeight); } }; resized.$disabled = true/*gets deleted when setup is finished*/; window.addEventListener('resize', F.debounce(resized, 250), false); return resized; })(); fossil.FRK = ForceResizeKludge/*for debugging*/; const Chat = ForceResizeKludge.chat = (function(){ const cs = { // the "Chat" object (result of this function) verboseErrors: false /* if true then certain, mostly extraneous, error messages may be sent to the console. */, e:{/*map of certain DOM elements.*/ messageInjectPoint: E1('#message-inject-point'), pageTitle: E1('head title'), loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */, inputWrapper: E1("#chat-input-area"), inputElementWrapper: E1('#chat-input-line-wrapper'), fileSelectWrapper: E1('#chat-input-file-area'), viewMessages: E1('#chat-messages-wrapper'), btnSubmit: E1('#chat-button-submit'), btnAttach: E1('#chat-button-attach'), inputX: E1('#chat-input-field-x'), input1: E1('#chat-input-field-single'), inputM: E1('#chat-input-field-multi'), inputFile: E1('#chat-input-file'), contentDiv: E1('div.content'), viewConfig: E1('#chat-config'), viewPreview: E1('#chat-preview'), previewContent: E1('#chat-preview-content'), btnPreview: E1('#chat-button-preview'), views: document.querySelectorAll('.chat-view'), activeUserListWrapper: E1('#chat-user-list-wrapper'), activeUserList: E1('#chat-user-list') }, me: F.user.name, mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50, mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/, pageIsActive: 'visible'===document.visibilityState, changesSincePageHidden: 0, notificationBubbleColor: 'white', totalMessageCount: 0, // total # of inbound messages //! Number of messages to load for the history buttons loadMessageCount: Math.abs(F.config.chat.initSize || 20), ajaxInflight: 0, usersLastSeen:{ /* Map of user names to their most recent message time (JS Date object). Only messages received by the chat client are considered. */ /* Reminder: to convert a Julian time J to JS: new Date((J - 2440587.5) * 86400000) */ }, filterState:{ activeUser: undefined, match: function(uname){ return this.activeUser===uname || !this.activeUser; } }, /** Gets (no args) or sets (1 arg) the current input text field value, taking into account single- vs multi-line input. The getter returns a string and the setter returns this object. */ inputValue: function(){ const e = this.inputElement(); if(arguments.length){ if(e.isContentEditable) e.innerText = arguments[0]; else e.value = arguments[0]; return this; } return e.isContentEditable ? e.innerText : e.value; }, /** Asks the current user input field to take focus. Returns this. */ inputFocus: function(){ this.inputElement().focus(); return this; }, /** Returns the current message input element. */ inputElement: function(){ return this.e.inputFields[this.e.inputFields.$currentIndex]; }, /** Enables (if yes is truthy) or disables all elements in * this.disableDuringAjax. */ enableAjaxComponents: function(yes){ D[yes ? 'enable' : 'disable'](this.disableDuringAjax); return this; }, /* Must be called before any API is used which starts ajax traffic. If this call represents the currently only in-flight ajax request, all DOM elements in this.disableDuringAjax are disabled. We cannot do this via a central API because (1) window.fetch()'s Promise-based API seemingly makes that impossible and (2) the polling technique holds ajax requests open for as long as possible. A call to this method obligates the caller to also call ajaxEnd(). This must NOT be called for the chat-polling API except, as a special exception, the very first one which fetches the initial message list. */ ajaxStart: function(){ if(1===++this.ajaxInflight){ this.enableAjaxComponents(false); } }, /* Must be called after any ajax-related call for which ajaxStart() was called, regardless of success or failure. If it was the last such call (as measured by calls to ajaxStart() and ajaxEnd()), elements disabled by a prior call to ajaxStart() will be re-enabled. */ ajaxEnd: function(){ if(0===--this.ajaxInflight){ this.enableAjaxComponents(true); } }, disableDuringAjax: [ /* List of DOM elements disable while ajax traffic is in transit. Must be populated before ajax starts. We do this to avoid various race conditions in the UI and long-running network requests. */ ], /** Either scrolls .message-widget element eMsg into view immediately or, if it represents an inlined image, delays the scroll until the image is loaded, at which point it will scroll to either the newest message, if one is set or to eMsg (the liklihood is good, at least on initial page load, that the the image won't be loaded until other messages have been injected). */ scheduleScrollOfMsg: function(eMsg){ if(1===+eMsg.dataset.hasImage){ eMsg.querySelector('img').addEventListener( 'load', ()=>(this.e.newestMessage || eMsg).scrollIntoView(false) ); }else{ eMsg.scrollIntoView(false); } return this; }, /* Injects DOM element e as a new row in the chat, at the oldest end of the list if atEnd is truthy, else at the newest end of the list. */ injectMessageElem: function f(e, atEnd){ const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint, holder = this.e.viewMessages, prevMessage = this.e.newestMessage; if(!this.filterState.match(e.dataset.xfrom)){ e.classList.add('hidden'); } if(atEnd){ const fe = mip.nextElementSibling; if(fe) mip.parentNode.insertBefore(e, fe); else D.append(mip.parentNode, e); }else{ D.append(holder,e); this.e.newestMessage = e; } if(!atEnd && !this._isBatchLoading && e.dataset.xfrom!==this.me && (prevMessage ? !this.messageIsInView(prevMessage) : false)){ /* If a new non-history message arrives while the user is scrolled elsewhere, do not scroll to the latest message, but gently alert the user that a new message has arrived. */ if(!f.btnDown){ f.btnDown = D.button("⇣⇣⇣"); f.btnDown.addEventListener('click',()=>this.scrollMessagesTo(1),false); } F.toast.message(f.btnDown," New message has arrived."); }else if(!this._isBatchLoading && e.dataset.xfrom===Chat.me){ this.scheduleScrollOfMsg(e); }else if(!this._isBatchLoading){ /* When a message from someone else arrives, we have to figure out whether or not to scroll it into view. Ideally we'd just stuff it in the UI and let the flexbox layout DTRT, but Safari has expressed, in no uncertain terms, some disappointment with that approach, so we'll heuristicize it: if the previous last message is in view, assume the user is at or near the input element and scroll this one into view. If that message is NOT in view, assume the user is up reading history somewhere and do NOT scroll because doing so would interrupt them. There are middle grounds here where the user will experience a slight UI jolt, but this heuristic mostly seems to work out okay. If there was no previous message, assume we don't have any messages yet and go ahead and scroll this message into view (noting that that scrolling is hypothetically a no-op in such cases). The wrench in these works are posts with IMG tags, as those images are loaded async and the element does not yet have enough information to know how far to scroll! For such cases we have to delay the scroll until the image loads (and we hope it does so before another message arrives). */ if(1===+e.dataset.hasImage){ e.querySelector('img').addEventListener('load',()=>e.scrollIntoView()); }else if(!prevMessage || (prevMessage && isEntirelyInViewport(prevMessage))){ e.scrollIntoView(false); } } }, /** Returns true if chat-only mode is enabled. */ isChatOnlyMode: ()=>document.body.classList.contains('chat-only-mode'), /** Enters (if passed a truthy value or no arguments) or leaves "chat-only" mode. That mode hides the page's header and footer, leaving only the chat application visible to the user. */ chatOnlyMode: function f(yes){ if(undefined === f.elemsToToggle){ f.elemsToToggle = []; GetFramingElements().forEach((e)=>f.elemsToToggle.push(e)); } if(!arguments.length) yes = true; if(yes === this.isChatOnlyMode()) return this; if(yes){ D.addClass(f.elemsToToggle, 'hidden'); D.addClass(document.body, 'chat-only-mode'); document.body.scroll(0,document.body.height); }else{ D.removeClass(f.elemsToToggle, 'hidden'); D.removeClass(document.body, 'chat-only-mode'); } ForceResizeKludge(); return this; }, /** Tries to scroll the message area to... <0 = top of the message list, >0 = bottom of the message list, 0 == the newest message (normally the same position as >1). */ scrollMessagesTo: function(where){ if(where<0){ Chat.e.viewMessages.scrollTop = 0; }else if(where>0){ Chat.e.viewMessages.scrollTop = Chat.e.viewMessages.scrollHeight; }else if(Chat.e.newestMessage){ Chat.e.newestMessage.scrollIntoView(false); } }, toggleChatOnlyMode: function(){ return this.chatOnlyMode(!this.isChatOnlyMode()); }, messageIsInView: function(e){ return e ? overlapsElemView(e, this.e.viewMessages) : false; }, settings:{ get: (k,dflt)=>F.storage.get(k,dflt), getBool: (k,dflt)=>F.storage.getBool(k,dflt), set: function(k,v){ F.storage.set(k,v); F.page.dispatchEvent('chat-setting',{key: k, value: v}); }, /* Toggles the boolean setting specified by k. Returns the new value.*/ toggle: function(k){ const v = this.getBool(k); this.set(k, !v); return !v; }, addListener: function(setting, f){ F.page.addEventListener('chat-setting', function(ev){ if(ev.detail.key===setting) f(ev.detail); }, false); }, /* Default values of settings. These are used for initializing the setting event listeners and config view UI. */ defaults:{ /* When on, inbound images are displayed inlined, else as a link to download the image. */ "images-inline": !!F.config.chat.imagesInline, /* When on, ctrl-enter sends messages, else enter and ctrl-enter both send them. */ "edit-ctrl-send": false, /* When on, the edit field starts as a single line and expands as the user types, and the relevant buttons are laid out in a compact form. When off, the edit field and buttons are larger. */ "edit-compact-mode": true, /* See notes for this setting in fossil.page.wikiedit.js. Both /wikiedit and /fileedit share this persistent config option under the same storage key. */ "edit-shift-enter-preview": F.storage.getBool('edit-shift-enter-preview', true), /* When on, sets the font-family on messages and the edit field to monospace. */ "monospace-messages": false, /* When on, non-chat UI elements (page header/footer) are hidden */ "chat-only-mode": false, /* When set to a URI, it is assumed to be an audio file, which gets played when new messages arrive. When true, the first entry in the audio file selection list will be used. */ "audible-alert": true, /* When on, show the list of "active" users - those from whom we have messages in the currently-loaded history (noting that deletions are also messages). */ "active-user-list": false, /* When on, the [active-user-list] setting includes the timestamp of each user's most recent message. */ "active-user-list-timestamps": false, /* When on, the [audible-alert] is played for one's own messages, else it is only played for other users' messages. */ "alert-own-messages": false, /* "Experimental mode" input: use a contenteditable field for input. This is generally more comfortable to use, and more modern, than plain text input fields, but the list of browser-specific quirks and bugs is... not short. */ "edit-widget-x": false } }, /** Plays a new-message notification sound IF the audible-alert setting is true, else this is a no-op. Returns this. */ playNewMessageSound: function f(){ if(f.uri){ try{ if(!f.audio) f.audio = new Audio(f.uri); f.audio.currentTime = 0; f.audio.play(); }catch(e){ console.error("Audio playblack failed.", f.uri, e); } } return this; }, /** Sets the current new-message audio alert URI (must be a repository-relative path which responds with an audio file). Pass a falsy value to disable audio alerts. Returns this. */ setNewMessageSound: function f(uri){ delete this.playNewMessageSound.audio; this.playNewMessageSound.uri = uri; this.settings.set('audible-alert', uri); return this; }, /** Expects e to be one of the elements in this.e.views. The 'hidden' class is removed from e and added to all other elements in that list. Returns e. */ setCurrentView: function(e){ if(e===this.e.currentView){ return e; } this.e.views.forEach(function(E){ if(e!==E) D.addClass(E,'hidden'); }); this.e.currentView = e; if(this.e.currentView.$beforeShow) this.e.currentView.$beforeShow(); D.removeClass(e,'hidden'); this.animate(this.e.currentView, 'anim-fade-in-fast'); return this.e.currentView; }, /** Updates the "active user list" view if we are not currently batch-loading messages and if the active user list UI element is active. */ updateActiveUserList: function callee(){ if(this._isBatchLoading || this.e.activeUserListWrapper.classList.contains('hidden')){ return this; }else if(!callee.sortUsersSeen){ /** Array.sort() callback. Expects an array of user names and sorts them in last-received message order (newest first). */ const self = this; callee.sortUsersSeen = function(l,r){ l = self.usersLastSeen[l]; r = self.usersLastSeen[r]; if(l && r) return r - l; else if(l) return -1; else if(r) return 1; else return 0; }; callee.addUserElem = function(u){ const uSpan = D.addClass(D.span(), 'chat-user'); const uDate = self.usersLastSeen[u]; if(self.filterState.activeUser===u){ uSpan.classList.add('selected'); } uSpan.dataset.uname = u; D.append(uSpan, u, "\n", D.append( D.addClass(D.span(),'timestamp'), localTimeString(uDate)//.substr(5/*chop off year*/) )); if(uDate.$uColor){ uSpan.style.backgroundColor = uDate.$uColor; } D.append(self.e.activeUserList, uSpan); }; } //D.clearElement(this.e.activeUserList); D.remove(this.e.activeUserList.querySelectorAll('.chat-user')); Object.keys(this.usersLastSeen).sort( callee.sortUsersSeen ).forEach(callee.addUserElem); return this; }, /** Show or hide the active user list. Returns this object. */ showActiveUserList: function(yes){ if(0===arguments.length) yes = true; this.e.activeUserListWrapper.classList[ yes ? 'remove' : 'add' ]('hidden'); D.removeClass(Chat.e.activeUserListWrapper, 'collapsed'); if(Chat.e.activeUserListWrapper.classList.contains('hidden')){ /* When hiding this element, undo all filtering */ Chat.setUserFilter(false); /*Ideally we'd scroll the final message into view now, but because viewMessages is currently hidden behind viewConfig, scrolling is a no-op. */ Chat.scrollMessagesTo(1); }else{ Chat.updateActiveUserList(); Chat.animate(Chat.e.activeUserListWrapper, 'anim-flip-v'); } return this; }, showActiveUserTimestamps: function(yes){ if(0===arguments.length) yes = true; this.e.activeUserList.classList[yes ? 'add' : 'remove']('timestamps'); return this; }, /** Applies user name filter to all current messages, or clears the filter if uname is falsy. */ setUserFilter: function(uname){ this.filterState.activeUser = uname; const mw = this.e.viewMessages.querySelectorAll('.message-widget'); const self = this; let eLast; if(!uname){ D.removeClass(Chat.e.viewMessages.querySelectorAll('.message-widget.hidden'), 'hidden'); }else{ mw.forEach(function(w){ if(self.filterState.match(w.dataset.xfrom)){ w.classList.remove('hidden'); eLast = w; }else{ w.classList.add('hidden'); } }); } if(eLast) eLast.scrollIntoView(false); else this.scrollMessagesTo(1); cs.e.activeUserList.querySelectorAll('.chat-user').forEach(function(e){ e.classList[uname===e.dataset.uname ? 'add' : 'remove']('selected'); }); return this; }, /** If animations are enabled, passes its arguments to D.addClassBriefly(), else this is a no-op. If cb is a function, it is called after the CSS class is removed. Returns this object; */ animate: function f(e,a,cb){ if(!f.$disabled){ D.addClassBriefly(e, a, 0, cb); } return this; } }; cs.e.inputFields = [ cs.e.input1, cs.e.inputM, cs.e.inputX ]; cs.e.inputFields.$currentIndex = 0; cs.e.inputFields.forEach(function(e,ndx){ if(ndx===cs.e.inputFields.$currentIndex) D.removeClass(e,'hidden'); else D.addClass(e,'hidden'); }); if(D.attr(cs.e.inputX,'contenteditable','plaintext-only').isContentEditable){ cs.$browserHasPlaintextOnly = true; }else{ /* Only the Chrome family supports contenteditable=plaintext-only */ cs.$browserHasPlaintextOnly = false; D.attr(cs.e.inputX,'contenteditable','true'); } cs.animate.$disabled = true; F.fetch.beforesend = ()=>cs.ajaxStart(); F.fetch.aftersend = ()=>cs.ajaxEnd(); cs.pageTitleOrig = cs.e.pageTitle.innerText; const qs = (e)=>document.querySelector(e); const argsToArray = function(args){ return Array.prototype.slice.call(args,0); }; /** Reports an error via console.error() and as a toast message. Accepts any argument types valid for fossil.toast.error(). */ cs.reportError = function(/*msg args*/){ const args = argsToArray(arguments); console.error("chat error:",args); F.toast.error.apply(F.toast, args); }; /** Reports an error in the form of a new message in the chat feed. All arguments are appended to the message's content area using fossil.dom.append(), so may be of any type supported by that function. */ cs.reportErrorAsMessage = function f(/*msg args*/){ if(undefined === f.$msgid) f.$msgid=0; const args = argsToArray(arguments).map(function(v){ return (v instanceof Error) ? v.message : v; }); console.error("chat error:",args); const d = new Date().toISOString(), mw = new this.MessageWidget({ isError: true, xfrom: null, msgid: "error-"+(++f.$msgid), mtime: d, lmtime: d, xmsg: args }); this.injectMessageElem(mw.e.body); mw.scrollIntoView(); }; cs.getMessageElemById = function(id){ return qs('[data-msgid="'+id+'"]'); }; /** Finds the last .message-widget element and returns it or the undefined value if none are found. */ cs.fetchLastMessageElem = function(){ const msgs = document.querySelectorAll('.message-widget'); var rc; if(msgs.length){ rc = this.e.newestMessage = msgs[msgs.length-1]; } return rc; }; /** LOCALLY deletes a message element by the message ID or passing the .message-row element. Returns true if it removes an element, else false. */ cs.deleteMessageElem = function(id){ var e; if(id instanceof HTMLElement){ e = id; id = e.dataset.msgid; }else{ e = this.getMessageElemById(id); } if(e && id){ D.remove(e); if(e===this.e.newestMessage){ this.fetchLastMessageElem(); } F.toast.message("Deleted message "+id+"."); } return !!e; }; /** Toggles the given message between its parsed and plain-text representations. It requires a server round-trip to collect the plain-text form but caches it for subsequent toggles. Expects the ID of a currently-loaded message or a message-widget DOM elment from which it can extract an id. This is an aync operation the first time it's passed a given message and synchronous on subsequent calls for that message. It is a no-op if id does not resolve to a loaded message. */ cs.toggleTextMode = function(id){ var e; if(id instanceof HTMLElement){ e = id; id = e.dataset.msgid; }else{ e = this.getMessageElemById(id); } if(!e || !id) return false; else if(e.$isToggling) return; e.$isToggling = true; const content = e.querySelector('.content-target'); if(!content){ console.warn("Should not be possible: trying to toggle text", "mode of a message with no .content-target.", e); return; } if(!content.$elems){ content.$elems = [ content.firstElementChild, // parsed elem undefined // plaintext elem ]; }else if(content.$elems[1]){ // We have both content types. Simply toggle them. const child = ( content.firstElementChild===content.$elems[0] ? content.$elems[1] : content.$elems[0] ); D.clearElement(content); if(child===content.$elems[1]){ /* When showing the unformatted version, inject a copy-to-clipboard button. This is a workaround for mouse-copying from that field collecting twice as many newlines as it should (for unknown reasons). */ const cpId = 'copy-to-clipboard-'+id; /* ^^^ copy button element ID, needed for LABEL element pairing. Recall that we destroy all child elements of `content` each time we hit this block, so we can reuse that element ID on subsequent toggles. */ const btnCp = D.attr(D.addClass(D.span(),'copy-button'), 'id', cpId); F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw}); const lblCp = D.label(cpId, "Copy unformatted text"); lblCp.addEventListener('click',()=>btnCp.click(), false); D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp)); } delete e.$isToggling; D.append(content, child); return; } // We need to fetch the plain-text version... const self = this; F.fetch('chat-fetch-one',{ urlParams:{ name: id, raw: true}, responseType: 'json', onload: function(msg){ content.$elems[1] = D.append(D.pre(),msg.xmsg); content.$elems[1]._xmsgRaw = msg.xmsg/*used for copy-to-clipboard feature*/; self.toggleTextMode(e); }, aftersend:function(){ delete e.$isToggling; Chat.ajaxEnd(); } }); return true; }; /** Given a .message-row element, this function returns whethe the current user may, at least hypothetically, delete the message globally. A user may always delete a local copy of a post. The server may trump this, e.g. if the login has been cancelled after this page was loaded. */ cs.userMayDelete = function(eMsg){ return +eMsg.dataset.msgid>0 && (this.me === eMsg.dataset.xfrom || F.user.isAdmin/*will be confirmed server-side*/); }; /** Returns a new Error() object encapsulating state from the given fetch() response promise. */ cs._newResponseError = function(response){ return new Error([ "HTTP status ", response.status,": ",response.url,": ", response.statusText].join('')); }; /** Helper for reporting HTTP-level response errors via fetch(). If response.ok then response.json() is returned, else an Error is thrown. */ cs._fetchJsonOrError = function(response){ if(response.ok) return response.json(); else throw cs._newResponseError(response); }; /** Removes the given message ID from the local chat record and, if the message was posted by this user OR this user in an admin/setup, also submits it for removal on the remote. id may optionally be a DOM element, in which case it must be a .message-row element. */ cs.deleteMessage = function(id){ var e; if(id instanceof HTMLElement){ e = id; id = e.dataset.msgid; }else{ e = this.getMessageElemById(id); } if(!(e instanceof HTMLElement)) return; if(this.userMayDelete(e)){ this.ajaxStart(); F.fetch("chat-delete/" + id, { responseType: 'json', onload:(r)=>this.deleteMessageElem(r), onerror:(err)=>this.reportErrorAsMessage(err) }); }else{ this.deleteMessageElem(id); } }; document.addEventListener('visibilitychange', function(ev){ cs.pageIsActive = ('visible' === document.visibilityState); if(cs.pageIsActive){ cs.e.pageTitle.innerText = cs.pageTitleOrig; if(document.activeElement!==cs.inputElement()){ /* An attempt to resolve usability problem reported by Joe M. where the Pale Moon browser is giving input focus to the Preview button. The down-side of this is that it will deselect any text which was previously selected on this page. This also, unfortunately, places the focus at the start of the element, rather than the last cursor position (like a textarea would). */ setTimeout(()=>cs.inputFocus(), 0); } } }, true); cs.setCurrentView(cs.e.viewMessages); cs.e.activeUserList.addEventListener('click', function f(ev){ /* Filter messages on a user clicked in activeUserList */ ev.stopPropagation(); ev.preventDefault(); let eUser = ev.target; while(eUser!==this && !eUser.classList.contains('chat-user')){ eUser = eUser.parentNode; } if(eUser==this || !eUser) return false; const uname = eUser.dataset.uname; let eLast; cs.setCurrentView(cs.e.viewMessages); if(eUser.classList.contains('selected')){ /* If curently selected, toggle filter off */ eUser.classList.remove('selected'); cs.setUserFilter(false); delete f.$eSelected; }else{ if(f.$eSelected) f.$eSelected.classList.remove('selected'); f.$eSelected = eUser; eUser.classList.add('selected'); cs.setUserFilter(uname); } return false; }, false); return cs; })()/*Chat initialization*/; /** Returns the first .message-widget element in DOM element e's lineage. */ const findMessageWidgetParent = function(e){ while( e && !e.classList.contains('message-widget')){ e = e.parentNode; } return e; }; /** Custom widget type for rendering messages (one message per instance). These are modelled after FIELDSET elements but we don't use FIELDSET because of cross-browser inconsistencies in features of the FIELDSET/LEGEND combination, e.g. inability to align legends via CSS in Firefox and clicking-related deficiencies in Safari. */ Chat.MessageWidget = (function(){ /** Constructor. If passed an argument, it is passed to this.setMessage() after initialization. */ const cf = function(){ this.e = { body: D.addClass(D.div(), 'message-widget'), tab: D.addClass(D.div(), 'message-widget-tab'), content: D.addClass(D.div(), 'message-widget-content') }; D.append(this.e.body, this.e.tab, this.e.content); this.e.tab.setAttribute('role', 'button'); if(arguments.length){ this.setMessage(arguments[0]); } }; /* Left-zero-pad a number to at least 2 digits */ const dowMap = { /* Map of Date.getDay() values to weekday names. */ 0: "Sunday", 1: "Monday", 2: "Tuesday", 3: "Wednesday", 4: "Thursday", 5: "Friday", 6: "Saturday" }; /* Given a Date, returns the timestamp string used in the "tab" part of message widgets. */ const theTime = function(d){ return [ //d.getFullYear(),'-',pad2(d.getMonth()+1/*sigh*/), //'-',pad2(d.getDate()), ' ', d.getHours(),":", (d.getMinutes()+100).toString().slice(1,3), ' ', dowMap[d.getDay()] ].join(''); }; /** Returns true if this page believes it can embed a view of the file wrapped by the given message object, else returns false. */ const canEmbedFile = function f(msg){ if(!f.$rx){ f.$rx = /\.((html?)|(txt)|(md)|(wiki)|(pikchr))$/i; f.$specificTypes = [ 'text/plain', 'text/html', 'text/x-markdown', /* Firefox sends text/markdown when uploading .md files */ 'text/markdown', 'text/x-pikchr', 'text/x-fossil-wiki' // add more as we discover which ones Firefox won't // force the user to try to download. ]; } if(msg.fmime){ if(msg.fmime.startsWith("image/") || f.$specificTypes.indexOf(msg.fmime)>=0){ return true; } } return (msg.fname && f.$rx.test(msg.fname)); }; /** Returns true if the given message object "should" be embedded in fossil-rendered form instead of raw content form. This is only intended to be passed message objects for which canEmbedFile() returns true. */ const shouldWikiRenderEmbed = function f(msg){ if(!f.$rx){ f.$rx = /\.((md)|(wiki)|(pikchr))$/i; f.$specificTypes = [ 'text/x-markdown', 'text/markdown' /* Firefox-uploaded md files */, 'text/x-pikchr', 'text/x-fossil-wiki' // add more as we discover which ones Firefox won't // force the user to try to download. ]; } if(msg.fmime){ if(f.$specificTypes.indexOf(msg.fmime)>=0) return true; } return msg.fname && f.$rx.test(msg.fname); }; const adjustIFrameSize = function(msgObj){ const iframe = msgObj.e.iframe; const body = iframe.contentWindow.document.querySelector('body'); if(body && !body.style.fontSize){ /** _Attempt_ to force the iframe to inherit the message's text size if the body has no explicit size set. On desktop systems the size is apparently being inherited in that case, but on mobile not. */ body.style.fontSize = window.getComputedStyle(msgObj.e.content); } if('' === iframe.style.maxHeight){ /* Resize iframe height to fit the content. Workaround: if we adjust the iframe height while it's hidden then its height is 0, so we must briefly unhide it. */ const isHidden = iframe.classList.contains('hidden'); if(isHidden) D.removeClass(iframe, 'hidden'); iframe.style.maxHeight = iframe.style.height = iframe.contentWindow.document.documentElement.scrollHeight + 'px'; if(isHidden) D.addClass(iframe, 'hidden'); } }; cf.prototype = { scrollIntoView: function(){ this.e.content.scrollIntoView(); }, setMessage: function(m){ const ds = this.e.body.dataset; ds.timestamp = m.mtime; ds.lmtime = m.lmtime; ds.msgid = m.msgid; ds.xfrom = m.xfrom || ''; if(m.xfrom === Chat.me){ D.addClass(this.e.body, 'mine'); } if(m.uclr){ this.e.content.style.backgroundColor = m.uclr; this.e.tab.style.backgroundColor = m.uclr; } const d = new Date(m.mtime); D.clearElement(this.e.tab); var contentTarget = this.e.content; var eXFrom /* element holding xfrom name */; if(m.xfrom){ eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom); const wrapper = D.append( D.span(), eXFrom, D.text(" #",(m.msgid||'???'),' @ ',theTime(d))) D.append(this.e.tab, wrapper); }else{/*notification*/ D.addClass(this.e.body, 'notification'); if(m.isError){ D.addClass([contentTarget, this.e.tab], 'error'); } D.append( this.e.tab, D.append(D.code(), 'notification @ ',theTime(d)) ); } if( m.xfrom && m.fsize>0 ){ if( m.fmime && m.fmime.startsWith("image/") && Chat.settings.getBool('images-inline',true) ){ const extension = m.fname.split('.').pop(); contentTarget.appendChild(D.img("chat-download/" + m.msgid +( extension ? ('.'+extension) : ''/*So that IMG tag mimetype guessing works*/ ))); ds.hasImage = 1; }else{ // Add a download link. const downloadUri = window.fossil.rootPath+ 'chat-download/' + m.msgid+'/'+encodeURIComponent(m.fname); const w = D.addClass(D.div(), 'attachment-link'); const a = D.a(downloadUri, // ^^^ add m.fname to URL to cause downloaded file to have that name. "(" + m.fname + " " + m.fsize + " bytes)" ) D.attr(a,'target','_blank'); D.append(w, a); if(canEmbedFile(m)){ /* Add an option to embed HTML attachments in an iframe. The primary use case is attached diffs. */ const shouldWikiRender = shouldWikiRenderEmbed(m); const downloadArgs = shouldWikiRender ? '?render' : ''; D.addClass(contentTarget, 'wide'); const embedTarget = this.e.content; const self = this; const btnEmbed = D.attr(D.checkbox("1", false), 'id', 'embed-'+ds.msgid); const btnLabel = D.label(btnEmbed, shouldWikiRender ? "Embed (fossil-rendered)" : "Embed"); /* Maintenance reminder: do not disable the toggle button while the content is loading because that will cause it to get stuck in disabled mode if the browser decides that loading the content should prompt the user to download it, rather than embed it in the iframe. */ btnEmbed.addEventListener('change',function(){ if(self.e.iframe){ if(btnEmbed.checked){ D.removeClass(self.e.iframe, 'hidden'); if(self.e.$iframeLoaded) adjustIFrameSize(self); } else D.addClass(self.e.iframe, 'hidden'); return; } const iframe = self.e.iframe = document.createElement('iframe'); D.append(embedTarget, iframe); iframe.addEventListener('load', function(){ self.e.$iframeLoaded = true; adjustIFrameSize(self); }); iframe.setAttribute('src', downloadUri + downloadArgs); }); D.append(w, btnEmbed, btnLabel); } contentTarget.appendChild(w); } } if(m.xmsg){ if(m.fsize>0){ /* We have file/image content, so need another element for the message text. */ contentTarget = D.div(); D.append(this.e.content, contentTarget); } D.addClass(contentTarget, 'content-target' /*target element for the 'toggle text mode' feature*/); // The m.xmsg text comes from the same server as this script and // is guaranteed by that server to be "safe" HTML - safe in the // sense that it is not possible for a malefactor to inject HTML // or javascript or CSS. The m.xmsg content might contain // hyperlinks, but otherwise it will be markup-free. See the // chat_format_to_html() routine in the server for details. // // Hence, even though innerHTML is normally frowned upon, it is // perfectly safe to use in this context. if(m.xmsg && 'string' !== typeof m.xmsg){ // Used by Chat.reportErrorAsMessage() D.append(contentTarget, m.xmsg); }else{ contentTarget.innerHTML = m.xmsg; contentTarget.querySelectorAll('a').forEach(addAnchorTargetBlank); if(F.pikchr){ F.pikchr.addSrcView(contentTarget.querySelectorAll('svg.pikchr')); } } } //console.debug("tab",this.e.tab); //console.debug("this.e.tab.firstElementChild",this.e.tab.firstElementChild); this.e.tab.firstElementChild.addEventListener('click', this._handleLegendClicked, false); /*if(eXFrom){ eXFrom.addEventListener('click', ()=>this.e.tab.click(), false); }*/ return this; }, /* Event handler for clicking .message-user elements to show their timestamps and a set of actions. */ _handleLegendClicked: function f(ev){ if(!f.popup){ /* "Popup" widget */ f.popup = { e: D.addClass(D.div(), 'chat-message-popup'), refresh:function(){ const eMsg = this.$eMsg/*.message-widget element*/; if(!eMsg) return; D.clearElement(this.e); const d = new Date(eMsg.dataset.timestamp); if(d.getMinutes().toString()!=="NaN"){ // Date works, render informative timestamps const xfrom = eMsg.dataset.xfrom || 'server'; D.append(this.e, D.append(D.span(), localTimeString(d)," ",Chat.me," time"), D.append(D.span(), iso8601ish(d))); if(eMsg.dataset.lmtime && xfrom!==Chat.me){ D.append(this.e, D.append(D.span(), localTime8601( new Date(eMsg.dataset.lmtime) ).replace('T',' ')," ",xfrom," time")); } }else{ /* This might not be necessary any more: it was initially caused by Safari being stricter than other browsers on its time string input, and that has since been resolved by emiting a stricter format. */ // Date doesn't work, so dumb it down... D.append(this.e, D.append(D.span(), eMsg.dataset.timestamp," zulu")); } const toolbar = D.addClass(D.div(), 'toolbar'); D.append(this.e, toolbar); const btnDeleteLocal = D.button("Delete locally"); D.append(toolbar, btnDeleteLocal); const self = this; btnDeleteLocal.addEventListener('click', function(){ self.hide(); Chat.deleteMessageElem(eMsg); }); if(Chat.userMayDelete(eMsg)){ const btnDeleteGlobal = D.button("Delete globally"); D.append(toolbar, btnDeleteGlobal); F.confirmer(btnDeleteGlobal,{ pinSize: true, ticks: F.config.confirmerButtonTicks, confirmText: "Confirm delete?", onconfirm:function(){ self.hide(); Chat.deleteMessage(eMsg); } }); } const toolbar3 = D.addClass(D.div(), 'toolbar'); D.append(this.e, toolbar3); D.append(toolbar3, D.button( "Locally remove all previous messages", function(){ self.hide(); Chat.mnMsg = +eMsg.dataset.msgid; var e = eMsg.previousElementSibling; while(e && e.classList.contains('message-widget')){ const n = e.previousElementSibling; D.remove(e); e = n; } eMsg.scrollIntoView(); } )); const toolbar2 = D.addClass(D.div(), 'toolbar'); D.append(this.e, toolbar2); if(eMsg.querySelector('.content-target')){ /* ^^^ messages with only an embedded image have no .content-target area. */ D.append(toolbar2, D.button( "Toggle text mode", function(){ self.hide(); Chat.toggleTextMode(eMsg); })); } if(eMsg.dataset.xfrom){ /* Add a link to the /timeline filtered on this user. */ const timelineLink = D.attr( D.a(F.repoUrl('timeline',{ u: eMsg.dataset.xfrom, y: 'a' }), "User's Timeline"), 'target', '_blank' ); D.append(toolbar2, timelineLink); if(Chat.filterState.activeUser && Chat.filterState.match(eMsg.dataset.xfrom)){ /* Add a button to clear user filter and jump to this message in its original context. */ D.append( this.e, D.append( D.addClass(D.div(), 'toolbar'), D.button( "Message in context", function(){ self.hide(); Chat.setUserFilter(false); eMsg.scrollIntoView(false); Chat.animate( eMsg.firstElementChild, 'anim-flip-h' //eMsg.firstElementChild, 'anim-flip-v' //eMsg.childNodes, 'anim-rotate-360' //eMsg.childNodes, 'anim-flip-v' //eMsg, 'anim-flip-v' ); }) ) ); }/*jump-to button*/ } const tab = eMsg.querySelector('.message-widget-tab'); D.append(tab, this.e); D.removeClass(this.e, 'hidden'); Chat.animate(this.e, 'anim-fade-in-fast'); }/*refresh()*/, hide: function(){ delete this.$eMsg; D.addClass(this.e, 'hidden'); D.clearElement(this.e); }, show: function(tgtMsg){ if(tgtMsg === this.$eMsg){ this.hide(); return; } this.$eMsg = tgtMsg; this.refresh(); } }/*f.popup*/; }/*end static init*/ const theMsg = findMessageWidgetParent(ev.target); if(theMsg) f.popup.show(theMsg); }/*_handleLegendClicked()*/ }; return cf; })()/*MessageWidget*/; const BlobXferState = (function(){ /* State for paste and drag/drop */ const bxs = { dropDetails: document.querySelector('#chat-drop-details'), blob: undefined, clear: function(){ this.blob = undefined; D.clearElement(this.dropDetails); Chat.e.inputFile.value = ""; } }; /** Updates the paste/drop zone with details of the pasted/dropped data. The argument must be a Blob or Blob-like object (File) or it can be falsy to reset/clear that state.*/ const updateDropZoneContent = bxs.updateDropZoneContent = function(blob){ //console.debug("updateDropZoneContent()",blob); const dd = bxs.dropDetails; bxs.blob = blob; D.clearElement(dd); if(!blob){ Chat.e.inputFile.value = ''; return; } D.append(dd, "Attached: ", blob.name, D.br(), "Size: ",blob.size); const btn = D.button("Cancel"); D.append(dd, D.br(), btn); btn.addEventListener('click', ()=>updateDropZoneContent(), false); if(blob.type && (blob.type.startsWith("image/") || blob.type==='BITMAP')){ const img = D.img(); D.append(dd, D.br(), img); const reader = new FileReader(); reader.onload = (e)=>img.setAttribute('src', e.target.result); reader.readAsDataURL(blob); } }; Chat.e.inputFile.addEventListener('change', function(ev){ updateDropZoneContent(this.files && this.files[0] ? this.files[0] : undefined) }); /* Handle image paste from clipboard. TODO: figure out how we can paste non-image binary data as if it had been selected via the file selection element. */ const pasteListener = function(event){ const items = event.clipboardData.items, item = items[0]; //console.debug("paste event",event.target,item,event); //console.debug("paste event item",item); if(item && item.type && ('file'===item.kind || 'BITMAP'===item.type)){ updateDropZoneContent(false/*clear prev state*/); updateDropZoneContent(item.getAsFile()); event.stopPropagation(); event.preventDefault(true); return false; } /* else continue propagating */ }; document.addEventListener('paste', pasteListener, true); if(window.Selection && window.Range && !Chat.$browserHasPlaintextOnly){ /* Acrobatics to keep *some* installations of Firefox from pasting formatting into contenteditable fields. This also works on Chrome, but chrome has the contenteditable=plaintext-only property which does this for us. */ Chat.e.inputX.addEventListener( 'paste', function(ev){ if (ev.clipboardData && ev.clipboardData.getData) { const pastedText = ev.clipboardData.getData('text/plain'); const selection = window.getSelection(); if (!selection.rangeCount) return false; selection.deleteFromDocument(/*remove selected content*/); selection.getRangeAt(0).insertNode(document.createTextNode(pastedText)); selection.collapseToEnd(/*deselect pasted text and set cursor at the end*/); ev.preventDefault(); return false; } }, false); } const noDragDropEvents = function(ev){ /* contenteditable tries to do its own thing with dropped data, which is not compatible with how we use it, so... */ ev.dataTransfer.effectAllowed = 'none'; ev.dataTransfer.dropEffect = 'none'; ev.preventDefault(); ev.stopPropagation(); return false; }; ['drop','dragenter','dragleave','dragend'].forEach( (k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false) ); return bxs; })()/*drag/drop/paste*/; const tzOffsetToString = function(off){ const hours = Math.round(off/60), min = Math.round(off % 30); return ''+(hours + (min ? '.5' : '')); }; const localTime8601 = function(d){ return [ d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()), 'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds()) ].join(''); }; /** Called by Chat.submitMessage() when message sending failed. Injects a fake message containing the content and attachment of the failed message and gives the user buttons to discard it or edit and retry. */ const recoverFailedMessage = function(state){ const w = D.addClass(D.div(), 'failed-message'); D.append(w, D.append( D.span(),"This message was not successfully sent to the server:" )); if(state.msg){ const ta = D.textarea(); ta.value = state.msg; D.append(w,ta); } if(state.blob){ D.append(w,D.append(D.span(),"Attachment: ",(state.blob.name||"unnamed"))); //console.debug("blob = ",state.blob); } const buttons = D.addClass(D.div(), 'buttons'); D.append(w, buttons); D.append(buttons, D.button("Discard message?", function(){ const theMsg = findMessageWidgetParent(w); if(theMsg) Chat.deleteMessageElem(theMsg); })); D.append(buttons, D.button("Edit message and try again?", function(){ if(state.msg) Chat.inputValue(state.msg); if(state.blob) BlobXferState.updateDropZoneContent(state.blob); const theMsg = findMessageWidgetParent(w); if(theMsg) Chat.deleteMessageElem(theMsg); })); Chat.reportErrorAsMessage(w); }; /** Submits the contents of the message input field (if not empty) and/or the file attachment field to the server. If both are empty, this is a no-op. */ Chat.submitMessage = function f(){ if(!f.spaces){ f.spaces = /\s+$/; f.markdownContinuation = /\\\s+$/; f.spaces2 = /\s{3,}$/; } this.setCurrentView(this.e.viewMessages); const fd = new FormData(); const fallback = {msg: this.inputValue()}; var msg = fallback.msg; if(msg && (msg.indexOf('\n')>0 || f.spaces.test(msg))){ /* Cosmetic: trim most whitespace from the ends of lines to try to keep copy/paste from terminals, especially wide ones, from forcing a horizontal scrollbar on all clients. This breaks markdown's use of blackslash-space-space for paragraph continuation, but *not* doing this affects all clients every time someone pastes in console copy/paste from an affected platform. We seem to have narrowed to the console pasting problem to users of tmux together with certain apps (vim, at a minimum). Most consoles don't behave that way. We retain two trailing spaces so that markdown conventions which use end-of-line spacing aren't broken by this stripping. */ const xmsg = msg.split('\n'); xmsg.forEach(function(line,ndx){ if(!f.markdownContinuation.test(line)){ xmsg[ndx] = line.replace(f.spaces2, ' '); } }); msg = xmsg.join('\n'); } if(msg) fd.set('msg',msg); const file = BlobXferState.blob || this.e.inputFile.files[0]; if(file) fd.set("file", file); if( !msg && !file ) return; fallback.blob = file; const self = this; fd.set("lmtime", localTime8601(new Date())); F.fetch("chat-send",{ payload: fd, responseType: 'text', onerror:function(err){ self.reportErrorAsMessage(err); recoverFailedMessage(fallback); }, onload:function(txt){ if(!txt) return/*success response*/; try{ const json = JSON.parse(txt); self.newContent({msgs:[json]}); }catch(e){ self.reportError(e); } recoverFailedMessage(fallback); } }); BlobXferState.clear(); Chat.inputValue("").inputFocus(); }; const inputWidgetKeydown = function f(ev){ if(!f.$toggleCtrl){ f.$toggleCtrl = function(currentMode){ currentMode = !currentMode; Chat.settings.set('edit-ctrl-send', currentMode); }; f.$toggleCompact = function(currentMode){ currentMode = !currentMode; Chat.settings.set('edit-compact-mode', currentMode); }; } if(13 !== ev.keyCode) return; const text = Chat.inputValue().trim(); const ctrlMode = Chat.settings.getBool('edit-ctrl-send', false); //console.debug("Enter key event:", ctrlMode, ev.ctrlKey, ev.shiftKey, ev); if(ev.shiftKey){ const compactMode = Chat.settings.getBool('edit-compact-mode', false); ev.preventDefault(); ev.stopPropagation(); /* Shift-enter will run preview mode UNLESS preview mode is active AND the input field is empty, in which case it will switch back to message view. */ if(Chat.e.currentView===Chat.e.viewPreview && !text){ Chat.setCurrentView(Chat.e.viewMessages); }else if(!text){ f.$toggleCompact(compactMode); }else if(Chat.settings.getBool('edit-shift-enter-preview', true)){ Chat.e.btnPreview.click(); } return false; } if(ev.ctrlKey && !text && !BlobXferState.blob){ /* Ctrl-enter on empty input field(s) toggles Enter/Ctrl-enter mode */ ev.preventDefault(); ev.stopPropagation(); f.$toggleCtrl(ctrlMode); return false; } if(!ctrlMode && ev.ctrlKey && text){ //console.debug("!ctrlMode && ev.ctrlKey && text."); /* Ctrl-enter in Enter-sends mode SHOULD, with this logic add a newline, but that is not happening, for unknown reasons (possibly related to this element being a contenteditable DIV instead of a textarea). Forcibly appending a newline do the input area does not work, also for unknown reasons, and would only be suitable when we're at the end of the input. Strangely, this approach DOES work for shift-enter, but we need shift-enter as a hotkey for preview mode. */ //return; // return here "should" cause newline to be added, but that doesn't work } if((!ctrlMode && !ev.ctrlKey) || (ev.ctrlKey/* && ctrlMode*/)){ /* Ship it! */ ev.preventDefault(); ev.stopPropagation(); Chat.submitMessage(); return false; } }; Chat.e.inputFields.forEach( (e)=>e.addEventListener('keydown', inputWidgetKeydown, false) ); Chat.e.btnSubmit.addEventListener('click',(e)=>{ e.preventDefault(); Chat.submitMessage(); return false; }); Chat.e.btnAttach.addEventListener( 'click', ()=>Chat.e.inputFile.click(), false); (function(){/*Set up #chat-button-settings and related bits */ if(window.innerWidth<window.innerHeight){ // Must be set up before config view is... /* Alignment of 'my' messages: right alignment is conventional for mobile chat apps but can be difficult to read in wide windows (desktop/tablet landscape mode), so we default to a layout based on the apparent "orientation" of the window: tall vs wide. Can be toggled via settings. */ document.body.classList.add('my-messages-right'); } const settingsButton = document.querySelector('#chat-button-settings'); const optionsMenu = E1('#chat-config-options'); const cbToggle = function(ev){ ev.preventDefault(); ev.stopPropagation(); Chat.setCurrentView(Chat.e.currentView===Chat.e.viewConfig ? Chat.e.viewMessages : Chat.e.viewConfig); return false; }; D.attr(settingsButton, 'role', 'button').addEventListener('click', cbToggle, false); Chat.e.viewConfig.querySelector('button').addEventListener('click', cbToggle, false); /** Internal acrobatics to allow certain settings toggles to access related toggles. */ const namedOptions = { activeUsers:{ label: "Show active users list", hint: "List users who have messages in the currently-loaded chat history.", boolValue: 'active-user-list' } }; if(1){ /* Per user request, toggle the list of users on and off if the legend element is tapped. */ const optAu = namedOptions.activeUsers; optAu.theLegend = Chat.e.activeUserListWrapper.firstElementChild/*LEGEND*/; optAu.theList = optAu.theLegend.nextElementSibling/*user list container*/; optAu.theLegend.addEventListener('click',function(){ D.toggleClass(Chat.e.activeUserListWrapper, 'collapsed'); if(!Chat.e.activeUserListWrapper.classList.contains('collapsed')){ Chat.animate(optAu.theList,'anim-flip-v'); } }, false); }/*namedOptions.activeUsers additional setup*/ /* Settings menu entries... the most frequently-needed ones "should" (arguably) be closer to the start of this list. */ /** Settings ops structure: label: string for the UI boolValue: string (name of Chat.settings setting) or a function which returns true or false. If it is a string, it gets replaced by a function which returns Chat.settings.getBool(thatString) and the string gets assigned to the persistentSetting property of this object. select: SELECT element (instead of boolValue) callback: optional handler to call after setting is modified. Its "this" is the options object. If this object has a boolValue string or a persistentSetting property, the argument passed to the callback is a settings object in the form {key:K, value:V}. If this object does not have boolValue string or persistentSetting then the callback is passed an event object in response to the config option's UI widget being activated, normally a 'change' event. children: [array of settings objects]. These get listed under this element and indented slightly for visual grouping. Only one level of indention is supported. Elements which only have a label and maybe a hint and children can be used as headings. If a setting has a boolValue set, that gets rendered as a checkbox which toggles the given persistent setting (if boolValue is a string) AND listens for changes to that setting fired via Chat.settings.set() so that the checkbox can stay in sync with external changes to that setting. Various Chat UI elements stay in sync with the config UI via those settings events. The checkbox element gets added to the options object so that the callback() can reference it via this.checkbox. */ const settingsOps = [{ label: "Chat Configuration Options", hint: F.storage.isTransient() ? "Local store is unavailable. These settings are transient." : ["Most of these settings are persistent via ", F.storage.storageImplName(), ": ", F.storage.storageHelpDescription()].join('') },{ label: "Editing Options...", children:[{ label: "Chat-only mode", hint: "Toggle the page between normal fossil view and chat-only view.", boolValue: 'chat-only-mode' },{ label: "Ctrl-enter to Send", hint: [ "When on, only Ctrl-Enter will send messages and Enter adds ", "blank lines. When off, both Enter and Ctrl-Enter send. ", "When the input field has focus and is empty ", "then Ctrl-Enter toggles this setting." ].join(''), boolValue: 'edit-ctrl-send' },{ label: "Compact mode", hint: [ "Toggle between a space-saving or more spacious writing area. ", "When the input field has focus, is empty, and preview mode ", "is NOT active then Shift-Enter toggles this setting."].join(''), boolValue: 'edit-compact-mode' },{ label: "Use 'contenteditable' editing mode", boolValue: 'edit-widget-x', hint: [ "When enabled, chat input uses a so-called 'contenteditable' ", "field. Though generally more comfortable and modern than ", "plain-text input fields, browser-specific quirks and bugs ", "may lead to frustration. Ideal for mobile devices." ].join('') },{ label: "Shift-enter to preview", hint: ["Use shift-enter to preview being-edited messages. ", "This is normally desirable but some software-mode ", "keyboards misinteract with this, in which cases it can be ", "disabled."], boolValue: 'edit-shift-enter-preview' }] },{ label: "Appearance Options...", children:[{ label: "Left-align my posts", hint: "Default alignment of your own messages is selected " + "based on the window width/height ratio.", boolValue: ()=>!document.body.classList.contains('my-messages-right'), callback: function f(){ document.body.classList[ this.checkbox.checked ? 'remove' : 'add' ]('my-messages-right'); } },{ label: "Monospace message font", hint: "Use monospace font for message and input text.", boolValue: 'monospace-messages', callback: function(setting){ document.body.classList[ setting.value ? 'add' : 'remove' ]('monospace-messages'); } },{ label: "Show images inline", hint: "When enabled, attached images are shown inline, "+ "else they appear as a download link.", boolValue: 'images-inline' }] }]; /** Set up selection list of notification sounds. */ if(1){ const selectSound = D.select(); D.option(selectSound, "", "(no audio)"); const firstSoundIndex = selectSound.options.length; F.config.chat.alerts.forEach((a)=>D.option(selectSound, a)); if(true===Chat.settings.getBool('audible-alert')){ /* This setting used to be a plain bool. If we encounter such a setting, take the first sound in the list. */ selectSound.selectedIndex = firstSoundIndex; }else{ selectSound.value = Chat.settings.get('audible-alert','<none>'); if(selectSound.selectedIndex<0){ /* Missing file - removed after this setting was applied. Fall back to the first sound in the list. */ selectSound.selectedIndex = firstSoundIndex; } } Chat.setNewMessageSound(selectSound.value); settingsOps.push({ label: "Sound Options...", hint: "How to enable audio playback is browser-specific!", children:[{ hint: "Audio alert", select: selectSound, callback: function(ev){ const v = ev.target.value; Chat.setNewMessageSound(v); F.toast.message("Audio notifications "+(v ? "enabled" : "disabled")+"."); if(v) setTimeout(()=>Chat.playNewMessageSound(), 0); } },{ label: "Play notification for your own messages", hint: "When enabled, the audio notification will be played for all messages, "+ "including your own. When disabled only messages from other users "+ "will trigger a notification.", boolValue: 'alert-own-messages' }] }); }/*audio notification config*/ settingsOps.push({ label: "Active User List...", hint: [ "/chat cannot track active connections, but it can tell ", "you who has posted recently..."].join(''), children:[ namedOptions.activeUsers,{ label: "Timestamps in active users list", indent: true, hint: "Show most recent message timestamps in the active user list.", boolValue: 'active-user-list-timestamps' } ] }); /** Build UI for config options... */ settingsOps.forEach(function f(op,indentOrIndex){ const menuEntry = D.addClass(D.div(), 'menu-entry'); if(true===indentOrIndex) D.addClass(menuEntry, 'child'); const label = op.label ? D.append(D.label(),op.label) : undefined; const labelWrapper = D.addClass(D.div(), 'label-wrapper'); var hint; if(op.hint){ hint = D.append(D.addClass(D.label(),'hint'),op.hint); } if(op.hasOwnProperty('select')){ const col0 = D.addClass(D.span(/*empty, but for spacing*/), 'toggle-wrapper'); D.append(menuEntry, labelWrapper, col0); D.append(labelWrapper, op.select); if(hint) D.append(labelWrapper, hint); if(label) D.append(label); if(op.callback){ op.select.addEventListener('change', (ev)=>op.callback(ev), false); } }else if(op.hasOwnProperty('boolValue')){ if(undefined === f.$id) f.$id = 0; ++f.$id; if('string' ===typeof op.boolValue){ const key = op.boolValue; op.boolValue = ()=>Chat.settings.getBool(key); op.persistentSetting = key; } const check = op.checkbox = D.attr(D.checkbox(1, op.boolValue()), 'aria-label', op.label); const id = 'cfgopt'+f.$id; const col0 = D.addClass(D.span(), 'toggle-wrapper'); check.checked = op.boolValue(); op.checkbox = check; D.attr(check, 'id', id); if(hint) D.attr(hint, 'for', id); D.append(menuEntry, labelWrapper, col0); D.append(col0, check); if(label){ D.attr(label, 'for', id); D.append(labelWrapper, label); } if(hint) D.append(labelWrapper, hint); }else{ if(op.callback){ menuEntry.addEventListener('click', (ev)=>op.callback(ev)); } D.append(menuEntry, labelWrapper); if(label) D.append(labelWrapper, label); if(hint) D.append(labelWrapper, hint); } D.append(optionsMenu, menuEntry); if(op.persistentSetting){ Chat.settings.addListener( op.persistentSetting, function(setting){ if(op.checkbox) op.checkbox.checked = !!setting.value; else if(op.select) op.select.value = setting.value; if(op.callback) op.callback(setting); } ); if(op.checkbox){ op.checkbox.addEventListener( 'change', function(){ Chat.settings.set(op.persistentSetting, op.checkbox.checked) }, false); } }else if(op.callback && op.checkbox){ op.checkbox.addEventListener('change', (ev)=>op.callback(ev), false); } if(op.children){ D.addClass(menuEntry, 'parent'); op.children.forEach((x)=>f(x,true)); } }); })()/*#chat-button-settings setup*/; (function(){ /* Install default settings... must come after chat-button-settings setup so that the listeners which that installs are notified via the properties getting initialized here. */ Chat.settings.addListener('monospace-messages',function(s){ document.body.classList[s.value ? 'add' : 'remove']('monospace-messages'); }) Chat.settings.addListener('active-user-list',function(s){ Chat.showActiveUserList(s.value); }); Chat.settings.addListener('active-user-list-timestamps',function(s){ Chat.showActiveUserTimestamps(s.value); }); Chat.settings.addListener('chat-only-mode',function(s){ Chat.chatOnlyMode(s.value); }); Chat.settings.addListener('edit-widget-x',function(s){ let eSelected; if(s.value){ if(Chat.e.inputX===Chat.inputElement()) return; eSelected = Chat.e.inputX; }else{ eSelected = Chat.settings.getBool('edit-compact-mode') ? Chat.e.input1 : Chat.e.inputM; } const v = Chat.inputValue(); Chat.inputValue(''); Chat.e.inputFields.forEach(function(e,ndx){ if(eSelected===e){ Chat.e.inputFields.$currentIndex = ndx; D.removeClass(e, 'hidden'); } else D.addClass(e,'hidden'); }); Chat.inputValue(v); eSelected.focus(); }); Chat.settings.addListener('edit-compact-mode',function(s){ if(Chat.e.inputX!==Chat.inputElement()){ /* Text field/textarea mode: swap them if needed. Compact mode of inputX is toggled via CSS. */ const a = s.value ? [Chat.e.input1, Chat.e.inputM, 0] : [Chat.e.inputM, Chat.e.input1, 1]; const v = Chat.inputValue(); Chat.inputValue(''); Chat.e.inputFields.$currentIndex = a[2]; Chat.inputValue(v); D.removeClass(a[0], 'hidden'); D.addClass(a[1], 'hidden'); } Chat.e.inputElementWrapper.classList[ s.value ? 'add' : 'remove' ]('compact'); Chat.e.inputFields[Chat.e.inputFields.$currentIndex].focus(); }); Chat.settings.addListener('edit-ctrl-send',function(s){ const label = (s.value ? "Ctrl-" : "")+"Enter submits messages."; Chat.e.inputFields.forEach((e)=>{ const v = e.dataset.placeholder0 + " " +label; if(e.isContentEditable) e.dataset.placeholder = v; else D.attr(e,'placeholder',v); }); Chat.e.btnSubmit.title = label; }); const valueKludges = { /* Convert certain string-format values to other types... */ "false": false, "true": true }; Object.keys(Chat.settings.defaults).forEach(function(k){ var v = Chat.settings.get(k,Chat); if(Chat===v) v = Chat.settings.defaults[k]; if(valueKludges.hasOwnProperty(v)) v = valueKludges[v]; Chat.settings.set(k,v) /* fires event listeners so that the Config area checkboxes get in sync */; }); })(); (function(){/*set up message preview*/ const btnPreview = Chat.e.btnPreview; Chat.setPreviewText = function(t){ this.setCurrentView(this.e.viewPreview); this.e.previewContent.innerHTML = t; this.e.viewPreview.querySelectorAll('a').forEach(addAnchorTargetBlank); this.inputFocus(); }; Chat.e.viewPreview.querySelector('#chat-preview-close'). addEventListener('click', ()=>Chat.setCurrentView(Chat.e.viewMessages), false); let previewPending = false; const elemsToEnable = [btnPreview, Chat.e.btnSubmit, Chat.e.inputFields]; const submit = function(ev){ ev.preventDefault(); ev.stopPropagation(); if(previewPending) return false; const txt = Chat.inputValue(); if(!txt){ Chat.setPreviewText(''); previewPending = false; return false; } const fd = new FormData(); fd.append('content', txt); fd.append('filename','chat.md' /*filename needed for mimetype determination*/); fd.append('render_mode',F.page.previewModes.wiki); F.fetch('ajax/preview-text',{ payload: fd, onload: function(html){ Chat.setPreviewText(html); F.pikchr.addSrcView(Chat.e.viewPreview.querySelectorAll('svg.pikchr')); }, onerror: function(e){ F.fetch.onerror(e); Chat.setPreviewText("ERROR: "+( e.message || 'Unknown error fetching preview!' )); }, beforesend: function(){ D.disable(elemsToEnable); Chat.ajaxStart(); previewPending = true; Chat.setPreviewText("Loading preview..."); }, aftersend:function(){ previewPending = false; Chat.ajaxEnd(); D.enable(elemsToEnable); } }); return false; }; btnPreview.addEventListener('click', submit, false); })()/*message preview setup*/; /** Callback for poll() to inject new content into the page. jx == the response from /chat-poll. If atEnd is true, the message is appended to the end of the chat list (for loading older messages), else the beginning (the default). */ const newcontent = function f(jx,atEnd){ if(!f.processPost){ /** Processes chat message m, placing it either the start (if atEnd is falsy) or end (if atEnd is truthy) of the chat history. atEnd should only be true when loading older messages. */ f.processPost = function(m,atEnd){ ++Chat.totalMessageCount; if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid; if( !Chat.mnMsg || m.msgid<Chat.mnMsg) Chat.mnMsg = m.msgid; if(m.xfrom && m.mtime){ const d = new Date(m.mtime); const uls = Chat.usersLastSeen[m.xfrom]; if(!uls || uls<d){ d.$uColor = m.uclr; Chat.usersLastSeen[m.xfrom] = d; } } if( m.mdel ){ /* A record deletion notice. */ Chat.deleteMessageElem(m.mdel); return; } if(!Chat._isBatchLoading && (Chat.me!==m.xfrom || Chat.settings.getBool('alert-own-messages'))){ Chat.playNewMessageSound(); } const row = new Chat.MessageWidget(m); Chat.injectMessageElem(row.e.body,atEnd); if(m.isError){ Chat._gotServerError = m; } }/*processPost()*/; }/*end static init*/ jx.msgs.forEach((m)=>f.processPost(m,atEnd)); Chat.updateActiveUserList(); if('visible'===document.visibilityState){ if(Chat.changesSincePageHidden){ Chat.changesSincePageHidden = 0; Chat.e.pageTitle.innerText = Chat.pageTitleOrig; } }else{ Chat.changesSincePageHidden += jx.msgs.length; if(jx.msgs.length){ Chat.e.pageTitle.innerText = '[*] '+Chat.pageTitleOrig; } } }/*newcontent()*/; Chat.newContent = newcontent; (function(){ /** Add toolbar for loading older messages. We use a FIELDSET here because a fieldset is the only parent element type which can automatically enable/disable its children by enabling/disabling the parent element. */ const loadLegend = D.legend("Load..."); const toolbar = Chat.e.loadOlderToolbar = D.attr( D.fieldset(loadLegend), "id", "load-msg-toolbar" ); Chat.disableDuringAjax.push(toolbar); /* Loads the next n oldest messages, or all previous history if n is negative. */ const loadOldMessages = function(n){ Chat.e.viewMessages.classList.add('loading'); Chat._isBatchLoading = true; const scrollHt = Chat.e.viewMessages.scrollHeight, scrollTop = Chat.e.viewMessages.scrollTop; F.fetch("chat-poll",{ urlParams:{ before: Chat.mnMsg, n: n }, responseType: 'json', onerror:function(err){ Chat.reportErrorAsMessage(err); Chat._isBatchLoading = false; }, onload:function(x){ let gotMessages = x.msgs.length; newcontent(x,true); Chat._isBatchLoading = false; Chat.updateActiveUserList(); if(Chat._gotServerError){ Chat._gotServerError = false; return; } if(n<0/*we asked for all history*/ || 0===gotMessages/*we found no history*/ || (n>0 && gotMessages<n /*we got fewer history entries than requested*/) || (n===0 && gotMessages<Chat.loadMessageCount /*we asked for default amount and got fewer than that.*/)){ /* We've loaded all history. Permanently disable the history-load toolbar and keep it from being re-enabled via the ajaxStart()/ajaxEnd() mechanism... */ const div = Chat.e.loadOlderToolbar.querySelector('div'); D.append(D.clearElement(div), "All history has been loaded."); D.addClass(Chat.e.loadOlderToolbar, 'all-done'); const ndx = Chat.disableDuringAjax.indexOf(Chat.e.loadOlderToolbar); if(ndx>=0) Chat.disableDuringAjax.splice(ndx,1); Chat.e.loadOlderToolbar.disabled = true; } if(gotMessages > 0){ F.toast.message("Loaded "+gotMessages+" older messages."); /* Return scroll position to where it was when the history load was requested, per user request */ Chat.e.viewMessages.scrollTo( 0, Chat.e.viewMessages.scrollHeight - scrollHt + scrollTop ); } }, aftersend:function(){ Chat.e.viewMessages.classList.remove('loading'); Chat.ajaxEnd(); } }); }; const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */; D.append(toolbar, wrapper); var btn = D.button("Previous "+Chat.loadMessageCount+" messages"); D.append(wrapper, btn); btn.addEventListener('click',()=>loadOldMessages(Chat.loadMessageCount)); btn = D.button("All previous messages"); D.append(wrapper, btn); btn.addEventListener('click',()=>loadOldMessages(-1)); D.append(Chat.e.viewMessages, toolbar); toolbar.disabled = true /*will be enabled when msg load finishes */; })()/*end history loading widget setup*/; const afterFetch = function f(){ if(true===f.isFirstCall){ f.isFirstCall = false; Chat.ajaxEnd(); Chat.e.viewMessages.classList.remove('loading'); setTimeout(function(){ Chat.scrollMessagesTo(1); }, 250); } if(Chat._gotServerError && Chat.intervalTimer){ clearInterval(Chat.intervalTimer); Chat.reportErrorAsMessage( "Shutting down chat poller due to server-side error. ", "Reload this page to reactivate it."); delete Chat.intervalTimer; } poll.running = false; }; afterFetch.isFirstCall = true; const poll = async function f(){ if(f.running) return; f.running = true; Chat._isBatchLoading = f.isFirstCall; if(true===f.isFirstCall){ f.isFirstCall = false; Chat.ajaxStart(); Chat.e.viewMessages.classList.add('loading'); } F.fetch("chat-poll",{ timeout: 420 * 1000/*FIXME: get the value from the server*/, urlParams:{ name: Chat.mxMsg }, responseType: "json", // Disable the ajax start/end handling for this long-polling op: beforesend: function(){}, aftersend: function(){}, onerror:function(err){ Chat._isBatchLoading = false; if(Chat.verboseErrors) console.error(err); /* ^^^ we don't use Chat.reportError() here b/c the polling fails exepectedly when it times out, but is then immediately resumed, and reportError() produces a loud error message. */ afterFetch(); }, onload:function(y){ newcontent(y); if(Chat._isBatchLoading){ Chat._isBatchLoading = false; Chat.updateActiveUserList(); } afterFetch(); } }); }; poll.isFirstCall = true; Chat._gotServerError = poll.running = false; if( window.fossil.config.chat.fromcli ){ Chat.chatOnlyMode(true); } Chat.intervalTimer = setInterval(poll, 1000); if(0){ const flip = (ev)=>Chat.animate(ev.target,'anim-flip-h'); document.querySelectorAll('#chat-buttons-wrapper .cbutton').forEach(function(e){ e.addEventListener('click',flip, false); }); } delete ForceResizeKludge.$disabled; ForceResizeKludge(); Chat.animate.$disabled = false; setTimeout( ()=>Chat.inputFocus(), 0 ); F.page.chat = Chat/* enables testing the APIs via the dev tools */; }); |
Added src/fossil.page.fileedit.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (function(F/*the fossil object*/){ "use strict"; /** Client-side implementation of the /filepage app. Requires that the fossil JS bootstrapping is complete and that several fossil JS APIs have been installed: fossil.fetch, fossil.dom, fossil.tabs, fossil.storage, fossil.confirmer. Custom events which can be listened for via fossil.page.addEventListener(): - Event 'fileedit-file-loaded': passes on information when it loads a file (whether from the network or its internal local-edit cache), in the form of an "finfo" object: { filename: string, checkin: UUID string, branch: branch name of UUID, isExe: bool, true only for executable files mimetype: mimetype string, as determined by the fossil server. } The internal docs and code frequently use the term "finfo", and such references refer to an object with that form. The fossil.page.fileContent() method gets or sets the current file content for the page. - Event 'fileedit-committed': is fired when a commit completes, passing on the same info as fileedit-file-loaded. - Event 'fileedit-content-replaced': when the editor's content is replaced, as opposed to it being edited via user interaction. This normally happens via selecting a file to load. The event detail is the fossil.page object, not the current file content. - Event 'fileedit-preview-updated': when the preview is refreshed from the server, this event passes on information about the preview change in the form of an object: { element: the DOM element which contains the content preview. mimetype: the fossil-reported content mimetype. previewMode: a string describing the preview mode: see the fossil.page.previewModes map for the values. This can be used to determine whether, e.g., the content is suitable for applying a 3rd-party code highlighting API to. } Here's an example which can be used with the highlightjs code highlighter to update the highlighting when the preview is refreshed in "wiki" mode (which includes fossil-native wiki and markdown): fossil.page.addEventListener( 'fileedit-preview-updated', (ev)=>{ if(ev.detail.previewMode==='wiki'){ ev.detail.element.querySelectorAll( 'code[class^=language-]' ).forEach((e)=>hljs.highlightBlock(e)); } } ); */ const E = (s)=>document.querySelector(s), D = F.dom, P = F.page; P.config = { defaultMaxStashSize: 7, /** See notes for this setting in fossil.page.wikiedit.js. Both /wikiedit and /fileedit share this persistent config option under the same storage key. */ shiftEnterPreview: F.storage.getBool('edit-shift-enter-preview', true) }; /** $stash is an internal-use-only object for managing "stashed" local edits, to help avoid that users accidentally lose content by switching tabs or following links or some such. The basic theory of operation is... All "stashed" state is stored using fossil.storage. - When the current file content is modified by the user, the current stathe of the current P.finfo and its the content is stashed. For the built-in editor widget, "changes" is notified via a 'change' event. For a client-side custom widget, the client needs to call P.stashContentChange() when their widget triggers the equivalent of a 'change' event. - For certain non-content updates (as of this writing, only the is-executable checkbox), only the P.finfo stash entry is updated, not the content (unless the content has not yet been stashed, in which case it is also stashed so that the stash always has matching pairs of finfo/content). - When saving, the stashed entry for the previous version is removed from the stash. - When "loading", we use any stashed state for the given checkin/file combination. When forcing a re-load of content, any stashed entry for that combination is removed from the stash. - Every time P.stashContentChange() updates the stash, it is pruned to $stash.prune.defaultMaxCount most-recently-updated entries. - This API often refers to "finfo objects." Those are objects with a minimum of {checkin,filename} properties (which must be valid), and a combination of those two properties is used as basis for the stash keys for any given checkin/filename combination. The structure of the stash is a bit convoluted for efficiency's sake: we store a map of file info (finfo) objects separately from those files' contents because otherwise we would be required to JSONize/de-JSONize the file content when stashing/restoring it, and that would be horribly inefficient (meaning "battery-consuming" on mobile devices). */ const $stash = { keys: { index: F.page.name+'.index' }, /** index: { "CHECKIN_HASH:FILENAME": {file info w/o content} ... } In F.storage we... - Store this.index under the key this.keys.index. - Store each file's content under the key (P.name+'/CHECKIN_HASH:FILENAME'). These are stored separately from the index entries to avoid having to JSONize/de-JSONize the content. The assumption/hope is that the browser can store those records "directly," without any intermediary encoding/decoding going on. */ indexKey: function(finfo){return finfo.checkin+':'+finfo.filename}, /** Returns the key for storing content for the given key suffix, by prepending P.name to suffix. */ contentKey: function(suffix){return P.name+'/'+suffix}, /** Returns the index object, fetching it from the stash or creating it anew on the first call. */ getIndex: function(){ if(!this.index){ this.index = F.storage.getJSON( this.keys.index, {} ); } return this.index; }, _fireStashEvent: function(){ if(this._disableNextEvent) delete this._disableNextEvent; else F.page.dispatchEvent('fileedit-stash-updated', this); }, /** Returns the stashed version, if any, for the given finfo object. */ getFinfo: function(finfo){ const ndx = this.getIndex(); return ndx[this.indexKey(finfo)]; }, /** Serializes this object's index to F.storage. Returns this. */ storeIndex: function(){ if(this.index) F.storage.setJSON(this.keys.index,this.index); return this; }, /** Updates the stash record for the given finfo and (optionally) content. If passed 1 arg, only the finfo stash is updated, else both the finfo and its contents are (re-)stashed. Returns this. */ updateFile: function(finfo,content){ const ndx = this.getIndex(), key = this.indexKey(finfo), old = ndx[key]; const record = old || (ndx[key]={ checkin: finfo.checkin, filename: finfo.filename, mimetype: finfo.mimetype }); record.isExe = !!finfo.isExe; record.stashTime = new Date().getTime(); if(!record.branch) record.branch=finfo.branch; this.storeIndex(); if(arguments.length>1){ F.storage.set(this.contentKey(key), content); } this._fireStashEvent(); return this; }, /** Returns the stashed content, if any, for the given finfo object. */ stashedContent: function(finfo){ return F.storage.get(this.contentKey(this.indexKey(finfo))); }, /** Returns true if we have stashed content for the given finfo record. */ hasStashedContent: function(finfo){ return F.storage.contains(this.contentKey(this.indexKey(finfo))); }, /** Unstashes the given finfo record and its content. Returns this. */ unstash: function(finfo){ const ndx = this.getIndex(), key = this.indexKey(finfo); delete finfo.stashTime; delete ndx[key]; F.storage.remove(this.contentKey(key)); this.storeIndex(); this._fireStashEvent(); return this; }, /** Clears all $stash entries from F.storage. Returns this. */ clear: function(){ const ndx = this.getIndex(), self = this; let count = 0; Object.keys(ndx).forEach(function(k){ ++count; const e = ndx[k]; delete ndx[k]; F.storage.remove(self.contentKey(k)); }); F.storage.remove(this.keys.index); delete this.index; if(count) this._fireStashEvent(); return this; }, /** Removes all but the maxCount most-recently-updated stash entries, where maxCount defaults to this.prune.defaultMaxCount. */ prune: function f(maxCount){ const ndx = this.getIndex(); const li = []; if(!maxCount || maxCount<0) maxCount = f.defaultMaxCount; Object.keys(ndx).forEach((k)=>li.push(ndx[k])); li.sort((l,r)=>l.stashTime - r.stashTime); let n = 0; while(li.length>maxCount){ ++n; const e = li.shift(); this._disableNextEvent = true; this.unstash(e); console.warn("Pruned oldest local file edit entry:",e); } if(n) this._fireStashEvent(); } }; $stash.prune.defaultMaxCount = P.config.defaultMaxStashSize; /** Widget for the checkin/file selection list. */ P.fileSelectWidget = { e:{ container: E('#fileedit-file-selector') }, finfo: {}, cache: { checkins: undefined, files:{}, branchKey: 'fileedit/uuid-branches', branchNames: {} }, /** Fetches the list of leaf checkins from the server and updates the UI with that list. */ loadLeaves: function(){ D.append(D.clearElement( this.e.ciListLabel, this.e.selectCi, this.e.selectFiles ),"Loading leaves..."); D.disable(this.e.btnLoadFile, this.e.selectFiles, this.e.selectCi); const self = this; const onload = function(list){ D.append(D.clearElement(self.e.ciListLabel), "Open leaves (newest first):"); self.cache.checkins = list; D.clearElement(D.enable(self.e.selectCi)); let loadThisOne = P.initialFiles/*possibly injected at page-load time*/; if(loadThisOne){ self.cache.files[loadThisOne.checkin] = loadThisOne; delete P.initialFiles; } list.forEach(function(o,n){ if(!n && !loadThisOne) loadThisOne = o; self.cache.branchNames[F.hashDigits(o.checkin,true)] = o.branch; D.option(self.e.selectCi, o.checkin, o.timestamp+' ['+o.branch+']: ' +F.hashDigits(o.checkin)); }); F.storage.setJSON(self.cache.branchKey, self.cache.branchNames); if(loadThisOne){ self.e.selectCi.value = loadThisOne.checkin; } self.loadFiles(loadThisOne ? loadThisOne.checkin : false); }; if(P.initialLeaves/*injected at page-load time.*/){ const lv = P.initialLeaves; delete P.initialLeaves; onload(lv); }else{ F.fetch('fileedit/filelist',{ urlParams:'leaves', responseType: 'json', onload: onload }); } }, /** Loads the file list for the given checkin UUID. It uses a cached copy on subsequent calls for the same UUID. If passed a falsy value, it instead clears and disables the file selection list. */ loadFiles: function(ciUuid){ delete this.finfo.filename; this.finfo.checkin = ciUuid; const selFiles = this.e.selectFiles; if(!ciUuid){ D.clearElement(D.disable(selFiles, this.e.btnLoadFile)); return this; } const onload = (response)=>{ D.clearElement(selFiles); D.append( D.clearElement(this.e.fileListLabel), "Editable files for ", D.append( D.code(), "[", D.a(F.repoUrl('timeline',{ c: ciUuid }), F.hashDigits(ciUuid)),"]" ), ":" ); this.cache.files[response.checkin] = response; response.editableFiles.forEach(function(fn,n){ D.option(selFiles, fn); }); if(selFiles.options.length){ D.enable(selFiles, this.e.btnLoadFile); } }; const got = this.cache.files[ciUuid]; if(got){ onload(got); return this; } D.disable(selFiles,this.e.btnLoadFile); D.clearElement(selFiles); D.append(D.clearElement(this.e.fileListLabel), "Loading files for "+F.hashDigits(ciUuid)+"..."); F.fetch('fileedit/filelist',{ urlParams:{checkin: ciUuid}, responseType: 'json', onload }); return this; }, /** If this object has ever loaded the given checkin version via loadLeaves(), this returns the branch name associated with that version, else returns undefined; */ checkinBranchName: function(uuid){ return this.cache.branchNames[F.hashDigits(uuid,true)]; }, /** Initializes the checkin/file selector widget. Must only be called once. */ init: function(){ this.cache.branchNames = F.storage.getJSON(this.cache.branchKey, {}); const selCi = this.e.selectCi = D.addClass(D.select(), 'flex-grow'), selFiles = this.e.selectFiles = D.addClass(D.select(), 'file-list'), btnLoad = this.e.btnLoadFile = D.addClass(D.button("Load file"), "flex-shrink"), filesLabel = this.e.fileListLabel = D.addClass(D.div(),'flex-shrink','file-list-label'), ciLabelWrapper = D.addClass( D.div(), 'flex-container','flex-row', 'flex-shrink', 'stretch', 'child-gap-small' ), btnReload = D.addClass( D.button('Reload'), 'flex-shrink' ), ciLabel = this.e.ciListLabel = D.addClass(D.span(),'flex-shrink','checkin-list-label') ; D.attr(selCi, 'title',"The list of opened leaves."); D.attr(selFiles, 'title', "The list of editable files for the selected checkin."); D.attr(btnLoad, 'title', "Load the selected file into the editor."); D.disable(selCi, selFiles, btnLoad); D.attr(selFiles, 'size', 12); D.append( this.e.container, ciLabel, D.append(ciLabelWrapper, selCi, btnReload), filesLabel, selFiles, /* Use a wrapper for btnLoad so that the button itself does not stretch to fill the parent width: */ D.append(D.addClass(D.div(), 'flex-shrink'), btnLoad) ); if(F.config['fileedit-glob']){ D.append( this.e.container, D.append( D.span(), D.append(D.code(),"fileedit-glob"), " config setting = ", D.append(D.code(), JSON.stringify(F.config['fileedit-glob'])) ) ); } this.loadLeaves(); selCi.addEventListener( 'change', (e)=>this.loadFiles(e.target.value), false ); const doLoad = (e)=>{ this.finfo.filename = selFiles.value; if(this.finfo.filename){ P.loadFile(this.finfo.filename, this.finfo.checkin); } }; btnLoad.addEventListener('click', doLoad, false); selFiles.addEventListener('dblclick', doLoad, false); btnReload.addEventListener( 'click', (e)=>this.loadLeaves(), false ); delete this.init; } }/*P.fileSelectWidget*/; /** Widget for listing and selecting $stash entries. */ P.stashWidget = { e:{/*DOM element(s)*/}, init: function(domInsertPoint/*insert widget BEFORE this element*/){ const wrapper = D.addClass( D.attr(D.div(),'id','fileedit-stash-selector'), 'input-with-label' ); const sel = this.e.select = D.select(); const btnClear = this.e.btnClear = D.button("Discard Edits"), btnHelp = D.append( D.addClass(D.div(), "help-buttonlet"), 'Locally-edited files. Timestamps are the last local edit time. ', 'Only the ',P.config.defaultMaxStashSize,' most recent files ', 'are retained. Saving or reloading a file removes it from this list. ', D.append(D.code(),F.storage.storageImplName()), ' = ',F.storage.storageHelpDescription() ); D.append(wrapper, "Local edits (", D.append(D.code(), F.storage.storageImplName()), "):", btnHelp, sel, btnClear); F.helpButtonlets.setup(btnHelp); D.option(D.disable(sel), undefined, "(empty)"); F.page.addEventListener('fileedit-stash-updated',(e)=>this.updateList(e.detail)); F.page.addEventListener('fileedit-file-loaded',(e)=>this.updateList($stash, e.detail)); sel.addEventListener('change',function(e){ const opt = this.selectedOptions[0]; if(opt && opt._finfo) P.loadFile(opt._finfo); }); if(F.storage.isTransient()){/*Warn if our storage is particularly transient...*/ D.append(wrapper, D.append( D.addClass(D.span(),'warning'), "Warning: persistent storage is not available, "+ "so uncomitted edits will not survive a page reload." )); } domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint); P.tabs.switchToTab(1/*DOM visibility workaround*/); F.confirmer(btnClear, { /* must come after insertion into the DOM for the pinSize option to work. */ pinSize: true, confirmText: "DISCARD all local edits?", onconfirm: function(e){ if(P.finfo){ const stashed = P.getStashedFinfo(P.finfo); P.clearStash(); if(stashed) P.loadFile(/*reload after discarding edits*/); }else{ P.clearStash(); } }, ticks: F.config.confirmerButtonTicks }); D.addClass(this.e.btnClear,'hidden' /* must not be set until after confirmer is set up!*/); $stash._fireStashEvent(/*read the page-load-time stash*/); P.tabs.switchToTab(0/*DOM visibility workaround*/); delete this.init; }, /** Regenerates the edit selection list. */ updateList: function f(stasher,theFinfo){ if(!f.compare){ const cmpBase = (l,r)=>l<r ? -1 : (l===r ? 0 : 1); f.compare = function(l,r){ const cmp = cmpBase(l.filename, r.filename); return cmp ? cmp : cmpBase(l.checkin, r.checkin); }; f.rxZ = /\.\d+Z$/ /* ms and 'Z' part of date string */; const pad=(x)=>(''+x).length>1 ? x : '0'+x; f.timestring = function ff(d){ return [ d.getFullYear(),'-',pad(d.getMonth()+1/*sigh*/),'-',pad(d.getDate()), '@',pad(d.getHours()),':',pad(d.getMinutes()) ].join(''); }; } const index = stasher.getIndex(), ilist = []; Object.keys(index).forEach((finfo)=>{ ilist.push(index[finfo]); }); const self = this; D.clearElement(this.e.select); if(0===ilist.length){ D.addClass(this.e.btnClear, 'hidden'); D.option(D.disable(this.e.select),undefined,"No local edits"); return; } D.enable(this.e.select); D.removeClass(this.e.btnClear, 'hidden'); D.disable(D.option(this.e.select,0,"Select a local edit...")); const currentFinfo = theFinfo || P.finfo || {filename:''}; ilist.sort(f.compare).forEach(function(finfo,n){ const key = stasher.indexKey(finfo), branch = finfo.branch || P.fileSelectWidget.checkinBranchName(finfo.checkin)||''; /* Remember that we don't know the branch name for non-leaf versions which P.fileSelectWidget() has never seen/cached. */ const opt = D.option( self.e.select, n+1/*value is (almost) irrelevant*/, [F.hashDigits(finfo.checkin), ' [',branch||'?branch?','] ', f.timestring(new Date(finfo.stashTime)),' ', false ? finfo.filename : F.shortenFilename(finfo.filename) ].join('') ); opt._finfo = finfo; if(0===f.compare(currentFinfo, finfo)){ D.attr(opt, 'selected', true); } }); } }/*P.stashWidget*/; /** Internal workaround to select the current preview mode and fire a change event if the value actually changes or if forceEvent is truthy. */ P.selectPreviewMode = function(modeValue, forceEvent){ const s = this.e.selectPreviewMode; if(!modeValue) modeValue = s.value; else if(s.value != modeValue){ s.value = modeValue; forceEvent = true; } if(forceEvent){ // Force UI update s.dispatchEvent(new Event('change',{target:s})); } }; /** Keep track of how many in-flight AJAX requests there are so we can disable input elements while any are pending. For simplicity's sake we simply disable ALL OF IT while any AJAX is pending, rather than disabling operation-specific UI elements, which would be a huge maintenance hassle. Noting, however, that this global on/off is not *quite* pedantically correct. Pedantically speaking. If an element is disabled before an XHR starts, this code "should" notice that and not include it in the to-re-enable list. That would be annoying to do, and becomes impossible to do properly once multiple XHRs are in transit and an element is disabled seprately between two of those in-transit requests (that would be an unlikely, but possible, corner case). As of this writing, the only elements which are ever normally programmatically toggled between enabled/disabled... 1) Belong to the file selection list and remain disabled until the list of leaves and files are loaded. i.e. they would be disabled *anyway* during their own XHR requests. 2) The stashWidget's SELECT list when no local edits are stashed. Curiously, the all-or-nothing re-enabling implemented here does not re-enable that particular selection list. That's because of timing, though: that widget is "manually" disabled when the list is empty, and that list is normally emptied in conjunction with an XHR request. */ const ajaxState = { count: 0 /* in-flight F.fetch() requests */, toDisable: undefined /* elements to disable during ajax activity */ }; F.fetch.beforesend = function f(){ if(!ajaxState.toDisable){ ajaxState.toDisable = document.querySelectorAll( 'button, input, select, textarea' ); } if(1===++ajaxState.count){ D.addClass(document.body, 'waiting'); D.disable(ajaxState.toDisable); } }; F.fetch.aftersend = function(){ if(0===--ajaxState.count){ D.removeClass(document.body, 'waiting'); D.enable(ajaxState.toDisable); } }; F.onPageLoad(function() { P.base = {tag: E('base')}; P.base.originalHref = P.base.tag.href; P.tabs = new F.TabManager('#fileedit-tabs'); P.e = { /* various DOM elements we work with... */ taEditor: E('#fileedit-content-editor'), taCommentSmall: E('#fileedit-comment'), taCommentBig: E('#fileedit-comment-big'), taComment: undefined/*gets set to one of taComment{Big,Small}*/, ajaxContentTarget: E('#ajax-target'), btnCommit: E("#fileedit-btn-commit"), btnReload: E("#fileedit-tab-content button.fileedit-content-reload"), selectPreviewMode: E('#select-preview-mode select'), selectHtmlEmsWrap: E('#select-preview-html-ems'), selectEolWrap: E('#select-eol-style'), selectEol: E('#select-eol-style select[name=eol]'), selectFontSizeWrap: E('#select-font-size'), selectDiffWS: E('select[name=diff_ws]'), cbLineNumbersWrap: E('#cb-line-numbers'), cbAutoPreview: E('#cb-preview-autorefresh'), previewTarget: E('#fileedit-tab-preview-wrapper'), manifestTarget: E('#fileedit-manifest'), diffTarget: E('#fileedit-tab-diff-wrapper'), cbIsExe: E('input[type=checkbox][name=exec_bit]'), cbManifest: E('input[type=checkbox][name=include_manifest]'), editStatus: E('#fileedit-edit-status'), tabs:{ content: E('#fileedit-tab-content'), preview: E('#fileedit-tab-preview'), diff: E('#fileedit-tab-diff'), commit: E('#fileedit-tab-commit'), fileSelect: E('#fileedit-tab-fileselect') } }; /* Figure out which comment editor to show by default and hide the other one. By default we take the one which does not have the 'hidden' CSS class. If neither do, we default to single-line mode. */ if(D.hasClass(P.e.taCommentSmall, 'hidden')){ P.e.taComment = P.e.taCommentBig; }else if(D.hasClass(P.e.taCommentBig,'hidden')){ P.e.taComment = P.e.taCommentSmall; }else{ P.e.taComment = P.e.taCommentSmall; D.addClass(P.e.taCommentBig, 'hidden'); } D.removeClass(P.e.taComment, 'hidden'); P.tabs.addCustomWidget( E('#fossil-status-bar') ).addCustomWidget(P.e.editStatus); let currentTab/*used for ctrl-enter switch between editor and preview*/; P.tabs.addEventListener( /* Set up auto-refresh of the preview tab... */ 'before-switch-to', function(ev){ currentTab = ev.detail; if(ev.detail===P.e.tabs.preview){ P.baseHrefForFile(); if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview(); }else if(ev.detail===P.e.tabs.diff){ /* Work around a weird bug where the page gets wider than the window when the diff tab is NOT in view and the current SBS diff widget is wider than the window. When the diff IS in view then CSS overflow magically reduces the page size again. Weird. Maybe FF-specific. Note that this weirdness happens even though P.e.diffTarget's parent is hidden (and therefore P.e.diffTarget is also hidden). */ D.removeClass(P.e.diffTarget, 'hidden'); } } ); P.tabs.addEventListener( /* Set up auto-refresh of the preview tab... */ 'before-switch-from', function(ev){ if(ev.detail===P.e.tabs.preview){ P.baseHrefRestore(); }else if(ev.detail===P.e.tabs.diff){ /* See notes in the before-switch-to handler. */ D.addClass(P.e.diffTarget, 'hidden'); } } ); //////////////////////////////////////////////////////////// // Trigger preview on Ctrl-Enter. This only works on the built-in // editor widget, not a client-provided one. P.e.taEditor.addEventListener('keydown',function(ev){ if(P.config.shiftEnterPreview && ev.shiftKey && 13===ev.keyCode){ ev.preventDefault(); ev.stopPropagation(); P.e.taEditor.blur(/*force change event, if needed*/); P.tabs.switchToTab(P.e.tabs.preview); if(!P.e.cbAutoPreview.checked){/* If NOT in auto-preview mode, trigger an update. */ P.preview(); } return false; } }, false); // If we're in the preview tab, have ctrl-enter switch back to the editor. document.body.addEventListener('keydown',function(ev){ if(ev.shiftKey && 13 === ev.keyCode){ if(currentTab === P.e.tabs.preview){ ev.preventDefault(); ev.stopPropagation(); P.tabs.switchToTab(P.e.tabs.content); P.e.taEditor.focus(/*doesn't work for client-supplied editor widget! And it's slow as molasses for long docs, as focus() forces a document reflow.*/); return false; } } }, true); F.connectPagePreviewers( P.e.tabs.preview.querySelector( '#btn-preview-refresh' ) ); const diffButtons = E('#fileedit-tab-diff-buttons'); diffButtons.querySelector('button.sbs').addEventListener( "click",(e)=>P.diff(true), false ); diffButtons.querySelector('button.unified').addEventListener( "click",(e)=>P.diff(false), false ); P.e.btnCommit.addEventListener( "click",(e)=>P.commit(), false ); P.tabs.switchToTab(1/*DOM visibility workaround*/); F.confirmer(P.e.btnReload, { pinSize: true, confirmText: "Really reload, losing edits?", onconfirm: (e)=>P.unstashContent().loadFile(), ticks: F.config.confirmerButtonTicks }); E('#comment-toggle').addEventListener( "click",(e)=>P.toggleCommentMode(), false ); P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false); P.e.cbIsExe.addEventListener( 'change', ()=>P.stashContentChange(true), false ); /** Cosmetic: jump through some hoops to enable/disable certain preview options depending on the current preview mode... */ P.e.selectPreviewMode.addEventListener( "change", function(e){ const mode = e.target.value, name = P.previewModes[mode], hide = [], unhide = []; P.previewModes.current = name; if('guess'===name){ unhide.push(P.e.cbLineNumbersWrap, P.e.selectHtmlEmsWrap); }else{ if('text'===name) unhide.push(P.e.cbLineNumbersWrap); else hide.push(P.e.cbLineNumbersWrap); if('htmlIframe'===name) unhide.push(P.e.selectHtmlEmsWrap); else hide.push(P.e.selectHtmlEmsWrap); } hide.forEach((e)=>e.classList.add('hidden')); unhide.forEach((e)=>e.classList.remove('hidden')); }, false ); P.selectPreviewMode(false, true); const selectFontSize = E('select[name=editor_font_size]'); if(selectFontSize){ selectFontSize.addEventListener( "change",function(e){ const ed = P.e.taEditor; ed.className = ed.className.replace( /\bfont-size-\d+/g, '' ); ed.classList.add('font-size-'+e.target.value); }, false ); selectFontSize.dispatchEvent( // Force UI update new Event('change',{target:selectFontSize}) ); } P.addEventListener( // Clear certain views when new content is loaded/set 'fileedit-content-replaced', ()=>{ P.previewNeedsUpdate = true; D.clearElement(P.e.diffTarget, P.e.previewTarget, P.e.manifestTarget); } ); P.addEventListener( // Clear certain views after a non-dry-run commit 'fileedit-committed', (e)=>{ if(!e.detail.dryRun){ D.clearElement(P.e.diffTarget, P.e.previewTarget); } } ); P.fileSelectWidget.init(); P.stashWidget.init( P.e.tabs.content.lastElementChild ); const cbEditPreview = E('#edit-shift-enter-preview'); cbEditPreview.addEventListener('change', function(e){ F.storage.set('edit-shift-enter-preview', P.config.shiftEnterPreview = e.target.checked); }, false); cbEditPreview.checked = P.config.shiftEnterPreview; }/*F.onPageLoad()*/); /** Getter (if called with no args) or setter (if passed an arg) for the current file content. The setter form sets the content, dispatches a 'fileedit-content-replaced' event, and returns this object. */ P.fileContent = function f(){ if(0===arguments.length){ return f.get(); }else{ f.set(arguments[0] || ''); this.dispatchEvent('fileedit-content-replaced', this); return this; } }; /* Default get/set impls for file content */ P.fileContent.get = function(){return P.e.taEditor.value}; P.fileContent.set = function(content){P.e.taEditor.value = content}; /** For use when installing a custom editor widget. Pass it the getter and setter callbacks to fetch resp. set the content of the custom widget. They will be triggered via P.fileContent(). Returns this object. */ P.setContentMethods = function(getter, setter){ this.fileContent.get = getter; this.fileContent.set = setter; return this; }; /** Alerts the editor app that a "change" has happened in the editor. When connecting 3rd-party editor widgets to this app, it is (or may be) necessary to call this for any "change" events the widget emits. Whether or not "change" means that there were "really" edits is irrelevant. This function may perform an arbitrary amount of work, so it should not be called for every keypress within the editor widget. Calling it for "blur" events is generally sufficient, and calling it for each Enter keypress is generally reasonable but also computationally costly. */ P.notifyOfChange = function(){ P.stashContentChange(); }; /** Removes the default editor widget (and any dependent elements) from the DOM, adds the given element in its place, removes this method from this object, and returns this object. */ P.replaceEditorElement = function(newEditor){ P.e.taEditor.parentNode.insertBefore(newEditor, P.e.taEditor); P.e.taEditor.remove(); P.e.selectFontSizeWrap.remove(); delete this.replaceEditorElement; return P; }; /** If either of... - P.previewModes.current==='wiki' - P.previewModes.current==='guess' AND the currently-loaded file has a mimetype of "text/x-fossil-wiki" or "text/x-markdown". ... then this function updates the document's base.href to a repo-relative /doc/{{this.finfo.checkin}}/{{directory part of this.finfo.filename}}/ If neither of those conditions applies, this is a no-op. */ P.baseHrefForFile = function f(){ const fn = this.finfo ? this.finfo.filename : undefined; if(!fn) return this; if(!f.wikiMimeTypes){ f.wikiMimeTypes = ["text/x-fossil-wiki", "text/x-markdown"]; } if('wiki'===P.previewModes.current || ('guess'===P.previewModes.current && f.wikiMimeTypes.indexOf(this.finfo.mimetype)>=0)){ const a = fn.split('/'); a.pop(); this.base.tag.href = F.repoUrl( 'doc/'+F.hashDigits(this.finfo.checkin) +'/'+(a.length ? a.join('/')+'/' : '') ); } return this; }; /** Sets the document's base.href value to its page-load-time setting. */ P.baseHrefRestore = function(){ P.base.tag.href = P.base.originalHref; }; /** Toggles between single- and multi-line comment mode. */ P.toggleCommentMode = function(){ var s, h, c = this.e.taComment.value; if(this.e.taComment === this.e.taCommentSmall){ s = this.e.taCommentBig; h = this.e.taCommentSmall; }else{ s = this.e.taCommentSmall; h = this.e.taCommentBig; /* Doing (input[type=text].value = textarea.value) unfortunately strips all newlines. To compensate we'll replace each EOL with a space. Not ideal. If we were to instead escape them as \n, and do the reverse when toggling again, then they would get committed as escaped newlines if the user did not first switch back to multi-line mode. We cannot blindly unescape the newlines, in the off chance that the user actually enters \n in the comment. */ c = c.replace(/\r?\n/g,' '); } s.value = c; this.e.taComment = s; D.addClass(h, 'hidden'); D.removeClass(s, 'hidden'); }; /** Returns true if fossil.page.finfo is set, indicating that a file has been loaded, else it reports an error and returns false. If passed a truthy value any error message about not having a file loaded is suppressed. */ const affirmHasFile = function(quiet){ if(!P.finfo){ if(!quiet) F.error("No file is loaded."); } return !!P.finfo; }; /** updateVersion() updates the filename and version in various UI elements... Returns this object. */ P.updateVersion = function f(file,rev){ if(!f.eLinks){ f.eName = P.e.editStatus.querySelector('span.name'); f.eLinks = P.e.editStatus.querySelector('span.links'); } if(1===arguments.length){/*assume object*/ this.finfo = arguments[0]; file = this.finfo.filename; rev = this.finfo.checkin; }else if(0===arguments.length){ if(affirmHasFile()){ file = this.finfo.filename; rev = this.finfo.checkin; } }else{ this.finfo = {filename:file,checkin:rev}; } const fi = this.finfo; D.clearElement(f.eName, f.eLinks); if(!fi){ D.append(f.eName, '(no file loaded)'); return this; } const rHuman = F.hashDigits(rev), rUrl = F.hashDigits(rev,true); //TODO? port over is-edited marker from /wikiedit //var marker = getEditMarker(wi, false); D.append(f.eName/*,marker*/,D.a(F.repoUrl('finfo',{name:file, m:rUrl}), file)); D.append( f.eLinks, D.append(D.span(), fi.mimetype||'?mimetype?'), D.a(F.repoUrl('info/'+rUrl), rHuman), D.a(F.repoUrl('timeline',{m:rUrl}), "timeline"), D.a(F.repoUrl('annotate',{filename:file, checkin:rUrl}),'annotate'), D.a(F.repoUrl('blame',{filename:file, checkin:rUrl}),'blame') ); const purlArgs = F.encodeUrlArgs({ filename: this.finfo.filename, checkin: rUrl },false,true); const purl = F.repoUrl('fileedit',purlArgs); D.append( f.eLinks, D.a(purl,"editor permalink") ); this.setPageTitle("Edit: "+fi.filename); return this; }; /** loadFile() loads (file,checkinVersion) and updates the relevant UI elements to reflect the loaded state. If passed no arguments then it re-uses the values from the currently-loaded file, reloading it (emitting an error message if no file is loaded). Returns this object, noting that the load is async. After loading it triggers a 'fileedit-file-loaded' event, passing it this.finfo. If a locally-edited copy of the given file/rev is found, that copy is used instead of one fetched from the server, but it is still treated as a load event. Alternate call forms: - no arguments: re-loads from this.finfo. - 1 argument: assumed to be an finfo-style object. Must have at least {filename, checkin} properties, but need not have other finfo state. */ P.loadFile = function(file,rev){ if(0===arguments.length){ /* Reload from this.finfo */ if(!affirmHasFile()) return this; file = this.finfo.filename; rev = this.finfo.checkin; }else if(1===arguments.length){ /* Assume finfo-like object */ const arg = arguments[0]; file = arg.filename; rev = arg.checkin; } const self = this; const onload = (r,headers)=>{ delete self.finfo; self.updateVersion({ filename: file, checkin: rev, branch: headers['x-fileedit-checkin-branch'], isExe: ('x'===headers['x-fileedit-file-perm']), mimetype: headers['content-type'].split(';').shift() }); self.tabs.switchToTab(self.e.tabs.content); self.e.cbIsExe.checked = self.finfo.isExe; self.fileContent(r); P.previewNeedsUpdate = true; self.dispatchEvent('fileedit-file-loaded', self.finfo); }; const semiFinfo = {filename: file, checkin: rev}; const stashFinfo = this.getStashedFinfo(semiFinfo); if(stashFinfo){ // fake a response from the stash... this.finfo = stashFinfo; this.e.cbIsExe.checked = !!stashFinfo.isExe; onload(this.contentFromStash()||'',{ 'x-fileedit-file-perm': stashFinfo.isExe ? 'x' : undefined, 'content-type': stashFinfo.mimetype, 'x-fileedit-checkin-branch': stashFinfo.branch }); F.message("Fetched from the local-edit storage:", F.hashDigits(stashFinfo.checkin), stashFinfo.filename); return this; } F.message( "Loading content..." ).fetch('fileedit/content',{ urlParams: { filename:file, checkin:rev }, responseHeaders: [ 'x-fileedit-file-perm', 'x-fileedit-checkin-branch', 'content-type'], onload:(r,headers)=>{ onload(r,headers); F.message('Loaded content for', F.hashDigits(self.finfo.checkin), self.finfo.filename); } }); return this; }; /** Fetches the page preview based on the contents and settings of this page's input fields, and updates the UI with with the preview. Returns this object, noting that the operation is async. */ P.preview = function f(switchToTab){ if(!affirmHasFile()) return this; return this._postPreview(this.fileContent(), function(c){ P._previewTo(c); if(switchToTab) self.tabs.switchToTab(self.e.tabs.preview); }); }; /** Callback for use with F.connectPagePreviewers(). Gets passed the preview content. */ P._previewTo = function(c){ const target = this.e.previewTarget; D.clearElement(target); if('string'===typeof c) D.parseHtml(target,c); if(F.pikchr){ F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr')); } }; /** Callback for use with F.connectPagePreviewers() */ P._postPreview = function(content,callback){ if(!affirmHasFile()) return this; if(!content){ callback(content); return this; } const fd = new FormData(); fd.append('render_mode',this.e.selectPreviewMode.value); fd.append('filename',this.finfo.filename); fd.append('ln',E('[name=preview_ln]').checked ? 1 : 0); fd.append('iframe_height', E('[name=preview_html_ems]').value); fd.append('content',content || ''); F.message( "Fetching preview..." ).fetch('ajax/preview-text',{ payload: fd, responseHeaders: 'x-ajax-render-mode', onload: (r,header)=>{ P.selectPreviewMode(P.previewModes[header]); if('wiki'===header) P.baseHrefForFile(); else P.baseHrefRestore(); callback(r); F.message('Updated preview.'); P.previewNeedsUpdate = false; P.dispatchEvent('fileedit-preview-updated',{ previewMode: P.previewModes.current, mimetype: P.finfo.mimetype, element: P.e.previewTarget }); }, onerror: (e)=>{ F.fetch.onerror(e); callback("Error fetching preview: "+e); } }); return this; }; /** Fetches the content diff based on the contents and settings of this page's input fields, and updates the UI with the diff view. Returns this object, noting that the operation is async. */ P.diff = function f(sbs){ if(!affirmHasFile()) return this; const content = this.fileContent(), self = this, target = this.e.diffTarget; const fd = new FormData(); fd.append('filename',this.finfo.filename); fd.append('checkin', this.finfo.checkin); fd.append('sbs', sbs ? 1 : 0); fd.append('content',content); if(this.e.selectDiffWS) fd.append('ws',this.e.selectDiffWS.value); F.message( "Fetching diff..." ).fetch('fileedit/diff',{ payload: fd, onload: function(c){ D.parseHtml(D.clearElement(target),[ "<div>Diff <code>[", self.finfo.checkin, "]</code> → Local Edits</div>", c||'No changes.' ].join('')); F.diff.setupDiffContextLoad(); if(sbs) P.tweakSbsDiffs(); F.message('Updated diff.'); self.tabs.switchToTab(self.e.tabs.diff); } }); return this; }; /** Performs an async commit based on the form contents and updates the UI. Returns this object. */ P.commit = function f(){ if(!affirmHasFile()) return this; const self = this; const content = this.fileContent(), target = D.clearElement(P.e.manifestTarget), cbDryRun = E('[name=dry_run]'), isDryRun = cbDryRun.checked, filename = this.finfo.filename; if(!f.onload){ f.onload = function(c){ const oldFinfo = JSON.parse(JSON.stringify(self.finfo)) if(c.manifest){ D.parseHtml(D.clearElement(target), [ "<h3>Manifest", (c.dryRun?" (dry run)":""), ": ", F.hashDigits(c.checkin),"</h3>", "<pre><code class='fileedit-manifest'>", c.manifest.replace(/</g,'<'), /* ^^^ replace() necessary or this breaks if the manifest comment contains an unclosed HTML tags, e.g. <script> */ "</code></pre>" ].join('')); delete c.manifest/*so we don't stash this with finfo*/; } const msg = [ 'Committed', c.dryRun ? '(dry run)' : '', '[', F.hashDigits(c.checkin) ,'].' ]; if(!c.dryRun){ self.unstashContent(oldFinfo); self.finfo = c; self.e.taComment.value = ''; self.updateVersion(); self.fileSelectWidget.loadLeaves(); } self.dispatchEvent('fileedit-committed', c); F.message.apply(F, msg); self.tabs.switchToTab(self.e.tabs.commit); }; } const fd = new FormData(); fd.append('filename',filename); fd.append('checkin', this.finfo.checkin); fd.append('content',content); fd.append('dry_run',isDryRun ? 1 : 0); fd.append('eol', this.e.selectEol.value || 0); /* Text fields or select lists... */ fd.append('comment', this.e.taComment.value); if(0){ // Comment mimetype is currently not supported by the UI... ['comment_mimetype' ].forEach(function(name){ var e = E('[name='+name+']'); if(e) fd.append(name,e.value); }); } /* Checkboxes: */ ['allow_fork', 'allow_older', 'exec_bit', 'allow_merge_conflict', 'include_manifest', 'prefer_delta' ].forEach(function(name){ var e = E('[name='+name+']'); if(e){ fd.append(name, e.checked ? 1 : 0); }else{ console.error("Missing checkbox? name =",name); } }); F.message( "Checking in..." ).fetch('fileedit/commit',{ payload: fd, responseType: 'json', onload: f.onload }); return this; }; /** Updates P.finfo for certain state and stashes P.finfo, with the current content fetched via P.fileContent(). If passed truthy AND the stash already has stashed content for the current file, only the stashed finfo record is updated, else both the finfo and content are updated. */ P.stashContentChange = function(onlyFinfo){ if(affirmHasFile(true)){ const fi = this.finfo; fi.isExe = this.e.cbIsExe.checked; if(!fi.branch) fi.branch = this.fileSelectWidget.checkinBranchName(fi.checkin); if(onlyFinfo && $stash.hasStashedContent(fi)){ $stash.updateFile(fi); }else{ $stash.updateFile(fi, P.fileContent()); } F.message("Stashed change to",F.hashDigits(fi.checkin),fi.filename); $stash.prune(); this.previewNeedsUpdate = true; } return this; }; /** Removes any stashed state for the current P.finfo (if set) from F.storage. Returns this. */ P.unstashContent = function(){ const finfo = arguments[0] || this.finfo; if(finfo){ this.previewNeedsUpdate = true; $stash.unstash(finfo); //console.debug("Unstashed",finfo); F.message("Unstashed",F.hashDigits(finfo.checkin),finfo.filename); } return this; }; /** Clears all stashed file state from F.storage. Returns this. */ P.clearStash = function(){ $stash.clear(); return this; }; /** If stashed content for P.finfo exists, it is returned, else undefined is returned. */ P.contentFromStash = function(){ return affirmHasFile(true) ? $stash.stashedContent(this.finfo) : undefined; }; /** If a stashed version of the given finfo object exists (same filename/checkin values), return it, else return undefined. */ P.getStashedFinfo = function(finfo){ return $stash.getFinfo(finfo); }; P.$stash = $stash /*only for testing/debugging - not part of the API.*/; })(window.fossil); |
Added src/fossil.page.forumpost.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (function(F/*the fossil object*/){ "use strict"; /* JS code for /forumpost and friends. Requires fossil.dom and can optionally use fossil.pikchr. */ const P = F.page, D = F.dom; /** When the page is loaded, this handler does the following: - Installs expand/collapse UI elements on "long" posts and collapses them. - Any pikchr-generated SVGs get a source-toggle button added to them which activates when the mouse is over the image or it is tapped. This is a harmless no-op if the current page has neither forum post constructs for (1) nor any pikchr images for (2), nor will NOT running this code cause any breakage for clients with no JS support: this is all "nice-to-have", not required functionality. */ F.onPageLoad(function(){ const scrollbarIsVisible = (e)=>e.scrollHeight > e.clientHeight; /* Returns an event handler which implements the post expand/collapse toggle on contentElem when the given widget is activated. */ const getWidgetHandler = function(widget, contentElem){ return function(ev){ if(ev) ev.preventDefault(); const wasExpanded = widget.classList.contains('expanded'); widget.classList.toggle('expanded'); contentElem.classList.toggle('expanded'); if(wasExpanded){ contentElem.classList.add('shrunken'); contentElem.parentElement.scrollIntoView({ /* This is non-standard, but !(MSIE, Safari) supposedly support it: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#Browser_compatibility */ behavior: 'smooth' }); }else{ contentElem.classList.remove('shrunken'); } return false; }; }; /* Adds an Expand/Collapse toggle to all div.forumPostBody elements which are deemed "too large" (those for which scrolling is currently activated because they are taller than their max-height). */ document.querySelectorAll( 'div.forumTime, div.forumEdit' ).forEach(function f(forumPostWrapper){ const content = forumPostWrapper.querySelector('div.forumPostBody'); if(!content || !scrollbarIsVisible(content)) return; const parent = content.parentElement, widget = D.addClass( D.div(), 'forum-post-collapser','bottom' ), rightTapZone = D.addClass( D.div(), 'forum-post-collapser','right' ); /* Repopulates the rightTapZone with arrow indicators. Because of the wildly varying height of these elements, This has to be done dynamically at init time and upon collapse/expand. Will not work until the rightTapZone has been added to the DOM. */ const refillTapZone = function f(){ if(!f.baseTapIndicatorHeight){ /* To figure out how often to place an arrow in the rightTapZone, we simply grab the first header element from the page and use its hight as our basis for calculation. */ const h1 = document.querySelector('h1, h2'); f.baseTapIndicatorHeight = h1.getBoundingClientRect().height; } D.clearElement(rightTapZone); var rtzHeight = parseInt(window.getComputedStyle(rightTapZone).height); do { D.append(rightTapZone, D.span()); rtzHeight -= f.baseTapIndicatorHeight * 8; }while(rtzHeight>0); }; const handlerStep1 = getWidgetHandler(widget, content); const widgetEventHandler = ()=>{ handlerStep1(); refillTapZone(); }; content.classList.add('with-expander'); widget.addEventListener('click', widgetEventHandler, false); /** Append 3 children, which CSS will evenly space across the widget. This improves visibility over having the label in only the left, right, or center. */ var i = 0; for( ; i < 3; ++i ) D.append(widget, D.span()); if(content.nextSibling){ forumPostWrapper.insertBefore(widget, content.nextSibling); }else{ forumPostWrapper.appendChild(widget); } content.appendChild(rightTapZone); rightTapZone.addEventListener('click', widgetEventHandler, false); refillTapZone(); })/*F.onPageLoad()*/; if(F.pikchr){ F.pikchr.addSrcView(); } /* Attempt to keep stray double-clicks from double-posting. */ const formSubmitted = function(event){ const form = event.target; if( form.dataset.submitted ){ event.preventDefault(); return; } form.dataset.submitted = '1'; /** If the user is left waiting "a long time," disable the resubmit protection. If we don't do this and they tap the browser's cancel button while waiting, they'll be stuck with an unsubmittable form. */ setTimeout(()=>{delete form.dataset.submitted}, 7000); return; }; document.querySelectorAll("form").forEach(function(form){ form.addEventListener('submit',formSubmitted); }); })/*F.onPageLoad callback*/; })(window.fossil); |
Added src/fossil.page.pikchrshow.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (function(F/*the fossil object*/){ "use strict"; /** Client-side implementation of the /pikchrshowcs app. Requires that the fossil JS bootstrapping is complete and that these fossil JS APIs have been installed: fossil.fetch, fossil.dom, fossil.copybutton, fossil.popupwidget, fossil.storage Maintenance funkiness note: this file is for the legacy /pikchrshowcs app, which was formerly named /pikchrshow. This file and its replacement were not renamed because the replacement impl would end up getting this file's name and cause confusion in the file history. Whether that confusion would be less than this file's name matching the _other_ /pikchrshow impl will cause more or less confusion than that remains to be seen. */ const E = (s)=>document.querySelector(s), D = F.dom, P = F.page; P.previewMode = 0 /*0==rendered SVG, 1==pikchr text markdown, 2==pikchr text fossil, 3==raw SVG. */ P.response = {/*stashed state for the server's preview response*/ isError: false, inputText: undefined /* value of the editor field at render-time */, raw: undefined /* raw response text/HTML from server */, rawSvg: undefined /* plain-text SVG part of responses. Required because the browser will convert \u00a0 to if we extract the SVG from the DOM, resulting in illegal SVG. */ }; /** If string r contains an SVG element, this returns that section of the string, else it returns falsy. */ const getResponseSvg = function(r){ const i0 = r.indexOf("<svg"); if(i0>=0){ const i1 = r.indexOf("</svg"); return r.substring(i0,i1+6); } return ''; }; F.onPageLoad(function() { document.body.classList.add('pikchrshow'); P.e = { /* various DOM elements we work with... */ previewTarget: E('#pikchrshow-output'), previewLegend: E('#pikchrshow-output-wrapper > legend'), previewCopyButton: D.attr( D.addClass(D.span(),'copy-button'), 'id','preview-copy-button' ), previewModeLabel: D.label('preview-copy-button'), btnSubmit: E('#pikchr-submit-preview'), btnStash: E('#pikchr-stash'), btnUnstash: E('#pikchr-unstash'), btnClearStash: E('#pikchr-clear-stash'), cbDarkMode: E('#flipcolors-wrapper > input[type=checkbox]'), taContent: E('#content'), taPreviewText: D.textarea(20,0,true), uiControls: E('#pikchrshow-controls'), previewModeToggle: D.button("Preview mode"), markupAlignDefault: D.attr(D.radio('markup-align','',true), 'id','markup-align-default'), markupAlignCenter: D.attr(D.radio('markup-align','center'), 'id','markup-align-center'), markupAlignIndent: D.attr(D.radio('markup-align','indent'), 'id','markup-align-indent'), markupAlignWrapper: D.addClass(D.span(), 'input-with-label') }; //////////////////////////////////////////////////////////// // Setup markup alignment selection... const alignEvent = function(ev){ /* Update markdown/fossil wiki preview if it's active */ if(P.previewMode==1 || P.previewMode==2){ P.renderPreview(); } }; P.e.markupAlignRadios = [ P.e.markupAlignDefault, P.e.markupAlignCenter, P.e.markupAlignIndent ]; D.append(P.e.markupAlignWrapper, D.addClass(D.append(D.span(),"align:"), 'v-align-middle')); P.e.markupAlignRadios.forEach( function(e){ e.addEventListener('change', alignEvent, false); D.append(P.e.markupAlignWrapper, D.addClass([ e, D.label(e, e.value || "left") ], 'v-align-middle')); } ); //////////////////////////////////////////////////////////// // Setup the preview fieldset's LEGEND element... D.append( P.e.previewLegend, P.e.previewModeToggle, '\u00a0', P.e.previewCopyButton, P.e.previewModeLabel, P.e.markupAlignWrapper ); //////////////////////////////////////////////////////////// // Trigger preview on Shift-Enter. P.e.taContent.addEventListener('keydown',function(ev){ if(ev.shiftKey && 13 === ev.keyCode){ ev.preventDefault(); ev.stopPropagation(); P.preview(); return false; } }, false); //////////////////////////////////////////////////////////// // Setup clipboard-copy of markup/SVG... F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText}); P.e.previewModeLabel.addEventListener('click', ()=>P.e.previewCopyButton.click(), false); //////////////////////////////////////////////////////////// // Set up dark mode simulator... P.e.cbDarkMode.addEventListener('change', function(ev){ if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode'); else D.removeClass(P.e.previewTarget, 'dark-mode'); }, false); if(P.e.cbDarkMode.checked) D.addClass(P.e.previewTarget, 'dark-mode'); //////////////////////////////////////////////////////////// // Set up preview update and preview mode toggle... P.e.btnSubmit.addEventListener('click', ()=>P.preview(), false); P.e.previewModeToggle.addEventListener('click', function(){ /* Rotate through the 4 available preview modes */ P.previewMode = ++P.previewMode % 4; P.renderPreview(); }, false); //////////////////////////////////////////////////////////// // Set up selection list of predefined scripts... if(true){ const selectScript = P.e.selectScript = D.select(), cbAutoPreview = P.e.cbAutoPreview = D.attr(D.checkbox(true),'id', 'cb-auto-preview'), cbWrap = D.addClass(D.div(),'input-with-label') ; D.append( cbWrap, selectScript, cbAutoPreview, D.label(cbAutoPreview,"Auto-preview?"), F.helpButtonlets.create( D.append(D.div(), 'Auto-preview automatically previews selected ', 'built-in pikchr scripts by sending them to ', 'the server for rendering. Not recommended on a ', 'slow connection/server.', D.br(),D.br(), 'Pikchr scripts may also be dragged/dropped from ', 'the local filesystem into the text area, if the ', 'environment supports it, but the auto-preview ', 'option does not apply to them.' ) ) )/*.childNodes.forEach(function(ch){ ch.style.margin = "0 0.25em"; })*/; D.append(P.e.uiControls, cbWrap); P.predefinedPiks.forEach(function(script,ndx){ const opt = D.option(script.code ? script.code.trim() :'', script.name); D.append(selectScript, opt); opt.$_sampleScript = script /* for response caching purposes */; if(!ndx) selectScript.selectedIndex = 0 /*timing/ordering workaround*/; if(!script.code) D.disable(opt); }); delete P.predefinedPiks; selectScript.addEventListener('change', function(ev){ const val = ev.target.value; if(!val) return; const opt = ev.target.selectedOptions[0]; P.e.taContent.value = val; if(cbAutoPreview.checked){ P.preview.$_sampleScript = opt.$_sampleScript; P.preview(); } }, false); } //////////////////////////////////////////////////////////// // Move dark mode checkbox to the end and add a help buttonlet D.append( P.e.uiControls, D.append( P.e.cbDarkMode.parentNode/*the .input-with-label element*/, F.helpButtonlets.create( D.div(), 'Dark mode changes the colors of rendered SVG to ', 'make them more visible in dark-themed skins. ', 'This only changes (using CSS) how they are rendered, ', 'not any actual colors written in the script.', D.br(), D.br(), 'In some color combinations, certain browsers might ', 'cause the SVG image to blur considerably with this ', 'setting enabled!' ) ) ); //////////////////////////////////////////////////////////// // File drag/drop pikchr scripts into P.e.taContent. // Adapted from: https://stackoverflow.com/a/58677161 const dropHighlight = P.e.taContent; const dropEvents = { drop: function(ev){ //ev.stopPropagation(); ev.preventDefault(); D.removeClass(dropHighlight, 'dragover'); const file = ev.dataTransfer.files[0]; if(file) { const reader = new FileReader(); reader.addEventListener( 'load', function(e) {P.e.taContent.value = e.target.result}, false ); reader.readAsText(file, "UTF-8"); } }, dragenter: function(ev){ //ev.stopPropagation(); ev.preventDefault(); ev.dataTransfer.dropEffect = "copy"; D.addClass(dropHighlight, 'dragover'); //console.debug("dragenter"); }, dragover: function(ev){ //ev.stopPropagation(); ev.preventDefault(); //console.debug("dragover"); }, dragend: function(ev){ //ev.stopPropagation(); ev.preventDefault(); //console.debug("dragend"); }, dragleave: function(ev){ //ev.stopPropagation(); ev.preventDefault(); D.removeClass(dropHighlight, 'dragover'); //console.debug("dragleave"); } }; /* The idea here is to accept drops at multiple points or, ideally, document.body, and apply them to P.e.taContent, but the precise combination of event handling needed to pull this off is eluding me. */ [P.e.taContent //P.e.previewTarget,// works only until we drag over the SVG element! //document.body /* ideally we'd link only to document.body, but the events seem to get out of whack, with dropleave being triggered at unexpected points. */ ].forEach(function(e){ Object.keys(dropEvents).forEach( (k)=>e.addEventListener(k, dropEvents[k], true) ); }); //////////////////////////////////////////////////////////// // Setup stash/unstash const stashKey = 'pikchrshow-stash'; P.e.btnStash.addEventListener('click', function(){ const val = P.e.taContent.value; if(val){ F.storage.set(stashKey, val); D.enable(P.e.btnUnstash); F.toast.message("Stashed pikchr."); } }, false); P.e.btnUnstash.addEventListener('click', function(){ const val = F.storage.get(stashKey); P.e.taContent.value = val || ''; }, false); P.e.btnClearStash.addEventListener('click', function(){ F.storage.remove(stashKey); D.disable(P.e.btnUnstash); F.toast.message("Cleared pikchr stash."); }, false); F.helpButtonlets.create(P.e.btnClearStash.nextElementSibling); // If we have stashed contents, enable Unstash, else disable it: if(F.storage.contains(stashKey)) D.enable(P.e.btnUnstash); else D.disable(P.e.btnUnstash); //////////////////////////////////////////////////////////// // If we start with content, get it in sync with the state // generated by P.preview(). Normally the server pre-populates it // with an example. let needsPreview; if(!P.e.taContent.value){ P.e.taContent.value = F.storage.get(stashKey,''); needsPreview = true; } if(P.e.taContent.value){ /* Fill our "response" state so that renderPreview() can work */ P.response.inputText = P.e.taContent.value; P.response.raw = P.e.previewTarget.innerHTML; P.response.rawSvg = getResponseSvg( P.response.raw /*note that this is already in the DOM, which means that the browser has already mangled \u00a0 to , so...*/.split(' ').join('\u00a0')); if(needsPreview) P.preview(); else{ /*If it's from the server, it's already rendered, but this gets all labels/headers in sync.*/ P.renderPreview(); } } }/*F.onPageLoad()*/); /** Updates the preview view based on the current preview mode and error state. */ P.renderPreview = function f(){ if(!f.hasOwnProperty('rxNonce')){ f.rxNonce = /<!--.+-->\r?\n?/g /*pikchr nonce comments*/; f.showMarkupAlignment = function(showIt){ P.e.markupAlignWrapper.classList[showIt ? 'remove' : 'add']('hidden'); }; f.getMarkupAlignmentClass = function(){ if(P.e.markupAlignCenter.checked) return ' center'; else if(P.e.markupAlignIndent.checked) return ' indent'; return ''; }; f.getSvgNode = function(txt){ const childs = D.parseHtml(txt); const wrapper = childs.filter((e)=>'DIV'===e.tagName)[0]; return wrapper ? wrapper.querySelector('svg.pikchr') : undefined; }; } const preTgt = this.e.previewTarget; if(this.response.isError){ D.append(D.clearElement(preTgt), D.parseHtml(P.response.raw)); D.addClass(preTgt, 'error'); this.e.previewModeLabel.innerText = "Error"; return; } D.removeClass(preTgt, 'error'); D.removeClass(this.e.previewCopyButton, 'disabled'); D.removeClass(this.e.markupAlignWrapper, 'hidden'); D.enable(this.e.previewModeToggle, this.e.markupAlignRadios); let label, svg; switch(this.previewMode){ case 0: label = "SVG"; f.showMarkupAlignment(false); D.parseHtml(D.clearElement(preTgt), P.response.raw); svg = preTgt.querySelector('svg.pikchr'); if(svg && P.response.rawSvg){ /*for copy button*/ this.e.taPreviewText.value = P.response.rawSvg; F.pikchr.addSrcView(svg); } break; case 1: label = "Markdown"; f.showMarkupAlignment(true); this.e.taPreviewText.value = [ '```pikchr'+f.getMarkupAlignmentClass(), this.response.inputText.trim(), '```' ].join('\n'); D.append(D.clearElement(preTgt), this.e.taPreviewText); break; case 2: label = "Fossil wiki"; f.showMarkupAlignment(true); this.e.taPreviewText.value = [ '<verbatim type="pikchr', f.getMarkupAlignmentClass(), '">', this.response.inputText.trim(), '</verbatim>' ].join(''); D.append(D.clearElement(preTgt), this.e.taPreviewText); break; case 3: label = "Raw SVG"; f.showMarkupAlignment(false); svg = f.getSvgNode(this.response.raw); if(svg){ this.e.taPreviewText.value = P.response.rawSvg || "Error extracting SVG element."; }else{ this.e.taPreviewText.value = "ERROR parsing response HTML:\n"+ this.response.raw; console.error("svg parsed HTML nodes:",childs); } D.append(D.clearElement(preTgt), this.e.taPreviewText); break; } this.e.previewModeLabel.innerText = label; this.e.taContent.focus(/*not sure why this gets lost on preview!*/); }; /** Fetches the preview from the server and updates the preview to the rendered SVG content or error report. */ P.preview = function fp(){ if(!fp.hasOwnProperty('toDisable')){ fp.toDisable = [ /* input elements to disable during ajax operations */ this.e.btnSubmit, this.e.taContent, this.e.cbAutoPreview, this.e.selectScript, this.e.btnStash, this.e.btnClearStash /* handled separately: previewModeToggle, previewCopyButton, markupAlignRadios */ ]; fp.target = this.e.previewTarget; fp.updateView = function(c,isError){ P.previewMode = 0; P.response.raw = c; P.response.rawSvg = getResponseSvg(c); P.response.isError = isError; D.enable(fp.toDisable); P.renderPreview(); }; } D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios); D.addClass(this.e.markupAlignWrapper, 'hidden'); D.addClass(this.e.previewCopyButton, 'disabled'); const content = this.e.taContent.value.trim(); this.response.raw = this.response.rawSvg = undefined; this.response.inputText = content; const sampleScript = fp.$_sampleScript; delete fp.$_sampleScript; if(sampleScript && sampleScript.cached){ fp.updateView(sampleScript.cached, false); return this; } if(!content){ fp.updateView("No pikchr content!",true); return this; } const self = this; const fd = new FormData(); fd.append('ajax', true); fd.append('content',content); F.fetch('pikchrshow',{ payload: fd, responseHeaders: 'x-pikchrshow-is-error', onload: (r,isErrHeader)=>{ const isErr = +isErrHeader ? true : false; if(!isErr && sampleScript){ sampleScript.cached = r; } fp.updateView(r,isErr); }, onerror: (e)=>{ F.fetch.onerror(e); fp.updateView("Error fetching preview: "+e, true); } }); return this; }/*preview()*/; /** Predefined scripts. Each entry is an object: { name: required string, code: optional code string. An entry with no code is treated like a separator in the resulting SELECT element (a disabled OPTION). } */ P.predefinedPiks = [ {name: "-- Example Scripts --"}, /* The following were imported from the pikchr test scripts: https://fossil-scm.org/pikchr/dir/examples */ {name:"Cardinal headings",code:` linerad = 5px C: circle "Center" rad 150% circle "N" at 1.0 n of C; arrow from C to last chop -> circle "NE" at 1.0 ne of C; arrow from C to last chop <- circle "E" at 1.0 e of C; arrow from C to last chop <-> circle "SE" at 1.0 se of C; arrow from C to last chop -> circle "S" at 1.0 s of C; arrow from C to last chop <- circle "SW" at 1.0 sw of C; arrow from C to last chop <-> circle "W" at 1.0 w of C; arrow from C to last chop -> circle "NW" at 1.0 nw of C; arrow from C to last chop <- arrow from 2nd circle to 3rd circle chop arrow from 4th circle to 3rd circle chop arrow from SW to S chop <-> circle "ESE" at 2.0 heading 112.5 from Center \ thickness 150% fill lightblue radius 75% arrow from Center to ESE thickness 150% <-> chop arrow from ESE up 1.35 then to NE chop line dashed <- from E.e to (ESE.x,E.y) line dotted <-> thickness 50% from N to NW chop `},{name:"Core object types",code:`AllObjects: [ # First row of objects box "box" box rad 10px "box (with" "rounded" "corners)" at 1in right of previous circle "circle" at 1in right of previous ellipse "ellipse" at 1in right of previous # second row of objects OVAL1: oval "oval" at 1in below first box oval "(tall &" "thin)" "oval" width OVAL1.height height OVAL1.width \ at 1in right of previous cylinder "cylinder" at 1in right of previous file "file" at 1in right of previous # third row shows line-type objects dot "dot" above at 1in below first oval line right from 1.8cm right of previous "lines" above arrow right from 1.8cm right of previous "arrows" above spline from 1.8cm right of previous \ go right .15 then .3 heading 30 then .5 heading 160 then .4 heading 20 \ then right .15 "splines" at 3rd vertex of previous # The third vertex of the spline is not actually on the drawn # curve. The third vertex is a control point. To see its actual # position, uncomment the following line: #dot color red at 3rd vertex of previous spline # Draw various lines below the first line line dashed right from 0.3cm below start of previous line line dotted right from 0.3cm below start of previous line thin right from 0.3cm below start of previous line thick right from 0.3cm below start of previous # Draw arrows with different arrowhead configurations below # the first arrow arrow <- right from 0.4cm below start of previous arrow arrow <-> right from 0.4cm below start of previous # Draw splines with different arrowhead configurations below # the first spline spline same from .4cm below start of first spline -> spline same from .4cm below start of previous <- spline same from .4cm below start of previous <-> ] # end of AllObjects # Label the whole diagram text "Examples Of Pikchr Objects" big bold at .8cm above north of AllObjects `},{name:"Swimlanes",code:` $laneh = 0.75 # Draw the lanes down box width 3.5in height $laneh fill 0xacc9e3 box same fill 0xc5d8ef box same as first box box same as 2nd box line from 1st box.sw+(0.2,0) up until even with 1st box.n \ "Alan" above aligned line from 2nd box.sw+(0.2,0) up until even with 2nd box.n \ "Betty" above aligned line from 3rd box.sw+(0.2,0) up until even with 3rd box.n \ "Charlie" above aligned line from 4th box.sw+(0.2,0) up until even with 4th box.n \ "Darlene" above aligned # fill in content for the Alice lane right A1: circle rad 0.1in at end of first line + (0.2,-0.2) \ fill white thickness 1.5px "1" arrow right 50% circle same "2" arrow right until even with first box.e - (0.65,0.0) ellipse "future" fit fill white height 0.2 width 0.5 thickness 1.5px A3: circle same at A1+(0.8,-0.3) "3" fill 0xc0c0c0 arrow from A1 to last circle chop "fork!" below aligned # content for the Betty lane B1: circle same as A1 at A1-(0,$laneh) "1" arrow right 50% circle same "2" arrow right until even with first ellipse.w ellipse same "future" B3: circle same at A3-(0,$laneh) "3" arrow right 50% circle same as A3 "4" arrow from B1 to 2nd last circle chop # content for the Charlie lane C1: circle same as A1 at B1-(0,$laneh) "1" arrow 50% circle same "2" arrow right 0.8in "goes" "offline" C5: circle same as A3 "5" arrow right until even with first ellipse.w \ "back online" above "pushes 5" below "pulls 3 & 4" below ellipse same "future" # content for the Darlene lane D1: circle same as A1 at C1-(0,$laneh) "1" arrow 50% circle same "2" arrow right until even with C5.w circle same "5" arrow 50% circle same as A3 "6" arrow right until even with first ellipse.w ellipse same "future" D3: circle same as B3 at B3-(0,2*$laneh) "3" arrow 50% circle same "4" arrow from D1 to D3 chop `} ]; })(window.fossil); |
Added src/fossil.page.pikchrshowasm.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* 2022-05-20 The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: * May you do good and not evil. * May you find forgiveness for yourself and forgive others. * May you share freely, never taking more than you give. *********************************************************************** This is the main entry point for the WASM rendition of fossil's /pikchrshow app. It sets up the various UI bits, loads a Worker for the pikchr process, and manages the communication between the UI and worker. API dependencies: fossil.dom, fossil.copybutton, fossil.storage */ (function(F/*fossil object*/){ 'use strict'; /* Recall that the 'self' symbol, except where locally overwritten, refers to the global window or worker object. */ const D = F.dom; /** Name of the stored copy of this app's config. */ const configStorageKey = 'pikchrshow-config'; /* querySelectorAll() proxy */ const EAll = function(/*[element=document,] cssSelector*/){ return (arguments.length>1 ? arguments[0] : document) .querySelectorAll(arguments[arguments.length-1]); }; /* querySelector() proxy */ const E = function(/*[element=document,] cssSelector*/){ return (arguments.length>1 ? arguments[0] : document) .querySelector(arguments[arguments.length-1]); }; /** The main application object. */ const PS = { /* Config options. */ config: { /* If true, display input/output areas side-by-side, else stack them vertically. */ sideBySide: true, /* If true, swap positions of the input/output areas. */ swapInOut: false, /* If true, the SVG is allowed to resize to fit the parent content area, else the parent is resized to fit the rendered SVG (as sized by pikchr). */ renderAutofit: false, /* If true, automatically render while the user is typing. */ renderWhileTyping: false }, /* Various DOM elements. */ e: { previewCopyButton: E('#preview-copy-button'), previewModeLabel: E('label[for=preview-copy-button]'), zoneInputButtons: E('.zone-wrapper.input > legend > .button-bar'), zoneOutputButtons: E('.zone-wrapper.output > legend > .button-bar'), outText: E('#pikchr-output-text'), pikOutWrapper: E('#pikchr-output-wrapper'), pikOut: E('#pikchr-output'), btnRender: E('#btn-render') }, renderModes: ['svg'/*SVG must be at index 0*/,'markdown', 'wiki', 'text'], renderModeLabels: { svg: 'SVG', markdown: 'Markdown', wiki: 'Fossil Wiki', text: 'Text' }, _msgMap: {}, /** Adds a worker message handler for messages of the given type. */ addMsgHandler: function f(type,callback){ if(Array.isArray(type)){ type.forEach((t)=>this.addMsgHandler(t, callback)); return this; } (this._msgMap.hasOwnProperty(type) ? this._msgMap[type] : (this._msgMap[type] = [])).push(callback); return this; }, /** Given a worker message, runs all handlers for msg.type. */ runMsgHandlers: function(msg){ const list = (this._msgMap.hasOwnProperty(msg.type) ? this._msgMap[msg.type] : false); if(!list){ console.warn("No handlers found for message type:",msg); return false; } list.forEach((f)=>f(msg)); return true; }, /** Removes all message handlers for the given message type. */ clearMsgHandlers: function(type){ delete this._msgMap[type]; return this; }, /* Posts a message in the form {type, data} to the db worker. Returns this. */ wMsg: function(type,data){ this.worker.postMessage({type, data}); return this; }, /** Stores this object's config in the browser's storage. */ storeConfig: function(){ F.storage.setJSON(configStorageKey,this.config); } }; PS.renderModes.selectedIndex = 0; PS._config = F.storage.getJSON(configStorageKey); if(PS._config){ /* Copy all properties to PS.config which are currently in PS._config. We don't bother copying any other properties: those would be stale/removed config entries. */ Object.keys(PS.config).forEach(function(k){ if(PS._config.hasOwnProperty(k)){ PS.config[k] = PS._config[k]; } }); delete PS._config; } PS.worker = new Worker('builtin/extsrc/pikchr-worker.js'); PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data); PS.addMsgHandler('stdout', console.log.bind(console)); PS.addMsgHandler('stderr', console.error.bind(console)); /** Handles status updates from the Module object. */ PS.addMsgHandler('module', function f(ev){ ev = ev.data; if('status'!==ev.type){ console.warn("Unexpected module-type message:",ev); return; } if(!f.ui){ f.ui = { status: E('#module-status'), progress: E('#module-progress'), spinner: E('#module-spinner') }; } const msg = ev.data; if(f.ui.progres){ progress.value = msg.step; progress.max = msg.step + 1/*we don't know how many steps to expect*/; } if(1==msg.step){ f.ui.progress.classList.remove('hidden'); f.ui.spinner.classList.remove('hidden'); } if(msg.text){ f.ui.status.classList.remove('hidden'); f.ui.status.innerText = msg.text; }else{ if(f.ui.progress){ f.ui.progress.remove(); f.ui.spinner.remove(); delete f.ui.progress; delete f.ui.spinner; } f.ui.status.classList.add('hidden'); /* The module can post messages about fatal problems, e.g. an exit() being triggered or assertion failure, after the last "load" message has arrived, so leave f.ui.status and message listener intact. */ } }); PS.e.previewModeLabel.innerText = PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]]; /** The 'pikchr-ready' event is fired (with no payload) when the wasm module has finished loading. */ PS.addMsgHandler('pikchr-ready', function(){ PS.clearMsgHandlers('pikchr-ready'); F.page.onPikchrshowLoaded(); }); /** Performs all app initialization which must wait until after the worker module is loaded. This function removes itself when it's called. */ F.page.onPikchrshowLoaded = function(){ delete this.onPikchrshowLoaded; // Unhide all elements which start out hidden EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden')); const taInput = E('#input'); const btnClearIn = E('#btn-clear'); btnClearIn.addEventListener('click',function(){ taInput.value = ''; },false); const getCurrentText = function(){ let text; if(taInput.selectionStart<taInput.selectionEnd){ text = taInput.value.substring(taInput.selectionStart,taInput.selectionEnd).trim(); }else{ text = taInput.value.trim(); } return text;; }; const renderCurrentText = function(){ const text = getCurrentText(); if(text) PS.render(text); }; const setCurrentText = function(txt){ taInput.value = txt; renderCurrentText(); }; PS.e.btnRender.addEventListener('click',function(ev){ ev.preventDefault(); renderCurrentText(); },false); /** To be called immediately before work is sent to the worker. Updates some UI elements. The 'working'/'end' event will apply the inverse, undoing the bits this function does. This impl is not in the 'working'/'start' event handler because that event is given to us asynchronously _after_ we need to have performed this work. */ const preStartWork = function f(){ if(!f._){ const title = E('title'); f._ = { pageTitle: title, pageTitleOrig: title.innerText }; } //f._.pageTitle.innerText = "[working...] "+f._.pageTitleOrig; PS.e.btnRender.setAttribute('disabled','disabled'); }; /** Submits the current input text to pikchr and renders the result. */ PS.render = function f(txt){ preStartWork(); this.wMsg('pikchr',{ pikchr: txt, darkMode: !!window.fossil.config.skin.isDark }); }; /** Event handler for 'pikchr' messages from the Worker thread. */ PS.addMsgHandler('pikchr', function(ev){ const m = ev.data, pikOut = this.e.pikOut; pikOut.classList[m.isError ? 'add' : 'remove']('error'); pikOut.dataset.pikchr = m.pikchr; const mode = this.renderModes[this.renderModes.selectedIndex]; switch(mode){ case 'text': case 'markdown': case 'wiki': { let body; switch(mode){ case 'markdown': body = ['```pikchr', m.pikchr, '```'].join('\n'); break; case 'wiki': body = ['<verbatim type="pikchr">', m.pikchr, '</verbatim>'].join(''); break; default: body = m.result; } this.e.outText.value = body; this.e.outText.classList.remove('hidden'); pikOut.classList.add('hidden'); this.e.pikOutWrapper.classList.add('text'); break; } case 'svg': this.e.outText.classList.add('hidden'); pikOut.classList.remove('hidden'); this.e.pikOutWrapper.classList.remove('text'); pikOut.innerHTML = m.result; this.e.outText.value = m.result/*for clipboard copy*/; break; default: throw new Error("Unhandled render mode: "+mode); } let vw = null, vh = null; if('svg'===mode){ if(m.isError){ vw = vh = '100%'; }else if(this.config.renderAutofit){ /* FIXME: current behavior doesn't work as desired when width>height (e.g. non-side-by-side mode).*/ vw = vh = '98%'; }else{ vw = m.width+1+'px'; vh = m.height+1+'px'; /* +1 is b/c the SVG uses floating point sizes but pikchr() returns truncated integers. */ } pikOut.style.width = vw; pikOut.style.height = vh; } }.bind(PS))/*'pikchr' msg handler*/; E('#btn-render-mode').addEventListener('click',function(){ const modes = this.renderModes; modes.selectedIndex = (modes.selectedIndex + 1) % modes.length; this.e.previewModeLabel.innerText = this.renderModeLabels[modes[modes.selectedIndex]]; if(this.e.pikOut.dataset.pikchr){ this.render(this.e.pikOut.dataset.pikchr); } }.bind(PS)); F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText}); PS.e.previewModeLabel.addEventListener('click', ()=>PS.e.previewCopyButton.click(), false); PS.addMsgHandler('working',function f(ev){ switch(ev.data){ case 'start': /* See notes in preStartWork(). */; return; case 'end': //preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig; this.e.btnRender.removeAttribute('disabled'); this.e.pikOutWrapper.classList[this.config.renderAutofit ? 'add' : 'remove']('autofit'); return; } console.warn("Unhandled 'working' event:",ev.data); }.bind(PS)); /* For each checkbox with data-csstgt, set up a handler which toggles the given CSS class on the element matching E(data-csstgt). */ EAll('input[type=checkbox][data-csstgt]') .forEach(function(e){ const tgt = E(e.dataset.csstgt); const cssClass = e.dataset.cssclass || 'error'; e.checked = tgt.classList.contains(cssClass); e.addEventListener('change', function(){ tgt.classList[ this.checked ? 'add' : 'remove' ](cssClass) }, false); }); /* For each checkbox with data-config=X, set up a binding to PS.config[X]. These must be set up AFTER data-csstgt checkboxes so that those two states can be synced properly. */ EAll('input[type=checkbox][data-config]') .forEach(function(e){ const confVal = !!PS.config[e.dataset.config]; if(e.checked !== confVal){ /* Ensure that data-csstgt mappings (if any) get synced properly. */ e.checked = confVal; e.dispatchEvent(new Event('change')); } e.addEventListener('change', function(){ PS.config[this.dataset.config] = this.checked; PS.storeConfig(); }, false); }); E('#opt-cb-autofit').addEventListener('change',function(){ /* PS.config.renderAutofit was set by the data-config event handler. */ if(0==PS.renderModes.selectedIndex && PS.e.pikOut.dataset.pikchr){ PS.render(PS.e.pikOut.dataset.pikchr); } }); /* For each button with data-cmd=X, map a click handler which calls PS.render(X). */ const cmdClick = function(){PS.render(this.dataset.cmd);}; EAll('button[data-cmd]').forEach( e => e.addEventListener('click', cmdClick, false) ); //////////////////////////////////////////////////////////// // Set up selection list of predefined scripts... if(true){ const selectScript = PS.e.selectScript = D.select(); D.append(PS.e.zoneInputButtons, selectScript); PS.predefinedPiks.forEach(function(script,ndx){ const opt = D.option(script.code ? script.code.trim() :'', script.name); D.append(selectScript, opt); if(!ndx) selectScript.selectedIndex = 0 /*timing/ordering workaround*/; if(ndx && !script.code){ /* Treat entries w/ no code as separators EXCEPT for the first one, which we want to keep selectable solely for cosmetic reasons. */ D.disable(opt); } }); delete PS.predefinedPiks; selectScript.addEventListener('change', function(ev){ const val = ev.target.value; if(!val) return; setCurrentText(val); }, false); }/*Examples*/ /** TODO? Handle load/import of an external pikchr file. */ if(0) E('#load-pikchr').addEventListener('change',function(){ const f = this.files[0]; const r = new FileReader(); const status = {loaded: 0, total: 0}; this.setAttribute('disabled','disabled'); const that = this; r.addEventListener('load', function(){ that.removeAttribute('disabled'); stdout("Loaded",f.name+". Opening pikchr..."); PS.wMsg('open',{ filename: f.name, buffer: this.result }); }); r.addEventListener('error',function(){ that.removeAttribute('disabled'); stderr("Loading",f.name,"failed for unknown reasons."); }); r.addEventListener('abort',function(){ that.removeAttribute('disabled'); stdout("Cancelled loading of",f.name+"."); }); r.readAsArrayBuffer(f); }); EAll('fieldset.collapsible').forEach(function(fs){ const btnToggle = E(fs,'legend > .fieldset-toggle'), content = EAll(fs,':scope > div'); btnToggle.addEventListener('click', function(){ fs.classList.toggle('collapsed'); content.forEach((d)=>d.classList.toggle('hidden')); }, false); }); PS.e.btnRender.click(); /** Debounce handler for auto-rendering while typing. */ const debounceAutoRender = F.debounce(function f(){ if(!PS._isDirty) return; const text = getCurrentText(); if(f._ === text){ PS._isDirty = false; return; } f._ = text; PS._isDirty = false; PS.render(text || ''); }, 800, false); taInput.addEventListener('keydown',function f(ev){ if((ev.ctrlKey || ev.shiftKey) && 13 === ev.keyCode){ // Ctrl-enter and shift-enter both run the current input PS._isDirty = false/*prevent a pending debounce from re-rendering*/; ev.preventDefault(); ev.stopPropagation(); renderCurrentText(); return; } if(!PS.config.renderWhileTyping) return; /* Auto-render while typing... */ switch(ev.keyCode){ case (ev.keyCode<32): /*any ctrl char*/ /* ^^^ w/o that, simply tapping ctrl is enough to force a re-render. Similarly, TAB-ing focus away should not re-render. */ case 33: case 34: /* page up/down */ case 35: case 36: /* home/end */ case 37: case 38: case 39: case 40: /* arrows */ return; } PS._isDirty = true; debounceAutoRender(); }, false); const ForceResizeKludge = (function(){ /* Workaround for Safari mayhem regarding use of vh CSS units.... We cannot use vh units to set the main view size because Safari chokes on that, so we calculate that height here. Larger than ~95% is too big for Firefox on Android, causing the input area to move off-screen. */ const appViews = EAll('.app-view'); const elemsToCount = [ /* Elements which we need to always count in the visible body size. */ E('body > header'), E('body > nav.mainmenu'), E('body > footer') ]; const resized = function f(){ if(f.$disabled) return; const wh = window.innerHeight; var ht; var extra = 0; elemsToCount.forEach((e)=>e ? extra += F.dom.effectiveHeight(e) : false); ht = wh - extra; appViews.forEach(function(e){ e.style.height = e.style.maxHeight = [ "calc(", (ht>=100 ? ht : 100), "px", " - 2em"/*fudge value*/,")" /* ^^^^ hypothetically not needed, but both Chrome/FF on Linux will force scrollbars on the body if this value is too small. */ ].join(''); }); }; resized.$disabled = true/*gets deleted when setup is finished*/; window.addEventListener('resize', F.debounce(resized, 250), false); return resized; })()/*ForceResizeKludge*/; delete ForceResizeKludge.$disabled; ForceResizeKludge(); }/*onPikchrshowLoaded()*/; /** Predefined scripts. Each entry is an object: { name: required string, code: optional code string. An entry with a falsy code is treated like a separator in the resulting SELECT element (a disabled OPTION). } */ PS.predefinedPiks = [ {name: "-- Example Scripts --", code: false}, /* The following were imported from the pikchr test scripts: https://fossil-scm.org/pikchr/dir/examples */ {name:"Cardinal headings",code:` linerad = 5px C: circle "Center" rad 150% circle "N" at 1.0 n of C; arrow from C to last chop -> circle "NE" at 1.0 ne of C; arrow from C to last chop <- circle "E" at 1.0 e of C; arrow from C to last chop <-> circle "SE" at 1.0 se of C; arrow from C to last chop -> circle "S" at 1.0 s of C; arrow from C to last chop <- circle "SW" at 1.0 sw of C; arrow from C to last chop <-> circle "W" at 1.0 w of C; arrow from C to last chop -> circle "NW" at 1.0 nw of C; arrow from C to last chop <- arrow from 2nd circle to 3rd circle chop arrow from 4th circle to 3rd circle chop arrow from SW to S chop <-> circle "ESE" at 2.0 heading 112.5 from Center \ thickness 150% fill lightblue radius 75% arrow from Center to ESE thickness 150% <-> chop arrow from ESE up 1.35 then to NE chop line dashed <- from E.e to (ESE.x,E.y) line dotted <-> thickness 50% from N to NW chop `},{name:"Core object types",code:`AllObjects: [ # First row of objects box "box" box rad 10px "box (with" "rounded" "corners)" at 1in right of previous circle "circle" at 1in right of previous ellipse "ellipse" at 1in right of previous # second row of objects OVAL1: oval "oval" at 1in below first box oval "(tall &" "thin)" "oval" width OVAL1.height height OVAL1.width \ at 1in right of previous cylinder "cylinder" at 1in right of previous file "file" at 1in right of previous # third row shows line-type objects dot "dot" above at 1in below first oval line right from 1.8cm right of previous "lines" above arrow right from 1.8cm right of previous "arrows" above spline from 1.8cm right of previous \ go right .15 then .3 heading 30 then .5 heading 160 then .4 heading 20 \ then right .15 "splines" at 3rd vertex of previous # The third vertex of the spline is not actually on the drawn # curve. The third vertex is a control point. To see its actual # position, uncomment the following line: #dot color red at 3rd vertex of previous spline # Draw various lines below the first line line dashed right from 0.3cm below start of previous line line dotted right from 0.3cm below start of previous line thin right from 0.3cm below start of previous line thick right from 0.3cm below start of previous # Draw arrows with different arrowhead configurations below # the first arrow arrow <- right from 0.4cm below start of previous arrow arrow <-> right from 0.4cm below start of previous # Draw splines with different arrowhead configurations below # the first spline spline same from .4cm below start of first spline -> spline same from .4cm below start of previous <- spline same from .4cm below start of previous <-> ] # end of AllObjects # Label the whole diagram text "Examples Of Pikchr Objects" big bold at .8cm above north of AllObjects `},{name:"Swimlanes",code:` $laneh = 0.75 # Draw the lanes down box width 3.5in height $laneh fill 0xacc9e3 box same fill 0xc5d8ef box same as first box box same as 2nd box line from 1st box.sw+(0.2,0) up until even with 1st box.n \ "Alan" above aligned line from 2nd box.sw+(0.2,0) up until even with 2nd box.n \ "Betty" above aligned line from 3rd box.sw+(0.2,0) up until even with 3rd box.n \ "Charlie" above aligned line from 4th box.sw+(0.2,0) up until even with 4th box.n \ "Darlene" above aligned # fill in content for the Alice lane right A1: circle rad 0.1in at end of first line + (0.2,-0.2) \ fill white thickness 1.5px "1" arrow right 50% circle same "2" arrow right until even with first box.e - (0.65,0.0) ellipse "future" fit fill white height 0.2 width 0.5 thickness 1.5px A3: circle same at A1+(0.8,-0.3) "3" fill 0xc0c0c0 arrow from A1 to last circle chop "fork!" below aligned # content for the Betty lane B1: circle same as A1 at A1-(0,$laneh) "1" arrow right 50% circle same "2" arrow right until even with first ellipse.w ellipse same "future" B3: circle same at A3-(0,$laneh) "3" arrow right 50% circle same as A3 "4" arrow from B1 to 2nd last circle chop # content for the Charlie lane C1: circle same as A1 at B1-(0,$laneh) "1" arrow 50% circle same "2" arrow right 0.8in "goes" "offline" C5: circle same as A3 "5" arrow right until even with first ellipse.w \ "back online" above "pushes 5" below "pulls 3 & 4" below ellipse same "future" # content for the Darlene lane D1: circle same as A1 at C1-(0,$laneh) "1" arrow 50% circle same "2" arrow right until even with C5.w circle same "5" arrow 50% circle same as A3 "6" arrow right until even with first ellipse.w ellipse same "future" D3: circle same as B3 at B3-(0,2*$laneh) "3" arrow 50% circle same "4" arrow from D1 to D3 chop `},{ name: "The Stuff of Dreams", code:` O: text "DREAMS" color grey circle rad 0.9 at 0.6 above O thick color red text "INEXPENSIVE" big bold at 0.9 above O color red circle rad 0.9 at 0.6 heading 120 from O thick color green text "FAST" big bold at 0.9 heading 120 from O color green circle rad 0.9 at 0.6 heading -120 from O thick color blue text "HIGH" big bold "QUALITY" big bold at 0.9 heading -120 from O color blue text "EXPENSIVE" at 0.55 below O color cyan text "SLOW" at 0.55 heading -60 from O color magenta text "POOR" "QUALITY" at 0.55 heading 60 from O color gold `},{name:"Precision Arrows",code:` # Source: https://pikchr.org/home/forumpost/7f2f9a03eb define quiver { dot invis at 0.5 < $1.ne , $1.e > dot invis at 0.5 < $1.nw , $1.w > dot invis at 0.5 < $1.se , $1.e > dot invis at 0.5 < $1.sw , $1.w > dot at $2 right of 4th previous dot dot at $3 right of 4th previous dot dot at $4 right of 4th previous dot dot at $5 right of 4th previous dot arrow <- from previous dot to 2nd previous dot arrow -> from 3rd previous dot to 4th previous dot } define show_compass_l { dot color red at $1.e " .e" ljust dot same at $1.ne " .ne" ljust above line thick color green from previous to 2nd last dot } define show_compass_r { dot color red at $1.w " .w" ljust dot same at $1.nw " .nw" ljust above line thick color green from previous to 2nd last dot } PROGRAM: file "Program" rad 45px show_compass_l(PROGRAM) QUIVER: box invis ht 0.75 DATABASE: oval "Database" ht 0.75 wid 1.1 show_compass_r(DATABASE) quiver(QUIVER, 5px, -5px, 5px, 0px) text "Query" with .c at 0.1in above last arrow text "Records" with .c at 0.1in below 2nd last arrow `} ]; })(window.fossil); |
Added src/fossil.page.whistory.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* This script adds interactivity for wiki-history webpages. * * The main code is within the 'on-click' handler of the "diff" links. * Instead of standard redirection it fills-in two hidden inputs with * the appropriate values and submits the corresponding form. * A special care should be taken if some intermediate edits are hidden. * * For the sake of compatibility with ascetic browsers the code tries * to avoid modern API and ECMAScript constructs. This makes it less * readable and may be reconsidered in the future. */ window.addEventListener( 'load', function() { var form = document.getElementById("wh-form"); form.method = "GET"; var csrf = form.querySelector("input[name='csrf']"); if( csrf ) form.removeChild( csrf ); var wh_id = document.getElementById("wh-id" ); var wh_pid = document.getElementById("wh-pid"); var wh_cleaner = document.getElementById("wh-cleaner"); var wh_collapser = document.getElementById("wh-collapser"); var wh_radios = []; // user-visible controls for baseline selection var wh_hidden = 0; // current number of hidden (collapsed) rows var wh_selected = -1; // index of the currently selected radio-button var wh_onRadio = function( event ){ var indx = event.target.indx; if( wh_selected == indx ){ wh_selected = -1; event.target.checked = false; } else wh_selected = indx; } var wh_onDifflink = function( event ){ event.preventDefault(); var indx = event.target.indx; wh_id.value = wh_radios[indx].value; if( wh_hidden > 0 ){ var p = indx + 1; if( wh_selected >= 0 ){ var tr = wh_radios[wh_selected].parentElement.parentElement; if( ! tr.hidden ) p = wh_selected; } while( p < wh_radios.length ){ if( ! wh_radios[p].parentElement.parentElement.hidden ) break; p++; } if( p < wh_radios.length ){ wh_pid.value = wh_radios[p].value; wh_pid.checked = true; } else { // just render the wiki for the case of the first major edit var path = document.location.pathname.split("/"); path.pop(); var newpath = path.join("/") + "/info/" + wh_radios[indx].value; document.location = document.location.origin + newpath; return; } } else if( wh_selected >= 0 ) { wh_pid.value = wh_radios[wh_selected].value; wh_pid.checked = true; } else wh_pid.checked = false; document.getElementById("wh-form").submit(); } var wh_onCleaner = function() { if( wh_selected >= 0 ) { wh_radios[wh_selected].checked = false; wh_selected = -1; } } var wh_onCollapser = function( event ){ var collapsing = ( wh_hidden == 0 ); for( var k = 0; k < wh_radios.length; k++ ){ var radio = wh_radios[k]; var tr = radio.parentElement.parentElement; if( tr.className == "wh-intermediate" ){ if( tr.hidden = ! tr.hidden ) wh_hidden++; else wh_hidden--; } else if( radio.iterspan ) radio.iterspan.hidden = ! collapsing; } if( wh_hidden > 0 ) { wh_collapser.title="Show intermediate edits"; wh_collapser.innerHTML = " ♻ " + wh_hidden; } else { wh_collapser.title="Hide intermediate edits"; wh_collapser.innerHTML = " ♲" } } var inputs = document.getElementsByTagName("input"); for( var k = 0, indx = 0; k < inputs.length; k++ ) { var r = inputs[k]; if( r.type == "radio" && r.name == "baseline" ) { wh_radios.push( r ); r.indx = indx++; r.addEventListener( "click", wh_onRadio ); r.disabled = false; var td = r.parentElement.nextElementSibling; r.iterspan = td.getElementsByTagName("span")[0]; } } for( var edits = 0, k = wh_radios.length - 1; k >= 0; k-- ) { var td = wh_radios[k].parentElement.nextElementSibling; if( td.parentElement.className == "wh-intermediate" ) edits++; else if( edits > 0 ){ var span = td.getElementsByTagName("span")[0]; span.innerHTML = " ♲" + edits; wh_radios[k].iterspan = span; edits = 0; // also: ∪ (union) Σ (sigma) × (times) } } var links = document.getElementsByTagName("a"); for( var i = 0, indx = 0; i < links.length; i++ ) { var l = links[i]; if( l.className == "wh-difflink" ){ l.indx = indx++; l.addEventListener( "click", wh_onDifflink ); } } wh_cleaner.addEventListener( "click", wh_onCleaner ); wh_collapser.addEventListener( "click", wh_onCollapser ); wh_collapser.title="Hide intermediate edits"; wh_collapser.hidden = false; }); // window.addEventListener( 'load' ... |
Added src/fossil.page.wikiedit.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (function(F/*the fossil object*/){ "use strict"; /** Client-side implementation of the /wikiedit app. Requires that the fossil JS bootstrapping is complete and that several fossil JS APIs have been installed: fossil.fetch, fossil.dom, fossil.tabs, fossil.storage, fossil.confirmer, fossil.popupwidget. Custom events which can be listened for via fossil.page.addEventListener(): - Event 'wiki-page-loaded': passes on information when it loads a wiki (whether from the network or its internal local-edit cache), in the form of an "winfo" object: { name: string, mimetype: mimetype string, type: "normal" | "tag" | "checkin" | "branch" | "sandbox", version: UUID string or null for a sandbox page or new page, parent: parent UUID string or null if no parent, isEmpty: true if page has no content (is "deleted"). content: string, optional in most contexts } The internal docs and code frequently use the term "winfo", and such references refer to an object with that form. The fossil.page.wikiContent() method gets or sets the current file content for the page. - Event 'wiki-saved': is fired when a commit completes, passing on the same info as wiki-page-loaded. - Event 'wiki-content-replaced': when the editor's content is replaced, as opposed to it being edited via user interaction. This normally happens via selecting a file to load. The event detail is the fossil.page object, not the current file content. - Event 'wiki-preview-updated': when the preview is refreshed from the server, this event passes on information about the preview change in the form of an object: { element: the DOM element which contains the content preview. mimetype: the page's mimetype. } Here's an example which can be used with the highlightjs code highlighter to update the highlighting when the preview is refreshed in "wiki" mode (which includes fossil-native wiki and markdown): fossil.page.addEventListener( 'wiki-preview-updated', (ev)=>{ if(ev.detail.mimetype!=='text/plain'){ ev.detail.element.querySelectorAll( 'code[class^=language-]' ).forEach((e)=>hljs.highlightBlock(e)); } } ); */ const E = (s)=>document.querySelector(s), D = F.dom, P = F.page; P.config = { /* Max number of locally-edited pages to stash, after which we drop the least-recently used. */ defaultMaxStashSize: 10, useConfirmerButtons:{ /* If true during fossil.page setup, certain buttons will use a "confirmer" step, else they will not. The confirmer topic has been the source of much contention in the forum. */ save: false, reload: true, discardStash: true }, /** If true, a keyboard combo of shift-enter (from the editor) toggles between preview and edit modes. This is normally desired but at least one software keyboard is known to misinteract with this, treating an Enter after automatically-capitalized letters as a shift-enter: https://fossil-scm.org/forum/forumpost/dbd5b68366147ce8 Maintenance note: /fileedit also uses this same key for the same purpose. */ shiftEnterPreview: F.storage.getBool('edit-shift-enter-preview', true) }; /** $stash is an internal-use-only object for managing "stashed" local edits, to help avoid that users accidentally lose content by switching tabs or following links or some such. The basic theory of operation is... All "stashed" state is stored using fossil.storage. - When the current wiki content is modified by the user, the current state of the page is stashed. - When saving, the stashed entry for the previous version is removed from the stash. - When "loading", we use any stashed state for the given checkin/file combination. When forcing a re-load of content, any stashed entry for that combination is removed from the stash. - Every time P.stashContentChange() updates the stash, it is pruned to $stash.prune.defaultMaxCount most-recently-updated entries. - This API often refers to "winfo objects." Those are objects with a minimum of {page,mimetype} properties (which must be valid), and the page name is used as basis for the stash keys for any given page. The structure of the stash is a bit convoluted for efficiency's sake: we store a map of file info (winfo) objects separately from those files' contents because otherwise we would be required to JSONize/de-JSONize the file content when stashing/restoring it, and that would be horribly inefficient (meaning "battery-consuming" on mobile devices). */ const $stash = { keys: { index: F.page.name+'.index' }, /** index: { "PAGE_NAME": {wiki page info w/o content} ... } In F.storage we... - Store this.index under the key this.keys.index. - Store each page's content under the key (P.name+'/PAGE_NAME'). These are stored separately from the index entries to avoid having to JSONize/de-JSONize the content. The assumption/hope is that the browser can store those records "directly," without any intermediary encoding/decoding going on. */ indexKey: function(winfo){return winfo.name}, /** Returns the key for storing content for the given key suffix, by prepending P.name to suffix. */ contentKey: function(suffix){return P.name+'/'+suffix}, /** Returns the index object, fetching it from the stash or creating it anew on the first call. */ getIndex: function(){ if(!this.index){ this.index = F.storage.getJSON( this.keys.index, {} ); } return this.index; }, _fireStashEvent: function(){ if(this._disableNextEvent) delete this._disableNextEvent; else F.page.dispatchEvent('wiki-stash-updated', this); }, /** Returns the stashed version, if any, for the given winfo object. */ getWinfo: function(winfo){ const ndx = this.getIndex(); return ndx[this.indexKey(winfo)]; }, /** Serializes this object's index to F.storage. Returns this. */ storeIndex: function(){ if(this.index) F.storage.setJSON(this.keys.index,this.index); return this; }, /** Updates the stash record for the given winfo and (optionally) content. If passed 1 arg, only the winfo stash is updated, else both the winfo and its contents are (re-)stashed. Returns this. */ updateWinfo: function(winfo,content){ const ndx = this.getIndex(), key = this.indexKey(winfo), old = ndx[key]; const record = old || (ndx[key]={ name: winfo.name }); record.mimetype = winfo.mimetype; record.type = winfo.type; record.parent = winfo.parent; record.version = winfo.version; record.stashTime = new Date().getTime(); record.isEmpty = !!winfo.isEmpty; record.attachments = winfo.attachments; this.storeIndex(); if(arguments.length>1){ if(content) delete record.isEmpty; F.storage.set(this.contentKey(key), content); } this._fireStashEvent(); return this; }, /** Returns the stashed content, if any, for the given winfo object. */ stashedContent: function(winfo){ return F.storage.get(this.contentKey(this.indexKey(winfo))); }, /** Returns true if we have stashed content for the given winfo record or page name. */ hasStashedContent: function(winfo){ if('string'===typeof winfo) winfo = {name: winfo}; return F.storage.contains(this.contentKey(this.indexKey(winfo))); }, /** Unstashes the given winfo record and its content. Returns this. */ unstash: function(winfo){ const ndx = this.getIndex(), key = this.indexKey(winfo); delete winfo.stashTime; delete ndx[key]; F.storage.remove(this.contentKey(key)); this.storeIndex(); this._fireStashEvent(); return this; }, /** Clears all $stash entries from F.storage. Returns this. */ clear: function(){ const ndx = this.getIndex(), self = this; let count = 0; Object.keys(ndx).forEach(function(k){ ++count; const e = ndx[k]; delete ndx[k]; F.storage.remove(self.contentKey(k)); }); F.storage.remove(this.keys.index); delete this.index; if(count) this._fireStashEvent(); return this; }, /** Removes all but the maxCount most-recently-updated stash entries, where maxCount defaults to this.prune.defaultMaxCount. */ prune: function f(maxCount){ const ndx = this.getIndex(); const li = []; if(!maxCount || maxCount<0) maxCount = f.defaultMaxCount; Object.keys(ndx).forEach((k)=>li.push(ndx[k])); li.sort((l,r)=>l.stashTime - r.stashTime); let n = 0; while(li.length>maxCount){ ++n; const e = li.shift(); this._disableNextEvent = true; this.unstash(e); console.warn("Pruned oldest local file edit entry:",e); } if(n) this._fireStashEvent(); } }; $stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10; P.$stash = $stash /* we have to expose this for the new-page case :/ */; /** Internal workaround to select the current preview mode and fire a change event if the value actually changes or if forceEvent is truthy. */ P.selectMimetype = function(modeValue, forceEvent){ const s = this.e.selectMimetype; if(!modeValue) modeValue = s.value; else if(s.value != modeValue){ s.value = modeValue; forceEvent = true; } if(forceEvent){ // Force UI update s.dispatchEvent(new Event('change',{target:s})); } }; /** Internal helper to get an edit status indicator for the given winfo object. Pass it a winfo object or one of the "constants" which are assigned as member properties of this function (see below its definition). */ const getEditMarker = function f(winfo, textOnly){ const esm = F.config.editStateMarkers; if(f.NEW===winfo){ /* force is-new */ return textOnly ? esm.isNew : D.addClass(D.append(D.span(),esm.isNew), 'is-new'); }else if(f.MODIFIED===winfo){ /* force is-modified */ return textOnly ? esm.isModified : D.addClass(D.append(D.span(),esm.isModified), 'is-modified'); }else if(f.DELETED===winfo){/* force is-deleted */ return textOnly ? esm.isDeleted : D.addClass(D.append(D.span(),esm.isDeleted), 'is-deleted'); }else if(winfo && winfo.version){ /* is existing page modified? */ if($stash.getWinfo(winfo)){ return textOnly ? esm.isModified : D.addClass(D.append(D.span(),esm.isModified), 'is-modified'); } /*fall through*/ } else if(winfo){ /* is new non-sandbox or is modified sandbox? */ if('sandbox'!==winfo.type){ return textOnly ? esm.isNew : D.addClass(D.append(D.span(),esm.isNew), 'is-new'); }else if($stash.getWinfo(winfo)){ return textOnly ? esm.isModified : D.addClass(D.append(D.span(),esm.isModified), 'is-modified'); } } return textOnly ? '' : D.span(); }; getEditMarker.NEW = 1; getEditMarker.MODIFIED = 2; getEditMarker.DELETED = 3; /** Returns undefined if winfo is falsy, true if the given winfo object appears to be "new", else returns false. */ const winfoIsNew = function(winfo){ if(!winfo) return undefined; else if('sandbox' === winfo.type) return false; else return !winfo.version; }; /** Sets up and maintains the widgets for the list of wiki pages. */ const WikiList = { e: { filterCheckboxes: { /*map of wiki page type to checkbox for list filtering purposes, except for "sandbox" type, which is assumed to be covered by the "normal" type filter. */}, }, cache: { pageList: [], optByName:{/*map of page names to OPTION object, to speed up certain operations.*/}, names: { /* Map of page names to "something." We don't map to their winfo bits because those regularly get swapped out via de/serialization. We need this map to support the add-new-page feature, to give us a way to check for dupes without asking the server or walking through the whole selection list. */} }, /** Updates OPTION elements to reflect whether the page has local changes or is new/unsaved. This implementation is horribly inefficient, in that we have to walk and validate the whole list for each stash-level change. If passed an argument, it is assumed to be an OPTION element and only that element is updated, else all OPTION elements in this.e.select are updated. Reminder to self: in order to mark is-edited/is-new state we have to update the OPTION element's inner text to reflect the is-modified/is-new flags, rather than use CSS classes to tag them, because mobile Chrome can neither restyle OPTION elements no render ::before content on them. We *also* use CSS tags, but they aren't sufficient for the mobile browsers. */ _refreshStashMarks: function callee(option){ if(!callee.eachOpt){ const self = this; callee.eachOpt = function(keyOrOpt){ const opt = 'string'===typeof keyOrOpt ? self.e.select.options[keyOrOpt] : keyOrOpt; const stashed = $stash.getWinfo({name:opt.value}); var prefix = ''; D.removeClass(opt, 'stashed', 'stashed-new', 'deleted'); if(stashed){ const isNew = winfoIsNew(stashed); prefix = getEditMarker(isNew ? getEditMarker.NEW : getEditMarker.MODIFIED, true); D.addClass(opt, isNew ? 'stashed-new' : 'stashed'); D.removeClass(opt, 'deleted'); }else if(opt.dataset.isDeleted){ prefix = getEditMarker(getEditMarker.DELETED,true); D.addClass(opt, 'deleted'); } opt.innerText = prefix + opt.value; self.cache.names[opt.value] = true; }; } if(arguments.length){ callee.eachOpt(option); }else{ this.cache.names = {/*must reset it to acount for local page removals*/}; Object.keys(this.e.select.options).forEach(callee.eachOpt); } }, /** Removes the given wiki page entry from the page selection list, if it's in the list. */ removeEntry: function(name){ const sel = this.e.select; var ndx = sel.selectedIndex; sel.value = name; if(sel.selectedIndex>-1){ if(ndx === sel.selectedIndex) ndx = -1; sel.options.remove(sel.selectedIndex); } sel.selectedIndex = ndx; delete this.cache.names[name]; delete this.cache.optByName[name]; this.cache.pageList = this.cache.pageList.filter((wi)=>name !== wi.name); }, /** Rebuilds the selection list. Necessary when it's loaded from the server, we locally create a new page, or we remove a locally-created new page. */ _rebuildList: function callee(){ /* Jump through some hoops to integrate new/unsaved pages into the list of existing pages... We use a map as an intermediary in order to filter out any local-stash dupes from server-side copies. */ const list = this.cache.pageList; if(!list) return; if(!callee.sorticase){ callee.sorticase = function(l,r){ if(l===r) return 0; l = l.toLowerCase(); r = r.toLowerCase(); return l<=r ? -1 : 1; }; } const map = {}, ndx = $stash.getIndex(), sel = this.e.select; D.clearElement(sel); list.forEach((winfo)=>map[winfo.name] = winfo); Object.keys(ndx).forEach(function(key){ const winfo = ndx[key]; if(!winfo.version/*new page*/) map[winfo.name] = winfo; }); const self = this; Object.keys(map) .sort(callee.sorticase) .forEach(function(name){ const winfo = map[name]; const opt = D.option(sel, winfo.name); const wtype = opt.dataset.wtype = winfo.type==='sandbox' ? 'normal' : (winfo.type||'normal'); const cb = self.e.filterCheckboxes[wtype]; self.cache.optByName[winfo.name] = opt; if(cb && !cb.checked) D.addClass(opt, 'hidden'); if(winfo.isEmpty){ opt.dataset.isDeleted = true; } self._refreshStashMarks(opt); }); D.enable(sel); if(P.winfo) sel.value = P.winfo.name; }, /** Loads the page list and populates the selection list. */ loadList: function callee(){ if(!callee.onload){ const self = this; callee.onload = function(list){ self.cache.pageList = list; self._rebuildList(); F.message("Loaded page list."); }; } if(P.initialPageList){ /* ^^^ injected at page-creation time. */ const list = P.initialPageList; delete P.initialPageList; callee.onload(list); }else{ F.fetch('wikiajax/list',{ urlParams:{verbose:true}, responseType: 'json', onload: callee.onload }); } return this; }, /** Returns true if the given name appears to be a valid wiki page name, noting that the final arbitrator is the server. On validation error it emits a message via fossil.error() and returns false. */ validatePageName: function(name){ var err; if(!name){ err = "may not be empty"; }else if(this.cache.names.hasOwnProperty(name)){ err = "page already exists: "+name; }else if(name.length>100){ err = "too long (limit is 100)"; }else if(/\s{2,}/.test(name)){ err = "multiple consecutive spaces"; }else if(/[\t\r\n]/.test(name)){ err = "contains control character(s)"; }else{ let i = 0, n = name.length, c; for( ; i < n; ++i ){ if(name.charCodeAt(i)<0x20){ err = "contains control character(s)"; break; } } } if(err){ F.error("Invalid name:",err); } return !err; }, /** If the given name is valid, a new page with that (trimmed) name is added to the local stash. */ addNewPage: function(name){ name = name.trim(); if(!this.validatePageName(name)) return false; var wtype = 'normal'; if(0===name.indexOf('checkin/')) wtype = 'checkin'; else if(0===name.indexOf('branch/')) wtype = 'branch'; else if(0===name.indexOf('tag/')) wtype = 'tag'; /* ^^^ note that we're not validating that, e.g., checkin/XYZ has a full artifact ID after "checkin/". */ const winfo = { name: name, type: wtype, mimetype: 'text/x-markdown', version: null, parent: null }; this.cache.pageList.push( winfo/*keeps entry from getting lost from the list on save*/ ); $stash.updateWinfo(winfo, ''); this._rebuildList(); P.loadPage(winfo.name); return true; }, /** Installs a wiki page selection list into the given parent DOM element and loads the page list from the server. */ init: function(parentElem){ const sel = D.select(), btn = D.addClass(D.button("Reload page list"), 'save'); this.e.select = sel; D.addClass(parentElem, 'WikiList'); D.clearElement(parentElem); D.append( parentElem, D.append(D.fieldset("Select a page to edit"), sel) ); D.attr(sel, 'size', 12); D.option(D.disable(D.clearElement(sel)), undefined, "Loading..."); /** Set up filter checkboxes for the various types of wiki pages... */ const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"), fsFilterBody = D.div(), filters = ['normal', 'branch/...', 'tag/...', 'checkin/...'] ; D.append(fsFilter, fsFilterBody); D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch'); // Add filters by page type... const self = this; const filterByType = function(wtype, show){ sel.querySelectorAll('option[data-wtype='+wtype+']').forEach(function(opt){ if(show) opt.classList.remove('hidden'); else opt.classList.add('hidden'); }); }; filters.forEach(function(label){ const wtype = label.split('/')[0]; const cbId = 'wtype-filter-'+wtype, lbl = D.attr(D.append(D.label(),label), 'for', cbId), cb = D.attr(D.input('checkbox'), 'id', cbId); D.append(fsFilterBody, D.append(D.span(), cb, lbl)); self.e.filterCheckboxes[wtype] = cb; cb.checked = true; filterByType(wtype, cb.checked); cb.addEventListener( 'change', function(ev){filterByType(wtype, ev.target.checked)}, false ); }); { /* add filter for "deleted" pages */ const cbId = 'wtype-filter-deleted', lbl = D.attr(D.append(D.label(), getEditMarker(getEditMarker.DELETED,false), 'deleted'), 'for', cbId), cb = D.attr(D.input('checkbox'), 'id', cbId); cb.checked = false; D.addClass(parentElem,'hide-deleted'); D.attr(lbl); const deletedTip = F.helpButtonlets.create( D.span(), 'Fossil considers empty pages to be "deleted" in some contexts.' ); D.append(fsFilterBody, D.append( D.span(), cb, lbl, deletedTip )); cb.addEventListener( 'change', function(ev){ if(ev.target.checked) D.removeClass(parentElem,'hide-deleted'); else D.addClass(parentElem,'hide-deleted'); }, false); } /* A legend of the meanings of the symbols we use in the OPTION elements to denote certain state. */ const fsLegend = D.fieldset("Edit status"), fsLegendBody = D.div(); D.append(fsLegend, fsLegendBody); D.addClass(fsLegendBody, 'flex-container', 'flex-column', 'stretch'); D.append( fsLegendBody, D.append(D.span(), getEditMarker(getEditMarker.NEW,false)," = new/unsaved"), D.append(D.span(), getEditMarker(getEditMarker.MODIFIED,false)," = has local edits"), D.append(D.span(), getEditMarker(getEditMarker.DELETED,false)," = is empty (deleted)") ); const fsNewPage = D.fieldset("Create new page"), fsNewPageBody = D.div(), newPageName = D.input('text'), newPageBtn = D.button("Add page locally") ; D.append(parentElem, fsNewPage); D.append(fsNewPage, fsNewPageBody); D.addClass(fsNewPageBody, 'flex-container', 'flex-column', 'new-page'); D.append( fsNewPageBody, newPageName, newPageBtn, D.append(D.addClass(D.span(), 'mini-tip'), "New pages exist only in this browser until they are saved.") ); newPageBtn.addEventListener('click', function(){ if(self.addNewPage(newPageName.value)){ newPageName.value = ''; } }, false); D.append( parentElem, D.append(D.addClass(D.div(), 'fieldset-wrapper'), fsFilter, fsNewPage, fsLegend) ); D.append(parentElem, btn); btn.addEventListener('click', ()=>this.loadList(), false); this.loadList(); const onSelect = (e)=>P.loadPage(e.target.value); sel.addEventListener('change', onSelect, false); sel.addEventListener('dblclick', onSelect, false); F.page.addEventListener('wiki-stash-updated', ()=>{ if(P.winfo) this._refreshStashMarks(); else this._rebuildList(); }); F.page.addEventListener('wiki-page-loaded', function(ev){ /* Needed to handle the saved-an-empty-page case. */ const page = ev.detail, opt = self.cache.optByName[page.name]; if(opt){ if(page.isEmpty) opt.dataset.isDeleted = true; else delete opt.dataset.isDeleted; self._refreshStashMarks(opt); }else if('sandbox'!==page.type){ F.error("BUG: internal mis-handling of page object: missing OPTION for page "+page.name); } }); const cbEditPreview = E('#edit-shift-enter-preview'); cbEditPreview.addEventListener('change', function(e){ F.storage.set('edit-shift-enter-preview', P.config.shiftEnterPreview = e.target.checked); }, false); cbEditPreview.checked = P.config.shiftEnterPreview; delete this.init; }/*init()*/ }; /** Widget for listing and selecting $stash entries. */ P.stashWidget = { e:{/*DOM element(s)*/}, init: function(domInsertPoint/*insert widget BEFORE this element*/){ const wrapper = D.addClass( D.attr(D.div(),'id','wikiedit-stash-selector'), 'input-with-label' ); const sel = this.e.select = D.select(), btnClear = this.e.btnClear = D.button("Discard Edits"), btnHelp = D.append( D.addClass(D.div(), "help-buttonlet"), 'Locally-edited wiki pages. Timestamps are the last local edit time. ', 'Only the ',P.config.defaultMaxStashSize,' most recent pages ', 'are retained. Saving or reloading a file removes it from this list. ', D.append(D.code(),F.storage.storageImplName()), ' = ',F.storage.storageHelpDescription() ); D.append(wrapper, "Local edits (", D.append(D.code(), F.storage.storageImplName()), "):", btnHelp, sel, btnClear); F.helpButtonlets.setup(btnHelp); D.option(D.disable(sel), undefined, "(empty)"); P.addEventListener('wiki-stash-updated',(e)=>this.updateList(e.detail)); P.addEventListener('wiki-page-loaded',(e)=>this.updateList($stash, e.detail)); sel.addEventListener('change',function(e){ const opt = this.selectedOptions[0]; if(opt && opt._winfo) P.loadPage(opt._winfo); }); if(F.storage.isTransient()){/*Warn if our storage is particularly transient...*/ D.append(wrapper, D.append( D.addClass(D.span(),'warning'), "Warning: persistent storage is not available, "+ "so uncomitted edits will not survive a page reload." )); } domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint); if(P.config.useConfirmerButtons.discardStash){ /* Must come after btnClear is in the DOM AND the button must not be hidden, else pinned sizing won't work. */ F.confirmer(btnClear, { pinSize: true, confirmText: "DISCARD all local edits?", onconfirm: ()=>P.clearStash(), ticks: F.config.confirmerButtonTicks }); }else{ btnClear.addEventListener('click', ()=>P.clearStash(), false); } D.addClass(btnClear,'hidden'); $stash._fireStashEvent(/*read the page-load-time stash*/); delete this.init; }, /** Regenerates the edit selection list. */ updateList: function f(stasher,theWinfo){ if(!f.compare){ const cmpBase = (l,r)=>l<r ? -1 : (l===r ? 0 : 1); f.compare = (l,r)=>cmpBase(l.name.toLowerCase(), r.name.toLowerCase()); f.rxZ = /\.\d+Z$/ /* ms and 'Z' part of date string */; const pad=(x)=>(''+x).length>1 ? x : '0'+x; f.timestring = function(d){ return [ d.getFullYear(),'-',pad(d.getMonth()+1/*sigh*/),'-',pad(d.getDate()), '@',pad(d.getHours()),':',pad(d.getMinutes()) ].join(''); }; } const index = stasher.getIndex(), ilist = []; Object.keys(index).forEach((winfo)=>{ ilist.push(index[winfo]); }); const self = this; D.clearElement(this.e.select); if(0===ilist.length){ D.addClass(this.e.btnClear, 'hidden'); D.option(D.disable(this.e.select),undefined,"No local edits"); return; } D.enable(this.e.select); if(true){ /* The problem with this Clear button is that it allows the user to nuke a non-empty newly-added page without the failsafe confirmation we have if they use P.e.btnReload. Not yet sure how best to resolve that. */ D.removeClass(this.e.btnClear, 'hidden'); } D.disable(D.option(this.e.select,undefined,"Select a local edit...")); const currentWinfo = theWinfo || P.winfo || {name:''}; ilist.sort(f.compare).forEach(function(winfo,n){ const key = stasher.indexKey(winfo), rev = winfo.version || ''; const opt = D.option( self.e.select, n+1/*value is (almost) irrelevant*/, [winfo.name, ' [', rev ? F.hashDigits(rev) : ( winfo.type==='sandbox' ? 'sandbox' : 'new/local' ),'] ', f.timestring(new Date(winfo.stashTime)) ].join('') ); opt._winfo = winfo; if(0===f.compare(currentWinfo, winfo)){ D.attr(opt, 'selected', true); } }); } }/*P.stashWidget*/; /** Keep track of how many in-flight AJAX requests there are so we can disable input elements while any are pending. For simplicity's sake we simply disable ALL OF IT while any AJAX is pending, rather than disabling operation-specific UI elements, which would be a huge maintenance hassle. Noting, however, that this global on/off is not *quite* pedantically correct. Pedantically speaking. If an element is disabled before an XHR starts, this code "should" notice that and not include it in the to-re-enable list. That would be annoying to do, and becomes impossible to do properly once multiple XHRs are in transit and an element is disabled seprately between two of those in-transit requests (that would be an unlikely, but possible, corner case). */ const ajaxState = { count: 0 /* in-flight F.fetch() requests */, toDisable: undefined /* elements to disable during ajax activity */ }; F.fetch.beforesend = function f(){ if(!ajaxState.toDisable){ ajaxState.toDisable = document.querySelectorAll( ['button:not([disabled])', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'fieldset:not([disabled])' ].join(',') ); } if(1===++ajaxState.count){ D.addClass(document.body, 'waiting'); D.disable(ajaxState.toDisable); } }; F.fetch.aftersend = function(){ if(0===--ajaxState.count){ D.removeClass(document.body, 'waiting'); D.enable(ajaxState.toDisable); delete ajaxState.toDisable /* required to avoid enable/disable race condition with the save button */; } }; F.onPageLoad(function() { document.body.classList.add('wikiedit'); P.base = {tag: E('base'), wikiUrl: F.repoUrl('wiki')}; P.base.originalHref = P.base.tag.href; P.e = { /* various DOM elements we work with... */ taEditor: E('#wikiedit-content-editor'), btnReload: E("#wikiedit-tab-content button.wikiedit-content-reload"), btnSave: E("button.wikiedit-save"), btnSaveClose: E("button.wikiedit-save-close"), selectMimetype: E('select[name=mimetype]'), selectFontSizeWrap: E('#select-font-size'), // selectDiffWS: E('select[name=diff_ws]'), cbAutoPreview: E('#cb-preview-autorefresh'), previewTarget: E('#wikiedit-tab-preview-wrapper'), diffTarget: E('#wikiedit-tab-diff-wrapper'), editStatus: E('#wikiedit-edit-status'), tabContainer: E('#wikiedit-tabs'), attachmentContainer: E("#attachment-wrapper"), tabs:{ pageList: E('#wikiedit-tab-pages'), content: E('#wikiedit-tab-content'), preview: E('#wikiedit-tab-preview'), diff: E('#wikiedit-tab-diff'), misc: E('#wikiedit-tab-misc') //commit: E('#wikiedit-tab-commit') } }; P.tabs = new F.TabManager(D.clearElement(P.e.tabContainer)); /* Move the status bar between the tab buttons and tab panels. Seems to be the best fit in terms of functionality and visibility. */ P.tabs.addCustomWidget( E('#fossil-status-bar') ).addCustomWidget(P.e.editStatus); let currentTab/*used for ctrl-enter switch between editor and preview*/; P.tabs.addEventListener( /* Set up some before-switch-to tab event tasks... */ 'before-switch-to', function(ev){ const theTab = currentTab = ev.detail, btnSlot = theTab.querySelector('.save-button-slot'); if(btnSlot){ /* Several places make sense for a save button, so we'll move that button around to those tabs where it makes sense. */ btnSlot.parentNode.insertBefore( P.e.btnSave.parentNode, btnSlot ); btnSlot.parentNode.insertBefore( P.e.btnSaveClose.parentNode, btnSlot ); P.updateSaveButton(); } if(theTab===P.e.tabs.preview){ P.baseHrefForWiki(); if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview(); }else if(theTab===P.e.tabs.diff){ /* Work around a weird bug where the page gets wider than the window when the diff tab is NOT in view and the current SBS diff widget is wider than the window. When the diff IS in view then CSS overflow magically reduces the page size again. Weird. Maybe FF-specific. Note that this weirdness happens even though P.e.diffTarget's parent is hidden (and therefore P.e.diffTarget is also hidden). */ D.removeClass(P.e.diffTarget, 'hidden'); } } ); P.tabs.addEventListener( /* Set up auto-refresh of the preview tab... */ 'before-switch-from', function(ev){ const theTab = ev.detail; if(theTab===P.e.tabs.preview){ P.baseHrefRestore(); }else if(theTab===P.e.tabs.diff){ /* See notes in the before-switch-to handler. */ D.addClass(P.e.diffTarget, 'hidden'); } } ); //////////////////////////////////////////////////////////// // Trigger preview on Ctrl-Enter. This only works on the built-in // editor widget, not a client-provided one. P.e.taEditor.addEventListener('keydown',function(ev){ if(P.config.shiftEnterPreview && ev.shiftKey && 13===ev.keyCode){ ev.preventDefault(); ev.stopPropagation(); P.e.taEditor.blur(/*force change event, if needed*/); P.tabs.switchToTab(P.e.tabs.preview); if(!P.e.cbAutoPreview.checked){/* If NOT in auto-preview mode, trigger an update. */ P.preview(); } } }, false); // If we're in the preview tab, have ctrl-enter switch back to the editor. document.body.addEventListener('keydown',function(ev){ if(ev.shiftKey && 13 === ev.keyCode){ if(currentTab === P.e.tabs.preview){ ev.preventDefault(); ev.stopPropagation(); P.tabs.switchToTab(P.e.tabs.content); P.e.taEditor.focus(/*doesn't work for client-supplied editor widget! And it's slow as molasses for long docs, as focus() forces a document reflow. */); //console.debug("BODY ctrl-enter"); return false; } } }, true); F.connectPagePreviewers( P.e.tabs.preview.querySelector( '#btn-preview-refresh' ) ); const diffButtons = E('#wikiedit-tab-diff-buttons'); diffButtons.querySelector('button.sbs').addEventListener( "click",(e)=>P.diff(true), false ); diffButtons.querySelector('button.unified').addEventListener( "click",(e)=>P.diff(false), false ); if(0) P.e.btnCommit.addEventListener( "click",(e)=>P.commit(), false ); const doSave = function(alsoClose){ const w = P.winfo; if(!w){ F.error("No page loaded."); return; } if(alsoClose){ P.save(()=>window.location.href=F.repoUrl('wiki',{name: w.name})); }else{ P.save(); } }; const doReload = function(e){ const w = P.winfo; if(!w){ F.error("No page loaded."); return; } if(!w.version/* new/unsaved page */ && w.type!=='sandbox' && P.wikiContent()){ F.error("This new/unsaved page has content.", "To really discard this page,", "first clear its content", "then use the Discard button."); return; } P.unstashContent(); if(w.version || w.type==='sandbox'){ P.loadPage(w); }else{ WikiList.removeEntry(w.name); delete P.winfo; P.updatePageTitle(); F.message("Discarded new page ["+w.name+"]."); } }; if(P.config.useConfirmerButtons.reload){ P.tabs.switchToTab(1/*DOM visibility workaround*/); F.confirmer(P.e.btnReload, { pinSize: true, confirmText: "Really reload, losing edits?", onconfirm: doReload, ticks: F.config.confirmerButtonTicks }); }else{ P.e.btnReload.addEventListener('click', doReload, false); } if(P.config.useConfirmerButtons.save){ P.tabs.switchToTab(1/*DOM visibility workaround*/); F.confirmer(P.e.btnSave, { pinSize: true, confirmText: "Really save changes?", onconfirm: ()=>doSave(), ticks: F.config.confirmerButtonTicks }); F.confirmer(P.e.btnSaveClose, { pinSize: true, confirmText: "Really save changes?", onconfirm: ()=>doSave(true), ticks: F.config.confirmerButtonTicks }); }else{ P.e.btnSave.addEventListener('click', ()=>doSave(), false); P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false); } P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false); P.selectMimetype(false, true); P.e.selectMimetype.addEventListener( 'change', function(e){ if(P.winfo && P.winfo.mimetype !== e.target.value){ P.winfo.mimetype = e.target.value; P._isDirty = true; P.stashContentChange(true); } }, false ); const selectFontSize = E('select[name=editor_font_size]'); if(selectFontSize){ selectFontSize.addEventListener( "change",function(e){ const ed = P.e.taEditor; ed.className = ed.className.replace( /\bfont-size-\d+/g, '' ); ed.classList.add('font-size-'+e.target.value); }, false ); selectFontSize.dispatchEvent( // Force UI update new Event('change',{target:selectFontSize}) ); } P.addEventListener( // Clear certain views when new content is loaded/set 'wiki-content-replaced', ()=>{ P.previewNeedsUpdate = true; D.clearElement(P.e.diffTarget, P.e.previewTarget); } ); P.addEventListener( // Clear certain views after a save 'wiki-saved', (e)=>{ D.clearElement(P.e.diffTarget, P.e.previewTarget); // TODO: replace preview with new content } ); P.addEventListener('wiki-stash-updated',function(){ /* MUST come before WikiList.init() and P.stashWidget.init() so that interwoven event handlers get called in the right order. */ if(P.winfo && !P.winfo.version && !$stash.getWinfo(P.winfo)){ // New local page was removed. delete P.winfo; P.wikiContent(''); P.updatePageTitle(); } P.updateSaveButton(); }).updatePageTitle().updateSaveButton(); P.addEventListener( // Update various state on wiki page load 'wiki-page-loaded', function(ev){ delete P._isDirty; const winfo = ev.detail; P.winfo = winfo; P.previewNeedsUpdate = true; P.e.selectMimetype.value = winfo.mimetype; P.tabs.switchToTab(P.e.tabs.content); P.wikiContent(winfo.content || ''); WikiList.e.select.value = winfo.name; if(!winfo.version && winfo.type!=='sandbox'){ F.message('You are editing a new, unsaved page:',winfo.name); } P.updatePageTitle().updateSaveButton(/* b/c save() routes through here */); }, false ); /* These init()s need to come after P's event handlers are registered. The tab-switching is a workaround for the pinSize option of the confirmer widgets: it does not work if the confirmer button being initialized is in a hidden part of the DOM :/. */ P.tabs.switchToTab(0); WikiList.init( P.e.tabs.pageList.firstElementChild ); P.tabs.switchToTab(1); P.stashWidget.init(P.e.tabs.content.lastElementChild); P.tabs.switchToTab(0); //P.$wikiList = WikiList/*only for testing/debugging*/; }/*F.onPageLoad()*/); /** Returns true if fossil.page.winfo is set, indicating that a page has been loaded, else it reports an error and returns false. If passed a truthy value any error message about not having a wiki page loaded is suppressed. */ const affirmPageLoaded = function(quiet){ if(!P.winfo && !quiet) F.error("No wiki page is loaded."); return !!P.winfo; }; /** Updates the attachments list from this.winfo. */ P.updateAttachmentsView = function f(){ if(!f.eAttach){ f.eAttach = P.e.attachmentContainer.querySelector('div'); } D.clearElement(f.eAttach); const wi = this.winfo; if(!wi){ D.append(f.eAttach,"No page loaded."); return this; } else if(!wi.version){ D.append(f.eAttach, "Page ["+wi.name+"] cannot have ", "attachments until it is saved once."); return this; } const btnReload = D.button("Reload list"); const self = this; btnReload.addEventListener('click', function(){ const isStashed = $stash.hasStashedContent(wi); F.fetch('wikiajax/attachments',{ responseType: 'json', urlParams: {page: wi.name}, onload: function(r){ wi.attachments = r; if(isStashed) self.stashContentChange(true); F.message("Reloaded attachment list for ["+wi.name+"]."); self.updateAttachmentsView(); } }); }); if(!wi.attachments || !wi.attachments.length){ D.append(f.eAttach, btnReload, " No attachments found for page ["+wi.name+"]. ", D.a(F.repoUrl('attachadd',{ page: wi.name, from: F.repoUrl('wikiedit',{name: wi.name})}), "Add attachments..." ) ); return this; } D.append( f.eAttach, D.append(D.p(), btnReload," ", D.a(F.repoUrl('attachlist',{page:wi.name}), "Attachments for page ["+wi.name+"]."), " ", D.a(F.repoUrl('attachadd',{ page:wi.name, from: F.repoUrl('wikiedit',{name: wi.name})}), "Add attachments..." ) ) ); wi.attachments.forEach(function(a){ const wrap = D.div(); D.append(f.eAttach, wrap); D.append(wrap, D.append(D.div(), "Attachment ", D.addClass( D.a(F.repoUrl('ainfo',{name:a.uuid}), F.hashDigits(a.uuid,true)), 'monospace'), " ", a.filename, (a.isLatest ? " (latest)" : "") ) ); //D.append(wrap,D.append(D.div(), "URLs:")); const ul = D.ul(); D.append(wrap, ul); [ // List download URL variants for each attachment: [ "attachdownload?page=", encodeURIComponent(wi.name), "&file=", encodeURIComponent(a.filename) ].join(''), "raw/"+a.src ].forEach(function(url){ const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url); const urlCopy = D.span(); const li = D.li(ul); D.append(li, urlCopy, " ", imgUrl); F.copyButton(urlCopy, {copyFromElement: imgUrl}); }); }); return this; }; /** Updates the in-tab title/edit status information */ P.updateEditStatus = function f(){ if(!f.eLinks){ f.eName = P.e.editStatus.querySelector('span.name'); f.eLinks = P.e.editStatus.querySelector('span.links'); } const wi = this.winfo; D.clearElement(f.eName, f.eLinks); if(!wi){ D.append(f.eName, '(no page loaded)'); this.updateAttachmentsView(); return this; } D.append(f.eName,getEditMarker(wi, false),wi.name); this.updateAttachmentsView(); if(!wi.version) return this; D.append( f.eLinks, D.a(F.repoUrl('wiki',{name:wi.name}),"viewer"), D.a(F.repoUrl('whistory',{name:wi.name}),'history'), D.a(F.repoUrl('attachlist',{page:wi.name}),"attachments"), D.a(F.repoUrl('attachadd',{page:wi.name,from: F.repoUrl('wikiedit',{name: wi.name})}), "attach"), D.a(F.repoUrl('wikiedit',{name:wi.name}),"editor permalink") ); return this; }; /** Update the page title and header based on the state of this.winfo. A no-op if this.winfo is not set. Returns this. */ P.updatePageTitle = function f(){ if(!f.titleElement){ f.titleElement = document.head.querySelector('title'); } const wi = P.winfo, marker = getEditMarker(wi, true), title = wi ? wi.name : 'no page loaded'; f.titleElement.innerText = 'Wiki Editor: ' + marker + title; this.updateEditStatus(); return this; }; /** Change the save button depending on whether we have stuff to save or not. */ P.updateSaveButton = function(){ /** // Currently disabled, per forum feedback and platform-level // event-handling compatibility, but might be revisited. We now // use an is-dirty flag instead to prevent saving when no change // event has fired for the current doc. if(!this.winfo || !this.getStashedWinfo(this.winfo)){ D.disable(this.e.btnSave).innerText = "No changes to save"; D.disable(this.e.btnSaveClose); }else{ D.enable(this.e.btnSave).innerText = "Save"; D.enable(this.e.btnSaveClose); }*/ return this; }; /** Getter (if called with no args) or setter (if passed an arg) for the current file content. The setter form sets the content, dispatches a 'wiki-content-replaced' event, and returns this object. */ P.wikiContent = function f(){ if(0===arguments.length){ return f.get(); }else{ f.set(arguments[0] || ''); this.dispatchEvent('wiki-content-replaced', this); return this; } }; /* Default get/set impls for file content */ P.wikiContent.get = function(){return P.e.taEditor.value}; P.wikiContent.set = function(content){P.e.taEditor.value = content}; /** For use when installing a custom editor widget. Pass it the getter and setter callbacks to fetch resp. set the content of the custom widget. They will be triggered via P.wikiContent(). Returns this object. */ P.setContentMethods = function(getter, setter){ this.wikiContent.get = getter; this.wikiContent.set = setter; return this; }; /** Alerts the editor app that a "change" has happened in the editor. When connecting 3rd-party editor widgets to this app, it is necessary to call this for any "change" events the widget emits. Whether or not "change" means that there were "really" edits is irrelevant, but this app will not allow saving unless it believes at least one "change" has been made (by being signaled through this method). This function may perform an arbitrary amount of work, so it should not be called for every keypress within the editor widget. Calling it for "blur" events is generally sufficient, and calling it for each Enter keypress is generally reasonable but also computationally costly. */ P.notifyOfChange = function(){ P._isDirty = true; P.stashContentChange(); }; /** Removes the default editor widget (and any dependent elements) from the DOM, adds the given element in its place, removes this method from this object, and returns this object. This is not needed if the 3rd-party widget replaces or hides this app's editor widget (e.g. TinyMCE). */ P.replaceEditorElement = function(newEditor){ P.e.taEditor.parentNode.insertBefore(newEditor, P.e.taEditor); P.e.taEditor.remove(); P.e.selectFontSizeWrap.remove(); delete this.replaceEditorElement; return P; }; /** Sets the current page's base.href to {g.zTop}/wiki. */ P.baseHrefForWiki = function f(){ this.base.tag.href = this.base.wikiUrl; return this; }; /** Sets the document's base.href value to its page-load-time setting. */ P.baseHrefRestore = function(){ this.base.tag.href = this.base.originalHref; }; /** loadPage() loads the given wiki page and updates the relevant UI elements to reflect the loaded state. If passed no arguments then it re-uses the values from the currently-loaded page, reloading it (emitting an error message if no file is loaded). Returns this object, noting that the load is async. After loading it triggers a 'wiki-page-loaded' event, passing it this.winfo. If a locally-edited copy of the given file/rev is found, that copy is used instead of one fetched from the server, but it is still treated as a load event. Alternate call forms: - no arguments: re-loads from this.winfo. - 1 non-string argument: assumed to be an winfo-style object. Must have at least the {name} property, but need not have other winfo state. */ P.loadPage = function(name){ if(0===arguments.length){ /* Reload from this.winfo */ if(!affirmPageLoaded()) return this; name = this.winfo.name; }else if(1===arguments.length && 'string' !== typeof name){ /* Assume winfo-like object */ const arg = arguments[0]; name = arg.name; } const onload = (r)=>{ this.dispatchEvent('wiki-page-loaded', r); }; const stashWinfo = this.getStashedWinfo({name: name}); if(stashWinfo){ // fake a response from the stash... F.message("Fetched from the local-edit storage:", stashWinfo.name); onload({ name: stashWinfo.name, mimetype: stashWinfo.mimetype, type: stashWinfo.type, version: stashWinfo.version, parent: stashWinfo.parent, isEmpty: !!stashWinfo.isEmpty, content: $stash.stashedContent(stashWinfo), attachments: stashWinfo.attachments }); this._isDirty = true/*b/c loading normally clears that flag*/; return this; } F.message( "Loading content..." ).fetch('wikiajax/fetch',{ urlParams: { page: name }, responseType: 'json', onload:(r)=>{ F.message('Loaded page ['+r.name+'].'); onload(r); } }); return this; }; /** Fetches the page preview based on the contents and settings of this page's input fields, and updates the UI with with the preview. Returns this object, noting that the operation is async. */ P.preview = function f(switchToTab){ if(!affirmPageLoaded()) return this; return this._postPreview(this.wikiContent(), function(c){ P._previewTo(c); if(switchToTab) self.tabs.switchToTab(self.e.tabs.preview); }); }; /** Callback for use with F.connectPagePreviewers(). Gets passed the preview content. */ P._previewTo = function(c){ const target = this.e.previewTarget; D.clearElement(target); if('string'===typeof c) D.parseHtml(target,c); if(F.pikchr){ F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr')); } }; /** Callback for use with F.connectPagePreviewers() */ P._postPreview = function(content,callback){ if(!affirmPageLoaded()) return this; if(!content){ callback(content); return this; } const fd = new FormData(); const mimetype = this.e.selectMimetype.value; fd.append('page', this.winfo.name); fd.append('mimetype',mimetype); fd.append('content',content || ''); F.message( "Fetching preview..." ).fetch('wikiajax/preview',{ payload: fd, onload: (r,header)=>{ callback(r); F.message('Updated preview.'); P.previewNeedsUpdate = false; P.dispatchEvent('wiki-preview-updated',{ mimetype: mimetype, element: P.e.previewTarget }); }, onerror: (e)=>{ F.fetch.onerror(e); callback("Error fetching preview: "+e); } }); return this; }; /** Fetches the content diff based on the contents and settings of this page's input fields, and updates the UI with the diff view. Returns this object, noting that the operation is async. */ P.diff = function f(sbs){ if(!affirmPageLoaded()) return this; const content = this.wikiContent(), self = this, target = this.e.diffTarget; const fd = new FormData(); fd.append('page',this.winfo.name); fd.append('sbs', sbs ? 1 : 0); fd.append('content',content); if(this.e.selectDiffWS) fd.append('ws',this.e.selectDiffWS.value); F.message( "Fetching diff..." ).fetch('wikiajax/diff',{ payload: fd, onload: function(c){ D.parseHtml(D.clearElement(target), [ "<div>Diff <code>[", self.winfo.name, "]</code> → Local Edits</div>", c||'No changes.' ].join('')); F.diff.setupDiffContextLoad(); if(sbs) P.tweakSbsDiffs(); F.message('Updated diff.'); self.tabs.switchToTab(self.e.tabs.diff); } }); return this; }; /** Saves the current wiki page and re-populates the editor with the saved state. If passed an argument, it is expected to be a function, which is called only if saving succeeds, after all other post-save processing. */ P.save = function callee(onSuccessCallback){ if(!affirmPageLoaded()) return this; else if(!this._isDirty){ F.error("There are no changes to save."); return this; } const content = this.wikiContent(); const self = this; callee.onload = function(w){ const oldWinfo = self.winfo; self.unstashContent(oldWinfo); self.dispatchEvent('wiki-page-loaded', w)/* will reset save buttons */; F.message("Saved page: ["+w.name+"]."); if('function'===typeof onSuccessCallback){ onSuccessCallback(); } }; const fd = new FormData(), w = P.winfo; fd.append('page',w.name); fd.append('mimetype', w.mimetype); fd.append('isnew', w.version ? 0 : 1); fd.append('content', P.wikiContent()); F.message( "Saving page..." ).fetch('wikiajax/save',{ payload: fd, responseType: 'json', onload: callee.onload }); return this; }; /** Updates P.winfo for certain state and stashes P.winfo, with the current content fetched via P.wikiContent(). If passed truthy AND the stash already has stashed content for the current page, only the stashed winfo record is updated, else both the winfo and content are updated. */ P.stashContentChange = function(onlyWinfo){ if(affirmPageLoaded(true)){ const wi = this.winfo; wi.mimetype = P.e.selectMimetype.value; if(onlyWinfo && $stash.hasStashedContent(wi)){ $stash.updateWinfo(wi); }else{ $stash.updateWinfo(wi, P.wikiContent()); } F.message("Stashed changes to page ["+wi.name+"]."); P.updatePageTitle(); $stash.prune(); this.previewNeedsUpdate = true; } return this; }; /** Removes any stashed state for the current P.winfo (if set) from F.storage. Returns this. */ P.unstashContent = function(){ const winfo = arguments[0] || this.winfo; if(winfo){ this.previewNeedsUpdate = true; $stash.unstash(winfo); //console.debug("Unstashed",winfo); F.message("Unstashed page ["+winfo.name+"]."); } return this; }; /** Clears all stashed file state from F.storage. Returns this. */ P.clearStash = function(){ $stash.clear(); return this; }; /** If stashed content for P.winfo exists, it is returned, else undefined is returned. */ P.contentFromStash = function(){ return affirmPageLoaded(true) ? $stash.stashedContent(this.winfo) : undefined; }; /** If a stashed version of the given winfo object exists (same filename/checkin values), return it, else return undefined. */ P.getStashedWinfo = function(winfo){ return $stash.getWinfo(winfo); }; })(window.fossil); |
Added src/fossil.pikchr.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (function(F/*window.fossil object*/){ "use strict"; const D = F.dom, P = F.pikchr = {}; /** Initializes pikchr-rendered elements with the ability to toggle between their SVG and source code. The first argument may be any of: - A single SVG.pikchr element. - A collection (with a forEach method) of such elements. - A CSS selector string for one or more such elements. - An array of such strings. Passing no value is equivalent to passing 'svg.pikchr'. For each SVG in the resulting set, this function sets up event handlers which allow the user to toggle the SVG between image and source code modes. The image will switch modes in response to cltr-click and, if its *parent* element has the "toggle" CSS class, it will also switch modes in response to single-click. If the parent element has the "source" CSS class, the image starts off with its source code visible and the image hidden, instead of the default of the other way around. Returns this object. Each element will only be processed once by this routine, even if it is passed to this function multiple times. Each processed element gets a "data" attribute set to it to indicate that it was already dealt with. This code expects the following structure around the SVGs, and will not process any which don't match this: <DIV.pikchr-wrapper> <DIV.pikchr-svg><SVG.pikchr></SVG></DIV> <PRE.pikchr-src></PRE> </DIV> */ P.addSrcView = function f(svg){ if(!f.hasOwnProperty('parentClick')){ f.parentClick = function(ev){ if(ev.altKey || ev.metaKey || ev.ctrlKey /* Every combination of special key (alt, shift, ctrl, meta) is handled differently everywhere. Shift is used by the browser, Ctrl doesn't work on an iMac, and Alt is intercepted by most Linux window managers to control window movement! So... we just listen for *any* of them (except Shift) and the user will need to find one which works on on their environment. */ || this.classList.contains('toggle')){ this.classList.toggle('source'); ev.stopPropagation(); ev.preventDefault(); } }; }; if(!svg) svg = 'svg.pikchr'; if('string' === typeof svg){ document.querySelectorAll(svg).forEach((e)=>f.call(this, e)); return this; }else if(svg.forEach){ svg.forEach((e)=>f.call(this, e)); return this; } if(svg.dataset.pikchrProcessed){ return this; } svg.dataset.pikchrProcessed = 1; const parent = svg.parentNode.parentNode /* outermost div.pikchr-wrapper */; const srcView = parent ? svg.parentNode.nextElementSibling : undefined; if(!srcView || !srcView.classList.contains('pikchr-src')){ /* Without this element, there's nothing for us to do here. */ return this; } parent.addEventListener('click', f.parentClick, false); return this; }; })(window.fossil); |
Added src/fossil.popupwidget.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (function(F/*fossil object*/){ /** A very basic tooltip-like widget. It's intended to be popped up to display basic information or basic user interaction components, e.g. a copy-to-clipboard button. Requires: fossil.bootstrap, fossil.dom */ const D = F.dom; /** Creates a new tooltip-like widget using the given options object. Options: .refresh: callback which is called just before the tooltip is revealed. It must refresh the contents of the tooltip, if needed, by applying the content to/within this.e, which is the base DOM element for the tooltip (and is a child of document.body). If the contents are static and set up via the .init option then this callback is not needed. When moving an already-shown tooltip, this is *not* called. It arguably should be, but the fact is that we often have to show() a popup twice in a row without hiding it between those calls: once to get its computed size and another to move it by some amount relative to that size. If the state of the popup depends on its position and a "double-show()" is needed then the client must hide() the popup between the two calls to show() in order to force a call to refresh() on the second show(). .adjustX: an optional callback which is called when the tooltip is to be displayed at a given position and passed the X viewport-relative coordinate. This routine must either return its argument as-is or return an adjusted value. The intent is to allow a given tooltip may be positioned more appropriately for a given context, if needed (noting that the desired position can, and probably should, be passed to the show() method instead). This class's API assumes that clients give it viewport-relative coordinates, and it will take care to translate those to page-relative, so this callback should not do so. .adjustY: the Y counterpart of adjustX. .init: optional callback called one time to initialize the state of the tooltip. This is called after the this.e has been created and added (initially hidden) to the DOM. If this is called, it is removed from the object immediately after it is called. All callback options are called with the PopupWidget object as their "this". .cssClass: optional CSS class, or list of classes, to apply to the new element. In addition to any supplied here (or inherited from the default), the class "fossil-PopupWidget" is always set in order to allow certain app-internal CSS to account for popup windows in special cases. .style: optional object of properties to copy directly into the element's style object. The options passed to this constructor get normalized into a separate object which includes any default values for options not provided by the caller. That object is available this the resulting PopupWidget's options property. Default values for any options not provided by the caller are pulled from PopupWidget.defaultOptions, and modifying those affects all future calls to this method but has no effect on existing instances. Example: const tip = new fossil.PopupWidget({ init: function(){ // optionally populate DOM element this.e with the widget's // content. }, refresh: function(){ // (re)populate/refresh the contents of the main // wrapper element, this.e. } }); tip.show(50, 100); // ^^^ viewport-relative coordinates. See show() for other options. */ F.PopupWidget = function f(opt){ opt = F.mergeLastWins(f.defaultOptions,opt); this.options = opt; const e = this.e = D.addClass(D.div(), opt.cssClass, "fossil-PopupWidget"); this.show(false); if(opt.style){ let k; for(k in opt.style){ if(opt.style.hasOwnProperty(k)) e.style[k] = opt.style[k]; } } D.append(document.body, e/*must be in the DOM for size calc. to work*/); D.copyStyle(e, opt.style); if(opt.init){ opt.init.call(this); delete opt.init; } }; /** Default options for the PopupWidget constructor. These values are used for any options not provided by the caller. Any changes made to this instace affect future calls to PopupWidget() but have no effect on existing instances. */ F.PopupWidget.defaultOptions = { cssClass: 'fossil-tooltip', style: undefined /*{optional properties copied as-is into element.style}*/, adjustX: (x)=>x, adjustY: (y)=>y, refresh: function(){}, init: undefined /* optional initialization function */ }; F.PopupWidget.prototype = { /** Returns true if the widget is currently being shown, else false. */ isShown: function(){return !this.e.classList.contains('hidden')}, /** Calls the refresh() method of the options object and returns this object. */ refresh: function(){ if(this.options.refresh){ this.options.refresh.call(this); } return this; }, /** Shows or hides the tooltip. Usages: (bool showIt) => hide it or reveal it at its last position. (x, y) => reveal/move it at/to the given relative-to-the-viewport position, which will be adjusted to make it page-relative. (DOM element) => reveal/move it at/to a position based on the the given element (adjusted slightly). For the latter two, this.options.adjustX() and adjustY() will be called to adjust it further. Returns this object. If this call will reveal the element then it calls this.refresh() to update the UI state. If the element was already revealed, the call to refresh() is skipped. Sidebar: showing/hiding the widget is, as is conventional for this framework, done by removing/adding the 'hidden' CSS class to it, so that class must be defined appropriately. */ show: function(){ var x = undefined, y = undefined, showIt, wasShown = !this.e.classList.contains('hidden'); if(2===arguments.length){ x = arguments[0]; y = arguments[1]; showIt = true; }else if(1===arguments.length){ if(arguments[0] instanceof HTMLElement){ const p = arguments[0]; const r = p.getBoundingClientRect(); x = r.x + r.x/5; y = r.y - r.height/2; showIt = true; }else{ showIt = !!arguments[0]; } } if(showIt){ if(!wasShown) this.refresh(); x = this.options.adjustX.call(this,x); y = this.options.adjustY.call(this,y); x += window.pageXOffset; y += window.pageYOffset; } if(showIt){ if('number'===typeof x && 'number'===typeof y){ this.e.style.left = x+"px"; this.e.style.top = y+"px"; } D.removeClass(this.e, 'hidden'); }else{ D.addClass(this.e, 'hidden'); this.e.style.removeProperty('left'); this.e.style.removeProperty('top'); } return this; }, /** Equivalent to show(false), but may be overridden by instances, so long as they also call this.show(false) to perform the actual hiding. Overriding can be used to clean up any state so that the next call to refresh() (before the popup is show()n again) can recognize whether it needs to do something, noting that it's legal, and sometimes necessary, to call show() multiple times without needing/wanting to completely refresh the popup between each call (e.g. when moving the popup after it's been show()n). */ hide: function(){return this.show(false)}, /** A convenience method which adds click handlers to this popup's main element and document.body to hide (via hide()) the popup when either element is clicked or the ESC key is pressed. Only call this once per instance, if at all. Returns this; The first argument specifies whether a click handler on this object is installed. The second specifies whether a click outside of this object should close it. The third specifies whether an ESC handler is installed. Passing no arguments is equivalent to passing (true,true,true), and passing fewer arguments defaults the unpassed parameters to true. */ installHideHandlers: function f(onClickSelf, onClickOther, onEsc){ if(!arguments.length) onClickSelf = onClickOther = onEsc = true; else if(1===arguments.length) onClickOther = onEsc = true; else if(2===arguments.length) onEsc = true; if(onClickSelf) this.e.addEventListener('click', ()=>this.hide(), false); if(onClickOther) document.body.addEventListener('click', ()=>this.hide(), true); if(onEsc){ const self = this; document.body.addEventListener('keydown', function(ev){ if(self.isShown() && 27===ev.which) self.hide(); }, true); } return this; } }/*F.PopupWidget.prototype*/; /** Internal impl for F.toast() and friends. args: 1) CSS class to assign to the outer element, along with fossil-toast-message. Must be falsy for the non-warning/non-error case. 2) Multiplier of F.toast.config.displayTimeMs. Should be 1 for default case and progressively higher for warning/error cases. 3) The 'arguments' object from the function which is calling this. Returns F.toast. */ const toastImpl = function f(cssClass, durationMult, argsObject){ if(!f.toaster){ f.toaster = new F.PopupWidget({ cssClass: 'fossil-toast-message' }); D.attr(f.toaster.e, 'role', 'alert'); } const T = f.toaster; if(f._timer) clearTimeout(f._timer); D.clearElement(T.e); if(f._prevCssClass) T.e.classList.remove(f._prevCssClass); if(cssClass) T.e.classList.add(cssClass); f._prevCssClass = cssClass; D.append(T.e, Array.prototype.slice.call(argsObject,0)); T.show(F.toast.config.position.x, F.toast.config.position.y); f._timer = setTimeout( ()=>T.hide(), F.toast.config.displayTimeMs * durationMult ); return F.toast; }; F.toast = { config: { position: { x: 5, y: 5 /*viewport-relative, pixels*/ }, displayTimeMs: 3000 }, /** Convenience wrapper around a PopupWidget which pops up a shared PopupWidget instance to show toast-style messages (commonly seen on Android). Its arguments may be anything suitable for passing to fossil.dom.append(), and each argument is first append()ed to the toast widget, then the widget is shown for F.toast.config.displayTimeMs milliseconds. If this is called while a toast is currently being displayed, the first will be overwritten and the time until the message is hidden will be reset. The toast is always shown at the viewport-relative coordinates defined by the F.toast.config.position. The toaster's DOM element has the CSS class fossil-tooltip and fossil-toast-message, so can be style via those. The 3 main message types (message, warning, error) each get a CSS class with that same name added to them. Thus CSS can select on .fossil-toast-message.error to style error toasts. */ message: function(/*...*/){ return toastImpl(false,1, arguments); }, /** Displays a toast with the 'warning' CSS class assigned to it. It displays for 1.5 times as long as a normal toast. */ warning: function(/*...*/){ return toastImpl('warning',1.5,arguments); }, /** Displays a toast with the 'error' CSS class assigned to it. It displays for twice as long as a normal toast. */ error: function(/*...*/){ return toastImpl('error',2,arguments); } }/*F.toast*/; F.helpButtonlets = { /** Initializes one or more "help buttonlets". It may be passed any of: - A string: CSS selector (multiple matches are legal) - A single DOM element. - A forEach-compatible container of DOM elements. - No arguments, which is equivalent to passing the string ".help-buttonlet:not(.processed)". Passing the same element(s) more than once is a no-op: during initialization, each elements get the class'processed' added to it, and any elements with that class are skipped. All child nodes of a help buttonlet are removed from the button during initialization and stashed away for use in a PopupWidget when the botton is clicked. */ setup: function f(){ if(!f.hasOwnProperty('clickHandler')){ f.clickHandler = function fch(ev){ ev.preventDefault(); ev.stopPropagation(); if(!fch.popup){ fch.popup = new F.PopupWidget({ cssClass: ['fossil-tooltip', 'help-buttonlet-content'], refresh: function(){ } }); fch.popup.e.style.maxWidth = '80%'/*of body*/; fch.popup.installHideHandlers(); } D.append(D.clearElement(fch.popup.e), ev.target.$helpContent); /* Shift the help around a bit to "better" fit the screen. However, fch.popup.e.getClientRects() is empty until the popup is shown, so we have to show it, calculate the resulting size, then move and/or resize it. This algorithm/these heuristics can certainly be improved upon. */ var popupRect, rectElem = ev.target; while(rectElem){ popupRect = rectElem.getClientRects()[0]/*undefined if off-screen!*/; if(popupRect) break; rectElem = rectElem.parentNode; } if(!popupRect) popupRect = {x:0, y:0, left:0, right:0}; var x = popupRect.left, y = popupRect.top; if(x<0) x = 0; if(y<0) y = 0; if(rectElem){ /* Try to ensure that the popup's z-level is higher than this element's */ const rz = window.getComputedStyle(rectElem).zIndex; var myZ; if(rz && !isNaN(+rz)){ myZ = +rz + 1; }else{ myZ = 10000/*guess!*/; } fch.popup.e.style.zIndex = myZ; } fch.popup.show(x, y); x = popupRect.left, y = popupRect.top; popupRect = fch.popup.e.getBoundingClientRect(); const rectBody = document.body.getClientRects()[0]; if(popupRect.right > rectBody.right){ x -= (popupRect.right - rectBody.right); } if(x + popupRect.width > rectBody.right){ x = rectBody.x + (rectBody.width*0.1); fch.popup.e.style.minWidth = '70%'; }else{ fch.popup.e.style.removeProperty('min-width'); x -= popupRect.width/2; } if(x<0) x = 0; //console.debug("dimensions",x,y, popupRect, rectBody); fch.popup.show(x, y); return false; }; f.foreachElement = function(e){ if(e.classList.contains('processed')) return; e.classList.add('processed'); e.$helpContent = []; /* We have to move all child nodes out of the way because we cannot hide TEXT nodes via CSS (which cannot select TEXT nodes). We have to do it in two steps to avoid invaliding the list during traversal. */ e.childNodes.forEach((ch)=>e.$helpContent.push(ch)); e.$helpContent.forEach((ch)=>ch.remove()); e.addEventListener('click', f.clickHandler, false); }; }/*static init*/ var elems; if(!arguments.length){ arguments[0] = '.help-buttonlet:not(.processed)'; arguments.length = 1; } if(arguments.length){ if('string'===typeof arguments[0]){ elems = document.querySelectorAll(arguments[0]); }else if(arguments[0] instanceof HTMLElement){ elems = [arguments[0]]; }else if(arguments[0].forEach){/* assume DOM element list or array */ elems = arguments[0]; } } if(elems) elems.forEach(f.foreachElement); }, /** Sets up the given element as a "help buttonlet", adding the CSS class help-buttonlet to it. Any (optional) arguments after the first are appended to the element using fossil.dom.append(), so that they become the content for the buttonlet's popup help. The element is then passed to this.setup() before it is returned from this function. */ create: function(elem/*...body*/){ D.addClass(elem, 'help-buttonlet'); if(arguments.length>1){ const args = Array.prototype.slice.call(arguments,1); D.append(elem, args); } this.setup(elem); return elem; } }/*helpButtonlets*/; F.onDOMContentLoaded( ()=>F.helpButtonlets.setup() ); })(window.fossil); |
Added src/fossil.storage.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | (function(F){ /** fossil.storage is a basic wrapper around localStorage or sessionStorage or a dummy proxy object if neither of those are available. */ const tryStorage = function f(obj){ if(!f.key) f.key = 'fossil.access.check'; try{ obj.setItem(f.key, 'f'); const x = obj.getItem(f.key); obj.removeItem(f.key); if(x!=='f') throw new Error(f.key+" failed") return obj; }catch(e){ return undefined; } }; /** Internal storage impl for fossil.storage. */ const $storage = tryStorage(window.localStorage) || tryStorage(window.sessionStorage) || tryStorage({ // A basic dummy xyzStorage stand-in $$$:{}, setItem: function(k,v){this.$$$[k]=v}, getItem: function(k){ return this.$$$.hasOwnProperty(k) ? this.$$$[k] : undefined; }, removeItem: function(k){delete this.$$$[k]}, clear: function(){this.$$$={}} }); /** For the dummy storage we need to differentiate between $storage and its real property storage for hasOwnProperty() to work properly... */ const $storageHolder = $storage.hasOwnProperty('$$$') ? $storage.$$$ : $storage; /** A prefix which gets internally applied to all fossil.storage property keys so that localStorage and sessionStorage across the same browser profile instance do not "leak" across multiple repos being hosted by the same origin server. Such polination is still there but, with this key prefix applied, it won't be immediately visible via the storage API. With this in place we can justify using localStorage instead of sessionStorage again. One implication, it was discovered after the release of 2.12, of using localStorage and sessionStorage, is that their scope (the same "origin" and client application/profile) allows multiple repos on the same origin to use the same storage. Thus a user editing a wiki in /repoA/wikiedit could then see those edits in /repoB/wikiedit. The data do not cross user- or browser boundaries, though, so it "might" arguably be called a bug. Even so, it was never intended for that to happen. Rather than lose localStorage access altogether, storageKeyPrefix was added so that we can sandbox that state for the various repos. See: https://fossil-scm.org/forum/forumpost/4afc4d34de Sidebar: it might seem odd to provide a key prefix and stick all properties in the topmost level of the storage object. We do that because adding a layer of object to sandbox each repo would mean (de)serializing that whole tree on every storage property change (and we update storage often during editing sessions). e.g. instead of storageObject.projectName.foo we have storageObject[storageKeyPrefix+'foo']. That's soley for efficiency's sake (in terms of battery life and environment-internal storage-level effort). Even so, it might (or might not) be useful to do that someday. */ const storageKeyPrefix = ( $storageHolder===$storage/*localStorage or sessionStorage*/ ? ( F.config.projectCode || F.config.projectName || F.config.shortProjectName || window.location.pathname )+'::' : ( '' /* transient storage */ ) ); /** A proxy for localStorage or sessionStorage or a page-instance-local proxy, if neither one is availble. Which exact storage implementation is uses is unspecified, and apps must not rely on it. */ F.storage = { storageKeyPrefix: storageKeyPrefix, /** Sets the storage key k to value v, implicitly converting it to a string. */ set: (k,v)=>$storage.setItem(storageKeyPrefix+k,v), /** Sets storage key k to JSON.stringify(v). */ setJSON: (k,v)=>$storage.setItem(storageKeyPrefix+k,JSON.stringify(v)), /** Returns the value for the given storage key, or dflt if the key is not found in the storage. */ get: (k,dflt)=>$storageHolder.hasOwnProperty( storageKeyPrefix+k ) ? $storage.getItem(storageKeyPrefix+k) : dflt, /** Returns true if the given key has a value of "true". If the key is not found, it returns true if the boolean value of dflt is "true". (Remember that JS persistent storage values are all strings.) */ getBool: function(k,dflt){ return 'true'===this.get(k,''+(!!dflt)); }, /** Returns the JSON.parse()'d value of the given storage key's value, or dflt is the key is not found or JSON.parse() fails. */ getJSON: function f(k,dflt){ try { const x = this.get(k,f); return x===f ? dflt : JSON.parse(x); } catch(e){return dflt} }, /** Returns true if the storage contains the given key, else false. */ contains: (k)=>$storageHolder.hasOwnProperty(storageKeyPrefix+k), /** Removes the given key from the storage. Returns this. */ remove: function(k){ $storage.removeItem(storageKeyPrefix+k); return this; }, /** Clears ALL keys from the storage. Returns this. */ clear: function(){ this.keys().forEach((k)=>$storage.removeItem(/*w/o prefix*/k)); return this; }, /** Returns an array of all keys currently in the storage. */ keys: ()=>Object.keys($storageHolder).filter((v)=>(v||'').startsWith(storageKeyPrefix)), /** Returns true if this storage is transient (only available until the page is reloaded), indicating that fileStorage and sessionStorage are unavailable. */ isTransient: ()=>$storageHolder!==$storage, /** Returns a symbolic name for the current storage mechanism. */ storageImplName: function(){ if($storage===window.localStorage) return 'localStorage'; else if($storage===window.sessionStorage) return 'sessionStorage'; else return 'transient'; }, /** Returns a brief help text string for the currently-selected storage type. */ storageHelpDescription: function(){ return { localStorage: "Browser-local persistent storage with an "+ "unspecified long-term lifetime (survives closing the browser, "+ "but maybe not a browser upgrade).", sessionStorage: "Storage local to this browser tab, "+ "lost if this tab is closed.", "transient": "Transient storage local to this invocation of this page." }[this.storageImplName()]; } }; })(window.fossil); |
Added src/fossil.tabs.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | "use strict"; (function(F/*fossil object*/){ const E = (s)=>document.querySelector(s), EA = (s)=>document.querySelectorAll(s), D = F.dom; /** Creates a TabManager. If passed a truthy first argument, it is passed to init(). If passed a truthy second argument, it must be an Object holding configuration options: { tabAccessKeys: boolean (=true) If true, tab buttons are assigned "accesskey" values equal to their 1-based tab number. } */ const TabManager = function(domElem, options){ this.e = {}; this.options = F.mergeLastWins(TabManager.defaultOptions , options); if(domElem) this.init(domElem); }; /** Default values for the options object passed to the TabManager constructor. Changing these affects the defaults of all TabManager instances instantiated after that point. */ TabManager.defaultOptions = { tabAccessKeys: true }; /** Internal helper to normalize a method argument to a tab element. arg may be a tab DOM element, a selector string, or an index into tabMgr.e.tabs.childNodes. Returns the corresponding tab element. */ const tabArg = function(arg,tabMgr){ if('string'===typeof arg) arg = E(arg); else if(tabMgr && 'number'===typeof arg && arg>=0){ arg = tabMgr.e.tabs.childNodes[arg]; } return arg; }; /** Sets sets the visibility of tab element e to on or off. e MUST be a TabManager tab element. */ const setVisible = function(e,yes){ D[yes ? 'removeClass' : 'addClass'](e, 'hidden'); }; TabManager.prototype = { /** Initializes the tabs associated with the given tab container (DOM element or selector for a single element). This must be called once before using any other member functions of a given instance, noting that the constructor will call this if it is passed an argument. The tab container must have an 'id' attribute. This function looks through the DOM for all elements which have data-tab-parent=thatId. For each one it creates a button to switch to that tab and moves the element into this.e.tabs, *possibly* injecting an intermediary element between this.e.tabs and the element. The label for each tab is set by the data-tab-label attribute of each element, defaulting to something not terribly useful. When it's done, it auto-selects the first tab unless a tab has a truthy numeric value in its data-tab-select attribute, in which case the last tab to have such a property is selected. This method must only be called once per instance. TabManagers may be nested but must not share any tabs instances. Returns this object. DOM elements of potential interest to users: this.e.container = the outermost container element. this.e.tabBar = the button bar. Each "button" (whether it's a buttor not is unspecified) has a class of .tab-button. this.e.tabs = the parent for all of the tab elements. It is legal, within reason, to manipulate these a bit, in particular this.e.container, e.g. by adding more children to it. Do not remove elements from the tabs or tabBar, however, or the tab state may get sorely out of sync. CSS classes: the container element has whatever class(es) the client sets on. this.e.tabBar gets the 'tab-bar' class and this.e.tabs gets the 'tabs' class. It's hypothetically possible to move the tabs to either side or the bottom using only CSS, but it's never been tested. */ init: function(container){ container = tabArg(container); const cID = container.getAttribute('id'); if(!cID){ throw new Error("Tab container element is missing 'id' attribute."); } const c = this.e.container = container; this.e.tabBar = D.addClass(D.div(),'tab-bar'); this.e.tabs = D.addClass(D.div(),'tabs'); D.append(c, this.e.tabBar, this.e.tabs); let selectIndex = 0; EA('[data-tab-parent='+cID+']').forEach((c,n)=>{ if(+c.dataset.tabSelect) selectIndex=n; this.addTab(c); }); return this.switchToTab(selectIndex); }, /** For the given tab element, unique selector string, or integer (0-based tab number), returns the button associated with that tab, or undefined if the argument does not match any current tab. */ getButtonForTab: function(tab){ tab = tabArg(tab,this); var i = -1; this.e.tabs.childNodes.forEach(function(e,n){ if(e===tab) i = n; }); return i>=0 ? this.e.tabBar.childNodes[i] : undefined; }, /** Adds the given DOM element or unique selector as the next tab in the tab container, adding a button to switch to the tab. Returns this object. If this object's options include a truthy tabAccessKeys then each tab button gets assigned an accesskey attribute equal to its 1-based index in the tab list. e.g. key 1 is the first tab and key 5 is the 5th. Whether/how that accesskey is accessed is dependent on the browser and its OS: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey */ addTab: function f(tab){ if(!f.click){ f.click = function(e){ e.target.$manager.switchToTab(e.target.$tab); }; } tab = tabArg(tab); tab.remove(); D.append(this.e.tabs, D.addClass(tab,'tab-panel')); const tabCount = this.e.tabBar.childNodes.length+1; const lbl = tab.dataset.tabLabel || 'Tab #'+tabCount; const btn = D.addClass(D.append(D.span(), lbl), 'tab-button'); D.append(this.e.tabBar,btn); btn.$manager = this; btn.$tab = tab; if(this.options.tabAccessKeys){ D.attr(btn, 'accesskey', tabCount); } btn.addEventListener('click', f.click, false); return this; }, /** Internal. Fires a new CustomEvent to all listeners which have registered via this.addEventListener(). */ _dispatchEvent: function(name, detail){ try{ this.e.container.dispatchEvent( new CustomEvent(name, {detail: detail}) ); }catch(e){ /* ignore */ } return this; }, /** Registers an event listener for this object's custom events. The callback gets a CustomEvent object with a 'detail' propertly holding any tab-related state for the event. The events are: - 'before-switch-from' is emitted immediately before a new tab is switched away from. detail = the tab element being switched away from. - 'before-switch-to' is emitted immediately before a new tab is switched to. detail = the tab element. - 'after-switch-to' is emitted immediately after a new tab is switched to. detail = the tab element. Any exceptions thrown by listeners are caught and ignored, to avoid that they knock the tab state out of sync. Returns this object. */ addEventListener: function(eventName, callback){ this.e.container.addEventListener(eventName, callback, false); return this; }, /** Inserts the given DOM element immediately after the tab bar. Intended for a status bar or similar always-visible component. Returns this object. */ addCustomWidget: function(e){ this.e.container.insertBefore(e, this.e.tabs); return this; }, /** If the given DOM element, unique selector, or integer (0-based tab number) is one of this object's tabs, the UI makes that tab the currently-visible one, firing any relevant events. Returns this object. If the argument is the current tab, this is a no-op, and no events are fired. */ switchToTab: function(tab){ tab = tabArg(tab,this); const self = this; if(tab===this._currentTab) return this; else if(this._currentTab){ this._dispatchEvent('before-switch-from', this._currentTab); } delete this._currentTab; this.e.tabs.childNodes.forEach((e,ndx)=>{ const btn = this.e.tabBar.childNodes[ndx]; if(e===tab){ if(D.hasClass(e,'selected')){ return; } self._dispatchEvent('before-switch-to',tab); setVisible(e, true); this._currentTab = e; D.addClass(btn,'selected'); self._dispatchEvent('after-switch-to',tab); }else{ if(D.hasClass(e,'selected')){ return; } setVisible(e, false); D.removeClass(btn,'selected'); } }); return this; } }; F.TabManager = TabManager; })(window.fossil); |
Added src/fossil.wikiedit-wysiwyg.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /** A slight adaptation of fossil's legacy wysiwyg wiki editor which makes it usable with the newer editor's edit widget replacement API. Requires: window.fossil, fossil.dom, and that the current page is /wikiedit. If called from another page it returns without effect. Caveat: this is an all-or-nothing solution. That is, once plugged in to /wikiedit, it cannot be removed without reloading the page. That is a limitation of the current editor-widget-swapping API. */ (function(F/*fossil object*/){ 'use strict'; if(!F || !F.page || F.page.name!=='wikiedit') return; const D = F.dom; //////////////////////////////////////////////////////////////////////// // Install an app-specific stylesheet... (function(){ const head = document.head || document.querySelector('head'), styleTag = document.createElement('style'), styleCSS = ` .intLink { cursor: pointer; } img.intLink { border: 0; } #wysiwyg-container { display: flex; flex-direction: column; max-width: 100% /* w/o this, toolbars don't wrap properly! */ } #wysiwygBox { border: 1px solid rgba(127,127,127,0.3); border-radius: 0.25em; padding: 0.25em 1em; margin: 0; overflow: auto; min-height: 20em; resize: vertical; } #wysiwygEditMode { /* wrapper for radio buttons */ border: 1px solid rgba(127,127,127,0.3); border-radius: 0.25em; padding: 0 0.35em 0 0.35em } #wysiwygEditMode > * { vertical-align: text-top; } #wysiwygEditMode label { cursor: pointer; } #wysiwyg-toolbars { margin: 0 0 0.25em 0; display: flex; flex-wrap: wrap; flex-direction: column; align-items: flex-start; } #wysiwyg-toolbars > * { margin: 0 0.5em 0.25em 0; } #wysiwyg-toolBar1, #wysiwyg-toolBar2 { margin: 0 0.2em 0.2em 0; display: flex; flex-flow: row wrap; } #wysiwyg-toolBar1 > * { /* formatting buttons */ vertical-align: middle; margin: 0 0.25em 0.25em 0; } #wysiwyg-toolBar2 > * { /* icons */ border: 1px solid rgba(127,127,127,0.3); vertical-align: baseline; margin: 0.1em; } `; head.appendChild(styleTag); styleTag.type = 'text/css'; D.append(styleTag, styleCSS); })(); const outerContainer = D.attr(D.div(), 'id', 'wysiwyg-container'), toolbars = D.attr(D.div(), 'id', 'wysiwyg-toolbars'), toolbar1 = D.attr(D.div(), 'id', 'wysiwyg-toolBar1'), // ^^^ formatting options toolbar2 = D.attr(D.div(), 'id', 'wysiwyg-toolBar2') // ^^^^ action icon buttons ; D.append(outerContainer, D.append(toolbars, toolbar1, toolbar2)); /** Returns a function which simplifies adding a list of options to the given select element. See below for example usage. */ const addOptions = function(select){ return function ff(value, label){ D.option(select, value, label || value); return ff; }; }; //////////////////////////////////////////////////////////////////////// // Edit mode selection (radio buttons). const radio0 = D.attr( D.input('radio'), 'name','wysiwyg-mode', 'id', 'wysiwyg-mode-0', 'value',0, 'checked',true), radio1 = D.attr( D.input('radio'), 'id','wysiwyg-mode-1', 'name','wysiwyg-mode', 'value',1), radios = D.append( D.attr(D.span(), 'id', 'wysiwygEditMode'), radio0, D.append( D.attr(D.label(), 'for', 'wysiwyg-mode-0'), "WYSIWYG" ), radio1, D.append( D.attr(D.label(), 'for', 'wysiwyg-mode-1'), "Raw HTML" ) ); D.append(toolbar1, radios); const radioHandler = function(){setDocMode(+this.value)}; radio0.addEventListener('change',radioHandler, false); radio1.addEventListener('change',radioHandler, false); //////////////////////////////////////////////////////////////////////// // Text formatting options... var select; select = D.addClass(D.select(), 'format'); select.dataset.format = "formatblock"; D.append(toolbar1, select); addOptions(select)( '', '- formatting -')( "h1", "Title 1 <h1>")( "h2", "Title 2 <h2>")( "h3", "Title 3 <h3>")( "h4", "Title 4 <h4>")( "h5", "Title 5 <h5>")( "h6", "Subtitle <h6>")( "p", "Paragraph <p>")( "pre", "Preformatted <pre>"); select = D.addClass(D.select(), 'format'); select.dataset.format = "fontname"; D.append(toolbar1, select); D.addClass( D.option(select, '', '- font -'), "heading" ); addOptions(select)( 'Arial')( 'Arial Black')( 'Courier New')( 'Times New Roman'); select = D.addClass(D.select(), 'format'); D.append(toolbar1, select); select.dataset.format = "fontsize"; D.addClass( D.option(select, '', '- size -'), "heading" ); addOptions(select)( "1", "Very small")( "2", "A bit small")( "3", "Normal")( "4", "Medium-large")( "5", "Big")( "6", "Very big")( "7", "Maximum"); select = D.addClass(D.select(), 'format'); D.append(toolbar1, select); select.dataset.format = 'forecolor'; D.addClass( D.option(select, '', '- color -'), "heading" ); addOptions(select)( "red", "Red")( "blue", "Blue")( "green", "Green")( "black", "Black")( "grey", "Grey")( "yellow", "Yellow")( "cyan", "Cyan")( "magenta", "Magenta"); //////////////////////////////////////////////////////////////////////// // Icon-based toolbar... /** Inject the icons... mkbuiltins strips anything which looks like a C++-style comment, even if it's in a string literal, and thus the runs of "/" characters in the DOM element data attributes have been mangled to work around that: we simply use \x2f for every 2nd slash. */ (function f(title,format,src){ const img = D.img(); D.append(toolbar2, img); D.addClass(img, 'intLink'); D.attr(img, 'title', title); img.dataset.format = format; D.attr(img, 'src', 'string'===typeof src ? src : src.join('')); return f; })( 'Undo', 'undo', ["data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7", "/I19DV3NHa7P/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f", "/\x2f/\x2f/\x2f/yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq704680", "7TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfE", "whdRIH4fh/DZMICe3/C4nBQBADs="] )( 'Redo','redo', ["data:image/gif;base64,R0lGODlhFgAWAMIHAB1ChDljwl9vj1iE34Kl8aPD+7/I1/", "/\x2f/yH5BAEKAAcALAAAAAAWABYAAANKeLrc/jDKSesyphi7SiEgsVXZEATDICqBVJjpqWZt9Na", "EDNbQK1wCQsxlYnxMAImhyDoFAElJasRRvAZVRqqQXUy7Cgx4TC6bswkAOw=="] )( "Remove formatting", "removeFormat", ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AA", "AABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwA", "AAAd0SU1FB9oECQMCKPI8CIIAAAAIdEVYdENvbW1lbnQA9syWvwAAAuhJREFUOMtjYBgFxAB5", "01ZWBvVaL2nHnlmk6mXCJbF69zU+Hz/9fB5O1lx+bg45qhl8/fYr5it3XrP/YWTUvvvk3VeqG", "Xz70TvbJy8+Wv39+2/Hz19/mGwjZzuTYjALuoBv9jImaXHeyD3H7kU8fPj2ICML8z92dlbtMz", "deiG3fco7J08foH1kurkm3E9iw54YvKwuTuom+LPt/BgbWf3/\x2fsf37/1/c02cCG1lB8f/\x2ff95", "DZx74MTMzshhoSm6szrQ/a6Ir/Z2RkfEjBxuLYFpDiDi6Af/\x2f/2ckaHBp7+7wmavP5n76+P2C", "lrLIYl8H9W36auJCbCxM4szMTJac7Kza/\x2f/\x2fR3H1w2cfWAgafPbqs5g7D95++/P1B4+ECK8tA", "wMDw/1H7159+/7r7ZcvPz4fOHbzEwMDwx8GBgaGnNatfHZx8zqrJ+4VJBh5CQEGOySEua/v3n", "7hXmqI8WUGBgYGL3vVG7fuPK3i5GD9/fja7ZsMDAzMG/Ze52mZeSj4yu1XEq/ff7W5dvfVAS1", "lsXc4Db7z8C3r8p7Qjf/\x2f/2dnZGxlqJuyr3rPqQd/Hhyu7oSpYWScylDQsd3kzvnH738wMDzj", "5GBN1VIWW4c3KDon7VOvm7S3paB9u5qsU5/x5KUnlY+eexQbkLNsErK61+++VnAJcfkyMTIwf", "fj0QwZbJDKjcETs1Y8evyd48toz8y/ffzv/\x2fvPP4veffxpX77z6l5JewHPu8MqTDAwMDLzyrj", "b/mZm0JcT5Lj+89+Ybm6zz95oMh7s4XbygN3Sluq4Mj5K8iKMgP4f0/\x2f/\x2ffv77/\x2f8nLy+7MCc", "XmyYDAwODS9jM9tcvPypd35pne3ljdjvj26+H2dhYpuENikgfvQeXNmSl3tqepxXsqhXPyc66", "6s+fv1fMdKR3TK72zpix8nTc7bdfhfkEeVbC9KhbK/9iYWHiErbu6MWbY/7/\x2f8/4/\x2f9/pgOnH", "6jGVazvFDRtq2VgiBIZrUTIBgCk+ivHvuEKwAAAAABJRU5ErkJggg=="] )( "Bold", "bold", ["data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB", "YAQAInhI+pa+H9mJy0LhdgtrxzDG5WGFVk6aXqyk6Y9kXvKKNuLbb6zgMFADs="] )( "Italic", "italic", ["data:image/gif;base64,R0lGODlhFgAWAKEDAAAAAF9vj5WIbf/\x2f/yH5BAEAAAMALA", "AAAAAWABYAAAIjnI+py+0Po5x0gXvruEKHrF2BB1YiCWgbMFIYpsbyTNd2UwAAOw=="] )( "Underline", "underline", ["data:image/gif;base64,R0lGODlhFgAWAKECAAAAAF9vj/\x2f/\x2f/\x2f/\x2fyH5BAEAAAIALA", "AAAAAWABYAAAIrlI+py+0Po5zUgAsEzvEeL4Ea15EiJJ5PSqJmuwKBEKgxVuXWtun+DwxCCgA", "7"] )( "Left align", "justifyleft", ["data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB", "YAQAIghI+py+0Po5y02ouz3jL4D4JMGELkGYxo+qzl4nKyXAAAOw=="] )( "Center align", "justifycenter", ["data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB", "YAQAIfhI+py+0Po5y02ouz3jL4D4JOGI7kaZ5Bqn4sycVbAQA7"] )( "Right align", "justifyright", ["data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB", "YAQAIghI+py+0Po5y02ouz3jL4D4JQGDLkGYxouqzl43JyVgAAOw=="] )( "Numbered list", "insertorderedlist", ["data:image/gif;base64,R0lGODlhFgAWAMIGAAAAADljwliE35GjuaezxtHa7P/\x2f/\x2f", "/\x2f/yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKSespwjoRFvggCBUBoTFBeq6QIAysQnRHaEO", "zyaZ07Lu9lUBnC0UGQU1K52s6n5oEADs="] )( "Dotted list", "insertunorderedlist", ["data:image/gif;base64,R0lGODlhFgAWAMIGAAAAAB1ChF9vj1iE33mOrqezxv/\x2f/\x2f", "/\x2f/yH5BAEAAAcALAAAAAAWABYAAAMyeLrc/jDKSesppNhGRlBAKIZRERBbqm6YtnbfMY7lud6", "4UwiuKnigGQliQuWOyKQykgAAOw=="] )( "Quote", "formatblock", ["data:image/gif;base64,R0lGODlhFgAWAIQXAC1NqjFRjkBgmT9nqUJnsk9xrFJ7u2", "R9qmKBt1iGzHmOrm6Sz4OXw3Odz4Cl2ZSnw6KxyqO306K63bG70bTB0rDI3bvI4P", "/\x2f/\x2f/\x2f/\x2f/", "/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f", "/\x2f/\x2f/\x2fyH5BAEKAB8ALAAAAAAWABYAAAVP4CeOZGmeaKqubEs2Cekk", "ErvEI1zZuOgYFlakECEZFi0GgTGKEBATFmJAVXweVOoKEQgABB9IQDCmrLpjETrQQlhHjINrT", "q/b7/i8fp8PAQA7"] )( "Delete indentation", "outdent", ["data:image/gif;base64,R0lGODlhFgAWAMIHAAAAADljwliE35GjuaezxtDV3NHa7P", "/\x2f/yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKCQG9F2i7u8agQgyK1z2EIBil+TWqEMxhMcz", "sYVJ3e4ahk+sFnAgtxSQDqWw6n5cEADs="] )( "Add indentation", "indent", ["data:image/gif;base64,R0lGODlhFgAWAOMIAAAAADljwl9vj1iE35GjuaezxtDV3N", "Ha7P/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f", "/\x2f/\x2f/yH5BAEAAAgALAAAAAAWABYAAAQ7EMlJq704650", "B/x8gemMpgugwHJNZXodKsO5oqUOgo5KhBwWESyMQsCRDHu9VOyk5TM9zSpFSr9gsJwIAOw==" ] )( "Hyperlink", "createlink", ["data:image/gif;base64,R0lGODlhFgAWAOMKAB1ChDRLY19vj3mOrpGjuaezxrCztb", "/I19Ha7Pv8/f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f", "/yH5BAEKAA8ALAAAAAAWABYAAARY8MlJq704682", "7/2BYIQVhHg9pEgVGIklyDEUBy/RlE4FQF4dCj2AQXAiJQDCWQCAEBwIioEMQBgSAFhDAGghG", "i9XgHAhMNoSZgJkJei33UESv2+/4vD4TAQA7"] )( "Cut", "cut", ["data:image/gif;base64,R0lGODlhFgAWAIQSAB1ChBFNsRJTySJYwjljwkxwl19vj1", "dusYODhl6MnHmOrpqbmpGjuaezxrCztcDCxL/I18rL1P/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f", "/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/", "/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f", "yH5BAEAAB8ALAAAAAAWABYAAAVu4CeOZGmeaKqubDs6TNnE", "bGNApNG0kbGMi5trwcA9GArXh+FAfBAw5UexUDAQESkRsfhJPwaH4YsEGAAJGisRGAQY7UCC9", "ZAXBB+74LGCRxIEHwAHdWooDgGJcwpxDisQBQRjIgkDCVlfmZqbmiEAOw=="] )( "Copy", "copy", ["data:image/gif;base64,R0lGODlhFgAWAIQcAB1ChBFNsTRLYyJYwjljwl9vj1iE31", "iGzF6MnHWX9HOdz5GjuYCl2YKl8ZOt4qezxqK63aK/9KPD+7DI3b/I17LM/MrL1MLY9NHa7OP", "s++bx/Pv8/f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f", "/yH5BAEAAB8ALAAAAAAWABYAAAWG4CeOZGmeaKqubOum1SQ/", "kPVOW749BeVSus2CgrCxHptLBbOQxCSNCCaF1GUqwQbBd0JGJAyGJJiobE+LnCaDcXAaEoxhQ", "ACgNw0FQx9kP+wmaRgYFBQNeAoGihCAJQsCkJAKOhgXEw8BLQYciooHf5o7EA+kC40qBKkAAA", "Grpy+wsbKzIiEAOw=="] )( /* Paste, when activated via JS, has no effect in some (maybe all) environments. Activated externally, e.g. keyboard, it works. */ "Paste (does not work in all environments)", "paste", ["data:image/gif;base64,R0lGODlhFgAWAIQUAD04KTRLY2tXQF9vj414WZWIbXmOrp", "qbmpGjudClFaezxsa0cb/I1+3YitHa7PrkIPHvbuPs+/fvrvv8/f/\x2f/\x2f/\x2f", "/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/", "/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f/\x2f", "yH5BAEAAB8ALAAAAAAWABYAAAWN4CeOZGmeaKqubGsusPvB", "SyFJjVDs6nJLB0khR4AkBCmfsCGBQAoCwjF5gwquVykSFbwZE+AwIBV0GhFog2EwIDchjwRiQ", "o9E2Fx4XD5R+B0DDAEnBXBhBhN2DgwDAQFjJYVhCQYRfgoIDGiQJAWTCQMRiwwMfgicnVcAAA", "MOaK+bLAOrtLUyt7i5uiUhADs="] ); //////////////////////////////////////////////////////////////////////// // The main editor area... const oDoc = D.attr(D.div(), 'id', "wysiwygBox"); D.attr(oDoc, 'contenteditable', 'true'); D.append(outerContainer, oDoc); /* Initialize the document editor */ function initDoc() { initEventHandlers(); if (!isWysiwyg()) { setDocMode(true); } } function initEventHandlers() { //console.debug("initEventHandlers()"); const handleDropDown = function() { formatDoc(this.dataset.format,this[this.selectedIndex].value); this.selectedIndex = 0; }; const handleFormatButton = function() { var extra; switch (this.dataset.format) { case 'createlink': const sLnk = prompt('Target URL:',''); if(sLnk) extra = sLnk; break; case 'formatblock': extra = 'blockquote'; break; } formatDoc(this.dataset.format, extra); }; var i, controls = outerContainer.querySelectorAll('select.format'); for(i = 0; i < controls.length; i++) { controls[i].addEventListener('change', handleDropDown, false);; } controls = outerContainer.querySelectorAll('.intLink'); for(i = 0; i < controls.length; i++) { controls[i].addEventListener('click', handleFormatButton, false); } } /* Return true if the document editor is in WYSIWYG mode. Return ** false if it is in Markup mode */ function isWysiwyg() { return radio0.checked; } /* 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, content) { if(undefined===content){ content = bToMarkup ? oDoc.innerHTML : oDoc.innerText; } if(!setDocMode.linebreak){ setDocMode.linebreak = new RegExp("</p><p>","ig"); } if(!setDocMode.toHide){ setDocMode.toHide = toolbars.querySelectorAll( '#wysiwyg-toolBar1 > *:not(#wysiwygEditMode), ' +'#wysiwyg-toolBar2'); } if (bToMarkup) { /* WYSIWYG -> Markup */ // Legacy did this: content=content.replace(setDocMode.linebreak,"</p>\n\n<p>") D.append(D.clearElement(oDoc), content) oDoc.style.whiteSpace = "pre-wrap"; D.addClass(setDocMode.toHide, 'hidden'); } else { /* Markup -> WYSIWYG */ D.parseHtml(D.clearElement(oDoc), content); oDoc.style.whiteSpace = "normal"; D.removeClass(setDocMode.toHide, 'hidden'); } oDoc.focus(); } //////////////////////////////////////////////////////////////////////// // A hook which can be activated via a site skin to plug this editor // in to the wikiedit page. F.page.wysiwyg = { // only for debugging: oDoc: oDoc, /* Replaces wikiedit's default editor widget with this wysiwyg editor. Must either be called via an onPageLoad handler via the site skin's footer or else it can be called manually from the dev tools console. Calling it too early (e.g. in the page footer outside of an an onPageLoad handler) will crash because wikiedit has not been initialized. */ init: function(){ initDoc(); const content = F.page.wikiContent() || ''; var isDirty = false /* keep from stashing too often */; F.page.setContentMethods( function(){ const rc = isWysiwyg() ? oDoc.innerHTML : oDoc.innerText; return rc; }, function(content){ isDirty = false; setDocMode(radio0.checked ? 0 : 1, content); } ); oDoc.addEventListener('blur', function(){ if(isDirty) F.page.notifyOfChange(); }, false); oDoc.addEventListener('input', function(){isDirty = true}, false); F.page.wikiContent(content)/*feed it back in to our widget*/; F.page.replaceEditorElement(outerContainer); F.message("Replaced wiki editor widget with legacy wysiwyg editor."); } }; })(window.fossil); |
Added src/fshell.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 | /* ** Copyright (c) 2016 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 module contains the code that implements the "fossil shell" command. ** ** The fossil shell prompts for lines of user input, then parses each line ** after the fashion of a standard Bourne shell and forks a child process ** to run the corresponding Fossil command. This only works on Unix. ** ** The "fossil shell" command is intended for use with SEE-enabled fossil. ** It allows multiple commands to be issued without having to reenter the ** crypto phasephrase for each command. */ #include "config.h" #include "fshell.h" #include <ctype.h> #ifndef _WIN32 # include "linenoise.h" # include <sys/types.h> # include <sys/wait.h> #endif /* ** COMMAND: shell* ** ** Usage: %fossil shell ** ** Prompt for lines of input from stdin. Parse each line and evaluate ** it as a separate fossil command, in a child process. The initial ** "fossil" is omitted from each line. ** ** This command only works on unix-like platforms that support fork(). ** It is non-functional on Windows. */ void shell_cmd(void){ #ifdef _WIN32 fossil_fatal("the 'shell' command is not supported on windows"); #else int nArg; int mxArg = 0; int n, i; char **azArg = 0; int fDebug; pid_t childPid; char *zLine = 0; char *zPrompt = 0; fDebug = find_option("debug", 0, 0)!=0; db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0); if(g.zRepositoryName!=0){ zPrompt = mprintf("fossil (%z)> ", db_get("project-name","unnamed")); }else{ zPrompt = mprintf("fossil (no repo)> "); } db_close(0); sqlite3_shutdown(); linenoiseSetMultiLine(1); while( (free(zLine), zLine = linenoise(zPrompt)) ){ /* Remember shell history within the current session */ linenoiseHistoryAdd(zLine); /* Parse the line of input */ n = (int)strlen(zLine); for(i=0, nArg=1; i<n; i++){ while( fossil_isspace(zLine[i]) ){ i++; } if( i>=n ) break; if( nArg>=mxArg ){ mxArg = nArg+10; azArg = fossil_realloc(azArg, sizeof(char*)*mxArg); if( nArg==1 ) azArg[0] = g.argv[0]; } if( zLine[i]=='"' || zLine[i]=='\'' ){ char cQuote = zLine[i]; i++; azArg[nArg++] = &zLine[i]; for(i++; i<n && zLine[i]!=cQuote; i++){} }else{ azArg[nArg++] = &zLine[i]; while( i<n && !fossil_isspace(zLine[i]) ){ i++; } } zLine[i] = 0; } /* If the --debug flag was used, display the parsed arguments */ if( fDebug ){ for(i=1; i<nArg; i++){ fossil_print("argv[%d] = [%s]\n", i, azArg[i]); } } /* Special cases */ if( nArg<2 ) continue; if( fossil_strcmp(azArg[1],"exit")==0 ) break; /* Fork a process to handle the command */ childPid = fork(); if( childPid<0 ){ printf("could not fork a child process to handle the command\n"); fflush(stdout); continue; } if( childPid==0 ){ /* This is the child process */ int main(int, char**); fossil_main(nArg, azArg); exit(0); }else{ /* The parent process */ int status; waitpid(childPid, &status, 0); } } free(zPrompt); #endif } |
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 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | /* ** 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> #ifdef FOSSIL_HAVE_FUSEFS #include <errno.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include "fusefs.h" #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.az[] 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 && pFile->zName[nPrev]=='/' ) 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 (FuseFS) 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 an artifact 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 installed 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){ #ifdef FOSSIL_HAVE_FUSEFS 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, ExtFILE, 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(); #else fprintf(stderr, "The FuseFS is not available in this build.\n"); exit(1); #endif /* FOSSIL_HAVE_FUSEFS */ } /* ** Return version numbers for the FUSE header that was used at compile-time ** and/or the FUSE library that was loaded at runtime. */ const char *fusefs_lib_version(void){ #if defined(FOSSIL_HAVE_FUSEFS) && FUSE_MAJOR_VERSION>=3 return fuse_pkgversion(); #else return "unknown"; #endif } const char *fusefs_inc_version(void){ #ifdef FOSSIL_HAVE_FUSEFS return COMPILER_STRINGIFY(FUSE_MAJOR_VERSION) "." COMPILER_STRINGIFY(FUSE_MINOR_VERSION); #else return "unknown"; #endif } |
Added src/fuzz.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 | /* ** Copyright (c) 2019 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 connect Fossil to libFuzzer. Do a web search ** for "libfuzzer" for details about that fuzzing platform. ** ** To build on linux (the only platform for which this works at ** present) first do ** ** ./configure ** ** Then edit the Makefile as follows: ** ** (1) Change CC to be "clang-6.0" or some other compiler that ** supports libFuzzer ** ** (2) Change APPNAME to "fossil-fuzz" ** ** (3) Add "-fsanitize=fuzzer" and "-DFOSSIL_FUZZ" to TCCFLAGS. Perhaps ** make the first change "-fsanitize=fuzzer,undefined,address" for ** extra, but slower, testing. ** ** Then build the fuzzer using: ** ** make clean fossil-fuzz ** ** To run the fuzzer, create a working directory ("cases"): ** ** mkdir cases ** ** Then seed the working directory with example input files. For example, ** if fuzzing the wiki formatter, perhaps copy *.wiki into cases. Then ** run the fuzzer thusly: ** ** fossil-fuzz cases ** ** The default is to fuzz the Fossil-wiki translator. Use the --fuzztype TYPE ** option to fuzz different aspects of the system. */ #include "config.h" #include "fuzz.h" #if LOCAL_INTERFACE /* ** Type of fuzzing: */ #define FUZZ_WIKI 0 /* The Fossil-Wiki formatter */ #define FUZZ_MARKDOWN 1 /* The Markdown formatter */ #define FUZZ_ARTIFACT 2 /* Fuzz the artifact parser */ #define FUZZ_WIKI2 3 /* FOSSIL_WIKI and FOSSIL_MARKDOWN */ #endif /* The type of fuzzing to do */ static int eFuzzType = FUZZ_WIKI; /* The fuzzer invokes this routine once for each fuzzer input */ int LLVMFuzzerTestOneInput(const uint8_t *aData, size_t nByte){ Blob in, out; blob_init(&in, 0, 0); blob_append(&in, (char*)aData, (int)nByte); blob_zero(&out); switch( eFuzzType ){ case FUZZ_WIKI: { wiki_convert(&in, &out, 0); blob_reset(&out); break; } case FUZZ_MARKDOWN: { Blob title = BLOB_INITIALIZER; blob_reset(&out); markdown_to_html(&in, &title, &out); blob_reset(&title); break; } case FUZZ_WIKI2: { Blob title = BLOB_INITIALIZER; wiki_convert(&in, &out, 0); blob_reset(&out); markdown_to_html(&in, &title, &out); blob_reset(&title); break; } case FUZZ_ARTIFACT: fossil_fatal("FUZZ_ARTIFACT is not implemented."); break; } blob_reset(&in); blob_reset(&out); return 0; } /* ** Check fuzzer command-line options. */ static void fuzzer_options(void){ const char *zType; db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); db_multi_exec("PRAGMA query_only=1;"); zType = find_option("fuzztype",0,1); if( zType==0 || fossil_strcmp(zType,"wiki")==0 ){ eFuzzType = FUZZ_WIKI; }else if( fossil_strcmp(zType,"markdown")==0 ){ eFuzzType = FUZZ_MARKDOWN; }else if( fossil_strcmp(zType,"wiki2")==0 ){ eFuzzType = FUZZ_WIKI2; }else{ fossil_fatal("unknown fuzz type: \"%s\"", zType); } } /* Libfuzzer invokes this routine once prior to start-up to ** process command-line options. */ int LLVMFuzzerInitialize(int *pArgc, char ***pArgv){ expand_args_option(*pArgc, *pArgv); fuzzer_options(); *pArgc = g.argc; *pArgv = g.argv; return 0; } /* ** COMMAND: test-fuzz ** ** Usage: %fossil test-fuzz [-fuzztype TYPE] INPUTFILE... ** ** Run a fuzz test using INPUTFILE as the test data. TYPE can be one of: ** ** wiki Fuzz the Fossil-wiki translator ** markdown Fuzz the markdown translator ** artifact Fuzz the artifact parser ** wiki2 Fuzz the Fossil-wiki and markdown translator */ void fuzz_command(void){ Blob in; int i; fuzzer_options(); verify_all_options(); for(i=2; i<g.argc; i++){ blob_read_from_file(&in, g.argv[i], ExtFILE); LLVMFuzzerTestOneInput((const uint8_t*)in.aData, (size_t)in.nUsed); fossil_print("%s\n", g.argv[i]); blob_reset(&in); } } |
Added src/glob.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 | /* ** 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/ ** ******************************************************************************* ** ** This file contains code used to pattern matching using "glob" syntax. */ #include "config.h" #include "glob.h" #include <assert.h> /* ** Construct and return a string which is an SQL expression that will ** be TRUE if value zVal matches any of the GLOB expressions in the list ** zGlobList. For example: ** ** zVal: "x" ** zGlobList: "*.o,*.obj" ** ** Result: "(x GLOB '*.o' OR x GLOB '*.obj')" ** ** Commas and whitespace are considered to be element delimters. Each ** element of the GLOB list may optionally be enclosed in either '...' or ** "...". This allows commas and/or whitespace to be used in the elements ** 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]=='"' ){ cTerm = zGlobList[0]; zGlobList++; }else{ cTerm = ','; } /* Find the next delimter (or the end of the string). */ for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){ if( cTerm!=',' ) continue; /* If quoted, keep going. */ if( fossil_isspace(zGlobList[i]) ) break; /* If space, stop. */ } blob_appendf(&expr, "%s%s GLOB '%#q'", zSep, zVal, i, zGlobList); zSep = " OR "; if( cTerm!=',' && zGlobList[i] ) i++; zGlobList += i; 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. */ struct Glob { int nPattern; /* Number of patterns */ char **azPattern; /* Array of pointers to patterns */ }; #endif /* INTERFACE */ /* ** zPatternList is a comma- or whitespace-separated list of glob patterns. ** Parse that list and use it to create a new Glob object. ** ** Elements of the glob list may be optionally enclosed in single- or ** double-quotes. This allows commas and whitespace to be part of a ** glob pattern. ** ** Leading and trailing spaces on glob patterns are ignored unless quoted. ** ** An empty or null pattern list results in a null glob, which will ** match nothing. */ Glob *glob_create(const char *zPatternList){ int nList; /* Size of zPatternList in bytes */ int i; /* Loop counters */ Glob *p; /* The glob being created */ char *z; /* Copy of the pattern list */ char delimiter; /* '\'' or '\"' or 0 */ if( zPatternList==0 || zPatternList[0]==0 ) return 0; nList = strlen(zPatternList); p = fossil_malloc( sizeof(*p) + nList+1 ); memset(p, 0, sizeof(*p)); z = (char*)&p[1]; memcpy(z, zPatternList, nList+1); while( z[0] ){ while( fossil_isspace(z[0]) || z[0]==',' ){ z++; /* Skip leading commas, spaces, and newlines */ } if( z[0]==0 ) break; if( z[0]=='\'' || z[0]=='"' ){ delimiter = z[0]; z++; }else{ delimiter = ','; } p->azPattern = fossil_realloc(p->azPattern, (p->nPattern+1)*sizeof(char*) ); p->azPattern[p->nPattern++] = z; /* Find the next delimiter (or the end of the string). */ for(i=0; z[i] && z[i]!=delimiter && !(delimiter==',' && fossil_isspace(z[i])); i++){ /* keep looking for the end of the glob pattern */ } 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 */ void glob_free(Glob *pGlob){ if( pGlob ){ fossil_free(pGlob->azPattern); fossil_free(pGlob); } } /* ** Appends the given glob to the given buffer in the form of a ** JS/JSON-compatible array. It requires that pDest have been ** initialized. If pGlob is NULL or empty it emits [] (an empty ** array). */ void glob_render_json_to_blob(Glob *pGlob, Blob *pDest){ int i = 0; blob_append(pDest, "[", 1); for( ; pGlob && i < pGlob->nPattern; ++i ){ if(i){ blob_append(pDest, ",", 1); } blob_appendf(pDest, "%!j", pGlob->azPattern[i]); } blob_append(pDest, "]", 1); } /* ** Functionally equivalent to glob_render_json_to_blob() ** but outputs via cgi_print(). */ void glob_render_json_to_cgi(Glob *pGlob){ int i = 0; CX("["); for( ; pGlob && i < pGlob->nPattern; ++i ){ if(i){ CX(","); } CX("%!j", pGlob->azPattern[i]); } CX("]"); } /* ** COMMAND: test-glob ** ** Usage: %fossil test-glob PATTERN STRING... ** ** PATTERN is a comma- and whitespace-separated list of optionally ** quoted glob patterns. Show which of the STRINGs that follow match ** the PATTERN. ** ** If PATTERN begins with "@" the rest of the pattern is understood ** to be a setting name (such as binary-glob, crln-glob, or encoding-glob) ** and the value of that setting is used as the actually glob pattern. */ void glob_test_cmd(void){ Glob *pGlob; int i; char *zPattern; if( g.argc<4 ) usage("PATTERN STRING ..."); zPattern = g.argv[2]; if( zPattern[0]=='@' ){ db_find_and_open_repository(OPEN_ANY_SCHEMA,0); zPattern = db_get(zPattern+1, 0); if( zPattern==0 ) fossil_fatal("no such setting: %s", g.argv[2]+1); fossil_print("GLOB pattern: %s\n", zPattern); } fossil_print("SQL expression: %s\n", glob_expr("x", zPattern)); pGlob = glob_create(zPattern); for(i=0; i<pGlob->nPattern; i++){ fossil_print("pattern[%d] = [%s]\n", i, pGlob->azPattern[i]); } for(i=3; i<g.argc; i++){ fossil_print("%d %s\n", glob_match(pGlob, g.argv[i]), g.argv[i]); } glob_free(pGlob); } |
Changes to src/graph.c.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 | ******************************************************************************* ** ** This file contains code to compute a revision history graph. */ #include "config.h" #include "graph.h" #include <assert.h> #if INTERFACE | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > | | > > > | > | | | > | < > > > > | | > | > > | | > > | | | | | | | < > > > | > > > > > > > > > > > | < | | > > > > > > > > > | | | | > > > > | | < | | > | | > > > > > | | > > > > > > > > > > | | | | < | > > > > | > > | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < | < < | < | < < | > > | < | < | < > > < | < | < | | < < < > | | > < < < | > | > > > > > > > | < < > | | < > | | > > | > > > > > > > | > > > > > > | < | > | > | < > | < > > > > | | | > > > | > > > > > > | > > > > > > > > | > > > > > > | | > > > > | > > > | > | > > | > | > > | | > > > > > > > > > > > > | > > > > > > > > > | > > > | > > > > > > > > | < < < < < < < | > > > > < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ******************************************************************************* ** ** This file contains code to compute a revision history graph. */ #include "config.h" #include "graph.h" #include <assert.h> /* Notes: ** ** The graph is laid out in 1 or more "rails". A "rail" is a vertical ** band in the graph in which one can place nodes or arrows connecting ** nodes. There can be between 1 and GR_MAX_RAIL rails. If the graph ** is too complex to be displayed in GR_MAX_RAIL rails, it is omitted. ** ** A "riser" is the thick line that comes out of the top of a node and ** goes up to the next node on the branch, or to the top of the screen. ** A "descender" is a thick line that comes out of the bottom of a node ** and proceeds down to the bottom of the page. ** ** A "merge riser" is a thin line going up out of a node to indicate a ** merge or cherrypick. (Cherrypicks are drawn with thin dashed lines. ** Merges are drawn with thin solid lines.) A "merge riser" might go ** stright up out of the top of a leaf node, but for non-leaves, they ** go horizontally to their assigned rail first, then up. ** ** Invoke graph_init() to create a new GraphContext object. Then ** call graph_add_row() to add nodes, one by one, to the graph. ** Nodes must be added in display order, from top to bottom. ** Then invoke graph_render() to run the layout algorithm. The ** layout algorithm computes which rail each nodes sit on, and ** the rails used for merge arrows. */ #if INTERFACE /* ** The type of integer identifiers for rows of the graph. ** ** For a normal /timeline graph, the identifiers are never that big ** an an ordinary 32-bit int will work fine. But for the /finfo page, ** the identifier is a combination of the BLOB.RID and the FILENAME.FNID ** values, and so it can become quite large for repos that have both many ** check-ins and many files. For this reason, we make the identifier ** a 64-bit integer, to dramatically reduce the risk of an overflow. */ typedef sqlite3_int64 GraphRowId; #define GR_MAX_RAIL 40 /* Max number of "rails" to display */ /* The graph appears vertically beside a timeline. Each row in the ** timeline corresponds to a row in the graph. GraphRow.idx is 0 for ** the top-most row and increases moving down. Hence (in the absence of ** time skew) parents have a larger index than their children. ** ** The nParent field is -1 for entires that do not participate in the graph ** but which are included just so that we can capture their background color. */ struct GraphRow { GraphRowId rid; /* The rid for the check-in */ i8 nParent; /* Number of parents. */ i8 nCherrypick; /* Subset of aParent that are cherrypicks */ i8 nNonCherrypick; /* Number of non-cherrypick parents */ u8 nMergeChild; /* Number of merge children */ GraphRowId *aParent; /* Array of parents. 0 element is primary .*/ char *zBranch; /* Branch name */ char *zBgClr; /* Background Color */ char zUuid[HNAME_MAX+1]; /* Check-in for file ID */ GraphRow *pNext; /* Next row down in the list of all rows */ GraphRow *pPrev; /* Previous row */ int idx; /* Row index. Top row is smallest. */ 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 isStepParent; /* pChild is actually a step-child. The thick ** arrow up to the child is dashed, not solid */ u8 hasNormalOutMerge; /* Is parent of at laest 1 non-cherrypick merge */ u8 timeWarp; /* Child is earlier in time */ u8 bDescender; /* True if riser from bottom of graph to here. */ u8 selfUp; /* Space above this node but belonging */ i8 iRail; /* Which rail this check-in appears on. 0-based.*/ i8 mergeOut; /* Merge out to this rail. -1 if no merge-out */ 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 */ int cherrypickUpto; /* Continue the mergeOut rail up to here */ u64 mergeDown; /* Draw merge lines up from bottom of graph */ u64 cherrypickDown; /* Draw cherrypick lines up from bottom */ u64 railInUse; /* Mask of occupied rails at this row */ }; /* Context while building a graph */ struct GraphContext { int nErr; /* Number of errors encountered */ int mxRail; /* Number of rails required to render the graph */ GraphRow *pFirst; /* First row in the list. Top row of graph. */ GraphRow *pLast; /* Last row in the list. Bottom row of graph. */ int nBranch; /* Number of distinct branches */ char **azBranch; /* Names of the branches */ int nRow; /* Number of rows */ int nHash; /* Number of slots in apHash[] */ u8 hasOffsetMergeRiser; /* Merge arrow from leaf goes up on a different ** rail that the node */ u64 mergeRail; /* Rails used for merge lines */ GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */ }; #endif /* The N-th bit */ #define BIT(N) (((u64)1)<<(N)) /* ** Number of rows before and after a node with a riser or descender ** that goes off-screen before we can reuse that rail. */ #define RISER_MARGIN 4 /* ** Malloc for zeroed space. Panic if unable to provide the ** requested space. */ void *safeMalloc(int nByte){ void *p = fossil_malloc(nByte); memset(p, 0, nByte); return p; } /* ** Create and initialize a GraphContext */ GraphContext *graph_init(void){ return (GraphContext*)safeMalloc( sizeof(GraphContext) ); } /* ** Clear all content from a graph */ static void graph_clear(GraphContext *p){ int i; GraphRow *pRow; while( p->pFirst ){ pRow = p->pFirst; p->pFirst = pRow->pNext; free(pRow); } for(i=0; i<p->nBranch; i++) free(p->azBranch[i]); free(p->azBranch); free(p->apHash); memset(p, 0, sizeof(*p)); p->nErr = 1; } /* ** Destroy a GraphContext; */ void graph_free(GraphContext *p){ graph_clear(p); free(p); } /* ** Insert a row into the hash table. pRow->rid is the key. Keys must ** be unique. If there is already another row with the same rid, ** overwrite the prior entry if and only if the overwrite flag is set. */ static void hashInsert(GraphContext *p, GraphRow *pRow, int overwrite){ int h; h = pRow->rid % p->nHash; while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){ h++; if( h>=p->nHash ) h = 0; } if( p->apHash[h]==0 || overwrite ){ p->apHash[h] = pRow; } } /* ** Look up the row with rid. */ static GraphRow *hashFind(GraphContext *p, GraphRowId rid){ int h = rid % p->nHash; while( p->apHash[h] && p->apHash[h]->rid!=rid ){ h++; if( h>=p->nHash ) h = 0; } return p->apHash[h]; } /* ** 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++){ if( fossil_strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i]; } p->nBranch++; p->azBranch = fossil_realloc(p->azBranch, sizeof(char*)*p->nBranch); p->azBranch[p->nBranch-1] = mprintf("%s", zBranch); return p->azBranch[p->nBranch-1]; } /* ** Add a new row to the graph context. Rows are added from top to bottom. */ int graph_add_row( GraphContext *p, /* The context to which the row is added */ GraphRowId rid, /* RID for the check-in */ int nParent, /* Number of parents */ int nCherrypick, /* How many of aParent[] are actually cherrypicks */ GraphRowId *aParent, /* Array of parents */ const char *zBranch, /* Branch for this check-in */ const char *zBgClr, /* Background color. NULL or "" for white. */ const char *zUuid, /* hash name of the object being graphed */ int isLeaf /* True if this row is a leaf */ ){ GraphRow *pRow; int nByte; static int nRow = 0; if( p->nErr ) return 0; nByte = sizeof(GraphRow); if( nParent>0 ) nByte += sizeof(pRow->aParent[0])*nParent; pRow = (GraphRow*)safeMalloc( nByte ); pRow->aParent = nParent>0 ? (GraphRowId*)&pRow[1] : 0; pRow->rid = rid; if( nCherrypick>=nParent ){ nCherrypick = nParent-1; /* Safety. Should never happen. */ } pRow->nParent = nParent; pRow->nCherrypick = nCherrypick; pRow->nNonCherrypick = nParent - nCherrypick; pRow->zBranch = persistBranchName(p, zBranch); if( zUuid==0 ) zUuid = ""; sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid); pRow->isLeaf = isLeaf; memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser)); if( zBgClr==0 ) zBgClr = ""; pRow->zBgClr = persistBranchName(p, zBgClr); if( nParent>0 ) memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent); if( p->pFirst==0 ){ p->pFirst = pRow; }else{ p->pLast->pNext = pRow; } p->pLast = pRow; p->nRow++; pRow->idx = pRow->idxTop = ++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 */ int iNearto, /* Find rail nearest to this rail */ int bMergeRail /* This rail will be used for a merge line */ ){ GraphRow *pRow; int i; int iBest = 0; int iBestDist = 9999; u64 inUseMask = 0; for(pRow=p->pFirst; pRow && pRow->idx<top; pRow=pRow->pNext){} while( pRow && pRow->idx<=btm ){ inUseMask |= pRow->railInUse; pRow = pRow->pNext; } /* First look for a match that honors bMergeRail */ for(i=0; i<=p->mxRail; i++){ u64 m = BIT(i); int dist; if( inUseMask & m ) continue; if( (bMergeRail!=0) != ((p->mergeRail & m)!=0) ) continue; if( iNearto<=0 ){ iBest = i; iBestDist = 1; break; } dist = i - iNearto; if( dist<0 ) dist = -dist; if( dist<iBestDist ){ iBestDist = dist; iBest = i; } } /* If no match, consider all possible rails */ if( iBestDist>1000 ){ for(i=0; i<=p->mxRail+1; i++){ int dist; if( inUseMask & BIT(i) ) continue; if( iNearto<=0 ){ iBest = i; iBestDist = 1; break; } dist = i - iNearto; if( dist<0 ) dist = -dist; if( dist<iBestDist ){ iBestDist = dist; iBest = i; } } } if( iBestDist>1000 ) p->nErr++; if( iBest>p->mxRail ) p->mxRail = iBest; if( bMergeRail ) p->mergeRail |= BIT(iBest); return iBest; } /* ** Assign all children of node pBottom to the same rail as pBottom. */ static void assignChildrenToRail(GraphRow *pBottom, u32 tmFlags){ int iRail = pBottom->iRail; GraphRow *pCurrent; GraphRow *pPrior; u64 mask = ((u64)1)<<iRail; pBottom->railInUse |= mask; pPrior = pBottom; for(pCurrent=pBottom->pChild; pCurrent; pCurrent=pCurrent->pChild){ assert( pPrior->idx > pCurrent->idx ); assert( pCurrent->iRail<0 ); if( pPrior->timeWarp ) break; pCurrent->iRail = iRail; pCurrent->railInUse |= mask; pPrior->aiRiser[iRail] = pCurrent->idx; while( pPrior->idx > pCurrent->idx ){ pPrior->railInUse |= mask; pPrior = pPrior->pPrev; assert( pPrior!=0 ); } } /* Mask of additional rows for the riser to infinity */ if( !pPrior->isLeaf && (tmFlags & TIMELINE_DISJOINT)==0 ){ int n = RISER_MARGIN; GraphRow *p; pPrior->selfUp = 0; for(p=pPrior; p && (n--)>0; p=p->pPrev){ pPrior->selfUp++; p->railInUse |= mask; } } } /* ** Check to see if rail iRail is clear from pBottom up to and including ** pTop. */ static int railIsClear(GraphRow *pBottom, int iTop, int iRail){ u64 m = BIT(iRail); while( pBottom && pBottom->idx>=iTop ){ if( pBottom->railInUse & m ) return 0; pBottom = pBottom->pPrev; } return 1; } /* ** Create a merge-arrow riser going from pParent up to pChild. */ static void createMergeRiser( GraphContext *p, GraphRow *pParent, /* Lower node from which the merge line begins */ GraphRow *pChild, /* Upper node at which the merge line ends */ int isCherrypick /* True for a cherry-pick merge */ ){ int u; u64 mask; GraphRow *pLoop; if( pParent->mergeOut<0 ){ u = pParent->aiRiser[pParent->iRail]; if( u<0 && railIsClear(pParent->pPrev, pChild->idx, pParent->iRail) ){ /* pParent is a leaf and the merge-line can be drawn straight up.*/ pParent->mergeOut = pParent->iRail; mask = BIT(pParent->iRail); for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid; pLoop=pLoop->pNext){ pLoop->railInUse |= mask; } }else if( u>0 && u<pChild->idx ){ /* The thick arrow up to the next primary child of pDesc goes ** further up than the thin merge arrow riser, so draw them both ** on the same rail. */ pParent->mergeOut = pParent->iRail; }else if( pParent->idx - pChild->idx < pParent->selfUp ){ pParent->mergeOut = pParent->iRail; }else{ /* The thin merge arrow riser is taller than the thick primary ** child riser, so use separate rails. */ int iTarget = pParent->iRail; if( u<0 ) p->hasOffsetMergeRiser = 1; pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1, iTarget, 1); mask = BIT(pParent->mergeOut); for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid; pLoop=pLoop->pNext){ pLoop->railInUse |= mask; } } } if( isCherrypick ){ if( pParent->cherrypickUpto==0 || pParent->cherrypickUpto > pChild->idx ){ pParent->cherrypickUpto = pChild->idx; } }else{ pParent->hasNormalOutMerge = 1; if( pParent->mergeUpto==0 || pParent->mergeUpto > pChild->idx ){ pParent->mergeUpto = pChild->idx; } } pChild->mergeIn[pParent->mergeOut] = isCherrypick ? 2 : 1; } /* ** Compute the maximum rail number. */ static void find_max_rail(GraphContext *p){ GraphRow *pRow; p->mxRail = 0; for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut; while( p->mxRail<GR_MAX_RAIL && (pRow->mergeDown|pRow->cherrypickDown)>(BIT(p->mxRail+1)-1) ){ p->mxRail++; } } } /* ** Draw a riser from pRow upward to indicate that it is going ** to a node that is off the graph to the top. */ static void riser_to_top(GraphRow *pRow){ u64 mask = BIT(pRow->iRail); int n = RISER_MARGIN; pRow->aiRiser[pRow->iRail] = 0; while( pRow && (n--)>0 ){ pRow->railInUse |= mask; pRow = pRow->pPrev; } } /* ** Compute the complete graph ** ** When primary or merge parents are off-screen, normally a line is drawn ** from the node down to the bottom of the graph. This line is called a ** "descender". But if the omitDescenders flag is true, then lines down ** to the bottom of the screen are omitted. ** ** The tmFlags parameter is zero or more of the TIMELINE_* constants. ** Only the following are honored: ** ** TIMELINE_DISJOINT: Omit descenders ** TIMELINE_FILLGAPS: Use step-children ** TIMELINE_XMERGE: Omit off-graph merge lines */ void graph_finish(GraphContext *p, const char *zLeftBranch, u32 tmFlags){ GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent; int i, j; u64 mask; int hasDup = 0; /* True if one or more isDup entries */ const char *zTrunk; u8 *aMap; /* Copy of p->aiRailMap */ int omitDescenders = (tmFlags & TIMELINE_DISJOINT)!=0; int nTimewarp = 0; int riserMargin = (tmFlags & TIMELINE_DISJOINT) ? 0 : RISER_MARGIN; /* If mergeRiserFrom[X]==Y that means rail X holds a merge riser ** coming up from the bottom of the graph from off-screen check-in Y ** where Y is the RID. There is no riser on rail X if mergeRiserFrom[X]==0. */ GraphRowId mergeRiserFrom[GR_MAX_RAIL]; if( p==0 || p->pFirst==0 || p->nErr ) return; p->nErr = 1; /* Assume an error until proven otherwise */ /* Initialize all rows */ p->nHash = p->nRow*2 + 1; p->apHash = safeMalloc( sizeof(p->apHash[0])*p->nHash ); for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->pNext ) pRow->pNext->pPrev = pRow; pRow->iRail = -1; pRow->mergeOut = -1; if( (pDup = hashFind(p, pRow->rid))!=0 ){ hasDup = 1; pDup->isDup = 1; } hashInsert(p, pRow, 1); } p->mxRail = -1; memset(mergeRiserFrom, 0, sizeof(mergeRiserFrom)); /* 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( (tmFlags & (TIMELINE_DISJOINT|TIMELINE_XMERGE))!=0 ){ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ for(i=1; i<pRow->nParent; i++){ GraphRow *pParent = hashFind(p, pRow->aParent[i]); if( pParent==0 ){ memmove(pRow->aParent+i, pRow->aParent+i+1, sizeof(pRow->aParent[0])*(pRow->nParent-i-1)); pRow->nParent--; if( i<pRow->nNonCherrypick ){ pRow->nNonCherrypick--; }else{ pRow->nCherrypick--; } i--; } } } } /* Put the deepest (earliest) merge parent first in the list. ** An off-screen merge parent is considered deepest. */ for(pRow=p->pFirst; pRow; pRow=pRow->pNext ){ if( pRow->nParent<=1 ) continue; for(i=1; i<pRow->nParent; i++){ GraphRow *pParent = hashFind(p, pRow->aParent[i]); if( pParent ) pParent->nMergeChild++; } if( pRow->nCherrypick>1 ){ int iBest = -1; int iDeepest = -1; for(i=pRow->nNonCherrypick; i<pRow->nParent; i++){ GraphRow *pParent = hashFind(p, pRow->aParent[i]); if( pParent==0 ){ iBest = i; break; } if( pParent->idx>iDeepest ){ iDeepest = pParent->idx; iBest = i; } } i = pRow->nNonCherrypick; if( iBest>i ){ GraphRowId x = pRow->aParent[i]; pRow->aParent[i] = pRow->aParent[iBest]; pRow->aParent[iBest] = x; } } if( pRow->nNonCherrypick>2 ){ int iBest = -1; int iDeepest = -1; for(i=1; i<pRow->nNonCherrypick; i++){ GraphRow *pParent = hashFind(p, pRow->aParent[i]); if( pParent==0 ){ iBest = i; break; } if( pParent->idx>iDeepest ){ iDeepest = pParent->idx; iBest = i; } } if( iBest>1 ){ GraphRowId x = pRow->aParent[1]; pRow->aParent[1] = pRow->aParent[iBest]; pRow->aParent[iBest] = x; } } } /* 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->nNonCherrypick<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->nNonCherrypick; i++){ pParent = hashFind(p, pRow->aParent[i]); if( pParent && pParent->zBranch==pRow->zBranch ){ GraphRowId 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. */ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->isDup ) continue; if( pRow->nParent<=0 ) continue; /* Root node */ pParent = hashFind(p, pRow->aParent[0]); if( pParent==0 ) continue; /* Parent off-screen */ if( pParent->zBranch!=pRow->zBranch ) continue; /* Different branch */ if( pParent->idx <= pRow->idx ){ pParent->timeWarp = 1; nTimewarp++; }else if( pRow->idxTop < pParent->idxTop ){ pParent->pChild = pRow; pParent->idxTop = pRow->idxTop; } } if( tmFlags & TIMELINE_FILLGAPS ){ /* If a node has no pChild in the graph ** and there is a node higher up in the graph ** that is in the same branch and has no in-graph parent, then ** make the lower node a step-child of the upper node. This will ** be represented on the graph by a thick dotted line without an arrowhead. */ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->pChild ) continue; if( pRow->isLeaf ) continue; for(pLoop=pRow->pPrev; pLoop; pLoop=pLoop->pPrev){ if( pLoop->nParent>0 && pLoop->zBranch==pRow->zBranch && hashFind(p,pLoop->aParent[0])==0 ){ pRow->pChild = pLoop; pRow->isStepParent = 1; pLoop->aParent[0] = pRow->rid; break; } } } } /* Set the idxTop values for all entries. The idxTop value is the ** "idx" value for the top entry in its stack of children. */ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ GraphRow *pChild = pRow->pChild; if( pChild && pRow->idxTop>pChild->idxTop ){ pRow->idxTop = pChild->idxTop; } } /* Identify rows where the primary parent is off screen. Assign ** each to a rail and draw descenders downward. ** ** Strive to put the "trunk" branch on far left. */ zTrunk = persistBranchName(p, "trunk"); for(i=0; i<2; i++){ for(pRow=p->pLast; pRow; pRow=pRow->pPrev){ if( i==0 && pRow->zBranch!=zTrunk ) continue; if( pRow->iRail>=0 ) continue; if( pRow->isDup ) continue; if( pRow->nParent<0 ) continue; if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0); if( p->mxRail>=GR_MAX_RAIL ) return; mask = BIT(pRow->iRail); if( !omitDescenders ){ int n = RISER_MARGIN; pRow->bDescender = pRow->nParent>0; for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){ pLoop->railInUse |= mask; } } assignChildrenToRail(pRow, tmFlags); } } } /* Assign rails to all rows that are still unassigned. */ for(pRow=p->pLast; pRow; pRow=pRow->pPrev){ GraphRowId parentRid; if( pRow->iRail>=0 ){ if( pRow->pChild==0 && !pRow->timeWarp ){ if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){ riser_to_top(pRow); } } continue; } if( pRow->isDup || pRow->nParent<0 ){ continue; }else{ assert( pRow->nParent>0 ); parentRid = pRow->aParent[0]; pParent = hashFind(p, parentRid); if( pParent==0 ){ pRow->iRail = ++p->mxRail; if( p->mxRail>=GR_MAX_RAIL ) return; pRow->railInUse = BIT(pRow->iRail); continue; } if( pParent->idx>pRow->idx ){ /* Common case: Child occurs after parent and is above the ** parent in the timeline */ pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx, pParent->iRail, 0); if( p->mxRail>=GR_MAX_RAIL ) return; pParent->aiRiser[pRow->iRail] = pRow->idx; }else{ /* Timewarp case: Child occurs earlier in time than parent and ** appears below the parent in the timeline. */ int iDownRail = ++p->mxRail; if( iDownRail<1 ) iDownRail = ++p->mxRail; pRow->iRail = ++p->mxRail; if( p->mxRail>=GR_MAX_RAIL ) return; pRow->railInUse = BIT(pRow->iRail); pParent->aiRiser[iDownRail] = pRow->idx; mask = BIT(iDownRail); for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){ pLoop->railInUse |= mask; } } } mask = BIT(pRow->iRail); pRow->railInUse |= mask; if( pRow->pChild ){ assignChildrenToRail(pRow, tmFlags); }else if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){ if( !pRow->timeWarp ) riser_to_top(pRow); } if( pParent ){ if( pParent->idx>pRow->idx ){ /* Common case: Parent is below current row in the graph */ for(pLoop=pParent->pPrev; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){ pLoop->railInUse |= mask; } }else{ /* Timewarp case: Parent is above current row in the graph */ for(pLoop=pParent->pNext; pLoop && pLoop!=pRow; pLoop=pLoop->pNext){ pLoop->railInUse |= mask; } } } } /* ** Insert merge rails and merge arrows */ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ int iReuseIdx = -1; int iReuseRail = -1; int isCherrypick = 0; for(i=1; i<pRow->nParent; i++){ GraphRowId parentRid = pRow->aParent[i]; if( i==pRow->nNonCherrypick ){ /* Because full merges are laid out before cherrypicks, ** it is ok to use a full-merge raiser for a cherrypick. ** See the graph on check-in 8ac66ef33b464d28 for example ** iReuseIdx = -1; ** iReuseRail = -1; */ isCherrypick = 1; } pDesc = hashFind(p, parentRid); if( pDesc==0 ){ int iMrail = -1; /* Merge from a node that is off-screen */ if( iReuseIdx>=p->nRow+1 ){ continue; /* Suppress multiple off-screen merges */ } for(j=0; j<GR_MAX_RAIL; j++){ if( mergeRiserFrom[j]==parentRid ){ iMrail = j; break; } } if( iMrail==-1 ){ iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1); if( p->mxRail>=GR_MAX_RAIL ) return; mergeRiserFrom[iMrail] = parentRid; } iReuseIdx = p->nRow+1; iReuseRail = iMrail; mask = BIT(iMrail); if( i>=pRow->nNonCherrypick ){ pRow->mergeIn[iMrail] = 2; pRow->cherrypickDown |= mask; }else{ pRow->mergeIn[iMrail] = 1; pRow->mergeDown |= mask; } for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){ pLoop->railInUse |= mask; } }else{ /* The merge parent node does exist on this graph */ if( iReuseIdx>pDesc->idx && pDesc->nMergeChild==1 ){ /* Reuse an existing merge riser */ pDesc->mergeOut = iReuseRail; if( isCherrypick ){ pDesc->cherrypickUpto = pDesc->idx; }else{ pDesc->hasNormalOutMerge = 1; pDesc->mergeUpto = pDesc->idx; } }else{ /* Create a new merge for an on-screen node */ createMergeRiser(p, pDesc, pRow, isCherrypick); if( p->mxRail>=GR_MAX_RAIL ) return; if( iReuseIdx<0 && pDesc->nMergeChild==1 && (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf) ){ iReuseIdx = pDesc->idx; iReuseRail = pDesc->mergeOut; } } } } } /* ** Insert merge rails from primaries to duplicates. */ if( hasDup ){ int dupRail; int mxRail; find_max_rail(p); mxRail = p->mxRail; dupRail = mxRail+1; if( p->mxRail>=GR_MAX_RAIL ) return; for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( !pRow->isDup ) continue; pRow->iRail = dupRail; pDesc = hashFind(p, pRow->rid); assert( pDesc!=0 && pDesc!=pRow ); createMergeRiser(p, pDesc, pRow, 0); if( pDesc->mergeOut>mxRail ) mxRail = pDesc->mergeOut; } if( dupRail<=mxRail ){ dupRail = mxRail+1; for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->isDup ) pRow->iRail = dupRail; } } if( mxRail>=GR_MAX_RAIL ) return; } /* ** Find the maximum rail number. */ find_max_rail(p); /* If a leaf node has a merge riser going up on a different rail, ** try to move the rail of the node (and its ancestors) to be underneath ** the merge riser. This is an optimization that improves the ** appearance of graph but is not strictly necessary. */ if( nTimewarp==0 && p->hasOffsetMergeRiser ){ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ GraphRow *pBottom; /* Bottom row of a branch */ GraphRow *pRoot; /* Node off of which the branch diverges */ int iFrom; /* Proposed to move from this rail */ int iTo; /* Move the branch to this rail */ iFrom = pRow->iRail; if( pRow->aiRiser[iFrom]>=0 ) continue; /* Not a leaf */ if( pRow->mergeOut<0 ) continue; /* No merge riser */ if( pRow->mergeOut==iFrom ) continue; /* Riser already aligned */ iTo = pRow->mergeOut; /* Find the bottom (oldest) node in the branch */ pBottom = 0; for(pLoop=pRow; pLoop; pLoop=pLoop->pNext){ if( pLoop->idxTop==pRow->idx ) pBottom = pLoop; } if( pBottom==0 ) continue; /* Not possible */ /* Verify that the rail we want to shift into is clear */ pLoop = pBottom; if( pLoop->pNext ) pLoop = pLoop->pNext; if( !railIsClear(pLoop, pRow->idx+1, iTo) ){ /* Other nodes or risers are already using the space that ** we propose to move the pRow branch into. */ continue; } /* Find the "root" of the branch. The root is a different branch ** from which the pRow branch emerges. There might not be a root ** if the pRow branch started off the bottom of the screen. */ for(pRoot=pBottom->pNext; pRoot; pRoot=pRoot->pNext){ if( pRoot->aiRiser[iFrom]==pBottom->idx ) break; } if( pRoot && pRoot->iRail==iTo ){ /* The parent branch from which this branch emerges is on the ** same rail as pRow. Do not shift as that would stack a child ** branch directly above its parent. */ continue; } /* All clear. Make the translation */ for(pLoop=pRow; pLoop && pLoop->idx<=pBottom->idx; pLoop=pLoop->pNext){ if( pLoop->iRail==iFrom ){ pLoop->iRail = iTo; pLoop->aiRiser[iTo] = pLoop->aiRiser[iFrom]; pLoop->aiRiser[iFrom] = -1; } } if( pRoot ){ pRoot->aiRiser[iTo] = pRoot->aiRiser[iFrom]; pRoot->aiRiser[iFrom] = -1; } } } /* ** Compute the rail mapping that tries to put the branch named ** zLeftBranch at the left margin. Other branches that merge ** with zLeftBranch are to the right with merge rails in between. ** ** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail. ** ** Do not move rails around if there are timewarps, because that can ** seriously mess up the display of timewarps. Timewarps should be ** rare so this should not be a serious limitation to the algorithm. */ aMap = p->aiRailMap; for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */ if( nTimewarp==0 ){ /* Priority bits: ** ** 0x04 The preferred branch ** ** 0x02 A merge rail - a rail that contains merge lines ** ** 0x01 A rail that merges with the preferred branch */ u8 aPriority[GR_MAX_RAIL]; memset(aPriority, 0, p->mxRail+1); if( zLeftBranch ){ char *zLeft = persistBranchName(p, zLeftBranch); for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->zBranch==zLeft ){ aPriority[pRow->iRail] |= 4; for(i=0; i<=p->mxRail; i++){ if( pRow->mergeIn[i] ) aPriority[i] |= 1; } if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; } } }else{ j = 1; aPriority[0] = 4; for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->iRail==0 ){ for(i=0; i<=p->mxRail; i++){ if( pRow->mergeIn[i] ) aPriority[i] |= 1; } if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; } } } for(i=0; i<=p->mxRail; i++){ if( p->mergeRail & BIT(i) ){ aPriority[i] |= 2; } } #if 0 fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail); fprintf(stderr,"Priority:"); for(i=0; i<=p->mxRail; i++) fprintf(stderr," %d", aPriority[i]); fprintf(stderr,"\n"); #endif j = 0; for(i=0; i<=p->mxRail; i++){ if( aPriority[i]>=4 ) aMap[i] = j++; } for(i=p->mxRail; i>=0; i--){ if( aPriority[i]==3 ) aMap[i] = j++; } for(i=0; i<=p->mxRail; i++){ if( aPriority[i]==1 || aPriority[i]==2 ) aMap[i] = j++; } for(i=0; i<=p->mxRail; i++){ if( aPriority[i]==0 ) aMap[i] = j++; } cgi_printf("<!-- aiRailMap ="); for(i=0; i<=p->mxRail; i++) cgi_printf(" %d", aMap[i]); cgi_printf(" -->\n"); } p->nErr = 0; } |
Added src/graph.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* This module contains javascript needed to render timeline graphs in Fossil. ** ** There can be multiple graphs on a single webpage, but this script is only ** loaded once. ** ** Prior to sourcing this script, there should be a separate ** <script type='application/json' id='timeline-data-NN'> for each graph, ** each containing JSON like this: ** ** { "iTableId": INTEGER, // Table sequence number (NN) ** "circleNodes": BOOLEAN, // True for circle nodes. False for squares ** "showArrowheads": BOOLEAN, // True for arrowheads. False to omit ** "iRailPitch": INTEGER, // Spacing between vertical lines (px) ** "colorGraph": BOOLEAN, // True to put color on graph lines ** "nomo": BOOLEAN, // True to join merge lines with rails ** "iTopRow": INTEGER, // Index of top-most row in the graph ** "omitDescenders": BOOLEAN, // Omit ancestor lines off bottom of screen ** "fileDiff": BOOLEAN, // True for file diff. False for check-in ** "scrollToSelect": BOOLEAN, // Scroll to selection on first render ** "nrail": INTEGER, // Number of vertical "rails" ** "baseUrl": TEXT, // Top-level URL ** "dwellTimeout": INTEGER, // Tooltip show delay in milliseconds ** "closeTimeout": INTEGER, // Tooltip close delay in milliseconds ** "hashDigits": INTEGER, // Limit of tooltip hashes ("hash-digits") ** "rowinfo": ROWINFO-ARRAY } ** ** The rowinfo field is an array of structures, one per entry in the timeline, ** where each structure has the following fields: ** ** 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 iTopRow 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: If exists and true then there is a "descender" - an arrow ** coming from the bottom of the page straight up to this node. ** mo: "merge-out". If it exists, this is the rail position ** for the upward portion of a merge arrow. The merge arrow goes as ** a solid normal merge line up to the row identified by "mu" and ** then as a dashed cherrypick merge line up further to "cu". ** If this value is omitted if there are no merge children. ** mu: The id of the row which is the top of the merge-out arrow. ** Only exists if "mo" exists. ** cu: Extend the mu merge arrow up to this row as a cherrypick ** merge line, if this value exists. ** 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. If there ** are no risers, this array does not exist. ** mi: "merge-in". An array of integer rail positions from which ** merge arrows should be drawn into this node. If the value is ** negative, then the rail position is -1-mi[] and a thin merge-arrow ** descender is drawn to the bottom of the screen. This array is ** omitted if there are no inbound merges. ** ci: "cherrypick-in". Like "mi" except for cherrypick merges. ** omitted if there are no cherrypick merges. ** h: The artifact hash of the object being graphed */ /* The amendCss() function does a one-time change to the CSS to account ** for the "circleNodes" and "showArrowheads" settings. Do this change ** only once, even if there are multiple graphs being rendered. */ var amendCssOnce = 1; // Only change the CSS one time function amendCss(circleNodes,showArrowheads){ if( !amendCssOnce ) return; var css = ""; if( circleNodes ){ css += ".tl-node, .tl-node:after { border-radius: 50%; }"; } if( !showArrowheads ){ css += ".tl-arrow.u { display: none; }"; } if( css!=="" ){ var style = document.createElement("style"); style.textContent = css; document.querySelector("head").appendChild(style); } amendCssOnce = 0; } /* The <span> object that holds the tooltip */ var tooltipObj = document.createElement("span"); tooltipObj.className = "tl-tooltip"; tooltipObj.style.display = "none"; document.getElementsByClassName("content")[0].appendChild(tooltipObj); tooltipObj.onmouseenter = function(){ /* Hold the tooltip constant as long as the mouse is over the tooltip. ** In other words, do not let any of the timers changes the tooltip while ** the mouse is directly over the tooltip. This makes it easier for the ** user to move over top of the "copy-button" or the hyperlink to the ** /info page. */ stopCloseTimer(); stopDwellTimer(); tooltipInfo.ixHover = tooltipInfo.ixActive; } tooltipObj.onmouseleave = function(){ if (tooltipInfo.ixActive != -1) resumeCloseTimer(); }; /* State information for the tooltip popup and its timers */ window.tooltipInfo = { dwellTimeout: 250, /* The tooltip dwell timeout. */ closeTimeout: 3000, /* The tooltip close timeout. */ hashDigits: 16, /* Limit of tooltip hashes ("hash-digits"). */ idTimer: 0, /* The tooltip dwell timer id */ idTimerClose: 0, /* The tooltip close timer id */ ixHover: -1, /* The mouse is over a thick riser arrow for ** tx.rowinfo[ixHover]. Or -2 when the mouse is ** over a graph node. Or -1 when the mouse is not ** over anything. */ ixActive: -1, /* The item shown in the tooltip is tx.rowinfo[ixActive]. ** ixActive is -1 if the tooltip is not visible */ nodeHover: null, /* Graph node under mouse when ixHover==-2 */ idNodeActive: 0, /* Element ID of the graph node with the tooltip. */ posX: 0, posY: 0 /* The last mouse position. */ }; /* Functions used to control the tooltip popup and its timer */ function onKeyDown(event){ /* Hide the tooltip when ESC key pressed */ var key = event.which || event.keyCode; if( key==27 ){ event.stopPropagation(); hideGraphTooltip(); } } function hideGraphTooltip(){ /* Hide the tooltip */ document.removeEventListener('keydown',onKeyDown,/* useCapture == */true); stopCloseTimer(); tooltipObj.style.display = "none"; tooltipInfo.ixActive = -1; tooltipInfo.idNodeActive = 0; } window.onpagehide = hideGraphTooltip; function stopDwellTimer(){ if(tooltipInfo.idTimer!=0){ clearTimeout(tooltipInfo.idTimer); tooltipInfo.idTimer = 0; } } function resumeCloseTimer(){ /* This timer must be stopped explicitly to reset the elapsed timeout. */ if(tooltipInfo.idTimerClose==0 && tooltipInfo.closeTimeout>0) { tooltipInfo.idTimerClose = setTimeout(function(){ tooltipInfo.idTimerClose = 0; hideGraphTooltip(); },tooltipInfo.closeTimeout); } } function stopCloseTimer(){ if(tooltipInfo.idTimerClose!=0){ clearTimeout(tooltipInfo.idTimerClose); tooltipInfo.idTimerClose = 0; } } /* Construct that graph corresponding to the timeline-data-N object that ** is passed in by the tx parameter */ function TimelineGraph(tx){ var topObj = document.getElementById("timelineTable"+tx.iTableId); amendCss(tx.circleNodes, tx.showArrowheads); tooltipInfo.dwellTimeout = tx.dwellTimeout tooltipInfo.closeTimeout = tx.closeTimeout tooltipInfo.hashDigits = tx.hashDigits topObj.onclick = clickOnGraph topObj.ondblclick = dblclickOnGraph topObj.onmousemove = function(e) { var ix = findTxIndex(e); topObj.style.cursor = (ix<0) ? "" : "pointer" mouseOverGraph(e,ix,null); }; topObj.onmouseleave = function(e) { /* Hide the tooltip if the mouse is outside the "timelineTableN" element, ** and outside the tooltip. */ if(e.relatedTarget && e.relatedTarget != tooltipObj){ tooltipInfo.ixHover = -1; hideGraphTooltip(); stopDwellTimer(); stopCloseTimer(); } }; function mouseOverNode(e){ /* Invoked by mousemove events over a graph node */ e.stopPropagation() mouseOverGraph(e,-2,this) } /* Combined mousemove handler for graph nodes and rails. */ function mouseOverGraph(e,ix,node){ stopDwellTimer(); // Mouse movement: reset the dwell timer. var ownTooltip = // Check if the hovered element already has the tooltip. (ix>=0 && ix==tooltipInfo.ixActive) || (ix==-2 && tooltipInfo.idNodeActive==node.id); if(ownTooltip) stopCloseTimer(); // ownTooltip: clear the close timer. else resumeCloseTimer(); // !ownTooltip: resume the close timer. tooltipInfo.ixHover = ix; tooltipInfo.nodeHover = node; tooltipInfo.posX = e.clientX; tooltipInfo.posY = e.clientY; if(ix!=-1 && !ownTooltip && tooltipInfo.dwellTimeout>0){ // Go dwell timer. tooltipInfo.idTimer = setTimeout(function(){ tooltipInfo.idTimer = 0; stopCloseTimer(); showGraphTooltip(); },tooltipInfo.dwellTimeout); } } var canvasDiv; var railPitch; var mergeOffset; var node, arrow, arrowSmall, line, mArrow, mLine, wArrow, wLine; function initGraph(){ var parent = topObj.rows[0].cells[1]; parent.style.verticalAlign = "top"; canvasDiv = document.createElement("div"); canvasDiv.className = "tl-canvas"; canvasDiv.style.position = "absolute"; parent.appendChild(canvasDiv); var elems = {}; var elemClasses = [ "rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line", "arrow merge r", "line merge", "arrow warp", "line warp", "line cherrypick", "line dotted" ]; for( var i=0; i<elemClasses.length; i++ ){ var cls = elemClasses[i]; var elem = document.createElement("div"); elem.className = "tl-" + cls; if( cls.indexOf("line")==0 ) elem.className += " v"; canvasDiv.appendChild(elem); var k = cls.replace(/\s/g, "_"); var r = elem.getBoundingClientRect(); var w = Math.round(r.right - r.left); var h = Math.round(r.bottom - r.top); elems[k] = {w: w, h: h, cls: cls}; } node = elems.node; arrow = elems.arrow_u; arrowSmall = elems.arrow_u_sm; line = elems.line; mArrow = elems.arrow_merge_r; mLine = elems.line_merge; cpLine = elems.line_cherrypick; wArrow = elems.arrow_warp; wLine = elems.line_warp; dotLine = elems.line_dotted; var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1); if( window.innerWidth<400 ){ railPitch = minRailPitch; }else{ if( tx.iRailPitch>0 ){ railPitch = tx.iRailPitch; }else{ railPitch = elems.rail.w; railPitch -= Math.floor((tx.nrail-1)*(railPitch-minRailPitch)/21); } railPitch = Math.max(railPitch, minRailPitch); } if( tx.nomo ){ mergeOffset = 0; }else{ mergeOffset = railPitch-minRailPitch-mLine.w; mergeOffset = Math.min(mergeOffset, elems.mergeoffset.w); mergeOffset = mergeOffset>0 ? mergeOffset + line.w/2 : 0; } var canvasWidth = (tx.nrail-1)*railPitch + node.w; canvasDiv.style.width = canvasWidth + "px"; canvasDiv.style.position = "relative"; } function drawBox(cls,color,x0,y0,x1,y1){ var n = document.createElement("div"); x0 = Math.floor(x0); y0 = Math.floor(y0); x1 = x1 || x1===0 ? Math.floor(x1) : x0; y1 = y1 || y1===0 ? Math.floor(y1) : y0; if( x0>x1 ){ var t=x0; x0=x1; x1=t; } if( y0>y1 ){ var t=y0; y0=y1; y1=t; } var w = x1-x0; var h = y1-y0; n.style.position = "absolute"; n.style.left = x0+"px"; n.style.top = y0+"px"; if( w ) n.style.width = w+"px"; if( h ) n.style.height = h+"px"; if( color ) n.style.backgroundColor = color; n.className = "tl-"+cls; canvasDiv.appendChild(n); return n; } function absoluteY(obj){ var y = 0; do{ y += obj.offsetTop; }while( obj = obj.offsetParent ); return y; } function absoluteX(obj){ var x = 0; do{ x += obj.offsetLeft; }while( obj = obj.offsetParent ); return x; } function miLineY(p){ return p.y + node.h - mLine.w - 1; } function drawLine(elem,color,x0,y0,x1,y1){ var cls = elem.cls + " "; if( x1===null ){ x1 = x0+elem.w; cls += "v"; }else{ y1 = y0+elem.w; cls += "h"; } return drawBox(cls,color,x0,y0,x1,y1); } function drawUpArrow(from,to,color,id){ var y = to.y + node.h; var arrowSpace = from.y - y + (!from.id || from.r!=to.r ? node.h/2 : 0); var arw = arrowSpace < arrow.h*1.5 ? arrowSmall : arrow; var x = to.x + (node.w-line.w)/2; var y0 = from.y + node.h/2; var y1 = Math.ceil(to.y + node.h + arw.h/2); var n = drawLine(line,color,x,y0,null,y1); addToolTip(n,id) x = to.x + (node.w-arw.w)/2; n = drawBox(arw.cls,null,x,y); if(color) n.style.borderBottomColor = color; addToolTip(n,id) } function drawDotted(from,to,color,id){ var x = to.x + (node.w-line.w)/2; var y0 = from.y + node.h/2; var y1 = Math.ceil(to.y + node.h); var n = drawLine(dotLine,null,x,y0,null,y1) if( color ) n.style.borderColor = color addToolTip(n,id) } function addToolTip(n,id){ if( id ) n.setAttribute("data-ix",id-tx.iTopRow) } /* Draw thin horizontal or vertical lines representing merges */ function drawMergeLine(x0,y0,x1,y1){ drawLine(mLine,null,x0,y0,x1,y1); } function drawCherrypickLine(x0,y0,x1,y1){ drawLine(cpLine,null,x0,y0,x1,y1); } /* Draw an arrow representing an in-bound merge from the "rail"-th rail ** over to the node of "p". Make it a checkpoint merge is "isCP" is true */ function drawMergeArrow(p,rail,isCP){ var x0 = rail*railPitch + node.w/2; if( rail in mergeLines ){ x0 += mergeLines[rail]; if( p.r<rail ) x0 += mLine.w; }else{ x0 += (p.r<rail ? -1 : 1)*line.w/2; } var x1 = mArrow.w ? mArrow.w/2 : -node.w/2; x1 = p.x + (p.r<rail ? node.w + Math.ceil(x1) : -x1); var y = miLineY(p); var x = p.x + (p.r<rail ? node.w : -mArrow.w); var cls; if( isCP ){ drawCherrypickLine(x0,y,x1,null); cls = "arrow cherrypick " + (p.r<rail ? "l" : "r"); }else{ drawMergeLine(x0,y,x1,null); cls = "arrow merge " + (p.r<rail ? "l" : "r"); } drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2); } function drawNode(p, btm){ if( p.bg ){ var e = document.getElementById("mc"+p.id); if(e) e.style.backgroundColor = p.bg; e = document.getElementById("md"+p.id); if(e) e.style.backgroundColor = p.bg; } if( p.r<0 ) return; if( p.u>0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg,p.id); if( p.sb>0 ) drawDotted(p,tx.rowinfo[p.sb-tx.iTopRow],p.fg,p.id); var cls = node.cls; if( p.hasOwnProperty('mi') && p.mi.length ) cls += " merge"; if( p.f&2 ) cls += " closed-leaf"; else if( p.f&1 ) cls += " leaf"; var n = drawBox(cls,p.bg,p.x,p.y); n.id = "tln"+p.id; n.onclick = clickOnNode; n.ondblclick = dblclickOnNode; n.onmousemove = mouseOverNode; n.style.zIndex = 10; if( p.f&2 ){ var pt1 = 0; var pt2 = 100; if( tx.circleNodes ){ pt1 = 14; pt2 = 86; } n.innerHTML = "<svg width='100%' height='100%'viewbox='0 0 100 100'>" + `<path d='M ${pt1},${pt1} L ${pt2},${pt2} M ${pt1},${pt2} L ${pt2},${pt1}'` + " stroke='currentcolor' stroke-width='13'/>" + "</svg>"; } if( !tx.omitDescenders ){ if( p.u==0 ){ if( p.hasOwnProperty('mo') && p.r==p.mo ){ var ix = p.hasOwnProperty('cu') ? p.cu : p.mu; var top = tx.rowinfo[ix-tx.iTopRow] drawUpArrow(p,{x: p.x, y: top.y-node.h}, p.fg, p.id); }else if( p.y>100 ){ drawUpArrow(p,{x: p.x, y: p.y-50}, p.fg, p.id); }else{ drawUpArrow(p,{x: p.x, y: 0},p.fg, p.id); } } if( p.hasOwnProperty('d') ){ if( p.y + 150 >= btm ){ drawUpArrow({x: p.x, y: btm - node.h/2},p,p.fg,p.id); }else{ drawUpArrow({x: p.x, y: p.y+50},p,p.fg,p.id); drawDotted({x: p.x, y: p.y+63},{x: p.x, y: p.y+50-node.h/2},p.fg,p.id); } } } if( p.hasOwnProperty('mo') ){ var x0 = p.x + node.w/2; var x1 = p.mo*railPitch + node.w/2; var u = tx.rowinfo[p.mu-tx.iTopRow]; var mtop = u; if( p.hasOwnProperty('cu') ){ mtop = tx.rowinfo[p.cu-tx.iTopRow]; } var y1 = miLineY(u); if( p.u<=0 || p.mo!=p.r ){ if( p.u==0 && p.mo==p.r ){ mergeLines[p.mo] = mtop.r<p.r ? -mergeOffset-mLine.w : mergeOffset; }else{ mergeLines[p.mo] = -mLine.w/2; } x1 += mergeLines[p.mo] var y0 = p.y+2; var isCP = p.hasOwnProperty('cu'); if( p.mu==p.id ){ /* Special case: The merge riser already exists. Only draw the /* horizontal line or arrow going from the node out to the riser. */ var dx = x1<x0 ? mArrow.w : -mArrow.w; if( isCP ){ drawCherrypickLine(x0,y0,x1+dx,null); cls = "arrow cherrypick " + (x1<x0 ? "l" : "r"); }else{ drawMergeLine(x0,y0,x1+dx,null); cls = "arrow merge " + (x1<x0 ? "l" : "r"); } if( !isCP || p.mu==p.cu ){ dx = x1<x0 ? mLine.w : -(mArrow.w + mLine.w/2); drawBox(cls,null,x1+dx,y0+(mLine.w-mArrow.h)/2); } y1 = y0; }else{ drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null); drawMergeLine(x1,y0+mLine.w,null,y1); } if( isCP && p.cu!=p.id ){ var u2 = tx.rowinfo[p.cu-tx.iTopRow]; var y2 = miLineY(u2); drawCherrypickLine(x1,y1,null,y2); } }else if( mergeOffset ){ mergeLines[p.mo] = mtop.r<p.r ? -mergeOffset-mLine.w : mergeOffset; x1 += mergeLines[p.mo]; if( p.mu<p.id ){ drawMergeLine(x1,p.y+node.h/2,null,y1); } if( p.hasOwnProperty('cu') ){ var u2 = tx.rowinfo[p.cu-tx.iTopRow]; var y2 = miLineY(u2); drawCherrypickLine(x1,y1,null,y2); } }else{ delete mergeLines[p.mo]; } } if( p.hasOwnProperty('au') ){ for( var i=0; i<p.au.length; i+=2 ){ var rail = p.au[i]; var x0 = p.x + node.w/2; var x1 = rail*railPitch + (node.w-line.w)/2; if( x0<x1 ){ x0 = Math.ceil(x0); x1 += line.w; } var y0 = p.y + (node.h-line.w)/2; var u = tx.rowinfo[p.au[i+1]-tx.iTopRow]; if( u.id<p.id ){ // normal thick up-arrow drawLine(line,u.fg,x0,y0,x1,null); drawUpArrow(p,u,u.fg,u.id); }else{ // timewarp: The child node occurs before the parent var y1 = u.y + (node.h-line.w)/2; var n = drawLine(wLine,u.fg,x0,y0,x1,null); addToolTip(n,u.id) n = drawLine(wLine,u.fg,x1-line.w,y0,null,y1+line.w); addToolTip(n,u.id) n = drawLine(wLine,u.fg,x1,y1,u.x-wArrow.w/2,null); addToolTip(n,u.id) var x = u.x-wArrow.w; var y = u.y+(node.h-wArrow.h)/2; n = drawBox(wArrow.cls,null,x,y); addToolTip(n,u.id) if( u.fg ) n.style.borderLeftColor = u.fg; } } } if( p.hasOwnProperty('mi') ){ for( var i=0; i<p.mi.length; i++ ){ var rail = p.mi[i]; if( rail<0 ){ rail = -1-rail; mergeLines[rail] = -mLine.w/2; var x = rail*railPitch + (node.w-mLine.w)/2; var y = miLineY(p); drawMergeLine(x,y,null,mergeBtm[rail]); mergeBtm[rail] = y; } drawMergeArrow(p,rail,0); } } if( p.hasOwnProperty('ci') ){ for( var i=0; i<p.ci.length; i++ ){ var rail = p.ci[i]; if( rail<0 ){ rail = -rail; mergeLines[rail] = -mLine.w/2; var x = rail*railPitch + (node.w-mLine.w)/2; var y = miLineY(p); drawCherrypickLine(x,y,null,mergeBtm[rail]); mergeBtm[rail] = y; } drawMergeArrow(p,rail,1); } } } var mergeLines; var mergeBtm = new Array; function renderGraph(){ mergeLines = {}; canvasDiv.innerHTML = ""; var canvasY = absoluteY(canvasDiv); for(var i=0; i<tx.rowinfo.length; i++ ){ var e = document.getElementById("m"+tx.rowinfo[i].id); tx.rowinfo[i].y = absoluteY(e) - canvasY; tx.rowinfo[i].x = tx.rowinfo[i].r*railPitch; } var tlBtm = document.getElementById(tx.bottomRowId); if( tlBtm.offsetHeight<node.h ){ tlBtm.style.height = node.h + "px"; } var btm = absoluteY(tlBtm) - canvasY + tlBtm.offsetHeight; for( var i=0; i<tx.nrail; i++) mergeBtm[i] = btm; for( var i=tx.rowinfo.length-1; i>=0; i-- ){ drawNode(tx.rowinfo[i], btm); } } var selRow; function clickOnNode(e){ hideGraphTooltip() var p = tx.rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-tx.iTopRow]; if( !selRow ){ selRow = p; this.className += " sel"; canvasDiv.className += " sel"; }else if( selRow==p ){ selRow = null; this.className = this.className.replace(" sel", ""); canvasDiv.className = canvasDiv.className.replace(" sel", ""); }else{ if( tx.fileDiff ){ location.href=tx.baseUrl + "/fdiff?v1="+selRow.h+"&v2="+p.h; }else{ var href = tx.baseUrl + "/vdiff?from="+selRow.h+"&to="+p.h; let params = (new URL(document.location)).searchParams; if(params && typeof params === "object"){ /* When called from /timeline page, If chng=str was specified in the ** QueryString, specify glob=str on the /vdiff page */ let glob = params.get("chng"); if( !glob ){ /* When called from /vdiff page, keep the glob= QueryString if ** present. */ glob = params.get("glob"); } if( glob ){ href += "&glob=" + glob; } } location.href = href; } } e.stopPropagation() } function dblclickOnNode(e){ var p = tx.rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-tx.iTopRow]; window.location.href = tx.baseUrl+"/info/"+p.h e.stopPropagation() } function findTxIndex(e){ if( !tx.rowinfo ) return -1; /* Look at all the graph elements. If any graph elements that is near ** the click-point "e" and has a "data-ix" attribute, then return ** the value of that attribute. Otherwise return -1 */ var x = e.clientX + window.pageXOffset - absoluteX(canvasDiv); var y = e.clientY + window.pageYOffset - absoluteY(canvasDiv); var aNode = canvasDiv.childNodes var nNode = aNode.length; var i; for(i=0;i<nNode;i++){ var n = aNode[i] if( !n.hasAttribute("data-ix") ) continue; if( x<n.offsetLeft-5 ) continue; if( x>n.offsetLeft+n.offsetWidth+5 ) continue; if( y<n.offsetTop-5 ) continue; if( y>n.offsetTop+n.offsetHeight ) continue; return n.getAttribute("data-ix") } return -1 } /* Compute the hyperlink for the branch graph for tx.rowinfo[ix] */ function branchHyperlink(ix){ var br = tx.rowinfo[ix].br var dest = tx.baseUrl + "/timeline?r=" + encodeURIComponent(br) dest += tx.fileDiff ? "&m&cf=" : "&m&c=" dest += encodeURIComponent(tx.rowinfo[ix].h) return dest } function clickOnGraph(e){ stopCloseTimer(); stopDwellTimer(); tooltipInfo.ixHover = findTxIndex(e); tooltipInfo.posX = e.clientX; tooltipInfo.posY = e.clientY; showGraphTooltip(); } function showGraphTooltip(){ var html = null var ix = -1 if( tooltipInfo.ixHover==-2 ){ ix = parseInt(tooltipInfo.nodeHover.id.match(/\d+$/)[0],10)-tx.iTopRow var h = tx.rowinfo[ix].h var dest = tx.baseUrl + "/info/" + h h = h.slice(0,tooltipInfo.hashDigits); // Assume single-byte characters. if( tx.fileDiff ){ html = "artifact <a id=\"tooltip-link\" href=\""+dest+"\">"+h+"</a>" }else{ html = "check-in <a id=\"tooltip-link\" href=\""+dest+"\">"+h+"</a>" } tooltipInfo.ixActive = -2; tooltipInfo.idNodeActive = tooltipInfo.nodeHover.id; }else if( tooltipInfo.ixHover>=0 ){ ix = tooltipInfo.ixHover var br = tx.rowinfo[ix].br var dest = branchHyperlink(ix) var hbr = br.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); html = "branch <a id=\"tooltip-link\" href=\""+dest+"\">"+hbr+"</a>" tooltipInfo.ixActive = ix; tooltipInfo.idNodeActive = 0; } if( html ){ /* Setup while hidden, to ensure proper dimensions. */ var s = getComputedStyle(document.body) if( tx.rowinfo[ix].bg.length ){ tooltipObj.style.backgroundColor = tx.rowinfo[ix].bg }else{ tooltipObj.style.backgroundColor = s.getPropertyValue('background-color') } tooltipObj.style.borderColor = tooltipObj.style.color = s.getPropertyValue('color') tooltipObj.style.visibility = "hidden" tooltipObj.innerHTML = html tooltipObj.insertBefore(makeCopyButton("tooltip-link",0,0), tooltipObj.childNodes[1]); tooltipObj.style.display = "inline" tooltipObj.style.position = "absolute" var x = tooltipInfo.posX + 4 + window.pageXOffset - absoluteX(tooltipObj.offsetParent) tooltipObj.style.left = x+"px" var y = tooltipInfo.posY + window.pageYOffset - tooltipObj.clientHeight - 4 - absoluteY(tooltipObj.offsetParent) tooltipObj.style.top = y+"px" tooltipObj.style.visibility = "visible" document.addEventListener('keydown',onKeyDown,/* useCapture == */true); }else{ hideGraphTooltip() } } function dblclickOnGraph(e){ var ix = findTxIndex(e); hideGraphTooltip() if( ix>=0 ){ var dest = branchHyperlink(ix) window.location.href = dest } } function changeDisplay(selector,value){ var x = document.getElementsByClassName(selector); var n = x.length; for(var i=0; i<n; i++) {x[i].style.display = value;} } function changeDisplayById(id,value){ var x = document.getElementById(id); if(x) x.style.display=value; } function toggleDetail(){ var id = parseInt(this.getAttribute('data-id')) var x = document.getElementById("detail-"+id); if( x.style.display=="inline" ){ x.style.display="none"; changeDisplayById("ellipsis-"+id,"inline"); changeDisplayById("links-"+id,"none"); }else{ x.style.display="inline"; changeDisplayById("ellipsis-"+id,"none"); changeDisplayById("links-"+id,"inline"); } checkHeight(); } function scrollToSelected(){ var x = document.getElementsByClassName('timelineSelected'); if(x[0]){ var h = window.innerHeight; var y = absoluteY(x[0]) - h/2; if( y>0 ) window.scrollTo(0, y); } } if( tx.rowinfo ){ var lastRow = document.getElementById("m"+tx.rowinfo[tx.rowinfo.length-1].id); var lastY = 0; function checkHeight(){ var h = absoluteY(lastRow); if( h!=lastY ){ renderGraph(); lastY = h; } setTimeout(checkHeight, 1000); } initGraph(); checkHeight(); }else{ function checkHeight(){} } if( tx.scrollToSelect ){ scrollToSelected(); } /* Set the onclick= attributes for elements of the "Compact" display ** mode so that clicking turns the details on and off. */ var lx = topObj.getElementsByClassName('timelineEllipsis'); var i; for(i=0; i<lx.length; i++){ if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail; } lx = topObj.getElementsByClassName('timelineCompactComment'); for(i=0; i<lx.length; i++){ if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail; } if( window.innerWidth<400 ){ /* On narrow displays, shift the date from the first column to the ** third column, to make the first column narrower */ lx = topObj.getElementsByClassName('timelineDateRow'); for(i=0; i<lx.length; i++){ var rx = lx[i]; if( rx.getAttribute('data-reordered') ) break; rx.setAttribute('data-reordered',1); rx.appendChild(rx.firstChild); rx.insertBefore(rx.childNodes[1],rx.firstChild); } /* Do not show the HH:MM timestamps on very narrow displays ** as they take up too much horizontal space. */ lx = topObj.getElementsByClassName('timelineHistLink'); for(i=0; i<lx.length; i++){ var rx = lx[i]; rx.style.display="none"; } } } /* Look for all timeline-data-NN objects. Load each one and draw ** a graph for each one. */ (function(){ var i; for(i=0; 1; i++){ var dataObj = document.getElementById("timeline-data-"+i); if(!dataObj) break; var txJson = dataObj.textContent || dataObj.innerText; var tx = JSON.parse(txJson); TimelineGraph(tx); } }()); |
Added src/gzip.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 | /* ** 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/ ** ******************************************************************************* ** ** 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> #include <zlib.h> #include "gzip.h" /* ** State information for the GZIP file under construction. */ struct gzip_state { int eState; /* 0: idle 1: header 2: compressing */ unsigned long iCRC; /* The checksum */ z_stream stream; /* The working compressor */ Blob out; /* Results stored here */ } gzip; /* ** Write a 32-bit integer as little-endian into the given buffer. */ static void put32(char *z, int v){ z[0] = v & 0xff; z[1] = (v>>8) & 0xff; z[2] = (v>>16) & 0xff; z[3] = (v>>24) & 0xff; } /* ** Begin constructing a gzip file. */ void gzip_begin(sqlite3_int64 now){ 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] = -1; 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; if( gzip.eState==1 ){ gzip.stream.zalloc = (alloc_func)0; gzip.stream.zfree = (free_func)0; gzip.stream.opaque = 0; deflateInit2(&gzip.stream, 9, Z_DEFLATED, -MAX_WBITS,8,Z_DEFAULT_STRATEGY); gzip.eState = 2; } gzip.iCRC = crc32(gzip.iCRC, gzip.stream.next_in, gzip.stream.avail_in); do{ deflate(&gzip.stream, nIn==0 ? Z_FINISH : 0); blob_append(&gzip.out, zOutBuf, nOut - gzip.stream.avail_out); gzip.stream.avail_out = nOut; gzip.stream.next_out = (unsigned char*)zOutBuf; }while( gzip.stream.avail_in>0 ); fossil_free(zOutBuf); } /* ** Finish the gzip file and put the content in *pOut */ void gzip_finish(Blob *pOut){ char aTrailer[8]; assert( gzip.eState>0 ); gzip_step("", 0); deflateEnd(&gzip.stream); put32(aTrailer, gzip.iCRC); put32(&aTrailer[4], gzip.stream.total_in); blob_append(&gzip.out, aTrailer, 8); *pOut = gzip.out; blob_zero(&gzip.out); gzip.eState = 0; } /* ** COMMAND: test-gzip ** ** Usage: %fossil test-gzip FILENAME ** ** 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], ExtFILE); 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); } |
Added src/hbmenu.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Originally: Copyright © 2018 Warren Young ** ** 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. ** ** Contact: wyoung on the Fossil forum, https://fossil-scm.org/forum/ ** Modified by others. ** ******************************************************************************* ** ** This file contains the JS code used to implement the expanding hamburger ** menu on various skins. ** ** This was original the "js.txt" file for the default skin. It was subsequently ** moved into src/hbmenu.js so that it could be more easily reused by other skins ** using the "builtin_request_js" TH1 command. ** ** Operation: ** ** This script expects the HTML to contain two elements: ** ** <a id="hbbtn"> <--- The hamburger menu button ** <nav id="hbdrop"> <--- Container for the hamburger menu ** ** Bindings are made on hbbtn so that when it is clicked, the following ** happens: ** ** 1. An XHR is made to /sitemap?popup to fetch the HTML for the ** popup menu. ** ** 2. The HTML for the popup is inserted into hddrop. ** ** 3. The hddrop container is made visible. ** ** CSS rules are also needed to cause the hddrop to be initially invisible, ** and to correctly style and position the hddrop container. */ (function() { var hbButton = document.getElementById("hbbtn"); if (!hbButton) return; // no hamburger button if (!document.addEventListener) return; // Incompatible browser var panel = document.getElementById("hbdrop"); if (!panel) return; // site admin might've nuked it if (!panel.style) return; // shouldn't happen, but be sure var panelBorder = panel.style.border; var panelInitialized = false; // reset if browser window is resized var panelResetBorderTimerID = 0; // used to cancel post-animation tasks // Disable animation if this browser doesn't support CSS transitions. // // We need this ugly calling form for old browsers that don't allow // panel.style.hasOwnProperty('transition'); catering to old browsers // is the whole point here. var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string"); // The duration of the animation can be overridden from the default skin // header.txt by setting the "data-anim-ms" attribute of the panel. var animMS = panel.getAttribute("data-anim-ms"); if (animMS) { // not null or empty string, parse it animMS = parseInt(animMS); if (isNaN(animMS) || animMS == 0) animate = false; // disable animation if non-numeric or zero else if (animMS < 0) animMS = 400; // set default animation duration if negative } else // attribute is null or empty string, use default animMS = 400; // Calculate panel height despite its being hidden at call time. // Based on https://stackoverflow.com/a/29047447/142454 var panelHeight; // computed on first panel display function calculatePanelHeight() { // Clear the max-height CSS property in case the panel size is recalculated // after the browser window was resized. panel.style.maxHeight = ''; // Get initial panel styles so we can restore them below. var es = window.getComputedStyle(panel), edis = es.display, epos = es.position, evis = es.visibility; // Restyle the panel so we can measure its height while invisible. panel.style.visibility = 'hidden'; panel.style.position = 'absolute'; panel.style.display = 'block'; panelHeight = panel.offsetHeight + 'px'; // Revert styles now that job is done. panel.style.display = edis; panel.style.position = epos; panel.style.visibility = evis; } // Show the panel by changing the panel height, which kicks off the // slide-open/closed transition set up in the XHR onload handler. // // Schedule the change for a near-future time in case this is the // first call, where the div was initially invisible. If we were // to change the panel's visibility and height at the same time // instead, that would prevent the browser from seeing the height // change as a state transition, so it'd skip the CSS transition: // // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples function showPanel() { // Cancel the timer to remove the panel border after the closing animation, // otherwise double-clicking the hamburger button with the panel opened will // remove the borders from the (closed and immediately reopened) panel. if (panelResetBorderTimerID) { clearTimeout(panelResetBorderTimerID); panelResetBorderTimerID = 0; } if (animate) { if (!panelInitialized) { panelInitialized = true; // Set up a CSS transition to animate the panel open and // closed. Only needs to be done once per page load. // Based on https://stackoverflow.com/a/29047447/142454 calculatePanelHeight(); panel.style.transition = 'max-height ' + animMS + 'ms ease-in-out'; panel.style.overflowY = 'hidden'; panel.style.maxHeight = '0'; } setTimeout(function() { panel.style.maxHeight = panelHeight; panel.style.border = panelBorder; }, 40); // 25ms is insufficient with Firefox 62 } panel.style.display = 'block'; document.addEventListener('keydown',panelKeydown,/* useCapture == */true); document.addEventListener('click',panelClick,false); } var panelKeydown = function(event) { var key = event.which || event.keyCode; if (key == 27) { event.stopPropagation(); // ignore other keydown handlers panelToggle(true); } }; var panelClick = function(event) { if (!panel.contains(event.target)) { // Call event.preventDefault() to have clicks outside the opened panel // just close the panel, and swallow clicks on links or form elements. //event.preventDefault(); panelToggle(true); } }; // Return true if the panel is showing. function panelShowing() { if (animate) { return panel.style.maxHeight == panelHeight; } else { return panel.style.display == 'block'; } } // Check if the specified HTML element has any child elements. Note that plain // text nodes, comments, and any spaces (presentational or not) are ignored. function hasChildren(element) { var childElement = element.firstChild; while (childElement) { if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1 return true; childElement = childElement.nextSibling; } return false; } // Reset the state of the panel to uninitialized if the browser window is // resized, so the dimensions are recalculated the next time it's opened. window.addEventListener('resize',function(event) { panelInitialized = false; },false); // Click handler for the hamburger button. hbButton.addEventListener('click',function(event) { // Break the event handler chain, or the handler for document → click // (about to be installed) may already be triggered by the current event. event.stopPropagation(); event.preventDefault(); // prevent browser from acting on <a> click panelToggle(false); },false); function panelToggle(suppressAnimation) { if (panelShowing()) { document.removeEventListener('keydown',panelKeydown,/* useCapture == */true); document.removeEventListener('click',panelClick,false); // Transition back to hidden state. if (animate) { if (suppressAnimation) { var transition = panel.style.transition; panel.style.transition = ''; panel.style.maxHeight = '0'; panel.style.border = 'none'; setTimeout(function() { // Make sure CSS transition won't take effect now, so restore it // asynchronously. Outer variable 'transition' still valid here. panel.style.transition = transition; }, 40); // 25ms is insufficient with Firefox 62 } else { panel.style.maxHeight = '0'; panelResetBorderTimerID = setTimeout(function() { // Browsers show a 1px high border line when maxHeight == 0, // our "hidden" state, so hide the borders in that state, too. panel.style.border = 'none'; panelResetBorderTimerID = 0; // clear ID of completed timer }, animMS); } } else { panel.style.display = 'none'; } } else { if (!hasChildren(panel)) { // Only get the sitemap once per page load: it isn't likely to // change on us. var xhr = new XMLHttpRequest(); xhr.onload = function() { var doc = xhr.responseXML; if (doc) { var sm = doc.querySelector("ul#sitemap"); if (sm && xhr.status == 200) { // Got sitemap. Insert it into the drop-down panel. panel.innerHTML = sm.outerHTML; // Display the panel showPanel(); } } // else, can't parse response as HTML or XML } // The extra "popup" query parameter is a single to the server that the // header and footer boiler-plate can be omitted. The boiler-plate is // ignored if it is included. The popup query parameter is just an // optimization. var url = hbButton.href + (hbButton.href.includes("?")?"&popup":"?popup") xhr.open("GET", url); xhr.responseType = "document"; xhr.send(); } else { showPanel(); // just show what we built above } } } })(); |
Added src/hname.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 | /* ** Copyright (c) 2017 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 generic code for dealing with hashes used for ** naming artifacts. Specific hash algorithms are implemented separately ** (for example in sha1.c and sha3.c). This file contains the generic ** interface logic. ** ** "hname" is intended to be an abbreviation of "hash name". */ #include "config.h" #include "hname.h" #if INTERFACE /* ** Code numbers for the allowed hash algorithms. */ #define HNAME_ERROR 0 /* Not a valid hash */ #define HNAME_SHA1 1 /* SHA1 */ #define HNAME_K256 2 /* SHA3-256 */ /* ** Minimum and maximum lengths for a hash value when hex encoded. */ #define HNAME_MIN 40 /* Length for SHA1 */ #define HNAME_MAX 64 /* Length for SHA3-256 */ /* ** Hash lengths for the various algorithms */ #define HNAME_LEN_SHA1 40 #define HNAME_LEN_K256 64 /* ** The number of distinct hash algorithms: */ #define HNAME_COUNT 2 /* Just SHA1 and SHA3-256. Let's keep it that way! */ /* ** Hash naming policies */ #define HPOLICY_SHA1 0 /* Use SHA1 hashes */ #define HPOLICY_AUTO 1 /* SHA1 but auto-promote to SHA3 */ #define HPOLICY_SHA3 2 /* Use SHA3 hashes */ #define HPOLICY_SHA3_ONLY 3 /* Use SHA3 hashes exclusively */ #define HPOLICY_SHUN_SHA1 4 /* Shun all SHA1 objects */ #endif /* INTERFACE */ /* ** Return a human-readable name for the hash algorithm given a hash with ** a length of nHash hexadecimal digits. */ const char *hname_alg(int nHash){ if( nHash==HNAME_LEN_SHA1 ) return "SHA1"; if( nHash==HNAME_LEN_K256 ) return "SHA3-256"; return "?"; } /* ** Return the integer hash algorithm code number (ex: HNAME_K256) for ** the hash string provided. Or return HNAME_ERROR (0) if the input string ** is not a valid artifact hash string. */ int hname_validate(const char *zHash, int nHash){ int id; switch( nHash ){ case HNAME_LEN_SHA1: id = HNAME_SHA1; break; case HNAME_LEN_K256: id = HNAME_K256; break; default: return HNAME_ERROR; } if( !validate16(zHash, nHash) ) return HNAME_ERROR; return id; } /* ** Verify that zHash is a valid hash for the content in pContent. ** Return true if the hash is correct. Return false if the content ** does not match the hash. ** ** Actually, the returned value is one of the hash algorithm constants ** corresponding to the hash that matched if the hash is correct. ** (Examples: HNAME_SHA1 or HNAME_K256). And the return is HNAME_ERROR ** if the hash does not match. */ int hname_verify_hash(Blob *pContent, const char *zHash, int nHash){ int id = HNAME_ERROR; switch( nHash ){ case HNAME_LEN_SHA1: { Blob hash; sha1sum_blob(pContent, &hash); if( memcmp(blob_buffer(&hash),zHash,HNAME_LEN_SHA1)==0 ) id = HNAME_SHA1; blob_reset(&hash); break; } case HNAME_LEN_K256: { sha3sum_init(256); sha3sum_step_blob(pContent); if( memcmp(sha3sum_finish(0),zHash,64)==0 ) id = HNAME_K256; break; } } return id; } /* ** Verify that zHash is a valid hash for the content of a file on ** disk named zFile. ** ** Return true if the hash is correct. Return false if the content ** does not match the hash. ** ** Actually, the returned value is one of the hash algorithm constants ** corresponding to the hash that matched if the hash is correct. ** (Examples: HNAME_SHA1 or HNAME_K256). And the return is HNAME_ERROR ** if the hash does not match. */ int hname_verify_file_hash(const char *zFile, const char *zHash, int nHash){ int id = HNAME_ERROR; switch( nHash ){ case HNAME_LEN_SHA1: { Blob hash; if( sha1sum_file(zFile, RepoFILE, &hash) ) break; if( memcmp(blob_buffer(&hash),zHash,HNAME_LEN_SHA1)==0 ) id = HNAME_SHA1; blob_reset(&hash); break; } case HNAME_LEN_K256: { Blob hash; if( sha3sum_file(zFile, RepoFILE, 256, &hash) ) break; if( memcmp(blob_buffer(&hash),zHash,64)==0 ) id = HNAME_LEN_K256; blob_reset(&hash); break; } } return id; } /* ** Compute a hash on blob pContent. Write the hash into blob pHashOut. ** This routine assumes that pHashOut is uninitialized. ** ** The preferred hash is used for iHType==0 and the alternative hash is ** used if iHType==1. (The interface is designed to accommodate more than ** just two hashes, but HNAME_COUNT is currently fixed at 2.) ** ** Depending on the hash policy, the alternative hash may be disallowed. ** If the alterative hash is disallowed, the routine returns 0. This ** routine returns 1 if iHType>0 and the alternative hash is allowed, ** and it always returns 1 when iHType==0. ** ** Alternative hash is disallowed for all hash policies except auto, ** sha1 and sha3. */ int hname_hash(const Blob *pContent, unsigned int iHType, Blob *pHashOut){ assert( iHType==0 || iHType==1 ); if( iHType==1 ){ switch( g.eHashPolicy ){ case HPOLICY_AUTO: case HPOLICY_SHA1: sha3sum_blob(pContent, 256, pHashOut); return 1; case HPOLICY_SHA3: sha1sum_blob(pContent, pHashOut); return 1; } } if( iHType==0 ){ switch( g.eHashPolicy ){ case HPOLICY_SHA1: case HPOLICY_AUTO: sha1sum_blob(pContent, pHashOut); return 1; case HPOLICY_SHA3: case HPOLICY_SHA3_ONLY: case HPOLICY_SHUN_SHA1: sha3sum_blob(pContent, 256, pHashOut); return 1; } } blob_init(pHashOut, 0, 0); return 0; } /* ** Return the default hash policy for repositories that do not currently ** have an assigned hash policy. ** ** Make the default HPOLICY_AUTO if there are SHA1 artficates but no SHA3 ** artifacts in the repository. Make the default HPOLICY_SHA3 if there ** are one or more SHA3 artifacts or if the repository is initially empty. */ int hname_default_policy(void){ if( db_exists("SELECT 1 FROM blob WHERE length(uuid)>40") || !db_exists("SELECT 1 FROM blob WHERE length(uuid)==40") ){ return HPOLICY_SHA3; }else{ return HPOLICY_AUTO; } } /* ** Names of the hash policies. */ static const char *const azPolicy[] = { "sha1", "auto", "sha3", "sha3-only", "shun-sha1" }; /* Return the name of the current hash policy. */ const char *hpolicy_name(void){ return azPolicy[g.eHashPolicy]; } /* ** COMMAND: hash-policy* ** ** Usage: fossil hash-policy ?NEW-POLICY? ** ** Query or set the hash policy for the current repository. Available hash ** policies are as follows: ** ** sha1 New artifact names are created using SHA1 ** ** auto New artifact names are created using SHA1, but ** automatically change the policy to "sha3" when ** any SHA3 artifact enters the repository. ** ** sha3 New artifact names are created using SHA3, but ** older artifacts with SHA1 names may be reused. ** ** sha3-only Use only SHA3 artifact names. Do not reuse legacy ** SHA1 names. ** ** shun-sha1 Shun any SHA1 artifacts received by sync operations ** other than clones. Older legacy SHA1 artifacts are ** allowed during a clone. ** ** The default hash policy for existing repositories is "auto", which will ** immediately promote to "sha3" if the repository contains one or more ** artifacts with SHA3 names. The default hash policy for new repositories ** is "shun-sha1". */ void hash_policy_command(void){ int i; db_find_and_open_repository(0, 0); if( g.argc!=2 && g.argc!=3 ) usage("?NEW-POLICY?"); if( g.argc==2 ){ fossil_print("%s\n", azPolicy[g.eHashPolicy]); return; } for(i=HPOLICY_SHA1; i<=HPOLICY_SHUN_SHA1; i++){ if( fossil_strcmp(g.argv[2],azPolicy[i])==0 ){ if( i==HPOLICY_AUTO && db_exists("SELECT 1 FROM blob WHERE length(uuid)>40") ){ i = HPOLICY_SHA3; } g.eHashPolicy = i; db_set_int("hash-policy", i, 0); fossil_print("%s\n", azPolicy[i]); return; } } fossil_fatal("unknown hash policy \"%s\" - should be one of: sha1 auto" " sha3 sha3-only shun-sha1", g.argv[2]); } |
Added src/hook.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 | /* ** Copyright (c) 2020 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 "hooks" - external programs that can be run ** when various events occur on a Fossil repository. ** ** Hooks are stored in the following CONFIG variables: ** ** hooks A JSON-array of JSON objects. Each object describes ** a single hook. Example: ** { ** "type": "after-receive", // type of hook ** "cmd": "command-to-run", // command to run ** "seq": 50 // run in this order ** } ** ** hook-last-rcvid The last rcvid for which post-receive hooks were ** run. ** ** hook-embargo Do not run hooks again before this julianday. ** ** For "after-receive" hooks, a list of the received artifacts is sent ** into the command via standard input. Each line of input begins with ** the hash of the artifact and continues with a description of the ** interpretation of the artifact. */ #include "config.h" #include "hook.h" /* ** SETTING: hooks sensitive width=40 block-text ** The "hooks" setting contains JSON that describes all defined ** hooks. The value is an array of objects. Each object describes ** a single hook. Example: ** ** ** { ** "type": "after-receive", // type of hook ** "cmd": "command-to-run", // command to run ** "seq": 50 // run in this order ** } */ /* ** List of valid hook types: */ static const char *azType[] = { "after-receive", "before-commit", "disabled", }; /* ** Return true if zType is a valid hook type. */ static int is_valid_hook_type(const char *zType){ int i; for(i=0; i<count(azType); i++){ if( strcmp(azType[i],zType)==0 ) return 1; } return 0; } /* ** Throw an error if zType is not a valid hook type */ static void validate_type(const char *zType){ int i; char *zMsg; if( is_valid_hook_type(zType) ) return; zMsg = mprintf("\"%s\" is not a valid hook type - should be one of:", zType); for(i=0; i<count(azType); i++){ zMsg = mprintf("%z %s", zMsg, azType[i]); } fossil_fatal("%s", zMsg); } /* ** Translate a hook command string into its executable format by ** converting every %-substitutions as follows: ** ** %F -> Name of the fossil executable ** %R -> Name of the repository ** %A -> Auxiliary information filename (might be empty string) ** ** The returned string is obtained from fossil_malloc() and should ** be freed by the caller. */ static char *hook_subst( const char *zCmd, const char *zAuxFilename /* Name of auxiliary information file */ ){ Blob r; int i; blob_init(&r, 0, 0); if( zCmd==0 ) return 0; while( zCmd[0] ){ for(i=0; zCmd[i] && zCmd[i]!='%'; i++){} blob_append(&r, zCmd, i); if( zCmd[i]==0 ) break; if( zCmd[i+1]=='F' ){ blob_append(&r, g.nameOfExe, -1); zCmd += i+2; }else if( zCmd[i+1]=='R' ){ blob_append(&r, g.zRepositoryName, -1); zCmd += i+2; }else if( zCmd[i+1]=='A' ){ if( zAuxFilename ) blob_append(&r, zAuxFilename, -1); zCmd += i+2; }else{ blob_append(&r, zCmd+i, 1); zCmd += i+1; } } blob_str(&r); return r.aData; } /* ** Record the fact that new artifacts are expected within N seconds ** (N is normally a small number) and so post-receive hooks should ** probably be deferred until after the new artifacts arrive. ** ** If N==0, then there is no expectation of new artifacts arriving ** soon and so post-receive hooks can be run without delay. */ void hook_expecting_more_artifacts(int N){ if( !db_is_writeable("repository") ){ /* No-op */ }else if( N>0 ){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "REPLACE INTO config(name,value,mtime)" "VALUES('hook-embargo',now()+%d,now())", N ); db_protect_pop(); }else{ db_unset("hook-embargo",0); } } /* ** Fill the Blob pOut with text that describes all artifacts ** received after zBaseRcvid up to and including zNewRcvid. ** Except, never include more than one days worth of changes. ** ** If zBaseRcvid is NULL, then use the "hook-last-rcvid" setting. ** If zNewRcvid is NULL, use the last available rcvid. */ void hook_changes(Blob *pOut, const char *zBaseRcvid, const char *zNewRcvid){ char *zWhere; Stmt q; if( zBaseRcvid==0 ){ zBaseRcvid = db_get("hook-last-rcvid","0"); } if( zNewRcvid==0 ){ zNewRcvid = db_text("0","SELECT max(rcvid) FROM rcvfrom"); } /* Adjust the baseline rcvid to omit change that are more than ** 24 hours older than the most recent change. */ zBaseRcvid = db_text(0, "SELECT min(rcvid) FROM rcvfrom" " WHERE rcvid>=%d" " AND mtime>=(SELECT mtime FROM rcvfrom WHERE rcvid=%d)-1.0", atoi(zBaseRcvid), atoi(zNewRcvid) ); zWhere = mprintf("IN (SELECT rid FROM blob WHERE rcvid>%d AND rcvid<=%d)", atoi(zBaseRcvid), atoi(zNewRcvid)); describe_artifacts(zWhere); fossil_free(zWhere); db_prepare(&q, "SELECT uuid, summary FROM description"); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(pOut, "%s %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); } /* ** COMMAND: hook* ** ** Usage: %fossil hook COMMAND ... ** ** Commands include: ** ** > fossil hook add --command COMMAND --type TYPE --sequence NUMBER ** ** Create a new hook. The --command and --type arguments are ** required. --sequence is optional. ** ** > fossil hook delete ID ... ** ** Delete one or more hooks by their IDs. ID can be "all" ** to delete all hooks. Caution: There is no "undo" for ** this operation. Deleted hooks are permanently lost. ** ** > fossil hook edit --command COMMAND --type TYPE --sequence NUMBER ID ... ** ** Make changes to one or more existing hooks. The ID argument ** is either a hook-id, or a list of hook-ids, or the keyword ** "all". For example, to disable hook number 2, use: ** ** fossil hook edit --type disabled 2 ** ** > fossil hook list ** ** Show all current hooks ** ** > fossil hook status ** ** Print the values of CONFIG table entries that are relevant to ** hook processing. Used for debugging. ** ** > fossil hook test [OPTIONS] ID ** ** Run the hook script given by ID for testing purposes. ** Options: ** ** --dry-run Print the script on stdout rather than run it ** --base-rcvid N Pretend that the hook-last-rcvid value is N ** --new-rcvid M Pretend that the last rcvid valud is M ** --aux-file NAME NAME is substituted for %A in the script ** ** The --base-rcvid and --new-rcvid options are silently ignored if ** the hook type is not "after-receive". The default values for ** --base-rcvid and --new-rcvid cause the last receive to be processed. */ void hook_cmd(void){ const char *zCmd; int nCmd; db_find_and_open_repository(0, 0); if( g.argc<3 ){ usage("SUBCOMMAND ..."); } zCmd = g.argv[2]; nCmd = (int)strlen(zCmd); if( strncmp(zCmd, "add", nCmd)==0 ){ const char *zCmd = find_option("command",0,1); const char *zType = find_option("type",0,1); const char *zSeq = find_option("sequence",0,1); int nSeq; verify_all_options(); if( zCmd==0 || zType==0 ){ fossil_fatal("the --command and --type options are required"); } validate_type(zType); nSeq = zSeq ? atoi(zSeq) : 10; db_begin_write(); db_unprotect(PROTECT_CONFIG); db_multi_exec( "INSERT OR IGNORE INTO config(name,value) VALUES('hooks','[]');\n" "UPDATE config" " SET value=json_insert(" " CASE WHEN json_valid(value) THEN value ELSE '[]' END,'$[#]'," " json_object('cmd',%Q,'type',%Q,'seq',%d))," " mtime=now()" " WHERE name='hooks';", zCmd, zType, nSeq ); db_protect_pop(); db_commit_transaction(); }else if( strncmp(zCmd, "edit", nCmd)==0 ){ const char *zCmd = find_option("command",0,1); const char *zType = find_option("type",0,1); const char *zSeq = find_option("sequence",0,1); int nSeq; int i; verify_all_options(); if( zCmd==0 && zType==0 && zSeq==0 ){ fossil_fatal("at least one of --command, --type, or --sequence" " is required"); } if( zType ) validate_type(zType); nSeq = zSeq ? atoi(zSeq) : 10; if( g.argc<4 ) usage("delete ID ..."); db_begin_write(); for(i=3; i<g.argc; i++){ Blob sql; int id; if( sqlite3_strglob("*[^0-9]*", g.argv[i])==0 ){ fossil_fatal("not a valid ID: \"%s\"", g.argv[i]); } id = atoi(g.argv[i]); blob_init(&sql, 0, 0); blob_append_sql(&sql, "UPDATE config SET mtime=now(), value=" "json_replace(CASE WHEN json_valid(value) THEN value ELSE '[]' END"); if( zCmd ){ blob_append_sql(&sql, ",'$[%d].cmd',%Q", id, zCmd); } if( zType ){ blob_append_sql(&sql, ",'$[%d].type',%Q", id, zType); } if( zSeq ){ blob_append_sql(&sql, ",'$[%d].seq',%d", id, nSeq); } blob_append_sql(&sql,") WHERE name='hooks';"); db_unprotect(PROTECT_CONFIG); db_multi_exec("%s", blob_sql_text(&sql)); db_protect_pop(); blob_reset(&sql); } db_commit_transaction(); }else if( strncmp(zCmd, "delete", nCmd)==0 ){ int i; verify_all_options(); if( g.argc<4 ) usage("delete ID ..."); db_begin_write(); db_unprotect(PROTECT_CONFIG); db_multi_exec( "INSERT OR IGNORE INTO config(name,value) VALUES('hooks','[]');\n" ); for(i=3; i<g.argc; i++){ const char *zId = g.argv[i]; if( strcmp(zId,"all")==0 ){ db_unprotect(PROTECT_ALL); db_set("hooks","[]", 0); db_protect_pop(); break; } if( sqlite3_strglob("*[^0-9]*", g.argv[i])==0 ){ fossil_fatal("not a valid ID: \"%s\"", g.argv[i]); } db_multi_exec( "UPDATE config" " SET value=json_remove(" " CASE WHEN json_valid(value) THEN value ELSE '[]' END,'$[%d]')," " mtime=now()" " WHERE name='hooks';", atoi(zId) ); } db_protect_pop(); db_commit_transaction(); }else if( strncmp(zCmd, "list", nCmd)==0 ){ Stmt q; int n = 0; verify_all_options(); db_prepare(&q, "SELECT jx.key," " jx.value->>'seq'," " jx.value->>'cmd'," " jx.value->>'type'" " FROM config, json_each(config.value) AS jx" " WHERE config.name='hooks' AND json_valid(config.value)" ); while( db_step(&q)==SQLITE_ROW ){ if( n++ ) fossil_print("\n"); fossil_print("%3d: type = %s\n", db_column_int(&q,0), db_column_text(&q,3)); fossil_print(" command = %s\n", db_column_text(&q,2)); fossil_print(" sequence = %d\n", db_column_int(&q,1)); } db_finalize(&q); }else if( strncmp(zCmd, "status", nCmd)==0 ){ Stmt q; db_prepare(&q, "SELECT name, quote(value) FROM config WHERE name IN" "('hooks','hook-embargo','hook-last-rcvid') ORDER BY name" ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%s: %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); }else if( strncmp(zCmd, "test", nCmd)==0 ){ Stmt q; int id; int bDryRun = find_option("dry-run", "n", 0)!=0; const char *zOrigRcvid = find_option("base-rcvid",0,1); const char *zNewRcvid = find_option("new-rcvid",0,1); const char *zAuxFilename = find_option("aux-file",0,1); verify_all_options(); if( g.argc<4 ) usage("test ID"); id = atoi(g.argv[3]); if( zOrigRcvid==0 ){ zOrigRcvid = db_text(0, "SELECT max(rcvid)-1 FROM rcvfrom"); } db_prepare(&q, "SELECT value->>'$[%d].cmd', value->>'$[%d].type'=='after-receive'" " FROM config" " WHERE name='hooks' AND json_valid(value)", id, id ); while( db_step(&q)==SQLITE_ROW ){ const char *zCmd = db_column_text(&q,0); char *zCmd2 = hook_subst(zCmd, zAuxFilename); int needOut = db_column_int(&q,1); Blob out; if( zCmd2==0 ) continue; blob_init(&out,0,0); if( needOut ) hook_changes(&out, zOrigRcvid, zNewRcvid); if( bDryRun ){ fossil_print("%s\n", zCmd2); if( needOut ){ fossil_print("%s", blob_str(&out)); } }else if( needOut ){ int fdFromChild; FILE *toChild; int pidChild; if( popen2(zCmd2, &fdFromChild, &toChild, &pidChild, 0)==0 ){ if( toChild ){ fwrite(blob_buffer(&out),1,blob_size(&out),toChild); } pclose2(fdFromChild, toChild, pidChild); } }else{ fossil_system(zCmd2); } fossil_free(zCmd2); blob_reset(&out); } db_finalize(&q); }else { fossil_fatal("unknown command \"%s\" - should be one of: " "add delete edit list test", zCmd); } } /* ** The backoffice calls this routine to run the after-receive hooks. */ int hook_backoffice(void){ Stmt q; const char *zLastRcvid = 0; char *zNewRcvid = 0; Blob chng; int cnt = 0; db_begin_write(); if( !db_exists("SELECT 1 FROM config WHERE name='hooks'") ){ goto hook_backoffice_done; /* No hooks */ } if( db_int(0, "SELECT now()<value+0 FROM config" " WHERE name='hook-embargo'") ){ goto hook_backoffice_done; /* Within the embargo window */ } zLastRcvid = db_get("hook-last-rcvid","0"); zNewRcvid = db_text("0","SELECT max(rcvid) FROM rcvfrom"); if( atoi(zLastRcvid)>=atoi(zNewRcvid) ){ goto hook_backoffice_done; /* no new content */ } blob_init(&chng, 0, 0); db_prepare(&q, "SELECT jx.value->>'cmd'" " FROM config, json_each(config.value) AS jx" " WHERE config.name='hooks' AND json_valid(config.value)" " AND jx.value->>'type'='after-receive'" " ORDER BY jx.value->>'seq';" ); while( db_step(&q)==SQLITE_ROW ){ char *zCmd; int fdFromChild; FILE *toChild; int childPid; if( cnt==0 ){ hook_changes(&chng, zLastRcvid, 0); } zCmd = hook_subst(db_column_text(&q,0), 0); if( popen2(zCmd, &fdFromChild, &toChild, &childPid, 0)==0 ){ if( toChild ){ fwrite(blob_buffer(&chng),1,blob_size(&chng),toChild); } pclose2(fdFromChild, toChild, childPid); } fossil_free(zCmd); cnt++; } db_finalize(&q); db_set("hook-last-rcvid", zNewRcvid, 0); blob_reset(&chng); hook_backoffice_done: db_commit_transaction(); return cnt; } /* ** Return true if one or more hooks of type zType exit. */ int hook_exists(const char *zType){ return db_exists( "SELECT 1" " FROM config, json_each(config.value) AS jx" " WHERE config.name='hooks' AND json_valid(config.value)" " AND jx.value->>'type'=%Q;", zType ); } /* ** Run all hooks of type zType. Use zAuxFile as the auxiliary information ** file. ** ** If any hook returns non-zero, then stop running and return non-zero. ** Return zero only if all hooks return zero. */ int hook_run(const char *zType, const char *zAuxFile, int traceFlag){ Stmt q; int rc = 0; if( !db_exists("SELECT 1 FROM config WHERE name='hooks'") ){ return 0; } db_prepare(&q, "SELECT jx.value->>'cmd' " " FROM config, json_each(config.value) AS jx" " WHERE config.name='hooks' AND json_valid(config.value)" " AND jx.value->>'type'==%Q" " ORDER BY jx.value->'seq';", zType ); while( db_step(&q)==SQLITE_ROW ){ char *zCmd; zCmd = hook_subst(db_column_text(&q,0), zAuxFile); if( traceFlag ){ fossil_print("%s hook: %s\n", zType, zCmd); } rc = fossil_system(zCmd); fossil_free(zCmd); if( rc ){ break; } } db_finalize(&q); return rc; } |
Added src/href.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* As an anti-robot defense, <a> elements are initially coded with the ** href= set to the honeypot, and <form> elements are initialized with ** action= set to the login page. The real values for href= and action= ** are held in data-href= and data-action=. The following code moves ** data-href= into href= and data-action= into action= for all ** <a> and <form> elements, after delay and maybe also after mouse ** movement is seen. ** ** Before sourcing this script, create a separate <script> element ** (with type='application/json' to avoid Content Security Policy issues) ** containing: ** ** {"delay":MILLISECONDS, "mouseover":BOOLEAN} ** ** The <script> must have an id='href-data'. DELAY is the number ** milliseconds delay prior to populating href= and action=. If the ** mouseover boolean is true, then the href= rewrite is further delayed ** until the first mousedown event that occurs after the timer expires. */ var antiRobot = 0; function antiRobotGo(){ if( antiRobot!=3 ) return; antiRobot = 7; var anchors = document.getElementsByTagName("a"); for(var i=0; i<anchors.length; i++){ var j = anchors[i]; if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href"); } var forms = document.getElementsByTagName("form"); for(var i=0; i<forms.length; i++){ var j = forms[i]; if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action"); } } function antiRobotDefense(){ var x = document.getElementById("href-data"); var jx = x.textContent || x.innerText; var g = JSON.parse(jx); if( g.mouseover ){ document.body.onmousedown=function(){ antiRobot |= 2; antiRobotGo(); document.body.onmousedown=null; } document.body.onmousemove=function(){ antiRobot |= 2; antiRobotGo(); document.body.onmousemove=null; } }else{ antiRobot |= 2; } if( g.delay>0 ){ setTimeout(function(){ antiRobot |= 1; antiRobotGo(); }, g.delay) }else{ antiRobot |= 1; } antiRobotGo(); } antiRobotDefense(); |
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 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 | ** ** 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 #if INTERFACE /* ** Bits of the mHttpFlags parameter to http_exchange() */ #define HTTP_USE_LOGIN 0x00001 /* Add a login card to the sync message */ #define HTTP_GENERIC 0x00002 /* Generic HTTP request */ #define HTTP_VERBOSE 0x00004 /* HTTP status messages */ #define HTTP_QUIET 0x00008 /* No surplus output */ #define HTTP_NOCOMPRESS 0x00010 /* Omit payload compression */ #endif /* Maximum number of HTTP Authorization attempts */ #define MAX_HTTP_AUTH 2 /* Keep track of HTTP Basic Authorization failures */ static int fSeenHttpAuth = 0; /* The N value for most recent http-request-N.txt and http-reply-N.txt ** trace files. */ static int traceCnt = 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 (as computed by ** sha1_shared_secret()), not the original password. So convert the ** password to its SHA1 encoding if it isn't already a SHA1 hash. ** ** We assume that a hexadecimal string of exactly 40 characters is a ** SHA1 hash, not an original password. If a user has a password which ** just happens to be a 40-character hex string, then this routine won't ** be able to distinguish it from a hash, the translation will not be ** performed, and the sync won't work. */ if( zPw && zPw[0] && (strlen(zPw)!=40 || !validate16(zPw,40)) ){ const char *zProjectCode = 0; if( g.url.flags & URL_USE_PARENT ){ zProjectCode = db_get("parent-project-code", 0); }else{ zProjectCode = db_get("project-code", 0); } zPw = sha1_shared_secret(zPw, zLogin, zProjectCode); if( g.url.pwConfig!=0 && (g.url.flags & URL_REMEMBER_PW)!=0 ){ char *x = obscure(zPw); db_set(g.url.pwConfig/*works-like:"x"*/, x, 0); fossil_free(x); } fossil_free(g.url.passwd); g.url.passwd = fossil_strdup(zPw); } blob_append(&pw, zPw, -1); sha1sum_blob(&pw, &sig); blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig); blob_reset(&pw); blob_reset(&sig); blob_reset(&nonce); } /* ** Construct an appropriate HTTP request header. Write the header ** into pHdr. This routine initializes the pHdr blob. pPayload is ** the complete payload (including the login card) already compressed. */ static void http_build_header( Blob *pPayload, /* the payload that will be sent */ Blob *pHdr, /* construct the header here */ const char *zAltMimetype /* Alternative mimetype */ ){ int nPayload = pPayload ? blob_size(pPayload) : 0; blob_zero(pHdr); blob_appendf(pHdr, "%s %s%s HTTP/1.0\r\n", nPayload>0 ? "POST" : "GET", g.url.path, g.url.path[0]==0 ? "/" : ""); 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( nPayload ){ if( zAltMimetype ){ blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype); }else 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", blob_size(pPayload)); } blob_append(pHdr, "\r\n", 2); } /* ** 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, 0); 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; } /* ** Send content pSend to the the server identified by g.url using the ** external program given by g.zHttpCmd. Capture the reply from that ** program and load it into pReply. ** ** This routine implements the --transport-command option for "fossil sync". */ static int http_exchange_external( Blob *pSend, /* Message to be sent */ Blob *pReply, /* Write the reply here */ int mHttpFlags, /* Flags. See above */ const char *zAltMimetype /* Alternative mimetype if not NULL */ ){ char *zUplink; char *zDownlink; char *zCmd; char *zFullUrl; int rc; zUplink = fossil_temp_filename(); zDownlink = fossil_temp_filename(); zFullUrl = url_full(&g.url); zCmd = mprintf("%s %$ %$ %$", g.zHttpCmd, zFullUrl,zUplink,zDownlink); fossil_free(zFullUrl); blob_write_to_file(pSend, zUplink); if( g.fHttpTrace ){ fossil_print("RUN %s\n", zCmd); } rc = fossil_system(zCmd); if( rc ){ fossil_warning("Transport command failed: %s\n", zCmd); } fossil_free(zCmd); file_delete(zUplink); if( file_size(zDownlink, ExtFILE)<0 ){ blob_zero(pReply); }else{ blob_read_from_file(pReply, zDownlink, ExtFILE); file_delete(zDownlink); } return rc; } /* If iTruth<0 then guess as to whether or not a PATH= argument is required ** when using ssh to run fossil on a remote machine name zHostname. Return ** true if a PATH= should be provided and 0 if not. ** ** If iTruth is 1 or 0 then that means that the PATH= is or is not required, ** respectively. Record this fact for future reference. ** ** If iTruth is 99 or more, then toggle the value that will be returned ** for future iTruth==(-1) queries. */ int ssh_needs_path_argument(const char *zHostname, int iTruth){ int ans = 0; /* Default to "no" */ char *z = mprintf("use-path-for-ssh:%s", zHostname); if( iTruth<0 ){ if( db_get_boolean(z/*works-like:"x"*/, 0) ) ans = 1; }else{ if( iTruth>=99 ){ iTruth = !db_get_boolean(z/*works-like:"x"*/, 0); } if( iTruth ){ ans = 1; db_set(z/*works-like:"x"*/, "1", 1); }else{ db_unset(z/*works-like:"x"*/, 1); } } fossil_free(z); return ans; } /* ** COMMAND: test-ssh-needs-path ** ** Usage: fossil test-ssh-needs-path ?HOSTNAME? ?BOOLEAN? ** ** With one argument, show whether or not the PATH= argument is included ** by default for HOSTNAME. If the second argument is a boolean, then ** change the value. ** ** With no arguments, show all hosts for which ssh-needs-path is true. */ void test_ssh_needs_path(void){ db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); db_open_config(0,0); if( g.argc>=3 ){ const char *zHost = g.argv[2]; int a = -1; int rc; if( g.argc>=4 ) a = is_truth(g.argv[3]); rc = ssh_needs_path_argument(zHost, a); fossil_print("%-20s %s\n", zHost, rc ? "yes" : "no"); }else{ Stmt s; db_swap_connections(); db_prepare(&s, "SELECT substr(name,18) FROM global_config" " WHERE name GLOB 'use-path-for-ssh:*'"); while( db_step(&s)==SQLITE_ROW ){ const char *zHost = db_column_text(&s,0); fossil_print("%-20s yes\n", zHost); } db_finalize(&s); db_swap_connections(); } } /* Add an approprate PATH= argument to the SSH command under construction ** in pCmd. ** ** About This Feature ** ================== ** ** On some ssh servers (Macs in particular are guilty of this) the PATH ** variable in the shell that runs the command that is sent to the remote ** host contains a limited number of read-only system directories: ** ** /usr/bin:/bin:/usr/sbin:/sbin ** ** The fossil executable cannot be installed into any of those directories ** because they are locked down, and so the "fossil" command cannot run. ** ** To work around this, the fossil command is prefixed with the PATH= ** argument, inserted by this function, to augment the PATH with additional ** directories in which the fossil executable is often found. ** ** But other ssh servers are confused by this initial PATH= argument. ** Some ssh servers have a list of programs that they are allowed to run ** and will fail if the first argument is not on that list, and PATH=.... ** is not on that list. ** ** So that various commands that use ssh can run seamlessly on a variety ** of systems (commands that use ssh include "fossil sync" with an ssh: ** URL and the "fossil patch pull" and "fossil patch push" commands where ** the destination directory starts with HOSTNAME: or USER@HOSTNAME:.) ** the following algorithm is used: ** ** * First try running the fossil without any PATH= argument. If that ** works (and it does on a majority of systems) then we are done. ** ** * If the first attempt fails, then try again after adding the ** PATH= prefix argument. (This function is what adds that ** argument.) If the retry works, then remember that fact using ** the use-path-for-ssh:HOSTNAME setting so that the first step ** is skipped on subsequent uses of the same command. ** ** See the forum thread at ** https://fossil-scm.org/forum/forumpost/4903cb4b691af7ce for more ** background. ** ** See also: ** ** * The ssh_needs_path_argument() function above. ** * The test-ssh-needs-path command that shows the settings ** that cache whether or not a PATH= is needed for a particular ** HOSTNAME. */ void ssh_add_path_argument(Blob *pCmd){ blob_append_escaped_arg(pCmd, "PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1); } /* ** Return the complete text of the last HTTP reply as saved in the ** http-reply-N.txt file. This only works if run using --httptrace. ** Without the --httptrace option, this routine returns a NULL pointer. ** It still might return a NULL pointer if for some reason it cannot ** find and open the last http-reply-N.txt file. */ char *http_last_trace_reply(void){ Blob x; int n; char *zFilename; if( g.fHttpTrace==0 ) return 0; zFilename = mprintf("http-reply-%d.txt", traceCnt); n = blob_read_from_file(&x, zFilename, ExtFILE); fossil_free(zFilename); if( n<=0 ) return 0; return blob_str(&x); } /* ** 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, /* Message to be sent */ Blob *pReply, /* Write the reply here */ int mHttpFlags, /* Flags. See above */ int maxRedirect, /* Max number of redirects */ const char *zAltMimetype /* Alternative mimetype if not NULL */ ){ 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 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( g.zHttpCmd!=0 ){ /* Handle the --transport-command option for "fossil sync" and similar */ return http_exchange_external(pSend,pReply,mHttpFlags,zAltMimetype); } /* Activate the PATH= auxiliary argument to the ssh command if that ** is called for. */ if( g.url.isSsh && (g.url.flags & URL_SSH_RETRY)==0 && ssh_needs_path_argument(g.url.hostname, -1) ){ g.url.flags |= URL_SSH_PATH; } if( transport_open(&g.url) ){ fossil_warning("%s", transport_errmsg(&g.url)); return 1; } /* Construct the login card and prepare the complete payload */ if( blob_size(pSend)==0 ){ blob_zero(&payload); }else{ blob_zero(&login); if( mHttpFlags & HTTP_USE_LOGIN ) http_build_login_card(pSend, &login); if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){ payload = login; blob_append(&payload, blob_buffer(pSend), blob_size(pSend)); }else{ blob_compress2(&login, pSend, &payload); blob_reset(&login); } } /* Construct the HTTP request header */ http_build_header(&payload, &hdr, zAltMimetype); /* When tracing, write the transmitted HTTP message both to standard ** output and into a file. The file can then be used to drive the ** server-side like this: ** ** ./fossil test-http <http-request-1.txt */ if( g.fHttpTrace ){ char *zOutFile; FILE *out; traceCnt++; zOutFile = mprintf("http-request-%d.txt", traceCnt); out = fopen(zOutFile, "wb"); if( out ){ fwrite(blob_buffer(&hdr), 1, blob_size(&hdr), out); fwrite(blob_buffer(&payload), 1, blob_size(&payload), out); fclose(out); } free(zOutFile); zOutFile = mprintf("http-reply-%d.txt", traceCnt); out = fopen(zOutFile, "wb"); transport_log(out); free(zOutFile); } /* ** Send the request to the server. */ if( mHttpFlags & HTTP_VERBOSE ){ fossil_print("URL: %s\n", g.url.canonical); fossil_print("Sending %d byte header and %d byte payload\n", blob_size(&hdr), blob_size(&payload)); } 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; iHttpVersion = -1; while( (zLine = transport_receive_line(&g.url))!=0 && zLine[0]!=0 ){ if( mHttpFlags & HTTP_VERBOSE ){ fossil_print("Read: [%s]\n", zLine); } 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, mHttpFlags, maxRedirect, zAltMimetype); } } if( rc!=200 && rc!=301 && rc!=302 && rc!=307 && rc!=308 ){ 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 && rc!=307 && rc!=308 ){ 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 ) iHttpVersion = 1; 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 ){ if( sqlite3_strlike("%close%", &zLine[11], 0)==0 ){ closeConnection = 1; }else if( sqlite3_strlike("%keep-alive%", &zLine[11], 0)==0 ){ closeConnection = 0; } }else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) && fossil_strnicmp(zLine, "location:", 9)==0 ){ int i, j; int wasHttps; 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; } if( (mHttpFlags & HTTP_QUIET)==0 ){ fossil_print("redirect with status %d to %s\n", rc, &zLine[i]); } if( g.url.isFile || g.url.isSsh ){ fossil_warning("cannot redirect from %s to %s", g.url.canonical, &zLine[i]); goto write_err; } wasHttps = g.url.isHttps; url_parse(&zLine[i], 0); if( wasHttps && !g.url.isHttps ){ fossil_warning("cannot redirect from HTTPS to HTTP"); goto write_err; } if( g.url.isSsh || g.url.isFile ){ fossil_warning("cannot redirect to %s", &zLine[i]); goto write_err; } transport_close(&g.url); transport_global_shutdown(&g.url); fSeenHttpAuth = 0; if( g.zHttpAuth ) free(g.zHttpAuth); g.zHttpAuth = get_httpauth(); if( rc==301 || rc==308 ) url_remember(); return http_exchange(pSend, pReply, mHttpFlags, maxRedirect, zAltMimetype); }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( mHttpFlags & HTTP_GENERIC ){ if( mHttpFlags & HTTP_NOCOMPRESS ) isCompressed = 0; }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){ isError = 1; } } } } if( iHttpVersion<0 ){ /* We got nothing back from the server. If using the ssh: protocol, ** this might mean we need to add or remove the PATH=... argument ** to the SSH command being sent. If that is the case, retry the ** request after adding or removing the PATH= argument. */ if( g.url.isSsh /* This is an SSH: sync */ && (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */ && (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */ ){ /* Retry after flipping the SSH_PATH setting */ transport_close(&g.url); fossil_print( "First attempt to run fossil on %s using SSH failed.\n" "Retrying %s the PATH= argument.\n", g.url.hostname, (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with" ); g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY; rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype); if( rc==0 ){ (void)ssh_needs_path_argument(g.url.hostname, (g.url.flags & URL_SSH_PATH)!=0); } return rc; }else{ /* The problem could not be corrected by retrying. Report the ** the error. */ if( g.url.isSsh && !g.fSshTrace ){ fossil_warning("server did not reply: " " rerun with --sshtrace for diagnostics"); }else{ 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); if( iLength==0 ){ /* No content to read */ }else if( iLength>0 ){ /* Read content of a known length */ int iRecvLen; /* Received length of the reply payload */ blob_resize(pReply, iLength); iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength); if( mHttpFlags & HTTP_VERBOSE ){ fossil_print("Reply received: %d of %d bytes\n", iRecvLen, iLength); } if( iRecvLen != iLength ){ fossil_warning("response truncated: got %d bytes of %d", iRecvLen, iLength); goto write_err; } }else if( closeConnection ){ /* Read content until end-of-file */ int iRecvLen; /* Received length of the reply payload */ unsigned int nReq = 1000; unsigned int nPrior = 0; do{ nReq *= 2; blob_resize(pReply, nPrior+nReq); iRecvLen = transport_receive(&g.url, &pReply->aData[nPrior], (int)nReq); nPrior += iRecvLen; pReply->nUsed = nPrior; }while( iRecvLen==nReq && nReq<0x20000000 ); if( mHttpFlags & HTTP_VERBOSE ){ fossil_print("Reply received: %u bytes (w/o content-length)\n", nPrior); } }else{ assert( iLength<0 && !closeConnection ); fossil_warning("\"content-length\" missing from %d keep-alive reply", rc); } 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; } /* ** COMMAND: test-httpmsg ** ** Usage: %fossil test-httpmsg ?OPTIONS? URL ?PAYLOAD? ?OUTPUT? ** ** Send an HTTP message to URL and get the reply. PAYLOAD is a file containing ** the payload, or "-" to read payload from standard input. a POST message ** is sent if PAYLOAD is specified and is non-empty. If PAYLOAD is omitted ** or is an empty file, then a GET message is sent. ** ** If a second filename (OUTPUT) is given after PAYLOAD, then the reply ** is written into that second file instead of being written on standard ** output. Use the "--out OUTPUT" option to specify an output file for ** a GET request where there is no PAYLOAD. ** ** Options: ** --compress Use ZLIB compression on the payload ** --mimetype TYPE Mimetype of the payload ** --out FILE Store the reply in FILE ** -v Verbose output ** --xfer PAYLOAD in a Fossil xfer protocol message */ void test_httpmsg_command(void){ const char *zMimetype; const char *zInFile; const char *zOutFile; Blob in, out; unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS; zMimetype = find_option("mimetype",0,1); zOutFile = find_option("out","o",1); if( find_option("verbose","v",0)!=0 ) mHttpFlags |= HTTP_VERBOSE; if( find_option("compress",0,0)!=0 ) mHttpFlags &= ~HTTP_NOCOMPRESS; if( find_option("xfer",0,0)!=0 ){ mHttpFlags |= HTTP_USE_LOGIN; mHttpFlags &= ~HTTP_GENERIC; } verify_all_options(); if( g.argc<3 || g.argc>5 ){ usage("URL ?PAYLOAD? ?OUTPUT?"); } zInFile = g.argc>=4 ? g.argv[3] : 0; if( g.argc==5 ){ if( zOutFile ){ fossil_fatal("output file specified twice: \"--out %s\" and \"%s\"", zOutFile, g.argv[4]); } zOutFile = g.argv[4]; } url_parse(g.argv[2], 0); if( g.url.protocol[0]!='h' ){ fossil_fatal("the %s command supports only http: and https:", g.argv[1]); } if( zInFile ){ blob_read_from_file(&in, zInFile, ExtFILE); if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){ if( fossil_strcmp(zInFile,"-")==0 ){ zMimetype = "application/x-unknown"; }else{ zMimetype = mimetype_from_name(zInFile); } } }else{ blob_init(&in, 0, 0); } blob_init(&out, 0, 0); if( (mHttpFlags & HTTP_VERBOSE)==0 && zOutFile==0 ){ zOutFile = "-"; mHttpFlags |= HTTP_QUIET; } http_exchange(&in, &out, mHttpFlags, 4, zMimetype); if( zOutFile ) blob_write_to_file(&out, zOutFile); blob_zero(&in); blob_zero(&out); } |
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 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 | ** 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. */ #if defined(_WIN32) # if defined(_WIN32_WINNT) # undef _WIN32_WINNT # endif # define _WIN32_WINNT 0x501 #endif #ifndef __EXTENSIONS__ # define __EXTENSIONS__ 1 /* IPv6 won't compile on Solaris without this */ #endif #include "config.h" #include "http_socket.h" #if defined(_WIN32) # include <winsock2.h> # include <ws2tcpip.h> #else # include <netinet/in.h> # include <arpa/inet.h> # include <sys/socket.h> # include <netdb.h> #endif #include <assert.h> #include <sys/types.h> #include <signal.h> /* ** There can only be a single socket connection open at a time. ** State information about that socket is stored in the following ** local variables: */ static int socketIsInit = 0; /* True after global initialization */ #if defined(_WIN32) static WSADATA socketInfo; /* Windows socket initialize data */ #endif static int iSocket = -1; /* The socket on which we talk to the server */ static char *socketErrMsg = 0; /* Text of most recent socket error */ /* ** Clear the socket error message */ static void socket_clear_errmsg(void){ 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); } /* ** Return the current socket error message */ char *socket_errmsg(void){ char *zResult = socketErrMsg; socketErrMsg = 0; return zResult; } /* ** Call this routine once before any other use of the socket interface. ** This routine does initial configuration of the socket module. */ void socket_global_init(void){ if( socketIsInit==0 ){ #if defined(_WIN32) if( WSAStartup(MAKEWORD(2,0), &socketInfo)!=0 ){ fossil_panic("can't initialize winsock"); } #endif socketIsInit = 1; } } /* ** Call this routine to shutdown the socket module prior to program ** exit. */ void socket_global_shutdown(void){ if( socketIsInit ){ #if defined(_WIN32) WSACleanup(); #endif socket_clear_errmsg(); socketIsInit = 0; } } /* ** Close the currently open socket. If no socket is open, this routine ** is a no-op. */ void socket_close(void){ if( iSocket>=0 ){ #if defined(_WIN32) if( shutdown(iSocket,1)==0 ) shutdown(iSocket,0); closesocket(iSocket); #else close(iSocket); #endif iSocket = -1; } } /* ** Open a socket connection. The identify of the server is determined ** by pUrlData ** ** pUrlData->name Name of the server. Ex: 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(); socket_close(); memset(&hints, 0, sizeof(struct addrinfo)); 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, const void *pContent, size_t N){ ssize_t sent; size_t total = 0; while( N>0 ){ sent = send(iSocket, pContent, N, 0); if( sent<=0 ) break; total += (size_t)sent; N -= (size_t)sent; pContent = (void*)&((char*)pContent)[sent]; } return total; } /* ** Receive content back from the open socket connection. ** Return the number of bytes read. ** ** When bDontBlock is false, this function blocks until all N bytes ** have been read. */ size_t socket_receive(void *NotUsed, void *pContent, size_t N, int bDontBlock){ ssize_t got; size_t total = 0; int flags = 0; #ifdef MSG_DONTWAIT if( bDontBlock ) flags |= MSG_DONTWAIT; #endif while( N>0 ){ /* WinXP fails for large values of N. So limit it to 64KiB. */ got = recv(iSocket, pContent, N>65536 ? 65536 : N, flags); if( got<=0 ) break; total += (size_t)got; N -= (size_t)got; pContent = (void*)&((char*)pContent)[got]; } return total; } |
Added src/http_ssl.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 | /* ** Copyright (c) 2009 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 manages low-level SSL communications. ** ** This file implements a singleton. A single SSL connection may be active ** at a time. State information is stored in static variables. ** ** The SSL connections can be either a client or a server. But all ** connections for a single process must be of the same type, either client ** or server. ** ** SSL support is abstracted out into this module because Fossil can ** be compiled without SSL support (which requires OpenSSL library) */ #include "config.h" #include "http_ssl.h" #ifdef FOSSIL_ENABLE_SSL #include <openssl/bio.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/x509.h> #include <assert.h> #include <sys/types.h> /* ** 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; /* 0: uninit 1: init as client 2: init as server */ 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; static struct { /* Accept this SSL cert for this session only */ char *zHost; /* Subject or host name */ char *zHash; /* SHA2-256 hash of the cert */ } sException; static int sslNoCertVerify = 0; /* Do not verify SSL certs */ /* This is a self-signed cert in the PEM format that can be used when ** no other certs are available. */ static const char sslSelfCert[] = "-----BEGIN CERTIFICATE-----\n" "MIIDMTCCAhkCFGrDmuJkkzWERP/ITBvzwwI2lv0TMA0GCSqGSIb3DQEBCwUAMFQx\n" "CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMw\n" "EQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYDVQQDDAZGb3NzaWwwIBcNMjExMjI3MTEz\n" "MTU2WhgPMjEyMTEyMjcxMTMxNTZaMFQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJO\n" "QzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMwEQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYD\n" "VQQDDAZGb3NzaWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCCbTU2\n" "6GRQHQqLq7vyZ0OxpAxmgfAKCxt6eIz+jBi2ZM/CB5vVXWVh2+SkSiWEA3UZiUqX\n" "xZlzmS/CglZdiwLLDJML8B4OiV72oivFH/vJ7+cbvh1dTxnYiHuww7GfQngPrLfe\n" "fiIYPDk1GTUJHBQ7Ue477F7F8vKuHdVgwktF/JDM6M60aSqlo2D/oysirrb+dlur\n" "Tlv0rjsYOfq6bLAajoL3qi/vek6DNssoywbge4PfbTgS9g7Gcgncbcet5pvaS12J\n" "avhFcd4JU4Ity49Hl9S/C2MfZ1tE53xVggRwKz4FPj65M5uymTdcxtjKXtCxIE1k\n" "KxJxXQh7rIYjm+RTAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFkdtpqcybAzJN8G\n" "+ONuUm5sXNbWta7JGvm8l0BTSBcCUtJA3hn16iJqXA9KmLnaF2denC4EYk+KlVU1\n" "QXxskPJ4jB8A5B05jMijYv0nzCxKhviI8CR7GLEEGKzeg9pbW0+O3vaVehoZtdFX\n" "z3SsCssr9QjCLiApQxMzW1Iv3od2JXeHBwfVMFrWA1VCEUCRs8OSW/VOqDPJLVEi\n" "G6wxc4kN9dLK+5S29q3nzl24/qzXoF8P9Re5KBCbrwaHgy+OEEceq5jkmfGFxXjw\n" "pvVCNry5uAhH5NqbXZampUWqiWtM4eTaIPo7Y2mDA1uWhuWtO6F9PsnFJlQHCnwy\n" "s/TsrXk=\n" "-----END CERTIFICATE-----\n"; /* This is the private-key corresponding to the cert above */ static const char sslSelfPKey[] = "-----BEGIN PRIVATE KEY-----\n" "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCCbTU26GRQHQqL\n" "q7vyZ0OxpAxmgfAKCxt6eIz+jBi2ZM/CB5vVXWVh2+SkSiWEA3UZiUqXxZlzmS/C\n" "glZdiwLLDJML8B4OiV72oivFH/vJ7+cbvh1dTxnYiHuww7GfQngPrLfefiIYPDk1\n" "GTUJHBQ7Ue477F7F8vKuHdVgwktF/JDM6M60aSqlo2D/oysirrb+dlurTlv0rjsY\n" "Ofq6bLAajoL3qi/vek6DNssoywbge4PfbTgS9g7Gcgncbcet5pvaS12JavhFcd4J\n" "U4Ity49Hl9S/C2MfZ1tE53xVggRwKz4FPj65M5uymTdcxtjKXtCxIE1kKxJxXQh7\n" "rIYjm+RTAgMBAAECggEANfTH1vc8yIe7HRzmm9lsf8jF+II4s2705y2H5qY+cvYx\n" "nKtZJGOG1X0KkYy7CGoFv5K0cSUl3lS5FVamM/yWIzoIex/Sz2C1EIL2aI5as6ez\n" "jB6SN0/J+XI8+Vt7186/rHxfdIPpxuzjHbxX3HTpScETNWcLrghbrPxakbTPPxwt\n" "+x7QlPmmkFNuMfvkzToFf9NdwL++44TeBPOpvD/Lrw+eyqdth9RJPq9cM96plh9V\n" "HuRqeD8+QNafaXBdSQs3FJK/cDK/vWGKZWIfFVSDbDhwYljkXGijreFjtXQfkkpF\n" "rl1J87/H9Ee7z8fTD2YXQHl+0/rghAVtac3u54dpQQKBgQC2XG3OEeMrOp9dNkUd\n" "F8VffUg0ecwG+9L3LCe7U71K0kPmXjV6xNnuYcNQu84kptc5vI8wD23p29LaxdNc\n" "9m0lcw06/YYBOPkNphcHkINYZTvVJF10mL3isymzMaTtwDkZUkOjL1B+MTiFT/qp\n" "ARKrTYGJ4HxY7+tUkI5pUmg4PQKBgQC3GA4d1Rz3Pb/RRpcsZgWknKsKhoN36mSn\n" "xFJ3wPBvVv2B1ltTMzh/+the0ty6clzMrvoLERzRcheDsNrc/j/TUVG8sVdBYJwX\n" "tMZyFW4NVMOErT/1ukh6jBqIMBo6NJL3EV/AKj0yniksgKOr0/AAduAccnGST8Jd\n" "SHOdjwvHzwKBgGZBq/zqgNTDuYseHGE07CMgcDWkumiMGv8ozlq3mSR0hUiPOTPP\n" "YFjQjyIdPXnF6FfiyPPtIvgIoNK2LVAqiod+XUPf152l4dnqcW13dn9BvOxGyPTR\n" "lWCikFaAFviOWjY9r9m4dU1dslDmySqthFd0TZgPvgps9ivkJ0cdw30NAoGAMC/E\n" "h1VvKiK2OP27C5ROJ+STn1GHiCfIFd81VQ8SODtMvL8NifgRBp2eFFaqgOdYRQZI\n" "CGGYlAbS6XXCJCdF5Peh62dA75PdgN+y2pOJQzjrvB9cle9Q4++7i9wdCvSLOTr5\n" "WDnFoWy+qVexu6crovOmR9ZWzYrwPFy1EOJ010ECgYBl7Q+jmjOSqsVwhFZ0U7LG\n" "diN+vXhWfn1wfOWd8u79oaqU/Oy7xyKW2p3H5z2KFrBM/vib53Lh4EwFZjcX+jVG\n" "krAmbL+M/hP7z3TD2UbESAzR/c6l7FU45xN84Lsz5npkR8H/uAHuqLgb9e430Mjx\n" "YNMwdb8rChHHChNZu6zuxw==\n" "-----END PRIVATE KEY-----\n"; /* ** Read a PEM certificate from memory and push it into an SSL_CTX. ** Return the number of errors. */ static int sslctx_use_cert_from_mem( SSL_CTX *ctx, const char *pData, int nData ){ BIO *in; int rc = 1; X509 *x = 0; X509 *cert = 0; in = BIO_new_mem_buf(pData, nData); if( in==0 ) goto end_of_ucfm; /* x = X509_new_ex(ctx->libctx, ctx->propq); */ x = X509_new(); if( x==0 ) goto end_of_ucfm; cert = PEM_read_bio_X509(in, &x, 0, 0); if( cert==0 ) goto end_of_ucfm; rc = SSL_CTX_use_certificate(ctx, x)<=0; end_of_ucfm: X509_free(x); BIO_free(in); return rc; } /* ** Read a PEM private key from memory and add it to an SSL_CTX. ** Return the number of errors. */ static int sslctx_use_pkey_from_mem( SSL_CTX *ctx, const char *pData, int nData ){ int rc = 1; BIO *in; EVP_PKEY *pkey = 0; in = BIO_new_mem_buf(pData, nData); if( in==0 ) goto end_of_upkfm; pkey = PEM_read_bio_PrivateKey(in, 0, 0, 0); if( pkey==0 ) goto end_of_upkfm; rc = SSL_CTX_use_PrivateKey(ctx, pkey)<=0; EVP_PKEY_free(pkey); end_of_upkfm: BIO_free(in); return rc; } /* ** 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); } /* ** Return the current SSL error message */ const char *ssl_errmsg(void){ return sslErrMsg; } /* ** When a server requests a client certificate that hasn't been provided, ** 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 */ } /* ** Convert an OpenSSL ASN1_TIME to an ISO8601 timestamp. ** ** Per RFC 5280, ASN1 timestamps in X.509 certificates must ** be in UTC (Zulu timezone) with no fractional seconds. ** ** If showUtc==1, add " UTC" at the end of the returned string. This is ** not ISO8601-compliant, but makes the displayed value more user-friendly. */ static const char *ssl_asn1time_to_iso8601(ASN1_TIME *asn1_time, int showUtc){ assert( showUtc==0 || showUtc==1 ); if( !ASN1_TIME_check(asn1_time) ){ return mprintf("Bad time value"); }else{ char res[20]; char *pr = res; const char *pt = (char *)asn1_time->data; /* 0123456789 1234 ** UTCTime: YYMMDDHHMMSSZ (YY >= 50 ? 19YY : 20YY) ** GeneralizedTime: YYYYMMDDHHMMSSZ */ if( asn1_time->length < 15 ){ /* UTCTime, fill out century digits */ *pr++ = pt[0]>='5' ? '1' : '2'; *pr++ = pt[0]>='5' ? '9' : '0'; }else{ /* GeneralizedTime, copy century digits and advance source */ *pr++ = pt[0]; *pr++ = pt[1]; pt += 2; } *pr++ = pt[0]; *pr++ = pt[1]; *pr++ = '-'; *pr++ = pt[2]; *pr++ = pt[3]; *pr++ = '-'; *pr++ = pt[4]; *pr++ = pt[5]; *pr++ = ' '; *pr++ = pt[6]; *pr++ = pt[7]; *pr++ = ':'; *pr++ = pt[8]; *pr++ = pt[9]; *pr++ = ':'; *pr++ = pt[10]; *pr++ = pt[11]; *pr = '\0'; return mprintf("%s%s", res, (showUtc ? " UTC" : "")); } } /* ** Call this routine once before any other use of the SSL interface. ** This routine does initial configuration of the SSL module. */ static void ssl_global_init_client(void){ const char *identityFile; if( sslIsInit==0 ){ const char *zFile; const char *zCaFile = 0; const char *zCaDirectory = 0; int i; SSL_library_init(); SSL_load_error_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); /* Find the trust store */ zFile = 0; for(i=0; zFile==0 && i<5; i++){ switch( i ){ case 0: /* First priority is environmentn variables */ zFile = fossil_getenv(X509_get_default_cert_file_env()); break; case 1: zFile = fossil_getenv(X509_get_default_cert_dir_env()); break; case 2: if( !g.repositoryOpen ) db_open_config(0,0); zFile = db_get("ssl-ca-location",0); break; case 3: zFile = X509_get_default_cert_file(); break; case 4: zFile = X509_get_default_cert_dir(); break; } if( zFile==0 ) continue; switch( file_isdir(zFile, ExtFILE) ){ case 0: { /* doesn't exist */ zFile = 0; break; } case 1: { /* directory */ zCaFile = 0; zCaDirectory = zFile; break; } case 2: { /* file */ zCaFile = zFile; zCaDirectory = 0; break; } } } if( zFile==0 ){ /* fossil_fatal("Cannot find a trust store"); */ }else if( SSL_CTX_load_verify_locations(sslCtx, zCaFile, zCaDirectory)==0 ){ fossil_fatal("Cannot load CA root certificates from %s", zFile); } /* 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); } if( identityFile!=0 && identityFile[0]!='\0' ){ if( SSL_CTX_use_certificate_chain_file(sslCtx,identityFile)!=1 || SSL_CTX_use_PrivateKey_file(sslCtx,identityFile,SSL_FILETYPE_PEM)!=1 ){ fossil_fatal("Could not load SSL identity from %s", identityFile); } } /* Register a callback to tell the user what to do when the server asks ** for a cert */ SSL_CTX_set_client_cert_cb(sslCtx, ssl_client_cert_callback); sslIsInit = 1; }else{ assert( sslIsInit==1 ); } } /* ** Call this routine to shutdown the SSL module prior to program exit. */ void ssl_global_shutdown(void){ if( sslIsInit ){ SSL_CTX_free(sslCtx); ssl_clear_errmsg(); sslIsInit = 0; } } /* ** Close the currently open client SSL connection. If no connection is open, ** this routine is a no-op. */ void ssl_close_client(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; } /* ** Invoke this routine to disable SSL cert verification. After ** this call is made, any SSL cert that the server provides will ** be accepted. Communication will still be encrypted, but the ** client has no way of knowing whether it is talking to the ** real server or a man-in-the-middle imposter. */ void ssl_disable_cert_verification(void){ sslNoCertVerify = 1; } /* ** Open an SSL connection as a client that is to connect to the server ** identified by pUrlData. ** * The identify of the server is determined as follows: ** ** pUrlData->name Name of the server. Ex: fossil-scm.org ** g.url.name Name of the proxy server, if proxying. ** pUrlData->port TCP/IP port to use. Ex: 80 ** ** Return the number of errors. */ int ssl_open_client(UrlData *pUrlData){ X509 *cert; const char *zRemoteHost; ssl_global_init_client(); if( pUrlData->useProxy ){ int rc; char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); BIO *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_client(); 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); zRemoteHost = pUrlData->hostname; }else{ iBio = BIO_new_ssl_connect(sslCtx); zRemoteHost = pUrlData->name; } 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, zRemoteHost)){ fossil_warning("WARNING: failed to set server name indication (SNI), " "continuing without it.\n"); } #endif SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); #if OPENSSL_VERSION_NUMBER >= 0x010002000 if( !sslNoCertVerify ){ X509_VERIFY_PARAM *param = 0; param = SSL_get0_param(ssl); if( !X509_VERIFY_PARAM_set1_host(param, zRemoteHost, strlen(zRemoteHost)) ){ fossil_fatal("failed to set hostname."); } /* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */ } #endif if( !pUrlData->useProxy ){ char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); BIO_set_conn_hostname(iBio, connStr); free(connStr); 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_client(); 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_client(); 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_client(); return 1; } /* Debugging hint: On unix-like system, run something like: ** ** SSL_CERT_DIR=/tmp ./fossil sync ** ** to cause certificate validation to fail, and thus test the fallback ** logic. */ if( !sslNoCertVerify && SSL_get_verify_result(ssl)!=X509_V_OK ){ int x, desclen; char *desc, *prompt; Blob ans; char cReply; BIO *mem; unsigned char md[EVP_MAX_MD_SIZE]; char zHash[EVP_MAX_MD_SIZE*2+1]; unsigned int mdLength = (int)sizeof(md); memset(md, 0, sizeof(md)); zHash[0] = 0; /* MMNNFFPPS */ #if OPENSSL_VERSION_NUMBER >= 0x010000000 x = X509_digest(cert, EVP_sha256(), md, &mdLength); #else x = X509_digest(cert, EVP_sha1(), md, &mdLength); #endif if( x ){ unsigned j; for(j=0; j<mdLength && j*2+1<sizeof(zHash); ++j){ zHash[j*2] = "0123456789abcdef"[md[j]>>4]; zHash[j*2+1] = "0123456789abcdef"[md[j]&0xf]; } zHash[j*2] = 0; } if( ssl_certificate_exception_exists(pUrlData, zHash) ){ /* Ignore the failure because an exception exists */ ssl_one_time_exception(pUrlData, zHash); }else{ /* Tell the user about the failure and ask what to do */ mem = BIO_new(BIO_s_mem()); BIO_puts(mem, " subject: "); X509_NAME_print_ex(mem, X509_get_subject_name(cert), 0, XN_FLAG_ONELINE); BIO_puts(mem, "\n issuer: "); X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE); BIO_printf(mem, "\n notBefore: %s", ssl_asn1time_to_iso8601(X509_get_notBefore(cert), 1)); BIO_printf(mem, "\n notAfter: %s", ssl_asn1time_to_iso8601(X509_get_notAfter(cert), 1)); BIO_printf(mem, "\n sha256: %s", zHash); desclen = BIO_get_mem_data(mem, &desc); prompt = mprintf("Unable to verify SSL cert from %s\n%.*s\n" "accept this cert and continue (y/N/fingerprint)? ", pUrlData->name, desclen, desc); BIO_free(mem); prompt_user(prompt, &ans); free(prompt); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y' && fossil_stricmp(blob_str(&ans),zHash)!=0 ){ X509_free(cert); ssl_set_errmsg("SSL cert declined"); ssl_close_client(); blob_reset(&ans); return 1; } blob_reset(&ans); ssl_one_time_exception(pUrlData, zHash); prompt_user("remember this exception (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply=='y' || cReply=='Y') { db_open_config(0,0); ssl_remember_certificate_exception(pUrlData, zHash); } blob_reset(&ans); } } /* 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. */ { /* As soon as libressl implements ** BIO_ADDR_hostname_string/BIO_get_conn_address. ** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable */ #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \ && !defined(LIBRESSL_VERSION_NUMBER) char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1); g.zIpAddr = mprintf("%s", ip); OPENSSL_free(ip); #else /* IPv4 only code */ const unsigned char *ip; ip = (const unsigned char*)BIO_ptr_ctrl(iBio,BIO_C_GET_CONNECT,2); g.zIpAddr = mprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); #endif } X509_free(cert); return 0; } /* ** Remember that the cert with the given hash is acceptable for ** use with pUrlData->name. */ LOCAL void ssl_remember_certificate_exception( UrlData *pUrlData, const char *zHash ){ db_set_mprintf(zHash, 1, "cert:%s", pUrlData->name); } /* ** Return true if the there exists a certificate exception for ** pUrlData->name that matches the hash. */ LOCAL int ssl_certificate_exception_exists( UrlData *pUrlData, const char *zHash ){ char *zName, *zValue; if( fossil_strcmp(sException.zHost,pUrlData->name)==0 && fossil_strcmp(sException.zHash,zHash)==0 ){ return 1; } zName = mprintf("cert:%s", pUrlData->name); zValue = db_get(zName,0); fossil_free(zName); return zValue!=0 && strcmp(zHash,zValue)==0; } /* ** Remember zHash as an acceptable certificate for this session only. */ LOCAL void ssl_one_time_exception( UrlData *pUrlData, const char *zHash ){ fossil_free(sException.zHost); sException.zHost = fossil_strdup(pUrlData->name); fossil_free(sException.zHash); sException.zHash = fossil_strdup(zHash); } /* ** Send content out over the SSL connection from the client to ** the server. */ size_t ssl_send(void *NotUsed, void *pContent, size_t N){ size_t total = 0; while( N>0 ){ int sent = BIO_write(iBio, pContent, N); if( sent<=0 ){ if( BIO_should_retry(iBio) ){ continue; } break; } total += sent; N -= sent; pContent = (void*)&((char*)pContent)[sent]; } return total; } /* ** Receive content back from the client SSL connection. In other ** words read the reply back from the server. */ size_t ssl_receive(void *NotUsed, void *pContent, size_t N){ size_t total = 0; while( N>0 ){ int got = BIO_read(iBio, pContent, N); if( got<=0 ){ if( BIO_should_retry(iBio) ){ continue; } break; } total += got; N -= got; pContent = (void*)&((char*)pContent)[got]; } return total; } /* ** Initialize the SSL library so that it is able to handle ** server-side connections. Invoke fossil_fatal() if there are ** any problems. ** ** If zKeyFile and zCertFile are not NULL, then they are the names ** of disk files that hold the certificate and private-key for the ** server. If zCertFile is not NULL but zKeyFile is NULL, then ** zCertFile is assumed to be a concatenation of the certificate and ** the private-key in the PEM format. ** ** If zCertFile is "unsafe-builtin", then a built-in self-signed cert ** is used. This built-in cert is insecure and should only be used for ** testing and debugging. */ void ssl_init_server(const char *zCertFile, const char *zKeyFile){ if( sslIsInit==0 && zCertFile ){ SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); sslCtx = SSL_CTX_new(SSLv23_server_method()); if( sslCtx==0 ){ ERR_print_errors_fp(stderr); fossil_fatal("Error initializing the SSL server"); } if( fossil_strcmp(zCertFile,"unsafe-builtin")==0 ){ if( sslctx_use_cert_from_mem(sslCtx, sslSelfCert, -1) || sslctx_use_pkey_from_mem(sslCtx, sslSelfPKey, -1) ){ fossil_fatal("Error loading self-signed CERT and KEY"); } }else{ if( SSL_CTX_use_certificate_chain_file(sslCtx,zCertFile)!=1 ){ ERR_print_errors_fp(stderr); fossil_fatal("Error loading CERT file \"%s\"", zCertFile); } if( zKeyFile==0 ) zKeyFile = zCertFile; if( SSL_CTX_use_PrivateKey_file(sslCtx, zKeyFile, SSL_FILETYPE_PEM)<=0 ){ ERR_print_errors_fp(stderr); if( strcmp(zKeyFile,zCertFile)==0 ){ fossil_fatal("The private key is not found in \"%s\". " "Either append the private key to the certification in that " "file or use a separate --pkey option to specify the private key.", zKeyFile); }else{ fossil_fatal("Error loading the private key from file \"%s\"", zKeyFile); } } } if( !SSL_CTX_check_private_key(sslCtx) ){ fossil_fatal("PRIVATE KEY \"%s\" does not match CERT \"%s\"", zKeyFile, zCertFile); } SSL_CTX_set_mode(sslCtx, SSL_MODE_AUTO_RETRY); sslIsInit = 2; }else{ assert( sslIsInit==2 ); } } typedef struct SslServerConn { SSL *ssl; /* The SSL codec */ int iSocket; /* The socket */ BIO *bio; /* BIO object. Needed for EOF detection. */ } SslServerConn; /* ** Create a new server-side codec. The argument is the socket's file ** descriptor from which the codec reads and writes. The returned ** memory must eventually be passed to ssl_close_server(). */ void *ssl_new_server(int iSocket){ SslServerConn *pServer = fossil_malloc_zero(sizeof(*pServer)); BIO *b = BIO_new_socket(iSocket, 0); pServer->ssl = SSL_new(sslCtx); pServer->iSocket = iSocket; pServer->bio = b; SSL_set_bio(pServer->ssl, b, b); SSL_accept(pServer->ssl); return (void*)pServer; } /* ** Close a server-side code previously returned from ssl_new_server(). */ void ssl_close_server(void *pServerArg){ SslServerConn *pServer = (SslServerConn*)pServerArg; SSL_free(pServer->ssl); fossil_free(pServer); } /* ** Return TRUE if there are no more bytes available to be read from ** the client. */ int ssl_eof(void *pServerArg){ SslServerConn *pServer = (SslServerConn*)pServerArg; return BIO_eof(pServer->bio); } /* ** Read cleartext bytes that have been received from the client and ** decrypted by the SSL server codec. ** ** If the expected payload size unknown, i.e. if the HTTP ** Content-Length: header field has not been parsed, the doLoop ** argument should be 0, or SSL_read() may block and wait for more ** data than is eventually going to arrive (on Windows). On ** non-Windows builds, it has been our experience that the final ** argument must always be true, as discussed at length at: ** ** https://fossil-scm.org/forum/forumpost/2f818850abb72719 */ size_t ssl_read_server(void *pServerArg, char *zBuf, size_t nBuf, int doLoop){ int n; size_t rc = 0; SslServerConn *pServer = (SslServerConn*)pServerArg; if( nBuf>0x7fffffff ){ fossil_fatal("SSL read too big"); } while( nBuf!=rc && BIO_eof(pServer->bio)==0 ){ n = SSL_read(pServer->ssl, zBuf + rc, (int)(nBuf - rc)); if( n>0 ){ rc += n; } if( doLoop==0 || n<=0 ){ break; } } return rc; } /* ** Read a single line of text from the client, up to nBuf-1 bytes. On ** success, writes nBuf-1 bytes to zBuf and NUL-terminates zBuf. ** Returns NULL on an I/O error or at EOF. */ char *ssl_gets(void *pServerArg, char *zBuf, int nBuf){ int n = 0; int i; SslServerConn *pServer = (SslServerConn*)pServerArg; if( BIO_eof(pServer->bio) ) return 0; for(i=0; i<nBuf-1; i++){ n = SSL_read(pServer->ssl, &zBuf[i], 1); if( n<=0 ){ return 0; } if( zBuf[i]=='\n' ) break; } zBuf[i+1] = 0; return zBuf; } /* ** Write cleartext bytes into the SSL server codec so that they can ** be encrypted and sent back to the client. */ size_t ssl_write_server(void *pServerArg, char *zBuf, size_t nBuf){ int n; SslServerConn *pServer = (SslServerConn*)pServerArg; if( nBuf<=0 ) return 0; if( nBuf>0x7fffffff ){ fossil_fatal("SSL write too big"); } n = SSL_write(pServer->ssl, zBuf, (int)nBuf); if( n<=0 ){ return -SSL_get_error(pServer->ssl, n); }else{ return n; } } #endif /* FOSSIL_ENABLE_SSL */ #ifdef FOSSIL_ENABLE_SSL /* ** zPath is a name that might be a file or directory containing a trust ** store. *pzStore is the name of the trust store to actually use. ** ** If *pzStore is not NULL (meaning no trust store has been found yet) ** and if zPath exists, then set *pzStore to point to zPath. */ static void trust_location_usable(const char *zPath, const char **pzStore){ if( *pzStore!=0 ) return; if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath; } #endif /* FOSSIL_ENABLE_SSL */ /* ** COMMAND: tls-config* ** COMMAND: ssl-config ** ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...] ** ** This command is used to view or modify the TLS (Transport Layer ** Security) configuration for Fossil. TLS (formerly SSL) is the ** encryption technology used for secure HTTPS transport. ** ** Sub-commands: ** ** remove-exception DOMAINS Remove TLS cert exceptions for the domains ** listed. Or remove them all if the --all ** option is specified. ** ** scrub ?--force? Remove all SSL configuration data from the ** repository. Use --force to omit the ** confirmation. ** ** show ?-v? Show the TLS configuration. Add -v to see ** additional explanation */ void test_tlsconfig_info(void){ const char *zCmd; size_t nCmd; int nHit = 0; db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); db_open_config(1,0); if( g.argc==2 || (g.argc>=3 && g.argv[2][0]=='-') ){ zCmd = "show"; nCmd = 4; }else{ zCmd = g.argv[2]; nCmd = strlen(zCmd); } if( strncmp("scrub",zCmd,nCmd)==0 && nCmd>4 ){ int bForce = find_option("force","f",0)!=0; verify_all_options(); if( !bForce ){ Blob ans; char cReply; prompt_user( "Scrubbing the SSL configuration 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); } } db_unprotect(PROTECT_ALL); db_multi_exec( "PRAGMA secure_delete=ON;" "DELETE FROM config WHERE name GLOB 'ssl-*';" ); db_protect_pop(); }else if( strncmp("show",zCmd,nCmd)==0 ){ #if defined(FOSSIL_ENABLE_SSL) const char *zName, *zValue; const char *zUsed = 0; /* Trust store location actually used */ size_t nName; #endif Stmt q; int verbose = find_option("verbose","v",0)!=0; verify_all_options(); #if !defined(FOSSIL_ENABLE_SSL) fossil_print("OpenSSL-version: (none)\n"); if( verbose ){ fossil_print("\n" " The OpenSSL library is not used by this build of Fossil\n\n" ); } #else fossil_print("OpenSSL-version: %s (0x%09x)\n", SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER); if( verbose ){ fossil_print("\n" " The version of the OpenSSL library being used\n" " by this instance of Fossil. Version 3.0.0 or\n" " later is recommended.\n\n" ); } fossil_print("Trust store location\n"); zName = X509_get_default_cert_file_env(); zValue = fossil_getenv(zName); if( zValue==0 ) zValue = ""; trust_location_usable(zValue, &zUsed); nName = strlen(zName); fossil_print(" %s:%*s%s\n", zName, 19-nName, "", zValue); zName = X509_get_default_cert_dir_env(); zValue = fossil_getenv(zName); if( zValue==0 ) zValue = ""; trust_location_usable(zValue, &zUsed); nName = strlen(zName); fossil_print(" %s:%*s%s\n", zName, 19-nName, "", zValue); if( verbose ){ fossil_print("\n" " Environment variables that determine alternative locations for\n" " the root certificates used by Fossil when it is acting as a SSL\n" " client. If specified, these alternative locations take top\n" " priority.\n\n" ); } zValue = db_get("ssl-ca-location",""); trust_location_usable(zValue, &zUsed); fossil_print(" ssl-ca-location: %s\n", zValue); if( verbose ){ fossil_print("\n" " This setting is the name of a file or directory that contains\n" " the complete set of root certificates used by Fossil when it\n" " is acting as a SSL client. If defined, this setting takes\n" " priority over built-in paths.\n\n" ); } zValue = X509_get_default_cert_file(); trust_location_usable(zValue, &zUsed); fossil_print(" OpenSSL-cert-file: %s\n", zValue); zValue = X509_get_default_cert_dir(); trust_location_usable(zValue, &zUsed); fossil_print(" OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir()); if( verbose ){ fossil_print("\n" " The default locations for the set of root certificates\n" " used by the \"fossil sync\" and similar commands to verify\n" " the identity of servers for \"https:\" URLs. These values\n" " come into play when Fossil is used as a TLS client. These\n" " values are built into your OpenSSL library.\n\n" ); } if( zUsed==0 ) zUsed = ""; fossil_print(" Trust store used: %s\n", zUsed); if( verbose ){ fossil_print("\n" " The location that is actually used for the root certificates\n" " used to verify the identity of servers for \"https:\" URLs.\n" " This will be one of the first of the five locations listed\n" " above that actually exists.\n\n" ); } #endif /* FOSSIL_ENABLE_SSL */ fossil_print("ssl-identity: %s\n", db_get("ssl-identity","")); if( verbose ){ fossil_print("\n" " This setting is the name of a file that contains the PEM-format\n" " certificate and private-key used by Fossil clients to authenticate\n" " with servers. Few servers actually require this, so this setting\n" " is usually blank.\n\n" ); } db_prepare(&q, "SELECT name, '', value FROM global_config" " WHERE name GLOB 'cert:*'" "UNION ALL " "SELECT name, date(mtime,'unixepoch'), value FROM config" " WHERE name GLOB 'cert:*'" " ORDER BY name" ); nHit = 0; while( db_step(&q)==SQLITE_ROW ){ /* 123456789 123456789 123456789 */ if( verbose ){ fossil_print("exception: %-40s %s\n" " hash: %.57s\n", db_column_text(&q,0)+5, db_column_text(&q,1), db_column_text(&q,2)); }else{ fossil_print("exception: %-40s %s\n", db_column_text(&q,0)+5, db_column_text(&q,1)); } nHit++; } db_finalize(&q); if( nHit && verbose ){ fossil_print("\n" " The exceptions are server certificates that the Fossil client\n" " is unable to verify using root certificates, but which should be\n" " accepted anyhow.\n\n" ); } }else if( strncmp("remove-exception",zCmd,nCmd)==0 ){ int i; Blob sql; char *zSep = "("; db_begin_transaction(); blob_init(&sql, 0, 0); if( g.argc==4 && find_option("all",0,0)!=0 ){ blob_append_sql(&sql, "DELETE FROM global_config WHERE name GLOB 'cert:*';\n" "DELETE FROM global_config WHERE name GLOB 'trusted:*';\n" "DELETE FROM config WHERE name GLOB 'cert:*';\n" "DELETE FROM config WHERE name GLOB 'trusted:*';\n" ); }else{ if( g.argc<4 ){ usage("remove-exception DOMAIN-NAME ..."); } blob_append_sql(&sql,"DELETE FROM global_config WHERE name IN "); for(i=3; i<g.argc; i++){ blob_append_sql(&sql,"%s'cert:%q','trust:%q'", zSep/*safe-for-%s*/, g.argv[i], g.argv[i]); zSep = ","; } blob_append_sql(&sql,");\n"); zSep = "("; blob_append_sql(&sql,"DELETE FROM config WHERE name IN "); for(i=3; i<g.argc; i++){ blob_append_sql(&sql,"%s'cert:%q','trusted:%q'", zSep/*safe-for-%s*/, g.argv[i], g.argv[i]); zSep = ","; } blob_append_sql(&sql,");"); } db_unprotect(PROTECT_CONFIG); db_exec_sql(blob_str(&sql)); db_protect_pop(); db_commit_transaction(); blob_reset(&sql); }else /*default*/{ fossil_fatal("unknown sub-command \"%s\".\nshould be one of:" " remove-exception scrub show", zCmd); } } /* ** WEBPAGE: .well-known ** ** If the "--acme" option was supplied to "fossil server" or "fossil http" or ** similar, then this page returns the content of files found in the ** ".well-known" subdirectory of the same directory that contains the ** repository file. This facilitates Automated Certificate ** Management using tools like "certbot". ** ** The content is returned directly, without any interpretation, using ** a generic mimetype. */ void wellknown_page(void){ char *zPath = 0; const char *zTail = P("name"); Blob content; int i; char c; if( !g.fAllowACME ) goto wellknown_notfound; if( g.zRepositoryName==0 ) goto wellknown_notfound; if( zTail==0 ) goto wellknown_notfound; zPath = mprintf("%z/.well-known/%s", file_dirname(g.zRepositoryName), zTail); for(i=0; (c = zTail[i])!=0; i++){ if( fossil_isalnum(c) ) continue; if( c=='.' ){ if( i==0 || zTail[i-1]=='/' || zTail[i-1]=='.' ) goto wellknown_notfound; continue; } if( c==',' || c!='-' || c=='/' || c==':' || c=='_' || c=='~' ) continue; goto wellknown_notfound; } if( strstr("/..", zPath)!=0 ) goto wellknown_notfound; if( !file_isfile(zPath, ExtFILE) ) goto wellknown_notfound; blob_read_from_file(&content, zPath, ExtFILE); cgi_set_content(&content); cgi_set_content_type(mimetype_from_name(zPath)); cgi_reply(); return; wellknown_notfound: fossil_free(zPath); webpage_notfound_error(0 /*works-like:""*/); return; } /* ** Return the OpenSSL version number being used. Space to hold ** this name is obtained from fossil_malloc() and should be ** freed by the caller. */ char *fossil_openssl_version(void){ #if defined(FOSSIL_ENABLE_SSL) return mprintf("%s (0x%09x)\n", SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER); #else return mprintf("none"); #endif } |
Changes to src/http_transport.c.
︙ | ︙ | |||
28 29 30 31 32 33 34 | */ static struct { int isOpen; /* True when the transport layer is open */ char *pBuf; /* Buffer used to hold the reply */ int nAlloc; /* Space allocated for transportBuf[] */ int nUsed ; /* Space of transportBuf[] used */ int iCursor; /* Next unread by in transportBuf[] */ | | | > | > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > > > | | | | | | < < > > > | < | < | | | > > > > > > | | | | | | | | | > > > | | | | | | | | | > | > | | > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | < < < < < < < < < < < < < > | | < | < | > | | | | > > > | > > > | > > > > > > > > > > > > | 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 | */ static struct { int isOpen; /* True when the transport layer is open */ char *pBuf; /* Buffer used to hold the reply */ int nAlloc; /* Space allocated for transportBuf[] */ int nUsed ; /* Space of transportBuf[] used */ int iCursor; /* Next unread by in transportBuf[] */ i64 nSent; /* Number of bytes sent */ i64 nRcvd; /* Number of bytes received */ FILE *pFile; /* File I/O for FILE: */ char *zOutFile; /* Name of outbound file for FILE: */ char *zInFile; /* Name of inbound file for FILE: */ FILE *pLog; /* Log output here */ } transport = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* ** Information about the connection to the SSH subprocess when ** using the ssh:// sync method. */ static int sshPid; /* Process id of ssh subprocess */ 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; } } /* ** Check zFossil to see if it is a reasonable "fossil" command to ** run on the server. Do not allow an attacker to substitute something ** like "/bin/rm". */ static int is_safe_fossil_command(const char *zFossil){ static const char *const azSafe[] = { "*/fossil", "*/fossil.exe", "*/echo" }; int i; for(i=0; i<(int)(sizeof(azSafe)/sizeof(azSafe[0])); i++){ if( sqlite3_strglob(azSafe[i], zFossil)==0 ) return 1; if( strcmp(azSafe[i]+2, zFossil)==0 ) return 1; } return 0; } /* ** Default SSH command */ #if 0 /* was: defined(_WIN32). Windows generally has ssh now. */ static const char zDefaultSshCmd[] = "plink -ssh"; #else static const char zDefaultSshCmd[] = "ssh -e none"; #endif /* ** Initialize a Blob to the name of the configured SSH command. */ void transport_ssh_command(Blob *p){ char *zSsh; /* The base SSH command */ zSsh = db_get("ssh-command", zDefaultSshCmd); blob_init(p, zSsh, -1); } /* ** 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. */ Blob zCmd; /* The SSH command */ char *zHost; /* The host name to contact */ fossil_free(g.zIpAddr); g.zIpAddr = mprintf("%s", pUrlData->name); transport_ssh_command(&zCmd); if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){ blob_appendf(&zCmd, " -p %d", pUrlData->port); } blob_appendf(&zCmd, " -T --"); /* End of switches */ if( pUrlData->user && pUrlData->user[0] ){ zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); blob_append_escaped_arg(&zCmd, zHost, 0); fossil_free(zHost); }else{ blob_append_escaped_arg(&zCmd, pUrlData->name, 0); } if( (pUrlData->flags & URL_SSH_EXE)!=0 && !is_safe_fossil_command(pUrlData->fossil) ){ fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on " "the server.", pUrlData->fossil); } if( (pUrlData->flags & URL_SSH_EXE)==0 && (pUrlData->flags & URL_SSH_PATH)!=0 ){ ssh_add_path_argument(&zCmd); } blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1); blob_append(&zCmd, " test-http", 10); if( pUrlData->path && pUrlData->path[0] ){ blob_append_escaped_arg(&zCmd, pUrlData->path, 1); }else{ fossil_fatal("ssh:// URI does not specify a path to the repository"); } if( g.fSshTrace || g.fHttpTrace ){ fossil_print("RUN %s\n", blob_str(&zCmd)); /* Show the whole SSH command */ } popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid, 0); 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: 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_client(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 ){ if( !db_looks_like_a_repository(pUrlData->name) ){ fossil_fatal("not a fossil repository: \"%s\"", pUrlData->name); } transport.zOutFile = fossil_temp_filename(); transport.zInFile = fossil_temp_filename(); 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_client(); #endif }else if( pUrlData->isFile ){ if( transport.pFile ){ fclose(transport.pFile); transport.pFile = 0; } file_delete(transport.zInFile); file_delete(transport.zOutFile); sqlite3_free(transport.zInFile); sqlite3_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 begin receiving a reply. */ void transport_flip(UrlData *pUrlData){ if( pUrlData->isFile ){ char *zCmd; fclose(transport.pFile); zCmd = mprintf("%$ http --in %$ --out %$ --ipaddr 127.0.0.1" " %$ --localauth", g.nameOfExe, transport.zOutFile, transport.zInFile, pUrlData->name ); if( g.fHttpTrace ) fossil_print("RUN %s\n", zCmd); fossil_system(zCmd); free(zCmd); transport.pFile = fossil_fopen(transport.zInFile, "rb"); } } /* ** Log all input to a file. The transport layer will take responsibility ** for closing the log file when it is done. */ void transport_log(FILE *pLog){ if( transport.pLog ){ fclose(transport.pLog); transport.pLog = 0; } 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( pUrlData->isSsh ){ 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, 0); } /* 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); } if( onHand>0 ){ int toMove = onHand; if( toMove>N ) toMove = N; /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); transport.iCursor += toMove; if( transport.iCursor>=transport.nUsed ){ transport.nUsed = 0; 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; } if( transport.iCursor>0 ){ for(i=0, j=transport.iCursor; j<transport.nUsed; i++, j++){ transport.pBuf[i] = transport.pBuf[j]; } transport.nUsed -= transport.iCursor; transport.iCursor = 0; } 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; } } if( transport.pBuf[i]=='\n' ){ transport.iCursor = i+1; while( i>=iStart && fossil_isspace(transport.pBuf[i]) ){ transport.pBuf[i] = 0; i--; } break; } 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; } } |
Added src/import.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 | /* ** Copyright (c) 2010 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 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 /* ** A single file change record. */ struct ImportFile { char *zName; /* Name of a file */ char *zUuid; /* Hash of the file */ char *zPrior; /* Prior name if the name was changed */ char isFrom; /* True if obtained from the parent */ char isExe; /* True if executable */ char isLink; /* True if symlink */ }; #endif /* ** State information common to all import types. */ static struct { const char *zTrunkName; /* Name of trunk branch */ const char *zBranchPre; /* Prepended to non-trunk branch names */ const char *zBranchSuf; /* Appended to non-trunk branch names */ const char *zTagPre; /* Prepended to non-trunk tag names */ const char *zTagSuf; /* Appended to non-trunk tag names */ } gimport; /* ** State information about an on-going fast-import parse. */ static struct { void (*xFinish)(void); /* Function to finish a prior record */ int nData; /* Bytes of data */ char *zTag; /* Name of a tag */ char *zBranch; /* Name of a branch for a commit */ char *zPrevBranch; /* The branch of the previous check-in */ char *aData; /* Data content */ char *zMark; /* The current mark */ char *zDate; /* Date/time stamp */ char *zUser; /* User name */ char *zComment; /* Comment of a commit */ char *zFrom; /* from value as a hash */ char *zPrevCheckin; /* Name of the previous check-in */ char *zFromMark; /* The mark of the "from" field */ int nMerge; /* Number of merge values */ int nMergeAlloc; /* Number of slots in azMerge[] */ char **azMerge; /* Merge values */ int nFile; /* Number of aFile values */ int nFileAlloc; /* Number of slots in aFile[] */ ImportFile *aFile; /* Information about files in a commit */ ImportFile *pInlineFile; /* File marked "inline" */ int fromLoaded; /* True zFrom content loaded into aFile[] */ int tagCommit; /* True if the commit adds a tag */ } gg; /* ** Duplicate a string. */ char *fossil_strndup(const char *zOrig, int len){ char *z = 0; if( zOrig ){ int n; if( len<0 ){ n = strlen(zOrig); }else{ for( n=0; zOrig[n] && n<len; ++n ); } z = fossil_malloc( n+1 ); memcpy(z, zOrig, n); z[n] = 0; } return z; } char *fossil_strdup(const char *zOrig){ return fossil_strndup(zOrig, -1); } /* ** A no-op "xFinish" method */ static void finish_noop(void){} /* ** Deallocate the state information. ** ** The azMerge[] and aFile[] arrays are zeroed by allocated space is ** retained unless the freeAll flag is set. */ static void import_reset(int freeAll){ int i; gg.xFinish = 0; fossil_free(gg.zTag); gg.zTag = 0; fossil_free(gg.zBranch); gg.zBranch = 0; fossil_free(gg.aData); gg.aData = 0; fossil_free(gg.zMark); gg.zMark = 0; fossil_free(gg.zDate); gg.zDate = 0; fossil_free(gg.zUser); gg.zUser = 0; fossil_free(gg.zComment); gg.zComment = 0; fossil_free(gg.zFrom); gg.zFrom = 0; fossil_free(gg.zFromMark); gg.zFromMark = 0; for(i=0; i<gg.nMerge; i++){ fossil_free(gg.azMerge[i]); gg.azMerge[i] = 0; } gg.nMerge = 0; for(i=0; i<gg.nFile; i++){ fossil_free(gg.aFile[i].zName); fossil_free(gg.aFile[i].zUuid); fossil_free(gg.aFile[i].zPrior); } memset(gg.aFile, 0, gg.nFile*sizeof(gg.aFile[0])); gg.nFile = 0; if( freeAll ){ fossil_free(gg.zPrevBranch); fossil_free(gg.zPrevCheckin); fossil_free(gg.azMerge); fossil_free(gg.aFile); memset(&gg, 0, sizeof(gg)); } gg.xFinish = finish_noop; } /* ** Insert an artifact into the BLOB table if it isn't there already. ** If zMark is not zero, create a cross-reference from that mark back ** to the newly inserted artifact. ** ** If saveHash is true, then pContent is a commit record. Record its ** artifact hash in gg.zPrevCheckin. */ static int fast_insert_content( Blob *pContent, /* Content to insert */ const char *zMark, /* Label using this mark, if not NULL */ ImportFile *pFile, /* Save hash on this file, if not NULL */ int saveHash, /* Save artifact hash in gg.zPrevCheckin */ int doParse /* Invoke manifest_crosslink() */ ){ Blob hash; Blob cmpr; int rid; hname_hash(pContent, 0, &hash); rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &hash); if( rid==0 ){ static Stmt ins; assert( g.rcvid>0 ); db_static_prepare(&ins, "INSERT INTO blob(uuid, size, rcvid, content)" "VALUES(:uuid, :size, %d, :content)", g.rcvid ); db_bind_text(&ins, ":uuid", blob_str(&hash)); db_bind_int(&ins, ":size", gg.nData); blob_compress(pContent, &cmpr); db_bind_blob(&ins, ":content", &cmpr); db_step(&ins); db_reset(&ins); blob_reset(&cmpr); rid = db_last_insert_rowid(); if( doParse ){ manifest_crosslink(rid, pContent, MC_NONE); } } if( zMark ){ db_multi_exec( "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" "VALUES(%Q,%d,%B)", zMark, rid, &hash ); db_multi_exec( "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" "VALUES(%B,%d,%B)", &hash, rid, &hash ); } if( saveHash ){ fossil_free(gg.zPrevCheckin); gg.zPrevCheckin = fossil_strdup(blob_str(&hash)); } if( pFile ){ fossil_free(pFile->zUuid); pFile->zUuid = fossil_strdup(blob_str(&hash)); } blob_reset(&hash); return rid; } /* ** Check to ensure the file in gg.aData,gg.nData is not a control ** artifact. Then add the file to the repository. */ static void check_and_add_file(const char *zMark, ImportFile *pFile){ Blob content; blob_init(&content, gg.aData, gg.nData); if( gg.nData && manifest_is_well_formed(gg.aData, gg.nData) ){ sterilize_manifest(&content, -1); } fast_insert_content(&content, zMark, pFile, 0, 0); blob_reset(&content); } /* ** Use data accumulated in gg from a "blob" record to add a new file ** to the BLOB table. */ static void finish_blob(void){ check_and_add_file(gg.zMark, 0); import_reset(0); } /* ** Use data accumulated in gg from a "tag" record to add a new ** control artifact to the BLOB table. */ static void finish_tag(void){ if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ Blob record, cksum; blob_zero(&record); blob_appendf(&record, "D %s\n", gg.zDate); blob_appendf(&record, "T +sym-%F%F%F %s", gimport.zTagPre, gg.zTag, gimport.zTagSuf, gg.zFrom); if( gg.zComment ){ blob_appendf(&record, " %F", gg.zComment); } blob_appendf(&record, "\nU %F\n", gg.zUser); md5sum_blob(&record, &cksum); blob_appendf(&record, "Z %b\n", &cksum); fast_insert_content(&record, 0, 0, 0, 1); blob_reset(&cksum); blob_reset(&record); } import_reset(0); } /* ** Compare two ImportFile objects for sorting */ static int mfile_cmp(const void *pLeft, const void *pRight){ const ImportFile *pA = (const ImportFile*)pLeft; const ImportFile *pB = (const ImportFile*)pRight; 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); /* ** Use data accumulated in gg from a "commit" record to add a new ** manifest artifact to the BLOB table. */ static void finish_commit(void){ int i; char *zFromBranch; char *aTCard[4]; /* Array of T cards for manifest */ int nTCard = 0; /* Entries used in aTCard[] */ Blob record, cksum; import_prior_files(); qsort(gg.aFile, gg.nFile, sizeof(gg.aFile[0]), mfile_cmp); blob_zero(&record); blob_appendf(&record, "C %F\n", gg.zComment); blob_appendf(&record, "D %s\n", gg.zDate); if( !g.fQuiet ){ fossil_print("%.10s\r", gg.zDate); fflush(stdout); } for(i=0; i<gg.nFile; i++){ const char *zUuid = gg.aFile[i].zUuid; if( zUuid==0 ) continue; blob_appendf(&record, "F %F %s", gg.aFile[i].zName, zUuid); if( gg.aFile[i].isExe ){ blob_append(&record, " x", 2); }else if( gg.aFile[i].isLink ){ blob_append(&record, " l", 2); }else{ blob_append(&record, " w", 2); } if( gg.aFile[i].zPrior!=0 ){ blob_appendf(&record, " %F", gg.aFile[i].zPrior); } blob_append(&record, "\n", 1); } if( gg.zFrom ){ blob_appendf(&record, "P %s", gg.zFrom); for(i=0; i<gg.nMerge; i++){ blob_appendf(&record, " %s", gg.azMerge[i]); } blob_append(&record, "\n", 1); zFromBranch = db_text(0, "SELECT brnm FROM xbranch WHERE tname=%Q", gg.zFromMark); }else{ zFromBranch = 0; } /* Add the required "T" cards to the manifest. Make sure they are added ** in sorted order and without any duplicates. Otherwise, fossil will not ** recognize the document as a valid manifest. */ if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){ aTCard[nTCard++] = mprintf("T *branch * %F%F%F\n", gimport.zBranchPre, gg.zBranch, gimport.zBranchSuf); aTCard[nTCard++] = mprintf("T *sym-%F%F%F *\n", gimport.zBranchPre, gg.zBranch, gimport.zBranchSuf); if( zFromBranch ){ aTCard[nTCard++] = mprintf("T -sym-%F%F%F *\n", gimport.zBranchPre, zFromBranch, gimport.zBranchSuf); } } if( gg.zFrom==0 ){ aTCard[nTCard++] = mprintf("T *sym-%F *\n", gimport.zTrunkName); } qsort(aTCard, nTCard, sizeof(char *), string_cmp); for(i=0; i<nTCard; i++){ if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){ blob_appendf(&record, "%s", aTCard[i]); } } for(i=0; i<nTCard; i++) free(aTCard[i]); free(zFromBranch); db_multi_exec("INSERT INTO xbranch(tname, brnm) VALUES(%Q,%Q)", gg.zMark, gg.zBranch); blob_appendf(&record, "U %F\n", gg.zUser); md5sum_blob(&record, &cksum); blob_appendf(&record, "Z %b\n", &cksum); fast_insert_content(&record, gg.zMark, 0, 1, 1); blob_reset(&cksum); /* The "git fast-export" command might output multiple "commit" lines ** that reference a tag using "refs/tags/TAGNAME". The tag should only ** be applied to the last commit that is output. The problem is we do not ** know at this time if the current commit is the last one to hold this ** tag or not. So make an entry in the XTAG table to record this tag ** but overwrite that entry if a later instance of the same tag appears. ** ** This behavior seems like a bug in git-fast-export, but it is easier ** to work around the problem than to fix git-fast-export. */ if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){ record.nUsed = 0 /*in case fast_insert_comment() did not indirectly blob_reset() it */; blob_appendf(&record, "D %s\n", gg.zDate); blob_appendf(&record, "T +sym-%F%F%F %s\n", gimport.zBranchPre, gg.zBranch, gimport.zBranchSuf, gg.zPrevCheckin); blob_appendf(&record, "U %F\n", gg.zUser); md5sum_blob(&record, &cksum); blob_appendf(&record, "Z %b\n", &cksum); db_multi_exec( "INSERT OR REPLACE INTO xtag(tname, tcontent)" " VALUES(%Q,%Q)", gg.zBranch, blob_str(&record) ); blob_reset(&cksum); } blob_reset(&record); fossil_free(gg.zPrevBranch); gg.zPrevBranch = gg.zBranch; gg.zBranch = 0; import_reset(0); } /* ** Turn the first \n in the input string into a \000 */ static void trim_newline(char *z){ while( z[0] && z[0]!='\n' ){ z++; } z[0] = 0; } /* ** Get a token from a line of text. Return a pointer to the first ** character of the token and zero-terminate the token. Make ** *pzIn point to the first character past the end of the zero ** terminator, or at the zero-terminator at EOL. */ static char *next_token(char **pzIn){ char *z = *pzIn; int i; if( z[0]==0 ) return z; for(i=0; z[i] && z[i]!=' ' && z[i]!='\n'; i++){} if( z[i] ){ z[i] = 0; *pzIn = &z[i+1]; }else{ *pzIn = &z[i]; } return z; } /* ** Return a token that is all text up to (but omitting) the next \n ** or \r\n. */ static char *rest_of_line(char **pzIn){ char *z = *pzIn; int i; if( z[0]==0 ) return z; for(i=0; z[i] && z[i]!='\r' && z[i]!='\n'; i++){} if( z[i] ){ if( z[i]=='\r' && z[i+1]=='\n' ){ z[i] = 0; i++; }else{ z[i] = 0; } *pzIn = &z[i+1]; }else{ *pzIn = &z[i]; } return z; } /* ** Convert a "mark" or "committish" into the artifact hash. */ static char *resolve_committish(const char *zCommittish){ char *zRes; zRes = db_text(0, "SELECT tuuid FROM xmark WHERE tname=%Q", zCommittish); return zRes; } /* ** Create a new entry in the gg.aFile[] array */ static ImportFile *import_add_file(void){ ImportFile *pFile; if( gg.nFile>=gg.nFileAlloc ){ gg.nFileAlloc = gg.nFileAlloc*2 + 100; gg.aFile = fossil_realloc(gg.aFile, gg.nFileAlloc*sizeof(gg.aFile[0])); } pFile = &gg.aFile[gg.nFile++]; memset(pFile, 0, sizeof(*pFile)); return pFile; } /* ** Load all file information out of the gg.zFrom check-in */ static void import_prior_files(void){ Manifest *p; int rid; ManifestFile *pOld; ImportFile *pNew; if( gg.fromLoaded ) return; gg.fromLoaded = 1; if( gg.zFrom==0 && gg.zPrevCheckin!=0 && fossil_strcmp(gg.zBranch, gg.zPrevBranch)==0 ){ gg.zFrom = gg.zPrevCheckin; gg.zPrevCheckin = 0; } if( gg.zFrom==0 ) return; rid = fast_uuid_to_rid(gg.zFrom); if( rid==0 ) return; p = manifest_get(rid, CFTYPE_MANIFEST, 0); if( p==0 ) return; manifest_file_rewind(p); while( (pOld = manifest_file_next(p, 0))!=0 ){ pNew = import_add_file(); pNew->zName = fossil_strdup(pOld->zName); pNew->isExe = pOld->zPerm && strstr(pOld->zPerm, "x")!=0; pNew->isLink = pOld->zPerm && strstr(pOld->zPerm, "l")!=0; pNew->zUuid = fossil_strdup(pOld->zUuid); pNew->isFrom = 1; } manifest_destroy(p); } /* ** Locate a file in the gg.aFile[] array by its name. Begin the search ** with the *pI-th file. Update *pI to be one past the file found. ** 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; } /* ** Dequote a fast-export filename. Filenames are normally unquoted. But ** if the contain some obscure special characters, quotes might be added. */ static void dequote_git_filename(char *zName){ int n, i, j; if( zName==0 || zName[0]!='"' ) return; n = (int)strlen(zName); if( zName[n-1]!='"' ) return; for(i=0, j=1; j<n-1; j++){ char c = zName[j]; int x; if( c=='\\' ){ if( j+3 <= n-1 && zName[j+1]>='0' && zName[j+1]<='3' && zName[j+2]>='0' && zName[j+2]<='7' && zName[j+3]>='0' && zName[j+3]<='7' && (x = 64*(zName[j+1]-'0') + 8*(zName[j+2]-'0') + zName[j+3]-'0')!=0 ){ c = (unsigned char)x; j += 3; }else{ c = zName[++j]; } } zName[i++] = c; } zName[i] = 0; } static struct{ const char *zMasterName; /* Name of master branch */ int authorFlag; /* Use author as check-in committer */ int nGitAttr; /* Number of Git --attribute entries */ struct { /* Git --attribute details */ char *zUser; char *zEmail; } *gitUserInfo; } ggit; /* ** Read the git-fast-import format from pIn and insert the corresponding ** content into the database. */ static void git_fast_import(FILE *pIn){ ImportFile *pFile, *pNew; int i, mx; char *z; char *zUuid; char *zName; char *zPerm; 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 ){ const char *zRefName; gg.xFinish(); gg.xFinish = finish_commit; trim_newline(&zLine[7]); zRefName = &zLine[7]; /* The argument to the "commit" line might match either of these ** patterns: ** ** (A) refs/heads/BRANCHNAME ** (B) refs/tags/TAGNAME ** ** If pattern A is used, then the branchname used is as shown. ** Except, the "master" branch which is the default branch name in ** Git is changed to "trunk" which is the default name in Fossil. ** If the pattern is B, then the new commit should be on the same ** branch as its parent. And, we might need to add the TAGNAME ** 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<(int)strlen(zRefName) && zRefName[i]!='/'; i++){} gg.tagCommit = strncmp(&zRefName[5], "tags", 4)==0; /* pattern B */ if( zRefName[i+1]!=0 ) zRefName += i+1; if( fossil_strcmp(zRefName, "master")==0 ) zRefName = ggit.zMasterName; gg.zBranch = fossil_strdup(zRefName); 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 ", 6)==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.xFinish==finish_tag) ){ /* Strip trailing newline, it's appended to the comment. */ if( gg.aData[got-1] == '\n' ) gg.aData[got-1] = '\0'; gg.zComment = gg.aData; gg.aData = 0; gg.nData = 0; } } if( gg.pInlineFile ){ check_and_add_file(0, gg.pInlineFile); gg.pInlineFile = 0; } }else if( (!ggit.authorFlag && strncmp(zLine, "author ", 7)==0) || (ggit.authorFlag && strncmp(zLine, "committer ",10)==0 && gg.zUser!=NULL) ){ /* 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 || (ggit.authorFlag && strncmp(zLine, "author ", 7)==0) || strncmp(zLine, "committer ",10)==0 ){ sqlite3_int64 secSince1970; z = strchr(zLine, ' '); while( fossil_isspace(*z) ) z++; if( (zTo=strchr(z, '>'))==NULL ) goto malformed_line; *(++zTo) = '\0'; /* ** If --attribute requested, lookup user in fx_ table by email address, ** otherwise lookup Git {author,committer} contact info in user table. If ** no matches, use email address as username for check-in attribution. */ fossil_free(gg.zUser); gg.zUser = db_text(0, "SELECT login FROM user WHERE info=%Q", z); if( gg.zUser==NULL ){ /* If there is no user with this contact info, * then use the email address as the username. */ if ( (z=strchr(z, '<'))==NULL ) goto malformed_line; z++; *(zTo-1) = '\0'; gg.zUser = fossil_strdup(z); } if (ggit.nGitAttr > 0 || db_table_exists("repository", "fx_git")) { gg.zUser = db_text(gg.zUser, "SELECT user FROM fx_git WHERE email=%Q", z); } secSince1970 = 0; for(zTo++; fossil_isdigit(*zTo); zTo++){ secSince1970 = secSince1970*10 + *zTo - '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 = (sqlite3_strglob("*755",zPerm)==0); pFile->isLink = (fossil_strcmp(zPerm, "120000")==0); fossil_free(pFile->zUuid); if( strcmp(zUuid,"inline")==0 ){ pFile->zUuid = 0; gg.pInlineFile = pFile; }else{ 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; nFrom = strlen(zFrom); while( (pFile = import_find_file(zFrom, &i, mx))!=0 ){ if( pFile->isFrom==0 ) continue; pNew = import_add_file(); pFile = &gg.aFile[i-1]; if( (int)strlen(pFile->zName)>nFrom ){ pNew->zName = mprintf("%s%s", zTo, pFile->zName+nFrom); }else{ pNew->zName = fossil_strdup(zTo); } 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); while( (pFile = import_find_file(zFrom, &i, gg.nFile))!=0 ){ if( pFile->isFrom==0 ) continue; pNew = import_add_file(); pFile = &gg.aFile[i-1]; if( (int)strlen(pFile->zName)>nFrom ){ pNew->zName = mprintf("%s%s", zTo, pFile->zName+nFrom); }else{ pNew->zName = fossil_strdup(zTo); } pNew->zPrior = pFile->zName; pNew->isExe = pFile->isExe; pNew->isLink = pFile->isLink; pNew->zUuid = pFile->zUuid; pNew->isFrom = 0; gg.nFile--; *pFile = *pNew; memset(pNew, 0, sizeof(*pNew)); } }else if( strncmp(zLine, "deleteall", 9)==0 ){ gg.fromLoaded = 1; }else if( strncmp(zLine, "N ", 2)==0 ){ /* No-op */ }else if( strncmp(zLine, "property branch-nick ", 21)==0 ){ /* Breezy uses this property to store the branch name. ** It has two values. Integer branch number, then the ** user-readable branch name. */ z = &zLine[21]; next_token(&z); fossil_free(gg.zBranch); gg.zBranch = fossil_strdup(next_token(&z)); }else if( strncmp(zLine, "property rebase-of ", 19)==0 ){ /* Breezy uses this property to record that a branch ** was rebased. Silently ignore it. */ }else { goto malformed_line; } } gg.xFinish(); 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 revFlag; /* Add svn-rev-nn tags on every check-in */ const char *zRevPre; /* Prepended to revision tag names */ const char *zRevSuf; /* Appended to revision tag names */ const char **azIgnTree; /* NULL-terminated list of dirs to ignore */ } 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( (int)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); if( pParentManifest ){ 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%F%F\n", gimport.zBranchPre, zBranch, gimport.zBranchSuf); blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre, zBranch, gimport.zBranchSuf); if( gsvn.revFlag ){ blob_appendf(&manifest, "T +sym-%Fr%d%F *\n", gimport.zTagPre, gsvn.rev, gimport.zTagSuf); } blob_appendf(&manifest, "T -sym-%F%F%F *\n", gimport.zBranchPre, zParentBranch, gimport.zBranchSuf); fossil_free(zParentBranch); }else{ char *zMergeUuid = rid_to_uuid(mergeRid); blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid); if( gsvn.revFlag ){ blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, gsvn.rev, gsvn.zRevSuf); } fossil_free(zMergeUuid); } fossil_free(zParentUuid); }else{ blob_appendf(&manifest, "T *branch * %F%F%F\n", gimport.zBranchPre, zBranch, gimport.zBranchSuf); blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre, zBranch, gimport.zBranchSuf); if( gsvn.revFlag ){ blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, gsvn.rev, gsvn.zRevSuf); } } }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%F%F %s\n", gimport.zTagPre, zBranch, gimport.zTagSuf, 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%F%F %s\n", gimport.zBranchPre, zBranch, gimport.zBranchSuf, 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. ** Return 0 if not a branch, tag, or trunk, or if ignored by --ignore-tree. */ static int svn_parse_path(char *zPath, char **zFile, int *type){ char *zBranch = 0; int branchId = 0; if( gsvn.azIgnTree ){ const char **pzIgnTree; unsigned nPath = strlen(zPath); for( pzIgnTree = gsvn.azIgnTree; *pzIgnTree; ++pzIgnTree ){ const char *zIgn = *pzIgnTree; unsigned nIgn = strlen(zIgn); if( strncmp(zPath, zIgn, nIgn) == 0 && ( nPath == nIgn || (nPath > nIgn && zPath[nIgn] == '/')) ){ return 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; } /* ** Insert content of corresponding content blob into the database. ** If content is identified as a symbolic link, then trailing ** "link " characters are removed from content. ** ** content is considered to be a symlink if zPerm contains at least ** one "l" character. */ static int svn_handle_symlinks(const char *perms, Blob *content){ Blob link_blob; if( perms && strstr(perms, "l")!=0 ){ if( blob_size(content)>5 ){ /* Skip trailing 'link ' characters */ blob_seek(content, 5, BLOB_SEEK_SET); blob_tail(content, &link_blob); return content_put(&link_blob); }else{ fossil_fatal("Too short symbolic link path"); } }else{ return content_put(content); } } /* ** 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 ( zPerm==0 ){ zPerm = svn_find_prop(rec, "svn:special") ? "l" : 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 = svn_handle_symlinks(zPerm, &target); }else if( rec.contentFlag ){ rid = svn_handle_symlinks(zPerm, &rec.content); }else if( zSrcPath ){ if ( zPerm==0 ){ zPerm = db_text(0, "SELECT tperm FROM xfiles" " WHERE tpath=%Q AND tbranch=%d" "", zSrcPath, branchId); } } 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( rec.contentFlag && strncmp(zKind, "dir", 3)!=0 ){ if ( zPerm==0 ){ zPerm = db_text(0, "SELECT tperm FROM xfiles" " WHERE tpath=%Q AND tbranch=%d" "", zFile, branchId); } if( deltaFlag ){ Blob deltaSrc; Blob target; rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" " SELECT tuuid FROM xfiles" " WHERE tpath=%Q AND tbranch=%d" ")", zFile, branchId); content_get(rid, &deltaSrc); svn_apply_svndiff(&rec.content, &deltaSrc, &target); rid = svn_handle_symlinks(zPerm, &target); }else{ rid = svn_handle_symlinks(zPerm, &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) ** Options: ** --import-marks FILE Restore marks table from FILE ** --export-marks FILE Save marks table to FILE ** --rename-master NAME Renames the master branch to NAME ** --use-author Uses author as the committer ** --attribute "EMAIL USER" Attribute commits to USER ** instead of Git committer EMAIL address ** ** --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/. The SVN --deltas ** format is supported but not required. ** 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 ** --rev-tags Tag each revision, implied by -i ** --no-rev-tags Disables tagging effect of -i ** --rename-rev PAT Rev tag names, default "svn-rev-%" ** --ignore-tree DIR Ignores subtree rooted at DIR ** ** Common Options: ** -i|--incremental Allow importing into an existing repository ** -f|--force Overwrite repository if already exists ** -q|--quiet Omit progress output ** --no-rebuild Skip the "rebuilding metadata" step ** --no-vacuum Skip the final VACUUM of the database file ** --rename-trunk NAME Use NAME as name of imported trunk branch ** --rename-branch PAT Rename all branch names using PAT pattern ** --rename-tag PAT Rename all tag names using PAT pattern ** -A|--admin-user NAME Use NAME for the admin user ** ** The --incremental option allows an existing repository to be extended ** with new content. The --rename-* options may be useful to avoid name ** conflicts when using the --incremental option. The --admin-user ** option is ignored if --incremental is specified. ** ** The argument to --rename-* contains one "%" character to be replaced ** with the original name. For example, "--rename-tag svn-%-tag" renames ** the tag called "release" to "svn-release-tag". ** ** --ignore-tree is useful for importing Subversion repositories which ** move branches to subdirectories of "branches/deleted" instead of ** deleting them. It can be supplied multiple times if necessary. ** ** The --attribute option takes a quoted string argument comprised of a ** Git committer email and the username to be attributed to corresponding ** check-ins in the Fossil repository. This option can be repeated. For ** example, --attribute "drh@sqlite.org drh" --attribute "xyz@abc.net X". ** Attributions are persisted to the repository so that subsequent ** 'fossil git export' operations attribute Fossil commits to corresponding ** 'Git Committer <git@committer.com>' users, and incremental imports with ** 'fossil import --git --incremental' use previous --attribute records. ** ** 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; int gitFlag = find_option("git", 0, 0)!=0; int omitRebuild = find_option("no-rebuild",0,0)!=0; int omitVacuum = find_option("no-vacuum",0,0)!=0; const char *zDefaultUser = find_option("admin-user","A",1); /* Options common to all input formats */ int incrFlag = find_option("incremental", "i", 0)!=0; /* Options for --svn only */ const char *zBase = ""; int flatFlag = 0; /* Options for --git only */ const char *markfile_in = 0; const char *markfile_out = 0; /* Interpret --rename-* options. Use a table to avoid code duplication. */ const struct { const char *zOpt, **varPre, *zDefaultPre, **varSuf, *zDefaultSuf; int format; /* 1=git, 2=svn, 3=any */ } renOpts[] = { {"rename-branch", &gimport.zBranchPre, "", &gimport.zBranchSuf, "", 3}, {"rename-tag" , &gimport.zTagPre , "", &gimport.zTagSuf , "", 3}, {"rename-rev" , &gsvn.zRevPre, "svn-rev-", &gsvn.zRevSuf , "", 2}, }, *renOpt = renOpts; int i; for( i = 0; i < count(renOpts); ++i, ++renOpt ){ if( 1 << svnFlag & renOpt->format ){ const char *zArgument = find_option(renOpt->zOpt, 0, 1); if( zArgument ){ const char *sep = strchr(zArgument, '%'); if( !sep ){ fossil_fatal("missing '%%' in argument to --%s", renOpt->zOpt); }else if( strchr(sep + 1, '%') ){ fossil_fatal("multiple '%%' in argument to --%s", renOpt->zOpt); } *renOpt->varPre = fossil_malloc(sep - zArgument + 1); memcpy((char *)*renOpt->varPre, zArgument, sep - zArgument); ((char *)*renOpt->varPre)[sep - zArgument] = 0; *renOpt->varSuf = sep + 1; }else{ *renOpt->varPre = renOpt->zDefaultPre; *renOpt->varSuf = renOpt->zDefaultSuf; } } } if( !(gimport.zTrunkName = find_option("rename-trunk", 0, 1)) ){ gimport.zTrunkName = "trunk"; } if( svnFlag ){ /* Get --svn related options here, so verify_all_options() fails when * svn-only options are specified with --git */ const char *zIgnTree; unsigned nIgnTree = 0; while( (zIgnTree = find_option("ignore-tree", 0, 1)) ){ if ( *zIgnTree ){ gsvn.azIgnTree = fossil_realloc((void *)gsvn.azIgnTree, sizeof(*gsvn.azIgnTree) * (nIgnTree + 2)); gsvn.azIgnTree[nIgnTree++] = zIgnTree; gsvn.azIgnTree[nIgnTree] = 0; } } 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.revFlag = find_option("rev-tags", 0, 0) || (incrFlag && !find_option("no-rev-tags", 0, 0)); }else if( gitFlag ){ const char *zGitUser; markfile_in = find_option("import-marks", 0, 1); markfile_out = find_option("export-marks", 0, 1); if( !(ggit.zMasterName = find_option("rename-master", 0, 1)) ){ ggit.zMasterName = "master"; } ggit.authorFlag = find_option("use-author", 0, 0)!=0; /* ** Extract --attribute 'emailaddr username' args that will populate ** new 'fx_' table to later match username for check-in attribution. */ zGitUser = find_option("attribute", 0, 1); while( zGitUser != 0 ){ char *currGitUser; ggit.gitUserInfo = fossil_realloc(ggit.gitUserInfo, ++ggit.nGitAttr * sizeof(ggit.gitUserInfo[0])); currGitUser = fossil_strdup(zGitUser); ggit.gitUserInfo[ggit.nGitAttr-1].zEmail = next_token(&currGitUser); ggit.gitUserInfo[ggit.nGitAttr-1].zUser = rest_of_line(&currGitUser); zGitUser = find_option("attribute", 0, 1); } } 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"); if( pIn==0 ) fossil_fatal("cannot open input file \"%s\"", g.argv[3]); }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, 0); db_unprotect(PROTECT_ALL); db_begin_transaction(); if( !incrFlag ){ db_initial_setup(0, 0, zDefaultUser); db_set("main-branch", gimport.zTrunkName, 0); } content_rcvid_init(svnFlag ? "svn-import" : "git-import"); 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{ Bag blobs, vers; bag_init(&blobs); bag_init(&vers); /* 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 hashes. ** ** Given any valid fast-import symbol, the corresponding fossil rid and ** hash 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 INDEX temp.i_xmark ON xmark(trid);" "CREATE TEMP TABLE xbranch(tname TEXT UNIQUE, brnm TEXT);" "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);" ); if( markfile_in ){ FILE *f = fossil_fopen(markfile_in, "r"); if( !f ){ fossil_fatal("cannot open %s for reading", markfile_in); } if( import_marks(f, &blobs, NULL, NULL)<0 ){ fossil_fatal("error importing marks from file: %s", markfile_in); } fclose(f); } manifest_crosslink_begin(); /* ** The following 'fx_' table is used to hold information needed for ** importing and exporting to attribute Fossil check-ins or Git commits ** to either a desired username or full contact information string. */ if(ggit.nGitAttr > 0) { int idx; db_unprotect(PROTECT_ALL); if( !db_table_exists("repository", "fx_git") ){ db_multi_exec( "CREATE TABLE fx_git(user TEXT, email TEXT UNIQUE);" ); } for(idx = 0; idx < ggit.nGitAttr; ++idx ){ db_multi_exec( "INSERT OR IGNORE INTO fx_git(user, email) VALUES(%Q, %Q)", ggit.gitUserInfo[idx].zUser, ggit.gitUserInfo[idx].zEmail ); } db_protect_pop(); } 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, 0, 1); import_reset(0); } db_finalize(&q); if( markfile_out ){ int rid; Stmt q_marks; FILE *f; db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark"); while( db_step(&q_marks)==SQLITE_ROW ){ rid = db_column_int(&q_marks, 0); if( db_int(0, "SELECT count(objid) FROM event" " WHERE objid=%d AND type='ci'", rid)==0 ){ /* Blob marks exported by git aren't saved between runs, so they need ** to be left free for git to re-use in the future. */ }else{ bag_insert(&vers, rid); } } db_finalize(&q_marks); f = fossil_fopen(markfile_out, "w"); if( !f ){ fossil_fatal("cannot open %s for writing", markfile_out); } export_marks(f, &blobs, &vers); fclose(f); bag_clear(&blobs); bag_clear(&vers); } manifest_crosslink_end(MC_NONE); } verify_cancel(); db_end_transaction(0); fossil_print(" \r"); if( omitRebuild ){ omitVacuum = 1; }else{ db_begin_transaction(); fossil_print("Rebuilding repository meta-data...\n"); rebuild_db(1, !incrFlag); verify_cancel(); db_end_transaction(0); } if( !omitVacuum ){ fossil_print("Vacuuming..."); fflush(stdout); db_multi_exec("VACUUM"); } fossil_print(" ok\n"); if( !incrFlag ){ 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 (password is \"%s\")\n", g.zLogin, zPassword); hash_user_password(g.zLogin); } } |
Changes to src/info.c.
︙ | ︙ | |||
20 21 22 23 24 25 26 | ** the current tree, or a particular artifact or check-in. */ #include "config.h" #include "info.h" #include <assert.h> /* | | | | > | > > > > > | | < > > > > > > > | > > > | | > | | > | | | | | | | | | | > | | > | | | | | | | | > | | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > | < > > > > | > > | < > | | < > > > | < > | | < < < < < < < < < < < < > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > | | | < | | | | < < < < | > < | | < > | < > > > > > > > > > | > > > | > > > > > > | | | < | | | > > | > < | > | < > | < | | < < | | | > > > > > > > > > | > | | > > > > > > > > | | > > > > > | | < > > > > > > > > | < | > | | > | | > > | > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | < < | | < < < > > | < | < > | | | | > | | | | > | > | > > > | > > > > > > | | > > > > > > > | < < < < < < > > > > > > | > | < > > > > > < | > > > | < | > | > > > > | | | > > | > > > > > > > > | > > | > | > | > | > | > | < > > > | | < > | > | > | > > > > > > > > > > > > | | > | | > | | > > | | | < < | > | | < < | < < < < < < < < < < < < | < < < < < < < < < | < | < | > > | | | | > > > > > > > | | > | | | > > > > > | < < > | < < | < < > > > > > > > > | > > | < < > | < | | | | | | | > > > > > > | > > > | | < < < | | | | > > | < < < | > > > > | < < < < | | | > > > | | > | | | > > > > | > > > | | | | > > | < | > > | < | > > | < < < < < | | > > | > > > > | < | < | < | > | > > | > > | > > > | | > > | | > > | | | > > > > > > | > > > > | | > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | > > > > | < < < < > > | > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > > > > | > > | | > > > > > > > > > > > > > > > > > | | > > > > > > > > > | > > > > > > > > > > > | < > > > > > > > > | < > > | > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > | > > > > > > > < < < < < < < < < < < < | | | > | > > > > | | > > > | > | > > > > > > > | > > > > > > > > > > > > > | > | | > > > | < > > | < | > > > | > > > > > | | > > > > > > > > > | | > | > > > > > > > > > > > > > > | | | | < < | < < | | > > > > > > > > > | > > > > > > | > | > | | > | | > | < > | > | | | | | > > > > > | > > > | > > | > > > > > > > > > > > | | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | > > | | > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > | < | < | > > > | > > < < < < | < < | < < | | > | > > > > > | | | > > > > | > | < > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** the current tree, or a particular artifact or check-in. */ #include "config.h" #include "info.h" #include <assert.h> /* ** Return a string (in memory obtained from malloc) holding a ** comma-separated list of tags that apply to check-in with ** record-id rid. If the "propagatingOnly" flag is true, then only ** show branch tags (tags that propagate to children). ** ** Return NULL if there are no such tags. */ char *info_tags_of_checkin(int rid, int propagatingOnly){ char *zTags; zTags = db_text(0, "SELECT group_concat(substr(tagname, 5), ', ')" " FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagxref.tagtype>%d" " AND tag.tagid=tagxref.tagid" " AND tag.tagname GLOB 'sym-*'", rid, propagatingOnly!=0); return zTags; } /* ** Print common information about a particular record. ** ** * The artifact hash ** * The record ID ** * mtime and ctime ** * who signed it ** */ void show_common_info( int rid, /* The rid for the check-in to display info for */ const char *zRecDesc, /* Brief record description; e.g. "check-out:" */ int showComment, /* True to show the check-in comment */ int showFamily /* True to show parents and children */ ){ Stmt q; char *zComment = 0; char *zTags; char *zDate; char *zUuid; zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); if( zUuid ){ zDate = db_text(0, "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", rid ); /* 01234567890123 */ fossil_print("%-13s %.40s %s\n", zRecDesc, zUuid, zDate ? zDate : ""); free(zDate); if( showComment ){ zComment = db_text(0, "SELECT coalesce(ecomment,comment) || " " ' (user: ' || coalesce(euser,user,'?') || ')' " " FROM event WHERE objid=%d", rid ); } free(zUuid); } if( showFamily ){ db_prepare(&q, "SELECT uuid, pid, isprim FROM plink JOIN blob ON pid=rid " " WHERE cid=%d" " ORDER BY isprim DESC, mtime DESC /*sort*/", rid); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); const char *zType = db_column_int(&q, 2) ? "parent:" : "merged-from:"; zDate = db_text("", "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", db_column_int(&q, 1) ); fossil_print("%-13s %.40s %s\n", zType, zUuid, zDate); free(zDate); } db_finalize(&q); db_prepare(&q, "SELECT uuid, cid, isprim FROM plink JOIN blob ON cid=rid " " WHERE pid=%d" " ORDER BY isprim DESC, mtime DESC /*sort*/", rid); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); const char *zType = db_column_int(&q, 2) ? "child:" : "merged-into:"; zDate = db_text("", "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", db_column_int(&q, 1) ); fossil_print("%-13s %.40s %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: "); comment_print(zComment, 0, 14, -1, get_comment_format()); free(zComment); } } /* ** Print information about the URLs used to access a repository and ** checkouts in a repository. */ static void extraRepoInfo(void){ Stmt s; db_prepare(&s, "SELECT substr(name,7), date(mtime,'unixepoch')" " FROM config" " WHERE name GLOB 'ckout:*' ORDER BY mtime DESC"); while( db_step(&s)==SQLITE_ROW ){ const char *zName; const char *zCkout = db_column_text(&s, 0); if( !vfile_top_of_checkout(zCkout) ) continue; if( g.localOpen ){ if( fossil_strcmp(zCkout, g.zLocalRoot)==0 ) continue; zName = "alt-root:"; }else{ zName = "check-out:"; } fossil_print("%-11s %-54s %s\n", zName, zCkout, db_column_text(&s, 1)); } db_finalize(&s); db_prepare(&s, "SELECT substr(name,9), date(mtime,'unixepoch')" " FROM config" " WHERE name GLOB 'baseurl:*' ORDER BY mtime DESC"); while( db_step(&s)==SQLITE_ROW ){ fossil_print("access-url: %-54s %s\n", db_column_text(&s, 0), db_column_text(&s, 1)); } db_finalize(&s); } /* ** Show the parent project, if any */ static void showParentProject(void){ const char *zParentCode; zParentCode = db_get("parent-project-code",0); if( zParentCode ){ fossil_print("derived-from: %s %s\n", zParentCode, db_get("parent-project-name","")); } } /* ** COMMAND: info ** ** Usage: %fossil info ?VERSION | REPOSITORY_FILENAME? ?OPTIONS? ** ** With no arguments, provide information about the current tree. ** If an argument is specified, provide information about the object ** in the repository of the current tree that the argument refers ** to. Or if the argument is the name of a repository, show ** information about that repository. ** ** If the argument is a repository name, then the --verbose option shows ** all known check-out locations for that repository and all URLs used ** to access the repository. The --verbose is (currently) a no-op if ** the argument is the name of an object within the repository. ** ** Use the "finfo" command to get information about a specific ** file in a check-out. ** ** Options: ** -R|--repository REPO Extract info from repository REPO ** -v|--verbose Show extra information about repositories ** ** See also: [[annotate]], [[artifact]], [[finfo]], [[timeline]] */ 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 && file_isfile(g.argv[2], ExtFILE) && (fsize = file_size(g.argv[2], ExtFILE))>0 && (fsize&0x1ff)==0 ){ db_open_config(0, 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>")); showParentProject(); extraRepoInfo(); return; } db_find_and_open_repository(OPEN_OK_NOT_FOUND,0); verify_all_options(); if( g.argc==2 ){ int vid; if( g.repositoryOpen ){ db_record_repository_filename(0); fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); }else{ db_open_config(0,1); } if( g.localOpen ){ fossil_print("repository: %s\n", db_repository_filename()); fossil_print("local-root: %s\n", g.zLocalRoot); } if( verboseFlag && g.repositoryOpen ){ extraRepoInfo(); } if( g.zConfigDbName ){ fossil_print("config-db: %s\n", g.zConfigDbName); } if( g.repositoryOpen ){ fossil_print("project-code: %s\n", db_get("project-code", "")); showParentProject(); 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*/")); } if( verboseFlag || !g.repositoryOpen ){ Blob vx; char *z; fossil_version_blob(&vx, 0); z = strstr(blob_str(&vx), "version"); if( z ){ z += 8; }else{ z = blob_str(&vx); } fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe)); fossil_print("version: %s", z); blob_reset(&vx); } }else{ int rid; rid = name_to_rid(g.argv[2]); if( rid==0 ){ fossil_fatal("no such object: %s", g.argv[2]); } show_common_info(rid, "hash:", 1, 1); } } /* ** Show the context graph (immediate parents and children) for ** check-in rid and rid2 */ void render_checkin_context(int rid, int rid2, int parentsOnly, int mFlags){ Blob sql; Stmt q; int rx[2]; int i, n; rx[0] = rid; rx[1] = rid2; n = rid2 ? 2 : 1; 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);" "DELETE FROM ok;" ); for(i=0; i<n; i++){ db_multi_exec( "INSERT OR IGNORE INTO ok VALUES(%d);" "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;", rx[i], rx[i] ); } if( !parentsOnly ){ for(i=0; i<n; i++){ db_multi_exec( "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rx[i] ); if( db_table_exists("repository","cherrypick") ){ db_multi_exec( "INSERT OR IGNORE INTO ok " " SELECT parentid FROM cherrypick WHERE childid=%d;" "INSERT OR IGNORE INTO ok " " SELECT childid FROM cherrypick WHERE parentid=%d;", rx[i], rx[i] ); } } } 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, mFlags |TIMELINE_GRAPH |TIMELINE_FILLGAPS |TIMELINE_NOSCROLL |TIMELINE_XMERGE |TIMELINE_CHPICK, 0, 0, 0, rid, rid2, 0); db_finalize(&q); } /* ** Append the difference between artifacts to the output */ static void append_diff( const char *zFrom, /* Diff from this artifact */ const char *zTo, /* ... to this artifact */ DiffConfig *pCfg /* The diff configuration */ ){ int fromid; int toid; Blob from, to; if( zFrom ){ fromid = uuid_to_rid(zFrom, 0); content_get(fromid, &from); pCfg->zLeftHash = zFrom; }else{ blob_zero(&from); pCfg->zLeftHash = 0; } if( zTo ){ toid = uuid_to_rid(zTo, 0); content_get(toid, &to); }else{ blob_zero(&to); } if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){ pCfg->diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG; }else{ pCfg->diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG; } text_diff(&from, &to, cgi_output_blob(), pCfg); pCfg->zLeftHash = 0; blob_reset(&from); blob_reset(&to); } /* ** Write a line of web-page output that shows changes that have occurred ** to a file between two check-ins. */ static void append_file_change_line( const char *zCkin, /* The check-in on which the change occurs */ const char *zName, /* Name of the file that has changed */ const char *zOld, /* blob.uuid before change. NULL for added files */ const char *zNew, /* blob.uuid after change. NULL for deletes */ const char *zOldName, /* Prior name. NULL if no name change. */ DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */ int mperm /* executable or symlink permission for zNew */ ){ @ <p> if( !g.perm.Hyperlink ){ if( zNew==0 ){ @ Deleted %h(zName). }else if( zOld==0 ){ @ Added %h(zName). }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ @ Name change from %h(zOldName) to %h(zName). }else if( fossil_strcmp(zNew, zOld)==0 ){ if( mperm==PERM_EXE ){ @ %h(zName) became executable. }else if( mperm==PERM_LNK ){ @ %h(zName) became a symlink. }else{ @ %h(zName) became a regular file. } }else{ @ Changes to %h(zName). } if( pCfg ){ append_diff(zOld, zNew, pCfg); } }else{ if( zOld && zNew ){ if( fossil_strcmp(zOld, zNew)!=0 ){ if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ @ Renamed and modified @ %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zOldName,zOld,zCkin))\ @ %h(zOldName)</a> @ %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\ @ %h(zName)</a> @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. }else{ @ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\ @ %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 ){ @ Name change @ from %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zOldName,zOld,zCkin))\ @ %h(zOldName)</a> @ to %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\ @ %h(zName)</a>. }else{ @ %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\ @ %h(zName)</a> became if( mperm==PERM_EXE ){ @ executable with contents }else if( mperm==PERM_LNK ){ @ a symlink with target }else{ @ a regular file with contents } @ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. } }else if( zOld ){ @ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zOld,zCkin))\ @ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>. }else{ @ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\ @ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. } if( pCfg ){ append_diff(zOld, zNew, pCfg); }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ @ @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a> } } @ </p> } /* ** Generate javascript to enhance HTML diffs. */ void append_diff_javascript(int diffType){ if( diffType==0 ) return; builtin_fossil_js_bundle_or("diff", NULL); } /* ** Construct an appropriate diffFlag for text_diff() based on query ** parameters and the to boolean arguments. */ DiffConfig *construct_diff_flags(int diffType, DiffConfig *pCfg){ u64 diffFlags = 0; /* Zero means do not show any diff */ if( diffType>0 ){ int x; if( diffType==2 ) diffFlags = DIFF_SIDEBYSIDE; if( P_NoBot("w") ) diffFlags |= DIFF_IGNORE_ALLWS; if( PD_NoBot("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT; diffFlags |= DIFF_STRIP_EOLCR; diff_config_init(pCfg, diffFlags); /* "dc" query parameter determines lines of context */ x = atoi(PD("dc","7")); if( x>0 ) pCfg->nContext = x; /* The "noopt" parameter disables diff optimization */ return pCfg; }else{ diff_config_init(pCfg, 0); return 0; } } /* ** WEBPAGE: ci_tags ** URL: /ci_tags?name=ARTIFACTID ** ** Show all tags and properties for a given check-in. ** ** This information used to be part of the main /ci page, but it is of ** marginal usefulness. Better to factor it out into a sub-screen. */ void ci_tags_page(void){ const char *zHash; int rid; Stmt q; int cnt = 0; Blob sql; char const *zType; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } rid = name_to_rid_www("name"); if( rid==0 ){ style_header("Check-in Information Error"); @ No such object: %h(PD("name","")) style_finish_page(); return; } cgi_check_for_malice(); zHash = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); style_header("Tags and Properties"); zType = whatis_rid_type_label(rid); if(!zType) zType = "Artifact"; @ <h1>Tags and Properties for %s(zType) \ @ %z(href("%R/ci/%!S",zHash))%S(zHash)</a></h1> db_prepare(&q, "SELECT tag.tagid, tagname, " " (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d)," " value, datetime(tagxref.mtime,toLocal()), 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, 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); const char *zOrigUuid = db_column_text(&q, 6); cnt++; if( cnt==1 ){ @ <ul> } @ <li> if( tagtype==0 ){ @ <span class="infoTagCancelled">%h(zTagname)</span> cancelled }else if( zValue ){ @ <span class="infoTag">%h(zTagname)=%h(zValue)</span> }else { @ <span class="infoTag">%h(zTagname)</span> } if( tagtype==2 ){ if( zOrigUuid && zOrigUuid[0] ){ @ inherited from hyperlink_to_version(zOrigUuid); }else{ @ propagates to descendants } } if( zSrcUuid && zSrcUuid[0] ){ if( tagtype==0 ){ @ by }else{ @ added by } hyperlink_to_version(zSrcUuid); @ on hyperlink_to_date(zDate,0); } @ </li> } db_finalize(&q); if( cnt ){ @ </ul> } @ <div class="section">Context</div> db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" "DELETE FROM ok;" "INSERT INTO ok VALUES(%d);" "INSERT OR IGNORE INTO ok " " SELECT tagxref.srcid" " FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid" " WHERE tagxref.rid=%d;" "INSERT OR IGNORE INTO ok " " SELECT tagxref.origid" " FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid" " WHERE tagxref.rid=%d;", rid, rid, rid ); #if 0 db_multi_exec( "SELECT tag.tagid, tagname, " " (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d)," " value, datetime(tagxref.mtime,toLocal()), 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, rid, rid ); #endif blob_zero(&sql); blob_append(&sql, timeline_query_for_www(), -1); 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|TIMELINE_NOSCROLL, 0, 0, 0, rid, 0, 0); db_finalize(&q); style_finish_page(); } /* ** WEBPAGE: vinfo ** WEBPAGE: ci ** URL: /ci/ARTIFACTID ** OR: /ci?name=ARTIFACTID ** ** Display information about a particular check-in. The exact ** same information is shown on the /info page if the name query ** parameter to /info describes a check-in. ** ** The ARTIFACTID can be a unique prefix for the HASH of the check-in, ** or a tag or branch name that identifies the check-in. */ void ci_page(void){ Stmt q1, q2, q3; int rid; int isLeaf; int diffType; /* 0: no diff, 1: unified, 2: side-by-side */ const char *zName; /* Name of the check-in to be displayed */ const char *zUuid; /* Hash of zName, found via blob.uuid */ const char *zParent; /* Hash 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 */ const char *zBrName; /* Branch name */ DiffConfig DCfg,*pCfg; /* Type of diff */ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_set_current_feature("vinfo"); zName = P("name"); rid = name_to_rid_www("name"); if( rid==0 ){ style_header("Check-in Information Error"); @ No such object: %h(zName) style_finish_page(); 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 = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid); db_prepare(&q1, "SELECT uuid, datetime(mtime,toLocal()), user, comment," " datetime(omtime,toLocal()), mtime" " FROM blob, event" " WHERE blob.rid=%d" " AND event.objid=%d", rid, rid ); zBrName = branch_of_rid(rid); diffType = preferred_diff_type(); cgi_check_for_malice(); if( db_step(&q1)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q1, 0); int nUuid = db_column_bytes(&q1, 0); char *zEUser, *zEComment; const char *zUser; const char *zOrigUser; const char *zComment; const char *zDate; const char *zOrigDate; int okWiki = 0; Blob wiki_read_links = BLOB_INITIALIZER; Blob wiki_add_links = BLOB_INITIALIZER; Th_Store("current_checkin", zName); 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); zOrigUser = db_column_text(&q1, 2); zUser = zEUser ? zEUser : zOrigUser; zComment = db_column_text(&q1, 3); zDate = db_column_text(&q1,1); zOrigDate = db_column_text(&q1, 4); if( zOrigDate==0 ) zOrigDate = zDate; @ <div class="section accordion">Overview</div> @ <div class="accordion_panel"> @ <table class="label-value"> @ <tr><th>Comment:</th><td class="infoComment">\ @ %!W(zEComment?zEComment:zComment)</td></tr> /* The Download: line */ if( g.perm.Zip ){ char *zPJ = db_get("short-project-name", 0); char *zUrl; 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] = '_'; } } zUrl = mprintf("%R/tarball/%S/%t-%S.tar.gz", zUuid, zPJ, zUuid); @ <tr><th>Downloads:</th><td> @ %z(href("%s",zUrl))Tarball</a> @ | %z(href("%R/zip/%S/%t-%S.zip",zUuid, zPJ,zUuid))ZIP archive</a> @ | %z(href("%R/sqlar/%S/%t-%S.sqlar",zUuid,zPJ,zUuid))\ @ SQL archive</a></td></tr> fossil_free(zUrl); blob_reset(&projName); } @ <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); if( fossil_strcmp(zTagName,zBrName)==0 ){ cgi_printf(" | "); style_copy_button(1, "name-br", 0, 0, "%z%h</a>", href("%R/timeline?r=%T&unhide",zTagName), zTagName); cgi_printf("\n"); if( wiki_tagid2("branch",zTagName)!=0 ){ blob_appendf(&wiki_read_links, " | %z%h</a>", href("%R/%s?name=branch/%h", (g.perm.Write && g.perm.WrWiki) ? "wikiedit" : "wiki", zTagName), zTagName); }else if( g.perm.Write && g.perm.WrWiki ){ blob_appendf(&wiki_add_links, " | %z%h</a>", href("%R/wikiedit?name=branch/%h",zTagName), zTagName); } }else{ @ | %z(href("%R/timeline?t=%T&unhide",zTagName))%h(zTagName)</a> if( wiki_tagid2("tag",zTagName)!=0 ){ blob_appendf(&wiki_read_links, " | %z%h</a>", href("%R/wiki?name=tag/%h",zTagName), zTagName); }else if( g.perm.Write && g.perm.WrWiki ){ blob_appendf(&wiki_add_links, " | %z%h</a>", href("%R/wikiedit?name=tag/%h",zTagName), zTagName); } } } db_finalize(&q2); @ </td></tr> @ <tr><th>Files:</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> @ </td> @ </tr> @ <tr><th>%s(hname_alg(nUuid)):</th><td> style_copy_button(1, "hash-ci", 0, 2, "%.32s<wbr>%s", zUuid, zUuid+32); if( g.perm.Setup ){ @ (Record ID: %d(rid)) } @ </td></tr> @ <tr><th>User & Date:</th><td> hyperlink_to_user(zUser,zDate," on "); hyperlink_to_date(zDate, "</td></tr>"); if( zEComment ){ @ <tr><th>Original Comment:</th> @ <td class="infoComment">%!W(zComment)</td></tr> } if( fossil_strcmp(zDate, zOrigDate)!=0 || fossil_strcmp(zOrigUser, zUser)!=0 ){ @ <tr><th>Original User & Date:</th><td> hyperlink_to_user(zOrigUser,zOrigDate," on "); hyperlink_to_date(zOrigDate, "</td></tr>"); } if( g.perm.Admin ){ db_prepare(&q2, "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)," " blob.rcvid" " 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); int rcvid = db_column_int(&q2,3); if( zUser==0 || zUser[0]==0 ) zUser = "unknown"; @ <tr><th>Received From:</th> @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate) \ @ (<a href="%R/rcvfrom?rcvid=%d(rcvid)">Rcvid %d(rcvid)</a>)</td></tr> } db_finalize(&q2); } /* Only show links to edit wiki pages if the users can read wiki ** and if the wiki pages already exist */ if( g.perm.WrWiki && g.perm.RdWiki && g.perm.Write && ((okWiki = wiki_tagid2("checkin",zUuid))!=0 || blob_size(&wiki_read_links)>0) && db_get_boolean("wiki-about",1) ){ const char *zLinks = blob_str(&wiki_read_links); @ <tr><th>Edit Wiki:</th><td>\ if( okWiki ){ @ %z(href("%R/wikiedit?name=checkin/%s",zUuid))this check-in</a>\ }else if( zLinks[0] ){ zLinks += 3; } @ %s(zLinks)</td></tr> } /* Only show links to create new wiki pages if the users can write wiki ** and if the wiki pages do not already exist */ if( g.perm.WrWiki && g.perm.RdWiki && g.perm.Write && (blob_size(&wiki_add_links)>0 || !okWiki) && db_get_boolean("wiki-about",1) ){ const char *zLinks = blob_str(&wiki_add_links); @ <tr><th>Add Wiki:</th><td>\ if( !okWiki ){ @ %z(href("%R/wikiedit?name=checkin/%s",zUuid))this check-in</a>\ }else if( zLinks[0] ){ zLinks += 3; } @ %s(zLinks)</td></tr> } if( g.perm.Hyperlink ){ @ <tr><th>Other Links:</th> @ <td> if( fossil_strcmp(zBrName, db_get("main-branch",0))!=0 ){ @ %z(href("%R/vdiff?branch=%!S", zUuid))branch diff</a> | } @ %z(href("%R/artifact/%!S",zUuid))manifest</a> @ | %z(href("%R/ci_tags/%!S",zUuid))tags</a> if( g.perm.Admin ){ @ | %z(href("%R/mlink?ci=%!S",zUuid))mlink table</a> } if( g.anon.Write ){ @ | %z(href("%R/ci_edit?r=%!S",zUuid))edit</a> } @ </td> @ </tr> } @ </table> blob_reset(&wiki_read_links); blob_reset(&wiki_add_links); }else{ style_header("Check-in Information"); login_anonymous_available(); } db_finalize(&q1); @ </div> builtin_request_js("accordion.js"); if( !PB("nowiki") ){ wiki_render_associated("checkin", zUuid, 0); } render_backlink_graph(zUuid, "<div class=\"section accordion\">References</div>\n"); @ <div class="section accordion">Context</div><div class="accordion_panel"> render_checkin_context(rid, 0, 0, 0); @ </div><div class="section accordion">Changes</div> @ <div class="accordion_panel"> @ <div class="sectionmenu"> pCfg = construct_diff_flags(diffType, &DCfg); DCfg.pRe = pRe; zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; if( diffType!=0 ){ @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\ @ Hide Diffs</a> } if( diffType!=1 ){ @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\ @ Unified Diffs</a> } if( diffType!=2 ){ @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\ @ Side-by-Side Diffs</a> } if( diffType!=0 ){ if( *zW ){ @ %z(chref("button","%R/%s/%T",zPage,zName)) @ Show Whitespace Changes</a> }else{ @ %z(chref("button","%R/%s/%T?w",zPage,zName)) @ Ignore Whitespace</a> } } if( zParent ){ @ %z(chref("button","%R/vpatch?from=%!S&to=%!S",zParent,zUuid)) @ Patch</a> } if( g.perm.Admin ){ @ %z(chref("button","%R/mlink?ci=%!S",zUuid))MLink Table</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(zUuid, zName, zOld, zNew, zOldName, pCfg,mperm); } db_finalize(&q3); @ </div> append_diff_javascript(diffType); style_finish_page(); } /* ** WEBPAGE: winfo ** URL: /winfo?name=HASH ** ** Display information about a wiki page. */ void winfo_page(void){ int rid; Manifest *pWiki; char *zUuid; char *zDate; Blob wiki; int modPending; const char *zModAction; int tagid; int ridNext; login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } style_set_current_feature("winfo"); 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_finish_page(); 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('w', rid); } } cgi_check_for_malice(); 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,toLocal())", pWiki->rDate); style_submenu_element("Raw", "%R/artifact/%s", zUuid); style_submenu_element("History", "%R/whistory?name=%t", pWiki->zWikiTitle); style_submenu_element("Page", "%R/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_www(rid); @ </td></tr> @ <tr><th>Page Name:</th>\ @ <td>%z(href("%R/whistory?name=%h",pWiki->zWikiTitle))\ @ %h(pWiki->zWikiTitle)</a></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("%R/info/%!S",zParent))%s(zParent)</a> @ %z(href("%R/wdiff?id=%!S&pid=%!S",zUuid,zParent))(diff)</a> } @ </td></tr> } tagid = wiki_tagid(pWiki->zWikiTitle); if( tagid>0 && (ridNext = wiki_next(tagid, pWiki->rDate))>0 ){ char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ridNext); @ <tr><th>Next</th> @ <td>%z(href("%R/info/%!S",zId))%s(zId)</a></td> } @ </table> if( g.perm.ModWiki && modPending ){ @ <div class="section">Moderation</div> @ <blockquote> @ <form method="POST" action="%R/winfo/%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</div> blob_init(&wiki, pWiki->zWiki, -1); safe_html_context(DOCSRC_WIKI); wiki_render_by_mimetype(&wiki, pWiki->zMimetype); blob_reset(&wiki); manifest_destroy(pWiki); document_emit_js(); style_finish_page(); } /* ** 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); } #if 0 /* not used */ /* ** Output a description of a check-in */ static void checkin_description(int rid){ Stmt q; db_prepare(&q, "SELECT datetime(mtime), coalesce(euser,user)," " coalesce(ecomment,comment), uuid," " (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)" " FROM event, blob" " WHERE event.objid=%d AND type='ci'" " AND blob.rid=%d", rid, rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zUser = db_column_text(&q, 1); const char *zUuid = db_column_text(&q, 3); const char *zTagList = db_column_text(&q, 4); Blob comment; int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS; if( db_get_boolean("timeline-block-markup", 0)==0 ){ wikiFlags |= WIKI_NOBLOCK; } hyperlink_to_version(zUuid); blob_zero(&comment); db_column_blob(&q, 2, &comment); wiki_convert(&comment, 0, wikiFlags); blob_reset(&comment); @ (user: hyperlink_to_user(zUser,zDate,","); if( zTagList && zTagList[0] && 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++){} blob_appendf(&links, "%z%#h</a>%.2s", href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i] ); if( z[i]==0 ) break; z += i+2; } @ tags: %s(blob_str(&links)), blob_reset(&links); }else{ @ tags: %h(zTagList), } @ date: hyperlink_to_date(zDate, ")"); tag_private_status(rid); } db_finalize(&q); } #endif /* not used */ /* ** WEBPAGE: vdiff ** URL: /vdiff?from=TAG&to=TAG ** ** Show the difference between two check-ins identified by the from= and ** to= query parameters. ** ** 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 ** diff=INTEGER 0: none, 1: unified, 2: side-by-side ** glob=STRING only diff files matching this glob ** dc=N show N lines of context around each diff ** w=BOOLEAN ignore whitespace when computing diffs ** nohdr omit the description at the top of the page ** nc omit branch coloration from the header graph ** inv "Invert". Exchange the roles of from= and to= ** ** Show all differences between two check-ins. */ void vdiff_page(void){ int ridFrom, ridTo; int diffType = 0; /* 0: none, 1: unified, 2: side-by-side */ Manifest *pFrom, *pTo; ManifestFile *pFileFrom, *pFileTo; const char *zBranch; const char *zFrom; const char *zTo; const char *zRe; const char *zGlob; char *zMergeOrigin = 0; ReCompiled *pRe = 0; DiffConfig DCfg, *pCfg = 0; int graphFlags = 0; Blob qp; int bInvert = PB("inv"); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } login_anonymous_available(); fossil_nice_default(); blob_init(&qp, 0, 0); diffType = preferred_diff_type(); zRe = P("regex"); if( zRe ) re_compile(&pRe, zRe, 0); zBranch = P("branch"); if( zBranch && zBranch[0]==0 ) zBranch = 0; if( zBranch ){ blob_appendf(&qp, "branch=%T", zBranch); zMergeOrigin = mprintf("merge-in:%s", zBranch); cgi_replace_parameter("from", zMergeOrigin); cgi_replace_parameter("to", zBranch); }else{ if( bInvert ){ blob_appendf(&qp, "to=%T&from=%T",PD("from",""),PD("to","")); }else{ blob_appendf(&qp, "from=%T&to=%T",PD("from",""),PD("to","")); } } pTo = vdiff_parse_manifest("to", &ridTo); if( pTo==0 ) return; pFrom = vdiff_parse_manifest("from", &ridFrom); if( pFrom==0 ) return; zGlob = P("glob"); /* ** Maintenace reminder: we explicitly do _not_ use P_NoBot() ** for "from" and "to" because those args can contain legitimate ** strings which may trigger the looks-like SQL checks, e.g. ** from=merge-in:OR-clause-improvement ** to=OR-clause-improvement */ zFrom = P("from"); zTo = P("to"); if( bInvert ){ Manifest *pTemp = pTo; const char *zTemp = zTo; pTo = pFrom; pFrom = pTemp; zTo = zFrom; zFrom = zTemp; } if( zGlob ){ if( !*zGlob ){ zGlob = NULL; }else{ blob_appendf(&qp, "&glob=%T", zGlob); } } if( PB("nc") ){ graphFlags |= TIMELINE_NOCOLOR; blob_appendf(&qp, "&nc"); } pCfg = construct_diff_flags(diffType, &DCfg); if( DCfg.diffFlags & DIFF_IGNORE_ALLWS ){ blob_appendf(&qp, "&w"); } cgi_check_for_malice(); style_set_current_feature("vdiff"); if( zBranch==0 ){ style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo); } if( diffType!=0 ){ style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b", &qp); } if( diffType!=2 ){ style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b", &qp); } if( diffType!=1 ) { style_submenu_element("Unified Diff", "%R/vdiff?diff=1&%b", &qp); } if( zBranch==0 ){ style_submenu_element("Invert","%R/vdiff?diff=%d&inv&%b", diffType, &qp); } if( zGlob ){ style_submenu_element("Clear glob", "%R/vdiff?diff=%d&%b", diffType, &qp); }else{ style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, (DCfg.diffFlags & DIFF_IGNORE_ALLWS)?"&w":""); } if( diffType!=0 ){ style_submenu_checkbox("w", "Ignore Whitespace", 0, 0); } if( zBranch ){ style_header("Changes On Branch %h", zBranch); }else{ style_header("Check-in Differences"); } if( P("nohdr")==0 ){ if( zBranch ){ char *zRealBranch = branch_of_rid(ridTo); char *zToUuid = rid_to_uuid(ridTo); char *zFromUuid = rid_to_uuid(ridFrom); @ <h2>Changes In Branch \ @ %z(href("%R/timeline?r=%T",zRealBranch))%h(zRealBranch)</a> if( ridTo != symbolic_name_to_rid(zRealBranch,"ci") ){ @ Through %z(href("%R/info/%!S",zToUuid))[%S(zToUuid)]</a> } @ Excluding Merge-Ins</h2> @ <p>This is equivalent to a diff from @ <span class='timelineSelected'>\ @ %z(href("%R/info/%!S",zFromUuid))%S(zFromUuid)</a></span> @ to <span class='timelineSelected timelineSecondary'>\ @ %z(href("%R/info/%!S",zToUuid))%S(zToUuid)</a></span></p> }else{ @ <h2>Difference From <span class='timelineSelected'>\ @ %z(href("%R/info/%h",zFrom))%h(zFrom)</a></span> @ To <span class='timelineSelected timelineSecondary'>\ @ %z(href("%R/info/%h",zTo))%h(zTo)</a></span></h2> } render_checkin_context(ridFrom, ridTo, 0, graphFlags); 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> } blob_reset(&qp); manifest_file_rewind(pFrom); pFileFrom = manifest_file_next(pFrom, 0); manifest_file_rewind(pTo); pFileTo = manifest_file_next(pTo, 0); DCfg.pRe = pRe; 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(zFrom, pFileFrom->zName, pFileFrom->zUuid, 0, 0, pCfg, 0); } pFileFrom = manifest_file_next(pFrom, 0); }else if( cmp>0 ){ if( !zGlob || sqlite3_strglob(zGlob, pFileTo->zName)==0 ){ append_file_change_line(zTo, pFileTo->zName, 0, pFileTo->zUuid, 0, pCfg, 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(zFrom, pFileFrom->zName, pFileFrom->zUuid, pFileTo->zUuid, 0, pCfg, 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(diffType); style_finish_page(); } #if INTERFACE /* ** Possible return values from object_description() */ #define OBJTYPE_CHECKIN 0x0001 #define OBJTYPE_CONTENT 0x0002 #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 #define OBJTYPE_FORUM 0x0200 /* ** Possible flags for the second parameter to ** object_description() */ #define OBJDESC_DETAIL 0x0001 /* Show more detail */ #define OBJDESC_BASE 0x0002 /* Set <base> using this object */ #endif /* ** Write a description of an object to the www reply. */ int object_description( int rid, /* The artifact ID for the object to describe */ u32 objdescFlags, /* Flags to control display */ const char *zFileName, /* For file objects, use this name. Can be NULL */ Blob *pDownloadName /* Fill with a good download name. Can be NULL */ ){ 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; int bNeedBase = (objdescFlags & OBJDESC_BASE)!=0; db_prepare(&q, "SELECT filename.name, datetime(event.mtime,toLocal())," " coalesce(event.ecomment,event.comment)," " coalesce(event.euser,event.user)," " b.uuid, mlink.mperm," " coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk')," " a.size" " 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 /*sort*/", TAG_BRANCH, rid ); @ <ul> while( db_step(&q)==SQLITE_ROW ){ 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 szFile = db_column_int(&q,7); int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0; if( zFileName && fossil_strcmp(zName,zFileName)!=0 ) continue; 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 if( bNeedBase ){ bNeedBase = 0; style_set_current_page("doc/%S/%s",zVers,zName); } } objType |= OBJTYPE_CONTENT; @ %z(href("%R/finfo?name=%T&ci=%!S&m=%!S",zName,zVers,zUuid))\ @ %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_version(zVers); }else{ @ — part of check-in hyperlink_to_version(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,","); @ size: %d(szFile)) if( g.perm.Hyperlink ){ @ %z(href("%R/annotate?filename=%T&checkin=%!S",zName,zVers)) @ [annotate]</a> @ %z(href("%R/blame?filename=%T&checkin=%!S",zName,zVers)) @ [blame]</a> @ %z(href("%R/timeline?uf=%!S",zUuid))[check-ins using]</a> if( fileedit_is_editable(zName) ){ @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zName,zVers))[edit]</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, toLocal())," " coalesce(event.euser, event.user)" " FROM tagxref, tag, event" " WHERE tagxref.rid=%d" " AND tag.tagid=tagxref.tagid" " AND tag.tagname LIKE 'wiki-%%'" " AND event.objid=tagxref.rid", rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zPagename = db_column_text(&q, 0); const char *zDate = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); if( cnt>0 ){ @ Also wiki page }else{ @ Wiki page } objType |= OBJTYPE_WIKI; @ [%z(href("%R/wiki?name=%t",zPagename))%h(zPagename)</a>] by hyperlink_to_user(zUser,zDate," on"); hyperlink_to_date(zDate,"."); nWiki++; cnt++; if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_appendf(pDownloadName, "%s.txt", zPagename); } } db_finalize(&q); if( nWiki==0 ){ db_prepare(&q, "SELECT datetime(mtime, toLocal()), user, comment, type, uuid, tagid" " FROM event, blob" " WHERE event.objid=%d" " AND blob.rid=%d", rid, rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zUser = db_column_text(&q, 1); const char *zCom = db_column_text(&q, 2); const char *zType = db_column_text(&q, 3); const char *zUuid = db_column_text(&q, 4); int eventTagId = db_column_int(&q, 5); if( cnt>0 ){ @ Also } if( zType[0]=='w' ){ @ Wiki edit objType |= OBJTYPE_WIKI; }else if( zType[0]=='t' ){ @ Ticket change objType |= OBJTYPE_TICKET; }else if( zType[0]=='c' ){ @ Manifest of check-in objType |= OBJTYPE_CHECKIN; }else if( zType[0]=='e' ){ if( eventTagId != 0) { @ Instance of technote objType |= OBJTYPE_EVENT; hyperlink_to_event_tagid(db_column_int(&q, 5)); }else{ @ Attachment to technote } }else if( zType[0]=='f' ){ objType |= OBJTYPE_FORUM; @ Forum post }else{ @ Tag referencing } if( zType[0]!='e' || eventTagId == 0){ hyperlink_to_version(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, toLocal()), user, src" " FROM attachment" " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)" " ORDER BY mtime DESC /*sort*/", rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zTarget = db_column_text(&q, 0); const char *zFilename = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); const char *zUser = db_column_text(&q, 3); /* const char *zSrc = db_column_text(&q, 4); */ if( cnt>0 ){ @ Also attachment "%h(zFilename)" to }else{ @ Attachment "%h(zFilename)" to } objType |= OBJTYPE_ATTACHMENT; if( fossil_is_artifact_hash(zTarget) ){ if ( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTarget) ){ if( g.perm.Hyperlink && g.anon.RdTkt ){ @ ticket [%z(href("%R/tktview?name=%!S",zTarget))%S(zTarget)</a>] }else{ @ ticket [%S(zTarget)] } }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", zTarget) ){ if( g.perm.Hyperlink && g.anon.RdWiki ){ @ tech note [%z(href("%R/technote/%h",zTarget))%S(zTarget)</a>] }else{ @ tech note [%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)] } } }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( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d", rid, TAG_CLUSTER) ){ @ Cluster cnt++; } if( cnt==0 ){ @ Unrecognized artifact if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_appendf(pDownloadName, "%S.txt", zUuid); } tag_private_status(rid); } return objType; } /* ** SETTING: preferred-diff-type width=16 default=0 ** ** The preferred-diff-type setting determines the preferred diff format ** for web pages if the format is not otherwise specified, for example ** by a query parameter or cookie. Allowed values: ** ** 1 Unified diff ** 2 Side-by-side diff ** ** If this setting is omitted or has a value of 0 or less, then it ** is ignored. */ /* ** Return the preferred diff type. ** ** 0 = No diff at all. ** 1 = unified diff ** 2 = side-by-side diff ** ** To determine the preferred diff type, the following values are ** consulted in the order shown. The first available source wins. ** ** * The "diff" query parameter ** * The "diff" field of the user display cookie ** * The "preferred-diff-type" setting ** * 1 for mobile and 2 for desktop, based on the UserAgent */ int preferred_diff_type(void){ int dflt; static char zDflt[2] /*static b/c cookie_link_parameter() does not copy it!*/; dflt = db_get_int("preferred-diff-type",-99); if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2; zDflt[0] = dflt + '0'; zDflt[1] = 0; cookie_link_parameter("diff","diff", zDflt); return atoi(PD_NoBot("diff",zDflt)); } /* ** WEBPAGE: fdiff ** URL: fdiff?v1=HASH&v2=HASH ** ** Two arguments, v1 and v2, identify the artifacts to be diffed. ** Show diff side by side unless sbs is 0. Generate plain text if ** "patch" is present, otherwise generate "pretty" HTML. ** ** Alternative URL: fdiff?from=filename1&to=filename2&ci=checkin ** ** If the "from" and "to" query parameters are both present, then they are ** the names of two files within the check-in "ci" that are diffed. If the ** "ci" parameter is omitted, then the most recent check-in ("tip") is ** used. ** ** Additional parameters: ** ** dc=N Show N lines of context around each diff ** patch Use the patch diff format ** regex=REGEX Only show differences that match REGEX ** sbs=BOOLEAN Turn side-by-side diffs on and off (default: on) ** verbose=BOOLEAN Show more detail when describing artifacts ** w=BOOLEAN Ignore whitespace */ void diff_page(void){ int v1, v2; int isPatch = P("patch")!=0; int diffType; /* 0: none, 1: unified, 2: side-by-side */ char *zV1; char *zV2; const char *zRe; ReCompiled *pRe = 0; u32 objdescFlags = 0; int verbose = PB("verbose"); DiffConfig DCfg; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } diff_config_init(&DCfg, 0); diffType = preferred_diff_type(); if( P("from") && P("to") ){ v1 = artifact_from_ci_and_filename("from"); v2 = artifact_from_ci_and_filename("to"); }else{ Stmt q; v1 = name_to_rid_www("v1"); v2 = name_to_rid_www("v2"); /* If the two file versions being compared both have the same ** filename, then offer an "Annotate" link that constructs an ** annotation between those version. */ db_prepare(&q, "SELECT (SELECT substr(uuid,1,20) FROM blob WHERE rid=a.mid)," " (SELECT substr(uuid,1,20) FROM blob WHERE rid=b.mid)," " (SELECT name FROM filename WHERE filename.fnid=a.fnid)" " FROM mlink a, event ea, mlink b, event eb" " WHERE a.fid=%d" " AND b.fid=%d" " AND a.fnid=b.fnid" " AND a.fid!=a.pid" " AND b.fid!=b.pid" " AND ea.objid=a.mid" " AND eb.objid=b.mid" " ORDER BY ea.mtime ASC, eb.mtime ASC", v1, v2 ); if( db_step(&q)==SQLITE_ROW ){ const char *zCkin = db_column_text(&q, 0); const char *zOrig = db_column_text(&q, 1); const char *zFN = db_column_text(&q, 2); style_submenu_element("Annotate", "%R/annotate?origin=%s&checkin=%s&filename=%T", zOrig, zCkin, zFN); } db_finalize(&q); } if( v1==0 || v2==0 ) fossil_redirect_home(); zRe = P("regex"); cgi_check_for_malice(); if( zRe ) re_compile(&pRe, zRe, 0); if( verbose ) objdescFlags |= OBJDESC_DETAIL; if( isPatch ){ Blob c1, c2, *pOut; DiffConfig DCfg; pOut = cgi_output_blob(); cgi_set_content_type("text/plain"); DCfg.diffFlags = DIFF_VERBOSE; content_get(v1, &c1); content_get(v2, &c2); DCfg.pRe = pRe; text_diff(&c1, &c2, pOut, &DCfg); blob_reset(&c1); blob_reset(&c2); return; } zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1); zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2); construct_diff_flags(diffType, &DCfg); DCfg.diffFlags |= DIFF_HTML; style_set_current_feature("fdiff"); style_header("Diff"); style_submenu_checkbox("w", "Ignore Whitespace", 0, 0); if( diffType==2 ){ style_submenu_element("Unified Diff", "%R/fdiff?v1=%T&v2=%T&diff=1", P("v1"), P("v2")); }else{ style_submenu_element("Side-by-side Diff", "%R/fdiff?v1=%T&v2=%T&diff=2", P("v1"), P("v2")); } style_submenu_checkbox("verbose", "Verbose", 0, 0); style_submenu_element("Patch", "%R/fdiff?v1=%T&v2=%T&patch", P("v1"), P("v2")); 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, 0); @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2> object_description(v2, objdescFlags,0, 0); } if( pRe ){ @ <b>Only differences that match regular expression "%h(zRe)" @ are shown.</b> DCfg.pRe = pRe; } @ <hr> append_diff(zV1, zV2, &DCfg); append_diff_javascript(diffType); style_finish_page(); } /* ** WEBPAGE: raw ** URL: /raw/ARTIFACTID ** URL: /raw?ci=BRANCH&filename=NAME ** ** Additional query parameters: ** ** m=MIMETYPE The mimetype is MIMETYPE ** at=FILENAME Content-disposition; attachment; filename=FILENAME; ** ** Return the uninterpreted content of an artifact. Used primarily ** to view artifacts that are images. */ void rawartifact_page(void){ int rid = 0; char *zUuid; (void)P("at")/*for cgi_check_for_malice()*/; (void)P("m"); if( P("ci") ){ rid = artifact_from_ci_and_filename(0); } if( rid==0 ){ rid = name_to_rid_www("name"); } login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } cgi_check_for_malice(); if( rid==0 ) fossil_redirect_home(); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); etag_check(ETAG_HASH, zUuid); if( fossil_strcmp(P("name"), zUuid)==0 && login_is_nobody() ){ g.isConst = 1; } free(zUuid); deliver_artifact(rid, P("m")); } /* ** WEBPAGE: secureraw ** URL: /secureraw/HASH?m=TYPE ** ** Return the uninterpreted content of an artifact. This is similar ** to /raw except in this case the only way to specify the artifact ** is by the full-length SHA1 or SHA3 hash. Abbreviations are not ** accepted. */ void secure_rawartifact_page(void){ int rid = 0; const char *zName = PD("name", ""); (void)P("at")/*for cgi_check_for_malice()*/; (void)P("m"); cgi_check_for_malice(); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); if( rid==0 ){ cgi_set_status(404, "Not Found"); @ Unknown artifact: "%h(zName)" return; } g.isConst = 1; deliver_artifact(rid, P("m")); } /* ** WEBPAGE: jchunk hidden ** URL: /jchunk/HASH?from=N&to=M ** ** Return lines of text from a file as a JSON array - one entry in the ** array for each line of text. ** ** **Warning:** This is an internal-use-only interface that is subject to ** change at any moment. External application should not use this interface ** since the application will break when this interface changes, and this ** interface will undoubtedly change. ** ** This page is intended to be used in an XHR from javascript on a ** diff page, to return unseen context to fill in additional context ** when the user clicks on the appropriate button. The response is ** always in JSON form and errors are reported as documented for ** ajax_route_error(). */ void jchunk_page(void){ int rid = 0; const char *zName = PD("name", ""); int iFrom = atoi(PD("from","0")); int iTo = atoi(PD("to","0")); int ln; int go = 1; const char *zSep; Blob content; Blob line; Blob *pOut; if(0){ ajax_route_error(400, "Just testing client-side error handling."); return; } login_check_credentials(); cgi_check_for_malice(); if( !g.perm.Read ){ ajax_route_error(403, "Access requires Read permissions."); return; } #if 1 /* Re-enable this block once this code is integrated somewhere into the UI. */ rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); if( rid==0 ){ ajax_route_error(404, "Unknown artifact: %h", zName); return; } #else /* This impl is only to simplify "manual" testing via the JS console. */ rid = symbolic_name_to_rid(zName, "*"); if( rid==0 ){ ajax_route_error(404, "Unknown artifact: %h", zName); return; }else if( rid<0 ){ ajax_route_error(418, "Ambiguous artifact name: %h", zName); return; } #endif if( iFrom<1 || iTo<iFrom ){ ajax_route_error(500, "Invalid line range from=%d, to=%d.", iFrom, iTo); return; } content_get(rid, &content); g.isConst = 1; cgi_set_content_type("application/json"); ln = 0; while( go && ln<iFrom ){ go = blob_line(&content, &line); ln++; } pOut = cgi_output_blob(); blob_append(pOut, "[\n", 2); zSep = 0; while( go && ln<=iTo ){ if( zSep ) blob_append(pOut, zSep, 2); blob_trim(&line); blob_append_json_literal(pOut, blob_buffer(&line), blob_size(&line)); zSep = ",\n"; go = blob_line(&content, &line); ln++; } blob_appendf(pOut,"]\n"); blob_reset(&content); } /* ** Generate a verbatim artifact as the result of an HTTP request. ** If zMime is not NULL, use it as the mimetype. If zMime is ** NULL, guess at the mimetype based on the filename ** associated with the artifact. */ void deliver_artifact(int rid, const char *zMime){ Blob content; const char *zAttachName = P("at"); if( zMime==0 ){ char *zFN = (char*)zAttachName; if( zFN==0 ){ zFN = db_text(0, "SELECT filename.name FROM mlink, filename" " WHERE mlink.fid=%d" " AND filename.fnid=mlink.fnid", rid); } if( zFN==0 ){ /* Look also at the attachment table */ zFN = db_text(0, "SELECT attachment.filename FROM attachment, blob" " WHERE blob.rid=%d" " AND attachment.src=blob.uuid", rid); } if( zFN ){ zMime = mimetype_from_name(zFN); } if( zMime==0 ){ zMime = "application/x-fossil-artifact"; } } content_get(rid, &content); fossil_free(style_csp(1)); cgi_set_content_type(zMime); if( zAttachName ){ cgi_content_disposition_filename(zAttachName); } cgi_set_content(&content); } /* ** Render a hex dump of a file. */ static void hexdump(Blob *pBlob){ |
︙ | ︙ | |||
853 854 855 856 857 858 859 | for(i=0; i<n; i+=16){ j = 0; zLine[0] = zHex[(i>>24)&0xf]; zLine[1] = zHex[(i>>16)&0xf]; zLine[2] = zHex[(i>>8)&0xf]; zLine[3] = zHex[i&0xf]; zLine[4] = ':'; | | > | < | | > > > > > > > > > > > > > > > > > > | | | | | > > > > > | > | | | < | < > | > > > > | > > | | | < > > > > > > > | | | > | > > > > > > > > | < | > | > > > > > | > | | > > > > | | > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > > | > > > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > | | > > > > | > > > > > > > > > > > > | > > > > > | > | < < > > | < | > > | > > > > > > > > | > > > > > > | > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > | > | < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < | < | > > | | | > > > > > > > > > > > | | > | | > > > > | | | | > | > | > > > | > > > | > > | > > | | < > > > > > > | > > > > > > > > > > > > > > | > > > > | | < > | > | | > > > > > | < | < | | | > | | < | | | > > | > | | | > | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | > | | | > > > > > | < < > > > | < > < < < < < < | > > | < < | | | | > > | > > > > > > | > | > | < | | | | | | > > | > > | > > > > > > > > > > | | > | | | > | > > > > > > | > > > | > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > > | > > | > > > | > | > > > > > > > > > > > > > > > | | | | | | | > | > > > > > < < < < < < < < < < < < < < < < < < < | | | | > | | | | > > > > | | > | | < < | < | | | < > | < < < | < > | < | < < < | < < < | < < | | < | < > | < < | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < | > > | | | > > > > | | < | | | | > | | > > > | > | > > > > > | | | < < < > | < < < < < < < < | | < < < | < | | | | > > > | | > | > > > > > > > > > > > > | | | | | > > > > > > | | > | > | > > > > > > > | | > | < < < < | | | | | | > > > > > > > > | | > > | | > | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | for(i=0; i<n; i+=16){ j = 0; zLine[0] = zHex[(i>>24)&0xf]; zLine[1] = zHex[(i>>16)&0xf]; zLine[2] = zHex[(i>>8)&0xf]; zLine[3] = zHex[i&0xf]; zLine[4] = ':'; sqlite3_snprintf(sizeof(zLine), zLine, "%04x: ", i); for(j=0; j<16; j++){ k = 5+j*3; zLine[k] = ' '; if( i+j<n ){ unsigned char c = x[i+j]; zLine[k+1] = zHex[c>>4]; zLine[k+2] = zHex[c&0xf]; }else{ zLine[k+1] = ' '; zLine[k+2] = ' '; } } zLine[53] = ' '; zLine[54] = ' '; cgi_append_content(zLine, 55); for(j=k=0; j<16; j++){ if( i+j<n ){ unsigned char c = x[i+j]; if( c>'>' && c<=0x7e ){ zLine[k++] = c; }else if( c=='>' ){ zLine[k++] = '&'; zLine[k++] = 'g'; zLine[k++] = 't'; zLine[k++] = ';'; }else if( c=='<' ){ zLine[k++] = '&'; zLine[k++] = 'l'; zLine[k++] = 't'; zLine[k++] = ';'; }else if( c=='&' ){ zLine[k++] = '&'; zLine[k++] = 'a'; zLine[k++] = 'm'; zLine[k++] = 'p'; zLine[k++] = ';'; }else if( c>=' ' ){ zLine[k++] = c; }else{ zLine[k++] = '.'; } }else{ break; } } zLine[k++] = '\n'; cgi_append_content(zLine, k); } } /* ** 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(); cgi_check_for_malice(); 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", "%R/shun?accept=%s&sub=1#delshun", zUuid); }else{ style_submenu_element("Shun", "%R/shun?shun=%s#addshun", zUuid); } } style_header("Hex Artifact Content"); zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid); etag_check(ETAG_HASH, zUuid); @ <h2>Artifact style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); if( g.perm.Setup ){ @ (%d(rid)):</h2> }else{ @ :</h2> } blob_zero(&downloadName); if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL; object_description(rid, objdescFlags, 0, &downloadName); style_submenu_element("Download", "%R/raw/%s?at=%T", zUuid, file_tail(blob_str(&downloadName))); @ <hr> content_get(rid, &content); if( !g.isHuman ){ /* Prevent robots from running hexdump on megabyte-sized source files ** and there by eating up lots of CPU time and bandwidth. There is ** no good reason for a robot to need a hexdump. */ @ <p>A hex dump of this file is not available. @ Please download the raw binary file and generate a hex dump yourself.</p> }else{ @ <blockquote><pre> hexdump(&content); @ </pre></blockquote> } style_finish_page(); } /* ** Look for "ci" and "filename" query parameters. If found, try to ** use them to extract the record ID of an artifact for the file. ** ** Also look for "fn" and "name" as an aliases for "filename". If any ** "filename" or "fn" or "name" are present but "ci" is missing, then ** use "tip" as the default value for "ci". ** ** If zNameParam is not NULL, then use that parameter as the filename ** rather than "fn" or "filename" or "name". the zNameParam is used ** for the from= and to= query parameters of /fdiff. */ int artifact_from_ci_and_filename(const char *zNameParam){ const char *zFilename; const char *zCI; int cirid; Manifest *pManifest; ManifestFile *pFile; int rid = 0; if( zNameParam ){ zFilename = P(zNameParam); }else{ zFilename = P("filename"); if( zFilename==0 ){ zFilename = P("fn"); } if( zFilename==0 ){ zFilename = P("name"); } } if( zFilename==0 ) return 0; zCI = PD("ci", "tip"); cirid = name_to_typed_rid(zCI, "ci"); if( cirid<=0 ) return 0; 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 ){ rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); break; } } manifest_destroy(pManifest); return rid; } /* ** The "z" argument is a string that contains the text of a source ** code file and nZ is its length in bytes. This routine appends that ** text to the HTTP reply with line numbering. ** ** zName is the content's file name, if any (it may be NULL). If that ** name contains a '.' then the part after the final '.' is used as ** the X part of a "language-X" CSS class on the generated CODE block. ** ** 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 [-,.]). ** ** If includeJS is true then the JS code associated with line ** numbering is also emitted, else it is not. If this routine is ** called multiple times in a single app run, the JS is emitted only ** once. Note that when using this routine to emit Ajax responses, the ** JS should be not be included, as it will not get imported properly ** into the response's rendering. */ void output_text_with_line_numbers( const char *z, int nZ, const char *zName, const char *zLn, int includeJS ){ 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. */ int nLine = 0; /* content line count */ int nSpans = 0; /* number of distinct zLn spans */ const char *zExt = file_extension(zName); static int emittedJS = 0; /* emitted shared JS yet? */ 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 ); ++nSpans; iStart = iEnd = atoi(&zLn[i++]); }while( zLn[i] && iStart && iEnd ); } /*cgi_printf("<!-- ln span count=%d -->", nSpans);*/ cgi_append_content("<table class='numbered-lines'><tbody>" "<tr><td class='line-numbers'><pre>", -1); iStart = iEnd = 0; count_lines(z, nZ, &nLine); for( n=1 ; n<=nLine; ++n ){ const char * zAttr = ""; const char * zId = ""; if(nSpans>0 && iEnd==0){/*Grab the next range of zLn marking*/ db_prepare(&q, "SELECT iStart, iEnd FROM lnos " "WHERE iStart >= %d ORDER BY iStart", n); if( db_step(&q)==SQLITE_ROW ){ iStart = db_column_int(&q, 0); iEnd = db_column_int(&q, 1); if(!iTop){ iTop = iStart - 15 + (iEnd-iStart)/4; if( iTop>iStart - 2 ) iTop = iStart-2; } }else{ /* Note that overlapping multi-spans, e.g. 10-15+12-20, can cause us to miss a row. */ iStart = iEnd = 0; } db_finalize(&q); --nSpans; /*cgi_printf("<!-- iStart=%d, iEnd=%d -->", iStart, iEnd);*/ } if(n==iTop) { zId = " id='scrollToMe'"; } if(n==iStart){/*Figure out which CSS class(es) this line needs...*/ if(n==iEnd){ zAttr = " class='selected-line start end'"; iEnd = 0; }else{ zAttr = " class='selected-line start'"; } iStart = 0; }else if(n==iEnd){ zAttr = " class='selected-line end'"; iEnd = 0; }else if( n>iStart && n<iEnd ){ zAttr = " class='selected-line'"; } cgi_printf("<span%s%s>%6d</span>\n", zId, zAttr, n) /* ^^^ explicit \n is necessary for text-mode browsers. */; } cgi_append_content("</pre></td><td class='file-content'><pre>",-1); if(zExt && *zExt){ cgi_printf("<code class='language-%h'>",zExt); }else{ cgi_append_content("<code>", -1); } cgi_printf("%z", htmlize(z, nZ)); CX("</code></pre></td></tr></tbody></table>\n"); if(includeJS && !emittedJS){ emittedJS = 1; if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ builtin_request_js("scroll.js"); } builtin_fossil_js_bundle_or("numbered-lines", NULL); } } /* ** COMMAND: test-line-numbers ** ** Usage: %fossil test-line-numbers FILE ?LN-SPEC? ** */ void cmd_test_line_numbers(void){ Blob content = empty_blob; const char * zLn = ""; const char * zFilename = 0; if(g.argc < 3){ usage("FILE"); }else if(g.argc>3){ zLn = g.argv[3]; } db_find_and_open_repository(0,0); zFilename = g.argv[2]; fossil_print("%s %s\n", zFilename, zLn); blob_read_from_file(&content, zFilename, ExtFILE); output_text_with_line_numbers(blob_str(&content), blob_size(&content), zFilename, zLn, 0); blob_reset(&content); fossil_print("%b\n", cgi_output_blob()); } /* ** WEBPAGE: artifact ** WEBPAGE: file ** WEBPAGE: whatis ** ** Typical usage: ** ** /artifact/HASH ** /whatis/HASH ** /file/NAME ** ** 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 - highlight lines M through N and Y through Z (inclusive) ** verbose - show more detail in the description ** download - redirect to the download (artifact page only) ** name=NAME - filename or hash as a query parameter ** filename=NAME - alternative spelling for "name=" ** fn=NAME - alternative spelling for "name=" ** ci=VERSION - The specific check-in to use with "name=" to ** identify the file. ** txt - Force display of unformatted source text ** ** The /artifact page show the complete content of a file ** identified by HASH. The /whatis page shows only a description ** of how the artifact is used. The /file page shows the most recent ** version of the file or directory called NAME, or a list of the ** top-level directory if NAME is omitted. ** ** For /artifact and /whatis, the name= query parameter can refer to ** either the name of a file, or an artifact hash. If the ci= query ** parameter is also present, then name= must refer to a file name. ** If ci= is omitted, then the hash interpretation is preferred but ** if name= cannot be understood as a hash, a default "tip" value is ** used for ci=. ** ** For /file, name= can only be interpreted as a filename. As before, ** a default value of "tip" is used for ci= if ci= is omitted. */ void artifact_page(void){ int rid = 0; Blob content; const char *zMime; Blob downloadName; int renderAsWiki = 0; int renderAsHtml = 0; int renderAsSvg = 0; int objType; int asText; const char *zUuid = 0; u32 objdescFlags = OBJDESC_BASE; int descOnly = fossil_strcmp(g.zPath,"whatis")==0; int isFile = fossil_strcmp(g.zPath,"file")==0; const char *zLn = P("ln"); const char *zName = P("name"); const char *zCI = P("ci"); HQuery url; char *zCIUuid = 0; int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */ int isBranchCI = 0; /* ci= refers to a branch name */ char *zHeader = 0; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } cgi_check_for_malice(); style_set_current_feature("artifact"); /* Capture and normalize the name= and ci= query parameters */ if( zName==0 ){ zName = P("filename"); if( zName==0 ){ zName = P("fn"); } } if( zCI && strlen(zCI)==0 ){ zCI = 0; } if( zCI && name_to_uuid2(zCI, "ci", &zCIUuid) && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0 ){ isSymbolicCI = 1; isBranchCI = branch_includes_uuid(zCI, zCIUuid); } /* The name= query parameter (or at least one of its alternative ** spellings) is required. Except for /file, show a top-level ** directory listing if name= is omitted. */ if( zName==0 ){ if( isFile ){ if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); page_tree(); return; } style_header("Missing name= query parameter"); @ The name= query parameter is missing style_finish_page(); return; } url_initialize(&url, g.zPath); url_add_parameter(&url, "name", zName); url_add_parameter(&url, "ci", zCI); /* no-op if zCI is NULL */ if( zCI==0 && !isFile ){ /* If there is no ci= query parameter, then prefer to interpret ** name= as a hash for /artifact and /whatis. But not for /file. ** For /file, a name= without a ci= will prefer to use the default ** "tip" value for ci=. */ rid = name_to_rid(zName); } if( rid==0 ){ rid = artifact_from_ci_and_filename(0); } if( rid==0 ){ /* Artifact not found */ if( isFile ){ Stmt q; /* For /file, also check to see if name= refers to a directory, ** and if so, do a listing for that directory */ int nName = (int)strlen(zName); if( nName && zName[nName-1]=='/' ) nName--; if( db_exists( "SELECT 1 FROM filename" " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';", nName, zName, nName+1, nName, zName ) ){ if( P("ci")==0 ) cgi_set_query_parameter("ci","tip"); page_tree(); return; } /* No directory found, look for an historic version of the file ** that was subsequently deleted. */ db_prepare(&q, "SELECT fid, uuid FROM mlink, filename, event, blob" " WHERE filename.name=%Q" " AND mlink.fnid=filename.fnid AND mlink.fid>0" " AND event.objid=mlink.mid" " AND blob.rid=mlink.mid" " ORDER BY event.mtime DESC", zName ); if( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); zCI = fossil_strdup(db_column_text(&q, 1)); zCIUuid = fossil_strdup(zCI); url_add_parameter(&url, "ci", zCI); } db_finalize(&q); if( rid==0 ){ style_header("No such file"); @ File '%h(zName)' does not exist in this repository. } }else{ style_header("No such artifact"); @ Artifact '%h(zName)' does not exist in this repository. } if( rid==0 ){ style_finish_page(); return; } } if( descOnly || P("verbose")!=0 ){ url_add_parameter(&url, "verbose", "1"); objdescFlags |= OBJDESC_DETAIL; } zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); etag_check(ETAG_HASH, zUuid); asText = P("txt")!=0; if( isFile ){ if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){ zCI = "tip"; @ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a> @ from the %z(href("%R/info/tip"))latest check-in</a></h2> }else{ const char *zPath; Blob path; blob_zero(&path); hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO); zPath = blob_str(&path); @ <h2>File %s(zPath) artifact \ style_copy_button(1,"hash-fid",0,0,"%z%S</a> ", href("%R/info/%s",zUuid),zUuid); if( isBranchCI ){ @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2> }else if( isSymbolicCI ){ @ part of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2> }else{ @ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2> } blob_reset(&path); } style_submenu_element("Artifact", "%R/artifact/%S", zUuid); zMime = mimetype_from_name(zName); style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T", zName, zCI); style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T", zName, zCI); style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName); blob_init(&downloadName, zName, -1); objType = OBJTYPE_CONTENT; }else{ @ <h2>Artifact style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); if( g.perm.Setup ){ @ (%d(rid)):</h2> }else{ @ :</h2> } blob_zero(&downloadName); if( asText ) objdescFlags &= ~OBJDESC_BASE; objType = object_description(rid, objdescFlags, (isFile?zName:0), &downloadName); zMime = mimetype_from_name(blob_str(&downloadName)); } if( !descOnly && P("download")!=0 ){ cgi_redirectf("%R/raw/%s?at=%T", db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid), file_tail(blob_str(&downloadName))); /*NOTREACHED*/ } 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", "%R/shun?accept=%s&sub=1#accshun", zUuid); }else{ style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid); } } if( isFile ){ if( isSymbolicCI ){ zHeader = mprintf("%s at %s", file_tail(zName), zCI); style_set_current_page("doc/%t/%T", zCI, zName); }else if( zCIUuid && zCIUuid[0] ){ zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid); style_set_current_page("doc/%S/%T", zCIUuid, zName); }else{ zHeader = mprintf("%s", file_tail(zName)); style_set_current_page("doc/tip/%T", zName); } }else if( descOnly ){ zHeader = mprintf("Artifact Description [%S]", zUuid); }else{ zHeader = mprintf("Artifact [%S]", zUuid); } style_header("%s", zHeader); fossil_free(zCIUuid); fossil_free(zHeader); if( !isFile && g.perm.Admin ){ Stmt q; db_prepare(&q, "SELECT coalesce(user.login,rcvfrom.uid)," " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr" " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid" " WHERE blob.rid=%d" " AND rcvfrom.rcvid=blob.rcvid;", rid); while( db_step(&q)==SQLITE_ROW ){ const char *zUser = db_column_text(&q,0); const char *zDate = db_column_text(&q,1); const char *zIp = db_column_text(&q,2); @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p> } db_finalize(&q); } style_submenu_element("Download", "%R/raw/%s?at=%T", zUuid, file_tail(zName)); if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){ style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid); } if( zMime ){ if( fossil_strcmp(zMime, "text/html")==0 ){ if( asText ){ style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0)); }else{ renderAsHtml = 1; style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0)); } }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 || fossil_strcmp(zMime, "text/x-markdown")==0 || fossil_strcmp(zMime, "text/x-pikchr")==0 ){ if( asText ){ style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki", "%s", url_render(&url, "txt", 0, 0, 0)); }else{ renderAsWiki = 1; style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0)); } }else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){ if( asText ){ style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0)); }else{ renderAsSvg = 1; style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0)); } } if( fileedit_is_editable(zName) ){ style_submenu_element("Edit", "%R/fileedit?filename=%T&checkin=%!S", zName, zCI); } } if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){ style_submenu_element("Parsed", "%R/info/%s", zUuid); } if( descOnly ){ style_submenu_element("Content", "%R/artifact/%s", zUuid); }else{ @ <hr> content_get(rid, &content); if( renderAsWiki ){ safe_html_context(DOCSRC_FILE); wiki_render_by_mimetype(&content, zMime); document_emit_js(); }else if( renderAsHtml ){ @ <iframe src="%R/raw/%s(zUuid)" @ width="100%%" frameborder="0" marginwidth="0" marginheight="0" @ sandbox="allow-same-origin" id="ifm1"> @ </iframe> @ <script nonce="%h(style_nonce())">/* info.c:%d(__LINE__) */ @ document.getElementById("ifm1").addEventListener("load", @ function(){ @ this.height=this.contentDocument.documentElement.scrollHeight + 75; @ } @ ); @ </script> }else if( renderAsSvg ){ @ <object type="image/svg+xml" data="%R/raw/%s(zUuid)"></object> }else{ const char *zContentMime; style_submenu_element("Hex", "%R/hexdump?name=%s", zUuid); if( zLn==0 || atoi(zLn)==0 ){ style_submenu_checkbox("ln", "Line Numbers", 0, 0); } blob_to_utf8_no_bom(&content, 0); zContentMime = mimetype_from_content(&content); if( zMime==0 ) zMime = zContentMime; @ <blockquote class="file-content"> if( zContentMime==0 ){ const char *z, *zFileName, *zExt; z = blob_str(&content); zFileName = db_text(0, "SELECT name FROM mlink, filename" " WHERE filename.fnid=mlink.fnid" " AND mlink.fid=%d", rid); zExt = zFileName ? file_extension(zFileName) : 0; if( zLn ){ output_text_with_line_numbers(z, blob_size(&content), zFileName, zLn, 1); }else if( zExt && zExt[0] ){ @ <pre> @ <code class="language-%s(zExt)">%h(z)</code> @ </pre> }else{ @ <pre> @ %h(z) @ </pre> } }else if( strncmp(zMime, "image/", 6)==0 ){ @ <p>(file is %d(blob_size(&content)) bytes of image data)</i></p> @ <p><img src="%R/raw/%s(zUuid)?m=%s(zMime)"></p> style_submenu_element("Image", "%R/raw/%s?m=%s", zUuid, zMime); }else if( strncmp(zMime, "audio/", 6)==0 ){ @ <p>(file is %d(blob_size(&content)) bytes of sound data)</i></p> @ <audio controls src="%R/raw/%s(zUuid)?m=%s(zMime)"> @ (Not supported by this browser) @ </audio> }else{ @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i> } @ </blockquote> } } style_finish_page(); } /* ** 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[HNAME_MAX+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(); } cgi_check_for_malice(); 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", "%R/shun?accept=%s&sub=1#accshun", zUuid); }else{ style_submenu_element("Shun", "%R/shun?shun=%s#addshun", zUuid); } } pTktChng = manifest_get(rid, CFTYPE_TICKET, 0); if( pTktChng==0 ) fossil_redirect_home(); zDate = db_text(0, "SELECT datetime(%.12f,toLocal())", pTktChng->rDate); sqlite3_snprintf(sizeof(zTktName), zTktName, "%s", pTktChng->zTicketUuid); 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('t', rid); } } zTktTitle = db_table_has_column("repository", "ticket", "title" ) ? db_text("(No title)", "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName) : 0; style_set_current_feature("tinfo"); style_header("Ticket Change Details"); style_submenu_element("Raw", "%R/artifact/%s", zUuid); style_submenu_element("History", "%R/tkthistory/%s#%S", zTktName,zUuid); style_submenu_element("Page", "%R/tktview/%t", zTktName); style_submenu_element("Timeline", "%R/tkttimeline/%t", zTktName); if( P("plaintext") ){ style_submenu_element("Formatted", "%R/info/%s", zUuid); }else{ style_submenu_element("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_www(rid); @ <tr><th>Ticket:</th> @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a> if( zTktTitle ){ @<br>%h(zTktTitle) } @</td></tr> @ <tr><th>User & Date:</th><td> hyperlink_to_user(pTktChng->zUser, zDate, " on "); hyperlink_to_date(zDate, "</td></tr>"); @ </table> free(zDate); free(zTktTitle); if( g.perm.ModTkt && modPending ){ @ <div class="section">Moderation</div> @ <blockquote> @ <form method="POST" action="%R/tinfo/%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">Changes</div> @ <p> ticket_output_change_artifact(pTktChng, 0, 1, 0); manifest_destroy(pTktChng); style_finish_page(); } /* ** WEBPAGE: info ** URL: info/NAME ** ** The NAME argument is any valid artifact name: an artifact hash, ** a timestamp, a tag name, etc. ** ** Because NAME can match so many different things (commit artifacts, ** wiki pages, ticket comments, forum posts...) the format of the output ** page depends on the type of artifact that NAME matches. */ void info_page(void){ const char *zName; Blob uuid; int rid; int rc; int nLen; zName = P("name"); if( zName==0 ) fossil_redirect_home(); cgi_check_for_malice(); 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) ){ cgi_set_parameter_nocopy("tl","1",0); 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_finish_page(); 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_finish_page(); return; } if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ ci_page(); }else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){ winfo_page(); }else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" " WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){ tinfo_page(); }else if( db_table_exists("repository","forumpost") && db_exists("SELECT 1 FROM forumpost WHERE fpid=%d", rid) ){ forumthread_page(); }else if( db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) ){ ci_page(); }else if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ ci_page(); }else if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){ ainfo_page(); }else { artifact_page(); } } /* ** Do a comment comparison. ** ** + Leading and trailing whitespace are ignored. ** + \r\n characters compare equal to \n ** ** Return true if equal and false if not equal. */ static int comment_compare(const char *zA, const char *zB){ if( zA==0 ) zA = ""; if( zB==0 ) zB = ""; while( fossil_isspace(zA[0]) ) zA++; while( fossil_isspace(zB[0]) ) zB++; while( zA[0] && zB[0] ){ if( zA[0]==zB[0] ){ zA++; zB++; continue; } if( zA[0]=='\r' && zA[1]=='\n' && zB[0]=='\n' ){ zA += 2; zB++; continue; } if( zB[0]=='\r' && zB[1]=='\n' && zA[0]=='\n' ){ zB += 2; zA++; continue; } return 0; } while( fossil_isspace(zB[0]) ) zB++; while( fossil_isspace(zA[0]) ) zA++; return zA[0]==0 && zB[0]==0; } /* ** The following methods operate on the newtags temporary table ** that is used to collect various changes to be added to a control ** artifact for a check-in edit. */ static void init_newtags(void){ db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)"); } static void change_special( const char *zName, /* Name of the special tag */ const char *zOp, /* Operation prefix (e.g. +,-,*) */ const char *zValue /* Value of the tag */ ){ db_multi_exec("REPLACE INTO newtags VALUES(%Q,'%q',%Q)", zName, zOp, zValue); } static void change_sym_tag(const char *zTag, const char *zOp){ db_multi_exec("REPLACE INTO newtags VALUES('sym-%q',%Q,NULL)", zTag, zOp); } static void cancel_special(const char *zTag){ change_special(zTag,"-",0); } static void add_color(const char *zNewColor, int fPropagateColor){ change_special("bgcolor",fPropagateColor ? "*" : "+", zNewColor); } static void cancel_color(void){ change_special("bgcolor","-",0); } static void add_comment(const char *zNewComment){ change_special("comment","+",zNewComment); } static void add_date(const char *zNewDate){ change_special("date","+",zNewDate); } static void add_user(const char *zNewUser){ change_special("user","+",zNewUser); } static void add_tag(const char *zNewTag){ change_sym_tag(zNewTag,"+"); } static void cancel_tag(int rid, const char *zCancelTag){ if( db_exists("SELECT 1 FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagtype>0" " AND tagxref.tagid=tag.tagid AND tagname='sym-%q'", rid, zCancelTag) ) change_sym_tag(zCancelTag,"-"); } static void hide_branch(void){ change_special("hidden","*",0); } static void close_leaf(int rid){ change_special("closed",is_a_leaf(rid)?"+":"*",0); } static void change_branch(int rid, const char *zNewBranch){ db_multi_exec( "REPLACE INTO newtags " " SELECT tagname, '-', NULL FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagtype==2" " AND tagname GLOB 'sym-*'" " AND tag.tagid=tagxref.tagid", rid ); change_special("branch","*",zNewBranch); change_sym_tag(zNewBranch,"*"); } /* ** The apply_newtags method is called after all newtags have been added ** and the control artifact is completed and then written to the DB. */ static void apply_newtags( Blob *ctrl, int rid, const char *zUuid, const char *zUserOvrd, /* The user name on the control artifact */ int fDryRun /* Print control artifact, but make no changes */ ){ Stmt q; int nChng = 0; db_prepare(&q, "SELECT tag, prefix, value FROM newtags" " ORDER BY prefix || tag"); while( db_step(&q)==SQLITE_ROW ){ const char *zTag = db_column_text(&q, 0); const char *zPrefix = db_column_text(&q, 1); const char *zValue = db_column_text(&q, 2); nChng++; if( zValue ){ blob_appendf(ctrl, "T %s%F %s %F\n", zPrefix, zTag, zUuid, zValue); }else{ blob_appendf(ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid); } } db_finalize(&q); if( nChng>0 ){ int nrid; Blob cksum; if( zUserOvrd && zUserOvrd[0] ){ blob_appendf(ctrl, "U %F\n", zUserOvrd); }else{ blob_appendf(ctrl, "U %F\n", login_name()); } md5sum_blob(ctrl, &cksum); blob_appendf(ctrl, "Z %b\n", &cksum); if( fDryRun ){ assert( g.isHTTP==0 ); /* Only print control artifact in console mode. */ fossil_print("%s", blob_str(ctrl)); blob_reset(ctrl); }else{ db_begin_transaction(); g.markPrivate = content_is_private(rid); nrid = content_put(ctrl); manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS); db_end_transaction(0); } assert( blob_is_reset(ctrl) ); } } /* ** This method checks that the date can be parsed. ** Returns 1 if datetime() can validate, 0 otherwise. */ int is_datetime(const char* zDate){ return db_int(0, "SELECT datetime(%Q) NOT NULL", zDate); } /* ** WEBPAGE: ci_edit ** ** Edit a check-in. (Check-ins are immutable and do not really change. ** This page really creates supplemental tags that affect the display ** of the check-in.) ** ** Query parameters: ** ** rid=INTEGER Record ID of the check-in to edit (REQUIRED) ** ** POST parameters after pressing "Preview", "Cancel", or "Apply": ** ** c=TEXT New check-in comment ** u=TEXT New user name ** newclr Apply a background color ** clr=TEXT New background color (only if newclr) ** pclr Propagate new background color (only if newclr) ** dt=TEXT New check-in date/time (ISO8610 format) ** newtag Add a new tag to the check-in ** tagname=TEXT Name of the new tag to be added (only if newtag) ** newbr Put the check-in on a new branch ** brname=TEXT Name of the new branch (only if newbr) ** close Close this check-in ** hide Hide this check-in ** cNNN Cancel tag with tagid=NNN ** ** cancel Cancel the edit. Return to the check-in view ** preview Show a preview of the edited check-in comment ** apply Apply changes */ void ci_edit_page(void){ int rid; const char *zComment; /* Current comment on the check-in */ const char *zNewComment; /* Revised check-in comment */ const char *zUser; /* Current user for the check-in */ const char *zNewUser; /* Revised user */ const char *zDate; /* Current date of the check-in */ const char *zNewDate; /* Revised check-in date */ const char *zNewColorFlag; /* "checked" if "Change color" is checked */ const char *zColor; /* Current background color */ const char *zNewColor; /* Revised background color */ const char *zNewTagFlag; /* "checked" if "Add tag" is checked */ const char *zNewTag; /* Name of the new tag */ const char *zNewBrFlag; /* "checked" if "New branch" is checked */ const char *zNewBranch; /* Name of the new branch */ const char *zCloseFlag; /* "checked" if "Close" is checked */ const char *zHideFlag; /* "checked" if "Hide" is checked */ 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("%R/ci/%S", zUuid); } if( g.perm.Setup ) zChngTime = P("chngtime"); zNewComment = PD("c",zComment); zUser = db_text(0, "SELECT coalesce(euser,user)" " FROM event WHERE objid=%d", rid); if( zUser==0 ) fossil_redirect_home(); zNewUser = PDT("u",zUser); zDate = db_text(0, "SELECT datetime(mtime)" " FROM event WHERE objid=%d", rid); if( zDate==0 ) fossil_redirect_home(); zNewDate = PDT("dt",zDate); zColor = db_text("", "SELECT bgcolor" " FROM event WHERE objid=%d", rid); zNewColor = PDT("clr",zColor); fPropagateColor = db_int(0, "SELECT tagtype FROM tagxref" " WHERE rid=%d AND tagid=%d", rid, TAG_BGCOLOR)==2; fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor; zNewColorFlag = P("newclr") ? " checked" : ""; 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") && cgi_csrf_safe(2) ){ Blob ctrl; char *zNow; blob_zero(&ctrl); zNow = date_in_standard_format(zChngTime ? zChngTime : "now"); blob_appendf(&ctrl, "D %s\n", zNow); init_newtags(); if( zNewColorFlag[0] && zNewColor[0] && (fPropagateColor!=fNewPropagateColor || fossil_strcmp(zColor,zNewColor)!=0) ){ add_color(zNewColor,fNewPropagateColor); } if( comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment); if( fossil_strcmp(zDate,zNewDate)!=0 ) add_date(zNewDate); if( fossil_strcmp(zUser,zNewUser)!=0 ) add_user(zNewUser); db_prepare(&q, "SELECT tag.tagid, tagname FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid", rid ); while( db_step(&q)==SQLITE_ROW ){ int tagid = db_column_int(&q, 0); const char *zTag = db_column_text(&q, 1); char zLabel[30]; sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid); if( P(zLabel) ) cancel_special(zTag); } db_finalize(&q); if( zHideFlag[0] ) hide_branch(); if( zCloseFlag[0] ) close_leaf(rid); if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag); if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch); apply_newtags(&ctrl, rid, zUuid, 0, 0); cgi_redirectf("%R/ci/%S", zUuid); } blob_zero(&comment); blob_append(&comment, zNewComment, -1); zUuid[10] = 0; style_header("Edit Check-in [%s]", zUuid); if( P("preview") ){ Blob suffix; int nTag = 0; @ <b>Preview:</b> @ <blockquote> @ <table border=0> if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){ @ <tr><td style="background-color: %h(zNewColor);"> }else if( zColor[0] ){ @ <tr><td style="background-color: %h(zColor);"> }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 ){ const char *zTag = db_column_text(&q, 0); if( nTag==0 ){ blob_appendf(&suffix, ", tags: %h", zTag); }else{ blob_appendf(&suffix, ", %h", zTag); } nTag++; } db_finalize(&q); blob_appendf(&suffix, ")"); @ %s(blob_str(&suffix)) @ </td></tr></table> if( zChngTime ){ @ <p>The timestamp on the tag used to make the changes above @ 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"); @ <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> @ <tr><th align="right" valign="top">Comment:</th> @ <td valign="top"> @ <textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea> @ </td></tr> @ <tr><th align="right" valign="top">Check-in Time:</th> @ <td valign="top"> @ <input type="text" name="dt" size="20" value="%h(zNewDate)"> @ </td></tr> if( zChngTime ){ @ <tr><th align="right" valign="top">Timestamp of this change:</th> @ <td valign="top"> @ <input type="text" name="chngtime" size="20" value="%h(zChngTime)"> @ </td></tr> } @ <tr><th align="right" valign="top">Background Color:</th> @ <td valign="top"> @ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag)> @ Change background color: \ @ <input type='color' name='clr'\ @ value='%s(zNewColor[0]?zNewColor:"#808080")'></label></div> @ <div><label> if( fNewPropagateColor ){ @ <input type="checkbox" name="pclr" checked="checked"> }else{ @ <input type="checkbox" name="pclr"> } @ Propagate color to descendants</label></div> @ <div class='font-size-80'>Be aware that fixed background @ colors will not interact well with all available skins. @ It is recommended that Fossil be allowed to select these @ colors automatically so that it can take the skin's @ preferences into account.</div> @ </td></tr> @ <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 size="15" name="tagname" id="tagname" value="%h(zNewTag)"> 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", 0); } 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" \ @ data-branch='%h(zBranchName)'%s(zNewBrFlag)> @ Starting from this check-in, rename the branch to:</label> @ <input id="brname" type="text" style="width:15;" name="brname" \ @ value="%h(zNewBranch)"></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="cancel" value="Cancel"> @ <input type="submit" name="preview" value="Preview"> if( P("preview") ){ @ <input type="submit" name="apply" value="Apply Changes"> } @ </td></tr> @ </table> @ </div></form> builtin_request_js("ci_edit.js"); style_finish_page(); } /* ** Prepare an ammended commit comment. Let the user modify it using the ** editor specified in the global_config table or either ** the VISUAL or EDITOR environment variable. ** ** Store the final commit comment in pComment. pComment is assumed ** to be uninitialized - any prior content is overwritten. ** ** Use zInit to initialize the check-in comment so that the user does ** not have to retype. */ static void prepare_amend_comment( Blob *pComment, const char *zInit, const char *zUuid ){ Blob prompt; #if defined(_WIN32) || defined(__CYGWIN__) int bomSize; const unsigned char *bom = get_utf8_bom(&bomSize); blob_init(&prompt, (const char *) bom, bomSize); if( zInit && zInit[0]){ blob_append(&prompt, zInit, -1); } #else blob_init(&prompt, zInit, -1); #endif blob_append(&prompt, "\n# Enter a new comment for check-in ", -1); if( zUuid && zUuid[0] ){ blob_append(&prompt, zUuid, -1); } blob_append(&prompt, ".\n# Lines beginning with a # are ignored.\n", -1); prompt_for_user_comment(pComment, &prompt); blob_reset(&prompt); } #define AMEND_USAGE_STMT "HASH OPTION ?OPTION ...?" /* ** COMMAND: amend ** ** Usage: %fossil amend HASH OPTION ?OPTION ...? ** ** Amend the tags on check-in HASH to change how it displays in the timeline. ** ** Options: ** --author USER Make USER the author for check-in ** -m|--comment COMMENT Make COMMENT the check-in comment ** -M|--message-file FILE Read the amended comment from FILE ** -e|--edit-comment Launch editor to revise comment ** --date DATETIME Make DATETIME the check-in time ** --bgcolor COLOR Apply COLOR to this check-in ** --branchcolor COLOR Apply and propagate COLOR to the branch ** --tag TAG Add new TAG to this check-in ** --cancel TAG Cancel TAG from this check-in ** --branch NAME Rename branch of check-in to NAME ** --hide Hide branch starting from this check-in ** --close Mark this "leaf" as closed ** -n|--dry-run Print control artifact, but make no changes ** --date-override DATETIME Set the change time on the control artifact ** --user-override USER Set the user name on the control artifact ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. */ void ci_amend_cmd(void){ int rid; const char *zComment; /* Current comment on the check-in */ const char *zNewComment; /* Revised check-in comment */ const char *zComFile; /* Filename from which to read comment */ const char *zUser; /* Current user for the check-in */ const char *zNewUser; /* Revised user */ const char *zDate; /* Current date of the check-in */ const char *zNewDate; /* Revised check-in date */ const char *zColor; const char *zNewColor; const char *zNewBrColor; const char *zNewBranch; const char **pzNewTags = 0; const char **pzCancelTags = 0; int fClose; /* True if leaf should be closed */ int fHide; /* True if branch should be hidden */ int fPropagateColor; /* True if color propagates before amend */ int fNewPropagateColor = 0; /* True if color propagates after amend */ int fHasHidden = 0; /* True if hidden tag already set */ int fHasClosed = 0; /* True if closed tag already set */ int fEditComment; /* True if editor to be used for comment */ int fDryRun; /* Print control artifact, make no changes */ const char *zChngTime; /* The change time on the control artifact */ const char *zUserOvrd; /* The user name on the control artifact */ const char *zUuid; Blob ctrl; Blob comment; char *zNow; int nTags, nCancels; int i; Stmt q; fEditComment = find_option("edit-comment","e",0)!=0; zNewComment = find_option("comment","m",1); zComFile = find_option("message-file","M",1); zNewBranch = find_option("branch",0,1); zNewColor = find_option("bgcolor",0,1); zNewBrColor = find_option("branchcolor",0,1); if( zNewBrColor ){ zNewColor = zNewBrColor; fNewPropagateColor = 1; } zNewDate = find_option("date",0,1); zNewUser = find_option("author",0,1); pzNewTags = find_repeatable_option("tag",0,&nTags); pzCancelTags = find_repeatable_option("cancel",0,&nCancels); fClose = find_option("close",0,0)!=0; fHide = find_option("hide",0,0)!=0; fDryRun = find_option("dry-run","n",0)!=0; zChngTime = find_option("date-override",0,1); if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1); zUserOvrd = find_option("user-override",0,1); db_find_and_open_repository(0,0); user_select(); verify_all_options(); if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT); rid = name_to_typed_rid(g.argv[2], "ci"); if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in"); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); if( zUuid==0 ) fossil_fatal("Unable to find artifact hash"); zComment = db_text(0, "SELECT coalesce(ecomment,comment)" " FROM event WHERE objid=%d", rid); zUser = db_text(0, "SELECT coalesce(euser,user)" " FROM event WHERE objid=%d", rid); zDate = db_text(0, "SELECT datetime(mtime)" " FROM event WHERE objid=%d", rid); zColor = db_text("", "SELECT bgcolor" " FROM event WHERE objid=%d", rid); fPropagateColor = db_int(0, "SELECT tagtype FROM tagxref" " WHERE rid=%d AND tagid=%d", rid, TAG_BGCOLOR)==2; fNewPropagateColor = zNewColor && zNewColor[0] ? fNewPropagateColor : fPropagateColor; db_prepare(&q, "SELECT tag.tagid FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid", rid ); while( db_step(&q)==SQLITE_ROW ){ int tagid = db_column_int(&q, 0); if( tagid == TAG_CLOSED ){ fHasClosed = 1; }else if( tagid==TAG_HIDDEN ){ fHasHidden = 1; }else{ continue; } } db_finalize(&q); blob_zero(&ctrl); zNow = date_in_standard_format(zChngTime && zChngTime[0] ? zChngTime : "now"); blob_appendf(&ctrl, "D %s\n", zNow); init_newtags(); if( zNewColor && zNewColor[0] && (fPropagateColor!=fNewPropagateColor || fossil_strcmp(zColor,zNewColor)!=0) ){ add_color( mprintf("%s%s", (zNewColor[0]!='#' && validate16(zNewColor,strlen(zNewColor)) && (strlen(zNewColor)==6 || strlen(zNewColor)==3)) ? "#" : "", zNewColor ), fNewPropagateColor ); } if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){ cancel_color(); } if( fEditComment ){ prepare_amend_comment(&comment, zComment, zUuid); zNewComment = blob_str(&comment); }else if( zComFile ){ blob_zero(&comment); blob_read_from_file(&comment, zComFile, ExtFILE); blob_to_utf8_no_bom(&comment, 1); zNewComment = blob_str(&comment); } if( zNewComment && zNewComment[0] && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment); if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){ if( is_datetime(zNewDate) ){ add_date(zNewDate); }else{ fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS"); } } if( zNewUser && zNewUser[0] && fossil_strcmp(zUser,zNewUser)!=0 ){ add_user(zNewUser); } if( pzNewTags!=0 ){ for(i=0; i<nTags; i++){ if( pzNewTags[i] && pzNewTags[i][0] ) add_tag(pzNewTags[i]); } fossil_free((void *)pzNewTags); } if( pzCancelTags!=0 ){ for(i=0; i<nCancels; i++){ if( pzCancelTags[i] && pzCancelTags[i][0] ) cancel_tag(rid,pzCancelTags[i]); } fossil_free((void *)pzCancelTags); } if( fHide && !fHasHidden ) hide_branch(); if( fClose && !fHasClosed ) close_leaf(rid); if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch); apply_newtags(&ctrl, rid, zUuid, zUserOvrd, fDryRun); if( fDryRun==0 ){ show_common_info(rid, "hash:", 1, 0); } if( g.localOpen ){ manifest_to_disk(rid); } } /* ** COMMAND: test-symlink-list ** ** Show all symlinks that have been checked into a Fossil repository. ** ** This command does a linear scan through all check-ins and so might take ** several seconds on a large repository. */ void test_symlink_list_cmd(void){ Stmt q; db_find_and_open_repository(0,0); add_content_sql_commands(g.db); db_prepare(&q, "SELECT min(date(e.mtime))," " b.uuid," " f.filename," " content(f.uuid)" " FROM event AS e, blob AS b, files_of_checkin(b.uuid) AS f" " WHERE e.type='ci'" " AND b.rid=e.objid" " AND f.perm LIKE '%%l%%'" " GROUP BY 3, 4" " ORDER BY 1 DESC" ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%s %.16s %s -> %s\n", db_column_text(&q,0), db_column_text(&q,1), db_column_text(&q,2), db_column_text(&q,3)); } db_finalize(&q); } #if INTERFACE /* ** Description of a check-in relative to an earlier, tagged check-in. */ typedef struct CommitDescr { char *zRelTagname; /* Tag name on the relative check-in */ int nCommitsSince; /* Number of commits since then */ char *zCommitHash; /* Hash of the described check-in */ int isDirty; /* Working directory has uncommitted changes */ } CommitDescr; #endif /* ** Describe the check-in given by 'zName', and possibly matching 'matchGlob', ** relative to an earlier, tagged check-in. Use 'descr' for the output. ** ** Finds the closest ancestor (ignoring merge-ins) that has a non-propagating ** label tag and the number of steps backwards that we had to search in ** order to find that tag. Tags applied to more than one check-in are ignored. ** ** Return values: ** 0: ok ** -1: zName does not resolve to a commit ** -2: zName resolves to more than a commit ** -3: no ancestor commit with a fitting non-propagating tag found */ int describe_commit( const char *zName, /* Name of the commit to be described */ const char *matchGlob, /* Glob pattern for the tag */ CommitDescr *descr /* Write the description here */ ){ int rid; /* rid for zName */ const char *zUuid; /* Hash of rid */ int nRet = 0; /* Value to be returned */ Stmt q; /* Query for tagged ancestors */ rid = symbolic_name_to_rid(zName, "ci"); /* only commits */ if( rid<=0 ){ /* Commit does not exist or is ambiguous */ descr->zRelTagname = mprintf(""); descr->nCommitsSince = -1; descr->zCommitHash = mprintf(""); descr->isDirty = -1; return (rid-1); } zUuid = rid_to_uuid(rid); descr->zCommitHash = mprintf("%s", zUuid); descr->isDirty = unsaved_changes(0); db_multi_exec( "DROP TABLE IF EXISTS temp.singletonTag;" "CREATE TEMP TABLE singletonTag(" " rid INT," " tagname TEXT," " PRIMARY KEY (rid,tagname)" ") WITHOUT ROWID;" "INSERT OR IGNORE INTO singletonTag(rid, tagname)" " SELECT min(rid)," " substr(tagname,5)" " FROM tag, tagxref" " WHERE tag.tagid=tagxref.tagid" " AND tagxref.tagtype=1" " AND tagname GLOB 'sym-%q'" " GROUP BY tagname" " HAVING count(*)==1;", (matchGlob ? matchGlob : "*") ); db_prepare(&q, "WITH RECURSIVE" " ancestor(rid,mtime,tagname,n) AS (" " SELECT %d, event.mtime, singletonTag.tagname, 0 " " FROM event" " LEFT JOIN singletonTag ON singletonTag.rid=event.objid" " WHERE event.objid=%d" " UNION ALL" " SELECT plink.pid, event.mtime, singletonTag.tagname, n+1" " FROM ancestor, plink, event" " LEFT JOIN singletonTag ON singletonTag.rid=plink.pid" " WHERE plink.cid=ancestor.rid" " AND event.objid=plink.pid" " AND ancestor.tagname IS NULL" " ORDER BY mtime DESC" " )" "SELECT tagname, n" " FROM ancestor" " WHERE tagname IS NOT NULL" " ORDER BY n LIMIT 1;", rid, rid ); if( db_step(&q)==SQLITE_ROW ){ const char *lastTag = db_column_text(&q, 0); descr->zRelTagname = mprintf("%s", lastTag); descr->nCommitsSince = db_column_int(&q, 1); nRet = 0; }else{ /* no ancestor commit with a fitting singleton tag found */ descr->zRelTagname = mprintf(""); descr->nCommitsSince = -1; nRet = -3; } db_finalize(&q); return nRet; } /* ** COMMAND: describe ** ** Usage: %fossil describe ?VERSION? ?OPTIONS? ** ** Provide a description of the given VERSION by showing a non-propagating ** tag of the youngest tagged ancestor, followed by the number of commits ** since that, and the short hash of VERSION. Only tags applied to a single ** check-in are considered. ** ** If no VERSION is provided, describe the currently checked-out version. ** ** If VERSION and the found ancestor refer to the same commit, the last two ** components are omitted, unless --long is provided. When no fitting tagged ** ancestor is found, show only the short hash of VERSION. ** ** Options: ** --digits Display so many hex digits of the hash ** (default: the larger of 6 and the 'hash-digit' setting) ** -d|--dirty Show whether there are changes to be committed ** --long Always show all three components ** --match GLOB Consider only non-propagating tags matching GLOB */ void describe_cmd(void){ const char *zName; const char *zMatchGlob; const char *zDigits; int nDigits; int bDirtyFlag = 0; int bLongFlag = 0; CommitDescr descr; db_find_and_open_repository(0,0); bDirtyFlag = find_option("dirty","d",0)!=0; bLongFlag = find_option("long","",0)!=0; zMatchGlob = find_option("match", 0, 1); zDigits = find_option("digits", 0, 1); if ( !zDigits || ((nDigits=atoi(zDigits))==0) ){ nDigits = hash_digits(0); } /* We should be done with options.. */ verify_all_options(); if( g.argc<3 ){ zName = "current"; }else{ zName = g.argv[2]; } if( bDirtyFlag ){ if ( g.argc>=3 ) fossil_fatal("cannot use --dirty with specific check-in"); } switch( describe_commit(zName, zMatchGlob, &descr) ){ case -1: fossil_fatal("commit %s does not exist", zName); break; case -2: fossil_fatal("commit %s is ambiguous", zName); break; case -3: fossil_print("%.*s%s\n", nDigits, descr.zCommitHash, bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : ""); break; case 0: if( descr.nCommitsSince==0 && !bLongFlag ){ fossil_print("%s%s\n", descr.zRelTagname, bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : ""); }else{ fossil_print("%s-%d-%.*s%s\n", descr.zRelTagname, descr.nCommitsSince, nDigits, descr.zCommitHash, bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : ""); } break; default: fossil_fatal("cannot describe commit"); break; } } |
Added src/interwiki.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 | /* ** Copyright (c) 2020 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 subroutines used for recognizing, configuring, and ** handling interwiki hyperlinks. */ #include "config.h" #include "interwiki.h" /* ** If zTarget is an interwiki link, return a pointer to a URL for that ** link target in memory obtained from fossil_malloc(). If zTarget is ** not a valid interwiki link, return NULL. ** ** An interwiki link target is of the form: ** ** Code:PageName ** ** "Code" is a brief code that describes the intended target wiki. ** The code must be ASCII alpha-numeric. No symbols or non-ascii ** characters are allows. Case is ignored for the code. ** Codes are assigned by "intermap:*" entries in the CONFIG table. ** The link is only valid if there exists an entry in the CONFIG table ** that matches "intermap:Code". ** ** Each value of each intermap:Code entry in the CONFIG table is a JSON ** object with the following fields: ** ** { ** "base": Base URL for the remote site. ** "hash": Append this to "base" for Hash targets. ** "wiki": Append this to "base" for Wiki targets. ** } ** ** If the remote wiki is Fossil, then the correct value for "hash" ** is "/info/" and the correct value for "wiki" is "/wiki?name=". ** If (for example) Wikipedia is the remote, then "hash" should be ** omitted and the correct value for "wiki" is "/wiki/". ** ** PageName is link name of the target wiki. Several different forms ** of PageName are recognized. ** ** Path If PageName is empty or begins with a "/" character, then ** it is a pathname that is appended to "base". ** ** Hash If PageName is a hexadecimal string of 4 or more ** characters, then PageName is appended to "hash" which ** is then appended to "base". ** ** Wiki If PageName does not start with "/" and it is ** not a hexadecimal string of 4 or more characters, then ** PageName is appended to "wiki" and that combination is ** appended to "base". ** ** See https://en.wikipedia.org/wiki/Interwiki_links for further information ** on interwiki links. */ char *interwiki_url(const char *zTarget){ int nCode; int i; const char *zPage; int nPage; char *zUrl = 0; char *zName; static Stmt q; for(i=0; fossil_isalnum(zTarget[i]); i++){} if( zTarget[i]!=':' ) return 0; nCode = i; if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0; zPage = zTarget + nCode + 1; nPage = (int)strlen(zPage); db_static_prepare(&q, "SELECT value->>'base', value->>'hash', value->>'wiki'" " FROM config WHERE name=lower($name) AND json_valid(value)" ); zName = mprintf("interwiki:%.*s", nCode, zTarget); db_bind_text(&q, "$name", zName); while( db_step(&q)==SQLITE_ROW ){ const char *zBase = db_column_text(&q,0); if( zBase==0 || zBase[0]==0 ) break; if( nPage==0 || zPage[0]=='/' ){ /* Path */ zUrl = mprintf("%s%s", zBase, zPage); }else if( nPage>=4 && validate16(zPage,nPage) ){ /* Hash */ const char *zHash = db_column_text(&q,1); if( zHash && zHash[0] ){ zUrl = mprintf("%s%s%s", zBase, zHash, zPage); } }else{ /* Wiki */ const char *zWiki = db_column_text(&q,2); if( zWiki && zWiki[0] ){ zUrl = mprintf("%s%s%s", zBase, zWiki, zPage); } } break; } db_reset(&q); free(zName); return zUrl; } /* ** If hyperlink target zTarget begins with an interwiki tag that ought ** to be excluded from display, then return the number of characters in ** that tag. ** ** Path interwiki targets always return zero. In other words, links ** of the form: ** ** remote:/path/to/file.txt ** ** Do not have the interwiki tag removed. But Hash and Wiki links are ** transformed: ** ** src:39cb0a323f2f3fb6 -> 39cb0a323f2f3fb6 ** fossil:To Do List -> To Do List */ int interwiki_removable_prefix(const char *zTarget){ int i; for(i=0; fossil_isalnum(zTarget[i]); i++){} if( zTarget[i]!=':' ) return 0; i++; if( zTarget[i]==0 || zTarget[i]=='/' ) return 0; return i; } /* ** Verify that a name is a valid interwiki "Code". Rules: ** ** * ascii ** * alphanumeric */ static int interwiki_valid_name(const char *zName){ int i; for(i=0; zName[i]; i++){ if( !fossil_isalnum(zName[i]) ) return 0; } return 1; } /* ** COMMAND: interwiki* ** ** Usage: %fossil interwiki COMMAND ... ** ** Manage the "intermap" that defines the mapping from interwiki tags ** to complete URLs for interwiki links. ** ** > fossil interwiki delete TAG ... ** ** Delete one or more interwiki maps. ** ** > fossil interwiki edit TAG --base URL --hash PATH --wiki PATH ** ** Create an interwiki referenced call TAG. The base URL is ** the --base option, which is required. The --hash and --wiki ** paths are optional. The TAG must be lower-case alphanumeric ** and must be unique. A new entry is created if it does not ** already exit. ** ** > fossil interwiki list ** ** Show all interwiki mappings. */ void interwiki_cmd(void){ const char *zCmd; int nCmd; db_find_and_open_repository(0, 0); if( g.argc<3 ){ usage("SUBCOMMAND ..."); } zCmd = g.argv[2]; nCmd = (int)strlen(zCmd); if( strncmp(zCmd,"edit",nCmd)==0 ){ const char *zName; const char *zBase = find_option("base",0,1); const char *zHash = find_option("hash",0,1); const char *zWiki = find_option("wiki",0,1); verify_all_options(); if( g.argc!=4 ) usage("add TAG ?OPTIONS?"); zName = g.argv[3]; if( zBase==0 ){ fossil_fatal("the --base option is required"); } if( !interwiki_valid_name(zName) ){ fossil_fatal("not a valid interwiki tag: \"%s\"", zName); } db_begin_write(); db_unprotect(PROTECT_CONFIG); db_multi_exec( "REPLACE INTO config(name,value,mtime)" " VALUES('interwiki:'||lower(%Q)," " json_object('base',%Q,'hash',%Q,'wiki',%Q)," " now());", zName, zBase, zHash, zWiki ); setup_incr_cfgcnt(); db_protect_pop(); db_commit_transaction(); }else if( strncmp(zCmd, "delete", nCmd)==0 ){ int i; verify_all_options(); if( g.argc<4 ) usage("delete ID ..."); db_begin_write(); db_unprotect(PROTECT_CONFIG); for(i=3; i<g.argc; i++){ const char *zName = g.argv[i]; db_multi_exec( "DELETE FROM config WHERE name='interwiki:%q'", zName ); } setup_incr_cfgcnt(); db_protect_pop(); db_commit_transaction(); }else if( strncmp(zCmd, "list", nCmd)==0 ){ Stmt q; int n = 0; verify_all_options(); db_prepare(&q, "SELECT substr(name,11)," " value->>'base', value->>'hash', value->>'wiki'" " FROM config WHERE name glob 'interwiki:*' AND json_valid(value)" ); while( db_step(&q)==SQLITE_ROW ){ const char *zBase, *z, *zName; if( n++ ) fossil_print("\n"); zName = db_column_text(&q,0); zBase = db_column_text(&q,1); fossil_print("%-15s %s\n", zName, zBase); z = db_column_text(&q,2); if( z ){ fossil_print("%15s %s%s\n", "", zBase, z); } z = db_column_text(&q,3); if( z ){ fossil_print("%15s %s%s\n", "", zBase, z); } } db_finalize(&q); }else { fossil_fatal("unknown command \"%s\" - should be one of: " "delete edit list", zCmd); } } /* ** Append text to the "Markdown" or "Wiki" rules pages that shows ** a table of all interwiki tags available on this system. */ void interwiki_append_map_table(Blob *out){ int n = 0; Stmt q; db_prepare(&q, "SELECT substr(name,11), value->>'base'" " FROM config WHERE name glob 'interwiki:*' AND json_valid(value)" " ORDER BY name;" ); while( db_step(&q)==SQLITE_ROW ){ if( n==0 ){ blob_appendf(out, "<blockquote><table>\n"); } blob_appendf(out,"<tr><td>%h</td><td> → </td>", db_column_text(&q,0)); blob_appendf(out,"<td>%h</td></tr>\n", db_column_text(&q,1)); n++; } db_finalize(&q); if( n>0 ){ blob_appendf(out,"</table></blockquote>\n"); }else{ blob_appendf(out,"<i>None</i></blockquote>\n"); } } /* ** WEBPAGE: intermap ** ** View and modify the interwiki tag map or "intermap". ** This page is visible to administrators only. */ void interwiki_page(void){ Stmt q; int n = 0; const char *z; const char *zTag = ""; const char *zBase = ""; const char *zHash = ""; const char *zWiki = ""; char *zErr = 0; login_check_credentials(); if( !g.perm.Read && !g.perm.RdWiki && ~g.perm.RdTkt ){ login_needed(0); return; } if( g.perm.Setup && P("submit")!=0 && cgi_csrf_safe(2) ){ zTag = PT("tag"); zBase = PT("base"); zHash = PT("hash"); zWiki = PT("wiki"); if( zTag==0 || zTag[0]==0 || !interwiki_valid_name(zTag) ){ zErr = mprintf("Not a valid interwiki tag name: \"%s\"", zTag?zTag : ""); }else if( zBase==0 || zBase[0]==0 ){ db_unprotect(PROTECT_CONFIG); db_multi_exec("DELETE FROM config WHERE name='interwiki:%q';", zTag); db_protect_pop(); }else{ if( zHash && zHash[0]==0 ) zHash = 0; if( zWiki && zWiki[0]==0 ) zWiki = 0; db_unprotect(PROTECT_CONFIG); db_multi_exec( "REPLACE INTO config(name,value,mtime)" "VALUES('interwiki:'||lower(%Q)," " json_object('base',%Q,'hash',%Q,'wiki',%Q)," " now());", zTag, zBase, zHash, zWiki); db_protect_pop(); } } style_set_current_feature("interwiki"); style_header("Interwiki Map Configuration"); @ <p>Interwiki links are hyperlink targets of the form @ <blockquote><i>Tag</i><b>:</b><i>PageName</i></blockquote> @ <p>Such links resolve to links to <i>PageName</i> on a separate server @ identified by <i>Tag</i>. The Interwiki Map or "intermap" is a mapping @ from <i>Tags</i> to complete Server URLs. db_prepare(&q, "SELECT substr(name,11)," " value->>'base', value->>'hash', value->>'wiki'" " FROM config WHERE name glob 'interwiki:*' AND json_valid(value)" ); while( db_step(&q)==SQLITE_ROW ){ if( n==0 ){ @ The current mapping is as follows: @ <ol> } @ <li><p> %h(db_column_text(&q,0)) @ <ul> @ <li> Base-URL: <tt>%h(db_column_text(&q,1))</tt> z = db_column_text(&q,2); if( z==0 ){ @ <li> Hash-path: <i>NULL</i> }else{ @ <li> Hash-path: <tt>%h(z)</tt> } z = db_column_text(&q,3); if( z==0 ){ @ <li> Wiki-path: <i>NULL</i> }else{ @ <li> Wiki-path: <tt>%h(z)</tt> } @ </ul> n++; } db_finalize(&q); if( n ){ @ </ol> }else{ @ No mappings are currently defined. } if( !g.perm.Setup ){ /* Do not show intermap editing fields to non-setup users */ style_finish_page(); return; } @ <p>To add a new mapping, fill out the form below providing a unique name @ for the tag. To edit an exist mapping, fill out the form and use the @ existing name as the tag. To delete an existing mapping, fill in the @ tag field but leave the "Base URL" field blank.</p> if( zErr ){ @ <p class="error">%h(zErr)</p> } @ <form method="POST" action="%R/intermap"> login_insert_csrf_secret(); @ <table border="0"> @ <tr><td class="form_label" id="imtag">Tag:</td> @ <td><input type="text" id="tag" aria-labeledby="imtag" name="tag" \ @ size="15" value="%h(zTag)"></td></tr> @ <tr><td class="form_label" id="imbase">Base URL:</td> @ <td><input type="text" id="base" aria-labeledby="imbase" name="base" \ @ size="70" value="%h(zBase)"></td></tr> @ <tr><td class="form_label" id="imhash">Hash-path:</td> @ <td><input type="text" id="hash" aria-labeledby="imhash" name="hash" \ @ size="20" value="%h(zHash)"> @ (use "<tt>/info/</tt>" when the target is Fossil)</td></tr> @ <tr><td class="form_label" id="imwiki">Wiki-path:</td> @ <td><input type="text" id="wiki" aria-labeledby="imwiki" name="wiki" \ @ size="20" value="%h(zWiki)"> @ (use "<tt>/wiki?name=</tt>" when the target is Fossil)</td></tr> @ <tr><td></td> @ <td><input type="submit" name="submit" value="Apply Changes"></td></tr> @ </table> @ </form> style_finish_page(); } |
Added src/json.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 | #ifdef FOSSIL_ENABLE_JSON /* ** Copyright (c) 2011-2022 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/ ** ******************************************************************************* ** ** Code for the JSON API. ** ** The JSON API's public interface is documented at: ** ** https://fossil-scm.org/fossil/doc/trunk/www/json-api/index.md ** ** 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 const FossilJsonKeys_ FossilJsonKeys = { "anonymousSeed" /*anonymousSeed*/, "authToken" /*authToken*/, "COMMAND_PATH" /*commandPath*/, "mtime" /*mtime*/, "payload" /* payload */, "requestId" /*requestId*/, "resultCode" /*resultCode*/, "resultText" /*resultText*/, "timestamp" /*timestamp*/ }; /* ** Given the current request path string, this function returns true ** if it refers to a JSON API path. i.e. if (1) it starts with /json ** or (2) g.zCmdName is "server" or "cgi" and the path starts with ** /somereponame/json. Specifically, it returns 1 in the former case ** and 2 for the latter. */ int json_request_is_json_api(const char * zPathInfo){ int rc = 0; if(zPathInfo==0){ rc = 0; }else if(0==strncmp("/json",zPathInfo,5) && (zPathInfo[5]==0 || zPathInfo[5]=='/')){ rc = 1; }else if(g.zCmdName!=0 && (0==strcmp("server",g.zCmdName) || 0==strcmp("ui",g.zCmdName) || 0==strcmp("cgi",g.zCmdName) || 0==strcmp("http",g.zCmdName)) ){ /* When running in server/cgi "directory" mode, zPathInfo is ** prefixed with the repository's name, so in order to determine ** whether or not we're really running in json mode we have to try ** a bit harder. Problem reported here: ** https://fossil-scm.org/forum/forumpost/e4953666d6 */ ReCompiled * pReg = 0; const char * zErr = re_compile(&pReg, "^/[^/]+/json(/.*)?", 0); assert(zErr==0 && "Regex compilation failed?"); if(zErr==0 && re_match(pReg, (const unsigned char *)zPathInfo, -1)){ rc = 2; } re_free(pReg); } return rc; } /* ** Returns true (non-0) if fossil appears to be running in JSON mode. ** and either has JSON POSTed input awaiting consumption or fossil is ** running in HTTP mode (in which case certain JSON data *might* be ** available via GET parameters). */ 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. */ cson_value * json_page_nyi(){ g.json.resultCode = FSL_JSON_E_NYI; return NULL; } /* ** Given a FossilJsonCodes value, it returns a string suitable for use ** as a resultCode string. Returns some unspecified non-empty string ** if errCode is not one of the FossilJsonCodes values. */ static char const * json_err_cstr( int errCode ){ switch( errCode ){ case 0: return "Success"; #define C(X,V) case FSL_JSON_E_ ## X: return V C(GENERIC,"Generic error"); C(INVALID_REQUEST,"Invalid request"); C(UNKNOWN_COMMAND,"Unknown command or subcommand"); C(UNKNOWN,"Unknown error"); C(TIMEOUT,"Timeout reached"); C(ASSERT,"Assertion failed"); C(ALLOC,"Resource allocation failed"); C(NYI,"Not yet implemented"); C(PANIC,"x"); C(MANIFEST_READ_FAILED,"Reading artifact manifest failed"); C(FILE_OPEN_FAILED,"Opening file failed"); C(AUTH,"Authentication error"); C(MISSING_AUTH,"Authentication info missing from request"); C(DENIED,"Access denied"); C(WRONG_MODE,"Request not allowed (wrong operation mode)"); C(LOGIN_FAILED,"Login failed"); C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed"); C(LOGIN_FAILED_NONAME,"Login failed - name not supplied"); C(LOGIN_FAILED_NOPW,"Login failed - password not supplied"); C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found"); C(USAGE,"Usage error"); C(INVALID_ARGS,"Invalid argument(s)"); C(MISSING_ARGS,"Missing argument(s)"); C(AMBIGUOUS_UUID,"Resource identifier is ambiguous"); C(UNRESOLVED_UUID,"Provided uuid/tag/branch could not be resolved"); C(RESOURCE_ALREADY_EXISTS,"Resource already exists"); C(RESOURCE_NOT_FOUND,"Resource not found"); C(DB,"Database error"); C(STMT_PREP,"Statement preparation failed"); C(STMT_BIND,"Statement parameter binding failed"); C(STMT_EXEC,"Statement execution/stepping failed"); C(DB_LOCKED,"Database is locked"); C(DB_NEEDS_REBUILD,"Fossil repository needs to be rebuilt"); C(DB_NOT_FOUND,"Fossil repository db file could not be found."); C(DB_NOT_VALID, "Fossil repository db file is not valid."); C(DB_NEEDS_CHECKOUT, "Command requires a local check-out."); #undef C default: return "Unknown Error"; } } /* ** Implements the cson_data_dest_f() interface and outputs the data to ** a fossil Blob object. pState must be-a initialized (Blob*), to ** which n bytes of src will be appended. **/ int cson_data_dest_Blob(void * pState, void const * src, unsigned int n){ Blob * b = (Blob*)pState; blob_append( b, (char const *)src, (int)n ) /* will die on OOM */; return 0; } /* ** Convenience wrapper around cson_output() which appends the output ** to pDest. pOpt may be NULL, in which case g.json.outOpt will be used. */ int cson_output_Blob( cson_value const * pVal, Blob * pDest, cson_output_opt const * pOpt ){ return cson_output( pVal, cson_data_dest_Blob, pDest, pOpt ? pOpt : &g.json.outOpt ); } /* ** Convenience wrapper around cson_parse() which reads its input ** from pSrc. pSrc is rewound before parsing. ** ** pInfo may be NULL. If it is not NULL then it will contain details ** about the parse state when this function returns. ** ** On success a new JSON Object or Array is returned (owned by the ** caller). On error NULL is returned. */ cson_value * cson_parse_Blob( Blob * pSrc, cson_parse_info * pInfo ){ cson_value * root = NULL; cson_parse_string( &root, blob_str(pSrc), blob_size(pSrc), NULL, pInfo ); return root; } /* ** Implements the cson_data_dest_f() interface and outputs the data to ** cgi_append_content(). pState is ignored. **/ int cson_data_dest_cgi(void * pState, void const * src, unsigned int n){ cgi_append_content( (char const *)src, (int)n ); return 0; } /* ** Returns a string in the form FOSSIL-XXXX, where XXXX is a ** left-zero-padded value of code. The returned buffer is static, and ** must be copied if needed for later. The returned value will always ** be 11 bytes long (not including the trailing NUL byte). ** ** In practice we will only ever call this one time per app execution ** when constructing the JSON response envelope, so the static buffer ** "shouldn't" be a problem. ** */ char const * json_rc_cstr( int code ){ enum { BufSize = 12 }; static char buf[BufSize] = {'F','O','S','S','I','L','-',0}; assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code."); sprintf(buf+7,"%04d", code); return buf; } /* ** Adds v to the API-internal cleanup mechanism. key is ignored ** (legacy) but might be re-introduced and "should" be a unique ** (app-wide) value. Failure to insert an item may be caused by any ** of the following: ** ** - Allocation error. ** - g.json.gc.a is NULL ** - key is NULL or empty. ** ** Returns 0 on success. ** ** Ownership of v is transfered to (or shared with) g.json.gc, and v ** will be valid until that object is cleaned up or some internal code ** incorrectly removes it from the gc (which we never do). If this ** function fails, it is fatal to the app (as it indicates an ** allocation error (more likely than not) or a serious internal error ** such as numeric overflow). */ void json_gc_add( char const * key, cson_value * v ){ int const rc = cson_array_append( g.json.gc.a, v ); assert( NULL != g.json.gc.a ); if( 0 != rc ){ cson_value_free( v ); } assert( (0==rc) && "Adding item to GC failed." ); if(0!=rc){ fprintf(stderr,"%s: FATAL: alloc error.\n", g.argv[0]) /* reminder: allocation error is the only reasonable cause of error here, provided g.json.gc.a and v are not NULL. */ ; fossil_exit(1)/*not fossil_panic() b/c it might land us somewhere where this function is called again. */; } } /* ** Returns the value of json_rc_cstr(code) as a new JSON ** string, which is owned by the caller and must eventually ** be cson_value_free()d or transfered to a JSON container. */ cson_value * json_rc_string( int code ){ return cson_value_new_string( json_rc_cstr(code), 11 ); } cson_value * json_new_string( char const * str ){ return str ? cson_value_new_string(str,strlen(str)) : NULL; } cson_value * json_new_string_f( char const * fmt, ... ){ cson_value * v; char * zStr; va_list vargs; va_start(vargs,fmt); zStr = vmprintf(fmt,vargs); va_end(vargs); v = cson_value_new_string(zStr, strlen(zStr)); fossil_free(zStr); return v; } cson_value * json_new_int( i64 v ){ return cson_value_new_integer((cson_int_t)v); } /* ** Gets a POST/POST.payload/GET/COOKIE/ENV value. The returned memory ** is owned by the g.json object (one of its sub-objects). Returns ** NULL if no match is found. ** ** ENV means the system environment (getenv()). ** ** Precedence: POST.payload, GET/COOKIE/non-JSON POST, JSON POST, ENV. ** ** FIXME: the precedence SHOULD be: GET, POST.payload, POST, COOKIE, ** ENV, but the amalgamation of the GET/POST vars makes it effectively ** impossible to do that. Since fossil only uses one cookie, cookie ** precedence isn't a real/high-priority problem. */ cson_value * json_getenv( char const * zKey ){ cson_value * rc; rc = g.json.reqPayload.o ? cson_object_get( g.json.reqPayload.o, zKey ) : NULL; if(rc){ return rc; } rc = cson_object_get( g.json.param.o, zKey ); if( rc ){ return rc; } rc = cson_object_get( g.json.post.o, zKey ); if(rc){ return rc; }else{ char const * cv = PD(zKey,NULL); if(!cv && !g.isHTTP){ /* reminder to self: in CLI mode i'd like to try find_option(zKey,NULL,XYZ) here, but we don't have a sane default for the XYZ param here. */ cv = fossil_getenv(zKey); } if(cv){/*transform it to JSON for later use.*/ /* use sscanf() to figure out if it's an int, and transform it to JSON int if it is. FIXME: use strtol(), since it has more accurate error handling. */ int intVal = -1; char endOfIntCheck; int const scanRc = sscanf(cv,"%d%c",&intVal, &endOfIntCheck) /* The %c bit there is to make sure that we don't accept 123x as a number. sscanf() returns the number of tokens successfully parsed, so an RC of 1 will be correct for "123" but "123x" will have RC==2. But it appears to not be working that way :/ */ ; if(1==scanRc){ json_setenv( zKey, cson_value_new_integer(intVal) ); }else{ rc = cson_value_new_string(cv,strlen(cv)); json_setenv( zKey, rc ); } return rc; }else{ return NULL; } } } /* ** Wrapper around json_getenv() which... ** ** If it finds a value and that value is-a JSON number or is a string ** which looks like an integer or is-a JSON bool/null then it is ** converted to an int. If none of those apply then dflt is returned. */ int json_getenv_int(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 (int)cson_value_get_integer(v); case CSON_TYPE_STRING: { char const * sv = cson_string_cstr(cson_value_get_string(v)); assert( (NULL!=sv) && "This is quite unexpected." ); return sv ? atoi(sv) : dflt; } case CSON_TYPE_BOOL: return cson_value_get_bool(v) ? 1 : 0; case CSON_TYPE_NULL: return 0; default: return dflt; } } /* ** Wrapper around json_getenv() which tries to evaluate a payload/env ** value as a boolean. Uses mostly the same logic as ** json_getenv_int(), with the addition that string values which ** either start with a digit 1..9 or the letters [tTyY] are considered ** to be true. If this function cannot find a matching key/value then ** dflt is returned. e.g. if it finds the key but the value is-a ** Object then dftl is returned. ** ** 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: { char const * sv = cson_string_cstr(cson_value_get_string(v)); assert( (NULL!=sv) && "This is quite unexpected." ); if(!*sv || ('0'==*sv)){ return 0; }else{ return ((('1'<=*sv) && ('9'>=*sv)) || ('t'==*sv) || ('T'==*sv) || ('y'==*sv) || ('Y'==*sv) ) ? 1 : 0; } } case CSON_TYPE_BOOL: return cson_value_get_bool(v) ? 1 : 0; case CSON_TYPE_NULL: return 0; default: return dflt; } } /* ** Returns the string form of a json_getenv() value, but ONLY If that ** value is-a String. Non-strings are not converted to strings for ** this purpose. Returned memory is owned by g.json or fossil and is ** valid until end-of-app or the given key is replaced in fossil's ** internals via cgi_replace_parameter() and friends or json_setenv(). */ char const * json_getenv_cstr( char const * zKey ){ return cson_value_get_cstr( json_getenv(zKey) ); } /* ** An extended form of find_option() which tries to look up a combo ** GET/POST/CLI argument. ** ** zKey must be the GET/POST parameter key. zCLILong must be the "long ** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or ** the "short form" CLI flag (if NULL, no short form is used). ** ** If argPos is >=0 and no other match is found, ** json_command_arg(argPos) is also checked. ** ** On error (no match found) NULL is returned. ** ** This ONLY works for String JSON/GET/CLI values, not JSON ** booleans and whatnot. */ char const * json_find_option_cstr2(char const * zKey, char const * zCLILong, char const * zCLIShort, int argPos){ char const * rc = NULL; assert(NULL != zKey); if(!g.isHTTP){ rc = find_option(zCLILong ? zCLILong : zKey, zCLIShort, 1); } if(!rc && fossil_has_json()){ rc = json_getenv_cstr(zKey); if(!rc && zCLIShort){ rc = cson_value_get_cstr( cson_object_get( g.json.param.o, zCLIShort) ); } } if(!rc && (argPos>=0)){ rc = json_command_arg((unsigned char)argPos); } return rc; } /* ** Short-hand form of json_find_option_cstr2(zKey,zCLILong,zCLIShort,-1). */ char const * json_find_option_cstr(char const * zKey, char const * zCLILong, char const * zCLIShort){ 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, int dflt ){ int rc = -1; if(!g.isHTTP){ if(NULL != find_option(zCLILong ? zCLILong : zKey, zCLIShort, 0)){ rc = 1; } } if((-1==rc) && fossil_has_json()){ rc = json_getenv_bool(zKey,-1); } return (-1==rc) ? dflt : rc; } /* ** The integer equivalent of json_find_option_cstr2(). ** If the option is not found, dftl is returned. */ int json_find_option_int(char const * zKey, char const * zCLILong, char const * zCLIShort, int dflt ){ enum { Magic = -1947854832 }; int rc = Magic; if(!g.isHTTP){ /* FIXME: use strtol() for better error/dflt handling. */ char const * opt = find_option(zCLILong ? zCLILong : zKey, zCLIShort, 1); if(NULL!=opt){ rc = atoi(opt); } } if(Magic==rc){ rc = json_getenv_int(zKey,Magic); } return (Magic==rc) ? dflt : rc; } /* ** Adds v to g.json.param.o using the given key. May cause any prior ** item with that key to be destroyed (depends on current reference ** count for that value). On success, transfers (or shares) ownership ** of v to (or with) g.json.param.o. On error ownership of v is not ** modified. */ int json_setenv( char const * zKey, cson_value * v ){ return cson_object_set( g.json.param.o, zKey, v ); } /* ** Guesses a RESPONSE Content-Type value based (primarily) on the ** HTTP_ACCEPT header. ** ** It will try to figure out if the client can support ** application/json, text/javascript, and will fall back to ** text/plain if it cannot figure out anything more specific. ** ** Returned memory is static and immutable, but if the environment ** changes after calling this then subsequent calls to this function ** might return different (also static/immutable) values. */ char const * json_guess_content_type(){ char const * cset; char doUtf8; cset = PD("HTTP_ACCEPT_CHARSET",NULL); doUtf8 = ((NULL == cset) || (NULL!=strstr("utf-8",cset))) ? 1 : 0; if( g.json.jsonp ){ return doUtf8 ? "text/javascript; charset=utf-8" : "text/javascript"; }else{ /* Content-type If the browser does not sent an ACCEPT for application/json then we fall back to text/plain. */ char const * cstr; cstr = PD("HTTP_ACCEPT",NULL); if( NULL == cstr ){ return doUtf8 ? "application/json; charset=utf-8" : "application/json"; }else{ if( strstr( cstr, "application/json" ) || strstr( cstr, "*/*" ) ){ return doUtf8 ? "application/json; charset=utf-8" : "application/json"; }else{ return "text/plain"; } } } } /* ** Given a request CONTENT_TYPE value, this function returns true ** if it is of a type which the JSON API can ostensibly read. ** ** It accepts any of application/json, text/plain, ** application/javascript, or text/javascript. The former is ** preferred, but was not widespread when this API was initially ** built, so the latter forms are permitted as fallbacks. */ int json_can_consume_content_type(const char * zType){ return fossil_strcmp(zType, "application/json")==0 || fossil_strcmp(zType,"text/plain")==0/*assume this MIGHT be JSON*/ || fossil_strcmp(zType,"text/javascript")==0 || fossil_strcmp(zType,"application/javascript")==0; } /* ** Sends pResponse to the output stream as the response object. This ** function does no validation of pResponse except to assert() that it ** is not NULL. The caller is responsible for ensuring that it meets ** API response envelope conventions. ** ** In CLI mode pResponse is sent to stdout immediately. In HTTP ** mode pResponse replaces any current CGI content but cgi_reply() ** is not called to flush the output. ** ** If g.json.jsonp is not NULL then the content type is set to ** text/javascript and the output is wrapped in a jsonp ** wrapper. ** ** This function works only the first time it is called. It "should ** not" ever be called more than once but certain calling ** constellations might trigger that, in which case the second and ** subsequent calls are no-ops. */ void json_send_response( cson_value const * pResponse ){ static int once = 0; assert( NULL != pResponse ); if( once++ ) return; if( g.isHTTP ){ cgi_reset_content(); if( g.json.jsonp ){ cgi_set_content_type("text/javascript"); cgi_printf("%s(",g.json.jsonp); } cson_output( pResponse, cson_data_dest_cgi, NULL, &g.json.outOpt ); if( g.json.jsonp ){ cgi_append_content(")",1); } }else{/*CLI mode*/ if( g.json.jsonp ){ fprintf(stdout,"%s(",g.json.jsonp); } cson_output_FILE( pResponse, stdout, &g.json.outOpt ); if( g.json.jsonp ){ fwrite(")\n", 2, 1, stdout); } } } /* ** Returns the current request's JSON authentication token, or NULL if ** none is found. The token's memory is owned by (or shared with) ** g.json. ** ** If an auth token is found in the GET/POST request data then fossil ** is given that data for use in authentication for this ** session. i.e. the GET/POST data overrides fossil's authentication ** cookie value (if any) and also works with clients which do not ** support cookies. ** ** Must be called once before login_check_credentials() is called or ** we will not be able to replace fossil's internal idea of the auth ** info in time (and future changes to that state may cause unexpected ** results). ** ** The result of this call are cached for future calls. ** ** Special case: if g.useLocalauth is true (i.e. the --localauth flag ** was used to start the fossil server instance) and the current ** connection is "local", any authToken provided by the user is ** ignored and no new token is created: localauth mode trumps the ** authToken. */ cson_value * json_auth_token(){ assert(g.json.gc.a && "json_bootstrap_early() was not called!"); if( g.json.authToken==0 && g.noPswd==0 /* g.noPswd!=0 means the user was logged in via a local connection using --localauth. */ ){ /* Try to get an authorization token from GET parameter, POSTed JSON, or fossil cookie (in that order). */ g.json.authToken = json_getenv(FossilJsonKeys.authToken); if(g.json.authToken && cson_value_is_string(g.json.authToken) && !PD(login_cookie_name(),NULL)){ /* tell fossil to use this login info. FIXME?: because the JSON bits don't carry around login_cookie_name(), there is(?) a potential(?) login hijacking window here. We may need to change the JSON auth token to be in the form: login_cookie_name()=... Then again, the hardened cookie value helps ensure that only a proper key/value match is valid. */ cgi_replace_parameter( login_cookie_name(), cson_value_get_cstr(g.json.authToken) ); }else if( g.isHTTP ){ /* try fossil's conventional cookie. */ /* Reminder: chicken/egg scenario regarding db access in CLI mode because login_cookie_name() needs the db. CLI mode does not use any authentication, so we don't need to support it here. */ char const * zCookie = P(login_cookie_name()); if( zCookie && *zCookie ){ /* Transfer fossil's cookie to JSON for downstream convenience... */ cson_value * v = cson_value_new_string(zCookie, strlen(zCookie)); json_gc_add( FossilJsonKeys.authToken, v ); g.json.authToken = v; } } } return g.json.authToken; } /* ** If g.json.reqPayload.o is NULL then NULL is returned, else the ** given property is searched for in the request payload. If found it ** is returned. The returned value is owned by (or shares ownership ** with) g.json, and must NOT be cson_value_free()'d by the ** caller. */ cson_value * json_req_payload_get(char const *pKey){ return g.json.reqPayload.o ? cson_object_get(g.json.reqPayload.o,pKey) : NULL; } /* ** Returns non-zero if the json_bootstrap_early() function has already ** been called. In general, this function should be used sparingly, ** e.g. from low-level support functions like fossil_warning() where ** there is genuine uncertainty about whether (or not) the JSON setup ** has already been called. */ int json_is_bootstrapped_early(void){ return ((g.json.gc.v != NULL) && (g.json.gc.a != NULL)); } /* ** Initializes some JSON bits which need to be initialized relatively ** early on. It should be called by any routine which might need to ** call into JSON relatively early on in the init process. ** Specifically, early on in cgi_init() and json_cmd_top(), but also ** from any error reporting routines which might be triggered (early ** on in those functions). ** ** Initializes g.json.gc and g.json.param. This code does not (and ** must not) rely on any of the fossil environment having been set ** up. e.g. it must not use cgi_parameter() and friends because this ** must be called before those data are initialized. ** ** If called multiple times, calls after the first are a no-op. */ void json_bootstrap_early(void){ cson_value * v; if(g.json.gc.v!=NULL){ /* Avoid multiple bootstrappings. */ return; } g.json.timerId = fossil_timer_start(); /* g.json.gc is our "garbage collector" - where we put JSON values which need a long lifetime but don't have a logical parent to put them in. */ v = cson_value_new_array(); g.json.gc.v = v; assert(0 != g.json.gc.v); g.json.gc.a = cson_value_get_array(v); assert(0 != g.json.gc.a); cson_value_add_reference(v) /* Needed to allow us to include this value in other JSON containers without transferring ownership to those containers. All other persistent g.json.XXX.v values get appended to g.json.gc.a, and therefore already have a live reference for this purpose. */ ; /* g.json.param holds the JSONized counterpart of fossil's cgi_parameter_xxx() family of data. We store them as JSON, as opposed to using fossil's data directly, because we can retain full type information for data this way (as opposed to it always being of type string). */ v = cson_value_new_object(); g.json.param.v = v; g.json.param.o = cson_value_get_object(v); json_gc_add("$PARAMS", v); } /* ** Appends a warning object to the (pending) JSON response. ** ** Code must be a FSL_JSON_W_xxx value from the FossilJsonCodes enum. ** ** A Warning object has this JSON structure: ** ** { "code":integer, "text":"string" } ** ** But the text part is optional. ** ** If msg is non-NULL and not empty then it is used as the "text" ** property's value. It is copied, and need not refer to static ** memory. ** ** CURRENTLY this code only allows a given warning code to be ** added one time, and elides subsequent warnings. The intention ** is to remove that burden from loops which produce warnings. ** ** FIXME: if msg is NULL then use a standard string for ** the given code. If !*msg then elide the "text" property, ** for consistency with how json_err() works. */ void json_warn( int code, char const * fmt, ... ){ cson_object * obj = NULL; assert( (code>FSL_JSON_W_START) && (code<FSL_JSON_W_END) && "Invalid warning code."); assert(g.json.gc.a && "json_bootstrap_early() was not called!"); if(!g.json.warnings){ g.json.warnings = cson_new_array(); assert((NULL != g.json.warnings) && "Alloc error."); json_gc_add("$WARNINGS",cson_array_value(g.json.warnings)); } obj = cson_new_object(); cson_array_append(g.json.warnings, cson_object_value(obj)); cson_object_set(obj,"code",cson_value_new_integer(code)); if(fmt && *fmt){ /* FIXME: treat NULL fmt as standard warning message for the code, but we don't have those yet. */ va_list vargs; char * msg; va_start(vargs,fmt); msg = vmprintf(fmt,vargs); va_end(vargs); cson_object_set(obj,"text", cson_value_new_string(msg,strlen(msg))); fossil_free(msg); } } /* ** Splits zStr (which must not be NULL) into tokens separated by the ** given separator character. If doDeHttp is true then each element ** will be passed through dehttpize(), otherwise they are used ** as-is. Note that tokenization happens before dehttpize(), ** which is significant if the ENcoded tokens might contain the ** separator character. ** ** Each new element is appended to the given target array object, ** which must not be NULL and ownership of it is not changed by this ** call. ** ** On success, returns the number of tokens _encountered_. On error a ** NEGATIVE number is returned - its absolute value is the number of ** tokens encountered (i.e. it reveals which token in zStr was ** problematic). ** ** Achtung: leading and trailing whitespace of elements are elided. ** ** Achtung: empty elements will be skipped, meaning consecutive empty ** elements are collapsed. */ int json_string_split( char const * zStr, char separator, int doDeHttp, cson_array * target ){ char const * p = zStr /* current byte */; char const * head /* current start-of-token */; unsigned int len = 0 /* current token's length */; int rc = 0 /* return code (number of added elements)*/; assert( zStr && target ); while( fossil_isspace(*p) ){ ++p; } head = p; for( ; ; ++p){ if( !*p || (separator == *p) ){ if( len ){/* append head..(head+len) as next array element. */ cson_value * part = NULL; char * zPart = NULL; ++rc; assert( head != p ); zPart = (char*)fossil_malloc(len+1); memcpy(zPart, head, len); zPart[len] = 0; if(doDeHttp){ dehttpize(zPart); } if( *zPart ){ /* should only fail if someone manages to url-encoded a NUL byte */ part = cson_value_new_string(zPart, strlen(zPart)); if( 0 != cson_array_append( target, part ) ){ cson_value_free(part); rc = -rc; break; } }else{ assert(0 && "i didn't think this was possible!"); fprintf(stderr,"%s:%d: My God! It's full of stars!\n", __FILE__, __LINE__); fossil_exit(1) /* Not fossil_panic() b/c this code needs to be able to run before some of the fossil/json bits are initialized, and fossil_panic() calls into the JSON API. */ ; } fossil_free(zPart); len = 0; } if( !*p ){ break; } head = p+1; while( *head && fossil_isspace(*head) ){ ++head; ++p; } if(!*head){ break; } continue; } ++len; } return rc; } /* ** Wrapper around json_string_split(), taking the same first 3 ** parameters as this function, but returns the results as a JSON ** Array (if splitting produced tokens) or NULL (if splitting failed ** in any way or produced no tokens). ** ** The returned value is owned by the caller. If not NULL then it ** _will_ have a JSON type of Array. */ cson_value * json_string_split2( char const * zStr, char separator, int doDeHttp ){ cson_array * a = cson_new_array(); int rc = json_string_split( zStr, separator, doDeHttp, a ); if( 0>=rc ){ cson_free_array(a); a = NULL; } return a ? cson_array_value(a) : NULL; } /* ** Performs some common initialization of JSON-related state. Must be ** called by the json_page_top() and json_cmd_top() dispatching ** functions to set up the JSON stat used by the dispatched functions. ** ** Implicitly sets up the login information state in CGI mode, but ** does not perform any permissions checking. It _might_ (haven't ** tested this) die with an error if an auth cookie is malformed. ** ** This must be called by the top-level JSON command dispatching code ** before they do any work. ** ** This must only be called once, or an assertion may be triggered. */ void json_bootstrap_late(){ static char once = 0 /* guard against multiple runs */; char const * zPath = P("PATH_INFO"); assert(g.json.gc.a && "json_bootstrap_early() was not called!"); assert( (0==once) && "json_bootstrap_late() called too many times!"); if( once ){ return; }else{ once = 1; } assert(g.json.isJsonMode && "g.json.isJsonMode should have been set up by now."); g.json.resultCode = 0; g.json.cmd.offset = -1; g.json.jsonp = PD("jsonp",NULL) /* FIXME: do some sanity checking on g.json.jsonp and ignore it if it is not halfway reasonable. */ ; if( !g.isHTTP && g.fullHttpReply ){ /* workaround for server mode, so we see it as CGI mode. */ g.isHTTP = 1; } if(g.isHTTP){ cgi_set_content_type(json_guess_content_type()) /* reminder: must be done after g.json.jsonp is initialized */ ; #if 0 /* Calling this seems to trigger an SQLITE_MISUSE warning??? Maybe it's not legal to set the logger more than once? */ sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0) /* avoids debug messages on stderr in JSON mode */ ; #endif } g.json.cmd.v = cson_value_new_array(); g.json.cmd.a = cson_value_get_array(g.json.cmd.v); json_gc_add( FossilJsonKeys.commandPath, g.json.cmd.v ); /* The following if/else block translates the PATH_INFO path (in CLI/server modes) or g.argv (CLI mode) into an internal list so that we can simplify command dispatching later on. Note that translating g.argv this way is overkill but allows us to avoid CLI-only special-case handling in other code, e.g. json_command_arg(). */ if( zPath ){/* Either CGI or server mode... */ /* Translate PATH_INFO into JSON array for later convenience. */ json_string_split(zPath, '/', 1, g.json.cmd.a); }else{/* assume CLI mode */ int i; char const * arg; cson_value * part; for(i = 1/*skip argv[0]*/; i < g.argc; ++i ){ arg = g.argv[i]; if( !arg || !*arg ){ continue; } if('-' == *arg){ /* workaround to skip CLI args so that json_command_arg() does not see them. This assumes that all arguments come LAST on the command line. */ break; } part = cson_value_new_string(arg,strlen(arg)); cson_array_append(g.json.cmd.a, part); } } while(!g.isHTTP){ /* Simulate JSON POST data via input file. Pedantic reminder: error handling does not honor user-supplied g.json.outOpt because outOpt cannot (generically) be configured until after POST-reading is finished. */ FILE * inFile = NULL; char const * jfile = find_option("json-input",NULL,1); Blob json = BLOB_INITIALIZER; 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. */ ; } blob_read_from_channel(&json, inFile, -1); fossil_fclose(inFile); cgi_parse_POST_JSON(&json); blob_reset(&json); break; } /* g.json.reqPayload exists only to simplify some of our access to the request payload. We currently only use this in the context of Object payloads, not Arrays, strings, etc. */ g.json.reqPayload.v = cson_object_get( g.json.post.o,FossilJsonKeys.payload ); if( g.json.reqPayload.v ){ g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v ) /* g.json.reqPayload.o may legally be NULL, which means only that g.json.reqPayload.v is-not-a Object. */; } /* Anything which needs json_getenv() and friends should go after this point. */ if(1 == cson_array_length_get(g.json.cmd.a)){ /* special case: if we're at the top path, look for a "command" request arg which specifies which command to run. */ char const * cmd = json_getenv_cstr("command"); if(cmd){ json_string_split(cmd, '/', 0, g.json.cmd.a); g.json.cmd.commandStr = cmd; } } if(!g.json.jsonp){ g.json.jsonp = json_find_option_cstr("jsonp",NULL,NULL); } if(!g.isHTTP){ g.json.errorDetailParanoia = 0;/*disable error code dumb-down for CLI mode*/ } {/* set up JSON output formatting options. */ int indent = -1; indent = json_find_option_int("indent",NULL,"I",-1); g.json.outOpt.indentation = (0>indent) ? (g.isHTTP ? 0 : 1) : (unsigned char)indent; g.json.outOpt.addNewline = g.isHTTP ? 0 : (g.json.jsonp ? 0 : 1); } if( g.isHTTP ){ json_auth_token()/* will copy our auth token, if any, to fossil's core, which we need before we call login_check_credentials(). */; login_check_credentials()/* populates g.perm */; } else{ /* FIXME: we need an option which allows us to skip this. At least one known command (/json/version) does not need an opened repo. The problem here is we cannot know which functions need it from here (because command dispatching hasn't yet happened) and all other commands rely on the repo being opened before they are called. A textbook example of lack of foresight :/. */ db_find_and_open_repository(OPEN_ANY_SCHEMA,0); } } /* ** Returns the ndx'th item in the "command path", where index 0 is the ** position of the "json" part of the path. Returns NULL if ndx is out ** of bounds or there is no "json" path element. ** ** In CLI mode the "path" is the list of arguments (skipping argv[0]). ** In server/CGI modes the path is taken from PATH_INFO. ** ** The returned bytes are owned by g.json.cmd.v and _may_ be ** invalidated if that object is modified (depending on how it is ** modified). ** ** Note that CLI options are not included in the command path. Use ** find_option() to get those. ** */ char const * json_command_arg(unsigned short ndx){ cson_array * ar = g.json.cmd.a; assert((NULL!=ar) && "Internal error. Was json_bootstrap_late() called?"); assert((g.argc>1) &&"Internal error - we never should have gotten this far."); if( g.json.cmd.offset < 0 ){ /* first-time setup. */ short i = 0; #define NEXT cson_string_cstr( \ cson_value_get_string( \ cson_array_get(ar,i) \ )) char const * tok = NEXT; while( tok ){ if( g.isHTTP/*workaround for "abbreviated name" in CLI mode*/ ? (0==strncmp("json",tok,4)) : (0==strcmp(g.argv[1],tok)) ){ g.json.cmd.offset = i; break; } ++i; tok = NEXT; } } #undef NEXT if(g.json.cmd.offset < 0){ return NULL; }else{ ndx = g.json.cmd.offset + ndx; return cson_string_cstr(cson_value_get_string( cson_array_get( ar, g.json.cmd.offset + ndx ))); } } /* Returns the C-string form of json_auth_token(), or NULL ** if json_auth_token() returns NULL. */ char const * json_auth_token_cstr(){ return cson_value_get_cstr( json_auth_token() ); } /* ** Returns the JsonPageDef with the given name, or NULL if no match is ** found. ** ** head must be a pointer to an array of JsonPageDefs in which the ** last entry has a NULL name. */ JsonPageDef const * json_handler_for_name( char const * name, JsonPageDef const * head ){ JsonPageDef const * pageDef = head; assert( head != NULL ); if(name && *name) for( ; pageDef->name; ++pageDef ){ if( 0 == strcmp(name, pageDef->name) ){ return pageDef; } } return NULL; } /* ** Given a Fossil/JSON result code, this function "dumbs it down" ** according to the current value of g.json.errorDetailParanoia. The ** dumbed-down value is returned. ** ** This function assert()s that code is in the inclusive range 0 to ** 9999. ** ** Note that WARNING codes (1..999) are never dumbed down. ** */ static int json_dumbdown_rc( int code ){ if(!g.json.errorDetailParanoia || !code || ((code>=FSL_JSON_W_START) && (code<FSL_JSON_W_END))){ return code; }else{ int modulo = 0; assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code."); switch( g.json.errorDetailParanoia ){ case 1: modulo = 10; break; case 2: modulo = 100; break; case 3: modulo = 1000; break; default: break; } if( modulo ) code = code - (code % modulo); return code; } } /* ** Convenience routine which converts a Julian time value into a Unix ** Epoch timestamp. Requires the db, so this cannot be used before the ** repo is opened (will trigger a fatal error in db_xxx()). The returned ** value is owned by the caller. */ cson_value * json_julian_to_timestamp(double j){ return cson_value_new_integer((cson_int_t) db_int64(0,"SELECT cast(strftime('%%s',%lf) as int)",j) ); } /* ** Returns a timestamp value. */ cson_int_t json_timestamp(){ return (cson_int_t)time(0); } /* ** Returns a new JSON value (owned by the caller) representing ** a timestamp. If timeVal is < 0 then time(0) is used to fetch ** the time, else timeVal is used as-is. The returned value is ** owned by the caller. */ cson_value * json_new_timestamp(cson_int_t timeVal){ return cson_value_new_integer((timeVal<0) ? (cson_int_t)time(0) : timeVal); } /* ** Internal helper for json_create_response(). Appends the first ** g.json.dispatchDepth elements of g.json.cmd.a, skipping the first ** one (the "json" part), to a string and returns that string value ** (which is owned by the caller). */ static cson_value * json_response_command_path(){ if(!g.json.cmd.a){ return NULL; }else{ cson_value * rc = NULL; Blob path = empty_blob; unsigned int aLen = g.json.dispatchDepth+1; /*cson_array_length_get(g.json.cmd.a);*/ unsigned int i = 1; for( ; i < aLen; ++i ){ char const * part = cson_string_cstr(cson_value_get_string( cson_array_get(g.json.cmd.a, i))); if(!part){ #if 1 fossil_warning("Iterating further than expected in %s.", __FILE__); #endif break; } blob_appendf(&path,"%s%s", (i>1 ? "/": ""), part); } rc = json_new_string((blob_size(&path)>0) ? blob_buffer(&path) : "") /* reminder; we need an empty string instead of NULL in this case, to avoid what outwardly looks like (but is not) an allocation error in json_create_response(). */ ; blob_reset(&path); return rc; } } /* ** Returns a JSON Object representation of the global g object. ** Returned value is owned by the caller. */ cson_value * json_g_to_json(){ cson_object * o = NULL; cson_object * pay = NULL; pay = o = cson_new_object(); #define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K)) #define CSTR(OBJ,K) cson_object_set(o, #K, OBJ.K ? json_new_string(OBJ.K) \ : cson_value_null()) #define VAL(K,V) cson_object_set(o, #K, (V) ? (V) : cson_value_null()) VAL(capabilities, json_cap_value()); INT(g, argc); INT(g, isConst); CSTR(g, zConfigDbName); INT(g, repositoryOpen); INT(g, localOpen); INT(g, minPrefix); INT(g, fSqlTrace); INT(g, fSqlStats); INT(g, fSqlPrint); INT(g, fQuiet); INT(g, fHttpTrace); INT(g, fSystemTrace); INT(g, fNoSync); INT(g, iErrPriority); INT(g, sslNotAvailable); INT(g, cgiOutput); 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, zOpenRevision); 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(); cson_object_set(pay, "json", cson_object_value(o) ); INT(g.json, isJsonMode); INT(g.json, resultCode); INT(g.json, errorDetailParanoia); INT(g.json, dispatchDepth); VAL(authToken, g.json.authToken); CSTR(g.json, jsonp); VAL(gc, g.json.gc.v); VAL(cmd, g.json.cmd.v); VAL(param, g.json.param.v); VAL(POST, g.json.post.v); VAL(warnings, cson_array_value(g.json.warnings)); /*cson_output_opt outOpt;*/ #undef INT #undef CSTR #undef VAL return cson_object_value(pay); } /* ** Creates a new Fossil/JSON response envelope skeleton. It is owned ** by the caller, who must eventually free it using cson_value_free(), ** or add it to a cson container to transfer ownership. Returns NULL ** on error. ** ** If payload is not NULL and resultCode is 0 then it is set as the ** "payload" property of the returned object. If resultCode is 0 then ** it defaults to g.json.resultCode. If resultCode is (or defaults to) ** non-zero and payload is not NULL then this function calls ** cson_value_free(payload) and does not insert the payload into the ** response. In either case, ownership of payload is transfered to (or ** shared with, if the caller holds a reference) this function. ** ** pMsg is an optional message string property (resultText) of the ** response. If resultCode is non-0 and pMsg is NULL then ** json_err_cstr() is used to get the error string. The caller may ** provide his own or may use an empty string to suppress the ** resultText property. ** */ static cson_value * json_create_response( int resultCode, char const * pMsg, cson_value * payload){ cson_value * v = NULL; cson_value * tmp = NULL; cson_object * o = NULL; int rc; resultCode = json_dumbdown_rc(resultCode ? resultCode : g.json.resultCode); o = cson_new_object(); v = cson_object_value(o); if( ! o ) return NULL; #define SET(K) if(!tmp) goto cleanup; \ cson_value_add_reference(tmp); \ rc = cson_object_set( o, K, tmp ); \ cson_value_free(tmp); \ if(rc) do{ \ tmp = NULL; \ goto cleanup; \ }while(0) tmp = json_new_string(MANIFEST_UUID); SET("fossil"); tmp = json_new_timestamp(-1); SET(FossilJsonKeys.timestamp); if( 0 != resultCode ){ if( ! pMsg ){ pMsg = g.zErrMsg; if(!pMsg){ pMsg = json_err_cstr(resultCode); } } tmp = json_new_string(json_rc_cstr(resultCode)); SET(FossilJsonKeys.resultCode); } if( pMsg && *pMsg ){ tmp = json_new_string(pMsg); SET(FossilJsonKeys.resultText); } if(g.json.cmd.commandStr){ tmp = json_new_string(g.json.cmd.commandStr); }else{ tmp = json_response_command_path(); } if(!tmp){ tmp = json_new_string("???"); } SET("command"); tmp = json_getenv(FossilJsonKeys.requestId); if( tmp ) cson_object_set( o, FossilJsonKeys.requestId, tmp ); if(0){/* these are only intended for my own testing...*/ if(g.json.cmd.v){ tmp = g.json.cmd.v; SET("$commandPath"); } if(g.json.param.v){ tmp = g.json.param.v; SET("$params"); } if(0){/*Only for debugging, add some info to the response.*/ tmp = cson_value_new_integer( g.json.cmd.offset ); SET("cmd.offset"); tmp = cson_value_new_bool( g.isHTTP ); SET("isCGI"); } } if(fossil_timer_is_active(g.json.timerId)){ /* This is, philosophically speaking, not quite the right place for ending the timer, but this is the one function which all of the JSON exit paths use (and they call it after processing, just before they end). */ sqlite3_uint64 span = fossil_timer_stop(g.json.timerId); /* I'm actually seeing sub-uSec runtimes in some tests, but a time of 0 is "just kinda wrong". */ cson_object_set(o,"procTimeUs", cson_value_new_integer((cson_int_t)span)); span /= 1000/*for milliseconds */; cson_object_set(o,"procTimeMs", cson_value_new_integer((cson_int_t)span)); assert(!fossil_timer_is_active(g.json.timerId)); g.json.timerId = -1; } if(g.json.warnings){ tmp = cson_array_value(g.json.warnings); SET("warnings"); } /* Only add the payload to SUCCESS responses. Else delete it. */ if( NULL != payload ){ if( resultCode ){ cson_value_free(payload); payload = NULL; }else{ tmp = payload; SET(FossilJsonKeys.payload); } } if((g.perm.Admin||g.perm.Setup) && json_find_option_bool("debugFossilG","json-debug-g",NULL,0) ){ tmp = json_g_to_json(); SET("g"); } #undef SET goto ok; cleanup: cson_value_free(v); v = NULL; ok: return v; } /* ** Outputs a JSON error response to either the cgi_xxx() family of ** buffers (in CGI/server mode) or stdout (in CLI mode). If rc is 0 ** then g.json.resultCode is used. If that is also 0 then the "Unknown ** Error" code is used. ** ** If g.isHTTP then the generated JSON error response object replaces ** any currently buffered page output. Because the output goes via ** the cgi_xxx() family of functions, this function inherits any ** compression which fossil does for its output. ** ** If alsoOutput is true AND g.isHTTP then cgi_reply() is called to ** flush the output (and headers). Generally only do this if you are ** about to call exit(). ** ** If !g.isHTTP then alsoOutput is ignored and all output is sent to ** stdout immediately. ** ** For generating the resultText property: if msg is not NULL then it ** is used as-is. If it is NULL then g.zErrMsg is checked, and if that ** is NULL then json_err_cstr(code) is used. */ void json_err( int code, char const * msg, int alsoOutput ){ int rc = code ? code : (g.json.resultCode ? g.json.resultCode : FSL_JSON_E_UNKNOWN); cson_value * resp = NULL; if(g.json.isJsonMode==0) return; rc = json_dumbdown_rc(rc); if( rc && !msg ){ msg = g.zErrMsg; if(!msg){ msg = json_err_cstr(rc); } } resp = json_create_response(rc, msg, NULL); if(!resp){ /* about the only error case here is out-of-memory. DO NOT call fossil_panic() or fossil_fatal() here because those allocate. */ fprintf(stderr, "%s: Fatal error: could not allocate " "response object.\n", g.argv[0]); fossil_exit(1); } if( g.isHTTP ){ if(alsoOutput){ json_send_response(resp); }else{ /* almost a duplicate of json_send_response() :( */ cgi_reset_content(); if( g.json.jsonp ){ cgi_printf("%s(",g.json.jsonp); } cson_output( resp, cson_data_dest_cgi, NULL, &g.json.outOpt ); if( g.json.jsonp ){ cgi_append_content(")",1); } } }else{ json_send_response(resp); } cson_value_free(resp); } /* ** Sets g.json.resultCode and g.zErrMsg, but does not report the error ** via json_err(). Returns the code passed to it. ** ** code must be in the inclusive range 1000..9999. */ int json_set_err( int code, char const * fmt, ... ){ assert( (code>=1000) && (code<=9999) ); fossil_free(g.zErrMsg); g.json.resultCode = code; if(!fmt || !*fmt){ g.zErrMsg = mprintf("%s", json_err_cstr(code)); }else{ va_list vargs; char * msg; va_start(vargs,fmt); msg = vmprintf(fmt, vargs); va_end(vargs); g.zErrMsg = msg; } return code; } /* ** Iterates through a prepared SELECT statement and converts each row ** to a JSON object. If pTgt is not NULL then this function will ** append the results to pTgt and return cson_array_value(pTgt). If ** pTgt is NULL then a new Array object is created and returned (owned ** by the caller). Each row of pStmt is converted to an Object and ** appended to the array. If the result set has no rows AND pTgt is ** NULL then NULL (not an empty array) is returned. */ cson_value * json_stmt_to_array_of_obj(Stmt *pStmt, cson_array * pTgt){ cson_array * a = pTgt; char const * warnMsg = NULL; cson_value * colNamesV = NULL; cson_array * colNames = NULL; while( (SQLITE_ROW==db_step(pStmt)) ){ cson_value * row = NULL; if(!a){ a = cson_new_array(); assert(NULL!=a); } if(!colNames){ colNamesV = cson_sqlite3_column_names(pStmt->pStmt); assert(NULL != colNamesV); /*Why? cson_value_add_reference(colNamesV) avoids an ownership problem*/; colNames = cson_value_get_array(colNamesV); assert(NULL != colNames); } row = cson_sqlite3_row_to_object2(pStmt->pStmt, colNames); if(!row && !warnMsg){ warnMsg = "Could not convert at least one result row to JSON."; continue; } if( 0 != cson_array_append(a, row) ){ cson_value_free(row); if(pTgt != a) { cson_free_array(a); } assert( 0 && "Alloc error."); return NULL; } } cson_value_free(colNamesV); if(warnMsg){ json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, "%s", warnMsg ); } return cson_array_value(a); } /* ** Works just like json_stmt_to_array_of_obj(), but each row in the ** result set is represented as an Array of values instead of an ** Object (key/value pairs). If pTgt is NULL and the statement ** has no results then NULL is returned, not an empty array. */ cson_value * json_stmt_to_array_of_array(Stmt *pStmt, cson_array * pTgt){ cson_array * a = pTgt; while( (SQLITE_ROW==db_step(pStmt)) ){ cson_value * row = NULL; if(!a){ a = cson_new_array(); assert(NULL!=a); } row = cson_sqlite3_row_to_array(pStmt->pStmt); cson_array_append(a, row); } return cson_array_value(a); } cson_value * json_stmt_to_array_of_values(Stmt *pStmt, int resultColumn, cson_array * pTgt){ cson_array * a = pTgt; while( (SQLITE_ROW==db_step(pStmt)) ){ cson_value * row = cson_sqlite3_column_to_value(pStmt->pStmt, resultColumn); if(row){ if(!a){ a = cson_new_array(); assert(NULL!=a); } cson_array_append(a, row); } } return cson_array_value(a); } /* ** Executes the given SQL and runs it through ** json_stmt_to_array_of_obj(), returning the result of that ** function. If resetBlob is true then blob_reset(pSql) is called ** after preparing the query. ** ** pTgt has the same semantics as described for ** json_stmt_to_array_of_obj(). ** ** FIXME: change this to take a (char const *) instead of a blob, ** 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, int 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; } /* ** If the given COMMIT rid has any tags associated with it, this ** function returns a JSON Array containing the tag names (owned by ** the caller), else it returns NULL. ** ** See info_tags_of_checkin() for more details (this is simply a JSON ** wrapper for that function). ** ** If there are no tags then this function returns NULL, not an empty ** Array. */ cson_value * json_tags_for_checkin_rid(int rid, int propagatingOnly){ cson_value * v = NULL; char * tags = info_tags_of_checkin(rid, propagatingOnly); if(tags){ if(*tags){ v = json_string_split2(tags,',',0); } fossil_free(tags); } return v; } /* ** Returns a "new" value representing the boolean value of zVal ** (false if zVal is NULL). Note that cson does not really allocate ** any memory for boolean values, but they "should" (for reasons of ** style and philosophy) be cleaned up like any other values (but ** it's a no-op for bools). */ cson_value * json_value_to_bool(cson_value const * zVal){ return cson_value_get_bool(zVal) ? cson_value_true() : cson_value_false(); } /* ** Impl of /json/resultCodes ** */ cson_value * json_page_resultCodes(void){ cson_array * list = cson_new_array(); cson_object * obj = NULL; cson_string * kRC; cson_string * kSymbol; cson_string * kNumber; cson_string * kDesc; cson_array_reserve( list, 35 ); kRC = cson_new_string("resultCode",10); kSymbol = cson_new_string("cSymbol",7); kNumber = cson_new_string("number",6); kDesc = cson_new_string("description",11); #define C(K) obj = cson_new_object(); \ cson_object_set_s(obj, kRC,json_new_string(json_rc_cstr(FSL_JSON_E_##K))); \ cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) ); \ cson_object_set_s(obj, kNumber, cson_value_new_integer(FSL_JSON_E_##K) ); \ cson_object_set_s(obj, kDesc, \ json_new_string(json_err_cstr(FSL_JSON_E_##K))); \ cson_array_append( list, cson_object_value(obj) ); obj = NULL; C(GENERIC); C(INVALID_REQUEST); C(UNKNOWN_COMMAND); C(UNKNOWN); C(TIMEOUT); C(ASSERT); C(ALLOC); C(NYI); C(PANIC); C(MANIFEST_READ_FAILED); C(FILE_OPEN_FAILED); C(AUTH); C(MISSING_AUTH); C(DENIED); C(WRONG_MODE); C(LOGIN_FAILED); C(LOGIN_FAILED_NOSEED); C(LOGIN_FAILED_NONAME); C(LOGIN_FAILED_NOPW); C(LOGIN_FAILED_NOTFOUND); C(USAGE); C(INVALID_ARGS); C(MISSING_ARGS); C(AMBIGUOUS_UUID); C(UNRESOLVED_UUID); C(RESOURCE_ALREADY_EXISTS); C(RESOURCE_NOT_FOUND); C(DB); C(STMT_PREP); C(STMT_BIND); C(STMT_EXEC); C(DB_LOCKED); C(DB_NEEDS_REBUILD); C(DB_NOT_FOUND); C(DB_NOT_VALID); #undef C return cson_array_value(list); } /* ** /json/version implementation. ** ** Returns the payload object (owned by the caller). */ cson_value * json_page_version(void){ cson_value * jval = NULL; cson_object * jobj = NULL; jval = cson_value_new_object(); jobj = cson_value_get_object(jval); #define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X))) FSET(MANIFEST_UUID,"manifestUuid"); FSET(MANIFEST_VERSION,"manifestVersion"); FSET(MANIFEST_DATE,"manifestDate"); FSET(MANIFEST_YEAR,"manifestYear"); FSET(RELEASE_VERSION,"releaseVersion"); cson_object_set( jobj, "releaseVersionNumber", cson_value_new_integer(RELEASE_VERSION_NUMBER) ); cson_object_set( jobj, "resultCodeParanoiaLevel", cson_value_new_integer(g.json.errorDetailParanoia) ); FSET(FOSSIL_JSON_API_VERSION, "jsonApiVersion" ); #undef FSET return jval; } /* ** Returns the current user's capabilities string as a String value. ** Returned value is owned by the caller, and will only be NULL if ** g.userUid is invalid or an out of memory error. Or, it turns out, ** in CLI mode (where there is no logged-in user). */ cson_value * json_cap_value(){ if(g.userUid<=0){ return NULL; }else{ Stmt q = empty_Stmt; cson_value * val = NULL; db_prepare(&q, "SELECT cap FROM user WHERE uid=%d", g.userUid); if( db_step(&q)==SQLITE_ROW ){ char const * str = (char const *)sqlite3_column_text(q.pStmt,0); if( str ){ val = json_new_string(str); } } db_finalize(&q); return val; } } /* ** Implementation for /json/cap ** ** Returned object contains details about the "capabilities" of the ** current user (what he may/may not do). ** ** This is primarily intended for debuggering, but may have ** a use in client code. (?) */ cson_value * json_page_cap(void){ cson_value * payload = cson_value_new_object(); cson_value * sub = cson_value_new_object(); Stmt q; cson_object * obj = cson_value_get_object(payload); db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d", g.userUid); if( db_step(&q)==SQLITE_ROW ){ /* reminder: we don't use g.zLogin because it's 0 for the guest user and the HTML UI appears to currently allow the name to be changed (but doing so would break other code). */ char const * str = (char const *)sqlite3_column_text(q.pStmt,0); if( str ){ cson_object_set( obj, "name", cson_value_new_string(str,strlen(str)) ); } str = (char const *)sqlite3_column_text(q.pStmt,1); if( str ){ cson_object_set( obj, "capabilities", cson_value_new_string(str,strlen(str)) ); } } db_finalize(&q); cson_object_set( obj, "permissionFlags", sub ); obj = cson_value_get_object(sub); #define ADD(X,K) cson_object_set(obj, K, cson_value_new_bool(g.perm.X)) ADD(Setup,"setup"); ADD(Admin,"admin"); ADD(Password,"password"); ADD(Query,"query"); /* don't think this one is actually used */ ADD(Write,"checkin"); ADD(Read,"checkout"); ADD(Hyperlink,"history"); ADD(Clone,"clone"); ADD(RdWiki,"readWiki"); ADD(NewWiki,"createWiki"); ADD(ApndWiki,"appendWiki"); ADD(WrWiki,"editWiki"); ADD(ModWiki,"moderateWiki"); ADD(RdTkt,"readTicket"); ADD(NewTkt,"createTicket"); ADD(ApndTkt,"appendTicket"); ADD(WrTkt,"editTicket"); ADD(ModTkt,"moderateTicket"); ADD(Attach,"attachFile"); ADD(TktFmt,"createTicketReport"); ADD(RdAddr,"readPrivate"); ADD(Zip,"zip"); ADD(Private,"xferPrivate"); ADD(WrUnver,"writeUnversioned"); ADD(RdForum,"readForum"); ADD(WrForum,"writeForum"); ADD(WrTForum,"writeTrustedForum"); ADD(ModForum,"moderateForum"); ADD(AdminForum,"adminForum"); ADD(EmailAlert,"emailAlert"); ADD(Announce,"announce"); ADD(Debug,"debug"); ADD(Chat,"chat"); #undef ADD return payload; } /* ** Implementation of the /json/stat page/command. ** */ cson_value * json_page_stat(void){ i64 t, fsize; int n, m; int full; enum { BufLen = 1000 }; char zBuf[BufLen]; cson_value * jv = NULL; cson_object * jo = NULL; cson_value * jv2 = NULL; cson_object * jo2 = NULL; char * zTmp = NULL; if( !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); return NULL; } full = json_find_option_bool("full",NULL,"f", json_find_option_bool("verbose",NULL,"v",0)); #define SETBUF(O,K) cson_object_set(O, K, \ cson_value_new_string(zBuf, strlen(zBuf))); jv = cson_value_new_object(); jo = cson_value_get_object(jv); zTmp = db_get("project-name",NULL); cson_object_set(jo, "projectName", json_new_string(zTmp)); fossil_free(zTmp); zTmp = db_get("project-description",NULL); cson_object_set(jo, "projectDescription", json_new_string(zTmp)); fossil_free(zTmp); zTmp = NULL; fsize = file_size(g.zRepositoryName, ExtFILE); cson_object_set(jo, "repositorySize", cson_value_new_integer((cson_int_t)fsize)); if(full){ n = db_int(0, "SELECT count(*) FROM blob"); m = db_int(0, "SELECT count(*) FROM delta"); cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n)); cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m)); if( n>0 ){ int a, b; Stmt q; db_prepare(&q, "SELECT total(size), avg(size), max(size)" " FROM blob WHERE size>0"); db_step(&q); t = db_column_int64(&q, 0); cson_object_set(jo, "uncompressedArtifactSize", cson_value_new_integer((cson_int_t)t)); cson_object_set(jo, "averageArtifactSize", cson_value_new_integer((cson_int_t)db_column_int(&q, 1))); cson_object_set(jo, "maxArtifactSize", cson_value_new_integer((cson_int_t)db_column_int(&q, 2))); db_finalize(&q); if( t/fsize < 5 ){ b = 10; fsize /= 10; }else{ b = 1; } a = t/fsize; sqlite3_snprintf(BufLen,zBuf, "%d:%d", a, b); SETBUF(jo, "compressionRatio"); } n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/"); cson_object_set(jo, "checkinCount", cson_value_new_integer((cson_int_t)n)); n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); cson_object_set(jo, "fileCount", cson_value_new_integer((cson_int_t)n)); n = db_int(0, "SELECT count(*) FROM tag /*scan*/" " WHERE +tagname GLOB 'wiki-*'"); cson_object_set(jo, "wikiPageCount", cson_value_new_integer((cson_int_t)n)); n = db_int(0, "SELECT count(*) FROM tag /*scan*/" " WHERE +tagname GLOB 'tkt-*'"); cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); }/*full*/ n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" " + 0.99"); cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n)); cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425)); sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); 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"); cson_object_set(jo2, "pageCount", cson_value_new_integer( (cson_int_t)db_int(0, "PRAGMA repository.page_count"))); cson_object_set(jo2, "pageSize", cson_value_new_integer( (cson_int_t)db_int(0, "PRAGMA repository.page_size"))); cson_object_set(jo2, "freeList", cson_value_new_integer( (cson_int_t)db_int(0, "PRAGMA repository.freelist_count"))); sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0,"PRAGMA repository.encoding")); SETBUF(jo2, "encoding"); sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA repository.journal_mode")); cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null()); return jv; #undef SETBUF } /* ** Creates a comma-separated list of command names ** taken from zPages. zPages must be an array of objects ** whose final entry MUST have a NULL name value or results ** are undefined. ** ** The list is appended to pOut. The number of items (not bytes) ** appended are returned. If filterByMode is non-0 then the result ** list will contain only commands which are able to run in the ** current run mode (CLI vs. HTTP). */ static int json_pagedefs_to_string(JsonPageDef const * zPages, 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; } /* ** Creates an error message using zErrPrefix and the given array of ** JSON command definitions, and sets the g.json error state to ** reflect FSL_JSON_E_MISSING_ARGS. If zErrPrefix is NULL then ** some default is used (e.g. "Try one of: "). If it is "" then ** no prefix is used. ** ** The intention is to provide the user (via the response.resultText) ** a list of available commands/subcommands. ** */ void json_dispatch_missing_args_err( JsonPageDef const * pCommands, char const * zErrPrefix ){ Blob cmdNames = empty_blob; blob_init(&cmdNames,NULL,0); if( !zErrPrefix ) { zErrPrefix = "Try one of: "; } blob_append( &cmdNames, zErrPrefix, strlen(zErrPrefix) ); json_pagedefs_to_string(pCommands, &cmdNames, 1); json_set_err(FSL_JSON_E_MISSING_ARGS, "%s", blob_str(&cmdNames)); blob_reset(&cmdNames); } cson_value * json_page_dispatch_helper(JsonPageDef const * pages){ JsonPageDef const * def; char const * cmd = json_command_arg(1+g.json.dispatchDepth); assert( NULL != pages ); if( ! cmd ){ json_dispatch_missing_args_err(pages, "No subcommand specified. " "Try one of: "); return NULL; } def = json_handler_for_name( cmd, pages ); if(!def){ json_set_err(FSL_JSON_E_UNKNOWN_COMMAND, "Unknown subcommand: %s", cmd); return NULL; } else{ ++g.json.dispatchDepth; return (*def->func)(); } } /* ** Impl of /json/rebuild. Requires admin privileges. */ static cson_value * json_page_rebuild(void){ if( !g.perm.Admin ){ json_set_err(FSL_JSON_E_DENIED,"Requires 'a' privileges."); return NULL; }else{ /* Reminder: the db_xxx() ops "should" fail via the fossil core error handlers, which will cause a JSON error and exit(). i.e. we don't handle the errors here. TODO: confirm that all these db routine fail gracefully in JSON mode. On large repos (e.g. fossil's) this operation is likely to take longer than the client timeout, which will cause it to fail (but it's sqlite3, so it'll fail gracefully). */ db_close(1); db_open_repository(g.zRepositoryName); db_begin_transaction(); rebuild_db(0, 0); db_end_transaction(0); return NULL; } } /* ** Impl of /json/g. Requires admin/setup rights. */ static cson_value * json_page_g(void){ if(!g.perm.Admin || !g.perm.Setup){ json_set_err(FSL_JSON_E_DENIED, "Requires 'a' or 's' privileges."); return NULL; } return json_g_to_json(); } /* Impl in json_login.c. */ cson_value * json_page_anon_password(void); /* Impl in json_artifact.c. */ cson_value * json_page_artifact(void); /* Impl in json_branch.c. */ cson_value * json_page_branch(void); /* Impl in json_diff.c. */ cson_value * json_page_diff(void); /* Impl in json_dir.c. */ cson_value * json_page_dir(void); /* Impl in json_login.c. */ cson_value * json_page_login(void); /* Impl in json_login.c. */ cson_value * json_page_logout(void); /* Impl in json_query.c. */ cson_value * json_page_query(void); /* Impl in json_report.c. */ cson_value * json_page_report(void); /* Impl in json_tag.c. */ cson_value * json_page_tag(void); /* Impl in json_user.c. */ cson_value * json_page_user(void); /* Impl in json_config.c. */ cson_value * json_page_config(void); /* Impl in json_finfo.c. */ cson_value * json_page_finfo(void); /* Impl in json_status.c. */ cson_value * json_page_status(void); /* ** Mapping of names to JSON pages/commands. Each name is a subpath of ** /json (in CGI mode) or a subcommand of the json command in CLI mode */ static const JsonPageDef JsonPageDefs[] = { /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */ {"anonymousPassword", json_page_anon_password, 0}, {"artifact", json_page_artifact, 0}, {"branch", json_page_branch,0}, {"cap", json_page_cap, 0}, {"config", json_page_config, 0 }, {"diff", json_page_diff, 0}, {"dir", json_page_dir, 0}, {"finfo", json_page_finfo, 0}, {"g", json_page_g, 0}, {"HAI",json_page_version,0}, {"login",json_page_login,0}, {"logout",json_page_logout,0}, {"query",json_page_query,0}, {"rebuild",json_page_rebuild,0}, {"report", json_page_report, 0}, {"resultCodes", json_page_resultCodes,0}, {"settings",json_page_settings,0}, {"stat",json_page_stat,0}, {"status", json_page_status, 0}, {"tag", json_page_tag,0}, /*{"ticket", json_page_nyi,0},*/ {"timeline", json_page_timeline,0}, {"user",json_page_user,0}, {"version",json_page_version,0}, {"whoami",json_page_whoami,0}, {"wiki",json_page_wiki,0}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; /* ** Internal helper for json_cmd_top() and json_page_top(). ** ** Searches JsonPageDefs for a command with the given name. If found, ** it is used to generate and output a JSON response. If not found, it ** generates a JSON-style error response. Returns 0 on success, non-0 ** on error. On error it will set g.json's error state. */ static int json_dispatch_root_command( char const * zCommand ){ int rc = 0; cson_value * payload = NULL; JsonPageDef const * pageDef = NULL; pageDef = json_handler_for_name(zCommand,&JsonPageDefs[0]); if( ! pageDef ){ rc = FSL_JSON_E_UNKNOWN_COMMAND; json_set_err( rc, "Unknown command: %s", zCommand ); }else if( pageDef->runMode < 0 /*CLI only*/){ rc = FSL_JSON_E_WRONG_MODE; }else if( (g.isHTTP && (pageDef->runMode < 0 /*CLI only*/)) || (!g.isHTTP && (pageDef->runMode > 0 /*HTTP only*/)) ){ rc = FSL_JSON_E_WRONG_MODE; } else{ rc = 0; g.json.dispatchDepth = 1; payload = (*pageDef->func)(); } payload = json_create_response(rc, NULL, payload); json_send_response(payload); cson_value_free(payload); return rc; } #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ /* ** WEBPAGE: json ** ** Pages under /json/... must be entered into JsonPageDefs. ** This function dispatches them, and is the HTTP equivalent of ** json_cmd_top(). */ void json_page_top(void){ char const * zCommand; assert(g.json.gc.a && "json_bootstrap_early() was not called!"); assert(g.json.cmd.a && "json_bootstrap_late() was not called!"); zCommand = json_command_arg(1); if(!zCommand || !*zCommand){ json_dispatch_missing_args_err( JsonPageDefs, "No command (sub-path) specified." " Try one of: "); return; } json_dispatch_root_command( zCommand ); } #endif /* FOSSIL_ENABLE_JSON for mkindex */ #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ /* ** This function dispatches json commands and is the CLI equivalent of ** json_page_top(). ** ** COMMAND: json ** ** Usage: %fossil json SUBCOMMAND ?OPTIONS? ** ** In CLI mode, the -R REPO common option is supported. Due to limitations ** in the argument dispatching code, any -FLAGS must come after the final ** sub- (or subsub-) command. ** ** The -json-input FILE option can be used to read JSON data and process ** it like the HTTP interface would. For example: ** ** %fossil json -json-input my.json ** ** The commands include: ** ** anonymousPassword ** artifact ** branch ** cap ** config ** diff ** dir ** g ** login ** logout ** query ** rebuild ** report ** resultCodes ** stat ** tag ** timeline ** user ** version (alias: HAI) ** whoami ** wiki ** ** Run '%fossil json' without any subcommand to see the full list (but be ** aware that some listed might not yet be fully implemented). ** */ void json_cmd_top(void){ char const * cmd = NULL; int rc = 0; memset( &g.perm, 0xff, sizeof(g.perm) ) /* In CLI mode fossil does not use permissions and they all default to false. We enable them here because (A) fossil doesn't use them in local mode but (B) having them set gives us one less difference in the CLI/CGI/Server-mode JSON handling. */ ; json_bootstrap_early(); json_bootstrap_late(); if( 2 > cson_array_length_get(g.json.cmd.a) ){ goto usage; } #if 0 json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing."); json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing again."); #endif cmd = json_command_arg(1); if( !cmd || !*cmd ){ goto usage; } rc = json_dispatch_root_command( cmd ); if(0 != rc){ /* FIXME: we need a way of passing this error back up to the routine which called this callback. e.g. add g.errCode. */ fossil_exit(1); } return; usage: { cson_value * payload; json_dispatch_missing_args_err( JsonPageDefs, "No subcommand specified." " Try one of: "); payload = json_create_response(0, NULL, NULL); json_send_response(payload); cson_value_free(payload); fossil_exit(1); } } #endif /* FOSSIL_ENABLE_JSON for mkindex */ #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_artifact.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "VERSION.h" #include "config.h" #include "json_artifact.h" #if INTERFACE #include "json_detail.h" #endif /* ** Internal callback for /json/artifact handlers. rid refers to ** the rid of a given type of artifact, and each callback is ** specialized to return a JSON form of one type of artifact. ** ** Implementations may assert() that rid refers to requested artifact ** type, since mismatches in the artifact types come from ** json_page_artifact() as opposed to client data. ** ** The pParent parameter points to the response payload object. It ** _may_ be used to populate "top-level" information in the response ** payload, but normally this is neither necessary nor desired. */ typedef cson_value * (*artifact_f)( cson_object * pParent, int rid ); /* ** Internal per-artifact-type dispatching helper. */ typedef struct ArtifactDispatchEntry { /** Artifact type name, e.g. "checkin", "ticket", "wiki". */ char const * name; /** JSON construction callback. Creates the contents for the payload.artifact property of /json/artifact responses. */ artifact_f func; } ArtifactDispatchEntry; /* ** Generates a JSON Array reference holding the parent UUIDs (as strings). ** If it finds no matches then it returns NULL (OOM is a fatal error). ** ** Returned value is NULL or an Array owned by the caller. */ cson_value * json_parent_uuids_for_ci( int rid ){ Stmt q = empty_Stmt; cson_array * pParents = NULL; db_prepare( &q, "SELECT uuid FROM plink, blob" " WHERE plink.cid=%d AND blob.rid=plink.pid" " ORDER BY plink.isprim DESC", rid ); while( SQLITE_ROW==db_step(&q) ){ if(!pParents) { pParents = cson_new_array(); } cson_array_append( pParents, cson_sqlite3_column_to_value( q.pStmt, 0 ) ); } 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; if(!eventTypeLabel){ eventTypeLabel = json_new_string("checkin"); json_gc_add("$EVENT_TYPE_LABEL(commit)", eventTypeLabel); } db_prepare(&q, "SELECT b.uuid, " " cast(strftime('%%s',e.mtime) as int), " " strftime('%%s',e.omtime)," " e.user, " " e.comment" " FROM blob b, event e" " WHERE b.rid=%d" " AND e.objid=%d", rid, rid ); if( db_step(&q)==SQLITE_ROW ){ cson_object * o; cson_value * tmpV = NULL; const char *zUuid = db_column_text(&q, 0); const char *zUser; const char *zComment; char * zEUser, * zEComment; i64 mtime, omtime; v = cson_value_new_object(); o = cson_value_get_object(v); #define SET(K,V) cson_object_set(o,(K), (V)) SET("type", eventTypeLabel ); SET("uuid",json_new_string(zUuid)); SET("isLeaf", cson_value_new_bool(is_a_leaf(rid))); mtime = db_column_int64(&q,1); SET("timestamp",json_new_int(mtime)); omtime = db_column_int64(&q,2); if(omtime && (omtime!=mtime)){ SET("originTime",json_new_int(omtime)); } zUser = db_column_text(&q,3); zEUser = db_text(0, "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", TAG_USER, rid); if(zEUser){ SET("user", json_new_string(zEUser)); if(0!=fossil_strcmp(zEUser,zUser)){ SET("originUser",json_new_string(zUser)); } free(zEUser); }else{ SET("user",json_new_string(zUser)); } zComment = db_column_text(&q,4); zEComment = db_text(0, "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", TAG_COMMENT, rid); if(zEComment){ SET("comment",json_new_string(zEComment)); if(0 != fossil_strcmp(zEComment,zComment)){ SET("originComment", json_new_string(zComment)); } free(zEComment); }else{ SET("comment",json_new_string(zComment)); } tmpV = json_parent_uuids_for_ci(rid); if(tmpV){ SET("parents", tmpV); } tmpV = json_tags_for_checkin_rid(rid,0); if(tmpV){ SET("tags",tmpV); } if( showFiles ){ tmpV = json_get_changed_files(rid, 1); if(tmpV){ SET("files",tmpV); } } #undef SET } db_finalize(&q); return v; } /* ** Very incomplete/incorrect impl of /json/artifact/TICKET_ID. */ cson_value * json_artifact_ticket( cson_object * zParent, int rid ){ cson_object * pay = NULL; Manifest *pTktChng = NULL; static cson_value * eventTypeLabel = NULL; if(! g.perm.RdTkt ){ g.json.resultCode = FSL_JSON_E_DENIED; return NULL; } if(!eventTypeLabel){ eventTypeLabel = json_new_string("ticket"); json_gc_add("$EVENT_TYPE_LABEL(ticket)", eventTypeLabel); } pTktChng = manifest_get(rid, CFTYPE_TICKET, 0); if( pTktChng==0 ){ g.json.resultCode = FSL_JSON_E_MANIFEST_READ_FAILED; return NULL; } pay = cson_new_object(); cson_object_set(pay, "eventType", eventTypeLabel ); cson_object_set(pay, "uuid", json_new_string(pTktChng->zTicketUuid)); 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); } return cson_object_value(zParent); } } /* ** Internal mapping of /json/artifact/FOO commands/callbacks. */ static ArtifactDispatchEntry ArtifactDispatchList[] = { {"checkin", json_artifact_ci}, {"file", json_artifact_file}, /*{"tag", NULL}, //impl missing */ /*{"technote", NULL}, //impl missing */ {"ticket", json_artifact_ticket}, {"wiki", json_artifact_wiki}, /* Final entry MUST have a NULL name. */ {NULL,NULL} }; /* ** 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(void){ 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?" ** and "is this object removed?" flags of the underlying query. This ** function returns a static string from the set (added, removed, ** modified), depending on the combination of the two args. ** ** Reminder to self: (mlink.pid==0) AS isNew, (mlink.fid==0) AS isDel */ char const * json_artifact_status_to_string( char isNew, char isDel ){ return isNew ? "added" : (isDel ? "removed" : "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; } pay = zParent; contentFormat = json_artifact_get_content_format_flag(); if( 0 != contentFormat ){ Blob content = empty_blob; const char *zMime; char const * zFormat = (contentFormat<1) ? "raw" : "html"; content_get(rid, &content); zMime = mimetype_from_content(&content); cson_object_set(zParent, "contentType", json_new_string(zMime ? zMime : "text/plain")); if(!zMime){/* text/plain */ if(0 < blob_size(&content)){ if( 0 < contentFormat ){/*HTML-size it*/ Blob html = empty_blob; wiki_convert(&content, &html, 0); assert( blob_size(&content) < blob_size(&html) ); blob_swap( &html, &content ); assert( blob_size(&content) > blob_size(&html) ); blob_reset( &html ); }/*else as-is*/ } cson_object_set(zParent, "content", cson_value_new_string(blob_str(&content), (unsigned int)blob_size(&content))); }/*else binary: ignore*/ contentSize = blob_size(&content); cson_object_set(zParent, "contentSize", json_new_int(contentSize) ); cson_object_set(zParent, "contentFormat", json_new_string(zFormat) ); blob_reset(&content); } contentSize = db_int64(-1, "SELECT size FROM blob WHERE rid=%d", rid); assert( -1 < contentSize ); cson_object_set(zParent, "size", json_new_int(contentSize) ); parentUuid = db_text(NULL, "SELECT DISTINCT p.uuid " "FROM blob p, blob f, mlink m " "WHERE m.pid=p.rid " "AND m.fid=f.rid " "AND f.rid=%d", 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. */ char const isNew = cson_value_get_bool(cson_object_get(row,"isNew")); char const isDel = cson_value_get_bool(cson_object_get(row,"isDel")); cson_object_set(row, "isNew", NULL); cson_object_set(row, "isDel", NULL); cson_object_set(row, "state", json_new_string( json_artifact_status_to_string(isNew, isDel))); cson_array_append( checkin_arr, cson_object_value(row) ); } db_finalize(&q); return cson_object_value(pay); } /* ** Impl of /json/artifact. This basically just determines the type of ** an artifact and forwards the real work to another function. */ cson_value * json_page_artifact(void){ cson_object * pay = NULL; char const * zName = NULL; char const * zType = NULL; char const * zUuid = NULL; cson_value * entry = NULL; Blob uuid = empty_blob; int rc; int rid = 0; ArtifactDispatchEntry const * dispatcher = &ArtifactDispatchList[0]; zName = json_find_option_cstr2("name", NULL, NULL, g.json.dispatchDepth+1); if(!zName || !*zName) { json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing 'name' argument."); return NULL; } if( validate16(zName, strlen(zName)) ){ if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ zType = "ticket"; goto handle_entry; } if( db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'event-%q*'", zName) ){ zType = "tag"; goto handle_entry; } } blob_set(&uuid,zName); rc = name_to_uuid(&uuid,-1,"*"); /* FIXME: check for a filename if all else fails. */ if(1==rc){ g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND; goto error; }else if(2==rc){ g.json.resultCode = FSL_JSON_E_AMBIGUOUS_UUID; goto error; } zUuid = blob_str(&uuid); rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zUuid); if(0==rid){ g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND; goto error; } if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) || db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) || db_exists("SELECT 1 FROM plink WHERE pid=%d", rid)){ zType = "checkin"; goto handle_entry; }else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){ zType = "wiki"; goto handle_entry; }else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" " WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){ zType = "ticket"; goto handle_entry; }else if ( db_exists("SELECT 1 FROM mlink WHERE fid = %d", rid) ){ zType = "file"; goto handle_entry; }else{ g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND; goto error; } error: assert( 0 != g.json.resultCode ); goto veryend; handle_entry: pay = cson_new_object(); assert( (NULL != zType) && "Internal dispatching error." ); for( ; dispatcher->name; ++dispatcher ){ if(0!=fossil_strcmp(dispatcher->name, zType)){ continue; }else{ entry = (*dispatcher->func)(pay, rid); break; } } if(entry==0){ g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND /* This is not quite right. We need a new result code for this case. */; g.zErrMsg = mprintf("Missing implementation for " "artifacts of this type."); goto error; } if(!g.json.resultCode){ assert( NULL != entry ); assert( NULL != zType ); cson_object_set( pay, "type", json_new_string(zType) ); cson_object_set( pay, "uuid", json_new_string(zUuid) ); /*cson_object_set( pay, "name", json_new_string(zName ? zName : zUuid) );*/ /*cson_object_set( pay, "rid", cson_value_new_integer(rid) );*/ if(cson_value_is_object(entry) && (cson_value_get_object(entry) != pay)){ cson_object_set(pay, "artifact", entry); } } veryend: blob_reset(&uuid); if(g.json.resultCode && pay){ cson_free_object(pay); pay = NULL; } return cson_object_value(pay); } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_branch.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "VERSION.h" #include "config.h" #include "json_branch.h" #if INTERFACE #include "json_detail.h" #endif static cson_value * json_branch_list(void); static cson_value * json_branch_create(void); /* ** Mapping of /json/branch/XXX commands/paths to callbacks. */ static const JsonPageDef JsonPageDefs_Branch[] = { {"create", json_branch_create, 0}, {"list", json_branch_list, 0}, {"new", json_branch_create, -1/* for compat with non-JSON branch command.*/}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; /* ** Implements the /json/branch family of pages/commands. Far from ** complete. ** */ cson_value * json_page_branch(void){ return json_page_dispatch_helper(&JsonPageDefs_Branch[0]); } /* ** Impl for /json/branch/list ** ** ** CLI mode options: ** ** -r|--range X, where X is one of (open,closed,all) ** (only the first letter is significant, default=open) ** -a (same as --range a) ** -c (same as --range c) ** ** HTTP mode options: ** ** "range" GET/POST.payload parameter. FIXME: currently we also use ** POST, but really want to restrict this to POST.payload. */ static cson_value * json_branch_list(void){ 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 = empty_Stmt; if( !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); return NULL; } payV = cson_value_new_object(); pay = cson_value_get_object(payV); listV = cson_value_new_array(); list = cson_value_get_array(listV); if(fossil_has_json()){ range = json_getenv_cstr("range"); } range = json_find_option_cstr("range",NULL,"r"); if((!range||!*range) && !g.isHTTP){ range = find_option("all","a",0); if(range && *range){ range = "a"; }else{ range = find_option("closed","c",0); if(range&&*range){ range = "c"; } } } 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, 0, 0, 0); /* Allow a user? */ 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", __FILE__,__LINE__); } } if( sawConversionError ){ json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,"%s",sawConversionError); free(sawConversionError); } db_finalize(&q); return payV; } /* ** Parameters for the create-branch operation. */ typedef struct BranchCreateOptions{ char const * zName; char const * zBasis; char const * zColor; int isPrivate; /** Might be set to an error string by json_branch_new(). */ char const * rcErrMsg; } BranchCreateOptions; /* ** Tries to create a new branch based on the options set in zOpt. If ** an error is encountered, zOpt->rcErrMsg _might_ be set to a ** descriptive string and one of the FossilJsonCodes values will be ** returned. Or fossil_fatal() (or similar) might be called, exiting ** the app. ** ** On success 0 is returned and if zNewRid is not NULL then the rid of ** the new branch is assigned to it. ** ** If zOpt->isPrivate is 0 but the parent branch is private, ** zOpt->isPrivate will be set to a non-zero value and the new branch ** will be private. */ static int json_branch_new(BranchCreateOptions * zOpt, int *zNewRid){ /* Mostly copied from branch.c:branch_new(), but refactored a small bit to not produce output or interact with the user. The down-side to that is that we dropped the gpg-signing. It was either that or abort the creation if we couldn't sign. We can't sign over HTTP mode, anyway. */ char const * zBranch = zOpt->zName; char const * zBasis = zOpt->zBasis; char const * zColor = zOpt->zColor; int rootid; /* RID of the root check-in - what we branch off of */ int brid; /* RID of the branch check-in */ int i; /* Loop counter */ char *zUuid; /* Artifact ID of origin */ Stmt q; /* Generic query */ char *zDate; /* Date that branch was created */ char *zComment; /* Check-in comment for the new branch */ Blob branch; /* manifest for the new branch */ Manifest *pParent; /* Parsed parent manifest */ Blob mcksum; /* Self-checksum on the manifest */ int bAutoColor = 0; /* Value of "--bgcolor" is "auto" */ if( fossil_strncmp(zColor, "auto", 4)==0 ) { bAutoColor = 1; zColor = 0; } /* fossil branch new name */ if( zBranch==0 || zBranch[0]==0 ){ zOpt->rcErrMsg = "Branch name may not be null/empty."; return FSL_JSON_E_INVALID_ARGS; } if( db_exists( "SELECT 1 FROM tagxref" " WHERE tagtype>0" " AND tagid=(SELECT tagid FROM tag WHERE tagname='sym-%q')", zBranch)!=0 ){ zOpt->rcErrMsg = "Branch already exists."; return FSL_JSON_E_RESOURCE_ALREADY_EXISTS; } db_begin_transaction(); rootid = name_to_typed_rid(zBasis, "ci"); if( rootid==0 ){ zOpt->rcErrMsg = "Basis branch not found."; return FSL_JSON_E_RESOURCE_NOT_FOUND; } pParent = manifest_get(rootid, CFTYPE_MANIFEST, 0); if( pParent==0 ){ zOpt->rcErrMsg = "Could not read parent manifest."; return FSL_JSON_E_UNKNOWN; } /* Create a manifest for the new branch */ blob_zero(&branch); if( pParent->zBaseline ){ blob_appendf(&branch, "B %s\n", pParent->zBaseline); } zComment = mprintf("Create new branch named \"%s\" " "from \"%s\".", zBranch, zBasis); blob_appendf(&branch, "C %F\n", zComment); free(zComment); zDate = date_in_standard_format("now"); blob_appendf(&branch, "D %s\n", zDate); free(zDate); /* Copy all of the content from the parent into the branch */ for(i=0; i<pParent->nFile; ++i){ blob_appendf(&branch, "F %F", pParent->aFile[i].zName); if( pParent->aFile[i].zUuid ){ blob_appendf(&branch, " %s", pParent->aFile[i].zUuid); if( pParent->aFile[i].zPerm && pParent->aFile[i].zPerm[0] ){ blob_appendf(&branch, " %s", pParent->aFile[i].zPerm); } } blob_append(&branch, "\n", 1); } zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid); blob_appendf(&branch, "P %s\n", zUuid); free(zUuid); if( pParent->zRepoCksum ){ blob_appendf(&branch, "R %s\n", pParent->zRepoCksum); } manifest_destroy(pParent); /* Add the symbolic branch name and the "branch" tag to identify ** this as a new branch */ if( content_is_private(rootid) ) zOpt->isPrivate = 1; if( zOpt->isPrivate && zColor==0 && !bAutoColor) zColor = "#fec084"; if( zColor!=0 ){ blob_appendf(&branch, "T *bgcolor * %F\n", zColor); } blob_appendf(&branch, "T *branch * %F\n", zBranch); blob_appendf(&branch, "T *sym-%F *\n", zBranch); /* Cancel all other symbolic tags */ db_prepare(&q, "SELECT tagname FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" " AND tagtype>0 AND tagname GLOB 'sym-*'" " ORDER BY tagname", 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", g.zLogin); md5sum_blob(&branch, &mcksum); blob_appendf(&branch, "Z %b\n", &mcksum); brid = content_put_ex(&branch, 0, 0, 0, zOpt->isPrivate); if( brid==0 ){ fossil_panic("Problem committing manifest: %s", g.zErrMsg); } db_add_unsent(brid); if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ fossil_panic("%s", g.zErrMsg); } assert( blob_is_reset(&branch) ); content_deltify(rootid, &brid, 1, 0); if( zNewRid ){ *zNewRid = brid; } /* Commit */ db_end_transaction(0); return 0; } /* ** Impl of /json/branch/create. */ static cson_value * json_branch_create(void){ cson_value * payV = NULL; cson_object * pay = NULL; int rc = 0; BranchCreateOptions opt; char * zUuid = NULL; int rid = 0; if( !g.perm.Write ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'i' permissions."); return NULL; } memset(&opt,0,sizeof(BranchCreateOptions)); if(fossil_has_json()){ opt.zName = json_getenv_cstr("name"); } if(!opt.zName){ opt.zName = json_command_arg(g.json.dispatchDepth+1); } if(!opt.zName){ json_set_err(FSL_JSON_E_MISSING_ARGS, "'name' parameter was not specified." ); return NULL; } opt.zColor = json_find_option_cstr("bgColor","bgcolor",NULL); opt.zBasis = json_find_option_cstr("basis",NULL,NULL); if(!opt.zBasis && !g.isHTTP){ opt.zBasis = json_command_arg(g.json.dispatchDepth+2); } if(!opt.zBasis){ opt.zBasis = "trunk"; } opt.isPrivate = json_find_option_bool("private",NULL,NULL,-1); if(-1==opt.isPrivate){ if(!g.isHTTP){ opt.isPrivate = (NULL != find_option("private","",0)); }else{ opt.isPrivate = 0; } } rc = json_branch_new( &opt, &rid ); if(rc){ json_set_err(rc, "%s", opt.rcErrMsg); goto error; } assert(0 != rid); payV = cson_value_new_object(); pay = cson_value_get_object(payV); cson_object_set(pay,"name",json_new_string(opt.zName)); cson_object_set(pay,"basis",json_new_string(opt.zBasis)); cson_object_set(pay,"rid",json_new_int(rid)); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); cson_object_set(pay,"uuid", json_new_string(zUuid)); cson_object_set(pay, "isPrivate", cson_value_new_bool(opt.isPrivate)); free(zUuid); if(opt.zColor){ cson_object_set(pay,"bgColor",json_new_string(opt.zColor)); } goto ok; error: assert( 0 != g.json.resultCode ); cson_value_free(payV); payV = NULL; ok: return payV; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_config.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "VERSION.h" #include "config.h" #include "json_config.h" #if INTERFACE #include "json_detail.h" #endif static cson_value * json_config_get(void); static cson_value * json_config_save(void); /* ** Mapping of /json/config/XXX commands/paths to callbacks. */ static const JsonPageDef JsonPageDefs_Config[] = { {"get", json_config_get, 0}, {"save", json_config_save, 0}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; static cson_value * json_settings_get(void); static cson_value * json_settings_set(void); /* ** Mapping of /json/settings/XXX commands/paths to callbacks. */ static const JsonPageDef JsonPageDefs_Settings[] = { {"get", json_settings_get, 0}, {"set", json_settings_set, 0}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; /* ** Implements the /json/config family of pages/commands. ** */ cson_value * json_page_config(void){ return json_page_dispatch_helper(&JsonPageDefs_Config[0]); } /* ** Implements the /json/settings family of pages/commands. ** */ cson_value * json_page_settings(void){ return json_page_dispatch_helper(&JsonPageDefs_Settings[0]); } /* ** JSON-internal mapping of config options to config groups. This is ** 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 }, { "icon-mimetype", CONFIGSET_SKIN }, { "icon-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 }, { "crlf-glob", CONFIGSET_PROJ }, { "crnl-glob", CONFIGSET_PROJ }, { "encoding-glob", CONFIGSET_PROJ }, { "empty-dirs", 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 }, { "ticket-reportlist", CONFIGSET_TKT }, { "ticket-report-template", CONFIGSET_TKT }, { "ticket-key-template", CONFIGSET_TKT }, { "ticket-title-expr", CONFIGSET_TKT }, { "ticket-closed-expr", CONFIGSET_TKT }, {NULL, 0} }; /* ** Impl of /json/config/get. Requires setup rights. ** */ static cson_value * json_config_get(void){ cson_object * pay = NULL; Stmt q = empty_Stmt; Blob sql = empty_blob; char const * zName = NULL; int confMask = 0; char optSkinBackups = 0; unsigned int i; if(!g.perm.Setup){ json_set_err(FSL_JSON_E_DENIED, "Requires 's' permissions."); return NULL; } i = g.json.dispatchDepth + 1; 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); return NULL; } } if(!confMask && !optSkinBackups){ json_set_err(FSL_JSON_E_MISSING_ARGS, "No configuration area(s) selected."); } blob_append(&sql, "SELECT name, value" " FROM config " " WHERE 0 ", -1); { 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))); } db_finalize(&q); return cson_object_value(pay); } /* ** Impl of /json/config/save. ** ** TODOs: */ static cson_value * json_config_save(void){ json_set_err(FSL_JSON_E_NYI, NULL); return NULL; } /* ** Impl of /json/settings/get. */ static cson_value * json_settings_get(void){ cson_object * pay = cson_new_object(); /* output payload */ int nSetting, i; /* setting count and loop var */ const Setting *aSetting = setting_info(&nSetting); const char * zRevision = 0; /* revision to look for versioned settings in */ char * zUuid = 0; /* Resolved UUID of zRevision */ Stmt q = empty_Stmt; /* Config-search query */ Stmt qFoci = empty_Stmt; /* foci query */ if( !g.perm.Read ){ json_set_err( FSL_JSON_E_DENIED, "Fetching settings requires 'o' access." ); return NULL; } zRevision = json_find_option_cstr("version",NULL,NULL); if( 0!=zRevision ){ int rid = name_to_uuid2(zRevision, "ci", &zUuid); if(rid<=0){ json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "Cannot find the given version."); return NULL; } db_multi_exec("CREATE VIRTUAL TABLE IF NOT EXISTS " "temp.foci USING files_of_checkin;"); db_prepare(&qFoci, "SELECT uuid FROM temp.foci WHERE " "checkinID=%d AND filename='.fossil-settings/' || :name", rid); } zRevision = 0; if( g.localOpen ){ db_prepare(&q, "SELECT 'checkout', value FROM vvar WHERE name=:name" " UNION ALL " "SELECT 'repo', value FROM config WHERE name=:name" ); }else{ db_prepare(&q, "SELECT 'repo', value FROM config WHERE name=:name" ); } for(i=0; i<nSetting; ++i){ const Setting *pSet = &aSetting[i]; cson_object * jSet; cson_value * pVal = 0, * pSrc = 0; jSet = cson_new_object(); cson_object_set(pay, pSet->name, cson_object_value(jSet)); cson_object_set(jSet, "versionable",cson_value_new_bool(pSet->versionable)); cson_object_set(jSet, "sensitive", cson_value_new_bool(pSet->sensitive)); cson_object_set(jSet, "defaultValue", (pSet->def && pSet->def[0]) ? json_new_string(pSet->def) : cson_value_null()); if( 0==pSet->sensitive || 0!=g.perm.Setup ){ if( pSet->versionable ){ /* Check to see if this is overridden by a versionable ** settings file */ if( 0!=zUuid ){ /* Attempt to find a versioned setting stored in the given ** check-in version. */ db_bind_text(&qFoci, ":name", pSet->name); if( SQLITE_ROW==db_step(&qFoci) ){ int frid = fast_uuid_to_rid(db_column_text(&qFoci, 0)); Blob content; blob_zero(&content); if( 0!=content_get(frid, &content) ){ pSrc = json_new_string("versioned"); pVal = json_new_string(blob_str(&content)); } blob_reset(&content); } db_reset(&qFoci); } if( 0==pSrc && g.localOpen ){ /* Pull value from a checkout-local .fossil-settings/X file, ** if one exists. */ Blob versionedPathname; blob_zero(&versionedPathname); blob_appendf(&versionedPathname, "%s.fossil-settings/%s", g.zLocalRoot, pSet->name); if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ Blob content; blob_zero(&content); blob_read_from_file(&content, blob_str(&versionedPathname),ExtFILE); pSrc = json_new_string("versioned"); pVal = json_new_string(blob_str(&content)); blob_reset(&content); } blob_reset(&versionedPathname); } } if( 0==pSrc ){ /* Setting is not versionable or we had no versioned value, so ** use the value from localdb.vvar or repository.config (in ** that order). */ db_bind_text(&q, ":name", pSet->name); if( SQLITE_ROW==db_step(&q) ){ pSrc = json_new_string(db_column_text(&q, 0)); pVal = json_new_string(db_column_text(&q, 1)); } db_reset(&q); } } cson_object_set(jSet, "valueSource", pSrc ? pSrc : cson_value_null()); cson_object_set(jSet, "value", pVal ? pVal : cson_value_null()); }/*aSetting loop*/ db_finalize(&q); db_finalize(&qFoci); fossil_free(zUuid); return cson_object_value(pay); } /* ** Impl of /json/settings/set. ** ** Input payload is an object mapping setting names to values. All ** values are set in the repository.config table. It has no response ** payload. */ static cson_value * json_settings_set(void){ Stmt q = empty_Stmt; /* Config-set query */ cson_object_iterator objIter = cson_object_iterator_empty; cson_kvp * pKvp; int nErr = 0, nProp = 0; if( 0==g.perm.Setup ){ json_set_err( FSL_JSON_E_DENIED, "Setting settings requires 's' access." ); return NULL; } else if( 0==g.json.reqPayload.o ){ json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing payload of setting-to-value mappings."); return NULL; } db_unprotect(PROTECT_CONFIG); db_prepare(&q, "INSERT OR REPLACE INTO config (name, value, mtime) " "VALUES(:name, :value, CAST(strftime('%%s') AS INT))" ); db_begin_transaction(); cson_object_iter_init( g.json.reqPayload.o, &objIter ); while( (pKvp = cson_object_iter_next(&objIter)) ){ char const * zKey = cson_string_cstr( cson_kvp_key(pKvp) ); cson_value * pVal; const Setting *pSetting = db_find_setting( zKey, 0 ); if( 0==pSetting ){ nErr = json_set_err(FSL_JSON_E_INVALID_ARGS, "Unknown setting: %s", zKey); break; } pVal = cson_kvp_value(pKvp); switch( cson_value_type_id(pVal) ){ case CSON_TYPE_NULL: db_multi_exec("DELETE FROM config WHERE name=%Q", pSetting->name); continue; case CSON_TYPE_BOOL: db_bind_int(&q, ":value", cson_value_get_bool(pVal) ? 1 : 0); break; case CSON_TYPE_INTEGER: db_bind_int64(&q, ":value", cson_value_get_integer(pVal)); break; case CSON_TYPE_DOUBLE: db_bind_double(&q, ":value", cson_value_get_double(pVal)); break; case CSON_TYPE_STRING: db_bind_text(&q, ":value", cson_value_get_cstr(pVal)); break; default: nErr = json_set_err(FSL_JSON_E_USAGE, "Invalid value type for setting '%s'.", pSetting->name); break; } if( 0!=nErr ) break; db_bind_text(&q, ":name", zKey); db_step(&q); db_reset(&q); ++nProp; } db_finalize(&q); if( 0==nErr && 0==nProp ){ nErr = json_set_err(FSL_JSON_E_INVALID_ARGS, "Payload contains no settings to set."); } db_end_transaction(nErr); db_protect_pop(); return NULL; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_detail.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #ifdef FOSSIL_ENABLE_JSON #if !defined(FOSSIL_JSON_DETAIL_H_INCLUDED) #define FOSSIL_JSON_DETAIL_H_INCLUDED /* ** 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/ ** */ #if !defined(_RC_COMPILE_) #include "cson_amalgamation.h" #endif /* !defined(_RC_COMPILE_) */ /** FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest "significant" change to the JSON API (a change in an interface or new functionality). It is sent as part of the /json/version request. We could arguably add it to each response or even add a version number to each response type, allowing very fine (too fine?) granularity in compatibility change notification. The version number could be included in part of the command dispatching framework, allowing the top-level dispatching code to deal with it (for the most part). */ #define FOSSIL_JSON_API_VERSION "20120713" /* ** Impl details for the JSON API which need to be shared ** across multiple C files. */ /* ** The "official" list of Fossil/JSON error codes. Their values might ** very well change during initial development but after their first ** public release they must stay stable. ** ** Values must be in the range 1000..9999 for error codes and 1..999 ** for warning codes. ** ** Numbers evenly dividable by 100 are "categories", and error codes ** for a given category have their high bits set to the category ** value. ** ** Maintenance reminder: when entries are added to this list, update ** the code in json_page_resultCodes() and json_err_cstr() (both in ** json.c)! ** */ #if !defined(_RC_COMPILE_) enum FossilJsonCodes { FSL_JSON_W_START = 0, FSL_JSON_W_UNKNOWN /*+1*/, FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/, FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/, FSL_JSON_W_STRING_TO_ARRAY_FAILED /*+4*/, FSL_JSON_W_TAG_NOT_FOUND /*+5*/, FSL_JSON_W_END = 1000, FSL_JSON_E_GENERIC = 1000, FSL_JSON_E_GENERIC_SUB1 = FSL_JSON_E_GENERIC + 100, FSL_JSON_E_INVALID_REQUEST /*+1*/, FSL_JSON_E_UNKNOWN_COMMAND /*+2*/, FSL_JSON_E_UNKNOWN /*+3*/, /*REUSE: +4*/ FSL_JSON_E_TIMEOUT /*+5*/, FSL_JSON_E_ASSERT /*+6*/, FSL_JSON_E_ALLOC /*+7*/, FSL_JSON_E_NYI /*+8*/, FSL_JSON_E_PANIC /*+9*/, FSL_JSON_E_MANIFEST_READ_FAILED /*+10*/, FSL_JSON_E_FILE_OPEN_FAILED /*+11*/, FSL_JSON_E_AUTH = 2000, FSL_JSON_E_MISSING_AUTH /*+1*/, FSL_JSON_E_DENIED /*+2*/, FSL_JSON_E_WRONG_MODE /*+3*/, FSL_JSON_E_LOGIN_FAILED = FSL_JSON_E_AUTH +100, FSL_JSON_E_LOGIN_FAILED_NOSEED /*+1*/, FSL_JSON_E_LOGIN_FAILED_NONAME /*+2*/, FSL_JSON_E_LOGIN_FAILED_NOPW /*+3*/, FSL_JSON_E_LOGIN_FAILED_NOTFOUND /*+4*/, FSL_JSON_E_USAGE = 3000, FSL_JSON_E_INVALID_ARGS /*+1*/, FSL_JSON_E_MISSING_ARGS /*+2*/, FSL_JSON_E_AMBIGUOUS_UUID /*+3*/, FSL_JSON_E_UNRESOLVED_UUID /*+4*/, FSL_JSON_E_RESOURCE_ALREADY_EXISTS /*+5*/, FSL_JSON_E_RESOURCE_NOT_FOUND /*+6*/, FSL_JSON_E_DB = 4000, FSL_JSON_E_STMT_PREP /*+1*/, FSL_JSON_E_STMT_BIND /*+2*/, FSL_JSON_E_STMT_EXEC /*+3*/, FSL_JSON_E_DB_LOCKED /*+4*/, FSL_JSON_E_DB_NEEDS_REBUILD = FSL_JSON_E_DB + 101, FSL_JSON_E_DB_NOT_FOUND = FSL_JSON_E_DB + 102, FSL_JSON_E_DB_NOT_VALID = FSL_JSON_E_DB + 103, /* ** Maintenance reminder: FSL_JSON_E_DB_NOT_FOUND gets triggered in the ** bootstrapping process before we know whether we need to check for ** FSL_JSON_E_DB_NEEDS_CHECKOUT. Thus the former error trumps the ** latter. */ FSL_JSON_E_DB_NEEDS_CHECKOUT = FSL_JSON_E_DB + 104 }; /* ** Signature for JSON page/command callbacks. Each callback is ** responsible for handling one JSON request/command and/or ** dispatching to sub-commands. ** ** By the time the callback is called, json_page_top() (HTTP mode) or ** json_cmd_top() (CLI mode) will have set up the JSON-related ** environment. Implementations may generate a "result payload" of any ** JSON type by returning its value from this function (ownership is ** transferred to the caller). On error they should set ** g.json.resultCode to one of the FossilJsonCodes values and return ** either their payload object or NULL. Note that NULL is a legal ** success value - it simply means the response will contain no ** payload. If g.json.resultCode is non-zero when this function ** returns then the top-level dispatcher will destroy any payload ** returned by this function and will output a JSON error response ** instead. ** ** All of the setup/response code is handled by the top dispatcher ** functions and the callbacks concern themselves only with: ** ** a) Permissions checking (inspecting g.perm). ** b) generating a response payload (if applicable) ** c) Setting g.json's error state (if applicable). See json_set_err(). ** ** It is imperative that NO callback functions EVER output ANYTHING to ** stdout, as that will effectively corrupt any JSON output, and ** almost certainly will corrupt any HTTP response headers. Output ** sent to stderr ends up in my apache log, so that might be useful ** for debugging in some cases, but no such code should be left ** enabled for non-debugging builds. */ typedef cson_value * (*fossil_json_f)(void); /* ** Holds name-to-function mappings for JSON page/command dispatching. ** ** Internally we model page dispatching lists as arrays of these ** objects, where the final entry in the array has a NULL name value ** to act as the end-of-list sentinel. ** */ typedef struct JsonPageDef{ /* ** The commmand/page's name (path, not including leading /json/). ** ** Reminder to self: we cannot use sub-paths with commands this way ** without additional string-splitting downstream. e.g. foo/bar. ** Alternately, we can create different JsonPageDef arrays for each ** subset. */ char const * name; /* ** Returns a payload object for the response. If it returns a ** non-NULL value, the caller owns it. To trigger an error this ** function should set g.json.resultCode to a value from the ** FossilJsonCodes enum. If it sets an error value and returns ** a payload, the payload will be destroyed (not sent with the ** response). */ fossil_json_f func; /* ** Which mode(s) of execution does func() support: ** ** <0 = CLI only, >0 = HTTP only, 0==both ** ** Now that we can simulate POST in CLI mode, the distinction ** between them has disappeared in most (or all) cases, so 0 is ** the standard value. */ int runMode; } JsonPageDef; /* ** Holds common keys used for various JSON API properties. */ typedef struct FossilJsonKeys_{ /** maintainers: please keep alpha sorted (case-insensitive) */ char const * anonymousSeed; char const * authToken; char const * commandPath; char const * mtime; char const * payload; char const * requestId; char const * resultCode; char const * resultText; char const * timestamp; } FossilJsonKeys_; extern const FossilJsonKeys_ FossilJsonKeys; /* ** A page/command dispatch helper for fossil_json_f() implementations. ** pages must be an array of JsonPageDef commands which we can ** dispatch. The final item in the array MUST have a NULL name ** element. ** ** This function takes the command specified in ** json_command_arg(1+g.json.dispatchDepth) and searches pages for a ** matching name. If found then that page's func() is called to fetch ** the payload, which is returned to the caller. ** ** On error, g.json.resultCode is set to one of the FossilJsonCodes ** values and NULL is returned. If non-NULL is returned, ownership is ** transfered to the caller (but the g.json error state might still be ** set in that case, so the caller must check that or pass it on up ** the dispatch chain). */ cson_value * json_page_dispatch_helper(JsonPageDef const * pages); /* ** Convenience wrapper around cson_value_new_string(). ** Returns NULL if str is NULL or on allocation error. */ cson_value * json_new_string( char const * str ); /* ** Similar to json_new_string(), but takes a printf()-style format ** specifiers. Supports the printf extensions supported by fossil's ** mprintf(). Returns NULL if str is NULL or on allocation error. ** ** Maintenance note: json_new_string() is NOT variadic because by the ** time the variadic form was introduced we already had use cases ** which segfaulted via json_new_string() because they contain printf ** markup (e.g. wiki content). Been there, debugged that. */ cson_value * json_new_string_f( char const * fmt, ... ); /* ** Returns true if fossil is running in JSON mode and we are either ** running in HTTP mode OR g.json.post.o is not NULL (meaning POST ** data was fed in from CLI mode). ** ** Specifically, it will return false when any of these apply: ** ** a) Not running in JSON mode (via json command or /json path). ** ** 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(void); enum json_get_changed_files_flags { json_get_changed_files_ELIDE_PARENT = 1 << 0 }; #endif /* !defined(_RC_COMPILE_) */ #endif/*FOSSIL_JSON_DETAIL_H_INCLUDED*/ #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_diff.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "config.h" #include "json_diff.h" #if INTERFACE #include "json_detail.h" #endif /* ** Generates a diff between two versions (zFrom and zTo), using nContext ** content lines in the output. On success, returns a new JSON String ** object. On error it sets g.json's error state and returns NULL. ** ** If fSbs is true (non-0) them side-by-side diffs are used. ** ** If fHtml is true then HTML markup is added to the diff. */ cson_value * json_generate_diff(const char *zFrom, const char *zTo, int nContext, char fSbs, char fHtml){ int fromid; int toid; int outLen; DiffConfig DCfg; Blob from = empty_blob, to = empty_blob, out = empty_blob; cson_value * rc = NULL; int flags = (fSbs ? DIFF_SIDEBYSIDE : 0) | (fHtml ? DIFF_HTML : 0); fromid = name_to_typed_rid(zFrom, "*"); if(fromid<=0){ json_set_err(FSL_JSON_E_UNRESOLVED_UUID, "Could not resolve 'from' ID."); return NULL; } toid = name_to_typed_rid(zTo, "*"); if(toid<=0){ json_set_err(FSL_JSON_E_UNRESOLVED_UUID, "Could not resolve 'to' ID."); return NULL; } content_get(fromid, &from); content_get(toid, &to); blob_zero(&out); diff_config_init(&DCfg, flags); text_diff(&from, &to, &out, &DCfg); blob_reset(&from); blob_reset(&to); outLen = blob_size(&out); if(outLen>=0){ rc = cson_value_new_string(blob_buffer(&out), (unsigned int)blob_size(&out)); } blob_reset(&out); return rc; } /* ** Implementation of the /json/diff page. ** ** Arguments: ** ** v1=1st version to diff ** v2=2nd version to diff ** ** Can come from GET, POST.payload, CLI -v1/-v2 or as positional ** parameters following the command name (in HTTP and CLI modes). ** */ cson_value * json_page_diff(void){ cson_object * pay = NULL; cson_value * v = NULL; char const * zFrom; char const * zTo; int nContext = 0; char doSBS; char doHtml; if(!g.perm.Read){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); return NULL; } zFrom = json_find_option_cstr("v1",NULL,NULL); if(!zFrom){ zFrom = json_command_arg(2); } if(!zFrom){ json_set_err(FSL_JSON_E_MISSING_ARGS, "Required 'v1' parameter is missing."); return NULL; } zTo = json_find_option_cstr("v2",NULL,NULL); if(!zTo){ zTo = json_command_arg(3); } if(!zTo){ json_set_err(FSL_JSON_E_MISSING_ARGS, "Required 'v2' parameter is missing."); return NULL; } nContext = json_find_option_int("context",NULL,"c",5); doSBS = json_find_option_bool("sbs",NULL,"y",0); doHtml = json_find_option_bool("html",NULL,"h",0); v = json_generate_diff(zFrom, zTo, nContext, doSBS, doHtml); if(!v){ if(!g.json.resultCode){ json_set_err(FSL_JSON_E_UNKNOWN, "Generating diff failed for unknown reason."); } return NULL; } pay = cson_new_object(); cson_object_set(pay, "from", json_new_string(zFrom)); cson_object_set(pay, "to", json_new_string(zTo)); cson_object_set(pay, "diff", v); v = 0; return pay ? cson_object_value(pay) : NULL; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_dir.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "VERSION.h" #include "config.h" #include "json_dir.h" #if INTERFACE #include "json_detail.h" #endif static cson_value * json_page_dir_list(void); /* ** Mapping of /json/wiki/XXX commands/paths to callbacks. */ #if 0 /* TODO: Not used? */ static const JsonPageDef JsonPageDefs_Dir[] = { /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; #endif #if 0 /* TODO: Not used? */ static char const * json_dir_path_extra(void){ static char const * zP = NULL; if( !zP ){ zP = g.zExtra; while(zP && *zP && ('/'==*zP)){ ++zP; } } return zP; } #endif /* ** Impl of /json/dir. 98% of it was taken directly ** from browse.c::page_dir() */ static cson_value * json_page_dir_list(void){ cson_object * zPayload = NULL; /* return value */ cson_array * zEntries = NULL; /* accumulated list of entries. */ cson_object * zEntry = NULL; /* a single dir/file entry. */ cson_array * keyStore = NULL; /* garbage collector for shared strings. */ cson_string * zKeyName = NULL; cson_string * zKeySize = NULL; cson_string * zKeyIsDir = NULL; cson_string * zKeyUuid = NULL; cson_string * zKeyTime = NULL; cson_string * zKeyRaw = NULL; char * zD = NULL; char const * zDX = NULL; int nD; char * zUuid = NULL; char const * zCI = NULL; Manifest * pM = NULL; Stmt q = empty_Stmt; int rid = 0; if( !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); return NULL; } zCI = json_find_option_cstr("checkin",NULL,"ci" ); /* 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 && *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); if(!zDX && !g.isHTTP){ zDX = json_command_arg(g.json.dispatchDepth+1); } if(zDX && (!*zDX || (0==strcmp(zDX,"/")))){ zDX = NULL; } zD = zDX ? fossil_strdup(zDX) : NULL; nD = zD ? strlen(zD)+1 : 0; while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, pathelementFunc, 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. */ if( zCI ){ Stmt ins; ManifestFile *pFile; ManifestFile *pPrev = 0; int nPrev = 0; int c; db_multi_exec( "CREATE TEMP TABLE json_dir_files(" " n UNIQUE NOT NULL," /* file name */ " fn UNIQUE NOT NULL," /* full file name */ " u DEFAULT NULL," /* file uuid */ " sz DEFAULT -1," /* file size */ " mtime DEFAULT NULL" /* file mtime in unix epoch format */ ");" ); db_prepare(&ins, "INSERT OR IGNORE INTO json_dir_files (n,fn,u,sz,mtime) " "SELECT" " pathelement(:path,0)," " CASE WHEN %Q IS NULL THEN '' ELSE %Q||'/' END ||:abspath," " a.uuid," " a.size," " CAST(strftime('%%s',e.mtime) AS INTEGER) " "FROM" " mlink m, " " event e," " blob a," " blob b " "WHERE" " e.objid=m.mid" " AND a.rid=m.fid"/*FILE artifact*/ " AND b.rid=m.mid"/*CHECKIN artifact*/ " AND a.uuid=:uuid", zD, zD ); manifest_file_rewind(pM); while( (pFile = manifest_file_next(pM,0))!=0 ){ if( nD>0 && ((pFile->zName[nD-1]!='/') || (0!=memcmp(pFile->zName, zD, nD-1))) ){ continue; } /*printf("zD=%s, nD=%d, pFile->zName=%s\n", zD, nD, pFile->zName);*/ if( pPrev && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 && (pFile->zName[nD+nPrev]==0 || pFile->zName[nD+nPrev]=='/') ){ continue; } db_bind_text( &ins, ":path", &pFile->zName[nD] ); db_bind_text( &ins, ":abspath", &pFile->zName[nD] ); db_bind_text( &ins, ":uuid", pFile->zUuid ); db_step(&ins); db_reset(&ins); pPrev = pFile; for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} if( c=='/' ) nPrev++; } db_finalize(&ins); }else if( zD && *zD ){ db_multi_exec( "CREATE TEMP VIEW json_dir_files AS" " SELECT DISTINCT(pathelement(name,%d)) AS n," " %Q||'/'||name AS fn," " NULL AS u, NULL AS sz, NULL AS mtime" " FROM filename" " WHERE name GLOB '%q/*'" " GROUP BY n", nD, zD, zD ); }else{ db_multi_exec( "CREATE TEMP VIEW json_dir_files" " AS SELECT DISTINCT(pathelement(name,0)) AS n, NULL AS fn" " FROM filename" ); } if(zCI){ db_prepare( &q, "SELECT" " n as name," " fn as fullname," " u as uuid," " sz as size," " mtime as mtime " "FROM json_dir_files ORDER BY n"); }else{/* UUIDs are all NULL. */ db_prepare( &q, "SELECT n, fn FROM json_dir_files ORDER BY n"); } zKeyName = cson_new_string("name",4); zKeyUuid = cson_new_string("uuid",4); zKeyIsDir = cson_new_string("isDir",5); keyStore = cson_new_array(); cson_array_append( keyStore, cson_string_value(zKeyName) ); cson_array_append( keyStore, cson_string_value(zKeyUuid) ); cson_array_append( keyStore, cson_string_value(zKeyIsDir) ); if( zCI ){ zKeySize = cson_new_string("size",4); cson_array_append( keyStore, cson_string_value(zKeySize) ); zKeyTime = cson_new_string("timestamp",9); cson_array_append( keyStore, cson_string_value(zKeyTime) ); zKeyRaw = cson_new_string("downloadPath",12); cson_array_append( keyStore, cson_string_value(zKeyRaw) ); } zPayload = cson_new_object(); cson_object_set_s( zPayload, zKeyName, json_new_string((zD&&*zD) ? zD : "/") ); if( zUuid ){ cson_object_set( zPayload, "checkin", json_new_string(zUuid) ); } while( (SQLITE_ROW==db_step(&q)) ){ cson_value * name = NULL; char const * n = db_column_text(&q,0); char const isDir = ('/'==*n); zEntry = cson_new_object(); if(!zEntries){ zEntries = cson_new_array(); cson_object_set( zPayload, "entries", cson_array_value(zEntries) ); } cson_array_append(zEntries, cson_object_value(zEntry) ); if(isDir){ name = json_new_string( n+1 ); cson_object_set_s(zEntry, zKeyIsDir, cson_value_true() ); } else{ name = json_new_string( n ); } cson_object_set_s(zEntry, zKeyName, name ); if( zCI && !isDir){ /* Don't add the uuid/size for dir entries - that data refers to one of the files in that directory :/. Entries with no --checkin may refer to N versions, and therefore we cannot associate a single size and uuid with them (and fetching all would be overkill for most use cases). */ char const * fullName = db_column_text(&q,1); char const * u = db_column_text(&q,2); sqlite_int64 const sz = db_column_int64(&q,3); sqlite_int64 const ts = db_column_int64(&q,4); cson_object_set_s(zEntry, zKeyUuid, json_new_string( u ) ); cson_object_set_s(zEntry, zKeySize, cson_value_new_integer( (cson_int_t)sz )); cson_object_set_s(zEntry, zKeyTime, cson_value_new_integer( (cson_int_t)ts )); cson_object_set_s(zEntry, zKeyRaw, json_new_string_f("/raw/%T?name=%t", fullName, u)); } } db_finalize(&q); if(pM){ manifest_destroy(pM); } cson_free_array( keyStore ); free( zUuid ); free( zD ); return cson_object_value(zPayload); } /* ** Implements the /json/dir family of pages/commands. ** */ cson_value * json_page_dir(void){ #if 1 return json_page_dir_list(); #else return json_page_dispatch_helper(&JsonPageDefs_Dir[0]); #endif } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_finfo.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "VERSION.h" #include "config.h" #include "json_finfo.h" #if INTERFACE #include "json_detail.h" #endif /* ** Implements the /json/finfo page/command. ** */ cson_value * json_page_finfo(void){ cson_object * pay = NULL; cson_array * checkins = NULL; char const * zFilename = NULL; Blob sql = empty_blob; Stmt q = empty_Stmt; char const * zAfter = NULL; char const * zBefore = NULL; int limit = -1; int currentRow = 0; char const * zCheckin = NULL; signed char sort = -1; if(!g.perm.Read){ json_set_err(FSL_JSON_E_DENIED,"Requires 'o' privileges."); return NULL; } json_warn( FSL_JSON_W_UNKNOWN, "Achtung: the output of the finfo command is up for change."); /* For the "name" argument we have to jump through some hoops to make sure that we don't get the fossil-internally-assigned "name" option. */ zFilename = json_find_option_cstr2("name",NULL,NULL, g.json.dispatchDepth+1); if(!zFilename || !*zFilename){ json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing 'name' parameter."); return NULL; } if(0==db_int(0,"SELECT 1 FROM filename WHERE name=%Q",zFilename)){ json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "File entry not found."); return NULL; } 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 hash %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)); } checkins = cson_new_array(); cson_object_set(pay, "checkins", cson_array_value(checkins)); while( db_step(&q)==SQLITE_ROW ){ cson_object * row = cson_new_object(); int const isNew = db_column_int(&q,9); int const isDel = db_column_int(&q,10); cson_array_append( checkins, cson_object_value(row) ); cson_object_set(row, "checkin", json_new_string( db_column_text(&q,1) )); cson_object_set(row, "uuid", json_new_string( db_column_text(&q,2) )); /*cson_object_set(row, "parentArtifact", json_new_string( db_column_text(&q,6) ));*/ cson_object_set(row, "timestamp", json_new_int( db_column_int64(&q,3) )); cson_object_set(row, "user", json_new_string( db_column_text(&q,4) )); cson_object_set(row, "comment", json_new_string( db_column_text(&q,5) )); /*cson_object_set(row, "bgColor", json_new_string( db_column_text(&q,7) ));*/ cson_object_set(row, "size", json_new_int( db_column_int64(&q,8) )); cson_object_set(row, "state", json_new_string( json_artifact_status_to_string(isNew, isDel))); if( (0 < limit) && (++currentRow >= limit) ){ break; } } db_finalize(&q); return pay ? cson_object_value(pay) : NULL; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_login.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "config.h" #include "json_login.h" #if INTERFACE #include "json_detail.h" #endif /* ** Implementation of the /json/login page. ** */ cson_value * json_page_login(void){ char preciseErrors = /* if true, "complete" JSON error codes are used, else they are "dumbed down" to a generic login error code. */ #if 1 g.json.errorDetailParanoia ? 0 : 1 #else 0 #endif ; /* FIXME: we want to check the GET/POST args in this order: - GET: name, n, password, p - POST: name, password but fossil's age-old behaviour of treating the last element of PATH_INFO as the value for the name parameter breaks that. Summary: If we check for P("name") first, then P("n"), then ONLY a GET param of "name" will match ("n" is not recognized). If we reverse the order of the checks then both forms work. The "p"/"password" check is not affected by this. */ char const * name = cson_value_get_cstr(json_req_payload_get("name")); char const * pw = NULL; char const * anonSeed = NULL; cson_value * payload = NULL; int uid = 0; /* reminder to self: Fossil internally (for the sake of /wiki) interprets paths in the form /foo/bar/baz such that P("name") == "bar/baz". This collides with our name/password checking, and thus we do some rather elaborate name=... checking. */ pw = cson_value_get_cstr(json_req_payload_get("password")); if( !pw ){ pw = PD("p",NULL); if( !pw ){ pw = PD("password",NULL); } } if(!pw){ g.json.resultCode = preciseErrors ? FSL_JSON_E_LOGIN_FAILED_NOPW : FSL_JSON_E_LOGIN_FAILED; return NULL; } if( !name ){ name = PD("n",NULL); if( !name ){ name = PD("name",NULL); if( !name ){ g.json.resultCode = preciseErrors ? FSL_JSON_E_LOGIN_FAILED_NONAME : FSL_JSON_E_LOGIN_FAILED; return NULL; } } } if(0 == strcmp("anonymous",name)){ /* check captcha/seed values... */ enum { SeedBufLen = 100 /* in some JSON tests i once actually got an 80-digit number. */ }; static char seedBuffer[SeedBufLen]; cson_value const * jseed = json_getenv(FossilJsonKeys.anonymousSeed); seedBuffer[0] = 0; if( !jseed ){ jseed = json_req_payload_get(FossilJsonKeys.anonymousSeed); if( !jseed ){ jseed = json_getenv("cs") /* name used by HTML interface */; } } if(jseed){ if( cson_value_is_number(jseed) ){ sprintf(seedBuffer, "%"CSON_INT_T_PFMT, cson_value_get_integer(jseed)); anonSeed = seedBuffer; }else if( cson_value_is_string(jseed) ){ anonSeed = cson_string_cstr(cson_value_get_string(jseed)); } } if(!anonSeed){ g.json.resultCode = preciseErrors ? FSL_JSON_E_LOGIN_FAILED_NOSEED : FSL_JSON_E_LOGIN_FAILED; return NULL; } } #if 0 { /* only for debugging the PD()-incorrect-result problem */ cson_object * o = NULL; uid = login_search_uid( &name, pw ); payload = cson_value_new_object(); o = cson_value_get_object(payload); cson_object_set( o, "n", cson_value_new_string(name,strlen(name))); cson_object_set( o, "p", cson_value_new_string(pw,strlen(pw))); return payload; } #endif uid = anonSeed ? login_is_valid_anonymous(name, pw, anonSeed) : login_search_uid(&name, pw) ; if( !uid ){ g.json.resultCode = preciseErrors ? FSL_JSON_E_LOGIN_FAILED_NOTFOUND : FSL_JSON_E_LOGIN_FAILED; return NULL; }else{ char * cookie = NULL; cson_object * po; char * cap = NULL; if(anonSeed){ login_set_anon_cookie(NULL, &cookie, 0); }else{ login_set_user_cookie(name, uid, &cookie, 0); } payload = cson_value_new_object(); po = cson_value_get_object(payload); cson_object_set(po, "authToken", json_new_string(cookie)); free(cookie); cson_object_set(po, "name", json_new_string(name)); cap = db_text(NULL, "SELECT cap FROM user WHERE login=%Q", name); cson_object_set(po, "capabilities", cap ? json_new_string(cap) : cson_value_null() ); free(cap); cson_object_set(po, "loginCookieName", json_new_string( login_cookie_name() ) ); /* TODO: add loginExpiryTime to the payload. To do this properly we "should" add an ([unsigned] int *) to login_set_user_cookie() and login_set_anon_cookie(), to which the expiry time is assigned. (Remember that JSON doesn't do unsigned int.) For non-anonymous users we could also simply query the user.cexpire db field after calling login_set_user_cookie(), but for anonymous we need to get the time when the cookie is set because anon does not get a db entry like normal users do. Anonymous cookies currently have a hard-coded lifetime in login_set_anon_cookie() (currently 6 hours), which we "should arguably" change to use the time configured for non-anonymous users (see login_set_user_cookie() for details). */ return payload; } } /* ** Impl of /json/logout. ** */ cson_value * json_page_logout(void){ cson_value const *token = g.json.authToken; /* Remember that json_bootstrap_late() replaces the login cookie with the JSON auth token if the request contains it. If the request is missing the auth token then this will fetch fossil's original cookie. Either way, it's what we want :). We require the auth token to avoid someone maliciously trying to log someone else out (not 100% sure if that would be possible, given fossil's hardened cookie, but I'll assume it would be for the time being). */ ; if(!token){ g.json.resultCode = FSL_JSON_E_MISSING_AUTH; }else{ login_clear_login_data(); g.json.authToken = NULL /* memory is owned elsewhere.*/; json_setenv(FossilJsonKeys.authToken, NULL); } return json_page_whoami(); } /* ** Implementation of the /json/anonymousPassword page. */ cson_value * json_page_anon_password(void){ cson_value * v = cson_value_new_object(); cson_object * o = cson_value_get_object(v); unsigned const int seed = captcha_seed(); char const * zCaptcha = captcha_decode(seed); cson_object_set(o, "seed", cson_value_new_integer( (cson_int_t)seed ) ); cson_object_set(o, "password", cson_value_new_string( zCaptcha, strlen(zCaptcha) ) ); return v; } /* ** Implements the /json/whoami page/command. */ cson_value * json_page_whoami(void){ cson_value * payload = NULL; cson_object * obj = NULL; Stmt q; if(!g.json.authToken && g.userUid==0){ /* assume we just logged out. */ db_prepare(&q, "SELECT login, cap FROM user WHERE login='nobody'"); } else{ db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d", g.userUid); } if( db_step(&q)==SQLITE_ROW ){ /* reminder: we don't use g.zLogin because it's 0 for the guest user and the HTML UI appears to currently allow the name to be changed (but doing so would break other code). */ char const * str; payload = cson_value_new_object(); obj = cson_value_get_object(payload); str = (char const *)sqlite3_column_text(q.pStmt,0); if( str ){ cson_object_set( obj, "name", cson_value_new_string(str,strlen(str)) ); } str = (char const *)sqlite3_column_text(q.pStmt,1); if( str ){ cson_object_set( obj, "capabilities", cson_value_new_string(str,strlen(str)) ); } if( g.json.authToken ){ cson_object_set( obj, "authToken", g.json.authToken ); } }else{ g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND; } db_finalize(&q); return payload; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_query.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "config.h" #include "json_query.h" #if INTERFACE #include "json_detail.h" #endif /* ** Implementation of the /json/query page. ** ** Requires admin privileges. Intended primarily to assist me in ** coming up with JSON output structures for pending features. ** ** Options/parameters: ** ** sql=string - a SELECT statement ** ** format=string 'a' means each row is an Array of values, 'o' ** (default) creates each row as an Object. ** ** TODO: in CLI mode (only) use -S FILENAME to read the sql ** from a file. */ cson_value * json_page_query(void){ char const * zSql = NULL; cson_value * payV; char const * zFmt; Stmt q = empty_Stmt; int check; if(!g.perm.Admin && !g.perm.Setup){ json_set_err(FSL_JSON_E_DENIED, "Requires 'a' or 's' privileges."); return NULL; } if( cson_value_is_string(g.json.reqPayload.v) ){ zSql = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v)); }else{ zSql = json_find_option_cstr2("sql",NULL,"s",2); } if(!zSql || !*zSql){ 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; } switch(*zFmt){ case 'a': check = cson_sqlite3_stmt_to_json(q.pStmt, &payV, 0); break; case 'o': default: check = cson_sqlite3_stmt_to_json(q.pStmt, &payV, 1); }; db_finalize(&q); if(0 != check){ json_set_err(FSL_JSON_E_UNKNOWN, "Conversion to JSON failed with cson code #%d (%s).", check, cson_rc_string(check)); assert(NULL==payV); } return payV; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_report.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "config.h" #include "json_report.h" #if INTERFACE #include "json_detail.h" #endif static cson_value * json_report_create(void); static cson_value * json_report_get(void); static cson_value * json_report_list(void); static cson_value * json_report_run(void); static cson_value * json_report_save(void); /* ** Mapping of /json/report/XXX commands/paths to callbacks. */ static const JsonPageDef JsonPageDefs_Report[] = { {"create", json_report_create, 0}, {"get", json_report_get, 0}, {"list", json_report_list, 0}, {"run", json_report_run, 0}, {"save", json_report_save, 0}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; /* ** Implementation of the /json/report page. ** ** */ cson_value * json_page_report(void){ if(!g.perm.RdTkt && !g.perm.NewTkt ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'r' or 'n' permissions."); return NULL; } return json_page_dispatch_helper(JsonPageDefs_Report); } /* ** Searches the environment for a "report" parameter ** (CLI: -report/-r #). ** ** If one is not found and argPos is >0 then json_command_arg() ** is checked. ** ** Returns >0 (the report number) on success . */ static int json_report_get_number(int argPos){ int nReport = json_find_option_int("report",NULL,"r",-1); if( (nReport<=0) && cson_value_is_integer(g.json.reqPayload.v)){ nReport = cson_value_get_integer(g.json.reqPayload.v); } if( (nReport <= 0) && (argPos>0) ){ char const * arg = json_command_arg(argPos); if(arg && fossil_isdigit(*arg)) { nReport = atoi(arg); } } return nReport; } static cson_value * json_report_create(void){ json_set_err(FSL_JSON_E_NYI, NULL); return NULL; } static cson_value * json_report_get(void){ int nReport; Stmt q = empty_Stmt; cson_value * pay = NULL; if(!g.perm.TktFmt){ json_set_err(FSL_JSON_E_DENIED, "Requires 't' privileges."); return NULL; } nReport = json_report_get_number(3); if(nReport <=0){ json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing or invalid 'report' (-r) parameter."); return NULL; } db_prepare(&q,"SELECT rn AS report," " owner AS owner," " title AS title," " cast(strftime('%%s',mtime) as int) as timestamp," " cols as columns," " sqlcode as sqlCode" " FROM reportfmt" " WHERE rn=%d", nReport); if( SQLITE_ROW != db_step(&q) ){ db_finalize(&q); json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "Report #%d not found.", nReport); return NULL; } pay = cson_sqlite3_row_to_object(q.pStmt); db_finalize(&q); return pay; } /* ** Impl of /json/report/list. */ static cson_value * json_report_list(void){ Blob sql = empty_blob; cson_value * pay = NULL; if(!g.perm.RdTkt){ json_set_err(FSL_JSON_E_DENIED, "Requires 'r' privileges."); return NULL; } blob_append(&sql, "SELECT" " rn AS report," " title as title," " owner as owner" " FROM reportfmt" " WHERE 1" " ORDER BY title", -1); pay = json_sql_to_array_of_obj(&sql, NULL, 1); if(!pay){ json_set_err(FSL_JSON_E_UNKNOWN, "Quite unexpected: no ticket reports found."); } return pay; } /* ** Impl for /json/report/run ** ** Options/arguments: ** ** report=int (CLI: -report # or -r #) is the report number to run. ** ** limit=int (CLI: -limit # or -n #) -n is for compat. with other commands. ** ** format=a|o Specifies result format: a=each row is an arry, o=each ** row is an object. Default=o. */ static cson_value * json_report_run(void){ int nReport; Stmt q = empty_Stmt; cson_object * pay = NULL; cson_array * tktList = NULL; char const * zFmt; char * zTitle = NULL; Blob sql = empty_blob; int limit = 0; cson_value * colNames = NULL; int i; if(!g.perm.RdTkt){ json_set_err(FSL_JSON_E_DENIED, "Requires 'r' privileges."); return NULL; } nReport = json_report_get_number(3); if(nReport <=0){ json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing or invalid 'number' (-n) parameter."); goto error; } zFmt = json_find_option_cstr2("format",NULL,"f",3); if(!zFmt) zFmt = "o"; db_prepare(&q, "SELECT sqlcode, " " title" " FROM reportfmt" " WHERE rn=%d", nReport); if(SQLITE_ROW != db_step(&q)){ json_set_err(FSL_JSON_E_INVALID_ARGS, "Report number %d not found.", nReport); db_finalize(&q); goto error; } 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){ cson_object_set(pay, "limit", json_new_int((limit<0) ? 0 : limit)); } free(zTitle); zTitle = NULL; if(g.perm.TktFmt){ cson_object_set(pay, "sqlcode", cson_value_new_string(blob_str(&sql), (unsigned int)blob_size(&sql))); } blob_reset(&sql); colNames = cson_sqlite3_column_names(q.pStmt); cson_object_set( pay, "columnNames", colNames); for( i = 0 ; ((limit>0) ?(i < limit) : 1) && (SQLITE_ROW == db_step(&q)); ++i){ cson_value * row = ('a'==*zFmt) ? cson_sqlite3_row_to_array(q.pStmt) : cson_sqlite3_row_to_object2(q.pStmt, cson_value_get_array(colNames)); ; if(row && !tktList){ tktList = cson_new_array(); } cson_array_append(tktList, row); } db_finalize(&q); cson_object_set(pay, "tickets", tktList ? cson_array_value(tktList) : cson_value_null()); goto end; error: assert(0 != g.json.resultCode); cson_value_free( cson_object_value(pay) ); pay = NULL; end: return pay ? cson_object_value(pay) : NULL; } static cson_value * json_report_save(void){ return NULL; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_status.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 | #ifdef FOSSIL_ENABLE_JSON /* ** Copyright (c) 2013 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/ ** */ #include "config.h" #include "json_status.h" #if INTERFACE #include "json_detail.h" #endif /* Reminder to check if a column exists: PRAGMA table_info(table_name) and search for a row where the 'name' field matches. That assumes, of course, that table_info()'s output format is stable. */ /* ** Implementation of the /json/status page. ** */ cson_value * json_page_status(){ Stmt q = empty_Stmt; cson_object * oPay; /*cson_object * files;*/ int vid, nErr = 0; cson_object * tmpO; char * zTmp; i64 iMtime; cson_array * aFiles; if(!db_open_local(0)){ json_set_err(FSL_JSON_E_DB_NEEDS_CHECKOUT, NULL); return NULL; } oPay = cson_new_object(); cson_object_set(oPay, "repository", json_new_string(db_repository_filename())); cson_object_set(oPay, "localRoot", json_new_string(g.zLocalRoot)); vid = db_lget_int("checkout", 0); if(!vid){ json_set_err( FSL_JSON_E_UNKNOWN, "Can this even happen?" ); return 0; } vfile_check_signature(vid, 0); /* TODO: dupe show_common_info() state */ tmpO = cson_new_object(); 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) " "FROM event WHERE objid=%d", vid); cson_object_set(tmpO, "timestamp", cson_value_new_integer((cson_int_t)iMtime)); #if 0 /* TODO: add parent artifact info */ tmpO = cson_new_object(); cson_object_set( oPay, "parent", cson_object_value(tmpO) ); cson_object_set( tmpO, "uuid", TODO ); cson_object_set( tmpO, "timestamp", TODO ); #endif /* Now get the list of non-pristine files... */ aFiles = cson_new_array(); cson_object_set( oPay, "files", cson_array_value( aFiles ) ); db_prepare(&q, "SELECT pathname, deleted, chnged, rid, " " coalesce(origname!=pathname,0), origname" " FROM vfile " " WHERE is_selected(id)" " AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1" ); while( db_step(&q)==SQLITE_ROW ){ cson_array *aStatuses = NULL; 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)==0; int isRenamed = db_column_int(&q,4); cson_object * oFile; char const * zStatus = "???"; char * zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); if( isDeleted ){ zStatus = "deleted"; }else if( isNew ){ zStatus = "new" /* maintenance reminder: MUST come BEFORE the isChnged checks. */; }else if( !file_isfile_or_link(zFullName) ){ if( file_access(zFullName, F_OK)==0 ){ zStatus = "notAFile"; ++nErr; }else{ zStatus = "missing"; ++nErr; } }else if( 2==isChnged ){ zStatus = "updatedByMerge"; }else if( 3==isChnged ){ zStatus = "addedByMerge"; }else if( 4==isChnged ){ zStatus = "updatedByIntegrate"; }else if( 5==isChnged ){ zStatus = "addedByIntegrate"; }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) ); if( isRenamed ){ if( *zStatus!='?' ){ aStatuses = cson_new_array(); cson_object_set( oFile, "status", cson_array_value( aStatuses ) ); cson_array_append(aStatuses, cson_value_new_string(zStatus, strlen(zStatus))); cson_array_append(aStatuses, cson_value_new_string("renamed", 7)); }else{ zStatus = "renamed"; } cson_object_set( oFile, "priorName", cson_sqlite3_column_to_value(q.pStmt,5)); } /* 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", aStatuses!=NULL ? cson_array_value(aStatuses) : json_new_string( zStatus ) ); free(zFullName); } cson_object_set( oPay, "errorCount", json_new_int( nErr ) ); db_finalize(&q); #if 0 /* TODO: add "merged with" status. First need (A) to decide on a structure and (B) to set up some tests for the multi-merge case.*/ db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0"); while( db_step(&q)==SQLITE_ROW ){ const char *zLabel = "MERGED_WITH"; switch( db_column_int(&q, 1) ){ case -1: zLabel = "CHERRYPICK "; break; case -2: zLabel = "BACKOUT "; break; case -4: zLabel = "INTEGRATE "; break; } blob_append(report, zPrefix, nPrefix); blob_appendf(report, "%s %s\n", zLabel, db_column_text(&q, 0)); } db_finalize(&q); if( nErr ){ fossil_fatal("aborting due to prior errors"); } #endif return cson_object_value( oPay ); } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_tag.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "VERSION.h" #include "config.h" #include "json_tag.h" #if INTERFACE #include "json_detail.h" #endif static cson_value * json_tag_add(void); static cson_value * json_tag_cancel(void); static cson_value * json_tag_find(void); static cson_value * json_tag_list(void); /* ** Mapping of /json/tag/XXX commands/paths to callbacks. */ static const JsonPageDef JsonPageDefs_Tag[] = { {"add", json_tag_add, 0}, {"cancel", json_tag_cancel, 0}, {"find", json_tag_find, 0}, {"list", json_tag_list, 0}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; /* ** Implements the /json/tag family of pages/commands. ** */ cson_value * json_page_tag(void){ return json_page_dispatch_helper(&JsonPageDefs_Tag[0]); } /* ** Impl of /json/tag/add. */ static cson_value * json_tag_add(void){ cson_value * payV = NULL; cson_object * pay = NULL; char const * zName = NULL; char const * zCheckin = NULL; char fRaw = 0; char fPropagate = 0; char const * zValue = NULL; const char *zPrefix = NULL; if( !g.perm.Write ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'i' permissions."); return NULL; } fRaw = json_find_option_bool("raw",NULL,NULL,0); fPropagate = json_find_option_bool("propagate",NULL,NULL,0); zName = json_find_option_cstr("name",NULL,NULL); zPrefix = fRaw ? "" : "sym-"; if(!zName || !*zName){ if(!fossil_has_json()){ zName = json_command_arg(3); } if(!zName || !*zName){ json_set_err(FSL_JSON_E_MISSING_ARGS, "'name' parameter is missing."); return NULL; } } zCheckin = json_find_option_cstr("checkin",NULL,NULL); if( !zCheckin ){ if(!fossil_has_json()){ zCheckin = json_command_arg(4); } if(!zCheckin || !*zCheckin){ json_set_err(FSL_JSON_E_MISSING_ARGS, "'checkin' parameter is missing."); return NULL; } } zValue = json_find_option_cstr("value",NULL,NULL); if(!zValue && !fossil_has_json()){ zValue = json_command_arg(5); } db_begin_transaction(); tag_add_artifact(zPrefix, zName, zCheckin, zValue, 1+fPropagate,NULL/*DateOvrd*/,NULL/*UserOvrd*/); db_end_transaction(0); payV = cson_value_new_object(); pay = cson_value_get_object(payV); cson_object_set(pay, "name", json_new_string(zName) ); cson_object_set(pay, "value", (zValue&&*zValue) ? json_new_string(zValue) : cson_value_null()); cson_object_set(pay, "propagate", cson_value_new_bool(fPropagate)); cson_object_set(pay, "raw", cson_value_new_bool(fRaw)); { Blob uu = empty_blob; int rc; blob_append(&uu, zName, -1); rc = name_to_uuid(&uu, 9, "*"); if(0!=rc){ json_set_err(FSL_JSON_E_UNKNOWN, "Could not convert name back to artifact hash!"); blob_reset(&uu); goto error; } cson_object_set(pay, "appliedTo", json_new_string(blob_buffer(&uu))); blob_reset(&uu); } goto ok; error: assert( 0 != g.json.resultCode ); cson_value_free(payV); payV = NULL; ok: return payV; } /* ** Impl of /json/tag/cancel. */ static cson_value * json_tag_cancel(void){ char const * zName = NULL; char const * zCheckin = NULL; char fRaw = 0; const char *zPrefix = NULL; if( !g.perm.Write ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'i' permissions."); return NULL; } fRaw = json_find_option_bool("raw",NULL,NULL,0); zPrefix = fRaw ? "" : "sym-"; zName = json_find_option_cstr("name",NULL,NULL); if(!zName || !*zName){ if(!fossil_has_json()){ zName = json_command_arg(3); } if(!zName || !*zName){ json_set_err(FSL_JSON_E_MISSING_ARGS, "'name' parameter is missing."); return NULL; } } zCheckin = json_find_option_cstr("checkin",NULL,NULL); if( !zCheckin ){ if(!fossil_has_json()){ zCheckin = json_command_arg(4); } if(!zCheckin || !*zCheckin){ json_set_err(FSL_JSON_E_MISSING_ARGS, "'checkin' parameter is missing."); return NULL; } } /* FIXME?: verify that the tag is currently active. We have no real error case unless we do that. */ db_begin_transaction(); tag_add_artifact(zPrefix, zName, zCheckin, NULL, 0, 0, 0); db_end_transaction(0); return NULL; } /* ** Impl of /json/tag/find. */ static cson_value * json_tag_find(void){ cson_value * payV = NULL; cson_object * pay = NULL; cson_value * listV = NULL; cson_array * list = NULL; char const * zName = NULL; char const * zType = NULL; char const * zType2 = NULL; char fRaw = 0; Stmt q = empty_Stmt; int limit = 0; int tagid = 0; if( !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); return NULL; } zName = json_find_option_cstr("name",NULL,NULL); if(!zName || !*zName){ if(!fossil_has_json()){ zName = json_command_arg(3); } if(!zName || !*zName){ json_set_err(FSL_JSON_E_MISSING_ARGS, "'name' parameter is missing."); return NULL; } } zType = json_find_option_cstr("type",NULL,"t"); if(!zType || !*zType){ zType = "*"; zType2 = zType; }else{ switch(*zType){ case 'c': zType = "ci"; zType2 = "checkin"; break; case 'e': zType = "e"; zType2 = "event"; break; case 'w': zType = "w"; zType2 = "wiki"; break; case 't': zType = "t"; zType2 = "ticket"; break; } } limit = json_find_option_int("limit",NULL,"n",0); fRaw = json_find_option_bool("raw",NULL,NULL,0); tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='%s' || %Q", fRaw ? "" : "sym-", zName); payV = cson_value_new_object(); pay = cson_value_get_object(payV); cson_object_set(pay, "name", json_new_string(zName)); cson_object_set(pay, "raw", cson_value_new_bool(fRaw)); cson_object_set(pay, "type", json_new_string(zType2)); cson_object_set(pay, "limit", json_new_int(limit)); #if 1 if( tagid<=0 ){ cson_object_set(pay,"artifacts", cson_value_null()); json_warn(FSL_JSON_W_TAG_NOT_FOUND, "Tag not found."); return payV; } #endif if( fRaw ){ db_prepare(&q, "SELECT blob.uuid FROM tagxref, blob" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" " AND tagxref.tagtype>0" " AND blob.rid=tagxref.rid" "%s LIMIT %d", zName, (limit>0)?"":"--", limit ); while( db_step(&q)==SQLITE_ROW ){ if(!listV){ listV = cson_value_new_array(); list = cson_value_get_array(listV); } cson_array_append(list, cson_sqlite3_column_to_value(q.pStmt,0)); } db_finalize(&q); }else{ char const * zSqlBase = /*modified from timeline_query_for_tty()*/ " SELECT" #if 0 " blob.rid AS rid," #endif " uuid AS uuid," " cast(strftime('%s',event.mtime) as int) AS timestamp," " coalesce(ecomment,comment) AS comment," " coalesce(euser,user) AS user," " CASE event.type" " WHEN 'ci' THEN 'checkin'" " WHEN 'w' THEN 'wiki'" " WHEN 'e' THEN 'event'" " WHEN 't' THEN 'ticket'" " ELSE 'unknown'" " END" " AS eventType" " FROM event, blob" " WHERE blob.rid=event.objid" ; /* FIXME: re-add tags. */ db_prepare(&q, "%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" "%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) { listV = cson_value_null(); } cson_object_set(pay, "artifacts", listV); return payV; } /* ** Impl for /json/tag/list ** ** TODOs: ** ** Add -type TYPE (ci, w, e, t) */ static cson_value * json_tag_list(void){ cson_value * payV = NULL; cson_object * pay = NULL; cson_value const * tagsVal = NULL; char const * zCheckin = NULL; char fRaw = 0; char fTicket = 0; Stmt q = empty_Stmt; if( !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); return NULL; } fRaw = json_find_option_bool("raw",NULL,NULL,0); fTicket = json_find_option_bool("includeTickets","tkt","t",0); zCheckin = json_find_option_cstr("checkin",NULL,NULL); if( !zCheckin ){ zCheckin = json_command_arg( g.json.dispatchDepth + 1); if( !zCheckin && cson_value_is_string(g.json.reqPayload.v) ){ zCheckin = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v)); assert(zCheckin); } } 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" " AND tagtype>%d" " ORDER BY tagname", rid, fRaw ? -1 : 0 ); while( SQLITE_ROW == db_step(&q) ){ const char *zName = db_column_text(&q, 0); const char *zValue = db_column_text(&q, 1); if( fRaw==0 ){ if( 0!=strncmp(zName, "sym-", 4) ) continue; zName += 4; assert( *zName ); } if(NULL==objV){ objV = cson_value_new_object(); obj = cson_value_get_object(objV); tagsVal = objV; cson_object_set( pay, "tags", objV ); } if( zValue && zValue[0] ){ cson_object_set( obj, zName, json_new_string(zValue) ); }else{ cson_object_set( obj, zName, cson_value_null() ); } } db_finalize(&q); }else{/* all tags */ /* Output format: RAW mode: ["tagname", "sym-tagname2",...] Non-raw: ["tagname", "tagname2",...] i don't really like the discrepancy in the format but this list can get really long and (A) most tags don't have values, (B) i don't want to bloat it more, and (C) cson_object_set() is O(N) (N=current number of properties) because it uses an unsorted list internally (for memory reasons), so this can slow down appreciably on a long list. The culprit is really tkt- tags, as there is one for each ticket (941 in the main fossil repo as of this writing). */ Blob sql = empty_blob; cson_value * arV = NULL; cson_array * ar = NULL; blob_append(&sql, "SELECT tagname FROM tag" " WHERE EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=tag.tagid" " AND tagtype>0)", -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); cson_object_set(pay, "tags", arV); tagsVal = arV; } else if( !fRaw && (0==strncmp(zName, "sym-", 4))){ zName += 4; assert( *zName ); } cson_array_append(ar, json_new_string(zName)); } db_finalize(&q); } goto end; error: assert(0 != g.json.resultCode); cson_value_free(payV); payV = NULL; end: if( payV && !tagsVal ){ cson_object_set( pay, "tags", cson_value_null() ); } return payV; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_timeline.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "VERSION.h" #include "config.h" #include "json_timeline.h" #if INTERFACE #include "json_detail.h" #endif static cson_value * json_timeline_branch(void); static cson_value * json_timeline_ci(void); static cson_value * json_timeline_ticket(void); /* ** Mapping of /json/timeline/XXX commands/paths to callbacks. */ static const JsonPageDef JsonPageDefs_Timeline[] = { /* the short forms are only enabled in CLI mode, to avoid that we end up with HTTP clients using 3 different names for the same requests. */ {"branch", json_timeline_branch, 0}, {"checkin", json_timeline_ci, 0}, {"event" /* old name for technotes */, json_timeline_event, 0}, {"technote", json_timeline_event, 0}, {"ticket", json_timeline_ticket, 0}, {"wiki", json_timeline_wiki, 0}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; /* ** Implements the /json/timeline family of pages/commands. Far from ** complete. ** */ cson_value * json_page_timeline(void){ #if 0 /* The original timeline code does not require 'h' access, but it arguably should. For JSON mode i think one could argue that History permissions are required. */ if(! g.perm.Hyperlink && !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Timeline requires 'h' or 'o' access."); return NULL; } #endif return json_page_dispatch_helper(&JsonPageDefs_Timeline[0]); } /* ** 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 ** "tag" or "branch" request properties (CLI: --tag/--branch) ** are set. Limits the query to a particular branch/tag. ** ** tag works like HTML mode's "t" option and branch works like HTML ** mode's "r" option. They are very similar, but subtly different - ** tag mode shows only entries with a given tag but branch mode can ** also reveal some with "related" tags (meaning they were merged into ** the requested branch, or back). ** ** pSql is the target blob to append the query [subset] ** to. ** ** Returns a positive value if it modifies pSql, 0 if it ** does not. It returns a negative value if the tag ** provided to the request was not found (pSql is not modified ** in that case). ** ** If payload is not NULL then on success its "tag" or "branch" ** property is set to the tag/branch name found in the request. ** ** Only one of "tag" or "branch" modes will work at a time, and if ** both are specified, which one takes precedence is unspecified. */ static signed 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 ** AND clause and an ORDER BY clause to pSql, depending on the state ** of the "after" ("a") or "before" ("b") environment parameters. ** This function gives "after" precedence over "before", and only ** applies one of them. ** ** Returns -1 if it adds a "before" clause, 1 if it adds ** an "after" clause, and 0 if adds only an order-by clause. */ static signed char json_timeline_add_time_clause(Blob *pSql){ char const * zAfter = NULL; char const * zBefore = NULL; int rc = 0; zAfter = json_find_option_cstr("after",NULL,"a"); zBefore = zAfter ? NULL : json_find_option_cstr("before",NULL,"b"); if(zAfter&&*zAfter){ while( fossil_isspace(*zAfter) ) ++zAfter; blob_appendf(pSql, " AND event.mtime>=(SELECT julianday(%Q,fromLocal())) " " ORDER BY event.mtime ASC ", zAfter); rc = 1; }else if(zBefore && *zBefore){ while( fossil_isspace(*zBefore) ) ++zBefore; blob_appendf(pSql, " AND event.mtime<=(SELECT julianday(%Q,fromLocal())) " " ORDER BY event.mtime DESC ", zBefore); rc = -1; }else{ blob_append(pSql, " ORDER BY event.mtime DESC ", -1); rc = 0; } return rc; } /* ** Tries to figure out a timeline query length limit base on ** environment parameters. If it can it returns that value, ** else it returns some statically defined default value. ** ** Never returns a negative value. 0 means no limit. */ static int json_timeline_limit(int defaultLimit){ int limit = -1; if(!g.isHTTP){/* CLI mode */ char const * arg = find_option("limit","n",1); if(arg && *arg){ limit = atoi(arg); } } if( (limit<0) && fossil_has_json() ){ limit = json_getenv_int("limit",-1); } return (limit<0) ? defaultLimit : limit; } /* ** Internal helper for the json_timeline_EVENTTYPE() family of ** functions. zEventType must be one of (ci, w, t). pSql must be a ** cleanly-initialized, empty Blob to store the sql in. If pPayload is ** not NULL it is assumed to be the pending response payload. If ** json_timeline_limit() returns non-0, this function adds a LIMIT ** clause to the generated SQL. ** ** If pPayload is not NULL then this might add properties to pPayload, ** reflecting options set in the request environment. ** ** Returns 0 on success. On error processing should not continue and ** the returned value should be used as g.json.resultCode. */ static int json_timeline_setup_sql( char const * zEventType, Blob * pSql, cson_object * pPayload ){ int limit; assert( zEventType && *zEventType && pSql ); json_timeline_temp_table(); blob_append(pSql, "INSERT OR IGNORE INTO json_timeline ", -1); blob_append(pSql, json_timeline_query(), -1 ); blob_appendf(pSql, " AND event.type IN(%Q) ", zEventType); if( json_timeline_add_tag_branch_clause(pSql, pPayload) < 0 ){ return FSL_JSON_E_INVALID_ARGS; } json_timeline_add_time_clause(pSql); limit = json_timeline_limit(20); if(limit>0){ blob_appendf(pSql,"LIMIT %d ",limit); } if(pPayload){ cson_object_set(pPayload, "limit", json_new_int(limit)); } return 0; } /* ** If any files are associated with the given rid, a JSON array ** containing information about them is returned (and is owned by the ** caller). If no files are associated with it then NULL is returned. ** ** 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," " (SELECT uuid FROM blob WHERE rid=fid) as uuid," " (SELECT uuid FROM blob WHERE rid=pid) as parent," " blob.size as size" " FROM mlink" " LEFT JOIN blob ON blob.rid=fid" " WHERE mid=%d AND pid!=fid" " AND NOT mlink.isaux" " ORDER BY name /*sort*/", rid ); while( (SQLITE_ROW == db_step(&q)) ){ cson_value * rowV = cson_value_new_object(); cson_object * row = cson_value_get_object(rowV); int const isNew = db_column_int(&q,0); int const isDel = db_column_int(&q,1); char * zDownload = NULL; if(!rowsV){ rowsV = cson_value_new_array(); rows = cson_value_get_array(rowsV); } cson_array_append( rows, rowV ); cson_object_set(row, "name", json_new_string(db_column_text(&q,2))); cson_object_set(row, "uuid", json_new_string(db_column_text(&q,3))); if(!isNew && (flags & json_get_changed_files_ELIDE_PARENT)){ cson_object_set(row, "parent", json_new_string(db_column_text(&q,4))); } cson_object_set(row, "size", json_new_int(db_column_int(&q,5))); cson_object_set(row, "state", json_new_string(json_artifact_status_to_string(isNew,isDel))); zDownload = mprintf("/raw/%s?name=%s", /* reminder: g.zBaseURL is of course not set for CLI mode. */ db_column_text(&q,2), db_column_text(&q,3)); cson_object_set(row, "downloadPath", json_new_string(zDownload)); free(zDownload); } db_finalize(&q); return rowsV; } static cson_value * json_timeline_branch(void){ cson_value * pay = NULL; Blob sql = empty_blob; Stmt q = empty_Stmt; int limit = 0; if(!g.perm.Read){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); return NULL; } json_timeline_temp_table(); blob_append(&sql, "SELECT" " blob.rid AS rid," " uuid AS uuid," " CAST(strftime('%s',event.mtime) AS INTEGER) as timestamp," " coalesce(ecomment, comment) as comment," " 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); cson_string * isLeaf = cson_new_string("isLeaf",6); cson_array * ar = cson_value_get_array(pay); cson_object * outer = NULL; unsigned int i = 0; unsigned int len = cson_array_length_get(ar); cson_value_add_reference( cson_string_value(tags) ); cson_value_add_reference( cson_string_value(isLeaf) ); for( ; i < len; ++i ){ cson_object * row = cson_value_get_object(cson_array_get(ar,i)); int rid = cson_value_get_integer(cson_object_get(row,"rid")); assert( rid > 0 ); cson_object_set_s(row, tags, json_tags_for_checkin_rid(rid,0)); cson_object_set_s(row, isLeaf, json_value_to_bool(cson_object_get(row,"isLeaf"))); cson_object_set(row, "rid", NULL) /* remove rid - we don't really want it to be public */; } cson_value_free( cson_string_value(tags) ); cson_value_free( cson_string_value(isLeaf) ); /* now we wrap the payload in an outer shell, for consistency with other /json/timeline/xyz APIs... */ outer = cson_new_object(); if(limit>0){ cson_object_set( outer, "limit", json_new_int(limit) ); } cson_object_set( outer, "timeline", pay ); pay = cson_object_value(outer); } return pay; } /* ** Implementation of /json/timeline/ci. ** ** Still a few TODOs (like figuring out how to structure ** inheritance info). */ static cson_value * json_timeline_ci(void){ cson_value * payV = NULL; cson_object * pay = NULL; cson_value * tmp = NULL; cson_value * listV = NULL; cson_array * list = NULL; int check = 0; char verboseFlag; 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(); pay = cson_value_get_object(payV); check = json_timeline_setup_sql( "ci", &sql, pay ); if(check){ json_set_err(check, "Query initialization failed."); goto error; } #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 #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); tmp = listV; SET("timeline"); while( (SQLITE_ROW == db_step(&q) )){ /* convert each row into a JSON object...*/ int const rid = db_column_int(&q,0); cson_value * rowV = json_artifact_for_ci(rid, verboseFlag); cson_object * row = cson_value_get_object(rowV); if(!row){ if( !warnRowToJsonFailed ){ warnRowToJsonFailed = 1; json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, "Could not convert at least one timeline result row to JSON." ); } continue; } cson_array_append(list, rowV); } #undef SET goto ok; error: assert( 0 != g.json.resultCode ); cson_value_free(payV); payV = NULL; ok: db_finalize(&q); return payV; } /* ** Implementation of /json/timeline/event. ** */ cson_value * json_timeline_event(void){ /* This code is 95% the same as json_timeline_ci(), by the way. */ cson_value * payV = NULL; cson_object * pay = NULL; cson_array * list = NULL; int check = 0; Stmt q = empty_Stmt; Blob sql = empty_blob; if( !g.perm.RdWiki ){ json_set_err( FSL_JSON_E_DENIED, "Event timeline requires 'j' access."); return NULL; } payV = cson_value_new_object(); pay = cson_value_get_object(payV); check = json_timeline_setup_sql( "e", &sql, pay ); 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" /* For events, the name is generally more useful than the uuid, but the uuid is unambiguous and can be used with commands like 'artifact'. */ " substr((SELECT tagname FROM tag AS tn " " WHERE tn.tagid=json_timeline.tagId " " AND tagname LIKE 'event-%%'),7) AS name," " uuid as uuid," " mtime AS timestamp," " comment AS comment, " " user AS user," " eventType AS eventType" " 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); payV = NULL; ok: db_finalize(&q); blob_reset(&sql); return payV; } /* ** Implementation of /json/timeline/wiki. ** */ cson_value * json_timeline_wiki(void){ /* This code is 95% the same as json_timeline_ci(), by the way. */ cson_value * payV = NULL; cson_object * pay = NULL; cson_array * list = NULL; int check = 0; Stmt q = empty_Stmt; Blob sql = empty_blob; if( !g.perm.RdWiki && !g.perm.Read ){ json_set_err( FSL_JSON_E_DENIED, "Wiki timeline requires 'o' or 'j' access."); return NULL; } payV = cson_value_new_object(); pay = cson_value_get_object(payV); check = json_timeline_setup_sql( "w", &sql, pay ); 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); payV = NULL; ok: db_finalize(&q); blob_reset(&sql); return payV; } /* ** Implementation of /json/timeline/ticket. ** */ static cson_value * json_timeline_ticket(void){ /* This code is 95% the same as json_timeline_ci(), by the way. */ cson_value * payV = NULL; cson_object * pay = NULL; cson_value * tmp = NULL; cson_value * listV = NULL; cson_array * list = NULL; int check = 0; Stmt q = empty_Stmt; Blob sql = empty_blob; if( !g.perm.RdTkt && !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Ticket timeline requires 'o' or 'r' access."); return NULL; } payV = cson_value_new_object(); 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 #if 0 /* only for testing! */ tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); SET("timelineSql"); #endif blob_reset(&sql); /* REMINDER/FIXME(?): we have both uuid (the change uuid?) and ticketUuid (the actual ticket). This is different from the wiki timeline, where we only have the wiki page uuid. */ db_prepare(&q, "SELECT rid AS rid," " uuid AS uuid," " mtime AS timestamp," #if 0 " 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; int const rid = db_column_int(&q,0); Manifest * pMan = NULL; cson_value * rowV; cson_object * row; /*printf("rid=%d\n",rid);*/ pMan = manifest_get(rid, CFTYPE_TICKET, 0); if(!pMan){ /* this might be an attachment? I'm seeing this with rid 15380, uuid [1292fef05f2472108]. /json/artifact/1292fef05f2472108 returns not-found, probably because we haven't added artifact/ticket yet(?). */ continue; } rowV = cson_sqlite3_row_to_object(q.pStmt); row = cson_value_get_object(rowV); if(!row){ manifest_destroy(pMan); json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, "Could not convert at least one timeline result row to JSON." ); continue; } /* FIXME: certainly there's a more efficient way for use to get the ticket UUIDs? */ cson_object_set(row,"ticketUuid",json_new_string(pMan->zTicketUuid)); manifest_destroy(pMan); rc = cson_array_append( list, rowV ); if( 0 != rc ){ cson_value_free(rowV); g.json.resultCode = (cson_rc.AllocError==rc) ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; goto error; } } #undef SET goto ok; error: assert( 0 != g.json.resultCode ); cson_value_free(payV); payV = NULL; ok: blob_reset(&sql); db_finalize(&q); return payV; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_user.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 | #ifdef FOSSIL_ENABLE_JSON /* ** 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/ ** */ #include "VERSION.h" #include "config.h" #include "json_user.h" #if INTERFACE #include "json_detail.h" #endif static cson_value * json_user_get(void); static cson_value * json_user_list(void); static cson_value * json_user_save(void); /* ** Mapping of /json/user/XXX commands/paths to callbacks. */ static const JsonPageDef JsonPageDefs_User[] = { {"save", json_user_save, 0}, {"get", json_user_get, 0}, {"list", json_user_list, 0}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; /* ** Implements the /json/user family of pages/commands. ** */ cson_value * json_page_user(void){ return json_page_dispatch_helper(&JsonPageDefs_User[0]); } /* ** Impl of /json/user/list. Requires admin/setup rights. */ static cson_value * json_user_list(void){ cson_value * payV = NULL; Stmt q; if(!g.perm.Admin && !g.perm.Setup){ json_set_err(FSL_JSON_E_DENIED, "Requires 'a' or 's' privileges."); return NULL; } db_prepare(&q,"SELECT uid AS uid," " login AS name," " cap AS capabilities," " info AS info," " mtime AS timestamp" " FROM user ORDER BY login"); payV = json_stmt_to_array_of_obj(&q, NULL); db_finalize(&q); if(NULL == payV){ json_set_err(FSL_JSON_E_UNKNOWN, "Could not convert user list to JSON."); } return payV; } /* ** Creates a new JSON Object based on the db state of ** the given user name. On error (no record found) ** it returns NULL, else the caller owns the returned ** object. */ static cson_value * json_load_user_by_name(char const * zName){ cson_value * u = NULL; Stmt q; db_prepare(&q,"SELECT uid AS uid," " login AS name," " cap AS capabilities," " info AS info," " mtime AS timestamp" " FROM user" " WHERE login=%Q", zName); if( (SQLITE_ROW == db_step(&q)) ){ u = cson_sqlite3_row_to_object(q.pStmt); } db_finalize(&q); return u; } /* ** Identical to json_load_user_by_name(), but expects a user ID. Returns ** NULL if no user found with that ID. */ static cson_value * json_load_user_by_id(int uid){ cson_value * u = NULL; Stmt q; db_prepare(&q,"SELECT uid AS uid," " login AS name," " cap AS capabilities," " info AS info," " mtime AS timestamp" " FROM user" " WHERE uid=%d", uid); if( (SQLITE_ROW == db_step(&q)) ){ u = cson_sqlite3_row_to_object(q.pStmt); } db_finalize(&q); return u; } /* ** Impl of /json/user/get. Requires admin or setup rights. */ static cson_value * json_user_get(void){ cson_value * payV = NULL; char const * pUser = NULL; if(!g.perm.Admin && !g.perm.Setup){ json_set_err(FSL_JSON_E_DENIED, "Requires 'a' or 's' privileges."); return NULL; } pUser = json_find_option_cstr2("name", NULL, NULL, g.json.dispatchDepth+1); if(!pUser || !*pUser){ json_set_err(FSL_JSON_E_MISSING_ARGS,"Missing 'name' property."); return NULL; } payV = json_load_user_by_name(pUser); if(!payV){ json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,"User not found."); } return payV; } /* ** Expects pUser to contain fossil user fields in JSON form: name, ** uid, info, capabilities, password. ** ** At least one of (name, uid) must be included. All others are ** optional and their db fields will not be updated if those fields ** are not included in pUser. ** ** If uid is specified then name may refer to a _new_ name ** for a user, otherwise the name must refer to an existing user. ** If uid=-1 then the name must be specified and a new user is ** created (fails if one already exists). ** ** If uid is not set, this function might modify pUser to contain the ** db-found (or inserted) user ID. ** ** On error g.json's error state is set and one of the FSL_JSON_E_xxx ** values from FossilJsonCodes is returned. ** ** On success the db record for the given user is updated. ** ** Requires either Admin, Setup, or Password access. Non-admin/setup ** users can only change their own information. Non-setup users may ** not modify the 's' permission. Admin users without setup ** permissions may not edit any other user who has the 's' permission. ** */ int json_user_update_from_json( cson_object * pUser ){ #define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, \ X ) )) char const * zName = CSTR("name"); char const * zNameNew = zName; char * zNameFree = NULL; char const * zInfo = CSTR("info"); char const * zCap = CSTR("capabilities"); char const * zPW = CSTR("password"); cson_value const * forceLogout = cson_object_get(pUser, "forceLogout"); int gotFields = 0; #undef CSTR cson_int_t uid = cson_value_get_integer( cson_object_get(pUser, "uid") ); char const tgtHasSetup = zCap && (NULL!=strchr(zCap, 's')); char tgtHadSetup = 0; Blob sql = empty_blob; Stmt q = empty_Stmt; if(uid<=0 && (!zName||!*zName)){ return json_set_err(FSL_JSON_E_MISSING_ARGS, "One of 'uid' or 'name' is required."); }else if(uid>0){ zNameFree = db_text(NULL, "SELECT login FROM user WHERE uid=%d",uid); if(!zNameFree){ return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "No login found for uid %d.", uid); } zName = zNameFree; }else if(-1==uid){ /* try to create a new user */ if(!g.perm.Admin && !g.perm.Setup){ json_set_err(FSL_JSON_E_DENIED, "Requires 'a' or 's' privileges."); goto error; }else if(!zName || !*zName){ json_set_err(FSL_JSON_E_MISSING_ARGS, "No name specified for new user."); goto error; }else if( db_exists("SELECT 1 FROM user WHERE login=%Q", zName) ){ json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, "User %s already exists.", zName); goto error; }else{ Stmt ins = empty_Stmt; db_unprotect(PROTECT_USER); db_prepare(&ins, "INSERT INTO user (login) VALUES(%Q)",zName); db_step( &ins ); db_finalize(&ins); db_protect_pop(); uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); assert(uid>0); zNameNew = zName; cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); } }else{ uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); if(uid<=0){ json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "No login found for user [%s].", zName); goto error; } cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); } /* Maintenance note: all error-returns from here on out should go via 'goto error' in order to clean up. */ if(uid != g.userUid){ if(!g.perm.Admin && !g.perm.Setup){ json_set_err(FSL_JSON_E_DENIED, "Changing another user's data requires " "'a' or 's' privileges."); goto error; } } /* check if the target uid currently has setup rights. */ tgtHadSetup = db_int(0,"SELECT 1 FROM user where uid=%d" " AND cap GLOB '*s*'", uid); if((tgtHasSetup || tgtHadSetup) && !g.perm.Setup){ /* Do not allow a non-setup user to set or remove setup privileges. setup.c uses similar logic. */ json_set_err(FSL_JSON_E_DENIED, "Modifying 's' users/privileges requires " "'s' privileges."); goto error; } /* Potential todo: do not allow a setup user to remove 's' from himself, to avoid locking himself out? */ blob_append(&sql, "UPDATE user SET",-1 ); blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); if((uid>0) && zNameNew){ /* Check for name change... */ if(0!=strcmp(zName,zNameNew)){ if( (!g.perm.Admin && !g.perm.Setup) && (zName != zNameNew)){ json_set_err( FSL_JSON_E_DENIED, "Modifying user names requires 'a' or 's' privileges."); goto error; } 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_unprotect(PROTECT_USER); db_prepare(&q, "%s", blob_sql_text(&sql)); db_exec(&q); db_finalize(&q); db_protect_pop(); #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)); db_unprotect(PROTECT_USER); login_group_sql(blob_str(&groupSql), NULL, NULL, &zErr); db_protect_pop(); blob_reset(&groupSql); if( zErr ){ json_set_err( FSL_JSON_E_UNKNOWN, "Repo-group update at least partially failed: %s", zErr); free(zErr); goto error; } } #endif /* TRY_LOGIN_GROUP */ #undef TRY_LOGIN_GROUP free( zNameFree ); blob_reset(&sql); return 0; error: assert(0 != g.json.resultCode); free(zNameFree); blob_reset(&sql); return g.json.resultCode; } /* ** Impl of /json/user/save. */ static cson_value * json_user_save(void){ /* try to get user info from GET/CLI args and construct a JSON form of it... */ cson_object * u = cson_new_object(); char const * str = NULL; int b = -1; int i = -1; int uid = -1; cson_value * payload = NULL; /* String properties... */ #define PROP(LK,SK) str = json_find_option_cstr(LK,NULL,SK); \ if(str){ cson_object_set(u, LK, json_new_string(str)); } (void)0 PROP("name","n"); PROP("password","p"); PROP("info","i"); PROP("capabilities","c"); #undef PROP /* Boolean properties... */ #define PROP(LK,DFLT) b = json_find_option_bool(LK,NULL,NULL,DFLT); \ if(DFLT!=b){ cson_object_set(u, LK, cson_value_new_bool(b ? 1 : 0)); } (void)0 PROP("forceLogout",-1); #undef PROP #define PROP(LK,DFLT) i = json_find_option_int(LK,NULL,NULL,DFLT); \ if(DFLT != i){ cson_object_set(u, LK, cson_value_new_integer(i)); } (void)0 PROP("uid",-99); #undef PROP if( g.json.reqPayload.o ){ cson_object_merge( u, g.json.reqPayload.o, CSON_MERGE_NO_RECURSE ); } json_user_update_from_json( u ); if(!g.json.resultCode){ uid = cson_value_get_integer( cson_object_get(u, "uid") ); assert((uid>0) && "Something went wrong in json_user_update_from_json()"); payload = json_load_user_by_id(uid); } cson_free_object(u); return payload; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/json_wiki.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 | #ifdef FOSSIL_ENABLE_JSON /* ** Copyright (c) 2011-12 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/ ** */ #include "VERSION.h" #include "config.h" #include "json_wiki.h" #if INTERFACE #include "json_detail.h" #endif static cson_value * json_wiki_create(void); static cson_value * json_wiki_get(void); static cson_value * json_wiki_list(void); static cson_value * json_wiki_preview(void); static cson_value * json_wiki_save(void); static cson_value * json_wiki_diff(void); /* ** Mapping of /json/wiki/XXX commands/paths to callbacks. */ static const JsonPageDef JsonPageDefs_Wiki[] = { {"create", json_wiki_create, 0}, {"diff", json_wiki_diff, 0}, {"get", json_wiki_get, 0}, {"list", json_wiki_list, 0}, {"preview", json_wiki_preview, 0}, {"save", json_wiki_save, 0}, {"timeline", json_timeline_wiki,0}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; /* ** Implements the /json/wiki family of pages/commands. ** */ cson_value * json_page_wiki(void){ return json_page_dispatch_helper(JsonPageDefs_Wiki); } /* ** Returns the UUID for the given wiki blob RID, or NULL if not ** found. The returned string is allocated via db_text() and must be ** free()d by the caller. */ char * json_wiki_get_uuid_for_rid( int rid ) { return db_text(NULL, "SELECT b.uuid FROM tag t, tagxref x, blob b" " WHERE x.tagid=t.tagid AND t.tagname GLOB 'wiki-*' " " AND b.rid=x.rid AND b.rid=%d" " ORDER BY x.mtime DESC LIMIT 1", rid ); } /* ** Tries to load a wiki page from the given rid creates a JSON object ** representation of it. If the page is not found then NULL is ** returned. If contentFormat is positive then the page content ** is HTML-ized using fossil's conventional wiki format, if it is ** negative then no parsing is performed, if it is 0 then the content ** 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{ unsigned int len = 0; cson_object * pay = cson_new_object(); char const * zBody = pWiki->zWiki; char const * zFormat = NULL; char * zUuid = json_wiki_get_uuid_for_rid(rid); cson_object_set(pay,"name",json_new_string(pWiki->zWikiTitle)); cson_object_set(pay,"uuid",json_new_string(zUuid)); free(zUuid); zUuid = NULL; if( pWiki->nParent > 0 ){ cson_object_set( pay, "parent", json_new_string(pWiki->azParent[0]) ) /* Reminder: wiki pages do not branch and have only one parent (except for the initial version, which has no parents). */; } /*cson_object_set(pay,"rid",json_new_int((cson_int_t)rid));*/ cson_object_set(pay,"user",json_new_string(pWiki->zUser)); cson_object_set(pay,FossilJsonKeys.timestamp, json_julian_to_timestamp(pWiki->rDate)); if(0 == contentFormat){ cson_object_set(pay,"size", json_new_int((cson_int_t)(zBody?strlen(zBody):0))); }else{ if( contentFormat>0 ){/*HTML-ize it*/ Blob content = empty_blob; Blob raw = empty_blob; zFormat = "html"; if(zBody && *zBody){ const char *zMimetype = pWiki->zMimetype; if( zMimetype==0 ) zMimetype = "text/x-fossil-wiki"; zMimetype = wiki_filter_mimetypes(zMimetype); blob_append(&raw,zBody,-1); if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ wiki_convert(&raw,&content,0); }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ markdown_to_html(&raw,0,&content); }else if( fossil_strcmp(zMimetype, "text/plain")==0 ){ htmlize_to_blob(&content,blob_str(&raw),blob_size(&raw)); }else{ json_set_err( FSL_JSON_E_UNKNOWN, "Unsupported MIME type '%s' for wiki page '%s'.", zMimetype, pWiki->zWikiTitle ); blob_reset(&content); blob_reset(&raw); cson_free_object(pay); manifest_destroy(pWiki); return NULL; } len = (unsigned int)blob_size(&content); } cson_object_set(pay,"size",json_new_int((cson_int_t)len)); cson_object_set(pay,"content", cson_value_new_string(blob_buffer(&content),len)); blob_reset(&content); blob_reset(&raw); }else{/*raw format*/ zFormat = "raw"; len = zBody ? strlen(zBody) : 0; cson_object_set(pay,"size",json_new_int((cson_int_t)len)); cson_object_set(pay,"content",cson_value_new_string(zBody,len)); } cson_object_set(pay,"contentFormat",json_new_string(zFormat)); } /*TODO: add 'T' (tag) fields*/ /*TODO: add the 'A' card (file attachment) entries?*/ manifest_destroy(pWiki); return cson_object_value(pay); } } /* ** 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 ); if( 0==rid ){ json_set_err( FSL_JSON_E_RESOURCE_NOT_FOUND, "Wiki page not found: %s", zPageName ); return NULL; } return json_get_wiki_page_by_rid(rid, contentFormat); } /* ** Searches json_find_option_ctr("format",NULL,"f") for a flag. ** If not found it returns defaultValue else it returns a value ** depending on the first character of the format option: ** ** [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; } else if('h'==*zFormat){ contentFormat = 1; } else if('n'==*zFormat){ contentFormat = 0; } return contentFormat; } /* ** Helper for /json/wiki/get and /json/wiki/preview. At least one of ** zPageName (wiki page name) or zSymname must be set to a ** 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); return NULL; }else if(rid==0){ json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "UUID [%s] does not resolve to a wiki page.", zSymname); return NULL; }else{ return json_get_wiki_page_by_rid(rid, contentFormat); } } } /* ** Implementation of /json/wiki/get. ** */ static cson_value * json_wiki_get(void){ 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); zSymName = json_find_option_cstr("uuid",NULL,"u"); if((!zPageName||!*zPageName) && (!zSymName || !*zSymName)){ json_set_err(FSL_JSON_E_MISSING_ARGS, "At least one of the 'name' or 'uuid' arguments must be provided."); return NULL; } /* TODO: see if we have a page named zPageName. If not, try to resolve zPageName as a UUID. */ contentFormat = json_wiki_get_content_format_flag(contentFormat); return json_wiki_get_by_name_or_symname( zPageName, zSymName, contentFormat ); } /* ** Implementation of /json/wiki/preview. */ static cson_value * json_wiki_preview(void){ char const * zContent = NULL; char const * zMime = NULL; cson_string * sContent = NULL; cson_value * pay = NULL; Blob contentOrig = empty_blob; Blob contentHtml = empty_blob; if( !g.perm.WrWiki ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'k' access."); return NULL; } if(g.json.reqPayload.o){ sContent = cson_value_get_string( cson_object_get(g.json.reqPayload.o, "body")); zMime = cson_value_get_cstr(cson_object_get(g.json.reqPayload.o, "mimetype")); }else{ sContent = cson_value_get_string(g.json.reqPayload.v); } if(!sContent) { json_set_err(FSL_JSON_E_MISSING_ARGS, "The 'payload' property must be either a string containing the " "Fossil wiki code to preview or an object with body + mimetype " "properties."); return NULL; } zContent = cson_string_cstr(sContent); blob_append( &contentOrig, zContent, (int)cson_string_length_bytes(sContent)); zMime = wiki_filter_mimetypes(zMime); if( 0==fossil_strcmp(zMime, "text/x-markdown") ){ markdown_to_html(&contentOrig, 0, &contentHtml); }else if( 0==fossil_strcmp(zMime, "text/plain") ){ blob_append(&contentHtml, "<pre class='textPlain'>", -1); blob_append(&contentHtml, blob_str(&contentOrig), blob_size(&contentOrig)); blob_append(&contentHtml, "</pre>", -1); }else{ wiki_convert( &contentOrig, &contentHtml, 0 ); } blob_reset( &contentOrig ); pay = cson_value_new_string( blob_str(&contentHtml), (unsigned int)blob_size(&contentHtml)); blob_reset( &contentHtml ); return pay; } /* ** Internal impl of /wiki/save and /wiki/create. If createMode is 0 ** and the page already exists then a ** FSL_JSON_E_RESOURCE_ALREADY_EXISTS error is triggered. If ** createMode is false then the FSL_JSON_E_RESOURCE_NOT_FOUND is ** triggered if the page does not already exists. ** ** Note that the error triggered when createMode==0 and no such page ** exists is rather arbitrary - we could just as well create the entry ** here if it doesn't already exist. With that, save/create would ** become one operation. That said, i expect there are people who ** would categorize such behaviour as "being too clever" or "doing too ** much automatically" (and i would likely agree with them). ** ** If allowCreateIfNotExists is true then this function will allow a new ** page to be created even if createMode is false. */ static cson_value * json_wiki_create_or_save(char createMode, char allowCreateIfNotExists){ 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')); return NULL; } nameV = json_req_payload_get("name"); if(!nameV){ json_set_err( FSL_JSON_E_MISSING_ARGS, "'name' parameter is missing."); return NULL; } zPageName = cson_string_cstr(cson_value_get_string(nameV)); if(!zPageName || !*zPageName){ json_set_err(FSL_JSON_E_INVALID_ARGS, "'name' parameter must be a non-empty string."); return NULL; } 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){ if(createMode){ json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, "Wiki page '%s' already exists.", zPageName); goto error; } }else if(!createMode && !allowCreateIfNotExists){ json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "Wiki page '%s' not found.", zPageName); goto error; } contentV = json_req_payload_get("content"); if( !contentV ){ if( createMode || (!rid && allowCreateIfNotExists) ){ contentV = emptyContent = cson_value_new_string("",0); }else{ json_set_err(FSL_JSON_E_MISSING_ARGS, "'content' parameter is missing."); goto error; } } if( !cson_value_is_string(nameV) || !cson_value_is_string(contentV)){ json_set_err(FSL_JSON_E_INVALID_ARGS, "'content' parameter must be a string."); 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"); zMimeType = wiki_filter_mimetypes(zMimeType); wiki_cmd_commit(zPageName, 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( cson_string_cstr( cson_value_get_string(nameV)), 0); goto ok; error: assert( 0 != g.json.resultCode ); assert( NULL == payV ); ok: if( emptyContent ){ /* We have some potentially tricky memory ownership here, which is why we handle emptyContent separately. This is, in fact, overkill because cson_value_new_string("",0) actually returns a shared singleton instance (i.e. doesn't allocate), but that is a cson implementation detail which i don't want leaking into this code... */ cson_value_free(emptyContent); } return payV; } /* ** Implementation of /json/wiki/create. */ static cson_value * json_wiki_create(void){ return json_wiki_create_or_save(1,0); } /* ** Implementation of /json/wiki/save. */ static cson_value * json_wiki_save(void){ char const createIfNotExists = json_getenv_bool("createIfNotExists",0); return json_wiki_create_or_save(0,createIfNotExists); } /* ** Implementation of /json/wiki/list. */ static cson_value * json_wiki_list(void){ cson_value * listV = NULL; cson_array * list = NULL; char const * zGlob = NULL; Stmt q = empty_Stmt; Blob sql = empty_blob; char const verbose = json_find_option_bool("verbose",NULL,"v",0); char fInvert = json_find_option_bool("invert",NULL,"i",0);; if( !g.perm.RdWiki && !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'j' or 'o' permissions."); return NULL; } blob_append(&sql,"SELECT" " DISTINCT substr(tagname,6) as name" " FROM tag JOIN tagxref USING('tagid')" " WHERE tagname GLOB 'wiki-*'" " AND TYPEOF(tagxref.value+0)='integer'", /* ^^^ elide wiki- tags which are not wiki pages */ -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); v = json_get_wiki_page_by_name(name,0); }else{ v = cson_sqlite3_column_to_value(q.pStmt,0); } if(!v){ json_set_err(FSL_JSON_E_UNKNOWN, "Could not convert wiki name column to JSON."); goto error; }else if( 0 != cson_array_append( list, v ) ){ cson_value_free(v); json_set_err(FSL_JSON_E_ALLOC,"Could not append wiki page name to array.") /* OOM (or maybe numeric overflow) are the only realistic error codes for that particular failure.*/; goto error; } } goto end; error: assert(0 != g.json.resultCode); cson_value_free(listV); listV = NULL; end: db_finalize(&q); return listV; } static cson_value * json_wiki_diff(void){ char const * zV1 = NULL; char const * zV2 = NULL; cson_object * pay = NULL; int argPos = g.json.dispatchDepth; int r1 = 0, r2 = 0; Manifest * pW1 = NULL, *pW2 = NULL; Blob w1 = empty_blob, w2 = empty_blob, d = empty_blob; char const * zErrTag = NULL; DiffConfig DCfg; char * zUuid = NULL; if( !g.perm.Hyperlink ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'h' permissions."); return NULL; } zV1 = json_find_option_cstr2( "v1",NULL, NULL, ++argPos ); zV2 = json_find_option_cstr2( "v2",NULL, NULL, ++argPos ); if(!zV1 || !*zV1 || !zV2 || !*zV2) { json_set_err(FSL_JSON_E_INVALID_ARGS, "Requires both 'v1' and 'v2' arguments."); return NULL; } r1 = symbolic_name_to_rid( zV1, "w" ); zErrTag = zV1; if(r1<0){ goto ambiguous; }else if(0==r1){ goto invalid; } r2 = symbolic_name_to_rid( zV2, "w" ); zErrTag = zV2; if(r2<0){ goto ambiguous; }else if(0==r2){ goto invalid; } zErrTag = zV1; pW1 = manifest_get(r1, CFTYPE_WIKI, 0); if( pW1==0 ) { goto manifest; } zErrTag = zV2; pW2 = manifest_get(r2, CFTYPE_WIKI, 0); if( pW2==0 ) { goto manifest; } blob_init(&w1, pW1->zWiki, -1); blob_zero(&w2); blob_init(&w2, pW2->zWiki, -1); blob_zero(&d); diff_config_init(&DCfg, DIFF_IGNORE_EOLWS | DIFF_STRIP_EOLCR); text_diff(&w1, &w2, &d, &DCfg); blob_reset(&w1); blob_reset(&w2); pay = cson_new_object(); zUuid = json_wiki_get_uuid_for_rid( pW1->rid ); cson_object_set(pay, "v1", json_new_string(zUuid) ); free(zUuid); zUuid = json_wiki_get_uuid_for_rid( pW2->rid ); cson_object_set(pay, "v2", json_new_string(zUuid) ); free(zUuid); zUuid = NULL; manifest_destroy(pW1); manifest_destroy(pW2); cson_object_set(pay, "diff", cson_value_new_string( blob_str(&d), (unsigned int)blob_size(&d))); return cson_object_value(pay); manifest: json_set_err(FSL_JSON_E_UNKNOWN, "Could not load wiki manifest for UUID [%s].", zErrTag); goto end; ambiguous: json_set_err(FSL_JSON_E_AMBIGUOUS_UUID, "UUID [%s] is ambiguous.", zErrTag); goto end; invalid: json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "UUID [%s] not found.", zErrTag); goto end; end: cson_free_object(pay); return NULL; } #endif /* FOSSIL_ENABLE_JSON */ |
Added src/leaf.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 | /* ** 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/ ** ******************************************************************************* ** ** 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; db_static_prepare(&checkIfLeaf, "SELECT 1 FROM plink" " WHERE pid=:rid" " AND coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND rid=:rid),'trunk')" " == coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND rid=plink.cid),'trunk');", TAG_BRANCH, TAG_BRANCH ); db_bind_int(&checkIfLeaf, ":rid", rid); rc = db_step(&checkIfLeaf); db_reset(&checkIfLeaf); if( rc==SQLITE_ROW ){ db_static_prepare(&removeLeaf, "DELETE FROM leaf WHERE rid=:rid"); db_bind_int(&removeLeaf, ":rid", rid); db_step(&removeLeaf); db_reset(&removeLeaf); }else{ db_static_prepare(&addLeaf, "INSERT OR IGNORE INTO leaf VALUES(:rid)"); db_bind_int(&addLeaf, ":rid", rid); db_step(&addLeaf); db_reset(&addLeaf); } } /* ** Return an SQL expression (stored in memory obtained from fossil_malloc()) ** that is true if the SQL variable named "zVar" contains the rid with ** a CLOSED tag. In other words, return true if the leaf is closed. ** ** The result can be prefaced with a NOT operator to get all leaves that ** are open. */ char *leaf_is_closed_sql(const char *zVar){ return mprintf( "EXISTS(SELECT 1 FROM tagxref AS tx" " WHERE tx.rid=%s" " AND tx.tagid=%d" " AND tx.tagtype>0)", zVar, TAG_CLOSED ); } /* ** Returns true if vid refers to a closed leaf, else false. vid is ** assumed to refer to a manifest, but this function does not verify ** that. */ int leaf_is_closed(int vid){ return db_exists("SELECT 1 FROM tagxref" " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, vid); } /* ** 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)); } db_reset(&parentsOf); } /* ** Do all pending leaf checks. */ void leaf_do_pending_checks(void){ int rid; for(rid=bag_first(&needToCheck); rid; rid=bag_next(&needToCheck,rid)){ leaf_check(rid); } bag_clear(&needToCheck); } /* ** If check-in rid is an open-leaf and there exists another ** open leaf on the same branch, then return 1. ** ** If check-in rid is not an open leaf, or if it is the only open leaf ** on its branch, then return 0. */ int leaf_ambiguity(int rid){ int rc; /* Result */ char zVal[30]; if( !is_a_leaf(rid) ) return 0; sqlite3_snprintf(sizeof(zVal), zVal, "%d", rid); rc = db_exists( "SELECT 1 FROM leaf" " WHERE NOT %z" " AND rid<>%d" " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=leaf.rid)=" " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)" " AND NOT %z", leaf_is_closed_sql(zVal), rid, TAG_BRANCH, TAG_BRANCH, rid, leaf_is_closed_sql("leaf.rid")); return rc; } /* ** If check-in rid is an open-leaf and there exists another open leaf ** on the same branch, then print a detailed warning showing all open ** leaves on that branch. */ int leaf_ambiguity_warning(int rid, int currentCkout){ char *zBr; Stmt q; int n = 0; Blob msg; if( leaf_ambiguity(rid)==0 ) return 0; zBr = db_text(0, "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", TAG_BRANCH, rid); if( zBr==0 ) zBr = fossil_strdup("trunk"); blob_init(&msg, 0, 0); blob_appendf(&msg, "WARNING: multiple open leaf check-ins on %s:", zBr); db_prepare(&q, "SELECT" " (SELECT uuid FROM blob WHERE rid=leaf.rid)," " (SELECT datetime(mtime,toLocal()) FROM event WHERE objid=leaf.rid)," " leaf.rid" " FROM leaf" " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=leaf.rid)=%Q" " AND NOT %z" " ORDER BY 2 DESC", TAG_BRANCH, zBr, leaf_is_closed_sql("leaf.rid") ); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&msg, "\n (%d) %s [%S]%s", ++n, db_column_text(&q,1), db_column_text(&q,0), db_column_int(&q,2)==currentCkout ? " (current)" : ""); } db_finalize(&q); fossil_warning("%s",blob_str(&msg)); blob_reset(&msg); return 1; } /* ** COMMAND: test-leaf-ambiguity ** ** Usage: %fossil NAME ... ** ** Resolve each name on the command line and call leaf_ambiguity_warning() ** for each resulting RID. */ void leaf_ambiguity_warning_test(void){ int i; int rid; int rc; db_find_and_open_repository(0,0); verify_all_options(); for(i=2; i<g.argc; i++){ char *zUuid; rid = name_to_typed_rid(g.argv[i], "ci"); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); fossil_print("%s (rid=%d) %S ", g.argv[i], rid, zUuid ? zUuid : "(none)"); fossil_free(zUuid); rc = leaf_ambiguity_warning(rid, rid); if( rc==0 ) fossil_print(" ok\n"); } } |
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 66 67 68 69 70 71 72 73 74 75 76 | /* ** 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 page request if the load average of the host ** computer is too high. Admin and Setup users are exempt from this ** restriction. */ void load_control(void){ double mxLoad = atof(db_get("max-loadavg", 0)); #if 1 /* Disable this block only to test load restrictions */ if( mxLoad<=0.0 || mxLoad>=load_average() ) return; login_check_credentials(); if(g.perm.Admin || g.perm.Setup){ return; } #endif style_set_current_feature("test"); 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_finish_page(); cgi_set_status(503,"Server Overload"); cgi_reply(); exit(0); } |
Changes to src/login.c.
︙ | ︙ | |||
15 16 17 18 19 20 21 | ** ******************************************************************************* ** ** This file contains code for generating the login and logout screens. ** ** Notes: ** | | > > | > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | < | < | < > > > > | > | < < < | > | < < < < < < < | | > > > | | > < | < < | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > | > | > | < | > | > > | | < < | | | < | > > | > > > > > > > > > > > > > > | > > | < < > > > > > | < > > > > > > > > > > > > | > > > > > > > > | > > > > > > | | | < | > > > > > > > > > > | > > > > > | > > > > > | < < < < < > | < | | | | | | | | | | > > > > > > > | < < < < | < > | | < | > | > > > > | | > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < < < < > | > > > > > | | | | > > > > > > > > | > | > > > > > > > > > > > > > > | | < > > < > | | | < | < | | < < < | > > > > > > > > > > > > | < | > > > | < > > > > | < < < < | < | | > > | > > > > > > > > > > > > > > > > > > | > > > > > > | > > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | < > | > | < | | | < > | > > | < > > > > > > | > > | > | | > > | > > | > | | > > | < > > | < < | < | > | | | < | | | > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > | > > > | > > | | > > > > > > > | > | < > > > > > | | > | | > > > > > > > > > > > | < | < < < < | | | | < < < < < | < | > | | | | | < | | > > > > > > > > > > | < > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > | 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 | ** ******************************************************************************* ** ** This file contains code for generating the login and logout screens. ** ** Notes: ** ** There are four special-case user-ids: "anonymous", "nobody", ** "developer" and "reader". ** ** The capabilities of the nobody user are available to anyone, ** regardless of whether or not they are logged in. The capabilities ** of anonymous are only available after logging in, but the login ** screen displays the password for the anonymous login, so this ** should not prevent a human user from doing so. The capabilities ** of developer and reader are inherited by any user that has the ** "v" and "u" capabilities, respectively. ** ** The nobody user has capabilities that you want spiders to have. ** The anonymous user has capabilities that you want people without ** logins to have. ** ** Of course, a sophisticated spider could easily circumvent the ** anonymous login requirement and walk the website. But that is ** 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> /* ** Compute an appropriate Anti-CSRF token into g.zCsrfToken[]. */ static void login_create_csrf_secret(const char *zSeed){ unsigned char zResult[20]; unsigned int i; sha1sum_binary(zSeed, zResult); for(i=0; i<sizeof(g.zCsrfToken)-1; i++){ g.zCsrfToken[i] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789-/"[zResult[i]%64]; } g.zCsrfToken[i] = 0; } /* ** Return the login-group name. Or return 0 if this repository is ** not a member of a login-group. */ const char *login_group_name(void){ static const char *zGroup = 0; static int once = 1; if( once ){ zGroup = db_get("login-group-name", 0); once = 0; } return zGroup; } /* ** Return a path appropriate for setting a cookie. ** ** The path is g.zTop for single-repo cookies. It is "/" for ** cookies of a login-group. */ const char *login_cookie_path(void){ if( login_group_name()==0 ){ return g.zTop; }else{ return "/"; } } /* ** Return the name of the login cookie. ** ** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX ** where the Xs are the first 16 characters of the login-group-code or ** of the project-code if we are not a member of any login-group. */ char *login_cookie_name(void){ static char *zCookieName = 0; if( zCookieName==0 ){ zCookieName = db_text(0, "SELECT 'fossil-' || substr(value,1,16)" " FROM config" " WHERE name IN ('project-code','login-group-code')" " ORDER BY name /*sort*/" ); } return zCookieName; } /* ** Redirect to the page specified by the "g" query parameter. ** Or if there is no "g" query parameter, redirect to the homepage. */ static void redirect_to_g(void){ const char *zGoto = P("g"); if( zGoto ){ cgi_redirectf("%R/%s",zGoto); }else{ fossil_redirect_home(); } } /* ** Return an abbreviated project code. The abbreviation is the first ** 16 characters of the project code. ** ** Memory is obtained from malloc. */ static char *abbreviated_project_code(const char *zFullCode){ return mprintf("%.16s", zFullCode); } /* ** Check to see if the anonymous login is valid. If it is valid, return ** the userid of the anonymous user. ** ** The zCS parameter is the "captcha seed" used for a specific ** anonymous login request. */ int login_is_valid_anonymous( const char *zUsername, /* The username. Must be "anonymous" */ const char *zPassword, /* The supplied password */ const char *zCS /* The captcha seed value */ ){ const char *zPw; /* The correct password shown in the captcha */ int uid; /* The user ID of anonymous */ if( zUsername==0 ) return 0; else if( zPassword==0 ) return 0; else if( zCS==0 ) return 0; else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0; zPw = captcha_decode((unsigned int)atoi(zCS)); if( fossil_stricmp(zPw, zPassword)!=0 ) return 0; uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" " AND octet_length(pw)>0 AND octet_length(cap)>0"); return uid; } /* ** Make sure the accesslog table exists. Create it if it does not */ void create_accesslog_table(void){ if( !db_table_exists("repository","accesslog") ){ db_unprotect(PROTECT_READONLY); db_multi_exec( "CREATE TABLE IF NOT EXISTS repository.accesslog(" " uname TEXT," " ipaddr TEXT," " success BOOLEAN," " mtime TIMESTAMP" ");" ); db_protect_pop(); } } /* ** Make a record of a login attempt, if login record keeping is enabled. */ static void record_login_attempt( const char *zUsername, /* Name of user logging in */ const char *zIpAddr, /* IP address from which they logged in */ int bSuccess /* True if the attempt was a success */ ){ db_unprotect(PROTECT_READONLY); if( db_get_boolean("access-log", 0) ){ create_accesslog_table(); db_multi_exec( "INSERT INTO accesslog(uname,ipaddr,success,mtime)" "VALUES(%Q,%Q,%d,julianday('now'));", zUsername, zIpAddr, bSuccess ); } if( bSuccess ){ alert_user_contact(zUsername); } db_protect_pop(); } /* ** 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. ** ** zUsername uses double indirection because we may re-point *zUsername ** at a C string allocated with fossil_strdup() if you pass an email ** address instead and we find that address in the user table's info ** field, which is expected to contain a string of the form "Human Name ** <human@example.com>". In that case, *zUsername will point to that ** user's actual login name on return, causing a leak unless the caller ** is diligent enough to check whether its pointer was re-pointed. ** ** zPassword may be either the plain-text form or the encrypted ** form of the user's password. */ int login_search_uid(const char **pzUsername, const char *zPasswd){ char *zSha1Pw = sha1_shared_secret(zPasswd, *pzUsername, 0); int uid = db_int(0, "SELECT uid FROM user" " WHERE login=%Q" " AND octet_length(cap)>0 AND octet_length(pw)>0" " AND login NOT IN ('anonymous','nobody','developer','reader')" " AND (pw=%Q OR (length(pw)<>40 AND pw=%Q))" " AND (info NOT LIKE '%%expires 20%%'" " OR substr(info,instr(lower(info),'expires')+8,10)>datetime('now'))", *pzUsername, zSha1Pw, zPasswd ); /* If we did not find a login on the first attempt, and the username ** looks like an email address, then perhaps the user entered their ** email address instead of their login. Try again to match the user ** against email addresses contained in the "info" field. */ if( uid==0 && strchr(*pzUsername,'@')!=0 ){ Stmt q; db_prepare(&q, "SELECT login FROM user" " WHERE find_emailaddr(info)=%Q" " AND instr(login,'@')==0", *pzUsername ); while( db_step(&q)==SQLITE_ROW ){ const char *zLogin = db_column_text(&q,0); if( (uid = login_search_uid(&zLogin, zPasswd) ) != 0 ){ *pzUsername = fossil_strdup(zLogin); break; } } db_finalize(&q); } free(zSha1Pw); return uid; } /* ** 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()). ** ** If bSessionCookie is true, the cookie will be a session cookie, ** else a persistent cookie. If it's a session cookie, the ** [user].[cexpire] and [user].[cookie] entries will be modified as if ** it were a persistent cookie because doing so is necessary for ** fossil's own "is this cookie still valid?" checks to work. */ void login_set_user_cookie( const char *zUsername, /* User's name */ int uid, /* User's ID */ char **zDest, /* Optional: store generated cookie value. */ int bSessionCookie /* True for session-only cookie */ ){ const char *zCookieName = login_cookie_name(); const char *zExpire = db_get("cookie-expire","8766"); const int expires = atoi(zExpire)*3600; char *zHash = 0; char *zCookie; const char *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */ assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data."); zHash = db_text(0, "SELECT cookie FROM user" " WHERE uid=%d" " AND cexpire>julianday('now')" " AND length(cookie)>30", uid); if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))"); zCookie = login_gen_user_cookie_value(zUsername, zHash); cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), bSessionCookie ? 0 : expires); record_login_attempt(zUsername, zIpAddr, 1); db_unprotect(PROTECT_USER); db_multi_exec("UPDATE user SET cookie=%Q," " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", zHash, expires, uid); db_protect_pop(); fossil_free(zHash); if( zDest ){ *zDest = zCookie; }else{ free(zCookie); } } /* Sets a cookie for an anonymous user login, which looks like this: ** ** HASH/TIME/anonymous ** ** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret. ** ** If zCookieDest is not NULL then the generated cookie is assigned to ** *zCookieDest and the caller must eventually free() it. ** ** If bSessionCookie is true, the cookie will be a session cookie. */ void login_set_anon_cookie(const char *zIpAddr, char **zCookieDest, int bSessionCookie ){ 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 */ int expires = bSessionCookie ? 0 : 6*3600; zCookieName = login_cookie_name(); zNow = db_text("0", "SELECT julianday('now')"); assert( zCookieName && zNow ); blob_init(&b, zNow, -1); blob_appendf(&b, "/%s", db_get("captcha-secret","")); sha1sum_blob(&b, &b); zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); blob_reset(&b); cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); if( zCookieDest ){ *zCookieDest = zCookie; }else{ free(zCookie); } } /* ** "Unsets" the login cookie (insofar as cookies can be unset) and ** clears the current user's (g.userUid) login information from the ** user table. Sets: user.cookie, user.ipaddr, user.cexpire. ** ** We could/should arguably clear out g.userUid and g.perm here, but ** we don't currently do not. ** ** 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_unprotect(PROTECT_USER); 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); db_protect_pop(); cgi_replace_parameter(cookie, NULL); cgi_replace_parameter("anon", NULL); } } /* ** Look at the HTTP_USER_AGENT parameter and try to determine if the user agent ** is a manually operated browser or a bot. When in doubt, assume a bot. ** Return true if we believe the agent is a real person. */ static int isHuman(const char *zAgent){ if( zAgent==0 ) return 0; /* If no UserAgent, then probably a bot */ if( strstr(zAgent, "bot")!=0 ) return 0; if( strstr(zAgent, "spider")!=0 ) return 0; if( strstr(zAgent, "crawl")!=0 ) return 0; /* If a URI appears in the User-Agent, it is probably a bot */ if( strstr(zAgent, "http")!=0 ) return 0; if( strncmp(zAgent, "Mozilla/", 8)==0 ){ if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */ /* Google AI Robot, maybe? */ if( strstr(zAgent, "GoogleOther)")!=0 ) return 0; /* 2016-05-30: A pernicious spider that likes to walk Fossil timelines has ** been detected on the SQLite website. The spider changes its user-agent ** string frequently, but it always seems to include the following text: */ if( strstr(zAgent, "Safari/537.36Mozilla/5.0")!=0 ) return 0; 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; if( sqlite3_strglob("*PaleMoon/[1-9]*", 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; } /* ** Make a guess at whether or not the requestor is a mobile device or ** a desktop device (narrow screen vs. wide screen) based the HTTP_USER_AGENT ** parameter. Return true for mobile and false for desktop. ** ** Caution: This is only a guess. ** ** Algorithm derived from https://developer.mozilla.org/en-US/docs/Web/ ** HTTP/Browser_detection_using_the_user_agent#mobile_device_detection on ** 2021-03-01 */ int user_agent_is_likely_mobile(void){ const char *zAgent = P("HTTP_USER_AGENT"); if( zAgent==0 ) return 0; if( strstr(zAgent,"Mobi")!=0 ) return 1; return 0; } /* ** COMMAND: test-ishuman ** ** Read lines of text from standard input. Interpret each line of text ** as a User-Agent string from an HTTP header. Label each line as HUMAN ** or ROBOT. */ void test_ishuman(void){ char zLine[3000]; while( fgets(zLine, sizeof(zLine), stdin) ){ fossil_print("%s %s", isHuman(zLine) ? "HUMAN" : "ROBOT", zLine); } } /* ** SQL function for constant time comparison of two values. ** Sets result to 0 if two values are equal. */ static void constant_time_cmp_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ const unsigned char *buf1, *buf2; int len, i; unsigned char rc = 0; assert( argc==2 ); len = sqlite3_value_bytes(argv[0]); if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){ rc = 1; }else{ buf1 = sqlite3_value_text(argv[0]); buf2 = sqlite3_value_text(argv[1]); for( i=0; i<len; i++ ){ 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; } /* ** Return true if users are allowed to reset their own passwords. */ int login_self_password_reset_available(void){ if( !db_get_boolean("self-pw-reset",0) ) return 0; if( !alert_tables_exist() ) return 0; return 1; } /* ** Return TRUE if self-registration is available. If the zNeeded ** argument is not NULL, then only return true if self-registration is ** available and any of the capabilities named in zNeeded are available ** to self-registered users. */ int login_self_register_available(const char *zNeeded){ CapabilityString *pCap; int rc; if( !db_get_boolean("self-register",0) ) return 0; if( zNeeded==0 ) return 1; pCap = capability_add(0, db_get("default-perms", "u")); capability_expand(pCap); rc = capability_has_any(pCap, zNeeded); capability_free(pCap); 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 int noAnon = P("noanon")!=0; int rememberMe; /* If true, use persistent cookie, else session cookie. Toggled per checkbox. */ if( P("pwreset")!=0 && login_self_password_reset_available() ){ /* If the "Reset Password" button in the form was pressed, render ** the Request Password Reset page in place of this one. */ login_reqpwreset_page(); return; } login_check_credentials(); fossil_redirect_to_https_if_needed(1); 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") && cgi_csrf_safe(2) ){ login_clear_login_data(); redirect_to_g(); return; } /* Redirect for create-new-account requests */ if( P("self") ){ cgi_redirectf("%R/register"); return; } /* Deal with password-change requests */ if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 && cgi_csrf_safe(2) ){ /* If there is not a "real" login, we cannot change any password. */ if( g.zLogin ){ /* 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); char *zChngPw; char *zErr; int rc; /* vvvvvvv--- tag-20230106-1 ----vvvvvv ** ** Replicate changes made below to tag-20230106-2 */ admin_log("password change for user %s", g.zLogin); db_unprotect(PROTECT_USER); db_multi_exec( "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid ); zChngPw = mprintf( "UPDATE user" " SET pw=shared_secret(%Q,%Q," " (SELECT value FROM config WHERE name='project-code'))" " WHERE login=%Q", zNew1, g.zLogin, g.zLogin ); fossil_free(zNewPw); rc = login_group_sql(zChngPw, "<p>", "</p>\n", &zErr); db_protect_pop(); /* ** ^^^^^^^^--- tag-20230106-1 ----^^^^^^^^^ ** ** Replicate changes above to tag-20230106-2 */ if( rc ){ zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr); fossil_free(zErr); }else{ redirect_to_g(); return; } } }else{ zErrMsg = @ <p><span class="loginError"> @ The password cannot be changed for this type of login. @ The password is unchanged. @ </span></p> ; } } zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */ uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs")); if(zUsername==0){ /* Initial login page hit. */ rememberMe = 0; }else{ rememberMe = P("remember")!=0; } if( uid>0 ){ login_set_anon_cookie(zIpAddr, NULL, rememberMe?0:1); 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); cgi_set_status(401, "Unauthorized"); }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, rememberMe?0:1); redirect_to_g(); } } style_set_current_feature("login"); style_header("Login/Logout"); style_adunit_config(ADUNIT_OFF); @ %s(zErrMsg) if( zGoto && !noAnon ){ 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>. } fossil_free(zAbbrev); } if( g.sslNotAvailable==0 && strncmp(g.zBaseURL,"https:",6)!=0 && db_get_boolean("https-login",0) ){ form_begin(0, "https:%s/login", g.zBaseURL+5); }else{ form_begin(0, "%R/login"); } if( zGoto ){ @ <input type="hidden" name="g" value="%h(zGoto)"> } 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> @ </form> }else{ unsigned int uSeed = captcha_seed(); if( g.zLogin==0 && (anonFlag || zGoto==0) ){ zAnonPw = db_text(0, "SELECT pw FROM user" " WHERE login='anonymous'" " AND cap!=''"); }else{ zAnonPw = 0; } @ <table class="login_out"> if( P("HTTPS")==0 ){ @ <tr><td class="form_label">Warning:</td> @ <td><span class='securityWarning'> @ Login information, including the password, @ will be sent in the clear over an unencrypted connection. if( !g.sslNotAvailable ){ @ Consider logging in at @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead. } @ </span></td></tr> } @ <tr> @ <td class="form_label" id="userlabel1">User ID:</td> @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \ @ size="30" value="%s(anonFlag?"anonymous":"")"></td> @ </tr> @ <tr> @ <td class="form_label" id="pswdlabel">Password:</td> @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \ @ name="p" value="" size="30">\ if( zAnonPw && !noAnon ){ captcha_speakit_button(uSeed, "Speak password for \"anonymous\""); } @ </td> @ </tr> @ <tr> @ <td></td> @ <td><input type="checkbox" name="remember" value="1" \ @ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")> @ <label for="remember-me">Remember me?</label></td> @ </tr> @ <tr> @ <td></td> @ <td><input type="submit" name="in" value="Login"> @ </tr> if( !noAnon && login_self_register_available(0) ){ @ <tr> @ <td></td> @ <td><input type="submit" name="self" value="Create A New Account"> @ </tr> } if( login_self_password_reset_available() ){ @ <tr> @ <td></td> @ <td><input type="submit" name="pwreset" value="Reset My Password"> @ </tr> } @ </table> if( zAnonPw && !noAnon ){ 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 class="captcha"> @ %h(zCaptcha) @ </pre></td></tr></table> if( bAutoCaptcha ) { @ <input type="button" value="Fill out captcha" id='autofillButton' \ @ data-af='%s(zDecoded)'> builtin_request_js("login.js"); } @ </div> free(zCaptcha); } @ </form> } if( login_is_individual() ){ if( g.perm.EmailAlert && alert_enabled() ){ @ <hr> @ <p>Configure <a href="%R/alerts">Email Alerts</a> @ for user <b>%h(g.zLogin)</b></p> } if( db_table_exists("repository","forumpost") ){ @ <hr><p> @ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum @ post timeline</a> for user <b>%h(g.zLogin)</b></p> } @ <hr><p> @ Select your preferred <a href="%R/skins">site skin</a>. @ </p> if( g.perm.Password ){ char *zRPW = fossil_random_password(12); @ <hr> @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> form_begin(0, "%R/login"); @ <table> @ <tr><td class="form_label" id="oldpw">Old Password:</td> @ <td><input aria-labelledby="oldpw" type="password" name="p" \ @ size="30"/></td></tr> @ <tr><td class="form_label" id="newpw">New Password:</td> @ <td><input aria-labelledby="newpw" type="password" name="n1" \ @ size="30"> Suggestion: %z(zRPW)</td></tr> @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> @ <td><input aria-labledby="reppw" type="password" name="n2" \ @ size="30"></td></tr> @ <tr><td></td> @ <td><input type="submit" value="Change Password"></td></tr> @ </table> @ </form> } } style_finish_page(); } /* ** Construct an appropriate URL suffix for the /resetpw page. The ** suffix will be of the form: ** ** UID-TIMESTAMP-HASH ** ** Where UID and TIMESTAMP are the parameters to this function, and HASH ** is constructed from information that is unique to the user in question ** and which is not publicly available. In particular, the HASH includes ** the existing user password. Thus, in order to construct a URL that can ** change a password, an attacker must know the current password, in which ** case the attacker does not need to construct the URL in order to take ** over the account. ** ** Return a pointer to the resulting string in memory obtained ** from fossil_malloc(). */ char *login_resetpw_suffix(int uid, i64 timestamp){ char *zHash; char *zInnerSql; char *zResult; extern int sqlite3_shathree_init(sqlite3*,char**,const sqlite3_api_routines*); if( timestamp<=0 ){ timestamp = time(0); } sqlite3_shathree_init(g.db, 0, 0); if( db_table_exists("repository","subscriber") ){ zInnerSql = mprintf( "SELECT %lld, login, pw, cookie, user.mtime, user.info, subscriberCode" " FROM user LEFT JOIN subscriber ON suname=login" " WHERE uid=%d", timestamp, uid); }else{ zInnerSql = mprintf( "SELECT %lld, login, pw, cookie, user.mtime, user.info" " FROM user WHERE uid=%d", timestamp, uid); } zHash = db_text(0, "SELECT lower(hex(sha3_query(%Q)))", zInnerSql); fossil_free(zInnerSql); zResult = mprintf("%x-%llx-%s", uid, timestamp, zHash); if( strlen(zHash)<64 || strlen(zResult)<70 ){ /* This should never happen, but if it does, we don't want it to lead ** to a security breach. */ fossil_panic("insecure password reset hash generated\n"); } fossil_free(zHash); return zResult; } /* ** Check to see if the "name" query parameter is a valid resetpw suffix ** for a user whose password we are allowed to reset. If it is, then return ** the positive integer UID for that user. If the query parameter is not ** valid, return 0. */ static int login_resetpw_suffix_is_valid(const char *zName){ int i, j; int uid; i64 timestamp; i64 now; char *zHash; if( zName==0 || strlen(zName)<70 ) goto not_valid_suffix; for(i=0; fossil_isxdigit(zName[i]); i++){} if( i<1 || zName[i]!='-' ) goto not_valid_suffix; for(j=i+1; fossil_isxdigit(zName[j]); j++){} if( j<=i+1 || zName[j]!='-' ) goto not_valid_suffix; uid = strtol(zName, 0, 16); if( uid<=0 ) goto not_valid_suffix; if( !db_exists("SELECT 1 FROM user WHERE uid=%d", uid) ){ goto not_valid_suffix; } timestamp = strtoll(&zName[i+1], 0, 16); now = time(0); if( timestamp+3600 <= now ) goto not_valid_suffix; zHash = login_resetpw_suffix(uid,timestamp); if( fossil_strcmp(zHash, zName)!=0 ){ fossil_free(zHash); goto not_valid_suffix; } fossil_free(zHash); return uid; not_valid_suffix: return 0; } /* ** COMMAND: test-resetpw-url ** Usage: fossil test-resetpw-url UID ** ** Generate and verify a /resetpw URL for user UID. ** ** This command is intended for unit testing the login_resetpw_suffix() ** and login_resetpw_suffix_is_valid() functions. */ void test_resetpw_url(void){ char *zSuffix; int uid; int xuid; char *zLogin; int i; db_find_and_open_repository(0, 0); verify_all_options(); if( g.argc<3 ){ usage("UID ..."); } for(i=2; i<g.argc; i++){ uid = atoi(g.argv[i]); zSuffix = login_resetpw_suffix(uid, 0); xuid = login_resetpw_suffix_is_valid(zSuffix); if( xuid>0 ){ zLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", xuid); }else{ zLogin = 0; } fossil_print("/resetpw/%s %d (%s)\n", zSuffix, xuid, zLogin ? zLogin : "???"); fossil_free(zSuffix); fossil_free(zLogin); } } /* ** WEBPAGE: resetpw ** ** The URL format must be like this: ** ** /resetpw/UID-TIMESTAMP-HASH ** ** Where UID is the uid of the user whose password is to be reset, ** TIMESTAMP is the unix timestamp when the request was made, and ** HASH is a hash based on UID, TIMESTAMP, and other information that ** is unavailable to an attacher. ** ** With no other arguments, a form is present which allows the user to ** enter a new password. When the SUBMIT button is pressed, a POST request ** back to the same URL that will change the password. */ void login_resetpw(void){ const char *zName; int uid; char *zRPW; const char *zNew1, *zNew2; style_set_current_feature("resetpw"); style_header("Reset Password"); style_adunit_config(ADUNIT_OFF); zName = PD("name",""); uid = login_resetpw_suffix_is_valid(zName); if( uid==0 ){ @ <p><span class="loginError"> @ This password-reset URL is invalid, probably because it has expired. @ Password-reset URLs have a short lifespan. @ </span></p> style_finish_page(); sleep(1); /* Introduce a small delay on an invalid suffix as an ** extra defense against search attacks */ return; } fossil_redirect_to_https_if_needed(1); login_set_uid(uid, 0); if( g.perm.Setup || g.perm.Admin || !g.perm.Password || g.zLogin==0 ){ @ <p><span class="loginError"> @ Cannot change the password for user <b>%h(g.zLogin)</b>. @ </span></p> style_finish_page(); return; } if( (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ if( fossil_strcmp(zNew1,zNew2)!=0 ){ @ <p><span class="loginError"> @ The two copies of your new passwords do not match. @ Try again. @ </span></p> }else{ char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0); char *zChngPw; char *zErr; int rc; /* vvvvvvv--- tag-20230106-2 ----vvvvvv ** ** Replicate changes made below to tag-20230106-1 */ admin_log("password change for user %s", g.zLogin); db_unprotect(PROTECT_USER); db_multi_exec( "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid ); zChngPw = mprintf( "UPDATE user" " SET pw=shared_secret(%Q,%Q," " (SELECT value FROM config WHERE name='project-code'))" " WHERE login=%Q", zNew1, g.zLogin, g.zLogin ); fossil_free(zNewPw); rc = login_group_sql(zChngPw, "<p>", "</p>\n", &zErr); db_protect_pop(); /* ** ^^^^^^^^--- tag-20230106-2 ----^^^^^^^^^ ** ** Replicate changes above to tag-20230106-1 */ if( rc ){ @ <p><span class='loginError'> @ %s(zErr); @ </span></p> fossil_free(zErr); }else{ @ <p>Password changed successfully. Go to the @ <a href="%R/login?u=%t(g.zLogin)">Login</a> page and log in @ using the new password to continue. @ </p> style_finish_page(); return; } } } zRPW = fossil_random_password(12); @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> form_begin(0, "%R/resetpw"); @ <input type='hidden' name='name' value='%h(zName)'> @ <table> @ <tr><td class="form_label" id="newpw">New Password:</td> @ <td><input aria-labelledby="newpw" type="password" name="n1" \ @ size="30"> Suggestion: %z(zRPW)</td></tr> @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> @ <td><input aria-labledby="reppw" type="password" name="n2" \ @ size="30"></td></tr> @ <tr><td></td> @ <td><input type="submit" value="Change Password"></td></tr> @ </table> @ </form> style_finish_page(); } /* ** 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 */ ){ 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 octet_length(cap)>0" " AND octet_length(pw)>0" " AND cexpire>julianday('now')" " AND constant_time_cmp(cookie,%Q)=0", zLogin, zHash ); pStmt = 0; rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ db_unprotect(PROTECT_USER); db_multi_exec( "UPDATE user SET cookie=%Q, cexpire=%.17g" " WHERE login=%Q", zHash, sqlite3_column_double(pStmt, 0), zLogin ); db_protect_pop(); nXfer++; } 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. ** Return 0 if not found. ** ** Note that this only searches for logged-in entries with matching ** zCookie (db: user.cookie) entries. */ static int login_find_user( const char *zLogin, /* User name */ const char *zCookie /* Login cookie value */ ){ int uid; if( login_is_special(zLogin) ) return 0; uid = db_int(0, "SELECT uid FROM user" " WHERE login=%Q" " AND cexpire>julianday('now')" " AND octet_length(cap)>0" " AND octet_length(pw)>0" " AND constant_time_cmp(cookie,%Q)=0", zLogin, zCookie ); return uid; } /* ** Attempt to use Basic Authentication to establish the user. Return the ** (non-zero) uid if successful. Return 0 if it does not work. */ static int login_basic_authentication(const char *zIpAddr){ const char *zAuth = PD("HTTP_AUTHORIZATION", 0); int i; int uid = 0; int nDecode = 0; char *zDecode = 0; const char *zUsername = 0; const char *zPasswd = 0; if( zAuth==0 ) return 0; /* Fail: No Authentication: header */ while( fossil_isspace(zAuth[0]) ) zAuth++; /* Skip leading whitespace */ if( strncmp(zAuth, "Basic ", 6)!=0 ){ return 0; /* Fail: Not Basic Authentication */ } /* Parse out the username and password, separated by a ":" */ zAuth += 6; while( fossil_isspace(zAuth[0]) ) zAuth++; zDecode = decode64(zAuth, &nDecode); for(i=0; zDecode[i] && zDecode[i]!=':'; i++){} if( zDecode[i] ){ zDecode[i] = 0; zUsername = zDecode; zPasswd = &zDecode[i+1]; /* Attempting to log in as the user provided by HTTP ** basic auth */ uid = login_search_uid(&zUsername, zPasswd); if( uid>0 ){ record_login_attempt(zUsername, zIpAddr, 1); }else{ record_login_attempt(zUsername, zIpAddr, 0); /* The user attempted to login specifically with HTTP basic ** auth, but provided invalid credentials. Inform them of ** the failed login attempt via 401. */ cgi_set_status(401, "Unauthorized"); cgi_reply(); fossil_exit(0); } } fossil_free(zDecode); return uid; } /* ** 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 ** g.perm Populated based on user account's capabilities ** */ 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 */ const char *zCap = 0; /* Capability string */ 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. */ zIpAddr = PD("REMOTE_ADDR","nil"); if( ( cgi_is_loopback(zIpAddr) || (g.fSshClient & CGI_SSH_CLIENT)!=0 ) && g.useLocalauth && db_get_int("localauth",0)==0 && P("HTTPS")==0 ){ char *zSeed; 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 = "sxy"; g.noPswd = 1; g.isHuman = 1; zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)" " FROM user WHERE uid=%d", uid); login_create_csrf_secret(zSeed); fossil_free(zSeed); } /* Check the login cookie to see if it matches a known valid user. */ if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ /* Parse the cookie value up into HASH/ARG/USER */ char *zHash = fossil_strdup(zCookie); char *zArg = 0; char *zUser = 0; int i, c; for(i=0; (c = zHash[i])!=0; i++){ if( c=='/' ){ zHash[i++] = 0; if( zArg==0 ){ zArg = &zHash[i]; }else{ zUser = &zHash[i]; break; } } } if( zUser==0 ){ /* Invalid cookie */ }else if( fossil_strcmp(zUser, "anonymous")==0 ){ /* Cookies of the form "HASH/TIME/anonymous". The TIME must not be ** too old and the sha1 hash of TIME/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", zArg, 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 octet_length(cap)>0" " AND octet_length(pw)>0" " AND %.17g+0.25>julianday('now')", rTime ); } blob_reset(&b); }else{ /* Cookies of the form "HASH/CODE/USER". Search first in the ** local user table, then the user table for project CODE if we ** are part of a login-group. */ uid = login_find_user(zUser, zHash); if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){ uid = login_find_user(zUser, zHash); if( uid ) record_login_attempt(zUser, zIpAddr, 1); } } login_create_csrf_secret(zHash); } /* If no user found and the REMOTE_USER environment variable is set, ** then accept the value of REMOTE_USER as the user. */ if( uid==0 ){ const char *zRemoteUser = P("REMOTE_USER"); if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){ uid = db_int(0, "SELECT uid FROM user WHERE login=%Q" " AND octet_length(cap)>0 AND octet_length(pw)>0", zRemoteUser); } } /* If the request didn't provide a login cookie or the login cookie didn't ** match a known valid user, check the HTTP "Authorization" header and ** see if those credentials are valid for a known user. */ if( uid==0 && db_get_boolean("http_authentication_ok",0) ){ uid = login_basic_authentication(zIpAddr); } /* Check for magic query parameters "resid" (for the username) and ** "token" for the password. Both values (if they exist) will be ** obfuscated. */ if( uid==0 ){ char *zUsr, *zPW; if( (zUsr = unobscure(P("resid")))!=0 && (zPW = unobscure(P("token")))!=0 ){ char *zSha1Pw = sha1_shared_secret(zPW, zUsr, 0); uid = db_int(0, "SELECT uid FROM user" " WHERE login=%Q" " AND (constant_time_cmp(pw,%Q)=0" " OR constant_time_cmp(pw,%Q)=0)", zUsr, zSha1Pw, zPW); fossil_free(zSha1Pw); } } /* If no user found yet, try to log in as "nobody" */ if( uid==0 ){ uid = db_int(0, "SELECT uid FROM user WHERE login='nobody'"); if( uid==0 ){ /* If there is no user "nobody", then make one up - with no privileges */ uid = -1; zCap = ""; } login_create_csrf_secret("none"); } login_set_uid(uid, zCap); } /* ** Set the current logged in user to be uid. zCap is precomputed ** (override) capabilities. If zCap==0, then look up the capabilities ** in the USER table. */ int login_set_uid(int uid, const char *zCap){ const char *zPublicPages = 0; /* GLOB patterns of public pages */ /* At this point, we know that uid!=0. Find the privileges associated ** with user uid. */ assert( uid!=0 ); if( zCap==0 ){ Stmt s; db_prepare(&s, "SELECT login, cap FROM user WHERE uid=%d", uid); |
︙ | ︙ | |||
435 436 437 438 439 440 441 | fprintf(stderr, "# login: [%s] with capabilities [%s]\n", g.zLogin, zCap); } /* Set the global variables recording the userid and login. The ** "nobody" user is a special case in that g.zLogin==0. */ g.userUid = uid; | | > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > > | | > > > > < | | | > | > > > | > | | > > > > > > | > > | < < > > > > | | | | | | > > > > | | | | > | | > | > | > > | | | > | | | | | > > | > > > > | > | > | | | | > > | | | > > | > > > > > > > > > > > > | > | | | < > | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | > > > > > > > > > > > > > | | | | | > | | | < < < | | | > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > | > > | > > > > > > > > > > | > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > | | 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 | fprintf(stderr, "# login: [%s] with capabilities [%s]\n", g.zLogin, zCap); } /* Set the global variables recording the userid and login. The ** "nobody" user is a special case in that g.zLogin==0. */ g.userUid = uid; if( fossil_strcmp(g.zLogin,"nobody")==0 ){ g.zLogin = 0; } if( PB("isrobot") ){ g.isHuman = 0; }else if( g.zLogin==0 ){ g.isHuman = isHuman(P("HTTP_USER_AGENT")); }else{ g.isHuman = 1; } /* Set the capabilities */ login_replace_capabilities(zCap, 0); /* The auto-hyperlink setting allows hyperlinks to be displayed for users ** who do not have the "h" permission as long as their UserAgent string ** makes it appear that they are human. Check to see if auto-hyperlink is ** enabled for this repository and make appropriate adjustments to the ** permission flags if it is. This should be done before the permissions ** are (potentially) copied to the anonymous permission set; otherwise, ** those will be out-of-sync. */ if( zCap[0] && !g.perm.Hyperlink && g.isHuman ){ int autoLink = db_get_int("auto-hyperlink",1); if( autoLink==1 ){ g.jsHref = 1; g.perm.Hyperlink = 1; }else if( autoLink==2 ){ g.perm.Hyperlink = 1; } } /* ** At this point, the capabilities for the logged in user are not going ** to be modified anymore; therefore, we can copy them over to the ones ** for the anonymous user. ** ** WARNING: In the future, please do not add code after this point that ** modifies the capabilities for the logged in user. */ login_set_anon_nobody_capabilities(); /* If the public-pages glob pattern is defined and REQUEST_URI matches ** one of the globs in public-pages, then also add in all default-perms ** permissions. */ zPublicPages = db_get("public-pages",0); if( zPublicPages!=0 ){ Glob *pGlob = glob_create(zPublicPages); const char *zUri = PD("REQUEST_URI",""); zUri += (int)strlen(g.zTop); if( glob_match(pGlob, zUri) ){ login_set_capabilities(db_get("default-perms", "u"), 0); } glob_free(pGlob); } return g.zLogin!=0; } /* ** 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 = p->RdForum = p->WrForum = p->ModForum = p->WrTForum = p->AdminForum = p->Chat = p->EmailAlert = p->Announce = p->Debug = 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 '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; case 'y': p->WrUnver = 1; break; case '6': p->AdminForum = 1; case '5': p->ModForum = 1; case '4': p->WrTForum = 1; case '3': p->WrForum = 1; case '2': p->RdForum = 1; break; case '7': p->EmailAlert = 1; break; case 'A': p->Announce = 1; break; case 'C': p->Chat = 1; break; case 'D': p->Debug = 1; break; /* The "u" privilege recursively ** inherits all privileges of the user named "reader" */ case 'u': { if( p->XReader==0 ){ const char *zUser; p->XReader = 1; zUser = db_text("", "SELECT cap FROM user WHERE login='reader'"); login_set_capabilities(zUser, flags); } break; } /* The "v" privilege recursively ** inherits all privileges of the user named "developer" */ case 'v': { if( p->XDeveloper==0 ){ const char *zDev; p->XDeveloper = 1; zDev = db_text("", "SELECT cap FROM user WHERE login='developer'"); login_set_capabilities(zDev, flags); } break; } } } } /* ** Zeroes out g.perm and calls login_set_capabilities(zCap,flags). */ void login_replace_capabilities(const char *zCap, unsigned flags){ memset(&g.perm, 0, sizeof(g.perm)); login_set_capabilities(zCap, flags); login_anon_once = 1; } /* ** If the current login lacks any of the capabilities listed in ** the input, then return 0. If all capabilities are present, then ** return 1. ** ** As a special case, the 'L' pseudo-capability ID means "is logged ** in" and will return true for any non-guest user. */ 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; /* d unused: see comment in capabilities.c */ 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': rc = p->WrUnver; break; case 'z': rc = p->Zip; break; case '2': rc = p->RdForum; break; case '3': rc = p->WrForum; break; case '4': rc = p->WrTForum; break; case '5': rc = p->ModForum; break; case '6': rc = p->AdminForum;break; case '7': rc = p->EmailAlert;break; case 'A': rc = p->Announce; break; case 'C': rc = p->Chat; break; case 'D': rc = p->Debug; break; case 'L': rc = g.zLogin && *g.zLogin; break; /* Mainenance reminder: '@' should not be used because it would semantically collide with the @ in the capexpr TH1 command. */ default: rc = 0; break; } } return rc; } /* ** Change the login to zUser. */ void login_as_user(const char *zUser){ char *zCap = ""; /* New capabilities */ /* Turn off all capabilities from prior logins */ memset( &g.perm, 0, sizeof(g.perm) ); /* Set the global variables recording the userid and login. The ** "nobody" user is a special case in that g.zLogin==0. */ g.userUid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUser); if( g.userUid==0 ){ zUser = 0; g.userUid = db_int(0, "SELECT uid FROM user WHERE login='nobody'"); } if( g.userUid ){ zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", g.userUid); } if( fossil_strcmp(zUser,"nobody")==0 ) zUser = 0; 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 true if the user is a specific individual, not "nobody" or ** "anonymous". */ int login_is_individual(void){ return g.zLogin!=0 && g.zLogin[0]!=0 && fossil_strcmp(g.zLogin,"nobody")!=0 && fossil_strcmp(g.zLogin,"anonymous")!=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 *zQS = P("QUERY_STRING"); const char *zPathInfo = PD("PATH_INFO",""); Blob redir; blob_init(&redir, 0, 0); if( zPathInfo[0]=='/' ) zPathInfo++; /* skip leading slash */ if( fossil_wants_https(1) ){ blob_appendf(&redir, "%s/login?g=%T", g.zHttpsURL, zPathInfo); }else{ blob_appendf(&redir, "%R/login?g=%T", zPathInfo); } if( zQS && zQS[0] ){ blob_appendf(&redir, "%%3f%T", zQS); } if( anonOk ) blob_append(&redir, "&anon", 5); 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("PATH_INFO", ""); @ <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. */ void login_insert_csrf_secret(void){ @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)"> } /* ** Check to see if the candidate username zUserID is already used. ** Return 1 if it is already in use. Return 0 if the name is ** available for a self-registeration. */ static int login_self_choosen_userid_already_exists(const char *zUserID){ int rc = db_exists( "SELECT 1 FROM user WHERE login=%Q " "UNION ALL " "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", zUserID, zUserID, zUserID ); return rc; } /* ** zEMail is an email address. (Example: "xyz@gmail.com".) This routine ** searches for a user or subscriber that has that email address. If the ** email address is used no-where in the system, return 0. If the email ** address is assigned to a particular user return the UID for that user. ** If the email address is used, but not by a particular user, return -1. */ static int email_address_in_use(const char *zEMail){ int uid; uid = db_int(0, "SELECT uid FROM user" " WHERE info LIKE '%%<%q>%%'", zEMail); if( uid>0 ){ if( db_exists("SELECT 1 FROM user WHERE uid=%d AND (" " cap GLOB '*[as]*' OR" " find_emailaddr(info)<>%Q COLLATE nocase)", uid, zEMail) ){ uid = -1; } } if( uid==0 && alert_tables_exist() ){ uid = db_int(0, "SELECT user.uid FROM subscriber JOIN user ON login=suname" " WHERE semail=%Q AND sverified", zEMail); if( uid ){ if( db_exists("SELECT 1 FROM user WHERE uid=%d AND " " cap GLOB '*[as]*'", uid) ){ uid = -1; } } } return uid; } /* ** COMMAND: test-email-used ** Usage: fossil test-email-used EMAIL ... ** ** Given a list of email addresses, show the UID and LOGIN associated ** with each one. */ void test_email_used(void){ int i; db_find_and_open_repository(0, 0); verify_all_options(); if( g.argc<3 ){ usage("EMAIL ..."); } for(i=2; i<g.argc; i++){ const char *zEMail = g.argv[i]; int uid = email_address_in_use(zEMail); if( uid==0 ){ fossil_print("%s: not used\n", zEMail); }else if( uid<0 ){ fossil_print("%s: used but no password reset is available\n", zEMail); }else{ char *zLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid); fossil_print("%s: UID %d (%s)\n", zEMail, uid, zLogin); fossil_free(zLogin); } } } /* ** Check an email address and confirm that it is valid for self-registration. ** The email address is known already to be well-formed. Return true ** if the email address is on the allowed list. ** ** The default behavior is that any valid email address is accepted. ** But if the "auth-sub-email" setting exists and is not empty, then ** it is a comma-separated list of GLOB patterns for email addresses ** that are authorized to self-register. */ int authorized_subscription_email(const char *zEAddr){ char *zGlob = db_get("auth-sub-email",0); Glob *pGlob; char *zAddr; int rc; if( zGlob==0 || zGlob[0]==0 ) return 1; zGlob = fossil_strtolwr(fossil_strdup(zGlob)); pGlob = glob_create(zGlob); fossil_free(zGlob); zAddr = fossil_strtolwr(fossil_strdup(zEAddr)); rc = glob_match(pGlob, zAddr); fossil_free(zAddr); glob_free(pGlob); return rc!=0; } /* ** WEBPAGE: register ** ** Page to allow users to self-register. The "self-register" setting ** must be enabled for this page to operate. */ void register_page(void){ const char *zUserID, *zPasswd, *zConfirm, *zEAddr; const char *zDName; unsigned int uSeed; const char *zDecoded; int iErrLine = -1; const char *zErr = 0; int uid = 0; /* User id with the same email */ int captchaIsCorrect = 0; /* True on a correct captcha */ char *zCaptcha = ""; /* Value of the captcha text */ char *zPerms; /* Permissions for the default user */ int canDoAlerts = 0; /* True if receiving email alerts is possible */ int doAlerts = 0; /* True if subscription is wanted too */ 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_finish_page(); return; } if( P("pwreset")!=0 && login_self_password_reset_available() ){ /* The "Request Password Reset" button was pressed, so render the ** "Request Password Reset" page instead of this one. */ login_reqpwreset_page(); return; } zPerms = db_get("default-perms", "u"); login_check_credentials(); /* Prompt the user for email alerts if this repository is configured for ** email alerts and if the default permissions include "7" */ canDoAlerts = alert_tables_exist() && (db_int(0, "SELECT fullcap(%Q) GLOB '*7*'", zPerms ) || db_get_boolean("selfreg-verify",0)); doAlerts = canDoAlerts && atoi(PD("alerts","1"))!=0; zUserID = PDT("u",""); zPasswd = PDT("p",""); zConfirm = PDT("cp",""); zEAddr = PDT("ea",""); zDName = PDT("dn",""); /* Verify user imputs */ if( P("new")==0 || !cgi_csrf_safe(2) ){ /* This is not a valid form submission. Fall through into ** the form display */ }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ iErrLine = 6; zErr = "Incorrect CAPTCHA"; }else if( strlen(zUserID)<6 ){ iErrLine = 1; zErr = "User ID too short. Must be at least 6 characters."; }else if( sqlite3_strglob("*[^-a-zA-Z0-9_.]*",zUserID)==0 ){ iErrLine = 1; zErr = "User ID may not contain spaces or special characters."; }else if( sqlite3_strlike("anonymous%", zUserID, 0)==0 || sqlite3_strlike("nobody%", zUserID, 0)==0 || sqlite3_strlike("reader%", zUserID, 0)==0 || sqlite3_strlike("developer%", zUserID, 0)==0 ){ iErrLine = 1; zErr = "This User ID is reserved. Choose something different."; }else if( zDName[0]==0 ){ iErrLine = 2; zErr = "Required"; }else if( zEAddr[0]==0 ){ iErrLine = 3; zErr = "Required"; }else if( email_address_is_valid(zEAddr,0)==0 ){ iErrLine = 3; zErr = "Not a valid email address"; }else if( authorized_subscription_email(zEAddr)==0 ){ iErrLine = 3; zErr = "Not an authorized email address"; }else if( strlen(zPasswd)<6 ){ iErrLine = 4; zErr = "Password must be at least 6 characters long"; }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ iErrLine = 5; zErr = "Passwords do not match"; }else if( (uid = email_address_in_use(zEAddr))!=0 ){ iErrLine = 3; zErr = "This email address is already associated with a user"; }else if( login_self_choosen_userid_already_exists(zUserID) ){ iErrLine = 1; zErr = "This User ID is already taken. Choose something different."; }else{ /* If all of the tests above have passed, that means that the submitted ** form contains valid data and we can proceed to create the new login */ Blob sql; int uid; char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); const char *zStartPerms = zPerms; if( db_get_boolean("selfreg-verify",0) ){ /* If email verification is required for self-registration, initalize ** the new user capabilities to just "7" (Sign up for email). The ** full "default-perms" permissions will be added when they click ** the verification link on the email they are sent. */ zStartPerms = "7"; } blob_init(&sql, 0, 0); blob_append_sql(&sql, "INSERT INTO user(login,pw,cap,info,mtime)\n" "VALUES(%Q,%Q,%Q," "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr); fossil_free(zPass); db_unprotect(PROTECT_USER); db_multi_exec("%s", blob_sql_text(&sql)); db_protect_pop(); uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); login_set_user_cookie(zUserID, uid, NULL, 0); if( doAlerts ){ /* Also make the new user a subscriber. */ Blob hdr, body; AlertSender *pSender; const char *zCode; /* New subscriber code (in hex) */ const char *zGoto = P("g"); int nsub = 0; char ssub[20]; CapabilityString *pCap; pCap = capability_add(0, zPerms); capability_expand(pCap); ssub[nsub++] = 'a'; if( capability_has_any(pCap,"o") ) ssub[nsub++] = 'c'; if( capability_has_any(pCap,"2") ) ssub[nsub++] = 'f'; if( capability_has_any(pCap,"r") ) ssub[nsub++] = 't'; if( capability_has_any(pCap,"j") ) ssub[nsub++] = 'w'; ssub[nsub] = 0; capability_free(pCap); /* Also add the user to the subscriber table. */ zCode = db_text(0, "INSERT INTO subscriber(semail,suname," " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip,lastContact)" " VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q,now()/86400)" " ON CONFLICT(semail) DO UPDATE" " SET suname=excluded.suname" " RETURNING hex(subscriberCode);", /* semail */ zEAddr, /* suname */ zUserID, /* sverified */ 0, /* sdigest */ 0, /* ssub */ ssub, /* smip */ g.zIpAddr ); if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q" " AND sverified", zEAddr) ){ /* This the case where the user was formerly a verified subscriber ** and here they have also registered as a user as well. It is ** not necessary to repeat the verfication step */ redirect_to_g(); } /* A verification email */ pSender = alert_sender_new(0,0); blob_init(&hdr,0,0); blob_init(&body,0,0); blob_appendf(&hdr, "To: <%s>\n", zEAddr); blob_appendf(&hdr, "Subject: Subscription verification\n"); alert_append_confirmation_message(&body, zCode); alert_send(pSender, &hdr, &body, 0); style_header("Email Verification"); if( pSender->zErr ){ @ <h1>Internal Error</h1> @ <p>The following internal error was encountered while trying @ to send the confirmation email: @ <blockquote><pre> @ %h(pSender->zErr) @ </pre></blockquote> }else{ @ <p>An email has been sent to "%h(zEAddr)". That email contains a @ hyperlink that you must click to activate your account.</p> } alert_sender_free(pSender); if( zGoto ){ @ <p><a href='%h(zGoto)'>Continue</a> } style_finish_page(); return; } redirect_to_g(); } /* Prepare the captcha. */ if( captchaIsCorrect ){ uSeed = strtoul(P("captchaseed"),0,10); }else{ uSeed = captcha_seed(); } zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); style_header("Register"); /* Print out the registration form. */ g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ form_begin(0, "%R/register"); if( P("g") ){ @ <input type="hidden" name="g" value="%h(P("g"))"> } @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)"> @ <table class="login_out"> @ <tr> @ <td class="form_label" align="right" id="uid">User ID:</td> @ <td><input aria-labelledby="uid" type="text" name="u" \ @ value="%h(zUserID)" size="30"></td> @ if( iErrLine==1 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> } @ <tr> @ <td class="form_label" align="right" id="dpyname">Display Name:</td> @ <td><input aria-labelledby="dpyname" type="text" name="dn" \ @ value="%h(zDName)" size="30"></td> @ </tr> if( iErrLine==2 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> } @ </tr> @ <tr> @ <td class="form_label" align="right" id="emaddr">Email Address:</td> @ <td><input aria-labelledby="emaddr" type="text" name="ea" \ @ value="%h(zEAddr)" size="30"></td> @ </tr> if( iErrLine==3 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span> if( uid>0 && login_self_password_reset_available() ){ @ <br> @ <input type="submit" name="pwreset" \ @ value="Request Password Reset For %h(zEAddr)"> } @ </td></tr> } if( canDoAlerts ){ int a = atoi(PD("alerts","1")); @ <tr> @ <td class="form_label" align="right" id="emalrt">Email Alerts?</td> @ <td><select aria-labelledby="emalrt" size='1' name='alerts'> @ <option value="1" %s(a?"selected":"")>Yes</option> @ <option value="0" %s(!a?"selected":"")>No</option> @ </select></td></tr> } @ <tr> @ <td class="form_label" align="right" id="pswd">Password:</td> @ <td><input aria-labelledby="pswd" type="password" name="p" \ @ value="%h(zPasswd)" size="30"> \ if( zPasswd[0]==0 ){ char *zRPW = fossil_random_password(12); @ Password suggestion: %z(zRPW)</td> }else{ @ </td> } @ <tr> if( iErrLine==4 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> } @ <tr> @ <td class="form_label" align="right" id="pwcfrm">Confirm:</td> @ <td><input aria-labelledby="pwcfrm" type="password" name="cp" \ @ value="%h(zConfirm)" size="30"></td> @ </tr> if( iErrLine==5 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> } @ <tr> @ <td class="form_label" align="right" id="cptcha">Captcha:</td> @ <td><input type="text" name="captcha" aria-labelledby="cptcha" \ @ value="%h(captchaIsCorrect?zDecoded:"")" size="30"> captcha_speakit_button(uSeed, "Speak the captcha text"); @ </td> @ </tr> if( iErrLine==6 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> } @ <tr><td></td> @ <td><input type="submit" name="new" value="Register"></td></tr> @ </table> @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> @ %h(zCaptcha) @ </pre> @ Enter this 8-letter code in the "Captcha" box above. @ </td></tr></table></div> @ </form> style_finish_page(); free(zCaptcha); } /* ** WEBPAGE: reqpwreset ** ** A web page to request a password reset. ** ** A form is presented where the user can enter their email address ** and a captcha. If the email address entered corresponds to a known ** users, an email is sent to that address that contains a link to the ** /resetpw page that allows the users to enter a new password. ** ** This page is only available if the self-pw-reset property is enabled ** and email notifications are configured and operating. Password resets ** are not available to users with Admin or Setup privilege. */ void login_reqpwreset_page(void){ const char *zEAddr; const char *zDecoded; unsigned int uSeed; int iErrLine = -1; const char *zErr = 0; int uid = 0; /* User id with the email zEAddr */ int captchaIsCorrect = 0; /* True on a correct captcha */ char *zCaptcha = ""; /* Value of the captcha text */ if( !login_self_password_reset_available() ){ style_header("Password reset not possible"); @ <p>This project does not allow users to reset their own passwords. @ If you need a password reset, you will have to negotiate that directly @ with the project administrator. style_finish_page(); return; } zEAddr = PDT("ea",""); /* Verify user imputs */ if( !cgi_csrf_safe(1) || P("reqpwreset")==0 ){ /* This is the initial display of the form. No processing or error ** checking is to be done. Fall through into the form display ** ** cgi_csrf_safe(): Nothing interesting happens on this page without ** a valid captcha solution, so we only need to check referrer and that ** the request is a POST. */ }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ iErrLine = 2; zErr = "Incorrect CAPTCHA"; }else if( zEAddr[0]==0 ){ iErrLine = 1; zErr = "Required"; }else if( email_address_is_valid(zEAddr,0)==0 ){ iErrLine = 1; zErr = "Not a valid email address"; }else if( authorized_subscription_email(zEAddr)==0 ){ iErrLine = 1; zErr = "Not an authorized email address"; }else if( (uid = email_address_in_use(zEAddr))<=0 ){ iErrLine = 1; zErr = "This email address is not associated with a user who has " "password reset privileges."; }else if( login_set_uid(uid,0)==0 || g.perm.Admin || g.perm.Setup || !g.perm.Password ){ iErrLine = 1; zErr = "This email address is not associated with a user who has " "password reset privileges."; }else{ /* If all of the tests above have passed, that means that the submitted ** form contains valid data and we can proceed to issue the password ** reset email. */ Blob hdr, body; AlertSender *pSender; char *zUrl = login_resetpw_suffix(uid, 0); pSender = alert_sender_new(0,0); blob_init(&hdr,0,0); blob_init(&body,0,0); blob_appendf(&hdr, "To: <%s>\n", zEAddr); blob_appendf(&hdr, "Subject: Password reset for %s\n", g.zBaseURL); blob_appendf(&body, "Someone has requested to reset the password for user \"%s\"\n", g.zLogin); blob_appendf(&body, "at %s.\n\n", g.zBaseURL); blob_appendf(&body, "If you did not request this password reset, ignore\n" "this email\n\n"); blob_appendf(&body, "To reset the password, visit the following link:\n\n" " %s/resetpw/%s\n\n", g.zBaseURL, zUrl); fossil_free(zUrl); alert_send(pSender, &hdr, &body, 0); style_header("Email Verification"); if( pSender->zErr ){ @ <h1>Internal Error</h1> @ <p>The following internal error was encountered while trying @ to send the confirmation email: @ <blockquote><pre> @ %h(pSender->zErr) @ </pre></blockquote> }else{ @ <p>An email containing a hyperlink that can be used to reset @ your password has been sent to "%h(zEAddr)".</p> } alert_sender_free(pSender); style_finish_page(); return; } /* Prepare the captcha. */ if( captchaIsCorrect ){ uSeed = strtoul(P("captchaseed"),0,10); }else{ uSeed = captcha_seed(); } zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); style_header("Request Password Reset"); /* Print out the registration form. */ g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ form_begin(0, "%R/reqpwreset"); @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)"> @ <p><input type="hidden" name="reqpwreset" value="1"> @ <table class="login_out"> @ <tr> @ <td class="form_label" align="right" id="emaddr">Email Address:</td> @ <td><input aria-labelledby="emaddr" type="text" name="ea" \ @ value="%h(zEAddr)" size="30"></td> @ </tr> if( iErrLine==1 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> } @ <tr> @ <td class="form_label" align="right" id="cptcha">Captcha:</td> @ <td><input type="text" name="captcha" aria-labelledby="cptcha" \ @ value="%h(captchaIsCorrect?zDecoded:"")" size="30"> captcha_speakit_button(uSeed, "Speak the captcha text"); @ </td> @ </tr> if( iErrLine==2 ){ @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> } @ <tr><td></td> @ <td><input type="submit" name="new" value="Request Password Reset"/>\ @ </td></tr> @ </table> @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> @ %h(zCaptcha) @ </pre> @ Enter this 8-letter code in the "Captcha" box above. @ </td></tr></table></div> @ </form> style_finish_page(); free(zCaptcha); } /* ** Run SQL on the repository database for every repository in our ** login group. The SQL is run in a separate database connection. ** ** Any members of the login group whose repository database file ** cannot be found is silently removed from the group. ** ** Error messages accumulate and are returned in *pzErrorMsg. The ** memory used to hold these messages should be freed using ** fossil_free() if one desired to avoid a memory leak. The ** zPrefix and zSuffix strings surround each error message. ** ** Return the number of errors. */ int login_group_sql( const char *zSql, /* The SQL to run */ const char *zPrefix, /* Prefix to each error message */ const char *zSuffix, /* Suffix to each error message */ char **pzErrorMsg /* Write error message here, if not NULL */ ){ sqlite3 *pPeer; /* Connection to another database */ int nErr = 0; /* Number of errors seen so far */ int rc; /* Result code from subroutine calls */ char *zErr; /* SQLite error text */ char *zSelfCode; /* Project code for ourself */ Blob err; /* Accumulate errors here */ 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, ExtFILE)<0 ){ /* Silently remove non-existent repositories from the login group. */ const char *zLabel = db_column_text(&q, 0); db_unprotect(PROTECT_CONFIG); db_multi_exec( "DELETE FROM config WHERE name GLOB 'peer-*-%q'", &zLabel[10] ); db_protect_pop(); 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++; }else if( rc!=SQLITE_OK ){ blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName, sqlite3_errmsg(pPeer), zSuffix); nErr++; } sqlite3_close(pPeer); } db_finalize(&q); if( pzErrorMsg && blob_size(&err)>0 ){ *pzErrorMsg = fossil_strdup(blob_str(&err)); } blob_reset(&err); fossil_free(zSelfCode); return nErr; } /* ** Attempt to join a login-group. ** ** If problems arise, leave an error message in *pzErrMsg. */ void login_group_join( const char *zRepo, /* Repository file in the login group */ int bPwRequired, /* True if the login,password is required */ const char *zLogin, /* Login name for the other repo */ const char *zPassword, /* Password to prove we are authorized to join */ const char *zNewName, /* Name of new login group if making a new one */ char **pzErrMsg /* Leave an error message here */ ){ Blob fullName; /* Blob for finding full pathnames */ sqlite3 *pOther; /* The other repository */ int rc; /* Return code from sqlite3 functions */ char *zOtherProjCode; /* Project code for pOther */ char *zSelfRepo; /* Name of our repository */ char *zSelfLabel; /* Project-name for our repository */ 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 = "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, ExtFILE)<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 ** valid and has Setup permission. */ db_attach(zRepo, "other"); zOtherProjCode = db_text("x", "SELECT value FROM other.config" " WHERE name='project-code'"); if( bPwRequired ){ char *zPwHash; /* Password hash on pOther */ zPwHash = sha1_shared_secret(zPassword, zLogin, zOtherProjCode); if( !db_exists( "SELECT 1 FROM other.user" " WHERE login=%Q AND cap GLOB '*s*'" " AND (pw=%Q OR pw=%Q)", zLogin, zPassword, zPwHash) ){ db_detach("other"); *pzErrMsg = "The supplied username/password does not correspond to a" " user Setup permission on the other repository."; return; } } /* 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_unprotect(PROTECT_CONFIG); 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_protect_pop(); db_end_transaction(0); db_multi_exec("DETACH other"); /* Propagate the changes to all other members of the login-group */ zSql = mprintf( "BEGIN;" "REPLACE INTO config(name,value,mtime) VALUES('peer-name-%q',%Q,now());" "REPLACE INTO config(name,value,mtime) VALUES('peer-repo-%q',%Q,now());" "COMMIT;", zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo ); db_unprotect(PROTECT_CONFIG); login_group_sql(zSql, "<li> ", "</li>", pzErrMsg); db_protect_pop(); fossil_free(zSql); } /* ** Leave the login group that we are currently part of. */ void login_group_leave(char **pzErrMsg){ char *zProjCode; char *zSql; *pzErrMsg = 0; zProjCode = abbreviated_project_code(db_get("project-code","x")); zSql = mprintf( "DELETE FROM config WHERE name GLOB 'peer-*-%q';" "DELETE FROM config" " WHERE name='login-group-name'" " AND (SELECT count(*) FROM config WHERE name GLOB 'peer-*')==0;", zProjCode ); fossil_free(zProjCode); db_unprotect(PROTECT_CONFIG); login_group_sql(zSql, "<li> ", "</li>", pzErrMsg); fossil_free(zSql); db_multi_exec( "DELETE FROM config " " WHERE name GLOB 'peer-*'" " OR name GLOB 'login-group-*';" ); db_protect_pop(); } /* ** COMMAND: login-group* ** ** Usage: %fossil login-group ?SUBCOMMAND? ?OPTIONS? ** ** Run various subcommands to manage login-group related settings of the open ** repository or of the repository identified by the -R or --repository option. ** ** > fossil login-group ?-R REPO? ** ** Show the login-group to which REPO, or if invoked from within a check-out ** the repository on which the current check-out is based, belongs. ** ** > fossil login-group join ?-R REPO? ?--name NAME? REPO2 ** ** This command will either: (1) add the repository on which the current ** check-out is based, or the repository REPO specified with -R, to the ** login group where REPO2 is a member, in which case the optional --name ** argument is not required; or (2) create a new login group between the ** repository on which the current check-out is based, or the repository ** REPO specified with -R, and REPO2, in which case the new group NAME is ** determined by the mandatory --name option. In both cases, the specified ** repositories will first leave any group in which they are currently a ** member before joining the new login group. ** ** > fossil login-group leave ?-R REPO? ** ** Take the repository REPO, or if invoked from within a check-out the ** repository on which the current check-out is based, out of whatever ** login group it is a member. ** ** About Login Groups: ** ** A login-group is a set of repositories that share user credentials. ** If a user is logged into one member of the group, then that user can ** access any other group member as long as they have an entry in the USER ** table of that member. If a user changes their password using web ** interface, their password is also automatically changed in every other ** member of the login group. */ void login_group_command(void){ const char *zLGName; const char *zCmd; int nCmd; Stmt q; db_find_and_open_repository(0, 0); if( g.argc>2 ){ zCmd = g.argv[2]; nCmd = (int)strlen(zCmd); if( strncmp(zCmd,"join",nCmd)==0 && nCmd>=1 ){ const char *zNewName = find_option("name",0,1); const char *zOther = 0; char *zErr = 0; verify_all_options(); if( g.argc!=4 ){ fossil_fatal("unexpected argument count for \"login-group join\""); } zOther = g.argv[3]; login_group_leave(&zErr); sqlite3_free(zErr); zErr = 0; login_group_join(zOther,0,0,0,zNewName,&zErr); if( zErr ){ fossil_fatal("%s", zErr); } }else if( strncmp(zCmd,"leave",nCmd)==0 && nCmd>=1 ){ verify_all_options(); if( g.argc!=3 ){ fossil_fatal("unknown extra arguments to \"login-group leave\""); } zLGName = login_group_name(); if( zLGName ){ char *zErr = 0; fossil_print("Leaving login-group \"%s\"\n", zLGName); login_group_leave(&zErr); if( zErr ) fossil_fatal("Oops: %s", zErr); return; } }else{ fossil_fatal("unknown command \"%s\" - should be \"join\" or \"leave\"", zCmd); } } /* Show the current login group information */ zLGName = login_group_name(); if( zLGName==0 ){ fossil_print("Not currently a part of any login-group\n"); return; } fossil_print("Now part of login-group \"%s\" with:\n", zLGName); db_prepare(&q, "SELECT value FROM config WHERE name LIKE 'peer-repo-%%'"); while( db_step(&q)==SQLITE_ROW ){ fossil_print(" %s\n", db_column_text(&q,0)); } db_finalize(&q); } |
Added src/login.js.
> > > > > > | 1 2 3 4 5 6 | /* Javascript code to handle button actions on the login page */ var autofillButton = document.getElementById('autofillButton'); autofillButton.onclick = function(){ document.getElementById('u').value = 'anonymous'; document.getElementById('p').value = autofillButton.getAttribute('data-af'); }; |
Added src/lookslike.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 | /* ** Copyright (c) 2013 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 try to guess if a particular file is ** text or binary, what types of line endings it uses, is it UTF8 or ** UTF16, etc. */ #include "config.h" #include "lookslike.h" #include <assert.h> #if INTERFACE /* ** This macro is designed to return non-zero if the specified blob contains ** data that MAY be binary in nature; otherwise, zero will be returned. */ #define looks_like_binary(blob) \ ((looks_like_utf8((blob), LOOK_BINARY) & LOOK_BINARY) != LOOK_NONE) /* ** Output flags for the looks_like_utf8() and looks_like_utf16() routines used ** to convey status information about the blob content. */ #define LOOK_NONE ((int)0x00000000) /* Nothing special was found. */ #define LOOK_NUL ((int)0x00000001) /* One or more NUL chars were found. */ #define LOOK_CR ((int)0x00000002) /* One or more CR chars were found. */ #define LOOK_LONE_CR ((int)0x00000004) /* An unpaired CR char was found. */ #define LOOK_LF ((int)0x00000008) /* One or more LF chars were found. */ #define LOOK_LONE_LF ((int)0x00000010) /* An unpaired LF char was found. */ #define LOOK_CRLF ((int)0x00000020) /* One or more CR/LF pairs were found. */ #define LOOK_LONG ((int)0x00000040) /* An over length line was found. */ #define LOOK_ODD ((int)0x00000080) /* An odd number of bytes was found. */ #define LOOK_SHORT ((int)0x00000100) /* Unable to perform full check. */ #define LOOK_INVALID ((int)0x00000200) /* Invalid sequence was found. */ #define LOOK_BINARY (LOOK_NUL | LOOK_LONG | LOOK_SHORT) /* May be binary. */ #define LOOK_EOL (LOOK_LONE_CR | LOOK_LONE_LF | LOOK_CRLF) /* Line seps. */ #endif /* INTERFACE */ /* definitions for various UTF-8 sequence lengths, encoded as start value * and size of each valid range belonging to some lead byte*/ #define US2A 0x80, 0x01 /* for lead byte 0xC0 */ #define US2B 0x80, 0x40 /* for lead bytes 0xC2-0xDF */ #define US3A 0xA0, 0x20 /* for lead byte 0xE0 */ #define US3B 0x80, 0x40 /* for lead bytes 0xE1-0xEF */ #define US4A 0x90, 0x30 /* for lead byte 0xF0 */ #define US4B 0x80, 0x40 /* for lead bytes 0xF1-0xF3 */ #define US4C 0x80, 0x10 /* for lead byte 0xF4 */ #define US0A 0x00, 0x00 /* for any other lead byte */ /* a table used for quick lookup of the definition that goes with a * particular lead byte */ static const unsigned char lb_tab[] = { US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US2A, US0A, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US2B, US3A, US3B, US3B, US3B, US3B, US3B, US3B, US3B, US3B, US3B, US3B, US3B, US3B, US3B, US3B, US3B, US4A, US4B, US4B, US4B, US4C, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A, US0A }; /* ** 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): ** ** !LOOK_BINARY -- The content appears to consist entirely of text; however, ** the encoding may not be UTF-8. ** ** LOOK_BINARY -- The content appears to be binary because it contains one ** or more embedded NUL characters or an extremely long line. ** Since this function does not understand UTF-16, it may ** falsely consider UTF-16 text to be binary. ** ** Additional flags (i.e. those other than the ones included in LOOK_BINARY) ** may be present in the result as well; however, they should not impact the ** determination of text versus binary content. ** ************************************ WARNING ********************************** ** ** This function does not validate that the blob content is properly formed ** UTF-8. It assumes that all code points are the same size. It does not ** validate any code points. It makes no attempt to detect if any [invalid] ** switches between UTF-8 and other encodings occur. ** ** The only code points that this function cares about are the NUL character, ** carriage-return, and line-feed. ** ** This function examines the contents of the blob until one of the flags ** specified in "stopFlags" is set. ** ************************************ WARNING ********************************** */ int looks_like_utf8(const Blob *pContent, int stopFlags){ const char *z = blob_buffer(pContent); unsigned int n = blob_size(pContent); int j, c, flags = LOOK_NONE; /* Assume UTF-8 text, prove otherwise */ if( n==0 ) return flags; /* Empty file -> text */ c = *z; if( c==0 ){ flags |= LOOK_NUL; /* NUL character in a file -> binary */ }else if( c=='\r' ){ flags |= LOOK_CR; if( n<=1 || z[1]!='\n' ){ flags |= LOOK_LONE_CR; /* Not enough chars or next char not LF */ } } j = (c!='\n'); if( !j ) flags |= (LOOK_LF | LOOK_LONE_LF); /* Found LF as first char */ while( !(flags&stopFlags) && --n>0 ){ int c2 = c; c = *++z; ++j; if( c==0 ){ flags |= LOOK_NUL; /* NUL character in a file -> binary */ }else if( c=='\n' ){ flags |= LOOK_LF; if( c2=='\r' ){ flags |= (LOOK_CR | LOOK_CRLF); /* Found LF preceded by CR */ }else{ flags |= LOOK_LONE_LF; } if( j>LENGTH_MASK ){ flags |= LOOK_LONG; /* Very long line -> binary */ } j = 0; }else if( c=='\r' ){ flags |= LOOK_CR; if( n<=1 || z[1]!='\n' ){ flags |= LOOK_LONE_CR; /* Not enough chars or next char not LF */ } } } if( n ){ flags |= LOOK_SHORT; /* The whole blob was not examined */ } 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. This function ** also considers valid the derivatives CESU-8 & WTF-8 (as described in ** the same wikipedia article referenced previously). For UTF-8 characters ** > 0x7f, the variable 'c' not necessary means the real lead byte. ** It's number of higher 1-bits indicate the number of continuation ** bytes that are expected to be followed. E.g. when 'c' has a value ** in the range 0xc0..0xdf it means that after 'c' a single continuation ** byte is expected. A value 0xe0..0xef means that after 'c' two more ** continuation bytes are 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; /* lead byte to be handled. */ if( n==0 ) return 0; /* Empty file -> OK */ c = *z; while( --n>0 ){ if( c>=0x80 ){ const unsigned char *def; /* pointer to range table*/ c <<= 1; /* multiply by 2 and get rid of highest bit */ def = &lb_tab[c]; /* search fb's valid range in table */ if( (unsigned int)(*++z-def[0])>=def[1] ){ return LOOK_INVALID; /* Invalid UTF-8 */ } c = (c>=0xC0) ? (c|3) : ' '; /* determine next lead byte */ } else { c = *++z; } } return (c>=0x80) ? LOOK_INVALID : 0; /* Final lead 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 # else # define WCHAR_T unsigned short # endif #endif /* ** Maximum length of a line in a text file, in UTF-16 characters. (4096) ** The number of bytes represented by this value cannot exceed LENGTH_MASK ** bytes, because that is the line buffer size used by the diff engine. */ #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): ** ** !LOOK_BINARY -- The content appears to consist entirely of text; however, ** the encoding may not be UTF-16. ** ** LOOK_BINARY -- The content appears to be binary because it contains one ** or more embedded NUL characters or an extremely long line. ** Since this function does not understand UTF-8, it may ** falsely consider UTF-8 text to be binary. ** ** Additional flags (i.e. those other than the ones included in LOOK_BINARY) ** may be present in the result as well; however, they should not impact the ** determination of text versus binary content. ** ************************************ WARNING ********************************** ** ** This function does not validate that the blob content is properly formed ** UTF-16. It assumes that all code points are the same size. It does not ** validate any code points. It makes no attempt to detect if any [invalid] ** switches between the UTF-16be and UTF-16le encodings occur. ** ** The only code points that this function cares about are the NUL character, ** carriage-return, and line-feed. ** ** This function examines the contents of the blob until one of the flags ** specified in "stopFlags" is set. ** ************************************ 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; /* Not enough chars or next char 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 */ }else if( c=='\n' ){ flags |= LOOK_LF; if( c2=='\r' ){ flags |= (LOOK_CR | LOOK_CRLF); /* Found LF preceded by CR */ }else{ flags |= LOOK_LONE_LF; } if( j>UTF16_LENGTH_MASK ){ flags |= LOOK_LONG; /* Very long line -> binary */ } j = 0; }else if( c=='\r' ){ flags |= LOOK_CR; if( n<(2*sizeof(WCHAR_T)) || UTF16_SWAP_IF(bReverse, z[1])!='\n' ){ flags |= LOOK_LONE_CR; /* Not enough chars or next char not LF */ } } } if( n ){ flags |= LOOK_SHORT; /* The whole blob was not examined */ } if( j>UTF16_LENGTH_MASK ){ flags |= LOOK_LONG; /* Very long line -> binary */ } return flags; } /* ** 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 ** byte-order-mark (BOM). */ int starts_with_utf8_bom(const Blob *pContent, int *pnByte){ const char *z = blob_buffer(pContent); int bomSize = 0; const unsigned char *bom = get_utf8_bom(&bomSize); if( pnByte ) *pnByte = bomSize; if( (int)blob_size(pContent)<bomSize ) return 0; return memcmp(z, bom, bomSize)==0; } /* ** This function returns non-zero if the blob starts with a UTF-16 ** byte-order-mark (BOM), either in the endianness of the machine ** or in reversed byte order. The UTF-32 BOM is ruled out by checking ** if the UTF-16 BOM is not immediately followed by (utf16) 0. ** pnByte is only set when the function returns 1. ** ** pbReverse is always set, even when no BOM is found. Without a BOM, ** it is set to 1 on little-endian and 0 on big-endian platforms. See ** clause D98 of conformance (section 3.10) of the Unicode standard. */ int starts_with_utf16_bom( const Blob *pContent, /* IN: Blob content to perform BOM detection on. */ int *pnByte, /* OUT: The number of bytes used for the BOM. */ int *pbReverse /* OUT: Non-zero for BOM in reverse byte-order. */ ){ const unsigned char *z = (unsigned char *)blob_buffer(pContent); int bomSize = sizeof(unsigned short); int size = blob_size(pContent); unsigned short i0; if( size<bomSize ) goto noBom; /* No: cannot read BOM. */ if( size>=(2*bomSize) && z[2]==0 && z[3]==0 ) goto noBom; memcpy(&i0, z, sizeof(i0)); if( i0==0xfeff ){ if( pbReverse ) *pbReverse = 0; }else if( i0==0xfffe ){ if( pbReverse ) *pbReverse = 1; }else{ static const int one = 1; noBom: if( pbReverse ) *pbReverse = *(char *) &one; return 0; /* No: UTF-16 byte-order-mark not found. */ } if( pnByte ) *pnByte = bomSize; return 1; /* Yes. */ } /* ** Returns non-zero if the specified content could be valid UTF-16. */ int could_be_utf16(const Blob *pContent, int *pbReverse){ return (blob_size(pContent) % sizeof(WCHAR_T) == 0) ? starts_with_utf16_bom(pContent, 0, pbReverse) : 0; } /* ** COMMAND: test-looks-like-utf ** ** Usage: %fossil test-looks-like-utf FILENAME ** ** Options: ** -n|--limit N Repeat looks-like function N 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], ExtFILE); 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"); fossil_print("Has flag LOOK_CR: %s\n",(lookFlags&LOOK_CR)?"yes":"no"); fossil_print("Has flag LOOK_LONE_CR: %s\n", (lookFlags&LOOK_LONE_CR)?"yes":"no"); fossil_print("Has flag LOOK_LF: %s\n",(lookFlags&LOOK_LF)?"yes":"no"); fossil_print("Has flag LOOK_LONE_LF: %s\n", (lookFlags&LOOK_LONE_LF)?"yes":"no"); fossil_print("Has flag LOOK_CRLF: %s\n",(lookFlags&LOOK_CRLF)?"yes":"no"); fossil_print("Has flag LOOK_LONG: %s\n",(lookFlags&LOOK_LONG)?"yes":"no"); fossil_print("Has flag LOOK_INVALID: %s\n", (lookFlags&LOOK_INVALID)?"yes":"no"); fossil_print("Has flag LOOK_ODD: %s\n",(lookFlags&LOOK_ODD)?"yes":"no"); fossil_print("Has flag LOOK_SHORT: %s\n",(lookFlags&LOOK_SHORT)?"yes":"no"); blob_reset(&blob); } /* ** Return true if z[i] is the whole word given by zWord in a context that ** might be an attempted SQL injection. */ static int isWholeWord(const char *z, unsigned int i, const char *zWord, int n){ if( i==0 ) return 0; if( sqlite3_strnicmp(z+i, zWord, n)!=0 ) return 0; if( fossil_isalnum(z[i-1]) ) return 0; if( fossil_isalnum(z[i+n]) ) return 0; if( strchr("-)_", z[i-1])!=0 ) return 0; if( strchr("(_", z[i+n])!=0 ) return 0; return 1; } /* ** Returns true if the given text contains certain keywords or ** punctuation which indicate that it might be an SQL injection attempt ** or some other kind of mischief. ** ** This is not a defense against vulnerabilities in the Fossil code. ** Rather, this is part of an effort to do early detection of malicious ** spiders to avoid them using up too many CPU cycles. */ int looks_like_sql_injection(const char *zTxt){ unsigned int i; if( zTxt==0 ) return 0; for(i=0; zTxt[i]; i++){ switch( zTxt[i] ){ case ';': case '\'': return 1; case '/': /* 0123456789 123456789 */ if( strncmp(zTxt+i+1, "/wp-content/plugins/", 20)==0 ) return 1; if( strncmp(zTxt+i+1, "/wp-admin/admin-ajax", 20)==0 ) return 1; break; case 'a': case 'A': if( isWholeWord(zTxt, i, "and", 3) ) return 1; break; case 'n': case 'N': if( isWholeWord(zTxt, i, "null", 4) ) return 1; break; case 'o': case 'O': if( isWholeWord(zTxt, i, "order", 5) && fossil_isspace(zTxt[i+5]) ){ return 1; } if( isWholeWord(zTxt, i, "or", 2) ) return 1; break; case 's': case 'S': if( isWholeWord(zTxt, i, "select", 6) ) return 1; break; case 'w': case 'W': if( isWholeWord(zTxt, i, "waitfor", 7) ) return 1; break; } } return 0; } /* ** This is a utility routine associated with the test-looks-like-sql-injection ** command. ** ** Read input from zInFile and print only those lines that look like they ** might be SQL injection. ** ** Or if bInvert is true, then show the opposite - those lines that do NOT ** look like SQL injection. */ static void show_sql_injection_lines( const char *zInFile, /* Name of input file */ int bInvert, /* Invert the sense of the output (-v) */ int bDeHttpize /* De-httpize the inputs. (-d) */ ){ FILE *in; char zLine[10000]; if( zInFile==0 || strcmp(zInFile,"-")==0 ){ in = stdin; }else{ in = fopen(zInFile, "rb"); if( in==0 ){ fossil_fatal("cannot open \"%s\" for reading\n", zInFile); } } while( fgets(zLine, sizeof(zLine), in) ){ dehttpize(zLine); if( (looks_like_sql_injection(zLine)!=0) ^ bInvert ){ fossil_print("%s", zLine); } } if( in!=stdin ) fclose(in); } /* ** COMMAND: test-looks-like-sql-injection ** ** Read lines of input from files named as arguments (or from standard ** input if no arguments are provided) and print those that look like they ** might be part of an SQL injection attack. ** ** Used to test the looks_lide_sql_injection() utility subroutine, possibly ** by piping in actual server log data. */ void test_looks_like_sql_injection(void){ int i; int bInvert = find_option("invert","v",0)!=0; int bDeHttpize = find_option("dehttpize","d",0)!=0; verify_all_options(); if( g.argc==2 ){ show_sql_injection_lines(0, bInvert, bDeHttpize); } for(i=2; i<g.argc; i++){ show_sql_injection_lines(g.argv[i], bInvert, bDeHttpize); } } |
Changes to src/main.c.
1 2 3 4 5 6 | /* ** 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".) | | > > > > > > > > > > > > > > > > > | | > > > > > > > > > | < | < > > > > > | > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > | > > | > > | | > | > | > > > > > > > > > > > | > | > > > > | > > > > | | < | < < | < | < | < < < | | > > > > > > > > > > > > > > | | > | | < | | | | | | | < | < < < < < < < < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | < > > > > > > > > > | | > | > > > | > > | < < | | | | < | | > | | | | > | < < | < > | | < < < < | > > > > > | | < < > > > > | < < | | < | | < < < | < < > > | | | | > > > > | > > > > > > > > | < < > < < | | | < < | > > | | | | > > > | > > > | < | < < | < < < < | | < < < < < < < < < < < < < < < < < | < < < < | | < < > > < > > > | < > | > > | > > > > | > | < > | < < | > > | | | | | | > > | | > | | < < < | | | < > | < > | < > | | | | | < > | < < < < < | < < | | > > > > > > > | | > > > > | | | < < < < | | > | > > | | | > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | | > > | > > > > | > > > | > > > | < | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > | | | | > < > | < > > | < < < < < | | < < > > > > > > > > | > > | | | > | > > | > > > > > > > > > > > > > > > > > > | > > | < > > > > > > > | < > | < > | | < > | | | | | | > > | < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > | < < < < < | < < < < < < > > > | > > > > > > > | > > > > > > > > | > | | | < < < < < | < < < < < | < | < > | > > > | < < < < < | > | < < > | | < < > > > > > | | | | > > > > | | > | > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | | | > > > > > > > | | > > | > | > | > | > > > > | > > > > | > > > > | | | > | | | > | > | | > | | > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | > > > > | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > < > > | > > | | | > | > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > | > > > > > | < | > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | | | < < | | | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | | > > > | > > > | | > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > | > > > > > > > > > | | > > > > > > > > > > > > | | > | > | > | > > > > > > > > > > > | > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | < < > | | > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > > > > | < < < < < > < < < < < < < < | | < > > > > > > > > > > > | | < | > > > | < < < > | < < < > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > | | | | | | | | | > > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > < < < < < < | | | | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > | < < | > > > > > > > > > > > | > > > > > > > > | | | > | > > > > > > | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > | | | < < > | > | < < | | > > | < < < < | > > | < < > > > > > > > > > > > > > > > > > | > | | | | | | > | > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | > > > > > > > > > | > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > < < | > > | | | | | > > > > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > | > > | | | | > | | < > > | > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | | | > > > | > > > > > > > > | > > > | > > > > > > > > > > > | | < < > < > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** 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 module codes the main() procedure that runs first when the ** program is invoked. */ #include "VERSION.h" #include "config.h" #if defined(_WIN32) # include <windows.h> # include <io.h> # define isatty(h) _isatty(h) # define GETPID (int)GetCurrentProcessId #endif /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */ #if USE_SEE #if defined(_WIN32) typedef DWORD PID_T; #else typedef pid_t PID_T; #endif #endif #include "main.h" #include <string.h> #include <time.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> /* atexit() */ #include <zlib.h> #if !defined(_WIN32) # include <errno.h> /* errno global */ # include <unistd.h> # include <signal.h> # define GETPID getpid #endif #ifdef FOSSIL_ENABLE_SSL # include "openssl/crypto.h" #endif #if INTERFACE #ifdef FOSSIL_ENABLE_TCL # include "tcl.h" #endif #ifdef FOSSIL_ENABLE_JSON # include "cson_amalgamation.h" /* JSON API. */ # include "json_detail.h" #endif #ifdef HAVE_BACKTRACE # include <execinfo.h> #endif /* ** Default length of a timeout for serving an HTTP request. Changable ** using the "--timeout N" command-line option or via "timeout: N" in the ** CGI script. */ #ifndef FOSSIL_DEFAULT_TIMEOUT # define FOSSIL_DEFAULT_TIMEOUT 600 /* 10 minutes */ #endif /* ** Maximum number of auxiliary parameters on reports */ #define MX_AUX 5 /* ** Holds flags for fossil user permissions. */ struct FossilUserPerms { char Setup; /* s: use Setup screens on web interface */ char Admin; /* a: administrative permission */ 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) */ char RdTkt; /* r: view tickets via web */ char NewTkt; /* n: create new tickets */ char ApndTkt; /* c: append to tickets via the web */ char WrTkt; /* w: make changes to tickets via web */ char ModTkt; /* q: approve and publish ticket changes (Moderator) */ char Attach; /* b: add attachments */ char TktFmt; /* t: create new ticket report formats */ char RdAddr; /* e: read email addresses or other private data */ char Zip; /* z: download zipped artifact via /zip URL */ char Private; /* x: can send and receive private content */ char WrUnver; /* y: can push unversioned content */ char RdForum; /* 2: Read forum posts */ char WrForum; /* 3: Create new forum posts */ char WrTForum; /* 4: Post to forums not subject to moderation */ char ModForum; /* 5: Moderate (approve or reject) forum posts */ char AdminForum; /* 6: Grant capability 4 to other users */ char EmailAlert; /* 7: Sign up for email notifications */ char Announce; /* A: Send announcements */ char Chat; /* C: read or write the chatroom */ char Debug; /* D: show extra Fossil debugging features */ /* These last two are included to block infinite recursion */ char XReader; /* u: Inherit all privileges of "reader" */ char XDeveloper; /* v: Inherit all privileges of "developer" */ }; #ifdef FOSSIL_ENABLE_TCL /* ** All Tcl related context information is in this structure. This structure ** definition has been copied from and should be kept in sync with the one in ** "th_tcl.c". */ struct TclContext { int argc; /* Number of original (expanded) arguments. */ char **argv; /* Full copy of the original (expanded) arguments. */ void *hLibrary; /* The Tcl library module handle. */ void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */ void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */ void *xDeleteInterp; /* See tcl_DeleteInterpProc in th_tcl.c. */ void *xFinalize; /* See tcl_FinalizeProc in th_tcl.c. */ Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ int useObjProc; /* Non-zero if an objProc can be called directly. */ int useTip285; /* Non-zero if TIP #285 is available. */ char *setup; /* The optional Tcl setup script. */ 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 **argvOrig; /* Original g.argv prior to removing options */ char *nameOfExe; /* Full path of executable. */ const char *zErrlog; /* Log errors to this file, if not NULL */ const char *zPhase; /* Phase of operation, for use by the error log ** and for deriving $canonical_page TH1 variable */ 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 dbIgnoreErrors; /* Ignore database errors if true */ 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 */ unsigned iRepoDataVers; /* Initial data version for repository database */ char *zRepositoryOption; /* Most recent cached repository option value */ char *zRepositoryName; /* Name of the repository database file */ char *zLocalDbName; /* Name of the local database file */ char *zOpenRevision; /* Check-in version to use during database open */ const char *zCmdName; /* Name of the Fossil command currently running */ 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 hash */ int eHashPolicy; /* Current hash policy. One of HPOLICY_* */ 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 fCgiTrace; /* True if --cgitrace is enabled */ int fQuiet; /* True if -quiet flag is present */ int fJail; /* True if running with a chroot jail */ int fHttpTrace; /* Trace outbound HTTP requests */ int fAnyTrace; /* Any kind of tracing */ int fAllowACME; /* Deliver files from .well-known */ 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 */ int fNoHttpCompress; /* Do not compress HTTP traffic (for debugging) */ char *zSshCmd; /* SSH command string */ const char *zHttpCmd; /* External program to do HTTP requests */ int fNoSync; /* Do not do an autosync ever. --nosync */ int fIPv4; /* Use only IPv4, not IPv6. --ipv4 */ char *zPath; /* Name of webpage being served (may be NULL) */ 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 */ int nExtraURL; /* Extra bytes added to SCRIPT_NAME */ const char *zExtRoot; /* Document root for the /ext sub-website */ 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; /* 0: command-line 1: CGI. 2: after 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 httpUseSSL; /* True to use an SSL codec for HTTP traffic */ void *httpSSLConn; /* The SSL connection */ 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 */ char *ckinLockFail; /* Check-in lock failure received from server */ 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 jsHref; /* If true, set href= using javascript, 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 *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */ const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */ const char *zSSLIdentity; /* Value of --ssl-identity option, filename of ** SSL client identity */ const char *zCgiFile; /* Name of the CGI file */ const char *zReqType; /* Type of request: "HTTP", "CGI", "SCGI" */ #if USE_SEE const char *zPidKey; /* Saved value of the --usepidkey option. Only * applicable when using SEE on Windows or Linux. */ #endif 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, should be ** accessed through get_comment_format(). */ /* 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[16]; /* Value of the anti-CSRF token */ int okCsrf; /* -1: unsafe ** 0: unknown ** 1: same origin ** 2: same origin + is POST ** 3: same origin, POST, valid csrf token */ 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 */ const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */ char *azAuxParam[MX_AUX]; /* Param of each aux() or option() value */ const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */ const char **azAuxOpt[MX_AUX]; /* Options of each option() value */ int anAuxCols[MX_AUX]; /* Number of columns for option() values */ int allowSymlinks; /* Cached "allow-symlinks" option */ int mainTimerId; /* Set to fossil_timer_start() */ int nPendingRequest; /* # of HTTP requests in "fossil server" */ int nRequest; /* Total # of HTTP request */ int bAvoidDeltaManifests; /* Avoid using delta manifests if true */ #ifdef FOSSIL_ENABLE_JSON struct FossilJsonBits { int isJsonMode; /* True if running in JSON mode, else false. This changes how errors are reported. In JSON mode we try to always output JSON-form error responses and always (in CGI mode) exit() with code 0 to avoid an HTTP 500 error. */ int preserveRc; /* Do not convert error codes into 0. * This is primarily intended for use * by the test suite. */ 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 */ int ftntsIssues[4]; /* Counts for misref, strayed, joined, overnested */ int diffCnt[3]; /* Counts for DIFF_NUMSTAT: files, ins, del */ }; /* ** Macro for debugging: */ #define CGIDEBUG(X) if( g.fDebug ) cgi_debug X #endif Global g; /* ** atexit() handler which frees up "some" of the resources ** used by fossil. */ static void fossil_atexit(void) { static int once = 0; if( once++ ) return; /* Ensure that this routine only runs once */ #if USE_SEE /* ** Zero, unlock, and free the saved database encryption key now. */ db_unsave_encryption_key(); #endif #if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)) /* ** Free the secure getpass() buffer now. */ freepass(); #endif #if defined(_WIN32) && !defined(_WIN64) && defined(FOSSIL_ENABLE_TCL) && \ defined(USE_TCL_STUBS) /* ** If Tcl is compiled on Windows using the latest MinGW, Fossil can crash ** when exiting while a stubs-enabled Tcl is still loaded. This is due to ** a bug in MinGW, see: ** ** http://comments.gmane.org/gmane.comp.gnu.mingw.user/41724 ** ** The workaround is to manually unload the loaded Tcl library prior to ** exiting the process. This issue does not impact 64-bit Windows. */ unloadTcl(g.interp, &g.tcl); #endif #ifdef FOSSIL_ENABLE_JSON cson_value_free(g.json.gc.v); memset(&g.json, 0, sizeof(g.json)); #endif free(g.zErrMsg); if(g.db){ db_close(0); } manifest_clear_cache(); content_clear_cache(1); rebuild_clear_cache(); /* ** 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; } #if defined(TH_MEMDEBUG) if( Th_GetOutstandingMalloc()!=0 ){ fossil_print("Th_GetOutstandingMalloc() => %d\n", Th_GetOutstandingMalloc()); } assert( Th_GetOutstandingMalloc()==0 ); #endif } } /* ** 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 ** (3) Use the contents of FILE to replace the two removed arguments: ** (a) Ignore blank lines in the file ** (b) Each non-empty line of the file is an argument, except ** (c) If the line begins with "-" and contains a space, it is broken ** into two arguments at the space. */ void expand_args_option(int argc, void *argv){ 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 */ unsigned int nArg; /* Number of new arguments */ char *z; /* General use string pointer */ char **newArgv; /* New expanded g.argv under construction */ const char *zFileName; /* input file name */ FILE *inFile; /* input FILE */ g.argc = argc; g.argv = argv; sqlite3_initialize(); #if defined(_WIN32) && defined(BROKEN_MINGW_CMDLINE) for(i=0; (int)i<g.argc; i++) g.argv[i] = fossil_mbcs_to_utf8(g.argv[i]); #else for(i=0; (int)i<g.argc; i++) g.argv[i] = fossil_path_to_utf8(g.argv[i]); #endif g.nameOfExe = file_fullexename(g.argv[0]); for(i=1; (int)i<g.argc-1; i++){ z = g.argv[i]; if( z[0]!='-' ) continue; z++; if( z[0]=='-' ) z++; /* Maintenance reminder: we do not stop at a "--" flag here, ** instead delegating that to find_option(). Doing it here ** introduces some weird corner cases, as covered in forum thread ** 4382bbc66757c39f. e.g. (fossil -U -- --args ...) is handled ** differently when we stop at "--" here. */ if( fossil_strcmp(z, "args")==0 ) break; } if( (int)i>=g.argc-1 ){ g.argvOrig = fossil_malloc( sizeof(char*)*(g.argc+1) ); memcpy(g.argvOrig, g.argv, sizeof(g.argv[0])*(g.argc+1)); return; } zFileName = g.argv[i+1]; if( strcmp(zFileName,"-")==0 ){ inFile = stdin; }else if( !file_isfile(zFileName, ExtFILE) ){ fossil_fatal("Not an ordinary file: \"%s\"", zFileName); }else{ inFile = fossil_fopen(zFileName,"rb"); if( inFile==0 ){ fossil_fatal("Cannot open -args file [%s]", zFileName); } } blob_read_from_channel(&file, inFile, -1); if(stdin != inFile){ fclose(inFile); } inFile = NULL; blob_to_utf8_no_bom(&file, 1); z = blob_str(&file); for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++; if( nLine>100000000 ) fossil_fatal("too many command-line arguments"); nArg = g.argc + nLine*2; newArgv = fossil_malloc( sizeof(char*)*nArg*2 + 2); for(j=0; j<i; j++) newArgv[j] = g.argv[j]; blob_rewind(&file); while( nLine-->0 && (n = blob_line(&file, &line))>0 ){ /* Reminder: ^^^ nLine check avoids that embedded NUL bytes in the ** --args file causes nLine to be less than blob_line() will end ** up reporting, as such a miscount leads to an illegal memory ** write. See forum post ** https://fossil-scm.org/forum/forumpost/7b34eecc1b8c for ** details */ if( n<1 ){ /* Reminder: corner-case: a line with 1 byte and no newline. */ continue; } z = blob_buffer(&line); if('\n'==z[n-1]){ z[n-1] = 0; } if((n>1) && ('\r'==z[n-2])){ if(n==2) continue /*empty line*/; z[n-2] = 0; } if(!z[0]) continue; if( j>=nArg ){ fossil_fatal("malformed command-line arguments"); } newArgv[j++] = z; if( z[0]=='-' ){ for(k=1; z[k] && !fossil_isspace(z[k]); k++){} if( z[k] ){ z[k] = 0; k++; if( z[k] ) newArgv[j++] = &z[k]; } } } i += 2; while( (int)i<g.argc ) newArgv[j++] = g.argv[i++]; newArgv[j] = 0; g.argc = j; g.argv = newArgv; g.argvOrig = &g.argv[j+1]; memcpy(g.argvOrig, g.argv, sizeof(g.argv[0])*(j+1)); } #ifdef FOSSIL_ENABLE_TCL /* ** Make a deep copy of the provided argument array and return it. */ static char **copy_args(int argc, char **argv){ char **zNewArgv; int i; zNewArgv = fossil_malloc( sizeof(char*)*(argc+1) ); memset(zNewArgv, 0, sizeof(char*)*(argc+1)); for(i=0; i<argc; i++){ 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){ sqlite3_stmt *p; Blob msg; #ifdef __APPLE__ /* Disable the file alias warning on apple products because Time Machine ** creates lots of aliases and the warnings alarm people. */ if( iCode==SQLITE_WARNING ) return; #endif #ifndef FOSSIL_DEBUG /* Disable the automatic index warning except in FOSSIL_DEBUG builds. */ if( iCode==SQLITE_WARNING_AUTOINDEX ) return; #endif if( iCode==SQLITE_SCHEMA ) return; if( g.dbIgnoreErrors ) return; #ifdef SQLITE_READONLY_DIRECTORY if( iCode==SQLITE_READONLY_DIRECTORY ){ zErrmsg = "database is in a read-only directory"; } #endif blob_init(&msg, 0, 0); blob_appendf(&msg, "%s(%d): %s", fossil_sqlite_return_code_name(iCode), iCode, zErrmsg); if( g.db ){ for(p=sqlite3_next_stmt(g.db, 0); p; p=sqlite3_next_stmt(g.db,p)){ const char *zSql; if( !sqlite3_stmt_busy(p) ) continue; zSql = sqlite3_sql(p); if( zSql==0 ) continue; blob_appendf(&msg, "\nSQL: %s", zSql); } } fossil_warning("%s", blob_str(&msg)); blob_reset(&msg); } /* ** 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==0 ){ zValue = find_option("comment-format", 0, 1); } if( zValue ){ g.comFmtFlags = atoi(zValue); }else{ g.comFmtFlags = COMMENT_PRINT_UNSET; /* Command-line option not found. */ } } /* ** Check to see if the Fossil binary contains an appended repository ** file using the appendvfs extension. If so, change command-line arguments ** to cause Fossil to launch with "fossil ui" on that repo. */ static int fossilExeHasAppendedRepo(void){ extern int deduceDatabaseType(const char*,int); if( 2==deduceDatabaseType(g.nameOfExe,0) ){ static char *azAltArgv[] = { 0, "ui", 0, 0 }; azAltArgv[0] = g.nameOfExe; azAltArgv[2] = g.nameOfExe; g.argv = azAltArgv; g.argc = 3; return 1; }else{ return 0; } } /* ** This procedure runs first. */ #if defined(FOSSIL_FUZZ) /* Do not include a main() procedure when building for fuzz testing. ** libFuzzer will supply main(). */ #elif 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){ return fossil_main(argc,(char**)argv); } #elif defined(_WIN32) int _CRT_glob = 0x0001; /* See MinGW bug #2062 */ int main(int argc, char **argv){ return fossil_main(argc, argv); } #else int main(int argc, char **argv){ return fossil_main(argc, argv); } #endif /* All the work of main() is done by a separate procedure "fossil_main()". ** We have to break this out, because fossil_main() is sometimes called ** separately (by the "shell" command) but we do not want atwait() handlers ** being called by separate invocations of fossil_main(). */ int fossil_main(int argc, char **argv){ const char *zCmdName = "unknown"; const CmdOrPage *pCmd = 0; int rc; g.zPhase = "init"; #if !defined(_WIN32_WCE) if( fossil_getenv("FOSSIL_BREAK") ){ if( isatty(0) && isatty(2) ){ fprintf(stderr, "attach debugger to process %d and press any key to continue.\n", GETPID()); fgetc(stdin); }else{ #if defined(_WIN32) || defined(WIN32) DebugBreak(); #elif defined(SIGTRAP) raise(SIGTRAP); #endif } } #endif fossil_printf_selfcheck(); fossil_limit_memory(1); /* When updating the minimum SQLite version, change the number here, ** and also MINIMUM_SQLITE_VERSION value set in ../auto.def. Take ** care that both places agree! */ if( sqlite3_libversion_number()<3043000 || strncmp(sqlite3_sourceid(),"2023-06-12",10)<0 ){ fossil_panic("Unsuitable SQLite version %s, must be at least 3.43.0", 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 One problem we have here is that this code is needed before the db is opened, so we can't sql for it.*/; #else g.json.errorDetailParanoia = 0; #endif g.json.outOpt = cson_output_opt_empty; g.json.outOpt.addNewline = 1; g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */; #endif /* FOSSIL_ENABLE_JSON */ 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( !find_option("nocgi", 0, 0) && fossil_getenv("GATEWAY_INTERFACE")!=0){ zCmdName = "cgi"; g.isHTTP = 1; }else if( g.argc<2 && !fossilExeHasAppendedRepo() ){ fossil_print( "Usage: %s COMMAND ...\n" " or: %s help -- for a list of common commands\n" " or: %s help COMMAND -- for help with the named command\n", g.argv[0], g.argv[0], g.argv[0]); fossil_print( "\nCommands and filenames may be passed on to fossil from a file\n" "by using:\n" "\n %s --args FILENAME ...\n", g.argv[0] ); fossil_print( "\nEach line of the file is assumed to be a filename unless it starts\n" "with '-' and contains a space, in which case it is assumed to be\n" "another flag and is treated as such. --args FILENAME may be used\n" "in conjunction with any other flags.\n"); fossil_exit(1); }else{ const char *zChdir = find_option("chdir",0,1); g.isHTTP = 0; g.rcvid = 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.fCgiTrace = find_option("cgitrace", 0, 0)!=0; g.fSshClient = 0; g.zSshCmd = 0; if( g.fSqlTrace ) g.fSqlStats = 1; #ifdef FOSSIL_ENABLE_JSON g.json.preserveRc = find_option("json-preserve-rc", 0, 0)!=0; #endif 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.fCgiTrace; 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 USE_SEE db_maybe_handle_saved_encryption_key_for_process(SEE_KEY_READ); #endif if( find_option("help",0,0)!=0 ){ /* If --help is found anywhere on the command line, translate the command * to "fossil help cmdname" where "cmdname" is the first argument that * does not begin with a "-" character. If all arguments start with "-", * translate to "fossil help argv[1] argv[2]...". */ int i, nNewArgc; char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+3) ); zNewArgv[0] = g.argv[0]; zNewArgv[1] = "help"; zNewArgv[2] = "-c"; for(i=1; i<g.argc; i++){ if( g.argv[i][0]!='-' ){ nNewArgc = 4; zNewArgv[3] = g.argv[i]; zNewArgv[4] = 0; break; } } if( i==g.argc ){ for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i]; nNewArgc = g.argc+1; zNewArgv[i+1] = 0; } g.argc = nNewArgc; g.argv = zNewArgv; #if 0 }else if( g.argc==2 && file_is_repository(g.argv[1]) ){ char **zNewArgv = fossil_malloc( sizeof(char*)*4 ); zNewArgv[0] = g.argv[0]; zNewArgv[1] = "ui"; zNewArgv[2] = g.argv[1]; zNewArgv[3] = 0; g.argc = 3; g.argv = zNewArgv; #endif } 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_panic("file descriptor 2 is not open. (fd=%d, errno=%d)", fd, x); } } #endif g.zCmdName = zCmdName; rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); if( rc==1 && g.argc==2 && file_is_repository(g.argv[1]) ){ /* If the command-line is "fossil ABC" and "ABC" is no a valid command, ** but "ABC" is the name of a repository file, make the command be ** "fossil ui ABC" instead. */ char **zNewArgv = fossil_malloc( sizeof(char*)*4 ); zNewArgv[0] = g.argv[0]; zNewArgv[1] = "ui"; zNewArgv[2] = g.argv[1]; zNewArgv[3] = 0; g.argc = 3; g.argv = zNewArgv; g.zCmdName = zCmdName = "ui"; rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); } 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", 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 ){ Blob couldbe; blob_init(&couldbe,0,0); dispatch_matching_names(zCmdName, CMDFLAG_COMMAND, &couldbe); 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); } #ifdef FOSSIL_ENABLE_JSON else if( rc==0 && strcmp("json",pCmd->zName)==0 ){ g.json.isJsonMode = 1; }else{ assert(!g.json.isJsonMode && "JSON-mode misconfiguration."); } #endif 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 skipped, 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(pCmd->zName, pCmd->eCmdFlags); }else{ rc = TH_OK; } if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ if( rc==TH_OK || rc==TH_RETURN ){ #endif g.zPhase = pCmd->zName; pCmd->xFunc(); g.zPhase = "shutdown"; #ifdef FOSSIL_ENABLE_TH1_HOOKS } if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags); } } #endif fossil_exit(0); /*NOT_REACHED*/ return 0; } /* ** Print a usage comment and quit */ void usage(const char *zFormat){ fossil_fatal("Usage: %s %s %s", g.argv[0], g.argv[1], zFormat); } /* ** Remove n elements from g.argv beginning with the i-th element. */ static void remove_from_argv(int i, int n){ int j; for(j=i+n; j<g.argc; i++, j++){ g.argv[i] = g.argv[j]; } g.argc = i; } /* ** Look for a command-line option. If present, remove it from the ** argument list and return a pointer to either the flag's name (if ** hasArg==0), sans leading - or --, or its value (if hasArg==1). ** Return NULL if the flag is not found. ** ** zLong is the "long" form of the flag and zShort is the ** short/abbreviated form (typically a single letter, but it may be ** longer). zLong must not be NULL, but zShort may be. ** ** hasArg==0 means the option is a flag. It is either present or not. ** hasArg==1 means the option has an argument, in which case a pointer ** to the argument's value is returned. For zLong, a flag value (if ** hasValue==1) may either be in the form (--flag=value) or (--flag ** value). For zShort, only the latter form is accepted. ** ** If a standalone argument of "--" is encountered in the argument ** list while searching for the given flag(s), this routine stops ** searching and NULL is returned. */ const char *find_option(const char *zLong, const char *zShort, int hasArg){ int i; int nLong; const char *zReturn = 0; assert( hasArg==0 || hasArg==1 ); nLong = strlen(zLong); for(i=1; i<g.argc; i++){ char *z; z = g.argv[i]; if( z[0]!='-' ) continue; z++; if( z[0]=='-' ){ if( z[1]==0 ){ /* Stop processing at "--" without consuming it. verify_all_options() will consume this flag. */ break; } z++; } if( strncmp(z,zLong,nLong)==0 ){ if( hasArg && z[nLong]=='=' ){ zReturn = &z[nLong+1]; remove_from_argv(i, 1); break; }else if( z[nLong]==0 ){ if( i+hasArg >= g.argc ) break; zReturn = g.argv[i+hasArg]; remove_from_argv(i, 1+hasArg); break; } }else if( fossil_strcmp(z,zShort)==0 ){ if( i+hasArg >= g.argc ) break; zReturn = g.argv[i+hasArg]; remove_from_argv(i, 1+hasArg); break; } } return zReturn; } /* Return true if zOption exists in the command-line arguments, ** but do not remove it from the list or otherwise process it. */ int has_option(const char *zOption){ int i; int n = (int)strlen(zOption); for(i=1; i<g.argc; i++){ char *z = g.argv[i]; if( z[0]!='-' ) continue; z++; if( z[0]=='-' ){ if( z[1]==0 ){ /* Stop processing at "--" */ break; } z++; } if( strncmp(z,zOption,n)==0 && (z[n]==0 || z[n]=='=') ) return 1; } return 0; } /* ** Look for multiple occurrences of a command-line option with the ** corresponding argument. ** ** Return a malloc allocated array of pointers to the arguments. ** ** pnUsedArgs is used to store the number of matched arguments. ** ** Caller is responsible for freeing allocated memory by passing the ** head of the array (not each entry) to fossil_free(). (The ** individual entries have the same lifetime as values returned from ** find_option().) */ const char **find_repeatable_option( const char *zLong, const char *zShort, int *pnUsedArgs ){ const char *zOption; const char **pzArgs = 0; int nAllocArgs = 0; int nUsedArgs = 0; while( (zOption = find_option(zLong, zShort, 1))!=0 ){ if( pzArgs==0 && nAllocArgs==0 ){ nAllocArgs = 1; pzArgs = fossil_malloc( nAllocArgs*sizeof(pzArgs[0]) ); }else if( nAllocArgs<=nUsedArgs ){ nAllocArgs = nAllocArgs*2; pzArgs = fossil_realloc( (void *)pzArgs, nAllocArgs*sizeof(pzArgs[0]) ); } pzArgs[nUsedArgs++] = zOption; } *pnUsedArgs = nUsedArgs; return pzArgs; } /* ** 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. ** ** Exception: if "--" is encountered, it is consumed from the argument ** list and this function immediately returns. The effect is to treat ** all arguments after "--" as non-flags (conventionally used to ** enable passing-in of filenames which start with a dash). ** ** This function must normally only be called one time per app ** invokation. The exception is commands which process their ** arguments, call this to confirm that there are no extraneous flags, ** then modify the arguments list for forwarding to another ** (sub)command (which itself will call this to confirm its own ** arguments). */ void verify_all_options(void){ int i; for(i=1; i<g.argc; i++){ const char * arg = g.argv[i]; if( arg[0]=='-' ){ if( arg[1]=='-' && arg[2]==0 ){ /* Remove "--" from the list and treat all following ** arguments as non-flags. */ remove_from_argv(i, 1); break; }else if( arg[1]!=0 ){ fossil_fatal( "unrecognized command-line option or missing argument: %s", arg); } } } } /* ** 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 populates a blob with version information. It is used by ** the "version" command and "test-version" web page. It assumes the blob ** passed to it is uninitialized; otherwise, it will leak memory. */ void fossil_version_blob( Blob *pOut, /* Write the manifest here */ int eVerbose /* 0: brief. 1: more text, 2: lots of text */ ){ #if defined(FOSSIL_ENABLE_TCL) int rc; const char *zRc; #endif Stmt q; size_t pageSize = 0; blob_zero(pOut); blob_appendf(pOut, "This is fossil version %s\n", get_version()); if( eVerbose<=0 ) return; blob_appendf(pOut, "Compiled on %s %s using %s (%d-bit)\n", __DATE__, __TIME__, COMPILER_NAME, sizeof(void*)*8); blob_appendf(pOut, "SQLite %s %.30s\n", sqlite3_libversion(), sqlite3_sourceid()); #if defined(FOSSIL_ENABLE_SSL) blob_appendf(pOut, "SSL (%s)\n", SSLeay_version(SSLEAY_VERSION)); #endif blob_appendf(pOut, "zlib %s, loaded %s\n", ZLIB_VERSION, zlibVersion()); #if defined(FOSSIL_HAVE_FUSEFS) blob_appendf(pOut, "libfuse %s, loaded %s\n", fusefs_inc_version(), fusefs_lib_version()); #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); blob_appendf(pOut, "TCL (Tcl %s, loaded %s: %s)\n", TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0) ); #endif if( eVerbose<=1 ) return; blob_appendf(pOut, "Schema version %s\n", AUX_SCHEMA_MAX); fossil_get_page_size(&pageSize); blob_appendf(pOut, "Detected memory page size is %lu bytes\n", (unsigned long)pageSize); #if FOSSIL_HARDENED_SHA1 blob_appendf(pOut, "hardened-SHA1 by Marc Stevens and Dan Shumow\n"); #endif #if defined(FOSSIL_DEBUG) blob_append(pOut, "FOSSIL_DEBUG\n", -1); #endif #if defined(FOSSIL_ENABLE_DELTA_CKSUM_TEST) blob_append(pOut, "FOSSIL_ENABLE_DELTA_CKSUM_TEST\n", -1); #endif blob_append(pOut, "FOSSIL_ENABLE_LEGACY_MV_RM\n", -1); #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) blob_append(pOut, "FOSSIL_ENABLE_EXEC_REL_PATHS\n", -1); #endif #if defined(FOSSIL_ENABLE_TH1_DOCS) blob_append(pOut, "FOSSIL_ENABLE_TH1_DOCS\n", -1); #endif #if defined(FOSSIL_ENABLE_TH1_HOOKS) blob_append(pOut, "FOSSIL_ENABLE_TH1_HOOKS\n", -1); #endif #if defined(USE_TCL_STUBS) blob_append(pOut, "USE_TCL_STUBS\n", -1); #endif #if defined(FOSSIL_ENABLE_TCL_STUBS) blob_append(pOut, "FOSSIL_TCL_STUBS\n", -1); #endif #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) blob_append(pOut, "FOSSIL_ENABLE_TCL_PRIVATE_STUBS\n", -1); #endif #if defined(FOSSIL_ENABLE_JSON) blob_appendf(pOut, "JSON (API %s)\n", FOSSIL_JSON_API_VERSION); #endif blob_append(pOut, "MARKDOWN\n", -1); #if defined(BROKEN_MINGW_CMDLINE) blob_append(pOut, "MBCS_COMMAND_LINE\n", -1); #else blob_append(pOut, "UNICODE_COMMAND_LINE\n", -1); #endif #if defined(FOSSIL_DYNAMIC_BUILD) blob_append(pOut, "FOSSIL_DYNAMIC_BUILD\n", -1); #else blob_append(pOut, "FOSSIL_STATIC_BUILD\n", -1); #endif #if defined(HAVE_PLEDGE) blob_append(pOut, "HAVE_PLEDGE\n", -1); #endif #if defined(USE_MMAN_H) blob_append(pOut, "USE_MMAN_H\n", -1); #endif #if defined(USE_SEE) blob_appendf(pOut, "USE_SEE (%s)\n", db_have_saved_encryption_key() ? "SET" : "UNSET"); #endif #if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES) blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n"); #endif if( g.db==0 ) sqlite3_open(":memory:", &g.db); db_prepare(&q, "pragma compile_options"); while( db_step(&q)==SQLITE_ROW ){ const char *text = db_column_text(&q, 0); if( strncmp(text, "COMPILER", 8) ){ blob_appendf(pOut, "SQLITE_%s\n", text); } } db_finalize(&q); } /* ** 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 ?-v|--verbose? ** ** 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. ** ** Repeat the -v option or use -vv for even more information. */ void version_cmd(void){ Blob versionInfo; int verboseFlag = 0; while( find_option("verbose","v",0)!=0 ) verboseFlag++; while( find_option("vv",0,0)!=0 ) verboseFlag += 2; /* We should be done with options.. */ verify_all_options(); fossil_version_blob(&versionInfo, verboseFlag); fossil_print("%s", blob_str(&versionInfo)); } /* ** WEBPAGE: version ** ** Show the version information for Fossil. ** ** Query parameters: ** ** verbose Show details */ void test_version_page(void){ Blob versionInfo; int verboseFlag; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } verboseFlag = P("verbose")!=0 ? 2 : 1; style_header("Version Information"); style_submenu_element("Stat", "stat"); fossil_version_blob(&versionInfo, verboseFlag); @ <pre> @ %h(blob_str(&versionInfo)) @ </pre> style_finish_page(); } /* ** Set the g.zBaseURL value to the full URL for the toplevel of ** the fossil tree. Set g.zTop to g.zBaseURL without the ** leading "http://" and the host and port. ** ** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME ** environment variables. However, if zAltBase is not NULL then it ** is the argument to the --baseurl option command-line option and ** g.zBaseURL and g.zTop is set from that instead. */ void set_base_url(const char *zAltBase){ int i; const char *zHost; const char *zMode; const char *zCur; if( g.zBaseURL!=0 ) return; if( zAltBase ){ int i, n, c; g.zTop = g.zBaseURL = mprintf("%s", zAltBase); i = (int)strlen(g.zBaseURL); while( i>3 && g.zBaseURL[i-1]=='/' ){ i--; } g.zBaseURL[i] = 0; if( strncmp(g.zTop, "http://", 7)==0 ){ /* it is HTTP, replace prefix with HTTPS. */ g.zHttpsURL = mprintf("https://%s", &g.zTop[7]); }else if( strncmp(g.zTop, "https://", 8)==0 ){ /* it is already HTTPS, use it. */ g.zHttpsURL = mprintf("%s", g.zTop); }else{ fossil_fatal("argument to --baseurl should be 'http://host/path'" " or 'https://host/path'"); } for(i=n=0; (c = g.zTop[i])!=0; i++){ if( c=='/' ){ n++; if( n==3 ){ g.zTop += i; break; } } } if( n==2 ) g.zTop = ""; if( g.zTop==g.zBaseURL ){ fossil_fatal("argument to --baseurl should be 'http://host/path'" " or 'https://host/path'"); } if( g.zTop[1]==0 ) g.zTop++; }else{ char *z; zMode = PD("HTTPS","off"); zHost = PD("HTTP_HOST",""); z = fossil_strdup(zHost); for(i=0; z[i]; i++){ if( z[i]<='Z' && z[i]>='A' ) z[i] += 'a' - 'A'; } if( fossil_strcmp(zMode,"on")==0 ){ /* Remove trailing ":443" from the HOST, if any */ if( i>4 && z[i-1]=='3' && z[i-2]=='4' && z[i-3]=='4' && z[i-4]==':' ){ i -= 4; } }else{ /* Remove trailing ":80" from the HOST */ if( i>3 && z[i-1]=='0' && z[i-2]=='8' && z[i-3]==':' ) i -= 3; } if( i && z[i-1]=='.' ) i--; z[i] = 0; 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", z, i, zCur); g.zTop = &g.zBaseURL[8+strlen(z)]; g.zHttpsURL = g.zBaseURL; }else{ g.zBaseURL = mprintf("http://%s%.*s", z, i, zCur); g.zTop = &g.zBaseURL[7+strlen(z)]; g.zHttpsURL = mprintf("https://%s%.*s", z, i, zCur); } fossil_free(z); } /* Try to record the base URL as a CONFIG table entry with a name ** of the form: "baseurl:BASE". This keeps a record of how the ** the repository is used as a server, to help in answering questions ** like "where is the CGI script that references this repository?" ** ** This is just a logging hint. So don't worry if it cannot be done. ** Don't try this if the repository database is not writable, for ** example. ** ** If g.useLocalauth is set, that (probably) means that we are running ** "fossil ui" and there is no point in logging those cases either. */ if( db_is_writeable("repository") && !g.useLocalauth ){ int nBase = (int)strlen(g.zBaseURL); char *zBase = g.zBaseURL; if( g.nExtraURL>0 && g.nExtraURL<nBase-6 ){ zBase = fossil_strndup(g.zBaseURL, nBase - g.nExtraURL); } db_unprotect(PROTECT_CONFIG); if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", zBase)){ db_multi_exec("INSERT INTO config(name,value,mtime)" "VALUES('baseurl:%q',1,now())", zBase); }else{ db_optional_sql("repository", "REPLACE INTO config(name,value,mtime)" "VALUES('baseurl:%q',1,now())", zBase ); } db_protect_pop(); if( zBase!=g.zBaseURL ) fossil_free(zBase); } } /* ** Send an HTTP redirect back to the designated Index Page. */ NORETURN void fossil_redirect_home(void){ /* In order for ?skin=... to work when visiting the site from ** a typical external link, we have to process it here, as ** that parameter gets lost during the redirect. We "could" ** pass the whole query string along instead, but that seems ** unnecessary. */ if(cgi_setup_query_string()>1){ cookie_render(); } cgi_redirectf("%R%s", db_get("index-page", "/index")); } /* ** If running as root, chroot to the directory containing the ** repository zRepo and then drop root privileges. Return the ** new repository name. ** ** zRepo can be a directory. If so and if the repo name was saved ** to g.zRepositoryName before we were called, we canonicalize the ** two paths and check that one is the prefix of the other, else you ** won't be able to open the repo inside the jail. If it all works ** out, we return the "jailed" version of the repo name. ** ** 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 user-id and group-id ** of the repository file. */ static char *enter_chroot_jail(const 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, ExtFILE)==1 ){ if( g.zRepositoryName ){ size_t n = strlen(zDir); Blob repo; file_canonical_name(g.zRepositoryName, &repo, 0); zRepo = blob_str(&repo); if( strncmp(zRepo, zDir, n)!=0 ){ fossil_fatal("repo %s not under chroot dir %s", zRepo, zDir); } zRepo += n; if( *zRepo == '\0' ) zRepo = "/"; }else { zRepo = "/"; } if( file_chdir(zDir, 1) ){ fossil_panic("unable to chroot into %s", zDir); } }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, ExtFILE) ){ db_open_repository(zRepo); } } #endif return (char*)zRepo; /* no longer const: always reassigned from blob_str() */ } /* ** Called whenever a crash is encountered while processing a webpage. */ void sigsegv_handler(int x){ #if HAVE_BACKTRACE void *array[20]; size_t size; char **strings; size_t i; Blob out; size = backtrace(array, sizeof(array)/sizeof(array[0])); strings = backtrace_symbols(array, size); blob_init(&out, 0, 0); blob_appendf(&out, "Segfault during %s in fossil %s", g.zPhase, MANIFEST_VERSION); for(i=0; i<size; i++){ size_t len; const char *z = strings[i]; if( i==0 ) blob_appendf(&out, "\nBacktrace:"); len = strlen(strings[i]); if( z[0]=='[' && z[len-1]==']' ){ blob_appendf(&out, " %.*s", (int)(len-2), &z[1]); }else{ blob_appendf(&out, " %s", z); } } fossil_panic("%s", blob_str(&out)); #else fossil_panic("Segfault during %s in fossil %s", g.zPhase, MANIFEST_VERSION); #endif exit(1); } /* ** Called if a server gets a SIGPIPE. This often happens when a client ** webbrowser opens a connection but never sends the HTTP request */ void sigpipe_handler(int x){ #ifndef _WIN32 if( g.fAnyTrace ){ fprintf(stderr,"/***** sigpipe received by subprocess %d ****\n", getpid()); } #endif g.zPhase = "sigpipe shutdown"; db_panic_close(); exit(1); } /* ** Return true if it is appropriate to redirect requests to HTTPS. ** ** Redirect to https is appropriate if all of the above are true: ** (1) The redirect-to-https flag has a valud of iLevel or greater. ** (2) The current connection is http, not https or ssh ** (3) The sslNotAvailable flag is clear */ int fossil_wants_https(int iLevel){ if( g.sslNotAvailable ) return 0; if( db_get_int("redirect-to-https",0)<iLevel ) return 0; if( P("HTTPS")!=0 ) return 0; return 1; } /* ** Redirect to the equivalent HTTPS request if the current connection is ** insecure and if the redirect-to-https flag greater than or equal to ** iLevel. iLevel is 1 for /login pages and 2 for every other page. */ int fossil_redirect_to_https_if_needed(int iLevel){ if( fossil_wants_https(iLevel) ){ const char *zQS = P("QUERY_STRING"); char *zURL; if( zQS==0 || zQS[0]==0 ){ zURL = mprintf("%s%T", g.zHttpsURL, P("PATH_INFO")); }else if( zQS[0]!=0 ){ zURL = mprintf("%s%T?%s", g.zHttpsURL, P("PATH_INFO"), zQS); } cgi_redirect_with_status(zURL, 301, "Moved Permanently"); return 1; } return 0; } /* ** Send a 404 Not Found reply */ void fossil_not_found_page(void){ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); return; } #endif @ <html><head> @ <meta name="viewport" \ @ content="width=device-width, initial-scale=1.0"> @ </head><body> @ <h1>Not Found</h1> @ </body> cgi_set_status(404, "Not Found"); cgi_reply(); } /* ** Preconditions: ** ** * Environment variables are set up according to the CGI standard. ** ** If the repository is known, it has already been opened. If unknown, ** then g.zRepositoryName holds the directory that contains the repository ** and the actual repository is taken from the first element of PATH_INFO. ** ** Process the webpage specified by the PATH_INFO or REQUEST_URI ** environment variable. ** ** If the repository is not known, then a search is done through the ** file hierarchy rooted at g.zRepositoryName for a suitable repository ** with a name of $prefix.fossil, where $prefix is any prefix of PATH_INFO. ** 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 = PD("PATH_INFO", ""); char *zPath = NULL; int i; const CmdOrPage *pCmd = 0; const char *zBase = g.zRepositoryName; int isReadonly = 0; g.zPhase = "process_one_web_page"; #if !defined(_WIN32) signal(SIGSEGV, sigsegv_handler); #endif /* Handle universal query parameters */ if( PB("utc") ){ g.fTimeFormat = 1; }else if( PB("localtime") ){ g.fTimeFormat = 2; } #ifdef FOSSIL_ENABLE_JSON /* ** Ensure that JSON mode is set up if we're visiting /json, to allow ** us to customize some following behaviour (error handling and only ** process JSON-mode POST data if we're actually in a /json ** page). This is normally set up before this routine is called, but ** it looks like the ssh_request_loop() approach to dispatching ** might bypass that. */ if( g.json.isJsonMode==0 && json_request_is_json_api(zPathInfo)!=0 ){ g.json.isJsonMode = 1; json_bootstrap_early(); } #endif /* If the repository has not been opened already, then find the ** repository based on the first element of PATH_INFO and open it. */ if( !g.repositoryOpen ){ char zBuf[24]; const char *zRepoExt = ".fossil"; char *zRepo; /* Candidate repository name */ char *zToFree = 0; /* Malloced memory that needs to be freed */ const char *zCleanRepo; /* zRepo with surplus leading "/" removed */ const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */ char *zNewScript; /* Revised SCRIPT_NAME after processing */ int j, k; /* Loop variables */ i64 szFile; /* File size of the candidate repository */ i = zPathInfo[0]!=0; if( fossil_strcmp(g.zRepositoryName, "/")==0 ){ zBase++; #if defined(_WIN32) || defined(__CYGWIN__) if( sqlite3_strglob("/[a-zA-Z]:/*", zPathInfo)==0 ) i = 4; #endif } while( 1 ){ size_t nBase = strlen(zBase); while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } /* The candidate repository name is some prefix of the PATH_INFO ** with ".fossil" appended */ zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt); if( g.fHttpTrace ){ @ <!-- Looking for repository named "%h(zRepo)" --> fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo); } /* For safety -- to prevent an attacker from accessing arbitrary disk ** files by sending a maliciously crafted request URI to a public ** server -- make sure the repository basename contains no ** characters other than alphanumerics, "/", "_", "-", and ".", and ** that "-" never occurs immediately after a "/" and that "." is always ** surrounded by two alphanumerics. Any character that does not ** satisfy these constraints is converted into "_". */ szFile = 0; for(j=nBase+1, k=0; zRepo[j] && k<i-1; j++, k++){ char c = zRepo[j]; if( fossil_isalnum(c) ) continue; #if defined(_WIN32) || defined(__CYGWIN__) /* Allow names to begin with "/X:/" on windows */ if( c==':' && j==2 && sqlite3_strglob("/[a-zA-Z]:/*", zRepo)==0 ){ continue; } #endif if( c=='/' ) continue; if( c=='_' ) continue; if( c=='-' && zRepo[j-1]!='/' ) continue; if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){ continue; } if( c=='.' && g.fAllowACME && j==(int)nBase+1 && strncmp(&zRepo[j-1],"/.well-known/",12)==0 ){ /* We allow .well-known as the top-level directory for ACME */ continue; } /* If we reach this point, it means that the request URI contains ** an illegal character or character combination. Provoke a ** "Not Found" error. */ szFile = 1; if( g.fHttpTrace ){ @ <!-- Unsafe pathname rejected: "%h(zRepo)" --> fprintf(stderr, "# unsafe pathname rejected: %s\n", zRepo); } break; } /* Check to see if a file name zRepo exists. If a file named zRepo ** does not exist, szFile will become -1. If the file does exist, ** then szFile will become zero (for an empty file) or positive. ** Special case: Assume any file with a basename of ".fossil" does ** not exist. */ zCleanRepo = file_cleanup_fullpath(zRepo); if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ szFile = file_size(zCleanRepo, ExtFILE); if( g.fHttpTrace ){ sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); } } /* If no file named by zRepo exists, remove the added ".fossil" suffix ** and check to see if there is a file or directory with the same ** name as the raw PATH_INFO text. */ if( szFile<0 && i>0 ){ const char *zMimetype; assert( file_is_repository_extension(&zRepo[j]) ); zRepo[j] = 0; /* Remove the ".fossil" suffix */ /* The PATH_INFO prefix seen so far is a valid directory. ** Continue the loop with the next element of the PATH_INFO */ if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){ fossil_free(zToFree); i++; continue; } /* If zRepo is the name of an ordinary file that matches the ** "--file GLOB" pattern, then the CGI reply is the text of ** of the file. ** ** For safety, do not allow any file whose name contains ".fossil" ** to be returned this way, to prevent complete repositories from ** being delivered accidently. This is not intended to be a ** general-purpose web server. The "--file GLOB" mechanism is ** designed to allow the delivery of a few static images or HTML ** pages. */ if( pFileGlob!=0 && file_isfile(zCleanRepo, ExtFILE) && glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase)) && !file_contains_repository_extension(zRepo) && (zMimetype = mimetype_from_name(zRepo))!=0 && strcmp(zMimetype, "application/x-fossil-artifact")!=0 ){ Blob content; blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE); cgi_set_content_type(zMimetype); cgi_set_content(&content); cgi_reply(); return; } /* In support of the ACME protocol, files under the .well-known/ ** directory is always accepted. */ if( g.fAllowACME && strncmp(&zRepo[nBase],"/.well-known/",12)==0 && file_isfile(zCleanRepo, ExtFILE) ){ Blob content; blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE); cgi_set_content_type(mimetype_from_name(zRepo)); cgi_set_content(&content); cgi_reply(); return; } zRepo[j] = '.'; } /* If we reach this point, it means that the search of the PATH_INFO ** string is finished. Either zRepo contains the name of the ** repository to be used, or else no repository could be found and ** some kind of error response is required. */ if( szFile<1024 ){ #if USE_SEE if( strcmp(zRepoExt,".fossil")==0 ){ fossil_free(zToFree); zRepoExt = ".efossil"; continue; } #endif set_base_url(0); if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0) && allowRepoList && repo_list_page() ){ /* Will return a list of repositories */ }else if( zNotFound ){ cgi_redirect(zNotFound); }else{ fossil_not_found_page(); } return; } break; } /* Add the repository name (without the ".fossil" suffix) to the end ** of SCRIPT_NAME and g.zTop and g.zBaseURL and remove the repository ** name from the beginning of PATH_INFO. */ zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo); if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo); if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); zPathInfo += i; cgi_replace_parameter("SCRIPT_NAME", zNewScript); #if USE_SEE if( zPathInfo ){ if( g.fHttpTrace ){ sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", i); @ <!-- see_path_info(%s(zBuf)) is %h(zPathInfo) --> fprintf(stderr, "# see_path_info(%d) = %s\n", i, zPathInfo); } if( strcmp(zPathInfo,"/setseekey")==0 && strcmp(zRepoExt,".efossil")==0 && !db_have_saved_encryption_key() ){ db_set_see_key_page(); cgi_reply(); fossil_exit(0); } } #endif db_open_repository(file_cleanup_fullpath(zRepo)); if( g.fHttpTrace ){ @ <!-- repository: "%h(zRepo)" --> @ <!-- translated PATH_INFO: "%h(zPathInfo)" --> @ <!-- translated SCRIPT_NAME: "%h(zNewScript)" --> fprintf(stderr, "# repository: [%s]\n" "# translated PATH_INFO = [%s]\n" "# translated SCRIPT_NAME = [%s]\n", zRepo, zPathInfo, zNewScript); if( g.zTop ){ @ <!-- translated g.zTop: "%h(g.zTop)" --> fprintf(stderr, "# translated g.zTop = [%s]\n", g.zTop); } if( g.zBaseURL ){ @ <!-- translated g.zBaseURL: "%h(g.zBaseURL)" --> fprintf(stderr, "# translated g.zBaseURL = [%s]\n", g.zBaseURL); } } } /* At this point, the appropriate repository database file will have ** been opened. */ /* ** Check to see if the first term of PATH_INFO specifies an ** alternative skin. This will be the case if the first term of ** PATH_INFO begins with "draftN/" where N is an integer between 1 ** and 9. If so, activate the skin associated with that draft. */ if( zPathInfo && strncmp(zPathInfo,"/draft",6)==0 && zPathInfo[6]>='1' && zPathInfo[6]<='9' && (zPathInfo[7]=='/' || zPathInfo[7]==0) ){ int iSkin = zPathInfo[6] - '0'; char *zNewScript; if( db_int(0,"SELECT count(*) FROM config WHERE name GLOB 'draft%d-*'", iSkin)<5 ){ fossil_not_found_page(); fossil_exit(0); } skin_use_draft(iSkin); zNewScript = mprintf("%T/draft%d", P("SCRIPT_NAME"), iSkin); if( g.zTop ) g.zTop = mprintf("%R/draft%d", iSkin); if( g.zBaseURL ) g.zBaseURL = mprintf("%s/draft%d", g.zBaseURL, iSkin); zPathInfo += 7; g.nExtraURL += 7; cgi_replace_parameter("PATH_INFO", zPathInfo); cgi_replace_parameter("SCRIPT_NAME", zNewScript); etag_cancel(); } /* If the content type is application/x-fossil or ** application/x-fossil-debug, then a sync/push/pull/clone is ** desired, so default the PATH_INFO to /xfer */ if( g.zContentType && strncmp(g.zContentType, "application/x-fossil", 20)==0 ){ /* Special case: If the content mimetype shows that it is "fossil sync" ** payload, then pretend that the PATH_INFO is /xfer so that we always ** invoke the sync page. */ zPathInfo = "/xfer"; } /* Use the first element of PATH_INFO as the page name ** and deliver the appropriate page back to the user. */ set_base_url(0); if( fossil_redirect_to_https_if_needed(2) ) return; if( zPathInfo==0 || zPathInfo[0]==0 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ /* Second special case: If the PATH_INFO is blank, issue a redirect to ** the home page identified by the "index-page" setting in the repository ** CONFIG table, to "/index" if there no "index-page" setting. */ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); fossil_exit(0); } #endif fossil_redirect_home() /*does not return*/; }else{ zPath = mprintf("%s", zPathInfo); } /* Make g.zPath point to the first element of the path. Make ** g.zExtra point to everything past that point. */ g.zPath = &zPath[1]; for(i=1; zPath[i] && zPath[i]!='/'; i++){} if( zPath[i]=='/' ){ zPath[i] = 0; g.zExtra = &zPath[i+1]; }else{ g.zExtra = 0; } if( g.zExtra ){ /* CGI parameters get this treatment elsewhere, but places like getfile ** will use g.zExtra directly. ** Reminder: the login mechanism uses 'name' differently, and may ** eventually have a problem/collision with this. ** ** Disabled by stephan when running in JSON mode because this ** particular parameter name is very common and i have had no end ** of grief with this handling. The JSON API never relies on the ** handling below, and by disabling it in JSON mode I can remove ** lots of special-case handling in several JSON handlers. */ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode==0){ #endif dehttpize(g.zExtra); cgi_set_parameter_nocopy("name", g.zExtra, 1); #ifdef FOSSIL_ENABLE_JSON } #endif } /* Locate the method specified by the path and execute the function ** that implements that method. */ if( dispatch_name_search(g.zPath-1, CMDFLAG_WEBPAGE, &pCmd) && dispatch_alias(g.zPath-1, &pCmd) ){ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode!=0){ 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( pCmd->xFunc!=page_xfer && db_schema_is_outofdate() ){ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode!=0){ 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{ if(0==(CMDFLAG_LDAVG_EXEMPT & pCmd->eCmdFlags)){ load_control(); } #ifdef FOSSIL_ENABLE_JSON { static int jsonOnce = 0; if( jsonOnce==0 && g.json.isJsonMode!=0 ){ assert(json_is_bootstrapped_early()); json_bootstrap_late(); jsonOnce = 1; } } #endif if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){ cgi_decode_post_parameters(); if( !cgi_same_origin() ){ isReadonly = 1; db_protect(PROTECT_READONLY); } } if( g.fCgiTrace ){ fossil_trace("######## Calling %s #########\n", pCmd->zName); cgi_print_all(1, 1, 0); } #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 skipped, 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(pCmd->zName+1, pCmd->eCmdFlags); }else{ rc = TH_OK; } if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ if( rc==TH_OK || rc==TH_RETURN ){ #endif g.zPhase = pCmd->zName; pCmd->xFunc(); #ifdef FOSSIL_ENABLE_TH1_HOOKS } if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ Th_WebpageNotify(pCmd->zName+1, pCmd->eCmdFlags); } } } #endif if( isReadonly ){ db_protect_pop(); } } /* Return the result. */ g.zPhase = "web-page reply"; 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" query parameter. If there is ** no "name" query parameter, use PATH_INFO instead. If a match is ** found, redirect to the corresponding URL. Substitute "%s" in the ** URL with the value of the name query parameter before the redirect. ** ** If there is a line of the form: ** ** redirect: * URL ** ** Then a redirect is made to URL if no match is found. If URL contains ** "%s" then substitute the "name" query parameter. If REPO is "*" and ** URL does not contains "%s" and does not contain "?" then append ** PATH_INFO and QUERY_STRING to the URL prior to the redirect. ** ** If no matches are found and if there is no "*" entry, then generate ** a primitive error message. ** ** USE CASES: ** ** (1) Suppose you have two related projects projA and projB. You can ** use this feature to set up an /info page that covers both ** projects. ** ** redirect: /fossils/projA.fossil /proj-a/info/%s ** redirect: /fossils/projB.fossil /proj-b/info/%s ** ** Then visits to the /info/HASH page will redirect to the ** first project that contains that hash. ** ** (2) Use the "*" form for to redirect legacy URLs. On the Fossil ** website we have an CGI at http://fossil.com/index.html (note ** ".com" instead of ".org") that looks like this: ** ** #!/usr/bin/fossil ** redirect: * https://fossil-scm.org/home ** ** Thus requests to the .com website redirect to the .org website. */ static void redirect_web_page(int nRedirect, char **azRedirect){ int i; /* Loop counter */ const char *zNotFound = 0; /* Not found URL */ const char *zName = P("name"); set_base_url(0); if( zName==0 ){ zName = P("PATH_INFO"); if( zName && zName[0]=='/' ) zName++; } if( zName ){ for(i=0; i<nRedirect; i++){ if( fossil_strcmp(azRedirect[i*2],"*")==0 ){ zNotFound = azRedirect[i*2+1]; continue; }else if( validate16(zName, strlen(zName)) ){ 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 ){ Blob to; const char *z; if( strstr(zNotFound, "%s") ){ cgi_redirectf(zNotFound /*works-like:"%s"*/, zName); } if( strchr(zNotFound, '?') ){ cgi_redirect(zNotFound); } blob_init(&to, zNotFound, -1); z = P("PATH_INFO"); if( z && z[0]=='/' ) blob_append(&to, z, -1); z = P("QUERY_STRING"); if( z && z[0]!=0 ) blob_appendf(&to, "?%s", z); cgi_redirect(blob_str(&to)); }else{ @ <html> @ <head><title>No Such Object</title></head> @ <body> @ <p>No such object: <b>%h(zName)</b></p> @ </body> cgi_reply(); } } /* ** COMMAND: cgi* ** ** Usage: %fossil ?cgi? FILE ** ** This command causes Fossil to generate reply to a CGI request. ** ** The FILE argument is the name of a control file that provides Fossil ** with important information such as where to find its repository. In ** a typical CGI deployment, FILE is the name of the CGI script and will ** typically look something like this: ** ** #!/usr/bin/fossil ** repository: /home/somebody/project.db ** ** The command name, "cgi", may be omitted if the GATEWAY_INTERFACE ** environment variable is set to "CGI", which should always be the ** case for CGI scripts run by a webserver. Fossil ignores any lines ** that begin with "#". ** ** The following control lines are recognized: ** ** repository: PATH Name of the Fossil repository ** ** directory: PATH Name of a directory containing many Fossil ** repositories whose names all end with ".fossil". ** There should only be one of "repository:" ** or "directory:" ** ** notfound: URL When in "directory:" mode, redirect to ** URL if no suitable repository is found. ** ** repolist When in "directory:" mode, display a page ** showing a list of available repositories if ** the URL is "/". ** ** localauth Grant administrator privileges to connections ** from 127.0.0.1 or ::1. ** ** nossl Signal that no SSL connections are available. ** ** nocompress Do not compress HTTP replies. ** ** skin: LABEL Use the built-in skin called LABEL rather than ** the default. If there are no skins called LABEL ** then this line is a no-op. ** ** files: GLOBLIST GLOBLIST is a comma-separated list of GLOB ** patterns that specify files that can be ** returned verbatim. This feature allows Fossil ** to act as a web server returning static ** content. ** ** setenv: NAME VALUE Set environment variable NAME to VALUE. Or ** if VALUE is omitted, unset NAME. ** ** HOME: PATH Shorthand for "setenv: HOME PATH" ** ** cgi-debug: FILE Causing debugging information to be written ** into FILE. ** ** errorlog: FILE Warnings, errors, and panics written to FILE. ** ** timeout: SECONDS Do not run for longer than SECONDS. The default ** timeout is FOSSIL_DEFAULT_TIMEOUT (600) seconds. ** ** extroot: DIR Directory that is the root of the sub-CGI tree ** on the /ext page. ** ** redirect: REPO URL Extract the "name" query parameter and search ** REPO for a check-in or ticket that matches the ** value of "name", then redirect to URL. There ** can be multiple "redirect:" lines that are ** processed in order. If the REPO is "*", then ** an unconditional redirect to URL is taken. ** ** jsmode: VALUE Specifies the delivery mode for JavaScript ** files. See the help text for the --jsmode ** flag of the http command. ** ** mainmenu: FILE Override the mainmenu config setting with the ** contents of the given file. ** ** Most CGI files contain only a "repository:" line. It is uncommon to ** use any other option. ** ** The lines are processed in the order they are read, which is most ** significant for "errorlog:", which should be set before "repository:" ** so that any warnings from the database when opening the repository ** go to that log file. ** ** See also: [[http]], [[server]], [[winsrv]] */ void cmd_cgi(void){ 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; /* Initialize the CGI environment. */ g.httpOut = stdout; g.httpIn = stdin; fossil_binary_mode(g.httpOut); fossil_binary_mode(g.httpIn); g.cgiOutput = 1; g.zReqType = "CGI"; fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT); /* Find the name of the CGI control file */ if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){ g.zCgiFile = g.argv[2]; }else if( g.argc>=2 ){ g.zCgiFile = g.argv[1]; }else{ cgi_panic("No CGI control file specified"); } /* Read and parse the CGI control file. */ blob_read_from_file(&config, g.zCgiFile, ExtFILE); 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, "nossl") ){ /* nossl ** ** Signal that no SSL connections are available. */ g.sslNotAvailable = 1; continue; } if( blob_eq(&key, "nocompress") ){ /* nocompress ** ** Do not compress HTTP replies. */ g.fNoHttpCompress = 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) ){ /* setenv: NAME VALUE ** setenv: NAME ** ** Sets environment variable NAME to VALUE. If VALUE is omitted, then ** the environment variable is unset. */ blob_token(&line,&value2); fossil_setenv(blob_str(&value), blob_str(&value2)); blob_reset(&value); blob_reset(&value2); 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, "extroot:") && blob_token(&line, &value) ){ /* extroot: DIRECTORY ** ** Enables the /ext webpage to use sub-cgi rooted at DIRECTORY */ g.zExtRoot = mprintf("%s", blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "timeout:") && blob_token(&line, &value) ){ /* timeout: SECONDS ** ** Set an alarm() that kills the process after SECONDS. The ** default value is FOSSIL_DEFAULT_TIMEOUT (600) seconds. */ fossil_set_timeout(atoi(blob_str(&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:") ){ /* 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. It may alternately be ** an absolute path to a directory which holds skin definition ** files (header.txt, footer.txt, etc.). If LABEL is empty, ** the skin stored in the CONFIG db table is used. */ blob_token(&line, &value); fossil_free(skin_use_alternative(blob_str(&value), 1, SKIN_FROM_CGI)); blob_reset(&value); continue; } if( blob_eq(&key, "jsmode:") && blob_token(&line, &value) ){ /* jsmode: MODE ** ** Change how JavaScript resources are delivered with each HTML ** page. MODE is "inline" to put all JS inline, or "separate" to ** cause each JS file to be requested using a separate HTTP request, ** or "bundled" to have all JS files to be fetched with a single ** auxiliary HTTP request. Noting, however, that "single" might ** actually mean more than one, depending on the script-timing ** requirements of any given page. */ builtin_set_js_delivery_mode(blob_str(&value),0); blob_reset(&value); continue; } if( blob_eq(&key, "mainmenu:") && blob_token(&line, &value) ){ /* mainmenu: FILENAME ** ** Use the contents of FILENAME as the value of the site's ** "mainmenu" setting, overriding the contents (for this ** request) of the db-side setting or the hard-coded default. */ g.zMainMenuFile = mprintf("%s", blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "cgi-debug:") && blob_token(&line, &value) ){ /* cgi-debug: FILENAME ** ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go ** into FILENAME. Useful for debugging CGI configuration problems. */ char *zNow = cgi_iso8601_datestamp(); cgi_load_environment(); g.fDebug = fossil_fopen(blob_str(&value), "ab"); blob_reset(&value); cgi_debug("-------- BEGIN cgi at %s --------\n", zNow); fossil_free(zNow); cgi_print_all(1,2,0); 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 exist, then we must be within ** an open check-out and the repository to serve 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. Always use "auto" hash-policy in this case. */ 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, ExtFILE); 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(); g.eHashPolicy = HPOLICY_SHA3; db_set_int("hash-policy", HPOLICY_SHA3, 0); 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); hash_user_password(g.zLogin); cache_initialize(); g.zLogin = 0; g.userUid = 0; }else{ db_open_repository(zRepo); } } } } #if USE_SEE /* ** This function attempts to parse a string value in the following ** format: ** ** "%lu:%p:%u" ** ** There are three parts, which must be delimited by colons. The ** first part is an unsigned long integer in base-10 (decimal) format. ** The second part is a numerical representation of a native pointer, ** in the appropriate implementation defined format. The third part ** is an unsigned integer in base-10 (decimal) format. ** ** If the specified value cannot be parsed, for any reason, a fatal ** error will be raised and the process will be terminated. */ void parse_pid_key_value( const char *zPidKey, /* The value to be parsed. */ PID_T *pProcessId, /* The extracted process identifier. */ LPVOID *ppAddress, /* The extracted pointer value. */ SIZE_T *pnSize /* The extracted size value. */ ){ unsigned long processId = 0; unsigned int nSize = 0; if( sscanf(zPidKey, "%lu:%p:%u", &processId, ppAddress, &nSize)==3 ){ *pProcessId = (PID_T)processId; *pnSize = (SIZE_T)nSize; }else{ fossil_fatal("failed to parse pid key"); } } #endif /* ** WEBPAGE: test-pid ** ** Return the process identifier of the running Fossil server instance. ** ** Query parameters: ** ** usepidkey When present and available, also return the ** address and size, within this server process, ** of the saved database encryption key. This ** is only supported when using SEE on Windows ** or Linux. */ void test_pid_page(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } #if USE_SEE if( P("usepidkey")!=0 ){ if( g.zPidKey ){ @ %s(g.zPidKey) return; }else{ const char *zSavedKey = db_get_saved_encryption_key(); size_t savedKeySize = db_get_saved_encryption_key_size(); if( zSavedKey!=0 && savedKeySize>0 ){ @ %lu(GETPID()):%p(zSavedKey):%u(savedKeySize) return; } } } #endif @ %d(GETPID()) } /* ** Check for options to "fossil server" or "fossil ui" that imply that ** SSL should be used, and initialize the SSL decoder. */ static void decode_ssl_options(void){ #if FOSSIL_ENABLE_SSL const char *zCertFile = 0; const char *zKeyFile = 0; zCertFile = find_option("cert",0,1); zKeyFile = find_option("pkey",0,1); if( zCertFile ){ g.httpUseSSL = 1; ssl_init_server(zCertFile, zKeyFile); }else if( zKeyFile ){ fossil_fatal("--pkey without a corresponding --cert"); } #endif } /* ** 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 REPOSITORY argument is the name of ** the repository. ** ** If REPOSITORY is a directory that contains one or more repositories, ** either directly in REPOSITORY itself or in subdirectories, and ** with names of the form "*.fossil" then a prefix of the URL pathname ** selects from among the various repositories. If the pathname does ** not select a valid repository and the --notfound option is available, ** then the server redirects (HTTP code 302) to the URL of --notfound. ** When REPOSITORY is a directory, the pathname must contain only ** alphanumerics, "_", "/", "-" and "." and no "-" may occur after a "/" ** and every "." must be surrounded on both sides by alphanumerics or else ** a 404 error is returned. Static content files in the directory are ** returned if they match comma-separate GLOB pattern specified by --files ** and do not match "*.fossil*" and have a well-known suffix. ** ** Options: ** --acme Deliver files from the ".well-known" subdirectory ** --baseurl URL Base URL (useful with reverse proxies) ** --cert FILE Use TLS (HTTPS) encryption with the certificate (the ** fullchain.pem) taken from FILE. ** --chroot DIR Use directory for chroot instead of repository path. ** --ckout-alias N Treat URIs of the form /doc/N/... as if they were ** /doc/ckout/... ** --extroot DIR Document root for the /ext extension mechanism ** --files GLOB Comma-separate glob patterns for static file to serve ** --host NAME DNS Hostname of the server ** --https The HTTP request originated from https but has already ** been decoded by a reverse proxy. Hence, URLs created ** by Fossil should use "https:" rather than "http:". ** --in FILE Take input from FILE instead of standard input ** --ipaddr ADDR Assume the request comes from the given IP address ** --jsmode MODE Determine how JavaScript is delivered with pages. ** Mode can be one of: ** inline All JavaScript is inserted inline at ** one or more points in the HTML file. ** separate Separate HTTP requests are made for ** each JavaScript file. ** bundled Groups JavaScript files into one or ** more bundled requests which ** concatenate scripts together. ** Depending on the needs of any given page, inline ** and bundled modes might result in a single ** amalgamated script or several, but both approaches ** result in fewer HTTP requests than the separate mode. ** --localauth Connections from localhost are given "setup" ** privileges without having to log in ** --mainmenu FILE Override the mainmenu config setting with the contents ** of the given file ** --nocompress Do not compress HTTP replies ** --nodelay Omit backoffice processing if it would delay ** process exit ** --nojail Drop root privilege but do not enter the chroot jail ** --nossl Do not do http: to https: redirects, regardless of ** the redirect-to-https setting. ** --notfound URL Use URL as the "HTTP 404, object not found" page ** --out FILE Write the HTTP reply to FILE instead of to ** standard output ** --pkey FILE Read the private key used for TLS from FILE ** --repolist If REPOSITORY is directory, URL "/" lists all repos ** --scgi Interpret input as SCGI rather than HTTP ** --skin LABEL Use override skin LABEL. Use an empty string ("") ** to force use of the current local skin config. ** --th-trace Trace TH1 execution (for debugging purposes) ** --usepidkey Use saved encryption key from parent process. This is ** only necessary when using SEE on Windows or Linux. ** ** 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; const char *zInFile; const char *zOutFile; const char *zChRoot; int useSCGI; int noJail; int allowRepoList; Th_InitTraceLog(); builtin_set_js_delivery_mode(find_option("jsmode",0,1),0); /* 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); zChRoot = find_option("chroot",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; g.fNoHttpCompress = find_option("nocompress",0,0)!=0; g.zExtRoot = find_option("extroot",0,1); g.zCkoutAlias = find_option("ckout-alias",0,1); g.zReqType = "HTTP"; zInFile = find_option("in",0,1); if( zInFile ){ backoffice_disable(); g.httpIn = fossil_fopen(zInFile, "rb"); if( g.httpIn==0 ) fossil_fatal("cannot open \"%s\" for reading", zInFile); }else{ g.httpIn = stdin; #if defined(_WIN32) _setmode(_fileno(stdin), _O_BINARY); #endif } zOutFile = find_option("out",0,1); if( zOutFile ){ g.httpOut = fossil_fopen(zOutFile, "wb"); if( g.httpOut==0 ) fossil_fatal("cannot open \"%s\" for writing", zOutFile); }else{ g.httpOut = stdout; #if defined(_WIN32) _setmode(_fileno(stdout), _O_BINARY); #endif } zIpAddr = find_option("ipaddr",0,1); useSCGI = find_option("scgi", 0, 0)!=0; if( useSCGI ) g.zReqType = "SCGI"; zAltBase = find_option("baseurl", 0, 1); if( find_option("nodelay",0,0)!=0 ) backoffice_no_delay(); 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.zMainMenuFile = find_option("mainmenu",0,1); if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){ fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile); } decode_ssl_options(); if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1; /* We should be done with options.. */ verify_all_options(); if( g.httpUseSSL ){ if( useSCGI ){ fossil_fatal("SSL not (yet) supported for SCGI"); } if( g.fSshClient & CGI_SSH_CLIENT ){ fossil_fatal("SSL not compatible with SSH"); } if( zInFile || zOutFile ){ fossil_fatal("SSL usable only on a socket"); } cgi_replace_parameter("HTTPS","on"); } if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); g.cgiOutput = 1; g.fullHttpReply = 1; 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( zChRoot ? zChRoot : g.zRepositoryName, noJail); if( useSCGI ){ cgi_handle_scgi_request(); }else if( g.fSshClient & CGI_SSH_CLIENT ){ ssh_request_loop(zIpAddr, glob_create(zFileGlob)); }else{ #if FOSSIL_ENABLE_SSL if( g.httpUseSSL ){ g.httpSSLConn = ssl_new_server(0); } #endif cgi_handle_http_request(zIpAddr); } process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList); #if FOSSIL_ENABLE_SSL if( g.httpUseSSL && g.httpSSLConn ){ ssl_close_server(g.httpSSLConn); g.httpSSLConn = 0; } #endif /* FOSSIL_ENABLE_SSL */ } /* ** 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 ); } /* ** COMMAND: test-http ** ** Works like the [[http]] command but gives setup permission to all users, ** or whatever permission is described by "--usercap CAP". ** ** This command can used for interactive debugging of web pages. For ** example, one can put a simple HTTP request in a file like this: ** ** echo 'GET /timeline' >request.txt ** ** Then run (in a debugger) a command like this: ** ** fossil test-http --debug <request.txt ** ** This command is also used internally by the "ssh" sync protocol. Some ** special processing to support sync happens when this command is run ** and the SSH_CONNECTION environment variable is set. Use the --test ** option on interactive sessions to avoid that special processing when ** using this command interactively over SSH. A better solution would be ** to use a different command for "ssh" sync, but we cannot do that without ** breaking legacy. ** ** Options: ** --test Do not do special "sync" processing when operating ** over an SSH link ** --th-trace Trace TH1 execution (for debugging purposes) ** --usercap CAP User capability string (Default: "sxy") ** */ void cmd_test_http(void){ const char *zIpAddr; /* IP address of remote client */ const char *zUserCap; int bTest = 0; Th_InitTraceLog(); zUserCap = find_option("usercap",0,1); if( zUserCap==0 ){ g.useLocalauth = 1; zUserCap = "sxy"; } bTest = find_option("test",0,0)!=0; login_set_capabilities(zUserCap, 0); g.httpIn = stdin; g.httpOut = stdout; fossil_binary_mode(g.httpOut); fossil_binary_mode(g.httpIn); g.zExtRoot = find_option("extroot",0,1); find_server_repository(2, 0); g.zReqType = "HTTP"; g.cgiOutput = 1; g.fNoHttpCompress = 1; g.fullHttpReply = 1; g.sslNotAvailable = 1; /* Avoid attempts to redirect */ zIpAddr = bTest ? 0 : 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, 1); } } /* ** Respond to a SIGALRM by writing a message to the error log (if there ** is one) and exiting. */ #ifndef _WIN32 static int nAlarmSeconds = 0; static void sigalrm_handler(int x){ sqlite3_uint64 tmUser = 0, tmKernel = 0; fossil_cpu_times(&tmUser, &tmKernel); if( fossil_strcmp(g.zPhase, "web-page reply")==0 && tmUser+tmKernel<10000000 ){ /* Do not log time-outs during web-page reply unless more than ** 10 seconds of CPU time has been consumed */ return; } fossil_panic("Timeout after %d seconds during %s" " - user %,llu µs, sys %,llu µs", nAlarmSeconds, g.zPhase, tmUser, tmKernel); } #endif /* ** Arrange to timeout using SIGALRM after N seconds. Or if N==0, cancel ** any pending timeout. ** ** Bugs: ** (1) This only works on unix systems. ** (2) Any call to sleep() or sqlite3_sleep() will cancel the alarm. */ void fossil_set_timeout(int N){ #ifndef _WIN32 signal(SIGALRM, sigalrm_handler); alarm(N); nAlarmSeconds = N; #endif } /* ** COMMAND: server* ** COMMAND: ui ** ** Usage: %fossil server ?OPTIONS? ?REPOSITORY? ** or: %fossil ui ?OPTIONS? ?REPOSITORY? ** ** Open a socket and begin listening and responding to HTTP requests on ** TCP port 8080, or on any other TCP port defined by the -P or ** --port option. The optional REPOSITORY argument is the name of the ** Fossil repository to be served. The REPOSITORY argument may be omitted ** if the working directory is within an open check-out, in which case the ** repository associated with that check-out is used. ** ** 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. ** ** If REPOSITORY is a directory name which is the root of a ** check-out, then use the repository associated with that check-out. ** This only works for the "fossil ui" command, not the "fossil server" ** command. ** ** If REPOSITORY begins with a "HOST:" or "USER@HOST:" prefix, then ** the command is run on the remote host specified and the results are ** tunneled back to the local machine via SSH. This feature only works for ** the "fossil ui" command, not the "fossil server" command. The name of the ** fossil executable on the remote host is specified by the --fossilcmd ** option, or if there is no --fossilcmd, it first tries "fossil" and if it ** is not found in the default $PATH set by SSH on the remote, it then adds ** "$HOME/bin:/usr/local/bin:/opt/homebrew/bin" to the PATH and tries again to ** run "fossil". ** ** REPOSITORY may also 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. ** ** For the special case REPOSITORY name of "/", the global configuration ** database is consulted for a list of all known repositories. The --repolist ** option is implied by this special case. The "fossil ui /" command is ** equivalent to "fossil all ui". To see all repositories owned by "user" ** on machine "remote" via ssh, run "fossil ui user@remote:/". ** ** 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: ** --acme Deliver files from the ".well-known" subdirectory ** --baseurl URL Use URL as the base (useful for reverse proxies) ** --cert FILE Use TLS (HTTPS) encryption with the certificate (the ** fullchain.pem) taken from FILE. ** --chroot DIR Use directory for chroot instead of repository path ** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were ** /doc/ckout/... ** --create Create a new REPOSITORY if it does not already exist ** --errorlog FILE Append HTTP error messages to FILE ** --extroot DIR Document root for the /ext extension mechanism ** --files GLOBLIST Comma-separated list of glob patterns for static files ** --fossilcmd PATH The pathname of the "fossil" executable on the remote ** system when REPOSITORY is remote. ** --localauth Enable automatic login for requests from localhost ** --localhost Listen on 127.0.0.1 only (always true for "ui") ** --https Indicates that the input is coming through a reverse ** proxy that has already translated HTTPS into HTTP. ** --jsmode MODE Determine how JavaScript is delivered with pages. ** Mode can be one of: ** inline All JavaScript is inserted inline at ** the end of the HTML file. ** separate Separate HTTP requests are made for ** each JavaScript file. ** bundled One single separate HTTP fetches all ** JavaScript concatenated together. ** Depending on the needs of any given page, inline ** and bundled modes might result in a single ** amalgamated script or several, but both approaches ** result in fewer HTTP requests than the separate mode. ** --mainmenu FILE Override the mainmenu config setting with the contents ** of the given file ** --max-latency N Do not let any single HTTP request run for more than N ** seconds (only works on unix) ** -B|--nobrowser Do not automatically launch a web-browser for the ** "fossil ui" command ** --nocompress Do not compress HTTP replies ** --nojail Drop root privileges but do not enter the chroot jail ** --nossl Do not force redirects to SSL even if the repository ** setting "redirect-to-https" requests it. This is set ** by default for the "ui" command. ** --notfound URL Redirect to URL if a page is not found. ** -p|--page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci" ** --pkey FILE Read the private key used for TLS from FILE ** -P|--port [IP:]PORT Listen on the given IP (optional) and port ** --repolist If REPOSITORY is dir, URL "/" lists repos ** --scgi Accept SCGI rather than HTTP ** --skin LABEL Use override skin LABEL ** --th-trace Trace TH1 execution (for debugging purposes) ** --usepidkey Use saved encryption key from parent process. This is ** only necessary when using SEE on Windows or Linux. ** ** 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) const char *zChRoot; /* Use for chroot instead of repository path */ int noJail; /* Do not enter the chroot jail */ const char *zTimeout = 0; /* Max runtime of any single HTTP request */ #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; /* The --create flag */ int fNoBrowser = 0; /* Do not auto-launch web-browser */ const char *zInitPage = 0; /* Start on this page. --page option */ int findServerArg = 2; /* argv index for find_server_repository() */ char *zRemote = 0; /* Remote host on which to run "fossil ui" */ const char *zJsMode; /* The --jsmode parameter */ const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ #if USE_SEE db_setup_for_saved_encryption_key(); #endif #if defined(_WIN32) const char *zStopperFile; /* Name of file used to terminate server */ zStopperFile = find_option("stopper", 0, 1); #endif if( g.zErrlog==0 ){ g.zErrlog = "-"; } g.zExtRoot = find_option("extroot",0,1); zJsMode = find_option("jsmode",0,1); builtin_set_js_delivery_mode(zJsMode,0); 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) zChRoot = find_option("chroot",0,1); noJail = find_option("nojail",0,0)!=0; zTimeout = find_option("max-latency",0,1); #endif g.useLocalauth = find_option("localauth", 0, 0)!=0; Th_InitTraceLog(); zPort = find_option("port", "P", 1); isUiCmd = g.argv[1][0]=='u'; if( isUiCmd ){ zInitPage = find_option("page", "p", 1); if( zInitPage && zInitPage[0]=='/' ) zInitPage++; zFossilCmd = find_option("fossilcmd", 0, 1); } zNotFound = find_option("notfound", 0, 1); allowRepoList = find_option("repolist",0,0)!=0; if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1; zAltBase = find_option("baseurl", 0, 1); fCreate = find_option("create",0,0)!=0; g.zReqType = "HTTP"; if( find_option("scgi", 0, 0)!=0 ){ g.zReqType = "SCGI"; flags |= HTTP_SERVER_SCGI; } if( zAltBase ){ set_base_url(zAltBase); } g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd; fNoBrowser = find_option("nobrowser", "B", 0)!=0; decode_ssl_options(); if( find_option("https",0,0)!=0 || g.httpUseSSL ){ cgi_replace_parameter("HTTPS","on"); } if( find_option("localhost", 0, 0)!=0 ){ flags |= HTTP_SERVER_LOCALHOST; } g.zCkoutAlias = find_option("ckout-alias",0,1); g.zMainMenuFile = find_option("mainmenu",0,1); if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){ fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile); } if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1; /* Undocumented option: --debug-nofork ** ** This sets the HTTP_SERVER_NOFORK flag, which causes only the ** very first incoming TCP/IP connection to be processed. Used for ** debugging, since debugging across a fork() can be tricky */ if( find_option("debug-nofork",0,0)!=0 ){ flags |= HTTP_SERVER_NOFORK; #if !defined(_WIN32) /* Disable the timeout during debugging */ zTimeout = "100000000"; #endif } /* We should be done with options.. */ verify_all_options(); if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); if( g.httpUseSSL && (flags & HTTP_SERVER_SCGI)!=0 ){ fossil_fatal("SCGI does not (yet) support TLS-encrypted connections"); } if( isUiCmd && 3==g.argc && file_isdir(g.argv[2], ExtFILE)>0 ){ /* If REPOSITORY arg is the root of a check-out, ** chdir to that check-out so that the current version ** gets highlighted in the timeline by default. */ const char * zDir = g.argv[2]; if(dir_has_ckout_db(zDir)){ if(0!=file_chdir(zDir, 0)){ fossil_fatal("Cannot chdir to %s", zDir); } findServerArg = 99; fCreate = 0; g.argv[2] = 0; --g.argc; } } if( isUiCmd && 3==g.argc && (zRemote = (char*)file_skip_userhost(g.argv[2]))!=0 ){ /* The REPOSITORY argument has a USER@HOST: or HOST: prefix */ const char *zRepoTail = file_skip_userhost(g.argv[2]); unsigned x; int n; sqlite3_randomness(2,&x); zPort = mprintf("%d", 8100+(x%32000)); n = (int)(zRepoTail - g.argv[2]) - 1; zRemote = mprintf("%.*s", n, g.argv[2]); g.argv[2] = (char*)zRepoTail; } if( isUiCmd ){ flags |= HTTP_SERVER_LOCALHOST|HTTP_SERVER_REPOLIST; g.useLocalauth = 1; allowRepoList = 1; } if( !zRemote ){ find_server_repository(findServerArg, fCreate); } if( zInitPage==0 ){ if( isUiCmd && g.localOpen ){ zInitPage = "timeline?c=current"; }else{ zInitPage = ""; } } if( zPort ){ if( strchr(zPort,':') ){ int i; for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){} if( i>0 ){ if( zPort[0]=='[' && zPort[i-1]==']' ){ zIpAddr = mprintf("%.*s", i-2, zPort+1); }else{ zIpAddr = mprintf("%.*s", i, zPort); } zPort += i+1; } } iPort = mxPort = atoi(zPort); if( iPort<=0 ) fossil_fatal("port number must be greater than zero"); }else{ iPort = db_get_int("http-port", 8080); mxPort = iPort+100; } if( isUiCmd && !fNoBrowser ){ char *zBrowserArg; const char *zProtocol = g.httpUseSSL ? "https" : "http"; db_open_config(0,0); zBrowser = fossil_web_browser(); if( zIpAddr==0 ){ zBrowserArg = mprintf("%s://localhost:%%d/%s", zProtocol, zInitPage); }else if( strchr(zIpAddr,':') ){ zBrowserArg = mprintf("%s://[%s]:%%d/%s", zProtocol, zIpAddr, zInitPage); }else{ zBrowserArg = mprintf("%s://%s:%%d/%s", zProtocol, zIpAddr, zInitPage); } zBrowserCmd = mprintf("%s %!$ &", zBrowser, zBrowserArg); fossil_free(zBrowserArg); } if( zRemote ){ /* If a USER@HOST:REPO argument is supplied, then use SSH to run ** "fossil ui --nobrowser" on the remote system and to set up a ** tunnel from the local machine to the remote. */ FILE *sshIn; Blob ssh; int bRunning = 0; /* True when fossil starts up on the remote */ int isRetry; /* True if on the second attempt */ char zLine[1000]; blob_init(&ssh, 0, 0); for(isRetry=0; isRetry<2 && !bRunning; isRetry++){ blob_reset(&ssh); transport_ssh_command(&ssh); blob_appendf(&ssh, " -t -L 127.0.0.1:%d:127.0.0.1:%d %!$", iPort, iPort, zRemote ); if( zFossilCmd==0 ){ if( ssh_needs_path_argument(zRemote,-1) ^ isRetry ){ ssh_add_path_argument(&ssh); } blob_append_escaped_arg(&ssh, "fossil", 1); }else{ blob_appendf(&ssh, " %$", zFossilCmd); } blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort); if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound); if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob); if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias); if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot); if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use()); if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode); if( fCreate ) blob_appendf(&ssh, " --create"); blob_appendf(&ssh, " %$", g.argv[2]); if( isRetry ){ fossil_print("First attempt to run \"fossil\" on %s failed\n" "Retry: ", zRemote); } fossil_print("%s\n", blob_str(&ssh)); sshIn = popen(blob_str(&ssh), "r"); if( sshIn==0 ){ fossil_fatal("unable to %s", blob_str(&ssh)); } while( fgets(zLine, sizeof(zLine), sshIn) ){ fputs(zLine, stdout); fflush(stdout); if( !bRunning && sqlite3_strglob("*Listening for HTTP*",zLine)==0 ){ bRunning = 1; if( isRetry ){ ssh_needs_path_argument(zRemote,99); } db_close_config(); if( zBrowserCmd ){ char *zCmd = mprintf(zBrowserCmd/*works-like:"%d"*/,iPort); fossil_system(zCmd); fossil_free(zCmd); fossil_free(zBrowserCmd); zBrowserCmd = 0; } } } pclose(sshIn); } fossil_free(zBrowserCmd); return; } if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY; if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT; db_close(1); #if !defined(_WIN32) if( getpid()==1 ){ /* Modern kernels suppress SIGTERM to PID 1 to prevent root from ** rebooting the system by nuking the init system. The only way ** Fossil becomes that PID 1 is when it's running solo in a Linux ** container or similar, so we do want to exit immediately, to ** allow the container to shut down quickly. ** ** This has to happen ahead of the other signal() calls below. ** They apply after the HTTP hit is handled, but this one needs ** to be registered while we're waiting for that to occur. **/ signal(SIGTERM, fossil_exit); signal(SIGINT, fossil_exit); } #endif /* !WIN32 */ /* Start up an HTTP server */ fossil_setenv("SERVER_SOFTWARE", "fossil version " RELEASE_VERSION " " MANIFEST_VERSION " " MANIFEST_DATE); #if !defined(_WIN32) /* Unix implementation */ if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){ fossil_fatal("unable to listen on TCP socket %d", iPort); } /* For the parent process, the cgi_http_server() command above never ** returns (except in the case of an error). Instead, for each incoming ** client connection, a child process is created, file descriptors 0 ** and 1 are bound to that connection, and the child returns. ** ** So, when control reaches this point, we are running as a ** child process, the HTTP or SCGI request is pending on file ** descriptor 0 and the reply should be written to file descriptor 1. */ if( zTimeout ){ fossil_set_timeout(atoi(zTimeout)); }else{ fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT); } g.httpIn = stdin; g.httpOut = stdout; signal(SIGSEGV, sigsegv_handler); signal(SIGPIPE, sigpipe_handler); if( g.fAnyTrace ){ fprintf(stderr, "/***** Subprocess %d *****/\n", getpid()); } g.cgiOutput = 1; find_server_repository(2, 0); if( fossil_strcmp(g.zRepositoryName,"/")==0 ){ allowRepoList = 1; }else{ g.zRepositoryName = enter_chroot_jail( zChRoot ? zChRoot : g.zRepositoryName, noJail); } if( flags & HTTP_SERVER_SCGI ){ cgi_handle_scgi_request(); }else if( g.httpUseSSL ){ #if FOSSIL_ENABLE_SSL g.httpSSLConn = ssl_new_server(0); #endif cgi_handle_http_request(0); }else{ cgi_handle_http_request(0); } process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList); if( g.fAnyTrace ){ fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n", getpid()); } #if FOSSIL_ENABLE_SSL if( g.httpUseSSL && g.httpSSLConn ){ ssl_close_server(g.httpSSLConn); g.httpSSLConn = 0; } #endif /* FOSSIL_ENABLE_SSL */ #else /* WIN32 */ /* Win32 implementation */ if( allowRepoList ){ flags |= HTTP_SERVER_REPOLIST; } if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){ win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zAltBase, zNotFound, zFileGlob, zIpAddr, flags); } #endif } /* ** COMMAND: test-echo ** ** Usage: %fossil test-echo [--hex] ARGS... ** ** Echo all command-line arguments (enclosed in [...]) to the screen so that ** wildcard expansion behavior of the host shell can be investigated. ** ** With the --hex option, show the output as hexadecimal. This can be used ** to verify the fossil_path_to_utf8() routine on Windows and Mac. */ void test_echo_cmd(void){ int i, j; if( find_option("hex",0,0)==0 ){ fossil_print("g.nameOfExe = [%s]\n", g.nameOfExe); for(i=0; i<g.argc; i++){ fossil_print("argv[%d] = [%s]\n", i, g.argv[i]); } }else{ unsigned char *z, c; for(i=0; i<g.argc; i++){ fossil_print("argv[%d] = [", i); z = (unsigned char*)g.argv[i]; for(j=0; (c = z[j])!=0; j++){ fossil_print("%02x", c); } fossil_print("]\n"); } } } /* ** WEBPAGE: test-warning ** ** Test error and warning log operation. This webpage is accessible to ** the administrator only. ** ** case=1 Issue a fossil_warning() while generating the page. ** case=2 Extra db_begin_transaction() ** case=3 Extra db_end_transaction() ** case=4 Error during SQL processing ** case=5 Call the segfault handler ** case=6 Call webpage_assert() ** case=7 Call webpage_error() */ void test_warning_page(void){ int iCase = atoi(PD("case","0")); int i; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_set_current_feature("test"); style_header("Warning Test Page"); style_submenu_element("Error Log","%R/errorlog"); if( iCase<1 || iCase>4 ){ @ <p>Generate a message to the <a href="%R/errorlog">error log</a> @ by clicking on one of the following cases: }else{ @ <p>This is the test page for case=%d(iCase). All possible cases: } for(i=1; i<=8; i++){ @ <a href='./test-warning?case=%d(i)'>[%d(i)]</a> } @ </p> @ <p><ol> @ <li value='1'> Call fossil_warning() if( iCase==1 ){ fossil_warning("Test warning message from /test-warning"); } @ <li value='2'> Call db_begin_transaction() if( iCase==2 ){ db_begin_transaction(); } @ <li value='3'> Call db_end_transaction() if( iCase==3 ){ db_end_transaction(0); } @ <li value='4'> warning during SQL if( iCase==4 ){ Stmt q; db_prepare(&q, "SELECT uuid FROM blob LIMIT 5"); db_step(&q); sqlite3_log(SQLITE_ERROR, "Test warning message during SQL"); db_finalize(&q); } @ <li value='5'> simulate segfault handling if( iCase==5 ){ sigsegv_handler(0); } @ <li value='6'> call webpage_assert(0) if( iCase==6 ){ webpage_assert( 5==7 ); } @ <li value='7'> call webpage_error()" if( iCase==7 ){ cgi_reset_content(); webpage_error("Case 7 from /test-warning"); } @ <li value='8'> simulated timeout" if( iCase==8 ){ fossil_set_timeout(1); cgi_reset_content(); sqlite3_sleep(1100); } @ </ol> @ <p>End of test</p> style_finish_page(); } |
Changes to src/main.mk.
|
| > > | > | | < < > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > | > | | | > > > > | | > | | | | > | | | > | | | > | | | > | | > > > > | > | > > > > > > | > > > | | > | > | > | > > > > > > | > | > > > > > > > > > > > > > | | | > | > > > > | > | | > > | > > | | | | > | > | > > | > | > > > | > | | | > > | | | | | > > | | | > > | | | | > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < | < < | > | > > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > > > > | | > > > > > > > > > | | > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < | | > | | | | > | > > > > > > > > > > > > > > > > | | | | | > | | > > > | > > > > > | > | > > > > > > > > | | | | > | > > > > > > > > | | | | > | | | | | | > | | > > > > > > > > > > > > > > > > > > > > > > > | > | > | > > > > > > > > | | | | | > | | > > > | > > > > > | > | | | | | | > | > > > > > > > > > > > > > > > > > > > > > > > > | | | > | | | > > | > > > > > > | > | | | > > | > > > > > > | > > > > | > > | > > > | | | > | | | > > | > > > > > > | > > > > > > > > > | | | | | > | | | > > | > > > > > > | > | | | > > > > > > > > | | > | | | > > | > > > > > > | > | | | > > | > > > > > > | > | | | | | > | > > | > > > > > > | > > | > > > > > > | > | | | | | > | | | > > | > > > > > > | > | | | > > | > > > > > > | > > > > | > > | > > > | > > | > > > > > > | > | | | | | > | | | | | > | | | | | | > > > > > > > > > | | | | | > > > > > > > > > | | | | > | > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > | > | > | > > | > > > > > > | > > | > > > > > > | > > > > > > > > > | > > | > > > > > > | > > | > > > > > > | > | | | | | > > > > > > > | > | > | > > | > > > > > > | > | | | > > | > > > > > > | > | | | > > | > > > > > > | > | | | | | > | > > | > > > > > > | > > | > > > > > > | > | | | > > | > > > > > > | > | | | > > > > > > > | > | > | | | > > | > > > > > > | > | | | > > | > > > > > > | > | > > | > > > > > > | > > | > > > > > > | > | > > | > > > > > > | > > | > > > > > > | > | | | | | > | | | > > | > > > > > > | > > > > | > > | > > > | > > | > > > > > > | > | | | | | > > > > > > > > > | > > > > > > > > | | > > | > > > > > > | > | | | | | > | | | > > | > > > > > > | > | | | | | > | > > | > > > > > > | > > > > | > > > > | > | | | > > | > > > > > > | > | | | > > > > > > > > > > | > > > > > > | > | | | > > | > > > > > > | > | | | > > > > > > > | > | > | | | > > | > > > > > > | > | | | > > | > > > > > > > > > > > > > > | > | | | > > | > > > > > > > > > > > > > > | > | | | | | > | | | > > | > > > > > > | > | | | > > | > > > > > > | > > > > | > > > > > > > > > > > > > | | | | | > | | | | > > > > > > > > | > | | | | > | > > > > > > > > | | | | | | > > > > > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "tools/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. # XBCC = $(BCC) $(BCCFLAGS) XTCC = $(TCC) $(CFLAGS_INCLUDE) -I$(OBJDIR) $(TCCFLAGS) TESTFLAGS := -quiet SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/ajax.c \ $(SRCDIR)/alerts.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/backlink.c \ $(SRCDIR)/backoffice.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)/capabilities.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/chat.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/color.c \ $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/deltafunc.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/extcgi.c \ $(SRCDIR)/file.c \ $(SRCDIR)/fileedit.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/forum.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/fuzz.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/hname.c \ $(SRCDIR)/hook.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/import.c \ $(SRCDIR)/info.c \ $(SRCDIR)/interwiki.c \ $(SRCDIR)/json.c \ $(SRCDIR)/json_artifact.c \ $(SRCDIR)/json_branch.c \ $(SRCDIR)/json_config.c \ $(SRCDIR)/json_diff.c \ $(SRCDIR)/json_dir.c \ $(SRCDIR)/json_finfo.c \ $(SRCDIR)/json_login.c \ $(SRCDIR)/json_query.c \ $(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)/patch.c \ $(SRCDIR)/path.c \ $(SRCDIR)/piechart.c \ $(SRCDIR)/pikchrshow.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)/repolist.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/setupuser.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/sha1hard.c \ $(SRCDIR)/sha3.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/smtp.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)/terminal.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ $(SRCDIR)/unicode.c \ $(SRCDIR)/unversioned.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)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c EXTRA_FILES = \ $(SRCDIR)/../extsrc/pikchr-worker.js \ $(SRCDIR)/../extsrc/pikchr.js \ $(SRCDIR)/../extsrc/pikchr.wasm \ $(SRCDIR)/../skins/ardoise/css.txt \ $(SRCDIR)/../skins/ardoise/details.txt \ $(SRCDIR)/../skins/ardoise/footer.txt \ $(SRCDIR)/../skins/ardoise/header.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/darkmode/css.txt \ $(SRCDIR)/../skins/darkmode/details.txt \ $(SRCDIR)/../skins/darkmode/footer.txt \ $(SRCDIR)/../skins/darkmode/header.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/etienne/css.txt \ $(SRCDIR)/../skins/etienne/details.txt \ $(SRCDIR)/../skins/etienne/footer.txt \ $(SRCDIR)/../skins/etienne/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/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/accordion.js \ $(SRCDIR)/alerts/bflat2.wav \ $(SRCDIR)/alerts/bflat3.wav \ $(SRCDIR)/alerts/bloop.wav \ $(SRCDIR)/alerts/plunk.wav \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/copybtn.js \ $(SRCDIR)/default.css \ $(SRCDIR)/diff.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/fossil.bootstrap.js \ $(SRCDIR)/fossil.confirmer.js \ $(SRCDIR)/fossil.copybutton.js \ $(SRCDIR)/fossil.diff.js \ $(SRCDIR)/fossil.dom.js \ $(SRCDIR)/fossil.fetch.js \ $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.brlist.js \ $(SRCDIR)/fossil.page.chat.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.pikchrshow.js \ $(SRCDIR)/fossil.page.pikchrshowasm.js \ $(SRCDIR)/fossil.page.whistory.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.pikchr.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ $(SRCDIR)/sounds/4.wav \ $(SRCDIR)/sounds/5.wav \ $(SRCDIR)/sounds/6.wav \ $(SRCDIR)/sounds/7.wav \ $(SRCDIR)/sounds/8.wav \ $(SRCDIR)/sounds/9.wav \ $(SRCDIR)/sounds/a.wav \ $(SRCDIR)/sounds/b.wav \ $(SRCDIR)/sounds/c.wav \ $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.chat.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/style.pikchrshow.css \ $(SRCDIR)/style.wikiedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/ajax_.c \ $(OBJDIR)/alerts_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/backlink_.c \ $(OBJDIR)/backoffice_.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)/capabilities_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/chat_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/color_.c \ $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/deltafunc_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/extcgi_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/fileedit_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/forum_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/fuzz_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/hook_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ $(OBJDIR)/import_.c \ $(OBJDIR)/info_.c \ $(OBJDIR)/interwiki_.c \ $(OBJDIR)/json_.c \ $(OBJDIR)/json_artifact_.c \ $(OBJDIR)/json_branch_.c \ $(OBJDIR)/json_config_.c \ $(OBJDIR)/json_diff_.c \ $(OBJDIR)/json_dir_.c \ $(OBJDIR)/json_finfo_.c \ $(OBJDIR)/json_login_.c \ $(OBJDIR)/json_query_.c \ $(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)/patch_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/piechart_.c \ $(OBJDIR)/pikchrshow_.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)/repolist_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/setupuser_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/sha1hard_.c \ $(OBJDIR)/sha3_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/smtp_.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)/terminal_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ $(OBJDIR)/unicode_.c \ $(OBJDIR)/unversioned_.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)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/ajax.o \ $(OBJDIR)/alerts.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/backlink.o \ $(OBJDIR)/backoffice.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)/capabilities.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/chat.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/color.o \ $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/deltafunc.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/extcgi.o \ $(OBJDIR)/file.o \ $(OBJDIR)/fileedit.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/forum.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/fuzz.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/hook.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/import.o \ $(OBJDIR)/info.o \ $(OBJDIR)/interwiki.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)/patch.o \ $(OBJDIR)/path.o \ $(OBJDIR)/piechart.o \ $(OBJDIR)/pikchrshow.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)/repolist.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/setupuser.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/sha1hard.o \ $(OBJDIR)/sha3.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/smtp.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)/terminal.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ $(OBJDIR)/unicode.o \ $(OBJDIR)/unversioned.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)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o all: $(OBJDIR) $(APPNAME) install: all mkdir -p $(INSTALLDIR) cp $(APPNAME) $(INSTALLDIR) codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1 $(OBJDIR)/codecheck1 $(TRANS_SRC) $(OBJDIR): -mkdir $(OBJDIR) $(OBJDIR)/translate: $(SRCDIR_tools)/translate.c $(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c $(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c $(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c $(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c $(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c $(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c $(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c $(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c $(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c $(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c # Run the test suite. # Other flags that can be included in TESTFLAGS are: # # -halt Stop testing after the first failed test # -keep Keep the temporary workspace for debugging # -prot Write a detailed log of the tests to the file ./prot # -verbose Include even more details in the output # -quiet Hide most output from the terminal # -strict Treat known bugs as failures # # TESTFLAGS can also include names of specific test files to limit # the run to just those test cases. # test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \ $(SRCDIR)/../manifest \ $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h $(OBJDIR)/phony.h: # Force rebuild of VERSION.h every time we run "make" # Setup the options used to compile the included SQLite library. SQLITE_OPTIONS = -DNDEBUG=1 \ -DSQLITE_DQS=0 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ -DSQLITE_OMIT_DECLTYPE \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_PROGRESS_CALLBACK \ -DSQLITE_OMIT_SHARED_CACHE \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_MAX_EXPR_DEPTH=0 \ -DSQLITE_ENABLE_LOCKING_STYLE=0 \ -DSQLITE_DEFAULT_FILE_FORMAT=4 \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_DBSTAT_VTAB \ -DSQLITE_ENABLE_FTS5 \ -DSQLITE_ENABLE_STMTVTAB \ -DSQLITE_HAVE_ZLIB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_TRUSTED_SCHEMA=0 \ -DHAVE_USLEEP # Setup the options used to compile the included SQLite shell. SHELL_OPTIONS = -DNDEBUG=1 \ -DSQLITE_DQS=0 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ -DSQLITE_OMIT_DECLTYPE \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_PROGRESS_CALLBACK \ -DSQLITE_OMIT_SHARED_CACHE \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_MAX_EXPR_DEPTH=0 \ -DSQLITE_ENABLE_LOCKING_STYLE=0 \ -DSQLITE_DEFAULT_FILE_FORMAT=4 \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_DBSTAT_VTAB \ -DSQLITE_ENABLE_FTS5 \ -DSQLITE_ENABLE_STMTVTAB \ -DSQLITE_HAVE_ZLIB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_TRUSTED_SCHEMA=0 \ -DHAVE_USLEEP \ -Dmain=sqlite3_shell \ -DSQLITE_SHELL_IS_UTF8=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname \ -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc # Setup the options used to compile the included Pikchr formatter. PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 # The USE_SYSTEM_SQLITE variable may be undefined, set to 0 or 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. # # Closely related is SQLITE3_ORIGIN, with the same numeric mapping plus # a value of 2 means that we are building a client-provided sqlite3.c. SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ.1 = $(OBJDIR)/sqlite3-see.o # SQLITE3_OBJ.2 is set by the configure process SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_LINENOISE variable may be undefined, set to 0, or set # to 1. If it is set to 0, then there is no need to build or link # the linenoise.o object. LINENOISE_DEF.0 = LINENOISE_DEF.1 = -DHAVE_LINENOISE LINENOISE_DEF. = $(LINENOISE_DEF.0) LINENOISE_OBJ.0 = LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o LINENOISE_OBJ. = $(LINENOISE_OBJ.0) # The USE_SEE variable may be undefined, 0 or 1. If undefined or 0, # in-tree SQLite is used. If 1, then sqlite3-see.c (not part of the # source tree) is used and extra flags are provided to enable the # SQLite Encryption Extension. SQLITE3_SRC.0 = $(SRCDIR_extsrc)/sqlite3.c SQLITE3_SRC.1 = $(SRCDIR_extsrc)/sqlite3-see.c # SQLITE3_SRC.2 is set by top-level configure/makefile process. SQLITE3_SRC. = $(SRCDIR_extsrc)/sqlite3.c SQLITE3_SRC = $(SQLITE3_SRC.$(SQLITE3_ORIGIN)) SQLITE3_SHELL_SRC.0 = $(SRCDIR_extsrc)/shell.c SQLITE3_SHELL_SRC.1 = $(SRCDIR_extsrc)/shell-see.c # SQLITE3_SHELL_SRC.2 comes from the configure process SQLITE3_SHELL_SRC. = $(SRCDIR_extsrc)/shell.c SQLITE3_SHELL_SRC = $(SQLITE3_SHELL_SRC.$(SQLITE3_ORIGIN)) SEE_FLAGS.0 = SEE_FLAGS.1 = -DSQLITE_HAS_CODEC -DSQLITE_SHELL_DBKEY_PROC=fossil_key SEE_FLAGS. = SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE)) EXTRAOBJ = \ $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) \ $(LINENOISE_OBJ.$(USE_LINENOISE)) \ $(OBJDIR)/pikchr.o \ $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ $(OBJDIR)/th_lang.o \ $(OBJDIR)/th_tcl.o \ $(OBJDIR)/cson_amalgamation.o $(APPNAME): $(OBJDIR)/headers $(OBJDIR)/codecheck1 $(EXTRAOBJ) $(OBJ) $(OBJDIR)/codecheck1 $(TRANS_SRC) $(TCC) $(TCCFLAGS) -o $(APPNAME) $(EXTRAOBJ) $(OBJ) $(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)/ajax_.c:$(OBJDIR)/ajax.h \ $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.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)/capabilities_.c:$(OBJDIR)/capabilities.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/chat_.c:$(OBJDIR)/chat.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)/color_.c:$(OBJDIR)/color.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/deltafunc_.c:$(OBJDIR)/deltafunc.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/extcgi_.c:$(OBJDIR)/extcgi.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/fileedit_.c:$(OBJDIR)/fileedit.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/fuzz_.c:$(OBJDIR)/fuzz.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/hook_.c:$(OBJDIR)/hook.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)/interwiki_.c:$(OBJDIR)/interwiki.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)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/piechart_.c:$(OBJDIR)/piechart.h \ $(OBJDIR)/pikchrshow_.c:$(OBJDIR)/pikchrshow.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)/repolist_.c:$(OBJDIR)/repolist.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)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/setupuser_.c:$(OBJDIR)/setupuser.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/sha1hard_.c:$(OBJDIR)/sha1hard.h \ $(OBJDIR)/sha3_.c:$(OBJDIR)/sha3.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/smtp_.c:$(OBJDIR)/smtp.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)/terminal_.c:$(OBJDIR)/terminal.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)/unversioned_.c:$(OBJDIR)/unversioned.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)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ $(SRCDIR_extsrc)/pikchr.c:$(OBJDIR)/pikchr.h \ $(SRCDIR_extsrc)/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)/ajax_.c: $(SRCDIR)/ajax.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/ajax.c >$@ $(OBJDIR)/ajax.o: $(OBJDIR)/ajax_.c $(OBJDIR)/ajax.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/ajax.o -c $(OBJDIR)/ajax_.c $(OBJDIR)/ajax.h: $(OBJDIR)/headers $(OBJDIR)/alerts_.c: $(SRCDIR)/alerts.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/alerts.c >$@ $(OBJDIR)/alerts.o: $(OBJDIR)/alerts_.c $(OBJDIR)/alerts.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/alerts.o -c $(OBJDIR)/alerts_.c $(OBJDIR)/alerts.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)/backlink_.c: $(SRCDIR)/backlink.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/backlink.c >$@ $(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h: $(OBJDIR)/headers $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/backoffice.c >$@ $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/backoffice.o -c $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.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)/capabilities_.c: $(SRCDIR)/capabilities.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/capabilities.c >$@ $(OBJDIR)/capabilities.o: $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/capabilities.o -c $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.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)/chat_.c: $(SRCDIR)/chat.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/chat.c >$@ $(OBJDIR)/chat.o: $(OBJDIR)/chat_.c $(OBJDIR)/chat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/chat.o -c $(OBJDIR)/chat_.c $(OBJDIR)/chat.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)/color_.c: $(SRCDIR)/color.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/color.c >$@ $(OBJDIR)/color.o: $(OBJDIR)/color_.c $(OBJDIR)/color.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/color.o -c $(OBJDIR)/color_.c $(OBJDIR)/color.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)/cookies_.c: $(SRCDIR)/cookies.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/cookies.c >$@ $(OBJDIR)/cookies.o: $(OBJDIR)/cookies_.c $(OBJDIR)/cookies.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cookies.o -c $(OBJDIR)/cookies_.c $(OBJDIR)/cookies.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)/deltafunc_.c: $(SRCDIR)/deltafunc.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/deltafunc.c >$@ $(OBJDIR)/deltafunc.o: $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltafunc.o -c $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.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)/dispatch_.c: $(SRCDIR)/dispatch.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/dispatch.c >$@ $(OBJDIR)/dispatch.o: $(OBJDIR)/dispatch_.c $(OBJDIR)/dispatch.h $(OBJDIR)/page_index.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/dispatch.o -c $(OBJDIR)/dispatch_.c $(OBJDIR)/dispatch.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)/etag_.c: $(SRCDIR)/etag.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/etag.c >$@ $(OBJDIR)/etag.o: $(OBJDIR)/etag_.c $(OBJDIR)/etag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/etag.o -c $(OBJDIR)/etag_.c $(OBJDIR)/etag.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)/extcgi_.c: $(SRCDIR)/extcgi.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/extcgi.c >$@ $(OBJDIR)/extcgi.o: $(OBJDIR)/extcgi_.c $(OBJDIR)/extcgi.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/extcgi.o -c $(OBJDIR)/extcgi_.c $(OBJDIR)/extcgi.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)/fileedit_.c: $(SRCDIR)/fileedit.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/fileedit.c >$@ $(OBJDIR)/fileedit.o: $(OBJDIR)/fileedit_.c $(OBJDIR)/fileedit.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fileedit.o -c $(OBJDIR)/fileedit_.c $(OBJDIR)/fileedit.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)/forum_.c: $(SRCDIR)/forum.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/forum.c >$@ $(OBJDIR)/forum.o: $(OBJDIR)/forum_.c $(OBJDIR)/forum.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/forum.o -c $(OBJDIR)/forum_.c $(OBJDIR)/forum.h: $(OBJDIR)/headers $(OBJDIR)/fshell_.c: $(SRCDIR)/fshell.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/fshell.c >$@ $(OBJDIR)/fshell.o: $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fshell.o -c $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.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)/fuzz_.c: $(SRCDIR)/fuzz.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/fuzz.c >$@ $(OBJDIR)/fuzz.o: $(OBJDIR)/fuzz_.c $(OBJDIR)/fuzz.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fuzz.o -c $(OBJDIR)/fuzz_.c $(OBJDIR)/fuzz.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)/hname_.c: $(SRCDIR)/hname.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/hname.c >$@ $(OBJDIR)/hname.o: $(OBJDIR)/hname_.c $(OBJDIR)/hname.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/hname.o -c $(OBJDIR)/hname_.c $(OBJDIR)/hname.h: $(OBJDIR)/headers $(OBJDIR)/hook_.c: $(SRCDIR)/hook.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/hook.c >$@ $(OBJDIR)/hook.o: $(OBJDIR)/hook_.c $(OBJDIR)/hook.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/hook.o -c $(OBJDIR)/hook_.c $(OBJDIR)/hook.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)/interwiki_.c: $(SRCDIR)/interwiki.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/interwiki.c >$@ $(OBJDIR)/interwiki.o: $(OBJDIR)/interwiki_.c $(OBJDIR)/interwiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/interwiki.o -c $(OBJDIR)/interwiki_.c $(OBJDIR)/interwiki.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 $(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)/patch_.c: $(SRCDIR)/patch.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/patch.c >$@ $(OBJDIR)/patch.o: $(OBJDIR)/patch_.c $(OBJDIR)/patch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/patch.o -c $(OBJDIR)/patch_.c $(OBJDIR)/patch.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)/piechart_.c: $(SRCDIR)/piechart.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/piechart.c >$@ $(OBJDIR)/piechart.o: $(OBJDIR)/piechart_.c $(OBJDIR)/piechart.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/piechart.o -c $(OBJDIR)/piechart_.c $(OBJDIR)/piechart.h: $(OBJDIR)/headers $(OBJDIR)/pikchrshow_.c: $(SRCDIR)/pikchrshow.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/pikchrshow.c >$@ $(OBJDIR)/pikchrshow.o: $(OBJDIR)/pikchrshow_.c $(OBJDIR)/pikchrshow.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pikchrshow.o -c $(OBJDIR)/pikchrshow_.c $(OBJDIR)/pikchrshow.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)/repolist_.c: $(SRCDIR)/repolist.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/repolist.c >$@ $(OBJDIR)/repolist.o: $(OBJDIR)/repolist_.c $(OBJDIR)/repolist.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/repolist.o -c $(OBJDIR)/repolist_.c $(OBJDIR)/repolist.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)/security_audit_.c: $(SRCDIR)/security_audit.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/security_audit.c >$@ $(OBJDIR)/security_audit.o: $(OBJDIR)/security_audit_.c $(OBJDIR)/security_audit.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/security_audit.o -c $(OBJDIR)/security_audit_.c $(OBJDIR)/security_audit.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)/setupuser_.c: $(SRCDIR)/setupuser.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/setupuser.c >$@ $(OBJDIR)/setupuser.o: $(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setupuser.o -c $(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.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)/sha1hard_.c: $(SRCDIR)/sha1hard.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/sha1hard.c >$@ $(OBJDIR)/sha1hard.o: $(OBJDIR)/sha1hard_.c $(OBJDIR)/sha1hard.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha1hard.o -c $(OBJDIR)/sha1hard_.c $(OBJDIR)/sha1hard.h: $(OBJDIR)/headers $(OBJDIR)/sha3_.c: $(SRCDIR)/sha3.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/sha3.c >$@ $(OBJDIR)/sha3.o: $(OBJDIR)/sha3_.c $(OBJDIR)/sha3.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha3.o -c $(OBJDIR)/sha3_.c $(OBJDIR)/sha3.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)/smtp_.c: $(SRCDIR)/smtp.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/smtp.c >$@ $(OBJDIR)/smtp.o: $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/smtp.o -c $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.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)/terminal_.c: $(SRCDIR)/terminal.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/terminal.c >$@ $(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.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)/unversioned_.c: $(SRCDIR)/unversioned.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/unversioned.c >$@ $(OBJDIR)/unversioned.o: $(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/unversioned.o -c $(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.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)/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 $(SQLITE3_OBJ): $(SQLITE3_SRC) $(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) \ -c $(SQLITE3_SRC) -o $@ $(OBJDIR)/shell.o: $(SQLITE3_SHELL_SRC) $(SRCDIR_extsrc)/sqlite3.h $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) $(SEE_FLAGS) $(LINENOISE_DEF.$(USE_LINENOISE)) -c $(SQLITE3_SHELL_SRC) -o $@ $(OBJDIR)/linenoise.o: $(SRCDIR_extsrc)/linenoise.c $(SRCDIR_extsrc)/linenoise.h $(XTCC) -c $(SRCDIR_extsrc)/linenoise.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)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore \ -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \ -sENVIRONMENT=web \ -sMODULARIZE \ -sEXPORT_NAME=initPikchrModule \ --minify 0 @chmod -x $(SRCDIR_extsrc)/pikchr.wasm wasm: $(SRCDIR_extsrc)/pikchr.js # # compile_commands.json support... # # We have to avoid applying compile_commands support to the in-tree # tools, as those compile with BCC, which may differ from TCC. # e.g. BCC might be gcc (which does not support -MJ ...) while TCC is # clang (which does). # # What follows is more verbose than strictly necessary because we're # limited to POSIX make syntax. all: compile-commands-dir.yes = $(OBJDIR) compile-commands-dir.no = compile-commands-dir = $(compile-commands-dir.$(MAKE_COMPILATION_DB)) compile-command-args.yes = -MJ $(TOPDIR)/$(compile-commands-dir)/$(@F:.o=.o.json) compile-command-args.no = TCCFLAGS += $(compile-command-args.$(MAKE_COMPILATION_DB)) compile_commands.json = $(TOPDIR)/compile_commands.json # compile_commands.json is a concatenation of the .o.json files # generated by the compilation process via TCCFLAGS. We have a # potential race condition in parallel builds, where a .o.json file is # not yet written to completion before compile_commands.json is # processed. How to resolve that in a way compatible with POSIX make # is unclear. # # This obscure sed bit ensures that the resulting JSON array does not # have a trailing comma. $(compile_commands.json): $(OBJ) @-rm -f $@ @{ echo '['; cat $(compile-commands-dir)/*.o.json | tr '\n' ' ' | sed -e 's/, $$//'; echo ']'; } > $@ @echo "Generated $@" compile-commands.no: compile-commands.yes: $(compile_commands.json) all: compile-commands.$(MAKE_COMPILATION_DB) clean: compile-commands-clean compile-commands-clean: rm -fr $(compile_commands.json) # # End compile_commands.json support # # # 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 .PHONY: compile-commands-clean compile-commands-dir |
Deleted src/makeheaders.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/makeheaders.html.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted src/makemake.tcl.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to src/manifest.c.
︙ | ︙ | |||
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 | #include "manifest.h" #include <assert.h> #if INTERFACE /* ** Types of control files */ #define CFTYPE_MANIFEST 1 #define CFTYPE_CLUSTER 2 #define CFTYPE_CONTROL 3 #define CFTYPE_WIKI 4 #define CFTYPE_TICKET 5 #define CFTYPE_ATTACHMENT 6 /* ** A parsed manifest or cluster. */ struct Manifest { Blob content; /* The original content blob */ int type; /* Type of artifact. One of CFTYPE_xxxxx */ char *zComment; /* Decoded comment. The C card. */ double rDate; /* Date and time from D card. 0.0 if no D card. */ char *zUser; /* Name of the user from the U card. */ char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ char *zWiki; /* Text of the wiki page. W card. */ char *zWikiTitle; /* Name of the wiki page. L card. */ char *zTicketUuid; /* UUID for a ticket. K card. */ char *zAttachName; /* Filename of an attachment. A card. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > < | < < < < | | > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | | | | | | | | < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | | > | < < | > | | > > > > > > < < | < < | < > | < < < | < < | | | > > | < < < < | < > | < > | | < > > > > > > > > > > > > > > > > > > | > | < | < < | > | | | | > | > > > > > > > > > | > | > > > > > > > | | < < < < < | < < < > | < > > > > > > > > | | | | | < < > | < > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > > > > < | | < | | | < | | > < < < < < < | | > > > > > < | < < | > < > > | | | < | | < | > | | < | | > > > > > > > > > > > > > > | | > > < | | < | < > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | < < < | > | > | | < | < < < < < | | | | < < | < | | | > < > < > > | < > | < > | < > > > < | | > < < | > < | | | > > | > > | | < < | | | | > < < | < | | < < < < < | | | | | | | | > > > | > | | > > | | | > | < | | | | | | > | > | > | | < < < | | < < < < > | | | < > | | < > | < > | | | > | < | > > | < > | > | | > > | > > > | > > | | < > | < < < < < < < | > > > | < > > | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > | > | | | > | > > > > > | < | > > > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include "manifest.h" #include <assert.h> #if INTERFACE /* ** Types of control files */ #define CFTYPE_ANY 0 #define CFTYPE_MANIFEST 1 #define CFTYPE_CLUSTER 2 #define CFTYPE_CONTROL 3 #define CFTYPE_WIKI 4 #define CFTYPE_TICKET 5 #define CFTYPE_ATTACHMENT 6 #define CFTYPE_EVENT 7 #define CFTYPE_FORUM 8 /* ** 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; /* Artifact hash for the file */ char *zPerm; /* File permissions */ char *zPrior; /* Prior name if the name was changed */ }; /* ** A parsed manifest or cluster. */ struct Manifest { Blob content; /* The original content blob */ int type; /* Type of artifact. One of CFTYPE_xxxxx */ int rid; /* The blob-id for this manifest */ const char *zBaseline;/* Baseline manifest. The B card. */ Manifest *pBaseline; /* The actual baseline manifest */ char *zComment; /* Decoded comment. The C card. */ double rDate; /* Date and time from D card. 0.0 if no D card. */ char *zUser; /* Name of the user from the U card. */ char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ char *zWiki; /* Text of the wiki page. W card. */ char *zWikiTitle; /* Name of the wiki page. L card. */ char *zMimetype; /* Mime type of wiki or comment text. N card. */ char *zThreadTitle; /* The forum thread title. H card */ double rEventDate; /* Date of an event. E card. */ char *zEventId; /* Artifact hash for an event. E card. */ char *zTicketUuid; /* UUID for a ticket. K card. */ char *zAttachName; /* Filename of an attachment. A card. */ char *zAttachSrc; /* Artifact hash for document being attached. A card. */ char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ char *zThreadRoot; /* Thread root artifact. G card */ char *zInReplyTo; /* Forum in-reply-to artifact. I card */ int nFile; /* Number of F cards */ 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; /* Hashes of parents. One for each P card argument */ int nCherrypick; /* Number of entries in aCherrypick[] */ struct { char *zCPTarget; /* Hash for cherry-picked version w/ +|- prefix */ char *zCPBase; /* Hash for cherry-pick baseline. NULL for singletons */ } *aCherrypick; int nCChild; /* Number of cluster children */ int nCChildAlloc; /* Number of closts allocated in azCChild[] */ char **azCChild; /* Hashes 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; /* Hash of artifact 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 /* ** Allowed and required card types in each style of artifact */ static struct { const char *zAllowed; /* Allowed cards. Human-readable */ const char *zRequired; /* Required cards. Human-readable */ } manifestCardTypes[] = { /* Allowed Required */ /* CFTYPE_MANIFEST 1 */ { "BCDFNPQRTUZ", "DZ" }, /* Wants to be "CDUZ" ----^^^^ ** but we must limit for historical compatibility */ /* CFTYPE_CLUSTER 2 */ { "MZ", "MZ" }, /* CFTYPE_CONTROL 3 */ { "DTUZ", "DTUZ" }, /* CFTYPE_WIKI 4 */ { "CDLNPUWZ", "DLUWZ" }, /* CFTYPE_TICKET 5 */ { "DJKUZ", "DJKUZ" }, /* CFTYPE_ATTACHMENT 6 */ { "ACDNUZ", "ADZ" }, /* CFTYPE_EVENT 7 */ { "CDENPTUWZ", "DEWZ" }, /* CFTYPE_FORUM 8 */ { "DGHINPUWZ", "DUWZ" }, }; /* ** Names of manifest types */ static const char *const azNameOfMType[] = { "manifest", "cluster", "tag", "wiki", "ticket", "attachment", "technote", "forum post" }; /* ** A cache of parsed manifests. This reduces the number of ** calls to manifest_parse() when doing a rebuild. */ #define MX_MANIFEST_CACHE 6 static struct { int nxAge; int aAge[MX_MANIFEST_CACHE]; Manifest *apManifest[MX_MANIFEST_CACHE]; } manifestCache; /* ** True if manifest_crosslink_begin() has been called but ** manifest_crosslink_end() is still pending. */ static int manifest_crosslink_busy = 0; /* ** There are some triggers that need to fire whenever new content ** is added to the EVENT table, to make corresponding changes to the ** PENDING_ALERT and CHAT tables. These are done with TEMP triggers ** which are created as needed. The reasons for using TEMP triggers: ** ** * A small minority of invocations of Fossil need to use those triggers. ** So we save CPU cycles in the common case by not having to parse the ** trigger definition ** ** * We don't have to worry about dangling table references inside ** of triggers. For example, we can create a trigger that adds ** to the CHAT table. But an admin can still drop that CHAT table ** at any moment, since the trigger that refers to CHAT is a TEMP ** trigger and won't persist to cause problems. ** ** * Because TEMP triggers are defined by the specific version of the ** application that is running, we don't have to worry with legacy ** compatibility of the triggers. ** ** This boolean variable is set when the TEMP triggers for EVENT ** have been created. */ static int manifest_event_triggers_are_enabled = 0; /* ** 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); } } /* ** Given a string of upper-case letters, compute a mask of the letters ** present. For example, "ABC" computes 0x0007. "DE" gives 0x0018". */ static unsigned int manifest_card_mask(const char *z){ unsigned int m = 0; char c; while( (c = *(z++))>='A' && c<='Z' ){ m |= 1 << (c - 'A'); } return m; } /* ** Given an integer mask representing letters A-Z, return the ** letter which is the first bit set in the mask. Example: ** 0x03520 gives 'F' since the F-bit is the lowest. */ static char maskToType(unsigned int x){ char c = 'A'; if( x==0 ) return '?'; while( (x&1)==0 ){ x >>= 1; c++; } return c; } /* ** Add an element to the manifest cache using LRU replacement. */ void manifest_cache_insert(Manifest *p){ while( p ){ int i; Manifest *pBaseline = p->pBaseline; p->pBaseline = 0; for(i=0; i<MX_MANIFEST_CACHE; i++){ if( manifestCache.apManifest[i]==0 ) break; } if( i>=MX_MANIFEST_CACHE ){ int oldest = 0; int oldestAge = manifestCache.aAge[0]; for(i=1; i<MX_MANIFEST_CACHE; i++){ if( manifestCache.aAge[i]<oldestAge ){ oldest = i; oldestAge = manifestCache.aAge[i]; } } manifest_destroy(manifestCache.apManifest[oldest]); i = oldest; } manifestCache.aAge[i] = ++manifestCache.nxAge; manifestCache.apManifest[i] = p; p = pBaseline; } } /* ** Try to extract a line from the manifest cache. Return 1 if found. ** Return 0 if not found. */ static Manifest *manifest_cache_find(int rid){ int i; Manifest *p; for(i=0; i<MX_MANIFEST_CACHE; i++){ if( manifestCache.apManifest[i] && manifestCache.apManifest[i]->rid==rid ){ p = manifestCache.apManifest[i]; manifestCache.apManifest[i] = 0; return p; } } return 0; } /* ** Clear the manifest cache. */ void manifest_cache_clear(void){ int i; for(i=0; i<MX_MANIFEST_CACHE; i++){ if( manifestCache.apManifest[i] ){ manifest_destroy(manifestCache.apManifest[i]); } } memset(&manifestCache, 0, sizeof(manifestCache)); } #ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM # define md5sum_init(X) # define md5sum_step_text(X,Y) #endif /* ** Return true if z points to the first character after a blank line. ** Tolerate either \r\n or \n line endings. */ static int after_blank_line(const char *z){ if( z[-1]!='\n' ) return 0; if( z[-2]=='\n' ) return 1; if( z[-2]=='\r' && z[-3]=='\n' ) return 1; return 0; } /* ** Remove the PGP signature from the artifact, if there is one. */ static void remove_pgp_signature(const char **pz, int *pn){ const 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, Blob *pErr){ const char *zHash; if( n<35 ) return 0; if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0; md5sum_init(); md5sum_step_text(z, n-35); zHash = md5sum_finish(0); if( memcmp(&z[n-33], zHash, 32)==0 ){ return 1; }else{ if(pErr!=0){ blob_appendf(pErr, "incorrect Z-card cksum: expected %.32s", zHash); } return 2; } } /* ** A structure used for rapid parsing of the Manifest file */ typedef struct ManifestText ManifestText; struct ManifestText { char *z; /* The first character of the next token */ char *zEnd; /* One character beyond the end of the manifest */ int atEol; /* True if z points to the start of a new line */ }; /* ** Return a pointer to the next token. The token is zero-terminated. ** Return NULL if there are no more tokens on the current line. */ static char *next_token(ManifestText *p, int *pLen){ char *zStart; int n; if( p->atEol ) return 0; zStart = p->z; n = strcspn(p->z, " \n"); p->atEol = p->z[n]=='\n'; p->z[n] = 0; p->z += n+1; if( pLen ) *pLen = n; return zStart; } /* ** Return the card-type for the next card. Or, return 0 if there are no ** more cards or if we are not at the end of the current card. */ static char next_card(ManifestText *p){ char c; if( !p->atEol || p->z>=p->zEnd ) return 0; c = p->z[0]; if( p->z[1]==' ' ){ p->z += 2; p->atEol = 0; }else if( p->z[1]=='\n' ){ p->z += 2; p->atEol = 1; }else{ c = 0; } return c; } /* ** Shorthand for a control-artifact parsing error */ #define SYNTAX(T) {zErr=(T); goto manifest_syntax_error;} /* ** A cache of manifest IDs which manifest_parse() has seen in this ** session. */ static Bag seenManifests = Bag_INIT; /* ** Frees all memory owned by the manifest "has-seen" cache. Intended ** to be called only from the app's atexit() handler. */ void manifest_clear_cache(){ bag_clear(&seenManifests); } /* ** Parse a blob into a Manifest object. The Manifest object ** takes over the input blob and will free it when the ** Manifest object is freed. Zeros are inserted into the blob ** as string terminators so that blob should not be used again. ** ** Return a pointer to an allocated Manifest object if the content ** really is a structural artifact of some kind. The returned Manifest ** object needs to be freed by a subsequent call to manifest_destroy(). ** Return NULL if there are syntax errors or if the input blob does ** not describe a valid structural artifact. ** ** This routine is strict about the format of a structural artifacts. ** The format must match exactly or else it is rejected. This ** rule minimizes the risk that a content artifact will be mistaken ** for a structural artifact simply because they look the same. ** ** The pContent is reset. If a pointer is returned, then pContent will ** be reset when the Manifest object is cleared. If NULL is ** returned then the Manifest object is cleared automatically ** and pContent is reset before the return. ** ** The entire input blob can be PGP clear-signed. The signature is ignored. ** The artifact consists of zero or more cards, one card per line. ** (Except: the content of the W card can extend of multiple lines.) ** Each card is divided into tokens by a single space character. ** The first token is a single upper-case letter which is the card type. ** The card type determines the other parameters to the card. ** Cards must occur in lexicographical order. */ Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){ Manifest *p; int i, lineNo=0; ManifestText x; char cPrevType = 0; char cType; char *z; int n; char *zUuid; int sz = 0; int isRepeat; int nSelfTag = 0; /* Number of T cards referring to this manifest */ int nSimpleTag = 0; /* Number of T cards with "+" prefix */ const char *zErr = 0; unsigned int m; unsigned int seenCard = 0; /* Which card types have been seen */ char zErrBuf[100]; /* Write error messages here */ if( rid==0 ){ isRepeat = 1; }else if( bag_find(&seenManifests, rid) ){ isRepeat = 1; }else{ isRepeat = 0; bag_insert(&seenManifests, rid); } /* Every structural 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( n<=0 || z[n-1]!='\n' ){ blob_reset(pContent); if(pErr!=0){ blob_appendf(pErr, "%s", n ? "not terminated with \\n" : "zero-length"); } return 0; } /* Strip off the PGP signature if there is one. */ remove_pgp_signature((const char**)&z, &n); /* Verify that the first few characters of the artifact look like ** a control artifact. */ if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){ blob_reset(pContent); if(pErr!=0){ blob_appendf(pErr, "line 1 not recognized"); } return 0; } /* Then verify the Z-card. */ #if 1 /* Disable this ***ONLY*** (ONLY!) when testing hand-written inputs for card-related syntax errors. */ if( verify_z_card(z, n, pErr)==2 ){ blob_reset(pContent); return 0; } #else #warning ACHTUNG - z-card check is disabled for testing purposes. if(0 && verify_z_card(NULL, 0, NULL)){ /*avoid unused static func error*/ } #endif /* 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; blob_zero(pContent); pContent = &p->content; /* Begin parsing, card by card. */ x.z = z; x.zEnd = &z[n]; x.atEol = 1; while( (cType = next_card(&x))!=0 ){ if( cType<cPrevType ){ /* Cards must be in increasing order. However, out-of-order detection ** was broken prior to 2021-02-10 due to a bug. Furthermore, there ** was a bug in technote generation (prior to 2021-02-10) that caused ** the P card to occur before the N card. Hence, for historical ** compatibility, we do allow the N card of a technote to occur after ** the P card. See tickets 15d04de574383d61 and 5e67a7f4041a36ad. */ if( cType!='N' || cPrevType!='P' || p->zEventId==0 ){ SYNTAX("cards not in lexicographical order"); } } lineNo++; if( cType<'A' || cType>'Z' ) SYNTAX("bad card type"); seenCard |= 1 << (cType-'A'); cPrevType = cType; switch( cType ){ /* ** A <filename> <target> ?<source>? ** ** Identifies an attachment to either a wiki page or a ticket. ** <source> is the artifact that is the attachment. <source> ** is omitted to delete an attachment. <target> is the name of ** a wiki page or ticket to which that attachment is connected. */ 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_nonstrict(zName) ){ SYNTAX("invalid filename on A-card"); } defossilize(zTarget); if( !hname_validate(zTarget,nTarget) && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ SYNTAX("invalid target on A-card"); } if( zSrc && !hname_validate(zSrc,nSrc) ){ SYNTAX("invalid source on A-card"); } p->zAttachName = (char*)file_tail(zName); p->zAttachSrc = zSrc; p->zAttachTarget = zTarget; p->type = CFTYPE_ATTACHMENT; break; } /* ** B <uuid> ** ** A B-line gives the artifact hash for the baseline of a delta-manifest. */ case 'B': { if( p->zBaseline ) SYNTAX("more than one B-card"); p->zBaseline = next_token(&x, &sz); if( p->zBaseline==0 ) SYNTAX("missing hash on B-card"); if( !hname_validate(p->zBaseline,sz) ){ SYNTAX("invalid hash on B-card"); } p->type = CFTYPE_MANIFEST; break; } /* ** C <comment> ** ** Comment text is fossil-encoded. There may be no more than ** one C line. C lines are required for manifests, are optional ** for Events and Attachments, and are disallowed on all other ** control files. */ case 'C': { if( p->zComment!=0 ) SYNTAX("more than one C-card"); p->zComment = next_token(&x, 0); if( p->zComment==0 ) SYNTAX("missing comment text on C-card"); defossilize(p->zComment); break; } /* ** D <timestamp> ** ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS ** There can be no more than 1 D line. D lines are required ** for all control files except for clusters. */ case 'D': { if( p->rDate>0.0 ) SYNTAX("more than one D-card"); p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0)); 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"); p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card"); p->zEventId = next_token(&x, &sz); if( p->zEventId==0 ) SYNTAX("missing hash on E-card"); if( !hname_validate(p->zEventId, sz) ){ SYNTAX("malformed hash on E-card"); } p->type = CFTYPE_EVENT; break; } /* ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>? ** ** Identifies a file in a manifest. Multiple F lines are ** allowed in a manifest. F lines are not allowed in any ** other control file. The filename and old-name are fossil-encoded. */ case 'F': { char *zName, *zPerm, *zPriorName; zName = next_token(&x,0); if( zName==0 ) SYNTAX("missing filename on F-card"); defossilize(zName); if( !file_is_simple_pathname_nonstrict(zName) ){ SYNTAX("F-card filename is not a simple path"); } zUuid = next_token(&x, &sz); if( p->zBaseline==0 || zUuid!=0 ){ if( zUuid==0 ) SYNTAX("missing hash on F-card"); if( !hname_validate(zUuid,sz) ){ SYNTAX("F-card hash invalid"); } } zPerm = next_token(&x,0); zPriorName = next_token(&x,0); if( zPriorName ){ defossilize(zPriorName); if( !file_is_simple_pathname_nonstrict(zPriorName) ){ 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++; if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){ SYNTAX("incorrect F-card sort order"); } if( file_is_reserved_name(zName,-1) ){ /* If reserved names leaked into historical manifests due to ** slack oversight by older versions of Fossil, simply ignore ** those files */ p->nFile--; break; } p->aFile[i].zName = zName; p->aFile[i].zUuid = zUuid; p->aFile[i].zPerm = zPerm; p->aFile[i].zPrior = zPriorName; p->type = CFTYPE_MANIFEST; break; } /* ** G <hash> ** ** A G-card identifies the initial root forum post for the thread ** of which this post is a part. Forum posts only. */ case 'G': { if( p->zThreadRoot!=0 ) SYNTAX("more than one G-card"); p->zThreadRoot = next_token(&x, &sz); if( p->zThreadRoot==0 ) SYNTAX("missing hash on G-card"); if( !hname_validate(p->zThreadRoot,sz) ){ SYNTAX("Invalid hash on G-card"); } p->type = CFTYPE_FORUM; break; } /* ** H <threadtitle> ** ** The title for a forum thread. */ case 'H': { if( p->zThreadTitle!=0 ) SYNTAX("more than one H-card"); p->zThreadTitle = next_token(&x,0); if( p->zThreadTitle==0 ) SYNTAX("missing title on H-card"); defossilize(p->zThreadTitle); p->type = CFTYPE_FORUM; break; } /* ** I <hash> ** ** A I-card identifies another forum post that the current forum post ** is in reply to. */ case 'I': { if( p->zInReplyTo!=0 ) SYNTAX("more than one I-card"); p->zInReplyTo = next_token(&x, &sz); if( p->zInReplyTo==0 ) SYNTAX("missing hash on I-card"); if( !hname_validate(p->zInReplyTo,sz) ){ SYNTAX("Invalid hash on I-card"); } p->type = CFTYPE_FORUM; break; } /* ** J <name> ?<value>? ** ** Specifies a name value pair for ticket. If the first character ** of <name> is "+" then the <value> is appended to any preexisting ** value. If <value> is omitted then it is understood to be an ** empty string. */ case 'J': { char *zName, *zValue; zName = next_token(&x,0); zValue = next_token(&x,0); if( zName==0 ) SYNTAX("name missing from J-card"); if( zValue==0 ) zValue = ""; defossilize(zValue); if( p->nField>=p->nFieldAlloc ){ p->nFieldAlloc = p->nFieldAlloc*2 + 10; p->aField = fossil_realloc(p->aField, p->nFieldAlloc*sizeof(p->aField[0]) ); } i = p->nField++; p->aField[i].zName = zName; p->aField[i].zValue = zValue; if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){ SYNTAX("incorrect J-card sort order"); } p->type = CFTYPE_TICKET; break; } /* ** K <uuid> ** ** A K-line gives the UUID for the ticket which this control file ** is amending. */ case 'K': { if( p->zTicketUuid!=0 ) SYNTAX("more than one K-card"); p->zTicketUuid = next_token(&x, &sz); if( sz!=HNAME_LEN_SHA1 ) SYNTAX("K-card UUID is the wrong size"); if( !validate16(p->zTicketUuid, sz) ){ SYNTAX("invalid K-card UUID"); } p->type = CFTYPE_TICKET; break; } /* ** L <wikititle> ** ** The wiki page title is fossil-encoded. There may be no more than ** one L line. */ case 'L': { if( p->zWikiTitle!=0 ) SYNTAX("more than one L-card"); p->zWikiTitle = next_token(&x,0); if( p->zWikiTitle==0 ) SYNTAX("missing title on L-card"); defossilize(p->zWikiTitle); if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ SYNTAX("L-card has malformed wiki name"); } p->type = CFTYPE_WIKI; break; } /* ** M <hash> ** ** An M-line identifies another artifact by its hash. M-lines ** occur in clusters only. */ case 'M': { zUuid = next_token(&x, &sz); if( zUuid==0 ) SYNTAX("missing hash on M-card"); if( !hname_validate(zUuid,sz) ){ SYNTAX("Invalid hash on M-card"); } if( p->nCChild>=p->nCChildAlloc ){ p->nCChildAlloc = p->nCChildAlloc*2 + 10; p->azCChild = fossil_realloc(p->azCChild , p->nCChildAlloc*sizeof(p->azCChild[0]) ); } i = p->nCChild++; p->azCChild[i] = zUuid; if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){ SYNTAX("M-card in the wrong order"); } p->type = CFTYPE_CLUSTER; break; } /* ** N <uuid> ** ** An N-line identifies the mimetype of wiki or comment text. */ case 'N': { if( p->zMimetype!=0 ) SYNTAX("more than one N-card"); p->zMimetype = next_token(&x,0); if( p->zMimetype==0 ) SYNTAX("missing mimetype on N-card"); defossilize(p->zMimetype); break; } /* ** 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( !hname_validate(zUuid, sz) ){ SYNTAX("invalid hash 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 hash on Q-card"); if( zUuid[0]!='+' && zUuid[0]!='-' ){ SYNTAX("Q-card does not begin with '+' or '-'"); } if( !hname_validate(&zUuid[1], sz-1) ){ SYNTAX("invalid hash on Q-card"); } n = p->nCherrypick; p->nCherrypick++; p->aCherrypick = fossil_realloc(p->aCherrypick, p->nCherrypick*sizeof(p->aCherrypick[0])); p->aCherrypick[n].zCPTarget = zUuid; p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz); if( zUuid && !hname_validate(zUuid,sz) ){ SYNTAX("invalid second hash on Q-card"); } p->type = CFTYPE_MANIFEST; break; } /* ** R <md5sum> ** ** Specify the MD5 checksum over the name and content of all files ** in the manifest. */ case 'R': { if( p->zRepoCksum!=0 ) SYNTAX("more than one R-card"); p->zRepoCksum = next_token(&x, &sz); if( sz!=32 ) SYNTAX("wrong size cksum on R-card"); if( !validate16(p->zRepoCksum, 32) ) SYNTAX("malformed R-card cksum"); p->type = CFTYPE_MANIFEST; break; } /* ** T (+|*|-)<tagname> <uuid> ?<value>? ** ** 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); if( zName==0 ) SYNTAX("missing name on T-card"); zUuid = next_token(&x, &sz); if( zUuid==0 ) SYNTAX("missing artifact hash on T-card"); zValue = next_token(&x, 0); if( zValue ) defossilize(zValue); if( hname_validate(zUuid, sz) ){ /* A valid artifact hash */ }else if( sz==1 && zUuid[0]=='*' ){ zUuid = 0; nSelfTag++; }else{ SYNTAX("malformed artifact hash on T-card"); } defossilize(zName); if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){ SYNTAX("T-card name does not begin with '-', '+', or '*'"); } if( zName[0]=='+' ) nSimpleTag++; if( validate16(&zName[1], strlen(&zName[1])) ){ /* Do not allow tags whose names look like a hash */ SYNTAX("T-card name looks like a hexadecimal hash"); } if( p->nTag>=p->nTagAlloc ){ p->nTagAlloc = p->nTagAlloc*2 + 10; p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); } i = p->nTag++; p->aTag[i].zName = zName; p->aTag[i].zUuid = zUuid; p->aTag[i].zValue = zValue; if( i>0 ){ int c = fossil_strcmp(p->aTag[i-1].zName, zName); if( c>0 || (c==0 && fossil_strcmp(p->aTag[i-1].zUuid, zUuid)>=0) ){ SYNTAX("T-card in the wrong order"); } } break; } /* ** U ?<login>? ** ** Identify the user who created this control file by their ** login. Only one U line is allowed. Prohibited in clusters. ** If the user name is omitted, take that to be "anonymous". */ case 'U': { if( p->zUser!=0 ) SYNTAX("more than one U-card"); p->zUser = next_token(&x, 0); if( p->zUser==0 || p->zUser[0]==0 ){ p->zUser = "anonymous"; }else{ defossilize(p->zUser); } break; } /* ** W <size> ** ** The next <size> bytes of the file contain the text of the wiki ** page. There is always an extra \n before the start of the next ** record. */ case 'W': { char *zSize; unsigned size, oldsize, c; Blob wiki; zSize = next_token(&x, 0); if( zSize==0 ) SYNTAX("missing size on W-card"); if( x.atEol==0 ) SYNTAX("no content after W-card"); for(oldsize=size=0; (c = zSize[0])>='0' && c<='9'; zSize++){ size = oldsize*10 + c - '0'; if( size<oldsize ) SYNTAX("size overflow on W-card"); oldsize = size; } if( p->zWiki!=0 ) SYNTAX("more than one W-card"); blob_zero(&wiki); if( (&x.z[size+1])>=x.zEnd )SYNTAX("not enough content after W-card"); p->zWiki = x.z; x.z += size; if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated"); x.z[0] = 0; x.z++; break; } /* ** Z <md5sum> ** ** MD5 checksum on this control file. The checksum is over all ** lines (other than PGP-signature lines) prior to the current ** line. This must be the last record. ** ** This card is required for all control file types except for ** Manifest. It is not required for manifest only for historical ** compatibility reasons. */ case 'Z': { zUuid = next_token(&x, &sz); if( sz!=32 ) SYNTAX("wrong size for Z-card cksum"); if( !validate16(zUuid, 32) ) SYNTAX("malformed Z-card cksum"); break; } default: { SYNTAX("unrecognized card"); } } } if( x.z<x.zEnd ) SYNTAX("extra characters at end of card"); /* If the artifact type has not yet been determined, then compute ** it now. */ if( p->type==0 ){ if( p->zComment!=0 || p->nFile>0 || p->nParent>0 ){ p->type = CFTYPE_MANIFEST; }else{ p->type = CFTYPE_CONTROL; } } /* Verify that no disallowed cards are present for this artifact type */ m = manifest_card_mask(manifestCardTypes[p->type-1].zAllowed); if( seenCard & ~m ){ sqlite3_snprintf(sizeof(zErrBuf), zErrBuf, "%c-card in %s", maskToType(seenCard & ~m), azNameOfMType[p->type-1]); zErr = zErrBuf; goto manifest_syntax_error; } /* Verify that all required cards are present for this artifact type */ m = manifest_card_mask(manifestCardTypes[p->type-1].zRequired); if( ~seenCard & m ){ sqlite3_snprintf(sizeof(zErrBuf), zErrBuf, "%c-card missing in %s", maskToType(~seenCard & m), azNameOfMType[p->type-1]); zErr = zErrBuf; goto manifest_syntax_error; } /* Additional checks based on artifact type */ switch( p->type ){ case CFTYPE_CONTROL: { if( nSelfTag ) SYNTAX("self-referential T-card in control artifact"); break; } case CFTYPE_EVENT: { if( p->nTag!=nSelfTag ){ SYNTAX("non-self-referential T-card in technote"); } if( p->nTag!=nSimpleTag ){ SYNTAX("T-card with '*' or '-' in technote"); } break; } case CFTYPE_FORUM: { if( p->zThreadTitle && p->zInReplyTo ){ SYNTAX("cannot have I-card and H-card in a forum post"); } if( p->nParent>1 ) SYNTAX("too many arguments to P-card"); break; } } md5sum_init(); if( !isRepeat ) g.parseCnt[p->type]++; return p; manifest_syntax_error: { char *zUuid = rid_to_uuid(rid); if( zUuid ){ if(pErr!=0){ blob_appendf(pErr, "artifact [%s] ", zUuid); } fossil_free(zUuid); } } if(pErr!=0){ 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); return 0; } /* ** Get a manifest given the rid for the control artifact. Return ** a pointer to the manifest on success or NULL if there is a failure. */ Manifest *manifest_get(int rid, int cfType, Blob *pErr){ Blob content; Manifest *p; if( !rid ) return 0; p = manifest_cache_find(rid); if( p ){ if( cfType!=CFTYPE_ANY && cfType!=p->type ){ manifest_cache_insert(p); p = 0; } return p; } content_get(rid, &content); p = manifest_parse(&content, rid, pErr); if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){ 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; } /* ** The input blob is text that may or may not be a valid Fossil ** control artifact of some kind. This routine returns true if ** the input is a well-formed control artifact and false if it ** is not. ** ** This routine is optimized to return false quickly and with minimal ** work in the common case where the input is some random file. */ int manifest_is_well_formed(const char *zIn, int nIn){ int i; int iRes; Manifest *pManifest; Blob copy, errmsg; remove_pgp_signature(&zIn, &nIn); /* Check to see that the file begins with a "card" */ if( nIn<3 ) return 0; if( zIn[0]<'A' || zIn[0]>'M' || zIn[1]!=' ' ) return 0; /* Check to see that the first card is followed by one more card */ for(i=2; i<nIn && zIn[i]!='\n'; i++){} if( i>=nIn-3 ) return 0; i++; if( !fossil_isupper(zIn[i]) || zIn[i]<zIn[0] || zIn[i+1]!=' ' ) return 0; /* The checks above will eliminate most random inputs. If these ** quick checks pass, then we could be dealing with a well-formed ** control artifact. Make a copy, and run it through the official ** artifact parser. This is the slow path, but it is rarely taken. */ blob_init(©, 0, 0); blob_init(&errmsg, 0, 0); blob_append(©, zIn, nIn); pManifest = manifest_parse(©, 0, &errmsg); iRes = pManifest!=0; manifest_destroy(pManifest); blob_reset(&errmsg); return iRes; } /* ** COMMAND: test-parse-manifest ** ** Usage: %fossil test-parse-manifest FILENAME ?N? ** ** Parse the manifest(s) given on the command-line and report any ** errors. If the N argument is given, run the parsing N times. */ void manifest_test_parse_cmd(void){ Manifest *p; Blob b; int i; int n = 1; int isWF; db_find_and_open_repository(OPEN_SUBSTITUTE|OPEN_OK_NOT_FOUND,0); verify_all_options(); if( g.argc!=3 && g.argc!=4 ){ usage("FILENAME"); } blob_read_from_file(&b, g.argv[2], ExtFILE); if( g.argc>3 ) n = atoi(g.argv[3]); isWF = manifest_is_well_formed(blob_buffer(&b), blob_size(&b)); fossil_print("manifest_is_well_formed() reports the input %s\n", isWF ? "is ok" : "contains errors"); for(i=0; i<n; i++){ Blob b2; Blob err; blob_copy(&b2, &b); blob_zero(&err); p = manifest_parse(&b2, 0, &err); if( p==0 ){ fossil_print("ERROR: %s\n", blob_str(&err)); }else if( i==0 || (n==2 && i==1) ){ fossil_print("manifest_parse() worked\n"); }else if( i==n-1 ){ fossil_print("manifest_parse() worked %d more times\n", n-1); } if( (p==0 && isWF) || (p!=0 && !isWF) ){ fossil_print("ERROR: manifest_is_well_formed() and " "manifest_parse() disagree!\n"); } blob_reset(&err); manifest_destroy(p); } blob_reset(&b); } /* ** COMMAND: test-parse-all-blobs ** ** Usage: %fossil test-parse-all-blobs ?OPTIONS? ** ** Parse all entries in the BLOB table that are believed to be non-data ** artifacts and report any errors. Run this test command on historical ** repositories after making any changes to the manifest_parse() ** implementation to confirm that the changes did not break anything. ** ** Options: ** --limit N Parse no more than N artifacts before stopping ** --wellformed Use all BLOB table entries as input, not just ** those entries that are believed to be valid ** artifacts, and verify that the result the ** manifest_is_well_formed() agrees with the ** result of manifest_parse(). */ void manifest_test_parse_all_blobs_cmd(void){ Manifest *p; Blob err; Stmt q; int nTest = 0; int nErr = 0; int N = 1000000000; int bWellFormed; const char *z; db_find_and_open_repository(0, 0); z = find_option("limit", 0, 1); if( z ) N = atoi(z); bWellFormed = find_option("wellformed",0,0)!=0; verify_all_options(); if( bWellFormed ){ db_prepare(&q, "SELECT rid FROM blob ORDER BY rid"); }else{ db_prepare(&q, "SELECT DISTINCT objid FROM EVENT ORDER BY objid"); } while( (N--)>0 && db_step(&q)==SQLITE_ROW ){ int id = db_column_int(&q,0); fossil_print("Checking %d \r", id); nTest++; fflush(stdout); blob_init(&err, 0, 0); if( bWellFormed ){ Blob content; int isWF; content_get(id, &content); isWF = manifest_is_well_formed(blob_buffer(&content),blob_size(&content)); p = manifest_parse(&content, id, &err); if( isWF && p==0 ){ fossil_print("%d ERROR: manifest_is_well_formed() reported true " "but manifest_parse() reports an error: %s\n", id, blob_str(&err)); nErr++; }else if( !isWF && p!=0 ){ fossil_print("%d ERROR: manifest_is_well_formed() reported false " "but manifest_parse() found nothing wrong.\n", id); nErr++; } }else{ p = manifest_get(id, CFTYPE_ANY, &err); if( p==0 ){ fossil_print("%d ERROR: %s\n", id, blob_str(&err)); nErr++; } } blob_reset(&err); manifest_destroy(p); } db_finalize(&q); fossil_print("%d tests with %d errors\n", nTest, nErr); } /* ** Fetch the baseline associated with the delta-manifest p. ** Return 0 on success. If unable to parse the baseline, ** throw an error. If the baseline is a manifest, throw an ** error if throwError is true, or record that p is an orphan ** and return 1 if throwError is false. */ static int fetch_baseline(Manifest *p, int throwError){ if( p->zBaseline!=0 && p->pBaseline==0 ){ int rid = uuid_to_rid(p->zBaseline, 1); p->pBaseline = manifest_get(rid, CFTYPE_MANIFEST, 0); 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; } /* ** Rewind a manifest-file iterator back to the beginning of the manifest. */ void manifest_file_rewind(Manifest *p){ p->iFile = 0; fetch_baseline(p, 1); if( p->pBaseline ){ p->pBaseline->iFile = 0; } } /* ** 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. */ if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++]; }else{ /* Manifest p is a delta-manifest. Scan the baseline but amend the ** file list in the baseline with changes described by p. */ Manifest *pB = p->pBaseline; int cmp; while(1){ if( pB->iFile>=pB->nFile ){ /* We have used all entries out of the baseline. Return the next ** entry from the delta. */ if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++]; break; }else if( p->iFile>=p->nFile ){ /* We have used all entries from the delta. Return the next ** entry from the baseline. */ if( pB->iFile<pB->nFile ) pOut = &pB->aFile[pB->iFile++]; break; }else if( (cmp = fossil_strcmp(pB->aFile[pB->iFile].zName, p->aFile[p->iFile].zName)) < 0 ){ /* The next baseline entry comes before the next delta entry. ** So return the baseline entry. */ pOut = &pB->aFile[pB->iFile++]; break; }else if( cmp>0 ){ /* The next delta entry comes before the next baseline ** entry so return the delta entry */ pOut = &p->aFile[p->iFile++]; break; }else if( p->aFile[p->iFile].zUuid ){ /* The next delta entry is a replacement for the next baseline ** entry. Skip the baseline entry and return the delta entry */ pB->iFile++; pOut = &p->aFile[p->iFile++]; break; }else{ /* The next delta entry is a delete of the next baseline ** entry. Skip them both. Repeat the loop to find the next ** non-delete entry. */ pB->iFile++; p->iFile++; continue; } } } return pOut; } /* ** Translate a filename into a filename-id (fnid). Create a new fnid ** if no previously exists. */ static int filename_to_fnid(const char *zFilename){ |
︙ | ︙ | |||
680 681 682 683 684 685 686 687 688 689 690 691 692 693 | db_static_prepare(&s1, "INSERT INTO filename(name) VALUES(:fn)"); db_bind_text(&s1, ":fn", zFilename); db_exec(&s1); fnid = db_last_insert_rowid(); } return fnid; } /* ** 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 mid, /* The record ID of the manifest */ | > > > > > > > > > > > > > > > > > > > > > > > < | | > > > > | | | > > > > > > > > > > > > > | | | | | | | > | | > > | > | | | > > | | > > > | > > > > > > > > > > > > > > > | > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | > > > > > > > > > > | | > > > > | > > > > > > > | > > > > | | > > > | | | | > > | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | | < > > > | < < | | < > > | > | | < > > > > > > > > > > > > > > > > > | > > > > | > > > > > > > > > > > | > > > | > | < > > > > | | > > > > > > | > > > > > > > | | > > > > | > > > > > > | > | > | | > > > | | < | | > | < < < < < | < | > > | > > > > > > | > > > > | > | > > > | > > > | < > | | > > > > > > > > | > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > < < > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > | > > > > > > > > > > > | > > > > > | > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | db_static_prepare(&s1, "INSERT INTO filename(name) VALUES(:fn)"); db_bind_text(&s1, ":fn", zFilename); db_exec(&s1); fnid = db_last_insert_rowid(); } return fnid; } /* ** Compute an appropriate mlink.mperm integer for the permission string ** of a file. */ int manifest_file_mperm(const 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. ** ** An mlink entry is always created if isPrimary is true. But if ** isPrimary is false (meaning that pmid is a merge parent of mid) ** then the mlink entry is only created if there is already an mlink ** from primary parent for the same file. */ static void add_one_mlink( int pmid, /* The parent manifest */ const char *zFromUuid, /* Artifact hash for content in parent */ int mid, /* The record ID of the manifest */ const char *zToUuid, /* artifact hash 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; int doInsert; static Stmt s1, s2; fnid = filename_to_fnid(zFilename); if( zPrior==0 ){ pfnid = 0; }else{ pfnid = filename_to_fnid(zPrior); } if( zFromUuid==0 || zFromUuid[0]==0 ){ pid = 0; }else{ pid = uuid_to_rid(zFromUuid, 1); } if( zToUuid==0 || zToUuid[0]==0 ){ fid = 0; }else{ fid = uuid_to_rid(zToUuid, 1); if( isPublic ) content_make_public(fid); } if( isPrimary ){ doInsert = 1; }else{ db_static_prepare(&s2, "SELECT 1 FROM mlink WHERE mid=:m AND fnid=:n AND NOT isaux" ); db_bind_int(&s2, ":m", mid); db_bind_int(&s2, ":n", fnid); doInsert = db_step(&s2)==SQLITE_ROW; db_reset(&s2); } if( doInsert ){ 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, 1, 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; if( p->aFile==0 ){ return 0; } lwr = 0; upr = p->nFile - 1; if( p->iFile>=lwr && p->iFile<upr ){ c = fossil_strcmp(p->aFile[p->iFile+1].zName, zName); if( c==0 ){ return &p->aFile[++p->iFile]; }else if( c>0 ){ upr = p->iFile; }else{ lwr = p->iFile+1; } } while( lwr<=upr ){ i = (lwr+upr)/2; c = fossil_strcmp(p->aFile[i].zName, zName); if( c<0 ){ lwr = i+1; }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; fetch_baseline(p, 1); pBase = p->pBaseline; if( pBase==0 ) return 0; for(i=0; i<pBase->nFile; i++){ if( fossil_stricmp(zName, pBase->aFile[i].zName)==0 ){ return &pBase->aFile[i]; } } return 0; } /* ** Add mlink table entries associated with manifest cid, pChild. The ** parent manifest is pid, pParent. One of either pChild or pParent ** will be NULL and it will be computed based on cid/pid. ** ** 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. ** File added by merge have mlink.pid=-1 ** Edited files have both mlink.pid!=0 and mlink.fid!=0 ** ** Many mlink entries for merge parents will only be added if another mlink ** entry already exists for the same file from the primary parent. Therefore, ** to ensure that all merge-parent mlink entries are properly created: ** ** (1) Make this routine a no-op if pParent is a merge parent and the ** primary parent is a phantom. ** (2) Invoke this routine recursively for merge-parents if pParent is the ** primary parent. */ 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); /* If pParent is not the primary parent of pChild, and the primary ** parent of pChild is a phantom, then abort this routine without ** doing any work. The mlink entries will be computed when the ** primary parent dephantomizes. */ if( !isPrim && otherRid==mid && !db_exists("SELECT 1 FROM blob WHERE uuid=%Q AND size>0", pChild->azParent[0]) ){ manifest_cache_insert(*ppOther); return; } /* 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, 1, 0); }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){ content_deltify(pParent->pBaseline->rid, &mid, 1, 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); /* If pParent is the primary parent of pChild, also run this analysis ** for all merge parents of pChild */ if( isPrim ){ for(i=1; i<pChild->nParent; i++){ pmid = uuid_to_rid(pChild->azParent[i], 0); if( pmid<=0 ) continue; add_mlink(pmid, 0, mid, pChild, 0); } for(i=0; i<pChild->nCherrypick; i++){ if( pChild->aCherrypick[i].zCPTarget[0]=='+' && (pmid = uuid_to_rid(pChild->aCherrypick[i].zCPTarget+1, 0))>0 ){ add_mlink(pmid, 0, mid, pChild, 0); } } } } /* ** For a check-in with RID "rid" that has nParent parent check-ins given ** by the hashes in azParent[], create all appropriate plink and mlink table ** entries. ** ** The primary parent is the first hash on the azParent[] list. ** ** Return the RID of the primary parent. */ static int manifest_add_checkin_linkages( int rid, /* The RID of the check-in */ Manifest *p, /* Manifest for this check-in */ int nParent, /* Number of parents for this check-in */ char * const * azParent /* hashes for each parent */ ){ int i; int parentid = 0; char zBaseId[30]; /* Baseline manifest RID for deltas. "NULL" otherwise */ Stmt q; int nLink; 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<nParent; i++){ int pid = uuid_to_rid(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*/); if( i==0 ) parentid = pid; } add_mlink(parentid, 0, rid, p, 1); nLink = nParent; for(i=0; i<p->nCherrypick; i++){ if( p->aCherrypick[i].zCPTarget[0]=='+' ) nLink++; } if( nLink>1 ){ /* Change MLINK.PID from 0 to -1 for files that are added by merge. */ db_multi_exec( "UPDATE mlink SET pid=-1" " WHERE mid=%d" " AND pid=0" " AND fnid IN " " (SELECT fnid FROM mlink WHERE mid=%d GROUP BY fnid" " HAVING count(*)<%d)", rid, rid, nLink ); } 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( 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])); } } return parentid; } /* ** There exists a "parent" tag against check-in rid that has value zValue. ** If value is well-formed (meaning that it is a list of hashes), then use ** zValue to reparent check-in rid. */ void manifest_reparent_checkin(int rid, const char *zValue){ int nParent = 0; char *zCopy = 0; char **azParent = 0; Manifest *p = 0; int i, j; int n = (int)strlen(zValue); int mxParent = (n+1)/(HNAME_MIN+1); if( mxParent<1 ) return; zCopy = fossil_strdup(zValue); azParent = fossil_malloc( sizeof(azParent[0])*mxParent ); for(nParent=0, i=0; zCopy[i]; i++){ char *z = &zCopy[i]; azParent[nParent++] = z; if( nParent>mxParent ) goto reparent_abort; for(j=HNAME_MIN; z[j]>' '; j++){} if( !hname_validate(z, j) ) goto reparent_abort; if( z[j]==0 ) break; z[j] = 0; i += j; } p = manifest_get(rid, CFTYPE_MANIFEST, 0); if( p!=0 ){ db_multi_exec( "DELETE FROM plink WHERE cid=%d;" "DELETE FROM mlink WHERE mid=%d;", rid, rid ); manifest_add_checkin_linkages(rid,p,nParent,azParent); manifest_destroy(p); } reparent_abort: fossil_free(azParent); fossil_free(zCopy); } /* ** Setup to do multiple manifest_crosslink() calls. ** ** This routine creates TEMP tables for holding information for ** processing that must be deferred until all artifacts have been ** seen at least once. The deferred processing is accomplished ** by the call to manifest_crosslink_end(). */ void manifest_crosslink_begin(void){ assert( manifest_crosslink_busy==0 ); manifest_crosslink_busy = 1; manifest_create_event_triggers(); db_begin_transaction(); db_multi_exec( "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;" "CREATE TEMP TABLE time_fudge(" " mid INTEGER PRIMARY KEY," /* The rid of a manifest */ " m1 REAL," /* The timestamp on mid */ " cid INTEGER," /* A child or mid */ " m2 REAL" /* Timestamp on the child */ ");" ); } /* ** Add a new entry to the pending_xlink table. */ static void add_pending_crosslink(char cType, const char *zId){ assert( manifest_crosslink_busy==1 ); db_multi_exec( "INSERT OR IGNORE INTO pending_xlink VALUES('%c%q')", cType, zId ); } #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 rid, value FROM tagxref" " WHERE tagid=%d AND tagtype=1", TAG_PARENT ); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q,0); const char *zValue = db_column_text(&q,1); manifest_reparent_checkin(rid, zValue); } db_finalize(&q); db_prepare(&q, "SELECT id FROM pending_xlink"); while( db_step(&q)==SQLITE_ROW ){ const char *zId = db_column_text(&q, 0); char cType; if( zId==0 || zId[0]==0 ) continue; cType = zId[0]; zId++; if( cType=='t' ){ ticket_rebuild_entry(zId); if( permitHooks && rc==TH_OK ){ rc = xfer_run_script(zScript, zId, 0); } }else if( cType=='w' ){ backlink_wiki_refresh(zId); } } db_finalize(&q); db_multi_exec("DROP TABLE pending_xlink"); /* If multiple check-ins happen close together in time, adjust their ** times by a few milliseconds to make sure they appear in chronological ** order. */ db_prepare(&q, "UPDATE time_fudge SET m1=m2-:incr WHERE m1>=m2 AND m1<m2+:window" ); db_bind_double(&q, ":incr", AGE_ADJUST_INCREMENT); db_bind_double(&q, ":window", AGE_FUDGE_WINDOW); db_prepare(&u, "UPDATE time_fudge SET m2=" "(SELECT x.m1 FROM time_fudge AS x WHERE x.mid=time_fudge.cid)" ); for(i=0; i<30; i++){ db_step(&q); 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)" " AND (mtime=omtime OR omtime IS NULL)" ); } db_multi_exec("DROP TABLE time_fudge;"); db_end_transaction(0); manifest_crosslink_busy = 0; return ( rc!=TH_ERROR ); } /* ** Activate EVENT triggers if they do not already exist. */ void manifest_create_event_triggers(void){ if( manifest_event_triggers_are_enabled ){ return; /* Triggers already exists. No-op. */ } alert_create_trigger(); manifest_event_triggers_are_enabled = 1; } /* ** Disable manifest event triggers. Drop them if they exist, but mark ** them has having been created so that they won't be recreated. This ** is used during "rebuild" to prevent triggers from firing then. */ void manifest_disable_event_triggers(void){ alert_drop_trigger(); manifest_event_triggers_are_enabled = 1; } /* ** 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 */ const Manifest *pManifest, /* Parsed content of the artifact */ |
︙ | ︙ | |||
900 901 902 903 904 905 906 | blob_zero(&comment); blob_zero(&brief); if( once ){ once = 0; zTitleExpr = db_get("ticket-title-expr", "title"); zStatusColumn = db_get("ticket-status-column", "status"); } | | | | | | | | | | | | | | | | | | | > | > > > > > > > > > > > > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > | | | < > > > > > > > > > | > > > > > > | | > > > > > > > > > > > | > > > > > > > > > > > > > > > < < < < < < | < < < < < | < < > | | | > | | | < < | | | > > > > > > > > > | > > > | > | > | | | > > > | > > | | | | | | > | | > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | | | | > > > | > > | | | | > > > > > > > > | > | < > | < > | | < | < < | | | > > > | | | > > > > > > > > > > > > > > > > > > > > > > | | > > | | | | | | > > > > | > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > | | | | | | < | | < | | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | < > > > | > > > > > > > > > > > > > > | | | | > > | > > > > > > > | | > > > > > > > > > > > > > > > > > > | < > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > | > > > > | > > > > > > > > > > > > > > > > > > | 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 | 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); manifest_create_event_triggers(); if( db_exists("SELECT 1 FROM event WHERE type='t' AND objid=%d", rid) ){ /* The ticket_rebuild_entry() function redoes all of the event entries ** for a ticket whenever a new event appears. Be careful to only UPDATE ** existing events, so that they do not get turned into alerts by ** the alert trigger. */ db_multi_exec( "UPDATE event SET tagid=%d, mtime=%.17g, user=%Q, comment=%Q, brief=%Q" " WHERE objid=%d", tktTagId, pManifest->rDate, pManifest->zUser, blob_str(&comment), blob_str(&brief), rid ); }else{ 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, int eType){ char *z, *zOrig; int n, nOrig; static const char zExtraLine[] = "# Remove this line to create a well-formed Fossil %s.\n"; const char *zType = eType==CFTYPE_MANIFEST ? "manifest" : "control artifact"; z = zOrig = blob_materialize(p); n = nOrig = blob_size(p); remove_pgp_signature((const char **)&z, &n); if( z==zOrig ){ blob_appendf(p, zExtraLine/*works-like:"%s"*/, zType); }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_appendf(p, zExtraLine/*works-like:"%s"*/, zType); 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; int c; c = fossil_strcmp(pA->zUuid, pB->zUuid); if( c==0 ){ c = fossil_strcmp(pA->zName, pB->zName); } return c; } /* ** Inserts plink entries for FORUM, WIKI, and TECHNOTE manifests. May ** assert for other manifest types. If a parent entry exists, it also ** propagates any tags for that parent. This is a no-op if ** p->nParent==0. */ static void manifest_add_fwt_plink(int rid, Manifest *p){ int i; int parentId = 0; assert(p->type==CFTYPE_WIKI || p->type==CFTYPE_FORUM || p->type==CFTYPE_EVENT); for(i=0; i<p->nParent; ++i){ int const pid = uuid_to_rid(p->azParent[i], 1); if(0==i){ parentId = pid; } db_multi_exec( "INSERT OR IGNORE INTO plink" "(pid, cid, isprim, mtime, baseid)" "VALUES(%d, %d, %d, %.17g, NULL)", pid, rid, i==0, p->rDate); } if(parentId){ tag_propagate_all(parentId); } } /* ** Scan artifact rid/pContent to see if it is a control artifact of ** any type: ** ** * Manifest ** * Control ** * Wiki Page ** * Ticket Change ** * Cluster ** * Attachment ** * Event ** * Forum post ** ** 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; int parentid = 0; int permitHooks = (flags & MC_PERMIT_HOOKS); const char *zScript = 0; const char *zUuid = 0; if( g.fSqlTrace ){ fossil_trace("-- manifest_crosslink(%d)\n", rid); } manifest_create_event_triggers(); 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 ){ char * zErrUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",rid); fossil_error(1, "syntax error in manifest [%S]", zErrUuid); fossil_free(zErrUuid); } 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( p->nCherrypick && db_table_exists("repository","cherrypick") ){ int i; for(i=0; i<p->nCherrypick; i++){ db_multi_exec( "REPLACE INTO cherrypick(parentid,childid,isExclude)" " SELECT rid, %d, %d FROM blob WHERE uuid=%Q", rid, p->aCherrypick[i].zCPTarget[0]=='-', p->aCherrypick[i].zCPTarget+1 ); } } if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ char *zCom; parentid = manifest_add_checkin_linkages(rid,p,p->nParent,p->azParent); search_doc_touch('c', rid, 0); assert( manifest_event_triggers_are_enabled ); zCom = db_text(0, "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)" "RETURNING coalesce(ecomment,comment);", TAG_DATE, rid, p->rDate, rid, p->zUser, p->zComment, TAG_BGCOLOR, rid, TAG_USER, rid, TAG_COMMENT, rid, p->rDate ); backlink_extract(zCom, MT_NONE, rid, BKLNK_COMMENT, p->rDate, 1); 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; if( once ){ db_set_int("seen-delta-manifest", 1, 0); once = 0; } } } } if( p->type==CFTYPE_CLUSTER ){ static Stmt del1; tag_insert("cluster", 1, 0, rid, p->rDate, rid); db_static_prepare(&del1, "DELETE FROM unclustered WHERE rid=:rid"); for(i=0; i<p->nCChild; i++){ int mid; mid = uuid_to_rid(p->azCChild[i], 1); if( mid>0 ){ db_bind_int(&del1, ":rid", mid); db_step(&del1); db_reset(&del1); } } } if( p->type==CFTYPE_CONTROL || p->type==CFTYPE_MANIFEST || p->type==CFTYPE_EVENT ){ for(i=0; i<p->nTag; i++){ int tid; int type; if( p->aTag[i].zUuid ){ tid = uuid_to_rid(p->aTag[i].zUuid, 1); }else{ tid = rid; } 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); manifest_destroy(p); 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 || p->type==CFTYPE_FORUM || p->type==CFTYPE_EVENT){ manifest_add_fwt_plink(rid, p); } if( p->type==CFTYPE_WIKI ){ char *zTag = mprintf("wiki-%s", p->zWikiTitle); int prior = 0; char cPrefix; 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); if(p->nParent){ prior = fast_uuid_to_rid(p->azParent[0]); } if( prior ){ content_deltify(prior, &rid, 1, 0); } if( nWiki<=0 ){ cPrefix = '-'; }else if( !prior ){ cPrefix = '+'; }else{ cPrefix = ':'; } search_doc_touch('w',rid,p->zWikiTitle); if( manifest_crosslink_busy ){ add_pending_crosslink('w',p->zWikiTitle); }else{ backlink_wiki_refresh(p->zWikiTitle); } assert( manifest_event_triggers_are_enabled ); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('w',%.17g,%d,%Q,'%c%q');", p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle ); } if( p->type==CFTYPE_EVENT ){ char *zTag = mprintf("event-%s", p->zEventId); int tagid = tag_findid(zTag, 1); int prior = 0, subsequent; int nWiki; char zLength[40]; Stmt qatt; 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); if(p->nParent){ prior = fast_uuid_to_rid(p->azParent[0]); } subsequent = db_int(0, /* BUG: this check is only correct if subsequent version has already been crosslinked. */ "SELECT rid FROM tagxref" " WHERE tagid=%d AND mtime>=%.17g AND rid!=%d" " ORDER BY mtime", tagid, p->rDate, rid ); if( prior ){ content_deltify(prior, &rid, 1, 0); if( !subsequent ){ db_multi_exec( "DELETE FROM event" " WHERE type='e'" " AND tagid=%d" " AND objid IN (SELECT rid FROM tagxref WHERE tagid=%d)", tagid, tagid ); } } if( subsequent ){ content_deltify(rid, &subsequent, 1, 0); }else{ search_doc_touch('e',rid,0); assert( manifest_event_triggers_are_enabled ); 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 ); } /* Locate and update comment for any attachments */ db_prepare(&qatt, "SELECT attachid, src, target, filename FROM attachment" " WHERE target=%Q", p->zEventId ); while( db_step(&qatt)==SQLITE_ROW ){ const char *zAttachId = db_column_text(&qatt, 0); const char *zSrc = db_column_text(&qatt, 1); const char *zTarget = db_column_text(&qatt, 2); const char *zName = db_column_text(&qatt, 3); const char isAdd = (zSrc && zSrc[0]) ? 1 : 0; char *zComment; if( isAdd ){ zComment = mprintf( "Add attachment [/artifact/%!S|%h] to" " tech note [/technote/%!S|%S]", zSrc, zName, zTarget, zTarget); }else{ zComment = mprintf( "Delete attachment \"%h\" from" " tech note [/technote/%!S|%S]", zName, zTarget, zTarget); } db_multi_exec("UPDATE event SET comment=%Q, type='e'" " WHERE objid=%Q", zComment, zAttachId); fossil_free(zComment); } db_finalize(&qatt); } if( p->type==CFTYPE_TICKET ){ char *zTag; Stmt qatt; assert( manifest_crosslink_busy==1 ); zTag = mprintf("tkt-%s", p->zTicketUuid); tag_insert(zTag, 1, 0, rid, p->rDate, rid); fossil_free(zTag); add_pending_crosslink('t',p->zTicketUuid); /* Locate and update comment for any attachments */ db_prepare(&qatt, "SELECT attachid, src, target, filename FROM attachment" " WHERE target=%Q", p->zTicketUuid ); while( db_step(&qatt)==SQLITE_ROW ){ const char *zAttachId = db_column_text(&qatt, 0); const char *zSrc = db_column_text(&qatt, 1); const char *zTarget = db_column_text(&qatt, 2); const char *zName = db_column_text(&qatt, 3); const char isAdd = (zSrc && zSrc[0]) ? 1 : 0; char *zComment; if( isAdd ){ zComment = mprintf( "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]", zSrc, zName, zTarget, zTarget); }else{ zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]", zName, zTarget, zTarget); } db_multi_exec("UPDATE event SET comment=%Q, type='t'" " WHERE objid=%Q", zComment, zAttachId); fossil_free(zComment); } db_finalize(&qatt); } if( p->type==CFTYPE_ATTACHMENT ){ char *zComment = 0; const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0; /* We assume that we're attaching to a wiki page until we ** prove otherwise (which could on a later artifact if we ** process the attachment artifact before the artifact to ** which it is attached!) */ char attachToType = 'w'; if( fossil_is_artifact_hash(p->zAttachTarget) ){ if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", p->zAttachTarget) ){ attachToType = 't'; /* Attaching to known ticket */ }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", p->zAttachTarget) ){ attachToType = 'e'; /* Attaching to known tech note */ } } 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( 'e' == attachToType ){ if( isAdd ){ zComment = mprintf( "Add attachment [/artifact/%!S|%h] to tech note [/technote/%!S|%S]", p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget); }else{ zComment = mprintf( "Delete attachment \"/artifact/%!S|%h\" from" " tech note [/technote/%!S|%S]", p->zAttachName, p->zAttachName, p->zAttachTarget,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); } } assert( manifest_event_triggers_are_enabled ); 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 hash, 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 ){ blob_appendf(&comment, " Change background color to \"%h\".", zValue); continue; }else if( strcmp(zName, "-bgcolor")==0 ){ blob_appendf(&comment, " Cancel background color"); }else if( strcmp(zName, "+comment")==0 ){ blob_appendf(&comment, " Edit check-in comment."); continue; }else if( strcmp(zName, "+user")==0 ){ blob_appendf(&comment, " Change user to \"%h\".", zValue); continue; }else if( strcmp(zName, "+date")==0 ){ blob_appendf(&comment, " Timestamp %h.", zValue); continue; }else if( memcmp(zName, "-sym-",5)==0 ){ if( !branchMove ){ blob_appendf(&comment, " Cancel tag \"%h\"", &zName[5]); }else{ continue; } }else if( memcmp(zName, "*sym-",5)==0 ){ if( !branchMove ){ blob_appendf(&comment, " Add propagating tag \"%h\"", &zName[5]); }else{ continue; } }else if( memcmp(zName, "+sym-",5)==0 ){ blob_appendf(&comment, " Add tag \"%h\"", &zName[5]); }else if( strcmp(zName, "+closed")==0 ){ blob_append(&comment, " Mark \"Closed\"", -1); }else if( strcmp(zName, "-closed")==0 ){ blob_append(&comment, " Remove the \"Closed\" mark", -1); }else { if( zName[0]=='-' ){ blob_appendf(&comment, " Cancel \"%h\"", &zName[1]); }else if( zName[0]=='+' ){ blob_appendf(&comment, " Add \"%h\"", &zName[1]); }else{ blob_appendf(&comment, " Add propagating \"%h\"", &zName[1]); } if( zValue && zValue[0] ){ blob_appendf(&comment, " with value \"%h\".", zValue); }else{ blob_appendf(&comment, "."); } continue; } if( zValue && zValue[0] ){ blob_appendf(&comment, " with note \"%h\".", zValue); }else{ blob_appendf(&comment, "."); } } /*blob_appendf(&comment, " [[/info/%S | details]]");*/ if( blob_size(&comment)==0 ) blob_append(&comment, " ", 1); assert( manifest_event_triggers_are_enabled ); db_multi_exec( "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); } if( p->type==CFTYPE_FORUM ){ int froot, fprev, firt; char *zFType; char *zTitle; schema_forum(); search_doc_touch('f', rid, 0); froot = p->zThreadRoot ? uuid_to_rid(p->zThreadRoot, 1) : rid; fprev = p->nParent ? uuid_to_rid(p->azParent[0],1) : 0; firt = p->zInReplyTo ? uuid_to_rid(p->zInReplyTo,1) : 0; db_multi_exec( "REPLACE INTO forumpost(fpid,froot,fprev,firt,fmtime)" "VALUES(%d,%d,nullif(%d,0),nullif(%d,0),%.17g)", p->rid, froot, fprev, firt, p->rDate ); if( firt==0 ){ /* This is the start of a new thread, either the initial entry ** or an edit of the initial entry. */ zTitle = p->zThreadTitle; if( zTitle==0 || zTitle[0]==0 ){ zTitle = "(Deleted)"; } zFType = fprev ? "Edit" : "Post"; assert( manifest_event_triggers_are_enabled ); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('f',%.17g,%d,%Q,'%q: %q')", p->rDate, rid, p->zUser, zFType, zTitle ); /* ** If this edit is the most recent, then make it the title for ** all other entries for the same thread */ if( !db_exists("SELECT 1 FROM forumpost WHERE froot=%d AND firt=0" " AND fpid!=%d AND fmtime>%.17g", froot, rid, p->rDate) ){ /* This entry establishes a new title for all entries on the thread */ db_multi_exec( "UPDATE event" " SET comment=substr(comment,1,instr(comment,':')) || ' %q'" " WHERE objid IN (SELECT fpid FROM forumpost WHERE froot=%d)", zTitle, froot ); } }else{ /* This is a reply to a prior post. Take the title from the root. */ zTitle = db_text(0, "SELECT substr(comment,instr(comment,':')+2)" " FROM event WHERE objid=%d", froot); if( zTitle==0 ) zTitle = fossil_strdup("<i>Unknown</i>"); if( p->zWiki[0]==0 ){ zFType = "Delete reply"; }else if( fprev ){ zFType = "Edit reply"; }else{ zFType = "Reply"; } assert( manifest_event_triggers_are_enabled ); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('f',%.17g,%d,%Q,'%q: %q')", p->rDate, rid, p->zUser, zFType, zTitle ); fossil_free(zTitle); } if( p->zWiki[0] ){ int mimetype = parse_mimetype(p->zMimetype); backlink_extract(p->zWiki, mimetype, rid, BKLNK_FORUM, p->rDate, 1); } } 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); } |
Added src/markdown.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 | /* ** Copyright (c) 2012 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 parse a blob containing markdown text, ** using an external renderer. */ #include "config.h" #include "markdown.h" #include <assert.h> #include <string.h> #include <stdlib.h> #define MKD_LI_END 8 /* internal list flag */ /******************** * TYPE DEFINITIONS * ********************/ #if INTERFACE /* mkd_autolink -- type of autolink */ enum mkd_autolink { MKDA_NOT_AUTOLINK, /* used internally when it is not an autolink*/ MKDA_NORMAL, /* normal http/http/ftp link */ MKDA_EXPLICIT_EMAIL, /* e-mail link with explicit mailto: */ MKDA_IMPLICIT_EMAIL /* e-mail link without mailto: */ }; /* mkd_renderer -- functions for rendering parsed data */ struct mkd_renderer { /* document level callbacks */ void (*prolog)(struct Blob *ob, void *opaque); void (*epilog)(struct Blob *ob, void *opaque); void (*footnotes)(struct Blob *ob, const struct Blob *items, void *opaque); /* block level callbacks - NULL skips the block */ void (*blockcode)(struct Blob *ob, struct Blob *text, void *opaque); void (*blockquote)(struct Blob *ob, struct Blob *text, void *opaque); void (*blockhtml)(struct Blob *ob, struct Blob *text, void *opaque); void (*header)(struct Blob *ob, struct Blob *text, int level, void *opaque); void (*hrule)(struct Blob *ob, void *opaque); void (*list)(struct Blob *ob, struct Blob *text, int flags, void *opaque); void (*listitem)(struct Blob *ob, struct Blob *text, int flags, void *opaque); void (*paragraph)(struct Blob *ob, struct Blob *text, void *opaque); void (*table)(struct Blob *ob, struct Blob *head_row, struct Blob *rows, void *opaque); void (*table_cell)(struct Blob *ob, struct Blob *text, int flags, void *opaque); void (*table_row)(struct Blob *ob, struct Blob *cells, int flags, void *opaque); void (*footnote_item)(struct Blob *ob, const struct Blob *text, int index, int nUsed, void *opaque); /* span level callbacks - NULL or return 0 prints the span verbatim */ int (*autolink)(struct Blob *ob, struct Blob *link, enum mkd_autolink type, void *opaque); int (*codespan)(struct Blob *ob, struct Blob *text, int nSep, void *opaque); int (*double_emphasis)(struct Blob *ob, struct Blob *text, char c, void *opaque); int (*emphasis)(struct Blob *ob, struct Blob *text, char c,void*opaque); int (*image)(struct Blob *ob, struct Blob *link, struct Blob *title, struct Blob *alt, void *opaque); int (*linebreak)(struct Blob *ob, void *opaque); int (*link)(struct Blob *ob, struct Blob *link, struct Blob *title, struct Blob *content, void *opaque); int (*raw_html_tag)(struct Blob *ob, struct Blob *tag, void *opaque); int (*triple_emphasis)(struct Blob *ob, struct Blob *text, char c, void *opaque); int (*footnote_ref)(struct Blob *ob, const struct Blob *span, const struct Blob *upc, int index, int locus, void *opaque); /* low level callbacks - NULL copies input directly into the output */ void (*entity)(struct Blob *ob, struct Blob *entity, void *opaque); void (*normal_text)(struct Blob *ob, struct Blob *text, void *opaque); /* renderer data */ const char *emph_chars; /* chars that trigger emphasis rendering */ void *opaque; /* opaque data send to every rendering callback */ }; /********* * FLAGS * *********/ /* list/listitem flags */ #define MKD_LIST_ORDERED 1 #define MKD_LI_BLOCK 2 /* <li> containing block data */ /* table cell flags */ #define MKD_CELL_ALIGN_DEFAULT 0 #define MKD_CELL_ALIGN_LEFT 1 #define MKD_CELL_ALIGN_RIGHT 2 #define MKD_CELL_ALIGN_CENTER 3 /* LEFT | RIGHT */ #define MKD_CELL_ALIGN_MASK 3 #define MKD_CELL_HEAD 4 #endif /* INTERFACE */ #define BLOB_COUNT(pBlob,el_type) (blob_size(pBlob)/sizeof(el_type)) #define COUNT_FOOTNOTES(pBlob) BLOB_COUNT(pBlob,struct footnote) #define CAST_AS_FOOTNOTES(pBlob) ((struct footnote*)blob_buffer(pBlob)) /*************** * LOCAL TYPES * ***************/ /* ** link_ref -- reference to a link. */ struct link_ref { struct Blob id; /* must be the first field as in footnote struct */ struct Blob link; struct Blob title; }; /* ** A footnote's data. ** id, text, and upc fields must be in that particular order. */ struct footnote { struct Blob id; /* must be the first field as in link_ref struct */ struct Blob text; /* footnote's content that is rendered at the end */ struct Blob upc; /* user-provided classes .ASCII-alnum.or-hypen: */ int bRndred; /* indicates if `text` holds a rendered content */ int defno; /* serial number of definition, set during the first pass */ int index; /* set to the index within array after ordering by id */ int iMark; /* user-visible numeric marker, assigned upon the first use*/ int nUsed; /* counts references to this note, increments upon each use*/ }; #define FOOTNOTE_INITIALIZER {empty_blob,empty_blob,empty_blob, 0,0,0,0,0} /* char_trigger -- function pointer to render active chars */ /* returns the number of chars taken care of */ /* data is the pointer of the beginning of the span */ /* offset is the number of valid chars before data */ struct render; typedef size_t (*char_trigger)( struct Blob *ob, struct render *rndr, char *data, size_t offset, size_t size); /* render -- structure containing one particular render */ struct render { struct mkd_renderer make; struct Blob refs; char_trigger active_char[256]; int iDepth; /* Depth of recursion */ int nBlobCache; /* Number of entries in aBlobCache */ struct Blob *aBlobCache[20]; /* Cache of Blobs available for reuse */ struct { Blob all; /* Buffer that holds array of footnotes. Its underline memory may be reallocated when a new footnote is added. */ int nLbled; /* number of labeled footnotes found during the first pass */ int nMarks; /* counts distinct indices found during the second pass */ struct footnote misref; /* nUsed counts misreferences, iMark must be -1 */ } notes; }; /* 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. ** ** When these HTML tags are separated from other text by newlines ** then they are rendered verbatim. Their content is not interpreted ** in any way. */ static const struct html_tag block_tags[] = { { "html", 4 }, { "pre", 3 }, { "script", 6 }, }; /*************************** * STATIC HELPER FUNCTIONS * ***************************/ /* ** build_ref_id -- collapse whitespace from input text to make it a ref id. ** Potential TODO: maybe also handle CR+LF line endings? */ static int build_ref_id(struct Blob *id, const char *data, size_t size){ size_t beg, i; char *id_data; /* skip leading whitespace */ while( size>0 && (data[0]==' ' || data[0]=='\t' || data[0]=='\n') ){ data++; size--; } /* skip trailing whitespace */ while( size>0 && (data[size-1]==' ' || data[size-1]=='\t' || data[size-1]=='\n') ){ size--; } if( size==0 ) return -1; /* making the ref id */ i = 0; blob_reset(id); while( i<size ){ /* copy non-whitespace into the output buffer */ beg = i; while( i<size && !(data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; } blob_append(id, data+beg, i-beg); /* add a single space and skip all consecutive whitespace */ if( i<size ) blob_append_char(id, ' '); while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; } } /* turn upper-case ASCII into their lower-case counterparts */ id_data = blob_buffer(id); for(i=0; i<blob_size(id); i++){ if( id_data[i]>='A' && id_data[i]<='Z' ) id_data[i] += 'a' - 'A'; } return 0; } /* cmp_link_ref -- comparison function for link_ref sorted arrays */ static int cmp_link_ref(const void *key, const void *array_entry){ struct link_ref *lr = (void *)array_entry; return blob_compare((void *)key, &lr->id); } /* cmp_link_ref_sort -- comparison function for link_ref qsort */ static int cmp_link_ref_sort(const void *a, const void *b){ struct link_ref *lra = (void *)a; struct link_ref *lrb = (void *)b; return blob_compare(&lra->id, &lrb->id); } /* ** cmp_footnote_id -- comparison function for footnotes qsort. ** Empty IDs sort last (in undetermined order). ** Equal IDs are sorted in the order of definition in the source. */ static int cmp_footnote_id(const void *fna, const void *fnb){ const struct footnote *a = fna, *b = fnb; const int szA = blob_size(&a->id), szB = blob_size(&b->id); if( szA ){ if( szB ){ int cmp = blob_compare(&a->id, &b->id); if( cmp ) return cmp; }else return -1; }else return szB ? 1 : 0; /* IDs are equal and non-empty */ if( a->defno < b->defno ) return -1; if( a->defno > b->defno ) return 1; assert(!"reachable"); return 0; /* should never reach here */ } /* ** cmp_footnote_sort -- comparison function for footnotes qsort. ** Unreferenced footnotes (when nUsed == 0) sort last and ** are sorted in the order of definition in the source. */ static int cmp_footnote_sort(const void *fna, const void *fnb){ const struct footnote *a = fna, *b = fnb; int i, j; assert( a->nUsed >= 0 ); assert( b->nUsed >= 0 ); assert( a->defno >= 0 ); assert( b->defno >= 0 ); if( a->nUsed ){ assert( a->iMark > 0 ); if( !b->nUsed ) return -1; assert( b->iMark > 0 ); i = a->iMark; j = b->iMark; }else{ if( b->nUsed ) return 1; i = a->defno; j = b->defno; } if( i < j ) return -1; if( i > j ) return 1; return 0; } /* cmp_html_tag -- comparison function for bsearch() (stolen from discount) */ static int cmp_html_tag(const void *a, const void *b){ const struct html_tag *hta = a; const struct html_tag *htb = b; int sz = hta->size; int c; if( htb->size<sz ) sz = htb->size; c = fossil_strnicmp(hta->text, htb->text, sz); if( c==0 ) c = hta->size - htb->size; return c; } /* 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') || (data[i]>='a' && data[i]<='z')) ){ i++; } if( i>=size ) return 0; /* binary search of the tag */ key.text = data; key.size = i; return bsearch(&key, block_tags, count(block_tags), sizeof block_tags[0], cmp_html_tag); } /* return true if recursion has gone too deep */ static int too_deep(struct render *rndr){ return rndr->iDepth>200; } /* get a new working buffer from the cache or create one. return NULL ** if failIfDeep is true and the depth of recursion has gone too deep. */ static struct Blob *new_work_buffer(struct render *rndr){ struct Blob *ret; rndr->iDepth++; if( rndr->nBlobCache ){ ret = rndr->aBlobCache[--rndr->nBlobCache]; }else{ ret = fossil_malloc(sizeof(*ret)); } *ret = empty_blob; return ret; } /* release the given working buffer back to the cache */ static void release_work_buffer(struct render *rndr, struct Blob *buf){ if( !buf ) return; rndr->iDepth--; blob_reset(buf); if( rndr->nBlobCache < (int)(sizeof(rndr->aBlobCache)/sizeof(rndr->aBlobCache[0])) ){ rndr->aBlobCache[rndr->nBlobCache++] = buf; }else{ fossil_free(buf); } } /**************************** * INLINE PARSING FUNCTIONS * ****************************/ /* is_mail_autolink -- looks for the address part of a mail autolink and '>' */ /* this is less strict than the original markdown e-mail address matching */ static size_t is_mail_autolink(char *data, size_t size){ size_t i = 0, nb = 0; /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */ while( i<size && (data[i]=='-' || data[i]=='.' || data[i]=='_' || data[i]=='@' || (data[i]>='a' && data[i]<='z') || (data[i]>='A' && data[i]<='Z') || (data[i]>='0' && data[i]<='9')) ){ if( data[i]=='@' ) nb++; i++; } if( i>=size || data[i]!='>' || nb!=1 ) return 0; return i+1; } /* tag_length -- returns the length of the given tag, or 0 if it's not valid */ static size_t tag_length(char *data, size_t size, enum mkd_autolink *autolink){ size_t i, j; /* a valid tag can't be shorter than 3 chars */ if( size<3 ) return 0; /* begins with a '<' optionally followed by '/', followed by letter */ if( data[0]!='<' ) return 0; i = (data[1]=='/') ? 2 : 1; if( (data[i]<'a' || data[i]>'z') && (data[i]<'A' || data[i]>'Z') ){ if( data[1]=='!' && size>=7 && data[2]=='-' && data[3]=='-' ){ for(i=6; i<size && (data[i]!='>'||data[i-1]!='-'|| data[i-2]!='-');i++){} if( i<size ) return i; } return 0; } /* scheme test */ *autolink = MKDA_NOT_AUTOLINK; if( size>6 && fossil_strnicmp(data+1, "http", 4)==0 && (data[5]==':' || ((data[5]=='s' || data[5]=='S') && data[6]==':')) ){ i = (data[5]==':') ? 6 : 7; *autolink = MKDA_NORMAL; }else if( size>5 && fossil_strnicmp(data+1, "ftp:", 4)==0 ){ i = 5; *autolink = MKDA_NORMAL; }else if( size>7 && fossil_strnicmp(data+1, "mailto:", 7)==0 ){ i = 8; /* not changing *autolink to go to the address test */ } /* completing autolink test: no whitespace or ' or " */ if( i>=size || i=='>' ){ *autolink = MKDA_NOT_AUTOLINK; }else if( *autolink ){ 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; }else if( (j = is_mail_autolink(data+i, size-i))!=0 ){ *autolink = (i==8) ? MKDA_EXPLICIT_EMAIL : MKDA_IMPLICIT_EMAIL; return i+j; } /* looking for something looking like a tag end */ while( i<size && data[i]!='>' ){ i++; } if( i>=size ) return 0; return i+1; } /* parse_inline -- parses inline markdown elements */ static void parse_inline( struct Blob *ob, struct render *rndr, char *data, size_t size ){ size_t i = 0, end = 0; char_trigger action = 0; struct Blob work = BLOB_INITIALIZER; if( too_deep(rndr) ){ blob_append(ob, data, size); return; } while( i<size ){ /* copying inactive chars into the output */ while( end<size && (action = rndr->active_char[(unsigned char)data[end]])==0 ){ end++; } if( end>i ){ if( rndr->make.normal_text ){ blob_init(&work, data+i, end-i); rndr->make.normal_text(ob, &work, rndr->make.opaque); }else{ blob_append(ob, data+i, end-i); } } if( end>=size ) break; i = end; /* calling the trigger */ end = action(ob, rndr, data+i, i, size-i); if( !end ){ /* no action from the callback */ end = i+1; }else{ i += end; end = i; } } } /* ** data[*pI] should be a "`" character that introduces a code-span. ** The code-span boundry mark can be any number of one or more "`" ** characters. We do not know the size of the boundry marker, only ** that there is at least one "`" at data[*pI]. ** ** This routine increases *pI to move it past the code-span, including ** the closing boundary mark. Or, if the code-span is unterminated, ** this routine moves *pI past the opening boundary mark only. */ static void skip_codespan(const char *data, size_t size, size_t *pI){ size_t i = *pI; size_t span_nb; /* Number of "`" characters in the boundary mark */ size_t bt; assert( i<size ); assert( data[i]=='`' ); data += i; size -= i; /* counting the number of opening backticks */ i = 0; span_nb = 0; while( i<size && data[i]=='`' ){ i++; span_nb++; } if( i>=size ){ *pI += span_nb; return; } /* finding the matching closing sequence */ bt = 0; while( i<size && bt<span_nb ){ if( data[i]=='`' ) bt += 1; else bt = 0; i++; } *pI += (bt == span_nb) ? i : span_nb; } /* find_emph_char -- looks for the next emph char, skipping other constructs */ static size_t find_emph_char(char *data, size_t size, char c){ size_t i = data[0]!='`'; while( i<size ){ while( i<size && data[i]!=c && data[i]!='`' && data[i]!='[' ){ i++; } if( i>=size ) return 0; /* not counting escaped chars */ if( i && data[i-1]=='\\' ){ i++; continue; } if( data[i]==c ) return i; if( data[i]=='`' ){ /* skip a code span */ skip_codespan(data, size, &i); }else if( data[i]=='[' ){ /* skip a link */ size_t tmp_i = 0; char cc; i++; while( i<size && data[i]!=']' ){ if( !tmp_i && data[i]==c ) tmp_i = i; i++; } i++; while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; } if( i>=size ) return tmp_i; if( data[i]!='[' && data[i]!='(' ){ /* not a link*/ if( tmp_i ) return tmp_i; else continue; } cc = data[i]; i++; while( i<size && data[i]!=cc ){ if( !tmp_i && data[i]==c ) tmp_i = i; i++; } if( i>=size ) return tmp_i; i++; } } return 0; } /* CommonMark defines separate "right-flanking" and "left-flanking" ** deliminators for emphasis. Whether a deliminator is left- or ** right-flanking, or both, or neither depends on the characters ** immediately before and after. ** ** before after example left-flanking right-flanking ** ------ ----- ------- ------------- -------------- ** space space * no no ** space punct *) yes no ** space alnum *x yes no ** punct space (* no yes ** punct punct (*) yes yes ** punct alnum (*x yes no ** alnum space a* no yes ** alnum punct a*( no yes ** alnum alnum a*x yes yes ** ** The following routines determine whether a delimitor is left ** or right flanking. */ static int left_flanking(char before, char after){ if( fossil_isspace(after) ) return 0; if( fossil_isalnum(after) ) return 1; if( fossil_isalnum(before) ) return 0; return 1; } static int right_flanking(char before, char after){ if( fossil_isspace(before) ) return 0; if( fossil_isalnum(before) ) return 1; if( fossil_isalnum(after) ) return 0; return 1; } /* ** parse_emph1 -- parsing single emphasis. ** closed by a symbol not preceded by whitespace and not followed by symbol. */ static size_t parse_emph1( struct Blob *ob, struct render *rndr, char *data, size_t size, char c ){ size_t i = 0, len; struct Blob *work = 0; int r; char after; if( !rndr->make.emphasis ) return 0; /* skipping one symbol if coming from emph3 */ if( size>1 && data[0]==c && data[1]==c ) i = 1; while( i<size ){ len = find_emph_char(data+i, size-i, c); if( !len ) return 0; i += len; if( i>=size ) return 0; if( i+1<size && data[i+1]==c ){ i++; continue; } after = i+1<size ? data[i+1] : ' '; if( data[i]==c && right_flanking(data[i-1],after) && (c!='_' || !fossil_isalnum(after)) && !too_deep(rndr) ){ work = new_work_buffer(rndr); parse_inline(work, rndr, data, i); r = rndr->make.emphasis(ob, work, c, rndr->make.opaque); release_work_buffer(rndr, work); return r ? i+1 : 0; } } return 0; } /* ** parse_emph2 -- parsing single emphasis. */ static size_t parse_emph2( struct Blob *ob, struct render *rndr, char *data, size_t size, char c ){ size_t i = 0, len; struct Blob *work = 0; int r; char after; if( !rndr->make.double_emphasis ) return 0; while( i<size ){ len = find_emph_char(data+i, size-i, c); if( !len ) return 0; i += len; after = i+2<size ? data[i+2] : ' '; if( i+1<size && data[i]==c && data[i+1]==c && right_flanking(data[i-1],after) && (c!='_' || !fossil_isalnum(after)) && !too_deep(rndr) ){ work = new_work_buffer(rndr); parse_inline(work, rndr, data, i); r = rndr->make.double_emphasis(ob, work, c, rndr->make.opaque); release_work_buffer(rndr, work); return r ? i+2 : 0; } i++; } return 0; } /* ** parse_emph3 -- parsing single emphasis. ** finds the first closing tag, and delegates to the other emph. */ static size_t parse_emph3( struct Blob *ob, struct render *rndr, char *data, size_t size, char c ){ size_t i = 0, len; int r; while( i<size ){ len = find_emph_char(data+i, size-i, c); if( !len ) return 0; i += len; /* skip whitespace preceded symbols */ if( data[i]!=c || data[i-1]==' ' || data[i-1]=='\t' || data[i-1]=='\n' ){ continue; } if( i+2<size && data[i+1]==c && data[i+2] == c && rndr->make.triple_emphasis && !too_deep(rndr) ){ /* triple symbol found */ struct Blob *work = new_work_buffer(rndr); parse_inline(work, rndr, data, i); r = rndr->make.triple_emphasis(ob, work, c, rndr->make.opaque); release_work_buffer(rndr, work); return r ? i+3 : 0; }else if( i+1<size && data[i+1]==c ){ /* double symbol found, handing over to emph1 */ len = parse_emph1(ob, rndr, data-2, size+2, c); return len ? len-2 : 0; }else{ /* single symbol found, handing over to emph2 */ len = parse_emph2(ob, rndr, data-1, size+1, c); return len ? len-1 : 0; } } return 0; } /* ** char_emphasis -- single and double emphasis parsing. */ static size_t char_emphasis( struct Blob *ob, struct render *rndr, char *data, size_t offset, size_t size ){ char c = data[0]; char before = offset>0 ? data[-1] : ' '; size_t ret; if( size>2 && data[1]!=c ){ if( !left_flanking(before, data[1]) || (c=='_' && fossil_isalnum(before)) || (ret = parse_emph1(ob, rndr, data+1, size-1, c))==0 ){ return 0; } return ret+1; } if( size>3 && data[1]==c && data[2]!=c ){ if( !left_flanking(before, data[2]) || (c=='_' && fossil_isalnum(before)) || (ret = parse_emph2(ob, rndr, data+2, size-2, c))==0 ){ return 0; } return ret+2; } if( size>4 && data[1]==c && data[2]==c && data[3]!=c ){ if( !left_flanking(before, data[3]) || (c=='_' && fossil_isalnum(before)) || (ret = parse_emph3(ob, rndr, data+3, size-3, c))==0 ){ return 0; } return ret+3; } return 0; } /* ** char_linebreak -- '\n' preceded by two spaces (assuming linebreak != 0). */ static size_t char_linebreak( struct Blob *ob, struct render *rndr, char *data, size_t offset, size_t size ){ if( offset<2 || data[-1]!=' ' || data[-2]!=' ' ) return 0; /* removing the last space from ob and rendering */ if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]==' ' ) ob->nUsed--; return rndr->make.linebreak(ob, rndr->make.opaque) ? 1 : 0; } /* ** char_codespan -- '`' parsing a code span (assuming codespan != 0). */ static size_t char_codespan( struct Blob *ob, struct render *rndr, char *data, size_t offset, size_t size ){ size_t end, nb = 0, i, f_begin, f_end; char delim = data[0]; /* counting the number of backticks in the delimiter */ while( nb<size && data[nb]==delim ){ nb++; } /* finding the next delimiter */ i = 0; for(end=nb; end<size && i<nb; end++){ if( data[end]==delim ) i++; else i = 0; } if( i<nb && end>=size ) return 0; /* no matching delimiter */ /* trimming outside whitespaces */ f_begin = nb; while( f_begin<end && (data[f_begin]==' ' || data[f_begin]=='\t') ){ f_begin++; } f_end = end-nb; while( f_end>nb && (data[f_end-1]==' ' || data[f_end-1]=='\t') ){ f_end--; } /* real code span */ if( f_begin<f_end ){ struct Blob work = BLOB_INITIALIZER; blob_init(&work, data+f_begin, f_end-f_begin); if( !rndr->make.codespan(ob, &work, nb, rndr->make.opaque) ) end = 0; }else{ if( !rndr->make.codespan(ob, 0, nb, rndr->make.opaque) ) end = 0; } return end; } /* ** char_escape -- '\\' backslash escape. */ static size_t char_escape( struct Blob *ob, struct render *rndr, char *data, size_t offset, size_t size ){ struct Blob work = BLOB_INITIALIZER; if( size>1 ){ if( rndr->make.normal_text ){ blob_init(&work, data+1,1); rndr->make.normal_text(ob, &work, rndr->make.opaque); }else{ blob_append(ob, data+1, 1); } } return 2; } /* ** char_entity -- '&' escaped when it doesn't belong to an entity. ** valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */ static size_t char_entity( struct Blob *ob, struct render *rndr, char *data, size_t offset, size_t size ){ size_t end = 1; struct Blob work = BLOB_INITIALIZER; if( end<size && data[end]=='#' ) end++; while( end<size && ((data[end]>='0' && data[end]<='9') || (data[end]>='a' && data[end]<='z') || (data[end]>='A' && data[end]<='Z')) ){ end++; } if( end<size && data[end]==';' ){ /* real entity */ end++; }else{ /* lone '&' */ return 0; } if( rndr->make.entity ){ blob_init(&work, data, end); rndr->make.entity(ob, &work, rndr->make.opaque); }else{ blob_append(ob, data, end); } return end; } /* ** char_langle_tag -- '<' when tags or autolinks are allowed. */ static size_t char_langle_tag( struct Blob *ob, struct render *rndr, char *data, size_t offset, size_t size ){ enum mkd_autolink altype = MKDA_NOT_AUTOLINK; size_t end = tag_length(data, size, &altype); struct Blob work = BLOB_INITIALIZER; int ret = 0; if( end ){ if( rndr->make.autolink && altype!=MKDA_NOT_AUTOLINK ){ blob_init(&work, data+1, end-2); ret = rndr->make.autolink(ob, &work, altype, rndr->make.opaque); }else if( rndr->make.raw_html_tag ){ blob_init(&work, data, end); ret = rndr->make.raw_html_tag(ob, &work, rndr->make.opaque); } } if( !ret ){ 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; size_t link_b, link_e; size_t title_b = 0, title_e = 0; /* skipping initial whitespace */ while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; } link_b = i; /* looking for link end: ' " */ while( i<size && data[i]!='\'' && data[i]!='"' ){ i++; } link_e = i; /* looking for title end if present */ if( data[i]=='\'' || data[i]=='"' ){ i++; title_b = i; /* skipping whitespaces after title */ title_e = size-1; while( title_e>title_b && (data[title_e]==' ' || data[title_e]=='\t' || data[title_e]=='\n') ){ title_e--; } /* checking for closing quote presence */ if (data[title_e] != '\'' && data[title_e] != '"') { title_b = title_e = 0; link_e = i; } } /* remove whitespace at the end of the link */ while( link_e>link_b && (data[link_e-1]==' ' || data[link_e-1]=='\t' || data[link_e-1]=='\n') ){ link_e--; } /* remove optional angle brackets around the link */ if( data[link_b]=='<' ) link_b += 1; if( link_e && data[link_e-1]=='>' ) link_e -= 1; /* escape backslashed character from link */ blob_reset(link); i = link_b; while( i<link_e ){ mark = i; while( i<link_e && data[i]!='\\' ){ i++; } blob_append(link, data+mark, i-mark); while( i<link_e && data[i]=='\\' ){ i++; } } /* handing back title */ blob_reset(title); if( title_e>title_b ) blob_append(title, data+title_b, title_e-title_b); /* this function always succeed */ return 0; } /* ** get_link_ref -- extract referenced link and title from id. */ static int get_link_ref( struct render *rndr, struct Blob *link, struct Blob *title, char *data, size_t size ){ struct link_ref *lr; const size_t sz = blob_size(&rndr->refs); /* find the link from its id (stored temporarily in link) */ blob_reset(link); if( !sz || build_ref_id(link, data, size)<0 ) return -1; lr = bsearch(link, blob_buffer(&rndr->refs), sz/sizeof(struct link_ref), sizeof (struct link_ref), cmp_link_ref); if( !lr ) return -1; /* fill the output buffers */ blob_reset(link); blob_reset(title); blob_appendb(link, &lr->link); blob_appendb(title, &lr->title); return 0; } /* ** get_footnote() -- find a footnote by label, invoked during the 2nd pass. ** If found then return a shallow copy of the corresponding footnote; ** otherwise return a shallow copy of rndr->notes.misref. ** In both cases corresponding `nUsed` field is incremented before return. */ static struct footnote get_footnote( struct render *rndr, const char *data, size_t size ){ struct footnote *fn = 0; struct Blob *id; if( !rndr->notes.nLbled ) goto fallback; id = new_work_buffer(rndr); if( build_ref_id(id, data, size)<0 ) goto cleanup; fn = bsearch(id, blob_buffer(&rndr->notes.all), rndr->notes.nLbled, sizeof (struct footnote), cmp_link_ref); if( !fn ) goto cleanup; if( fn->nUsed == 0 ){ /* the first reference to the footnote */ assert( fn->iMark == 0 ); fn->iMark = ++(rndr->notes.nMarks); } assert( fn->iMark > 0 ); cleanup: release_work_buffer( rndr, id ); fallback: if( !fn ) fn = &rndr->notes.misref; fn->nUsed++; assert( fn->nUsed > 0 ); return *fn; } /* ** Counts characters in the blank prefix within at most nHalfLines. ** A sequence of spaces and tabs counts as odd halfline, ** a newline counts as even halfline. ** If nHalfLines < 0 then proceed without constraints. */ static inline size_t sizeof_blank_prefix( const char *data, size_t size, int nHalfLines ){ const char *p = data; const char * const end = data+size; if( nHalfLines < 0 ){ while( p!=end && fossil_isspace(*p) ){ p++; } }else while( nHalfLines > 0 ){ while( p!=end && (*p==' ' || *p=='\t' ) ){ p++; } if( p==end || --nHalfLines == 0 ) break; if( *p=='\n' || *p=='\r' ){ p++; if( p==end ) break; if( *p=='\n' && p[-1]=='\r' ){ p++; } } nHalfLines--; } return p-data; } /* ** Check if the data starts with a classlist token of the special form. ** If so then return the length of that token, otherwise return 0. ** ** The token must start with a dot and must end with a colon; ** in between of these it must be a dot-separated list of words; ** each word may contain only alphanumeric characters and hyphens. ** ** If `bBlank` is non-zero then a blank character must follow ** the token's ending colon: otherwise function returns 0 ** despite the well-formed token. */ static size_t is_footnote_classlist(const char * const data, size_t size, int bBlank){ const char *p; const char * const end = data+size; if( data==end || *data != '.' ) return 0; for(p=data+1; p!=end; p++){ if( fossil_isalnum(*p) || *p=='-' ) continue; if( p[-1]=='.' ) break; if( *p==':' ){ p++; if( bBlank ){ if( p==end || !fossil_isspace(*p) ) break; } return p-data; } if( *p!='.' ) break; } return 0; } /* ** Adds unlabeled footnote to the rndr->notes.all. ** On success puts a shallow copy of the constructed footnote into pFN ** and returns 1, otherwise pFN is unchanged and 0 is returned. */ static inline int add_inline_footnote( struct render *rndr, const char *text, size_t size, struct footnote* pFN ){ struct footnote fn = FOOTNOTE_INITIALIZER, *last; const char *zUPC = 0; size_t nUPC = 0, n = sizeof_blank_prefix(text, size, 3); if( n >= size ) return 0; text += n; size -= n; nUPC = is_footnote_classlist(text, size, 1); if( nUPC ){ assert( nUPC<size ); zUPC = text; text += nUPC; size -= nUPC; } if( sizeof_blank_prefix(text,size,-1)==size ){ if( !nUPC ) return 0; /* empty inline footnote */ text = zUPC; size = nUPC; /* bare classlist is treated */ nUPC = 0; /* as plain text */ } fn.iMark = ++(rndr->notes.nMarks); fn.nUsed = 1; fn.index = COUNT_FOOTNOTES(&rndr->notes.all); assert( fn.iMark > 0 ); blob_append(&fn.text, text, size); if(nUPC) blob_append(&fn.upc, zUPC, nUPC); blob_append(&rndr->notes.all, (char *)&fn, sizeof fn); last = (struct footnote*)( blob_buffer(&rndr->notes.all) +( blob_size(&rndr->notes.all)-sizeof fn )); assert( pFN ); memcpy( pFN, last, sizeof fn ); return 1; } /* ** Return the byte offset of the matching closing bracket or 0 if not ** found. begin[0] must be either '[' or '('. ** ** TODO: It seems that things like "\\(" are not handled correctly. ** That is historical behavior for a corner-case, ** so it's left as it is until somebody complains. */ static inline size_t matching_bracket_offset( const char* begin, const char* end ){ const char *i; int level; const char bra = *begin; const char ket = bra=='[' ? ']' : ')'; assert( bra=='[' || bra=='(' ); for(i=begin+1,level=1; i!=end; i++){ if( *i=='\n' ) /* do nothing */; else if( i[-1]=='\\' ) continue; else if( *i==bra ) level++; else if( *i==ket ){ if( --level<=0 ) return i-begin; } } return 0; } /* ** char_footnote -- '(': parsing a standalone inline footnote. */ static size_t char_footnote( struct Blob *ob, struct render *rndr, char *data, size_t offset, size_t size ){ size_t end; struct footnote fn; if( size<4 || data[1]!='^' ) return 0; end = matching_bracket_offset(data, data+size); if( !end ) return 0; if( !add_inline_footnote(rndr, data+2, end-2, &fn) ) return 0; if( rndr->make.footnote_ref ){ rndr->make.footnote_ref(ob,0,&fn.upc,fn.iMark,1,rndr->make.opaque); } return end+1; } /* ** char_link -- '[': parsing a link or an image. */ static size_t char_link( struct Blob *ob, struct render *rndr, char *data, size_t offset, size_t size /* parse_inline() ensures that size > 0 */ ){ const int bFsfn = (size>3 && data[1]=='^'); /*free-standing footnote ref*/ const int bImg = !bFsfn && (offset && data[-1] == '!'); size_t i, txt_e; struct Blob *content; struct Blob *link; struct Blob *title; struct footnote fn; int ret; /* checking whether the correct renderer exists */ if( !bFsfn ){ if( (bImg && !rndr->make.image) || (!bImg && !rndr->make.link) ){ return 0; } } /* looking for the matching closing bracket */ txt_e = matching_bracket_offset(data, data+size); if( !txt_e ) return 0; i = txt_e + 1; ret = 0; /* error if we don't get to the callback */ /* free-standing footnote reference */ if( bFsfn ){ fn = get_footnote(rndr, data+2, txt_e-2); content = link = title = 0; }else{ fn.nUsed = 0; /* skip "inter-bracket-whitespace" - any amount of whitespace or newline */ /* (this is much more lax than original markdown syntax) */ while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; } /* allocate temporary buffers to store content, link and title */ title = new_work_buffer(rndr); content = new_work_buffer(rndr); link = new_work_buffer(rndr); if( i<size && data[i]=='(' ){ if( i+2<size && data[i+1]=='^' ){ /* span-bounded inline footnote */ const size_t k = matching_bracket_offset(data+i, data+size); if( !k ) goto char_link_cleanup; add_inline_footnote(rndr, data+(i+2), k-2, &fn); i += k+1; }else{ /* inline style link */ size_t span_end = i; while( span_end<size && !(data[span_end]==')' && (span_end==i || data[span_end-1]!='\\')) ){ span_end++; } if( span_end>=size || get_link_inline(link, title, data+i+1, span_end-(i+1))<0 ){ goto char_link_cleanup; } i = span_end+1; } /* reference style link or span-bounded footnote reference */ }else if( i<size && data[i]=='[' ){ char *id_data; size_t id_size, id_end = i; int bFootnote; while( id_end<size && data[id_end]!=']' ){ id_end++; } if( id_end>=size ) goto char_link_cleanup; bFootnote = data[i+1]=='^'; if( i+1==id_end || (bFootnote && i+2==id_end) ){ /* implicit id - use the contents */ id_data = data+1; id_size = txt_e-1; }else{ /* explicit id - between brackets */ id_data = data+i+1; id_size = id_end-(i+1); if( bFootnote ){ id_data++; id_size--; } } if( bFootnote ){ fn = get_footnote(rndr, id_data, id_size); }else if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){ goto char_link_cleanup; } i = id_end+1; /* shortcut reference style link */ }else{ if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){ goto char_link_cleanup; } /* rewinding an "inter-bracket-whitespace" */ i = txt_e+1; } } /* building content: img alt is escaped, link content is parsed */ if( txt_e>1 && content ){ if( bImg ) blob_append(content, data+1, txt_e-1); else parse_inline(content, rndr, data+1, txt_e-1); } /* calling the relevant rendering function */ if( bImg ){ if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ){ ob->nUsed--; } ret = rndr->make.image(ob, link, title, content, rndr->make.opaque); }else if( fn.nUsed ){ if( rndr->make.footnote_ref ){ ret = rndr->make.footnote_ref(ob, content, &fn.upc, fn.iMark, fn.nUsed, rndr->make.opaque); } }else{ ret = rndr->make.link(ob, link, title, content, rndr->make.opaque); } /* cleanup */ char_link_cleanup: release_work_buffer(rndr, title); release_work_buffer(rndr, link); release_work_buffer(rndr, content); return ret ? i : 0; } /********************************* * 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; } /* is_hrule -- returns whether a line is a horizontal rule */ static int is_hrule(char *data, size_t size){ size_t i = 0, n = 0; char c; /* skipping initial spaces */ if( size<3 ) return 0; if( data[0]==' ' ){ i++; if( data[1]==' ' ){ i++; if( data[2]==' ' ){ i++; } } } /* looking at the hrule char */ if( i+2>=size || (data[i]!='*' && data[i]!='-' && data[i]!='_') ) return 0; c = data[i]; /* the whole line must be the char or whitespace */ while (i < size && data[i] != '\n') { if( data[i]==c ){ n += 1; }else if( data[i]!=' ' && data[i]!='\t' ){ return 0; } i++; } return n>=3; } /* is_headerline -- returns whether the line is a setext-style hdr underline */ static int is_headerline(char *data, size_t size){ size_t i = 0; /* test of level 1 header */ if( data[i]=='=' ){ for(i=1; i<size && data[i]=='='; i++); while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; } return (i>=size || data[i]=='\n') ? 1 : 0; } /* test of level 2 header */ if( data[i]=='-' ){ for(i=1; i<size && data[i]=='-'; i++); while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; } return (i>=size || data[i]=='\n') ? 2 : 0; } return 0; } /* is_table_sep -- returns whether there is a table separator at pos */ static int is_table_sep(char *data, size_t pos){ return data[pos]=='|' && (pos==0 || data[pos-1]!='\\'); } /* is_tableline -- returns the number of column tables in the given line */ static int is_tableline(char *data, size_t size){ size_t i = 0; int n_sep = 0, outer_sep = 0; /* skip initial blanks */ while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; } /* check for initial '|' */ if( i<size && data[i]=='|') outer_sep++; /* count the number of pipes in the line */ for(n_sep=0; i<size && data[i]!='\n'; i++){ if( is_table_sep(data, i) ) n_sep++; if( data[i]=='`' ){ skip_codespan(data, size, &i); i--; } } /* march back to check for optional last '|' before blanks and EOL */ while( i && (data[i-1]==' ' || data[i-1]=='\t' || data[i-1]=='\n') ){ i--; } if( i && is_table_sep(data, i-1) ) outer_sep += 1; /* return the number of column or 0 if it's not a table line */ return (n_sep>0) ? (n_sep-outer_sep+1) : 0; } /* prefix_quote -- returns blockquote prefix length */ static size_t prefix_quote(char *data, size_t size){ size_t i = 0; if( i<size && data[i]==' ' ) i++; if( i<size && data[i]==' ' ) i++; if( i<size && data[i]==' ' ) i++; if( i<size && data[i]=='>' ){ if( i+1<size && (data[i+1]==' ' || data[i+1]=='\t') ){ return i + 2; }else{ return i + 1; } }else{ return 0; } } /* prefix_code -- returns prefix length for block code */ static size_t prefix_code(char *data, size_t size){ if( size>0 && data[0]=='\t' ) return 1; if( size>3 && data[0]==' ' && data[1]==' ' && data[2]==' ' && data[3]==' ' ){ return 4; } return 0; } /* Return the number of characters in the delimiter of a fenced code ** block. */ static size_t prefix_fencedcode(char *data, size_t size){ char c = data[0]; int nb; if( c!='`' && c!='~' ) return 0; for(nb=1; nb<(int)size-3 && data[nb]==c; nb++){} if( nb<3 ) return 0; if( nb>=(int)size-nb ) return 0; return nb; } /* prefix_oli -- returns ordered list item prefix */ static size_t prefix_oli(char *data, size_t size){ size_t i = 0; if( i<size && data[i]==' ') i++; if( i<size && data[i]==' ') i++; if( i<size && data[i]==' ') i++; if( i>=size || data[i]<'0' || data[i]>'9' ) return 0; while( i<size && data[i]>='0' && data[i]<='9' ){ i++; } if( i+1>=size || (data[i]!='.' && data[i]!=')') || (data[i+1]!=' ' && data[i+1]!='\t') ){ return 0; } i = i+2; while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; } return i; } /* prefix_uli -- returns ordered list item prefix */ static size_t prefix_uli(char *data, size_t size){ size_t i = 0; if( i<size && data[i]==' ') i++; if( i<size && data[i]==' ') i++; if( i<size && data[i]==' ') i++; if( i+1>=size || (data[i]!='*' && data[i]!='+' && data[i]!='-') || (data[i+1]!=' ' && data[i+1]!='\t') ){ return 0; } i = i+2; while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; } return i; } /* parse_block predeclaration */ static void parse_block( struct Blob *ob, struct render *rndr, char *data, size_t size); /* parse_blockquote -- handles parsing of a blockquote fragment */ static size_t parse_blockquote( struct Blob *ob, struct render *rndr, char *data, size_t size ){ size_t beg, end = 0, pre, work_size = 0, nb, endFence = 0; char *work_data = 0; struct Blob *out = new_work_buffer(rndr); /* Check to see if this is a quote of a fenced code block, because ** if it is, then blank lines do not terminated the quoted text. Ex: ** ** > ~~~~ ** First line ** ** Line after blank ** ~~~~ ** ** If this is a quoted fenced block, then set endFence to be the ** offset of the end of the fenced block. */ pre = prefix_quote(data,size); pre += is_empty(data+pre,size-pre); nb = prefix_fencedcode(data+pre,size-pre); if( nb ){ size_t i = 0; char delim = data[pre]; for(end=pre+nb; end<size && i<nb; end++){ if( data[end]==delim ) i++; else i = 0; } if( i>=nb ) endFence = end; } beg = 0; while( beg<size ){ for(end=beg+1; end<size && data[end-1]!='\n'; end++); pre = prefix_quote(data+beg, end-beg); if( pre ){ beg += pre; /* skipping prefix */ }else if( is_empty(data+beg, end-beg) && (end>=size || (end>endFence && prefix_quote(data+end, size-end)==0 && !is_empty(data+end, size-end))) ){ /* empty line followed by non-quote line */ break; } if( beg<end ){ /* copy into the in-place working buffer */ if( !work_data ){ work_data = data+beg; }else if( (data+beg)!=(work_data+work_size) ){ memmove(work_data+work_size, data+beg, end-beg); } work_size += end-beg; } beg = end; } if( rndr->make.blockquote ){ if( !too_deep(rndr) ){ parse_block(out, rndr, work_data, work_size); }else{ blob_append(out, work_data, work_size); } rndr->make.blockquote(ob, out, rndr->make.opaque); } release_work_buffer(rndr, out); return end; } /* parse_paragraph -- handles parsing of a regular paragraph */ static size_t parse_paragraph( struct Blob *ob, struct render *rndr, char *data, size_t size ){ size_t i = 0, end = 0; int level = 0; char *work_data = data; size_t work_size = 0; while( i<size ){ char *zEnd = memchr(data+i, '\n', size-i-1); end = zEnd==0 ? size : (size_t)(zEnd - (data-1)); /* The above is the same as: ** for(end=i+1; end<size && data[end-1]!='\n'; end++); ** "end" is left with a value such that data[end] is one byte ** past the first '\n' or one byte past the end of the string */ if( is_empty(data+i, size-i) || (level = is_headerline(data+i, size-i))!= 0 ){ break; } if( (i && data[i]=='#') || is_hrule(data+i, size-i) || prefix_uli(data+i, size-i) || prefix_oli(data+i, size-i) ){ end = i; break; } i = end; } work_size = i; while( work_size && data[work_size-1]=='\n' ){ work_size--; } if( !level ){ if( rndr->make.paragraph ){ struct Blob *tmp = new_work_buffer(rndr); parse_inline(tmp, rndr, work_data, work_size); rndr->make.paragraph(ob, tmp, rndr->make.opaque); release_work_buffer(rndr, tmp); } }else{ if( work_size ){ size_t beg; i = work_size; work_size -= 1; while( work_size && data[work_size]!='\n' ){ work_size--; } beg = work_size+1; while( work_size && data[work_size-1]=='\n'){ work_size--; } if( work_size ){ struct Blob *tmp = new_work_buffer(rndr); parse_inline(tmp, rndr, work_data, work_size); if( rndr->make.paragraph ){ rndr->make.paragraph(ob, tmp, rndr->make.opaque); } release_work_buffer(rndr, tmp); work_data += beg; work_size = i - beg; }else{ work_size = i; } } if( rndr->make.header ){ struct Blob *span = new_work_buffer(rndr); parse_inline(span, rndr, work_data, work_size); rndr->make.header(ob, span, level, rndr->make.opaque); release_work_buffer(rndr, span); } } return end; } /* parse_blockcode -- handles parsing of a block-level code fragment */ static size_t parse_blockcode( struct Blob *ob, struct render *rndr, char *data, size_t size ){ size_t beg, end, pre; struct Blob *work = new_work_buffer(rndr); beg = 0; while( beg<size ){ char *zEnd = memchr(data+beg, '\n', size-beg-1); end = zEnd==0 ? size : (size_t)(zEnd - (data-1)); /* The above is the same as: ** for(end=beg+1; end<size && data[end-1]!='\n'; end++); ** "end" is left with a value such that data[end] is one byte ** past the first \n or past then end of the string. */ pre = prefix_code(data+beg, end-beg); if( pre ){ beg += pre; /* skipping prefix */ }else if( !is_empty(data+beg, end-beg) ){ /* non-empty non-prefixed line breaks the pre */ break; } if( beg<end ){ /* verbatim copy to the working buffer, escaping entities */ if( is_empty(data + beg, end - beg) ){ blob_append_char(work, '\n'); }else{ blob_append(work, data+beg, end-beg); } } beg = end; } end = blob_size(work); while( end>0 && blob_buffer(work)[end-1]=='\n' ){ end--; } work->nUsed = end; blob_append_char(work, '\n'); if( work!=ob ){ if( rndr->make.blockcode ){ rndr->make.blockcode(ob, work, rndr->make.opaque); } release_work_buffer(rndr, work); } return beg; } /* parse_listitem -- parsing of a single list item */ /* assuming initial prefix is already removed */ static size_t parse_listitem( struct Blob *ob, struct render *rndr, char *data, size_t size, int *flags ){ struct Blob *work = 0, *inter = 0; size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i; int in_empty = 0, has_inside_empty = 0; /* keeping track of the first indentation prefix */ if( size>1 && data[0]==' ' ){ orgpre = 1; if( size>2 && data[1]==' ' ){ orgpre = 2; if( size>3 && data[2]==' ' ){ orgpre = 3; } } } beg = prefix_uli(data, size); if( !beg ) beg = prefix_oli(data, size); if( !beg ) return 0; /* skipping to the beginning of the following line */ end = beg; while( end<size && data[end-1]!='\n' ){ end++; } /* getting working buffers */ work = new_work_buffer(rndr); inter = new_work_buffer(rndr); /* putting the first line into the working buffer */ blob_append(work, data+beg, end-beg); beg = end; /* process the following lines */ while( beg<size ){ end++; while( end<size && data[end-1]!='\n' ){ end++; } /* process an empty line */ if( is_empty(data+beg, end-beg) ){ in_empty = 1; beg = end; continue; } /* computing the indentation */ i = 0; if( end-beg>1 && data[beg]==' ' ){ i = 1; if( end-beg>2 && data[beg+1]==' ' ){ i = 2; if( end-beg>3 && data[beg+2]==' ' ){ i = 3; if( end-beg>3 && data[beg+3]==' ' ){ i = 4; } } } } pre = i; if( data[beg]=='\t' ){ i = 1; pre = 8; } /* checking for a new item */ if( (prefix_uli(data+beg+i, end-beg-i) && !is_hrule(data+beg+i, end-beg-i)) || prefix_oli(data+beg+i, end-beg-i) ){ if( in_empty ) has_inside_empty = 1; if( pre == orgpre ){ /* the following item must have */ break; /* the same indentation */ } if( !sublist ) sublist = blob_size(work); /* joining only indented stuff after empty lines */ }else if( in_empty && i<4 && data[beg]!='\t' ){ *flags |= MKD_LI_END; break; }else if( in_empty ){ blob_append_char(work, '\n'); has_inside_empty = 1; } in_empty = 0; /* adding the line without prefix into the working buffer */ blob_append(work, data+beg+i, end-beg-i); beg = end; } /* non-recursive fallback when working buffer stack is full */ if( !inter ){ if( rndr->make.listitem ){ rndr->make.listitem(ob, work, *flags, rndr->make.opaque); } release_work_buffer(rndr, work); return beg; } /* render of li contents */ if( has_inside_empty ) *flags |= MKD_LI_BLOCK; if( *flags & MKD_LI_BLOCK ){ /* intermediate render of block li */ if( sublist && sublist<blob_size(work) ){ parse_block(inter, rndr, blob_buffer(work), sublist); parse_block(inter, rndr, blob_buffer(work)+sublist, blob_size(work)-sublist); }else{ parse_block(inter, rndr, blob_buffer(work), blob_size(work)); } }else{ /* intermediate render of inline li */ if( sublist && sublist<blob_size(work) ){ parse_inline(inter, rndr, blob_buffer(work), sublist); parse_block(inter, rndr, blob_buffer(work)+sublist, blob_size(work)-sublist); }else{ parse_inline(inter, rndr, blob_buffer(work), blob_size(work)); } } /* render of li itself */ if( rndr->make.listitem ){ rndr->make.listitem(ob, inter, *flags, rndr->make.opaque); } release_work_buffer(rndr, inter); release_work_buffer(rndr, work); return beg; } /* parse_list -- parsing ordered or unordered list block */ static size_t parse_list( struct Blob *ob, struct render *rndr, char *data, size_t size, int flags ){ struct Blob *work = new_work_buffer(rndr); size_t i = 0, j; while( i<size ){ 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); release_work_buffer(rndr, work); return i; } /* parse_atxheader -- parsing of atx-style headers */ static size_t parse_atxheader( struct Blob *ob, struct render *rndr, char *data, size_t size ){ int level = 0; size_t i, end, skip, span_beg, span_size; if( !size || data[0]!='#' ) return 0; while( level<(int)size && level<6 && data[level]=='#' ){ level++; } for(i=level; i<size && (data[i]==' ' || data[i]=='\t'); i++); if ( (int)i == level ) return parse_paragraph(ob, rndr, data, size); span_beg = i; for(end=i; end<size && data[end]!='\n'; end++); skip = end; if( end<=i ) return parse_paragraph(ob, rndr, data, size); while( end && data[end-1]=='#' ){ end--; } while( end && (data[end-1]==' ' || data[end-1]=='\t') ){ end--; } if( end<=i ) return parse_paragraph(ob, rndr, data, size); span_size = end-span_beg; if( rndr->make.header ){ struct Blob *span = new_work_buffer(rndr); parse_inline(span, rndr, data+span_beg, span_size); rndr->make.header(ob, span, level, rndr->make.opaque); release_work_buffer(rndr, span); } 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)>(int)size || fossil_strnicmp(data+2, tag->text, tag->size) || data[tag->size+2]!='>' ){ return 0; } /* checking white lines */ i = tag->size + 3; w = 0; if( i<size && (w = is_empty(data+i, size-i))==0 ){ return 0; /* non-blank after tag */ } i += w; w = 0; if( i<size && (w = is_empty(data + i, size - i))==0 ){ return 0; /* non-blank line after tag line */ } return i+w; } /* parse_htmlblock -- parsing of inline HTML block */ 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); /* handling of special cases */ if( !curtag ){ /* HTML comment, laxist form */ if( size>5 && data[1]=='!' && data[2]=='-' && data[3]=='-' ){ i = 5; while( i<size && !(data[i-2]=='-' && data[i-1]=='-' && data[i]=='>') ){ i++; } i++; if( i<size ){ j = is_empty(data+i, size-i); if( j ){ work_size = i+j; if( !rndr->make.blockhtml ) return work_size; blob_init(&work, data, work_size); rndr->make.blockhtml(ob, &work, rndr->make.opaque); return work_size; } } } /* HR, which is the only self-closing block tag considered */ if( size>4 && (data[1]=='h' || data[1]=='H') && (data[2]=='r' || data[2]=='R') ){ i = 3; while( i<size && data[i]!='>' ){ i++; } if( i+1<size ){ i += 1; j = is_empty(data+i, size-i); if( j ){ work_size = i+j; if( !rndr->make.blockhtml ) return work_size; blob_init(&work, data, work_size); rndr->make.blockhtml(ob, &work, rndr->make.opaque); return work_size; } } } /* no special case recognised */ return 0; } /* looking for an matching closing tag */ /* followed by a blank line */ i = 1; found = 0; while( i<size ){ i++; while( i<size && !(data[i-1]=='<' && data[i]=='/') ){ i++; } if( (i+2+curtag->size)>size ) break; j = htmlblock_end(curtag, data+i-1, size-i+1); if (j) { i += j-1; found = 1; break; } } if( !found ) return 0; /* the end of the block has been found */ if( strcmp(curtag->text,"html")==0 ){ /* Omit <html> tags */ enum mkd_autolink dummy; int k = tag_length(data, size, &dummy); int sz = i - (j+k); if( sz>0 ) blob_init(&work, data+k, sz); }else{ blob_init(&work, data, i); } if( rndr->make.blockhtml ){ rndr->make.blockhtml(ob, &work, rndr->make.opaque); } return i; } /* parse_table_cell -- parse a cell inside a table */ static void parse_table_cell( struct Blob *ob, /* output blob */ struct render *rndr, /* renderer description */ char *data, /* input text */ size_t size, /* input text size */ int flags /* table flags */ ){ struct Blob *span = new_work_buffer(rndr); parse_inline(span, rndr, data, size); rndr->make.table_cell(ob, span, flags, rndr->make.opaque); release_work_buffer(rndr, span); } /* parse_table_row -- parse an input line into a table row */ static size_t parse_table_row( struct Blob *ob, /* output blob for rendering */ struct render *rndr, /* renderer description */ char *data, /* input text */ size_t size, /* input text size */ int *aligns, /* array of default alignment for columns */ size_t align_size, /* number of columns with default alignment */ int flags /* table flags */ ){ size_t i = 0, col = 0; size_t beg, end, total = 0; struct Blob *cells = new_work_buffer(rndr); int align; /* skip leading blanks and separator */ while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; } if( i<size && data[i]=='|' ) i++; /* go over all the cells */ while( i<size && total==0 ){ /* check optional left/center align marker */ align = 0; if( data[i]==':' ){ align |= MKD_CELL_ALIGN_LEFT; i++; } /* skip blanks */ while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; } beg = i; /* forward to the next separator or EOL */ while( i<size && !is_table_sep(data, i) && data[i]!='\n' ){ if( data[i]=='`' ){ skip_codespan(data, size, &i); }else{ i++; } } end = i; if( i<size ){ i++; if( data[i-1]=='\n' ) total = i; } /* check optional right/center align marker */ if( i>beg && data[end-1]==':' ){ align |= MKD_CELL_ALIGN_RIGHT; end--; } /* remove trailing blanks */ while( end>beg && (data[end-1]==' ' || data[end-1]=='\t') ){ end--; } /* skip the last cell if it was only blanks */ /* (because it is only the optional end separator) */ if( total && end<=beg ) continue; /* fallback on default alignment if not explicit */ if( align==0 && aligns && col<align_size ) align = aligns[col]; /* render cells */ if( cells && end>=beg ){ parse_table_cell(cells, rndr, data+beg, end-beg, align|flags); } col++; } /* render the whole row and clean up */ rndr->make.table_row(ob, cells, flags, rndr->make.opaque); release_work_buffer(rndr, cells); return total ? total : size; } /* parse_table -- parsing of a whole table */ static size_t parse_table( struct Blob *ob, struct render *rndr, char *data, size_t size ){ size_t i = 0, head_end, col; size_t align_size = 0; int *aligns = 0; struct Blob *head = 0; struct Blob *rows = new_work_buffer(rndr); /* skip the first (presumably header) line */ while( i<size && data[i]!='\n' ){ i++; } head_end = i; /* fallback on end of input */ if( i>=size ){ parse_table_row(rows, rndr, data, size, 0, 0, 0); rndr->make.table(ob, 0, rows, rndr->make.opaque); release_work_buffer(rndr, rows); return i; } /* attempt to parse a table rule, i.e. blanks, dash, colons and sep */ i++; col = 0; while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='-' || data[i] == ':' || data[i] =='|') ){ if( data[i] == '|' ) align_size++; if( data[i] == ':' ) col = 1; i += 1; } if( i<size && data[i]=='\n' ){ align_size++; /* render the header row */ head = new_work_buffer(rndr); parse_table_row(head, rndr, data, head_end, 0, 0, MKD_CELL_HEAD); /* parse alignments if provided */ if( col && (aligns=fossil_malloc(align_size * sizeof *aligns))!=0 ){ for(i=0; i<align_size; i++) aligns[i] = 0; col = 0; i = head_end+1; /* skip initial white space and optional separator */ while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; } if( data[i]=='|' ) i++; /* compute default alignment for each column */ while (i < size && data[i] != '\n') { if (data[i] == ':') aligns[col] |= MKD_CELL_ALIGN_LEFT; while (i < size && data[i] != '|' && data[i] != '\n') i += 1; if (data[i - 1] == ':') aligns[col] |= MKD_CELL_ALIGN_RIGHT; if (i < size && data[i] == '|') i += 1; col += 1; } } /* point i to the beginning of next line/row */ i++; }else{ /* there is no valid ruler, continuing without header */ i = 0; } /* render the table body lines */ while( i<size && is_tableline(data + i, size - i) ){ i += parse_table_row(rows, rndr, data+i, size-i, aligns, align_size, 0); } /* render the full table */ rndr->make.table(ob, head, rows, rndr->make.opaque); /* cleanup */ release_work_buffer(rndr, head); release_work_buffer(rndr, rows); fossil_free(aligns); return i; } /* parse_block -- parsing of one block, returning next char to parse */ static void parse_block( struct Blob *ob, /* output blob */ struct render *rndr, /* renderer internal state */ char *data, /* input text */ size_t size /* input text size */ ){ size_t beg, end, i; char *txt_data; int has_table; if( !size ) return; has_table = (rndr->make.table && rndr->make.table_row && rndr->make.table_cell && memchr(data, '|', size)!=0); beg = 0; while( beg<size ){ txt_data = data+beg; end = size-beg; if( data[beg]=='#' ){ beg += parse_atxheader(ob, rndr, txt_data, end); }else if( data[beg]=='<' && rndr->make.blockhtml && (i = parse_htmlblock(ob, rndr, txt_data, end))!=0 ){ beg += i; }else if( (i=is_empty(txt_data, end))!=0 ){ beg += i; }else if( is_hrule(txt_data, end) ){ if( rndr->make.hrule ) rndr->make.hrule(ob, rndr->make.opaque); while( beg<size && data[beg]!='\n' ){ beg++; } beg++; }else if( prefix_quote(txt_data, end) ){ beg += parse_blockquote(ob, rndr, txt_data, end); }else if( prefix_code(txt_data, end) ){ beg += parse_blockcode(ob, rndr, txt_data, end); }else if( prefix_uli(txt_data, end) ){ beg += parse_list(ob, rndr, txt_data, end, 0); }else if( prefix_oli(txt_data, end) ){ beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED); }else if( has_table && is_tableline(txt_data, end) ){ beg += parse_table(ob, rndr, txt_data, end); }else if( prefix_fencedcode(txt_data, end) && (i = char_codespan(ob, rndr, txt_data, 0, end))!=0 ){ beg += i; }else{ beg += parse_paragraph(ob, rndr, txt_data, end); } } } /********************* * REFERENCE PARSING * *********************/ /* is_ref -- returns whether a line is a reference or not */ static int is_ref( const char *data, /* input text */ size_t beg, /* offset of the beginning of the line */ size_t end, /* offset of the end of the text */ size_t *last, /* last character of the link */ struct Blob *refs /* array of link references */ ){ size_t i = 0; size_t id_offset, id_end; size_t link_offset, link_end; size_t title_offset, title_end; size_t line_end; struct link_ref lr = { BLOB_INITIALIZER, BLOB_INITIALIZER, BLOB_INITIALIZER }; /* up to 3 optional leading spaces */ if( beg+3>=end ) return 0; if( data[beg]==' ' ){ i = 1; if( data[beg+1]==' ' ){ i = 2; if( data[beg+2]==' ' ){ i = 3; if( data[beg+3]==' ' ) return 0; } } } i += beg; /* id part: anything but a newline between brackets */ if( data[i]!='[' ) return 0; i++; if( i>=end || data[i]=='^' ) return 0; /* see is_footnote() */ id_offset = i; while( i<end && data[i]!='\n' && data[i]!='\r' && data[i]!=']' ){ i++; } if( i>=end || data[i]!=']' ) return 0; id_end = i; /* spacer: colon (space | tab)* newline? (space | tab)* */ i++; if( i>=end || data[i]!=':' ) return 0; i++; while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; } if( i<end && (data[i]=='\n' || data[i]=='\r') ){ i++; if( i<end && data[i]=='\r' && data[i-1] == '\n' ) i++; } while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; } if( i>=end ) return 0; /* link: whitespace-free sequence, optionally between angle brackets */ if( data[i]=='<' ) i++; link_offset = i; while( i<end && data[i]!=' ' && data[i]!='\t' && data[i]!='\n' && data[i]!='\r' ){ i += 1; } /* TODO: maybe require both data[i-1]=='>' && data[link_offset-1]=='<' ? */ if( data[i-1]=='>' ) link_end = i-1; else link_end = i; /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */ while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; } if( i<end && data[i]!='\n' && data[i]!='\r' && data[i]!='\'' && data[i]!='"' && data[i]!='(' ){ return 0; } line_end = 0; /* computing end-of-line */ if( i>=end || data[i]=='\r' || data[i]=='\n' ) line_end = i; if( i+1<end && data[i]=='\n' && data[i+1]=='\r' ) line_end = i+1; /* optional (space|tab)* spacer after a newline */ if( line_end ){ i = line_end+1; while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; } } /* optional title: any non-newline sequence enclosed in '"() alone on its line */ title_offset = title_end = 0; if( i+1<end && (data[i]=='\'' || data[i]=='"' || data[i]=='(') ){ i += 1; title_offset = i; /* looking for EOL */ while( i<end && data[i]!='\n' && data[i]!='\r' ){ i++; } if( i+1<end && data[i]=='\n' && data[i+1]=='\r' ){ title_end = i + 1; }else{ title_end = i; } /* stepping back */ i--; while( i>title_offset && (data[i]==' ' || data[i]=='\t') ){ i--; } if( i>title_offset && (data[i]=='\'' || data[i]=='"' || data[i]==')') ){ line_end = title_end; title_end = i; } } if( !line_end ) return 0; /* garbage after the link */ /* a valid ref has been found, filling-in return structures */ if( last ) *last = line_end; if( !refs ) return 1; if( build_ref_id(&lr.id, data+id_offset, id_end-id_offset)<0 ) return 0; blob_append(&lr.link, data+link_offset, link_end-link_offset); if( title_end>title_offset ){ blob_append(&lr.title, data+title_offset, title_end-title_offset); } blob_append(refs, (char *)&lr, sizeof lr); return 1; } /********************* * FOOTNOTE PARSING * *********************/ /* is_footnote -- check if data holds a definition of a labeled footnote. * If so then append the corresponding element to `footnotes` array */ static int is_footnote( const char *data, /* input text */ size_t beg, /* offset of the beginning of the line */ size_t end, /* offset of the end of the text */ size_t *last, /* last character of the link */ struct Blob * footnotes ){ size_t i, id_offset, id_end, upc_offset, upc_size; struct footnote fn = FOOTNOTE_INITIALIZER; /* failfast if data is too short */ if( beg+5>=end ) return 0; i = beg; /* footnote definition must start at the begining of a line */ if( data[i]!='[' ) return 0; i++; if( data[i]!='^' ) return 0; id_offset = ++i; /* id part: anything but a newline between brackets */ while( i<end && data[i]!=']' && data[i]!='\n' && data[i]!='\r' ){ i++; } if( i>=end || data[i]!=']' ) return 0; id_end = i++; /* spacer: colon (space | tab)* */ if( i>=end || data[i]!=':' ) return 0; i++; while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; } /* passthrough truncated footnote definition */ if( i>=end ) return 0; if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0; /* footnote's text may start on the same line after [^id]: */ upc_offset = upc_size = 0; if( data[i]!='\n' && data[i]!='\r' ){ size_t j; upc_size = is_footnote_classlist(data+i, end-i, 1); upc_offset = i; /* prevent further checks for a classlist */ i += upc_size; j = i; while( i<end && data[i]!='\n' && data[i]!='\r' ){ i++; }; if( i!=j )blob_append(&fn.text, data+j, i-j); if( i<end ){ blob_append_char(&fn.text, data[i]); i++; if( i<end && data[i]=='\n' && data[i-1]=='\r' ){ blob_append_char(&fn.text, data[i]); i++; } } }else{ i++; if( i<end && data[i]=='\n' && data[i-1]=='\r' ) i++; } if( i<end ){ /* compute the indentation from the 2nd line */ size_t indent = i; const char *spaces = data+i; while( indent<end && data[indent]==' ' ){ indent++; } if( indent>=end ) goto footnote_finish; indent -= i; if( indent<2 ) goto footnote_finish; /* process the 2nd and subsequent lines */ while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){ size_t j; i += indent; if( !upc_offset ){ /* a classlist must be provided no later than at the 2nd line */ upc_offset = i + sizeof_blank_prefix(data+i, end-i, 1); upc_size = is_footnote_classlist(data+upc_offset, end-upc_offset, 1); if( upc_size ){ i = upc_offset + upc_size; } } j = i; while( i<end && data[i]!='\n' && data[i]!='\r' ){ i++; } if( i!=j ) blob_append(&fn.text, data+j, i-j); if( i>=end ) break; blob_append_char(&fn.text, data[i]); i++; if( i<end && data[i]=='\n' && data[i-1]=='\r' ){ blob_append_char(&fn.text, data[i]); i++; } } } footnote_finish: if( !blob_size(&fn.text) ){ blob_reset(&fn.id); return 0; } if( !blob_trim(&fn.text) ){ /* if the content is all-blank */ if( upc_size ){ /* interpret UPC as plain text */ blob_append(&fn.text, data+upc_offset, upc_size); upc_size = 0; }else{ blob_reset(&fn.id); /* or clean up and fail */ blob_reset(&fn.text); return 0; } } /* a valid note has been found */ if( last ) *last = i; if( footnotes ){ fn.defno = COUNT_FOOTNOTES( footnotes ); if( upc_size ){ assert( upc_offset && upc_offset+upc_size<end ); blob_append(&fn.upc, data+upc_offset, upc_size); } blob_append(footnotes, (char *)&fn, sizeof fn); } return 1; } /********************** * EXPORTED FUNCTIONS * **********************/ /* markdown -- parses the input buffer and renders it into the output buffer */ void markdown( struct Blob *ob, /* output blob for rendered text */ const struct Blob *ib, /* input blob in markdown */ const struct mkd_renderer *rndrer /* renderer descriptor (callbacks) */ ){ struct link_ref *lr; struct footnote *fn; int i; size_t beg, end = 0; struct render rndr; size_t size; Blob text = BLOB_INITIALIZER; /* input after the first pass */ Blob * const allNotes = &rndr.notes.all; /* filling the render structure */ if( !rndrer ) return; rndr.make = *rndrer; rndr.nBlobCache = 0; rndr.iDepth = 0; rndr.refs = empty_blob; rndr.notes.all = empty_blob; rndr.notes.nMarks = 0; rndr.notes.misref.id = empty_blob; rndr.notes.misref.text = empty_blob; rndr.notes.misref.upc = empty_blob; rndr.notes.misref.bRndred = 0; rndr.notes.misref.nUsed = 0; rndr.notes.misref.iMark = -1; for(i=0; i<256; i++) rndr.active_char[i] = 0; if( (rndr.make.emphasis || rndr.make.double_emphasis || rndr.make.triple_emphasis) && rndr.make.emph_chars ){ for(i=0; rndr.make.emph_chars[i]; i++){ rndr.active_char[(unsigned char)rndr.make.emph_chars[i]] = char_emphasis; } } if( rndr.make.codespan ) rndr.active_char['`'] = char_codespan; if( rndr.make.linebreak ) rndr.active_char['\n'] = char_linebreak; if( rndr.make.image || rndr.make.link ) rndr.active_char['['] = char_link; if( rndr.make.footnote_ref ) rndr.active_char['('] = char_footnote; rndr.active_char['<'] = char_langle_tag; rndr.active_char['\\'] = char_escape; rndr.active_char['&'] = char_entity; /* first pass: iterate over lines looking for references, * copying everything else into "text" */ beg = 0; for(size = blob_size(ib); beg<size ;){ const char* const data = blob_buffer(ib); if( is_ref(data, beg, size, &end, &rndr.refs) ){ beg = end; }else if(is_footnote(data, beg, size, &end, &rndr.notes.all)){ beg = end; }else{ /* skipping to the next line */ end = beg; while( end<size && data[end]!='\n' && data[end]!='\r' ){ end += 1; } /* adding the line body if present */ if( end>beg ) blob_append(&text, data + beg, end - beg); while( end<size && (data[end]=='\n' || data[end]=='\r') ){ /* add one \n per newline */ if( data[end]=='\n' || (end+1<size && data[end+1]!='\n') ){ blob_append_char(&text, '\n'); } end += 1; } beg = end; } } /* sorting the reference array */ if( blob_size(&rndr.refs) ){ qsort(blob_buffer(&rndr.refs), blob_size(&rndr.refs)/sizeof(struct link_ref), sizeof(struct link_ref), cmp_link_ref_sort); } rndr.notes.nLbled = COUNT_FOOTNOTES( allNotes ); /* sort footnotes by ID and join duplicates */ if( rndr.notes.nLbled > 1 ){ int nDups = 0; fn = CAST_AS_FOOTNOTES( allNotes ); qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id); /* concatenate footnotes with equal labels */ for(i=0; i<rndr.notes.nLbled ;){ struct footnote *x = fn + i; int j = i+1; size_t k = blob_size(&x->text) + 64 + blob_size(&x->upc); while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){ k += blob_size(&fn[j].text) + 10 + blob_size(&fn[j].upc); j++; nDups++; } if( i+1<j ){ Blob list = empty_blob; blob_reserve(&list, k); /* must match _joined_footnote_indicator in html_footnote_item() */ blob_append_literal(&list, "<ul class='fn-joined'>\n"); for(k=i; (int)k<j; k++){ struct footnote *y = fn + k; blob_append_literal(&list, "<li>"); if( blob_size(&y->upc) ){ blob_appendb(&list, &y->upc); blob_reset(&y->upc); } blob_appendb(&list, &y->text); blob_append_literal(&list, "</li>\n"); /* free memory buffer */ blob_reset(&y->text); if( (int)k!=i ) blob_reset(&y->id); } blob_append_literal(&list, "</ul>\n"); x->text = list; g.ftntsIssues[2]++; } i = j; } if( nDups ){ /* clean rndr.notes.all from invalidated footnotes */ const int n = rndr.notes.nLbled - nDups; struct Blob filtered = empty_blob; blob_reserve(&filtered, n*sizeof(struct footnote)); for(i=0; i<rndr.notes.nLbled; i++){ if( blob_size(&fn[i].id) ){ blob_append(&filtered, (char*)(fn+i), sizeof(struct footnote)); } } blob_reset( allNotes ); rndr.notes.all = filtered; rndr.notes.nLbled = n; assert( (int)(COUNT_FOOTNOTES(allNotes)) == rndr.notes.nLbled ); } } fn = CAST_AS_FOOTNOTES( allNotes ); for(i=0; i<rndr.notes.nLbled; i++){ fn[i].index = i; } assert( rndr.notes.nMarks==0 ); /* 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( blob_size(allNotes) || rndr.notes.misref.nUsed ){ /* Footnotes must be parsed for the correct discovery of (back)links */ Blob *notes = new_work_buffer( &rndr ); if( blob_size(allNotes) ){ Blob *tmp = new_work_buffer( &rndr ); int nMarks = -1, maxDepth = 5; /* inline notes may get appended to rndr.notes.all while rendering */ while(1){ struct footnote *aNotes; const int N = COUNT_FOOTNOTES( allNotes ); /* make a shallow copy of `allNotes` */ blob_truncate(notes,0); blob_appendb(notes, allNotes); aNotes = CAST_AS_FOOTNOTES(notes); qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort); if( --maxDepth < 0 || nMarks == rndr.notes.nMarks ) break; nMarks = rndr.notes.nMarks; for(i=0; i<N; i++){ const int j = aNotes[i].index; struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j; assert( 0<=j && j<N ); if( x->bRndred || !x->nUsed ) continue; assert( x->iMark > 0 ); assert( blob_size(&x->text) ); blob_truncate(tmp,0); /* `allNotes` may be altered and extended through this call */ parse_inline(tmp, &rndr, blob_buffer(&x->text), blob_size(&x->text)); x = CAST_AS_FOOTNOTES(allNotes) + j; blob_truncate(&x->text,0); blob_appendb(&x->text, tmp); x->bRndred = 1; } } release_work_buffer(&rndr,tmp); } /* footnotes rendering */ if( rndr.make.footnote_item && rndr.make.footnotes ){ Blob *all_items = new_work_buffer(&rndr); int j = -1; /* Assert that the in-memory layout of id, text and upc within ** footnote struct matches the expectations of html_footnote_item() ** If it doesn't then a compiler has done something very weird. */ assert( &(rndr.notes.misref.id) == &(rndr.notes.misref.text) - 1 ); assert( &(rndr.notes.misref.upc) == &(rndr.notes.misref.text) + 1 ); for(i=0; i<(int)(COUNT_FOOTNOTES(notes)); i++){ const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i; const int xUsed = x->bRndred ? x->nUsed : 0; if( !x->iMark ) break; assert( x->nUsed ); rndr.make.footnote_item(all_items, &x->text, x->iMark, xUsed, rndr.make.opaque); if( !xUsed ) g.ftntsIssues[3]++; /* an overnested footnote */ j = i; } if( rndr.notes.misref.nUsed ){ rndr.make.footnote_item(all_items, 0, -1, rndr.notes.misref.nUsed, rndr.make.opaque); g.ftntsIssues[0] += rndr.notes.misref.nUsed; } while( ++j < (int)(COUNT_FOOTNOTES(notes)) ){ const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j; assert( !x->iMark ); assert( !x->nUsed ); assert( !x->bRndred ); rndr.make.footnote_item(all_items,&x->text,0,0,rndr.make.opaque); g.ftntsIssues[1]++; } rndr.make.footnotes(ob, all_items, rndr.make.opaque); release_work_buffer(&rndr, all_items); } release_work_buffer(&rndr, notes); } if( rndr.make.epilog ) rndr.make.epilog(ob, rndr.make.opaque); /* clean-up */ assert( rndr.iDepth==0 ); blob_reset(&text); lr = (struct link_ref *)blob_buffer(&rndr.refs); end = blob_size(&rndr.refs)/sizeof(struct link_ref); for(i=0; i<(int)end; i++){ blob_reset(&lr[i].id); blob_reset(&lr[i].link); blob_reset(&lr[i].title); } blob_reset(&rndr.refs); fn = CAST_AS_FOOTNOTES( allNotes ); end = COUNT_FOOTNOTES( allNotes ); for(i=0; i<(int)end; i++){ if(blob_size(&fn[i].id)) blob_reset(&fn[i].id); if(blob_size(&fn[i].upc)) blob_reset(&fn[i].upc); blob_reset(&fn[i].text); } blob_reset(&rndr.notes.all); for(i=0; i<rndr.nBlobCache; i++){ fossil_free(rndr.aBlobCache[i]); } } |
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 | # Markdown Overview # ## Paragraphs ## > Paragraphs are divided by blank lines. > End a line with two or more spaces to force a mid-paragraph line break. ## Headings ## > # Top Level Heading Alternative Top Level Heading # Top Level Heading Variant # ============================= > ## 2nd Level Heading Alternative 2nd Level Heading ## 2nd Level Heading Variant ## ----------------------------- > ### 3rd Level Heading ### 3rd Level Heading Variant ### #### 4th Level Heading #### 4th Level Heading Variant #### ##### 5th Level Heading ##### 5th Level Heading Variant ##### ###### 6th Level Heading ###### 6th Level Heading Variant ###### ## Links ## > 1. **\[display text\]\(URL\)** > 2. **\[display text\]\(URL "Title"\)** > 3. **\[display text\]\(URL 'Title'\)** > 4. **\<URL\>** > 5. **\[display text\]\[label\]** > 6. **\[display text\]\[\]** > 7. **\[display text\]** > 8. **\[\]\(URL\)** > With link formats 5, 6, and 7 ("reference links"), the URL is supplied > elsewhere in the document, as shown below. Link formats 6 and 7 reuse > the display text as the label. Labels are case-insensitive. The title > may be split onto the next line with optional indenting. > * **\[label\]: URL** > * **\[label\]: URL "Title"** > * **\[label\]: URL 'Title'** > * **\[label\]: URL (Title)** > If **URL** begins with "http:", "https:', "ftp:' or "mailto:", > it may optionally be written **\<URL\>** (format 4). > Other **URL** formats include: > <ul> > <li> A relative pathname. > <li> A pathname starting with "/" in which case the Fossil server > URL prefix is prepended > <li> A wiki page name, or a wiki page name preceded by "wiki:" > <li> An artifact or ticket hash or hash prefix > <li> A date and time stamp: "YYYY-MM-DD HH:MM:SS" or a subset that > includes at least the day of the month. > <li> An [interwiki link](#intermap) of the form "<i>Tag</i><b>:</b><i>PageName</i>"</ul> > In format 8, then the URL becomes the display text. This is useful for > hyperlinks that refer to wiki pages and check-in and ticket hashes. ## Text Style ## > * _\*italic\*_ > * *\_italic\_* > * __\*\*bold\*\*__ > * **\_\_bold\_\_** > * ___\*\*\*italic+bold\*\*\*___ > * ***\_\_\_italic+bold\_\_\_*** > * \``code`\` > The **\`code\`** construct disables HTML markup, so one can write, for > example, **\`\<html\>\`** to yield **`<html>`**. ## Lists ## > ~~~ * bullet item + bullet item - bullet item 1. numbered item 2) numbered item ~~~ > A two-level list is created by placing additional whitespace before the > **\***/**+**/**-**/**1.** of the secondary items. > ~~~ * top-level item * second-level item ~~~ ## Block Quotes ## > Begin each line of a paragraph with **>** to block quote that paragraph. > > > This paragraph is indented > > > > Double-indented paragraph ## Literal/Verbatim Text - Code Blocks ## > For inline text, you can either use \``backticks`\` or the HTML > `<code>` tag. > > For blocks of text or code: > > 1. Indent the text using a tab character or at least four spaces. > 2. Precede the block with an HTML `<pre>` tag and follow it with `</pre>`. > 3. Surround the block by <tt>\`\`\`</tt> (three or more) or <tt>\~\~\~</tt> either at the > left margin or indented no more than three spaces. The first word > on that same line (if any) is used in a “`language-WORD`†CSS style in > the HTML rendering of that code block and is intended for use by > code syntax highlighters. Thus <tt>\`\`\`c</tt> would mark a block of code > in the C programming language. Text to be rendered inside the code block > should therefore start on the next line, not be cuddled up with the > backticks or tildes. See the "Diagrams" section below for the case where > "`language-WORD`" is "pikchr". > With the standard skins, verbatim text is rendered in a fixed-width font, > but that is purely a presentation matter, controlled by the skin’s CSS. ## Tables ## > | Header 1 | Header 2 | Header 3 | ---------------------------------------------- | Row 1 Col 1 | Row 1 Col 2 | Row 1 Col 3 | |:Left-aligned |:Centered :| Right-aligned:| | | ↠Blank → | | | Row 4 Col 1 | Row 4 Col 2 | Row 4 Col 3 | > The first row is a header if followed by a horizontal rule or a blank line. > Placing **:** at the left, both, or right sides of a cell gives left-aligned, > centered, or right-aligned text, respectively. By default, header cells are > centered, and body cells are left-aligned. > The leftmost or rightmost **\|** is required only if the first or last column, > respectively, contains at least one blank cell. ## Diagrams ## > ~~~~~ ~~~ pikchr oval "Start" fit; arrow; box "Hello, World!" fit; arrow; oval "Done" fit ~~~ ~~~~~ > Formatted using [Pikchr](https://pikchr.org/home), resulting in: > ~~~ pikchr oval "Start" fit; arrow; box "Hello, World!" fit; arrow; oval "Done" fit ~~~ <a id="ftnts"></a> ## Footnotes ## > Footnotes (or "endnotes") is a Fossil extension of classical Markdown. > Fossil's syntax for footnotes is similar to links and > is distinguished by the use of character **^** > that *immediately* follows an opening bracket. > 1. **\(^** footnote's text **)** > 2. **\[** fragment of text **]\(^** a comment about that fragment **\)** > 3. **\[^** label **\]** > 4. **\[** fragment of text **\]\[^** label **\]** > 5. **\[** fragment of text **\]\[^\]** > With formats 1 and 2 ("inline footnotes") text of a footnote is provided > in the place where the corresponding numeric mark will be rendered. > With formats 3, 4, and 5 ("reference footnotes") text of a footnote > is supplied elsewhere in the document, as shown below. > Formats 2, 4 and 5 ("span-specific footnotes") mark a specific fragment > that is being commented in the footnote. > Format 5 reuses a fragment of text as a label. > Labels are case-insensitive. > ``` > [^label]: Footnote definition must start on the first column. > The second line (if any) must be indented by two or more spaces. > Definition continues until indentation drops below that of the 2nd line. >``` > Character **^** is not part of a label, it is part of the syntax. > Both a footnote's text and a fragment to which a footnote applies > are subject to further interpretation as Markdown sources. ## Miscellaneous ## > * In-line images are made using **\!\[alt-text\]\(image-URL\)**. > * Use HTML for advanced formatting such as forms, noting that certain > tags are [disallowed in some contexts](/help?cmd=safe-html). > * **\<!--** HTML-style comments **-->** are supported. > * Escape special characters (ex: **\[** **\(** **\|** **\***) > using backslash (ex: **\\\[** **\\\(** **\\\|** **\\\***). > * A line consisting of **---**, **\*\*\***, or **\_\_\_** is a horizontal > rule. Spaces and extra **-**/**\***/**_** are allowed. > * Paragraphs enclosed in **\<html\>...\</html\>** is passed through unchanged. > * See [daringfireball.net][] for additional information. > * See this page's [Markdown source](/md_rules?txt=1) for more examples. ## Special Features For Fossil ## > * In hyperlinks, if the URL begins with **/** then the root of the Fossil > repository is prepended. This allows for repository-relative hyperlinks. > * For documents that begin with a top-level heading (ex: **# heading #**), > the heading is omitted from the body of the document and becomes the > document title displayed at the top of the Fossil page. [daringfireball.net]: http://daringfireball.net/projects/markdown/syntax <a name="intermap"></a> ## Interwiki Tag [Map](/intermap) |
Added src/markdown_html.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 | /* ** Copyright (c) 2012 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 callbacks for the markdown parser that generate ** XHTML output. */ #include "config.h" #include "markdown_html.h" #if INTERFACE void markdown_to_html( struct Blob *input_markdown, struct Blob *output_title, struct Blob *output_body); #endif /* INTERFACE */ /* ** Markdown-internal helper for generating unique link reference IDs. ** Fields provide typed interpretation of the underline memory buffer. */ typedef union bitfield64_t bitfield64_t; union bitfield64_t{ char c[8]; /* interpret as the array of signed characters */ unsigned char b[8]; /* interpret as the array of unsigned characters */ }; /* ** An instance of the following structure is passed through the ** "opaque" pointer. */ typedef struct MarkdownToHtml MarkdownToHtml; struct MarkdownToHtml { Blob *output_title; /* Store the title here */ bitfield64_t unique; /* Enables construction of unique #id elements */ #ifndef FOOTNOTES_WITHOUT_URI Blob reqURI; /* REQUEST_URI with escaped quotes */ #endif }; /* INTER_BLOCK -- skip a line between block level elements */ #define INTER_BLOCK(ob) \ do { if( blob_size(ob)>0 ) blob_append_char(ob, '\n'); } while (0) /* ** FOOTNOTES_WITHOUT_URI macro was introduced by [2c1f8f3592ef00e0] ** to enable flexibility in rendering of footnote-specific hyperlinks. ** It may be defined for a particular build in order to omit ** full REQUEST_URIs within footnote-specific (and page-local) hyperlinks. ** This *is* used for the builds that incorporate 'base-href-fix' branch ** (which in turn fixes footnotes on the preview tab of /wikiedit page). */ #ifndef FOOTNOTES_WITHOUT_URI #define BLOB_APPEND_URI(dest,ctx) blob_appendb(dest,&((ctx)->reqURI)) #else #define BLOB_APPEND_URI(dest,ctx) #endif /* Converts an integer to a textual base26 representation ** with proper null-termination. * Return empty string if that integer is negative. */ static bitfield64_t to_base26(int i, int uppercase){ bitfield64_t x; int j; memset( &x, 0, sizeof(x) ); if( i >= 0 ){ for(j=7; j >= 0; j--){ x.b[j] = (unsigned char)(uppercase?'A':'a') + i%26; if( (i /= 26) == 0 ) break; } assert( j > 0 ); /* because 2^32 < 26^7 */ for(i=0; i<8-j; i++) x.b[i] = x.b[i+j]; for( ; i<8 ; i++) x.b[i] = 0; } assert( x.c[7] == 0 ); return x; } /* HTML escapes ** ** html_escape() converts < to <, > to >, and & to &. ** html_quote() goes further and converts " into " and ' in '. */ static void html_quote(struct Blob *ob, const char *data, size_t size){ size_t beg = 0, i = 0; while( i<size ){ beg = i; while( i<size && data[i]!='<' && data[i]!='>' && data[i]!='"' && data[i]!='&' && data[i]!='\'' ){ i++; } blob_append(ob, data+beg, i-beg); while( i<size ){ if( data[i]=='<' ){ blob_append_literal(ob, "<"); }else if( data[i]=='>' ){ blob_append_literal(ob, ">"); }else if( data[i]=='&' ){ blob_append_literal(ob, "&"); }else if( data[i]=='"' ){ blob_append_literal(ob, """); }else if( data[i]=='\'' ){ blob_append_literal(ob, "'"); }else{ break; } i++; } } } static void html_escape(struct Blob *ob, const char *data, size_t size){ size_t beg = 0, i = 0; while( i<size ){ beg = i; while( i<size && data[i]!='<' && data[i]!='>' && data[i]!='&' ){ i++; } blob_append(ob, data+beg, i-beg); while( i<size ){ if( data[i]=='<' ){ blob_append_literal(ob, "<"); }else if( data[i]=='>' ){ blob_append_literal(ob, ">"); }else if( data[i]=='&' ){ blob_append_literal(ob, "&"); }else{ break; } 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_literal(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_literal(ob, "</div>\n"); } static void html_blockhtml(struct Blob *ob, struct Blob *text, void *opaque){ char *data = blob_buffer(text); size_t size = blob_size(text); Blob *title = ((MarkdownToHtml*)opaque)->output_title; while( size>0 && fossil_isspace(data[0]) ){ data++; size--; } while( size>0 && fossil_isspace(data[size-1]) ){ size--; } /* If the first raw block is an <h1> element, then use it as the title. */ if( blob_size(ob)<=PROLOG_SIZE && size>9 && title!=0 && sqlite3_strnicmp("<h1",data,3)==0 && sqlite3_strnicmp("</h1>", &data[size-5],5)==0 ){ int nTag = html_tag_length(data); blob_append(title, data+nTag, size - nTag - 5); return; } INTER_BLOCK(ob); blob_append(ob, data, size); blob_append_literal(ob, "\n"); } static void html_blockcode(struct Blob *ob, struct Blob *text, void *opaque){ INTER_BLOCK(ob); blob_append_literal(ob, "<pre><code>"); html_escape(ob, blob_buffer(text), blob_size(text)); blob_append_literal(ob, "</code></pre>\n"); } static void html_blockquote(struct Blob *ob, struct Blob *text, void *opaque){ INTER_BLOCK(ob); blob_append_literal(ob, "<blockquote>\n"); blob_appendb(ob, text); blob_append_literal(ob, "</blockquote>\n"); } static void html_header( struct Blob *ob, struct Blob *text, int level, void *opaque ){ struct Blob *title = ((MarkdownToHtml*)opaque)->output_title; /* The first header at the beginning of a text is considered as * a title and not output. */ if( blob_size(ob)<=PROLOG_SIZE && title!=0 && blob_size(title)==0 ){ blob_appendb(title, text); return; } INTER_BLOCK(ob); blob_appendf(ob, "<h%d>", level); blob_appendb(ob, text); blob_appendf(ob, "</h%d>", level); } static void html_hrule(struct Blob *ob, void *opaque){ INTER_BLOCK(ob); blob_append_literal(ob, "<hr>\n"); } static void html_list( struct Blob *ob, struct Blob *text, int flags, void *opaque ){ char ol[] = "ol"; char ul[] = "ul"; char *tag = (flags & MKD_LIST_ORDERED) ? ol : ul; INTER_BLOCK(ob); blob_appendf(ob, "<%s>\n", tag); blob_appendb(ob, text); blob_appendf(ob, "</%s>\n", tag); } static void html_list_item( struct Blob *ob, struct Blob *text, int flags, void *opaque ){ char *text_data = blob_buffer(text); size_t text_size = blob_size(text); while( text_size>0 && text_data[text_size-1]=='\n' ) text_size--; blob_append_literal(ob, "<li>"); blob_append(ob, text_data, text_size); blob_append_literal(ob, "</li>\n"); } static void html_paragraph(struct Blob *ob, struct Blob *text, void *opaque){ INTER_BLOCK(ob); blob_append_literal(ob, "<p>"); blob_appendb(ob, text); blob_append_literal(ob, "</p>\n"); } static void html_table( struct Blob *ob, struct Blob *head_row, struct Blob *rows, void *opaque ){ INTER_BLOCK(ob); blob_append_literal(ob, "<table>\n"); if( head_row && blob_size(head_row)>0 ){ blob_append_literal(ob, "<thead>\n"); blob_appendb(ob, head_row); blob_append_literal(ob, "</thead>\n<tbody>\n"); } if( rows ){ blob_appendb(ob, rows); } if( head_row && blob_size(head_row)>0 ){ blob_append_literal(ob, "</tbody>\n"); } blob_append_literal(ob, "</table>\n"); } static void html_table_cell( struct Blob *ob, struct Blob *text, int flags, void *opaque ){ if( flags & MKD_CELL_HEAD ){ blob_append_literal(ob, " <th"); }else{ blob_append_literal(ob, " <td"); } switch( flags & MKD_CELL_ALIGN_MASK ){ case MKD_CELL_ALIGN_LEFT: { blob_append_literal(ob, " align=\"left\""); break; } case MKD_CELL_ALIGN_RIGHT: { blob_append_literal(ob, " align=\"right\""); break; } case MKD_CELL_ALIGN_CENTER: { blob_append_literal(ob, " align=\"center\""); break; } } blob_append_literal(ob, ">"); blob_appendb(ob, text); if( flags & MKD_CELL_HEAD ){ blob_append_literal(ob, "</th>\n"); }else{ blob_append_literal(ob, "</td>\n"); } } static void html_table_row( struct Blob *ob, struct Blob *cells, int flags, void *opaque ){ blob_append_literal(ob, " <tr>\n"); blob_appendb(ob, cells); blob_append_literal(ob, " </tr>\n"); } /* ** Render a token of user provided classes. ** If bHTML is true then render HTML for (presumably) visible text, ** otherwise just a space-separated list of the derived classes. */ static void append_footnote_upc( struct Blob *ob, const struct Blob *upc, /* token of user-provided classes */ int bHTML ){ const char *z = blob_buffer(upc); int i, n = blob_size(upc); if( n<3 ) return; assert( z[0]=='.' && z[n-1] == ':' ); if( bHTML ){ blob_append_literal(ob, "<span class='fn-upc'>" "<span class='fn-upcDot'>.</span>"); } n = 0; do{ z++; if( *z!='.' && *z!=':' ){ assert( fossil_isalnum(*z) || *z=='-' ); n++; continue; } assert( n ); if( bHTML ) blob_append_literal(ob, "<span class='"); blob_append_literal(ob, "fn-upc-"); for(i=-n; i<0; i++){ blob_append_char(ob, fossil_tolower(z[i]) ); } if( bHTML ){ blob_append_literal(ob, "'>"); blob_append(ob, z-n, n); blob_append_literal(ob, "</span>"); }else{ blob_append_char(ob, ' '); } n = 0; if( bHTML ){ if( *z==':' ){ blob_append_literal(ob,"<span class='fn-upcColon'>:</span>"); }else{ blob_append_literal(ob,"<span class='fn-upcDot'>.</span>"); } } }while( *z != ':' ); if( bHTML ) blob_append_literal(ob,"</span>\n"); } static int html_footnote_ref( struct Blob *ob, const struct Blob *span, const struct Blob *upc, int iMark, int locus, void *opaque ){ const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque; const bitfield64_t l = to_base26(locus-1,0); char pos[32]; memset(pos,0,32); assert( locus > 0 ); /* expect BUGs if the following yields compiler warnings */ if( iMark > 0 ){ /* a regular reference to a footnote */ sqlite3_snprintf(sizeof(pos), pos, "%s-%d-%s", ctx->unique.c, iMark, l.c); if(span && blob_size(span)) { blob_append_literal(ob,"<span class='"); append_footnote_upc(ob, upc, 0); blob_append_literal(ob,"notescope' id='noteref"); blob_appendf(ob,"%s'>",pos); blob_appendb(ob, span); blob_trim(ob); blob_append_literal(ob,"<sup class='noteref'><a href='"); BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#footnote%s'>%d</a></sup></span>", pos, iMark); }else{ blob_trim(ob); blob_append_literal(ob,"<sup class='"); append_footnote_upc(ob, upc, 0); blob_append_literal(ob,"noteref'><a href='"); BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#footnote%s' id='noteref%s'>%d</a></sup>", pos, pos, iMark); } }else{ /* misreference */ assert( iMark == -1 ); sqlite3_snprintf(sizeof(pos), pos, "%s-%s", ctx->unique.c, l.c); if(span && blob_size(span)) { blob_appendf(ob, "<span class='notescope' id='misref%s'>", pos); blob_appendb(ob, span); blob_trim(ob); blob_append_literal(ob, "<sup class='noteref misref'><a href='"); BLOB_APPEND_URI(ob, ctx); blob_appendf(ob, "#misreference%s'>misref</a></sup></span>", pos); }else{ blob_trim(ob); blob_append_literal(ob, "<sup class='noteref misref'><a href='"); BLOB_APPEND_URI(ob, ctx); blob_appendf(ob, "#misreference%s' id='misref%s'>", pos, pos); blob_append_literal(ob, "misref</a></sup>"); } } return 1; } /* Render a single item of the footnotes list. * Each backref gets a unique id to enable dynamic styling. */ static void html_footnote_item( struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque ){ const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque; const char * const unique = ctx->unique.c; assert( nUsed >= 0 ); /* expect BUGs if the following yields compiler warnings */ if( iMark < 0 ){ /* misreferences */ assert( iMark == -1 ); assert( nUsed ); blob_append_literal(ob,"<li class='fn-misreference'>" "<sup class='fn-backrefs'>"); if( nUsed == 1 ){ blob_appendf(ob,"<a id='misreference%s-a' href='", unique); BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#misref%s-a'>^</a>", unique); }else{ int i; blob_append_char(ob, '^'); for(i=0; i<nUsed && i<26; i++){ const int c = i + (unsigned)'a'; blob_appendf(ob," <a id='misreference%s-%c' href='", unique,c); BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#misref%s-%c'>%c</a>", unique,c, c); } if( i < nUsed ) blob_append_literal(ob," …"); } blob_append_literal(ob,"</sup>\n<span>Misreference</span>"); }else if( iMark > 0 ){ /* regular, joined and overnested footnotes */ char pos[24]; int bJoin = 0; #define _joined_footnote_indicator "<ul class='fn-joined'>" #define _jfi_sz (sizeof(_joined_footnote_indicator)-1) /* make.footnote_item() invocations should pass args accordingly */ const struct Blob *upc = text+1; assert( text ); /* allow blob_size(text)==0 for constructs like [...](^ [] ()) */ memset(pos,0,24); sqlite3_snprintf(sizeof(pos), pos, "%s-%d", unique, iMark); blob_appendf(ob, "<li id='footnote%s' class='", pos); if( nUsed ){ if( blob_size(text)>=_jfi_sz && !memcmp(blob_buffer(text),_joined_footnote_indicator,_jfi_sz)){ bJoin = 1; blob_append_literal(ob, "fn-joined "); } append_footnote_upc(ob, upc, 0); }else{ blob_append_literal(ob, "fn-toodeep "); } if( nUsed <= 1 ){ blob_append_literal(ob, "fn-monoref'><sup class='fn-backrefs'>"); blob_appendf(ob,"<a id='footnote%s-a' href='", pos); BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#noteref%s-a'>^</a>", pos); }else{ int i; blob_append_literal(ob, "fn-polyref'><sup class='fn-backrefs'>^"); for(i=0; i<nUsed && i<26; i++){ const int c = i + (unsigned)'a'; blob_appendf(ob," <a id='footnote%s-%c' href='", pos,c); BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#noteref%s-%c'>%c</a>", pos,c, c); } /* It's unlikely that so many backrefs will be usefull */ /* but maybe for some machine generated documents... */ for(; i<nUsed && i<676; i++){ const bitfield64_t l = to_base26(i,0); blob_appendf(ob," <a id='footnote%s-%s' href='", pos, l.c); BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#noteref%s-%s'>%s</a>", pos,l.c, l.c); } if( i < nUsed ) blob_append_literal(ob," …"); } blob_append_literal(ob,"</sup>\n"); if( bJoin ){ blob_append_literal(ob,"<sup class='fn-joined'></sup><ul>"); blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz); }else if( nUsed ){ append_footnote_upc(ob, upc, 1); blob_appendb(ob, text); }else{ blob_append_literal(ob,"<i></i>\n" "<pre><code class='language-markdown'>"); if( blob_size(upc) ){ blob_appendb(ob, upc); } html_escape(ob, blob_buffer(text), blob_size(text)); blob_append_literal(ob,"</code></pre>"); } #undef _joined_footnote_indicator #undef _jfi_sz }else{ /* a footnote was defined but wasn't referenced */ /* make.footnote_item() invocations should pass args accordingly */ const struct Blob *id = text-1, *upc = text+1; assert( !nUsed ); assert( text ); assert( blob_size(text) ); assert( blob_size(id) ); blob_append_literal(ob,"<li class='fn-unreferenced'>\n[^ <code>"); html_escape(ob, blob_buffer(id), blob_size(id)); blob_append_literal(ob, "</code> ]<i></i>\n" "<pre><code class='language-markdown'>"); if( blob_size(upc) ){ blob_appendb(ob, upc); } html_escape(ob, blob_buffer(text), blob_size(text)); blob_append_literal(ob,"</code></pre>"); } blob_append_literal(ob, "\n</li>\n"); } static void html_footnotes( struct Blob *ob, const struct Blob *items, void *opaque ){ if( items && blob_size(items) ){ blob_append_literal(ob, "\n<hr class='footnotes-separator'/>\n<ol class='footnotes'>\n"); blob_appendb(ob, items); blob_append_literal(ob, "</ol>\n"); } } /* HTML span tags */ static int html_raw_html_tag(struct Blob *ob, struct Blob *text, void *opaque){ blob_append(ob, blob_buffer(text), blob_size(text)); return 1; } static int html_autolink( struct Blob *ob, struct Blob *link, enum mkd_autolink type, void *opaque ){ if( !link || blob_size(link)<=0 ) return 0; blob_append_literal(ob, "<a href=\""); if( type==MKDA_IMPLICIT_EMAIL ) blob_append_literal(ob, "mailto:"); html_quote(ob, blob_buffer(link), blob_size(link)); blob_append_literal(ob, "\">"); if( type==MKDA_EXPLICIT_EMAIL && blob_size(link)>7 ){ /* remove "mailto:" from displayed text */ html_escape(ob, blob_buffer(link)+7, blob_size(link)-7); }else{ html_escape(ob, blob_buffer(link), blob_size(link)); } blob_append_literal(ob, "</a>"); return 1; } /* ** Flags for use with/via pikchr_to_html_add_flags(). */ static int pikchrToHtmlFlags = 0; /* ** Sets additional pikchr_process() flags to use for all future calls ** to pikch_to_html(). This is intended to be used by commands such as ** test-wiki-render and test-markdown-render to set the ** PIKCHR_PROCESS_DARK_MODE flag for all embedded pikchr elements. ** ** Not all PIKCHR_PROCESS flags are legal, as pikchr_to_html() ** hard-codes a subset of flags and passing arbitrary flags here may ** interfere with that. ** ** The only tested/intended use of this function is to pass it either ** 0 or PIKCHR_PROCESS_DARK_MODE. ** ** Design note: this is not implemented as an additional argument to ** pikchr_to_html() because the commands for which dark-mode rendering ** are now supported (test-wiki-render and test-markdown-render) are ** far removed from their corresponding pikchr_to_html() calls and ** there is no direct path from those commands to those calls. A ** cleaner, but much more invasive, approach would be to add a flag to ** markdown_to_html(), extend the WIKI_... flags with ** WIKI_DARK_PIKCHR, and extend both wiki.c:Renderer and ** markdown_html.c:MarkdownToHtml to contain and pass on that flag. */ void pikchr_to_html_add_flags( int f ){ pikchrToHtmlFlags = f; } /* ** The nSrc bytes at zSrc[] are Pikchr input text (allegedly). Process that ** text and insert the result in place of the original. */ void pikchr_to_html( Blob *ob, /* Write the generated SVG here */ const char *zSrc, int nSrc, /* The Pikchr source text */ const char *zArg, int nArg /* Addition arguments */ ){ int pikFlags = PIKCHR_PROCESS_NONCE | PIKCHR_PROCESS_DIV | PIKCHR_PROCESS_SRC | PIKCHR_PROCESS_ERR_PRE | pikchrToHtmlFlags; Blob bSrc = empty_blob; const char *zPikVar; double rPikVar; while( nArg>0 ){ int i; for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){} if( i==6 && strncmp(zArg, "center", 6)==0 ){ pikFlags |= PIKCHR_PROCESS_DIV_CENTER; }else if( i==6 && strncmp(zArg, "indent", 6)==0 ){ pikFlags |= PIKCHR_PROCESS_DIV_INDENT; }else if( i==10 && strncmp(zArg, "float-left", 10)==0 ){ pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT; }else if( i==11 && strncmp(zArg, "float-right", 11)==0 ){ pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT; }else if( i==6 && strncmp(zArg, "toggle", 6)==0 ){ pikFlags |= PIKCHR_PROCESS_DIV_TOGGLE; }else if( i==6 && strncmp(zArg, "source", 6)==0 ){ pikFlags |= PIKCHR_PROCESS_DIV_SOURCE; }else if( i==13 && strncmp(zArg, "source-inline", 13)==0 ){ pikFlags |= PIKCHR_PROCESS_DIV_SOURCE_INLINE; } while( i<nArg && fossil_isspace(zArg[i]) ){ i++; } zArg += i; nArg -= i; } if( skin_detail_boolean("white-foreground") ){ pikFlags |= 0x02; /* PIKCHR_DARK_MODE */ } zPikVar = skin_detail("pikchr-foreground"); if( zPikVar && zPikVar[0] ){ blob_appendf(&bSrc, "fgcolor = %s\n", zPikVar); } zPikVar = skin_detail("pikchr-background"); if( zPikVar && zPikVar[0] ){ blob_appendf(&bSrc, "bgcolor = %s\n", zPikVar); } zPikVar = skin_detail("pikchr-scale"); if( zPikVar && (rPikVar = atof(zPikVar))>=0.1 && rPikVar<10.0 ){ blob_appendf(&bSrc, "scale = %.13g\n", rPikVar); } zPikVar = skin_detail("pikchr-fontscale"); if( zPikVar && (rPikVar = atof(zPikVar))>=0.1 && rPikVar<10.0 ){ blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar); } blob_append(&bSrc, zSrc, nSrc) /*have to dup input to ensure a NUL-terminated source string */; pikchr_process(blob_str(&bSrc), pikFlags, 0, ob); blob_reset(&bSrc); } /* Invoked for `...` blocks where there are nSep grave accents in a ** row that serve as the delimiter. According to CommonMark: ** ** * https://spec.commonmark.org/0.29/#fenced-code-blocks ** * https://spec.commonmark.org/0.29/#code-spans ** ** If nSep is 1 or 2, then this is a code-span which is inline. ** If nSep is 3 or more, then this is a fenced code block */ static int html_codespan( struct Blob *ob, /* Write the output here */ struct Blob *text, /* The stuff in between the code span marks */ int nSep, /* Number of grave accents marks as delimiters */ void *opaque ){ if( text==0 ){ /* no-op */ }else if( nSep<=2 ){ /* One or two graves: an in-line code span */ blob_append_literal(ob, "<code>"); html_escape(ob, blob_buffer(text), blob_size(text)); blob_append_literal(ob, "</code>"); }else{ /* Three or more graves: a fenced code block */ int n = blob_size(text); const char *z = blob_buffer(text); int i; for(i=0; i<n && z[i]!='\n'; i++){} if( i>=n ){ blob_appendf(ob, "<pre><code>%#h</code></pre>", n, z); }else{ int k, j; i++; for(k=0; k<i && fossil_isspace(z[k]); k++){} if( k==i ){ blob_appendf(ob, "<pre><code>%#h</code></pre>", n-i, z+i); }else{ for(j=k+1; j<i && !fossil_isspace(z[j]); j++){} if( j-k==6 && strncmp(z+k,"pikchr",6)==0 ){ while( j<i && fossil_isspace(z[j]) ){ j++; } pikchr_to_html(ob, z+i, n-i, z+j, i-j); }else{ blob_appendf(ob, "<pre><code class='language-%#h'>%#h</code></pre>", j-k, z+k, n-i, z+i); } } } } return 1; } static int html_double_emphasis( struct Blob *ob, struct Blob *text, char c, void *opaque ){ blob_append_literal(ob, "<strong>"); blob_appendb(ob, text); blob_append_literal(ob, "</strong>"); return 1; } static int html_emphasis( struct Blob *ob, struct Blob *text, char c, void *opaque ){ blob_append_literal(ob, "<em>"); blob_appendb(ob, text); blob_append_literal(ob, "</em>"); return 1; } static int html_image( struct Blob *ob, struct Blob *link, struct Blob *title, struct Blob *alt, void *opaque ){ blob_append_literal(ob, "<img src=\""); html_quote(ob, blob_buffer(link), blob_size(link)); blob_append_literal(ob, "\" alt=\""); html_quote(ob, blob_buffer(alt), blob_size(alt)); if( title && blob_size(title)>0 ){ blob_append_literal(ob, "\" title=\""); html_quote(ob, blob_buffer(title), blob_size(title)); } blob_append_literal(ob, "\">"); return 1; } static int html_linebreak(struct Blob *ob, void *opaque){ blob_append_literal(ob, "<br>\n"); return 1; } static int html_link( struct Blob *ob, struct Blob *link, struct Blob *title, struct Blob *content, void *opaque ){ char *zLink = blob_buffer(link); char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0; char zClose[20]; if( zLink==0 || zLink[0]==0 ){ zClose[0] = 0; }else{ static const int flags = WIKI_NOBADLINKS | WIKI_MARKDOWNLINKS ; wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle); } if( blob_size(content)==0 ){ if( link ) blob_appendb(ob, link); }else{ blob_appendb(ob, content); } blob_append(ob, zClose, -1); return 1; } static int html_triple_emphasis( struct Blob *ob, struct Blob *text, char c, void *opaque ){ blob_append_literal(ob, "<strong><em>"); blob_appendb(ob, text); blob_append_literal(ob, "</em></strong>"); return 1; } static void html_normal_text(struct Blob *ob, struct Blob *text, void *opaque){ html_escape(ob, blob_buffer(text), blob_size(text)); } /* ** Convert markdown into HTML. ** ** The document title is placed in output_title if not NULL. Or if ** output_title is NULL, the document title appears in the body. */ void markdown_to_html( struct Blob *input_markdown, /* Markdown content to be rendered */ struct Blob *output_title, /* Put title here. May be NULL */ struct Blob *output_body /* Put document body here. */ ){ struct mkd_renderer html_renderer = { /* prolog and epilog */ html_prolog, html_epilog, html_footnotes, /* block level elements */ html_blockcode, html_blockquote, html_blockhtml, html_header, html_hrule, html_list, html_list_item, html_paragraph, html_table, html_table_cell, html_table_row, html_footnote_item, /* span level elements */ html_autolink, html_codespan, html_double_emphasis, html_emphasis, html_image, html_linebreak, html_link, html_raw_html_tag, html_triple_emphasis, html_footnote_ref, /* low level elements */ 0, /* entity */ html_normal_text, /* misc. parameters */ "*_", /* emph_chars */ 0 /* opaque */ }; static int invocation = -1; /* no marker for the first document */ static const char* zRU = 0; /* REQUEST_URI with escaped quotes */ MarkdownToHtml context; memset(&context, 0, sizeof(context)); context.output_title = output_title; context.unique = to_base26(invocation++,1); if( !zRU ) zRU = escape_quotes(PD("REQUEST_URI","")); #ifndef FOOTNOTES_WITHOUT_URI blob_set( &context.reqURI, zRU ); #endif html_renderer.opaque = &context; if( 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 28 29 30 31 | * 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, * you just set "uint32" to the appropriate datatype for an * unsigned 32-bit integer. For example: * * cc -Duint32='unsigned long' md5.c | > > > > > > > > > > > > > > | 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 | * 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 0 /* defined FOSSIL_ENABLE_SSL */ /* MD5 is deprecated in OpenSSL. So we have to fall back to our own ** implementation */ # include <openssl/md5.h> # define MD5Context MD5_CTX # define MD5Init MD5_Init # define MD5Update MD5_Update # define MD5Final MD5_Final #else /* * If compiled on a machine that doesn't have a 32-bit integer, * you just set "uint32" to the appropriate datatype for an * unsigned 32-bit integer. For example: * * cc -Duint32='unsigned long' md5.c |
︙ | ︙ | |||
39 40 41 42 43 44 45 46 | int isInit; uint32 buf[4]; uint32 bits[2]; unsigned char in[64]; }; typedef struct Context MD5Context; /* | > > > > > > > | > > | 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 | int isInit; uint32 buf[4]; uint32 bits[2]; unsigned char in[64]; }; typedef struct Context MD5Context; #if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ defined(__arm__) || defined(_WIN32) # define byteReverse(A,B) #else /* * Convert an array of integers to little-endian. * Note: this code is a no-op on little-endian machines. */ static void byteReverse (unsigned char *buf, unsigned longs){ uint32 t; do { t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | ((unsigned)buf[1]<<8 | buf[0]); *(uint32 *)buf = t; buf += 4; } while (--longs); } #endif /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) |
︙ | ︙ | |||
155 156 157 158 159 160 161 | } /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ static void MD5Init(MD5Context *ctx){ | | | | 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 | } /* * 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]; |
︙ | ︙ | |||
215 216 217 218 219 220 221 | /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /* | | | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | /* 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; |
︙ | ︙ | |||
250 251 252 253 254 255 256 | } else { /* Pad block to 56 bytes */ memset(p, 0, count-8); } byteReverse(ctx->in, 14); /* Append length in bits and transform */ | | < | > | | | 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 | } else { /* Pad block to 56 bytes */ memset(p, 0, count-8); } byteReverse(ctx->in, 14); /* Append length in bits and transform */ memcpy(&ctx->in[14*sizeof(uint32)], ctx->bits, 2*sizeof(uint32)); MD5Transform(ctx->buf, (uint32 *)ctx->in); byteReverse((unsigned char *)ctx->buf, 4); memcpy(digest, ctx->buf, 16); memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } #endif /* ** 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]; } zBuf[j] = 0; } /* ** The state of an incremental MD5 checksum computation. Only one ** such computation can be underway at a time, of course. */ static MD5Context incrCtx; static int incrInit = 0; /* ** Initialize the incremental MD5 checksum. |
︙ | ︙ | |||
312 313 314 315 316 317 318 319 320 321 | /* ** Add the content of a blob to the incremental MD5 checksum. */ void md5sum_step_blob(Blob *p){ md5sum_step_text(blob_buffer(p), blob_size(p)); } /* ** Finish the incremental MD5 checksum. Store the result in blob pOut | > > > > > > > > > > > > > > > > > > > > | | 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 | /* ** Add the content of a blob to the incremental MD5 checksum. */ void md5sum_step_blob(Blob *p){ md5sum_step_text(blob_buffer(p), blob_size(p)); } /* ** For trouble-shooting only: ** ** Report the current state of the incremental checksum. */ const char *md5sum_current_state(void){ unsigned int cksum = 0; unsigned int *pFirst, *pLast; static char zResult[12]; pFirst = (unsigned int*)&incrCtx; pLast = (unsigned int*)((&incrCtx)+1); while( pFirst<pLast ){ cksum += *pFirst; pFirst++; } 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]; |
︙ | ︙ | |||
338 339 340 341 342 343 344 | } return zOut; } /* ** Compute the MD5 checksum of a file on disk. Store the resulting | | | | 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 | } return zOut; } /* ** Compute the MD5 checksum of a file on disk. Store the resulting ** checksum in the blob pCksum. pCksum is assumed to be initialized. ** ** Return the number of errors. */ int md5sum_file(const char *zFilename, Blob *pCksum){ FILE *in; MD5Context ctx; unsigned char zResult[16]; char zBuf[10240]; in = fossil_fopen(zFilename,"rb"); if( in==0 ){ return 1; } MD5Init(&ctx); for(;;){ int n; n = fread(zBuf, 1, sizeof(zBuf), in); |
︙ | ︙ | |||
393 394 395 396 397 398 399 | MD5Final(zResult, &ctx); DigestToBase16(zResult, blob_buffer(pCksum)); return 0; } /* | | > > | | > | | 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 | MD5Final(zResult, &ctx); DigestToBase16(zResult, blob_buffer(pCksum)); return 0; } /* ** COMMAND: md5sum* ** ** Usage: %fossil md5sum FILES.... ** ** 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); } } |
Added src/menu.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* This script runs when the submenu contains controls created by routines ** like style_submenu_checkbox() or style_submenu_multichoice() - controls ** that require javascript support. */ function toggle_annotation_log(){ var w = document.getElementById("annotation_log"); var x = document.forms["f01"].elements["log"].checked w.style.display = x ? "block" : "none"; } function submenu_onchange_submit(){ var w = document.getElementById("f01"); w.submit(); } (function (){ for(var i=0; 1; i++){ var x = document.getElementById("submenuctrl-"+i); if(!x) break; if( !x.hasAttribute('data-ctrl') ){ x.onchange = submenu_onchange_submit; }else{ var cx = x.getAttribute('data-ctrl'); if( cx=="toggle_annotation_log" ){ x.onchange = toggle_annotation_log; } } } })(); |
Changes to src/merge.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file contains code used to merge two or more branches into ** a single tree. */ #include "config.h" #include "merge.h" #include <assert.h> | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > < > | < | > < | > > > > > > > > > > > > > > > > > > > | | | > > | | > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | > > > > > > > > > | > > > > > > | | | > > | < > | | > | > > > | | > > | > | > > > > > > > | > | > > > > > > < | > > > | | | > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | > > > > > > | < > | | > > > > > | | > | < > | < | > > > > > > > > > > > > > > | | < < < < | | > | | < > | > > > | > | > > | < > > > > > > | | | | > > > | < < < < < > | > > | > > > > | > > > > > > > > > > > > > > > > | > < < | | > > | > > > > > > > | > > > | > > | < > | < > > > | > > > > > > > > > > > > > > | > > > > > > > > > > > < > | | > | < < | < < < < < < < | | > | | > > | > > > > | > > > > | | | | | > | > | | > > > | | | < > | > > > > | | > > > > < | | | > | > > > > | | | | < < | | | | | > > | | > | > > > | | > > | | | > | < | | < | < > | | | | > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > | | | 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 | ** This file contains code used to merge two or more branches into ** a single tree. */ #include "config.h" #include "merge.h" #include <assert.h> /* ** 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,toLocal())," " 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", 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, get_comment_format()); fossil_free(zCom); } db_finalize(&q); } /* Pick the most recent leaf that is (1) not equal to vid and (2) ** has not already been merged into vid and (3) the leaf is not ** closed and (4) the leaf is in the same branch as vid. ** ** Set vmergeFlag to control whether the vmerge table is checked. */ int fossil_find_nearest_fork(int vid, int vmergeFlag){ Blob sql; Stmt q; int rid = 0; blob_zero(&sql); blob_append_sql(&sql, "SELECT leaf.rid" " FROM leaf, event" " WHERE leaf.rid=event.objid" " AND leaf.rid!=%d", /* Constraint (1) */ vid ); if( vmergeFlag ){ blob_append_sql(&sql, " AND leaf.rid NOT IN (SELECT merge FROM vmerge)" /* Constraint (2) */ ); } blob_append_sql(&sql, " AND NOT EXISTS(SELECT 1 FROM tagxref" /* Constraint (3) */ " WHERE rid=leaf.rid" " AND tagid=%d" " AND tagtype>0)" " AND (SELECT value FROM tagxref" /* Constraint (4) */ " WHERE tagid=%d AND rid=%d AND tagtype>0) =" " (SELECT value FROM tagxref" " WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)" " ORDER BY event.mtime DESC LIMIT 1", TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH ); db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); if( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); } db_finalize(&q); return rid; } /* ** Check content that was received with rcvid and return true if any ** fork was created. */ int fossil_any_has_fork(int rcvid){ static Stmt q; int fForkSeen = 0; if( rcvid==0 ) return 0; db_static_prepare(&q, " SELECT pid FROM plink WHERE pid>0 AND isprim" " AND cid IN (SELECT blob.rid FROM blob" " WHERE rcvid=:rcvid)"); db_bind_int(&q, ":rcvid", rcvid); while( !fForkSeen && db_step(&q)==SQLITE_ROW ){ int pid = db_column_int(&q, 0); if( count_nonbranch_children(pid)>1 ){ compute_leaves(pid,1); if( db_int(0, "SELECT count(*) FROM leaves")>1 ){ int rid = db_int(0, "SELECT rid FROM leaves, event" " WHERE event.objid=leaves.rid" " ORDER BY event.mtime DESC LIMIT 1"); fForkSeen = fossil_find_nearest_fork(rid, db_open_local(0))!=0; } } } db_finalize(&q); return fForkSeen; } /* ** Add an entry to the FV table for all files renamed between ** version N and the version specified by vid. */ static void add_renames( const char *zFnCol, /* The FV column for the filename in vid */ int vid, /* The desired version's- RID */ int nid, /* The check-in rid for the name pivot */ int revOK, /* OK to move backwards (child->parent) if true */ const char *zDebug /* Generate trace output if not NULL */ ){ int nChng; /* Number of file name changes */ int *aChng; /* An array of file name changes */ int i; /* Loop counter */ find_filename_changes(nid, vid, revOK, &nChng, &aChng, zDebug); if( nChng==0 ) return; for(i=0; i<nChng; i++){ char *zN, *zV; zN = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]); zV = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2+1]); db_multi_exec( "INSERT OR IGNORE INTO fv(%s,fnn) VALUES(%Q,%Q)", zFnCol /*safe-for-%s*/, zV, zN ); if( db_changes()==0 ){ db_multi_exec( "UPDATE fv SET %s=%Q WHERE fnn=%Q", zFnCol /*safe-for-%s*/, zV, zN ); } free(zN); free(zV); } free(aChng); } /* Make an entry in the vmerge table for the given id, and rid. */ static void vmerge_insert(int id, int rid){ db_multi_exec( "INSERT OR IGNORE INTO vmerge(id,merge,mhash)" "VALUES(%d,%d,(SELECT uuid FROM blob WHERE rid=%d))", id, rid, rid ); } /* ** Print the contents of the "fv" table on standard output, for debugging ** purposes. ** ** Only show entries where a file has changed, unless showAll is true. */ static void debug_fv_dump(int showAll){ Stmt q; if( showAll ){ db_prepare(&q, "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, " " isexe, islinkv, islinkm, fnn FROM fv" ); }else{ db_prepare(&q, "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, " " isexe, islinkv, islinkm, fnn FROM fv" " WHERE chnged OR (ridv!=ridm AND ridm!=ridp)" ); } while( db_step(&q)==SQLITE_ROW ){ fossil_print("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d " " islinkv=%d islinkm=%d\n", db_column_int(&q, 0), db_column_int(&q, 5), db_column_int(&q, 6), db_column_int(&q, 7), db_column_int(&q, 4), db_column_int(&q, 8), db_column_int(&q, 9), db_column_int(&q, 10)); fossil_print(" fn = [%s]\n", db_column_text(&q, 1)); fossil_print(" fnp = [%s]\n", db_column_text(&q, 2)); fossil_print(" fnm = [%s]\n", db_column_text(&q, 3)); fossil_print(" fnn = [%s]\n", db_column_text(&q, 11)); } db_finalize(&q); } /* ** Print the content of the VFILE table on standard output, for ** debugging purposes. */ static void debug_show_vfile(void){ Stmt q; int pvid = -1; db_prepare(&q, "SELECT vid, id, chnged, deleted, isexe, islink, rid, mrid, mtime," " pathname, origname, mhash FROM vfile" " ORDER BY vid, pathname" ); while( db_step(&q)==SQLITE_ROW ){ int vid = db_column_int(&q, 0); int chnged = db_column_int(&q, 2); int dltd = db_column_int(&q, 3); int isexe = db_column_int(&q, 4); int islnk = db_column_int(&q, 5); int rid = db_column_int(&q, 6); int mrid = db_column_int(&q, 7); const char *zPath = db_column_text(&q, 9); const char *zOrig = db_column_text(&q, 10); if( vid!=pvid ){ fossil_print("VFILE vid=%d (%z):\n", vid, db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid)); pvid = vid; } fossil_print(" rid %-6d mrid %-6d %4s %3s %3s %3s %s", rid, mrid, chnged ? "chng" : "", dltd ? "del" : "", isexe ? "exe" : "", islnk ? "lnk" : "", zPath); if( zOrig && zOrig[0] ){ fossil_print(" <- %s\n", zOrig); }else{ fossil_print("\n"); } } db_finalize(&q); } /* ** COMMAND: test-show-vfile ** Usage: %fossil test-show-vfile ** ** Show the content of the VFILE table in a local check-out. */ void test_show_vfile_cmd(void){ if( g.argc!=2 ){ fossil_fatal("unknown arguments to the %s command\n", g.argv[1]); } verify_all_options(); db_must_be_within_tree(); debug_show_vfile(); } /* ** COMMAND: merge ** COMMAND: cherry-pick ** ** Usage: %fossil merge ?OPTIONS? ?VERSION? ** ** The argument VERSION is a version that should be merged into the ** current check-out. All changes from VERSION back to the nearest ** common ancestor are merged. Except, if either of the --cherrypick ** or --backout options are used only the changes associated with the ** single check-in VERSION are merged. The --backout option causes ** the changes associated with VERSION to be removed from the current ** check-out rather than added. When invoked with the name ** cherry-pick, this command works exactly like merge --cherrypick. ** ** Files which are renamed in the merged-in branch will be renamed in ** the current check-out. ** ** If the VERSION argument is omitted, then Fossil attempts to find ** a recent fork on the current branch to merge. ** ** Options: ** --backout Do a reverse cherrypick merge against VERSION. ** In other words, back out the changes that were ** added by VERSION. ** --baseline BASELINE Use BASELINE as the "pivot" of the merge instead ** of the nearest common ancestor. This allows ** a sequence of changes in a branch to be merged ** without having to merge the entire branch. ** --binary GLOBPATTERN Treat files that match GLOBPATTERN as binary ** and do not try to merge parallel changes. This ** option overrides the "binary-glob" setting. ** --cherrypick Do a cherrypick merge VERSION into the current ** check-out. A cherrypick merge pulls in the changes ** of the single check-in VERSION, rather than all ** changes back to the nearest common ancestor. ** -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 ** -K|--keep-merge-files On merge conflict, retain the temporary files ** used for merging, named *-baseline, *-original, ** and *-merge. ** -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 = 0; /* The pivot version - most recent common ancestor P */ int nid = 0; /* The name pivot version "N" */ 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 showVfileFlag; /* True if the --show-vfile flag is present */ int keepMergeFlag; /* True if --keep-merge-files is present */ int nConflict = 0; /* Number of conflicts seen */ int nOverwrite = 0; /* Number of unmanaged files overwritten */ char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ Stmt q; /* Notation: ** ** V The current check-out ** M The version being merged in ** P The "pivot" - the most recent common ancestor of V and M. ** N The "name pivot" - for detecting renames */ 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; if('c'==*g.zCmdName/*called as cherry-pick, possibly a short form*/){ pickFlag = 1; } integrateFlag = find_option("integrate",0,0)!=0; backoutFlag = find_option("backout",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); keepMergeFlag = find_option("keep-merge-files", "K",0)!=0; /* Undocumented --debug and --show-vfile options: ** ** When included on the command-line, --debug causes lots of state ** information to be displayed. This option is undocumented as it ** might change or be eliminated in future releases. ** ** The --show-vfile flag does a dump of the VFILE table for reference. ** ** Hints: ** * Combine --debug and --verbose for still more output. ** * The --dry-run option is also useful in combination with --debug. */ debugFlag = find_option("debug",0,0)!=0; if( debugFlag && verboseFlag ) debugFlag = 2; showVfileFlag = find_option("show-vfile",0,0)!=0; 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"); } if( forceFlag==0 && leaf_is_closed(vid) ){ fossil_fatal("cannot merge into a closed leaf. Use --force to override"); } if( !dryRunFlag ){ if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "merge") ){ fossil_fatal("merge abandoned due to sync failure"); } } /* Find mid, the artifactID of the version to be merged into the current ** check-out */ if( g.argc==3 ){ /* Mid is specified as an argument on the command-line */ mid = name_to_typed_rid(g.argv[2], "ci"); if( mid==0 || !is_a_version(mid) ){ 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 check-out and (3) ** the leaf is not closed and (4) the leaf is in the same branch ** as the current check-out. */ Stmt q; if( pickFlag || backoutFlag || integrateFlag){ fossil_fatal("cannot use --backout, --cherrypick or --integrate " "with a fork merge"); } mid = fossil_find_nearest_fork(vid, db_open_local(0)); if( mid==0 ){ fossil_fatal("no unmerged forks of branch \"%s\"", 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,toLocal())," " coalesce(ecomment, comment)," " coalesce(euser, user)" " FROM event, blob" " WHERE event.objid=%d AND blob.rid=%d", 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, get_comment_format()); fossil_free(zCom); } db_finalize(&q); }else{ usage("?OPTIONS? ?VERSION?"); return; } if( zPivot ){ pid = name_to_typed_rid(zPivot, "ci"); if( pid==0 || !is_a_version(pid) ){ fossil_fatal("not a version: %s", zPivot); } if( pickFlag ){ fossil_fatal("incompatible options: --cherrypick and --baseline"); } } if( pickFlag || backoutFlag ){ if( integrateFlag ){ fossil_fatal("incompatible options: --integrate and --cherrypick " "with --backout"); } pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid); if( pid<=0 ){ fossil_fatal("cannot find an ancestor for %s", g.argv[2]); } }else{ if( !zPivot ){ pivot_set_primary(mid); pivot_set_secondary(vid); db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0"); while( db_step(&q)==SQLITE_ROW ){ pivot_set_secondary(db_column_int(&q,0)); } db_finalize(&q); pid = pivot_find(0); if( pid<=0 ){ fossil_fatal("cannot find a common ancestor between the current " "check-out and %s", g.argv[2]); } } pivot_set_primary(mid); pivot_set_secondary(vid); nid = pivot_find(1); if( nid!=pid ){ pivot_set_primary(nid); pivot_set_secondary(pid); nid = pivot_find(1); } } if( backoutFlag ){ int t = pid; pid = mid; mid = t; } if( nid==0 ) nid = pid; if( !is_a_version(pid) ){ 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( integrateFlag && content_is_private(mid) ){ fossil_warning( "ignoring --integrate: %s is on a private branch" "\n Use \"fossil amend --close\" (after commit) to close the 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( zPivot ){ vAncestor = db_exists( "WITH RECURSIVE ancestor(id) AS (" " VALUES(%d)" " UNION" " SELECT pid FROM plink, ancestor" " WHERE cid=ancestor.id AND pid!=%d AND cid!=%d)" "SELECT 1 FROM ancestor WHERE id=%d LIMIT 1", vid, nid, pid, pid ) ? 'p' : 'n'; } if( debugFlag ){ char *z; z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nid); fossil_print("N=%-4d %z (file rename pivot)\n", nid, z); z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid); fossil_print("P=%-4d %z (file content pivot)\n", pid, z); z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); fossil_print("M=%-4d %z (merged-in version)\n", mid, z); z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); fossil_print("V=%-4d %z (current version)\n", vid, z); fossil_print("vAncestor = '%c'\n", vAncestor); } if( showVfileFlag ) debug_show_vfile(); /* ** The vfile.pathname field is used to match files against each other. The ** FV table contains one row for each each unique filename in ** in the current check-out, the pivot, and the version being merged. */ db_multi_exec( "DROP TABLE IF EXISTS fv;" "CREATE TEMP TABLE fv(\n" " fn TEXT UNIQUE %s,\n" /* The filename */ " idv INTEGER DEFAULT 0,\n" /* VFILE entry for current version */ " idp INTEGER DEFAULT 0,\n" /* VFILE entry for the pivot */ " idm INTEGER DEFAULT 0,\n" /* VFILE entry for version merging in */ " chnged BOOLEAN,\n" /* True if current version has been edited */ " ridv INTEGER DEFAULT 0,\n" /* Record ID for current version */ " ridp INTEGER DEFAULT 0,\n" /* Record ID for pivot */ " ridm INTEGER DEFAULT 0,\n" /* Record ID for merge */ " isexe BOOLEAN,\n" /* Execute permission enabled */ " fnp TEXT UNIQUE %s,\n" /* The filename in the pivot */ " fnm TEXT UNIQUE %s,\n" /* The filename in the merged version */ " fnn TEXT UNIQUE %s,\n" /* The filename in the name pivot */ " islinkv BOOLEAN,\n" /* True if current version is a symlink */ " islinkm BOOLEAN\n" /* True if merged version in is a symlink */ ");", filename_collation(), filename_collation(), filename_collation(), filename_collation() ); /* ** Compute name changes from N to V, P, and M */ add_renames("fn", vid, nid, 0, debugFlag ? "N->V" : 0); add_renames("fnp", pid, nid, 0, debugFlag ? "N->P" : 0); add_renames("fnm", mid, nid, backoutFlag, debugFlag ? "N->M" : 0); if( debugFlag ){ fossil_print("******** FV after name change search *******\n"); debug_fv_dump(1); } if( nid!=pid ){ /* See forum thread https://fossil-scm.org/forum/forumpost/549700437b ** ** If a filename changes between nid and one of the other check-ins ** pid, vid, or mid, then it might not have changed for all of them. ** try to fill in the appropriate filename in all slots where the ** name is missing. ** ** This does not work if ** (1) The filename changes more than once in between nid and vid/mid ** (2) Two or more filenames swap places - for example if A is renamed ** to B and B is renamed to A. ** The Fossil merge algorithm breaks down in those cases. It will need ** to be completely rewritten to handle such complex cases. Such cases ** appear to be rare, and also confusing to humans. */ db_multi_exec( "UPDATE OR IGNORE fv SET fnp=vfile.pathname FROM vfile" " WHERE fnp IS NULL" " AND vfile.pathname = fv.fnn" " AND vfile.vid=%d;", pid ); db_multi_exec( "UPDATE OR IGNORE fv SET fn=vfile.pathname FROM vfile" " WHERE fn IS NULL" " AND vfile.pathname = coalesce(fv.fnp,fv.fnn)" " AND vfile.vid=%d;", vid ); db_multi_exec( "UPDATE OR IGNORE fv SET fnm=vfile.pathname FROM vfile" " WHERE fnm IS NULL" " AND vfile.pathname = coalesce(fv.fnp,fv.fnn)" " AND vfile.vid=%d;", mid ); db_multi_exec( "UPDATE OR IGNORE fv SET fnp=vfile.pathname FROM vfile" " WHERE fnp IS NULL" " AND vfile.pathname IN (fv.fnm,fv.fn)" " AND vfile.vid=%d;", pid ); db_multi_exec( "UPDATE OR IGNORE fv SET fn=vfile.pathname FROM vfile" " WHERE fn IS NULL" " AND vfile.pathname = fv.fnm" " AND vfile.vid=%d;", vid ); db_multi_exec( "UPDATE OR IGNORE fv SET fnm=vfile.pathname FROM vfile" " WHERE fnm IS NULL" " AND vfile.pathname = fv.fn" " AND vfile.vid=%d;", mid ); } if( debugFlag ){ fossil_print("******** FV after name change fill-in *******\n"); debug_fv_dump(1); } /* ** Add files found in V */ db_multi_exec( "UPDATE OR IGNORE fv SET fn=coalesce(fn%c,fnn) WHERE fn IS NULL;" "REPLACE INTO fv(fn,fnp,fnm,fnn,idv,ridv,islinkv,isexe,chnged)" " SELECT pathname, fnp, fnm, fnn, id, rid, islink, vf.isexe, vf.chnged" " FROM vfile vf" " LEFT JOIN fv ON fn=coalesce(origname,pathname)" " AND rid>0 AND vf.chnged NOT IN (3,5)" " WHERE vid=%d;", vAncestor, vid ); if( debugFlag>=2 ){ fossil_print("******** FV after adding files in current version *******\n"); debug_fv_dump(1); } /* ** Add files found in P */ db_multi_exec( "UPDATE OR IGNORE fv SET fnp=coalesce(fnn," " (SELECT coalesce(origname,pathname) FROM vfile WHERE id=idv))" " WHERE fnp IS NULL;" "INSERT OR IGNORE INTO fv(fnp)" " SELECT coalesce(origname,pathname) FROM vfile WHERE vid=%d;", pid ); if( debugFlag>=2 ){ fossil_print("******** FV after adding pivot files *******\n"); debug_fv_dump(1); } /* ** Add files found in M */ db_multi_exec( "UPDATE OR IGNORE fv SET fnm=fnp WHERE fnm IS NULL;" "INSERT OR IGNORE INTO fv(fnm)" " SELECT pathname FROM vfile WHERE vid=%d;", mid ); if( debugFlag>=2 ){ fossil_print("******** FV after adding merge-in files *******\n"); debug_fv_dump(1); } /* ** Compute the file version ids for P and M */ if( pid==vid ){ db_multi_exec( "UPDATE fv SET idp=idv, ridp=ridv WHERE ridv>0 AND chnged NOT IN (3,5)" ); }else{ db_multi_exec( "UPDATE fv SET idp=coalesce(vfile.id,0), ridp=coalesce(vfile.rid,0)" " FROM vfile" " WHERE vfile.vid=%d AND fv.fnp=vfile.pathname", pid ); } db_multi_exec( "UPDATE fv SET" " idm=coalesce(vfile.id,0)," " ridm=coalesce(vfile.rid,0)," " islinkm=coalesce(vfile.islink,0)," " isexe=coalesce(vfile.isexe,fv.isexe)" " FROM vfile" " WHERE vid=%d AND fnm=pathname", mid ); /* ** Update the execute bit on files where it's changed from P->M but not P->V */ db_prepare(&q, "SELECT idv, fn, fv.isexe FROM fv, vfile p, vfile v" " WHERE p.id=idp AND v.id=idv AND fv.isexe!=p.isexe AND v.isexe=p.isexe" ); while( db_step(&q)==SQLITE_ROW ){ int idv = db_column_int(&q, 0); const char *zName = db_column_text(&q, 1); int isExe = db_column_int(&q, 2); fossil_print("%s %s\n", isExe ? "EXECUTABLE" : "UNEXEC", zName); if( !dryRunFlag ){ char *zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); file_setexe(zFullPath, isExe); free(zFullPath); db_multi_exec("UPDATE vfile SET isexe=%d WHERE id=%d", isExe, idv); } } db_finalize(&q); if( debugFlag ){ fossil_print("******** FV final *******\n"); debug_fv_dump( debugFlag>=2 ); } /************************************************************************ ** All of the information needed to do the merge is now contained in the ** FV table. Starting here, we begin to actually carry out the merge. ** ** First, 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" ); while( db_step(&q)==SQLITE_ROW ){ int idv = db_column_int(&q, 0); int ridm = db_column_int(&q, 1); const char *zName = db_column_text(&q, 2); int islinkm = db_column_int(&q, 3); /* Copy content from idm over into idv. Overwrite idv. */ fossil_print("UPDATE %s\n", zName); if( !dryRunFlag ){ undo_save(zName); db_multi_exec( "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d," " mhash=CASE WHEN rid<>%d" " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END" " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv ); vfile_to_disk(0, idv, 0, 0); } } db_finalize(&q); /* ** Do a three-way merge on files that have changes on both P->M and P->V. ** ** Proceed even if the file doesn't exist on P, just like the common ancestor ** of M and V is an empty file. In this case, merge conflict marks will be ** added to the file and user will be forced to take a decision. */ db_prepare(&q, "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv" " WHERE idv>0 AND idm>0" " AND ridm!=ridp AND (ridv!=ridp OR chnged)", glob_expr("fv.fn", zBinGlob) ); while( db_step(&q)==SQLITE_ROW ){ int ridm = db_column_int(&q, 0); int idv = db_column_int(&q, 1); int ridp = db_column_int(&q, 2); int ridv = db_column_int(&q, 3); int isBinary = db_column_int(&q, 4); const char *zName = db_column_text(&q, 5); int isExe = db_column_int(&q, 6); 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 ){ fossil_print("***** Cannot merge symlink %s\n", zName); nConflict++; }else{ if( !dryRunFlag ) undo_save(zName); zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); content_get(ridp, &p); content_get(ridm, &m); if( isBinary ){ rc = -1; blob_zero(&r); }else{ unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES; rc = merge_3way(&p, zFullPath, &m, &r, mergeFlags); } if( rc>=0 ){ if( !dryRunFlag ){ blob_write_to_file(&r, zFullPath); file_setexe(zFullPath, isExe); } db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv); if( rc>0 ){ fossil_print("***** %d merge conflict%s in %s\n", rc, rc>1 ? "s" : "", zName); nConflict++; } }else{ fossil_print("***** Cannot merge binary file %s\n", zName); nConflict++; } blob_reset(&p); blob_reset(&m); blob_reset(&r); } vmerge_insert(idv, ridm); } db_finalize(&q); /* ** Drop files that are in P and V but not in M */ db_prepare(&q, "SELECT idv, fn, chnged FROM fv" " WHERE idp>0 AND idv>0 AND idm=0" ); while( db_step(&q)==SQLITE_ROW ){ int idv = db_column_int(&q, 0); const char *zName = db_column_text(&q, 1); int chnged = db_column_int(&q, 2); /* Delete the file idv */ fossil_print("DELETE %s\n", zName); if( chnged ){ fossil_warning("WARNING: local edits lost for %s", zName); nConflict++; } if( !dryRunFlag ) undo_save(zName); db_multi_exec( "UPDATE vfile SET deleted=1 WHERE id=%d", idv ); if( !dryRunFlag ){ char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); file_delete(zFullPath); free(zFullPath); } } db_finalize(&q); /* For certain sets of renames (e.g. A -> B and B -> A), a file that is ** being renamed must first be moved to a temporary location to avoid ** being overwritten by another rename operation. A row is added to the ** TMPRN table for each of these temporary renames. */ db_multi_exec( "DROP TABLE IF EXISTS tmprn;" "CREATE TEMP TABLE tmprn(fn UNIQUE, tmpfn);" ); /* ** Rename files that have taken a rename on P->M but which keep the same ** name on P->V. If a file is renamed on P->V only or on both P->V and ** P->M then we retain the V name of the file. */ db_prepare(&q, "SELECT idv, fnp, fnm, isexe FROM fv" " WHERE idv>0 AND idp>0 AND idm>0 AND fnp=fn AND fnm!=fnp" ); while( db_step(&q)==SQLITE_ROW ){ int idv = db_column_int(&q, 0); const char *zOldName = db_column_text(&q, 1); const char *zNewName = db_column_text(&q, 2); int isExe = db_column_int(&q, 3); fossil_print("RENAME %s -> %s\n", zOldName, zNewName); if( !dryRunFlag ) undo_save(zOldName); if( !dryRunFlag ) undo_save(zNewName); db_multi_exec( "UPDATE vfile SET pathname=NULL, origname=pathname" " WHERE vid=%d AND pathname=%Q;" "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)" " WHERE id=%d;", vid, zNewName, zNewName, idv ); if( !dryRunFlag ){ char *zFullOldPath, *zFullNewPath; zFullOldPath = db_text(0,"SELECT tmpfn FROM tmprn WHERE fn=%Q", zOldName); if( !zFullOldPath ){ zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName); } zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName); if( file_size(zFullNewPath, RepoFILE)>=0 ){ Blob tmpPath; file_tempname(&tmpPath, "", 0); db_multi_exec("INSERT INTO tmprn(fn,tmpfn) VALUES(%Q,%Q)", zNewName, blob_str(&tmpPath)); if( file_islink(zFullNewPath) ){ symlink_copy(zFullNewPath, blob_str(&tmpPath)); }else{ file_copy(zFullNewPath, blob_str(&tmpPath)); } blob_reset(&tmpPath); } if( file_islink(zFullOldPath) ){ symlink_copy(zFullOldPath, zFullNewPath); }else{ file_copy(zFullOldPath, zFullNewPath); } file_setexe(zFullNewPath, isExe); file_delete(zFullOldPath); free(zFullNewPath); free(zFullOldPath); } } db_finalize(&q); /* A file that has been deleted and replaced by a renamed file will have a ** NULL pathname. Change it to something that makes the output of "status" ** and similar commands make sense for such files and that will (most likely) ** not be an actual existing pathname. */ db_multi_exec( "UPDATE vfile SET pathname=origname || ' (overwritten by rename)'" " WHERE pathname IS NULL" ); /* ** Insert into V any files that are not in V or P but are in M. */ db_prepare(&q, "SELECT idm, fnm FROM fv" " WHERE idp=0 AND idv=0 AND idm>0" ); while( db_step(&q)==SQLITE_ROW ){ int idm = db_column_int(&q, 0); const char *zName; char *zFullName; db_multi_exec( "REPLACE INTO vfile(vid,chnged,deleted,rid,mrid," "isexe,islink,pathname,mhash)" " SELECT %d,%d,0,rid,mrid,isexe,islink,pathname," "CASE WHEN rid<>mrid" " THEN (SELECT uuid FROM blob WHERE blob.rid=vfile.mrid) END " "FROM vfile WHERE id=%d", vid, integrateFlag?5:3, idm ); zName = db_column_text(&q, 1); zFullName = mprintf("%s%s", g.zLocalRoot, zName); if( file_isfile_or_link(zFullName) && !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){ /* Name of backup file with Original content */ char *zOrig = file_newname(zFullName, "original", 1); /* Backup previously unanaged file before to be overwritten */ file_copy(zFullName, zOrig); fossil_free(zOrig); fossil_print("ADDED %s (overwrites an unmanaged file)", zName); if( !dryRunFlag ) fossil_print(", original copy backed up locally"); fossil_print("\n"); nOverwrite++; }else{ fossil_print("ADDED %s\n", zName); } fossil_free(zFullName); if( !dryRunFlag ){ undo_save(zName); vfile_to_disk(0, idm, 0, 0); } } db_finalize(&q); /* Report on conflicts */ if( nConflict ){ fossil_warning("WARNING: %d merge conflicts", nConflict); } 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 ){ vmerge_insert(-1, mid); /* For a cherry-pick merge, make the default check-in comment the same ** as the check-in comment on the check-in that is being merged in. */ db_multi_exec( "REPLACE INTO vvar(name,value)" " SELECT 'ci-comment', coalesce(ecomment,comment) FROM event" " WHERE type='ci' AND objid=%d", mid ); }else if( backoutFlag ){ vmerge_insert(-2, pid); }else if( integrateFlag ){ vmerge_insert(-4, mid); }else{ vmerge_insert(0, mid); } if( !dryRunFlag ) undo_finish(); db_end_transaction(dryRunFlag); } |
Changes to src/merge3.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 | #define ISDEBUG 1 #else #define DEBUG(X) #define ISDEBUG 0 #endif /* The minimum of two integers */ | > | > > | | | | | 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 | #define ISDEBUG 1 #else #define DEBUG(X) #define ISDEBUG 0 #endif /* The minimum of two integers */ #ifndef min # define min(A,B) (A<B?A:B) #endif /* ** Compare N lines of text from pV1 and pV2. If the lines ** are the same, return true. Return false if one or more of the N ** lines are different. ** ** The cursors on both pV1 and pV2 is unchanged by this comparison. */ static int sameLines(Blob *pV1, Blob *pV2, int N){ char *z1, *z2; int i; char c; if( N==0 ) return 1; z1 = &blob_buffer(pV1)[blob_tell(pV1)]; z2 = &blob_buffer(pV2)[blob_tell(pV2)]; for(i=0; (c=z1[i])==z2[i]; i++){ if( c=='\n' || c==0 ){ N--; if( N==0 || c==0 ) return 1; } } return 0; } /* ** Look at the next edit triple in both aC1 and aC2. (An "edit triple" is ** three integers describing the number of copies, deletes, and inserts in ** moving from the original to the edited copy of the file.) If the three ** integers of the edit triples describe an identical edit, then return 1. ** If the edits are different, return 0. */ static int sameEdit( int *aC1, /* Array of edit integers for file 1 */ int *aC2, /* Array of edit integers for file 2 */ Blob *pV1, /* Text of file 1 */ Blob *pV2 /* Text of file 2 */ |
︙ | ︙ | |||
78 79 80 81 82 83 84 | ** The aC[] array contains triples of integers. Within each triple, the ** elements are: ** ** (0) The number of lines to copy ** (1) The number of lines to delete ** (2) The number of liens to insert ** | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | ** The aC[] array contains triples of integers. Within each triple, the ** elements are: ** ** (0) The number of lines to copy ** (1) The number of lines to delete ** (2) The number of liens to insert ** ** Suppose we want to advance over sz lines of the original file. This routine ** returns true if that advance would land us on a copy operation. It ** returns false if the advance would end on a delete. */ static int ends_at_CPY(int *aC, int sz){ while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){ if( aC[0]>=sz ) return 1; sz -= aC[0]; |
︙ | ︙ | |||
111 112 113 114 115 116 117 | ** The aC[] array is updated and the new index into aC[] is returned. */ static int output_one_side( Blob *pOut, /* Write to this blob */ Blob *pSrc, /* The edited file that is to be copied to pOut */ int *aC, /* Array of integer triples describing the edit */ int i, /* Index in aC[] of current location in pSrc */ | | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > > > > > > > > > > > > > > > > > > | | | 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 | ** The aC[] array is updated and the new index into aC[] is returned. */ static int output_one_side( Blob *pOut, /* Write to this blob */ Blob *pSrc, /* The edited file that is to be copied to pOut */ int *aC, /* Array of integer triples describing the edit */ int i, /* Index in aC[] of current location in pSrc */ int sz, /* Number of lines in unedited source to output */ int *pLn /* Line number counter */ ){ while( sz>0 ){ if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break; if( aC[i]>=sz ){ blob_copy_lines(pOut, pSrc, sz); *pLn += sz; aC[i] -= sz; break; } blob_copy_lines(pOut, pSrc, aC[i]); *pLn += aC[i]; blob_copy_lines(pOut, pSrc, aC[i+2]); *pLn += aC[i+2]; sz -= aC[i] + aC[i+1]; i += 3; } 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 <<<<<<<<<<<<", "||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||", "======= MERGED IN content follows ===============================", ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" }; /* ** Return true if the input blob contains any CR/LF pairs on the first ** ten lines. This should be enough to detect files that use mainly CR/LF ** line endings without causing a performance impact for LF only files. */ int contains_crlf(Blob *p){ int i; int j = 0; const int maxL = 10; /* Max lines to check */ const char *z = blob_buffer(p); int n = blob_size(p)+1; for(i=1; i<n; ){ if( z[i-1]=='\r' && z[i]=='\n' ) return 1; while( i<n && z[i]!='\n' ){ i++; } j++; if( j>maxL ) return 0; } return 0; } /* ** Ensure that the text in pBlob ends with a new line. ** If useCrLf is true adds "\r\n" otherwise '\n'. */ void ensure_line_end(Blob *pBlob, int useCrLf){ if( pBlob->nUsed<=0 ) return; if( pBlob->aData[pBlob->nUsed-1]!='\n' ){ if( useCrLf ) blob_append_char(pBlob, '\r'); blob_append_char(pBlob, '\n'); } } /* ** Write out one of the four merge-marks. */ void append_merge_mark(Blob *pOut, int iMark, int ln, int useCrLf){ ensure_line_end(pOut, useCrLf); blob_append(pOut, mergeMarker[iMark], -1); if( ln>0 ) blob_appendf(pOut, " (line %d)", ln); ensure_line_end(pOut, useCrLf); } /* ** Do a three-way merge. Initialize pOut to contain the result. ** ** The merge is an edit against pV2. Both pV1 and pV2 have a ** common origin at pPivot. Apply the changes of pPivot ==> pV1 ** to pV2. ** ** The return is 0 upon complete success. If any input file is binary, ** -1 is returned and pOut is unmodified. If there are merge ** conflicts, the merge proceeds as best as it can and the number ** of conflicts is returns */ static int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){ int *aC1; /* Changes from pPivot to pV1 */ int *aC2; /* Changes from pPivot to pV2 */ int i1, i2; /* Index into aC1[] and aC2[] */ int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ int limit1, limit2; /* Sizes of aC1[] and aC2[] */ int nConflict = 0; /* Number of merge conflicts seen so far */ int useCrLf = 0; int ln1, ln2, lnPivot; /* Line numbers for all files */ DiffConfig DCfg; blob_zero(pOut); /* Merge results stored in pOut */ /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM), ** keep it in the output. This should be secure enough not to cause ** unintended changes to the merged file and consistent with what ** users are using in their source files. */ if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){ blob_append(pOut, (char*)get_utf8_bom(0), -1); } /* Check once to see if both pV1 and pV2 contains CR/LF endings. ** If true, CR/LF pair will be used later to append the ** boundary markers for merge conflicts. */ if( contains_crlf(pV1) && contains_crlf(pV2) ){ useCrLf = 1; } /* Compute the edits that occur from pPivot => pV1 (into aC1) ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is ** an array of integer triples. Within each triple, the first integer ** is the number of lines of text to copy directly from the pivot, ** the second integer is the number of lines of text to omit from the ** pivot, and the third integer is the number of lines of text that are ** inserted. The edit array ends with a triple of 0,0,0. */ diff_config_init(&DCfg, 0); aC1 = text_diff(pPivot, pV1, 0, &DCfg); aC2 = text_diff(pPivot, pV2, 0, &DCfg); if( aC1==0 || aC2==0 ){ free(aC1); free(aC2); return -1; } blob_rewind(pV1); /* Rewind inputs: Needed to reconstruct output */ |
︙ | ︙ | |||
186 187 188 189 190 191 192 | limit2 = i2; DEBUG( for(i1=0; i1<limit1; i1+=3){ printf("c1: %4d %4d %4d\n", aC1[i1], aC1[i1+1], aC1[i1+2]); } for(i2=0; i2<limit2; i2+=3){ | | > | | | | | | | | | | | | | | > | | > > > > | | < > | | 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 | limit2 = i2; DEBUG( for(i1=0; i1<limit1; i1+=3){ printf("c1: %4d %4d %4d\n", aC1[i1], aC1[i1+1], aC1[i1+2]); } for(i2=0; i2<limit2; i2+=3){ printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]); } ) /* Loop over the two edit vectors and use them to compute merged text ** which is written into pOut. i1 and i2 are multiples of 3 which are ** indices into aC1[] and aC2[] to the edit triple currently being ** processed */ i1 = i2 = 0; ln1 = ln2 = lnPivot = 1; while( i1<limit1 && i2<limit2 ){ DEBUG( printf("%d: %2d %2d %2d %d: %2d %2d %2d\n", i1/3, aC1[i1], aC1[i1+1], aC1[i1+2], i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); ) if( aC1[i1]>0 && aC2[i2]>0 ){ /* Output text that is unchanged in both V1 and V2 */ nCpy = min(aC1[i1], aC2[i2]); DEBUG( printf("COPY %d\n", nCpy); ) blob_copy_lines(pOut, pPivot, nCpy); lnPivot += nCpy; blob_copy_lines(0, pV1, nCpy); ln1 += nCpy; blob_copy_lines(0, pV2, nCpy); ln2 += nCpy; aC1[i1] -= nCpy; aC2[i2] -= nCpy; }else if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){ /* Output edits to V2 that occurs within unchanged regions of V1 */ nDel = aC2[i2+1]; nIns = aC2[i2+2]; DEBUG( printf("EDIT -%d+%d left\n", nDel, nIns); ) blob_copy_lines(0, pPivot, nDel); lnPivot += nDel; blob_copy_lines(0, pV1, nDel); ln1 += nDel; blob_copy_lines(pOut, pV2, nIns); ln2 += nIns; aC1[i1] -= nDel; i2 += 3; }else if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){ /* Output edits to V1 that occur within unchanged regions of V2 */ nDel = aC1[i1+1]; nIns = aC1[i1+2]; DEBUG( printf("EDIT -%d+%d right\n", nDel, nIns); ) blob_copy_lines(0, pPivot, nDel); lnPivot += nDel; blob_copy_lines(0, pV2, nDel); ln2 += nDel; blob_copy_lines(pOut, pV1, nIns); ln1 += nIns; aC2[i2] -= nDel; i1 += 3; }else if( sameEdit(&aC1[i1], &aC2[i2], pV1, pV2) ){ /* Output edits that are identical in both V1 and V2. */ assert( aC1[i1]==0 ); nDel = aC1[i1+1]; nIns = aC1[i1+2]; DEBUG( printf("EDIT -%d+%d both\n", nDel, nIns); ) blob_copy_lines(0, pPivot, nDel); lnPivot += nDel; blob_copy_lines(pOut, pV1, nIns); ln1 += nIns; blob_copy_lines(0, pV2, nIns); ln2 += nIns; i1 += 3; i2 += 3; }else { /* We have found a region where different edits to V1 and V2 overlap. ** This is a merge conflict. Find the size of the conflict, then ** output both possible edits separated by distinctive marks. */ 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); ) append_merge_mark(pOut, 0, ln1, useCrLf); i1 = output_one_side(pOut, pV1, aC1, i1, sz, &ln1); append_merge_mark(pOut, 1, lnPivot, useCrLf); blob_copy_lines(pOut, pPivot, sz); lnPivot += sz; append_merge_mark(pOut, 2, ln2, useCrLf); i2 = output_one_side(pOut, pV2, aC2, i2, sz, &ln2); append_merge_mark(pOut, 3, -1, useCrLf); } /* 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; } |
︙ | ︙ | |||
292 293 294 295 296 297 298 | free(aC1); free(aC2); return nConflict; } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > | > > > > > > > > > > > > > > > > | < | | < | | < | | < | | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | free(aC1); free(aC2); return nConflict; } /* ** Return true if the input string contains a merge marker on a line by ** itself. */ int contains_merge_marker(Blob *p){ int i, j; int len = (int)strlen(mergeMarker[0]); const char *z = blob_buffer(p); int n = blob_size(p) - len + 1; assert( len==(int)strlen(mergeMarker[1]) ); assert( len==(int)strlen(mergeMarker[2]) ); assert( len==(int)strlen(mergeMarker[3]) ); assert( count(mergeMarker)==4 ); for(i=0; i<n; ){ for(j=0; j<4; j++){ if( (memcmp(&z[i], mergeMarker[j], len)==0) ){ return 1; } } while( i<n && z[i]!='\n' ){ i++; } while( i<n && (z[i]=='\n' || z[i]=='\r') ){ i++; } } return 0; } /* ** Return true if the named file contains an unresolved merge marker line. */ int file_contains_merge_marker(const char *zFullpath){ Blob file; int rc; blob_read_from_file(&file, zFullpath, ExtFILE); rc = contains_merge_marker(&file); blob_reset(&file); return rc; } /* ** COMMAND: 3-way-merge* ** ** Usage: %fossil 3-way-merge BASELINE V1 V2 MERGED ** ** Inputs are files BASELINE, V1, and V2. The file MERGED is generated ** as output. ** ** BASELINE is a common ancestor of two files V1 and V2 that have diverging ** edits. The generated output file MERGED is the combination of all ** changes in both V1 and V2. ** ** This command has no effect on the Fossil repository. It is a utility ** command made available for the convenience of users. This command can ** be used, for example, to help import changes from an upstream project. ** ** Suppose an upstream project has a file named "Xup.c" which is imported ** with modifications to the local project as "Xlocal.c". Suppose further ** 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; int nConflict; /* 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], ExtFILE)<0 ){ fossil_fatal("cannot read %s", g.argv[2]); } if( blob_read_from_file(&v1, g.argv[3], ExtFILE)<0 ){ fossil_fatal("cannot read %s", g.argv[3]); } if( blob_read_from_file(&v2, g.argv[4], ExtFILE)<0 ){ fossil_fatal("cannot read %s", g.argv[4]); } nConflict = blob_merge(&pivot, &v1, &v2, &merged); if( blob_write_to_file(&merged, g.argv[5])<(int)blob_size(&merged) ){ fossil_fatal("cannot write %s", g.argv[4]); } blob_reset(&pivot); blob_reset(&v1); blob_reset(&v2); blob_reset(&merged); if( nConflict>0 ) fossil_warning("WARNING: %d merge conflicts", nConflict); } /* ** aSubst is an array of string pairs. The first element of each pair is ** a string that begins with %. The second element is a replacement for that ** string. ** ** This routine makes a copy of zInput into memory obtained from malloc and ** performance all applicable substitutions on that string. */ char *string_subst(const char *zInput, int nSubst, const char **azSubst){ Blob x; int i, j; blob_zero(&x); while( zInput[0] ){ for(i=0; zInput[i] && zInput[i]!='%'; i++){} if( i>0 ){ blob_append(&x, zInput, i); zInput += i; } if( zInput[0]==0 ) break; for(j=0; j<nSubst; j+=2){ int n = strlen(azSubst[j]); if( strncmp(zInput, azSubst[j], n)==0 ){ blob_append(&x, azSubst[j+1], -1); zInput += n; break; } } if( j>=nSubst ){ blob_append(&x, "%", 1); zInput++; } } return blob_str(&x); } #if INTERFACE /* ** Flags to the 3-way merger */ #define MERGE_DRYRUN 0x0001 /* ** The MERGE_KEEP_FILES flag specifies that merge_3way() should retain ** its temporary files on error. By default they are removed after the ** merge, regardless of success or failure. */ #define MERGE_KEEP_FILES 0x0002 #endif /* ** This routine is a wrapper around blob_merge() with the following ** enhancements: ** ** (1) If the merge-command is defined, then use the external merging ** program specified instead of the built-in blob-merge to do the ** merging. Panic if the external merger fails. ** ** Not currently implemented ** ** ** (2) If gmerge-command is defined and there are merge conflicts in ** blob_merge() then invoke the external graphical merger to resolve ** the conflicts. ** ** (3) If a merge conflict occurs and gmerge-command is not defined, ** then write the pivot, original, and merge-in files to the ** filesystem. */ int merge_3way( Blob *pPivot, /* Common ancestor (older) */ const char *zV1, /* Name of file for version merging into (mine) */ Blob *pV2, /* Version merging from (yours) */ Blob *pOut, /* Output written here */ unsigned mergeFlags /* Flags that control operation */ ){ Blob v1; /* Content of zV1 */ int rc; /* Return code of subroutines and this routine */ const char *zGMerge; /* Name of the gmerge command */ blob_read_from_file(&v1, zV1, ExtFILE); rc = blob_merge(pPivot, &v1, pV2, pOut); zGMerge = rc<=0 ? 0 : db_get("gmerge-command", 0); if( (mergeFlags & MERGE_DRYRUN)==0 && ((zGMerge!=0 && zGMerge[0]!=0) || (rc!=0 && (mergeFlags & MERGE_KEEP_FILES)!=0)) ){ char *zPivot; /* Name of the pivot file */ char *zOrig; /* Name of the original content file */ char *zOther; /* Name of the merge file */ zPivot = file_newname(zV1, "baseline", 1); blob_write_to_file(pPivot, zPivot); zOrig = file_newname(zV1, "original", 1); blob_write_to_file(&v1, zOrig); zOther = file_newname(zV1, "merge", 1); blob_write_to_file(pV2, zOther); if( rc>0 ){ if( zGMerge && zGMerge[0] ){ char *zOut; /* Temporary output file */ char *zCmd; /* Command to invoke */ const char *azSubst[8]; /* Strings to be substituted */ zOut = file_newname(zV1, "output", 1); azSubst[0] = "%baseline"; azSubst[1] = zPivot; azSubst[2] = "%original"; azSubst[3] = zOrig; azSubst[4] = "%merge"; azSubst[5] = zOther; azSubst[6] = "%output"; azSubst[7] = zOut; zCmd = string_subst(zGMerge, 8, azSubst); printf("%s\n", zCmd); fflush(stdout); fossil_system(zCmd); if( file_size(zOut, RepoFILE)>=0 ){ blob_read_from_file(pOut, zOut, ExtFILE); file_delete(zOut); } fossil_free(zCmd); fossil_free(zOut); } } if( (mergeFlags & MERGE_KEEP_FILES)==0 ){ file_delete(zPivot); file_delete(zOrig); file_delete(zOther); } fossil_free(zPivot); fossil_free(zOrig); fossil_free(zOther); } blob_reset(&v1); return rc; } |
Deleted src/mkindex.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added src/moderate.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 | /* ** Copyright (c) 2012 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 deal with moderator actions for ** Wiki and Tickets. */ #include "config.h" #include "moderate.h" #include <assert.h> /* ** 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 repository.modreq(\n" " objid INTEGER PRIMARY KEY,\n" /* Record pending approval */ " attachRid INT,\n" /* Object attached */ " tktid TEXT\n" /* Associated ticket id */ ");\n" ); } /* ** 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; } /* ** If the rid object is being held for moderation, write out ** an "awaiting moderation" message and return true. ** ** If the object is not being held for moderation, simply return ** false without generating any output. */ int moderation_pending_www(int rid){ int pending = moderation_pending(rid); if( pending ){ @ <span class="modpending">(Awaiting Moderator Approval)</span> } return pending; } /* ** Return TRUE if there any pending moderation requests. */ int moderation_needed(void){ if( !moderation_table_exists() ) return 0; return db_exists("SELECT 1 FROM modreq"); } /* ** 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<count(aTabField); 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 */ void moderation_disapprove(int objid){ Stmt q; char *zTktid; int attachRid = 0; int rid; if( !moderation_pending(objid) ) return; db_begin_transaction(); rid = objid; while( rid && content_is_private(rid) ){ db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid); while( db_step(&q)==SQLITE_ROW ){ int ridUser = db_column_int(&q, 0); content_undelta(ridUser); } db_finalize(&q); db_multi_exec( "DELETE FROM blob WHERE rid=%d;" "DELETE FROM delta WHERE rid=%d;" "DELETE FROM event WHERE objid=%d;" "DELETE FROM tagxref WHERE rid=%d;" "DELETE FROM private WHERE rid=%d;" "DELETE FROM attachment WHERE attachid=%d;", rid, rid, rid, rid, rid, rid ); if( db_table_exists("repository","forumpost") ){ db_multi_exec("DELETE FROM forumpost WHERE fpid=%d", rid); } zTktid = db_text(0, "SELECT tktid FROM modreq WHERE objid=%d", rid); if( zTktid && zTktid[0] ){ ticket_rebuild_entry(zTktid); 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(char class, 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 %c-%d.", class, rid); if( class!='a' ) search_doc_touch(class, rid, 0); setup_incr_cfgcnt(); 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.ModWiki && !g.perm.ModTkt && !g.perm.ModForum ){ login_needed(g.anon.ModWiki && g.anon.ModTkt && g.anon.ModForum); 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, 0, 0); db_finalize(&q); } style_finish_page(); } /* ** Disapproves any entries in the modreq table which belong to any ** user whose name is no longer found in the user table. This is only ** intended to be called after user deletion via /setup_uedit. ** ** To figure out whether a name exists it cross-references ** coalesce(event.euser, event.user) with user.login, limiting the ** selection to event entries where objid matches an entry in the ** modreq table. ** ** This is a no-op if called without g.perm.Admin permissions or if ** moderation_table_exists() returns false. */ void moderation_disapprove_for_missing_users(){ Stmt q; if( !g.perm.Admin || !moderation_table_exists() ){ return; } db_begin_transaction(); db_prepare(&q, "SELECT objid FROM event WHERE objid IN " "(SELECT objid FROM modreq) " "AND coalesce(euser,user) NOT IN " "(SELECT login FROM user)" ); while( db_step(&q)==SQLITE_ROW ){ int const objid = db_column_int(&q, 0); moderation_disapprove(objid); } db_finalize(&q); setup_incr_cfgcnt(); db_end_transaction(0); } |
Changes to src/name.c.
1 2 3 4 5 6 | /* ** 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".) | | | < < < < > > > > > > > > > > > | < > > > > | | > > > > > > > > > > > > > | > > | | > > | | > | > > > > > > | > | | | | | | | | | | | > | > > > > | | > | > > > | > > > > > > | > > > > > > | > > > | | > > > | > > > > > > > > > | > > | > > | > | | < > > > | < < | < > > | < > | | | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | > > > | < > > > | < | | < > | < | < | | | < > > > > > > > > > > > > > > | > > > > > > > > > > > > > | | > > > > > > > > > | < < < | < < < < | > > > > > > > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > < > > > > > | > | | < < | < < > > > > > | < < > > > > > > > > > > > > > > > > > > | > > > > > | > > | > > | > > > > | | > > > > > > > > > | > > > > | < > | > > > > | > > < > > | > > > > | < > > > > > > > > > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > | | | < < < | < | > | < < | < < | > | > > > > > > > > > > > > > > > > > | | > > | | > > > > > | > | | < | > > > > | > > > | | | | < | | > > > > > > > > | | > | > | | > | > > > > > | > > > | > | > > > | | | > > > | > > > > > > > > > | > > > > > > > > > > > > > > > | | < < < | | | | > > > > > > | | < < > > | > | | | > | > > > > > > > > > | | | < < < < | | < > | < < < < < < > > | | | | < > > > | | > > > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > | | | | | | | | | < < > | > > | < < | > > > > > > > > | < | > > > > > > | < < < < < < < < | | > > | < < < < < | > > > > > > > > > > > > > > > > > > | | > > | > > > > > > > > | | | > > > | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | > | > < | | < < | | < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** 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 contains code used to resolved user-supplied object names. */ #include "config.h" #include "name.h" #include <assert.h> #if INTERFACE /* ** An upper boundary on RIDs, provided in order to be able to ** distinguish real RID values from RID_CKOUT and any future ** RID_... values. */ #define RID_MAX 0x7ffffff0 /* ** A "magic" RID representing the current checkout in some contexts. */ #define RID_CKOUT (RID_MAX+1) #endif /* ** Return TRUE if the string begins with something that looks roughly ** like an ISO date/time string. The SQLite date/time functions will ** have the final say-so about whether or not the date/time string is ** well-formed. */ int fossil_isdate(const char *z){ if( !fossil_isdigit(z[0]) ) return 0; if( !fossil_isdigit(z[1]) ) return 0; if( !fossil_isdigit(z[2]) ) return 0; if( !fossil_isdigit(z[3]) ) return 0; if( z[4]!='-') return 0; 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; } /* ** Check to see if the string might be a compact date/time that omits ** the punctuation. Example: "20190327084549" instead of ** "2019-03-27 08:45:49". If the string is of the appropriate form, ** then return an alternative string (in static space) that is the same ** string with punctuation inserted. ** ** If the bVerifyNotAHash flag is true, then a check is made to see if ** the string is a hash prefix and NULL is returned if it is. If the ** bVerifyNotAHash flag is false, then the result is determined by syntax ** of the input string only, without reference to the artifact table. */ char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){ static char zEDate[20]; static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' }; int n = (int)strlen(zIn); int i, j; /* Only three forms allowed: ** (1) YYYYMMDD ** (2) YYYYMMDDHHMM ** (3) YYYYMMDDHHMMSS */ if( n!=8 && n!=12 && n!=14 ) return 0; /* Every character must be a digit */ for(i=0; fossil_isdigit(zIn[i]); i++){} if( i!=n ) return 0; /* Expand the date */ for(i=j=0; zIn[i]; i++){ if( i>=4 && (i%2)==0 ){ zEDate[j++] = aPunct[i/2]; } zEDate[j++] = zIn[i]; } zEDate[j] = 0; /* Check for reasonable date values. ** Offset references: ** YYYY-MM-DD HH:MM:SS ** 0123456789 12345678 */ i = atoi(zEDate); if( i<1970 || i>2100 ) return 0; i = atoi(zEDate+5); if( i<1 || i>12 ) return 0; i = atoi(zEDate+8); if( i<1 || i>31 ) return 0; if( n>8 ){ i = atoi(zEDate+11); if( i>24 ) return 0; i = atoi(zEDate+14); if( i>60 ) return 0; if( n==14 && atoi(zEDate+17)>60 ) return 0; } /* The string is not also a hash prefix */ if( bVerifyNotAHash ){ if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'",zIn) ) return 0; } /* It looks like this may be a date. Return it with punctuation added. */ return zEDate; } /* ** The data-time string in the argument is going to be used as an ** upper bound like this: mtime<=julianday(zDate,'localtime'). ** But if the zDate parameter omits the fractional seconds or the ** seconds, or the time, that might mess up the == part of the ** comparison. So add in missing factional seconds or seconds or time. ** ** The returned string is held in a static buffer that is overwritten ** with each call, or else is just a copy of its input if there are ** no changes. */ const char *fossil_roundup_date(const char *zDate){ static char zUp[24]; int n = (int)strlen(zDate); if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */ memcpy(zUp, zDate, 19); memcpy(zUp+19, ".999", 5); return zUp; } if( n==16 ){ /* YYYY-MM-DD HH:MM */ memcpy(zUp, zDate, 16); memcpy(zUp+16, ":59.999", 8); return zUp; } if( n==10 ){ /* YYYY-MM-DD */ memcpy(zUp, zDate, 10); memcpy(zUp+10, " 23:59:59.999", 14); return zUp; } return zDate; } /* ** Return the RID that is the "root" of the branch that contains ** check-in "rid". Details depending on eType: ** ** eType==0 The check-in of the parent branch off of which ** the branch containing RID originally diverged. ** ** eType==1 The first check-in of the branch that contains RID. ** ** eType==2 The youngest ancestor of RID that is on the branch ** from which the branch containing RID diverged. */ int start_of_branch(int rid, int eType){ Stmt q; int rc; int ans = rid; char *zBr = branch_of_rid(rid); db_prepare(&q, "WITH RECURSIVE" " par(pid, ex, cnt) as (" " SELECT pid, EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0" " AND value=%Q AND rid=plink.pid), 1" " FROM plink WHERE cid=%d AND isprim" " UNION ALL " " SELECT plink.pid, EXISTS(SELECT 1 FROM tagxref " " WHERE tagid=%d AND tagtype>0" " AND value=%Q AND rid=plink.pid)," " 1+par.cnt" " FROM plink, par" " WHERE cid=par.pid AND isprim AND par.ex " " LIMIT 100000 " " )" " SELECT pid FROM par WHERE ex>=%d ORDER BY cnt DESC LIMIT 1", TAG_BRANCH, zBr, ans, TAG_BRANCH, zBr, eType%2 ); fossil_free(zBr); rc = db_step(&q); if( rc==SQLITE_ROW ){ ans = db_column_int(&q, 0); } db_finalize(&q); if( eType==2 && ans>0 ){ zBr = branch_of_rid(ans); ans = compute_youngest_ancestor_in_branch(rid, zBr); fossil_free(zBr); } return ans; } /* ** Find the RID of the most recent object with symbolic tag zTag ** and having a type that matches zType. ** ** Return 0 if there are no matches. ** ** This is a tricky query to do efficiently. ** If the tag is very common (ex: "trunk") then ** we want to use the query identified below as Q1 - which searches ** the most recent EVENT table entries for the most recent with the tag. ** But if the tag is relatively scarce (anything other than "trunk", basically) ** then we want to do the indexed search show below as Q2. */ static int most_recent_event_with_tag(const char *zTag, const char *zType){ return db_int(0, "SELECT objid FROM (" /* Q1: Begin by looking for the tag in the 30 most recent events */ "SELECT objid" " FROM (SELECT * FROM event ORDER BY mtime DESC LIMIT 30) AS ex" " WHERE type GLOB '%q'" " AND EXISTS(SELECT 1 FROM tagxref, tag" " WHERE tag.tagname='sym-%q'" " AND tagxref.tagid=tag.tagid" " AND tagxref.tagtype>0" " AND tagxref.rid=ex.objid)" " ORDER BY mtime DESC LIMIT 1" ") UNION ALL SELECT * FROM (" /* Q2: If the tag is not found in the 30 most recent events, then using ** the tagxref table to index for the tag */ "SELECT event.objid" " 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'" " ORDER BY event.mtime DESC LIMIT 1" ") LIMIT 1;", zType, zTag, zTag, zType ); } /* ** Find the RID for a check-in that is the most recent check-in with ** tag zTag that occurs on or prior to rDate. ** ** See also the performance note on most_recent_event_with_tag() which ** applies to this routine too. */ int last_checkin_with_tag_before_date(const char *zTag, double rLimit){ Stmt s; int rid = 0; if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4; db_prepare(&s, "SELECT objid FROM (" /* Q1: Begin by looking for the tag in the 30 most recent events */ "SELECT objid" " FROM (SELECT * FROM event WHERE mtime<=:datelimit" " ORDER BY mtime DESC LIMIT 30) AS ex" " WHERE type='ci'" " AND EXISTS(SELECT 1 FROM tagxref, tag" " WHERE tag.tagname='sym-%q'" " AND tagxref.tagid=tag.tagid" " AND tagxref.tagtype>0" " AND tagxref.rid=ex.objid)" " ORDER BY mtime DESC LIMIT 1" ") UNION ALL SELECT * FROM (" /* Q2: If the tag is not found in the 30 most recent events, then using ** the tagxref table to index for the tag */ "SELECT event.objid" " 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='ci'" " AND event.mtime<=:datelimit" " ORDER BY event.mtime DESC LIMIT 1" ") LIMIT 1;", zTag, zTag ); db_bind_double(&s, ":datelimit", rLimit); if( db_step(&s)==SQLITE_ROW ){ rid = db_column_int(&s,0); } db_finalize(&s); return rid; } /* ** Find the RID of the first check-in (chronologically) after rStart that ** has tag zTag. ** ** See also the performance note on most_recent_event_with_tag() which ** applies to this routine too. */ int first_checkin_with_tag_after_date(const char *zTag, double rStart){ Stmt s; int rid = 0; if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4; db_prepare(&s, "SELECT objid FROM (" /* Q1: Begin by looking for the tag in the 30 most recent events */ "SELECT objid" " FROM (SELECT * FROM event WHERE mtime>=:startdate" " ORDER BY mtime LIMIT 30) AS ex" " WHERE type='ci'" " AND EXISTS(SELECT 1 FROM tagxref, tag" " WHERE tag.tagname='sym-%q'" " AND tagxref.tagid=tag.tagid" " AND tagxref.tagtype>0" " AND tagxref.rid=ex.objid)" " ORDER BY mtime LIMIT 1" ") UNION ALL SELECT * FROM (" /* Q2: If the tag is not found in the 30 most recent events, then using ** the tagxref table to index for the tag */ "SELECT event.objid" " 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='ci'" " AND event.mtime>=:startdate" " ORDER BY event.mtime LIMIT 1" ") LIMIT 1;", zTag, zTag ); db_bind_double(&s, ":startdate", rStart); if( db_step(&s)==SQLITE_ROW ){ rid = db_column_int(&s,0); } db_finalize(&s); return rid; } /* ** Return true if character "c" is a character that might have been ** accidentally appended to the end of a URL. */ static int is_trailing_punct(char c){ return c=='.' || c=='_' || c==')' || c=='>' || c=='!' || c=='?' || c==','; } /* ** Convert a symbolic name into a RID. Acceptable forms: ** ** * artifact hash (optionally enclosed in [...]) ** * 4-character or larger prefix of an artifact ** * 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" ** ** The following modifier prefixes may be applied to the above forms: ** ** * "root:BR" = The origin of the branch named BR. ** * "start:BR" = The first check-in of the branch named BR. ** * "merge-in:BR" = The most recent merge-in for the branch named BR. ** ** In those forms, BR may be any symbolic form but is assumed to be a ** check-in. Thus root:2021-02-01 would resolve to a check-in, possibly ** in a branch and possibly in the trunk, but never a wiki edit or ** forum post. ** ** 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, f. ** 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. A value of "ci+" works like "ci" but adds these ** semantics: if zTag is "ckout" and a checkout is open, "ci+" causes ** RID_CKOUT to be returned, in which case g.localOpen will hold the ** RID of the checkout. Conversely, passing in the hash, or another ** symbolic name of the local checkout version, will always result in ** its RID being returned. ** ** Note that the input zTag for types "t" and "e" is the artifact hash of ** the ticket-change or technote-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 rid = 0; int ridCkout = 0; int nTag; int i; int startOfBranch = 0; const char *zXTag; /* zTag with optional [...] removed */ int nXTag; /* Size of zXTag */ const char *zDate; /* Expanded date-time string */ int isCheckin = 0; /* zType==ci = 1, zType==ci+ = 2 */ if( zType==0 || zType[0]==0 ){ zType = "*"; }else if( zType[0]=='b' ){ zType = "ci"; startOfBranch = 1; } if( zTag==0 || zTag[0]==0 ) return 0; else if( 'c'==zType[0] ){ if( fossil_strcmp(zType,"ci")==0 ){ isCheckin = 1; }else if( fossil_strcmp(zType,"ci+")==0 ){ isCheckin = 2; zType = "ci"; } } /* special keyword: "tip" */ if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || isCheckin!=0) ){ rid = db_int(0, "SELECT objid" " FROM event" " WHERE type='ci'" " ORDER BY event.mtime DESC" ); if( rid ) return rid; } if( g.localOpen ) { ridCkout = db_lget_int("checkout",0); } /* special keywords: "prev", "previous", "current", "ckout", and ** "next" */ if( (zType[0]=='*' || isCheckin!=0) && 0<ridCkout ){ if( fossil_strcmp(zTag, "current")==0 ){ rid = ridCkout; }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", ridCkout); }else if( fossil_strcmp(zTag, "next")==0 ){ rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d" " ORDER BY isprim DESC, mtime DESC", ridCkout); }else if( isCheckin>1 && fossil_strcmp(zTag, "ckout")==0 ){ rid = RID_CKOUT; } if( rid ) return rid; } /* Date and times */ if( memcmp(zTag, "date:", 5)==0 ){ zDate = fossil_expand_datetime(&zTag[5],0); if( zDate==0 ) zDate = &zTag[5]; rid = db_int(0, "SELECT objid FROM event" " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" " ORDER BY mtime DESC LIMIT 1", fossil_roundup_date(zDate), zType); return rid; } if( fossil_isdate(zTag) ){ rid = db_int(0, "SELECT objid FROM event" " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" " ORDER BY mtime DESC LIMIT 1", fossil_roundup_date(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", fossil_roundup_date(&zTag[4]), zType); return rid; } /* "tag:" + symbolic-name */ if( memcmp(zTag, "tag:", 4)==0 ){ rid = most_recent_event_with_tag(&zTag[4], zType); if( startOfBranch ) rid = start_of_branch(rid,1); return rid; } /* root:BR -> The origin of the branch named BR */ if( strncmp(zTag, "root:", 5)==0 ){ rid = symbolic_name_to_rid(zTag+5, zType); return start_of_branch(rid, 0); } /* start:BR -> The first check-in on branch named BR */ if( strncmp(zTag, "start:", 6)==0 ){ rid = symbolic_name_to_rid(zTag+6, zType); return start_of_branch(rid, 1); } /* merge-in:BR -> Most recent merge-in for the branch named BR */ if( strncmp(zTag, "merge-in:", 9)==0 ){ rid = symbolic_name_to_rid(zTag+9, zType); return start_of_branch(rid, 2); } /* symbolic-name ":" date-time */ nTag = strlen(zTag); for(i=0; i<nTag-8 && zTag[i]!=':'; i++){} if( zTag[i]==':' && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0) ){ char *zDate = mprintf("%s", &zTag[i+1]); char *zTagBase = mprintf("%.*s", i, zTag); char *zXDate; int nDate = strlen(zDate); if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ zDate[nDate-3] = 'z'; zDate[nDate-2] = 0; } zXDate = fossil_expand_datetime(zDate,0); if( zXDate==0 ) zXDate = zDate; 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.mtime<=julianday(%Q,fromLocal())" " AND event.type GLOB '%q'", zTagBase, fossil_roundup_date(zXDate), zType ); fossil_free(zDate); fossil_free(zTagBase); return rid; } /* Remove optional [...] */ zXTag = zTag; nXTag = nTag; if( zXTag[0]=='[' ){ zXTag++; nXTag--; } if( nXTag>0 && zXTag[nXTag-1]==']' ){ nXTag--; } /* artifact hash or prefix */ if( nXTag>=4 && nXTag<=HNAME_MAX && validate16(zXTag, nXTag) ){ Stmt q; char zUuid[HNAME_MAX+1]; memcpy(zUuid, zXTag, nXTag); zUuid[nXTag] = 0; canonical16(zUuid, nXTag); 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 CROSS JOIN 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); if( db_step(&q)==SQLITE_ROW ) rid = -1; } db_finalize(&q); if( rid ) return rid; } if( zType[0]=='w' ){ rid = db_int(0, "SELECT event.objid, max(event.mtime)" " FROM tag, tagxref, event" " WHERE tag.tagname='wiki-%q' " " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " " AND event.objid=tagxref.rid " " AND event.type GLOB '%q'", zTag, zType ); }else{ rid = most_recent_event_with_tag(zTag, zType); } if( rid>0 ){ if( startOfBranch ) rid = start_of_branch(rid,1); return rid; } /* Pure numeric date/time */ zDate = fossil_expand_datetime(zTag, 0); if( zDate ){ rid = db_int(0, "SELECT objid FROM event" " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" " ORDER BY mtime DESC LIMIT 1", fossil_roundup_date(zDate), zType); if( rid) 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; } /* If nothing matches and the name ends with punctuation, ** then the name might have originated from a URL in plain text ** that was incorrectly extracted from the text. Try to remove ** the extra punctuation and rerun the match. */ if( nTag>4 && is_trailing_punct(zTag[nTag-1]) && !is_trailing_punct(zTag[nTag-2]) ){ char *zNew = fossil_strndup(zTag, nTag-1); rid = symbolic_name_to_rid(zNew,zType); fossil_free(zNew); }else if( nTag>5 && is_trailing_punct(zTag[nTag-1]) && is_trailing_punct(zTag[nTag-2]) && !is_trailing_punct(zTag[nTag-3]) ){ char *zNew = fossil_strndup(zTag, nTag-2); rid = symbolic_name_to_rid(zNew,zType); fossil_free(zNew); } return rid; } /* ** This routine takes a user-entered string and tries to convert it to ** an artifact hash. ** ** We first try to treat the string as an artifact hash, or at least a ** unique prefix of an artifact hash. The input may be in mixed case. ** If we are passed such a string, this routine has the effect of ** converting the hash [prefix] to canonical form. ** ** If the input is not a hash or a hash prefix, then try to resolve ** the name as a tag. If multiple tags match, pick the latest. ** A caller can force this routine to skip the hash case above by ** prefixing the string with "tag:", a useful property when the tag ** may be misinterpreted as a hex ASCII string. (e.g. "decade" or "facade") ** ** 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); if( rid<0 ){ fossil_error(iErrPriority, "ambiguous name: %s", zName); return 2; }else if( rid==0 ){ fossil_error(iErrPriority, "cannot resolve name: %s", zName); return 1; }else{ blob_reset(pName); db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid); return 0; } } /* ** This routine is similar to name_to_uuid() except in the form it ** takes its parameters and returns its value, and in that it does not ** treat errors as fatal. zName must be an artifact hash or prefix of ** a hash. zType is also as described for name_to_uuid(). 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 a newly-allocated string, ** the full hash, 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 = (rid<RID_MAX) ? db_text(NULL, "SELECT uuid FROM blob WHERE rid=%d", rid) : NULL; } return rid; } /* ** name_collisions searches through events, blobs, and tickets for ** collisions of a given hash based on its length, counting only ** hashes greater than or equal to 4 hex ASCII characters (16 bits) ** 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<=HNAME_MAX && 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 ** ** Usage: %fossil test-name-to-id [--count N] [--type ARTIFACT_TYPE] NAME ** ** Convert a NAME to a full artifact ID. Repeat the conversion N ** times (for timing purposes) if the --count option is given. */ void test_name_to_id(void){ int i; int n = 0; Blob name; const char *zType; db_must_be_within_tree(); if( (zType = find_option("type","t",1))==0 ){ zType = "*"; } for(i=2; i<g.argc; i++){ if( strcmp(g.argv[i],"--count")==0 && i+1<g.argc ){ i++; n = atoi(g.argv[i]); continue; } do{ blob_init(&name, g.argv[i], -1); fossil_print("%s -> ", g.argv[i]); if( name_to_uuid(&name, 1, zType) ){ fossil_print("ERROR: %s\n", g.zErrMsg); fossil_error_reset(); }else{ fossil_print("%s\n", blob_buffer(&name)); } blob_reset(&name); }while( n-- > 0 ); } } /* ** Convert a name to a rid. If the name can be any of the various forms ** accepted: ** ** * artifact hash or prefix thereof ** * symbolic name ** * date ** * label:date ** * prev, previous ** * next ** * tip ** ** This routine is used by command-line routines to resolve command-line inputs ** into a rid. */ 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("cannot resolve name: %s", zName); } return rid; } int name_to_rid(const char *zName){ return name_to_typed_rid(zName, "*"); } /* ** Try to resolve zQP1 into a check-in name. If zQP1 does not exist, ** return 0. If zQP1 exists but cannot be resolved, then also try to ** resolve zQP2 if it exists. If zQP1 cannot be resolved but zQP2 does ** not exist, then raise an error. If both zQP1 and zQP2 exists but ** neither can be resolved, also raise an error. ** ** If pzPick is not a NULL pointer, then *pzPick to be the value of whichever ** query parameter ended up being used. */ int name_choice(const char *zQP1, const char *zQP2, const char **pzPick){ const char *zName, *zName2; int rid; zName = P(zQP1); if( zName==0 || zName[0]==0 ) return 0; rid = symbolic_name_to_rid(zName, "ci"); if( rid>0 ){ if( pzPick ) *pzPick = zName; return rid; } if( rid<0 ){ fossil_fatal("ambiguous name: %s", zName); } zName2 = P(zQP2); if( zName2==0 || zName2[0]==0 ){ fossil_fatal("cannot resolve name: %s", zName); } if( pzPick ) *pzPick = zName2; return name_to_typed_rid(zName2, "ci"); } /* ** WEBPAGE: ambiguous ** URL: /ambiguous?name=NAME&src=WEBPAGE ** ** The NAME given by the name parameter is ambiguous. Display a page ** that shows all possible choices and let the user select between them. ** ** The src= query parameter is optional. If omitted it defaults ** to "info". */ void ambiguous_page(void){ Stmt q; const char *zName = P("name"); const char *zSrc = PD("src","info"); char *z; if( zName==0 || zName[0]==0 || zSrc==0 || zSrc[0]==0 ){ fossil_redirect_home(); } style_header("Ambiguous Artifact ID"); @ <p>The artifact hash prefix <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, 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_version(zUuid); @ - %h(zTitle). @ <ul><li> object_description(rid, 0, 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, 0); @ </li></ul> @ </p></li> } @ </ol> db_finalize(&q); style_finish_page(); } /* ** 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 ** shows all possibilities and do not return. */ int name_to_rid_www(const char *zParamName){ int rid; const char *zName = P(zParamName); #ifdef FOSSIL_ENABLE_JSON if(!zName && fossil_has_json()){ zName = json_find_option_cstr(zParamName,NULL,NULL); } #endif if( zName==0 || zName[0]==0 ) return 0; rid = symbolic_name_to_rid(zName, "*"); if( rid<0 ){ cgi_redirectf("%R/ambiguous/%T?src=%t", zName, g.zPath); rid = 0; } return rid; } /* ** Given an RID of a structural artifact, which is assumed to be ** valid, this function returns one of the CFTYPE_xxx values ** describing the record type, or 0 if the RID does not refer to an ** artifact record (as determined by reading the event table). ** ** Note that this function never returns CFTYPE_ATTACHMENT or ** CFTYPE_CLUSTER because those are not used in the event table. Thus ** it cannot be used to distinguish those artifact types from ** non-artifact file content. ** ** Potential TODO: if the rid is not found in the timeline, try to ** match it to a client file and return a hypothetical new ** CFTYPE_OPAQUE or CFTYPE_NONARTIFACT if a match is found. */ int whatis_rid_type(int rid){ Stmt q = empty_Stmt; int type = 0; /* Check for entries on the timeline that reference this object */ db_prepare(&q, "SELECT type FROM event WHERE objid=%d", rid); if( db_step(&q)==SQLITE_ROW ){ switch( db_column_text(&q,0)[0] ){ case 'c': type = CFTYPE_MANIFEST; break; case 'w': type = CFTYPE_WIKI; break; case 'e': type = CFTYPE_EVENT; break; case 'f': type = CFTYPE_FORUM; break; case 't': type = CFTYPE_TICKET; break; case 'g': type = CFTYPE_CONTROL; break; default: break; } } db_finalize(&q); return type; } /* ** A proxy for whatis_rid_type() which returns a brief string (in ** static memory) describing the record type. Returns NULL if rid does ** not refer to an artifact record (as determined by reading the event ** table). The returned string is intended to be used in headers which ** can refer to different artifact types. It is not "definitive," in ** that it does not distinguish between closely-related types like ** wiki creation, edit, and removal. */ char const * whatis_rid_type_label(int rid){ char const * zType = 0; switch( whatis_rid_type(rid) ){ case CFTYPE_MANIFEST: zType = "Check-in"; break; case CFTYPE_WIKI: zType = "Wiki-edit"; break; case CFTYPE_EVENT: zType = "Technote"; break; case CFTYPE_FORUM: zType = "Forum-post"; break; case CFTYPE_TICKET: zType = "Ticket-change"; break; case CFTYPE_CONTROL: zType = "Tag-change"; break; default: break; } return zType; } /* ** Flag values for whatis_rid(). */ #if INTERFACE #define WHATIS_VERBOSE 0x01 /* Extra output */ #define WHATIS_BRIEF 0x02 /* Omit unnecessary output */ #define WHATIS_REPO 0x04 /* Show repository name */ #define WHATIS_OMIT_UNK 0x08 /* Do not show "unknown" lines */ #endif /* ** Generate a description of artifact "rid" */ void whatis_rid(int rid, int flags){ Stmt q; int cnt; /* Basic information about the object. */ db_prepare(&q, "SELECT uuid, size, datetime(mtime,toLocal()), ipaddr" " FROM blob, rcvfrom" " WHERE rid=%d" " AND rcvfrom.rcvid=blob.rcvid", rid); if( db_step(&q)==SQLITE_ROW ){ if( flags & WHATIS_VERBOSE ){ 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 tagxref.tagtype<>0" " 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,toLocal())," " coalesce(euser,user), coalesce(ecomment,comment)" " FROM event WHERE objid=%d", 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 = "Technote"; break; case 'f': zType = "Forum-post"; 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, get_comment_format()); cnt++; } 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,toLocal())," " 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 %s /*sort*/", rid, (flags & WHATIS_BRIEF) ? "LIMIT 1" : "DESC"); while( db_step(&q)==SQLITE_ROW ){ if( flags & WHATIS_BRIEF ){ fossil_print("mtime: %s\n", db_column_text(&q,2)); } 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, get_comment_format()); cnt++; } 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,toLocal())," " 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", 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( flags & WHATIS_VERBOSE ){ 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, get_comment_format()); cnt++; } db_finalize(&q); /* If other information available, try to describe the object */ if( cnt==0 ){ char *zWhere = mprintf("=%d", rid); char *zDesc; describe_artifacts(zWhere); free(zWhere); zDesc = db_text(0, "SELECT printf('%%-12s%%s %%s',type||':',summary,substr(ref,1,16))" " FROM description WHERE rid=%d", rid); fossil_print("%s\n", zDesc); fossil_free(zDesc); } } /* ** Generate a description of artifact from it symbolic name. */ void whatis_artifact( const char *zName, /* Symbolic name or full hash */ const char *zFileName,/* Optional: original filename (in file mode) */ const char *zType, /* Artifact type filter */ int mFlags /* WHATIS_* flags */ ){ int rid = symbolic_name_to_rid(zName, zType); if( rid<0 ){ Stmt q; int cnt = 0; if( mFlags & WHATIS_REPO ){ fossil_print("\nrepository: %s\n", g.zRepositoryName); } if( zFileName ){ fossil_print("%-12s%s\n", "name:", zFileName); } fossil_print("%-12s%s (ambiguous)\n", "hash:", 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), mFlags); } db_finalize(&q); }else if( rid==0 ){ if( (mFlags & WHATIS_OMIT_UNK)==0 ){ /* 0123456789 12 */ if( zFileName ){ fossil_print("%-12s%s\n", "name:", zFileName); } fossil_print("unknown: %s\n", zName); } }else{ if( mFlags & WHATIS_REPO ){ fossil_print("\nrepository: %s\n", g.zRepositoryName); } if( zFileName ){ zName = zFileName; } fossil_print("%-12s%s\n", "name:", zName); whatis_rid(rid, mFlags); } } /* ** COMMAND: whatis* ** ** Usage: %fossil whatis NAME ** ** Resolve the symbol NAME into its canonical artifact hash ** artifact name and provide a description of what role that artifact ** plays. ** ** Options: ** -f|--file Find artifacts with the same hash as file NAME. ** If NAME is "-", read content from standard input. ** -q|--quiet Show nothing if NAME is not found ** --type TYPE Only find artifacts of TYPE (one of: 'ci', 't', ** 'w', 'g', or 'e') ** -v|--verbose Provide extra information (such as the RID) */ void whatis_cmd(void){ int mFlags = 0; int fileFlag; int i; const char *zType = 0; db_find_and_open_repository(0,0); if( find_option("verbose","v",0)!=0 ){ mFlags |= WHATIS_VERBOSE; } if( find_option("quiet","q",0)!=0 ){ mFlags |= WHATIS_OMIT_UNK | WHATIS_REPO; } fileFlag = find_option("file","f",0)!=0; zType = find_option("type",0,1); /* We should be done with options.. */ verify_all_options(); if( g.argc<3 ) usage("NAME ..."); for(i=2; i<g.argc; i++){ const char *zName = g.argv[i]; if( i>2 ) fossil_print("%.79c\n",'-'); if( fileFlag ){ Blob in; Blob hash = empty_blob; const char *zHash; /* Always follow symlinks (when applicable) */ blob_read_from_file(&in, zName, ExtFILE); /* First check the auxiliary hash to see if there is already an artifact ** that uses the auxiliary hash name */ hname_hash(&in, 1, &hash); zHash = (const char*)blob_str(&hash); if( fast_uuid_to_rid(zHash)==0 ){ /* No existing artifact with the auxiliary hash name. Therefore, use ** the primary hash name. */ blob_reset(&hash); hname_hash(&in, 0, &hash); zHash = (const char*)blob_str(&hash); } whatis_artifact(zHash, zName, zType, mFlags); blob_reset(&hash); }else{ whatis_artifact(zName, 0, zType, mFlags); } } } /* ** 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 artifact 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, -- hash of the object @ ctime DATETIME, -- Time of creation @ isPrivate BOOLEAN DEFAULT 0, -- True for unpublished artifacts @ type TEXT, -- file, check-in, wiki, ticket, etc. @ rcvid INT, -- When the artifact was received @ summary TEXT, -- Summary comment for the object @ ref TEXT -- hash of an object to link against @ ); @ CREATE INDEX IF NOT EXISTS desctype @ ON description(summary) WHERE summary='unknown'; ; /* ** Attempt to describe all phantom artifacts. The artifacts are ** already loaded into the description table and have summary='unknown'. ** This routine attempts to generate a better summary, and possibly ** fill in the ref field. */ static void describe_unknown_artifacts(){ /* Try to figure out the origin of unknown artifacts */ db_multi_exec( "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" " SELECT description.rid, description.uuid, isPrivate, type,\n" " CASE WHEN plink.isprim THEN '' ELSE 'merge ' END ||\n" " 'parent of check-in', blob.uuid\n" " FROM description, plink, blob\n" " WHERE description.summary='unknown'\n" " AND plink.pid=description.rid\n" " AND blob.rid=plink.cid;" ); db_multi_exec( "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" " SELECT description.rid, description.uuid, isPrivate, type,\n" " 'child of check-in', blob.uuid\n" " FROM description, plink, blob\n" " WHERE description.summary='unknown'\n" " AND plink.cid=description.rid\n" " AND blob.rid=plink.pid;" ); db_multi_exec( "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" " SELECT description.rid, description.uuid, isPrivate, type,\n" " 'check-in referenced by \"'||tag.tagname ||'\" tag',\n" " blob.uuid\n" " FROM description, tagxref, tag, blob\n" " WHERE description.summary='unknown'\n" " AND tagxref.origid=description.rid\n" " AND tag.tagid=tagxref.tagid\n" " AND blob.rid=tagxref.srcid;" ); db_multi_exec( "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" " SELECT description.rid, description.uuid, isPrivate, type,\n" " 'file \"'||filename.name||'\"',\n" " blob.uuid\n" " FROM description, mlink, filename, blob\n" " WHERE description.summary='unknown'\n" " AND mlink.fid=description.rid\n" " AND blob.rid=mlink.mid\n" " AND filename.fnid=mlink.fnid;" ); if( !db_exists("SELECT 1 FROM description WHERE summary='unknown'") ){ return; } add_content_sql_commands(g.db); db_multi_exec( "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n" " SELECT description.rid, description.uuid, isPrivate, type,\n" " 'referenced by cluster', blob.uuid\n" " FROM description, tagxref, blob\n" " WHERE description.summary='unknown'\n" " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n" " AND blob.rid=tagxref.rid\n" " AND CAST(content(blob.uuid) AS text)" " GLOB ('*M '||description.uuid||'*');" ); } /* ** 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,rcvid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, blob.rcvid, event.mtime, 'checkin',\n" " 'check-in to '\n" " || coalesce((SELECT value FROM tagxref WHERE tagid=%d" " AND tagtype>0 AND tagxref.rid=blob.rid),'trunk')\n" " || ' by ' || coalesce(event.euser,event.user)\n" " || ' 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;", TAG_BRANCH, zWhere /*safe-for-%s*/ ); /* Describe files */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, blob.rcvid, 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,rcvid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, blob.rcvid, 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,rcvid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, blob.rcvid, rcvfrom.mtime," " 'cluster', 'cluster'\n" " FROM tagxref, blob, rcvfrom\n" " WHERE (tagxref.rid %s)\n" " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n" " AND blob.rid=tagxref.rid" " AND rcvfrom.rcvid=blob.rcvid;", zWhere /*safe-for-%s*/ ); /* Ticket change artifacts */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, blob.rcvid, 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,rcvid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, blob.rcvid, 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,rcvid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, blob.rcvid, 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,rcvid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, blob.rcvid, 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,rcvid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, blob.rcvid, 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*/ ); /* Forum posts */ if( db_table_exists("repository","forumpost") ){ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n" "SELECT postblob.rid, postblob.uuid, postblob.rcvid," " forumpost.fmtime, 'forumpost',\n" " CASE WHEN fpid=froot THEN 'forum-post '\n" " ELSE 'forum-reply-to ' END || substr(rootblob.uuid,1,14)\n" " FROM forumpost, blob AS postblob, blob AS rootblob\n" " WHERE (forumpost.fpid %s)\n" " AND postblob.rid=forumpost.fpid" " AND rootblob.rid=forumpost.froot", zWhere /*safe-for-%s*/ ); } /* Mark all other artifacts as "unknown" for now */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,rcvid,type,summary)\n" "SELECT blob.rid, blob.uuid,blob.rcvid,\n" " CASE WHEN EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)\n" " THEN 'phantom' ELSE '' END,\n" " 'unknown'\n" " FROM blob\n" " WHERE (blob.rid %s)\n" " AND (blob.rid NOT IN (SELECT rid FROM description));", zWhere /*safe-for-%s*/ ); /* Mark private elements */ db_multi_exec( "UPDATE description SET isPrivate=1 WHERE rid IN private" ); if( db_exists("SELECT 1 FROM description WHERE summary='unknown'") ){ describe_unknown_artifacts(); } } /* ** Print the content of the description table on stdout. ** ** The description table is computed using the WHERE clause zWhere if ** the zWhere parameter is not NULL. If zWhere is NULL, then this ** routine assumes that the description table already exists and is ** populated and merely prints the contents. */ int describe_artifacts_to_stdout(const char *zWhere, const char *zLabel){ Stmt q; int cnt = 0; if( zWhere!=0 ) describe_artifacts(zWhere); db_prepare(&q, "SELECT uuid, summary, coalesce(ref,''), 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 %s", db_column_text(&q,0), db_column_text(&q,1), db_column_text(&q,2)); if( db_column_int(&q,3) ) fossil_print(" (private)"); fossil_print("\n"); cnt++; } db_finalize(&q); if( zWhere!=0 ) 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 ** priv Show only unpublished or private artifacts ** phan Show only phantom artifacts ** hclr Color code hash types (SHA1 vs SHA3) */ 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"); int privOnly = PB("priv"); int phantomOnly = PB("phan"); int hashClr = PB("hclr"); char *zRange; char *zSha1Bg; char *zSha3Bg; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } cgi_check_for_malice(); style_header("List Of Artifacts"); style_submenu_element("250 Largest", "bigbloblist"); if( g.perm.Admin ){ style_submenu_element("Artifact Log", "rcvfromlist"); } if( !phantomOnly ){ style_submenu_element("Phantoms", "bloblist?phan"); } if( g.perm.Private || g.perm.Admin ){ if( !privOnly ){ style_submenu_element("Private", "bloblist?priv"); } }else{ privOnly = 0; } if( g.perm.Write ){ style_submenu_element("Artifact Stats", "artifact_stats"); } if( !privOnly && !phantomOnly && 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_finish_page(); return; } if( phantomOnly || privOnly || mx>n ){ style_submenu_element("Index", "bloblist"); } if( privOnly ){ zRange = mprintf("IN private"); }else if( phantomOnly ){ zRange = mprintf("IN phantom"); }else{ zRange = mprintf("BETWEEN %d AND %d", s, s+n-1); } describe_artifacts(zRange); fossil_free(zRange); db_prepare(&q, "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref" " FROM description ORDER BY rid" ); if( skin_detail_boolean("white-foreground") ){ zSha1Bg = "#714417"; zSha3Bg = "#177117"; }else{ zSha1Bg = "#ebffb0"; zSha3Bg = "#b0ffb0"; } @ <table cellpadding="2" cellspacing="0" border="1"> if( g.perm.Admin ){ @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks }else{ @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks } 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,3); int isPhantom = db_column_int(&q,4); const char *zRef = db_column_text(&q,6); if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){ /* Don't show private artifacts to users without Private (x) permission */ continue; } if( hashClr ){ const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg; @ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td> }else{ @ <tr><td align="right">%d(rid)</td> } @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> if( g.perm.Admin ){ int rcvid = db_column_int(&q,5); if( rcvid<=0 ){ @ <td> }else{ @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a> } } @ <td align="left">%h(zDesc)</td> if( zRef && zRef[0] ){ @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a> }else{ @ <td> } if( isPriv || isPhantom ){ if( isPriv==0 ){ @ <td>phantom</td> }else if( isPhantom==0 ){ @ <td>private</td> }else{ @ <td>private,phantom</td> } }else{ @ <td> } @ </tr> } @ </table> db_finalize(&q); style_finish_page(); } /* ** Output HTML that shows a table of all public phantoms. */ void table_of_public_phantoms(void){ Stmt q; char *zRange; double rNow; zRange = mprintf("IN (SELECT rid FROM phantom EXCEPT" " SELECT rid FROM private)"); describe_artifacts(zRange); fossil_free(zRange); db_prepare(&q, "SELECT rid, uuid, summary, ref," " (SELECT mtime FROM blob, rcvfrom" " WHERE blob.uuid=ref AND rcvfrom.rcvid=blob.rcvid)" " FROM description ORDER BY rid" ); rNow = db_double(0.0, "SELECT julianday('now')"); @ <table cellpadding="2" cellspacing="0" border="1"> @ <tr><th>RID<th>Description<th>Source<th>Age 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); const char *zRef = db_column_text(&q,3); double mtime = db_column_double(&q,4); @ <tr><td valign="top">%d(rid)</td> @ <td valign="top" align="left">%h(zUuid)<br>%h(zDesc)</td> if( zRef && zRef[0] ){ @ <td valign="top">%z(href("%R/info/%!S",zRef))%!S(zRef)</a> if( mtime>0 ){ char *zAge = human_readable_age(rNow - mtime); @ <td valign="top">%h(zAge) fossil_free(zAge); }else{ @ <td> } }else{ @ <td> <td> } @ </tr> } @ </table> db_finalize(&q); } /* ** WEBPAGE: phantoms ** ** Show a list of all "phantom" artifacts that are not marked as "private". ** ** A "phantom" artifact is an artifact whose hash named appears in some ** artifact but whose content is unknown. For example, if a manifest ** references a particular SHA3 hash of a file, but that SHA3 hash is ** not on the shunning list and is not in the database, then the file ** is a phantom. We know it exists, but we do not know its content. ** ** Whenever a sync occurs, both each party looks at its phantom list ** and for every phantom that is not also marked private, it asks the ** other party to send it the content. This mechanism helps keep all ** repositories synced up. ** ** This page is similar to the /bloblist page in that it lists artifacts. ** But this page is a special case in that it only shows phantoms that ** are not private. In other words, this page shows all phantoms that ** generate extra network traffic on every sync request. */ void phantom_list_page(void){ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("Public Phantom Artifacts"); if( g.perm.Admin ){ style_submenu_element("Artifact Log", "rcvfromlist"); style_submenu_element("Artifact List", "bloblist"); } if( g.perm.Write ){ style_submenu_element("Artifact Stats", "artifact_stats"); } table_of_public_phantoms(); style_finish_page(); } /* ** WEBPAGE: bigbloblist ** ** Return a page showing the largest artifacts in the repository in order ** of decreasing size. ** ** n=N Show the top N artifacts (default: 250) */ void bigbloblist_page(void){ Stmt q; int n = atoi(PD("n","250")); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( g.perm.Admin ){ style_submenu_element("Artifact Log", "rcvfromlist"); } if( g.perm.Write ){ style_submenu_element("Artifact Stats", "artifact_stats"); } style_submenu_element("All Artifacts", "bloblist"); style_header("%d Largest Artifacts", n); db_multi_exec( "CREATE TEMP TABLE toshow(rid INTEGER PRIMARY KEY);" "INSERT INTO toshow(rid)" " SELECT rid FROM blob" " ORDER BY length(content) DESC" " LIMIT %d;", n ); describe_artifacts("IN toshow"); db_prepare(&q, "SELECT description.rid, description.uuid, description.summary," " length(blob.content), coalesce(delta.srcid,'')," " datetime(description.ctime)" " FROM description, blob LEFT JOIN delta ON delta.rid=blob.rid" " WHERE description.rid=blob.rid" " ORDER BY length(content) DESC" ); @ <table cellpadding="2" cellspacing="0" border="1" \ @ class='sortable' data-column-types='NnnttT' data-init-sort='0'> @ <thead><tr><th align="right">Size<th align="right">RID @ <th align="right">From<th>Hash<th>Description<th>Date</tr></thead> @ <tbody> 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 sz = db_column_int(&q,3); const char *zSrcId = db_column_text(&q,4); const char *zDate = db_column_text(&q,5); @ <tr><td align="right">%d(sz)</td> @ <td align="right">%d(rid)</td> @ <td align="right">%s(zSrcId)</td> @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> @ <td align="left">%h(zDesc)</td> @ <td align="left">%z(href("%R/timeline?c=%T",zDate))%s(zDate)</a></td> @ </tr> } @ </tbody></table> db_finalize(&q); style_table_sorter(); style_finish_page(); } /* ** WEBPAGE: deltachain ** ** Usage: /deltachain/RID ** ** The RID query parameter is required. Generate a page with a table ** showing storage characteristics of RID and other artifacts that are ** derived from RID via delta. */ void deltachain_page(void){ Stmt q; int id = atoi(PD("name","0")); int top; i64 nStored = 0; i64 nExpanded = 0; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } top = db_int(id, "WITH RECURSIVE chain(aa,bb) AS (\n" " SELECT rid, srcid FROM delta WHERE rid=%d\n" " UNION ALL\n" " SELECT bb, delta.srcid" " FROM chain LEFT JOIN delta ON delta.rid=bb" " WHERE bb IS NOT NULL\n" ")\n" "SELECT aa FROM chain WHERE bb IS NULL", id ); style_header("Delta Chain Containing Artifact %d", id); db_multi_exec( "CREATE TEMP TABLE toshow(rid INT, gen INT);\n" "WITH RECURSIVE tx(id,px) AS (\n" " VALUES(%d,0)\n" " UNION ALL\n" " SELECT delta.rid, px+1 FROM tx, delta where delta.srcid=tx.id\n" " ORDER BY 2\n" ") " "INSERT INTO toshow(rid,gen) SELECT id,px FROM tx;", top ); db_multi_exec("CREATE INDEX toshow_rid ON toshow(rid);"); describe_artifacts("IN (SELECT rid FROM toshow)"); db_prepare(&q, "SELECT description.rid, description.uuid, description.summary," " length(blob.content), coalesce(delta.srcid,'')," " datetime(description.ctime), toshow.gen, blob.size" " FROM description, toshow, blob LEFT JOIN delta ON delta.rid=blob.rid" " WHERE description.rid=blob.rid" " AND toshow.rid=description.rid" " ORDER BY toshow.gen, description.ctime" ); @ <table cellpadding="2" cellspacing="0" border="1" \ @ class='sortable' data-column-types='nNnnttT' data-init-sort='0'> @ <thead><tr><th align="right">Level</th> @ <th align="right">Size<th align="right">RID @ <th align="right">From<th>Hash<th>Description<th>Date</tr></thead> @ <tbody> 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 sz = db_column_int(&q,3); const char *zSrcId = db_column_text(&q,4); const char *zDate = db_column_text(&q,5); int gen = db_column_int(&q,6); nExpanded += db_column_int(&q,7); nStored += sz; @ <tr><td align="right">%d(gen)</td> @ <td align="right">%d(sz)</td> if( rid==id ){ @ <td align="right"><b>%d(rid)</b></td> }else{ @ <td align="right">%d(rid)</td> } @ <td align="right">%s(zSrcId)</td> @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> @ <td align="left">%h(zDesc)</td> @ <td align="left">%z(href("%R/timeline?c=%T",zDate))%s(zDate)</a></td> @ </tr> } @ </tbody></table> db_finalize(&q); style_table_sorter(); @ <p> @ <table border="0" cellspacing="0" cellpadding="0"> @ <tr><td>Bytes of content</td><td> </td> @ <td align="right">%,lld(nExpanded)</td></tr> @ <tr><td>Bytes stored in repository</td><td></td> @ <td align="right">%,lld(nStored)</td> @ </table> @ </p> style_finish_page(); } /* ** 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 /* ** Generate a report on the number of collisions in artifact hashes ** generated by the SQL given in the argument. */ static void collision_report(const char *zSql){ int i, j; int nHash = 0; Stmt q; char zPrev[HNAME_MAX+1]; struct { int cnt; char *azHit[MAX_COLLIDE]; char z[HNAME_MAX+1]; } aCollide[HNAME_MAX+1]; memset(aCollide, 0, sizeof(aCollide)); memset(zPrev, 0, sizeof(zPrev)); db_prepare(&q,"%s",zSql/*safe-for-%s*/); 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<=HNAME_MAX ){ 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); @ <table border=1><thead> @ <tr><th>Length<th>Instances<th>First Instance</tr> @ </thead><tbody> for(i=1; i<=HNAME_MAX; 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> for(i=HNAME_MAX; i>=4; i--){ if( aCollide[i].cnt==0 ) continue; if( aCollide[i].cnt>200 ) break; 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/ambiguous/%s",zId))%h(zId)</a> } } for(i=4; i<count(aCollide); i++){ for(j=0; j<aCollide[i].cnt && j<MAX_COLLIDE; j++){ fossil_free(aCollide[i].azHit[j]); } } } /* ** WEBPAGE: hash-collisions ** ** Show the number of hash collisions for hash prefixes of various lengths. */ void hash_collisions_webpage(void){ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("Hash Prefix Collisions"); style_submenu_element("Activity Reports", "reports"); style_submenu_element("Stats", "stat"); @ <h1>Hash Prefix Collisions on Check-ins</h1> collision_report("SELECT (SELECT uuid FROM blob WHERE rid=objid)" " FROM event WHERE event.type='ci'" " ORDER BY 1"); @ <h1>Hash Prefix Collisions on All Artifacts</h1> collision_report("SELECT uuid FROM blob ORDER BY 1"); style_finish_page(); } |
Added src/patch.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 | /* ** Copyright (c) 2021 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 "patch" command */ #include "config.h" #include "patch.h" #include <assert.h> /* ** Try to compute the name of the computer on which this process ** is running. */ char *fossil_hostname(void){ FILE *in; char zBuf[200]; in = popen("hostname","r"); if( in ){ int n = fread(zBuf, 1, sizeof(zBuf)-1, in); while( n>0 && fossil_isspace(zBuf[n-1]) ){ n--; } if( n<0 ) n = 0; zBuf[n] = 0; pclose(in); return fossil_strdup(zBuf); } return 0; } /* ** Flags passed from the main patch_cmd() routine into subfunctions used ** to implement the various subcommands. */ #define PATCH_DRYRUN 0x0001 #define PATCH_VERBOSE 0x0002 #define PATCH_FORCE 0x0004 #define PATCH_RETRY 0x0008 /* Second attempt */ /* ** Implementation of the "readfile(X)" SQL function. The entire content ** of the check-out file named X is read and returned as a BLOB. */ static void readfileFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zName; Blob x; sqlite3_int64 sz; (void)(argc); /* Unused parameter */ zName = (const char*)sqlite3_value_text(argv[0]); if( zName==0 || (zName[0]=='-' && zName[1]==0) ) return; sz = blob_read_from_file(&x, zName, RepoFILE); sqlite3_result_blob64(context, x.aData, sz, SQLITE_TRANSIENT); blob_reset(&x); } /* ** mkdelta(X,Y) ** ** X is an numeric artifact id. Y is a filename. ** ** Compute a compressed delta that carries X into Y. Or return ** and zero-length blob if X is equal to Y. */ static void mkdeltaFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zFile; Blob x, y; int rid; char *aOut; int nOut; sqlite3_int64 sz; rid = sqlite3_value_int(argv[0]); if( !content_get(rid, &x) ){ sqlite3_result_error(context, "mkdelta(X,Y): no content for X", -1); return; } zFile = (const char*)sqlite3_value_text(argv[1]); if( zFile==0 ){ sqlite3_result_error(context, "mkdelta(X,Y): NULL Y argument", -1); blob_reset(&x); return; } sz = blob_read_from_file(&y, zFile, RepoFILE); if( sz<0 ){ sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1); blob_reset(&x); return; } aOut = sqlite3_malloc64(sz+70); if( aOut==0 ){ sqlite3_result_error_nomem(context); blob_reset(&y); blob_reset(&x); return; } if( blob_size(&x)==blob_size(&y) && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0 ){ blob_reset(&y); blob_reset(&x); sqlite3_result_blob64(context, "", 0, SQLITE_STATIC); return; } nOut = delta_create(blob_buffer(&x),blob_size(&x), blob_buffer(&y),blob_size(&y), aOut); blob_reset(&x); blob_reset(&y); blob_init(&x, aOut, nOut); blob_compress(&x, &x); sqlite3_result_blob64(context, blob_buffer(&x), blob_size(&x), SQLITE_TRANSIENT); blob_reset(&x); } /* ** Generate a binary patch file and store it into the file ** named zOut. Or if zOut is NULL, write it into out. ** ** Return the number of errors. */ void patch_create(unsigned mFlags, const char *zOut, FILE *out){ int vid; char *z; if( zOut && file_isdir(zOut, ExtFILE)!=0 ){ if( mFlags & PATCH_FORCE ){ file_delete(zOut); } if( file_isdir(zOut, ExtFILE)!=0 ){ fossil_fatal("patch file already exists: %s", zOut); } } add_content_sql_commands(g.db); deltafunc_init(g.db); sqlite3_create_function(g.db, "read_co_file", 1, SQLITE_UTF8, 0, readfileFunc, 0, 0); sqlite3_create_function(g.db, "mkdelta", 2, SQLITE_UTF8, 0, mkdeltaFunc, 0, 0); db_multi_exec("ATTACH %Q AS patch;", zOut ? zOut : ":memory:"); db_multi_exec( "PRAGMA patch.journal_mode=OFF;\n" "PRAGMA patch.page_size=512;\n" "CREATE TABLE patch.chng(\n" " pathname TEXT,\n" /* Filename */ " origname TEXT,\n" /* Name before rename. NULL if not renamed */ " hash TEXT,\n" /* Baseline hash. NULL for new files. */ " isexe BOOL,\n" /* True if executable */ " islink BOOL,\n" /* True if is a symbolic link */ " delta BLOB\n" /* compressed delta. NULL if deleted. ** length 0 if unchanged */ ");" "CREATE TABLE patch.cfg(\n" " key TEXT,\n" " value ANY\n" ");" ); vid = db_lget_int("checkout", 0); vfile_check_signature(vid, CKSIG_ENOTFILE); user_select(); db_multi_exec( "INSERT INTO patch.cfg(key,value)" "SELECT 'baseline',uuid FROM blob WHERE rid=%d " "UNION ALL" " SELECT 'ckout',rtrim(%Q,'/')" "UNION ALL" " SELECT 'repo',%Q " "UNION ALL" " SELECT 'user',%Q " "UNION ALL" " SELECT 'date',julianday('now')" "UNION ALL" " SELECT name,value FROM repository.config" " WHERE name IN ('project-code','project-name') " "UNION ALL" " SELECT 'fossil-date',julianday('" MANIFEST_DATE "')" ";", vid, g.zLocalRoot, g.zRepositoryName, g.zLogin); z = fossil_hostname(); if( z ){ db_multi_exec( "INSERT INTO patch.cfg(key,value)VALUES('hostname',%Q)", z); fossil_free(z); } /* New files */ db_multi_exec( "INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)" " SELECT pathname, NULL, isexe, islink," " compress(read_co_file(%Q||pathname))" " FROM vfile WHERE rid==0;", g.zLocalRoot ); /* Deleted files */ db_multi_exec( "INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)" " SELECT pathname, NULL, 0, 0, NULL" " FROM vfile WHERE deleted;" ); /* Changed files */ db_multi_exec( "INSERT INTO patch.chng(pathname,origname,hash,isexe,islink,delta)" " SELECT pathname, nullif(origname,pathname), blob.uuid, isexe, islink," " mkdelta(blob.rid, %Q||pathname)" " FROM vfile, blob" " WHERE blob.rid=vfile.rid" " AND NOT deleted AND (chnged OR origname<>pathname);", g.zLocalRoot ); /* Merges */ if( db_exists("SELECT 1 FROM localdb.vmerge WHERE id<=0") ){ db_multi_exec( "CREATE TABLE patch.patchmerge(type TEXT,mhash TEXT);\n" "WITH tmap(id,type) AS (VALUES(0,'merge'),(-1,'cherrypick')," "(-2,'backout'),(-4,'integrate'))" "INSERT INTO patch.patchmerge(type,mhash)" " SELECT tmap.type,vmerge.mhash FROM vmerge, tmap" " WHERE tmap.id=vmerge.id;" ); } /* Write the database to standard output if zOut==0 */ if( zOut==0 ){ sqlite3_int64 sz; unsigned char *pData; pData = sqlite3_serialize(g.db, "patch", &sz, 0); if( pData==0 ){ fossil_fatal("out of memory"); } #ifdef _WIN32 fflush(out); _setmode(_fileno(out), _O_BINARY); #endif fwrite(pData, 1, sz, out); fflush(out); sqlite3_free(pData); } db_multi_exec("DETACH patch;"); } /* ** Attempt to load and validate a patchfile identified by the first ** argument. */ void patch_attach(const char *zIn, FILE *in, int bIgnoreEmptyPatch){ Stmt q; if( g.db==0 ){ sqlite3_open(":memory:", &g.db); } if( zIn==0 ){ Blob buf; int rc; int sz; unsigned char *pData; blob_init(&buf, 0, 0); #ifdef _WIN32 _setmode(_fileno(in), _O_BINARY); #endif sz = blob_read_from_channel(&buf, in, -1); pData = (unsigned char*)blob_buffer(&buf); if( sz<512 ){ blob_reset(&buf); if( bIgnoreEmptyPatch ) return; fossil_fatal("input is too small to be a patch file"); } db_multi_exec("ATTACH ':memory:' AS patch"); if( g.fSqlTrace ){ fossil_trace("-- deserialize(\"patch\", pData, %lld);\n", sz); } rc = sqlite3_deserialize(g.db, "patch", pData, sz, sz, 0); if( rc ){ fossil_fatal("cannot open patch database: %s", sqlite3_errmsg(g.db)); } }else if( !file_isfile(zIn, ExtFILE) ){ fossil_fatal("no such file: %s", zIn); }else{ db_multi_exec("ATTACH %Q AS patch", zIn); } db_prepare(&q, "PRAGMA patch.quick_check"); while( db_step(&q)==SQLITE_ROW ){ if( fossil_strcmp(db_column_text(&q,0),"ok")!=0 ){ fossil_fatal("file %s is not a well-formed Fossil patchfile", zIn); } } db_finalize(&q); } /* ** Show a summary of the content of a patch on standard output */ void patch_view(unsigned mFlags){ Stmt q; db_prepare(&q, "WITH nmap(nkey,nm) AS (VALUES" "('baseline','BASELINE')," "('project-name','PROJECT-NAME'))" "SELECT nm, value FROM nmap, patch.cfg WHERE nkey=key;" ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%-12s %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); if( mFlags & PATCH_VERBOSE ){ db_prepare(&q, "WITH nmap(nkey,nm,isDate) AS (VALUES" "('project-code','PROJECT-CODE',0)," "('date','TIMESTAMP',1)," "('user','USER',0)," "('hostname','HOSTNAME',0)," "('ckout','CHECKOUT',0)," "('repo','REPOSITORY',0))" "SELECT nm, CASE WHEN isDate THEN datetime(value) ELSE value END" " FROM nmap, patch.cfg WHERE nkey=key;" ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%-12s %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); } if( db_table_exists("patch","patchmerge") ){ db_prepare(&q, "SELECT upper(type),mhash FROM patchmerge"); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%-12s %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); } db_prepare(&q, "SELECT pathname," /* 0: new name */ " hash IS NULL AND delta IS NOT NULL," /* 1: isNew */ " delta IS NULL," /* 2: isDeleted */ " origname" /* 3: old name or NULL */ " FROM patch.chng ORDER BY 1"); while( db_step(&q)==SQLITE_ROW ){ const char *zClass = "EDIT"; const char *zName = db_column_text(&q,0); const char *zOrigName = db_column_text(&q, 3); if( db_column_int(&q, 1) && zOrigName==0 ){ zClass = "NEW"; }else if( db_column_int(&q, 2) ){ zClass = zOrigName==0 ? "DELETE" : 0; } if( zOrigName!=0 && zOrigName[0]!=0 ){ fossil_print("%-12s %s -> %s\n", "RENAME",zOrigName,zName); } if( zClass ){ fossil_print("%-12s %s\n", zClass, zName); } } db_finalize(&q); } /* ** Apply the patch currently attached as database "patch". ** ** First update the check-out to be at "baseline". Then loop through ** and update all files. */ void patch_apply(unsigned mFlags){ Stmt q; Blob cmd; blob_init(&cmd, 0, 0); if( unsaved_changes(0) ){ if( (mFlags & PATCH_FORCE)==0 ){ fossil_fatal("there are unsaved changes in the current check-out"); }else{ blob_appendf(&cmd, "%$ revert", g.nameOfExe); if( mFlags & PATCH_DRYRUN ){ fossil_print("%s\n", blob_str(&cmd)); }else{ int rc = fossil_system(blob_str(&cmd)); if( rc ){ fossil_fatal("unable to revert preexisting changes: %s", blob_str(&cmd)); } } blob_reset(&cmd); } } file_chdir(g.zLocalRoot, 0); db_prepare(&q, "SELECT patch.cfg.value" " FROM patch.cfg, localdb.vvar" " WHERE patch.cfg.key='baseline'" " AND localdb.vvar.name='checkout-hash'" " AND patch.cfg.key<>localdb.vvar.name" ); if( db_step(&q)==SQLITE_ROW ){ blob_append_escaped_arg(&cmd, g.nameOfExe, 1); blob_appendf(&cmd, " update %s", db_column_text(&q, 0)); if( mFlags & PATCH_VERBOSE ){ fossil_print("%-10s %s\n", "BASELINE", db_column_text(&q,0)); } } db_finalize(&q); if( blob_size(&cmd)>0 ){ if( mFlags & PATCH_DRYRUN ){ fossil_print("%s\n", blob_str(&cmd)); }else{ int rc = fossil_system(blob_str(&cmd)); if( rc ){ fossil_fatal("unable to update to the baseline check-out: %s", blob_str(&cmd)); } } } blob_reset(&cmd); if( db_table_exists("patch","patchmerge") ){ db_prepare(&q, "SELECT type, mhash, upper(type) FROM patch.patchmerge" " WHERE type IN ('merge','cherrypick','backout','integrate')" " AND mhash NOT GLOB '*[^a-fA-F0-9]*';" ); while( db_step(&q)==SQLITE_ROW ){ const char *zType = db_column_text(&q,0); blob_append_escaped_arg(&cmd, g.nameOfExe, 1); if( strcmp(zType,"merge")==0 ){ blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1)); }else{ blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1)); } if( mFlags & PATCH_VERBOSE ){ fossil_print("%-10s %s\n", db_column_text(&q,2), db_column_text(&q,0)); } } db_finalize(&q); if( mFlags & PATCH_DRYRUN ){ fossil_print("%s", blob_str(&cmd)); }else{ int rc = fossil_unsafe_system(blob_str(&cmd)); if( rc ){ fossil_fatal("unable to do merges:\n%s", blob_str(&cmd)); } } blob_reset(&cmd); } /* Deletions */ db_prepare(&q, "SELECT pathname FROM patch.chng" " WHERE origname IS NULL AND delta IS NULL"); while( db_step(&q)==SQLITE_ROW ){ blob_append_escaped_arg(&cmd, g.nameOfExe, 1); blob_appendf(&cmd, " rm --hard %$\n", db_column_text(&q,0)); if( mFlags & PATCH_VERBOSE ){ fossil_print("%-10s %s\n", "DELETE", db_column_text(&q,0)); } } db_finalize(&q); if( blob_size(&cmd)>0 ){ if( mFlags & PATCH_DRYRUN ){ fossil_print("%s", blob_str(&cmd)); }else{ int rc = fossil_unsafe_system(blob_str(&cmd)); if( rc ){ fossil_fatal("unable to do merges:\n%s", blob_str(&cmd)); } } blob_reset(&cmd); } /* Renames */ db_prepare(&q, "SELECT origname, pathname FROM patch.chng" " WHERE origname IS NOT NULL" " AND origname<>pathname" ); while( db_step(&q)==SQLITE_ROW ){ blob_append_escaped_arg(&cmd, g.nameOfExe, 1); blob_appendf(&cmd, " mv --hard %$ %$\n", db_column_text(&q,0), db_column_text(&q,1)); if( mFlags & PATCH_VERBOSE ){ fossil_print("%-10s %s -> %s\n", "RENAME", db_column_text(&q,0), db_column_text(&q,1)); } } db_finalize(&q); if( blob_size(&cmd)>0 ){ if( mFlags & PATCH_DRYRUN ){ fossil_print("%s", blob_str(&cmd)); }else{ int rc = fossil_unsafe_system(blob_str(&cmd)); if( rc ){ fossil_print("%-10s unable to rename files:\n%s", "WARNING!", blob_str(&cmd)); } } blob_reset(&cmd); } /* Edits and new files */ db_prepare(&q, "SELECT pathname, hash, isexe, islink, delta FROM patch.chng" " WHERE delta IS NOT NULL" ); while( db_step(&q)==SQLITE_ROW ){ const char *zPathname = db_column_text(&q,0); const char *zHash = db_column_text(&q,1); int isExe = db_column_int(&q,2); int isLink = db_column_int(&q,3); Blob data; blob_init(&data, 0, 0); db_ephemeral_blob(&q, 4, &data); if( blob_size(&data) ){ blob_uncompress(&data, &data); } if( blob_size(&data)==0 ){ /* No changes to the file */ }else if( zHash ){ Blob basis; int rid = fast_uuid_to_rid(zHash); int outSize, sz; char *aOut; if( rid==0 ){ fossil_fatal("cannot locate basis artifact %s for %s", zHash, zPathname); } if( !content_get(rid, &basis) ){ fossil_fatal("cannot load basis artifact %d for %s", rid, zPathname); } outSize = delta_output_size(blob_buffer(&data),blob_size(&data)); if( outSize<=0 ){ fossil_fatal("malformed delta for %s", zPathname); } aOut = sqlite3_malloc64( outSize+1 ); if( aOut==0 ){ fossil_fatal("out of memory"); } sz = delta_apply(blob_buffer(&basis), blob_size(&basis), blob_buffer(&data), blob_size(&data), aOut); if( sz<0 ){ fossil_fatal("malformed delta for %s", zPathname); } blob_reset(&basis); blob_reset(&data); blob_append(&data, aOut, sz); sqlite3_free(aOut); if( mFlags & PATCH_VERBOSE ){ fossil_print("%-10s %s\n", "EDIT", zPathname); } }else{ blob_append_escaped_arg(&cmd, g.nameOfExe, 1); blob_appendf(&cmd, " add %$\n", zPathname); if( mFlags & PATCH_VERBOSE ){ fossil_print("%-10s %s\n", "NEW", zPathname); } } if( (mFlags & PATCH_DRYRUN)==0 ){ if( isLink ){ symlink_create(blob_str(&data), zPathname); }else{ blob_write_to_file(&data, zPathname); } file_setexe(zPathname, isExe); blob_reset(&data); } } db_finalize(&q); if( blob_size(&cmd)>0 ){ if( mFlags & PATCH_DRYRUN ){ fossil_print("%s", blob_str(&cmd)); }else{ int rc = fossil_unsafe_system(blob_str(&cmd)); if( rc ){ fossil_fatal("unable to add new files:\n%s", blob_str(&cmd)); } } blob_reset(&cmd); } } /* ** This routine processes the ** ** ... [--dir64 DIR64] [DIRECTORY] FILENAME ** ** part of various "fossil patch" subcommands. ** ** Find and return the filename of the patch file to be used by ** "fossil patch apply" or "fossil patch create". Space to hold ** the returned name is obtained from fossil_malloc() and should ** be freed by the caller. ** ** If the name is "-" return NULL. The caller will interpret this ** to mean the patch is coming in over stdin or going out over ** stdout. ** ** If there is a prior DIRECTORY argument, or if ** the --dir64 option is present, first chdir to the specified ** directory, and adjust the path of FILENAME as appropriate so ** that it still points to the same file. ** ** The --dir64 option is undocumented. The argument to --dir64 ** is a base64-encoded directory name. The --dir64 option is used ** to transmit the directory as part of the command argument to ** a "ssh" command without having to worry about quoting ** any special characters in the filename. ** ** The returned name is obtained from fossil_malloc() and should ** be freed by the caller. */ static char *patch_find_patch_filename(const char *zCmdName){ const char *zDir64 = find_option("dir64",0,1); const char *zDir = 0; const char *zBaseName; char *zToFree = 0; char *zPatchFile = 0; if( zDir64 ){ int n = 0; zToFree = decode64(zDir64, &n); zDir = zToFree; } verify_all_options(); if( g.argc!=4 && g.argc!=5 ){ usage(mprintf("%s [DIRECTORY] FILENAME", zCmdName)); } if( g.argc==5 ){ zDir = g.argv[3]; zBaseName = g.argv[4]; }else{ zBaseName = g.argv[3]; } if( fossil_strcmp(zBaseName, "-")==0 ){ zPatchFile = 0; }else if( zDir ){ zPatchFile = file_canonical_name_dup(g.argv[4]); }else{ zPatchFile = fossil_strdup(g.argv[3]); } if( zDir && file_chdir(zDir,0) ){ fossil_fatal("cannot change to directory \"%s\"", zDir); } fossil_free(zToFree); return zPatchFile; } /* ** Create a FILE* that will execute the remote side of a push or pull ** using ssh (probably) or fossil for local pushes and pulls. Return ** a FILE* obtained from popen() into which we write the patch, or from ** which we read the patch, depending on whether this is a push or pull. */ static FILE *patch_remote_command( unsigned mFlags, /* flags */ const char *zThisCmd, /* "push" or "pull" */ const char *zRemoteCmd, /* "apply" or "create" */ const char *zFossilCmd, /* Name of "fossil" on remote system */ const char *zRW /* "w" or "r" */ ){ char *zRemote = 0; char *zDir = 0; Blob cmd; FILE *f = 0; Blob flgs; char *zForce = 0; int isRetry = (mFlags & PATCH_RETRY)!=0; blob_init(&flgs, 0, 0); blob_init(&cmd, 0, 0); if( mFlags & PATCH_FORCE ) blob_appendf(&flgs, " -f"); if( mFlags & PATCH_VERBOSE ) blob_appendf(&flgs, " -v"); if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n"); zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : ""; if( g.argc!=4 ){ usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd)); } zRemote = fossil_strdup(g.argv[3]); zDir = (char*)file_skip_userhost(zRemote); if( zDir==0 ){ if( isRetry ) goto remote_command_error; zDir = zRemote; blob_append_escaped_arg(&cmd, g.nameOfExe, 1); blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir); }else{ Blob remote; *(char*)(zDir-1) = 0; transport_ssh_command(&cmd); blob_appendf(&cmd, " -T"); blob_append_escaped_arg(&cmd, zRemote, 0); blob_init(&remote, 0, 0); if( zFossilCmd==0 ){ if( ssh_needs_path_argument(zRemote,-1) ^ isRetry ){ ssh_add_path_argument(&cmd); } zFossilCmd = "fossil"; }else if( mFlags & PATCH_RETRY ){ goto remote_command_error; } blob_appendf(&remote, "%$ patch %s%s --dir64 %z -", zFossilCmd, zRemoteCmd, zForce, encode64(zDir, -1)); blob_append_escaped_arg(&cmd, blob_str(&remote), 0); blob_reset(&remote); } if( isRetry ){ fossil_print("First attempt to run \"fossil\" on %s failed\n" "Retry: ", zRemote); } fossil_print("%s\n", blob_str(&cmd)); fflush(stdout); f = popen(blob_str(&cmd), zRW); if( f==0 ){ fossil_fatal("cannot run command: %s", blob_str(&cmd)); } remote_command_error: fossil_free(zRemote); blob_reset(&cmd); blob_reset(&flgs); return f; } /* ** Toggle the use-path-for-ssh setting for the remote host defined ** by g.argv[3]. */ static void patch_toggle_ssh_needs_path(void){ char *zRemote = fossil_strdup(g.argv[3]); char *zDir = (char*)file_skip_userhost(zRemote); if( zDir ){ *(char*)(zDir - 1) = 0; ssh_needs_path_argument(zRemote, 99); } fossil_free(zRemote); } /* ** Show a diff for the patch currently loaded into database "patch". */ static void patch_diff( unsigned mFlags, /* Patch flags. only -f is allowed */ DiffConfig *pCfg /* Diff options */ ){ int nErr = 0; Stmt q; int bWebpage = (pCfg->diffFlags & DIFF_WEBPAGE)!=0; Blob empty; blob_zero(&empty); if( (mFlags & PATCH_FORCE)==0 ){ /* Check to ensure that the patch is against the repository that ** we have opened. ** ** To do: If there is a mismatch, should we scan all of the repositories ** listed in the global_config table looking for a match? */ if( db_exists( "SELECT 1 FROM patch.cfg" " WHERE cfg.key='baseline'" " AND NOT EXISTS(SELECT 1 FROM blob WHERE uuid=cfg.value)" )){ char *zBaseline; db_prepare(&q, "SELECT config.value, cfg.value FROM config, cfg" " WHERE config.name='project-name'" " AND cfg.key='project-name'" " AND config.value<>cfg.value" ); if( db_step(&q)==SQLITE_ROW ){ char *zRepo = fossil_strdup(db_column_text(&q,0)); char *zPatch = fossil_strdup(db_column_text(&q,1)); db_finalize(&q); fossil_fatal("the patch is against project \"%z\" but you are using " "project \"%z\"", zPatch, zRepo); } db_finalize(&q); zBaseline = db_text(0, "SELECT value FROM patch.cfg" " WHERE key='baseline'"); if( zBaseline ){ fossil_fatal("the baseline of the patch (check-in %S) is not found " "in the %s repository", zBaseline, g.zRepositoryName); } } } diff_begin(pCfg); db_prepare(&q, "SELECT" " (SELECT blob.rid FROM blob WHERE blob.uuid=chng.hash)," " pathname," /* 1: new pathname */ " origname," /* 2: original pathname. Null if not renamed */ " delta," /* 3: delta. NULL if deleted. empty is no change */ " hash" /* 4: baseline hash */ " FROM patch.chng" " ORDER BY pathname" ); while( db_step(&q)==SQLITE_ROW ){ int rid; const char *zName; Blob a, b; if( db_column_type(&q,0)!=SQLITE_INTEGER && db_column_type(&q,4)==SQLITE_TEXT ){ char *zUuid = fossil_strdup(db_column_text(&q,4)); char *zName = fossil_strdup(db_column_text(&q,1)); if( mFlags & PATCH_FORCE ){ fossil_print("ERROR cannot find base artifact %S for file \"%s\"\n", zUuid, zName); nErr++; fossil_free(zUuid); fossil_free(zName); continue; }else{ db_finalize(&q); fossil_fatal("base artifact %S for file \"%s\" not found", zUuid, zName); } } zName = db_column_text(&q, 1); rid = db_column_int(&q, 0); pCfg->diffFlags &= (~DIFF_FILE_MASK); if( db_column_type(&q,3)==SQLITE_NULL ){ if( !bWebpage ) fossil_print("DELETE %s\n", zName); pCfg->diffFlags |= DIFF_FILE_DELETED; diff_print_index(zName, pCfg, 0); content_get(rid, &a); diff_file_mem(&a, &empty, zName, pCfg); }else if( rid==0 ){ db_ephemeral_blob(&q, 3, &a); blob_uncompress(&a, &a); if( !bWebpage ) fossil_print("ADDED %s\n", zName); pCfg->diffFlags |= DIFF_FILE_ADDED; diff_print_index(zName, pCfg, 0); diff_file_mem(&empty, &a, zName, pCfg); blob_reset(&a); }else if( db_column_bytes(&q, 3)>0 ){ Blob delta; db_ephemeral_blob(&q, 3, &delta); blob_uncompress(&delta, &delta); content_get(rid, &a); blob_delta_apply(&a, &delta, &b); diff_file_mem(&a, &b, zName, pCfg); blob_reset(&a); blob_reset(&b); blob_reset(&delta); } } db_finalize(&q); diff_end(pCfg, nErr); if( nErr ) fossil_fatal("abort due to prior errors"); } /* ** COMMAND: patch ** ** Usage: %fossil patch SUBCOMMAND ?ARGS ..? ** ** This command is used to create, view, and apply Fossil binary patches. ** A Fossil binary patch is a single (binary) file that captures all of the ** uncommitted changes of a check-out. Use Fossil binary patches to transfer ** proposed or incomplete changes between machines for testing or analysis. ** ** > fossil patch create [DIRECTORY] PATCHFILE ** ** Create a new binary patch in PATCHFILE that captures all uncommitted ** changes in the check-out at DIRECTORY, or the current directory if ** DIRECTORY is omitted. If PATCHFILE is "-" then the binary patch ** is written to standard output. ** ** Options: ** -f|--force Overwrite an existing patch with the same name ** ** > fossil patch apply [DIRECTORY] PATCHFILE ** ** Apply the changes in PATCHFILE to the check-out at DIRECTORY, or ** in the current directory if DIRECTORY is omitted. ** ** Options: ** -f|--force Apply the patch even though there are unsaved ** changes in the current check-out. Unsaved changes ** are reverted and permanently lost. ** -n|--dry-run Do nothing, but print what would have happened ** -v|--verbose Extra output explaining what happens ** ** > fossil patch diff [DIRECTORY] PATCHFILE ** > fossil patch gdiff [DIRECTORY] PATCHFILE ** ** Show a human-readable diff for the patch in PATCHFILE and associated ** with the repository checked out in DIRECTORY. The current ** directory is used if DIRECTORY is omitted. All the usual ** diff flags described at "fossil help diff" apply. With gdiff, ** gdiff-command is used instead of internal diff logic. In addition: ** ** -f|--force Continue trying to perform the diff even if ** baseline information is missing from the current ** repository ** ** > fossil patch push REMOTE-CHECKOUT ** ** Create a patch for the current check-out, transfer that patch to ** a remote machine (using ssh) and apply the patch there. The ** REMOTE-CHECKOUT is in one of the following formats: ** ** * DIRECTORY ** * HOST:DIRECTORY ** * USER@HOST:DIRECTORY ** ** The name of the fossil executable on the remote host is specified ** by the --fossilcmd option, or if there is no --fossilcmd, it first ** tries "$HOME/bin/fossil" and if not found there it searches for any ** executable named "fossil" on the default $PATH set by SSH on the ** remote. ** ** Command-line options: ** ** -f|--force Apply the patch even though there are unsaved ** changes in the current check-out. Unsaved ** changes will be reverted and then the patch is ** applied. ** --fossilcmd EXE Name of the "fossil" executable on the remote ** -n|--dry-run Do nothing, but print what would have happened ** -v|--verbose Extra output explaining what happens ** ** ** > fossil patch pull REMOTE-CHECKOUT ** ** Like "fossil patch push" except that the transfer is from remote ** to local. All the same command-line options apply. ** ** > fossil patch view PATCHFILE ** ** View a summary of the changes in the binary patch in PATCHFILE. ** Use "fossil patch diff" for detailed patch content. ** ** -v|--verbose Show extra detail about the patch ** */ void patch_cmd(void){ const char *zCmd; size_t n; if( g.argc<3 ){ patch_usage: usage("apply|create|diff|gdiff|pull|push|view"); } zCmd = g.argv[2]; n = strlen(zCmd); if( strncmp(zCmd, "apply", n)==0 ){ char *zIn; unsigned flags = 0; if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN; if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; if( find_option("force","f",0) ) flags |= PATCH_FORCE; zIn = patch_find_patch_filename("apply"); db_must_be_within_tree(); patch_attach(zIn, stdin, 0); patch_apply(flags); fossil_free(zIn); }else if( strncmp(zCmd, "create", n)==0 ){ char *zOut; unsigned flags = 0; if( find_option("force","f",0) ) flags |= PATCH_FORCE; zOut = patch_find_patch_filename("create"); verify_all_options(); db_must_be_within_tree(); patch_create(flags, zOut, stdout); fossil_free(zOut); }else if( (strncmp(zCmd, "diff", n)==0) || (strncmp(zCmd, "gdiff", n)==0) ){ char *zIn; unsigned flags = 0; DiffConfig DCfg; if( find_option("tk",0,0)!=0 ){ db_close(0); diff_tk("patch diff", 3); return; } db_find_and_open_repository(0, 0); if( find_option("force","f",0) ) flags |= PATCH_FORCE; diff_options(&DCfg, zCmd[0]=='g', 0); verify_all_options(); zIn = patch_find_patch_filename("apply"); patch_attach(zIn, stdin, 0); patch_diff(flags, &DCfg); fossil_free(zIn); }else if( strncmp(zCmd, "pull", n)==0 ){ FILE *pIn = 0; unsigned flags = 0; const char *zFossilCmd = find_option("fossilcmd",0,1); if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN; if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; if( find_option("force","f",0) ) flags |= PATCH_FORCE; db_must_be_within_tree(); verify_all_options(); pIn = patch_remote_command(flags & (~PATCH_FORCE), "pull", "create", zFossilCmd, "r"); if( pIn ){ patch_attach(0, pIn, 1); if( pclose(pIn) ){ flags |= PATCH_RETRY; pIn = patch_remote_command(flags & (~PATCH_FORCE), "pull", "create", zFossilCmd, "r"); if( pIn ){ patch_attach(0, pIn, 0); if( pclose(pIn)==0 ){ patch_toggle_ssh_needs_path(); } } } patch_apply(flags); } }else if( strncmp(zCmd, "push", n)==0 ){ FILE *pOut = 0; unsigned flags = 0; const char *zFossilCmd = find_option("fossilcmd",0,1); if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN; if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; if( find_option("force","f",0) ) flags |= PATCH_FORCE; db_must_be_within_tree(); verify_all_options(); pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w"); if( pOut ){ patch_create(0, 0, pOut); if( pclose(pOut)!=0 ){ flags |= PATCH_RETRY; pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w"); if( pOut ){ patch_create(0, 0, pOut); if( pclose(pOut)==0 ){ patch_toggle_ssh_needs_path(); } } } } }else if( strncmp(zCmd, "view", n)==0 ){ const char *zIn; unsigned int flags = 0; if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; verify_all_options(); if( g.argc!=4 ){ usage("view FILENAME"); } zIn = g.argv[3]; if( fossil_strcmp(zIn, "-")==0 ) zIn = 0; patch_attach(zIn, stdin, 0); patch_view(flags); }else { goto patch_usage; } } |
Added src/path.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 | /* ** 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@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. */ struct PathNode { int rid; /* ID for this node */ u8 fromIsParent; /* True if pFrom is the parent of rid */ u8 isPrim; /* True if primary side of common ancestor */ u8 isHidden; /* Abbreviate output in "fossil bisect ls" */ PathNode *pFrom; /* Node we came from */ union { PathNode *pPeer; /* List of nodes of the same generation */ PathNode *pTo; /* Next on path from beginning to end */ } u; PathNode *pAll; /* List of all nodes */ }; #endif /* ** Local variables for this module */ static struct { PathNode *pCurrent; /* Current generation of nodes */ PathNode *pAll; /* All nodes */ Bag seen; /* Nodes seen before */ int nStep; /* Number of steps from first to last */ int nNotHidden; /* Number of steps not counting hidden nodes */ PathNode *pStart; /* Earliest node */ PathNode *pEnd; /* Most recent */ } path; /* ** Return the first (last) element of the computed path. */ PathNode *path_first(void){ return path.pStart; } PathNode *path_last(void){ return path.pEnd; } /* ** Return the number of steps in the computed path. */ int path_length(void){ return path.nStep; } /* ** Return the number of non-hidden steps in the computed path. */ int path_length_not_hidden(void){ return path.nNotHidden; } /* ** Create a new node */ static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){ PathNode *p; p = fossil_malloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); p->rid = rid; p->fromIsParent = isParent; p->pFrom = pFrom; p->u.pPeer = path.pCurrent; path.pCurrent = p; p->pAll = path.pAll; path.pAll = p; bag_insert(&path.seen, rid); return p; } /* ** Reset memory used by the shortest path algorithm. */ void path_reset(void){ PathNode *p; while( path.pAll ){ p = path.pAll; path.pAll = p->pAll; fossil_free(p); } bag_clear(&path.seen); memset(&path, 0, sizeof(path)); } /* ** Construct the path from path.pStart to path.pEnd in the u.pTo fields. */ static void path_reverse_path(void){ PathNode *p; assert( path.pEnd!=0 ); for(p=path.pEnd; p && p->pFrom; p = p->pFrom){ p->pFrom->u.pTo = p; } path.pEnd->u.pTo = 0; assert( p==path.pStart ); } /* ** 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 */ int iTo, /* Path ends here */ int directOnly, /* No merge links if true */ int oneWayOnly, /* Parent->child only if true */ Bag *pHidden /* Hidden nodes */ ){ Stmt s; PathNode *pPrev; PathNode *p; 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++; pPrev = path.pCurrent; path.pCurrent = 0; while( pPrev ){ db_bind_int(&s, ":pid", pPrev->rid); while( db_step(&s)==SQLITE_ROW ){ int cid = db_column_int(&s, 0); int isParent = db_column_int(&s, 1); if( bag_find(&path.seen, cid) ) continue; p = path_new_node(cid, pPrev, isParent); if( pHidden && bag_find(pHidden,cid) ) p->isHidden = 1; if( cid==iTo ){ db_finalize(&s); path.pEnd = p; path_reverse_path(); for(p=path.pStart->u.pTo; p; p=p->u.pTo ){ if( !p->isHidden ) path.nNotHidden++; } return path.pStart; } } db_reset(&s); pPrev = pPrev->u.pPeer; } } db_finalize(&s); path_reset(); return 0; } /* ** Find the mid-point of the path. If the path contains fewer than ** 2 steps, return 0. */ PathNode *path_midpoint(void){ PathNode *p; int i; if( path.nNotHidden<2 ) return 0; for(p=path.pEnd, i=0; p && (p->isHidden || i<path.nNotHidden/2); p=p->pFrom){ if( !p->isHidden ) i++; } return p; } /* ** Find the next most recent node on a path. */ PathNode *path_next(void){ PathNode *p; p = path.pStart; if( p ) p = p->u.pTo; return p; } /* ** Return an estimate of the number of comparisons remaining in order ** to bisect path. This is based on the log2() of path.nStep. */ int path_search_depth(void){ int i, j; for(i=0, j=1; j<path.nNotHidden; i++, j+=j){} return i; } /* ** Compute the shortest path between two check-ins and then transfer ** that path into the "ancestor" table. This is a utility used by ** both /annotate and /finfo. See also: compute_direct_ancestors(). */ void path_shortest_stored_in_ancestor_table( int origid, /* RID for check-in at start of the path */ int cid /* RID for check-in at the end of the path */ ){ PathNode *pPath; int gen = 0; Stmt ins; pPath = path_shortest(cid, origid, 1, 0, 0); db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS ancestor(" " rid INT UNIQUE," " generation INTEGER PRIMARY KEY" ");" "DELETE FROM ancestor;" ); db_prepare(&ins, "INSERT INTO ancestor(rid, generation) VALUES(:rid,:gen)"); while( pPath ){ db_bind_int(&ins, ":rid", pPath->rid); db_bind_int(&ins, ":gen", ++gen); db_step(&ins); db_reset(&ins); pPath = pPath->u.pTo; } db_finalize(&ins); path_reset(); } /* ** 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; int directOnly; int oneWay; db_find_and_open_repository(0,0); directOnly = find_option("no-merge",0,0)!=0; oneWay = find_option("one-way",0,0)!=0; if( g.argc!=4 ) usage("VERSION1 VERSION2"); iFrom = name_to_rid(g.argv[2]); iTo = name_to_rid(g.argv[3]); p = path_shortest(iFrom, iTo, directOnly, oneWay, 0); if( p==0 ){ fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]); } for(n=1, p=path.pStart; p; p=p->u.pTo, n++){ char *z; z = db_text(0, "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"); } } } /* ** Find the closest common ancestor of two nodes. "Closest" means the ** fewest number of arcs. */ int path_common_ancestor(int iMe, int iYou){ Stmt s; PathNode *pPrev; PathNode *p; Bag me, you; if( iMe==iYou ) return iMe; if( iMe==0 || iYou==0 ) return 0; path_reset(); path.pStart = path_new_node(iMe, 0, 0); path.pStart->isPrim = 1; path.pEnd = path_new_node(iYou, 0, 0); db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid"); bag_init(&me); bag_insert(&me, iMe); bag_init(&you); bag_insert(&you, iYou); while( path.pCurrent ){ pPrev = path.pCurrent; path.pCurrent = 0; while( pPrev ){ db_bind_int(&s, ":cid", pPrev->rid); while( db_step(&s)==SQLITE_ROW ){ int pid = db_column_int(&s, 0); if( bag_find(pPrev->isPrim ? &you : &me, pid) ){ /* pid is the common ancestor */ PathNode *pNext; for(p=path.pAll; p && p->rid!=pid; p=p->pAll){} assert( p!=0 ); pNext = p; while( pNext ){ pNext = p->pFrom; p->pFrom = pPrev; pPrev = p; p = pNext; } if( pPrev==path.pStart ) path.pStart = path.pEnd; path.pEnd = pPrev; path_reverse_path(); db_finalize(&s); return pid; }else if( bag_find(&path.seen, pid) ){ /* pid is just an alternative path on one of the legs */ continue; } p = path_new_node(pid, pPrev, 0); p->isPrim = pPrev->isPrim; bag_insert(pPrev->isPrim ? &me : &you, pid); } db_reset(&s); pPrev = pPrev->u.pPeer; } } db_finalize(&s); path_reset(); return 0; } /* ** COMMAND: test-ancestor-path ** ** Usage: %fossil test-ancestor-path VERSION1 VERSION2 ** ** Report the path from VERSION1 to VERSION2 through their most recent ** common ancestor. */ void ancestor_path_test_cmd(void){ int iFrom; int iTo; int iPivot; PathNode *p; int n; db_find_and_open_repository(0,0); if( g.argc!=4 ) usage("VERSION1 VERSION2"); iFrom = name_to_rid(g.argv[2]); iTo = name_to_rid(g.argv[3]); iPivot = path_common_ancestor(iFrom, iTo); for(n=1, p=path.pStart; p; p=p->u.pTo, n++){ char *z; z = db_text(0, "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->rid==iFrom ) fossil_print(" VERSION1"); if( p->rid==iTo ) fossil_print(" VERSION2"); if( p->rid==iPivot ) fossil_print(" PIVOT"); fossil_print("\n"); } } /* ** A record of a file rename operation. */ typedef struct NameChange NameChange; struct NameChange { 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 ** in this path.c module in order to leverage some of the path ** infrastructure. */ void find_filename_changes( int iFrom, /* Ancestor check-in */ int iTo, /* Recent check-in */ int revOK, /* OK to move backwards (child->parent) if true */ int *pnChng, /* Number of name changes along the path */ int **aiChng, /* Name changes */ const char *zDebug /* Generate trace output if no NULL */ ){ PathNode *p; /* For looping over path from iFrom to iTo */ NameChange *pAll = 0; /* List of all name changes seen so far */ NameChange *pChng; /* For looping through the name change list */ 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, 0); if( p==0 ) return; path_reverse_path(); db_prepare(&q1, "SELECT pfnid, fnid FROM mlink" " WHERE mid=:mid AND (pfnid>0 OR fid==0)" " ORDER BY pfnid" ); for(p=path.pStart; p; p=p->u.pTo){ int fnid, pfnid; if( !p->fromIsParent && (p->u.pTo==0 || p->u.pTo->fromIsParent) ){ /* Skip nodes where the parent is not on the path */ continue; } db_bind_int(&q1, ":mid", p->rid); while( db_step(&q1)==SQLITE_ROW ){ fnid = db_column_int(&q1, 1); pfnid = db_column_int(&q1, 0); if( pfnid==0 ){ pfnid = fnid; fnid = 0; } if( !p->fromIsParent ){ int t = fnid; fnid = pfnid; pfnid = t; } if( zDebug ){ fossil_print("%s at %d%s %.10z: %d[%z] -> %d[%z]\n", zDebug, p->rid, p->fromIsParent ? ">" : "<", db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid), pfnid, db_text(0, "SELECT name FROM filename WHERE fnid=%d", pfnid), fnid, db_text(0, "SELECT name FROM filename WHERE fnid=%d", fnid)); } for(pChng=pAll; pChng; pChng=pChng->pNext){ if( pChng->curName==pfnid ){ pChng->newName = fnid; break; } } if( pChng==0 && fnid>0 ){ pChng = fossil_malloc( sizeof(*pChng) ); pChng->pNext = pAll; pAll = pChng; pChng->origName = pfnid; pChng->curName = pfnid; pChng->newName = fnid; nChng++; } } for(pChng=pAll; pChng; pChng=pChng->pNext){ pChng->curName = pChng->newName; } db_reset(&q1); } db_finalize(&q1); if( nChng ){ aChng = *aiChng = fossil_malloc( nChng*2*sizeof(int) ); for(pChng=pAll, i=0; pChng; pChng=pChng->pNext){ if( pChng->newName==0 ) continue; if( pChng->origName==0 ) continue; aChng[i] = pChng->origName; aChng[i+1] = pChng->newName; if( zDebug ){ fossil_print("%s summary %d[%z] -> %d[%z]\n", zDebug, aChng[i], db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i]), aChng[i+1], db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i+1])); } i += 2; } *pnChng = i/2; while( pAll ){ pChng = pAll; pAll = pAll->pNext; fossil_free(pChng); } } } /* ** COMMAND: test-name-changes ** ** Usage: %fossil test-name-changes [--debug] VERSION1 VERSION2 ** ** Show all filename changes that occur going from VERSION1 to VERSION2 */ void test_name_change(void){ int iFrom; int iTo; int *aChng; int nChng; int i; const char *zDebug = 0; int revOK = 0; db_find_and_open_repository(0,0); zDebug = find_option("debug",0,0)!=0 ? "debug" : 0; revOK = find_option("bidirectional",0,0)!=0; if( g.argc<4 ) usage("VERSION1 VERSION2"); while( g.argc>=4 ){ iFrom = name_to_rid(g.argv[2]); iTo = name_to_rid(g.argv[3]); find_filename_changes(iFrom, iTo, revOK, &nChng, &aChng, zDebug); fossil_print("------ Changes for (%d) %s -> (%d) %s\n", iFrom, g.argv[2], iTo, g.argv[3]); for(i=0; i<nChng; i++){ char *zFrom, *zTo; zFrom = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]); zTo = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2+1]); fossil_print("[%s] -> [%s]\n", zFrom, zTo); fossil_free(zFrom); fossil_free(zTo); } fossil_free(aChng); g.argv += 2; g.argc -= 2; } } /* Query to extract all rename operations */ static const char zRenameQuery[] = @ CREATE TEMP TABLE renames AS @ SELECT @ datetime(event.mtime) AS date, @ F.name AS old_name, @ T.name AS new_name, @ blob.uuid AS checkin @ 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; ; /* Query to extract distinct rename operations */ static const char zDistinctRenameQuery[] = @ CREATE TEMP TABLE renames AS @ SELECT @ min(datetime(event.mtime)) AS date, @ F.name AS old_name, @ T.name AS new_name, @ blob.uuid AS checkin @ 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 @ GROUP BY 2, 3; ; /* ** WEBPAGE: test-rename-list ** ** Print a list of all file rename operations throughout history. ** This page is intended for testing purposes only and may change ** or be discontinued without notice. */ void test_rename_list_page(void){ Stmt q; int nRename; int nCheckin; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_set_current_feature("test"); if( P("all")!=0 ){ style_header("List Of All Filename Changes"); db_multi_exec("%s", zRenameQuery/*safe-for-%s*/); style_submenu_element("Distinct", "%R/test-rename-list"); }else{ style_header("List Of Distinct Filename Changes"); db_multi_exec("%s", zDistinctRenameQuery/*safe-for-%s*/); style_submenu_element("All", "%R/test-rename-list?all"); } nRename = db_int(0, "SELECT count(*) FROM renames;"); nCheckin = db_int(0, "SELECT count(DISTINCT checkin) FROM renames;"); db_prepare(&q, "SELECT date, old_name, new_name, checkin FROM renames" " ORDER BY date DESC, old_name ASC"); @ <h1>%d(nRename) filename changes in %d(nCheckin) check-ins</h1> @ <table class='sortable' data-column-types='tttt' data-init-sort='1'\ @ border="1" cellpadding="2" cellspacing="0"> @ <thead><tr><th>Date & Time</th> @ <th>Old Name</th> @ <th>New Name</th> @ <th>Check-in</th></tr></thead><tbody> 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> } @ </tbody></table> db_finalize(&q); style_table_sorter(); style_finish_page(); } |
Added src/piechart.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 | /* ** 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/ ** ******************************************************************************* ** ** This file contains code for generating pie charts on web pages. ** */ #include "config.h" #include "piechart.h" #include <math.h> #ifndef M_PI # define M_PI 3.1415926535897932385 #endif /* ** Return an RGB color name given HSV values. The HSV values ** must each be between between 0 and 255. The string ** returned is held in a static buffer and is overwritten ** on each call. */ const char *rgbName(unsigned char h, unsigned char s, unsigned char v){ static char zColor[8]; unsigned char A, B, C, r, g, b; unsigned int i, m; if( s==0 ){ r = g = b = v; }else{ i = (h*6)/256; m = (h*6)&0xff; A = v*(256-s)/256; B = v*(65536-s*m)/65536; C = v*(65536-s*(256-m))/65536; @ <!-- hsv=%d(h),%d(s),%d(v) i=%d(i) m=%d(m) ABC=%d(A),%d(B),%d(C) --> switch( i ){ case 0: r=v; g=C; b=A; break; case 1: r=B; g=v; b=A; break; case 2: r=A; g=v; b=C; break; case 3: r=A; g=B; b=v; break; case 4: r=C; g=A; b=v; break; default: r=v; g=A; b=B; break; } } sqlite3_snprintf(sizeof(zColor),zColor,"#%02x%02x%02x",r,g,b); return zColor; } /* ** Flags that can be passed into the pie-chart generator */ #if INTERFACE #define PIE_OTHER 0x0001 /* No wedge less than 1/60th of the circle */ #define PIE_CHROMATIC 0x0002 /* Wedge colors are in chromatic order */ #define PIE_PERCENT 0x0004 /* Add "(XX%)" marks on each label */ #endif /* ** A pie-chart wedge label */ struct WedgeLabel { double rCos, rSin; /* Sine and Cosine of center angle of wedge */ char *z; /* Label to draw on this wedge */ }; typedef struct WedgeLabel WedgeLabel; /* ** Comparison callback for qsort() to sort labels in order of increasing ** distance above and below the horizontal centerline. */ static int wedgeCompare(const void *a, const void *b){ const WedgeLabel *pA = (const WedgeLabel*)a; const WedgeLabel *pB = (const WedgeLabel*)b; double rA = fabs(pA->rCos); double rB = fabs(pB->rCos); if( rA<rB ) return -1; if( rA>rB ) return +1; return 0; } /* ** Output HTML that will render a pie chart using data from ** the PIECHART temporary table. ** ** The schema for the PIECHART table should be: ** ** CREATE TEMP TABLE piechart(amt REAL, label TEXT); */ void piechart_render(int width, int height, unsigned int pieFlags){ Stmt q; double cx, cy; /* center of the pie */ double r, r2; /* Radius of the pie */ double x1,y1; /* Start of the slice */ double x2,y2; /* End of the slice */ double x3,y3; /* Middle point of the slice */ double x4,y4; /* End of line extending from x3,y3 */ double x5,y5; /* Text anchor */ double d1; /* radius to x4,y4 */ const char *zAnc; /* Anchor point for text */ double a1 = 0.0; /* Angle for first edge of slice */ double a2; /* Angle for second edge */ double a3; /* Angle at middle of slice */ unsigned char h; /* Hue */ const char *zClr; /* Color */ int l; /* Large arc flag */ int j; /* Wedge number */ double rTotal; /* Total piechart.amt */ double rTooSmall; /* Sum of pieChart.amt entries less than 1/60th */ int nTotal; /* Total number of entries in piechart */ int nTooSmall; /* Number of pieChart.amt entries less than 1/60th */ const char *zFg; /* foreground color for lines and text */ int nWedgeAlloc = 0; /* Slots allocated for aWedge[] */ int nWedge = 0; /* Slots used for aWedge[] */ WedgeLabel *aWedge = 0; /* Labels */ double rUprRight; /* Floor for next label in the upper right quadrant */ double rUprLeft; /* Floor for next label in the upper left quadrant */ double rLwrRight; /* Ceiling for label in the lower right quadrant */ double rLwrLeft; /* Ceiling for label in the lower left quadrant */ int i; /* Loop counter looping over wedge labels */ # define SATURATION 128 # define VALUE 192 # define OTHER_CUTOFF 90.0 # define TEXT_HEIGHT 15.0 cx = 0.5*width; cy = 0.5*height; r2 = cx<cy ? cx : cy; r = r2 - 80.0; if( r<0.33333*r2 ) r = 0.33333*r2; h = 0; zFg = skin_detail_boolean("white-foreground") ? "white" : "black"; db_prepare(&q, "SELECT sum(amt), count(*) FROM piechart"); if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); return; } rTotal = db_column_double(&q, 0); nTotal = db_column_int(&q, 1); db_finalize(&q); rTooSmall = 0.0; nTooSmall = 0; if( (pieFlags & PIE_OTHER)!=0 && nTotal>1 ){ db_prepare(&q, "SELECT sum(amt), count(*) FROM piechart WHERE amt<:amt"); db_bind_double(&q, ":amt", rTotal/OTHER_CUTOFF); if( db_step(&q)==SQLITE_ROW ){ rTooSmall = db_column_double(&q, 0); nTooSmall = db_column_int(&q, 1); } db_finalize(&q); } if( nTooSmall>1 ){ db_prepare(&q, "SELECT amt, label FROM piechart WHERE amt>=:limit" " UNION ALL SELECT %.17g, '%d others';", rTooSmall, nTooSmall); db_bind_double(&q, ":limit", rTotal/OTHER_CUTOFF); nTotal += 1 - nTooSmall; }else{ db_prepare(&q, "SELECT amt, label FROM piechart"); } if( nTotal<=10 ) pieFlags |= PIE_CHROMATIC; for(j=0; db_step(&q)==SQLITE_ROW; j++){ double x = db_column_double(&q,0)/rTotal; const char *zLbl = db_column_text(&q,1); /* @ <!-- x=%g(x) zLbl="%h(zLbl)" h=%d(h) --> */ if( x<=0.0 ) continue; x1 = cx + sin(a1)*r; y1 = cy - cos(a1)*r; a2 = a1 + x*2.0*M_PI; x2 = cx + sin(a2)*r; y2 = cy - cos(a2)*r; a3 = 0.5*(a1+a2); if( nWedge+1>nWedgeAlloc ){ nWedgeAlloc = nWedgeAlloc*2 + 40; aWedge = fossil_realloc(aWedge, sizeof(aWedge[0])*nWedgeAlloc); } if( pieFlags & PIE_PERCENT ){ int pct = (int)(x*100.0 + 0.5); aWedge[nWedge].z = mprintf("%s (%d%%)", zLbl, pct); }else{ aWedge[nWedge].z = fossil_strdup(zLbl); } aWedge[nWedge].rSin = sin(a3); aWedge[nWedge].rCos = cos(a3); nWedge++; if( (j&1)==0 || (pieFlags & PIE_CHROMATIC)!=0 ){ h = 256*j/nTotal; }else if( j+2<nTotal ){ h = 256*(j+2)/nTotal; }else{ h = 256*((j+2+(nTotal&1))%nTotal)/nTotal; } zClr = rgbName(h,SATURATION,VALUE); l = x>=0.5; a1 = a2; @ <path class='piechartWedge' @ stroke="black" stroke-width="1" fill="%s(zClr)" @ d='M%g(cx),%g(cy)L%g(x1),%g(y1)A%g(r),%g(r) 0 %d(l),1 %g(x2),%g(y2)z'/> } qsort(aWedge, nWedge, sizeof(aWedge[0]), wedgeCompare); rUprLeft = height; rLwrLeft = 0; rUprRight = height; rLwrRight = 0; d1 = r*1.1; for(i=0; i<nWedge; i++){ WedgeLabel *p = &aWedge[i]; x3 = cx + p->rSin*r; y3 = cy - p->rCos*r; x4 = cx + p->rSin*d1; y4 = cy - p->rCos*d1; if( y4<=cy ){ if( x4>=cx ){ if( y4>rUprRight ){ y4 = rUprRight; } rUprRight = y4 - TEXT_HEIGHT; }else{ if( y4>rUprLeft ){ y4 = rUprLeft; } rUprLeft = y4 - TEXT_HEIGHT; } }else{ if( x4>=cx ){ if( y4<rLwrRight ){ y4 = rLwrRight; } rLwrRight = y4 + TEXT_HEIGHT; }else{ if( y4<rLwrLeft ){ y4 = rLwrLeft; } rLwrLeft = y4 + TEXT_HEIGHT; } } if( x4<cx ){ x5 = x4 - 1.0; zAnc = "end"; }else{ x5 = x4 + 1.0; zAnc = "start"; } y5 = y4 - 3.0 + 6.0*(1.0 - p->rCos); @ <line stroke-width='1' stroke='%s(zFg)' class='piechartLine' @ x1='%g(x3)' y1='%g(y3)' x2='%g(x4)' y2='%g(y4)'/> @ <text text-anchor="%s(zAnc)" fill='%s(zFg)' class="piechartLabel" @ x='%g(x5)' y='%g(y5)'>%h(p->z)</text> fossil_free(p->z); } db_finalize(&q); fossil_free(aWedge); } /* ** WEBPAGE: test-piechart ** ** Generate a pie-chart based on data input from a form. */ void piechart_test_page(void){ const char *zData; Stmt ins; int n = 0; int width; int height; int i, j; login_check_credentials(); style_set_current_feature("test"); style_header("Pie Chart Test"); db_multi_exec("CREATE TEMP TABLE piechart(amt REAL, label TEXT);"); db_prepare(&ins, "INSERT INTO piechart(amt,label) VALUES(:amt,:label)"); zData = PD("data",""); width = atoi(PD("width","800")); height = atoi(PD("height","400")); i = 0; while( zData[i] ){ double rAmt; char *zLabel; while( fossil_isspace(zData[i]) ){ i++; } j = i; while( fossil_isdigit(zData[j]) ){ j++; } if( zData[j]=='.' ){ j++; while( fossil_isdigit(zData[j]) ){ j++; } } if( i==j ) break; rAmt = atof(&zData[i]); i = j; while( zData[i]==',' || fossil_isspace(zData[i]) ){ i++; } n++; zLabel = mprintf("label%02d-%g", n, rAmt); db_bind_double(&ins, ":amt", rAmt); db_bind_text(&ins, ":label", zLabel); db_step(&ins); db_reset(&ins); fossil_free(zLabel); } db_finalize(&ins); if( n>1 ){ @ <svg width=%d(width) height=%d(height) style="border:1px solid #d3d3d3;"> piechart_render(width,height, PIE_OTHER|PIE_PERCENT); @ </svg> @ <hr> } @ <form method="POST" action='%R/test-piechart'> @ <p>Comma-separated list of slice widths:<br> @ <input type='text' name='data' size='80' value='%h(zData)'/><br> @ Width: <input type='text' size='8' name='width' value='%d(width)'/> @ Height: <input type='text' size='8' name='height' value='%d(height)'/><br> @ <input type='submit' value='Draw The Pie Chart'/> @ </form> @ <p>Interesting test cases: @ <ul> @ <li> <a href='test-piechart?data=44,2,2,2,2,2,3,2,2,2,2,2,44'>Case 1</a> @ <li> <a href='test-piechart?data=2,2,2,2,2,44,44,2,2,2,2,2'>Case 2</a> @ <li> <a href='test-piechart?data=20,2,2,2,2,2,2,2,2,2,2,80'>Case 3</a> @ <li> <a href='test-piechart?data=80,2,2,2,2,2,2,2,2,2,2,20'>Case 4</a> @ <li> <a href='test-piechart?data=2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2'>Case 5</a> @ </ul> style_finish_page(); } |
Added src/pikchrshow.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 | /* ** Copyright (c) 2020 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 fossil-specific code related to pikchr. */ #include "config.h" #include <assert.h> #include <ctype.h> #include "pikchrshow.h" #if INTERFACE /* These are described in pikchr_process()'s docs. */ /* The first two must match the values from pikchr.c */ #define PIKCHR_PROCESS_PLAINTEXT_ERRORS 0x0001 #define PIKCHR_PROCESS_DARK_MODE 0x0002 /* end of flags supported directly by pikchr() */ #define PIKCHR_PROCESS_PASSTHROUGH 0x0003 /* Pass through these flags */ #define PIKCHR_PROCESS_TH1 0x0004 #define PIKCHR_PROCESS_TH1_NOSVG 0x0008 #define PIKCHR_PROCESS_NONCE 0x0010 #define PIKCHR_PROCESS_ERR_PRE 0x0020 #define PIKCHR_PROCESS_SRC 0x0040 #define PIKCHR_PROCESS_DIV 0x0080 #define PIKCHR_PROCESS_DIV_INDENT 0x0100 #define PIKCHR_PROCESS_DIV_CENTER 0x0200 #define PIKCHR_PROCESS_DIV_FLOAT_LEFT 0x0400 #define PIKCHR_PROCESS_DIV_FLOAT_RIGHT 0x0800 #define PIKCHR_PROCESS_DIV_TOGGLE 0x1000 #define PIKCHR_PROCESS_DIV_SOURCE 0x2000 #define PIKCHR_PROCESS_DIV_SOURCE_INLINE 0x4000 #endif /* ** Processes a pikchr script, optionally with embedded TH1, and ** produces HTML code for it. zIn is the NUL-terminated input ** script. pikFlags may be a bitmask of any of the PIKCHR_PROCESS_xxx ** flags documented below. thFlags may be a bitmask of any of the ** TH_INIT_xxx and/or TH_R2B_xxx flags. Output is sent to pOut, ** appending to it without modifying any prior contents. ** ** Returns 0 on success, 1 if TH1 processing failed, or 2 if pikchr ** processing failed. In either case, the error message (if any) from ** TH1 or pikchr will be appended to pOut. ** ** pikFlags flag descriptions: ** ** - PIKCHR_PROCESS_TH1 means to run zIn through TH1, using the TH1 ** init flags specified in the 3rd argument. If thFlags is non-0 then ** this flag is assumed even if it is not specified. ** ** - PIKCHR_PROCESS_TH1_NOSVG means that processing stops after the ** TH1 eval step, thus the output will be (presumably) a ** TH1-generated/processed pikchr script (or whatever else the TH1 ** outputs). If this flag is set, PIKCHR_PROCESS_TH1 is assumed even ** if it is not specified. ** ** All of the remaining flags listed below are ignored if ** PIKCHR_PROCESS_TH1_NOSVG is specified! ** ** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV ** element which specifies a max-width style value based on the SVG's ** calculated size. This flag has multiple mutually exclusive forms: ** ** - PIKCHR_PROCESS_DIV uses default element alignment. ** - PIKCHR_PROCESS_DIV_INDENT indents the div. ** - PIKCHR_PROCESS_DIV_CENTER centers the div. ** - PIKCHR_PROCESS_DIV_FLOAT_LEFT floats the div left. ** - PIKCHR_PROCESS_DIV_FLOAT_RIGHT floats the div right. ** ** If more than one is specified, which one is used is undefined. Those ** flags may be OR'd with one or both of the following: ** ** - PIKCHR_PROCESS_DIV_TOGGLE: adds the 'toggle' CSS class to the ** outer DIV so that event-handler code can install different ** toggling behaviour than the default. Default is ctrl-click, but ** this flag enables single-click toggling for the element. ** ** - PIKCHR_PROCESS_DIV_SOURCE: adds the 'source' CSS class to the ** outer DIV, which is a hint to the client-side renderer (see ** fossil.pikchr.js) that the pikchr should initially be rendered ** in source code form mode (the default is to hide the source and ** show the SVG). ** ** - PIKCHR_PROCESS_DIV_SOURCE_INLINE: adds the 'source-inline' CSS ** class to the outer wrapper. This modifier changes how the ** 'source' CSS class gets applied: with this flag, the source view ** should be rendered "inline" (same position as the graphic), else ** it is to be left-aligned. ** ** - PIKCHR_PROCESS_NONCE: if set, the resulting SVG/DIV are wrapped ** in "safe nonce" comments, which are a fossil-internal mechanism ** which prevents the wiki/markdown processors from re-processing this ** output. This is necessary when calling this routine in the context ** of wiki/embedded doc processing, but not (e.g.) when fetching ** an image for /pikchrpage. ** ** - PIKCHR_PROCESS_SRC: if set, a new PRE.pikchr-src element is ** injected adjacent to the SVG element which contains the ** HTML-escaped content of the input script. If ** PIKCHR_PROCESS_DIV_SOURCE or PIKCHR_PROCESS_DIV_SOURCE_INLINE is ** set, this flag is automatically implied. ** ** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting ** error report is wrapped in a PRE element, else it is retained ** as-is (intended only for console output). */ int pikchr_process(const char * zIn, int pikFlags, int thFlags, Blob * pOut){ Blob bIn = empty_blob; int isErr = 0; const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags) ? safe_html_nonce(1) : 0; if(!(PIKCHR_PROCESS_DIV & pikFlags) /* If any DIV_xxx flags are set, set DIV */ && (PIKCHR_PROCESS_DIV_INDENT | PIKCHR_PROCESS_DIV_CENTER | PIKCHR_PROCESS_DIV_FLOAT_RIGHT | PIKCHR_PROCESS_DIV_FLOAT_LEFT | PIKCHR_PROCESS_DIV_SOURCE | PIKCHR_PROCESS_DIV_SOURCE_INLINE | PIKCHR_PROCESS_DIV_TOGGLE ) & pikFlags){ pikFlags |= PIKCHR_PROCESS_DIV; } if(!(PIKCHR_PROCESS_TH1 & pikFlags) /* If any TH1_xxx flags are set, set TH1 */ && (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){ pikFlags |= PIKCHR_PROCESS_TH1; } if(zNonce){ blob_appendf(pOut, "%s\n", zNonce); } if(PIKCHR_PROCESS_TH1 & pikFlags){ Blob out = empty_blob; isErr = Th_RenderToBlob(zIn, &out, thFlags) ? 1 : 0; if(isErr){ blob_append(pOut, blob_str(&out), blob_size(&out)); blob_reset(&out); }else{ bIn = out; } }else{ blob_init(&bIn, zIn, -1); } if(!isErr){ if(PIKCHR_PROCESS_TH1_NOSVG & pikFlags){ blob_append(pOut, blob_str(&bIn), blob_size(&bIn)); }else{ int w = 0, h = 0; const char * zContent = blob_str(&bIn); char *zOut; zOut = pikchr(zContent, "pikchr", 0x01 | (pikFlags&PIKCHR_PROCESS_PASSTHROUGH), &w, &h); if( w>0 && h>0 ){ const char * zClassToggle = ""; const char * zClassSource = ""; const char * zWrapperClass = ""; if(PIKCHR_PROCESS_DIV & pikFlags){ if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){ zWrapperClass = " center"; }else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){ zWrapperClass = " indent"; }else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){ zWrapperClass = " float-left"; }else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){ zWrapperClass = " float-right"; } if(PIKCHR_PROCESS_DIV_TOGGLE & pikFlags){ zClassToggle = " toggle"; } if(PIKCHR_PROCESS_DIV_SOURCE_INLINE & pikFlags){ if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){ zClassSource = " source source-inline"; }else{ zClassSource = " source-inline"; } pikFlags |= PIKCHR_PROCESS_SRC; }else if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){ zClassSource = " source"; pikFlags |= PIKCHR_PROCESS_SRC; } blob_appendf(pOut,"<div class='pikchr-wrapper" "%s%s%s'>" "<div class=\"pikchr-svg\" " "style=\"max-width:%dpx\">\n", zWrapperClass/*safe-for-%s*/, zClassToggle/*safe-for-%s*/, zClassSource/*safe-for-%s*/, w); } blob_append(pOut, zOut, -1); if(PIKCHR_PROCESS_DIV & pikFlags){ blob_append(pOut, "</div>\n", 7); } if(PIKCHR_PROCESS_SRC & pikFlags){ blob_appendf(pOut, "<pre class='pikchr-src'>%h</pre>\n", blob_str(&bIn)); } if(PIKCHR_PROCESS_DIV & pikFlags){ blob_append(pOut, "</div>\n", 7); } }else{ isErr = 2; if(PIKCHR_PROCESS_ERR_PRE & pikFlags){ blob_append(pOut, "<pre class='error'>\n", 20); } blob_appendf(pOut, "%h", zOut); if(PIKCHR_PROCESS_ERR_PRE & pikFlags){ blob_append(pOut, "\n</pre>\n", 8); } } fossil_free(zOut); } } if(zNonce){ blob_appendf(pOut, "%s\n", zNonce); } blob_reset(&bIn); return isErr; } /* ** Legacy impl of /pikchrshow. pikchrshow_page() will delegate to ** this one if the "legacy" or "ajax" request arguments are set. ** ** A pikchr code editor and previewer, allowing users to experiment ** with pikchr code or prototype it for use in copy/pasting into forum ** posts, wiki pages, or embedded docs. This version of pikchrshow ** uses JavaScript to send pikchr code to the server for ** processing. The newer /pikchrshow applications runs pikchr on the ** client machine, without the need for back-and-forth network ** traffic. */ void pikchrshowcs_page(void){ const char *zContent = 0; int isDark; /* true if the current skin is "dark" */ int pikFlags = PIKCHR_PROCESS_DIV | PIKCHR_PROCESS_SRC | PIKCHR_PROCESS_ERR_PRE; login_check_credentials(); if( !g.perm.RdWiki && !g.perm.Read && !g.perm.RdForum ){ cgi_redirectf("%R/login?g=pikchrshowcs"); } if(P("wasm")){ pikchrshow_page(); return; } zContent = PD("content",P("p")); if(P("ajax")!=0){ /* Called from the JS-side preview updater. TODO: respond with JSON instead.*/ cgi_set_content_type("text/html"); if(zContent && *zContent){ Blob out = empty_blob; const int isErr = pikchr_process(zContent, pikFlags, 0, &out); if(isErr){ cgi_printf_header("x-pikchrshow-is-error: %d\r\n", isErr); } CX("%b", &out); blob_reset(&out); }else{ CX("<pre>No content! Nothing to render</pre>"); } return; }/*ajax response*/ style_emit_noscript_for_js_page(); isDark = skin_detail_boolean("white-foreground"); if(!zContent){ zContent = "arrow right 200% \"Markdown\" \"Source\"\n" "box rad 10px \"Markdown\" \"Formatter\" \"(markdown.c)\" fit\n" "arrow right 200% \"HTML+SVG\" \"Output\"\n" "arrow <-> down from last box.s\n" "box same \"Pikchr\" \"Formatter\" \"(pikchr.c)\" fit\n"; } style_header("PikchrShow Client/Server"); CX("<style>"); { CX("div.content { padding-top: 0.5em }\n"); CX("#sbs-wrapper {" "display: flex; flex-direction: column;" "}\n"); CX("#sbs-wrapper > * {" "margin: 0 0.25em 0.5em 0; flex: 1 10 auto;" "align-self: stretch;" "}\n"); CX("#sbs-wrapper textarea {" "max-width: initial; flex: 1 1 auto;" "}\n"); CX("#pikchrshow-output, #pikchrshow-form" "{display: flex; flex-direction: column; align-items: stretch;}"); CX("#pikchrshow-form > * {margin: 0.25em 0}\n"); CX("#pikchrshow-output {flex: 5 1 auto; padding: 0}\n"); CX("#pikchrshow-output > pre, " "#pikchrshow-output > pre > div, " "#pikchrshow-output > pre > div > pre " "{margin: 0; padding: 0}\n"); CX("#pikchrshow-output.error > pre " /* Server-side error report */ "{padding: 0.5em}\n"); CX("#pikchrshow-controls {" /* where the buttons live */ "display: flex; flex-direction: row; " "align-items: center; flex-wrap: wrap;" "}\n"); CX("#pikchrshow-controls > * {" "display: inline; margin: 0 0.25em 0.5em 0;" "}\n"); CX("#pikchrshow-output-wrapper label {" "cursor: pointer;" "}\n"); CX("body.pikchrshow .input-with-label > * {" "margin: 0 0.2em;" "}\n"); CX("body.pikchrshow .input-with-label > label {" "cursor: pointer;" "}\n"); CX("#pikchrshow-output.dark-mode svg {" /* Flip the colors to approximate a dark theme look */ "filter: invert(1) hue-rotate(180deg);" "}\n"); CX("#pikchrshow-output-wrapper {" "padding: 0.25em 0.5em; border-radius: 0.25em;" "border-width: 1px;"/*some skins disable fieldset borders*/ "}\n"); CX("#pikchrshow-output-wrapper > legend > *:not(.copy-button){" "margin-right: 0.5em; vertical-align: middle;" "}\n"); CX("body.pikchrshow .v-align-middle{" "vertical-align: middle" "}\n"); CX(".dragover {border: 3px dotted rgba(0,255,0,0.6)}\n"); } CX("</style>"); CX("<div>Input pikchr code and tap Preview (or Shift-Enter) to render " "it. <a href='?wasm'>Switch to WASM mode</a>.</div>"); CX("<div id='sbs-wrapper'>"); { CX("<div id='pikchrshow-form'>"); { CX("<textarea id='content' name='content' rows='15'>" "%s</textarea>",zContent/*safe-for-%s*/); CX("<div id='pikchrshow-controls'>"); { CX("<button id='pikchr-submit-preview'>Preview</button>"); CX("<div class='input-with-label'>"); { CX("<button id='pikchr-stash'>Stash</button>"); CX("<button id='pikchr-unstash'>Unstash</button>"); CX("<button id='pikchr-clear-stash'>Clear stash</button>"); CX("<span>Stores/restores a single pikchr script to/from " "browser-local storage from/to the editor.</span>" /* gets turned into a help-buttonlet */); } CX("</div>"/*stash controls*/); style_labeled_checkbox("flipcolors-wrapper", "flipcolors", "Dark mode?", "1", isDark, 0); } CX("</div>"/*#pikchrshow-controls*/); } CX("</div>"/*#pikchrshow-form*/); CX("<fieldset id='pikchrshow-output-wrapper'>"); { CX("<legend></legend>" /* Reminder: Firefox does not properly flexbox a LEGEND element, always flowing it in column mode. */); CX("<div id='pikchrshow-output'>"); if(*zContent){ Blob out = empty_blob; pikchr_process(zContent, pikFlags, 0, &out); CX("%b", &out); blob_reset(&out); } CX("</div>"/*#pikchrshow-output*/); } CX("</fieldset>"/*#pikchrshow-output-wrapper*/); } CX("</div>"/*sbs-wrapper*/); builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget", "storage", "pikchr", NULL); builtin_request_js("fossil.page.pikchrshow.js"); builtin_fulfill_js_requests(); style_finish_page(); } /* ** WEBPAGE: pikchrshow ** ** A pikchr code editor and previewer, allowing users to experiment ** with pikchr code or prototype it for use in copy/pasting into forum ** posts, wiki pages, or embedded docs. This version of pikchrshow ** uses WebAssembly to run entirely in the client browser, without a ** need for back-and-forth client/server traffic to perform the ** rendering. The "legacy" version of this application, which sends ** all input to the server for rendering, can be accessed by adding ** the "legacy" URL argument. ** ** It optionally accepts a p=pikchr-script-code URL parameter or POST ** value to pre-populate the editor with that code. */ void pikchrshow_page(void){ const char *zContent = 0; if(P("legacy") || P("ajax")){ pikchrshowcs_page(); return; } login_check_credentials(); if( !g.perm.RdWiki && !g.perm.Read && !g.perm.RdForum ){ cgi_redirectf("%R/login?g=pikchrshow"); } style_emit_noscript_for_js_page(); style_header("PikchrShow"); zContent = PD("content",P("p")); if(!zContent){ zContent = "arrow right 200% \"Markdown\" \"Source\"\n" "box rad 10px \"Markdown\" \"Formatter\" \"(markdown.c)\" fit\n" "arrow right 200% \"HTML+SVG\" \"Output\"\n" "arrow <-> down from last box.s\n" "box same \"Pikchr\" \"Formatter\" \"(pikchr.c)\" fit\n"; } /* Wasm load/init progress widget... */ CX("<div class='emscripten'>"); { CX("<figure id='module-spinner'>"); CX("<div class='spinner'></div>"); CX("<div class='center'><strong>Initializing app...</strong></div>"); CX("<div class='center'>"); CX("On a slow internet connection this may take a moment. If this "); CX("message displays for \"a long time\", initialization may have "); CX("failed and the JavaScript console may contain clues as to why. "); CX("</div>"); CX("<div><a href='?legacy'>Switch to legacy mode</a></div>"); CX("</figure>"); CX("<div class='emscripten' id='module-status'>Downloading...</div>"); CX("<progress value='0' max='100' id='module-progress' hidden='1'>" "</progress>"); } CX("</div><!-- .emscripten -->"); /* Main view... */ CX("<div id='view-split' class='app-view initially-hidden'>"); { CX("<fieldset class='options collapsible'>"); { CX("<legend><button class='fieldset-toggle'>Options</button></legend>"); CX("<div>"); CX("<span class='labeled-input'>"); CX("<input type='checkbox' id='opt-cb-sbs' "); CX("data-csstgt='#main-wrapper' "); CX("data-cssclass='side-by-side' "); CX("data-config='sideBySide'>"); CX("<label for='opt-cb-sbs'>Side-by-side</label>"); CX("</span>"); CX("<span class='labeled-input'>"); CX("<input type='checkbox' id='opt-cb-swapio' "); CX("data-csstgt='#main-wrapper' "); CX("data-cssclass='swapio' "); CX("data-config='swapInOut'>"); CX("<label for='opt-cb-swapio'>Swap in/out</label>"); CX("</span>"); CX("<span class='labeled-input'>"); CX("<input type='checkbox' id='opt-cb-autofit' "); CX("data-config='renderAutofit'>"); CX("<label for='opt-cb-autofit' " "title='Attempt to scale SVG to fit viewport. " "Whether it will work depends in part on the size " "and shape of the image and the viewport.'" ">Auto-fit SVG</label>"); CX("</span>"); CX("<span class='labeled-input'>"); CX("<input type='checkbox' id='opt-cb-autorender' "); CX("data-csstgt='#main-wrapper' "); CX("data-cssclass='auto-render' "); CX("data-config='renderWhileTyping'>"); CX("<label for='opt-cb-autorender'>Render while typing</label>"); CX("</span>"); CX("<span class='labeled-input'>"); CX("<a href='?legacy'>Legacy mode</a>"); CX("</span>"); CX("</div><!-- options wrapper -->"); } CX("</fieldset>"); CX("<div id='main-wrapper' class=''>"); { CX("<fieldset class='zone-wrapper input'>"); { CX("<legend><div class='button-bar'>"); CX("<button id='btn-render' " "title='Ctrl-Enter/Shift-Enter'>Render</button>"); CX("<button id='btn-clear'>Clear Input</button>"); CX("</div></legend>"); CX("<div><textarea id='input'"); CX("placeholder='Pikchr input. Ctrl-enter/shift-enter runs it.'>"); CX("/**\n"); CX(" Use ctrl-enter or shift-enter to execute\n"); CX(" pikchr code. If only a subset is currently\n"); CX(" selected, only that part is evaluated.\n*/\n"); CX("%s</textarea></div>",zContent/*safe-for-%s*/); } CX("</fieldset><!-- .zone-wrapper.input -->"); CX("<fieldset class='zone-wrapper output'>"); { CX("<legend><div class='button-bar'>"); CX("<button id='btn-render-mode'>Render Mode</button> "); CX("<span style='white-space:nowrap'>" "<span id='preview-copy-button' " "title='Tap to copy to clipboard.'></span>" "<label for='preview-copy-button' " "title='Tap to copy to clipboard.'></label>" "</span>"); CX("</div></legend>"); CX("<div id='pikchr-output-wrapper'>"); CX("<div id='pikchr-output'></div>"); CX("<textarea class='hidden' id='pikchr-output-text'></textarea>"); CX("</div>"); } CX("</fieldset> <!-- .zone-wrapper.output -->"); } CX("</div><!-- #main-wrapper -->"); } CX("</div><!-- #view-split -->"); builtin_fossil_js_bundle_or("dom", "storage", "copybutton", NULL); builtin_request_js("fossil.page.pikchrshowasm.js"); builtin_fulfill_js_requests(); style_finish_page(); } /* ** COMMAND: pikchr* ** ** Usage: %fossil pikchr [options] ?INFILE? ?OUTFILE? ** ** Accepts a pikchr script as input and outputs the rendered script as ** an SVG graphic. The INFILE and OUTFILE options default to stdin ** resp. stdout, and the names "-" can be used as aliases for those ** streams. ** ** Options: ** -div On success, add a DIV wrapper around the ** resulting SVG output which limits its max-width to ** its computed maximum ideal size ** ** -div-indent Like -div but indent the div ** ** -div-center Like -div but center the div ** ** -div-left Like -div but float the div left ** ** -div-right Like -div but float the div right ** ** -div-toggle Set the 'toggle' CSS class on the div (used by the ** JavaScript-side post-processor) ** ** -div-source Set the 'source' CSS class on the div, which tells ** CSS to hide the SVG and reveal the source by default. ** ** -src Store the input pikchr's source code in the output as ** a separate element adjacent to the SVG one. Implied ** by -div-source. ** ** ** -th Process the input using TH1 before passing it to pikchr ** ** -th-novar Disable $var and $<var> TH1 processing. Use this if the ** pikchr script uses '$' for its own purposes and that ** causes issues. This only affects parsing of '$' outside ** of TH1 script blocks. Code in such blocks is unaffected. ** ** -th-nosvg When using -th, output the post-TH1'd script ** instead of the pikchr-rendered output ** ** -th-trace Trace TH1 execution (for debugging purposes) ** ** -dark Change pikchr colors to assume a dark-mode theme. ** ** ** The -div-indent/center/left/right flags may not be combined. ** ** TH1-related Notes and Caveats: ** ** If the -th flag is used, this command must open a fossil database ** for certain functionality to work (via a check-out or the -R REPO ** flag). If opening a db fails, execution will continue but any TH1 ** commands which require a db will trigger a fatal error. ** ** In Fossil skins, TH1 variables in the form $varName are expanded ** as-is and those in the form $<varName> are htmlized in the ** resulting output. This processor disables the htmlizing step, so $x ** and $<x> are equivalent unless the TH1-processed pikchr script ** invokes the TH1 command [enable_htmlify 1] to enable it. Normally ** that option will interfere with pikchr output, however, e.g. by ** HTML-encoding double-quotes. ** ** Many of the fossil-installed TH1 functions simply do not make any ** sense for pikchr scripts. */ void pikchr_cmd(void){ Blob bIn = empty_blob; Blob bOut = empty_blob; const char * zInfile = "-"; const char * zOutfile = "-"; const int fTh1 = find_option("th",0,0)!=0; const int fNosvg = find_option("th-nosvg",0,0)!=0; int isErr = 0; int pikFlags = find_option("src",0,0)!=0 ? PIKCHR_PROCESS_SRC : 0; u32 fThFlags = TH_INIT_NO_ENCODE | (find_option("th-novar",0,0)!=0 ? TH_R2B_NO_VARS : 0); Th_InitTraceLog()/*processes -th-trace flag*/; if(find_option("div",0,0)!=0){ pikFlags |= PIKCHR_PROCESS_DIV; }else if(find_option("div-indent",0,0)!=0){ pikFlags |= PIKCHR_PROCESS_DIV_INDENT; }else if(find_option("div-center",0,0)!=0){ pikFlags |= PIKCHR_PROCESS_DIV_CENTER; }else if(find_option("div-float-left",0,0)!=0){ pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT; }else if(find_option("div-float-right",0,0)!=0){ pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT; } if(find_option("div-toggle",0,0)!=0){ pikFlags |= PIKCHR_PROCESS_DIV_TOGGLE; } if(find_option("div-source",0,0)!=0){ pikFlags |= PIKCHR_PROCESS_DIV_SOURCE | PIKCHR_PROCESS_SRC; } if(find_option("dark",0,0)!=0){ pikFlags |= PIKCHR_PROCESS_DARK_MODE; } verify_all_options(); if(g.argc>4){ usage("?INFILE? ?OUTFILE?"); } if(g.argc>2){ zInfile = g.argv[2]; } if(g.argc>3){ zOutfile = g.argv[3]; } blob_read_from_file(&bIn, zInfile, ExtFILE); if(fTh1){ db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0) /* ^^^ needed for certain TH1 functions to work */; pikFlags |= PIKCHR_PROCESS_TH1; if(fNosvg) pikFlags |= PIKCHR_PROCESS_TH1_NOSVG; } isErr = pikchr_process(blob_str(&bIn), pikFlags, fTh1 ? fThFlags : 0, &bOut); if(isErr){ fossil_fatal("%s ERROR:%c%b", 1==isErr ? "TH1" : "pikchr", 1==isErr ? ' ' : '\n', &bOut); }else{ blob_write_to_file(&bOut, zOutfile); } Th_PrintTraceLog(); blob_reset(&bIn); blob_reset(&bOut); } |
Changes to src/pivot.c.
︙ | ︙ | |||
28 29 30 31 32 33 34 | ** Set the primary file. The primary version is one of the two ** files that have a common ancestor. The other file is the secondary. ** There can be multiple secondaries but only a single primary. ** The primary must be set first. ** ** In the merge algorithm, the file being merged in is the primary. ** The current check-out or other files that have been merged into | | | | > | | | | | | > > | | | | > | | | > | 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 | ** Set the primary file. The primary version is one of the two ** files that have a common ancestor. The other file is the secondary. ** There can be multiple secondaries but only a single primary. ** The primary must be set first. ** ** In the merge algorithm, the file being merged in is the primary. ** The current check-out or other files that have been merged into ** the current check-out are the secondaries. ** ** The act of setting the primary resets the pivot-finding algorithm. */ void pivot_set_primary(int rid){ /* Set up table used to do the search */ db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS aqueue(" " rid INTEGER," /* The record id for this version */ " mtime REAL," /* Time when this version was created */ " pending BOOLEAN," /* True if we have not check this one yet */ " src BOOLEAN," /* 1 for primary. 0 for others */ " PRIMARY KEY(rid,src)" ") WITHOUT ROWID;" "DELETE FROM aqueue;" "CREATE INDEX IF NOT EXISTS aqueue_idx1 ON aqueue(pending, mtime);" ); /* Insert the primary record */ db_multi_exec( "INSERT INTO aqueue(rid, mtime, pending, src)" " SELECT %d, mtime, 1, 1 FROM event WHERE objid=%d AND type='ci' LIMIT 1", rid, rid ); } /* ** Set a secondary file. The primary file must be set first. There ** must be at least one secondary but there can be more than one if ** desired. */ void pivot_set_secondary(int rid){ /* Insert the secondary record */ db_multi_exec( "INSERT OR IGNORE INTO aqueue(rid, mtime, pending, src)" " SELECT %d, mtime, 1, 0 FROM event WHERE objid=%d AND type='ci'", rid, rid ); } /* ** Find the most recent common ancestor of the primary and one of ** the secondaries. Return its rid. Return 0 if no common ancestor ** can be found. ** ** If ignoreMerges is true, follow only "primary" parent links. */ int pivot_find(int ignoreMerges){ Stmt q1, q2, u1, i1; int rid = 0; /* aqueue must contain at least one primary and one other. Otherwise ** we abort early */ if( db_int(0, "SELECT count(distinct src) FROM aqueue")<2 ){ fossil_fatal("lack both primary and secondary files"); } /* Prepare queries we will be needing ** ** The first query finds the oldest pending version on the aqueue. This ** will be next one searched. */ db_prepare(&q1, "SELECT rid FROM aqueue WHERE pending" " ORDER BY pending DESC, mtime DESC"); /* Check to see if the record :rid is a common ancestor. The result ** set contains one or more rows if it is and is the empty set if it ** is not. */ db_prepare(&q2, "SELECT 1 FROM aqueue A, plink, aqueue B" " WHERE plink.pid=:rid" " AND plink.cid=B.rid" " AND A.rid=:rid" " AND A.src!=B.src %s", ignoreMerges ? "AND plink.isprim" : "" ); /* Mark the :rid record has having been checked. It is not the ** common ancestor. */ db_prepare(&u1, "UPDATE aqueue SET pending=0 WHERE rid=:rid" ); /* Add to the queue all ancestors of :rid. */ db_prepare(&i1, "REPLACE INTO aqueue " "SELECT plink.pid," " coalesce((SELECT mtime FROM event X WHERE X.objid=plink.pid), 0.0)," " 1," " aqueue.src " " FROM plink, aqueue" " WHERE plink.cid=:rid" " AND aqueue.rid=:rid %s", ignoreMerges ? "AND plink.isprim" : "" ); while( db_step(&q1)==SQLITE_ROW ){ rid = db_column_int(&q1, 0); db_reset(&q1); db_bind_int(&q2, ":rid", rid); if( db_step(&q2)==SQLITE_ROW ){ |
︙ | ︙ | |||
145 146 147 148 149 150 151 | db_finalize(&q2); db_finalize(&i1); db_finalize(&u1); return rid; } /* | | > > > > > | > | > > > | | > > > | | > > > > > > > > | > > > > > > > | > > > | 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 | db_finalize(&q2); db_finalize(&i1); db_finalize(&u1); return rid; } /* ** COMMAND: merge-base ** ** Usage: %fossil merge-base ?options? PRIMARY SECONDARY ... ** ** Find a common ancestor given two or more check-in versions to ** hypothetically merge. ** ** Options: ** --ignore-merges Ignore merges for discovering name pivots */ void merge_base_cmd(void){ int i, rid; int ignoreMerges = find_option("ignore-merges",0,0)!=0; int showDetails = find_option("details",0,0)!=0 /* intentionally undocumented */; if( g.argc<4 ){ usage("?options? PRIMARY SECONDARY ..."); } db_must_be_within_tree(); pivot_set_primary(name_to_rid(g.argv[2])); for(i=3; i<g.argc; i++){ pivot_set_secondary(name_to_rid(g.argv[i])); } rid = pivot_find(ignoreMerges); if( rid==0 ){ puts("No common ancestor found."); }else{ printf("pivot=%s\n", db_text("?","SELECT uuid FROM blob WHERE rid=%d",rid)); } if( showDetails ){ Stmt q; db_prepare(&q, "SELECT substr(uuid,1,12), aqueue.rid, datetime(aqueue.mtime)," " aqueue.pending, aqueue.src\n" " FROM aqueue JOIN blob ON aqueue.rid=blob.rid\n" " ORDER BY aqueue.mtime DESC" ); while( db_step(&q)==SQLITE_ROW ){ printf("\"%s\",%d,\"%s\",%d,%d\n", db_column_text(&q, 0), db_column_int(&q, 1), db_column_text(&q, 2), db_column_int(&q, 3), db_column_int(&q, 4)); } db_finalize(&q); } } |
Added src/popen.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 | /* ** Copyright (c) 2010 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 an implementation of a bi-directional popen(). */ #include "config.h" #include "popen.h" #ifdef _WIN32 #include <windows.h> #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. ** ** 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. ** ** This code is copied out of SQLite. */ #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ # define INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) # define PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) #elif !defined(__GNUC__) /* Works for compilers other than LLVM */ # define INT_TO_PTR(X) ((void*)&((char*)0)[X]) # define PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */ # define INT_TO_PTR(X) ((void*)(intptr_t)(X)) # define PTR_TO_INT(X) ((int)(intptr_t)(X)) #else /* Generates a warning - but it always works */ # define INT_TO_PTR(X) ((void*)(X)) # define PTR_TO_INT(X) ((int)(X)) #endif #ifdef _WIN32 /* ** On windows, create a child process and specify the stdin, stdout, ** and stderr channels for that process to use. ** ** Return the number of errors. */ static int win32_create_child_process( wchar_t *zCmd, /* The command that the child process will run */ HANDLE hIn, /* Standard input */ HANDLE hOut, /* Standard output */ HANDLE hErr, /* Standard error */ DWORD *pChildPid /* OUT: Child process handle */ ){ STARTUPINFOW si; PROCESS_INFORMATION pi; BOOL rc; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; SetHandleInformation(hIn, HANDLE_FLAG_INHERIT, TRUE); si.hStdInput = hIn; SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, TRUE); si.hStdOutput = hOut; SetHandleInformation(hErr, HANDLE_FLAG_INHERIT, TRUE); si.hStdError = hErr; rc = CreateProcessW( NULL, /* Application Name */ zCmd, /* Command-line */ NULL, /* Process attributes */ NULL, /* Thread attributes */ TRUE, /* Inherit Handles */ 0, /* Create flags */ NULL, /* Environment */ NULL, /* Current directory */ &si, /* Startup Info */ &pi /* Process Info */ ); if( rc ){ CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); *pChildPid = pi.dwProcessId; }else{ win32_fatal_error("cannot create child process"); } return rc!=0; } #endif /* ** Create a child process running shell command "zCmd". *ppOut is ** a FILE that becomes the standard input of the child process. ** (The caller writes to *ppOut in order to send text to the child.) ** *ppIn is stdout from the child process. (The caller ** reads from *ppIn in order to receive input from the child.) ** Note that *ppIn is an unbuffered file descriptor, not a FILE. ** The process ID of the child is written into *pChildPid. ** ** Return the number of errors. */ int popen2( const char *zCmd, /* Command to run in the child process */ int *pfdIn, /* Read from child using this file descriptor */ FILE **ppOut, /* Write to child using this file descriptor */ int *pChildPid, /* PID of the child process */ int bDirect /* 0: run zCmd as a shell cmd. 1: run directly */ ){ #ifdef _WIN32 HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr; SECURITY_ATTRIBUTES saAttr; DWORD childPid = 0; int fd; saAttr.nLength = sizeof(saAttr); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; hStderr = GetStdHandle(STD_ERROR_HANDLE); if( !CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 4096) ){ win32_fatal_error("cannot create pipe for stdout"); } SetHandleInformation( hStdoutRd, HANDLE_FLAG_INHERIT, FALSE); if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){ win32_fatal_error("cannot create pipe for stdin"); } SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE); win32_create_child_process(fossil_utf8_to_unicode(zCmd), hStdinRd, hStdoutWr, hStderr,&childPid); *pChildPid = childPid; *pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0); fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0); *ppOut = _fdopen(fd, "w"); CloseHandle(hStdinRd); CloseHandle(hStdoutWr); return 0; #else int pin[2], pout[2]; *pfdIn = 0; *ppOut = 0; *pChildPid = 0; if( pipe(pin)<0 ){ return 1; } if( pipe(pout)<0 ){ close(pin[0]); close(pin[1]); return 1; } *pChildPid = fork(); if( *pChildPid<0 ){ 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; /* This is the child process */ close(0); fd = dup(pout[0]); if( fd!=0 ) fossil_panic("popen() failed to open file descriptor 0"); close(pout[0]); close(pout[1]); close(1); fd = dup(pin[1]); if( fd!=1 ) fossil_panic("popen() failed to open file descriptor 1"); close(pin[0]); close(pin[1]); if( bDirect ){ execl(zCmd, zCmd, (char*)0); }else{ execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0); } return 1; }else{ /* This is the parent process */ close(pin[1]); *pfdIn = pin[0]; close(pout[0]); *ppOut = fdopen(pout[1], "w"); 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/pqueue.c.
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** value. We can insert integers with each integer tied to its ** value then extract the integer with the smallest value. ** ** The way this queue is used, we never expect it to contain more ** than 2 or 3 elements, so a simple array is sufficient as the ** implementation. This could give worst case O(N) insert times, ** but because of the nature of the problem we expect O(1) performance. */ #include "config.h" #include "pqueue.h" #include <assert.h> #if INTERFACE | > > > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ** value. We can insert integers with each integer tied to its ** value then extract the integer with the smallest value. ** ** The way this queue is used, we never expect it to contain more ** than 2 or 3 elements, so a simple array is sufficient as the ** implementation. This could give worst case O(N) insert times, ** but because of the nature of the problem we expect O(1) performance. ** ** Compatibility note: Some versions of OpenSSL export a symbols ** like "pqueue_insert". This is, technically, a bug in OpenSSL. ** We work around it here by using "pqueuex_" instead of "pqueue_". */ #include "config.h" #include "pqueue.h" #include <assert.h> #if INTERFACE |
︙ | ︙ | |||
44 45 46 47 48 49 50 | } *a; }; #endif /* ** Initialize a PQueue structure */ | | | | | | | | | | 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 | } *a; }; #endif /* ** Initialize a PQueue structure */ void pqueuex_init(PQueue *p){ memset(p, 0, sizeof(*p)); } /* ** Destroy a PQueue. Delete all of its content. */ void pqueuex_clear(PQueue *p){ free(p->a); pqueuex_init(p); } /* ** Change the size of the queue so that it contains N slots */ static void pqueuex_resize(PQueue *p, int N){ p->a = fossil_realloc(p->a, sizeof(p->a[0])*N); p->sz = N; } /* ** Insert element e into the queue. */ void pqueuex_insert(PQueue *p, int e, double v){ int i, j; if( p->cnt+1>p->sz ){ pqueuex_resize(p, p->cnt+5); } for(i=0; i<p->cnt; i++){ if( p->a[i].value>v ){ for(j=p->cnt; j>i; j--){ p->a[j] = p->a[j-1]; } break; } } p->a[i].id = e; p->a[i].value = v; p->cnt++; } /* ** Extract the first element from the queue (the element with ** the smallest value) and return its ID. Return 0 if the queue ** is empty. */ int pqueuex_extract(PQueue *p){ int e, i; if( p->cnt==0 ){ return 0; } e = p->a[0].id; for(i=0; i<p->cnt-1; i++){ p->a[i] = p->a[i+1]; } p->cnt--; return e; } |
Changes to src/printf.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 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 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains implementions of routines for formatting output ** (ex: mprintf()) and for output to the console. */ #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 artifact 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 HNAME_MAX, whichever is least. */ #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 artifact 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. */ int hash_digits(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 > HNAME_MAX ) nDigitHuman = HNAME_MAX; nDigitUrl = nDigitHuman + 6; if( nDigitUrl < FOSSIL_HASH_DIGITS_URL ) nDigitUrl = FOSSIL_HASH_DIGITS_URL; if( nDigitUrl > HNAME_MAX ) nDigitUrl = HNAME_MAX; } return bForUrl ? nDigitUrl : nDigitHuman; } /* ** Return the number of characters in a %S output. */ int length_of_S_display(void){ return hash_digits(0); } /* ** 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 */ #define etEXP 3 /* Exponential notation. %e and %E */ #define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */ #define etSIZE 5 /* Return number of characters processed so far. %n */ #define etSTRING 6 /* Strings. %s */ #define etDYNSTRING 7 /* Dynamically allocated strings. %z */ #define etPERCENT 8 /* Percent symbol. %% */ #define etCHARX 9 /* Characters. %c */ #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 hash prefix: %S */ #define etROOT 24 /* String value of g.zTop: %R */ #define etJSONSTR 25 /* String encoded as a JSON string literal: %j Use %!j to include double-quotes around it. */ #define etSHELLESC 26 /* Escape a filename for use in a shell command: %$ See blob_append_escaped_arg() for details "%$" -> adds "./" prefix if necessary. "%!$" -> omits the "./" prefix. */ /* ** An "etByte" is an 8-bit unsigned value. */ typedef unsigned char etByte; |
︙ | ︙ | |||
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | #define FLAG_INTERN 2 /* True if for internal use only */ #define FLAG_STRING 4 /* Allow infinity precision */ /* ** The following table is searched linearly, so it is good to put the ** most frequently used conversion types first. */ static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; static const char aPrefix[] = "-x0\000X0"; static const et_info fmtinfo[] = { { 'd', 10, 1, etRADIX, 0, 0 }, { '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 }, | > > > > | < > > > > > | > > > > > > > > > > > > > | 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 | #define FLAG_INTERN 2 /* True if for internal use only */ #define FLAG_STRING 4 /* Allow infinity precision */ /* ** The following table is searched linearly, so it is good to put the ** most frequently used conversion types first. ** ** NB: When modifying this table is it vital that you also update the fmtchr[] ** variable to match!!! */ static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; static const char aPrefix[] = "-x0\000X0"; static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$"; static const et_info fmtinfo[] = { { 'd', 10, 1, etRADIX, 0, 0 }, { '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 }, { 'j', 0, 0, etJSONSTR, 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 }, { 'f', 0, 1, etFLOAT, 0, 0 }, { 'e', 0, 1, etEXP, 30, 0 }, { 'E', 0, 1, etEXP, 14, 0 }, { 'G', 0, 1, etGENERIC, 14, 0 }, { 'i', 10, 1, etRADIX, 0, 0 }, { 'n', 0, 0, etSIZE, 0, 0 }, { '%', 0, 0, etPERCENT, 0, 0 }, { 'p', 16, 0, etPOINTER, 0, 1 }, { '/', 0, 0, etPATH, 0, 0 }, { '$', 0, 0, etSHELLESC, 0, 0 }, { etERROR, 0,0,0,0,0} /* Must be last */ }; #define etNINFO count(fmtinfo) /* ** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement. ** ** This routine is a defense against programming errors. */ void fossil_printf_selfcheck(void){ int i; for(i=0; fmtchr[i]; i++){ assert( fmtchr[i]==fmtinfo[i].fmttype ); } } /* ** "*val" is a double such that 0.1 <= *val < 10.0 ** Return the ascii code for the leading digit of *val, then ** multiply "*val" by 10.0 to renormalize. ** ** Example: |
︙ | ︙ | |||
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | /* ** Find the length of a string as long as that length does not ** exceed N bytes. If no zero terminator is seen in the first ** N bytes then return N. If N is negative, then this routine ** is an alias for strlen(). */ static int StrNLen32(const char *z, int N){ int n = 0; while( (N-- != 0) && *(z++)!=0 ){ n++; } return n; } /* ** The root program. All variations call this core. ** ** INPUTS: | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < | < | | 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 | /* ** Find the length of a string as long as that length does not ** exceed N bytes. If no zero terminator is seen in the first ** N bytes then return N. If N is negative, then this routine ** is an alias for strlen(). */ #if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L # define StrNLen32(Z,N) (int)strnlen(Z,N) #else static int StrNLen32(const char *z, int N){ int n = 0; while( (N-- != 0) && *(z++)!=0 ){ n++; } return n; } #endif /* ** 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) ){ wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS; }else{ wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS; } if( db_get_boolean("timeline-plaintext", 0) ){ wikiFlags |= WIKI_LINKSONLY; } if( db_get_boolean("timeline-hard-newlines", 0) ){ wikiFlags |= WIKI_NEWLINE; } } return wikiFlags; } /* ** The root program. All variations call this core. ** ** INPUTS: ** pBlob This is the blob where the output will be built. ** ** fmt This is the format string, as in the usual print. ** ** ap This is a pointer to a list of arguments. Same as in ** vfprint. ** ** OUTPUTS: ** The return value is the total number of characters sent to ** the function "func". Returns -1 on error. ** ** Note that the order in which automatic variables are declared below ** seems to make a big difference in determining how fast this beast ** will run. */ int vxprintf( Blob *pBlob, /* Append output to this blob */ |
︙ | ︙ | |||
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 | etByte flag_blanksign; /* True if " " flag is present */ etByte flag_alternateform; /* True if "#" flag is present */ etByte flag_altform2; /* True if "!" flag is present */ etByte flag_zeropad; /* True if field width constant starts with zero */ etByte flag_long; /* True if "l" flag is present */ etByte flag_longlong; /* True if the "ll" flag is present */ etByte done; /* Loop termination flag */ u64 longvalue; /* Value for integer types */ long double realvalue; /* Value for real types */ const et_info *infop; /* Pointer to the appropriate info structure */ char buf[etBUFSIZE]; /* Conversion buffer */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ etByte errorflag = 0; /* True if an error is encountered */ etByte xtype; /* Conversion paradigm */ char *zExtra; /* Extra memory used for etTCLESCAPE conversions */ static const char spaces[] = " "; #define etSPACESIZE (sizeof(spaces)-1) int exp, e2; /* exponent of real numbers */ double rounder; /* Used for rounding floating point values */ etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ etByte flag_exp; /* True to force display of the exponent */ int nsd; /* Number of significant digits returned */ count = length = 0; bufpt = 0; for(; (c=(*fmt))!=0; ++fmt){ if( c!='%' ){ | > > < > | > | > | < | | > | 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 | etByte flag_blanksign; /* True if " " flag is present */ etByte flag_alternateform; /* True if "#" flag is present */ etByte flag_altform2; /* True if "!" flag is present */ etByte flag_zeropad; /* True if field width constant starts with zero */ etByte flag_long; /* True if "l" flag is present */ etByte flag_longlong; /* True if the "ll" flag is present */ etByte done; /* Loop termination flag */ etByte cThousand; /* Thousands separator for %d and %u */ u64 longvalue; /* Value for integer types */ long double realvalue; /* Value for real types */ const et_info *infop; /* Pointer to the appropriate info structure */ char buf[etBUFSIZE]; /* Conversion buffer */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ etByte errorflag = 0; /* True if an error is encountered */ etByte xtype; /* Conversion paradigm */ char *zExtra; /* Extra memory used for etTCLESCAPE conversions */ static const char spaces[] = " "; #define etSPACESIZE (sizeof(spaces)-1) int exp, e2; /* exponent of real numbers */ double rounder; /* Used for rounding floating point values */ etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ etByte flag_exp; /* True to force display of the exponent */ int nsd; /* Number of significant digits returned */ char *zFmtLookup; count = length = 0; bufpt = 0; for(; (c=(*fmt))!=0; ++fmt){ if( c!='%' ){ bufpt = (char *)fmt; #if HAVE_STRCHRNUL fmt = strchrnul(fmt, '%'); #else do{ fmt++; }while( *fmt && *fmt != '%' ); #endif blob_append(pBlob, bufpt, (int)(fmt - bufpt)); if( *fmt==0 ) break; } if( (c=(*++fmt))==0 ){ errorflag = 1; blob_append(pBlob,"%",1); count++; break; } /* Find out what flags are present */ flag_leftjustify = flag_plussign = flag_blanksign = cThousand = flag_alternateform = flag_altform2 = flag_zeropad = 0; done = 0; do{ switch( c ){ case '-': flag_leftjustify = 1; break; case '+': flag_plussign = 1; break; case ' ': flag_blanksign = 1; break; case '#': flag_alternateform = 1; break; case '!': flag_altform2 = 1; break; case '0': flag_zeropad = 1; break; case ',': cThousand = ','; break; default: done = 1; break; } }while( !done && (c=(*++fmt))!=0 ); /* Get the field width */ width = 0; if( c=='*' ){ width = va_arg(ap,int); |
︙ | ︙ | |||
302 303 304 305 306 307 308 | }else{ flag_longlong = 0; } }else{ flag_long = flag_longlong = 0; } /* Fetch the info entry for the field */ | | < < | | | < | > > | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | }else{ flag_longlong = 0; } }else{ flag_long = flag_longlong = 0; } /* Fetch the info entry for the field */ zFmtLookup = strchr(fmtchr,c); if( zFmtLookup ){ infop = &fmtinfo[zFmtLookup-fmtchr]; xtype = infop->type; }else{ infop = 0; xtype = etERROR; } zExtra = 0; /* Limit the precision to prevent overflowing buf[] during conversion */ if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){ precision = etBUFSIZE-40; } |
︙ | ︙ | |||
381 382 383 384 385 386 387 | base = infop->base; do{ /* Convert to ascii */ *(--bufpt) = cset[longvalue%base]; longvalue = longvalue/base; }while( longvalue>0 ); } length = &buf[etBUFSIZE-1]-bufpt; | | > > > > > > > > > > > > > > > | 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 | base = infop->base; do{ /* Convert to ascii */ *(--bufpt) = cset[longvalue%base]; longvalue = longvalue/base; }while( longvalue>0 ); } length = &buf[etBUFSIZE-1]-bufpt; while( precision>length ){ *(--bufpt) = '0'; /* Zero pad */ length++; } if( cThousand ){ int nn = (length - 1)/3; /* Number of "," to insert */ int ix = (length - 1)%3 + 1; bufpt -= nn; for(idx=0; nn>0; idx++){ bufpt[idx] = bufpt[idx+nn]; ix--; if( ix==0 ){ bufpt[++idx] = cThousand; nn--; ix = 3; } } } if( prefix ) *(--bufpt) = prefix; /* Add sign */ if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ const char *pre; char x; pre = &aPrefix[infop->prefix]; if( *bufpt!=pre[0] ){ |
︙ | ︙ | |||
543 544 545 546 547 548 549 | break; case etPERCENT: buf[0] = '%'; bufpt = buf; length = 1; break; case etCHARX: | | | | | | > > > > | 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 | break; case etPERCENT: buf[0] = '%'; bufpt = buf; length = 1; break; case etCHARX: c = buf[0] = va_arg(ap,int); if( precision>=0 ){ for(idx=1; idx<precision; idx++) buf[idx] = c; length = precision; }else{ length =1; } bufpt = buf; break; case etPATH: { int i; int limit = flag_alternateform ? va_arg(ap,int) : -1; char *e = va_arg(ap,char*); if( e==0 ){e="";} length = StrNLen32(e, limit); zExtra = bufpt = fossil_malloc(length+1); for( i=0; i<length; i++ ){ if( e[i]=='\\' ){ bufpt[i]='/'; }else{ bufpt[i]=e[i]; } } bufpt[length]='\0'; 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 = hash_digits(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; |
︙ | ︙ | |||
603 604 605 606 607 608 609 | Blob *pBlob = va_arg(ap, Blob*); char *zOrig = blob_buffer(pBlob); int i, j, n, cnt; n = blob_size(pBlob); if( limit>=0 && limit<n ) n = limit; for(cnt=i=0; i<n; i++){ if( zOrig[i]=='\'' ) cnt++; } if( n+cnt+2 > etBUFSIZE ){ | | > | > | | < | | | | 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 | Blob *pBlob = va_arg(ap, Blob*); char *zOrig = blob_buffer(pBlob); int i, j, n, cnt; n = blob_size(pBlob); if( limit>=0 && limit<n ) n = limit; for(cnt=i=0; i<n; i++){ if( zOrig[i]=='\'' ) cnt++; } if( n+cnt+2 > etBUFSIZE ){ bufpt = zExtra = fossil_malloc( n + cnt + 2 ); }else{ bufpt = buf; } bufpt[0] = '\''; for(i=0, j=1; i<n; i++, j++){ if( zOrig[i]=='\'' ){ bufpt[j++] = '\''; } bufpt[j] = zOrig[i]; } 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; |
︙ | ︙ | |||
684 685 686 687 688 689 690 691 692 693 694 695 696 | int limit = flag_alternateform ? va_arg(ap,int) : -1; char *zMem = va_arg(ap,char*); if( zMem==0 ) zMem = ""; zExtra = bufpt = fossilize(zMem, limit); length = strlen(bufpt); if( precision>=0 && precision<length ) length = precision; break; } case etWIKISTR: { int limit = flag_alternateform ? va_arg(ap,int) : -1; char *zWiki = va_arg(ap, char*); Blob wiki; blob_init(&wiki, zWiki, limit); | > > > > > > > > > > > > > > | | | | | 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 | int limit = flag_alternateform ? va_arg(ap,int) : -1; char *zMem = va_arg(ap,char*); if( zMem==0 ) zMem = ""; zExtra = bufpt = fossilize(zMem, limit); length = strlen(bufpt); if( precision>=0 && precision<length ) length = precision; break; } case etJSONSTR: { int limit = flag_alternateform ? va_arg(ap,int) : -1; char *zMem = va_arg(ap,char*); if( limit!=0 ){ /* Ignore the limit flag, if set, for JSON string ** output. This block exists to squelch the associated ** "unused variable" compiler warning. */ } if( zMem==0 ) zMem = ""; zExtra = bufpt = encode_json_string_literal(zMem, flag_altform2, &length); if( precision>=0 && precision<length ) length = precision; break; } case etWIKISTR: { int limit = flag_alternateform ? va_arg(ap,int) : -1; char *zWiki = va_arg(ap, char*); Blob wiki; blob_init(&wiki, zWiki, limit); wiki_convert(&wiki, pBlob, wiki_convert_flags(flag_altform2)); blob_reset(&wiki); length = width = 0; break; } case etSHELLESC: { char *zArg = va_arg(ap, char*); blob_append_escaped_arg(pBlob, zArg, !flag_altform2); length = width = 0; break; } case etERROR: buf[0] = '%'; buf[1] = c; errorflag = 0; |
︙ | ︙ | |||
721 722 723 724 725 726 727 | ** the output. */ if( !flag_leftjustify ){ register int nspace; nspace = width-length; if( nspace>0 ){ count += nspace; | | | | | | 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 | ** the output. */ if( !flag_leftjustify ){ register int nspace; nspace = width-length; if( nspace>0 ){ count += nspace; while( nspace>=(int)etSPACESIZE ){ blob_append(pBlob,spaces,etSPACESIZE); nspace -= etSPACESIZE; } if( nspace>0 ) blob_append(pBlob,spaces,nspace); } } if( length>0 ){ blob_append(pBlob,bufpt,length); count += length; } if( flag_leftjustify ){ register int nspace; nspace = width-length; if( nspace>0 ){ count += nspace; while( nspace>=(int)etSPACESIZE ){ blob_append(pBlob,spaces,etSPACESIZE); nspace -= etSPACESIZE; } if( nspace>0 ) blob_append(pBlob,spaces,nspace); } } if( zExtra ){ fossil_free(zExtra); } }/* End for loop over the format string */ return errorflag ? -1 : count; } /* End of function */ /* ** Print into memory obtained from fossil_malloc(). */ char *mprintf(const char *zFormat, ...){ va_list ap; char *z; va_start(ap,zFormat); z = vmprintf(zFormat, ap); va_end(ap); |
︙ | ︙ | |||
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 | g.iErrPriority = iPriority; } void fossil_error_reset(void){ free(g.zErrMsg); g.zErrMsg = 0; g.iErrPriority = 0; } /* ** Write output for user consumption. If g.cgiOutput is enabled, then ** send the output as part of the CGI reply. If g.cgiOutput is false, ** then write on standard output. */ void fossil_print(const char *zFormat, ...){ va_list ap; va_start(ap, zFormat); if( g.cgiOutput ){ cgi_vprintf(zFormat, ap); }else{ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | g.iErrPriority = iPriority; } void fossil_error_reset(void){ free(g.zErrMsg); g.zErrMsg = 0; g.iErrPriority = 0; } /* True if the last character standard output cursor is setting at ** the beginning of a blank link. False if a \r has been to move the ** cursor to the beginning of the line or if not at the beginning of ** a line. ** was a \n */ static int stdoutAtBOL = 1; /* ** Write to standard output or standard error. ** ** On windows, transform the output into the current terminal encoding ** if the output is going to the screen. If output is redirected into ** a file, no translation occurs. Switch output mode to binary to ** properly process line-endings, make sure to switch the mode back to ** text when done. ** No translation ever occurs on unix. */ void fossil_puts(const char *z, int toStdErr, int n){ FILE* out = (toStdErr ? stderr : stdout); if( n==0 ) return; assert( toStdErr==0 || toStdErr==1 ); if( toStdErr==0 ) stdoutAtBOL = (z[n-1]=='\n'); #if defined(_WIN32) if( fossil_utf8_to_console(z, n, toStdErr) >= 0 ){ return; } fflush(out); _setmode(_fileno(out), _O_BINARY); #endif fwrite(z, 1, n, out); #if defined(_WIN32) fflush(out); _setmode(_fileno(out), _O_TEXT); #endif } /* ** 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, 1); 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){ stdoutAtBOL = 1; } /* ** Write output for user consumption. If g.cgiOutput is enabled, then ** send the output as part of the CGI reply. If g.cgiOutput is false, ** then write on standard output. */ void fossil_print(const char *zFormat, ...){ va_list ap; va_start(ap, zFormat); if( g.cgiOutput ){ cgi_vprintf(zFormat, ap); }else{ vxprintf(0, zFormat, ap); } va_end(ap); } void fossil_vprint(const char *zFormat, va_list ap){ if( g.cgiOutput ){ cgi_vprintf(zFormat, ap); }else{ vxprintf(0, zFormat, ap); } } /* ** Print a trace message on standard error. */ void fossil_trace(const char *zFormat, ...){ va_list ap; Blob b; va_start(ap, zFormat); b = empty_blob; vxprintf(&b, zFormat, ap); fossil_puts(blob_buffer(&b), 1, blob_size(&b)); blob_reset(&b); va_end(ap); } /* ** Write a message to the error log, if the error log filename is ** defined. ** ** If the message format begins with 'X', then omit that X from the ** beginning of the message and add much more CGI context. */ void fossil_errorlog(const char *zFormat, ...){ struct tm *pNow; time_t now; FILE *out; const char *z; int i; int bDetail = 0; va_list ap; static const char *const azEnv[] = { "HTTP_HOST", "HTTP_REFERER", "HTTP_USER_AGENT", "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_NAME" }; if( g.zErrlog==0 ) return; if( g.zErrlog[0]=='-' && g.zErrlog[1]==0 ){ out = stderr; }else{ 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, pNow->tm_hour, pNow->tm_min, pNow->tm_sec); va_start(ap, zFormat); if( zFormat[0]=='X' ){ bDetail = 1; zFormat++; } vfprintf(out, zFormat, ap); fprintf(out, "\n"); va_end(ap); if( bDetail ){ cgi_print_all(1,3,out); }else{ for(i=0; i<count(azEnv); i++){ char *p; if( (p = fossil_getenv(azEnv[i]))!=0 && p[0]!=0 ){ fprintf(out, "%s=%s\n", azEnv[i], p); fossil_path_free(p); }else if( (z = P(azEnv[i]))!=0 && z[0]!=0 ){ fprintf(out, "%s=%s\n", azEnv[i], z); } } } fclose(out); } /* ** The following variable becomes true while processing a fatal error ** or a panic. If additional "recursive-fatal" errors occur while ** shutting down, the recursive errors are silently ignored. */ static int mainInFatalError = 0; /* ** Write error message output */ static int fossil_print_error(int rc, const char *z){ #ifdef FOSSIL_ENABLE_JSON if( g.json.isJsonMode!=0 ){ /* ** Avoid calling into the JSON support subsystem if it ** has not yet been initialized, e.g. early SQLite log ** messages, etc. */ if( !json_is_bootstrapped_early() ) json_bootstrap_early(); json_err( 0, z, 1 ); if( g.isHTTP && !g.json.preserveRc ){ rc = 0 /* avoid HTTP 500 */; } if( g.cgiOutput==1 ){ g.cgiOutput = 2; cgi_reply(); } } else #endif if( g.cgiOutput==1 && g.db ){ g.cgiOutput = 2; cgi_reset_content(); cgi_set_content_type("text/html"); style_set_current_feature("error"); style_header("Bad Request"); etag_cancel(); @ <p class="generalError">%h(z)</p> cgi_set_status(400, "Bad Request"); style_finish_page(); cgi_reply(); }else if( !g.fQuiet ){ fossil_force_newline(); fossil_trace("%s\n", z); } return rc; } /* ** Print an error message, rollback all databases, and quit. These ** routines never return and produce a non-zero process exit status. ** ** The main difference between fossil_fatal() and fossil_panic() is that ** fossil_panic() makes an entry in the error log whereas fossil_fatal() ** does not. On POSIX platforms, if there is not an error log, then both ** routines work similarly with respect to user-visible effects. Hence, ** the routines are interchangable for commands and only act differently ** when processing web pages. On the Windows platform, fossil_panic() ** also displays a pop-up stating that an error has occured and allowing ** just-in-time debugging to commence. On all platforms, fossil_panic() ** ends execution with a SIGABRT signal, bypassing atexit processing. ** This signal can also produce a core dump on POSIX platforms. ** ** Use fossil_fatal() for malformed inputs that should be reported back ** to the user, but which do not represent a configuration problem or bug. ** ** Use fossil_panic() for any kind of error that should be brought to the ** attention of the system administrator or Fossil developers. It should ** be avoided for ordinary usage, parameter, OOM and I/O errors. */ NORETURN void fossil_panic(const char *zFormat, ...){ va_list ap; int rc = 1; char z[1000]; static int once = 0; if( once ) exit(1); once = 1; mainInFatalError = 1; /* db_force_rollback(); */ va_start(ap, zFormat); sqlite3_vsnprintf(sizeof(z),z,zFormat, ap); va_end(ap); if( g.fAnyTrace ){ fprintf(stderr, "/***** panic on %d *****/\n", getpid()); } fossil_errorlog("panic: %s", z); rc = fossil_print_error(rc, z); abort(); exit(rc); } NORETURN void fossil_fatal(const char *zFormat, ...){ static int once = 0; va_list ap; char *z; int rc = 1; if( once ) exit(1); once = 1; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); rc = fossil_print_error(rc, z); fossil_free(z); db_force_rollback(); fossil_exit(rc); } /* This routine works like fossil_fatal() except that if called ** recursively, the recursive call is a no-op. ** ** Use this in places where an error might occur while doing ** fatal error shutdown processing. Unlike fossil_panic() and ** fossil_fatal() which never return, this routine might return if ** the fatal error handing is already in process. The caller must ** be prepared for this routine to return. */ void fossil_fatal_recursive(const char *zFormat, ...){ char *z; va_list ap; int rc = 1; if( mainInFatalError ) return; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); rc = fossil_print_error(rc, z); db_force_rollback(); fossil_exit(rc); } /* Print a warning message. ** ** Unlike fossil_fatal() and fossil_panic(), this routine does return ** and processing attempts to continue. A message is written to the ** error log, however, so this routine should only be used for situations ** that require administrator or developer attention. Minor problems ** in user inputs should not use this routine. */ void fossil_warning(const char *zFormat, ...){ char *z; va_list ap; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); fossil_errorlog("warning: %s", z); #ifdef FOSSIL_ENABLE_JSON if( g.json.isJsonMode!=0 ){ /* ** Avoid calling into the JSON support subsystem if it ** has not yet been initialized, e.g. early SQLite log ** messages, etc. */ if( !json_is_bootstrapped_early() ) json_bootstrap_early(); json_warn( FSL_JSON_W_UNKNOWN, "%s", z ); }else #endif { if( g.cgiOutput==1 ){ etag_cancel(); cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); }else{ fossil_force_newline(); fossil_trace("%s\n", z); } } free(z); } /* ** Turn off any LF to CRLF translation on the stream given as an ** argument. This is a no-op on unix but is necessary on windows. */ void fossil_binary_mode(FILE *p){ #if defined(_WIN32) _setmode(_fileno(p), _O_BINARY); #endif #ifdef __EMX__ /* OS/2 */ setmode(fileno(p), O_BINARY); #endif } |
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 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 | /* ** 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 the graveyard. ** ** 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, -- 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 @ ); ; /* ** Flags for the purge_artifact_list() function. */ #if INTERFACE #define PURGE_MOVETO_GRAVEYARD 0x0001 /* Move artifacts in graveyard */ #define PURGE_EXPLAIN_ONLY 0x0002 /* Show what would have happened */ #define PURGE_PRINT_SUMMARY 0x0004 /* Print a summary report at end */ #endif /* ** 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. ** ** Steps 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 */ unsigned purgeFlags /* zero or more PURGE_* flags */ ){ 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); describe_artifacts_to_stdout(0, 0); /* The explain-only flags causes this routine to list the artifacts ** that would have been purged but to not actually make any changes ** to the repository. */ if( purgeFlags & PURGE_EXPLAIN_ONLY ){ db_end_transaction(0); return 0; } /* 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_panic("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( purgeFlags & PURGE_MOVETO_GRAVEYARD ){ db_multi_exec(zPurgeInit /*works-like:"%w%w"*/, "repository", "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); if( purgeFlags & PURGE_PRINT_SUMMARY ){ fossil_print("%d artifacts purged\n", db_int(0, "SELECT count(*) FROM \"%w\";", zTab)); fossil_print("undoable using \"%s purge undo %d\".\n", g.nameOfExe, peid); } 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 by the 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, 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); if( hname_verify_hash(pOut, blob_buffer(&h1), blob_size(&h1))==0 ){ fossil_fatal("incorrect artifact hash on %b", &h1); } blob_reset(&h1); 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, 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; } if( hname_verify_hash(&c2, blob_buffer(&h1), blob_size(&h1))==0 ){ fossil_fatal("incorrect hash on %b", &h1); } 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. The "fossil purge obliterate" ** command empties the graveyard, making the content unrecoverable. ** ** WARNING: This command can potentially destroy historical data and ** leave your repository in a goofy state. Know what you are doing! ** Make a backup of your repository before using this command! ** ** FURTHER WARNING: This command is a work-in-progress and may yet ** contain bugs. ** ** > fossil purge artifacts HASH... ?OPTIONS? ** ** Move arbitrary artifacts identified by the HASH list into the ** graveyard. ** ** > fossil purge cat HASH... ** ** Write the content of one or more artifacts in the graveyard onto ** standard output. ** ** > fossil purge checkins TAGS... ?OPTIONS? ** ** Move the check-ins or branches identified by TAGS and all of ** their descendants out of the repository and into the graveyard. ** If TAGS includes a branch name then it means all the check-ins ** on the most recent occurrence of that branch. ** ** > fossil purge files NAME ... ?OPTIONS? ** ** Move all instances of files called NAME into the graveyard. ** NAME should be the name of the file relative to the root of the ** repository. If NAME is a directory, then all files within that ** directory are moved. ** ** > fossil purge list|ls ?-l? ** ** Show the graveyard of prior purges. The -l option gives more ** detail in the output. ** ** > fossil purge obliterate ID... ?--force? ** ** Remove one or more purge events from the graveyard. Once a purge ** event is obliterated, it can no longer be undone. The --force ** option suppresses the confirmation prompt. ** ** > fossil purge tickets NAME ... ?OPTIONS? ** ** TBD... ** ** > fossil purge undo ID ** ** Restore the content previously removed by purge ID. ** ** > fossil purge wiki NAME ... ?OPTIONS? ** ** TBD... ** ** COMMON OPTIONS: ** ** --explain Make no changes, but show what would happen ** --dry-run An alias for --explain */ void purge_cmd(void){ int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY; const char *zSubcmd; int n; int i; 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( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){ purgeFlags |= PURGE_EXPLAIN_ONLY; } if( strncmp(zSubcmd, "artifacts", n)==0 ){ verify_all_options(); db_begin_transaction(); db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); for(i=3; i<g.argc; i++){ int r = name_to_typed_rid(g.argv[i], ""); db_multi_exec("INSERT OR IGNORE INTO ok(rid) VALUES(%d);", r); } describe_artifacts_to_stdout("IN ok", 0); purge_artifact_list("ok", "", purgeFlags); db_end_transaction(0); }else if( strncmp(zSubcmd, "cat", n)==0 ){ int i, piid; Blob content; if( g.argc<4 ) usage("cat HASH..."); 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); } }else if( strncmp(zSubcmd, "checkins", n)==0 ){ int vid; if( find_option("explain",0,0)!=0 || find_option("dry-run",0,0)!=0 ){ purgeFlags |= PURGE_EXPLAIN_ONLY; } verify_all_options(); db_begin_transaction(); if( g.argc<=3 ) usage("checkins TAGS... [OPTIONS]"); db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); for(i=3; 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 check-out"); } find_checkin_associates("ok", 1); purge_artifact_list("ok", "", purgeFlags); db_end_transaction(0); }else if( strncmp(zSubcmd, "files", n)==0 ){ verify_all_options(); db_begin_transaction(); db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); for(i=3; i<g.argc; i++){ db_multi_exec( "INSERT OR IGNORE INTO ok(rid) " " SELECT fid FROM mlink, filename" " WHERE mlink.fnid=filename.fnid" " AND (filename.name=%Q OR filename.name GLOB '%q/*')", g.argv[i], g.argv[i] ); } purge_artifact_list("ok", "", purgeFlags); db_end_transaction(0); }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',toLocal())" " 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; int bForce = find_option("force","f",0)!=0; if( g.argc<4 ) usage("obliterate ID..."); if( !bForce ){ Blob ans; char cReply; prompt_user( "Obliterating the graveyard 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); } } 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, "tickets", n)==0 ){ fossil_fatal("not yet implemented...."); }else if( strncmp(zSubcmd, "undo", n)==0 ){ int peid; if( g.argc!=4 ) usage("undo ID"); peid = atoi(g.argv[3]); if( (purgeFlags & PURGE_EXPLAIN_ONLY)==0 ){ 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 if( strncmp(zSubcmd, "wiki", n)==0 ){ fossil_fatal("not yet implemented...."); }else{ fossil_fatal("unknown subcommand \"%s\".\n" "should be one of: cat, checkins, files, list, obliterate," " tickets, undo, wiki", zSubcmd); } } |
Changes to src/rebuild.c.
︙ | ︙ | |||
16 17 18 19 20 21 22 | ******************************************************************************* ** ** This file contains code used to rebuild the database. */ #include "config.h" #include "rebuild.h" #include <assert.h> | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < > | > | < < < < < < < < < < < < | < < < < | | | < < > > | > > > | > > > | > | < | | | > > > | > | < < | < < < > | < > | < | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < > > > > > > > > > > > | > > | | | | | | | | > | | | | | | | | | | | | | | | | > > | > > > > > > > > > > > > > > > > > > > | | > > > | > | | | < < < < < < | > | | | | | | | > | | > > > > > > > | | | | | < | | > | | | | < < < < < | | < > > | | | < > | < > > | | | | | | > > > > | < > | < > > > | > > > > > > > | > > | | 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 | ******************************************************************************* ** ** This file contains code used to rebuild the database. */ #include "config.h" #include "rebuild.h" #include <assert.h> #include <errno.h> /* ** Update the schema as necessary */ static void rebuild_update_schema(void){ /* 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 repository.plink ADD COLUMN baseid;" ); } /* 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 repository.mlink ADD COLUMN pmid INTEGER DEFAULT 0;" "ALTER TABLE repository.mlink ADD COLUMN isaux BOOLEAN DEFAULT 0;" ); db_end_transaction(0); } /* Add the user.mtime column if it is missing. (2011-04-27) */ if( !db_table_has_column("repository", "user", "mtime") ){ db_unprotect(PROTECT_ALL); db_multi_exec( "CREATE TEMP TABLE temp_user AS SELECT * FROM user;" "DROP TABLE user;" "CREATE TABLE user(\n" " uid INTEGER PRIMARY KEY,\n" " login TEXT UNIQUE,\n" " pw TEXT,\n" " cap TEXT,\n" " cookie TEXT,\n" " ipaddr TEXT,\n" " cexpire DATETIME,\n" " info TEXT,\n" " mtime DATE,\n" " photo BLOB\n" ");" "INSERT OR IGNORE INTO user" " SELECT uid, login, pw, cap, cookie," " ipaddr, cexpire, info, now(), photo FROM temp_user;" "DROP TABLE temp_user;" ); db_protect_pop(); } /* Add the config.mtime column if it is missing. (2011-04-27) */ if( !db_table_has_column("repository", "config", "mtime") ){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "ALTER TABLE config ADD COLUMN mtime INTEGER;" "UPDATE config SET mtime=now();" ); db_protect_pop(); } /* Add the shun.mtime and shun.scom columns if they are missing. ** (2011-04-27) */ if( !db_table_has_column("repository", "shun", "mtime") ){ db_multi_exec( "ALTER TABLE shun ADD COLUMN mtime INTEGER;" "ALTER TABLE shun ADD COLUMN scom TEXT;" "UPDATE shun SET mtime=now();" ); } /* Add the reportfmt.mtime column if it is missing. (2011-04-27) */ if( !db_table_has_column("repository", "reportfmt", "mtime") ){ static const char zCreateReportFmtTable[] = @ -- An entry in this table describes a database query that generates a @ -- table of tickets. @ -- @ CREATE TABLE IF NOT EXISTS reportfmt( @ rn INTEGER PRIMARY KEY, -- Report number @ owner TEXT, -- Owner of this report format (not used) @ title TEXT UNIQUE, -- Title of this report @ mtime INTEGER, -- Time last modified. Seconds since 1970 @ cols TEXT, -- A color-key specification @ sqlcode TEXT -- An SQL SELECT statement for this report @ ); ; db_multi_exec( "CREATE TEMP TABLE old_fmt AS SELECT * FROM reportfmt;" "DROP TABLE reportfmt;" ); db_multi_exec("%s", zCreateReportFmtTable/*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;" ); } /* Add the concealed.mtime column if it is missing. (2011-04-27) */ if( !db_table_has_column("repository", "concealed", "mtime") ){ db_multi_exec( "ALTER TABLE concealed ADD COLUMN mtime INTEGER;" "UPDATE concealed SET mtime=now();" ); } /* Do the fossil-2.0 updates to the schema. (2017-02-28) */ rebuild_schema_update_2_0(); } /* ** Update the repository schema for Fossil version 2.0. (2017-02-28) ** (1) Change the CHECK constraint on BLOB.UUID so that the length ** is greater than or equal to 40, not exactly equal to 40. */ void rebuild_schema_update_2_0(void){ char *z = db_text(0, "SELECT sql FROM repository.sqlite_schema" " WHERE name='blob'"); if( z ){ /* Search for: length(uuid)==40 ** 0123456789 12345 */ int i; for(i=10; z[i]; i++){ if( z[i]=='=' && strncmp(&z[i-6],"(uuid)==40",10)==0 ){ int rc = 0; z[i] = '>'; sqlite3_db_config(g.db, SQLITE_DBCONFIG_DEFENSIVE, 0, &rc); db_multi_exec( "PRAGMA writable_schema=ON;" "UPDATE repository.sqlite_schema SET sql=%Q WHERE name LIKE 'blob';" "PRAGMA writable_schema=OFF;", z ); sqlite3_db_config(g.db, SQLITE_DBCONFIG_DEFENSIVE, 1, &rc); break; } } fossil_free(z); } db_multi_exec( "CREATE VIEW IF NOT EXISTS " " repository.artifact(rid,rcvid,size,atype,srcid,hash,content) AS " " SELECT blob.rid,rcvid,size,1,srcid,uuid,content" " FROM blob LEFT JOIN delta ON (blob.rid=delta.rid);" ); } /* ** 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 */ static int ttyOutput; /* Do progress output */ static Bag bagDone = Bag_INIT; /* Bag of records rebuilt */ static char *zFNameFormat; /* Format string for filenames on deconstruct */ static int cchFNamePrefix; /* Length of directory prefix in zFNameFormat */ static const char *zDestDir;/* Destination directory on deconstruct */ static int prefixLength; /* Length of directory prefix for deconstruct */ static int fKeepRid1; /* Flag to preserve RID=1 on de- and reconstruct */ /* ** Draw the percent-complete message. ** The input is actually the permill complete. */ static void percent_complete(int permill){ static int lastOutput = -1; if( permill>lastOutput ){ fossil_print(" %d.%d%% complete...\r", permill/10, permill%10); fflush(stdout); lastOutput = permill; } } /* ** Frees rebuild-level cached state. Intended only to be called by the ** app-level atexit() handler. */ void rebuild_clear_cache(){ bag_clear(&bagDone); } /* ** Called after each artifact is processed */ static void rebuild_step_done(int rid){ /* assert( bag_find(&bagDone, rid)==0 ); */ bag_insert(&bagDone, rid); if( ttyOutput ){ processCnt++; if (!g.fQuiet && totalSize>0) { percent_complete((processCnt*1000)/totalSize); } } } /* ** Rebuild cross-referencing information for the artifact ** rid with content pBase and all of its descendants. This ** routine clears the content buffer before returning. ** ** If the zFNameFormat variable is set, then this routine is ** called to run "fossil deconstruct" instead of the usual ** "fossil rebuild". In that case, instead of rebuilding the ** cross-referencing information, write the file content out ** to the appropriate directory. ** ** In both cases, this routine automatically recurses to process ** other artifacts that are deltas off of the current artifact. ** This is the most efficient way to extract all of the original ** artifact content from the Fossil repository. */ static void rebuild_step(int rid, int size, Blob *pBase){ static Stmt q1; Bag children; Blob copy; Blob *pUse; int nChild, i, cid; while( rid>0 ){ /* Fix up the "blob.size" field if needed. */ if( size!=(int)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); if( rid==1 && fKeepRid1!=0 ){ char *zFnDotRid1 = mprintf("%s/.rid1", zDestDir); char *zFnRid1 = zFile + cchFNamePrefix + 1; /* Skip directory slash */ Blob bFileContents = empty_blob; blob_appendf(&bFileContents, "# The file holding the artifact with RID=1\n" "%s\n", zFnRid1); blob_write_to_file(&bFileContents, zFnDotRid1); blob_reset(&bFileContents); free(zFnDotRid1); } 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); if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){ Blob delta, next; db_ephemeral_blob(&q2, 0, &delta); blob_uncompress(&delta, &delta); blob_delta_apply(pBase, &delta, &next); blob_reset(&delta); db_reset(&q2); if( i<nChild ){ rebuild_step(cid, sz, &next); }else{ /* Tail recursion */ rid = cid; size = sz; blob_reset(pBase); *pBase = next; } }else{ db_reset(&q2); blob_reset(pBase); } } bag_clear(&children); } } /* ** Check to see if the "sym-trunk" tag exists. If not, create it ** and attach it to the very first check-in. */ static void rebuild_tag_trunk(void){ int tagid = db_int(0, "SELECT 1 FROM tag WHERE tagname='sym-trunk'"); int rid; char *zUuid; if( tagid>0 ) return; rid = db_int(0, "SELECT pid FROM plink AS x WHERE NOT EXISTS(" " SELECT 1 FROM plink WHERE cid=x.pid)"); if( rid==0 ) return; /* Add the trunk tag to the root of the whole tree */ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); if( zUuid==0 ) return; tag_add_artifact("sym-", "trunk", zUuid, 0, 2, 0, 0); tag_add_artifact("", "branch", zUuid, "trunk", 2, 0, 0); } /* ** Core function to rebuild the information in the derived tables of a ** fossil repository from the blobs. This function is shared between ** 'rebuild_database' ('rebuild') and 'reconstruct_cmd' ** ('reconstruct'), both of which have to regenerate this information ** from scratch. */ int rebuild_db(int doOut, int doClustering){ Stmt s, q; int errCnt = 0; int incrSize; Blob sql; bag_clear(&bagDone); ttyOutput = doOut; processCnt = 0; if (ttyOutput && !g.fQuiet) { percent_complete(0); } manifest_disable_event_triggers(); rebuild_update_schema(); blob_init(&sql, 0, 0); db_unprotect(PROTECT_ALL); db_prepare(&q, "SELECT name FROM sqlite_schema /*scan*/" " WHERE type='table'" " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," "'config','shun','private','reportfmt'," "'concealed','accesslog','modreq'," "'purgeevent','purgeitem','unversioned'," "'subscriber','pending_alert','chat')" " AND name NOT GLOB 'sqlite_*'" " AND name NOT GLOB 'fx_*'" ); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0)); } db_finalize(&q); db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/); blob_reset(&sql); 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; db_prepare(&s, "SELECT rid, size FROM blob /*scan*/" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" " AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)" ); manifest_crosslink_begin(); while( db_step(&s)==SQLITE_ROW ){ int rid = db_column_int(&s, 0); int size = db_column_int(&s, 1); |
︙ | ︙ | |||
268 269 270 271 272 273 274 | } }else{ db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid); rebuild_step_done(rid); } } db_finalize(&s); | | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > < | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | | > > > > > > > > > > > > > | | > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > | | > | > > > > > > > > > > | > > > > > > | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | > | < > > > > > > > < | < | | | > > > > | | < | | > | > > > > > > | > | | > > > > > > > > > > > > > > > > > > > | | < | | | > | < > > > > > | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } }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 ){ processCnt += incrSize; percent_complete((processCnt*1000)/totalSize); } if(!g.fQuiet && ttyOutput ){ percent_complete(1000); fossil_print("\n"); } db_protect_pop(); return errCnt; } /* ** Number of neighbors to search */ #define N_NEIGHBOR 5 /* ** Attempt to convert more full-text blobs into delta-blobs for ** storage efficiency. Return the number of bytes of storage space ** saved. */ i64 extra_deltification(int *pnDelta){ Stmt q; int aPrev[N_NEIGHBOR]; int nPrev; int rid; int prevfnid, fnid; int nDelta = 0; i64 nByte = 0; int nSaved; db_begin_transaction(); /* Look for manifests that have not been deltaed and try to make them ** children of one of the 5 chronologically subsequent check-ins */ db_prepare(&q, "SELECT rid FROM event, blob" " WHERE blob.rid=event.objid" " AND event.type='ci'" " AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)" " ORDER BY event.mtime DESC" ); nPrev = 0; while( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); if( nPrev>0 ){ nSaved = content_deltify(rid, aPrev, nPrev, 0); if( nSaved>0 ){ nDelta++; nByte += nSaved; } } if( nPrev<N_NEIGHBOR ){ aPrev[nPrev++] = rid; }else{ int i; for(i=0; i<N_NEIGHBOR-1; i++) aPrev[i] = aPrev[i+1]; aPrev[N_NEIGHBOR-1] = rid; } } db_finalize(&q); /* For individual files that have not been deltaed, try to find ** a parent which is an undeltaed file with the same name in a ** more recent branch. */ db_prepare(&q, "SELECT DISTINCT blob.rid, mlink.fnid FROM blob, mlink, plink" " WHERE NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)" " AND mlink.fid=blob.rid" " AND mlink.mid=plink.cid" " AND plink.cid=mlink.mid" " ORDER BY mlink.fnid, plink.mtime DESC" ); prevfnid = 0; while( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); fnid = db_column_int(&q, 1); if( fnid!=prevfnid ) nPrev = 0; prevfnid = fnid; if( nPrev>0 ){ nSaved = content_deltify(rid, aPrev, nPrev, 0); if( nSaved>0 ){ nDelta++; nByte += nSaved; } } if( nPrev<N_NEIGHBOR ){ aPrev[nPrev++] = rid; }else{ int i; for(i=0; i<N_NEIGHBOR-1; i++) aPrev[i] = aPrev[i+1]; aPrev[N_NEIGHBOR-1] = rid; } } db_finalize(&q); db_end_transaction(0); if( pnDelta!=0 ) *pnDelta = nDelta; return nByte; } /* Reconstruct the private table. The private table contains the rid ** of every manifest that is tagged with "private" and every file that ** is not used by a manifest that is not private. */ static void reconstruct_private_table(void){ db_multi_exec( "CREATE TEMP TABLE private_ckin(rid INTEGER PRIMARY KEY);" "INSERT INTO private_ckin " " SELECT rid FROM tagxref WHERE tagid=%d AND tagtype>0;" "INSERT OR IGNORE INTO private" " SELECT fid FROM mlink" " EXCEPT SELECT fid FROM mlink WHERE mid NOT IN private_ckin;" "INSERT OR IGNORE INTO private SELECT rid FROM private_ckin;" "DROP TABLE private_ckin;", TAG_PRIVATE ); fix_private_blob_dependencies(0); } /* ** COMMAND: repack ** ** Usage: %fossil repack ?REPOSITORY? ** ** Perform extra delta-compression to try to minimize the size of the ** repository. This command is simply a short-hand for: ** ** fossil rebuild --compress-only ** ** The name for this command is stolen from the "git repack" command that ** does approximately the same thing in Git. */ void repack_command(void){ i64 nByte = 0; int nDelta = 0; int runVacuum = 0; verify_all_options(); if( g.argc==3 ){ db_open_repository(g.argv[2]); }else if( g.argc==2 ){ db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); if( g.argc!=2 ){ usage("?REPOSITORY-FILENAME?"); } db_close(1); db_open_repository(g.zRepositoryName); }else{ usage("?REPOSITORY-FILENAME?"); } db_unprotect(PROTECT_ALL); nByte = extra_deltification(&nDelta); if( nDelta>0 ){ if( nDelta==1 ){ fossil_print("1 new delta saves %,lld bytes\n", nByte); }else{ fossil_print("%d new deltas save %,lld bytes\n", nDelta, nByte); } runVacuum = 1; }else{ fossil_print("no new compression opportunities found\n"); runVacuum = db_int(0, "PRAGMA repository.freelist_count")>0; } if( runVacuum ){ fossil_print("Vacuuming the database... "); fflush(stdout); db_multi_exec("VACUUM"); fossil_print("done\n"); } } /* ** COMMAND: rebuild ** ** 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 ** --compress-only Skip the rebuilding step. Do --compress only ** --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, power of 2) ** --quiet Only show output if there are errors ** --stats Show artifact statistics after rebuilding ** --vacuum Run VACUUM on the database after rebuilding ** --wal Set Write-Ahead-Log journalling mode on the database */ void rebuild_database(void){ int forceFlag; int errCnt = 0; 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; int compressOnlyFlag; omitVerify = find_option("noverify",0,0)!=0; forceFlag = find_option("force","f",0)!=0; doClustering = find_option("cluster", 0, 0)!=0; runVacuum = find_option("vacuum",0,0)!=0; runDeanalyze = find_option("deanalyze",0,0)!=0; /* Deprecated */ 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; compressOnlyFlag = find_option("compress-only",0,0)!=0; if( compressOnlyFlag ) runCompress = 1; 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() && !compressOnlyFlag; 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(); db_unprotect(PROTECT_ALL); if( !compressOnlyFlag ){ search_drop_index(); ttyOutput = 1; errCnt = rebuild_db(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); }else{ if( runCompress ){ i64 nByte = 0; int nDelta = 0; fossil_print("Extra delta compression... "); fflush(stdout); nByte = extra_deltification(&nDelta); if( nDelta>0 ){ if( nDelta==1 ){ fossil_print("1 new delta saves %,lld bytes", nByte); }else{ fossil_print("%d new deltas save %,lld bytes", nDelta, nByte); } runVacuum = 1; }else{ fossil_print("none found"); } fflush(stdout); } if( omitVerify ) verify_cancel(); db_end_transaction(0); if( runCompress ) fossil_print("\n"); db_close(0); 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(); db_protect_pop(); 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:" }, { CFTYPE_EVENT, "Events:" }, }; int i; int subtotal = 0; for(i=0; i<count(aStat); i++){ int k = aStat[i].idx; fossil_print("%-15s %6d\n", aStat[i].zLabel, g.parseCnt[k]); if( k>0 ) subtotal += g.parseCnt[k]; } fossil_print("%-15s %6d\n", "Other:", g.parseCnt[CFTYPE_ANY] - subtotal); } } /* ** COMMAND: detach* ** ** Usage: %fossil detach ?REPOSITORY? ** ** Change the project-code and make other changes to REPOSITORY so that ** it becomes a new and distinct child project. After being detached, ** REPOSITORY will not longer be able to push and pull from other clones ** of the original project. However REPOSITORY will still be able to pull ** from those other clones using the --from-parent-project option of the ** "fossil pull" command. ** ** This is an experts-only command. You should not use this command unless ** you fully understand what you are doing. ** ** The original use-case for this command was to create test repositories ** from real-world working repositories that could be safely altered by ** making strange commits or other changes, without having to worry that ** those test changes would leak back into the original project via an ** accidental auto-sync. */ void test_detach_cmd(void){ const char *zXfer[] = { "project-name", "parent-project-name", "project-code", "parent-project-code", "last-sync-url", "parent-project-url", "last-sync-pw", "parent-project-pw" }; int i; Blob ans; char cReply; db_find_and_open_repository(0, 2); prompt_user("This change will be difficult to undo. Are you sure (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y' ) return; db_begin_transaction(); db_unprotect(PROTECT_CONFIG); for(i=0; i<ArraySize(zXfer)-1; i+=2 ){ db_multi_exec( "REPLACE INTO config(name,value,mtime)" " SELECT %Q, value, now() FROM config WHERE name=%Q", zXfer[i+1], zXfer[i] ); } db_multi_exec( "DELETE FROM config WHERE name IN" "(WITH pattern(x) AS (VALUES" " ('baseurl:*')," " ('cert:*')," " ('ckout:*')," " ('gitpush:*')," " ('http-auth:*')," " ('last-sync-*')," " ('link:*')," " ('login-group-*')," " ('peer-*')," " ('subrepo:*')," " ('sync-*')," " ('syncfrom:*')," " ('syncwith:*')," " ('ssl-*')" ") SELECT name FROM config, pattern WHERE name GLOB x);" "UPDATE config SET value=lower(hex(randomblob(20)))" " WHERE name='project-code';" "UPDATE config SET value='detached-' || value" " WHERE name='project-name' AND value NOT GLOB 'detached-*';" ); db_protect_pop(); db_end_transaction(0); fossil_print("New project code: %s\n", db_get("project-code","")); } /* ** COMMAND: test-create-clusters ** ** Create clusters for all unclustered artifacts if the number of unclustered ** artifacts exceeds the current clustering threshold. */ void test_createcluster_cmd(void){ if( g.argc==3 ){ db_open_repository(g.argv[2]); }else{ db_find_and_open_repository(0, 0); if( g.argc!=2 ){ 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" " SELECT blob.rid FROM shun JOIN blob USING(uuid);" ); db_prepare(&q, "SELECT rid FROM unclustered WHERE rid IN" " (SELECT rid FROM tagxref WHERE tagid=%d)", TAG_CLUSTER ); while( db_step(&q)==SQLITE_ROW ){ 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]; int crid = name_to_rid(zUuid); if( crid==0 ){ fossil_warning("cluster (rid=%d) references unknown artifact %s", rid, zUuid); continue; } db_multi_exec("INSERT OR IGNORE INTO xdone VALUES(%d)", crid); if( db_exists("SELECT 1 FROM tagxref WHERE tagid=%d AND rid=%d", TAG_CLUSTER, crid) ){ bag_insert(&pending, crid); } } manifest_destroy(p); } n = db_int(0, "SELECT count(*) FROM /*scan*/" " (SELECT rid FROM blob EXCEPT SELECT x FROM xdone)"); if( n==0 ){ fossil_print("all artifacts reachable through clusters\n"); }else{ fossil_print("%d unreachable artifacts:\n", n); db_prepare(&q, "SELECT rid, uuid FROM blob WHERE rid NOT IN xdone"); while( db_step(&q)==SQLITE_ROW ){ fossil_print(" %3d %s\n", db_column_int(&q,0), db_column_text(&q,1)); } db_finalize(&q); } } /* ** COMMAND: scrub* ** ** Usage: %fossil scrub ?OPTIONS? ?REPOSITORY? ** ** The command removes sensitive information (such as passwords) from a ** repository so that the repository can be sent to an untrusted reader. ** ** By default, only passwords are removed. However, if the --verily option ** is added, then private branches, concealed email addresses, IP ** addresses of correspondents, and similar privacy-sensitive fields ** are also purged. If the --private option is used, then only private ** branches are removed and all other information is left intact. ** ** This command permanently deletes the scrubbed information. THE EFFECTS ** OF THIS COMMAND ARE IRREVERSIBLE. USE WITH CAUTION! ** ** The user is prompted to confirm the scrub unless the --force option ** is used. ** ** Options: ** --force Do not prompt for confirmation ** --private Only private branches are removed from the repository ** --verily Scrub real thoroughly (see above) */ void scrub_cmd(void){ 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); } } db_begin_transaction(); if( privateOnly || bVerily ){ bNeedRebuild = db_exists("SELECT 1 FROM private"); delete_private_content(); } if( !privateOnly ){ db_unprotect(PROTECT_ALL); db_multi_exec( "PRAGMA secure_delete=ON;" "UPDATE user SET pw='';" "DELETE FROM config WHERE name IN" "(WITH pattern(x) AS (VALUES" " ('baseurl:*')," " ('cert:*')," " ('ckout:*')," " ('draft[1-9]-*')," " ('gitpush:*')," " ('http-auth:*')," " ('last-sync-*')," " ('link:*')," " ('login-group-*')," " ('parent-project-*')," " ('peer-*')," " ('skin:*')," " ('subrepo:*')," " ('sync-*')," " ('syncfrom:*')," " ('syncwith:*')," " ('ssl-*')" ") SELECT name FROM config, pattern WHERE name GLOB x);" ); 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" "DROP TABLE IF EXISTS chat;\n" ); } db_protect_pop(); } if( !bNeedRebuild ){ db_end_transaction(0); db_unprotect(PROTECT_ALL); db_multi_exec("VACUUM;"); db_protect_pop(); }else{ rebuild_db(1, 0); db_end_transaction(0); } } /* ** Recursively read all files from the directory zPath and install ** every file read as a new artifact in the repository. */ void recon_read_dir(char *zPath){ DIR *d; struct dirent *pEntry; Blob aContent; /* content of the just read artifact */ static int nFileRead = 0; void *zUnicodePath; char *zUtf8Name; static int recursionLevel = 0; /* Bookkeeping about the recursion level */ static char *zFnRid1 = 0; /* The file holding the artifact with RID=1 */ static int cchPathInitial = 0; /* The length of zPath on first recursion */ recursionLevel++; if( recursionLevel==1 ){ cchPathInitial = strlen(zPath); if( fKeepRid1!=0 ){ char *zFnDotRid1 = mprintf("%s/.rid1", zPath); Blob bFileContents; if( blob_read_from_file(&bFileContents, zFnDotRid1, ExtFILE)!=-1 ){ Blob line, value; while( blob_line(&bFileContents, &line)>0 ){ if( blob_token(&line, &value)==0 ) continue; /* Empty line */ if( blob_buffer(&value)[0]=='#' ) continue; /* Comment */ blob_trim(&value); zFnRid1 = mprintf("%s/%s", zPath, blob_str(&value)); break; } blob_reset(&bFileContents); if( zFnRid1 ){ if( blob_read_from_file(&aContent, zFnRid1, ExtFILE)==-1 ){ fossil_fatal("some unknown error occurred while reading \"%s\"", zFnRid1); }else{ recon_set_hash_policy(0, zFnRid1); content_put(&aContent); recon_restore_hash_policy(); blob_reset(&aContent); fossil_print("\r%d", ++nFileRead); fflush(stdout); } }else{ fossil_fatal("an error occurred while reading or parsing \"%s\"", zFnDotRid1); } } free(zFnDotRid1); } } zUnicodePath = fossil_utf8_to_path(zPath, 1); d = opendir(zUnicodePath); if( d ){ while( (pEntry=readdir(d))!=0 ){ Blob path; char *zSubpath; if( pEntry->d_name[0]=='.' ){ continue; } zUtf8Name = fossil_path_to_utf8(pEntry->d_name); zSubpath = mprintf("%s/%s", zPath, zUtf8Name); fossil_path_free(zUtf8Name); #ifdef _DIRENT_HAVE_D_TYPE if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) ? (file_isdir(zSubpath, ExtFILE)==1) : (pEntry->d_type==DT_DIR) ) #else if( file_isdir(zSubpath, ExtFILE)==1 ) #endif { recon_read_dir(zSubpath); }else if( fossil_strcmp(zSubpath, zFnRid1)!=0 ){ blob_init(&path, 0, 0); blob_appendf(&path, "%s", zSubpath); if( blob_read_from_file(&aContent, blob_str(&path), ExtFILE)==-1 ){ fossil_fatal("some unknown error occurred while reading \"%s\"", blob_str(&path)); } recon_set_hash_policy(cchPathInitial, blob_str(&path)); content_put(&aContent); recon_restore_hash_policy(); 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_path_free(zUnicodePath); if( recursionLevel==1 && zFnRid1!=0 ) free(zFnRid1); recursionLevel--; } /* ** Helper functions called from recon_read_dir() to set and restore the correct ** hash policy for an artifact read from disk, inferred from the length of the ** path name. */ static int saved_eHashPolicy = -1; void recon_set_hash_policy( const int cchPathPrefix, /* Directory prefix length for zUuidAsFilePath */ const char *zUuidAsFilePath /* Relative, well-formed, from recon_read_dir() */ ){ int cchUuidAsFilePath; const char *zHashPart; int cchHashPart = 0; int new_eHashPolicy = -1; assert( HNAME_COUNT==2 ); /* Review function if new hashes are implemented. */ if( zUuidAsFilePath==0 ) return; cchUuidAsFilePath = strlen(zUuidAsFilePath); if( cchUuidAsFilePath==0 ) return; if( cchPathPrefix>=cchUuidAsFilePath ) return; for( zHashPart = zUuidAsFilePath + cchPathPrefix; *zHashPart; zHashPart++ ){ if( *zHashPart!='/' ) cchHashPart++; } if( cchHashPart>=HNAME_LEN_K256 ){ new_eHashPolicy = HPOLICY_SHA3; }else if( cchHashPart>=HNAME_LEN_SHA1 ){ new_eHashPolicy = HPOLICY_SHA1; } if( new_eHashPolicy!=-1 ){ saved_eHashPolicy = g.eHashPolicy; g.eHashPolicy = new_eHashPolicy; } } void recon_restore_hash_policy(){ if( saved_eHashPolicy!=-1 ){ g.eHashPolicy = saved_eHashPolicy; saved_eHashPolicy = -1; } } #if 0 /* ** COMMAND: test-hash-from-path* ** ** Usage: %fossil test-hash-from-path ?OPTIONS? DESTINATION UUID ** ** Generate a sample path name from DESTINATION and UUID, as the `deconstruct' ** command would do. Then try to guess the hash policy from the path name, as ** the `reconstruct' command would do. ** ** No files or directories will be created. ** ** Options: ** -L|--prefixlength N Set the length of the names of the DESTINATION ** subdirectories to N */ void test_hash_from_path_cmd(void) { char *zDest; char *zUuid; char *zFile; const char *zHashPolicy = "unknown"; const char *zPrefixOpt = find_option("prefixlength","L",1); int iPrefixLength; if( !zPrefixOpt ){ iPrefixLength = 2; }else{ iPrefixLength = atoi(zPrefixOpt); if( iPrefixLength<0 || iPrefixLength>9 ){ fossil_fatal("N(%s) is not a valid prefix length!",zPrefixOpt); } } if( g.argc!=4 ){ usage ("?OPTIONS? DESTINATION UUID"); } zDest = g.argv[2]; zUuid = g.argv[3]; if( iPrefixLength ){ zFNameFormat = mprintf("%s/%%.%ds/%%s",zDest,iPrefixLength); }else{ zFNameFormat = mprintf("%s/%%s",zDest); } cchFNamePrefix = strlen(zDest); zFile = mprintf(zFNameFormat /*works-like:"%s:%s"*/, zUuid, zUuid+iPrefixLength); recon_set_hash_policy(cchFNamePrefix,zFile); if( saved_eHashPolicy!=-1 ){ zHashPolicy = hpolicy_name(); } recon_restore_hash_policy(); fossil_print( "\nPath Name: %s" "\nHash Policy: %s\n", zFile,zHashPolicy); free(zFile); free(zFNameFormat); zFNameFormat = 0; cchFNamePrefix = 0; } #endif /* ** Helper functions used by the `deconstruct' and `reconstruct' commands to ** save and restore the contents of the PRIVATE table. */ void private_export(char *zFileName) { Stmt q; Blob fctx = empty_blob; blob_append(&fctx, "# The hashes of private artifacts\n", -1); db_prepare(&q, "SELECT uuid FROM blob WHERE rid IN ( SELECT rid FROM private );"); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); blob_append(&fctx, zUuid, -1); blob_append(&fctx, "\n", -1); } db_finalize(&q); blob_write_to_file(&fctx, zFileName); blob_reset(&fctx); } void private_import(char *zFileName) { Blob fctx; if( blob_read_from_file(&fctx, zFileName, ExtFILE)!=-1 ){ Blob line, value; while( blob_line(&fctx, &line)>0 ){ char *zUuid; int nUuid; if( blob_token(&line, &value)==0 ) continue; /* Empty line */ if( blob_buffer(&value)[0]=='#' ) continue; /* Comment */ blob_trim(&value); zUuid = blob_buffer(&value); nUuid = blob_size(&value); zUuid[nUuid] = 0; if( hname_validate(zUuid, nUuid)!=HNAME_ERROR ){ canonical16(zUuid, nUuid); db_multi_exec( "INSERT OR IGNORE INTO private" " SELECT rid FROM blob WHERE uuid = %Q;", zUuid); } } blob_reset(&fctx); } } /* ** COMMAND: reconstruct* ** ** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY ** ** This command studies the artifacts (files) in DIRECTORY and reconstructs the ** Fossil record from them. It places the new Fossil repository in FILENAME. ** Subdirectories are read, files with leading '.' in the filename are ignored. ** ** Options: ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the ** file .rid in DIRECTORY. ** -P|--keep-private Mark the artifacts listed in the file .private in ** DIRECTORY as private in the new Fossil repository. */ void reconstruct_cmd(void) { char *zPassword; int fKeepPrivate; fKeepRid1 = find_option("keep-rid1","K",0)!=0; fKeepPrivate = find_option("keep-private","P",0)!=0; if( g.argc!=4 ){ usage("FILENAME DIRECTORY"); } if( file_isdir(g.argv[3], ExtFILE)!=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, 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(1, 1); /* Backwards compatibility: Mark check-ins with "+private" tags as private. */ reconstruct_private_table(); /* Newer method: Import the list of private artifacts to the PRIVATE table. */ if( fKeepPrivate ){ char *zFnDotPrivate = mprintf("%s/.private", g.argv[3]); private_import(zFnDotPrivate); free(zFnDotPrivate); } /* 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); hash_user_password(g.zLogin); } /* ** 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 REPO Deconstruct given REPOSITORY ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to ** the file .rid1 in the DESTINATION directory ** -L|--prefixlength N Set the length of the names of the DESTINATION ** subdirectories to N ** --private Include private artifacts ** -P|--keep-private Save the list of private artifacts to the file ** .private in the DESTINATION directory (implies ** the --private option) */ void deconstruct_cmd(void){ const char *zPrefixOpt; Stmt s; int privateFlag; int fKeepPrivate; fKeepRid1 = find_option("keep-rid1","K",0)!=0; /* get and check prefix length argument and build format string */ zPrefixOpt=find_option("prefixlength","L",1); if( !zPrefixOpt ){ prefixLength = 2; }else{ if( zPrefixOpt[0]>='0' && zPrefixOpt[0]<='9' && !zPrefixOpt[1] ){ prefixLength = (int)(*zPrefixOpt-'0'); }else{ fossil_fatal("N(%s) is not a valid prefix length!",zPrefixOpt); } } /* open repository and open query for all artifacts */ db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); privateFlag = find_option("private",0,0)!=0; fKeepPrivate = find_option("keep-private","P",0)!=0; if( fKeepPrivate ) privateFlag = 1; verify_all_options(); /* check number of arguments */ if( g.argc!=3 ){ usage ("?OPTIONS? DESTINATION"); } /* get and check argument destination directory */ zDestDir = g.argv[g.argc-1]; if( !*zDestDir || !file_isdir(zDestDir, ExtFILE)) { fossil_fatal("DESTINATION(%s) is not a directory!",zDestDir); } #ifndef _WIN32 if( file_access(zDestDir, W_OK) ){ fossil_fatal("DESTINATION(%s) is not writeable!",zDestDir); } #else /* write access on windows is not checked, errors will be ** detected on blob_write_to_file */ #endif if( prefixLength ){ zFNameFormat = mprintf("%s/%%.%ds/%%s",zDestDir,prefixLength); }else{ zFNameFormat = mprintf("%s/%%s",zDestDir); } cchFNamePrefix = strlen(zDestDir); bag_init(&bagDone); ttyOutput = 1; processCnt = 0; if (!g.fQuiet) { fossil_print("0 (0%%)...\r"); fflush(stdout); } totalSize = db_int(0, "SELECT count(*) FROM blob"); db_prepare(&s, "SELECT rid, size FROM blob /*scan*/" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" " AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid) %s", privateFlag==0 ? "AND rid NOT IN private" : "" ); while( db_step(&s)==SQLITE_ROW ){ int rid = db_column_int(&s, 0); int size = db_column_int(&s, 1); if( size>=0 ){ Blob content; content_get(rid, &content); rebuild_step(rid, size, &content); } } db_finalize(&s); db_prepare(&s, "SELECT rid, size FROM blob" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid) %s", privateFlag==0 ? "AND rid NOT IN private" : "" ); while( db_step(&s)==SQLITE_ROW ){ int rid = db_column_int(&s, 0); int size = db_column_int(&s, 1); if( size>=0 ){ if( !bag_find(&bagDone, rid) ){ Blob content; content_get(rid, &content); rebuild_step(rid, size, &content); } } } db_finalize(&s); /* Export the list of private artifacts. */ if( fKeepPrivate ){ char *zFnDotPrivate = mprintf("%s/.private", zDestDir); private_export(zFnDotPrivate); free(zFnDotPrivate); } if(!g.fQuiet && ttyOutput ){ fossil_print("\n"); } /* free filename format string */ free(zFNameFormat); zFNameFormat = 0; } |
Added src/regexp.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 | /* ** Copyright (c) 2013 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 was adapted from the ext/misc/regexp.c file in SQLite3. That ** file is in the public domain. ** ** See ../www/grep.md for details of the algorithm and RE dialect. ** ** ** The following regular expression syntax is supported: ** ** X* zero or more occurrences of X ** X+ one or more occurrences of X ** X? zero or one occurrences of X ** X{p,q} between p and q occurrences of X ** (X) match X ** X|Y X or Y ** ^X X occurring at the beginning of the string ** X$ X occurring at the end of the string ** . Match any single character ** \c Character c where c is one of \{}()[]|*+?. ** \c C-language escapes for c in afnrtv. ex: \t or \n ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX ** \xXX Where XX is exactly 2 hex digits, unicode value XX ** [abc] Any single character from the set abc ** [^abc] Any single character not in the set abc ** [a-z] Any single character in the range a-z ** [^a-z] Any single character not in the range a-z ** \b Word boundary ** \w Word character. [A-Za-z0-9_] ** \W Non-word character ** \d Digit ** \D Non-digit ** \s Whitespace character ** \S Non-whitespace character ** ** A nondeterministic finite automaton (NFA) is used for matching, so the ** performance is bounded by O(N*M) where N is the size of the regular ** expression and M is the size of the input string. The matcher never ** exhibits exponential behavior. Note that the X{p,q} operator expands ** to p copies of X following by q-p copies of X? and that the size of the ** regular expression in the O(N*M) performance bound is computed after ** this expansion. */ #include "config.h" #include "regexp.h" /* The end-of-input character */ #define RE_EOF 0 /* End of input */ /* The NFA is implemented as sequence of opcodes taken from the following ** set. Each opcode has a single integer argument. */ #define RE_OP_MATCH 1 /* Match the one character in the argument */ #define RE_OP_ANY 2 /* Match any one character. (Implements ".") */ #define RE_OP_ANYSTAR 3 /* Special optimized version of .* */ #define RE_OP_FORK 4 /* Continue to both next and opcode at iArg */ #define RE_OP_GOTO 5 /* Jump to opcode at iArg */ #define RE_OP_ACCEPT 6 /* Halt and indicate a successful match */ #define RE_OP_CC_INC 7 /* Beginning of a [...] character class */ #define RE_OP_CC_EXC 8 /* Beginning of a [^...] character class */ #define RE_OP_CC_VALUE 9 /* Single value in a character class */ #define RE_OP_CC_RANGE 10 /* Range of values in a character class */ #define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */ #define RE_OP_NOTWORD 12 /* Not a perl word character */ #define RE_OP_DIGIT 13 /* digit: [0-9] */ #define RE_OP_NOTDIGIT 14 /* Not a digit */ #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */ #define RE_OP_NOTSPACE 16 /* Not a digit */ #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */ /* Each opcode is a "state" in the NFA */ typedef unsigned short ReStateNumber; /* Because this is an NFA and not a DFA, multiple states can be active at ** once. An instance of the following object records all active states in ** the NFA. The implementation is optimized for the common case where the ** number of actives states is small. */ typedef struct ReStateSet { unsigned nState; /* Number of current states */ ReStateNumber *aState; /* Current states */ } ReStateSet; #if INTERFACE /* An input string read one character at a time. */ struct ReInput { const unsigned char *z; /* All text */ int i; /* Next byte to read */ int mx; /* EOF when i>=mx */ }; /* A compiled NFA (or an NFA that is in the process of being compiled) is ** an instance of the following object. */ struct ReCompiled { ReInput sIn; /* Regular expression text */ const char *zErr; /* Error message to return */ char *aOp; /* Operators for the virtual machine */ int *aArg; /* Arguments to each operator */ unsigned (*xNextChar)(ReInput*); /* Next character function */ unsigned char zInit[12]; /* Initial text to match */ int nInit; /* Number of characters in zInit */ unsigned nState; /* Number of entries in aOp[] and aArg[] */ unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */ }; #endif /* Add a state to the given state set if it is not already there */ static void re_add_state(ReStateSet *pSet, int newState){ unsigned i; for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return; pSet->aState[pSet->nState++] = (ReStateNumber)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 ){ if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){ c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f); if( c<0x80 ) c = 0xfffd; }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80 && (p->z[p->i+1]&0xc0)==0x80 ){ c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f); p->i += 2; if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80 && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){ c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6) | (p->z[p->i+2]&0x3f); p->i += 3; if( c<=0xffff || c>0x10ffff ) c = 0xfffd; }else{ c = 0xfffd; } } return c; } static unsigned re_next_char_nocase(ReInput *p){ unsigned c = re_next_char(p); return unicode_fold(c,2); } /* Return true if c is a perl "word" character: [A-Za-z0-9_] */ static int re_word_char(int c){ return unicode_isalnum(c) || c=='_'; } /* Return true if c is a "digit" character: [0-9] */ static int re_digit_char(int c){ return (c>='0' && c<='9'); } /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ static int re_space_char(int c){ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; } /* Run a compiled regular expression on the zero-terminated input ** string zIn[]. Return true on a match and false if there is no match. */ int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ ReStateSet aStateSet[2], *pThis, *pNext; ReStateNumber aSpace[100]; ReStateNumber *pToFree; unsigned int i = 0; unsigned int iSwap = 0; int c = RE_EOF+1; int cPrev = 0; int rc = 0; ReInput in; in.z = zIn; in.i = 0; in.mx = nIn>=0 ? nIn : (int)strlen((char const*)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; } if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){ pToFree = 0; aStateSet[0].aState = aSpace; }else{ pToFree = fossil_malloc( sizeof(ReStateNumber)*2*pRe->nState ); if( pToFree==0 ) return -1; aStateSet[0].aState = pToFree; } aStateSet[1].aState = &aStateSet[0].aState[pRe->nState]; pNext = &aStateSet[1]; pNext->nState = 0; re_add_state(pNext, 0); while( c!=RE_EOF && pNext->nState>0 ){ cPrev = c; c = pRe->xNextChar(&in); pThis = pNext; pNext = &aStateSet[iSwap]; iSwap = 1 - iSwap; pNext->nState = 0; for(i=0; i<pThis->nState; i++){ int x = pThis->aState[i]; switch( pRe->aOp[x] ){ case RE_OP_MATCH: { if( pRe->aArg[x]==c ) re_add_state(pNext, x+1); break; } case RE_OP_ANY: { if( c!=0 ) re_add_state(pNext, x+1); break; } case RE_OP_WORD: { if( re_word_char(c) ) re_add_state(pNext, x+1); break; } case RE_OP_NOTWORD: { if( !re_word_char(c) && c!=0 ) re_add_state(pNext, x+1); break; } case RE_OP_DIGIT: { if( re_digit_char(c) ) re_add_state(pNext, x+1); break; } case RE_OP_NOTDIGIT: { if( !re_digit_char(c) && c!=0 ) re_add_state(pNext, x+1); break; } case RE_OP_SPACE: { if( re_space_char(c) ) re_add_state(pNext, x+1); break; } case RE_OP_NOTSPACE: { if( !re_space_char(c) && c!=0 ) re_add_state(pNext, x+1); break; } case RE_OP_BOUNDARY: { if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1); break; } case RE_OP_ANYSTAR: { re_add_state(pNext, x); re_add_state(pThis, x+1); break; } case RE_OP_FORK: { re_add_state(pThis, x+pRe->aArg[x]); re_add_state(pThis, x+1); break; } case RE_OP_GOTO: { re_add_state(pThis, x+pRe->aArg[x]); break; } case RE_OP_ACCEPT: { rc = 1; goto re_match_end; } case RE_OP_CC_EXC: { if( c==0 ) break; /* fall-through */ } case RE_OP_CC_INC: { int j = 1; int n = pRe->aArg[x]; int hit = 0; for(j=1; j>0 && j<n; j++){ if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){ if( pRe->aArg[x+j]==c ){ hit = 1; j = -1; } }else{ if( pRe->aArg[x+j]<=c && pRe->aArg[x+j+1]>=c ){ hit = 1; j = -1; }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; } } re_match_end: fossil_free(pToFree); return rc; } /* Resize the opcode and argument arrays for an RE under construction. */ static int re_resize(ReCompiled *p, int N){ char *aOp; int *aArg; aOp = fossil_realloc(p->aOp, N*sizeof(p->aOp[0])); if( aOp==0 ) return 1; p->aOp = aOp; aArg = fossil_realloc(p->aArg, N*sizeof(p->aArg[0])); if( aArg==0 ) return 1; p->aArg = aArg; p->nAlloc = N; return 0; } /* Insert a new opcode and argument into an RE under construction. The ** insertion point is just prior to existing opcode iBefore. */ static int re_insert(ReCompiled *p, int iBefore, int op, int arg){ int i; if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0; for(i=p->nState; i>iBefore; i--){ p->aOp[i] = p->aOp[i-1]; p->aArg[i] = p->aArg[i-1]; } p->nState++; p->aOp[iBefore] = (char)op; p->aArg[iBefore] = arg; return iBefore; } /* Append a new opcode and argument to the end of the RE under construction. */ static int re_append(ReCompiled *p, int op, int arg){ return re_insert(p, p->nState, op, arg); } /* Make a copy of N opcodes starting at iStart onto the end of the RE ** under construction. */ static void re_copy(ReCompiled *p, int iStart, int N){ if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return; memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0])); memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0])); p->nState += N; } /* Return true if c is a hexadecimal digit character: [0-9a-fA-F] ** If c is a hex digit, also set *pV = (*pV)*16 + valueof(c). If ** c is not a hex digit *pV is unchanged. */ static int re_hex(int c, int *pV){ if( c>='0' && c<='9' ){ c -= '0'; }else if( c>='a' && c<='f' ){ c -= 'a' - 10; }else if( c>='A' && c<='F' ){ c -= 'A' - 10; }else{ return 0; } *pV = (*pV)*16 + (c & 0xff); return 1; } /* A backslash character has been seen, read the next character and ** return its interpretation. */ static unsigned re_esc_char(ReCompiled *p){ static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; static const char zTrans[] = "\a\f\n\r\t\v"; int i, v = 0; char c; if( p->sIn.i>=p->sIn.mx ) return 0; c = p->sIn.z[p->sIn.i]; if( c=='u' && p->sIn.i+4<p->sIn.mx ){ const unsigned char *zIn = p->sIn.z + p->sIn.i; if( re_hex(zIn[1],&v) && re_hex(zIn[2],&v) && re_hex(zIn[3],&v) && re_hex(zIn[4],&v) ){ p->sIn.i += 5; return v; } } if( c=='x' && p->sIn.i+2<p->sIn.mx ){ const unsigned char *zIn = p->sIn.z + p->sIn.i; if( re_hex(zIn[1],&v) && re_hex(zIn[2],&v) ){ p->sIn.i += 3; return v; } } for(i=0; zEsc[i] && zEsc[i]!=c; i++){} if( zEsc[i] ){ if( i<6 ) c = zTrans[i]; p->sIn.i++; }else{ p->zErr = "unknown \\ escape"; } return c; } /* Forward declaration */ static const char *re_subcompile_string(ReCompiled*); /* Peek at the next byte of input */ static unsigned char rePeek(ReCompiled *p){ return p->sIn.i<p->sIn.mx ? p->sIn.z[p->sIn.i] : 0; } /* Compile RE text into a sequence of opcodes. Continue up to the ** first unmatched ")" character, then return. If an error is found, ** return a pointer to the error message string. */ static const char *re_subcompile_re(ReCompiled *p){ const char *zErr; int iStart, iEnd, iGoto; iStart = p->nState; zErr = re_subcompile_string(p); if( zErr ) return zErr; while( rePeek(p)=='|' ){ iEnd = p->nState; re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart); iGoto = re_append(p, RE_OP_GOTO, 0); p->sIn.i++; zErr = re_subcompile_string(p); if( zErr ) return zErr; p->aArg[iGoto] = p->nState - iGoto; } return 0; } /* Compile an element of regular expression text (anything that can be ** an operand to the "|" operator). Return NULL on success or a pointer ** to the error message if there is a problem. */ static const char *re_subcompile_string(ReCompiled *p){ int iPrev = -1; 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); re_append(p, RE_OP_FORK, iPrev - p->nState + 1); break; } case '+': { if( iPrev<0 ) return "'+' without operand"; re_append(p, RE_OP_FORK, iPrev - p->nState); break; } case '?': { if( iPrev<0 ) return "'?' without operand"; re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1); break; } case '{': { int m = 0, n = 0; int sz, j; if( iPrev<0 ) return "'{m,n}' without operand"; while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; } n = m; if( c==',' ){ p->sIn.i++; n = 0; while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; } } if( c!='}' ) return "unmatched '{'"; if( n>0 && n<m ) return "n less than m in '{m,n}'"; p->sIn.i++; sz = p->nState - iPrev; if( m==0 ){ if( n==0 ) return "both m and n are zero in '{m,n}'"; re_insert(p, iPrev, RE_OP_FORK, sz+1); n--; }else{ for(j=1; j<m; j++) re_copy(p, iPrev, sz); } for(j=m; j<n; j++){ re_append(p, RE_OP_FORK, sz+1); re_copy(p, iPrev, sz); } if( n==0 && m>0 ){ re_append(p, RE_OP_FORK, -sz); } break; } case '[': { int iFirst = p->nState; if( rePeek(p)=='^' ){ re_append(p, RE_OP_CC_EXC, 0); p->sIn.i++; }else{ re_append(p, RE_OP_CC_INC, 0); } while( (c = p->xNextChar(&p->sIn))!=0 ){ if( c=='[' && rePeek(p)==':' ){ return "POSIX character classes not supported"; } if( c=='\\' ) c = re_esc_char(p); if( rePeek(p)=='-' ){ re_append(p, RE_OP_CC_RANGE, c); p->sIn.i++; c = p->xNextChar(&p->sIn); if( c=='\\' ) c = re_esc_char(p); re_append(p, RE_OP_CC_RANGE, c); }else{ re_append(p, RE_OP_CC_VALUE, c); } if( rePeek(p)==']' ){ p->sIn.i++; break; } } if( c==0 ) return "unclosed '['"; p->aArg[iFirst] = p->nState - iFirst; break; } case '\\': { int specialOp = 0; switch( rePeek(p) ){ case 'b': specialOp = RE_OP_BOUNDARY; break; case 'd': specialOp = RE_OP_DIGIT; break; case 'D': specialOp = RE_OP_NOTDIGIT; break; case 's': specialOp = RE_OP_SPACE; break; case 'S': specialOp = RE_OP_NOTSPACE; break; case 'w': specialOp = RE_OP_WORD; break; case 'W': specialOp = RE_OP_NOTWORD; break; } if( specialOp ){ p->sIn.i++; re_append(p, specialOp, 0); }else{ c = re_esc_char(p); re_append(p, RE_OP_MATCH, c); } break; } default: { re_append(p, RE_OP_MATCH, c); break; } } iPrev = iStart; } return 0; } /* Free and reclaim all the memory used by a previously compiled ** regular expression. Applications should invoke this routine once ** for every call to re_compile() to avoid memory leaks. */ void re_free(ReCompiled *pRe){ if( pRe ){ fossil_free(pRe->aOp); fossil_free(pRe->aArg); fossil_free(pRe); } } /* ** Compile a textual regular expression in zIn[] into a compiled regular ** expression suitable for us by re_match() and return a pointer to the ** compiled regular expression in *ppRe. Return NULL on success or an ** error message if something goes wrong. */ const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ ReCompiled *pRe; const char *zErr; int i, j; *ppRe = 0; pRe = fossil_malloc( sizeof(*pRe) ); if( pRe==0 ){ return "out of memory"; } memset(pRe, 0, sizeof(*pRe)); pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char; if( re_resize(pRe, 30) ){ re_free(pRe); return "out of memory"; } if( zIn[0]=='^' ){ zIn++; }else{ re_append(pRe, RE_OP_ANYSTAR, 0); } pRe->sIn.z = (unsigned char*)zIn; pRe->sIn.i = 0; pRe->sIn.mx = (int)strlen(zIn); zErr = re_subcompile_re(pRe); if( zErr ){ re_free(pRe); return zErr; } if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){ re_append(pRe, RE_OP_MATCH, RE_EOF); re_append(pRe, RE_OP_ACCEPT, 0); *ppRe = pRe; }else if( pRe->sIn.i>=pRe->sIn.mx ){ re_append(pRe, RE_OP_ACCEPT, 0); *ppRe = pRe; }else{ 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 && !noCase ){ for(j=0, i=1; j<(int)sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ unsigned x = pRe->aArg[i]; if( x<=127 ){ pRe->zInit[j++] = (unsigned char)x; }else if( x<=0xfff ){ pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6)); pRe->zInit[j++] = 0x80 | (x&0x3f); }else if( x<=0xffff ){ pRe->zInit[j++] = (unsigned char)(0xd0 | (x>>12)); pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); pRe->zInit[j++] = 0x80 | (x&0x3f); }else{ break; } } if( j>0 && pRe->zInit[j-1]==0 ) j--; pRe->nInit = j; } return pRe->zErr; } /* ** Implementation of the regexp() SQL function. This function implements ** the build-in REGEXP operator. The first argument to the function is the ** 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 */ int setAux = 0; /* True to invoke sqlite3_set_auxdata() */ (void)argc; /* Unused */ pRe = sqlite3_get_auxdata(context, 0); if( pRe==0 ){ zPattern = (const char*)sqlite3_value_text(argv[0]); if( zPattern==0 ) return; zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0); if( zErr ){ re_free(pRe); sqlite3_result_error(context, zErr, -1); return; } if( pRe==0 ){ sqlite3_result_error_nomem(context); return; } setAux = 1; } zStr = (const unsigned char*)sqlite3_value_text(argv[1]); if( zStr!=0 ){ sqlite3_result_int(context, re_match(pRe, zStr, -1)); } if( setAux ){ sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free); } } /* ** Invoke this routine to register the regexp() function with the ** SQLite database connection. */ int re_add_sql_func(sqlite3 *db){ int rc; rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8|SQLITE_INNOCUOUS, 0, re_sql_func, 0, 0); if( rc==SQLITE_OK ){ /* The regexpi(PATTERN,STRING) function is a case-insensitive version ** of regexp(PATTERN,STRING). */ rc = sqlite3_create_function(db, "regexpi", 2, SQLITE_UTF8|SQLITE_INNOCUOUS, (void*)db, re_sql_func, 0, 0); } return rc; } /* ** Run a "grep" over a single file read from disk. */ static void grep_file(ReCompiled *pRe, const char *zFile, FILE *in){ int ln = 0; int n; char zLine[2000]; while( fgets(zLine, sizeof(zLine), in) ){ ln++; n = (int)strlen(zLine); while( n && (zLine[n-1]=='\n' || zLine[n-1]=='\r') ) n--; if( re_match(pRe, (const unsigned char*)zLine, n) ){ fossil_print("%s:%d:%.*s\n", zFile, ln, n, zLine); } } } /* ** Flags for grep_buffer() */ #define GREP_EXISTS 0x001 /* If any match, print only the name and stop */ #define GREP_QUIET 0x002 /* Return code only */ /* ** Run a "grep" over a text file */ static int grep_buffer( ReCompiled *pRe, const char *zName, const char *z, u32 flags ){ int i, j, n, ln, cnt; for(i=j=ln=cnt=0; z[i]; i=j+1){ for(j=i; z[j] && z[j]!='\n'; j++){} n = j - i; ln++; if( re_match(pRe, (const unsigned char*)(z+i), j-i) ){ cnt++; if( flags & GREP_EXISTS ){ if( (flags & GREP_QUIET)==0 && zName ) fossil_print("%s\n", zName); break; } if( (flags & GREP_QUIET)==0 ){ if( cnt==1 && zName ){ fossil_print("== %s\n", zName); } fossil_print("%d:%.*s\n", ln, n, z+i); } } } return cnt; } /* ** COMMAND: test-grep ** ** Usage: %fossil test-grep REGEXP [FILE...] ** ** Run a regular expression match over the named disk files, or against ** standard input if no disk files are named on the command-line. ** ** Options: ** -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_file(pRe, "-", stdin); }else{ int i; for(i=3; i<g.argc; i++){ FILE *in = fossil_fopen(g.argv[i], "rb"); if( in==0 ){ fossil_warning("cannot open \"%s\"", g.argv[i]); }else{ grep_file(pRe, g.argv[i], in); fclose(in); } } } re_free(pRe); } /* ** COMMAND: grep ** ** Usage: %fossil grep [OPTIONS] PATTERN FILENAME ... ** ** Attempt to match the given POSIX extended regular expression PATTERN over ** all historic versions of FILENAME. The search begins with the most recent ** version of the file and moves backwards in time. Multiple FILENAMEs can ** be specified, in which case all named files are searched in reverse ** chronological order. ** ** For details of the supported regular expression dialect, see ** https://fossil-scm.org/fossil/doc/trunk/www/grep.md ** ** Options: ** -c|--count Suppress normal output; instead print a count ** of the number of matching files ** -i|--ignore-case Ignore case ** -l|--files-with-matches List only hash for each match ** --once Stop searching after the first match ** -s|--no-messages Suppress error messages about nonexistent ** or unreadable files ** -v|--invert-match Invert the sense of matching. Show only ** files that have no matches. Implies -l ** --verbose Show each file as it is analyzed */ void re_grep_cmd(void){ u32 flags = 0; int bVerbose = 0; ReCompiled *pRe; const char *zErr; int ignoreCase = 0; Blob fullName; int ii; int nMatch = 0; int bNoMsg; int cntFlag; int bOnce; int bInvert; int nSearch = 0; Stmt q; if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1; if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS; if( find_option("verbose",0,0)!=0 ) bVerbose = 1; if( find_option("quiet","q",0) ) flags |= GREP_QUIET|GREP_EXISTS; bNoMsg = find_option("no-messages","s",0)!=0; bOnce = find_option("once",0,0)!=0; bInvert = find_option("invert-match","v",0)!=0; if( bInvert ){ flags |= GREP_QUIET|GREP_EXISTS; } cntFlag = find_option("count","c",0)!=0; if( cntFlag ){ flags |= GREP_QUIET|GREP_EXISTS; } db_find_and_open_repository(0, 0); verify_all_options(); if( g.argc<4 ){ usage("REGEXP FILENAME ..."); } zErr = re_compile(&pRe, g.argv[2], ignoreCase); if( zErr ) fossil_fatal("%s", zErr); add_content_sql_commands(g.db); db_multi_exec("CREATE TEMP TABLE arglist(iname,fname,fnid);"); for(ii=3; ii<g.argc; ii++){ const char *zTarget = g.argv[ii]; if( file_tree_name(zTarget, &fullName, 0, 1) ){ int fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", blob_str(&fullName)); if( !fnid ){ if( bNoMsg ) continue; if( file_size(zTarget, ExtFILE)<0 ){ fossil_fatal("no such file: %s", zTarget); } fossil_fatal("not a managed file: %s", zTarget); }else{ db_multi_exec( "INSERT INTO arglist(iname,fname,fnid) VALUES(%Q,%Q,%d)", zTarget, blob_str(&fullName), fnid); } } blob_reset(&fullName); } db_prepare(&q, " SELECT" " A.uuid," /* file hash */ " A.rid," /* file rid */ " B.uuid," /* check-in hash */ " datetime(min(event.mtime))," /* check-in time */ " arglist.iname" /* file name */ " FROM arglist, mlink, blob A, blob B, event" " WHERE mlink.mid=event.objid" " AND mlink.fid=A.rid" " AND mlink.mid=B.rid" " AND mlink.fnid=arglist.fnid" " GROUP BY A.uuid" " ORDER BY min(event.mtime) DESC;" ); while( db_step(&q)==SQLITE_ROW ){ const char *zFileHash = db_column_text(&q,0); int rid = db_column_int(&q,1); const char *zCkinHash = db_column_text(&q,2); const char *zDate = db_column_text(&q,3); const char *zFN = db_column_text(&q,4); char *zLabel; Blob cx; content_get(rid, &cx); zLabel = mprintf("%.16s %s %S checkin %S", zDate, zFN,zFileHash,zCkinHash); if( bVerbose ) fossil_print("Scanning: %s\n", zLabel); nSearch++; nMatch += grep_buffer(pRe, zLabel, blob_str(&cx), flags); blob_reset(&cx); if( bInvert && cntFlag==0 ){ if( nMatch==0 ){ fossil_print("== %s\n", zLabel); if( bOnce ) nMatch = 1; }else{ nMatch = 0; } } fossil_free(zLabel); if( nMatch ){ if( (flags & GREP_QUIET)!=0 ) break; if( bOnce ) break; } } db_finalize(&q); re_free(pRe); if( cntFlag ){ if( bInvert ){ fossil_print("%d\n", nSearch-nMatch); }else{ fossil_print("%d\n", nMatch); } } } |
Added src/repolist.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 | /* ** Copyright (c) 2018 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 module contains code to implement the repository list page when ** "fossil server" or "fossil ui" is serving a directory full of repositories. */ #include "config.h" #include "repolist.h" #if INTERFACE /* ** Return value from the remote_repo_info() command. zRepoName is the ** input. All other fields are outputs. */ struct RepoInfo { char *zRepoName; /* Name of the repository file */ int isValid; /* True if zRepoName is a valid Fossil repository */ int isRepolistSkin; /* 1 or 2 if this repository wants to be the skin ** for the repository list. 2 means do use this ** repository but do not display it in the list. */ char *zProjName; /* Project Name. Memory from fossil_malloc() */ char *zLoginGroup; /* Name of login group, or NULL. Malloced() */ double rMTime; /* Last update. Julian day number */ }; #endif /* ** Discover information about the repository given by ** pRepo->zRepoName. The discovered information is stored in other ** fields of the RepoInfo object. */ static void remote_repo_info(RepoInfo *pRepo){ sqlite3 *db; sqlite3_stmt *pStmt; int rc; pRepo->isRepolistSkin = 0; pRepo->isValid = 0; pRepo->zProjName = 0; pRepo->zLoginGroup = 0; pRepo->rMTime = 0.0; g.dbIgnoreErrors++; rc = sqlite3_open_v2(pRepo->zRepoName, &db, SQLITE_OPEN_READWRITE, 0); if( rc ) goto finish_repo_list; rc = sqlite3_prepare_v2(db, "SELECT value FROM config" " WHERE name='repolist-skin'", -1, &pStmt, 0); if( rc ) goto finish_repo_list; if( sqlite3_step(pStmt)==SQLITE_ROW ){ pRepo->isRepolistSkin = sqlite3_column_int(pStmt,0); } sqlite3_finalize(pStmt); if( rc ) goto finish_repo_list; rc = sqlite3_prepare_v2(db, "SELECT value FROM config" " WHERE name='project-name'", -1, &pStmt, 0); if( rc ) goto finish_repo_list; if( sqlite3_step(pStmt)==SQLITE_ROW ){ pRepo->zProjName = fossil_strdup((char*)sqlite3_column_text(pStmt,0)); } sqlite3_finalize(pStmt); rc = sqlite3_prepare_v2(db, "SELECT value FROM config" " WHERE name='login-group-name'", -1, &pStmt, 0); if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ pRepo->zLoginGroup = fossil_strdup((char*)sqlite3_column_text(pStmt,0)); } sqlite3_finalize(pStmt); rc = sqlite3_prepare_v2(db, "SELECT max(mtime) FROM event", -1, &pStmt, 0); if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ pRepo->rMTime = sqlite3_column_double(pStmt,0); } pRepo->isValid = 1; sqlite3_finalize(pStmt); finish_repo_list: g.dbIgnoreErrors--; sqlite3_close(db); } /* ** Generate a web-page that lists all repositories located under the ** g.zRepositoryName directory and return non-zero. ** ** For the special case when g.zRepositoryName is a non-chroot-jail "/", ** compose the list using the "repo:" entries in the global_config ** table of the configuration database. These entries comprise all ** of the repositories known to the "all" command. The special case ** processing is disallowed for chroot jails because g.zRepositoryName ** is always "/" inside a chroot jail and so it cannot be used as a flag ** to signal the special processing in that case. The special case ** processing is intended for the "fossil all ui" command which never ** runs in a chroot jail anyhow. ** ** Or, if no repositories can be located beneath g.zRepositoryName, ** close g.db and return 0. */ int repo_list_page(void){ Blob base; /* document root for all repositories */ int n = 0; /* Number of repositories found */ int allRepo; /* True if running "fossil ui all". ** False if a directory scan of base for repos */ Blob html; /* Html for the body of the repository list */ char *zSkinRepo = 0; /* Name of the repository database used for skins */ char *zSkinUrl = 0; /* URL for the skin database */ assert( g.db==0 ); blob_init(&html, 0, 0); if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){ /* For the special case of the "repository directory" being "/", ** show all of the repositories named in the ~/.fossil database. ** ** On unix systems, then entries are of the form "repo:/home/..." ** and on Windows systems they are like on unix, starting with a "/" ** or they can begin with a drive letter: "repo:C:/Users/...". In either ** case, we want returned path to omit any initial "/". */ db_open_config(1, 0); db_multi_exec( "CREATE TEMP VIEW sfile AS" " SELECT ltrim(substr(name,6),'/') AS 'pathname' FROM global_config" " WHERE name GLOB 'repo:*'" ); allRepo = 1; }else{ /* The default case: All repositories under the g.zRepositoryName ** directory. */ blob_init(&base, g.zRepositoryName, -1); sqlite3_open(":memory:", &g.db); db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); db_multi_exec("CREATE TABLE vfile(pathname);"); vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE); db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'" #if USE_SEE " AND pathname NOT GLOB '*[^/].efossil'" #endif ); allRepo = 0; } n = db_int(0, "SELECT count(*) FROM sfile"); if( n==0 ){ sqlite3_close(g.db); g.db = 0; g.repositoryOpen = 0; g.localOpen = 0; return 0; }else{ Stmt q; double rNow; blob_append_sql(&html, "<table border='0' class='sortable' data-init-sort='1'" " data-column-types='txtxkxt'><thead>\n" "<tr><th>Filename<th width='20'>" "<th>Project Name<th width='20'>" "<th>Last Modified<th width='20'>" "<th>Login Group</tr>\n" "</thead><tbody>\n"); db_prepare(&q, "SELECT pathname" " FROM sfile ORDER BY pathname COLLATE nocase;"); rNow = db_double(0, "SELECT julianday('now')"); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); int nName = (int)strlen(zName); int nSuffix = 7; /* ".fossil" */ char *zUrl; char *zAge; char *zFull; RepoInfo x; sqlite3_int64 iAge; #if USE_SEE int bEncrypted = sqlite3_strglob("*.efossil", zName)==0; if( bEncrypted ) nSuffix = 8; /* ".efossil" */ #endif if( nName<nSuffix ) continue; zUrl = sqlite3_mprintf("%.*s", nName-nSuffix, zName); if( zName[0]=='/' #ifdef _WIN32 || sqlite3_strglob("[a-zA-Z]:/*", zName)==0 #endif ){ zFull = mprintf("%s", zName); }else if ( allRepo ){ zFull = mprintf("/%s", zName); }else{ zFull = mprintf("%s/%s", g.zRepositoryName, zName); } x.zRepoName = zFull; remote_repo_info(&x); if( x.isRepolistSkin ){ if( zSkinRepo==0 ){ zSkinRepo = mprintf("%s", x.zRepoName); zSkinUrl = mprintf("%s", zUrl); } } fossil_free(zFull); if( !x.isValid #if USE_SEE && !bEncrypted #endif ){ continue; } if( x.isRepolistSkin==2 && !allRepo ){ /* Repositories with repolist-skin==2 are omitted from directory ** scan lists, but included in "fossil all ui" lists */ continue; } if( rNow <= x.rMTime ){ x.rMTime = rNow; }else if( x.rMTime<0.0 ){ x.rMTime = rNow; } iAge = (sqlite3_int64)((rNow - x.rMTime)*86400); zAge = human_readable_age(rNow - x.rMTime); if( x.rMTime==0.0 ){ /* This repository has no entry in the "event" table. ** Its age will still be maximum, so data-sortkey will work. */ zAge = mprintf("unknown"); } blob_append_sql(&html, "<tr><td valign='top'>"); if( !file_ends_with_repository_extension(zName,0) ){ /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands ** do not work for repositories whose names do not end in ".fossil". ** So do not hyperlink those cases. */ blob_append_sql(&html,"%h",zName); } else if( sqlite3_strglob("*/.*", zName)==0 ){ /* Do not show hyperlinks for hidden repos */ blob_append_sql(&html, "%h (hidden)", zName); } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){ blob_append_sql(&html, "<a href='%R/%T/home' target='_blank'>/%h</a>\n", zUrl, zName); }else if( file_ends_with_repository_extension(zName,1) ){ /* As described in ** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if ** foo.fossil and foo/bar.fossil both exist and we create a ** link to foo/bar/... then the URI dispatcher will instead ** see that as a link to foo.fossil. In such cases, do not ** emit a link to foo/bar.fossil. */ char * zDirPart = file_dirname(zName); if( db_exists("SELECT 1 FROM sfile " "WHERE pathname=(%Q || '.fossil') COLLATE nocase" #if USE_SEE " OR pathname=(%Q || '.efossil') COLLATE nocase" #endif , zDirPart #if USE_SEE , zDirPart #endif ) ){ blob_append_sql(&html, "<s>%h</s> (directory/repo name collision)\n", zName); }else{ blob_append_sql(&html, "<a href='%R/%T/home' target='_blank'>%h</a>\n", zUrl, zName); } fossil_free(zDirPart); }else{ blob_append_sql(&html, "<a href='%R/%T/home' target='_blank'>%h</a>\n", zUrl, zName); } if( x.zProjName ){ blob_append_sql(&html, "<td></td><td>%h</td>\n", x.zProjName); fossil_free(x.zProjName); }else{ blob_append_sql(&html, "<td></td><td></td>\n"); } blob_append_sql(&html, "<td></td><td data-sortkey='%08x'>%h</td>\n", (int)iAge, zAge); fossil_free(zAge); if( x.zLoginGroup ){ blob_append_sql(&html, "<td></td><td>%h</td></tr>\n", x.zLoginGroup); fossil_free(x.zLoginGroup); }else{ blob_append_sql(&html, "<td></td><td></td></tr>\n"); } sqlite3_free(zUrl); } db_finalize(&q); blob_append_sql(&html,"</tbody></table>\n"); } if( zSkinRepo ){ char *zNewBase = mprintf("%s/%s", g.zBaseURL, zSkinUrl); g.zBaseURL = 0; set_base_url(zNewBase); db_open_repository(zSkinRepo); fossil_free(zSkinRepo); fossil_free(zSkinUrl); } if( g.repositoryOpen ){ /* This case runs if remote_repo_info() found a repository ** that has the "repolist_skin" property set to non-zero and left ** that repository open in g.db. Use the skin of that repository ** for display. */ login_check_credentials(); style_set_current_feature("repolist"); style_header("Repository List"); @ %s(blob_str(&html)) style_table_sorter(); style_finish_page(); }else{ /* If no repositories were found that had the "repolist_skin" ** property set, then use a default skin */ @ <html> @ <head> @ <base href="%s(g.zBaseURL)/"> @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> @ <title>Repository List</title> @ </head> @ <body> @ <h1 align="center">Fossil Repositories</h1> @ %s(blob_str(&html)) @ <script>%s(builtin_text("sorttable.js"))</script> @ </body> @ </html> } blob_reset(&html); cgi_reply(); return n; } /* ** COMMAND: test-list-page ** ** Usage: %fossil test-list-page DIRECTORY ** ** Show all repositories underneath DIRECTORY. Or if DIRECTORY is "/" ** show all repositories in the ~/.fossil file. */ void test_list_page(void){ if( g.argc<3 ){ g.zRepositoryName = "/"; }else{ g.zRepositoryName = g.argv[2]; } g.httpOut = stdout; repo_list_page(); } |
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 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 | ** 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 /* Settings that can be used to control ticket reports */ /* ** SETTING: ticket-default-report width=80 ** If this setting has a string value, then when the ticket ** search page query is blank, the report with this title is shown. ** If the setting is blank (default), then no report is shown. */ /* ** WEBPAGE: reportlist ** ** Main menu for Tickets. */ void view_list(void){ const char *zScript; Blob ril; /* Report Item List */ Stmt q; int rn = 0; char *defaultReport = db_get("ticket-default-report", 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); if( zTitle[0] =='_' && !g.perm.TktFmt ){ continue; } rn = db_column_int(&q, 0); blob_appendf(&ril, "<li>"); if( zTitle[0] == '_' ){ blob_appendf(&ril, "%s", zTitle); } else { blob_appendf(&ril, "%z%h</a>", href("%R/rptview/%d", rn), zTitle); } blob_appendf(&ril, " "); 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/%d?copy=1", rn)); } if( g.perm.Admin || (g.perm.WrTkt && zOwner && fossil_strcmp(g.zLogin,zOwner)==0) ){ blob_appendf(&ril, "[%zedit</a>]", href("%R/rptedit/%d", rn)); } if( g.perm.TktFmt ){ blob_appendf(&ril, "[%zsql</a>]", href("%R/rptsql/%d", rn)); } if( fossil_strcmp(zTitle, defaultReport)==0 ){ blob_appendf(&ril, " ↠default"); } 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_finish_page(); } /* ** Remove whitespace from both ends of a string. */ char *trim_string(const char *zOrig){ int i; while( fossil_isspace(*zOrig) ){ zOrig++; } i = strlen(zOrig); while( i>0 && fossil_isspace(zOrig[i-1]) ){ i--; } return mprintf("%.*s", i, zOrig); } /* ** Extract a numeric (integer) value from a string. */ char *extract_integer(const char *zOrig){ if( zOrig == NULL || zOrig[0] == 0 ) return ""; while( *zOrig && !fossil_isdigit(*zOrig) ){ zOrig++; } if( *zOrig ){ /* we have a digit. atoi() will get as much of the number as it ** can. We'll run it through mprintf() to get a string. Not ** an efficient way to do it, but effective. */ return mprintf("%d", atoi(zOrig)); } return ""; } /* ** Remove blank lines from the beginning of a string and ** all whitespace from the end. Removes whitespace preceding a LF, ** which also converts any CRLF sequence into a single LF. */ char *remove_blank_lines(const char *zOrig){ int i, j, n; char *z; for(i=j=0; fossil_isspace(zOrig[i]); i++){ if( zOrig[i]=='\n' ) j = i+1; } n = strlen(&zOrig[j]); while( n>0 && fossil_isspace(zOrig[j+n-1]) ){ n--; } z = mprintf("%.*s", n, &zOrig[j]); for(i=j=0; z[i]; i++){ if( z[i+1]=='\n' && z[i]!='\n' && fossil_isspace(z[i]) ){ z[j] = z[i]; while(fossil_isspace(z[j]) && z[j] != '\n' ){ j--; } j++; continue; } z[j++] = z[i]; } z[j] = 0; return z; } /*********************************************************************/ /* ** This is the SQLite authorizer callback used to make sure that the ** SQL statements entered by users do not try to do anything untoward. ** If anything suspicious is tried, set *(char**)pError to an error ** message obtained from malloc. ** ** Use the "fossil test-db-prepare --auth-report SQL" command to perform ** manual testing of this authorizer. */ static int report_query_authorizer( void *pError, int code, const char *zArg1, const char *zArg2, const char *zArg3, const char *zArg4 ){ int rc = SQLITE_OK; if( *(char**)pError ){ /* We've already seen an error. No need to continue. */ return SQLITE_DENY; } switch( code ){ case SQLITE_SELECT: case SQLITE_RECURSIVE: case SQLITE_FUNCTION: { break; } case SQLITE_READ: { static const char *const azAllowed[] = { "backlink", "blob", "event", "filename", "json_each", "json_tree", "mlink", "plink", "tag", "tagxref", "ticket", "ticketchng", "unversioned", }; int lwr = 0; int upr = count(azAllowed) - 1; int cmp = 0; if( zArg1==0 ){ /* Some legacy versions of SQLite will sometimes send spurious ** READ authorizations that have no table name. These can be ** ignored. */ rc = SQLITE_IGNORE; break; } while( lwr<=upr ){ int i = (lwr+upr)/2; cmp = fossil_stricmp(zArg1, azAllowed[i]); if( cmp<0 ){ upr = i - 1; }else if( cmp>0 ){ lwr = i + 1; }else{ break; } } if( cmp ){ /* Always ok to access tables whose names begin with "fx_" */ cmp = sqlite3_strnicmp(zArg1, "fx_", 3); } if( cmp ){ *(char**)pError = mprintf("access to table \"%s\" is restricted",zArg1); rc = SQLITE_DENY; }else if( !g.perm.RdAddr && sqlite3_strnicmp(zArg2, "private_", 8)==0 ){ rc = SQLITE_IGNORE; } break; } default: { *(char**)pError = mprintf("only SELECT statements are allowed"); rc = SQLITE_DENY; break; } } return rc; } /* ** Make sure the reportfmt table is up-to-date. It should contain ** the "jx" column (as of version 2.21). If it does not, add it. ** ** The "jx" column is intended to hold a JSON object containing optional ** key-value pairs. */ void report_update_reportfmt_table(void){ if( db_table_has_column("repository","reportfmt","jx")==0 ){ db_multi_exec("ALTER TABLE repository.reportfmt" " ADD COLUMN jx TEXT DEFAULT '{}';"); } } /* ** Activate the ticket report query authorizer. Must be followed by an ** eventual call to report_unrestrict_sql(). */ void report_restrict_sql(char **pzErr){ db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report"); sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000); } void report_unrestrict_sql(void){ db_clear_authorizer(); } /* ** Check the given SQL to see if is a valid query that does not ** attempt to do anything dangerous. Return 0 on success and a ** pointer to an error message string (obtained from malloc) if ** there is a problem. */ char *verify_sql_statement(char *zSql){ 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 ){ sqlite3_finalize(pStmt); } report_unrestrict_sql(); return zErr; } /* ** Get a report number from query parameters. This can be done in various ** ways: ** ** (1) (legacy) rn=NNN where NNN is the reportfmt.rn integer primary key. ** ** (2) name=NNN where NNN is the rn. ** ** (3) name=TAG where TAG matches reportfmt.jx->>tag ** ** Regardless of how the report is specified, return the primary key, rn. ** Return 0 if not found. */ static int report_number(void){ int rn; const char *zName; char *zEnd; /* Case (1) */ rn = atoi(PD("rn","0")); if( rn>0 ) return rn; zName = P("name"); if( zName==0 || zName[0]==0 ) return 0; if( fossil_isdigit(zName[0]) && (rn = strtol(zName, &zEnd, 10))>0 && zEnd[0]==0 ){ /* Case 2 */ return rn; } rn = db_int(0, "SELECT rn FROM reportfmt WHERE jx->>'tag'==%Q", zName); return rn; } /* ** WEBPAGE: rptsql ** URL: /rptsql/N ** ** Display the SQL query used to generate a ticket report. The N value ** is either the report number of a report tag. */ void view_see_sql(void){ int rn; const char *zTitle; 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 = report_number(); db_prepare(&q, "SELECT title, sqlcode, owner, cols " "FROM reportfmt WHERE rn=%d",rn); style_set_current_feature("report"); style_header("SQL For Report Format Number %d", rn); if( db_step(&q)!=SQLITE_ROW ){ @ <p>Unknown report number: %d(rn)</p> style_finish_page(); 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> @ <tr><td valign="top" align="right">Title:</td><td width=15></td> @ <td colspan="3">%h(zTitle)</td></tr> @ <tr><td valign="top" align="right">Owner:</td><td></td> @ <td colspan="3">%h(zOwner)</td></tr> @ <tr><td valign="top" align="right">SQL:</td><td></td> @ <td valign="top"><pre> @ <code class="language-sql">%h(zSQL)</code> @ </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_finish_page(); db_finalize(&q); } /* ** WEBPAGE: rptnew ** WEBPAGE: rptedit ** ** Create (/rptnew) or edit (/rptedit) a ticket report format. ** Query parameters: ** ** name=N Ticket report number or tag. ** rn=N Ticket report number (legacy). ** ^^^-- one of the two previous is required. ** t=TITLE Title of the report format ** w=USER Owner of the report format ** s=SQL SQL text used to implement the report ** k=KEY Color key ** d=DESC Optional descriptive text ** m=MIMETYPE Mimetype for DESC ** x=TAG Symbolic name for the report */ void view_edit(void){ int rn; const char *zTitle; /* Title of the report */ const char *z; const char *zOwner; /* Owner of the report */ const char *zClrKey; /* Color key - used to add colors to lines */ char *zSQL; /* The SQL text that gnerates the report */ char *zErr = 0; /* An error message */ const char *zDesc; /* Extra descriptive text about the report */ const char *zMimetype; /* Mimetype for zDesc */ const char *zTag; /* Symbolic name for this report */ int dflt = P("dflt") ? 1 : 0; login_check_credentials(); if( !g.perm.TktFmt ){ login_needed(g.anon.TktFmt); return; } style_set_current_feature("report"); /*view_add_functions(0);*/ rn = report_number(); zTitle = P("t"); zOwner = PD("w",g.zLogin); z = P("s"); zSQL = z ? trim_string(z) : 0; zClrKey = trim_string(PD("k","")); zDesc = trim_string(PD("d","")); zMimetype = P("m"); zTag = P("x"); report_update_reportfmt_table(); if( rn>0 && P("del2") && cgi_csrf_safe(2) ){ db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn); cgi_redirect("reportlist"); return; }else if( rn>0 && P("del1") && cgi_csrf_safe(2) ){ zTitle = db_text(0, "SELECT title FROM reportfmt " "WHERE rn=%d", rn); if( zTitle==0 ) cgi_redirect("reportlist"); style_header("Are You Sure?"); @ <form action="rptedit" method="post"> @ <p>You are about to delete all traces of the report @ <strong>%h(zTitle)</strong> from @ the database. This is an irreversible operation. All records @ related to this report will be removed and cannot be recovered.</p> @ @ <input type="hidden" name="rn" value="%d(rn)"> login_insert_csrf_secret(); @ <input type="submit" name="del2" value="Delete The Report"> @ <input type="submit" name="can" value="Cancel"> @ </form> style_finish_page(); return; }else if( P("can") ){ /* user cancelled */ 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) ){ zErr = mprintf("There is already another report named \"%h\"", zTitle); } if( zErr==0 && cgi_csrf_safe(2) ){ if( zTag && zTag[0]==0 ) zTag = 0; if( zDesc && zDesc[0]==0 ){ zDesc = 0; zMimetype = 0; } if( zMimetype && zMimetype[0]==0 ){ zDesc = 0; zMimetype = 0; } if( rn>0 ){ db_multi_exec( "UPDATE reportfmt SET title=%Q, sqlcode=%Q," " owner=%Q, cols=%Q, mtime=now(), " " jx=json_patch(jx,json_object('desc',%Q,'descmt',%Q,'tag',%Q))" " WHERE rn=%d", zTitle, zSQL, zOwner, zClrKey, zDesc, zMimetype, zTag, rn); }else{ db_multi_exec( "INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime,jx) " "VALUES(%Q,%Q,%Q,%Q,now()," "json_object('desc',%Q,'descmt',%Q,'tag',%Q))", zTitle, zSQL, zOwner, zClrKey, zDesc, zMimetype, zTag); rn = db_last_insert_rowid(); } if( dflt ){ db_set("ticket-default-report", zTitle, 0); }else{ char *defaultReport = db_get("ticket-default-report", 0); if( fossil_strcmp(zTitle, defaultReport)==0 ){ db_set("ticket-default-report", "", 0); } } cgi_redirect(mprintf("rptview/%d", rn)); return; } }else if( rn==0 ){ zTitle = ""; zSQL = ticket_report_template(); zClrKey = ticket_key_template(); }else{ Stmt q; int hasJx = 0; zDesc = 0; zMimetype = 0; zTag = 0; db_prepare(&q, "SELECT title, sqlcode, owner, cols, json_valid(jx) " "FROM reportfmt WHERE rn=%d",rn); if( db_step(&q)==SQLITE_ROW ){ char *defaultReport = db_get("ticket-default-report", 0); zTitle = db_column_malloc(&q, 0); zSQL = db_column_malloc(&q, 1); zOwner = db_column_malloc(&q, 2); zClrKey = db_column_malloc(&q, 3); dflt = fossil_strcmp(zTitle, defaultReport)==0; hasJx = db_column_int(&q, 4); } db_finalize(&q); if( hasJx ){ db_prepare(&q, "SELECT jx->>'desc', jx->>'descmt', jx->>'tag'" " FROM reportfmt WHERE rn=%d", rn); if( db_step(&q)==SQLITE_ROW ){ zDesc = db_column_malloc(&q, 0); zMimetype = db_column_malloc(&q, 1); zTag = db_column_malloc(&q, 2); } db_finalize(&q); } if( P("copy") ){ rn = 0; zTitle = mprintf("Copy Of %s", zTitle); zOwner = g.zLogin; } } if( zOwner==0 ) zOwner = g.zLogin; style_submenu_element("Cancel", "%R/reportlist"); if( rn>0 ){ style_submenu_element("Delete", "%R/rptedit/%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> @ <p>Enter a complete SQL query statement against the "TICKET" table:<br> @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea> @ </p> login_insert_csrf_secret(); if( g.perm.Admin ){ @ <p>Report owner: @ <input type="text" name="w" size="20" value="%h(zOwner)"> @ </p> @ <p>Tag: @ <input type="text" name="x" size="20" value="%h(zTag?zTag:"")"> @ </p> } else { @ <input type="hidden" name="w" value="%h(zOwner)"> if( zTag && zTag[0] ){ @ <input type="hidden" name="x" value="%h(zTag)"> } } @ <p>Enter an optional color key in the following box. (If blank, no @ color key is displayed.) Each line contains the text for a single @ entry in the key. The first token of each line is the background @ color for that line.<br> @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea> @ </p> @ <p>Optional human-readable description for this report<br> @ %z(href("%R/markup_help"))Markup style</a>: mimetype_option_menu(zMimetype, "m"); @ <br><textarea aria-label="Description:" name="d" class="wikiedit" \ @ cols="80" rows="15" wrap="virtual">%h(zDesc)</textarea> @ </p> @ <p><label><input type="checkbox" name="dflt" %s(dflt?"checked":"")> \ @ Make this the default report</label></p> if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){ @ <p>This report format is owned by %h(zOwner). You are not allowed @ to change it.</p> @ </form> report_format_hints(); style_finish_page(); return; } @ <input type="submit" value="Apply Changes"> if( rn>0 ){ @ <input type="submit" value="Delete This Report" name="del1"> } @ </div></form> report_format_hints(); style_finish_page(); } /* ** Output a bunch of text that provides information about report ** formats */ static void report_format_hints(void){ char *zSchema; zSchema = db_text(0,"SELECT sql FROM sqlite_schema WHERE name='ticket'"); if( zSchema==0 ){ zSchema = db_text(0,"SELECT sql FROM repository.sqlite_schema" " WHERE name='ticket'"); } @ <hr><h3>TICKET Schema</h3> @ <blockquote><pre> @ <code class="language-sql">%h(zSchema)</code> @ </pre></blockquote> @ <h3>Notes</h3> @ <ul> @ <li><p>The SQL must consist of a single SELECT statement</p></li> @ @ <li><p>If a column of the result set is named "#" then that column @ is assumed to hold a ticket number. A hyperlink will be created from @ that column to a detailed view of the ticket.</p></li> @ @ <li><p>If a column of the result set is named "bgcolor" then the content @ of that column determines the background color of the row.</p></li> @ @ <li><p>The text of all columns prior to the first column whose name begins @ with underscore ("_") is shown character-for-character as it appears in @ the database. In other words, it is assumed to have a mimetype of @ text/plain. @ @ <li><p>The first column whose name begins with underscore ("_") and all @ subsequent columns are shown on their own rows in the table and with @ wiki formatting. In other words, such rows are shown with a mimetype @ of text/x-fossil-wiki. This is recommended for the "description" field @ of tickets. @ </p></li> @ @ <li><p>The query can join other tables in the database besides TICKET. @ </p></li> @ </ul> @ @ <h3>Examples</h3> @ <p>In this example, the first column in the result set is named @ "bgcolor". The value of this column is not displayed. Instead, it @ selects the background color of each row based on the TICKET.STATUS @ field of the database. The color key at the right shows the various @ color codes.</p> @ <table class="rpteditex"> @ <tr style="background-color:#f2dcdc;"><td class="rpteditex">new or active</td></tr> @ <tr style="background-color:#e8e8bd;"><td class="rpteditex">review</td></tr> @ <tr style="background-color:#cfe8bd;"><td class="rpteditex">fixed</td></tr> @ <tr style="background-color:#bde5d6;"><td class="rpteditex">tested</td></tr> @ <tr style="background-color:#cacae5;"><td class="rpteditex">defer</td></tr> @ <tr style="background-color:#c8c8c8;"><td class="rpteditex">closed</td></tr> @ </table> @ <blockquote><pre> @ SELECT @ CASE WHEN status IN ('new','active') THEN '#f2dcdc' @ WHEN status='review' THEN '#e8e8bd' @ WHEN status='fixed' THEN '#cfe8bd' @ WHEN status='tested' THEN '#bde5d6' |
︙ | ︙ | |||
508 509 510 511 512 513 514 | @ priority AS 'Pri', @ title AS 'Title' @ FROM ticket @ </pre></blockquote> @ <p>To base the background color on the TICKET.PRIORITY or @ TICKET.SEVERITY fields, substitute the following code for the @ first column of the query:</p> | | | | | | | | 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 | @ priority AS 'Pri', @ title AS 'Title' @ FROM ticket @ </pre></blockquote> @ <p>To base the background color on the TICKET.PRIORITY or @ TICKET.SEVERITY fields, substitute the following code for the @ first column of the query:</p> @ <table class="rpteditex"> @ <tr style="background-color:#f2dcdc;"><td class="rpteditex">1</td></tr> @ <tr style="background-color:#e8e8bd;"><td class="rpteditex">2</td></tr> @ <tr style="background-color:#cfe8bd;"><td class="rpteditex">3</td></tr> @ <tr style="background-color:#cacae5;"><td class="rpteditex">4</td></tr> @ <tr style="background-color:#c8c8c8;"><td class="rpteditex">5</td></tr> @ </table> @ <blockquote><pre> @ SELECT @ CASE priority WHEN 1 THEN '#f2dcdc' @ WHEN 2 THEN '#e8e8bd' @ WHEN 3 THEN '#cfe8bd' @ WHEN 4 THEN '#cacae5' |
︙ | ︙ | |||
561 562 563 564 565 566 567 | @ owner AS 'By', @ subsystem AS 'Subsys', @ sdate(changetime) AS 'Changed', @ assignedto AS 'Assigned', @ severity AS 'Svr', @ priority AS 'Pri', @ title AS 'Title', | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > | | | < | < < < < < | | | > > > > > > > > > > > > | | | | | | < | | | > | > | > < < < | < | | | | | | | 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 | @ owner AS 'By', @ subsystem AS 'Subsys', @ sdate(changetime) AS 'Changed', @ assignedto AS 'Assigned', @ severity AS 'Svr', @ 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 */ int nCount; /* Row number */ int nCol; /* Number of columns */ int isMultirow; /* True if multiple table rows per query result row */ int iNewRow; /* Index of first column that goes on separate row */ int iBg; /* Index of column that defines background color */ int wikiFlags; /* Flags passed into wiki_convert() */ const char *zWikiStart; /* HTML before display of multi-line wiki */ const char *zWikiEnd; /* HTML after display of multi-line wiki */ }; /* ** 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 hash. (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. */ db_clear_authorizer(); /* Figure out the number of columns, the column that determines background ** color, and whether or not this row of data is represented by multiple ** rows in the table. */ pState->nCol = 0; pState->isMultirow = 0; pState->iNewRow = -1; pState->iBg = -1; for(i=0; i<nArg; i++){ if( azName[i][0]=='b' && fossil_strcmp(azName[i],"bgcolor")==0 ){ pState->iBg = i; continue; } if( g.perm.Write && azName[i][0]=='#' ){ pState->nCol++; } if( !pState->isMultirow ){ if( azName[i][0]=='_' ){ pState->isMultirow = 1; pState->iNewRow = i; pState->wikiFlags = WIKI_NOBADLINKS; pState->zWikiStart = ""; pState->zWikiEnd = ""; if( P("plaintext") ){ pState->wikiFlags |= WIKI_LINKSONLY; pState->zWikiStart = "<pre class='verbatim'>"; pState->zWikiEnd = "</pre>"; style_submenu_element("Formatted", "%R/rptview/%d", pState->rn); }else{ style_submenu_element("Plaintext", "%R/rptview/%d?plaintext", pState->rn); } }else{ pState->nCol++; } } } /* 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++; @ </tr><tr><th colspan=%d(pState->nCol)>%h(zName)</th> }else{ if( zName[0]=='#' ){ zTid = zName; } @ <th>%h(zName)</th> } } if( g.perm.Write && zTid ){ @ <th> </th> } @ </tr></thead><tbody> } if( azArg==0 ){ @ <tr><td colspan="%d(pState->nCol)"> @ <i>No records match the report criteria</i> @ </td></tr> return 0; } ++pState->nCount; /* Output the separator above each entry in a table which has multiple lines ** per database entry. */ if( pState->iNewRow>=0 ){ @ <tr><td colspan=%d(pState->nCol)><font size=1> </font></td></tr> } /* 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; } if( zData[0] ){ Blob content; @ </tr> @ <tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)> @ %s(pState->zWikiStart) blob_init(&content, zData, -1); wiki_convert(&content, 0, pState->wikiFlags); blob_reset(&content); @ %s(pState->zWikiEnd) } }else if( azName[i][0]=='#' ){ zTid = zData; @ <td valign="top">%z(href("%R/tktview/%h",zData))%h(zData)</a></td> }else if( zData[0]==0 ){ @ <td valign="top"> </td> }else{ @ <td valign="top"> @ %h(zData) @ </td> } } if( zTid && g.perm.Write ){ @ <td valign="top">%z(href("%R/tktedit/%h",zTid))edit</a></td> } @ </tr> return 0; } /* ** Output the text given in the argument. Convert tabs and newlines into ** spaces. */ static void output_no_tabs(const char *z){ while( z && z[0] ){ int i, j; for(i=0; z[i] && (!fossil_isspace(z[i]) || z[i]==' '); i++){} if( i>0 ){ cgi_printf("%.*s", i, z); } for(j=i; fossil_isspace(z[j]); j++){} if( j>i ){ cgi_printf("%*s", j-i, ""); } z += j; } } /* ** 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]); |
︙ | ︙ | |||
813 814 815 816 817 818 819 | } /* ** Generate HTML that describes a color key. */ void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){ int i, j, k; | | > | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > | < < | | < > > > > > | > > > | | > > > > | > > | > > > > | > > > > > > | > > | > > > > > > > > > > | | | | | | | | | < | | > > > > > > > > | | | > | | | | | | > > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } /* ** 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", i, zSafeKey, k-j, &zSafeKey[j]); }else{ cgi_printf("<td style=\"background-color: %.*s;\">%.*s</td>\n", i, zSafeKey, k-j, &zSafeKey[j]); } zSafeKey += k; } free(zToFree); if( horiz ){ @ </tr> } @ </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 */ int nVar; /* Number of parameters */ pStmt = 0; rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); assert( rc==SQLITE_OK || pStmt==0 ); if( rc!=SQLITE_OK ){ return rc; } if( !pStmt ){ /* this happens for a comment or white-space */ return SQLITE_OK; } if( !sqlite3_stmt_readonly(pStmt) ){ sqlite3_finalize(pStmt); return SQLITE_ERROR; } nVar = sqlite3_bind_parameter_count(pStmt); for(i=1; i<=nVar; i++){ const char *zVar = sqlite3_bind_parameter_name(pStmt, i); if( zVar==0 ) continue; if( zVar[0]!='$' && zVar[0]!='@' && zVar[0]!=':' ) continue; if( !fossil_islower(zVar[1]) ) continue; if( strcmp(zVar, "$login")==0 ){ sqlite3_bind_text(pStmt, i, g.zLogin, -1, SQLITE_TRANSIENT); }else{ sqlite3_bind_text(pStmt, i, P(zVar+1), -1, SQLITE_TRANSIENT); } } 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; } /* ** WEBPAGE: rptview ** ** Generate a report. The rn query parameter is the report number ** corresponding to REPORTFMT.RN. If the tablist query parameter exists, ** then the output consists of lines of tab-separated fields instead of ** an HTML table. */ void rptview_page(void){ rptview_page_content(0, 1, 1); } /* ** Render a report. */ void rptview_page_content( const char *defaultTitleSearch, /* If rn and title query parameters are blank, search reports by this title. */ int pageWrap, /* If true, render full page; otherwise, just the report */ int redirectMissing /* If true and report not found, go to reportlist */ ){ int count = 0; int rn, rc; char *zSql; char *zTitle; char *zOwner; char *zClrKey; char *zDesc; char *zMimetype; int tabs; Stmt q; char *zErr1 = 0; char *zErr2 = 0; login_check_credentials(); if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } report_update_reportfmt_table(); rn = report_number(); tabs = P("tablist")!=0; db_prepare(&q, "SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'" " FROM reportfmt WHERE rn=%d", rn); rc = db_step(&q); if( rc!=SQLITE_ROW ){ const char *titleSearch = defaultTitleSearch==0 || trim_string(defaultTitleSearch)[0]==0 ? P("title") : defaultTitleSearch; db_finalize(&q); db_prepare(&q, "SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'" " FROM reportfmt WHERE title GLOB %Q", titleSearch); rc = db_step(&q); } if( rc!=SQLITE_ROW ){ db_finalize(&q); if( redirectMissing ) { cgi_redirect("reportlist"); } 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); rn = db_column_int(&q,4); zDesc = db_column_malloc(&q, 5); zMimetype = db_column_malloc(&q, 6); db_finalize(&q); if( P("order_by") ){ /* ** If the user wants to do a column sort, wrap the query into a sub ** query and then sort the results. This is a whole lot easier than ** trying to insert an ORDER BY into the query itself, especially ** if the query is already ordered. */ int nField = atoi(P("order_by")); if( nField > 0 ){ const char* zDir = PD("order_dir",""); zDir = !strcmp("ASC",zDir) ? "ASC" : "DESC"; zSql = mprintf("SELECT * FROM (%s) ORDER BY %d %s", zSql, nField, zDir); } } count = 0; if( !tabs ){ struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; const char *zQS = PD("QUERY_STRING",""); db_multi_exec("PRAGMA empty_result_callbacks=ON"); style_set_current_feature("report"); if( pageWrap ) { /* style_finish_page() should provide escaping via %h formatting */ if( zQS[0] ){ if( g.zExtra && g.zExtra[0] ){ style_submenu_element("Raw","%R/%s/%s?tablist=1&%s", g.zPath, g.zExtra, zQS); }else{ style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS); } style_submenu_element("Reports","%R/reportlist?%s",zQS); } else { if( g.zExtra && g.zExtra[0] ){ style_submenu_element("Raw","%R/%s/%s?tablist=1",g.zPath,g.zExtra); }else{ style_submenu_element("Raw","%R/%s?tablist=1",g.zPath); } style_submenu_element("Reports","%R/reportlist"); } if( g.perm.Admin || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){ style_submenu_element("Edit", "%R/rptedit/%d", rn); } if( g.perm.TktFmt ){ style_submenu_element("SQL", "%R/rptsql/%d",rn); } if( g.perm.NewTkt ){ style_submenu_element("New Ticket", "%R/tktnew"); } style_header("%s", zTitle); } if( zDesc && zDesc[0] && zMimetype ){ Blob src; blob_init(&src, zDesc, -1); wiki_render_by_mimetype(&src, zMimetype); blob_reset(&src); @ <br> } output_color_key(zClrKey, 1, "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\""); @ <table border="1" cellpadding="2" cellspacing="0" class="report sortable" @ data-column-types='' data-init-sort='0'> 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> } style_table_sorter(); if( pageWrap ) { style_finish_page(); } }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 */ static const char zFullTicketRptRn[] = "0"; /* ** report title for full table ticket export */ static const char zFullTicketRptTitle[] = "full ticket export"; /* ** 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 */ static const char *zSep = 0; /* ** select the quoting algorithm for "ticket show" */ #if INTERFACE typedef enum eTktShowEnc { tktNoTab=0, tktFossilize=1 } tTktShowEncoding; #endif static tTktShowEncoding tktEncode = tktNoTab; /* ** Output the text given in the argument. Convert tabs and newlines into ** spaces. */ static void output_no_tabs_file(const char *z){ switch( tktEncode ){ case tktFossilize: { char *zFosZ; if( z && *z ){ zFosZ = fossilize(z,-1); fossil_print("%s",zFosZ); free(zFosZ); } break; } default: while( z && z[0] ){ int i, j; for(i=0; z[i] && (!fossil_isspace(z[i]) || z[i]==' '); i++){} if( i>0 ){ fossil_print("%.*s", i, z); } 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]); fossil_print("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n"); } } ++*pCount; for(i=0; i<nArg; i++){ output_no_tabs_file(azArg[i]); fossil_print("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n"); } return 0; } /* ** 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{ db_prepare(&q, "SELECT sqlcode FROM reportfmt WHERE title=%Q", zRep); } if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); rpt_list_reports(); fossil_fatal("unknown report format(%s)!",zRep); } zSql = db_column_malloc(&q, 0); db_finalize(&q); } 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.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to create a RSS feed for the CGI interface. */ #include "config.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 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 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to create a RSS feed for the CGI interface. */ #include "config.h" #include <time.h> #include "rss.h" #include <assert.h> /* ** WEBPAGE: timeline.rss ** URL: /timeline.rss?y=TYPE&n=LIMIT&tkt=HASH&tag=TAG&wiki=NAME&name=FILENAME ** ** Produce an RSS feed of the timeline. ** ** TYPE may be: all, ci (show check-ins only), t (show ticket changes only), ** w (show wiki only), e (show tech notes only), f (show forum posts only), ** g (show tag/branch changes only). ** ** LIMIT is the number of items to show. ** ** tkt=HASH 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). */ void page_timeline_rss(void){ Stmt q; int nLine=0; char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0; Blob bSQL; const char *zType = PD("y","all"); /* Type of events. All if NULL */ const char *zTicketUuid = PD("tkt",NULL); const char *zTag = PD("tag",NULL); const char *zFilename = PD("name",NULL); const char *zWiki = PD("wiki",NULL); int nLimit = atoi(PD("n","20")); int nTagId; const char zSQL1[] = @ SELECT @ blob.rid, @ uuid, @ event.mtime, @ event.type, @ coalesce(ecomment,comment), @ coalesce(euser,user), @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim), @ (SELECT count(*) FROM plink WHERE cid=blob.rid), @ (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 @ 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_sql( &bSQL, "%s", zSQL1/*safe-for-%s*/ ); 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"; if( zType[0]=='f' && !g.perm.RdForum ) zType = "x"; blob_append_sql(&bSQL, " AND event.type=%Q", zType); }else{ blob_append_sql(&bSQL, " AND event.type in ("); if( g.perm.Read ){ blob_append_sql(&bSQL, "'ci',"); } if( g.perm.RdTkt ){ blob_append_sql(&bSQL, "'t',"); } if( g.perm.RdWiki ){ blob_append_sql(&bSQL, "'w',"); } if( g.perm.RdForum ){ blob_append_sql(&bSQL, "'f',"); } blob_append_sql(&bSQL, "'x')"); } 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_sql( &bSQL, " ORDER BY event.mtime DESC" ); cgi_set_content_type("application/rss+xml"); zProjectName = db_get("project-name", 0); if( zProjectName==0 ){ zFreeProjectName = zProjectName = mprintf("Fossil source repository for: %s", g.zBaseURL); } zProjectDescr = db_get("project-description", 0); if( zProjectDescr==0 ){ zProjectDescr = zProjectName; } zPubDate = cgi_rfc822_datestamp(time(NULL)); @ <?xml version="1.0"?> @ <rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> @ <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 *zEType = db_column_text(&q, 3); const char *zCom = db_column_text(&q, 4); const char *zAuthor = db_column_text(&q, 5); char *zPrefix = ""; char *zSuffix = 0; char *zDate; int nChild = db_column_int(&q, 5); int nParent = db_column_int(&q, 7); const char *zTagList = db_column_text(&q, 8); time_t ts; if( zTagList && zTagList[0]==0 ) zTagList = 0; ts = (time_t)((db_column_double(&q,2) - 2440587.5)*86400.0); zDate = cgi_rfc822_datestamp(ts); if('c'==zEType[0]){ if( nParent>1 && nChild>1 ){ zPrefix = "*MERGE/FORK* "; }else if( nParent>1 ){ zPrefix = "*MERGE* "; }else if( nChild>1 ){ zPrefix = "*FORK* "; } }else if('w'==zEType[0]){ switch(zCom ? zCom[0] : 0){ case ':': zPrefix = "Edit wiki page: "; break; case '+': zPrefix = "Add wiki page: "; break; case '-': zPrefix = "Delete wiki page: "; break; } if(*zPrefix) ++zCom; } if( zTagList ){ zSuffix = mprintf(" (tags: %s)", zTagList); } @ <item> @ <title>%s(zPrefix)%h(zCom)%h(zSuffix)</title> @ <link>%s(g.zBaseURL)/info/%s(zId)</link> @ <description>%s(zPrefix)%h(zCom)%h(zSuffix)</description> @ <pubDate>%s(zDate)</pubDate> @ <dc:creator>%h(zAuthor)</dc:creator> @ <guid>%s(g.zBaseURL)/info/%s(zId)</guid> @ </item> free(zDate); free(zSuffix); 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 HASH Filter for only those events for the specified ticket ** ** -tag TAG Filter for a tag ** ** -wiki NAME Filter on a specific wiki page ** ** Only one of -tkt, -tag, or -wiki may be used. ** ** -name FILENAME Filter for a specific file. This may be combined ** with one of the other filters (useful for looking ** at a specific branch). ** ** -url STRING Set 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), @ (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 @ 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 *zSuffix = 0; char *zDate; int nChild = db_column_int(&q, 5); int nParent = db_column_int(&q, 6); const char *zTagList = db_column_text(&q, 7); time_t ts; if( zTagList && zTagList[0]==0 ) zTagList = 0; 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* "; } if( zTagList ){ zSuffix = mprintf(" (tags: %s)", zTagList); } fossil_print("<item>"); fossil_print("<title>%s%h%h</title>\n", zPrefix, zCom, zSuffix); fossil_print("<link>%s/info/%s</link>\n", zBaseURL, zId); fossil_print("<description>%s%h%h</description>\n", zPrefix, zCom, zSuffix); 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); free(zSuffix); nLine++; } db_finalize(&q); fossil_print("</channel>\n"); fossil_print("</rss>\n"); if( zFreeProjectName != 0 ){ free( zFreeProjectName ); } } |
Changes to src/schema.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2007 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".) | | | | > > > > | > | > > > | | > | | > > > | | | | > > > > > > > | | > | > | > | > > > > > > > > > > | | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > | > < < < < < < < < < < < < < | > > > > > > > > > > > > > > > > > > | > > | | | | > | | > > | | > | > > > > > > > > > | > > > > > > | | | > | > > > > > > > > > > | 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 | /* ** Copyright (c) 2007 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 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 @ ); @ @ -- Identifier for this file type. @ -- The integer is the same as 'FSLG'. @ PRAGMA application_id=252006675; ; #if INTERFACE /* ** 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, -- 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 @ uid INTEGER REFERENCES user, -- User login @ mtime DATETIME, -- Time of receipt. Julian day. @ nonce TEXT UNIQUE, -- Nonce used for login @ ipaddr TEXT -- Remote IP address. NULL for direct. @ ); @ @ -- Information about users @ -- @ -- The user.pw field can be either cleartext of the password, or @ -- a SHA1 hash of the password. If the user.pw field is exactly 40 @ -- characters long we assume it is a SHA1 hash. Otherwise, it is @ -- cleartext. The sha1_shared_secret() routine computes the password @ -- hash based on the project-code, the user login, and the cleartext @ -- password. @ -- @ CREATE TABLE user( @ uid INTEGER PRIMARY KEY, -- User ID @ login TEXT UNIQUE, -- login name of the user @ pw TEXT, -- password @ cap TEXT, -- Capabilities of this user @ cookie TEXT, -- WWW login cookie @ 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 @ jx TEXT DEFAULT '{}' -- Extra fields in JSON @ ); @ @ -- 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 ) @ ); @ @ -- Artifacts that should not be processed are identified in the @ -- "shun" table. Artifacts that are control-file forgeries or @ -- spam or artifacts whose contents violate administrative policy @ -- can be shunned in order to prevent them from contaminating @ -- the repository. @ -- @ -- Shunned artifacts do not exist in the blob table. Hence they @ -- have not artifact ID (rid) and we thus must store their full @ -- UUID. @ -- @ CREATE TABLE shun( @ uuid UNIQUE, -- UUID of artifact to be shunned. Canonical form @ mtime DATE, -- 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. Private artifacts are omitted from the "unclustered" and @ -- "unsent" tables. @ -- @ -- A phantom artifact (that is, an artifact with BLOB.SIZE<0 - an artifact @ -- for which we do not know the content) might also be marked as private. @ -- This comes about when an artifact is named in a manifest or tag but @ -- the content of that artifact is held privately by some other peer @ -- repository. @ -- @ CREATE TABLE private(rid INTEGER PRIMARY KEY); @ @ -- An entry in this table describes a database query that generates a @ -- table of tickets. @ -- @ CREATE TABLE reportfmt( @ rn INTEGER PRIMARY KEY, -- Report number @ owner TEXT, -- Owner of this report format (not used) @ title TEXT UNIQUE, -- Title of this report @ mtime DATE, -- Last modified. seconds since 1970 @ cols TEXT, -- A color-key specification @ sqlcode TEXT, -- An SQL SELECT statement for this report @ jx TEXT DEFAULT '{}' -- Additional fields encoded as JSON @ ); @ @ -- 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 @ 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'', @ substr(tkt_uuid,1,10) AS ''#'', @ datetime(tkt_mtime) AS ''mtime'', @ type, @ status, @ subsystem, @ title @ FROM ticket'); ; const char zRepositorySchema2[] = @ -- 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. @ -- pid==(-1) if the file exists in a merge parents but not in the primary @ -- parent. In other words, if the file file was added by merge. @ -- fid==0 if the file is removed by check-in mid. @ -- @ CREATE TABLE mlink( @ mid INTEGER, -- Check-in that contains fid @ fid INTEGER, -- New file content. 0 if deleted @ pmid INTEGER, -- Check-in that contains pid @ pid INTEGER, -- Prev file content. 0 if new. -1 merge @ fnid INTEGER REFERENCES filename, -- Name of the file @ pfnid INTEGER, -- 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. Type meanings: @ -- ci Check-ins @ -- e Technotes @ -- f Forum posts @ -- g Tags @ -- t Ticket changes @ -- w Wiki page edit @ -- @ CREATE TABLE event( @ type TEXT, -- Type of event: ci, e, f, g, t, w @ mtime DATETIME, -- Time of occurrence. Julian day. @ objid INTEGER PRIMARY KEY, -- Associated record ID @ tagid INTEGER, -- Associated ticket or wiki name tag @ uid INTEGER REFERENCES user, -- User who caused the event @ bgcolor TEXT, -- Color set by 'bgcolor' property @ euser TEXT, -- User set by 'user' property @ user TEXT, -- Name of the user @ ecomment TEXT, -- Comment set by 'comment' property @ comment TEXT, -- Comment describing the event @ brief TEXT, -- Short comment when tagid already seen @ omtime DATETIME -- Original unchanged date+time, or NULL @ ); @ CREATE INDEX event_i1 ON event(mtime); @ @ -- A record of phantoms. A phantom is a record for which we know the @ -- file hash but we do not (yet) know the file content. @ -- @ CREATE TABLE phantom( @ rid INTEGER PRIMARY KEY -- Record ID of the phantom @ ); @ @ -- A record of orphaned delta-manifests. An orphan is a delta-manifest @ -- for which we have content, but its baseline-manifest is a phantom. @ -- We have to track all orphan manifests so that when the baseline arrives, @ -- we know to process the orphaned deltas. @ CREATE TABLE orphan( @ rid INTEGER PRIMARY KEY, -- Delta manifest with a phantom baseline @ baseline INTEGER -- Phantom baseline of this orphan @ ); @ CREATE INDEX orphan_baseline ON orphan(baseline); @ @ -- Unclustered records. An unclustered record is a record (including @ -- a cluster records themselves) that is not mentioned by some other @ -- cluster. @ -- @ -- Phantoms are usually included in the unclustered table. A new cluster @ -- will never be created that contains a phantom. But another repository |
︙ | ︙ | |||
261 262 263 264 265 266 267 | @ -- used to reduce push operations to a single HTTP request in the @ -- common case when one repository only talks to a single server. @ -- @ CREATE TABLE unsent( @ rid INTEGER PRIMARY KEY -- Record ID of the phantom @ ); @ | | | | | > > | | > | | > | > | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < | | | | | | | | | | < < | | | | | | > | > > > > > > > > > > > | > > > < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | @ -- used to reduce push operations to a single HTTP request in the @ -- common case when one repository only talks to a single server. @ -- @ CREATE TABLE unsent( @ rid INTEGER PRIMARY KEY -- Record ID of the phantom @ ); @ @ -- Each artifact 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-HASH" where @ -- HASH 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. @ ); @ INSERT INTO tag VALUES(1, 'bgcolor'); -- TAG_BGCOLOR @ INSERT INTO tag VALUES(2, 'comment'); -- TAG_COMMENT @ INSERT INTO tag VALUES(3, 'user'); -- TAG_USER @ INSERT INTO tag VALUES(4, 'date'); -- TAG_DATE @ INSERT INTO tag VALUES(5, 'hidden'); -- TAG_HIDDEN @ INSERT INTO tag VALUES(6, 'private'); -- TAG_PRIVATE @ INSERT INTO tag VALUES(7, 'cluster'); -- TAG_CLUSTER @ INSERT INTO tag VALUES(8, 'branch'); -- TAG_BRANCH @ INSERT INTO tag VALUES(9, 'closed'); -- TAG_CLOSED @ INSERT INTO tag VALUES(10,'parent'); -- TAG_PARENT @ INSERT INTO tag VALUES(11,'note'); -- TAG_NOTE @ @ -- Assignments of tags to artifacts. Note that we allow tags to @ -- have values assigned to them. So we are not really dealing with @ -- tags here. These are really properties. But we are going to @ -- keep calling them tags because in many cases the value is ignored. @ -- @ CREATE TABLE tagxref( @ tagid INTEGER REFERENCES tag, -- The tag being added, removed, @ -- or propagated @ tagtype INTEGER, -- 0:-,cancel 1:+,single 2:*,propagate @ srcid INTEGER REFERENCES blob, -- Artifact tag originates from, or @ -- 0 for propagated tags @ origid INTEGER REFERENCES blob, -- Artifact holding propagated tag @ -- (any artifact type with a P-card) @ value TEXT, -- Value of the tag. Might be NULL. @ mtime TIMESTAMP, -- Time of addition or removal. Julian day @ rid INTEGER REFERENCE blob, -- Artifact tag is applied to @ UNIQUE(rid, tagid) @ ); @ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime); @ @ -- When a hyperlink occurs from one artifact to another (for example @ -- 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=comment 1=ticket 2=wiki. See BKLNK_* below. @ srcid INT, -- EVENT.OBJID for the source document @ 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. @ -- @ CREATE TABLE attachment( @ attachid INTEGER PRIMARY KEY, -- Local id for this attachment @ isLatest BOOLEAN DEFAULT 0, -- True if this is the one to use @ mtime TIMESTAMP, -- Last changed. Julian day. @ src TEXT, -- Hash of the attachment. NULL to delete @ target TEXT, -- Object attached to. Wikiname or Tkt hash @ filename TEXT, -- Filename for the attachment @ comment TEXT, -- Comment associated with this attachment @ user TEXT -- Name of user adding attachment @ ); @ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime); @ CREATE INDEX attachment_idx2 ON attachment(src); @ @ -- Template for the TICKET table @ -- @ -- NB: when changing the schema of the TICKET table here, also make the @ -- same change in tktsetup.c. @ -- @ CREATE TABLE ticket( @ -- Do not change any column that begins with tkt_ @ tkt_id INTEGER PRIMARY KEY, @ tkt_uuid TEXT UNIQUE, @ tkt_mtime DATE, @ tkt_ctime DATE, @ -- Add as many field as required below this line @ type TEXT, @ status TEXT, @ subsystem TEXT, @ priority TEXT, @ severity TEXT, @ foundin TEXT, @ private_contact TEXT, @ resolution TEXT, @ title TEXT, @ comment TEXT @ ); @ CREATE TABLE ticketchng( @ -- Do not change any column that begins with tkt_ @ tkt_id INTEGER REFERENCES ticket, @ tkt_rid INTEGER REFERENCES blob, @ tkt_mtime DATE, @ tkt_user TEXT, @ -- Add as many fields as required below this line @ login TEXT, @ username TEXT, @ mimetype TEXT, @ icomment TEXT @ ); @ CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime); @ @ -- For tracking cherrypick merges @ CREATE TABLE cherrypick( @ parentid INT, @ childid INT, @ isExclude BOOLEAN DEFAULT false, @ PRIMARY KEY(parentid, childid) @ ) WITHOUT ROWID; @ CREATE INDEX cherrypick_cid ON cherrypick(childid); ; /* ** Allowed values for backlink.srctype */ #if INTERFACE # define BKLNK_COMMENT 0 /* Check-in comment */ # define BKLNK_TICKET 1 /* Ticket body or title */ # define BKLNK_WIKI 2 /* Wiki */ # define BKLNK_EVENT 3 /* Technote */ # define BKLNK_FORUM 4 /* Forum post */ # define ValidBklnk(X) (X>=0 && X<=4) /* True if backlink.srctype is valid */ #endif /* ** Allowed values for MIMEtype codes */ #if INTERFACE # define MT_NONE 0 /* unspecified */ # define MT_WIKI 1 /* Wiki */ # define MT_MARKDOWN 2 /* Markdonw */ # define MT_UNKNOWN 3 /* unknown */ # define ValidMTC(X) ((X)>=0 && (X)<=3) /* True if MIMEtype code is valid */ #endif /* ** 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 */ # define TAG_NOTE 11 /* Extra text appended to a check-in comment */ #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 check-out. See also the addendum in zLocalSchemaVmerge[]. */ 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 check-out. @ -- @ -- Important Variables: @ -- @ -- repository Full pathname of the repository database @ -- user-id Userid to use @ -- @ CREATE TABLE vvar( @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry @ value CLOB, -- Content of the named parameter @ CHECK( typeof(name)='text' AND length(name)>=1 ) @ ); @ @ -- Each entry in the vfile table represents a single file in the @ -- current check-out. @ -- @ -- The file.rid field is 0 for files or folders that have been @ -- added but not yet committed. @ -- @ -- Vfile.chnged meaning: @ -- 0 File is unmodified @ -- 1 Manually edited and/or modified as part of a merge command @ -- 2 Replaced by a merge command @ -- 3 Added by a merge command @ -- 4,5 Same as 2,3 except merge using --integrate @ -- @ CREATE TABLE vfile( @ id INTEGER PRIMARY KEY, -- ID of the checked-out file @ vid INTEGER REFERENCES blob, -- The check-in 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 @ origname TEXT, -- Original pathname. NULL if unchanged @ mhash TEXT, -- Hash of mrid iff mrid!=rid @ UNIQUE(pathname,vid) @ ); @ @ -- Identifier for this file type. @ -- The integer is the same as 'FSLC'. @ PRAGMA application_id=252006674; ; /* Additional local database initialization following the schema ** enhancement of 2019-01-19, in which the mhash column was added ** to vmerge and vfile. */ const char zLocalSchemaVmerge[] = @ -- This table holds a record of uncommitted merges in the local @ -- file tree. If a VFILE entry with id has merged with another @ -- record, there is an entry in this table with (id,merge) where @ -- merge is the RECORD table entry that the file merged against. @ -- An id of 0 or <-3 here means the version record itself. When @ -- id==(-1) that is a cherrypick merge, id==(-2) that is a @ -- backout merge and id==(-4) is an integrate merge. @ -- @ @ CREATE TABLE vmerge( @ id INTEGER REFERENCES vfile, -- VFILE entry that has been merged @ merge INTEGER, -- Merged with this record @ mhash TEXT -- SHA1/SHA3 hash for merge object @ ); @ CREATE UNIQUE INDEX vmergex1 ON vmerge(id,mhash); @ @ -- The following trigger will prevent older versions of Fossil that @ -- do not know about the new vmerge.mhash column from updating the @ -- vmerge table. This must be done with a trigger, since legacy Fossil @ -- uses INSERT OR IGNORE to update vmerge, and the OR IGNORE will cause @ -- a NOT NULL constraint to be silently ignored. @ @ CREATE TRIGGER vmerge_ck1 AFTER INSERT ON vmerge @ WHEN new.mhash IS NULL BEGIN @ SELECT raise(FAIL, @ 'trying to update a newer check-out with an older version of Fossil'); @ END; @ ; /* ** The following table holds information about forum posts. It ** is created on-demand whenever the manifest parser encounters ** a forum-post artifact. */ static const char zForumSchema[] = @ CREATE TABLE repository.forumpost( @ fpid INTEGER PRIMARY KEY, -- BLOB.rid for the artifact @ froot INT, -- fpid of the thread root @ fprev INT, -- Previous version of this same post @ firt INT, -- This post is in-reply-to @ fmtime REAL -- When posted. Julian day @ ); @ CREATE INDEX repository.forumthread ON forumpost(froot,fmtime); ; /* Create the forum-post schema if it does not already exist */ void schema_forum(void){ if( !db_table_exists("repository","forumpost") ){ db_multi_exec("%s",zForumSchema/*safe-for-%s*/); } } |
Added src/scroll.js.
> > | 1 2 | /* Cause the page to scroll so that the #scrollToMe is visible */ (document.getElementById('scrollToMe')||document.body).scrollIntoView(true); |
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 71 72 73 74 75 76 77 78 79 80 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement a search functions ** against timeline comments, check-in content, wiki pages, tickets, ** and/or forum posts. ** ** The search can be either a per-query "grep"-like search that scans ** the entire corpus. Or it can use the FTS4 search engine of SQLite. ** The choice is an administrator configuration option. ** ** The first option is referred to as "full-scan search". The second ** option is called "indexed search". ** ** The code in this file is ordered approximately as follows: ** ** (1) The full-scan search engine ** (2) The indexed search engine ** (3) Higher level interfaces that use either (1) or (b2) according ** to the current search configuration settings */ #include "config.h" #include "search.h" #include <assert.h> #if INTERFACE /* Maximum number of search terms for full-scan search */ #define SEARCH_MAX_TERM 8 /* ** A compiled search pattern used for full-scan search. */ 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, |
︙ | ︙ | |||
86 87 88 89 90 91 92 93 94 | 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, }; /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > < | | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > | | | | < | | < | < | < | > | > | | > > > | < > > > > | | | < > | > > > > | < > > > > < > > > | > > > > > > > > > > > > > > > > > > > | > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | | > > > > > | > > > > > > > > > > > > | > | | > > > > > > > > > > > > > > > > > > > > > > > < > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | 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 full-scan 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 full-scan search pattern */ static 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); } } } /* This the core search engine for full-scan search. ** ** 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 ... ** ** Run the full-scan search algorithm using SEARCHSTRING against ** the text of the files listed. Output matches and snippets. ** ** Options: ** --begin TEXT Text to insert before each match ** --end TEXT Text to insert after each match ** --gap TEXT Text to indicate elided content ** --html Input is HTML ** --static Use the static Search object */ 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], ExtFILE); 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 full-scan search pattern: ** ** search_init(PATTERN,BEGIN,END,GAP,FLAGS) ** ** All arguments are optional. PATTERN is the search pattern. If it ** is omitted, then the global search pattern is reset. BEGIN and END ** and GAP are the strings used to construct snippets. FLAGS is an ** integer bit pattern containing the various SRCH_CKIN, SRCH_DOC, ** SRCH_TKT, SRCH_FORUM, or SRCH_ALL bits to determine what is to be ** searched. */ 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); } } /* search_match(TEXT, TEXT, ....) ** ** Using the full-scan search engine created by the most recent call ** to search_init(), match the input the TEXT arguments. ** Remember the results in the global full-scan search object. ** 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<count(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); } /* search_score() ** ** Return the match score for the last successful search_match call. */ static void search_score_sqlfunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ sqlite3_result_int(context, gSearch.iScore); } /* search_snippet() ** ** Return a snippet for the last successful search_match() call. */ 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); } } /* stext(TYPE, RID, ARG) ** ** 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); } /* title(TYPE, RID, ARG) ** ** Return the title of the document to be search. */ 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 = 0; 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]); } } /* body(TYPE, RID, ARG) ** ** Return the body of the document to be search. */ 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 = 0; char *z = search_stext_cached(zType[0], rid, zName, &nHdr); sqlite3_result_text(context, z+nHdr+1, -1, SQLITE_TRANSIENT); } /* urlencode(X) ** ** Encode a string for use as a query parameter in a URL. This is ** the equivalent of printf("%T",X). */ 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 various SQL functions (defined above) needed to implement ** full-scan search. */ void search_sql_setup(sqlite3 *db){ static int once = 0; static const int enc = SQLITE_UTF8|SQLITE_INNOCUOUS; if( once++ ) return; sqlite3_create_function(db, "search_match", -1, enc, 0, search_match_sqlfunc, 0, 0); sqlite3_create_function(db, "search_score", 0, enc, 0, search_score_sqlfunc, 0, 0); sqlite3_create_function(db, "search_snippet", 0, enc, 0, search_snippet_sqlfunc, 0, 0); sqlite3_create_function(db, "search_init", -1, enc, 0, search_init_sqlfunc, 0, 0); sqlite3_create_function(db, "stext", 3, enc, 0, search_stext_sqlfunc, 0, 0); sqlite3_create_function(db, "title", 3, enc, 0, search_title_sqlfunc, 0, 0); sqlite3_create_function(db, "body", 3, enc, 0, search_body_sqlfunc, 0, 0); sqlite3_create_function(db, "urlencode", 1, enc, 0, search_urlencode_sqlfunc, 0, 0); } /* ** Testing the search function. ** ** COMMAND: search* ** ** Usage: %fossil search [-a|-all] [-n|-limit #] [-W|-width #] pattern... ** ** Search for timeline entries matching all words provided on the ** command line. Whole-word matches scope more highly than partial ** matches. ** ** Note: The command only search the EVENT table. So it will only ** display check-in comments or other comments that appear on an ** unaugmented timeline. It does not search document text or forum ** messages. ** ** 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. ** ** Options: ** -a|--all Output all matches, not just best matches ** --debug Show additional debug content on --fts search ** --fts Use the full-text search mechanism (testing only) ** -n|--limit N Limit output to N matches ** --scope SCOPE Scope of search. Valid for --fts only. One or ** more of: all, c, d, e, f, t, w. Defaults to all. ** -W|--width WIDTH Set display width to WIDTH columns, 0 for ** unlimited. Defaults the terminal's width. */ void search_cmd(void){ Blob pattern; int i; Blob sql = empty_blob; Stmt q; int iBest; char fAll = NULL != find_option("all", "a", 0); const char *zLimit = find_option("limit","n",1); const char *zWidth = find_option("width","W",1); const char *zScope = find_option("scope",0,1); int bDebug = find_option("debug",0,0)!=0; int nLimit = zLimit ? atoi(zLimit) : -1000; int width; int bFts = find_option("fts",0,0)!=0; if( zWidth ){ width = atoi(zWidth); if( (width!=0) && (width<=20) ){ fossil_fatal("-W|--width value must be >20 or 0"); } }else{ width = -1; } db_find_and_open_repository(0, 0); if( g.argc<3 ) return; blob_init(&pattern, g.argv[2], -1); for(i=3; i<g.argc; i++){ blob_appendf(&pattern, " %s", g.argv[i]); } if( bFts ){ /* Search using FTS */ Blob com; Blob snip; const char *zPattern = blob_str(&pattern); int srchFlags; unsigned int j; if( zScope==0 ){ srchFlags = SRCH_ALL; }else{ srchFlags = 0; for(i=0; zScope[i]; i++){ switch( zScope[i] ){ case 'a': srchFlags = SRCH_ALL; break; case 'c': srchFlags |= SRCH_CKIN; break; case 'd': srchFlags |= SRCH_DOC; break; case 'e': srchFlags |= SRCH_TECHNOTE; break; case 'f': srchFlags |= SRCH_FORUM; break; case 't': srchFlags |= SRCH_TKT; break; case 'w': srchFlags |= SRCH_WIKI; break; } } } 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); /* Full-scan search */ }else{ search_update_index(srchFlags); /* Update the index */ search_indexed(zPattern, srchFlags); /* Indexed search */ } db_prepare(&q, "SELECT snip, label, score, id, date" " FROM x" " ORDER BY score DESC, date DESC;"); blob_init(&com, 0, 0); blob_init(&snip, 0, 0); if( width<0 ) width = 80; while( db_step(&q)==SQLITE_ROW ){ const char *zSnippet = db_column_text(&q, 0); const char *zLabel = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 4); const char *zScore = db_column_text(&q, 2); const char *zId = db_column_text(&q, 3); blob_appendf(&snip, "%s", zSnippet); for(j=0; j<snip.nUsed; j++){ if( snip.aData[j]=='\n' ){ if( j>0 && snip.aData[j-1]=='\r' ) snip.aData[j-1] = ' '; snip.aData[j] = ' '; } } blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate); if( bDebug ){ blob_appendf(&com," score: %s id: %s", zScore, zId); } comment_print(blob_str(&com), 0, 5, width, COMMENT_PRINT_TRIM_CRLF | COMMENT_PRINT_WORD_BREAK | COMMENT_PRINT_TRIM_SPACE); blob_reset(&com); blob_reset(&snip); if( nLimit>=1 ){ nLimit--; if( nLimit==0 ) break; } } db_finalize(&q); blob_reset(&pattern); }else{ /* Legacy timeline search (the default) */ (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,toLocal())," " coalesce(ecomment,comment)," " search_score()" " FROM event, blob" " WHERE blob.rid=event.objid" " AND search_match(coalesce(ecomment,comment));" ); 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, 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_TECHNOTE 0x0010 /* Search over tech notes */ #define SRCH_FORUM 0x0020 /* Search over forum messages */ #define SRCH_ALL 0x003f /* Search over everything */ #endif /* ** Remove bits from srchFlags which are disallowed by either the ** current server configuration or by user permissions. Return ** the revised search flags mask. */ 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" }, { SRCH_TECHNOTE, "search-technote" }, { SRCH_FORUM, "search-forum" }, }; int i; if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE); if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT); if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI); if( g.perm.RdForum==0) srchFlags &= ~(SRCH_FORUM); for(i=0; i<count(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,id,snip). ** ** label: The "name" of the document containing the match ** url: A URL for the document ** score: How well the document matched ** id: The document id. Format: xNNNNN, x: type, N: number ** snip: A snippet for the match ** ** And the srchFlags parameter has been validated. This routine ** fills the X table with search results using a full-scan search. ** ** The companion indexed search routine is search_indexed(). */ LOCAL 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',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));" ); } if( (srchFlags & SRCH_TECHNOTE)!=0 ){ db_multi_exec( "WITH technote(uuid,rid,mtime) AS (" " SELECT substr(tagname,7), tagxref.rid, max(tagxref.mtime)" " FROM tag, tagxref" " WHERE tag.tagname GLOB 'event-*'" " AND tagxref.tagid=tag.tagid" " GROUP BY 1" ")" "INSERT INTO x(label,url,score,id,date,snip)" " SELECT printf('Tech Note: %%s',uuid)," " printf('/technote/%%s',uuid)," " search_score()," " 'e'||rid," " datetime(mtime)," " search_snippet()" " FROM technote" " WHERE search_match('',body('e',rid,NULL));" ); } if( (srchFlags & SRCH_FORUM)!=0 ){ db_multi_exec( "INSERT INTO x(label,url,score,id,date,snip)" " SELECT 'Forum '||comment," " '/forumpost/'||uuid," " search_score()," " 'f'||rid," " datetime(event.mtime)," " search_snippet()" " FROM event JOIN blob on event.objid=blob.rid" " WHERE search_match('',body('f',rid,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,id,snip). ** ** label: The "name" of the document containing the match ** url: A URL for the document ** score: How well the document matched ** id: The document id. Format: xNNNNN, x: type, N: number ** snip: A snippet for the match ** ** And the srchFlags parameter has been validated. This routine ** fills the X table with search results using FTS indexed search. ** ** The companion full-scan search routine is search_fullscan(). */ LOCAL void search_indexed( const char *zPattern, /* The query pattern */ unsigned int srchFlags /* What to search over */ ){ Blob sql; char *zPat = mprintf("%s",zPattern); int i; static const char *zSnippetCall; if( srchFlags==0 ) return; sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0, search_rank_sqlfunc, 0, 0); for(i=0; zPat[i]; i++){ if( (zPat[i]&0x80)==0 && !fossil_isalnum(zPat[i]) ) zPat[i] = ' '; } for(i--; i>=0 && zPat[i]==' '; i--){} if( i<0 ){ fossil_free(zPat); zPat = mprintf("\"\""); } blob_init(&sql, 0, 0); if( search_index_type(0)==4 ){ /* If this repo is still using the legacy FTS4 search index, then ** the snippet() function is slightly different */ zSnippetCall = "snippet(ftsidx,'<mark>','</mark>',' ... ',-1,35)"; }else{ /* This is the common case - Using newer FTS5 search index */ zSnippetCall = "snippet(ftsidx,-1,'<mark>','</mark>',' ... ',35)"; } 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)," " %s" " FROM ftsidx CROSS JOIN ftsdocs" " WHERE ftsidx MATCH %Q" " AND ftsdocs.rowid=ftsidx.rowid", zSnippetCall /*safe-for-%s*/, zPat ); fossil_free(zPat); 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' }, { SRCH_TECHNOTE, 'e' }, { SRCH_FORUM, 'f' }, }; int i; for(i=0; i<count(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; if( zSnip==0 ) zSnip = ""; 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. ** ** This routine works for both full-scan and indexed search. The ** appropriate low-level search routine is called according to the ** current configuration. ** ** 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; int nLimit = db_get_int("search-limit", 100); if( P("searchlimit")!=0 ){ nLimit = atoi(P("searchlimit")); } 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); /* Full-scan search */ }else{ search_update_index(srchFlags); /* Update the index, if necessary */ search_indexed(zPattern, srchFlags); /* Indexed search */ } db_prepare(&q, "SELECT url, snip, label, score, id, substr(date,1,10)" " 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); const char *zDate = db_column_text(&q, 5); 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)) \ if( zLabel && zDate && zDate[0] && strstr(zLabel,zDate)==0 ){ @ <small>(%h(zDate))</small> } @ </span></li> if( nLimit && nRow>=nLimit ) break; } 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. ** ** The mFlags value controls options: ** ** 0x01 If the y= query parameter is present, use it as an addition ** restriction what to search. ** ** 0x02 Show nothing if search is disabled. ** ** Return true if there are search results. */ int search_screen(unsigned srchFlags, int mFlags){ const char *zType = 0; const char *zClass = 0; const char *zDisable1; const char *zDisable2; const char *zPattern; int fDebug = PB("debug"); int haveResult = 0; 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; case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break; case SRCH_FORUM: zType = " Forum"; zClass = "Frm"; break; } if( srchFlags==0 ){ if( mFlags & 0x02 ) return 0; zDisable1 = " disabled"; zDisable2 = " disabled"; zPattern = ""; }else{ zDisable1 = ""; /* Was: " 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( (mFlags & 0x01)!=0 && (srchFlags & (srchFlags-1))!=0 ){ static const struct { const char *z; const 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 }, { "e", "Tech Notes", SRCH_TECHNOTE }, { "f", "Forum", SRCH_FORUM }, }; const char *zY = PD("y","all"); unsigned newFlags = srchFlags; int i; @ <select size='1' name='y'> for(i=0; i<count(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> haveResult = 1; } return haveResult; } /* ** WEBPAGE: search ** ** Search for check-in comments, documents, tickets, or wiki that ** match a user-supplied pattern. ** ** s=PATTERN Specify the full-text pattern to search for ** y=TYPE What to search. ** c -> check-ins ** d -> documentation ** t -> tickets ** w -> wiki ** e -> tech notes ** f -> forum ** all -> everything */ void search_page(void){ const int isSearch = P("s")!=0; login_check_credentials(); style_header("Search%s", isSearch ? " Results" : ""); cgi_check_for_malice(); search_screen(SRCH_ALL, 1); style_finish_page(); } /* ** This is a helper function for search_stext(). Writing into pOut ** the search text obtained from pIn according to zMimetype. ** ** If a title is not specified in zTitle (e.g. for wiki pages that do not ** include the title in the body), it is determined from the page content. ** ** 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, const char *zTitle, Blob *pOut ){ Blob html, title; Blob *pHtml = &html; blob_init(&html, 0, 0); if( zTitle==0 ){ blob_init(&title, 0, 0); }else{ blob_init(&title, zTitle, -1); } if( zMimetype==0 ) zMimetype = "text/plain"; if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){ if( blob_size(&title) ){ wiki_convert(pIn, &html, 0); }else{ 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, blob_size(&title) ? NULL : &title, &html); }else if( fossil_strcmp(zMimetype,"text/html")==0 ){ if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title); pHtml = pIn; } blob_appendf(pOut, "%s\n", blob_str(&title)); if( blob_size(pHtml) ){ html_to_plaintext(blob_str(pHtml), pOut); }else{ 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, NULL, 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 ** e Tech note ** f Forum ** ** rid The RID of an artifact that defines the object ** being searched. ** ** zName Name of the object being searched. This is used ** only to help figure out the mimetype (text/plain, ** test/html, test/x-fossil-wiki, or text/x-markdown) ** so that the code can know how to simplify the text. */ 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), NULL, pOut); blob_reset(&doc); break; } case 'f': /* Forum messages */ case 'e': /* Tech Notes */ case 'w': { /* Wiki */ Manifest *pWiki = manifest_get(rid, cType == 'e' ? CFTYPE_EVENT : cType == 'f' ? CFTYPE_FORUM : CFTYPE_WIKI, 0); Blob wiki; if( pWiki==0 ) break; if( cType=='f' ){ blob_init(&wiki, 0, 0); if( pWiki->zThreadTitle ){ blob_appendf(&wiki, "<h1>%h</h1>\n", pWiki->zThreadTitle); } blob_appendf(&wiki, "From %s:\n\n%s", pWiki->zUser, pWiki->zWiki); }else{ blob_init(&wiki, pWiki->zWiki, -1); } get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype), cType=='w' ? pWiki->zWikiTitle : NULL, 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", NULL, 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, for mimetype */ 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 RID NAME ** ** Compute the search text for document TYPE-RID whose name is NAME. ** The TYPE is one of "c", "d", "t", "w", or "e". The RID is the document ** ID. The NAME is used to figure out a mimetype to use for formatting ** the raw document text. */ 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], ExtFILE); blob_init(&out, 0, 0); get_stext_by_mimetype(&in, g.argv[3], NULL, &out); fossil_print("%s\n",blob_str(&out)); blob_reset(&in); blob_reset(&out); } /* ** The schema for the full-text index. The %s part must be an empty ** string or a comma followed by additional flags for the FTS virtual ** table. */ static const char zFtsSchema[] = @ -- One entry for each possible search result @ CREATE TABLE IF NOT EXISTS repository.ftsdocs( @ rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.rowid @ 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 repository.ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0; @ CREATE INDEX repository.ftsdocName ON ftsdocs(name) WHERE type='w'; @ CREATE VIEW IF NOT EXISTS repository.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 repository.ftsidx @ USING fts5(content="ftscontent", title, body%s); ; static const char zFtsDrop[] = @ DROP TABLE IF EXISTS repository.ftsidx; @ DROP VIEW IF EXISTS repository.ftscontent; @ DROP TABLE IF EXISTS repository.ftsdocs; ; #if INTERFACE /* ** Values for the search-tokenizer config option. */ #define FTS5TOK_NONE 0 /* disabled */ #define FTS5TOK_PORTER 1 /* porter stemmer */ #define FTS5TOK_UNICODE61 2 /* unicode61 tokenizer */ #define FTS5TOK_TRIGRAM 3 /* trigram tokenizer */ #endif /* ** Cached FTS5TOK_xyz value for search_tokenizer_type() and ** friends. */ static int iFtsTokenizer = -1; /* ** Returns one of the FTS5TOK_xyz values, depending on the value of ** the search-tokenizer config entry, defaulting to FTS5TOK_NONE. The ** result of the first call is cached for subsequent calls unless ** bRecheck is true. */ int search_tokenizer_type(int bRecheck){ char *z; if( iFtsTokenizer>=0 && bRecheck==0 ){ return iFtsTokenizer; } z = db_get("search-tokenizer",0); if( 0==z ){ iFtsTokenizer = FTS5TOK_NONE; }else if(0==fossil_strcmp(z,"porter")){ iFtsTokenizer = FTS5TOK_PORTER; }else if(0==fossil_strcmp(z,"unicode61")){ iFtsTokenizer = FTS5TOK_UNICODE61; }else if(0==fossil_strcmp(z,"trigram")){ iFtsTokenizer = FTS5TOK_TRIGRAM; }else{ iFtsTokenizer = is_truth(z) ? FTS5TOK_PORTER : FTS5TOK_NONE; } fossil_free(z); return iFtsTokenizer; } /* ** Returns a string value suitable for use as the search-tokenizer ** setting's value, depending on the value of z. If z is 0 then the ** current search-tokenizer value is used as the basis for formulating ** the result (which may differ from the current value but will have ** the same meaning). Any unknown/unsupported value is interpreted as ** "off". */ const char *search_tokenizer_for_string(const char *z){ char * zTmp = 0; const char *zRc = 0; if( 0==z ){ z = zTmp = db_get("search-tokenizer",0); } if( 0==z ){ zRc = "off"; }else if( 0==fossil_strcmp(z,"porter") ){ zRc = "porter"; }else if( 0==fossil_strcmp(z,"unicode61") ){ zRc = "unicode61"; }else if( 0==fossil_strcmp(z,"trigram") ){ zRc = "trigram"; }else{ zRc = is_truth(z) ? "porter" : "off"; } fossil_free(zTmp); return zRc; } /* ** Sets the search-tokenizer config setting to the value of ** search_tokenizer_for_string(zName). */ void search_set_tokenizer(const char *zName){ db_set("search-tokenizer", search_tokenizer_for_string( zName ), 0); iFtsTokenizer = -1; } /* ** Create or drop the tables associated with a full-text index. */ static int searchIdxExists = -1; void search_create_index(void){ const int useTokenizer = search_tokenizer_type(0); const char *zExtra; switch(useTokenizer){ case FTS5TOK_PORTER: zExtra = ",tokenize=porter"; break; case FTS5TOK_UNICODE61: zExtra = ",tokenize=unicode61"; break; case FTS5TOK_TRIGRAM: zExtra = ",tokenize=trigram"; break; default: zExtra = ""; break; } search_sql_setup(g.db); db_multi_exec(zFtsSchema/*works-like:"%s"*/, zExtra/*safe-for-%s*/); searchIdxExists = 1; } void search_drop_index(void){ db_multi_exec(zFtsDrop/*works-like:""*/); searchIdxExists = 0; } /* ** Return true if the full-text search index exists. See also the ** search_index_type() function. */ int search_index_exists(void){ if( searchIdxExists<0 ){ searchIdxExists = db_table_exists("repository","ftsdocs"); } return searchIdxExists; } /* ** Determine which full-text search index is currently being used to ** add searching. Return values: ** ** 0 No search index is available ** 4 FTS3/4 ** 5 FTS5 ** ** Results are cached. Make the argument 1 to reset the cache. See ** also the search_index_exists() routine. */ int search_index_type(int bReset){ static int idxType = -1; if( idxType<0 || bReset ){ idxType = db_int(0, "SELECT CASE WHEN sql GLOB '*fts4*' THEN 4 ELSE 5 END" " FROM repository.sqlite_schema WHERE name='ftsidx'" ); } return idxType; } /* ** 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;" ); db_multi_exec( "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed)" " SELECT type, objid, comment, 0 FROM event WHERE type IN ('e','f');" ); } /* ** 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() && !content_is_private(rid) ){ char zType[2]; zType[0] = cType; zType[1] = 0; search_sql_setup(g.db); db_multi_exec( "DELETE FROM ftsidx WHERE rowid 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' || cType=='e' ){ db_multi_exec( "DELETE FROM ftsidx WHERE rowid IN" " (SELECT rowid FROM ftsdocs WHERE type='%c' AND name=%Q AND idxed)", cType, zName ); db_multi_exec( "DELETE FROM ftsdocs WHERE type='%c' AND name=%Q AND rid!=%d", cType, zName, rid ); } /* All forum posts are always indexed */ } } /* ** 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; 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); 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 rowid 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/%T/%%s',urlencode(name))," " %.17g" " FROM current_docs", zDocBr, rTime ); db_multi_exec( "INSERT INTO ftsidx(rowid,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(rowid,title,body)" " SELECT rowid, '', body('c',rid,NULL) FROM ftsdocs" " WHERE type='c' AND NOT idxed;" ); db_multi_exec( "UPDATE ftsdocs SET idxed=1, name=NULL," " (label,url,mtime) = " " (SELECT printf('Check-in [%%.16s] on %%s',blob.uuid," " datetime(event.mtime))," " printf('/timeline?y=ci&c=%%.20s',blob.uuid)," " event.mtime" " FROM event, blob" " WHERE event.objid=ftsdocs.rid" " AND blob.rid=ftsdocs.rid)" "WHERE ftsdocs.type='c' AND NOT ftsdocs.idxed" ); } /* ** Deal with all of the unindexed 't' terms in FTSDOCS */ static void search_update_ticket_index(void){ db_multi_exec( "INSERT INTO ftsidx(rowid,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( "UPDATE ftsdocs SET idxed=1, name=NULL," " (label,url,mtime) =" " (SELECT printf('Ticket: %%s (%%s)',title('t',tkt_id,null)," " datetime(tkt_mtime))," " printf('/tktview/%%.20s',tkt_uuid)," " tkt_mtime" " FROM ticket" " WHERE tkt_id=ftsdocs.rid)" "WHERE ftsdocs.type='t' AND NOT ftsdocs.idxed" ); } /* ** Deal with all of the unindexed 'w' terms in FTSDOCS */ static void search_update_wiki_index(void){ db_multi_exec( "INSERT INTO ftsidx(rowid,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( "UPDATE ftsdocs SET idxed=1," " (name,label,url,mtime) = " " (SELECT ftsdocs.name," " 'Wiki: '||ftsdocs.name," " '/wiki?name='||urlencode(ftsdocs.name)," " tagxref.mtime" " FROM tagxref WHERE tagxref.rid=ftsdocs.rid)" " WHERE ftsdocs.type='w' AND NOT ftsdocs.idxed" ); } /* ** Deal with all of the unindexed 'f' terms in FTSDOCS */ static void search_update_forum_index(void){ db_multi_exec( "INSERT INTO ftsidx(rowid,title,body)" " SELECT rowid, title('f',rid,NULL),body('f',rid,NULL) FROM ftsdocs" " WHERE type='f' AND NOT idxed;" ); if( db_changes()==0 ) return; db_multi_exec( "UPDATE ftsdocs SET idxed=1, name=NULL," " (label,url,mtime) = " " (SELECT 'Forum '||event.comment," " '/forumpost/'||blob.uuid," " event.mtime" " FROM event, blob" " WHERE event.objid=ftsdocs.rid" " AND blob.rid=ftsdocs.rid)" "WHERE ftsdocs.type='f' AND NOT ftsdocs.idxed" ); } /* ** Deal with all of the unindexed 'e' terms in FTSDOCS */ static void search_update_technote_index(void){ db_multi_exec( "INSERT INTO ftsidx(rowid,title,body)" " SELECT rowid, title('e',rid,NULL),body('e',rid,NULL) FROM ftsdocs" " WHERE type='e' AND NOT idxed;" ); if( db_changes()==0 ) return; db_multi_exec( "UPDATE ftsdocs SET idxed=1," " (name,label,url,mtime) = " " (SELECT ftsdocs.name," " 'Tech Note: '||ftsdocs.name," " '/technote/'||substr(tag.tagname,7)," " tagxref.mtime" " FROM tagxref, tag USING (tagid)" " WHERE tagxref.rid=ftsdocs.rid" " AND tagname GLOB 'event-*')" " WHERE ftsdocs.type='e' AND NOT ftsdocs.idxed" ); } /* ** 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); db_unprotect(PROTECT_READONLY); 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(); } if( srchFlags & SRCH_TECHNOTE ){ search_update_technote_index(); } if( srchFlags & SRCH_FORUM ){ search_update_forum_index(); } db_protect_pop(); } /* ** 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 cdtwef Enable various kinds of search. c=Check-ins, ** d=Documents, t=Tickets, w=Wiki, e=Tech Notes, ** f=Forum. ** ** disable cdtwef Disable various kinds of search ** ** tokenizer VALUE Select a tokenizer for indexed search. VALUE ** may be one of (porter, on, off, trigram, unicode61), ** and "on" is equivalent to "porter". Unindexed ** search never uses tokenization or stemming. ** ** The current search settings are displayed after any changes are applied. ** Run this command with no arguments to simply see the settings. */ void fts_config_cmd(void){ static const struct { int iCmd; const char *z; } aCmd[] = { { 1, "reindex" }, { 2, "index" }, { 3, "disable" }, { 4, "enable" }, { 5, "tokenizer"}, }; static const struct { const char *zSetting; const char *zName; const char *zSw; } aSetng[] = { { "search-ci", "check-in search:", "c" }, { "search-doc", "document search:", "d" }, { "search-tkt", "ticket search:", "t" }, { "search-wiki", "wiki search:", "w" }, { "search-technote", "tech note search:", "e" }, { "search-forum", "forum search:", "f" }, }; 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<count(aCmd); i++){ if( fossil_strncmp(aCmd[i].z, zSubCmd, n)==0 ) break; } if( i>=count(aCmd) ){ Blob all; blob_init(&all,0,0); for(i=0; i<count(aCmd); i++) blob_appendf(&all, " %s", aCmd[i].z); fossil_fatal("unknown \"%s\" - should be one of:%s", zSubCmd, blob_str(&all)); return; } iCmd = aCmd[i].iCmd; } g.perm.Read = 1; g.perm.RdTkt = 1; g.perm.RdWiki = 1; 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<count(aSetng); j++){ if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){ db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0); } } }else if( iCmd==5 ){ int iOldTokenizer, iNewTokenizer; if( g.argc<4 ) usage("tokenizer porter|on|off|trigram|unicode61"); iOldTokenizer = search_tokenizer_type(0); db_set("search-tokenizer", search_tokenizer_for_string(g.argv[3]), 0); iNewTokenizer = search_tokenizer_type(1); if( iOldTokenizer!=iNewTokenizer ){ /* Drop or rebuild index if tokenizer changes. */ iAction = 1 + ((iOldTokenizer && iNewTokenizer) ? 1 : (iNewTokenizer ? 1 : 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<count(aSetng); i++){ fossil_print("%-17s %s\n", aSetng[i].zName, db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off"); } fossil_print("%-17s %s\n", "tokenizer:", search_tokenizer_for_string(0)); if( search_index_exists() ){ int pgsz = db_int64(0, "PRAGMA repository.page_size;"); i64 nTotal = db_int64(0, "PRAGMA repository.page_count;")*pgsz; i64 nFts = db_int64(0, "SELECT count(*) FROM dbstat" " WHERE schema='repository'" " AND name LIKE 'fts%%'")*pgsz; char zSize[50]; fossil_print("%-17s FTS%d\n", "full-text index:", search_index_type(1)); fossil_print("%-17s %d\n", "documents:", db_int(0, "SELECT count(*) FROM ftsdocs")); approxSizeName(sizeof(zSize), zSize, nFts); fossil_print("%-17s %s (%.1f%% of repository)\n", "space used", zSize, 100.0*((double)nFts/(double)nTotal)); }else{ fossil_print("%-17s disabled\n", "full-text index:"); } db_end_transaction(0); } /* ** WEBPAGE: test-ftsdocs ** ** Show a table of all documents currently in the search index. */ void search_data_page(void){ Stmt q; const char *zId = P("id"); const char *zType = P("y"); const char *zIdxed = P("ixed"); int id; int cnt1 = 0, cnt2 = 0, cnt3 = 0; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_set_current_feature("test"); if( !search_index_exists() ){ @ <p>Indexed search is disabled style_finish_page(); return; } search_sql_setup(g.db); style_submenu_element("Setup","%R/srchsetup"); if( zId!=0 && (id = atoi(zId))>0 ){ /* Show information about a single ftsdocs entry */ style_header("Information about ftsdoc entry %d", id); style_submenu_element("Summary","%R/test-ftsdocs"); db_prepare(&q, "SELECT type||rid, name, idxed, label, url, datetime(mtime)" " FROM ftsdocs WHERE rowid=%d", id ); if( db_step(&q)==SQLITE_ROW ){ const char *zUrl = db_column_text(&q,4); const char *zDocId = db_column_text(&q,0); char *zName; char *z; @ <table border=0> @ <tr><td align='right'>rowid:<td> <td>%d(id) @ <tr><td align='right'>id:<td><td>%s(zDocId) @ <tr><td align='right'>name:<td><td>%h(db_column_text(&q,1)) @ <tr><td align='right'>idxed:<td><td>%d(db_column_int(&q,2)) @ <tr><td align='right'>label:<td><td>%h(db_column_text(&q,3)) @ <tr><td align='right'>url:<td><td> @ <a href='%R%s(zUrl)'>%h(zUrl)</a> @ <tr><td align='right'>mtime:<td><td>%s(db_column_text(&q,5)) z = db_text(0, "SELECT title FROM ftsidx WHERE rowid=%d",id); if( z && z[0] ){ @ <tr><td align="right">title:<td><td>%h(z) fossil_free(z); } z = db_text(0, "SELECT body FROM ftsidx WHERE rowid=%d",id); if( z && z[0] ){ @ <tr><td align="right" valign="top">body:<td><td>%h(z) fossil_free(z); } @ </table> zName = mprintf("Indexed '%c' docs",zDocId[0]); style_submenu_element(zName,"%R/test-ftsdocs?y=%c&ixed=1",zDocId[0]); zName = mprintf("Unindexed '%c' docs",zDocId[0]); style_submenu_element(zName,"%R/test-ftsdocs?y=%c&ixed=0",zDocId[0]); } db_finalize(&q); style_finish_page(); return; } if( zType!=0 && zType[0]!=0 && zType[1]==0 && zIdxed!=0 && (zIdxed[0]=='1' || zIdxed[0]=='0') && zIdxed[1]==0 ){ int ixed = zIdxed[0]=='1'; char *zName; style_header("List of '%c' documents that are%s indexed", zType[0], ixed ? "" : " not"); style_submenu_element("Summary","%R/test-ftsdocs"); if( ixed==0 ){ zName = mprintf("Indexed '%c' docs",zType[0]); style_submenu_element(zName,"%R/test-ftsdocs?y=%c&ixed=1",zType[0]); }else{ zName = mprintf("Unindexed '%c' docs",zType[0]); style_submenu_element(zName,"%R/test-ftsdocs?y=%c&ixed=0",zType[0]); } db_prepare(&q, "SELECT rowid, type||rid ||' '|| coalesce(label,'')" " FROM ftsdocs WHERE type='%c' AND %s idxed", zType[0], ixed ? "" : "NOT" ); @ <ul> while( db_step(&q)==SQLITE_ROW ){ @ <li> <a href='test-ftsdocs?id=%d(db_column_int(&q,0))'> @ %h(db_column_text(&q,1))</a> } @ </ul> db_finalize(&q); style_finish_page(); return; } style_header("Summary of ftsdocs"); db_prepare(&q, "SELECT type, sum(idxed IS TRUE), sum(idxed IS FALSE), count(*)" " FROM ftsdocs" " GROUP BY 1 ORDER BY 4 DESC" ); @ <table border=1 cellpadding=3 cellspacing=0> @ <thead> @ <tr><th>Type<th>Indexed<th>Unindexed<th>Total @ </thead> @ <tbody> while( db_step(&q)==SQLITE_ROW ){ const char *zType = db_column_text(&q,0); int nIndexed = db_column_int(&q, 1); int nUnindexed = db_column_int(&q, 2); int nTotal = db_column_int(&q, 3); @ <tr><td>%h(zType) if( nIndexed>0 ){ @ <td align="right"><a href='%R/test-ftsdocs?y=%s(zType)&ixed=1'>\ @ %d(nIndexed)</a> }else{ @ <td align="right">0 } if( nUnindexed>0 ){ @ <td align="right"><a href='%R/test-ftsdocs?y=%s(zType)&ixed=0'>\ @ %d(nUnindexed)</a> }else{ @ <td align="right">0 } @ <td align="right">%d(nTotal) @ </tr> cnt1 += nIndexed; cnt2 += nUnindexed; cnt3 += nTotal; } db_finalize(&q); @ </tbody><tfooter> @ <tr><th>Total<th align="right">%d(cnt1)<th align="right">%d(cnt2) @ <th align="right">%d(cnt3) @ </tfooter> @ </table> style_finish_page(); } /* ** The Fts5MatchinfoCtx bits were all taken verbatim from: ** ** https://sqlite.org/src/finfo?name=ext/fts5/fts5_test_mi.c */ typedef struct Fts5MatchinfoCtx Fts5MatchinfoCtx; #if INTERFACE #ifndef SQLITE_AMALGAMATION typedef unsigned int u32; #endif #endif struct Fts5MatchinfoCtx { int nCol; /* Number of cols in FTS5 table */ int nPhrase; /* Number of phrases in FTS5 query */ char *zArg; /* nul-term'd copy of 2nd arg */ int nRet; /* Number of elements in aRet[] */ u32 *aRet; /* Array of 32-bit unsigned ints to return */ }; /* ** Return a pointer to the fts5_api pointer for database connection db. ** If an error occurs, return NULL and leave an error in the database ** handle (accessible using sqlite3_errcode()/errmsg()). */ static int fts5_api_from_db(sqlite3 *db, fts5_api **ppApi){ sqlite3_stmt *pStmt = 0; int rc; *ppApi = 0; rc = sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0); if( rc==SQLITE_OK ){ sqlite3_bind_pointer(pStmt, 1, (void*)ppApi, "fts5_api_ptr", 0); (void)sqlite3_step(pStmt); rc = sqlite3_finalize(pStmt); } return rc; } /* ** Argument f should be a flag accepted by matchinfo() (a valid character ** in the string passed as the second argument). If it is not, -1 is ** returned. Otherwise, if f is a valid matchinfo flag, the value returned ** is the number of 32-bit integers added to the output array if the ** table has nCol columns and the query nPhrase phrases. */ static int fts5MatchinfoFlagsize(int nCol, int nPhrase, char f){ int ret = -1; switch( f ){ case 'p': ret = 1; break; case 'c': ret = 1; break; case 'x': ret = 3 * nCol * nPhrase; break; case 'y': ret = nCol * nPhrase; break; case 'b': ret = ((nCol + 31) / 32) * nPhrase; break; case 'n': ret = 1; break; case 'a': ret = nCol; break; case 'l': ret = nCol; break; case 's': ret = nCol; break; } return ret; } static int fts5MatchinfoIter( const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ Fts5Context *pFts, /* First arg to pass to pApi functions */ Fts5MatchinfoCtx *p, int(*x)(const Fts5ExtensionApi*,Fts5Context*,Fts5MatchinfoCtx*,char,u32*) ){ int i; int n = 0; int rc = SQLITE_OK; char f; for(i=0; (f = p->zArg[i]); i++){ rc = x(pApi, pFts, p, f, &p->aRet[n]); if( rc!=SQLITE_OK ) break; n += fts5MatchinfoFlagsize(p->nCol, p->nPhrase, f); } return rc; } static int fts5MatchinfoXCb( const Fts5ExtensionApi *pApi, Fts5Context *pFts, void *pUserData ){ Fts5PhraseIter iter; int iCol, iOff; u32 *aOut = (u32*)pUserData; int iPrev = -1; for(pApi->xPhraseFirst(pFts, 0, &iter, &iCol, &iOff); iCol>=0; pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) ){ aOut[iCol*3+1]++; if( iCol!=iPrev ) aOut[iCol*3 + 2]++; iPrev = iCol; } return SQLITE_OK; } static int fts5MatchinfoGlobalCb( const Fts5ExtensionApi *pApi, Fts5Context *pFts, Fts5MatchinfoCtx *p, char f, u32 *aOut ){ int rc = SQLITE_OK; switch( f ){ case 'p': aOut[0] = p->nPhrase; break; case 'c': aOut[0] = p->nCol; break; case 'x': { int i; for(i=0; i<p->nPhrase && rc==SQLITE_OK; i++){ void *pPtr = (void*)&aOut[i * p->nCol * 3]; rc = pApi->xQueryPhrase(pFts, i, pPtr, fts5MatchinfoXCb); } break; } case 'n': { sqlite3_int64 nRow; rc = pApi->xRowCount(pFts, &nRow); aOut[0] = (u32)nRow; break; } case 'a': { sqlite3_int64 nRow = 0; rc = pApi->xRowCount(pFts, &nRow); if( nRow==0 ){ memset(aOut, 0, sizeof(u32) * p->nCol); }else{ int i; for(i=0; rc==SQLITE_OK && i<p->nCol; i++){ sqlite3_int64 nToken; rc = pApi->xColumnTotalSize(pFts, i, &nToken); if( rc==SQLITE_OK){ aOut[i] = (u32)((2*nToken + nRow) / (2*nRow)); } } } break; } } return rc; } static int fts5MatchinfoLocalCb( const Fts5ExtensionApi *pApi, Fts5Context *pFts, Fts5MatchinfoCtx *p, char f, u32 *aOut ){ int i; int rc = SQLITE_OK; switch( f ){ case 'b': { int iPhrase; int nInt = ((p->nCol + 31) / 32) * p->nPhrase; for(i=0; i<nInt; i++) aOut[i] = 0; for(iPhrase=0; iPhrase<p->nPhrase; iPhrase++){ Fts5PhraseIter iter; int iCol; for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol); iCol>=0; pApi->xPhraseNextColumn(pFts, &iter, &iCol) ){ aOut[iPhrase * ((p->nCol+31)/32) + iCol/32] |= ((u32)1 << iCol%32); } } break; } case 'x': case 'y': { int nMul = (f=='x' ? 3 : 1); int iPhrase; for(i=0; i<(p->nCol*p->nPhrase); i++) aOut[i*nMul] = 0; for(iPhrase=0; iPhrase<p->nPhrase; iPhrase++){ Fts5PhraseIter iter; int iOff, iCol; for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); iOff>=0; pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) ){ aOut[nMul * (iCol + iPhrase * p->nCol)]++; } } break; } case 'l': { for(i=0; rc==SQLITE_OK && i<p->nCol; i++){ int nToken; rc = pApi->xColumnSize(pFts, i, &nToken); aOut[i] = (u32)nToken; } break; } case 's': { int nInst; memset(aOut, 0, sizeof(u32) * p->nCol); rc = pApi->xInstCount(pFts, &nInst); for(i=0; rc==SQLITE_OK && i<nInst; i++){ int iPhrase, iOff, iCol = 0; int iNextPhrase; int iNextOff; u32 nSeq = 1; int j; rc = pApi->xInst(pFts, i, &iPhrase, &iCol, &iOff); iNextPhrase = iPhrase+1; iNextOff = iOff+pApi->xPhraseSize(pFts, 0); for(j=i+1; rc==SQLITE_OK && j<nInst; j++){ int ip, ic, io; rc = pApi->xInst(pFts, j, &ip, &ic, &io); if( ic!=iCol || io>iNextOff ) break; if( ip==iNextPhrase && io==iNextOff ){ nSeq++; iNextPhrase = ip+1; iNextOff = io + pApi->xPhraseSize(pFts, ip); } } if( nSeq>aOut[iCol] ) aOut[iCol] = nSeq; } break; } } return rc; } static Fts5MatchinfoCtx *fts5MatchinfoNew( const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ Fts5Context *pFts, /* First arg to pass to pApi functions */ sqlite3_context *pCtx, /* Context for returning error message */ const char *zArg /* Matchinfo flag string */ ){ Fts5MatchinfoCtx *p; int nCol; int nPhrase; int i; int nInt; sqlite3_int64 nByte; int rc; nCol = pApi->xColumnCount(pFts); nPhrase = pApi->xPhraseCount(pFts); nInt = 0; for(i=0; zArg[i]; i++){ int n = fts5MatchinfoFlagsize(nCol, nPhrase, zArg[i]); if( n<0 ){ char *zErr = sqlite3_mprintf("unrecognized matchinfo flag: %c", zArg[i]); sqlite3_result_error(pCtx, zErr, -1); sqlite3_free(zErr); return 0; } nInt += n; } nByte = sizeof(Fts5MatchinfoCtx) /* The struct itself */ + sizeof(u32) * nInt /* The p->aRet[] array */ + (i+1); /* The p->zArg string */ p = (Fts5MatchinfoCtx*)sqlite3_malloc64(nByte); if( p==0 ){ sqlite3_result_error_nomem(pCtx); return 0; } memset(p, 0, nByte); p->nCol = nCol; p->nPhrase = nPhrase; p->aRet = (u32*)&p[1]; p->nRet = nInt; p->zArg = (char*)&p->aRet[nInt]; memcpy(p->zArg, zArg, i); rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoGlobalCb); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); sqlite3_free(p); p = 0; } return p; } static void fts5MatchinfoFunc( const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ Fts5Context *pFts, /* First arg to pass to pApi functions */ sqlite3_context *pCtx, /* Context for returning result/error */ int nVal, /* Number of values in apVal[] array */ sqlite3_value **apVal /* Array of trailing arguments */ ){ const char *zArg; Fts5MatchinfoCtx *p; int rc = SQLITE_OK; if( nVal>0 ){ zArg = (const char*)sqlite3_value_text(apVal[0]); }else{ zArg = "pcx"; } p = (Fts5MatchinfoCtx*)pApi->xGetAuxdata(pFts, 0); if( p==0 || sqlite3_stricmp(zArg, p->zArg) ){ p = fts5MatchinfoNew(pApi, pFts, pCtx, zArg); if( p==0 ){ rc = SQLITE_NOMEM; }else{ rc = pApi->xSetAuxdata(pFts, p, sqlite3_free); } } if( rc==SQLITE_OK ){ rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoLocalCb); } if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); }else{ /* No errors has occured, so return a copy of the array of integers. */ int nByte = p->nRet * sizeof(u32); sqlite3_result_blob(pCtx, (void*)p->aRet, nByte, SQLITE_TRANSIENT); } } int db_register_fts5(sqlite3 *db){ int rc; /* Return code */ fts5_api *pApi; /* FTS5 API functions */ /* Extract the FTS5 API pointer from the database handle. The ** fts5_api_from_db() function above is copied verbatim from the ** FTS5 documentation. Refer there for details. */ rc = fts5_api_from_db(db, &pApi); if( rc!=SQLITE_OK ) return rc; /* If fts5_api_from_db() returns NULL, then either FTS5 is not registered ** with this database handle, or an error (OOM perhaps?) has occurred. ** ** Also check that the fts5_api object is version 2 or newer. */ if( pApi==0 || pApi->iVersion<2 ){ return SQLITE_ERROR; } /* Register the implementation of matchinfo() */ rc = pApi->xCreateFunction(pApi, "matchinfo", 0, fts5MatchinfoFunc, 0); return rc; } |
Added src/security_audit.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 | /* ** Copyright (c) 2017 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 various web pages use for running a security audit ** of a Fossil configuration. */ #include "config.h" #include <assert.h> #include "security_audit.h" /* ** Return TRUE if any of the capability letters in zTest are found ** in the capability string zCap. */ static int hasAnyCap(const char *zCap, const char *zTest){ while( zTest[0] ){ if( strchr(zCap, zTest[0]) ) return 1; zTest++; } return 0; } /* ** Parse the content-security-policy ** into separate fields, and return a pointer to a null-terminated ** array of pointers to strings, one entry for each field. Or return ** a NULL pointer if no CSP could be located in the header. ** ** Memory to hold the returned array and of the strings is obtained from ** a single memory allocation, which the caller should free to avoid a ** memory leak. */ static char **parse_content_security_policy(void){ char **azCSP = 0; int nCSP = 0; char *zAll; char *zCopy; int nAll = 0; int jj; int nSemi; zAll = style_csp(0); nAll = (int)strlen(zAll); for(jj=nSemi=0; jj<nAll; jj++){ if( zAll[jj]==';' ) nSemi++; } azCSP = fossil_malloc( nAll+1+(nSemi+2)*sizeof(char*) ); zCopy = (char*)&azCSP[nSemi+2]; memcpy(zCopy,zAll,nAll); zCopy[nAll] = 0; while( fossil_isspace(zCopy[0]) || zCopy[0]==';' ){ zCopy++; } azCSP[0] = zCopy; nCSP = 1; for(jj=0; zCopy[jj]; jj++){ if( zCopy[jj]==';' ){ int k; for(k=jj-1; k>0 && fossil_isspace(zCopy[k]); k--){ zCopy[k] = 0; } zCopy[jj] = 0; while( jj+1<nAll && (fossil_isspace(zCopy[jj+1]) || zCopy[jj+1]==';') ){ jj++; } assert( nCSP<nSemi+1 ); azCSP[nCSP++] = zCopy+jj; } } assert( nCSP<=nSemi+2 ); azCSP[nCSP] = 0; fossil_free(zAll); return azCSP; } /* ** WEBPAGE: secaudit0 ** ** Run a security audit of the current Fossil setup, looking ** for configuration problems that might allow unauthorized ** access to the repository. ** ** This page requires administrator access. It is usually ** accessed using the Admin/Security-Audit menu option ** from any of the default skins. */ void secaudit0_page(void){ const char *zAnonCap; /* Capabilities of user "anonymous" and "nobody" */ const char *zDevCap; /* Capabilities of user group "developer" */ const char *zReadCap; /* Capabilities of user group "reader" */ const char *zPubPages; /* GLOB pattern for public pages */ const char *zSelfCap; /* Capabilities of self-registered users */ int hasSelfReg = 0; /* True if able to self-register */ const char *zPublicUrl; /* Canonical access URL */ Blob cmd; char *z; int n, i; CapabilityString *pCap; char **azCSP; /* Parsed content security policy */ login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_header("Security Audit"); @ <ol> /* Step 1: Determine if the repository is public or private. "Public" ** means that any anonymous user on the internet can access all content. ** "Private" repos require (non-anonymous) login to access all content, ** though some content may be accessible anonymously. */ zAnonCap = db_text("", "SELECT fullcap(NULL)"); zDevCap = db_text("", "SELECT fullcap('v')"); zReadCap = db_text("", "SELECT fullcap('u')"); zPubPages = db_get("public-pages",0); hasSelfReg = db_get_boolean("self-register",0); pCap = capability_add(0, db_get("default-perms","u")); capability_expand(pCap); zSelfCap = capability_string(pCap); capability_free(pCap); if( hasAnyCap(zAnonCap,"as") ){ @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because @ it grants administrator privileges to anonymous users. You @ should <a href="takeitprivate">take this repository private</a> @ immediately! Or, at least remove the Setup and Admin privileges @ for users "anonymous" and "login" on the @ <a href="setup_ulist">User Configuration</a> page. }else if( hasAnyCap(zSelfCap,"as") && hasSelfReg ){ @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because @ it grants administrator privileges to self-registered users. You @ should <a href="takeitprivate">take this repository private</a> @ and/or disable self-registration @ immediately! Or, at least remove the Setup and Admin privileges @ from the default permissions for new users. }else if( hasAnyCap(zAnonCap,"y") ){ @ <li><p>This repository is <big><b>INSECURE</b></big> because @ it allows anonymous users to push unversioned files. @ Fix this by <a href="takeitprivate">taking the repository private</a> @ or by removing the "y" permission from users "anonymous" and @ "nobody" on the <a href="setup_ulist">User Configuration</a> page. }else if( hasAnyCap(zSelfCap,"y") ){ @ <li><p>This repository is <big><b>INSECURE</b></big> because @ it allows self-registered users to push unversioned files. @ Fix this by <a href="takeitprivate">taking the repository private</a> @ or by removing the "y" permission from the default permissions or @ by disabling self-registration. }else if( hasAnyCap(zAnonCap,"goz") ){ @ <li><p>This repository is <big><b>PUBLIC</b></big>. All @ checked-in content can be accessed by anonymous users. @ <a href="takeitprivate">Take it private</a>.<p> }else if( hasAnyCap(zSelfCap,"goz") && hasSelfReg ){ @ <li><p>This repository is <big><b>PUBLIC</b></big> because all @ checked-in content can be accessed by self-registered users. @ This repostory would be private if you disabled self-registration.</p> }else if( !hasAnyCap(zAnonCap, "jrwy234567") && (!hasSelfReg || !hasAnyCap(zSelfCap, "jrwy234567")) && (zPubPages==0 || zPubPages[0]==0) ){ @ <li><p>This repository is <big><b>Completely PRIVATE</b></big>. @ A valid login and password is required to access any content. }else{ @ <li><p>This repository is <big><b>Mostly PRIVATE</b></big>. @ A valid login and password is usually required, however some @ content can be accessed either anonymously or by self-registered @ users: @ <ul> if( hasSelfReg ){ if( hasAnyCap(zAnonCap,"j") || hasAnyCap(zSelfCap,"j") ){ @ <li> Wiki pages } if( hasAnyCap(zAnonCap,"r") || hasAnyCap(zSelfCap,"r") ){ @ <li> Tickets } if( hasAnyCap(zAnonCap,"234567") || hasAnyCap(zSelfCap,"234567") ){ @ <li> Forum posts } } if( zPubPages && zPubPages[0] ){ Glob *pGlob = glob_create(zPubPages); int i; @ <li> "Public Pages" are URLs that match any of these GLOB patterns: @ <p><ul> for(i=0; i<pGlob->nPattern; i++){ @ <li> %h(pGlob->azPattern[i]) } @ </ul> @ <p>Anoymous users are vested with capabilities "%h(zSelfCap)" on @ public pages. See the "Public Pages" entry in the @ "User capability summary" below. } @ </ul> if( zPubPages && zPubPages[0] ){ @ <p>Change GLOB patterns exceptions using the "Public pages" setting @ on the <a href="setup_access">Access Settings</a> page.</p> } } zPublicUrl = public_url(); if( zPublicUrl!=0 ){ int nOther = db_int(0, "SELECT count(*) FROM config" " WHERE name GLOB 'baseurl:*'" " AND name<>'baseurl:%q'", zPublicUrl); @ <li><p>The <a href="setup_config#eurl">canonical URL</a> for this @ repository is <a href="%s(zPublicUrl)">%h(zPublicUrl)</a>. if( nOther==1 ){ @ This is also <a href="urllist?urlonly">1 other URL</a> that has @ been used to access this repository. }else if( nOther>=2 ){ @ There are also @ <a href="urllist?all&urlonly">%d(nOther) other URLs</a> that have @ been used to access this repository. } }else{ int nUrl = db_int(0, "SELECT count(*) FROM config" " WHERE name GLOB 'baseurl:*'"); @ <li><p>This repository does not have a @ <a href="setup_config#eurl">canonical access URL</a>. if( nUrl==1 ){ @ There is @ <a href="urllist?urlonly">1 non-canonical URL</a> @ that has been used to access this repository. }else if( nUrl>=2 ){ @ There are @ <a href="urllist?all&urlonly">%d(nUrl) non-canonical URLs</a> @ that have been used to access this repository. } } /* Make sure the HTTPS is required for login, at least, so that the ** password does not go across the Internet in the clear. */ if( db_get_int("redirect-to-https",0)==0 ){ @ <li><p><b>WARNING:</b> @ Sensitive material such as login passwords can be sent over an @ unencrypted connection. @ Fix this by changing the "Redirect to HTTPS" setting on the @ <a href="setup_access">Access Control</a> page. If you were using @ the old "Redirect to HTTPS on Login Page" setting, switch to the @ new setting: it has a more secure implementation. } #ifdef FOSSIL_ENABLE_TH1_DOCS /* The use of embedded TH1 is dangerous. Warn if it is possible. */ if( !Th_AreDocsEnabled() ){ @ <li><p> @ This server is compiled with -DFOSSIL_ENABLE_TH1_DOCS. TH1 docs @ are disabled for this particular repository, so you are safe for @ now. However, to prevent future problems caused by accidentally @ enabling TH1 docs in the future, it is recommended that you @ recompile Fossil without the -DFOSSIL_ENABLE_TH1_DOCS flag.</p> }else{ @ <li><p><b>DANGER:</b> @ This server is compiled with -DFOSSIL_ENABLE_TH1_DOCS and TH1 docs @ are enabled for this repository. Anyone who can check-in or push @ to this repository can create a malicious TH1 script and then cause @ that script to be run on the server. This is a serious security concern. @ TH1 docs should only be enabled for repositories with a very limited @ number of trusted committers, and the repository should be monitored @ closely to ensure no hostile content sneaks in. If a bad TH1 script @ does make it into the repository, the only want to prevent it from @ being run is to shun it.</p> @ @ <p>Disable TH1 docs by recompiling Fossil without the @ -DFOSSIL_ENABLE_TH1_DOCS flag, and/or clear the th1-docs setting @ and ensure that the TH1_ENABLE_DOCS environment variable does not @ exist in the environment.</p> } #endif #if FOSSIL_ENABLE_TCL @ <li><p> if( db_get_boolean("tcl",0) ){ #ifdef FOSSIL_ENABLE_TH1_DOCS if( Th_AreDocsEnabled() ){ @ <b>DANGER:</b> }else{ @ <b>WARNING:</b> } #else @ <b>WARNING:</b> #endif @ This server is compiled with -DFOSSIL_ENABLE_TCL and Tcl integration @ is enabled for this repository. Anyone who can execute malicious @ TH1 script on that server can also execute arbitrary Tcl script @ under the identity of the operating system process of that server. @ This is a serious security concern.</p> @ @ <p>Disable Tcl integration by recompiling Fossil without the @ -DFOSSIL_ENABLE_TCL flag, and/or clear the 'tcl' setting.</p> }else{ @ This server is compiled with -DFOSSIL_ENABLE_TCL. Tcl integration @ is disabled for this particular repository, so you are safe for @ now. However, to prevent potential problems caused by accidentally @ enabling Tcl integration in the future, it is recommended that you @ recompile Fossil without the -DFOSSIL_ENABLE_TCL flag.</p> } #endif /* Anonymous users should not be able to harvest email addresses ** from tickets. */ if( hasAnyCap(zAnonCap, "e") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can view email addresses and other personally @ identifiable information on tickets. @ Fix this by removing the "Email" privilege @ (<a href="setup_ucap_list">capability "e"</a>) from users @ "anonymous" and "nobody" on the @ <a href="setup_ulist">User Configuration</a> page. } /* Anonymous users probably should not be allowed to push content ** to the repository. */ if( hasAnyCap(zAnonCap, "i") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can push new check-ins into the repository. @ Fix this by removing the "Check-in" privilege @ (<a href="setup_ucap_list">capability</a> "i") from users @ "anonymous" and "nobody" on the @ <a href="setup_ulist">User Configuration</a> page. } /* Anonymous users probably should not be allowed act as moderators ** for wiki or tickets. */ if( hasAnyCap(zAnonCap, "lq5") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can act as moderators for wiki, tickets, or @ forum posts. This defeats the whole purpose of moderation. @ Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum" @ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5") @ from users "anonymous" and "nobody" @ on the <a href="setup_ulist">User Configuration</a> page. } /* Check to see if any TH1 scripts are configured to run on a sync */ if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'" " AND length(value)>0") ){ @ <li><p><b>WARNING:</b> @ TH1 scripts might be configured to run on any sync, push, pull, or @ clone operation. See the the <a href="%R/xfersetup">/xfersetup</a> @ page for more information. These TH1 scripts are a potential @ security concern and so should be carefully audited by a human. } /* The strict-manifest-syntax setting should be on. */ if( db_get_boolean("strict-manifest-syntax",1)==0 ){ @ <li><p><b>WARNING:</b> @ The "strict-manifest-syntax" flag is off. This is a security @ risk. Turn this setting on (its default) to protect the users @ of this repository. } /* Obsolete: */ if( hasAnyCap(zAnonCap, "d") || hasAnyCap(zDevCap, "d") || hasAnyCap(zReadCap, "d") ){ @ <li><p><b>WARNING:</b> @ One or more users has the <a @ href="https://fossil-scm.org/forum/forumpost/43c78f4bef">obsolete</a> @ "d" capability. You should remove it using the @ <a href="setup_ulist">User Configuration</a> page in case we @ ever reuse the letter for another purpose. } /* If anonymous users are allowed to create new Wiki, then ** wiki moderation should be activated to pervent spam. */ if( hasAnyCap(zAnonCap, "fk") ){ if( db_get_boolean("modreq-wiki",0)==0 ){ @ <li><p><b>WARNING:</b> @ Anonymous users can create or edit wiki without moderation. @ This can result in robots inserting lots of wiki spam into @ repository. @ Fix this by removing the "New-Wiki" and "Write-Wiki" @ privileges from users "anonymous" and "nobody" on the @ <a href="setup_ulist">User Configuration</a> page or @ by enabling wiki moderation on the @ <a href="setup_modreq">Moderation Setup</a> page. }else{ @ <li><p> @ Anonymous users can create or edit wiki, but moderator @ approval is required before the edits become permanent. } } /* Anonymous users should not be able to create trusted forum ** posts. */ if( hasAnyCap(zAnonCap, "456") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can create forum posts that are @ accepted into the permanent record without moderation. @ This can result in robots generating spam on forum posts. @ Fix this by removing the "WriteTrusted-Forum" privilege @ (<a href="setup_ucap_list">capabilities</a> "456") from @ users "anonymous" and "nobody" on the @ <a href="setup_ulist">User Configuration</a> page or } /* Anonymous users should not be able to send announcements. */ if( hasAnyCap(zAnonCap, "A") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can send announcements to anybody who is signed @ up to receive announcements. This can result in spam. @ Fix this by removing the "Announce" privilege @ (<a href="setup_ucap_list">capability</a> "A") from @ users "anonymous" and "nobody" on the @ <a href="setup_ulist">User Configuration</a> page or } /* Administrative privilege should only be provided to ** specific individuals, not to entire classes of people. ** And not too many people should have administrator privilege. */ z = db_text(0, "SELECT group_concat(" "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," "' and ')" " FROM user" " WHERE cap GLOB '*[as]*'" " AND login in ('anonymous','nobody','reader','developer')" ); if( z && z[0] ){ @ <li><p><b>WARNING:</b> @ Administrative privilege ('a' or 's') @ is granted to an entire class of users: %s(z). @ Administrative privilege should only be @ granted to specific individuals. } n = db_int(0,"SELECT count(*) FROM user WHERE fullcap(cap) GLOB '*[as]*'"); if( n==0 ){ @ <li><p> @ No users have administrator privilege. }else{ z = db_text(0, "SELECT group_concat(" "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," "', ')" " FROM user" " WHERE fullcap(cap) GLOB '*[as]*'" ); @ <li><p> @ Users with administrator privilege are: %s(z) fossil_free(z); if( n>3 ){ @ <li><p><b>WARNING:</b> @ Administrator privilege is granted to @ <a href='setup_ulist?with=as'>%d(n) users</a>. @ Ideally, administrator privilege ('s' or 'a') should only @ be granted to one or two users. } } /* The push-unversioned privilege should only be provided to ** specific individuals, not to entire classes of people. ** And no too many people should have this privilege. */ z = db_text(0, "SELECT group_concat(" "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," "' and ')" " FROM user" " WHERE cap GLOB '*y*'" " AND login in ('anonymous','nobody','reader','developer')" ); if( z && z[0] ){ @ <li><p><b>WARNING:</b> @ The "Write-Unver" privilege is granted to an entire class of users: %s(z). @ The Write-Unver privilege should only be granted to specific individuals. fossil_free(z); } n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*y*'"); if( n>0 ){ z = db_text(0, "SELECT group_concat(" "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),', ')" " FROM user WHERE fullcap(cap) GLOB '*y*'" ); @ <li><p> @ Users with "Write-Unver" privilege: %s(z) fossil_free(z); if( n>3 ){ @ <p><b>Caution:</b> @ The "Write-Unver" privilege ('y') is granted to an excessive @ number of users (%d(n)). @ Ideally, the Write-Unver privilege should only @ be granted to one or two users. } } /* Providing hyperlink capability to user "nobody" can lead to robots ** making excessive requests resulting in DoS */ if( db_exists("SELECT 1 FROM user WHERE login='nobody' AND cap GLOB '*h*'") ){ int nobodyId = db_int(0,"SELECT uid FROM user WHERE login='nobody'"); int anonId = db_int(0, "SELECT uid FROM user WHERE login='anonymous' AND cap NOT GLOB '*h*'"); @ <li><p> @ User "nobody" has "Hyperlink" privilege ('h') which can lead to @ robots walking a nearly endless progression of pages on public-facing @ repositories, causing excessive server load and possible DoS. @ Suggested remediation: @ <ol type="a"> @ <li>Remove the 'h' privilege from the @ <a href="%R/setup_uedit?id=%d(nobodyId)">'nobody' user</a> so that @ robots cannot see hyperlinks. @ <li>Activate <a href="%R/setup_robot">autohyperlink</a> so that @ human readers can still see hyperlinks even if they are not logged in. @ Set the delay to at least 50 milliseconds and require a mouse @ event for maximum robot defense. if( anonId>0 ){ @ <li>Perhaps set the 'h' privilege on the @ <a href="%R/setup_uedit?id=%d(anonId)">'anonymous' user</a> so @ that humans that have javascript disabled in their browsers can @ still see hyperlinks if they will log in as "anonymous". } @ </ol> } /* Notify if REMOTE_USER or HTTP_AUTHENTICATION is used for login. */ if( db_get_boolean("remote_user_ok", 0) ){ @ <li><p><b>Caution:</b> @ This repository trusts that the REMOTE_USER environment variable set @ up by the webserver contains the name of an authenticated user. @ Fossil's built-in authentication mechanism is bypassed. @ Fix this by deactivating the "Allow REMOTE_USER authentication" @ checkbox on the <a href="setup_access">Access Control</a> page. } if( db_get_boolean("http_authentication_ok", 0) ){ @ <li><p><b>Caution:</b> @ This repository trusts that the HTTP_AUTHENITICATION environment @ variable set up by the webserver contains the name of an @ authenticated user. @ Fossil's built-in authentication mechanism is bypassed. @ Fix this by deactivating the "Allow HTTP_AUTHENTICATION authentication" @ checkbox on the <a href="setup_access">Access Control</a> page. } /* Logging should be turned on */ if( db_get_boolean("access-log",0)==0 ){ @ <li><p> @ The <a href="access_log">User Log</a> is disabled. The user log @ keeps a record of successful and unsuccessful login attempts and is @ useful for security monitoring. } if( db_get_boolean("admin-log",0)==0 ){ @ <li><p> @ The <a href="admin_log">Administrative Log</a> is disabled. @ The administrative log provides a record of configuration changes @ and is useful for security monitoring. } #if !defined(_WIN32) && !defined(FOSSIL_OMIT_LOAD_AVERAGE) /* Make sure that the load-average limiter is armed and working */ if( load_average()==0.0 ){ @ <li><p> @ Unable to get the system load average. This can prevent Fossil @ from throttling expensive operations during peak demand. @ If running in a chroot jail on Linux, verify that the /proc @ filesystem is mounted within the jail, so that the load average @ can be obtained from the /proc/loadavg file. }else { double r = atof(db_get("max-loadavg", 0)); if( r<=0.0 ){ @ <li><p> @ Load average limiting is turned off. This can cause the server @ to bog down if many requests for expensive services (such as @ large diffs or tarballs) arrive at about the same time. @ To fix this, set the @ <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on the @ <a href='%R/setup_access'>Access Control</a> page to the approximate @ the number of available cores on your server, or maybe just a little @ less. }else if( r>=8.0 ){ @ <li><p> @ The <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on @ the <a href="setup_access">Access Control</a> page is set to %g(r), @ which seems high. Is this server really a %d((int)r)-core machine? } } #endif if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ @ <li><p> @ The server error log is disabled. @ To set up an error log, if( fossil_strcmp(g.zCmdName, "cgi")==0 ){ @ make an entry like "errorlog: <i>FILENAME</i>" in the @ CGI script at %h(P("SCRIPT_FILENAME")). }else{ @ add the "--errorlog <i>FILENAME</i>" option to the @ "%h(g.argv[0]) %h(g.zCmdName)" command that launched this server. } }else{ FILE *pTest = fossil_fopen(g.zErrlog,"a"); if( pTest==0 ){ @ <li><p> @ <b>Error:</b> @ There is an error log at "%h(g.zErrlog)" but that file is not @ writable and so no logging will occur. }else{ fclose(pTest); @ <li><p> @ The error log at "<a href='%R/errorlog'>%h(g.zErrlog)</a>" is @ %,lld(file_size(g.zErrlog, ExtFILE)) bytes in size. } } if( g.zExtRoot ){ int nFile; int nCgi; ext_files(); nFile = db_int(0, "SELECT count(*) FROM sfile"); nCgi = nFile==0 ? 0 : db_int(0,"SELECT count(*) FROM sfile WHERE isexe"); @ <li><p> CGI Extensions are enabled with a document root @ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding @ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files. } if( fileedit_glob()!=0 ){ @ <li><p><a href='%R/fileedit'>Online File Editing</a> is enabled @ for this repository. Clear the @ <a href='%R/setup_settings'>"fileedit-glob" setting</a> to @ disable online editing.</p> } @ <li><p> User capability summary: capability_summary(); azCSP = parse_content_security_policy(); if( azCSP==0 ){ @ <li><p> WARNING: No Content Security Policy (CSP) is specified in the @ header. Though not required, a strong CSP is recommended. Fossil will @ automatically insert an appropriate CSP if you let it generate the @ HTML <tt><head></tt> element by omitting <tt><body></tt> @ from the header configuration in your customized skin. @ }else{ int ii; @ <li><p> Content Security Policy: @ <ol type="a"> for(ii=0; azCSP[ii]; ii++){ @ <li>%h(azCSP[ii]) } @ </ol> } fossil_free(azCSP); if( alert_enabled() ){ @ <li><p> Email alert configuration summary: @ <table class="label-value"> stats_for_email(); @ </table> }else{ @ <li><p> Email alerts are disabled } n = db_int(0,"SELECT count(*) FROM (" "SELECT rid FROM phantom EXCEPT SELECT rid FROM private)"); if( n>0 ){ @ <li><p>\ @ There exists public phantom artifacts in this repository, shown below. @ Phantom artifacts are artifacts whose hash name is referenced by some @ other artifact but whose content is unknown. Some phantoms are marked @ private and those are ignored. But public phantoms cause unnecessary @ sync traffic and might represent malicious attempts to corrupt the @ repository structure. @ </p><p> @ To suppress unnecessary sync traffic caused by phantoms, add the RID @ of each phantom to the "private" table. Example: @ <blockquote><pre> @ INSERT INTO private SELECT rid FROM blob WHERE content IS NULL; @ </pre></blockquote> @ </p> table_of_public_phantoms(); @ </li> } blob_init(&cmd, 0, 0); for(i=0; g.argvOrig[i]!=0; i++){ blob_append_escaped_arg(&cmd, g.argvOrig[i], 0); } @ <li><p> if( g.zCgiFile ){ Blob fullname; blob_init(&fullname, 0, 0); file_canonical_name(g.zCgiFile, &fullname, 0); @ The CGI control file for this page is "%h(blob_str(&fullname))". } @ The command that generated this page: @ <blockquote> @ <tt>%h(blob_str(&cmd))</tt> @ </blockquote></li> blob_zero(&cmd); @ </ol> style_finish_page(); } /* ** WEBPAGE: takeitprivate ** ** Disable anonymous access to this website */ void takeitprivate_page(void){ login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } if( P("cancel") ){ /* User pressed the cancel button. Go back */ cgi_redirect("secaudit0"); } if( P("apply") ){ db_unprotect(PROTECT_ALL); db_multi_exec( "UPDATE user SET cap=''" " WHERE login IN ('nobody','anonymous');" "DELETE FROM config WHERE name='public-pages';" ); db_set("self-register","0",0); db_protect_pop(); cgi_redirect("secaudit0"); } style_header("Make This Website Private"); @ <p>Click the "Make It Private" button below to disable all @ anonymous access to this repository. A valid login and password @ will be required to access this repository after clicking that @ button.</p> @ @ <p>Click the "Cancel" button to leave things as they are.</p> @ @ <form action="%s(g.zPath)" method="post"> @ <input type="submit" name="apply" value="Make It Private"> @ <input type="submit" name="cancel" value="Cancel"> @ </form> style_finish_page(); } /* ** Output a message explaining that no error log is available. */ static void no_error_log_available(void){ @ <p>No error log is configured. if( g.zCgiFile==0 ){ @ To create an error log, add the "--errorlog FILENAME" @ command-line option to the command that launches the Fossil server. }else{ Blob fullname; blob_init(&fullname, 0, 0); file_canonical_name(g.zCgiFile, &fullname, 0); @ To create an error log, edit the CGI control file @ named "%h(blob_str(&fullname))" to add a line like this: @ <blockquote><pre> @ errorlog: <i>FILENAME</i> @ </pre></blockquote> blob_reset(&fullname); } } /* ** The maximum number of bytes of the error log to show by default. */ #define MXSHOWLOG 500000 /* ** WEBPAGE: errorlog ** ** Show the content of the error log. Only the administrator can view ** this page. */ void errorlog_page(void){ i64 szFile; FILE *in; char *zLog; char z[10000]; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_header("Server Error Log"); style_submenu_element("Test", "%R/test-warning"); style_submenu_element("Refresh", "%R/errorlog"); style_submenu_element("Log-Menu", "%R/setup-logmenu"); style_submenu_element("Panics", "%R/paniclog"); if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ no_error_log_available(); style_finish_page(); return; } if( P("truncate1") && cgi_csrf_safe(2) ){ fclose(fopen(g.zErrlog,"w")); } if( P("download") ){ Blob log; blob_read_from_file(&log, g.zErrlog, ExtFILE); cgi_set_content_type("text/plain"); cgi_set_content(&log); return; } szFile = file_size(g.zErrlog, ExtFILE); if( P("truncate") ){ @ <form action="%R/errorlog" method="POST"> login_insert_csrf_secret(); @ <p>Confirm that you want to truncate the %,lld(szFile)-byte error log: @ <input type="submit" name="truncate1" value="Confirm"> @ <input type="submit" name="cancel" value="Cancel"> @ </form> style_finish_page(); return; } zLog = file_canonical_name_dup(g.zErrlog); @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size. fossil_free(zLog); style_submenu_element("Download", "%R/errorlog?download"); style_submenu_element("Truncate", "%R/errorlog?truncate"); in = fossil_fopen(g.zErrlog, "rb"); if( in==0 ){ @ <p class='generalError'>Unable to open that file for reading!</p> style_finish_page(); return; } if( szFile>MXSHOWLOG && P("all")==0 ){ @ <form action="%R/errorlog" method="POST"> @ <p>Only the last %,d(MXSHOWLOG) bytes are shown. @ <input type="submit" name="all" value="Show All"> @ </form> fseek(in, -MXSHOWLOG, SEEK_END); } @ <hr> @ <pre> while( fgets(z, sizeof(z), in) ){ @ %h(z)\ } fclose(in); @ </pre> style_finish_page(); } /* ** WEBPAGE: paniclog ** ** Scan the error log for panics. Show all panic messages, ignoring all ** other error log entries. */ void paniclog_page(void){ i64 szFile; char *zLog; FILE *in; int bOutput = 0; int prevWasTime = 0; char z[10000]; char zTime[10000]; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_header("Server Panic Log"); style_submenu_element("Log-Menu", "%R/setup-logmenu"); if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ no_error_log_available(); style_finish_page(); return; } in = fossil_fopen(g.zErrlog, "rb"); if( in==0 ){ @ <p class='generalError'>Unable to open that file for reading!</p> style_finish_page(); return; } szFile = file_size(g.zErrlog, ExtFILE); zLog = file_canonical_name_dup(g.zErrlog); @ Panic messages contained within the %lld(szFile)-byte @ <a href="%R/errorlog?all">error log</a> found at @ "%h(zLog)". fossil_free(zLog); @ <hr> @ <pre> while( fgets(z, sizeof(z), in) ){ if( prevWasTime && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){ @ %h(zTime)\ bOutput = 1; } if( strncmp(z, "--------", 8)==0 ){ size_t n = strlen(z); memcpy(zTime, z, n+1); prevWasTime = 1; bOutput = 0; }else{ prevWasTime = 0; } if( bOutput ){ @ %h(z)\ } } fclose(in); @ </pre> style_finish_page(); } |
Changes to src/setup.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2007 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".) | | > | | > > > > > > > > > > > > | > > > | | > > | > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > | | | < > > | | > > > | | > | | | | < < | < < | < < < < < < < < | < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < | < < > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | | | < < < < < < < < < < < < | | < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < | < < | < > > > > | | < < | < < < < > < < < < < < < < < < < < < < < < < < < < < < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | | | < | < < | > > | < | < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > | < > < < < < < < | > > > > > > > > > > < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < | > > > > | < < < < < < < < < < | < < < < < | < | < < < < > | < < < < < < < < | < | < | | | > | | | | | > > > > > > | > | < > > | | > | | > > | > > > > | > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > | < > > | > > > > < | > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | > > > > > > > > > > > > > | | | | > > > > > > | > > > | > > > > > > > > > > > > > > > > | | > > > > > > > > > | > | | > > > > > > > > > | > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | > | | > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > | > > | | | > | > > > > > > | < > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | > | | | | > > | | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < | > | | | | > > | > > > > > > > | | > > | > > < < < < < < < < < < < < < | > | > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > > | < > > > | > > > < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > | > | > > | | | > < < < < < < | > | > | < > > > > > > > | > > > > > > > > > > > > > | | | | < | > | | > | > | > > > > | > > | < | < > | > > > > > > > | > | < > | < < | > > | | < > | | | | > | > | < > > > > > > > > > > > | | | | < < | > > | < | > > | < | | | > > > | > > > > > > > | > > > > > > > > | > > > | | > > > | | > > > | | > > > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | > | > > > | | | | > > > > | | < < < | < | > | > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > | | < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Copyright (c) 2007 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/ ** ******************************************************************************* ** ** Implementation of the Setup page */ #include "config.h" #include <assert.h> #include "setup.h" /* ** Increment the "cfgcnt" variable, so that ETags will know that ** the configuration has changed. */ void setup_incr_cfgcnt(void){ static int once = 1; if( once ){ once = 0; db_unprotect(PROTECT_CONFIG); db_multi_exec("UPDATE config SET value=value+1 WHERE name='cfgcnt'"); if( db_changes()==0 ){ db_multi_exec("INSERT INTO config(name,value) VALUES('cfgcnt',1)"); } db_protect_pop(); } } /* ** Output a single entry for a menu generated using an HTML table. ** If zLink is not NULL or an empty string, then it is the page that ** the menu entry will hyperlink to. If zLink is NULL or "", then ** the menu entry has no hyperlink - it is disabled. */ void setup_menu_entry( const char *zTitle, const char *zLink, const char *zDesc ){ @ <tr><td valign="top" align="right"> if( zLink && zLink[0] ){ @ <a href="%s(zLink)"><nobr>%h(zTitle)</nobr></a> }else{ @ %h(zTitle) } @ </td><td width="5"></td><td valign="top">%h(zDesc)</td></tr> } /* ** WEBPAGE: setup ** ** Main menu for the administrative pages. Requires Admin or Setup ** privileges. Links to sub-pages only usable by Setup users are ** shown only to Setup users. */ void setup_page(void){ int setup_user = 0; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); } setup_user = g.perm.Setup; style_set_current_feature("setup"); 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."); if( setup_user ){ 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("Security-Audit", "secaudit0", "Analyze the current configuration for security problems"); if( setup_user ){ setup_menu_entry("Robot-Defense", "setup_robot", "Settings for configure defense against robots"); setup_menu_entry("Settings", "setup_settings", "Web interface to the \"fossil settings\" command"); } setup_menu_entry("Timeline", "setup_timeline", "Timeline display preferences"); if( setup_user ){ 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("Wiki", "setup_wiki", "Configure the wiki for this repository"); setup_menu_entry("Interwiki Map", "intermap", "Mapping keywords for interwiki links"); setup_menu_entry("Chat", "setup_chat", "Configure the chatroom"); setup_menu_entry("Forum", "setup_forum", "Forum config and metrics"); } setup_menu_entry("Search","srchsetup", "Configure the built-in search engine"); setup_menu_entry("URL Aliases", "waliassetup", "Configure URL aliases"); if( setup_user ){ setup_menu_entry("Notification", "setup_notification", "Automatic notifications of changes via outbound email"); setup_menu_entry("Transfers", "xfersetup", "Configure the transfer system for this repository"); } setup_menu_entry("Skins", "setup_skin_admin", "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("URLs & Checkouts", "urllist", "Show URLs used to access this repo and known check-outs"); if( setup_user ){ setup_menu_entry("Web-Cache", "cachestat", "View the status of the expensive-page cache"); } 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("Log Files", "setup-logmenu", "A menu of available log files"); setup_menu_entry("Unversioned Files", "uvlist?byage=1", "Show all unversioned files held"); setup_menu_entry("Stats", "stat", "Repository Status Reports"); setup_menu_entry("Sitemap", "sitemap", "Links to miscellaneous pages"); if( setup_user ){ setup_menu_entry("SQL", "admin_sql", "Enter raw SQL commands"); setup_menu_entry("TH1", "admin_th1", "Enter raw TH1 commands"); } @ </table> style_finish_page(); } /* ** WEBPAGE: setup-logmenu ** ** Show a menu of available log renderings accessible to an administrator, ** together with a succinct explanation of each. ** ** This page is only accessible by administrators. */ void setup_logmenu_page(void){ Blob desc; blob_init(&desc, 0, 0); /* Administrator access only */ login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_header("Log Menu"); @ <table border="0" cellspacing="3"> setup_menu_entry("Admin Log", "admin_log", "The admin log records configuration changes to the repository.\n" "The admin log is stored in the \"admin_log\" table of the repository.\n" ); setup_menu_entry("Artifact Log", "rcvfromlist", "The artifact log records when new content is added to the repository.\n" "The time and date and origin of the new content is entered into the\n" "Log. The artifact log is always on and is stored in the \"rcvfrom\"\n" "table of the repository.\n" ); blob_appendf(&desc, "The error log is a separate text file to which warning and error\n" "messages are appended. A single error log can and often is shared\n" "across multiple repositories.\n" ); if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ blob_appendf(&desc,"The error log is disabled for this repository."); }else{ blob_appendf(&desc,"In this repository, the error log is in the file" "named \"%s\".", g.zErrlog); } setup_menu_entry("Error Log", "errorlog", blob_str(&desc)); blob_reset(&desc); setup_menu_entry("Panic Log", "paniclog", "The panic log is a filtering of the Error Log that shows only the\n" "most important messages - assertion faults, segmentation faults, and\n" "similar malfunctions." ); setup_menu_entry("User Log", "user_log", "The user log is a record of login attempts. The user log is stored\n" "in the \"accesslog\" table of the respository.\n" ); @ </table> style_finish_page(); } /* ** Generate a checkbox for an attribute. */ void onoff_attribute( const char *zLabel, /* The text label on the checkbox */ const char *zVar, /* The corresponding row in the CONFIG table */ const char *zQParm, /* The query parameter */ int dfltVal, /* Default value if CONFIG table entry does not exist */ int disabled /* 1 if disabled */ ){ const char *zQ = P(zQParm); int iVal = db_get_boolean(zVar, dfltVal); if( zQ==0 && !disabled && P("submit") ){ zQ = "off"; } if( zQ ){ int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ); if( iQ!=iVal && cgi_csrf_safe(2) ){ db_protect_only(PROTECT_NONE); db_set(zVar/*works-like:"x"*/, iQ ? "1" : "0", 0); db_protect_pop(); setup_incr_cfgcnt(); admin_log("Set option [%q] to [%q].", zVar, iQ ? "on" : "off"); iVal = iQ; } } @ <label><input type="checkbox" name="%s(zQParm)" \ @ aria-label="%h(zLabel[0]?zLabel:zQParm)" \ if( iVal ){ @ checked="checked" \ } if( disabled ){ @ disabled="disabled" \ } @ > <b>%s(zLabel)</b></label> } /* ** Generate an entry box for an attribute. */ void entry_attribute( const char *zLabel, /* The text label on the entry box */ int width, /* Width of the entry box */ const char *zVar, /* The corresponding row in the CONFIG table */ const char *zQParm, /* The query parameter */ const char *zDflt, /* Default value if CONFIG 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 && cgi_csrf_safe(2) ){ const int nZQ = (int)strlen(zQ); setup_incr_cfgcnt(); db_protect_only(PROTECT_NONE); db_set(zVar/*works-like:"x"*/, zQ, 0); db_protect_pop(); admin_log("Set entry_attribute %Q to: %.*s%s", zVar, 20, zQ, (nZQ>20 ? "..." : "")); zVal = zQ; } @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" 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 CONFIG table */ const char *zQP, /* The query parameter */ const char *zDflt, /* Default value if CONFIG table entry does not exist */ int disabled /* 1 if the textarea should not be editable */ ){ const char *z = db_get(zVar, zDflt); const char *zQ = P(zQP); if( zQ && !disabled && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){ const int nZQ = (int)strlen(zQ); db_protect_only(PROTECT_NONE); db_set(zVar/*works-like:"x"*/, zQ, 0); db_protect_pop(); setup_incr_cfgcnt(); 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)" \ @ aria-label="%h(zLabel[0]?zLabel:zQP)" \ if( disabled ){ @ disabled="disabled" \ } @ cols="%d(cols)">%h(z)</textarea> if( *zLabel ){ @ <span class="textareaLabel">%s(zLabel)</span> } } return z; } /* ** Generate a text box for an attribute. */ void multiple_choice_attribute( const char *zLabel, /* The text label on the menu */ const char *zVar, /* The corresponding row in the CONFIG table */ const char *zQP, /* The query parameter */ const char *zDflt, /* Default value if CONFIG table entry does not exist */ int nChoice, /* Number of choices */ const char *const *azChoice /* Choices in pairs (VAR value, Display) */ ){ const char *z = db_get(zVar, zDflt); const char *zQ = P(zQP); int i; if( zQ && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){ const int nZQ = (int)strlen(zQ); db_unprotect(PROTECT_ALL); db_set(zVar/*works-like:"x"*/, zQ, 0); setup_incr_cfgcnt(); db_protect_pop(); admin_log("Set multiple_choice_attribute %Q to: %.*s%s", zVar, 20, zQ, (nZQ>20 ? "..." : "")); z = zQ; } @ <select aria-label="%h(zLabel)" 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> } /* ** Insert code into the current page that allows the user to configure ** auto-hyperlink related robot defense settings. */ static void addAutoHyperlinkSettings(void){ static const char *const azDefenseOpts[] = { "0", "Off", "2", "UserAgent only", "1", "UserAgent and Javascript", }; multiple_choice_attribute( "Enable hyperlinks base on User-Agent and/or Javascript", "auto-hyperlink", "autohyperlink", "1", count(azDefenseOpts)/2, azDefenseOpts); @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users, @ including user "nobody", as long as the User-Agent string in the @ HTTP header indicates that the request is coming from an actual human @ being. If this setting is "UserAgent only" (2) then the @ UserAgent string is the only factor considered. If the value of this @ setting is "UserAgent And Javascript" (1) then Javascript is added that @ runs after the page loads and fills in the href= values of <a> @ elements. In either case, <a> tags are only generated if the @ UserAgent string indicates that the request is coming from a human and @ not a robot. @ @ <p>This setting is designed to give easy access to humans while @ keeping out robots. @ You do not normally want a robot 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>The "UserAgent and Javascript" value for this setting provides @ superior protection from robots. However, that setting also prevents @ the visited/unvisited colors on hyperlinks from displaying correctly @ on Safari-derived browsers. (Chrome and Firefox work fine.) Since @ Safari is the underlying rendering engine on all iPhones and iPads, @ this means that hyperlink visited/unvisited colors will not operate @ on those platforms when "UserAgent and Javascript" is selected.</p> @ @ <p>Additional parameters that control the behavior of Javascript:</p> @ <blockquote> entry_attribute("Delay in milliseconds before enabling hyperlinks", 5, "auto-hyperlink-delay", "ah-delay", "50", 0); @ <br> onoff_attribute("Also require a mouse event before enabling hyperlinks", "auto-hyperlink-mouseover", "ahmo", 0, 0); @ </blockquote> @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds @ and "require a mouse event" should be turned on. These values only come @ into play when the main auto-hyperlink settings is 2 ("UserAgent and @ Javascript").</p> @ @ <p>To see if Javascript-base hyperlink enabling mechanism is working, @ visit the <a href="%R/test_env">/test_env</a> page (from a separate @ web browser that is not logged in, even as "anonymous") and verify @ that the "g.jsHref" value is "1".</p> @ <p>(Properties: "auto-hyperlink", "auto-hyperlink-delay", and @ "auto-hyperlink-mouseover"")</p> } /* ** WEBPAGE: setup_robot ** ** Settings associated with defense against robots. Requires setup privilege. */ void setup_robots(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_set_current_feature("setup"); style_header("Robot Defense Settings"); db_begin_transaction(); @ <p>A Fossil website can have billions of pages in its tree, even for a @ modest project. Many of those pages (examples: diffs and tarballs) @ might be expensive to compute. A robot that tries to walk the entire @ website can present a crippling CPU and bandwidth load. @ @ <p>The settings on this page are intended to help site administrators @ defend the site against robots. @ @ <form action="%R/setup_robot" method="post"><div> login_insert_csrf_secret(); @ <input type="submit" name="submit" value="Apply Changes"></p> @ <hr> addAutoHyperlinkSettings(); @ <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. @ (Property: "max-loadavg")</p> @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); style_finish_page(); } /* ** WEBPAGE: setup_access ** ** The access-control settings page. Requires Setup privileges. */ void setup_access(void){ static const char *const azRedirectOpts[] = { "0", "Off", "1", "Login Page Only", "2", "All Pages" }; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_set_current_feature("setup"); style_header("Access Control Settings"); db_begin_transaction(); @ <form action="%R/setup_access" method="post"><div> login_insert_csrf_secret(); @ <input type="submit" name="submit" value="Apply Changes"></p> @ <hr> multiple_choice_attribute("Redirect to HTTPS", "redirect-to-https", "redirhttps", "0", count(azRedirectOpts)/2, azRedirectOpts); @ <p>Force the use of HTTPS by redirecting to HTTPS when an @ unencrypted request is received. This feature can be enabled @ for the Login page only, or for all pages. @ <p>Further 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. @ (Property: "redirect-to-https". "0" for off, "1" for Login page only, @ "2" otherwise.) @ <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> @ (Property: "localauth") @ @ <hr> onoff_attribute("Enable /test_env", "test_env_enable", "test_env_enable", 0, 0); @ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all @ users. When disabled (the default) only users Admin and Setup can visit @ the /test_env page. @ (Property: "test_env_enable") @ </p> @ @ <hr> onoff_attribute("Enable /artifact_stats", "artifact_stats_enable", "artifact_stats_enable", 0, 0); @ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all @ users. When disabled (the default) only users with check-in privilege may @ access the /artifact_stats page. @ (Property: "artifact_stats_enable") @ </p> @ @ <hr> onoff_attribute("Allow REMOTE_USER authentication", "remote_user_ok", "remote_user_ok", 0, 0); @ <p>When enabled, if the REMOTE_USER environment variable is set to the @ login name of a valid user and no other login credentials are available, @ then the REMOTE_USER is accepted as an authenticated user. @ (Property: "remote_user_ok") @ </p> @ @ <hr> onoff_attribute("Allow HTTP_AUTHENTICATION authentication", "http_authentication_ok", "http_authentication_ok", 0, 0); @ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment @ variable or the "Authentication:" HTTP header to find the username and @ password. This is another way of supporting Basic Authentication. @ (Property: "http_authentication_ok") @ </p> @ @ <hr> entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766", 0); @ <p>The number of hours for which a login is valid. This must be a @ positive number. The default is 8766 hours which is approximately equal @ to a year. @ (Property: "cookie-expire")</p> @ <hr> entry_attribute("Download packet limit", 10, "max-download", "mxdwn", "5000000", 0); @ <p>Fossil tries to limit out-bound sync, clone, and pull packets @ to this many bytes, uncompressed. If the client requires more data @ than this, then the client will issue multiple HTTP requests. @ Values below 1 million are not recommended. 5 million is a @ reasonable number. (Property: "max-download")</p> @ <hr> entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", "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. @ (Property: "max-download-time")</p> @ <a id="slal"></a> @ <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. @ (Property: "max-loadavg")</p> /* Add the auto-hyperlink settings controls. These same controls ** are also accessible from the /setup_robot page. */ @ <hr> addAutoHyperlinkSettings(); @ <hr> onoff_attribute("Require a CAPTCHA if not logged in", "require-captcha", "reqcapt", 1, 0); @ <p>Require a CAPTCHA for edit operations (appending, creating, or @ editing wiki or tickets or adding attachments to wiki or tickets) @ for users who are not logged in. (Property: "require-captcha")</p> @ <hr> entry_attribute("Public pages", 30, "public-pages", "pubpage", "", 0); @ <p>A comma-separated list of glob patterns for pages that are accessible @ without needing a login and using the privileges given by the @ "Default privileges" setting below. @ @ <p>Example use case: Set this field to "/doc/trunk/www/*" and set @ the "Default privileges" to include the "o" privilege @ to give anonymous users read-only permission to the @ latest version of the embedded documentation in the www/ folder without @ allowing them to see the rest of the source code. @ (Property: "public-pages") @ </p> @ <hr> onoff_attribute("Allow users to register themselves", "self-register", "selfreg", 0, 0); @ <p>Allow users to register themselves on the /register webpage. @ A self-registration creates a new entry in the USER table and @ perhaps also in the SUBSCRIBER table if email notification is @ enabled. @ (Property: "self-register")</p> @ <hr> onoff_attribute("Allow users to reset their own passwords", "self-pw-reset", "selfpw", 0, 0); @ <p>Allow users to request that an email contains a hyperlink to a @ password reset page be sent to their email address of record. This @ enables forgetful users to recover their forgotten passwords without @ administrator intervention. @ (Property: "self-pw-reset")</p> @ <hr> onoff_attribute("Email verification required for self-registration", "selfreg-verify", "sfverify", 0, 0); @ <p>If enabled, self-registration creates a new entry in the USER table @ with only capabilities "7". The default user capabilities are not @ added until the email address associated with the self-registration @ has been verified. This setting only makes sense if @ email notifications are enabled. @ (Property: "selfreg-verify")</p> @ <hr> onoff_attribute("Allow anonymous subscriptions", "anon-subscribe", "anonsub", 1, 0); @ <p>If disabled, email notification subscriptions are only allowed @ for users with a login. If Nobody or Anonymous visit the /subscribe @ page, they are redirected to /register or /login. @ (Property: "anon-subscribe")</p> @ <hr> entry_attribute("Authorized subscription email addresses", 35, "auth-sub-email", "asemail", "", 0); @ <p>This is a comma-separated list of GLOB patterns that specify @ email addresses that are authorized to subscriptions. If blank @ (the usual case), then any email address can be used to self-register. @ This setting is used to limit subscriptions to members of a particular @ organization or group based on their email address. @ (Property: "auth-sub-email")</p> @ <hr> entry_attribute("Default privileges", 10, "default-perms", "defaultperms", "u", 0); @ <p>Permissions given to users that... <ul><li>register themselves using @ the self-registration procedure (if enabled), or <li>access "public" @ pages identified by the public-pages glob pattern above, or <li> @ are users newly created by the administrator.</ul> @ <p>Recommended value: "u" for Reader. @ <a href="%R/setup_ucap_list">Capability Key</a>. @ (Property: "default-perms") @ </p> @ <hr> onoff_attribute("Show javascript button to fill in CAPTCHA", "auto-captcha", "autocaptcha", 0, 0); @ <p>When enabled, a button appears on the login screen for user @ "anonymous" that will automatically fill in the CAPTCHA password. @ This is less secure than forcing the user to do it manually, but is @ probably secure enough and it is certainly more convenient for @ anonymous users. (Property: "auto-captcha")</p> @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); style_finish_page(); } /* ** WEBPAGE: setup_login_group ** ** Change how the current repository participates in a login ** group. */ void setup_login_group(void){ const char *zGroup; char *zErrMsg = 0; Blob fullName; char *zSelfRepo; 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, 1, zLogin, zPw, zNewName, &zErrMsg); }else if( P("leave") ){ login_group_leave(&zErrMsg); } style_set_current_feature("setup"); style_header("Login Group Configuration"); if( zErrMsg ){ @ <p class="generalError">%s(zErrMsg)</p> } zGroup = login_group_name(); if( zGroup==0 ){ @ <p>This repository (in the file named "%h(zSelfRepo)") @ is not currently part of any login-group. @ To join a login group, fill out the form below.</p> @ @ <form action="%R/setup_login_group" method="post"><div> login_insert_csrf_secret(); @ <blockquote><table border="0"> @ @ <tr><th align="right" id="rfigtj">Repository filename \ @ in group to join:</th> @ <td width="5"></td><td> @ <input aria-labelledby="rfigtj" type="text" size="50" \ @ value="%h(zRepo)" name="repo"></td></tr> @ @ <tr><th align="right" id="lotar">Login on the above repo:</th> @ <td width="5"></td><td> @ <input aria-labelledby="lotar" type="text" size="20" \ @ value="%h(zLogin)" name="login"></td></tr> @ @ <tr><th align="right" id="lgpw">Password:</th> @ <td width="5"></td><td> @ <input aria-labelledby="lgpw" type="password" size="20" name="pw">\ @ </td></tr> @ @ <tr><th align="right" id="nolg">Name of login-group:</th> @ <td width="5"></td><td> @ <input aria-labelledby="nolg" type="text" size="30" \ @ value="%h(zNewName)" name="newname"> @ (only used if creating a new login-group).</td></tr> @ @ <tr><td colspan="3" align="center"> @ <input type="submit" value="Join" name="join"></td></tr> @ </table></blockquote></div></form> }else{ Stmt q; int n = 0; @ <p>This repository (in the file "%h(zSelfRepo)") @ is currently part of the "<b>%h(zGroup)</b>" login group. @ Other repositories in that group are:</p> @ <table border="0" cellspacing="4"> @ <tr><td colspan="2"><th align="left">Project Name<td> @ <th align="left">Repository File</tr> db_prepare(&q, "SELECT value," " (SELECT value FROM config" " WHERE name=('peer-name-' || substr(x.name,11)))" " FROM config AS x" " WHERE name GLOB 'peer-repo-*'" " ORDER BY value" ); while( db_step(&q)==SQLITE_ROW ){ const char *zRepo = db_column_text(&q, 0); const char *zTitle = db_column_text(&q, 1); n++; @ <tr><td align="right">%d(n).</td><td width="4"> @ <td>%h(zTitle)<td width="10"><td>%h(zRepo)</tr> } db_finalize(&q); @ </table> @ @ <p><form action="%R/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' class='sortable' data-column-types='ttt' \ @ data-init-sort='1'> @ <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-*'" " OR name GLOB 'login-group-*'" " 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> style_table_sorter(); } style_finish_page(); } /* ** WEBPAGE: setup_timeline ** ** Edit administrative settings controlling the display of ** timelines. */ 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.Admin ){ login_needed(0); return; } style_set_current_feature("setup"); style_header("Timeline Display Preferences"); db_begin_transaction(); @ <form action="%R/setup_timeline" method="post"><div> login_insert_csrf_secret(); @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ <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 such as paragraphs, tables, etc. @ (Property: "timeline-block-markup")</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. Use CSS to change @ display formatting features such as fonts and line-wrapping behavior. @ (Property: "timeline-plaintext")</p> @ <hr> onoff_attribute("Truncate comment at first blank line (Git-style)", "timeline-truncate-at-blank", "ttb", 0, 0); @ <p>In timeline displays, check-in comments are displayed only through @ the first blank line. This is the traditional way to display comments @ in Git repositories (Property: "timeline-truncate-at-blank")</p> @ <hr> onoff_attribute("Break comments at newline characters", "timeline-hard-newlines", "thnl", 0, 0); @ <p>In timeline displays, newline characters in check-in comments force @ a line break on the display. @ (Property: "timeline-hard-newlines")</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> } @ <p>(Property: "timeline-utc") @ <hr> multiple_choice_attribute("Style", "timeline-default-style", "tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles); @ <p>The default timeline viewing style, for when the user has not @ specified an alternative. (Property: "timeline-default-style")</p> @ <hr> entry_attribute("Default Number Of Rows", 6, "timeline-default-length", "tldl", "50", 0); @ <p>The maximum number of rows to show on a timeline in the absence @ of a user display preference cookie setting or an explicit n= query @ parameter. (Property: "timeline-default-length")</p> @ <hr> multiple_choice_attribute("Per-Item Time Format", "timeline-date-format", "tdf", "0", count(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". (Property: "timeline-date-format")</p> @ <hr> entry_attribute("Max timeline comment length", 6, "timeline-max-comment", "tmc", "0", 0); @ <p>The maximum length of a comment to be displayed in a timeline. @ "0" there is no length limit. @ (Property: "timeline-max-comment")</p> @ <hr> entry_attribute("Tooltip dwell time (milliseconds)", 6, "timeline-dwelltime", "tdt", "100", 0); @ <br> entry_attribute("Tooltip close time (milliseconds)", 6, "timeline-closetime", "tct", "250", 0); @ <p>The <strong>dwell time</strong> defines how long the mouse pointer @ should be stationary above an object of the graph before a tooltip @ appears.<br> @ The <strong>close time</strong> defines how long the mouse pointer @ can be away from an object before a tooltip is closed.</p> @ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br> @ Set <strong>close time</strong> to "0" to keep tooltips visible until @ the mouse is clicked elsewhere.<p> @ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p> @ <hr> onoff_attribute("Timestamp hyperlinks to /info", "timeline-tslink-info", "ttlti", 0, 0); @ <p>The hyperlink on the timestamp associated with each timeline entry, @ on the far left-hand side of the screen, normally targets another @ /timeline page that shows the entry in context. However, if this @ option is turned on, that hyperlink targets the /info page showing @ the details of the entry. @ <p>The /timeline link is the default since it is often useful to @ see an entry in context, and because that link is not otherwise @ accessible on the timeline. The /info link is also accessible by @ double-clicking the timeline node or by clicking on the hash that @ follows "check-in:" in the supplemental information section on the @ right of the entry. @ <p>(Properties: "timeline-tslink-info") @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); style_finish_page(); } /* ** WEBPAGE: setup_settings ** ** Change or view miscellaneous settings. Part of the ** /setup pages requiring Setup privileges. */ void setup_settings(void){ int nSetting; int i; Setting const *pSet; const Setting *aSetting = setting_info(&nSetting); login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_set_current_feature("setup"); 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>Settings marked with (v) are "versionable" and will be overridden @ by the contents of managed files named @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". @ If the file for a versionable setting exists, the value cannot be @ changed on this screen.</p><hr><p> @ @ <form action="%R/setup_settings" method="post"><div> @ <table border="0"><tr><td valign="top"> login_insert_csrf_secret(); for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ if( pSet->width==0 ){ int hasVersionableValue = pSet->versionable && (db_get_versioned(pSet->name, NULL)!=0); onoff_attribute("", pSet->name, pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, is_truth(pSet->def), hasVersionableValue); @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> 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"> @ <table> for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ if( pSet->width>0 && !pSet->forceTextArea ){ int hasVersionableValue = pSet->versionable && (db_get_versioned(pSet->name, NULL)!=0); @ <tr><td> @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> if( pSet->versionable ){ @ (v) } else { @ } @</td><td> entry_attribute("", /*pSet->width*/ 25, pSet->name, pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, (char*)pSet->def, hasVersionableValue); @</td></tr> } } @</table> @ </td><td style="width:50px;"></td><td valign="top"> for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ if( pSet->width>0 && pSet->forceTextArea ){ int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> if( pSet->versionable ){ @ (v)<br> } else { @ <br> } textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name, pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, (char*)pSet->def, hasVersionableValue); @<br> } } @ </td></tr></table> @ </div></form> db_end_transaction(0); style_finish_page(); } /* ** SETTING: mainmenu width=70 block-text keep-empty ** ** The mainmenu setting specifies the entries on the main menu ** for many skins. The mainmenu should be a TCL list. Each set ** of four consecutive values defines a single main menu item: ** ** * The first term is text that appears on the menu. ** ** * The second term is a hyperlink to take when a user clicks on the ** entry. Hyperlinks that start with "/" are relative to the ** repository root. ** ** * The third term is an argument to the TH1 "capexpr" command. ** If capexpr evaluates to true, then the entry is shown. If not, ** the entry is omitted. "*" is always true. "{}" is never true. ** ** * The fourth term is a list of extra class names to apply to the ** new menu entry. Some skins use classes "desktoponly" and ** "wideonly" to only show the entries when the web browser ** screen is wide or very wide, respectively. ** ** Some custom skins might not use this property. Whether the property ** is used or not a choice made by the skin designer. Some skins may add ** extra choices (such as the hamburger button) to the menu. */ /* ** SETTING: sitemap-extra width=70 block-text ** ** The sitemap-extra setting defines extra links to appear on the ** /sitemap web page as sub-items of the "Home Page" entry before the ** "Documentation Search" entry (if any). For skins that use the /sitemap ** page to construct a hamburger menu dropdown, new entries added here ** will appear on the hamburger menu. ** ** This setting should be a TCL list divided into triples. Each ** triple defines a new entry: ** ** * The first term is the display name of the /sitemap entry ** ** * The second term is a hyperlink to take when a user clicks on the ** entry. Hyperlinks that start with "/" are relative to the ** repository root. ** ** * The third term is an argument to the TH1 "capexpr" command. ** If capexpr evaluates to true, then the entry is shown. If not, ** the entry is omitted. "*" is always true. ** ** The default value is blank, meaning no added entries. */ /* ** WEBPAGE: setup_config ** ** The "Admin/Configuration" page. Requires Setup privilege. */ void setup_config(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_set_current_feature("setup"); style_header("WWW Configuration"); db_begin_transaction(); @ <form action="%R/setup_config" method="post"><div> login_insert_csrf_secret(); @ <input type="submit" name="submit" value="Apply Changes"></p> @ <hr> entry_attribute("Project Name", 60, "project-name", "pn", "", 0); @ <p>A brief project name so visitors know what this site is about. @ The project name will also be used as the RSS feed title. @ (Property: "project-name") @ </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. @ (Property: "project-description")</p> @ <hr> entry_attribute("Canonical Server URL", 40, "email-url", "eurl", "", 0); @ <p>This is the URL used to access this repository as a server. @ Other repositories use this URL to clone or sync against this repository. @ This is also the basename for hyperlinks included in email alert text. @ Omit the trailing "/". @ If this repo will not be set up as a persistent server and will not @ be sending email alerts, then leave this entry blank. @ Suggested value: "%h(g.zBaseURL)" @ (Property: "email-url")</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. @ (Property: "short-project-name") @ </p> @ <hr> entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0); @ <p>The <a href='%R/download'>/download</a> page is designed to provide @ a convenient place for newbies @ to download a ZIP archive or a tarball of the project. By default, @ the latest trunk check-in is downloaded. Change this tag to something @ else (ex: release) to alter the behavior of the /download page. @ (Property: "download-tag") @ </p> @ <hr> entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0); @ <p>Enter the pathname of the page to display when the "Home" menu @ option is selected and when no pathname is @ specified in the URL. For example, if you visit the url:</p> @ @ <blockquote><p>%h(g.zBaseURL)</p></blockquote> @ @ <p>And you have specified an index page of "/home" the above will @ automatically redirect to:</p> @ @ <blockquote><p>%h(g.zBaseURL)/home</p></blockquote> @ @ <p>The default "/home" page displays a Wiki page with the same name @ as the Project Name specified above. Some sites prefer to redirect @ to a documentation page (ex: "/doc/trunk/index.wiki") or to "/timeline".</p> @ @ <p>Note: To avoid a redirect loop or other problems, this entry must @ begin with "/" and it must specify a valid page. For example, @ "<b>/home</b>" will work but "<b>home</b>" will not, since it omits the @ leading "/".</p> @ <p>(Property: "index-page") @ <hr> @ <p>The main menu for the web interface @ <p> @ @ <p>This setting should be a TCL list. Each set of four consecutive @ values defines a single main menu item: @ <ol> @ <li> The first term is text that appears on the menu. @ <li> The second term is a hyperlink to take when a user clicks on the @ entry. Hyperlinks that start with "/" are relative to the @ repository root. @ <li> The third term is an argument to the TH1 "capexpr" command. @ If capexpr evaluates to true, then the entry is shown. If not, @ the entry is omitted. "*" is always true. "{}" is never true. @ <li> The fourth term is a list of extra class names to apply to the new @ menu entry. Some skins use classes "desktoponly" and "wideonly" @ to only show the entries when the web browser screen is wide or @ very wide, respectively. @ </ol> @ @ <p>Some custom skins might not use this property. Whether the property @ is used or not a choice made by the skin designer. Some skins may add extra @ choices (such as the hamburger button) to the menu that are not shown @ on this list. (Property: mainmenu) @ <p> if(P("resetMenu")!=0){ db_unset("mainmenu", 0); cgi_delete_parameter("mmenu"); } textarea_attribute("Main Menu", 12, 80, "mainmenu", "mmenu", style_default_mainmenu(), 0); @ </p> @ <p><input type='checkbox' id='cbResetMenu' name='resetMenu' value='1'> @ <label for='cbResetMenu'>Reset menu to default value</label> @ </p> @ <hr> @ <p>Extra links to appear on the <a href="%R/sitemap">/sitemap</a> page, @ as sub-items of the "Home Page" entry, appearing before the @ "Documentation Search" entry (if any). In skins that use the /sitemap @ page to construct a hamburger menu dropdown, new entries added here @ will appear on the hamburger menu. @ @ <p>This setting should be a TCL list divided into triples. Each @ triple defines a new entry: @ <ol> @ <li> The first term is the display name of the /sitemap entry @ <li> The second term is a hyperlink to take when a user clicks on the @ entry. Hyperlinks that start with "/" are relative to the @ repository root. @ <li> The third term is an argument to the TH1 "capexpr" command. @ If capexpr evaluates to true, then the entry is shown. If not, @ the entry is omitted. "*" is always true. @ </ol> @ @ <p>The default value is blank, meaning no added entries. @ (Property: sitemap-extra) @ <p> textarea_attribute("Custom Sitemap Entries", 8, 80, "sitemap-extra", "smextra", "", 0); @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); style_finish_page(); } /* ** WEBPAGE: setup_wiki ** ** The "Admin/Wiki" page. Requires Setup privilege. */ void setup_wiki(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_set_current_feature("setup"); style_header("Wiki Configuration"); db_begin_transaction(); @ <form action="%R/setup_wiki" method="post"><div> login_insert_csrf_secret(); @ <input type="submit" name="submit" value="Apply Changes"></p> @ <hr> onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins", "wiki-about", "wiki-about", 1, 0); @ <p> @ Associate wiki pages with branches, tags, or checkins, based on @ the wiki page name. Wiki pages that begin with "branch/", "checkin/" @ or "tag/" and which continue with the name of an existing branch, check-in @ or tag are treated specially when this feature is enabled. @ <ul> @ <li> <b>branch/</b><i>branch-name</i> @ <li> <b>checkin/</b><i>full-check-in-hash</i> @ <li> <b>tag/</b><i>tag-name</i> @ </ul> @ (Property: "wiki-about")</p> @ <hr> entry_attribute("Allow Unsafe HTML In Markdown", 6, "safe-html", "safe-html", "", 0); @ <p>Allow "unsafe" HTML (ex: <script>, <form>, etc) to be @ generated by <a href="%R/md_rules">Markdown-formatted</a> documents. @ This setting is a string where each character indicates a "type" of @ document in which to allow unsafe HTML: @ <ul> @ <li> <b>b</b> → checked-in files, embedded documentation @ <li> <b>f</b> → forum posts @ <li> <b>t</b> → tickets @ <li> <b>w</b> → wiki pages @ </ul> @ Include letters for each type of document for which unsafe HTML should @ be allowed. For example, to allow unsafe HTML only for checked-in files, @ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except @ in forum posts, make this setting be "<b>btw</b>". The default is an @ empty string which means that Fossil never allows Markdown documents @ to generate unsafe HTML. @ (Property: "safe-html")</p> @ <hr> @ The current interwiki tag map is as follows: interwiki_append_map_table(cgi_output_blob()); @ <p>Visit <a href="./intermap">%R/intermap</a> for details or to @ modify the interwiki tag map. @ <hr> onoff_attribute("Use HTML as wiki markup language", "wiki-use-html", "wiki-use-html", 0, 0); @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed @ but all other wiki formatting will be ignored.</p> @ <p><strong>CAUTION:</strong> when @ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki. @ No sanitization is done. This means that it is very possible for malicious @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> @ <p>This should <strong>only</strong> be enabled when wiki editing is limited @ to trusted users. It should <strong>not</strong> be used on a publicly @ editable wiki.</p> @ (Property: "wiki-use-html") @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); style_finish_page(); } /* ** WEBPAGE: setup_chat ** ** The "Admin/Chat" page. Requires Setup privilege. */ void setup_chat(void){ static const char *const azAlerts[] = { "alerts/plunk.wav", "Plunk", "alerts/bflat3.wav", "Tone-1", "alerts/bflat2.wav", "Tone-2", "alerts/bloop.wav", "Bloop", }; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_set_current_feature("setup"); style_header("Chat Configuration"); db_begin_transaction(); @ <form action="%R/setup_chat" method="post"><div> login_insert_csrf_secret(); @ <input type="submit" name="submit" value="Apply Changes"></p> @ <hr> entry_attribute("Initial Chat History Size", 10, "chat-initial-history", "chatih", "50", 0); @ <p>When /chat first starts up, it preloads up to this many historical @ messages. @ (Property: "chat-initial-history")</p> @ <hr> entry_attribute("Minimum Number Of Historical Messages To Retain", 10, "chat-keep-count", "chatkc", "50", 0); @ <p>The chat subsystem purges older messages. But it will always retain @ the N most recent messages where N is the value of this setting. @ (Property: "chat-keep-count")</p> @ <hr> entry_attribute("Maximum Message Age In Days", 10, "chat-keep-days", "chatkd", "7", 0); @ <p>Chat message are removed after N days, where N is the value of @ this setting. N may be fractional. So, for example, to only keep @ an historical record of chat messages for 12 hours, set this value @ to 0.5. @ (Property: "chat-keep-days")</p> @ <hr> entry_attribute("Chat Polling Timeout", 10, "chat-poll-timeout", "chatpt", "420", 0); @ <p>New chat content is downloaded using the "long poll" technique. @ HTTP requests are made to /chat-poll which blocks waiting on new @ content to arrive. But the /chat-poll cannot block forever. It @ eventual must give up and return an empty message set. This setting @ determines how long /chat-poll will wait before giving up. The @ default setting of approximately 7 minutes works well on many systems. @ Shorter delays might be required on installations that use proxies @ or web-servers with short timeouts. For best efficiency, this value @ should be larger rather than smaller. @ (Property: "chat-poll-timeout")</p> @ <hr> entry_attribute("Chat Timeline Robot Username", 15, "chat-timeline-user", "chatrobot", "", 0); @ <p>If this setting is not an empty string, then any changes that appear @ on the timeline are announced in the chatroom under the username @ supplied. The username does not need to actually exist in the USER table. @ Suggested username: "chat-robot". @ (Property: "chat-timeline-user")</p> @ <hr> multiple_choice_attribute("Alert sound", "chat-alert-sound", "snd", azAlerts[0], count(azAlerts)/2, azAlerts); @ <p>The sound used in the client-side chat to indicate that a new @ chat message has arrived. @ (Property: "chat-alert-sound")</p> @ <hr/> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); @ <script nonce="%h(style_nonce())"> @ (function(){ @ var w = document.getElementById('idsnd'); @ w.onchange = function(){ @ var audio = new Audio('%s(g.zBaseURL)/builtin/' + w.value); @ audio.currentTime = 0; @ audio.play(); @ } @ })(); @ </script> style_finish_page(); } /* ** WEBPAGE: setup_modreq ** ** Admin page for setting up moderation of tickets and wiki. */ void setup_modreq(void){ login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_set_current_feature("setup"); 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. (Property: "modreq-tkt") @ @ <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. (Property: "modreq-wiki") @ </p> @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); style_finish_page(); } /* ** WEBPAGE: setup_adunit ** ** Administrative page for configuring and controlling ad units ** and how they are displayed. */ void setup_adunit(void){ login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } db_begin_transaction(); if( P("clear")!=0 && cgi_csrf_safe(2) ){ db_unprotect(PROTECT_CONFIG); db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'"); db_protect_pop(); cgi_replace_parameter("adunit",""); cgi_replace_parameter("adright",""); setup_incr_cfgcnt(); } style_set_current_feature("setup"); style_header("Edit Ad Unit"); @ <form action="%R/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> onoff_attribute("Temporarily disable all ads", "adunit-disable", "oall", 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>Properties: "adunit", "adunit-right", "adunit-omit-if-admin", and @ "adunit-omit-if-user". @ <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_finish_page(); db_end_transaction(0); } /* ** WEBPAGE: setup_logo ** ** Administrative page for changing the logo, background, and icon images. */ 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")); const char *zIconMtime = db_get_mtime("icon-image", 0, 0); const char *zIconMime = db_get("icon-mimetype","image/gif"); const char *aIconImg = P("iconim"); int szIconImg = atoi(PD("iconim:bytes","0")); if( szLogoImg>0 ){ zLogoMime = PD("logoim:mimetype","image/gif"); } if( szBgImg>0 ){ zBgMime = PD("bgim:mimetype","image/gif"); } if( szIconImg>0 ){ zIconMime = PD("iconim:mimetype","image/gif"); } login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } db_begin_transaction(); if( !cgi_csrf_safe(2) ){ /* Allow no state changes if not safe from CSRF */ }else if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){ Blob img; Stmt ins; blob_init(&img, aLogoImg, szLogoImg); db_unprotect(PROTECT_CONFIG); db_prepare(&ins, "REPLACE INTO config(name,value,mtime)" " VALUES('logo-image',:bytes,now())" ); db_bind_blob(&ins, ":bytes", &img); db_step(&ins); db_finalize(&ins); db_multi_exec( "REPLACE INTO config(name,value,mtime) VALUES('logo-mimetype',%Q,now())", zLogoMime ); db_protect_pop(); db_end_transaction(0); cgi_redirect("setup_logo"); }else if( P("clrlogo")!=0 ){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "DELETE FROM config WHERE name IN " "('logo-image','logo-mimetype')" ); db_protect_pop(); db_end_transaction(0); cgi_redirect("setup_logo"); }else if( P("setbg")!=0 && zBgMime && zBgMime[0] && szBgImg>0 ){ Blob img; Stmt ins; blob_init(&img, aBgImg, szBgImg); db_unprotect(PROTECT_CONFIG); db_prepare(&ins, "REPLACE INTO config(name,value,mtime)" " VALUES('background-image',:bytes,now())" ); db_bind_blob(&ins, ":bytes", &img); db_step(&ins); db_finalize(&ins); db_multi_exec( "REPLACE INTO config(name,value,mtime)" " VALUES('background-mimetype',%Q,now())", zBgMime ); db_protect_pop(); db_end_transaction(0); cgi_redirect("setup_logo"); }else if( P("clrbg")!=0 ){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "DELETE FROM config WHERE name IN " "('background-image','background-mimetype')" ); db_protect_pop(); db_end_transaction(0); cgi_redirect("setup_logo"); }else if( P("seticon")!=0 && zIconMime && zIconMime[0] && szIconImg>0 ){ Blob img; Stmt ins; blob_init(&img, aIconImg, szIconImg); db_unprotect(PROTECT_CONFIG); db_prepare(&ins, "REPLACE INTO config(name,value,mtime)" " VALUES('icon-image',:bytes,now())" ); db_bind_blob(&ins, ":bytes", &img); db_step(&ins); db_finalize(&ins); db_multi_exec( "REPLACE INTO config(name,value,mtime)" " VALUES('icon-mimetype',%Q,now())", zIconMime ); db_protect_pop(); db_end_transaction(0); cgi_redirect("setup_logo"); }else if( P("clricon")!=0 ){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "DELETE FROM config WHERE name IN " "('icon-image','icon-mimetype')" ); db_protect_pop(); db_end_transaction(0); cgi_redirect("setup_logo"); } style_set_current_feature("setup"); 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="%R/logo/%z(zLogoMtime)" alt="logo" border="1"> @ </p></blockquote> @ @ <form action="%R/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> @ <p>(Properties: "logo-image" and "logo-mimetype") @ </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="%R/background/%z(zBgMtime)" \ @ alt="background" border=1> @ </p></blockquote> @ @ <form action="%R/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> @ </div></form> @ <p>(Properties: "background-image" and "background-mimetype") @ <hr> @ @ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b> @ and looks like this:</p> @ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \ @ alt="icon" border=1> @ </p></blockquote> @ @ <form action="%R/setup_logo" method="post" @ enctype="multipart/form-data"><div> @ <p>The icon image is accessible to all users at this URL: @ <a href="%s(g.zBaseURL)/favicon.ico">%s(g.zBaseURL)/favicon.ico</a>. @ The icon image may or may not appear on each @ page depending on the web browser in use and the MIME-Types that it @ supports for icon images. @ To change the icon image, use the following form:</p> login_insert_csrf_secret(); @ Icon image file: @ <input type="file" name="iconim" size="60" accept="image/*"> @ <p align="center"> @ <input type="submit" name="seticon" value="Change Icon"> @ <input type="submit" name="clricon" value="Revert To Default"></p> @ </div></form> @ <p>(Properties: "icon-image" and "icon-mimetype") @ <hr> @ @ <p><span class="note">Note:</span> Your browser has probably cached these @ images, so you may need to press the Reload button before changes will @ take effect. </p> style_finish_page(); db_end_transaction(0); } /* ** Prevent the RAW SQL feature from being used to ATTACH a different ** database and query it. ** ** Actually, the RAW SQL feature only does a single statement per request. ** So it is not possible to ATTACH and then do a separate query. This ** routine is not strictly necessary, therefore. But it does not hurt ** to be paranoid. */ int raw_sql_query_authorizer( void *pError, int code, const char *zArg1, const char *zArg2, const char *zArg3, const char *zArg4 ){ if( code==SQLITE_ATTACH ){ return SQLITE_DENY; } return SQLITE_OK; } /* ** WEBPAGE: admin_sql ** ** Run raw SQL commands against the database file using the web interface. ** Requires Setup privileges. */ void sql_page(void){ const char *zQ; int go = P("go")!=0; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } add_content_sql_commands(g.db); zQ = cgi_csrf_safe(2) ? P("q") : 0; style_set_current_feature("setup"); 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> @ #if 0 @ <p>Only the first statement in the entry box will be run. @ Any subsequent statements will be silently ignored.</p> @ @ <p>Database names:<ul><li>repository if( g.zConfigDbName ){ @ <li>configdb } if( g.localOpen ){ @ <li>localdb } @ </ul></p> #endif if( P("configtab") ){ /* If the user presses the "CONFIG Table Query" button, populate the ** query text with a pre-packaged query against the CONFIG table */ zQ = "SELECT\n" " CASE WHEN length(name)<50 THEN name ELSE printf('%.50s...',name)" " END AS name,\n" " CASE WHEN typeof(value)<>'blob' AND length(value)<80 THEN value\n" " ELSE '...' END AS value,\n" " datetime(mtime, 'unixepoch') AS mtime\n" "FROM config\n" "-- ORDER BY mtime DESC; -- optional"; go = 1; } @ @ <form method="post" action="%R/admin_sql"> login_insert_csrf_secret(); @ SQL:<br> @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br> @ <input type="submit" name="go" value="Run SQL"> @ <input type="submit" name="schema" value="Show Schema"> @ <input type="submit" name="tablelist" value="List Tables"> @ <input type="submit" name="configtab" value="CONFIG Table Query"> @ </form> if( P("schema") ){ zQ = sqlite3_mprintf( "SELECT sql FROM repository.sqlite_schema" " WHERE sql IS NOT NULL ORDER BY name"); go = 1; }else if( P("tablelist") ){ zQ = sqlite3_mprintf("SELECT*FROM pragma_table_list ORDER BY schema, name"); go = 1; } if( go && cgi_csrf_safe(2) ){ sqlite3_stmt *pStmt; int rc; const char *zTail; int nCol; int nRow = 0; int i; @ <hr> sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0); search_sql_setup(g.db); rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail); if( rc!=SQLITE_OK ){ @ <div class="generalError">%h(sqlite3_errmsg(g.db))</div> sqlite3_finalize(pStmt); }else if( pStmt==0 ){ /* No-op */ }else if( (nCol = sqlite3_column_count(pStmt))==0 ){ sqlite3_step(pStmt); rc = sqlite3_finalize(pStmt); if( rc ){ @ <div class="generalError">%h(sqlite3_errmsg(g.db))</div> } }else{ @ <table border="1" cellpadding="4" cellspacing="0"> while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( nRow==0 ){ @ <tr> for(i=0; i<nCol; i++){ @ <th>%h(sqlite3_column_name(pStmt, i))</th> } @ </tr> } nRow++; @ <tr> for(i=0; i<nCol; i++){ switch( sqlite3_column_type(pStmt, i) ){ case SQLITE_INTEGER: case SQLITE_FLOAT: { @ <td align="right" valign="top"> @ %s(sqlite3_column_text(pStmt, i))</td> break; } case SQLITE_NULL: { @ <td valign="top" align="center"><i>NULL</i></td> break; } case SQLITE_TEXT: { const char *zText = (const char*)sqlite3_column_text(pStmt, i); @ <td align="left" valign="top" @ style="white-space:pre;">%h(zText)</td> break; } case SQLITE_BLOB: { @ <td valign="top" align="center"> @ <i>%d(sqlite3_column_bytes(pStmt, i))-byte BLOB</i></td> break; } } } @ </tr> } sqlite3_finalize(pStmt); @ </table> } } style_finish_page(); } /* ** WEBPAGE: admin_th1 ** ** Run raw TH1 commands using the web interface. If Tcl integration was ** enabled at compile-time and the "tcl" setting is enabled, Tcl commands ** may be run as well. Requires Admin privilege. */ 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; } style_set_current_feature("setup"); 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> @ form_begin(0, "%R/admin_th1"); @ TH1:<br> @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br> @ <input type="submit" name="go" value="Run TH1"> @ </form> if( go && cgi_csrf_safe(2) ){ const char *zR; int rc; int n; @ <hr> rc = Th_Eval(g.interp, 0, zQ, -1); zR = Th_GetResult(g.interp, &n); if( rc==TH_OK ){ @ <pre class="th1result">%h(zR)</pre> }else{ @ <pre class="th1error">%h(zR)</pre> } } style_finish_page(); } /* ** 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; int limit; /* How many entries to show */ int ofst; /* Offset to the first entry */ int fLogEnabled; int counter = 0; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_set_current_feature("setup"); style_header("Admin Log"); style_submenu_element("Log-Menu", "setup-logmenu"); create_admin_log_table(); limit = atoi(PD("n","200")); ofst = atoi(PD("x","0")); fLogEnabled = db_get_boolean("admin-log", 0); @ <div>Admin logging is %s(fLogEnabled?"on":"off"). @ (Change this on the <a href="setup_settings">settings</a> page.)</div> if( ofst>0 ){ int prevx = ofst - limit; if( prevx<0 ) prevx = 0; @ <p><a href="admin_log?n=%d(limit)&x=%d(prevx)">[Newer]</a></p> } db_prepare(&stLog, "SELECT datetime(time,'unixepoch'), who, page, what " "FROM admin_log " "ORDER BY time DESC, rowid DESC"); style_table_sorter(); @ <table class="sortable adminLogTable" width="100%%" \ @ data-column-types='Tttx' data-init-sort='1'> @ <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); counter++; if( counter<ofst ) continue; if( counter>ofst+limit ) break; @ <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> } db_finalize(&stLog); @ </tbody></table> if( counter>ofst+limit ){ @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p> } style_finish_page(); } /* ** Renders a selection list of values for the search-tokenizer ** setting, using the form field name "ftstok". */ static void select_fts_tokenizer(void){ const char *const aTokenizer[] = { "off", "None", "porter", "Porter Stemmer", "unicode61", "Unicode without stemming", "trigram", "Trigram", }; multiple_choice_attribute("FTS Tokenizer", "search-tokenizer", "ftstok", "off", 4, aTokenizer); } /* ** WEBPAGE: srchsetup ** ** Configure the search engine. Requires Admin privilege. */ void page_srchsetup(){ login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_set_current_feature("setup"); style_header("Search Configuration"); @ <form action="%R/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); @ <br> onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); @ <br> onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ <hr> if( P("fts0") ){ search_drop_index(); }else if( P("fts1") ){ const char *zTokenizer = PD("ftstok","off"); search_set_tokenizer(zTokenizer); search_drop_index(); search_create_index(); search_fill_index(); search_update_index(search_restrict(SRCH_ALL)); } if( search_index_exists() ){ int pgsz = db_int64(0, "PRAGMA repository.page_size;"); i64 nTotal = db_int64(0, "PRAGMA repository.page_count;")*pgsz; i64 nFts = db_int64(0, "SELECT count(*) FROM dbstat" " WHERE schema='repository'" " AND name LIKE 'fts%%'")*pgsz; char zSize[30]; approxSizeName(sizeof(zSize),zSize,nFts); @ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index. @ The index helps search run faster, especially on large repositories, @ but takes up space. The index is currently using about %s(zSize) @ or %.1f(100.0*(double)nFts/(double)nTotal)%% of the repository.</p> select_fts_tokenizer(); @ <p><input type="submit" name="fts0" value="Delete The Full-Text Index"> @ <input type="submit" name="fts1" value="Rebuild The Full-Text Index"> style_submenu_element("FTS Index Debugging","%R/test-ftsdocs"); }else{ @ <p>The SQLite search index is disabled. All searching will be @ a full-text scan. This usually works fine, but can be slow for @ larger repositories.</p> select_fts_tokenizer(); @ <p><input type="submit" name="fts1" value="Create A Full-Text Index"> } @ </div></form> style_finish_page(); } /* ** A URL Alias originally called zOldName is now zNewName/zValue. ** Write SQL to make this change into pSql. ** ** If zNewName or zValue is an empty string, then delete the entry. ** ** If zOldName is an empty string, create a new entry. */ static void setup_update_url_alias( Blob *pSql, const char *zOldName, const char *zNewName, const char *zValue ){ if( !cgi_csrf_safe(2) ) return; if( zNewName[0]==0 || zValue[0]==0 ){ if( zOldName[0] ){ blob_append_sql(pSql, "DELETE FROM config WHERE name='walias:%q';\n", zOldName); } return; } if( zOldName[0]==0 ){ blob_append_sql(pSql, "INSERT INTO config(name,value,mtime) VALUES('walias:%q',%Q,now());\n", zNewName, zValue); return; } if( strcmp(zOldName, zNewName)!=0 ){ blob_append_sql(pSql, "UPDATE config SET name='walias:%q', value=%Q, mtime=now()" " WHERE name='walias:%q';\n", zNewName, zValue, zOldName); }else{ blob_append_sql(pSql, "UPDATE config SET value=%Q, mtime=now()" " WHERE name='walias:%q' AND value<>%Q;\n", zValue, zOldName, zValue); } } /* ** WEBPAGE: waliassetup ** ** Configure the URL aliases */ void page_waliassetup(){ Stmt q; int cnt = 0; Blob namelist; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_set_current_feature("setup"); style_header("URL Alias Configuration"); if( P("submit")!=0 && cgi_csrf_safe(2) ){ Blob token; Blob sql; const char *zNewName; const char *zValue; char zCnt[10]; blob_init(&namelist, PD("namelist",""), -1); blob_init(&sql, 0, 0); while( blob_token(&namelist, &token) ){ const char *zOldName = blob_str(&token); sqlite3_snprintf(sizeof(zCnt), zCnt, "n%d", cnt); zNewName = PD(zCnt, ""); sqlite3_snprintf(sizeof(zCnt), zCnt, "v%d", cnt); zValue = PD(zCnt, ""); setup_update_url_alias(&sql, zOldName, zNewName, zValue); cnt++; blob_reset(&token); } sqlite3_snprintf(sizeof(zCnt), zCnt, "n%d", cnt); zNewName = PD(zCnt,""); sqlite3_snprintf(sizeof(zCnt), zCnt, "v%d", cnt); zValue = PD(zCnt,""); setup_update_url_alias(&sql, "", zNewName, zValue); db_unprotect(PROTECT_CONFIG); db_multi_exec("%s", blob_sql_text(&sql)); db_protect_pop(); blob_reset(&sql); blob_reset(&namelist); cnt = 0; } db_prepare(&q, "SELECT substr(name,8), value FROM config WHERE name GLOB 'walias:/*'" " UNION ALL SELECT '', ''" ); @ <form action="%R/waliassetup" method="post"><div> login_insert_csrf_secret(); @ <table border=0 cellpadding=5> @ <tr><th>Alias<th>URI That The Alias Maps Into blob_init(&namelist, 0, 0); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zValue = db_column_text(&q, 1); @ <tr><td> @ <input type='text' size='20' value='%h(zName)' name='n%d(cnt)'> @ </td><td> @ <input type='text' size='80' value='%h(zValue)' name='v%d(cnt)'> @ </td></tr> cnt++; if( blob_size(&namelist)>0 ) blob_append(&namelist, " ", 1); blob_append(&namelist, zName, -1); } db_finalize(&q); @ <tr><td> @ <input type='hidden' name='namelist' value='%h(blob_str(&namelist))'> @ <input type='submit' name='submit' value="Apply Changes"> @ </td><td></td></tr> @ </table></form> @ <hr> @ <p>When the first term of an incoming URL exactly matches one of @ the "Aliases" on the left-hand side (LHS) above, the URL is @ converted into the corresponding form on the right-hand side (RHS). @ <ul> @ <li><p> @ The LHS is compared against only the first term of the incoming URL. @ All LHS entries in the alias table should therefore begin with a @ single "/" followed by a single path element. @ <li><p> @ The RHS entries in the alias table should begin with a single "/" @ followed by a path element, and optionally followed by "?" and a @ list of query parameters. @ <li><p> @ Query parameters on the RHS are added to the set of query parameters @ in the incoming URL. @ <li><p> @ If the same query parameter appears in both the incoming URL and @ on the RHS of the alias, the RHS query parameter value overwrites @ the value on the incoming URL. @ <li><p> @ If a query parameter on the RHS of the alias is of the form "X!" @ (a name followed by "!") then the X query parameter is removed @ from the incoming URL if @ it exists. @ <li><p> @ Only a single alias operation occurs. It is not possible to nest aliases. @ The RHS entries must be built-in webpage names. @ <li><p> @ The alias table is only checked if no built-in webpage matches @ the incoming URL. @ Hence, it is not possible to override a built-in webpage using aliases. @ This is by design. @ </ul> @ @ <p>To delete an entry from the alias table, change its name or value to an @ empty string and press "Apply Changes". @ @ <p>To add a new alias, fill in the name and value in the bottom row @ of the table above and press "Apply Changes". style_finish_page(); } |
Added src/setupuser.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 | /* ** Copyright (c) 2007 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 pages associated with user management. The code in this ** file was formerly part of the "setup.c" module, but has been broken ** out into its own module to improve maintainability. ** ** Note: Do not confuse "Users" with "Subscribers". Code to deal with ** subscribers is over in the "alerts.c" source file. */ #include "config.h" #include <assert.h> #include "setupuser.h" /* ** WEBPAGE: setup_ulist ** ** Show a list of users. Clicking on any user jumps to the edit ** screen for that user. Requires Admin privileges. ** ** Query parameters: ** ** with=CAP Only show users that have one or more capabilities in CAP. ** ubg Color backgrounds by username hash */ void setup_ulist(void){ Stmt s; double rNow; const char *zWith = P("with"); int bUnusedOnly = P("unused")!=0; int bUbg = P("ubg")!=0; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_submenu_element("Add", "setup_uedit"); style_submenu_element("Log", "access_log"); style_submenu_element("Help", "setup_ulist_notes"); if( alert_tables_exist() ){ style_submenu_element("Subscribers", "subscribers"); } style_set_current_feature("setup"); style_header("User List"); if( (zWith==0 || zWith[0]==0) && !bUnusedOnly ){ @ <table border=1 cellpadding=2 cellspacing=0 class='userTable'> @ <thead><tr> @ <th>Category @ <th>Capabilities (<a href='%R/setup_ucap_list'>key</a>) @ <th>Info <th>Last Change</tr></thead> @ <tbody> db_prepare(&s, "SELECT uid, login, cap, date(mtime,'unixepoch')" " FROM user" " WHERE login IN ('anonymous','nobody','developer','reader')" " ORDER BY login" ); while( db_step(&s)==SQLITE_ROW ){ int uid = db_column_int(&s, 0); const char *zLogin = db_column_text(&s, 1); const char *zCap = db_column_text(&s, 2); const char *zDate = db_column_text(&s, 4); @ <tr> @ <td><a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> @ <td>%h(zCap) if( fossil_strcmp(zLogin,"anonymous")==0 ){ @ <td>All logged-in users }else if( fossil_strcmp(zLogin,"developer")==0 ){ @ <td>Users with '<b>v</b>' capability }else if( fossil_strcmp(zLogin,"nobody")==0 ){ @ <td>All users without login }else if( fossil_strcmp(zLogin,"reader")==0 ){ @ <td>Users with '<b>u</b>' capability }else{ @ <td> } if( zDate && zDate[0] ){ @ <td>%h(zDate) }else{ @ <td> } @ </tr> } db_finalize(&s); @ </tbody></table> @ <div class='section'>Users</div> }else{ style_submenu_element("All Users", "setup_ulist"); if( bUnusedOnly ){ @ <div class='section'>Unused logins</div> }else if( zWith ){ if( zWith[1]==0 ){ @ <div class='section'>Users with capability "%h(zWith)"</div> }else{ @ <div class='section'>Users with any capability in "%h(zWith)"</div> } } } if( !bUnusedOnly ){ style_submenu_element("Unused", "setup_ulist?unused"); } @ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \ @ data-column-types='ktxTTKt' data-init-sort='2'> @ <thead><tr> @ <th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login\ @ <th>Alerts</tr></thead> @ <tbody> db_multi_exec( "CREATE TEMP TABLE lastAccess(uname TEXT PRIMARY KEY, atime REAL)" "WITHOUT ROWID;" ); if( db_table_exists("repository","accesslog") ){ db_multi_exec( "INSERT INTO lastAccess(uname, atime)" " SELECT uname, max(mtime) FROM (" " SELECT uname, mtime FROM accesslog WHERE success" " UNION ALL" " SELECT login AS uname, rcvfrom.mtime AS mtime" " FROM rcvfrom JOIN user USING(uid))" " GROUP BY 1;" ); } if( !db_table_exists("repository","subscriber") ){ db_multi_exec( "CREATE TEMP TABLE subscriber(suname PRIMARY KEY, ssub, subscriberId)" "WITHOUT ROWID;" ); } if( bUnusedOnly ){ zWith = mprintf( " AND login NOT IN (" "SELECT user FROM event WHERE user NOT NULL " "UNION ALL SELECT euser FROM event WHERE euser NOT NULL%s)" " AND uid NOT IN (SELECT uid FROM rcvfrom)", alert_tables_exist() ? " UNION ALL SELECT suname FROM subscriber WHERE suname NOT NULL":""); }else if( zWith && zWith[0] ){ zWith = mprintf(" AND fullcap(cap) GLOB '*[%q]*'", zWith); }else{ zWith = ""; } db_prepare(&s, "SELECT uid, login, cap, info, date(user.mtime,'unixepoch')," " lower(login) AS sortkey, " " CASE WHEN info LIKE '%%expires 20%%'" " THEN substr(info,instr(lower(info),'expires')+8,10)" " END AS exp," "atime," " subscriber.ssub, subscriber.subscriberId" " FROM user LEFT JOIN lastAccess ON login=uname" " LEFT JOIN subscriber ON login=suname" " WHERE login NOT IN ('anonymous','nobody','developer','reader') %s" " ORDER BY sortkey", zWith/*safe-for-%s*/ ); rNow = db_double(0.0, "SELECT julianday('now');"); while( db_step(&s)==SQLITE_ROW ){ int uid = db_column_int(&s, 0); const char *zLogin = db_column_text(&s, 1); const char *zCap = db_column_text(&s, 2); const char *zInfo = db_column_text(&s, 3); const char *zDate = db_column_text(&s, 4); const char *zSortKey = db_column_text(&s,5); const char *zExp = db_column_text(&s,6); double rATime = db_column_double(&s,7); char *zAge = 0; const char *zSub; int sid = db_column_int(&s,9); if( rATime>0.0 ){ zAge = human_readable_age(rNow - rATime); } if( bUbg ){ @ <tr style='background-color: %h(user_color(zLogin));'> }else{ @ <tr> } @ <td data-sortkey='%h(zSortKey)'>\ @ <a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> @ <td>%h(zCap) @ <td>%h(zInfo) @ <td>%h(zDate?zDate:"") @ <td>%h(zExp?zExp:"") @ <td data-sortkey='%f(rATime)' style='white-space:nowrap'>%s(zAge?zAge:"") if( db_column_type(&s,8)==SQLITE_NULL ){ @ <td> }else if( (zSub = db_column_text(&s,8))==0 || zSub[0]==0 ){ @ <td><a href="%R/alerts?sid=%d(sid)"><i>off</i></a> }else{ @ <td><a href="%R/alerts?sid=%d(sid)">%h(zSub)</a> } @ </tr> fossil_free(zAge); } @ </tbody></table> db_finalize(&s); style_table_sorter(); style_finish_page(); } /* ** WEBPAGE: setup_ulist_notes ** ** A documentation page showing notes about user configuration. This ** information used to be a side-bar on the user list page, but has been ** factored out for improved presentation. */ void setup_ulist_notes(void){ style_set_current_feature("setup"); style_header("User Configuration Notes"); @ <h1>User Configuration Notes:</h1> @ <ol> @ <li><p> @ Every user, logged in or not, inherits the privileges of @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ Any human can login as <span class="usertype">anonymous</span> since the @ password is clearly displayed on the login page for them to type. The @ purpose of requiring anonymous to log in is to prevent access by spiders. @ Every logged-in user inherits the combined privileges of @ <span class="usertype">anonymous</span> and @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ Users with privilege <span class="capability">u</span> inherit the combined @ privileges of <span class="usertype">reader</span>, @ <span class="usertype">anonymous</span>, and @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ Users with privilege <span class="capability">v</span> inherit the combined @ privileges of <span class="usertype">developer</span>, @ <span class="usertype">anonymous</span>, and @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p>The permission flags are as follows:</p> capabilities_table(CAPCLASS_ALL); @ </li> @ </ol> style_finish_page(); } /* ** WEBPAGE: setup_ucap_list ** ** A documentation page showing the meaning of the various user capabilities ** code letters. */ void setup_ucap_list(void){ style_set_current_feature("setup"); style_header("User Capability Codes"); @ <h1>All capabilities</h1> capabilities_table(CAPCLASS_ALL); @ <h1>Capabilities associated with checked-in content</h1> capabilities_table(CAPCLASS_CODE); @ <h1>Capabilities associated with data transfer and sync</h1> capabilities_table(CAPCLASS_DATA); @ <h1>Capabilities associated with the forum</h1> capabilities_table(CAPCLASS_FORUM); @ <h1>Capabilities associated with tickets</h1> capabilities_table(CAPCLASS_TKT); @ <h1>Capabilities associated with wiki</h1> capabilities_table(CAPCLASS_WIKI); @ <h1>Administrative capabilities</h1> capabilities_table(CAPCLASS_SUPER); @ <h1>Miscellaneous capabilities</h1> capabilities_table(CAPCLASS_OTHER); style_finish_page(); } /* ** Return true if zPw is a valid password string. A valid ** password string is: ** ** (1) A zero-length string, or ** (2) a string that contains a character other than '*'. */ static int isValidPwString(const char *zPw){ if( zPw==0 ) return 0; if( zPw[0]==0 ) return 1; while( zPw[0]=='*' ){ zPw++; } return zPw[0]!=0; } /* ** WEBPAGE: setup_uedit ** ** Edit information about a user or create a new user. ** Requires Admin privileges. */ void user_edit(void){ const char *zId, *zLogin, *zInfo, *zCap, *zPw; const char *zGroup; const char *zOldLogin; int uid, i; char *zDeleteVerify = 0; /* Delete user verification text */ int higherUser = 0; /* True if user being edited is SETUP and the */ /* user doing the editing is ADMIN. Disallow editing */ const 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") ){ /* User pressed the cancel button */ cgi_redirect(cgi_referer("setup_ulist")); return; } /* Check for requests to delete the user */ if( P("delete") && cgi_csrf_safe(2) ){ int n; if( P("verifydelete") ){ /* Verified delete user request */ db_unprotect(PROTECT_USER); if( alert_tables_exist() ){ /* Also delete any subscriptions associated with this user */ db_multi_exec("DELETE FROM subscriber WHERE suname=" "(SELECT login FROM user WHERE uid=%d)", uid); } db_multi_exec("DELETE FROM user WHERE uid=%d", uid); db_protect_pop(); moderation_disapprove_for_missing_users(); admin_log("Deleted user [%s] (uid %d).", PD("login","???")/*safe-for-%s*/, uid); cgi_redirect(cgi_referer("setup_ulist")); return; } n = db_int(0, "SELECT count(*) FROM event" " WHERE user=%Q AND objid NOT IN private", P("login")); if( n==0 ){ zDeleteVerify = mprintf("Check this box and press \"Delete User\" again"); }else{ zDeleteVerify = mprintf( "User \"%s\" has %d or more artifacts in the block-chain. " "Delete anyhow?", P("login")/*safe-for-%s*/, n); } } style_set_current_feature("setup"); /* 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. */ if( !cgi_all("login","info","pw","apply") ){ /* need all of the above properties to make a change. Since one or ** more are missing, no-op */ }else if( higherUser ){ /* An Admin (a) user cannot edit a Superuser (s) */ }else if( zDeleteVerify!=0 ){ /* Need to verify a delete request */ }else if( !cgi_csrf_safe(2) ){ /* This might be a cross-site request forgery, so ignore it */ }else{ /* We have all the information we need to make the change to the user */ char c; char zCap[70], zNm[4]; zNm[0] = 'a'; zNm[2] = 0; for(i=0, c='a'; c<='z'; c++){ zNm[1] = c; a[c&0x7f] = ((c!='s' && c!='y') || g.perm.Setup) && P(zNm)!=0; if( a[c&0x7f] ) zCap[i++] = c; } for(c='0'; c<='9'; c++){ zNm[1] = c; a[c&0x7f] = P(zNm)!=0; if( a[c&0x7f] ) zCap[i++] = c; } for(c='A'; c<='Z'; c++){ zNm[1] = c; a[c&0x7f] = P(zNm)!=0; if( a[c&0x7f] ) zCap[i++] = c; } zCap[i] = 0; zPw = P("pw"); zLogin = P("login"); if( strlen(zLogin)==0 ){ const char *zRef = cgi_referer("setup_ulist"); style_header("User Creation Error"); @ <span class="loginError">Empty login not allowed.</span> @ @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> @ [Bummer]</a></p> style_finish_page(); return; } 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) ){ const char *zRef = cgi_referer("setup_ulist"); 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)&referer=%T(zRef)"> @ [Bummer]</a></p> style_finish_page(); return; } cgi_csrf_verify(); db_unprotect(PROTECT_USER); 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 ); if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){ if( alert_tables_exist() ){ /* Rename matching subscriber entry, else the user cannot re-subscribe with their same email address. */ db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q", zLogin, zOldLogin); } admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin ); } db_protect_pop(); setup_incr_cfgcnt(); 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)" " SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);", zLogin, zLogin ); zOldLogin = zLogin; } #if 0 /* Problem: when renaming a user we need to update the subcriber ** names to match but we cannot know from here if each member of ** the login group has the subscriber tables, so we cannot blindly ** include this SQL. */ else if( fossil_strcmp(zLogin, zOldLogin)!=0 && alert_tables_exist() ){ /* Rename matching subscriber entry, else the user cannot re-subscribe with their same email address. */ blob_appendf(&sql, "UPDATE subscriber SET suname=%Q WHERE suname=%Q;", zLogin, zOldLogin); } #endif blob_appendf(&sql, "UPDATE user SET login=%Q," " pw=coalesce(shared_secret(%Q,%Q," "(SELECT value FROM config WHERE name='project-code')),pw)," " info=%Q," " cap=%Q," " mtime=now()" " WHERE login=%Q;", zLogin, P("pw"), zLogin, P("info"), zCap, zOldLogin ); db_unprotect(PROTECT_USER); login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); db_protect_pop(); blob_reset(&sql); admin_log( "Updated user [%q] in all login groups " "with capabilities [%q].", zLogin, zCap ); if( zErr ){ const char *zRef = cgi_referer("setup_ulist"); style_header("User Change Error"); admin_log( "Error updating user '%q': %s'.", zLogin, zErr ); @ <span class="loginError">%h(zErr)</span> @ @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> @ [Bummer]</a></p> style_finish_page(); return; } } cgi_redirect(cgi_referer("setup_ulist")); return; } /* Load the existing information about the user, if any */ zLogin = ""; zInfo = ""; zCap = ""; zPw = ""; for(i='a'; i<='z'; i++) oa[i] = ""; for(i='0'; i<='9'; i++) oa[i] = ""; for(i='A'; i<='Z'; i++) oa[i] = ""; if( uid ){ zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); for(i=0; zCap[i]; i++){ char c = zCap[i]; if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){ oa[c&0x7f] = " checked=\"checked\""; } } } /* figure out inherited permissions */ memset((char *)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", "%s", cgi_referer("setup_ulist")); if( uid ){ style_header("Edit User %h", zLogin); if( !login_is_special(zLogin) ){ style_submenu_element("Access Log", "%R/access_log?u=%t", zLogin); style_submenu_element("Timeline","%R/timeline?u=%t", 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="*"> } @ <input type="hidden" name="referer" value="%h(cgi_referer("setup_ulist"))"> @ <table width="100%%"> @ <tr> @ <td class="usetupEditLabel" id="suuid">User ID:</td> if( uid ){ @ <td>%d(uid) <input aria-labelledby="suuid" type="hidden" \ @ name="id" value="%d(uid)"/>\ @ </td> }else{ @ <td>(new user)<input aria-labelledby="suuid" type="hidden" name="id" \ @ value="0"></td> } @ </tr> @ <tr> @ <td class="usetupEditLabel" id="sulgn">Login:</td> if( login_is_special(zLogin) ){ @ <td><b>%h(zLogin)</b></td> }else{ @ <td><input aria-labelledby="sulgn" type="text" name="login" \ @ value="%h(zLogin)"> if( alert_tables_exist() ){ int sid; sid = db_int(0, "SELECT subscriberId FROM subscriber" " WHERE suname=%Q", zLogin); if( sid>0 ){ @ <a href="%R/alerts?sid=%d(sid)">\ @ (subscription info for %h(zLogin))</a>\ } } @ </td></tr> @ <tr> @ <td class="usetupEditLabel" id="sucnfo">Contact Info:</td> @ <td><textarea aria-labelledby="sucnfo" name="info" cols="40" \ @ rows="2">%h(zInfo)</textarea></td> } @ </tr> @ <tr> @ <td class="usetupEditLabel">Capabilities:</td> @ <td width="100%%"> #define B(x) inherit[x] @ <div class="columns" style="column-width:13em;"> @ <ul style="list-style-type: none;"> if( g.perm.Setup ){ @ <li><label><input type="checkbox" name="as"%s(oa['s'])> @ Setup%s(B('s'))</label> } @ <li><label><input type="checkbox" name="aa"%s(oa['a'])> @ Admin%s(B('a'))</label> @ <li><label><input type="checkbox" name="au"%s(oa['u'])> @ Reader%s(B('u'))</label> @ <li><label><input type="checkbox" name="av"%s(oa['v'])> @ Developer%s(B('v'))</label> #if 0 /* Not Used */ @ <li><label><input type="checkbox" name="ad"%s(oa['d'])> @ Delete%s(B('d'))</label> #endif @ <li><label><input type="checkbox" name="ae"%s(oa['e'])> @ View-PII%s(B('e'))</label> @ <li><label><input type="checkbox" name="ap"%s(oa['p'])> @ Password%s(B('p'))</label> @ <li><label><input type="checkbox" name="ai"%s(oa['i'])> @ Check-In%s(B('i'))</label> @ <li><label><input type="checkbox" name="ao"%s(oa['o'])> @ Check-Out%s(B('o'))</label> @ <li><label><input type="checkbox" name="ah"%s(oa['h'])> @ Hyperlinks%s(B('h'))</label> @ <li><label><input type="checkbox" name="ab"%s(oa['b'])> @ Attachments%s(B('b'))</label> @ <li><label><input type="checkbox" name="ag"%s(oa['g'])> @ Clone%s(B('g'))</label> @ <li><label><input type="checkbox" name="aj"%s(oa['j'])> @ Read Wiki%s(B('j'))</label> @ <li><label><input type="checkbox" name="af"%s(oa['f'])> @ New Wiki%s(B('f'))</label> @ <li><label><input type="checkbox" name="am"%s(oa['m'])> @ Append Wiki%s(B('m'))</label> @ <li><label><input type="checkbox" name="ak"%s(oa['k'])> @ Write Wiki%s(B('k'))</label> @ <li><label><input type="checkbox" name="al"%s(oa['l'])> @ Moderate Wiki%s(B('l'))</label> @ <li><label><input type="checkbox" name="ar"%s(oa['r'])> @ Read Ticket%s(B('r'))</label> @ <li><label><input type="checkbox" name="an"%s(oa['n'])> @ New Tickets%s(B('n'))</label> @ <li><label><input type="checkbox" name="ac"%s(oa['c'])> @ Append To Ticket%s(B('c'))</label> @ <li><label><input type="checkbox" name="aw"%s(oa['w'])> @ Write Tickets%s(B('w'))</label> @ <li><label><input type="checkbox" name="aq"%s(oa['q'])> @ Moderate Tickets%s(B('q'))</label> @ <li><label><input type="checkbox" name="at"%s(oa['t'])> @ Ticket Report%s(B('t'))</label> @ <li><label><input type="checkbox" name="ax"%s(oa['x'])> @ Private%s(B('x'))</label> @ <li><label><input type="checkbox" name="ay"%s(oa['y'])> @ Write Unversioned%s(B('y'))</label> @ <li><label><input type="checkbox" name="az"%s(oa['z'])> @ Download Zip%s(B('z'))</label> @ <li><label><input type="checkbox" name="a2"%s(oa['2'])> @ Read Forum%s(B('2'))</label> @ <li><label><input type="checkbox" name="a3"%s(oa['3'])> @ Write Forum%s(B('3'))</label> @ <li><label><input type="checkbox" name="a4"%s(oa['4'])> @ WriteTrusted Forum%s(B('4'))</label> @ <li><label><input type="checkbox" name="a5"%s(oa['5'])> @ Moderate Forum%s(B('5'))</label> @ <li><label><input type="checkbox" name="a6"%s(oa['6'])> @ Supervise Forum%s(B('6'))</label> @ <li><label><input type="checkbox" name="a7"%s(oa['7'])> @ Email Alerts%s(B('7'))</label> @ <li><label><input type="checkbox" name="aA"%s(oa['A'])> @ Send Announcements%s(B('A'))</label> @ <li><label><input type="checkbox" name="aC"%s(oa['C'])> @ Chatroom%s(B('C'))</label> @ <li><label><input type="checkbox" name="aD"%s(oa['D'])> @ Enable Debug%s(B('D'))</label> @ </ul></div> @ </td> @ </tr> @ <tr> @ <td class="usetupEditLabel">Selected Cap:</td> @ <td> @ <span id="usetupEditCapability">(missing JS?)</span> @ <a href="%R/setup_ucap_list">(key)</a> @ </td> @ </tr> if( !login_is_special(zLogin) ){ @ <tr> @ <td align="right" id="supw">Password:</td> if( zPw[0] ){ /* Obscure the password for all users */ @ <td><input aria-labelledby="supw" type="password" autocomplete="off" \ @ name="pw" value="**********"> @ (Leave unchanged to retain password)</td> }else{ /* Show an empty password as an empty input field */ char *zRPW = fossil_random_password(12); @ <td><input aria-labelledby="supw" type="password" name="pw" \ @ autocomplete="off" value=""> Password suggestion: %z(zRPW)</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 ){ if( zDeleteVerify ){ @ <tr> @ <td valign="top" align="right">Verify:</td> @ <td><label><input type="checkbox" name="verifydelete">\ @ Confirm Delete \ @ <span class="loginError">← %h(zDeleteVerify)</span> @ </label></td> @ <tr> } @ <tr> @ <td> </td> @ <td><input type="submit" name="apply" value="Apply Changes"> if( !login_is_special(zLogin) ){ @ <input type="submit" name="delete" value="Delete User"> } @ <input type="submit" name="can" value="Cancel"></td> @ </tr> } @ </table> @ </div></form> @ </div> builtin_request_js("useredit.js"); @ <hr> @ <h1>Notes On Privileges And Capabilities:</h1> @ <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 @ ability to erase wiki, tickets, and attachments that have been added @ by anonymous users. This capability is intended for deletion of spam. @ The delete capability is only in effect for 24 hours after the item @ is first posted. The <span class="usertype">Setup</span> user can @ delete anything at any time. @ </p></li> @ @ <li><p> @ The <span class="capability">Hyperlinks</span> privilege allows a user @ to see most hyperlinks. This is recommended ON for most logged-in users @ but OFF for user "nobody" to avoid problems with spiders trying to walk @ every diff and annotation of every historical check-in and file. @ </p></li> @ @ <li><p> @ The <span class="capability">Zip</span> privilege allows a user to @ see the "download as ZIP" @ hyperlink and permits access to the <tt>/zip</tt> page. This allows @ users to download ZIP archives without granting other rights like @ <span class="capability">Read</span> or @ <span class="capability">Hyperlink</span>. The "z" privilege is recommended @ for user <span class="usertype">nobody</span> so that automatic package @ downloaders can obtain the sources without going through the login @ procedure. @ </p></li> @ @ <li><p> @ The <span class="capability">Check-in</span> privilege allows remote @ users to "push". The <span class="capability">Check-out</span> privilege @ allows remote users to "pull". The <span class="capability">Clone</span> @ privilege allows remote users to "clone". @ </p></li> @ @ <li><p> @ The <span class="capability">Read Wiki</span>, @ <span class="capability">New Wiki</span>, @ <span class="capability">Append Wiki</span>, and @ <b>Write Wiki</b> privileges control access to wiki pages. The @ <span class="capability">Read Ticket</span>, @ <span class="capability">New Ticket</span>, @ <span class="capability">Append Ticket</span>, and @ <span class="capability">Write Ticket</span> privileges control access @ to trouble tickets. @ The <span class="capability">Ticket Report</span> privilege allows @ the user to create or edit ticket report formats. @ </p></li> @ @ <li><p> @ Users with the <span class="capability">Password</span> privilege @ are allowed to change their own password. Recommended ON for most @ users but OFF for special users <span class="usertype">developer</span>, @ <span class="usertype">anonymous</span>, @ and <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ The <span class="capability">View-PII</span> privilege allows the display @ of personally-identifiable information information such as the @ email address of users and contact @ information on tickets. Recommended OFF for @ <span class="usertype">anonymous</span> and for @ <span class="usertype">nobody</span> but ON for @ <span class="usertype">developer</span>. @ </p></li> @ @ <li><p> @ The <span class="capability">Attachment</span> privilege is needed in @ order to add attachments to tickets or wiki. Write privilege on the @ ticket or wiki is also required. @ </p></li> @ @ <li><p> @ Login is prohibited if the password is an empty string. @ </p></li> @ </ul> @ @ <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 @ be able to login. Set the capabilities of the @ <span class="usertype">anonymous</span> @ user to things that you want any human to be able to do, but not any @ spider. Every other logged-in user inherits the privileges of @ <span class="usertype">anonymous</span>. @ </p></li> @ @ <li><p> @ The <span class="usertype">developer</span> user is intended as a template @ for trusted users with check-in privileges. When adding new trusted users, @ simply select the <span class="capability">developer</span> privilege to @ cause the new user to inherit all privileges of the @ <span class="usertype">developer</span> @ user. Similarly, the <span class="usertype">reader</span> user is a @ template for users who are allowed more access than @ <span class="usertype">anonymous</span>, @ but less than a <span class="usertype">developer</span>. @ </p></li> @ </ul> style_finish_page(); } |
Changes to src/sha1.c.
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 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 | /* ** 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 implementation of SHA1. */ #include "config.h" #include <sys/types.h> #include "sha1.h" /* ** SHA1 Implementation #1 is the hardened SHA1 implementation by ** Marc Stevens. Code obtained from GitHub ** ** https://github.com/cr-marcstevens/sha1collisiondetection ** ** Downloaded on 2017-03-01 then repackaged to work with Fossil ** and makeheaders. */ #if FOSSIL_HARDENED_SHA1 #if INTERFACE typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*); struct SHA1_CTX { uint64_t total; uint32_t ihv[5]; unsigned char buffer[64]; int bigendian; int found_collision; int safe_hash; int detect_coll; int ubc_check; int reduced_round_coll; collision_block_callback callback; uint32_t ihv1[5]; uint32_t ihv2[5]; uint32_t m1[80]; uint32_t m2[80]; uint32_t states[80][5]; }; #endif void SHA1DCInit(SHA1_CTX*); void SHA1DCUpdate(SHA1_CTX*, const unsigned char*, unsigned); int SHA1DCFinal(unsigned char[20], SHA1_CTX*); #define SHA1Context SHA1_CTX #define SHA1Init SHA1DCInit #define SHA1Update SHA1DCUpdate #define SHA1Final SHA1DCFinal /* ** SHA1 Implementation #2: use the SHA1 algorithm built into SSL */ #elif defined(FOSSIL_ENABLE_SSL) # include <openssl/sha.h> # define SHA1Context SHA_CTX # define SHA1Init SHA1_Init # define SHA1Update SHA1_Update # define SHA1Final SHA1_Final /* ** SHA1 Implementation #3: If none of the previous two SHA1 ** algorithms work, there is this built-in. This built-in was the ** original implementation used by Fossil. */ #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 $ ** ** SHA-1 in C ** By Steve Reid <steve@edmweb.com> ** 100% Public Domain */ typedef struct SHA1Context SHA1Context; struct SHA1Context { unsigned int state[5]; unsigned int count[2]; unsigned char buffer[64]; }; /* * blk0() and blk() perform the initial expand. * I got the idea of expanding during the round function from SSLeay * * blk0le() for little-endian and blk0be() for big-endian. */ #define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r)) #define rol(x,k) SHA_ROT(x,k,32-(k)) #define ror(x,k) SHA_ROT(x,32-(k),k) #define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \ |(rol(block[i],8)&0x00FF00FF)) #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) \ z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2); #define R2(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2); #define R3(v,w,x,y,z,i) \ z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2); #define R4(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2); /* * Hash a single 512-bit block. This is the core of the algorithm. */ #define a qq[0] #define b qq[1] #define c qq[2] #define d qq[3] #define e qq[4] void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]) { unsigned int qq[5]; /* a, b, c, d, e; */ static int one = 1; unsigned int block[16]; memcpy(block, buffer, 64); memcpy(qq,state,5*sizeof(unsigned int)); /* Copy context->state[] to working vars */ /* a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; */ /* 4 rounds of 20 operations each. Loop unrolled. */ if( 1 == *(unsigned char*)&one ){ Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); }else{ Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); } R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; } /* * SHA1Init - Initialize new context */ static void SHA1Init(SHA1Context *context){ /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* * Run your data through this. */ static void SHA1Update( SHA1Context *context, 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(unsigned char *digest, SHA1Context *context){ 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 /* Built-in SHA1 implemenation */ /* ** 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'; } /* ** The state of an incremental SHA1 checksum computation. Only one ** such computation can be underway at a time, of course. */ static SHA1Context incrCtx; static int incrInit = 0; /* ** Add more text to the incremental SHA1 checksum. */ void sha1sum_step_text(const char *zText, int nBytes){ if( !incrInit ){ SHA1Init(&incrCtx); incrInit = 1; } if( nBytes<=0 ){ if( nBytes==0 ) return; nBytes = strlen(zText); } SHA1Update(&incrCtx, (unsigned char*)zText, nBytes); } /* ** Add the content of a blob to the incremental SHA1 checksum. */ 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]; static char zOut[41]; sha1sum_step_text(0,0); SHA1Final(zResult, &incrCtx); incrInit = 0; DigestToBase16(zResult, zOut); if( pOut ){ blob_zero(pOut); blob_append(pOut, zOut, 40); } return zOut; } /* ** Compute the SHA1 checksum of a file on disk. Store the resulting ** checksum in the blob pCksum. pCksum is assumed to be initialized. ** ** Return the number of errors. */ int sha1sum_file(const char *zFilename, int eFType, Blob *pCksum){ FILE *in; SHA1Context ctx; unsigned char zResult[20]; char zBuf[10240]; if( eFType==RepoFILE && file_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"); if( in==0 ){ return 1; } SHA1Init(&ctx); for(;;){ int n; n = fread(zBuf, 1, sizeof(zBuf), in); if( n<=0 ) break; SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n); } fclose(in); blob_zero(pCksum); blob_resize(pCksum, 40); SHA1Final(zResult, &ctx); DigestToBase16(zResult, blob_buffer(pCksum)); return 0; } /* ** Compute the SHA1 checksum of a blob in memory. Store the resulting ** checksum in the blob pCksum. pCksum is assumed to be either ** uninitialized or the same blob as pIn. ** ** Return the number of errors. */ int sha1sum_blob(const Blob *pIn, Blob *pCksum){ SHA1Context ctx; unsigned char zResult[20]; SHA1Init(&ctx); SHA1Update(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn)); if( pIn==pCksum ){ blob_reset(pCksum); }else{ blob_zero(pCksum); } blob_resize(pCksum, 40); SHA1Final(zResult, &ctx); DigestToBase16(zResult, blob_buffer(pCksum)); return 0; } /* ** Compute a binary SHA1 checksum of a zero-terminated string. The ** result is stored in zOut, which is a buffer that must be at least ** 20 bytes in size. */ void sha1sum_binary(const char *zIn, unsigned char *zOut){ SHA1Context ctx; SHA1Init(&ctx); SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); SHA1Final(zOut, &ctx); } /* ** Compute the SHA1 checksum of a zero-terminated string. The ** result is held in memory obtained from mprintf(). */ char *sha1sum(const char *zIn){ SHA1Context ctx; unsigned char zResult[20]; char zDigest[41]; SHA1Init(&ctx); SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); SHA1Final(zResult, &ctx); 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 */ ){ static char *zProjectId = 0; SHA1Context ctx; unsigned char zResult[20]; char zDigest[41]; SHA1Init(&ctx); if( zProjCode==0 ){ if( zProjectId==0 ){ zProjectId = db_get("project-code", 0); /* On the first xfer request of a clone, the project-code is not yet ** known. Use the cleartext password, since that is all we have. */ if( zProjectId==0 ){ return mprintf("%s", zPw); } } zProjCode = zProjectId; } SHA1Update(&ctx, (unsigned char*)zProjCode, strlen(zProjCode)); SHA1Update(&ctx, (unsigned char*)"/", 1); SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin)); SHA1Update(&ctx, (unsigned char*)"/", 1); SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw)); SHA1Final(zResult, &ctx); DigestToBase16(zResult, zDigest); return mprintf("%s", zDigest); } /* ** Implement the shared_secret() SQL function. shared_secret() takes two or ** three arguments; the third argument is optional. ** ** (1) The cleartext password ** (2) The login name ** (3) The project code ** ** Returns sha1($password/$login/$projcode). */ void sha1_shared_secret_sql_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zPw; const char *zLogin; const char *zProjid; assert( argc==2 || argc==3 ); zPw = (const char*)sqlite3_value_text(argv[0]); if( zPw==0 || zPw[0]==0 ) return; zLogin = (const char*)sqlite3_value_text(argv[1]); if( zLogin==0 ) return; if( argc==3 ){ zProjid = (const char*)sqlite3_value_text(argv[2]); if( zProjid && zProjid[0]==0 ) zProjid = 0; }else{ zProjid = 0; } sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1, fossil_free); } /* ** COMMAND: sha1sum* ** ** Usage: %fossil sha1sum FILE... ** ** Compute an SHA1 checksum of all files named on the command-line. ** If a file is named "-" then take its content from standard input. ** ** Options: ** -h|--dereference If FILE is a symbolic link, compute the hash ** on the object that the link points to. Normally, ** the hash is over the name of the object that ** the link points to. ** ** See also: [[md5sum]], [[sha3sum]] */ void sha1sum_test(void){ int i; Blob in; Blob cksum; int eFType = SymFILE; if( find_option("dereference","h",0)!=0 ){ eFType = ExtFILE; } 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], eFType, &cksum); } fossil_print("%s %s\n", blob_str(&cksum), g.argv[i]); blob_reset(&cksum); } } |
Added src/sha1hard.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 | /* ** The code in this file is the concatenation of several files ** copied out of ** ** https://github.com/cr-marcstevens/sha1collisiondetection ** ** The copy was made on 2017-03-01. Some minor formatting changes ** were made but otherwise the code is unchanged. All ** original copyright claims are preserved. ** ** The code here implements a version of the SHA1 hash function that ** is not vulnerable to crypto-analysis based attacks. If an input ** is detected that looks like it might have been the result of a ** crypto-analysis attack, then the hash is perturbed to generate a ** completely different hash. The authors claim that the chance of ** a false-positive is vanishingly small. */ /*MAKEHEADERS-STOP*/ #include "config.h" #if FOSSIL_HARDENED_SHA1 /* Only do this code if requested */ /*************** File: lib/sha1.c ****************/ /*** * Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow (danshu@microsoft.com) * Distributed under the MIT Software License. * See accompanying file LICENSE.txt or copy at * https://opensource.org/licenses/MIT ***/ /*************** File: LICENSE.txt ***************/ /* ** MIT License ** ** Copyright (c) 2017: ** Marc Stevens ** Cryptology Group ** Centrum Wiskunde & Informatica ** P.O. Box 94079, 1090 GB Amsterdam, Netherlands ** marc@marc-stevens.nl ** ** Dan Shumow ** Microsoft Research ** danshu@microsoft.com ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal ** in the Software without restriction, including without limitation the rights ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ** copies of the Software, and to permit persons to whom the Software is ** furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in all ** copies or substantial portions of the Software. ** ** 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 OR COPYRIGHT HOLDERS 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. */ #include <string.h> #include <memory.h> #include <stdio.h> #define DVMASKSIZE 1 typedef struct { int dvType; int dvK; int dvB; int testt; int maski; int maskb; uint32_t dm[80]; } dv_info_t; #define DOSTORESTATE58 #define DOSTORESTATE65 typedef void(*sha1_recompression_type)(uint32_t*, uint32_t*, const uint32_t*, const uint32_t*); void sha1_message_expansion(uint32_t W[80]); void sha1_compression(uint32_t ihv[5], const uint32_t m[16]); void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]); void sha1_compression_states(uint32_t ihv[5], const uint32_t W[80], uint32_t states[80][5]); extern sha1_recompression_type sha1_recompression_step[80]; typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*); typedef struct { uint64_t total; uint32_t ihv[5]; unsigned char buffer[64]; int bigendian; int found_collision; int safe_hash; int detect_coll; int ubc_check; int reduced_round_coll; collision_block_callback callback; uint32_t ihv1[5]; uint32_t ihv2[5]; uint32_t m1[80]; uint32_t m2[80]; uint32_t states[80][5]; } SHA1_CTX; /******************** File: lib/ubc_check.c **************************/ /*** * Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com> * Distributed under the MIT Software License. * See accompanying file LICENSE.txt or copy at * https://opensource.org/licenses/MIT ***/ /* ** this file was generated by the 'parse_bitrel' program in the tools section ** using the data files from directory 'tools/data/3565' ** ** sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check ** dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper) ** dm[80] is the expanded message block XOR-difference defined by the DV ** testt is the step to do the recompression from for collision detection ** maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check ** ** ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs ** it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met ** thus one needs to do the recompression check for each DV that has its bit set ** ** ubc_check is programmatically generated and the unavoidable bitconditions have been hardcoded ** a directly verifiable version named ubc_check_verify can be found in ubc_check_verify.c ** ubc_check has been verified against ubc_check_verify using the 'ubc_check_test' program in the tools section */ static const uint32_t DV_I_43_0_bit = (uint32_t)(1) << 0; static const uint32_t DV_I_44_0_bit = (uint32_t)(1) << 1; static const uint32_t DV_I_45_0_bit = (uint32_t)(1) << 2; static const uint32_t DV_I_46_0_bit = (uint32_t)(1) << 3; static const uint32_t DV_I_46_2_bit = (uint32_t)(1) << 4; static const uint32_t DV_I_47_0_bit = (uint32_t)(1) << 5; static const uint32_t DV_I_47_2_bit = (uint32_t)(1) << 6; static const uint32_t DV_I_48_0_bit = (uint32_t)(1) << 7; static const uint32_t DV_I_48_2_bit = (uint32_t)(1) << 8; static const uint32_t DV_I_49_0_bit = (uint32_t)(1) << 9; static const uint32_t DV_I_49_2_bit = (uint32_t)(1) << 10; static const uint32_t DV_I_50_0_bit = (uint32_t)(1) << 11; static const uint32_t DV_I_50_2_bit = (uint32_t)(1) << 12; static const uint32_t DV_I_51_0_bit = (uint32_t)(1) << 13; static const uint32_t DV_I_51_2_bit = (uint32_t)(1) << 14; static const uint32_t DV_I_52_0_bit = (uint32_t)(1) << 15; static const uint32_t DV_II_45_0_bit = (uint32_t)(1) << 16; static const uint32_t DV_II_46_0_bit = (uint32_t)(1) << 17; static const uint32_t DV_II_46_2_bit = (uint32_t)(1) << 18; static const uint32_t DV_II_47_0_bit = (uint32_t)(1) << 19; static const uint32_t DV_II_48_0_bit = (uint32_t)(1) << 20; static const uint32_t DV_II_49_0_bit = (uint32_t)(1) << 21; static const uint32_t DV_II_49_2_bit = (uint32_t)(1) << 22; static const uint32_t DV_II_50_0_bit = (uint32_t)(1) << 23; static const uint32_t DV_II_50_2_bit = (uint32_t)(1) << 24; static const uint32_t DV_II_51_0_bit = (uint32_t)(1) << 25; static const uint32_t DV_II_51_2_bit = (uint32_t)(1) << 26; static const uint32_t DV_II_52_0_bit = (uint32_t)(1) << 27; static const uint32_t DV_II_53_0_bit = (uint32_t)(1) << 28; static const uint32_t DV_II_54_0_bit = (uint32_t)(1) << 29; static const uint32_t DV_II_55_0_bit = (uint32_t)(1) << 30; static const uint32_t DV_II_56_0_bit = (uint32_t)(1) << 31; dv_info_t sha1_dvs[] = { {1,43,0,58,0,0, { 0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161,0x80000599 } } , {1,44,0,58,0,1, { 0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161 } } , {1,45,0,58,0,2, { 0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803 } } , {1,46,0,58,0,3, { 0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c } } , {1,46,2,58,0,4, { 0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a,0x00000132 } } , {1,47,0,58,0,5, { 0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6 } } , {1,47,2,58,0,6, { 0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a } } , {1,48,0,58,0,7, { 0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408 } } , {1,48,2,58,0,8, { 0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020 } } , {1,49,0,58,0,9, { 0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164 } } , {1,49,2,58,0,10, { 0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590 } } , {1,50,0,65,0,11, { 0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018 } } , {1,50,2,65,0,12, { 0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060 } } , {1,51,0,65,0,13, { 0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202 } } , {1,51,2,65,0,14, { 0xa0000003,0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a } } , {1,52,0,65,0,15, { 0x04000010,0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012 } } , {2,45,0,58,0,16, { 0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054,0x00000967 } } , {2,46,0,58,0,17, { 0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054 } } , {2,46,2,58,0,18, { 0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6,0x0000106a,0x00000b90,0x00000152 } } , {2,47,0,58,0,19, { 0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4 } } , {2,48,0,58,0,20, { 0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a } } , {2,49,0,58,0,21, { 0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d } } , {2,49,2,58,0,22, { 0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6 } } , {2,50,0,65,0,23, { 0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b } } , {2,50,2,65,0,24, { 0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c } } , {2,51,0,65,0,25, { 0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b } } , {2,51,2,65,0,26, { 0x00000043,0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e } } , {2,52,0,65,0,27, { 0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014 } } , {2,53,0,65,0,28, { 0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089 } } , {2,54,0,65,0,29, { 0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107 } } , {2,55,0,65,0,30, { 0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b } } , {2,56,0,65,0,31, { 0x2600001a,0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046 } } , {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}} }; void ubc_check(const uint32_t W[80], uint32_t dvmask[1]) { uint32_t mask = ~((uint32_t)(0)); mask &= (((((W[44]^W[45])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_I_51_0_bit|DV_I_52_0_bit|DV_II_45_0_bit|DV_II_46_0_bit|DV_II_50_0_bit|DV_II_51_0_bit)); mask &= (((((W[49]^W[50])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_II_45_0_bit|DV_II_50_0_bit|DV_II_51_0_bit|DV_II_55_0_bit|DV_II_56_0_bit)); mask &= (((((W[48]^W[49])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_52_0_bit|DV_II_49_0_bit|DV_II_50_0_bit|DV_II_54_0_bit|DV_II_55_0_bit)); mask &= ((((W[47]^(W[50]>>25))&(1<<4))-(1<<4)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)); mask &= (((((W[47]^W[48])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_51_0_bit|DV_II_48_0_bit|DV_II_49_0_bit|DV_II_53_0_bit|DV_II_54_0_bit)); mask &= (((((W[46]>>4)^(W[49]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_50_0_bit|DV_II_55_0_bit)); mask &= (((((W[46]^W[47])>>29)&1)-1) | ~(DV_I_43_0_bit|DV_I_50_0_bit|DV_II_47_0_bit|DV_II_48_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)); mask &= (((((W[45]>>4)^(W[48]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_49_0_bit|DV_II_54_0_bit)); mask &= (((((W[45]^W[46])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_51_0_bit|DV_II_52_0_bit)); mask &= (((((W[44]>>4)^(W[47]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_53_0_bit)); mask &= (((((W[43]>>4)^(W[46]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_52_0_bit)); mask &= (((((W[43]^W[44])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_I_50_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_49_0_bit|DV_II_50_0_bit)); mask &= (((((W[42]>>4)^(W[45]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_51_0_bit)); mask &= (((((W[41]>>4)^(W[44]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_50_0_bit)); mask &= (((((W[40]^W[41])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_47_0_bit|DV_I_48_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_56_0_bit)); mask &= (((((W[54]^W[55])>>29)&1)-1) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_50_0_bit|DV_II_55_0_bit|DV_II_56_0_bit)); mask &= (((((W[53]^W[54])>>29)&1)-1) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_49_0_bit|DV_II_54_0_bit|DV_II_55_0_bit)); mask &= (((((W[52]^W[53])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit|DV_II_53_0_bit|DV_II_54_0_bit)); mask &= ((((W[50]^(W[53]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_48_0_bit|DV_II_54_0_bit)); mask &= (((((W[50]^W[51])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_II_46_0_bit|DV_II_51_0_bit|DV_II_52_0_bit|DV_II_56_0_bit)); mask &= ((((W[49]^(W[52]>>25))&(1<<4))-(1<<4)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_47_0_bit|DV_II_53_0_bit)); mask &= ((((W[48]^(W[51]>>25))&(1<<4))-(1<<4)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_52_0_bit)); mask &= (((((W[42]^W[43])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)); mask &= (((((W[41]^W[42])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_48_0_bit)); mask &= (((((W[40]>>4)^(W[43]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_50_0_bit|DV_II_49_0_bit|DV_II_56_0_bit)); mask &= (((((W[39]>>4)^(W[42]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_49_0_bit|DV_II_48_0_bit|DV_II_55_0_bit)); if (mask & (DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)) mask &= (((((W[38]>>4)^(W[41]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)); mask &= (((((W[37]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_47_0_bit|DV_II_46_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)); if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)) mask &= (((((W[55]^W[56])>>29)&1)-1) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit)); if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit)) mask &= ((((W[52]^(W[55]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit)); if (mask & (DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit)) mask &= ((((W[51]^(W[54]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit)); if (mask & (DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)) mask &= (((((W[51]^W[52])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit)); if (mask & (DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit)) mask &= (((((W[36]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit)); if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)) mask &= ((0-(((W[53]^W[56])>>29)&1)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit)); if (mask & (DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit)) mask &= ((0-(((W[51]^W[54])>>29)&1)) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit)); if (mask & (DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit)) mask &= ((0-(((W[50]^W[52])>>29)&1)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit)); if (mask & (DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit)) mask &= ((0-(((W[49]^W[51])>>29)&1)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit)); if (mask & (DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit)) mask &= ((0-(((W[48]^W[50])>>29)&1)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit)); if (mask & (DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit)) mask &= ((0-(((W[47]^W[49])>>29)&1)) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit)); if (mask & (DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit)) mask &= ((0-(((W[46]^W[48])>>29)&1)) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit)); mask &= ((((W[45]^W[47])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit|DV_I_51_2_bit)); if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit)) mask &= ((0-(((W[45]^W[47])>>29)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit)); mask &= (((((W[44]^W[46])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit|DV_I_50_2_bit)); if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit)) mask &= ((0-(((W[44]^W[46])>>29)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit)); mask &= ((0-((W[41]^(W[42]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_II_46_2_bit|DV_II_51_2_bit)); mask &= ((0-((W[40]^(W[41]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_51_2_bit|DV_II_50_2_bit)); if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit)) mask &= ((0-(((W[40]^W[42])>>4)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit)); mask &= ((0-((W[39]^(W[40]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_50_2_bit|DV_II_49_2_bit)); if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit)) mask &= ((0-(((W[39]^W[41])>>4)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit)); if (mask & (DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)) mask &= ((0-(((W[38]^W[40])>>4)&1)) | ~(DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit)); if (mask & (DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)) mask &= ((0-(((W[37]^W[39])>>4)&1)) | ~(DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit)); mask &= ((0-((W[36]^(W[37]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_50_2_bit|DV_II_46_2_bit)); if (mask & (DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit)) mask &= (((((W[35]>>4)^(W[39]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit)); if (mask & (DV_I_48_0_bit|DV_II_48_0_bit)) mask &= ((0-((W[63]^(W[64]>>5))&(1<<0))) | ~(DV_I_48_0_bit|DV_II_48_0_bit)); if (mask & (DV_I_45_0_bit|DV_II_45_0_bit)) mask &= ((0-((W[63]^(W[64]>>5))&(1<<1))) | ~(DV_I_45_0_bit|DV_II_45_0_bit)); if (mask & (DV_I_47_0_bit|DV_II_47_0_bit)) mask &= ((0-((W[62]^(W[63]>>5))&(1<<0))) | ~(DV_I_47_0_bit|DV_II_47_0_bit)); if (mask & (DV_I_46_0_bit|DV_II_46_0_bit)) mask &= ((0-((W[61]^(W[62]>>5))&(1<<0))) | ~(DV_I_46_0_bit|DV_II_46_0_bit)); mask &= ((0-((W[61]^(W[62]>>5))&(1<<2))) | ~(DV_I_46_2_bit|DV_II_46_2_bit)); if (mask & (DV_I_45_0_bit|DV_II_45_0_bit)) mask &= ((0-((W[60]^(W[61]>>5))&(1<<0))) | ~(DV_I_45_0_bit|DV_II_45_0_bit)); if (mask & (DV_II_51_0_bit|DV_II_54_0_bit)) mask &= (((((W[58]^W[59])>>29)&1)-1) | ~(DV_II_51_0_bit|DV_II_54_0_bit)); if (mask & (DV_II_50_0_bit|DV_II_53_0_bit)) mask &= (((((W[57]^W[58])>>29)&1)-1) | ~(DV_II_50_0_bit|DV_II_53_0_bit)); if (mask & (DV_II_52_0_bit|DV_II_54_0_bit)) mask &= ((((W[56]^(W[59]>>25))&(1<<4))-(1<<4)) | ~(DV_II_52_0_bit|DV_II_54_0_bit)); if (mask & (DV_II_51_0_bit|DV_II_52_0_bit)) mask &= ((0-(((W[56]^W[59])>>29)&1)) | ~(DV_II_51_0_bit|DV_II_52_0_bit)); if (mask & (DV_II_49_0_bit|DV_II_52_0_bit)) mask &= (((((W[56]^W[57])>>29)&1)-1) | ~(DV_II_49_0_bit|DV_II_52_0_bit)); if (mask & (DV_II_51_0_bit|DV_II_53_0_bit)) mask &= ((((W[55]^(W[58]>>25))&(1<<4))-(1<<4)) | ~(DV_II_51_0_bit|DV_II_53_0_bit)); if (mask & (DV_II_50_0_bit|DV_II_52_0_bit)) mask &= ((((W[54]^(W[57]>>25))&(1<<4))-(1<<4)) | ~(DV_II_50_0_bit|DV_II_52_0_bit)); if (mask & (DV_II_49_0_bit|DV_II_51_0_bit)) mask &= ((((W[53]^(W[56]>>25))&(1<<4))-(1<<4)) | ~(DV_II_49_0_bit|DV_II_51_0_bit)); mask &= ((((W[51]^(W[50]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_46_2_bit)); mask &= ((((W[48]^W[50])&(1<<6))-(1<<6)) | ~(DV_I_50_2_bit|DV_II_46_2_bit)); if (mask & (DV_I_51_0_bit|DV_I_52_0_bit)) mask &= ((0-(((W[48]^W[55])>>29)&1)) | ~(DV_I_51_0_bit|DV_I_52_0_bit)); mask &= ((((W[47]^W[49])&(1<<6))-(1<<6)) | ~(DV_I_49_2_bit|DV_I_51_2_bit)); mask &= ((((W[48]^(W[47]>>5))&(1<<1))-(1<<1)) | ~(DV_I_47_2_bit|DV_II_51_2_bit)); mask &= ((((W[46]^W[48])&(1<<6))-(1<<6)) | ~(DV_I_48_2_bit|DV_I_50_2_bit)); mask &= ((((W[47]^(W[46]>>5))&(1<<1))-(1<<1)) | ~(DV_I_46_2_bit|DV_II_50_2_bit)); mask &= ((0-((W[44]^(W[45]>>5))&(1<<1))) | ~(DV_I_51_2_bit|DV_II_49_2_bit)); mask &= ((((W[43]^W[45])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit)); mask &= (((((W[42]^W[44])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit)); mask &= ((((W[43]^(W[42]>>5))&(1<<1))-(1<<1)) | ~(DV_II_46_2_bit|DV_II_51_2_bit)); mask &= ((((W[42]^(W[41]>>5))&(1<<1))-(1<<1)) | ~(DV_I_51_2_bit|DV_II_50_2_bit)); mask &= ((((W[41]^(W[40]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_49_2_bit)); if (mask & (DV_I_52_0_bit|DV_II_51_0_bit)) mask &= ((((W[39]^(W[43]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_51_0_bit)); if (mask & (DV_I_51_0_bit|DV_II_50_0_bit)) mask &= ((((W[38]^(W[42]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_50_0_bit)); if (mask & (DV_I_48_2_bit|DV_I_51_2_bit)) mask &= ((0-((W[37]^(W[38]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_I_51_2_bit)); if (mask & (DV_I_50_0_bit|DV_II_49_0_bit)) mask &= ((((W[37]^(W[41]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_II_49_0_bit)); if (mask & (DV_II_52_0_bit|DV_II_54_0_bit)) mask &= ((0-((W[36]^W[38])&(1<<4))) | ~(DV_II_52_0_bit|DV_II_54_0_bit)); mask &= ((0-((W[35]^(W[36]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_49_2_bit)); if (mask & (DV_I_51_0_bit|DV_II_47_0_bit)) mask &= ((((W[35]^(W[39]>>25))&(1<<3))-(1<<3)) | ~(DV_I_51_0_bit|DV_II_47_0_bit)); if (mask) { if (mask & DV_I_43_0_bit) if ( !((W[61]^(W[62]>>5)) & (1<<1)) || !(!((W[59]^(W[63]>>25)) & (1<<5))) || !((W[58]^(W[63]>>30)) & (1<<0)) ) mask &= ~DV_I_43_0_bit; if (mask & DV_I_44_0_bit) if ( !((W[62]^(W[63]>>5)) & (1<<1)) || !(!((W[60]^(W[64]>>25)) & (1<<5))) || !((W[59]^(W[64]>>30)) & (1<<0)) ) mask &= ~DV_I_44_0_bit; if (mask & DV_I_46_2_bit) mask &= ((~((W[40]^W[42])>>2)) | ~DV_I_46_2_bit); if (mask & DV_I_47_2_bit) if ( !((W[62]^(W[63]>>5)) & (1<<2)) || !(!((W[41]^W[43]) & (1<<6))) ) mask &= ~DV_I_47_2_bit; if (mask & DV_I_48_2_bit) if ( !((W[63]^(W[64]>>5)) & (1<<2)) || !(!((W[48]^(W[49]<<5)) & (1<<6))) ) mask &= ~DV_I_48_2_bit; if (mask & DV_I_49_2_bit) if ( !(!((W[49]^(W[50]<<5)) & (1<<6))) || !((W[42]^W[50]) & (1<<1)) || !(!((W[39]^(W[40]<<5)) & (1<<6))) || !((W[38]^W[40]) & (1<<1)) ) mask &= ~DV_I_49_2_bit; if (mask & DV_I_50_0_bit) mask &= ((((W[36]^W[37])<<7)) | ~DV_I_50_0_bit); if (mask & DV_I_50_2_bit) mask &= ((((W[43]^W[51])<<11)) | ~DV_I_50_2_bit); if (mask & DV_I_51_0_bit) mask &= ((((W[37]^W[38])<<9)) | ~DV_I_51_0_bit); if (mask & DV_I_51_2_bit) if ( !(!((W[51]^(W[52]<<5)) & (1<<6))) || !(!((W[49]^W[51]) & (1<<6))) || !(!((W[37]^(W[37]>>5)) & (1<<1))) || !(!((W[35]^(W[39]>>25)) & (1<<5))) ) mask &= ~DV_I_51_2_bit; if (mask & DV_I_52_0_bit) mask &= ((((W[38]^W[39])<<11)) | ~DV_I_52_0_bit); if (mask & DV_II_46_2_bit) mask &= ((((W[47]^W[51])<<17)) | ~DV_II_46_2_bit); if (mask & DV_II_48_0_bit) if ( !(!((W[36]^(W[40]>>25)) & (1<<3))) || !((W[35]^(W[40]<<2)) & (1<<30)) ) mask &= ~DV_II_48_0_bit; if (mask & DV_II_49_0_bit) if ( !(!((W[37]^(W[41]>>25)) & (1<<3))) || !((W[36]^(W[41]<<2)) & (1<<30)) ) mask &= ~DV_II_49_0_bit; if (mask & DV_II_49_2_bit) if ( !(!((W[53]^(W[54]<<5)) & (1<<6))) || !(!((W[51]^W[53]) & (1<<6))) || !((W[50]^W[54]) & (1<<1)) || !(!((W[45]^(W[46]<<5)) & (1<<6))) || !(!((W[37]^(W[41]>>25)) & (1<<5))) || !((W[36]^(W[41]>>30)) & (1<<0)) ) mask &= ~DV_II_49_2_bit; if (mask & DV_II_50_0_bit) if ( !((W[55]^W[58]) & (1<<29)) || !(!((W[38]^(W[42]>>25)) & (1<<3))) || !((W[37]^(W[42]<<2)) & (1<<30)) ) mask &= ~DV_II_50_0_bit; if (mask & DV_II_50_2_bit) if ( !(!((W[54]^(W[55]<<5)) & (1<<6))) || !(!((W[52]^W[54]) & (1<<6))) || !((W[51]^W[55]) & (1<<1)) || !((W[45]^W[47]) & (1<<1)) || !(!((W[38]^(W[42]>>25)) & (1<<5))) || !((W[37]^(W[42]>>30)) & (1<<0)) ) mask &= ~DV_II_50_2_bit; if (mask & DV_II_51_0_bit) if ( !(!((W[39]^(W[43]>>25)) & (1<<3))) || !((W[38]^(W[43]<<2)) & (1<<30)) ) mask &= ~DV_II_51_0_bit; if (mask & DV_II_51_2_bit) if ( !(!((W[55]^(W[56]<<5)) & (1<<6))) || !(!((W[53]^W[55]) & (1<<6))) || !((W[52]^W[56]) & (1<<1)) || !((W[46]^W[48]) & (1<<1)) || !(!((W[39]^(W[43]>>25)) & (1<<5))) || !((W[38]^(W[43]>>30)) & (1<<0)) ) mask &= ~DV_II_51_2_bit; if (mask & DV_II_52_0_bit) if ( !(!((W[59]^W[60]) & (1<<29))) || !(!((W[40]^(W[44]>>25)) & (1<<3))) || !(!((W[40]^(W[44]>>25)) & (1<<4))) || !((W[39]^(W[44]<<2)) & (1<<30)) ) mask &= ~DV_II_52_0_bit; if (mask & DV_II_53_0_bit) if ( !((W[58]^W[61]) & (1<<29)) || !(!((W[57]^(W[61]>>25)) & (1<<4))) || !(!((W[41]^(W[45]>>25)) & (1<<3))) || !(!((W[41]^(W[45]>>25)) & (1<<4))) ) mask &= ~DV_II_53_0_bit; if (mask & DV_II_54_0_bit) if ( !(!((W[58]^(W[62]>>25)) & (1<<4))) || !(!((W[42]^(W[46]>>25)) & (1<<3))) || !(!((W[42]^(W[46]>>25)) & (1<<4))) ) mask &= ~DV_II_54_0_bit; if (mask & DV_II_55_0_bit) if ( !(!((W[59]^(W[63]>>25)) & (1<<4))) || !(!((W[57]^(W[59]>>25)) & (1<<4))) || !(!((W[43]^(W[47]>>25)) & (1<<3))) || !(!((W[43]^(W[47]>>25)) & (1<<4))) ) mask &= ~DV_II_55_0_bit; if (mask & DV_II_56_0_bit) if ( !(!((W[60]^(W[64]>>25)) & (1<<4))) || !(!((W[44]^(W[48]>>25)) & (1<<3))) || !(!((W[44]^(W[48]>>25)) & (1<<4))) ) mask &= ~DV_II_56_0_bit; } dvmask[0]=mask; } /******************** End Of File: lib/ubc_check.c *******************/ /******************** Continue with: lib/sha1.c **********************/ #define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n)))) #define rotate_left(x,n) (((x)<<(n))|((x)>>(32-(n)))) #define sha1_f1(b,c,d) ((d)^((b)&((c)^(d)))) #define sha1_f2(b,c,d) ((b)^(c)^(d)) #define sha1_f3(b,c,d) (((b) & ((c)|(d))) | ((c)&(d))) #define sha1_f4(b,c,d) ((b)^(c)^(d)) #define HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, m, t) \ { e += rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; b = rotate_left(b, 30); } #define HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, m, t) \ { e += rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; b = rotate_left(b, 30); } #define HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, m, t) \ { e += rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; b = rotate_left(b, 30); } #define HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, m, t) \ { e += rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; b = rotate_left(b, 30); } #define HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, m, t) \ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; } #define HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, m, t) \ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; } #define HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, m, t) \ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; } #define HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, m, t) \ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; } #define SHA1_STORE_STATE(i) states[i][0] = a; states[i][1] = b; states[i][2] = c; states[i][3] = d; states[i][4] = e; void sha1_message_expansion(uint32_t W[80]) { unsigned i; for (i = 16; i < 80; ++i) W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); } void sha1_compression(uint32_t ihv[5], const uint32_t m[16]) { uint32_t W[80]; uint32_t a,b,c,d,e; unsigned i; memcpy(W, m, 16 * 4); for (i = 16; i < 80; ++i) W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); a = ihv[0]; b = ihv[1]; c = ihv[2]; d = ihv[3]; e = ihv[4]; HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79); ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; } void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]) { uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4]; HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18); HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38); HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58); HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78); HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79); ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; } void sha1_compression_states(uint32_t ihv[5], const uint32_t W[80], uint32_t states[80][5]) { uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4]; #ifdef DOSTORESTATE00 SHA1_STORE_STATE(0) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0); #ifdef DOSTORESTATE01 SHA1_STORE_STATE(1) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1); #ifdef DOSTORESTATE02 SHA1_STORE_STATE(2) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2); #ifdef DOSTORESTATE03 SHA1_STORE_STATE(3) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3); #ifdef DOSTORESTATE04 SHA1_STORE_STATE(4) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4); #ifdef DOSTORESTATE05 SHA1_STORE_STATE(5) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5); #ifdef DOSTORESTATE06 SHA1_STORE_STATE(6) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6); #ifdef DOSTORESTATE07 SHA1_STORE_STATE(7) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7); #ifdef DOSTORESTATE08 SHA1_STORE_STATE(8) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8); #ifdef DOSTORESTATE09 SHA1_STORE_STATE(9) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9); #ifdef DOSTORESTATE10 SHA1_STORE_STATE(10) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10); #ifdef DOSTORESTATE11 SHA1_STORE_STATE(11) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11); #ifdef DOSTORESTATE12 SHA1_STORE_STATE(12) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12); #ifdef DOSTORESTATE13 SHA1_STORE_STATE(13) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13); #ifdef DOSTORESTATE14 SHA1_STORE_STATE(14) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14); #ifdef DOSTORESTATE15 SHA1_STORE_STATE(15) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15); #ifdef DOSTORESTATE16 SHA1_STORE_STATE(16) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16); #ifdef DOSTORESTATE17 SHA1_STORE_STATE(17) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17); #ifdef DOSTORESTATE18 SHA1_STORE_STATE(18) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18); #ifdef DOSTORESTATE19 SHA1_STORE_STATE(19) #endif HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19); #ifdef DOSTORESTATE20 SHA1_STORE_STATE(20) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20); #ifdef DOSTORESTATE21 SHA1_STORE_STATE(21) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21); #ifdef DOSTORESTATE22 SHA1_STORE_STATE(22) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22); #ifdef DOSTORESTATE23 SHA1_STORE_STATE(23) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23); #ifdef DOSTORESTATE24 SHA1_STORE_STATE(24) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24); #ifdef DOSTORESTATE25 SHA1_STORE_STATE(25) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25); #ifdef DOSTORESTATE26 SHA1_STORE_STATE(26) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26); #ifdef DOSTORESTATE27 SHA1_STORE_STATE(27) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27); #ifdef DOSTORESTATE28 SHA1_STORE_STATE(28) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28); #ifdef DOSTORESTATE29 SHA1_STORE_STATE(29) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29); #ifdef DOSTORESTATE30 SHA1_STORE_STATE(30) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30); #ifdef DOSTORESTATE31 SHA1_STORE_STATE(31) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31); #ifdef DOSTORESTATE32 SHA1_STORE_STATE(32) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32); #ifdef DOSTORESTATE33 SHA1_STORE_STATE(33) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33); #ifdef DOSTORESTATE34 SHA1_STORE_STATE(34) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34); #ifdef DOSTORESTATE35 SHA1_STORE_STATE(35) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35); #ifdef DOSTORESTATE36 SHA1_STORE_STATE(36) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36); #ifdef DOSTORESTATE37 SHA1_STORE_STATE(37) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37); #ifdef DOSTORESTATE38 SHA1_STORE_STATE(38) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38); #ifdef DOSTORESTATE39 SHA1_STORE_STATE(39) #endif HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39); #ifdef DOSTORESTATE40 SHA1_STORE_STATE(40) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40); #ifdef DOSTORESTATE41 SHA1_STORE_STATE(41) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41); #ifdef DOSTORESTATE42 SHA1_STORE_STATE(42) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42); #ifdef DOSTORESTATE43 SHA1_STORE_STATE(43) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43); #ifdef DOSTORESTATE44 SHA1_STORE_STATE(44) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44); #ifdef DOSTORESTATE45 SHA1_STORE_STATE(45) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45); #ifdef DOSTORESTATE46 SHA1_STORE_STATE(46) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46); #ifdef DOSTORESTATE47 SHA1_STORE_STATE(47) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47); #ifdef DOSTORESTATE48 SHA1_STORE_STATE(48) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48); #ifdef DOSTORESTATE49 SHA1_STORE_STATE(49) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49); #ifdef DOSTORESTATE50 SHA1_STORE_STATE(50) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50); #ifdef DOSTORESTATE51 SHA1_STORE_STATE(51) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51); #ifdef DOSTORESTATE52 SHA1_STORE_STATE(52) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52); #ifdef DOSTORESTATE53 SHA1_STORE_STATE(53) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53); #ifdef DOSTORESTATE54 SHA1_STORE_STATE(54) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54); #ifdef DOSTORESTATE55 SHA1_STORE_STATE(55) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55); #ifdef DOSTORESTATE56 SHA1_STORE_STATE(56) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56); #ifdef DOSTORESTATE57 SHA1_STORE_STATE(57) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57); #ifdef DOSTORESTATE58 SHA1_STORE_STATE(58) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58); #ifdef DOSTORESTATE59 SHA1_STORE_STATE(59) #endif HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59); #ifdef DOSTORESTATE60 SHA1_STORE_STATE(60) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60); #ifdef DOSTORESTATE61 SHA1_STORE_STATE(61) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61); #ifdef DOSTORESTATE62 SHA1_STORE_STATE(62) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62); #ifdef DOSTORESTATE63 SHA1_STORE_STATE(63) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63); #ifdef DOSTORESTATE64 SHA1_STORE_STATE(64) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64); #ifdef DOSTORESTATE65 SHA1_STORE_STATE(65) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65); #ifdef DOSTORESTATE66 SHA1_STORE_STATE(66) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66); #ifdef DOSTORESTATE67 SHA1_STORE_STATE(67) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67); #ifdef DOSTORESTATE68 SHA1_STORE_STATE(68) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68); #ifdef DOSTORESTATE69 SHA1_STORE_STATE(69) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69); #ifdef DOSTORESTATE70 SHA1_STORE_STATE(70) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70); #ifdef DOSTORESTATE71 SHA1_STORE_STATE(71) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71); #ifdef DOSTORESTATE72 SHA1_STORE_STATE(72) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72); #ifdef DOSTORESTATE73 SHA1_STORE_STATE(73) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73); #ifdef DOSTORESTATE74 SHA1_STORE_STATE(74) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74); #ifdef DOSTORESTATE75 SHA1_STORE_STATE(75) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75); #ifdef DOSTORESTATE76 SHA1_STORE_STATE(76) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76); #ifdef DOSTORESTATE77 SHA1_STORE_STATE(77) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77); #ifdef DOSTORESTATE78 SHA1_STORE_STATE(78) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78); #ifdef DOSTORESTATE79 SHA1_STORE_STATE(79) #endif HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79); ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e; } #define SHA1_RECOMPRESS(t) \ void sha1recompress_fast_ ## t (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) \ { \ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; \ if (t > 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 79); \ if (t > 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 78); \ if (t > 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 77); \ if (t > 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 76); \ if (t > 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 75); \ if (t > 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 74); \ if (t > 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 73); \ if (t > 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 72); \ if (t > 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 71); \ if (t > 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 70); \ if (t > 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 69); \ if (t > 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 68); \ if (t > 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 67); \ if (t > 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 66); \ if (t > 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 65); \ if (t > 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 64); \ if (t > 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 63); \ if (t > 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 62); \ if (t > 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 61); \ if (t > 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 60); \ if (t > 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 59); \ if (t > 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 58); \ if (t > 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 57); \ if (t > 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 56); \ if (t > 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 55); \ if (t > 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 54); \ if (t > 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 53); \ if (t > 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 52); \ if (t > 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 51); \ if (t > 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 50); \ if (t > 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 49); \ if (t > 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 48); \ if (t > 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 47); \ if (t > 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 46); \ if (t > 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 45); \ if (t > 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 44); \ if (t > 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 43); \ if (t > 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 42); \ if (t > 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 41); \ if (t > 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 40); \ if (t > 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 39); \ if (t > 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 38); \ if (t > 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 37); \ if (t > 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 36); \ if (t > 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 35); \ if (t > 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 34); \ if (t > 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 33); \ if (t > 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 32); \ if (t > 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 31); \ if (t > 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 30); \ if (t > 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 29); \ if (t > 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 28); \ if (t > 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 27); \ if (t > 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 26); \ if (t > 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 25); \ if (t > 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 24); \ if (t > 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 23); \ if (t > 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 22); \ if (t > 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 21); \ if (t > 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 20); \ if (t > 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 19); \ if (t > 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 18); \ if (t > 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 17); \ if (t > 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 16); \ if (t > 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 15); \ if (t > 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 14); \ if (t > 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 13); \ if (t > 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 12); \ if (t > 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 11); \ if (t > 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 10); \ if (t > 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 9); \ if (t > 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 8); \ if (t > 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 7); \ if (t > 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 6); \ if (t > 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 5); \ if (t > 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 4); \ if (t > 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 3); \ if (t > 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 2); \ if (t > 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 1); \ if (t > 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 0); \ ihvin[0] = a; ihvin[1] = b; ihvin[2] = c; ihvin[3] = d; ihvin[4] = e; \ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; \ if (t <= 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 0); \ if (t <= 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 1); \ if (t <= 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 2); \ if (t <= 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 3); \ if (t <= 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 4); \ if (t <= 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 5); \ if (t <= 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 6); \ if (t <= 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 7); \ if (t <= 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 8); \ if (t <= 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 9); \ if (t <= 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 10); \ if (t <= 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 11); \ if (t <= 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 12); \ if (t <= 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 13); \ if (t <= 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 14); \ if (t <= 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 15); \ if (t <= 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 16); \ if (t <= 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 17); \ if (t <= 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 18); \ if (t <= 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 19); \ if (t <= 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 20); \ if (t <= 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 21); \ if (t <= 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 22); \ if (t <= 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 23); \ if (t <= 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 24); \ if (t <= 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 25); \ if (t <= 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 26); \ if (t <= 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 27); \ if (t <= 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 28); \ if (t <= 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 29); \ if (t <= 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 30); \ if (t <= 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 31); \ if (t <= 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 32); \ if (t <= 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 33); \ if (t <= 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 34); \ if (t <= 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 35); \ if (t <= 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 36); \ if (t <= 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 37); \ if (t <= 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 38); \ if (t <= 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 39); \ if (t <= 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 40); \ if (t <= 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 41); \ if (t <= 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 42); \ if (t <= 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 43); \ if (t <= 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 44); \ if (t <= 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 45); \ if (t <= 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 46); \ if (t <= 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 47); \ if (t <= 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 48); \ if (t <= 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 49); \ if (t <= 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 50); \ if (t <= 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 51); \ if (t <= 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 52); \ if (t <= 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 53); \ if (t <= 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 54); \ if (t <= 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 55); \ if (t <= 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 56); \ if (t <= 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 57); \ if (t <= 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 58); \ if (t <= 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 59); \ if (t <= 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 60); \ if (t <= 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 61); \ if (t <= 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 62); \ if (t <= 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 63); \ if (t <= 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 64); \ if (t <= 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 65); \ if (t <= 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 66); \ if (t <= 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 67); \ if (t <= 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 68); \ if (t <= 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 69); \ if (t <= 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 70); \ if (t <= 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 71); \ if (t <= 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 72); \ if (t <= 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 73); \ if (t <= 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 74); \ if (t <= 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 75); \ if (t <= 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 76); \ if (t <= 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 77); \ if (t <= 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 78); \ if (t <= 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 79); \ ihvout[0] = ihvin[0] + a; ihvout[1] = ihvin[1] + b; ihvout[2] = ihvin[2] + c; ihvout[3] = ihvin[3] + d; ihvout[4] = ihvin[4] + e; \ } SHA1_RECOMPRESS(0) SHA1_RECOMPRESS(1) SHA1_RECOMPRESS(2) SHA1_RECOMPRESS(3) SHA1_RECOMPRESS(4) SHA1_RECOMPRESS(5) SHA1_RECOMPRESS(6) SHA1_RECOMPRESS(7) SHA1_RECOMPRESS(8) SHA1_RECOMPRESS(9) SHA1_RECOMPRESS(10) SHA1_RECOMPRESS(11) SHA1_RECOMPRESS(12) SHA1_RECOMPRESS(13) SHA1_RECOMPRESS(14) SHA1_RECOMPRESS(15) SHA1_RECOMPRESS(16) SHA1_RECOMPRESS(17) SHA1_RECOMPRESS(18) SHA1_RECOMPRESS(19) SHA1_RECOMPRESS(20) SHA1_RECOMPRESS(21) SHA1_RECOMPRESS(22) SHA1_RECOMPRESS(23) SHA1_RECOMPRESS(24) SHA1_RECOMPRESS(25) SHA1_RECOMPRESS(26) SHA1_RECOMPRESS(27) SHA1_RECOMPRESS(28) SHA1_RECOMPRESS(29) SHA1_RECOMPRESS(30) SHA1_RECOMPRESS(31) SHA1_RECOMPRESS(32) SHA1_RECOMPRESS(33) SHA1_RECOMPRESS(34) SHA1_RECOMPRESS(35) SHA1_RECOMPRESS(36) SHA1_RECOMPRESS(37) SHA1_RECOMPRESS(38) SHA1_RECOMPRESS(39) SHA1_RECOMPRESS(40) SHA1_RECOMPRESS(41) SHA1_RECOMPRESS(42) SHA1_RECOMPRESS(43) SHA1_RECOMPRESS(44) SHA1_RECOMPRESS(45) SHA1_RECOMPRESS(46) SHA1_RECOMPRESS(47) SHA1_RECOMPRESS(48) SHA1_RECOMPRESS(49) SHA1_RECOMPRESS(50) SHA1_RECOMPRESS(51) SHA1_RECOMPRESS(52) SHA1_RECOMPRESS(53) SHA1_RECOMPRESS(54) SHA1_RECOMPRESS(55) SHA1_RECOMPRESS(56) SHA1_RECOMPRESS(57) SHA1_RECOMPRESS(58) SHA1_RECOMPRESS(59) SHA1_RECOMPRESS(60) SHA1_RECOMPRESS(61) SHA1_RECOMPRESS(62) SHA1_RECOMPRESS(63) SHA1_RECOMPRESS(64) SHA1_RECOMPRESS(65) SHA1_RECOMPRESS(66) SHA1_RECOMPRESS(67) SHA1_RECOMPRESS(68) SHA1_RECOMPRESS(69) SHA1_RECOMPRESS(70) SHA1_RECOMPRESS(71) SHA1_RECOMPRESS(72) SHA1_RECOMPRESS(73) SHA1_RECOMPRESS(74) SHA1_RECOMPRESS(75) SHA1_RECOMPRESS(76) SHA1_RECOMPRESS(77) SHA1_RECOMPRESS(78) SHA1_RECOMPRESS(79) sha1_recompression_type sha1_recompression_step[80] = { sha1recompress_fast_0, sha1recompress_fast_1, sha1recompress_fast_2, sha1recompress_fast_3, sha1recompress_fast_4, sha1recompress_fast_5, sha1recompress_fast_6, sha1recompress_fast_7, sha1recompress_fast_8, sha1recompress_fast_9, sha1recompress_fast_10, sha1recompress_fast_11, sha1recompress_fast_12, sha1recompress_fast_13, sha1recompress_fast_14, sha1recompress_fast_15, sha1recompress_fast_16, sha1recompress_fast_17, sha1recompress_fast_18, sha1recompress_fast_19, sha1recompress_fast_20, sha1recompress_fast_21, sha1recompress_fast_22, sha1recompress_fast_23, sha1recompress_fast_24, sha1recompress_fast_25, sha1recompress_fast_26, sha1recompress_fast_27, sha1recompress_fast_28, sha1recompress_fast_29, sha1recompress_fast_30, sha1recompress_fast_31, sha1recompress_fast_32, sha1recompress_fast_33, sha1recompress_fast_34, sha1recompress_fast_35, sha1recompress_fast_36, sha1recompress_fast_37, sha1recompress_fast_38, sha1recompress_fast_39, sha1recompress_fast_40, sha1recompress_fast_41, sha1recompress_fast_42, sha1recompress_fast_43, sha1recompress_fast_44, sha1recompress_fast_45, sha1recompress_fast_46, sha1recompress_fast_47, sha1recompress_fast_48, sha1recompress_fast_49, sha1recompress_fast_50, sha1recompress_fast_51, sha1recompress_fast_52, sha1recompress_fast_53, sha1recompress_fast_54, sha1recompress_fast_55, sha1recompress_fast_56, sha1recompress_fast_57, sha1recompress_fast_58, sha1recompress_fast_59, sha1recompress_fast_60, sha1recompress_fast_61, sha1recompress_fast_62, sha1recompress_fast_63, sha1recompress_fast_64, sha1recompress_fast_65, sha1recompress_fast_66, sha1recompress_fast_67, sha1recompress_fast_68, sha1recompress_fast_69, sha1recompress_fast_70, sha1recompress_fast_71, sha1recompress_fast_72, sha1recompress_fast_73, sha1recompress_fast_74, sha1recompress_fast_75, sha1recompress_fast_76, sha1recompress_fast_77, sha1recompress_fast_78, sha1recompress_fast_79, }; void sha1_process(SHA1_CTX* ctx, const uint32_t block[16]) { unsigned i, j; uint32_t ubc_dv_mask[DVMASKSIZE]; uint32_t ihvtmp[5]; for (i=0; i < DVMASKSIZE; ++i) ubc_dv_mask[i]=0; ctx->ihv1[0] = ctx->ihv[0]; ctx->ihv1[1] = ctx->ihv[1]; ctx->ihv1[2] = ctx->ihv[2]; ctx->ihv1[3] = ctx->ihv[3]; ctx->ihv1[4] = ctx->ihv[4]; memcpy(ctx->m1, block, 64); sha1_message_expansion(ctx->m1); if (ctx->detect_coll && ctx->ubc_check) { ubc_check(ctx->m1, ubc_dv_mask); } sha1_compression_states(ctx->ihv, ctx->m1, ctx->states); if (ctx->detect_coll) { for (i = 0; sha1_dvs[i].dvType != 0; ++i) { if ((0 == ctx->ubc_check) || (((uint32_t)(1) << sha1_dvs[i].maskb) & ubc_dv_mask[sha1_dvs[i].maski])) { for (j = 0; j < 80; ++j) ctx->m2[j] = ctx->m1[j] ^ sha1_dvs[i].dm[j]; (sha1_recompression_step[sha1_dvs[i].testt])(ctx->ihv2, ihvtmp, ctx->m2, ctx->states[sha1_dvs[i].testt]); /* to verify SHA-1 collision detection code with collisions for reduced-step SHA-1 */ if ((ihvtmp[0] == ctx->ihv[0] && ihvtmp[1] == ctx->ihv[1] && ihvtmp[2] == ctx->ihv[2] && ihvtmp[3] == ctx->ihv[3] && ihvtmp[4] == ctx->ihv[4]) || (ctx->reduced_round_coll && ctx->ihv1[0] == ctx->ihv2[0] && ctx->ihv1[1] == ctx->ihv2[1] && ctx->ihv1[2] == ctx->ihv2[2] && ctx->ihv1[3] == ctx->ihv2[3] && ctx->ihv1[4] == ctx->ihv2[4])) { ctx->found_collision = 1; /* TODO: call callback */ if (ctx->callback != NULL) ctx->callback(ctx->total - 64, ctx->ihv1, ctx->ihv2, ctx->m1, ctx->m2); if (ctx->safe_hash) { sha1_compression_W(ctx->ihv, ctx->m1); sha1_compression_W(ctx->ihv, ctx->m1); } break; } } } } } void swap_bytes(uint32_t val[16]) { unsigned i; for (i = 0; i < 16; ++i) { val[i] = ((val[i] << 8) & 0xFF00FF00) | ((val[i] >> 8) & 0xFF00FF); val[i] = (val[i] << 16) | (val[i] >> 16); } } void SHA1DCInit(SHA1_CTX* ctx) { static const union { unsigned char bytes[4]; uint32_t value; } endianness = { { 0, 1, 2, 3 } }; static const uint32_t littleendian = 0x03020100; ctx->total = 0; ctx->ihv[0] = 0x67452301; ctx->ihv[1] = 0xEFCDAB89; ctx->ihv[2] = 0x98BADCFE; ctx->ihv[3] = 0x10325476; ctx->ihv[4] = 0xC3D2E1F0; ctx->found_collision = 0; ctx->safe_hash = 1; ctx->ubc_check = 1; ctx->detect_coll = 1; ctx->reduced_round_coll = 0; ctx->bigendian = (endianness.value != littleendian); ctx->callback = NULL; } void SHA1DCSetSafeHash(SHA1_CTX* ctx, int safehash) { if (safehash) ctx->safe_hash = 1; else ctx->safe_hash = 0; } void SHA1DCSetUseUBC(SHA1_CTX* ctx, int ubc_check) { if (ubc_check) ctx->ubc_check = 1; else ctx->ubc_check = 0; } void SHA1DCSetUseDetectColl(SHA1_CTX* ctx, int detect_coll) { if (detect_coll) ctx->detect_coll = 1; else ctx->detect_coll = 0; } void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX* ctx, int reduced_round_coll) { if (reduced_round_coll) ctx->reduced_round_coll = 1; else ctx->reduced_round_coll = 0; } void SHA1DCSetCallback(SHA1_CTX* ctx, collision_block_callback callback) { ctx->callback = callback; } void SHA1DCUpdate(SHA1_CTX* ctx, const unsigned char* buf, unsigned len) { unsigned left, fill; if (len == 0) return; left = ctx->total & 63; fill = 64 - left; if (left && len >= fill) { ctx->total += fill; memcpy(ctx->buffer + left, buf, fill); if (!ctx->bigendian) swap_bytes((uint32_t*)(ctx->buffer)); sha1_process(ctx, (uint32_t*)(ctx->buffer)); buf += fill; len -= fill; left = 0; } while (len >= 64) { ctx->total += 64; if (!ctx->bigendian) { memcpy(ctx->buffer, buf, 64); swap_bytes((uint32_t*)(ctx->buffer)); sha1_process(ctx, (uint32_t*)(ctx->buffer)); } else sha1_process(ctx, (uint32_t*)(buf)); buf += 64; len -= 64; } if (len > 0) { ctx->total += len; memcpy(ctx->buffer + left, buf, len); } } static const unsigned char sha1_padding[64] = { 0x80, 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 }; int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx) { uint32_t last = ctx->total & 63; uint32_t padn = (last < 56) ? (56 - last) : (120 - last); uint64_t total; SHA1DCUpdate(ctx, sha1_padding, padn); total = ctx->total - padn; total <<= 3; ctx->buffer[56] = (unsigned char)(total >> 56); ctx->buffer[57] = (unsigned char)(total >> 48); ctx->buffer[58] = (unsigned char)(total >> 40); ctx->buffer[59] = (unsigned char)(total >> 32); ctx->buffer[60] = (unsigned char)(total >> 24); ctx->buffer[61] = (unsigned char)(total >> 16); ctx->buffer[62] = (unsigned char)(total >> 8); ctx->buffer[63] = (unsigned char)(total); if (!ctx->bigendian) swap_bytes((uint32_t*)(ctx->buffer)); sha1_process(ctx, (uint32_t*)(ctx->buffer)); output[0] = (unsigned char)(ctx->ihv[0] >> 24); output[1] = (unsigned char)(ctx->ihv[0] >> 16); output[2] = (unsigned char)(ctx->ihv[0] >> 8); output[3] = (unsigned char)(ctx->ihv[0]); output[4] = (unsigned char)(ctx->ihv[1] >> 24); output[5] = (unsigned char)(ctx->ihv[1] >> 16); output[6] = (unsigned char)(ctx->ihv[1] >> 8); output[7] = (unsigned char)(ctx->ihv[1]); output[8] = (unsigned char)(ctx->ihv[2] >> 24); output[9] = (unsigned char)(ctx->ihv[2] >> 16); output[10] = (unsigned char)(ctx->ihv[2] >> 8); output[11] = (unsigned char)(ctx->ihv[2]); output[12] = (unsigned char)(ctx->ihv[3] >> 24); output[13] = (unsigned char)(ctx->ihv[3] >> 16); output[14] = (unsigned char)(ctx->ihv[3] >> 8); output[15] = (unsigned char)(ctx->ihv[3]); output[16] = (unsigned char)(ctx->ihv[4] >> 24); output[17] = (unsigned char)(ctx->ihv[4] >> 16); output[18] = (unsigned char)(ctx->ihv[4] >> 8); output[19] = (unsigned char)(ctx->ihv[4]); return ctx->found_collision; } #endif /* FOSSIL_HARDENED_SHA1 */ |
Added src/sha3.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 | /* ** Copyright (c) 2017 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 an implementation of SHA3 (Keccak) hashing. */ #include "config.h" #include "sha3.h" /* ** Macros to determine whether the machine is big or little endian, ** and whether or not that determination is run-time or compile-time. ** ** For best performance, an attempt is made to guess at the byte-order ** using C-preprocessor macros. If that is unsuccessful, or if ** -DSHA3_BYTEORDER=0 is set, then byte-order is determined ** at run-time. */ #ifndef SHA3_BYTEORDER # if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ defined(__arm__) # define SHA3_BYTEORDER 1234 # elif defined(sparc) || defined(__ppc__) # define SHA3_BYTEORDER 4321 # else # define SHA3_BYTEORDER 0 # endif #endif /* ** State structure for a SHA3 hash in progress */ typedef struct SHA3Context SHA3Context; struct SHA3Context { union { u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */ unsigned char x[1600]; /* ... or 1600 bytes */ } u; unsigned nRate; /* Bytes of input accepted per Keccak iteration */ unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */ unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */ }; /* ** A single step of the Keccak mixing function for a 1600-bit state */ static void KeccakF1600Step(SHA3Context *p){ int i; u64 B0, B1, B2, B3, B4; u64 C0, C1, C2, C3, C4; u64 D0, D1, D2, D3, D4; static const u64 RC[] = { 0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL, 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL }; # define A00 (p->u.s[0]) # define A01 (p->u.s[1]) # define A02 (p->u.s[2]) # define A03 (p->u.s[3]) # define A04 (p->u.s[4]) # define A10 (p->u.s[5]) # define A11 (p->u.s[6]) # define A12 (p->u.s[7]) # define A13 (p->u.s[8]) # define A14 (p->u.s[9]) # define A20 (p->u.s[10]) # define A21 (p->u.s[11]) # define A22 (p->u.s[12]) # define A23 (p->u.s[13]) # define A24 (p->u.s[14]) # define A30 (p->u.s[15]) # define A31 (p->u.s[16]) # define A32 (p->u.s[17]) # define A33 (p->u.s[18]) # define A34 (p->u.s[19]) # define A40 (p->u.s[20]) # define A41 (p->u.s[21]) # define A42 (p->u.s[22]) # define A43 (p->u.s[23]) # define A44 (p->u.s[24]) # define ROL64(a,x) ((a<<x)|(a>>(64-x))) for(i=0; i<24; i+=4){ C0 = A00^A10^A20^A30^A40; C1 = A01^A11^A21^A31^A41; C2 = A02^A12^A22^A32^A42; C3 = A03^A13^A23^A33^A43; C4 = A04^A14^A24^A34^A44; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A11^D1), 44); B2 = ROL64((A22^D2), 43); B3 = ROL64((A33^D3), 21); B4 = ROL64((A44^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i]; A11 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B2 = ROL64((A20^D0), 3); B3 = ROL64((A31^D1), 45); B4 = ROL64((A42^D2), 61); B0 = ROL64((A03^D3), 28); B1 = ROL64((A14^D4), 20); A20 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A40^D0), 18); B0 = ROL64((A01^D1), 1); B1 = ROL64((A12^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A34^D4), 8); A40 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B1 = ROL64((A10^D0), 36); B2 = ROL64((A21^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A43^D3), 56); B0 = ROL64((A04^D4), 27); A10 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B3 = ROL64((A30^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A02^D2), 62); B1 = ROL64((A13^D3), 55); B2 = ROL64((A24^D4), 39); A30 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); C0 = A00^A20^A40^A10^A30; C1 = A11^A31^A01^A21^A41; C2 = A22^A42^A12^A32^A02; C3 = A33^A03^A23^A43^A13; C4 = A44^A14^A34^A04^A24; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A31^D1), 44); B2 = ROL64((A12^D2), 43); B3 = ROL64((A43^D3), 21); B4 = ROL64((A24^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+1]; A31 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B2 = ROL64((A40^D0), 3); B3 = ROL64((A21^D1), 45); B4 = ROL64((A02^D2), 61); B0 = ROL64((A33^D3), 28); B1 = ROL64((A14^D4), 20); A40 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A30^D0), 18); B0 = ROL64((A11^D1), 1); B1 = ROL64((A42^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A04^D4), 8); A30 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B1 = ROL64((A20^D0), 36); B2 = ROL64((A01^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A13^D3), 56); B0 = ROL64((A44^D4), 27); A20 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B3 = ROL64((A10^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A22^D2), 62); B1 = ROL64((A03^D3), 55); B2 = ROL64((A34^D4), 39); A10 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); C0 = A00^A40^A30^A20^A10; C1 = A31^A21^A11^A01^A41; C2 = A12^A02^A42^A32^A22; C3 = A43^A33^A23^A13^A03; C4 = A24^A14^A04^A44^A34; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A21^D1), 44); B2 = ROL64((A42^D2), 43); B3 = ROL64((A13^D3), 21); B4 = ROL64((A34^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+2]; A21 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B2 = ROL64((A30^D0), 3); B3 = ROL64((A01^D1), 45); B4 = ROL64((A22^D2), 61); B0 = ROL64((A43^D3), 28); B1 = ROL64((A14^D4), 20); A30 = B0 ^((~B1)& B2 ); A01 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A10^D0), 18); B0 = ROL64((A31^D1), 1); B1 = ROL64((A02^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A44^D4), 8); A10 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); B1 = ROL64((A40^D0), 36); B2 = ROL64((A11^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A03^D3), 56); B0 = ROL64((A24^D4), 27); A40 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B3 = ROL64((A20^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A12^D2), 62); B1 = ROL64((A33^D3), 55); B2 = ROL64((A04^D4), 39); A20 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); C0 = A00^A30^A10^A40^A20; C1 = A21^A01^A31^A11^A41; C2 = A42^A22^A02^A32^A12; C3 = A13^A43^A23^A03^A33; C4 = A34^A14^A44^A24^A04; D0 = C4^ROL64(C1, 1); D1 = C0^ROL64(C2, 1); D2 = C1^ROL64(C3, 1); D3 = C2^ROL64(C4, 1); D4 = C3^ROL64(C0, 1); B0 = (A00^D0); B1 = ROL64((A01^D1), 44); B2 = ROL64((A02^D2), 43); B3 = ROL64((A03^D3), 21); B4 = ROL64((A04^D4), 14); A00 = B0 ^((~B1)& B2 ); A00 ^= RC[i+3]; A01 = B1 ^((~B2)& B3 ); A02 = B2 ^((~B3)& B4 ); A03 = B3 ^((~B4)& B0 ); A04 = B4 ^((~B0)& B1 ); B2 = ROL64((A10^D0), 3); B3 = ROL64((A11^D1), 45); B4 = ROL64((A12^D2), 61); B0 = ROL64((A13^D3), 28); B1 = ROL64((A14^D4), 20); A10 = B0 ^((~B1)& B2 ); A11 = B1 ^((~B2)& B3 ); A12 = B2 ^((~B3)& B4 ); A13 = B3 ^((~B4)& B0 ); A14 = B4 ^((~B0)& B1 ); B4 = ROL64((A20^D0), 18); B0 = ROL64((A21^D1), 1); B1 = ROL64((A22^D2), 6); B2 = ROL64((A23^D3), 25); B3 = ROL64((A24^D4), 8); A20 = B0 ^((~B1)& B2 ); A21 = B1 ^((~B2)& B3 ); A22 = B2 ^((~B3)& B4 ); A23 = B3 ^((~B4)& B0 ); A24 = B4 ^((~B0)& B1 ); B1 = ROL64((A30^D0), 36); B2 = ROL64((A31^D1), 10); B3 = ROL64((A32^D2), 15); B4 = ROL64((A33^D3), 56); B0 = ROL64((A34^D4), 27); A30 = B0 ^((~B1)& B2 ); A31 = B1 ^((~B2)& B3 ); A32 = B2 ^((~B3)& B4 ); A33 = B3 ^((~B4)& B0 ); A34 = B4 ^((~B0)& B1 ); B3 = ROL64((A40^D0), 41); B4 = ROL64((A41^D1), 2); B0 = ROL64((A42^D2), 62); B1 = ROL64((A43^D3), 55); B2 = ROL64((A44^D4), 39); A40 = B0 ^((~B1)& B2 ); A41 = B1 ^((~B2)& B3 ); A42 = B2 ^((~B3)& B4 ); A43 = B3 ^((~B4)& B0 ); A44 = B4 ^((~B0)& B1 ); } } /* ** Initialize a new hash. iSize determines the size of the hash ** in bits and should be one of 224, 256, 384, or 512. Or iSize ** can be zero to use the default hash size of 256 bits. */ static void SHA3Init(SHA3Context *p, int iSize){ memset(p, 0, sizeof(*p)); if( iSize>=128 && iSize<=512 ){ p->nRate = (1600 - ((iSize + 31)&~31)*2)/8; }else{ p->nRate = (1600 - 2*256)/8; } #if SHA3_BYTEORDER==1234 /* Known to be little-endian at compile-time. No-op */ #elif SHA3_BYTEORDER==4321 p->ixMask = 7; /* Big-endian */ #else { static unsigned int one = 1; if( 1==*(unsigned char*)&one ){ /* Little endian. No byte swapping. */ p->ixMask = 0; }else{ /* Big endian. Byte swap. */ p->ixMask = 7; } } #endif } /* ** Make consecutive calls to the SHA3Update function to add new content ** to the hash */ static void SHA3Update( SHA3Context *p, const unsigned char *aData, unsigned int nData ){ unsigned int i = 0; #if SHA3_BYTEORDER==1234 if( (p->nLoaded % 8)==0 && (((intptr_t)aData)&7)==0 ){ for(; i+7<nData; i+=8){ p->u.s[p->nLoaded/8] ^= *(u64*)&aData[i]; p->nLoaded += 8; if( p->nLoaded>=p->nRate ){ KeccakF1600Step(p); p->nLoaded = 0; } } } #endif for(; i<nData; i++){ #if SHA3_BYTEORDER==1234 p->u.x[p->nLoaded] ^= aData[i]; #elif SHA3_BYTEORDER==4321 p->u.x[p->nLoaded^0x07] ^= aData[i]; #else p->u.x[p->nLoaded^p->ixMask] ^= aData[i]; #endif p->nLoaded++; if( p->nLoaded==p->nRate ){ KeccakF1600Step(p); p->nLoaded = 0; } } } /* ** After all content has been added, invoke SHA3Final() to compute ** the final hash. The function returns a pointer to the binary ** hash value. */ static unsigned char *SHA3Final(SHA3Context *p){ unsigned int i; if( p->nLoaded==p->nRate-1 ){ const unsigned char c1 = 0x86; SHA3Update(p, &c1, 1); }else{ const unsigned char c2 = 0x06; const unsigned char c3 = 0x80; SHA3Update(p, &c2, 1); p->nLoaded = p->nRate - 1; SHA3Update(p, &c3, 1); } for(i=0; i<p->nRate; i++){ p->u.x[i+p->nRate] = p->u.x[i^p->ixMask]; } return &p->u.x[p->nRate]; } /* ** Convert a digest into base-16. digest should be declared as ** "unsigned char digest[20]" in the calling function. The SHA3 ** digest is stored in the first 20 bytes. zBuf should ** be "char zBuf[41]". */ static void DigestToBase16(unsigned char *digest, char *zBuf, int nByte){ static const char zEncode[] = "0123456789abcdef"; int ix; for(ix=0; ix<nByte; ix++){ *zBuf++ = zEncode[(*digest>>4)&0xf]; *zBuf++ = zEncode[*digest++ & 0xf]; } *zBuf = '\0'; } /* ** The state of an incremental SHA3 checksum computation. Only one ** such computation can be underway at a time, of course. */ static SHA3Context incrCtx; static int incrInit = 0; /* ** Initialize a new global SHA3 hash. */ void sha3sum_init(int iSize){ assert( incrInit==0 ); incrInit = iSize; SHA3Init(&incrCtx, incrInit); } /* ** Add more text to the incremental SHA3 checksum. */ void sha3sum_step_text(const char *zText, int nBytes){ assert( incrInit ); if( nBytes<=0 ){ if( nBytes==0 ) return; nBytes = strlen(zText); } SHA3Update(&incrCtx, (unsigned char*)zText, nBytes); } /* ** Add the content of a blob to the incremental SHA3 checksum. */ void sha3sum_step_blob(Blob *p){ assert( incrInit ); SHA3Update(&incrCtx, (unsigned char*)blob_buffer(p), blob_size(p)); } /* ** Finish the incremental SHA3 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 *sha3sum_finish(Blob *pOut){ static char zOut[132]; DigestToBase16(SHA3Final(&incrCtx), zOut, incrInit/8); if( pOut ){ blob_zero(pOut); blob_append(pOut, zOut, incrInit/4); } incrInit = 0; return zOut; } /* ** Compute the SHA3 checksum of a file on disk. Store the resulting ** checksum in the blob pCksum. pCksum is assumed to be initialized. ** ** Return the number of errors. */ int sha3sum_file(const char *zFilename, int eFType, int iSize, Blob *pCksum){ FILE *in; SHA3Context ctx; char zBuf[10240]; if( eFType==RepoFILE && file_islink(zFilename) ){ /* Instead of file content, return sha3 of link destination path */ Blob destinationPath; int rc; blob_read_link(&destinationPath, zFilename); rc = sha3sum_blob(&destinationPath, iSize, pCksum); blob_reset(&destinationPath); return rc; } in = fossil_fopen(zFilename,"rb"); if( in==0 ){ return 1; } SHA3Init(&ctx, iSize); for(;;){ int n; n = fread(zBuf, 1, sizeof(zBuf), in); if( n<=0 ) break; SHA3Update(&ctx, (unsigned char*)zBuf, (unsigned)n); } fclose(in); blob_zero(pCksum); blob_resize(pCksum, iSize/4); DigestToBase16(SHA3Final(&ctx), blob_buffer(pCksum), iSize/8); return 0; } /* ** Compute the SHA3 checksum of a blob in memory. Store the resulting ** checksum in the blob pCksum. pCksum is assumed to be either ** uninitialized or the same blob as pIn. ** ** Return the number of errors. */ int sha3sum_blob(const Blob *pIn, int iSize, Blob *pCksum){ SHA3Context ctx; SHA3Init(&ctx, iSize); SHA3Update(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn)); if( pIn==pCksum ){ blob_reset(pCksum); }else{ blob_zero(pCksum); } blob_resize(pCksum, iSize/4); DigestToBase16(SHA3Final(&ctx), blob_buffer(pCksum), iSize/8); return 0; } #if 0 /* NOT USED */ /* ** Compute the SHA3 checksum of a zero-terminated string. The ** result is held in memory obtained from mprintf(). */ char *sha3sum(const char *zIn, int iSize){ SHA3Context ctx; char zDigest[132]; SHA3Init(&ctx, iSize); SHA3Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); DigestToBase16(SHA3Final(&ctx), zDigest, iSize/8); return mprintf("%s", zDigest); } #endif /* ** COMMAND: sha3sum* ** ** Usage: %fossil sha3sum FILE... ** ** Compute an SHA3 checksum of all files named on the command-line. ** If a file is named "-" then take its content from standard input. ** ** To be clear: The official NIST FIPS-202 implementation of SHA3 ** with the added 01 padding is used, not the original Keccak submission. ** ** Options: ** --224 Compute a SHA3-224 hash ** --256 Compute a SHA3-256 hash (the default) ** --384 Compute a SHA3-384 hash ** --512 Compute a SHA3-512 hash ** --size N An N-bit hash. N must be a multiple of 32 between ** 128 and 512. ** -h|--dereference If FILE is a symbolic link, compute the hash on ** the object pointed to, not on the link itself. ** ** See also: [[md5sum]], [[sha1sum]] */ void sha3sum_test(void){ int i; Blob in; Blob cksum = empty_blob; int iSize = 256; int eFType = SymFILE; if( find_option("dereference","h",0) ) eFType = ExtFILE; if( find_option("224",0,0)!=0 ) iSize = 224; else if( find_option("256",0,0)!=0 ) iSize = 256; else if( find_option("384",0,0)!=0 ) iSize = 384; else if( find_option("512",0,0)!=0 ) iSize = 512; else{ const char *zN = find_option("size",0,1); if( zN!=0 ){ int n = atoi(zN); if( n%32!=0 || n<128 || n>512 ){ fossil_fatal("--size must be a multiple of 64 between 128 and 512"); } iSize = n; } } verify_all_options(); for(i=2; i<g.argc; i++){ if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ blob_read_from_channel(&in, stdin, -1); sha3sum_blob(&in, iSize, &cksum); }else if( sha3sum_file(g.argv[i], eFType, iSize, &cksum) > 0 ){ fossil_fatal("Cannot read file: %s", g.argv[i]); } fossil_print("%s %s\n", blob_str(&cksum), g.argv[i]); blob_reset(&cksum); } } |
Changes to src/shun.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | /* ** Return true if the given artifact ID should be shunned. */ int uuid_is_shunned(const char *zUuid){ static Stmt q; int rc; if( zUuid==0 || zUuid[0]==0 ) return 0; db_static_prepare(&q, "SELECT 1 FROM shun WHERE uuid=:uuid"); db_bind_text(&q, ":uuid", zUuid); rc = db_step(&q); db_reset(&q); return rc==SQLITE_ROW; } /* ** WEBPAGE: shun */ void shun_page(void){ Stmt q; int cnt = 0; const char *zUuid = P("uuid"); | > > > > > > > > | > | | | > | | > > > > | | > > | > > | | > > > > | | > > > > > > > > > > | > > | | > > | | > > > > > > | > | > | | > > > | | | | > | > | > > > > > > > > > > > > > > > > | > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | > > > > > > > > > > > > > > > > > | > | > | | | | > > > > > > > > > > > > | | | | | | | | | | | > | 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 | /* ** Return true if the given artifact ID should be shunned. */ int uuid_is_shunned(const char *zUuid){ static Stmt q; int rc; if( zUuid==0 || zUuid[0]==0 ) return 0; if( g.eHashPolicy==HPOLICY_SHUN_SHA1 && zUuid[HNAME_LEN_SHA1]==0 ) return 1; db_static_prepare(&q, "SELECT 1 FROM shun WHERE uuid=:uuid"); db_bind_text(&q, ":uuid", zUuid); rc = db_step(&q); db_reset(&q); return rc==SQLITE_ROW; } /* ** WEBPAGE: shun ** ** View the hashes of all shunned artifacts. Add new hashes ** to the shun set. Requires Admin privilege. */ 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 reviewList = P("review")!=0; 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); 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( !(reviewList || hname_validate(p, nUuid)) ){ @ <p class="generalError">Error: Bad artifact IDs.</p> fossil_free(zCanonical); zCanonical = 0; break; }else{ canonical16(p, nUuid); p += nUuid+1; } } zUuid = zCanonical; } style_header("Shunned Artifacts"); if( zUuid && P("sub") && cgi_csrf_safe(2) ){ const char *p = zUuid; int allExist = 1; 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 += strlen(p)+1; } if( allExist ){ @ <p class="noMoreShun">Artifact(s)<br> for( p = zUuid ; *p ; p += strlen(p)+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 += strlen(p)+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") && cgi_csrf_safe(2) ){ const char *p = zUuid; int rid, tagid; 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 += strlen(p)+1; } @ <p class="shunned">Artifact(s)<br> for( p = zUuid ; *p ; p += strlen(p)+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( zUuid && reviewList ){ const char *p; int nTotal = 0; int nOk = 0; @ <table class="shun-review"><tbody><tr><td> for( p = zUuid ; *p ; p += strlen(p)+1 ){ int rid = symbolic_name_to_rid(p, 0); nTotal++; if( rid < 0 ){ @ Ambiguous<br> }else if( rid == 0 ){ if( !hname_validate(p, strlen(p)) ){ @ Bad artifact<br> }else if(db_int(0, "SELECT 1 FROM shun WHERE uuid=%Q", p)){ @ Already shunned<br> }else{ @ Unknown<br> } }else{ char *zCmpUuid = db_text(0, "SELECT uuid" " FROM blob, rcvfrom" " WHERE rid=%d" " AND rcvfrom.rcvid=blob.rcvid", rid); if( fossil_strcmp(p, zCmpUuid)==0 ){ nOk++; @ OK</br> }else{ @ Abbreviated<br> } } } @ </td><td> for( p = zUuid ; *p ; p += strlen(p)+1 ){ int rid = symbolic_name_to_rid(p, 0); if( rid > 0 ){ @ <a href="%R/artifact/%s(p)">%s(p)</a><br> }else{ @ %s(p)<br> } } @ </td></tr></tbody></table> @ <p class="shunned"> if( nOk < nTotal){ @ <b>Warning:</b> Not all artifacts }else if( nTotal==1 ){ @ The artifact is present and }else{ @ All %i(nOk) artifacts are present and } @ can be shunned with its hash above.</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 hashes (the 40- or @ 64-character lowercase hexadecimal hash of the artifact content) 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 full artifact hashes, not abbreviations @ or symbolic tags.</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="%R/%s(g.zPath)"><div> login_insert_csrf_secret(); @ <textarea class="fullsize-text" cols="70" 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); } }else if( zUuid && reviewList ){ const char *p; for( p = zUuid ; *p ; p += strlen(p)+1 ){ @ %s(p) } } @ </textarea> @ <input type="submit" name="add" value="Shun"> @ <input type="submit" name="review" value="Review"> @ </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="%R/%s(g.zPath)"><div> login_insert_csrf_secret(); @ <textarea class="fullsize-text" cols="70" 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="%R/%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_finish_page(); fossil_free(zCanonical); } /* ** Remove from the BLOB table all artifacts that are in the SHUN table. */ void shun_artifacts(void){ Stmt q; |
︙ | ︙ | |||
196 197 198 199 200 201 202 203 204 205 206 207 208 209 | ); } /* ** 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(); | > > > > > > > | | > | > > > > > > | | > > > > > > > > > > > > > > > > > > > > > | | > > > | | | > > | | | > > | > > > > | | < > > | > > > | | > | | | > > | | > | > > > > > > > > > > > > | | | | | | > > > > > > > | > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < > > > > > > > > > | | > > > > > > > > > > > > | > > > > | 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 | ); } /* ** WEBPAGE: rcvfromlist ** ** Show a listing of RCVFROM table entries. ** ** The RCVFROM table records where this repository received each ** artifact, including the time of receipt, user, and IP address. ** ** Access requires Admin privilege. */ void rcvfromlist_page(void){ int ofst = atoi(PD("ofst","0")); int showAll = P("all")!=0; int cnt; Stmt q; const int perScreen = 500; /* RCVIDs per page */ login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_header("Artifact Receipts"); style_submenu_element("Log-Menu", "setup-logmenu"); if( showAll ){ ofst = 0; }else{ style_submenu_element("All", "rcvfromlist?all=1"); } if( ofst>0 ){ style_submenu_element("Newer", "rcvfromlist?ofst=%d", ofst>perScreen ? ofst-perScreen : 0); } style_submenu_element("Artifacts", "bloblist"); style_submenu_element("Top-250", "bigbloblist"); db_multi_exec( "CREATE TEMP TABLE rcvidUsed(x INTEGER PRIMARY KEY);" "CREATE TEMP TABLE rcvidSha1(x INTEGER PRIMARY KEY);" "CREATE TEMP TABLE rcvidSha3(x INTEGER PRIMARY KEY);" "INSERT OR IGNORE INTO rcvidUsed(x) SELECT rcvid FROM blob;" "INSERT OR IGNORE INTO rcvidSha1(x)" " SELECT rcvid FROM blob WHERE length(uuid)==40;" "INSERT OR IGNORE INTO rcvidSha3(x)" " SELECT rcvid FROM blob WHERE length(uuid)==64;" ); if( db_table_exists("repository","unversioned") ){ db_multi_exec( "INSERT OR IGNORE INTO rcvidUsed(x) SELECT rcvid FROM unversioned;" "INSERT OR IGNORE INTO rcvidSha1(x)" " SELECT rcvid FROM unversioned WHERE length(hash)==40;" "INSERT OR IGNORE INTO rcvidSha3(x)" " SELECT rcvid FROM unversioned WHERE length(hash)==64;" ); } db_prepare(&q, "SELECT rcvid, login, datetime(rcvfrom.mtime), rcvfrom.ipaddr," " EXISTS(SELECT 1 FROM rcvidUsed WHERE x=rcvfrom.rcvid)," " EXISTS(SELECT 1 FROM rcvidSha1 WHERE x=rcvfrom.rcvid)," " EXISTS(SELECT 1 FROM rcvidSha3 WHERE x=rcvfrom.rcvid)" " FROM rcvfrom LEFT JOIN user USING(uid)" " ORDER BY rcvid DESC LIMIT %d OFFSET %d", showAll ? -1 : perScreen+1, 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="padding-right: 15px;text-align: left;">Hash</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); int usesSha1 = db_column_int(&q, 5)!=0; int usesSha3 = db_column_int(&q, 6)!=0; static const char *const zHashType[] = { "", "sha1", "sha3", "both" }; const char *zHash = zHashType[usesSha1+usesSha3*2]; if( cnt==perScreen && !showAll ){ style_submenu_element("Older", "rcvfromlist?ofst=%d", ofst+perScreen); }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="padding-right: 15px;text-align: left;">%s(zHash)</td> @ <td style="text-align: left;">%s(zIpAddr)</td> @ </tr> } } db_finalize(&q); @ </table> style_finish_page(); } /* ** WEBPAGE: rcvfrom ** ** Show a single RCVFROM table entry identified by the rcvid= query ** parameters. Requires Admin privilege. */ void rcvfrom_page(void){ int rcvid = atoi(PD("rcvid","0")); Stmt q; int cnt; 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?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", "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 ); cnt = 0; 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 = ""; if( cnt==0 ){ @ <tr><th valign="top" align="right">Artifacts:</th> @ <td valign="top"> } cnt++; @ <a href="%R/info/%s(zUuid)">%s(zUuid)</a> @ %h(zDesc) (size: %d(size))<br> } if( cnt>0 ){ @ <p> if( db_exists( "SELECT 1 FROM blob WHERE rcvid=%d AND" " NOT EXISTS (SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)", rcvid) ){ @ <form action='%R/shun'> @ <input type="hidden" name="shun"> @ <input type="hidden" name="rcvid" value='%d(rcvid)'> @ <input type="submit" value="Shun All These Artifacts"> @ </form> } if( db_exists( "SELECT 1 FROM blob WHERE rcvid=%d AND" " EXISTS (SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)", rcvid) ){ @ <form action='%R/shun'> @ <input type="hidden" name="unshun"> @ <input type="hidden" name="rcvid" value='%d(rcvid)'> @ <input type="submit" value="Unshun All These Artifacts"> @ </form> } @ </td></tr> } if( db_table_exists("repository","unversioned") ){ cnt = 0; if( PB("uvdelete") && PB("confirmdelete") ){ db_multi_exec( "DELETE FROM unversioned WHERE rcvid=%d", rcvid ); } db_finalize(&q); db_prepare(&q, "SELECT name, hash, sz\n" " FROM unversioned " " WHERE rcvid=%d", rcvid ); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q,0); const char *zHash = db_column_text(&q,1); int size = db_column_int(&q,2); int isDeleted = zHash==0; if( cnt==0 ){ @ <tr><th valign="top" align="right">Unversioned Files:</th> @ <td valign="top"> } cnt++; if( isDeleted ){ @ %h(zName) (deleted)<br> }else{ @ <a href="%R/uv/%h(zName)">%h(zName)</a> (size: %d(size))<br> } } if( cnt>0 ){ @ <p><form action='%R/rcvfrom'> @ <input type="hidden" name="rcvid" value='%d(rcvid)'> @ <input type="hidden" name="uvdelete" value="1"> if( PB("uvdelete") ){ @ <input type="hidden" name="confirmdelete" value="1"> @ <input type="submit" value="Confirm Deletion of These Files"> }else{ @ <input type="submit" value="Delete These Unversioned Files"> } @ </form> @ </td></tr> } } @ </table> db_finalize(&q); style_finish_page(); } |
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 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 | /* ** 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 ** ** List some of the web pages offered by the Fossil web engine. This ** page is intended as a supplement to the menu bar on the main screen. ** That is, this page is designed to hold links that are omitted from ** the main menu due to lack of space. ** ** Additional entries defined by the "sitemap-extra" setting are included ** in the sitemap. "sitemap-extra" should be a TCL script with three ** values per entry: ** ** * The displayed text ** ** * The URL ** ** * A "capexpr" expression that determines whether or not to include ** the entry based on user capabilities. "*" means always include ** the entry and "{}" means never. ** ** If the "e=1" query parameter is present, then the standard content ** is omitted and only the sitemap-extra content is shown. If "e=2" is ** present, then only the standard content is shown and sitemap-extra ** content is omitted. ** ** If the "popup" query parameter is present and this is a POST request ** from the same origin, then the normal HTML header and footer information ** is omitted and the HTML text returned is just a raw "<ul>...</ul>". */ void sitemap_page(void){ int srchFlags; int inSublist = 0; int i; int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ int e = atoi(PD("e","0")); const char *zExtra; #if 0 /* Removed 2021-01-26 */ const struct { const char *zTitle; const char *zProperty; } aExtra[] = { { "Documentation", "sitemap-docidx" }, { "Download", "sitemap-download" }, { "License", "sitemap-license" }, { "Contact", "sitemap-contact" }, }; #endif login_check_credentials(); if( P("popup")!=0 ){ /* The "popup" query parameter ** then disable anti-robot defenses */ isPopup = 1; g.perm.Hyperlink = 1; g.jsHref = 0; } srchFlags = search_restrict(SRCH_ALL); if( !isPopup ){ style_header("Site Map"); style_adunit_config(ADUNIT_RIGHT_OK); } @ <ul id="sitemap" class="columns" style="column-width:20em"> if( (e&1)==0 ){ @ <li>%z(href("%R/home"))Home Page</a> } #if 0 /* Removed 2021-01-26 */ for(i=0; i<sizeof(aExtra)/sizeof(aExtra[0]); i++){ char *z = db_get(aExtra[i].zProperty,0); if( z==0 || z[0]==0 ) continue; if( !inSublist ){ @ <ul> inSublist = 1; } if( z[0]=='/' ){ @ <li>%z(href("%R%s",z))%s(aExtra[i].zTitle)</a></li> }else{ @ <li>%z(href("%s",z))%s(aExtra[i].zTitle)</a></li> } } #endif zExtra = db_get("sitemap-extra",0); if( zExtra && (e&2)==0 ){ int rc; char **azExtra = 0; int *anExtra; int nExtra = 0; if( isPopup ) Th_FossilInit(0); if( (e&1)!=0 ) inSublist = 1; rc = Th_SplitList(g.interp, zExtra, (int)strlen(zExtra), &azExtra, &anExtra, &nExtra); if( rc==TH_OK && nExtra ){ for(i=0; i+2<nExtra; i+=3){ int nResult = 0; const char *zResult; int iCond = 0; rc = capexprCmd(g.interp, 0, 2, (const char**)&azExtra[i+1], (int*)&anExtra[i+1]); if( rc!=TH_OK ) continue; zResult = Th_GetResult(g.interp, &nResult); Th_ToInt(g.interp, zResult, nResult, &iCond); if( iCond==0 ) continue; if( !inSublist ){ @ <ul> inSublist = 1; } if( azExtra[i+1][0]=='/' ){ @ <li>%z(href("%R%s",azExtra[i+1]))%h(azExtra[i])</a></li> }else{ @ <li>%z(href("%s",azExtra[i+1]))%s(azExtra[i])</a></li> } } } Th_Free(g.interp, azExtra); } if( (e&1)!=0 ) goto end_of_sitemap; #if 0 /* Removed on 2021-02-11. Make a sitemap-extra entry if you */ /* really want this */ if( srchFlags & SRCH_DOC ){ if( !inSublist ){ @ <ul> inSublist = 1; } @ <li>%z(href("%R/docsrch"))Documentation Search</a></li> } #endif if( inSublist ){ @ </ul> inSublist = 0; } @ </li> if( g.perm.Read ){ const char *zEditGlob = db_get("fileedit-glob",""); @ <li>%z(href("%R/tree"))File Browser</a> @ <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> @ <li>%z(href("%R/uvlist"))Unversioned Files</a> if( g.perm.Write && zEditGlob[0]!=0 ){ @ <li>%z(href("%R/fileedit"))On-line File Editor</li> } @ </ul> } if( g.perm.Read ){ @ <li>%z(href("%R/timeline"))Project Timeline</a> @ <ul> @ <li>%z(href("%R/reports"))Activity Reports</a></li> @ <li>%z(href("%R/sitemap-timeline"))Other timelines</a></li> @ </ul> @ </li> } if( g.perm.Read ){ @ <li>%z(href("%R/brlist"))Branches</a> @ <ul> @ <li>%z(href("%R/taglist"))Tags</a></li> @ <li>%z(href("%R/leaves"))Leaf Check-ins</a></li> @ </ul> @ </li> } if( srchFlags ){ @ <li>%z(href("%R/search"))Search</a></li> } if( g.perm.Chat ){ @ <li>%z(href("%R/chat"))Chat</a></li> } if( g.perm.RdForum ){ @ <li>%z(href("%R/forum"))Forum</a> @ <ul> @ <li>%z(href("%R/timeline?y=f"))Recent activity</a></li> @ </ul> @ </li> } if( g.perm.RdTkt ){ @ <li>%z(href("%R/reportlist"))Tickets/Bug Reports</a> @ <ul> if( srchFlags & SRCH_TKT ){ @ <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> } if( g.perm.RdWiki ){ @ <li>%z(href("%R/wikihelp"))Wiki</a> @ <ul> if( srchFlags & SRCH_WIKI ){ @ <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/wikiedit?name=Sandbox"))Wiki Sandbox</a></li> @ <li>%z(href("%R/attachlist"))List of Attachments</a></li> @ <li>%z(href("%R/pikchrshow"))Pikchr Sandbox</a></li> @ </ul> @ </li> } if( !g.zLogin ){ @ <li>%z(href("%R/login"))Login</a> @ <ul> if( login_self_register_available(0) ){ @ <li>%z(href("%R/register"))Create a new account</a></li> } }else { @ <li>%z(href("%R/logout"))Logout from %h(g.zLogin)</a> @ <ul> if( g.perm.Password ){ @ <li>%z(href("%R/logout"))Change Password for %h(g.zLogin)</a></li> } } if( alert_enabled() && g.perm.EmailAlert ){ if( login_is_individual() ){ @ <li>%z(href("%R/alerts"))Email Alerts for %h(g.zLogin)</a></li> }else{ @ <li>%z(href("%R/subscribe"))Subscribe to Email Alerts</a></li> } } @ <li>%z(href("%R/cookies"))Cookies</a></li> @ </ul> @ </li> if( g.perm.Read ){ @ <li>%z(href("%R/stat"))Repository Status</a> @ <ul> @ <li>%z(href("%R/hash-collisions"))Collisions on hash prefixes</a></li> if( g.perm.Admin ){ @ <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>%z(href("%R/help"))Help</a> @ <ul> if( g.perm.Admin || g.perm.Write || g.perm.WrForum || g.perm.WrTForum || g.perm.NewWiki || g.perm.ApndWiki || g.perm.WrWiki || g.perm.ModWiki || g.perm.NewTkt || g.perm.ApndTkt || g.perm.WrTkt || g.perm.ModTkt ){ @ <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/test-all-help"))All "help" text on a single page</a></li> if( g.perm.Admin || g.perm.Write || g.perm.WrUnver ){ @ <li>%z(href("%R/mimetype_list"))\ @ Filename suffix to MIME type map</a></li> } @ </ul></li> if( g.perm.Admin ){ @ <li><a href="%R/setup">Administration Pages</a> @ <ul> @ <li><a href="%R/secaudit0">Security Audit</a></li> @ <li><a href="%R/modreq">Pending Moderation Requests</a></li> @ </ul></li> } @ <li>%z(href("%R/skins"))Skins</a></li> @ <li>%z(href("%R/sitemap-test"))Test Pages</a></li> if( isPopup ){ @ <li>%z(href("%R/sitemap"))Site Map</a></li> } end_of_sitemap: @ </ul> if( !isPopup ){ style_finish_page(); } } /* ** WEBPAGE: sitemap-test ** ** List some of the web pages offered by the Fossil web engine for testing ** purposes. This is similar to /sitemap, but is focused only on showing ** pages associated with testing. */ void sitemap_test_page(void){ int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ login_check_credentials(); style_set_current_feature("sitemap"); if( P("popup")!=0 && cgi_csrf_safe(0) ){ /* If this is a POST from the same origin with the popup=1 parameter, ** then disable anti-robot defenses */ isPopup = 1; g.perm.Hyperlink = 1; g.jsHref = 0; } if( !isPopup ){ style_header("Test Page Map"); style_adunit_config(ADUNIT_RIGHT_OK); } @ <ul id="sitemap" class="columns" style="column-width:20em"> if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){ @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li> } if( g.perm.Read ){ @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> } @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li> @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li> @ <li>%z(href("%R/hash-color-test"))Hash color test</a> if( g.perm.Admin ){ @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li> @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li> @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li> @ <li>%z(href("%R/test-warning"))Error Log test page</a></li> @ <li>%z(href("%R/repo_stat1"))Repository <tt>sqlite_stat1</tt> table</a> @ <li>%z(href("%R/repo_schema"))Repository schema</a></li> } if( g.perm.Read && g.perm.Hyperlink ){ @ <li>%z(href("%R/timewarps"))Timeline of timewarps</a></li> } @ <li>%z(href("%R/cookies"))Content of display preference cookie</a></li> @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li> @ <li>%z(href("%R/test-piechart"))Pie-Chart generator test</a></li> if( !isPopup ){ style_finish_page(); } } /* ** WEBPAGE: sitemap-timeline ** ** Generate a list of hyperlinks to various (obscure) variations on ** the /timeline page. */ void sitemap_timeline_page(void){ int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ login_check_credentials(); style_set_current_feature("sitemap"); if( P("popup")!=0 && cgi_csrf_safe(0) ){ /* If this is a POST from the same origin with the popup=1 parameter, ** then disable anti-robot defenses */ isPopup = 1; g.perm.Hyperlink = 1; g.jsHref = 0; } if( !isPopup ){ style_header("Timeline Examples"); style_adunit_config(ADUNIT_RIGHT_OK); } @ <ul id="sitemap" class="columns" style="column-width:20em"> @ <li>%z(href("%R/timeline?ymd"))Current day</a></li> @ <li>%z(href("%R/timeline?yw"))Current week</a></li> @ <li>%z(href("%R/timeline?ym"))Current month</a></li> @ <li>%z(href("%R/thisdayinhistory"))Today in history</a></li> @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10 @ check-ins</a></li> @ <li>%z(href("%R/timeline?namechng"))File name changes</a></li> @ <li>%z(href("%R/timeline?forks"))Forks</a></li> @ <li>%z(href("%R/timeline?cherrypicks"))Cherrypick merges</a></li> @ <li>%z(href("%R/timewarps"))Timewarps</a></li> @ <li>%z(href("%R/timeline?ubg"))Color-coded by user</a></li> @ <li>%z(href("%R/timeline?deltabg"))Delta vs. baseline manifests</a></li> @ </ul> if( !isPopup ){ style_finish_page(); } } |
Added src/skin.js.
> > > > > > > > | 1 2 3 4 5 6 7 8 | /* Javascript that runs for the /setup_skin page. */ (function(){ var x = document.getElementById('skStep1'); x.onchange = function(){ document.getElementById('f01').submit() } }()); |
Changes to src/skins.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2009 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".) | | < > < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > | | > | > > > > > > > > > > > | > > > > > > | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Copyright (c) 2009 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/ ** ******************************************************************************* ** ** Implementation of the Setup page for "skins". */ #include "config.h" #include <assert.h> #include "skins.h" /* ** SETTING: default-skin width=16 ** ** If the text value if this setting is the name of a built-in skin ** then the named skin becomes the default skin for the repository. */ /* ** 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 }, { "Ardoise", "ardoise", 0 }, { "Black & White", "black_and_white", 0 }, { "Blitz", "blitz", 0 }, { "Dark Mode", "darkmode", 0 }, { "Eagle", "eagle", 0 }, { "Étienne", "etienne", 0 }, { "Khaki", "khaki", 0 }, { "Original", "original", 0 }, { "Plain Gray", "plain_gray", 0 }, { "Xekri", "xekri", 0 }, }; /* ** A skin consists of five "files" named here: */ static const char *const azSkinFile[] = { "css", "header", "footer", "details", "js" }; /* ** 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; static int iDraftSkin = 0; /* ** Used by skin_use_alternative() to store the current skin rank skin ** so that the /skins page can, if warranted, warn the user that skin ** changes won't have any effect. */ static int nSkinRank = 6; /* ** How the specific skin being used was chosen */ #if INTERFACE #define SKIN_FROM_DRAFT 0 /* The "draftN" prefix on the PATH_INFO */ #define SKIN_FROM_CMDLINE 1 /* --skin option to server command-line */ #define SKIN_FROM_CGI 2 /* skin: parameter in CGI script */ #define SKIN_FROM_QPARAM 3 /* skin= query parameter */ #define SKIN_FROM_COOKIE 4 /* skin= from fossil_display_settings cookie*/ #define SKIN_FROM_SETTING 5 /* Built-in named by "default-skin" setting */ #define SKIN_FROM_CUSTOM 6 /* Skin values in CONFIG table */ #define SKIN_FROM_DEFAULT 7 /* The built-in named "default" */ #define SKIN_FROM_UNKNOWN 8 /* Do not yet know which skin to use */ #endif /* INTERFACE */ static int iSkinSource = SKIN_FROM_UNKNOWN; /* ** 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 */ const char *zValue; /* Value of the detail */ } aSkinDetail[] = { { "pikchr-background", "" }, { "pikchr-fontscale", "" }, { "pikchr-foreground", "" }, { "pikchr-scale", "" }, { "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. ** ** The 2nd parameter is a ranking of how important this alternative ** skin declaration is, and lower values trump higher ones. If a call ** to this function passes a higher-valued rank than a previous call, ** the subsequent call becomes a no-op. Only calls with the same or ** lower rank (i.e. higher priority) will overwrite a previous ** setting. This approach is used because the CGI/server-time ** initialization happens in an order which is incompatible with our ** preferred ranking, making it otherwise more invasive to tell the ** internals "the --skin flag ranks higher than a URL parameter" (the ** former gets initialized before both URL parameters and the /draft ** path determination). ** ** The rankings were initially defined in ** https://fossil-scm.org/forum/forumpost/caf8c9a8bb ** but where subsequently revised: ** ** 0) A skin name matching the glob pattern "draft[1-9]" at the start of ** the PATH_INFO. ** ** 1) The --skin flag for commands like "fossil ui", "fossil server", or ** "fossil http", or the "skin:" CGI config setting. ** ** 2) The "skin" display setting cookie or URL argument, in that ** order. If the "skin" URL argument is provided and refers to a legal ** skin then that will update the display cookie. If the skin name is ** illegal it is silently ignored. ** ** 3) The built-in skin identfied by the "default-skin" setting, if such ** a setting exists and matches one of the built-in skin names. ** ** 4) Skin properties (settings "css", "details", "footer", "header", ** and "js") from the CONFIG db table ** ** 5) The built-in skin named "default" ** ** The iSource integer privides additional detail about where the skin ** ** As a special case, a NULL or empty name resets zAltSkinDir and ** pAltSkin to 0 to indicate that the current config-side skin should ** be used (rank 3, above), then returns 0. */ char *skin_use_alternative(const char *zName, int rank, int iSource){ int i; Blob err = BLOB_INITIALIZER; if(rank > nSkinRank) return 0; nSkinRank = rank; if( zName && 1==rank && strchr(zName, '/')!=0 ){ zAltSkinDir = fossil_strdup(zName); iSkinSource = iSource; return 0; } if( zName && sqlite3_strglob("draft[1-9]", zName)==0 ){ skin_use_draft(zName[5] - '0'); iSkinSource = iSource; return 0; } if(!zName || !*zName){ pAltSkin = 0; zAltSkinDir = 0; return 0; } if( fossil_strcmp(zName, "custom")==0 ){ pAltSkin = 0; zAltSkinDir = 0; iSkinSource = iSource; return 0; } for(i=0; i<count(aBuiltinSkin); i++){ if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){ pAltSkin = &aBuiltinSkin[i]; iSkinSource = iSource; return 0; } } blob_appendf(&err, "available skins: %s", aBuiltinSkin[0].zLabel); for(i=1; i<count(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. ** ** This routine is called during command-line parsing for commands ** like "fossil ui" and "fossil http". */ void skin_override(void){ const char *zSkin = find_option("skin",0,1); if( zSkin ){ char *zErr = skin_use_alternative(zSkin, 1, SKIN_FROM_CMDLINE); if( zErr ) fossil_fatal("%s", zErr); } } /* ** Use one of the draft skins. */ void skin_use_draft(int i){ iDraftSkin = i; iSkinSource = SKIN_FROM_DRAFT; } /* ** The following routines return the various components of the skin ** that should be used for the current run. ** ** zWhat is one of: "css", "header", "footer", "details", "js" */ const char *skin_get(const char *zWhat){ const char *zOut; char *z; if( iDraftSkin ){ z = mprintf("draft%d-%s", iDraftSkin, zWhat); zOut = db_get(z, 0); fossil_free(z); if( zOut ) return zOut; } if( zAltSkinDir ){ char *z = mprintf("%s/%s.txt", zAltSkinDir, zWhat); if( file_isfile(z, ExtFILE) ){ Blob x; blob_read_from_file(&x, z, ExtFILE); fossil_free(z); return blob_str(&x); } fossil_free(z); } if( iSkinSource==SKIN_FROM_UNKNOWN ){ const char *zDflt = db_get("default-skin", 0); iSkinSource = SKIN_FROM_DEFAULT; if( zDflt!=0 ){ int i; for(i=0; i<count(aBuiltinSkin); i++){ if( fossil_strcmp(aBuiltinSkin[i].zLabel, zDflt)==0 ){ pAltSkin = &aBuiltinSkin[i]; iSkinSource = SKIN_FROM_SETTING; break; } } } } 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); }else if( iSkinSource==SKIN_FROM_DEFAULT ){ iSkinSource = SKIN_FROM_CUSTOM; } } return zOut; } /* ** Return the command-line option used to set the skin, or return NULL ** if the default skin is being used. */ const char *skin_in_use(void){ if( zAltSkinDir ) return zAltSkinDir; if( pAltSkin ) return pAltSkin->zLabel; return 0; } /* ** 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 = count(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. ** ** The zResource argument is the name of a CONFIG setting that ** defines the resource. Examples: "css", "logo-image". */ 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); } /* Change the ID every time Fossil is recompiled */ h = skin_hash(h, fossil_exe_id()); 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. ** |
︙ | ︙ | |||
681 682 683 684 685 686 687 | free(z); z = 0; } return z; } /* | > > > > | > > > > > | > > > > > | > > > < > > > > > > | > > > | > > > | < < | < > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | | < | | | < > > | | < | < | < | | | | < > | | | > > > > > > > > > > > > | > > > > > > > > | > > > > | > > > | > > > > > > | < | > > > > | > > > > > > | < > < < < > > > > > > > > > > > > > > > > | | | | | | | | > > > | > > > | > > > > | > > > > > > > > | | > > > > > > | > > > > | > > > > | < < < < > > > > > > > > > > | > > > > > > | < | > | | < < | > > > | | > > > > > > > > > > | > | < > > > > > | < > > > > > > > > > > > > > > > > | < < > | | < < < > > | < < < < | | > > > > > | < > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > | > > > > | < | < < < < < > > > > | | > | < | | | | | > > > > > > > > > > > > > > > > | | > | > > | > > > | 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 | 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<count(aBuiltinSkin); 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; int i; Blob val; blob_zero(&val); for(i=0; i<count(azSkinFile); i++){ if( zName ){ zLabel = mprintf("skins/%s/%s.txt", zName, azSkinFile[i]); z = builtin_text(zLabel); fossil_free(zLabel); }else{ z = db_get(azSkinFile[i], 0); if( z==0 ){ zLabel = mprintf("skins/default/%s.txt", azSkinFile[i]); z = builtin_text(zLabel); fossil_free(zLabel); } } db_unprotect(PROTECT_CONFIG); blob_appendf(&val, "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now());\n", azSkinFile[i], z ); db_protect_pop(); } 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_set_current_feature("skins"); 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="%R/setup_skin_admin" 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_finish_page(); return 1; } db_unprotect(PROTECT_CONFIG); db_multi_exec( "UPDATE config SET name='skin:%q' WHERE name='skin:%q';", zNewName, zOldName ); db_protect_pop(); 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_set_current_feature("skins"); 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="%R/setup_skin_admin" 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_finish_page(); return 1; } db_unprotect(PROTECT_CONFIG); db_multi_exec( "INSERT OR IGNORE INTO config(name, value, mtime)" "VALUES('skin:%q',%Q,now())", zNewName, zCurrent ); db_protect_pop(); return 0; } /* ** Return true if a custom skin exists */ static int skin_exists_custom(void){ return db_exists("SELECT 1 FROM config WHERE name IN" " ('css','details','footer','header','js')"); } static void skin_publish(int); /* Forward reference */ /* ** WEBPAGE: setup_skin_admin ** ** Administrative actions on skins. For administrators only. */ void setup_skin_admin(void){ const char *z; char *zName; char *zErr = 0; const char *zCurrent = 0; /* Current skin */ int i; /* Loop counter */ Stmt q; int once; const char *zOverride = 0; const char *zDfltSkin = 0; int seenDefault = 0; int hasCustom; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } db_begin_transaction(); zCurrent = getSkin(0); for(i=0; i<count(aBuiltinSkin); i++){ aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel); } style_set_current_feature("skins"); if( cgi_csrf_safe(2) ){ /* Process requests to delete a user-defined skin */ if( P("del1") && P("sn")!=0 ){ style_header("Confirm Custom Skin Delete"); @ <form action="%R/setup_skin_admin" 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_finish_page(); db_end_transaction(1); return; } if( P("del2")!=0 ){ db_unprotect(PROTECT_CONFIG); if( fossil_strcmp(P("sn"),"custom")==0 ){ db_multi_exec("DELETE FROM config WHERE name IN" "('css','details','footer','header','js')"); }else if( (zName = skinVarName(P("sn"), 1))!=0 ){ db_multi_exec("DELETE FROM config WHERE name=%Q", zName); } db_protect_pop(); } if( P("draftdel")!=0 ){ const char *zDraft = P("name"); if( sqlite3_strglob("draft[1-9]",zDraft)==0 ){ db_unprotect(PROTECT_CONFIG); db_multi_exec("DELETE FROM config WHERE name GLOB '%q-*'", zDraft); db_protect_pop(); } } if( P("editdraft")!=0 ){ db_end_transaction(0); cgi_redirectf("%R/setup_skin"); return; } if( skinRename() || skinSave(zCurrent) ){ db_end_transaction(0); return; } if( P("setdflt") && (z = P("bisl"))!=0 ){ if( z[0] ){ db_set("default-skin", z, 0); }else{ db_unset("default-skin", 0); } db_end_transaction(0); cgi_redirectf("%R/setup_skin_admin"); 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 */ hasCustom = skin_exists_custom(); if( hasCustom ){ zCurrent = getSkin(0); for(i=0; i<count(aBuiltinSkin); 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_unprotect(PROTECT_CONFIG); db_multi_exec( "INSERT INTO config(name,value,mtime) VALUES(" " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," " %Q,now())", zCurrent ); db_protect_pop(); } } } seen = 0; if( z[0]>='1' && z[0]<='9' && z[1]==0 ){ skin_publish(z[0]-'0'); seen = 1; } for(i=0; seen==0 && i<count(aBuiltinSkin); i++){ if( fossil_strcmp(aBuiltinSkin[i].zDesc, z)==0 ){ seen = 1; zCurrent = aBuiltinSkin[i].zSQL; db_unprotect(PROTECT_CONFIG); db_multi_exec("%s", zCurrent/*safe-for-%s*/); db_protect_pop(); break; } } if( !seen ){ zName = skinVarName(z,0); zCurrent = db_get(zName, 0); db_unprotect(PROTECT_CONFIG); db_multi_exec("%s", zCurrent/*safe-for-%s*/); db_protect_pop(); } } } zDfltSkin = db_get("default-skin",0); hasCustom = skin_exists_custom(); if( !hasCustom && zDfltSkin==0 ){ zDfltSkin = "default"; } style_header("Skins"); if( zErr ){ @ <p style="color:red">%h(zErr)</p> } @ <table border="0"> @ <tr><td colspan=4><h2>Built-in Skins:</h2></td></tr> for(i=0; i<count(aBuiltinSkin); i++){ z = aBuiltinSkin[i].zDesc; @ <tr><td>%d(i+1).<td>%h(z)<td> <td> @ <form action="%R/setup_skin_admin" method="POST"> login_insert_csrf_secret(); if( zDfltSkin==0 || fossil_strcmp(aBuiltinSkin[i].zLabel, zDfltSkin)!=0 ){ /* vvvv--- mnemonic: Built-In Skin Label */ @ <input type="hidden" name="bisl" value="%h(aBuiltinSkin[i].zLabel)"> @ <input type="submit" name="setdflt" value="Set"> }else{ @ (Selected) seenDefault = 1; } if( pAltSkin==&aBuiltinSkin[i] && iSkinSource!=SKIN_FROM_SETTING ){ @ (Override) zOverride = z; } @ </form></td></tr> } if( zOverride ){ @ <tr><td> <td colspan="3"> @ <p>Note: Built-in skin "%h(zOverride)" is currently being used because of switch( iSkinSource ){ case SKIN_FROM_CMDLINE: @ the --skin command-line option. break; case SKIN_FROM_CGI: @ the "skin:" option on CGI script. break; case SKIN_FROM_QPARAM: @ the "skin=NAME" query parameter. break; case SKIN_FROM_COOKIE: @ the "skin" value of the @ <a href='./fdscookie'>fossil_display_setting</a> cookie. break; case SKIN_FROM_SETTING: @ the "default-skin" setting. break; default: @ reasons unknown. (Fix me!) break; } @ </tr> } i++; @ <tr><td colspan=4><h2>Custom skin:</h2></td></tr> @ <tr><td>%d(i). if( hasCustom ){ @ <td>Custom<td> <td> }else{ @ <td><i>(None)</i><td> <td> } @ <form method="post"> login_insert_csrf_secret(); if( hasCustom ){ @ <input type="submit" name="save" value="Backup"> @ <input type="submit" name="editdraft" value="Edit"> if( !seenDefault ){ @ (Selected) }else{ @ <input type="hidden" name="bisl" value=""> @ <input type="submit" name="setdflt" value="Set"> @ <input type="submit" name="del1" value="Delete"> @ <input type="hidden" name="sn" value="custom"> } }else{ @ <input type="submit" name="editdraft" value="Create"> } @ </form> @ </td></tr> db_prepare(&q, "SELECT substr(name, 6) FROM config" " WHERE name GLOB 'skin:*'" " ORDER BY name" ); once = 1; while( db_step(&q)==SQLITE_ROW ){ const char *zN = db_column_text(&q, 0); i++; if( once ){ once = 0; @ <tr><td colspan=4><h2>Backups of past custom skins:</h2></td></tr> } @ <tr><td>%d(i).<td>%h(zN)<td> <td> @ <form action="%R/setup_skin_admin" method="post"> login_insert_csrf_secret(); @ <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); db_prepare(&q, "SELECT DISTINCT substr(name, 1, 6) FROM config" " WHERE name GLOB 'draft[1-9]-*'" " ORDER BY name" ); once = 1; while( db_step(&q)==SQLITE_ROW ){ const char *zN = db_column_text(&q, 0); i++; if( once ){ once = 0; @ <tr><td colspan=4><h2>Draft skins:</h2></td></tr> } @ <tr><td>%d(i).<td>%h(zN)<td> <td> @ <form action="%R/setup_skin_admin" method="post"> login_insert_csrf_secret(); @ <input type="submit" name="load" value="Install"> @ <input type="submit" name="draftdel" value="Delete"> @ <input type="hidden" name="name" value="%h(zN)"> @ <input type="hidden" name="sn" value="%h(zN+5)"> @ </form></tr> } db_finalize(&q); @ </table> style_finish_page(); db_end_transaction(0); } /* ** Generate HTML for a <select> that lists all the available skin names, ** except for zExcept if zExcept!=NULL. */ static void skin_emit_skin_selector( const char *zVarName, /* Variable name for the <select> */ const char *zDefault, /* The default value, if not NULL */ const char *zExcept /* Omit this skin if not NULL */ ){ int i; Stmt s; @ <select size='1' name='%s(zVarName)'> if( fossil_strcmp(zExcept, "current")!=0 && skin_exists_custom() ){ @ <option value='current'>Current Custom Skin</option> } for(i=0; i<count(aBuiltinSkin); i++){ const char *zName = aBuiltinSkin[i].zLabel; if( fossil_strcmp(zName, zExcept)==0 ) continue; if( fossil_strcmp(zDefault, zName)==0 ){ @ <option value='%s(zName)' selected>\ @ %h(aBuiltinSkin[i].zDesc)</option> }else{ @ <option value='%s(zName)'>\ @ %h(aBuiltinSkin[i].zDesc)</option> } } db_prepare(&s, "SELECT DISTINCT substr(name,1,6) FROM config" " WHERE name GLOB 'draft[1-9]-*' ORDER BY 1"); while( db_step(&s)==SQLITE_ROW ){ const char *zName = db_column_text(&s, 0); if( fossil_strcmp(zName, zExcept)==0 ) continue; if( fossil_strcmp(zDefault, zName)==0 ){ @ <option value='%s(zName)' selected>%s(zName)</option> }else{ @ <option value='%s(zName)'>%s(zName)</option> } } db_finalize(&s); @ </select> } /* ** Return the text of one of the skin files. */ static const char *skin_file_content(const char *zLabel, const char *zFile){ const char *zResult; if( fossil_strcmp(zLabel, "current")==0 ){ zResult = skin_get(zFile); }else if( sqlite3_strglob("draft[1-9]", zLabel)==0 ){ zResult = db_get_mprintf("", "%s-%s", zLabel, zFile); }else{ int i; for(i=0; i<2; i++){ char *zKey = mprintf("skins/%s/%s.txt", zLabel, zFile); zResult = builtin_text(zKey); fossil_free(zKey); if( zResult!=0 ) break; zLabel = "default"; } } return zResult; } extern const struct strctCssDefaults { /* From the generated default_css.h, which we cannot #include here ** without causing an ODR violation. */ const char *elementClass; /* Name of element needed */ const char *value; /* CSS text */ } cssDefaultList[]; /* ** WEBPAGE: setup_skinedit ** ** Edit aspects of a skin determined by the w= query parameter. ** Requires Admin or Setup privileges. ** ** w=NUM -- 0=CSS, 1=footer, 2=header, 3=details, 4=js ** sk=NUM -- the draft skin number */ 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", }, /* 4 */ { "js", "JavaScript", "Script", }, }; const char *zBasis; /* The baseline file */ const char *zOrig; /* Original content prior to editing */ const char *zContent; /* Content after editing */ const char *zDflt; /* Default content */ char *zDraft; /* Which draft: "draft%d" */ char *zTitle; /* Title of this page */ const char *zFile; /* One of "css", "footer", "header", "details" */ int iSkin; /* draft number. 1..9 */ int ii; /* Index in aSkinAttr[] of this file */ int j; /* Loop counter */ int isRevert = 0; /* True if Revert-to-Baseline was pressed */ login_check_credentials(); /* Figure out which skin we are editing */ iSkin = atoi(PD("sk","1")); if( iSkin<1 || iSkin>9 ) iSkin = 1; /* Check that the user is authorized to edit this skin. */ if( !g.perm.Admin ){ char *zAllowedEditors = ""; Glob *pAllowedEditors; int isMatch = 0; if( login_is_individual() ){ zAllowedEditors = db_get_mprintf("", "draft%d-users", iSkin); } if( zAllowedEditors[0] ){ pAllowedEditors = glob_create(zAllowedEditors); isMatch = glob_match(pAllowedEditors, g.zLogin); glob_free(pAllowedEditors); } if( isMatch==0 ){ login_needed(0); return; } } /* figure out which file is to be edited */ ii = atoi(PD("w","0")); if( ii<0 || ii>count(aSkinAttr) ) ii = 0; zFile = aSkinAttr[ii].zFile; zDraft = mprintf("draft%d", iSkin); zTitle = mprintf("%s for Draft%d", aSkinAttr[ii].zTitle, iSkin); zBasis = PD("basis","current"); zDflt = skin_file_content(zBasis, zFile); zOrig = db_get_mprintf(zDflt, "draft%d-%s",iSkin,zFile); zContent = PD(zFile,zOrig); if( P("revert")!=0 && cgi_csrf_safe(2) ){ zContent = zDflt; isRevert = 1; } db_begin_transaction(); style_set_current_feature("skins"); style_header("%s", zTitle); for(j=0; j<count(aSkinAttr); j++){ style_submenu_element(aSkinAttr[j].zSubmenu, "%R/setup_skinedit?w=%d&basis=%h&sk=%d",j,zBasis,iSkin); } @ <form action="%R/setup_skinedit" method="post"><div> login_insert_csrf_secret(); @ <input type='hidden' name='w' value='%d(ii)'> @ <input type='hidden' name='sk' value='%d(iSkin)'> @ <h2>Edit %s(zTitle):</h2> if( P("submit") && cgi_csrf_safe(2) && (zOrig==0 || strcmp(zOrig,zContent)!=0) ){ db_set_mprintf(zContent, 0, "draft%d-%s",iSkin,zFile); } @ <textarea name="%s(zFile)" rows="10" cols="80">\ @ %h(zContent)</textarea> @ <br> @ <input type="submit" name="submit" value="Apply Changes"> if( isRevert ){ @ ← Press to complete reversion to "%s(zBasis)" }else if( fossil_strcmp(zContent,zDflt)!=0 ){ @ <input type="submit" name="revert" value='Revert To "%s(zBasis)"'> } @ <hr> @ Baseline: \ skin_emit_skin_selector("basis", zBasis, zDraft); @ <input type="submit" name="diff" value="Unified Diff"> @ <input type="submit" name="sbsdiff" value="Side-by-Side Diff"> if( P("diff")!=0 || P("sbsdiff")!=0 ){ Blob from, to, out; DiffConfig DCfg; construct_diff_flags(1, &DCfg); DCfg.diffFlags |= DIFF_STRIP_EOLCR; if( P("sbsdiff")!=0 ) DCfg.diffFlags |= DIFF_SIDEBYSIDE; blob_init(&to, zContent, -1); blob_init(&from, skin_file_content(zBasis, zFile), -1); blob_zero(&out); DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG; if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){ text_diff(&from, &to, &out, &DCfg); @ %s(blob_str(&out)) }else{ DCfg.diffFlags |= DIFF_LINENO; text_diff(&from, &to, &out, &DCfg); @ <pre class="udiff"> @ %s(blob_str(&out)) @ </pre> } blob_reset(&from); blob_reset(&to); blob_reset(&out); } @ </div></form> style_finish_page(); db_end_transaction(0); } /* ** Try to initialize draft skin iSkin to the built-in or preexisting ** skin named by zTemplate. */ static void skin_initialize_draft(int iSkin, const char *zTemplate){ int i; if( zTemplate==0 ) return; for(i=0; i<count(azSkinFile); i++){ const char *z = skin_file_content(zTemplate, azSkinFile[i]); db_set_mprintf(z, 0, "draft%d-%s", iSkin, azSkinFile[i]); } } /* ** Publish the draft skin iSkin as the new default. */ static void skin_publish(int iSkin){ char *zCurrent; /* SQL description of the current skin */ char *zBuiltin; /* SQL description of a built-in skin */ int i; int seen = 0; /* True if no need to make a backup */ /* 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<count(aBuiltinSkin); i++){ zBuiltin = getSkin(aBuiltinSkin[i].zLabel); if( fossil_strcmp(zBuiltin, 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_unprotect(PROTECT_CONFIG); db_multi_exec( "INSERT INTO config(name,value,mtime) VALUES(" " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," " %Q,now())", zCurrent ); db_protect_pop(); } /* Publish draft iSkin */ for(i=0; i<count(azSkinFile); i++){ char *zNew = db_get_mprintf("", "draft%d-%s", iSkin, azSkinFile[i]); db_set(azSkinFile[i]/*works-like:"x"*/, zNew, 0); } db_unset("default-skin", 0); } /* ** WEBPAGE: setup_skin ** ** Generate a page showing the steps needed to create or edit ** a custom skin. */ void setup_skin(void){ int i; /* Loop counter */ int iSkin; /* Which draft skin is being edited */ int isSetup; /* True for an administrator */ int isEditor; /* Others authorized to make edits */ char *zAllowedEditors; /* Who may edit the draft skin */ char *zBase; /* Base URL for draft under test */ static const char *const azTestPages[] = { "home", "timeline", "dir?ci=tip", "dir?ci=tip&type=tree", "brlist", "info/trunk", }; /* Figure out which skin we are editing */ iSkin = atoi(PD("sk","1")); if( iSkin<1 || iSkin>9 ) iSkin = 1; /* Figure out if the current user is allowed to make administrative ** changes and/or edits */ login_check_credentials(); if( !login_is_individual() ){ login_needed(0); return; } zAllowedEditors = db_get_mprintf("", "draft%d-users", iSkin); if( g.perm.Admin ){ isSetup = isEditor = 1; }else{ Glob *pAllowedEditors; isSetup = isEditor = 0; if( zAllowedEditors[0] ){ pAllowedEditors = glob_create(zAllowedEditors); isEditor = glob_match(pAllowedEditors, g.zLogin); glob_free(pAllowedEditors); } } /* Initialize the skin, if requested and authorized. */ if( P("init3")!=0 && isEditor ){ skin_initialize_draft(iSkin, P("initskin")); } if( P("submit2")!=0 && isSetup ){ db_set_mprintf(PD("editors",""), 0, "draft%d-users", iSkin); zAllowedEditors = db_get_mprintf("", "draft%d-users", iSkin); } /* Publish the draft skin */ if( P("pub7")!=0 && PB("pub7ck1") && PB("pub7ck2") ){ skin_publish(iSkin); } style_set_current_feature("skins"); style_header("Customize Skin"); if( g.perm.Admin ){ style_submenu_element("Skin-Admin", "%R/setup_skin_admin"); } @ <p>Customize the look of this Fossil repository by making changes @ to the CSS, Header, Footer, and Detail Settings in one of nine "draft" @ configurations. Then, after verifying that all is working correctly, @ publish the draft to become the new main Skin. Users can select a skin @ of their choice from the built-in ones or the locally-edited one via @ <a href='%R/skins'>the /skins page</a>.</p> @ @ <a name='step1'></a> @ <h1>Step 1: Identify Which Draft To Use</h1> @ @ <p>The main skin of Fossil cannot be edited directly. Instead, @ edits are made to one of nine draft skins. A draft skin can then @ be published to become the default skin. @ Nine separate drafts are available to facilitate A/B testing.</p> @ @ <form method='POST' action='%R/setup_skin#step2' id='f01'> @ <p class='skinInput'>Draft skin to edit: @ <select size='1' name='sk' id='skStep1'> for(i=1; i<=9; i++){ if( i==iSkin ){ @ <option value='%d(i)' selected>draft%d(i)</option> }else{ @ <option value='%d(i)'>draft%d(i)</option> } } @ </select> @ </p> @ </form> @ @ <a name='step2'></a> @ <h1>Step 2: Authenticate</h1> @ if( isSetup ){ @ <p>As an administrator, you can make any edits you like to this or @ any other skin. You can also authorize other users to edit this @ skin. Any user whose login name matches the comma-separated list @ of GLOB expressions below is given special permission to edit @ the draft%d(iSkin) skin: @ @ <form method='POST' action='%R/setup_skin#step2' id='f02'> @ <p class='skinInput'> @ <input type='hidden' name='sk' value='%d(iSkin)'> @ Authorized editors for skin draft%d(iSkin): @ <input type='text' name='editors' value='%h(zAllowedEditors)'\ @ width='40'> @ <input type='submit' name='submit2' value='Change'> @ </p> @ </form> }else if( isEditor ){ @ <p>You are authorized to make changes to the draft%d(iSkin) skin. @ Continue to the <a href='#step3'>next step</a>.</p> }else{ @ <p>You are not authorized to make changes to the draft%d(iSkin) @ skin. Contact the administrator of this Fossil repository for @ further information.</p> } @ @ <a name='step3'></a> @ <h1>Step 3: Initialize The Draft</h1> @ if( !isEditor ){ @ <p>You are not allowed to initialize draft%d(iSkin). Contact @ the administrator for this repository for more information. }else{ char *zDraft = mprintf("draft%d", iSkin); @ <p>Initialize the draft%d(iSkin) skin to one of the built-in skins @ or a preexisting skin, to use as a baseline.</p> @ @ <form method='POST' action='%R/setup_skin#step4' id='f03'> @ <p class='skinInput'> @ <input type='hidden' name='sk' value='%d(iSkin)'> @ Initialize skin <b>draft%d(iSkin)</b> using skin_emit_skin_selector("initskin", 0, zDraft); fossil_free(zDraft); @ <input type='submit' name='init3' value='Go'> @ </p> @ </form> } @ @ <a name='step4'></a> @ <h1>Step 4: Make Edits</h1> @ if( !isEditor ){ @ <p>You are not authorized to make edits to the draft%d(iSkin) skin. @ Contact the administrator of this Fossil repository for help.</p> }else{ @ <p>Edit the components of the draft%d(iSkin) skin: @ <ul> @ <li><a href='%R/setup_skinedit?w=0&sk=%d(iSkin)' target='_blank'>CSS</a> @ <li><a href='%R/setup_skinedit?w=2&sk=%d(iSkin)' target='_blank'>\ @ Header</a> @ <li><a href='%R/setup_skinedit?w=1&sk=%d(iSkin)' target='_blank'>\ @ Footer</a> @ <li><a href='%R/setup_skinedit?w=3&sk=%d(iSkin)' target='_blank'>\ @ Details</a> @ <li><a href='%R/setup_skinedit?w=4&sk=%d(iSkin)' target='_blank'>\ @ Javascript</a> (optional) @ </ul> } @ @ <a name='step5'></a> @ <h1>Step 5: Verify The Draft Skin</h1> @ @ <p>To test this draft skin, insert text "/draft%d(iSkin)/" just before the @ operation name in the URL. Here are a few links to try: @ <ul> if( iDraftSkin && sqlite3_strglob("*/draft[1-9]", g.zBaseURL)==0 ){ zBase = mprintf("%.*s/draft%d", (int)strlen(g.zBaseURL)-7,g.zBaseURL,iSkin); }else{ zBase = mprintf("%s/draft%d", g.zBaseURL, iSkin); } for(i=0; i<count(azTestPages); i++){ @ <li><a href='%s(zBase)/%s(azTestPages[i])' target='_blank'>\ @ %s(zBase)/%s(azTestPages[i])</a> } fossil_free(zBase); @ </ul> @ @ <p>You will probably need to press Reload on your browser before any @ CSS changes will take effect.</p> @ @ <a hame='step6'></a> @ <h1>Step 6: Iterate</h1> @ @ <p>Repeat <a href='#step4'>step 4</a> and @ <a href='#step5'>step 5</a> as many times as necessary to create @ a production-ready skin. @ @ <a name='step7'></a> @ <h1>Step 7: Publish</h1> @ if( !g.perm.Admin ){ @ <p>Only administrators are allowed to publish draft skins. Contact @ an administrator to get this "draft%d(iSkin)" skin published.</p> }else{ @ <p>When the draft%d(iSkin) skin is ready for production use, @ make it the default skin by clicking the acknowledgements and @ pressing the button below:</p> @ @ <form method='POST' action='%R/setup_skin#step7'> @ <p class='skinInput'> @ <input type='hidden' name='sk' value='%d(iSkin)'> @ <input type='checkbox' name='pub7ck1' value='yes'>\ @ Skin draft%d(iSkin) has been tested and found ready for production.<br> @ <input type='checkbox' name='pub7ck2' value='yes'>\ @ The current skin should be overwritten with draft%d(iSkin).<br> @ <input type='submit' name='pub7' value='Publish Draft%d(iSkin)'> @ </p></form> @ @ <p>You will probably need to press Reload on your browser after @ publishing the new skin.</p> } @ @ <a name='step8'></a> @ <h1>Step 8: Cleanup and Undo Actions</h1> @ if( !g.perm.Admin ){ @ <p>Administrators can optionally save or restore legacy skins, and/or @ undo a prior publish. }else{ @ <p>Visit the <a href='%R/setup_skin_admin'>Skin Admin</a> page @ for cleanup and recovery actions. } builtin_request_js("skin.js"); style_finish_page(); } /* ** WEBPAGE: skins ** ** Show a list of all of the built-in skins, plus the responsitory skin, ** and provide the user with an opportunity to change to any of them. */ void skins_page(void){ int i; char *zBase = fossil_strdup(g.zTop); size_t nBase = strlen(zBase); login_check_credentials(); if( iDraftSkin && sqlite3_strglob("*/draft?", zBase)==0 ){ nBase -= 7; zBase[nBase] = 0; }else if( pAltSkin ){ char *zPattern = mprintf("*/skn_%s", pAltSkin->zLabel); if( sqlite3_strglob(zPattern, zBase)==0 ){ nBase -= strlen(zPattern)-1; zBase[nBase] = 0; } fossil_free(zPattern); } style_header("Skins"); if( iDraftSkin || nSkinRank<=1 ){ @ <p class="warning">Warning: if( iDraftSkin>0 ){ @ you are using a draft skin, }else{ @ this fossil instance was started with a hard-coded skin @ value } @ which supercedes any option selected below. A skin selected @ below will be recorded in your @ "%z(href("%R/fdscookie"))fossil_display_settings</a>" cookie @ but will not be used so long as the site has a @ higher-priority skin in place. @ </p> } @ <p>The following skins are available for this repository:</p> @ <ul> for(i=0; i<count(aBuiltinSkin); i++){ if( pAltSkin==&aBuiltinSkin[i] ){ @ <li> %h(aBuiltinSkin[i].zDesc) ← <i>Currently in use</i> }else{ char *zUrl = href("%R/skins?skin=%T", aBuiltinSkin[i].zLabel); @ <li> %z(zUrl)%h(aBuiltinSkin[i].zDesc)</a> } } if( skin_exists_custom() ){ if( pAltSkin==0 && zAltSkinDir==0 && iDraftSkin==0 ){ @ <li> Custom skin for this repository ← <i>Currently in use</i> }else{ @ <li> %z(href("%R/skins?skin=custom"))\ @ Custom skin for this repository</a> } } @ </ul> if( iSkinSource<SKIN_FROM_CUSTOM ){ @ <p>The current skin is selected by switch( iSkinSource ){ case SKIN_FROM_DRAFT: @ the "debugN" prefix on the PATH_INFO portion of the URL. break; case SKIN_FROM_CMDLINE: @ the "--skin" command-line option on the Fossil server. break; case SKIN_FROM_CGI: @ the "skin:" property in the CGI script that runs the Fossil server. break; case SKIN_FROM_QPARAM: @ the "skin=NAME" query parameter on the URL. break; case SKIN_FROM_COOKIE: @ the "skin" property in the @ "%z(href("%R/fdscookie"))fossil_display_settings</a>" cookie. break; case SKIN_FROM_SETTING: @ the "default-skin" setting on the repository. break; } } if( iSkinSource==SKIN_FROM_COOKIE || iSkinSource==SKIN_FROM_QPARAM ){ @ <ul> @ <li> %z(href("%R/skins?skin="))<i>Let Fossil choose \ @ which skin to use</i></a> @ </ul> } style_finish_page(); if( P("skin")!=0 ){ sqlite3_uint64 x; sqlite3_randomness(sizeof(x), &x); cgi_redirectf("%R/skins/%llx", x); } fossil_free(zBase); } |
Added src/smtp.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 | /* ** Copyright (c) 2018 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/ ** ******************************************************************************* ** ** Implementation of SMTP (Simple Mail Transport Protocol) according ** to RFC 5321. */ #include "config.h" #include "smtp.h" #include <assert.h> #if (HAVE_DN_EXPAND || HAVE___NS_NAME_UNCOMPRESS || HAVE_NS_NAME_UNCOMPRESS) \ && (HAVE_NS_PARSERR || HAVE___NS_PARSERR) && !defined(FOSSIL_OMIT_DNS) # include <sys/types.h> # include <netinet/in.h> # if defined(HAVE_BIND_RESOLV_H) # include <bind/resolv.h> # include <bind/arpa/nameser_compat.h> # else # include <arpa/nameser.h> # include <resolv.h> # endif # if defined(HAVENS_NAME_UNCOMPRESS) && !defined(dn_expand) # define dn_expand ns_name_uncompress # endif # if defined(HAVE__NS_NAME_UNCOMPRESS) && !defined(dn_expand) # define dn_expand __ns_name_uncompress # endif # define FOSSIL_UNIX_STYLE_DNS 1 #endif #if defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) # include <windows.h> # include <windns.h> # define FOSSIL_WINDOWS_STYLE_DNS 1 #endif /* ** Find the hostname for receiving email for the domain given ** in zDomain. Return NULL if not found or not implemented. ** If multiple email receivers are advertized, pick the one with ** the lowest preference number. ** ** The returned string is obtained from fossil_malloc() ** and should be released using fossil_free(). */ char *smtp_mx_host(const char *zDomain){ #if defined(FOSSIL_UNIX_STYLE_DNS) int nDns; /* Length of the DNS reply */ int rc; /* Return code from various APIs */ int i; /* Loop counter */ int iBestPriority = 9999999; /* Best priority */ int nRec; /* Number of answers */ ns_msg h; /* DNS reply parser */ const unsigned char *pBest = 0; /* RDATA for the best answer */ unsigned char aDns[5000]; /* Raw DNS reply content */ char zHostname[5000]; /* Hostname for the MX */ nDns = res_query(zDomain, C_IN, T_MX, aDns, sizeof(aDns)); if( nDns<=0 ) return 0; res_init(); rc = ns_initparse(aDns,nDns,&h); if( rc ) return 0; nRec = ns_msg_count(h, ns_s_an); for(i=0; i<nRec; i++){ ns_rr x; int priority, sz; const unsigned char *p; rc = ns_parserr(&h, ns_s_an, i, &x); if( rc ) continue; p = ns_rr_rdata(x); sz = ns_rr_rdlen(x); if( sz>2 ){ priority = p[0]*256 + p[1]; if( priority<iBestPriority ){ pBest = p; iBestPriority = priority; } } } if( pBest ){ dn_expand(aDns, aDns+nDns, pBest+2, zHostname, sizeof(zHostname)); return fossil_strdup(zHostname); } return 0; #elif defined(FOSSIL_WINDOWS_STYLE_DNS) DNS_STATUS status; /* Return status */ PDNS_RECORDA pDnsRecord, p; /* Pointer to DNS_RECORD structure */ int iBestPriority = 9999999; /* Best priority */ char *pBest = 0; /* RDATA for the best answer */ status = DnsQuery_UTF8(zDomain, /* Domain name */ DNS_TYPE_MX, /* DNS record type */ DNS_QUERY_STANDARD, /* Query options */ NULL, /* List of DNS servers */ &pDnsRecord, /* Query results */ NULL); /* Reserved */ if( status ) return NULL; p = pDnsRecord; while( p ){ if( p->Data.MX.wPreference<iBestPriority ){ iBestPriority = p->Data.MX.wPreference; pBest = p->Data.MX.pNameExchange; } p = p->pNext; } if( pBest ){ pBest = fossil_strdup(pBest); } DnsRecordListFree(pDnsRecord, DnsFreeRecordListDeep); return pBest; #else return 0; #endif /* defined(FOSSIL_WINDOWS_STYLE_DNS) */ } /* ** COMMAND: test-find-mx ** ** Usage: %fossil test-find-mx DOMAIN ... ** ** Do a DNS MX lookup to find the hostname for sending email for ** DOMAIN. */ void test_find_mx(void){ int i; if( g.argc<=2 ){ usage("DOMAIN ..."); } for(i=2; i<g.argc; i++){ char *z = smtp_mx_host(g.argv[i]); fossil_print("%s: %s\n", g.argv[i], z); fossil_free(z); } } #if INTERFACE /* ** Information about a single SMTP connection. */ struct SmtpSession { const char *zFrom; /* Domain from which we are sending */ const char *zDest; /* Domain that will receive the email */ char *zHostname; /* Hostname of SMTP server for zDest */ u32 smtpFlags; /* Flags changing the operation */ FILE *logFile; /* Write session transcript to this log file */ Blob *pTranscript; /* Record session transcript here */ int atEof; /* True after connection closes */ char *zErr; /* Error message */ Blob inbuf; /* Input buffer */ }; /* Allowed values for SmtpSession.smtpFlags */ #define SMTP_TRACE_STDOUT 0x00001 /* Debugging info to console */ #define SMTP_TRACE_FILE 0x00002 /* Debugging info to logFile */ #define SMTP_TRACE_BLOB 0x00004 /* Record transcript */ #define SMTP_DIRECT 0x00008 /* Skip the MX lookup */ #define SMTP_PORT 0x00010 /* Use an alternate port number */ #endif /* ** Shutdown an SmtpSession */ void smtp_session_free(SmtpSession *pSession){ socket_close(); blob_reset(&pSession->inbuf); fossil_free(pSession->zHostname); fossil_free(pSession->zErr); fossil_free(pSession); } /* ** Allocate a new SmtpSession object. ** ** Both zFrom and zDest must be specified. ** ** The ... arguments are in this order: ** ** SMTP_PORT: int ** SMTP_TRACE_FILE: FILE* ** SMTP_TRACE_BLOB: Blob* */ SmtpSession *smtp_session_new( const char *zFrom, /* Domain for the client */ const char *zDest, /* Domain of the server */ u32 smtpFlags, /* Flags */ ... /* Arguments depending on the flags */ ){ SmtpSession *p; va_list ap; UrlData url; p = fossil_malloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); p->zFrom = zFrom; p->zDest = zDest; p->smtpFlags = smtpFlags; memset(&url, 0, sizeof(url)); url.port = 25; blob_init(&p->inbuf, 0, 0); va_start(ap, smtpFlags); if( smtpFlags & SMTP_PORT ){ url.port = va_arg(ap, int); } if( smtpFlags & SMTP_TRACE_FILE ){ p->logFile = va_arg(ap, FILE*); } if( smtpFlags & SMTP_TRACE_BLOB ){ p->pTranscript = va_arg(ap, Blob*); } va_end(ap); if( (smtpFlags & SMTP_DIRECT)!=0 ){ int i; p->zHostname = fossil_strdup(zDest); for(i=0; p->zHostname[i] && p->zHostname[i]!=':'; i++){} if( p->zHostname[i]==':' ){ p->zHostname[i] = 0; url.port = atoi(&p->zHostname[i+1]); } }else{ p->zHostname = smtp_mx_host(zDest); } if( p->zHostname==0 ){ p->atEof = 1; p->zErr = mprintf("cannot locate SMTP server for \"%s\"", zDest); return p; } url.name = p->zHostname; socket_global_init(); if( socket_open(&url) ){ p->atEof = 1; p->zErr = socket_errmsg(); socket_close(); } return p; } /* ** Send a single line of output the SMTP client to the server. */ static void smtp_send_line(SmtpSession *p, const char *zFormat, ...){ Blob b = empty_blob; va_list ap; char *z; int n; if( p->atEof ) return; va_start(ap, zFormat); blob_vappendf(&b, zFormat, ap); va_end(ap); z = blob_buffer(&b); n = blob_size(&b); assert( n>=2 ); assert( z[n-1]=='\n' ); assert( z[n-2]=='\r' ); if( p->smtpFlags & SMTP_TRACE_STDOUT ){ fossil_print("C: %.*s\n", n-2, z); } if( p->smtpFlags & SMTP_TRACE_FILE ){ fprintf(p->logFile, "C: %.*s\n", n-2, z); } if( p->smtpFlags & SMTP_TRACE_BLOB ){ blob_appendf(p->pTranscript, "C: %.*s\n", n-2, z); } socket_send(0, z, n); blob_reset(&b); } /* ** Read a line of input received from the SMTP server. Make in point ** to the next input line. ** ** Content is actually read into the p->in buffer. Then blob_line() ** is used to extract individual lines, passing each to "in". */ static void smtp_recv_line(SmtpSession *p, Blob *in){ int n = blob_size(&p->inbuf); char *z = blob_buffer(&p->inbuf); int i = blob_tell(&p->inbuf); int nDelay = 0; if( i<n && z[n-1]=='\n' ){ blob_line(&p->inbuf, in); }else if( p->atEof ){ blob_init(in, 0, 0); }else{ if( n>0 && i>=n ){ blob_truncate(&p->inbuf, 0); blob_rewind(&p->inbuf); n = 0; } do{ size_t got; blob_resize(&p->inbuf, n+1000); z = blob_buffer(&p->inbuf); got = socket_receive(0, z+n, 1000, 1); if( got>0 ){ in->nUsed += got; n += got; z[n] = 0; if( n>0 && z[n-1]=='\n' ) break; if( got==1000 ) continue; } nDelay++; if( nDelay>100 ){ blob_init(in, 0, 0); p->zErr = mprintf("timeout"); socket_close(); p->atEof = 1; return; }else{ sqlite3_sleep(100); } }while( n<1 || z[n-1]!='\n' ); blob_truncate(&p->inbuf, n); blob_line(&p->inbuf, in); } z = blob_buffer(in); n = blob_size(in); if( n && z[n-1]=='\n' ) n--; if( n && z[n-1]=='\r' ) n--; if( p->smtpFlags & SMTP_TRACE_STDOUT ){ fossil_print("S: %.*s\n", n, z); } if( p->smtpFlags & SMTP_TRACE_FILE ){ fprintf(p->logFile, "S: %.*s\n", n, z); } if( p->smtpFlags & SMTP_TRACE_BLOB ){ blob_appendf(p->pTranscript, "S: %.*s\n", n-2, z); } } /* ** Capture a single-line server reply. */ static void smtp_get_reply_from_server( SmtpSession *p, /* The SMTP connection */ Blob *in, /* Buffer used to hold the reply */ int *piCode, /* The return code */ int *pbMore, /* True if the reply is not complete */ char **pzArg /* Argument */ ){ int n; char *z; blob_truncate(in, 0); smtp_recv_line(p, in); z = blob_str(in); n = blob_size(in); if( z[0]=='#' ){ *piCode = 0; *pbMore = 1; *pzArg = z; }else{ *piCode = atoi(z); *pbMore = n>=4 && z[3]=='-'; *pzArg = n>=4 ? z+4 : ""; } } /* ** Have the client send a QUIT message. */ int smtp_client_quit(SmtpSession *p){ Blob in = BLOB_INITIALIZER; int iCode = 0; int bMore = 0; char *zArg = 0; smtp_send_line(p, "QUIT\r\n"); do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); p->atEof = 1; socket_close(); return 0; } /* ** Begin a client SMTP session. Wait for the initial 220 then send ** the EHLO and wait for a 250. ** ** Return 0 on success and non-zero for a failure. */ int smtp_client_startup(SmtpSession *p){ Blob in = BLOB_INITIALIZER; int iCode = 0; int bMore = 0; char *zArg = 0; do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=220 ){ smtp_client_quit(p); return 1; } smtp_send_line(p, "EHLO %s\r\n", p->zFrom); do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=250 ){ smtp_client_quit(p); return 1; } return 0; } /* ** COMMAND: test-smtp-probe ** ** Usage: %fossil test-smtp-probe DOMAIN [ME] ** ** Interact with the SMTP server for DOMAIN by setting up a connection ** and then immediately shutting it back down. Log all interaction ** on the console. Use ME as the domain name of the sender. ** ** Options: ** --direct Use DOMAIN directly without going through MX ** --port N Talk on TCP port N */ void test_smtp_probe(void){ SmtpSession *p; const char *zDomain; const char *zSelf; const char *zPort; int iPort = 25; u32 smtpFlags = SMTP_TRACE_STDOUT|SMTP_PORT; if( find_option("direct",0,0)!=0 ) smtpFlags |= SMTP_DIRECT; zPort = find_option("port",0,1); if( zPort ) iPort = atoi(zPort); verify_all_options(); if( g.argc!=3 && g.argc!=4 ) usage("DOMAIN [ME]"); zDomain = g.argv[2]; zSelf = g.argc==4 ? g.argv[3] : "fossil-scm.org"; p = smtp_session_new(zSelf, zDomain, smtpFlags, iPort); if( p->zErr ){ fossil_fatal("%s", p->zErr); } fossil_print("Connection to \"%s\"\n", p->zHostname); smtp_client_startup(p); smtp_client_quit(p); if( p->zErr ){ fossil_fatal("ERROR: %s\n", p->zErr); } smtp_session_free(p); } /* ** Send the content of an email message followed by a single ** "." line. All lines must be \r\n terminated. Any isolated ** \n line terminators in the input must be converted. Also, ** a line beginning with "." must have the dot doubled per ** https://tools.ietf.org/html/rfc5321#section-4.5.2 */ static void smtp_send_email_body( const char *zMsg, /* Message to send */ size_t (*xSend)(void*,const void*,size_t), /* Sender callback function */ void *pArg /* First arg to sender */ ){ Blob in; Blob out = BLOB_INITIALIZER; Blob line; blob_init(&in, zMsg, -1); while( blob_line(&in, &line) ){ char *z = blob_buffer(&line); int n = blob_size(&line); if( n==0 ) break; n--; if( n && z[n-1]=='\r' ) n--; if( z[0]=='.' ){ blob_append(&out, "..", 2); /* RFC 5321 § 4.5.2 */ blob_append(&out, z+1, n-1); }else{ blob_append(&out, z, n); } blob_append(&out, "\r\n", 2); } blob_append(&out, ".\r\n", 3); xSend(pArg, blob_buffer(&out), blob_size(&out)); blob_reset(&out); blob_reset(&line); } /* A sender function appropriate for use by smtp_send_email_body() to ** send all content to the console, for testing. */ static size_t smtp_test_sender(void *NotUsed, const void *pContent, size_t N){ return fwrite(pContent, 1, N, stdout); } /* ** COMMAND: test-smtp-senddata ** ** Usage: %fossil test-smtp-senddata FILE ** ** Read content from FILE, then send it to stdout encoded as if sent ** to the DATA portion of an SMTP session. This command is used to ** test the encoding logic. */ void test_smtp_senddata(void){ Blob f; if( g.argc!=3 ) usage("FILE"); blob_read_from_file(&f, g.argv[2], ExtFILE); smtp_send_email_body(blob_str(&f), smtp_test_sender, 0); blob_reset(&f); } /* ** Send a single email message to the SMTP server. ** ** All email addresses (zFrom and azTo) must be plain "local@domain" ** format without the surrounding "<..>". This routine will add the ** necessary "<..>". ** ** The body of the email should be well-structured. This routine will ** convert any \n line endings into \r\n and will escape lines containing ** just ".", but will not make any other alterations or corrections to ** the message content. ** ** Return 0 on success. Otherwise an error code. */ int smtp_send_msg( SmtpSession *p, /* The SMTP server to which the message is sent */ const char *zFrom, /* Who the message is from */ int nTo, /* Number of receipients */ const char **azTo, /* Email address of each recipient */ const char *zMsg /* Body of the message */ ){ int i; int iCode = 0; int bMore = 0; char *zArg = 0; Blob in; blob_init(&in, 0, 0); smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom); do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=250 ) return 1; for(i=0; i<nTo; i++){ smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]); do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=250 ) return 1; } smtp_send_line(p, "DATA\r\n"); do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=354 ) return 1; smtp_send_email_body(zMsg, socket_send, 0); if( p->smtpFlags & SMTP_TRACE_STDOUT ){ fossil_print("C: # message content\nC: .\n"); } if( p->smtpFlags & SMTP_TRACE_FILE ){ fprintf(p->logFile, "C: # message content\nC: .\n"); } if( p->smtpFlags & SMTP_TRACE_BLOB ){ blob_appendf(p->pTranscript, "C: # message content\nC: .\n"); } do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=250 ) return 1; return 0; } /* ** The input is a base email address of the form "local@domain". ** Return a pointer to just the "domain" part, or 0 if the string ** contains no "@". */ const char *domain_of_addr(const char *z){ while( z[0] && z[0]!='@' ) z++; if( z[0]==0 ) return 0; return z+1; } /* ** COMMAND: test-smtp-send ** ** Usage: %fossil test-smtp-send EMAIL FROM TO ... ** ** Use SMTP to send the email message contained in the file named EMAIL ** to the list of users TO. FROM is the sender of the email. ** ** Options: ** --direct Go directly to the TO domain. Bypass MX lookup ** --relayhost R Use R as relay host directly for delivery. ** --port N Use TCP port N instead of 25 ** --trace Show the SMTP conversation on the console */ void test_smtp_send(void){ SmtpSession *p; const char *zFrom; int nTo; const char *zToDomain; const char *zFromDomain; const char *zRelay; const char **azTo; int smtpPort = 25; const char *zPort; Blob body; u32 smtpFlags = SMTP_PORT; if( find_option("trace",0,0)!=0 ) smtpFlags |= SMTP_TRACE_STDOUT; if( find_option("direct",0,0)!=0 ) smtpFlags |= SMTP_DIRECT; zPort = find_option("port",0,1); if( zPort ) smtpPort = atoi(zPort); zRelay = find_option("relayhost",0,1); verify_all_options(); if( g.argc<5 ) usage("EMAIL FROM TO ..."); blob_read_from_file(&body, g.argv[2], ExtFILE); zFrom = g.argv[3]; nTo = g.argc-4; azTo = (const char**)g.argv+4; zFromDomain = domain_of_addr(zFrom); if( zRelay!=0 && zRelay[0]!= 0) { smtpFlags |= SMTP_DIRECT; zToDomain = zRelay; }else{ zToDomain = domain_of_addr(azTo[0]); } p = smtp_session_new(zFromDomain, zToDomain, smtpFlags, smtpPort); if( p->zErr ){ fossil_fatal("%s", p->zErr); } fossil_print("Connection to \"%s\"\n", p->zHostname); smtp_client_startup(p); smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body)); smtp_client_quit(p); if( p->zErr ){ fossil_fatal("ERROR: %s\n", p->zErr); } smtp_session_free(p); blob_reset(&body); } |
Added src/sorttable.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* Javascript code that will enables sorting of the table. This code is ** derived from ** ** http://www.webtoolkit.info/sortable-html-table.html ** ** but with extensive modifications. ** ** All tables with class "sortable" are registered with the SortableTable() ** function. Example: ** ** <table class='sortable' data-column-types='tnkx' data-init-sort='2'> ** ** Column data types are determined by the data-column-types attribute of ** the table. The value of data-column-types is a string where each ** character of the string represents a datatype for one column in the ** table. ** ** 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, ** then all extra columns assume type "t" (text). ** ** If a column of the table is initially sorted, then the data-init-sort ** attribute should be set to the 1-based index of that column. (The ** left-most column is 1, the next column to the right is 2, and so forth.) ** A value of 0 in the data-init-sort attribute indicates that no columns ** where initially sorted. ** ** Clicking on the same column header twice in a row inverts the sort. */ function SortableTable(tableEl){ var columnTypes = tableEl.getAttribute("data-column-types"); var initSort = tableEl.getAttribute("data-init-sort"); this.tbody = tableEl.getElementsByTagName('tbody'); this.columnTypes = columnTypes; if(tableEl.rows.length==0) return; var ncols = tableEl.rows[0].cells.length; for(var i = columnTypes.length; i<=ncols; i++){this.columnTypes += 't';} this.sort = function (cell) { var column = cell.cellIndex; var sortFn; switch( cell.sortType ){ case "n": sortFn = this.sortNumeric; break; case "N": sortFn = this.sortReverseNumeric; break; case "t": sortFn = this.sortText; break; case "T": sortFn = this.sortReverseText; break; case "k": sortFn = this.sortKey; break; case "K": sortFn = this.sortReverseKey; 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; } 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 -1; if(aa==bb) return a.rowIndex-b.rowIndex; return 1; } this.sortReverseText = 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 +1; if(aa==bb) return a.rowIndex-b.rowIndex; 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.sortReverseNumeric = 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 bb-aa; } 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 -1; if(aa==bb) return a.rowIndex-b.rowIndex; return 1; } this.sortReverseKey = 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 +1; if(aa==bb) return a.rowIndex-b.rowIndex; 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() } (function(){ var x = document.getElementsByClassName("sortable"); for(var i=0; i<x.length; i++){ new SortableTable(x[i]); } }()) |
Added src/sounds/0.wav.
cannot compute difference between binary files
Added src/sounds/1.wav.
cannot compute difference between binary files
Added src/sounds/2.wav.
cannot compute difference between binary files
Added src/sounds/3.wav.
cannot compute difference between binary files
Added src/sounds/4.wav.
cannot compute difference between binary files
Added src/sounds/5.wav.
cannot compute difference between binary files
Added src/sounds/6.wav.
cannot compute difference between binary files
Added src/sounds/7.wav.
cannot compute difference between binary files
Added src/sounds/8.wav.
cannot compute difference between binary files
Added src/sounds/9.wav.
cannot compute difference between binary files
Added src/sounds/README.md.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | The `[0-9a-f].wav` files in this directory contain a human voice speaking each of the 16 hexadecimal digits. If a captcha string consists of just hexadecimal digits (as is the case for captchas generated by the [captcha.c module](/finfo/src/captcha.c)) then these WAV files can be concatenated together to generate an audio reading of the captcha, which enables visually impaired users to complete the captcha. Each of the WAV files uses 8000 samples per second, 8 bits per sample and are 6000 samples in length. The recordings are made by Philip Bennefall and are of his own voice. Mr. Bennefall is himself blind and uses this system implemented with these recordings to complete captchas for Fossil. |
Added src/sounds/a.wav.
cannot compute difference between binary files
Added src/sounds/b.wav.
cannot compute difference between binary files
Added src/sounds/c.wav.
cannot compute difference between binary files
Added src/sounds/d.wav.
cannot compute difference between binary files
Added src/sounds/e.wav.
cannot compute difference between binary files
Added src/sounds/f.wav.
cannot compute difference between binary files
Added src/sqlcmd.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 | /* ** Copyright (c) 2010 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 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" #include <stdlib.h> /* atexit() */ #include <zlib.h> #ifndef _WIN32 # include "linenoise.h" #endif /* ** True if the "fossil sql" command has the --test flag. False otherwise. */ static int local_bSqlCmdTest = 0; /* ** 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, int argc, sqlite3_value **argv ){ int rid; Blob cx; const char *zName; assert( argc==1 ); 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 ** compressed using zLib and the output is returned. */ static void sqlcmd_compress( sqlite3_context *context, int argc, sqlite3_value **argv ){ const unsigned char *pIn; unsigned char *pOut; unsigned int nIn; unsigned long int nOut; int rc; pIn = sqlite3_value_blob(argv[0]); nIn = sqlite3_value_bytes(argv[0]); nOut = 13 + nIn + (nIn+999)/1000; pOut = sqlite3_malloc( nOut+4 ); pOut[0] = nIn>>24 & 0xff; pOut[1] = nIn>>16 & 0xff; pOut[2] = nIn>>8 & 0xff; pOut[3] = nIn & 0xff; rc = compress(&pOut[4], &nOut, pIn, nIn); if( rc==Z_OK ){ sqlite3_result_blob(context, pOut, nOut+4, sqlite3_free); }else if( rc==Z_MEM_ERROR ){ sqlite3_free(pOut); sqlite3_result_error_nomem(context); }else{ sqlite3_free(pOut); sqlite3_result_error(context, "input cannot be zlib compressed", -1); } } /* ** Implementation of the "decompress(X)" SQL function. The argument X ** is a blob which was obtained from compress(Y). The output will be ** the value Y. */ static void sqlcmd_decompress( sqlite3_context *context, int argc, sqlite3_value **argv ){ const unsigned char *pIn; unsigned char *pOut; unsigned int nIn; unsigned long int nOut; int rc; pIn = sqlite3_value_blob(argv[0]); if( pIn==0 ) return; nIn = sqlite3_value_bytes(argv[0]); if( nIn<4 ) return; nOut = (pIn[0]<<24) + (pIn[1]<<16) + (pIn[2]<<8) + pIn[3]; pOut = sqlite3_malloc( nOut+1 ); rc = uncompress(pOut, &nOut, &pIn[4], nIn-4); if( rc==Z_OK ){ sqlite3_result_blob(context, pOut, nOut, sqlite3_free); }else if( rc==Z_MEM_ERROR ){ sqlite3_free(pOut); sqlite3_result_error_nomem(context); }else{ sqlite3_free(pOut); sqlite3_result_error(context, "input is not zlib compressed", -1); } } /* ** Implementation of the "gather_artifact_stats(X)" SQL function. ** That function merely calls the gather_artifact_stats() function ** in stat.c to populate the ARTSTAT temporary table. */ static void sqlcmd_gather_artifact_stats( sqlite3_context *context, int argc, sqlite3_value **argv ){ gather_artifact_stats(1); } /* ** Add the content(), compress(), decompress(), and ** gather_artifact_stats() 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); sqlite3_create_function(db, "gather_artifact_stats", 0, SQLITE_UTF8, 0, sqlcmd_gather_artifact_stats, 0, 0); return SQLITE_OK; } /* ** Undocumented test SQL functions: ** ** db_protect(X) ** db_protect_pop(X) ** ** These invoke the corresponding C routines. ** ** WARNING: ** Do not instantiate these functions for any Fossil webpage or command ** method other than the "fossil sql" command. If an attacker gains access ** to these functions, he will be able to disable other defense mechanisms. ** ** This routines are for interactiving testing only. They are experimental ** and undocumented (apart from this comments) and might go away or change ** in future releases. ** ** 2020-11-29: These functions are now only available if the "fossil sql" ** command is started with the --test option. */ static void sqlcmd_db_protect( sqlite3_context *context, int argc, sqlite3_value **argv ){ unsigned mask = 0; const char *z = (const char*)sqlite3_value_text(argv[0]); if( z!=0 && local_bSqlCmdTest ){ if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; db_protect(mask); } } static void sqlcmd_db_protect_pop( sqlite3_context *context, int argc, sqlite3_value **argv ){ if( !local_bSqlCmdTest ) db_protect_pop(); } /* ** 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 ){ int mTrace = SQLITE_TRACE_CLOSE; add_content_sql_commands(db); db_add_aux_functions(db); re_add_sql_func(db); search_sql_setup(db); foci_register(db); deltafunc_init(db); helptext_vtab_register(db); builtin_vtab_register(db); g.repositoryOpen = 1; g.db = db; sqlite3_busy_timeout(db, 10000); sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository"); db_maybe_set_encryption_key(db, g.zRepositoryName); if( g.zLocalDbName ){ char *zSql = sqlite3_mprintf("ATTACH %Q AS 'localdb' KEY ''", g.zLocalDbName); sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); } if( g.zConfigDbName ){ char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''", g.zConfigDbName); sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); } /* Arrange to trace close operations so that static prepared statements ** will get cleaned up when the shell closes the database connection */ if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); db_protect_only(PROTECT_NONE); sqlite3_set_authorizer(db, db_top_authorizer, db); if( local_bSqlCmdTest ){ sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, sqlcmd_db_protect, 0, 0); sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, sqlcmd_db_protect_pop, 0, 0); sqlite3_create_function(db, "shared_secret", 2, SQLITE_UTF8, 0, sha1_shared_secret_sql_function, 0, 0); } return SQLITE_OK; } /* ** atexit() handler that cleans up global state modified by this module. */ static void sqlcmd_atexit(void) { g.zConfigDbName = 0; /* prevent panic */ } /* ** This routine is called by the sqlite3 command-line shell to ** to load the name the Fossil repository database. */ void sqlcmd_get_dbname(const char **pzRepoName){ *pzRepoName = g.zRepositoryName; } /* ** This routine is called by the sqlite3 command-line shell to do ** extra initialization prior to starting up the shell. */ void sqlcmd_init_proc(void){ sqlite3_initialize(); sqlite3_auto_extension((void(*)(void))sqlcmd_autoinit); } #if USE_SEE /* ** This routine is called by the patched sqlite3 command-line shell in order ** to load the encryption key for the open Fossil database. The memory that ** is pointed to by the value placed in pzKey must be obtained from malloc. */ void fossil_key(const char **pzKey, int *pnKey){ char *zSavedKey = db_get_saved_encryption_key(); char *zKey; size_t savedKeySize = db_get_saved_encryption_key_size(); if( !db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ) return; zKey = (char*)malloc( savedKeySize ); if( zKey ){ memcpy(zKey, zSavedKey, savedKeySize); *pzKey = zKey; if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){ *pnKey = (int)strlen(zKey); }else{ *pnKey = -1; } }else{ fossil_fatal("failed to allocate %u bytes for key", savedKeySize); } } #endif /* ** This routine closes the Fossil databases and/or invalidates the global ** state variables that keep track of them. */ static void fossil_close(int bDb, int noRepository){ if( bDb ) db_close(1); if( noRepository ) g.zRepositoryName = 0; g.db = 0; g.repositoryOpen = 0; g.localOpen = 0; } /* ** COMMAND: sql ** COMMAND: sqlite3* ** ** Usage: %fossil sql ?OPTIONS? ** ** Run the sqlite3 command-line shell on the Fossil repository ** identified by the -R option, or on the current repository. ** See https://www.sqlite.org/cli.html for additional information about ** the sqlite3 command-line shell. ** ** 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 modify the repository database. Use the ** --readonly option to prevent accidental damage to the repository. ** ** Options: ** --no-repository Skip opening the repository database ** --readonly Open the repository read-only. No changes ** are allowed. This is a recommended safety ** precaution to prevent repository damage. ** -R REPOSITORY Use REPOSITORY as the repository database ** --test Enable some testing and analysis features ** that are normally disabled. ** ** All of the standard sqlite3 command-line shell options should also ** work. ** ** The following SQL extensions are provided with this Fossil-enhanced ** version of the sqlite3 command-line shell: ** ** builtin A virtual table that contains one row for ** each datafile that is built into the Fossil ** binary. ** ** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) ** found in check-in X (another BLOB.RID value). ** ** compress(X) Compress text X with the same algorithm used ** to compress artifacts in the BLOB table. ** ** content(X) Return the content of artifact X. X can be an ** artifact hash or hash prefix or a tag. Artifacts ** are stored compressed and deltaed. This function ** does all necessary decompression and undeltaing. ** ** decompress(X) Decompress text X. Undoes the work of ** compress(X). ** ** delta_apply(X,D) Apply delta D to source blob X and return ** the result. ** ** delta_create(X,Y) Create and return a delta that will convert ** X into Y. ** ** delta_output_size(D) Return the number of bytes of output to expect ** when applying delta D ** ** delta_parse(D) A table-valued function that deconstructs ** delta D and returns rows for each element of ** that delta. ** ** files_of_checkin(X) A table-valued function that returns info on ** all files contained in check-in X. Example: ** ** SELECT * FROM files_of_checkin('trunk'); ** ** helptext A virtual table with one row for each command, ** webpage, and setting together with the built-in ** help text. ** ** now() Return the number of seconds since 1970. ** ** obscure(T) Obfuscate the text password T so that its ** original value is not readily visible. Fossil ** uses this same algorithm when storing passwords ** of remote URLs. ** ** regexp The REGEXP operator works, unlike in ** standard SQLite. ** ** symbolic_name_to_rid(X) Return the BLOB.RID corresponding to symbolic ** name X. */ void cmd_sqlite3(void){ int noRepository; char *zConfigDb; extern int sqlite3_shell(int, char**); #ifdef FOSSIL_ENABLE_TH1_HOOKS g.fNoThHook = 1; #endif noRepository = find_option("no-repository", 0, 0)!=0; local_bSqlCmdTest = find_option("test",0,0)!=0; if( !noRepository ){ db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); } db_open_config(1,0); zConfigDb = fossil_strdup(g.zConfigDbName); fossil_close(1, noRepository); sqlite3_shutdown(); #ifndef _WIN32 linenoiseSetMultiLine(1); #endif atexit(sqlcmd_atexit); g.zConfigDbName = zConfigDb; g.argv[1] = "-quote"; sqlite3_shell(g.argc, g.argv); sqlite3_cancel_auto_extension((void(*)(void))sqlcmd_autoinit); fossil_close(0, noRepository); } |
Deleted src/sqlite3.c.
more than 10,000 changes
Deleted src/sqlite3.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added src/stash.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 | /* ** Copyright (c) 2010 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 contains code used to implement the "stash" command. */ #include "config.h" #include "stash.h" #include <assert.h> /* ** SQL code to implement the tables needed by the stash. ** ** Historical schema changes: ** ** 2019-01-19: stash.hash and stashfile.hash columns added. The ** corresponding stash.vid and stashfile.rid columns are ** retained for compatibility with older versions of ** fossil but are no longer used. ** ** 2016-10-16: Change the PRIMARY KEY on stashfile from (origname,stashid) ** to (newname,stashid). ** ** 2011-09-01: stashfile.isLink column added ** */ static const char zStashInit[] = @ CREATE TABLE IF NOT EXISTS localdb.stash( @ stashid INTEGER PRIMARY KEY, -- Unique stash identifier @ vid INTEGER, -- Legacy baseline RID value. Do not use. @ hash TEXT, -- The SHA hash for the baseline @ comment TEXT, -- Comment for this stash. Or NULL @ ctime TIMESTAMP -- When the stash was created @ ); @ CREATE TABLE IF NOT EXISTS localdb.stashfile( @ stashid INTEGER REFERENCES stash, -- Stash that contains this file @ 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 @ rid INTEGER, -- Legacy baseline RID value. Do not use @ hash TEXT, -- Hash for baseline or NULL @ origname TEXT, -- Original filename @ newname TEXT, -- New name for file at next check-in @ delta BLOB, -- Delta from baseline or raw content @ PRIMARY KEY(newname, stashid) @ ); @ INSERT OR IGNORE INTO vvar(name, value) VALUES('stash-next', 1); ; /* ** Make sure the stash and stashfile tables exist and have been ** upgraded to their latest format. Create and upgrade the tables ** as necessary. */ static void stash_tables_exist_and_current(void){ if( db_table_has_column("localdb","stashfile","hash") ){ /* The schema is up-to-date. But it could be that an older version ** of Fossil that does no know about the stash.hash and stashfile.hash ** columns has run since the schema was updated, and added entries that ** have NULL hash columns. Check for this case, and fill in any missing ** hash values. */ if( db_int(0, "SELECT hash IS NULL FROM stash" " ORDER BY stashid DESC LIMIT 1") ){ db_multi_exec( "UPDATE stash" " SET hash=(SELECT uuid FROM blob WHERE blob.rid=stash.vid)" " WHERE hash IS NULL;" "UPDATE stashfile" " SET hash=(SELECT uuid FROM blob WHERE blob.rid=stashfile.rid)" " WHERE hash IS NULL AND rid>0;" ); } return; } if( !db_table_exists("localdb","stashfile") || !db_table_exists("localdb","stash") ){ /* Tables do not exist. Create them from scratch. */ db_multi_exec("DROP TABLE IF EXISTS localdb.stash;"); db_multi_exec("DROP TABLE IF EXISTS localdb.stashfile;"); db_multi_exec(zStashInit /*works-like:""*/); return; } /* The tables exists but are not necessarily current. Upgrade them ** to the latest format. ** ** We can assume the 2011-09-01 format that includes the stashfile.isLink ** column. The only upgrades we need to worry about the PRIMARY KEY ** change on 2016-10-16 and the addition of the "hash" columns on ** 2019-01-19. */ db_multi_exec( "ALTER TABLE localdb.stash RENAME TO old_stash;" "ALTER TABLE localdb.stashfile RENAME TO old_stashfile;" ); db_multi_exec(zStashInit /*works-like:""*/); db_multi_exec( "INSERT INTO localdb.stash(stashid,vid,hash,comment,ctime)" " SELECT stashid, vid," " (SELECT uuid FROM blob WHERE blob.rid=old_stash.vid)," " comment, ctime FROM old_stash;" "DROP TABLE old_stash;" ); db_multi_exec( "INSERT INTO localdb.stashfile(stashid,isAdded,isRemoved,isExec," "isLink,rid,hash,origname,newname,delta)" " SELECT stashid, isAdded, isRemoved, isExec, isLink, rid," " (SELECT uuid FROM blob WHERE blob.rid=old_stashfile.rid)," " origname, newname, delta FROM old_stashfile;" "DROP TABLE old_stashfile;" ); } /* ** Update the stash.vid and stashfile.rid values after a RID renumbering ** event. */ void stash_rid_renumbering_event(void){ if( !db_table_has_column("localdb","stash","hash") ){ /* If the stash schema was the older style that lacked hash value, then ** recovery is not possible. Save off the old data, then reset the stash ** to empty. */ if( db_table_exists("localdb","stash") ){ db_multi_exec("ALTER TABLE stash RENAME TO broken_stash;"); fossil_print("Unrecoverable stash content stored in \"broken_stash\"\n"); } if( db_table_exists("localdb","stashfile") ){ db_multi_exec("ALTER TABLE stashfile RENAME TO broken_stashfile;"); fossil_print("Unrecoverable stashfile content stored" " in \"broken_stashfile\"\n"); } }else{ /* Reset stash.vid and stash.rid values based on hashes */ db_multi_exec( "UPDATE stash" " SET vid=(SELECT rid FROM blob WHERE blob.uuid=stash.hash);" "UPDATE stashfile" " SET rid=(SELECT rid FROM blob WHERE blob.uuid=stashfile.hash)" " WHERE hash IS NOT NULL;" ); } } /* ** Add zFName to the stash given by stashid. zFName might be the name of a ** file or a directory. If a directory, add all changed files contained ** within that directory. */ static void stash_add_file_or_dir(int stashid, int vid, const char *zFName){ char *zFile; /* Normalized filename */ char *zTreename; /* Name of the file in the tree */ Blob fname; /* Filename relative to root */ Blob sql; /* Query statement text */ Stmt q; /* Query against the vfile table */ Stmt ins; /* Insert statement */ zFile = mprintf("%/", zFName); file_tree_name(zFile, &fname, 0, 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, isAdded, isRemoved, isExec, isLink, rid, " "hash, origname, newname, delta)" "VALUES(%d,:isadd,:isrm,:isexe,:islink,:rid," "(SELECT uuid FROM blob WHERE rid=:rid),:orig,:new,:content)", stashid ); while( db_step(&q)==SQLITE_ROW ){ int deleted = db_column_int(&q, 0); int rid = db_column_int(&q, 3); const char *zName = db_column_text(&q, 4); const char *zOrig = db_column_text(&q, 5); char *zPath = mprintf("%s%s", g.zLocalRoot, zName); Blob content; db_bind_int(&ins, ":rid", rid); db_bind_int(&ins, ":isadd", rid==0); db_bind_int(&ins, ":isrm", deleted); db_bind_int(&ins, ":isexe", db_column_int(&q, 1)); db_bind_int(&ins, ":islink", db_column_int(&q, 2)); db_bind_text(&ins, ":orig", zOrig); db_bind_text(&ins, ":new", zName); if( rid==0 ){ /* A new file */ blob_read_from_file(&content, zPath, RepoFILE); db_bind_blob(&ins, ":content", &content); }else if( deleted ){ blob_zero(&content); db_bind_null(&ins, ":content"); }else{ /* A modified file */ Blob orig; Blob disk; blob_read_from_file(&disk, zPath, RepoFILE); content_get(rid, &orig); blob_delta_create(&orig, &disk, &content); blob_reset(&orig); blob_reset(&disk); db_bind_blob(&ins, ":content", &content); } db_bind_int(&ins, ":islink", file_islink(zPath)); db_step(&ins); db_reset(&ins); fossil_free(zPath); blob_reset(&content); } db_finalize(&ins); db_finalize(&q); fossil_free(zFile); blob_reset(&fname); } /* ** Create a new stash based on the uncommitted changes currently in ** the working directory. ** ** If the "-m" or "--comment" command-line option is present, gather ** its argument as the stash comment. ** ** If files are named on the command-line, then only stash the named ** files. */ static int stash_create(void){ const char *zComment; /* Comment to add to the stash */ int stashid; /* ID of the new stash */ int vid; /* Current check-out */ zComment = find_option("comment", "m", 1); verify_all_options(); if( zComment==0 ){ Blob prompt; /* Prompt for stash comment */ Blob comment; /* User comment reply */ #if defined(_WIN32) || defined(__CYGWIN__) int bomSize; const unsigned char *bom = get_utf8_bom(&bomSize); 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); db_lset_int("stash-next", stashid+1); vid = db_lget_int("checkout", 0); vfile_check_signature(vid, 0); db_multi_exec( "INSERT INTO stash(stashid,vid,hash,comment,ctime)" "VALUES(%d,%d,(SELECT uuid FROM blob WHERE rid=%d),%Q,julianday('now'))", stashid, vid, vid, zComment ); if( g.argc>3 ){ int i; for(i=3; i<g.argc; i++){ stash_add_file_or_dir(stashid, vid, g.argv[i]); } }else{ stash_add_file_or_dir(stashid, vid, g.zLocalRoot); } 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 blob.rid, isRemoved, isExec, isLink, origname, newname, delta" " FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash" " UNION ALL SELECT 0, isRemoved, isExec, isLink, origname, newname, delta" " FROM stashfile WHERE stashid=%d AND stashfile.hash IS NULL", stashid, stashid ); vid = db_lget_int("checkout",0); db_multi_exec("CREATE TEMP TABLE sfile(pathname 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(pathname) VALUES(%Q)", zNew); db_ephemeral_blob(&q, 6, &delta); blob_write_to_file(&delta, zNPath); file_setexe(zNPath, isExec); }else if( isRemoved ){ fossil_print("DELETE %s\n", zOrig); file_delete(zOPath); }else if( file_unsafe_in_tree_path(zNPath) ){ /* Ignore the unsafe path */ }else{ Blob a, b, out, disk; int isNewLink = file_islink(zOPath); db_ephemeral_blob(&q, 6, &delta); blob_read_from_file(&disk, zOPath, RepoFILE); content_get(rid, &a); blob_delta_apply(&a, &delta, &b); if( isLink == isNewLink && blob_compare(&disk, &a)==0 ){ if( isLink || isNewLink ){ file_delete(zNPath); } if( isLink ){ symlink_create(blob_str(&b), zNPath); }else{ blob_write_to_file(&b, zNPath); } file_setexe(zNPath, isExec); fossil_print("UPDATE %s\n", zNew); }else{ int rc; if( isLink || isNewLink ){ rc = -1; blob_zero(&b); /* because we reset it later */ fossil_print("***** Cannot merge symlink %s\n", zNew); }else{ rc = merge_3way(&a, zOPath, &b, &out, MERGE_KEEP_FILES); blob_write_to_file(&out, zNPath); blob_reset(&out); file_setexe(zNPath, isExec); } if( rc ){ fossil_print("CONFLICT %s\n", zNew); nConflict++; }else{ fossil_print("MERGE %s\n", zNew); } } blob_reset(&a); blob_reset(&b); blob_reset(&disk); } blob_reset(&delta); if( fossil_strcmp(zOrig,zNew)!=0 ){ undo_save(zOrig); file_delete(zOPath); db_multi_exec( "UPDATE vfile SET pathname='%q', origname='%q'" " WHERE pathname='%q' %s AND vid=%d", zNew, zOrig, zOrig, filename_collation(), vid ); } } stash_add_files_in_sfile(vid); db_finalize(&q); if( nConflict ){ fossil_print( "WARNING: %d merge conflicts - see messages above for details.\n", nConflict); } } /* ** Show the diffs associate with a single stash. */ static void stash_diff( int stashid, /* The stash entry to diff */ int fBaseline, /* Diff against original baseline check-in if true */ DiffConfig *pCfg /* Diff formatting options */ ){ Stmt q; Blob empty; int bWebpage = (pCfg->diffFlags & (DIFF_WEBPAGE|DIFF_JSON|DIFF_TCL))!=0; blob_zero(&empty); diff_begin(pCfg); db_prepare(&q, "SELECT blob.rid, isRemoved, isExec, isLink, origname, newname, delta" " FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash" " UNION ALL SELECT 0, isRemoved, isExec, isLink, origname, newname, delta" " FROM stashfile WHERE stashid=%d AND stashfile.hash IS NULL", stashid, stashid ); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); int isRemoved = db_column_int(&q, 1); 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); Blob a, b; pCfg->diffFlags &= (~DIFF_FILE_MASK); if( rid==0 ){ db_ephemeral_blob(&q, 6, &a); if( !bWebpage ) fossil_print("ADDED %s\n", zNew); pCfg->diffFlags |= DIFF_FILE_ADDED; diff_print_index(zNew, pCfg, 0); diff_file_mem(&empty, &a, zNew, pCfg); }else if( isRemoved ){ if( !bWebpage) fossil_print("DELETE %s\n", zOrig); pCfg->diffFlags |= DIFF_FILE_DELETED; diff_print_index(zNew, pCfg, 0); if( fBaseline ){ content_get(rid, &a); diff_file_mem(&a, &empty, zOrig, pCfg); } }else{ Blob delta; int isOrigLink = file_islink(zOPath); db_ephemeral_blob(&q, 6, &delta); if( !bWebpage ) fossil_print("CHANGED %s\n", zNew); if( !isOrigLink != !isLink ){ diff_print_index(zNew, pCfg, 0); diff_print_filenames(zOrig, zNew, pCfg, 0); printf(DIFF_CANNOT_COMPUTE_SYMLINK); }else{ content_get(rid, &a); blob_delta_apply(&a, &delta, &b); if( fBaseline ){ diff_file_mem(&a, &b, zNew, pCfg); }else{ pCfg->diffFlags ^= DIFF_INVERT; diff_file(&b, zOPath, zNew, pCfg, 0); pCfg->diffFlags ^= DIFF_INVERT; } blob_reset(&a); blob_reset(&b); } blob_reset(&delta); } } db_finalize(&q); diff_end(pCfg, 0); } /* ** Drop the indicated stash */ static void stash_drop(int stashid){ db_multi_exec( "DELETE FROM stash WHERE stashid=%d;" "DELETE FROM stashfile WHERE stashid=%d;", stashid, stashid ); } /* ** If zStashId is non-NULL then interpret is as a stash number and ** return that number. Or throw a fatal error if it is not a valid ** stash number. If it is NULL, return the most recent stash or ** throw an error if the stash is empty. */ static int stash_get_id(const char *zStashId){ int stashid; if( zStashId==0 ){ stashid = db_int(0, "SELECT max(stashid) FROM stash"); if( stashid==0 ) fossil_fatal("empty stash"); }else{ stashid = atoi(zStashId); if( !db_exists("SELECT 1 FROM stash WHERE stashid=%d", stashid) ){ fossil_fatal("no such stash: %s", zStashId); } } return stashid; } /* ** COMMAND: stash ** ** Usage: %fossil stash SUBCOMMAND ARGS... ** ** > fossil stash ** > fossil stash save ?-m|--comment COMMENT? ?FILES...? ** > fossil stash snapshot ?-m|--comment COMMENT? ?FILES...? ** ** Save the current changes in the working tree as a new stash. ** Then revert the changes back to the last check-in. If FILES ** are listed, then only stash and revert the named files. The ** "save" verb can be omitted if and only if there are no other ** arguments. The "snapshot" verb works the same as "save" but ** omits the revert, keeping the check-out unchanged. ** ** > fossil stash list|ls ?-v|--verbose? ?-W|--width NUM? ** ** List all changes sets currently stashed. Show information about ** individual files in each changeset if -v or --verbose is used. ** ** > fossil stash show|cat ?STASHID? ?DIFF-OPTIONS? ** > fossil stash gshow|gcat ?STASHID? ?DIFF-OPTIONS? ** ** Show the contents of a stash as a diff against its baseline. ** With gshow and gcat, gdiff-command is used instead of internal ** diff logic. ** ** > fossil stash pop ** > fossil stash apply ?STASHID? ** ** Apply STASHID or the most recently created stash to the current ** working check-out. The "pop" command deletes that changeset from ** the stash after applying it but the "apply" command retains the ** changeset. ** ** > fossil stash goto ?STASHID? ** ** Update to the baseline check-out for STASHID then apply the ** changes of STASHID. Keep STASHID so that it can be reused ** This command is undoable. ** ** > fossil stash drop|rm ?STASHID? ?-a|--all? ** ** Forget everything about STASHID. Forget the whole stash if the ** -a|--all flag is used. Individual drops are undoable but -a|--all ** is not. ** ** > fossil stash diff ?STASHID? ?DIFF-OPTIONS? ** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS? ** ** Show diffs of the current working directory and what that ** directory would be if STASHID were applied. With gdiff, ** gdiff-command is used instead of internal diff logic. */ void stash_cmd(void){ const char *zCmd; int nCmd; int stashid = 0; undo_capture_command_line(); db_must_be_within_tree(); db_open_config(0, 0); db_begin_transaction(); stash_tables_exist_and_current(); if( g.argc<=2 ){ zCmd = "save"; }else{ zCmd = g.argv[2]; } nCmd = strlen(zCmd); if( strncmp(zCmd, "save", nCmd)==0 ){ if( unsaved_changes(0)==0 ){ fossil_fatal("nothing to stash"); } stashid = stash_create(); undo_disable(); if( g.argc>=2 ){ int nFile = db_int(0, "SELECT count(*) FROM stashfile WHERE stashid=%d", stashid); char **newArgv = fossil_malloc( sizeof(char*)*(nFile+2) ); int i = 2; Stmt q; db_prepare(&q,"SELECT origname FROM stashfile WHERE stashid=%d", stashid); while( db_step(&q)==SQLITE_ROW ){ newArgv[i++] = mprintf("%s%s", g.zLocalRoot, db_column_text(&q, 0)); } db_finalize(&q); newArgv[0] = g.argv[0]; newArgv[1] = 0; g.argv = newArgv; g.argc = nFile+2; if( nFile==0 ) return; } /* Make sure the stash has committed before running the revert, so that ** we have a copy of the changes before deleting them. */ db_commit_transaction(); g.argv[1] = "revert"; revert_cmd(); fossil_print("stash %d saved\n", stashid); return; }else if( strncmp(zCmd, "snapshot", nCmd)==0 ){ stash_create(); }else if( strncmp(zCmd, "list", nCmd)==0 || strncmp(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, hash, 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, get_comment_format()); } 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); const char *zNew = db_column_text(&q2, 3); if( isAdded ){ fossil_print(" ADD %s\n", zNew); }else if( isRemoved ){ fossil_print(" REMOVE %s\n", zOrig); }else if( fossil_strcmp(zOrig,zNew)!=0 ){ fossil_print(" RENAME %s -> %s\n", zOrig, zNew); }else{ fossil_print(" EDIT %s\n", zOrig); } } db_reset(&q2); } } db_finalize(&q); if( verboseFlag ) db_finalize(&q2); if( n==0 ) fossil_print("empty stash\n"); }else if( strncmp(zCmd, "drop", nCmd)==0 || strncmp(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; undo_begin(); for(i=3; i<g.argc; i++){ stashid = stash_get_id(g.argv[i]); undo_save_stash(stashid); stash_drop(stashid); } undo_finish(); }else{ undo_begin(); undo_save_stash(0); stash_drop(stashid); undo_finish(); } }else if( strncmp(zCmd, "pop", nCmd)==0 || strncmp(zCmd, "apply", nCmd)==0 ){ char *zCom = 0, *zDate = 0, *zHash = 0; int popped = *zCmd=='p'; if( popped ){ if( g.argc>3 ) usage("pop"); stashid = stash_get_id(0); }else{ if( g.argc>4 ) usage("apply STASHID"); stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); } zCom = db_text(0, "SELECT comment FROM stash WHERE stashid=%d", stashid); zDate = db_text(0, "SELECT datetime(ctime) FROM stash WHERE stashid=%d", stashid); zHash = db_text(0, "SELECT hash FROM stash WHERE stashid=%d", stashid); undo_begin(); stash_apply(stashid, 0); if( popped ) undo_save_stash(stashid); fossil_print("%s stash:\n%5d: [%.14s] from %s\n", popped ? "Popped" : "Applied", stashid, zHash, zDate); if( zCom && *zCom ){ fossil_print(" "); comment_print(zCom, 0, 7, -1, get_comment_format()); } fossil_free(zCom); fossil_free(zDate); fossil_free(zHash); undo_finish(); if( popped ) stash_drop(stashid); }else if( strncmp(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 blob.rid FROM stash,blob" " WHERE stashid=%d AND blob.uuid=stash.hash", stashid); nConflict = update_to(vid); stash_apply(stashid, nConflict); db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN " "(SELECT origname FROM stashfile WHERE stashid=%d)", stashid); undo_finish(); }else if( strncmp(zCmd, "diff", nCmd)==0 || strncmp(zCmd, "gdiff", nCmd)==0 || strncmp(zCmd, "show", nCmd)==0 || strncmp(zCmd, "gshow", nCmd)==0 || strncmp(zCmd, "cat", nCmd)==0 || strncmp(zCmd, "gcat", nCmd)==0 ){ int fBaseline = 0; DiffConfig DCfg; if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){ fBaseline = 1; } if( find_option("tk",0,0)!=0 ){ db_close(0); diff_tk(fBaseline ? "stash show" : "stash diff", 3); return; } diff_options(&DCfg, zCmd[0]=='g', 0); stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); stash_diff(stashid, fBaseline, &DCfg); }else if( strncmp(zCmd, "help", nCmd)==0 ){ g.argv[1] = "help"; g.argv[2] = "stash"; g.argc = 3; help_cmd(); }else { usage("SUBCOMMAND ARGS..."); } db_end_transaction(0); } |
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 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 | ** 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)", v, (double)v/1000000000.0); } } /* ** Return the approximate size as KB, MB, GB, or TB. */ void approxSizeName(int nOut, char *zOut, sqlite3_int64 v){ if( v<1000 ){ sqlite3_snprintf(nOut, zOut, "%,lld bytes", v); }else if( v<1000000 ){ sqlite3_snprintf(nOut, zOut, "%.1fKB", (double)v/1000.0); }else if( v<1000000000 ){ sqlite3_snprintf(nOut, zOut, "%.1fMB", (double)v/1000000.0); }else{ sqlite3_snprintf(nOut, zOut, "%.1fGB", (double)v/1000000000.0); } } /* ** Generate stats for the email notification subsystem. */ void stats_for_email(void){ const char *zDest = db_get("email-send-method",0); int nSub, nASub, nPend, nDPend; const char *zDir, *zDb, *zCmd, *zRelay; int iCutoff; double rDigest; @ <tr><th>Outgoing Email:</th><td> if( fossil_strcmp(zDest,"pipe")==0 && (zCmd = db_get("email-send-command",0))!=0 ){ @ Piped to command "%h(zCmd)" }else if( fossil_strcmp(zDest,"db")==0 && (zDb = db_get("email-send-db",0))!=0 ){ sqlite3 *db; sqlite3_stmt *pStmt; int rc; @ Queued to database "%h(zDb)" g.dbIgnoreErrors++; rc = sqlite3_open(zDb, &db); if( rc==SQLITE_OK ){ rc = sqlite3_prepare_v2(db, "SELECT count(*) FROM email",-1,&pStmt,0); if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ @ (%,d(sqlite3_column_int(pStmt,0)) messages, @ %,d(file_size(zDb,ExtFILE)) bytes) } sqlite3_finalize(pStmt); } g.dbIgnoreErrors--; if( rc ){ @ ← cannot access database! } sqlite3_close(db); }else if( fossil_strcmp(zDest,"dir")==0 && (zDir = db_get("email-send-dir",0))!=0 ){ @ Written to files in "%h(zDir)" @ (%,d(file_directory_size(zDir,0,1)) messages) }else if( fossil_strcmp(zDest,"relay")==0 && (zRelay = db_get("email-send-relayhost",0))!=0 ){ @ Relay to %h(zRelay) using SMTP } else{ @ Off } @ </td></tr> nPend = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep"); nDPend = db_int(0,"SELECT count(*) FROM pending_alert" " WHERE NOT sentDigest"); @ <tr><th>Pending Alerts:</th><td> @ %,d(nPend) normal, %,d(nDPend) digest @ </td></tr> if( g.perm.Admin ){ @ <tr><th><a href="%R/subscribers">Subscribers:</a></th><td> }else{ @ <tr><th>Subscribers:</th><td> } nSub = db_int(0, "SELECT count(*) FROM subscriber"); iCutoff = db_get_int("email-renew-cutoff",0); nASub = db_int(0, "SELECT count(*) FROM subscriber WHERE sverified" " AND NOT sdonotcall AND octet_length(ssub)>1" " AND lastContact>=%d;", iCutoff); @ %,d(nASub) active, %,d(nSub) total @ </td></tr> rDigest = db_double(-1.0, "SELECT (julianday('now') - value)*24.0" " FROM config WHERE name='email-last-digest'"); if( rDigest>0.0 ){ @ <tr><th>Last Digest:</th><td>Approximately \ if( rDigest>48.0 ){ @ %.1f(rDigest/24.0) days ago</td> }else{ @ %.1f(rDigest) hours ago</td> } } } /* ** WEBPAGE: stat ** ** Show statistics and global information about the repository. */ void stat_page(void){ i64 t, fsize; int n, m; int szMax, szAvg; int brief; const char *p; char *z; int Y, M, D; 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", "urllist"); style_submenu_element("Schema", "repo_schema"); style_submenu_element("Web-Cache", "cachestat"); } style_submenu_element("Activity Reports", "reports"); style_submenu_element("Hash Collisions", "hash-collisions"); style_submenu_element("Artifacts", "bloblist"); if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){ style_submenu_element("Table Sizes", "repo-tabsize"); } if( g.perm.Admin || g.perm.Setup || db_get_boolean("test_env_enable",0) ){ style_submenu_element("Environment", "test_env"); } @ <table class="label-value"> fsize = file_size(g.zRepositoryName, ExtFILE); @ <tr><th>Repository Size:</th><td>%,lld(fsize) bytes</td> @ </td></tr> if( !brief ){ @ <tr><th>Number Of Artifacts:</th><td> n = db_int(0, "SELECT count(*) FROM blob WHERE content IS NOT NULL"); m = db_int(0, "SELECT count(*) FROM delta"); @ %,d(n) (%,d(n-m) fulltext and %,d(m) deltas) if( g.perm.Write ){ @ <a href='%R/artifact_stats'>Details</a> } @ </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 content IS NOT NULL /*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); @ %,d(szAvg) bytes average, %,d(szMax) bytes max, %,lld(t) total @ </td></tr> @ <tr><th>Compression Ratio:</th><td> if( t/fsize < 5 ){ b = 10; a = t/(fsize/10); }else{ b = 1; a = t/fsize; } @ %d(a):%d(b) @ </td></tr> } if( db_table_exists("repository","unversioned") ){ Stmt q; char zStored[100]; db_prepare(&q, "SELECT count(*), sum(sz), sum(octet_length(content))" " FROM unversioned" " WHERE length(hash)>1" ); if( db_step(&q)==SQLITE_ROW && (n = db_column_int(&q,0))>0 ){ sqlite3_int64 iStored, pct; iStored = db_column_int64(&q,2); pct = (iStored*100 + fsize/2)/fsize; approxSizeName(sizeof(zStored), zStored, iStored); @ <tr><th>Unversioned Files:</th><td> @ %z(href("%R/uvlist"))%d(n) files</a>, @ %s(zStored) compressed, %d(pct)%% of total repository space @ </td></tr> } db_finalize(&q); } @ <tr><th>Number Of Check-ins:</th><td> n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"); @ %,d(n) @ </td></tr> @ <tr><th>Number Of Files:</th><td> n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); @ %,d(n) @ </td></tr> @ <tr><th>Number Of Wiki Pages:</th><td> n = db_int(0, "SELECT count(*) FROM tag /*scan*/" " WHERE +tagname GLOB 'wiki-*'"); @ %,d(n) @ </td></tr> if( g.perm.Chat && db_table_exists("repository","chat") ){ sqlite3_int64 sz = 0; char zSz[100]; n = db_int(0, "SELECT max(msgid) FROM chat"); m = db_int(0, "SELECT count(*) FROM chat WHERE mdel IS NOT TRUE"); sz = db_int64(0, "SELECT sum(coalesce(octet_length(xmsg),0)+" "coalesce(octet_length(file),0)) FROM chat"); approxSizeName(sizeof(zSz), zSz, sz); @ <tr><th>Number Of Chat Messages:</th> @ <td>%,d(n) (%,d(m) still alive, %s(zSz) in size)</td></tr> } n = db_int(0, "SELECT count(*) FROM tag /*scan*/" " WHERE +tagname GLOB 'tkt-*'"); if( n>0 ){ @ <tr><th>Number Of Tickets:</th><td>%,d(n)</td></tr> } if( db_table_exists("repository","forumpost") ){ n = db_int(0, "SELECT count(*) FROM forumpost/*scan*/"); if( n>0 ){ int nThread = db_int(0, "SELECT count(*) FROM forumpost" " WHERE froot=fpid"); @ <tr><th>Number Of Forum Posts:</th> @ <td>%,d(n) on %d(nThread) threads</td></tr> } } } @ <tr><th>Project Age:</th><td> z = db_text(0, "SELECT timediff('now',(SELECT min(mtime) FROM event));"); sscanf(z, "+%d-%d-%d", &Y, &M, &D); if( Y>0 ){ @ %d(Y) years, \ } if( M>0 ){ @ %d(M) months, \ } @ %d(D) days @ </td></tr> p = db_get("project-code", 0); if( p ){ @ <tr><th>Project ID:</th> @ <td>%h(p) %h(db_get("project-name",""))</td></tr> } p = db_get("parent-project-code", 0); if( p ){ @ <tr><th>Parent Project ID:</th> @ <td>%h(p) %h(db_get("parent-project-name",""))</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)) <a href='version?verbose'>(details)</a> @ </td></tr> @ <tr><th>SQLite Version:</th><td>%.19s(sqlite3_sourceid()) @ [%.10s(&sqlite3_sourceid()[20])] (%s(sqlite3_libversion())) @ <a href='version?verbose'>(details)</a></td></tr> if( g.perm.Admin ){ const char *zCgi = P("SERVER_SOFTWARE"); @ <tr><th>OpenSSL Version:</th> @ <td>%z(fossil_openssl_version())</td></tr> if( zCgi ){ @ <tr><th>Web Server:</th><td>%s(zCgi)</td></tr> } } if( g.eHashPolicy!=HPOLICY_AUTO ){ @ <tr><th>Schema Version:</th><td>%h(g.zAuxSchema), @ %s(hpolicy_name())</td></tr> }else{ @ <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> @ %,d(db_int(0, "PRAGMA repository.page_count")) pages, @ %d(db_int(0, "PRAGMA repository.page_size")) bytes/page, @ %,d(db_int(0, "PRAGMA repository.freelist_count")) free pages, @ %s(db_text(0, "PRAGMA repository.encoding")), @ %s(db_text(0, "PRAGMA repository.journal_mode")) mode @ </td></tr> if( g.perm.Admin && g.zErrlog && g.zErrlog[0] ){ i64 szFile = file_size(g.zErrlog, ExtFILE); if( szFile>=0 ){ @ <tr><th>Error Log:</th> @ <td><a href='%R/errorlog'>%h(g.zErrlog)</a> (%,lld(szFile) bytes) } @ </td></tr> } if( g.perm.Admin ){ @ <tr><th>Backoffice:</th> @ <td>Last run: %z(backoffice_last_run())</td></tr> } if( g.perm.Admin && alert_enabled() ){ stats_for_email(); } @ </table> style_finish_page(); } /* ** COMMAND: dbstat ** ** Usage: %fossil dbstat OPTIONS ** ** Shows statistics and global information about the repository and/or ** verify the integrity of a repository. ** ** Options: ** -b|--brief Only show essential elements ** --db-check Run "PRAGMA quick_check" on the repository database ** --db-verify Run a full verification of the repository integrity. ** This involves decoding and reparsing all artifacts ** and can take significant time. ** --omit-version-info Omit the SQLite and Fossil version information */ void dbstat_cmd(void){ i64 t, fsize; int n, m; int szMax, szAvg; int brief; int omitVers; /* Omit Fossil and SQLite version information */ int dbCheck; /* True for the --db-check option */ 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; if( find_option("db-verify",0,0)!=0 ) dbCheck = 2; 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, ExtFILE); fossil_print( "%*s%,lld bytes\n", colWidth, "repository-size:", fsize); if( !brief ){ n = db_int(0, "SELECT count(*) FROM blob WHERE content IS NOT NULL"); m = db_int(0, "SELECT count(*) FROM delta"); fossil_print("%*s%,d (stored as %,d full text and %,d deltas)\n", colWidth, "artifact-count:", n, n-m, m); if( n>0 ){ int a, b; Stmt q; db_prepare(&q, "SELECT total(size), avg(size), max(size)" " FROM blob WHERE size>0"); db_step(&q); t = db_column_int64(&q, 0); szAvg = db_column_int(&q, 1); szMax = db_column_int(&q, 2); db_finalize(&q); fossil_print( "%*s%,d average, " "%,d max, %,lld total\n", colWidth, "artifact-sizes:", szAvg, szMax, t); if( t/fsize < 5 ){ b = 10; fsize /= 10; }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 (" "SELECT DISTINCT substr(tagname,6) " "FROM tag JOIN tagxref USING('tagid')" " WHERE tagname GLOB 'wiki-*'" " AND TYPEOF(tagxref.value+0)='integer'" ")"); 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); if( db_table_exists("repository","forumpost") ){ n = db_int(0, "SELECT count(*) FROM forumpost/*scan*/"); if( n>0 ){ int nThread = db_int(0, "SELECT count(*) FROM forumpost" " WHERE froot=fpid"); fossil_print("%*s%,d (on %,d threads)\n", colWidth, "forum-posts:", n, nThread); } } 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); if( !brief ){ 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()); } fossil_print("%*s%,d pages, %d bytes/pg, %,d free pages, " "%s, %s mode\n", colWidth, "database-stats:", db_int(0, "PRAGMA repository.page_count"), db_int(0, "PRAGMA repository.page_size"), db_int(0, "PRAGMA repository.freelist_count"), db_text(0, "PRAGMA repository.encoding"), db_text(0, "PRAGMA repository.journal_mode")); if( dbCheck ){ if( dbCheck<2 ){ char *zRes = db_text(0, "PRAGMA repository.quick_check(1)"); fossil_print("%*s%s\n", colWidth, "database-check:", zRes); }else{ char *newArgv[3]; newArgv[0] = g.argv[0]; newArgv[1] = "test-integrity"; newArgv[2] = 0; g.argv = newArgv; g.argc = 2; fossil_print("Full repository verification follows:\n"); test_integrity(); } } } /* ** Return a string which is the public URL used to access this repository. ** Or return a NULL pointer if this repository does not have a public ** access URL. ** ** Algorithm: ** ** The public URL is given by the email-url property. But it is only ** returned if there have been one more more accesses (as recorded by ** "baseurl:URL" entries in the CONFIG table). */ const char *public_url(void){ const char *zUrl = db_get("email-url", 0); if( zUrl==0 ) return 0; if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", zUrl) ){ return 0; } return zUrl; } /* ** WEBPAGE: urllist ** ** Show ways in which this repository has been accessed */ void urllist_page(void){ Stmt q; int cnt; int total = 0; int showAll = P("all")!=0; int nOmitted; sqlite3_int64 iNow; char *zPriorRepo = 0; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_set_current_feature("stat"); style_header("URLs and Checkouts"); style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("Stat", "stat"); style_submenu_element("Schema", "repo_schema"); iNow = db_int64(0, "SELECT strftime('%%s','now')"); db_prepare(&q, "SELECT substr(name,9), datetime(mtime,'unixepoch'), mtime" " FROM config WHERE name GLOB 'baseurl:*' ORDER BY 3 DESC"); cnt = 0; nOmitted = 0; while( db_step(&q)==SQLITE_ROW ){ if( cnt==0 ){ @ <div class="section">URLs used to access this repository</div> @ <table border="0" width='100%%'> } if( !showAll && db_column_int64(&q,2)<(iNow - 3600*24*30) && cnt>8 ){ nOmitted++; }else{ @ <tr><td width='100%%'>%h(db_column_text(&q,0))</td> @ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr> } cnt++; } db_finalize(&q); if( nOmitted ){ @ <tr><td><a href="urllist?all"><i>Show %d(nOmitted) more...</i></a> } if( cnt ){ @ </table> total += cnt; } if( P("urlonly") ){ style_finish_page(); return; } db_prepare(&q, "SELECT substr(name,7), datetime(mtime,'unixepoch')" " FROM config WHERE name GLOB 'ckout:*' ORDER BY 2 DESC"); cnt = 0; while( db_step(&q)==SQLITE_ROW ){ const char *zPath = db_column_text(&q,0); if( vfile_top_of_checkout(zPath) ){ if( cnt==0 ){ @ <div class="section">Checkouts</div> @ <table border="0" width='100%%'> } @ <tr><td width='100%%'>%h(zPath)</td> @ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr> cnt++; } } db_finalize(&q); if( cnt ){ @ </table> total += cnt; } cnt = 0; db_prepare(&q, "SELECT substr(name,10), datetime(mtime,'unixepoch')" " FROM config WHERE name GLOB 'syncwith:*'" "UNION ALL " "SELECT substr(name,10), datetime(mtime,'unixepoch')" " FROM config WHERE name GLOB 'syncfrom:*'" "UNION ALL " "SELECT substr(name,9), datetime(mtime,'unixepoch')" " FROM config WHERE name GLOB 'gitpush:*'" "GROUP BY 1 ORDER BY 2 DESC" ); while( db_step(&q)==SQLITE_ROW ){ const char *zURL = db_column_text(&q,0); UrlData x; if( cnt==0 ){ @ <div class="section">Recently synced with these URLs</div> @ <table border='0' width='100%%'> } memset(&x, 0, sizeof(x)); url_parse_local(zURL, URL_OMIT_USER, &x); @ <tr><td width='100%%'><a href='%h(x.canonical)'>%h(x.canonical)</a> @ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr> cnt++; url_unparse(&x); } db_finalize(&q); if( cnt ){ @ </table> total += cnt; } cnt = 0; db_prepare(&q, "SELECT" " substr(name,6)," " datetime(mtime,'unixepoch')," " value->>'type'," " value->>'src'\n" "FROM config\n" "WHERE name GLOB 'link:*'\n" "AND json_valid(value)\n" "ORDER BY 4, 2 DESC" ); while( db_step(&q)==SQLITE_ROW ){ const char *zUrl = db_column_text(&q, 0); const char *zType = db_column_text(&q, 2); const char *zSrc = db_column_text(&q, 3); if( zUrl==0 || zSrc==0 ) continue; if( cnt++==0 ){ @ <div class="section">Links from other repositories</div> @ <table border='0' width='100%%'> } if( zPriorRepo==0 || strcmp(zPriorRepo,zSrc)!=0 ){ fossil_free(zPriorRepo); zPriorRepo = fossil_strdup(zSrc); @ <tr><td colspan="4">\ @ From <a href='%T(zSrc)'>%h(zSrc)</a>...</td></tr> } @ <tr><td> </td> @ <td width='90%%'><a href='%h(zUrl)'>%h(zUrl)</a></td> if( zType ){ @ <td> (%h(zType)) </td> }else{ @ <td> </td> } @ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr> } db_finalize(&q); fossil_free(zPriorRepo); if( cnt ){ @ </table> total += cnt; } cnt = 0; db_prepare(&q, "SELECT" " value," " url_nouser(value)," " substr(name,10)," " datetime(mtime,'unixepoch')" "FROM config\n" "WHERE name GLOB 'sync-url:*'\n" "ORDER BY 2" ); while( db_step(&q)==SQLITE_ROW ){ const char *zUrl = db_column_text(&q, 0); const char *zLink = db_column_text(&q, 1); const char *zName = db_column_text(&q, 2); if( cnt++==0 ){ @ <div class="section">Defined sync targets</div> @ <table border='0' width='100%%'> } @ <tr><td>%h(zName)</td><td> </td> @ <td width='95%%'><a href='%h(zLink)'>%h(zUrl)</a></td> @ <td><nobr>%h(db_column_text(&q,3))</nobr></td></tr> } db_finalize(&q); if( cnt ){ @ </table> total += cnt; } if( total==0 ){ @ <p>No record of any URLs or checkouts</p> } style_finish_page(); } /* ** WEBPAGE: repo_schema ** ** Show the repository schema */ void repo_schema_page(void){ Stmt q; Blob sql; const char *zArg = P("n"); login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } if( zArg!=0 && db_table_exists("repository",zArg) && cgi_csrf_safe(1) ){ if( P("analyze")!=0 ){ db_multi_exec("ANALYZE \"%w\"", zArg); }else if( P("analyze200")!=0 ){ db_multi_exec("PRAGMA analysis_limit=200; ANALYZE \"%w\"", zArg); }else if( P("deanalyze")!=0 ){ db_unprotect(PROTECT_ALL); db_multi_exec("DELETE FROM repository.sqlite_stat1" " WHERE tbl LIKE %Q", zArg); db_protect_pop(); } } style_set_current_feature("stat"); style_header("Repository Schema"); style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("Stat", "stat"); style_submenu_element("URLs", "urllist"); if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){ style_submenu_element("Table Sizes", "repo-tabsize"); } blob_init(&sql, "SELECT sql FROM repository.sqlite_schema WHERE sql IS NOT NULL", -1); if( zArg ){ style_submenu_element("All", "repo_schema"); blob_appendf(&sql, " AND (tbl_name=%Q OR name=%Q)", zArg, zArg); } blob_appendf(&sql, " ORDER BY tbl_name, type<>'table', name"); db_prepare(&q, "%s", blob_str(&sql)/*safe-for-%s*/); blob_reset(&sql); @ <pre> while( db_step(&q)==SQLITE_ROW ){ @ %h(db_column_text(&q, 0)); } @ </pre> db_finalize(&q); if( db_table_exists("repository","sqlite_stat1") ){ if( zArg ){ db_prepare(&q, "SELECT tbl, idx, stat FROM repository.sqlite_stat1" " WHERE tbl LIKE %Q OR idx LIKE %Q" " ORDER BY tbl, idx", zArg, zArg); @ <hr> @ <pre> while( db_step(&q)==SQLITE_ROW ){ const char *zTab = db_column_text(&q,0); const char *zIdx = db_column_text(&q,1); const char *zStat = db_column_text(&q,2); @ INSERT INTO sqlite_stat1 VALUES('%h(zTab)','%h(zIdx)','%h(zStat)'); } @ </pre> db_finalize(&q); }else{ style_submenu_element("Stat1","repo_stat1"); } } @ <hr><form method="POST"> @ <input type="submit" name="analyze" value="Run ANALYZE"><br /> @ <input type="submit" name="analyze200"\ @ value="Run ANALYZE with limit=200"><br /> @ <input type="submit" name="deanalyze" value="De-ANALYZE"> @ </form> style_finish_page(); } /* ** WEBPAGE: repo_stat1 ** ** Show the sqlite_stat1 table for the repository schema */ void repo_stat1_page(void){ int bTabular; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } bTabular = PB("tabular"); if( P("analyze")!=0 && cgi_csrf_safe(1) ){ db_multi_exec("ANALYZE"); }else if( P("analyze200")!=0 && cgi_csrf_safe(1) ){ db_multi_exec("PRAGMA analysis_limit=200; ANALYZE;"); }else if( P("deanalyze")!=0 && cgi_csrf_safe(1) ){ db_unprotect(PROTECT_ALL); db_multi_exec("DELETE FROM repository.sqlite_stat1;"); db_protect_pop(); } style_set_current_feature("stat"); style_header("Repository STAT1 Table"); style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("Stat", "stat"); style_submenu_element("Schema", "repo_schema"); style_submenu_checkbox("tabular", "Tabular", 0, 0); if( db_table_exists("repository","sqlite_stat1") ){ Stmt q; db_prepare(&q, "SELECT tbl, idx, stat FROM repository.sqlite_stat1" " ORDER BY tbl, idx"); if( bTabular ){ @ <table border="1" cellpadding="0" cellspacing="0"> @ <tr><th>Table<th>Index<th>Stat }else{ @ <pre> } while( db_step(&q)==SQLITE_ROW ){ const char *zTab = db_column_text(&q,0); const char *zIdx = db_column_text(&q,1); const char *zStat = db_column_text(&q,2); char *zUrl = href("%R/repo_schema?n=%t",zTab); if( bTabular ){ @ <tr><td>%z(zUrl)%h(zTab)</a><td>%h(zIdx)<td>%h(zStat) }else{ @ INSERT INTO sqlite_stat1 \ @ VALUES('%z(zUrl)%h(zTab)</a>','%h(zIdx)','%h(zStat)'); } } if( bTabular ){ @ </table> }else{ @ </pre> } db_finalize(&q); } @ <p><form method="POST"> if( bTabular ){ @ <input type="hidden" name="tabular" value="1"> } @ <input type="submit" name="analyze" value="Run ANALYZE"><br /> @ <input type="submit" name="analyze200"\ @ value="Run ANALYZE with limit=200"><br> @ <input type="submit" name="deanalyze"\ @ value="De-ANALYZE"> @ </form> style_finish_page(); } /* ** WEBPAGE: repo-tabsize ** ** Show relative sizes of tables in the repository database. */ void repo_tabsize_page(void){ int nPageFree; sqlite3_int64 fsize; char zBuf[100]; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } cgi_check_for_malice(); style_set_current_feature("stat"); style_header("Repository Table Sizes"); style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("Stat", "stat"); if( g.perm.Admin ){ style_submenu_element("Schema", "repo_schema"); } db_multi_exec( "CREATE TEMP TABLE trans(name TEXT PRIMARY KEY,tabname TEXT)WITHOUT ROWID;" "INSERT INTO trans(name,tabname)" " SELECT name, tbl_name FROM repository.sqlite_schema;" "CREATE TEMP TABLE piechart(amt REAL, label TEXT);" "INSERT INTO piechart(amt,label)" " SELECT sum(pageno)," " coalesce((SELECT tabname FROM trans WHERE trans.name=dbstat.name),name)" " FROM dbstat('repository',TRUE)" " GROUP BY 2 ORDER BY 2;" ); nPageFree = db_int(0, "PRAGMA repository.freelist_count"); if( nPageFree>0 ){ db_multi_exec( "INSERT INTO piechart(amt,label) VALUES(%d,'freelist')", nPageFree ); } fsize = file_size(g.zRepositoryName, ExtFILE); approxSizeName(sizeof(zBuf), zBuf, fsize); @ <h2>Repository Size: %s(zBuf)</h2> @ <center><svg width='800' height='500'> piechart_render(800,500,PIE_OTHER|PIE_PERCENT); @ </svg></center> if( g.localOpen ){ db_multi_exec( "DELETE FROM trans;" "INSERT INTO trans(name,tabname)" " SELECT name, tbl_name FROM localdb.sqlite_schema;" "DELETE FROM piechart;" "INSERT INTO piechart(amt,label)" " SELECT sum(pageno), " " coalesce((SELECT tabname FROM trans WHERE trans.name=dbstat.name),name)" " FROM dbstat('localdb',TRUE)" " GROUP BY 2 ORDER BY 2;" ); nPageFree = db_int(0, "PRAGMA localdb.freelist_count"); if( nPageFree>0 ){ db_multi_exec( "INSERT INTO piechart(amt,label) VALUES(%d,'freelist')", nPageFree ); } fsize = file_size(g.zLocalDbName, ExtFILE); approxSizeName(sizeof(zBuf), zBuf, fsize); @ <h2>%h(file_tail(g.zLocalDbName)) Size: %s(zBuf)</h2> @ <center><svg width='800' height='500'> piechart_render(800,500,PIE_OTHER|PIE_PERCENT); @ </svg></center> } style_finish_page(); } /* ** Gather statistics on artifact types, counts, and sizes. ** ** Only populate the artstat.atype field if the bWithTypes parameter is true. */ void gather_artifact_stats(int bWithTypes){ static const char zSql[] = @ CREATE TEMP TABLE artstat( @ id INTEGER PRIMARY KEY, -- Corresponds to BLOB.RID @ atype TEXT, -- 'data', 'manifest', 'tag', 'wiki', etc. @ isDelta BOOLEAN, -- true if stored as a delta @ szExp, -- expanded, uncompressed size @ szCmpr -- size as stored on disk @ ); @ INSERT INTO artstat(id,atype,isDelta,szExp,szCmpr) @ SELECT blob.rid, NULL, @ delta.rid IS NOT NULL, @ size, octet_length(content) @ FROM blob LEFT JOIN delta ON blob.rid=delta.rid @ WHERE content IS NOT NULL; ; static const char zSql2[] = @ UPDATE artstat SET atype='file' @ WHERE +id IN (SELECT fid FROM mlink); @ UPDATE artstat SET atype='manifest' @ WHERE id IN (SELECT objid FROM event WHERE type='ci') AND atype IS NULL; @ UPDATE artstat SET atype='forum' @ WHERE id IN (SELECT objid FROM event WHERE type='f') AND atype IS NULL; @ UPDATE artstat SET atype='cluster' @ WHERE atype IS NULL @ AND id IN (SELECT rid FROM tagxref @ WHERE tagid=(SELECT tagid FROM tag @ WHERE tagname='cluster')); @ UPDATE artstat SET atype='ticket' @ WHERE atype IS NULL @ AND id IN (SELECT rid FROM tagxref @ WHERE tagid IN (SELECT tagid FROM tag @ WHERE tagname GLOB 'tkt-*')); @ UPDATE artstat SET atype='wiki' @ WHERE atype IS NULL @ AND id IN (SELECT rid FROM tagxref @ WHERE tagid IN (SELECT tagid FROM tag @ WHERE tagname GLOB 'wiki-*')); @ UPDATE artstat SET atype='technote' @ WHERE atype IS NULL @ AND id IN (SELECT rid FROM tagxref @ WHERE tagid IN (SELECT tagid FROM tag @ WHERE tagname GLOB 'event-*')); @ UPDATE artstat SET atype='attachment' @ WHERE atype IS NULL @ AND id IN (SELECT attachid FROM attachment UNION @ SELECT blob.rid FROM attachment JOIN blob ON uuid=src); @ UPDATE artstat SET atype='tag' @ WHERE atype IS NULL @ AND id IN (SELECT srcid FROM tagxref); @ UPDATE artstat SET atype='tag' @ WHERE atype IS NULL @ AND id IN (SELECT objid FROM event WHERE type='g'); @ UPDATE artstat SET atype='unused' WHERE atype IS NULL; ; db_multi_exec("%s", zSql/*safe-for-%s*/); if( bWithTypes ){ db_multi_exec("%s", zSql2/*safe-for-%s*/); } } /* ** Output text "the largest N artifacts". Make this text a hyperlink ** to bigbloblist if N is not too big. */ static void largest_n_artifacts(int N){ if( N>250 ){ @ (the largest %,d(N) artifacts) }else{ @ (the <a href='%R/bigbloblist?n=%d(N)'>largest %d(N) artifacts</a>) } } /* ** WEBPAGE: artifact_stats ** ** Show information about the sizes of artifacts in this repository */ void artifact_stats_page(void){ Stmt q; int nTotal = 0; /* Total number of artifacts */ int nDelta = 0; /* Total number of deltas */ int nFull = 0; /* Total number of full-texts */ double avgCmpr = 0.0; /* Average size of an artifact after compression */ double avgExp = 0.0; /* Average size of an uncompressed artifact */ int mxCmpr = 0; /* Maximum compressed artifact size */ int mxExp = 0; /* Maximum uncompressed artifact size */ sqlite3_int64 sumCmpr = 0; /* Total size of all compressed artifacts */ sqlite3_int64 sumExp = 0; /* Total size of all expanded artifacts */ sqlite3_int64 sz1pct = 0; /* Space used by largest 1% */ sqlite3_int64 sz10pct = 0; /* Space used by largest 10% */ sqlite3_int64 sz25pct = 0; /* Space used by largest 25% */ sqlite3_int64 sz50pct = 0; /* Space used by largest 50% */ int n50pct = 0; /* Artifacts using the first 50% of space */ int n; /* Loop counter */ int medCmpr = 0; /* Median compressed artifact size */ int medExp = 0; /* Median expanded artifact size */ int med; double r; login_check_credentials(); /* These stats are expensive to compute. To disable them for ** user without check-in privileges, to prevent excessive usage by ** robots and random passers-by on the internet */ if( !g.perm.Write && !db_get_boolean("artifact_stats_enable",0) ){ login_needed(g.anon.Write); return; } cgi_check_for_malice(); fossil_nice_default(); style_set_current_feature("stat"); style_header("Artifact Statistics"); style_submenu_element("Repository Stats", "stat"); style_submenu_element("Artifact List", "bloblist"); gather_artifact_stats(1); db_prepare(&q, "SELECT count(*), sum(isDelta), max(szCmpr)," " max(szExp), sum(szCmpr), sum(szExp)" " FROM artstat" ); db_step(&q); nTotal = db_column_int(&q,0); nDelta = db_column_int(&q,1); nFull = nTotal - nDelta; mxCmpr = db_column_int(&q, 2); mxExp = db_column_int(&q, 3); sumCmpr = db_column_int64(&q, 4); sumExp = db_column_int64(&q, 5); db_finalize(&q); if( nTotal==0 ){ @ No artifacts in this repository! style_finish_page(); return; } avgCmpr = (double)sumCmpr/nTotal; avgExp = (double)sumExp/nTotal; db_prepare(&q, "SELECT szCmpr FROM artstat ORDER BY 1 DESC"); r = 0; n = 0; while( db_step(&q)==SQLITE_ROW ){ r += db_column_int(&q, 0); if( n50pct==0 && r>=sumCmpr/2 ) n50pct = n; if( n==(nTotal+99)/100 ) sz1pct = (sqlite3_int64)r; if( n==(nTotal+9)/10 ) sz10pct = (sqlite3_int64)r; if( n==(nTotal+4)/5 ) sz25pct = (sqlite3_int64)r; if( n==(nTotal+1)/2 ){ sz50pct = (sqlite3_int64)r; medCmpr = db_column_int(&q,0); } n++; } db_finalize(&q); @ <h1>Overall Artifact Size Statistics:</h1> @ <table class="label-value"> @ <tr><th>Number of artifacts:</th><td>%,d(nTotal)</td></tr> @ <tr><th>Number of deltas:</th>\ @ <td>%,d(nDelta) (%d(nDelta*100/nTotal)%%)</td></tr> @ <tr><th>Number of full-text:</th><td>%,d(nFull) \ @ (%d(nFull*100/nTotal)%%)</td></tr> medExp = db_int(0, "SELECT szExp FROM artstat ORDER BY szExp" " LIMIT 1 OFFSET %d", nTotal/2); @ <tr><th>Uncompressed artifact sizes:</th>\ @ <td>largest: %,d(mxExp), average: %,d((int)avgExp), median: %,d(medExp)</td> @ <tr><th>Compressed artifact sizes:</th>\ @ <td>largest: %,d(mxCmpr), average: %,d((int)avgCmpr), \ @ median: %,d(medCmpr)</td> db_prepare(&q, "SELECT avg(szCmpr), max(szCmpr) FROM artstat WHERE isDelta" ); if( db_step(&q)==SQLITE_ROW ){ int mxDelta = db_column_int(&q,1); double avgDelta = db_column_double(&q,0); med = db_int(0, "SELECT szCmpr FROM artstat WHERE isDelta ORDER BY szCmpr" " LIMIT 1 OFFSET %d", nDelta/2); @ <tr><th>Delta artifact sizes:</th>\ @ <td>largest: %,d(mxDelta), average: %,d((int)avgDelta), \ @ median: %,d(med)</td> } db_finalize(&q); r = db_double(0.0, "SELECT avg(szCmpr) FROM artstat WHERE NOT isDelta;"); med = db_int(0, "SELECT szCmpr FROM artstat WHERE NOT isDelta ORDER BY szCmpr" " LIMIT 1 OFFSET %d", nFull/2); @ <tr><th>Full-text artifact sizes:</th> @ <td>largest: %,d(mxCmpr), average: %,d((int)r), median: %,d(med)</td> @ </table> @ <h1>Artifact Size Distribution Facts:</h1> @ <ol> @ <li><p>The largest %.2f(n50pct*100.0/nTotal)%% of artifacts largest_n_artifacts(n50pct); @ use 50%% of the total artifact space. @ <li><p>The largest 1%% of artifacts largest_n_artifacts((nTotal+99)/100); @ use %lld(sz1pct*100/sumCmpr)%% of the total artifact space. @ <li><p>The largest 10%% of artifacts largest_n_artifacts((nTotal+9)/10); @ use %lld(sz10pct*100/sumCmpr)%% of the total artifact space. @ <li><p>The largest 25%% of artifacts largest_n_artifacts((nTotal+4)/5); @ use %lld(sz25pct*100/sumCmpr)%% of the total artifact space. @ <li><p>The largest 50%% of artifacts largest_n_artifacts((nTotal+1)/2); @ use %lld(sz50pct*100/sumCmpr)%% of the total artifact space. @ </ol> @ <h1>Artifact Sizes By Type:</h1> db_prepare(&q, "SELECT atype, count(*), sum(isDelta), sum(szCmpr), sum(szExp)" " FROM artstat GROUP BY 1" " UNION ALL " "SELECT 'ALL', count(*), sum(isDelta), sum(szCmpr), sum(szExp)" " FROM artstat" " ORDER BY 4;" ); @ <table class='sortable' border='1' \ @ data-column-types='tkkkkk' data-init-sort='5'> @ <thead><tr> @ <th>Artifact Type</th> @ <th>Count</th> @ <th>Full-Text</th> @ <th>Delta</th> @ <th>Compressed Size</th> @ <th>Uncompressed Size</th> @ </tr></thead><tbody> while( db_step(&q)==SQLITE_ROW ){ const char *zType = db_column_text(&q, 0); int nTotal = db_column_int(&q, 1); int nDelta = db_column_int(&q, 2); int nFull = nTotal - nDelta; sqlite3_int64 szCmpr = db_column_int64(&q, 3); sqlite3_int64 szExp = db_column_int64(&q, 4); @ <tr><td>%h(zType)</td> @ <td data-sortkey='%08x(nTotal)' align='right'>%,d(nTotal)</td> @ <td data-sortkey='%08x(nFull)' align='right'>%,d(nFull)</td> @ <td data-sortkey='%08x(nDelta)' align='right'>%,d(nDelta)</td> @ <td data-sortkey='%016llx(szCmpr)' align='right'>%,lld(szCmpr)</td> @ <td data-sortkey='%016llx(szExp)' align='right'>%,lld(szExp)</td> } @ </tbody></table> db_finalize(&q); if( db_exists("SELECT 1 FROM artstat WHERE atype='unused'") ){ @ <h1>Unused Artifacts:</h1> db_prepare(&q, "SELECT artstat.id, blob.uuid, user.login," " datetime(rcvfrom.mtime), rcvfrom.rcvid" " FROM artstat JOIN blob ON artstat.id=blob.rid" " LEFT JOIN rcvfrom USING(rcvid)" " LEFT JOIN user USING(uid)" " WHERE atype='unused'" ); @ <table class='sortable' border='1' \ @ data-column-types='ntttt' data-init-sort='0'> @ <thead><tr> @ <th>RecordID</th> @ <th>Hash</th> @ <th>User</th> @ <th>Date</th> @ <th>RcvID</th> @ </tr></thead><tbody> while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); const char *zHash = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); const char *zDate = db_column_text(&q, 3); int iRcvid = db_column_int(&q, 4); @ <tr><td>%d(rid)</td> @ <td>%z(href("%R/info/%!S",zHash))%S(zHash)</a></td> @ <td>%h(zUser)</td> @ <td>%h(zDate)</td> @ <td>%z(href("%R/rcvfrom?rcvid=%d",iRcvid))%d(iRcvid)</a></td></tr> } @ </tbody></table></div> db_finalize(&q); } style_table_sorter(); style_finish_page(); } |
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 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 | /* ** Copyright (c) 2013 Stephan 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; /* ** 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', 'f', '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 */ char *zTimeSpan; /* Time span */ 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 'f': case 'F': zRealType = "f"; rc = *zRealType; break; case 'g': case 'G': zRealType = "g"; rc = *zRealType; break; case 'm': case 'M': zRealType = "m"; rc = *zRealType; break; case 'n': case 'N': zRealType = "n"; 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( P("from")!=0 && P("to")!=0 ){ zTimeSpan = mprintf( " (event.mtime BETWEEN julianday(%Q) AND julianday(%Q))", P("from"), P("to")); }else{ zTimeSpan = " 1"; } if( zRealType==0 ){ statsReportTimelineYFlag = "a"; db_multi_exec("CREATE TEMP VIEW v_reports AS " "SELECT * FROM event WHERE %s", zTimeSpan/*safe-for-%s*/); }else if( rc!='n' && rc!='m' ){ statsReportTimelineYFlag = zRealType; db_multi_exec("CREATE TEMP VIEW v_reports AS " "SELECT * FROM event WHERE (type GLOB %Q) AND %s", zRealType, zTimeSpan/*safe-for-%s*/); }else{ const char *zNot = rc=='n' ? "NOT" : ""; statsReportTimelineYFlag = "ci"; db_multi_exec( "CREATE TEMP VIEW v_reports AS " "SELECT * FROM event WHERE type='ci' AND %s" " AND objid %s IN (SELECT cid FROM plink WHERE NOT isprim)", zTimeSpan/*safe-for-%s*/, zNot/*safe-for-%s*/ ); } 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 'm': return "merge check-ins"; case 'n': return "non-merge check-ins"; case 'e': return "technotes"; case 'f': return "forum posts"; case 'w': return "wiki changes"; case 't': return "ticket changes"; case 'g': return "tag changes"; default: return "all types"; } } /* ** 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 then the report is ** restricted to events created by the named user account. */ static void stats_report_by_month_year( char includeMonth, /* 0 for stats-by-year. 1 for stats-by-month */ const char *zUserName /* Only report events by this 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 */ 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 */ int nMaxEvents = 1; /* for calculating length of graph bars. */ int iterations = 0; /* number of weeks/months we iterate over */ char *zCurrentTF; /* The timeframe in which 'now' lives */ double rNowFraction; /* Fraction of 'now' timeframe that has passed */ int nTFChar; /* Prefix of date() for timeframe */ nTFChar = includeMonth ? 7 : 4; stats_report_init_view(); db_prepare(&query, "SELECT substr(date(mtime),1,%d) AS timeframe," " count(*) AS eventCount" " FROM v_reports" " WHERE ifnull(coalesce(euser,user,'')=%Q,1)" " GROUP BY timeframe" " ORDER BY timeframe DESC", nTFChar, zUserName); @ <h1>Timeline Events (%s(stats_report_label_for_type())) @ by year%s(includeMonth ? "/month" : "") if( zUserName ){ @ for user %h(zUserName) } @ </h1> @ <table border='0' cellpadding='2' cellspacing='0' \ zCurrentTF = db_text(0, "SELECT substr(date(),1,%d)", nTFChar); if( !includeMonth ){ @ class='statistics-report-table-events sortable' \ @ data-column-types='tnx' data-init-sort='0'> style_table_sorter(); rNowFraction = db_double(0.5, "SELECT (unixepoch() - unixepoch('now','start of year'))*1.0/" " (unixepoch('now','start of year','+1 year') - " " unixepoch('now','start of year'));"); }else{ @ class='statistics-report-table-events'> rNowFraction = db_double(0.5, "SELECT (unixepoch() - unixepoch('now','start of month'))*1.0/" " (unixepoch('now','start of month','+1 month') - " " unixepoch('now','start of month'));"); } @ <thead> @ <th>%s(zTimeLabel)</th> @ <th>Events</th> @ <th width='90%%'><!-- relative commits graph --></th> @ </thead><tbody> /* 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) ){ int nCount = db_column_int(&query, 1); if( strcmp(db_column_text(&query,0),zCurrentTF)==0 && rNowFraction>0.05 ){ nCount = (int)(((double)nCount)/rNowFraction); } 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>0 && nMaxEvents>0) ? (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> showYearTotal = 0; } 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 ){ 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 ){ cgi_printf("&u=%t", zUserName); } cgi_printf("'>%s</a>", zTimeframe); } @ </td><td>%d(nCount)</td> @ <td style='white-space: nowrap;'> if( strcmp(zTimeframe, zCurrentTF)==0 && rNowFraction>0.05 && nCount>0 && nMaxEvents>0 ){ /* If the timespan covered by this row contains "now", then project ** the number of changes until the completion of the timespan and ** show a dashed box of that projection. */ int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; int nXSize = (100 * nExtra)/nMaxEvents; @ <span class='statistics-report-graph-line' \ @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ @ <span class='statistics-report-graph-extra' \ @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ }else{ @ <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. */ } 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> } } /* ** Implements the "byuser" view for /reports. */ static void stats_report_by_user(){ Stmt query = empty_Stmt; int nRowNumber = 0; /* current TR number */ int rowClass = 0; /* counter for alternating row colors */ int nMaxEvents = 1; /* max number of events for all rows. */ stats_report_init_view(); @ <h1>Timeline Events @ (%s(stats_report_label_for_type())) by User</h1> db_multi_exec( "CREATE TEMP VIEW piechart(amt,label) AS" " SELECT count(*), ifnull(euser,user) FROM v_reports" " GROUP BY ifnull(euser,user) ORDER BY count(*) DESC;" ); if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ @ <center><svg width=700 height=400> piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); @ </svg></centre><hr> } style_table_sorter(); @ <table class='statistics-report-table-events sortable' border='0' \ @ cellpadding='2' cellspacing='0' data-column-types='tkx' data-init-sort='2'> @ <thead><tr> @ <th>User</th> @ <th>Events</th> @ <th width='90%%'><!-- relative commits graph --></th> @ </tr></thead><tbody> db_prepare(&query, "SELECT ifnull(euser,user), " "COUNT(*) AS eventCount " "FROM v_reports " "GROUP BY ifnull(euser,user) ORDER BY eventCount DESC"); 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); char y = (char)statsReportType; int nSize = nCount ? (int)(100 * nCount / nMaxEvents) : 0; if(!nCount) continue /* arguable! Possible? */; else if(!nSize) nSize = 1; rowClass = ++nRowNumber % 2; @ <tr class='row%d(rowClass)'> @ <td> @ <a href="?view=bymonth&user=%h(zUser)&type=%c(y)">%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); } /* ** Implements the "byfile" view for /reports. If zUserName is not NULL then the ** report is restricted to events created by the named user account. */ static void stats_report_by_file(const char *zUserName){ 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, event" " WHERE filename.fnid=mlink.fnid" " AND mlink.mid=event.objid" " AND ifnull(coalesce(euser,user,'')=%Q,1)" " GROUP BY 1", zUserName ); 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 if( zUserName ){ @ for user %h(zUserName) } @ </h1> style_table_sorter(); @ <table class='statistics-report-table-events sortable' border='0' \ @ cellpadding='2' cellspacing='0' data-column-types='tNx' data-init-sort='2'> @ <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); } /* ** Implements the "byweekday" view for /reports. If zUserName is not NULL then ** the report is restricted to events created by the named user account. */ static void stats_report_day_of_week(const char *zUserName){ Stmt query = empty_Stmt; int nRowNumber = 0; /* current TR number */ int rowClass = 0; /* counter for alternating row colors */ int nMaxEvents = 1; /* max number of events for all rows. */ static const char *const daysOfWeek[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; stats_report_init_view(); db_prepare(&query, "SELECT cast(strftime('%%w', mtime) AS INTEGER) dow," " COUNT(*) AS eventCount" " FROM v_reports" " WHERE ifnull(coalesce(euser,user,'')=%Q,1)" " GROUP BY dow ORDER BY dow", zUserName); @ <h1>Timeline Events (%h(stats_report_label_for_type())) by Day of the Week if( zUserName ){ @ for user %h(zUserName) } @ </h1> db_multi_exec( "CREATE TEMP VIEW piechart(amt,label) AS" " SELECT count(*)," " CASE cast(strftime('%%w', mtime) AS INT)" " WHEN 0 THEN 'Sunday'" " WHEN 1 THEN 'Monday'" " WHEN 2 THEN 'Tuesday'" " WHEN 3 THEN 'Wednesday'" " WHEN 4 THEN 'Thursday'" " WHEN 5 THEN 'Friday'" " WHEN 6 THEN 'Saturday'" " ELSE 'ERROR'" " END" " FROM v_reports" " WHERE ifnull(coalesce(euser,user,'')=%Q,1)" " GROUP BY 2 ORDER BY cast(strftime('%%w', mtime) AS INT);" , zUserName ); if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ @ <center><svg width=700 height=400> piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); @ </svg></centre><hr> } style_table_sorter(); @ <table class='statistics-report-table-events sortable' border='0' \ @ cellpadding='2' cellspacing='0' data-column-types='ntnx' data-init-sort='1'> @ <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; @<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); } /* ** Implements the "byhour" view for /reports. If zUserName is not NULL ** then the report is restricted to events created by the named user ** account. */ static void stats_report_hour_of_day(const char *zUserName){ Stmt query = empty_Stmt; int nRowNumber = 0; /* current TR number */ int rowClass = 0; /* counter for alternating row colors */ int nMaxEvents = 1; /* max number of events for all rows. */ stats_report_init_view(); db_prepare(&query, "SELECT cast(strftime('%%H', mtime) AS INTEGER) hod," " COUNT(*) AS eventCount" " FROM v_reports" " WHERE ifnull(coalesce(euser,user,'')=%Q,1)" " GROUP BY hod ORDER BY hod", zUserName); @ <h1>Timeline Events (%h(stats_report_label_for_type())) by Hour of Day if( zUserName ){ @ for user %h(zUserName) } @ </h1> db_multi_exec( "CREATE TEMP VIEW piechart(amt,label) AS" " SELECT count(*), strftime('%%H', mtime) hod" " FROM v_reports" " WHERE ifnull(coalesce(euser,user,'')=%Q,1)" " GROUP BY 2 ORDER BY hod;", zUserName ); if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ @ <center><svg width=700 height=400> piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); @ </svg></centre><hr> } style_table_sorter(); @ <table class='statistics-report-table-events sortable' border='0' \ @ cellpadding='2' cellspacing='0' data-column-types='nnx' data-init-sort='1'> @ <thead><tr> @ <th>Hour</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 hourNum =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; @<tr class='row%d(rowClass)'> @ <td>%d(hourNum)</td> @ <td>%d(nCount)</td> @ <td> @ <div class='statistics-report-graph-line' @ style='width:%d(nSize)%%;'> </div> @ </td> @</tr> } @ </tbody></table> db_finalize(&query); } /* ** Helper for stats_report_by_month_year(), which generates a list of ** week numbers. The "y" query parameter is the year in format YYYY. ** If zUserName is not NULL then the report is restricted to events ** created by the named user account. */ static void stats_report_year_weeks(const char *zUserName){ const char *zYear = P("y"); /* Year for which report shown */ Stmt q; int nMaxEvents = 1; /* max number of events for all rows. */ int iterations = 0; /* # of active time periods. */ int rowCount = 0; int total = 0; char *zCurrentWeek; /* Current week number */ double rNowFraction = 0.0; /* Fraction of current week that has ** passed */ stats_report_init_view(); style_submenu_sql("y", "Year:", "WITH RECURSIVE a(b) AS (" " SELECT substr(date('now'),1,4) UNION ALL" " SELECT b-1 FROM a" " WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)" ") SELECT b, b FROM a ORDER BY b DESC"); if( zYear==0 || strlen(zYear)!=4 ){ zYear = db_text("1970","SELECT substr(date('now'),1,4);"); } cgi_printf("<br>\n"); db_prepare(&q, "SELECT DISTINCT strftime('%%W',mtime) AS wk, " " count(*) AS n " " FROM v_reports " " WHERE %Q=substr(date(mtime),1,4) " " AND mtime < current_timestamp " " AND ifnull(coalesce(euser,user,'')=%Q,1)" " GROUP BY wk ORDER BY wk DESC", zYear, zUserName); @ <h1>Timeline events (%h(stats_report_label_for_type())) @ for the calendar weeks of %h(zYear) if( zUserName ){ @ for user %h(zUserName) } @ </h1> zCurrentWeek = db_text(0, "SELECT strftime('%%W','now') WHERE date() LIKE '%q%%'", zYear); if( zCurrentWeek ){ rNowFraction = db_double(0.5, "SELECT (unixepoch()-unixepoch('now','weekday 0','-7 days'))/604800.0;"); } style_table_sorter(); cgi_printf("<table class='statistics-report-table-events sortable' " "border='0' cellpadding='2' width='100%%' " "cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n"); cgi_printf("<thead><tr>" "<th>Week</th>" "<th>Events</th>" "<th width='90%%'><!-- relative commits graph --></th>" "</tr></thead>\n" "<tbody>\n"); while( SQLITE_ROW == db_step(&q) ){ int nCount = db_column_int(&q, 1); if( zCurrentWeek!=0 && strcmp(db_column_text(&q,0),zCurrentWeek)==0 && rNowFraction>0.05 ){ nCount = (int)(((double)nCount)/rNowFraction); } if(nCount>nMaxEvents){ nMaxEvents = nCount; } ++iterations; } db_reset(&q); while( SQLITE_ROW == db_step(&q) ){ const char *zWeek = db_column_text(&q,0); const int nCount = db_column_int(&q,1); int nSize = (nCount>0 && nMaxEvents>0) ? (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 ){ cgi_printf("&u=%t",zUserName); } cgi_printf("'>%s</a></td>",zWeek); cgi_printf("<td>%d</td>",nCount); cgi_printf("<td style='white-space: nowrap;'>"); if( nCount ){ if( zCurrentWeek!=0 && strcmp(zWeek, zCurrentWeek)==0 && rNowFraction>0.05 && nMaxEvents>0 ){ /* If the covered covered by this row contains "now", then project ** the number of changes until the completion of the week and ** show a dashed box of that projection. */ int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; int nXSize = (100 * nExtra)/nMaxEvents; @ <span class='statistics-report-graph-line' \ @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ @ <span class='statistics-report-graph-extra' \ @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ }else{ @ <div class='statistics-report-graph-line' \ @ style='width:%d(nSize)%%;'> </div> \ } } cgi_printf("</td></tr>\n"); } db_finalize(&q); 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); } } /* ** Generate a report that shows the most recent change for each user. */ static void stats_report_last_change(void){ Stmt s; double rNow; char *zBaseUrl; stats_report_init_view(); style_table_sorter(); @ <h1>Event Summary @ (%s(stats_report_label_for_type())) by User</h1> @ <table border=1 class='statistics-report-table-events sortable' \ @ cellpadding=2 cellspacing=0 data-column-types='tNK' data-init-sort='3'> @ <thead><tr> @ <th>User<th>Total Changes<th>Last Change</tr></thead> @ <tbody> zBaseUrl = mprintf("%R/timeline?y=%t&u=", PD("type","ci")); db_prepare(&s, "SELECT coalesce(euser,user)," " count(*)," " max(mtime)" " FROM v_reports" " GROUP BY 1" " ORDER BY 3 DESC" ); rNow = db_double(0.0, "SELECT julianday('now');"); while( db_step(&s)==SQLITE_ROW ){ const char *zUser = db_column_text(&s, 0); int cnt = db_column_int(&s, 1); double rMTime = db_column_double(&s,2); char *zAge = human_readable_age(rNow - rMTime); @ <tr> @ <td><a href='%s(zBaseUrl)%t(zUser)'>%h(zUser)</a> @ <td>%d(cnt) @ <td data-sortkey='%f(rMTime)' style='white-space:nowrap'>%s(zAge?zAge:"") @ </tr> fossil_free(zAge); } @ </tbody></table> db_finalize(&s); } /* Report types */ #define RPT_BYFILE 1 #define RPT_BYMONTH 2 #define RPT_BYUSER 3 #define RPT_BYWEEK 4 #define RPT_BYWEEKDAY 5 #define RPT_BYYEAR 6 #define RPT_LASTCHNG 7 /* Last change made for each user */ #define RPT_BYHOUR 8 /* hour-of-day */ #define RPT_NONE 0 /* None of the above */ /* ** WEBPAGE: reports ** ** Shows activity reports for the repository. ** ** Query Parameters: ** ** view=REPORT_NAME Valid REPORT_NAME values: ** * byyear ** * bymonth ** * byweek ** * byweekday ** * byhour ** * byuser ** * byfile ** * lastchng ** user=NAME Restricts statistics to the given user ** type=TYPE Restricts the report to a specific event type: ** * all (everything), ** * ci (check-in) ** * m (merge check-in), ** * n (non-merge check-in) ** * f (forum post) ** * w (wiki page change) ** * t (ticket change) ** * g (tag added or removed) ** 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(){ const char *zView = P("view"); /* Which view/report to show. */ int eType = RPT_NONE; /* Numeric code for view/report to show */ int i; /* Loop counter */ const char *zUserName; /* Name of user */ const char *azView[16]; /* Drop-down menu of view types */ static const struct { const char *zName; /* Name of view= screen type */ const char *zVal; /* Value of view= query parameter */ int eType; /* Corresponding RPT_* define */ } aViewType[] = { { "File Changes","byfile", RPT_BYFILE }, { "Last Change", "lastchng", RPT_LASTCHNG }, { "By Month", "bymonth", RPT_BYMONTH }, { "By User", "byuser", RPT_BYUSER }, { "By Week", "byweek", RPT_BYWEEK }, { "By Weekday", "byweekday", RPT_BYWEEKDAY }, { "By Year", "byyear", RPT_BYYEAR }, { "By Hour", "byhour", RPT_BYHOUR }, }; static const char *const azType[] = { "a", "All Changes", "ci", "Check-ins", "f", "Forum Posts", "m", "Merge check-ins", "n", "Non-merge check-ins", "g", "Tags", "e", "Tech Notes", "t", "Tickets", "w", "Wiki" }; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } zUserName = P("user"); if( zUserName==0 ) zUserName = P("u"); if( zUserName && zUserName[0]==0 ) zUserName = 0; if( zView==0 ){ zView = "byuser"; cgi_replace_query_parameter("view","byuser"); } for(i=0; i<count(aViewType); i++){ if( fossil_strcmp(zView, aViewType[i].zVal)==0 ){ eType = aViewType[i].eType; break; } } cgi_check_for_malice(); if( eType!=RPT_NONE ){ int nView = 0; /* Slots used in azView[] */ for(i=0; i<count(aViewType); i++){ azView[nView++] = aViewType[i].zVal; azView[nView++] = aViewType[i].zName; } if( eType!=RPT_BYFILE ){ style_submenu_multichoice("type", count(azType)/2, azType, 0); } style_submenu_multichoice("view", nView/2, azView, 0); if( eType!=RPT_BYUSER && eType!=RPT_LASTCHNG ){ style_submenu_sql("user","User:", "SELECT '', 'All Users' UNION ALL " "SELECT x, x FROM (" " SELECT DISTINCT trim(coalesce(euser,user)) AS x FROM event %s" " ORDER BY 1 COLLATE nocase) WHERE x!=''", eType==RPT_BYFILE ? "WHERE type='ci'" : "" ); } } style_submenu_element("Stats", "%R/stat"); style_header("Activity Reports"); switch( eType ){ case RPT_BYYEAR: stats_report_by_month_year(0, zUserName); break; case RPT_BYMONTH: stats_report_by_month_year(1, zUserName); break; case RPT_BYWEEK: stats_report_year_weeks(zUserName); break; default: case RPT_BYUSER: stats_report_by_user(); break; case RPT_BYWEEKDAY: stats_report_day_of_week(zUserName); break; case RPT_BYFILE: stats_report_by_file(zUserName); break; case RPT_BYHOUR: stats_report_hour_of_day(zUserName); break; case RPT_LASTCHNG: stats_report_last_change(); break; } style_finish_page(); } |
Added src/style.admin_log.css.
> > > > > > | 1 2 3 4 5 6 | /* This file is just to demonstrate/test page-specific CSS. Using the browser dev tools, select any link, "inspect" it, and edit this style. */ a{ font-size: inherit; } |
Changes to src/style.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement the basic web page look and feel. ** */ #include "config.h" #include "style.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 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 | ** 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_binary() ** style_submenu_multichoice() ** style_submenu_sql() ** ** prior to calling style_finish_page(). The style_finish_page() routine ** will generate the appropriate HTML text just below the main ** menu. */ static struct Submenu { const char *zLabel; /* Button label */ 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_CHECKBOX */ unsigned char eVisible; /* STYLE_NORMAL or STYLE_DISABLED */ short int iSize; /* Width for FF_ENTRY. Count for FF_MULTI */ const char *const *azChoice; /* value/display pairs for FF_MULTI */ const char *zFalse; /* FF_BINARY label when false */ const char *zJS; /* Javascript to run on toggle */ } aSubmenuCtrl[20]; static int nSubmenuCtrl = 0; #define FF_ENTRY 1 /* Text entry box */ #define FF_MULTI 2 /* Combobox. Multiple choices. */ #define FF_BINARY 3 /* Control for binary query parameter */ #define FF_CHECKBOX 4 /* Check-box */ #if INTERFACE #define STYLE_NORMAL 0 /* Normal display of control */ #define STYLE_DISABLED 1 /* Control is disabled */ #endif /* INTERFACE */ /* ** 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; /* ** Submenu disable flag */ static int submenuEnable = 1; /* ** Disable content-security-policy. ** Warning: Do not disable the CSP without careful consideration! */ static int disableCSP = 0; /* ** Flags for various javascript files needed prior to </body> */ static int needHrefJs = 0; /* href.js */ /* ** Extra JS added to the end of the file. */ static Blob blobOnLoad = BLOB_INITIALIZER; /* ** Generate and return an anchor tag like this: ** ** <a href="URL"> ** or <a id="ID"> ** ** The form of the anchor tag is determined by the g.jsHref ** and g.perm.Hyperlink variables. ** ** g.perm.Hyperlink g.jsHref Returned anchor format ** ---------------- -------- ------------------------ ** 0 0 (empty string) ** 0 1 (empty string) ** 1 0 <a href="URL"> ** 1 1 <a data-href="URL"> ** ** No anchor tag is generated if g.perm.Hyperlink is false. ** The href="URL" form is used if g.jsHref is false. ** If g.jsHref is true then the data-href="URL" and ** href="/honeypot" is generated and javascript is added to the footer ** to cause data-href values to be inserted into href ** after the page has loaded. The use of the data-href="URL" form ** instead of href="URL" is a defense against bots. ** ** If the user lacks the Hyperlink (h) property and the "auto-hyperlink" ** setting is true, then g.perm.Hyperlink is changed from 0 to 1 and ** g.jsHref is set to 1 by login_check_credentials(). Thus ** the g.perm.Hyperlink property will be true even if the user does not ** have the "h" privilege if the "auto-hyperlink" setting is true. ** ** User has "h" auto-hyperlink g.perm.Hyperlink g.jsHref ** ------------ -------------- ---------------- --------------------- ** 0 0 0 0 ** 1 0 1 0 ** 0 1 1 1 ** 1 1 1 0 ** ** So, in other words, tracing input configuration to final actions we have: ** ** User has "h" auto-hyperlink Returned anchor format ** ------------ -------------- ---------------------- ** 0 0 (empty string) ** 1 0 <a href="URL"> ** 0 1 <a data-href="URL"> ** 1 1 <a href="URL"> ** ** The name of these routines are deliberately kept short so that can be ** easily used within @-lines. Example: ** ** @ %z(href("%R/artifact/%s",zUuid))%h(zFN)</a> ** ** Note %z format. The string returned by this function is always ** obtained from fossil_malloc() so rendering it with %z will reclaim ** that memory space. ** ** There are three versions of this routine: ** ** (1) href() does a plain hyperlink ** (2) xhref() adds extra attribute text ** (3) chref() adds a class name ** ** g.perm.Hyperlink is true if the user has the Hyperlink (h) property. ** Most logged in users should have this property, since we can assume ** that a logged in user is not a bot. Only "nobody" lacks g.perm.Hyperlink, ** typically. */ char *xhref(const char *zExtra, const char *zFormat, ...){ char *zUrl; va_list ap; if( !g.perm.Hyperlink ) return fossil_strdup(""); va_start(ap, zFormat); zUrl = vmprintf(zFormat, ap); va_end(ap); if( !g.jsHref ){ char *zHUrl; if( zExtra ){ zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl); }else{ zHUrl = mprintf("<a href=\"%h\">", zUrl); } fossil_free(zUrl); return zHUrl; } needHrefJs = 1; if( zExtra==0 ){ return mprintf("<a data-href='%z' href='%R/honeypot'>", zUrl); }else{ return mprintf("<a %s data-href='%z' href='%R/honeypot'>", zExtra, zUrl); } } char *chref(const char *zExtra, const char *zFormat, ...){ char *zUrl; va_list ap; if( !g.perm.Hyperlink ) return fossil_strdup(""); va_start(ap, zFormat); zUrl = vmprintf(zFormat, ap); va_end(ap); if( !g.jsHref ){ char *zHUrl = mprintf("<a class=\"%s\" href=\"%h\">", zExtra, zUrl); fossil_free(zUrl); return zHUrl; } needHrefJs = 1; return mprintf("<a class='%s' data-href='%z' href='%R/honeypot'>", zExtra, zUrl); } char *href(const char *zFormat, ...){ char *zUrl; va_list ap; if( !g.perm.Hyperlink ) return fossil_strdup(""); va_start(ap, zFormat); zUrl = vmprintf(zFormat, ap); va_end(ap); if( !g.jsHref ){ char *zHUrl = mprintf("<a href=\"%h\">", zUrl); fossil_free(zUrl); return zHUrl; } needHrefJs = 1; return mprintf("<a data-href='%s' href='%R/honeypot'>", zUrl); } /* ** Generate <form method="post" action=ARG>. The ARG value is determined ** by the arguments. ** ** As a defense against robots, the action=ARG might instead by data-action=ARG ** and javascript (href.js) added to the page so that the data-action= is ** changed into action= after the page loads. Whether or not this happens ** depends on if the user has the "h" privilege and whether or not the ** auto-hyperlink setting is on. These setings determine the values of ** variables g.perm.Hyperlink and g.jsHref. ** ** User has "h" auto-hyperlink g.perm.Hyperlink g.jsHref ** ------------ -------------- ---------------- -------- ** 1: 0 0 0 0 ** 2: 1 0 1 0 ** 3: 0 1 1 1 ** 4: 1 1 1 0 ** ** The data-action=ARG form is used for cases 1 and 3. In case 1, the href.js ** javascript is omitted and so the form is effectively disabled. */ void form_begin(const char *zOtherArgs, const char *zAction, ...){ char *zLink; va_list ap; if( zOtherArgs==0 ) zOtherArgs = ""; va_start(ap, zAction); zLink = vmprintf(zAction, ap); va_end(ap); if( g.perm.Hyperlink ){ @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)> }else{ needHrefJs = 1; @ <form method="POST" data-action='%s(zLink)' action='%R/login' \ @ %s(zOtherArgs)> } login_insert_csrf_secret(); } /* ** Add a new element to the submenu */ void style_submenu_element( const char *zLabel, const char *zLink, ... ){ va_list ap; assert( nSubmenu < count(aSubmenu) ); aSubmenu[nSubmenu].zLabel = 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 eVisible /* Visible or disabled */ ){ assert( nSubmenuCtrl < count(aSubmenuCtrl) ); aSubmenuCtrl[nSubmenuCtrl].zName = zName; aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel; aSubmenuCtrl[nSubmenuCtrl].iSize = iSize; aSubmenuCtrl[nSubmenuCtrl].eVisible = eVisible; aSubmenuCtrl[nSubmenuCtrl].eType = FF_ENTRY; nSubmenuCtrl++; } void style_submenu_checkbox( const char *zName, /* Query parameter name */ const char *zLabel, /* Label to display after the checkbox */ int eVisible, /* Visible or disabled */ const char *zJS /* Optional javascript to run on toggle */ ){ assert( nSubmenuCtrl < count(aSubmenuCtrl) ); aSubmenuCtrl[nSubmenuCtrl].zName = zName; aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel; aSubmenuCtrl[nSubmenuCtrl].eVisible = eVisible; aSubmenuCtrl[nSubmenuCtrl].zJS = zJS; aSubmenuCtrl[nSubmenuCtrl].eType = FF_CHECKBOX; 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 eVisible /* Visible or disabled */ ){ assert( nSubmenuCtrl < count(aSubmenuCtrl) ); aSubmenuCtrl[nSubmenuCtrl].zName = zName; aSubmenuCtrl[nSubmenuCtrl].zLabel = zTrue; aSubmenuCtrl[nSubmenuCtrl].zFalse = zFalse; aSubmenuCtrl[nSubmenuCtrl].eVisible = eVisible; aSubmenuCtrl[nSubmenuCtrl].eType = FF_BINARY; nSubmenuCtrl++; } void style_submenu_multichoice( const char *zName, /* Query parameter name */ int nChoice, /* Number of options */ const char *const *azChoice, /* value/display pairs. 2*nChoice entries */ int eVisible /* Visible or disabled */ ){ assert( nSubmenuCtrl < count(aSubmenuCtrl) ); aSubmenuCtrl[nSubmenuCtrl].zName = zName; aSubmenuCtrl[nSubmenuCtrl].iSize = nChoice; aSubmenuCtrl[nSubmenuCtrl].azChoice = azChoice; aSubmenuCtrl[nSubmenuCtrl].eVisible = eVisible; aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI; nSubmenuCtrl++; } void style_submenu_sql( const char *zName, /* Query parameter name */ const char *zLabel, /* Label on the control */ const char *zFormat, /* Format string for SQL command for choices */ ... /* Arguments to the format string */ ){ Stmt q; int n = 0; int nAlloc = 0; char **az = 0; va_list ap; va_start(ap, zFormat); db_vprepare(&q, 0, zFormat, ap); va_end(ap); while( SQLITE_ROW==db_step(&q) ){ if( n+2>=nAlloc ){ nAlloc += nAlloc + 20; az = fossil_realloc(az, sizeof(char*)*nAlloc); } az[n++] = fossil_strdup(db_column_text(&q,0)); az[n++] = fossil_strdup(db_column_text(&q,1)); } db_finalize(&q); if( n>0 ){ aSubmenuCtrl[nSubmenuCtrl].zName = zName; aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel; aSubmenuCtrl[nSubmenuCtrl].iSize = n/2; aSubmenuCtrl[nSubmenuCtrl].azChoice = (const char *const *)az; aSubmenuCtrl[nSubmenuCtrl].eVisible = STYLE_NORMAL; aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI; nSubmenuCtrl++; } } /* ** Disable or enable the submenu */ void style_submenu_enable(int onOff){ submenuEnable = onOff; } /* ** 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; return fossil_strcmp(A->zLabel, B->zLabel); } /* Use this for the $current_page variable if it is not NULL. If it ** is NULL then use g.zPath. */ static char *local_zCurrentPage = 0; /* ** Set the desired $current_page to something other than g.zPath */ void style_set_current_page(const char *zFormat, ...){ fossil_free(local_zCurrentPage); if( zFormat==0 ){ local_zCurrentPage = 0; }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 stylesheet. ** ** The name of the new variable will be "stylesheet_url". ** ** The value will be a URL for accessing the appropriate stylesheet. ** This URL will include query parameters such as "id=" and "once&skin=" ** to cause the correct stylesheet to be loaded after a skin change ** or after a change to the stylesheet. */ static void stylesheet_url_var(void){ char *zBuiltin; /* Auxiliary page-specific CSS page */ Blob url; /* The URL */ /* Initialize the URL to its baseline */ url = empty_blob; blob_appendf(&url, "%R/style.css"); /* If page-specific CSS exists for the current page, then append ** the pathname for the page-specific CSS. The default CSS is ** ** /style.css ** ** But for the "/wikiedit" page (to name but one example), we ** append a path as follows: ** ** /style.css/wikiedit ** ** The /style.css page (implemented below) will detect this extra "wikiedit" ** path information and include the page-specific CSS along with the ** default CSS when it delivers the page. */ zBuiltin = mprintf("style.%s.css", g.zPath); if( builtin_file(zBuiltin,0)!=0 ){ blob_appendf(&url, "/%s", g.zPath); } fossil_free(zBuiltin); /* Add query parameters that will change whenever the skin changes ** or after any updates to the CSS files */ blob_appendf(&url, "?id=%x", skin_id("css")); if( P("once")!=0 && P("skin")!=0 ){ blob_appendf(&url, "&skin=%s&once", skin_in_use()); } /* Generate the CSS URL variable */ Th_Store("stylesheet_url", blob_str(&url)); blob_reset(&url); } /* ** Create a TH1 variable containing the URL for the specified image. ** The resulting variable name will be of the form $[zImageName]_image_url. ** The value will be a URL that includes an id= query parameter that ** changes if the underlying resource changes or if a different skin ** is selected. */ static void image_url_var(const char *zImageName){ char *zVarName; /* Name of the new TH1 variable */ char *zResource; /* Name of CONFIG entry holding content */ char *zUrl; /* The URL */ zResource = mprintf("%s-image", zImageName); zUrl = mprintf("%R/%s?id=%x", zImageName, skin_id(zResource)); free(zResource); zVarName = mprintf("%s_image_url", zImageName); Th_Store(zVarName, zUrl); free(zVarName); free(zUrl); } /* ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js ** Javascript module, and generates HTML elements with the following IDs: ** ** TARGETID: The <span> wrapper around TEXT. ** copy-TARGETID: The <span> for the copy button. ** ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT. ** ** The COPYLENGTH argument defines the length of the substring of TEXT copied to ** clipboard: ** ** <= 0: No limit (default if the argument is omitted). ** >= 3: Truncate TEXT after COPYLENGTH (single-byte) characters. ** 1: Use the "hash-digits" setting as the limit. ** 2: Use the length appropriate for URLs as the limit (defined at ** compile-time by FOSSIL_HASH_DIGITS_URL, defaults to 16). */ char *style_copy_button( int bOutputCGI, /* Don't return result, but send to cgi_printf(). */ const char *zTargetId, /* The TARGETID argument. */ int bFlipped, /* The FLIPPED argument. */ int cchLength, /* The COPYLENGTH argument. */ const char *zTextFmt, /* Formatting of the TEXT argument (htmlized). */ ... /* Formatting parameters of the TEXT argument. */ ){ va_list ap; char *zText; char *zResult = 0; va_start(ap,zTextFmt); zText = vmprintf(zTextFmt/*works-like:?*/,ap); va_end(ap); if( cchLength==1 ) cchLength = hash_digits(0); else if( cchLength==2 ) cchLength = hash_digits(1); if( !bFlipped ){ const char *zBtnFmt = "<span class=\"nobr\">" "<span " "class=\"copy-button\" " "id=\"copy-%h\" " "data-copytarget=\"%h\" " "data-copylength=\"%d\">" "</span>" "<span id=\"%h\">" "%s" "</span>" "</span>"; if( bOutputCGI ){ cgi_printf( zBtnFmt/*works-like:"%h%h%d%h%s"*/, zTargetId,zTargetId,cchLength,zTargetId,zText); }else{ zResult = mprintf( zBtnFmt/*works-like:"%h%h%d%h%s"*/, zTargetId,zTargetId,cchLength,zTargetId,zText); } }else{ const char *zBtnFmt = "<span class=\"nobr\">" "<span id=\"%h\">" "%s" "</span>" "<span " "class=\"copy-button copy-button-flipped\" " "id=\"copy-%h\" " "data-copytarget=\"%h\" " "data-copylength=\"%d\">" "</span>" "</span>"; if( bOutputCGI ){ cgi_printf( zBtnFmt/*works-like:"%h%s%h%h%d"*/, zTargetId,zText,zTargetId,zTargetId,cchLength); }else{ zResult = mprintf( zBtnFmt/*works-like:"%h%s%h%h%d"*/, zTargetId,zText,zTargetId,zTargetId,cchLength); } } free(zText); builtin_request_js("copybtn.js"); return zResult; } /* ** Return a random nonce that is stored in static space. For a particular ** run, the same nonce is always returned. */ char *style_nonce(void){ static char zNonce[52]; if( zNonce[0]==0 ){ unsigned char zSeed[24]; sqlite3_randomness(24, zSeed); encode16(zSeed,(unsigned char*)zNonce,24); } return zNonce; } /* ** Return the default Content Security Policy (CSP) string. ** If the toHeader argument is true, then also add the ** CSP to the HTTP reply header. ** ** The CSP comes from the "default-csp" setting if it exists and ** is non-empty. If that setting is an empty string, then the following ** default is used instead: ** ** default-src 'self' data:; ** script-src 'self' 'nonce-$nonce'; ** style-src 'self' 'unsafe-inline'; ** img-src * data:; ** ** The text '$nonce' is replaced by style_nonce() if and whereever it ** occurs in the input string. ** ** The string returned is obtained from fossil_malloc() and ** should be released by the caller. */ char *style_csp(int toHeader){ static const char zBackupCSP[] = "default-src 'self' data:; " "script-src 'self' 'nonce-$nonce'; " "style-src 'self' 'unsafe-inline'; " "img-src * data:"; const char *zFormat; Blob csp; char *zNonce; char *zCsp; int i; if( disableCSP ) return fossil_strdup(""); zFormat = db_get("default-csp",0); if( zFormat==0 ){ zFormat = zBackupCSP; } blob_init(&csp, 0, 0); while( zFormat[0] && (zNonce = strstr(zFormat,"$nonce"))!=0 ){ blob_append(&csp, zFormat, (int)(zNonce - zFormat)); blob_append(&csp, style_nonce(), -1); zFormat = zNonce + 6; } blob_append(&csp, zFormat, -1); zCsp = blob_str(&csp); /* No whitespace other than actual space characters allowed in the CSP ** string. See https://fossil-scm.org/forum/forumpost/d29e3af43c */ for(i=0; zCsp[i]; i++){ if( fossil_isspace(zCsp[i]) ) zCsp[i] = ' '; } if( toHeader ){ cgi_printf_header("Content-Security-Policy: %s\r\n", zCsp); } return zCsp; } /* ** Disable content security policy for the current page. ** WARNING: Do not do this lightly! ** ** This routine must be called before the CSP is sued by ** style_header(). */ void style_disable_csp(void){ disableCSP = 1; } /* ** Default HTML page header text through <body>. If the repository-specific ** header template lacks a <body> tag, then all of the following is ** prepended. */ static const char zDfltHeader[] = @ <html> @ <head> @ <meta charset="UTF-8"> @ <base href="$baseurl/$current_page"> @ <meta http-equiv="Content-Security-Policy" content="$default_csp"> @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> @ <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"> @ </head> @ <body class="$current_feature rpage-$requested_page cpage-$canonical_page"> ; /* ** Returns the default page header. */ const char *get_default_header(){ return zDfltHeader; } /* ** The default TCL list that defines the main menu. */ static const char zDfltMainMenu[] = @ Home /home * {} @ Timeline /timeline {o r j} {} @ Files /dir?ci=tip oh desktoponly @ Branches /brlist o wideonly @ Tags /taglist o wideonly @ Forum /forum {@2 3 4 5 6} wideonly @ Chat /chat C wideonly @ Tickets /ticket r wideonly @ Wiki /wiki j wideonly @ Admin /setup {a s} desktoponly @ Logout /logout L wideonly @ Login /login !L wideonly ; /* ** Return the default menu */ const char *style_default_mainmenu(void){ return zDfltMainMenu; } /* ** Given a URL path, extract the first element as a "feature" name, ** used as the <body class="FEATURE"> value by default, though ** later-running code may override this, typically to group multiple ** Fossil UI URLs into a single "feature" so you can have per-feature ** CSS rules. ** ** For example, "body.forum div.markdown blockquote" targets only ** block quotes made in forum posts, leaving other Markdown quotes ** alone. Because feature class "forum" groups /forummain, /forumpost, ** and /forume2, it works across all renderings of Markdown to HTML ** within the Fossil forum feature. */ static const char* feature_from_page_path(const char *zPath){ const char* zSlash = strchr(zPath, '/'); if (zSlash) { return fossil_strndup(zPath, zSlash - zPath); } else { return zPath; } } /* ** Override the value of the TH1 variable current_feature, its default ** set by feature_from_page_path(). We do not call this from ** style_init_th1_vars() because that uses Th_MaybeStore() instead to ** allow webpage implementations to call this before style_header() ** to override that "maybe" default with something better. */ void style_set_current_feature(const char* zFeature){ Th_Store("current_feature", zFeature); } /* ** Returns the current mainmenu value from either the --mainmenu flag ** (handled by the server/ui/cgi commands), the "mainmenu" config ** setting, or style_default_mainmenu(), in that order, returning the ** first of those which is defined. */ const char *style_get_mainmenu(){ static const char *zMenu = 0; if(!zMenu){ if(g.zMainMenuFile){ Blob b = empty_blob; blob_read_from_file(&b, g.zMainMenuFile, ExtFILE); zMenu = blob_str(&b); }else{ zMenu = db_get("mainmenu", style_default_mainmenu()); } } return zMenu; } /* ** Initialize all the default TH1 variables */ static void style_init_th1_vars(const char *zTitle){ const char *zNonce = style_nonce(); char *zDfltCsp; zDfltCsp = style_csp(1); /* ** Do not overwrite the TH1 variable "default_csp" if it exists, as this ** allows it to be properly overridden via the TH1 setup script (i.e. it ** is evaluated before the header is rendered). */ Th_MaybeStore("default_csp", zDfltCsp); fossil_free(zDfltCsp); Th_Store("nonce", zNonce); Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); Th_Store("project_description", db_get("project-description","")); if( zTitle ) Th_Store("title", zTitle); Th_Store("baseurl", g.zBaseURL); Th_Store("secureurl", fossil_wants_https(1)? 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); if( g.zPath ){ /* store the first segment of a path; */ char *pSlash = strchr(g.zPath,'/'); if( pSlash ) *pSlash = 0; /* make a temporary cut if necessary */ Th_Store("requested_page", escape_quotes(g.zPath)); if( pSlash ) *pSlash = '/'; }else{ Th_Store("requested_page", ""); } Th_Store("canonical_page", escape_quotes(g.zPhase+1)); 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); Th_Store("mainmenu", style_get_mainmenu()); stylesheet_url_var(); image_url_var("logo"); image_url_var("background"); if( !login_is_nobody() ){ Th_Store("login", g.zLogin); } Th_MaybeStore("current_feature", feature_from_page_path(local_zCurrentPage) ); if( g.ftntsIssues[0] || g.ftntsIssues[1] || g.ftntsIssues[2] || g.ftntsIssues[3] ){ char buf[80]; sqlite3_snprintf(sizeof(buf), buf, "%i %i %i %i", g.ftntsIssues[0], g.ftntsIssues[1], g.ftntsIssues[2], g.ftntsIssues[3]); Th_Store("footnotes_issues_counters", buf); } } /* ** 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 */ style_init_th1_vars(zTitle); if( sqlite3_strlike("%<body%", zHeader, 0)!=0 ){ Th_Render(zDfltHeader); } 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); g.cgiOutput = 1; headerHasBeenGenerated = 1; sideboxUsed = 0; if( g.perm.Debug && P("showqp") ){ @ <div class="debug"> cgi_print_all(0, 0, 0); @ </div> } } #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( db_get_boolean("adunit-disable",0) ) return 0; 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; } /* ** Indicate that the table-sorting javascript is needed. */ void style_table_sorter(void){ builtin_request_js("sorttable.js"); } /* ** Generate code to load all required javascript files. */ static void style_load_all_js_files(void){ if( needHrefJs && g.perm.Hyperlink ){ int nDelay = db_get_int("auto-hyperlink-delay",0); int bMouseover = db_get_boolean("auto-hyperlink-mouseover",0) && sqlite3_strglob("*Android*",PD("HTTP_USER_AGENT","")); @ <script id='href-data' type='text/json'>\ @ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script> } @ <script nonce="%h(style_nonce())">/* style.c:%d(__LINE__) */ @ function debugMsg(msg){ @ var n = document.getElementById("debugMsg"); @ if(n){n.textContent=msg;} @ } if( needHrefJs && g.perm.Hyperlink ){ @ /* href.js */ cgi_append_content(builtin_text("href.js"),-1); } if( blob_size(&blobOnLoad)>0 ){ @ window.onload = function(){ cgi_append_content(blob_buffer(&blobOnLoad), blob_size(&blobOnLoad)); cgi_append_content("\n}\n", -1); } @ </script> builtin_fulfill_js_requests(); } /* ** Transorm input string into a token that is safe for inclusion into ** class attribute. Digits and low-case letter are passed unchanged, ** upper-case letters are transformed to low-case, everything else is ** tranformed into hyphens; consequtive and pending hyphens are squeezed. ** If result does not fit into szOut chars then it is truncated. ** Result is always terminated with null. */ void style_derive_classname(const char *zIn, char *zOut, int szOut){ assert( zOut ); assert( szOut>0 ); if( zIn ){ int n = 0; /* number of chars written to zOut */ char c; for(--szOut; (c=*zIn) && n<szOut; zIn++) { if( ('a'<=c && c<='z') || ('0'<=c && c<='9') ){ *zOut = c; }else if( 'A'<=c && c<='Z' ){ *zOut = c - 'A' + 'a'; }else{ if( n==0 || zOut[-1]=='-' ) continue; *zOut = '-'; } zOut++; n++; } if( n && zOut[-1]=='-' ) zOut--; } *zOut = 0; } /* ** Invoke this routine after all of the content for a webpage has been ** generated. This routine should be called once for every webpage, at ** or near the end of page generation. This routine does the following: ** ** * Populates the header of the page, including setting up appropriate ** submenu elements. The header generation is deferred until this point ** so that we know that all style_submenu_element() and similar have ** been received. ** ** * Finalizes the page content. ** ** * Appends the footer. */ void style_finish_page(){ 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( submenuEnable && nSubmenu+nSubmenuCtrl>0 ){ int i; char zClass[32]; /* reduced form of the main attribute */ if( nSubmenuCtrl ){ @ <form id='f01' method='GET' action='%R/%s(g.zPath)'> @ <input type='hidden' name='udc' value='1'> cgi_tag_query_parameter("udc"); } @ <div class="submenu"> if( nSubmenu>0 ){ qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare); for(i=0; i<nSubmenu; i++){ struct Submenu *p = &aSubmenu[i]; style_derive_classname(p->zLabel, zClass, sizeof zClass); /* switching away from the %h formatting below might be dangerous ** because some places use %s to compose zLabel and zLink; ** e.g. /rptview page and the submenuCmd() function. ** "sml" stands for submenu link. */ if( p->zLink==0 ){ @ <span class="label sml-%s(zClass)">%h(p->zLabel)</span> }else{ @ <a class="label sml-%s(zClass)" href="%h(p->zLink)">%h(p->zLabel)</a> } } } strcpy(zClass,"smc-"); /* common prefix for submenu controls */ for(i=0; i<nSubmenuCtrl; i++){ const char *zQPN = aSubmenuCtrl[i].zName; const char *zDisabled = ""; const char *zXtraClass = ""; if( aSubmenuCtrl[i].eVisible & STYLE_DISABLED ){ zDisabled = " disabled"; }else if( zQPN ){ cgi_tag_query_parameter(zQPN); } style_derive_classname(zQPN, zClass+4, sizeof(zClass)-4); switch( aSubmenuCtrl[i].eType ){ case FF_ENTRY: @ <span class='submenuctrl%s(zXtraClass) %s(zClass)'>\ @ %h(aSubmenuCtrl[i].zLabel)\ @ <input type='text' name='%s(zQPN)' value='%h(PD(zQPN, ""))' \ if( aSubmenuCtrl[i].iSize<0 ){ @ size='%d(-aSubmenuCtrl[i].iSize)' \ }else if( aSubmenuCtrl[i].iSize>0 ){ @ size='%d(aSubmenuCtrl[i].iSize)' \ @ maxlength='%d(aSubmenuCtrl[i].iSize)' \ } @ id='submenuctrl-%d(i)'%s(zDisabled)></span> break; case FF_MULTI: { int j; const char *zVal = P(zQPN); if( zXtraClass[0] ){ @ <span class='%s(zXtraClass+1) %s(zClass)'> } if( aSubmenuCtrl[i].zLabel ){ @ %h(aSubmenuCtrl[i].zLabel)\ } @ <select class='submenuctrl %s(zClass)' size='1' name='%s(zQPN)' \ @ id='submenuctrl-%d(i)'%s(zDisabled)> for(j=0; j<aSubmenuCtrl[i].iSize*2; j+=2){ const char *zQPV = aSubmenuCtrl[i].azChoice[j]; @ <option value='%h(zQPV)'\ if( fossil_strcmp(zVal, zQPV)==0 ){ @ selected\ } @ >%h(aSubmenuCtrl[i].azChoice[j+1])</option> } @ </select> if( zXtraClass[0] ){ @ </span> } break; } case FF_BINARY: { int isTrue = PB(zQPN); @ <select class='submenuctrl%s(zXtraClass)' size='1' \ @ name='%s(zQPN)' id='submenuctrl-%d(i)'%s(zDisabled)> @ <option value='1'\ if( isTrue ){ @ selected\ } @ >%h(aSubmenuCtrl[i].zLabel)</option> @ <option value='0'\ if( !isTrue ){ @ selected\ } @ >%h(aSubmenuCtrl[i].zFalse)</option> @ </select> break; } case FF_CHECKBOX: { @ <label class='submenuctrl submenuckbox%s(zXtraClass) %s(zClass)'>\ @ <input type='checkbox' name='%s(zQPN)' id='submenuctrl-%d(i)' \ if( PB(zQPN) ){ @ checked \ } if( aSubmenuCtrl[i].zJS ){ @ data-ctrl='%s(aSubmenuCtrl[i].zJS)'%s(zDisabled)>\ }else{ @ %s(zDisabled)>\ } @ %h(aSubmenuCtrl[i].zLabel)</label> break; } } } @ </div> if( nSubmenuCtrl ){ cgi_query_parameters_to_hidden(); cgi_tag_query_parameter(0); @ </form> builtin_request_js("menu.js"); } } 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"><span id="debugMsg"></span> cgi_destination(CGI_BODY); if( sideboxUsed ){ @ <div class="endContent"></div> } @ </div> /* Put the footer at the bottom of the page. */ zFooter = skin_get("footer"); if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){ style_load_all_js_files(); } 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); cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog)); cgi_append_content("</span>\n", -1); } /* Add document end mark if it was not in the footer */ if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){ style_load_all_js_files(); @ </body> @ </html> } /* Update the user display prefs cookie if it was modified */ cookie_render(); } /* ** Begin a side-box on the right-hand side of a page. The title and ** the width of the box are given as arguments. The width is usually ** a percentage of total screen width. */ void style_sidebox_begin(const char *zTitle, const char *zWidth){ sideboxUsed = 1; @ <div class="sidebox" style="width:%s(zWidth)"> @ <div class="sideboxTitle">%h(zTitle)</div> } /* End the side-box */ void style_sidebox_end(void){ @ </div> } /* ** Search string zCss for zSelector. ** ** Return true if found. Return false if not found */ static int containsSelector(const char *zCss, const char *zSelector){ const char *z; int n; int selectorLen = (int)strlen(zSelector); for(z=zCss; *z; z+=selectorLen){ z = strstr(z, zSelector); if( z==0 ) return 0; if( z!=zCss ){ for( n=-1; z+n!=zCss && fossil_isspace(z[n]); n--); if( z+n!=zCss && z[n]!=',' && z[n]!= '}' && z[n]!='/' ) continue; } for( n=selectorLen; z[n] && fossil_isspace(z[n]); n++ ); if( z[n]==',' || z[n]=='{' || z[n]=='/' ) return 1; } return 0; } /* ** COMMAND: test-contains-selector ** ** Usage: %fossil test-contains-selector FILENAME SELECTOR ** ** Determine if the CSS stylesheet FILENAME contains SELECTOR. ** ** Note that as of 2020-05-28, the default rules are always emitted, ** so the containsSelector() logic is no longer applied when emitting ** style.css. It is unclear whether this test command is now obsolete ** or whether it may still serve a purpose. */ void contains_selector_cmd(void){ int found; char *zSelector; Blob css; if( g.argc!=4 ) usage("FILENAME SELECTOR"); blob_read_from_file(&css, g.argv[2], ExtFILE); zSelector = g.argv[3]; found = containsSelector(blob_str(&css), zSelector); fossil_print("%s %s\n", zSelector, found ? "found" : "not found"); blob_reset(&css); } /* ** WEBPAGE: script.js ** ** Return the "Javascript" content for the current skin (if there is any) */ void page_script_js(void){ const char *zScript = skin_get("js"); if( P("test") ){ /* Render the script as plain-text for testing purposes, if the "test" ** query parameter is present */ cgi_set_content_type("text/plain"); }else{ /* Default behavior is to return javascript */ cgi_set_content_type("text/javascript"); } style_init_th1_vars(0); Th_Render(zScript?zScript:""); } /* ** Check for "name" or "page" query parameters on an /style.css ** page request. If present, then page-specific CSS is requested, ** so add that CSS to pOut. If the "name" and "page" query parameters ** are omitted, then pOut is unchnaged. */ static void page_style_css_append_page_style(Blob *pOut){ const char *zPage = PD("name",P("page")); char * zFile; int nFile = 0; const char *zBuiltin; if(zPage==0 || zPage[0]==0){ return; } zFile = mprintf("style.%s.css", zPage); zBuiltin = (const char *)builtin_file(zFile, &nFile); if(nFile>0){ blob_appendf(pOut, "\n/***********************************************************\n" "** Page-specific CSS for \"%s\"\n" "***********************************************************/\n", zPage); blob_append(pOut, zBuiltin, nFile); fossil_free(zFile); return; } /* Potential TODO: check for aliases/page groups. e.g. group all ** /forumXYZ CSS into one file, all /setupXYZ into another, etc. As ** of this writing, doing so would only shave a few kb from ** default.css. */ fossil_free(zFile); } /* ** WEBPAGE: style.css loadavg-exempt ** ** Return the style sheet. The style sheet is assemblied from ** multiple sources, in order: ** ** (1) The built-in "default.css" style sheet containing basic defaults. ** ** (2) The page-specific style sheet taken from the built-in ** called "PAGENAME.css" where PAGENAME is the value of the name= ** or page= query parameters. If neither name= nor page= exist, ** then this section is a no-op. ** ** (3) The skin-specific "css.txt" file, if there one. ** ** All of (1), (2), and (3) above (or as many as exist) are concatenated. ** The result is then run through TH1 with the following variables set: ** ** * $basename ** * $secureurl ** * $home ** * $logo ** * $background ** ** The output from TH1 becomes the style sheet. Fossil always reports ** that the style sheet is cacheable. */ void page_style_css(void){ Blob css = empty_blob; int i; const char * zDefaults; const char *zSkin; cgi_set_content_type("text/css"); etag_check(0, 0); /* Emit all default rules... */ zDefaults = (const char*)builtin_file("default.css", &i); blob_append(&css, zDefaults, i); /* Page-specific CSS, if any... */ page_style_css_append_page_style(&css); zSkin = skin_in_use(); if( zSkin==0 ) zSkin = "this repository"; blob_appendf(&css, "\n/***********************************************************\n" "** Skin-specific CSS for %s\n" "***********************************************************/\n", zSkin); blob_append(&css,skin_get("css"),-1); /* Process through TH1 in order to give an opportunity to substitute ** variables such as $baseurl. */ Th_Store("baseurl", g.zBaseURL); Th_Store("secureurl", fossil_wants_https(1)? 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; } /* ** All possible capabilities */ static const char allCap[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL"; /* ** Compute the current login capabilities */ static char *find_capabilities(char *zCap){ int i, j; char c; for(i=j=0; (c = allCap[j])!=0; j++){ if( login_has_capability(&c, 1, 0) ) zCap[i++] = c; } zCap[i] = 0; return zCap; } /* ** Compute the current login capabilities that were ** contributed by Anonymous */ static char *find_anon_capabilities(char *zCap){ int i, j; char c; for(i=j=0; (c = allCap[j])!=0; j++){ if( login_has_capability(&c, 1, LOGIN_ANON) && !login_has_capability(&c, 1, 0) ) zCap[i++] = c; } zCap[i] = 0; return zCap; } /* ** WEBPAGE: test_env ** ** Display CGI-variables and other aspects of the run-time ** environment, for debugging and trouble-shooting purposes. */ void page_test_env(void){ webpage_error(""); } /* ** WEBPAGE: honeypot ** This page is a honeypot for spiders and bots. */ void honeypot_page(void){ style_header("I think you are a robot"); @ <p>You seem like a robot.</p> @ @ <p>Is this wrong? Are you really a human? If so, please prove it @ by <a href="%R/login">logging in</a>. if( g.anon.Hyperlink ){ @ You can <a href="%R/login?anon=1">log in anonymously</a> if you @ prefer. } @ <p>Sorry for the inconvenience. The point of this is to prevent @ robots from following the countless of hyperlinks in this site and @ soaking up all the available CPU time and network bandwidth. style_finish_page(); } /* ** Webpages that encounter an error due to missing or incorrect ** query parameters can jump to this routine to render an error ** message screen. ** ** For administators, or if the test_env_enable setting is true, then ** details of the request environment are displayed. Otherwise, just ** the error message is shown. ** ** If zFormat is an empty string, then this is the /test_env page. */ void webpage_error(const char *zFormat, ...){ int showAll = 0; char *zErr = 0; int isAuth = 0; char zCap[100]; login_check_credentials(); if( g.perm.Admin || g.perm.Setup || db_get_boolean("test_env_enable",0) ){ isAuth = 1; } cgi_load_environment(); style_set_current_feature(zFormat[0]==0 ? "test" : "error"); if( zFormat[0] ){ va_list ap; va_start(ap, zFormat); zErr = vmprintf(zFormat, ap); va_end(ap); style_header("Bad Request"); @ <h1>/%h(g.zPath): %h(zErr)</h1> showAll = 0; cgi_set_status(500, "Bad Request"); }else if( !isAuth ){ login_needed(0); return; }else{ style_header("Environment Test"); showAll = PB("showall"); style_submenu_checkbox("showall", "Cookies", 0, 0); style_submenu_element("Stats", "%R/stat"); } if( isAuth ){ #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> @ g.userUid = %d(g.userUid)<br> @ g.zLogin = %h(g.zLogin)<br> @ g.isHuman = %d(g.isHuman)<br> @ g.jsHref = %d(g.jsHref)<br> if( g.zLocalRoot ){ @ g.zLocalRoot = %h(g.zLocalRoot)<br> }else{ @ g.zLocalRoot = <i>none</i><br> } if( g.nRequest ){ @ g.nRequest = %d(g.nRequest)<br> } if( g.nPendingRequest>1 ){ @ g.nPendingRequest = %d(g.nPendingRequest)<br> } @ capabilities = %s(find_capabilities(zCap))<br> if( zCap[0] ){ @ anonymous-adds = %s(find_anon_capabilities(zCap))<br> } @ g.zRepositoryName = %h(g.zRepositoryName)<br> @ load_average() = %f(load_average())<br> #ifndef _WIN32 @ RSS = %.2f(fossil_rss()/1000000.0) MB</br> #endif (void)cgi_csrf_safe(2); switch( g.okCsrf ){ case 1: { @ CSRF safety = Same origin<br> break; } case 2: { @ CSRF safety = Same origin, POST<br> break; } case 3: { @ CSRF safety = Same origin, POST, CSRF token<br> break; } default: { @ CSRF safety = unsafe<br> break; } } @ fossil_exe_id() = %h(fossil_exe_id())<br> if( g.perm.Admin ){ int k; for(k=0; g.argvOrig[k]; k++){ Blob t; blob_init(&t, 0, 0); blob_append_escaped_arg(&t, g.argvOrig[k], 0); @ argv[%d(k)] = %h(blob_str(&t))<br> blob_zero(&t); } } @ <hr> P("HTTP_USER_AGENT"); P("SERVER_SOFTWARE"); cgi_print_all(showAll, 0, 0); @ <p><form method="POST" action="%R/test_env"> @ <input type="hidden" name="showall" value="%d(showAll)"> @ <input type="submit" name="post-test-button" value="POST Test"> @ </form> if( showAll && blob_size(&g.httpHeader)>0 ){ @ <hr> @ <pre> @ %h(blob_str(&g.httpHeader)) @ </pre> } } if( zErr && zErr[0] ){ style_finish_page(); cgi_reply(); fossil_exit(1); }else{ style_finish_page(); } } /* ** Generate a Not Yet Implemented error page. */ void webpage_not_yet_implemented(void){ webpage_error("Not yet implemented"); } /* ** Generate a webpage for a webpage_assert(). */ void webpage_assert_page(const char *zFile, int iLine, const char *zExpr){ fossil_warning("assertion fault at %s:%d - %s", zFile, iLine, zExpr); cgi_reset_content(); webpage_error("assertion fault at %s:%d - %s", zFile, iLine, zExpr); } /* ** Issue a 404 Not Found error for a webpage */ void webpage_notfound_error(const char *zFormat, ...){ char *zMsg; va_list ap; if( zFormat ){ va_start(ap, zFormat); zMsg = vmprintf(zFormat, ap); va_end(ap); }else{ zMsg = "Not Found"; } style_set_current_feature("enotfound"); style_header("Not Found"); @ <p>%h(zMsg)</p> cgi_set_status(404, "Not Found"); style_finish_page(); } #if INTERFACE # define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);} #endif /* ** Returns a pseudo-random input field ID, for use in associating an ** ID-less input field with a label. The memory is owned by the ** caller. */ static char * style_next_input_id(){ static int inputID = 0; ++inputID; return mprintf("input-id-%d", inputID); } /* ** Outputs a labeled checkbox element. zWrapperId is an optional ID ** value for the containing element (see below). zFieldName is the ** form element name. zLabel is the label for the checkbox. zValue is ** the optional value for the checkbox. zTip is an optional tooltip, ** which gets set as the "title" attribute of the outermost ** element. If isChecked is true, the checkbox gets the "checked" ** attribute set, else it is not. ** ** Resulting structure: ** ** <div class='input-with-label' title={{zTip}} id={{zWrapperId}}> ** <input type='checkbox' name={{zFieldName}} value={{zValue}} ** id='A RANDOM VALUE' ** {{isChecked ? " checked : ""}}/> ** <label for='ID OF THE INPUT FIELD'>{{zLabel}}</label> ** </div> ** ** zLabel, and zValue are required. zFieldName, zWrapperId, and zTip ** are may be NULL or empty. ** ** Be sure that the input-with-label CSS class is defined sensibly, in ** particular, having its display:inline-block is useful for alignment ** purposes. */ void style_labeled_checkbox(const char * zWrapperId, const char *zFieldName, const char * zLabel, const char * zValue, int isChecked, const char * zTip){ char * zLabelID = style_next_input_id(); CX("<div class='input-with-label'"); if(zTip && *zTip){ CX(" title='%h'", zTip); } if(zWrapperId && *zWrapperId){ CX(" id='%s'",zWrapperId); } CX("><input type='checkbox' id='%s' ", zLabelID); if(zFieldName && *zFieldName){ CX("name='%s' ",zFieldName); } CX("value='%T'%s/>", zValue ? zValue : "", isChecked ? " checked" : ""); CX("<label for='%s'>%h</label></div>", zLabelID, zLabel); fossil_free(zLabelID); } /* ** Outputs a SELECT list from a compile-time list of integers. ** The vargs must be a list of (const char *, int) pairs, terminated ** with a single NULL. Each pair is interpreted as... ** ** If the (const char *) is NULL, it is the end of the list, else ** a new OPTION entry is created. If the string is empty, the ** label and value of the OPTION is the integer part of the pair. ** If the string is not empty, it becomes the label and the integer ** the value. If that value == selectedValue then that OPTION ** element gets the 'selected' attribute. ** ** Note that the pairs are not in (int, const char *) order because ** there is no well-known integer value which we can definitively use ** as a list terminator. ** ** zWrapperId is an optional ID value for the containing element (see ** below). ** ** zFieldName is the value of the form element's name attribute. Note ** that fossil prefers underscores over '-' for separators in form ** element names. ** ** zLabel is an optional string to use as a "label" for the element ** (see below). ** ** zTooltip is an optional value for the SELECT's title attribute. ** ** The structure of the emitted HTML is: ** ** <div class='input-with-label' title={{zToolTip}} id={{zWrapperId}}> ** <label for='SELECT ELEMENT ID'>{{zLabel}}</label> ** <select id='RANDOM ID' name={{zFieldName}}>...</select> ** </div> ** ** Example: ** ** style_select_list_int("my-grapes", "my_grapes", "Grapes", ** "Select the number of grapes", ** atoi(PD("my_field","0")), ** "", 1, "2", 2, "Three", 3, ** NULL); ** */ void style_select_list_int(const char * zWrapperId, const char *zFieldName, const char * zLabel, const char * zToolTip, int selectedVal, ... ){ char * zLabelID = style_next_input_id(); va_list vargs; va_start(vargs,selectedVal); CX("<div class='input-with-label'"); if(zToolTip && *zToolTip){ CX(" title='%h'",zToolTip); } if(zWrapperId && *zWrapperId){ CX(" id='%s'",zWrapperId); } CX(">"); if(zLabel && *zLabel){ CX("<label for='%s'>%h</label>", zLabelID, zLabel); } CX("<select name='%s' id='%s'>",zFieldName, zLabelID); while(1){ const char * zOption = va_arg(vargs,char *); int v; if(NULL==zOption){ break; } v = va_arg(vargs,int); CX("<option value='%d'%s>", v, v==selectedVal ? " selected" : ""); if(*zOption){ CX("%s", zOption); }else{ CX("%d",v); } CX("</option>\n"); } CX("</select>\n"); CX("</div>\n"); va_end(vargs); fossil_free(zLabelID); } /* ** The C-string counterpart of style_select_list_int(), this variant ** differs only in that its variadic arguments are C-strings in pairs ** of (optionLabel, optionValue). If a given optionLabel is an empty ** string, the corresponding optionValue is used as its label. If any ** given value matches zSelectedVal, that option gets preselected. If ** no options match zSelectedVal then the first entry is selected by ** default. ** ** Any of (zWrapperId, zTooltip, zSelectedVal) may be NULL or empty. ** ** Example: ** ** style_select_list_str("my-grapes", "my_grapes", "Grapes", ** "Select the number of grapes", ** P("my_field"), ** "1", "One", "2", "Two", "", "3", ** NULL); */ void style_select_list_str(const char * zWrapperId, const char *zFieldName, const char * zLabel, const char * zToolTip, char const * zSelectedVal, ... ){ char * zLabelID = style_next_input_id(); va_list vargs; va_start(vargs,zSelectedVal); if(!zSelectedVal){ zSelectedVal = __FILE__/*some string we'll never match*/; } CX("<div class='input-with-label'"); if(zToolTip && *zToolTip){ CX(" title='%h'",zToolTip); } if(zWrapperId && *zWrapperId){ CX(" id='%s'",zWrapperId); } CX(">"); if(zLabel && *zLabel){ CX("<label for='%s'>%h</label>", zLabelID, zLabel); } CX("<select name='%s' id='%s'>",zFieldName, zLabelID); while(1){ const char * zLabel = va_arg(vargs,char *); const char * zVal; if(NULL==zLabel){ break; } zVal = va_arg(vargs,char *); CX("<option value='%T'%s>", zVal, 0==fossil_strcmp(zVal, zSelectedVal) ? " selected" : ""); if(*zLabel){ CX("%s", zLabel); }else{ CX("%h",zVal); } CX("</option>\n"); } CX("</select>\n"); CX("</div>\n"); va_end(vargs); fossil_free(zLabelID); } /* ** Generate a <script> with an appropriate nonce. ** ** zOrigin and iLine are the source code filename and line number ** that generated this request. */ void style_script_begin(const char *zOrigin, int iLine){ const char *z; for(z=zOrigin; z[0]!=0; z++){ if( z[0]=='/' || z[0]=='\\' ){ zOrigin = z+1; } } CX("<script nonce='%s'>/* %s:%d */\n", style_nonce(), zOrigin, iLine); } /* Generate the closing </script> tag */ void style_script_end(void){ CX("</script>\n"); } /* ** Emits a NOSCRIPT tag with an error message stating that JS is ** required for the current page. This "should" be called near the top ** of pages which *require* JS. The inner DIV has the CSS class ** 'error' and can be styled via a (noscript > .error) CSS selector. */ void style_emit_noscript_for_js_page(void){ CX("<noscript><div class='error'>" "This page requires JavaScript (ES2015, a.k.a. ES6, or newer)." "</div></noscript>"); } |
Added src/style.chat.css.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* Chat-related */ body.chat span.at-name { /* for @USERNAME references */ text-decoration: underline; font-weight: bold; } /* A wrapper for a single single chat message (one row of the UI) */ body.chat .message-widget { margin-bottom: 0.75em; border: none; display: flex; flex-direction: column; border: none; align-items: flex-start; } body.chat button, body.chat input[type=button] { line-height: inherit/*undo skin-specific funkiness*/; } body.chat .message-widget:last-of-type { /* Latest message: reduce bottom gap */ margin-bottom: 0.1em; } body.chat.my-messages-right .message-widget.mine { /* Right-aligns a user's own chat messages, similar to how most/some mobile messaging apps do it. */ align-items: flex-end; } body.chat.my-messages-right .message-widget.notification { /* Center-aligns a system-level notification message. */ align-items: center; } /* The content area of a message. */ body.chat .message-widget-content { border-radius: 0.25em; border: 1px solid rgba(0,0,0,0.2); box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29); padding: 0.25em 0.5em; margin-top: 0; min-width: 9em /*avoid unsightly "underlap" with the neighboring .message-widget-tab element*/; white-space: normal; word-break: break-word /* so that full hashes wrap on narrow screens */; } body.chat .message-widget-content.wide { /* Special case for when embedding content which we really want to expand, namely iframes. */ width: 98%; } body.chat .message-widget-content label[for] { margin-left: 0.25em; cursor: pointer; } body.chat .message-widget-content > .attachment-link { display: flex; flex-direction: row; } body.chat .message-widget-content > .attachment-link > a { margin-right: 1em; } body.chat .message-widget-content > iframe { width: 100%; max-width: 100%; resize: both; } body.chat .message-widget-content> a { /* Cosmetic: keep skin-induced on-hover underlining from shifting content placed below this. */ border-bottom: 1px transparent; } body.chat.monospace-messages .message-widget-content, body.chat.monospace-messages .chat-input-field{ font-family: monospace; } body.chat .message-widget-content > * { margin: 0; padding: 0; } body.chat .message-widget-content > pre { white-space: pre-wrap; } body.chat .message-widget-content > .markdown > *:first-child { margin-top: 0; } body.chat .message-widget-content > .markdown > *:last-child { margin-bottom: 0; } body.chat .message-widget-content.error .buttons { display: flex; flex-direction: row; justify-content: space-around; flex-wrap: wrap; } body.chat .message-widget-content.error .buttons > button { margin: 0.25em; } body.chat .message-widget-content.error a { color: inherit; } body.chat .message-widget-content.error .failed-message { display: flex; flex-direction: column; } body.chat .message-widget-content.error .failed-message textarea { min-height: 5rem; } /* User name and timestamp (a LEGEND-like element) */ body.chat .message-widget .message-widget-tab { border-radius: 0.25em 0.25em 0 0; margin: 0 0.25em 0em 0.15em; padding: 0 0.5em 0.15em 0.5em; cursor: pointer; white-space: nowrap; } body.chat .fossil-tooltip.help-buttonlet-content { font-size: 80%; } body.chat .message-widget .message-widget-tab .xfrom { /* Element which holds the "this message is from user X" part of the message banner. */ font-style: italic; font-weight: bold; } /* The popup element for displaying message timestamps and deletion controls. */ body.chat .chat-message-popup { font-family: monospace; font-size: 0.9em; text-align: left; display: flex; flex-direction: column; align-items: stretch; padding: 0.25em; margin-top: 0.25em; border: 1px outset; border-radius: 0.5em; } /* Full message timestamps. */ body.chat .chat-message-popup > span { white-space: nowrap; } /* Container for the message deletion buttons. */ body.chat .chat-message-popup > .toolbar { padding: 0; margin: 0; border: 2px inset rgba(0,0,0,0.3); border-radius: 0.25em; display: flex; flex-direction: row; justify-content: stretch; flex-wrap: wrap; align-items: center; } body.chat .chat-message-popup > .toolbar > * { margin: 0.35em; } body.chat .chat-message-popup > .toolbar > button { flex: 1 1 auto; } /* The widget for loading more/older chat messages. */ body.chat #load-msg-toolbar { border-radius: 0.25em; padding: 0.1em 0.2em; margin-bottom: 1em; } /* .all-done is set when chat has loaded all of the available historical messages */ body.chat #load-msg-toolbar.all-done { opacity: 0.5; } body.chat #load-msg-toolbar > div { display: flex; flex-direction: row; justify-content: stretch; flex-wrap: wrap; } body.chat #load-msg-toolbar > div > button { flex: 1 1 auto; } /* "Chat-only mode" hides the site header/footer, showing only the chat app. */ body.chat.chat-only-mode{ padding: 0; margin: 0 auto; } body.chat #chat-button-settings {} /** Popup widget for the /chat settings. */ body.chat .chat-settings-popup { font-size: 0.8em; text-align: left; display: flex; flex-direction: column; align-items: stretch; padding: 0.25em; z-index: 200; } /** Container for the list of /chat messages. */ body.chat #chat-messages-wrapper { overflow: auto; padding: 0 0.25em; } body.chat #chat-messages-wrapper.loading > * { /* An attempt at reducing flicker when loading lots of messages. */ visibility: hidden; } body.chat div.content { margin: 0; padding: 0; display: flex; flex-direction: column-reverse; /* ^^^^ In order to get good automatic scrolling of new messages on the BOTTOM in bottom-up chat mode, such that they scroll up instead of down, we have to use column-reverse layout, which changes #chat-messages-wrapper's "gravity" for purposes of scrolling! If we instead use flex-direction:column then each new message pushes #chat-input-area down further off the screen! */ align-items: stretch; } /* Wrapper for /chat user input controls */ body.chat #chat-input-area { display: flex; flex-direction: column; padding: 0; margin: 0; flex: 0 1 auto; } body.chat:not(.chat-only-mode) #chat-input-area{ /* Safari user reports that 2em is necessary to keep the file selection widget from overlapping the page footer, whereas a margin of 0 is fine for FF/Chrome (and 2em is a *huge* waste of space for those). */ margin-bottom: 0; } .chat-input-field { flex: 10 1 auto; margin: 0; } #chat-input-field-x, #chat-input-field-multi { overflow: auto; resize: vertical; } #chat-input-field-x { display: inline-block/*supposed workaround for Chrome weirdness*/; padding: 0.2em; background-color: rgba(156,156,156,0.3); white-space: pre-wrap; /* ^^^ Firefox, when pasting plain text into a contenteditable field, loses all newlines unless we explicitly set this. Chrome does not. */ cursor: text; /* ^^^ In some browsers the cursor may not change for a contenteditable element until it has focus, causing potential confusion. */ } #chat-input-field-x:empty::before { content: attr(data-placeholder); opacity: 0.6; } .chat-input-field:not(:focus){ border-width: 1px; border-style: solid; border-radius: 0.25em; } .chat-input-field:focus{ /* This transparent border helps avoid the text shifting around when the contenteditable attribute causes a border (which we apparently cannot style) to be added. */ border-width: 1px; border-style: solid; border-color: transparent; border-radius: 0.25em; } /* Widget holding the chat message input field, send button, and settings button. */ body.chat #chat-input-line-wrapper { display: flex; flex-direction: row; align-items: stretch; flex-wrap: nowrap; } body.chat.chat-only-mode #chat-input-line-wrapper { padding: 0 0.25em; } /*body.chat #chat-input-line-wrapper:not(.compact) { flex-wrap: nowrap; }*/ body.chat #chat-input-line-wrapper.compact { /* "The problem" with wrapping, together with a contenteditable input field, is that the latter grows as the user types, so causes wrapping to happen while they type, then to unwrap as soon as the input field is cleared (when the message is sent). When we stay wrapped in compact mode, the wrapped buttons simply take up too much space. */ /*flex-wrap: wrap; justify-content: flex-end;*/ flex-direction: column; /** We "really do" need column orientation here because it's the only way to eliminate the possibility that (A) the buttons get truncated in very narrow windows and (B) that they keep stable positions. */ } body.chat #chat-input-line-wrapper.compact #chat-input-field-x { } body.chat #chat-buttons-wrapper { flex: 0 1 auto; display: flex; flex-direction: column; align-items: center; min-width: 4em; min-height: 1.5em; align-self: flex-end /*keep buttons stable at bottom/right even when input field resizes */; } body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper { flex-direction: row; flex: 1 1 auto; align-self: stretch; justify-content: flex-end; /*flex-wrap: wrap;*/ /* Wrapping would be ideal except that the edit widget grows in width as the user types, moving the buttons around */ } body.chat #chat-buttons-wrapper > .cbutton { padding: 0; display: inline-block; border-width: 1px; border-style: solid; border-radius: 0.25em; min-width: 4ex; max-width: 4ex; min-height: 3ex; max-height: 3ex; margin: 0.125em; display: inline-flex; justify-content: center; align-items: center; cursor: pointer; font-size: 130%; } body.chat #chat-buttons-wrapper > .cbutton:hover { background-color: rgba(200,200,200,0.3); } body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper > .cbutton { margin: 2px 0.125em 0 0.125em; min-width: 6ex; max-width: 6ex; min-height: 2.3ex; max-height: 2.3ex; font-size: 120%; } body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper #chat-button-submit { min-width: 12ex; } .chat-input-field { font-family: inherit } body.chat #chat-input-line-wrapper:not(.compact) #chat-input-field-multi, body.chat #chat-input-line-wrapper:not(.compact) #chat-input-field-x { min-height: 4rem; /* Problems related to max-height: - If we do NOT set a max-height then pasting/typing a large amount of text can cause this element to grow without bounds, larger than the window, and there's no way to navigate it sensibly. In this case, manually resizing the element (desktop only - mobile doesn't offer that) will force it to stay at the selected size even if more content is added to it later. - If we DO set a max-height then its growth is bounded but it also cannot manually expanded by the user. The lesser of the two evils seems to be to rely on the browser feature that a manual resize of the element will pin its size. */ } body.chat #chat-input-line-wrapper > #chat-button-settings{ margin: 0 0 0 0.25em; max-width: 2em; } body.chat #chat-input-line-wrapper > input[type=text], body.chat #chat-input-line-wrapper > textarea { flex: 20 1 auto; max-width: revert; min-width: 20em; } body.chat #chat-input-line-wrapper.compact > input[type=text] { margin: 0 0 0.25em 0/* gap for if/when buttons wrap*/; } /* Widget holding the file selection control and preview */ body.chat #chat-input-file-area { display: flex; flex-direction: row; margin: 0; } body.chat #chat-input-file-area > .file-selection-wrapper { align-self: flex-start; margin-right: 0.5em; flex: 0 1 auto; padding: 0.25em 0.5em; white-space: nowrap; } body.chat #chat-input-file { border:1px solid rgba(0,0,0,0);/*avoid UI shift during drop-targeting*/ border-radius: 0.25em; padding: 0.25em; } body.chat #chat-input-file > input { flex: 1 0 auto; } /* Indicator when a drag/drop is in progress */ body.chat #chat-input-file.dragover { border: 1px dashed green; } /* Widget holding the details of a selected/dropped file/image. */ body.chat #chat-drop-details { padding: 0 1em; white-space: pre; font-family: monospace; margin: auto; flex: 0; } body.chat #chat-drop-details:empty { padding: 0; margin: 0; } body.chat #chat-drop-details img { max-width: 45%; max-height: 45%; } body.chat .chat-view { flex: 20 1 auto /*ensure that these grow more than the non-.chat-view elements. Note that setting flex shrink to 0 breaks/disables scrolling!*/; margin-bottom: 0.2em; } body.chat #chat-config, body.chat #chat-preview { /* /chat configuration widget */ display: flex; flex-direction: column; overflow: auto; padding: 0; margin: 0; align-items: stretch; min-height: 6em; } body.chat #chat-config #chat-config-options { /* /chat config options go here */ flex: 1 1 auto; display: flex; flex-direction: column; overflow: auto; align-items: stretch; } body.chat #chat-config #chat-config-options .menu-entry { display: flex; align-items: center; flex-direction: row-reverse; flex-wrap: nowrap; padding: 1em; flex: 1 1 auto; align-self: stretch; } body.chat #chat-config #chat-config-options .menu-entry.parent{ border-radius: 1em 1em 0 1em; margin-top: 1em; } body.chat #chat-config #chat-config-options .menu-entry.child { /*padding-left: 2.5em;*/ margin-left: 2em; } body.chat #chat-config #chat-config-options .menu-entry:nth-of-type(even){ background-color: rgba(175,175,175,0.15); } body.chat #chat-config #chat-config-options .menu-entry:nth-of-type(odd){ background-color: rgba(175,175,175,0.35); } body.chat #chat-config #chat-config-options .menu-entry:first-child { /* Config list header */ border-radius: 0 0 1em 1em; } body.chat #chat-config #chat-config-options .menu-entry:first-child .label-wrapper { align-items: start; } body.chat #chat-config #chat-config-options .menu-entry > .toggle-wrapper { /* Holder for a checkbox, if any */ min-width: 1.5rem; margin-right: 1rem; } body.chat #chat-config #chat-config-options .menu-entry .label-wrapper { /* Wrapper for a LABEL and a .hint element. */ display: flex; flex-direction: column; align-self: baseline; flex: 1 1 auto; } body.chat #chat-config #chat-config-options .menu-entry label { /* Config option label. */ font-weight: bold; white-space: initial; } body.chat #chat-config #chat-config-options .menu-entry label[for] { cursor: pointer; } body.chat #chat-config #chat-config-options .menu-entry .hint { /* Config menu hint text */ font-size: 85%; font-weight: normal; white-space: pre-wrap; display: inline-block; opacity: 0.85; } body.chat #chat-config #chat-config-options .menu-entry select { } body.chat #chat-preview #chat-preview-content { overflow: auto; flex: 1 1 auto; padding: 0.5em; border: 1px dotted; } body.chat #chat-preview #chat-preview-content > * { margin: 0; padding: 0; } body.chat #chat-preview #chat-preview-buttons { flex: 0 1 auto; display: flex; flex-direction: column; } body.chat #chat-config > button, body.chat #chat-preview #chat-preview-buttons > button { padding: 0.5em; flex: 0 1 auto; margin: 0.25em 0; } body.chat #chat-user-list-wrapper { /* Safari can't do fieldsets right, so we emulate one. */ border-radius: 0.5em; margin: 1em 0 0.2em 0; padding: 0 0.5em; border-style: inset; border-width: 0 1px 1px 1px/*else collides with the LEGEND*/; } body.chat #chat-user-list-wrapper.collapsed { padding: 0; } body.chat #chat-user-list-wrapper > .legend { font-weight: initial; padding: 0 0.5em 0 0.5em; position: relative; top: -1.75ex/* place it like a fieldset legend */; cursor: pointer; } body.chat #chat-user-list-wrapper > .legend > * { vertical-align: middle; } body.chat #chat-user-list-wrapper > .legend > *:nth-child(2){ /* Title label */ opacity: 0.6; font-size: 0.8em; } body.chat #chat-user-list-wrapper.collapsed > .legend > *:nth-child(2)::after { content: " (tap to toggle)"; } body.chat #chat-user-list-wrapper .help-buttonlet { margin: 0; } body.chat #chat-user-list-wrapper.collapsed #chat-user-list { position: absolute !important; opacity: 0 !important; pointer-events: none !important; display: none !important; } body.chat #chat-user-list { margin-top: -1.25ex; display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; } body.chat #chat-user-list .chat-user { margin: 0.2em; padding: 0.1em 0.5em 0.2em 0.5em; border-radius: 0.5em; cursor: pointer; text-align: center; white-space: pre; } body.chat #chat-user-list .timestamp { font-size: 85%; font-family: monospace; } body.chat #chat-user-list:not(.timestamps) .timestamp { display: none; } body.chat #chat-user-list .chat-user.selected { font-weight: bold; text-decoration: underline; } body.chat.fossil-dark-style #chat-button-attach > svg { /* The black paperclip is barely visible in dark-mode skins when they have dark buttons */ filter: invert(0.8); } body.chat .anim-rotate-360 { animation: rotate-360 750ms linear; } @keyframes rotate-360 { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } body.chat .anim-flip-h { animation: flip-h 750ms linear; } @keyframes flip-h{ from { transform: rotateY(0deg); } to { transform: rotateY(360deg); } } body.chat .anim-flip-v { animation: flip-v 750ms linear; } @keyframes flip-v{ from { transform: rotateX(0deg); } to { transform: rotateX(360deg); } } body.chat .anim-fade-in { animation: fade-in 750ms linear; } body.chat .anim-fade-in-fast { animation: fade-in 350ms linear; } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } body.chat .anim-fade-out-fast { animation: fade-out 250ms linear; } @keyframes fade-out { from { opacity: 1; } to { opacity: 0; } } |
Added src/style.fileedit.css.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /** Styles specific to /fileedit... */ body.fileedit.waiting * { /* Triggered during AJAX requests. */ cursor: wait; } body.fileedit .error { padding: 0.25em; } body.fileedit .warning { padding: 0.25em; } body.fileedit textarea { font-family: monospace; flex: 10 1 auto; height: initial/*undo damage from some skins*/; max-width: initial /* default.css pins it at 95% */; } body.fileedit textarea:focus, body.fileedit input:focus{ /* Depending on the skin, it might be useful to add one or both of the following... */ /*border-width: 1px;*/ /*border: initial; */ } body.fileedit fieldset:not(.tab-wrapper) { margin: 0.5em 0 0.5em 0; padding: 0.25em 0; border-radius: 0.5em; border-color: inherit; border-width: 1px; font-size: 90%; overflow: auto; } body.fileedit fieldset > legend { margin: 0 0 0 1em; padding: 0 0.5em 0 0.5em; } body.fileedit fieldset > div { margin: 0 0.25em 0 0.25em; padding: 0; overflow: auto; } body.fileedit fieldset > div > .input-with-label { margin: 0.25em 0.5em; } body.fileedit fieldset > div > button { margin: 0.25em 0.5em; } body.fileedit .fileedit-hint { font-size: 80%; opacity: 0.75; } body.fileedit .fileedit-error-report { background: yellow; color: darkred; margin: 1em 0; padding: 0.5em; border-radius: 0.5em; } body.fileedit code.fileedit-manifest { display: block; height: 16em; overflow: auto; white-space: pre; } body.fileedit div.fileedit-preview { margin: 0; padding: 0; } body.fileedit #fileedit-tabs { margin: 0.5em 0 0 0; } body.fileedit #fileedit-tab-preview-wrapper { overflow: auto; } body.fileedit #fileedit-tab-preview-wrapper > pre { margin: 0; } body.fileedit #fileedit-tab-fileselect > h1 { margin: 0; } body.fileedit .fileedit-options > div > * { margin: 0.25em; } body.fileedit .fileedit-options.commit-message > div { display: flex; flex-direction: column; align-items: stretch; font-family: monospace; } body.fileedit .fileedit-options.commit-message > div > * { margin: 0.25em; } body.fileedit #fileedit-commit-button-wrapper { margin: 0.25em; } body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options { margin-top: 0; border: none; border-radius: 0; border-bottom-width: 1px; border-bottom-style: dotted; } body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > button { vertical-align: middle; margin: 0.5em; } body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > input { vertical-align: middle; margin: 0.5em; } body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > .input-with-label { margin: 0 0.5em 0.25em 0.5em; } body.fileedit .fileedit-options > div > * { margin: 0.25em; } body.fileedit .fileedit-options.flex-container.flex-row { align-items: first baseline; } body.fileedit #fileedit-file-selector { display: flex; flex-direction: column; align-content: flex-start; padding: 0 0.25em; margin: 0; min-height: 12em; } body.fileedit #fileedit-file-selector select { margin: 0 0 0.5em 0; height: initial; font-family: monospace; border: initial; } body.fileedit select:focus { border: initial; } body.fileedit #fileedit-file-selector select option { margin: 0 0 0.5em 0.55em; } body.fileedit select option, body.fileedit select option:focus { border: none; } body.fileedit #fileedit-file-selector > div { padding: 0; margin: 0; } body.fileedit #fileedit-file-selector > div > * { margin: 0.25em 0.5em 0.25em 0; } body.fileedit #fileedit-stash-selector { margin: 0.25em; display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; } body.fileedit #fileedit-stash-selector select { margin: 0 1em; height: initial; font-family: monospace; flex: 1 1 auto; } body.fileedit .tab-container > .tabs > .tab-panel { display: flex; flex-direction: column; } body.fileedit #fileedit-tab-diff-wrapper { margin: 0; padding: 0; /*overflow: hidden;*/ /* ^^^ we "really" want hidden and let a sub-sub-child element handle that, but that isn't working, for unknown reasons. */ overflow-x: auto; /*display: flex; flex-direction: column; align-items: stretch;*/ } body.fileedit #fileedit-tab-diff-wrapper > div { margin: 0.5em 0 0.5em 0; overflow-wrap: break-word; } body.fileedit table.sbsdiffcols { /*width: initial;*/ } body.fileedit #fileedit-tab-diff-wrapper > pre.udiff { margin-top: 0; } body.fileedit .sbsdiffcols div.difftxtcol { display: flex; flex-direction: column; align-items: stretch; width: initial; } body.fileedit .sbsdiffcols div.difftxtcol pre { max-width: 44em; } body.fileedit #fileedit-edit-status { border-radius: 0.25em 0.25em 0 0; margin: 0; padding: 0; width: 100%; cursor: initial; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; font-family: monospace; } body.fileedit #fileedit-edit-status > span.name > a { display: block; word-break: break-word /* needed for long paths */; } body.fileedit #fileedit-edit-status > span.links { display: flex; flex-wrap: wrap; flex-direction: row; } body.fileedit #fileedit-file-selector span.is-new, body.fileedit #fileedit-file-selector span.is-modified { font-family: monospace; } body.fileedit #fileedit-edit-status span.links > * { margin: 0 0.25em; white-space: nowrap; } body.fileedit #fileedit-edit-status span.links > *::before { content: "["; } body.fileedit #fileedit-edit-status span.links > *::after { content: "]"; } /* JS selection of line numbers cannot work in preview mode, so disable the UI indications which imply that it does something... */ body.fileedit table.numbered-lines td.line-numbers > span { cursor: unset; } body.fileedit table.numbered-lines td.line-numbers > span:hover { background-color: inherit; } |
Added src/style.pikchrshow.css.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* CSS for the WASM /pikchrshow app. */ /* emcscript-related styling, used during the module load/initialization processes... */ .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; } div.emscripten { text-align: center; } div.emscripten_border { border: 1px solid black; } #module-spinner { overflow: visible; } #module-spinner > * { margin-top: 1em; } .spinner { height: 50px; width: 50px; margin: 0px auto; animation: rotation 0.8s linear infinite; border-left: 10px solid rgb(0,150,240); border-right: 10px solid rgb(0,150,240); border-bottom: 10px solid rgb(0,150,240); border-top: 10px solid rgb(100,0,200); border-radius: 100%; background-color: rgb(200,100,250); } @keyframes rotation { from {transform: rotate(0deg);} to {transform: rotate(360deg);} } /* The following styles are for app-level use. */ /* TODO: consolidate the WASM- and legacy-mode CSS into this file. Since both versions of /pikchrshow share a URI, they both load this file. */ textarea { font-family: monospace; flex: 1 1 auto; } #main-wrapper { display: flex; flex-direction: column-reverse; flex: 1 1 auto; margin: 0.5em 0; overflow: hidden; } #main-wrapper.side-by-side { flex-direction: row; } #main-wrapper.side-by-side > fieldset { margin-left: 0.25em; margin-right: 0.25em; } #main-wrapper:not(.side-by-side) > fieldset { margin-bottom: 0.25em; } #main-wrapper.swapio { flex-direction: column; } #main-wrapper.side-by-side.swapio { flex-direction: row-reverse; } .zone-wrapper{ display: flex; margin: 0; flex: 1 1 0%; border-radius: 0.5em; min-width: inherit/*important: resolves inability to scroll fieldset child element!*/; padding: 0.35em 0 0 0; } .zone-wrapper > div { display:flex; flex: 1 1 0%; } .zone-wrapper.output { } #main-wrapper.side-by-side .zone-wrapper { /* Keep the layout from "flopping around" when render-while-typing mode is on. */ /*max-width: 50%;*/ } #pikchr-output { padding: 0; margin: 0 auto/*auto resolves a weird left-shift truncation of the SVG*/; } #pikchr-output-wrapper { flex: 1 1 auto; overflow: auto; } #pikchr-output-wrapper.text { display: flex; align-items: stretch; } #pikchr-output-wrapper.text > #pikchr-output { display: flex; align-items: stretch; flex: 1 1 auto; } #pikchr-output-wrapper.text > #pikchr-output > textarea { flex: 1 1 auto; } fieldset > legend { font-size: 85%; } .zone-wrapper textarea { border-radius: 0.5em; flex: 1 1 auto; /*min/max width resolve an inexplicable margin on the RHS. The -1em is for the padding, else we overlap the parent boundaries.*/ min-width: calc(100% - 1em); max-width: calc(100% - 1em); padding: 0 0.5em; } .zone-wrapper.input { min-height: 10em; /*width: 50%;*/ min-width: 20em; } .zone-wrapper textarea { border: 0; outline: 0; } .zone-wrapper.output { overflow: auto; justify-content: space-between; } .button-bar { display: flex; flex-wrap: wrap; align-items: center; align-content: space-between; justify-content: flex-start; } .button-bar > * { margin: 0.05em 0.5em 0.05em 0; flex: 0 1 auto; align-self: auto; } fieldset.options { border-radius: 0.5em; border: 1px inset; display: flex; flex-direction: column; padding: 0.25em; } fieldset.options > div { display: flex; flex-wrap: wrap; font-size: 85%; } fieldset.collapsible.options > legend > .fieldset-toggle::after { content: " [hide]"; position: relative; } fieldset.collapsible.collapsed > legend > .fieldset-toggle::after { content: " [show]"; position: relative; } span.labeled-input { padding: 0.25em; margin: 0.25em 0.5em; border-radius: 0.25em; white-space: nowrap; background: #0002; display: flex; align-items: center; } span.labeled-input > *:nth-child(2) { margin-left: 0.3em; } .center { text-align: center; } .app-view { flex: 20 1 auto; } #titlebar { display: flex; justify-content: space-between; margin-bottom: 0.5em; } #view-split { display: flex; flex-direction: column-reverse; } |
Added src/style.wikiedit.css.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | body.wikieedit.waiting * { /* Triggered during AJAX requests. */ cursor: wait; } body.wikiedit textarea, body.wikiedit textarea:focus, body.wikiedit input, body.wikiedit input:focus, body.wikiedit select, body.wikiedit select:focus{ /* Depending on the skin, it might be useful to add one or both of the following... */ /*border-width: 1px;*/ /*border: initial; */ } body.wikiedit div.wikiedit-preview { margin: 0; padding: 0; } body.wikiedit #wikiedit-tabs { margin: 0.5em 0 0 0; } body.wikiedit #wikiedit-tab-preview-wrapper { overflow: auto; } body.wikiedit #wikiedit-tab-diff-wrapper { /*overflow: hidden;*/ /* ^^^ we "really" want hidden and let a sub-sub-child element handle that, but that isn't working, for unknown reasons. */ overflow-x: auto; } body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options { margin-top: 0; border: none; border-radius: 0; border-bottom-width: 1px; border-bottom-style: dotted; } body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > button { vertical-align: middle; margin: 0.5em; } body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input { vertical-align: middle; margin: 0.5em; } body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > .input-with-label { margin: 0 0.5em 0.25em 0.5em; } body.wikiedit label { display: inline; /* some skins set label display to block! */ } body.wikiedit .wikiedit-options > div > * { margin: 0.25em; } body.wikiedit .wikiedit-options.flex-container.flex-row { align-items: first baseline; } body.wikiedit .WikiList { display: flex; flex-direction: column; align-items: start; } body.wikiedit .WikiList select { font-size: 110%; margin: initial; height: initial /* some skins set these to a fixed height */; font-family: monospace; border: initial; } body.wikiedit select:focus { border: initial; } body.wikiedit .WikiList select option { margin: 0 0 0.5em 0.55em; } body.wikiedit select option, body.wikiedit select option:focus { border: none; } body.wikiedit .WikiList select option.stashed, body.wikiedit .WikiList select option.stashed-new, body.wikiedit .WikiList select option.deleted { margin-left: -0.4em; } body.wikiedit .WikiList.hide-deleted select option.deleted { display: none; } body.wikiedit textarea { max-width: initial; } body.wikiedit .tabs .tab-panel { /* Needed for wide diffs */ overflow: auto; } body.wikiedit .WikiList fieldset { padding: 0.25em; border-width: 1px /* Ardoise skin sets this to 0 */; min-width: 6em; border-style: inset; } body.wikiedit .WikiList label { margin: 0 0.5em; vertical-align: text-bottom; } body.wikiedit .WikiList legend { margin: 0 0 0 0.5em; } body.wikiedit .WikiList > fieldset { margin: 0; width: calc(100% - 1em); } body.wikiedit .WikiList fieldset > :not(legend) { /* Stretch page selection list when it's empty or only has short page names */ margin: 0; width: 100%; } body.wikiedit .WikiList .fieldset-wrapper { /* Container for the filter and edit status fieldsets */ display: flex; flex-direction: row; flex-wrap: wrap; align-items: stretch; justify-content: stretch; margin: 0; } body.wikiedit .WikiList button.save { margin: 1em 0 0 0; } body.wikiedit .WikiList .new-page { align-items: flex-start; max-width: 15em; } body.wikiedit .WikiList .new-page input { } body.wikiedit #wikiedit-tab-misc h3 { margin: 0; } body.wikiedit span.mini-tip { font-size: 80%; } body.wikiedit span.save-button-slot { /* These invisible placeholders mark spots in the UI (max. 1 per tab) to where the save button gets relocated as we switch between tabs. */ display: none; } body.wikiedit #wikiedit-edit-status { border-radius: 0.25em 0.25em 0 0; margin: 0; padding: 0; width: 100%; cursor: initial; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; font-family: monospace; } body.wikiedit #wikiedit-edit-status > span.name { display: block; word-break: break-word /* needed for long names, e.g. checkin/... */; } body.wikiedit #wikiedit-edit-status > span.links { display: flex; flex-wrap: wrap; flex-direction: row; } body.wikiedit .WikiList span.is-new, body.wikiedit .WikiList span.is-modified, body.wikiedit .WikiList span.is-deleted { font-family: monospace; } body.wikiedit #wikiedit-edit-status span.links > a { margin: 0 0.25em; white-space: nowrap; } body.wikiedit #wikiedit-edit-status span.links > a::before { content: "["; } body.wikiedit #wikiedit-edit-status span.links > a::after { content: "]"; } body.wikiedit #wikiedit-stash-selector { margin: 0.25em; display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; } body.wikiedit #wikiedit-stash-selector select { margin: 0 1em 0 0.5em; height: initial; font-family: monospace; flex: 1 1 auto; } body.wikiedit fieldset.page-types-list > div > span { display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center; } |
Changes to src/sync.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 | ** ** This file contains code used to push, pull, and sync a repository */ #include "config.h" #include "sync.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 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 | ** ** This file contains code used to push, pull, and sync a repository */ #include "config.h" #include "sync.h" #include <assert.h> /* ** Explain what type of sync operation is about to occur */ static void sync_explain(unsigned syncFlags){ if( g.url.isAlias ){ const char *url; if( g.url.useProxy ){ url = g.url.proxyUrlCanonical; }else{ url = g.url.canonical; } if( (syncFlags & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){ fossil_print("Sync with %s\n", url); }else if( syncFlags & SYNC_PUSH ){ fossil_print("Push to %s\n", url); }else if( syncFlags & SYNC_PULL ){ fossil_print("Pull from %s\n", url); } } } /* ** Call client_sync() one or more times in order to complete a ** sync operation. Usually, client_sync() is called only once, though ** is can be called multiple times if the SYNC_ALLURL flags is set. */ static int client_sync_all_urls( unsigned syncFlags, /* Mask of SYNC_* flags */ unsigned configRcvMask, /* Receive these configuration items */ unsigned configSendMask, /* Send these configuration items */ const char *zAltPCode /* Alternative project code (usually NULL) */ ){ int nErr = 0; /* Number of errors seen */ int nOther; /* Number of extra remote URLs */ char **azOther; /* Text of extra remote URLs */ int i; /* Loop counter */ int iEnd; /* Loop termination point */ int nextIEnd; /* Loop termination point for next pass */ int iPass; /* Which pass through the remotes. 0 or 1 */ int nPass; /* Number of passes to make. 1 or 2 */ Stmt q; /* An SQL statement */ UrlData baseUrl; /* Saved parse of the default remote */ sync_explain(syncFlags); if( (syncFlags & SYNC_ALLURL)==0 ){ /* Common-case: Only sync with the remote identified by g.url */ nErr = client_sync(syncFlags, configRcvMask, configSendMask, zAltPCode, 0); if( nErr==0 ) url_remember(); return nErr; } /* If we reach this point, it means we want to sync with all remotes */ memset(&baseUrl, 0, sizeof(baseUrl)); url_move_parse(&baseUrl, &g.url); nOther = 0; azOther = 0; db_prepare(&q, "SELECT substr(name,10) FROM config" " WHERE name glob 'sync-url:*'" " AND value<>(SELECT value FROM config WHERE name='last-sync-url')" ); while( db_step(&q)==SQLITE_ROW ){ const char *zUrl = db_column_text(&q, 0); azOther = fossil_realloc(azOther, sizeof(*azOther)*(nOther+1)); azOther[nOther++] = fossil_strdup(zUrl); } db_finalize(&q); iEnd = nOther+1; nextIEnd = 0; nPass = 1 + ((syncFlags & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL)); for(iPass=0; iPass<nPass; iPass++){ for(i=0; i<iEnd; i++){ int rc; int nRcvd; if( i==0 ){ url_move_parse(&g.url, &baseUrl); /* Load canonical URL */ }else{ /* Load an auxiliary remote URL */ url_parse(azOther[i-1], URL_PROMPT_PW|URL_ASK_REMEMBER_PW|URL_USE_CONFIG); } if( i>0 || iPass>0 ) sync_explain(syncFlags); rc = client_sync(syncFlags, configRcvMask, configSendMask, zAltPCode, &nRcvd); if( nRcvd>0 ){ /* If new artifacts were received, we want to repeat all prior ** remotes on the second pass */ nextIEnd = i; } nErr += rc; if( rc==0 && iPass==0 ){ if( i==0 ){ url_remember(); }else if( (g.url.flags & URL_REMEMBER_PW)!=0 ){ char *zKey = mprintf("sync-pw:%s", azOther[i-1]); char *zPw = obscure(g.url.passwd); if( zPw && zPw[0] ){ db_set(zKey/*works-like:""*/, zPw, 0); } fossil_free(zPw); fossil_free(zKey); } } if( i==0 ){ url_move_parse(&baseUrl, &g.url); /* Don't forget canonical URL */ }else{ url_unparse(&g.url); /* Delete auxiliary URL parses */ } } iEnd = nextIEnd; } for(i=0; i<nOther; i++){ fossil_free(azOther[i]); azOther[i] = 0; } fossil_free(azOther); url_move_parse(&g.url, &baseUrl); /* Restore the canonical URL parse */ return nErr; } /* ** If the repository is configured for autosyncing, then do an ** autosync. Bits of the "flags" parameter determine details of behavior: ** ** SYNC_PULL Pull content from the server to the local repo ** SYNC_PUSH Push content from local up to the server ** SYNC_CKIN_LOCK Take a check-in lock on the current check-out. ** SYNC_VERBOSE Extra output ** ** Return the number of errors. ** ** The autosync setting can be a boolean or "pullonly". No autosync ** is attempted if the autosync setting is off, and only auto-pull is ** attempted if autosync is set to "pullonly". The check-in lock is ** not acquired unless autosync is set to "on". ** ** If dont-push setting is true, that is the same as having autosync ** set to pullonly. */ static int autosync(int flags, const char *zSubsys){ const char *zAutosync; int rc; int configSync = 0; /* configuration changes transferred */ if( g.fNoSync ){ return 0; } zAutosync = db_get_for_subsystem("autosync", zSubsys); if( zAutosync==0 ) zAutosync = "on"; /* defend against misconfig */ if( is_false(zAutosync) ) return 0; if( db_get_boolean("dont-push",0) || sqlite3_strglob("*pull*", zAutosync)==0 ){ flags &= ~SYNC_CKIN_LOCK; if( flags & SYNC_PUSH ) return 0; } if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE; url_parse(0, URL_REMEMBER|URL_USE_CONFIG); 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(); if( sqlite3_strglob("*all*", zAutosync)==0 ){ rc = client_sync_all_urls(flags|SYNC_ALLURL, configSync, 0, 0); }else{ url_remember(); sync_explain(flags); url_enable_proxy("via proxy: "); rc = client_sync(flags, configSync, 0, 0, 0); } return rc; } /* ** This routine will try a number of times to perform autosync with a ** 0.5 second sleep between attempts. The number of attempts is determined ** by the "autosync-tries" setting, which defaults to 1. ** ** Return zero on success and non-zero on a failure. If failure occurs ** and doPrompt flag is true, ask the user if they want to continue, and ** if they answer "yes" then return zero in spite of the failure. */ int autosync_loop(int flags, int doPrompt, const char *zSubsystem){ int n = 0; int rc = 0; int nTries = db_get_int("autosync-tries", 1); if( (flags & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) && db_get_boolean("uv-sync",0) ){ flags |= SYNC_UNVERSIONED; } if( nTries<1 ) nTries = 1; while( (n==0 || n<nTries) && (rc=autosync(flags, zSubsystem)) ){ if( rc ){ if( ++n<nTries ){ fossil_warning("Autosync failed, making another attempt."); sqlite3_sleep(500); }else{ fossil_warning("Autosync failed."); } } } if( rc && doPrompt ){ Blob ans; char cReply; prompt_user("continue in spite of sync failure (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply=='y' || cReply=='Y' ) rc = 0; blob_reset(&ans); } 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, /* Write configuration flags here */ unsigned *pSyncFlags, /* Write sync flags here */ int uvOnly, /* Special handling flags for UV sync */ unsigned urlOmitFlags /* Omit these URL flags */ ){ 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( (*pSyncFlags) & SYNC_FROMPARENT ) urlFlags |= URL_USE_PARENT; if( !uvOnly ){ if( find_option("private",0,0)!=0 ){ *pSyncFlags |= SYNC_PRIVATE; } /* 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; } } if( find_option("private",0,0)!=0 ){ *pSyncFlags |= SYNC_PRIVATE; } if( find_option("verbose","v",0)!=0 ){ *pSyncFlags |= SYNC_VERBOSE; if( find_option("verbose","v",0)!=0 ){ *pSyncFlags |= SYNC_XVERBOSE; } } if( find_option("no-http-compression",0,0)!=0 ){ *pSyncFlags |= SYNC_NOHTTPCOMPRESS; } if( find_option("all",0,0)!=0 ){ *pSyncFlags |= SYNC_ALLURL; } /* Undocumented option to cause links transitive links to other ** repositories to be shared */ if( ((*pSyncFlags) & SYNC_PULL)!=0 && find_option("share-links",0,0)!=0 ){ *pSyncFlags |= SYNC_SHARE_LINKS; } /* Option: --transport-command COMMAND ** ** Causes COMMAND to be run with three arguments in order to talk ** to the server. ** ** COMMAND URL PAYLOAD REPLY ** ** URL is the server name. PAYLOAD is the name of a temporary file ** that will contain the xfer-protocol payload to send to the server. ** REPLY is a temporary filename in which COMMAND should write the ** content of the reply from the server. ** ** CMD is reponsible for HTTP redirects. The following Fossil command ** can be used for CMD to achieve a working sync: ** ** fossil test-httpmsg --xfer */ g.zHttpCmd = find_option("transport-command",0,1); url_proxy_options(); clone_ssh_find_options(); if( !uvOnly ) db_find_and_open_repository(0, 0); db_open_config(0, 1); if( g.argc==2 ){ if( db_get_boolean("auto-shun",0) ) configSync = CONFIGSET_SHUN; }else if( g.argc==3 ){ zUrl = g.argv[2]; if( (*pSyncFlags) & SYNC_ALLURL ){ fossil_fatal("cannot use both the --all option and specific URL \"%s\"", zUrl); } } if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) && db_get_boolean("uv-sync",0) ){ *pSyncFlags |= SYNC_UNVERSIONED; } urlFlags &= ~urlOmitFlags; if( urlFlags & URL_REMEMBER ){ clone_ssh_db_set_options(); } url_parse(zUrl, urlFlags|URL_USE_CONFIG); remember_or_get_http_auth(zHttpAuth, urlFlags & URL_REMEMBER, zUrl); if( g.url.protocol==0 ){ if( urlOptional ) fossil_exit(0); usage("URL"); } user_select(); url_enable_proxy("via proxy: "); *pConfigFlags |= configSync; if( (*pSyncFlags & SYNC_ALLURL)==0 && zUrl==0 ){ const char *zAutosync = db_get_for_subsystem("autosync", "sync"); if( sqlite3_strglob("*all*", zAutosync)==0 ){ *pSyncFlags |= SYNC_ALLURL; } } } /* ** COMMAND: pull ** ** Usage: %fossil pull ?URL? ?options? ** ** Pull all sharable changes from a remote repository into the local ** repository. Sharable changes include public check-ins, edits to ** wiki pages, tickets, tech-notes, and forum posts. Add ** the --private option to pull private branches. Use the ** "configuration pull" command to pull website configuration details. ** ** If URL is not specified, then the URL from the most recent clone, push, ** pull, remote, or sync command is used. See "fossil help clone" for ** details on the URL formats. ** ** Options: ** --all Pull from all remotes, not just the default ** -B|--httpauth USER:PASS Credentials for the simple HTTP auth protocol, ** if required by the remote website ** --from-parent-project Pull content from the parent project ** --ipv4 Use only IPv4, not IPv6 ** --no-http-compression Do not compress HTTP traffic ** --once Do not remember URL for subsequent syncs ** --private Pull private branches too ** --project-code CODE Use CODE as the project code ** --proxy PROXY Use the specified HTTP proxy ** -R|--repository REPO Local repository to pull into ** --ssl-identity FILE Local SSL credentials, if requested by remote ** --ssh-command SSH Use SSH as the "ssh" command ** --transport-command CMD Use external command CMD to move messages ** between client and server ** -v|--verbose Additional (debugging) output - use twice to ** also trace network traffic. ** --verily Exchange extra information with the remote ** to ensure no content is overlooked ** ** See also: [[clone]], [[config]], [[push]], [[remote]], [[sync]] */ void pull_cmd(void){ unsigned configFlags = 0; unsigned syncFlags = SYNC_PULL; unsigned urlOmitFlags = 0; const char *zAltPCode = find_option("project-code",0,1); if( find_option("from-parent-project",0,0)!=0 ){ syncFlags |= SYNC_FROMPARENT; } if( zAltPCode ) urlOmitFlags = URL_REMEMBER; process_sync_args(&configFlags, &syncFlags, 0, urlOmitFlags); /* We should be done with options.. */ verify_all_options(); client_sync_all_urls(syncFlags, configFlags, 0, zAltPCode); } /* ** COMMAND: push ** ** Usage: %fossil push ?URL? ?options? ** ** Push all sharable changes from the local repository to a remote ** repository. Sharable changes include public check-ins, edits to ** wiki pages, tickets, tech-notes, and forum posts. Use ** --private to also push private branches. Use the "configuration ** push" command to push website configuration details. ** ** If URL is not specified, then the URL from the most recent clone, push, ** pull, remote, or sync command is used. See "fossil help clone" for ** details on the URL formats. ** ** Options: ** --all Push to all remotes, not just the default ** -B|--httpauth USER:PASS Credentials for the simple HTTP auth protocol, ** if required by the remote website ** --ipv4 Use only IPv4, not IPv6 ** --no-http-compression Do not compress HTTP traffic ** --once Do not remember URL for subsequent syncs ** --proxy PROXY Use the specified HTTP proxy ** --private Push private branches too ** -R|--repository REPO Local repository to push from ** --ssl-identity FILE Local SSL credentials, if requested by remote ** --ssh-command SSH Use SSH as the "ssh" command ** --transport-command CMD Use external command CMD to communicate with ** the server ** -v|--verbose Additional (debugging) output - use twice for ** network debugging ** --verily Exchange extra information with the remote ** to ensure no content is overlooked ** ** See also: [[clone]], [[config]], [[pull]], [[remote]], [[sync]] */ void push_cmd(void){ unsigned configFlags = 0; unsigned syncFlags = SYNC_PUSH; process_sync_args(&configFlags, &syncFlags, 0, 0); /* 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_all_urls(syncFlags, 0, 0, 0); } /* ** COMMAND: sync ** ** Usage: %fossil sync ?URL? ?options? ** ** Synchronize all sharable changes between the local repository and a ** remote repository. Sharable changes include public check-ins and ** edits to wiki pages, tickets, forum posts, and technical notes. ** ** If URL is not specified, then the URL from the most recent clone, push, ** pull, remote, or sync command is used. See "fossil help clone" for ** details on the URL formats. ** ** Options: ** --all Sync with all remotes, not just the default ** -B|--httpauth USER:PASS Credentials for the simple HTTP auth protocol, ** if required by the remote website ** --ipv4 Use only IPv4, not IPv6 ** --no-http-compression Do not compress HTTP traffic ** --once Do not remember URL for subsequent syncs ** --proxy PROXY Use the specified HTTP proxy ** --private Sync private branches too ** -R|--repository REPO Local repository to sync with ** --ssl-identity FILE Local SSL credentials, if requested by remote ** --ssh-command SSH Use SSH as the "ssh" command ** --transport-command CMD Use external command CMD to move message ** between the client and the server ** -u|--unversioned Also sync unversioned content ** -v|--verbose Additional (debugging) output - use twice to ** get network debug info ** --verily Exchange extra information with the remote ** to ensure no content is overlooked ** ** See also: [[clone]], [[pull]], [[push]], [[remote]] */ void sync_cmd(void){ unsigned configFlags = 0; unsigned syncFlags = SYNC_PUSH|SYNC_PULL; if( find_option("unversioned","u",0)!=0 ){ syncFlags |= SYNC_UNVERSIONED; } process_sync_args(&configFlags, &syncFlags, 0, 0); /* We should be done with options.. */ verify_all_options(); if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH; if( (syncFlags & SYNC_PUSH)==0 ){ fossil_warning("pull only: the 'dont-push' option is set"); } client_sync_all_urls(syncFlags, configFlags, 0, 0); } /* ** Handle the "fossil unversioned sync" and "fossil unversioned revert" ** commands. */ void sync_unversioned(unsigned syncFlags){ unsigned configFlags = 0; (void)find_option("uv-noop",0,0); process_sync_args(&configFlags, &syncFlags, 1, 0); verify_all_options(); client_sync(syncFlags, 0, 0, 0, 0); } /* ** COMMAND: remote ** COMMAND: remote-url* ** ** Usage: %fossil remote ?SUBCOMMAND ...? ** ** View or modify the URLs of remote repositories used for syncing. ** The "default" remote is specially named by Fossil and corresponds to ** the URL used in the most recent "sync", "push", "pull", "clone", or ** similar command. As such, the default remote can be updated by ** Fossil with each sync command. Other named remotes are persistent. ** ** > fossil remote ** ** With no arguments, this command shows the current default remote ** URL. If there is no default, it shows "off". ** ** > fossil remote add NAME URL ** ** Add a new named URL. Afterwards, NAME can be used as a short ** symbolic name for URL in contexts where a URL is required. The ** URL argument can be "default" or a prior symbolic name to make ** a copy of an existing URL under the new NAME. The "default" ** remote cannot be defined with this subcommand; instead, ** use 'fossil remote REF' as documented below. ** ** > fossil remote config-data ** ** DEBUG USE ONLY - Show the name and value of every CONFIG table ** entry in the repository that is associated with the remote URL store. ** Passwords are obscured in the output. ** ** > fossil remote delete NAME ** ** Delete a named URL previously created by the "add" subcommand. ** ** > fossil remote hyperlink ?FILENAME? ?LINENUM? ?LINENUM? ** ** Print a URL that will access the current check-out on the remote ** repository. Or if the FILENAME argument is included, print the ** URL to access that particular file within the current check-out. ** If one or two linenumber arguments are provided after the filename, ** then the URL is for the line or range of lines specified. ** ** > fossil remote list|ls ** ** Show all remote repository URLs. ** ** > fossil remote off ** ** Forget the default URL. This disables autosync. ** ** This is a convenient way to enter "airplane mode". To enter ** airplane mode, first save the current default URL, then turn the ** default off. Perhaps like this: ** ** fossil remote add main default ** fossil remote off ** ** To exit airplane mode and turn autosync back on again: ** ** fossil remote main ** ** > fossil remote scrub ** ** Forget any saved passwords for remote repositories, but continue ** to remember the URLs themselves. You will be prompted for the ** password the next time it is needed. ** ** > fossil remote ui ?FILENAME? ?LINENUM? ?LINENUM? ** ** Bring up a web browser pointing at the remote repository, and ** specifically to the page that describes the current check-out ** on that remote repository. Or if FILENAME and/or LINENUM arguments ** are provided, to the specific file and range of lines. This ** command is similar to "fossil remote hyperlink" except that instead ** of printing the URL, it passes the URL off to the web browser. ** ** > fossil remote REF ** ** Make REF the new default URL, replacing the prior default. ** REF may be a URL or a NAME from a prior "add". */ void remote_url_cmd(void){ char *zUrl, *zArg; int nArg; int showPw; db_find_and_open_repository(0, 0); showPw = find_option("show-passwords",0,0)!=0; /* We should be done with options.. */ verify_all_options(); /* 2021-10-25: A note about data structures. ** ** The remote URLs are stored in the CONFIG table. The URL is stored ** separately from the password. The password is obscured using the ** obscure() function. ** ** Originally, Fossil only preserved a single remote URL. That URL ** is stored in "last-sync-url" and the password in "last-sync-pw". The ** ability to have multiple remotes was added later so these names ** were retained for backwards compatibility. The other remotes are ** stored in "sync-url:NAME" and "sync-pw:NAME" where NAME is the name ** of the remote. ** ** The last-sync-url is called "default" for the display list. ** ** The last-sync-url might be duplicated into one of the sync-url:NAME ** entries. Thus, when doing a "fossil sync --all" or an autosync with ** autosync=all, each sync-url:NAME entry is checked to see if it is the ** same as last-sync-url and if it is then that entry is skipped. */ if( g.argc==2 ){ /* "fossil remote" with no arguments: Show the last sync URL. */ zUrl = db_get("last-sync-url", 0); if( zUrl==0 ){ fossil_print("off\n"); }else{ url_parse(zUrl, 0); fossil_print("%s\n", g.url.canonical); } return; } zArg = g.argv[2]; nArg = (int)strlen(zArg); if( strcmp(zArg,"off")==0 ){ /* fossil remote off ** Forget the last-sync-URL and its password */ if( g.argc!=3 ) usage("off"); remote_delete_default: db_unprotect(PROTECT_CONFIG); db_multi_exec( "DELETE FROM config WHERE name GLOB 'last-sync-*';" ); db_protect_pop(); return; } if( strncmp(zArg, "list", nArg)==0 || strcmp(zArg,"ls")==0 ){ Stmt q; if( g.argc!=3 ) usage("list"); db_prepare(&q, "SELECT 'default', value FROM config WHERE name='last-sync-url'" " UNION ALL " "SELECT substr(name,10), value FROM config" " WHERE name GLOB 'sync-url:*'" " ORDER BY 1" ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%-18s %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); return; } if( strcmp(zArg, "add")==0 ){ char *zName; char *zUrl; UrlData x; if( g.argc!=5 ) usage("add NAME URL"); memset(&x, 0, sizeof(x)); zName = g.argv[3]; zUrl = g.argv[4]; if( strcmp(zName,"default")==0 ){ fossil_fatal("update the \"default\" remote-url with 'fossil remote REF'" "\nsee 'fossil help remote' for complete usage information"); } db_begin_write(); if( fossil_strcmp(zUrl,"default")==0 ){ x.canonical = db_get("last-sync-url",0); x.passwd = unobscure(db_get("last-sync-pw",0)); }else{ url_parse_local(zUrl, URL_PROMPT_PW|URL_USE_CONFIG, &x); } db_unprotect(PROTECT_CONFIG); db_multi_exec( "REPLACE INTO config(name, value, mtime)" " VALUES('sync-url:%q',%Q,now())", zName, x.canonical ); db_multi_exec( "REPLACE INTO config(name, value, mtime)" " VALUES('sync-pw:%q',obscure(%Q),now())", zName, x.passwd ); db_protect_pop(); db_commit_transaction(); return; } if( strncmp(zArg, "delete", nArg)==0 ){ char *zName; if( g.argc!=4 ) usage("delete NAME"); zName = g.argv[3]; if( strcmp(zName,"default")==0 ) goto remote_delete_default; db_begin_write(); db_unprotect(PROTECT_CONFIG); db_multi_exec("DELETE FROM config WHERE name glob 'sync-url:%q'", zName); db_multi_exec("DELETE FROM config WHERE name glob 'sync-pw:%q'", zName); db_protect_pop(); db_commit_transaction(); return; } if( strncmp(zArg, "hyperlink", nArg)==0 || (nArg==2 && strcmp(zArg, "ui")==0) ){ char *zBase; char *zUuid; Blob fname; Blob url; char *zSubCmd = g.argv[2][0]=='u' ? "ui" : "hyperlink"; if( !db_table_exists("localdb","vvar") ){ fossil_fatal("the \"remote %s\" command only works from " "within an open check-out", zSubCmd); } zUrl = db_get("last-sync-url", 0); if( zUrl==0 ){ zUrl = "http://localhost:8080/"; } url_parse(zUrl, 0); if( g.url.isFile ){ url_parse("http://localhost:8080/", 0); } zBase = url_nouser(&g.url); blob_init(&url, 0, 0); if( g.argc==3 ){ blob_appendf(&url, "%s/info/%!S", zBase, db_text("???", "SELECT uuid FROM blob, vvar" " WHERE blob.rid=0+vvar.value" " AND vvar.name='checkout';" )); }else{ blob_init(&fname, 0, 0); file_tree_name(g.argv[3], &fname, 0, 1); zUuid = db_text(0, "SELECT uuid FROM files_of_checkin" " WHERE checkinID=(SELECT value FROM vvar WHERE name='checkout')" " AND filename=%Q", blob_str(&fname) ); if( zUuid==0 ){ fossil_fatal("not a managed file: \"%s\"", g.argv[3]); } blob_appendf(&url, "%s/info/%S",zBase,zUuid); if( g.argc>4 ){ int ln1 = atoi(g.argv[4]); if( ln1<=0 || sqlite3_strglob("*[^0-9]*",g.argv[4])==0 ){ fossil_fatal("\"%s\" is not a valid line number", g.argv[4]); } if( g.argc>5 ){ int ln2 = atoi(g.argv[5]); if( ln2==0 || sqlite3_strglob("*[^0-9]*",g.argv[5])==0 ){ fossil_fatal("\"%s\" is not a valid line number", g.argv[5]); } if( ln2<=ln1 ){ fossil_fatal("second line number should be greater than the first"); } blob_appendf(&url,"?ln=%d,%d", ln1, ln2); }else{ blob_appendf(&url,"?ln=%d", ln1); } } if( g.argc>6 ){ usage(mprintf("%s ?FILENAME? ?LINENUMBER? ?LINENUMBER?", zSubCmd)); } } if( g.argv[2][0]=='u' ){ char *zCmd; zCmd = mprintf("%s %!$ &", fossil_web_browser(), blob_str(&url)); fossil_system(zCmd); }else{ fossil_print("%s\n", blob_str(&url)); } return; } if( strncmp(zArg, "scrub", nArg)==0 ){ if( g.argc!=3 ) usage("scrub"); db_begin_write(); db_unprotect(PROTECT_CONFIG); db_multi_exec("DELETE FROM config WHERE name glob 'sync-pw:*'"); db_multi_exec("DELETE FROM config WHERE name = 'last-sync-pw'"); db_protect_pop(); db_commit_transaction(); return; } if( strncmp(zArg, "config-data", nArg)==0 ){ /* Undocumented command: "fossil remote config-data [-show-passwords]" ** ** Show the CONFIG table entries that relate to remembering remote URLs */ Stmt q; int n; sqlite3_create_function(g.db, "unobscure", 1, SQLITE_UTF8, &g.db, db_obscure, 0, 0); n = db_int(13, "SELECT max(length(name))" " FROM config" " WHERE name GLOB 'sync-*:*'" " OR name GLOB 'last-sync-*'" " OR name GLOB 'parent-project-*'" ); db_prepare(&q, "SELECT name," " CASE WHEN name NOT LIKE '%%sync-pw%%' AND name<>'parent-project-pw'" " THEN value" " WHEN %d THEN unobscure(value)" " ELSE printf('%%.*c',length(value)/2-1,'*') END" " FROM config" " WHERE name GLOB 'sync-*:*'" " OR name GLOB 'last-sync-*'" " OR name GLOB 'parent-project-*'" " ORDER BY name LIKE '%%sync-pw%%' OR name='parent-project-pw', name", showPw ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%-*s %s\n", n, db_column_text(&q,0), db_column_text(&q,1) ); } db_finalize(&q); return; } if( sqlite3_strlike("http://%",zArg,0)==0 || sqlite3_strlike("https://%",zArg,0)==0 || sqlite3_strlike("ssh:%",zArg,0)==0 || sqlite3_strlike("file:%",zArg,0)==0 || db_exists("SELECT 1 FROM config WHERE name='sync-url:%q'",zArg) ){ db_unset("last-sync-url", 0); db_unset("last-sync-pw", 0); url_parse(g.argv[2], URL_REMEMBER|URL_PROMPT_PW| URL_USE_CONFIG|URL_ASK_REMEMBER_PW); url_remember(); return; } fossil_fatal("unknown command \"%s\" - should be a URL or one of: " "add delete hyperlink list off scrub", zArg); } /* ** COMMAND: backup* ** ** Usage: %fossil backup ?OPTIONS? FILE|DIRECTORY ** ** Make a backup of the repository into the named file or into the named ** directory. This backup is guaranteed to be consistent even if there are ** concurrent changes taking place on the repository. In other words, it ** is safe to run "fossil backup" on a repository that is in active use. ** ** Only the main repository database is backed up by this command. The ** open check-out file (if any) is not saved. Nor is the global configuration ** database. ** ** Options: ** --overwrite OK to overwrite an existing file ** -R NAME Filename of the repository to backup */ void backup_cmd(void){ char *zDest; int bOverwrite = 0; db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); bOverwrite = find_option("overwrite",0,0)!=0; verify_all_options(); if( g.argc!=3 ){ usage("FILE|DIRECTORY"); } zDest = g.argv[2]; if( file_isdir(zDest, ExtFILE)==1 ){ zDest = mprintf("%s/%s", zDest, file_tail(g.zRepositoryName)); } if( file_isfile(zDest, ExtFILE) ){ if( bOverwrite ){ if( file_delete(zDest) ){ fossil_fatal("unable to delete old copy of \"%s\"", zDest); } }else{ fossil_fatal("backup \"%s\" already exists", zDest); } } db_unprotect(PROTECT_ALL); db_multi_exec("VACUUM repository INTO %Q", zDest); } |
Changes to src/tag.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 | /* ** Propagate the tag given by tagid to the children of pid. ** ** This routine assumes that tagid is a tag that should be ** propagated and that the tag is already present in pid. ** ** If tagtype is 2 then the tag is being propagated from an | | | | | > > | | | > > > > > | | > > > | | > > > | | | < > | 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 | /* ** Propagate the tag given by tagid to the children of pid. ** ** This routine assumes that tagid is a tag that should be ** propagated and that the tag is already present in pid. ** ** If tagtype is 2 then the tag is being propagated from an ** ancestor node. If tagtype is 0 it means a propagating tag is ** being blocked. */ static void tag_propagate( int pid, /* Propagate the tag to children of this node */ int tagid, /* Tag to propagate */ int tagType, /* 2 for a propagating tag. 0 for an antitag */ int origId, /* Artifact of tag, when tagType==2 */ const char *zValue, /* Value of the tag. Might be NULL */ double mtime /* Timestamp on the tag */ ){ PQueue queue; /* Queue of check-ins to be tagged */ Stmt s; /* Query the children of :pid to which to propagate */ Stmt ins; /* INSERT INTO tagxref */ Stmt eventupdate; /* UPDATE event */ assert( tagType==0 || tagType==2 ); pqueuex_init(&queue); pqueuex_insert(&queue, pid, 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, "UPDATE event SET bgcolor=%Q WHERE objid=:rid", zValue ); } while( (pid = pqueuex_extract(&queue))!=0 ){ db_bind_int(&s, ":pid", pid); while( db_step(&s)==SQLITE_ROW ){ int doit = db_column_int(&s, 2); if( doit ){ int cid = db_column_int(&s, 0); double mtime = db_column_double(&s, 1); pqueuex_insert(&queue, cid, mtime); db_bind_int(&ins, ":rid", cid); db_step(&ins); db_reset(&ins); if( tagid==TAG_BGCOLOR ){ db_bind_int(&eventupdate, ":rid", cid); db_step(&eventupdate); db_reset(&eventupdate); } if( tagid==TAG_BRANCH ){ leaf_eventually_check(cid); } } } db_reset(&s); } pqueuex_clear(&queue); db_finalize(&ins); db_finalize(&s); if( tagid==TAG_BGCOLOR ){ db_finalize(&eventupdate); } } /* ** Propagate all propagatable tags in pid to the children of pid. */ void tag_propagate_all(int pid){ Stmt q; db_prepare(&q, "SELECT tagid, tagtype, mtime, value, origid FROM tagxref" " WHERE rid=%d", pid ); while( db_step(&q)==SQLITE_ROW ){ int tagid = db_column_int(&q, 0); int tagtype = db_column_int(&q, 1); double mtime = db_column_double(&q, 2); const char *zValue = db_column_text(&q, 3); int origid = db_column_int(&q, 4); if( tagtype==1 ) tagtype = 0; tag_propagate(pid, tagid, tagtype, origid, zValue, mtime); } db_finalize(&q); } /* ** Get a tagid for the given TAG. Create a new tag if necessary |
︙ | ︙ | |||
133 134 135 136 137 138 139 140 141 142 143 144 145 146 | id = db_last_insert_rowid(); } return id; } /* ** Insert a tag into the database. */ int tag_insert( const char *zTag, /* Name of the tag (w/o the "+" or "-" prefix */ int tagtype, /* 0:cancel 1:singleton 2:propagated */ const char *zValue, /* Value if the tag is really a property */ int srcId, /* Artifact that contains this tag */ double mtime, /* Timestamp. Use default if <=0.0 */ | > > > | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | id = db_last_insert_rowid(); } return id; } /* ** Insert a tag into the database. ** ** Also translate zTag into a tagid and return the tagid. (In other words ** if zTag is "bgcolor" then return TAG_BGCOLOR.) */ int tag_insert( const char *zTag, /* Name of the tag (w/o the "+" or "-" prefix */ int tagtype, /* 0:cancel 1:singleton 2:propagated */ const char *zValue, /* Value if the tag is really a property */ int srcId, /* Artifact that contains this tag */ double mtime, /* Timestamp. Use default if <=0.0 */ |
︙ | ︙ | |||
164 165 166 167 168 169 170 | 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; } | | > > > > > > | > | > | | > > > > > > | | < > | | | > > > > > > > > > > > > > | > > > > > > > | | < | > > > > | | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > | | | > | | > | > > > > > > > > > > > > > > > > > | > > > > > > > > > > | | > | > > | > > > > > > > > > | | > > > > > > | > > > > > > > > > > > | 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 | 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); if( tagid==TAG_BRANCH ) leaf_eventually_check(rid); if( tagtype==0 ){ zValue = 0; } zCol = 0; switch( tagid ){ case TAG_BGCOLOR: { zCol = "bgcolor"; break; } case TAG_COMMENT: { zCol = "ecomment"; break; } case TAG_USER: { zCol = "euser"; break; } case TAG_PRIVATE: { 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); backlink_extract(zCopy, MT_NONE, rid, BKLNK_COMMENT, mtime, 1); free(zCopy); } } if( tagid==TAG_DATE ){ db_multi_exec("UPDATE event " " SET mtime=julianday(%Q)," " omtime=coalesce(omtime,mtime)" " WHERE objid=%d", zValue, rid); } if( tagid==TAG_PARENT && tagtype==1 ){ manifest_reparent_checkin(rid, zValue); } if( tagtype==1 ) tagtype = 0; tag_propagate(rid, tagid, tagtype, rid, zValue, mtime); return tagid; } /* ** COMMAND: test-tag ** ** Usage: %fossil test-tag (+|*|-)TAGNAME ARTIFACT-ID ?VALUE? ** ** Add a tag or anti-tag to the rebuildable tables of the local repository. ** No tag artifact is created so the new tag is erased the next ** time the repository is rebuilt. This routine is for testing ** use only. */ void testtag_cmd(void){ const char *zTag; const char *zValue; int rid; int tagtype; db_must_be_within_tree(); if( g.argc!=4 && g.argc!=5 ){ 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); } /* ** OR this value into the tagtype argument to tag_add_artifact to ** cause the tag to be displayed on standard output rather than be ** inserted. Used for --dry-run options and debugging. */ #if INTERFACE #define TAG_ADD_DRYRUN 0x04 #endif /* ** Add a control record to the repository that either creates ** or cancels a tag. ** ** tagtype should normally be 0, 1, or 2. But if the TAG_ADD_DRYRUN bit ** is also set, then simply print the text of the tag on standard output ** (for testing purposes) rather than create the tag. */ void tag_add_artifact( const char *zPrefix, /* Prefix to prepend to tag name */ const char *zTagname, /* The tag to add or cancel */ const char *zObjName, /* Name of object attached to */ const char *zValue, /* Value for the tag. Might be NULL */ int tagtype, /* 0:cancel 1:singleton 2:propagated */ const char *zDateOvrd, /* Override date string */ const char *zUserOvrd /* Override user name */ ){ int rid; int nrid; char *zDate; Blob uuid; Blob ctrl; Blob cksum; static const char zTagtype[] = { '-', '+', '*' }; int dryRun = 0; if( tagtype & TAG_ADD_DRYRUN ){ tagtype &= ~TAG_ADD_DRYRUN; dryRun = 1; } assert( tagtype>=0 && tagtype<=2 ); user_select(); blob_zero(&uuid); blob_append(&uuid, zObjName, -1); if( name_to_uuid(&uuid, 9, "*") ){ fossil_fatal("%s", g.zErrMsg); return; } rid = name_to_rid(blob_str(&uuid)); g.markPrivate = content_is_private(rid); blob_zero(&ctrl); #if 0 if( validate16(zTagname, strlen(zTagname)) ){ fossil_fatal( "invalid tag name \"%s\" - might be confused with" " a hexadecimal artifact ID", zTagname ); } #endif zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); blob_appendf(&ctrl, "D %s\n", zDate); 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); if( dryRun ){ fossil_print("%s", blob_str(&ctrl)); blob_reset(&ctrl); }else{ nrid = content_put(&ctrl); manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); } assert( blob_is_reset(&ctrl) ); if( g.localOpen ){ manifest_to_disk(rid); } } /* ** If zTag is NULL or valid for use as a tag for the `tag add` and ** `tag cancel` commands, returns without side effects, else emits a ** fatal error message. We reject certain prefixes to avoid that ** clients cause undue grief by improperly tagging artifacts as being, ** e.g., wiki pages or tickets. ** ** Note that we intentionally allow the "sym-" prefix, partly for ** historical compatibility and partly because it can be applied ** properly, whereas the other reserved name types have special ** meanings for fossil and cannot be sensibly manually manipulated. */ static void tag_cmd_tagname_check(const char *zTag){ if(zTag && *zTag && (strncmp(zTag,"wiki-",5)==0 || strncmp(zTag,"tkt-",4)==0 || strncmp(zTag,"event-",6)==0)){ fossil_fatal("Invalid prefix for tag name: %s", zTag); } } /* ** COMMAND: tag ** ** Usage: %fossil tag SUBCOMMAND ... ** ** Run various subcommands to control tags and properties. ** ** > fossil tag add ?OPTIONS? TAGNAME ARTIFACT-ID ?VALUE? ** ** Add a new tag or property to an artifact referenced by ** ARTIFACT-ID. For check-ins, the tag will be usable instead ** of a CHECK-IN in commands such as update and merge. If the ** --propagate flag is present and ARTIFACT-ID refers to a ** wiki page, forum post, technote, or check-in, the tag ** propagates to all descendants of that artifact. ** ** Options: ** --date-override DATETIME Set date and time added ** -n|--dry-run Display the tag text, but do not ** actually insert it into the database ** --propagate Propagating tag ** --raw Raw tag name. Ignored for ** non-CHECK-IN artifacts. ** --user-override USER Name USER when adding the tag ** ** The --date-override and --user-override options support ** importing history from other SCM systems. DATETIME has ** the form 'YYYY-MMM-DD HH:MM:SS'. ** ** Note that fossil uses some tag prefixes internally and this ** command will reject tags with these prefixes to avoid ** causing problems or confusion: "wiki-", "tkt-", "event-". ** ** > fossil tag cancel ?--raw? TAGNAME ARTIFACT-ID ** ** Remove the tag TAGNAME from the artifact referenced by ** ARTIFACT-ID, and also remove the propagation of the tag to ** any descendants. Use the the -n|--dry-run option to see ** what would have happened. Certain tag name prefixes are ** forbidden, as documented for the 'add' subcommand. ** ** Options: ** --date-override DATETIME Set date and time deleted ** -n|--dry-run Display the control artifact, but do ** not insert it into the database ** --raw Raw tag name. Ignored for ** non-CHECK-IN artifacts. ** --user-override USER Name USER when deleting the tag ** ** > fossil tag find ?OPTIONS? TAGNAME ** ** List all objects that use TAGNAME. ** ** Options: ** -n|--limit N Limit to N results ** --raw Interprets tag as a raw name instead of a ** branch name and matches any type of artifact. ** Changes the output to include only the ** hashes of matching objects. ** -t|--type TYPE One of: ci (check-in), w (wiki), ** e (event/technote), f (forum post), ** t (ticket). Default is all types. Ignored ** if --raw is used. ** ** > fossil tag list|ls ?OPTIONS? ?ARTIFACT-ID? ** ** List all tags or, if ARTIFACT-ID is supplied, all tags and ** their values for that artifact. The tagtype option accepts ** one of: propagated, singleton, cancel. For historical ** scripting compatibility, the internal tag types "wiki-", ** "tkt-", and "event-" (technote) are elided by default ** unless the --raw or --prefix options are used. ** ** Options: ** -v|--inverse Inverse the meaning of --tagtype TYPE ** --prefix List only tags with the given prefix ** Fossil-internal prefixes include "sym-" ** (branch name), "wiki-", "event-" ** (technote), and "tkt-" (ticket). The ** prefix is stripped from the resulting ** list unless --raw is provided. Ignored if ** ARTIFACT-ID is provided. ** --raw List raw names of tags ** --tagtype TYPE List only tags of type TYPE, which must ** be one of: cancel, singleton, propagated ** ** 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 ** not use this option to make changes unless you are sure what ** you are doing. ** |
︙ | ︙ | |||
353 354 355 356 357 358 359 | ** fossil update tag:decaf ** ** will assume that "decaf" is a tag/branch name. ** */ void tag_cmd(void){ int n; | < < < | > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > | | > > > > > | | > > > | > | | > > | | > > > > > > > > > > > > > > > > > > > | | > | > > | > > > | > | > > > > > > > > > > > > > > > > > > > | | > | < | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > | | | > | | | < < | | | < < < | < < < < < < < < < < < < < < < | > | | > > > > > > > > | | > > | | | | | > > > > > > > | < | > > > > > > | | > > | > > > > > > > > > > > > > | | > > > | > > > > > > > > | | > > > > > > > | | > > > > > > | 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 | ** fossil update tag:decaf ** ** will assume that "decaf" is a tag/branch name. ** */ void tag_cmd(void){ int n; db_find_and_open_repository(0, 0); if( g.argc<3 ){ goto tag_cmd_usage; } n = strlen(g.argv[2]); if( n==0 ){ goto tag_cmd_usage; } if( strncmp(g.argv[2],"add",n)==0 ){ char *zValue; int dryRun = 0; int fRaw = find_option("raw","",0)!=0; const char *zPrefix = ""; int fPropagate = find_option("propagate","",0)!=0; const char *zDateOvrd = find_option("date-override",0,1); const char *zUserOvrd = find_option("user-override",0,1); const char *zTag; const char *zObjId; int objType; if( find_option("dry-run","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN; if( g.argc!=5 && g.argc!=6 ){ usage("add ?options? TAGNAME ARTIFACT-ID ?VALUE?"); } zTag = g.argv[3]; tag_cmd_tagname_check(zTag); zObjId = g.argv[4]; zValue = g.argc==6 ? g.argv[5] : 0; objType = whatis_rid_type(symbolic_name_to_rid(zObjId, 0)); switch(objType){ case 0: fossil_fatal("Cannot resolve artifact ID: %s", zObjId); break; case CFTYPE_MANIFEST: zPrefix = fRaw ? "" : "sym-"; break; default: break; } db_begin_transaction(); tag_add_artifact(zPrefix, zTag, zObjId, zValue, 1+fPropagate+dryRun,zDateOvrd,zUserOvrd); db_end_transaction(0); }else if( strncmp(g.argv[2],"branch",n)==0 ){ fossil_fatal("the \"fossil tag branch\" command is discontinued\n" "Use the \"fossil branch new\" command instead."); }else if( strncmp(g.argv[2],"cancel",n)==0 ){ int dryRun = 0; int fRaw = find_option("raw","",0)!=0; const char *zPrefix = ""; const char *zDateOvrd = find_option("date-override",0,1); const char *zUserOvrd = find_option("user-override",0,1); const char *zTag; const char *zObjId; int objType; if( find_option("dry-run","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN; if( g.argc!=5 ){ usage("cancel ?options? TAGNAME ARTIFACT-ID"); } zTag = g.argv[3]; tag_cmd_tagname_check(zTag); zObjId = g.argv[4]; objType = whatis_rid_type(symbolic_name_to_rid(zObjId, 0)); switch(objType){ case 0: fossil_fatal("Cannot resolve artifact ID: %s", zObjId); break; case CFTYPE_MANIFEST: zPrefix = fRaw ? "" : "sym-"; break; default: break; } db_begin_transaction(); tag_add_artifact(zPrefix, zTag, zObjId, 0, dryRun, zDateOvrd, zUserOvrd); db_end_transaction(0); }else if( strncmp(g.argv[2],"find",n)==0 ){ Stmt q; int fRaw = find_option("raw","",0)!=0; const char *zFindLimit = find_option("limit","n",1); const int nFindLimit = zFindLimit ? atoi(zFindLimit) : -2000; 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='%s%q'", (zType && 'c'==zType[0]) ? "sym-" : ""/*safe-for-%s*/, 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 /*sort*/", timeline_query_for_tty(), zType, tagid ); db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); print_timeline(&q, nFindLimit, 79, 0, 0); db_finalize(&q); } } }else if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){ Stmt q; const int fRaw = find_option("raw","",0)!=0; const char *zTagType = find_option("tagtype","t",1); const int fInverse = find_option("inverse","v",0)!=0; const char *zTagPrefix = find_option("prefix","",1); int nTagType = fRaw ? -1 : 0; if( zTagType!=0 ){ int l = strlen(zTagType); if( strncmp(zTagType,"cancel",l)==0 ){ nTagType = 0; }else if( strncmp(zTagType,"singleton",l)==0 ){ nTagType = 1; }else if( strncmp(zTagType,"propagated",l)==0 ){ nTagType = 2; }else{ fossil_fatal("unrecognized tag type"); } } if( g.argc==3 ){ const int nTagPrefix = zTagPrefix ? (int)strlen(zTagPrefix) : 0; db_prepare(&q, "SELECT tagname FROM tag" " WHERE EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=tag.tagid" " AND tagtype%s%d)" " AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' " " END ORDER BY tagname COLLATE uintnocase", zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, nTagType, zTagPrefix, zTagPrefix ); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); if( fRaw ){ fossil_print("%s\n", zName); }else if( nTagPrefix>0 ){ assert(db_column_bytes(&q,0)>=nTagPrefix); fossil_print("%s\n", &zName[nTagPrefix]); }else if( strncmp(zName, "sym-", 4)==0 ){ fossil_print("%s\n", &zName[4]); } } db_finalize(&q); }else if( g.argc==4 ){ char const *zObjId = g.argv[3]; const int rid = name_to_rid(zObjId); const int objType = whatis_rid_type(rid); int nTagOffset = 0; zTagPrefix = 0; if(objType<=0){ fossil_fatal("Cannot resolve artifact ID: %s", zObjId); }else if(fRaw==0){ /* Figure out the tag prefix to strip */ switch(objType){ case CFTYPE_MANIFEST: zTagPrefix = "sym-"; break; case CFTYPE_WIKI: zTagPrefix = "wiki-"; break; case CFTYPE_TICKET: zTagPrefix = "tkt-"; break; case CFTYPE_EVENT: zTagPrefix = "event-"; break; default: break; } if(zTagPrefix!=0){ nTagOffset = (int)strlen(zTagPrefix); } } db_prepare(&q, "SELECT tagname, value FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" " AND tagtype%s%d" " ORDER BY tagname COLLATE uintnocase", rid, zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/, nTagType ); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zValue = db_column_text(&q, 1); if( zTagPrefix && strncmp(zName, zTagPrefix, nTagOffset)==0 ){ zName += nTagOffset; } if( zValue && zValue[0] ){ fossil_print("%s=%s\n", zName, zValue); }else{ fossil_print("%s\n", zName); } } db_finalize(&q); }else{ usage("list ?OPTIONS? ?CHECK-IN?"); } }else { goto tag_cmd_usage; } /* Cleanup */ return; tag_cmd_usage: usage("add|cancel|find|list ..."); } /* ** COMMAND: reparent* ** ** Usage: %fossil reparent [OPTIONS] CHECK-IN PARENT ... ** ** Create a "parent" tag that causes CHECK-IN to be interpreted as a ** child of PARENT. If multiple PARENTs are listed, then the first is ** the primary parent and others are merge ancestors. ** ** This is an experts-only command. It is used to patch up a repository ** that has been damaged by a shun or that has been pieced together from ** two or more separate repositories. You should never need to reparent ** during normal operations. ** ** Reparenting is accomplished by adding a parent tag. So to undo the ** reparenting operation, simply delete the tag. ** ** --test Make database entries but do not add the tag artifact. ** So the reparent operation will be undone by the next ** "fossil rebuild" command. ** -n|--dry-run Print the tag that would have been created but do not ** actually change the database in any way. ** --date-override DATETIME Set the change time on the control artifact ** --user-override USER Set the user name on the control artifact */ void reparent_cmd(void){ int bTest = find_option("test","",0)!=0; int rid; int i; Blob value; char *zUuid; int dryRun = 0; const char *zDateOvrd; /* The change time on the control artifact */ const char *zUserOvrd; /* The user name on the control artifact */ if( find_option("dry-run","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN; zDateOvrd = find_option("date-override",0,1); zUserOvrd = find_option("user-override",0,1); db_find_and_open_repository(0, 0); verify_all_options(); if( g.argc<4 ){ usage("[OPTIONS] CHECK-IN PARENT ..."); } rid = name_to_typed_rid(g.argv[2], "ci"); blob_init(&value, 0, 0); for(i=3; i<g.argc; i++){ int pid = name_to_typed_rid(g.argv[i], "ci"); if( i>3 ) blob_append(&value, " ", 1); zUuid = rid_to_uuid(pid); blob_append(&value, zUuid, strlen(zUuid)); fossil_free(zUuid); } if( bTest && !dryRun ){ tag_insert("parent", 1, blob_str(&value), -1, 0.0, rid); }else{ zUuid = rid_to_uuid(rid); tag_add_artifact("","parent",zUuid,blob_str(&value),1|dryRun, zDateOvrd,zUserOvrd); } } /* ** WEBPAGE: taglist ** ** List all non-propagating symbolic tags. */ void taglist_page(void){ Stmt q; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); } cgi_check_for_malice(); login_anonymous_available(); style_header("Tags"); style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("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 COLLATE uintnocase" ); @ <ul> while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); if( g.perm.Hyperlink ){ @ <li>%z(chref("taglink","%R/timeline?t=%T",zName)) @ %h(zName)</a></li> }else{ @ <li><span class="tagDsp">%h(zName)</span></li> } } @ </ul> db_finalize(&q); style_finish_page(); } /* ** WEBPAGE: /tagtimeline ** ** Render a timeline with all check-ins that contain non-propagating ** symbolic tags. ** ** Query parameters: ** ** ng No graph ** nohidden Hide check-ins with "hidden" tag ** onlyhidden Show only check-ins with "hidden" tag ** brbg Background color by branch name ** ubg Background color by user name */ void tagtimeline_page(void){ Blob sql = empty_blob; Stmt q; int tmFlags; /* Timeline display flags */ int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */ int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("Tagged Check-ins"); style_submenu_element("List", "taglist"); login_anonymous_available(); timeline_ss_submenu(); @ <h2>Check-ins with non-propagating tags:</h2> blob_append(&sql, timeline_query_for_www(), -1); blob_append_sql(&sql, "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-*'))"); if( fNoHidden || fOnlyHidden ){ const char* zUnaryOp = fNoHidden ? "NOT" : ""; blob_append_sql(&sql, " AND %s EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n", zUnaryOp/*safe-for-%s*/, TAG_HIDDEN); } db_prepare(&q, "%s ORDER BY event.mtime DESC /*sort*/", blob_sql_text(&sql)); blob_reset(&sql); /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too ** many descenders to (off-screen) parents. */ tmFlags = TIMELINE_XMERGE | TIMELINE_FILLGAPS | TIMELINE_NOSCROLL; if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH; if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR; if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR; www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); db_finalize(&q); @ <br> style_finish_page(); } /* ** Returns true if the given blob.rid value has the given tag ID ** applied to it, else false. */ int rid_has_tag(int rid, int tagId){ return db_exists( "SELECT tag.tagid FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagtype>0 " " AND tag.tagid=%d" " AND tagxref.tagid=tag.tagid", rid, tagId ); } /* ** Returns tagxref.rowid if the given blob.rid has a tagxref.rid entry ** of an active (non-cancelled) tag matching the given rid and tag ** name string, else returns 0. Note that this function does not ** distinguish between a non-existent tag and a cancelled tag. ** ** Design note: the return value is the tagxref.rowid because that ** gives us an easy way to fetch the value of the tag later on, if ** needed. */ int rid_has_active_tag_name(int rid, const char *zTagName){ static Stmt q = empty_Stmt_m; int rc; assert( 0 != zTagName ); if( !q.pStmt ){ db_static_prepare(&q, "SELECT x.rowid FROM tagxref x, tag t" " WHERE x.rid=$rid AND x.tagtype>0 " " AND x.tagid=t.tagid" " AND t.tagname=$tagname" ); } db_bind_int(&q, "$rid", rid); db_bind_text(&q, "$tagname", zTagName); rc = (SQLITE_ROW==db_step(&q)) ? db_column_int(&q, 0) : 0; db_reset(&q); return rc; } |
Added src/tar.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 | /* ** 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/ ** ******************************************************************************* ** ** This file contains code used to generate tarballs. */ #include "config.h" #include <assert.h> #include <zlib.h> #include "tar.h" /* ** State information for the tarball builder. */ static struct tarball_t { unsigned char *aHdr; /* Space for building headers */ char *zSpaces; /* Spaces for padding */ char *zPrevDir; /* Name of directory for previous entry */ int nPrevDirAlloc; /* size of zPrevDir */ Blob pax; /* PAX data */ } tball; /* ** field lengths of 'ustar' name and prefix fields. */ #define USTAR_NAME_LEN 100 #define USTAR_PREFIX_LEN 155 /* ** Begin the process of generating a tarball. ** ** Initialize the GZIP compressor and the table of directory names. */ static void tar_begin(sqlite3_int64 mTime){ assert( tball.aHdr==0 ); tball.aHdr = fossil_malloc(512+512); memset(tball.aHdr, 0, 512+512); tball.zSpaces = (char*)&tball.aHdr[512]; /* zPrevDir init */ tball.zPrevDir = NULL; tball.nPrevDirAlloc = 0; /* scratch buffer init */ blob_zero(&tball.pax); memcpy(&tball.aHdr[108], "0000000", 8); /* Owner ID */ memcpy(&tball.aHdr[116], "0000000", 8); /* Group ID */ memcpy(&tball.aHdr[257], "ustar\00000", 8); /* POSIX.1 format */ memcpy(&tball.aHdr[265], "nobody", 7); /* Owner name */ memcpy(&tball.aHdr[297], "nobody", 7); /* Group name */ gzip_begin(mTime); db_multi_exec( "CREATE TEMP TABLE dir(name UNIQUE);" ); } /* ** Verify that all characters in 'zName' are in the ** ISO646 (=ASCII) character set. */ static int is_iso646_name( const char *zName, /* file path */ int nName /* path length */ ){ int i; for(i = 0; i < nName; i++){ unsigned char c = (unsigned char)zName[i]; if( c>0x7e ) return 0; } return 1; } /* ** copy string pSrc into pDst, truncating or padding with 0 if necessary */ static void padded_copy( char *pDest, int nDest, const char *pSrc, int nSrc ){ if(nSrc >= nDest){ memcpy(pDest, pSrc, nDest); }else{ memcpy(pDest, pSrc, nSrc); memset(&pDest[nSrc], 0, nDest - nSrc); } } /****************************************************************************** ** ** The 'tar' format has evolved over time. Initially the name was stored ** in a 100 byte null-terminated field 'name'. File path names were ** limited to 99 bytes. ** ** The Posix.1 'ustar' format added a 155 byte field 'prefix', allowing ** for up to 255 characters to be stored. The full file path is formed by ** concatenating the field 'prefix', a slash, and the field 'name'. This ** gives some measure of compatibility with programs that only understand ** the oldest format. ** ** The latest Posix extension is called the 'pax Interchange Format'. ** It removes all the limitations of the previous two formats by allowing ** the storage of arbitrary-length attributes in a separate object that looks ** like a file to programs that do not understand this extension. So the ** contents of the 'name' and 'prefix' fields should contain values that allow ** versions of tar that do not understand this extension to still do ** something useful. ** ******************************************************************************/ /* ** The position we use to split a file path into the 'name' and 'prefix' ** fields needs to meet the following criteria: ** ** - not at the beginning or end of the string ** - the position must contain a slash ** - no more than 100 characters follow the slash ** - no more than 155 characters precede it ** ** The routine 'find_split_pos' finds a split position. It will meet the ** criteria of listed above if such a position exists. If no such ** position exists it generates one that useful for generating the ** values used for backward compatibility. */ static int find_split_pos( const char *zName, /* file path */ int nName /* path length */ ){ int i, split = 0; /* only search if the string needs splitting */ if(nName > USTAR_NAME_LEN){ for(i = 1; i+1 < nName; i++) if(zName[i] == '/'){ split = i+1; /* if the split position is within USTAR_NAME_LEN bytes from * the end we can quit */ if(nName - split <= USTAR_NAME_LEN) break; } } return split; } /* ** attempt to split the file name path to meet 'ustar' header ** criteria. */ static int tar_split_path( const char *zName, /* path */ int nName, /* path length */ char *pName, /* name field */ char *pPrefix /* prefix field */ ){ int split = find_split_pos(zName, nName); /* check whether both pieces fit */ if(nName - split > USTAR_NAME_LEN || split > USTAR_PREFIX_LEN+1){ return 0; /* no */ } /* extract name */ padded_copy(pName, USTAR_NAME_LEN, &zName[split], nName - split); /* extract prefix */ padded_copy(pPrefix, USTAR_PREFIX_LEN, zName, (split > 0 ? split - 1 : 0)); return 1; /* success */ } /* ** When using an extension header we still need to put something ** reasonable in the name and prefix fields. This is probably as ** good as it gets. */ static void approximate_split_path( const char *zName, /* path */ int nName, /* path length */ char *pName, /* name field */ char *pPrefix, /* prefix field */ int bHeader /* is this a 'x' type tar header? */ ){ int split; /* if this is a Pax Interchange header prepend "PaxHeader/" ** so we can tell files apart from metadata */ if( bHeader ){ blob_reset(&tball.pax); blob_appendf(&tball.pax, "PaxHeader/%*.*s", nName, nName, zName); zName = blob_buffer(&tball.pax); nName = blob_size(&tball.pax); } /* find the split position */ split = find_split_pos(zName, nName); /* extract a name, truncate if needed */ padded_copy(pName, USTAR_NAME_LEN, &zName[split], nName - split); /* extract a prefix field, truncate when needed */ padded_copy(pPrefix, USTAR_PREFIX_LEN, zName, (split > 0 ? split-1 : 0)); } /* ** add a Pax Interchange header to the scratch buffer ** ** format: <length> <key>=<value>\n ** the tricky part is that each header contains its own ** size in decimal, counting that length. */ static void add_pax_header( const char *zField, const char *zValue, int nValue ){ /* calculate length without length field */ int blen = strlen(zField) + nValue + 3; /* calculate the length of the length field */ int next10 = 1; int n; for(n = blen; n > 0; ){ blen++; next10 *= 10; n /= 10; } /* adding the length extended the length field? */ if(blen > next10){ blen++; } /* build the string */ blob_appendf(&tball.pax, "%d %s=%*.*s\n", blen, zField, nValue, nValue, zValue); /* this _must_ be right */ if((int)blob_size(&tball.pax) != blen){ fossil_panic("internal error: PAX tar header has bad length"); } } /* ** set the header type, calculate the checksum and output ** the header */ static void cksum_and_write_header( char cType ){ unsigned int cksum = 0; int i; memset(&tball.aHdr[148], ' ', 8); tball.aHdr[156] = cType; for(i=0; i<512; i++) cksum += tball.aHdr[i]; sqlite3_snprintf(8, (char*)&tball.aHdr[148], "%07o", cksum); tball.aHdr[155] = 0; gzip_step((char*)tball.aHdr, 512); } /* ** Build a header for a file or directory and write that header ** into the growing tarball. */ 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 */ if( !is_iso646_name(zName, nName) || !tar_split_path(zName, nName, (char*)tball.aHdr, (char*)&tball.aHdr[345]) ){ int lastPage; /* add a file name for interoperability with older programs */ approximate_split_path(zName, nName, (char*)tball.aHdr, (char*)&tball.aHdr[345], 1); /* generate the Pax Interchange path header */ blob_reset(&tball.pax); add_pax_header("path", zName, nName); /* set the header length, and write the header */ sqlite3_snprintf(12, (char*)&tball.aHdr[124], "%011o", blob_size(&tball.pax)); cksum_and_write_header('x'); /* write the Pax Interchange data */ gzip_step(blob_buffer(&tball.pax), blob_size(&tball.pax)); lastPage = blob_size(&tball.pax) % 512; if( lastPage!=0 ){ gzip_step(tball.zSpaces, 512 - lastPage); } /* generate an approximate path for the regular header */ approximate_split_path(zName, nName, (char*)tball.aHdr, (char*)&tball.aHdr[345], 0); } /* set the size */ sqlite3_snprintf(12, (char*)&tball.aHdr[124], "%011o", iSize); /* write the regular header */ cksum_and_write_header(cType); } /* ** Recursively add an directory entry for the given file if those ** directories have not previously been seen. */ static void tar_add_directory_of( 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) nsize = i+1; tball.zPrevDir = fossil_realloc(tball.zPrevDir, nsize); tball.nPrevDirAlloc = nsize; } memcpy(tball.zPrevDir, zName, i); tball.zPrevDir[i] = 0; } /* ** Add a single file to the growing tarball. */ static void tar_add_file( const char *zName, /* Name of the file. nul-terminated */ Blob *pContent, /* Content of the file */ int mPerm, /* 1: executable file, 2: symlink */ unsigned int mTime /* Last modification time of the file */ ){ int nName = strlen(zName); 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); } } } /* ** Finish constructing the tarball. Put the content of the tarball ** in Blob pOut. */ static void tar_finish(Blob *pOut){ db_multi_exec("DROP TABLE dir"); gzip_step(tball.zSpaces, 512); gzip_step(tball.zSpaces, 512); gzip_finish(pOut); fossil_free(tball.aHdr); tball.aHdr = 0; fossil_free(tball.zPrevDir); tball.zPrevDir = NULL; tball.nPrevDirAlloc = 0; blob_reset(&tball.pax); } /* ** COMMAND: test-tarball ** ** Generate a GZIP-compressed tarball in the file given by the first argument ** that contains files given in the second and subsequent arguments. ** ** -h|--dereference Follow symlinks and archive the files they point to */ void test_tarball_cmd(void){ int i; Blob zip; int eFType = SymFILE; if( g.argc<3 ){ usage("ARCHIVE [options] FILE...."); } if( find_option("dereference","h",0) ){ eFType = ExtFILE; } 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], eFType); tar_add_file(g.argv[i], &file, file_perm(0,eFType), file_mtime(0,eFType)); 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 that match pGlob (or all files if ** pGlob is NULL). ** ** 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. pInclude and pExclude are ignored in this case. ** ** If the RID object does not exist in the repository, then ** pTar is zeroed. ** ** zDir is a "synthetic" subdirectory which all files get ** added to as part of the tarball. It may be 0 or an empty string, in ** which case it is ignored. The intention is to create a tarball which ** politely expands into a subdir instead of filling your current dir ** with source files. For example, pass an artifact hash or "ProjectName". ** */ void tarball_of_checkin( int rid, /* The RID of the check-in from which to form a tarball*/ Blob *pTar, /* Write the tarball into this blob */ const char *zDir, /* Directory prefix for all file added to tarball */ Glob *pInclude, /* Only add files matching this pattern */ Glob *pExclude, /* Exclude files matching this pattern */ int listFlag /* Show filenames on stdout */ ){ Blob mfile, hash, file; Manifest *pManifest; ManifestFile *pFile; Blob filename; int nPrefix; char *zName = 0; unsigned int mTime; content_get(rid, &mfile); if( blob_size(&mfile)==0 ){ blob_zero(pTar); return; } blob_set_dynamic(&hash, rid_to_uuid(rid)); blob_zero(&filename); if( zDir && zDir[0] ){ blob_appendf(&filename, "%s/", zDir); } nPrefix = blob_size(&filename); pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); if( pManifest ){ int flg, eflg = 0; mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0); if( pTar ) tar_begin(mTime); flg = db_get_manifest_setting(); if( flg ){ /* eflg is the effective flags, taking include/exclude into account */ if( (pInclude==0 || glob_match(pInclude, "manifest")) && !glob_match(pExclude, "manifest") && (flg & MFESTFLG_RAW) ){ eflg |= MFESTFLG_RAW; } if( (pInclude==0 || glob_match(pInclude, "manifest.uuid")) && !glob_match(pExclude, "manifest.uuid") && (flg & MFESTFLG_UUID) ){ eflg |= MFESTFLG_UUID; } if( (pInclude==0 || glob_match(pInclude, "manifest.tags")) && !glob_match(pExclude, "manifest.tags") && (flg & MFESTFLG_TAGS) ){ eflg |= MFESTFLG_TAGS; } if( eflg & (MFESTFLG_RAW|MFESTFLG_UUID) ){ if( eflg & MFESTFLG_RAW ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); if( listFlag ) fossil_print("%s\n", zName); if( pTar ){ tar_add_file(zName, &mfile, 0, mTime); } } } blob_reset(&mfile); if( eflg & MFESTFLG_UUID ){ blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.uuid", -1); zName = blob_str(&filename); if( listFlag ) fossil_print("%s\n", zName); if( pTar ){ blob_append(&hash, "\n", 1); tar_add_file(zName, &hash, 0, mTime); } } if( eflg & MFESTFLG_TAGS ){ blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.tags", -1); zName = blob_str(&filename); if( listFlag ) fossil_print("%s\n", zName); if( pTar ){ Blob tagslist; blob_zero(&tagslist); get_checkin_taglist(rid, &tagslist); tar_add_file(zName, &tagslist, 0, mTime); blob_reset(&tagslist); } } } manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ int fid; if( pInclude!=0 && !glob_match(pInclude, pFile->zName) ) continue; if( glob_match(pExclude, pFile->zName) ) continue; fid = uuid_to_rid(pFile->zUuid, 0); if( fid ){ blob_resize(&filename, nPrefix); blob_append(&filename, pFile->zName, -1); zName = blob_str(&filename); if( listFlag ) fossil_print("%s\n", zName); if( pTar ){ content_get(fid, &file); tar_add_file(zName, &file, manifest_file_mperm(pFile), mTime); blob_reset(&file); } } } }else{ blob_append(&filename, blob_str(&hash), 16); zName = blob_str(&filename); if( listFlag ) fossil_print("%s\n", zName); if( pTar ){ mTime = db_int64(0, "SELECT (julianday('now') - 2440587.5)*86400.0;"); tar_begin(mTime); tar_add_file(zName, &mfile, 0, mTime); } } manifest_destroy(pManifest); blob_reset(&mfile); blob_reset(&hash); blob_reset(&filename); if( pTar ) tar_finish(pTar); } /* ** COMMAND: tarball* ** ** Usage: %fossil tarball VERSION OUTPUTFILE [OPTIONS] ** ** 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 ** name is derived from the project name, the check-in date and time, and ** the artifact ID of the check-in. ** ** The GLOBLIST argument to --exclude and --include can be a comma-separated ** list of glob patterns, where each glob pattern may optionally be enclosed ** in "..." or '...' so that it may contain commas. If a file matches both ** --include and --exclude then it is excluded. ** ** If OUTPUTFILE is an empty string or "/dev/null" then no tarball is ** actually generated. This feature can be used in combination with ** the --list option to get a list of the filenames that would be in the ** tarball had it actually been generated. Note that --list shows only ** filenames. "tar tzf" shows both filenames and subdirectory names. ** ** Options: ** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude ** --include GLOBLIST Comma-separated list of GLOBs of files to include ** -l|--list Show archive content on stdout ** --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; Glob *pInclude = 0; Glob *pExclude = 0; const char *zInclude; const char *zExclude; int listFlag = 0; const char *zOut; zName = find_option("name", 0, 1); zExclude = find_option("exclude", "X", 1); if( zExclude ) pExclude = glob_create(zExclude); zInclude = find_option("include", 0, 1); if( zInclude ) pInclude = glob_create(zInclude); db_find_and_open_repository(0, 0); listFlag = find_option("list","l",0)!=0; /* We should be done with options.. */ verify_all_options(); if( g.argc!=4 ){ usage("VERSION OUTPUTFILE"); } g.zOpenRevision = g.argv[2]; rid = name_to_typed_rid(g.argv[2], "ci"); if( rid==0 ){ fossil_fatal("Check-in not found: %s", g.argv[2]); return; } zOut = g.argv[3]; if( fossil_strcmp("/dev/null",zOut)==0 || fossil_strcmp("",zOut)==0 ){ zOut = 0; } if( zName==0 ){ zName = db_text("default-name", "SELECT replace(%Q,' ','_') " " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) " " || substr(blob.uuid, 1, 10)" " FROM event, blob" " WHERE event.objid=%d" " AND blob.rid=%d", db_get("project-name", "unnamed"), rid, rid ); } tarball_of_checkin(rid, zOut ? &tarball : 0, zName, pInclude, pExclude, listFlag); glob_free(pInclude); glob_free(pExclude); if( listFlag ) fflush(stdout); if( zOut ){ blob_write_to_file(&tarball, zOut); blob_reset(&tarball); } } /* ** Check to see if the input string is of the form: ** ** check-in-name/filename.ext ** ** In other words, check to see if the input contains a single '/' ** character that separates a valid check-in name from a filename. ** ** If the condition is true, return the check-in name and set the ** input string to be the filename. ** ** If the condition is false, return NULL */ char *tar_uuid_from_name(char **pzName){ char *zName = *pzName; int i, n; for(i=n=0; zName[i]; i++){ if( zName[i]=='/' ){ if( n==0 ) n = i; else return 0; } } if( n==0 ) return 0; if( zName[n+1]==0 ) return 0; zName[n] = 0; *pzName = fossil_strdup(&zName[n+1]); return zName; } /* ** WEBPAGE: tarball ** URL: /tarball/[VERSION/]NAME.tar.gz ** ** Generate a compressed tarball for the check-in specified by VERSION. ** The tarball is called NAME.tar.gz and has a top-level directory called ** NAME. ** ** The optional VERSION element defaults to "trunk" per the r= rules below. ** All of the following URLs are equivalent: ** ** /tarball/release/xyz.tar.gz ** /tarball?r=release&name=xyz.tar.gz ** /tarball/xyz.tar.gz?r=release ** /tarball?name=release/xyz.tar.gz ** ** Query parameters: ** ** name=[CKIN/]NAME The optional CKIN component of the name= parameter ** identifies the check-in from which the tarball is ** constructed. If CKIN is omitted and there is no ** r= query parameter, then use "trunk". NAME is the ** name of the download file. The top-level directory ** in the generated tarball is called by NAME with the ** file extension removed. ** ** r=TAG TAG identifies the check-in that is turned into a ** compressed tarball. The default value is "trunk". ** If r= is omitted and if the name= query parameter ** contains one "/" character then the of part the ** name= value before the / becomes the TAG and the ** part of the name= value after the / is the download ** filename. If no check-in is specified by either ** name= or r=, then "trunk" is used. ** ** in=PATTERN Only include files that match the comma-separate ** list of GLOB patterns in PATTERN, as with ex= ** ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a ** comma-separated list of GLOB patterns, where each ** pattern can optionally be quoted using ".." or '..'. ** Any file matching both ex= and in= is excluded. */ void tarball_page(void){ int rid; char *zName, *zRid, *zKey; int nName, nRid; const char *zInclude; /* The in= query parameter */ const char *zExclude; /* The ex= query parameter */ Blob cacheKey; /* The key to cache */ Glob *pInclude = 0; /* The compiled in= glob pattern */ Glob *pExclude = 0; /* The compiled ex= glob pattern */ Blob tarball; /* Tarball accumulated here */ const char *z; login_check_credentials(); if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; } fossil_nice_default(); zName = fossil_strdup(PD("name","")); z = P("r"); if( z==0 ) z = P("uuid"); if( z==0 ) z = tar_uuid_from_name(&zName); if( z==0 ) z = "trunk"; g.zOpenRevision = zRid = fossil_strdup(z); nRid = strlen(zRid); zInclude = P("in"); if( zInclude ) pInclude = glob_create(zInclude); zExclude = P("ex"); if( zExclude ) pExclude = glob_create(zExclude); if( zInclude==0 && zExclude==0 ){ etag_check_for_invariant_name(z); } nName = strlen(zName); if( nName>7 && fossil_strcmp(&zName[nName-7], ".tar.gz")==0 ){ /* Special case: Remove the ".tar.gz" suffix. */ nName -= 7; zName[nName] = 0; }else{ /* If the file suffix is not ".tar.gz" 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 = symbolic_name_to_rid(nRid?zRid:zName, "ci"); if( rid==0 ){ cgi_set_status(404, "Not Found"); @ Not found return; } if( nRid==0 && nName>10 ) zName[10] = 0; /* Compute a unique key for the cache entry based on query parameters */ blob_init(&cacheKey, 0, 0); blob_appendf(&cacheKey, "/tarball/%z", rid_to_uuid(rid)); blob_appendf(&cacheKey, "/%q", zName); if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude); if( zExclude ) blob_appendf(&cacheKey, ",ex=%Q", zExclude); zKey = blob_str(&cacheKey); etag_check(ETAG_HASH, zKey); if( P("debug")!=0 ){ style_header("Tarball Generator Debug Screen"); @ zName = "%h(zName)"<br> @ rid = %d(rid)<br> if( zInclude ){ @ zInclude = "%h(zInclude)"<br> } if( zExclude ){ @ zExclude = "%h(zExclude)"<br> } @ zKey = "%h(zKey)" style_finish_page(); return; } if( referred_from_login() ){ style_header("Tarball Download"); @ <form action='%R/tarball/%h(zName).tar.gz'> 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_finish_page(); return; } cgi_check_for_malice(); blob_zero(&tarball); if( cache_read(&tarball, zKey)==0 ){ tarball_of_checkin(rid, &tarball, zName, pInclude, pExclude, 0); cache_write(&tarball, zKey); } glob_free(pInclude); glob_free(pExclude); fossil_free(zName); fossil_free(zRid); g.zOpenRevision = 0; blob_reset(&cacheKey); cgi_set_content(&tarball); cgi_set_content_type("application/x-compressed"); } |
Added src/terminal.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 | /* ** Copyright (c) 2020 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 query terminal info */ #include "config.h" #include "terminal.h" #include <assert.h> #ifdef _WIN32 # include <windows.h> #else #ifdef __EXTENSIONS__ #include <termio.h> #endif #include <sys/ioctl.h> #include <stdio.h> #include <unistd.h> #endif #if INTERFACE /* ** Terminal size defined in terms of columns and lines. */ struct TerminalSize { unsigned int nColumns; /* Number of characters on a single line */ unsigned int nLines; /* Number of lines */ }; #endif /* Get the current terminal size by calling a system service. ** ** Return 1 on success. This sets the size parameters to the values retured by ** the system call, when such is supported; set the size to zero otherwise. ** Return 0 on the system service call failure. ** ** Under Linux/bash the size info is also available from env $LINES, $COLUMNS. ** Or it can be queried using tput `echo -e "lines\ncols"|tput -S`. ** Technically, this info could be cached, but then we'd need to handle ** SIGWINCH signal to requery the terminal on resize event. */ int terminal_get_size(TerminalSize *t){ memset(t, 0, sizeof(*t)); #if defined(TIOCGSIZE) { struct ttysize ts; if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)!=-1 ){ t->nColumns = ts.ts_cols; t->nLines = ts.ts_lines; return 1; } return 0; } #elif defined(TIOCGWINSZ) { struct winsize ws; if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)!=-1 ){ t->nColumns = ws.ws_col; t->nLines = ws.ws_row; return 1; } return 0; } #elif defined(_WIN32) { CONSOLE_SCREEN_BUFFER_INFO csbi; if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){ t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1; t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; return 1; } return 0; } #else return 1; #endif } /* ** Return the terminal's current width in columns when available, otherwise ** return the specified default value. */ unsigned int terminal_get_width(unsigned int nDefault){ TerminalSize ts; if( terminal_get_size(&ts) ){ return ts.nColumns; } return nDefault; } /* ** Return the terminal's current height in lines when available, otherwise ** return the specified default value. */ unsigned int terminal_get_height(unsigned int nDefault){ TerminalSize ts; if( terminal_get_size(&ts) ){ return ts.nLines; } return nDefault; } /* ** COMMAND: test-terminal-size ** ** Show the size of the terminal window from which the command is launched ** as two integers, the width in characters and the height in lines. ** ** If the size cannot be determined, two zeros are shown. */ void test_terminal_size_cmd(void){ TerminalSize ts; terminal_get_size(&ts); fossil_print("%d %d\n", ts.nColumns, ts.nLines); } |
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | /* ** 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> /* ** Values used for element values in the tcl_platform array. */ #if !defined(TH_ENGINE) # define TH_ENGINE "TH1" #endif #if !defined(TH_PLATFORM) # if defined(_WIN32) || defined(WIN32) # define TH_PLATFORM "windows" # else # define TH_PLATFORM "unix" # endif #endif /* ** Forward declarations for structures defined below. */ typedef struct Th_Command Th_Command; typedef struct Th_Frame Th_Frame; typedef struct Th_Variable Th_Variable; typedef struct Th_InterpAndList Th_InterpAndList; /* ** 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 | | | | | | | | 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 | ** 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 */ }; |
︙ | ︙ | |||
75 76 77 78 79 80 81 | ** is stored in Th_Variable.nRef. ** ** For scalar variables, Th_Variable.zData is never 0. Th_Variable.nData ** stores the number of bytes in the value pointed to by zData. ** ** For an array variable, Th_Variable.zData is 0 and pHash points to ** a hash table mapping between array key name (a th1 string) and | | | > > > > > > > > > > > | | | | > | | | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > | | | | | | | < < < | < < < < < | | | > > > > | > > | > > | | < < | 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 | ** is stored in Th_Variable.nRef. ** ** For scalar variables, Th_Variable.zData is never 0. Th_Variable.nData ** stores the number of bytes in the value pointed to by zData. ** ** For an array variable, Th_Variable.zData is 0 and pHash points to ** 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 */ }; /* ** This structure is used to pass complete context information to the ** hash iteration callback functions that need a Th_Interp and a list ** to operate on, e.g. thListAppendHashKey(). */ struct Th_InterpAndList { Th_Interp *interp; /* Associated interpreter context */ char **pzList; /* IN/OUT: Ptr to ptr to list */ int *pnList; /* IN/OUT: Current length of *pzList */ }; /* ** 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. */ static int thSubstWord(Th_Interp*, const char *z, int n); /* ** The Buffer structure and the thBufferXXX() functions are used to make ** memory allocation easier when building up a result. */ struct Buffer { char *zBuf; int nBuf; int nBufAlloc; }; typedef struct Buffer Buffer; static void thBufferInit(Buffer *); static void thBufferFree(Th_Interp *interp, Buffer *); /* ** This version of memcpy() allows the first and second argument to ** be NULL as long as the number of bytes to copy is zero. */ static void th_memcpy(void *dest, const void *src, size_t n){ if( n>0 ) memcpy(dest,src,n); } /* ** 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 void thBufferWriteResize( Th_Interp *interp, Buffer *pBuffer, const char *zAdd, int nAdd ){ int nNew = (pBuffer->nBuf+nAdd)*2+32; #if defined(TH_MEMDEBUG) char *zNew = (char *)Th_Malloc(interp, nNew); th_memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf); Th_Free(interp, pBuffer->zBuf); pBuffer->zBuf = zNew; #else int nOld = pBuffer->nBufAlloc; pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew); memset(pBuffer->zBuf+nOld, 0, nNew-nOld); #endif pBuffer->nBufAlloc = nNew; th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd); pBuffer->nBuf += nAdd; } static void thBufferWriteFast( Th_Interp *interp, Buffer *pBuffer, const char *zAdd, int nAdd ){ if( pBuffer->nBuf+nAdd > pBuffer->nBufAlloc ){ thBufferWriteResize(interp, pBuffer, zAdd, nAdd); }else{ if( pBuffer->zBuf ){ memcpy(pBuffer->zBuf + pBuffer->nBuf, zAdd, nAdd); } pBuffer->nBuf += nAdd; } } #define thBufferWrite(a,b,c,d) thBufferWriteFast(a,b,(const char *)c,d) /* ** Add a single character to a buffer */ static void thBufferAddChar( Th_Interp *interp, Buffer *pBuffer, char c ){ if( pBuffer->nBuf+1 > pBuffer->nBufAlloc ){ thBufferWriteResize(interp, pBuffer, &c, 1); }else{ pBuffer->zBuf[pBuffer->nBuf++] = c; } } /* ** Initialize the Buffer structure pointed to by pBuffer. */ static void thBufferInit(Buffer *pBuffer){ memset(pBuffer, 0, sizeof(Buffer)); } |
︙ | ︙ | |||
250 251 252 253 254 255 256 | case 'f': case 'F': return 15; } return -1; } /* ** Argument pEntry points to an entry in a stack frame hash table | | > > | > > > > > | > > > > > > > > > > > > > > > > | 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 | case 'f': case 'F': return 15; } return -1; } /* ** 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; } /* ** Argument pEntry points to an entry in a hash table. The key is ** the list element to be added. ** ** Argument pContext is a pointer to the Th_InterpAndList structure. ** ** Always returns non-zero. */ static int thListAppendHashKey(Th_HashEntry *pEntry, void *pContext){ Th_InterpAndList *pInterpAndList = (Th_InterpAndList *)pContext; Th_ListAppend(pInterpAndList->interp, pInterpAndList->pzList, pInterpAndList->pnList, pEntry->zKey, pEntry->nKey); 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; } /* | | | | | 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | 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 | | | | | | 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 | } *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 | | | | | | | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | *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; } /* | | | | | | | | | | 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 | *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; } /* | > > | 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | 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 | | | | 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 | 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 ){ |
︙ | ︙ | |||
558 559 560 561 562 563 564 | int rc = thSubstWord(interp, &zWord[i+1], nWord-i-2); if( rc!=TH_OK ) return rc; zInner = Th_GetResult(interp, &nInner); thBufferInit(&varname); thBufferWrite(interp, &varname, &zWord[1], i); thBufferWrite(interp, &varname, zInner, nInner); | | | | 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 | int rc = thSubstWord(interp, &zWord[i+1], nWord-i-2); if( rc!=TH_OK ) return rc; zInner = Th_GetResult(interp, &nInner); thBufferInit(&varname); thBufferWrite(interp, &varname, &zWord[1], i); thBufferWrite(interp, &varname, zInner, nInner); thBufferAddChar(interp, &varname, ')'); rc = Th_GetVar(interp, varname.zBuf, varname.nBuf); thBufferFree(interp, &varname); return rc; } } 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 | | | | | | | | | | 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 | 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 ){ int rc = TH_OK; Buffer output; int i; thBufferInit(&output); if( nWord>1 && (zWord[0]=='{' && zWord[nWord-1]=='}') ){ thBufferWrite(interp, &output, &zWord[1], nWord-2); }else{ /* If the word is surrounded by double-quotes strip these away. */ if( nWord>1 && (zWord[0]=='"' && zWord[nWord-1]=='"') ){ zWord++; nWord -= 2; } for(i=0; rc==TH_OK && i<nWord; i++){ 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: { thBufferAddChar(interp, &output, zWord[i]); continue; /* Go to the next iteration of the for(...) loop */ } } rc = xGet(interp, &zWord[i], nWord-i, &nGet); if( rc==TH_OK ){ rc = xSubst(interp, &zWord[i], nGet); } if( rc==TH_OK ){ const char *zRes; int nRes; zRes = Th_GetResult(interp, &nRes); thBufferWrite(interp, &output, zRes, nRes); i += (nGet-1); } } } if( rc==TH_OK ){ Th_SetResult(interp, output.zBuf, output.nBuf); } thBufferFree(interp, &output); return rc; } /* ** 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); ** | | | | | 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 | ** 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; |
︙ | ︙ | |||
761 762 763 764 765 766 767 | goto finish; } zInput = &zInput[nWord]; nInput = nList-(zInput-zList); if( nWord>0 ){ zWord = Th_GetResult(interp, &nWord); thBufferWrite(interp, &strbuf, zWord, nWord); | | | | | | | | | 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 | goto finish; } zInput = &zInput[nWord]; nInput = nList-(zInput-zList); if( nWord>0 ){ zWord = Th_GetResult(interp, &nWord); thBufferWrite(interp, &strbuf, zWord, nWord); thBufferAddChar(interp, &strbuf, 0); thBufferWrite(interp, &lenbuf, &nWord, sizeof(int)); nCount++; } } assert((int)(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]; th_memcpy(anElem, lenbuf.zBuf, lenbuf.nBuf); th_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; } /* |
︙ | ︙ | |||
844 845 846 847 848 849 850 | continue; } /* Gobble up input a word at a time until the end of the command ** (a semi-colon or end of line). */ while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){ | | | 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 | continue; } /* Gobble up input a word at a time until the end of the command ** (a semi-colon or end of line). */ while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){ int nWord=0; thNextSpace(interp, zInput, nInput, &nSpace); rc = thNextWord(interp, &zInput[nSpace], nInput-nSpace, &nWord, 1); zInput += (nSpace+nWord); nInput -= (nSpace+nWord); } if( rc!=TH_OK ) continue; |
︙ | ︙ | |||
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); } | | | | | 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 | /* 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. ** | | | | | 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 | ** 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 | | | | | 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 | 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, | | | | 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 | ** 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 | > > > > > > > > > > > > > > > | | > | | | | > > > > > > > | > > > > | > > > > > > | > > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | | | | > > > > > > > > | | | 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 | *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); } /* ** If interp has a variable with the given name, its value is returned ** and its length is returned via *nOut if nOut is not NULL. If ** interp has no such var then NULL is returned without setting any ** error state and *nOut, if not NULL, is set to -1. The returned value ** is owned by the interpreter and may be invalidated the next time ** the interpreter is modified. */ const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName, int *nOut){ Th_Variable *pValue; pValue = thFindValue(interp, zVarName, -1, 0, 0, 1, 0); if( !pValue || !pValue->zData ){ if( nOut!=0 ) *nOut = -1; return NULL; } if( nOut!=0 ) *nOut = pValue->nData; return pValue->zData; } /* ** 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); } /* ** Return true if array variable (zVar, nVar) exists. */ int Th_ExistsArrayVar(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); } if( pValue->zData ){ Th_Free(interp, pValue->zData); pValue->zData = 0; } assert(zValue || nValue==0); pValue->zData = Th_Malloc(interp, nValue+1); pValue->zData[nValue] = '\0'; th_memcpy(pValue->zData, zValue, nValue); pValue->nData = nValue; return TH_OK; } /* ** 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. */ char *th_strdup(Th_Interp *interp, const char *z, int n){ char *zRes; if( n<0 ){ n = th_strlen(z); } zRes = Th_Malloc(interp, n+1); th_memcpy(zRes, z, n); zRes[n] = '\0'; return zRes; } /* ** Argument zPre must be a nul-terminated string. Set the interpreter ** result to a string containing the contents of zPre, followed by |
︙ | ︙ | |||
1281 1282 1283 1284 1285 1286 1287 | */ 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); | | | 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 | */ 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); |
︙ | ︙ | |||
1316 1317 1318 1319 1320 1321 1322 | if( n<0 ){ n = th_strlen(z); } if( z && n>0 ){ char *zResult; zResult = Th_Malloc(pInterp, n+1); | | | 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 | if( n<0 ){ n = th_strlen(z); } if( z && n>0 ){ char *zResult; zResult = Th_Malloc(pInterp, n+1); th_memcpy(zResult, z, n); zResult[n] = '\0'; pInterp->zResult = zResult; pInterp->nResult = n; } return TH_OK; } |
︙ | ︙ | |||
1362 1363 1364 1365 1366 1367 1368 | pInterp->nResult = 0; return zResult; }else{ return (char *)Th_Malloc(pInterp, 1); } } | | | | | > | | > | > > | > > | > > | | | > > | | | 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 | pInterp->nResult = 0; return zResult; }else{ return (char *)Th_Malloc(pInterp, 1); } } #if defined(TH_MEMDEBUG) /* ** Wrappers around the supplied malloc() and free() */ void *Th_DbgMalloc(Th_Interp *pInterp, int nByte){ void *p; Th_Vtab *pVtab = pInterp->pVtab; if( pVtab ){ p = pVtab->xMalloc(nByte); if( p ) memset(p, 0, nByte); }else{ p = Th_SysMalloc(pInterp, nByte); } return p; } void Th_DbgFree(Th_Interp *pInterp, void *z){ if( z ){ Th_Vtab *pVtab = pInterp->pVtab; if( pVtab ){ pVtab->xFree(z); }else{ Th_SysFree(pInterp, z); } } } #endif /* ** 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; |
︙ | ︙ | |||
1407 1408 1409 1410 1411 1412 1413 | }else{ pCommand = Th_Malloc(interp, sizeof(Th_Command)); } pCommand->xProc = xProc; pCommand->pContext = pContext; pCommand->xDel = xDel; pEntry->pData = (void *)pCommand; | | | | | | | | 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 | }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 ){ |
︙ | ︙ | |||
1481 1482 1483 1484 1485 1486 1487 | ** 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 | | | 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 | ** 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: |
︙ | ︙ | |||
1507 1508 1509 1510 1511 1512 1513 | ** } ** ** Th_Free(interp, azElem); ** */ int Th_SplitList( Th_Interp *interp, | | | | | | | | | | | | | > > > > > > > > > | | | | | | | | | > > > > > > > > > > > > | > > > | | | > > > > > > | | 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 | ** } ** ** 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; /* Whitespace or {}[]'" */ int hasEscapeChar = 0; /* '}' without matching '{' to the left or a '\\' */ int nBrace = 0; output.zBuf = *pzList; output.nBuf = *pnList; output.nBufAlloc = output.nBuf; if( nElem<0 ){ nElem = th_strlen(zElem); } if( output.nBuf>0 ){ thBufferAddChar(interp, &output, ' '); } for(i=0; i<nElem; i++){ char c = zElem[i]; if( th_isspecial(c) ) hasSpecialChar = 1; if( c=='\\' ){ hasEscapeChar = 1; break; } if( c=='{' ) nBrace++; if( c=='}' ){ if( nBrace==0 ){ /* A closing brace that does not have a matching open brace to ** its left needs to be excaped. See ticket 4d73b4a2258a78e2 */ hasEscapeChar = 1; break; }else{ nBrace--; } } } if( nElem==0 || (!hasEscapeChar && hasSpecialChar && nBrace==0) ){ thBufferAddChar(interp, &output, '{'); thBufferWrite(interp, &output, zElem, nElem); thBufferAddChar(interp, &output, '}'); }else{ for(i=0; i<nElem; i++){ char c = zElem[i]; if( th_isspecial(c) ) thBufferAddChar(interp, &output, '\\'); thBufferAddChar(interp, &output, c); } } *pzList = output.zBuf; *pnList = output.nBuf; return TH_OK; } /* ** 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); } nNew = *pnStr + nElem; zNew = Th_Malloc(interp, nNew); th_memcpy(zNew, *pzStr, *pnStr); th_memcpy(&zNew[*pnStr], zElem, nElem); Th_Free(interp, *pzStr); *pzStr = zNew; *pnStr = nNew; return TH_OK; } /* ** Initialize an interpreter. */ static int thInitialize(Th_Interp *interp){ assert(interp->pFrame); Th_SetVar(interp, (char *)"::tcl_platform(engine)", -1, TH_ENGINE, -1); Th_SetVar(interp, (char *)"::tcl_platform(platform)", -1, TH_PLATFORM, -1); 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){ int nByte = sizeof(Th_Interp) + sizeof(Th_Frame); Th_Interp *p; /* Allocate and initialise the interpreter and the global frame */ #if defined(TH_MEMDEBUG) if( pVtab ){ p = pVtab->xMalloc(nByte); memset(p, 0, nByte); p->pVtab = pVtab; }else #endif p = Th_SysMalloc(0, nByte); p->paCmd = Th_HashNew(p); thPushFrame(p, (Th_Frame *)&p[1]); thInitialize(p); return p; } /* ** 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 |
︙ | ︙ | |||
1730 1731 1732 1733 1734 1735 1736 | */ #define ARG_INTEGER 1 #define ARG_NUMBER 2 #define ARG_STRING 3 static Operator aOperator[] = { | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | */ #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; } |
︙ | ︙ | |||
1815 1816 1817 1818 1819 1820 1821 | if( pExpr->pOp==0 ){ /* A literal */ rc = thSubstWord(interp, pExpr->zValue, pExpr->nValue); }else{ int eArgType = 0; /* Actual type of arguments */ /* Argument values */ | | | | 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 | if( pExpr->pOp==0 ){ /* A literal */ rc = thSubstWord(interp, pExpr->zValue, pExpr->nValue); }else{ int eArgType = 0; /* Actual type of arguments */ /* Argument values */ int iLeft = 0; int iRight = 0; double fLeft; double fRight; /* Left and right arguments as strings */ char *zLeft = 0; int nLeft = 0; char *zRight = 0; int nRight = 0; |
︙ | ︙ | |||
1846 1847 1848 1849 1850 1851 1852 | 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; | | | | > > > > > > > | > > > > > > > > | > > > > > > | > | | | | | | | | > > > > | 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 | 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; } |
︙ | ︙ | |||
1937 1938 1939 1940 1941 1942 1943 | 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; | | | 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 | 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; } |
︙ | ︙ | |||
1963 1964 1965 1966 1967 1968 1969 | } } } iLeft = 0; for(jj=nToken-1; jj>=0; jj--){ if( apToken[jj] ){ | | > < < | | 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 | } } } 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]; |
︙ | ︙ | |||
2011 2012 2013 2014 2015 2016 2017 | } /* ** Parse a string containing a TH expression to a list of tokens. */ static int exprParse( Th_Interp *interp, /* Interpreter to leave error message in */ | | > > > > > > | | | 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 | } /* ** 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; |
︙ | ︙ | |||
2058 2059 2060 2061 2062 2063 2064 | pNew->nValue = iEnd+1-i; } break; } default: { int j; | > | | > > > > > > > > > > > > > > > > > > > > | < | | | | > > > > | | | 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 | 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); th_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)); th_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); |
︙ | ︙ | |||
2166 2167 2168 2169 2170 2171 2172 | 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 | | > | | | | > | | | | 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 | 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; |
︙ | ︙ | |||
2253 2254 2255 2256 2257 2258 2259 | pRet = 0; } if( op>0 && !pRet ){ pRet = (Th_HashEntry *)Th_Malloc(interp, sizeof(Th_HashEntry) + nKey); pRet->zKey = (char *)&pRet[1]; pRet->nKey = nKey; | | | 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 | pRet = 0; } if( op>0 && !pRet ){ pRet = (Th_HashEntry *)Th_Malloc(interp, sizeof(Th_HashEntry) + nKey); pRet->zKey = (char *)&pRet[1]; pRet->nKey = nKey; th_memcpy(pRet->zKey, zKey, nKey); pRet->pNext = pHash->a[iKey]; pHash->a[iKey] = pRet; } return pRet; } |
︙ | ︙ | |||
2285 2286 2287 2288 2289 2290 2291 | ** '\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. | | > | | | | 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 | ** '\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. */ |
︙ | ︙ | |||
2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 | } 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 | > > > > > > > > > > > > < | 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 | } 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). |
︙ | ︙ | |||
2437 2438 2439 2440 2441 2442 2443 | } *pResult = sign<0 ? -v1 : v1; return z - zBegin; } /* ** Try to convert the string passed as arguments (z, n) to an integer. | | | | > > | > > > > > > > > > > > > > > > > | > > > > > > > | | | | | | | > | | | | | | | | | | | 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 | } *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; } sqlite3AtoF((const char *)z, pfOut); return TH_OK; } /* ** Set the result of the interpreter to the th1 representation of ** the integer iVal and return TH_OK. */ int Th_SetResultInt(Th_Interp *interp, int iVal){ int isNegative = 0; unsigned int uVal = iVal; char zBuf[32]; char *z = &zBuf[32]; if( iVal<0 ){ isNegative = 1; uVal = iVal * -1; } *(--z) = '\0'; *(--z) = (char)(48+(uVal%10)); while( (uVal = (uVal/10))>0 ){ *(--z) = (char)(48+(uVal%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 |
︙ | ︙ | |||
2603 2604 2605 2606 2607 2608 2609 | *z++ = zExp[i]; } } *z = '\0'; return Th_SetResult(interp, zBuf, -1); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | *z++ = zExp[i]; } } *z = '\0'; return Th_SetResult(interp, zBuf, -1); } /* ** Appends all currently registered command names to the specified list ** and returns TH_OK upon success. Any other return value indicates an ** error. */ int Th_ListAppendCommands( Th_Interp *interp, /* Interpreter context */ char **pzList, /* OUT: List of command names */ int *pnList /* OUT: Number of command names */ ){ Th_InterpAndList *p = (Th_InterpAndList *)Th_Malloc( interp, sizeof(Th_InterpAndList) ); p->interp = interp; p->pzList = pzList; p->pnList = pnList; Th_HashIterate(interp, interp->paCmd, thListAppendHashKey, p); Th_Free(interp, p); return TH_OK; } /* ** Appends all variable names for the current frame to the specified list ** and returns TH_OK upon success. Any other return value indicates an ** error. If the current frame cannot be obtained, TH_ERROR is returned. */ int Th_ListAppendVariables( Th_Interp *interp, /* Interpreter context */ char **pzList, /* OUT: List of variable names */ int *pnList /* OUT: Number of variable names */ ){ Th_Frame *pFrame = getFrame(interp, 0); if( pFrame ){ Th_InterpAndList *p = (Th_InterpAndList *)Th_Malloc( interp, sizeof(Th_InterpAndList) ); p->interp = interp; p->pzList = pzList; p->pnList = pnList; Th_HashIterate(interp, pFrame->paVar, thListAppendHashKey, p); Th_Free(interp, p); return TH_OK; }else{ return TH_ERROR; } } /* ** Appends all array element names for the specified array variable to the ** specified list and returns TH_OK upon success. Any other return value ** indicates an error. */ int Th_ListAppendArray( Th_Interp *interp, /* Interpreter context */ const char *zVar, /* Pointer to variable name */ int nVar, /* Number of bytes at nVar */ char **pzList, /* OUT: List of array element names */ int *pnList /* OUT: Number of array element names */ ){ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0); if( pValue && !pValue->zData && pValue->pHash ){ Th_InterpAndList *p = (Th_InterpAndList *)Th_Malloc( interp, sizeof(Th_InterpAndList) ); p->interp = interp; p->pzList = pzList; p->pnList = pnList; Th_HashIterate(interp, pValue->pHash, thListAppendHashKey, p); Th_Free(interp, p); }else{ *pzList = 0; *pnList = 0; } return TH_OK; } |
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 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 | /* 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 *); 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_ExistsArrayVar(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); /* ** If interp has a variable with the given name, its value is returned ** and its length is returned via *nOut if nOut is not NULL. If ** interp has no such var then NULL is returned without setting any ** error state and *nOut, if not NULL, is set to 0. The returned value ** is owned by the interpreter and may be invalidated the next time ** the interpreter is modified. ** ** zVarName must be NUL-terminated. */ const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName, int *nOut); 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 #define TH_RETURN2 5 /* ** 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. */ #if defined(TH_MEMDEBUG) void *Th_DbgMalloc(Th_Interp *, int); void Th_DbgFree(Th_Interp *, void *); #endif void *fossil_malloc_zero(size_t); void *fossil_realloc(void *, size_t); void fossil_free(void *); #define Th_SysMalloc(I,N) fossil_malloc_zero((N)) #define Th_SysRealloc(I,P,N) fossil_realloc((P),(N)) #define Th_SysFree(I,P) fossil_free((P)) #if defined(TH_MEMDEBUG) # define Th_Malloc(I,N) Th_DbgMalloc((I),(N)) # define Th_Free(I,P) Th_DbgFree((I),(P)) #else # define Th_Malloc(I,N) Th_SysMalloc((I),(N)) # define Th_Realloc(I,P,N) Th_SysRealloc((I),(P),(N)) # define Th_Free(I,P) Th_SysFree((I),(P)) #endif /* ** 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); /* ** Functions for handling command and variable introspection. */ int Th_ListAppendCommands(Th_Interp *, char **, int *); int Th_ListAppendVariables(Th_Interp *, char **, int *); int Th_ListAppendArray(Th_Interp *, const char *, int, char **, int *); /* ** 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,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; |
︙ | ︙ | |||
91 92 93 94 95 96 97 | 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 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 | 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: ** ** foreach VARLIST LIST SCRIPT */ static int foreach_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; char **azVar = 0; int *anVar; int nVar; char **azValue = 0; int *anValue; int nValue; int ii, jj; if( argc!=4 ){ return Th_WrongNumArgs(interp, "foreach varlist list script"); } rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar); if( rc ) return rc; rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue); for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){ for(jj=0; jj<nVar; jj++){ Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], anValue[ii+jj]); } rc = eval_loopbody(interp, argv[3], argl[3]); } if( rc==TH_BREAK ) rc = TH_OK; Th_Free(interp, azVar); Th_Free(interp, azValue); 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: ** ** lappend var ?arg1? ?arg2? ...? ** ** Interpret the content of variable var as a list. Create var if it ** does not already exist. Append each argument as a new list element. */ static int lappend_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ char *zList = 0; int nList = 0; int i, rc; if( argc<2 ){ return Th_WrongNumArgs(interp, "lappend var ..."); } rc = Th_GetVar(interp, argv[1], argl[1]); if( rc==TH_OK ){ zList = Th_TakeResult(interp, &nList); } for(i=2; i<argc; i++){ Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); } Th_SetVar(interp, argv[1], argl[1], zList, nList); 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; |
︙ | ︙ | |||
224 225 226 227 228 229 230 | Th_Free(interp, azElem); } return rc; } /* | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | 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 | 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: ** ** lsearch list string */ static int lsearch_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; char **azElem; int *anElem; int nCount; int i; if( argc!=3 ){ return Th_WrongNumArgs(interp, "lsearch list string"); } rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount); if( rc==TH_OK ){ Th_SetResultInt(interp, -1); for(i=0; i<nCount; i++){ if( anElem[i]==argl[2] && 0==memcmp(azElem[i], argv[2], argl[2]) ){ Th_SetResultInt(interp, i); break; } } Th_Free(interp, azElem); } 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; |
︙ | ︙ | |||
320 321 322 323 324 325 326 | 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. */ | | | 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | 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); |
︙ | ︙ | |||
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | if( p->hasArgs ){ char *zArgs = 0; int nArgs = 0; for(i=p->nParam+1; i<pArgs->argc; i++){ Th_ListAppend(interp, &zArgs, &nArgs, pArgs->argv[i], pArgs->argl[i]); } Th_SetVar(interp, (const char *)"args", -1, zArgs, nArgs); } Th_SetResult(interp, 0, 0); return Th_Eval(interp, 0, p->zProgram, p->nProgram); } /* ** 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, | > > > | | > > > | | | | | | | | | | | | | > | | | > | | 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 | if( p->hasArgs ){ char *zArgs = 0; int nArgs = 0; for(i=p->nParam+1; i<pArgs->argc; i++){ Th_ListAppend(interp, &zArgs, &nArgs, pArgs->argv[i], pArgs->argl[i]); } Th_SetVar(interp, (const char *)"args", -1, zArgs, nArgs); if(zArgs){ Th_Free(interp, zArgs); } } Th_SetResult(interp, 0, 0); return Th_Eval(interp, 0, p->zProgram, p->nProgram); } /* ** 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; /* Call function proc_call2(), which will call Th_Eval() to evaluate ** the body of the [proc], in a new Th stack frame. This is so that ** the proc body has its own local variable context. */ procargs.argc = argc; procargs.argv = argv; procargs.argl = argl; rc = Th_InFrame(interp, proc_call2, (void *)p, (void *)&procargs); if( rc==TH_RETURN ){ rc = TH_OK; } if( rc==TH_RETURN2 ){ rc = TH_RETURN; } 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( nParam>0 ){ 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; } |
︙ | ︙ | |||
515 516 517 518 519 520 521 | 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. */ | | | | | | | | | | | | | | | | | | 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 | 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 ){ |
︙ | ︙ | |||
632 633 634 635 636 637 638 | } if( iRes==0 ){ iRes = nLeft-nRight; } if( iRes<0 ) iRes = -1; if( iRes>0 ) iRes = 1; | | < < < < | > > > | > | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < | < < > > | < | | | | | | > > > > > > > > > > > > > > > > > > > > > > < < < | < | > > > | > | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } 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 index STRING INDEX */ static int string_index_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iIndex; if( argc!=4 ){ return Th_WrongNumArgs(interp, "string index string index"); } if( argl[3]==3 && 0==memcmp("end", argv[3], 3) ){ iIndex = argl[2]-1; }else if( Th_ToInt(interp, argv[3], argl[3], &iIndex) ){ Th_ErrorMessage( interp, "Expected \"end\" or integer, got:", argv[3], argl[3]); return TH_ERROR; } if( iIndex>=0 && iIndex<argl[2] ){ return Th_SetResult(interp, &argv[2][iIndex], 1); }else{ return Th_SetResult(interp, 0, 0); } } /* ** TH Syntax: ** ** string is CLASS STRING */ static int string_is_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=4 ){ return Th_WrongNumArgs(interp, "string is class string"); } if( argl[2]==5 && 0==memcmp(argv[2], "alnum", 5) ){ int i; int iRes = 1; for(i=0; i<argl[3]; i++){ if( !th_isalnum(argv[3][i]) ){ iRes = 0; } } return Th_SetResultInt(interp, iRes); }else if( argl[2]==6 && 0==memcmp(argv[2], "double", 6) ){ double fVal; if( Th_ToDouble(interp, argv[3], argl[3], &fVal)==TH_OK ){ return Th_SetResultInt(interp, 1); } return Th_SetResultInt(interp, 0); }else if( argl[2]==7 && 0==memcmp(argv[2], "integer", 7) ){ int iVal; if( Th_ToInt(interp, argv[3], argl[3], &iVal)==TH_OK ){ return Th_SetResultInt(interp, 1); } return Th_SetResultInt(interp, 0); }else if( argl[2]==4 && 0==memcmp(argv[2], "list", 4) ){ if( Th_SplitList(interp, argv[3], argl[3], 0, 0, 0)==TH_OK ){ return Th_SetResultInt(interp, 1); } return Th_SetResultInt(interp, 0); }else{ Th_ErrorMessage(interp, "Expected alnum, double, integer, or list, got:", argv[2], argl[2]); return TH_ERROR; } } /* ** 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 */ static int string_length_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=3 ){ return Th_WrongNumArgs(interp, "string length string"); } return Th_SetResultInt(interp, argl[2]); } /* ** TH Syntax: ** ** string match PATTERN STRING ** */ static int string_match_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ extern char *fossil_strndup(const char*,int); extern void fossil_free(void*); char *zPat, *zStr; int rc; if( argc!=4 ){ return Th_WrongNumArgs(interp, "string match pattern string"); } zPat = fossil_strndup(argv[2],argl[2]); zStr = fossil_strndup(argv[3],argl[3]); rc = sqlite3_strglob(zPat,zStr); fossil_free(zPat); fossil_free(zStr); return Th_SetResultInt(interp, !rc); } /* ** TH Syntax: ** ** string range STRING FIRST LAST */ static int string_range_command( |
︙ | ︙ | |||
812 813 814 815 816 817 818 | Th_Free(interp, zByte); return TH_OK; } /* ** TH Syntax: ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > | | | | | | | | > > > | > | | > | | | | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < | | | < < | > | | | | > | | | | | | 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 | Th_Free(interp, zByte); return TH_OK; } /* ** TH Syntax: ** ** string trim STRING ** string trimleft STRING ** string trimright STRING */ static int string_trim_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int n; const char *z; if( argc!=3 ){ return Th_WrongNumArgs(interp, "string trim string"); } z = argv[2]; n = argl[2]; if( argl[1]<5 || argv[1][4]=='l' ){ while( n && th_isspace(z[0]) ){ z++; n--; } } if( argl[1]<5 || argv[1][4]=='r' ){ while( n && th_isspace(z[n-1]) ){ n--; } } Th_SetResult(interp, z, n); return TH_OK; } /* ** TH Syntax: ** ** info exists VARNAME */ static int info_exists_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; if( argc!=3 ){ return Th_WrongNumArgs(interp, "info exists var"); } rc = Th_ExistsVar(interp, argv[2], argl[2]); Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH Syntax: ** ** info commands */ static int info_commands_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; char *zElem = 0; int nElem = 0; if( argc!=2 ){ return Th_WrongNumArgs(interp, "info commands"); } rc = Th_ListAppendCommands(interp, &zElem, &nElem); if( rc!=TH_OK ){ return rc; } Th_SetResult(interp, zElem, nElem); if( zElem ) Th_Free(interp, zElem); return TH_OK; } /* ** TH Syntax: ** ** info vars */ static int info_vars_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; char *zElem = 0; int nElem = 0; if( argc!=2 ){ return Th_WrongNumArgs(interp, "info vars"); } rc = Th_ListAppendVariables(interp, &zElem, &nElem); if( rc!=TH_OK ){ return rc; } Th_SetResult(interp, zElem, nElem); if( zElem ) Th_Free(interp, zElem); return TH_OK; } /* ** TH Syntax: ** ** array exists VARNAME */ static int array_exists_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; if( argc!=3 ){ return Th_WrongNumArgs(interp, "array exists var"); } rc = Th_ExistsArrayVar(interp, argv[2], argl[2]); Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH Syntax: ** ** array names VARNAME */ static int array_names_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; char *zElem = 0; int nElem = 0; if( argc!=3 ){ return Th_WrongNumArgs(interp, "array names varname"); } rc = Th_ListAppendArray(interp, argv[2], argl[2], &zElem, &nElem); if( rc!=TH_OK ){ return rc; } Th_SetResult(interp, zElem, nElem); if( zElem ) Th_Free(interp, zElem); return TH_OK; } /* ** TH Syntax: ** ** unset VARNAME */ 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]); }else{ Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], argl[1]); } return TH_ERROR; } /* ** TH Syntax: ** ** string compare STR1 STR2 ** string first NEEDLE HAYSTACK ?STARTINDEX? ** string index STRING INDEX ** string is CLASS STRING ** string last NEEDLE HAYSTACK ?STARTINDEX? ** string length STRING ** string range STRING FIRST LAST ** string repeat STRING COUNT ** string trim STRING ** string trimleft STRING ** string trimright STRING */ 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 }, { "index", string_index_command }, { "is", string_is_command }, { "last", string_last_command }, { "length", string_length_command }, { "match", string_match_command }, { "range", string_range_command }, { "repeat", string_repeat_command }, { "trim", string_trim_command }, { "trimleft", string_trim_command }, { "trimright", string_trim_command }, { 0, 0 } }; return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub); } /* ** TH Syntax: ** ** info commands ** info exists VARNAME ** info vars */ static int info_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ static const Th_SubCommand aSub[] = { { "commands", info_commands_command }, { "exists", info_exists_command }, { "vars", info_vars_command }, { 0, 0 } }; return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub); } /* ** TH Syntax: ** ** array exists VARNAME ** array names VARNAME */ static int array_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ static const Th_SubCommand aSub[] = { { "exists", array_exists_command }, { "names", array_names_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; |
︙ | ︙ | |||
950 951 952 953 954 955 956 | /* ** TH Syntax: ** ** uplevel ?LEVEL? SCRIPT */ static int uplevel_command( | | | | | | | | | | | | | | | > > > > | | | | | | > > | | 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 | /* ** 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 ){ static unsigned int cnt = 0; if( (cnt++)==0xffffffff ) printf("too many TH3 breakpoints\n"); return TH_OK; } /* ** Register the built-in th1 language commands with interpreter interp. ** Usually this is called soon after interpreter creation. */ int th_register_language(Th_Interp *interp){ /* Array of built-in commands. */ struct _Command { const char *zName; Th_CommandProc xProc; void *pContext; } aCommand[] = { {"array", array_command, 0}, {"catch", catch_command, 0}, {"expr", expr_command, 0}, {"for", for_command, 0}, {"foreach", foreach_command, 0}, {"if", if_command, 0}, {"info", info_command, 0}, {"lappend", lappend_command, 0}, {"lindex", lindex_command, 0}, {"list", list_command, 0}, {"llength", llength_command, 0}, {"lsearch", lsearch_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.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | ******************************************************************************* ** ** This file contains an interface between the TH scripting language ** (an independent project) and fossil. */ #include "config.h" #include "th_main.h" /* ** 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; /* ** Implementations of malloc() and free() to pass to the interpreter. */ static void *xMalloc(unsigned int n){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | | > | | | | > | | | | | | > > > > > > > > > > > > > > > > > > > > > > | | > > | | | | > > > > | > | > > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | | | | | | | | | | | < | > > | | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | 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 | ******************************************************************************* ** ** This file contains an interface between the TH scripting language ** (an independent project) and fossil. */ #include "config.h" #include "th_main.h" #include "sqlite3.h" #if INTERFACE /* ** 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_NO_REPO ((u32)0x00000010) /* Skip opening repository. */ #define TH_INIT_NO_ENCODE ((u32)0x00000020) /* Do not html-encode sendText()*/ /* output. */ #define TH_INIT_MASK ((u32)0x0000003F) /* 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)0x00000200) /* We opened the config. */ #define TH_STATE_REPOSITORY ((u32)0x00000400) /* We opened the repository. */ #define TH_STATE_MASK ((u32)0x00000600) /* 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) /* ** When memory debugging is enabled, use our custom memory allocator. */ #if defined(TH_MEMDEBUG) /* ** 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; /* ** Implementations of malloc() and free() to pass to the interpreter. */ static void *xMalloc(unsigned int n){ void *p = fossil_malloc(n); if( p ){ nOutstandingMalloc++; } return p; } 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; } #endif /* ** 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; if( g.thTrace ){ g.fAnyTrace = 1; blob_zero(&g.thLog); } } /* ** Prints the entire contents of the TH1 trace log to the standard ** output channel. */ 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"); } } /* ** - adopted from ls_cmd_rev in checkin.c ** - adopted commands/error handling for usage within th1 ** - interface adopted to allow result creation as TH1 List ** ** Takes a check-in identifier in zRev and an optiona glob pattern in zGLOB ** as parameter returns a TH list in pzList,pnList with filenames matching ** glob pattern with the checking */ static void dir_cmd_rev( Th_Interp *interp, char **pzList, int *pnList, const char *zRev, /* Revision string given */ const char *zGlob, /* Glob pattern given */ int bDetails ){ Stmt q; char *zOrderBy = "pathname COLLATE nocase"; int rid; rid = th1_name_to_typed_rid(interp, zRev, "ci"); compute_fileage(rid, zGlob); db_prepare(&q, "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n" " blob.size\n" " FROM fileage, blob\n" " WHERE blob.rid=fileage.fid \n" " ORDER BY %s;", zOrderBy /*safe-for-%s*/ ); while( db_step(&q)==SQLITE_ROW ){ const char *zFile = db_column_text(&q, 1); if( bDetails ){ const char *zTime = db_column_text(&q, 0); int size = db_column_int(&q, 2); char zSize[50]; char *zSubList = 0; int nSubList = 0; sqlite3_snprintf(sizeof(zSize), zSize, "%d", size); Th_ListAppend(interp, &zSubList, &nSubList, zFile, -1); Th_ListAppend(interp, &zSubList, &nSubList, zSize, -1); Th_ListAppend(interp, &zSubList, &nSubList, zTime, -1); Th_ListAppend(interp, pzList, pnList, zSubList, -1); Th_Free(interp, zSubList); }else{ Th_ListAppend(interp, pzList, pnList, zFile, -1); } } db_finalize(&q); } /* ** TH1 command: dir CHECKIN ?GLOB? ?DETAILS? ** ** Returns a list containing all files in CHECKIN. If GLOB is given only ** the files matching the pattern GLOB within CHECKIN will be returned. ** If DETAILS is non-zero, the result will be a list-of-lists, with each ** element containing at least three elements: the file name, the file ** size (in bytes), and the file last modification time (relative to the ** time zone configured for the repository). */ static int dirCmd( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ const char *zGlob = 0; int bDetails = 0; if( argc<2 || argc>4 ){ return Th_WrongNumArgs(interp, "dir CHECKIN ?GLOB? ?DETAILS?"); } if( argc>=3 ){ zGlob = argv[2]; } if( argc>=4 && Th_ToInt(interp, argv[3], argl[3], &bDetails) ){ return TH_ERROR; } if( Th_IsRepositoryOpen() ){ char *zList = 0; int nList = 0; dir_cmd_rev(interp, &zList, &nList, argv[1], zGlob, bDetails); Th_SetResult(interp, zList, nList); Th_Free(interp, zList); return TH_OK; }else{ Th_SetResult(interp, "repository unavailable", -1); return TH_ERROR; } } /* ** 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, wiki, combobox and copybtn 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; } /* ** TH1 command: enable_htmlify ?BOOLEAN? ** ** Enable or disable the HTML escaping done by all output which ** originates from TH1 (via sendText()). ** ** If passed no arguments it instead returns 0 or 1 to indicate the ** current state. */ static int enableHtmlifyCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int rc = 0, buul; if( argc>3 ){ return Th_WrongNumArgs(interp, "enable_htmlify [TRACE_LABEL] ?BOOLEAN?"); } buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1; Th_SetResultInt(g.interp, buul); if(argc>1){ if( g.thTrace ){ Th_Trace("enable_htmlify {%.*s} -> %d<br>\n", argl[1],argv[1],buul); } rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul); if(!rc){ if(buul){ g.th1Flags &= ~TH_INIT_NO_ENCODE; }else{ g.th1Flags |= TH_INIT_NO_ENCODE; } } } 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"; case TH_RETURN2: return "TH_RETURN2"; default: { sqlite3_snprintf(sizeof(zRc), zRc, "TH1 return code %d", rc); } } return zRc; } /* See Th_SetOutputBlob() */ static Blob * pThOut = 0; /* ** Sets the th1-internal output-redirection blob and returns the ** previous value. That blob is used by certain output-generation ** routines to emit its output. It returns the previous value so that ** a routine can temporarily replace the buffer with its own and ** restore it when it's done. */ Blob * Th_SetOutputBlob(Blob * pOut){ Blob * tmp = pThOut; pThOut = pOut; return tmp; } /* ** Send text to the appropriate output: If pOut is not NULL, it is ** appended there, else to the console or to the CGI reply buffer. ** Escape all characters with special meaning to HTML if the encode ** parameter is true, with the exception that that flag is ignored if ** g.th1Flags has the TH_INIT_NO_ENCODE flag. ** ** If pOut is NULL and the global pThOut is not then that blob ** is used for output. */ static void sendText(Blob * pOut, const char *z, int n, int encode){ if(0==pOut && pThOut!=0){ pOut = pThOut; } if(TH_INIT_NO_ENCODE & g.th1Flags){ encode = 0; } if( enableOutput && n ){ if( n<0 ) n = strlen(z); if( encode ){ z = htmlize(z, n); n = strlen(z); } if(pOut!=0){ blob_append(pOut, z, n); }else if( g.cgiOutput ){ cgi_append_content(z, n); }else{ fwrite(z, 1, n, stdout); fflush(stdout); } if( encode ) free((char*)z); } } /* ** error-reporting counterpart of sendText(). */ static void sendError(Blob * pOut, const char *z, int n, int forceCgi){ int savedEnable = enableOutput; enableOutput = 1; if( forceCgi || g.cgiOutput ){ sendText(pOut, "<hr><p class=\"thmainError\">", -1, 0); } sendText(pOut,"ERROR: ", -1, 0); sendText(pOut,(char*)z, n, 1); sendText(pOut,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: nonce ** ** Returns the value of the cryptographic nonce for the request being ** processed. */ static int nonceCmd( Th_Interp *interp, void *pConvert, int argc, const char **argv, int *argl ){ if( argc!=1 ){ return Th_WrongNumArgs(interp, "nonce"); } Th_SetResult(interp, style_nonce(), -1); return TH_OK; } /* ** TH1 command: puts STRING ** TH1 command: html STRING ** ** Output STRING escaped for HTML (puts) or unchanged (html). */ 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(0,(char*)argv[1], argl[1], *(unsigned int*)pConvert); return TH_OK; } /* ** TH1 command: redirect URL ?withMethod? ** ** Issues an HTTP redirect to the specified URL and then exits the process. ** By default, an HTTP status code of 302 is used. If the optional withMethod ** argument is present and non-zero, an HTTP status code of 307 is used, which ** should force the user agent to preserve the original method for the request ** (e.g. GET, POST) instead of (possibly) forcing the user agent to change the ** method to GET. */ static int redirectCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int withMethod = 0; if( argc!=2 && argc!=3 ){ return Th_WrongNumArgs(interp, "redirect URL ?withMethod?"); } if( argc==3 ){ if( Th_ToInt(interp, argv[2], argl[2], &withMethod) ){ return TH_ERROR; } } if( withMethod ){ cgi_redirect_with_method(argv[1]); }else{ cgi_redirect(argv[1]); } Th_SetResult(interp, argv[1], argl[1]); /* NOT REACHED */ return TH_OK; } /* ** TH1 command: insertCsrf ** ** While rendering a form, call this command to add the Anti-CSRF token ** as a hidden element of the form. */ static int insertCsrfCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=1 ){ return Th_WrongNumArgs(interp, "insertCsrf"); } login_insert_csrf_secret(); return TH_OK; } /* ** TH1 command: verifyCsrf ** ** Before using the results of a form, first call this command to verify ** that this Anti-CSRF token is present and is valid. If the Anti-CSRF token ** is missing or is incorrect, that indicates a cross-site scripting attack. ** If the event of an attack is detected, an error message is generated and ** all further processing is aborted. */ static int verifyCsrfCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=1 ){ return Th_WrongNumArgs(interp, "verifyCsrf"); } if( !cgi_csrf_safe(2) ){ fossil_fatal("possible CSRF attack"); } return TH_OK; } /* ** TH1 command: verifyLogin ** ** Returns non-zero if the specified user name and password represent a ** valid login for the repository. */ static int verifyLoginCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ const char *zUser; const char *zPass; int uid; if( argc!=3 ){ return Th_WrongNumArgs(interp, "verifyLogin userName password"); } zUser = argv[1]; zPass = argv[2]; uid = login_search_uid(&zUser, zPass); Th_SetResultInt(interp, uid!=0); if( uid==0 ) sqlite3_sleep(100); return TH_OK; } /* ** TH1 command: markdown STRING ** ** Renders the input string as markdown. The result is a two-element list. ** The first element is the text-only title string. The second element ** contains the body, rendered as HTML. */ static int markdownCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ Blob src, title, body; char *zValue = 0; int nValue = 0; if( argc!=2 ){ return Th_WrongNumArgs(interp, "markdown STRING"); } blob_zero(&src); blob_init(&src, (char*)argv[1], argl[1]); blob_zero(&title); blob_zero(&body); markdown_to_html(&src, &title, &body); Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title)); Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body)); Th_SetResult(interp, zValue, nValue); Th_Free(interp, zValue); return TH_OK; } /* ** TH1 command: decorate STRING ** TH1 command: wiki STRING ** ** Render the input string as wiki. For the decorate command, only links ** are handled. */ 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: encode64 STRING ** ** Encode the specified string using Base64 and return the result. */ static int encode64Cmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ char *zOut; if( argc!=2 ){ return Th_WrongNumArgs(interp, "encode64 STRING"); } zOut = encode64((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',toLocal())"); }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 = 1, i; char *zCapList = 0; int nCapList = 0; if( argc<2 ){ return Th_WrongNumArgs(interp, "hascap STRING ..."); } for(i=1; rc==1 && i<argc; i++){ if( g.thTrace ){ Th_ListAppend(interp, &zCapList, &nCapList, argv[i], argl[i]); } rc = login_has_capability((char*)argv[i],argl[i],*(int*)p); } if( g.thTrace ){ Th_Trace("[%s %#h] => %d<br>\n", argv[0], nCapList, zCapList, rc); Th_Free(interp, zCapList); } Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH1 command: capexpr CAPABILITY-EXPR ** ** Nmemonic: "CAPability EXPRression" ** ** The capability expression is a list. Each term of the list is a cluster ** of capability letters. The overall expression is true if any one term ** is true. A single term is true if all letters within that term are true. ** Or, if the term begins with "!", then the term is true if none of the ** terms or true. Or, if the term begins with "@" then the term is true ** if all of the capability letters in that term are available to the ** "anonymous" user. Or, if the term is "*" then it is always true. ** ** Examples: ** ** capexpr {j o r} True if any one of j, o, or r are available ** capexpr {oh} True if both o and h are available ** capexpr {@2 @3 4 5 6} 2 or 3 available for anonymous or one of ** 4, 5 or 6 is available for the user */ int capexprCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ char **azCap; int *anCap; int nCap; int rc; int i; if( argc!=2 ){ return Th_WrongNumArgs(interp, "capexpr EXPR"); } rc = Th_SplitList(interp, argv[1], argl[1], &azCap, &anCap, &nCap); if( rc ) return rc; rc = 0; for(i=0; i<nCap; i++){ if( azCap[i][0]=='!' ){ rc = !login_has_capability(azCap[i]+1, anCap[i]-1, 0); }else if( azCap[i][0]=='@' ){ rc = login_has_capability(azCap[i]+1, anCap[i]-1, LOGIN_ANON); }else if( azCap[i][0]=='*' ){ rc = 1; }else{ rc = login_has_capability(azCap[i], anCap[i], 0); } if( rc ) break; } Th_Free(interp, azCap); 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 ** "legacyMvRm" = FOSSIL_ENABLE_LEGACY_MV_RM ** "execRelPaths" = FOSSIL_ENABLE_EXEC_REL_PATHS ** "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 ** "dynamicBuild" = FOSSIL_DYNAMIC_BUILD ** "mman" = USE_MMAN_H ** "see" = USE_SEE ** ** Specifying an unknown feature will return a value of false, it will not ** raise a script error. */ 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 else if( 0 == fossil_strnicmp( zArg, "legacyMvRm\0", 11 ) ){ rc = 1; } #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) else if( 0 == fossil_strnicmp( zArg, "execRelPaths\0", 13 ) ){ 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) else if( 0 == fossil_strnicmp( zArg, "useTclStubs\0", 12 ) ){ rc = 1; } #endif #if defined(FOSSIL_ENABLE_TCL_STUBS) else if( 0 == fossil_strnicmp( zArg, "tclStubs\0", 9 ) ){ rc = 1; } #endif #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) else if( 0 == fossil_strnicmp( zArg, "tclPrivateStubs\0", 16 ) ){ 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 #if defined(FOSSIL_DYNAMIC_BUILD) else if( 0 == fossil_strnicmp( zArg, "dynamicBuild\0", 13 ) ){ rc = 1; } #endif #if defined(USE_MMAN_H) else if( 0 == fossil_strnicmp( zArg, "mman\0", 5 ) ){ rc = 1; } #endif #if defined(USE_SEE) else if( 0 == fossil_strnicmp( zArg, "see\0", 4 ) ){ 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, void *p, int argc, const char **argv, int *argl ){ int rc = 0; if( argc!=1 ){ return Th_WrongNumArgs(interp, "tclReady"); } #if defined(FOSSIL_ENABLE_TCL) if( g.tcl.interp ){ rc = 1; } #endif if( g.thTrace ){ Th_Trace("[tclReady] => %d<br>\n", rc); } 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("[anycap %#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; Blob name; int nValue; const char *zValue; char *z, *zH; int nElem; int *aszElem; char **azElem; int i; if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR; Th_SplitList(interp, argv[2], argl[2], &azElem, &aszElem, &nElem); blob_init(&name, (char*)argv[1], argl[1]); zValue = Th_Fetch(blob_str(&name), &nValue); zH = htmlize(blob_buffer(&name), blob_size(&name)); z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height); free(zH); sendText(0,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(0,z, -1, 0); free(z); } sendText(0,"</select>", -1, 0); Th_Free(interp, azElem); } return TH_OK; } /* ** TH1 command: copybtn TARGETID FLIPPED TEXT ?COPYLENGTH? ** ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js ** Javascript module, and generates HTML elements with the following IDs: ** ** TARGETID: The <span> wrapper around TEXT. ** copy-TARGETID: The <span> for the copy button. ** ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT. ** ** The optional COPYLENGTH argument defines the length of the substring of TEXT ** copied to clipboard: ** ** <= 0: No limit (default if the argument is omitted). ** >= 3: Truncate TEXT after COPYLENGTH (single-byte) characters. ** 1: Use the "hash-digits" setting as the limit. ** 2: Use the length appropriate for URLs as the limit (defined at ** compile-time by FOSSIL_HASH_DIGITS_URL, defaults to 16). */ static int copybtnCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=4 && argc!=5 ){ return Th_WrongNumArgs(interp, "copybtn TARGETID FLIPPED TEXT ?COPYLENGTH?"); } if( enableOutput ){ int flipped = 0; int copylength = 0; char *zResult; if( Th_ToInt(interp, argv[2], argl[2], &flipped) ) return TH_ERROR; if( argc==5 ){ if( Th_ToInt(interp, argv[4], argl[4], ©length) ) return TH_ERROR; } zResult = style_copy_button( /*bOutputCGI==*/0, /*TARGETID==*/(char*)argv[1], flipped, copylength, "%h", /*TEXT==*/(char*)argv[3]); sendText(0,zResult, -1, 0); free(zResult); } 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"); |
︙ | ︙ | |||
324 325 326 327 328 329 330 331 332 333 334 335 336 337 | } } if( n<iMin ) n = iMin; if( n>iMax ) n = iMax; Th_SetResultInt(interp, n); return TH_OK; } /* ** 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. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | > > > | | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > > > | > > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | } } 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 check-out or an ** empty string if it is not available. Optionally, it will attempt to find ** the current check-out, 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 check-out 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: defHeader TITLE ** ** Returns the default page header. */ static int defHeaderCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=1 ){ return Th_WrongNumArgs(interp, "defHeader"); } Th_SetResult(interp, get_default_header(), -1); return TH_OK; } /* ** TH1 command: styleHeader TITLE ** ** Render the configured style header for the selected skin. */ 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 for the selected skin. */ 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_finish_page(); Th_SetResult(interp, 0, 0); return TH_OK; }else{ Th_SetResult(interp, "repository unavailable", -1); return TH_ERROR; } } /* ** TH1 command: styleScript ?BUILTIN-FILENAME? ** ** Render the js.txt file from the current skin. Or, if an argument ** is supplied, render the built-in filename given. ** ** By "rendering" we mean that the script is loaded and run through ** TH1 to expand variables and process <th1>...</th1> script. Contrast ** with the "builtin_request_js BUILTIN-FILENAME" command which just ** loads the file as-is without interpretation. */ static int styleScriptCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=1 && argc!=2 ){ return Th_WrongNumArgs(interp, "styleScript ?BUILTIN_NAME?"); } if( Th_IsRepositoryOpen() ){ const char *zScript; if( argc==2 ){ zScript = (const char*)builtin_file(argv[1], 0); }else{ zScript = skin_get("js"); } if( zScript==0 ) zScript = ""; Th_Render(zScript); Th_SetResult(interp, 0, 0); return TH_OK; }else{ Th_SetResult(interp, "repository unavailable", -1); return TH_ERROR; } } /* ** TH1 command: submenu link LABEL URL ** ** Add a hyperlink to the submenu. */ static int submenuCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=4 || memcmp(argv[1],"link",5)!=0 ){ return Th_WrongNumArgs(interp, "submenu link LABEL URL"); } if( argl[2]==0 ){ Th_SetResult(interp, "link's LABEL is empty", -1); return TH_ERROR; } if( argl[3]==0 ){ Th_SetResult(interp, "link's URL is empty", -1); return TH_ERROR; } /* ** Label and URL are unescaped because it is expected that ** style_finish_page() provides propper escaping via %h format. */ style_submenu_element( fossil_strdup(argv[2]), "%s", argv[3] ); Th_SetResult(interp, 0, 0); return TH_OK; } /* ** TH1 command: builtin_request_js NAME ** ** Request that the built-in javascript file called NAME be added to the ** end of the generated page. ** ** See also: styleScript */ static int builtinRequestJsCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=2 ){ return Th_WrongNumArgs(interp, "builtin_request_js NAME"); } builtin_request_js(argv[1]); return TH_OK; } /* ** 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; } } /* ** TH1 command: cgiHeaderLine line ** ** Adds the specified line to the CGI header. */ static int cgiHeaderLineCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=2 ){ return Th_WrongNumArgs(interp, "cgiHeaderLine line"); } cgi_append_header(argv[1]); return TH_OK; } /* ** TH1 command: unversioned content FILENAME ** ** Attempts to locate the specified unversioned file and return its contents. ** An error is generated if the repository is not open or the unversioned file ** cannot be found. */ static int unversionedContentCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=3 ){ return Th_WrongNumArgs(interp, "unversioned content FILENAME"); } if( Th_IsRepositoryOpen() ){ Blob content; if( unversioned_content(argv[2], &content)!=0 ){ 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; } } /* ** TH1 command: unversioned list ** ** Returns a list of the names of all unversioned files held in the local ** repository. An error is generated if the repository is not open. */ static int unversionedListCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=2 ){ return Th_WrongNumArgs(interp, "unversioned list"); } if( Th_IsRepositoryOpen() ){ Stmt q; char *zList = 0; int nList = 0; db_prepare(&q, "SELECT name FROM unversioned WHERE hash IS NOT NULL" " ORDER BY name"); while( db_step(&q)==SQLITE_ROW ){ Th_ListAppend(interp, &zList, &nList, db_column_text(&q,0), -1); } db_finalize(&q); Th_SetResult(interp, zList, nList); Th_Free(interp, zList); return TH_OK; }else{ Th_SetResult(interp, "repository unavailable", -1); return TH_ERROR; } } static int unversionedCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ static const Th_SubCommand aSub[] = { { "content", unversionedContentCmd }, { "list", unversionedListCmd }, { 0, 0 } }; return Th_CallSubCommand(interp, p, argc, argv, argl, aSub); } /* ** 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]; fossil_cpu_times(&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]; fossil_cpu_times(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?"); } if( argc==2 ){ if( Th_ToInt(interp, argv[1], argl[1], &n) ){ return TH_ERROR; } if( n<1 ) n = 1; if( n>(int)sizeof(aRand) ) n = sizeof(aRand); }else{ n = 10; } sqlite3_randomness(n, aRand); encode16(aRand, zOut, n); Th_SetResult(interp, (const char *)zOut, -1); return TH_OK; } /* ** Run sqlite3_step() while suppressing error messages sent to the ** rendered webpage or to the console. */ static int ignore_errors_step(sqlite3_stmt *pStmt){ int rc; g.dbIgnoreErrors++; rc = sqlite3_step(pStmt); g.dbIgnoreErrors--; return rc; } /* ** TH1 command: query [-nocomplain] 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; int n, i; int res = TH_OK; int nVar; char *zErr = 0; int noComplain = 0; if( argc>3 && argl[1]==11 && strncmp(argv[1], "-nocomplain", 11)==0 ){ argc--; argv++; argl++; noComplain = 1; } if( argc!=3 ){ return Th_WrongNumArgs(interp, "query SQL CODE"); } if( g.db==0 ){ if( noComplain ) return TH_OK; Th_ErrorMessage(interp, "database is not open", 0, 0); return TH_ERROR; } zSql = argv[1]; nSql = argl[1]; while( res==TH_OK && nSql>0 ){ zErr = 0; report_restrict_sql(&zErr); g.dbIgnoreErrors++; rc = sqlite3_prepare_v2(g.db, argv[1], argl[1], &pStmt, &zTail); g.dbIgnoreErrors--; report_unrestrict_sql(); if( rc!=0 || zErr!=0 ){ if( noComplain ) return TH_OK; Th_ErrorMessage(interp, "SQL error: ", zErr ? zErr : sqlite3_errmsg(g.db), -1); return TH_ERROR; } n = (int)(zTail - zSql); zSql += n; nSql -= n; if( pStmt==0 ) continue; nVar = sqlite3_bind_parameter_count(pStmt); for(i=1; i<=nVar; i++){ const char *zVar = sqlite3_bind_parameter_name(pStmt, i); int szVar = zVar ? th_strlen(zVar) : 0; if( szVar>1 && zVar[0]=='$' && Th_GetVar(interp, zVar+1, szVar-1)==TH_OK ){ int nVal; const char *zVal = Th_GetResult(interp, &nVal); sqlite3_bind_text(pStmt, i, zVal, nVal, SQLITE_TRANSIENT); } } while( res==TH_OK && ignore_errors_step(pStmt)==SQLITE_ROW ){ int nCol = sqlite3_column_count(pStmt); for(i=0; i<nCol; i++){ const char *zCol = sqlite3_column_name(pStmt, i); int szCol = th_strlen(zCol); const char *zVal = (const char*)sqlite3_column_text(pStmt, i); int szVal = sqlite3_column_bytes(pStmt, i); Th_SetVar(interp, zCol, szCol, zVal, szVal); } if( g.thTrace ){ Th_Trace("query_eval {<pre>%#h</pre>}<br>\n", argl[2], argv[2]); } res = Th_Eval(interp, 0, argv[2], argl[2]); if( g.thTrace ){ int nTrRes; char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); Th_Trace("[query_eval] => %h {%#h}<br>\n", Th_ReturnCodeName(res, 0), nTrRes, zTrRes); } if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK; } rc = sqlite3_finalize(pStmt); if( rc!=SQLITE_OK ){ if( noComplain ) return TH_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, int argc, const char **argv, int *argl ){ int rc; int strict = 0; int nArg = 1; char *zValue; if( argc<2 || argc>4 ){ return Th_WrongNumArgs(interp, SETTING_WRONGNUMARGS); } if( fossil_strcmp(argv[nArg], "-strict")==0 ){ strict = 1; nArg++; } if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; if( nArg+1!=argc ){ return Th_WrongNumArgs(interp, SETTING_WRONGNUMARGS); } zValue = db_get(argv[nArg], 0); if( zValue!=0 ){ Th_SetResult(interp, zValue, -1); rc = TH_OK; }else if( strict ){ Th_ErrorMessage(interp, "no value for setting \"", argv[nArg], -1); rc = TH_ERROR; }else{ Th_SetResult(interp, 0, 0); rc = TH_OK; } if( g.thTrace ){ Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "", argl[nArg], argv[nArg], rc); } return rc; } /* ** TH1 command: glob_match ?-one? ?--? patternList string ** ** Checks the string against the specified glob pattern -OR- list of glob ** patterns and returns non-zero if there is a match. */ #define GLOB_MATCH_WRONGNUMARGS "glob_match ?-one? ?--? patternList string" static int globMatchCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int rc; int one = 0; int nArg = 1; Glob *pGlob = 0; if( argc<3 || argc>5 ){ return Th_WrongNumArgs(interp, GLOB_MATCH_WRONGNUMARGS); } if( fossil_strcmp(argv[nArg], "-one")==0 ){ one = 1; nArg++; } if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; if( nArg+2!=argc ){ return Th_WrongNumArgs(interp, GLOB_MATCH_WRONGNUMARGS); } if( one ){ Th_SetResultInt(interp, sqlite3_strglob(argv[nArg], argv[nArg+1])==0); rc = TH_OK; }else{ pGlob = glob_create(argv[nArg]); if( pGlob ){ Th_SetResultInt(interp, glob_match(pGlob, argv[nArg+1])); rc = TH_OK; }else{ Th_SetResult(interp, "unable to create glob from pattern list", -1); rc = TH_ERROR; } glob_free(pGlob); } 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( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int rc; int noCase = 0; int nArg = 1; ReCompiled *pRe = 0; const char *zErr; if( argc<3 || argc>5 ){ return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); } if( fossil_strcmp(argv[nArg], "-nocase")==0 ){ noCase = 1; nArg++; } if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; if( nArg+2!=argc ){ return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); } zErr = re_compile(&pRe, argv[nArg], noCase); if( !zErr ){ Th_SetResultInt(interp, re_match(pRe, (const unsigned char *)argv[nArg+1], argl[nArg+1])); rc = TH_OK; }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; } } /* ** TH1 command: captureTh1 STRING ** ** Evaluates the given string as TH1 code and captures any of its ** TH1-generated output as a string (instead of it being output), ** which becomes the result of the function. */ static int captureTh1Cmd( Th_Interp *interp, void *pConvert, int argc, const char **argv, int *argl ){ Blob out = empty_blob; Blob * pOrig; const char * zStr; int nStr, rc; if( argc!=2 ){ return Th_WrongNumArgs(interp, "captureTh1 STRING"); } pOrig = Th_SetOutputBlob(&out); zStr = argv[1]; nStr = argl[1]; rc = Th_Eval(g.interp, 0, zStr, nStr); Th_SetOutputBlob(pOrig); if(0==rc){ Th_SetResult(g.interp, blob_str(&out), blob_size(&out)); } blob_reset(&out); return rc; } /* ** 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, 1); 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; int noRepo = flags & TH_INIT_NO_REPO; 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}, {"builtin_request_js", builtinRequestJsCmd, 0}, {"capexpr", capexprCmd, 0}, {"captureTh1", captureTh1Cmd, 0}, {"cgiHeaderLine", cgiHeaderLineCmd, 0}, {"checkout", checkoutCmd, 0}, {"combobox", comboboxCmd, 0}, {"copybtn", copybtnCmd, 0}, {"date", dateCmd, 0}, {"decorate", wikiCmd, (void*)&aFlags[2]}, {"defHeader", defHeaderCmd, 0}, {"dir", dirCmd, 0}, {"enable_htmlify",enableHtmlifyCmd, 0}, {"enable_output", enableOutputCmd, 0}, {"encode64", encode64Cmd, 0}, {"getParameter", getParameterCmd, 0}, {"glob_match", globMatchCmd, 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}, {"insertCsrf", insertCsrfCmd, 0}, {"linecount", linecntCmd, 0}, {"markdown", markdownCmd, 0}, {"nonce", nonceCmd, 0}, {"puts", putsCmd, (void*)&aFlags[1]}, {"query", queryCmd, 0}, {"randhex", randhexCmd, 0}, {"redirect", redirectCmd, 0}, {"regexp", regexpCmd, 0}, {"reinitialize", reinitializeCmd, 0}, {"render", renderCmd, 0}, {"repository", repositoryCmd, 0}, {"searchable", searchableCmd, 0}, {"setParameter", setParameterCmd, 0}, {"setting", settingCmd, 0}, {"styleFooter", styleFooterCmd, 0}, {"styleHeader", styleHeaderCmd, 0}, {"styleScript", styleScriptCmd, 0}, {"submenu", submenuCmd, 0}, {"tclReady", tclReadyCmd, 0}, {"trace", traceCmd, 0}, {"stime", stimeCmd, 0}, {"unversioned", unversionedCmd, 0}, {"utime", utimeCmd, 0}, {"verifyCsrf", verifyCsrfCmd, 0}, {"verifyLogin", verifyLoginCmd, 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(!noRepo); } if( forceReset || forceTcl || g.interp==0 ){ int created = 0; int i; if( g.interp==0 ){ Th_Vtab *pVtab = 0; #if defined(TH_MEMDEBUG) if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ pVtab = &vtab; if( g.thTrace ){ Th_Trace("th1-init MEMDEBUG ENABLED<br>\n"); } } #endif g.interp = Th_CreateInterp(pVtab); 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<count(aCommand); i++){ if ( !aCommand[i].zName || !aCommand[i].xProc ) continue; Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc, aCommand[i].pContext, 0); } }else{ wasInit = 1; } if( forceSetup || !wasInit ){ int rc = TH_OK; if( !g.th1Setup ){ g.th1Setup = db_get("th1-setup", 0); /* Grab TH1 setup script. */ } if( g.th1Setup ){ rc = Th_Eval(g.interp, 0, g.th1Setup, -1); if( rc==TH_ERROR ){ int nResult = 0; char *zResult = (char*)Th_GetResult(g.interp, &nResult); sendError(0,zResult, nResult, 0); } } 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 if the variable ** does not already exist. */ void Th_MaybeStore(const char *zName, const char *zValue){ Th_FossilInit(TH_INIT_DEFAULT); if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){ if( g.thTrace ){ Th_Trace("maybe_set %h {%h}<br>\n", zName, zValue); } Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); } } /* ** 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; Th_FossilInit(TH_INIT_DEFAULT); blob_zero(&value); blob_appendf(&value, "%d", iValue); zValue = blob_str(&value); if( g.thTrace ){ Th_Trace("set %h {%h}<br>\n", zName, zValue); } Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); blob_reset(&value); } /* ** Unset a variable. */ void Th_Unstore(const char *zName){ if( g.interp ){ Th_UnsetVar(g.interp, (char*)zName, -1); } } /* ** Retrieve a string value from the interpreter. If no such ** variable exists, return NULL. */ char *Th_Fetch(const char *zName, int *pSize){ int rc; Th_FossilInit(TH_INIT_DEFAULT); rc = Th_GetVar(g.interp, (char*)zName, -1); if( rc==TH_OK ){ return (char*)Th_GetResult(g.interp, pSize); }else{ return 0; } } |
︙ | ︙ | |||
432 433 434 435 436 437 438 | static int validVarName(const char *z){ int i = 0; int inBracket = 0; if( z[0]=='<' ){ inBracket = 1; z++; } | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | > > > | > | > | | | > | | > > > > > > > > > < | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | static int validVarName(const char *z){ int i = 0; int inBracket = 0; if( z[0]=='<' ){ inBracket = 1; z++; } if( z[0]==':' && z[1]==':' && fossil_isalpha(z[2]) ){ z += 3; i += 3; }else if( fossil_isalpha(z[0]) ){ z ++; i += 1; }else{ return 0; } while( fossil_isalnum(z[0]) || z[0]=='_' ){ z++; i++; } 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, unsigned int 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(0,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, unsigned int 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, unsigned int 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(0,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, unsigned int 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 #if INTERFACE /* ** Flags for use with Th_RenderToBlob. These must not overlap with ** TH_INIT_MASK. */ #define TH_R2B_MASK ((u32)0x0f000) #define TH_R2B_NO_VARS ((u32)0x01000) /* Disables eval of $vars and $<vars> */ #endif /* ** If pOut is NULL, this works identically to Th_Render() and sends ** any TH1-generated output to stdin (in CLI mode) or the CGI buffer ** (in CGI mode), else it works just like that function but appends ** any TH1-generated output to the given blob. A bitmask of TH_R2B_xxx ** and/or TH_INIT_xxx flags may be passed as the 3rd argument, or 0 ** for default options. Note that this function necessarily calls ** Th_FossilInit(), which may unset flags used on previous calls ** unless mFlags is explicitly passed in. */ int Th_RenderToBlob(const char *z, Blob * pOut, u32 mFlags){ int i = 0; int n; int rc = TH_OK; char *zResult; Blob * const origOut = Th_SetOutputBlob(pOut); assert(0==(TH_R2B_MASK & TH_INIT_MASK) && "init/r2b mask conflict"); Th_FossilInit(mFlags & TH_INIT_MASK); while( z[i] ){ if( 0==(TH_R2B_NO_VARS & mFlags) && z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){ const char *zVar; int nVar; int encode = 1; sendText(pOut,z, i, 0); if( z[i+1]=='<' ){ /* Variables of the form $<aaa> are html escaped */ zVar = &z[i+2]; nVar = n-2; }else{ /* Variables of the form $aaa are output raw */ zVar = &z[i+1]; nVar = n; encode = 0; } rc = Th_GetVar(g.interp, (char*)zVar, nVar); z += i+1+n; i = 0; zResult = (char*)Th_GetResult(g.interp, &n); sendText(pOut,(char*)zResult, n, encode); }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ sendText(pOut,z, i, 0); z += i+5; for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} if( g.thTrace ){ Th_Trace("render_eval {<pre>%#h</pre>}<br>\n", i, z); } rc = Th_Eval(g.interp, 0, (const char*)z, i); if( g.thTrace ){ int nTrRes; char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); Th_Trace("[render_eval] => %h {%#h}<br>\n", Th_ReturnCodeName(rc, 0), nTrRes, zTrRes); } if( rc!=TH_OK ) break; z += i; if( z[0] ){ z += 6; } i = 0; }else{ i++; } } if( rc==TH_ERROR ){ zResult = (char*)Th_GetResult(g.interp, &n); sendError(pOut,zResult, n, 1); }else{ sendText(pOut,z, i, 0); } Th_SetOutputBlob(origOut); return rc; } /* ** 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 to one ** of stdout, CGI, or an internal blob which was set up via a prior ** call to Th_SetOutputBlob(). */ int Th_Render(const char *z){ return Th_RenderToBlob(z, pThOut, g.th1Flags) /* Maintenance reminder: on most calls to Th_Render(), e.g. for ** outputing the site skin, pThOut will be 0, which means that ** Th_RenderToBlob() will output directly to the CGI buffer (in ** CGI mode) or stdout (in CLI mode). Recursive calls, however, ** e.g. via the "render" script function binding, need to use the ** pThOut blob in order to avoid out-of-order output if ** Th_SetOutputBlob() has been called. If it has not been called, ** pThOut will be 0, which will redirect the output to CGI/stdout, ** as appropriate. We need to pass on g.th1Flags for the case of ** recursive calls, so that, e.g., TH_INIT_NO_ENCODE does not get ** inadvertently toggled off by a recursive call. */; } /* ** 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 ** --set-anon-caps Set anonymous login capabilities ** --set-user-caps Set user login capabilities ** --th-trace Trace TH1 execution (for debugging purposes) */ void test_th_render(void){ int forceCgi, fullHttpReply; 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); } if( find_option("set-anon-caps", 0, 0)!=0 ){ const char *zCap = fossil_getenv("TH1_TEST_ANON_CAPS"); login_set_capabilities(zCap ? zCap : "sx", LOGIN_ANON); g.useLocalauth = 1; } if( find_option("set-user-caps", 0, 0)!=0 ){ const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS"); login_set_capabilities(zCap ? zCap : "sx", 0); g.useLocalauth = 1; } verify_all_options(); if( g.argc<3 ){ usage("FILE"); } blob_zero(&in); blob_read_from_file(&in, g.argv[2], ExtFILE); 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 and show the results on standard output. SCRIPT may be either ** a filename or a string of th1 script code. ** ** 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 ** --set-anon-caps Set anonymous login capabilities ** --set-user-caps Set user login capabilities ** --th-trace Trace TH1 execution (for debugging purposes) */ void test_th_eval(void){ int rc; const char *zRc; const char *zCode = 0; int forceCgi, fullHttpReply; Blob code = empty_blob; 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( find_option("set-anon-caps", 0, 0)!=0 ){ const char *zCap = fossil_getenv("TH1_TEST_ANON_CAPS"); login_set_capabilities(zCap ? zCap : "sx", LOGIN_ANON); g.useLocalauth = 1; } if( find_option("set-user-caps", 0, 0)!=0 ){ const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS"); login_set_capabilities(zCap ? zCap : "sx", 0); g.useLocalauth = 1; } verify_all_options(); if( g.argc!=3 ){ usage("script"); } if(file_isfile(g.argv[2], ExtFILE)){ blob_read_from_file(&code, g.argv[2], ExtFILE); zCode = blob_str(&code); }else{ zCode = g.argv[2]; } Th_FossilInit(TH_INIT_DEFAULT); rc = Th_Eval(g.interp, 0, zCode, -1); zRc = Th_ReturnCodeName(rc, 1); fossil_print("%s%s%s\n", zRc, zRc ? ": " : "", Th_GetResult(g.interp, 0)); Th_PrintTraceLog(); blob_reset(&code); if( forceCgi ) cgi_reply(); } /* ** COMMAND: test-th-source ** ** Usage: %fossil test-th-source FILE ** ** Evaluate the contents of the file named "FILE" as if it were a header ** or footer or ticket rendering script 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 ** --set-anon-caps Set anonymous login capabilities ** --set-user-caps Set user login capabilities ** --th-trace Trace TH1 execution (for debugging purposes) ** --no-print-result Do not output the final result. Use if it ** interferes with script output. */ void test_th_source(void){ int rc; const char *zRc; int forceCgi, fullHttpReply, fNoPrintRc; Blob in; Th_InitTraceLog(); forceCgi = find_option("cgi", 0, 0)!=0; fullHttpReply = find_option("http", 0, 0)!=0; fNoPrintRc = find_option("no-print-result",0,0)!=0; if( fullHttpReply ) forceCgi = 1; if( forceCgi ) Th_ForceCgi(fullHttpReply); if( find_option("open-config", 0, 0)!=0 ){ Th_OpenConfig(1); } if( find_option("set-anon-caps", 0, 0)!=0 ){ const char *zCap = fossil_getenv("TH1_TEST_ANON_CAPS"); login_set_capabilities(zCap ? zCap : "sx", LOGIN_ANON); g.useLocalauth = 1; } if( find_option("set-user-caps", 0, 0)!=0 ){ const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS"); login_set_capabilities(zCap ? zCap : "sx", 0); g.useLocalauth = 1; } verify_all_options(); if( g.argc!=3 ){ usage("file"); } blob_zero(&in); blob_read_from_file(&in, g.argv[2], ExtFILE); Th_FossilInit(TH_INIT_DEFAULT); rc = Th_Eval(g.interp, 0, blob_str(&in), -1); zRc = Th_ReturnCodeName(rc, 1); if(0==fNoPrintRc){ 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 ** ** Usage: %fossil test-th-hook TYPE NAME FLAGS ** ** Evaluates the TH1 script configured for the pre-operation (i.e. a command ** or web page) "hook" or post-operation "notification". The results of the ** script evaluation, if any, will be printed to the standard output channel. ** The NAME argument must be the name of a command or web page; however, it ** does not necessarily have to be a command or web page that is normally ** recognized by Fossil. The FLAGS argument will be used to set the value ** of the "cmd_flags" and/or "web_flags" TH1 variables, if applicable. The ** TYPE argument must be one of the following: ** ** cmdhook Executes the TH1 procedure [command_hook], after ** setting the TH1 variables "cmd_name", "cmd_args", ** and "cmd_flags" to appropriate values. ** ** cmdnotify Executes the TH1 procedure [command_notify], after ** setting the TH1 variables "cmd_name", "cmd_args", ** and "cmd_flags" to appropriate values. ** ** webhook Executes the TH1 procedure [webpage_hook], after ** setting the TH1 variables "web_name", "web_args", ** and "web_flags" to appropriate values. ** ** webnotify Executes the TH1 procedure [webpage_notify], after ** setting the TH1 variables "web_name", "web_args", ** and "web_flags" to appropriate values. ** ** Options: ** --cgi Include a CGI response header in the output ** --http Include an HTTP response header in the output ** --th-trace Trace TH1 execution (for debugging purposes) */ void test_th_hook(void){ int rc = TH_OK; int nResult = 0; char *zResult = 0; 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); verify_all_options(); if( g.argc<5 ){ usage("TYPE NAME FLAGS"); } if( fossil_stricmp(g.argv[2], "cmdhook")==0 ){ rc = Th_CommandHook(g.argv[3], (unsigned int)atoi(g.argv[4])); }else if( fossil_stricmp(g.argv[2], "cmdnotify")==0 ){ rc = Th_CommandNotify(g.argv[3], (unsigned int)atoi(g.argv[4])); }else if( fossil_stricmp(g.argv[2], "webhook")==0 ){ rc = Th_WebpageHook(g.argv[3], (unsigned int)atoi(g.argv[4])); }else if( fossil_stricmp(g.argv[2], "webnotify")==0 ){ rc = Th_WebpageNotify(g.argv[3], (unsigned int)atoi(g.argv[4])); }else{ fossil_fatal("Unknown TH1 hook %s", g.argv[2]); } if( g.interp ){ zResult = (char*)Th_GetResult(g.interp, &nResult); } sendText(0,"RESULT (", -1, 0); sendText(0,Th_ReturnCodeName(rc, 0), -1, 0); sendText(0,")", -1, 0); if( zResult && nResult>0 ){ sendText(0,": ", -1, 0); sendText(0,zResult, nResult, 0); } sendText(0,"\n", -1, 0); Th_PrintTraceLog(); if( forceCgi ) cgi_reply(); } #endif |
Added src/th_tcl.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 | /* ** Copyright (c) 2011 D. Richard Hipp ** Copyright (c) 2011 Joe Mistachkin ** ** 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 bridge the TH1 and Tcl scripting languages. */ #include "config.h" #ifdef FOSSIL_ENABLE_TCL #include "sqlite3.h" #include "th.h" #include "tcl.h" /* ** This macro is used to verify that the header version of Tcl meets some ** minimum requirement. */ #define MINIMUM_TCL_VERSION(major, minor) \ ((TCL_MAJOR_VERSION > (major)) || \ ((TCL_MAJOR_VERSION == (major)) && (TCL_MINOR_VERSION >= (minor)))) /* ** 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]); \ objv[obji-1] = 0; \ } \ ckfree((char *)objv); \ objv = 0; /* ** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl ** context. */ #define GET_CTX_TCL_INTERP(ctx) \ ((struct TclContext *)(ctx))->interp /* ** Fetch the (logically boolean) value from the specified void pointer that ** indicates whether or not we can/should use direct objProc calls. */ #define GET_CTX_TCL_USEOBJPROC(ctx) \ ((struct TclContext *)(ctx))->useObjProc /* ** This is the name of an environment variable that may refer to a Tcl library ** directory or file name. If this environment variable is set [to anything], ** its value will be used when searching for a Tcl library to load. */ #ifndef TCL_PATH_ENV_VAR_NAME # define TCL_PATH_ENV_VAR_NAME "FOSSIL_TCL_PATH" #endif /* ** Define the Tcl shared library name, some exported function names, and some ** cross-platform macros for use with the Tcl stubs mechanism, when enabled. */ #if defined(USE_TCL_STUBS) # if defined(_WIN32) # if !defined(WIN32_LEAN_AND_MEAN) # define WIN32_LEAN_AND_MEAN # endif # if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0502) # undef _WIN32_WINNT # define _WIN32_WINNT 0x0502 /* SetDllDirectory, Windows XP SP2 */ # endif # include <windows.h> # ifndef TCL_DIRECTORY_SEP # define TCL_DIRECTORY_SEP '\\' # endif # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "tcl87.dll\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (4) # endif # ifndef dlopen # define dlopen(a,b) (void *)LoadLibrary((a)) # endif # ifndef dlsym # define dlsym(a,b) GetProcAddress((HANDLE)(a),(b)) # endif # ifndef dlclose # define dlclose(a) FreeLibrary((HANDLE)(a)) # endif # else # include <dlfcn.h> # ifndef TCL_DIRECTORY_SEP # define TCL_DIRECTORY_SEP '/' # endif # if defined(__CYGWIN__) # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl8.7.dll\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (8) # endif # elif defined(__APPLE__) # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl8.7.dylib\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (8) # endif # elif defined(__FreeBSD__) # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl87.so\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (7) # endif # else # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl8.7.so\0" # 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) */ /* ** If this constant is defined to non-zero, the Win32 SetDllDirectory function ** will be used during the Tcl library loading process if the path environment ** variable for Tcl was set. */ #ifndef TCL_USE_SET_DLL_DIRECTORY # if defined(_WIN32) && defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0502) # define TCL_USE_SET_DLL_DIRECTORY (1) # else # define TCL_USE_SET_DLL_DIRECTORY (0) # endif #endif /* TCL_USE_SET_DLL_DIRECTORY */ /* ** 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 ** the only Tcl API functions that MUST be called prior to being able to call ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete ** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp ** and Tcl_Finalize function types are also required. */ typedef void (tcl_FindExecutableProc) (const char *); typedef Tcl_Interp *(tcl_CreateInterpProc) (void); typedef void (tcl_DeleteInterpProc) (Tcl_Interp *); typedef void (tcl_FinalizeProc) (void); /* ** The function types for the "hook" functions to be called before and after a ** TH1 command makes a call to evaluate a Tcl script. If the "pre" function ** returns anything but TH_OK, then evaluation of the Tcl script is skipped and ** that value is used as the return code. If the "post" function returns ** anything other than its rc argument, that will become the new return code ** for the command. */ typedef int (tcl_NotifyProc) ( void *pContext, /* The context for this notification. */ Th_Interp *interp, /* The TH1 interpreter being used. */ void *ctx, /* The original TH1 command context. */ int argc, /* Number of arguments for the TH1 command. */ const char **argv, /* Array of arguments for the TH1 command. */ int *argl, /* Array of lengths for the TH1 command arguments. */ int rc /* Recommended notification return value. */ ); /* ** Are we using our own private implementation of the Tcl stubs mechanism? If ** this is enabled, it prevents the user from having to link against the Tcl ** stubs library for the target platform, which may not be readily available. */ #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 ** stubs mechanism. */ struct PrivateTclInterp { char *result; Tcl_FreeProc *freeProc; int errorLine; const struct TclStubs *stubTable; }; /* ** Fossil can now be compiled without linking to the actual Tcl stubs library. ** In that case, this function will be used to perform those steps that would ** normally be performed within the Tcl stubs library. */ static int initTclStubs( Th_Interp *interp, Tcl_Interp *tclInterp ){ tclStubsPtr = ((struct PrivateTclInterp *)tclInterp)->stubTable; if( !tclStubsPtr || (tclStubsPtr->magic!=TCL_STUB_MAGIC) ){ Th_ErrorMessage(interp, "could not initialize Tcl stubs: incompatible mechanism", (const char *)"", 0); return TH_ERROR; } /* NOTE: At this point, the Tcl API functions should be available. */ if( Tcl_PkgRequireEx(tclInterp, "Tcl", "8.4", 0, (void *)&tclStubsPtr)==0 ){ Th_ErrorMessage(interp, "could not initialize Tcl stubs: incompatible version", (const char *)"", 0); return TH_ERROR; } return TH_OK; } #endif /* defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) */ /* ** Is the loaded version of Tcl one where querying and/or calling the objProc ** for a command does not work for some reason? The following special cases ** are currently handled by this function: ** ** 1. All versions of Tcl 8.4 have a bug that causes a crash when calling into ** the Tcl_GetCommandFromObj function via stubs (i.e. the stubs table entry ** is NULL). ** ** 2. Various beta builds of Tcl 8.6, namely 1 and 2, have an NRE-specific bug ** in Tcl_EvalObjCmd (SF bug #3399564) that cause a panic when calling into ** the objProc directly. ** ** For both of the above cases, the Tcl_EvalObjv function must be used instead ** of the more direct route of querying and calling the objProc directly. */ static int canUseObjProc(){ int major = -1, minor = -1, patchLevel = -1, type = -1; Tcl_GetVersion(&major, &minor, &patchLevel, &type); if( major<0 || minor<0 || patchLevel<0 || type<0 ){ return 0; /* NOTE: Invalid version info, assume bad. */ } if( major==8 && minor==4 ){ return 0; /* NOTE: Disabled on Tcl 8.4, missing public API. */ } if( major==8 && minor==6 && type==TCL_BETA_RELEASE && patchLevel<3 ){ return 0; /* NOTE: Disabled on Tcl 8.6b1/b2, SF bug #3399564. */ } return 1; /* NOTE: For all other cases, assume good. */ } /* ** Is the loaded version of Tcl one where TIP #285 (asynchronous script ** cancellation) is available? This should return non-zero only for Tcl ** 8.6 and higher. */ static int canUseTip285(){ #if MINIMUM_TCL_VERSION(8, 6) int major = -1, minor = -1, patchLevel = -1, type = -1; Tcl_GetVersion(&major, &minor, &patchLevel, &type); if( major<0 || minor<0 || patchLevel<0 || type<0 ){ return 0; /* NOTE: Invalid version info, assume bad. */ } return (major>8 || (major==8 && minor>=6)); #else return 0; #endif } /* ** 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 TH1 return code corresponding to the specified Tcl ** return code. */ static int getTh1ReturnCode( int rc /* The Tcl return code value to convert. */ ){ switch( rc ){ case /*0*/ TCL_OK: return /*0*/ TH_OK; case /*1*/ TCL_ERROR: return /*1*/ TH_ERROR; case /*2*/ TCL_RETURN: return /*3*/ TH_RETURN; case /*3*/ TCL_BREAK: return /*2*/ TH_BREAK; case /*4*/ TCL_CONTINUE: return /*4*/ TH_CONTINUE; default /*?*/: return /*?*/ rc; } } /* ** Returns the Tcl return code corresponding to the specified TH1 ** return code. */ static int getTclReturnCode( int rc /* The TH1 return code value to convert. */ ){ switch( rc ){ case /*0*/ TH_OK: return /*0*/ TCL_OK; case /*1*/ TH_ERROR: return /*1*/ TCL_ERROR; case /*2*/ TH_BREAK: return /*3*/ TCL_BREAK; case /*3*/ TH_RETURN: return /*2*/ TCL_RETURN; case /*4*/ TH_CONTINUE: return /*4*/ TCL_CONTINUE; default /*?*/: return /*?*/ rc; } } /* ** Returns a name for a Tcl return code. */ static const char *getTclReturnCodeName( int rc, int nullIfOk ){ static char zRc[TCL_INTEGER_SPACE + 17]; /* "Tcl return code\0" */ switch( rc ){ case TCL_OK: return nullIfOk ? 0 : "TCL_OK"; case TCL_ERROR: return "TCL_ERROR"; case TCL_RETURN: return "TCL_RETURN"; case TCL_BREAK: return "TCL_BREAK"; 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( Tcl_Interp *pInterp, int *pN ){ Tcl_Obj *resultPtr; if( !pInterp ){ /* This should not happen. */ if( pN ) *pN = 0; return 0; } resultPtr = Tcl_GetObjResult(pInterp); if( !resultPtr ){ /* This should not happen either? */ if( pN ) *pN = 0; return 0; } return Tcl_GetStringFromObj(resultPtr, pN); } /* ** Tcl context information used by TH1. This structure definition has been ** copied from and should be kept in sync with the one in "main.c". */ struct TclContext { int argc; /* Number of original arguments. */ char **argv; /* Full copy of the original arguments. */ void *hLibrary; /* The Tcl library module handle. */ tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */ tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */ tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */ tcl_FinalizeProc *xFinalize; /* Tcl_Finalize() pointer. */ Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ int useObjProc; /* Non-zero if an objProc can be called directly. */ int useTip285; /* Non-zero if TIP #285 is available. */ const char *setup; /* The optional Tcl setup script. */ tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */ void *pPreContext; /* Optional, provided to xPreEval(). */ tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */ void *pPostContext; /* Optional, provided to xPostEval(). */ }; /* ** This function calls the configured xPreEval or xPostEval functions, if any. ** May have arbitrary side-effects. This function returns the result of the ** called notification function or the value of the rc argument if there is no ** notification function configured. */ static int notifyPreOrPostEval( int bIsPost, Th_Interp *interp, void *ctx, int argc, 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; } /* ** TH1 command: tclEval arg ?arg ...? ** ** Evaluates the Tcl script and returns its result verbatim. If a Tcl script ** error is generated, it will be transformed into a TH1 script error. The ** Tcl interpreter will be created automatically if it has not been already. */ static int tclEval_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ Tcl_Interp *tclInterp; Tcl_Obj *objPtr; int rc = TH_OK; int nResult; const char *zResult; if( createTclInterp(interp, ctx)!=TH_OK ){ return TH_ERROR; } if( argc<2 ){ return Th_WrongNumArgs(interp, "tclEval arg ?arg ...?"); } tclInterp = GET_CTX_TCL_INTERP(ctx); if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); return TH_ERROR; } rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc); if( rc!=TH_OK ){ return rc; } Tcl_Preserve((ClientData)tclInterp); if( argc==2 ){ objPtr = Tcl_NewStringObj(argv[1], argl[1]); Tcl_IncrRefCount(objPtr); rc = Tcl_EvalObjEx(tclInterp, objPtr, 0); Tcl_DecrRefCount(objPtr); objPtr = 0; }else{ USE_ARGV_TO_OBJV(); COPY_ARGV_TO_OBJV(); objPtr = Tcl_ConcatObj(objc, objv); Tcl_IncrRefCount(objPtr); rc = Tcl_EvalObjEx(tclInterp, objPtr, 0); Tcl_DecrRefCount(objPtr); objPtr = 0; FREE_ARGV_TO_OBJV(); } zResult = getTclResult(tclInterp, &nResult); Th_SetResult(interp, zResult, nResult); Tcl_Release((ClientData)tclInterp); rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, getTh1ReturnCode(rc)); return rc; } /* ** TH1 command: tclExpr arg ?arg ...? ** ** Evaluates the Tcl expression and returns its result verbatim. If a Tcl ** script error is generated, it will be transformed into a TH1 script error. ** The Tcl interpreter will be created automatically if it has not been ** already. */ static int tclExpr_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ Tcl_Interp *tclInterp; Tcl_Obj *objPtr; Tcl_Obj *resultObjPtr; int rc = TH_OK; int nResult; const char *zResult; if( createTclInterp(interp, ctx)!=TH_OK ){ return TH_ERROR; } if( argc<2 ){ return Th_WrongNumArgs(interp, "tclExpr arg ?arg ...?"); } tclInterp = GET_CTX_TCL_INTERP(ctx); if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); return TH_ERROR; } rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc); if( rc!=TH_OK ){ return rc; } Tcl_Preserve((ClientData)tclInterp); if( argc==2 ){ objPtr = Tcl_NewStringObj(argv[1], argl[1]); Tcl_IncrRefCount(objPtr); rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr); Tcl_DecrRefCount(objPtr); objPtr = 0; }else{ USE_ARGV_TO_OBJV(); COPY_ARGV_TO_OBJV(); objPtr = Tcl_ConcatObj(objc, objv); Tcl_IncrRefCount(objPtr); rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr); Tcl_DecrRefCount(objPtr); objPtr = 0; FREE_ARGV_TO_OBJV(); } if( rc==TCL_OK ){ zResult = Tcl_GetStringFromObj(resultObjPtr, &nResult); }else{ zResult = getTclResult(tclInterp, &nResult); } Th_SetResult(interp, zResult, nResult); if( rc==TCL_OK ){ Tcl_DecrRefCount(resultObjPtr); resultObjPtr = 0; } Tcl_Release((ClientData)tclInterp); rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, getTh1ReturnCode(rc)); return rc; } /* ** TH1 command: tclInvoke command ?arg ...? ** ** Invokes the Tcl command using the supplied arguments. No additional ** substitutions are performed on the arguments. The Tcl interpreter ** will be created automatically if it has not been already. */ static int tclInvoke_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ Tcl_Interp *tclInterp; int rc = TH_OK; int nResult; const char *zResult; USE_ARGV_TO_OBJV(); if( createTclInterp(interp, ctx)!=TH_OK ){ return TH_ERROR; } if( argc<2 ){ return Th_WrongNumArgs(interp, "tclInvoke command ?arg ...?"); } tclInterp = GET_CTX_TCL_INTERP(ctx); if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); return TH_ERROR; } rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc); if( rc!=TH_OK ){ return rc; } Tcl_Preserve((ClientData)tclInterp); #if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV if( GET_CTX_TCL_USEOBJPROC(ctx) ){ Tcl_Command command; Tcl_CmdInfo cmdInfo; Tcl_Obj *objPtr = Tcl_NewStringObj(argv[1], argl[1]); Tcl_IncrRefCount(objPtr); command = Tcl_GetCommandFromObj(tclInterp, objPtr); if( !command || Tcl_GetCommandInfoFromToken(command, &cmdInfo)==0 ){ Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]); Tcl_DecrRefCount(objPtr); objPtr = 0; Tcl_Release((ClientData)tclInterp); return TH_ERROR; } if( !cmdInfo.objProc ){ Th_ErrorMessage(interp, "cannot invoke Tcl command:", argv[1], argl[1]); Tcl_DecrRefCount(objPtr); objPtr = 0; Tcl_Release((ClientData)tclInterp); return TH_ERROR; } Tcl_DecrRefCount(objPtr); objPtr = 0; COPY_ARGV_TO_OBJV(); Tcl_ResetResult(tclInterp); rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv); FREE_ARGV_TO_OBJV(); }else #endif /* !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV */ { COPY_ARGV_TO_OBJV(); rc = Tcl_EvalObjv(tclInterp, objc, objv, 0); FREE_ARGV_TO_OBJV(); } zResult = getTclResult(tclInterp, &nResult); Th_SetResult(interp, zResult, nResult); Tcl_Release((ClientData)tclInterp); rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, getTh1ReturnCode(rc)); return rc; } /* ** TH1 command: tclIsSafe ** ** Returns non-zero if the Tcl interpreter is "safe". The Tcl interpreter ** will be created automatically if it has not been already. */ static int tclIsSafe_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ Tcl_Interp *tclInterp; if( createTclInterp(interp, ctx)!=TH_OK ){ return TH_ERROR; } if( argc!=1 ){ return Th_WrongNumArgs(interp, "tclIsSafe"); } tclInterp = GET_CTX_TCL_INTERP(ctx); if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); return TH_ERROR; } Th_SetResultInt(interp, Tcl_IsSafe(tclInterp)); return TH_OK; } /* ** TH1 command: tclMakeSafe ** ** Forces the Tcl interpreter into "safe" mode by removing all "unsafe" ** commands and variables. This operation cannot be undone. The Tcl ** interpreter will remain "safe" until the process terminates. */ static int tclMakeSafe_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ static int registerChans = 1; Tcl_Interp *tclInterp; int rc = TH_OK; if( createTclInterp(interp, ctx)!=TH_OK ){ return TH_ERROR; } if( argc!=1 ){ return Th_WrongNumArgs(interp, "tclMakeSafe"); } tclInterp = GET_CTX_TCL_INTERP(ctx); if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); return TH_ERROR; } if( Tcl_IsSafe(tclInterp) ){ Th_ErrorMessage(interp, "Tcl interpreter is already 'safe'", (const char *)"", 0); return TH_ERROR; } if( registerChans ){ /* ** HACK: Prevent the call to Tcl_MakeSafe() from actually closing the ** standard channels instead of simply unregistering them from ** the Tcl interpreter. This should only need to be done once ** per thread (process?). */ registerChans = 0; Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDIN)); Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDOUT)); Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDERR)); } Tcl_Preserve((ClientData)tclInterp); if( Tcl_MakeSafe(tclInterp)!=TCL_OK ){ int nResult; const char *zResult = getTclResult(tclInterp, &nResult); Th_ErrorMessage(interp, "could not make Tcl interpreter 'safe':", zResult, nResult); rc = TH_ERROR; }else{ Th_SetResult(interp, 0, 0); } Tcl_Release((ClientData)tclInterp); return rc; } /* ** Tcl command: th1Eval arg ** ** Evaluates the TH1 script and returns its result verbatim. If a TH1 script ** error is generated, it will be transformed into a Tcl script error. */ static int Th1EvalObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[] ){ Th_Interp *th1Interp; int nArg; const char *arg; int rc; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "arg"); return TCL_ERROR; } th1Interp = (Th_Interp *)clientData; if( !th1Interp ){ Tcl_AppendResult(interp, "invalid TH1 interpreter", NULL); return TCL_ERROR; } arg = Tcl_GetStringFromObj(objv[1], &nArg); rc = Th_Eval(th1Interp, 0, arg, nArg); arg = Th_GetResult(th1Interp, &nArg); Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg)); return getTclReturnCode(rc); } /* ** Tcl command: th1Expr arg ** ** Evaluates the TH1 expression and returns its result verbatim. If a TH1 ** script error is generated, it will be transformed into a Tcl script error. */ static int Th1ExprObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[] ){ Th_Interp *th1Interp; int nArg; const char *arg; int rc; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "arg"); return TCL_ERROR; } th1Interp = (Th_Interp *)clientData; if( !th1Interp ){ Tcl_AppendResult(interp, "invalid TH1 interpreter", NULL); return TCL_ERROR; } arg = Tcl_GetStringFromObj(objv[1], &nArg); rc = Th_Expr(th1Interp, arg, nArg); arg = Th_GetResult(th1Interp, &nArg); Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg)); return getTclReturnCode(rc); } /* ** Array of Tcl integration commands. Used when adding or removing the Tcl ** integration commands from TH1. */ static struct _Command { const char *zName; Th_CommandProc xProc; void *pContext; } aCommand[] = { {"tclEval", tclEval_command, 0}, {"tclExpr", tclExpr_command, 0}, {"tclInvoke", tclInvoke_command, 0}, {"tclIsSafe", tclIsSafe_command, 0}, {"tclMakeSafe", tclMakeSafe_command, 0}, {0, 0, 0} }; /* ** Called if the Tcl interpreter is deleted. Removes the Tcl integration ** commands from the TH1 interpreter. */ static void Th1DeleteProc( ClientData clientData, Tcl_Interp *interp ){ int i; Th_Interp *th1Interp = (Th_Interp *)clientData; if( !th1Interp ) return; /* Remove the Tcl integration commands. */ for(i=0; i<count(aCommand); i++){ Th_RenameCommand(th1Interp, aCommand[i].zName, -1, NULL, 0); } } /* ** When Tcl stubs support is enabled, attempts to dynamically load the Tcl ** shared library and fetch the function pointers necessary to create an ** interpreter and initialize the stubs mechanism; otherwise, simply setup ** the function pointers provided by the caller with the statically linked ** functions. */ char *fossil_getenv(const char *zName); /* file.h */ int file_isdir(const char *zPath, int); /* file.h */ #define ExtFILE 0 /* file.h */ #define RepoFILE 1 /* file.h */ #define SymFILE 2 /* file.h */ char *file_dirname(const char *zPath); /* file.h */ void fossil_free(void *p); /* util.h */ static int loadTcl( Th_Interp *interp, void **phLibrary, tcl_FindExecutableProc **pxFindExecutable, tcl_CreateInterpProc **pxCreateInterp, tcl_DeleteInterpProc **pxDeleteInterp, tcl_FinalizeProc **pxFinalize ){ #if defined(USE_TCL_STUBS) const char *zEnvPath = fossil_getenv(TCL_PATH_ENV_VAR_NAME); char aFileName[] = TCL_LIBRARY_NAME; #endif /* defined(USE_TCL_STUBS) */ if( !phLibrary || !pxFindExecutable || !pxCreateInterp || !pxDeleteInterp || !pxFinalize ){ Th_ErrorMessage(interp, "invalid Tcl loader argument(s)", (const char *)"", 0); return TH_ERROR; } #if defined(USE_TCL_STUBS) do { char *zFileName; void *hLibrary; if( !zEnvPath ){ zFileName = aFileName; /* NOTE: Assume present in PATH. */ }else if( file_isdir(zEnvPath, ExtFILE)==1 ){ #if TCL_USE_SET_DLL_DIRECTORY SetDllDirectory(zEnvPath); /* NOTE: Maybe needed for "zlib1.dll". */ #endif /* TCL_USE_SET_DLL_DIRECTORY */ /* NOTE: The environment variable contains a directory name. */ zFileName = sqlite3_mprintf("%s%c%s%c", zEnvPath, TCL_DIRECTORY_SEP, aFileName, '\0'); }else{ #if TCL_USE_SET_DLL_DIRECTORY char *zDirName = file_dirname(zEnvPath); if( zDirName ){ SetDllDirectory(zDirName); /* NOTE: Maybe needed for "zlib1.dll". */ } #endif /* TCL_USE_SET_DLL_DIRECTORY */ /* NOTE: The environment variable might contain a file name. */ zFileName = sqlite3_mprintf("%s%c", zEnvPath, '\0'); #if TCL_USE_SET_DLL_DIRECTORY if( zDirName ){ fossil_free(zDirName); zDirName = 0; } #endif /* TCL_USE_SET_DLL_DIRECTORY */ } if( !zFileName ) break; hLibrary = dlopen(zFileName, RTLD_NOW | RTLD_GLOBAL); /* NOTE: If the file name was allocated, free it now. */ if( zFileName!=aFileName ){ sqlite3_free(zFileName); zFileName = 0; } if( hLibrary ){ tcl_FindExecutableProc *xFindExecutable; tcl_CreateInterpProc *xCreateInterp; tcl_DeleteInterpProc *xDeleteInterp; tcl_FinalizeProc *xFinalize; const char *procName = TCL_FINDEXECUTABLE_NAME; xFindExecutable = (tcl_FindExecutableProc *)dlsym(hLibrary, procName+1); if( !xFindExecutable ){ xFindExecutable = (tcl_FindExecutableProc *)dlsym(hLibrary, procName); } if( !xFindExecutable ){ Th_ErrorMessage(interp, "could not locate Tcl_FindExecutable", (const char *)"", 0); dlclose(hLibrary); hLibrary = 0; return TH_ERROR; } procName = TCL_CREATEINTERP_NAME; xCreateInterp = (tcl_CreateInterpProc *)dlsym(hLibrary, procName+1); if( !xCreateInterp ){ xCreateInterp = (tcl_CreateInterpProc *)dlsym(hLibrary, procName); } if( !xCreateInterp ){ Th_ErrorMessage(interp, "could not locate Tcl_CreateInterp", (const char *)"", 0); dlclose(hLibrary); hLibrary = 0; return TH_ERROR; } procName = TCL_DELETEINTERP_NAME; xDeleteInterp = (tcl_DeleteInterpProc *)dlsym(hLibrary, procName+1); if( !xDeleteInterp ){ xDeleteInterp = (tcl_DeleteInterpProc *)dlsym(hLibrary, procName); } if( !xDeleteInterp ){ Th_ErrorMessage(interp, "could not locate Tcl_DeleteInterp", (const char *)"", 0); dlclose(hLibrary); hLibrary = 0; return TH_ERROR; } procName = TCL_FINALIZE_NAME; xFinalize = (tcl_FinalizeProc *)dlsym(hLibrary, procName+1); if( !xFinalize ){ xFinalize = (tcl_FinalizeProc *)dlsym(hLibrary, procName); } if( !xFinalize ){ Th_ErrorMessage(interp, "could not locate Tcl_Finalize", (const char *)"", 0); dlclose(hLibrary); hLibrary = 0; return TH_ERROR; } *phLibrary = hLibrary; *pxFindExecutable = xFindExecutable; *pxCreateInterp = xCreateInterp; *pxDeleteInterp = xDeleteInterp; *pxFinalize = xFinalize; return TH_OK; } } while( --aFileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */ aFileName[TCL_MINOR_OFFSET] = 'x'; Th_ErrorMessage(interp, "could not load any supported Tcl 8.x shared library \"", aFileName, -1); return TH_ERROR; #else *phLibrary = 0; *pxFindExecutable = Tcl_FindExecutable; *pxCreateInterp = Tcl_CreateInterp; *pxDeleteInterp = Tcl_DeleteInterp; *pxFinalize = Tcl_Finalize; return TH_OK; #endif /* defined(USE_TCL_STUBS) */ } /* ** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter ** based on the supplied command line arguments. */ static int setTclArguments( Tcl_Interp *pInterp, int argc, char **argv ){ Tcl_Obj *objPtr; Tcl_Obj *resultObjPtr; Tcl_Obj *listPtr; int rc = TCL_OK; if( argc<=0 || !argv ){ return TCL_OK; } objPtr = Tcl_NewStringObj(argv[0], -1); Tcl_IncrRefCount(objPtr); resultObjPtr = Tcl_SetVar2Ex(pInterp, "argv0", NULL, objPtr, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); Tcl_DecrRefCount(objPtr); objPtr = 0; if( !resultObjPtr ){ return TCL_ERROR; } objPtr = Tcl_NewWideIntObj(argc - 1); Tcl_IncrRefCount(objPtr); resultObjPtr = Tcl_SetVar2Ex(pInterp, "argc", NULL, objPtr, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); Tcl_DecrRefCount(objPtr); objPtr = 0; if( !resultObjPtr ){ return TCL_ERROR; } listPtr = Tcl_NewListObj(0, NULL); Tcl_IncrRefCount(listPtr); if( argc>1 ){ while( --argc ){ objPtr = Tcl_NewStringObj(*++argv, -1); Tcl_IncrRefCount(objPtr); rc = Tcl_ListObjAppendElement(pInterp, listPtr, objPtr); Tcl_DecrRefCount(objPtr); objPtr = 0; if( rc!=TCL_OK ){ break; } } } if( rc==TCL_OK ){ resultObjPtr = Tcl_SetVar2Ex(pInterp, "argv", NULL, listPtr, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); if( !resultObjPtr ){ rc = TCL_ERROR; } } Tcl_DecrRefCount(listPtr); listPtr = 0; 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 bCancel, int bWait, int bVerbose ){ struct TclContext *tclContext = (struct TclContext *)pContext; Tcl_Interp *tclInterp; int rc; int flags = TCL_ALL_EVENTS; int useTip285; if( createTclInterp(interp, pContext)!=TH_OK ){ return TH_ERROR; } tclInterp = tclContext->interp; useTip285 = bCancel ? tclContext->useTip285 : 0; 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; Tcl_Preserve((ClientData)tclInterp); while( Tcl_DoOneEvent(flags) ){ if( Tcl_InterpDeleted(tclInterp) ){ break; } #if MINIMUM_TCL_VERSION(8, 6) if( useTip285 && Tcl_Canceled(tclInterp, 0)!=TCL_OK ){ break; } #endif } Tcl_Release((ClientData)tclInterp); 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; const 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->hLibrary, &tclContext->xFindExecutable, &tclContext->xCreateInterp, &tclContext->xDeleteInterp, &tclContext->xFinalize)!=TH_OK ){ return TH_ERROR; } argc = tclContext->argc; argv = tclContext->argv; if( argc>0 && argv ){ argv0 = argv[0]; } tclContext->xFindExecutable(argv0); tclInterp = tclContext->xCreateInterp(); if( !tclInterp ){ Th_ErrorMessage(interp, "could not create Tcl interpreter", (const char *)"", 0); return TH_ERROR; } #if defined(USE_TCL_STUBS) #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) if( initTclStubs(interp, tclInterp)!=TH_OK ){ tclContext->xDeleteInterp(tclInterp); tclInterp = 0; return TH_ERROR; } #else if( !Tcl_InitStubs(tclInterp, "8.4", 0) ){ Th_ErrorMessage(interp, "could not initialize Tcl stubs", (const char *)"", 0); tclContext->xDeleteInterp(tclInterp); tclInterp = 0; return TH_ERROR; } #endif /* defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) */ #endif /* defined(USE_TCL_STUBS) */ if( Tcl_InterpDeleted(tclInterp) ){ Th_ErrorMessage(interp, "Tcl interpreter appears to be deleted", (const char *)"", 0); Tcl_DeleteInterp(tclInterp); /* TODO: Redundant? */ tclInterp = 0; return TH_ERROR; } tclContext->interp = tclInterp; if( Tcl_Init(tclInterp)!=TCL_OK ){ Th_ErrorMessage(interp, "Tcl initialization error:", Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1); Tcl_DeleteInterp(tclInterp); tclContext->interp = tclInterp = 0; return TH_ERROR; } if( setTclArguments(tclInterp, argc, argv)!=TCL_OK ){ Th_ErrorMessage(interp, "Tcl error setting arguments:", Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1); Tcl_DeleteInterp(tclInterp); tclContext->interp = tclInterp = 0; return TH_ERROR; } /* ** Determine (and cache) if an objProc can be called directly for a Tcl ** command invoked via the tclInvoke TH1 command. */ tclContext->useObjProc = canUseObjProc(); /* ** Determine (and cache) whether or not we can use TIP #285 (asynchronous ** script cancellation). */ tclContext->useTip285 = canUseTip285(); /* Add the TH1 integration commands to Tcl. */ Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp); Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL); Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL); /* If necessary, evaluate the custom Tcl setup script. */ setup = tclContext->setup; if( setup && Tcl_EvalEx(tclInterp, setup, -1, 0)!=TCL_OK ){ Th_ErrorMessage(interp, "Tcl setup script error:", Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1); Tcl_DeleteInterp(tclInterp); tclContext->interp = tclInterp = 0; return TH_ERROR; } return TH_OK; } /* ** Finalizes and unloads the previously loaded Tcl library, if applicable. */ int unloadTcl( Th_Interp *interp, void *pContext ){ struct TclContext *tclContext = (struct TclContext *)pContext; Tcl_Interp *tclInterp; tcl_FinalizeProc *xFinalize; #if defined(USE_TCL_STUBS) void *hLibrary; #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). */ if( xFinalize ) xFinalize(); #if defined(USE_TCL_STUBS) /* ** If Tcl is compiled on Windows using the latest MinGW, Fossil can crash ** when exiting while a stubs-enabled Tcl is still loaded. This is due to ** a bug in MinGW, see: ** ** http://comments.gmane.org/gmane.comp.gnu.mingw.user/41724 ** ** The workaround is to manually unload the loaded Tcl library prior to ** exiting the process. */ hLibrary = tclContext->hLibrary; if( hLibrary ){ dlclose(hLibrary); tclContext->hLibrary = hLibrary = 0; } #endif /* defined(USE_TCL_STUBS) */ return TH_OK; } /* ** Register the Tcl language commands with interpreter interp. ** Usually this is called soon after interpreter creation. */ int th_register_tcl( Th_Interp *interp, void *pContext ){ int i; /* Add the Tcl integration commands to TH1. */ for(i=0; i<count(aCommand); 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 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 | ** 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) /* ** timeline mode options */ #define TIMELINE_MODE_NONE 0 #define TIMELINE_MODE_BEFORE 1 #define TIMELINE_MODE_AFTER 2 #define TIMELINE_MODE_CHILDREN 3 #define TIMELINE_MODE_PARENTS 4 #define TIMELINE_FMT_ONELINE \ "%h %c" #define TIMELINE_FMT_MEDIUM \ "Commit: %h%nDate: %d%nAuthor: %a%nComment: %c" #define TIMELINE_FMT_FULL \ "Commit: %H%nDate: %d%nAuthor: %a%nComment: %c%n"\ "Branch: %b%nTags: %t%nPhase: %p" /* ** 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_version(const char *zVerHash){ if( g.perm.Hyperlink ){ @ %z(chref("timelineHistLink","%R/info/%!S",zVerHash))[%S(zVerHash)]</a> }else{ @ <span class="timelineHistDsp">[%S(zVerHash)]</span> } } /* ** Generate a hyperlink to a date & time. */ void hyperlink_to_date(const char *zDate, const char *zSuffix){ if( zSuffix==0 ) zSuffix = ""; if( g.perm.Hyperlink ){ @ %z(href("%R/timeline?c=%T",zDate))%s(zDate)</a>%s(zSuffix) }else{ @ %s(zDate)%s(zSuffix) } } /* ** Generate a hyperlink to a user. This will link to a timeline showing ** events by that user. If the date+time is specified, then the timeline ** is centered on that date+time. */ void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){ if( zU==0 || zU[0]==0 ) zU = "anonymous"; if( zSuf==0 ) zSuf = ""; if( g.perm.Hyperlink ){ if( zD && zD[0] ){ @ %z(href("%R/timeline?c=%T&u=%T&y=a",zD,zU))%h(zU)</a>%s(zSuf) }else{ @ %z(href("%R/timeline?u=%T&y=a",zU))%h(zU)</a>%s(zSuf) } }else{ @ %s(zU) } } /* ** Allowed flags for the tmFlags argument to www_print_timeline */ #if INTERFACE #define TIMELINE_ARTID 0x0000001 /* Show artifact IDs on non-check-in lines*/ #define TIMELINE_LEAFONLY 0x0000002 /* Show "Leaf" but not "Merge", "Fork" etc*/ #define TIMELINE_BRIEF 0x0000004 /* Combine adjacent elements of same obj */ #define TIMELINE_GRAPH 0x0000008 /* Compute a graph */ #define TIMELINE_DISJOINT 0x0000010 /* Elements are not contiguous */ #define TIMELINE_FCHANGES 0x0000020 /* Detail file changes */ #define TIMELINE_BRCOLOR 0x0000040 /* Background color by branch name */ #define TIMELINE_UCOLOR 0x0000080 /* Background color by user */ #define TIMELINE_FRENAMES 0x0000100 /* Detail only file name changes */ #define TIMELINE_UNHIDE 0x0000200 /* Unhide check-ins with "hidden" tag */ #define TIMELINE_SHOWRID 0x0000400 /* Show RID values in addition to hashes */ #define TIMELINE_BISECT 0x0000800 /* Show supplemental bisect information */ #define TIMELINE_COMPACT 0x0001000 /* Use the "compact" view style */ #define TIMELINE_VERBOSE 0x0002000 /* Use the "detailed" view style */ #define TIMELINE_MODERN 0x0004000 /* Use the "modern" view style */ #define TIMELINE_COLUMNAR 0x0008000 /* Use the "columns" view style */ #define TIMELINE_CLASSIC 0x0010000 /* Use the "classic" view style */ #define TIMELINE_VIEWS 0x001f000 /* Mask for all of the view styles */ #define TIMELINE_NOSCROLL 0x0100000 /* Don't scroll to the selection */ #define TIMELINE_FILEDIFF 0x0200000 /* Show File differences, not ckin diffs */ #define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */ #define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */ #define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */ #define TIMELINE_NOTKT 0x2000000 /* Omit extra ticket classes */ #define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */ #define TIMELINE_REFS 0x8000000 /* Output intended for References tab */ #define TIMELINE_DELTA 0x10000000 /* Background color shows delta manifests */ #define TIMELINE_NOCOLOR 0x20000000 /* No colors except for highlights */ #endif /* ** Return a new timelineTable id. */ int timeline_tableid(void){ static int id = 0; return id++; } /* ** Return true if the checking identified by "rid" has a valid "closed" ** tag. */ static int has_closed_tag(int rid){ static Stmt q; int res = 0; db_static_prepare(&q, "SELECT 1 FROM tagxref WHERE rid=$rid AND tagid=%d AND tagtype>0", TAG_CLOSED); db_bind_int(&q, "$rid", rid); res = db_step(&q)==SQLITE_ROW; db_reset(&q); return res; } /* ** Return the text of the unformatted ** forum post given by the RID in the argument. */ static void forum_post_content_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ int rid = sqlite3_value_int(argv[0]); Manifest *pPost = manifest_get(rid, CFTYPE_FORUM, 0); if( pPost ){ sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT); manifest_destroy(pPost); } } /* ** Output a timeline in the web format given a query. The query ** should return these columns: ** ** 0. rid ** 1. artifact hash ** 2. Date/Time ** 3. Comment string ** 4. User ** 5. True if is a leaf ** 6. background color ** 7. type ("ci", "w", "t", "e", "g", "f", "div") ** 8. list of symbolic tags. ** 9. tagid for ticket or wiki or event ** 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 */ const char *zLeftBranch, /* Strive to put this branch on the left margin */ int selectedRid, /* Highlight the line with this RID value or zero */ int secondRid, /* Secondary highlight (or zero) */ 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 check-out version */ int dateFormat = 0; /* 0: HH:MM (default) */ int bCommentGitStyle = 0; /* Only show comments through first blank line */ const char *zStyle; /* Sub-name for classes for the style */ const char *zDateFmt; int iTableId = timeline_tableid(); int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info ** page rather than the /timeline page */ if( cgi_is_loopback(g.zIpAddr) && 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); bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0); bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0); if( (tmFlags & TIMELINE_VIEWS)==0 ){ tmFlags |= timeline_ss_cookie(); } if( tmFlags & TIMELINE_COLUMNAR ){ zStyle = "Columnar"; }else if( tmFlags & TIMELINE_COMPACT ){ zStyle = "Compact"; }else if( tmFlags & TIMELINE_VERBOSE ){ zStyle = "Verbose"; }else if( tmFlags & TIMELINE_CLASSIC ){ zStyle = "Classic"; }else{ zStyle = "Modern"; } zDateFmt = P("datefmt"); if( zDateFmt ) dateFormat = atoi(zDateFmt); if( tmFlags & TIMELINE_GRAPH ){ pGraph = graph_init(); } db_static_prepare(&qbranch, "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", TAG_BRANCH ); if( (tmFlags & TIMELINE_CHPICK)!=0 && !db_table_exists("repository","cherrypick") ){ tmFlags &= ~TIMELINE_CHPICK; } @ <table id="timelineTable%d(iTableId)" class="timelineTable"> \ @ <!-- tmFlags: 0x%x(tmFlags) --> blob_zero(&comment); while( db_step(pQuery)==SQLITE_ROW ){ int rid = db_column_int(pQuery, 0); const char *zUuid = db_column_text(pQuery, 1); int isLeaf = db_column_int(pQuery, 5); const char *zBgClr = db_column_text(pQuery, 6); const char *zDate = db_column_text(pQuery, 2); const char *zType = db_column_text(pQuery, 7); 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 *zDateLink; /* URL for the link on the timestamp */ int drawDetailEllipsis; /* True to show ellipsis in place of detail */ int gidx = 0; /* Graph row identifier */ int isSelectedOrCurrent = 0; /* True if current row is selected */ const char *zExtraClass = ""; 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> 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 class="timelineDateRow"><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="timelineSelected"> isSelectedOrCurrent = 1; }else if( rid==secondRid ){ @ <tr class="timelineSelected timelineSecondary"> isSelectedOrCurrent = 1; }else if( rid==vid ){ @ <tr class="timelineCurrent"> isSelectedOrCurrent = 1; }else { @ <tr> } if( zType[0]=='t' && tagid && (tmFlags & TIMELINE_NOTKT)==0 ){ char *zTktid = db_text(0, "SELECT substr(tagname,5) FROM tag" " WHERE tagid=%d", tagid); if( zTktid ){ int isClosed = 0; if( is_ticket(zTktid, &isClosed) && isClosed ){ zExtraClass = " tktTlClosed"; }else{ zExtraClass = " tktTlOpen"; } fossil_free(zTktid); } } if( zType[0]=='e' && tagid ){ if( bTimestampLinksToInfo ){ char *zId; zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d", tagid); zDateLink = href("%R/technote/%s",zId); free(zId); }else{ zDateLink = href("%R/timeline?c=%t&y=a",zDate); } }else if( zUuid ){ if( bTimestampLinksToInfo ){ zDateLink = chref("timelineHistLink", "%R/info/%!S", zUuid); }else{ zDateLink = chref("timelineHistLink", "%R/timeline?c=%!S&y=a", zUuid); } }else{ zDateLink = mprintf("<a>"); } @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td> @ <td class="timelineGraph"> if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){ if( tmFlags & TIMELINE_UCOLOR ){ zBgClr = zUser ? user_color(zUser) : 0; }else if( tmFlags & TIMELINE_NOCOLOR ){ zBgClr = 0; }else if( zType[0]=='c' ){ static Stmt qdelta; db_static_prepare(&qdelta, "SELECT baseid IS NULL FROM plink" " WHERE cid=:rid"); db_bind_int(&qdelta, ":rid", rid); if( db_step(&qdelta)!=SQLITE_ROW ){ zBgClr = 0; /* Not a check-in */ }else if( db_column_int(&qdelta, 0) ){ zBgClr = hash_color("b"); /* baseline manifest */ }else{ zBgClr = hash_color("f"); /* delta manifest */ } db_reset(&qdelta); } } if( zType[0]=='c' && (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0) ){ db_reset(&qbranch); db_bind_int(&qbranch, ":rid", rid); if( db_step(&qbranch)==SQLITE_ROW ){ zBr = db_column_text(&qbranch, 0); }else{ zBr = "trunk"; } if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){ if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){ }else if( zBr==0 || strcmp(zBr,"trunk")==0 ){ zBgClr = 0; }else{ zBgClr = hash_color(zBr); } } } if( zType[0]=='c' && pGraph ){ int nParent = 0; int nCherrypick = 0; GraphRowId aParent[GR_MAX_RAIL]; 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<count(aParent) ){ aParent[nParent++] = db_column_int(&qparent, 0); } db_reset(&qparent); if( (tmFlags & TIMELINE_CHPICK)!=0 && nParent>0 ){ static Stmt qcherrypick; db_static_prepare(&qcherrypick, "SELECT parentid FROM cherrypick" " WHERE childid=:rid AND parentid NOT IN phantom" ); db_bind_int(&qcherrypick, ":rid", rid); while( db_step(&qcherrypick)==SQLITE_ROW && nParent<count(aParent) ){ aParent[nParent++] = db_column_int(&qcherrypick, 0); nCherrypick++; } db_reset(&qcherrypick); } gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent, zBr, zBgClr, zUuid, isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0); db_reset(&qbranch); @ <div id="m%d(gidx)" class="tl-nodemark"></div> }else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){ /* For technotes, make a graph node with nParent==(-1). This will ** not actually draw anything on the graph, but it will set the ** background color of the timeline entry */ gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0); @ <div id="m%d(gidx)" class="tl-nodemark"></div> } @</td> if( !isSelectedOrCurrent ){ @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'> }else{ @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)"> } if( pGraph ){ if( zType[0]=='e' ){ @ <b>Note:</b> }else if( zType[0]!='c' ){ @ • } } if( modPending ){ @ <span class="modpending">(Awaiting Moderator Approval)</span> } if( (tmFlags & TIMELINE_BISECT)!=0 && zType[0]=='c' ){ static Stmt bisectQuery; db_static_prepare(&bisectQuery, "SELECT seq, stat FROM bilog WHERE rid=:rid AND seq"); db_bind_int(&bisectQuery, ":rid", rid); if( db_step(&bisectQuery)==SQLITE_ROW ){ @ <b>%s(db_column_text(&bisectQuery,1))</b> @ (%d(db_column_int(&bisectQuery,0))) } db_reset(&bisectQuery); } drawDetailEllipsis = (tmFlags & (TIMELINE_COMPACT))!=0; db_column_blob(pQuery, commentColumn, &comment); if( tmFlags & TIMELINE_COMPACT ){ @ <span class='timelineCompactComment' data-id='%d(rid)'> }else{ @ <span class='timeline%s(zStyle)Comment'> } if( (tmFlags & TIMELINE_CLASSIC)!=0 ){ if( zType[0]=='c' ){ hyperlink_to_version(zUuid); if( isLeaf ){ if( has_closed_tag(rid) ){ @ <span class="timelineLeaf">Closed-Leaf:</span> }else{ @ <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_version(zUuid); } if( tmFlags & TIMELINE_SHOWRID ){ int srcId = delta_source_rid(rid); if( srcId ){ @ (%z(href("%R/deltachain/%d",rid))%d(rid)←%d(srcId)</a>) }else{ @ (%z(href("%R/deltachain/%d",rid))%d(rid)</a>) } } } 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 */ if( zType[0]=='w' ){ const char *zCom = blob_str(&comment); /* Except, the comments generated by "fossil rebuild" for a wiki ** page edit consist of a single character '-', '+', or ':' (to ** indicate "deleted", "added", or "edited") followed by the ** raw wiki page name. We have to generate an appropriate ** comment on-the-fly */ wiki_hyperlink_override(zUuid); if( zCom[0]=='-' ){ @ Deleted wiki page "%z(href("%R/whistory?name=%t",zCom+1))\ @ %h(zCom+1)</a>" }else if( (tmFlags & TIMELINE_REFS)!=0 && (zCom[0]=='+' || zCom[0]==':') ){ @ Wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>" }else if( zCom[0]=='+' ){ @ Added wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>" }else if( zCom[0]==':' ){ @ %z(href("%R/wdiff?id=%!S",zUuid))Changes</a> to wiki page @ "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a>" }else{ /* Assume this is an attachment message. It _might_ also ** be a legacy-format wiki log entry, in which case it ** will simply be rendered in the older format. */ wiki_convert(&comment, 0, WIKI_INLINE); } wiki_hyperlink_override(0); }else{ wiki_convert(&comment, 0, WIKI_INLINE); } }else{ if( bCommentGitStyle ){ /* Truncate comment at first blank line */ int ii, jj; int n = blob_size(&comment); char *z = blob_str(&comment); for(ii=0; ii<n; ii++){ if( z[ii]=='\n' ){ for(jj=ii+1; jj<n && z[jj]!='\n' && fossil_isspace(z[jj]); jj++){} if( z[jj]=='\n' ) break; } } z[ii] = 0; cgi_printf("%W",z); }else if( mxWikiLen>0 && (int)blob_size(&comment)>mxWikiLen ){ Blob truncated; blob_zero(&truncated); blob_append(&truncated, blob_buffer(&comment), mxWikiLen); blob_append(&truncated, "...", 3); @ %W(blob_str(&truncated)) blob_reset(&truncated); drawDetailEllipsis = 0; }else{ cgi_printf("%W",blob_str(&comment)); } } @ </span> blob_reset(&comment); /* Generate extra information and hyperlinks to follow the comment. ** Example: "(check-in: [abcdefg], user: drh, tags: trunk)" */ if( drawDetailEllipsis ){ @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \ @ data-id='%d(rid)'>...</span> } if( tmFlags & TIMELINE_COLUMNAR ){ if( !isSelectedOrCurrent ){ @ <td class="timelineDetailCell%s(zExtraClass)" id='md%d(gidx)'> }else{ @ <td class="timelineDetailCell%s(zExtraClass)"> } } if( tmFlags & TIMELINE_COMPACT ){ cgi_printf("<span class='clutter' id='detail-%d'>",rid); } cgi_printf("<span class='timeline%sDetail'>", zStyle); if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){ cgi_printf("("); } if( (tmFlags & TIMELINE_CLASSIC)==0 ){ if( zType[0]=='c' ){ if( isLeaf ){ if( has_closed_tag(rid) ){ @ <span class='timelineLeaf'>Closed-Leaf</span> }else{ @ <span class='timelineLeaf'>Leaf</span> } } cgi_printf("check-in: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); }else if( zType[0]=='e' && tagid ){ cgi_printf("technote: "); hyperlink_to_event_tagid(tagid<0?-tagid:tagid); }else{ cgi_printf("artifact: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); } }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t' || zType[0]=='n' || zType[0]=='f'){ cgi_printf("artifact: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); } if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){ char *zLink; if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){ zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate); }else{ zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate); } cgi_printf("user: %z%h</a>", href("%z",zLink), zDispUser); }else{ cgi_printf("user: %h", zDispUser); } /* Generate the "tags: TAGLIST" at the end of the comment, together ** with hyperlinks to the tag list. */ if( zTagList && zTagList[0]==0 ) zTagList = 0; 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&c=%t",i,z,zDate), i,z, &z[i] ); }else{ blob_appendf(&links, "%#h", i+2, z); } if( z[i]==0 ) break; z += i+2; } cgi_printf(" tags: %s", blob_str(&links)); blob_reset(&links); }else{ cgi_printf(" tags: %h", zTagList); } } if( tmFlags & TIMELINE_SHOWRID ){ int srcId = delta_source_rid(rid); if( srcId ){ cgi_printf(" id: %z%d←%d</a>", href("%R/deltachain/%d",rid), rid, srcId); }else{ cgi_printf(" id: %z%d</a>", href("%R/deltachain/%d",rid), rid); } } tag_private_status(rid); if( xExtra ){ xExtra(rid); } /* End timelineDetail */ if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){ cgi_printf(")"); } if( tmFlags & TIMELINE_COMPACT ){ @ </span></span> }else{ @ </span> } /* 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," " 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)<=0; int isMergeNew = db_column_int(&fchngQuery, 0)<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; if( !inUl ){ @ <ul class="filelist"> inUl = 1; } if( tmFlags & TIMELINE_SHOWRID ){ int srcId = delta_source_rid(fid); if( srcId ){ zId = mprintf(" (%z%d←%d</a>) ", href("%R/deltachain/%d", fid), fid, srcId); }else{ zId = mprintf(" (%z%d</a>) ", href("%R/deltachain/%d", fid), fid); } }else{ zId = fossil_strdup(""); } 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) if( isMergeNew ){ @ (added by merge) }else{ @ (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?v1=%!S&v2=%!S",zOld,zNew))[diff]</a></li> } fossil_free(zA); fossil_free(zId); } db_reset(&fchngQuery); if( inUl ){ @ </ul> } } /* Show the complete text of forum messages */ if( (tmFlags & (TIMELINE_FORUMTXT))!=0 && zType[0]=='f' && g.perm.Hyperlink && (!content_is_private(rid) || g.perm.ModForum) ){ Manifest *pPost = manifest_get(rid, CFTYPE_FORUM, 0); if( pPost ){ const char *zClass = "forumTimeline"; if( forum_rid_has_been_edited(rid) ){ zClass = "forumTimeline forumObs"; } forum_render(0, pPost->zMimetype, pPost->zWiki, zClass, 1); manifest_destroy(pPost); } } } 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, zLeftBranch, tmFlags); if( pGraph->nErr ){ graph_free(pGraph); pGraph = 0; }else{ @ <tr class="timelineBottom" id="btm-%d(iTableId)">\ @ <td></td><td></td><td></td></tr> } } @ </table> if( fchngQueryInit ) db_finalize(&fchngQuery); timeline_output_graph_javascript(pGraph, tmFlags, iTableId); } /* ** 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] = x[i]>=mx-t ? x[i] - (mx-t) : 0; } } } 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 tmFlags, /* Flags that control rendering */ int iTableId /* Which graph is this for */ ){ if( pGraph && pGraph->nErr==0 ){ GraphRow *pRow; int i; char cSep; 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 */ int iTopRow; /* Index of the top row of the graph */ int fileDiff; /* True for file diff. False for check-in diff */ int omitDescenders; /* True to omit descenders */ int scrollToSelect; /* True to scroll to the selection */ int dwellTimeout; /* Milliseconds to wait for tooltips to show */ int closeTimeout; /* Milliseconds to wait for tooltips to close */ u8 *aiMap; /* The rail map */ iRailPitch = atoi(PD("railpitch","0")); showArrowheads = skin_detail_boolean("timeline-arrowheads"); circleNodes = skin_detail_boolean("timeline-circle-nodes"); colorGraph = skin_detail_boolean("timeline-color-graph-lines"); iTopRow = pGraph->pFirst ? pGraph->pFirst->idx : 0; omitDescenders = (tmFlags & TIMELINE_DISJOINT)!=0; fileDiff = (tmFlags & TIMELINE_FILEDIFF)!=0; scrollToSelect = (tmFlags & TIMELINE_NOSCROLL)==0; dwellTimeout = atoi(db_get("timeline-dwelltime","100")); closeTimeout = atoi(db_get("timeline-closetime","250")); @ <script id='timeline-data-%d(iTableId)' type='application/json'>{ @ "iTableId": %d(iTableId), @ "circleNodes": %d(circleNodes), @ "showArrowheads": %d(showArrowheads), @ "iRailPitch": %d(iRailPitch), @ "colorGraph": %d(colorGraph), @ "nomo": %d(PB("nomo")), @ "iTopRow": %d(iTopRow), @ "omitDescenders": %d(omitDescenders), @ "fileDiff": %d(fileDiff), @ "scrollToSelect": %d(scrollToSelect), @ "nrail": %d(pGraph->mxRail+1), @ "baseUrl": "%R", @ "dwellTimeout": %d(dwellTimeout), @ "closeTimeout": %d(closeTimeout), @ "hashDigits": %d(hash_digits(1)), @ "bottomRowId": "btm-%d(iTableId)", if( pGraph->nRow==0 ){ @ "rowinfo": null }else{ @ "rowinfo": [ } /* 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 iTopRow 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: If exists and true then there is a "descender" - an arrow ** coming from the bottom of the page or further down on the page ** straight up to this node. ** mo: "merge-out". If it exists, this is the rail position ** for the upward portion of a merge arrow. The merge arrow goes as ** a solid normal merge line up to the row identified by "mu" and ** then as a dashed cherrypick merge line up further to "cu". ** If this value is omitted if there are no merge children. ** mu: The id of the row which is the top of the merge-out arrow. ** Only exists if "mo" exists. ** cu: Extend the mu merge arrow up to this row as a cherrypick ** merge line, if this value exists. ** 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 or just up a little ways, -1 if there is ** no thick-line riser (if the node is a leaf). ** sb: Draw a dotted child-line out of the top of this node up to the ** node with the id equal to the value. This is like "u" except ** that the line is dotted instead of solid and has no arrow. ** Mnemonic: "Same Branch". ** f: 0x01: a leaf node, 0x02: a closed 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. If there ** are no risers, this array does not exist. ** mi: "merge-in". An array of integer rail positions from which ** merge arrows should be drawn into this node. If the value is ** negative, then the rail position is -1-mi[] and a thin merge-arrow ** descender is drawn to the bottom of the screen. This array is ** omitted if there are no inbound merges. ** ci: "cherrypick-in". Like "mi" except for cherrypick merges. ** omitted if there are no cherrypick merges. ** h: The artifact hash of the object being graphed * br: The branch to which the artifact belongs */ aiMap = pGraph->aiRailMap; for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ int k = 0; cgi_printf("{\"id\":%d,", pRow->idx); cgi_printf("\"bg\":\"%s\",", pRow->zBgClr); cgi_printf("\"r\":%d,", pRow->iRail>=0 ? aiMap[pRow->iRail] : -1); if( pRow->bDescender ){ cgi_printf("\"d\":%d,", pRow->bDescender); } if( pRow->mergeOut>=0 ){ cgi_printf("\"mo\":%d,", aiMap[pRow->mergeOut]); if( pRow->mergeUpto==0 ) pRow->mergeUpto = pRow->idx; cgi_printf("\"mu\":%d,", pRow->mergeUpto); if( pRow->cherrypickUpto>0 && pRow->cherrypickUpto<=pRow->mergeUpto ){ cgi_printf("\"cu\":%d,", pRow->cherrypickUpto); } } if( pRow->isStepParent ){ cgi_printf("\"sb\":%d,", pRow->aiRiser[pRow->iRail]); }else{ cgi_printf("\"u\":%d,", pRow->aiRiser[pRow->iRail]); } k = 0; if( pRow->isLeaf ) k |= 1; if( pRow->isLeaf & 2) k |= 2; cgi_printf("\"f\":%d,",k); for(i=k=0; i<GR_MAX_RAIL; i++){ if( i==pRow->iRail ) continue; if( pRow->aiRiser[i]>0 ){ if( k==0 ){ cgi_printf("\"au\":"); cSep = '['; } k++; cgi_printf("%c%d,%d", cSep, aiMap[i], pRow->aiRiser[i]); cSep = ','; } } if( k ){ cgi_printf("],"); } if( colorGraph && pRow->zBgClr[0]=='#' ){ cgi_printf("\"fg\":\"%s\",", bg_to_fg(pRow->zBgClr)); } /* mi */ for(i=k=0; i<GR_MAX_RAIL; i++){ if( pRow->mergeIn[i]==1 ){ int mi = aiMap[i]; if( (pRow->mergeDown >> i) & 1 ) mi = -1-mi; if( k==0 ){ cgi_printf("\"mi\":"); cSep = '['; } k++; cgi_printf("%c%d", cSep, mi); cSep = ','; } } if( k ) cgi_printf("],"); /* ci */ for(i=k=0; i<GR_MAX_RAIL; i++){ if( pRow->mergeIn[i]==2 ){ int mi = aiMap[i]; if( (pRow->cherrypickDown >> i) & 1 ) mi = -mi; if( k==0 ){ cgi_printf("\"ci\":"); cSep = '['; } k++; cgi_printf("%c%d", cSep, mi); cSep = ','; } } if( k ) cgi_printf("],"); cgi_printf("\"br\":\"%j\",", pRow->zBranch ? pRow->zBranch : ""); cgi_printf("\"h\":\"%!S\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "]\n"); } @ }</script> builtin_request_js("graph.js"); builtin_request_js("copybtn.js"); /* Required by graph.js */ graph_free(pGraph); } } /* ** Create a temporary table suitable for storing timeline data. */ static void timeline_temp_table(void){ static const char zSql[] = @ CREATE TEMP TABLE IF NOT EXISTS timeline( @ rid INTEGER PRIMARY KEY, @ uuid TEXT, @ timestamp TEXT, @ comment TEXT, @ user TEXT, @ isleaf BOOLEAN, @ bgcolor TEXT, @ 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[] = @ SELECT @ blob.rid AS blobRid, @ uuid AS uuid, @ datetime(event.mtime,toLocal()) 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 ; return zBase; } /* ** Convert a symbolic name used as an argument to the a=, b=, or c= ** query parameters of timeline into a julianday mtime value. */ double symbolic_name_to_mtime(const char *z, const char **pzDisplay){ double mtime; int rid; const char *zDate; if( z==0 ) return -1.0; if( fossil_isdate(z) ){ mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); if( mtime>0.0 ) return mtime; } zDate = fossil_expand_datetime(z, 1); if( zDate!=0 ){ mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", fossil_roundup_date(zDate)); if( mtime>0.0 ){ if( pzDisplay ) *pzDisplay = fossil_strdup(zDate); 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&m=%!S", zFN, zUuid), 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[16]; 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"; az[i++] = "n"; az[i++] = "New Tickets"; } if( g.perm.RdWiki ){ az[i++] = "w"; az[i++] = "Wiki"; } if( g.perm.RdForum ){ az[i++] = "f"; az[i++] = "Forum"; } assert( i<=count(az) ); } if( i>2 ){ style_submenu_multichoice("y", i/2, az, isDisabled); } } /* ** Return the default value for the "ss" cookie or query parameter. ** The "ss" cookie determines the graph style. See the ** timeline_view_styles[] global constant for a list of choices. */ const char *timeline_default_ss(void){ static const char *zSs = 0; if( zSs==0 ) zSs = db_get("timeline-default-style","m"); return zSs; } /* ** Convert the current "ss" display preferences cookie into an ** appropriate TIMELINE_* flag */ int timeline_ss_cookie(void){ int tmFlags; const char *v = cookie_value("ss",0); if( v==0 ) v = timeline_default_ss(); switch( v[0] ){ case 'c': tmFlags = TIMELINE_COMPACT; break; case 'v': tmFlags = TIMELINE_VERBOSE; break; case 'j': tmFlags = TIMELINE_COLUMNAR; break; case 'x': tmFlags = TIMELINE_CLASSIC; break; default: tmFlags = TIMELINE_MODERN; break; } return tmFlags; } /* Available timeline display styles, together with their y= query ** parameter names. */ const char *const timeline_view_styles[] = { "m", "Modern View", "j", "Columnar View", "c", "Compact View", "v", "Verbose View", "x", "Classic View", }; #if INTERFACE # define N_TIMELINE_VIEW_STYLE 5 #endif /* ** Add the select/option box to the timeline submenu that is used to ** set the ss= parameter that determines the viewing mode. ** ** Return the TIMELINE_* value appropriate for the view-style. */ int timeline_ss_submenu(void){ cookie_link_parameter("ss","ss",timeline_default_ss()); style_submenu_multichoice("ss", N_TIMELINE_VIEW_STYLE, timeline_view_styles, 0); return timeline_ss_cookie(); } /* ** If the zChng string is not NULL, then it should be a comma-separated ** list of glob patterns for filenames. Add an term to the WHERE clause ** for the SQL statement under construction that excludes any check-in that ** does not modify one or more files matching the globs. */ static void addFileGlobExclusion( const char *zChng, /* The filename GLOB list */ Blob *pSql /* The SELECT statement under construction */ ){ if( zChng==0 || zChng[0]==0 ) return; blob_append_sql(pSql," AND event.objid IN (" "SELECT mlink.mid FROM mlink, filename" " WHERE mlink.fnid=filename.fnid AND %s)", glob_expr("filename.name", mprintf("\"%s\"", zChng))); } static void addFileGlobDescription( const char *zChng, /* The filename GLOB list */ Blob *pDescription /* Result description */ ){ if( zChng==0 || zChng[0]==0 ) return; blob_appendf(pDescription, " that include changes to files matching '%h'", zChng); } /* ** Tag match expression type code. */ typedef enum { MS_EXACT, /* Matches a single tag by exact string comparison. */ MS_GLOB, /* Matches tags against a list of GLOB patterns. */ MS_LIKE, /* Matches tags against a list of LIKE patterns. */ MS_REGEXP, /* Matches tags against a list of regular expressions. */ MS_BRLIST, /* Same as REGEXP, except the regular expression is a list ** of branch names */ } MatchStyle; /* ** Quote a tag string by surrounding it with double quotes and preceding ** internal double quotes and backslashes with backslashes. */ static const char *tagQuote( int len, /* Maximum length of zTag, or negative for unlimited */ const char *zTag /* Tag string */ ){ Blob blob = BLOB_INITIALIZER; int i, j; blob_zero(&blob); blob_append(&blob, "\"", 1); for( i=j=0; zTag[j] && (len<0 || j<len); ++j ){ if( zTag[j]=='\"' || zTag[j]=='\\' ){ if( j>i ){ blob_append(&blob, zTag+i, j-i); } blob_append(&blob, "\\", 1); i = j; } } if( j>i ){ blob_append(&blob, zTag+i, j-i); } blob_append(&blob, "\"", 1); return blob_str(&blob); } /* ** Construct the tag match SQL expression. ** ** This function is adapted from glob_expr() to support the MS_EXACT, MS_GLOB, ** MS_LIKE, MS_REGEXP, and MS_BRLIST match styles. ** ** For MS_EXACT, the returned expression ** checks for integer match against the tag ID which is looked up directly by ** this function. For the other modes, the returned SQL expression performs ** string comparisons against the tag names, so it is necessary to join against ** the tag table to access the "tagname" column. ** ** Each pattern is adjusted to to start with "sym-" and be anchored at end. ** ** In MS_REGEXP mode, backslash can be used to protect delimiter characters. ** The backslashes are not removed from the regular expression. ** ** In addition to assembling and returning an SQL expression, this function ** makes an English-language description of the patterns being matched, suitable ** for display in the web interface. ** ** If any errors arise during processing, *zError is set to an error message. ** Otherwise it is set to NULL. */ static const char *tagMatchExpression( MatchStyle matchStyle, /* Match style code */ const char *zTag, /* Tag name, match pattern, or pattern list */ const char **zDesc, /* Output expression description string */ const char **zError /* Output error string */ ){ Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */ Blob desc = BLOB_INITIALIZER; /* English description of match patterns */ Blob err = BLOB_INITIALIZER; /* Error text assembly buffer */ const char *zStart; /* Text at start of expression */ const char *zDelimiter; /* Text between expression terms */ const char *zEnd; /* Text at end of expression */ const char *zPrefix; /* Text before each match pattern */ const char *zSuffix; /* Text after each match pattern */ const char *zIntro; /* Text introducing pattern description */ const char *zPattern = 0; /* Previous quoted pattern */ const char *zFail = 0; /* Current failure message or NULL if okay */ const char *zOr = " or "; /* Text before final quoted pattern */ char cDel; /* Input delimiter character */ int i; /* Input match pattern length counter */ /* Optimize exact matches by looking up the ID in advance to create a simple * numeric comparison. Bypass the remainder of this function. */ if( matchStyle==MS_EXACT ){ *zDesc = tagQuote(-1, zTag); return mprintf("(tagid=%d)", db_int(-1, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag)); } /* Decide pattern prefix and suffix strings according to match style. */ if( matchStyle==MS_GLOB ){ zStart = "("; zDelimiter = " OR "; zEnd = ")"; zPrefix = "tagname GLOB 'sym-"; zSuffix = "'"; zIntro = "glob pattern "; }else if( matchStyle==MS_LIKE ){ zStart = "("; zDelimiter = " OR "; zEnd = ")"; zPrefix = "tagname LIKE 'sym-"; zSuffix = "'"; zIntro = "SQL LIKE pattern "; }else if( matchStyle==MS_REGEXP ){ zStart = "(tagname REGEXP '^sym-("; zDelimiter = "|"; zEnd = ")$')"; zPrefix = ""; zSuffix = ""; zIntro = "regular expression "; }else/* if( matchStyle==MS_BRLIST )*/{ zStart = "tagname IN ('sym-"; zDelimiter = "','sym-"; zEnd = "')"; zPrefix = ""; zSuffix = ""; zIntro = ""; } /* Convert the list of matches into an SQL expression and text description. */ blob_zero(&expr); blob_zero(&desc); blob_zero(&err); while( 1 ){ /* Skip leading delimiters. */ for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag ); /* Next non-delimiter character determines quoting. */ if( !*zTag ){ /* Terminate loop at end of string. */ break; }else if( *zTag=='\'' || *zTag=='"' ){ /* If word is quoted, prepare to stop at end quote. */ cDel = *zTag; ++zTag; }else{ /* If word is not quoted, prepare to stop at delimiter. */ cDel = ','; } /* Find the next delimiter character or end of string. */ for( i=0; zTag[i] && zTag[i]!=cDel; ++i ){ /* If delimiter is comma, also recognize spaces as delimiters. */ if( cDel==',' && fossil_isspace(zTag[i]) ){ break; } /* In regexp mode, ignore delimiters following backslashes. */ if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){ ++i; } } /* Check for regular expression syntax errors. */ if( matchStyle==MS_REGEXP ){ ReCompiled *regexp; char *zTagDup = fossil_strndup(zTag, i); zFail = re_compile(®exp, zTagDup, 0); re_free(regexp); fossil_free(zTagDup); } /* Process success and error results. */ if( !zFail ){ /* Incorporate the match word into the output expression. %q is used to * protect against SQL injection attacks by replacing ' with ''. */ blob_appendf(&expr, "%s%s%#q%s", blob_size(&expr) ? zDelimiter : zStart, zPrefix, i, zTag, zSuffix); /* Build up the description string. */ if( !blob_size(&desc) ){ /* First tag: start with intro followed by first quoted tag. */ blob_append(&desc, zIntro, -1); blob_append(&desc, tagQuote(i, zTag), -1); }else{ if( zPattern ){ /* Third and subsequent tags: append comma then previous tag. */ blob_append(&desc, ", ", 2); blob_append(&desc, zPattern, -1); zOr = ", or "; } /* Second and subsequent tags: store quoted tag for next iteration. */ zPattern = tagQuote(i, zTag); } }else{ /* On error, skip the match word and build up the error message buffer. */ if( !blob_size(&err) ){ blob_append(&err, "Error: ", 7); }else{ blob_append(&err, ", ", 2); } blob_appendf(&err, "(%s%s: %s)", zIntro, tagQuote(i, zTag), zFail); } /* Advance past all consumed input characters. */ zTag += i; if( cDel!=',' && *zTag==cDel ){ ++zTag; } } /* Finalize and extract the pattern description. */ if( zPattern ){ blob_append(&desc, zOr, -1); blob_append(&desc, zPattern, -1); } *zDesc = blob_str(&desc); /* Finalize and extract the error text. */ *zError = blob_size(&err) ? blob_str(&err) : 0; /* Finalize and extract the SQL expression. */ if( blob_size(&expr) ){ blob_append(&expr, zEnd, -1); return blob_str(&expr); } /* If execution reaches this point, the pattern was empty. Return NULL. */ return 0; } /* ** Similar to fossil_expand_datetime() ** ** Add missing "-" characters into a date/time. Examples: ** ** 20190419 => 2019-04-19 ** 201904 => 2019-04 */ const char *timeline_expand_datetime(const char *zIn){ static char zEDate[20]; static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' }; int n = (int)strlen(zIn); int i, j; /* Only three forms allowed: ** (1) YYYYMMDD ** (2) YYYYMM ** (3) YYYYWW */ if( n!=8 && n!=6 ) return zIn; /* Every character must be a digit */ for(i=0; fossil_isdigit(zIn[i]); i++){} if( i!=n ) return zIn; /* Expand the date */ for(i=j=0; zIn[i]; i++){ if( i>=4 && (i%2)==0 ){ zEDate[j++] = aPunct[i/2]; } zEDate[j++] = zIn[i]; } zEDate[j] = 0; /* It looks like this may be a date. Return it with punctuation added. */ return zEDate; } /* ** Find the first check-in encountered with a particular tag ** when moving either forwards are backwards in time from a ** particular starting point (iFrom). Return the rid of that ** first check-in. If there are no check-ins in the decendent ** or ancestor set of check-in iFrom that match the tag, then ** return 0. */ static int timeline_endpoint( int iFrom, /* Starting point */ const char *zEnd, /* Tag we are searching for */ int bForward /* 1: forwards in time (descendents) 0: backwards */ ){ int tagId; int endId = 0; Stmt q; int ans = 0; tagId = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zEnd); if( tagId==0 ){ endId = symbolic_name_to_rid(zEnd, "ci"); if( endId==0 ) return 0; } if( bForward ){ if( tagId ){ db_prepare(&q, "WITH RECURSIVE dx(id,mtime) AS (" " SELECT %d, event.mtime FROM event WHERE objid=%d" " UNION" " SELECT plink.cid, plink.mtime" " FROM dx, plink" " WHERE plink.pid=dx.id" " AND plink.mtime<=(SELECT max(event.mtime) FROM tagxref, event" " WHERE tagxref.tagid=%d AND tagxref.tagtype>0" " AND event.objid=tagxref.rid)" " ORDER BY plink.mtime)" "SELECT id FROM dx, tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=id LIMIT 1", iFrom, iFrom, tagId, tagId ); }else{ db_prepare(&q, "WITH RECURSIVE dx(id,mtime) AS (" " SELECT %d, event.mtime FROM event WHERE objid=%d" " UNION" " SELECT plink.cid, plink.mtime" " FROM dx, plink" " WHERE plink.pid=dx.id" " AND plink.mtime<=(SELECT mtime FROM event WHERE objid=%d)" " ORDER BY plink.mtime)" "SELECT id FROM dx WHERE id=%d", iFrom, iFrom, endId, endId ); } }else{ if( tagId ){ db_prepare(&q, "WITH RECURSIVE dx(id,mtime) AS (" " SELECT %d, event.mtime FROM event WHERE objid=%d" " UNION" " SELECT plink.pid, event.mtime" " FROM dx, plink, event" " WHERE plink.cid=dx.id AND event.objid=plink.pid" " AND event.mtime>=(SELECT min(event.mtime) FROM tagxref, event" " WHERE tagxref.tagid=%d AND tagxref.tagtype>0" " AND event.objid=tagxref.rid)" " ORDER BY event.mtime DESC)" "SELECT id FROM dx, tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=id LIMIT 1", iFrom, iFrom, tagId, tagId ); }else{ db_prepare(&q, "WITH RECURSIVE dx(id,mtime) AS (" " SELECT %d, event.mtime FROM event WHERE objid=%d" " UNION" " SELECT plink.pid, event.mtime" " FROM dx, plink, event" " WHERE plink.cid=dx.id AND event.objid=plink.pid" " AND event.mtime>=(SELECT mtime FROM event WHERE objid=%d)" " ORDER BY event.mtime DESC)" "SELECT id FROM dx WHERE id=%d", iFrom, iFrom, endId, endId ); } } if( db_step(&q)==SQLITE_ROW ){ ans = db_column_int(&q, 0); } db_finalize(&q); return ans; } /* ** COMMAND: test-endpoint ** ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? ** ** Show the first check-in with TAG that is a descendent or ancestor ** of BASE. The first descendent checkin is shown by default. Use ** the --backto to see the first ancestor checkin. ** ** Options: ** ** --backto Show ancestor. Others defaults to descendents. */ void timeline_test_endpoint(void){ int bForward = find_option("backto",0,0)==0; int from_rid; int ans; db_find_and_open_repository(0, 0); verify_all_options(); if( g.argc!=4 ){ usage("BASE-CHECKIN TAG ?--backto?"); } from_rid = symbolic_name_to_rid(g.argv[2],"ci"); ans = timeline_endpoint(from_rid, g.argv[3], bForward); if( ans ){ fossil_print("Result: %d (%S)\n", ans, rid_to_uuid(ans)); }else{ fossil_print("No path found\n"); } } /* ** WEBPAGE: timeline ** ** Query parameters: ** ** a=TIMEORTAG Show events after TIMEORTAG ** b=TIMEORTAG Show events before TIMEORTAG ** c=TIMEORTAG Show events that happen "circa" TIMEORTAG ** cf=FILEHASH Show events around the time of the first use of ** the file with FILEHASH ** m=TIMEORTAG Highlight the event at TIMEORTAG, or the closest available ** event if TIMEORTAG is not part of the timeline. If ** the t= or r= is used, the m event is added to the timeline ** if it isn't there already. ** x=HASHLIST Show all check-ins in the comma-separated HASHLIST ** in addition to check-ins specified by t= or r= ** sel1=TIMEORTAG Highlight the check-in at TIMEORTAG if it is part of ** the timeline. Similar to m= except TIMEORTAG must ** match a check-in that is already in the timeline. ** sel2=TIMEORTAG Like sel1= but use the secondary highlight. ** n=COUNT Maximum number of events. "all" for no limit ** n1=COUNT Same as "n" but doesn't set the display-preference cookie ** Use "n1=COUNT" for a one-time display change ** p=CHECKIN Parents and ancestors of CHECKIN ** bt=PRIOR ... going back to PRIOR ** d=CHECKIN Children and descendants of CHECKIN ** ft=DESCENDANT ... going forward to DESCENDANT ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN ** p=CX ... from CX back to time of CHECKIN ** from=CX ... shortest path from CX back to CHECKIN ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN ** d=CX ... from CX up to the time of CHECKIN ** from=CX ... shortest path from CX up to CHECKIN ** t=TAG Show only check-ins with the given TAG ** r=TAG Show check-ins related to TAG, equivalent to t=TAG&rel ** tl=TAGLIST Shorthand for t=TAGLIST&ms=brlist ** rl=TAGLIST Shorthand for r=TAGLIST&ms=brlist ** rel Show related check-ins as well as those matching t=TAG ** mionly Limit rel to show ancestors but not descendants ** nowiki Do not show wiki associated with branch or tag ** ms=MATCHSTYLE Set tag match style to EXACT, GLOB, LIKE, REGEXP ** u=USER Only show items associated with USER ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'. ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar", ** x: "Classic". ** advm Use the "Advanced" or "Busy" menu design. ** ng No Graph. ** ncp Omit cherrypick merges ** nd Do not highlight the focus check-in ** nsm Omit the submenu ** nc Omit all graph colors other than highlights ** v Show details of files changed ** vfx Show complete text of forum messages ** f=CHECKIN Show family (immediate parents and children) of CHECKIN ** from=CHECKIN Path from... ** to=CHECKIN ... to this ** to2=CHECKIN ... backup name if to= doesn't resolve ** shortest ... show only the shortest path ** rel ... also show related checkins ** bt=PRIOR ... path from CHECKIN back to PRIOR ** ft=LATER ... path from CHECKIN forward to LATER ** uf=FILE_HASH Show only check-ins that contain the given file version ** All qualifying check-ins are shown unless there is ** also an n= or n1= query parameter. ** chng=GLOBLIST Show only check-ins that involve changes to a file whose ** name matches one of the comma-separate GLOBLIST ** brbg Background color determined by branch name ** ubg Background color determined by user ** deltabg Background color red for delta manifests or green ** for baseline manifests ** namechng Show only check-ins that have filename changes ** forks Show only forks and their children ** cherrypicks Show all cherrypicks ** ym=YYYY-MM Show only events for the given year/month ** yw=YYYY-WW Show only events for the given week of the given year ** yw=YYYY-MM-DD Show events for the week that includes the given day ** ymd=YYYY-MM-DD Show only events on the given day. The use "ymd=now" ** to see all changes for the current week. ** year=YYYY Show only events on the given year. The use "year=0" ** to see all changes for the current year. ** days=N Show events over the previous N days ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS, ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off". ** bisect Show the check-ins that are in the current bisect ** oldestfirst Show events oldest first. ** showid Show RIDs ** showsql Show the SQL text ** ** 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. ** ** When multiple time-related filters are used, e.g. ym, yw, and ymd, ** which one(s) is/are applied is unspecified and may change between ** fossil versions. ** ** CHECKIN or TIMEORTAG can be a check-in hash prefix, or a tag, or the ** name of a branch. */ 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; /* artifact p and its parents */ int d_rid; /* artifact d and descendants */ int f_rid; /* artifact f and close family */ const char *zUser = P("u"); /* All entries by this user if not NULL */ const char *zType; /* Type of events to display */ 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"); /* Equivalent to t=TAG&rel */ int related = PB("rel"); /* Show events related to zTagName */ const char *zMatchStyle = P("ms"); /* Tag/branch match style string */ MatchStyle matchStyle = MS_EXACT; /* Match style code */ const char *zMatchDesc = 0; /* Tag match expression description text */ const char *zError = 0; /* Tag match error string */ const char *zTagSql = 0; /* Tag/branch match SQL expression */ 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) */ char *zYearWeekStart = 0; /* YYYY-MM-DD for start of YYYY-WW */ const char *zDay = P("ymd"); /* Check-ins for the day YYYY-MM-DD */ const char *zYear = P("year"); /* Events for the year YYYY */ const char *zNDays = P("days"); /* Show events over the previous N days */ int nDays = 0; /* Numeric value for zNDays */ const char *zChng = P("chng"); /* List of GLOBs for files that changed */ int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */ int renameOnly = P("namechng")!=0; /* Show only check-ins that rename files */ int forkOnly = PB("forks"); /* Show only forks and their children */ int bisectLocal = PB("bisect"); /* Show the check-ins of the bisect */ const char *zBisect = P("bid"); /* Bisect description */ int cpOnly = PB("cherrypicks"); /* Show all cherrypick checkins */ 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 */ const char *zTo2 = 0; int to_rid = name_choice("to","to2",&zTo2); /* 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 */ char *zOlderButtonLabel = 0; /* Label for the Older Button */ char *zNewerButton = 0; /* URL for Newer button at the top */ char *zNewerButtonLabel = 0; /* Label for the Newer button */ int selectedRid = 0; /* Show a highlight on this RID */ int secondaryRid = 0; /* Show secondary highlight */ int disableY = 0; /* Disable type selector on submenu */ int advancedMenu = 0; /* Use the advanced menu design */ char *zPlural; /* Ending for plural forms */ int showCherrypicks = 1; /* True to show cherrypick merges */ int haveParameterN; /* True if n= query parameter present */ int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */ url_initialize(&url, "timeline"); cgi_query_parameters_to_url(&url); (void)P_NoBot("ss") /* "ss" is processed via the udc but at least one spider likes to ** try to SQL inject via this argument, so let's catch that. */; /* Set number of rows to display */ z = P("n"); if( z!=0 ){ haveParameterN = 1; cookie_write_parameter("n","n",0); }else{ const char *z2; haveParameterN = 0; cookie_read_parameter("n","n"); z = P("n"); if( z==0 ){ z = db_get("timeline-default-length",0); } cgi_replace_query_parameter("n",fossil_strdup(z)); cookie_write_parameter("n","n",0); z2 = P("n1"); if( z2 ){ haveParameterN = 2; z = z2; } } if( z ){ if( fossil_strcmp(z,"all")==0 ){ nEntry = 0; }else{ nEntry = atoi(z); if( nEntry<=0 ){ z = "50"; nEntry = 50; } } }else{ nEntry = 50; } /* Query parameters d=, p=, and f= and variants */ z = P("p"); p_rid = z ? name_to_typed_rid(z,"ci") : 0; z = P("d"); d_rid = z ? name_to_typed_rid(z,"ci") : 0; z = P("f"); f_rid = z ? name_to_typed_rid(z,"ci") : 0; z = P("df"); if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){ nEntry = 0; useDividers = 0; cgi_replace_query_parameter("d",fossil_strdup(z)); } /* Undocumented query parameter to set JS mode */ builtin_set_js_delivery_mode(P("jsmode"),1); secondaryRid = name_to_typed_rid(P("sel2"),"ci"); selectedRid = name_to_typed_rid(P("sel1"),"ci"); if( from_rid!=0 && to_rid!=0 ){ if( selectedRid==0 ) selectedRid = from_rid; if( secondaryRid==0 ) secondaryRid = to_rid; } tmFlags |= timeline_ss_submenu(); cookie_link_parameter("advm","advm","0"); advancedMenu = atoi(PD("advm","0")); /* Omit all cherry-pick merge lines if the "ncp" query parameter is ** present or if this repository lacks a "cherrypick" table. */ if( PB("ncp") || !db_table_exists("repository","cherrypick") ){ showCherrypicks = 0; } /* 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 && !g.perm.RdForum) || (bisectLocal && !g.perm.Setup) ){ login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); return; } if( !bisectLocal ){ etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0); } cookie_read_parameter("y","y"); zType = P("y"); if( zType==0 ){ zType = g.perm.Read ? "ci" : "all"; cgi_set_parameter("y", zType); } if( zType[0]=='a' || ( g.perm.Read && zType[0]=='c' ) || ( g.perm.RdTkt && (zType[0]=='t' || zType[0]=='n') ) || ( g.perm.RdWiki && (zType[0]=='w' || zType[0]=='e') ) || ( g.perm.RdForum && zType[0]=='f' ) ){ cookie_write_parameter("y","y",zType); } /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */ if( P("cf")!=0 ){ zCirca = db_text(0, "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)" " FROM mlink, event" " WHERE mlink.fid=(SELECT rid FROM blob WHERE uuid LIKE '%q%%')" " AND event.objid=mlink.mid" " ORDER BY event.mtime LIMIT 1", P("cf") ); } /* Check for tl=TAGLIST and rl=TAGLIST which are abbreviations for ** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */ if( zBrName==0 && zTagName==0 ){ const char *z; if( (z = P("tl"))!=0 ){ zTagName = z; zMatchStyle = "brlist"; } if( (z = P("rl"))!=0 ){ zBrName = z; zMatchStyle = "brlist"; } } /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ if( zBrName && !related ){ cgi_delete_query_parameter("r"); cgi_set_query_parameter("t", zBrName); (void)P("t"); cgi_set_query_parameter("rel", "1"); zTagName = zBrName; related = 1; zType = "ci"; } /* Ignore empty tag query strings. */ if( zTagName && !*zTagName ){ zTagName = 0; } /* Finish preliminary processing of tag match queries. */ if( zTagName ){ zType = "ci"; /* Interpet the tag style string. */ if( fossil_stricmp(zMatchStyle, "glob")==0 ){ matchStyle = MS_GLOB; }else if( fossil_stricmp(zMatchStyle, "like")==0 ){ matchStyle = MS_LIKE; }else if( fossil_stricmp(zMatchStyle, "regexp")==0 ){ matchStyle = MS_REGEXP; }else if( fossil_stricmp(zMatchStyle, "brlist")==0 ){ matchStyle = MS_BRLIST; }else{ /* For exact maching, inhibit links to the selected tag. */ zThisTag = zTagName; Th_Store("current_checkin", zTagName); } /* Display a checkbox to enable/disable display of related check-ins. */ if( advancedMenu ){ style_submenu_checkbox("rel", "Related", 0, 0); } /* Construct the tag match expression. */ zTagSql = tagMatchExpression(matchStyle, zTagName, &zMatchDesc, &zError); } if( zMark && zMark[0]==0 ){ if( zAfter ) zMark = zAfter; if( zBefore ) zMark = zBefore; if( zCirca ) zMark = zCirca; } if( (zTagSql && db_int(0,"SELECT count(*) " "FROM tagxref NATURAL JOIN tag WHERE %s",zTagSql/*safe-for-%s*/)<=nEntry) ){ nEntry = -1; zCirca = 0; } if( zType[0]=='a' ){ tmFlags |= TIMELINE_BRIEF | TIMELINE_GRAPH | TIMELINE_CHPICK; }else{ tmFlags |= TIMELINE_GRAPH | TIMELINE_CHPICK; } if( related ){ tmFlags |= TIMELINE_FILLGAPS | TIMELINE_XMERGE; tmFlags &= ~TIMELINE_DISJOINT; } if( PB("ncp") ){ tmFlags &= ~TIMELINE_CHPICK; } if( PB("ng") || zSearch!=0 ){ tmFlags &= ~(TIMELINE_GRAPH|TIMELINE_CHPICK); } if( PB("nsm") ){ style_submenu_enable(0); } if( PB("brbg") ){ tmFlags |= TIMELINE_BRCOLOR; } if( PB("unhide") ){ tmFlags |= TIMELINE_UNHIDE; } if( PB("ubg") ){ tmFlags |= TIMELINE_UCOLOR; } if( PB("deltabg") ){ tmFlags |= TIMELINE_DELTA; } if( PB("nc") ){ tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR); tmFlags |= TIMELINE_NOCOLOR; } 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; if( !haveParameterN ) nEntry = 0; }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; } if( forkOnly ){ db_multi_exec( "CREATE TEMP TABLE rnfork(rid INTEGER PRIMARY KEY);\n" "INSERT OR IGNORE INTO rnfork(rid)\n" " SELECT pid FROM plink\n" " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n" " GROUP BY pid" " HAVING count(*)>1;\n" "INSERT OR IGNORE INTO rnfork(rid)" " SELECT cid FROM plink\n" " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n" " GROUP BY cid" " HAVING count(*)>1;\n", TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH ); db_multi_exec( "INSERT OR IGNORE INTO rnfork(rid)\n" " SELECT cid FROM plink\n" " WHERE pid IN rnfork" " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n" " UNION " " SELECT pid FROM plink\n" " WHERE cid IN rnfork" " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==" " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n", TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH ); tmFlags |= TIMELINE_UNHIDE; zType = "ci"; disableY = 1; } if( bisectLocal && cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){ int iCurrent = db_lget_int("checkout",0); char *zPerm = bisect_permalink(); bisect_create_bilog_table(iCurrent, 0, 1); tmFlags |= TIMELINE_UNHIDE | TIMELINE_BISECT | TIMELINE_FILLGAPS; zType = "ci"; disableY = 1; style_submenu_element("Permalink", "%R/timeline?bid=%z", zPerm); }else{ bisectLocal = 0; } if( zBisect!=0 && bisect_create_bilog_table(0, zBisect, 1) ){ tmFlags |= TIMELINE_UNHIDE | TIMELINE_BISECT | TIMELINE_FILLGAPS; zType = "ci"; disableY = 1; }else{ zBisect = 0; } style_header("Timeline"); if( advancedMenu ){ style_submenu_element("Help", "%R/help?cmd=/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( PB("vfx") ){ tmFlags |= TIMELINE_FORUMTXT; } 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 && (P("ft")!=0 || P("bt")!=0) ){ const char *zTo = P("ft"); if( zTo ){ from_to_mode = 1; to_rid = timeline_endpoint(from_rid, zTo, 1); }else{ from_to_mode = 2; zTo = P("bt"); to_rid = timeline_endpoint(from_rid, zTo, 0); } if( to_rid ){ cgi_replace_parameter("to", zTo); if( selectedRid==0 ) selectedRid = from_rid; if( secondaryRid==0 ) secondaryRid = to_rid; }else{ to_rid = from_rid; blob_appendf(&desc, "There is no path from %h %s to %h.<br>Instead: ", P("from"), from_to_mode==1 ? "forward" : "back", zTo); } } 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; Blob ins; int nNodeOnPath = 0; if( from_rid && to_rid ){ if( from_to_mode==0 ){ p = path_shortest(from_rid, to_rid, noMerge, 0, 0); }else if( from_to_mode==1 ){ p = path_shortest(from_rid, to_rid, 0, 1, 0); }else{ p = path_shortest(to_rid, from_rid, 0, 1, 0); } zFrom = P("from"); zTo = zTo2 ? zTo2 : P("to"); }else{ if( path_common_ancestor(me_rid, you_rid) ){ p = path_first(); } zFrom = P("me"); zTo = P("you"); } blob_init(&ins, 0, 0); db_multi_exec( "CREATE TABLE IF NOT EXISTS temp.pathnode(x INTEGER PRIMARY KEY);" ); if( p ){ blob_init(&ins, 0, 0); blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid); p = p->u.pTo; while( p ){ blob_append_sql(&ins, ",(%d)", p->rid); p = p->u.pTo; } } path_reset(); db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/); blob_reset(&ins); if( related || P("mionly") ){ db_multi_exec( "CREATE TABLE IF NOT EXISTS temp.related(x INTEGER PRIMARY KEY);" "INSERT OR IGNORE INTO related(x)" " SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;" ); if( P("mionly")==0 ){ db_multi_exec( "INSERT OR IGNORE INTO related(x)" " SELECT cid FROM plink WHERE pid IN pathnode;" ); } if( showCherrypicks ){ db_multi_exec( "INSERT OR IGNORE INTO related(x)" " SELECT parentid FROM cherrypick WHERE childid IN pathnode;" ); if( P("mionly")==0 ){ db_multi_exec( "INSERT OR IGNORE INTO related(x)" " SELECT childid FROM cherrypick WHERE parentid IN pathnode;" ); } } db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related"); } blob_append_sql(&sql, " AND event.objid IN pathnode"); if( zChng && zChng[0] ){ db_multi_exec( "DELETE FROM pathnode " " WHERE NOT EXISTS(SELECT 1 FROM mlink, filename" " WHERE mlink.mid=x" " AND mlink.fnid=filename.fnid AND %s)", glob_expr("filename.name", zChng) ); } tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; db_multi_exec("%s", blob_sql_text(&sql)); if( advancedMenu ){ style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); } nNodeOnPath = db_int(0, "SELECT count(*) FROM temp.pathnode"); if( nNodeOnPath==1 && from_to_mode>0 ){ blob_appendf(&desc,"Check-in "); }else if( from_to_mode>0 ){ blob_appendf(&desc, "%d check-ins on the shorted path from ",nNodeOnPath); }else{ blob_appendf(&desc, "%d check-ins going from ", nNodeOnPath); } if( from_rid==selectedRid ){ blob_appendf(&desc, "<span class='timelineSelected'>"); } blob_appendf(&desc, "%z%h</a>", href("%R/info/%h", zFrom), zFrom); if( from_rid==selectedRid ) blob_appendf(&desc, "</span>"); if( nNodeOnPath==1 && from_to_mode>0 ){ blob_appendf(&desc, " only"); }else{ blob_append(&desc, " to ", -1); if( to_rid==secondaryRid ){ blob_appendf(&desc,"<span class='timelineSelected timelineSecondary'>"); } blob_appendf(&desc, "%z%h</a>", href("%R/info/%h",zTo), zTo); if( to_rid==secondaryRid ) blob_appendf(&desc, "</span>"); if( related ){ int nRelated = db_int(0, "SELECT count(*) FROM timeline") - nNodeOnPath; if( nRelated>0 ){ blob_appendf(&desc, " and %d related check-in%s", nRelated, nRelated>1 ? "s" : ""); } } } addFileGlobDescription(zChng, &desc); }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){ /* If p= or d= is present, ignore all other parameters other than n= */ char *zUuid; const char *zCiName; int np = 0, nd; const char *zBackTo = 0; const char *zFwdTo = 0; int ridBackTo = 0; int ridFwdTo = 0; tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; if( p_rid && d_rid ){ if( p_rid!=d_rid ) p_rid = d_rid; if( !haveParameterN ) 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); zCiName = pd_rid ? P("pd") : p_rid ? P("p") : P("d"); if( zCiName==0 ) zCiName = zUuid; blob_append_sql(&sql, " AND event.objid IN ok"); nd = 0; if( d_rid ){ Stmt s; double rStopTime = 9e99; zFwdTo = P("ft"); if( zFwdTo ){ double rStartDate = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", d_rid); ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate); if( ridFwdTo==0 ){ ridFwdTo = name_to_typed_rid(zBackTo,"ci"); } if( ridFwdTo ){ if( !haveParameterN ) nEntry = 0; rStopTime = db_double(9e99, "SELECT mtime FROM event WHERE objid=%d", ridFwdTo); } } db_prepare(&s, "WITH RECURSIVE" " dx(rid,mtime) AS (" " SELECT %d, 0" " UNION" " SELECT plink.cid, plink.mtime FROM dx, plink" " WHERE plink.pid=dx.rid" " AND (:stop>=8e99 OR plink.mtime<=:stop)" " ORDER BY 2" " )" "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d", d_rid, nEntry<=0 ? -1 : nEntry+1 ); db_bind_double(&s, ":stop", rStopTime); db_step(&s); db_finalize(&s); /* compute_descendants(d_rid, nEntry==0 ? 0 : 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 || p_rid==0 ){ blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); } if( useDividers && !selectedRid ) selectedRid = d_rid; db_multi_exec("DELETE FROM ok"); } if( p_rid ){ zBackTo = P("bt"); if( zBackTo ){ double rDateLimit = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", p_rid); ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit); if( ridBackTo==0 ){ ridBackTo = name_to_typed_rid(zBackTo,"ci"); } if( ridBackTo && !haveParameterN ) nEntry = 0; } compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); np = db_int(0, "SELECT count(*)-1 FROM ok"); if( np>0 || nd==0 ){ if( nd>0 ) blob_appendf(&desc, " and "); blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); db_multi_exec("%s", blob_sql_text(&sql)); } if( useDividers && !selectedRid ) selectedRid = p_rid; } blob_appendf(&desc, " of %z%h</a>", href("%R/info?name=%h", zCiName), zCiName); if( ridBackTo ){ if( np==0 ){ blob_reset(&desc); blob_appendf(&desc, "Check-in %z%h</a> only (%z%h</a> is not an ancestor)", href("%R/info?name=%h",zCiName), zCiName, href("%R/info?name=%h",zBackTo), zBackTo); }else{ blob_appendf(&desc, " back to %z%h</a>", href("%R/info?name=%h",zBackTo), zBackTo); if( ridFwdTo && zFwdTo ){ blob_appendf(&desc, " and up to %z%h</a>", href("%R/info?name=%h",zFwdTo), zFwdTo); } } }else if( ridFwdTo ){ if( nd==0 ){ blob_reset(&desc); blob_appendf(&desc, "Check-in %z%h</a> only (%z%h</a> is not an descendant)", href("%R/info?name=%h",zCiName), zCiName, href("%R/info?name=%h",zFwdTo), zFwdTo); }else{ blob_appendf(&desc, " up to %z%h</a>", href("%R/info?name=%h",zFwdTo), zFwdTo); } } 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); } } if( advancedMenu ){ style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); } style_submenu_entry("n","Max:",4,0); timeline_y_submenu(1); }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 ); if( showCherrypicks ){ db_multi_exec( "INSERT OR IGNORE INTO ok SELECT parentid FROM cherrypick" " WHERE childid=%d;" "INSERT OR IGNORE INTO ok SELECT childid FROM cherrypick" " WHERE parentid=%d;", f_rid, f_rid ); } blob_append_sql(&sql, " AND event.objid IN ok"); db_multi_exec("%s", blob_sql_text(&sql)); if( useDividers && !selectedRid ) 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_XMERGE; if( advancedMenu ){ style_submenu_checkbox("unhide", "Unhide", 0, 0); style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); } }else{ /* Otherwise, a timeline based on a span of time */ int n; const char *zEType = "event"; char *zDate; Blob cond; blob_zero(&cond); tmFlags |= TIMELINE_FILLGAPS; if( zChng && *zChng ){ addFileGlobExclusion(zChng, &cond); tmFlags |= TIMELINE_XMERGE; } if( zUses ){ blob_append_sql(&cond, " AND event.objid IN usesfile "); } if( renameOnly ){ blob_append_sql(&cond, " AND event.objid IN rnfile "); } if( forkOnly ){ blob_append_sql(&cond, " AND event.objid IN rnfork "); } if( cpOnly && showCherrypicks ){ db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS cpnodes(rid INTEGER PRIMARY KEY);" "INSERT OR IGNORE INTO cpnodes SELECT childid FROM cherrypick;" "INSERT OR IGNORE INTO cpnodes SELECT parentid FROM cherrypick;" ); blob_append_sql(&cond, " AND event.objid IN cpnodes "); } if( bisectLocal || zBisect!=0 ){ blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) "); } if( zYearMonth ){ char *zNext; zYearMonth = timeline_expand_datetime(zYearMonth); if( strlen(zYearMonth)>7 ){ zYearMonth = mprintf("%.7s", zYearMonth); } if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){ zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');"); } zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','+1 month');", zYearMonth); if( db_int(0, "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" " WHERE blob.rid=event.objid AND mtime>=julianday('%q-01')%s)", zNext, blob_sql_text(&cond)) ){ zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0)); zNewerButtonLabel = "Following month"; } fossil_free(zNext); zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','-1 month');", zYearMonth); if( db_int(0, "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" " WHERE blob.rid=event.objid AND mtime<julianday('%q-01')%s)", zYearMonth, blob_sql_text(&cond)) ){ zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0)); zOlderButtonLabel = "Previous month"; } fossil_free(zNext); blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ", zYearMonth); nEntry = -1; } else if( zYearWeek ){ char *z, *zNext; zYearWeek = timeline_expand_datetime(zYearWeek); z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek); if( z && z[0] ){ zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')", zYearWeek); zYearWeek = z; }else{ if( strlen(zYearWeek)==7 ){ zYearWeekStart = db_text(0, "SELECT date('%.4q-01-01','%+d days','weekday 1')", zYearWeek, atoi(zYearWeek+5)*7-6); }else{ zYearWeekStart = 0; } if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){ zYearWeekStart = db_text(0, "SELECT date('now','-6 days','weekday 1');"); zYearWeek = db_text(0, "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')"); } } zNext = db_text(0, "SELECT date(%Q,'+7 day');", zYearWeekStart); if( db_int(0, "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)", zNext, blob_sql_text(&cond)) ){ zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0)); zNewerButtonLabel = "Following week"; } fossil_free(zNext); zNext = db_text(0, "SELECT date(%Q,'-7 days');", zYearWeekStart); if( db_int(0, "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)", zYearWeekStart, blob_sql_text(&cond)) ){ zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0)); zOlderButtonLabel = "Previous week"; } fossil_free(zNext); blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ", zYearWeek); nEntry = -1; } else if( zDay ){ char *zNext; zDay = timeline_expand_datetime(zDay); zDay = db_text(0, "SELECT date(%Q)", zDay); if( zDay==0 || zDay[0]==0 ){ zDay = db_text(0, "SELECT date('now')"); } zNext = db_text(0, "SELECT date(%Q,'+1 day');", zDay); if( db_int(0, "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)", zNext, blob_sql_text(&cond)) ){ zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0)); zNewerButtonLabel = "Following day"; } fossil_free(zNext); zNext = db_text(0, "SELECT date(%Q,'-1 day');", zDay); if( db_int(0, "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)", zDay, blob_sql_text(&cond)) ){ zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0)); zOlderButtonLabel = "Previous day"; } fossil_free(zNext); blob_append_sql(&cond, " AND %Q=date(event.mtime) ", zDay); nEntry = -1; } else if( zNDays ){ nDays = atoi(zNDays); if( nDays<1 ) nDays = 1; blob_append_sql(&cond, " AND event.mtime>=julianday('now','-%d days') ", nDays); nEntry = -1; } else if( zYear && ((4==strlen(zYear) && atoi(zYear)>1900) || (1==strlen(zYear) && 0==atoi(zYear)))){ int year = atoi(zYear); char *zNext = 0; if(0==year){/*use current year*/ Stmt qy; db_prepare(&qy, "SELECT strftime('%%Y','now')"); db_step(&qy); year = db_column_int(&qy, 0); zYear = fossil_strdup(db_column_text(&qy, 0)); db_finalize(&qy); }else{ zNext = mprintf("%d", year+1); if( db_int(0, "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" " WHERE blob.rid=event.objid AND strftime('%%Y',mtime)=%Q %s)", zNext, blob_sql_text(&cond)) ){ zNewerButton = fossil_strdup(url_render(&url, "year", zNext, 0, 0)); zNewerButtonLabel = "Following year"; } fossil_free(zNext); } zNext = mprintf("%d", year-1); if( db_int(0, "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" " WHERE blob.rid=event.objid AND strftime('%%Y',mtime)=%Q %s)", zNext, blob_sql_text(&cond)) ){ zOlderButton = fossil_strdup(url_render(&url, "year", zNext, 0, 0)); zOlderButtonLabel = "Previous year"; } fossil_free(zNext); blob_append_sql(&cond, " AND %Q=strftime('%%Y',event.mtime) ", zYear); nEntry = -1; } if( zTagSql ){ db_multi_exec( "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);" "INSERT OR IGNORE INTO selected_nodes" " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag" " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/ ); if( zMark ){ /* If the t=release option is used with m=UUID, then also ** include the UUID check-in in the display list */ int ridMark = name_to_rid(zMark); db_multi_exec( "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark); } if( P("x")!=0 ){ char *zX = fossil_strdup(P("x")); int ii; int ridX; while( zX[0] ){ char c; if( zX[0]==',' || zX[0]==' ' ){ zX++; continue; } for(ii=1; zX[ii] && zX[ii]!=',' && zX[ii]!=' '; ii++){} c = zX[ii]; zX[ii] = 0; ridX = name_to_rid(zX); db_multi_exec( "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridX); zX[ii] = c; zX += ii; } } if( !related ){ blob_append_sql(&cond, " AND blob.rid IN selected_nodes"); }else{ db_multi_exec( "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);" "INSERT INTO related_nodes SELECT rid FROM selected_nodes;" ); blob_append_sql(&cond, " AND blob.rid IN related_nodes"); /* 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. These 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. */ db_multi_exec( "INSERT OR IGNORE INTO related_nodes" " SELECT pid FROM selected_nodes CROSS JOIN plink" " WHERE selected_nodes.rid=plink.cid;" ); if( P("mionly")==0 ){ db_multi_exec( "INSERT OR IGNORE INTO related_nodes" " SELECT cid FROM selected_nodes CROSS JOIN plink" " WHERE selected_nodes.rid=plink.pid;" ); if( showCherrypicks ){ db_multi_exec( "INSERT OR IGNORE INTO related_nodes" " SELECT childid FROM selected_nodes CROSS JOIN cherrypick" " WHERE selected_nodes.rid=cherrypick.parentid;" ); } } if( showCherrypicks ){ db_multi_exec( "INSERT OR IGNORE INTO related_nodes" " SELECT parentid FROM selected_nodes CROSS JOIN cherrypick" " WHERE selected_nodes.rid=cherrypick.childid;" ); } if( (tmFlags & TIMELINE_UNHIDE)==0 ){ db_multi_exec( "DELETE FROM related_nodes WHERE rid IN " " (SELECT related_nodes.rid FROM related_nodes, tagxref" " WHERE tagid=%d AND tagtype>0 AND tagxref.rid=related_nodes.rid)", TAG_HIDDEN ); } } } if( (zType[0]=='w' && !g.perm.RdWiki) || (zType[0]=='t' && !g.perm.RdTkt) || (zType[0]=='n' && !g.perm.RdTkt) || (zType[0]=='e' && !g.perm.RdWiki) || (zType[0]=='c' && !g.perm.Read) || (zType[0]=='g' && !g.perm.Read) || (zType[0]=='f' && !g.perm.RdForum) ){ zType = "all"; } if( zType[0]=='a' ){ if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){ char cSep = '('; blob_append_sql(&cond, " AND event.type IN "); if( g.perm.Read ){ blob_append_sql(&cond, "%c'ci','g'", cSep); cSep = ','; } if( g.perm.RdWiki ){ blob_append_sql(&cond, "%c'w','e'", cSep); cSep = ','; } if( g.perm.RdTkt ){ blob_append_sql(&cond, "%c't'", cSep); cSep = ','; } if( g.perm.RdForum ){ blob_append_sql(&cond, "%c'f'", cSep); cSep = ','; } blob_append_sql(&cond, ")"); } }else{ /* zType!="all" */ if( zType[0]=='n' ){ blob_append_sql(&cond, " AND event.type='t' AND event.comment GLOB 'New ticket*'"); }else{ blob_append_sql(&cond, " AND event.type=%Q", zType); } if( zType[0]=='c' ){ zEType = "check-in"; }else if( zType[0]=='w' ){ zEType = "wiki"; }else if( zType[0]=='t' ){ zEType = "ticket change"; }else if( zType[0]=='n' ){ zEType = "new ticket"; }else if( zType[0]=='e' ){ zEType = "technical note"; }else if( zType[0]=='g' ){ zEType = "tag"; }else if( zType[0]=='f' ){ zEType = "forum post"; } } if( zUser ){ int n = db_int(0,"SELECT count(*) FROM event" " WHERE user=%Q OR euser=%Q", zUser, zUser); if( n<=nEntry ){ nEntry = -1; } blob_append_sql(&cond, " AND (event.user=%Q OR event.euser=%Q)", zUser, zUser); zThisUser = zUser; } if( zSearch ){ if( tmFlags & TIMELINE_FORUMTXT ){ sqlite3_create_function(g.db, "forum_post_content", 1, SQLITE_UTF8, 0, forum_post_content_function, 0, 0); blob_append_sql(&cond, " AND (event.comment LIKE '%%%q%%'" " OR event.brief LIKE '%%%q%%'" " OR (event.type=='f' AND" " forum_post_content(event.objid) LIKE '%%%q%%'))", zSearch, zSearch, zSearch); }else{ blob_append_sql(&cond, " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", zSearch, zSearch); } } rBefore = symbolic_name_to_mtime(zBefore, &zBefore); rAfter = symbolic_name_to_mtime(zAfter, &zAfter); rCirca = symbolic_name_to_mtime(zCirca, &zCirca); blob_append_sql(&sql, "%s", blob_sql_text(&cond)); 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 ASC", rCirca); if( nEntry>0 ){ blob_append_sql(&sql2," LIMIT %d", (nEntry+1)/2); } if( PB("showsql") ){ @ <pre>%h(blob_sql_text(&sql2))</pre> } db_multi_exec("%s", blob_sql_text(&sql2)); if( nEntry>0 ){ nEntry -= db_int(0,"select count(*) from timeline"); if( nEntry<=0 ) nEntry = 1; } blob_reset(&sql2); blob_append_sql(&sql, " AND event.mtime<=%f ORDER BY event.mtime DESC", 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*/"); zPlural = n==1 ? "" : "s"; if( zYearMonth ){ blob_appendf(&desc, "%d %s%s for the month beginning %h-01", n, zEType, zPlural, zYearMonth); }else if( zYearWeek ){ blob_appendf(&desc, "%d %s%s for week %h beginning on %h", n, zEType, zPlural, zYearWeek, zYearWeekStart); }else if( zDay ){ blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay); }else if( zNDays ){ blob_appendf(&desc, "%d %s%s within the past %d day%s", n, zEType, zPlural, nDays, nDays>1 ? "s" : ""); }else if( zBefore==0 && zCirca==0 && n>=nEntry && nEntry>0 ){ blob_appendf(&desc, "%d most recent %s%s", n, zEType, zPlural); }else{ blob_appendf(&desc, "%d %s%s", n, zEType, zPlural); } 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_XMERGE | TIMELINE_FILLGAPS; } if( renameOnly ){ blob_appendf(&desc, " that contain filename changes"); tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; } if( forkOnly ){ blob_appendf(&desc, " associated with forks"); tmFlags |= TIMELINE_DISJOINT; } if( bisectLocal || zBisect!=0 ){ blob_appendf(&desc, " in a bisect"); tmFlags |= TIMELINE_DISJOINT; } if( cpOnly && showCherrypicks ){ blob_appendf(&desc, " that participate in a cherrypick merge"); tmFlags |= TIMELINE_CHPICK|TIMELINE_DISJOINT; } if( zUser ){ blob_appendf(&desc, " by user %h", zUser); tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; } if( zTagSql ){ if( matchStyle==MS_EXACT || matchStyle==MS_BRLIST ){ if( related ){ blob_appendf(&desc, " related to %h", zMatchDesc); }else{ blob_appendf(&desc, " tagged with %h", zMatchDesc); } }else{ if( related ){ blob_appendf(&desc, " related to tags matching %h", zMatchDesc); }else{ blob_appendf(&desc, " with tags matching %h", zMatchDesc); } } if( zMark ){ blob_appendf(&desc," plus check-in \"%h\"", zMark); } tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; } addFileGlobDescription(zChng, &desc); if( rAfter>0.0 ){ if( rBefore>0.0 ){ blob_appendf(&desc, " occurring between %h and %h.<br>", zAfter, zBefore); }else{ blob_appendf(&desc, " occurring on or after %h.<br>", zAfter); } }else if( rBefore>0.0 ){ blob_appendf(&desc, " occurring on or before %h.<br>", zBefore); }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 ){ static const char *const azMatchStyles[] = { "exact", "Exact", "glob", "Glob", "like", "Like", "regexp", "Regexp", "brlist", "List" }; double rDate; zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); } if( zDate ){ rDate = symbolic_name_to_mtime(zDate, 0); if( db_int(0, "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", rDate-ONE_SECOND, blob_sql_text(&cond)) ){ zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0)); zOlderButtonLabel = "More"; } free(zDate); } zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); } if( zDate ){ rDate = symbolic_name_to_mtime(zDate, 0); if( db_int(0, "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", rDate+ONE_SECOND, blob_sql_text(&cond)) ){ zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0)); zNewerButtonLabel = "More"; } free(zDate); } if( advancedMenu ){ if( zType[0]=='a' || zType[0]=='c' ){ style_submenu_checkbox("unhide", "Unhide", 0, 0); } style_submenu_checkbox("v", "Files",(zType[0]!='a' && zType[0]!='c'),0); } style_submenu_entry("n","Max:",4,0); timeline_y_submenu(disableY); if( advancedMenu ){ style_submenu_entry("t", "Tag Filter:", -8, 0); style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0); } } blob_zero(&cond); } if( PB("showsql") ){ @ <pre>%h(blob_sql_text(&sql))</pre> } if( search_restrict(SRCH_CKIN)!=0 ){ style_submenu_element("Search", "%R/search?y=c"); } if( advancedMenu ){ style_submenu_element("Basic", "%s", url_render(&url, "advm", "0", "udc", "1")); }else{ style_submenu_element("Advanced", "%s", url_render(&url, "advm", "1", "udc", "1")); } if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID; if( useDividers && zMark && zMark[0] ){ double r = symbolic_name_to_mtime(zMark, 0); if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r); } blob_zero(&sql); if( PB("oldestfirst") ){ db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/"); }else{ db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); } if( fossil_islower(desc.aData[0]) ){ desc.aData[0] = fossil_toupper(desc.aData[0]); } if( zBrName ){ if( !PB("nowiki") && wiki_render_associated("branch", zBrName, WIKIASSOC_ALL) ){ @ <div class="section">%b(&desc)</div> } else{ @ <h2>%b(&desc)</h2> } style_submenu_element("Diff", "%R/vdiff?branch=%T", zBrName); }else if( zTagName && matchStyle==MS_EXACT && zBrName==0 && !PB("nowiki") && wiki_render_associated("tag", zTagName, WIKIASSOC_ALL) ){ @ <div class="section">%b(&desc)</div> } else{ @ <h2>%b(&desc)</h2> } blob_reset(&desc); /* Report any errors. */ if( zError ){ @ <p class="generalError">%h(zError)</p> } if( zNewerButton ){ @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\ @ ↑</a> } cgi_check_for_malice(); www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName, selectedRid, secondaryRid, 0); db_finalize(&q); if( zOlderButton ){ @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\ @ ↓</a> } document_emit_js(/*handles pikchrs rendered above*/); style_finish_page(); } /* ** Translate a timeline entry into the printable format by ** converting every %-substitutions as follows: ** ** %n newline ** %% a raw % ** %H commit hash ** %h abbreviated commit hash ** %a author name ** %d date ** %c comment (\n, \t replaced by space, \r deleted) ** %b branch ** %t tags ** %p phase (zero or more of: *CURRENT*, *MERGE*, *FORK*, ** *UNPUBLISHED*, *LEAF*, *BRANCH*) ** ** The returned string is obtained from fossil_malloc() and should ** be freed by the caller. */ static char *timeline_entry_subst( const char *zFormat, int *nLine, const char *zId, const char *zDate, const char *zUser, const char *zCom, const char *zBranch, const char *zTags, const char *zPhase ){ Blob r, co; int i, j; blob_init(&r, 0, 0); blob_init(&co, 0, 0); if( 0==zCom ){ zCom = "(NULL)"; } /* Replace LF and tab with space, delete CR */ while( zCom[0] ){ for(j=0; zCom[j] && zCom[j]!='\r' && zCom[j]!='\n' && zCom[j]!='\t'; j++){} blob_append(&co, zCom, j); if( zCom[j]==0 ) break; if( zCom[j]!='\r') blob_append(&co, " ", 1); zCom += j+1; } blob_str(&co); *nLine = 1; while( zFormat[0] ){ for(i=0; zFormat[i] && zFormat[i]!='%'; i++){} blob_append(&r, zFormat, i); if( zFormat[i]==0 ) break; if( zFormat[i+1]=='%' ){ blob_append(&r, "%", 1); zFormat += i+2; }else if( zFormat[i+1]=='n' ){ blob_append(&r, "\n", 1); *nLine += 1; zFormat += i+2; }else if( zFormat[i+1]=='H' ){ blob_append(&r, zId, -1); zFormat += i+2; }else if( zFormat[i+1]=='h' ){ char *zFree = 0; zFree = mprintf("%S", zId); blob_append(&r, zFree, -1); fossil_free(zFree); zFormat += i+2; }else if( zFormat[i+1]=='d' ){ blob_append(&r, zDate, -1); zFormat += i+2; }else if( zFormat[i+1]=='a' ){ blob_append(&r, zUser, -1); zFormat += i+2; }else if( zFormat[i+1]=='c' ){ blob_append(&r, co.aData, -1); zFormat += i+2; }else if( zFormat[i+1]=='b' ){ if( zBranch ) blob_append(&r, zBranch, -1); zFormat += i+2; }else if( zFormat[i+1]=='t' ){ blob_append(&r, zTags, -1); zFormat += i+2; }else if( zFormat[i+1]=='p' ){ blob_append(&r, zPhase, -1); zFormat += i+2; }else{ blob_append(&r, zFormat+i, 1); zFormat += i+1; } } fossil_free(co.aData); blob_str(&r); return r.aData; } /* ** 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, user, and tags ** 4. Number of non-merge children ** 5. Number of parents ** 6. mtime ** 7. branch ** 8. event-type: 'ci', 'w', 't', 'f', and so forth. ** 9. comment ** 10. user ** 11. tags */ void print_timeline(Stmt *q, int nLimit, int width, const char *zFormat, 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; /* True: separate entries with a newline after file listing */ int bVerboseNL = (zFormat && (fossil_strcmp(zFormat, TIMELINE_FMT_ONELINE)!=0)); /* True: separate entries with a newline even with no file listing */ int bNoVerboseNL = (zFormat && (fossil_strcmp(zFormat, TIMELINE_FMT_MEDIUM)==0 || fossil_strcmp(zFormat, TIMELINE_FMT_FULL)==0)); 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); const char *zBranch = db_column_text(q, 7); const char *zType = db_column_text(q, 8); const char *zComShort = db_column_text(q, 9); const char *zUserShort = db_column_text(q, 10); const char *zTags = db_column_text(q, 11); 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( zFormat == 0 && fossil_strnicmp(zDate, zPrevDate, 10) ){ fossil_print("=== %.10s ===\n", zDate); memcpy(zPrevDate, zDate, 10); nLine++; /* record another line */ } if( zCom==0 ) zCom = ""; if( zFormat == 0 ) 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); } if( zType && zType[0]=='w' && (zCom[0]=='+' || zCom[0]=='-' || zCom[0]==':') ){ /* Special processing for Wiki comments */ if(!zComShort || !*zComShort){ /* Shouldn't be possible, but just in case... */ zComShort = " "; } if( zCom[0]=='+' ){ zFree = mprintf("[%S] Add wiki page \"%s\" (user: %s)", zId, zComShort+1, zUserShort); }else if( zCom[0]=='-' ){ zFree = mprintf("[%S] Delete wiki page \"%s\" (user: %s)", zId, zComShort+1, zUserShort); }else{ zFree = mprintf("[%S] Edit to wiki page \"%s\" (user: %s)", zId, zComShort+1, zUserShort); } }else{ zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom); } if( zFormat ){ char *zEntry; int nEntryLine = 0; if( nChild==0 ){ sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*LEAF* "); } zEntry = timeline_entry_subst(zFormat, &nEntryLine, zId, zDate, zUserShort, zComShort, zBranch, zTags, zPrefix); nLine += nEntryLine; fossil_print("%s\n", zEntry); fossil_free(zEntry); } else{ /* record another X lines */ nLine += comment_print(zFree, zCom, 9, width, get_comment_format()); } 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," " (SELECT uuid FROM blob WHERE rid=fid)," " (SELECT uuid FROM blob WHERE rid=pid)" " FROM mlink" " WHERE mid=:mid AND pid!=fid 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 isDel = db_column_int(&fchngQuery, 1); 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); if( bVerboseNL ) fossil_print("\n"); }else{ if( bNoVerboseNL ) fossil_print("\n"); } 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,toLocal()) 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, @ event.type @ , coalesce(ecomment,comment) AS comment0 @ , coalesce(euser,user,'?') AS user0 @ , (SELECT case when length(x)>0 then 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 tags @ 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 zBaseSql; } /* ** 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]); } /* ** Return true if the input string can be converted to a julianday. */ static int fossil_is_julianday(const char *zDate){ return db_int(0, "SELECT EXISTS (SELECT julianday(%Q) AS jd" " WHERE jd IS NOT NULL)", zDate); } /* ** 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. You ** can also say "current" for the current version. ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. ** ** ** Options: ** -b|--branch BRANCH Show only items on the branch named BRANCH ** -c|--current-branch Show only items on the current branch ** -F|--format Entry format. Values "oneline", "medium", and "full" ** get mapped to the full options below. Otherwise a ** string which can contain these placeholders: ** %n newline ** %% a raw % ** %H commit hash ** %h abbreviated commit hash ** %a author name ** %d date ** %c comment (NL, TAB replaced by space, LF erased) ** %b branch ** %t tags ** %p phase: zero or more of *CURRENT*, *MERGE*, ** *FORK*, *UNPUBLISHED*, *LEAF*, *BRANCH* ** --oneline Show only short hash and comment for each entry ** --medium Medium-verbose entry formatting ** --full Extra verbose entry formatting ** -n|--limit N If N is positive, output the first N entries. If ** N is negative, output the first -N lines. If N is ** zero, no limit. Default is -20 meaning 20 lines. ** --offset P Skip P changes ** -p|--path PATH Output items affecting PATH only. ** PATH can be a file or a sub directory. ** -R REPO_FILE Specifies the repository db to use. Default is ** the current check-out's repository. ** --sql Show the SQL used to generate the timeline ** -t|--type TYPE Output items from the given types only, such as: ** ci = file commits only ** e = technical notes only ** f = forum posts 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 N Width of lines (default is to auto-detect). N must be ** either greater than 20 or it must be zero 0 to ** indicate no limit, resulting in a single line per ** entry. */ 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 = TIMELINE_MODE_NONE; int verboseFlag = 0 ; int iOffset; const char *zFilePattern = 0; const char *zFormat = 0; const char *zBr = 0; Blob treeName; int showSql = 0; 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); zFormat = find_option("format","F",1); zBr = find_option("branch","b",1); if( find_option("current-branch","c",0)!=0 ){ if( !g.localOpen ){ fossil_fatal("not within an open check-out"); }else{ int vid = db_lget_int("checkout", 0); zBr = db_text(0, "SELECT value FROM tagxref WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH); } } if( find_option("oneline",0,0)!= 0 || fossil_strcmp(zFormat,"oneline")==0 ){ zFormat = TIMELINE_FMT_ONELINE; } if( find_option("medium",0,0)!= 0 || fossil_strcmp(zFormat,"medium")==0 ){ zFormat = TIMELINE_FMT_MEDIUM; } if( find_option("full",0,0)!= 0 || fossil_strcmp(zFormat,"full")==0 ){ zFormat = TIMELINE_FMT_FULL; } showSql = find_option("sql",0,0)!=0; 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 = TIMELINE_MODE_BEFORE; }else if( strncmp(g.argv[2],"after",k)==0 && k>1 ){ mode = TIMELINE_MODE_AFTER; }else if( strncmp(g.argv[2],"descendants",k)==0 ){ mode = TIMELINE_MODE_CHILDREN; }else if( strncmp(g.argv[2],"children",k)==0 ){ mode = TIMELINE_MODE_CHILDREN; }else if( strncmp(g.argv[2],"ancestors",k)==0 && k>1 ){ mode = TIMELINE_MODE_PARENTS; }else if( strncmp(g.argv[2],"parents",k)==0 ){ mode = TIMELINE_MODE_PARENTS; }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 ){ zOrigin = g.argv[2]; }else{ zOrigin = "now"; } k = strlen(zOrigin); blob_zero(&uuid); blob_append(&uuid, zOrigin, -1); if( fossil_strcmp(zOrigin, "now")==0 ){ if( mode==TIMELINE_MODE_CHILDREN || mode==TIMELINE_MODE_PARENTS ){ fossil_fatal("cannot compute descendants or ancestors of a date"); } zDate = mprintf("(SELECT datetime('now'))"); }else if( strncmp(zOrigin, "current", k)==0 ){ if( !g.localOpen ){ fossil_fatal("must be within a local check-out to use 'current'"); } objid = db_lget_int("checkout",0); zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid); }else if( fossil_is_julianday(zOrigin) ){ const char *zShift = ""; if( mode==TIMELINE_MODE_CHILDREN || mode==TIMELINE_MODE_PARENTS ){ fossil_fatal("cannot compute descendants or ancestors of a date"); } if( mode==TIMELINE_MODE_NONE ){ if( isIsoDate(zOrigin) ) zShift = ",'+1 day'"; } zDate = mprintf("(SELECT julianday(%Q%s, fromLocal()))", zOrigin, zShift); }else if( name_to_uuid(&uuid, 0, "*")==0 ){ objid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid); zDate = mprintf("(SELECT mtime FROM event WHERE objid=%d)", objid); }else{ fossil_fatal("unknown check-in or invalid date: %s", zOrigin); } 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, 0, 1); if( fossil_strcmp(blob_str(&treeName), ".")==0 ){ /* When zTreeName refers to g.zLocalRoot, it's like not specifying * zFilePattern. */ zFilePattern = 0; } } if( mode==TIMELINE_MODE_NONE ) mode = TIMELINE_MODE_BEFORE; blob_zero(&sql); blob_append(&sql, timeline_query_for_tty(), -1); blob_append_sql(&sql, "\n AND event.mtime %s %s", ( mode==TIMELINE_MODE_BEFORE || mode==TIMELINE_MODE_PARENTS ) ? "<=" : ">=", zDate /*safe-for-%s*/ ); /* When zFilePattern is specified, compute complete ancestry; * limit later at print_timeline() */ if( mode==TIMELINE_MODE_CHILDREN || mode==TIMELINE_MODE_PARENTS ){ db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); if( mode==TIMELINE_MODE_CHILDREN ){ compute_descendants(objid, (zFilePattern ? 0 : n)); }else{ compute_ancestors(objid, (zFilePattern ? 0 : n), 0, 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); } if( zBr ){ blob_append_sql(&sql, "\n AND blob.rid IN (\n" /* Commits */ " SELECT rid FROM tagxref NATURAL JOIN tag\n" " WHERE tagtype>0 AND tagname='sym-%q'\n" " UNION\n" /* Tags */ " SELECT srcid FROM tagxref WHERE origid IN (\n" " SELECT rid FROM tagxref NATURAL JOIN tag\n" " WHERE tagname='sym-%q')\n" " UNION\n" /* Branch wikis */ " SELECT objid FROM event WHERE comment LIKE '_branch/%q'\n" " UNION\n" /* Check-in wikis */ " SELECT e.objid FROM event e\n" " INNER JOIN blob b ON b.uuid=substr(e.comment, 10)\n" " AND e.comment LIKE '_checkin/%%'\n" " LEFT JOIN tagxref tx ON tx.rid=b.rid AND tx.tagid=%d\n" " WHERE tx.value='%q'\n" ")\n" /* No merge closures */ " AND (tagxref.value IS NULL OR tagxref.value='%q')", zBr, zBr, zBr, TAG_BRANCH, zBr, zBr); } 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); } if( showSql ){ fossil_print("%s\n", blob_str(&sql)); } db_prepare_blob(&q, &sql); blob_reset(&sql); print_timeline(&q, n, width, zFormat, verboseFlag); db_finalize(&q); } /* ** WEBPAGE: thisdayinhistory ** ** Generate a vanity page that shows project activity for the current ** day of the year for various years in the history of the project. ** ** Query parameters: ** ** today=DATE Use DATE as today's date */ void thisdayinhistory_page(void){ static int aYearsAgo[] = { 1, 2, 3, 4, 5, 10, 15, 20, 30, 40, 50, 75, 100 }; const char *zToday; char *zStartOfProject; int i; Stmt q; char *z; login_check_credentials(); if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) ){ login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); return; } style_set_current_feature("timeline"); style_header("Today In History"); zToday = (char*)P("today"); if( zToday ){ zToday = timeline_expand_datetime(zToday); if( !fossil_isdate(zToday) ) zToday = 0; } if( zToday==0 ){ zToday = db_text(0, "SELECT date('now',toLocal())"); } @ <h1>This Day In History For %h(zToday)</h1> z = db_text(0, "SELECT date(%Q,'-1 day')", zToday); style_submenu_element("Yesterday", "%R/thisdayinhistory?today=%t", z); z = db_text(0, "SELECT date(%Q,'+1 day')", zToday); style_submenu_element("Tomorrow", "%R/thisdayinhistory?today=%t", z); zStartOfProject = db_text(0, "SELECT datetime(min(mtime),toLocal(),'startofday') FROM event;" ); timeline_temp_table(); db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); for(i=0; i<(int)(sizeof(aYearsAgo)/sizeof(aYearsAgo[0])); i++){ int iAgo = aYearsAgo[i]; char *zThis = db_text(0, "SELECT date(%Q,'-%d years')", zToday, iAgo); Blob sql; char *zId; if( strcmp(zThis, zStartOfProject)<0 ) break; blob_init(&sql, 0, 0); blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1); blob_append(&sql, timeline_query_for_www(), -1); blob_append_sql(&sql, " AND %Q=date(event.mtime,toLocal()) " " AND event.mtime BETWEEN julianday(%Q,'-1 day')" " AND julianday(%Q,'+2 days')", zThis, zThis, zThis ); db_multi_exec("DELETE FROM timeline; %s;", blob_sql_text(&sql)); blob_reset(&sql); if( db_int(0, "SELECT count(*) FROM timeline")==0 ){ continue; } zId = db_text(0, "SELECT timestamp FROM timeline" " ORDER BY sortby DESC LIMIT 1"); @ <h2>%d(iAgo) Year%s(iAgo>1?"s":"") Ago @ <small>%z(href("%R/timeline?c=%t",zId))(more context)</a>\ @ </small></h2> www_print_timeline(&q, TIMELINE_GRAPH, 0, 0, 0, 0, 0, 0); } db_finalize(&q); style_finish_page(); } /* ** 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; if( !verboseFlag ){ verboseFlag = find_option("detail", 0, 0)!=0; /* deprecated */ } db_prepare(&q, "SELECT (SELECT uuid FROM blob WHERE rid=p.cid)," " (SELECT uuid FROM blob WHERE rid=c.cid)," " datetime(p.mtime), datetime(c.mtime)" " FROM plink p, plink c" " WHERE p.cid=c.pid AND p.mtime>c.mtime" ); while( db_step(&q)==SQLITE_ROW ){ if( !verboseFlag ){ fossil_print("%s\n", db_column_text(&q, 1)); }else{ fossil_print("%.14s -> %.14s %s -> %s\n", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 2), db_column_text(&q, 3)); } } db_finalize(&q); } /* ** WEBPAGE: timewarps ** ** Show all check-ins that are "timewarps". A timewarp is a ** check-in that occurs before its parent, according to the ** timestamp information on the check-in. This can only actually ** happen, of course, if a users system clock is set incorrectly. */ void test_timewarp_page(void){ Stmt q; int cnt = 0; login_check_credentials(); if( !g.perm.Read || !g.perm.Hyperlink ){ login_needed(g.anon.Read && g.anon.Hyperlink); return; } style_header("Instances of timewarp"); db_prepare(&q, "SELECT blob.uuid, " " date(ce.mtime)," " pe.mtime>ce.mtime," " coalesce(ce.euser,ce.user)" " FROM plink p, plink c, blob, event pe, event ce" " WHERE p.cid=c.pid AND p.mtime>c.mtime" " AND blob.rid=c.cid" " AND pe.objid=p.cid" " AND ce.objid=c.cid" " ORDER BY 2 DESC" ); while( db_step(&q)==SQLITE_ROW ){ const char *zCkin = db_column_text(&q, 0); const char *zDate = db_column_text(&q, 1); const char *zStatus = db_column_int(&q,2) ? "Open" : "Resolved by editing date"; const char *zUser = db_column_text(&q, 3); char *zHref = href("%R/timeline?c=%S", zCkin); if( cnt==0 ){ style_table_sorter(); @ <div class="brlist"> @ <table class='sortable' data-column-types='tttt' data-init-sort='2'> @ <thead><tr> @ <th>Check-in</th> @ <th>Date</th> @ <th>User</th> @ <th>Status</th> @ </tr></thead><tbody> } @ <tr> @ <td>%s(zHref)%S(zCkin)</a></td> @ <td>%s(zHref)%s(zDate)</a></td> @ <td>%h(zUser)</td> @ <td>%s(zStatus)</td> @ </tr> fossil_free(zHref); cnt++; } db_finalize(&q); if( cnt==0 ){ @ <p>No timewarps in this repository</p> }else{ @ </tbody></table></div> } style_finish_page(); } |
Changes to src/tkt.c.
︙ | ︙ | |||
21 22 23 24 25 26 27 | #include "config.h" #include "tkt.h" #include <assert.h> /* ** The list of database user-defined fields in the TICKET table. ** The real table also contains some addition fields for internal | | > | > > > > > > > > > > > > > > > > | > > | | > > > > > > > > > > > > > | | | | | > | | > > | > | > > > > > > > > > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > | > | < < | | > > > | | | < > > | < < | | | < < | | | | > < | | < < < | < < < < < | | < < | < < | | < > > > > > > > > > > | | | < > | > > > > > > > > | < | | > > > | | | < | | | | > > | > > > | | | | | > > > | | > | > | > > > > > > | | | > > > > | | > > | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | | > | > > > > > > | > | > | > > | < > | | | > > > > > > > > > > > > > | | > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > | | | < | | | < < | < | > | | < | | < | > > > > > > > > > > > > > > > > > > > | > > > | > | | < < < < < < < < < < < < < | < | < < < < < < | < < < < < < < | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > | > > > > > > > | < > > > > > > > > > | < < < | | | > | > > | | | > | | > | > | | | < | | | > > > > > > > > > > > > > > > | | | | < < < < < | < > > > | | > | > > | > < | | > > > > > > > > > > > > > > > > > > | | | > | | | > > > | > | | | | | | > | | > | < | | | > | | | 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 | #include "config.h" #include "tkt.h" #include <assert.h> /* ** The list of database user-defined fields in the TICKET table. ** The real table also contains some addition fields for internal ** use. The internal-use fields begin with "tkt_". */ static int nField = 0; static struct tktFieldInfo { char *zName; /* Name of the database field */ char *zValue; /* Value to store */ char *zAppend; /* Value to append */ char *zBsln; /* "baseline for $zName" if that field exists*/ unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ } *aField; #define USEDBY_TICKET 01 #define USEDBY_TICKETCHNG 02 #define USEDBY_BOTH 03 #define JCARD_ASSIGN ('=') #define JCARD_APPEND ('+') #define JCARD_PRIVATE ('p') static u8 haveTicket = 0; /* True if the TICKET table exists */ static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */ static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */ static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */ static u8 haveTicketChngUser = 0;/* True if TICKETCHNG.TKT_USER exists */ static u8 useTicketGenMt = 0; /* use generated TICKET.MIMETYPE */ static u8 useTicketChngGenMt = 0;/* use generated TICKETCHNG.MIMETYPE */ static int nTicketBslns = 0; /* number of valid "baseline for ..." */ /* ** Compare two entries in aField[] for sorting purposes */ static int nameCmpr(const void *a, const void *b){ return fossil_strcmp(((const struct tktFieldInfo*)a)->zName, ((const struct tktFieldInfo*)b)->zName); } /* ** Return the index into aField[] of the given field name. ** Return -1 if zFieldName is not in aField[]. */ static int fieldId(const char *zFieldName){ int i; 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; int i, noRegularMimetype, nBaselines; static int once = 0; if( once ) return; once = 1; nBaselines = 0; db_prepare(&q, "PRAGMA table_info(ticket)"); while( db_step(&q)==SQLITE_ROW ){ const char *zFieldName = db_column_text(&q, 1); haveTicket = 1; if( memcmp(zFieldName,"tkt_",4)==0 ){ if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1; continue; } if( memcmp(zFieldName,"baseline for ",13)==0 ){ if( strcmp(db_column_text(&q,2),"INTEGER")==0 ){ nBaselines++; } continue; } if( strchr(zFieldName,' ')!=0 ) continue; if( nField%10==0 ){ aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); } aField[nField].zBsln = 0; aField[nField].zName = mprintf("%s", zFieldName); aField[nField].mUsed = USEDBY_TICKET; nField++; } db_finalize(&q); if( nBaselines ){ db_prepare(&q, "SELECT 1 FROM pragma_table_info('ticket') " "WHERE type = 'INTEGER' AND name = :n"); for(i=0; i<nField && nBaselines!=0; i++){ char *zBsln = mprintf("baseline for %s",aField[i].zName); db_bind_text(&q, ":n", zBsln); if( db_step(&q)==SQLITE_ROW ){ aField[i].zBsln = zBsln; nTicketBslns++; nBaselines--; }else{ free(zBsln); } db_reset(&q); } db_finalize(&q); } db_prepare(&q, "PRAGMA table_info(ticketchng)"); while( db_step(&q)==SQLITE_ROW ){ const char *zFieldName = db_column_text(&q, 1); haveTicketChng = 1; if( memcmp(zFieldName,"tkt_",4)==0 ){ if( strcmp(zFieldName+4,"rid")==0 ){ haveTicketChngRid = 1; /* tkt_rid */ }else if( strcmp(zFieldName+4,"user")==0 ){ haveTicketChngUser = 1; /* tkt_user */ } continue; } if( strchr(zFieldName,' ')!=0 ) continue; if( (i = fieldId(zFieldName))>=0 ){ aField[i].mUsed |= USEDBY_TICKETCHNG; continue; } if( nField%10==0 ){ aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); } aField[nField].zBsln = 0; aField[nField].zName = mprintf("%s", zFieldName); aField[nField].mUsed = USEDBY_TICKETCHNG; nField++; } db_finalize(&q); qsort(aField, nField, sizeof(aField[0]), nameCmpr); noRegularMimetype = 1; for(i=0; i<nField; i++){ aField[i].zValue = ""; aField[i].zAppend = 0; if( strcmp(aField[i].zName,"mimetype")==0 ){ noRegularMimetype = 0; } } if( noRegularMimetype ){ /* check for generated "mimetype" columns */ useTicketGenMt = db_exists( "SELECT 1 FROM pragma_table_xinfo('ticket') " "WHERE name = 'mimetype'"); useTicketChngGenMt = db_exists( "SELECT 1 FROM pragma_table_xinfo('ticketchng') " "WHERE name = 'mimetype'"); } } /* ** Query the database for all TICKET fields for the specific ** ticket whose name is given by the "name" CGI parameter. ** Load the values for all fields into the interpreter. ** ** Only load those fields which do not already exist as ** variables. ** ** Fields of the TICKET table that begin with "private_" are ** expanded using the db_reveal() function. If g.perm.RdAddr is ** true, then the db_reveal() function will decode the content ** using the CONCEALED table so that the content legible. ** Otherwise, db_reveal() is a no-op and the content remains ** obscured. */ 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,toLocal()) AS tkt_datetime, *" " FROM ticket WHERE tkt_uuid GLOB '%q*'", 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 ){ zVal = ""; }else if( strncmp(zName, "private_", 8)==0 ){ zVal = zRevealed = db_reveal(zVal); } if( (j = fieldId(zName))>=0 ){ aField[j].zValue = mprintf("%s", zVal); }else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){ Th_Store(zName, zVal); } free(zRevealed); } } db_finalize(&q); for(i=0; i<nField; i++){ if( Th_Fetch(aField[i].zName, &size)==0 ){ Th_Store(aField[i].zName, aField[i].zValue); } } } /* ** Transfer all CGI parameters to variables in the interpreter. */ static void initializeVariablesFromCGI(void){ int i; const char *z; for(i=0; (z = cgi_parameter_name(i))!=0; i++){ Th_Store(z, P(z)); } } /* ** Information about a single J-card */ struct jCardInfo { char *zValue; int mimetype; int rid; double mtime; }; /* ** Update an entry of the TICKET and TICKETCHNG tables according to the ** information in the ticket artifact given in p. Attempt to create ** the appropriate TICKET table entry if tktid is zero. If tktid is nonzero ** then it will be the ROWID of an existing TICKET entry. ** ** Parameter rid is the recordID for the ticket artifact in the BLOB table. ** Upon assignment of a field this rid is stored into a corresponding ** zBsln integer column (provided that it is defined within TICKET table). ** ** If a field is USEDBY_TICKETCHNG table then back-references within it ** are extracted and inserted into the BACKLINK table; otherwise ** a corresponding blob in the `fields` array is updated so that the ** caller could extract backlinks from the most recent field's values. ** ** Return the new rowid of the TICKET table entry. */ static int ticket_insert(const Manifest *p, const int rid, int tktid, Blob *fields){ Blob sql1; /* update or replace TICKET ... */ Blob sql2; /* list of TICKETCHNG's fields that are in the manifest */ Blob sql3; /* list of values which correspond to the previous list */ Stmt q; int i, j; char *aUsed; int mimetype_tkt = MT_NONE, mimetype_tktchng = MT_NONE; if( tktid==0 ){ 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_zero( nField ); for(i=0; i<p->nField; i++){ const char * const zName = p->aField[i].zName; const char * const 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]=='+' ){ blob_append_sql(&sql1,", \"%w\"=coalesce(\"%w\",'') || %Q", zBaseName, zBaseName, p->aField[i].zValue); /* when appending keep "baseline for ..." unchanged */ }else{ blob_append_sql(&sql1,", \"%w\"=%Q", zBaseName, p->aField[i].zValue); if( aField[j].zBsln ){ blob_append_sql(&sql1,", \"%w\"=%d", aField[j].zBsln, rid); } } } if( aField[j].mUsed & USEDBY_TICKETCHNG ){ blob_append_sql(&sql2, ",\"%w\"", zBaseName); blob_append_sql(&sql3, ",%Q", p->aField[i].zValue); } if( strcmp(zBaseName,"mimetype")==0 ){ const char *zMimetype = p->aField[i].zValue; /* "mimetype" is a regular column => these two flags must be 0 */ assert(!useTicketGenMt); assert(!useTicketChngGenMt); mimetype_tkt = mimetype_tktchng = parse_mimetype( zMimetype ); } } blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid); if( useTicketGenMt ){ blob_append_literal(&sql1, " RETURNING mimetype"); } db_prepare(&q, "%s", blob_sql_text(&sql1)); db_bind_double(&q, ":mtime", p->rDate); db_step(&q); if( useTicketGenMt ){ mimetype_tkt = parse_mimetype( db_column_text(&q,0) ); if( !useTicketChngGenMt ){ mimetype_tktchng = mimetype_tkt; } } db_finalize(&q); blob_reset(&sql1); if( blob_size(&sql2)>0 || haveTicketChngRid || haveTicketChngUser ){ int fromTkt = 0; if( haveTicketChngRid ){ blob_append_literal(&sql2, ",tkt_rid"); blob_append_sql(&sql3, ",%d", rid); } if( haveTicketChngUser && p->zUser ){ blob_append_literal(&sql2, ",tkt_user"); blob_append_sql(&sql3, ",%Q", p->zUser); } 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%s", blob_sql_text(&sql2), tktid, blob_sql_text(&sql3), tktid, useTicketChngGenMt ? " RETURNING mimetype" : ""); }else{ db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" "VALUES(%d,:mtime%s)%s", blob_sql_text(&sql2), tktid, blob_sql_text(&sql3), useTicketChngGenMt ? " RETURNING mimetype" : ""); } db_bind_double(&q, ":mtime", p->rDate); db_step(&q); if( useTicketChngGenMt ){ mimetype_tktchng = parse_mimetype( db_column_text(&q, 0) ); /* substitute NULL with a value generated within another table */ if( !useTicketGenMt ){ mimetype_tkt = mimetype_tktchng; }else if( mimetype_tktchng==MT_NONE ){ mimetype_tktchng = mimetype_tkt; }else if( mimetype_tkt==MT_NONE ){ mimetype_tkt = mimetype_tktchng; } } db_finalize(&q); } blob_reset(&sql2); blob_reset(&sql3); fossil_free(aUsed); if( rid>0 ){ /* extract backlinks */ 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; if( aField[j].mUsed & USEDBY_TICKETCHNG ){ backlink_extract(p->aField[i].zValue, mimetype_tktchng, rid, BKLNK_TICKET, p->rDate, /* existing backlinks must have been * already deleted by the caller */ 0 ); }else{ /* update field's data with the most recent values */ Blob *cards = fields + j; struct jCardInfo card = { fossil_strdup(p->aField[i].zValue), mimetype_tkt, rid, p->rDate }; if( blob_size(cards) && zName[0]!='+' ){ struct jCardInfo *x = (struct jCardInfo *)blob_buffer(cards); struct jCardInfo *end = x + blob_count(cards,struct jCardInfo); for(; x!=end; x++){ fossil_free( x->zValue ); } blob_truncate(cards,0); } blob_append(cards, (const char*)(&card), sizeof(card)); } } } 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, i; int createFlag = 1; Blob *fields; /* array of blobs; each blob holds array of jCardInfo */ fossil_free(zTag); getAllTicketFields(); if( haveTicket==0 ) return; tktid = db_int(0, "SELECT tkt_id FROM ticket WHERE tkt_uuid=%Q", zTktUuid); if( tktid!=0 ) 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; fields = blobarray_new( nField ); db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid IN " "(SELECT rid FROM tagxref WHERE tagid=%d)",BKLNK_TICKET, tagid); db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); pTicket = manifest_get(rid, CFTYPE_TICKET, 0); if( pTicket ){ tktid = ticket_insert(pTicket, rid, tktid, fields); manifest_ticket_event(rid, pTicket, createFlag, tagid); manifest_destroy(pTicket); } createFlag = 0; } db_finalize(&q); search_doc_touch('t', tktid, 0); /* Extract backlinks from the most recent values of TICKET fields */ for(i=0; i<nField; i++){ Blob *cards = fields + i; if( blob_size(cards) ){ struct jCardInfo *x = (struct jCardInfo *)blob_buffer(cards); struct jCardInfo *end = x + blob_count(cards,struct jCardInfo); for(; x!=end; x++){ assert( x->zValue ); backlink_extract(x->zValue,x->mimetype, x->rid,BKLNK_TICKET,x->mtime,0); fossil_free( x->zValue ); } } blob_truncate(cards,0); } blobarray_delete(fields,nField); } /* ** Create the TH1 interpreter and load the "common" code. */ void ticket_init(void){ const char *zConfig; Th_FossilInit(TH_INIT_DEFAULT); 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); } /* ** An authorizer function for the SQL used to initialize the ** schema for the ticketing system. Only allow ** ** CREATE TABLE ** CREATE INDEX ** CREATE VIEW ** DROP INDEX ** DROP VIEW ** ** And for objects in "main" or "repository" whose names ** begin with "ticket" or "fx_". Also allow ** ** INSERT ** UPDATE ** DELETE ** ** But only for tables in "main" or "repository" whose names ** begin with "ticket", "sqlite_", or "fx_". ** ** Of particular importance for security is that this routine ** disallows data changes on the "config" table, as that could ** allow a malicious server to modify settings in such a way as ** to cause a remote code execution. ** ** Use the "fossil test-db-prepare --auth-ticket SQL" command to perform ** manual testing of this authorizer. */ static int ticket_schema_auth( void *pNErr, int eCode, const char *z0, const char *z1, const char *z2, const char *z3 ){ switch( eCode ){ case SQLITE_DROP_VIEW: case SQLITE_CREATE_VIEW: case SQLITE_CREATE_TABLE: { if( sqlite3_stricmp(z2,"main")!=0 && sqlite3_stricmp(z2,"repository")!=0 ){ goto ticket_schema_error; } if( sqlite3_strnicmp(z0,"ticket",6)!=0 && sqlite3_strnicmp(z0,"fx_",3)!=0 ){ goto ticket_schema_error; } break; } case SQLITE_DROP_INDEX: case SQLITE_CREATE_INDEX: { if( sqlite3_stricmp(z2,"main")!=0 && sqlite3_stricmp(z2,"repository")!=0 ){ goto ticket_schema_error; } if( sqlite3_strnicmp(z1,"ticket",6)!=0 && sqlite3_strnicmp(z0,"fx_",3)!=0 ){ goto ticket_schema_error; } break; } case SQLITE_INSERT: case SQLITE_UPDATE: case SQLITE_DELETE: { if( sqlite3_stricmp(z2,"main")!=0 && sqlite3_stricmp(z2,"repository")!=0 ){ goto ticket_schema_error; } if( sqlite3_strnicmp(z0,"ticket",6)!=0 && sqlite3_strnicmp(z0,"sqlite_",7)!=0 && sqlite3_strnicmp(z0,"fx_",3)!=0 ){ goto ticket_schema_error; } break; } case SQLITE_SELECT: case SQLITE_FUNCTION: case SQLITE_REINDEX: case SQLITE_TRANSACTION: case SQLITE_READ: { break; } default: { goto ticket_schema_error; } } return SQLITE_OK; ticket_schema_error: if( pNErr ) *(int*)pNErr = 1; return SQLITE_DENY; } /* ** Activate the ticket schema authorizer. Must be followed by ** an eventual call to ticket_unrestrict_sql(). */ void ticket_restrict_sql(int * pNErr){ db_set_authorizer(ticket_schema_auth,(void*)pNErr,"Ticket-Schema"); } /* ** Deactivate the ticket schema authorizer. */ void ticket_unrestrict_sql(void){ db_clear_authorizer(); } /* ** Recreate the TICKET and TICKETCHNG tables. */ void ticket_create_table(int separateConnection){ char *zSql; db_multi_exec( "DROP TABLE IF EXISTS ticket;" "DROP TABLE IF EXISTS ticketchng;" ); zSql = ticket_table_schema(); ticket_restrict_sql(0); if( separateConnection ){ if( db_transaction_nesting_depth() ) db_end_transaction(0); db_init_database(g.zRepositoryName, zSql, 0); }else{ db_multi_exec("%s", zSql/*safe-for-%s*/); } ticket_unrestrict_sql(); fossil_free(zSql); } /* ** Repopulate the TICKET and TICKETCHNG tables from scratch using all ** available ticket artifacts. */ void ticket_rebuild(void){ Stmt q; ticket_create_table(1); db_begin_transaction(); db_prepare(&q,"SELECT tagname FROM tag WHERE tagname GLOB 'tkt-*'"); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); int len; zName += 4; len = strlen(zName); if( len<20 || !validate16(zName, len) ) continue; ticket_rebuild_entry(zName); } db_finalize(&q); db_end_transaction(0); } /* ** COMMAND: test-ticket-rebuild ** ** Usage: %fossil test-ticket-rebuild TICKETID|all ** ** Rebuild the TICKET and TICKETCHNG tables for the given ticket ID ** or for ALL. */ void test_ticket_rebuild(void){ db_find_and_open_repository(0, 0); if( g.argc!=3 ) usage("TICKETID|all"); if( fossil_strcmp(g.argv[2], "all")==0 ){ ticket_rebuild(); }else{ const char *zUuid; zUuid = db_text(0, "SELECT substr(tagname,5) FROM tag" " WHERE tagname GLOB 'tkt-%q*'", g.argv[2]); if( zUuid==0 ) fossil_fatal("no such ticket: %s", g.argv[2]); ticket_rebuild_entry(zUuid); } } /* ** For trouble-shooting purposes, render a dump of the aField[] table to ** the webpage currently under construction. */ static void showAllFields(void){ int i; @ <div style="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></div> } /* ** WEBPAGE: tktview ** URL: tktview/HASH ** ** View a ticket identified by the name= query parameter. ** Other query parameters: ** ** tl Show a timeline of the ticket above the status */ void tktview_page(void){ const char *zScript; char *zFullName; const char *zUuid = PD("name",""); int showTimeline = P("tl")!=0; login_check_credentials(); if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } if( g.anon.WrTkt || g.anon.ApndTkt ){ style_submenu_element("Edit", "%R/tktedit/%T", PD("name","")); } if( g.perm.Hyperlink ){ style_submenu_element("History", "%R/tkthistory/%T", zUuid); if( g.perm.Read ){ style_submenu_element("Check-ins", "%R/tkttimeline/%T?y=ci", zUuid); } } if( g.anon.NewTkt ){ style_submenu_element("New Ticket", "%R/tktnew"); } if( g.anon.ApndTkt && g.anon.Attach ){ style_submenu_element("Attach", "%R/attachadd?tkt=%T&from=%R/tktview/%t", zUuid, zUuid); } if( P("plaintext") ){ style_submenu_element("Formatted", "%R/tktview/%s", zUuid); }else{ style_submenu_element("Plaintext", "%R/tktview/%s?plaintext", zUuid); } style_set_current_feature("tkt"); style_header("View Ticket"); if( showTimeline ){ int tagid = db_int(0,"SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'", zUuid); if( tagid ){ tkt_draw_timeline(tagid, "a"); @ <hr> }else{ showTimeline = 0; } } if( !showTimeline && g.perm.Hyperlink ){ style_submenu_element("Timeline", "%R/info/%T", zUuid); } 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); safe_html_context(DOCSRC_TICKET); 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_finish_page(); } /* ** 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"); } if( g.thTrace ){ Th_Trace("append_field %#h {%#h}<br>\n", argl[1], argv[1], argl[2], argv[2]); } for(idx=0; idx<nField; idx++){ if( memcmp(aField[idx].zName, argv[1], argl[1])==0 && aField[idx].zName[argl[1]]==0 ){ break; } } if( idx>=nField ){ Th_ErrorMessage(g.interp, "no such TICKET column: ", argv[1], argl[1]); return TH_ERROR; } aField[idx].zAppend = mprintf("%.*s", argl[2], argv[2]); return TH_OK; } /* ** Write a ticket into the repository. ** Upon reassignment of fields try to delta-compress an artifact against ** all artifacts that are referenced in the corresponding zBsln fields. */ static int ticket_put( Blob *pTicket, /* The text of the ticket change record */ const char *zTktId, /* The ticket to which this change is applied */ const char *aUsed, /* Indicators for fields' modifications */ int needMod /* True if moderation is needed */ ){ int result; int rid; manifest_crosslink_begin(); rid = content_put_ex(pTicket, 0, 0, 0, needMod); if( rid==0 ){ fossil_fatal("trouble committing ticket: %s", g.zErrMsg); } if( nTicketBslns ){ int i, s, buf[8], nSrc=0, *aSrc=&(buf[0]); if( nTicketBslns > count(buf) ){ aSrc = (int*)fossil_malloc(sizeof(int)*nTicketBslns); } for(i=0; i<nField; i++){ if( aField[i].zBsln && aUsed[i]==JCARD_ASSIGN ){ s = db_int(0,"SELECT \"%w\" FROM ticket WHERE tkt_uuid = '%q'", aField[i].zBsln, zTktId ); if( s > 0 ) aSrc[nSrc++] = s; } } if( nSrc ) content_deltify(rid, aSrc, nSrc, 0); if( aSrc!=&(buf[0]) ) fossil_free( aSrc ); } if( needMod ){ moderation_table_create(); db_multi_exec( "INSERT INTO modreq(objid, tktid) VALUES(%d,%Q)", rid, zTktId ); }else{ db_add_unsent(rid); db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); } 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, *aUsed; const char *zUuid; int i; int nJ = 0, rc = TH_OK; Blob tktchng, cksum; int needMod; if( !cgi_csrf_safe(2) ){ @ <p class="generalError">Error: Invalid CSRF token.</p> return TH_OK; } if( !captcha_is_correct(0) ){ @ <p class="generalError">Error: Incorrect security code.</p> return TH_OK; } zUuid = (const char *)pUuid; blob_zero(&tktchng); zDate = date_in_standard_format("now"); blob_appendf(&tktchng, "D %s\n", zDate); free(zDate); aUsed = fossil_malloc_zero( nField ); for(i=0; i<nField; i++){ if( aField[i].zAppend ){ blob_appendf(&tktchng, "J +%s %z\n", aField[i].zName, fossilize(aField[i].zAppend, -1)); ++nJ; aUsed[i] = JCARD_APPEND; } } for(i=0; i<nField; i++){ const char *zValue; int nValue; if( aField[i].zAppend ) continue; zValue = Th_Fetch(aField[i].zName, &nValue); if( zValue ){ while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0) || memcmp(zValue, aField[i].zValue, nValue)!=0 ||(int)strlen(aField[i].zValue)!=nValue ){ if( memcmp(aField[i].zName, "private_", 8)==0 ){ zValue = db_conceal(zValue, nValue); blob_appendf(&tktchng, "J %s %s\n", aField[i].zName, zValue); aUsed[i] = JCARD_PRIVATE; }else{ blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue); aUsed[i] = JCARD_ASSIGN; } 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); goto finish; } needMod = ticket_need_moderation(0); if( g.zPath[0]=='d' ){ const char *zNeedMod = needMod ? "required" : "skipped"; /* If called from /debug_tktnew or /debug_tktedit... */ @ <div style="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> @ </div> @ <hr> }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, aUsed, needMod); rc = ticket_change(zUuid); } finish: fossil_free( aUsed ); return rc; } /* ** 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; int uid; login_check_credentials(); if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; } if( P("cancel") ){ cgi_redirect("home"); } style_set_current_feature("tkt"); 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); if( P("date_override") && g.perm.Setup ){ @ <input type="hidden" name="date_override" value="%h(P("date_override"))"> } zScript = ticket_newpage_code(); if( g.zLogin && g.zLogin[0] ){ int nEmail = 0; (void)Th_MaybeGetVar(g.interp, "private_contact", &nEmail); uid = nEmail>0 ? 0 : db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin); if( uid ){ char * zEmail = db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", uid); if( zEmail ){ Th_Store("private_contact", zEmail); fossil_free(zEmail); } } } 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("%R/tktview/%s", zNewUuid)); return; } captcha_generate(0); @ </form> if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); style_finish_page(); } /* ** WEBPAGE: tktedit ** WEBPAGE: debug_tktedit ** ** Edit a ticket. The ticket is identified by the name CGI parameter. ** /tktedit is the official page. The /debug_tktedit page does the same ** thing except that it does not save the ticket change record when you ** press submit - it instead prints the ticket change record at the top ** of the page. The /debug_tktedit page is intended to be used when ** debugging ticket configurations. */ 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/%T", zName); } style_set_current_feature("tkt"); style_header("Edit Ticket"); if( zName==0 || (nName = strlen(zName))<4 || nName>HNAME_LEN_SHA1 || !validate16(zName,nName) ){ @ <span class="tktError">Not a valid ticket id: "%h(zName)"</span> style_finish_page(); return; } nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'", zName); if( nRec==0 ){ @ <span class="tktError">No such ticket: "%h(zName)"</span> style_finish_page(); return; } if( nRec>1 ){ @ <span class="tktError">%d(nRec) tickets begin with: @ "%h(zName)"</span> style_finish_page(); return; } if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1); ticket_init(); getAllTicketFields(); initializeVariablesFromCGI(); initializeVariablesFromDb(); if( g.zPath[0]=='d' ) showAllFields(); form_begin(0, "%R/%s", g.zPath); @ <input type="hidden" name="name" value="%s(zName)"> 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("%R/tktview/%s", zName)); return; } captcha_generate(0); @ </form> if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1); style_finish_page(); } /* ** Check the ticket table schema in zSchema to see if it appears to ** be well-formed. If everything is OK, return NULL. If something is ** amiss, then return a pointer to a string (obtained from malloc) that ** describes the problem. |
︙ | ︙ | |||
605 606 607 608 609 610 611 | rc = sqlite3_exec(db, zSchema, 0, 0, &zErr); if( rc!=SQLITE_OK ){ sqlite3_close(db); return zErr; } rc = sqlite3_exec(db, "SELECT tkt_id, tkt_uuid, tkt_mtime FROM ticket", 0, 0, 0); | > > > > | | | | < | | > > | | | > > | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > | > > > > > | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > | > | | > > > > > > > > > > > > > | > > | | | < > | > | > > > > | < | > > > > > > > > > | | | | < | < | | > < | > > | < | > | | | | | | < > > > > > > > > > | > | > > > > | > > > > | > > > > > > > | | | > | | > > > > > > > > > > > > > | | > > > > > > > > > > > | > > > > > > > > > > | > | > > > > | | | | | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | rc = sqlite3_exec(db, zSchema, 0, 0, &zErr); if( rc!=SQLITE_OK ){ sqlite3_close(db); return zErr; } rc = sqlite3_exec(db, "SELECT tkt_id, tkt_uuid, tkt_mtime FROM ticket", 0, 0, 0); if( rc!=SQLITE_OK ){ zErr = mprintf("schema fails to define valid a TICKET " "table containing all required fields"); }else{ rc = sqlite3_exec(db, "SELECT tkt_id, tkt_mtime FROM ticketchng", 0,0,0); if( rc!=SQLITE_OK ){ zErr = mprintf("schema fails to define valid a TICKETCHNG " "table containing all required fields"); } } sqlite3_close(db); } return zErr; } /* ** Draw a timeline for a ticket with tag.tagid given by the tagid ** parameter. ** ** If zType[0]=='c' then only show check-ins associated with the ** ticket. For any other value of zType, show all events associated ** with the ticket. */ void tkt_draw_timeline(int tagid, const char *zType){ Stmt q; char *zFullUuid; char *zSQL; zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d", tagid); if( zType[0]=='c' ){ zSQL = mprintf( "%s AND event.objid IN " " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' " "AND srctype=0 " "AND '%s' GLOB (target||'*')) " "ORDER BY mtime DESC", timeline_query_for_www(), zFullUuid, zFullUuid ); }else{ zSQL = mprintf( "%s AND event.objid IN " " (SELECT rid FROM tagxref WHERE tagid=%d" " UNION" " SELECT CASE srctype WHEN 2 THEN" " (SELECT rid FROM tagxref WHERE tagid=backlink.srcid" " ORDER BY mtime DESC LIMIT 1)" " ELSE srcid END" " FROM backlink" " WHERE target GLOB '%.4s*'" " 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 | TIMELINE_NOTKT | TIMELINE_REFS, 0, 0, 0, 0, 0, 0); db_finalize(&q); fossil_free(zFullUuid); } /* ** WEBPAGE: tkttimeline ** URL: /tkttimeline/TICKETUUID ** ** Show the change history for a single ticket in timeline format. ** ** Query parameters: ** ** y=ci Show only check-ins associated with the ticket */ void tkttimeline_page(void){ char *zTitle; const char *zUuid; 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' ){ if( g.perm.Read ){ style_submenu_element("Check-ins", "%R/tkttimeline/%T?y=ci", zUuid); } }else{ style_submenu_element("Timeline", "%R/tkttimeline/%T", zUuid); } style_submenu_element("History", "%R/tkthistory/%s", zUuid); style_submenu_element("Status", "%R/info/%s", zUuid); if( zType[0]=='c' ){ zTitle = mprintf("Check-ins Associated With Ticket %h", zUuid); }else{ zTitle = mprintf("Timeline Of Ticket %h", zUuid); } style_set_current_feature("tkt"); 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_finish_page(); return; } tkt_draw_timeline(tagid, zType); style_finish_page(); } /* ** WEBPAGE: tkthistory ** URL: /tkthistory/TICKETUUID ** ** Show the complete change history for a single ticket. Or (to put it ** another way) show a list of artifacts associated with a single ticket. ** ** By default, the artifacts are decoded and formatted. Text fields ** are formatted as text/plain, since in the general case Fossil does ** not have knowledge of the encoding. If the "raw" query parameter ** is present, then the undecoded and unformatted text of each artifact ** is displayed. ** ** Reassignments of a field of the TICKET table that has a corresponding ** "baseline for ..." companion are rendered as unified diffs. */ void tkthistory_page(void){ Stmt q; char *zTitle; const char *zUuid; int tagid; int nChng = 0; Blob *aLastVal = 0; /* holds the last rendered value for each field */ 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", "%R/info/%s", zUuid); if( g.perm.Read ){ style_submenu_element("Check-ins", "%R/tkttimeline/%s?y=ci", zUuid); } style_submenu_element("Timeline", "%R/tkttimeline/%s", zUuid); if( P("raw")!=0 ){ style_submenu_element("Decoded", "%R/tkthistory/%s", zUuid); }else if( g.perm.Admin ){ style_submenu_element("Raw", "%R/tkthistory/%s?raw", zUuid); } style_set_current_feature("tkt"); 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_finish_page(); return; } if( P("raw")!=0 ){ @ <h2>Raw Artifacts Associated With Ticket %h(zUuid)</h2> }else{ @ <h2>Artifacts Associated With Ticket %h(zUuid)</h2> getAllTicketFields(); if( nTicketBslns ){ aLastVal = blobarray_new(nField); } } db_prepare(&q, "SELECT datetime(mtime,toLocal()), 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,toLocal()), 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", tagid, tagid ); for(nChng=0; db_step(&q)==SQLITE_ROW; nChng++){ 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 class="tkt-changes"> } if( zFile!=0 ){ const char *zSrc = db_column_text(&q, 3); const char *zUser = db_column_text(&q, 5); @ @ <li id="%S(zChngUuid)"><p><span> if( zSrc==0 || zSrc[0]==0 ){ @ Delete attachment "%h(zFile)" }else{ @ Add attachment @ "%z(href("%R/artifact/%!S",zSrc))%s(zFile)</a>" } @ [%z(href("%R/artifact/%!S",zChngUuid))%S(zChngUuid)</a>]</span> @ (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 id="%S(zChngUuid)"><p><span>Ticket change @ [%z(href("%R/artifact/%!S",zChngUuid))%S(zChngUuid)</a>]</span> @ (rid %d(rid)) by hyperlink_to_user(pTicket->zUser,zDate," on"); hyperlink_to_date(zDate, ":"); @ </p> if( P("raw")!=0 ){ Blob c; content_get(rid, &c); @ <blockquote><pre> @ %h(blob_str(&c)) @ </pre></blockquote> blob_reset(&c); }else{ ticket_output_change_artifact(pTicket, "a", nChng, aLastVal); } } manifest_destroy(pTicket); } @ </li> } db_finalize(&q); if( nChng ){ @ </ol> } style_finish_page(); if( aLastVal ) blobarray_delete(aLastVal, nField); } /* ** Return TRUE if the given BLOB contains a newline character. */ static int contains_newline(Blob *p){ const char *z = blob_str(p); while( *z ){ if( *z=='\n' ) return 1; z++; } return 0; } /* ** The pTkt object is a ticket change artifact. Output a detailed ** description of this object. ** ** If `aLastVal` is not NULL then render selected fields as unified diffs ** and update corresponding elements of that array with values from `pTkt`. */ void ticket_output_change_artifact( Manifest *pTkt, /* Parsed artifact for the ticket change */ const char *zListType, /* Which type of list */ int n, /* Which ticket change is this */ Blob *aLastVal /* Array of the latest values for the diffs */ ){ int i; if( zListType==0 ) zListType = "1"; getAllTicketFields(); @ <ol type="%s(zListType)"> for(i=0; i<pTkt->nField; i++){ const char *z = pTkt->aField[i].zName; const char *zX = z[0]=='+' ? z+1 : z; const int id = fieldId(zX); const char *zValue = pTkt->aField[i].zValue; const size_t nValue = strlen(zValue); const int bLong = nValue>50 || memchr(zValue,'\n',nValue)!=NULL; /* zValue is long enough to justify a <blockquote> */ const int bCanDiff = aLastVal && id>=0 && aField[id].zBsln; /* preliminary flag for rendering via unified diff */ int bAppend = 0; /* zValue is being appended to a TICKET's field */ int bRegular = 0; /* prev value of a TICKET's field is being superseded*/ @ <li>\ if( id<0 ){ @ Untracked field %h(zX): }else if( aField[id].mUsed==USEDBY_TICKETCHNG ){ @ %h(zX): }else if( n==0 ){ @ %h(zX) initialized to: }else if( z[0]=='+' && (aField[id].mUsed&USEDBY_TICKET)!=0 ){ @ Appended to %h(zX): bAppend = 1; }else{ if( !bCanDiff ){ @ %h(zX) changed to: \ } bRegular = 1; } if( bCanDiff ){ Blob *prev = aLastVal+id; Blob val = BLOB_INITIALIZER; if( nValue ){ blob_init(&val, zValue, nValue+1); val.nUsed--; /* makes blob_str() faster */ } if( bRegular && nValue && blob_buffer(prev) && blob_size(prev) ){ Blob d = BLOB_INITIALIZER; DiffConfig DCfg; construct_diff_flags(1, &DCfg); DCfg.diffFlags |= DIFF_HTML | DIFF_LINENO; text_diff(prev, &val, &d, &DCfg); @ %h(zX) changed as: @ %s(blob_str(&d)) @ </li> blob_reset(&d); }else{ if( bRegular ){ @ %h(zX) changed to: } if( bLong ){ @ <blockquote><pre class='verbatim'> @ %h(zValue) @ </pre></blockquote></li> }else{ @ "%h(zValue)"</li> } } if( blob_buffer(prev) && blob_size(prev) && !bAppend ){ blob_truncate(prev,0); } if( nValue ) blob_appendb(prev, &val); blob_reset(&val); }else{ if( bLong ){ @ <blockquote><pre class='verbatim'> @ %h(zValue) @ </pre></blockquote></li> }else{ @ "%h(zValue)"</li> } } } @ </ol> } /* ** COMMAND: ticket* ** ** Usage: %fossil ticket SUBCOMMAND ... ** ** Run various subcommands to control tickets ** ** > fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?OPTIONS? ** ** Options: ** -l|--limit LIMITCHAR ** -q|--quote ** -R|--repository REPO ** ** Run the ticket report, identified by the report format title ** used in the GUI. The data is written as flat file on stdout, ** using TAB as separator. The separator can be changed using ** the -l or --limit option. ** ** If TICKETFILTER is given on the commandline, the query is ** limited with a new WHERE-condition. ** example: Report lists a column # with the uuid ** TICKETFILTER may be [#]='uuuuuuuuu' ** example: Report only lists rows with status not open ** TICKETFILTER: status != 'open' ** ** If --quote is used, the tickets are encoded by quoting special ** chars (space -> \\s, tab -> \\t, newline -> \\n, cr -> \\r, ** formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\). ** Otherwise, the simplified encoding as on the show report raw page ** in the GUI is used. This has no effect in JSON mode. ** ** Instead of the report title it's possible to use the report ** number; the special report number 0 lists 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 to set the values of ** each field FIELD to VALUE. ** ** Field names as defined in the TICKET table. By default, these ** names include: type, status, subsystem, priority, severity, foundin, ** resolution, title, and comment, but other field names can be added ** or substituted in customized installations. ** ** If you use +FIELD, the VALUE is appended to the field FIELD. You ** can use more than one field/value pair on the commandline. Using ** --quote enables the special character decoding as in "ticket ** show", which allows setting multiline text or text with special ** characters. ** ** > fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? ** ** Like set, but create a new ticket with the given values. ** ** > fossil ticket history TICKETUUID ** ** Show the complete change history for the ticket ** ** Note that the values in set|add are not validated against the ** definitions given in "Ticket Common Script". */ void ticket_cmd(void){ int n; const char *zUser; const char *zDate; const char *zTktUuid; /* do some ints, we want to be inside a check-out */ 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"); } /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", zUser) ){ fossil_fatal("no such user: %s", zUser); } if( g.argc<3 ){ 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; /* read all available ticket fields */ getAllTicketFields(); for(i=0; i<nField; i++){ printf("%s\n",aField[i].zName); } }else if( !strncmp(g.argv[3],"reports",n) ){ rpt_list_reports(); }else{ fossil_fatal("unknown ticket list option '%s'!",g.argv[3]); } } }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; zSep = find_option("limit","l",1); zRep = g.argv[3]; if( !strcmp(zRep,"0") ){ zRep = 0; } if( g.argc>4 ){ zFilterUuid = g.argv[4]; } rptshow( zRep, zSep, zFilterUuid, tktEncoding ); } }else{ /* add a new ticket or update an existing ticket */ enum { set,add,history,err } eCmd = err; int i = 0; Blob tktchng, cksum; char *aUsed; /* get command type (set/add) and get uuid, if needed for set */ if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 || strncmp(g.argv[2],"history",n)==0 ){ if( strncmp(g.argv[2],"history",n)==0 ){ 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; i = 3; if( zTktUuid==0 ){ zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); } } /* none of set/add, so show the usage! */ if( eCmd==err ){ usage("add|fieldlist|set|show|history"); } /* 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,toLocal()), 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,toLocal()), 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", tagid, 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,get_comment_format()); }else{ fossil_print("%s\n",blob_str(&val)); } blob_reset(&val); } } manifest_destroy(pTicket); } } db_finalize(&q); return; } /* read all given ticket field/value pairs from command line */ if( i==g.argc ){ fossil_fatal("empty %s command aborted!",g.argv[2]); } getAllTicketFields(); /* read command-line and assign fields in the aField[].zValue array */ while( i<g.argc ){ char *zFName; char *zFValue; int j; int append = 0; zFName = g.argv[i++]; if( i==g.argc ){ fossil_fatal("missing value for '%s'!",zFName); } 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; } } } aUsed = fossil_malloc_zero( nField ); /* 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; aUsed[i] = JCARD_APPEND; }else if( aField[i].zValue && aField[i].zValue[0] ){ zPfx = " "; zValue = aField[i].zValue; aUsed[i] = JCARD_ASSIGN; }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); aUsed[i] = JCARD_PRIVATE; }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, aUsed, ticket_need_moderation(1) )==0 ){ fossil_fatal("%s", g.zErrMsg); }else{ fossil_print("ticket %s succeeded for %s\n", (eCmd==set?"set":"add"),zTktUuid); } fossil_free( aUsed ); } } } #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", "%R/tktsrch"); } if( (ok & T_REPLIST)!=0 ){ style_submenu_element("Reports", "%R/reportlist"); } if( (ok & T_NEW)!=0 && g.anon.NewTkt ){ style_submenu_element("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){ char *defaultReport; login_check_credentials(); style_set_current_feature("tkt"); style_header("Ticket Search"); ticket_standard_submenu(T_ALL_BUT(T_SRCH)); if( !search_screen(SRCH_TKT, 0) ){ defaultReport = db_get("ticket-default-report", 0); if( defaultReport ){ rptview_page_content(defaultReport, 0, 0); } } style_finish_page(); } |
Changes to src/tktsetup.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | ** setup screens. */ #include "config.h" #include "tktsetup.h" #include <assert.h> /* | < > | | > > > | > | > > > > > > > > > > > > > | > | | | | | | | > > | | > | < | | | < | | | | | | | > > | | | 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 | ** setup screens. */ #include "config.h" #include "tktsetup.h" #include <assert.h> /* ** WEBPAGE: tktsetup ** Main sub-menu for configuring the ticketing system. */ 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", "How to display ticket status in the timeline"); setup_menu_entry("Common", "tktsetup_com", "Common TH1 code run before all ticket processing."); setup_menu_entry("Change", "tktsetup_change", "The TH1 code run after a ticket is edited or created."); setup_menu_entry("New Ticket Page", "tktsetup_newpage", "HTML with embedded TH1 code for the \"new ticket\" webpage."); setup_menu_entry("View Ticket Page", "tktsetup_viewpage", "HTML with embedded TH1 code for the \"view ticket\" webpage."); setup_menu_entry("Edit Ticket Page", "tktsetup_editpage", "HTML with embedded TH1 code for the \"edit ticket\" webpage."); setup_menu_entry("Report List Page", "tktsetup_reportlist", "HTML with embedded TH1 code for the \"report list\" webpage."); setup_menu_entry("Report Template", "tktsetup_rpttplt", "The default ticket report format."); setup_menu_entry("Key Template", "tktsetup_keytplt", "The default color key for reports."); @ </table> style_finish_page(); } /* ** NOTE: When changing the table definition below, also change the ** equivalent definition found in schema.c. */ /* @-comment: ** */ static const char zDefaultTicketTable[] = @ CREATE TABLE ticket( @ -- Do not change any column that begins with tkt_ @ tkt_id INTEGER PRIMARY KEY, @ tkt_uuid TEXT UNIQUE, @ tkt_mtime DATE, @ tkt_ctime DATE, @ -- Add as many fields as required below this line @ type TEXT, @ status TEXT, @ subsystem TEXT, @ priority TEXT, @ severity TEXT, @ foundin TEXT, @ private_contact TEXT, @ resolution TEXT, @ title TEXT, @ comment TEXT @ ); @ CREATE TABLE ticketchng( @ -- Do not change any column that begins with tkt_ @ tkt_id INTEGER REFERENCES ticket, @ tkt_rid INTEGER REFERENCES blob, @ tkt_mtime DATE, @ tkt_user TEXT, @ -- Add as many fields as required below this line @ login TEXT, @ username TEXT, @ mimetype TEXT, @ icomment TEXT @ ); @ CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime); ; /* ** Return the ticket table definition in heap-allocated ** memory owned by the caller. */ char *ticket_table_schema(void){ return db_get("ticket-table", zDefaultTicketTable); } /* ** Common implementation for the ticket setup editor pages. */ static void tktsetup_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; } style_set_current_feature("tktsetup"); if( PB("setup") ){ cgi_redirect("tktsetup"); } isSubmit = P("submit")!=0; z = P("x"); if( z==0 ){ z = db_get(zDbField, zDfltValue); } style_set_current_feature("tktsetup"); style_header("Edit %s", zTitle); if( P("clear")!=0 && cgi_csrf_safe(2) ){ db_unset(zDbField/*works-like:"x"*/, 0); if( xRebuild ) xRebuild(); cgi_redirect("tktsetup"); }else if( isSubmit && cgi_csrf_safe(2) ){ char *zErr = 0; if( xText && (zErr = xText(z))!=0 ){ @ <p class="tktsetupError">ERROR: %h(zErr)</p> }else{ db_set(zDbField/*works-like:"x"*/, z, 0); if( xRebuild ) xRebuild(); cgi_redirect("tktsetup"); } } @ <form action="%R/%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> @ <blockquote><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></blockquote> @ </div></form> @ <hr> @ <h2>Default %s(zTitle)</h2> @ <blockquote><pre> @ %h(zDfltValue) @ </pre></blockquote> style_finish_page(); } /* ** WEBPAGE: tktsetup_tab ** Administrative page for defining the "ticket" table used ** to hold ticket information. */ void tktsetup_tab_page(void){ static const char zDesc[] = @ Enter a valid CREATE TABLE statement for the "ticket" table. The @ table must contain columns named "tkt_id", "tkt_uuid", and "tkt_mtime" @ with an unique index on "tkt_uuid" and "tkt_mtime". ; tktsetup_generic( "Ticket Table Schema", "ticket-table", zDefaultTicketTable, zDesc, ticket_schema_check, |
︙ | ︙ | |||
219 220 221 222 223 224 225 | @ } ; /* ** Return the ticket common code. */ const char *ticket_common_code(void){ | | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | < | | | > | > | | | < | | | > | | | | | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < | | | > > | | > > | | | > | > > > > > | > > > > > > > > > | | | | | | | | | | > > | > > | | | > > > > > | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | < > > | | | | | | | | | > > | > | > | > | > | > | > | > | > | > < < < < < < < | | | > | | < < < > > | > > > > | > | > > > > | > > | | < | < | > < | < | > | > | > | | < | > | > | > | < | > > > | > > | < | | | | | > | > > > > | > > | < | | 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 | @ } ; /* ** Return the ticket common code. */ const char *ticket_common_code(void){ return db_get("ticket-common", zDefaultTicketCommon); } /* ** WEBPAGE: tktsetup_com ** Administrative page used to define TH1 script that is ** common to all ticket screens. */ void tktsetup_com_page(void){ static const char zDesc[] = @ Enter TH1 script that initializes variables prior to generating @ any of the ticket view, edit, or creation pages. ; tktsetup_generic( "Ticket Common Script", "ticket-common", zDefaultTicketCommon, zDesc, 0, 0, 30 ); } static const char zDefaultTicketChange[] = @ return ; /* ** Return the ticket change code. */ const char *ticket_change_code(void){ return db_get("ticket-change", zDefaultTicketChange); } /* ** WEBPAGE: tktsetup_change ** Administrative screen used to view or edit the TH1 script ** that shows ticket changes. */ void tktsetup_change_page(void){ static const char zDesc[] = @ Enter TH1 script that runs after processing the ticket editing @ and creation pages. ; tktsetup_generic( "Ticket Change Script", "ticket-change", zDefaultTicketChange, zDesc, 0, 0, 30 ); } static const char zDefaultNew[] = @ <th1> @ if {![info exists mutype]} {set mutype Markdown} @ if {[info exists submit]} { @ set status Open @ if {$mutype eq "HTML"} { @ set mimetype "text/html" @ } elseif {$mutype eq "Wiki"} { @ set mimetype "text/x-fossil-wiki" @ } elseif {$mutype eq "Markdown"} { @ set mimetype text/x-markdown @ } elseif {$mutype eq {[links only]}} { @ set mimetype "text/x-fossil-plain" @ } else { @ set mimetype "text/plain" @ } @ submit_ticket @ set preview 1 @ } @ </th1> @ <h1 style="text-align: center;">Enter A New Ticket</h1> @ <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 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 {HTML {[links only]} Markdown {Plain Text} Wiki} 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 "Markdown"} { @ html [lindex [markdown "$icomment\n"] 1] @ } 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> ; /* ** Return the code used to generate the new ticket page */ const char *ticket_newpage_code(void){ return db_get("ticket-newpage", zDefaultNew); } /* ** WEBPAGE: tktsetup_newpage ** Administrative page used to view or edit the TH1 script used ** to enter new tickets. */ void tktsetup_newpage_page(void){ static const char zDesc[] = @ Enter HTML with embedded TH1 script that will render the "new ticket" @ page ; tktsetup_generic( "HTML For New Tickets", "ticket-newpage", zDefaultNew, zDesc, 0, 0, 40 ); } static const char zDefaultView[] = @ <table cellpadding="5"> @ <tr><td class="tktDspLabel">Ticket Hash:</td> @ <th1> @ if {[info exists tkt_uuid]} { @ html "<td class='tktDspValue' colspan='3'>" @ copybtn hash-tk 0 $tkt_uuid 2 @ if {[hascap s]} { @ html " ($tkt_id)" @ } @ html "</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"> @ $<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="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 @ FROM ticketchng @ WHERE tkt_id=$tkt_id AND length(icomment)>0} { @ if {$seenRow} { @ html "<hr>\n" @ } else { @ html "<tr><td class='tktDspLabel'>User Comments:</td></tr>\n" @ html "<tr><td colspan='5' class='tktDspValue'>\n" @ set seenRow 1 @ } @ html "<span class='tktDspCommenter'>" @ html "[htmlize $xlogin]" @ if {$xlogin ne $xusername && [string length $xusername]>0} { @ html " (claiming to be [htmlize $xusername])" @ } @ html " added on $xdate:" @ html "</span>\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/x-markdown"} { @ html [lindex [markdown $xcomment] 1] @ } elseif {$xmimetype eq "text/html"} { @ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n" @ } else { @ set r [randhex] @ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n" @ } @ } @ if {$seenRow} {html "</td></tr>\n"} @ </th1> @ </table> ; /* ** Return the code used to generate the view ticket page */ const char *ticket_viewpage_code(void){ return db_get("ticket-viewpage", zDefaultView); } /* ** WEBPAGE: tktsetup_viewpage ** Administrative page used to view or edit the TH1 script that ** displays individual tickets. */ void tktsetup_viewpage_page(void){ static const char zDesc[] = @ Enter HTML with embedded TH1 script that will render the "view ticket" page ; tktsetup_generic( "HTML For Viewing Tickets", "ticket-viewpage", zDefaultView, zDesc, 0, 0, 40 ); } static const char zDefaultEdit[] = @ <th1> @ if {![info exists mutype]} {set mutype Markdown} @ if {![info exists icomment]} {set icomment {}} @ if {![info exists username]} {set username $login} @ if {[info exists submit]} { @ if {$mutype eq "Wiki"} { @ set mimetype text/x-fossil-wiki @ } elseif {$mutype eq "Markdown"} { @ set mimetype text/x-markdown @ } elseif {$mutype eq "HTML"} { @ set mimetype text/html @ } elseif {$mutype eq {[links only]}} { @ set mimetype text/x-fossil-plain @ } else { @ set mimetype text/plain @ } @ submit_ticket @ 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 {HTML {[links only]} Markdown {Plain Text} Wiki} 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 "Markdown"} { @ html [lindex [markdown "$icomment\n"] 1] @ } 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){ return db_get("ticket-editpage", zDefaultEdit); } /* ** WEBPAGE: tktsetup_editpage ** Administrative page for viewing or editing the TH1 script that ** drives the ticket editing page. */ void tktsetup_editpage_page(void){ static const char zDesc[] = @ Enter HTML with embedded TH1 script that will render the "edit ticket" page ; tktsetup_generic( "HTML For Editing Tickets", "ticket-editpage", zDefaultEdit, zDesc, 0, 0, 40 ); } /* ** 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> ; /* ** Return the code used to generate the report list */ const char *ticket_reportlist_code(void){ return db_get("ticket-reportlist", zDefaultReportList); } /* ** WEBPAGE: tktsetup_reportlist ** Administrative page used to view or edit the TH1 script that ** defines the "report list" page. */ void tktsetup_reportlist(void){ static const char zDesc[] = @ Enter HTML with embedded TH1 script that will render the "report list" page ; tktsetup_generic( "HTML For Report List", "ticket-reportlist", zDefaultReportList, zDesc, 0, 0, 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', |
︙ | ︙ | |||
628 629 630 631 632 633 634 635 636 637 | */ char *ticket_report_template(void){ return db_get("ticket-report-template", zDefaultReport); } /* ** WEBPAGE: tktsetup_rpttplt */ void tktsetup_rpttplt_page(void){ static const char zDesc[] = | > > > | | | | | > > > | | > > > | | > > | | > | > | > | > | | > | | | | 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 | */ char *ticket_report_template(void){ return db_get("ticket-report-template", zDefaultReport); } /* ** WEBPAGE: tktsetup_rpttplt ** ** Administrative page used to view or edit the ticket report ** template. */ void tktsetup_rpttplt_page(void){ static const char zDesc[] = @ Enter the default ticket report format template. This is the @ template report format that initially appears when creating a @ new ticket summary report. ; tktsetup_generic( "Default Report Template", "ticket-report-template", zDefaultReport, zDesc, 0, 0, 20 ); } /* ** The default template ticket key: */ static const char zDefaultKey[] = @ #ffffff Key: @ #f2dcdc Active @ #e8e8e8 Review @ #cfe8bd Fixed @ #bde5d6 Tested @ #cacae5 Deferred @ #c8c8c8 Closed ; /* ** Return the template ticket report format: */ const char *ticket_key_template(void){ return db_get("ticket-key-template", zDefaultKey); } /* ** WEBPAGE: tktsetup_keytplt ** ** Administrative page used to view or edit the Key template ** for tickets. */ void tktsetup_keytplt_page(void){ static const char zDesc[] = @ Enter the default ticket report color-key template. This is the @ the color-key that initially appears when creating a @ new ticket summary report. ; tktsetup_generic( "Default Report Color-Key Template", "ticket-key-template", zDefaultKey, zDesc, 0, 0, 10 ); } /* ** WEBPAGE: tktsetup_timeline ** ** Administrative page used ot configure how tickets are ** rendered on timeline views. */ void tktsetup_timeline_page(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } if( P("setup") ){ cgi_redirect("tktsetup"); } style_set_current_feature("tktsetup"); style_header("Ticket Display On Timelines"); db_begin_transaction(); @ <form action="%R/tktsetup_timeline" method="post"><div> login_insert_csrf_secret(); @ <hr> entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title", 0); @ <p>An SQL expression in a query against the TICKET table that will @ return the title of the ticket for display purposes. @ (Property: ticket-title-expr)</p> @ <hr> entry_attribute("Ticket Status", 40, "ticket-status-column", "s", "status", 0); @ <p>The name of the column in the TICKET table that contains the ticket @ status in human-readable form. Case sensitive. @ (Property: ticket-status-column)</p> @ <hr> entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c", "status='Closed'", 0); @ <p>An SQL expression that evaluates to true in a TICKET table query if @ the ticket is closed. @ (Property: ticket-closed-expr)</p> @ <hr> @ <p> @ <input type="submit" name="submit" value="Apply Changes"> @ <input type="submit" name="setup" value="Cancel"> @ </p> @ </div></form> db_end_transaction(0); style_finish_page(); } |
Deleted src/translate.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added src/tree.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* Javascript to implement the file hierarchy tree. */ (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(document.getElementById(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; } }()) |
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 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 | ** 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" #if INTERFACE /* ** Possible return values from the undo_maybe_save() routine. */ #define UNDO_NONE (0) /* Placeholder only used to initialize vars. */ #define UNDO_SAVED_OK (1) /* The specified file was saved succesfully. */ #define UNDO_DISABLED (2) /* File not saved, subsystem is disabled. */ #define UNDO_INACTIVE (3) /* File not saved, subsystem is not active. */ #define UNDO_TOOBIG (4) /* File not saved, it exceeded a size limit. */ #endif /* ** Undo the change to the file zPathname. zPathname is the pathname ** of the file relative to the root of the repository. If redoFlag is ** true the redo a change. If there is nothing to undo (or redo) then ** this routine is a noop. */ static void undo_one(const char *zPathname, int redoFlag){ Stmt q; char *zFullname; db_prepare(&q, "SELECT content, existsflag, isExe, isLink FROM undo" " WHERE pathname=%Q AND redoflag=%d", zPathname, redoFlag ); if( db_step(&q)==SQLITE_ROW ){ int old_exists; int new_exists; int old_exe; 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_size(zFullname, RepoFILE)>=0; new_link = file_islink(0); if( new_exists ){ blob_read_from_file(¤t, zFullname, RepoFILE); new_exe = file_isexe(0,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); if( old_exists ){ db_ephemeral_blob(&q, 0, &new); } if( file_unsafe_in_tree_path(zFullname) ){ /* do nothign with this unsafe file */ }else if( old_exists ){ if( new_exists ){ fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname); }else{ fossil_print("NEW %s\n", zPathname); } if( new_exists && (new_link || old_link) ){ file_delete(zFullname); } if( old_link ){ symlink_create(blob_str(&new), zFullname); }else{ blob_write_to_file(&new, zFullname); } file_setexe(zFullname, old_exe); }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); } db_step(&q); blob_reset(¤t); } |
︙ | ︙ | |||
117 118 119 120 121 122 123 124 125 126 | "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;" ); ncid = db_lget_int("undo_checkout", 0); ucid = db_lget_int("checkout", 0); db_lset_int("undo_checkout", ucid); | > > > > > > > > > > > > > > | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | | < > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > | < > | | | > > > > > | > | | | | | | | > > > > > > > > > > > > | > > > > > > > | | > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > | > | > > > | > > > > > > > > > > > > | | > < > | > | < < < < < < < < < < < < < < < < < < < < < < < < < < | > > > > | > > | > | | | < < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | > > > > > > | 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 | "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{ db_multi_exec( "INSERT OR IGNORE INTO stash SELECT * FROM undo_stash;" "INSERT OR IGNORE INTO stashfile SELECT * FROM undo_stashfile;" ); } } ncid = db_lget_int("undo_checkout", 0); ucid = db_lget_int("checkout", 0); db_lset_int("undo_checkout", ucid); db_set_checkout(ncid); } /* ** Reset the undo memory. */ void undo_reset(void){ 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_exec_sql(zSql); 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. */ static char *undoCmd = 0; /* ** This flag is true if we are in the process of collecting file changes ** for undo. When this flag is false, undo_save() is a no-op. ** ** The undoDisable flag, if set, prevents undo from being activated. */ static int undoActive = 0; static int undoDisable = 0; /* ** Capture the current command-line and store it as part of the undo ** state. This routine is called before options are extracted from the ** command-line so that we can record the complete command-line. */ void undo_capture_command_line(void){ Blob cmdline; int i; if( undoCmd!=0 || undoDisable ) return; blob_zero(&cmdline); for(i=1; i<g.argc; i++){ if( i>1 ) blob_append(&cmdline, " ", 1); blob_append(&cmdline, g.argv[i], -1); } undoCmd = blob_str(&cmdline); } /* ** Begin capturing a snapshot that can be undone. */ void undo_begin(void){ int cid; static const char zSql[] = @ CREATE TABLE localdb.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 localdb.undo_vfile AS SELECT * FROM vfile; @ CREATE TABLE localdb.undo_vmerge AS SELECT * FROM vmerge; ; if( undoDisable ) return; undo_reset(); db_exec_sql(zSql); 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 ** recorded in the undo log but the undo log has not yet been committed. ** ** If a fatal error occurs and this flag is set, that means we should ** rollback all the filesystem changes. */ static int undoNeedRollback = 0; /* ** Save the current content of the file zPathname so that it ** will be undoable. The name is relative to the root of the ** tree. */ void undo_save(const char *zPathname){ if( undoDisable ) return; if( undo_maybe_save(zPathname, -1)!=UNDO_SAVED_OK ){ fossil_fatal("failed to save undo information for path: %s", zPathname); } } /* ** Possibly save the current content of the file zPathname so ** that it will be undoable. The name is relative to the root ** of the tree. The limit argument may be used to specify the ** maximum size for the file to be saved. If the size of the ** specified file exceeds this size limit (in bytes), it will ** not be saved and an appropriate code will be returned. ** ** WARNING: Please do NOT call this function with a limit ** value less than zero, call the undo_save() ** function instead. ** ** The return value of this function will always be one of the ** following codes: ** ** UNDO_SAVED_OK: The specified file was saved succesfully. ** ** UNDO_DISABLED: The specified file was NOT saved, because the ** "undo subsystem" is disabled. This error may ** indicate that a call to undo_disable() was ** issued. ** ** UNDO_INACTIVE: The specified file was NOT saved, because the ** "undo subsystem" is not active. This error ** may indicate that a call to undo_begin() is ** missing. ** ** UNDO_TOOBIG: The specified file was NOT saved, because it ** exceeded the specified size limit. It is ** impossible for this value to be returned if ** the specified size limit is less than zero ** (i.e. unlimited). */ int undo_maybe_save(const char *zPathname, i64 limit){ char *zFullname; i64 size; int result; if( undoDisable ) return UNDO_DISABLED; if( !undoActive ) return UNDO_INACTIVE; zFullname = mprintf("%s%s", g.zLocalRoot, zPathname); size = file_size(zFullname, RepoFILE); if( limit<0 || size<=limit ){ int existsFlag = (size>=0); int isLink = file_islink(zFullname); Stmt q; Blob content; db_prepare(&q, "INSERT OR IGNORE INTO" " undo(pathname,redoflag,existsflag,isExe,isLink,content)" " VALUES(%Q,0,%d,%d,%d,:c)", zPathname, existsFlag, file_isexe(zFullname,RepoFILE), isLink ); if( existsFlag ){ blob_read_from_file(&content, zFullname, RepoFILE); db_bind_blob(&q, ":c", &content); } db_step(&q); db_finalize(&q); if( existsFlag ){ blob_reset(&content); } undoNeedRollback = 1; result = UNDO_SAVED_OK; }else{ result = UNDO_TOOBIG; } free(zFullname); return result; } /* ** Returns an error message for the undo_maybe_save() return code. ** Currently, this function assumes that the caller is using the ** returned error message in a context prefixed with "because". */ const char *undo_save_message(int rc){ static char zRc[32]; switch( rc ){ case UNDO_NONE: return "undo is disabled for this operation"; case UNDO_SAVED_OK: return "the save operation was successful"; case UNDO_DISABLED: return "the undo subsystem is disabled"; case UNDO_INACTIVE: return "the undo subsystem is inactive"; case UNDO_TOOBIG: return "the file is too big"; default: { sqlite3_snprintf(sizeof(zRc), zRc, "of error code %d", rc); } } return zRc; } /* ** Make the current state of stashid undoable. */ void undo_save_stash(int stashid){ db_multi_exec( "CREATE TABLE IF NOT EXISTS localdb.undo_stash" " AS SELECT * FROM stash WHERE 0;" "INSERT INTO undo_stash" " SELECT * FROM stash WHERE stashid=%d;", stashid ); db_multi_exec( "CREATE TABLE IF NOT EXISTS localdb.undo_stashfile" " AS SELECT * FROM stashfile WHERE 0;" "INSERT INTO undo_stashfile" " SELECT * FROM stashfile WHERE stashid=%d;", stashid ); } /* ** Complete the undo process is one is currently in process. */ void undo_finish(void){ if( undoActive ){ if( undoNeedRollback ){ fossil_print(" \"fossil undo\" is available to undo changes" " to the working checkout.\n"); } undoActive = 0; undoNeedRollback = 0; } } /* ** This routine is called when the process aborts due to an error. ** If an undo was being accumulated but was not finished, attempt ** to rollback all of the filesystem changes. ** ** This rollback occurs, for example, if an "update" or "merge" operation ** could not run to completion because a file that needed to be written ** was locked or had permissions turned off. */ void undo_rollback(void){ if( !undoNeedRollback ) return; assert( undoActive ); undoNeedRollback = 0; undoActive = 0; fossil_print("Rolling back prior filesystem changes...\n"); undo_all_filesystem(0); } /* ** COMMAND: undo ** COMMAND: redo* ** ** Usage: %fossil undo ?OPTIONS? ?FILENAME...? ** or: %fossil redo ?OPTIONS? ?FILENAME...? ** ** The undo command reverts the changes caused by the previous command ** if the previous command is one of the following: ** * fossil update ** * fossil merge ** * fossil revert ** * fossil stash pop ** * fossil stash apply ** * fossil stash drop ** * fossil stash goto ** * fossil clean (*see note below*) ** ** Note: The "fossil clean" command only saves state for files less than ** 10MiB in size and so if fossil clean deleted files larger than that, ** then "fossil undo" will not recover the larger files. ** ** 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. ** ** If the most recent command is not one of those listed as undoable, ** then the undo command might try to restore the state to be what it was ** prior to the last undoable command, or it might be a no-op. If in ** doubt about what the undo command will do, first run it with the -n ** option. ** ** A single level of undo/redo is supported. The undo/redo stack ** is cleared by the commit and check-out commands. Other commands may ** or may not clear the undo stack. ** ** Future versions of Fossil might add new commands to the set of commands ** that are undoable. ** ** Options: ** -n|--dry-run Do not make changes, but show what would be done ** ** See also: [[commit]], [[status]] */ void undo_cmd(void){ int isRedo = g.argv[1][0]=='r'; int undo_available; int dryRunFlag = find_option("dry-run", "n", 0)!=0; const char *zCmd = isRedo ? "redo" : "undo"; if( !dryRunFlag ){ dryRunFlag = find_option("explain", 0, 0)!=0; } db_must_be_within_tree(); verify_all_options(); db_begin_transaction(); undo_available = db_lget_int("undo_available", 0); if( dryRunFlag ){ if( undo_available==0 ){ fossil_print("No undo or redo is available\n"); }else{ Stmt q; int nChng = 0; const char *zArticle = undo_available==1 ? "An" : "A"; zCmd = undo_available==1 ? "undo" : "redo"; fossil_print("%s %s is available for the following command:\n\n" " %s %s\n\n", zArticle, zCmd, g.argv[0], db_lget("undo_cmdline", "???")); db_prepare(&q, "SELECT existsflag, pathname FROM undo ORDER BY pathname" ); 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"); } } }else{ int vid1 = db_lget_int("checkout", 0); int vid2; if( g.argc==2 ){ if( undo_available!=(1+isRedo) ){ fossil_fatal("nothing to %s", zCmd); } undo_all(isRedo); db_lset_int("undo_available", 2-isRedo); }else if( g.argc>=3 ){ int i; if( undo_available==0 ){ fossil_fatal("nothing to %s", zCmd); } for(i=2; i<g.argc; i++){ const char *zFile = g.argv[i]; Blob path; file_tree_name(zFile, &path, 0, 1); undo_one(blob_str(&path), isRedo); blob_reset(&path); } } vid2 = db_lget_int("checkout", 0); if( vid1!=vid2 ){ fossil_print("--------------------\n"); show_common_info(vid2, "updated-to:", 1, 0); } } db_end_transaction(0); } |
Added src/unicode.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 | /* ** Copyright (c) 2013 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 is copied from ext/fts5/fts5_unicode2.c of SQLite3 with ** minor changes. */ #include "config.h" #include "unicode.h" /* ** Return true if the argument corresponds to a unicode codepoint ** classified as either a letter or a number. Otherwise false. ** ** The results are undefined if the value passed to this function ** 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, 0x001FF403, 0x00205804, 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403, 0x00217801, 0x00234C31, 0x0024E803, 0x0024F812, 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001, 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802, 0x0027C802, 0x0027E802, 0x0027F402, 0x00280403, 0x0028F001, 0x0028F805, 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D402, 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03, 0x002B8802, 0x002BC002, 0x002BE806, 0x002C0403, 0x002CF001, 0x002CF807, 0x002D1C02, 0x002D2C03, 0x002D5403, 0x002D8802, 0x002DC001, 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01, 0x002FCC08, 0x00300005, 0x0030F807, 0x00311803, 0x00312804, 0x00315402, 0x00318802, 0x0031DC01, 0x0031FC01, 0x00320404, 0x0032F001, 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802, 0x00340004, 0x0034EC02, 0x0034F807, 0x00351803, 0x00352804, 0x00353C01, 0x00355C01, 0x00358802, 0x0035E401, 0x00360403, 0x00372801, 0x00373C06, 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007, 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD009, 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, 0x00621402, 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002, 0x00677822, 0x00685C05, 0x00687802, 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006, 0x006AC011, 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D, 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802, 0x00730008, 0x00734019, 0x0073B401, 0x0073D001, 0x0073DC03, 0x0077003A, 0x0077EC05, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403, 0x007FB403, 0x007FF402, 0x00800065, 0x0081980A, 0x0081E805, 0x00822805, 0x00828020, 0x00834021, 0x00840002, 0x00840C04, 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401, 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005, 0x00852804, 0x00853C01, 0x00862802, 0x00864297, 0x0091000B, 0x0092704E, 0x00940276, 0x009E53E0, 0x00ADD820, 0x00AE5C69, 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001, 0x00B5FC01, 0x00B7804F, 0x00B8C023, 0x00BA001A, 0x00BA6C59, 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807, 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01, 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E, 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB0140, 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10, 0x029A7802, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402, 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C0A, 0x02A0D804, 0x02A1D004, 0x02A20002, 0x02A2D012, 0x02A33802, 0x02A38012, 0x02A3E003, 0x02A3F001, 0x02A3FC01, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004, 0x02A6CC1B, 0x02A77802, 0x02A79401, 0x02A8A40E, 0x02A90C01, 0x02A93002, 0x02A97004, 0x02A9DC03, 0x02A9EC03, 0x02AAC001, 0x02AAC803, 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07, 0x02ABD402, 0x02AD6C01, 0x02ADA802, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02, 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F88033, 0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003, 0x0404DC09, 0x0405E411, 0x04063003, 0x0406400D, 0x04068001, 0x0407402E, 0x040B8001, 0x040DD805, 0x040E7C01, 0x040F4001, 0x0415BC01, 0x04215C01, 0x0421DC02, 0x04247C01, 0x0424FC01, 0x04280403, 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009, 0x0429FC01, 0x042B2001, 0x042B9402, 0x042BC007, 0x042CE407, 0x042E6404, 0x04349004, 0x043AAC03, 0x043D180B, 0x043D5405, 0x04400003, 0x0440E016, 0x0441FC04, 0x0442C012, 0x04433401, 0x04440003, 0x04449C0E, 0x04450004, 0x04451402, 0x0445CC03, 0x04460003, 0x0446CC0E, 0x0447140B, 0x04476C01, 0x04477403, 0x0448B013, 0x044AA401, 0x044B7C0C, 0x044C0004, 0x044CEC02, 0x044CF807, 0x044D1C02, 0x044D2C03, 0x044D5C01, 0x044D8802, 0x044D9807, 0x044DC005, 0x0450D412, 0x04512C05, 0x04516802, 0x04517402, 0x0452C014, 0x04531801, 0x0456BC07, 0x0456E020, 0x04577002, 0x0458C014, 0x0459800D, 0x045AAC0D, 0x045C740F, 0x045CF004, 0x0460B010, 0x0464C006, 0x0464DC02, 0x0464EC04, 0x04650001, 0x04650805, 0x04674407, 0x04676807, 0x04678801, 0x04679001, 0x0468040A, 0x0468CC07, 0x0468EC0D, 0x0469440B, 0x046A2813, 0x046A7805, 0x0470BC08, 0x0470E008, 0x04710405, 0x0471C002, 0x04724816, 0x0472A40E, 0x0474C406, 0x0474E801, 0x0474F002, 0x0474FC07, 0x04751C01, 0x04762805, 0x04764002, 0x04764C05, 0x047BCC06, 0x047F541D, 0x047FFC01, 0x0491C005, 0x04D0C009, 0x05A9B802, 0x05ABC006, 0x05ACC010, 0x05AD1002, 0x05BA5C04, 0x05BD3C01, 0x05BD4437, 0x05BE3C04, 0x05BF8801, 0x05BF9001, 0x05BFC002, 0x06F27008, 0x074000F6, 0x07440027, 0x0744A4C0, 0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401, 0x075F0C01, 0x0760028C, 0x076A6C05, 0x076A840F, 0x07800007, 0x07802011, 0x07806C07, 0x07808C02, 0x07809805, 0x0784C007, 0x07853C01, 0x078BB004, 0x078BFC01, 0x07A34007, 0x07A51007, 0x07A57802, 0x07B2B001, 0x07B2C001, 0x07B4B801, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F, 0x07C2C40F, 0x07C3040F, 0x07C34425, 0x07C434A1, 0x07C7981D, 0x07C8402C, 0x07C90009, 0x07C94002, 0x07C98006, 0x07CC03D8, 0x07DB800D, 0x07DBC00D, 0x07DC0074, 0x07DE0059, 0x07DF800C, 0x07E0000C, 0x07E04038, 0x07E1400A, 0x07E18028, 0x07E2401E, 0x07E2C002, 0x07E40079, 0x07E5E852, 0x07E73487, 0x07E9800E, 0x07E9C005, 0x07E9E003, 0x07EA0007, 0x07EA4019, 0x07EAC007, 0x07EB0003, 0x07EB4007, 0x07EC0093, 0x07EE5037, 0x38000401, 0x38008060, 0x380400F0, }; static const unsigned int aAscii[4] = { 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001, }; if( (unsigned int)c<128 ){ return ( (aAscii[c >> 5] & ((unsigned int)1 << (c & 0x001F)))==0 ); }else if( (unsigned int)c<(1<<22) ){ unsigned int key = (((unsigned int)c)<<10) | 0x000003FF; int iRes = 0; int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; int iLo = 0; while( iHi>=iLo ){ int iTest = (iHi + iLo) / 2; if( key >= aEntry[iTest] ){ iRes = iTest; iLo = iTest+1; }else{ iHi = iTest-1; } } assert( aEntry[0]<key ); assert( key>=aEntry[iRes] ); return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF))); } return 1; } /* ** If the argument is a codepoint corresponding to a lowercase letter ** 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, int bComplex){ 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, 3766, 3832, 3896, 3912, 3928, 3944, 3968, 4008, 4040, 4056, 4106, 4138, 4170, 4202, 4234, 4266, 4296, 4312, 4344, 4408, 4424, 4442, 4472, 4488, 4504, 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529, 61448, 61468, 61512, 61534, 61592, 61610, 61642, 61672, 61688, 61704, 61726, 61784, 61800, 61816, 61836, 61880, 61896, 61914, 61948, 61998, 62062, 62122, 62154, 62184, 62200, 62218, 62252, 62302, 62364, 62410, 62442, 62478, 62536, 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730, 62766, 62830, 62890, 62924, 62974, 63032, 63050, 63082, 63118, 63182, 63242, 63274, 63310, 63368, 63390, }; #define HIBIT ((unsigned char)0x80) static const unsigned 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', 'u'|HIBIT, 'a'|HIBIT, 'g', 'k', 'o', 'o'|HIBIT, 'j', 'g', 'n', 'a'|HIBIT, 'a', 'e', 'i', 'o', 'r', 'u', 's', 't', 'h', 'a', 'e', 'o'|HIBIT, 'o', 'o'|HIBIT, 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 'a', 'b', 'c'|HIBIT, 'd', 'd', 'e'|HIBIT, 'e', 'e'|HIBIT, 'f', 'g', 'h', 'h', 'i', 'i'|HIBIT, 'k', 'l', 'l'|HIBIT, 'l', 'm', 'n', 'o'|HIBIT, 'p', 'r', 'r'|HIBIT, 'r', 's', 's'|HIBIT, 't', 'u', 'u'|HIBIT, 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a', 'a'|HIBIT, 'a'|HIBIT, 'a'|HIBIT, 'e', 'e'|HIBIT, 'e'|HIBIT, 'i', 'o', 'o'|HIBIT, 'o'|HIBIT, 'o'|HIBIT, 'u', 'u'|HIBIT, 'u'|HIBIT, '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] ); if( bComplex==0 && (aChar[iRes] & 0x80) ) return c; return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : ((int)aChar[iRes] & 0x7F); } /* ** Return true if the argument interpreted as a unicode codepoint ** is a diacritical modifier character. */ int unicode_is_diacritic(int c){ unsigned int mask0 = 0x08029FDF; unsigned int mask1 = 0x000361F8; if( c<768 || c>817 ) return 0; return (c < 768+32) ? (mask0 & ((unsigned int)1 << (c-768))) : (mask1 & ((unsigned int)1 << (c-768-32))); } /* ** Interpret the argument as a unicode codepoint. If the codepoint ** is an upper case character that has a lower case equivalent, ** return the codepoint corresponding to the lower case version. ** Otherwise, return a copy of the argument. ** ** The results are undefined if the value passed to this function ** is less than zero. */ int unicode_fold(int c, int eRemoveDiacritic){ /* Each entry in the following array defines a rule for folding a range ** of codepoints to lower case. The rule applies to a range of nRange ** codepoints starting at codepoint iCode. ** ** If the least significant bit in flags is clear, then the rule applies ** to all nRange codepoints (i.e. all nRange codepoints are upper case and ** need to be folded). Or, if it is set, then the rule only applies to ** every second codepoint in the range, starting with codepoint C. ** ** The 7 most significant bits in flags are an index into the aiOff[] ** array. If a specific codepoint C does require folding, then its lower ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF). ** ** The contents of this array are generated by parsing the CaseFolding.txt ** file distributed as part of the "Unicode Character Database". See ** http://www.unicode.org for details. */ static const struct TableEntry { unsigned short iCode; unsigned char flags; unsigned char nRange; } aEntry[] = { {65, 14, 26}, {181, 66, 1}, {192, 14, 23}, {216, 14, 7}, {256, 1, 48}, {306, 1, 6}, {313, 1, 16}, {330, 1, 46}, {376, 156, 1}, {377, 1, 6}, {383, 144, 1}, {385, 52, 1}, {386, 1, 4}, {390, 46, 1}, {391, 0, 1}, {393, 44, 2}, {395, 0, 1}, {398, 34, 1}, {399, 40, 1}, {400, 42, 1}, {401, 0, 1}, {403, 44, 1}, {404, 48, 1}, {406, 54, 1}, {407, 50, 1}, {408, 0, 1}, {412, 54, 1}, {413, 56, 1}, {415, 58, 1}, {416, 1, 6}, {422, 62, 1}, {423, 0, 1}, {425, 62, 1}, {428, 0, 1}, {430, 62, 1}, {431, 0, 1}, {433, 60, 2}, {435, 1, 4}, {439, 64, 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, 162, 1}, {503, 174, 1}, {504, 1, 40}, {544, 150, 1}, {546, 1, 18}, {570, 74, 1}, {571, 0, 1}, {573, 148, 1}, {574, 72, 1}, {577, 0, 1}, {579, 146, 1}, {580, 30, 1}, {581, 32, 1}, {582, 1, 10}, {837, 38, 1}, {880, 1, 4}, {886, 0, 1}, {895, 38, 1}, {902, 20, 1}, {904, 18, 3}, {908, 28, 1}, {910, 26, 2}, {913, 14, 17}, {931, 14, 9}, {962, 0, 1}, {975, 4, 1}, {976, 180, 1}, {977, 182, 1}, {981, 186, 1}, {982, 184, 1}, {984, 1, 24}, {1008, 176, 1}, {1009, 178, 1}, {1012, 170, 1}, {1013, 168, 1}, {1015, 0, 1}, {1017, 192, 1}, {1018, 0, 1}, {1021, 150, 3}, {1024, 36, 16}, {1040, 14, 32}, {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1}, {1217, 1, 14}, {1232, 1, 96}, {1329, 24, 38}, {4256, 70, 38}, {4295, 70, 1}, {4301, 70, 1}, {5112, 190, 6}, {7296, 126, 1}, {7297, 128, 1}, {7298, 130, 1}, {7299, 134, 2}, {7301, 132, 1}, {7302, 136, 1}, {7303, 138, 1}, {7304, 100, 1}, {7312, 142, 43}, {7357, 142, 3}, {7680, 1, 150}, {7835, 172, 1}, {7838, 120, 1}, {7840, 1, 96}, {7944, 190, 8}, {7960, 190, 6}, {7976, 190, 8}, {7992, 190, 8}, {8008, 190, 6}, {8025, 191, 8}, {8040, 190, 8}, {8072, 190, 8}, {8088, 190, 8}, {8104, 190, 8}, {8120, 190, 2}, {8122, 166, 2}, {8124, 188, 1}, {8126, 124, 1}, {8136, 164, 4}, {8140, 188, 1}, {8152, 190, 2}, {8154, 160, 2}, {8168, 190, 2}, {8170, 158, 2}, {8172, 192, 1}, {8184, 152, 2}, {8186, 154, 2}, {8188, 188, 1}, {8486, 122, 1}, {8490, 116, 1}, {8491, 118, 1}, {8498, 12, 1}, {8544, 8, 16}, {8579, 0, 1}, {9398, 10, 26}, {11264, 24, 47}, {11360, 0, 1}, {11362, 112, 1}, {11363, 140, 1}, {11364, 114, 1}, {11367, 1, 6}, {11373, 108, 1}, {11374, 110, 1}, {11375, 104, 1}, {11376, 106, 1}, {11378, 0, 1}, {11381, 0, 1}, {11390, 102, 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, 98, 1}, {42878, 1, 10}, {42891, 0, 1}, {42893, 88, 1}, {42896, 1, 4}, {42902, 1, 20}, {42922, 80, 1}, {42923, 76, 1}, {42924, 78, 1}, {42925, 84, 1}, {42926, 80, 1}, {42928, 92, 1}, {42929, 86, 1}, {42930, 90, 1}, {42931, 68, 1}, {42932, 1, 12}, {42946, 0, 1}, {42948, 178, 1}, {42949, 82, 1}, {42950, 96, 1}, {42951, 1, 4}, {42997, 0, 1}, {43888, 94, 80}, {65313, 14, 26}, }; static const unsigned short aiOff[] = { 1, 2, 8, 15, 16, 26, 28, 32, 34, 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, 928, 7264, 10792, 10795, 23217, 23221, 23228, 23229, 23231, 23254, 23256, 23275, 23278, 26672, 30152, 30204, 35267, 54721, 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274, 57921, 58019, 58363, 59314, 59315, 59324, 59325, 59326, 59332, 59356, 61722, 62528, 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( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 ); if( c<128 ){ if( c>='A' && c<='Z' ) ret = c + ('a' - 'A'); }else if( c<65536 ){ const struct TableEntry *p; int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; int iLo = 0; int iRes = -1; assert( c>aEntry[0].iCode ); while( iHi>=iLo ){ int iTest = (iHi + iLo) / 2; int cmp = (c - aEntry[iTest].iCode); if( cmp>=0 ){ iRes = iTest; iLo = iTest+1; }else{ iHi = iTest-1; } } assert( iRes>=0 && c>=aEntry[iRes].iCode ); p = &aEntry[iRes]; if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){ ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF; assert( ret>0 ); } if( eRemoveDiacritic ){ ret = unicode_remove_diacritic(ret, eRemoveDiacritic==2); } } else if( c>=66560 && c<66600 ){ ret = c + 40; } else if( c>=66736 && c<66772 ){ ret = c + 40; } else if( c>=68736 && c<68787 ){ ret = c + 64; } else if( c>=71840 && c<71872 ){ ret = c + 32; } else if( c>=93760 && c<93792 ){ ret = c + 32; } else if( c>=125184 && c<125218 ){ ret = c + 34; } return ret; } |
Added src/unversioned.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 | /* ** Copyright (c) 2016 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 unversioned file interfaces. */ #include "config.h" #include <assert.h> #include <zlib.h> #include "unversioned.h" #include <time.h> /* ** SQL code to implement the tables needed by the unversioned. */ static const char zUnversionedInit[] = @ CREATE TABLE IF NOT EXISTS repository.unversioned( @ uvid INTEGER PRIMARY KEY AUTOINCREMENT, -- unique ID for this file @ name TEXT UNIQUE, -- Name of the uv file @ rcvid INTEGER, -- Where received from @ mtime DATETIME, -- timestamp. Seconds since 1970. @ hash TEXT, -- Content hash. NULL if a delete marker @ sz INTEGER, -- size of content after decompression @ encoding INT, -- 0: plaintext. 1: zlib compressed @ content BLOB -- content of the file. NULL if oversized @ ); ; /* ** Make sure the unversioned table exists in the repository. */ void unversioned_schema(void){ if( !db_table_exists("repository", "unversioned") ){ db_multi_exec(zUnversionedInit/*works-like:""*/); } } /* ** Return a string which is the hash of the unversioned content. ** This is the hash used by repositories to compare content before ** exchanging a catalog. So all repositories must compute this hash ** in exactly the same way. ** ** If debugFlag is set, force the value to be recomputed and write ** the text of the hashed string to stdout. */ const char *unversioned_content_hash(int debugFlag){ const char *zHash = debugFlag ? 0 : db_get("uv-hash", 0); if( zHash ) return zHash; if( !db_table_exists("repository","unversioned") ){ return "da39a3ee5e6b4b0d3255bfef95601890afd80709"; }else{ Stmt q; db_prepare(&q, "SELECT printf('%%s %%s %%s\n',name,datetime(mtime,'unixepoch'),hash)" " FROM unversioned" " WHERE hash IS NOT NULL" " ORDER BY name" ); while( db_step(&q)==SQLITE_ROW ){ const char *z = db_column_text(&q, 0); if( debugFlag ) fossil_print("%s", z); sha1sum_step_text(z,-1); } db_finalize(&q); db_set("uv-hash", sha1sum_finish(0), 0); zHash = db_get("uv-hash",0); } return zHash; } /* ** Initialize pContent to be the content of an unversioned file zName. ** ** Return 0 on failures. ** Return 1 if the file is found by name. ** Return 2 if the file is found by hash. */ int unversioned_content(const char *zName, Blob *pContent){ Stmt q; int rc = 0; blob_init(pContent, 0, 0); db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE name=%Q", zName); if( db_step(&q)==SQLITE_ROW ){ db_column_blob(&q, 1, pContent); if( db_column_int(&q, 0)==1 ){ blob_uncompress(pContent, pContent); } rc = 1; } db_finalize(&q); if( rc==0 && validate16(zName,-1) ){ db_prepare(&q, "SELECT encoding, content FROM unversioned WHERE hash=%Q", zName); if( db_step(&q)==SQLITE_ROW ){ db_column_blob(&q, 1, pContent); if( db_column_int(&q, 0)==1 ){ blob_uncompress(pContent, pContent); } rc = 2; } db_finalize(&q); } return rc; } /* ** Write unversioned content into the database. */ static void unversioned_write( const char *zUVFile, /* Name of the unversioned file */ Blob *pContent, /* File content */ sqlite3_int64 mtime /* Modification time */ ){ Stmt ins; Blob compressed; Blob hash; db_prepare(&ins, "REPLACE INTO unversioned(name,rcvid,mtime,hash,sz,encoding,content)" " VALUES(:name,:rcvid,:mtime,:hash,:sz,:encoding,:content)" ); hname_hash(pContent, 0, &hash); blob_compress(pContent, &compressed); db_bind_text(&ins, ":name", zUVFile); db_bind_int(&ins, ":rcvid", g.rcvid); db_bind_int64(&ins, ":mtime", mtime); db_bind_text(&ins, ":hash", blob_str(&hash)); db_bind_int(&ins, ":sz", blob_size(pContent)); if( blob_size(&compressed) <= 0.8*blob_size(pContent) ){ db_bind_int(&ins, ":encoding", 1); db_bind_blob(&ins, ":content", &compressed); }else{ db_bind_int(&ins, ":encoding", 0); db_bind_blob(&ins, ":content", pContent); } db_step(&ins); blob_reset(&compressed); blob_reset(&hash); db_finalize(&ins); db_unset("uv-hash", 0); } /* ** Check the status of unversioned file zName. "mtime" and "zHash" are the ** time of last change and hash of a copy of this file on a remote ** server. Return an integer status code as follows: ** ** 0: zName does not exist in the unversioned table. ** 1: zName exists and should be replaced by the mtime/zHash remote. ** 2: zName exists and is the same as zHash but has an older mtime ** 3: zName exists and is identical to mtime/zHash in all respects. ** 4: zName exists and is the same as zHash but has a newer mtime. ** 5: zName exists and should override the mtime/zHash remote. */ int unversioned_status( const char *zName, sqlite3_int64 mtime, const char *zHash ){ int iStatus = 0; Stmt q; db_prepare(&q, "SELECT mtime, hash FROM unversioned WHERE name=%Q", zName); if( db_step(&q)==SQLITE_ROW ){ const char *zLocalHash = db_column_text(&q, 1); int hashCmp; sqlite3_int64 iLocalMtime = db_column_int64(&q, 0); int mtimeCmp = iLocalMtime<mtime ? -1 : (iLocalMtime==mtime ? 0 : +1); if( zLocalHash==0 ) zLocalHash = "-"; hashCmp = strcmp(zLocalHash, zHash); if( hashCmp==0 ){ iStatus = 3 + mtimeCmp; }else if( mtimeCmp<0 || (mtimeCmp==0 && hashCmp<0) ){ iStatus = 1; }else{ iStatus = 5; } } db_finalize(&q); return iStatus; } /* ** Extract command-line options for the "revert" and "sync" subcommands */ static int unversioned_sync_flags(unsigned syncFlags){ if( find_option("verbose","v",0)!=0 ){ syncFlags |= SYNC_UV_TRACE | SYNC_VERBOSE; } if( find_option("dry-run","n",0)!=0 ){ syncFlags |= SYNC_UV_DRYRUN | SYNC_UV_TRACE | SYNC_VERBOSE; } return syncFlags; } /* ** Return true if the zName contains any whitespace */ static int contains_whitespace(const char *zName){ while( zName[0] ){ if( fossil_isspace(zName[0]) ) return 1; zName++; } return 0; } /* ** COMMAND: uv# ** COMMAND: unversioned ** ** Usage: %fossil unversioned SUBCOMMAND ARGS... ** or: %fossil uv SUBCOMMAND ARGS.. ** ** Unversioned files (UV-files) are artifacts that are synced and are available ** for download but which do not preserve history. Only the most recent version ** of each UV-file is retained. Changes to an UV-file are permanent and cannot ** be undone, so use appropriate caution with this command. ** ** Subcommands: ** ** add FILE ... Add or update one or more unversioned files in ** the local repository so that they match FILEs ** on disk. Changes are not pushed to other ** repositories until the next sync. ** ** add FILE --as UVFILE Add or update a single file named FILE on disk ** and UVFILE in the repository unversioned file ** namespace. This variant of the 'add' command allows ** the name to be different in the repository versus ** what appears on disk, but it only allows adding ** a single file at a time. ** ** cat FILE ... Concatenate the content of FILEs to stdout. ** ** edit FILE Bring up FILE in a text editor for modification. ** ** export FILE OUTPUT Write the content of FILE into OUTPUT on disk ** ** list | ls Show all unversioned files held in the local ** repository. ** ** Options: ** --glob PATTERN Show only files that match ** --like PATTERN Show only files that match ** -l Show additional details for ** files that match. Implied ** when 'list' is used. ** ** revert ?URL? Restore the state of all unversioned files in the ** local repository to match the remote repository ** URL. ** ** Options: ** -v|--verbose Extra diagnostic output ** -n|--dry-run Show what would have happened ** ** remove|rm|delete FILE ... ** Remove unversioned files from the local repository. ** Changes are not pushed to other repositories until ** the next sync. ** ** Options: ** --glob PATTERN Remove files that match ** --like PATTERN Remove files that match ** ** sync ?URL? Synchronize the state of all unversioned files with ** the remote repository URL. The most recent version ** of each file is propagated to all repositories and ** all prior versions are permanently forgotten. ** The remote account requires the 'y' capability. ** ** Options: ** -v|--verbose Extra diagnostic output ** -n|--dry-run Show what would have happened ** ** touch FILE ... Update the TIMESTAMP on all of the listed files ** ** Options: ** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add", ** "edit", "remove", and "touch" subcommands. ** -R|--repository REPO Use FILE as the repository */ void unversioned_cmd(void){ const char *zCmd; int nCmd; const char *zMtime = find_option("mtime", 0, 1); sqlite3_int64 mtime; db_find_and_open_repository(0, 0); unversioned_schema(); zCmd = g.argc>=3 ? g.argv[2] : "x"; nCmd = (int)strlen(zCmd); if( zMtime==0 ){ mtime = time(0); }else{ mtime = db_int(0, "SELECT strftime('%%s',%Q)", zMtime); if( mtime<=0 ) fossil_fatal("bad timestamp: %Q", zMtime); } if( strncmp(zCmd, "add", nCmd)==0 ){ const char *zError = 0; const char *zIn; const char *zAs; Blob file; int i; zAs = find_option("as",0,1); verify_all_options(); if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE"); db_begin_transaction(); content_rcvid_init("#!fossil unversioned add"); for(i=3; i<g.argc; i++){ zIn = zAs ? zAs : g.argv[i]; if( zIn[0]==0 ){ zError = "be empty string"; }else if( zIn[0]=='/' ){ zError = "be absolute"; }else if ( !file_is_simple_pathname(zIn,1) ){ zError = "contain complex paths"; }else if( contains_whitespace(zIn) ){ zError = "contain whitespace"; } if( zError ){ fossil_fatal("unversioned filenames may not %s: %Q", zError, zIn); } blob_init(&file,0,0); blob_read_from_file(&file, g.argv[i], ExtFILE); unversioned_write(zIn, &file, mtime); blob_reset(&file); } db_end_transaction(0); }else if( strncmp(zCmd, "cat", nCmd)==0 ){ int i; verify_all_options(); db_begin_transaction(); for(i=3; i<g.argc; i++){ Blob content; if( unversioned_content(g.argv[i], &content)!=0 ){ blob_write_to_file(&content, "-"); } blob_reset(&content); } db_end_transaction(0); }else if( strncmp(zCmd, "edit", nCmd)==0 ){ const char *zEditor; /* Name of the text-editor command */ const char *zTFile; /* Temporary file */ const char *zUVFile; /* Name of the unversioned file */ char *zCmd; /* Command to run the text editor */ Blob content; /* Content of the unversioned file */ verify_all_options(); if( g.argc!=4) usage("edit UVFILE"); zUVFile = g.argv[3]; zEditor = fossil_text_editor(); if( zEditor==0 ){ fossil_fatal("no text editor - set the VISUAL env variable"); } zTFile = fossil_temp_filename(); if( zTFile==0 ) fossil_fatal("cannot find a temporary filename"); db_begin_transaction(); content_rcvid_init("#!fossil unversioned edit"); if( unversioned_content(zUVFile, &content)==0 ){ fossil_fatal("no such uv-file: %Q", zUVFile); } if( looks_like_binary(&content) ){ fossil_fatal("cannot edit binary content"); } #if defined(_WIN32) || defined(__CYGWIN__) blob_add_cr(&content); #endif blob_write_to_file(&content, zTFile); zCmd = mprintf("%s %$", zEditor, zTFile); if( fossil_system(zCmd) ){ fossil_fatal("editor aborted: %Q", zCmd); } fossil_free(zCmd); blob_reset(&content); blob_read_from_file(&content, zTFile, ExtFILE); #if defined(_WIN32) || defined(__CYGWIN__) blob_to_lf_only(&content); #endif file_delete(zTFile); if( zMtime==0 ) mtime = time(0); unversioned_write(zUVFile, &content, mtime); db_end_transaction(0); blob_reset(&content); }else if( strncmp(zCmd, "export", nCmd)==0 ){ Blob content; verify_all_options(); if( g.argc!=5 ) usage("export UVFILE OUTPUT"); if( unversioned_content(g.argv[3], &content)==0 ){ fossil_fatal("no such uv-file: %Q", g.argv[3]); } blob_write_to_file(&content, g.argv[4]); blob_reset(&content); }else if( strncmp(zCmd, "hash", nCmd)==0 ){ /* undocumented */ /* Show the hash value used during uv sync */ int debugFlag = find_option("debug",0,0)!=0; fossil_print("%s\n", unversioned_content_hash(debugFlag)); }else if( strncmp(zCmd, "list", nCmd)==0 || strncmp(zCmd, "ls", nCmd)==0 ){ Stmt q; int allFlag = find_option("all","a",0)!=0; int longFlag = find_option("l",0,0)!=0 || (nCmd>1 && zCmd[1]=='i'); char *zPattern = sqlite3_mprintf("true"); const char *zGlob; zGlob = find_option("glob",0,1); if( zGlob ){ sqlite3_free(zPattern); zPattern = sqlite3_mprintf("(name GLOB %Q)", zGlob); } zGlob = find_option("like",0,1); if( zGlob ){ sqlite3_free(zPattern); zPattern = sqlite3_mprintf("(name LIKE %Q)", zGlob); } verify_all_options(); if( !longFlag ){ if( allFlag ){ db_prepare(&q, "SELECT name FROM unversioned WHERE %s ORDER BY name", zPattern/*safe-for-%s*/); }else{ db_prepare(&q, "SELECT name FROM unversioned" " WHERE %s AND hash IS NOT NULL" " ORDER BY name", zPattern/*safe-for-%s*/); } while( db_step(&q)==SQLITE_ROW ){ fossil_print("%s\n", db_column_text(&q,0)); } }else{ db_prepare(&q, "SELECT hash, datetime(mtime,'unixepoch'), sz, length(content), name" " FROM unversioned WHERE %s" " ORDER BY name;", zPattern/*safe-for-%s*/ ); while( db_step(&q)==SQLITE_ROW ){ const char *zHash = db_column_text(&q, 0); const char *zNoContent = ""; if( zHash==0 ){ if( !allFlag ) continue; zHash = "(deleted)"; }else if( db_column_type(&q,3)==SQLITE_NULL ){ zNoContent = " (no content)"; } fossil_print("%12.12s %s %8d %8d %s%s\n", zHash, db_column_text(&q,1), db_column_int(&q,2), db_column_int(&q,3), db_column_text(&q,4), zNoContent ); } } db_finalize(&q); sqlite3_free(zPattern); }else if( strncmp(zCmd, "revert", nCmd)==0 ){ unsigned syncFlags = unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT); g.argv[1] = "sync"; g.argv[2] = "--uv-noop"; sync_unversioned(syncFlags); }else if( strncmp(zCmd, "remove", nCmd)==0 || strncmp(zCmd, "rm", nCmd)==0 || strncmp(zCmd, "delete", nCmd)==0 ){ int i; const char *zGlob; db_begin_transaction(); while( (zGlob = find_option("glob",0,1))!=0 ){ db_multi_exec( "UPDATE unversioned" " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name GLOB %Q", mtime, zGlob ); } while( (zGlob = find_option("like",0,1))!=0 ){ db_multi_exec( "UPDATE unversioned" " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name LIKE %Q", mtime, zGlob ); } verify_all_options(); for(i=3; i<g.argc; i++){ db_multi_exec( "UPDATE unversioned" " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q", mtime, g.argv[i] ); } db_unset("uv-hash", 0); db_end_transaction(0); }else if( strncmp(zCmd,"sync",nCmd)==0 ){ unsigned syncFlags = unversioned_sync_flags(SYNC_UNVERSIONED); g.argv[1] = "sync"; g.argv[2] = "--uv-noop"; sync_unversioned(syncFlags); }else if( strncmp(zCmd, "touch", nCmd)==0 ){ int i; verify_all_options(); db_begin_transaction(); for(i=3; i<g.argc; i++){ db_multi_exec( "UPDATE unversioned SET mtime=%lld WHERE name=%Q", mtime, g.argv[i] ); } db_unset("uv-hash", 0); db_end_transaction(0); }else{ usage("add|cat|edit|export|list|revert|remove|sync|touch"); } } /* ** WEBPAGE: uvlist ** ** Display a list of all unversioned files in the repository. ** Query parameters: ** ** byage=1 Order the initial display be decreasing age ** showdel=0 Show deleted files */ void uvlist_page(void){ Stmt q; sqlite3_int64 iNow; sqlite3_int64 iTotalSz = 0; int cnt = 0; int n = 0; const char *zOrderBy = "name"; int showDel = 0; char zSzName[100]; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } cgi_check_for_malice(); etag_check(ETAG_DATA,0); style_header("Unversioned Files"); if( !db_table_exists("repository","unversioned") ){ @ No unversioned files on this server style_finish_page(); return; } if( PB("byage") ) zOrderBy = "mtime DESC"; if( PB("showdel") ) showDel = 1; db_prepare(&q, "SELECT" " name," " mtime," " hash," " sz," " (SELECT login FROM rcvfrom, user" " WHERE user.uid=rcvfrom.uid AND rcvfrom.rcvid=unversioned.rcvid)," " rcvid" " FROM unversioned %s ORDER BY %s", showDel ? "" : "WHERE hash IS NOT NULL" /*safe-for-%s*/, zOrderBy/*safe-for-%s*/ ); iNow = db_int64(0, "SELECT strftime('%%s','now');"); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); sqlite3_int64 mtime = db_column_int(&q, 1); const char *zHash = db_column_text(&q, 2); int isDeleted = zHash==0; const char *zAlgo; int fullSize = db_column_int(&q, 3); char *zAge = human_readable_age((iNow - mtime)/86400.0); const char *zLogin = db_column_text(&q, 4); int rcvid = db_column_int(&q,5); if( isDeleted ) zAlgo = "deleted"; else zAlgo = hname_alg(strlen(zHash)); if( zLogin==0 ) zLogin = ""; if( (n++)==0 ){ style_table_sorter(); @ <div class="uvlist"> @ <table cellpadding="2" cellspacing="0" border="1" class='sortable' \ @ data-column-types='tkKttn' data-init-sort='1'> @ <thead><tr> @ <th> Name @ <th> Age @ <th> Size @ <th> User @ <th> Hash @ <th> Algo if( g.perm.Admin ){ @ <th> rcvid } @ </tr></thead> @ <tbody> } @ <tr> if( isDeleted ){ sqlite3_snprintf(sizeof(zSzName), zSzName, "<i>Deleted</i>"); zHash = ""; fullSize = 0; @ <td> %h(zName) </td> }else{ approxSizeName(sizeof(zSzName), zSzName, fullSize); iTotalSz += fullSize; cnt++; @ <td> <a href='%R/uv/%T(zName)'>%h(zName)</a> </td> } @ <td data-sortkey='%016llx(-mtime)'> %s(zAge) </td> @ <td data-sortkey='%08x(fullSize)'> %s(zSzName) </td> @ <td> %h(zLogin) </td> @ <td><code> %h(zHash) </code></td> @ <td> %s(zAlgo) </td> if( g.perm.Admin ){ if( rcvid ){ @ <td> <a href="%R/rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a> }else{ @ <td> } } @ </tr> fossil_free(zAge); } db_finalize(&q); if( n ){ approxSizeName(sizeof(zSzName), zSzName, iTotalSz); @ </tbody> @ <tfoot><tr><td><b>Total for %d(cnt) files</b><td><td>%s(zSzName) @ <td><td> if( g.perm.Admin ){ @ <td> } @ <td> @ </tfoot> @ </table></div> }else{ @ No unversioned files on this server. } style_finish_page(); } /* ** WEBPAGE: juvlist ** ** Return a complete list of unversioned files as JSON. The JSON ** looks like this: ** ** [{"name":NAME, ** "mtime":MTIME, ** "hash":HASH, ** "size":SIZE, ** "user":USER}] */ void uvlist_json_page(void){ Stmt q; char *zSep = "["; Blob json; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } cgi_check_for_malice(); cgi_set_content_type("application/json"); etag_check(ETAG_DATA,0); if( !db_table_exists("repository","unversioned") ){ blob_init(&json, "[]", -1); cgi_set_content(&json); return; } blob_init(&json, 0, 0); db_prepare(&q, "SELECT" " name," " mtime," " hash," " sz," " (SELECT login FROM rcvfrom, user" " WHERE user.uid=rcvfrom.uid AND rcvfrom.rcvid=unversioned.rcvid)" " FROM unversioned WHERE hash IS NOT NULL" ); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); sqlite3_int64 mtime = db_column_int(&q, 1); const char *zHash = db_column_text(&q, 2); int fullSize = db_column_int(&q, 3); const char *zLogin = db_column_text(&q, 4); if( zLogin==0 ) zLogin = ""; blob_appendf(&json, "%s{\"name\":\"%j\",\n", zSep, zName); zSep = ",\n "; blob_appendf(&json, " \"mtime\":%lld,\n", mtime); blob_appendf(&json, " \"hash\":\"%j\",\n", zHash); blob_appendf(&json, " \"size\":%d,\n", fullSize); blob_appendf(&json, " \"user\":\"%j\"}", zLogin); } db_finalize(&q); blob_appendf(&json,"]\n"); cgi_set_content(&json); } |
Changes to src/update.c.
︙ | ︙ | |||
12 13 14 15 16 17 18 | ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to merge the changes in the current | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | > | > > > > | > > > | | | | | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > | > | | < < | > > > > > | | | | | | < < | > > > > > > > | > > > > > > > > > > > > > > | | | | | | | | | > | > > > > > > > > > | | | > > | | > > | > > > | < < > | > > > > | | > | < > | > | > > > | | > > > > > > > > | > > > > > > > > > | | > > > > > > > > | | > > > > > | | > > > > > > > > > > | > > | | | | | < | < > > > | | > | > < > | > | > > > | > | > > > > > > | > < | > > > > | | > > > > > | > > > > > > > > > > > > > | | | > > > > > > > > > > > > | > | | | > > > | > | | | | | | | | > > | > < | | < | > | > > > > > > > > > > | > > > | > > > > > > > | | | < | < | > | > > | | > | | > > > > > > > > > > > > | > > | > > > > > < > > > | > > > > > | | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | < | < < | | < | > | | > > | > > > > > > > | > > | > > | > > | | > | > > > | | > > > > > > > > > | < | | > > > | > > | > > | > | > > | > | > > | > > > > > > > | > > | > > | > > | | | | > > > > > > > | > > > > > > > > > > | | | < < > > | > | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > | > | > | > > > > > > > > | > | > | | < > | > | > > > > | > > | | > > > > > > > | < < | > > | | > > > > > > > > | > > > | 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 | ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to merge the changes in the current ** check-out into a different version and switch to that version. */ #include "config.h" #include "update.h" #include <assert.h> /* ** Return true if artifact rid is a version */ int is_a_version(int rid){ return db_exists("SELECT 1 FROM event WHERE objid=%d AND type='ci'", rid); } /* 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]; newArgv[0] = g.argv[0]; newArgv[1] = "update"; newArgv[2] = 0; savedArgv = g.argv; savedArgc = g.argc; g.argc = 2; g.argv = newArgv; internalUpdate = vid; internalConflictCnt = 0; update_cmd(); g.argc = savedArgc; g.argv = savedArgv; return internalConflictCnt; } /* ** COMMAND: update ** ** Usage: %fossil update ?OPTIONS? ?VERSION? ?FILES...? ** ** Change the version of the current check-out to VERSION. Any ** uncommitted changes are retained and applied to the new check-out. ** ** 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 check-out are subject ** to being updated and the version of the current check-out is changed ** to VERSION. Any uncommitted changes are retained and applied to the ** new check-out. ** ** 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 check-out 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 ** -n|--dry-run If given, display instead of run actions ** --force-missing Force update if missing content after sync ** -K|--keep-merge-files On merge conflict, retain the temporary files ** used for merging, named *-baseline, *-original, ** and *-merge. ** --latest Acceptable in place of VERSION, update to ** latest version ** --nosync Do not auto-sync prior to update ** --setmtime Set timestamps of all files to match their ** SCM-side times (the timestamp of the last ** check-in which modified them). ** -v|--verbose Print status information about all files ** -W|--width WIDTH Width of lines (default is to auto-detect). ** Must be more than 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 keepMergeFlag; /* True if --keep-merge-files is present */ 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 bNosync = 0; /* --nosync. Omit the auto-sync */ 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; keepMergeFlag = find_option("keep-merge-files", "K",0)!=0; bNosync = find_option("nosync",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 && !bNosync ){ if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "update") ){ fossil_fatal("update abandoned due to sync failure"); } } /* Create any empty directories now, as well as after the update, ** so changes in settings are reflected now */ if( !dryRunFlag ) ensure_empty_dirs_created(0); if( internalUpdate ){ tid = internalUpdate; }else if( g.argc>=3 ){ if( fossil_strcmp(g.argv[2], "current")==0 ){ /* If VERSION is "current", then use the same algorithm to find the ** target as if VERSION were omitted. */ }else if( fossil_strcmp(g.argv[2], "latest")==0 ){ /* If VERSION is "latest", then use the same algorithm to find the ** target as if VERSION were omitted and the --latest flag is present. */ latestFlag = 1; }else{ tid = name_to_typed_rid(g.argv[2],"ci"); if( tid==0 || !is_a_version(tid) ){ fossil_fatal("no such check-in: %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 ){ int closeCode = 1; compute_leaves(vid, closeCode); if( !db_exists("SELECT 1 FROM leaves") ){ closeCode = 0; compute_leaves(vid, closeCode); } if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){ db_multi_exec( "DELETE FROM leaves WHERE rid NOT IN" " (SELECT leaves.rid FROM leaves, tagxref" " 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, 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(); db_multi_exec( "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID", filename_collation() ); 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 check-out, the pivot, and the version being merged. */ db_multi_exec( "DROP TABLE IF EXISTS fv;" "CREATE TEMP TABLE fv(" " fn TEXT %s PRIMARY KEY," /* The filename relative to root */ " idv INTEGER," /* VFILE entry for current version */ " idt INTEGER," /* VFILE entry for target version */ " chnged BOOLEAN," /* True if current version has been edited */ " islinkv BOOLEAN," /* True if current file is a link */ " islinkt BOOLEAN," /* True if target file is a link */ " ridv INTEGER," /* Record ID for current version */ " ridt INTEGER," /* Record ID for target */ " isexe BOOLEAN," /* Does target have execute permission? */ " deleted BOOLEAN DEFAULT 0,"/* File marked by "rm" to become unmanaged */ " fnt TEXT %s" /* Filename of same file on target version */ ");", filename_collation(), filename_collation() ); /* Add files found in the current version */ db_multi_exec( "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged,deleted)" " SELECT pathname, pathname, id, 0, rid, 0, isexe, chnged, deleted" " 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)" " SELECT pathname, pathname, 0, 0, 0, 0, isexe, 0 FROM vfile" " WHERE vid=%d" " AND pathname %s NOT IN (SELECT fnt FROM fv)", tid, filename_collation() ); /* ** Compute the file version ids for T */ db_multi_exec( "UPDATE fv SET" " idt=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnt=pathname),0)," " ridt=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnt=pathname),0)", tid, tid ); /* ** Add islink information */ db_multi_exec( "UPDATE fv SET" " islinkv=coalesce((SELECT islink FROM vfile" " WHERE vid=%d AND fnt=pathname),0)," " islinkt=coalesce((SELECT islink FROM vfile" " WHERE vid=%d AND fnt=pathname),0)", vid, tid ); if( debugFlag ){ db_prepare(&q, "SELECT rowid, fn, fnt, chnged, ridv, ridt, isexe," " islinkv, islinkt FROM fv" ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%3d: ridv=%-4d ridt=%-4d chnged=%d isexe=%d" " islinkv=%d islinkt=%d\n", db_column_int(&q, 0), db_column_int(&q, 4), db_column_int(&q, 5), db_column_int(&q, 3), db_column_int(&q, 6), db_column_int(&q, 7), db_column_int(&q, 8)); fossil_print(" fnv = [%s]\n", db_column_text(&q, 1)); fossil_print(" fnt = [%s]\n", db_column_text(&q, 2)); } db_finalize(&q); } /* If FILES appear on the command-line, remove from the "fv" table ** every entry that is not named on the command-line or which is not ** in a directory named on the command-line. */ if( g.argc>=4 ){ Blob sql; /* SQL statement to purge unwanted entries */ Blob treename; /* Normalized filename */ int i; /* Loop counter */ const char *zSep; /* Term separator */ 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, 0, 1); if( file_isdir(g.argv[i], RepoFILE)==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 check-out 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 */ int chnged = db_column_int(&q, 5); /* Current is edited */ const char *zNewName = db_column_text(&q,6);/* New filename */ int isexe = db_column_int(&q, 7); /* EXE perm for new file */ int islinkv = db_column_int(&q, 8); /* Is current file is a link */ int islinkt = db_column_int(&q, 9); /* Is target file is a link */ int deleted = db_column_int(&q, 10); /* Marked for deletion */ char *zFullPath; /* Full pathname of the file */ char *zFullNewPath; /* Full pathname of dest */ char nameChng; /* True if the name changed */ zFullPath = mprintf("%s%s", g.zLocalRoot, zName); zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName); nameChng = fossil_strcmp(zName, zNewName); nUpdate++; if( deleted ){ db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt); } if( idv>0 && ridv==0 && idt>0 && ridt>0 ){ /* Conflict. This file has been added to the current check-out ** but also exists in the target check-out. Use the current version. */ fossil_print("CONFLICT %s\n", zName); nConflict++; }else if( idt>0 && idv==0 ){ /* File added in the target. */ if( file_isfile_or_link(zFullPath) ){ /* Name of backup file with Original content */ char *zOrig = file_newname(zFullPath, "original", 1); /* Backup previously unanaged file before to be overwritten */ file_copy(zFullPath, zOrig); fossil_free(zOrig); fossil_print("ADD %s - overwrites an unmanaged file", zName); if( !dryRunFlag ) fossil_print(", original copy backed up locally"); fossil_print("\n"); nOverwrite++; }else{ fossil_print("ADD %s\n", zName); } if( !dryRunFlag && !internalUpdate ) undo_save(zName); if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); }else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){ /* The file is unedited. Change it to the target version */ if( deleted ){ fossil_print("UPDATE %s - change to unmanaged file\n", zName); }else{ fossil_print("UPDATE %s\n", zName); } if( !dryRunFlag && !internalUpdate ) undo_save(zName); if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); }else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){ /* The file missing from the local check-out. Restore it to the ** version that appears in the target. */ fossil_print("UPDATE %s\n", zName); if( !dryRunFlag && !internalUpdate ) undo_save(zName); if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); }else if( idt==0 && idv>0 ){ if( ridv==0 ){ /* Added in current check-out. Continue to hold the file as ** as an addition */ db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv); }else if( chnged ){ /* Edited locally but deleted from the target. Do not track the ** file but keep the edited version around. */ fossil_print("CONFLICT %s - edited locally but deleted by update\n", zName); nConflict++; }else{ fossil_print("REMOVE %s\n", zName); if( !dryRunFlag && !internalUpdate ) undo_save(zName); if( !dryRunFlag ){ char *zDir; file_delete(zFullPath); zDir = file_dirname(zName); while( zDir!=0 ){ char *zNext; db_multi_exec("INSERT OR IGNORE INTO dir_to_delete(name)" "VALUES(%Q)", zDir); zNext = db_changes() ? file_dirname(zDir) : 0; fossil_free(zDir); zDir = zNext; } } } }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){ /* Merge the changes in the current tree into the target version */ Blob r, t, v; int rc; if( nameChng ){ fossil_print("MERGE %s -> %s\n", zName, zNewName); }else{ fossil_print("MERGE %s\n", zName); } if( islinkv || islinkt ){ fossil_print("***** Cannot merge symlink %s\n", zNewName); nConflict++; }else{ unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES; if( !dryRunFlag && !internalUpdate ) undo_save(zName); content_get(ridt, &t); content_get(ridv, &v); rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags); if( rc>=0 ){ if( !dryRunFlag ){ blob_write_to_file(&r, zFullNewPath); file_setexe(zFullNewPath, isexe); } if( rc>0 ){ fossil_print("***** %d merge conflicts in %s\n", rc, zNewName); nConflict++; } }else{ if( !dryRunFlag ){ if( !keepMergeFlag ){ /* Name of backup file with Original content */ char *zOrig = file_newname(zFullPath, "original", 1); /* Backup non-mergeable binary file when --keep-merge-files is not specified */ file_copy(zFullPath, zOrig); fossil_free(zOrig); } blob_write_to_file(&t, zFullNewPath); file_setexe(zFullNewPath, isexe); } fossil_print("***** Cannot merge binary file %s", zNewName); if( !dryRunFlag ){ fossil_print(", original copy backed up locally"); } fossil_print("\n"); nConflict++; } } if( nameChng && !dryRunFlag ) file_delete(zFullPath); blob_reset(&v); blob_reset(&t); blob_reset(&r); }else{ nUpdate--; if( chnged ){ if( verboseFlag ) fossil_print("EDITED %s\n", zName); }else{ db_bind_int(&mtimeXfer, ":idv", idv); db_bind_int(&mtimeXfer, ":idt", idt); db_step(&mtimeXfer); db_reset(&mtimeXfer); if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName); } } free(zFullPath); free(zFullNewPath); } db_finalize(&q); db_finalize(&mtimeXfer); fossil_print("%.79c\n",'-'); if( nUpdate==0 ){ show_common_info(tid, "checkout:", 1, 0); fossil_print("%-13s None. Already up-to-date\n", "changes:"); }else{ fossil_print("%-13s %.40s %s\n", "updated-from:", rid_to_uuid(vid), db_text("", "SELECT datetime(mtime) || ' UTC' FROM event " " WHERE objid=%d", vid)); show_common_info(tid, "updated-to:", 1, 0); fossil_print("%-13s %d file%s modified.\n", "changes:", nUpdate, nUpdate>1 ? "s" : ""); } /* Report on conflicts */ if( !dryRunFlag ){ Stmt q; int nMerge = 0; db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0"); while( db_step(&q)==SQLITE_ROW ){ const char *zLabel = "merge"; switch( db_column_int(&q, 1) ){ case -1: zLabel = "cherrypick merge"; break; case -2: zLabel = "backout merge"; break; } fossil_warning("uncommitted %s against %S.", zLabel, db_column_text(&q, 0)); nMerge++; } db_finalize(&q); leaf_ambiguity_warning(tid, tid); 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{ char *zPwd; ensure_empty_dirs_created(1); sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, file_rmdir_sql_function, 0, 0); zPwd = file_getcwd(0,0); db_multi_exec( "SELECT rmdir(%Q||name) FROM dir_to_delete" " WHERE (%Q||name)<>%Q ORDER BY name DESC", g.zLocalRoot, g.zLocalRoot, zPwd ); fossil_free(zPwd); if( g.argc<=3 ){ /* All files updated. Shift the current check-out to the target. */ db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); checkout_set_all_exe(tid); manifest_to_disk(tid); db_set_checkout(tid); }else{ /* A subset of files have been checked out. Keep the current ** check-out unchanged. */ db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); } if( !internalUpdate ) undo_finish(); if( setmtimeFlag ) vfile_check_signature(tid, CKSIG_SETMTIME); db_end_transaction(0); } } /* ** Create empty directories specified by the empty-dirs setting. */ void ensure_empty_dirs_created(int clearDirTable){ char *zEmptyDirs = db_get("empty-dirs", 0); if( zEmptyDirs!=0 ){ int i; Glob *pGlob = glob_create(zEmptyDirs); for(i=0; pGlob!=0 && i<pGlob->nPattern; i++){ const char *zDir = pGlob->azPattern[i]; char *zPath = mprintf("%s/%s", g.zLocalRoot, zDir); switch( file_isdir(zPath, RepoFILE) ){ case 0: { /* doesn't exist */ fossil_free(zPath); zPath = mprintf("%s/%s/x", g.zLocalRoot, zDir); if( file_mkfolder(zPath, RepoFILE, 0, 1)!=0 ) { fossil_warning("couldn't create directory %s as " "required by empty-dirs setting", zDir); } break; } case 1: { /* exists, and is a directory */ /* make sure this directory is not on the delete list */ if( clearDirTable ){ db_multi_exec( "DELETE FROM dir_to_delete WHERE name=%Q", zDir ); } break; } case 2: { /* exists, but isn't a directory */ fossil_warning("file %s found, but a directory is required " "by empty-dirs setting", zDir); } } fossil_free(zPath); } glob_free(pGlob); } } /* ** Get the manifest record for a given revision, or the current check-out if ** zRevision is NULL. */ Manifest *historical_manifest( const char *zRevision /* The check-in to query, or NULL for current */ ){ int vid; Manifest *pManifest; /* Determine the check-in manifest artifact ID. Panic on failure. */ if( zRevision ){ vid = name_to_typed_rid(zRevision, "ci"); }else if( !g.localOpen ){ vid = name_to_typed_rid(db_get("main-branch", 0), "ci"); }else{ vid = db_lget_int("checkout", 0); if( !is_a_version(vid) ){ if( vid==0 ) return 0; zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); if( zRevision ){ fossil_fatal("check-out artifact is not a check-in: %s", zRevision); }else{ fossil_fatal("invalid check-out artifact ID: %d", vid); } } } /* Parse the manifest, given its artifact ID. Panic on failure. */ if( !(pManifest = manifest_get(vid, CFTYPE_MANIFEST, 0)) ){ if( zRevision ){ fossil_fatal("could not parse manifest for check-in: %s", zRevision); }else{ fossil_fatal("could not parse manifest for current check-out"); } } /* Return the manifest pointer. The caller must use manifest_destroy() to * clean up when finished using the manifest. */ return pManifest; } /* ** Get the contents of a file within the check-in "zRevision". If ** zRevision==NULL then get the file content for the current check-out. */ int historical_blob( const char *zRevision, /* The check-in containing the file */ const char *zFile, /* Full treename of the file */ Blob *pBlob, /* Put the content here */ int fatal /* If nonzero, panic if file/artifact not found */ ){ int result = 0; /* Get the manifest for the requested check-in version. This call unavoidably * panics on failure even if fatal is not set. */ Manifest *pManifest = historical_manifest(zRevision); /* Try to find the file record within the manifest. */ ManifestFile *pFile = manifest_file_find(pManifest, zFile); if( !pFile ){ /* Process file-not-found errors. */ if( fatal ){ if( zRevision ){ fossil_fatal("file %s does not exist in check-in %s", zFile, zRevision); }else{ fossil_fatal("no such file: %s", zFile); } } }else{ /* Get the file's contents. */ result = content_get(fast_uuid_to_rid(pFile->zUuid), pBlob); /* Process artifact-not-found errors. */ if( !result && fatal ){ if( zRevision ){ fossil_fatal("missing artifact %s for file %s in check-in %s", pFile->zUuid, zFile, zRevision); }else{ fossil_fatal("missing artifact %s for file %s", pFile->zUuid, zFile); } } } /* Deallocate the parsed manifest structure. */ manifest_destroy(pManifest); /* Return 1 on success and (assuming fatal is not set) 0 if not found. */ return result; } /* ** COMMAND: revert ** ** Usage: %fossil revert ?OPTIONS? ?FILE ...? ** ** Revert to the current repository version of FILE, or to ** the baseline VERSION specified with -r flag. ** ** If FILE was part of a rename operation, both the original file ** and the renamed file are reverted. ** ** Using a directory name for any of the FILE arguments is the same ** as using every subdirectory and file beneath that directory. ** ** Revert all files if no file name is provided. ** ** If a file is reverted accidentally, it can be restored using ** the "fossil undo" command. ** ** Options: ** -r|--revision VERSION Revert given FILE(s) back to given ** VERSION ** ** See also: [[redo]], [[undo]], [[checkout]], [[update]] */ void revert_cmd(void){ Manifest *pCoManifest; /* Manifest of current check-out */ Manifest *pRvManifest; /* Manifest of selected revert version */ ManifestFile *pCoFile; /* File within current check-out manifest */ ManifestFile *pRvFile; /* File within revert version manifest */ const char *zFile; /* Filename relative to check-out root */ const char *zRevision; /* Selected revert version, NULL if current */ Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */ int i; Stmt q; int revertAll = 0; int revisionOptNotSupported = 0; 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("directories or the entire tree can only be reverted" " back to current version"); } db_must_be_within_tree(); /* Get manifests of revert version and (if different) current check-out. */ pRvManifest = historical_manifest(zRevision); pCoManifest = zRevision ? historical_manifest(0) : 0; db_begin_transaction(); undo_begin(); db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);"); if( g.argc>2 ){ for(i=2; i<g.argc; i++){ Blob fname; zFile = mprintf("%/", g.argv[i]); blob_zero(&fname); file_tree_name(zFile, &fname, 0, 1); if( blob_eq(&fname, ".") ){ if( zRevision ){ revisionOptNotSupported = 1; break; } revertAll = 1; break; }else if( db_exists( "SELECT pathname" " FROM vfile" " WHERE (substr(pathname,1,length('%q/'))='%q/'" " OR substr(origname,1,length('%q/'))='%q/');", blob_str(&fname), blob_str(&fname), blob_str(&fname), blob_str(&fname)) ){ int vid; vid = db_lget_int("checkout", 0); vfile_check_signature(vid, 0); if( zRevision ){ revisionOptNotSupported = 1; break; } db_multi_exec( "INSERT OR IGNORE INTO torevert" " SELECT pathname" " FROM vfile" " WHERE (substr(pathname,1,length('%q/'))='%q/'" " OR substr(origname,1,length('%q/'))='%q/')" " AND (chnged OR deleted OR rid=0 OR pathname!=origname);", blob_str(&fname), blob_str(&fname), blob_str(&fname), blob_str(&fname) ); }else{ 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{ revertAll = 1; } if( revisionOptNotSupported ){ fossil_fatal("directories or the entire tree can only be reverted" " back to current version"); } if ( revertAll ){ 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 ){ char *zFull; zFile = db_column_text(&q, 0); zFull = mprintf("%/%/", g.zLocalRoot, zFile); pRvFile = pRvManifest? manifest_file_find(pRvManifest, zFile) : 0; if( !pRvFile ){ if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q", zFile, zFile)==0 ){ 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 if( file_unsafe_in_tree_path(zFull) ){ /* Ignore this file */ }else{ sqlite3_int64 mtime; int rvChnged = 0; int rvPerm = manifest_file_mperm(pRvFile); /* Determine if reverted-to file is different than checked-out file. */ if( pCoManifest && (pCoFile = manifest_file_find(pCoManifest, zFile)) ){ rvChnged = manifest_file_mperm(pRvFile)!=rvPerm || fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0; } /* Get contents of reverted-to file. */ content_get(fast_uuid_to_rid(pRvFile->zUuid), &record); undo_save(zFile); if( file_size(zFull, RepoFILE)>=0 && (rvPerm==PERM_LNK || file_islink(0)) ){ file_delete(zFull); } if( rvPerm==PERM_LNK ){ symlink_create(blob_str(&record), zFull); }else{ blob_write_to_file(&record, zFull); } file_setexe(zFull, rvPerm==PERM_EXE); fossil_print("REVERT %s\n", zFile); mtime = file_mtime(zFull, RepoFILE); db_multi_exec( "UPDATE vfile" " SET mtime=%lld, chnged=%d, deleted=0, isexe=%d, islink=%d," " mrid=rid, mhash=NULL" " WHERE pathname=%Q OR origname=%Q", mtime, rvChnged, rvPerm==PERM_EXE, rvPerm==PERM_LNK, zFile, zFile ); } blob_reset(&record); free(zFull); } db_finalize(&q); undo_finish(); db_end_transaction(0); /* Deallocate parsed manifest structures. */ manifest_destroy(pRvManifest); manifest_destroy(pCoManifest); } |
Changes to src/url.c.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 | ** ******************************************************************************* ** ** This file contains code for parsing URLs that appear on the command-line */ #include "config.h" #include "url.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 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 | ** ******************************************************************************* ** ** 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 0x0001 /* Prompt for password if needed */ #define URL_REMEMBER 0x0002 /* Remember the url for later reuse */ #define URL_ASK_REMEMBER_PW 0x0004 /* Ask whether to remember prompted pw */ #define URL_REMEMBER_PW 0x0008 /* Should remember pw */ #define URL_PROMPTED 0x0010 /* Prompted for PW already */ #define URL_OMIT_USER 0x0020 /* Omit the user name from URL */ #define URL_USE_CONFIG 0x0040 /* Use remembered URLs from CONFIG table */ #define URL_USE_PARENT 0x0080 /* Use the URL of the parent project */ #define URL_SSH_PATH 0x0100 /* Include PATH= on SSH syncs */ #define URL_SSH_RETRY 0x0200 /* This a retry of an SSH */ #define URL_SSH_EXE 0x0400 /* ssh: URL contains fossil= query param*/ /* ** 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 */ int isAlias; /* Input URL was an alias */ char *name; /* Hostname for http: or filename for file: */ char *hostname; /* The HOST: parameter on http headers */ const char *protocol; /* "http" or "https" or "ssh" or "file" */ 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: */ char *pwConfig; /* CONFIG table entry that gave us the password */ unsigned flags; /* Boolean flags controlling URL processing */ int useProxy; /* Used to remember that a proxy is in use */ int proxyOrigPort; /* Tunneled port number for https through proxy */ char *proxyUrlPath; /* Remember path when proxy is use */ char *proxyUrlCanonical; /* Remember canonical path when proxy is use */ }; #endif /* INTERFACE */ /* ** Parse the URL in the zUrl argument. Store results in the pUrlData object. ** Populate members of pUrlData as follows: ** ** isFile True if FILE: ** isHttps True if HTTPS: ** isSsh True if SSH: ** protocol "http" or "https" or "file" or "ssh" ** 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 ** ** If URL_USECONFIG is set and zUrl is NULL or "default", then parse the ** URL stored in last-sync-url and last-sync-pw of the CONFIG table. Or if ** URL_USE_PARENT is also set, then use parent-project-url and ** parent-project-pw from the CONFIG table instead of last-sync-url ** and last-sync-pw. ** ** If URL_USE_CONFIG is set and zUrl is a symbolic name, then look up ** the URL in sync-url:%Q and sync-pw:%Q elements of the CONFIG table where ** %Q is the symbolic name. ** ** This routine differs from url_parse() in that this routine stores the ** results in pUrlData and does not change the values of global variables. ** The url_parse() routine puts its result in g.url. */ void url_parse_local( const char *zUrl, unsigned int urlFlags, UrlData *pUrlData ){ int i, j, c; char *zFile = 0; memset(pUrlData, 0, sizeof(*pUrlData)); if( urlFlags & URL_USE_CONFIG ){ if( zUrl==0 || strcmp(zUrl,"default")==0 ){ const char *zPwConfig = "last-sync-pw"; if( urlFlags & URL_USE_PARENT ){ zUrl = db_get("parent-project-url", 0); if( zUrl==0 ){ zUrl = db_get("last-sync-url",0); }else{ zPwConfig = "parent-project-pw"; } }else{ zUrl = db_get("last-sync-url", 0); } if( zUrl==0 ) return; if( pUrlData->passwd==0 ){ pUrlData->passwd = unobscure(db_get(zPwConfig, 0)); pUrlData->pwConfig = fossil_strdup(zPwConfig); } pUrlData->isAlias = 1; }else{ char *zKey = sqlite3_mprintf("sync-url:%q", zUrl); char *zAlt = db_get(zKey, 0); if( zAlt ){ pUrlData->pwConfig = mprintf("sync-pw:%q", zUrl); pUrlData->passwd = unobscure( db_text(0, "SELECT value FROM config WHERE name='sync-pw:%q'",zUrl) ); zUrl = zAlt; urlFlags |= URL_REMEMBER_PW; pUrlData->isAlias = 1; }else{ pUrlData->isAlias = 0; } sqlite3_free(zKey); } }else{ if( zUrl==0 ) return; } if( strncmp(zUrl, "http://", 7)==0 || strncmp(zUrl, "https://", 8)==0 || strncmp(zUrl, "ssh://", 6)==0 ){ int iStart; char *zLogin; char *zExe; char cQuerySep = '?'; 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_strdup("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; } if( urlFlags & URL_OMIT_USER ){ zLogin = mprintf(""); }else{ 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(""); } fossil_strtolwr(pUrlData->name); if( c==':' ){ pUrlData->port = 0; i++; while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ pUrlData->port = pUrlData->port*10 + c - '0'; i++; } if( c!=0 && c!='/' ) fossil_fatal("url missing '/' after port number"); 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 ){ fossil_free(pUrlData->fossil); pUrlData->fossil = fossil_strdup(zValue); dehttpize(pUrlData->fossil); fossil_free(zExe); zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil); cQuerySep = '&'; urlFlags |= URL_SSH_EXE; } } dehttpize(pUrlData->path); if( pUrlData->dfltPort==pUrlData->port ){ pUrlData->canonical = mprintf( "%s://%s%T%T%z", pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe ); }else{ pUrlData->canonical = mprintf( "%s://%s%T:%d%T%z", pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port, pUrlData->path, zExe ); } if( pUrlData->isSsh && pUrlData->path[1] ){ char *zOld = pUrlData->path; pUrlData->path = mprintf("%s", zOld+1); fossil_free(zOld); } 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, ExtFILE) ){ pUrlData->isFile = 1; zFile = mprintf("%s", zUrl); }else if( file_isdir(zUrl, ExtFILE)==1 ){ zFile = mprintf("%s/FOSSIL", zUrl); if( file_isfile(zFile, ExtFILE) ){ 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 = mprintf(""); 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)!=0 ){ url_prompt_for_password_local(pUrlData); }else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ if( isatty(fileno(stdin)) && ( urlFlags & URL_REMEMBER_PW )==0 ){ if( save_password_prompt(pUrlData->passwd) ){ pUrlData->flags = urlFlags |= URL_REMEMBER_PW; }else{ pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW; } } } } /* ** Construct the complete URL for a UrlData object, including the ** login name and password, into memory obtained from fossil_malloc() ** and return a pointer to that URL text. */ char *url_full(const UrlData *p){ Blob x = BLOB_INITIALIZER; if( p->isFile || p->user==0 || p->user[0]==0 ){ return fossil_strdup(p->canonical); } blob_appendf(&x, "%s://", p->protocol); if( p->user && p->user[0] ){ blob_appendf(&x, "%t", p->user); if( p->passwd && p->passwd[0] ){ blob_appendf(&x, ":%t", p->passwd); } blob_appendf(&x, "@"); } blob_appendf(&x, "%T", p->name); if( p->dfltPort!=p->port ){ blob_appendf(&x, ":%d", p->port); } blob_appendf(&x, "%T", p->path); (void)blob_str(&x); return x.aData; } /* ** Construct a URL for a UrlData object that omits the ** login name and password, into memory obtained from fossil_malloc() ** and return a pointer to that URL text. */ char *url_nouser(const UrlData *p){ Blob x = BLOB_INITIALIZER; if( p->isFile || p->user==0 || p->user[0]==0 ){ return fossil_strdup(p->canonical); } blob_appendf(&x, "%s://", p->protocol); blob_appendf(&x, "%T", p->name); if( p->dfltPort!=p->port ){ blob_appendf(&x, ":%d", p->port); } blob_appendf(&x, "%T", p->path); (void)blob_str(&x); return x.aData; } /* ** SQL function to remove the username/password from a URL */ void url_nouser_func( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zOrig = (const char*)sqlite3_value_text(argv[0]); UrlData x; if( zOrig==0 ) return; memset(&x, 0, sizeof(x)); url_parse_local(zOrig, URL_OMIT_USER, &x); sqlite3_result_text(context, x.canonical, -1, SQLITE_TRANSIENT); url_unparse(&x); } /* ** Reclaim malloced memory from a UrlData object */ void url_unparse(UrlData *p){ if( p==0 ){ p = &g.url; } fossil_free(p->canonical); fossil_free(p->name); fossil_free(p->path); fossil_free(p->user); fossil_free(p->passwd); fossil_free(p->fossil); fossil_free(p->pwConfig); memset(p, 0, sizeof(*p)); } /* ** Move a URL parse from one UrlData object to another. */ void url_move_parse(UrlData *pTo, UrlData *pFrom){ url_unparse(pTo); memcpy(pTo, pFrom, sizeof(*pTo)); memset(pFrom, 0, sizeof(*pFrom)); } /* ** Parse the given URL, which describes a sync server. Populate variables ** in the global "g.url" structure as shown below. If zUrl is NULL, then ** parse the URL given in the last-sync-url setting, taking the password ** form last-sync-pw. ** ** 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" or "ssh" ** 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 ** g.url.pwConfig Name of CONFIG table entry containing 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 ** ** If URL_USE_CONFIG is set then the URL and password might be pulled from ** the CONFIG table rather than from the zUrl parameter. If zUrl is NULL ** or "default" then the URL is given by the "last-sync-url" setting and ** the password comes form the "last-sync-pw" setting. If zUrl is a symbolic ** name, then the URL comes from "sync-url:NAME" and the password from ** "sync-pw:NAME" where NAME is the input zUrl string. Whenever the ** password is taken from the CONFIG table, the g.url.pwConfig field is ** set to the CONFIG.NAME value from which that password is taken. Otherwise, ** g.url.pwConfig is NULL. */ void url_parse(const char *zUrl, unsigned int urlFlags){ url_parse_local(zUrl, urlFlags, &g.url); } /* ** Print the content of g.url */ void urlparse_print(int showPw){ 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); if( showPw || g.url.pwConfig==0 ){ fossil_print("g.url.passwd = %s\n", g.url.passwd); }else{ fossil_print("g.url.passwd = ************\n"); } fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig); 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%04x\n", g.url.flags); fossil_print("url_full(g.url) = %z\n", url_full(&g.url)); } /* ** COMMAND: test-urlparser ** ** Usage: %fossil test-urlparser URL ?options? ** ** --prompt-pw Prompt for password if missing ** --remember Store results in last-sync-url ** --show-pw Show the CONFIG-derived password in the output ** --use-config Pull URL and password from the CONFIG table ** --use-parent Use the parent project URL */ void cmd_test_urlparser(void){ int i; unsigned fg = 0; int showPw = 0; db_must_be_within_tree(); url_proxy_options(); if( find_option("remember",0,0) ) fg |= URL_REMEMBER; if( find_option("prompt-pw",0,0) ) fg |= URL_PROMPT_PW; if( find_option("use-parent",0,0) ) fg |= URL_USE_PARENT|URL_USE_CONFIG; if( find_option("use-config",0,0) ) fg |= URL_USE_CONFIG; if( find_option("show-pw",0,0) ) showPw = 1; if( (fg & URL_USE_CONFIG)==0 ) showPw = 1; if( g.argc!=3 && g.argc!=4 ){ usage("URL"); } url_parse(g.argv[2], fg); for(i=0; i<2; i++){ urlparse_print(showPw); if( g.url.isFile || g.url.isSsh ) break; if( i==0 ){ fossil_print("********\n"); url_enable_proxy("Using proxy: "); } url_unparse(0); } } /* ** Proxy specified on the command-line using the --proxy option. ** If there is no --proxy option on the command-line then this ** variable holds a NULL pointer. */ static const char *zProxyOpt = 0; /* ** Extract any proxy options from the command-line. ** ** --proxy URL|off ** ** The original purpose of this routine is the above. But this ** also happens to be a convenient place to look for other ** network-related options: ** ** --nosync Temporarily disable "autosync" ** ** --ipv4 Disallow IPv6. Use only IPv4. ** ** --accept-any-cert Disable server SSL cert validation. Accept ** any SSL cert that the server provides. ** WARNING: this option opens you up to ** forged-DNS and man-in-the-middle attacks! */ 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; #ifdef FOSSIL_ENABLE_SSL if( find_option("accept-any-cert",0,0) ){ ssl_disable_cert_verification(); } #endif /* FOSSIL_ENABLE_SSL */ } /* ** 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", "system"); if( fossil_strcmp(zProxy, "system")==0 ){ 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.proxyUrlCanonical = zOriginalUrl;; 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((void *)p->azName); fossil_free((void *)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((void *)p->azName, sizeof(p->azName[0])*p->nAlloc); p->azValue = fossil_realloc((void *)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, "%R/%s", 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; if( z==0 ) continue; } if( zName2 && fossil_strcmp(zName2,p->azName[i])==0 ){ zName2 = 0; z = zValue2; if( z==0 ) continue; } blob_appendf(&p->url, "%s%s", zSep, p->azName[i]); if( z && z[0] ) blob_appendf(&p->url, "=%T", z); zSep = "&"; } if( zName1 && zValue1 ){ blob_appendf(&p->url, "%s%s", zSep, zName1); if( zValue1[0] ) blob_appendf(&p->url, "=%T", zValue1); zSep = "&"; } if( zName2 && zValue2 ){ 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->canonical); 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 ){ const char *url; if( g.url.useProxy ){ url = g.url.proxyUrlCanonical; }else{ url = g.url.canonical; } if( g.url.flags & URL_USE_PARENT ){ db_set("parent-project-url", url, 0); }else{ db_set("last-sync-url", url, 0); } if( g.url.user!=0 && g.url.passwd!=0 && ( g.url.flags & URL_REMEMBER_PW ) ){ if( g.url.flags & URL_USE_PARENT ){ db_set("parent-project-pw", obscure(g.url.passwd), 0); }else{ 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(); } } /* ** Given a URL for a remote repository clone point, try to come up with a ** reasonable basename of a local clone of that repository. ** ** * If the URL has a path, use the tail of the path, with any suffix ** elided. ** ** * If the URL is just a domain name, without a path, then use the ** first element of the domain name, except skip over "www." if ** present and if there is a ".com" or ".org" or similar suffix. ** ** The string returned is obtained from fossil_malloc(). NULL might be ** returned if there is an error. */ char *url_to_repo_basename(const char *zUrl){ const char *zTail = 0; int i; if( zUrl==0 ) return 0; for(i=0; zUrl[i]; i++){ if( zUrl[i]=='?' ) break; if( (zUrl[i]=='/' || zUrl[i]=='@') && zUrl[i+1]!=0 ) zTail = &zUrl[i+1]; } if( zTail==0 ) return 0; if( sqlite3_strnicmp(zTail, "www.", 4)==0 && strchr(zTail+4,'.')!=0 ){ /* Remove the "www." prefix if there are more "." characters later. ** But don't remove the "www." prefix if what follows is the suffix. ** forum:/forumpost/74e111a2ee */ zTail += 4; } if( zTail[0]==0 ) return 0; for(i=0; zTail[i] && zTail[i]!='.' && zTail[i]!='?' && zTail[i]!=':' && zTail[i]!='/'; i++){} if( i==0 ) return 0; return mprintf("%.*s", i, zTail); } /* ** COMMAND: test-url-basename ** Usage: %fossil test-url-basenames URL ... ** ** This command is used for unit testing of the url_to_repo_basename() ** routine. The command-line arguments are URL, presumably for remote ** Fossil repositories. This command runs url_to_repo_basename() on each ** of those inputs and displays the result. */ void cmd_test_url_basename(void){ int i; char *z; for(i=2; i<g.argc; i++){ z = url_to_repo_basename(g.argv[i]); fossil_print("%s -> %s\n", g.argv[i], z); fossil_free(z); } } |
Changes to src/user.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 | ** ** Commands and procedures used for creating, processing, editing, and ** querying information about users. */ #include "config.h" #include "user.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 | ** ** Commands and procedures used for creating, processing, editing, and ** querying information about users. */ #include "config.h" #include "user.h" /* ** Strip leading and trailing space from a string and add the string ** onto the end of a blob. */ static void strip_string(Blob *pBlob, char *z){ int i; blob_reset(pBlob); 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__) && !defined(FOSSIL_HAVE_GETPASS)) #ifdef _WIN32 #include <conio.h> #endif /* ** getpass() for Windows and Android. */ static char *zPwdBuffer = 0; static size_t nPwdBuffer = 0; static char *getpass(const char *prompt){ char *zPwd; size_t nPwd; size_t i; #if defined(_WIN32) int useGetch = _isatty(_fileno(stderr)); #endif if( zPwdBuffer==0 ){ zPwdBuffer = fossil_secure_alloc_page(&nPwdBuffer); assert( zPwdBuffer ); }else{ fossil_secure_zero(zPwdBuffer, nPwdBuffer); } zPwd = zPwdBuffer; nPwd = nPwdBuffer; fputs(prompt,stderr); fflush(stderr); assert( zPwd!=0 ); assert( nPwd>0 ); for(i=0; i<nPwd-1; ++i){ #if defined(_WIN32) zPwd[i] = useGetch ? _getch() : getc(stdin); #else zPwd[i] = getc(stdin); #endif if(zPwd[i]=='\r' || zPwd[i]=='\n'){ break; } /* BS or DEL */ else if(i>0 && (zPwd[i]==8 || zPwd[i]==127)){ i -= 2; continue; } /* CTRL-C */ else if(zPwd[i]==3) { i=0; break; } /* ESC */ else if(zPwd[i]==27){ i=0; break; } else{ #if defined(_WIN32) if( useGetch ) #endif fputc('*',stderr); } } zPwd[i]='\0'; fputs("\n", stderr); assert( zPwd==zPwdBuffer ); return zPwd; } void freepass(){ if( !zPwdBuffer ) return; assert( nPwdBuffer>0 ); fossil_secure_free_page(zPwdBuffer, nPwdBuffer); } #endif /* ** Scramble substitution matrix: */ static char aSubst[256]; /* ** Descramble the password */ static void userDescramble(char *z){ int i; for(i=0; z[i]; i++) z[i] = aSubst[(unsigned char)z[i]]; } /* Print a string in 5-letter groups */ static void printFive(const unsigned char *z){ int i; for(i=0; z[i]; i++){ if( i>0 && (i%5)==0 ) putchar(' '); putchar(z[i]); } putchar('\n'); } /* Return a pseudo-random integer between 0 and N-1 */ static int randint(int N){ unsigned char x; assert( N<256 ); sqlite3_randomness(1, &x); return x % N; } /* ** Generate and print a random scrambling of letters a through z (omitting x) ** and set up the aSubst[] matrix to descramble. */ static void userGenerateScrambleCode(void){ unsigned char zOrig[30]; unsigned char zA[30]; unsigned char zB[30]; int nA = 25; int nB = 0; int i; memcpy(zOrig, "abcdefghijklmnopqrstuvwyz", nA+1); memcpy(zA, zOrig, nA+1); assert( nA==(int)strlen((char*)zA) ); for(i=0; i<(int)sizeof(aSubst); i++) aSubst[i] = i; printFive(zA); while( nA>0 ){ int x = randint(nA); zB[nB++] = zA[x]; zA[x] = zA[--nA]; } assert( nB==25 ); zB[nB] = 0; printFive(zB); for(i=0; i<nB; i++) aSubst[zB[i]] = zOrig[i]; } /* ** Return the value of the FOSSIL_SECURITY_LEVEL environment variable. ** Or return 0 if that variable does not exist. */ int fossil_security_level(void){ const char *zLevel = fossil_getenv("FOSSIL_SECURITY_LEVEL"); if( zLevel==0 ) return 0; return atoi(zLevel); } /* ** Do a single prompt for a passphrase. Store the results in the blob. ** ** ** The return value is a pointer to a static buffer that is overwritten ** on subsequent calls to this same routine. */ static void prompt_for_passphrase(const char *zPrompt, Blob *pPassphrase){ char *z; #if 0 */ ** If the FOSSIL_PWREADER environment variable is set, then it will ** be the name of a program that prompts the user for their password/ ** passphrase in a secure manner. The program should take one or more ** arguments which are the prompts and should output the acquired ** passphrase as a single line on stdout. This function will read the ** output using popen(). ** ** If FOSSIL_PWREADER is not set, or if it is not the name of an ** executable, then use the C-library getpass() routine. */ const char *zProg = fossil_getenv("FOSSIL_PWREADER"); if( zProg && zProg[0] ){ static char zPass[100]; Blob cmd; FILE *in; blob_zero(&cmd); blob_appendf(&cmd, "%s \"Fossil Passphrase\" \"%s\"", zProg, zPrompt); zPass[0] = 0; in = popen(blob_str(&cmd), "r"); fgets(zPass, sizeof(zPass), in); pclose(in); blob_reset(&cmd); z = zPass; }else #endif if( fossil_security_level()>=2 ){ userGenerateScrambleCode(); z = getpass(zPrompt); if( z ) userDescramble(z); printf("\033[3A\033[J"); /* Erase previous three lines */ fflush(stdout); }else{ z = getpass(zPrompt); } strip_string(pPassphrase, z); } /* ** Prompt the user for a password. Store the result in the pPassphrase ** blob. ** |
︙ | ︙ | |||
110 111 112 113 114 115 116 | Blob secondTry; blob_zero(pPassphrase); blob_zero(&secondTry); while(1){ prompt_for_passphrase(zPrompt, pPassphrase); if( verify==0 ) break; if( verify==1 && blob_size(pPassphrase)==0 ) break; | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > < | | | > > > > | | > | | | | | > | > | | | > < > | | > | | | | > | > > | > > > > > > > > > > > > > > > > > > > | | > | | | | 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 | Blob secondTry; blob_zero(pPassphrase); blob_zero(&secondTry); while(1){ prompt_for_passphrase(zPrompt, pPassphrase); if( verify==0 ) break; if( verify==1 && blob_size(pPassphrase)==0 ) break; prompt_for_passphrase("Retype new password: ", &secondTry); if( blob_compare(pPassphrase, &secondTry) ){ 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; if( fossil_security_level()>=1 ) 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]; blob_zero(pIn); fossil_force_newline(); fossil_print("%s", zPrompt); fflush(stdout); z = fgets(zLine, sizeof(zLine), stdin); if( z ){ int n = (int)strlen(z); if( n>0 && z[n-1]=='\n' ) fossil_new_line_started(); strip_string(pIn, z); } } /* ** COMMAND: user* ** ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository REPO? ** ** Run various subcommands on users of the open repository or of ** the repository identified by the -R or --repository option. ** ** > fossil user capabilities USERNAME ?STRING? ** ** Query or set the capabilities for user USERNAME ** ** > fossil user contact USERNAME ?CONTACT-INFO? ** ** Query or set contact information for user USERNAME ** ** > 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 ** to exist in the database. ** ** > fossil user password USERNAME ?PASSWORD? ** ** Change the web access password for a user. */ void user_cmd(void){ int n; db_find_and_open_repository(0, 0); if( g.argc<3 ){ usage("capabilities|contact|default|list|new|password ..."); } n = strlen(g.argv[2]); if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){ Blob passwd, login, caps, contact; char *zPw; blob_init(&caps, db_get("default-perms", 0), -1); if( g.argc>=4 ){ blob_init(&login, g.argv[3], -1); }else{ prompt_user("login: ", &login); } if( db_exists("SELECT 1 FROM user WHERE login=%B", &login) ){ fossil_fatal("user %b already exists", &login); } if( g.argc>=5 ){ blob_init(&contact, g.argv[4], -1); }else{ prompt_user("contact-info: ", &contact); } if( g.argc>=6 ){ blob_init(&passwd, g.argv[5], -1); }else{ prompt_for_password("password: ", &passwd, 1); } zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0); db_unprotect(PROTECT_USER); db_multi_exec( "INSERT INTO user(login,pw,cap,info,mtime)" "VALUES(%B,%Q,%B,%B,now())", &login, zPw, &caps, &contact ); db_protect_pop(); 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 ){ char *zPrompt; int uid; Blob pw; if( g.argc!=4 && g.argc!=5 ) usage("password USERNAME ?NEW-PASSWORD?"); 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 ){ blob_init(&pw, g.argv[4], -1); }else{ zPrompt = mprintf("New password for %s: ", g.argv[3]); prompt_for_password(zPrompt, &pw, 1); } if( blob_size(&pw)==0 ){ fossil_print("password unchanged\n"); }else{ char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0); db_unprotect(PROTECT_USER); db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d", zSecret, uid); db_protect_pop(); 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_unprotect(PROTECT_USER); db_multi_exec( "UPDATE user SET cap=%Q, mtime=now() WHERE uid=%d", g.argv[4], uid ); db_protect_pop(); } fossil_print("%s\n", db_text(0, "SELECT cap FROM user WHERE uid=%d", uid)); }else if( n>=2 && strncmp(g.argv[2], "contact", 2)==0 ){ int uid; if( g.argc!=4 && g.argc!=5 ){ usage("contact USERNAME ?CONTACT-INFO?"); } 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_unprotect(PROTECT_USER); db_multi_exec( "UPDATE user SET info=%Q, mtime=now() WHERE uid=%d", g.argv[4], uid ); db_protect_pop(); } fossil_print("%s\n", db_text(0, "SELECT info FROM user WHERE uid=%d", uid)); }else{ fossil_fatal("user subcommand should be one of: " "capabilities contact default list new password"); } } /* ** Attempt to set the user to zLogin */ static int attempt_user(const char *zLogin){ |
︙ | ︙ | |||
295 296 297 298 299 300 301 | ** ** (1) Use the --user and -U command-line options. ** ** (2) If the local database is open, check in VVAR. ** ** (3) Check the default user in the repository ** | > > | > > | > > < | > | > > > > > > > | > | > | > > > | > > > > < | < | | > > > | > | < < > | < < | | | | > > | | | > > | > | | > | | > | > > > | < < < < | < | < < < < > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** ** (1) Use the --user and -U command-line options. ** ** (2) If the local database is open, check in VVAR. ** ** (3) Check the default user in the repository ** ** (4) Try the FOSSIL_USER environment variable. ** ** (5) Try the USER environment variable. ** ** (6) Try the LOGNAME environment variable. ** ** (7) Try the USERNAME environment variable. ** ** (8) Check if the user can be extracted from the remote URL. ** ** The user name is stored in g.zLogin. The uid is in g.userUid. */ void user_select(void){ UrlData url; if( g.userUid ) return; if( g.zLogin ){ if( attempt_user(g.zLogin)==0 ){ fossil_fatal("no such user: %s", g.zLogin); }else{ return; } } if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return; if( attempt_user(db_get("default-user", 0)) ) return; if( attempt_user(fossil_getenv("FOSSIL_USER")) ) return; if( attempt_user(fossil_getenv("USER")) ) return; if( attempt_user(fossil_getenv("LOGNAME")) ) return; if( attempt_user(fossil_getenv("USERNAME")) ) return; memset(&url, 0, sizeof(url)); url_parse_local(0, URL_USE_CONFIG, &url); if( url.user && attempt_user(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"); } /* ** COMMAND: test-usernames ** ** Usage: %fossil test-usernames ** ** Print details about sources of fossil usernames. */ void test_usernames_cmd(void){ db_find_and_open_repository(0, 0); fossil_print("Initial g.zLogin: %s\n", g.zLogin); fossil_print("Initial g.userUid: %d\n", g.userUid); fossil_print("check-out default-user: %s\n", g.localOpen ? db_lget("default-user","") : "<<no open check-out>>"); fossil_print("default-user: %s\n", db_get("default-user","")); fossil_print("FOSSIL_USER: %s\n", fossil_getenv("FOSSIL_USER")); fossil_print("USER: %s\n", fossil_getenv("USER")); fossil_print("LOGNAME: %s\n", fossil_getenv("LOGNAME")); fossil_print("USERNAME: %s\n", fossil_getenv("USERNAME")); url_parse(0, URL_USE_CONFIG); fossil_print("URL user: %s\n", g.url.user); user_select(); fossil_print("Final g.zLogin: %s\n", g.zLogin); fossil_print("Final g.userUid: %d\n", g.userUid); } /* ** Make sure the USER table is up-to-date. It should contain ** the "JX" column (as of version 2.21). If it does not, add it. ** ** The "JX" column is intended to hold a JSON object containing optional ** key-value pairs. */ void user_update_user_table(void){ if( db_table_has_column("repository","user","jx")==0 ){ db_multi_exec("ALTER TABLE repository.user" " ADD COLUMN jx TEXT DEFAULT '{}';"); } } /* ** COMMAND: test-hash-passwords ** ** Usage: %fossil test-hash-passwords REPOSITORY ** ** Convert all local password storage to use a SHA1 hash of the password ** rather than cleartext. Passwords that are already stored as the SHA1 ** has are unchanged. */ void user_hash_passwords_cmd(void){ if( g.argc!=3 ) usage("REPOSITORY"); db_open_repository(g.argv[2]); sqlite3_create_function(g.db, "shared_secret", 2, SQLITE_UTF8, 0, sha1_shared_secret_sql_function, 0, 0); db_unprotect(PROTECT_ALL); db_multi_exec( "UPDATE user SET pw=shared_secret(pw,login), mtime=now()" " WHERE length(pw)>0 AND length(pw)!=40" ); } /* ** Ensure that the password for a user is hashed. */ void hash_user_password(const char *zUser){ sqlite3_create_function(g.db, "shared_secret", 2, SQLITE_UTF8, 0, sha1_shared_secret_sql_function, 0, 0); db_unprotect(PROTECT_USER); db_multi_exec( "UPDATE user SET pw=shared_secret(pw,login), mtime=now()" " WHERE login=%Q AND length(pw)>0 AND length(pw)!=40", zUser ); db_protect_pop(); } /* ** COMMAND: test-prompt-user ** ** Usage: %fossil test-prompt-user PROMPT ** ** Prompts the user for input and then prints it verbatim (i.e. without ** a trailing line terminator). */ void test_prompt_user_cmd(void){ Blob answer; if( g.argc!=3 ) usage("PROMPT"); prompt_user(g.argv[2], &answer); fossil_print("%s\n", blob_str(&answer)); } /* ** COMMAND: test-prompt-password ** ** Usage: %fossil test-prompt-password PROMPT VERIFY ** ** Prompts the user for a password and then prints it verbatim. ** ** Behavior is controlled by the VERIFY parameter: ** ** 0 Just ask once. ** ** 1 If the first answer is a non-empty string, ask for ** verification. Repeat if the two strings do not match. ** ** 2 Ask twice, repeat if the strings do not match. */ void test_prompt_password_cmd(void){ Blob answer; int iVerify = 0; if( g.argc!=4 ) usage("PROMPT VERIFY"); iVerify = atoi(g.argv[3]); prompt_for_password(g.argv[2], &answer, iVerify); fossil_print("[%s]\n", blob_str(&answer)); } /* ** WEBPAGE: access_log ** WEBPAGE: user_log ** ** Show login attempts, including timestamp and IP address. ** Requires Admin privileges. ** ** Query parameters: ** ** y=N 1: success only. 2: failure only. 3: both (default: 3) ** n=N Number of entries to show (default: 200) ** o=N Skip this many entries (default: 0) */ void user_log_page(void){ int y = atoi(PD("y","3")); int n = atoi(PD("n","200")); int skip = atoi(PD("o","0")); const char *zUser = P("u"); Blob sql; Stmt q; int cnt = 0; int rc; int fLogEnabled; 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("%R/user_log?y=%d&n=%d&o=%o", y, n, skip); return; } if( P("delanon") && P("delanonbtn") ){ db_multi_exec("DELETE FROM accesslog WHERE uname='anonymous'"); cgi_redirectf("%R/user_log?y=%d&n=%d&o=%o", y, n, skip); return; } if( P("delfail") && P("delfailbtn") ){ db_multi_exec("DELETE FROM accesslog WHERE NOT success"); cgi_redirectf("%R/user_log?y=%d&n=%d&o=%o", y, n, skip); return; } if( P("delold") && P("deloldbtn") ){ db_multi_exec("DELETE FROM accesslog WHERE rowid in" "(SELECT rowid FROM accesslog ORDER BY rowid DESC" " LIMIT -1 OFFSET 200)"); cgi_redirectf("%R/user_log?y=%d&n=%d", y, n); return; } style_header("User Log"); style_submenu_element("Log-Menu", "setup-logmenu"); blob_zero(&sql); blob_append_sql(&sql, "SELECT uname, ipaddr, datetime(mtime,toLocal()), success" " FROM accesslog" ); if( zUser ){ blob_append_sql(&sql, " WHERE uname=%Q", zUser); n = 1000000000; skip = 0; }else 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", "%R/user_log?o=%d&n=%d&y=%d", skip>=n ? skip-n : 0, n, y); } rc = db_prepare_ignore_error(&q, "%s", blob_sql_text(&sql)); fLogEnabled = db_get_boolean("access-log", 0); @ <div align="center">User logging is %s(fLogEnabled?"on":"off"). @ (Change this on the <a href="setup_settings">settings</a> page.)</div> @ <table border="1" cellpadding="5" class="sortable" align="center" \ @ data-column-types='Ttt' data-init-sort='1'> @ <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 ){ style_submenu_element("Older", "%R/user_log?o=%d&n=%d&y=%d", skip+n, n, y); break; } if( bSuccess ){ @ <tr> }else{ @ <tr bgcolor="#ffacc0"> } @ <td>%s(zDate)</td><td>%h(zName)</td><td>%h(zIP)</td></tr> } if( skip>0 || cnt>n ){ style_submenu_element("All", "%R/user_log?n=10000000"); } @ </tbody></table> db_finalize(&q); @ <hr> @ <form method="post" action="%R/user_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> @ <form method="post" action="%R/user_log"> @ <label><input type="checkbox" name="delanon"> @ Delete all entries for user "anonymous"</input></label> @ <input type="submit" name="delanonbtn" value="Delete"></input> @ </form> @ <form method="post" action="%R/user_log"> @ <label><input type="checkbox" name="delfail"> @ Delete all failed login attempts</input></label> @ <input type="submit" name="delfailbtn" value="Delete"></input> @ </form> @ <form method="post" action="%R/user_log"> @ <label><input type="checkbox" name="delall"> @ Delete all entries</input></label> @ <input type="submit" name="delallbtn" value="Delete"></input> @ </form> style_table_sorter(); style_finish_page(); } |
Added src/useredit.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* This script accompanies the /setup_uedit web page. Its job is to keep ** the check-boxes with user capabilities up-to-date with the capability ** string. ** ** The capability string is stored in #usetupEditCapability */ function updateCapabilityString(){ 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 e.onchange = updateCapabilityString; 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 */ } } updateCapabilityString(); |
Added src/utf8.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 | /* ** Copyright (c) 2012 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 utilities for converting text between UTF-8 (which ** is always used internally) and whatever encodings are used by the underlying ** filesystem and operating system. */ #include "config.h" #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*); return sqlite3_win32_mbcs_to_utf8(zMbcs); } /* ** After translating from UTF-8 to MBCS, invoke this routine to deallocate ** any memory used to hold the translation */ void fossil_mbcs_free(char *zOld){ sqlite3_free(zOld); } #endif /* _WIN32 */ /* ** Translate Unicode text into UTF-8. ** 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() or fossil_utf8_to_unicode(). */ void fossil_unicode_free(void *pOld){ fossil_free(pOld); } #if defined(__APPLE__) && !defined(WITHOUT_ICONV) # include <iconv.h> #endif /* ** Translate text from the filename character set into UTF-8. ** Return a pointer to the translated text. ** Call fossil_path_free() to deallocate any memory used to store the ** returned pointer when done. ** ** This function must not convert '\' to '/' on windows/cygwin, as it is ** used in places where we are not sure it's really filenames we are handling, ** e.g. fossil_getenv() or handling the argv arguments from main(). ** ** On Windows, translate some characters in the in the range ** U+F001 - U+F07F (private use area) to ASCII. Cygwin sometimes ** generates such filenames. See: ** <http://cygwin.com/cygwin-ug-net/using-specialnames.html> */ char *fossil_path_to_utf8(const void *zPath){ #if defined(_WIN32) int nByte = WideCharToMultiByte(CP_UTF8, 0, zPath, -1, 0, 0, 0, 0); char *zUtf = sqlite3_malloc( nByte ); char *pUtf, *qUtf; if( zUtf==0 ){ return 0; } WideCharToMultiByte(CP_UTF8, 0, zPath, -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; #elif defined(__CYGWIN__) char *zOut; zOut = fossil_strdup(zPath); return zOut; #elif defined(__APPLE__) && !defined(WITHOUT_ICONV) char *zIn = (char*)zPath; char *zOut; iconv_t cd; size_t n, x; for(n=0; zIn[n]>0 && zIn[n]<=0x7f; n++){} if( zIn[n]!=0 && (cd = iconv_open("UTF-8", "UTF-8-MAC"))!=(iconv_t)-1 ){ char *zOutx; char *zOrig = zIn; size_t nIn, nOutx; nIn = n = strlen(zIn); nOutx = nIn+100; zOutx = zOut = fossil_malloc( nOutx+1 ); x = iconv(cd, &zIn, &nIn, &zOutx, &nOutx); if( x==(size_t)-1 ){ fossil_free(zOut); zOut = fossil_strdup(zOrig); }else{ zOut[n+100-nOutx] = 0; } iconv_close(cd); }else{ zOut = fossil_strdup(zPath); } return zOut; #else return (char *)zPath; /* No-op on non-mac unix */ #endif } /* ** Translate text from UTF-8 to the filename character set. ** Return a pointer to the translated text. ** Call fossil_path_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_path(const char *zUtf8, int isDir){ #ifdef _WIN32 int nReserved = isDir ? 12 : 0; /* For dir, need room for "FILENAME.EXT" */ 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+nReserved)>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+nReserved)>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; #elif defined(__CYGWIN__) char *zPath, *p; if( fossil_isalpha(zUtf8[0]) && (zUtf8[1]==':') && (zUtf8[2]=='\\' || zUtf8[2]=='/')) { /* win32 absolute path starting with drive specifier. */ int nByte; wchar_t zUnicode[2000]; wchar_t *wUnicode = zUnicode; MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, count(zUnicode)); while( *wUnicode != '\0' ){ if( *wUnicode == '/' ){ *wUnicode = '\\'; } ++wUnicode; } nByte = cygwin_conv_path(CCP_WIN_W_TO_POSIX, zUnicode, NULL, 0); zPath = fossil_malloc(nByte); cygwin_conv_path(CCP_WIN_W_TO_POSIX, zUnicode, zPath, nByte); }else{ zPath = fossil_strdup(zUtf8); zUtf8 = p = zPath; while( (*p = *zUtf8++) != 0){ if( *p++ == '\\' ) { p[-1] = '/'; } } } return zPath; #elif defined(__APPLE__) && !defined(WITHOUT_ICONV) return fossil_strdup(zUtf8); #else return (void *)zUtf8; /* No-op on unix */ #endif } /* ** Deallocate any memory that was previously allocated by ** fossil_path_to_utf8() or fossil_utf8_to_path(). */ void fossil_path_free(void *pOld){ #if defined(_WIN32) sqlite3_free(pOld); #elif (defined(__APPLE__) && !defined(WITHOUT_ICONV)) || defined(__CYGWIN__) fossil_free(pOld); #else /* No-op on all other unix */ #endif } /* ** For a given index in a UTF-8 string, return the nearest index that is the ** start of a new code point. The returned index is equal or lower than the ** given index. The end of the string (the null-terminator) is considered a ** valid start index. The given index is returned unchanged if the string ** contains invalid UTF-8 (i.e. overlong runs of trail bytes). ** This function is useful to find code point boundaries for truncation, for ** example, so that no incomplete UTF-8 sequences are left at the end of the ** truncated string. ** This function does not attempt to keep logical and/or visual constructs ** spanning across multiple code points intact, that is no attempts are made ** keep combining characters together with their base characters, or to keep ** more complex grapheme clusters intact. */ #define IsUTF8TrailByte(c) ( (c&0xc0)==0x80 ) int utf8_nearest_codepoint(const char *zString, int maxByteIndex){ int i,n; for( n=0, i=maxByteIndex; n<4 && i>=0; n++, i-- ){ if( !IsUTF8TrailByte(zString[i]) ) return i; } return maxByteIndex; } /* ** Find the byte index corresponding to the given code point index in a UTF-8 ** string. If the string contains fewer than the given number of code points, ** the index of the end of the string (the null-terminator) is returned. ** Incomplete, ill-formed and overlong sequences are counted as one sequence. ** The invalid lead bytes 0xC0 to 0xC1 and 0xF5 to 0xF7 are allowed to initiate ** (ill-formed) 2- and 4-byte sequences, respectively, the other invalid lead ** bytes 0xF8 to 0xFF are treated as invalid 1-byte sequences (as lone trail ** bytes). */ int utf8_codepoint_index(const char *zString, int nCodePoint){ int i; /* Counted bytes. */ int lenUTF8; /* Counted UTF-8 sequences. */ if( zString==0 ) return 0; for(i=0, lenUTF8=0; zString[i]!=0 && lenUTF8<nCodePoint; i++, lenUTF8++){ char c = zString[i]; int cchUTF8=1; /* Code units consumed. */ int maxUTF8=1; /* Expected sequence length. */ if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */ else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */ else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */ while( cchUTF8<maxUTF8 && (zString[i+1]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */ cchUTF8++; i++; } } return i; } /* ** Display UTF-8 on the console. Return the number of ** Characters written. If stdout or stderr is redirected ** to a file, -1 is returned and nothing is written ** to the console. */ #ifdef _WIN32 int fossil_utf8_to_console( const char *zUtf8, int nByte, int toStdErr ){ int nChar, written = 0; wchar_t *zUnicode; /* Unicode version of zUtf8 */ DWORD dummy; Blob blob; static int istty[2] = { -1, -1 }; assert( toStdErr==0 || toStdErr==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 = fossil_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 output 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( toStdErr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE), zUnicode + written, size, &dummy, 0); written += size; } fossil_free(zUnicode); return nChar; } #endif |
Added src/util.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 | /* ** 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 contains code for miscellaneous utility routines. */ #include "config.h" #include "util.h" #if defined(USE_MMAN_H) # include <sys/mman.h> # include <unistd.h> #endif #include <math.h> /* ** For the fossil_timer_xxx() family of functions... */ #ifdef _WIN32 # include <windows.h> #else # include <sys/time.h> # include <sys/resource.h> # include <sys/types.h> # include <sys/stat.h> # include <unistd.h> # include <fcntl.h> # include <errno.h> #endif /* ** Exit. Take care to close the database first. */ NORETURN void fossil_exit(int rc){ db_close(1); #ifndef _WIN32 if( g.fAnyTrace ){ fprintf(stderr, "/***** Subprocess %d exit(%d) *****/\n", getpid(), rc); fflush(stderr); } #endif exit(rc); } /* ** Malloc and free routines that cannot fail */ void *fossil_malloc(size_t n){ void *p = malloc(n==0 ? 1 : n); if( p==0 ) fossil_fatal("out of memory"); return p; } void *fossil_malloc_zero(size_t n){ void *p = malloc(n==0 ? 1 : n); if( p==0 ) fossil_fatal("out of memory"); memset(p, 0, n); return p; } void fossil_free(void *p){ free(p); } void *fossil_realloc(void *p, size_t n){ p = realloc(p, n); if( p==0 ) fossil_fatal("out of memory"); return p; } void fossil_secure_zero(void *p, size_t n){ volatile unsigned char *vp = (volatile unsigned char *)p; size_t i; if( p==0 ) return; assert( n>0 ); if( n==0 ) return; for(i=0; i<n; i++){ vp[i] ^= 0xFF; } for(i=0; i<n; i++){ vp[i] ^= vp[i]; } } void fossil_get_page_size(size_t *piPageSize){ #if defined(_WIN32) SYSTEM_INFO sysInfo; memset(&sysInfo, 0, sizeof(SYSTEM_INFO)); GetSystemInfo(&sysInfo); *piPageSize = (size_t)sysInfo.dwPageSize; #elif defined(USE_MMAN_H) *piPageSize = (size_t)sysconf(_SC_PAGE_SIZE); #else *piPageSize = 4096; /* FIXME: What for POSIX? */ #endif } void *fossil_secure_alloc_page(size_t *pN){ void *p; size_t pageSize = 0; fossil_get_page_size(&pageSize); assert( pageSize>0 ); assert( pageSize%2==0 ); #if defined(_WIN32) p = VirtualAlloc(NULL, pageSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); if( p==NULL ){ fossil_fatal("VirtualAlloc failed: %lu\n", GetLastError()); } if( !VirtualLock(p, pageSize) ){ fossil_fatal("VirtualLock failed: %lu\n", GetLastError()); } #elif defined(USE_MMAN_H) p = mmap(0, pageSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if( p==MAP_FAILED ){ fossil_fatal("mmap failed: %d\n", errno); } if( mlock(p, pageSize) ){ fossil_fatal("mlock failed: %d\n", errno); } #else p = fossil_malloc(pageSize); #endif fossil_secure_zero(p, pageSize); if( pN ) *pN = pageSize; return p; } void fossil_secure_free_page(void *p, size_t n){ if( !p ) return; assert( n>0 ); fossil_secure_zero(p, n); #if defined(_WIN32) if( !VirtualUnlock(p, n) ){ fossil_panic("VirtualUnlock failed: %lu\n", GetLastError()); } if( !VirtualFree(p, 0, MEM_RELEASE) ){ fossil_panic("VirtualFree failed: %lu\n", GetLastError()); } #elif defined(USE_MMAN_H) if( munlock(p, n) ){ fossil_panic("munlock failed: %d\n", errno); } if( munmap(p, n) ){ fossil_panic("munmap failed: %d\n", errno); } #else fossil_free(p); #endif } /* ** Translate every upper-case character in the input string into ** its equivalent lower-case. */ char *fossil_strtolwr(char *zIn){ char *zStart = zIn; if( zIn ){ while( *zIn ){ *zIn = fossil_tolower(*zIn); zIn++; } } return zStart; } /* ** This local variable determines the behavior of ** fossil_assert_safe_command_string(): ** ** 0 (default) fossil_panic() on an unsafe command string ** ** 1 Print an error but continue process. Used for ** testing of fossil_assert_safe_command_string(). ** ** 2 No-op. Used to allow any arbitrary command string ** through fossil_system(), such as when invoking ** COMMAND in "fossil bisect run COMMAND". */ static int safeCmdStrTest = 0; /* ** Check the input string to ensure that it is safe to pass into system(). ** A string is unsafe for system() on unix if it contains any of the following: ** ** * Any occurrance of '$' or '`' except single-quoted or after \ ** * Any of the following characters, unquoted: ;|& or \n except ** these characters are allowed as the very last character in the ** string. ** * Unbalanced single or double quotes ** ** This routine is intended as a second line of defense against attack. ** It should never fail. Dangerous shell strings should be detected and ** fixed before calling fossil_system(). This routine serves only as a ** safety net in case of bugs elsewhere in the system. ** ** If an unsafe string is seen, either abort (default) or print ** a warning message (if safeCmdStrTest is true). */ static void fossil_assert_safe_command_string(const char *z){ int unsafe = 0; #ifndef _WIN32 /* Unix */ int inQuote = 0; int i, c; for(i=0; !unsafe && (c = z[i])!=0; i++){ switch( c ){ case '$': case '`': { if( inQuote!='\'' ) unsafe = i+1; break; } case ';': case '|': case '&': case '\n': { if( inQuote!='\'' && z[i+1]!=0 ) unsafe = i+1; break; } case '"': case '\'': { if( inQuote==0 ){ inQuote = c; }else if( inQuote==c ){ inQuote = 0; } break; } case '\\': { if( z[i+1]==0 ){ unsafe = i+1; }else if( inQuote!='\'' ){ i++; } break; } } } if( inQuote ) unsafe = i; #else /* Windows */ int i, c; int inQuote = 0; for(i=0; !unsafe && (c = z[i])!=0; i++){ switch( c ){ case '>': case '<': case '|': case '&': case '\n': { if( inQuote==0 && z[i+1]!=0 ) unsafe = i+1; break; } case '"': { if( inQuote==c ){ inQuote = 0; }else{ inQuote = c; } break; } case '^': { if( !inQuote && z[i+1]!=0 ){ i++; } break; } } } if( inQuote ) unsafe = i; #endif if( unsafe && safeCmdStrTest<2 ){ char *zMsg = mprintf("Unsafe command string: %s\n%*shere ----^", z, unsafe+13, ""); if( safeCmdStrTest ){ fossil_print("%z\n", zMsg); fossil_free(zMsg); }else{ fossil_panic("%s", zMsg); } } } /* ** This function implements a cross-platform "system()" interface. */ int fossil_system(const char *zOrigCmd){ int rc; #if defined(_WIN32) /* On windows, we have to put double-quotes around the entire command. ** Who knows why - this is just the way windows works. */ char *zNewCmd = mprintf("\"%s\"", zOrigCmd); wchar_t *zUnicode = fossil_utf8_to_unicode(zNewCmd); if( g.fSystemTrace ) { fossil_trace("SYSTEM: %s\n", zNewCmd); } fossil_assert_safe_command_string(zOrigCmd); 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); fossil_assert_safe_command_string(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 */ fossil_limit_memory(0); rc = system(zOrigCmd); fossil_limit_memory(1); #endif return rc; } /* ** Like "fossil_system()" but does not check the command-string for ** potential security problems. */ int fossil_unsafe_system(const char *zOrigCmd){ int rc; safeCmdStrTest = 2; rc = fossil_system(zOrigCmd); safeCmdStrTest = 0; return rc; } /* ** COMMAND: test-fossil-system ** ** Read lines of input and send them to fossil_system() for evaluation. ** Use this command to verify that fossil_system() will not run "unsafe" ** commands. */ void test_fossil_system_cmd(void){ char zLine[10000]; safeCmdStrTest = 1; while(1){ size_t n; int rc; printf("system-test> "); fflush(stdout); if( !fgets(zLine, sizeof(zLine), stdin) ) break; n = strlen(zLine); while( n>0 && fossil_isspace(zLine[n-1]) ) n--; zLine[n] = 0; printf("cmd: [%s]\n", zLine); fflush(stdout); rc = fossil_system(zLine); printf("result: %d\n", rc); } } /* ** Like strcmp() except that it accepts NULL pointers. NULL sorts before ** all non-NULL string pointers. Also, this strcmp() is a binary comparison ** that does not consider locale. */ int fossil_strcmp(const char *zA, const char *zB){ if( zA==0 ){ if( zB==0 ) return 0; return -1; }else if( zB==0 ){ return +1; }else{ return strcmp(zA,zB); } } int fossil_strncmp(const char *zA, const char *zB, int nByte){ if( zA==0 ){ if( zB==0 ) return 0; return -1; }else if( zB==0 ){ return +1; }else if( nByte>0 ){ int a, b; do{ a = *zA++; b = *zB++; }while( a==b && a!=0 && (--nByte)>0 ); return ((unsigned char)a) - (unsigned char)b; }else{ return 0; } } /* ** Case insensitive string comparison. */ int fossil_strnicmp(const char *zA, const char *zB, int nByte){ if( zA==0 ){ if( zB==0 ) return 0; return -1; }else if( zB==0 ){ return +1; } if( nByte<0 ) nByte = strlen(zB); return sqlite3_strnicmp(zA, zB, nByte); } int fossil_stricmp(const char *zA, const char *zB){ int nByte; int rc; if( zA==0 ){ if( zB==0 ) return 0; return -1; }else if( zB==0 ){ return +1; } nByte = strlen(zB); rc = sqlite3_strnicmp(zA, zB, nByte); if( rc==0 && zA[nByte] ) rc = 1; return rc; } /* ** Get user and kernel times in microseconds. */ void fossil_cpu_times(sqlite3_uint64 *piUser, sqlite3_uint64 *piKernel){ #ifdef _WIN32 FILETIME not_used; FILETIME kernel_time; FILETIME user_time; GetProcessTimes(GetCurrentProcess(), ¬_used, ¬_used, &kernel_time, &user_time); if( piUser ){ *piUser = ((((sqlite3_uint64)user_time.dwHighDateTime)<<32) + (sqlite3_uint64)user_time.dwLowDateTime + 5)/10; } if( piKernel ){ *piKernel = ((((sqlite3_uint64)kernel_time.dwHighDateTime)<<32) + (sqlite3_uint64)kernel_time.dwLowDateTime + 5)/10; } #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 } /* ** Return the resident set size for this process */ sqlite3_uint64 fossil_rss(void){ #ifdef _WIN32 return 0; #else struct rusage s; getrusage(RUSAGE_SELF, &s); return s.ru_maxrss*1024; #endif } /* ** Internal helper type for fossil_timer_xxx(). */ enum FossilTimerEnum { FOSSIL_TIMER_COUNT = 10 /* Number of timers we can track. */ }; static struct FossilTimer { sqlite3_uint64 u; /* "User" CPU times */ sqlite3_uint64 s; /* "System" CPU times */ int id; /* positive if allocated, else 0. */ } fossilTimerList[FOSSIL_TIMER_COUNT] = {{0,0,0}}; /* ** Stores the current CPU times into the shared timer list ** and returns that timer's internal ID. Pass that ID to ** fossil_timer_fetch() to get the elapsed time for that ** timer. ** ** The system has a fixed number of timers, and they can be ** "deallocated" by passing this function's return value to ** fossil_timer_stop() Adjust FOSSIL_TIMER_COUNT to set the number of ** available timers. ** ** Returns 0 on error (no more timers available), with 1+ being valid ** timer IDs. */ int fossil_timer_start(){ int i; for( i = 0; i < FOSSIL_TIMER_COUNT; ++i ){ struct FossilTimer * ft = &fossilTimerList[i]; if(ft->id) continue; ft->id = i+1; fossil_cpu_times( &ft->u, &ft->s ); break; } return (i<FOSSIL_TIMER_COUNT) ? i+1 : 0; } /* ** Returns the difference in CPU times in microseconds since ** fossil_timer_start() was called and returned the given timer ID (or ** since it was last reset). Returns 0 if timerId is out of range. */ sqlite3_uint64 fossil_timer_fetch(int timerId){ if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){ struct FossilTimer * start = &fossilTimerList[timerId-1]; if( !start->id ){ fossil_panic("Invalid call to fetch a non-allocated " "timer (#%d)", timerId); /*NOTREACHED*/ }else{ sqlite3_uint64 eu = 0, es = 0; fossil_cpu_times( &eu, &es ); return (eu - start->u) + (es - start->s); } } return 0; } /* ** Resets the timer associated with the given ID, as obtained via ** fossil_timer_start(), to the current CPU time values. */ sqlite3_uint64 fossil_timer_reset(int timerId){ if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){ struct FossilTimer * start = &fossilTimerList[timerId-1]; if( !start->id ){ fossil_panic("Invalid call to reset a non-allocated " "timer (#%d)", timerId); /*NOTREACHED*/ }else{ sqlite3_uint64 const rc = fossil_timer_fetch(timerId); fossil_cpu_times( &start->u, &start->s ); return rc; } } return 0; } /** "Deallocates" the fossil timer identified by the given timer ID. returns the difference (in uSec) between the last time that timer was started or reset. Returns 0 if timerId is out of range (but note that, due to system-level precision restrictions, this function might return 0 on success, too!). It is not legal to re-use the passed-in timerId after calling this until/unless it is re-initialized using fossil_timer_start() (NOT fossil_timer_reset()). */ sqlite3_uint64 fossil_timer_stop(int timerId){ if(timerId<1 || timerId>FOSSIL_TIMER_COUNT){ return 0; }else{ sqlite3_uint64 const rc = fossil_timer_fetch(timerId); struct FossilTimer * t = &fossilTimerList[timerId-1]; t->id = 0; t->u = t->s = 0U; return rc; } } /* ** 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 HNAME_LEN_SHA1 or HNAME_LEN_K256 ** bytes long and contains only lower-case ASCII hexadecimal values. */ int fossil_is_artifact_hash(const char *zSym){ int sz = zSym ? (int)strlen(zSym) : 0; return (HNAME_LEN_SHA1==sz || HNAME_LEN_K256==sz) && validate16(zSym, sz); } /* ** 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; } /* ** Return the name of the users preferred text editor. Return NULL if ** not found. ** ** Search algorithm: ** (1) The local "editor" setting ** (2) The global "editor" setting ** (3) The VISUAL environment variable ** (4) The EDITOR environment variable ** (5) (Windows only:) "notepad.exe" */ const char *fossil_text_editor(void){ const char *zEditor = db_get("editor", 0); if( zEditor==0 ){ zEditor = fossil_getenv("VISUAL"); } if( zEditor==0 ){ zEditor = fossil_getenv("EDITOR"); } #if defined(_WIN32) || defined(__CYGWIN__) if( zEditor==0 ){ zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SYSTEMROOT")); #if defined(__CYGWIN__) zEditor = fossil_utf8_to_path(zEditor, 0); #endif } #endif return zEditor; } /* ** Construct a temporary filename. ** ** The returned string is obtained from sqlite3_malloc() and must be ** freed by the caller. ** ** See also: file_tempname() and file_time_timename(); */ char *fossil_temp_filename(void){ char *zTFile = 0; const char *zDir; char cDirSep; char zSep[2]; size_t nDir; u64 r[2]; #ifdef _WIN32 char *zTempDirA = NULL; WCHAR zTempDirW[MAX_PATH+1]; const DWORD dwTempSizeW = sizeof(zTempDirW)/sizeof(zTempDirW[0]); DWORD dwTempLenW; #else int i; static const char *azTmp[] = {"/var/tmp","/usr/tmp","/tmp"}; #endif if( g.db ){ sqlite3_file_control(g.db, 0, SQLITE_FCNTL_TEMPFILENAME, (void*)&zTFile); if( zTFile ) return zTFile; } sqlite3_randomness(sizeof(r), &r); #if _WIN32 cDirSep = '\\'; dwTempLenW = GetTempPathW(dwTempSizeW, zTempDirW); if( dwTempLenW>0 && dwTempLenW<dwTempSizeW && ( zTempDirA = fossil_path_to_utf8(zTempDirW) )){ zDir = zTempDirA; }else{ zDir = fossil_getenv("LOCALAPPDATA"); if( zDir==0 ) zDir = "."; } #else for(i=0; i<(int)(sizeof(azTmp)/sizeof(azTmp[0])); i++){ struct stat buf; zDir = azTmp[i]; if( stat(zDir,&buf)==0 && S_ISDIR(buf.st_mode) && access(zDir,03)==0 ){ break; } } if( i>=(int)(sizeof(azTmp)/sizeof(azTmp[0])) ) zDir = "."; cDirSep = '/'; #endif nDir = strlen(zDir); zSep[1] = 0; zSep[0] = (nDir && zDir[nDir-1]==cDirSep) ? 0 : cDirSep; zTFile = sqlite3_mprintf("%s%sfossil%016llx%016llx", zDir,zSep,r[0],r[1]); #ifdef _WIN32 if( zTempDirA ) fossil_path_free(zTempDirA); #endif return zTFile; } /* ** Turn memory limits for stack and heap on and off. The argument ** is true to turn memory limits on and false to turn them off. ** ** Memory limits should be enabled at startup, but then turned off ** before starting subprocesses. */ void fossil_limit_memory(int onOff){ #if defined(__unix__) static sqlite3_int64 origHeap = 10000000000LL; /* 10GB */ static sqlite3_int64 origStack = 8000000 ; /* 8MB */ struct rlimit x; #if defined(RLIMIT_DATA) getrlimit(RLIMIT_DATA, &x); if( onOff ){ origHeap = x.rlim_cur; if( sizeof(void*)<8 || sizeof(x.rlim_cur)<8 ){ x.rlim_cur = 1000000000 ; /* 1GB on 32-bit systems */ }else{ x.rlim_cur = 10000000000LL; /* 10GB on 64-bit systems */ } }else{ x.rlim_cur = origHeap; } setrlimit(RLIMIT_DATA, &x); #endif /* defined(RLIMIT_DATA) */ #if defined(RLIMIT_STACK) getrlimit(RLIMIT_STACK, &x); if( onOff ){ origStack = x.rlim_cur; x.rlim_cur = 8000000; /* 8MB */ }else{ x.rlim_cur = origStack; } setrlimit(RLIMIT_STACK, &x); #endif /* defined(RLIMIT_STACK) */ #endif /* defined(__unix__) */ } #if defined(HAVE_PLEDGE) /* ** Interface to pledge() on OpenBSD 5.9 and later. ** ** On platforms that have pledge(), use this routine. ** On all other platforms, this routine does not exist, but instead ** a macro defined in config.h is used to provide a no-op. */ void fossil_pledge(const char *promises){ if( pledge(promises, 0) ){ fossil_panic("pledge(\"%s\",NULL) fails with errno=%d", promises, (int)errno); } } #endif /* defined(HAVE_PLEDGE) */ /* ** Construct a random password and return it as a string. N is the ** recommended number of characters for the password. ** ** Space to hold the returned string is obtained from fossil_malloc() ** and should be freed by the caller. */ char *fossil_random_password(int N){ char zSrc[60]; int nSrc; int i; char z[60]; /* Source characters for the password. Omit characters like "0", "O", ** "1" and "I" that might be easily confused */ static const char zAlphabet[] = /* 0 1 2 3 4 5 */ /* 123456789 123456789 123456789 123456789 123456789 123456 */ "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; if( N<8 ) N = 8; nSrc = sizeof(zAlphabet) - 1; if( N>nSrc ) N = nSrc; memcpy(zSrc, zAlphabet, nSrc); for(i=0; i<N; i++){ unsigned r; sqlite3_randomness(sizeof(r), &r); r %= nSrc; z[i] = zSrc[r]; zSrc[r] = zSrc[--nSrc]; } z[i] = 0; return fossil_strdup(z); } /* ** COMMAND: test-random-password ** ** Usage: %fossil test-random-password [N] [--entropy] ** ** Generate a random password string of approximately N characters in length. ** If N is omitted, use 12. Values of N less than 8 are changed to 8 ** and greater than 57 and changed to 57. ** ** If the --entropy flag is included, the number of bits of entropy in ** the password is show as well. */ void test_random_password(void){ int N = 12; int showEntropy = 0; int i; char *zPassword; for(i=2; i<g.argc; i++){ const char *z = g.argv[i]; if( z[0]=='-' && z[1]=='-' ) z++; if( strcmp(z,"-entropy")==0 ){ showEntropy = 1; }else if( fossil_isdigit(z[0]) ){ N = atoi(z); if( N<8 ) N = 8; if( N>57 ) N = 57; }else{ usage("[N] [--entropy]"); } } zPassword = fossil_random_password(N); if( showEntropy ){ double et = 57.0; for(i=1; i<N; i++) et *= 57-i; fossil_print("%s (%d bits of entropy)\n", zPassword, (int)(log(et)/log(2.0))); }else{ fossil_print("%s\n", zPassword); } fossil_free(zPassword); } /* ** Return the number of decimal digits in a nonnegative integer. This is useful ** when formatting text. */ int fossil_num_digits(int n){ return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3 : n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6 : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10; } #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. */ static int binaryOnPath(const char *zBinary){ const char *zPath = fossil_getenv("PATH"); char *zFull; int i; int bExists; while( zPath && zPath[0] ){ while( zPath[0]==':' ) zPath++; for(i=0; zPath[i] && zPath[i]!=':'; i++){} zFull = mprintf("%.*s/%s", i, zPath, zBinary); bExists = file_access(zFull, X_OK); fossil_free(zFull); if( bExists==0 ) return 1; zPath += i; } return 0; } #endif #endif /* ** Return the name of a command that will launch a web-browser. */ const char *fossil_web_browser(void){ const char *zBrowser = 0; #if defined(_WIN32) zBrowser = db_get("web-browser", "start \"\""); #elif defined(__DARWIN__) || defined(__APPLE__) || defined(__HAIKU__) zBrowser = db_get("web-browser", "open"); #else zBrowser = db_get("web-browser", 0); if( zBrowser==0 ){ static const char *const azBrowserProg[] = { "xdg-open", "gnome-open", "firefox", "google-chrome" }; int i; zBrowser = "echo"; for(i=0; i<count(azBrowserProg); i++){ if( binaryOnPath(azBrowserProg[i]) ){ zBrowser = azBrowserProg[i]; break; } } } #endif return zBrowser; } /* ** On non-Windows systems, calls nice(2) with the given level. Errors ** are ignored. On Windows this is a no-op. */ void fossil_nice(int level){ #ifndef _WIN32 /* dummy if() condition to avoid nuisance warning about unused result on certain compiler */ if( nice(level) ){ /*ignored*/ } #else (void)level; #endif } /* ** Calls fossil_nice() with a default level. */ void fossil_nice_default(void){ fossil_nice(19); } |
Changes to src/verify.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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to help verify the integrity of ** the repository. ** ** This file primarily implements the verify_before_commit() interface. ** Any function can call verify_before_commit() with a record id (RID) ** as an argument. Then before the next change to the database commits, ** this routine will reach in and check that the record can be extracted ** correctly from the BLOB table. */ #include "config.h" #include "verify.h" #include <assert.h> /* ** Load the record identify by rid. Make sure we can reproduce it ** without error. ** ** Panic if anything goes wrong. If this procedure returns it means ** that everything is OK. */ static void verify_rid(int rid){ Blob uuid, content; if( content_size(rid, 0)<0 ){ return; /* No way to verify phantoms */ } blob_zero(&uuid); db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid); if( !hname_validate(blob_buffer(&uuid), blob_size(&uuid)) ){ fossil_fatal("not a valid rid: %d", rid); } if( content_get(rid, &content) ){ if( !hname_verify_hash(&content, blob_buffer(&uuid), blob_size(&uuid)) ){ fossil_panic("hash of rid %d does not match its uuid (%b)", rid, &uuid); } blob_reset(&content); } blob_reset(&uuid); } /* ** 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(0); 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.
︙ | ︙ | |||
17 18 19 20 21 22 23 | ** ** Procedures for managing the VFILE table. */ #include "config.h" #include "vfile.h" #include <assert.h> #include <sys/types.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 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 | ** ** Procedures for managing the VFILE table. */ #include "config.h" #include "vfile.h" #include <assert.h> #include <sys/types.h> /* ** The input is guaranteed to be a 40- or 64-character well-formed ** artifact hash. Find its rid. */ int fast_uuid_to_rid(const char *zUuid){ static Stmt q; int rid; db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid"); db_bind_text(&q, ":uuid", zUuid); if( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); }else{ rid = 0; } db_reset(&q); return rid; } /* ** 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[HNAME_MAX+1]; sz = strlen(zUuid); if( !hname_validate(zUuid, sz) ){ return 0; /* Not a valid hash */ } memcpy(z, zUuid, sz+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,mhash) " " VALUES(:vid,:isexe,:islink,:id,:id,:name,NULL)"); 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: */ #define CKSIG_ENOTFILE 0x001 /* non-file FS objects throw an error */ #define CKSIG_HASH 0x002 /* Verify file content using hashing */ #define CKSIG_SETMTIME 0x004 /* Set mtime to last check-out time */ #endif /* INTERFACE */ /* ** Look at every VFILE entry with the given vid and update VFILE.CHNGED field ** according to whether or not the file has changed. ** - 0 means no change. ** - 1 means edited. ** - 2 means changed due to a merge. ** - 3 means added by a merge. ** - 4 means changed due to an integrate merge. ** - 5 means added by an integrate merge. ** - 6 means became executable but has unmodified contents. ** - 7 means became a symlink whose target equals its old contents. ** - 8 means lost executable status but has unmodified contents. ** - 9 means lost symlink status and has contents equal to its old target. ** ** 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 ** is true (or undefined - it defaults to true) and the CKSIG_HASH ** flag is false. If the mtime-changes setting is false or if ** CKSIG_HASH is true, then we do not trust the mtime and will examine ** the on-disk content to determine if a file really is the same. ** ** If the mtime is used, it is used only to determine if files are the same. ** If the mtime of a file has changed, we still examine the on-disk content ** to see whether or not the edit was a null-edit. */ void vfile_check_signature(int vid, unsigned int cksigFlags){ int nErr = 0; Stmt q; int useMtime = (cksigFlags & CKSIG_HASH)==0 && db_get_boolean("mtime-changes", 1); db_begin_transaction(); db_prepare(&q, "SELECT id, %Q || pathname," " vfile.mrid, deleted, chnged, uuid, size, mtime," " CASE WHEN isexe THEN %d WHEN islink THEN %d ELSE %d END" " FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid" " WHERE vid=%d ", g.zLocalRoot, PERM_EXE, PERM_LNK, PERM_REG, vid); while( db_step(&q)==SQLITE_ROW ){ int id, rid, isDeleted; const char *zName; int chnged = 0; int oldChnged; #ifndef _WIN32 int origPerm; int currentPerm; #endif i64 oldMtime; i64 currentMtime; i64 origSize; i64 currentSize; 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_size(zName, RepoFILE); currentMtime = file_mtime(0, 0); #ifndef _WIN32 origPerm = db_column_int(&q, 8); currentPerm = file_perm(zName, RepoFILE); #endif if( chnged==0 && (isDeleted || rid==0) ){ /* "fossil rm" or "fossil add" always change the file */ chnged = 1; }else if( !file_isfile_or_link(0) && currentSize>=0 ){ if( cksigFlags & CKSIG_ENOTFILE ){ fossil_warning("not an ordinary file: %s", zName); nErr++; } chnged = 1; } if( origSize!=currentSize ){ if( chnged!=1 ){ /* A file size change is definitive - the file has changed. No ** need to check the mtime or hash */ chnged = 1; } }else if( chnged==1 && rid!=0 && !isDeleted ){ /* File is believed to have changed but it is the same size. ** Double check that it really has changed by looking at content. */ const char *zUuid = db_column_text(&q, 5); int nUuid = db_column_bytes(&q, 5); assert( origSize==currentSize ); if( hname_verify_file_hash(zName, zUuid, nUuid) ) chnged = 0; }else if( (chnged==0 || chnged==2 || chnged==4) && (useMtime==0 || currentMtime!=oldMtime) ){ /* For files that were formerly believed to be unchanged or that were ** changed by merging, if their mtime changes, or unconditionally ** if --hash is used, check to see if they have been edited by ** looking at their artifact hashes */ const char *zUuid = db_column_text(&q, 5); int nUuid = db_column_bytes(&q, 5); assert( origSize==currentSize ); if( !hname_verify_file_hash(zName, zUuid, nUuid) ) chnged = 1; } if( (cksigFlags & CKSIG_SETMTIME) && (chnged==0 || chnged==2 || chnged==4)){ i64 desiredMtime; if( mtime_of_manifest_file(vid,rid,&desiredMtime)==0 ){ if( currentMtime!=desiredMtime ){ file_set_mtime(zName, desiredMtime); currentMtime = file_mtime(zName, RepoFILE); } } } #ifndef _WIN32 if( origPerm!=PERM_LNK && currentPerm==PERM_LNK ){ /* Changing to a symlink takes priority over all other change types. */ chnged = 7; }else if( origPerm==PERM_LNK && currentPerm!=PERM_LNK ){ /* Ditto, other direction */ chnged = 9; }else if( chnged==0 || chnged==6 || chnged==7 || chnged==8 || chnged==9 ){ /* Confirm metadata change types. */ if( origPerm==currentPerm ){ chnged = 0; }else if( currentPerm==PERM_EXE ){ chnged = 6; }else if( origPerm==PERM_EXE ){ chnged = 8; }else if( origPerm==PERM_LNK ){ chnged = 9; } } #endif if( currentMtime!=oldMtime || chnged!=oldChnged ){ db_multi_exec("UPDATE vfile SET mtime=%lld, chnged=%d WHERE id=%d", currentMtime, chnged, id); } } db_finalize(&q); if( nErr ) fossil_fatal("abort due to prior errors"); db_end_transaction(0); } /* ** Write all files from vid to the disk. Or if vid==0 and id!=0 ** write just the specific file where VFILE.ID=id. */ void vfile_to_disk( int vid, /* vid to write to disk */ int id, /* Write this one file, if not zero */ int verbose, /* Output progress information */ int promptFlag /* Prompt user to confirm overwrites */ ){ Stmt q; Blob content; int nRepos = strlen(g.zLocalRoot); if( vid>0 && id==0 ){ db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe, islink" " FROM vfile" " WHERE vid=%d AND mrid>0", g.zLocalRoot, vid); }else{ assert( vid==0 && id>0 ); db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe, islink" " FROM vfile" " WHERE id=%d AND mrid>0", g.zLocalRoot, id); } while( db_step(&q)==SQLITE_ROW ){ int id, rid, isExe, isLink; const char *zName; id = db_column_int(&q, 0); zName = db_column_text(&q, 1); rid = db_column_int(&q, 2); isExe = db_column_int(&q, 3); isLink = db_column_int(&q, 4); if( file_unsafe_in_tree_path(zName) ){ continue; } content_get(rid, &content); if( file_is_the_same(&content, zName) ){ blob_reset(&content); if( file_setexe(zName, isExe) ){ db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", file_mtime(zName, RepoFILE), id); } continue; } if( promptFlag && file_size(zName, RepoFILE)>=0 ){ Blob ans; char *zMsg; char cReply; zMsg = mprintf("overwrite %s (a=always/y/N)? ", zName); prompt_user(zMsg, &ans); free(zMsg); cReply = blob_str(&ans)[0]; blob_reset(&ans); if( cReply=='a' || cReply=='A' ){ promptFlag = 0; } else if( cReply!='y' && cReply!='Y' ){ blob_reset(&content); continue; } } if( verbose ) fossil_print("%s\n", &zName[nRepos]); if( file_isdir(zName, RepoFILE)==1 ){ /*TODO(dchest): remove directories? */ fossil_fatal("%s is directory, cannot overwrite", zName); } if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){ file_delete(zName); } if( isLink ){ symlink_create(blob_str(&content), zName); }else{ blob_write_to_file(&content, zName); } file_setexe(zName, isExe); blob_reset(&content); db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", file_mtime(zName, RepoFILE), id); } db_finalize(&q); } /* ** Check to see if the directory named in zPath is the top of a check-out. ** In other words, check to see if directory pPath contains a file named ** "_FOSSIL_" or ".fslckout". Return true or false. */ int vfile_top_of_checkout(const char *zPath){ char *zFile; int fileFound = 0; zFile = mprintf("%s/_FOSSIL_", zPath); fileFound = file_size(zFile, ExtFILE)>=1024; fossil_free(zFile); if( !fileFound ){ zFile = mprintf("%s/.fslckout", zPath); fileFound = file_size(zFile, ExtFILE)>=1024; fossil_free(zFile); } /* Check for ".fos" for legacy support. But the use of ".fos" as the ** per-check-out database name is deprecated. At some point, all support ** for ".fos" will end and this code should be removed. This comment ** added on 2012-02-04. */ if( !fileFound ){ zFile = mprintf("%s/.fos", zPath); fileFound = file_size(zFile, ExtFILE)>=1024; fossil_free(zFile); } return fileFound; } /* ** Return TRUE if zFile is a temporary file. Return FALSE if not. */ static int is_temporary_file(const char *zName){ 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<count(azTemp); 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(). */ #define SCAN_ALL 0x001 /* Includes files that begin with "." */ #define SCAN_TEMP 0x002 /* Only Fossil-generated files like *-baseline */ #define SCAN_NESTED 0x004 /* Scan for empty dirs in nested checkouts */ #define SCAN_MTIME 0x008 /* Populate mtime column */ #define SCAN_SIZE 0x010 /* Populate size column */ #define SCAN_ISEXE 0x020 /* Populate isexe column */ #endif /* INTERFACE */ /* ** Load into table SFILE the name of every ordinary file in ** the directory pPath. Omit the first nPrefix characters of ** of pPath when inserting into the SFILE table. ** Subdirectories are scanned recursively. ** ** Omit files named in VFILE if eFType==RepoFILE. Include all files ** if eFType==ExtFILE. ** ** Files whose names begin with "." are omitted unless the SCAN_ALL ** flag is set. ** ** Any files or directories that match the glob patterns pIgnore* ** are excluded from the scan. Name matching occurs after the ** first nPrefix characters are elided from the filename. */ void vfile_scan( Blob *pPath, /* Directory to be scanned */ int nPrefix, /* Number of bytes in directory name */ unsigned scanFlags, /* Zero or more SCAN_xxx flags */ Glob *pIgnore1, /* Do not add files that match this GLOB */ Glob *pIgnore2, /* Omit files matching this GLOB too */ int eFType /* ExtFILE or RepoFILE */ ){ DIR *d; int origSize; struct dirent *pEntry; int skipAll = 0; static Stmt ins; static int depth = 0; void *zNative; origSize = blob_size(pPath); if( pIgnore1 || pIgnore2 ){ blob_appendf(pPath, "/"); if( glob_match(pIgnore1, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; if( glob_match(pIgnore2, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; blob_resize(pPath, origSize); } if( skipAll ) return; if( depth==0 ){ if( eFType==ExtFILE ){ db_prepare(&ins, "INSERT OR IGNORE INTO sfile(pathname%s%s%s) VALUES(:file%s%s%s)", scanFlags & SCAN_MTIME ? ",mtime" : "", scanFlags & SCAN_SIZE ? ",size" : "", scanFlags & SCAN_ISEXE ? ",isexe" : "", scanFlags & SCAN_MTIME ? ",:mtime" : "", scanFlags & SCAN_SIZE ? ",:size" : "", scanFlags & SCAN_ISEXE ? ",:isexe" : "" ); }else{ db_prepare(&ins, "INSERT OR IGNORE INTO sfile(pathname%s%s%s) SELECT :file%s%s%s" " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE" " pathname=:file %s)", scanFlags & SCAN_MTIME ? ",mtime" : "", scanFlags & SCAN_SIZE ? ",size" : "", scanFlags & SCAN_ISEXE ? ",isexe" : "", scanFlags & SCAN_MTIME ? ",:mtime" : "", scanFlags & SCAN_SIZE ? ",:size" : "", scanFlags & SCAN_ISEXE ? ",:isexe" : "", filename_collation() ); } } depth++; zNative = fossil_utf8_to_path(blob_str(pPath), 1); d = opendir(zNative); if( d ){ while( (pEntry=readdir(d))!=0 ){ char *zPath; char *zUtf8; if( pEntry->d_name[0]=='.' ){ if( (scanFlags & SCAN_ALL)==0 ) continue; if( pEntry->d_name[1]==0 ) continue; if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; } zUtf8 = fossil_path_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_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){ #else }else if( file_isdir(zPath, eFType)==1 ){ #endif if( !vfile_top_of_checkout(zPath) ){ vfile_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2, eFType); } #ifdef _DIRENT_HAVE_D_TYPE }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) ? (file_isfile_or_link(zPath)) : (pEntry->d_type==DT_REG) ){ #else }else if( file_isfile_or_link(zPath) ){ #endif if( (scanFlags & SCAN_TEMP)==0 || is_temporary_file(zUtf8) ){ db_bind_text(&ins, ":file", &zPath[nPrefix+1]); if( scanFlags & SCAN_MTIME ){ db_bind_int(&ins, ":mtime", file_mtime(zPath, eFType)); } if( scanFlags & SCAN_SIZE ){ db_bind_int(&ins, ":size", file_size(zPath, eFType)); } if( scanFlags & SCAN_ISEXE ){ db_bind_int(&ins, ":isexe", file_isexe(zPath, eFType)); } db_step(&ins); db_reset(&ins); } } fossil_path_free(zUtf8); blob_resize(pPath, origSize); } closedir(d); } fossil_path_free(zNative); depth--; if( depth==0 ){ db_finalize(&ins); } } /* ** Scans the specified base directory for any directories within it, while ** keeping a count of how many files they each contains, either directly or ** indirectly. ** ** Subdirectories are scanned recursively. ** Omit files named in VFILE. ** ** Directories whose names begin with "." are omitted unless the SCAN_ALL ** flag is set. ** ** Any directories that match the glob patterns pIgnore* are excluded from ** the scan. Name matching occurs after the first nPrefix characters are ** elided from the filename. ** ** Returns the total number of files found. */ int vfile_dir_scan( Blob *pPath, /* Base directory to be scanned */ int nPrefix, /* Number of bytes in base directory name */ unsigned scanFlags, /* Zero or more SCAN_xxx flags */ Glob *pIgnore1, /* Do not add directories that match this GLOB */ Glob *pIgnore2, /* Omit directories matching this GLOB too */ int eFType /* ExtFILE or RepoFILE */ ){ int result = 0; DIR *d; int origSize; struct dirent *pEntry; int skipAll = 0; static Stmt ins; static Stmt upd; static int depth = 0; void *zNative; origSize = blob_size(pPath); if( pIgnore1 || pIgnore2 ){ blob_appendf(pPath, "/"); if( glob_match(pIgnore1, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; if( glob_match(pIgnore2, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; blob_resize(pPath, origSize); } if( skipAll ) return result; if( depth==0 ){ db_multi_exec("DROP TABLE IF EXISTS dscan_temp;" "CREATE TEMP TABLE dscan_temp(" " x TEXT PRIMARY KEY %s, y INTEGER)", filename_collation()); db_prepare(&ins, "INSERT OR IGNORE INTO dscan_temp(x, y) SELECT :file, :count" " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE" " pathname GLOB :file || '/*' %s)", filename_collation() ); db_prepare(&upd, "UPDATE OR IGNORE dscan_temp SET y = coalesce(y, 0) + 1" " WHERE x=:file %s", filename_collation() ); } depth++; zNative = fossil_utf8_to_path(blob_str(pPath), 1); d = opendir(zNative); if( d ){ while( (pEntry=readdir(d))!=0 ){ char *zOrigPath; char *zPath; char *zUtf8; if( pEntry->d_name[0]=='.' ){ if( (scanFlags & SCAN_ALL)==0 ) continue; if( pEntry->d_name[1]==0 ) continue; if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; } zOrigPath = mprintf("%s", blob_str(pPath)); zUtf8 = fossil_path_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_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){ #else }else if( file_isdir(zPath, eFType)==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, eFType); 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_isfile_or_link(zPath)) : (pEntry->d_type==DT_REG) ){ #else }else if( file_isfile_or_link(zPath) ){ #endif db_bind_text(&upd, ":file", zOrigPath); db_step(&upd); db_reset(&upd); result++; /* found 1 normal file */ } fossil_path_free(zUtf8); blob_resize(pPath, origSize); fossil_free(zOrigPath); } closedir(d); } fossil_path_free(zNative); depth--; if( depth==0 ){ db_finalize(&upd); db_finalize(&ins); } return result; } /* ** Compute an aggregate MD5 checksum over the disk image of every ** file in vid. The file names are part of the checksum. The resulting ** checksum is the same as is expected on the R-card of a manifest. ** ** This function operates differently if the Global.aCommitFile ** variable is not NULL. In that case, the disk image is used for ** each file in aCommitFile[] and the repository image ** is used for all others). ** ** Newly added files that are not contained in the repository are ** omitted from the checksum if they are not in Global.aCommitFile[]. ** ** Newly deleted files are included in the checksum if they are not ** part of Global.aCommitFile[] ** ** Renamed files use their new name if they are in Global.aCommitFile[] ** and their original name if they are not in Global.aCommitFile[] ** ** Return the resulting checksum in blob pOut. */ 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_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 ){ md5sum_step_text(" 0\n", -1); continue; } fseek(in, 0L, SEEK_END); sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in)); fseek(in, 0L, SEEK_SET); md5sum_step_text(zBuf, -1); /*printf("%s %s %s",md5sum_current_state(),zName,zBuf);fflush(stdout);*/ for(;;){ int n; n = fread(zBuf, 1, sizeof(zBuf), in); if( n<=0 ) break; md5sum_step_text(zBuf, n); } fclose(in); } }else{ int rid = db_column_int(&q, 4); const char *zOrigName = db_column_text(&q, 2); char zBuf[100]; Blob file; if( zOrigName ) zName = zOrigName; if( rid>0 ){ md5sum_step_text(zName, -1); blob_zero(&file); content_get(rid, &file); sqlite3_snprintf(sizeof(zBuf), zBuf, " %d\n", blob_size(&file)); md5sum_step_text(zBuf, -1); md5sum_step_blob(&file); blob_reset(&file); } } } db_finalize(&q); md5sum_finish(pOut); } /* ** Write a BLOB into a random filename. Return the name of the file. */ char *write_blob_to_temp_file(Blob *pBlob){ sqlite3_uint64 r; char *zOut = 0; do{ sqlite3_free(zOut); sqlite3_randomness(8, &r); zOut = sqlite3_mprintf("file-%08llx", r); }while( file_size(zOut, ExtFILE)>=0 ); blob_write_to_file(pBlob, zOut); return zOut; } /* ** Do a file-by-file comparison of the content of the repository and ** the working check-out on disk. Report any errors. */ void vfile_compare_repository_to_disk(int vid){ sqlite3_int64 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 ){ const char *zFullpath = db_column_text(&q, 0); const char *zName = db_column_text(&q, 1); int rid = db_column_int(&q, 2); blob_zero(&disk); rc = blob_read_from_file(&disk, zFullpath, RepoFILE); if( rc<0 ){ fossil_print("ERROR: cannot read file [%s]\n", zFullpath); blob_reset(&disk); continue; } blob_zero(&repo); content_get(rid, &repo); if( blob_size(&repo)!=blob_size(&disk) ){ fossil_print("ERROR: [%s] is %d bytes on disk but %d in the repository\n", zName, blob_size(&disk), blob_size(&repo)); zOut = write_blob_to_temp_file(&repo); fossil_print("NOTICE: Repository version of [%s] stored in [%s]\n", zName, zOut); sqlite3_free(zOut); blob_reset(&disk); blob_reset(&repo); continue; } if( blob_compare(&repo, &disk) ){ fossil_print( "ERROR: [%s] is different on disk compared to the repository\n", zName); zOut = write_blob_to_temp_file(&repo); fossil_print("NOTICE: Repository version of [%s] stored in [%s]\n", zName, zOut); sqlite3_free(zOut); } blob_reset(&disk); blob_reset(&repo); } db_finalize(&q); } /* ** Compute an aggregate MD5 checksum over the repository image of every ** file in vid. The file names are part of the checksum. The resulting ** checksum is suitable for the R-card of a manifest. ** ** Return the resulting checksum in blob pOut. */ 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); md5sum_init(); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zOrigName = db_column_text(&q, 1); int rid = db_column_int(&q, 2); int isSelected = db_column_int(&q, 3); if( zOrigName && !isSelected ) zName = zOrigName; md5sum_step_text(zName, -1); content_get(rid, &file); sqlite3_snprintf(sizeof(zBuf), zBuf, " %d\n", blob_size(&file)); md5sum_step_text(zBuf, -1); /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/ md5sum_step_blob(&file); blob_reset(&file); } db_finalize(&q); md5sum_finish(pOut); } /* ** Compute an aggregate MD5 checksum over the repository image of every ** file in manifest vid. The file names are part of the checksum. The ** resulting checksum is suitable for use as the R-card of a manifest. ** ** 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; char zBuf[100]; blob_zero(pOut); blob_zero(&err); if( pManOut ){ blob_zero(pManOut); } db_must_be_within_tree(); pManifest = manifest_get(vid, CFTYPE_MANIFEST, &err); if( pManifest==0 ){ fossil_fatal("manifest file (%d) is malformed:\n%s", vid, blob_str(&err)); } manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ if( pFile->zUuid==0 ) continue; fid = uuid_to_rid(pFile->zUuid, 0); md5sum_step_text(pFile->zName, -1); content_get(fid, &file); sqlite3_snprintf(sizeof(zBuf), zBuf, " %d\n", blob_size(&file)); md5sum_step_text(zBuf, -1); md5sum_step_blob(&file); blob_reset(&file); } if( pManOut ){ if( pManifest->zRepoCksum ){ blob_append(pManOut, pManifest->zRepoCksum, -1); }else{ blob_zero(pManOut); } } manifest_destroy(pManifest); md5sum_finish(pOut); } /* ** COMMAND: test-agg-cksum ** ** Display the aggregate checksum for content computed in several ** different ways. The aggregate checksum is used during "fossil commit" ** to double-check that the information about to be committed to the ** repository exactly matches the information currently in the check-out. */ void test_agg_cksum_cmd(void){ int vid; Blob hash, hash2; db_must_be_within_tree(); vid = db_lget_int("checkout", 0); vfile_aggregate_checksum_disk(vid, &hash); printf("disk: %s\n", blob_str(&hash)); blob_reset(&hash); vfile_aggregate_checksum_repository(vid, &hash); printf("archive: %s\n", blob_str(&hash)); blob_reset(&hash); vfile_aggregate_checksum_manifest(vid, &hash, &hash2); printf("manifest: %s\n", blob_str(&hash)); printf("recorded: %s\n", blob_str(&hash2)); } /* ** This routine recomputes certain columns of the vfile and vmerge tables ** when the associated repository is swapped out for a clone of the same ** project, and the blob.rid value change. The following columns are ** updated: ** ** vmerge.merge ** vfile.vid ** vfile.rid ** vfile.mrid ** ** Also: ** ** vvar.value WHERE name='checkout' */ void vfile_rid_renumbering_event(int dryRun){ int oldVid; int newVid; char *zUnresolved; oldVid = db_lget_int("checkout", 0); newVid = db_int(0, "SELECT blob.rid FROM blob, vvar" " WHERE blob.uuid=vvar.value" " AND vvar.name='checkout-hash'"); /* The idMap table will make old RID values into new ones */ db_multi_exec( "CREATE TEMP TABLE idMap(oldrid INTEGER PRIMARY KEY, newrid INT);\n" ); /* Add the RID value for the current check-out */ db_multi_exec( "INSERT INTO idMap(oldrid, newrid) VALUES(%d,%d)", oldVid, newVid ); /* Add the RID values for any other check-ins that have been merged into ** the current check-out. */ db_multi_exec( "INSERT OR IGNORE INTO idMap(oldrid, newrid)" " SELECT vmerge.merge, blob.rid FROM vmerge, blob" " WHERE blob.uuid=vmerge.mhash;" ); /* Add RID values for files in the current check-out */ db_multi_exec( "CREATE TEMP TABLE hashoffile(name TEXT PRIMARY KEY, hash TEXT)" "WITHOUT ROWID;" "INSERT INTO hashoffile(name,hash)" " SELECT filename, uuid FROM vvar, files_of_checkin(vvar.value)" " WHERE vvar.name='checkout-hash';" "INSERT OR IGNORE INTO idMap(oldrid, newrid)" " SELECT vfile.rid, blob.rid FROM vfile, hashoffile, blob" " WHERE hashoffile.name=coalesce(vfile.origname,vfile.pathname)" " AND blob.uuid=hashoffile.hash;" ); /* Add RID values for merged-in files */ db_multi_exec( "INSERT OR IGNORE INTO idMap(oldrid, newrid)" " SELECT vfile.mrid, blob.rid FROM vfile, blob" " WHERE blob.uuid=vfile.mhash;" ); if( dryRun ){ Stmt q; db_prepare(&q, "SELECT oldrid, newrid, blob.uuid" " FROM idMap, blob WHERE blob.rid=idMap.newrid"); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%8d -> %8d %.25s\n", db_column_int(&q,0), db_column_int(&q,1), db_column_text(&q,2)); } db_finalize(&q); } /* Verify that all RID values in the VFILE table and VMERGE table have ** been resolved. */ zUnresolved = db_text("", "WITH allrid(x) AS (" " SELECT rid FROM vfile" " UNION SELECT mrid FROM vfile" " UNION SELECT merge FROM vmerge" " UNION SELECT %d" ")" "SELECT group_concat(x,' ') FROM allrid" " WHERE x<>0 AND x NOT IN (SELECT oldrid FROM idMap);", oldVid ); if( zUnresolved[0] ){ fossil_fatal("Unresolved RID values: %s\n" "\n" "Local check-out database is out of sync with repository file:\n" "\n" " %s\n" "\n" "Has the repository file been replaced?\n", zUnresolved, db_repository_filename()); } /* Make the changes to the VFILE and VMERGE tables */ if( !dryRun ){ db_multi_exec( "UPDATE vfile" " SET rid=(SELECT newrid FROM idMap WHERE oldrid=vfile.rid)" " WHERE vid=%d AND rid>0;", oldVid); db_multi_exec( "UPDATE vfile" " SET mrid=(SELECT newrid FROM idMap WHERE oldrid=vfile.mrid)" " WHERE vid=%d AND mrid>0;", oldVid); db_multi_exec( "UPDATE vfile" " SET vid=%d" " WHERE vid=%d", newVid, oldVid); db_multi_exec( "UPDATE vmerge" " SET merge=(SELECT newrid FROM idMap WHERE oldrid=vmerge.merge);"); db_lset_int("checkout",newVid); } /* Clear out the TEMP tables we constructed */ db_multi_exec( "DROP TABLE idMap;" "DROP TABLE hashoffile;" ); } |
Changes to src/wiki.c.
1 2 3 4 5 6 7 | /* ** Copyright (c) 2007 D. Richard Hipp ** Copyright (c) 2008 Stephan 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".) | | > < | | | | | | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | > > < | | > > < < < | < | | < < < < | < < < < < < < < < < < < > < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < < < < < < < < < < < < < < < < | | | | | | | > > > > > > > > > | > | < > > > > > > > | > > | > > > | > > > | > | > | | > > > > > > > > > | > > > > > > > > > > > > | > > > > > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | > > > | > > | > > > > > | > > > > > > > > > > > > > > > > | > | | < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > > | > > > > > > | | > < > > > > > > > > > > | > | > > > | > > > | > > > | > > > > > > > | > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > | > > > > | > > > > > > > > > > | > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | > | > > > > > > > > > | > > > > > > > > > > > > > > | > > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | > | > > | < | | > > | | | | | > > > | | | < > | > > > > | | | | > > > > > > | > > | > > < | | > > > > > > > > > | | > | < | < < > | > > > < < < < < < < < < < < < | < < | | | < | | > > > | | | | | | | | | | | | | | < < < < | < > > | > > > | | > > > > > > | | > | | < > > | | > < < | < < < < | < < < < < < < < > > > > < < > > > | > | < > | | > > > | > | > | < < > | | < | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | < | > > > > > > > > > | < | < | > > < | > | | | | < < | > > | | | | < < | < > > | > | < | > > > > > > > > | < < < < | > > | > > > > > > > | < | < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > > > > > | > > > > | < > > | < < | > > | > > > > | > | > > > > > | > > > > > > > > | > > | > > > > | > > > > > > | > > > > < > | | | > > | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | < > > > > | > < < < < < < < < < < < < < < < | < > > > > | | | < < < < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > | | < > > | < | > > > > > > > > > > > > > | | | < > | | > > > > > > > > > > | | | | | | > > > | > > > | > | | < | | > > > > > | > > > > > > > < > > > > > < > > | > > > | | | > | | | > | > > > > > > > > > > > > > | | | | < < < | < | < | | > | | | | | | | < | < < < < < | > > > > | > > > > | | | > > > > | | > > > > > > > > > > > > | < < | < < > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > | | < | | > > > > > > | > > > | > > > > > > > > > | | | | > > > > > > > | > > | > > | | > > > > > > > > > > > > > > | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Copyright (c) 2007 D. Richard Hipp ** Copyright (c) 2008 Stephan 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 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. */ static int check_name(const char *z){ if( !wiki_name_is_wellformed((const unsigned char *)z) ){ style_set_current_feature("wiki"); style_header("Wiki Page Name Error"); @ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed. @ Rules for wiki page names: well_formed_wiki_name_rules(); style_finish_page(); return 1; } return 0; } /* ** Return the tagid associated with a particular wiki page. */ int wiki_tagid(const char *zPageName){ return db_int(0, "SELECT tagid FROM tag WHERE tagname='wiki-%q'",zPageName); } int wiki_tagid2(const char *zPrefix, const char *zPageName){ return db_int(0, "SELECT tagid FROM tag WHERE tagname='wiki-%q/%q'", zPrefix, zPageName); } /* ** Return the RID of the next or previous version of a wiki page. ** Return 0 if rid is the last/first version. */ int wiki_next(int tagid, double mtime){ return db_int(0, "SELECT srcid FROM tagxref" " WHERE tagid=%d AND mtime>%.16g" " ORDER BY mtime ASC LIMIT 1", tagid, mtime); } int wiki_prev(int tagid, double mtime){ return db_int(0, "SELECT srcid FROM tagxref" " WHERE tagid=%d AND mtime<%.16g" " ORDER BY mtime DESC LIMIT 1", tagid, mtime); } /* ** WEBPAGE: home ** WEBPAGE: index ** WEBPAGE: not_found ** ** The /home, /index, and /not_found pages all redirect to the homepage ** configured by the administrator. */ void home_page(void){ char *zPageName = db_get("project-name",0); char *zIndexPage = db_get("index-page",0); login_check_credentials(); cgi_check_for_malice(); if( zIndexPage ){ const char *zPathInfo = P("PATH_INFO"); while( zIndexPage[0]=='/' ) zIndexPage++; while( zPathInfo[0]=='/' ) zPathInfo++; if( fossil_strcmp(zIndexPage, zPathInfo)==0 ) zIndexPage = 0; } if( zIndexPage ){ cgi_redirectf("%R/%s", zIndexPage); } if( !g.perm.RdWiki ){ cgi_redirectf("%R/login?g=home"); } if( zPageName ){ login_check_credentials(); g.zExtra = zPageName; cgi_set_parameter_nocopy("name", g.zExtra, 1); g.isHome = 1; wiki_page(); return; } style_set_current_feature("wiki"); style_header("Home"); @ <p>This is a stub home-page for the project. @ To fill in this page, first go to @ %z(href("%R/setup_config"))setup/config</a> @ and establish a "Project Name". Then create a @ wiki page with that name. The content of that wiki page @ will be displayed in place of this message.</p> style_finish_page(); } /* ** Return true if the given pagename is the name of the sandbox */ static int is_sandbox(const char *zPagename){ return fossil_stricmp(zPagename,"sandbox")==0 || fossil_stricmp(zPagename,"sand box")==0; } /* ** Formal, common and short names for the various wiki styles. */ static const char *const azStyles[] = { "text/x-fossil-wiki", "Fossil Wiki", "wiki", "text/x-markdown", "Markdown", "markdown", "text/plain", "Plain Text", "plain" }; /* ** Only allow certain mimetypes through. ** All others become "text/x-fossil-wiki" */ const char *wiki_filter_mimetypes(const char *zMimetype){ if( zMimetype!=0 ){ int i; for(i=0; i<count(azStyles); i+=3){ if( fossil_strcmp(zMimetype,azStyles[i+2])==0 ){ return azStyles[i]; } } if( fossil_strcmp(zMimetype, "text/x-markdown")==0 || fossil_strcmp(zMimetype, "text/plain")==0 ){ return zMimetype; } } return "text/x-fossil-wiki"; } /* ** Render wiki text according to its mimetype. ** ** text/x-fossil-wiki Fossil wiki ** text/x-markdown Markdown ** text/x-pikchr Pikchr ** anything else... Plain text ** ** If zMimetype is a null pointer, then use "text/x-fossil-wiki". */ 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 tail = BLOB_INITIALIZER; markdown_to_html(pWiki, 0, &tail); safe_html(&tail); @ %s(blob_str(&tail)) blob_reset(&tail); }else if( fossil_strcmp(zMimetype, "text/x-pikchr")==0 ){ int isPopup = P("popup")!=0; const char *zPikchr = blob_str(pWiki); int w, h; char *zOut = pikchr(zPikchr, "pikchr", 0, &w, &h); if( w>0 ){ if( isPopup ) cgi_set_content_type("image/svg+xml"); else{ @ <div class="pikchr-svg" style="max-width:%d(w)px"> } @ %s(zOut) if( !isPopup){ @ </div> } }else{ @ <pre class='error'> @ %h(zOut) @ </pre> } free(zOut); }else{ @ <pre class='textPlain'> @ %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_set_current_feature("wiki"); style_header("Markdown Formatting Rules"); if( fTxt ){ style_submenu_element("Formatted", "%R/md_rules"); }else{ style_submenu_element("Plain-Text", "%R/md_rules?txt=1"); } style_submenu_element("Wiki", "%R/wiki_rules"); blob_init(&x, builtin_text("markdown.md"), -1); blob_materialize(&x); interwiki_append_map_table(&x); safe_html_context(DOCSRC_TRUSTED); wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-markdown"); blob_reset(&x); style_finish_page(); } /* ** WEBPAGE: wiki_rules ** ** Show a summary of the wiki formatting rules. */ void wiki_rules_page(void){ Blob x; int fTxt = P("txt")!=0; style_set_current_feature("wiki"); style_header("Wiki Formatting Rules"); if( fTxt ){ style_submenu_element("Formatted", "%R/wiki_rules"); }else{ style_submenu_element("Plain-Text", "%R/wiki_rules?txt=1"); } style_submenu_element("Markdown","%R/md_rules"); blob_init(&x, builtin_text("wiki.wiki"), -1); blob_materialize(&x); interwiki_append_map_table(&x); safe_html_context(DOCSRC_TRUSTED); wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-fossil-wiki"); blob_reset(&x); style_finish_page(); } /* ** WEBPAGE: markup_help ** ** Show links to the md_rules and wiki_rules pages. */ void markup_help_page(void){ style_set_current_feature("wiki"); style_header("Fossil Markup Styles"); @ <ul> @ <li><p>%z(href("%R/wiki_rules"))Fossil Wiki Formatting Rules</a></p></li> @ <li><p>%z(href("%R/md_rules"))Markdown Formatting Rules</a></p></li> @ </ul> style_finish_page(); } /* ** 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", "%R/wikisrch"); } if( (ok & W_LIST)!=0 ){ style_submenu_element("List", "%R/wcontent"); } if( (ok & W_HELP)!=0 ){ style_submenu_element("Help", "%R/wikihelp"); } if( (ok & W_NEW)!=0 && g.anon.NewWiki ){ style_submenu_element("New", "%R/wikinew"); } if( (ok & W_SANDBOX)!=0 ){ style_submenu_element("Sandbox", "%R/wikiedit?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_set_current_feature("wiki"); style_header("Wiki Help"); wiki_standard_submenu(W_ALL_BUT(W_HELP)); @ <h2>Wiki Links</h2> @ <ul> @ <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/wikiedit?name=Sandbox"))Sandbox</a> @ to experiment.</li> if( g.perm.NewWiki ){ @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li> if( g.perm.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> @ <li> %z(href("%R/timeline?y=e"))List of All Tech-notes</a> @ available on this server.</li> if( g.perm.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_finish_page(); return; } /* ** WEBPAGE: wikisrch ** Usage: /wikisrch?s=PATTERN ** ** Full-text search of all current wiki text */ void wiki_srchpage(void){ login_check_credentials(); style_set_current_feature("wiki"); style_header("Wiki Search"); wiki_standard_submenu(W_HELP|W_LIST|W_SANDBOX); search_screen(SRCH_WIKI, 0); style_finish_page(); } /* Return values from wiki_page_type() */ #if INTERFACE # define WIKITYPE_UNKNOWN (-1) # define WIKITYPE_NORMAL 0 # define WIKITYPE_BRANCH 1 # define WIKITYPE_CHECKIN 2 # define WIKITYPE_TAG 3 #endif /* ** Figure out what type of wiki page we are dealing with. */ int wiki_page_type(const char *zPageName){ if( db_get_boolean("wiki-about",1)==0 ){ return WIKITYPE_NORMAL; }else if( sqlite3_strglob("checkin/*", zPageName)==0 && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) ){ return WIKITYPE_CHECKIN; }else if( sqlite3_strglob("branch/*", zPageName)==0 ){ return WIKITYPE_BRANCH; }else if( sqlite3_strglob("tag/*", zPageName)==0 ){ return WIKITYPE_TAG; } return WIKITYPE_NORMAL; } /* ** Returns a JSON-friendly string form of the integer value returned ** by wiki_page_type(zPageName). */ const char * wiki_page_type_name(const char *zPageName){ switch(wiki_page_type(zPageName)){ case WIKITYPE_CHECKIN: return "checkin"; case WIKITYPE_BRANCH: return "branch"; case WIKITYPE_TAG: return "tag"; case WIKITYPE_NORMAL: default: return "normal"; } } /* ** Add an appropriate style_header() for either the /wiki or /wikiedit page ** for zPageName. zExtra is an empty string for /wiki but has the text ** "Edit: " for /wikiedit. ** ** If the page is /wiki and the page is one of the special times (check-in, ** branch, or tag) and the "p" query parameter is omitted, then do a ** redirect to the display of the check-in, branch, or tag rather than ** continuing to the plain wiki display. */ static int wiki_page_header( int eType, /* Page type. Might be WIKITYPE_UNKNOWN */ const char *zPageName, /* Name of the page */ const char *zExtra /* Extra prefix text on the page header */ ){ style_set_current_feature("wiki"); if( eType==WIKITYPE_UNKNOWN ) eType = wiki_page_type(zPageName); switch( eType ){ case WIKITYPE_NORMAL: { style_header("%s%s", zExtra, zPageName); break; } case WIKITYPE_CHECKIN: { zPageName += 8; if( zExtra[0]==0 && !P("p") ){ cgi_redirectf("%R/info/%s",zPageName); }else{ style_header("Notes About Check-in %S", zPageName); style_submenu_element("Check-in Timeline","%R/timeline?f=%s", zPageName); style_submenu_element("Check-in Info","%R/info/%s", zPageName); } break; } case WIKITYPE_BRANCH: { zPageName += 7; if( zExtra[0]==0 && !P("p") ){ cgi_redirectf("%R/timeline?r=%t", zPageName); }else{ style_header("Notes About Branch %h", zPageName); style_submenu_element("Branch Timeline","%R/timeline?r=%t", zPageName); } break; } case WIKITYPE_TAG: { zPageName += 4; if( zExtra[0]==0 && !P("p") ){ cgi_redirectf("%R/timeline?t=%t",zPageName); }else{ style_header("Notes About Tag %h", zPageName); style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName); } break; } } return eType; } /* ** Wiki pages with special names "branch/...", "checkin/...", and "tag/..." ** requires perm.Write privilege in addition to perm.WrWiki in order ** to write. This function determines whether the extra perm.Write ** is required and available. Return true if writing to the wiki page ** may proceed, and return false if permission is lacking. */ static int wiki_special_permission(const char *zPageName){ if( strncmp(zPageName,"branch/",7)!=0 && strncmp(zPageName,"checkin/",8)!=0 && strncmp(zPageName,"tag/",4)!=0 ){ return 1; } if( db_get_boolean("wiki-about",1)==0 ){ return 1; } return g.perm.Write; } /* ** WEBPAGE: wiki ** ** Display a wiki page. Example: /wiki?name=PAGENAME ** ** Query parameters: ** ** name=NAME Name of the wiki page to display. Required. ** nsm Omit the submenu if present. (Mnemonic: No SubMenu) ** p Always show just the wiki page. For special ** pages for check-ins, branches, or tags, there will ** be a redirect to the associated /info page unless ** this query parameter is present. ** popup Suppress the header and footer and other page ** boilerplate and only return the formatted content ** of the wiki page. */ void wiki_page(void){ char *zTag; int rid = 0; int isSandbox; unsigned submenuFlags = W_HELP; Blob wiki; Manifest *pWiki = 0; const char *zPageName; const char *zMimetype = 0; int isPopup = P("popup")!=0; char *zBody = mprintf("%s","<i>Empty Page</i>"); int noSubmenu = P("nsm")!=0 || g.isHome; login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } zPageName = P("name"); (void)P("s")/*for cgi_check_for_malice(). "s" == search stringy*/; cgi_check_for_malice(); 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( !noSubmenu ){ if( ((rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki)) && wiki_special_permission(zPageName) ){ style_submenu_element("Edit", "%R/wikiedit?name=%T", zPageName); }else if( rid && g.perm.ApndWiki ){ style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName); } if( g.perm.Hyperlink ){ style_submenu_element("History", "%R/whistory?name=%T", zPageName); } } if( !isPopup ){ style_set_current_page("%T?name=%T", g.zPath, zPageName); wiki_page_header(WIKITYPE_UNKNOWN, zPageName, ""); if( !noSubmenu ){ wiki_standard_submenu(submenuFlags); } } if( zBody[0]==0 ){ @ <i>This page has been deleted</i> }else{ blob_init(&wiki, zBody, -1); safe_html_context(DOCSRC_WIKI); wiki_render_by_mimetype(&wiki, zMimetype); blob_reset(&wiki); } manifest_destroy(pWiki); if( !isPopup ){ char * zLabel = mprintf("<hr><h2><a href='%R/attachlist?name=%T'>" "Attachments</a>:</h2><ul>", zPageName); attachment_list(zPageName, zLabel); fossil_free(zLabel); document_emit_js(/*for optional pikchr support*/); style_finish_page(); } } /* ** Write a wiki artifact into the repository */ int 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, 1, 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_add_unsent(nrid); db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid); manifest_crosslink(nrid, pWiki, MC_NONE); if( login_is_individual() ){ alert_user_contact(login_name()); } return nrid; } /* ** Output a selection box from which the user can select the ** wiki mimetype. Arguments: ** ** zMimetype - The current value of the query parameter ** zParam - The name of the query parameter */ void mimetype_option_menu(const char *zMimetype, const char *zParam){ unsigned i; @ <select name="%s(zParam)" size="1"> for(i=0; i<count(azStyles); i+=3){ 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> } } @ </select> } /* ** Given a mimetype, return its common name. */ static const char *mimetype_common_name(const char *zMimetype){ int i; for(i=6; i>=0; i-=3){ if( zMimetype && fossil_strcmp(zMimetype, azStyles[i])==0 ){ return azStyles[i+1]; } } return azStyles[1]; } /* ** Tries to fetch a wiki page for the given name. If found, it ** returns true, else false. ** ** versionsBack specifies how many versions back in the history to ** fetch. Use 0 for the latest version, 1 for its parent, etc. ** ** If pRid is not NULL then if a result is found *pRid is set to its ** RID. If ppWiki is not NULL then if found *ppWiki is set to the ** loaded wiki object, which the caller is responsible for passing to ** manifest_destroy(). */ static int wiki_fetch_by_name( const char *zPageName, unsigned int versionsBack, int * pRid, Manifest **ppWiki ){ Manifest *pWiki = 0; char *zTag = mprintf("wiki-%s", zPageName); Stmt q = empty_Stmt; int rid = 0; db_prepare(&q, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE" " tagname=%Q) " " ORDER BY mtime DESC LIMIT -1 OFFSET %u", zTag, versionsBack); fossil_free(zTag); if(SQLITE_ROW == db_step(&q)){ rid = db_column_int(&q, 0); } db_finalize(&q); if( rid == 0 ){ return 0; } else if(pRid){ *pRid = rid; } if(ppWiki){ pWiki = manifest_get(rid, CFTYPE_WIKI, 0); if( pWiki==0 ){ /* "Cannot happen." */ return 0; } *ppWiki = pWiki; } return 1; } /* ** Determines whether the wiki page with the given name can be edited ** or created by the current user. If not, an AJAX error is queued and ** false is returned, else true is returned. A NULL, empty, or ** malformed name is considered non-writable, regardless of the user. ** ** If pRid is not NULL then this function writes the page's rid to ** *pRid (whether or not access is granted). On error or if the page ** does not yet exist, *pRid will be set to 0. ** ** Note that the sandbox is a special case: it is a pseudo-page with ** no rid and the /wikiajax API does not allow anyone to actually save ** a sandbox page, but it is reported as writable here (with rid 0). */ static int wiki_ajax_can_write(const char *zPageName, int * pRid){ int rid = 0; const char * zErr = 0; if(pRid) *pRid = 0; if(!zPageName || !*zPageName || !wiki_name_is_wellformed((unsigned const char *)zPageName)){ zErr = "Invalid page name."; }else if(is_sandbox(zPageName)){ return 1; }else{ wiki_fetch_by_name(zPageName, 0, &rid, 0); if(pRid) *pRid = rid; if(!wiki_special_permission(zPageName)){ zErr = "Editing this page requires non-wiki write permissions."; }else if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){ return 3; }else if(rid && !g.perm.WrWiki){ zErr = "Requires wiki-write permissions."; }else if(!rid && !g.perm.NewWiki){ zErr = "Requires new-wiki permissions."; }else{ zErr = "Cannot happen! Please report this as a bug."; } } ajax_route_error(403, "%s", zErr); return 0; } /* ** Emits an array of attachment info records for the given wiki page ** artifact. ** ** Output format: ** ** [{ ** "uuid": attachment artifact hash, ** "src": hash of the attachment blob, ** "target": wiki page name or ticket/event ID, ** "filename": filename of attachment, ** "mtime": ISO-8601 timestamp UTC, ** "isLatest": true this is the latest version of this file ** else false, ** }, ...once per attachment] ** ** If there are no matching attachments then it will emit a JSON ** null (if nullIfEmpty) or an empty JSON array. ** ** If latestOnly is true then only the most recent entry for a given ** attachment is emitted, else all versions are emitted in descending ** mtime order. */ static void wiki_ajax_emit_page_attachments(Manifest * pWiki, int latestOnly, int nullIfEmpty){ int i = 0; Stmt q = empty_Stmt; db_prepare(&q, "SELECT datetime(mtime), src, target, filename, isLatest," " (SELECT uuid FROM blob WHERE rid=attachid) uuid" " FROM attachment" " WHERE target=%Q" " AND (isLatest OR %d)" " ORDER BY target, isLatest DESC, mtime DESC", pWiki->zWikiTitle, !latestOnly ); while(SQLITE_ROW == db_step(&q)){ const char * zTime = db_column_text(&q, 0); const char * zSrc = db_column_text(&q, 1); const char * zTarget = db_column_text(&q, 2); const char * zName = db_column_text(&q, 3); const int isLatest = db_column_int(&q, 4); const char * zUuid = db_column_text(&q, 5); if(!i++){ CX("["); }else{ CX(","); } CX("{"); CX("\"uuid\": %!j, \"src\": %!j, \"target\": %!j, " "\"filename\": %!j, \"mtime\": %!j, \"isLatest\": %s}", zUuid, zSrc, zTarget, zName, zTime, isLatest ? "true" : "false"); } db_finalize(&q); if(!i){ if(nullIfEmpty){ CX("null"); }else{ CX("[]"); } }else{ CX("]"); } } /* ** Proxy for wiki_ajax_emit_page_attachments() which attempts to load ** the given wiki page artifact. Returns true if it can load the given ** page, else false. If it returns false then it queues up a 404 ajax ** error response. */ static int wiki_ajax_emit_page_attachments2(const char *zPageName, int latestOnly, int nullIfEmpty){ Manifest * pWiki = 0; if( !wiki_fetch_by_name(zPageName, 0, 0, &pWiki) ){ ajax_route_error(404, "Wiki page could not be loaded: %s", zPageName); return 0; } wiki_ajax_emit_page_attachments(pWiki, latestOnly, nullIfEmpty); manifest_destroy(pWiki); return 1; } /* ** Loads the given wiki page, sets the response type to ** application/json, and emits it as a JSON object. If zPageName is a ** sandbox page then a "fake" object is emitted, as the wikiajax API ** does not permit saving the sandbox. ** ** Returns true on success, false on error, and on error it ** queues up a JSON-format error response. ** ** Output JSON format: ** ** { name: "page name", ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox", ** mimetype: "mimetype", ** version: UUID string or null for a sandbox page, ** parent: "parent uuid" or null if no parent, ** isDeleted: true if the page has no content (is "deleted") ** else not set (making it "falsy" in JS), ** attachments: see wiki_ajax_emit_page_attachments(), ** content: "page content" (only if includeContent is true) ** } ** ** If includeContent is false then the content member is elided. */ static int wiki_ajax_emit_page_object(const char *zPageName, int includeContent){ Manifest * pWiki = 0; char * zUuid; if( is_sandbox(zPageName) ){ char * zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki"); char * zBody = db_get("sandbox",""); CX("{\"name\": %!j, \"type\": \"sandbox\", " "\"mimetype\": %!j, \"version\": null, \"parent\": null", zPageName, zMimetype); if(includeContent){ CX(", \"content\": %!j", zBody); } CX("}"); fossil_free(zMimetype); fossil_free(zBody); return 1; }else if( !wiki_fetch_by_name(zPageName, 0, 0, &pWiki) ){ ajax_route_error(404, "Wiki page could not be loaded: %s", zPageName); return 0; }else{ zUuid = rid_to_uuid(pWiki->rid); CX("{\"name\": %!j, \"type\": %!j, " "\"version\": %!j, " "\"mimetype\": %!j, ", pWiki->zWikiTitle, wiki_page_type_name(pWiki->zWikiTitle), zUuid, pWiki->zMimetype ? pWiki->zMimetype : "text/x-fossil-wiki"); CX("\"parent\": "); if(pWiki->nParent){ CX("%!j", pWiki->azParent[0]); }else{ CX("null"); } if(!pWiki->zWiki || !pWiki->zWiki[0]){ CX(", \"isEmpty\": true"); } if(includeContent){ CX(", \"content\": %!j", pWiki->zWiki); } CX(", \"attachments\": "); wiki_ajax_emit_page_attachments(pWiki, 0, 1); CX("}"); fossil_free(zUuid); manifest_destroy(pWiki); return 2; } } /* ** Ajax route handler for /wikiajax/save. ** ** URL params: ** ** page = the wiki page name. ** mimetype = content mimetype. ** content = page content. Fossil considers an empty page to ** be "deleted". ** isnew = 1 if the page is to be newly-created, else 0 or ** not send. ** ** Responds with JSON. On error, an object in the form documented by ** ajax_route_error(). On success, an object in the form documented ** for wiki_ajax_emit_page_object(). ** ** The wikiajax API disallows saving of a sandbox pseudo-page, and ** will respond with an error if asked to save one. Should we want to ** enable it, it's implemented like this for any saved page for which ** is_sandbox(zPageName) is true: ** ** db_set("sandbox",zBody,0); ** db_set("sandbox-mimetype",zMimetype,0); ** */ static void wiki_ajax_route_save(void){ const char *zPageName = P("page"); const char *zMimetype = P("mimetype"); const char *zContent = P("content"); const int isNew = ajax_p_bool("isnew"); Blob content = empty_blob; int parentRid = 0; int rollback = 0; if(!wiki_ajax_can_write(zPageName, &parentRid)){ return; }else if(is_sandbox(zPageName)){ ajax_route_error(403,"Saving a sandbox page is prohibited."); return; } /* These isNew checks are just me being pedantic. We could just as easily derive isNew based on whether or not the page already exists. */ if(isNew){ if(parentRid>0){ ajax_route_error(403,"Requested a new page, " "but it already exists with RID %d: %s", parentRid, zPageName); return; } }else if(parentRid==0){ ajax_route_error(403,"Creating new page [%s] requires passing " "isnew=1.", zPageName); return; } blob_init(&content, zContent ? zContent : "", -1); cgi_set_content_type("application/json"); db_begin_transaction(); wiki_cmd_commit(zPageName, parentRid, &content, zMimetype, 0); rollback = wiki_ajax_emit_page_object(zPageName, 1) ? 0 : 1; db_end_transaction(rollback); } /* ** Ajax route handler for /wikiajax/fetch. ** ** URL params: ** ** page = the wiki page name ** ** Responds with JSON. On error, an object in the form documented by ** ajax_route_error(). On success, an object in the form documented ** for wiki_ajax_emit_page_object(). */ static void wiki_ajax_route_fetch(void){ const char * zPageName = P("page"); if( zPageName==0 || zPageName[0]==0 ){ ajax_route_error(400,"Missing page name."); return; } cgi_set_content_type("application/json"); wiki_ajax_emit_page_object(zPageName, 1); } /* ** Ajax route handler for /wikiajax/attachments. ** ** URL params: ** ** page = the wiki page name ** latestOnly = if set, only latest version of each attachment ** is emitted. ** ** Responds with JSON: see wiki_ajax_emit_page_attachments() ** ** If there are no attachments it emits an empty array instead of null ** so that the output can be used as a top-level JSON response. ** ** On error, an object in the form documented by ** ajax_route_error(). On success, an object in the form documented ** for wiki_ajax_emit_page_attachments(). */ static void wiki_ajax_route_attachments(void){ const char * zPageName = P("page"); const int fLatestOnly = P("latestOnly")!=0; if( zPageName==0 || zPageName[0]==0 ){ ajax_route_error(400,"Missing page name."); return; } cgi_set_content_type("application/json"); wiki_ajax_emit_page_attachments2(zPageName, fLatestOnly, 0); } /* ** Ajax route handler for /wikiajax/diff. ** ** URL params: ** ** page = the wiki page name ** content = the new/edited wiki page content ** ** Requires that the user have write access solely to avoid some ** potential abuse cases. It does not actually write anything. */ static void wiki_ajax_route_diff(void){ const char * zPageName = P("page"); Blob contentNew = empty_blob, contentOrig = empty_blob; Manifest * pParent = 0; const char * zContent = P("content"); u64 diffFlags = DIFF_HTML | DIFF_NOTTOOBIG | DIFF_STRIP_EOLCR; char * zParentUuid = 0; if( zPageName==0 || zPageName[0]==0 ){ ajax_route_error(400,"Missing page name."); return; }else if(!wiki_ajax_can_write(zPageName, 0)){ return; } switch(atoi(PD("sbs","0"))){ case 0: diffFlags |= DIFF_LINENO; break; default: diffFlags |= DIFF_SIDEBYSIDE; } switch(atoi(PD("ws","2"))){ case 1: diffFlags |= DIFF_IGNORE_EOLWS; break; case 2: diffFlags |= DIFF_IGNORE_ALLWS; break; default: break; } wiki_fetch_by_name( zPageName, 0, 0, &pParent ); if( pParent ){ zParentUuid = rid_to_uuid(pParent->rid); } if( pParent && pParent->zWiki && *pParent->zWiki ){ blob_init(&contentOrig, pParent->zWiki, -1); }else{ blob_init(&contentOrig, "", 0); } blob_init(&contentNew, zContent ? zContent : "", -1); cgi_set_content_type("text/html"); ajax_render_diff(&contentOrig, zParentUuid, &contentNew, diffFlags); blob_reset(&contentNew); blob_reset(&contentOrig); fossil_free(zParentUuid); manifest_destroy(pParent); } /* ** Ajax route handler for /wikiajax/preview. ** ** URL params: ** ** mimetype = the wiki page mimetype (determines rendering style) ** content = the wiki page content */ static void wiki_ajax_route_preview(void){ const char * zContent = P("content"); if( zContent==0 ){ ajax_route_error(400,"Missing content to preview."); return; }else{ Blob content = empty_blob; const char * zMimetype = PD("mimetype","text/x-fossil-wiki"); blob_init(&content, zContent, -1); cgi_set_content_type("text/html"); wiki_render_by_mimetype(&content, zMimetype); blob_reset(&content); } } /* ** Outputs the wiki page list in JSON form. If verbose is false then ** it emits an array of strings (page names). If verbose is true it outputs ** an array of objects in this form: ** ** { name: string, version: string or null of sandbox box, ** parent: uuid or null for first version or sandbox, ** mimetype: string, ** type: string (normal, branch, tag, check-in, or sandbox) ** } ** ** If includeContent is true, the object contains a "content" member ** with the raw page content. includeContent is ignored if verbose is ** false. ** */ static void wiki_render_page_list_json(int verbose, int includeContent){ Stmt q = empty_Stmt; int n = 0; db_begin_transaction(); db_prepare(&q, "SELECT" " substr(tagname,6) AS name" " FROM tag JOIN tagxref USING('tagid')" " WHERE tagname GLOB 'wiki-*'" " AND TYPEOF(tagxref.value+0)='integer'" /* ^^^ elide wiki- tags which are not wiki pages */ " UNION SELECT 'Sandbox' AS name" " ORDER BY name COLLATE NOCASE"); CX("["); while( SQLITE_ROW==db_step(&q) ){ char const * zName = db_column_text(&q,0); if(n++){ CX(","); } if(verbose==0){ CX("%!j", zName); }else{ wiki_ajax_emit_page_object(zName, includeContent); } } CX("]"); db_finalize(&q); db_end_transaction(0); } /* ** Ajax route handler for /wikiajax/list. ** ** Optional parameters: verbose, includeContent (see below). ** ** Responds with JSON. On error, an object in the form documented by ** ajax_route_error(). ** ** On success, it emits an array of strings (page names) sorted ** case-insensitively. If the "verbose" parameter is passed in then ** the result list contains objects in the format documented for ** wiki_ajax_emit_page_object(). The content of each object is elided ** unless the "includeContent" parameter is passed on with a ** "non-false" value.. ** ** The result list always contains an entry named "Sandbox" which ** represents the sandbox pseudo-page. */ static void wiki_ajax_route_list(void){ const int verbose = ajax_p_bool("verbose"); const int includeContent = ajax_p_bool("includeContent"); cgi_set_content_type("application/json"); wiki_render_page_list_json(verbose, includeContent); } /* ** WEBPAGE: wikiajax hidden ** ** An internal dispatcher for wiki AJAX operations. Not for direct ** client use. All routes defined by this interface are app-internal, ** subject to change */ void wiki_ajax_page(void){ const char * zName = P("name"); AjaxRoute routeName = {0,0,0,0}; const AjaxRoute * pRoute = 0; const AjaxRoute routes[] = { /* Keep these sorted by zName (for bsearch()) */ {"attachments", wiki_ajax_route_attachments, 0, 0}, {"diff", wiki_ajax_route_diff, 1, 1}, {"fetch", wiki_ajax_route_fetch, 0, 0}, {"list", wiki_ajax_route_list, 0, 0}, {"preview", wiki_ajax_route_preview, 0, 1}, {"save", wiki_ajax_route_save, 1, 1} }; if(zName==0 || zName[0]==0){ ajax_route_error(400,"Missing required [route] 'name' parameter."); return; } routeName.zName = zName; pRoute = (const AjaxRoute *)bsearch(&routeName, routes, count(routes), sizeof routes[0], cmp_ajax_route_name); if(pRoute==0){ ajax_route_error(404,"Ajax route not found."); return; } login_check_credentials(); if( pRoute->bWriteMode!=0 && g.perm.WrWiki==0 ){ ajax_route_error(403,"Write permissions required."); return; }else if( pRoute->bWriteMode==0 && g.perm.RdWiki==0 ){ ajax_route_error(403,"Read-Wiki permissions required."); return; }else if(0==cgi_csrf_safe(pRoute->bPost)){ ajax_route_error(403, "CSRF violation (make sure sending of HTTP " "Referer headers is enabled for XHR " "connections)."); return; } pRoute->xCallback(); } /* ** Emits a preview-toggle option widget for /wikiedit and /fileedit. */ void wikiedit_emit_toggle_preview(void){ CX("<div class='input-with-label'>" "<input type='checkbox' id='edit-shift-enter-preview' " "></input><label for='edit-shift-enter-preview'>" "Shift-enter previews</label>" "<div class='help-buttonlet'>" "When enabled, shift-enter switches between preview and edit modes. " "Some software-based keyboards misinteract with this, so it can be " "disabled when needed." "</div>" "</div>"); } /* ** WEBPAGE: wikiedit ** URL: /wikedit?name=PAGENAME ** ** The main front-end for the Ajax-based wiki editor app. Passing ** in the name of an unknown page will trigger the creation ** of a new page (which is not actually created in the database ** until the user explicitly saves it). If passed no page name, ** the user may select a page from the list on the first UI tab. ** ** When creating a new page, the mimetype URL parameter may optionally ** be used to set its mimetype to one of text/x-fossil-wiki, ** text/x-markdown, or text/plain, defaulting to the former. */ void wikiedit_page(void){ const char *zPageName; const char * zMimetype = P("mimetype"); int isSandbox; int found = 0; login_check_credentials(); zPageName = PD("name",""); if(zPageName && *zPageName){ if( check_name(zPageName) ) return; } isSandbox = is_sandbox(zPageName); if( isSandbox ){ if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } found = 1; }else if( zPageName!=0 && zPageName[0]!=0){ int rid = 0; if( !wiki_special_permission(zPageName) ){ login_needed(0); return; } found = wiki_fetch_by_name(zPageName, 0, &rid, 0); if( (rid && !g.perm.RdWiki) || (!rid && !g.perm.NewWiki) ){ login_needed(rid ? g.anon.RdWiki : g.anon.NewWiki); return; } }else{ if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } } style_set_current_feature("wiki"); style_header("Wiki Editor"); style_emit_noscript_for_js_page(); /* Status bar */ CX("<div id='fossil-status-bar' " "title='Status message area. Double-click to clear them.'>" "Status messages will go here.</div>\n" /* will be moved into the tab container via JS */); CX("<div id='wikiedit-edit-status''>" "<span class='name'></span>" "<span class='links'></span>" "</div>"); /* Main tab container... */ CX("<div id='wikiedit-tabs' class='tab-container'>Loading...</div>"); /* The .hidden class on the following tab elements is to help lessen the FOUC effect of the tabs before JS re-assembles them. */ /******* Page list *******/ { CX("<div id='wikiedit-tab-pages' " "data-tab-parent='wikiedit-tabs' " "data-tab-label='Wiki Page List' " "class='hidden'" ">"); CX("<div>Loading wiki pages list...</div>"); CX("</div>"/*#wikiedit-tab-pages*/); } /******* Content tab *******/ { CX("<div id='wikiedit-tab-content' " "data-tab-parent='wikiedit-tabs' " "data-tab-label='Editor' " "class='hidden'" ">"); CX("<div class='" "wikiedit-options flex-container flex-row child-gap-small'>"); CX("<div class='input-with-label'>" "<label>Mime type</label>"); mimetype_option_menu("text/x-markdown", "mimetype"); CX("</div>"); style_select_list_int("select-font-size", "editor_font_size", "Editor font size", NULL/*tooltip*/, 100, "100%", 100, "125%", 125, "150%", 150, "175%", 175, "200%", 200, NULL); CX("<div class='input-with-label'>" /*will get moved around dynamically*/ "<button class='wikiedit-save'>" "Save</button>" "<button class='wikiedit-save-close'>" "Save & Close</button>" "<div class='help-buttonlet'>" "Save edits to this page and optionally return " "to the wiki page viewer." "</div>" "</div>" /*will get moved around dynamically*/); CX("<span class='save-button-slot'></span>"); CX("<div class='input-with-label'>" "<button class='wikiedit-content-reload' " ">Discard & Reload</button>" "<div class='help-buttonlet'>" "Reload the file from the server, discarding " "any local edits. To help avoid accidental loss of " "edits, it requires confirmation (a second click) within " "a few seconds or it will not reload." "</div>" "</div>"); wikiedit_emit_toggle_preview(); CX("</div>"); CX("<div class='flex-container flex-column stretch'>"); CX("<textarea name='content' id='wikiedit-content-editor' " "class='wikiedit' rows='25'>"); CX("</textarea>"); CX("</div>"/*textarea wrapper*/); CX("</div>"/*#tab-file-content*/); } /****** Preview tab ******/ { CX("<div id='wikiedit-tab-preview' " "data-tab-parent='wikiedit-tabs' " "data-tab-label='Preview' " "class='hidden'" ">"); CX("<div class='wikiedit-options flex-container " "flex-row child-gap-small'>"); CX("<button id='btn-preview-refresh' " "data-f-preview-from='wikiContent' " /* ^^^ fossil.page[methodName]() OR text source elem ID, ** but we need a method in order to support clients swapping out ** the text editor with their own. */ "data-f-preview-via='_postPreview' " /* ^^^ fossil.page[methodName](content, callback) */ "data-f-preview-to='_previewTo' " /* ^^^ dest elem ID or fossil.page[methodName]*/ ">Refresh</button>"); /* Toggle auto-update of preview when the Preview tab is selected. */ CX("<div class='input-with-label'>" "<input type='checkbox' value='1' " "id='cb-preview-autorefresh' checked>" "<label for='cb-preview-autorefresh'>Auto-refresh?</label>" "</div>"); CX("<span class='save-button-slot'></span>"); CX("</div>"/*.wikiedit-options*/); CX("<div id='wikiedit-tab-preview-wrapper'></div>"); CX("</div>"/*#wikiedit-tab-preview*/); } /****** Diff tab ******/ { CX("<div id='wikiedit-tab-diff' " "data-tab-parent='wikiedit-tabs' " "data-tab-label='Diff' " "class='hidden'" ">"); CX("<div class='wikiedit-options flex-container " "flex-row child-gap-small' " "id='wikiedit-tab-diff-buttons'>"); CX("<div class='input-with-label'>" "<button class='sbs'>Side-by-side</button>" "<button class='unified'>Unified</button>" "</div>"); CX("<span class='save-button-slot'></span>"); CX("</div>"); CX("<div id='wikiedit-tab-diff-wrapper'>" "Diffs will be shown here." "</div>"); CX("</div>"/*#wikiedit-tab-diff*/); } /****** The obligatory "Misc" tab ******/ { CX("<div id='wikiedit-tab-misc' " "data-tab-parent='wikiedit-tabs' " "data-tab-label='Misc.' " "class='hidden'" ">"); CX("<fieldset id='attachment-wrapper'>"); CX("<legend>Attachments</legend>"); CX("<div>No attachments for the current page.</div>"); CX("</fieldset>"); CX("<h2>Wiki formatting rules</h2>"); CX("<ul>"); CX("<li><a href='%R/wiki_rules'>Fossil wiki format</a></li>"); CX("<li><a href='%R/md_rules'>Markdown format</a></li>"); CX("<li>Plain-text pages use no special formatting.</li>"); CX("</ul>"); CX("<h2>The \"Sandbox\" Page</h2>"); CX("<p>The page named \"Sandbox\" is not a real wiki page. " "It provides a place where users may test out wiki syntax " "without having to actually save anything, nor pollute " "the repo with endless test runs. Any attempt to save the " "sandbox page will fail.</p>"); CX("<h2>Wiki Name Rules</h2>"); well_formed_wiki_name_rules(); CX("</div>"/*#wikiedit-tab-save*/); } builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", "storage", "popupwidget", "copybutton", "pikchr", NULL); builtin_fossil_js_bundle_or("diff", NULL); builtin_request_js("fossil.page.wikiedit.js"); builtin_fulfill_js_requests(); /* Dynamically populate the editor... */ style_script_begin(__FILE__,__LINE__); { /* Render the current page list to save us an XHR request during page initialization. This must be OUTSIDE of an onPageLoad() handler or else it does not get applied until after the wiki list widget is initialized. Similarly, it must come *after* window.fossil is initialized. */ CX("\nfossil.page.initialPageList = "); wiki_render_page_list_json(1, 0); CX(";\n"); } CX("fossil.onPageLoad(function(){\n"); CX("const P = fossil.page;\n" "try{\n"); if(!found && zPageName && *zPageName){ /* For a new page, stick a dummy entry in the JS-side stash and "load" it from there. */ CX("const winfo = {" "\"name\": %!j, \"mimetype\": %!j, " "\"type\": %!j, " "\"parent\": null, \"version\": null" "};\n", zPageName, zMimetype ? zMimetype : "text/x-fossil-wiki", wiki_page_type_name(zPageName)); /* If the JS-side stash already has this page, load that copy from the stash, otherwise inject a new stash entry for it and load *that* one... */ CX("if(!P.$stash.getWinfo(winfo)){" "P.$stash.updateWinfo(winfo,'');" "}\n"); } if(zPageName && *zPageName){ CX("P.loadPage(%!j);\n", zPageName); } CX("}catch(e){" "fossil.error(e); console.error('Exception:',e);" "}\n"); CX("});\n"/*fossil.onPageLoad()*/); style_script_end(); style_finish_page(); } /* ** WEBPAGE: wikinew ** URL /wikinew ** ** Prompt the user to enter the name of a new wiki page. Then redirect ** 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) ){ cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype); } style_set_current_feature("wiki"); 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> @ %z(href("%R/markup_help"))Markup style</a>: mimetype_option_menu("text/x-markdown", "mimetype"); @ <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> } style_finish_page(); } /* ** Append the wiki text for an remark to the end of the given BLOB. */ static void appendRemark(Blob *p, const char *zMimetype){ char *zDate; const char *zUser; const char *zRemark; 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); } /* ** WEBPAGE: wikiappend ** URL: /wikiappend?name=PAGENAME&mimetype=MIMETYPE ** ** Append text to the end of a wiki page. */ void wikiappend_page(void){ char *zTag; int rid = 0; const char *zPageName; const char *zUser; const char *zMimetype; int goodCaptcha = 1; const char *zFormat; Manifest *pWiki = 0; int isSandbox; login_check_credentials(); if( !g.perm.ApndWiki ){ login_needed(g.anon.ApndWiki); return; } 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); pWiki = rid ? manifest_get(rid, CFTYPE_WIKI, 0) : 0; if( !pWiki ){ fossil_redirect_home(); return; } zMimetype = wiki_filter_mimetypes(pWiki->zMimetype) /* see https://fossil-scm.org/forum/forumpost/0acfdaac80 */; } if( !isSandbox && P("submit")!=0 && P("r")!=0 && P("u")!=0 && (goodCaptcha = captcha_is_correct(0)) && cgi_csrf_safe(2) ){ char *zDate; Blob cksum; Blob body; Blob wiki; blob_zero(&body); blob_append(&body, pWiki->zWiki, -1); blob_zero(&wiki); db_begin_transaction(); zDate = date_in_standard_format("now"); blob_appendf(&wiki, "D %s\n", zDate); blob_appendf(&wiki, "L %F\n", zPageName); if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")!=0 ){ 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); manifest_destroy(pWiki); cgi_redirectf("wiki?name=%T", zPageName); return; } if( !isSandbox && P("cancel")!=0 ){ manifest_destroy(pWiki); cgi_redirectf("wiki?name=%T", zPageName); return; } style_set_current_page("%T?name=%T", g.zPath, zPageName); style_set_current_feature("wiki"); style_header("Append Comment To: %s", zPageName); if( !goodCaptcha ){ @ <p class="generalError">Error: Incorrect security code.</p> } if( isSandbox ){ @ <p class="generalError">Error: the Sandbox page may not @ be appended to.</p> } if( !isSandbox && P("preview")!=0 ){ Blob preview; blob_zero(&preview); appendRemark(&preview, zMimetype); @ Preview:<hr> safe_html_context(DOCSRC_WIKI); wiki_render_by_mimetype(&preview, zMimetype); @ <hr> blob_reset(&preview); } zUser = PD("u", g.zLogin); form_begin(0, "%R/wikiappend"); @ <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> manifest_destroy(pWiki); style_finish_page(); } /* ** WEBPAGE: whistory ** URL: /whistory?name=PAGENAME ** ** Additional parameters: ** ** showid Show RID values ** ** Show the complete change history for a single wiki page. */ void whistory_page(void){ Stmt q; const char *zPageName; double rNow; int showRid; char zAuthor[64]; login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } zPageName = PD("name",""); style_set_current_feature("wiki"); style_header("History Of %s", zPageName); showRid = P("showid")!=0; db_prepare(&q, "SELECT" " event.mtime," " blob.uuid," " coalesce(event.euser,event.user)," " event.objid," " datetime(event.mtime)" " FROM event, blob, tag, tagxref" " WHERE event.type='w' AND blob.rid=event.objid" " AND tag.tagname='wiki-%q'" " AND tagxref.tagid=tag.tagid AND tagxref.srcid=event.objid" " ORDER BY event.mtime DESC", zPageName ); @ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2> form_begin( "id='wh-form'", "%R/wdiff" ); @ <input id="wh-pid" name="pid" type="radio" hidden> @ <input id="wh-id" name="id" type="hidden"> @ </form> @ <style> .wh-clickable { cursor: pointer; } </style> @ <div class="brlist"> @ <table> @ <thead><tr> @ <th>Age</th> @ <th>Hash</th> @ <th><span title="Baseline from which diffs are computed (click to unset)" @ id="wh-cleaner" class="wh-clickable">⚓</span></th> @ <th>User<span hidden class="wh-clickable" @ id="wh-collapser"> ♲</span></th> if( showRid ){ @ <th>RID</th> } @ <th> </th> @ </tr></thead><tbody> rNow = db_double(0.0, "SELECT julianday('now')"); memset( zAuthor, 0, sizeof(zAuthor) ); while( db_step(&q)==SQLITE_ROW ){ double rMtime = db_column_double(&q, 0); const char *zUuid = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); int wrid = db_column_int(&q, 3); const char *zWhen = db_column_text(&q, 4); /* sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0); */ char *zAge = human_readable_age(rNow - rMtime); if( strncmp( zAuthor, zUser, sizeof(zAuthor) - 1 ) == 0 ) { @ <tr class="wh-intermediate" title="%s(zWhen)"> } else { strncpy( zAuthor, zUser, sizeof(zAuthor) - 1 ); @ <tr class="wh-major" title="%s(zWhen)"> } /* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */ @ <td>%s(zAge)</td> fossil_free(zAge); @ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td> @ <td><input disabled type="radio" name="baseline" value="%S(zUuid)"/></td> @ <td>%h(zUser)<span class="wh-iterations" hidden></td> if( showRid ){ @ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td> } @ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td> @ </tr> } @ </tbody></table></div> db_finalize(&q); builtin_request_js("fossil.page.whistory.js"); /* style_table_sorter(); */ style_finish_page(); } /* ** WEBPAGE: wdiff ** ** Show the changes to a wiki page. ** ** Query parameters: ** ** id=HASH Hash prefix for the child version to be diffed. ** rid=INTEGER RecordID for the child version ** pid=HASH Hash prefix for the parent. ** ** The "id" query parameter is required. "pid" is optional. If "pid" ** is omitted, then the diff is against the first parent of the child. */ void wdiff_page(void){ const char *zId; const char *zPid; Manifest *pW1, *pW2 = 0; int rid1, rid2, nextRid; Blob w1, w2, d; DiffConfig DCfg; login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } zId = P("id"); if( zId==0 ){ rid1 = atoi(PD("rid","0")); }else{ rid1 = name_to_typed_rid(zId, "w"); } zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid1); pW1 = manifest_get(rid1, CFTYPE_WIKI, 0); if( pW1==0 ) fossil_redirect_home(); blob_init(&w1, pW1->zWiki, -1); zPid = P("pid"); if( ( zPid==0 || zPid[0] == 0 ) && pW1->nParent ){ zPid = pW1->azParent[0]; } cgi_check_for_malice(); if( zPid && zPid[0] != 0 ){ char *zDate; rid2 = name_to_typed_rid(zPid, "w"); pW2 = manifest_get(rid2, CFTYPE_WIKI, 0); blob_init(&w2, pW2->zWiki, -1); @ <h2>Changes to \ @ "%z(href("%R/whistory?name=%s",pW1->zWikiTitle))%h(pW1->zWikiTitle)</a>" \ zDate = db_text(0, "SELECT datetime(%.16g,toLocal())",pW2->rDate); @ between %z(href("%R/info/%s",zPid))%z(zDate)</a> \ zDate = db_text(0, "SELECT datetime(%.16g,toLocal())",pW1->rDate); @ and %z(href("%R/info/%s",zId))%z(zDate)</a></h2> style_submenu_element("Previous", "%R/wdiff?id=%S", zPid); }else{ blob_zero(&w2); @ <h2>Initial version of \ @ "%z(href("%R/whistory?name=%s",pW1->zWikiTitle))%h(pW1->zWikiTitle)</a>"\ @ </h2> } nextRid = wiki_next(wiki_tagid(pW1->zWikiTitle),pW1->rDate); if( nextRid ){ style_submenu_element("Next", "%R/wdiff?rid=%d", nextRid); } style_set_current_feature("wiki"); style_header("Changes To %s", pW1->zWikiTitle); blob_zero(&d); construct_diff_flags(1, &DCfg); DCfg.diffFlags |= DIFF_HTML | DIFF_LINENO; text_diff(&w2, &w1, &d, &DCfg); @ %s(blob_str(&d)) manifest_destroy(pW1); manifest_destroy(pW2); style_finish_page(); } /* ** A query that returns information about all wiki pages. ** ** wname Name of the wiki page ** wsort Sort names by this label ** wrid rid of the most recent version of the page ** wmtime time most recent version was created ** wcnt Number of versions of this wiki page ** ** The wrid value is zero for deleted wiki pages. */ static const char listAllWikiPages[] = @ SELECT @ substr(tag.tagname, 6) AS wname, @ lower(substr(tag.tagname, 6)) AS sortname, @ tagxref.value+0 AS wrid, @ max(tagxref.mtime) AS wmtime, @ count(*) AS wcnt @ FROM @ tag, @ tagxref @ WHERE @ tag.tagname GLOB 'wiki-*' @ AND tagxref.tagid=tag.tagid @ AND TYPEOF(wrid)='integer' -- only wiki- tags which are wiki pages @ GROUP BY 1 @ ORDER BY 2; ; /* ** WEBPAGE: wcontent ** ** all=1 Show deleted pages ** showid Show rid values for each page. ** ** List all available wiki pages with date created and last modified. */ void wcontent_page(void){ Stmt q; double rNow; int showAll = P("all")!=0; int showRid = P("showid")!=0; int showCkBr; login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } style_set_current_feature("wiki"); style_header("Available Wiki Pages"); if( showAll ){ style_submenu_element("Active", "%R/wcontent"); }else{ style_submenu_element("All", "%R/wcontent?all=1"); } cgi_check_for_malice(); showCkBr = db_exists( "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) " "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' ) " " AND TYPEOF(tagxref.value+0)='integer'" ); if( showCkBr ){ showCkBr = P("showckbr")!=0; style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0); } wiki_standard_submenu(W_ALL_BUT(W_LIST)); db_prepare(&q, listAllWikiPages/*works-like:""*/); @ <div class="brlist"> @ <table class='sortable' data-column-types='tKN' data-init-sort='1'> @ <thead><tr> @ <th>Name</th> @ <th>Last Change</th> @ <th>Versions</th> if( showRid ){ @ <th>RID</th> } @ </tr></thead><tbody> rNow = db_double(0.0, "SELECT julianday('now')"); while( db_step(&q)==SQLITE_ROW ){ const char *zWName = db_column_text(&q, 0); const char *zSort = db_column_text(&q, 1); int wrid = db_column_int(&q, 2); double rWmtime = db_column_double(&q, 3); sqlite3_int64 iMtime = (sqlite3_int64)(rWmtime*86400.0); char *zAge; int wcnt = db_column_int(&q, 4); char *zWDisplayName; if( !showCkBr && (sqlite3_strglob("checkin/*", zWName)==0 || sqlite3_strglob("branch/*", zWName)==0) ){ continue; } if( sqlite3_strglob("checkin/*", zWName)==0 ){ zWDisplayName = mprintf("%.25s...", zWName); }else{ zWDisplayName = mprintf("%s", zWName); } if( wrid==0 ){ if( !showAll ) continue; @ <tr><td data-sortkey="%h(zSort)">\ @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td> }else{ @ <tr><td data-sortkey="%h(zSort)">\ @ %z(href("%R/wiki?name=%T&p",zWName))%h(zWDisplayName)</a></td> } zAge = human_readable_age(rNow - rWmtime); @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> fossil_free(zAge); @ <td>%z(href("%R/whistory?name=%T",zWName))%d(wcnt)</a></td> if( showRid ){ @ <td>%d(wrid)</td> } @ </tr> fossil_free(zWDisplayName); } @ </tbody></table></div> db_finalize(&q); style_table_sorter(); style_finish_page(); } /* ** 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","*"); cgi_check_for_malice(); style_set_current_feature("wiki"); 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_finish_page(); } /* ** Add a new wiki page to the repository. The page name is ** given by the zPageName parameter. rid must be zero to create ** a new page otherwise the page identified by rid is updated. ** ** 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 rid, Blob *pContent, const char *zMimeType, int localUser){ Blob wiki; /* Wiki page content */ Blob cksum; /* wiki checksum */ char *zDate; /* timestamp */ char *zUuid; /* uuid for rid */ 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; } /* ** Determine the rid for a tech note given either its id, its timestamp, ** or its tag. Returns 0 if there is no such item and -1 if the details ** are ambiguous and could refer to multiple items. */ int wiki_technote_to_rid(const char *zETime) { int rid=0; /* Artifact ID of the tech note */ int nETime = strlen(zETime); Stmt q; if( nETime>=4 && nETime<=HNAME_MAX && validate16(zETime, nETime) ){ char zUuid[HNAME_MAX+1]; memcpy(zUuid, zETime, nETime+1); canonical16(zUuid, nETime); db_prepare(&q, "SELECT e.objid" " FROM event e, tag t" " WHERE e.type='e' AND e.tagid IS NOT NULL AND t.tagid=e.tagid" " AND t.tagname GLOB 'event-%q*'", zUuid ); if( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); if( db_step(&q)==SQLITE_ROW ) rid = -1; } db_finalize(&q); } if (!rid) { if (strlen(zETime)>4) { rid = db_int(0, "SELECT objid" " FROM event" " WHERE datetime(mtime)=datetime('%q')" " AND type='e'" " AND tagid IS NOT NULL" " ORDER BY objid DESC LIMIT 1", zETime); } } if( !rid ) { /* ** At present, technote tags are prefixed with 'sym-', which shouldn't ** be the case, so we check for both with and without the prefix until ** such time as tags have the errant prefix dropped. */ rid = db_int(0, "SELECT e.objid" " FROM event e, tag t, tagxref tx" " WHERE e.type='e'" " AND e.tagid IS NOT NULL" " AND e.objid IN" " (SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag" " WHERE tagname GLOB '%q'))" " OR e.objid IN" " (SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag" " WHERE tagname GLOB 'sym-%q'))" " ORDER BY e.mtime DESC LIMIT 1", zETime, zETime); } return rid; } /* ** COMMAND: wiki* ** ** Usage: %fossil wiki (export|create|commit|list) WikiName ** ** Run various subcommands to work with wiki entries or tech notes. ** ** > fossil wiki export ?OPTIONS? PAGENAME ?FILE? ** > fossil wiki export ?OPTIONS? -t|--technote DATETIME|TECHNOTE-ID|TAG ?FILE? ** ** Sends the latest version of either a wiki page or of a tech ** note to the given file or standard output. A filename of "-" ** writes the output to standard output. The directory parts of ** the output filename are created if needed. ** If PAGENAME is provided, the named wiki page will be output. ** ** Options: ** -t|--technote DATETIME|TECHNOTE-ID|TAG ** Specifies that a technote, rather than a wiki page, ** will be exported. If DATETIME is used, the most ** recently modified tech note with that DATETIME will ** output. If TAG is used, the most recently modified ** tech note with that TAG will be output. ** -h|--html The body (only) is rendered in HTML form, without ** any page header/foot or HTML/BODY tag wrappers. ** -H|--HTML Works like -h|-html but wraps the output in ** <html><body>...</body></html>. ** -p|--pre If -h|-H is used and the page or technote has ** the text/plain mimetype, its HTML-escaped output ** will be wrapped in <pre>...</pre>. ** ** > fossil wiki (create|commit) (PAGENAME | TECHNOTE-COMMENT) ?FILE? ?OPTIONS? ** ** Create a new or commit changes to an existing wiki page or ** technote from FILE or from standard input. PAGENAME is the ** name of the wiki entry. TECHNOTE-COMMENT is the timeline comment of ** the technote. ** ** Options: ** -M|--mimetype TEXT-FORMAT The mime type of the update. ** Defaults to the type used by ** the previous version of the ** page, or text/x-fossil-wiki. ** Valid values are: text/x-fossil-wiki, ** text/x-markdown and text/plain. fossil, ** markdown or plain can be specified as ** synonyms of these values. ** -t|--technote DATETIME Specifies the timestamp of ** the technote to be created or ** updated. The timestamp specifies when ** this technote appears in the timeline ** and is its permanent handle although ** it may not be unique. When updating ** a technote the most recently modified ** tech note with the specified timestamp ** will be updated. ** -t|--technote TECHNOTE-ID Specifies the technote to be ** updated by its technote id, which is ** its UUID. ** --technote-tags TAGS The set of tags for a technote. ** --technote-bgcolor COLOR The color used for the technote ** on the timeline. ** ** > fossil wiki list ?OPTIONS? ** > fossil wiki ls ?OPTIONS? ** ** Lists all wiki entries, one per line, ordered ** case-insensitively by name. Wiki pages associated with ** check-ins and branches are NOT shown, unless -a is given. ** ** Options: ** --all Include "deleted" pages in output. ** By default deleted pages are elided. ** -t|--technote Technotes will be listed instead of ** pages. The technotes will be in order ** of timestamp with the most recent ** first. ** -a|--show-associated Show wiki pages associated with ** check-ins and branches. ** -s|--show-technote-ids The id of the tech note will be listed ** along side the timestamp. The tech note ** id will be the first word on each line. ** This option only applies if the ** --technote option is also specified. ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. ** ** The "Sandbox" wiki pseudo-page is a special case. Its name is ** checked case-insensitively and either "create" or "commit" may be ** used to update its contents. */ void wiki_cmd(void){ int n; int isSandbox = 0; /* true if dealing with sandbox pseudo-page */ const int showAll = find_option("all", 0, 0)!=0; 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 = 0; /* Name of the wiki page to export */ const char *zFile; /* Name of the output file (0=stdout) */ const char *zETime; /* The name of the technote to export */ int rid = 0; /* Artifact ID of the wiki page */ int i; /* Loop counter */ char *zBody = 0; /* Wiki page content */ Blob body = empty_blob; /* Wiki page content */ Manifest *pWiki = 0; /* Parsed wiki page content */ int fHtml = 0; /* Export in HTML form */ FILE * pFile = 0; /* Output file */ int fPre = 0; /* Indicates that -h|-H should be ** wrapped in <pre>...</pre> if pWiki ** has the text/plain mimetype. */ fHtml = find_option("HTML","H",0)!=0 ? 2 : (find_option("html","h",0)!=0 ? 1 : 0) /* 1 == -html, 2 == -HTML */; fPre = fHtml==0 ? 0 : find_option("pre","p",0)!=0; zETime = find_option("technote","t",1); verify_all_options(); if( !zETime ){ if( (g.argc!=4) && (g.argc!=5) ){ usage("export ?-html? PAGENAME ?FILE?"); } zPageName = g.argv[3]; isSandbox = is_sandbox(zPageName); if(isSandbox){ zBody = db_get("sandbox", 0); }else{ wiki_fetch_by_name(zPageName, 0, &rid, &pWiki); if(pWiki){ zBody = pWiki->zWiki; } } if( zBody==0 ){ fossil_fatal("wiki page [%s] not found",zPageName); } zFile = (g.argc==4) ? "-" : g.argv[4]; }else{ if( (g.argc!=3) && (g.argc!=4) ){ usage("export ?-html? ?FILE? --technote " "DATETIME|TECHNOTE-ID"); } rid = wiki_technote_to_rid(zETime); if ( rid==-1 ){ fossil_fatal("ambiguous tech note id: %s", zETime); } if( (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0 ){ zBody = pWiki->zWiki; } if( zBody==0 ){ fossil_fatal("technote [%s] not found",zETime); } zFile = (g.argc==3) ? "-" : g.argv[3]; } for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){} zBody[i] = 0; blob_init(&body, zBody, -1); if(fHtml==0){ blob_append(&body, "\n", 1); }else{ Blob html = empty_blob; /* HTML-ized content */ const char * zMimetype = isSandbox ? db_get("sandbox-mimetype", "text/x-fossil-wiki") : wiki_filter_mimetypes(pWiki->zMimetype); if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ wiki_convert(&body,&html,0); }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ markdown_to_html(&body,0,&html); safe_html_context(DOCSRC_WIKI); safe_html(&html); }else if( fossil_strcmp(zMimetype, "text/plain")==0 ){ htmlize_to_blob(&html,zBody,i); }else{ fossil_fatal("Unsupported MIME type '%s' for wiki page '%s'.", zMimetype, pWiki ? pWiki->zWikiTitle : zPageName ); } blob_reset(&body); body = html /* transfer memory */; } pFile = fossil_fopen_for_output(zFile); if(fHtml==2){ fwrite("<html><body>", 1, 12, pFile); } if(fPre!=0){ fwrite("<pre>", 1, 5, pFile); } fwrite(blob_buffer(&body), 1, blob_size(&body), pFile); if(fPre!=0){ fwrite("</pre>", 1, 6, pFile); } if(fHtml==2){ fwrite("</body></html>\n", 1, 15, pFile); } fossil_fclose(pFile); 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 = 0; Manifest *pWiki = 0; /* Parsed wiki page content */ const int isCreate = 'r'==g.argv[2][1] /* else "commit" */; const char *zMimeType = find_option("mimetype", "M", 1); const char *zETime = find_option("technote", "t", 1); const char *zTags = find_option("technote-tags", NULL, 1); const char *zClr = find_option("technote-bgcolor", NULL, 1); verify_all_options(); if( g.argc!=4 && g.argc!=5 ){ usage("commit|create PAGENAME ?FILE? [--mimetype TEXT-FORMAT]" " [--technote DATETIME] [--technote-tags TAGS]" " [--technote-bgcolor COLOR]"); } zPageName = g.argv[3]; if( g.argc==4 ){ blob_read_from_channel(&content, stdin, -1); }else{ blob_read_from_file(&content, g.argv[4], ExtFILE); } isSandbox = is_sandbox(zPageName); if ( !zETime ){ if( !isSandbox ){ wiki_fetch_by_name(zPageName, 0, &rid, &pWiki); } }else{ rid = wiki_technote_to_rid(zETime); if( rid>0 ){ pWiki = manifest_get(rid, CFTYPE_EVENT, 0); } } if( !zMimeType || !*zMimeType ){ /* Try to deduce the mimetype based on the prior version. */ if(isSandbox){ zMimeType = wiki_filter_mimetypes(db_get("sandbox-mimetype", "text/x-fossil-wiki")); }else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){ zMimeType = pWiki->zMimetype; } }else{ zMimeType = wiki_filter_mimetypes(zMimeType); } if( isCreate && rid>0 ){ if ( !zETime ){ fossil_fatal("wiki page %s already exists", zPageName); }else{ /* Creating a tech note with same timestamp is permitted and should create a new tech note */ rid = 0; } }else if( !isCreate && rid==0 && isSandbox==0 ){ if ( !zETime ){ fossil_fatal("no such wiki page: %s", zPageName); }else{ fossil_fatal("no such tech note: %s", zETime); } } if( !zETime ){ if(isSandbox){ db_set("sandbox",blob_str(&content),0); db_set("sandbox-mimetype",zMimeType,0); fossil_print("Updated sandbox pseudo-page.\n"); }else{ wiki_cmd_commit(zPageName, rid, &content, zMimeType, 1); if( g.argv[2][1]=='r' ){ fossil_print("Created new wiki page %s.\n", zPageName); }else{ fossil_print("Updated wiki page %s.\n", zPageName); } } }else{ if( rid != -1 ){ char *zMETime; /* Normalized, mutable version of zETime */ zMETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime); event_cmd_commit(zMETime, rid, &content, zMimeType, zPageName, zTags, zClr); if( g.argv[2][1]=='r' ){ fossil_print("Created new tech note %s.\n", zMETime); }else{ fossil_print("Updated tech note %s.\n", zMETime); } free(zMETime); }else{ fossil_fatal("ambiguous tech note id: %s", zETime); } } manifest_destroy(pWiki); blob_reset(&content); }else if( strncmp(g.argv[2],"delete",n)==0 ){ if( g.argc!=4 ){ 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; const int fTechnote = find_option("technote","t",0)!=0; const int showIds = find_option("show-technote-ids","s",0)!=0; const int showCkBr = find_option("show-associated","a",0)!=0; verify_all_options(); if (fTechnote==0){ db_prepare(&q, listAllWikiPages/*works-like:""*/); }else{ db_prepare(&q, "SELECT datetime(e.mtime), substr(t.tagname,7), e.objid" " FROM event e, tag t" " WHERE e.type='e'" " AND e.tagid IS NOT NULL" " AND t.tagid=e.tagid" " ORDER BY e.mtime DESC /*sort*/" ); } while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const int wrid = db_column_int(&q, 2); if(!showAll && !wrid){ continue; } if( !showCkBr && (sqlite3_strglob("checkin/*", zName)==0 || sqlite3_strglob("branch/*", zName)==0) ){ continue; } if( showIds ){ const char *zUuid = db_column_text(&q, 1); fossil_print("%s ",zUuid); } fossil_print( "%s\n",zName); } db_finalize(&q); }else{ goto wiki_cmd_usage; } return; wiki_cmd_usage: usage("export|create|commit|list ..."); } /* ** Allowed flags for wiki_render_associated */ #if INTERFACE #define WIKIASSOC_FULL_TITLE 0x00001 /* Full title */ #define WIKIASSOC_MENU_READ 0x00002 /* Add submenu link to read wiki */ #define WIKIASSOC_MENU_WRITE 0x00004 /* Add submenu link to add wiki */ #define WIKIASSOC_ALL 0x00007 /* All of the above */ #endif /* ** Show the default Section label for an associated wiki page. */ static void wiki_section_label( const char *zPrefix, /* "branch", "tag", or "checkin" */ const char *zName, /* Name of the object */ unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ ){ if( (mFlags & WIKIASSOC_FULL_TITLE)==0 ){ @ <div class="section accordion">About</div> }else if( zPrefix[0]=='c' ){ /* checkin/... */ @ <div class="section accordion">About check-in %.20h(zName)</div> }else{ @ <div class="section accordion">About %s(zPrefix) %h(zName)</div> } } /* ** Add an "Wiki" button in a submenu that links to the read-wiki page. */ static void wiki_submenu_to_edit_wiki( const char *zPrefix, /* "branch", "tag", or "checkin" */ const char *zName, /* Name of the object */ unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ ){ if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){ style_submenu_element("Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName); } } /* ** Check to see if there exists a wiki page with a name zPrefix/zName. ** If there is, then render a <div class='section'>..</div> and ** return true. ** ** If there is no such wiki page, return false. */ int wiki_render_associated( const char *zPrefix, /* "branch", "tag", or "checkin" */ const char *zName, /* Name of the object */ unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ ){ int rid; Manifest *pWiki; if( !db_get_boolean("wiki-about",1) ) return 0; rid = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname='wiki-%q/%q')" " ORDER BY mtime DESC LIMIT 1", zPrefix, zName ); pWiki = rid==0 ? 0 : manifest_get(rid, CFTYPE_WIKI, 0); if( pWiki==0 || pWiki->zWiki==0 || pWiki->zWiki[0]==0 ){ if( g.perm.WrWiki && g.perm.Write && (mFlags & WIKIASSOC_MENU_WRITE)!=0 ){ style_submenu_element("Add Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName); } return 0; } if( fossil_strcmp(pWiki->zMimetype, "text/x-markdown")==0 ){ Blob tail = BLOB_INITIALIZER; Blob title = BLOB_INITIALIZER; Blob markdown; blob_init(&markdown, pWiki->zWiki, -1); markdown_to_html(&markdown, &title, &tail); if( blob_size(&title) ){ @ <div class="section accordion">%h(blob_str(&title))</div> }else{ wiki_section_label(zPrefix, zName, mFlags); } wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); @ <div class="accordion_panel"> safe_html_context(DOCSRC_WIKI); safe_html(&tail); convert_href_and_output(&tail); @ </div> blob_reset(&tail); blob_reset(&title); blob_reset(&markdown); }else if( fossil_strcmp(pWiki->zMimetype, "text/plain")==0 ){ wiki_section_label(zPrefix, zName, mFlags); wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); @ <div class="accordion_panel"><pre> @ %h(pWiki->zWiki) @ </pre></div> }else{ Blob tail = BLOB_INITIALIZER; Blob title = BLOB_INITIALIZER; Blob wiki; Blob *pBody; blob_init(&wiki, pWiki->zWiki, -1); if( wiki_find_title(&wiki, &title, &tail) ){ @ <div class="section accordion">%h(blob_str(&title))</div> pBody = &tail; }else{ wiki_section_label(zPrefix, zName, mFlags); pBody = &wiki; } wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); @ <div class="accordion_panel"><div class="wiki"> wiki_convert(pBody, 0, WIKI_BUTTONS); @ </div></div> blob_reset(&tail); blob_reset(&title); blob_reset(&wiki); } manifest_destroy(pWiki); builtin_request_js("accordion.js"); return 1; } |
Added src/wiki.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 | <h2>Wiki Formatting Rule Summary</h2> # Blank lines are paragraph breaks # Bullets are "*" surrounded by two spaces at the beginning of a line # Enumeration items are "#" or a digit and a "." surrounded by two spaces at the beginning of a line # Indented paragraphs begin with a tab or two spaces # Hyperlinks are contained within square brackets: <nowiki>"<b>[</b><i>target</i><b>]</b>" or "<b>[</b><i>target</i><b>|</b><i>label</i><b>]</b>"</nowiki> # Most ordinary HTML works # <verbatim> and <nowiki> We call the first five rules above the "wiki" formatting rules. The last two rules are the HTML formatting rules. <h2>Formatting Rule Details</h2> 1. <b>Paragraphs.</b> 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. 2. <b>Bullet Lists.</b> 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. 3. <b>Enumeration Lists.</b> 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. Or it can be a number and a "." (ex: "5.") surrounded on both sides by two spaces or 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 numerals, use HTML. 4. <b>Indented Paragraphs.</b> 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. 5. <b>Hyperlinks.</b> Text within square brackets <nowiki>("[...]")</nowiki> 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, a URL, or an [#intermap|interwiki link] of the form "<i>Tag</i><b>:</b><i>PageName</i>". 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. You can also link to internal anchor names using <nowiki>[#anchor-name],</nowiki> providing you have added the necessary "<a name='anchor-name'></a>" tag to your wiki page. 6. <b>HTML.</b> The following standard HTML elements may be used: <a> <address> <article> <aside> <b> <big> <blockquote> <br> <center> <cite> <code> <col> <colgroup> <dd> <del> <dfn> <div> <dl> <dt> <em> <font> <footer> <ins> <h1> <h2> <h3> <h4> <h5> <h6> <header> <hr> <i> <img> <kbd> <li> <nav> <nobr> <nowiki> <ol> <p> <pre> <s> <samp> <section> <small> <span> <strike> <strong> <sub> <sup> <table> <tbody> <td> <tfoot> <th> <thead> <title> <tr> <tt> <u> <ul> <var> <verbatim>. There are two non-standard elements available: <verbatim> and <nowiki>. No other elements are allowed. All attributes are checked and only a few benign attributes are allowed on each element. In particular, any attributes that specify javascript or CSS are elided. 7. <b>Special Markup.</b> The <nowiki> tag disables all wiki formatting rules through the matching </nowiki> element. The <verbatim> tag works like <pre> with the addition that it also disables all wiki and HTML markup through the matching </verbatim>. Text within <tt><verbatim type="pikchr">...</verbatim></tt> is formatted using <a href="https://pikchr.org/home">Pikchr</a>. <a name="intermap"></a> <h2>Interwiki Tag [/intermap|Map]</h2> |
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 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 | ** 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 */ #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */ #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */ #define WIKI_SAFE 0x100 /* Make the result safe for embedding */ #define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */ #define WIKI_NOBRACKET 0x400 /* Omit extra [..] around hyperlinks */ #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_TITLE, 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, AMSK_FACE = 0x00000800, AMSK_HEIGHT = 0x00001000, AMSK_HREF = 0x00002000, AMSK_HSPACE = 0x00004000, AMSK_ID = 0x00008000, AMSK_LINKS = 0x00010000, AMSK_NAME = 0x00020000, AMSK_ROWSPAN = 0x00040000, AMSK_SIZE = 0x00080000, AMSK_SRC = 0x00100000, AMSK_START = 0x00200000, AMSK_STYLE = 0x00400000, AMSK_TARGET = 0x00800000, AMSK_TITLE = 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 }, { "title", AMSK_TITLE }, { "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; first = 1; last = count(aAttribute) - 1; while( first<=last ){ i = (first+last)/2; c = fossil_strcmp(aAttribute[i].zName, z); if( c==0 ){ return i; }else if( c<0 ){ first = i+1; }else{ last = i-1; } } return 0; } /* ** Allowed markup. ** ** Except for MARKUP_INVALID, this must all be in alphabetical order ** and in numerical sequence. The first markup type must be zero. ** The value for MARKUP_XYZ must correspond to the <xyz> entry ** in aMarkup[]. */ enum markup_t { MARKUP_INVALID = 0, MARKUP_A, MARKUP_ABBR, MARKUP_ADDRESS, MARKUP_HTML5_ARTICLE, MARKUP_HTML5_ASIDE, MARKUP_B, MARKUP_BIG, MARKUP_BLOCKQUOTE, MARKUP_BR, MARKUP_CENTER, MARKUP_CITE, MARKUP_CODE, MARKUP_COL, MARKUP_COLGROUP, MARKUP_DD, MARKUP_DEL, MARKUP_DETAILS, MARKUP_DFN, MARKUP_DIV, MARKUP_DL, MARKUP_DT, MARKUP_EM, MARKUP_FONT, MARKUP_HTML5_FOOTER, MARKUP_H1, MARKUP_H2, MARKUP_H3, MARKUP_H4, MARKUP_H5, MARKUP_H6, MARKUP_HTML5_HEADER, MARKUP_HR, MARKUP_I, MARKUP_IMG, MARKUP_INS, MARKUP_KBD, MARKUP_LI, MARKUP_HTML5_NAV, MARKUP_NOBR, MARKUP_NOWIKI, MARKUP_OL, MARKUP_P, MARKUP_PRE, MARKUP_S, MARKUP_SAMP, MARKUP_HTML5_SECTION, MARKUP_SMALL, MARKUP_SPAN, MARKUP_STRIKE, MARKUP_STRONG, MARKUP_SUB, MARKUP_SUMMARY, MARKUP_SUP, MARKUP_TABLE, MARKUP_TBODY, MARKUP_TD, MARKUP_TFOOT, MARKUP_TH, MARKUP_THEAD, MARKUP_TITLE, MARKUP_TR, MARKUP_TT, MARKUP_U, MARKUP_UL, MARKUP_VAR, MARKUP_VERBATIM }; /* ** 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> */ #define MUTYPE_LIST 0x0010 /* Lists. <ol>, <ul>, or <dl> */ #define MUTYPE_LI 0x0020 /* List items. <li>, <dd>, <dt> */ #define MUTYPE_TABLE 0x0040 /* <table> */ #define MUTYPE_TR 0x0080 /* <tr> */ #define MUTYPE_TD 0x0100 /* <td> or <th> */ #define MUTYPE_SPECIAL 0x0200 /* <nowiki> or <verbatim> */ #define MUTYPE_HYPERLINK 0x0400 /* <a> */ /* MUTYPE values for elements that require strictly nested end-tags */ #define MUTYPE_Nested 0x0656 /* ** These markup types must have an end tag. */ #define MUTYPE_STACK (MUTYPE_BLOCK | MUTYPE_FONT | MUTYPE_LIST | MUTYPE_TABLE) /* ** This markup types are allowed for "inline" text. */ #define MUTYPE_INLINE (MUTYPE_FONT | MUTYPE_HYPERLINK) static const struct AllowedMarkup { const char *zName; /* Name of the markup */ char iCode; /* The MARKUP_* code */ 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| AMSK_TITLE}, { "abbr", MARKUP_ABBR, MUTYPE_FONT, AMSK_ID|AMSK_CLASS|AMSK_STYLE|AMSK_TITLE }, { "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 }, { "col", MARKUP_COL, MUTYPE_SINGLE, AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE }, { "colgroup", MARKUP_COLGROUP, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE}, { "dd", MARKUP_DD, MUTYPE_LI, AMSK_STYLE }, { "del", MARKUP_DEL, MUTYPE_FONT, AMSK_STYLE }, { "details", MARKUP_DETAILS, MUTYPE_BLOCK, AMSK_ID|AMSK_CLASS|AMSK_STYLE }, { "dfn", MARKUP_DFN, MUTYPE_FONT, AMSK_STYLE }, { "div", MARKUP_DIV, MUTYPE_BLOCK, 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 }, { "ins", MARKUP_INS, MUTYPE_FONT, 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 }, { "summary", MARKUP_SUMMARY, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "sup", MARKUP_SUP, MUTYPE_FONT, AMSK_STYLE }, { "table", MARKUP_TABLE, MUTYPE_TABLE, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING| AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE|AMSK_CLASS| AMSK_STYLE }, { "tbody", MARKUP_TBODY, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "td", MARKUP_TD, MUTYPE_TD, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, { "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<=count(aMarkup) - 1 ; i++ ){ @ <%s(aMarkup[i].zName)> } } /* ** Use binary search to locate a tag in the aMarkup[] table. */ static int findTag(const char *z){ int i, c, first, last; first = 1; last = count(aMarkup) - 1; while( first<=last ){ i = (first+last)/2; c = fossil_strcmp(aMarkup[i].zName, z); if( c==0 ){ assert( aMarkup[i].iCode==i ); return i; }else if( c<0 ){ first = i+1; }else{ last = i-1; } } return MARKUP_INVALID; } /* ** Token types */ #define TOKEN_MARKUP 1 /* <...> */ #define TOKEN_CHARACTER 2 /* "&" or "<" not part of markup */ #define TOKEN_LINK 3 /* [...] */ #define TOKEN_PARAGRAPH 4 /* blank lines */ #define TOKEN_NEWLINE 5 /* A single "\n" */ #define TOKEN_BUL_LI 6 /* " * " */ #define TOKEN_NUM_LI 7 /* " # " */ #define TOKEN_ENUM 8 /* " \(?\d+[.)]? " */ #define TOKEN_INDENT 9 /* " " */ #define TOKEN_RAW 10 /* Output exactly (used when wiki-use-html==1) */ #define TOKEN_TEXT 11 /* None of the above */ /* ** State flags. Save the lower 16 bits for the WIKI_* flags. */ #define AT_NEWLINE 0x0010000 /* At start of a line */ #define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */ #define ALLOW_WIKI 0x0040000 /* Allow wiki markup */ #define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */ #define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */ #define INLINE_MARKUP_ONLY 0x0200000 /* Allow only "inline" markup */ #define IN_LIST 0x0400000 /* Within wiki <ul> or <ol> */ /* ** Current state of the rendering engine */ typedef struct Renderer Renderer; struct Renderer { Blob *pOut; /* Output appended to this blob */ int state; /* Flag that govern rendering */ unsigned renderFlags; /* Flags from the client */ int wikiList; /* Current wiki list type */ int inVerbatim; /* True in <verbatim> mode */ int preVerbState; /* Value of state prior to verbatim */ int wantAutoParagraph; /* True if a <p> is desired */ int inAutoParagraph; /* True if within an automatic paragraph */ int pikchrHtmlFlags; /* Flags for pikchr_to_html() */ const char *zVerbatimId; /* The id= attribute of <verbatim> */ int nStack; /* Number of elements on the stack */ int nAlloc; /* Space allocated for aStack */ struct sStack { short iCode; /* Markup code */ short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */ const char *zId; /* ID attribute or NULL */ |
︙ | ︙ | |||
376 377 378 379 380 381 382 | */ static int wikiUsesHtml(void){ static int r = -1; if( r<0 ) r = db_get_boolean("wiki-use-html", 0); return r; } | < | | | > > | | | | 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 | */ static int wikiUsesHtml(void){ static int r = -1; if( r<0 ) r = db_get_boolean("wiki-use-html", 0); return r; } /* ** z points to a "<" character. Check to see if this is the start of ** a valid markup. If it is, return the total number of characters in ** the markup including the initial "<" and the terminating ">". If ** it is not well-formed markup, return 0. */ int html_tag_length(const char *z){ int n = 1; int inparen = 0; int c; if( z[n]=='/' ){ n++; } if( !fossil_isalpha(z[n]) ) return 0; while( fossil_isalnum(z[n]) || z[n]=='-' ){ n++; } c = z[n]; if( c=='/' && z[n+1]=='>' ){ return n+2; } if( c!='>' && !fossil_isspace(c) ) return 0; while( (c = z[n])!=0 && (c!='>' || inparen) ){ if( c==inparen ){ inparen = 0; }else if( inparen==0 && (c=='"' || c=='\'') ){ inparen = c; } n++; } if( z[n]!='>' ) return 0; return n+1; } /* ** z points to a "\n" character. Check to see if this newline is ** followed by one or more blank lines. If it is, return the number ** of characters through the closing "\n". If not, return 0. */ static int paragraphBreakLength(const char *z){ int i, n; int nNewline = 1; for(i=1, n=0; fossil_isspace(z[i]); i++){ if( z[i]=='\n' ){ nNewline++; n = i; } } if( nNewline>=2 ){ return n+1; |
︙ | ︙ | |||
435 436 437 438 439 440 441 | ** Interesting characters are: ** ** < ** & ** \n ** [ ** | > | < | > > | > | < < < > | | | | | 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 | ** Interesting characters are: ** ** < ** & ** \n ** [ ** ** The "[" is only considered if flags contain ALLOW_LINKS or ALLOW_WIKI. ** The "\n" is only considered interesting if the flags constains ALLOW_WIKI. */ static int textLength(const char *z, int flags){ const char *zReject; if( flags & ALLOW_WIKI ){ zReject = "<&[\n"; }else if( flags & ALLOW_LINKS ){ zReject = "<&["; }else{ zReject = "<&"; } return strcspn(z, zReject); } /* ** Return true if z[] begins with an HTML character element. */ static int isElement(const char *z){ int i; assert( z[0]=='&' ); if( z[1]=='#' ){ for(i=2; fossil_isdigit(z[i]); i++){} return i>2 && z[i]==';'; }else{ for(i=1; fossil_isalpha(z[i]); i++){} return i>1 && z[i]==';'; } } /* ** Check to see if the z[] string is the beginning of a wiki list item. ** If it is, return the length of the bullet text. Otherwise return 0. |
︙ | ︙ | |||
485 486 487 488 489 490 491 | n++; i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } | | | | | | | 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 | n++; i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } if( i<2 || fossil_isspace(z[n]) ) return 0; return n; } /* ** Check to see if the z[] string is the beginning of an enumeration value. ** If it is, return the length of the bullet text. Otherwise return 0. ** ** Syntax: ** * a tab or two or more spaces ** * one or more digits ** * optional "." ** * another tab or two ore more spaces. ** */ static int enumLength(const char *z){ int i, n; n = 0; i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } if( i<2 ) return 0; for(i=0; fossil_isdigit(z[n]); i++, n++){} if( i==0 ) return 0; if( z[n]=='.' ){ n++; } i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } if( i<2 || fossil_isspace(z[n]) ) return 0; return n; } /* ** Check to see if the z[] string is the beginning of an indented ** paragraph. If it is, return the length of the indent. Otherwise ** return 0. */ static int indentLength(const char *z){ int i, n; n = 0; i = 0; while( z[n]==' ' || z[n]=='\t' ){ if( z[n]=='\t' ) i++; i++; n++; } if( i<2 || fossil_isspace(z[n]) ) return 0; return n; } /* ** Check to see if the z[] string is a wiki hyperlink. If it is, ** return the length of the hyperlink. Otherwise return 0. */ |
︙ | ︙ | |||
567 568 569 570 571 572 573 | ** ** z points to the start of a token. Return the number of ** characters in that token. Write the token type into *pTokenType. */ static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){ int n; if( z[0]=='<' ){ | | | | | > > > | | 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 | ** ** z points to the start of a token. Return the number of ** characters in that token. Write the token type into *pTokenType. */ static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){ int n; if( z[0]=='<' ){ n = html_tag_length(z); if( n>0 ){ *pTokenType = TOKEN_MARKUP; return n; }else{ *pTokenType = TOKEN_CHARACTER; return 1; } } if( z[0]=='&' && (p->inVerbatim || !isElement(z)) ){ *pTokenType = TOKEN_CHARACTER; return 1; } if( (p->state & ALLOW_WIKI)!=0 ){ if( z[0]=='\n' ){ n = paragraphBreakLength(z); if( n>0 ){ *pTokenType = TOKEN_PARAGRAPH; return n; }else{ *pTokenType = TOKEN_NEWLINE; return 1; } } if( (p->state & AT_NEWLINE)!=0 && fossil_isspace(z[0]) ){ n = listItemLength(z, '*'); if( n>0 ){ *pTokenType = TOKEN_BUL_LI; return n; } n = listItemLength(z, '#'); if( n>0 ){ *pTokenType = TOKEN_NUM_LI; return n; } n = enumLength(z); if( n>0 ){ *pTokenType = TOKEN_ENUM; return n; } } if( (p->state & AT_PARAGRAPH)!=0 && fossil_isspace(z[0]) ){ n = indentLength(z); if( n>0 ){ *pTokenType = TOKEN_INDENT; return n; } } if( z[0]=='[' && (n = linkLength(z))>0 ){ *pTokenType = TOKEN_LINK; return n; } }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){ *pTokenType = TOKEN_LINK; return n; } *pTokenType = TOKEN_TEXT; return 1 + textLength(z+1, p->state); } /* ** Parse only Wiki links, return everything else as TOKEN_RAW. ** ** z points to the start of a token. Return the number of ** characters in that token. Write the token type into *pTokenType. |
︙ | ︙ | |||
664 665 666 667 668 669 670 | /* ** 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. */ | | | | > > > > > > > > > > > | | | | | | | | > > > > > > | > | | > > > > > | | | > > > > | > | | | > > > > > > > > > > > > > > > < < < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < < | 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 | /* ** 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]=='/' ){ p->endTag = 1; i = 2; }else{ p->endTag = 0; i = 1; } j = 0; while( fossil_isalnum(z[i]) ){ if( j<(int)sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]); i++; } zTag[j] = 0; p->iCode = findTag(zTag); p->iType = aMarkup[p->iCode].iType; p->nAttr = 0; c = 0; if( z[i]=='-' ){ 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<(int)sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]); i++; } zTag[j] = 0; p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0; while( fossil_isspace(z[i]) ){ z++; } if( z[i]!='=' ){ p->aAttr[p->nAttr].zValue = 0; p->aAttr[p->nAttr].cTerm = 0; c = 0; }else{ i++; while( fossil_isspace(z[i]) ){ z++; } if( z[i]=='"' ){ i++; zValue = &z[i]; while( z[i] && z[i]!='"' ){ i++; } }else if( z[i]=='\'' ){ i++; zValue = &z[i]; while( z[i] && z[i]!='\'' ){ i++; } }else{ zValue = &z[i]; while( !fossil_isspace(z[i]) && z[i]!='>' ){ if( z[i]=='\'' || z[i]=='"' ) attrOk = 0; i++; } } if( attrOk ){ p->aAttr[p->nAttr].zValue = zValue; p->aAttr[p->nAttr].cTerm = c = z[i]; if( z[i]==0 ){ i--; }else{ z[i] = 0; } } i++; } if( attrOk ){ seen |= aAttribute[iACode].iMask; p->nAttr++; } while( fossil_isspace(z[i]) ){ i++; } if( z[i]==0 || 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; if( p->endTag ){ blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName); }else{ blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName); for(i=0; i<p->nAttr; i++){ blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName); if( p->aAttr[i].zValue ){ const char *zVal = p->aAttr[i].zValue; if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){ blob_appendf(pOut, "=\"%R%s\"", zVal); }else{ blob_appendf(pOut, "=\"%s\"", zVal); } } } if (p->iType & MUTYPE_SINGLE){ blob_append_string(pOut, " /"); } blob_append_char(pOut, '>'); } } /* ** When the markup was parsed, some "\000" may have been inserted. ** This routine restores to those "\000" values back to their ** 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. */ static const char *attributeValue(ParsedMarkup *p, int attrId){ int i; for(i=0; i<p->nAttr; i++){ if( p->aAttr[i].iACode==attrId ){ return p->aAttr[i].zValue; } } return 0; } /* ** Return the ID attribute for markup. Return NULL if there is no ** ID attribute. */ static const char *markupId(ParsedMarkup *p){ return attributeValue(p, ATTR_ID); } /* ** Check markup pMarkup to see if it is a hyperlink with class "button" ** that is follows by simple text and an </a> only. Example: ** ** <a class="button" href="../index.wiki">Index</a> ** ** If the markup matches this pattern, and if the WIKI_BUTTONS flag was ** passed to wiki_convert(), then transform this link into a submenu ** button, skip the text, and set *pN equal to the total length of the ** text through the end of </a> and return true. If the markup does ** not match or if WIKI_BUTTONS is not set, then make no changes to *pN ** and return false. */ static int isButtonHyperlink( Renderer *p, /* Renderer state */ ParsedMarkup *pMarkup, /* Potential button markup */ const char *z, /* Complete text of Wiki */ int *pN /* Characters of z[] consumed */ ){ const char *zClass; const char *zHref; char *zTag; int i, j; if( (p->state & WIKI_BUTTONS)==0 ) return 0; zClass = attributeValue(pMarkup, ATTR_CLASS); if( zClass==0 ) return 0; if( fossil_strcmp(zClass, "button")!=0 ) return 0; zHref = attributeValue(pMarkup, ATTR_HREF); if( zHref==0 ) return 0; i = *pN; while( z[i] && z[i]!='<' ){ i++; } if( fossil_strnicmp(&z[i], "</a>",4)!=0 ) return 0; for(j=*pN; fossil_isspace(z[j]); j++){} zTag = mprintf("%.*s", i-j, &z[j]); j = (int)strlen(zTag); while( j>0 && fossil_isspace(zTag[j-1]) ){ j--; } if( j==0 ) return 0; style_submenu_element(zTag, "%s", zHref); *pN = i+4; return 1; } /* ** Pop a single element off of the stack. As the element is popped, ** 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 ** if necessary. */ static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){ if( p->nStack>=p->nAlloc ){ p->nAlloc = p->nAlloc*2 + 100; p->aStack = fossil_realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0])); } p->aStack[p->nStack].iCode = elem; p->aStack[p->nStack].zId = zId; p->aStack[p->nStack].allowWiki = w; p->nStack++; } static void pushStack(Renderer *p, int elem){ |
︙ | ︙ | |||
846 847 848 849 850 851 852 | */ static int findTagWithId(Renderer *p, int iTag, const char *zId){ int i; assert( zId!=0 ); for(i=p->nStack-1; i>=0; i--){ if( p->aStack[i].iCode!=iTag ) continue; if( p->aStack[i].zId==0 ) continue; | | | 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 | */ static int findTagWithId(Renderer *p, int iTag, const char *zId){ int i; assert( zId!=0 ); for(i=p->nStack-1; i>=0; i--){ if( p->aStack[i].iCode!=iTag ) continue; if( p->aStack[i].zId==0 ) continue; if( fossil_strcmp(zId, p->aStack[i].zId)!=0 ) continue; break; } return i; } /* ** Pop the stack until the top-most element of the stack |
︙ | ︙ | |||
877 878 879 880 881 882 883 | return p->aStack[i-1].iCode; } /* ** Begin a new paragraph if that something that is needed. */ static void startAutoParagraph(Renderer *p){ | > > | | < < | | > > > > > > > > > > > > > > > > > > > > > > > > | < | | | | | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | | | > < | < < > > > > > > > | > | | | > > > > > > | > > > > > > | > | < < | < < < | > | | | < < > | > | | | > | | | | | > > > | > > > | < > > | > | | > > > > > | > | | > > > > > > > > | > > | | > > | > > > > > > > > > | | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | return p->aStack[i-1].iCode; } /* ** 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_string(p->pOut, "<p>"); p->wantAutoParagraph = 0; p->inAutoParagraph = 1; } /* ** End a paragraph if we are in one. */ static void endAutoParagraph(Renderer *p){ if( p->inAutoParagraph ){ p->inAutoParagraph = 0; } } /* ** If the input string corresponds to an existing baseline, ** return true. */ static int is_valid_hname(const char *z){ int n = strlen(z); if( n<4 || n>HNAME_MAX ) return 0; if( !validate16(z, n) ) return 0; return 1; } /* ** Return TRUE if a hash name corresponds to an artifact in this ** repository. */ static int in_this_repo(const char *zUuid){ static Stmt q; int rc; int n; char zU2[HNAME_MAX+1]; db_static_prepare(&q, "SELECT 1 FROM blob WHERE uuid>=:u AND uuid<:u2" ); db_bind_text(&q, ":u", zUuid); n = (int)strlen(zUuid); if( n>=(int)sizeof(zU2) ) n = sizeof(zU2)-1; memcpy(zU2, zUuid, n); zU2[n-1]++; zU2[n] = 0; db_bind_text(&q, ":u2", zU2); rc = db_step(&q); db_reset(&q); return rc==SQLITE_ROW; } /* ** zTarget is guaranteed to be a UUID. It might be the UUID of a ticket. ** If it is, store in *pClosed a true or false depending on whether or not ** the ticket is closed and return true. If zTarget ** is not the UUID of a ticket, return false. */ int is_ticket( const char *zTarget, /* Ticket UUID */ int *pClosed /* True if the ticket is closed */ ){ static Stmt q; int n; int rc; char zLower[HNAME_MAX+1]; char zUpper[HNAME_MAX+1]; n = strlen(zTarget); memcpy(zLower, zTarget, n+1); canonical16(zLower, n+1); memcpy(zUpper, zLower, n+1); zUpper[n-1]++; if( !db_static_stmt_is_init(&q) ){ char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'"); db_static_prepare(&q, "SELECT %z FROM ticket " " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr", zClosedExpr /*safe-for-%s*/ ); } db_bind_text(&q, ":lwr", zLower); db_bind_text(&q, ":upr", zUpper); if( db_step(&q)==SQLITE_ROW ){ rc = 1; *pClosed = db_column_int(&q, 0); }else{ rc = 0; } db_reset(&q); return rc; } /* ** Return a pointer to the name part of zTarget (skipping the "wiki:" prefix ** if there is one) if zTarget is a valid wiki page name. Return NULL if ** zTarget names a page that does not exist. */ static const char *validWikiPageName(int mFlags, const char *zTarget){ if( strncmp(zTarget, "wiki:", 5)==0 && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ return zTarget+5; } if( strcmp(zTarget, "Sandbox")==0 ) return zTarget; if( wiki_name_is_wellformed((const unsigned char *)zTarget) && ((mFlags & WIKI_NOBADLINKS)==0 || db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'wiki-%q'" " AND (SELECT value FROM tagxref WHERE tagid=tag.tagid" " ORDER BY mtime DESC LIMIT 1) > 0", zTarget)) ){ return zTarget; } return 0; } static const char *wikiOverrideHash = 0; /* ** Fossil-wiki hyperlinks to wiki pages should be overridden to the ** hash value supplied. If the value is NULL, then override is cancelled ** and all overwrites operate normally. */ void wiki_hyperlink_override(const char *zUuid){ wikiOverrideHash = zUuid; } /* ** If links to wiki page zTarget should be redirected to some historical ** version of that page, then return the hash of the historical version. ** If no override is required, return NULL. */ static const char *wiki_is_overridden(const char *zTarget){ if( wikiOverrideHash==0 ) return 0; /* The override should only happen if the override version is not the ** latest version of the wiki page. */ if( !db_exists( "SELECT 1 FROM tag, blob, tagxref AS xA, tagxref AS xB " " WHERE tag.tagname GLOB 'wiki-%q*'" " AND blob.uuid GLOB '%q'" " AND xA.tagid=tag.tagid AND xA.rid=blob.rid" " AND xB.tagid=tag.tagid AND xB.mtime>xA.mtime", zTarget, wikiOverrideHash ) ){ return 0; } return wikiOverrideHash; } /* ** Resolve a hyperlink. The zTarget argument is the content of the [...] ** in the wiki. Append to the output string whatever text is appropriate ** for opening the hyperlink. Write into zClose[0...nClose-1] text that will ** close the markup. ** ** If this routine determines that no hyperlink should be generated, then ** set zClose[0] to 0. ** ** Actually, this routine might or might not append the hyperlink, depending ** on current rendering rules: specifically does the current user have ** "History" permission. ** ** [http://fossil-scm.org/] ** [https://fossil-scm.org/] ** [ftp://fossil-scm.org/] ** [mailto:fossil-users@lists.fossil-scm.org] ** ** [/path] -> Refers to the root of the Fossil hierarchy, not ** the root of the URI domain ** ** [./relpath] ** [../relpath] ** ** [#fragment] ** ** [0123456789abcdef] ** ** [WikiPageName] ** [wiki:WikiPageName] ** ** [2010-02-27 07:13] ** ** [InterMap:Link] -> Interwiki link */ void wiki_resolve_hyperlink( Blob *pOut, /* Write the HTML output here */ int mFlags, /* Rendering option flags */ const char *zTarget, /* Hyperlink target; text within [...] */ char *zClose, /* Write hyperlink closing text here */ int nClose, /* Bytes available in zClose[] */ const char *zOrig, /* Complete document text */ const char *zTitle /* Title of the link */ ){ const char *zTerm = "</a>"; const char *z; char *zExtra = 0; const char *zExtraNS = 0; char *zRemote = 0; if( zTitle ){ zExtra = mprintf(" title='%h'", zTitle); zExtraNS = zExtra+1; }else if( mFlags & WIKI_TARGET_BLANK ){ zExtra = mprintf(" target='_blank'"); zExtraNS = zExtra+1; } assert( nClose>=20 ); if( strncmp(zTarget, "http:", 5)==0 || strncmp(zTarget, "https:", 6)==0 || strncmp(zTarget, "ftp:", 4)==0 || strncmp(zTarget, "mailto:", 7)==0 ){ blob_appendf(pOut, "<a href=\"%s\"%s>", zTarget, zExtra); }else if( zTarget[0]=='/' ){ blob_appendf(pOut, "<a href=\"%R%h\"%s>", zTarget, zExtra); }else if( zTarget[0]=='.' && (zTarget[1]=='/' || (zTarget[1]=='.' && zTarget[2]=='/')) && (mFlags & WIKI_LINKSONLY)==0 ){ blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra); }else if( zTarget[0]=='#' ){ blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra); }else if( is_valid_hname(zTarget) ){ int isClosed = 0; const char *zLB = (mFlags & WIKI_NOBRACKET)==0 ? "[" : ""; if( strlen(zTarget)<=HNAME_MAX && is_ticket(zTarget, &isClosed) ){ /* Special display processing for tickets. Display the hyperlink ** as crossed out if the ticket is closed. */ if( isClosed ){ if( g.perm.Hyperlink ){ blob_appendf(pOut, "%z<span class=\"wikiTagCancelled\">%s", xhref(zExtraNS,"%R/info/%s",zTarget), zLB ); zTerm = "]</span></a>"; }else{ blob_appendf(pOut,"<span class=\"wikiTagCancelled\">%s", zLB); zTerm = "]</span>"; } }else{ if( g.perm.Hyperlink ){ blob_appendf(pOut,"%z%s", xhref(zExtraNS,"%R/info/%s", zTarget),zLB); zTerm = "]</a>"; }else{ blob_appendf(pOut, "%s", zLB); zTerm = "]"; } } }else if( !in_this_repo(zTarget) ){ if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){ zTerm = ""; }else{ blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB); zTerm = "]</span>"; } }else if( g.perm.Hyperlink ){ blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB); zTerm = "]</a>"; }else{ zTerm = ""; } if( zTerm[0]==']' && (mFlags & WIKI_NOBRACKET)!=0 ) zTerm++; }else if( (zRemote = interwiki_url(zTarget))!=0 ){ blob_appendf(pOut, "<a href=\"%z\"%s>", zRemote, zExtra); zTerm = "</a>"; }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){ /* The link is to a valid wiki page name */ const char *zOverride = wiki_is_overridden(zTarget); if( zOverride ){ blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra); }else{ blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra); } }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ /* Dates or date-and-times in ISO8610 resolve to a link to the ** timeline for that date */ blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra); }else if( mFlags & WIKI_MARKDOWNLINKS ){ /* If none of the above, and if rendering links for markdown, then ** create a link to the literal text of the target */ blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra); }else if( zOrig && zTarget>=&zOrig[2] && zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){ /* If the hyperlink markup is not preceded by whitespace, then it ** is probably a C-language subscript or similar, not really a ** hyperlink. Just ignore it. */ zTerm = ""; }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){ /* Also ignore the link if various flags are set */ zTerm = ""; }else{ blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget); zTerm = "</span>"; } if( zExtra ) fossil_free(zExtra); assert( (int)strlen(zTerm)<nClose ); sqlite3_snprintf(nClose, zClose, "%s", zTerm); } /* ** Check to see if the given parsed markup is the correct ** </verbatim> tag. */ static int endVerbatim(Renderer *p, ParsedMarkup *pMarkup){ char *z; assert( p->inVerbatim ); if( pMarkup->iCode!=MARKUP_VERBATIM ) return 0; if( !pMarkup->endTag ) return 0; if( p->zVerbatimId==0 ) return 1; if( pMarkup->nAttr!=1 ) return 0; z = pMarkup->aAttr[0].zValue; return fossil_strcmp(z, p->zVerbatimId)==0; } /* ** z[] points to the text that immediately follows markup of the form: ** ** <verbatim type='pikchr ...'> ** ** zClass is the argument to "type". This routine will process the ** Pikchr text through the next matching </verbatim> (or until end-of-file) ** and append the resulting SVG output onto p. It then returns the ** number of bytes of text processed, including the closing </verbatim>. */ static int wiki_process_pikchr(Renderer *p, char *z, const char *zClass){ ParsedMarkup m; /* Parsed closing tag */ int i = 0; /* For looping over z[] in search of </verbatim> */ int iRet = 0; /* Value to return */ int atEnd = 0; /* True if se have found the </verbatim> */ int nMarkup = 0; /* Length of a markup we are checking */ /* Search for the closing </verbatim> tag */ while( z[i]!=0 ){ char *zEnd = strchr(z+i, '<'); if( zEnd==0 ){ i += (int)strlen(z+i); iRet = i; break; } nMarkup = html_tag_length(zEnd); if( nMarkup<11 || fossil_strnicmp(zEnd, "</verbatim", 10)!=0 ){ i = (int)(zEnd - z) + 1; continue; } (void)parseMarkup(&m, z+i); atEnd = endVerbatim(p, &m); unparseMarkup(&m); if( atEnd ){ iRet = i + nMarkup; break; } i++; } /* The Pikchr source text should be i character in length and iRet is ** i plus the number of bytes in the </verbatim>. Generate the reply. */ assert( strncmp(zClass,"pikchr",6)==0 ); zClass += 6; while( fossil_isspace(zClass[0]) ) zClass++; blob_append(p->pOut, "<p>", 3); pikchr_to_html(p->pOut, z, i, zClass, (int)strlen(zClass)); blob_append(p->pOut, "</p>\n", 5); return iRet; } /* ** Return the MUTYPE for the top of the stack. */ static int stackTopType(Renderer *p){ if( p->nStack<=0 ) return 0; |
︙ | ︙ | |||
1077 1078 1079 1080 1081 1082 1083 | ** This routine will probably modify the content of z[]. */ static void wiki_render(Renderer *p, char *z){ int tokenType; ParsedMarkup markup; int n; int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0; | > | > > > > > | | | | > > > | > | > | | | > | | > | | | | > > > | > | | | > | > > > > > > > > | | | | | | > > > | > > > | > | > > > > > > > > > | | < | | | | | 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 | ** This routine will probably modify the content of z[]. */ static void wiki_render(Renderer *p, char *z){ int tokenType; ParsedMarkup markup; int n; int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0; int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0; int linksOnly = (p->state & WIKI_LINKSONLY)!=0; char *zOrig = z; /* Make sure the attribute constants and names still align ** following changes in the attribute list. */ assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 ); while( z[0] ){ if( wikiHtmlOnly ){ n = nextRawToken(z, p, &tokenType); }else{ n = nextWikiToken(z, p, &tokenType); } p->state &= ~(AT_NEWLINE|AT_PARAGRAPH); switch( tokenType ){ case TOKEN_PARAGRAPH: { if( inlineOnly ){ /* blob_append_string(p->pOut, " ¶ "); */ blob_append_string(p->pOut, " "); }else{ if( p->wikiList ){ popStackToTag(p, p->wikiList); p->wikiList = 0; } endAutoParagraph(p); blob_append_string(p->pOut, "\n\n"); p->wantAutoParagraph = 1; } p->state |= AT_PARAGRAPH|AT_NEWLINE; break; } case TOKEN_NEWLINE: { if( p->renderFlags & WIKI_NEWLINE ){ blob_append_string(p->pOut, "<br>\n"); }else{ blob_append_string(p->pOut, "\n"); } p->state |= AT_NEWLINE; break; } case TOKEN_BUL_LI: { if( inlineOnly ){ blob_append_string(p->pOut, " • "); }else{ if( p->wikiList!=MARKUP_UL ){ if( p->wikiList ){ popStackToTag(p, p->wikiList); } endAutoParagraph(p); pushStack(p, MARKUP_UL); blob_append_string(p->pOut, "<ul>"); p->wikiList = MARKUP_UL; } popStackToTag(p, MARKUP_LI); startAutoParagraph(p); pushStack(p, MARKUP_LI); blob_append_string(p->pOut, "<li>"); } break; } case TOKEN_NUM_LI: { if( inlineOnly ){ blob_append_string(p->pOut, " # "); }else{ if( p->wikiList!=MARKUP_OL ){ if( p->wikiList ){ popStackToTag(p, p->wikiList); } endAutoParagraph(p); pushStack(p, MARKUP_OL); blob_append_string(p->pOut, "<ol>"); p->wikiList = MARKUP_OL; } popStackToTag(p, MARKUP_LI); startAutoParagraph(p); pushStack(p, MARKUP_LI); blob_append_string(p->pOut, "<li>"); } break; } case TOKEN_ENUM: { if( inlineOnly ){ blob_appendf(p->pOut, " (%d) ", atoi(z)); }else{ if( p->wikiList!=MARKUP_OL ){ if( p->wikiList ){ popStackToTag(p, p->wikiList); } endAutoParagraph(p); pushStack(p, MARKUP_OL); blob_append_string(p->pOut, "<ol>"); p->wikiList = MARKUP_OL; } popStackToTag(p, MARKUP_LI); startAutoParagraph(p); pushStack(p, MARKUP_LI); blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z)); } break; } case TOKEN_INDENT: { if( !inlineOnly ){ assert( p->wikiList==0 ); pushStack(p, MARKUP_BLOCKQUOTE); blob_append_string(p->pOut, "<blockquote>"); p->wantAutoParagraph = 0; p->wikiList = MARKUP_BLOCKQUOTE; } break; } case TOKEN_CHARACTER: { startAutoParagraph(p); if( z[0]=='<' ){ blob_append_string(p->pOut, "<"); }else if( z[0]=='&' ){ blob_append_string(p->pOut, "&"); } break; } case TOKEN_LINK: { char *zTarget; char *zDisplay = 0; int i, j; int savedState; char zClose[20]; char cS1 = 0; int iS1 = 0; startAutoParagraph(p); zTarget = &z[1]; for(i=1; z[i] && z[i]!=']'; i++){ if( z[i]=='|' && zDisplay==0 ){ zDisplay = &z[i+1]; for(j=i; j>0 && fossil_isspace(z[j-1]); j--){} iS1 = j; cS1 = z[j]; z[j] = 0; } } z[i] = 0; if( zDisplay==0 ){ zDisplay = zTarget + interwiki_removable_prefix(zTarget); }else{ while( fossil_isspace(*zDisplay) ) zDisplay++; } wiki_resolve_hyperlink(p->pOut, p->state, zTarget, zClose, sizeof(zClose), zOrig, 0); if( linksOnly || zClose[0]==0 || p->inVerbatim ){ if( cS1 ) z[iS1] = cS1; if( zClose[0]!=']' ){ blob_appendf(p->pOut, "[%h]%s", zTarget, zClose); }else{ blob_appendf(p->pOut, "%h%s", zTarget, zClose); } }else{ savedState = p->state; p->state &= ~ALLOW_WIKI; p->state |= FONT_MARKUP_ONLY; wiki_render(p, zDisplay); p->state = savedState; blob_append(p->pOut, zClose, -1); } break; } case TOKEN_TEXT: { int i; for(i=0; i<n && fossil_isspace(z[i]); i++){} if( i<n ) startAutoParagraph(p); blob_append(p->pOut, z, n); break; } case TOKEN_RAW: { if( linksOnly ){ htmlize_to_blob(p->pOut, z, n); }else{ 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 && (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0 ){ if( p->inVerbatim ){ p->inVerbatim = 0; p->state = p->preVerbState; blob_append_string(p->pOut, "</pre>"); } while( p->nStack>iDiv+1 ) popStack(p); if( p->aStack[iDiv].allowWiki ){ p->state |= ALLOW_WIKI; }else{ p->state &= ~ALLOW_WIKI; } assert( p->nStack==iDiv+1 ); p->nStack--; }else /* If within <verbatim id=ID> ignore everything other than ** </verbatim id=ID> and the </dev id=ID2> above. */ if( p->inVerbatim ){ if( endVerbatim(p, &markup) ){ p->inVerbatim = 0; p->state = p->preVerbState; blob_append_string(p->pOut, "</pre>"); }else{ unparseMarkup(&markup); blob_append_string(p->pOut, "<"); n = 1; } }else /* Render invalid markup literally. The markup appears in the ** final output as plain text. */ if( markup.iCode==MARKUP_INVALID ){ unparseMarkup(&markup); startAutoParagraph(p); blob_append_string(p->pOut, "<"); n = 1; }else /* If the markup is not font-change markup ignore it if the ** font-change-only flag is set. */ if( (markup.iType&MUTYPE_FONT)==0 && (p->state & FONT_MARKUP_ONLY)!=0 ){ |
︙ | ︙ | |||
1306 1307 1308 1309 1310 1311 1312 | /* Generate end-tags */ if( markup.endTag ){ popStackToTag(p, markup.iCode); }else /* Push <div> markup onto the stack together with the id=ID attribute. */ | | | > | | | > > | < | < > > | | > > > > > > > > > > | | > > | | | | > | > > > > > > > > > > < < < < < < < < < < < > | < < < | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > | > > | > > > | < < < | | | < < < | < | | < < < < | < < < < < < < | 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 | /* 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. */ if( markup.iCode==MARKUP_VERBATIM ){ int ii; //, vAttrDidAppend=0; const char *zClass = 0; p->zVerbatimId = 0; p->inVerbatim = 1; p->preVerbState = p->state; p->state &= ~ALLOW_WIKI; for(ii=0; ii<markup.nAttr; ii++){ if( markup.aAttr[ii].iACode == ATTR_ID ){ p->zVerbatimId = markup.aAttr[ii].zValue; }else if( markup.aAttr[ii].iACode==ATTR_TYPE ){ zClass = markup.aAttr[ii].zValue; }else if( markup.aAttr[ii].iACode==ATTR_LINKS && !is_false(markup.aAttr[ii].zValue) ){ p->state |= ALLOW_LINKS; } } endAutoParagraph(p); if( zClass==0 ){ blob_append_string(p->pOut, "<pre class='verbatim'>"); }else if( strncmp(zClass,"pikchr",6)==0 && (fossil_isspace(zClass[6]) || zClass[6]==0) ){ n += wiki_process_pikchr(p, z+n, zClass); p->inVerbatim = 0; p->state = p->preVerbState; }else{ blob_appendf(p->pOut, "<pre name='code' class='%h'>", zClass); } p->wantAutoParagraph = 0; }else if( markup.iType==MUTYPE_LI ){ if( backupToType(p, MUTYPE_LIST)==0 ){ endAutoParagraph(p); pushStack(p, MARKUP_UL); blob_append_string(p->pOut, "<ul>"); } pushStack(p, MARKUP_LI); renderMarkup(p->pOut, &markup); }else if( markup.iType==MUTYPE_TR ){ if( backupToType(p, MUTYPE_TABLE) ){ pushStack(p, MARKUP_TR); renderMarkup(p->pOut, &markup); } }else if( markup.iType==MUTYPE_TD ){ if( backupToType(p, MUTYPE_TABLE|MUTYPE_TR) ){ if( stackTopType(p)==MUTYPE_TABLE ){ pushStack(p, MARKUP_TR); blob_append_string(p->pOut, "<tr>"); } p->wantAutoParagraph = 0; pushStack(p, markup.iCode); renderMarkup(p->pOut, &markup); } }else if( markup.iType==MUTYPE_HYPERLINK ){ if( !isButtonHyperlink(p, &markup, z, &n) ){ popStackToTag(p, markup.iCode); startAutoParagraph(p); renderMarkup(p->pOut, &markup); pushStack(p, markup.iCode); } }else { if( markup.iType==MUTYPE_FONT ){ startAutoParagraph(p); }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){ p->wantAutoParagraph = 0; } if( markup.iCode==MARKUP_HR || markup.iCode==MARKUP_H1 || markup.iCode==MARKUP_H2 || markup.iCode==MARKUP_H3 || markup.iCode==MARKUP_H4 || markup.iCode==MARKUP_H5 || markup.iCode==MARKUP_P ){ endAutoParagraph(p); } if( (markup.iType & MUTYPE_STACK )!=0 ){ pushStack(p, markup.iCode); } renderMarkup(p->pOut, &markup); } break; } } z += n; } } /* ** Transform the text in the pIn blob. Write the results ** into the pOut blob. The pOut blob should already be ** initialized. The output is merely appended to pOut. ** If pOut is NULL, then the output is appended to the CGI ** reply. */ void wiki_convert(Blob *pIn, Blob *pOut, int flags){ Renderer renderer; memset(&renderer, 0, sizeof(renderer)); renderer.renderFlags = flags; renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; if( flags & WIKI_INLINE ){ renderer.wantAutoParagraph = 0; }else{ renderer.wantAutoParagraph = 1; } if( wikiUsesHtml() ){ renderer.state |= WIKI_HTMLONLY; } if( pOut ){ renderer.pOut = pOut; }else{ renderer.pOut = cgi_output_blob(); } blob_to_utf8_no_bom(pIn, 0); wiki_render(&renderer, blob_str(pIn)); endAutoParagraph(&renderer); while( renderer.nStack ){ popStack(&renderer); } blob_append_char(renderer.pOut, '\n'); free(renderer.aStack); } /* ** COMMAND: test-wiki-render ** ** Usage: %fossil test-wiki-render FILE [OPTIONS] ** ** Translate the input FILE from Fossil-wiki into HTML and write ** the resulting HTML on standard output. ** ** Options: ** --buttons Set the WIKI_BUTTONS flag ** --htmlonly Set the WIKI_HTMLONLY flag ** --linksonly Set the WIKI_LINKSONLY flag ** --nobadlinks Set the WIKI_NOBADLINKS flag ** --inline Set the WIKI_INLINE flag ** --noblock Set the WIKI_NOBLOCK flag ** --dark-pikchr Render pikchrs in dark mode */ void test_wiki_render(void){ Blob in, out; int flags = 0; if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS; if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY; if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY; if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS; if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE; if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK; if( find_option("dark-pikchr",0,0)!=0 ){ pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE ); } db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); verify_all_options(); if( g.argc!=3 ) usage("FILE"); blob_zero(&out); blob_read_from_file(&in, g.argv[2], ExtFILE); wiki_convert(&in, &out, flags); blob_write_to_file(&out, "-"); } /* ** COMMAND: test-markdown-render ** ** Usage: %fossil test-markdown-render FILE ... ** ** Render markdown in FILE as HTML on stdout. ** Options: ** ** --safe Restrict the output to use only "safe" HTML ** --lint-footnotes Print stats for footnotes-related issues ** --dark-pikchr Render pikchrs in dark mode */ void test_markdown_render(void){ Blob in, out; int i; int bSafe = 0, bFnLint = 0; db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); bSafe = find_option("safe",0,0)!=0; bFnLint = find_option("lint-footnotes",0,0)!=0; if( find_option("dark-pikchr",0,0)!=0 ){ pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE ); } verify_all_options(); for(i=2; i<g.argc; i++){ blob_zero(&out); blob_read_from_file(&in, g.argv[i], ExtFILE); if( g.argc>3 ){ fossil_print("<!------ %h ------->\n", g.argv[i]); } markdown_to_html(&in, 0, &out); safe_html_context( bSafe ? DOCSRC_UNTRUSTED : DOCSRC_TRUSTED ); safe_html(&out); blob_write_to_file(&out, "-"); blob_reset(&in); blob_reset(&out); } if( bFnLint && (g.ftntsIssues[0] || g.ftntsIssues[1] || g.ftntsIssues[2] || g.ftntsIssues[3] )){ fossil_fatal("There were issues with footnotes:\n" " %8d misreference%s\n" " %8d unreferenced\n" " %8d split\n" " %8d overnested", g.ftntsIssues[0], g.ftntsIssues[0]==1?"":"s", g.ftntsIssues[1], g.ftntsIssues[2], g.ftntsIssues[3]); } } /* ** Search for a <title>...</title> at the beginning of a wiki page. ** Return true (nonzero) if a title is found. Return zero if there is ** not title. ** ** If a title is found, initialize the pTitle blob to be the content ** of the title and initialize pTail to be the text that follows the ** title. */ int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ char *z; int i; 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: ** ** [target] ** [target|...] ** ** Where "target" can be either an artifact ID prefix or a wiki page ** name. For each such hyperlink found, add an entry to the ** backlink table. */ void wiki_extract_links( char *z, /* The wiki text from which to extract links */ Backlink *pBklnk, /* Backlink extraction context */ int flags /* wiki parsing flags */ ){ Renderer renderer; int tokenType; ParsedMarkup markup; int n; int inlineOnly; int wikiHtmlOnly = 0; memset(&renderer, 0, sizeof(renderer)); renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH; if( flags & WIKI_NOBLOCK ){ renderer.state |= INLINE_MARKUP_ONLY; } if( wikiUsesHtml() ){ renderer.state |= WIKI_HTMLONLY; wikiHtmlOnly = 1; } inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0; while( z[0] ){ if( wikiHtmlOnly ){ n = nextRawToken(z, &renderer, &tokenType); }else{ n = nextWikiToken(z, &renderer, &tokenType); } switch( tokenType ){ case TOKEN_LINK: { char *zTarget; int i; zTarget = &z[1]; for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){} while(i>1 && zTarget[i-1]==' '){ i--; } backlink_create(pBklnk, zTarget, i); break; } case TOKEN_MARKUP: { const char *zId; int iDiv; parseMarkup(&markup, z); |
︙ | ︙ | |||
1623 1624 1625 1626 1627 1628 1629 | }else /* Enter <verbatim> processing. With verbatim enabled, all other ** markup other than the corresponding end-tag with the same ID is ** ignored. */ if( markup.iCode==MARKUP_VERBATIM ){ | | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | }else /* Enter <verbatim> processing. With verbatim enabled, all other ** markup other than the corresponding end-tag with the same ID is ** ignored. */ if( markup.iCode==MARKUP_VERBATIM ){ int vAttrIdx; renderer.zVerbatimId = 0; renderer.inVerbatim = 1; renderer.preVerbState = renderer.state; renderer.state &= ~ALLOW_WIKI; for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){ if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){ renderer.zVerbatimId = markup.aAttr[0].zValue; } } renderer.wantAutoParagraph = 0; } /* Restore the input text to its original configuration */ unparseMarkup(&markup); break; } default: { break; } } z += n; } free(renderer.aStack); } /* ** Return the length, in bytes, of the HTML token that z is pointing to. */ int html_token_length(const char *z){ int n; char c; if( (c=z[0])=='<' ){ n = html_tag_length(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; } /* ** z points to someplace in the middle of HTML markup. Return the length ** of the subtoken that starts on z. */ int html_subtoken_length(const char *z){ int n; char c; c = z[0]; if( fossil_isspace(c) ){ for(n=1; z[n] && fossil_isspace(z[n]); n++){} return n; } if( c=='"' || c=='\'' ){ for(n=1; z[n] && z[n]!=c && z[n]!='>'; n++){} if( z[n]==c ) n++; return n; } if( c=='>' ){ return 0; } if( c=='=' ){ return 1; } if( fossil_isalnum(c) || c=='/' ){ for(n=1; (c=z[n])!=0 && (fossil_isalnum(c) || c=='-' || c=='_'); n++){} return n; } return 1; } /* ** z points to an HTML markup token: <TAG ATTR=VALUE ...> ** This routine looks for the VALUE associated with zAttr and returns ** a pointer to the start of that value and sets *pLen to be the length ** in bytes for the value. Or it returns NULL if no such attr exists. */ const char *html_attribute(const char *zMarkup, const char *zAttr, int *pLen){ int i = 1; int n; int nAttr; int iMatchCnt = 0; assert( zMarkup[0]=='<' ); assert( zMarkup[1]!=0 ); n = html_subtoken_length(zMarkup+i); if( n==0 ) return 0; i += n; nAttr = (int)strlen(zAttr); while( 1 ){ const char *zStart = zMarkup+i; n = html_subtoken_length(zStart); if( n==0 ) break; i += n; if( fossil_isspace(zStart[0]) ) continue; if( n==nAttr && fossil_strnicmp(zAttr,zStart,nAttr)==0 ){ iMatchCnt = 1; }else if( n==1 && zStart[0]=='=' && iMatchCnt==1 ){ iMatchCnt = 2; }else if( iMatchCnt==2 ){ if( (zStart[0]=='"' || zStart[0]=='\'') && zStart[n-1]==zStart[0] ){ zStart++; n -= 2; } *pLen = n; return zStart; }else{ iMatchCnt = 0; } } return 0; } /* ** COMMAND: test-html-tokenize ** ** Tokenize an HTML file. Return the offset and length and text of ** each token - one token per line. Omit white-space tokens. */ void test_html_tokenize(void){ Blob in; char *z; int i; int iOfst, n; for(i=2; i<g.argc; i++){ blob_read_from_file(&in, g.argv[i], ExtFILE); z = blob_str(&in); for(iOfst=0; z[iOfst]; iOfst+=n){ n = html_token_length(z+iOfst); if( fossil_isspace(z[iOfst]) ) continue; fossil_print("%d %d %.*s\n", iOfst, n, n, z+iOfst); if( z[iOfst]=='<' && n>1 ){ int j,k; for(j=iOfst+1; (k = html_subtoken_length(z+j))>0; j+=k){ if( fossil_isspace(z[j]) || z[j]=='=' ) continue; fossil_print("# %d %d %.*s\n", j, k, k, z+j); } } } blob_reset(&in); } } /* ** Attempt to reformat messy HTML to be easily readable by humans. ** ** * Try to keep lines less than 80 characters in length ** * Collapse white space into a single space ** * Put a blank line before: ** <blockquote><center><code><hN><p><pre><table> ** * Put a newline after <br> and <hr> ** * Start each of the following elements on a new line: ** <address><cite><dd><div><dl><dt><li><ol><samp> ** <tbody><td><tfoot><th><thead><tr><ul> ** ** Except, do not do any reformatting inside of <pre>...</pre> */ void htmlTidy(const char *zIn, Blob *pOut){ int n; int nPre = 0; int iCur = 0; int wantSpace = 0; int omitSpace = 1; while( zIn[0] ){ n = html_token_length(zIn); if( zIn[0]=='<' && n>1 ){ int i, j; 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_PRE ){ if( isCloseTag ){ nPre--; blob_append(pOut, zIn, n); zIn += n; if( nPre==0 ){ blob_append_char(pOut, '\n'); iCur = 0; } continue; }else{ if( iCur && nPre==0 ){ blob_append_char(pOut, '\n'); iCur = 0; } nPre++; } }else if( eType & (MUTYPE_BLOCK|MUTYPE_TABLE) ){ if( !isCloseTag && nPre==0 && blob_size(pOut)>0 ){ blob_append(pOut, "\n\n", 1 + (iCur>0)); iCur = 0; } wantSpace = 0; omitSpace = 1; }else if( (eType & (MUTYPE_LIST|MUTYPE_LI|MUTYPE_TR|MUTYPE_TD))!=0 || eTag==MARKUP_HR ){ if( nPre==0 && (!isCloseTag || (eType&MUTYPE_LIST)!=0) && iCur>0 ){ blob_append_char(pOut, '\n'); iCur = 0; } wantSpace = 0; omitSpace = 1; } if( wantSpace && nPre==0 ){ if( iCur+n+1>=80 ){ blob_append_char(pOut, '\n'); iCur = 0; }else{ blob_append_char(pOut, ' '); iCur++; } } blob_append(pOut, zIn, n); iCur += n; wantSpace = 0; if( eTag==MARKUP_BR || eTag==MARKUP_HR ){ blob_append_char(pOut, '\n'); iCur = 0; } }else if( fossil_isspace(zIn[0]) ){ if( nPre ){ blob_append(pOut, zIn, n); }else{ wantSpace = !omitSpace; } }else{ if( wantSpace && nPre==0 ){ if( iCur+n+1>=80 ){ blob_append_char(pOut, '\n'); iCur = 0; }else{ blob_append_char(pOut, ' '); iCur++; } } blob_append(pOut, zIn, n); iCur += n; wantSpace = omitSpace = 0; } zIn += n; } if( iCur ) blob_append_char(pOut, '\n'); } /* ** COMMAND: test-html-tidy ** ** Run the htmlTidy() routine on the content of all files named on ** the command-line and write the results to standard output. */ void test_html_tidy(void){ Blob in, out; int i; for(i=2; i<g.argc; i++){ blob_read_from_file(&in, g.argv[i], ExtFILE); blob_zero(&out); htmlTidy(blob_str(&in), &out); blob_reset(&in); fossil_puts(blob_buffer(&out), 0, blob_size(&out)); 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 = html_token_length(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 = html_token_length(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_char(pOut, '\n'); 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_char(pOut, nNL ? '\n' : ' '); 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<count(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_char(pOut, c); nWS = 1; nNL = c=='\n'; }else{ if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); seenText = 1; nNL = nWS = 0; blob_append_char(pOut, c); } }else{ if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); seenText = 1; nNL = nWS = 0; blob_append(pOut, zIn, n); } zIn += n; } if( nNL==0 ) blob_append_char(pOut, '\n'); } /* ** COMMAND: test-html-to-text ** ** Usage: %fossil test-html-to-text FILE ... ** ** Read all files named on the command-line. Convert the file ** content from HTML to text and write the results on standard ** output. ** ** This command is intended as a test and debug interface for ** the html_to_plaintext() routine. */ 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], ExtFILE); blob_zero(&out); html_to_plaintext(blob_str(&in), &out); blob_reset(&in); fossil_puts(blob_buffer(&out), 0, blob_size(&out)); blob_reset(&out); } } /**************************************************************************** ** safe-html: ** ** An interface for preventing HTML constructs (ex: <style>, <form>, etc) ** from being inserted into Wiki and Forum posts using Markdown. See the ** comment on safe_html_append() for additional information on what is meant ** by "safe". ** ** The safe-html restrictions only apply to Markdown, as Fossil-Wiki only ** allows safe-html by design - unsafe-HTML is never and has never been ** allowed in Fossil-Wiki. ** ** This code is in the wikiformat.c file so that it can have access to the ** white-list of acceptable HTML in the aMarkup[] array. */ /* ** An instance of this object keeps track of the nesting of HTML ** elements for safe_html_append(). */ typedef struct HtmlTagStack HtmlTagStack; struct HtmlTagStack { int n; /* Current tag stack depth */ int nAlloc; /* Space allocated for aStack[] */ int *aStack; /* The stack of tags */ int aSpace[10]; /* Initial static space, to avoid malloc() */ }; /* ** Initialize bulk memory to a valid empty tagstack. */ static void html_tagstack_init(HtmlTagStack *p){ p->n = 0; p->nAlloc = 0; p->aStack = p->aSpace; } /* ** Push a new element onto the tag statk */ static void html_tagstack_push(HtmlTagStack *p, int e){ if( p->n>=ArraySize(p->aSpace) && p->n>=p->nAlloc ){ if( p->nAlloc==0 ){ int *aNew; p->nAlloc = 50; aNew = fossil_malloc( sizeof(p->aStack[0])*p->nAlloc ); memcpy(aNew, p->aStack, sizeof(p->aStack[0])*p->n ); p->aStack = aNew; }else{ p->nAlloc *= 2; p->aStack = fossil_realloc(p->aStack, sizeof(p->aStack[0])*p->nAlloc ); } } p->aStack[p->n++] = e; } /* ** Clear a tag stack, reclaiming any memory allocations. */ static void html_tagstack_clear(HtmlTagStack *p){ if( p->nAlloc ){ fossil_free(p->aStack); p->nAlloc = 0; p->aStack = p->aSpace; } p->n = 0; } /* ** The HTML end-tag eEnd wants to be added to pBlob. ** ** If an open-tag for eEnd exists anywhere on the stack, then ** pop it and all prior elements from the task, issuing appropriate ** end-tags as you go. ** ** If there is no open-tag for eEnd on the stack, then this ** routine is a no-op. */ static void html_tagstack_pop(HtmlTagStack *p, Blob *pBlob, int eEnd){ int i, e; if( eEnd!=0 ){ for(i=p->n-1; i>=0 && p->aStack[i]!=eEnd; i--){} if( i<0 ){ blob_appendf(pBlob, "<span class='error'></%s></span>", aMarkup[eEnd].zName); return; } }else if( p->n==0 ){ return; } do{ e = p->aStack[--p->n]; if( e==eEnd || (aMarkup[e].iType & MUTYPE_Nested)!=0 ){ blob_appendf(pBlob, "</%s>", aMarkup[e].zName); } }while( e!=eEnd && p->n>0 ); } /* ** Return a nonce to indicate that safe_html() can allow code through ** without censoring. ** ** When safe_html() is asked to sanitize some HTML, it will ignore ** any text in between two consecutive instances of the nonce. The ** nonce itself is an HTML comment so it is harmless to keep the ** nonce in the middle of the HTML stream. A different nonce is ** choosen each time Fossil is run, using a lot of randomness, so ** an attacker will be unable to guess the nonce in advance. ** ** The original use-case for this mechanism is to allow Pikchr-generated ** SVG in the middle of HTML generated from Markdown. The Markdown ** output will normally be processed by safe_html() to prevent accidental ** or malicious introduction of harmful HTML (ex: <script>) in the ** output stream. The safe_html() only lets through HTML elements ** that are on its allow-list and SVG is not on that list. Hence, in order ** to allow the Pikchr-generated SVG through, it must be surrounded by ** the nonce. */ const char *safe_html_nonce(int bGenerate){ static char *zNonce = 0; if( zNonce==0 && bGenerate ){ zNonce = db_text(0, "SELECT '<!--'||hex(randomblob(32))||'-->';"); } return zNonce; } #define SAFE_NONCE_SIZE (4+64+3) /* ** Append a safe translation of HTML text to a Blob object. ** ** Restriction: The input to this routine must be writable. * Temporary changes may be made to the input, but the input is restored ** to its original state prior to returning. If zHtml[nHtml] is not a ** zero character, then a zero might be written in that position ** temporarily, but that slot will also be restored before this routine ** returns. */ static void safe_html_append(Blob *pBlob, char *zHtml, int nHtml){ char cLast; int i, j, n; HtmlTagStack s; ParsedMarkup markup; const char *zNonce; char *z; if( nHtml<=0 ) return; cLast = zHtml[nHtml]; zHtml[nHtml] = 0; html_tagstack_init(&s); i = 0; while( i<nHtml ){ if( zHtml[i]=='<' ){ j = i; }else{ z = strchr(zHtml+i, '<'); if( z==0 ){ blob_append(pBlob, zHtml+i, nHtml-i); break; } j = (int)(z - zHtml); blob_append(pBlob, zHtml+i, j-i); } if( zHtml[j+1]=='!' && j+2*SAFE_NONCE_SIZE<nHtml && (zNonce = safe_html_nonce(0))!=0 && strncmp(zHtml+j,zNonce,SAFE_NONCE_SIZE)==0 && (z = strstr(zHtml+j+SAFE_NONCE_SIZE,zNonce))!=0 ){ i = (int)(z - zHtml) + SAFE_NONCE_SIZE; blob_append(pBlob, zHtml+j, i-j); continue; } n = html_tag_length(zHtml+j); if( n==0 ){ blob_append(pBlob, "<", 4); i = j+1; continue; }else{ i = j + n; } parseMarkup(&markup, zHtml+j); if( markup.iCode==MARKUP_INVALID ){ unparseMarkup(&markup); blob_appendf(pBlob, "<span class='error'><%.*s></span>", n-2, zHtml+j+1); continue; } if( (markup.iType & MUTYPE_Nested)==0 || markup.iCode==MARKUP_P ){ renderMarkup(pBlob, &markup); }else{ if( markup.endTag ){ html_tagstack_pop(&s, pBlob, markup.iCode); }else{ renderMarkup(pBlob, &markup); html_tagstack_push(&s, markup.iCode); } } unparseMarkup(&markup); } html_tagstack_pop(&s, pBlob, 0); html_tagstack_clear(&s); zHtml[nHtml] = cLast; } /* ** This local variable is true if the safe_html() function is enabled. ** In other words, this is true if the output of Markdown should be ** restricted to use only "safe" HTML. */ static int safeHtmlEnable = 1; #if INTERFACE /* ** Allowed values for the eTrust parameter to safe_html_context(). */ #define DOCSRC_FILE 1 /* Document is a checked-in file */ #define DOCSRC_FORUM 2 /* Document is a forum post */ #define DOCSRC_TICKET 3 /* Document is a ticket comment */ #define DOCSRC_WIKI 4 /* Document is a wiki page */ #define DOCSRC_TRUSTED 5 /* safe_html() is always a no-op */ #define DOCSRC_UNTRUSTED 6 /* safe_html() is always enabled */ #endif /* INTERFACE */ /* ** Specify the context in which a markdown document with potentially ** unsafe HTML will be rendered. */ void safe_html_context(int eTrust){ static const char *zSafeHtmlSetting = 0; char cPerm = 0; if( eTrust==DOCSRC_TRUSTED ){ safeHtmlEnable = 0; return; } if( eTrust==DOCSRC_UNTRUSTED ){ safeHtmlEnable = 1; return; } if( zSafeHtmlSetting==0 ){ zSafeHtmlSetting = db_get("safe-html", ""); } switch( eTrust ){ case DOCSRC_FILE: cPerm = 'b'; break; case DOCSRC_FORUM: cPerm = 'f'; break; case DOCSRC_TICKET: cPerm = 't'; break; case DOCSRC_WIKI: cPerm = 'w'; break; } safeHtmlEnable = (strchr(zSafeHtmlSetting,cPerm)==0); } /* ** SETTING: safe-html width=8 ** This setting controls whether or not unsafe HTML elements ** (such as SCRIPT or STYLE tags) are allowed in Markdown-formatted ** documents. Unsafe HTML is disabled by default. If this setting ** exists and is a string, then letters in that string can enable ** unsafe HTML in various contexts: ** ** - b Unsafe HTML allowed in embedded documentation ** - f Unsafe HTML allowed in forum posts ** - t Unsafe HTML allowed in tickets ** - w Unsafe HTML allowed on wiki pages */ /* ** The input blob contains HTML. If safe-html is enabled, then ** convert the input into "safe HTML". The following modifications ** are made: ** ** 1. Remove any elements that are not on the AllowedMarkup list. ** (ex: <script>, <form>, etc.) ** ** 2. Remove any attributes that are not on the AllowedMarkup list. ** (ex: onload=, etc.) ** ** 3. Omit any surplus close-tags. This prevents the script from ** terminating an <div> or similar in the outer context. ** ** 4. Insert additional close-tags as necessary so that any ** tag in the input that needs a close-tag has one. This ** prevents tags in the embedded script from affecting the ** display of content that follows this script in the enclosing ** context. ** ** These modifications are intended to make the generated HTML safe ** to be embedded in a larger HTML document, such that the embedded ** HTML has no influence on the formatting and operation of the ** larger document. ** ** If safe-html is disabled, then this routine is a no-op. */ void safe_html(Blob *in){ Blob out; /* Holding area for the revised text during construction */ char *z; /* Original input text */ int n; /* Number of bytes in the original input text */ int k; if( safeHtmlEnable==0 ) return; z = blob_str(in); n = blob_size(in); blob_init(&out, 0, 0); while( fossil_isspace(z[0]) ){ z++; n--; } for(k=n-1; k>5 && fossil_isspace(z[k]); k--){} if( fossil_strnicmp(z, "<div",4)==0 && !fossil_isalpha(z[4]) && fossil_strnicmp(z+k-5, "</div>",6)==0 ){ /* The input contains an outer <div>...</div>. Preserve the ** full scope of that <div>. */ int m = html_tag_length(z); k -= 5; blob_append(&out, z, m); safe_html_append(&out, z+m, k-m); blob_append(&out, z+k, n-k); }else{ safe_html_append(&out, z, n); } blob_reset(in); *in = out; } /* ** COMMAND: test-safe-html ** ** Usage: %fossil test-safe-html FILE ... ** ** Read files named on the command-line. Send the text of each file ** through safe_html_append() and then write the result on ** standard output. */ void test_safe_html_cmd(void){ int i; Blob x; for(i=2; i<g.argc; i++){ char *z; int n; blob_read_from_file(&x, g.argv[i], ExtFILE); blob_terminate(&x); safe_html(&x); z = blob_str(&x); n = blob_size(&x); while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ) n--; fossil_print("%.*s\n", n, z); blob_reset(&x); } } |
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 293 294 295 | /* ** 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 eFType is RepoFile and the allow-symlinks ** setting is on. But as windows does not support symbolic links, the ** eFType parameter is ignored here. */ int win32_stat(const wchar_t *zFilename, struct fossilStat *buf, int eFType){ 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_path_to_utf8(zWide); fossil_free(zWide); for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/'; strncpy(zBuf, zUtf8, nBuf); fossil_path_free(zUtf8); } #endif /* _WIN32 -- This code is for win32 only */ |
Changes to src/winhttp.c.
︙ | ︙ | |||
12 13 14 15 16 17 18 | ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file implements a very simple (and low-performance) HTTP server | | > > > | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > | | | > > | > | > > > > > > > | > > > > > | | > | | | > > > > > | | > | | | > > > > > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | > | | > > > | < > > > > > | | | > > > > | > > > > < < > > > > > | > > > > > > > > | > > > | > > > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > < | < < | < < < < < < | | | | > | > | > | | > > | > > > > > > > > > > | > > > > > | > > | | | | > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > | > > > > > > | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > | < > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > | > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file implements a very simple (and low-performance) HTTP server ** for windows. It also implements a Windows Service which allows the HTTP ** server to be run without any user logged on. */ #include "config.h" #ifdef _WIN32 /* This code is for win32 only */ # if !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0501 # endif #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <process.h> #include "winhttp.h" #ifndef IPV6_V6ONLY # define IPV6_V6ONLY 27 /* Because this definition is missing in MinGW */ #endif /* ** The SocketAddr structure holds a SOCKADDR_STORAGE and its content size. */ typedef struct SocketAddr SocketAddr; struct SocketAddr { SOCKADDR_STORAGE addr; int len; }; static char* SocketAddr_toString(const SocketAddr* pAddr){ SocketAddr addr; char* zIp; DWORD nIp = 50; assert( pAddr!=NULL ); memcpy(&addr, pAddr, sizeof(SocketAddr)); if( addr.len==sizeof(SOCKADDR_IN6) ){ ((SOCKADDR_IN6*)&addr)->sin6_port = 0; }else{ ((SOCKADDR_IN*)&addr)->sin_port = 0; } zIp = fossil_malloc(nIp); if( WSAAddressToStringA((SOCKADDR*)&addr, addr.len, NULL, zIp, &nIp)!=0 ){ zIp[0] = 0; } return zIp; } /* ** The DualAddr structure holds two SocketAddr (one IPv4 and on IPv6). */ typedef struct DualAddr DualAddr; struct DualAddr { SocketAddr a4; /* IPv4 SOCKADDR_IN */ SocketAddr a6; /* IPv6 SOCKADDR_IN6 */ }; static void DualAddr_init(DualAddr* pDA){ assert( pDA!=NULL ); memset(pDA, 0, sizeof(DualAddr)); pDA->a4.len = sizeof(SOCKADDR_IN); pDA->a6.len = sizeof(SOCKADDR_IN6); } /* ** The DualSocket structure holds two SOCKETs. One or both could be ** used or INVALID_SOCKET. One is dedicated to IPv4, the other to IPv6. */ typedef struct DualSocket DualSocket; struct DualSocket { SOCKET s4; /* IPv4 socket or INVALID_SOCKET */ SOCKET s6; /* IPv6 socket or INVALID_SOCKET */ }; /* ** Initializes a DualSocket. */ static void DualSocket_init(DualSocket* ds){ assert( ds!=NULL ); ds->s4 = INVALID_SOCKET; ds->s6 = INVALID_SOCKET; }; /* ** Close and reset a DualSocket. */ static void DualSocket_close(DualSocket* ds){ assert( ds!=NULL ); if( ds->s4!=INVALID_SOCKET ){ closesocket(ds->s4); ds->s4 = INVALID_SOCKET; } if( ds->s6!=INVALID_SOCKET ){ closesocket(ds->s6); ds->s6 = INVALID_SOCKET; } }; /* ** When ip is "W", listen to wildcard address (IPv4/IPv6 as available). ** When ip is "L", listen to loopback address (IPv4/IPv6 as available). ** Else listen only the specified ip, which is either IPv4 or IPv6 or invalid. ** Returns 1 on success, 0 on failure. */ static int DualSocket_listen(DualSocket* ds, const char* zIp, int iPort){ SOCKADDR_IN addr4; SOCKADDR_IN6 addr6; assert( ds!=NULL && zIp!=NULL && iPort!=0 ); DualSocket_close(ds); memset(&addr4, 0, sizeof(addr4)); memset(&addr6, 0, sizeof(addr6)); if (strcmp(zIp, "W")==0 || strcmp(zIp, "L")==0 ){ ds->s4 = socket(AF_INET, SOCK_STREAM, 0); ds->s6 = socket(AF_INET6, SOCK_STREAM, 0); if( ds->s4==INVALID_SOCKET && ds->s6==INVALID_SOCKET ){ return 0; } if (ds->s4!=INVALID_SOCKET ) { addr4.sin_family = AF_INET; addr4.sin_port = htons(iPort); if( strcmp(zIp, "L")==0 ){ addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); }else{ addr4.sin_addr.s_addr = INADDR_ANY; } } if( ds->s6!=INVALID_SOCKET ) { DWORD ipv6only = 1; /* don't want a dual-stack socket */ setsockopt(ds->s6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only)); addr6.sin6_family = AF_INET6; addr6.sin6_port = htons(iPort); if( strcmp(zIp, "L")==0 ){ memcpy(&addr6.sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)); }else{ memcpy(&addr6.sin6_addr, &in6addr_any, sizeof(in6addr_any)); } } }else{ if( strstr(zIp, ".") ){ int addrlen = sizeof(addr4); ds->s4 = socket(AF_INET, SOCK_STREAM, 0); if( ds->s4==INVALID_SOCKET ){ return 0; } addr4.sin_family = AF_INET; if (WSAStringToAddress((char*)zIp, AF_INET, NULL, (struct sockaddr *)&addr4, &addrlen) != 0){ return 0; } addr4.sin_port = htons(iPort); }else{ DWORD ipv6only = 1; /* don't want a dual-stack socket */ int addrlen = sizeof(addr6); ds->s6 = socket(AF_INET6, SOCK_STREAM, 0); if( ds->s6==INVALID_SOCKET ){ return 0; } setsockopt(ds->s6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only)); addr6.sin6_family = AF_INET6; if (WSAStringToAddress((char*)zIp, AF_INET6, NULL, (struct sockaddr *)&addr6, &addrlen) != 0){ return 0; } addr6.sin6_port = htons(iPort); } } assert( ds->s4!=INVALID_SOCKET || ds->s6!=INVALID_SOCKET ); if( ds->s4!=INVALID_SOCKET && bind(ds->s4, (struct sockaddr*)&addr4, sizeof(addr4))==SOCKET_ERROR ){ return 0; } if( ds->s6!=INVALID_SOCKET && bind(ds->s6, (struct sockaddr*)&addr6, sizeof(addr6))==SOCKET_ERROR ){ return 0; } if( ds->s4!=INVALID_SOCKET && listen(ds->s4, SOMAXCONN)==SOCKET_ERROR ){ return 0; } if( ds->s6!=INVALID_SOCKET && listen(ds->s6, SOMAXCONN)==SOCKET_ERROR ){ return 0; } return 1; }; /* ** Accepts connections on DualSocket. */ static void DualSocket_accept(DualSocket* pListen, DualSocket* pClient, DualAddr* pClientAddr){ fd_set rs; int rs_count = 0; assert( pListen!=NULL && pClient!=NULL && pClientAddr!= NULL ); DualSocket_init(pClient); DualAddr_init(pClientAddr); FD_ZERO(&rs); if( pListen->s4!=INVALID_SOCKET ){ FD_SET(pListen->s4, &rs); ++rs_count; } if( pListen->s6!=INVALID_SOCKET ){ FD_SET(pListen->s6, &rs); ++rs_count; } if( select(rs_count, &rs, 0, 0, 0 /*blocking*/)==SOCKET_ERROR ){ return; } if( FD_ISSET(pListen->s4, &rs) ){ pClient->s4 = accept(pListen->s4, (struct sockaddr*)&pClientAddr->a4.addr, &pClientAddr->a4.len); } if( FD_ISSET(pListen->s6, &rs) ){ pClient->s6 = accept(pListen->s6, (struct sockaddr*)&pClientAddr->a6.addr, &pClientAddr->a6.len); } } /* ** The HttpServer structure holds information about an instance of ** the HTTP server itself. */ typedef struct HttpServer HttpServer; struct HttpServer { HANDLE hStoppedEvent; /* Event to signal when server is stopped, ** must be closed by callee. */ char *zStopper; /* The stopper file name, must be freed by ** callee. */ DualSocket listener; /* Sockets on which the server is listening, ** may be closed by callee. */ }; /* ** The HttpRequest structure holds information about each incoming ** HTTP request. */ typedef struct HttpRequest HttpRequest; struct HttpRequest { int id; /* ID counter */ SOCKET s; /* Socket on which to receive data */ SocketAddr addr; /* Address from which data is coming */ int flags; /* Flags passed to win32_http_server() */ const char *zOptions; /* --baseurl, --notfound, --localauth, --th-trace */ }; /* ** Prefix for a temporary file. */ static char *zTempPrefix; /* ** Look at the HTTP header contained in zHdr. Find the content ** length and return it. Return 0 if there is no Content-Length: ** header line. */ static int find_content_length(const char *zHdr){ while( *zHdr ){ if( zHdr[0]=='\n' ){ if( zHdr[1]=='\r' ) return 0; if( fossil_strnicmp(&zHdr[1], "content-length:", 15)==0 ){ 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); } /* ** Make sure the server stops as soon as possible after the stopper file ** is found. If there is no stopper file name, do nothing. */ static void win32_server_stopper(void *pAppData){ HttpServer *p = (HttpServer*)pAppData; if( p!=0 ){ HANDLE hStoppedEvent = p->hStoppedEvent; const char *zStopper = p->zStopper; if( hStoppedEvent!=NULL && zStopper!=0 ){ while( 1 ){ DWORD dwResult = WaitForMultipleObjectsEx(1, &hStoppedEvent, FALSE, 1000, TRUE); if( dwResult!=WAIT_IO_COMPLETION && dwResult!=WAIT_TIMEOUT ){ /* The event is either invalid, signaled, or abandoned. Bail ** out now because those conditions should indicate the parent ** thread is dead or dying. */ break; } if( file_size(zStopper, ExtFILE)>=0 ){ /* The stopper file has been found. Attempt to close the server ** listener socket now and then exit. */ DualSocket_close(&p->listener); break; } } } if( hStoppedEvent!=NULL ){ CloseHandle(hStoppedEvent); p->hStoppedEvent = NULL; } if( zStopper!=0 ){ fossil_free(p->zStopper); p->zStopper = 0; } fossil_free(p); } } /* ** Process a single incoming HTTP request. */ static void win32_http_request(void *pAppData){ HttpRequest *p = (HttpRequest*)pAppData; FILE *in = 0, *out = 0, *aux = 0; int amt, got, i; int wanted = 0; char *z; char *zIp; void *sslConn = 0; char zCmdFName[MAX_PATH]; char zRequestFName[MAX_PATH]; char zReplyFName[MAX_PATH]; char zCmd[2000]; /* Command-line to process the request */ char zBuf[65536]; /* The HTTP request header */ const int szHdr = 4000; /* Reduced header size */ sqlite3_snprintf(MAX_PATH, zCmdFName, "%s_%06d_cmd.txt", zTempPrefix, p->id); sqlite3_snprintf(MAX_PATH, zRequestFName, "%s_%06d_in.txt", zTempPrefix, p->id); sqlite3_snprintf(MAX_PATH, zReplyFName, "%s_%06d_out.txt", zTempPrefix, p->id); amt = 0; if( g.httpUseSSL ){ #ifdef FOSSIL_ENABLE_SSL sslConn = ssl_new_server(p->s); #endif } while( amt<szHdr ){ if( sslConn ){ #ifdef FOSSIL_ENABLE_SSL got = ssl_read_server(sslConn, &zBuf[amt], szHdr-1-amt, 0); #endif }else{ got = recv(p->s, &zBuf[amt], szHdr-1-amt, 0); if( got==SOCKET_ERROR ) goto end_request; } if( got==0 ){ wanted = 0; break; } amt += got; zBuf[amt] = 0; z = strstr(zBuf, "\r\n\r\n"); if( z ){ wanted = find_content_length(zBuf) + (&z[4]-zBuf) - amt; break; }else{ z = strstr(zBuf, "\n\n"); if( z ){ wanted = find_content_length(zBuf) + (&z[2]-zBuf) - amt; break; } } } if( amt>=szHdr ) goto end_request; out = fossil_fopen(zRequestFName, "wb"); if( out==0 ) goto end_request; fwrite(zBuf, 1, amt, out); while( wanted>0 ){ if( sslConn ){ #ifdef FOSSIL_ENABLE_SSL got = ssl_read_server(sslConn, zBuf, min(wanted, sizeof(zBuf)), 1); #endif }else{ got = recv(p->s, zBuf, sizeof(zBuf), 0); if( got==SOCKET_ERROR ) goto end_request; } if( got>0 ){ fwrite(zBuf, 1, got, out); }else{ break; } wanted -= got; } /* ** The repository name is only needed if there was no open check-out. This ** is designed to allow the open check-out for the interactive user to work ** with the local Fossil server started via the "ui" command. */ zIp = SocketAddr_toString(&p->addr); if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){ assert( g.zRepositoryName && g.zRepositoryName[0] ); sqlite3_snprintf(sizeof(zCmd), zCmd, "%s--in %s\n--out %s\n--ipaddr %s\n%s", get_utf8_bom(0), zRequestFName, zReplyFName, zIp, g.zRepositoryName ); }else{ sqlite3_snprintf(sizeof(zCmd), zCmd, "%s--in %s\n--out %s\n--ipaddr %s", get_utf8_bom(0), zRequestFName, zReplyFName, zIp ); } fossil_free(zIp); aux = fossil_fopen(zCmdFName, "wb"); if( aux==0 ) goto end_request; fwrite(zCmd, 1, strlen(zCmd), aux); sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http -args \"%s\"%s%s", g.nameOfExe, zCmdFName, g.httpUseSSL ? "" : " --nossl", p->zOptions ); in = fossil_fopen(zReplyFName, "w+b"); fflush(out); fflush(aux); if( g.fHttpTrace ){ fossil_print("%s\n", zCmd); } fossil_system(zCmd); if( in ){ while( (got = fread(zBuf, 1, sizeof(zBuf), in))>0 ){ if( sslConn ){ #ifdef FOSSIL_ENABLE_SSL ssl_write_server(sslConn, zBuf, got); #endif }else{ send(p->s, zBuf, got, 0); } } } end_request: if( out ) fclose(out); if( aux ) fclose(aux); if( in ) fclose(in); /* Initiate shutdown prior to closing the socket */ if( sslConn!=0 ){ #ifdef FOSSIL_ENABLE_SSL ssl_close_server(sslConn); #endif } if( shutdown(p->s,1)==0 ) shutdown(p->s,0); closesocket(p->s); /* Make multiple attempts to delete the temporary files. Sometimes AV ** software keeps the files open for a few seconds, preventing the file ** from being deleted on the first try. */ if( !g.fHttpTrace ){ for(i=1; i<=10 && file_delete(zRequestFName); i++){ Sleep(1000*i); } for(i=1; i<=10 && file_delete(zCmdFName); i++){ Sleep(1000*i); } for(i=1; i<=10 && file_delete(zReplyFName); i++){ Sleep(1000*i); } } fossil_free(p); } /* ** Process a single incoming SCGI request. */ static void win32_scgi_request(void *pAppData){ HttpRequest *p = (HttpRequest*)pAppData; FILE *in = 0, *out = 0; int amt, got, nHdr, i; int wanted = 0; char *zIp; char zRequestFName[MAX_PATH]; char zReplyFName[MAX_PATH]; char zCmd[2000]; /* Command-line to process the request */ char zHdr[4000]; /* The SCGI request header */ sqlite3_snprintf(MAX_PATH, zRequestFName, "%s_%06d_in.txt", zTempPrefix, p->id); sqlite3_snprintf(MAX_PATH, zReplyFName, "%s_%06d_out.txt", zTempPrefix, p->id); out = fossil_fopen(zRequestFName, "wb"); if( out==0 ) goto end_request; amt = 0; got = recv(p->s, zHdr, sizeof(zHdr), 0); if( got==SOCKET_ERROR ) goto end_request; amt = fwrite(zHdr, 1, got, out); nHdr = 0; for(i=0; zHdr[i]>='0' && zHdr[i]<='9'; i++){ nHdr = 10*nHdr + zHdr[i] - '0'; } wanted = nHdr + i + 1; if( strcmp(zHdr+i+1, "CONTENT_LENGTH")==0 ){ wanted += atoi(zHdr+i+15); } while( wanted>amt ){ got = recv(p->s, zHdr, wanted<sizeof(zHdr) ? wanted : sizeof(zHdr), 0); if( got<=0 ) break; fwrite(zHdr, 1, got, out); wanted += got; } assert( g.zRepositoryName && g.zRepositoryName[0] ); zIp = SocketAddr_toString(&p->addr); sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http --in \"%s\" --out \"%s\" --ipaddr %s \"%s\"" " --scgi --nossl%s", g.nameOfExe, zRequestFName, zReplyFName, zIp, g.zRepositoryName, p->zOptions ); fossil_free(zIp); in = fossil_fopen(zReplyFName, "w+b"); fflush(out); fossil_system(zCmd); 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); /* Initiate shutdown prior to closing the socket */ if( shutdown(p->s,1)==0 ) shutdown(p->s,0); closesocket(p->s); /* Make multiple attempts to delete the temporary files. Sometimes AV ** software keeps the files open for a few seconds, preventing the file ** from being deleted on the first try. */ for(i=1; i<=10 && file_delete(zRequestFName); i++){ Sleep(1000*i); } for(i=1; i<=10 && file_delete(zReplyFName); i++){ Sleep(1000*i); } fossil_free(p); } /* forward reference */ static void win32_http_service_running(DualSocket* pS); /* ** Start a listening socket and process incoming HTTP requests on ** that socket. */ void win32_http_server( int mnPort, int mxPort, /* Range of allowed TCP port numbers */ const char *zBrowser, /* Command to launch browser. (Or NULL) */ const char *zStopper, /* Stop server when this file is exists (Or NULL) */ const char *zBaseUrl, /* The --baseurl option, or NULL */ const char *zNotFound, /* The --notfound option, or NULL */ const char *zFileGlob, /* The --fileglob option, or NULL */ const char *zIpAddr, /* Bind to this IP address, if not NULL */ int flags /* One or more HTTP_SERVER_ flags */ ){ HANDLE hStoppedEvent; WSADATA wd; DualSocket ds; int idCnt = 0; int iPort = mnPort; Blob options; wchar_t zTmpPath[MAX_PATH]; char *zTempSubDirPath; const char *zTempSubDir = "fossil"; const char *zSkin; #if USE_SEE const char *zSavedKey = 0; size_t savedKeySize = 0; #endif blob_zero(&options); if( PB("HTTPS") ){ blob_appendf(&options, " --https"); } if( zBaseUrl ){ blob_appendf(&options, " --baseurl "); blob_append_escaped_arg(&options, zBaseUrl, 0); } if( zNotFound ){ blob_appendf(&options, " --notfound "); blob_append_escaped_arg(&options, zNotFound, 1); } if( g.zCkoutAlias ){ blob_appendf(&options, " --ckout-alias "); blob_append_escaped_arg(&options, g.zCkoutAlias, 0); } if( zFileGlob ){ blob_appendf(&options, " --files-urlenc %T", zFileGlob); } if( g.useLocalauth ){ blob_appendf(&options, " --localauth"); } if( g.thTrace ){ blob_appendf(&options, " --th-trace"); } if( flags & HTTP_SERVER_REPOLIST ){ blob_appendf(&options, " --repolist"); } if( g.zExtRoot && g.zExtRoot[0] ){ blob_appendf(&options, " --extroot"); blob_append_escaped_arg(&options, g.zExtRoot, 1); } zSkin = skin_in_use(); if( zSkin ){ blob_appendf(&options, " --skin %s", zSkin); } if( g.zMainMenuFile ){ blob_appendf(&options, " --mainmenu "); blob_append_escaped_arg(&options, g.zMainMenuFile, 1); } if( builtin_get_js_delivery_mode()!=0 /* JS_INLINE==0 may change? */ ){ blob_appendf(&options, " --jsmode "); blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0); } #if USE_SEE zSavedKey = db_get_saved_encryption_key(); savedKeySize = db_get_saved_encryption_key_size(); if( db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ){ blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(), zSavedKey, savedKeySize); } #endif if( WSAStartup(MAKEWORD(2,0), &wd) ){ fossil_panic("unable to initialize winsock"); } DualSocket_init(&ds); while( iPort<=mxPort ){ if( zIpAddr ){ if( DualSocket_listen(&ds, zIpAddr, iPort)==0 ){ iPort++; continue; } }else{ if( DualSocket_listen(&ds, (flags & HTTP_SERVER_LOCALHOST) ? "L" : "W", iPort )==0 ){ iPort++; continue; } } break; } if( iPort>mxPort ){ /* These exits are merely fatal because firewall settings can cause them. */ if( mnPort==mxPort ){ fossil_fatal("unable to open listening socket on port %d", mnPort); }else{ fossil_fatal("unable to open listening socket on any" " port in the range %d..%d", mnPort, mxPort); } } if( !GetTempPathW(MAX_PATH, zTmpPath) ){ fossil_panic("unable to get path to the temporary directory."); } /* Use a subdirectory for temp files (can then be excluded from virus scan) */ zTempSubDirPath = mprintf("%s%s\\",fossil_path_to_utf8(zTmpPath),zTempSubDir); if ( !file_mkdir(zTempSubDirPath, ExtFILE, 0) || file_isdir(zTempSubDirPath, ExtFILE)==1 ){ wcscpy(zTmpPath, fossil_utf8_to_path(zTempSubDirPath, 1)); } if( g.fHttpTrace ){ zTempPrefix = mprintf("httptrace"); }else{ zTempPrefix = mprintf("%sfossil_server_P%d", fossil_unicode_to_utf8(zTmpPath), iPort); } fossil_print("Temporary files: %s*\n", zTempPrefix); fossil_print("Listening for %s requests on TCP port %d\n", (flags&HTTP_SERVER_SCGI)!=0 ? "SCGI" : g.httpUseSSL ? "TLS-encrypted HTTPS" : "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"); /* Create an event used to signal when this server is exiting. */ hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); assert( hStoppedEvent!=NULL ); /* If there is a stopper file name, start the dedicated thread now. ** It will attempt to close the listener socket within 1 second of ** the stopper file being created. */ if( zStopper ){ HttpServer *pServer = fossil_malloc(sizeof(HttpServer)); memset(pServer, 0, sizeof(HttpServer)); DuplicateHandle(GetCurrentProcess(), hStoppedEvent, GetCurrentProcess(), &pServer->hStoppedEvent, 0, FALSE, DUPLICATE_SAME_ACCESS); assert( pServer->hStoppedEvent!=NULL ); pServer->zStopper = fossil_strdup(zStopper); pServer->listener = ds; file_delete(zStopper); _beginthread(win32_server_stopper, 0, (void*)pServer); } /* Set the service status to running and pass the listener socket to the ** service handling procedures. */ win32_http_service_running(&ds); for(;;){ DualSocket client; DualAddr client_addr; HttpRequest *pRequest; int wsaError; DualSocket_accept(&ds, &client, &client_addr); if( client.s4==INVALID_SOCKET && client.s6==INVALID_SOCKET ){ /* If the service control handler has closed the listener socket, ** cleanup and return, otherwise report a fatal error. */ wsaError = WSAGetLastError(); DualSocket_close(&ds); if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){ WSACleanup(); return; }else{ WSACleanup(); fossil_panic("error from accept()"); } } if( client.s4!=INVALID_SOCKET ){ pRequest = fossil_malloc(sizeof(HttpRequest)); pRequest->id = ++idCnt; pRequest->s = client.s4; memcpy(&pRequest->addr, &client_addr.a4, sizeof(client_addr.a4)); pRequest->flags = flags; pRequest->zOptions = blob_str(&options); if( flags & HTTP_SERVER_SCGI ){ _beginthread(win32_scgi_request, 0, (void*)pRequest); }else{ _beginthread(win32_http_request, 0, (void*)pRequest); } } if( client.s6!=INVALID_SOCKET ){ pRequest = fossil_malloc(sizeof(HttpRequest)); pRequest->id = ++idCnt; pRequest->s = client.s6; memcpy(&pRequest->addr, &client_addr.a6, sizeof(client_addr.a6)); pRequest->flags = flags; pRequest->zOptions = blob_str(&options); if( flags & HTTP_SERVER_SCGI ){ _beginthread(win32_scgi_request, 0, (void*)pRequest); }else{ _beginthread(win32_http_request, 0, (void*)pRequest); } } } DualSocket_close(&ds); WSACleanup(); SetEvent(hStoppedEvent); CloseHandle(hStoppedEvent); } /* ** The HttpService structure is used to pass information to the service main ** function and to the service control handler function. */ typedef struct HttpService HttpService; struct HttpService { int port; /* Port on which the http server should run */ const char *zBaseUrl; /* The --baseurl option, or NULL */ const char *zNotFound; /* The --notfound option, or NULL */ const char *zFileGlob; /* The --files option, or NULL */ int flags; /* One or more HTTP_SERVER_ flags */ int isRunningAsService; /* Are we running as a service ? */ const wchar_t *zServiceName;/* Name of the service */ DualSocket s; /* Sockets on which the http server listens */ }; /* ** Variables used for running as windows service. */ static HttpService hsData = {8080, NULL, NULL, NULL, 0, 0, NULL, {INVALID_SOCKET, INVALID_SOCKET}}; static SERVICE_STATUS ssStatus; static SERVICE_STATUS_HANDLE sshStatusHandle; /* ** Get message string of the last system error. Return a pointer to the ** message string. Call fossil_unicode_free() to deallocate any memory used ** to store the message string when done. */ static char *win32_get_last_errmsg(void){ DWORD nMsg; DWORD nErr = GetLastError(); LPWSTR tmp = NULL; char *zMsg = NULL; /* Try first to get the error text in English. */ nMsg = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, nErr, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPWSTR) &tmp, 0, NULL ); if( !nMsg ){ /* No english, get what the system has available. */ nMsg = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, nErr, 0, (LPWSTR) &tmp, 0, NULL ); } if( nMsg ){ zMsg = fossil_unicode_to_utf8(tmp); }else{ fossil_panic("unable to get system error message."); } if( tmp ){ LocalFree((HLOCAL) tmp); } return zMsg; } /* ** Report the current status of the service to the service control manager. ** Make sure that during service startup no control codes are accepted. */ static void win32_report_service_status( DWORD dwCurrentState, /* The current state of the service */ DWORD dwWin32ExitCode, /* The error code to report */ DWORD dwWaitHint /* The estimated time for a pending operation */ ){ if( dwCurrentState==SERVICE_START_PENDING ){ ssStatus.dwControlsAccepted = 0; }else{ ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; } ssStatus.dwCurrentState = dwCurrentState; ssStatus.dwWin32ExitCode = dwWin32ExitCode; ssStatus.dwWaitHint = dwWaitHint; if( (dwCurrentState==SERVICE_RUNNING) || (dwCurrentState==SERVICE_STOPPED) ){ ssStatus.dwCheckPoint = 0; }else{ ssStatus.dwCheckPoint++; } SetServiceStatus(sshStatusHandle, &ssStatus); return ; } /* ** Handle control codes sent from the service control manager. ** The control dispatcher in the main thread of the service process invokes ** this function whenever it receives a control request from the service ** control manager. */ static void WINAPI win32_http_service_ctrl( DWORD dwCtrlCode ){ switch( dwCtrlCode ){ case SERVICE_CONTROL_STOP: { DualSocket_close(&hsData.s); win32_report_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0); break; } default: { break; } } return; } /* ** This is the main entry point for the service. ** When the service control manager receives a request to start the service, ** it starts the service process (if it is not already running). The main ** thread of the service process calls the StartServiceCtrlDispatcher ** function with a pointer to an array of SERVICE_TABLE_ENTRY structures. ** Then the service control manager sends a start request to the service ** control dispatcher for this service process. The service control dispatcher ** creates a new thread to execute the ServiceMain function (this function) ** of the service being started. */ static void WINAPI win32_http_service_main( DWORD argc, /* Number of arguments in argv */ LPWSTR *argv /* Arguments passed */ ){ /* Update the service information. */ hsData.isRunningAsService = 1; if( argc>0 ){ hsData.zServiceName = argv[0]; } /* Register the service control handler function */ sshStatusHandle = RegisterServiceCtrlHandlerW(L"", win32_http_service_ctrl); if( !sshStatusHandle ){ win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0); return; } /* Set service specific data and report that the service is starting. */ ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ssStatus.dwServiceSpecificExitCode = 0; win32_report_service_status(SERVICE_START_PENDING, NO_ERROR, 3000); /* Execute the http server */ win32_http_server(hsData.port, hsData.port, NULL, NULL, hsData.zBaseUrl, hsData.zNotFound, hsData.zFileGlob, 0, hsData.flags); /* Service has stopped now. */ win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0); return; } /* ** When running as service, update the HttpService structure with the ** listener socket and update the service status. This procedure must be ** called from the http server when he is ready to accept connections. */ static void win32_http_service_running(DualSocket *pS){ if( hsData.isRunningAsService ){ hsData.s = *pS; win32_report_service_status(SERVICE_RUNNING, NO_ERROR, 0); } } /* ** Try to start the http server as a windows service. If we are running in ** an interactive console session, this routine fails and returns a non zero ** integer value. When running as service, this routine does not return until ** the service is stopped. In this case, the return value is zero. */ int win32_http_service( int nPort, /* TCP port number */ const char *zBaseUrl, /* The --baseurl option, or NULL */ const char *zNotFound, /* The --notfound option, or NULL */ const char *zFileGlob, /* The --files option, or NULL */ int flags /* One or more HTTP_SERVER_ flags */ ){ /* Define the service table. */ SERVICE_TABLE_ENTRYW ServiceTable[] = {{L"", (LPSERVICE_MAIN_FUNCTIONW)win32_http_service_main}, {NULL, NULL}}; /* Initialize the HttpService structure. */ hsData.port = nPort; hsData.zBaseUrl = zBaseUrl; hsData.zNotFound = zNotFound; hsData.zFileGlob = zFileGlob; hsData.flags = flags; if( GetStdHandle(STD_INPUT_HANDLE)!=NULL ){ return 1; } /* Try to start the control dispatcher thread for the service. */ if( !StartServiceCtrlDispatcherW(ServiceTable) ){ if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){ return 1; }else{ fossil_fatal("error from StartServiceCtrlDispatcher()"); } } return 0; } /* Duplicate #ifdef needed for mkindex */ #ifdef _WIN32 /* ** COMMAND: winsrv* ** ** Usage: %fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS? ** ** Where METHOD is one of: create delete show start stop. ** ** The winsrv command manages Fossil as a Windows service. This allows ** (for example) Fossil to be running in the background when no user ** is logged in. ** ** In the following description of the methods, "Fossil-DSCM" will be ** used as the default SERVICE-NAME: ** ** %fossil winsrv create ?SERVICE-NAME? ?OPTIONS? ** ** Creates a service. Available options include: ** ** -D|--display DISPLAY-NAME ** ** Sets the display name of the service. This name is shown ** by graphical interface programs. By default, the display name ** equals to the service name. ** ** -S|--start TYPE ** ** Sets the start type of the service. TYPE can be "manual", ** which means you need to start the service yourself with the ** 'fossil winsrv start' command or with the "net start" command ** from the operating system. If TYPE is set to "auto", the service ** will be started automatically by the system during startup. ** ** --username USERNAME ** ** Specifies the user account which will be used to run the ** service. The account needs the "Logon as a service" right ** enabled in its profile. Specify local accounts as follows: ** ".\\USERNAME". By default, the "LocalSystem" account will be ** used. ** ** -W|--password PASSWORD ** ** Password for the user account. ** ** The following options are more or less the same as for the "server" ** command and influence the behaviour of the http server: ** ** --baseurl URL ** ** Use URL as the base (useful for reverse proxies) ** ** -P|--port TCPPORT ** ** Specifies the TCP port (default port is 8080) on which the ** server should listen. ** ** -R|--repository REPO ** ** Specifies the name of the repository to be served. ** The repository option may be omitted if the working directory ** is within an open check-out. ** The REPOSITORY can be a directory (aka folder) that contains ** one or more repositories with names ending in ".fossil". ** In that case, the first element of the URL is used to select ** among the various repositories. ** ** --notfound URL ** ** If REPOSITORY is a directory that contains one or more ** repositories with names of the form "*.fossil" then the ** first element of the URL pathname selects among the various ** repositories. If the pathname does not select a valid ** repository and the --notfound option is available, ** then the server redirects (HTTP code 302) to the URL of ** --notfound. ** ** --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? ** ** Deletes a service. If the service is currently running, it will be ** stopped first and then deleted. ** ** ** %fossil winsrv show ?SERVICE-NAME? ** ** Shows how the service is configured and its current state. ** ** ** %fossil winsrv start ?SERVICE-NAME? ** ** Start the service. ** ** ** %fossil winsrv stop ?SERVICE-NAME? ** ** Stop the service. ** ** ** NOTE: This command is available on Windows operating systems only and ** requires administrative rights on the machine executed. ** */ void cmd_win32_service(void){ int n; const char *zMethod; const char *zSvcName = "Fossil-DSCM"; /* Default service name */ if( g.argc<3 ){ usage("create|delete|show|start|stop ..."); } zMethod = g.argv[2]; 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 *zAltBase = find_option("baseurl", 0, 1); const char *zDisplay = find_option("display", "D", 1); const char *zStart = find_option("start", "S", 1); const char *zUsername = find_option("username", 0, 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, ExtFILE)==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( zAltBase ) blob_appendf(&binPath, " --baseurl %s", zAltBase); 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()); } QueryServiceStatus(hSvc, &sstat); } while( sstat.dwCurrentState==SERVICE_STOP_PENDING || sstat.dwCurrentState==SERVICE_RUNNING ){ Sleep(100); fossil_print("."); QueryServiceStatus(hSvc, &sstat); } if( sstat.dwCurrentState==SERVICE_STOPPED ){ fossil_print("\nService '%s' stopped.\n", zSvcName); }else{ winhttp_fatal("delete", zSvcName, win32_get_last_errmsg()); } } 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" }; const char *zSvcType = ""; static const char *const zSvcStartTypes[] = { "Started by the system loader", "Started by the IoInitSystem function", "Started automatically by the service control manager", "Started manually", "Service cannot be started" }; const char *zSvcStartType = ""; static const char *const zSvcStates[] = { "Stopped", "Starting", "Stopping", "Running", "Continue pending", "Pause pending", "Paused" }; 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; case SERVICE_PAUSE_PENDING: zSvcState = zSvcStates[5]; break; case SERVICE_PAUSED: zSvcState = zSvcStates[6]; break; } /* Print service information to terminal */ fossil_print("Service name .......: %s\n", zSvcName); fossil_print("Display name .......: %s\n", fossil_unicode_to_utf8(pSvcConfig->lpDisplayName)); fossil_print("Service description : %s\n", fossil_unicode_to_utf8(pSvcDescr->lpDescription)); fossil_print("Service type .......: %s.\n", zSvcType); fossil_print("Service start type .: %s.\n", zSvcStartType); fossil_print("Binary path name ...: %s\n", fossil_unicode_to_utf8(pSvcConfig->lpBinaryPathName)); fossil_print("Service username ...: %s\n", fossil_unicode_to_utf8(pSvcConfig->lpServiceStartName)); fossil_print("Current state ......: %s.\n", zSvcState); /* Cleanup */ fossil_free(pSvcConfig); fossil_free(pSvcDescr); 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()); } QueryServiceStatus(hSvc, &sstat); } while( sstat.dwCurrentState==SERVICE_START_PENDING || sstat.dwCurrentState==SERVICE_STOPPED ){ Sleep(100); fossil_print("."); QueryServiceStatus(hSvc, &sstat); } if( sstat.dwCurrentState==SERVICE_RUNNING ){ fossil_print("\nService '%s' started.\n", zSvcName); }else{ winhttp_fatal("start", zSvcName, win32_get_last_errmsg()); } }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()); } QueryServiceStatus(hSvc, &sstat); } while( sstat.dwCurrentState==SERVICE_STOP_PENDING || sstat.dwCurrentState==SERVICE_RUNNING ){ Sleep(100); fossil_print("."); QueryServiceStatus(hSvc, &sstat); } if( sstat.dwCurrentState==SERVICE_STOPPED ){ fossil_print("\nService '%s' stopped.\n", zSvcName); }else{ winhttp_fatal("stop", zSvcName, win32_get_last_errmsg()); } }else{ fossil_print("Service '%s' is already stopped.\n", zSvcName); } CloseServiceHandle(hSvc); CloseServiceHandle(hScm); }else { fossil_fatal("METHOD should be one of:" " create delete show start stop"); } return; } #endif /* _WIN32 -- dupe needed for mkindex */ #endif /* _WIN32 -- This code is for win32 only */ |
Changes to src/xfer.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2007 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".) | | > > > > > > > > | > | > > > > > > > | | | > > | > > > > > > > > > > > > > > > > | | | | > > > > > | > > | > > | | | < > | > > > > > > > | > > > > > > > > > > > > > > > | | | > > > | > > | > > | < | | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < | < < | > > | > | > < | > | | > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > > > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > > | | > > > < | < < | | | | > > | | | > | > > > > > | | | | < < < < < < < < | 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 | /* ** Copyright (c) 2007 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 file transfer protocol. */ #include "config.h" #include "xfer.h" #include <time.h> /* ** Maximum number of HTTP redirects that any http_exchange() call will ** follow before throwing a fatal error. Most browsers use a limit of 20. */ #define MAX_REDIRECTS 20 /* ** This structure holds information about the current state of either ** a client or a server that is participating in xfer. */ typedef struct Xfer Xfer; struct Xfer { Blob *pIn; /* Input text from the other side */ Blob *pOut; /* Compose our reply here */ Blob line; /* The current line of input */ Blob aToken[6]; /* Tokenized version of line */ Blob err; /* Error message text */ int nToken; /* Number of tokens in line */ int nIGotSent; /* Number of "igot" cards sent */ int nPrivIGot; /* Number of private "igot" cards */ int nGimmeSent; /* Number of gimme cards sent */ int nFileSent; /* Number of files sent */ int nDeltaSent; /* Number of deltas sent */ int nFileRcvd; /* Number of files received */ int nDeltaRcvd; /* Number of deltas received */ int nDanglingFile; /* Number of dangling deltas received */ int mxSend; /* Stop sending "file" when pOut reaches this size */ int resync; /* Send igot cards for all holdings */ u8 syncPrivate; /* True to enable syncing private content */ u8 nextIsPrivate; /* If true, next "file" received is a private */ u32 remoteVersion; /* Version of fossil running on the other side */ u32 remoteDate; /* Date for specific client software edition */ u32 remoteTime; /* Time of date correspoding on remoteDate */ time_t maxTime; /* Time when this transfer should be finished */ }; /* ** The input blob contains an artifact. Convert it into a record ID. ** Create a phantom record if no prior record exists and ** phantomize is true. ** ** Compare to uuid_to_rid(). This routine takes a blob argument ** and does less error checking. */ static int rid_from_uuid(Blob *pUuid, int phantomize, int isPrivate){ static Stmt q; int rid; db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid"); db_bind_str(&q, ":uuid", pUuid); if( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); }else{ rid = 0; } db_reset(&q); if( rid==0 && phantomize ){ rid = content_new(blob_str(pUuid), isPrivate); } return rid; } /* ** Remember that the other side of the connection already has a copy ** of the file rid. */ static void remote_has(int rid){ if( rid ){ static Stmt q; db_static_prepare(&q, "INSERT OR IGNORE INTO onremote VALUES(:r)"); db_bind_int(&q, ":r", rid); db_step(&q); db_reset(&q); } } /* ** Remember that the other side of the connection lacks a copy of ** the artifact with the given hash. */ static void remote_unk(Blob *pHash){ static Stmt q; db_static_prepare(&q, "INSERT OR IGNORE INTO unk VALUES(:h)"); db_bind_text(&q, ":h", blob_str(pHash)); 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 HASH SIZE \n CONTENT ** file HASH 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; int isPriv; Blob *pUuid; isPriv = pXfer->nextIsPrivate; pXfer->nextIsPrivate = 0; if( pXfer->nToken<3 || pXfer->nToken>4 || !blob_is_hname(&pXfer->aToken[1]) || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n) || n<0 || (pXfer->nToken==4 && !blob_is_hname(&pXfer->aToken[2])) ){ blob_appendf(&pXfer->err, "malformed file line"); return; } blob_zero(&content); blob_extract(pXfer->pIn, n, &content); pUuid = &pXfer->aToken[1]; if( !cloneFlag && uuid_is_shunned(blob_str(pUuid)) ){ /* Ignore files that have been shunned */ blob_reset(&content); return; } if( isPriv && !g.perm.Private ){ /* Do not accept private files if not authorized */ blob_reset(&content); return; } if( cloneFlag ){ if( pXfer->nToken==4 ){ srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv); pXfer->nDeltaRcvd++; }else{ srcid = 0; pXfer->nFileRcvd++; } rid = content_put_ex(&content, blob_str(pUuid), srcid, 0, isPriv); Th_AppendToList(pzUuidList, pnUuidList, blob_str(pUuid), blob_size(pUuid)); 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(pUuid), srcid, 0, isPriv); Th_AppendToList(pzUuidList, pnUuidList, blob_str(pUuid), blob_size(pUuid)); 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++; } if( hname_verify_hash(&content, blob_buffer(pUuid), blob_size(pUuid))==0 ){ blob_appendf(&pXfer->err, "wrong hash on received artifact: %b", pUuid); } rid = content_put_ex(&content, blob_str(pUuid), 0, 0, isPriv); Th_AppendToList(pzUuidList, pnUuidList, blob_str(pUuid), blob_size(pUuid)); 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 HASH USIZE CSIZE \n CONTENT ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT ** ** The content is CSIZE bytes immediately following the newline. ** If DELTASRC exists, then the CONTENT is a delta against the ** content of DELTASRC. ** ** The original size of the HASH artifact is USIZE. ** ** 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_hname(&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_hname(&pXfer->aToken[2])) ){ blob_appendf(&pXfer->err, "malformed cfile line"); return; } if( isPriv && !g.perm.Private ){ /* Do not accept private files if not authorized */ return; } blob_zero(&content); blob_extract(pXfer->pIn, szC, &content); if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ /* Ignore files that have been shunned */ blob_reset(&content); return; } if( pXfer->nToken==5 ){ srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv); 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); } /* ** The aToken[0..nToken-1] blob array is a parse of a "uvfile" line ** message. This routine finishes parsing that message and adds the ** unversioned file to the "unversioned" table. ** ** The file line is in one of the following two forms: ** ** uvfile NAME MTIME HASH SIZE FLAGS ** uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT ** ** If the 0x0001 bit of FLAGS is set, that means the file has been ** deleted, SIZE is zero, the HASH is "-", and the "\n CONTENT" is omitted. ** ** SIZE is the number of bytes of CONTENT. The CONTENT is uncompressed. ** HASH is the artifact hash of CONTENT. ** ** If the 0x0004 bit of FLAGS is set, that means the CONTENT is omitted. ** The sender might have omitted the content because it is too big to ** transmit, or because it is unchanged and this record exists purely ** to update the MTIME. */ static void xfer_accept_unversioned_file(Xfer *pXfer, int isWriter){ sqlite3_int64 mtime; /* The MTIME */ Blob *pHash; /* The HASH value */ int sz; /* The SIZE */ int flags; /* The FLAGS */ Blob content; /* The CONTENT */ Blob x; /* Compressed content */ Stmt q; /* SQL statements for comparison and insert */ int isDelete; /* HASH is "-" indicating this is a delete */ int nullContent; /* True of CONTENT is NULL */ int iStatus; /* Result from unversioned_status() */ pHash = &pXfer->aToken[3]; if( pXfer->nToken==5 || !blob_is_filename(&pXfer->aToken[1]) || !blob_is_int64(&pXfer->aToken[2], &mtime) || (!blob_eq(pHash,"-") && !blob_is_hname(pHash)) || !blob_is_int(&pXfer->aToken[4], &sz) || !blob_is_int(&pXfer->aToken[5], &flags) ){ blob_appendf(&pXfer->err, "malformed uvfile line"); return; } blob_init(&content, 0, 0); blob_init(&x, 0, 0); if( sz>0 && (flags & 0x0005)==0 ){ blob_extract(pXfer->pIn, sz, &content); nullContent = 0; if( hname_verify_hash(&content, blob_buffer(pHash), blob_size(pHash))==0 ){ blob_appendf(&pXfer->err, "in uvfile line, HASH does not match CONTENT"); goto end_accept_unversioned_file; } }else{ nullContent = 1; } /* The isWriter flag must be true in order to land the new file */ if( !isWriter ){ blob_appendf(&pXfer->err,"Write permissions for unversioned files missing"); goto end_accept_unversioned_file; } /* Make sure we have a valid g.rcvid marker */ content_rcvid_init(0); /* Check to see if current content really should be overwritten. Ideally, ** a uvfile card should never have been sent unless the overwrite should ** occur. But do not trust the sender. Double-check. */ iStatus = unversioned_status(blob_str(&pXfer->aToken[1]), mtime, blob_str(pHash)); if( iStatus>=3 ) goto end_accept_unversioned_file; /* Store the content */ isDelete = blob_eq(pHash, "-"); if( isDelete ){ db_prepare(&q, "UPDATE unversioned" " SET rcvid=:rcvid, mtime=:mtime, hash=NULL," " sz=0, encoding=0, content=NULL" " WHERE name=:name" ); db_bind_int(&q, ":rcvid", g.rcvid); }else if( iStatus==2 ){ db_prepare(&q, "UPDATE unversioned SET mtime=:mtime WHERE name=:name"); }else{ db_prepare(&q, "REPLACE INTO unversioned(name,rcvid,mtime,hash,sz,encoding,content)" " VALUES(:name,:rcvid,:mtime,:hash,:sz,:encoding,:content)" ); db_bind_int(&q, ":rcvid", g.rcvid); db_bind_text(&q, ":hash", blob_str(pHash)); db_bind_int(&q, ":sz", blob_size(&content)); if( !nullContent ){ blob_compress(&content, &x); if( blob_size(&x) < 0.8*blob_size(&content) ){ db_bind_blob(&q, ":content", &x); db_bind_int(&q, ":encoding", 1); }else{ db_bind_blob(&q, ":content", &content); db_bind_int(&q, ":encoding", 0); } }else{ db_bind_int(&q, ":encoding", 0); } } db_bind_text(&q, ":name", blob_str(&pXfer->aToken[1])); db_bind_int64(&q, ":mtime", mtime); db_step(&q); db_finalize(&q); db_unset("uv-hash", 0); end_accept_unversioned_file: blob_reset(&x); blob_reset(&content); } /* ** Try to send a file as a delta against its parent. ** 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_parent( Xfer *pXfer, /* The transfer context */ int rid, /* record id of the file to send */ int isPrivate, /* True if rid is a private artifact */ Blob *pContent, /* The content of the file to send */ Blob *pUuid /* The HASH 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); size = blob_size(&delta); if( size>=(int)blob_size(pContent)-50 ){ size = 0; }else if( uuid_is_shunned(zUuid) ){ size = 0; }else{ if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1); blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size); blob_append(pXfer->pOut, blob_buffer(&delta), size); } blob_reset(&delta); 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( Xfer *pXfer, /* The transfer context */ int rid, /* record id of the file to send */ int isPrivate, /* True if rid is a private artifact */ Blob *pUuid /* The HASH of the file to send */ ){ Blob src, delta; int size = 0; int srcId; srcId = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", rid); if( srcId>0 && (pXfer->syncPrivate || !content_is_private(srcId)) ){ blob_zero(&src); db_blob(&src, "SELECT uuid FROM blob WHERE rid=%d", srcId); if( uuid_is_shunned(blob_str(&src)) ){ blob_reset(&src); return 0; } blob_zero(&delta); db_blob(&delta, "SELECT content FROM blob WHERE rid=%d", rid); blob_uncompress(&delta, &delta); if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1); blob_appendf(pXfer->pOut, "file %b %b %d\n", pUuid, &src, blob_size(&delta)); blob_append(pXfer->pOut, blob_buffer(&delta), blob_size(&delta)); size = blob_size(&delta); blob_reset(&delta); blob_reset(&src); }else{ size = 0; } return size; } /* ** Push an error message to alert the older client that the repository ** has SHA3 content and cannot be synced or cloned. */ static void xfer_cannot_send_sha3_error(Xfer *pXfer){ blob_appendf(pXfer->pOut, "error Fossil\\sversion\\s2.0\\sor\\slater\\srequired.\n" ); } /* ** Send the file identified by rid. ** ** The pUuid can be NULL in which case the correct hash is computed ** from the rid. ** ** Try to send the file as a native delta if nativeDelta is true, or ** as a parent delta if nativeDelta is false. ** ** It should never be the case that rid is a private artifact. But ** as a precaution, this routine does check on rid and if it is private ** this routine becomes a no-op. */ static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){ Blob content, uuid; int size = 0; int isPriv = content_is_private(rid); if( isPriv && pXfer->syncPrivate==0 ){ if( pXfer->remoteDate>=20200413 && pUuid && blob_size(pUuid)>0 ){ /* If the artifact is private and we are not doing a private sync, ** at least tell the other side that the artifact exists and is ** known to be private. But only do this for newer clients since ** older ones will throw an error if they get a private igot card ** and private syncing is disallowed */ blob_appendf(pXfer->pOut, "igot %b 1\n", pUuid); pXfer->nIGotSent++; pXfer->nPrivIGot++; } return; } if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){ return; } blob_zero(&uuid); db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid); if( blob_size(&uuid)==0 ){ return; } if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->remoteVersion<20000 ){ xfer_cannot_send_sha3_error(pXfer); return; } if( pUuid ){ if( blob_compare(pUuid, &uuid)!=0 ){ blob_reset(&uuid); return; } }else{ pUuid = &uuid; } if( uuid_is_shunned(blob_str(pUuid)) ){ blob_reset(&uuid); return; } if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) || pXfer->mxSend<=(int)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 ){ pXfer->nDeltaSent++; } } if( size==0 ){ content_get(rid, &content); if( !nativeDelta && blob_size(&content)>100 ){ size = send_delta_parent(pXfer, rid, isPriv, &content, pUuid); } if( size==0 ){ int size = blob_size(&content); if( isPriv ) blob_append(pXfer->pOut, "private\n", -1); blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size); blob_append(pXfer->pOut, blob_buffer(&content), size); pXfer->nFileSent++; }else{ pXfer->nDeltaSent++; } 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; int szC; int rc; int isPrivate; int srcIsPrivate; static Stmt q1; Blob fullContent; isPrivate = content_is_private(rid); if( isPrivate && pXfer->syncPrivate==0 ) return; db_static_prepare(&q1, "SELECT uuid, size, content, delta.srcid IN private," " (SELECT uuid FROM blob WHERE rid=delta.srcid)" " FROM blob LEFT JOIN delta ON (blob.rid=delta.rid)" " WHERE blob.rid=:rid" " AND blob.size>=0" " AND NOT EXISTS(SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)" ); db_bind_int(&q1, ":rid", rid); rc = db_step(&q1); if( rc==SQLITE_ROW ){ zUuid = db_column_text(&q1, 0); szU = db_column_int(&q1, 1); szC = db_column_bytes(&q1, 2); zContent = db_column_raw(&q1, 2); srcIsPrivate = db_column_int(&q1, 3); zDelta = db_column_text(&q1, 4); if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1); if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){ xfer_cannot_send_sha3_error(pXfer); db_reset(&q1); return; } blob_appendf(pXfer->pOut, "cfile %s ", zUuid); if( !isPrivate && srcIsPrivate ){ content_get(rid, &fullContent); szU = blob_size(&fullContent); blob_compress(&fullContent, &fullContent); szC = blob_size(&fullContent); zContent = blob_buffer(&fullContent); zDelta = 0; } if( zDelta ){ blob_appendf(pXfer->pOut, "%s ", zDelta); 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 the unversioned file identified by zName by generating the ** appropriate "uvfile" card. ** ** uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT ** ** If the noContent flag is set, omit the CONTENT and set the 0x0004 ** flag in FLAGS. */ static void send_unversioned_file( Xfer *pXfer, /* Transfer context */ const char *zName, /* Name of unversioned file to be sent */ int noContent /* True to omit the content */ ){ Stmt q1; if( (int)blob_size(pXfer->pOut)>=pXfer->mxSend ) noContent = 1; if( noContent ){ db_prepare(&q1, "SELECT mtime, hash, encoding, sz FROM unversioned WHERE name=%Q", zName ); }else{ db_prepare(&q1, "SELECT mtime, hash, encoding, sz, content FROM unversioned" " WHERE name=%Q", zName ); } if( db_step(&q1)==SQLITE_ROW ){ sqlite3_int64 mtime = db_column_int64(&q1, 0); const char *zHash = db_column_text(&q1, 1); if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){ xfer_cannot_send_sha3_error(pXfer); db_reset(&q1); return; } if( (int)blob_size(pXfer->pOut)>=pXfer->mxSend ){ /* If we have already reached the send size limit, send a (short) ** uvigot card rather than a uvfile card. This only happens on the ** server side. The uvigot card will provoke the client to resend ** another uvgimme on the next cycle. */ blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n", zName, mtime, zHash, db_column_int(&q1,3)); }else{ blob_appendf(pXfer->pOut, "uvfile %s %lld", zName, mtime); if( zHash==0 ){ blob_append(pXfer->pOut, " - 0 1\n", -1); }else if( noContent ){ blob_appendf(pXfer->pOut, " %s %d 4\n", zHash, db_column_int(&q1,3)); }else{ Blob content; blob_init(&content, 0, 0); db_column_blob(&q1, 4, &content); if( db_column_int(&q1, 2) ){ blob_uncompress(&content, &content); } blob_appendf(pXfer->pOut, " %s %d 0\n", zHash, blob_size(&content)); blob_append(pXfer->pOut, blob_buffer(&content), blob_size(&content)); blob_reset(&content); } } } db_finalize(&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 unk WHERE unk.uuid=blob.uuid)" " AND 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); pXfer->nGimmeSent++; } db_finalize(&q); } /* ** Compute an hash on the tail of pMsg. Verify that it matches the ** the hash given in pHash. Return non-zero for an error and 0 on success. ** ** The type of hash computed (SHA1, SHA3-256) is determined by ** the length of the input hash in pHash. */ static int check_tail_hash(Blob *pHash, Blob *pMsg){ Blob tail; int rc; blob_tail(pMsg, &tail); rc = hname_verify_hash(&tail, blob_buffer(pHash), blob_size(pHash)); blob_reset(&tail); return rc==HNAME_ERROR; } /* ** 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 sha1_shared_secret() encoding of the users password. ** ** SIGNATURE = sha1_sum( NONCE + sha1_shared_secret(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. */ static 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", zLogin ); if( db_step(&q)==SQLITE_ROW ){ int szPw; Blob pw, combined, hash; blob_zero(&pw); db_ephemeral_blob(&q, 0, &pw); szPw = blob_size(&pw); blob_zero(&combined); blob_copy(&combined, pNonce); blob_append(&combined, blob_buffer(&pw), szPw); sha1sum_blob(&combined, &hash); assert( blob_size(&hash)==40 ); rc = blob_constant_time_cmp(&hash, pSig); blob_reset(&hash); blob_reset(&combined); if( rc!=0 && szPw!=40 ){ /* If this server stores cleartext passwords and the password did not ** match, then perhaps the client is sending SHA1 passwords. Try ** again with the SHA1 password. */ const char *zPw = db_column_text(&q, 0); char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0); blob_zero(&combined); blob_copy(&combined, pNonce); blob_append(&combined, zSecret, -1); free(zSecret); sha1sum_blob(&combined, &hash); rc = blob_constant_time_cmp(&hash, pSig); blob_reset(&hash); blob_reset(&combined); } if( rc==0 ){ const char *zCap; zCap = db_column_text(&q, 1); login_set_capabilities(zCap, 0); g.userUid = db_column_int(&q, 2); g.zLogin = mprintf("%b", pLogin); g.zNonce = mprintf("%b", pNonce); } } db_finalize(&q); return rc; } /* ** Send the content of all files in the unsent table. ** ** This is really just an optimization. If you clear the |
︙ | ︙ | |||
463 464 465 466 467 468 469 | /* ** Check to see if the number of unclustered entries is greater than ** 100 and if it is, form a new cluster. Unclustered phantoms do not ** count toward the 100 total. And phantoms are never added to a new ** cluster. */ | | > > > > > | | < < | > | | | | | | | | > > > > > > > > > > > | > | > > > > > > > | | | < | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | | | | > | > > > | > > | | > > > > > > > > > > > | > > > | | > > | > | > > > > > > > | > > > > > > > | > > > > > > | > > > > > > | > > > > | | | > > > > | | > > > > > > > > > > > > | < < > > > > > > > > | > > > > | | > > > > > > > > > > > > > > > > > > | > > | | > | > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | > > > > > > > > > > > | | > | | > > > > | > > > > > > > > > > > > > > | > > > > > > > > > | | | < | | | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > | | | | | | > | > > > | | | | | < < < < < < < < < < < < < < < < < < < < < < < | < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > | > > > > > > | | | > > | > | | | < > | | > | > | | > > > | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < | | > > | < > | > > > > > > > > > > > > > > > > > > > > > > | < > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | | < > > | | > > > > > | > | > > < > > | > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < | > > | > | | | > < < < > > > > > > > > > > > > > | > < > > | > | > > > > > > > > > > > > > > > > > > | > > > > > > > | > | > > > > | > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | | | > > > > > > > | > | > > > > > > > > > | | > | > > > > > > > > > > > > > > > > > > > | | > | | | > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | > | | | | > > > > | | > > | > > > | > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | < < < | | | > > > | | < < < < < < < < < < < < < < < < < < < < < < < < < | > | | > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | < | | < | < > | < > | > > | < | > < > | > > | > | > > < < < | | > > > > | | < > > > > > > > > > > > > > > > > > > | > > | < < < < | > | > > | < < > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > | | > | | > | | > > > > > > > > > > > > > > > | 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 | /* ** Check to see if the number of unclustered entries is greater than ** 100 and if it is, form a new cluster. Unclustered phantoms do not ** count toward the 100 total. And phantoms are never added to a new ** cluster. */ void create_cluster(void){ Blob cluster, cksum; Blob deleteWhere; Stmt q; int nUncl; int nRow = 0; int rid; #if 0 /* We should not ever get any private artifacts in the unclustered table. ** But if we do (because of a bug) now is a good time to delete them. */ db_multi_exec( "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" ); #endif nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" " WHERE NOT EXISTS(SELECT 1 FROM phantom" " WHERE rid=unclustered.rid)"); if( nUncl>=100 ){ blob_zero(&cluster); blob_zero(&deleteWhere); db_prepare(&q, "SELECT uuid FROM unclustered, blob" " WHERE NOT EXISTS(SELECT 1 FROM phantom" " WHERE rid=unclustered.rid)" " AND unclustered.rid=blob.rid" " AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" " ORDER BY 1"); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&cluster, "M %s\n", db_column_text(&q, 0)); nRow++; if( nRow>=800 && nUncl>nRow+100 ){ md5sum_blob(&cluster, &cksum); blob_appendf(&cluster, "Z %b\n", &cksum); blob_reset(&cksum); rid = content_put(&cluster); manifest_crosslink(rid, &cluster, MC_NONE); 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); rid = content_put(&cluster); manifest_crosslink(rid, &cluster, MC_NONE); blob_reset(&cluster); } } } /* ** Send igot messages for every private artifact */ static int send_private(Xfer *pXfer){ int cnt = 0; Stmt q; if( pXfer->syncPrivate ){ db_prepare(&q, "SELECT uuid FROM private JOIN blob USING(rid)"); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(pXfer->pOut, "igot %s 1\n", db_column_text(&q,0)); cnt++; } db_finalize(&q); } return cnt; } /* ** Send an igot message for every entry in unclustered table. ** Return the number of cards sent. ** ** Except: ** * Do not send igot cards for shunned artifacts ** * Do not send igot cards for phantoms ** * Do not send igot cards for private artifacts ** * Do not send igot cards for any artifact that is in the ** ONREMOTE table, if that table exists. ** ** If the pXfer->resync flag is set, that means we are doing a "--verily" ** sync and all artifacts that don't meet the restrictions above should ** be sent. */ static int send_unclustered(Xfer *pXfer){ Stmt q; int cnt = 0; const char *zExtra; if( db_table_exists("temp","onremote") ){ zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)"; }else{ zExtra = ""; } 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)%s" " AND blob.rid<=%d" " ORDER BY blob.rid DESC", zExtra /*safe-for-%s*/, pXfer->resync ); }else{ db_prepare(&q, "SELECT uuid FROM unclustered JOIN blob USING(rid) /*scan*/" " 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)%s", zExtra /*safe-for-%s*/ ); } while( db_step(&q)==SQLITE_ROW ){ blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0)); cnt++; if( pXfer->resync && pXfer->mxSend<(int)blob_size(pXfer->pOut) ){ pXfer->resync = db_column_int(&q, 1)-1; } } db_finalize(&q); if( cnt==0 ) pXfer->resync = 0; return cnt; } /* ** 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)); } db_finalize(&q); } /* ** pXfer is a "pragma uv-hash HASH" card. ** ** If HASH is different from the unversioned content hash on this server, ** then send a bunch of uvigot cards, one for each entry unversioned file ** on this server. */ static void send_unversioned_catalog(Xfer *pXfer){ Stmt uvq; unversioned_schema(); db_prepare(&uvq, "SELECT name, mtime, hash, sz FROM unversioned" ); while( db_step(&uvq)==SQLITE_ROW ){ const char *zName = db_column_text(&uvq,0); sqlite3_int64 mtime = db_column_int64(&uvq,1); const char *zHash = db_column_text(&uvq,2); int sz = db_column_int(&uvq,3); if( zHash==0 ){ sz = 0; zHash = "-"; } blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n", zName, mtime, zHash, sz); } db_finalize(&uvq); } /* ** Called when there is an attempt to transfer private content to and ** 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); } /* ** This routine makes a "syncwith:URL" entry in the CONFIG table to ** indicate that a sync is occuring with zUrl. ** ** Add a "syncfrom:URL" entry instead of "syncwith:URL" if bSyncFrom is true. */ static void xfer_syncwith(const char *zUrl, int bSyncFrom){ UrlData x; memset(&x, 0, sizeof(x)); url_parse_local(zUrl, URL_OMIT_USER, &x); if( x.protocol && strncmp(x.protocol,"http",4)==0 && x.name && sqlite3_strlike("%localhost%", x.name, 0)!=0 ){ db_unprotect(PROTECT_CONFIG); db_multi_exec("REPLACE INTO config(name,value,mtime)" "VALUES('sync%q:%q','{}',now())", bSyncFrom ? "from" : "with", x.canonical); db_protect_pop(); } url_unparse(&x); } /* ** If this variable is set, disable login checks. Used for debugging ** only. */ static int disableLogin = 0; /* ** The CGI/HTTP preprocessor always redirects requests with a content-type ** of application/x-fossil or application/x-fossil-debug to this page, ** regardless of what path was specified in the HTTP header. This allows ** clone clients to specify a URL that omits default pathnames, such ** as "http://fossil-scm.org/" instead of "http://fossil-scm.org/index.cgi". ** ** WEBPAGE: xfer raw-content loadavg-exempt ** ** This is the transfer handler on the server side. The transfer ** message has been uncompressed and placed in the g.cgiIn blob. ** Process this message and form an appropriate reply. */ void page_xfer(void){ int isPull = 0; int isPush = 0; int nErr = 0; Xfer xfer; int deltaFlag = 0; int isClone = 0; int nGimme = 0; int size; char *zNow; int rc; const char *zScript = 0; char *zUuidList = 0; int nUuidList = 0; char **pzUuidList = 0; int *pnUuidList = 0; int uvCatalogSent = 0; int bSendLinks = 0; if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ fossil_redirect_home(); } g.zLogin = "anonymous"; login_set_anon_nobody_capabilities(); login_check_credentials(); cgi_check_for_malice(); memset(&xfer, 0, sizeof(xfer)); blobarray_zero(xfer.aToken, count(xfer.aToken)); cgi_set_content_type(g.zContentType); cgi_reset_content(); if( db_schema_is_outofdate() ){ @ error database\sschema\sis\sout-of-date\son\sthe\sserver. return; } blob_zero(&xfer.err); xfer.pIn = &g.cgiIn; xfer.pOut = cgi_output_blob(); xfer.mxSend = db_get_int("max-download", 5000000); xfer.maxTime = db_get_int("max-download-time", 30); if( xfer.maxTime<1 ) xfer.maxTime = 1; xfer.maxTime += time(NULL); g.xferPanic = 1; db_begin_write(); db_multi_exec( "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" "CREATE TEMP TABLE unk(uuid TEXT PRIMARY KEY) WITHOUT ROWID;" ); 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 HASH SIZE \n CONTENT ** file HASH DELTASRC SIZE \n CONTENT ** ** Server accepts 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 HASH USIZE CSIZE \n CONTENT ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT ** ** Server accepts a compressed 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 /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT ** ** Server accepts an unversioned file from the client. */ if( blob_eq(&xfer.aToken[0], "uvfile") ){ xfer_accept_unversioned_file(&xfer, g.perm.WrUnver); if( blob_size(&xfer.err) ){ cgi_reset_content(); @ error %T(blob_str(&xfer.err)) fossil_print("%%%%%%%% xfer.err: '%s'\n", blob_str(&xfer.err)); nErr++; break; } }else /* gimme HASH ** ** Client is requesting a file from the server. Send it. */ if( blob_eq(&xfer.aToken[0], "gimme") && xfer.nToken==2 && blob_is_hname(&xfer.aToken[1]) ){ nGimme++; remote_unk(&xfer.aToken[1]); if( isPull ){ int rid = rid_from_uuid(&xfer.aToken[1], 0, 0); if( rid ){ send_file(&xfer, rid, &xfer.aToken[1], deltaFlag); } } }else /* uvgimme NAME ** ** Client is requesting an unversioned file from the server. Send it. */ if( blob_eq(&xfer.aToken[0], "uvgimme") && xfer.nToken==2 && blob_is_filename(&xfer.aToken[1]) ){ send_unversioned_file(&xfer, blob_str(&xfer.aToken[1]), 0); }else /* igot HASH ?ISPRIVATE? ** ** Client announces that it has a particular file. If the ISPRIVATE ** argument exists and is "1", then the file is a private file. */ if( xfer.nToken>=2 && blob_eq(&xfer.aToken[0], "igot") && blob_is_hname(&xfer.aToken[1]) ){ if( isPush ){ int rid = 0; int isPriv = 0; if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){ /* Client says the artifact is public */ rid = rid_from_uuid(&xfer.aToken[1], 1, 0); }else if( g.perm.Private ){ /* Client says the artifact is private and the client has ** permission to push private content. Create a new phantom ** artifact that is marked private. */ rid = rid_from_uuid(&xfer.aToken[1], 1, 1); isPriv = 1; }else{ /* Client says the artifact is private and the client is unable ** or unwilling to send us the artifact. If we already hold the ** artifact here on the server as a phantom, make sure that ** phantom is marked as private so that we don't keep asking about ** it in subsequent sync requests. */ rid = rid_from_uuid(&xfer.aToken[1], 0, 1); isPriv = 1; } if( rid ){ remote_has(rid); if( isPriv ){ content_make_private(rid); }else{ content_make_public(rid); } } } }else /* pull SERVERCODE PROJECTCODE ** push SERVERCODE PROJECTCODE ** ** The client wants either send or receive. The server should ** verify that the project code matches. The server code is ignored. */ if( xfer.nToken==3 && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push")) && blob_is_hname(&xfer.aToken[2]) ){ const char *zPCode; zPCode = db_get("project-code", 0); if( zPCode==0 ){ fossil_fatal("missing project code"); } if( !blob_eq_str(&xfer.aToken[2], zPCode, -1) ){ cgi_reset_content(); @ error wrong\sproject nErr++; break; } login_check_credentials(); if( blob_eq(&xfer.aToken[0], "pull") ){ if( !g.perm.Read ){ cgi_reset_content(); @ error not\sauthorized\sto\sread nErr++; break; } isPull = 1; }else{ if( !g.perm.Write ){ if( !isPull ){ cgi_reset_content(); @ error not\sauthorized\sto\swrite nErr++; }else{ @ message pull\sonly\s-\snot\sauthorized\sto\spush } }else{ isPush = 1; } } }else /* clone ?PROTOCOL-VERSION? ?SEQUENCE-NUMBER? ** ** The client knows nothing. Tell all. */ if( blob_eq(&xfer.aToken[0], "clone") ){ int iVers; login_check_credentials(); if( !g.perm.Clone ){ cgi_reset_content(); @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) @ error not\sauthorized\sto\sclone nErr++; break; } if( db_get_boolean("uv-sync",0) && !uvCatalogSent ){ @ pragma uv-pull-only send_unversioned_catalog(&xfer); uvCatalogSent = 1; } if( xfer.nToken==3 && blob_is_int(&xfer.aToken[1], &iVers) && iVers>=2 ){ int seqno, max; if( iVers>=3 ){ cgi_set_content_type("application/x-fossil-uncompressed"); } blob_is_int(&xfer.aToken[2], &seqno); max = db_int(0, "SELECT max(rid) FROM blob"); while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){ if( time(NULL) >= xfer.maxTime ) break; if( iVers>=3 ){ send_compressed_file(&xfer, seqno); }else{ send_file(&xfer, seqno, 0, 1); } seqno++; } if( seqno>max ) seqno = 0; @ clone_seqno %d(seqno) }else{ isClone = 1; isPull = 1; deltaFlag = 1; } @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) }else /* login USER NONCE SIGNATURE ** ** The client has sent login credentials to the server. ** Validate the login. This has to happen before anything else. ** The client can send multiple logins. Permissions are cumulative. */ if( blob_eq(&xfer.aToken[0], "login") && xfer.nToken==4 ){ if( disableLogin ){ g.perm.Read = g.perm.Write = g.perm.Private = g.perm.Admin = 1; }else{ 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 ** ** Client is requesting a configuration value from the server */ if( blob_eq(&xfer.aToken[0], "reqconfig") && xfer.nToken==2 ){ if( g.perm.Read ){ char *zName = blob_str(&xfer.aToken[1]); if( zName[0]=='/' ){ /* New style configuration transfer */ int groupMask = configure_name_to_mask(&zName[1], 0); if( !g.perm.Admin ) groupMask &= ~(CONFIGSET_USER|CONFIGSET_SCRIBER); if( !g.perm.RdAddr ) groupMask &= ~CONFIGSET_ADDR; configure_send_group(xfer.pOut, groupMask, 0); } } }else /* config NAME SIZE \n CONTENT ** ** Client has sent a configuration value to the server. ** 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) ){ const char *zName = blob_str(&xfer.aToken[1]); Blob content; blob_zero(&content); blob_extract(xfer.pIn, size, &content); if( !g.perm.Admin ){ cgi_reset_content(); @ error not\sauthorized\sto\spush\sconfiguration nErr++; break; } configure_receive(zName, &content, CONFIGSET_ALL); blob_reset(&content); blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR); }else /* cookie TEXT ** ** A cookie contains an 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 ** communication. The cookie might record information that helps ** the server optimize a push or pull. ** ** The client is not required to return a cookie. So the server ** must not depend on the cookie. The cookie should be an optimization ** only. The client might also send a cookie that came from a different ** server. So the server must be prepared to distinguish its own cookie ** from cookies originating from other servers. The client might send ** back several different cookies to the server. The server should be ** prepared to sift through the cookies and pick the one that it wants. */ if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){ /* Process the cookie */ }else /* private ** ** The client card indicates that the next "file" or "cfile" will contain ** private content. */ if( blob_eq(&xfer.aToken[0], "private") ){ if( !g.perm.Private ){ server_private_xfer_not_authorized(); }else{ xfer.nextIsPrivate = 1; } }else /* pragma NAME VALUE... ** ** The client issues pragmas to try to influence the behavior of the ** server. These are requests only. Unknown pragmas are silently ** ignored. */ if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){ /* pragma send-private ** ** The client is requesting private artifacts. ** ** If the user has the "x" privilege (which must be set explicitly - ** it is not automatic with "a" or "s") then this pragma causes ** private information to be pulled in addition to public records. */ if( blob_eq(&xfer.aToken[1], "send-private") ){ login_check_credentials(); if( !g.perm.Private ){ server_private_xfer_not_authorized(); }else{ xfer.syncPrivate = 1; } }else /* pragma send-catalog ** ** The client wants to see igot cards for all known artifacts. ** This is used as part of "sync --verily" to help ensure that ** no artifacts have been missed on prior syncs. */ if( blob_eq(&xfer.aToken[1], "send-catalog") ){ xfer.resync = 0x7fffffff; }else /* pragma client-version VERSION ?DATE? ?TIME? ** ** The client announces to the server what version of Fossil it ** is running. The DATE and TIME are a pure numeric ISO8601 time ** for the specific check-in of the client. */ if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){ xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2])); if( xfer.nToken>=5 ){ xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); @ pragma server-version %d(RELEASE_VERSION_NUMBER) \ @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME) } }else /* pragma uv-hash HASH ** ** The client wants to make sure that unversioned files are all synced. ** If the HASH does not match, send a complete catalog of ** "uvigot" cards. */ if( blob_eq(&xfer.aToken[1], "uv-hash") && blob_is_hname(&xfer.aToken[2]) ){ if( !uvCatalogSent && g.perm.Read && !blob_eq_str(&xfer.aToken[2], unversioned_content_hash(0),-1) ){ if( g.perm.WrUnver ){ @ pragma uv-push-ok }else if( g.perm.Read ){ @ pragma uv-pull-only } send_unversioned_catalog(&xfer); } uvCatalogSent = 1; }else /* pragma ci-lock CHECKIN-HASH CLIENT-ID ** ** The client wants to make non-branch commit against the check-in ** identified by CHECKIN-HASH. The server will remember this and ** subsequent ci-lock requests from different clients will generate ** a ci-lock-fail pragma in the reply. */ if( blob_eq(&xfer.aToken[1], "ci-lock") && xfer.nToken==4 && blob_is_hname(&xfer.aToken[2]) ){ Stmt q; sqlite3_int64 iNow = time(0); sqlite3_int64 maxAge = db_get_int("lock-timeout",60); int seenFault = 0; db_prepare(&q, "SELECT value->>'login'," " mtime," " value->>'clientid'," " (SELECT rid FROM blob WHERE uuid=substr(name,9))," " name" " FROM config" " WHERE name GLOB 'ci-lock-*'" " AND json_valid(value)" ); while( db_step(&q)==SQLITE_ROW ){ int x = db_column_int(&q,3); const char *zName = db_column_text(&q,4); if( db_column_int64(&q,1)<=iNow-maxAge || !is_a_leaf(x) ){ /* check-in locks expire after maxAge seconds, or when the ** check-in is no longer a leaf */ db_unprotect(PROTECT_CONFIG); db_multi_exec("DELETE FROM config WHERE name=%Q", zName); db_protect_pop(); continue; } if( fossil_strcmp(zName+8, blob_str(&xfer.aToken[2]))==0 ){ const char *zClientId = db_column_text(&q, 2); const char *zLogin = db_column_text(&q,0); sqlite3_int64 mtime = db_column_int64(&q, 1); if( fossil_strcmp(zClientId, blob_str(&xfer.aToken[3]))!=0 ){ @ pragma ci-lock-fail %F(zLogin) %lld(mtime) } seenFault = 1; } } db_finalize(&q); if( !seenFault ){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "REPLACE INTO config(name,value,mtime)" "VALUES('ci-lock-%q',json_object('login',%Q,'clientid',%Q),now())", blob_str(&xfer.aToken[2]), g.zLogin, blob_str(&xfer.aToken[3]) ); db_protect_pop(); } if( db_get_boolean("forbid-delta-manifests",0) ){ @ pragma avoid-delta-manifests } }else /* pragma ci-unlock CLIENT-ID ** ** Remove any locks previously held by CLIENT-ID. Clients send this ** pragma with their own ID whenever they know that they no longer ** have any commits pending. */ if( blob_eq(&xfer.aToken[1], "ci-unlock") && xfer.nToken==3 && blob_is_hname(&xfer.aToken[2]) ){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "DELETE FROM config" " WHERE name GLOB 'ci-lock-*'" " AND (NOT json_valid(value) OR value->>'clientid'==%Q)", blob_str(&xfer.aToken[2]) ); db_protect_pop(); }else /* pragma client-url URL ** ** This pragma is an informational notification to the server that ** their relationship could, in theory, be inverted by having the ** server call the client at URL. */ if( blob_eq(&xfer.aToken[1], "client-url") && xfer.nToken==3 && g.perm.Write ){ xfer_syncwith(blob_str(&xfer.aToken[2]), 1); }else /* pragma req-links ** ** The client sends this message to the server to ask the server ** to tell it about alternative repositories in the reply. */ if( blob_eq(&xfer.aToken[1], "req-links") ){ bSendLinks = 1; } }else /* 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); } hook_expecting_more_artifacts(xfer.nGimmeSent?60:0); db_multi_exec("DROP TABLE onremote; DROP TABLE unk;"); manifest_crosslink_end(MC_PERMIT_HOOKS); /* Send URLs for alternative repositories for the same project, ** if requested by the client. */ if( bSendLinks && g.zBaseURL ){ Stmt q; db_prepare(&q, "WITH remote(mtime, url, arg) AS (\n" " SELECT mtime, substr(name,10), '{}' FROM config\n" " WHERE name GLOB 'syncwith:http*'\n" " UNION ALL\n" " SELECT mtime, substr(name,10), '{}' FROM config\n" " WHERE name GLOB 'syncfrom:http*'\n" " UNION ALL\n" " SELECT mtime, substr(name,9), '{\"type\":\"git\"}' FROM config\n" " WHERE name GLOB 'gitpush:*'\n" ")\n" "SELECT url, json_insert(arg,'$.src',%Q), max(mtime)\n" " FROM remote WHERE mtime>unixepoch('now','-1 month')\n" " GROUP BY url;", g.zBaseURL ); while( db_step(&q)==SQLITE_ROW ){ UrlData x; const char *zUrl = db_column_text(&q, 0); const char *zArg = db_column_text(&q, 1); i64 iMtime = db_column_int64(&q, 2); memset(&x, 0, sizeof(x)); url_parse_local(zUrl, URL_OMIT_USER, &x); if( x.name!=0 && sqlite3_strlike("%localhost%", x.name, 0)!=0 ){ @ pragma link %F(x.canonical) %F(zArg) %lld(iMtime) } url_unparse(&x); } db_finalize(&q); } /* 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) errors %d(nErr) free(zNow); db_commit_transaction(); configure_rebuild(); } /* ** COMMAND: test-xfer ** ** Usage: %fossil test-xfer ?OPTIONS? XFERFILE ** ** Pass the sync-protocol input file XFERFILE into the server-side sync ** protocol handler. Generate a reply on standard output. ** ** This command was original created to help debug the server side of ** sync messages. The XFERFILE is the uncompressed content of an ** "xfer" HTTP request from client to server. This command interprets ** that message and generates the content of an HTTP reply (without any ** encoding and without the HTTP reply headers) and writes that reply ** on standard output. ** ** One possible usages scenario is to capture some XFERFILE examples ** using a command like: ** ** fossil push http://bogus/ --httptrace ** ** The complete HTTP requests are stored in files named "http-request-N.txt". ** Find one of those requests, remove the HTTP header, and make other edits ** as necessary to generate an appropriate XFERFILE test case. Then run: ** ** fossil test-xfer xferfile.txt ** ** Options: ** --host HOSTNAME Supply a server hostname used to populate ** g.zBaseURL and similar. */ void cmd_test_xfer(void){ const char *zHost; db_find_and_open_repository(0,0); zHost = find_option("host",0,1); verify_all_options(); if( g.argc!=2 && g.argc!=3 ){ usage("?MESSAGEFILE?"); } if( zHost==0 ) zHost = "localhost:8080"; g.zBaseURL = mprintf("http://%s", zHost); g.zHttpsURL = mprintf("https://%s", zHost); g.zTop = mprintf(""); blob_zero(&g.cgiIn); blob_read_from_file(&g.cgiIn, g.argc==2 ? "-" : g.argv[2], ExtFILE); disableLogin = 1; page_xfer(); fossil_print("%s", cgi_extract_content()); } /* ** Format strings for progress reporting. */ static const char zLabelFormat[] = "%-10s %10s %10s %10s %10s\n"; static const char zValueFormat[] = "\r%-10s %10d %10d %10d %10d\n"; static const char zBriefFormat[] = "Round-trips: %d Artifacts sent: %d received: %d\r"; #if INTERFACE /* ** Flag options for controlling client_sync() */ #define SYNC_PUSH 0x00001 /* push content client to server */ #define SYNC_PULL 0x00002 /* pull content server to client */ #define SYNC_CLONE 0x00004 /* clone the repository */ #define SYNC_PRIVATE 0x00008 /* Also transfer private content */ #define SYNC_VERBOSE 0x00010 /* Extra diagnostics */ #define SYNC_RESYNC 0x00020 /* --verily */ #define SYNC_FROMPARENT 0x00040 /* Pull from the parent project */ #define SYNC_UNVERSIONED 0x00100 /* Sync unversioned content */ #define SYNC_UV_REVERT 0x00200 /* Copy server unversioned to client */ #define SYNC_UV_TRACE 0x00400 /* Describe UV activities */ #define SYNC_UV_DRYRUN 0x00800 /* Do not actually exchange files */ #define SYNC_IFABLE 0x01000 /* Inability to sync is not fatal */ #define SYNC_CKIN_LOCK 0x02000 /* Lock the current check-in */ #define SYNC_NOHTTPCOMPRESS 0x04000 /* Do not compression HTTP messages */ #define SYNC_ALLURL 0x08000 /* The --all flag - sync to all URLs */ #define SYNC_SHARE_LINKS 0x10000 /* Request alternate repo links */ #define SYNC_XVERBOSE 0x20000 /* Extra verbose. Network traffic */ #endif /* ** 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( unsigned syncFlags, /* Mask of SYNC_* flags */ unsigned configRcvMask, /* Receive these configuration items */ unsigned configSendMask, /* Send these configuration items */ const char *zAltPCode, /* Alternative project code (usually NULL) */ int *pnRcvd /* Set to # received artifacts, if not NULL */ ){ int go = 1; /* Loop until zero */ int nCardSent = 0; /* Number of cards sent */ int nCardRcvd = 0; /* Number of cards received */ int nCycle = 0; /* Number of round trips to the server */ int size; /* Size of a config value or uvfile */ int origConfigRcvMask; /* Original value of configRcvMask */ int nFileRecv; /* Number of files received */ int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ const char *zCookie; /* Server cookie */ i64 nUncSent, nUncRcvd; /* Bytes sent and received (before compression) */ i64 nSent, nRcvd; /* Bytes sent and received (after compression) */ int cloneSeqno = 1; /* Sequence number for clones */ Blob send; /* Text we are sending to the server */ Blob recv; /* Reply we got back from the server */ Xfer xfer; /* Transfer data */ int pctDone; /* Percentage done with a message */ int lastPctDone = -1; /* Last displayed pctDone */ double rArrivalTime; /* Time at which a message arrived */ const char *zSCode = db_get("server-code", "x"); const char *zPCode = db_get("project-code", 0); int nErr = 0; /* Number of errors */ int nRoundtrip= 0; /* Number of HTTP requests */ int nArtifactSent = 0; /* Total artifacts sent */ int nArtifactRcvd = 0; /* Total artifacts received */ int nPriorArtifact = 0; /* Artifacts received on prior round-trips */ const char *zOpType = 0;/* Push, Pull, Sync, Clone */ double rSkew = 0.0; /* Maximum time skew */ int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ int uvDoPush = 0; /* Generate uvfile messages to send to server */ int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */ int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */ sqlite3_int64 mtime; /* Modification time on a UV file */ int autopushFailed = 0; /* Autopush following commit failed if true */ const char *zCkinLock; /* Name of check-in to lock. NULL for none */ const char *zClientId; /* A unique identifier for this check-out */ unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ if( pnRcvd ) *pnRcvd = 0; if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH; if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0 && configRcvMask==0 && configSendMask==0 ){ return 0; /* Nothing to do */ } /* Compute an appropriate project code. zPCode is the project code ** for the local repository. zAltPCode will usually be NULL, but might ** also be an alternative project code to expect on the server. When ** zAltPCode is not NULL, that means we are doing a cross-project import - ** in other words, reading content from one project into a different ** project. */ if( syncFlags & SYNC_FROMPARENT ){ const char *zPX; configRcvMask = 0; configSendMask = 0; syncFlags &= ~(SYNC_PUSH); zPX = db_get("parent-project-code", 0); if( zPX==0 || db_get("parent-project-name",0)==0 ){ fossil_fatal("there is no parent project: set the 'parent-project-code'" " and 'parent-project-name' config parameters in order" " to pull from a parent project"); } if( zPX ){ zAltPCode = zPX; } } if( zAltPCode!=0 && zPCode!=0 && sqlite3_stricmp(zPCode, zAltPCode)==0 ){ zAltPCode = 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; xfer.remoteVersion = RELEASE_VERSION_NUMBER; 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; nUncSent = nUncRcvd = 0; /* 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); } /* Figure out which check-in to lock */ if( syncFlags & SYNC_CKIN_LOCK ){ int vid = db_lget_int("checkout",0); zCkinLock = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); }else{ zCkinLock = 0; } zClientId = g.localOpen ? db_lget("client-id", 0) : 0; /* When syncing unversioned files, create a TEMP table in which to store ** the names of files that need to be sent from client to server. ** ** The initial assumption is that all unversioned files need to be sent ** to the other side. But "uvigot" cards received back from the remote ** side will normally cause many of these entries to be removed since they ** do not really need to be sent. */ if( (syncFlags & (SYNC_UNVERSIONED|SYNC_CLONE))!=0 ){ unversioned_schema(); db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS uv_tosend(" " name TEXT PRIMARY KEY," /* Name of file to send client->server */ " mtimeOnly BOOLEAN" /* True to only send mtime, not content */ ") WITHOUT ROWID;" "REPLACE INTO uv_tosend(name,mtimeOnly)" " SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;" ); } /* ** The request from the client always begin with a clone, pull, ** or push message. */ blob_appendf(&send, "pragma client-version %d %d %d\n", RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE, MANIFEST_NUMERIC_TIME); if( syncFlags & SYNC_CLONE ){ blob_appendf(&send, "clone 3 %d\n", cloneSeqno); syncFlags &= ~(SYNC_PUSH|SYNC_PULL); nCardSent++; /* TBD: Request all transferable configuration values */ content_enable_dephantomize(0); zOpType = "Clone"; }else if( syncFlags & SYNC_PULL ){ blob_appendf(&send, "pull %s %s\n", zSCode, zAltPCode ? zAltPCode : zPCode); nCardSent++; zOpType = (syncFlags & SYNC_PUSH)?"Sync":"Pull"; if( (syncFlags & SYNC_RESYNC)!=0 && nCycle<2 ){ blob_appendf(&send, "pragma send-catalog\n"); nCardSent++; } } 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"); } /* Send the client-url pragma on the first cycle if the client has ** a known public url. */ if( zAltPCode==0 ){ const char *zSelfUrl = public_url(); if( zSelfUrl ){ blob_appendf(&send, "pragma client-url %s\n", zSelfUrl); } } /* Request URLs of alternative repositories */ if( zAltPCode==0 && (syncFlags & SYNC_SHARE_LINKS)!=0 ){ blob_appendf(&send, "pragma req-links\n"); } 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);" "CREATE TEMP TABLE unk(uuid TEXT PRIMARY KEY) WITHOUT ROWID;" ); manifest_crosslink_begin(); /* Client sends the most recently received cookie back to the server. ** 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); } /* Client sends gimme cards for phantoms */ 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); } /* Client sends 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 ){ blob_appendf(&send, "reqconfig %s\n", zName); zName = configure_next_name(configRcvMask); nCardSent++; } origConfigRcvMask = configRcvMask; configRcvMask = 0; } /* Client sends a request to sync unversioned files. ** On a clone, delay sending this until the second cycle since ** the login card might fail on the first cycle. */ if( (syncFlags & SYNC_UNVERSIONED)!=0 && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) && !uvHashSent ){ blob_appendf(&send, "pragma uv-hash %s\n", unversioned_content_hash(0)); nCardSent++; uvHashSent = 1; } /* On a "fossil config push", the client send configuration parameters ** being pushed up to the server */ if( configSendMask ){ if( zOpType==0 ) zOpType = "Push"; nCardSent += configure_send_group(xfer.pOut, configSendMask, 0); configSendMask = 0; } /* Send unversioned files present here on the client but missing or ** obsolete on the server. ** ** Or, if the SYNC_UV_REVERT flag is set, delete the local unversioned ** files that do not exist on the server. ** ** This happens on the second exchange, since we do not know what files ** need to be sent until after the uvigot cards from the first exchange ** have been processed. */ if( uvDoPush ){ assert( (syncFlags & SYNC_UNVERSIONED)!=0 ); if( syncFlags & SYNC_UV_DRYRUN ){ uvDoPush = 0; }else if( syncFlags & SYNC_UV_REVERT ){ db_multi_exec( "DELETE FROM unversioned" " WHERE name IN (SELECT name FROM uv_tosend);" "DELETE FROM uv_tosend;" ); uvDoPush = 0; }else{ Stmt uvq; int rc = SQLITE_OK; db_prepare(&uvq, "SELECT name, mtimeOnly FROM uv_tosend"); while( (rc = db_step(&uvq))==SQLITE_ROW ){ const char *zName = db_column_text(&uvq, 0); send_unversioned_file(&xfer, zName, db_column_int(&uvq,1)); nCardSent++; nArtifactSent++; db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName); if( syncFlags & SYNC_VERBOSE ){ fossil_print("\rUnversioned-file sent: %s\n", zName); } if( (int)blob_size(xfer.pOut)>xfer.mxSend ) break; } db_finalize(&uvq); if( rc==SQLITE_DONE ) uvDoPush = 0; } } /* Lock the current check-out */ if( zCkinLock ){ if( zClientId==0 ){ zClientId = db_text(0, "SELECT lower(hex(randomblob(20)))"); db_lset("client-id", zClientId); } blob_appendf(&send, "pragma ci-lock %s %s\n", zCkinLock, zClientId); zCkinLock = 0; }else if( zClientId ){ blob_appendf(&send, "pragma ci-unlock %s\n", zClientId); } /* Append randomness to the end of the uplink message. This makes all ** 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)!=0 && (syncFlags & SYNC_XVERBOSE)==0 ){ fossil_print("waiting for server..."); } fflush(stdout); /* Exchange messages with the server */ if( (syncFlags & SYNC_CLONE)!=0 && nCycle==0 ){ /* Do not send a login card on the first round-trip of a clone */ mHttpFlags = 0; }else{ mHttpFlags = HTTP_USE_LOGIN; } if( syncFlags & SYNC_NOHTTPCOMPRESS ){ mHttpFlags |= HTTP_NOCOMPRESS; } if( syncFlags & SYNC_XVERBOSE ){ mHttpFlags |= HTTP_VERBOSE; } /* Do the round-trip to the server */ if( http_exchange(&send, &recv, mHttpFlags, MAX_REDIRECTS, 0) ){ nErr++; go = 2; break; } /* Remember the URL of the sync target in the config file on the ** first successful round-trip */ if( nCycle==0 && db_is_writeable("repository") ){ xfer_syncwith(g.url.canonical, 0); } /* 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; xfer.nPrivIGot = 0; lastPctDone = -1; nUncSent += blob_size(&send); blob_reset(&send); blob_appendf(&send, "pragma client-version %d %d %d\n", RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE, MANIFEST_NUMERIC_TIME); 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); } /* Begin constructing the next message (which might never be ** sent) by beginning with the pull or push cards */ if( syncFlags & SYNC_PULL ){ blob_appendf(&send, "pull %s %s\n", zSCode, zAltPCode ? zAltPCode : zPCode); nCardSent++; } if( syncFlags & SYNC_PUSH ){ blob_appendf(&send, "push %s %s\n", zSCode, zPCode); nCardSent++; } go = 0; nUvGimmeSent = 0; nUvFileRcvd = 0; nPriorArtifact = nArtifactRcvd; /* Process the reply that came back from the server */ while( blob_line(&recv, &xfer.line) ){ if( blob_buffer(&xfer.line)[0]=='#' ){ const char *zLine = blob_buffer(&xfer.line); if( memcmp(zLine, "# timestamp ", 12)==0 ){ char zTime[20]; double rDiff; sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]); rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g", zTime, rArrivalTime); if( rDiff>9e98 || rDiff<-9e98 ) rDiff = 0.0; if( rDiff*24.0*3600.0 >= -(blob_size(&recv)/5000.0 + 20) ){ rDiff = 0.0; } if( fossil_fabs(rDiff)>fossil_fabs(rSkew) ) rSkew = rDiff; } nCardRcvd++; continue; } xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); nCardRcvd++; if( (syncFlags & SYNC_VERBOSE)!=0 && recv.nUsed>0 ){ pctDone = (recv.iCursor*100)/recv.nUsed; if( pctDone!=lastPctDone ){ fossil_print("\rprocessed: %d%% ", pctDone); lastPctDone = pctDone; fflush(stdout); } } /* file HASH SIZE \n CONTENT ** file HASH DELTASRC SIZE \n CONTENT ** ** Client receives 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 HASH USIZE CSIZE \n CONTENT ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT ** ** Client receives a compressed file transmitted from the server. */ if( blob_eq(&xfer.aToken[0],"cfile") ){ xfer_accept_compressed_file(&xfer, 0, 0); nArtifactRcvd++; }else /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT ** ** Client accepts an unversioned file from the server. */ if( blob_eq(&xfer.aToken[0], "uvfile") ){ xfer_accept_unversioned_file(&xfer, 1); nArtifactRcvd++; nUvFileRcvd++; if( syncFlags & SYNC_VERBOSE ){ fossil_print("\rUnversioned-file received: %s\n", blob_str(&xfer.aToken[1])); } }else /* gimme HASH ** ** Client receives an artifact request from the server. ** If the file is a manifest, assume that the server will also want ** to know all of the content artifacts associated with the manifest ** and send those too. */ if( blob_eq(&xfer.aToken[0], "gimme") && xfer.nToken==2 && blob_is_hname(&xfer.aToken[1]) ){ remote_unk(&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 HASH ?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_hname(&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_private(rid); }else{ 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 /* uvigot NAME MTIME HASH SIZE ** ** Server announces that it has a particular unversioned file. The ** server will only send this card if the client had previously sent ** a "pragma uv-hash" card with a hash that does not match. ** ** If the identified file needs to be transferred, then setup for the ** transfer. Generate a "uvgimme" card in the reply if the server ** version is newer than the client. Generate a "uvfile" card if ** the client version is newer than the server. If HASH is "-" ** (indicating that the file has been deleted) and MTIME is newer, ** then do the deletion. */ if( xfer.nToken==5 && blob_eq(&xfer.aToken[0], "uvigot") && blob_is_filename(&xfer.aToken[1]) && blob_is_int64(&xfer.aToken[2], &mtime) && blob_is_int(&xfer.aToken[4], &size) && (blob_eq(&xfer.aToken[3],"-") || blob_is_hname(&xfer.aToken[3])) ){ const char *zName = blob_str(&xfer.aToken[1]); const char *zHash = blob_str(&xfer.aToken[3]); int iStatus; iStatus = unversioned_status(zName, mtime, zHash); if( (syncFlags & SYNC_UV_REVERT)!=0 ){ if( iStatus==4 ) iStatus = 2; if( iStatus==5 ) iStatus = 1; } if( syncFlags & (SYNC_UV_TRACE|SYNC_UV_DRYRUN) ){ const char *zMsg = 0; switch( iStatus ){ case 0: case 1: zMsg = "UV-PULL"; break; case 2: zMsg = "UV-PULL-MTIME-ONLY"; break; case 4: zMsg = "UV-PUSH-MTIME-ONLY"; break; case 5: zMsg = "UV-PUSH"; break; } if( zMsg ) fossil_print("\r%s: %s\n", zMsg, zName); if( syncFlags & SYNC_UV_DRYRUN ){ iStatus = 99; /* Prevent any changes or reply messages */ } } if( iStatus<=1 ){ if( zHash[0]!='-' ){ blob_appendf(xfer.pOut, "uvgimme %s\n", zName); nCardSent++; nUvGimmeSent++; db_multi_exec("DELETE FROM unversioned WHERE name=%Q", zName); }else if( iStatus==1 ){ db_multi_exec( "UPDATE unversioned" " SET mtime=%lld, hash=NULL, sz=0, encoding=0, content=NULL" " WHERE name=%Q", mtime, zName ); db_unset("uv-hash", 0); } }else if( iStatus==2 ){ db_multi_exec( "UPDATE unversioned SET mtime=%lld WHERE name=%Q", mtime, zName ); db_unset("uv-hash", 0); } if( iStatus>=4 && uvPullOnly==1 ){ fossil_warning( "Warning: uv-pull-only \n" " Unable to push unversioned content because you lack\n" " sufficient permission on the server\n" ); uvPullOnly = 2; } if( iStatus<=3 || uvPullOnly ){ db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName); }else if( iStatus==4 ){ db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q",zName); }else if( iStatus==5 ){ db_multi_exec("REPLACE INTO uv_tosend(name,mtimeOnly) VALUES(%Q,0)", zName); } }else /* push SERVERCODE PRODUCTCODE ** ** Should only happen in response to a clone. This message tells ** the client what product code to use for the new database. */ if( blob_eq(&xfer.aToken[0],"push") && xfer.nToken==3 && (syncFlags & SYNC_CLONE)!=0 && blob_is_hname(&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 ** ** Client 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. */ if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 && blob_is_int(&xfer.aToken[2], &size) ){ const char *zName = blob_str(&xfer.aToken[1]); Blob content; blob_zero(&content); blob_extract(xfer.pIn, size, &content); g.perm.Admin = g.perm.RdAddr = 1; configure_receive(zName, &content, origConfigRcvMask); nCardRcvd++; nArtifactRcvd++; blob_reset(&content); blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR); }else /* cookie TEXT ** ** The client reserves a cookie from the server. 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 ** same server. */ if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){ db_set("cookie", blob_str(&xfer.aToken[1]), 0); }else /* private ** ** The server tells the client that the next "file" or "cfile" will ** contain private content. */ if( blob_eq(&xfer.aToken[0], "private") ){ xfer.nextIsPrivate = 1; }else /* clone_seqno N ** ** When doing a clone, the server tries to send all of its artifacts ** in sequence. This card indicates the sequence number of the next ** blob that needs to be sent. If N<=0 that indicates that all blobs ** have been sent. */ if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){ blob_is_int(&xfer.aToken[1], &cloneSeqno); }else /* message MESSAGE ** ** A message is received from the server. Print it. ** 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 ){ /* pragma server-version VERSION ?DATE? ?TIME? ** ** The server announces to the server what version of Fossil it ** is running. The DATE and TIME are a pure numeric ISO8601 time ** for the specific check-in of the client. */ if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){ xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2])); if( xfer.nToken>=5 ){ xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); } } /* pragma uv-pull-only ** pragma uv-push-ok ** ** If the server is unwilling to accept new unversioned content (because ** this client lacks the necessary permissions) then it sends a ** "uv-pull-only" pragma so that the client will know not to waste ** bandwidth trying to upload unversioned content. If the server ** does accept new unversioned content, it sends "uv-push-ok". */ else if( syncFlags & SYNC_UNVERSIONED ){ if( blob_eq(&xfer.aToken[1], "uv-pull-only") ){ uvPullOnly = 1; if( syncFlags & SYNC_UV_REVERT ) uvDoPush = 1; }else if( blob_eq(&xfer.aToken[1], "uv-push-ok") ){ uvDoPush = 1; } } /* pragma ci-lock-fail USER-HOLDING-LOCK LOCK-TIME ** ** The server generates this message when a "pragma ci-lock" ** is attempted on a check-in for which there is an existing ** lock. USER-HOLDING-LOCK is the name of the user who originated ** the lock, and LOCK-TIME is the timestamp (seconds since 1970) ** when the lock was taken. */ else if( blob_eq(&xfer.aToken[1], "ci-lock-fail") && xfer.nToken==4 ){ char *zUser = blob_terminate(&xfer.aToken[2]); sqlite3_int64 mtime, iNow; defossilize(zUser); iNow = time(NULL); if( blob_is_int64(&xfer.aToken[3], &mtime) && iNow>mtime ){ iNow = time(NULL); fossil_print("\nParent check-in locked by %s %s ago\n", zUser, human_readable_age((iNow+1-mtime)/86400.0)); }else{ fossil_print("\nParent check-in locked by %s\n", zUser); } g.ckinLockFail = fossil_strdup(zUser); } /* pragma avoid-delta-manifests ** ** Discourage the use of delta manifests. The remote side sends ** this pragma when its forbid-delta-manifests setting is true. */ else if( blob_eq(&xfer.aToken[1], "avoid-delta-manifests") ){ g.bAvoidDeltaManifests = 1; } /* pragma link URL ARG MTIME ** ** The server has sent the URL for a link to another repository. ** Record this as a link:URL entry in the config table. */ else if( blob_eq(&xfer.aToken[1], "link") && xfer.nToken==5 && (syncFlags & SYNC_SHARE_LINKS)!=0 ){ UrlData x; char *zUrl = blob_str(&xfer.aToken[2]); char *zArg = blob_str(&xfer.aToken[3]); i64 iTime = strtoll(blob_str(&xfer.aToken[4]),0,0); memset(&x, 0, sizeof(x)); defossilize(zUrl); defossilize(zArg); url_parse_local(zUrl, URL_OMIT_USER, &x); if( x.protocol && strncmp(x.protocol,"http",4)==0 && iTime>0 ){ db_unprotect(PROTECT_CONFIG); db_multi_exec( "INSERT INTO config(name,value,mtime)\n" " VALUES('link:%q',%Q,%lld)\n" " ON CONFLICT DO UPDATE\n" " SET value=excluded.value, mtime=excluded.mtime\n" " WHERE mtime<excluded.mtime;", zUrl, zArg, iTime ); db_protect_pop(); } url_unparse(&x); } }else /* error MESSAGE ** ** The server is reporting an error. The client will 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. ** ** Also ignore "not authorized to write" errors if this is an ** autopush following a commit. */ if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){ char *zMsg = blob_terminate(&xfer.aToken[1]); defossilize(zMsg); if( (syncFlags & SYNC_IFABLE)!=0 && sqlite3_strlike("%not authorized to write%",zMsg,0)==0 ){ autopushFailed = 1; nErr++; }else if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){ fossil_force_newline(); fossil_print("Error: %s\n", zMsg); blob_appendf(&xfer.err, "server says: %s\n", zMsg); nErr++; break; } }else /* Unknown message */ if( xfer.nToken>0 ){ if( blob_str(&xfer.aToken[0])[0]=='<' ){ fossil_warning( "server replies with HTML instead of fossil sync protocol:\n%b", &recv ); nErr++; break; } blob_appendf(&xfer.err, "unknown command: [%b]\n", &xfer.aToken[0]); } if( blob_size(&xfer.err) ){ fossil_force_newline(); fossil_warning("%b", &xfer.err); nErr++; break; } blobarray_reset(xfer.aToken, xfer.nToken); blob_reset(&xfer.line); } 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); } nUncRcvd += blob_size(&recv); blob_reset(&recv); nCycle++; /* Set go to 1 if we need to continue the sync/push/pull/clone for ** another round. Set go to 0 if it is time to quit. */ nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile; if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){ go = 1; mxPhantomReq = nFileRecv*2; if( mxPhantomReq<200 ) mxPhantomReq = 200; }else if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){ /* Go another round if files are queued to send */ go = 1; }else if( xfer.nPrivIGot>0 && nCycle==1 ){ go = 1; }else if( nUvGimmeSent>0 && (nUvFileRcvd>0 || nCycle<3) ){ /* Continue looping as long as new uvfile cards are being received ** and uvgimme cards are being sent. */ go = 1; }else if( (syncFlags & SYNC_CLONE)!=0 ){ if( nCycle==1 ){ go = 1; /* go at least two rounds on a clone */ }else if( nFileRecv>0 ){ go = 1; }else if( cloneSeqno>0 && nArtifactRcvd>nPriorArtifact ){ /* Continue the clone until we see the clone_seqno 0" card or ** until we stop receiving artifacts */ go = 1; } } nCardRcvd = 0; xfer.nFileRcvd = 0; xfer.nDeltaRcvd = 0; xfer.nDanglingFile = 0; db_multi_exec("DROP TABLE onremote; DROP TABLE unk;"); 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( pnRcvd ) *pnRcvd = nArtifactRcvd; 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(); if( g.zHttpCmd==0 ){ if( syncFlags & SYNC_VERBOSE ){ fossil_print( "%s done, wire bytes sent: %lld received: %lld remote: %s%s\n", zOpType, nSent, nRcvd, (g.url.name && g.url.name[0]!='\0') ? g.url.name : "", (g.zIpAddr && g.zIpAddr[0]!='\0' && fossil_strcmp(g.zIpAddr, g.url.name)) ? mprintf(" (%s)", g.zIpAddr) : ""); }else{ fossil_print( "%s done, wire bytes sent: %lld received: %lld remote: %s\n", zOpType, nSent, nRcvd, g.zIpAddr); } } if( syncFlags & SYNC_VERBOSE ){ fossil_print( "Uncompressed payload sent: %lld received: %lld\n", nUncSent, nUncRcvd); } blob_reset(&send); blob_reset(&recv); transport_close(&g.url); transport_global_shutdown(&g.url); if( nErr && go==2 ){ db_multi_exec("DROP TABLE onremote; DROP TABLE unk;"); manifest_crosslink_end(MC_PERMIT_HOOKS); content_enable_dephantomize(1); db_end_transaction(0); } if( nErr && autopushFailed ){ fossil_warning( "Warning: The check-in was successful and is saved locally but you\n" " are not authorized to push the changes back to the server\n" " at %s", g.url.canonical ); nErr--; } if( (syncFlags & SYNC_CLONE)==0 && g.rcvid && fossil_any_has_fork(g.rcvid) ){ fossil_warning("***** WARNING: a fork has occurred *****\n" "use \"fossil leaves -multiple\" for more details."); } return nErr; } |
Added src/xfersetup.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 | /* ** Copyright (c) 2007 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 transfer configuration ** setup screens. */ #include "config.h" #include "xfersetup.h" #include <assert.h> /* ** WEBPAGE: xfersetup ** Main sub-menu for configuring the transfer system. */ 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, URL_USE_CONFIG); 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="%R/%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, 0, 0); @ </pre> } } style_finish_page(); } /* ** 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 ){ z = db_get(zDbField, zDfltValue); } style_set_current_feature("xfersetup"); style_header("Edit %s", zTitle); if( P("clear")!=0 && cgi_csrf_safe(2) ){ db_unset(zDbField/*works-like:"x"*/, 0); if( xRebuild ) xRebuild(); z = zDfltValue; }else if( isSubmit && cgi_csrf_safe(2) ){ char *zErr = 0; if( xText && (zErr = xText(z))!=0 ){ @ <p class="xfersetupError">ERROR: %h(zErr)</p> }else{ db_set(zDbField/*works-like:"x"*/, z, 0); if( xRebuild ) xRebuild(); cgi_redirect("xfersetup"); } } @ <form action="%R/%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_finish_page(); } static const char *zDefaultXferCommon = 0; /* ** WEBPAGE: xfersetup_com ** View or edit the TH1 script that runs prior to receiving a ** transfer. */ void xfersetup_com_page(void){ static const char zDesc[] = @ Enter TH1 script that initializes variables prior to running @ any of the transfer request scripts. ; xfersetup_generic( "Transfer Common Script", "xfer-common-script", zDefaultXferCommon, zDesc, 0, 0, 30 ); } static const char *zDefaultXferPush = 0; /* ** WEBPAGE: xfersetup_push ** View or edit the TH1 script that runs after receiving a "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 ** View or edit the TH1 script that runs when a transfer commit ** is processed. */ 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 ** View or edit the TH1 script that runs when a ticket change artifact ** is processed during a transfer. */ 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.
︙ | ︙ | |||
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 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to generate ZIP and SQLAR archives. */ #include "config.h" #include <assert.h> #include <zlib.h> #include "zip.h" /* ** Type of archive to build. */ #define ARCHIVE_ZIP 0 #define ARCHIVE_SQLAR 1 /* ** 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; z[1] = (v>>8) & 0xff; } |
︙ | ︙ | |||
44 45 46 47 48 49 50 51 52 53 54 55 56 57 | static int nEntry; /* Number of files */ static int dosTime; /* DOS-format time */ static int dosDate; /* DOS-format date */ static int unixTime; /* Seconds since 1970 */ static int nDir; /* Number of entries in azDir[] */ static char **azDir; /* Directory names already added to the archive */ /* ** Initialize a new ZIP archive. */ void zip_open(void){ blob_zero(&body); blob_zero(&toc); nEntry = 0; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | static int nEntry; /* Number of files */ static int dosTime; /* DOS-format time */ static int dosDate; /* DOS-format date */ static int unixTime; /* Seconds since 1970 */ static int nDir; /* Number of entries in azDir[] */ static char **azDir; /* Directory names already added to the archive */ typedef struct Archive Archive; struct Archive { int eType; /* Type of archive (SQLAR or ZIP) */ Blob *pBlob; /* Output blob */ Blob tmp; /* Blob used as temp space for compression */ sqlite3 *db; /* Db used to assemble sqlar archive */ sqlite3_stmt *pInsert; /* INSERT statement for SQLAR */ sqlite3_vfs vfs; /* VFS object */ }; /* ** Ensure that blob pBlob is at least nMin bytes in size. */ static void zip_blob_minsize(Blob *pBlob, int nMin){ if( (int)blob_size(pBlob)<nMin ){ blob_resize(pBlob, nMin); } } /************************************************************************* ** Implementation of "archive" VFS. A VFS designed to store the contents ** of a new database in a Blob. Used to construct sqlar archives in ** memory. */ typedef struct ArchiveFile ArchiveFile; struct ArchiveFile { sqlite3_file base; /* Base class */ Blob *pBlob; }; static int archiveClose(sqlite3_file *pFile){ return SQLITE_OK; } static int archiveRead( sqlite3_file *pFile, void *pBuf, int iAmt, sqlite3_int64 iOfst ){ assert( iOfst==0 || iOfst==24 ); return SQLITE_IOERR_SHORT_READ; } static int archiveWrite( sqlite3_file *pFile, const void *pBuf, int iAmt, sqlite3_int64 iOfst ){ ArchiveFile *pAF = (ArchiveFile*)pFile; int nMin = (int)iOfst + iAmt; char *aBlob; /* Output buffer */ zip_blob_minsize(pAF->pBlob, nMin); aBlob = blob_buffer(pAF->pBlob); memcpy(&aBlob[iOfst], pBuf, iAmt); return SQLITE_OK; } static int archiveTruncate(sqlite3_file *pFile, sqlite3_int64 size){ return SQLITE_OK; } static int archiveSync(sqlite3_file *pFile, int flags){ return SQLITE_OK; } static int archiveFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){ *pSize = 0; return SQLITE_OK; } static int archiveLock(sqlite3_file *pFile, int eLock){ return SQLITE_OK; } static int archiveUnlock(sqlite3_file *pFile, int eLock){ return SQLITE_OK; } static int archiveCheckReservedLock(sqlite3_file *pFile, int *pResOut){ *pResOut = 0; return SQLITE_OK; } static int archiveFileControl(sqlite3_file *pFile, int op, void *pArg){ if( op==SQLITE_FCNTL_SIZE_HINT ){ ArchiveFile *pAF = (ArchiveFile*)pFile; zip_blob_minsize(pAF->pBlob, (int)(*(sqlite3_int64*)pArg)); } return SQLITE_NOTFOUND; } static int archiveSectorSize(sqlite3_file *pFile){ return 512; } static int archiveDeviceCharacteristics(sqlite3_file *pFile){ return 0; } static int archiveOpen( sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ static struct sqlite3_io_methods methods = { 1, /* iVersion */ archiveClose, archiveRead, archiveWrite, archiveTruncate, archiveSync, archiveFileSize, archiveLock, archiveUnlock, archiveCheckReservedLock, archiveFileControl, archiveSectorSize, archiveDeviceCharacteristics, 0, 0, 0, 0, 0, 0 }; ArchiveFile *pAF = (ArchiveFile*)pFile; assert( flags & SQLITE_OPEN_MAIN_DB ); pAF->base.pMethods = &methods; pAF->pBlob = (Blob*)pVfs->pAppData; return SQLITE_OK; } static int archiveDelete(sqlite3_vfs *pVfs, const char *zName, int syncDir){ return SQLITE_OK; } static int archiveAccess( sqlite3_vfs *pVfs, const char *zName, int flags, int *pResOut ){ *pResOut = 0; return SQLITE_OK; } static int archiveFullPathname( sqlite3_vfs *pVfs, const char *zIn, int nOut, char *zOut ){ int n = strlen(zIn); memcpy(zOut, zIn, n+1); return SQLITE_OK; } static int archiveRandomness(sqlite3_vfs *pVfs, int nByte, char *zOut){ memset(zOut, 0, nByte); return SQLITE_OK; } static int archiveSleep(sqlite3_vfs *pVfs, int microseconds){ return SQLITE_OK; } static int archiveCurrentTime(sqlite3_vfs *pVfs, double *prOut){ return SQLITE_OK; } static int archiveGetLastError(sqlite3_vfs *pVfs, int nBuf, char *aBuf){ return SQLITE_OK; } /* ** End of "archive" VFS. *************************************************************************/ /* ** Initialize a new ZIP archive. */ void zip_open(void){ blob_zero(&body); blob_zero(&toc); nEntry = 0; |
︙ | ︙ | |||
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); | | | < < < < < < < < < < < < < < < < < < < < < < < < < < > > | > > > | > > | | > > > | > | < | | | | 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 | /* ** 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 = (int)((rDate - 2440587.5)*86400.0); } /* ** Append a single file to a growing ZIP archive. ** ** pFile is the file to be appended. zName is the name ** that the file should be saved as. */ static void zip_add_file_to_zip( Archive *p, const char *zName, const Blob *pFile, int mPerm ){ z_stream stream; int nameLen; int toOut = 0; int iStart; unsigned long iCRC = 0; int nByte = 0; int nByteCompr = 0; int nBlob; /* Size of the blob */ int iMethod; /* Compression method. */ int iMode = 0644; /* Access permissions */ char *z; char zHdr[30]; char zExTime[13]; char zBuf[100]; char zOutBuf[100000]; /* Fill in as much of the header as we know. */ nameLen = (int)strlen(zName); if( nameLen==0 ) return; nBlob = pFile ? blob_size(pFile) : 0; if( pFile ){ /* This is a file, possibly empty... */ iMethod = (nBlob>0) ? 8 : 0; /* Cannot compress zero bytes. */ switch( mPerm ){ case PERM_LNK: iMode = 0120755; break; case PERM_EXE: iMode = 0100755; break; default: iMode = 0100644; break; } }else{ /* This is a directory, no blob... */ iMethod = 0; iMode = 040755; } memset(zHdr, 0, sizeof(zHdr)); put32(&zHdr[0], 0x04034b50); 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); |
︙ | ︙ | |||
196 197 198 199 200 201 202 | 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); | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < > > > | | | | | | | | | | | | | | | | | > > > > > > | | > > > > > > > > | | | | | | > > > > > > | > | | > | > > > > > > > < < | < | > | > | | > > > > > > > > > > > > > > > > > > > > | | > > | | < < > > > | | | | | < < < | < < < < | | < > > > > > > > > > > > > > | > > | < > > > > > > > | > > > > > > > | | > > > > > > > | > | < < < < < < < < | > > > > > > > > > > > > | > > > > > | > > > > > > > > > | > > > > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | > > > > > | > > | > > > > > > > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > | | > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > | > | 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 | 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); put16(&zBuf[10], iMethod); put16(&zBuf[12], dosTime); put16(&zBuf[14], dosDate); put32(&zBuf[16], iCRC); put32(&zBuf[20], nByteCompr); put32(&zBuf[24], nByte); put16(&zBuf[28], nameLen); put16(&zBuf[30], 9); put16(&zBuf[32], 0); put16(&zBuf[34], 0); put16(&zBuf[36], 0); put32(&zBuf[38], ((unsigned)iMode)<<16); put32(&zBuf[42], iStart); blob_append(&toc, zBuf, 46); blob_append(&toc, zName, nameLen); put16(&zExTime[2], 5); blob_append(&toc, zExTime, 9); nEntry++; } static void zip_add_file_to_sqlar( Archive *p, const char *zName, const Blob *pFile, int mPerm ){ int nName = (int)strlen(zName); if( p->db==0 ){ assert( p->vfs.zName==0 ); p->vfs.zName = (const char*)mprintf("archivevfs%p", (void*)p); p->vfs.iVersion = 1; p->vfs.szOsFile = sizeof(ArchiveFile); p->vfs.mxPathname = 512; p->vfs.pAppData = (void*)p->pBlob; p->vfs.xOpen = archiveOpen; p->vfs.xDelete = archiveDelete; p->vfs.xAccess = archiveAccess; p->vfs.xFullPathname = archiveFullPathname; p->vfs.xRandomness = archiveRandomness; p->vfs.xSleep = archiveSleep; p->vfs.xCurrentTime = archiveCurrentTime; p->vfs.xGetLastError = archiveGetLastError; sqlite3_vfs_register(&p->vfs, 0); sqlite3_open_v2("file:xyz.db", &p->db, SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, p->vfs.zName ); assert( p->db ); blob_zero(&p->tmp); sqlite3_exec(p->db, "PRAGMA page_size=512;" "PRAGMA journal_mode = off;" "PRAGMA cache_spill = off;" "BEGIN;" "CREATE TABLE sqlar(" "name TEXT PRIMARY KEY, -- name of the file\n" "mode INT, -- access permissions\n" "mtime INT, -- last modification time\n" "sz INT, -- original file size\n" "data BLOB -- compressed content\n" ");", 0, 0, 0 ); sqlite3_prepare(p->db, "INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1, &p->pInsert, 0 ); assert( p->pInsert ); sqlite3_bind_int64(p->pInsert, 3, unixTime); blob_zero(p->pBlob); } if( nName==0 ) return; if( pFile==0 ){ /* Directory. */ if( zName[nName-1]=='/' ) nName--; sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC); sqlite3_bind_int(p->pInsert, 2, 040755); sqlite3_bind_int(p->pInsert, 4, 0); sqlite3_bind_null(p->pInsert, 5); }else{ sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC); if( mPerm==PERM_LNK ){ sqlite3_bind_int(p->pInsert, 2, 0120755); sqlite3_bind_int(p->pInsert, 4, -1); sqlite3_bind_text(p->pInsert, 5, blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC ); }else{ unsigned int nIn = blob_size(pFile); unsigned long int nOut = nIn; sqlite3_bind_int(p->pInsert, 2, mPerm==PERM_EXE ? 0100755 : 0100644); sqlite3_bind_int(p->pInsert, 4, nIn); zip_blob_minsize(&p->tmp, nIn); compress( (unsigned char*) blob_buffer(&p->tmp), &nOut, (unsigned char*)blob_buffer(pFile), nIn ); if( nOut>=(unsigned long)nIn ){ sqlite3_bind_blob(p->pInsert, 5, blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC ); }else{ sqlite3_bind_blob(p->pInsert, 5, blob_buffer(&p->tmp), nOut, SQLITE_STATIC ); } } } sqlite3_step(p->pInsert); sqlite3_reset(p->pInsert); } static void zip_add_file( Archive *p, const char *zName, const Blob *pFile, int mPerm ){ if( p->eType==ARCHIVE_ZIP ){ zip_add_file_to_zip(p, zName, pFile, mPerm); }else{ zip_add_file_to_sqlar(p, zName, pFile, mPerm); } } /* ** 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. */ static void zip_add_folders(Archive *p, char *zName){ int i, c; int j; for(i=0; zName[i]; i++){ if( zName[i]=='/' ){ c = zName[i+1]; zName[i+1] = 0; for(j=0; j<nDir; j++){ if( fossil_strcmp(zName, azDir[j])==0 ) break; } if( j>=nDir ){ nDir++; azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir); azDir[j] = mprintf("%s", zName); zip_add_file(p, zName, 0, 0); } zName[i+1] = c; } } } /* ** Free all the members of structure Archive allocated while processing ** an SQLAR request. */ static void free_archive(Archive *p){ if( p->vfs.zName ){ sqlite3_vfs_unregister(&p->vfs); fossil_free((char*)p->vfs.zName); p->vfs.zName = 0; } sqlite3_finalize(p->pInsert); p->pInsert = 0; sqlite3_close(p->db); p->db = 0; } /* ** Write the ZIP archive into the given BLOB. */ static void zip_close(Archive *p){ int i; if( p->eType==ARCHIVE_ZIP ){ int iTocStart; int iTocEnd; char zBuf[30]; iTocStart = blob_size(&body); blob_append(&body, blob_buffer(&toc), blob_size(&toc)); iTocEnd = blob_size(&body); memset(zBuf, 0, sizeof(zBuf)); put32(&zBuf[0], 0x06054b50); put16(&zBuf[4], 0); put16(&zBuf[6], 0); put16(&zBuf[8], nEntry); put16(&zBuf[10], nEntry); put32(&zBuf[12], iTocEnd - iTocStart); put32(&zBuf[16], iTocStart); put16(&zBuf[20], 0); blob_append(&body, zBuf, 22); blob_reset(&toc); *(p->pBlob) = body; blob_zero(&body); }else{ if( p->db ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); free_archive(p); blob_reset(&p->tmp); } nEntry = 0; for(i=0; i<nDir; i++){ fossil_free(azDir[i]); } fossil_free(azDir); nDir = 0; azDir = 0; } /* ** COMMAND: test-filezip ** ** Generate a ZIP archive specified by the first argument that ** contains files given in the second and subsequent arguments. */ void filezip_cmd(void){ int i; Blob zip; Blob file; int eFType = SymFILE; Archive sArchive; memset(&sArchive, 0, sizeof(Archive)); sArchive.eType = ARCHIVE_ZIP; sArchive.pBlob = &zip; if( g.argc<3 ){ usage("ARCHIVE FILE...."); } if( find_option("dereference","h",0)!=0 ){ eFType = ExtFILE; } zip_open(); for(i=3; i<g.argc; i++){ blob_zero(&file); blob_read_from_file(&file, g.argv[i], eFType); zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,eFType)); blob_reset(&file); } zip_close(&sArchive); blob_write_to_file(&zip, g.argv[2]); } /* ** Given the RID for a manifest, construct a ZIP archive containing ** all files in the corresponding baseline. ** ** If RID is for an object that is not a real manifest, then the ** resulting ZIP archive contains a single file which is the RID ** object. The pInclude and pExclude parameters are ignored in this case. ** ** If the RID object does not exist in the repository, then ** pZip is zeroed. ** ** zDir is a "synthetic" subdirectory which all zipped files get ** added to as part of the zip file. It may be 0 or an empty string, ** in which case it is ignored. The intention is to create a zip which ** politely expands into a subdir instead of filling your current dir ** with source files. For example, pass a commit hash or "ProjectName". ** */ static void zip_of_checkin( int eType, /* Type of archive (ZIP or SQLAR) */ int rid, /* The RID of the check-in to build the archive from */ Blob *pZip, /* Write the archive content into this blob */ const char *zDir, /* Top-level directory of the archive */ Glob *pInclude, /* Only include files that match this pattern */ Glob *pExclude, /* Exclude files that match this pattern */ int listFlag /* Print each file on stdout */ ){ Blob mfile, hash, file; Manifest *pManifest; ManifestFile *pFile; Blob filename; int nPrefix; Archive sArchive; memset(&sArchive, 0, sizeof(Archive)); sArchive.eType = eType; sArchive.pBlob = pZip; blob_zero(&sArchive.tmp); if( pZip ) blob_zero(pZip); content_get(rid, &mfile); if( blob_size(&mfile)==0 ){ return; } blob_set_dynamic(&hash, rid_to_uuid(rid)); blob_zero(&filename); if( pZip ) zip_open(); if( zDir && zDir[0] ){ blob_appendf(&filename, "%s/", zDir); } nPrefix = blob_size(&filename); pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); if( pManifest ){ int flg, eflg = 0; char *zName = 0; zip_set_timedate(pManifest->rDate); flg = db_get_manifest_setting(); if( flg ){ /* eflg is the effective flags, taking include/exclude into account */ if( (pInclude==0 || glob_match(pInclude, "manifest")) && !glob_match(pExclude, "manifest") && (flg & MFESTFLG_RAW) ){ eflg |= MFESTFLG_RAW; } if( (pInclude==0 || glob_match(pInclude, "manifest.uuid")) && !glob_match(pExclude, "manifest.uuid") && (flg & MFESTFLG_UUID) ){ eflg |= MFESTFLG_UUID; } if( (pInclude==0 || glob_match(pInclude, "manifest.tags")) && !glob_match(pExclude, "manifest.tags") && (flg & MFESTFLG_TAGS) ){ eflg |= MFESTFLG_TAGS; } if( eflg & MFESTFLG_RAW ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); if( listFlag ) fossil_print("%s\n", zName); if( pZip ){ zip_add_folders(&sArchive, zName); zip_add_file(&sArchive, zName, &mfile, 0); } } if( eflg & MFESTFLG_UUID ){ blob_append(&hash, "\n", 1); blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.uuid", -1); zName = blob_str(&filename); if( listFlag ) fossil_print("%s\n", zName); if( pZip ){ zip_add_folders(&sArchive, zName); zip_add_file(&sArchive, zName, &hash, 0); } } if( eflg & MFESTFLG_TAGS ){ blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.tags", -1); zName = blob_str(&filename); if( listFlag ) fossil_print("%s\n", zName); if( pZip ){ Blob tagslist; blob_zero(&tagslist); get_checkin_taglist(rid, &tagslist); zip_add_folders(&sArchive, zName); zip_add_file(&sArchive, zName, &tagslist, 0); blob_reset(&tagslist); } } } manifest_file_rewind(pManifest); if( pZip ) zip_add_file(&sArchive, "", 0, 0); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ int fid; if( pInclude!=0 && !glob_match(pInclude, pFile->zName) ) continue; if( glob_match(pExclude, pFile->zName) ) continue; fid = uuid_to_rid(pFile->zUuid, 0); if( fid ){ blob_resize(&filename, nPrefix); blob_append(&filename, pFile->zName, -1); zName = blob_str(&filename); if( listFlag ) fossil_print("%s\n", zName); if( pZip ){ content_get(fid, &file); zip_add_folders(&sArchive, zName); zip_add_file(&sArchive, zName, &file, manifest_file_mperm(pFile)); blob_reset(&file); } } } } blob_reset(&mfile); manifest_destroy(pManifest); blob_reset(&filename); blob_reset(&hash); if( pZip ){ zip_close(&sArchive); } } /* ** Implementation of zip_cmd and sqlar_cmd. */ static void archive_cmd(int eType){ int rid; Blob zip; const char *zName; Glob *pInclude = 0; Glob *pExclude = 0; const char *zInclude; const char *zExclude; int listFlag = 0; const char *zOut; zName = find_option("name", 0, 1); zExclude = find_option("exclude", "X", 1); if( zExclude ) pExclude = glob_create(zExclude); zInclude = find_option("include", 0, 1); if( zInclude ) pInclude = glob_create(zInclude); listFlag = find_option("list","l",0)!=0; db_find_and_open_repository(0, 0); /* We should be done with options.. */ verify_all_options(); if( g.argc!=4 ){ usage("VERSION OUTPUTFILE"); } g.zOpenRevision = g.argv[2]; rid = name_to_typed_rid(g.argv[2], "ci"); if( rid==0 ){ fossil_fatal("Check-in not found: %s", g.argv[2]); return; } zOut = g.argv[3]; if( fossil_strcmp(zOut,"")==0 || fossil_strcmp(zOut,"/dev/null")==0 ){ zOut = 0; } if( zName==0 ){ zName = db_text("default-name", "SELECT replace(%Q,' ','_') " " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) " " || substr(blob.uuid, 1, 10)" " FROM event, blob" " WHERE event.objid=%d" " AND blob.rid=%d", db_get("project-name", "unnamed"), rid, rid ); } zip_of_checkin(eType, rid, zOut ? &zip : 0, zName, pInclude, pExclude, listFlag); glob_free(pInclude); glob_free(pExclude); if( zOut ){ blob_write_to_file(&zip, zOut); blob_reset(&zip); } } /* ** COMMAND: zip* ** ** Usage: %fossil zip VERSION OUTPUTFILE [OPTIONS] ** ** Generate a ZIP archive for a check-in. If the --name option is ** used, its argument becomes the name of the top-level directory in the ** resulting ZIP archive. If --name is omitted, the top-level directory ** name is derived from the project name, the check-in date and time, and ** the artifact ID of the check-in. ** ** The GLOBLIST argument to --exclude and --include can be a comma-separated ** list of glob patterns, where each glob pattern may optionally be enclosed ** in "..." or '...' so that it may contain commas. If a file matches both ** --include and --exclude then it is excluded. ** ** If OUTPUTFILE is an empty string or "/dev/null" then no ZIP archive is ** actually generated. This feature can be used in combination with ** the --list option to get a list of the filenames that would be in the ** ZIP archive had it actually been generated. ** ** Options: ** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude ** --include GLOBLIST Comma-separated list of GLOBs of files to include ** -l|--list Show archive content on stdout ** --name DIRECTORYNAME The name of the top-level directory in the archive ** -R REPOSITORY Specify a Fossil repository */ void zip_cmd(void){ archive_cmd(ARCHIVE_ZIP); } /* ** COMMAND: sqlar* ** ** Usage: %fossil sqlar VERSION OUTPUTFILE [OPTIONS] ** ** Generate an SQLAR archive for a check-in. If the --name option is ** used, its argument becomes the name of the top-level directory in the ** resulting SQLAR archive. If --name is omitted, the top-level directory ** name is derived from the project name, the check-in date and time, and ** the artifact ID of the check-in. ** ** The GLOBLIST argument to --exclude and --include can be a comma-separated ** list of glob patterns, where each glob pattern may optionally be enclosed ** in "..." or '...' so that it may contain commas. If a file matches both ** --include and --exclude then it is excluded. ** ** If OUTPUTFILE is an empty string or "/dev/null" then no SQLAR archive is ** actually generated. This feature can be used in combination with ** the --list option to get a list of the filenames that would be in the ** SQLAR archive had it actually been generated. ** ** Options: ** -X|--exclude GLOBLIST Comma-separated list of GLOBs of files to exclude ** --include GLOBLIST Comma-separated list of GLOBs of files to include ** -l|--list Show archive content on stdout ** --name DIRECTORYNAME The name of the top-level directory in the archive ** -R REPOSITORY Specify a Fossil repository */ void sqlar_cmd(void){ archive_cmd(ARCHIVE_SQLAR); } /* ** WEBPAGE: sqlar ** WEBPAGE: zip ** ** URLs: ** ** /zip/[VERSION/]NAME.zip ** /sqlar/[VERSION/]NAME.sqlar ** ** Generate a ZIP Archive or an SQL Archive for the check-in specified by ** VERSION. The archive is called NAME.zip or NAME.sqlar and has a top-level ** directory called NAME. ** ** The optional VERSION element defaults to "trunk" per the r= rules below. ** All of the following URLs are equivalent: ** ** /zip/release/xyz.zip ** /zip?r=release&name=xyz.zip ** /zip/xyz.zip?r=release ** /zip?name=release/xyz.zip ** ** Query parameters: ** ** name=[CKIN/]NAME The optional CKIN component of the name= parameter ** identifies the check-in from which the archive is ** constructed. If CKIN is omitted and there is no ** r= query parameter, then use "trunk". NAME is the ** name of the download file. The top-level directory ** in the generated archive is called by NAME with the ** file extension removed. ** ** r=TAG TAG identifies the check-in that is turned into an ** SQL or ZIP archive. The default value is "trunk". ** If r= is omitted and if the name= query parameter ** contains one "/" character then the of part the ** name= value before the / becomes the TAG and the ** part of the name= value after the / is the download ** filename. If no check-in is specified by either ** name= or r=, then "trunk" is used. ** ** in=PATTERN Only include files that match the comma-separate ** list of GLOB patterns in PATTERN, as with ex= ** ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a ** comma-separated list of GLOB patterns, where each ** pattern can optionally be quoted using ".." or '..'. ** Any file matching both ex= and in= is excluded. */ void baseline_zip_page(void){ int rid; const char *z; char *zName, *zRid, *zKey; int nName, nRid; const char *zInclude; /* The in= query parameter */ const char *zExclude; /* The ex= query parameter */ Blob cacheKey; /* The key to cache */ Glob *pInclude = 0; /* The compiled in= glob pattern */ Glob *pExclude = 0; /* The compiled ex= glob pattern */ Blob zip; /* ZIP archive accumulated here */ int eType = ARCHIVE_ZIP; /* Type of archive to generate */ char *zType; /* Human-readable archive type */ login_check_credentials(); if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; } if( fossil_strcmp(g.zPath, "sqlar")==0 ){ eType = ARCHIVE_SQLAR; zType = "SQL"; }else{ eType = ARCHIVE_ZIP; zType = "ZIP"; } fossil_nice_default(); zName = fossil_strdup(PD("name","")); z = P("r"); if( z==0 ) z = P("uuid"); if( z==0 ) z = tar_uuid_from_name(&zName); if( z==0 ) z = "trunk"; nName = strlen(zName); g.zOpenRevision = zRid = fossil_strdup(z); nRid = strlen(zRid); zInclude = P("in"); if( zInclude ) pInclude = glob_create(zInclude); zExclude = P("ex"); if( zExclude ) pExclude = glob_create(zExclude); if( zInclude==0 && zExclude==0 ){ etag_check_for_invariant_name(z); } if( eType==ARCHIVE_ZIP && nName>4 && fossil_strcmp(&zName[nName-4], ".zip")==0 ){ /* Special case: Remove the ".zip" suffix. */ nName -= 4; zName[nName] = 0; }else if( eType==ARCHIVE_SQLAR && nName>6 && fossil_strcmp(&zName[nName-6], ".sqlar")==0 ){ /* Special case: Remove the ".sqlar" suffix. */ nName -= 6; zName[nName] = 0; }else{ /* If the file suffix is not ".zip" or ".sqlar" 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 = symbolic_name_to_rid(nRid?zRid:zName, "ci"); if( rid<=0 ){ cgi_set_status(404, "Not Found"); @ Not found return; } if( nRid==0 && nName>10 ) zName[10] = 0; /* Compute a unique key for the cache entry based on query parameters */ blob_init(&cacheKey, 0, 0); blob_appendf(&cacheKey, "/%s/%z", g.zPath, rid_to_uuid(rid)); blob_appendf(&cacheKey, "/%q", zName); if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude); if( zExclude ) blob_appendf(&cacheKey, ",ex=%Q", zExclude); zKey = blob_str(&cacheKey); etag_check(ETAG_HASH, zKey); style_set_current_feature("zip"); if( P("debug")!=0 ){ style_header("%s Archive Generator Debug Screen", zType); @ zName = "%h(zName)"<br> @ rid = %d(rid)<br> if( zInclude ){ @ zInclude = "%h(zInclude)"<br> } if( zExclude ){ @ zExclude = "%h(zExclude)"<br> } @ zKey = "%h(zKey)" style_finish_page(); return; } if( referred_from_login() ){ style_header("%s Archive Download", zType); @ <form action='%R/%s(g.zPath)/%h(zName).%s(g.zPath)'> cgi_query_parameters_to_hidden(); @ <p>%s(zType) Archive named <b>%h(zName).%s(g.zPath)</b> @ holding the content of check-in <b>%h(zRid)</b>: @ <input type="submit" value="Download"> @ </form> style_finish_page(); return; } cgi_check_for_malice(); blob_zero(&zip); if( cache_read(&zip, zKey)==0 ){ zip_of_checkin(eType, rid, &zip, zName, pInclude, pExclude, 0); cache_write(&zip, zKey); } glob_free(pInclude); glob_free(pExclude); fossil_free(zName); fossil_free(zRid); g.zOpenRevision = 0; blob_reset(&cacheKey); cgi_set_content(&zip); if( eType==ARCHIVE_ZIP ){ cgi_set_content_type("application/zip"); }else{ cgi_set_content_type("application/sqlar"); } } |
Added test/Greek-Lipsum-1.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 | Κυο εξ υνυμ δισπυθανδο, εÏος αλιενυμ κυι θε. Îες εξ ελωκυενθιαμ ινστÏυσθιοÏ. Î˜ÎµÎ¼Ï€Î¿Ï Î½Î¿ÏƒÎ¸ÎµÏ ÏƒÏ… εως. Î Ï…Ïθο μωφεθ μωδεÏατιυς ατ μελ. Συ δυο αμετ ειυς. Î Ïι δεσωÏε ινθεγÏε ασυμσαν αδ, Ï€Ïω αν Ïεβυμ ÎµÏ†Ï†Î¹ÏƒÎ¹Î±Î½Î¸Ï…Ï Î½ÎµÏƒÎµÏƒÎ¹Ï„Î±Î¸Î¹Î²Ï…Ï‚. ÎοσθÏυμ ÏƒÏ…ÏƒÎ¹Ï€Î¹Î±Î½Ï„Ï…Ï Î·Î±Ï‚ ει, οÏναθυς Ïεσυσαβο Ï€Ïι ιδ, Ï€ÎµÏ Î½Î¿Î»Ï…Î¹ÏƒÎµ οπωÏθεÏε ιδ. Θε παÏτιενδω πεÏτινασια ινσωÏÏυπτε φις. Δισθας φαβυλας γυβεÏγÏεν εως ιν, αλιι σολυμ ηις θε, ποσθυλανθ ασυσαμυς ετ ηας. Îο ινανι φαβυλας θχεωπηÏαστυς ναμ, ευμ διστα ηομεÏω εα. Μαγνα φυγιθ υθ πεÏ, εσθ ατ νοσθÏυμ δεσεÏυισε. Φις αυδιαμ λαβοÏες παθÏιοκυε εξ, ετ φευγιαθ δεφινιεβας σιθ. Αμετ εÏιπυιτ δελισατα υσυ ετ, σενσιβυς φολυπθατιβυς Ï€ÎµÏ ÎµÎ¾. Κυωδ ιγνωθα τιβικυε ατ εαμ, νυλλα ηωνεσθαθις υθ νες. Φιξ αν μυτατ εξεÏσι λαβωÏε. Σεδ νονυμυ κυοδσι δελενιτ νε, συμο φιδε εα κυι. Ποπυλω μαιοÏυμ πεÏσεκυεÏις αν Ï€Ïω. Σολυμ σωνφενιÏε αδ ηας, αν ευμ σολυτα Ïεγιονε Ï€Ïοδεσεθ. ΦεÏο λαβοÏες σαλυταθυς θε δυο, ηις νε φεÏο βλανδιτ Ï€Ïαεσενθ, ιδ φις σολεατ φιφενδυμ. Συ συμ μωδω συμμο δολοÏες. Θε ναμ πωσιθ φευγιαθ τινσιδυνθ. Υθ ιψυμ νεμωÏε σαπιενθεμ μεα, ει εφεÏτι εφφισιενδι ηας. Ευμ αλβυσιυς Ï€Ïαεσενθ συ, δεσωÏε σεθεÏο ινδοστυμ μει ει. Ηις υθ συμμο μαλοÏυμ μανδαμυς, κυι ιν συαφιθαθε πεÏισυλις, ιισκυε οφφισιις κυο νο. Îε νονυμυ ηαβεμυς πχιλωσοπηια φις. Ετ ηας Ï…Ï„Î±Î¼Ï…Ï ÏεφοÏμιδανς. ΙνεÏμις δεθÏαξιθ Î½ÎµÎ³Î»ÎµÎ³ÎµÎ½Î¸Ï…Ï Î´Ï…Î¿ υθ, τωÏκυαθος δισεντιυνθ φιθυπεÏατοÏιβυς φιξ νε. Εα σεδ συας μελιυς, φιμ Ï€Ïοβο ινδοστυμ ÏεπÏιμικυε ευ. Î Ïι ιν λυδυς αυδιÏε, συμμο πεÏτινασια ÏƒÏ‰Î½ÏƒÎµÎ¸ÎµÎ¸Ï…Ï Ï†Î¹Ï‚ ιν, σιθ εξ επισυÏι μαλυισετ σωνσεπθαμ. Αν δετÏασθο ελειφενδ εξπλισαÏι Ï€Ïω. Ιυδισο σομμοδο συμ αδ. Δισαμ δισυντ φυλπυτατε ιν Ï€Ïω, εξ ηις δελενιτ μαιεσθατις. Ρεβυμ νονυμυ αππαÏεατ σιθ εα, σιθ ιδ νυλλα σολεατ πεθενθιυμ, ει οπθιων πεÏσεκυεÏις ευμ. Υθ νισλ ινσωλενς φιξ, εσθ φεÏι ιισκυε αÏγυμενθυμ συ, σεθεÏο μολεστιε αδιπισινγ ευ μεα. Ετ μεα μυσιυς λατινε, μει ÏƒÎµÎ¼Ï€ÎµÏ Î´ÎµÏƒÎµÏυντ πεÏτινασια αν. Συ φενιαμ ποπυλω αθωμωÏυμ κυο. Îο ιυς Ïεβυμ φιθυπεÏαθα δισπυτατιονι, ατ αλθεÏυμ χενδÏεÏιτ φιθυπεÏαθα συμ. Ευμ αυτεμ αππετεÏε αδιπισινγ ετ, νο κυο συας ελειφενδ. Εαμ θαλε δισαμ εξ. Ετ σομμοδο λεγενδως φελ, διαμ φωλυπθαÏια νο μελ, δυο φελιτ νεμωÏε αδ. Αν εξπετενδα συαφιθαθε φελ, ενιμ ασυμσαν Ï€ÎµÏ Î±Î´, εα φιμ μωδω υνυμ. Εα κυωδ Ï€Ïοβο πεÏσεσυτι φελ, ευ φεÏι Ï€ÏωπÏιαε ινσιδεÏιντ νες. Εξ νες οδιο δελενιτ, ελιτ ιυδισο ινθεγÏε δυο ιδ. Μελ αλικυιπ πεÏισυλις ετ, ατ ηας αυγυε λαβοÏες ασεντιοÏ. Συ νυλλα δωσενδι δεφινιτιωνες φελ. ΔωλοÏε δισεÏετ ÏεφοÏμιδανς αδ Ï€Ïω. ΕφεÏτι Ï€Ïωβατυς Ï…Ïβανιθας νο μελ. Ιν φιξ φασεθε δεθÏαξιθ ομιθταντυÏ, ζÏιλ υτιναμ παθÏιοκυε συ νες. Κυο ει δισενθιετ ασομμοδαÏε. Ηας θε ομνεσκυε δελισαθισιμι. ΕξεÏσι δελισατα ινιμισυς ευμ ευ, ιδ ÎµÎ»Î¹Ï„Ï Î¼ÎµÎ»Î¹Î¿Ïε αβχοÏÏεανθ εσθ, εως οπθιων Ï€Ïοδεσεθ ÏƒÎ¿Î½ÏƒÎµÏƒÎ¸ÎµÎ¸Ï…ÎµÏ Î¹Î½. Îαμ διαμ ασυμ τεμποÏιβυς αν. Σομμυνε δεφινιθιονεμ κυο ιν, ηας νωμιναφι φιφενδυμ ατ. Ομνεσκυε δεφινιεβας μεα θε. Εαμ σανστυς αλβυσιυς ευ, φελ στετ επισυÏι ιν, κυο αδ πεÏτιναξ σενσεÏιτ τωÏκυαθος. ΛαβωÏε νυσκυαμ ιν κυι, εÏος σαεπε τιβικυε εσθ ατ. ΦεÏο υτιναμ φελ νε, αδ απεÏιÏι Î¿Î¼Î¹Î¸Ï„Î±Î½Ï„Ï…Ï Î´ÎµÏ†Î¹Î½Î¹Ï„Î¹Ï‰Î½ÎµÏ‚ δυο. ΙνφενιÏε ελειφενδ παθÏιοκυε εξ ναμ. Ιδ ναμ μινιμ υθÏοκυε. Αδ ναθυμ αππετεÏε σεα. Μολλις φολυμυς κυι νο, θε φιμ υβικυε αδιπισι διγνισιμ. Îοβις νοσθÏω μενανδÏι υσυ νο, Ï€Ïιμα ÎµÎ»Î¹Ï„Ï ÎºÏ…Î±ÎµÎºÏ…Îµ ιδ ηας. Î Ïω εα παÏτεμ δομινγ. Θε φασεθε αυδιÏε φολυπθατιβυς ιυς. Φις δεθÏαξιθ ινφενιÏε ετ, αν ιυς πωσθεα μεδιοσÏιθαθεμ. Εα αδχυς Ï…Ï„Î±Î¼Ï…Ï Ï†Î¹Ï‚. Σιβω λαυδεμ υσυ αδ, φις λεγιμυς πλασεÏαθ φεÏθεÏεμ συ. Φιμ ατ ειυς αλθεÏυμ φιθυπεÏατοÏιβυς, ατ λατινε ηαβεμυς φολυτπατ μεα. ΓÏαεσω λυσιλιυς εα φελ. Θε φιξ βÏυτε συμμο, φελ ωμιτθαμ ιμπεÏδιετ εξ. Μεα ιν μωδω νυμκυαμ, σεα Ï„Ïασθατος εξπετενδα αδ. ΓÏαεσε πλαθονεμ Ïεπυδιανδαε φιξ εα, εα ετιαμ σωνσθιτυθο ασυεφεÏιθ σιθ. Ατ πυÏθο ναθυμ σονγυε φιξ, κυι ετ δισαμ ινεÏμις ινιμισυς. Î ÎµÏ Ï…Î¸ διστα ινθεγÏε, Ï€ÎµÏ Ïεκυε φιεÏενθ αδ. Îε δεσεÏυντ ινφενιÏε ÏƒÏ‰Î½ÏƒÎµÎ¸ÎµÎ¸Ï…Ï Î¼ÎµÎ¹, αν ηομεÏω αÏγυμενθυμ Ïεπυδιανδαε πεÏ, ηις σωνσυλ μελιοÏε ινθελλεγαμ υθ. Îες εα Î»Î±Î²Î¹Î¸Ï…Ï Î´Î¿Î»Î¿Ïεμ υλλαμσοÏπεÏ. Μει εσεντ νεσεσιταθιβυς ιν, αφφεÏθ σαυσαε ινθεÏεσετ ηας αν. |
Added test/Greek-Lipsum-2.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 | Κυο εξ υνυμ δισπυθανδο, εÏος αλιενυμ κυι θε. Îες εξ ελωκυενθιαμ ινστÏυσθιοÏ. Î˜ÎµÎ¼Ï€Î¿Ï Î½Î¿ÏƒÎ¸ÎµÏ ÏƒÏ… εως. Î Ï…Ïθο μωφεθ μωδεÏατιυς ατ μελ. Συ δυο αμετ ειυς. Î Ïι δεσωÏε ινθεγÏε ασυμσαν αδ, Φιξ αν Ïεβυμ ÎµÏ†Ï†Î¹ÏƒÎ¹Î±Î½Î¸Ï…Ï Î½ÎµÏƒÎµÏƒÎ¹Ï„Î±Î¸Î¹Î²Ï…Ï‚. ÎοσθÏυμ ÏƒÏ…ÏƒÎ¹Ï€Î¹Î±Î½Ï„Ï…Ï Î·Î±Ï‚ ει, οÏναθυς Ïεσυσαβο Ï€Ïι ιδ, Ï€ÎµÏ Î½Î¿Î»Ï…Î¹ÏƒÎµ οπωÏθεÏε ιδ. Θε παÏτιενδω πεÏτινασια ινσωÏÏυπτε φις. Δισθας φαβυλας γυβεÏγÏεν εως ιν, αλιι σολυμ ηις θε, ποσθυλανθ ασυσαμυς ετ ηας. Îο ινανι φαβυλας θχεωπηÏαστυς ναμ, ευμ διστα ηομεÏω εα. Μαγνα φυγιθ υθ πεÏ, εσθ ατ νοσθÏυμ δεσεÏυισε. Φις αυδιαμ λαβοÏες παθÏιοκυε εξ, ετ φευγιαθ δεφινιεβας σιθ. Αμετ εÏιπυιτ δελισατα υσυ ετ, σενσιβυς φολυπθατιβυς Ï€ÎµÏ ÎµÎ¾. Κυωδ ιγνωθα τιβικυε ατ εαμ, νυλλα ηωνεσθαθις υθ νες. Φιξ αν μυτατ εξεÏσι λαβωÏε. Σεδ νονυμυ κυοδσι δελενιτ νε, συμο φιδε εα κυι. Ποπυλω μαιοÏυμ πεÏσεκυεÏις αν Ï€Ïω. Σολυμ σωνφενιÏε αδ ηας, αν ευμ σολυτα Ïεγιονε Ï€Ïοδεσεθ. ΦεÏο λαβοÏες σαλυταθυς θε δυο, ηις νε φεÏο βλανδιτ Ï€Ïαεσενθ, ιδ φις σολεατ φιφενδυμ. Συ συμ μωδω συμμο δολοÏες. Θε ναμ πωσιθ φευγιαθ τινσιδυνθ. Υθ ιψυμ νεμωÏε σαπιενθεμ μεα, ει εφεÏτι εφφισιενδι ηας. Ευμ αλβυσιυς Ï€Ïαεσενθ συ, δεσωÏε σεθεÏο ινδοστυμ μει ει. Ηις υθ συμμο μαλοÏυμ μανδαμυς, κυι ιν συαφιθαθε πεÏισυλις, ιισκυε οφφισιις κυο νο. Îε νονυμυ ηαβεμυς πχιλωσοπηια φις. Ετ ηας Ï…Ï„Î±Î¼Ï…Ï ÏεφοÏμιδανς. ΙνεÏμις δεθÏαξιθ Î½ÎµÎ³Î»ÎµÎ³ÎµÎ½Î¸Ï…Ï Î´Ï…Î¿ υθ, τωÏκυαθος δισεντιυνθ φιθυπεÏατοÏιβυς φιξ νε. Εα σεδ συας μελιυς, φιμ Ï€Ïοβο ινδοστυμ ÏεπÏιμικυε ευ. Î Ïι ιν λυδυς αυδιÏε, συμμο πεÏτινασια ÏƒÏ‰Î½ÏƒÎµÎ¸ÎµÎ¸Ï…Ï Ï†Î¹Ï‚ ιν, σιθ εξ επισυÏι μαλυισετ σωνσεπθαμ. Αν δετÏασθο ελειφενδ εξπλισαÏι Ï€Ïω. Ιυδισο σομμοδο συμ αδ. Δισαμ δισυντ φυλπυτατε ιν Ï€Ïω, εξ ηις δελενιτ μαιεσθατις. Ρεβυμ νονυμυ αππαÏεατ σιθ εα, σιθ ιδ νυλλα σολεατ πεθενθιυμ, ει οπθιων πεÏσεκυεÏις ευμ. Υθ νισλ ινσωλενς φιξ, εσθ φεÏι ιισκυε αÏγυμενθυμ συ, σεθεÏο μολεστιε αδιπισινγ ευ μεα. Ετ μεα μυσιυς λατινε, μει ÏƒÎµÎ¼Ï€ÎµÏ Î´ÎµÏƒÎµÏυντ πεÏτινασια αν. Συ φενιαμ ποπυλω αθωμωÏυμ κυο. Îο ιυς Ïεβυμ φιθυπεÏαθα δισπυτατιονι, ατ αλθεÏυμ χενδÏεÏιτ φιθυπεÏαθα συμ. Ευμ αυτεμ αππετεÏε αδιπισινγ ετ, νο κυο συας ελειφενδ. Εαμ θαλε δισαμ εξ. Ετ σομμοδο λεγενδως φελ, διαμ φωλυπθαÏια νο μελ, δυο φελιτ νεμωÏε αδ. Αν εξπετενδα συαφιθαθε φελ, ενιμ ασυμσαν Ï€ÎµÏ Î±Î´, εα φιμ μωδω υνυμ. Εα κυωδ Ï€Ïοβο πεÏσεσυτι φελ, ευ φεÏι Ï€ÏωπÏιαε ινσιδεÏιντ νες. Εξ νες οδιο δελενιτ, ελιτ ιυδισο ινθεγÏε δυο ιδ. Μελ αλικυιπ πεÏισυλις ετ, ατ ηας αυγυε λαβοÏες ασεντιοÏ. Συ νυλλα δωσενδι δεφινιτιωνες φελ. ΔωλοÏε δισεÏετ ÏεφοÏμιδανς αδ Ï€Ïω. ΕφεÏτι Ï€Ïωβατυς Ï…Ïβανιθας νο μελ. Ιν φιξ φασεθε δεθÏαξιθ ομιθταντυÏ, ζÏιλ υτιναμ παθÏιοκυε συ νες. Κυο ει δισενθιετ ασομμοδαÏε. Ηας θε ομνεσκυε δελισαθισιμι. ΕξεÏσι δελισατα ινιμισυς ευμ ευ, ιδ ÎµÎ»Î¹Ï„Ï Î¼ÎµÎ»Î¹Î¿Ïε αβχοÏÏεανθ εσθ, εως οπθιων Ï€Ïοδεσεθ ÏƒÎ¿Î½ÏƒÎµÏƒÎ¸ÎµÎ¸Ï…ÎµÏ Î¹Î½. Îαμ διαμ ασυμ τεμποÏιβυς αν. Σομμυνε δεφινιθιονεμ κυο ιν, ηας νωμιναφι φιφενδυμ ατ. Ομνεσκυε δεφινιεβας μεα θε. Εαμ σανστυς αλβυσιυς ευ, φελ στετ επισυÏι ιν, κυο αδ πεÏτιναξ σενσεÏιτ τωÏκυαθος. ΛαβωÏε νυσκυαμ ιν κυι, εÏος σαεπε τιβικυε εσθ ατ. ΦεÏο υτιναμ φελ νε, αδ απεÏιÏι Î¿Î¼Î¹Î¸Ï„Î±Î½Ï„Ï…Ï Î´ÎµÏ†Î¹Î½Î¹Ï„Î¹Ï‰Î½ÎµÏ‚ δυο. ΙνφενιÏε ελειφενδ παθÏιοκυε εξ ναμ. Ιδ ναμ μινιμ υθÏοκυε. Αδ ναθυμ αππετεÏε σεα. Μολλις φολυμυς κυι νο, θε φιμ υβικυε αδιπισι διγνισιμ. Îοβις νοσθÏω μενανδÏι υσυ νο, Ï€Ïιμα ÎµÎ»Î¹Ï„Ï ÎºÏ…Î±ÎµÎºÏ…Îµ ιδ ηας. Î Ïω εα παÏτεμ δομινγ. Θε φασεθε αυδιÏε φολυπθατιβυς ιυς. Φις δεθÏαξιθ ινφενιÏε ετ, αν ιυς πωσθεα μεδιοσÏιθαθεμ. Εα αδχυς Ï…Ï„Î±Î¼Ï…Ï Ï†Î¹Ï‚. Σιβω λαυδεμ υσυ αδ, φις λεγιμυς πλασεÏαθ φεÏθεÏεμ συ. Φιμ ατ ειυς αλθεÏυμ φιθυπεÏατοÏιβυς, ατ λατινε ηαβεμυς φολυτπατ μεα. ΓÏαεσω λυσιλιυς εα φελ. Θε φιξ βÏυτε συμμο, φελ ωμιτθαμ ιμπεÏδιετ εξ. Μεα ιν μωδω νυμκυαμ, σεα Ï„Ïασθατος εξπετενδα αδ. ΓÏαεσε πλαθονεμ Ïεπυδιανδαε φιξ εα, εα ετιαμ σωνσθιτυθο ασυεφεÏιθ σιθ. Ατ πυÏθο ναθυμ σονγυε φιξ, κυι ετ δισαμ ινεÏμις ινιμισυς. Î ÎµÏ Ï…Î¸ διστα ινθεγÏε, Ï€ÎµÏ Ïεκυε φιεÏενθ αδ. Îε δεσεÏυντ ινφενιÏε ÏƒÏ‰Î½ÏƒÎµÎ¸ÎµÎ¸Ï…Ï Î¼ÎµÎ¹, αν ηομεÏω αÏγυμενθυμ Ïεπυδιανδαε πεÏ, ηις σωνσυλ μελιοÏε ινθελλεγαμ υθ. Îες εα Î»Î±Î²Î¹Î¸Ï…Ï Î´Î¿Î»Î¿Ïεμ υλλαμσοÏπεÏ. Μει εσεντ νεσεσιταθιβυς ιν, αφφεÏθ σαυσαε ινθεÏεσετ ηας αν. |
Added test/amend.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 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 | # # 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/ # ############################################################################ # # Tests for the "amend" command. # proc artifact_from_timeline {res var} { upvar $var artid regexp {(?x)[0-9]{2}(?::[0-9]{2}){2}\s+\[([0-9a-f]+)]} $res m artid } proc manifest_comment {comment} { string map [list { } {\\s} \n {\\n} \r {\\r}] $comment } proc uuid_from_commit {res var} { upvar $var HASH regexp {^New_Version: ([0-9a-f]{40})[0-9a-f]*$} $res m HASH } proc uuid_from_branch {res var} { upvar $var HASH regexp {^New branch: ([0-9a-f]{40})[0-9a-f]*$} $res m HASH } proc uuid_from_checkout {var} { global RESULT upvar $var HASH fossil status regexp {checkout:\s+([0-9a-f]{40})[0-9a-f]*} $RESULT m HASH } # Make sure we are not in an open repository and initialize new repository test_setup ######################################## # Setup: Add file and commit # ######################################## if {![uuid_from_checkout HASHINIT]} { test amend-checkout-failure false test_cleanup_then_return } write_file datafile "data" fossil add datafile fossil commit -m "c1" if {![uuid_from_commit $RESULT HASH]} { test amend-setup-failure false test_cleanup_then_return } ######################################## # Test: -branch # ######################################## set HASHB HASHB write_file datafile "data.file" fossil commit -m "c2" if {![uuid_from_commit $RESULT HASHB]} { test amend-branch.setup false } fossil amend $HASHB -branch amended-branch test amend-branch-1.1 {[regexp {tags:\s+amended-branch} $RESULT]} fossil branch ls test amend-branch-1.2 {[string first "* amended-branch" $RESULT] != -1} fossil tag list test amend-branch-1.3 {[string first amended-branch $RESULT] != -1} fossil tag list --raw $HASHB test amend-branch-1.4 {[string first "branch=amended-branch" $RESULT] != -1} test amend-branch-1.5 {[string first "sym-amended-branch" $RESULT] != -1} fossil timeline -n 1 test amend-branch-1.6 {[string match {*Move*to*branch*amended-branch*} $RESULT]} ######################################## # Test: -bgcolor # ######################################## set tc 0 foreach {color result} { 0 0 a a abcdef #abcdef abc123 #abc123 123efg 123efg abcdefg abcdefg abcdeg abcdeg blue blue acf #acf 123 #123 #1234 #1234 1234 1234 123456 #123456 } { incr tc fossil amend $HASH -bgcolor $color test amend-bgcolor-1.$tc.a {[string match "*hash:*$HASH*" $RESULT]} fossil tag list --raw $HASH test amend-bgcolor-1.$tc.b {[string first "bgcolor=$result" $RESULT] != -1} fossil timeline -n 1 test amend-bgcolor-1.$tc.c { [string match "*Change*background*color*to*\"$result\"*" $RESULT] } if {[artifact_from_timeline $RESULT artid]} { fossil artifact $artid test amend-bgcolor-1.$tc.d { [string match "*T +bgcolor $HASH* $result*" $RESULT] } } else { if {$VERBOSE} { protOut "No artifact found in timeline output" } test amend-bgcolor-1.$tc.d false } } fossil amend $HASH -bgcolor {} test amend-bgcolor-2.1 {[string match "*hash:*$HASH*" $RESULT]} fossil tag list --raw $HASH test amend-bgcolor-2.2 { [string first "bgcolor=" $RESULT] == -1 && [string first "bgcolor" $RESULT] != -1 } fossil timeline -n 1 test amend-bgcolor-2.3 {[string match "*Cancel*background*color.*" $RESULT]} if {[artifact_from_timeline $RESULT artid]} { fossil artifact $artid test amend-bgcolor-2.4 {[string match "*T -bgcolor $HASH*" $RESULT]} } else { if {$VERBOSE} { protOut "No artifact found in timeline output" } test amend-bgcolor-2.4 false } ######################################## # Test: -branchcolor # ######################################## set HASH2 HASH2 fossil branch new brclr $HASH if {![uuid_from_branch $RESULT HASH2]} { test amend-branchcolor.setup false } fossil update $HASH2 fossil amend $HASH2 -branchcolor yellow test amend-branchcolor-1.1 {[string match "*hash:*$HASH2*" $RESULT]} fossil tag ls --raw $HASH2 test amend-branchcolor-1.2 {[string first "bgcolor=yellow" $RESULT] != -1} fossil timeline -n 1 test amend-branchcolor-1.3 { [string match {*Change*branch*background*color*to*"yellow".*} $RESULT] } if {[regexp {(?x)[0-9]{2}(?::[0-9]{2}){2}\s+\[([0-9a-f]+)]} $RESULT m artid]} { fossil artifact $artid test amend-branchcolor-1.4 { [string match "*T \*bgcolor $HASH2* yellow*" $RESULT] } } else { if {$VERBOSE} { protOut "No artifact found in timeline output" } test amend-branchcolor-1.4 false } set HASHN HASHN write_file datafile "brclr" fossil commit -m "brclr" if {![uuid_from_commit $RESULT HASHN]} { test amend-branchcolor-propagating.setup false } write_file datafile "bc1" fossil commit -m "mc1" write_file datafile "bc2" fossil commit -m "mc2" fossil amend $HASHN -branchcolor deadbe test amend-branchcolor-2.1 {[string match "*hash:*$HASHN*" $RESULT]} fossil tag ls --raw current test amend-branchcolor-2.2 {[string first "bgcolor=#deadbe" $RESULT] != -1} fossil timeline -n 1 test amend-branchcolor-2.3 { [string match {*Change*branch*background*color*to*"#deadbe".*} $RESULT] } ######################################## # Test: -author # ######################################## fossil amend $HASH -author author-test test amend-author-1.1 {[string match {*comment:*(user:*author-test)*} $RESULT]} fossil tag ls --raw $HASH test amend-author-1.2 {[string first "user=author-test" $RESULT] != -1} fossil timeline -n 1 test amend-author-1.3 {[string match {*Change*user*to*"author-test".*} $RESULT]} ######################################## # Test: -date # ######################################## set timestamp [clock scan yesterday] set date [clock format $timestamp -format "%Y-%m-%d" -gmt 1] set time [clock format $timestamp -format "%H:%M:%S" -gmt 1] set datetime "$date $time" fossil amend $HASHINIT -date $datetime test amend-date-1.1 {[string match "*hash:*$HASHINIT*$datetime*" $RESULT]} fossil tag ls --raw $HASHINIT test amend-date-1.2 {[string first "date=$datetime" $RESULT] != -1} fossil timeline -n 1 test amend-date-1.3 {[string match "*Timestamp*$date*$time*" $RESULT]} set badformats { "%+" "%Y-%m-%d %H:%M%:%S %Z" "%d/%m/%Y %H:%M%:%S %Z" "%d/%m/%Y %H:%M%:%S" "%d/%m/%Y" } set sc 0 foreach badformat $badformats { incr sc set datetime [clock format $timestamp -format $badformat -gmt 1] fossil amend $HASHINIT -date $datetime -expectError test amend-date-2.$sc {[string first "YYYY-MM-DD HH:MM:SS" $RESULT] != -1} } ######################################## # Test: -hide # ######################################## set HASHH HASHH fossil revert fossil update trunk fossil branch new tohide current if {![uuid_from_branch $RESULT HASHH]} { test amend-hide-setup false } fossil amend $HASHH -hide test amend-hide-1.1 {[string match "*hash:*$HASHH*" $RESULT]} fossil tag ls --raw $HASHH test amend-hide-1.2 {[string first "hidden" $RESULT] != -1} fossil timeline -n 1 test amend-hide-1.3 {[string match {*Add*propagating*"hidden".*} $RESULT]} ######################################## # Test: -close # ######################################## set HASHC HASHC fossil branch new cllf $HASH if {![uuid_from_branch $RESULT HASHC]} { test amend-close.setup false } fossil update $HASHC fossil amend $HASHC -close test amend-close-1.1.a {[string match "*hash:*$HASHC*" $RESULT]} test amend-close-1.1.b { [string match "*comment:*Create*new*branch*named*\"cllf\"*" $RESULT] } fossil tag ls --raw $HASHC test amend-close-1.2 {[string first "closed" $RESULT] != -1} fossil timeline -n 1 test amend-close-1.3 {[string match {*Mark*"Closed".*} $RESULT]} write_file datafile "cllf" fossil commit -m "should fail" -expectError test amend-close-2 {[string first "closed leaf" $RESULT] != -1} set HASH3 HASH3 fossil revert fossil update trunk write_file datafile "cb" fossil commit -m "closed-branch" --branch "closebranch" if {![uuid_from_commit $RESULT HASH3]} { test amend-close-3.setup false } write_file datafile "b1" fossil commit -m "m1" write_file datafile "b2" fossil commit -m "m2" fossil amend $HASH3 --close test amend-close-3.1 {[string match "*hash:*$HASH3*" $RESULT]} fossil tag ls --raw current test amend-close-3.2 {[string first "closed" $RESULT] != -1} fossil timeline -n 1 test amend-close-3.3 { [string match "*Add*propagating*\"closed\".*" $RESULT] } write_file datafile "changed" fossil commit -m "should fail" -expectError test amend-close-3.4 {[string first "closed leaf" $RESULT] != -1} ######################################## # Test: -tag/-cancel # ######################################## set tagtests { tagged tagged {000000 lower Upper alpha 0alpha} {000000 0alpha Upper alpha lower} } set tc 0 foreach {tagt result} $tagtests { incr tc set tags {} set cancels {} set t1exp "" set t2exp "*" set t3exp "*" set t5exp "*" foreach tag $tagt { lappend tags -tag $tag lappend cancels -cancel $tag } foreach res $result { append t1exp ", $res" append t3exp "Add*tag*\"$res\".*" append t5exp "Cancel*tag*\"$res\".*" } foreach res [lsort -nocase $result] { append t2exp "sym-$res*" } eval fossil amend $HASH $tags test amend-tag-$tc.1 {[string match "*hash:*$HASH*tags:*$t1exp*" $RESULT]} fossil tag ls --raw $HASH test amend-tag-$tc.2 {[string match $t2exp $RESULT]} fossil timeline -n 1 test amend-tag-$tc.3 {[string match $t3exp $RESULT]} eval fossil amend $HASH $cancels test amend-tag-$tc.4 {![string match "*tags:*$t1exp*" $RESULT]} fossil timeline -n 1 test amend-tag-$tc.5 {[string match $t5exp $RESULT]} } ######################################## # Test: -comment # ######################################## proc prep-test {comment content} { global HASH RESULT fossil revert fossil update trunk write_file datafile $comment fossil commit -m $content if {![uuid_from_commit $RESULT HASH]} { set HASH "" } } proc test-comment {name HASH comment} { global VERBOSE RESULT test amend-comment-$name.1 { [string match "*hash:*$HASH*comment:*$comment*" $RESULT] } fossil timeline -n 1 if {[artifact_from_timeline $RESULT artid]} { fossil artifact $artid test amend-comment-$name.2 { [string match "*T +comment $HASH* *[manifest_comment $comment]*" $RESULT] } } else { if {$VERBOSE} { protOut "No artifact found in timeline output: $RESULT" } test amend-comment-$name.2 false } fossil timeline -n 1 test amend-comment-$name.3 { [string match "*[short_uuid $HASH]*Edit*check-in*comment.*" $RESULT] } fossil info $HASH test amend-comment-$name.4 { [string match "*hash:*$HASH*comment:*$comment*" $RESULT] } } prep-test "revision 1" "revision 1" fossil amend $HASH -comment "revised revision 1" test-comment 1 $HASH "revised revision 1" prep-test "revision 2" "revision 2" fossil amend $HASH -m "revised revision 2 with -m" test-comment 2 $HASH "revised revision 2 with -m" prep-test "revision 3" "revision 3" write_file commitmsg "revision 3 revised" fossil amend $HASH -message-file commitmsg test-comment 3 $HASH "revision 3 revised" prep-test "revision 4" "revision 4" write_file commitmsg "revision 4 revised with -M" fossil amend $HASH -M commitmsg test-comment 4 $HASH "revision 4 revised with -M" prep-test "final comment" "final content" if {[catch {exec which ed} result] == 0} { fossil settings editor "ed -s" set comment "interactive edited comment" fossil_maybe_answer "a\n$comment\n.\nw\nq\n" amend $HASH --edit-comment test-comment 5 $HASH $comment } ######################################## # Test: NULL hash # ######################################## fossil amend {} -close -expectError test amend-null-uuid {$CODE && [string first "no such check-in" $RESULT] != -1} ############################################################################### test_cleanup |
Added test/clean.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 | # # 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/ # ############################################################################ # # Tests of the "clean" command, including the ability to undo it. # test_setup ############################################################################### fossil extra test clean-0 {[normalize_result] eq {}} ############################################################################### write_file f1 "f1 line" fossil add f1 fossil commit -m "base file" fossil ls test clean-1 {[normalize_result] eq {f1}} ############################################################################### fossil extra test clean-2 {[normalize_result] eq {}} ############################################################################### write_file f2 "f2 line" fossil extra test clean-3 {[normalize_result] eq {f2}} ############################################################################### # clean w/undo enabled, should not prompt, 1 file < 10MiB fossil clean test clean-4 {[normalize_result] eq \ {"fossil undo" is available to undo changes to the working checkout.}} ############################################################################### fossil extra test clean-5 {[normalize_result] eq {}} ############################################################################### fossil undo test clean-6 {[normalize_result] eq {NEW f2}} test clean-7 {[read_file f2] eq "f2 line"} ############################################################################### fossil extra test clean-8 {[normalize_result] eq {f2}} ############################################################################### write_file f3 [string repeat ABCDEFGHIJK 1048576] fossil extra test clean-9 {[normalize_result] eq {f2 f3}} ############################################################################### # clean w/undo enabled, no prompt, 1 file < 10MiB, 1 file > 10MiB fossil clean --no-prompt test clean-10 {[normalize_result] eq \ {"fossil undo" is available to undo changes to the working checkout.}} ############################################################################### fossil extra test clean-11 {[normalize_result] eq {f3}} ############################################################################### fossil undo test clean-12 {[normalize_result] eq {NEW f2}} test clean-13 {[read_file f2] eq "f2 line"} test clean-14 {[read_file f3] eq [string repeat ABCDEFGHIJK 1048576]} ############################################################################### fossil extra test clean-15 {[normalize_result] eq {f2 f3}} ############################################################################### # clean w/undo enabled, force, 1 file < 10MiB, 1 file > 10MiB fossil clean --force test clean-16 {[normalize_result] eq \ {"fossil undo" is available to undo changes to the working checkout.}} ############################################################################### fossil extra test clean-17 {[normalize_result] eq {}} ############################################################################### fossil undo test clean-18 {[normalize_result] eq {NEW f2}} test clean-19 {[read_file f2] eq "f2 line"} ############################################################################### write_file f4 [string repeat KJIHGFEDCBA 1048576] fossil extra test clean-20 {[normalize_result] eq {f2 f4}} ############################################################################### # clean w/undo disabled, no prompt, 1 file < 10MiB, 1 file > 10MiB fossil clean --disable-undo --no-prompt test clean-21 {[normalize_result] eq {}} ############################################################################### fossil extra test clean-22 {[normalize_result] eq {f2 f4}} ############################################################################### fossil undo -expectError test clean-23 {[normalize_result] eq {nothing to undo}} ############################################################################### # clean w/undo disabled, force, 1 file < 10MiB, 1 file > 10MiB fossil clean --disable-undo --force test clean-24 {[normalize_result] eq {}} ############################################################################### fossil extra test clean-25 {[normalize_result] eq {}} ############################################################################### fossil undo -expectError test clean-26 {[normalize_result] eq {nothing to undo}} ############################################################################### write_file f5 "f5 line" fossil extra test clean-27 {[normalize_result] eq {f5}} ############################################################################### # clean w/undo disabled, should prompt, 1 file < 10MiB fossil_maybe_answer Y clean --disable-undo test clean-28 {[normalize_result] eq \ {WARNING: Deletion of this file will not be undoable via the 'undo' command because undo is disabled for this operation. Remove unmanaged file "f5" (a=all/y/N)?}} ############################################################################### fossil extra test clean-29 {[normalize_result] eq {}} ############################################################################### fossil undo -expectError test clean-30 {[normalize_result] eq {nothing to undo}} ############################################################################### fossil extra test clean-31 {[normalize_result] eq {}} ############################################################################### test_cleanup |
Added test/cmdline.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 | # # Copyright (c) 2012 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 command line parsing # test_setup "" proc cmd-line {usefile testname args} { set i 1 foreach {cmdline result} $args { if {$usefile} { set cmdlinefile [file join \ [getTemporaryPath] fossil-cmd-line-$testname.txt] write_file $cmdlinefile $cmdline fossil test-echo --args $cmdlinefile file delete $cmdlinefile } else { fossil test-echo $cmdline } test cmd-line-$testname.$i \ {[lrange [split $::RESULT \n] 3 end]=="\{argv\[2\] = \[$result\]\}"} incr i } } cmd-line false 100 abc abc a\"bc a\"bc \"abc\" \"abc\" # # NOTE: Use an --args file on Windows to avoid unwanted glob expansion # from MinGW and/or the MSVCRT. # cmd-line $is_windows 101 * * *.* *.* ############################################################################### test_cleanup |
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 319 320 321 322 323 324 325 326 | # # 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. # test_setup "" ############################################################################### 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)"} ############################################################################### test_cleanup |
Added test/commit-warning.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 | # # Copyright (c) 2016 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/ # ############################################################################ # # The focus of this file is to test pre-commit warnings. # test_setup # binary write_file binary "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" # text with CRLF lines write_file crlf.txt [subst -nocommands -novariables \ {ordinary text\r cariage returns and line feeds\r on several lines\r\n}] # text with mixed line endings write_file cr-only.txt "AAA\rBBB\rCCC\r" write_file cr-lf-crlf.txt "AAA\rBBB\nCCC\r\n" # unix plain text includes the empty file by generalization write_file empty "" write_file plain.txt { Lorem ipsum dolor sic amet } # try long lines set a3 "abcdefgh" set a4 $a3$a3 set a5 $a4$a4 set a6 $a5$a5 write_file line-0064 "$a6\n" set a7 $a6$a6 set a8 $a7$a7 set a9 $a8$a8 set a10 $a9$a9 write_file line-1024 "$a10\n" set a11 $a10$a10 write_file line-2048 "$a11\n" set a12 $a11$a11 write_file line-4096 "$a12\n" set a13 $a12$a12 write_file line-8192 "$a13\n" set a14 $a13$a13 write_file line-16K "$a14\n" set a15 $a14$a14 write_file line-32K "$a15\n" set a16 $a15$a15 write_file line-64K "$a16\n" # UTF-8 extends 7-bit ASCII using bytes 80 and above to encode # larger character codes. Unicode uses U+0 through U+10FFFF only, # with U+D800 through U+DFFF reserved for surrogate pairs. # UTF-8 is valid if it is the shortest possible coding, encodes a # valid Unicode code point. But it's complicated. write_file utf-mod-c0-80 "11 bit NUL:\xC0\x80 is sometimes ok\n" write_file utf-bad-e0-80-80 "16 bit NUL:\xE0\x80\x80 is bad\n" write_file utf-bad-f0-80-80-80 "21 bit NUL:\xF0\x80\x80\x80 is bad\n" write_file utf-bad-f8-80-80-80-80 "26 bit NUL:\xF8\x80\x80\x80\x80 is bad\n" write_file utf-bad-fc-80-80-80-80-80 "31 bit NUL:\xFC\x80\x80\x80\x80\x80 is bad\n" write_file utf-bad-fe-80-80-80-80-80-80 "36 bit NUL:\xFC\x80\x80\x80\x80\x80 is bad\n" write_file utf-bad-c0-81 "overlong SOH:\xC0\x81 is bad\n" write_file utf-bad-c0-bf "overlong '?':\xC0\xBF is bad\n" write_file utf-bad-c1-bf "overlong DEL:\xC1\xBF is bad\n" write_file utf-bad-f4-90-80-80 "U+110000 not allowed:\xF4\x90\x80 not unicode\n" write_file utf-bad-f9-80-80-80-80 "U+2000000 not allowed:\xF9\x80\x80\x80\x80 not unicode\n" write_file utf-bad-ff "no byte FF:\xFF\n" write_file utf-ill16-lead "lead surrogate U+D800:\xED\xA0\x80 is ill formed\n" write_file utf-ill16-trail "trail surrogate U+DC00:\xED\xB0\x80 is ill formed\n" write_file utf-ill16-pair "surrogate pair U+10000:\xED\xA0\x80\xED\xB0\x80 is ill formed\n" set emoji "micro-smile \xC2\xB5\xE2\x98\xBA\npale facepalm \xF0\x9F\xA4\xA6\xF0\x9F\x8F\xBB\n" protOut $emoji write_file utf-8-emoji $emoji write_file utf-8-bom-emoji "\xef\xbb\xbf$emoji" # UTF-16 uses 16-bit values to cover all valid unicode code points # from U+0 to U+10FFFF, using surrogate pairs to escape the BMP. # Interchange require knowing (and preserving) byte order. set hello16LE "h\x00e\x00l\x00l\x00o\x00\n\x00" set hello16BE "\x00h\x00e\x00l\x00l\x00o\x00\n" write_file utf-16le-hello $hello16LE write_file utf-16be-hello $hello16BE set bomLE "\xff\xfe" set bomBE "\xfe\xff" write_file utf-16le-bomle-hello "$bomLE$hello16LE" write_file utf-16be-bombe-hello "$bomBE$hello16BE" write_file utf-16le-bombe-hello "$bomBE$hello16LE" write_file utf-16be-bomle-hello "$bomLE$hello16BE" set le16 [read_file [file join $testdir utf16le.txt]] set be16 [read_file [file join $testdir utf16be.txt]] write_file utf-16le.txt $le16 write_file utf-16be.txt $be16 write_file utf-nobom-16le.txt [string range $le16 2 end] write_file utf-nobom-16be.txt [string range $be16 2 end] #write_file [file join $::env(TEMP) utf-nobom-16le.txt] [string range $le16 2 end] #write_file [file join $::env(TEMP) utf-nobom-16be.txt] [string range $be16 2 end] # make all the test files known to fossil, then test fossil addremove fossil test-commit-warning --no-settings -v test pre-commit-warnings-1 {[normalize_result] eq \ [subst -nocommands -novariables [string trim { 1\tbinary\tbinary data 1\tcr-lf-crlf.txt\tmixed line endings 1\tcr-only.txt\tCR line endings 1\tcrlf.txt\tCR/LF line endings 0\tempty\t 0\tline-0064\t 0\tline-1024\t 0\tline-16K\t 0\tline-2048\t 1\tline-32K\tlong lines 0\tline-4096\t 1\tline-64K\tlong lines 0\tline-8192\t 0\tplain.txt\t 1\tutf-16be-bombe-hello\tUnicode 1\tutf-16be-bomle-hello\tUnicode 1\tutf-16be-hello\tbinary data 1\tutf-16be.txt\tUnicode 1\tutf-16le-bombe-hello\tUnicode 1\tutf-16le-bomle-hello\tUnicode 1\tutf-16le-hello\tbinary data 1\tutf-16le.txt\tUnicode 0\tutf-8-bom-emoji\t 0\tutf-8-emoji\t 1\tutf-bad-c0-81\tinvalid UTF-8 1\tutf-bad-c0-bf\tinvalid UTF-8 1\tutf-bad-c1-bf\tinvalid UTF-8 1\tutf-bad-e0-80-80\tinvalid UTF-8 1\tutf-bad-f0-80-80-80\tinvalid UTF-8 1\tutf-bad-f4-90-80-80\tinvalid UTF-8 1\tutf-bad-f8-80-80-80-80\tinvalid UTF-8 1\tutf-bad-f9-80-80-80-80\tinvalid UTF-8 1\tutf-bad-fc-80-80-80-80-80\tinvalid UTF-8 1\tutf-bad-fe-80-80-80-80-80-80\tinvalid UTF-8 1\tutf-bad-ff\tinvalid UTF-8 0\tutf-ill16-lead\t 0\tutf-ill16-pair\t 0\tutf-ill16-trail\t 0\tutf-mod-c0-80\t 1\tutf-nobom-16be.txt\tbinary data 1\tutf-nobom-16le.txt\tbinary data 1}]]} ############################################################################### # TODO: Change to a collection of test-case crafted files # rather than depend on this list of files that will # be fragile as development progresses. # # Unless the real goal of this test is to document a collection # of source files that MUST NEVER BE TEXT. # test_block_in_checkout pre-commit-warnings-fossil-1 { fossil test-commit-warning --no-settings } { test pre-commit-warnings-fossil-1 {[normalize_result] eq \ [subst -nocommands -novariables [string trim { 1\tcompat/zlib/contrib/blast/test.pk\tbinary data 1\tcompat/zlib/contrib/dotzlib/DotZLib.build\tCR/LF line endings 1\tcompat/zlib/contrib/dotzlib/DotZLib.chm\tbinary data 1\tcompat/zlib/contrib/dotzlib/DotZLib.sln\tCR/LF line endings 1\tcompat/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs\tCR/LF line endings 1\tcompat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs\tinvalid UTF-8 1\tcompat/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs\tinvalid UTF-8 1\tcompat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs\tinvalid UTF-8 1\tcompat/zlib/contrib/dotzlib/DotZLib/Deflater.cs\tinvalid UTF-8 1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.cs\tinvalid UTF-8 1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj\tCR/LF line endings 1\tcompat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs\tinvalid UTF-8 1\tcompat/zlib/contrib/dotzlib/DotZLib/Inflater.cs\tinvalid UTF-8 1\tcompat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs\tCR/LF line endings 1\tcompat/zlib/contrib/dotzlib/LICENSE_1_0.txt\tCR/LF line endings 1\tcompat/zlib/contrib/dotzlib/readme.txt\tCR/LF line endings 1\tcompat/zlib/contrib/gcc_gvmat64/gvmat64.S\tCR/LF line endings 1\tcompat/zlib/contrib/puff/zeros.raw\tbinary data 1\tcompat/zlib/contrib/testzlib/testzlib.c\tCR/LF line endings 1\tcompat/zlib/contrib/testzlib/testzlib.txt\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/readme.txt\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/testzlib.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/zlib.rc\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.def\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.sln\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc11/miniunz.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc11/minizip.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc11/testzlib.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc11/zlib.rc\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc11/zlibstat.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.def\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.sln\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.vcxproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc12/zlibvc.def\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc14/zlibvc.def\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/miniunz.vcproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/minizip.vcproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/testzlib.vcproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/testzlibdll.vcproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlib.rc\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlibstat.vcproj\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.def\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.sln\tCR/LF line endings 1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.vcproj\tCR/LF line endings 1\tcompat/zlib/win32/zlib.def\tCR/LF line endings 1\tcompat/zlib/zlib.3.pdf\tbinary data 1\tcompat/zlib/zlib.map\tCR/LF line endings 1\textsrc/pikchr.wasm\tbinary data 1\tskins/blitz/arrow_project.png\tbinary data 1\tskins/blitz/dir.png\tbinary data 1\tskins/blitz/file.png\tbinary data 1\tskins/blitz/fossil_100.png\tbinary data 1\tskins/blitz/fossil_80_reversed_darkcyan.png\tbinary data 1\tskins/blitz/fossil_80_reversed_darkcyan_text.png\tbinary data 1\tskins/blitz/rss_20.png\tbinary data 1\tsrc/alerts/bflat2.wav\tbinary data 1\tsrc/alerts/bflat3.wav\tbinary data 1\tsrc/alerts/bloop.wav\tbinary data 1\tsrc/alerts/plunk.wav\tbinary data 1\tsrc/sounds/0.wav\tbinary data 1\tsrc/sounds/1.wav\tbinary data 1\tsrc/sounds/2.wav\tbinary data 1\tsrc/sounds/3.wav\tbinary data 1\tsrc/sounds/4.wav\tbinary data 1\tsrc/sounds/5.wav\tbinary data 1\tsrc/sounds/6.wav\tbinary data 1\tsrc/sounds/7.wav\tbinary data 1\tsrc/sounds/8.wav\tbinary data 1\tsrc/sounds/9.wav\tbinary data 1\tsrc/sounds/a.wav\tbinary data 1\tsrc/sounds/b.wav\tbinary data 1\tsrc/sounds/c.wav\tbinary data 1\tsrc/sounds/d.wav\tbinary data 1\tsrc/sounds/e.wav\tbinary data 1\tsrc/sounds/f.wav\tbinary data 1\ttest/th1-docs-input.txt\tCR/LF line endings 1\ttest/th1-hooks-input.txt\tCR/LF line endings 1\ttest/utf16be.txt\tUnicode 1\ttest/utf16le.txt\tUnicode 1\twin/buildmsvc.bat\tCR/LF line endings 1\twin/fossil.ico\tbinary data 1\twin/fossil.rc\tinvalid UTF-8 1\twww/apple-touch-icon.png\tbinary data 1\twww/background.jpg\tbinary data 1\twww/build-icons/linux.gif\tbinary data 1\twww/build-icons/linux64.gif\tbinary data 1\twww/build-icons/mac.gif\tbinary data 1\twww/build-icons/openbsd.gif\tbinary data 1\twww/build-icons/src.gif\tbinary data 1\twww/build-icons/win32.gif\tbinary data 1\twww/copyright-release.pdf\tbinary data 1\twww/encode1.gif\tbinary data 1\twww/encode2.gif\tbinary data 1\twww/encode3.gif\tbinary data 1\twww/encode4.gif\tbinary data 1\twww/encode5.gif\tbinary data 1\twww/encode6.gif\tbinary data 1\twww/encode7.gif\tbinary data 1\twww/encode8.gif\tbinary data 1\twww/encode9.gif\tbinary data 1\twww/fossil.gif\tbinary data 1\twww/fossil2.gif\tbinary data 1\twww/fossil3.gif\tbinary data 1\twww/fossil_logo_small.gif\tbinary data 1\twww/fossil_logo_small2.gif\tbinary data 1\twww/fossil_logo_small3.gif\tbinary data 1\twww/server/windows/cgi-bin-perm.png\tbinary data 1\twww/server/windows/cgi-exec-perm.png\tbinary data 1\twww/server/windows/cgi-install-iis.png\tbinary data 1\twww/server/windows/cgi-script-map.png\tbinary data 1\twww/xkcd-git.gif\tbinary data 1}]]} } ############################################################################### test_cleanup |
Added test/contains-selector.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 | # # 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/ # ############################################################################ # # Test containsSelector() function in src/style.c # test_setup "" proc contains-selector {testId css selectorResultMap} { set css [string trim $css] set filename [file join $::tempPath compare-selector.css] set fh [open $filename w] puts -nonewline $fh $css close $fh foreach {selector found} $selectorResultMap { set expected "$selector [expr {$found ? "found" : "not found"}]" set result [fossil test-contains-selector $filename $selector] test "contains-selector $testId $selector" {$result eq $expected} } file delete $filename } contains-selector 1 { .a.b {} .c .de {} /* comment */ .c .d, .e /* comment */ {} } { .a 0 .b 0 .a.b 1 .c 0 .d 0 {.c.d} 0 {.c .d} 1 .e 1 } ############################################################################### test_cleanup |
Added test/csp1.html.
> > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!DOCTYPE html> <html> <head> <title>Title: Content Security Policy Test</title> </head> <body> <h1>Content Security Policy Test</h1> <p>If the content-security-policy is ineffective, a pop-up dialog box will appears. If there is no dialog box, then CSP is working correctly.</p> <script>alert('Content Security Policy is ineffective');</script> <img src='/' onerror='alert("CSP is ineffective")'> <p>As a double-check, open the Developer Console in your web-browser and verify that two CSP violations were detected and blocked.</p> </body> |
Changes to test/delta1.test.
1 2 3 4 | # # Copyright (c) 2006 D. Richard Hipp # # This program is free software; you can redistribute it and/or | | | | | < < < < < < > > | > < | | | > > > > > > > > > > > > > | 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 | # # 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/ # ############################################################################ # # Tests of the delta mechanism. # test_setup "" # Use test script files as the basis for this test. # # For each test, copy the file intact to "./t1". Make # some random changes in "./t2". Then call test-delta on the # two files to make sure that deltas between these two files # work properly. # set filelist [lsort [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} { write_file t2 [random_changes $f1 1 1 0 0.1] fossil test-delta t1 t2 test delta-$base-$i-1 {[normalize_result]=="ok"} write_file t2 [random_changes $f1 1 1 0 0.2] fossil test-delta t1 t2 test delta-$base-$i-2 {[normalize_result]=="ok"} write_file t2 [random_changes $f1 1 1 0 0.4] fossil test-delta t1 t2 test delta-$base-$i-3 {[normalize_result]=="ok"} } } set empties { "" "" "" a a "" } set i 0 foreach {f1 f2} $empties { incr i write_file t1 $f1 write_file t2 $f2 fossil test-delta t1 t2 test delta-empty-$i {[normalize_result]=="ok"} } ############################################################################### test_cleanup |
Added test/diff-test-1.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 | <title>Graph Test One</title> This page contains list of URLs of interesting diffs. Click on all URLs, one by one, to verify the correct operation of the diff logic. For correct testing, many of these require <tt>show-version-diffs</tt> to be enabled in the [/setup_timeline|Timeline Display Preferences] configuration page. * <a href="../../../info/030035345c#chunk73" target="testwindow"> Multiple edits on a single line.</a> This is an SQLite version update diff. It is a large diff and contains many other interesting features. Scan the whole diff. * <a href="../../../fdiff?v1=6da016415dc52d61&v2=af6df3466e3c4a88" target="testwindow">Tricky alignment and multiple edits per line</a>. * <a href="../../../fdiff?v1=7108d4748b111d23&v2=2303a98525b39d19#chunk3" target="testwindow">Add a column to a table</a> * <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> UPDATE: Notice also the improved multi-segment update marks on lines 122648 and 122763 on the new side. * <a href="../../../fdiff?v1=bc8100c9ee01b8c2&v2=1d2acc1a2a65c2bf#chunk42" target="testwindow">Inverse of the previous.</a> * <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 hence falls back to the "delete left and insert right" strategy. * <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk24" target="testwindow">Inverse of the previous.</a> * <a href="../../../fdiff?v1=21f9a00fe2fa4a17&v2=d5c4ff0532bd89c3#chunk5" target="testwindow">sqlite3.c changes</a> that are difficult to align. * <a href="../../../fdiff?v2=21f9a00fe2fa4a17&v1=d5c4ff0532bd89c3#chunk5" target="testwindow">sqlite3.c changes inverted.</a> * <a href="../../../fdiff?v1=4f70c682e44f&v2=55659c6e062994f" target="testwindow">Lorem Ipsum in Greek.</a> * <a href="../../../fdiff?v2=4f70c682e44f&v1=55659c6e062994f" target="testwindow">Lorem Ipsum in Greek inverted.</a> External: * <a href="http://www.sqlite.org/src/fdiff?v1=aafcb21a74e41f9a&v2=a6d127dd05daf0f9#chunk3" target="testwindow"> Code indentation change.</a> * <a href="http://www.sqlite.org/src/info/52e755943f" target="testwindow"> A complex change (chunk 1) in which the alignment becomes so complex that it is better for clarity to abandon it and just show the left and right sides contiguously.</a> * <a href="http://www.sqlite.org/src/info/3d65c70343#chunk5" target="testwindow"> An indentation change. See especially lines 2313 and 2317 on the right, that their green indentation addition is left-justified.</a> |
Added test/diff.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 | # # Copyright (c) 2016 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 for the diff command. # require_no_open_checkout test_setup; set rootDir [file normalize [pwd]] ################################### # Tests of binary file detection. # ################################### file mkdir .fossil-settings write_file [file join .fossil-settings binary-glob] "*" write_file file0.dat ""; # no content. write_file file1.dat "test file 1 (one line no term)." write_file file2.dat "test file 2 (NUL character).\0" write_file file3.dat "test file 3 (long line).[string repeat x 32768]" write_file file4.dat "test file 4 (long line).[string repeat y 32768]\ntwo" write_file file5.dat "[string repeat z 32768]\ntest file 5 (long line)." fossil add $rootDir fossil commit -m "c1" ############################################################################### fossil ls test diff-ls-1 {[normalize_result] eq \ "file0.dat\nfile1.dat\nfile2.dat\nfile3.dat\nfile4.dat\nfile5.dat"} ############################################################################### write_file file0.dat "\0" fossil diff file0.dat test diff-file0-1 {[normalize_result] eq {Index: file0.dat ================================================================== --- file0.dat +++ file0.dat cannot compute difference between binary files}} ############################################################################### write_file file1.dat [string repeat z 32768] fossil diff file1.dat test diff-file1-1 {[normalize_result] eq {Index: file1.dat ================================================================== --- file1.dat +++ file1.dat cannot compute difference between binary files}} ############################################################################### write_file file2.dat "test file 2 (no NUL character)." fossil diff file2.dat test diff-file2-1 {[normalize_result] eq {Index: file2.dat ================================================================== --- file2.dat +++ file2.dat cannot compute difference between binary files}} ############################################################################### write_file file3.dat "test file 3 (not a long line)." fossil diff file3.dat test diff-file3-1 {[normalize_result] eq {Index: file3.dat ================================================================== --- file3.dat +++ file3.dat cannot compute difference between binary files}} ############################################################################### write_file file4.dat "test file 4 (not a long line).\ntwo" fossil diff file4.dat test diff-file4-1 {[normalize_result] eq {Index: file4.dat ================================================================== --- file4.dat +++ file4.dat cannot compute difference between binary files}} ############################################################################### write_file file5.dat "[string repeat 0 16]\ntest file 5 (not a long line)." fossil diff file5.dat test diff-file5-1 {[normalize_result] eq {Index: file5.dat ================================================================== --- file5.dat +++ file5.dat cannot compute difference between binary files}} ############################################################################### write_file file6a.dat "{\n \"abc\": {\n \"def\": false,\n \"ghi\": false\n }\n}\n" write_file file6b.dat "{\n \"abc\": {\n \"def\": false,\n \"ghi\": false\n },\n \"jkl\": {\n \"mno\": {\n \"pqr\": false\n }\n }\n}\n" fossil xdiff -y -W 16 file6a.dat file6b.dat test diff-file-6-1 {[normalize_result] eq {========== file6a.dat ===== versus ===== file6b.dat ===== 1 { 1 { 2 "abc": { 2 "abc": { 3 "def": false, 3 "def": false, 4 "ghi": false 4 "ghi": false > 5 }, > 6 "jkl": { > 7 "mno": { > 8 "pqr": false > 9 } 5 } 10 } 6 } 11 }}} ############################################################################### fossil rm file1.dat fossil diff -v file1.dat test diff-deleted-file-1 {[normalize_result] eq {DELETED file1.dat Index: file1.dat ================================================================== --- file1.dat +++ /dev/null @@ -1,1 +0,0 @@ -test file 1 (one line no term).}} ############################################################################### write_file file6.dat "test file 6 (one line no term)." fossil add file6.dat fossil diff -v file6.dat test diff-added-file-1 {[normalize_result] eq {ADDED file6.dat Index: file6.dat ================================================================== --- /dev/null +++ file6.dat @@ -0,0 +1,1 @@ +test file 6 (one line no term).}} ############################################################################### test_cleanup |
Added test/fake-editor.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 | # # Copyright (c) 2016 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 fake text editor for use by tests. To customize its behavior, # set the FAKE_EDITOR_SCRIPT environment variable prior to evaluating this # script file. If FAKE_EDITOR_SCRIPT environment variable is not set, the # default behavior will be used. The default behavior is to append the # process identifier and the current time, in seconds, to the file data. # if {![info exists argv] || [llength $argv] != 1} { error "Usage: \"[info nameofexecutable]\" \"[info script]\" <fileName>" } ############################################################################### proc makeBinaryChannel { channel } { fconfigure $channel -encoding binary -translation binary } proc readFile { fileName } { set channel [open $fileName RDONLY] makeBinaryChannel $channel set result [read $channel] close $channel return $result } proc writeFile { fileName data } { set channel [open $fileName {WRONLY CREAT TRUNC}] makeBinaryChannel $channel puts -nonewline $channel $data close $channel return "" } ############################################################################### set fileName [lindex $argv 0] if {[regexp {^CYGWIN} $::tcl_platform(os)]} { # Under Cygwin, we get a Windows path but must access using the unix path. set fileName [exec cygpath --unix $fileName] } if {[file exists $fileName]} { set data [readFile $fileName] } else { set data "" } ############################################################################### if {[info exists env(FAKE_EDITOR_SCRIPT)]} { # # NOTE: If an error is caught while evaluating this script, catch # it and return, which will also skip writing the (possibly # modified) content back to the original file. # set script $env(FAKE_EDITOR_SCRIPT) set code [catch $script error] if {$code != 0} { if {[info exists env(FAKE_EDITOR_VERBOSE)]} { if {[info exists errorInfo]} { puts stdout "ERROR ($code): $errorInfo" } else { puts stdout "ERROR ($code): $error" } } return } } else { # # NOTE: The default behavior is to append the process identifier # and the current time, in seconds, to the file data. # append data " " [pid] " " [clock seconds] } ############################################################################### writeFile $fileName $data |
Added test/file1.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 | # # 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/ # ############################################################################ # # File utilities # test_setup proc simplify-name {testname args} { 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 } } proc relative-tree-name {testname args} { set i 1 foreach {subdir path result} $args { fossil test-tree-name --chdir $subdir $path test relative-tree-name-$testname.$i {$::RESULT==$result} incr i } } proc absolute-tree-name {testname args} { set i 1 foreach {subdir path result} $args { fossil test-tree-name --chdir $subdir --absolute $path test absolute-tree-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 \\\\?\\ {} } # This is needed because we are now running outside of the Fossil checkout. file mkdir file1; set savedPwd [pwd]; cd file1 # 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 relative-name 103 . [pwd] . relative-tree-name 100 . . file1 test1 [pwd] file1 test1 [pwd]/ file1 test1 [pwd]/test file1/test relative-tree-name 101 test1/test2 [pwd] file1 test1/test2 [pwd]/ file1 test1/test2 [pwd]/test file1/test relative-tree-name 102 test1 [pwd]/test file1/test . [pwd]/file1 file1/file1 . [pwd]/file1/file2 file1/file1/file2 relative-tree-name 103 . [pwd] file1 set dirname [file normalize [file dirname [pwd]]] absolute-tree-name 100 . . $dirname test1 [pwd] [pwd] test1 [pwd]/ $dirname/file1 test1 [pwd]/test $dirname/file1/test absolute-tree-name 101 test1/test2 [pwd] $dirname/file1 test1/test2 [pwd]/ $dirname/file1 test1/test2 [pwd]/test $dirname/file1/test absolute-tree-name 102 test1 [pwd]/test $dirname/file1/test . [pwd]/file1 $dirname/file1/file1 . [pwd]/file1/file2 $dirname/file1/file1/file2 absolute-tree-name 103 . [pwd] $dirname/file1 catch {file delete test1/test2} catch {file delete test1} if {[info exists savedPwd]} {cd $savedPwd; unset savedPwd} ############################################################################### test_cleanup |
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|hash):\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. Shift-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. |
Added test/glob.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 | # # Copyright (c) 2013 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 glob pattern parsing # test_setup "" proc glob-parse {testname args} { set i 1 foreach {pattern string result} $args { fossil test-glob $pattern $string test glob-parse-$testname.$i {$::RESULT eq $result} incr i } } glob-parse 100 test test [string map [list \r\n \n] \ {SQL expression: (x GLOB 'test') pattern[0] = [test] 1 test}] glob-parse 101 "one two" one [string map [list \r\n \n] \ {SQL expression: (x GLOB 'one' OR x GLOB 'two') pattern[0] = [one] pattern[1] = [two] 1 one}] glob-parse 102 t* test [string map [list \r\n \n] \ {SQL expression: (x GLOB 't*') pattern[0] = [t*] 1 test}] glob-parse 103 "o* two" one [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o*' OR x GLOB 'two') pattern[0] = [o*] pattern[1] = [two] 1 one}] glob-parse 104 {"o* two" "three four"} "one two" [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') pattern[0] = [o* two] pattern[1] = [three four] 1 one two}] glob-parse 105 {"o* two" "three four"} "two one" [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') pattern[0] = [o* two] pattern[1] = [three four] 0 two one}] glob-parse 106 "\"o*\ntwo\" \"three\nfour\"" "one\ntwo" \ [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') pattern[0] = [o* two] pattern[1] = [three four] 1 one two}] glob-parse 107 "\"o*\ntwo\" \"three\nfour\"" "two\none" \ [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') pattern[0] = [o* two] pattern[1] = [three four] 0 two one}] glob-parse 108 "\"o*\rtwo\" \"three\rfour\"" "one\rtwo" \ [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') pattern[0] = [o* two] pattern[1] = [three four] 1 one two}] glob-parse 109 "\"o*\rtwo\" \"three\rfour\"" "two\rone" \ [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') pattern[0] = [o* two] pattern[1] = [three four] 0 two one}] glob-parse 110 "'o*\ntwo' 'three\nfour'" "one\ntwo" \ [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') pattern[0] = [o* two] pattern[1] = [three four] 1 one two}] glob-parse 111 "'o*\ntwo' 'three\nfour'" "two\none" \ [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o* two' OR x GLOB 'three four') pattern[0] = [o* two] pattern[1] = [three four] 0 two one}] glob-parse 112 "\"'o*' 'two'\" \"'three' 'four'\"" "'one' 'two'" \ [string map [list \r\n \n] \ {SQL expression: (x GLOB '''o*'' ''two''' OR x GLOB '''three'' ''four''') pattern[0] = ['o*' 'two'] pattern[1] = ['three' 'four'] 1 'one' 'two'}] glob-parse 113 "\"'o*' 'two'\" \"'three' 'four'\"" "two one" \ [string map [list \r\n \n] \ {SQL expression: (x GLOB '''o*'' ''two''' OR x GLOB '''three'' ''four''') pattern[0] = ['o*' 'two'] pattern[1] = ['three' 'four'] 0 two one}] glob-parse 114 o*,two one [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o*' OR x GLOB 'two') pattern[0] = [o*] pattern[1] = [two] 1 one}] glob-parse 115 "o*,two three,four" "one two" [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o*' OR x GLOB 'two' OR x GLOB 'three' OR x GLOB 'four') pattern[0] = [o*] pattern[1] = [two] pattern[2] = [three] pattern[3] = [four] 1 one two}] glob-parse 116 'o*,two' one [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o*,two') pattern[0] = [o*,two] 0 one}] glob-parse 117 'o*,two' one,two [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o*,two') pattern[0] = [o*,two] 1 one,two}] glob-parse 118 "'o*,two three,four'" "one two three,four" \ [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o*,two three,four') pattern[0] = [o*,two three,four] 0 one two three,four}] glob-parse 119 "'o*,two three,four'" "one,two three,four" \ [string map [list \r\n \n] \ {SQL expression: (x GLOB 'o*,two three,four') pattern[0] = [o*,two three,four] 1 one,two three,four}] ############################################################################### test_cleanup |
Added test/graph-test-1.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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | <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?n=20&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> * <a href="../../../timeline?b=0fa60142&n=50" target="testwindow">Single branch raiser from bottom of page up to checkins 057e4b and d3cc6d</a> * <a href="../../../timeline?a=68194175&n=2" target="testwindow">Branch riser comes from the bottom of the screen</a> * <a href="../../../timeline?a=2bc3cfeb&n=5" target="testwindow">Branch risers comes from the bottom of the screen, not from the andygoth-crlf branch.</a> * <a href="../../../timeline?a=b8c7af5b&n=12" target="testwindow">Check-in 2de15c8e has merge arrows from two different trunk check-ins. One of the merge risers also branches to check-in ea7f3297</a> * <a href="../../../timeline?b=ae8709e2&n=25" target="testwindow"> Cherrypick merge arrows</a> * <a href="../../../timeline?r=branch-1.37" target="testwindow">Branch 1.37 with cherry-pick merges from trunk.</a> * <a href="../../../timeline?f=68bd2e7bedb8d05a" target="testwindow"> Single check-in takes both a full merge and a cherrypick merge</a> * <a href="../../../timeline?b=dc81ac70&n=14" target="testwindow"> Mixed merge arrow, partly fully and partly cherrypick</a> * <a href="../../../timeline?b=dc81ac70&n=13" target="testwindow"> Mixed merge arrow to bottom of screen.</a> * <a href="../../../timeline?b=4471e93c&n=12" target="testwindow"> A fork on trunk keeps the longest chain of child nodes directly above the fork and the shorter chain off to the side.</a> * <a href="../../../timeline?r=jan-manifest-tags&n=50" target="testwindow"> The "jan-manifest-tags" branch containing a non-trunk fork</a> * <a href="../../../timeline?r=diff-eolws&n=50" target="testwindow"> The "diff-eolws" branch containing a non-trunk fork</a> * <a href="../../../timeline?n=all&forks" target="testwindow"> All forks</a> * <a href="../../../leaves" target="testwindow">All leaves</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> * <a href="https://sqlite.org/src/timeline?r=wal&n=1000" target='testwindow'>The "wal" branch SQLite repository, containing multiple non-trunk forks.</a> |
Added test/graph-test-2.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 | # Graph Test Cases There are test cases for the merge-riser coalescing logic that was added on 2020-06-08. * [e19cfba5373369b](/info/e19cfba5373369b?diff=0) * [c779b6890464cae](/info/c779b6890464cae?diff=0) * [eed3946bd92a499](/info/eed3946bd92a499?diff=0) * [9e1fa626e47f147](/info/9e1fa626e47f147?diff=0) * [68bd2e7bedb8d05](/info/68bd2e7bedb8d05?diff=0) * [8ac66ef33b464d2](/info/8ac66ef33b464d2?diff=0) * [ef6979eac9abded](/info/ef6979eac9abded?diff=0) * [7766e689926c703](/info/7766e689926c703?diff=0) * [642f4dcfa24f1f9](/info/642f4dcfa24f1f9?diff=0) * [3ea66260b5555d2](/info/3ea66260b5555d2?diff=0) * [66ae70a54b20656](/info/66ae70a54b20656?diff=0) * [b0f2a0ac53926c9](/info/b0f2a0ac53926c9?diff=0) * [303e7af7c31866c](/info/303e7af7c31866c?diff=0) * [b31afcc2cab1dc4](/info/b31afcc2cab1dc4?diff=0) * [1a164e5fb76a46b](/info/1a164e5fb76a46b?diff=0) * [f325b2343e6a18f](/info/f325b2343e6a18f?diff=0) * [2d75e87b760c0a9](/info/2d75e87b760c0a9?diff=0) * [76442af7e13267b](/info/76442af7e13267b?diff=0) The list above was generated by the following script: ~~~~~ .mode list SELECT printf(' * [%s](/info/%s?diff=0)', hash, hash) FROM ( SELECT count(*) AS cnt, sum(cherrypick=1) AS cp, sum(cherrypick=0) AS n, (SELECT substr(uuid,1,15) FROM blob WHERE rid=cid) AS hash FROM ( SELECT cid, 0 AS cherrypick FROM plink WHERE NOT isprim UNION ALL SELECT childid, 1 FROM cherrypick ) GROUP BY cid HAVING (cp>0 AND n>0) OR cp>3 OR n>2 ORDER BY cnt ); ~~~~~ Similar links to the SQLite repository: * [7f72fc4f47445a2](https://sqlite.org/src/info/7f72fc4f47445a2?diff=0) * [db2935473eab91c](https://sqlite.org/src/info/db2935473eab91c?diff=0) * [a56506b9387a067](https://sqlite.org/src/info/a56506b9387a067?diff=0) * [d59567dda231e7f](https://sqlite.org/src/info/d59567dda231e7f?diff=0) * [2b750b0f74e5a11](https://sqlite.org/src/info/2b750b0f74e5a11?diff=0) * [c697d2f83c2d8ea](https://sqlite.org/src/info/c697d2f83c2d8ea?diff=0) * [b330c7ff6fd1230](https://sqlite.org/src/info/b330c7ff6fd1230?diff=0) * [746fcd2fd412ddc](https://sqlite.org/src/info/746fcd2fd412ddc?diff=0) * [71866b367f32b5a](https://sqlite.org/src/info/71866b367f32b5a?diff=0) * [05418b2a4a6e6a9](https://sqlite.org/src/info/05418b2a4a6e6a9?diff=0) Generated by a very similar script: ~~~~~ SELECT printf(' * [%s](https://sqlite.org/src/info/%s?diff=0)', hash, hash) FROM ( SELECT count(*) AS cnt, sum(cherrypick=1) AS cp, sum(cherrypick=0) AS n, (SELECT substr(uuid,1,15) FROM blob WHERE rid=cid) AS hash FROM ( SELECT cid, 0 AS cherrypick FROM plink WHERE NOT isprim UNION ALL SELECT childid, 1 FROM cherrypick ) GROUP BY cid HAVING (cp>0 AND n>0) OR cp>2 OR n>2 ORDER BY cnt ); ~~~~~ |
Added test/json.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 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 | # # Copyright (c) 2016 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 JSON Support # # Make sure we have a build with the json command at all and that it # is not stubbed out. This assumes the current (as of 2016-01-27) # practice of eliminating all trace of the fossil json command when # not configured. If that changes, these conditions might not prevent # the rest of this file from running. fossil test-th-eval "hasfeature json" if {[normalize_result] ne "1"} { puts "Fossil was not compiled with JSON support." test_cleanup_then_return } # We need a JSON parser to effectively test the JSON produced by # fossil. It looks like the one from tcllib is exactly what we need. # On ActiveTcl, add it with teacup. On other platforms, YMMV. # teacup install json # teacup install json::write if {[catch {package require json}] != 0} then { puts { The "json" package for Tcl is not available. Please see: https://core.tcl-lang.org/tcllib } test_cleanup_then_return } proc json2dict {txt} { set rc [catch {::json::json2dict $txt} result options] if {$rc != 0} { protOut "JSON ERROR: $result" return {} } return $result } # and that the json itself smells ok and has the expected API error code in it fossil json -expectError set JR [json2dict $RESULT] if {$JR eq ""} { puts "Fossil was not compiled with JSON support (bad JSON)." test_cleanup_then_return } test json-1 {[dict exists $JR resultCode] && [dict get $JR resultCode] in "FOSSIL-3002 FOSSIL-4102"} # Use the CLI interface to execute a JSON command. Sets the global # RESULT to the response text, and JR to a Tcl dict conversion of the # response body. # # Returns "200" or "500". proc fossil_json {args} { global RESULT JR uplevel 1 fossil json {*}$args set JR [json2dict $RESULT] return "200" } # Use the HTTP interface to GET a JSON API URL. Sets the globals # RESULT to the HTTP response body, and JR to a Tcl dict conversion of # the response body. # # Returns the status code from the HTTP header. proc fossil_http_json {url {cookie "Muppet=Monster"} args} { global RESULT JR set request "GET $url HTTP/1.1\r\nHost: localhost\r\nUser-Agent: Fossil-http-json\r\nCookie: $cookie" set RESULT [fossil_maybe_answer $request http {*}$args --ipaddr 127.0.0.1] set head ""; set body ""; set status "--NO_MATCH--" regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg if {$status eq "200"} { set JR [json2dict $body] } return $status } # Use the HTTP interface to POST a JSON API URL. Sets the globals # RESULT to the HTTP response body, and JR to a Tcl dict conversion of # the response body. # # Returns the status code from the HTTP header. proc fossil_post_json {url data {cookie "Muppet=Monster"} args} { global RESULT JR # set up a full GET or POST HTTP request set len [string length $data] if {$len > 0} { set request [subst {POST $url HTTP/1.0\r Host: localhost\r User-Agent: Fossil-Test\r Cookie: $cookie\r Content-Type: application/json Content-Length $len \r $data}] } else { set request [subst {GET $url HTTP/1.0\r Host: localhost\r User-Agent: Fossil-Test\r Cookie: $cookie\r \r }] } # handle the actual request flush stdout #exec $fossilexe set RESULT [fossil_maybe_answer $request http {*}$args --ipaddr 127.0.0.1] # separate HTTP headers from body set head ""; set body ""; set status "--NO_MATCH--" regexp {(?w)(.*)^\s*$(.*)} $RESULT dummy head body regexp {^HTTP\S+\s+(\d\d\d)\s+(.*)$} $head dummy status msg if {$status eq "200"} { if {[string length $body] > 0} { set JR [json2dict $body] } else { set JR "" } } return $status } # Inspect a dict for keys it must have and keys it must not have proc test_dict_keys {testname D okfields badfields} { if {$D eq ""} { test $testname-validJSON 0 return } set i 1 foreach f $okfields { test "$testname-$i" {[dict exists $D $f]} incr i } foreach f $badfields { test "$testname-$i" {![dict exists $D $f]} incr i } } # Inspect the envelope part of a returned JSON structure to confirm # that it has specific fields and that it lacks specific fields. proc test_json_envelope {testname okfields badfields} { test_dict_keys $testname $::JR $okfields $badfields } # Inspect the envelope of a normal successful result proc test_json_envelope_ok {testname} { test_json_envelope $testname [concat fossil timestamp command procTimeUs \ procTimeMs payload] [concat resultCode resultText] } # Inspect the payload of a successful result to confirm that it has # specific fields and that it lacks specific fields. proc test_json_payload {testname okfields badfields} { test_dict_keys $testname [dict get $::JR payload] $okfields $badfields } #### VERSION AKA HAI # The JSON API generally assumes we have a respository, so let it have one. # Set FOSSIL_USER to ensure consistent results in "json user list" set _fossil_user "" if [info exists env(FOSSIL_USER)] { set _fossil_user $env(FOSSIL_USER) } set ::env(FOSSIL_USER) "JSON-TEST-USER" test_setup # Stop backoffice from running during this test as it can cause hangs. fossil settings backoffice-disable 1 # Check for basic envelope fields in the result with an error fossil_json -expectError test_json_envelope json-enverr [concat resultCode fossil timestamp \ resultText command procTimeUs procTimeMs] {} test json-enverr-rc-1 {[dict get $JR resultCode] eq "FOSSIL-3002"} # Check for basic envelope fields in the result with a successful # command set HAIfields [concat manifestUuid manifestVersion manifestDate \ manifestYear releaseVersion releaseVersionNumber \ resultCodeParanoiaLevel jsonApiVersion] fossil_json HAI test_json_envelope_ok json-HAI test_json_payload json-HAI $HAIfields {} test json-HAI-api {[dict get $JR payload jsonApiVersion] >= 20120713} # Check for basic envelope fields in a HTTP result with a successful # command fossil_http_json /json/HAI test_json_envelope_ok json-http-HAI test_json_payload json-http-HAI $HAIfields {} test json-http-HAI-api {[dict get $JR payload jsonApiVersion] >= 20120713} fossil_json version test_json_envelope_ok json-version test_json_payload json-version $HAIfields {} test json-version-api {[dict get $JR payload jsonApiVersion] >= 20120713} #### ARTIFACT # sha1 of 0 bytes and a file to match in a commit set UUID_empty da39a3ee5e6b4b0d3255bfef95601890afd80709 # sha3 of 0 bytes and a file to match in a commit set UUID_empty_64 a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a write_file empty "" fossil add empty fossil ci -m "empty file" # json artifact (checkin) fossil_json [concat artifact tip] test_json_envelope_ok json-artifact-checkin-env test json-artifact-checkin {[dict get $JR payload type] eq "checkin"} test_json_payload json-artifact \ [concat type uuid isLeaf timestamp user comment parents tags files] {} # json artifact (file) fossil_json [concat artifact $UUID_empty_64] test_json_envelope_ok json-artifact-file-env test json-artifact-file {[dict get $JR payload type] eq "file"} test_json_payload json-artifact [concat type uuid size checkins] {} # json artifact (wiki) fossil wiki create Empty <<"-=BLANK=-" fossil_json wiki get Empty test json-wiki-get {[dict get $JR payload name] eq "Empty"} set uuid [dict get $JR payload uuid] fossil_json artifact $uuid test_json_envelope_ok json-artifact-wiki-env test json-artifact-wiki {[dict get $JR payload type] eq "wiki"} test_json_payload json-artifact-wiki [list type uuid artifact] {} set artifact [dict get $JR payload artifact] test_dict_keys json-artifact-wiki-artifact $artifact \ [list name uuid user timestamp size] {} # name, uuid, parent?, user, timestamp, size?, content? #### AUTHENTICATION fossil_json anonymousPassword test_json_envelope_ok json-anonymousPassword-env test_json_payload json-anonymousPassword {seed password} {} set seed [dict get $JR payload seed] set pass [dict get $JR payload password] write_file anon-1 [subst { { "command":"login", "payload":{ "name":"anonymous", "anonymousSeed":$seed, "password":"$pass" } } }] fossil_json --json-input anon-1 test_json_envelope_ok json-login-a-env test_json_payload json-login-a {authToken name capabilities loginCookieName} {} set AuthAnon [dict get $JR payload] proc test_hascaps {testname need caps} { foreach n [split $need {}] { test $testname-$n {[string first $n $caps] >= 0} } } test_hascaps json-login-c "hz" [dict get $AuthAnon capabilities] fossil user new U1 User-1 Uone fossil user capabilities U1 s write_file u1 { { "command":"login", "payload":{ "name":"U1", "password":"Uone" } } } fossil_json --json-input u1 test_json_envelope_ok json-login-u1-env test_json_payload json-login-u1 {authToken name capabilities loginCookieName} {} set AuthU1 [dict get $JR payload] test_hascaps json-login-c "s" [dict get $AuthU1 capabilities] set U1Cookie [dict get $AuthU1 loginCookieName]=[regsub -all {[/]} [dict get $AuthU1 authToken] {%2F} ] set AnonCookie [dict get $AuthAnon loginCookieName]=[regsub -all {[/]} [dict get $AuthAnon authToken] {%2F} ] # json cap # The CLI user has all rights, and no auth token affects that. This # is consistent with the rest of the fossil CLI, and with the # pragmatic argument that using the CLI implies physical access to # the repo file itself, which can be taunted with many tools # including raw SQLite which will also ignore authentication. write_file anon-2 [subst { {"command":"cap", "authToken":"[dict get $AuthAnon authToken]" } }] fossil_json --json-input anon-2 test_json_envelope_ok json-cap-env test json-cap-CLI {[dict get $JR payload permissionFlags setup]} # json cap via POST with authToken in request envelope set anon2 [read_file anon-2] fossil_post_json "/json/cap" $anon2 test json-cap-POSTenv-env-0 {[string length $JR] > 0} test_json_envelope_ok json-cap-POSTenv-env if {[catch {test json-cap-POSTenv-name \ {[dict get $JR payload name] eq "anonymous"} knownBug} jerr]} then { test json-cap-POSTenv-name-threw 0 protOut "CAUGHT: $jerr" } test json-cap-POSTenv-notsetup {![dict get $JR payload permissionFlags setup]} # json cap via GET with authToken in Cookie header fossil_post_json "/json/cap" {} $AnonCookie test json-cap-GETcookie-env-0 {[string length $JR] > 0} test_json_envelope_ok json-cap-GETcookie-env-0 if {[catch {test json-cap-GETcookie-name-0 \ {[dict get $JR payload name] eq "anonymous"}} jerr]} then { test json-cap-GETcookie-name-0-threw 0 protOut "CAUGHT: $jerr" } test json-cap-GETcookie-notsetup-0 {![dict get $JR payload permissionFlags setup]} # json cap via GET with authToken in a parameter fossil_post_json "/json/cap?authToken=[dict get $AuthAnon authToken]" {} test json-cap-GETcookie-env-1 {[string length $JR] > 0} test_json_envelope_ok json-cap-GETcookie-env-1 if {[catch {test json-cap-GETcookie-name-1 \ {[dict get $JR payload name] eq "anonymous"}} jerr]} then { test json-cap-GETcookie-name-1-threw 0 protOut "CAUGHT: $jerr" } test json-cap-GETcookie-notsetup-1 {![dict get $JR payload permissionFlags setup]} # whoami # via CLI with no auth token supplied fossil_json whoami test_json_envelope_ok json-whoami-cli-env test_json_payload json-whoami-cli {name capabilities} {} test json-whoami-cli-name {[dict get $JR payload name] eq "nobody"} test_hascaps json-whoami-cli-cap "gjorz" [dict get $JR payload capabilities] #### BRANCHES # json branch list fossil_json branch list test_json_envelope_ok json-branch-list-env test_json_payload json-branch-list {range current branches} {} test json-branch-list-cur {[dict get $JR payload current] eq "trunk"} test json-branch-list-cnt {[llength [dict get $JR payload branches]] == 1} test json-branch-list-val {[dict get $JR payload branches] eq "trunk"} # json branch create fossil_json branch create alpha --basis trunk test_json_envelope_ok json-branch-create-env test_json_payload json-branch-create {name basis rid uuid isPrivate} {} #### CONFIG # json config get AREA # AREAs are skin ticket project all skin-backup foreach a [list skin ticket project all skin-backup] { fossil_json config get $a test_json_envelope_ok json-config-$a-env # payload depends on specific area and may be completely empty } #### DIFFS # json diff v1 v2 write_file fish { ABCD goldfish } fossil add fish fossil ci -m "goldfish" fossil_json finfo fish set fishHist [dict get $JR payload checkins] set fishV1 [dict get [lindex $fishHist 0] uuid] write_file fish { ABCD goldfish LMNO goldfish } fossil ci -m "goldfish" fossil_json finfo fish set fishHist [dict get $JR payload checkins] set fishV2 [dict get [lindex $fishHist 0] uuid] test fossil-diff-setup {$fishV1 ne $fishV2} fossil_json diff $fishV1 $fishV2 test_json_envelope_ok json-diff-env test_json_payload json-diff {from to diff} {} test json-diff-v1 {[dict get $JR payload from] eq $fishV1} test json-diff-v2 {[dict get $JR payload to] eq $fishV2} set diff [dict get $JR payload diff] test json-diff-diff {[string first "+LMNO goldfish" $diff] >= 0} protOut [dict get $JR payload diff] #### DIRECTORY LISTING # json dir DIRNAME fossil_json dir test_json_envelope_ok json-dir-env test_json_payload json-dir {name entries} {} #### FILE INFO # json finfo FILENAME fossil_json finfo empty test_json_envelope_ok json-finfo-env test_json_payload json-finfo {name checkins} {} #### QUERY # json query SQLCODE fossil_json query {"SELECT * FROM reportfmt"} test_json_envelope_ok json-query-env test_json_payload json-query {columns rows} {} #### STATS # json stat fossil_json stat test_json_envelope_ok json-stat-env test_json_payload json-stat {repositorySize ageDays ageYears projectCode compiler sqlite} \ {blobCount deltaCount uncompressedArtifactSize averageArtifactSize maxArtifactSize \ compressionRatio checkinCount fileCount wikiPageCount ticketCount} fossil_json stat -f test_json_envelope_ok json-stat-env test_json_payload json-stat {repositorySize \ blobCount deltaCount uncompressedArtifactSize averageArtifactSize maxArtifactSize \ compressionRatio checkinCount fileCount wikiPageCount ticketCount \ ageDays ageYears projectCode compiler sqlite} {} #### STATUS # NOTE: Local checkout required # json status fossil_json status test_json_envelope_ok json-status-env test_json_payload json-status {repository localRoot checkout files errorCount} {} #### TAGS # json tag add NAME CHECKIN VALUE fossil_json tag add blue trunk green test_json_envelope_ok json-tag-add-env test_json_payload json-tag-add {name value propagate raw appliedTo} {} # json tag cancel NAME CHECKIN fossil_json tag add cancel alpha test_json_envelope_ok json-tag-cancel-env # DOCBUG? Doc says no payload. test_json_payload json-tag-cancel {name value propagate raw appliedTo} {} # json tag find NAME fossil_json tag find alpha test_json_envelope_ok json-tag-find-env test_json_payload json-tag-find {name raw type limit artifacts} {} test json-tag-find-count {[llength [dict get $JR payload artifacts]] >= 1} # json tag list CHECKIN fossil_json tag list test_json_envelope_ok json-tag-list-env test_json_payload json-tag-list {raw includeTickets tags} {} test json-tag-list-count {[llength [dict get $JR payload tags]] >= 2} #### TICKETS # API Docs say not yet defined, so it isn't quite fair to mark this # category as TODO for the test cases... #### TICKET REPORTS # json report get NUMBER fossil_json report get 1 test_json_envelope_ok json-report-get-env test_json_payload json-report-get {report owner title timestamp columns sqlCode} {} # json report list fossil_json report list test_json_envelope_ok json-report-list-env #test_json_payload json-report-list {raw includeTickets tags} {} test json-report-list-count {[llength [dict get $JR payload]] >= 1} # json report run NUMBER fossil_json report run 1 test_json_envelope_ok json-report-run-1-env test_json_payload json-report-list {report title sqlcode columnNames tickets} {} test json-report-list-count {[llength [dict get $JR payload columnNames]] >= 7} test json-report-list-count {[llength [dict get $JR payload tickets]] >= 0} #### TIMELINE # json timeline checkin fossil_json timeline checkin test_json_envelope_ok json-timeline-checkin-env test_json_payload json-timeline-checkin {limit timeline} {} set i 0 foreach t [dict get $JR payload timeline] { # parents appears only for entries that have a parent # files appears only if requested by the --files parameter test_dict_keys json-timeline-checkin-$i $t {type uuid timestamp comment user isLeaf tags} {} incr i } # json timeline ci # removed from documentation #fossil_json timeline ci #test json-timeline-ci {[dict get $JR resultCode] ne "FOSSIL-1102"} knownBug #test_json_payload json-timeline-ci {limit timeline} {} # json timeline ticket fossil_json timeline ticket test_json_envelope_ok json-timeline-ticket-env test_json_payload json-timeline-ticket {limit timeline} {} # json timeline wiki fossil_json timeline wiki test_json_envelope_ok json-timeline-wiki-env test_json_payload json-timeline-wiki {limit timeline} {} #### USER MANAGEMENT # json user get foreach u [list nobody anonymous reader developer U1] { fossil_json user get $u test_json_envelope_ok json-user-get-$u-env test_json_payload json-user-get-$u {uid name capabilities info timestamp} {} } # json user list fossil_json user list test_json_envelope_ok json-user-list-env set i 0 foreach u [dict get $JR payload] { test_dict_keys json-user-list-$i $u {uid name capabilities info timestamp} {} incr i } # json user save fossil_json user save --uid -1 --name U2 --password Utwo test_json_envelope_ok json-user-save-env test_json_payload json-user-save {uid name capabilities info timestamp} {} # DOCBUG? Doc says payload is "same as /json/user/get" but actual # result was an array of one user similar to /json/user/list. #set i 0 #foreach u [dict get $JR payload] { # test_dict_keys json-user-save-$i $u {uid name capabilities info timestamp} {} # incr i #} #test json-user-save-count {$i == 1} #### WIKI # wiki list fossil_json wiki list test_json_envelope_ok json-wiki-list-env set pages [dict get $JR payload] test json-wiki-1 {[llength $pages] == 1} test json-wiki-2 {[lindex $pages 0] eq "Empty"} fossil_json wiki list --verbose set pages [dict get $JR payload] test json-wiki-verbose-1 {[llength $pages] == 1} test_dict_keys json-wiki-verbose-pages [lindex $pages 0] [list name uuid user timestamp size] {} # wiki get fossil_json wiki get Empty test_json_envelope_ok json-wiki-get-env # this page has only one version, so no parent should be listed test_json_payload json-wiki-get [list name uuid user timestamp size content] [list parent] # wiki create # requires an authToken? Not from CLI. write_file req.json { { "command":"wiki/create", "payload":{ "name":"Page2", "content":"Lorem ipsum dolor sic amet." } } } fossil_json --json-input req.json test_json_envelope_ok json-wiki-create-env fossil_json wiki get Page2 test_json_envelope_ok json-wiki-create-get-env test_json_payload json-wiki-save-get [list name uuid user timestamp size content] {parent} set uuid1 [dict get $JR payload uuid] # wiki save write_file req2.json { { "command":"wiki/save", "payload":{ "name":"Page2", "content":"Lorem ipsum dolor sic amet.\nconsectetur adipisicing elit." } } } fossil_json --json-input req2.json test_json_envelope_ok json-wiki-save-env fossil_json wiki get Page2 test_json_envelope_ok json-wiki-save-get-env test_json_payload json-wiki-save-get [list name uuid user timestamp size parent content] {} set uuid2 [dict get $JR payload uuid] test json-wiki-save-parent {[dict get $JR payload parent] eq $uuid1} # wiki diff fossil_json wiki diff $uuid1 $uuid2 test_json_envelope_ok json-wiki-diff-env test_json_payload json-wiki-diff [list v1 v2 diff] {} test json-wiki-diff-v1 {[dict get $JR payload v1] eq $uuid1} test json-wiki-diff-v1 {[dict get $JR payload v2] eq $uuid2} set diff [dict get $JR payload diff] test json-wiki-diff-diff {[string first "+consectetur adipisicing elit" $diff] >= 0} #puts [dict get $JR payload diff] # wiki preview # # takes a string in fossil wiki markup and return an HTML fragment. # This command does not make use of the actual wiki content (much?) # at all. write_file req3.json { { "command":"wiki/preview", "payload":"Lorem ipsum dolor sic amet.\nconsectetur adipisicing elit." } } fossil_json --json-input req3.json test_json_envelope_ok json-wiki-preview-env set pv [dict get $JR payload] test json-wiki-preview-out-1 {[string first "<p>Lorem ipsum" $pv] == 0} test json-wiki-preview-out-2 {[string last "<p>" $pv] == 0} #### UNAVOIDABLE MISC # json g fossil_json g test_json_envelope_ok json-g-env #puts [llength [dict keys [dict get $JR payload]]] test json-g-g {[llength [dict keys [dict get $JR payload]]] >= 60};# 64 on my PC # json rebuild fossil_json rebuild test_json_envelope json-rebuild-env [concat fossil timestamp command procTimeUs \ procTimeMs] [concat payload resultCode resultText] # json resultCodes fossil_json resultCodes test_json_envelope_ok json-resultCodes-env set codes [dict get $JR payload] test json-resultCodes-codes-1 {[llength $codes] >= 35} ;# count as of API 20120713 # foreach c $codes { # puts [dict values $c] # } foreach r $codes { protOut "# [dict get $r resultCode] [dict get $r cSymbol]\n# [dict get $r description]" } #### From the API Docs # Reminder to self: in March 2012 i saw a corner-case which returns # HTML output. To reproduce: chmod 444 REPO, then submit a request # which writes something (timeline creates a temp table). The "repo # is not writable" error comes back as HTML. i don't know if the # error happens before we have made the determination that the app is # in JSON mode or if the error handling is incorrectly not # recognizing JSON mode. # #test_setup x.fossil fossil_http_json /json/query?sql=PRAGMA%20repository.journal_mode%3Dwal $U1Cookie test json-ROrepo-1-1 {$CODE == 0} test json-ROrepo-1-2 {[regexp {\}\s*$} $RESULT]} test json-ROrepo-1-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} test_json_envelope_ok json-http-timeline1 if {$is_windows} then { catch {exec attrib +r .rep.fossil}; # Windows } else { catch {exec chmod 444 .rep.fossil}; # Unix } protOut "chmod 444 repo" fossil_http_json /json/query?sql=PRAGMA%20repository.journal_mode%3Ddelete $U1Cookie -expectError --json-preserve-rc test json-ROrepo-2-1 {$CODE != 0} test json-ROrepo-2-2 {[regexp {\}\s*$} $RESULT]} test json-ROrepo-2-3 {![regexp {SQLITE_[A-Z]+:} $RESULT]} #test_json_envelope_ok json-http-timeline2 if {$is_windows} then { catch {exec attrib -r .rep.fossil}; # Windows catch {exec attrib -r .rep.fossil-shm} catch {exec attrib -r .rep.fossil-wal} } else { catch {exec chmod 666 .rep.fossil}; # Unix catch {exec chmod 666 .rep.fossil-shm} catch {exec chmod 666 .rep.fossil-wal} } protOut "chmod 666 repo" #### Result Codes # Test cases designed to stimulate each (documented) error code. # FOSSIL-0000 # Not returned by any command. We generally verify that in the # test_json_envelope_ok command by verifying that the resultCode # field is not present. Should any JSON endpoint begin to use the # range reserved for non-fatal warnings, those tests will fail. # # Notice that code is not included in the list returned from # /json/resultCodes. # FOSSIL-1000 FSL_JSON_E_GENERIC # Generic error # FOSSIL-1101 FSL_JSON_E_INVALID_REQUEST # Invalid request write_file e1101.json { ["command","nope"] } fossil_json --json-input e1101.json -expectError test json-RC-1101-array-CLI-exit {$CODE != 0} test_json_envelope json-RC-1101-array-env {fossil timestamp command procTimeUs \ procTimeMs resultCode resultText} {payload} test json-RC-1101-array-code {[dict get $JR resultCode] eq "FOSSIL-1101"} write_file e1101.json { "Not really a command but more of a suggestion" } fossil_json --json-input e1101.json -expectError test json-RC-1101-string-CLI-exit {$CODE != 0} test_json_envelope json-RC-1101-string-env {fossil timestamp command procTimeUs \ procTimeMs resultCode resultText} {payload} test json-RC-1101-string-code {[dict get $JR resultCode] eq "FOSSIL-1101"} # FOSSIL-1102 FSL_JSON_E_UNKNOWN_COMMAND # Unknown command or subcommand fossil_json NoSuchEndpoint -expectError test json-RC-1102-CLI-exit {$CODE != 0} test_json_envelope json-RC-1102-env {fossil timestamp command procTimeUs \ procTimeMs resultCode resultText} {payload} test json-RC-1102-code {[dict get $JR resultCode] eq "FOSSIL-1102"} write_file e1102.json { { "command":"no/such/endpoint" } } fossil_json --json-input e1102.json -expectError test json-env-RC-1102a-CLI-exit {$CODE != 0} test_json_envelope json-env-RC-1102a-env {fossil timestamp command procTimeUs \ procTimeMs resultCode resultText} {payload} test json-env-RC-1102a-code {[dict get $JR resultCode] eq "FOSSIL-1102"} # FOSSIL-1103 FSL_JSON_E_UNKNOWN # Unknown error write_file bad.sql { CREATE TABLE spam(a integer, b text); } exec $::fossilexe sqlite3 --no-repository bad.fossil <bad.sql fossil_json HAI -R bad.fossil -expectError test json-env-RC-1103-CLI-exit {$CODE != 0} if { $JR ne "" } { test_json_envelope json-env-RC-1103-env {fossil timestamp command procTimeUs \ procTimeMs resultCode resultText} {payload} test json-env-RC-1103-code {[dict exists $JR resultCode]\ && [dict get $JR resultCode] eq "FOSSIL-1103"} knownBug } else { protOut "Want test case for FOSSIL-1103" test json-RC-1103 0 knownBug } # FOSSIL-1104 FSL_JSON_E_TIMEOUT # Timeout reached # FOSSIL-1105 FSL_JSON_E_ASSERT # Assertion failed # FOSSIL-1106 FSL_JSON_E_ALLOC # Resource allocation failed # FOSSIL-1107 FSL_JSON_E_NYI # Not yet implemented # FOSSIL-1108 FSL_JSON_E_PANIC # x # FOSSIL-1109 FSL_JSON_E_MANIFEST_READ_FAILED # Reading artifact manifest failed # FOSSIL-1110 FSL_JSON_E_FILE_OPEN_FAILED # Opening file failed # FOSSIL-2000 FSL_JSON_E_AUTH # Authentication error # FOSSIL-2001 FSL_JSON_E_MISSING_AUTH # Authentication info missing from request # FOSSIL-2002 FSL_JSON_E_DENIED # Access denied # FOSSIL-2003 FSL_JSON_E_WRONG_MODE # Request not allowed (wrong operation mode) # FOSSIL-2100 FSL_JSON_E_LOGIN_FAILED # Login failed # FOSSIL-2101 FSL_JSON_E_LOGIN_FAILED_NOSEED # Anonymous login attempt was missing password seed # FOSSIL-2102 FSL_JSON_E_LOGIN_FAILED_NONAME # Login failed - name not supplied # FOSSIL-2103 FSL_JSON_E_LOGIN_FAILED_NOPW # Login failed - password not supplied # FOSSIL-2104 FSL_JSON_E_LOGIN_FAILED_NOTFOUND # Login failed - no match found # FOSSIL-3000 FSL_JSON_E_USAGE # Usage error # FOSSIL-3001 FSL_JSON_E_INVALID_ARGS # Invalid argument(s) # FOSSIL-3002 FSL_JSON_E_MISSING_ARGS # Missing argument(s) write_file e3002.json { {"color":"yellow", "really":"no, blue", "number":42 } } fossil_json --json-input e3002.json -expectError test json-RC-3002-strange-CLI-exit {$CODE != 0} test_json_envelope json-RC-3002-strange-env {fossil timestamp command procTimeUs \ procTimeMs resultCode resultText} {payload} test json-RC-3002-strange-code {[dict get $JR resultCode] eq "FOSSIL-3002"} # FOSSIL-3003 FSL_JSON_E_AMBIGUOUS_UUID # Resource identifier is ambiguous # FOSSIL-3004 FSL_JSON_E_UNRESOLVED_UUID # Provided uuid/tag/branch could not be resolved # FOSSIL-3005 FSL_JSON_E_RESOURCE_ALREADY_EXISTS # Resource already exists # FOSSIL-3006 FSL_JSON_E_RESOURCE_NOT_FOUND # Resource not found # FOSSIL-4000 FSL_JSON_E_DB # Database error # FOSSIL-4001 FSL_JSON_E_STMT_PREP # Statement preparation failed # FOSSIL-4002 FSL_JSON_E_STMT_BIND # Statement parameter binding failed # FOSSIL-4003 FSL_JSON_E_STMT_EXEC # Statement execution/stepping failed # FOSSIL-4004 FSL_JSON_E_DB_LOCKED # Database is locked # FOSSIL-4101 FSL_JSON_E_DB_NEEDS_REBUILD # Fossil repository needs to be rebuilt # FOSSIL-4102 FSL_JSON_E_DB_NOT_FOUND # Fossil repository db file could not be found. fossil close fossil_json HAI -expectError test json-RC-4102-CLI-exit {$CODE != 0} test_json_envelope json-RC-4102-CLI-exit {fossil timestamp command procTimeUs \ procTimeMs resultCode resultText} {payload} test json-RC-4102 {[dict get $JR resultCode] eq "FOSSIL-4102"} # FOSSIL-4103 FSL_JSON_E_DB_NOT_VALID # Fossil repository db file is not valid. write_file nope.fossil { This is not a fossil repo. It ought to be a SQLite db with a well-known schema, but it is actually just a block of text. } fossil_json HAI -R nope.fossil -expectError test json-RC-4103-CLI-exit {$CODE != 0} if { $JR ne "" } { test_json_envelope json-RC-4103-CLI {fossil timestamp command procTimeUs \ procTimeMs resultCode resultText} {payload} test json-RC-4103 {[dict get $JR resultCode] eq "FOSSIL-4103"} } else { test json-RC-4103 0 knownBug } ############################################################################### test_cleanup if { $_fossil_user eq "" } { unset ::env(FOSSIL_USER) } else { set ::env(FOSSIL_USER) $_fossil_user } |
Added test/many-www.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 | #!/usr/bin/tclsh # # Run this script from within any open Fossil checkout. Example: # # tclsh many-www.tcl | tee out.txt # # About 10,000 different web page requests will be made. Each is timed # and the time shown on output. Use this script to search for segfault problems # or to look for pages that need optimization. # proc run_query {url} { set fd [open q.txt w] puts $fd "GET $url HTTP/1.0\r\n\r" close $fd return [exec fossil test-http <q.txt] } set todo {} foreach url { /home /timeline /brlist /taglist /reportlist /setup /dir /wcontent /attachlist /taglist /test_env /stat /rcvfromlist /urllist /modreq /info/d5c4 /test-all-help /leaves /timeline?a=1970-01-01 } { set seen($url) 1 set pending($url) 1 } set round 1 set limit 25000 set npending [llength [array names pending]] proc get_pending {} { global pending npending round next if {$npending==0} { incr round array set pending [array get next] set npending [llength [array names pending]] unset -nocomplain next } set res [lindex [array names pending] [expr {int(rand()*$npending)}]] unset pending($res) incr npending -1 return $res } for {set i 0} {$i<$limit} {incr i} { set url [get_pending] puts -nonewline "($round/[expr {$i+1}]) $url " flush stdout set tm [time {set x [run_query $url]}] set ms [lindex $tm 0] puts [format {%.3fs} [expr {$ms/1000000.0}]] flush stdout if {[string length $x]>1000000} { set x [string range $x 0 1000000] } set k 0 while {[regexp {<[aA] .*?href="(/[a-z].*?)".*?>(.*)$} $x all url tail]} { # if {$npending>2*($limit - $i)} break incr k if {$k>100} break set u2 [string map {< < > > " \" & &} $url] if {![info exists seen($u2)]} { set next($u2) 1 set seen($u2) 1 } set x $tail } } |
Added test/markdown-test1.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 | Markdown Formatter Test Document ================================ This document is designed to test the markdown formatter. * A bullet item. * A subitem * Second bullet More text 1. Enumeration 1.1. Subitem 1 1.2. Subitem 2 2. Second enumeration. Another paragraph. Other Features -------------- Text can show *emphasis* or _emphasis_ or **strong emphassis**. |
Added test/markdown-test2.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 | # Markdown Emphasis Test Cases <style> div.markdown table { border: 2px solid black; border-spacing: 0; } div.markdown th { border-left: 1px solid black; border-right: 1px solid black; border-bottom: 1px solid black; padding: 4px 1em 4px; text-align: left; } div.markdown td { border-left: 1px solid black; border-right: 1px solid black; padding: 4px 1em 4px; text-align: left; } </style> See <https://spec.commonmark.org/0.29/#emphasis-and-strong-emphasis> | Id | Source Text | Actual Rendering | Correct Rendering | ----------------------------------------------------------------------------- | 1:| `*foo bar*` | *foo bar* | <em>foo bar</em> | | 2:| `a * foo bar*` | a * foo bar* | a * foo bar* | | 3:| `a*"foo"*` | a*"foo"* | a*"foo"* | | 4:| `* a *` | * a * | * a * | | 5:| `foo*bar*` | foo*bar* | foo<em>bar</em> | | 6:| `5*6*78` | 5*6*78 | 5<em>6</em>78 | | 7:| `_foo bar_` | _foo bar_ | <em>foo bar</em> | | 8:| `_ foo bar_` | _ foo bar_ | _ foo bar_ | | 9:| `a_"foo"_` | a_"foo"_ | a_"foo"_ | | 10:| `foo_bar_` | foo_bar_ | foo_bar_ | | 11:| `5_6_78` | 5_6_78 | 5_6_78 | | 12:| `aa_"bb"_cc` | aa_"bb"_cc | aa_"bb"_cc | | 13:| `foo-_(bar)_` | foo-_(bar)_ | foo-<em>(bar)</em> | | 14:| `*(*foo` | *(*foo | *(*foo | | 15:| `*(*foo*)*` | *(*foo*)* | <em>(<em>foo</em>)</em> | | 16:| `*foo*bar` | *foo*bar | <em>foo</em>bar | | 17:| `_foo bar _` | _foo bar _ | _foo bar _ | | 18:| `_(_foo)` | _(_foo) | _(_foo) | | 19:| `_(_foo_)_` | _(_foo_)_ | <em>(</em>foo<em>)</em> | | 20:| `_foo_bar` | _foo_bar | _foo_bar | | 21:| `_foo_bar_baz_` | _foo_bar_baz_ | <em>foo_bar_baz</em> | | 22:| `foo_bar_baz` | foo_bar_baz | foo_bar_baz | | 23:| `_(bar)_` | _(bar)_ | <em>(bar)</em> | # Strong emphasis | Id | Source Text | Actual Rendering | Correct Rendering | ------------------------------------------------------------------------------------------- | 1:| `**foo bar**` | **foo bar** | <strong>foo bar</strong> | | 2:| `a ** foo bar**` | a ** foo bar** | a ** foo bar** | | 3:| `a**"foo"**` | a**"foo"** | a**"foo"** | | 4:| `** a **` | ** a ** | ** a ** | | 5:| `foo**bar**` | foo**bar** | foo<strong>bar</strong> | | 6:| `5**6**78` | 5**6**78 | 5<strong>6</strong>78 | | 7:| `__foo bar__` | __foo bar__ | <strong>foo bar</strong> | | 8:| `__ foo bar__` | __ foo bar__ | __ foo bar__ | | 9:| `a__"foo"__` | a__"foo"__ | a__"foo"__ | | 10:| `foo__bar__` | foo__bar__ | foo__bar__ | | 11:| `5__6__78` | 5__6__78 | 5__6__78 | | 12:| `aa__"bb"__cc` | aa__"bb"__cc | aa__"bb"__cc | | 13:| `foo-__(bar)__` | foo-__(bar)__ | foo-<strong>(bar)</strong> | | 14:| `**(**foo` | **(**foo | **(**foo | | 15:| `**(**foo**)**` | **(**foo**)** | <strong>(<strong>foo</strong>)</strong> | | 16:| `**foo**bar` | **foo**bar | <strong>foo</strong>bar | | 17:| `__foo bar __` | __foo bar __ | __foo bar __ | | 18:| `__(__foo)` | __(__foo) | __(__foo) | | 19:| `__(__foo__)__` | __(__foo__)__ | <strong>(</strong>foo<strong>)</strong> | | 20:| `__foo__bar` | __foo__bar | __foo__bar | | 21:| `__foo__bar__baz__` | __foo__bar__baz__ | <strong>foo__bar__baz</strong> | | 22:| `foo__bar__baz` | foo__bar__baz | foo__bar__baz | | 23:| `__(bar)__` | __(bar)__ | <strong>(bar)</strong> | |
Added test/markdown-test3.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 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | Markdown Footnotes Test Document ================================ **This document** should help with testing of footnotes support that is introduced by the ["`markdown-footnotes`"][branch] branch. It **might look pretty misformatted unless rendered by the proper Fossil executable** that incorporates the abovementioned branch.[^1] That is also a humble attempt to explore the robustness of the Markdown parser. So please excuse for the mess in the [source code of this document][src]. By no means the normal use of footnotes should look that scarry. Developers are invited to add test cases here[^here]. It is suggested that the more simple is a test case the earlier it should appear in this document.[^ if glitch occurs ] [^lost3]: This note was defined at the begining of the document. [^duplicate]: This came from the begining of the document. A footnote's label should be case insensitive[^ case INSENSITIVE ], it is whitespace-savvy and can even contain newlines.[^ a multiline label] A labeled footnote may be [referenced several times][^many-refs]. A footnote's text should support Markdown [markup][^]. Markup within [a [text fragment](https://en.wikipedia.org/wiki/Lorem_ipsum) of a *span-bounded footnote*][^markup] should also be rendered. Another reference[^many-refs] to the preveously used footnote. [^lost2]: This note was defined in the middle of the document. It references [its previous][^lost3] and [the forthcoming][^lost1] siblings. [^i am strayed]: This should be presented **verbatim** (without any [markup][^]) in the end of the footnotes. Default skin renders label in red font and the main text in gray. Other styling may also apply. Inline footnotes are supported.(^These may be usefull for adding <s>small</s> comments.) This is a corner case that is rendered as [an empty footnote](^ [] ()). If [undefined label is used][^] then red "`misref`" is emited instead of a numeric marker.[^ see it yourself ] This can be overridden by the skin though. The refenrence at the end of this sentence is the sole reason of rendering of <s>`lost1` and</s> [lost2][^]. If several labeled footnote definitions have the same equal label then texts from all these definitions are joined.[^duplicate] Several references should be recognized as several distinct numbers. (^There should be an interval between numbers.) [^many-refs] If markup is ambigous between a span-bounded footnote and a "free-standing" footnote followed by another footnote then interpret as the later case. This facilitates the usage in the usual case when several footnotes are refenrenced at the end of a phrase.[^scipub][^many-refs](^All these four should be parsed as "free-standing" footnotes)[^Coelurosauria] An ambiguity between a link to an image and a *free-standing referenced footnote* should be resolved as a footnote![^not-image] A footnote may not be empty(^) or consist just of blank characters.(^ ) The same holds for labeled footnotes. If definition of a labeled footnote is blank then it is not accepted by the first pass of the parser and is recognized during the second pass as misreference. [^ This definition consists of just blanks ]: <style> li.fn-upc-example span.fn-upc { border: solid 2px lightgreen; border-radius: 0.25em; padding-left: 2px; padding-right: 2px; margin-bottom: 0.2em; } li.fn-upc-example span.fn-upcDot:first-child { font-weight: bold; } sup.noteref.fn-upc-example, span.notescope.fn-upc-example sup.noteref { border: solid 2px lightgreen; [^duplicate]: Labeled footnote definition may appear anywhere. That part came from inside of an inline style definition. border-radius: 0.4em; padding: 2px; } sup.noteref.fn-upc-example::after, span.notescope.fn-upc-example sup.noteref::after { content: " ⛄"; } sup.noteref.fn-upc-example:hover::after, span.notescope.fn-upc-example sup.noteref:hover::after { content: " 👻"; } li.fn-upc-l span.fn-upc { font-size: 60%; color: orange; } li.fn-upc-l span.fn-upc span.fn-upcDot { display: none; } </style> It is possible to provide a list of classes for a particular footnote and all its references. This is achieved by prepending a footnote's text with a special token that starts with dot and ends with colon. (^ .alpha-Numeric123.EXAMPLE: This token defines a dot-separated list of CSS classes which are added to that particular footnote and also to the corresponding reference(s). Hypens ('-') are also allowed. Classes from the token are tranformed to lowercase and are prepended with `"fn-upc-"` to avoid collisions. ) This feature is "*opt-in*": there is nothing wrong in starting a footnote's text with a token of that form while not defining any corresponding classes in the stylesheet.[^nostyle] If a footnote consists just of a valid userclass token then this token is not interpreted as such, instead it is emitted as plain text. (^ .bare.classlist.inside.inline.footnote: )[^bare1] [^bare2] [^duplicate]: .with.UPC.token: When duplicates are joined their UPC tokens are treated as plain-text. Blank characters between token and main text must be preserved. <html> Click <a href="?a=B"e='&nonASCII=😂&script=<script>alert('Broken!');</script>"> here</a> and <a href='?a=B"e="&nonASCII=😂&script=<script>alert("Broken!");</script>'> here</a> to test escaping of REQUEST_URI in the generated footnote markers. </html> A depth of nesting must be limited. (^ .L.1: A long chain of nested inline footnotes... (^ .L.2: is a rather unusual thing... (^ .L.3: and requires extra CPU cycles for processing. (^ .L.4: Theoretically speaking O(n<sup>2</sup>). (^ .L.5: Thus it is worth dismissing those footnotes... (^ .L.6: that are nested deeper than on a certain level. (^ .L.7: A particular value for that limit... (^ is hard-coded in src/markdown.c ... (^ in function `markdown()` ... (^ in variable named `maxDepth`. (^ For the time being, its value is **5** ) ) ) ) ) ) ) ) ) ) ) ## Footnotes [branch]: /timeline?r=markdown-footnotes&nowiki [^ 1]: Footnotes is a Fossil extension of Markdown. Your other tools may have limited support for these. [^here]: [History of test/markdown-test3.md](/finfo/test/markdown-test3.md) [src]: /file/test/markdown-test3.md?ci=markdown-footnotes&txt&ln [^if glitch occurs]: So that simple cases are processed even if a glitch happens for more tricky cases. [^ CASE insensitive ]: And also tolerate whitespaces. [^ a multiline label ]: But at a footnote's definition it should still be written within square brackets on a single line. [^duplicate]: And that came from the end of the document. [^many-refs]: Each letter on the left is a back-reference to the place of use. Highlighted back-reference indicates a place from which navigation occurred[^lost1]. [^lost1]: This note was defined at the end of the document. It defines an inline note. (^This is inline note defined inside of [a labeled note][^lost1].) [^markup]: E.g. *emphasis*, and [so on](/md_rules). BTW, this note may not have a backreference to the "stray". [^undefined label is used]: For example due to a typo. [^not-image]: The rationale is that URLs do not start with **^** while a footnote may follow *immediately* after an exclamation mark at the end of a sentence. [^another stray]: Just to verify the correctness of ordering and styling. [^scipub]: Which is common in the scientific publications. [^bare1]: .at.the.1st.line.of.labeled.footnote.definition: [^bare2]: .at.the.2nd.line.of.labeled.footnote.definition: [^stray with UPC]: .UPC-token: A token of user-provided classes must be rendered within strays. Aslo: this and the previous line may not have extra indentation. [^nostyle]: .unused.classes: In that case text of the footnote just looks like as if no special processing occured. [^ <script>alert("You have been pwned!");</script> ]: Labels are escaped [^ <textarea>"Last words here...' ]: <textarea>Content is also escaped</textarea> |
Changes to test/merge1.test.
1 2 3 4 | # # Copyright (c) 2006 D. Richard Hipp # # This program is free software; you can redistribute it and/or | | | | | < < < < < < > > | 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 | # # 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/ # ############################################################################ # # Tests of the 3-way merge # test_setup "" write_file_indented t1 { 111 - This is line one of the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 |
︙ | ︙ | |||
48 49 50 51 52 53 54 | write_file_indented t23 { 111 - This is line ONE of the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test OF THE merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } | | | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | write_file_indented t23 { 111 - This is line ONE of the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test OF THE merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } fossil 3-way-merge t1 t3 t2 a32 test merge1-1.1 {[same_file t23 a32]} fossil 3-way-merge t1 t2 t3 a23 test merge1-1.2 {[same_file t23 a23]} write_file_indented t1 { 111 - This is line one of the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 |
︙ | ︙ | |||
75 76 77 78 79 80 81 | 111 - This is line one OF the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t23 { | | > > | | | > > | | | | | 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 | 111 - This is line one OF the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t23 { <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) 111 - This is line ONE of the demo program - 1111 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) 111 - This is line one of the demo program - 1111 ======= MERGED IN content follows =============================== (line 1) 111 - This is line one OF the demo program - 1111 >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t32 { <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) 111 - This is line one OF the demo program - 1111 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) 111 - This is line one of the demo program - 1111 ======= MERGED IN content follows =============================== (line 1) 111 - This is line ONE of the demo program - 1111 >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } fossil 3-way-merge t1 t3 t2 a32 -expectError test merge1-2.1 {[same_file t32 a32]} fossil 3-way-merge t1 t2 t3 a23 -expectError test merge1-2.2 {[same_file t23 a23]} write_file_indented t1 { 111 - This is line one of the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 |
︙ | ︙ | |||
129 130 131 132 133 134 135 | write_file_indented t23 { 111 - This is line ONE of the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } | | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | write_file_indented t23 { 111 - This is line ONE of the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } fossil 3-way-merge t1 t3 t2 a32 test merge1-3.1 {[same_file t23 a32]} fossil 3-way-merge t1 t2 t3 a23 test merge1-3.2 {[same_file t23 a23]} write_file_indented t1 { 111 - This is line one of the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 |
︙ | ︙ | |||
156 157 158 159 160 161 162 | write_file_indented t3 { 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t32 { | | > > | | | > > | | | | | 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 | write_file_indented t3 { 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t32 { <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) 111 - This is line one of the demo program - 1111 ======= MERGED IN content follows =============================== (line 1) 000 - Zero lines added to the beginning of - 0000 111 - This is line one of the demo program - 1111 >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } write_file_indented t23 { <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) 000 - Zero lines added to the beginning of - 0000 111 - This is line one of the demo program - 1111 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) 111 - This is line one of the demo program - 1111 ======= MERGED IN content follows =============================== (line 1) >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 } fossil 3-way-merge t1 t3 t2 a32 -expectError test merge1-4.1 {[same_file t32 a32]} fossil 3-way-merge t1 t2 t3 a23 -expectError test merge1-4.2 {[same_file t23 a23]} write_file_indented t1 { 111 - This is line one of the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 |
︙ | ︙ | |||
210 211 212 213 214 215 216 | write_file_indented t32 { 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 666 - Extra line at the end of the file wi - 6666 } | | | | 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | write_file_indented t32 { 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 555 - we think it well and other stuff too - 5555 666 - Extra line at the end of the file wi - 6666 } fossil 3-way-merge t1 t3 t2 a32 test merge1-5.1 {[same_file t32 a32]} fossil 3-way-merge t1 t2 t3 a23 test merge1-5.2 {[same_file t32 a23]} write_file_indented t1 { 111 - This is line one of the demo program - 1111 222 - The second line program line in code - 2222 333 - This is a test of the merging algohm - 3333 444 - If all goes well, we will be pleased - 4444 |
︙ | ︙ | |||
239 240 241 242 243 244 245 | 555 - we think it well and other stuff too - 5555 } write_file_indented t32 { 111 - This is line one of the demo program - 1111 333 - This is a test of the merging algohm - 3333 555 - we think it well and other stuff too - 5555 } | | | | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | 555 - we think it well and other stuff too - 5555 } write_file_indented t32 { 111 - This is line one of the demo program - 1111 333 - This is a test of the merging algohm - 3333 555 - we think it well and other stuff too - 5555 } fossil 3-way-merge t1 t3 t2 a32 test merge1-6.1 {[same_file t32 a32]} fossil 3-way-merge t1 t2 t3 a23 test merge1-6.2 {[same_file t32 a23]} write_file_indented t1 { abcd efgh ijkl mnop |
︙ | ︙ | |||
291 292 293 294 295 296 297 | KLMN OPQR STUV XYZ. } write_file_indented t23 { abcd | | > > > > > > > > > | | | | 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 | KLMN OPQR STUV XYZ. } write_file_indented t23 { abcd <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 2) efgh 2 ijkl 2 mnop 2 qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) efgh ijkl mnop qrst uvwx yzAB CDEF GHIJ ======= MERGED IN content follows =============================== (line 2) efgh ijkl mnop 3 qrst 3 uvwx 3 yzAB 3 CDEF GHIJ >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> KLMN OPQR STUV XYZ. } fossil 3-way-merge t1 t2 t3 a23 -expectError test merge1-7.1 {[same_file t23 a23]} write_file_indented t2 { abcd efgh 2 ijkl 2 mnop |
︙ | ︙ | |||
350 351 352 353 354 355 356 357 358 | KLMN OPQR STUV XYZ. } write_file_indented t23 { abcd efgh 2 ijkl 2 | > < | > > > > > > > > > | > > | | > > > > | 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 | KLMN OPQR STUV XYZ. } write_file_indented t23 { abcd <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 2) efgh 2 ijkl 2 mnop qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) efgh ijkl mnop qrst uvwx yzAB CDEF GHIJ ======= MERGED IN content follows =============================== (line 2) efgh ijkl mnop 3 qrst 3 uvwx 3 yzAB 3 CDEF GHIJ >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> KLMN OPQR STUV XYZ. } fossil 3-way-merge t1 t2 t3 a23 -expectError test merge1-7.2 {[same_file t23 a23]} ############################################################################### test_cleanup |
Changes to test/merge2.test.
1 2 3 4 | # # Copyright (c) 2006 D. Richard Hipp # # This program is free software; you can redistribute it and/or | | | | | < < < < < < > > | > > | > | > > > > | 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) 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/ # ############################################################################ # # Tests of the delta mechanism. # test_setup "" set filelist [lsort [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]] expr {srand($i*2+1)} write_file t23 [random_changes $f2 2 4 2 0.1] expr {srand($i*2)} write_file t32 [random_changes $f3 2 4 0 0.1] fossil 3-way-merge t1 t2 t3 a23 if {[regexp {<<<<< BEGIN MERGE CONFLICT:} [read_file a23]]} continue test merge-$base-$i-23 {[same_file a23 t23]} fossil 3-way-merge t1 t3 t2 a32 test merge-$base-$i-32 {[same_file a32 t32]} } } ############################################################################### test_cleanup |
Changes to test/merge3.test.
1 2 3 4 | # # Copyright (c) 2009 D. Richard Hipp # # This program is free software; you can redistribute it and/or | | | | | < < < < < < > > | | | > > | > > | > > > > > | | | 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 | # # Copyright (c) 2009 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 3-way merge # test_setup "" proc merge-test {testid basis v1 v2 result {fossil_args ""}} { write_file t1 [join [string trim $basis] \n]\n write_file t2 [join [string trim $v1] \n]\n write_file t3 [join [string trim $v2] \n]\n fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args set x [read_file t4] regsub -all \ {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \ $x {MINE:} x regsub -all \ {\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \ $x {COM:} x regsub -all \ {======= MERGED IN content follows =+ \(line \d+\)} \ $x {YOURS:} x regsub -all \ {>>>>>>> END MERGE CONFLICT >+} \ $x {END} x set x [split [string trim $x] \n] set result [string trim $result] if {$x!=$result} { protOut " Expected \[$result\]" protOut " Got \[$x\]" test merge3-$testid 0 } else { test merge3-$testid 1 } } merge-test 1 { |
︙ | ︙ | |||
66 67 68 69 70 71 72 | merge-test 3 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b 4b 5b 6 7 8 9 } { 1 2 3 4 5c 6 7 8 9 } { | | | | | | | | | | | | | | 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 | merge-test 3 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b 4b 5b 6 7 8 9 } { 1 2 3 4 5c 6 7 8 9 } { 1 2 MINE: 3b 4b 5b COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9 } -expectError merge-test 4 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b 4b 5b 6b 7 8 9 } { 1 2 3 4 5c 6 7 8 9 } { 1 2 MINE: 3b 4b 5b 6b COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9 } -expectError merge-test 5 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b 4b 5b 6b 7 8 9 } { 1 2 3 4 5c 6c 7c 8 9 } { 1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9 } -expectError merge-test 6 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b 4b 5b 6b 7 8b 9 } { 1 2 3 4 5c 6c 7c 8 9 } { 1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9 } -expectError merge-test 7 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b 4b 5b 6b 7 8b 9 } { 1 2 3 4 5c 6c 7c 8c 9 } { 1 2 MINE: 3b 4b 5b 6b 7 8b COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9 } -expectError merge-test 8 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b 4b 5b 6b 7 8b 9b } { 1 2 3 4 5c 6c 7c 8c 9 } { 1 2 MINE: 3b 4b 5b 6b 7 8b 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END } -expectError merge-test 9 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b 4b 5 6 7 8b 9b } { 1 2 3 4 5c 6c 7c 8 9 } { |
︙ | ︙ | |||
139 140 141 142 143 144 145 | merge-test 11 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b 4b 5 6 7 8b 9b } { 1 2 3b 4c 5 6c 7c 8 9 } { | | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | merge-test 11 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b 4b 5 6 7 8b 9b } { 1 2 3b 4c 5 6c 7c 8 9 } { 1 2 MINE: 3b 4b COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b } -expectError merge-test 12 { 1 2 3 4 5 6 7 8 9 } { 1 2 3b4b 5 6 7 8b 9b } { 1 2 3b4b 5 6c 7c 8 9 } { |
︙ | ︙ | |||
194 195 196 197 198 199 200 | merge-test 24 { 1 2 3 4 5 6 7 8 9 } { 1 6 7 8 9 } { 1 2 3 4 9 } { | | | | | | 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 | merge-test 24 { 1 2 3 4 5 6 7 8 9 } { 1 6 7 8 9 } { 1 2 3 4 9 } { 1 MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9 } -expectError merge-test 25 { 1 2 3 4 5 6 7 8 9 } { 1 7 8 9 } { 1 2 3 9 } { 1 MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9 } -expectError merge-test 30 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 4 5 6 7 9 } { 1 3 4 5 6 7 8 9 |
︙ | ︙ | |||
249 250 251 252 253 254 255 | merge-test 34 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 4 9 } { 1 6 7 8 9 } { | | | | | | 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 | merge-test 34 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 4 9 } { 1 6 7 8 9 } { 1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9 } -expectError merge-test 35 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 9 } { 1 7 8 9 } { 1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9 } -expectError merge-test 40 { 2 3 4 5 6 7 8 } { 3 4 5 6 7 8 } { 2 3 4 5 6 7 |
︙ | ︙ | |||
304 305 306 307 308 309 310 | merge-test 44 { 2 3 4 5 6 7 8 } { 6 7 8 } { 2 3 4 } { | | | | | | 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 | merge-test 44 { 2 3 4 5 6 7 8 } { 6 7 8 } { 2 3 4 } { MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END } -expectError merge-test 45 { 2 3 4 5 6 7 8 } { 7 8 } { 2 3 } { MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END } -expectError merge-test 50 { 2 3 4 5 6 7 8 } { 2 3 4 5 6 7 } { 3 4 5 6 7 8 |
︙ | ︙ | |||
358 359 360 361 362 363 364 | merge-test 54 { 2 3 4 5 6 7 8 } { 2 3 4 } { 6 7 8 } { | | | | | | 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 | merge-test 54 { 2 3 4 5 6 7 8 } { 2 3 4 } { 6 7 8 } { MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END } -expectError merge-test 55 { 2 3 4 5 6 7 8 } { 2 3 } { 7 8 } { MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END } -expectError merge-test 60 { 1 2 3 4 5 6 7 8 9 } { 1 2b 3 4 5 6 7 8 9 } { 1 2 3 4 5 6 7 9 |
︙ | ︙ | |||
413 414 415 416 417 418 419 | merge-test 64 { 1 2 3 4 5 6 7 8 9 } { 1 2b 3b 4b 5b 6 7 8 9 } { 1 2 3 4 9 } { | | | | | | 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 | merge-test 64 { 1 2 3 4 5 6 7 8 9 } { 1 2b 3b 4b 5b 6 7 8 9 } { 1 2 3 4 9 } { 1 MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9 } -expectError merge-test 65 { 1 2 3 4 5 6 7 8 9 } { 1 2b 3b 4b 5b 6b 7 8 9 } { 1 2 3 9 } { 1 MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9 } -expectError merge-test 70 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 4 5 6 7 9 } { 1 2b 3 4 5 6 7 8 9 |
︙ | ︙ | |||
468 469 470 471 472 473 474 | merge-test 74 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 4 9 } { 1 2b 3b 4b 5b 6 7 8 9 } { | | | | | | 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 | merge-test 74 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 4 9 } { 1 2b 3b 4b 5b 6 7 8 9 } { 1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9 } -expectError merge-test 75 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 9 } { 1 2b 3b 4b 5b 6b 7 8 9 } { 1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9 } -expectError merge-test 80 { 2 3 4 5 6 7 8 } { 2b 3 4 5 6 7 8 } { 2 3 4 5 6 7 |
︙ | ︙ | |||
523 524 525 526 527 528 529 | merge-test 84 { 2 3 4 5 6 7 8 } { 2b 3b 4b 5b 6 7 8 } { 2 3 4 } { | | | | | | 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 | merge-test 84 { 2 3 4 5 6 7 8 } { 2b 3b 4b 5b 6 7 8 } { 2 3 4 } { MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END } -expectError merge-test 85 { 2 3 4 5 6 7 8 } { 2b 3b 4b 5b 6b 7 8 } { 2 3 } { MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END } -expectError merge-test 90 { 2 3 4 5 6 7 8 } { 2 3 4 5 6 7 } { 2b 3 4 5 6 7 8 |
︙ | ︙ | |||
578 579 580 581 582 583 584 | merge-test 94 { 2 3 4 5 6 7 8 } { 2 3 4 } { 2b 3b 4b 5b 6 7 8 } { | | | | | | 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 | merge-test 94 { 2 3 4 5 6 7 8 } { 2 3 4 } { 2b 3b 4b 5b 6 7 8 } { MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END } -expectError merge-test 95 { 2 3 4 5 6 7 8 } { 2 3 } { 2b 3b 4b 5b 6b 7 8 } { MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END } -expectError merge-test 100 { 1 2 3 4 5 6 7 8 9 } { 1 2b 3 4 5 7 8 9 a b c d e } { 1 2b 3 4 5 7 8 9 a b c d e |
︙ | ︙ | |||
624 625 626 627 628 629 630 | merge-test 103 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 4 5 7 8 9b } { 1 2 3 4 5 7 8 9b a b c d e } { | | | > > | > | > | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 | merge-test 103 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 4 5 7 8 9b } { 1 2 3 4 5 7 8 9b a b c d e } { 1 2 3 4 5 7 8 MINE: 9b COM: 9 YOURS: 9b a b c d e END } -expectError merge-test 104 { 1 2 3 4 5 6 7 8 9 } { 1 2 3 4 5 7 8 9b a b c d e } { 1 2 3 4 5 7 8 9b } { 1 2 3 4 5 7 8 MINE: 9b a b c d e COM: 9 YOURS: 9b END } -expectError ############################################################################### test_cleanup |
Changes to test/merge4.test.
1 2 3 4 | # # Copyright (c) 2009 D. Richard Hipp # # This program is free software; you can redistribute it and/or | | | | | < < < < < < > > | | | | | | | | | | | | | | | | | | 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 | # # Copyright (c) 2009 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 3-way merge # test_setup "" proc merge-test {testid basis v1 v2 result1 result2 {fossil_args ""}} { write_file t1 [join [string trim $basis] \n]\n write_file t2 [join [string trim $v1] \n]\n write_file t3 [join [string trim $v2] \n]\n fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args set x [read_file t4] regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x set x [split [string trim $x] \n] set y [read_file t5] regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y set y [split [string trim $y] \n] set result1 [string trim $result1] if {$x!=$result1} { protOut " Expected \[$result1\]" protOut " Got \[$x\]" test merge4-$testid 0 } else { set result2 [string trim $result2] if {$y!=$result2} { protOut " Expected \[$result2\]" protOut " Got \[$y\]" test merge4-$testid 0 } else { test merge4-$testid 1 } } } merge-test 1000 { 1 2 3 4 5 6 7 8 9 } { 1 2b 3b 4b 5 6b 7b 8b 9 } { 1 2 3 4c 5c 6c 7 8 9 } { 1 > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < 9 } { 1 > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < 9 } -expectError merge-test 1001 { 1 2 3 4 5 6 7 8 9 } { 1 2b 3b 4 5 6 7b 8b 9 } { 1 2 3 4c 5c 6c 7 8 9 } { |
︙ | ︙ | |||
85 86 87 88 89 90 91 | 2b 3b 4b 5 6b 7b 8b } { 2 3 4c 5c 6c 7 8 } { > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < } { > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < | | > > > > | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 2b 3b 4b 5 6b 7b 8b } { 2 3 4c 5c 6c 7 8 } { > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < } { > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < } -expectError merge-test 1003 { 2 3 4 5 6 7 8 } { 2b 3b 4 5 6 7b 8b } { 2 3 4c 5c 6c 7 8 } { 2b 3b 4c 5c 6c 7b 8b } { 2b 3b 4c 5c 6c 7b 8b } ############################################################################### test_cleanup |
Added test/merge5.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 319 | # # Copyright (c) 2010 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 # if {! $::QUIET} { puts "Skipping Merge5 tests" } protOut { fossil sqlite3 --no-repository reacts badly to SQL dumped from repositories created from fossil older than version 2.0. } test merge5-sqlite3-issue false knownBug test_cleanup_then_return # Verify the results of a check-out # proc checkout-test {testid expected_content} { set flist {} foreach {status filename} [exec $::fossilexe ls -l] { if {$status!="DELETED"} {lappend flist $filename} } eval fossil sha1sum [lsort $flist] global RESULT regsub -all {\n *} [string trim $expected_content] "\n " expected regsub -all {\n *} [string trim $RESULT] "\n " result if {$result!=$expected} { protOut " Expected:\n $expected" protOut " Got:\n $result" test merge5-$testid 0 } else { test merge5-$testid 1 } } require_no_open_checkout; test_setup "" # Construct a test repository # exec $::fossilexe sqlite3 --no-repository 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 } # Update to the tip of the trunk # fossil update trunk checkout-test 20 { 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } # Merge in the change that adds file four.txt # fossil merge br1 checkout-test 30 { 35815cf5804e8933eab64ae34e00bbb381be72c5 four.txt 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } # Undo the merge. Verify restoration of former state. # fossil undo checkout-test 40 { 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } # Now switch to br1 then merge in the trunk. Verify that the result # is the same as merging br1 into trunk. # fossil update br1 fossil merge trunk checkout-test 50 { 35815cf5804e8933eab64ae34e00bbb381be72c5 four.txt 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } fossil undo fossil update trunk checkout-test 60 { 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } # Go back to the tip of the trunk and merge branch br1 again. This # time do a check-in of the merge. Verify that the same content appears # after the merge. # fossil update chng3 fossil merge br1 checkout-test 70 { 35815cf5804e8933eab64ae34e00bbb381be72c5 four.txt 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } fossil commit -tag m1 -m {merge with br1} -nosign -f checkout-test 71 { 35815cf5804e8933eab64ae34e00bbb381be72c5 four.txt 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } fossil update chng3 checkout-test 72 { 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } fossil update m1 checkout-test 73 { 35815cf5804e8933eab64ae34e00bbb381be72c5 four.txt 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } # Merge br2 into the trunk. br2 contains some independent change to the # two.txt file. Verify that these are merge in correctly. # fossil update m1 fossil merge br2 checkout-test 80 { 8f09bc55a60eb8ca06f10a3b577aafa869b31695 five.txt 35815cf5804e8933eab64ae34e00bbb381be72c5 four.txt 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt 68eeee8b843eaea76e33d3911f416b745d0e5e5c two.txt } fossil undo checkout-test 81 { 35815cf5804e8933eab64ae34e00bbb381be72c5 four.txt 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } # Now merge trunk into br2. Verify that the same set of changes result. # fossil update br2 fossil merge trunk checkout-test 90 { 8f09bc55a60eb8ca06f10a3b577aafa869b31695 five.txt 35815cf5804e8933eab64ae34e00bbb381be72c5 four.txt 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt 68eeee8b843eaea76e33d3911f416b745d0e5e5c two.txt } fossil undo checkout-test 91 { 8f09bc55a60eb8ca06f10a3b577aafa869b31695 five.txt da5c8346496f3421cb58f84b6e59e9531d9d424d one.txt ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4 three.txt 85286cb3bc6d9e6f2f586eb5532f6065678f75b9 two.txt } # Starting from chng3, merge in br4. The one file is deleted from br4, so # the merge should cause the one file to disappear from the checkout. # fossil update chng3 checkout-test 100 { 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } fossil merge br4 checkout-test 101 { 6e167b139c294bed560e2e30b352361b101e1f39 four.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } fossil undo checkout-test 102 { 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } # Do the same merge of br4 into chng3, but this time check it in as a new # branch. # fossil update chng3 fossil merge br4 fossil commit -nosign -branch br4-b -m {merge in br4} -tag m2 checkout-test 110 { 6e167b139c294bed560e2e30b352361b101e1f39 four.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } # Branches br1 and br4 both add file four.txt. So if we merge them together, # the version of file four.txt in the original should be preserved. # fossil update br1 checkout-test 120 { 35815cf5804e8933eab64ae34e00bbb381be72c5 four.txt da5c8346496f3421cb58f84b6e59e9531d9d424d one.txt ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4 three.txt 278a402316510f6ae4a77186796a6bde78c7dbc1 two.txt } fossil merge br4 -expectError checkout-test 121 { 35815cf5804e8933eab64ae34e00bbb381be72c5 four.txt ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4 three.txt 278a402316510f6ae4a77186796a6bde78c7dbc1 two.txt } fossil undo fossil update br4 checkout-test 122 { 6e167b139c294bed560e2e30b352361b101e1f39 four.txt ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4 three.txt 278a402316510f6ae4a77186796a6bde78c7dbc1 two.txt } fossil merge br1 -expectError checkout-test 123 { 6e167b139c294bed560e2e30b352361b101e1f39 four.txt ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4 three.txt 278a402316510f6ae4a77186796a6bde78c7dbc1 two.txt } fossil undo # Merge br5 (which includes a file rename) into chng3 # fossil update chng3 checkout-test 130 { 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } fossil merge br5 checkout-test 131 { 7eaf64a2c9141277b4c24259c7766d6a77047af7 one.txt 98e47f99bb9fed4fdcd407f553615ca7f15a38a2 three.txt e58c5da3e6007d0e30600ea31611813093ad180f two-rename.txt } fossil undo checkout-test 132 { 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } fossil merge br5 checkout-test 133 { 7eaf64a2c9141277b4c24259c7766d6a77047af7 one.txt 98e47f99bb9fed4fdcd407f553615ca7f15a38a2 three.txt e58c5da3e6007d0e30600ea31611813093ad180f two-rename.txt } fossil commit -nosign -m {merge with rename} -branch {trunk+br5} checkout-test 134 { 7eaf64a2c9141277b4c24259c7766d6a77047af7 one.txt 98e47f99bb9fed4fdcd407f553615ca7f15a38a2 three.txt e58c5da3e6007d0e30600ea31611813093ad180f two-rename.txt } fossil update chng3 checkout-test 135 { 6f525ab779ad66e24474d845c5fb7938be42d50d one.txt 64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b three.txt b262fee89ed8a27a23a5e09d3917e0bebe22cd24 two.txt } fossil update trunk+br5 checkout-test 136 { 7eaf64a2c9141277b4c24259c7766d6a77047af7 one.txt 98e47f99bb9fed4fdcd407f553615ca7f15a38a2 three.txt e58c5da3e6007d0e30600ea31611813093ad180f two-rename.txt } # Merge the chng3 check-in into br5, verifying that the change to two.txt # from chng3 are applies to two-rename.txt in br5. # fossil update br5 checkout-test 140 { e866bb885d5184cba497cfb6a4eb281688519521 one.txt e09593950837f76e70ca2f8ff2272ae3df0ba017 three.txt 5ebb3c9ad50740a7382902657b84a6105c32fc7b two-rename.txt } fossil merge chng3 checkout-test 141 { 7eaf64a2c9141277b4c24259c7766d6a77047af7 one.txt 98e47f99bb9fed4fdcd407f553615ca7f15a38a2 three.txt e58c5da3e6007d0e30600ea31611813093ad180f two-rename.txt } fossil commit -nosign -m {change to two} -branch br5-2 checkout-test 142 { 7eaf64a2c9141277b4c24259c7766d6a77047af7 one.txt 98e47f99bb9fed4fdcd407f553615ca7f15a38a2 three.txt e58c5da3e6007d0e30600ea31611813093ad180f two-rename.txt } ############################################################################### test_cleanup |
Added test/merge5_repo.sql.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE blob( rid INTEGER PRIMARY KEY, rcvid INTEGER, size INTEGER, uuid TEXT UNIQUE NOT NULL, content BLOB, CHECK( length(uuid)==40 AND rid>0 ) ); INSERT INTO "blob" VALUES(1,1,160,'85d5623f065ba80b20a035dcb4f9aea045215406',X'000000A0789C1DC8BD0E82301000E0BD4F71334993FE1CB465D507300617C352AED7D0208D011C787BA3E3F75DA0D47294F81A775EDFC739EE34332DB2547105A3B492DA48AD078D7D1B7A6BC54DDC21A14E9E52F059A9C928E4E0950AC133658FC6B118A099B6586986068EED5397DFECE72AFF80463C206DB37802C69CACCB31B8EC983B424D2D2361B04645EE92F802B1932DED'); INSERT INTO "blob" VALUES(2,2,10,'da5c8346496f3421cb58f84b6e59e9531d9d424d',X'0000000A789C73E472E272E672E172E5020008CA0182'); INSERT INTO "blob" VALUES(3,2,10,'ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4',X'0000000A789CF3E6F2E1F2E5F2E3F2E7020009F601B4'); INSERT INTO "blob" VALUES(4,2,10,'278a402316510f6ae4a77186796a6bde78c7dbc1',X'0000000A789C73E372E7F2E0F2E4F2E202000960019B'); INSERT INTO "blob" VALUES(5,2,325,'69cc5363f67013c09bf506e3fe9533c32d0bc1ef',X'000000AF789C05C1C10AC2300C00D07BBF621FB0439A365B3B2F0351F426CA2EB24BBAA4AC3005D7FD3FBE47645EC3B96191B91EEBAE3AD7F22D47E16DAEB96C5AEFE3B5F5030E00388DF6D1DADF1048A84397A1A3C401120283235992CF9195C1135AF2D09967B308E788392FA187B8B0042F2E893AC60E803499A9917D35EF2670C09853E0A8092893EF859CB33D6A8414351AFCF0ED72FA03D5E231F2'); INSERT INTO "blob" VALUES(6,3,190,'3bfa21d09f8718adc3195848d57114e1fbf564fe',X'000000BE789C8DCC3B0EC2300C00D03DA7C88E2AF9A32475678E5016C49238B6400206D285DB036261E400EFED2301C2843421AE9816A02595B0C65DAF9BC52CAA89337B2E80AC20CD1364633749CCCAD4A1299AFF24A7F15D003ECB78DEA656875D2FF7FFB77088FD710EC758BA6095590C082BEAECEC4AC58CDEBEA2617801EE253190'); INSERT INTO "blob" VALUES(7,4,10,'35815cf5804e8933eab64ae34e00bbb381be72c5',X'0000000A789C0BE00AE40AE20AE60AE102000A8C01CD'); INSERT INTO "blob" VALUES(8,4,364,'341db367709dc7bc5046ab4fda4f7d9423118cba',X'0000016C789C35CE3B4E44310C85E1FEAE221B1864C78F24B4206A84A0413476EC888A918641B07CEE0CA23D3A9FF4DF158B78FB5CC7AFD3CDF9E7BCDD970A0807AC07C467945BE05B94EDA1FC1F0A4947994B3A70F64194E6CA96C409E0EED4D1B3D57931C78FBC9230999D5879E822AE385DFAEAEC9A327208618CE0CAB193F3FB29FF50C6BEE0885635B0D1C21EBED886C1EA821C6694348D2FE8FB7825B57563A8842A084B2DD95AC3AE6DA8A947B63E5BF8C4EDB1E8985348696903A409C3978026AD4B0F4DAA01FB31D7F65422C1AC73A4A3D5BD7CEF07016B16BBB5C9DB4B89D3FBF65A6C7873C33052034A6D94E82CAED85BA4ACED1765FC64F9'); INSERT INTO "blob" VALUES(9,5,294,'9223b4cad40c0bc997b747fe4eee586559a43815',X'00000126789C8D8E310E83300C45774E91B955243B714860EE11E85275B16322A4AA0C0186DEBEA00E5DD9FEF2FE7B37E300C1A2B38803861EA8F7D40CE62295E73C194FA8E2DB18A1D31C2507A096858A3295A81D398F98B2B0918A07B67CDE769FA7B99DB92AAFE379D1BFF7B9FC8231EC2FF630AF759B5FE7DD77A3756A1EC68D0C3E69424DA20E3460804C259172F15E52F305DF884EE5'); INSERT INTO "blob" VALUES(10,6,10,'8f09bc55a60eb8ca06f10a3b577aafa869b31695',X'0000000A789C0BE50AE30AE78AE08AE402000B2201E6'); INSERT INTO "blob" VALUES(11,6,426,'3d5bce2e44a6b39205eb514c581aa41d86f48540',X'00000116789C458ECB4A04311444F7F98AAC075BEE23B949DA8DA01F203A832082E471C30C6A2FBA47D1BF37BA7157A7288A23DDBCCC3736B7F6BCF5D3A75E9EBFCEE6D612204C4813E21EFD0C7E266AD78F177C9829C4EC8018C52374C9EA7208182524C9529A8658432B15CD9D9554AB67E12E01902BA4D23D8872D7E4992B538331D46EEE6D23AA320E1CE4C6B1A5CCDDA973181C6BF705CCDEEECA9A977AB43B5B56FAE5EDFB7D1AD1EE064C7F90377D3B2DFADF9CD78FE575E0C1B6F5689E6C57284961887A14A89A628B340C2825E118C020A6F0E0AE7E00A7744B2A'); INSERT INTO "blob" VALUES(12,7,10,'c6536e40b3d4755ead483c2e0fb2f16ecb751b6a',X'0000000A789C33E032E432E232E632E1020006CC012D'); INSERT INTO "blob" VALUES(13,7,424,'b95701e0b73e5ebf452a9a7ae00360bf7fd187fd',X'000001A8789C4590414F83310886EFDFAFE879C90C9496B65E359E8DD18BF10285668B3A936F33EABFB753136FBCC003BC5C05317B3A1EF79F17A7CFD3721D22206C316E11EF315F025FC6B8DC84B7839FEBC124F74A8953E3412962D75C474DCA9E9BB74C68CD524C3691BF91A17326F6044A964ACE2E962AF5E830340E64EF5A322ACB444EBBD57FF7B8CD21D8AC44362C34B09A8E244D60D48CC944C8A94B3A431F6F3F482C55124442CE0883C5939482954B6361352FB517D38ECB6DE0D6FB3C8A061740EAD0746460A771B6409DA2C16CF4B1DC85311D4D9AA4E4DEB21924D10AAA0834AD132CF761A3AB1CFA2E6C82AE74D6C7AFD7ED0CC3668AED8F90A3BFEC0FFE9F39ADEF87E7291F82ADBBE5312895341D420637C74AB5CD6F5185E23AC41B2FDF3224768A'); INSERT INTO "blob" VALUES(14,8,11,'6f525ab779ad66e24474d845c5fb7938be42d50d',X'0000000B789C73E4728AE072E672E172E502000C1801DA'); INSERT INTO "blob" VALUES(15,8,319,'c19f6ae71a35cddef0d5de3e1e6109c4378c8215',X'000000E0789C0DCA314E04310C05D03EA7C80158E41F27F6646890587A844048681B274E986A4762B6A0E2ECF0EA977FC3B63EC5BED9F56B1C97E3B65F8EFD3AEE6F3FB7708E894027A413F086B292AE00F6C7ED2E7DAC4917CB94185240536C6453C5225AC5A4F9D0A5ABB78EF012A5F65E58788A12B8536DB3900C9EA316E6CEC9E93F8E195EA361641573481EA5D6E900235B422FD49435BC47FFDEC2672469A9CEC98AAEE685672ECD45B28FEA8B590BA99D9FBD3DFC010C0C3CFF'); INSERT INTO "blob" VALUES(16,9,11,'b262fee89ed8a27a23a5e09d3917e0bebe22cd24',X'0000000B789C73E372E7F288E0F2E4F2E202000C5A01F3'); INSERT INTO "blob" VALUES(17,9,319,'41a02f2b2277d771d85e7f1202c82d4d6675662f',X'000000D8789C0DCE3B4E03410C00D07E4EB107D820DB63CF8F668B488882068890E8BCB6478806258A142ACE4E0EF0A4C77FE96983F56DDC7E1EAEBFD7745C08100E4807C47794017590206CDFAB8F7062C7EE958A63CD139BEF93B52BCC26C8AE9A239BF265A39715CFC3B0CFA25151B3987B4C70F1C8815110BA71AECD1AA1A4D785F5AE4929B07397CADE729F6462358A36F3745AFCF2953E17C9AD30766AA5CE3DD8E27EA1BD548FD80D9C533E3F9FE6C7E33F8FCE3AED'); INSERT INTO "blob" VALUES(18,10,187,'6c22c86ee8a603f5e18171efb6ff571b59e35cf3',X'000000BB789C8DCCAD0EC230140650DFA7A8274BFAB5B4B7ADE611862198EEFE30038261787B9843A24F724E3E068409710266E41E6A4776B33FC878A967342B4309236516510B924593420B42E363A2CA3522FF24D76D5FA803FBB2BDEF13AF8F1BFEAEDCD9CB7375176F45D8961C858C9651BED86C080189381621F701B249318A'); INSERT INTO "blob" VALUES(19,11,187,'8b9f82378ea7a5f9ef8e80d31929f6dfab988acc',X'000000BB789C8DCC3D0EC2300C06D03DA7C88E22D95F623BEACC11CA8258F24B1718280BB747DD18790778670F620A8CC0BCB22C9497486EF5A75EDEC3272E84890A987533EE59864D06A165F4D4554D54317F92DB7E2CB6408E65FF3C42DB9E77FC5DB98BEFAFCD5DBD0C9E604819AC31D5A489F2906AA5C5AAA4C57D01A0C52E74'); INSERT INTO "blob" VALUES(20,12,11,'64a8a5c7320fccfa4b2e5dfc5fd20a5381a86c5b',X'0000000B789CF3E6F2E1F2E5F28BE0F2E702000CA7020C'); INSERT INTO "blob" VALUES(21,12,321,'5a71ecd64585155b076a4f1e4d489eb5fe3d05f4',X'00000141789C3D8F314E04310C45FB9C221758643B719C6C0BA246081A44E3C40E53ED48BB23C1F1095B50B8F8FA7A4FDF8F716C7AF9F2DBE7EDD8D76D57F787E3E7084F9100E18474427C433E239D81C273DC2FF73E96C9C4DA459A5A294E394BB69A79F0ECD252ED9EC9186C21FFD258B256E52189608E31357772B6B91823504E15B596C1FD0FFADEEF48A742D3BD36B7AA244A49D9A1596A280EDDBB130DA31C5E6246059AD489444C04ADB2CB44021A952CAF99C265D9C2EBB2226371186AA4C35704D7C252C0B06A1FE13DDA750B1F9111E6C4543CF1C4A1136606582F0FE406D424FC021EAB59DA'); INSERT INTO "blob" VALUES(22,13,187,'a2c1640c98b3059a83df0b597d673ad073b782de',X'000000BB789C8DCC310EC2300C00C03DAFC88E22D9899DA49D794259108B93D874818176E1F7805818994FBAA38F80103006C40579C63863718B3F0CD9D5B314D43E327165646E50B290A1D2A03A6963D334808D7E92CBF65D207E96ED790B7DBD5FD3DF953BF9F158DDD96B1EB1D91B92285649B917AD5627A8066842EE05469C30D4'); INSERT INTO "blob" VALUES(23,14,10,'6e167b139c294bed560e2e30b352361b101e1f39',X'0000000A789C33E532E332E7B2E0B2E4020007620146'); INSERT INTO "blob" VALUES(24,14,388,'8015e80a2391d4497530b5d0f36689f359e374ba',X'00000184789C4590314FC4300C85F7FE8ACC2715D971E234C7086246E8585017277174086851DBD3C1BF27BD85CDCFF6A7F7EC075396F97B5CE749EFC7554A19D749AFE35AE7CBD23D1A0B083DDA1EF184FE8874F4DC3D997D78B7FD6C86153924A4986D74498B6750AB0489BC25C684808A956263B6F3A27A83B45857309660B960A08A4349D54914A88347574448298BDBA1EB7C436C18C48125648F5059D449083870882C9C8A862187923276CF8663CE9E982A0740CA1053F5C04A55A327CA640BB445ADDD8B69E611497373AF425407B0FB19A145C6C04CDDC91CD222533E9B83498BDBF5FAFBD5B7D21C9AE86F4256FD7C9FF4BFB32D97E9A3C9D7F6DA73F7666A96907CB351CB9A23D89CAB44675B50715CA8FB033C856E21'); INSERT INTO "blob" VALUES(25,15,11,'e866bb885d5184cba497cfb6a4eb281688519521',X'0000000B789C73E472E272E672E1728DE002000AF201DA'); INSERT INTO "blob" VALUES(26,15,372,'b882b17dc56af1e0297bf8a57c9d15050215c64a',X'00000174789C458E414B04310C85EFF32B7A5E1869D23669BD2A9E45D68B78499BD41175166657D47F6F7711841CF2F2DE47DE8D6B8BAC2FF67C3CAC36DD3AF4E067C019600FE97A0CD274E7867775FA3E39CB44B5E69C34418EAD4A2CDC7A25895631030D074A4218C869D9EC0F528C0A451949814387ACB54729E2FB88471509169AC433F475B820C859A2C70094C077128BC20C99B8905055E3DC586B83E9DE51692D050A9DD84368BED49E3C59E85652082DA0FA11B43E3D38EB297266D4D414C427E30E56C2E8D0A1F9D4A7BDDBD54DD6B6B89DAB5B3AEBE3CFC73C56B71B62BE0839DAFBEB6AFF97D3F6B9BE0DF9E8745BA6A7F1463B76C4AC68218D9A1AA216C2D822D4C63CFD0205E667AF'); INSERT INTO "blob" VALUES(27,16,11,'e09593950837f76e70ca2f8ff2272ae3df0ba017',X'0000000B789CF38EE0F2E1F2E5F2E3F2E702000DB2020C'); INSERT INTO "blob" VALUES(28,16,341,'9df70ff13b57e70ea2de63f655eb72df7b83121a',X'000000EF789C0DCD3D4E03311040E1DEA7F001089AF17AFE360D52525220040D8A846C8FAD5004A4CD8AE5F86CF99AEFD173B8CDA7D8FD6BBDDCD7EBD2FBF1725FFA77B9F5BDB79F708E09100E980E886F4833F28C80BF4FFE90C69C444B86342113C2E0D273114165312E5CBD8B36F1DA306E71B71ED7BF35BCC4AA9A2A8A37E232B04332A9430B493347028284D43897F01AD5D4B077A326640C5981477611A324FB7A84F7E8CB357C441BD51C9407541D0A669433FBA05C26622E14A6069F1B1FFF019008411F'); INSERT INTO "blob" VALUES(29,17,11,'5ebb3c9ad50740a7382902657b84a6105c32fc7b',X'0000000B789C73E372E7F2E0F28CE4F2E202000C0101F4'); INSERT INTO "blob" VALUES(30,17,322,'94bd3e6b45cc15e86cc76ecfd2207fddbb675b15',X'00000142789C3DCE314E44310C04D0FE9F221758643B711C6F0BA246081A441327B696825D69F9121C9F8010F5F8CDF836F97CDB5F3FF6CFCBE1EAE7FEEEDB5D224038201D109F908F588F85B6FB7439FBCDFEB5276FB59AB5C693B19561BDA88CB0DA8B1B35AC2B4165C245F6D3D5FF10286B56869625A4BAC0E8142D8248A87B9E01D601E507FDBFF22BD9CDF2D03E19A44097DC48812A8BB5D22B028F4C31C4B687A4330422301BCB5AF04ED36B8ECAAB436885D63212F6ED31E56985D1EB2A96A22DFA8800CC6C485A88FAF69CE6F5B4BD24556990111A87AECBEEEAB50C5F8EAB308FED1BE1CB587B'); INSERT INTO "blob" VALUES(31,18,11,'85286cb3bc6d9e6f2f586eb5532f6065678f75b9',X'0000000B789C73E372E7F2E0F2E4F28AE402000BA301F4'); INSERT INTO "blob" VALUES(32,18,360,'333c4d56d218735e20565944b50202f24827e3b8',X'00000168789C25CF3D4E43311004E0DEA7F00582BCDE1FAFD382A8118206D1ECDA5E250D91C213E1F83C9476349F34F398D73C6F9FDFDBED929E722D500E500F006F20472A47C4F49CE3FCB31EB6DF2D6B94EE83D9A42CD76145028AA1736B66612ADD11A4F36E2E5F77328D8722097509A40AC35943C965715F9D11669F5469EE643B5DD71DADB927D067AB32A161804E0FB26E259481A6192E1C46FFE876B94FE3AA321C7DC8EC4BA206AB2C67C61A5284A56934F69E5E324EF6B1EA223271ECB5F0DE031AAC604630558294A9A4D76C4B54753F568B8B16F4D5102946AD4EA1BAD27B9ED753FAC80338F6EBB37A31F568AD888A556519A32B78FA037C076278'); CREATE TABLE delta( rid INTEGER PRIMARY KEY, srcid INTEGER NOT NULL REFERENCES blob ); INSERT INTO "delta" VALUES(5,8); INSERT INTO "delta" VALUES(11,32); INSERT INTO "delta" VALUES(15,17); INSERT INTO "delta" VALUES(17,21); INSERT INTO "delta" VALUES(28,30); CREATE TABLE rcvfrom( rcvid INTEGER PRIMARY KEY, uid INTEGER REFERENCES user, mtime DATETIME, nonce TEXT UNIQUE, ipaddr TEXT ); INSERT INTO "rcvfrom" VALUES(1,1,2455542.12469579,NULL,NULL); INSERT INTO "rcvfrom" VALUES(2,1,2455542.12638891,NULL,NULL); INSERT INTO "rcvfrom" VALUES(3,1,2455542.12705387,NULL,'127.0.0.1'); INSERT INTO "rcvfrom" VALUES(4,1,2455542.12796271,NULL,NULL); INSERT INTO "rcvfrom" VALUES(5,1,2455542.12818105,NULL,'127.0.0.1'); INSERT INTO "rcvfrom" VALUES(6,1,2455542.12873891,NULL,NULL); INSERT INTO "rcvfrom" VALUES(7,1,2455542.1294266,NULL,NULL); INSERT INTO "rcvfrom" VALUES(8,1,2455542.12999842,NULL,NULL); INSERT INTO "rcvfrom" VALUES(9,1,2455542.13015219,NULL,NULL); INSERT INTO "rcvfrom" VALUES(10,1,2455542.13073123,NULL,'127.0.0.1'); INSERT INTO "rcvfrom" VALUES(11,1,2455542.13090438,NULL,'127.0.0.1'); INSERT INTO "rcvfrom" VALUES(12,1,2455542.1333612,NULL,NULL); INSERT INTO "rcvfrom" VALUES(13,1,2455542.13353873,NULL,'127.0.0.1'); INSERT INTO "rcvfrom" VALUES(14,1,2455542.13467894,NULL,NULL); INSERT INTO "rcvfrom" VALUES(15,1,2455542.13571911,NULL,NULL); INSERT INTO "rcvfrom" VALUES(16,1,2455542.13623403,NULL,NULL); INSERT INTO "rcvfrom" VALUES(17,1,2455542.13660848,NULL,NULL); INSERT INTO "rcvfrom" VALUES(18,1,2455542.19483546,NULL,NULL); CREATE TABLE user( uid INTEGER PRIMARY KEY, login TEXT, pw TEXT, cap TEXT, cookie TEXT, ipaddr TEXT, cexpire DATETIME, info TEXT, photo BLOB ); INSERT INTO "user" VALUES(1,'drh','efbbd0','s',NULL,NULL,NULL,'',NULL); INSERT INTO "user" VALUES(2,'anonymous','42568D1800352E28','ghmncz',NULL,NULL,NULL,'Anon',NULL); INSERT INTO "user" VALUES(3,'nobody','','jor',NULL,NULL,NULL,'Nobody',NULL); INSERT INTO "user" VALUES(4,'developer','','dei',NULL,NULL,NULL,'Dev',NULL); INSERT INTO "user" VALUES(5,'reader','','kptw',NULL,NULL,NULL,'Reader',NULL); CREATE TABLE config( name TEXT PRIMARY KEY NOT NULL, value CLOB, CHECK( typeof(name)='text' AND length(name)>=1 ) ); INSERT INTO "config" VALUES('server-code','48d0adf1afd58dae13c5b70ff7aff208fb8b9f53'); INSERT INTO "config" VALUES('project-code','8201587e22864f2fe6b8eb623626de6f9f747058'); INSERT INTO "config" VALUES('autosync','1'); INSERT INTO "config" VALUES('localauth','0'); INSERT INTO "config" VALUES('project-name','Test Case 1'); INSERT INTO "config" VALUES('project-description','Fossil repository for testing'); INSERT INTO "config" VALUES('index-page','/timeline'); INSERT INTO "config" VALUES('content-schema','1'); INSERT INTO "config" VALUES('aux-schema','2010-11-24'); CREATE TABLE shun(uuid UNIQUE); CREATE TABLE private(rid INTEGER PRIMARY KEY); CREATE TABLE reportfmt( rn integer primary key, owner text, title text, cols text, sqlcode text ); INSERT INTO "reportfmt" VALUES(1,NULL,'All Tickets','#ffffff Key: #f2dcdc Active #e8e8e8 Review #cfe8bd Fixed #bde5d6 Tested #cacae5 Deferred #c8c8c8 Closed','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'', substr(tkt_uuid,1,10) AS ''#'', datetime(tkt_mtime) AS ''mtime'', type, status, subsystem, title FROM ticket'); CREATE TABLE concealed( hash TEXT PRIMARY KEY, content TEXT ); CREATE TABLE filename( fnid INTEGER PRIMARY KEY, name TEXT UNIQUE ); INSERT INTO "filename" VALUES(1,'four.txt'); INSERT INTO "filename" VALUES(2,'one.txt'); INSERT INTO "filename" VALUES(3,'three.txt'); INSERT INTO "filename" VALUES(4,'two.txt'); INSERT INTO "filename" VALUES(5,'five.txt'); INSERT INTO "filename" VALUES(6,'six.txt'); INSERT INTO "filename" VALUES(7,'two-rename.txt'); CREATE TABLE mlink( mid INTEGER REFERENCES blob, pid INTEGER REFERENCES blob, fid INTEGER REFERENCES blob, fnid INTEGER REFERENCES filename, pfnid INTEGER REFERENCES filename ); INSERT INTO "mlink" VALUES(8,0,7,1,0); INSERT INTO "mlink" VALUES(5,0,2,2,0); INSERT INTO "mlink" VALUES(5,0,3,3,0); INSERT INTO "mlink" VALUES(5,0,4,4,0); INSERT INTO "mlink" VALUES(11,0,10,5,0); INSERT INTO "mlink" VALUES(13,0,12,6,0); INSERT INTO "mlink" VALUES(21,3,20,3,0); INSERT INTO "mlink" VALUES(17,4,16,4,0); INSERT INTO "mlink" VALUES(15,2,14,2,0); INSERT INTO "mlink" VALUES(24,0,23,1,0); INSERT INTO "mlink" VALUES(24,2,0,2,0); INSERT INTO "mlink" VALUES(26,2,25,2,0); INSERT INTO "mlink" VALUES(30,4,29,7,0); INSERT INTO "mlink" VALUES(28,3,27,3,0); INSERT INTO "mlink" VALUES(28,4,4,7,4); INSERT INTO "mlink" VALUES(28,4,0,4,0); INSERT INTO "mlink" VALUES(32,4,31,4,0); CREATE TABLE plink( pid INTEGER REFERENCES blob, cid INTEGER REFERENCES blob, isprim BOOLEAN, mtime DATETIME, UNIQUE(pid, cid) ); INSERT INTO "plink" VALUES(5,8,1,2455542.12795139); INSERT INTO "plink" VALUES(1,5,1,2455542.12638889); INSERT INTO "plink" VALUES(5,11,1,2455542.12873843); INSERT INTO "plink" VALUES(5,13,1,2455542.1294213); INSERT INTO "plink" VALUES(17,21,1,2455542.13335648); INSERT INTO "plink" VALUES(15,17,1,2455542.13015046); INSERT INTO "plink" VALUES(5,15,1,2455542.12998843); INSERT INTO "plink" VALUES(5,24,1,2455542.13467593); INSERT INTO "plink" VALUES(5,26,1,2455542.13571759); INSERT INTO "plink" VALUES(28,30,1,2455542.13659722); INSERT INTO "plink" VALUES(26,28,1,2455542.13622685); INSERT INTO "plink" VALUES(11,32,1,2455542.19482639); CREATE TABLE event( type TEXT, mtime DATETIME, objid INTEGER PRIMARY KEY, tagid INTEGER, uid INTEGER REFERENCES user, bgcolor TEXT, euser TEXT, user TEXT, ecomment TEXT, comment TEXT, brief TEXT, omtime DATETIME ); INSERT INTO "event" VALUES('ci',2455542.1246875,1,NULL,NULL,NULL,NULL,'drh',NULL,'initial empty check-in',NULL,NULL); INSERT INTO "event" VALUES('ci',2455542.12638889,5,NULL,NULL,NULL,NULL,'drh',NULL,'add three initial files',NULL,NULL); INSERT INTO "event" VALUES('ci',2455542.12795139,8,NULL,NULL,NULL,NULL,'drh',NULL,'add four.txt',NULL,2455542.12795139); INSERT INTO "event" VALUES('ci',2455542.12873843,11,NULL,NULL,NULL,NULL,'drh',NULL,'add five.txt',NULL,NULL); INSERT INTO "event" VALUES('ci',2455542.1294213,13,NULL,NULL,NULL,NULL,'drh',NULL,'add six.txt',NULL,NULL); INSERT INTO "event" VALUES('ci',2455542.12998843,15,NULL,NULL,NULL,NULL,'drh',NULL,'changes to one.txt',NULL,NULL); INSERT INTO "event" VALUES('ci',2455542.13015046,17,NULL,NULL,NULL,NULL,'drh',NULL,'changes to two.txt',NULL,NULL); INSERT INTO "event" VALUES('ci',2455542.13335648,21,NULL,NULL,NULL,NULL,'drh',NULL,'changes to three.txt',NULL,2455542.13335648); INSERT INTO "event" VALUES('ci',2455542.13467593,24,NULL,NULL,NULL,NULL,'drh',NULL,'drop one; add new four',NULL,NULL); INSERT INTO "event" VALUES('ci',2455542.13571759,26,NULL,NULL,NULL,NULL,'drh',NULL,'change one',NULL,NULL); INSERT INTO "event" VALUES('ci',2455542.13622685,28,NULL,NULL,NULL,NULL,'drh',NULL,'edit three; rename two',NULL,NULL); INSERT INTO "event" VALUES('ci',2455542.13659722,30,NULL,NULL,NULL,NULL,'drh',NULL,'edit two-rename',NULL,NULL); INSERT INTO "event" VALUES('ci',2455542.19482639,32,NULL,NULL,NULL,NULL,'drh',NULL,'edit two',NULL,NULL); CREATE TABLE phantom( rid INTEGER PRIMARY KEY ); CREATE TABLE orphan( rid INTEGER PRIMARY KEY, baseline INTEGER ); CREATE TABLE unclustered( rid INTEGER PRIMARY KEY ); INSERT INTO "unclustered" VALUES(1); INSERT INTO "unclustered" VALUES(2); INSERT INTO "unclustered" VALUES(3); INSERT INTO "unclustered" VALUES(4); INSERT INTO "unclustered" VALUES(5); INSERT INTO "unclustered" VALUES(6); INSERT INTO "unclustered" VALUES(7); INSERT INTO "unclustered" VALUES(8); INSERT INTO "unclustered" VALUES(9); INSERT INTO "unclustered" VALUES(10); INSERT INTO "unclustered" VALUES(11); INSERT INTO "unclustered" VALUES(12); INSERT INTO "unclustered" VALUES(13); INSERT INTO "unclustered" VALUES(14); INSERT INTO "unclustered" VALUES(15); INSERT INTO "unclustered" VALUES(16); INSERT INTO "unclustered" VALUES(17); INSERT INTO "unclustered" VALUES(18); INSERT INTO "unclustered" VALUES(19); INSERT INTO "unclustered" VALUES(20); INSERT INTO "unclustered" VALUES(21); INSERT INTO "unclustered" VALUES(22); INSERT INTO "unclustered" VALUES(23); INSERT INTO "unclustered" VALUES(24); INSERT INTO "unclustered" VALUES(25); INSERT INTO "unclustered" VALUES(26); INSERT INTO "unclustered" VALUES(27); INSERT INTO "unclustered" VALUES(28); INSERT INTO "unclustered" VALUES(29); INSERT INTO "unclustered" VALUES(30); INSERT INTO "unclustered" VALUES(31); INSERT INTO "unclustered" VALUES(32); CREATE TABLE unsent( rid INTEGER PRIMARY KEY ); INSERT INTO "unsent" VALUES(31); INSERT INTO "unsent" VALUES(32); CREATE TABLE tag( tagid INTEGER PRIMARY KEY, tagname TEXT UNIQUE ); INSERT INTO "tag" VALUES(1,'bgcolor'); INSERT INTO "tag" VALUES(2,'comment'); INSERT INTO "tag" VALUES(3,'user'); INSERT INTO "tag" VALUES(4,'date'); INSERT INTO "tag" VALUES(5,'hidden'); INSERT INTO "tag" VALUES(6,'private'); INSERT INTO "tag" VALUES(7,'cluster'); INSERT INTO "tag" VALUES(8,'branch'); INSERT INTO "tag" VALUES(9,'closed'); INSERT INTO "tag" VALUES(10,'sym-trunk'); INSERT INTO "tag" VALUES(11,'sym-baseline'); INSERT INTO "tag" VALUES(12,'sym-br1'); INSERT INTO "tag" VALUES(13,'sym-br2'); INSERT INTO "tag" VALUES(14,'sym-br3'); INSERT INTO "tag" VALUES(15,'sym-chng1'); INSERT INTO "tag" VALUES(16,'sym-chng2'); INSERT INTO "tag" VALUES(17,'sym-chng3'); INSERT INTO "tag" VALUES(18,'sym-br4'); INSERT INTO "tag" VALUES(19,'sym-br5'); CREATE TABLE tagxref( tagid INTEGER REFERENCES tag, tagtype INTEGER, srcid INTEGER REFERENCES blob, origid INTEGER REFERENCES blob, value TEXT, mtime TIMESTAMP, rid INTEGER REFERENCE blob, UNIQUE(rid, tagid) ); INSERT INTO "tagxref" VALUES(8,2,1,1,'trunk',2455542.1246875,1); INSERT INTO "tagxref" VALUES(10,2,1,1,NULL,2455542.1246875,1); INSERT INTO "tagxref" VALUES(4,1,6,5,'2010-12-11 15:02:00',2455542.12704861,5); INSERT INTO "tagxref" VALUES(11,1,6,5,NULL,2455542.12704861,5); INSERT INTO "tagxref" VALUES(8,2,0,1,'trunk',2455542.1246875,5); INSERT INTO "tagxref" VALUES(10,2,0,1,NULL,2455542.1246875,5); INSERT INTO "tagxref" VALUES(8,2,9,8,'br1',2455542.1281713,8); INSERT INTO "tagxref" VALUES(12,2,9,8,NULL,2455542.1281713,8); INSERT INTO "tagxref" VALUES(4,1,9,8,'2010-12-11 15:04:15',2455542.1281713,8); INSERT INTO "tagxref" VALUES(10,0,9,8,NULL,2455542.1281713,8); INSERT INTO "tagxref" VALUES(8,2,11,11,'br2',2455542.12873843,11); INSERT INTO "tagxref" VALUES(13,2,11,11,NULL,2455542.12873843,11); INSERT INTO "tagxref" VALUES(11,0,11,11,NULL,2455542.12873843,11); INSERT INTO "tagxref" VALUES(10,0,11,11,NULL,2455542.12873843,11); INSERT INTO "tagxref" VALUES(8,2,13,13,'br3',2455542.1294213,13); INSERT INTO "tagxref" VALUES(14,2,13,13,NULL,2455542.1294213,13); INSERT INTO "tagxref" VALUES(11,0,13,13,NULL,2455542.1294213,13); INSERT INTO "tagxref" VALUES(10,0,13,13,NULL,2455542.1294213,13); INSERT INTO "tagxref" VALUES(4,1,18,15,'2010-12-11 15:07:11',2455542.13072917,15); INSERT INTO "tagxref" VALUES(15,1,18,15,NULL,2455542.13072917,15); INSERT INTO "tagxref" VALUES(4,1,19,17,'2010-12-11 15:07:25',2455542.13090278,17); INSERT INTO "tagxref" VALUES(16,1,19,17,NULL,2455542.13090278,17); INSERT INTO "tagxref" VALUES(8,2,0,1,'trunk',2455542.1246875,15); INSERT INTO "tagxref" VALUES(8,2,0,1,'trunk',2455542.1246875,17); INSERT INTO "tagxref" VALUES(8,2,0,1,'trunk',2455542.1246875,21); INSERT INTO "tagxref" VALUES(10,2,0,1,NULL,2455542.1246875,15); INSERT INTO "tagxref" VALUES(10,2,0,1,NULL,2455542.1246875,17); INSERT INTO "tagxref" VALUES(10,2,0,1,NULL,2455542.1246875,21); INSERT INTO "tagxref" VALUES(4,1,22,21,'2010-12-11 15:12:02',2455542.13353009,21); INSERT INTO "tagxref" VALUES(17,1,22,21,NULL,2455542.13353009,21); INSERT INTO "tagxref" VALUES(8,2,24,24,'br4',2455542.13467593,24); INSERT INTO "tagxref" VALUES(18,2,24,24,NULL,2455542.13467593,24); INSERT INTO "tagxref" VALUES(11,0,24,24,NULL,2455542.13467593,24); INSERT INTO "tagxref" VALUES(10,0,24,24,NULL,2455542.13467593,24); INSERT INTO "tagxref" VALUES(8,2,26,26,'br5',2455542.13571759,26); INSERT INTO "tagxref" VALUES(19,2,26,26,NULL,2455542.13571759,26); INSERT INTO "tagxref" VALUES(11,0,26,26,NULL,2455542.13571759,26); INSERT INTO "tagxref" VALUES(10,0,26,26,NULL,2455542.13571759,26); INSERT INTO "tagxref" VALUES(8,2,0,26,'br5',2455542.13571759,28); INSERT INTO "tagxref" VALUES(8,2,0,26,'br5',2455542.13571759,30); INSERT INTO "tagxref" VALUES(19,2,0,26,NULL,2455542.13571759,28); INSERT INTO "tagxref" VALUES(19,2,0,26,NULL,2455542.13571759,30); INSERT INTO "tagxref" VALUES(8,2,0,11,'br2',2455542.12873843,32); INSERT INTO "tagxref" VALUES(13,2,0,11,NULL,2455542.12873843,32); CREATE TABLE backlink( target TEXT, srctype INT, srcid INT, mtime TIMESTAMP, UNIQUE(target, srctype, srcid) ); CREATE TABLE attachment( attachid INTEGER PRIMARY KEY, isLatest BOOLEAN DEFAULT 0, mtime TIMESTAMP, src TEXT, target TEXT, filename TEXT, comment TEXT, user TEXT ); CREATE TABLE ticket( -- Do not change any column that begins with tkt_ tkt_id INTEGER PRIMARY KEY, tkt_uuid TEXT UNIQUE, tkt_mtime DATE, -- Add as many field as required below this line type TEXT, status TEXT, subsystem TEXT, priority TEXT, severity TEXT, foundin TEXT, private_contact TEXT, resolution TEXT, title TEXT, comment TEXT ); CREATE INDEX delta_i1 ON delta(srcid); 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); CREATE INDEX plink_i2 ON plink(cid,pid); CREATE INDEX event_i1 ON event(mtime); CREATE INDEX orphan_baseline ON orphan(baseline); CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime); CREATE INDEX backlink_src ON backlink(srcid, srctype); CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime); CREATE INDEX attachment_idx2 ON attachment(src); COMMIT; |
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 68 69 70 71 | # # 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 # #################################################################### test_setup fossil ls test merge_multi-0 {[normalize_result] eq {}} write_file f1 "f1 line" fossil add f1 fossil commit -m "base file" fossil ls test merge_multi-1 {[normalize_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 {[normalize_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 {[normalize_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 {[normalize_result] eq {f1 f2 f3 f4}} ############################################################################### test_cleanup |
Added test/merge_exe.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 | # # Copyright (c) 2016 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/ # ############################################################################ # # Testing changes to a file's execute bit caused by a merge # if {$tcl_platform(platform) eq "unix"} { proc setx {fn isexe} { file attributes $fn -permissions [expr {$isexe ? "+" : "-"}]x } proc test_exe {fn expected} { test merge_exe-$fn {[file executable $fn]==$expected} } } else { # WARNING: This is a hack for setting and testing a file's execute bit # on Windows. Never operate directly on Fossil database files like this # unless you really need to and really know what you're doing. proc query {sql} { return [exec $::fossilexe sqlite3 --no-repository _FOSSIL_ $sql] } proc setx {fn isexe} { set isexe [expr {bool($isexe)}] query "UPDATE vfile SET isexe=$isexe WHERE pathname='$fn'" } proc test_exe {fn expected} { set result [query "SELECT isexe FROM vfile WHERE pathname='$fn'"] test merge_exe-$fn {$result==$expected} } } test_setup write_file f1 "line" write_file f2 "line" write_file f3 "line" write_file f4 "line" fossil addremove setx f3 1 setx f4 1 fossil commit -m "add files" write_file f0 "f0" fossil add f0 setx f0 1 fossil mv --hard f1 f1n setx f1n 1 write_file f2 "line\nline2" setx f2 1 write_file f3 "line\nline2" setx f3 0 setx f4 0 fossil commit -b b -m "changes" fossil update trunk write_file f3 "line3\nline" fossil commit -m "edit f3" fossil merge b test_status_list merge_exe-mrg $RESULT { EXECUTABLE f1 EXECUTABLE f2 UNEXEC f3 UNEXEC f4 UPDATE f2 MERGE f3 RENAME f1 -> f1n ADDED f0 } foreach {fn isexe} {f0 1 f1n 1 f2 1 f3 0 f4 0} { test_exe $fn $isexe } ############################################################################### test_cleanup |
Added test/merge_renames.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 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 | # # Tests for merging with renames # # proc commit_id {version} { regexp -line {^artifact:\s+(\S+)} [fossil whatis $version] - id return $id } require_no_open_checkout ###################################### # Test 1 # # Reported: Ticket [554f44ee74e3d] # ###################################### test_setup write_file f1 "line" fossil add f1 fossil commit -m "c1" fossil tag add pivot current write_file f1 "line2" fossil commit -m "c2" write_file f1 "line3" fossil commit -m "c3" write_file f1 "line4" fossil commit -m "c4" write_file f1 "line5" fossil commit -m "c4" write_file f1 "line6" fossil commit -m "c4" fossil update pivot fossil mv --hard f1 f2 fossil commit -b rename -m "c5" fossil merge trunk fossil commit -m "trunk merged" fossil update pivot write_file f3 "someline" fossil add f3 fossil commit -b branch2 -m "newbranch" fossil merge trunk test_status_list merge_renames-1 $RESULT {UPDATE f1} ###################################### # Test 2 # # Reported: Ticket [74413366fe5067] # ###################################### test_setup 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 --hard 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 test_status_list merge_renames-2 $RESULT {UPDATE f1} ###################################### # Test 3 # # Reported: Ticket [30b28cf351] # ###################################### test_setup 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 --hard 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 test_status_list merge_renames-3 $RESULT {UPDATE f1} ###################################### # Test 4 # # Reported: Ticket [67176c3aa4] # ###################################### test_setup write_file f1 "f1" fossil add f1 fossil commit -m "add f1" write_file f1 "f1.1" fossil commit --branch b -m "change f1" fossil update trunk fossil mv --hard f1 f2 fossil commit -m "f1 -> f2" fossil merge b test_status_list merge_renames-4-1 $RESULT {UPDATE f2} test_file_contents merge_renames-4-2 f2 "f1.1" ###################################### # Test 5 # # Handle Rename/Add via Merge # ###################################### test_setup 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 --hard 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 test_status_list merge_renames-5-1 $RESULT { RENAME f1 -> f2 ADDED f1 } fossil commit -m "trunk merged, should have 3 files" fossil ls test merge_renames-5-2 {[normalize_result] eq {f1 f2 f3}} ##################################### # Test 6 # # Merging a branch multiple times # ##################################### test_setup write_file f1 "f1" fossil add f1 fossil commit -m "add f1" fossil mv --hard f1 f2 fossil commit -b b -m "f1 -> f2" fossil update trunk write_file f3 "f3" write_file f4 "f4" fossil add f3 f4 fossil ci -m "add f3, f4" fossil mv --hard f3 f3-old fossil mv --hard f4 f3 fossil mv --hard f3-old f4 fossil ci -m "swap f3 and f4" write_file f1 "f1.1" fossil commit -m "edit f1" fossil update b fossil merge trunk fossil commit -m "merge trunk" fossil update trunk write_file f1 "f1.2" write_file f3 "f3.1" write_file f4 "f4.1" fossil commit -m "edit f1, f4" fossil update b fossil merge trunk test_status_list merge_renames-6-1 $RESULT { UPDATE f2 UPDATE f3 UPDATE f4 } test_file_contents merge_renames-6-2 f2 "f1.2" test_file_contents merge_renames-6-3 f3 "f3.1" test_file_contents merge_renames-6-4 f4 "f4.1" ######################################################################## # Test 7 # # Merging with an uncommitted rename of a file that has been renamed # # in the merged branch and adding a new file with the original name # ######################################################################## test_setup write_file f1 "f1" fossil add f1 fossil commit -m "add f1" fossil mv --hard f1 f2 write_file f2 "f2" fossil commit -b b -m "f1 -> f2, edit f2" fossil update trunk fossil mv --hard f1 f3 write_file f1 "f1.1" fossil add f1 fossil merge b test_status_list merge_renames-7-1 $RESULT {UPDATE f3} test_file_contents merge_renames-7-2 f1 "f1.1" test_file_contents merge_renames-7-3 f3 "f2" ###################################################### # Test 8 # # Merging two branches that both add the same file # ###################################################### test_setup write_file f1 "f1.1" fossil add f1 fossil commit -b b1 -m "add f1" fossil update trunk write_file f1 "f1.2" fossil add f1 fossil commit -b b2 -m "add f1" fossil update trunk fossil merge b1 fossil merge b2 -expectError test_status_list merge_renames-8-1 $RESULT { MERGE f1 WARNING: 1 merge conflicts } fossil revert fossil merge --integrate b1 fossil merge b2 -expectError test_status_list merge_renames-8-2 $RESULT { MERGE f1 WARNING: 1 merge conflicts } ############################################# # Test 9 # # Merging a delete/rename/add combination # ############################################# test_setup write_file f1 "f1" write_file f2 "f2" fossil add f1 f2 fossil commit -m "add files" fossil rm --hard f2 fossil commit -b b -m "delete f2" fossil mv --hard f1 f2 fossil commit -m "f1 -> f2" write_file f1 "f1.1" fossil add f1 fossil commit -m "add new f1" fossil update trunk fossil merge b set expectedMerge { DELETE f2 RENAME f1 -> f2 ADDED f1 } test_status_list merge_renames-9-1 $RESULT $expectedMerge fossil changes test_status_list merge_renames-9-2 $RESULT " MERGED_WITH [commit_id b] ADDED_BY_MERGE f1 RENAMED f1 -> f2 DELETED f2 -> f2 (overwritten by rename) " test_file_contents merge_renames-9-3 f1 "f1.1" test_file_contents merge_renames-9-4 f2 "f1" # Undo and ensure a dry run merge results in no changes fossil undo test_status_list merge_renames-9-5 $RESULT { UNDO f1 UNDO f2 } fossil merge -n b -expectError test_status_list merge_renames-9-6 $RESULT " $expectedMerge REMINDER: this was a dry run - no files were actually changed. " test merge_renames-9-7 {[fossil changes] eq ""} ################################################################### # Test 10 # # Merge swapped filenames, backout the swap, then merge changes # ################################################################### test_setup write_file f1 "f1" write_file f2 "f2" fossil add f1 f2 fossil commit -m "add files" ;# N fossil mv --hard f1 f1-tmp fossil mv --hard f2 f1 fossil mv --hard f1-tmp f2 fossil commit -b b -m "swap f1, f2" ;# P fossil update trunk fossil merge b test_status_list merge_renames-10-1 $RESULT { RENAME f1 -> f2 RENAME f2 -> f1 } test_file_contents merge_renames-10-2 f1 "f2" test_file_contents merge_renames-10-3 f2 "f1" fossil commit -m "merge b" fossil update b write_file f1 f1.1 write_file f2 f2.1 fossil commit -m "edit" ;# M fossil update trunk fossil merge --backout trunk test_status_list merge_renames-10-4 $RESULT { RENAME f1 -> f2 RENAME f2 -> f1 } test_file_contents merge_renames-10-5 f1 "f1" test_file_contents merge_renames-10-6 f2 "f2" test_status_list merge_renames-10-7 [fossil changes] " RENAMED f1 -> f2 RENAMED f2 -> f1 BACKOUT [commit_id trunk] " fossil commit -m "swap back" ;# V fossil merge b test_status_list merge_renames-10-8 $RESULT { UPDATE f1 UPDATE f2 } test_file_contents merge_renames-10-9 f1 "f2.1" test_file_contents merge_renames-10-10 f2 "f1.1" ############################################ # Test 11 # # Specifying a baseline # ############################################ test_setup write_file f1 "line" fossil add f1 fossil commit -m "add f1" write_file f1 "line\nline2" fossil commit -b b -m "edit f2" --tag p1 fossil mv --hard f1 f2 fossil commit -m "f1 -> f2" write_file f2 "line\nline2\nline3" fossil commit -m "edit f2" --tag p2 write_file f2 "line\nline2\nline3\nline4" fossil commit -m "edit f2" fossil update trunk fossil merge --baseline p1 b test_status_list merge_renames-11-1 $RESULT { MERGE f1 RENAME f1 -> f2 } test_file_contents merge_renames-11-2 f2 "line\nline3\nline4" fossil revert fossil merge --baseline p2 b test_status_list merge_renames-11-3 $RESULT {MERGE f1} test_file_contents merge_renames-11-4 f1 "line\nline4" ################################################################# # Test 12 # # Merge involving a pivot that isn't a first-parent ancestor # # of either the checked-out commit or the commit being merged # ################################################################# test_setup write_file f1 "f1\n" fossil add f1 fossil commit -m "add f1" --tag n fossil mv --hard f1 f1n fossil commit -m "f1 -> f1n" fossil mv --hard f1n f1v write_file f1v "f1v\n" fossil commit -b v -m "f1n -> f1v, edit f1v" fossil update trunk fossil mv --hard f1n f1m fossil commit -b m -m "f1n -> f1m" fossil update n fossil mv --hard f1 f1p write_file f1p "f1\np" fossil commit -b p -m "f1 -> f1p, edit f1p" fossil update m fossil merge p test_status_list merge_renames-12-1 $RESULT {UPDATE f1m} test_file_contents merge_renames-12-2 f1m "f1\np" fossil commit -m "merge p" write_file f1m "f1\nm" fossil commit -m "edit f1m" fossil update v fossil merge p test_status_list merge_renames-12-3 $RESULT {MERGE f1v} test_file_contents merge_renames-12-4 f1v "f1v\np" fossil commit -m "merge p" fossil merge m test_status_list merge_renames-12-5 $RESULT {MERGE f1v} test_file_contents merge_renames-12-6 f1v "f1v\nm" fossil commit -m "merge m" ################################################################# # Test 13 # # Merge in add+rename that happened on a branch, then merged # # back to branch, and again to trunk, then merge in branch. # ################################################################# set repoDir [test_setup] write_file f1 "line1" fossil add f1 fossil commit -m "add f1" --tag c1 write_file f2 "line1" fossil add f2 fossil commit -m "add f2 on branch" -b b --tag c2 fossil update trunk test_status_list merge_renames_13-1 $RESULT {REMOVE f2} write_file f1 "line1\nline2\n" fossil commit -m "edit f1 on trunk" --tag c3 fossil update b test_status_list merge_renames_13-2 $RESULT { UPDATE f1 ADD f2 } fossil merge trunk fossil commit -m "merge trunk" --tag c4 fossil mv --hard f2 f2n test_status_list merge_renames-13-3 $RESULT " RENAME f2 f2n MOVED_FILE [file normalize $repoDir]/f2 " fossil commit -m "renamed f2->f2n" --tag c5 fossil update trunk fossil merge b test_status_list merge_renames-13-4 $RESULT {ADDED f2n} fossil commit -m "merge f2n" --tag m1 --tag c6 fossil update b write_file f1 "line1\nline3\nline2" fossil commit -m "edit f1 on b" --tag c7 fossil update trunk write_file f1 "line1\nline3\nline2\nline4" fossil commit -m "edit f1 on trunk" --tag c8 fossil update b fossil merge trunk test_status_list merge_renames-13-5 $RESULT {MERGE f1} fossil commit -m "merge trunk" --tag c9 write_file f1 "line1\nline3\nline4" fossil commit -m "edit f1 on b" --tag c10 fossil update m1 fossil merge b test_status_list merge_renames-13-6 $RESULT { UPDATE f1 } test_file_contents merge_renames-13-7 f2n "line1" fossil revert test_status_list merge_renames-13-8 $RESULT { REVERT f1 } fossil update trunk fossil merge --integrate b test_status_list merge_renames-13-9 $RESULT { UPDATE f1 } test_file_contents merge_renames-13-10 f2n "line1" fossil revert test_status_list merge_renames-13-11 $RESULT { REVERT f1 } ###################################### # # 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 ############################################################################### test_cleanup |
Added test/merge_renames_2.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 | # # Tests for merging with renames # # proc commit_id {version} { regexp -line {^artifact:\s+(\S+)} [fossil whatis $version] - id return $id } require_no_open_checkout ################################################################# # Test 1 # # https://fossil-scm.org/forum/forumpost/549700437b # ################################################################# test_setup write_file file1 "file1\n" fossil add file1 fossil commit -m "added file1" write_file file2 "file2\n" fossil add file2 fossil commit -m "added file2" --branch added write_file file2 "edit file2 on added\n" fossil commit -m "edited file2" fossil mv --hard file2 file2.renamed fossil commit -m "renamed file2" --branch renamed fossil branch new branched current write_file file2.renamed "edit file2.renamed on renamed\n" fossil commit -m "edited file2.renamed" fossil update trunk fossil merge renamed fossil commit -m "merged from renamed" write_file file2.renamed "edit file2.renamed on trunk (1)\n" fossil commit -m "edited file2.renamed on trunk (1)" fossil update branched fossil merge trunk test_status_list merge_renames_2-1.1 $RESULT {UPDATE file2.renamed} fossil commit -m "merged edit from trunk (1:this merge succeeded)" fossil update trunk write_file file2.renamed "edit2 file2.renamed on trunk (2)\n" fossil commit -m "edited file2.renamed on trunk (2)" fossil update branched fossil merge trunk test_status_list merge_renames_2-1.2 $RESULT {UPDATE file2.renamed} test_cleanup |
Added test/merge_warn.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 | # # Copyright (c) 2016 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/ # ############################################################################ # # Testing "merge" command warnings # test_setup write_file f1 "f1" fossil add f1 fossil commit -m "add f1" --tag pivot write_file f2 "f2" fossil add f2 fossil commit -m "add f2" fossil update pivot fossil rm --hard f1 write_file f2 "f2.1" write_file f3 "f3" fossil add f2 f3 fossil commit -b b -m "delete f1, add f2 and f3" --tag mrg write_file f4 "f4" fossil add f4 fossil commit -m "add f4" fossil update trunk write_file f1 "f1.1" write_file f3 "f3.1" fossil merge --integrate mrg -expectError test_status_list merge_warn-1 $RESULT { WARNING: 1 unmanaged files were overwritten WARNING: 2 merge conflicts DELETE f1 MERGE f2 ADDED f3 (overwrites an unmanaged file), original copy backed up locally WARNING: local edits lost for f1 } test merge_warn-2 { [string first "ignoring --integrate: mrg is not a leaf" $RESULT]>=0 } ############################################################################### test_cleanup |
Added test/mv-rm.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 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 | # # 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/ # ############################################################################ # # MV / RM Commands # set path [file dirname [info script]] require_no_open_checkout ######################################## # Setup: Add Files and Commit # ######################################## test_setup; set rootDir [file normalize [pwd]] set undoMsg "\n \"fossil undo\" is\ available to undo changes to the\ working checkout." write_file f1 "f1" write_file f2 "f2" write_file f3 "f3" write_file f4 "f4" write_file f5 "f5" write_file f6 "f6" write_file f7 "f7" write_file f8 "f8" file mkdir [file join $rootDir subdirA] # NOTE: There are no files in subdirA. file mkdir [file join $rootDir subdirB] write_file [file join $rootDir subdirB f9] "f9" file mkdir [file join $rootDir subdirC] write_file [file join $rootDir subdirC f10] "f10" write_file [file join $rootDir subdirC f11] "f11" write_file f12 "f12" file mkdir [file join $rootDir subdirE a] write_file [file join $rootDir subdirE a f14] "f14" fossil add f1 f2 f3 f4 f5 f6 f7 f8 subdirB/f9 subdirC/f10 subdirC/f11 f12 fossil add subdirE/a/f14 fossil commit -m "c1" ######################################## # Test 1: Soft Move Relative Directory # ######################################## file mkdir [file join $rootDir subdir1] cd [file join $rootDir subdir1] fossil mv ../f1 . test mv-soft-relative-1 {$RESULT eq "RENAME f1 subdir1/f1"} fossil revert test mv-soft-relative-2 { [normalize_result] eq "DELETE subdir1/f1\nREVERT f1${undoMsg}" } cd $rootDir ################################### # Test 2: Soft Move Relative File # ################################### file mkdir [file join $rootDir subdir2] cd [file join $rootDir subdir2] fossil mv ../f2 ./f2 test mv-soft-relative-3 {$RESULT eq "RENAME f2 subdir2/f2"} fossil revert test mv-soft-relative-4 { [normalize_result] eq "DELETE subdir2/f2\nREVERT f2${undoMsg}" } cd $rootDir ######################################## # Test 3: Hard Move Relative Directory # ######################################## file mkdir [file join $rootDir subdir3] cd [file join $rootDir subdir3] fossil mv --hard ../f3 . test mv-hard-relative-1 { [normalize_result] eq "RENAME f3 subdir3/f3\nMOVED_FILE ${rootDir}/f3" } fossil revert test mv-hard-relative-2 { [normalize_result] eq "DELETE subdir3/f3\nREVERT f3${undoMsg}" } cd $rootDir ################################### # Test 4: Hard Move Relative File # ################################### file mkdir [file join $rootDir subdir4] cd [file join $rootDir subdir4] fossil mv --hard ../f4 ./f4 test mv-hard-relative-3 { [normalize_result] eq "RENAME f4 subdir4/f4\nMOVED_FILE ${rootDir}/f4" } fossil revert test mv-hard-relative-4 { [normalize_result] eq "DELETE subdir4/f4\nREVERT f4${undoMsg}" } cd $rootDir ######################################## # Test 5: Soft Move Absolute Directory # ######################################## file mkdir [file join $rootDir subdir5] cd [file join $rootDir subdir5] fossil mv [file join $rootDir f5] [file join $rootDir subdir5] test mv-soft-absolute-1 {$RESULT eq "RENAME f5 subdir5/f5"} fossil revert test mv-soft-absolute-2 { [normalize_result] eq "DELETE subdir5/f5\nREVERT f5${undoMsg}" } cd $rootDir ################################### # Test 6: Soft Move Absolute File # ################################### file mkdir [file join $rootDir subdir6] cd [file join $rootDir subdir6] fossil mv [file join $rootDir f6] [file join $rootDir subdir6 f6] test mv-soft-absolute-3 {$RESULT eq "RENAME f6 subdir6/f6"} fossil revert test mv-soft-absolute-4 { [normalize_result] eq "DELETE subdir6/f6\nREVERT f6${undoMsg}" } cd $rootDir ######################################## # Test 7: Hard Move Absolute Directory # ######################################## file mkdir [file join $rootDir subdir7] cd [file join $rootDir subdir7] fossil mv --hard [file join $rootDir f7] [file join $rootDir subdir7] test mv-hard-absolute-1 { [normalize_result] eq "RENAME f7 subdir7/f7\nMOVED_FILE ${rootDir}/f7" } fossil revert test mv-hard-absolute-2 { [normalize_result] eq "DELETE subdir7/f7\nREVERT f7${undoMsg}" } cd $rootDir ################################### # Test 8: Hard Move Absolute File # ################################### file mkdir [file join $rootDir subdir8] cd [file join $rootDir subdir8] fossil mv --hard [file join $rootDir f8] [file join $rootDir subdir8 f8] test mv-hard-absolute-3 { [normalize_result] eq "RENAME f8 subdir8/f8\nMOVED_FILE ${rootDir}/f8" } fossil revert test mv-hard-absolute-4 { [normalize_result] eq "DELETE subdir8/f8\nREVERT f8${undoMsg}" } cd $rootDir ########################################## # Test 9: Soft Remove Relative Directory # ########################################## file mkdir [file join $rootDir subdir1] cd [file join $rootDir subdir1] fossil rm ../subdirA test rm-soft-relative-1 {$RESULT eq ""} fossil rm ../subdirB test rm-soft-relative-2 {$RESULT eq "DELETED subdirB/f9"} fossil rm ../subdirC test rm-soft-relative-3 { [normalize_result] eq "DELETED subdirC/f10\nDELETED subdirC/f11" } fossil revert test rm-soft-relative-4 { [normalize_result] eq \ "REVERT subdirB/f9\nREVERT subdirC/f10\nREVERT subdirC/f11${undoMsg}" } cd $rootDir ###################################### # Test 10: Soft Remove Relative File # ###################################### file mkdir [file join $rootDir subdir2] cd [file join $rootDir subdir2] fossil rm ../f2 test rm-soft-relative-5 {$RESULT eq "DELETED f2"} fossil revert test rm-soft-relative-6 { [normalize_result] eq "REVERT f2${undoMsg}" } cd $rootDir ########################################### # Test 11: Hard Remove Relative Directory # ########################################### file mkdir [file join $rootDir subdir3] cd [file join $rootDir subdir3] fossil rm --hard ../subdirA test rm-hard-relative-1 { [normalize_result] eq "" } fossil rm --hard ../subdirB test rm-hard-relative-2 { [normalize_result] eq \ "DELETED subdirB/f9\nDELETED_FILE ${rootDir}/subdirB/f9" } fossil rm --hard ../subdirC test rm-hard-relative-3 { [normalize_result] eq \ "DELETED subdirC/f10\nDELETED subdirC/f11\nDELETED_FILE\ ${rootDir}/subdirC/f10\nDELETED_FILE ${rootDir}/subdirC/f11" } fossil revert test rm-hard-relative-4 { [normalize_result] eq \ "REVERT subdirB/f9\nREVERT subdirC/f10\nREVERT subdirC/f11${undoMsg}" } cd $rootDir ###################################### # Test 12: Hard Remove Relative File # ###################################### file mkdir [file join $rootDir subdir4] cd [file join $rootDir subdir4] fossil rm --hard ../f4 test rm-hard-relative-5 { [normalize_result] eq "DELETED f4\nDELETED_FILE ${rootDir}/f4" } fossil revert test rm-hard-relative-6 { [normalize_result] eq "REVERT f4${undoMsg}" } cd $rootDir ########################################### # Test 13: Soft Remove Absolute Directory # ########################################### file mkdir [file join $rootDir subdir5] cd [file join $rootDir subdir5] fossil rm [file join $rootDir subdirA] test rm-soft-absolute-1 {$RESULT eq ""} fossil rm [file join $rootDir subdirB] test rm-soft-absolute-2 {$RESULT eq "DELETED subdirB/f9"} fossil rm [file join $rootDir subdirC] test rm-soft-absolute-3 { [normalize_result] eq "DELETED subdirC/f10\nDELETED subdirC/f11" } fossil revert test rm-soft-absolute-4 { [normalize_result] eq \ "REVERT subdirB/f9\nREVERT subdirC/f10\nREVERT subdirC/f11${undoMsg}" } cd $rootDir ###################################### # Test 14: Soft Remove Absolute File # ###################################### file mkdir [file join $rootDir subdir6] cd [file join $rootDir subdir6] fossil rm [file join $rootDir f6] test rm-soft-absolute-5 {$RESULT eq "DELETED f6"} fossil revert test rm-soft-absolute-6 { [normalize_result] eq "REVERT f6${undoMsg}" } cd $rootDir ########################################### # Test 15: Hard Remove Absolute Directory # ########################################### file mkdir [file join $rootDir subdir7] cd [file join $rootDir subdir7] fossil rm --hard [file join $rootDir subdirA] test rm-hard-absolute-1 {$RESULT eq ""} fossil rm --hard [file join $rootDir subdirB] test rm-hard-absolute-2 { [normalize_result] eq \ "DELETED subdirB/f9\nDELETED_FILE ${rootDir}/subdirB/f9" } fossil rm --hard [file join $rootDir subdirC] test rm-hard-absolute-3 { [normalize_result] eq \ "DELETED subdirC/f10\nDELETED subdirC/f11\nDELETED_FILE\ ${rootDir}/subdirC/f10\nDELETED_FILE ${rootDir}/subdirC/f11" } fossil revert test rm-hard-absolute-4 { [normalize_result] eq \ "REVERT subdirB/f9\nREVERT subdirC/f10\nREVERT subdirC/f11${undoMsg}" } cd $rootDir ###################################### # Test 16: Hard Remove Absolute File # ###################################### file mkdir [file join $rootDir subdir8] cd [file join $rootDir subdir8] fossil rm --hard [file join $rootDir f8] test rm-hard-absolute-5 { [normalize_result] eq "DELETED f8\nDELETED_FILE ${rootDir}/f8" } fossil revert test rm-hard-absolute-6 { [normalize_result] eq "REVERT f8${undoMsg}" } cd $rootDir ####################################### # Test 17: Move File to New Directory # ####################################### fossil mv --hard f12 d2/f13 test mv-file-new-directory-1 { [normalize_result] eq "RENAME f12 d2/f13\nMOVED_FILE ${rootDir}/f12" } test mv-file-new-directory-2 {[file size d2/f13] == 3} test mv-file-new-directory-3 {[read_file d2/f13] eq "f12"} fossil revert test mv-file-new-directory-4 { [normalize_result] eq "DELETE d2/f13\nREVERT f12${undoMsg}" } test mv-file-new-directory-5 {[file size f12] == 3} test mv-file-new-directory-6 {[read_file f12] eq "f12"} cd $rootDir ############################################ # Test 18: Move Directory to New Directory # ############################################ fossil mv --hard subdirC subdirD test mv-file-new-directory-7 { [normalize_result] eq "RENAME subdirC/f10 subdirD/f10\nRENAME subdirC/f11 subdirD/f11\nMOVED_FILE ${rootDir}/subdirC/f10\nMOVED_FILE ${rootDir}/subdirC/f11" } test mv-file-new-directory-8 {[file size subdirD/f10] == 3} test mv-file-new-directory-9 {[read_file subdirD/f10] eq "f10"} test mv-file-new-directory-10 {[file size subdirD/f11] == 3} test mv-file-new-directory-11 {[read_file subdirD/f11] eq "f11"} fossil revert test mv-file-new-directory-12 { [normalize_result] eq "DELETE subdirD/f10\nDELETE subdirD/f11\nREVERT subdirC/f10\nREVERT subdirC/f11${undoMsg}" } test mv-file-new-directory-13 {[file size subdirC/f10] == 3} test mv-file-new-directory-14 {[read_file subdirC/f10] eq "f10"} test mv-file-new-directory-15 {[file size subdirC/f11] == 3} test mv-file-new-directory-16 {[read_file subdirC/f11] eq "f11"} cd $rootDir ############################################################################### # Test 19: Follow-up Soft Rename of a Directory Already Renamed on Filesystem # ############################################################################### file rename [file join $rootDir subdirE/a] [file join $rootDir subdirE/a_renamed] fossil mv subdirE/a subdirE/a_renamed test mv-soft-already-renamed-directory-1 { [normalize_result] eq "RENAME subdirE/a/f14 subdirE/a_renamed/f14" } test mv-soft-already-renamed-directory-2 {[file size subdirE/a_renamed/f14] == 3} test mv-soft-already-renamed-directory-3 {[read_file subdirE/a_renamed/f14] eq "f14"} fossil revert test mv-soft-already-renamed-directory-4 { [normalize_result] eq "DELETE subdirE/a_renamed/f14\nREVERT subdirE/a/f14${undoMsg}" } test mv-soft-already-renamed-directory-5 {[file size subdirE/a/f14] == 3} test mv-soft-already-renamed-directory-6 {[read_file subdirE/a/f14] eq "f14"} file delete -force [file join $rootDir subdirE/a_renamed] cd $rootDir ############################################################################### test_cleanup |
Added test/release-checklist.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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | <title>Release Checklist</title> This file describes the testing procedures for Fossil prior to an official release. <ol> <li><p> From within a checkout of the Fossil tree, display this file with the command "<b>fossil ui --page doc/ckout/test/release-checklist.wiki</b>". That is the only way the links below will work. <li><p> From a private directory (not the source tree) run "<b>tclsh $SRC/test/tester.tcl $FOSSIL</b>" where $FOSSIL is the name of the executable under test and $SRC is the source tree. Verify that there are no errors. <li><p> Click on each of the links in in the [./graph-test-1.wiki] document and verify that all graphs are rendered correctly. <li><p> Click on each of the links in in the [./graph-test-2.md] document and verify that all graphs are rendered correctly. <ol type="a"> <li> Also view the same check-ins on a /timeline view by clicking on the date for each check-in in the /info view, as the graph rendering is slightly different. </ol> <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: <pre><b>fossil test-name-changes --debug b120bc8b262ac 374920b20944b </b></pre> <li><p> Compile for all of the following platforms: <ol type="a"> <li> Linux x86 <li> Linux x86_64 <li> Mac x86 <li> Mac x86_64 <li> Windows (mingw) <li> Windows (vc++) <li> OpenBSD </ol> <li><p> Run at least one occurrence of the following commands on every platform: <ol type="a"> <li> <b>fossil rebuild</b> <li> <b>fossil sync</b> <li> <b>fossil test-integrity</b> </ol> <li><p> Run the following commands on Linux and verify no major memory leaks and no run-time errors or warnings (except for the well-known jump on an uninitialized value that occurs within zlib).</p> <ol type="a"> <li> <b>valgrind fossil rebuild</b> <li> <b>valgrind fossil sync</b> </ol> <p>Achtung: make sure to point valgrind to the proper fossil binary so that it does not pick up another from the PATH.</p> <li><p> Inspect [http://fossil-scm.org/home/vdiff?from=release&to=trunk&sbs=1|all code changes since the previous release], paying particular attention to the following details: <ol type="a"> <li> Can a malicious HTTP request cause a buffer overrun. <li> Can a malicious HTTP request expose privileged information to unauthorized users. </ol> <li><p> Use the release candidate version of fossil in production on the [http://fossil-scm.org/] website for at least 48 hours (without incident) prior to making the release official. <li><p> Verify that the minimum SQLite version requirement is up-to-date: <ol type="a"> <li> Check the version number in the line starting "define MINIMUM_SQLITE_VERSION" near the top of [/file?name=auto.def&ci=tip | auto.def] <li> Check the output of <b>./configure --print-minimum-sqlite-version</b> </ol> <li><p> Verify that the [../www/changes.wiki | Change Log] is correct and up-to-date. </ol> <hr> Upon successful completion of all tests above, tag the release candidate with the "release" tag and set its background color to "#d0c0ff". Update the www/changes.wiki file to show the date of the release. |
Added test/reserved-names.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 | # # Copyright (c) 2020 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 for reserved names. # test_setup ############################################################################### set reserved_names_tests [list \ {0 {}} \ {0 a.fslckout} \ {1 .fslckout} \ {1 .FSlckOUT} \ {2 a/.fslckout} \ {0 .fslckout/b} \ {0 fslckout} \ {0 .fslckoutx} \ {1 _FOSSIL_} \ {0 _FOSSIL} \ {0 FOSSIL_} \ {0 FOSSIL_} \ {0 a_FOSSIL_} \ {0 _FOSSIL__} \ {0 __FOSSIL__} \ {0 __FOssIL__} \ {0 _FOSSIL_/a} \ {2 a/_FOSSIL_} \ {2 _FOSSIL_/c/.fslckout} \ {2 _FOSSIL_/c/.fslckout/_FOSSIL_} \ {0 _FOSSIL_/c/.fslckout/._FOSSIL_t} \ {0 _FOSSIL_/c/.fslckout/t._FOSSIL_} \ {0 a} \ {0 a/b} \ {0 a/b/c} \ {0 a/b/c/} \ {0 a/_FOSSIL/} \ {0 a/fslckout/} \ {0 a/_fslckout/} \ {0 _FOSSIL-wal} \ {0 _FOSSIL-shm} \ {0 _FOSSIL-journal} \ {0 _FOSSIL_-wal/a} \ {0 _FOSSIL_-shm/a} \ {0 _FOSSIL_-journal/a} \ {1 _FOSSIL_-wal} \ {1 _FOSSIL_-shm} \ {1 _FOSSIL_-journal} \ {2 a/_FOSSIL_-wal} \ {2 a/_FOSSIL_-shm} \ {2 a/_FOSSIL_-journal} \ {0 .fslckout-wal/a} \ {0 .fslckout-shm/a} \ {0 .fslckout-journal/a} \ {1 .fslckout-wal} \ {1 .fslckout-shm} \ {1 .fslckout-journal} \ {2 a/.fslckout-wal} \ {2 a/.fslckout-shm} \ {2 a/.fslckout-journal} \ ] ############################################################################### set testNo 0 foreach reserved_names_test $reserved_names_tests { incr testNo set reserved_result [lindex $reserved_names_test 0] set reserved_name [lindex $reserved_names_test 1] fossil test-is-reserved-name $reserved_name test reserved-result-$testNo { [lindex [normalize_result] 0] eq $reserved_result } test reserved-name-$testNo { [lindex [normalize_result] 1] eq $reserved_name } fossil test-is-reserved-name [string toupper $reserved_name] test reserved-result-upper-$testNo { [lindex [normalize_result] 0] eq $reserved_result } test reserved-name-upper-$testNo { [lindex [normalize_result] 1] eq [string toupper $reserved_name] } fossil test-is-reserved-name [string tolower $reserved_name] test reserved-result-lower-$testNo { [lindex [normalize_result] 0] eq $reserved_result } test reserved-name-lower-$testNo { [lindex [normalize_result] 1] eq [string tolower $reserved_name] } } ############################################################################### test_cleanup |
Added test/revert.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 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 | # # Copyright (c) 2013 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 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 } require_no_open_checkout test_setup # 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 REVERT f1 REVERT f2 REVERT 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 f3 -> f3n } -addremove { ADDED f0 } -exists {f0 f2 f3n} -notexists f3 revert-test 1-3 f1 { REVERT f1 } -changes { ADDED f0 EDITED f2 RENAMED f3 -> f3n } -exists {f0 f1 f2 f3n} -notexists f3 revert-test 1-4 f2 { REVERT f2 } -changes { ADDED f0 DELETED f1 RENAMED f3 -> 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 { REVERT f3 DELETE f3n } -changes { ADDED f0 DELETED f1 EDITED f2 } -exists {f0 f2 f3} -notexists {f1 f3n} revert-test 1-6 f3n { REVERT 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 REVERT f2 REVERT 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. # test_setup 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 {} { REVERT f1 DELETE f1n } -exists {f1} -notexists {f1n} # Test reverting a rename in the repo but not completed in the file # system test_setup write_file f1 "f1" fossil add f1 fossil commit -m "add f1" fossil mv --soft f1 f1new test 3-mv-1 {[file exists f1]} test 3-mv-2 {![file exists f1new]} revert-test 3-1 {} { REVERT f1 DELETE f1new } -exists {f1} -notexists {f1n} # Test reverting of files under a sub-directory test_setup file mkdir d write_file d/f1 "d/f1" write_file d/f2 "d/f2" write_file d/f3 "d/f3" write_file d/f4 "d/f4" fossil add d fossil delete d/f1 fossil commit -m "d/f2 d/f3 d/f4" ## Changes to revert fossil add d/f1 write_file d/f2 "4-1:d/f2" fossil changes d/f2 fossil delete --soft d/f3 revert-test 4-1 {d/f1} { UNMANAGE d/f1 } -changes { EDITED d/f2 DELETED d/f3 } -addremove { ADDED d/f1 } -exists {d/f1 d/f2 d/f3} revert-test 4-2 {d/f2} { REVERT d/f2 } -changes { ADDED d/f1 DELETED d/f3 } -exists {d/f1 d/f2 d/f3} revert-test 4-3 {d/f3} { REVERT d/f3 } -changes { ADDED d/f1 EDITED d/f2 } -exists {d/f1 d/f2 d/f3} fossil mv --soft d/f4 d/f4new test 4-4-mv-1 {[file exists d/f4]} test 4-4-mv-2 {![file exists d/f4new]} revert-test 4-4 {d/f4} { DELETE d/f4new REVERT d/f4 } -changes { ADDED d/f1 EDITED d/f2 DELETED d/f3 } -exists {d/f4} -notexists {d/f4new} ## Commit changes before testing reverting of directory rename, ## otherwise there're could be sequencing issues fossil redo fossil commit -m "4-5:setup" fossil mv --soft d dnew revert-test 4-5 {d/f1 d/f2 d/f3 d/f4} { REVERT d/f1 REVERT d/f2 UNMANAGE d/f3 REVERT d/f4 DELETE dnew/f1 DELETE dnew/f2 DELETE dnew/f4 } -addremove { ADDED d/f3 } -exists {d/f1 d/f2 d/f3 d/f4} -notexists {dnew} ## Test reverting of changes in whole sub-directory tree test_setup file mkdir d write_file f0 "f0" write_file d/f1 "d/f1" write_file d/f2 "d/f2" write_file d/f3 "d/f3" write_file d/f4 "d/f4" fossil add f0 d fossil delete d/f1 fossil commit -m "f0 d/f2 d/f3 d/f4" ## Changes to revert fossil add d/f1 write_file d/f2 "5-1:d/f2" fossil changes d/f2 fossil delete --soft d/f3 revert-test 5-1 {d} { UNMANAGE d/f1 REVERT d/f2 REVERT d/f3 } -addremove { ADDED d/f1 } -exists {f0 d/f1 d/f2 d/f3} write_file f0 "5-2:f0" fossil changes f0 revert-test 5-2 {f0 d} { UNMANAGE d/f1 REVERT d/f2 REVERT d/f3 REVERT f0 } -addremove { ADDED d/f1 } -exists {f0 d/f1 d/f2 d/f3} ## Commit changes before testing the revert of directory rename, ## otherwise there're could be sequencing issues fossil commit -m "5-3:setup" fossil changes fossil mv --soft d dnew revert-test 5-3 {d} { REVERT d/f1 REVERT d/f2 REVERT d/f4 DELETE dnew/f1 DELETE dnew/f2 DELETE dnew/f4 } -addremove { ADDED d/f3 } -exists {f0 d/f1 d/f2 d/f3 d/f4} -notexists {dnew} ## Reset/redo the undone results of revert to get to a clean checkout fossil redo file mkdir d/e file mkdir d/e/f write_file d/e/fe1 "d/e/fe1" write_file d/e/f/ff1 "d/e/f/ff1" file mkdir d1 file mkdir d1/e write_file d1/e/fe1 "d1/e/fe1" write_file d1/e/fe2 "d1/e/fe2" fossil add d1/e/fe1 fossil commit d1/e/fe1 -m "d1/e/fe1" write_file d1/e/fe1 "5-4:d1/e/fe1" fossil changes d1/e/fe1 fossil add d d1 revert-test 5-4 {d d1} { UNMANAGE d/f3 UNMANAGE d/e/fe1 UNMANAGE d/e/f/ff1 REVERT d1/e/fe1 UNMANAGE d1/e/fe2 } -addremove { ADDED d/f3 ADDED d/e/fe1 ADDED d/e/f/ff1 ADDED d1/e/fe2 } -exists {d/f1 d/f2 d/f3 d/f4 d/e/fe1 d/e/fe1 d/e/f/ff1 d1/e/fe1 d1/e/fe2} ############################################################################### test_cleanup |
Added test/rewrite-test-output.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 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 | #!/usr/bin/env tclsh # Script to anonymise test results for comparison. # - Replaces hashes, pids and similar with fixed strings # - Rewrites temporary paths to standardise them in output # Pick up options set EXTRA 0 set i [lsearch $argv -extra] while { $i >= 0 } { incr EXTRA set argv [lreplace $argv $i $i] set i [lsearch $argv -extra] } # With no arguments or "-", use stdin. set fname "-" if { [llength $argv] > 0 } { set fname [lindex $argv 0] } # Any -options, or an empty first argument, is an error. if { [llength $argv] > 1 || [regexp {^-.+} $fname] } { puts stderr "Error: argument error" puts stderr "usage: \[-extra\] [file tail $argv0] ?FILE" puts stderr " Rewrite test output to ease comparison of outputs." puts stderr " With -extra, more output is rewritten as is summaries" puts stderr " to make diff(1) mor euseful across runs and platforms." exit 1 } elseif { $fname ne "-" && ! [file exists $fname] } { puts stderr "File does not exist: '$fname'" exit 1 } proc common_rewrites { line testname } { # Normalise the fossil commands with path as just fossil regsub {^(?:[A-Z]:)?/.*?/fossil(?:\.exe)? } $line {fossil } line if {[string match "Usage: *" $line]} { regsub {^(Usage: )/.*?/fossil(?:\.exe)? } $line {\1fossil } line regsub {^(Usage: )[A-Z]:\\.*?\\fossil(?:\.exe)? } $line {\1fossil } line } # Accept 40 and 64 byte hashes as such regsub -all {[[:<:]][0-9a-f]{40}[[:>:]]} $line HASH line regsub -all {[[:<:]][0-9a-f]{64}[[:>:]]} $line HASH line # Date and time regsub -all {[[:<:]]\d{4}-\d\d-\d\d \d\d:\d\d:\d\d[[:>:]]} $line {YYYY-mm-dd HH:MM:SS} line if { [lsearch -exact {"amend" "wiki"} $testname] >= 0 } { # With embedded T and milliseconds regsub { \d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}$} $line { YYYY-mm-ddTHH:MM:SS.NNN} line } if { [lsearch -exact {"amend" "th1-hooks" "wiki"} $testname] >= 0 } { regsub {[[:<:]]\d{4}-\d\d-\d\d[[:>:]]} $line {YYYY-mm-dd} line } # Timelines have HH:MM:SS [HASH], but don't mess with the zero'ed version. regsub {^(?!00:00:00 \[0000000000\])\d\d:\d\d:\d\d \[[0-9a-f]{10}\] } $line {HH:MM:SS [HASH] } line # Temporary directories regsub -all {(?:[A-Z]:)?/.*?/repo_\d+/\d+_\d+} $line {/TMP/repo_PID/SEC_SEQ} line # Home directories only seem present with .fossil or _fossil. Simplify to .fossil. regsub -all {(?:[A-Z]:)?/.*?/home_\d+/[._]fossil[[:>:]]} $line {/TMP/home_PID/.fossil} line # Users in output regsub { (\(user: )[^\)]*\)$} $line { \1USER)} line return $line } # # tests/tests_unix/tests_windows contain tuples of # # 1. A regular expression to match current line # 2. A substitution for the current line # # Some common patterns applicable to multiples tests are appended below. # # The common_rewrites procedure is run first, so use e.g. HASH as needed. # dict set tests "amend" { {^(fossil artifact) [0-9a-f]{10}} {\1 HASH} {^U [^ ]+$} {U USER} {^Z [0-9a-f]{32}$} {Z CHECKSUM} {^(ed -s \./ci-comment-).*?(\.txt)$} {\1UNIQ\2} {^(fossil amend HASH -date \{?)\d\d/\d\d/\d{4}} {\1dd/mm/YYYY} {^(fossil amend HASH -date \{.* )\d{4}(\})$} {\1YYYY\2} {^(fossil amend HASH -date \{.* )\d\d:} {\1HH:} {^(fossil amend HASH -date \{)[A-Z][a-z]{2} [A-Z][a-z]{2} [ 0-9]\d } {\1Day Mon dd } {(\] Edit \[)[0-9a-f]{16}.[0-9a-f]{10}(\]: )} {\1HASH1|HASH2\2} {(\] Edit \[.*?&dp=)[0-9a-f]{16}} {\1dp=HASH} } dict set tests "cmdline" { {^(fossil test-echo --args) .*/} {\1 /TMP/} {^(g\.nameOfExe =) \[[^\]]+[/\\]fossil(?:\.exe)?\]$} {\1 [/PATH/FOSSILCMD]} {^(argv\[0\] =) \[[^\]]+[/\\]fossil(?:\.exe)?\]$} {\1 [/PATH/FOSSILCMD]} } dict set tests "contains-selector" { {^(fossil test-contains-selector) .*?/(compare-selector.css )} {\1 /TMP/\2} } dict set tests "json" { {^(Content-Length) \d+$} {\1 LENGTH} {^(Cookie: fossil-)[0-9a-f]{16}(\=HASH%2F)\d+\.\d+(%2Fanonymous)$} {\1CODE\2NOW\3} {^(GET /json/cap\?authToken\=HASH)/\d+\.\d+/(anonymous )} {\1/NOW/\2} {^(Cookie: fossil-)[0-9a-f]{16}\=[0-9A-F]{50}%2F[0-9a-f]{16}%2F(.*)$} {\1CODE=SHA1%2FCODE%2F\2} {("authToken":").+?(")} {\1AUTHTOKEN\2} {("averageArtifactSize":)\d+()} {\1SIZE\2} {("compiler":").+?(")} {\1COMPILER\2} {("loginCookieName":").+?(")} {\1COOKIE\2} {("manifestVersion":"\[)[0-9a-f]{10}(\]")} {\1HASH\2} {("manifestYear":")\d{4}(")} {\1YYYY\2} {("name":").+?(")} {\1NAME\2} {("password":")[0-9a-f]+(")} {\1PASSWORD\2} {("projectCode":")[0-9a-f]{40}(")} {\1HASH\2} {("procTimeMs":)\d+} {\1MSEC} {("procTimeUs":)\d+} {\1USEC} {("releaseVersion":")\d+\.\d+(")} {\1VERSION\2} {("releaseVersionNumber":")\d+(")} {\1VERSION_NUMBER\2} {("timestamp":)\d+} {\1SEC} {("seed":)\d+()} {\1SEED\2} {("uid":)\d+()} {\1UID\2} {("uncompressedArtifactSize":)\d+()} {\1SIZE\2} {("user":").+?(")} {\1USER\2} {("version":"YYYY-mm-dd HH:MM:SS )\[[0-9a-f]{10}\] \(\d+\.\d+\.\d+\)"} {\1[HASH] (major.minor.patch)} {^(Date:) [A-Z][a-z]{2}, \d\d? [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d [-+]\d{4}$} {\1 Day, dd Mon YYYY HH:MM:SS TZ} } dict set tests "merge_renames" { {^(size: {7})\d+( bytes)$} {\1N\2} {^(type: {7}Check-in by ).+?( on YYYY-mm-dd HH:MM:SS)$} {\1USER\2} } dict set tests "set-manifest" { {^(project-code: )[0-9a-f]{40}$} {\1HASH} line } dict set tests "stash" { {^(---|\+\+\+) NUL$} {\1 /dev/null} {(^ 1: \[)[0-9a-f]{14}(\] on YYYY-mm-dd HH:MM:SS)$} {\1HASH\2} {(^ 1: \[)[0-9a-f]{14}(\] from YYYY-mm-dd HH:MM:SS)$} {\1HASH\2} } dict set tests "th1" { {^(fossil test-th-source) (?:[A-Z]:)?.*?/(th1-)\d+([.]th1)$} {\1 /TMP/\2PID\3} {^(?:[A-Z]:)?[/\\].*?[/\\]fossil(?:\.exe)?$} {/PATH/FOSSILCMD} {[[:<:]](Content-Security-Policy[[:>:]].*'nonce-)[0-9a-f]{48}(';)} {\1NONCE\2} {^(<link rel="stylesheet" href="/style.css\?id=)[0-9a-f]+(" type="text/css">)$} {\1ID\2} {^\d+\.\d{3}(s by)$} {N.MMM\1} {^(Fossil) \d+\.\d+ \[[0-9a-f]{10}\] (YYYY-mm-dd HH:MM:SS)$} {\1 N.M [HASH] \2} {^(<script nonce=")[0-9a-f]{48}(">/\* style\.c:)\d+} {\1NONCE\2LINENO} } dict set tests "th1-docs" { {^(check-ins: ).*} {\1COUNT} {^(local-root: ).*} {\1/PATH/} {^(repository: ).*} {\1/PATH/REPO} {^(comment: ).*} {\1/COMMENT/} {^(tags: ).*} {\1/TAGS/} {(--ipaddr 127\.0\.0\.1) .*? (--localauth)} {\1 REPO \2} } dict set tests "th1-hooks" { {^(?:/[^:]*/fossil|[A-Z]:\\[^:]*\\fossil\.exe): (unknown command:|use \"help\")} {fossil: \1} {^(project-code: )[0-9a-f]{40}$} {\1HASH} } dict set tests "th1-tcl" { {^(fossil test-th-render --open-config) \{?.*?[/\\]test[/\\]([^/\\]*?)\}?$} {\1 /CHECKOUT/test/\2} {^(fossil)(?:\.exe)?( 3 \{test-th-render --open-config )(?:\{[A-Z]:)?[/\\].*?[/\\]test[/\\](th1-tcl9.txt\})\}?$} {\1\2/CHECKOUT/test/\3} {^\d{10}$} {SEC} } dict set tests "unversioned" { {^(fossil user new uvtester.*) \d+$} {\1 PASSWORD} {^(fossil .*http://uvtester:)\d+(@localhost:)\d+} {\1PASSWORD\2PORT} {^(Pull from http://uvtester@localhost:)\d+} {\1PORT} {^(ERROR \(1\): Usage:) .*?[/\\]fossil(?:\.exe)? (unversioned)} {\1 /PATH/fossil \2} {^(Started Fossil server, pid \")\d+(\", port \")\d+} {\1PID\2PORT} {^(Now in client directory \")(?:[A-Z]:)?/.*?/uvtest_\d+_\d+\"} {\1/TMP/uvtest_SEC_SEQ} {^(Stopped Fossil server, pid \")\d+(\", using argument \")(?:\d+|[^\"]*\.stopper)(\")} {\1PID\2PID_OR_SCRIPT\3} {^(This is unversioned file #4\.) \d+ \d+} {\1 PID SEC} {^(This is unversioned file #4\. PID SEC) \d+ \d+} {\1 PID SEC} {^[0-9a-f]{12}( YYYY-mm-dd HH:MM:SS *)(\d+)( *)\2( unversioned4.txt)$} {HASH \1SZ\3SZ\4} {^[0-9a-f]{40}$} {\1HASH} {^((?:Clone|Pull)? done, wire bytes sent: )\d+( received: )\d+( remote: )(?:127\.0.0\.1|::1)$} {\1SENT\2RECV\3LOCALIP} {^(project-id: )[0-9a-f]{40}$} {\1HASH} {^(server-id: )[0-9a-f]{40}$} {\1HASH} {^(admin-user: uvtester \(password is ").*("\))$} {\1PASSWORD\2} {^(repository: ).*?/uvtest_\d+_\d+/(uvrepo.fossil)$} {\1/TMP/uvtest_SEC_SEQ/\2} {^(local-root: ).*?/uvtest_\d+_\d+/$} {\1/TMP/uvtest_SEC_SEQ/} {^(project-code: )[0-9a-f]{40}$} {\1HASH} } dict set tests "utf" { {^(fossil test-looks-like-utf) (?:[A-Z]:)?/.*?/([^/\\]*?)\}?$} {\1 /TMP/test/\2} {^(File ")(?:[A-Z]:)?/.*?/(utf-check-\d+-\d+-\d+-\d+.jnk" has \d+ bytes\.)$} {\1/TMP/\2} } dict set tests "wiki" { {^(fossil (?:attachment|wiki) .*--technote )[0-9a-f]{21}$} {\1HASH} {^(fossil (?:attachment|wiki) .* (?:a13|f15|fa) --technote )[0-9a-f]+$} {\1ID} {^[0-9a-f]{40}( YYYY-mm-dd HH:MM:SS)} {HASH\1} {(\] Add attachment \[/artifact/)[0-9a-f]{16}(|)} {\1HASH\2} { (to tech note \[/technote/)[0-9a-f]{16}\|[0-9a-f]{10}(\] \(user:)} {\1HASH1|HASH2\2} {^(ambiguous tech note id: )[0-9a-f]+$} {\1ID} {^(Attached fa to tech note )[0-9a-f]{21}(?:[0-9a-f]{19})?\.$} {\1HASH.} {^(Date:) [A-Z][a-z]{2}, \d\d? [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d [-+]\d{4}$} {\1 Day, dd Mon YYYY HH:MM:SS TZ} {(Content-Security-Policy.*'nonce-)[0-9a-f]{48}(';)} {\1NONCE\2} {^(<link rel="stylesheet" href="/style.css\?id=)[0-9a-f]+(" type="text/css">)$} {\1ID\2} {^(added by )[^ ]*( on)$} {\1USER\2} {^(<script nonce=['\"])[0-9a-f]{48}(['\"]>/\* [a-z]+\.c:)\d+} {\1NONCE\2LINENO} {^(<script nonce=['\"])[0-9a-f]{48}(['\"]>)$} {\1NONCE\2} {^(projectCode: ")[0-9a-f]{40}(",)$} {\1HASH\2} {^\d+\.\d+(s by)$} {N.SUB\1} {^(window\.fossil.version = ")\d+\.\d+ \[[0-9a-f]{10}\] (YYYY-mm-dd HH:MM:SS(?: UTC";)?)$} {\1N.M [HASH] \2} {^(Fossil) \d+\.\d+ \[[0-9a-f]{10}\]( YYYY-mm-dd HH:MM:SS)$} {\1 N.M [HASH]\2} {^(type: Wiki-edit by ).+?( on YYYY-mm-dd HH:MM:SS)$$} {\1USER\2} {^(size: )\d+( bytes)$} {\1N\2} {^U [^ ]+$} {U USER} {^Z [0-9a-f]{32}$} {Z CHECKSUM} } # # Some pattersn are used in multiple groups # set testnames {"th1" "th1-docs" "th1-hooks"} set pat {^((?:ERROR \(1\): )?/[*]{5} Subprocess) \d+ (exit)} set sub {\1 PID \2} foreach testname $testnames { dict lappend tests $testname $pat $sub } set testnames {"th1-docs" "th1-hooks"} set pat {(?:[A-Z]:)?/.*?/(test-http-(?:in|out))-\d+-\d+-\d+(\.txt)} set sub {/TMP/\1-PID-SEQ-SEC\2} foreach testname $testnames { dict lappend tests $testname $pat $sub } set testnames {"json" "th1" "wiki"} set pat {^(Content-Length:) \d+$} set sub {\1 LENGTH} foreach testname $testnames { dict lappend tests $testname $pat $sub } set testnames {"th1" "wiki"} set pat {^\d+\.\d+(s by)$} set sub {N.SUB\1} foreach testname $testnames { dict lappend tests $testname $pat $sub } # # Main # if { $fname eq "-" } { set fd stdin } else { set fd [open $fname r] } # Platforms we detect set UNKOWN_PLATFORM 0 set UNIX 1 set WINDOWS 2 set CYGWIN 3 # One specific wiki test creates repetitive output of varying length set wiki_f13_cmd1 "fossil wiki create {timestamp of 2399999} f13 --technote 2399999" set wiki_f13_cmd2 "fossil wiki list --technote --show-technote-ids" set wiki_f13_cmd3 "fossil wiki export a13 --technote ID" set collecting_f3 0 set collecting_f3_verbose 0 # Collected lines for summaries in --extra mode set amend_ed_lines [list] set amend_ed_failed 0 set symlinks_lines [list] set symlinks_failed 0 set test_simplify_name_lines [list] set test_simplify_name_failed 0 # State information s we progress set check_json_empty_line 0 set lineno 0 set platform $UNKOWN_PLATFORM set prev_line "" set testname "" while { [gets $fd line] >= 0 } { incr lineno if { $lineno == 1 } { if { [string index $line 0] in {"\UFFEF" "\UFEFF"} } { set line [string range $line 1 end] } } # Remove RESULT status while matching (inserted again in output). # If collecting lines of output, include $result_prefix as needed. regexp {^(RESULT \([01]\): )?(.*)} $line match result_prefix line if { [regsub {^\*{5} ([^ ]+) \*{6}$} $line {\1} new_testname] } { # Pick up test name for special handling below set testname "$new_testname" } elseif { [regexp {^\*{5} End of } $line] } { # Test done. Handle --extra before resetting. if { $EXTRA } { if { $testname eq "symlinks" } { if { $symlinks_failed } { foreach l $symlinks_lines { puts "$l" } } else { puts "All symlinks tests OK (not run on Windows)" } } regsub {(: )\d+( errors so far)} $line {\1N\2} line } set testname "" } elseif { $testname ne "" } { if { $platform == $UNKOWN_PLATFORM } { if { [regexp {^[A-Z]:/.*?/fossil\.exe } $line] } { set platform $WINDOWS } elseif { [regexp {^/.*?/fossil\.exe } $line] } { # No drive, but still .exe - must be CYGWIN set platform $CYGWIN } elseif { [regexp {^/.*?/fossil } $line] } { set platform $UNIX } } # Do common and per testname rewrites set line [common_rewrites $line $testname] if { [dict exists $tests $testname] } { foreach {pat sub} [dict get $tests $testname] { regsub $pat $line $sub line } } # On Windows, HTTP headers may get printed with an extra newline if { $testname eq "json" } { if { $check_json_empty_line == 1 } { if { "$result_prefix$line" eq "" } { set check_json_empty_line 2 continue } set check_json_empty_line 0 } elseif { [regexp {^(?:$|GET |POST |[A-Z][A-Za-z]*(?:-[A-Z][A-Za-z]*)*: )} $line] } { set check_json_empty_line 1 } else { if { $check_json_empty_line == 2 } { # The empty line we skipped was meant to be followed by a new # HTTP header or empty line, but it was not. puts "" } set check_json_empty_line 0 } } # Summarise repetitive output of varying length for f13 in wiki test if { $testname eq "wiki" } { if { $collecting_f3 == 2 } { if { $collecting_f3_verbose == 1 && [regexp {^HASH } $line] } { incr collecting_f3_verbose } elseif { $line eq $wiki_f13_cmd3 } { incr collecting_f3 puts "\[...\]" } else { continue } } elseif { $collecting_f3 == 1 } { if { $line eq $wiki_f13_cmd2 } { incr collecting_f3 } elseif { $collecting_f3_verbose == 0 } { incr collecting_f3_verbose } } elseif { $line eq $wiki_f13_cmd1 } { incr collecting_f3 } } if { $EXTRA } { if { $line eq "ERROR (0): " && $platform == $WINDOWS } { if { [string match "fossil http --in *" $prev_line] } { continue } } if { $testname eq "amend" } { # The amend-comment-5.N tests are not run on Windows if { $line eq "fossil amend {} -close" } { if { $amend_ed_failed } { foreach l $amend_ed_lines { puts "$l" } } else { puts "All amend tests based on ed -s OK (not run on Windows)" } set amend_ed_lines [list] } elseif { [llength $amend_ed_lines] } { if { [regexp {^test amend-comment-5\.\d+ (.*)} $line match status] } { lappend amend_ed_lines "$result_prefix$line" if { $status ne "OK" } { incr amend_ed_failed } continue } elseif { [string range $line 0 4] eq "test " } { # Handle change in tests by simply emitting what we got foreach l $amend_ed_lines { puts "$l" } set amend_ed_lines [list] } else { lappend amend_ed_lines "$result_prefix$line" continue } } elseif { $line eq "fossil settings editor {ed -s}" } { lappend amend_ed_lines "$result_prefix$line" continue } } elseif { $testname eq "cmdline" } { if { [regexp {^(fossil test-echo) (.*)} $line match test args] } { if { ($platform == $UNIX && $args in {"*" "*.*"}) || ($platform == $WINDOWS && $args eq "--args /TMP/fossil-cmd-line-101.txt") || ($platform == $CYGWIN && $args in {"*" "*.*"}) } { set line "$test ARG_FOR_PLATFORM" } } } elseif { $testname eq "commit-warning" } { if { [regexp {^(micro-smile|pale facepalm) .*} $line match desc] } { set line "$desc PLATFORM_SPECIFIC_BYTES" } } elseif { $testname eq "file1" } { # test-simplify-name with question marks is specific to Windows # They all immediately preceed "fossil test-relative-name --chdir . ." if { $line eq "fossil test-relative-name --chdir . ." } { if { $test_simplify_name_failed } { foreach l $test_simplify_name_lines { puts "$l" } } else { puts "ALL Windows specific test-relative-name tests OK (if on Windows)" } set test_simplify_name_lines [list] } elseif { [regexp {^fossil test-simplify-name .*([/\\])\?\1} $line] } { lappend test_simplify_name_lines $line continue } elseif { [llength $test_simplify_name_lines] } { if { [regexp {^test simplify-name-\d+ (.*)} $line match status] } { if { $status ne "OK" } { incr test_simplify_name_failed } } lappend test_simplify_name_lines "$result_prefix$line" continue } } elseif { $testname eq "settings-repo" } { if { [regexp {^fossil test-th-eval (?:--open-config )?\{setting case-sensitive\}$} $prev_line] } { if { ($platform == $UNIX && $line eq "on") || ($platform == $WINDOWS && $line eq "off") || ($platform == $CYGWIN && $line eq "off") } { set line "EXPECTED_FOR_PLATFORM" } } } elseif { $testname eq "symlinks" } { # Collect all lines and post-process at the end lappend symlinks_lines "$result_prefix$line" if { [regexp {^test symlinks-[^ ]* (.*)} $line match status] } { if { $status ne "OK" } { #TODO: incr symlinks_failed } } continue } elseif { $testname in {"th1" "th1-docs" "th1-hooks"} } { # Special case that spans a couple of tests # "Subprocess PID exit(0)" is sent on stderr on Unix. On Windows, there is no output if { [regexp {^(ERROR \(1\): )?/\*{5} Subprocess PID exit\(0\) \*{5}/$} $line match prefix] } { if { $prefix eq "" } { continue } elseif { $prefix eq "ERROR (1): " } { set line "RESULT (0): " } } elseif { $testname eq "th1" } { if { [regexp {^fossil test-th-eval --vfs ([^ ]+) \{globalState vfs\}$} $line match vfs] } { if { ($platform == $UNIX && $vfs == "unix-dotfile") || ($platform == $WINDOWS && $vfs == "win32-longpath") || ($platform == $CYGWIN && $vfs == "win32-longpath") } { regsub $vfs $line {EXEPECTED_VFS} line } } elseif { $prev_line eq "fossil test-th-eval --vfs EXEPECTED_VFS {globalState vfs}" } { # Replace $vfs from previous line regsub "^$vfs\$" $line {EXEPECTED_VFS} line } elseif { $prev_line eq "fossil test-th-eval {set tcl_platform(platform)}" } { if { $platform == $UNIX } { regsub {^unix$} $line {EXPECTED_PLATFORM} line } elseif { $platform == $WINDOWS } { regsub {^windows$} $line {EXPECTED_PLATFORM} line } elseif { $platform == $CYGWIN } { regsub {^unix$} $line {EXPECTED_PLATFORM} line } } elseif { [string match "fossil test-th-eval --th-trace *" $prev_line] } { if { ($result_prefix eq "RESULT (1): " && $line eq "") || ($result_prefix eq "" && $line eq "ERROR (0): ") } { set result_prefix "" set line "RESULT (0): / ERROR (1): " } } } elseif { $testname eq "th1-docs" } { # In th1-docs, the fossil check-out is exposed in various states. regsub {(^project-code:) CE59BB9F186226D80E49D1FA2DB29F935CCA0333} $line {\1 HASH} line if { [regexp {^merged-from: HASH YYYY-mm-dd HH:MM:SS UTC$} $line] } { continue } } } } } elseif { $EXTRA } { # Fix up summaries to be generic and easy to diff(1) if { [regsub {(^\*{5} (Final|Ignored) results: )\d+} $line {\1N} line] } { regsub {\d+} $line {N} line } elseif { [regexp {^(\*{5} (?:Considered failure|Ignored failure|Skipped test))s: (.*)} $line match desc vals] } { if { $vals ne ""} { foreach val [split $vals " "] { puts "$desc: $val" } continue } } } # Not exactly correct if we continue'd, but OK for the purpose set prev_line "$result_prefix$line" puts "$prev_line" } |
Added test/set-manifest.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 | # # Copyright (c) 2016 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 manifest setting # proc file_contains {fname match} { set fp [open $fname r] set contents [read $fp] close $fp set lines [split $contents "\n"] foreach line $lines { if {[regexp $match $line]} { return 1 } } return 0 } # We need SHA1 to effectively test the manifest files produced by # fossil. It looks like the one from tcllib is exactly what we need. # On ActiveTcl, add it with teacup. On other platforms, YMMV. # teacup install sha1 if {[catch {package require sha1}] != 0} { puts "The \"sha1\" package is not available." test_cleanup_then_return } # We need a respository, so let it have one. test_setup #### Verify classic behavior of the manifest setting # Setting is off by default, and there are no extra files. fossil settings manifest test "set-manifest-1" {[regexp {^manifest *$} $RESULT]} set filelist [lsort [glob -nocomplain manifest*]] test "set-manifest-1-n" {[llength $filelist] == 0} # Classic behavior: TRUE value creates manifest and manifest.uuid set truths [list true on 1] foreach v $truths { fossil settings manifest $v test "set-manifest-2-$v" {$RESULT eq ""} fossil settings manifest test "set-manifest-2-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]} set filelist [lsort [glob manifest*]] test "set-manifest-2-$v-n" {[llength $filelist] == 2} foreach f $filelist { test "set-manifest-2-$v-f-$f" {[file isfile $f]} } } # ... and manifest.uuid is the checkout's hash fossil info regexp {(?m)^checkout:\s+([0-9a-f]{40,64})\s.*$} $RESULT ckoutline ckid set uuid [string trim [read_file "manifest.uuid"]] test "set-manifest-2-uuid" {[same_uuid $ckid $uuid]} # ... which is also the SHA1 of the file "manifest" before it was # sterilized by appending an extra line when writing the file. The # extra text begins with # and is a full line, so we'll just strip # it with a brute-force substitution. This probably has the right # effect even if the checkin was PGP-signed, but we don't have that # setting turned on for this manifest in any case. #regsub {(?m)^#.*\n} [read_file "manifest"] "" manifest #set muuid [::sha1::sha1 $manifest] #test "set-manifest-2-manifest" {[same_uuid $muuid $uuid]} # Classic behavior: FALSE value removes manifest and manifest.uuid set falses [list false off 0] foreach v $falses { fossil settings manifest $v test "set-manifest-3-$v" {$RESULT eq ""} fossil settings manifest test "set-manifest-3-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]} set filelist [lsort [glob -nocomplain manifest*]] test "set-manifest-3-$v-n" {[llength $filelist] == 0} } # Classic behavior: unset removes manifest and manifest.uuid fossil unset manifest test "set-manifest-4" {$RESULT eq ""} fossil settings manifest test "set-manifest-4-a" {[regexp {^manifest *$} $RESULT]} set filelist [lsort [glob -nocomplain manifest*]] test "set-manifest-4-n" {[llength $filelist] == 0} ##### Tags Manifest feature extends the manifest setting # Manifest Tags: use letters r, u, and t to select each of manifest, # manifest.uuid, and manifest.tags files. set truths [list r u t ru ut rt rut] foreach v $truths { fossil settings manifest $v test "set-manifest-5-$v" {$RESULT eq ""} fossil settings manifest test "set-manifest-5-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]} set filelist [lsort [glob manifest*]] test "set-manifest-5-$v-n" {[llength $filelist] == [string length $v]} foreach f $filelist { test "set-manifest-5-$v-f-$f" {[file isfile $f]} } } # Quick check for tags applied in trunk test_file_contents "set-manifest-6" "manifest.tags" "branch trunk\ntag trunk\n" ##### Test manifest.tags file content updates after commits # Explicitly set manifest.tags mode fossil set manifest t test "set-manifest-7-1" {[file isfile manifest.tags]} # Add a tag and make sure it appears in manifest.tags fossil tag add manifest-7-tag-1 tip test "set-manifest-7-2" {[file_contains "manifest.tags" "^tag manifest-7-tag-1$"]} # Add a file and make sure tag has disappeared from manifest.tags write_file file1 "file1 contents" fossil add file1 fossil commit -m "Added file1." test "set-manifest-7-3" {![file_contains "manifest.tags" "^tag manifest-7-tag-1$"]} # Add new tag and check that it is in manifest.tags fossil tag add manifest-7-tag-2 tip test "set-manifest-7-4" {[file_contains "manifest.tags" "^tag manifest-7-tag-2$"]} ##### Tags manifest branch= updates # Add file, create new branch on commit and check that # manifest.tags has been updated appropriately write_file file3 "file3 contents" fossil add file3 fossil commit -m "Added file3." --branch manifest-8-branch test "set-manifest-8" {[file_contains "manifest.tags" "^branch manifest-8-branch$"]} test_cleanup |
Added test/settings-repo.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 | # # Copyright (c) 2016 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/ # ############################################################################ # # The "settings" and "unset" commands that may modify the repository. # set path [file dirname [info script]] require_no_open_checkout test_setup ############################################################################### # # Complete syntax as tested: # # fossil settings ?PROPERTY? ?VALUE? ?OPTIONS? # fossil unset PROPERTY ?OPTIONS? # # Where the only supported options are "--global" and "--exact". # ############################################################################### set all_settings [get_all_settings] foreach name $all_settings { # # HACK: Make 100% sure that there are no non-default setting values # present anywhere. # if {$name eq "manifest"} { fossil unset $name --exact --global -expectError } else { fossil unset $name --exact --global } fossil unset $name --exact # # NOTE: Query for the hard-coded default value of this setting and # save it. # fossil test-th-eval "setting $name" set defaults($name) [normalize_result] } ############################################################################### fossil settings bad-setting some_value -expectError test settings-set-bad-local { [normalize_result] eq "no such setting: bad-setting" } fossil settings bad-setting some_value --global -expectError test settings-set-bad-global { [normalize_result] eq "no such setting: bad-setting" } ############################################################################### fossil unset bad-setting -expectError test settings-unset-bad-local { [normalize_result] eq "no such setting: bad-setting" } fossil unset bad-setting --global -expectError test settings-unset-bad-global { [normalize_result] eq "no such setting: bad-setting" } ############################################################################### fossil settings ssl some_value -expectError test settings-set-ambiguous-local { [normalize_result] eq "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity" } fossil settings ssl some_value --global -expectError test settings-set-ambiguous-global { [normalize_result] eq "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity" } ############################################################################### fossil unset ssl -expectError test settings-unset-ambiguous-local { [normalize_result] eq "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity" } fossil unset ssl --global -expectError test settings-unset-ambiguous-global { [normalize_result] eq "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity" } ############################################################################### set pattern(1) {^%name%$} set pattern(3) {^%name%[ ]+\(global\)[ ]+%value%+$} set pattern(4) {^%name%[ ]+\(local\)[ ]+%value%+$} foreach name $all_settings { if {$name ne "manifest"} { set value #global_for_$name fossil settings $name $value --exact --global set data [normalize_result] test settings-set-$name-global { $data eq "" } fossil settings $name --exact --global set data [normalize_result] test settings-set-check1-$name-global { [regexp -- [string map \ [list %name% $name %value% $value] $pattern(3)] $data] } fossil test-th-eval --open-config "setting $name" set data [normalize_result] test settings-set-check2-$name-global { $data eq $value } fossil unset $name --exact --global set data [normalize_result] test settings-unset-$name-global { $data eq "" } fossil settings $name --exact --global set data [normalize_result] test settings-unset-check1-$name-global { [regexp -- [string map \ [list %name% $name %value% $value] $pattern(1)] $data] } fossil test-th-eval --open-config "setting $name" set data [normalize_result] test settings-unset-check2-$name-global { $data eq $defaults($name) } } set value #local_for_$name fossil settings $name $value --exact set data [normalize_result] test settings-set-$name-local { $data eq "" } fossil settings $name --exact set data [normalize_result] test settings-set-check1-$name-local { [regexp -- [string map \ [list %name% $name %value% $value] $pattern(4)] $data] } fossil test-th-eval --open-config "setting $name" set data [normalize_result] test settings-set-check2-$name-local { $data eq $value } fossil unset $name --exact set data [normalize_result] test settings-unset-$name-local { $data eq "" } fossil settings $name --exact set data [normalize_result] test settings-unset-check1-$name-local { [regexp -- [string map \ [list %name% $name %value% $value] $pattern(1)] $data] } fossil test-th-eval --open-config "setting $name" set data [normalize_result] test settings-unset-check2-$name-local { $data eq $defaults($name) } } ############################################################################### set pattern(5) \ {^%name%[ ]+\n \(overridden by contents of file \.fossil-settings/%name%\)$} set versionable_settings [get_versionable_settings] file mkdir .fossil-settings foreach name $versionable_settings { fossil settings $name --exact set data [normalize_result] test settings-before-versionable-$name { [regexp -- [string map [list %name% $name] $pattern(1)] $data] } set value #versionable_for_$name set fileName [file join .fossil-settings $name] write_file $fileName $value fossil settings $name --exact set data [normalize_result] test settings-set-check1-versionable-$name { [regexp -- [string map [list %name% $name] $pattern(5)] $data] } fossil test-th-eval --open-config "setting $name" set data [normalize_result] test settings-set-check2-versionable-$name { $data eq "" } file delete $fileName fossil settings $name --exact set data [normalize_result] test settings-after-versionable-$name { [regexp -- [string map [list %name% $name] $pattern(1)] $data] } } ############################################################################### test_cleanup |
Added test/settings.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 | # # Copyright (c) 2016 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/ # ############################################################################ # # The "settings" and "unset" commands. # set path [file dirname [info script]]; test_setup ############################################################################### # # Complete syntax as tested: # # fossil settings ?PROPERTY? ?VALUE? ?OPTIONS? # fossil unset PROPERTY ?OPTIONS? # # Where the only supported options are "--global" and "--exact". # ############################################################################### # # NOTE: The [extract_setting_names] procedure extracts the list of setting # names from the line-ending normalized output of the "fossil settings" # command. It assumes that a setting name must begin with a lowercase # letter. It also assumes that any output lines that start with a # lowercase letter contain a setting name starting at that same point. # proc extract_setting_names { data } { set names [list] foreach {dummy name} [regexp \ -all -line -inline -- {^([a-z][a-z0-9\-]*) ?.*$} $data] { lappend names $name } return $names } ############################################################################### set all_settings [get_all_settings] fossil settings set local_settings [extract_setting_names [normalize_result_no_trim]] fossil settings --global set global_settings [extract_setting_names [normalize_result_no_trim]] foreach name $all_settings { test settings-have-local-$name { [lsearch -exact $local_settings $name] != -1 } test settings-have-global-$name { [lsearch -exact $global_settings $name] != -1 } } foreach name $local_settings { test settings-valid-local-$name { [lsearch -exact $all_settings $name] != -1 } } foreach name $global_settings { test settings-valid-global-$name { [lsearch -exact $all_settings $name] != -1 } } ############################################################################### set pattern(1) {^%name%$} set pattern(2) {^%name%[ ]+\((?:local|global)\)[ ]+[^ ]+$} foreach name $all_settings { fossil settings $name --exact set data [normalize_result] test settings-query-local-$name { [regexp -- [string map [list %name% $name] $pattern(1)] $data] || [regexp -- [string map [list %name% $name] $pattern(2)] $data] } if {$name eq "manifest"} { fossil settings $name --exact --global -expectError } else { fossil settings $name --exact --global } set data [normalize_result] if {$name eq "manifest"} { test settings-query-global-$name { $data eq "cannot set 'manifest' globally" } } else { test settings-query-global-$name { [regexp -- [string map [list %name% $name] $pattern(1)] $data] || [regexp -- [string map [list %name% $name] $pattern(2)] $data] } } } ############################################################################### fossil settings bad-setting -expectError test settings-query-bad-local { [normalize_result] eq "no such setting: bad-setting" } fossil settings bad-setting --global -expectError test settings-query-bad-global { [normalize_result] eq "no such setting: bad-setting" } ############################################################################### test_cleanup |
Added test/stash.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 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 | # # Copyright (c) 2013 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 for 'fossil stash' # # proc knownBug {t tests} { return [expr {$t in $tests ? "knownBug" : ""}] } # Test 'fossil stash' against expected results from 'fossil changes' and # 'fossil addremove -n', as well as by verifying the existence of files # on the file system. Unlike the similar function found in # revert.test, 'fossil undo' is not called after each test because # many stash operations aren't undoable, and because further testing # of the stash content is more likely to be useful. # # The extra list "-knownbugs" is a list of areas that should be # marked as "knownBug" to the inner call to test. Known areas are: # -code The exit status of fossil stash # -result The result string didn't match # -changes The changed file set didn't match # -addremove The addremove result set didn't match # -exists One or more listed files don't exist # -notexists One or more listed files do exist # # Also, if the exit status of fossil stash does not match # expectations, the rest of the areas are not tested. proc test_result_state {testid cmdArgs expectedOutput args} { global RESULT set passed 1 set args [dict merge { -changes {} -addremove {} -exists {} -notexists {} -knownbugs {} } $args] set knownbugs [dict get $args "-knownbugs"] set result $::RESULT set code $::CODE if {[lindex $cmdArgs end] eq "-expectError"} { test $testid-CODE {$code} [knownBug "-code" $knownbugs] if {!$code} { return } } else { test $testid-CODE {!$code} [knownBug "-code" $knownbugs] if {$code} { return } } test_status_list $testid $result $expectedOutput [knownBug "-result" $knownbugs] 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 $testid$key $result $expected [knownBug $key $knownbugs] } 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 $testid$key $passed [knownBug $key $knownbugs] } #fossil undo } proc stash-test {testid stashArgs expectedStashOutput args} { fossil stash {*}$stashArgs return [test_result_state stash-$testid "stash $stashArgs" $expectedStashOutput {*}$args] } require_no_open_checkout test_setup # Prepare first commit # write_file f1 "f1" write_file f2 "f2" write_file f3 "f3" fossil add f1 f2 f3 fossil commit -m "c1" --tag c1 ######## # fossil stash # fossil stash save ?-m|--comment COMMENT? ?FILES...? # Make simple changes to stash # Add f0, remove f1, edit f2, rename f3 to f3n write_file f0 "f0" fossil add f0 file delete f1 fossil rm f1 write_file f2 "f2.1" file rename -force f3 f3n fossil mv f3 f3n # Stash these changes and confirm stash-test 1 {save -m "stash 1"} { UNMANAGE f0 REVERT f1 REVERT f2 REVERT f3 DELETE f3n } -addremove { ADDED f0 } -exists {f0 f1 f2 f3} -notexists {f3n} ######## # fossil stash list|ls ?-v|--verbose? ?-W|--width <num>? # Confirm there is a stash saved fossil stash list #protOut "{[normalize_result]}" #{1: [21bc64cff8c702] on 2016-02-10 19:48:44 # stash 1} test stash-1-list-1 {[regexp {^1: \[[0-9a-z]+\] on } [first_data_line]]} test stash-1-list-2 {[regexp {^\s+stash 1\s*$} [second_data_line]]} set diff_stash_1 {DELETE f1 Index: f1 ================================================================== --- f1 +++ /dev/null @@ -1,1 +0,0 @@ -f1 CHANGED f2 --- f2 +++ f2 @@ -1,1 +1,1 @@ -f2 +f2.1 CHANGED f3n --- f3n +++ f3n ADDED f0 Index: f0 ================================================================== --- /dev/null +++ f0 @@ -0,0 +1,1 @@ +f0} ######## # fossil stash show|cat ?STASHID? ?DIFF-OPTIONS? # fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS? fossil stash show test stash-1-show {[normalize_result] eq $diff_stash_1} fossil stash diff test stash-1-diff {[normalize_result] eq $diff_stash_1} knownBug ######## # fossil stash pop stash-test 2 pop { DELETE f1 UPDATE f2 UPDATE f3n ADDED f0 } -changes { ADDED f0 MISSING f1 EDITED f2 RENAMED f3 -> f3n } -addremove { DELETED f1 } -exists {f0 f2 f3n} -notexists {f1 f3} # Confirm there is no longer a stash saved fossil stash list test stash-2-list {[first_data_line] eq "empty stash"} # Test stashed mv without touching the file system # Issue reported by email to fossil-users # from Warren Young, dated Tue, 9 Feb 2016 01:22:54 -0700 # with checkin [b8c7af5bd9] plus a local patch on CentOS 5 # 64 bit intel, 8-byte pointer, 4-byte integer # Stashed renamed file said: # fossil: ./src/delta.c:231: checksum: Assertion '...' failed. # Should be triggered by this stash-WY-1 test. fossil checkout --force c1 fossil clean fossil mv --soft f1 f1new stash-test WY-1 {-expectError save -m "Reported 2016-02-09"} { REVERT f1 DELETE f1new } -changes { } -addremove { } -exists {f1 f2 f3} -notexists {f1new} -knownbugs {-code -result} # TODO: add tests that verify the saved stash is sensible. Possibly # by applying it and checking results. But until the SQLITE_CONSTRAINT # error is fixed, there is nothing stashed to test. # Test stashing the combination of a renamed file and an added file that # uses the renamed file's original filename. I expect to see the same # behavior as fossil revert: calmly back out both the rename and the # add, and presumably stash the content of the added file before it # is replaced by the revert. # test_setup 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 stash-test 2-1 {save -m "f1b"} { REVERT f1 DELETE f1n } -exists {f1} -notexists {f1n} -knownbugs {-code -result} # TODO: add tests that verify the saved stash is sensible. Possibly # by applying it and checking results. But until the MISSING file # error is fixed, there is nothing stashed to test. # Test stashing a newly added (but never committed) file. As with # fossil revert, fossil stash save unmanages the new file, but # leaves the copy present on disk. This is undocumented, but # probably sensible. test_setup write_file f1 "f1" write_file f2 "f2" fossil add f1 f2 fossil commit -m "baseline" write_file f3 "f3" fossil add f3 stash-test 3-1 {save -m f3} { UNMANAGE f3 } -addremove { ADDED f3 } -exists {f1 f2 f3} -notexists {} #fossil status fossil stash show test stash-3-1-show {[normalize_result] eq {ADDED f3 Index: f3 ================================================================== --- /dev/null +++ f3 @@ -0,0 +1,1 @@ +f3}} stash-test 3-1-pop {pop} { ADDED f3 } -changes { ADDED f3 } -addremove { } -exists {f1 f2 f3} -notexists {} fossil status # Test stashing a rename of one file with at least one file # unchanged. This should stash (and revert) just the rename # operation. Instead it also stores and touches the unchanged file. test_setup write_file f1 "f1" write_file f2 "f2" fossil add f1 f2 fossil commit -m "baseline" fossil mv --hard f2 f2n test_result_state stash-3-2-mv "mv --hard f2 f2n" [concat { RENAME f2 f2n MOVED_FILE} [file normalize f2] { }] -changes { RENAMED f2 -> f2n } -addremove { } -exists {f1 f2n} -notexists {f2} stash-test 3-2 {save -m f2n} { REVERT f2 DELETE f2n } -exists {f1 f2} -notexists {f2n} -knownbugs {-result} fossil stash show test stash-3-2-show-1 {![regexp {\sf1} $RESULT]} knownBug test stash-3-2-show-2 {[regexp {\sf2n} $RESULT]} stash-test 3-2-pop {pop} { UPDATE f1 UPDATE f2n } -changes { RENAMED f2 -> f2n } -addremove { } -exists {f1 f2n} -notexists {f2} ######## # fossil stash snapshot ?-m|--comment COMMENT? ?FILES...? test_setup write_file f1 "f1" write_file f2 "f2" write_file f3 "f3" fossil add f1 f2 f3 fossil commit -m "c1" --tag c1 # Make simple changes and snapshot them # Add f0, edit f2 write_file f0 "f0" fossil add f0 write_file f2 "f2.1" # Snapshot these changes and confirm stash-test 4-1 {snapshot -m "snap 1"} { } -changes { ADDED f0 EDITED f2 } -addremove { } -exists {f0 f1 f2 f3} -notexists {} fossil stash diff test stash-4-1-diff-CODE {!$::CODE} fossil stash show test stash-4-1-show-1 {[regexp {CHANGED f2} $RESULT]} test stash-4-1-show-2 {[regexp {ADDED f0} $RESULT]} # remove f1 and snapshot file delete f1 fossil rm f1 stash-test 4-2 {snapshot -m "snap 2"} { } -changes { ADDED f0 DELETED f1 EDITED f2 } -addremove { } -exists {f0 f2 f3} -notexists {f1} fossil stash diff test stash-4-2-diff-CODE {!$::CODE} knownBug fossil stash show test stash-4-2-show-1 {[regexp {DELETE f1} $RESULT]} test stash-4-2-show-2 {[regexp {CHANGED f2} $RESULT]} test stash-4-2-show-3 {[regexp {ADDED f0} $RESULT]} # rename f3 to f3n and snapshot file rename -force f3 f3n fossil mv f3 f3n stash-test 4-3 {snapshot -m "snap 3"} { } -changes { ADDED f0 DELETED f1 EDITED f2 RENAMED f3 -> f3n } -addremove { } -exists {f0 f2 f3n} -notexists {f1 f3} fossil stash diff test stash-4-3-diff-CODE {!$::CODE} knownBug fossil stash show test stash-4-3-show-1 {[regexp {DELETE f1} $RESULT]} test stash-4-3-show-2 {[regexp {CHANGED f2} $RESULT]} test stash-4-3-show-2 {[regexp {CHANGED f3n} $RESULT]} test stash-4-3-show-3 {[regexp {ADDED f0} $RESULT]} # fossil stash apply ?STASHID? # fossil stash goto ?STASHID? # fossil stash rm|drop ?STASHID? ?-a|--all? #fossil checkout --force c1 #fossil clean ############################################################################### test_cleanup |
Added test/subdir with spaces/filename with spaces.txt.
> > | 1 2 | This file has a name that contains spaces. It is used to help verify that fossil can handle filenames that contain spaces. |
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/README.html.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <h1>An Example HTML Readme</h1> <p> The test/subdir/ directory and its children exist purely for testing in the self-hosting Fossil repository. This particular file is used to verify that a file name "README.html" is displayed correctly. beneath the directory listing. </p> <center> <table border=1> <tr><td>This</td><td>is</td></tr> <tr><td>a</td><td><table></td></tr> </table> </center> |
Added test/subdir/one/readme.wiki.
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | <title>Fossil Wiki Readme</title> This is another test README file. The point of this file is to show that lower-case "readme" is recognized, and the Fossil-Wiki formatting is displayed correctly. * First bullet * Second bullet |
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/symlinks.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 | # # Copyright (c) 2016 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/ # ############################################################################ # # Symbolic link tests. # set path [file dirname [info script]] if {$is_windows} { puts "Symlinks are not supported on Windows." test_cleanup_then_return } require_no_open_checkout ############################################################################### test_setup; set rootDir [file normalize [pwd]] # Using tempHomePath, allow-symlinks will always be off at this point. fossil set allow-symlinks on fossil test-th-eval --open-config {repository} set repository [normalize_result] if {[string length $repository] == 0} { puts "Detection of the open repository file failed." test_cleanup_then_return } ####################################### # Use symbolic link to a directory... # ####################################### file mkdir [file join $rootDir subdirA] exec ln -s [file join $rootDir subdirA] symdirA ############################################################################### write_file [file join $rootDir subdirA f1.txt] "f1" write_file [file join $rootDir subdirA f2.txt] "f2" test symlinks-dir-1 {[file exists [file join $rootDir subdirA f1.txt]] eq 1} test symlinks-dir-2 {[file exists [file join $rootDir symdirA f1.txt]] eq 1} test symlinks-dir-3 {[file exists [file join $rootDir subdirA f2.txt]] eq 1} test symlinks-dir-4 {[file exists [file join $rootDir symdirA f2.txt]] eq 1} fossil add [file join $rootDir symdirA f1.txt] test symlinks-skip-dir-traversal {[normalize_result] eq \ "SKIP symdirA/f1.txt"} fossil commit -m "c1" -expectError test symlinks-empty-commit {[normalize_result] eq \ "nothing has changed; use --allow-empty to override"} ############################################################################### fossil ls test symlinks-dir-5 {[normalize_result] eq ""} ############################################################################### fossil extras test symlinks-dir-6 {[normalize_result] eq \ "subdirA/f1.txt\nsubdirA/f2.txt\nsymdirA"} ############################################################################### fossil close file delete [file join $rootDir subdirA f1.txt] test symlinks-dir-7 {[file exists [file join $rootDir subdirA f1.txt]] eq 0} test symlinks-dir-8 {[file exists [file join $rootDir symdirA f1.txt]] eq 0} test symlinks-dir-9 {[file exists [file join $rootDir subdirA f2.txt]] eq 1} test symlinks-dir-10 {[file exists [file join $rootDir symdirA f2.txt]] eq 1} ############################################################################### fossil open --force $repository set code [catch {file readlink [file join $rootDir symdirA]} result] test symlinks-dir-11 {$code == 0} test symlinks-dir-12 {$result eq [file join $rootDir subdirA]} test symlinks-dir-13 {[file exists [file join $rootDir subdirA f1.txt]] eq 0} test symlinks-dir-14 {[file exists [file join $rootDir symdirA f1.txt]] eq 0} test symlinks-dir-15 {[file exists [file join $rootDir subdirA f2.txt]] eq 1} test symlinks-dir-16 {[file exists [file join $rootDir symdirA f2.txt]] eq 1} ############################################################################### # # TODO: Add tests for symbolic links as files here, including tests with the # "allow-symlinks" setting on and off. # ############################################################################### test_cleanup |
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.
1 2 3 4 | # # Copyright (c) 2006 D. Richard Hipp # # This program is free software; you can redistribute it and/or | | | | | < < < < < < > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > | > > > > > | > | | > > > | 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 | # # 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 is the main test script. To run a regression test, do this: # # 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. # # To run a subset of tests (i.e. only one or more of the test/*.test # scripts), append the script base names as arguments: # # tclsh ../test/tester.tcl ../bld/fossil <script-basename>... # # We use some things introduced in 8.6 such as lmap. auto.def should # have found us a suitable Tcl installation. package require Tcl 8.6 set testfiledir [file normalize [file dirname [info script]]] set testrundir [pwd] set testdir [file normalize [file dirname $argv0]] set fossilexe [file normalize [lindex $argv 0]] set is_windows [expr {$::tcl_platform(platform) eq "windows"}] set is_cygwin [regexp {^CYGWIN} $::tcl_platform(os)] if {$::is_windows} { if {[string length [file extension $fossilexe]] == 0} { append fossilexe .exe } set outside_fossil_repo [expr ![file exists "$::testfiledir\\..\\_FOSSIL_"]] } else { set outside_fossil_repo [expr ![file exists "$::testfiledir/../.fslckout"]] } catch {exec $::fossilexe changes --changed} res set dirty_ckout [string length $res] set argv [lrange $argv 1 end] set i [lsearch $argv -keep] if {$i>=0} { set KEEP 1 set argv [lreplace $argv $i $i] } else { set KEEP 0 } 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 } set i [lsearch $argv -quiet] if {$i>=0} { set QUIET 1 set argv [lreplace $argv $i $i] } else { set QUIET 0 } set i [lsearch $argv -strict] if {$i>=0} { set STRICT 1 set argv [lreplace $argv $i $i] } else { set STRICT 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} { 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 {noQuiet 0}} { if {$noQuiet || !$::QUIET} { puts stdout $msg } if {$::PROT} { set out [open [file join $::testrundir prot] a] fconfigure $out -translation platform puts $out $msg close $out } } # write a dict with just enough formatting # to make it human readable # proc protOutDict {dict {pattern *}} { set longest [tcl::mathfunc::max 0 {*}[lmap key [dict keys $dict $pattern] {string length $key}]] dict for {key value} $dict { protOut [format "%-${longest}s = %s" $key $value] } } # Run the Fossil program with the specified arguments. # # Consults the VERBOSE global variable to determine if # diagnostics should be emitted when no error is seen. # Sets the CODE and RESULT global variables for use in # test expressions. # proc fossil {args} { return [uplevel 1 fossil_maybe_answer [list ""] $args] } # Run the Fossil program with the specified arguments # and possibly answer the first prompt, if any. # # Consults the VERBOSE global variable to determine if # diagnostics should be emitted when no error is seen. # Sets the CODE and RESULT global variables for use in # test expressions. # proc fossil_maybe_answer {answer args} { global fossilexe set cmd $fossilexe set expectError 0 set index [lsearch -exact $args -expectError] if {$index != -1} { set expectError 1 set args [lreplace $args $index $index] } set keepNewline 0 set index [lsearch -exact $args -keepNewline] if {$index != -1} { set keepNewline 1 set args [lreplace $args $index $index] } set whatIf 0 set index [lsearch -exact $args -whatIf] if {$index != -1} { set whatIf 1 set args [lreplace $args $index $index] } foreach a $args { lappend cmd $a } protOut $cmd flush stdout if {$whatIf} { protOut [pwd]; protOut $answer set result WHAT-IF-MODE; set rc 42 } else { if {[string length $answer] > 0} { protOut $answer set prompt_file [file join $::tempPath fossil_prompt_answer] write_file $prompt_file $answer\n set execCmd [list eval exec] if {$keepNewline} {lappend execCmd -keepnewline} lappend execCmd $cmd <$prompt_file set rc [catch $execCmd result] file delete $prompt_file } else { set execCmd [list eval exec] if {$keepNewline} {lappend execCmd -keepnewline} lappend execCmd $cmd set rc [catch $execCmd result] } } set ab(str) {child process exited abnormally} set ab(len) [string length $ab(str)] set ab(off) [expr {$ab(len) - 1}] if {$rc && $expectError && \ [string range $result end-$ab(off) end] eq $ab(str)} { set result [string range $result 0 end-$ab(len)] } global RESULT CODE set CODE $rc if {!$whatIf} { if {($rc && !$expectError) || (!$rc && $expectError)} { protOut "ERROR ($rc): $result" 1 } elseif {$::VERBOSE} { protOut "RESULT ($rc): $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 } # Write a file to disk # proc write_file {filename txt} { set out [open $filename w] fconfigure $out -translation binary puts -nonewline $out $txt close $out } proc write_file_indented {filename txt} { write_file $filename [string trim [string map [list "\n " \n] $txt]]\n } # Returns the list of all supported versionable settings. # proc get_versionable_settings {} { # # TODO: If the list of supported versionable settings in "db.c" is modified, # this list (and procedure) most likely needs to be modified as well. # set result [list \ binary-glob \ clean-glob \ crlf-glob \ crnl-glob \ dotfiles \ empty-dirs \ encoding-glob \ ignore-glob \ keep-glob \ manifest] return [lsort -dictionary $result] } # Returns the list of all supported settings. # proc get_all_settings {} { # # TODO: If the list of supported settings in "db.c" is modified, this list # (and procedure) most likely needs to be modified as well. # set result [list \ access-log \ admin-log \ allow-symlinks \ auto-captcha \ auto-hyperlink \ auto-hyperlink-delay \ auto-hyperlink-mouseover \ auto-shun \ autosync \ autosync-tries \ backoffice-disable \ backoffice-logfile \ backoffice-nodelay \ binary-glob \ case-sensitive \ chat-alert-sound \ chat-initial-history \ chat-inline-images \ chat-keep-count \ chat-keep-days \ chat-poll-timeout \ chat-timeline-user \ clean-glob \ clearsign \ comment-format \ crlf-glob \ crnl-glob \ default-csp \ default-perms \ diff-binary \ diff-command \ dont-commit \ dont-push \ dotfiles \ editor \ email-admin \ email-listid \ email-renew-interval \ email-self \ email-send-command \ email-send-db \ email-send-dir \ email-send-method \ email-send-relayhost \ email-subname \ email-url \ empty-dirs \ encoding-glob \ exec-rel-paths \ fileedit-glob \ forbid-delta-manifests \ forum-close-policy \ gdiff-command \ gmerge-command \ hash-digits \ hooks \ http-port \ https-login \ ignore-glob \ keep-glob \ large-file-size \ localauth \ lock-timeout \ main-branch \ mainmenu \ manifest \ max-cache-entry \ max-loadavg \ max-upload \ mimetypes \ mtime-changes \ mv-rm-files \ pgp-command \ preferred-diff-type \ proxy \ redirect-to-https \ relative-paths \ repo-cksum \ repolist-skin \ safe-html \ self-pw-reset \ self-register \ sitemap-extra \ ssh-command \ ssl-ca-location \ ssl-identity \ tclsh \ th1-setup \ th1-uri-regexp \ ticket-default-report \ user-color-map \ uv-sync \ web-browser] fossil test-th-eval "hasfeature legacyMvRm" if {[normalize_result] eq "1"} { lappend result mv-rm-files } fossil test-th-eval "hasfeature tcl" if {[normalize_result] eq "1"} { lappend result tcl tcl-setup } fossil test-th-eval "hasfeature th1Docs" if {[normalize_result] eq "1"} { lappend result th1-docs } fossil test-th-eval "hasfeature th1Hooks" if {[normalize_result] eq "1"} { lappend result th1-hooks } return [lsort -dictionary $result] } # Return true if two files are the same # 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 if {$x == $y} { return 1 } else { if {$::VERBOSE} { protOut "NOT_SAME_FILE($a): \{\n$x\n\}" protOut "NOT_SAME_FILE($b): \{\n$y\n\}" } return 0 } } # Return true if two strings refer to the # same uuid. That is, the shorter is a prefix # of the longer. # proc same_uuid {a b} { set na [string length $a] set nb [string length $b] if {$na == $nb} { return [expr {$a eq $b}] } if {$na < $nb} { return [string match "$a*" $b] } return [string match "$b*" $a] } # Return a prefix of a uuid, defaulting to 10 chars. # proc short_uuid {uuid {len 10}} { string range $uuid 0 $len-1 } proc require_no_open_checkout {} { if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \ $::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} { return } catch {exec $::fossilexe info} res if {[regexp {local-root:} $res]} { global skipped_tests testfile lappend skipped_tests $testfile set projectName <unknown> set localRoot <unknown> regexp -line -- {^project-name: (.*)$} $res dummy projectName set projectName [string trim $projectName] regexp -line -- {^local-root: (.*)$} $res dummy localRoot set localRoot [string trim $localRoot] error "Detected an open checkout of project \"$projectName\",\ rooted at \"$localRoot\", testing halted." } } proc get_script_or_fail {} { set fileName [file normalize [info script]] if {[string length $fileName] == 0 || ![file exists $fileName]} { error "Failed to obtain the file name of the test being run." } return $fileName } proc robust_delete { path {force ""} } { set error "unknown error" for {set try 0} {$try < 10} {incr try} { if {$force eq "YES_DO_IT"} { if {[catch {file delete -force $path} error] == 0} { return } } else { if {[catch {file delete $path} error] == 0} { return } } after [expr {$try * 100}] } error "Could not delete \"$path\", error: $error" } proc test_cleanup_then_return {} { global skipped_tests testfile lappend skipped_tests $testfile uplevel 1 [list test_cleanup] return -code return } proc test_cleanup {} { if {$::KEEP} { # To avoid errors with require_no_open_checkout, cd out of here. if {[info exists ::tempSavedPwd]} {cd $::tempSavedPwd; unset ::tempSavedPwd} return } if {![info exists ::tempRepoPath]} {return} if {![file exists $::tempRepoPath]} {return} if {![file isdirectory $::tempRepoPath]} {return} set tempPathEnd [expr {[string length $::tempPath] - 1}] if {[string length $::tempPath] == 0 || \ [string range $::tempRepoPath 0 $tempPathEnd] ne $::tempPath} { error "Temporary repository path has wrong parent during cleanup." } if {[info exists ::tempSavedPwd]} {cd $::tempSavedPwd; unset ::tempSavedPwd} # First, attempt to delete the specific temporary repository directories # for this test file. set scriptName [file tail [get_script_or_fail]] foreach repoSeed $::tempRepoSeeds { set repoPath [file join $::tempRepoPath $repoSeed $scriptName] robust_delete $repoPath YES_DO_IT; # FORCE, arbitrary children. set seedPath [file join $::tempRepoPath $repoSeed] robust_delete $seedPath; # NO FORCE. } # Next, attempt to gracefully delete the temporary repository directory # for this process. robust_delete $::tempRepoPath # Finally, attempt to gracefully delete the temporary home directory, # unless forbidden by external forces. if {![info exists ::tempKeepHome]} {delete_temporary_home} } proc delete_temporary_home {} { if {$::KEEP} {return}; # All cleanup disabled? if {$::is_windows || $::is_cygwin} { robust_delete [file join $::tempHomePath _fossil] } else { robust_delete [file join $::tempHomePath .fossil] } robust_delete $::tempHomePath } proc is_home_elsewhere {} { return [expr {[info exists ::env(FOSSIL_HOME)] && \ $::env(FOSSIL_HOME) eq $::tempHomePath}] } proc set_home_to_elsewhere {} { # # Fossil will write data on $HOME (or $FOSSIL_HOME). We need not # to clutter the real $HOME (or $FOSSIL_HOME) of the test caller. # if {[is_home_elsewhere]} {return} set ::env(FOSSIL_HOME) $::tempHomePath } # # Create and open a new Fossil repository and clean the checkout # proc test_setup {{filename ".rep.fossil"}} { set_home_to_elsewhere if {![info exists ::tempRepoPath]} { set ::tempRepoPath [file join $::tempPath repo_[pid]] } set repoSeed [appendArgs [string trim [clock seconds] -] _ [getSeqNo]] lappend ::tempRepoSeeds $repoSeed set repoPath [file join \ $::tempRepoPath $repoSeed [file tail [get_script_or_fail]]] if {[catch { file mkdir $repoPath } error] != 0} { error "Could not make directory \"$repoPath\",\ please set TEMP variable in environment, error: $error" } if {![info exists ::tempSavedPwd]} {set ::tempSavedPwd [pwd]}; cd $repoPath if {[string length $filename] > 0} { exec $::fossilexe new $filename exec $::fossilexe open $filename exec $::fossilexe set mtime-changes off } return $repoPath } # This procedure only returns non-zero if the Tcl integration feature was # enabled at compile-time and is now enabled at runtime. proc is_tcl_usable_by_fossil {} { fossil test-th-eval "hasfeature tcl" if {[normalize_result] ne "1"} {return 0} fossil test-th-eval "setting tcl" if {[normalize_result] eq "1"} {return 1} fossil test-th-eval --open-config "setting tcl" if {[normalize_result] eq "1"} {return 1} return [info exists ::env(TH1_ENABLE_TCL)] } # This procedure only returns non-zero if the TH1 hooks feature was enabled # at compile-time and is now enabled at runtime. proc are_th1_hooks_usable_by_fossil {} { fossil test-th-eval "hasfeature th1Hooks" if {[normalize_result] ne "1"} {return 0} fossil test-th-eval "setting th1-hooks" if {[normalize_result] eq "1"} {return 1} fossil test-th-eval --open-config "setting th1-hooks" if {[normalize_result] eq "1"} {return 1} return [info exists ::env(TH1_ENABLE_HOOKS)] } # Run the given command script inside the Fossil source repo checkout. # # Callers of this function must ensure two things: # # 1. This test run is in fact being done from within a Fossil repo # checkout directory. If you are unsure, test $::outside_fossil_repo # or call one of the test_* wrappers below which do that for you. # # As a rule, you should not be calling this function directly! # # 2. This test run is being done from a repo checkout directory that # doesn't have any uncommitted changes. If it does, that affects the # output of any test based on the output of "fossil status", # "... diff", etc., which is likely to make the test appear to fail. # If you must call this function directly, test $::dirty_ckout and # skip the call if it's true. The test_* wrappers do this for you. # # 3. The test does NOT modify the Fossil checkout tree in any way. proc run_in_checkout { script {dir ""} } { if {[string length $dir] == 0} {set dir $::testfiledir} set savedPwd [pwd]; cd $dir set code [catch { uplevel 1 $script } result] cd $savedPwd; unset savedPwd return -code $code $result } # Wrapper for the above function pair. The tscript parameter is an # optional post-run test script. Some callers choose instead to put # the tests inline with the rscript commands. # # Be sure to adhere to the requirements of run_in_checkout! proc test_block_in_checkout { name rscript {tscript ""} } { if {$::outside_fossil_repo || $::dirty_ckout} { set $::CODE 0 set $::RESULT "" } else { uplevel 1 [list run_in_checkout $rscript] if {[string length $tscript] == 0} { return "" } else { set code [catch { uplevel 1 $tscript } result] return -code $code $result } } } # Single-test wrapper for the above. proc test_in_checkout { name rscript tscript } { return test_block_in_checkout name rscript { test $name $tscript } } # 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 {constraints ""}} { set expected [normalize_status_list $expected] set result [normalize_status_list $result] if {$result eq $expected} { test $name 1 $constraints } else { protOut " Expected:\n [join $expected "\n "]" 1 protOut " Got:\n [join $result "\n "]" 1 test $name 0 $constraints } } # Perform a test on the contents of a file # proc test_file_contents {name path expected {constraints ""}} { if {[file exists $path]} { set result [read_file $path] set passed [expr {$result eq $expected}] if {!$passed} { set expectedLines [split $expected "\n"] set resultLines [split $result "\n"] protOut " Expected:\n [join $expectedLines "\n "]" 1 protOut " Got:\n [join $resultLines "\n "]" 1 } } else { set passed 0 protOut " File does not exist: $path" 1 } test $name $passed $constraints } # Append all arguments into a single value and then returns it. # proc appendArgs {args} { eval append result $args } # Returns the value of the specified environment variable -OR- any empty # string if it does not exist. # proc getEnvironmentVariable { name } { return [expr {[info exists ::env($name)] ? $::env($name) : ""}] } # Returns a usable temporary directory -OR- fails the testing process. # proc getTemporaryPath {} { # # NOTE: Build the list of "temporary directory" environment variables # to check, including all reasonable "cases" of the environment # variable names. # set names [list] # # TODO: Add more here, if necessary. # foreach name [list FOSSIL_TEST_TEMP FOSSIL_TEMP TEMP TMP] { lappend names [string toupper $name] [string tolower $name] \ [string totitle $name] } # # NOTE: Check if we can use any of the environment variables. # foreach name $names { set value [getEnvironmentVariable $name] if {[string length $value] > 0} { set value [file normalize $value] if {[file exists $value] && [file isdirectory $value]} { return $value } } } # # NOTE: On non-Windows systems, fallback to /tmp if it is usable. # if {!$::is_windows} { set value /tmp if {[file exists $value] && [file isdirectory $value]} { return $value } } # # NOTE: There must be a usable temporary directory to continue testing. # error "Cannot find a usable temporary directory, testing halted." } # 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 .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 } { set fileName [getTh1SetupFileName] file mkdir [file dirname $fileName] return [write_file $fileName $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]} { 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]} { 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 {constraints ""}} { global bad_test ignored_test test_count RESULT incr test_count set knownBug [expr {"knownBug" in $constraints}] set r [uplevel 1 [list expr $expr]] if {$r} { if {$knownBug && !$::STRICT} { protOut "test $name OK (knownBug)?" } else { protOut "test $name OK" } } else { if {$knownBug && !$::STRICT} { protOut "test $name FAILED (knownBug)!" 1 lappend ignored_test $name } else { protOut "test $name FAILED!" 1 if {$::QUIET} {protOut "RESULT: $RESULT" 1} lappend bad_test $name if {$::HALT} {exit 1} } } } set bad_test {} set ignored_test {} set skipped_tests {} # Return a random string N characters long. # set vocabulary 01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" append vocabulary " ()*^!.eeeeeeeeaaaaattiioo " set nvocabulary [string length $vocabulary] proc rand_str {N} { |
︙ | ︙ | |||
164 165 166 167 168 169 170 171 172 | set line [string range $line 0 $i]$stuff[string range $line $ip1 end] } } append out \n$line } return [string range $out 1 end] } foreach testfile $argv { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | 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 | set line [string range $line 0 $i]$stuff[string range $line $ip1 end] } } append out \n$line } return [string range $out 1 end] } # This procedure executes the "fossil server" command. The return value # is a list comprised of the new process identifier and the port on which # the server started. The varName argument refers to a variable # where the "stop argument" is to be stored. This value must eventually be # passed to the [test_stop_server] procedure. proc test_start_server { repository {varName ""} } { global fossilexe tempPath set command [list exec $fossilexe server --localhost] if {[string length $varName] > 0} { upvar 1 $varName stopArg } if {$::is_windows} { set stopArg [file join [getTemporaryPath] [appendArgs \ [string trim [clock seconds] -] _ [getSeqNo] .stopper]] lappend command --stopper $stopArg } set outFileName [file join $tempPath [appendArgs \ fossil_server_ [string trim [clock seconds] -] _ \ [getSeqNo]]].out lappend command $repository >&$outFileName & set pid [eval $command] if {!$::is_windows} { set stopArg $pid } after 1000; # output might not be there yet set output [read_file $outFileName] if {![regexp {Listening.*TCP port (\d+)} $output dummy port]} { puts stdout "Could not detect Fossil server port, using default..." set port 8080; # return the default port just in case } return [list $pid $port $outFileName] } # This procedure stops a Fossil server instance that was previously started # by the [test_start_server] procedure. The value of the "stop argument" # will vary by platform as will the exact method used to stop the server. # The fileName argument is the name of a temporary output file to delete. proc test_stop_server { stopArg pid fileName } { if {$::is_windows} { # # NOTE: On Windows, the "stop argument" must be the name of a file # that does NOT already exist. # if {[string length $stopArg] > 0 && \ ![file exists $stopArg] && \ [catch {write_file $stopArg [clock seconds]}] == 0} { while {1} { if {[catch { # # NOTE: Using the TaskList utility requires Windows XP or # later. # exec tasklist.exe /FI "PID eq $pid" } result] != 0 || ![regexp -- " $pid " $result]} { break } after 1000; # wait a bit... } file delete $stopArg if {[string length $fileName] > 0} { file delete $fileName } return true } } else { # # NOTE: On Unix, the "stop argument" must be an integer identifier # that refers to an existing process. # if {[regexp {^(?:-)?\d+$} $stopArg] && \ [catch {exec kill -TERM $stopArg}] == 0} { while {1} { if {[catch { # # TODO: Is this portable to all the supported variants of # Unix? It should be, it's POSIX. # exec ps -p $pid } result] != 0 || ![regexp -- "(?:^$pid| $pid) " $result]} { break } after 1000; # wait a bit... } if {[string length $fileName] > 0} { file delete $fileName } return true } } return false } # 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 --in $inFileName --out $outFileName --ipaddr 127.0.0.1 \ $repository --localauth --th-trace -expectError set result [expr {[file exists $outFileName] ? [read_file $outFileName] : ""}] if {1} { 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]] } # fixup the line-endings in the result to make it easier to compare. proc normalize_result_no_trim {} { return [string map [list \r\n \n] $::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] } set tempPath [getTemporaryPath] if {$is_windows} { set tempPath [string map [list \\ /] $tempPath] } if {[catch { set tempFile [file join $tempPath temporary.txt] write_file $tempFile [clock seconds]; file delete $tempFile } error] != 0} { error "Could not write file \"$tempFile\" in directory \"$tempPath\",\ please set TEMP variable in environment, error: $error" } set tempHomePath [file join $tempPath home_[pid]] # Close stdin to avoid errors on wrapped text for narrow terminals. # Closing stdin means that terminal detection returns 0 width, in turn # causing the relvant strings to be printed on a single line. # However, closing stdin makes file descriptor 0 avaailable on some systems # and/or TCL implementations, which triggers fossil to complain about opening # databases using fd 0. Avoid this by opening the script, consuming fd 0. close stdin set possibly_fd0 [open [info script] r] if {[catch { file mkdir $tempHomePath } error] != 0} { error "Could not make directory \"$tempHomePath\",\ please set TEMP variable in environment, error: $error" } protInit $fossilexe set ::tempKeepHome 1 # Start in tempHomePath to help avoid errors with require_no_open_checkout set startPwd [pwd] cd $tempHomePath foreach testfile $argv { protOut "***** $testfile ******" if { [catch {source $testdir/$testfile.test} testerror testopts] } { test test-framework-$testfile 0 protOut "!!!!! $testfile: $testerror" protOutDict $testopts" } else { test test-framework-$testfile 1 } protOut "***** End of $testfile: [llength $bad_test] errors so far ******" } cd $startPwd unset ::tempKeepHome; delete_temporary_home # Clean up the file descriptor close $possibly_fd0 set nErr [llength $bad_test] if {$nErr>0 || !$::QUIET} { protOut "***** Final results: $nErr errors out of $test_count tests" 1 } if {$nErr>0} { protOut "***** Considered failures: $bad_test" 1 } set nErr [llength $ignored_test] if {$nErr>0 || !$::QUIET} { protOut "***** Ignored results: $nErr ignored errors out of $test_count tests" 1 } if {$nErr>0} { protOut "***** Ignored failures: $ignored_test" 1 } set nSkipped [llength $skipped_tests] if {$nSkipped>0} { protOut "***** Skipped tests: $skipped_tests" 1 } if {$bad_test>0} { exit 1 } |
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | # # 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 {[normalize_result] ne "1"} { puts "Fossil was not compiled with TH1 docs support." test_cleanup_then_return } fossil test-th-eval "hasfeature tcl" if {[normalize_result] ne "1"} { puts "Fossil was not compiled with Tcl support." test_cleanup_then_return } ############################################################################### test_setup ############################################################################### 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]} { error "unable to locate repository" } set dataFileName [file join $::testdir th1-docs-input.txt] set origFileStat [file join $::testdir fileStat.th1] if {![file exists $origFileStat]} { error "unable to locate [$origFileStat]" } file copy $origFileStat fileStat.th1 fossil add fileStat.th1 fossil commit -m "Add fileStat.th1" ############################################################################### set RESULT [test_fossil_http \ $repository $dataFileName /doc/trunk/fileStat.th1] test th1-docs-1a {[regexp {<title>Unnamed Fossil Project: fileStat.th1</title>} $RESULT]} test th1-docs-1b {[regexp {>\[[0-9a-f]{40,64}\]<} $RESULT]} test th1-docs-1c {[regexp { contains \d+ files\.} $RESULT]} ############################################################################### test_cleanup |
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 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 | # # 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 {[normalize_result] ne "1"} { puts "Fossil was not compiled with TH1 hooks support." test_cleanup_then_return } ############################################################################### test_setup ############################################################################### write_file f1 "f1"; fossil add f1; fossil commit -m "c1" ############################################################################### 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 5 "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"} { append_hook_log "CUSTOM TIMELINE" emit_hook_log return "custom timeline" } elseif {[lindex $::cmd_args $length] eq "custom2"} { emit_hook_log puts "+++ some stuff here +++" continue "custom2 timeline" } elseif {[lindex $::cmd_args $length] eq "custom3"} { emit_hook_log return -code 5 "TH_RETURN return code" } 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]} { error "unable to locate repository" } set dataFileName [file join $::testdir th1-hooks-input.txt] ############################################################################### set savedTh1Setup [fossil settings th1-setup] fossil settings th1-setup $testTh1Setup ############################################################################### fossil timeline custom -expectError; # NOTE: Bad "WHEN" argument. test th1-cmd-hooks-1a {[normalize_result] eq \ {<h1><b>command_hook timeline CUSTOM TIMELINE</b></h1> unknown check-in or invalid date: custom}} ############################################################################### fossil timeline custom2; # NOTE: Bad "WHEN" argument. test th1-cmd-hooks-1b {[normalize_result] eq \ {<h1><b>command_hook timeline</b></h1> +++ some stuff here +++ <h1><b>command_hook timeline command_notify timeline</b></h1>}} ############################################################################### fossil timeline custom3 -expectError; # NOTE: Bad "WHEN" argument. test th1-cmd-hooks-1c {[normalize_result] eq \ {<h1><b>command_hook timeline</b></h1> unknown check-in or invalid date: custom3}} ############################################################################### 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 -n -1 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 -expectError test th1-custom-cmd-4a {[first_data_line] eq \ {<h1><b>command_hook test4</b></h1>}} test th1-custom-cmd-4b {[regexp -- \ {: unknown command: test4$} [second_data_line]]} test th1-custom-cmd-4d {[regexp -- \ {: use "help" for more information$} [third_data_line]]} ############################################################################### set RESULT [test_fossil_http $repository $dataFileName /timeline] test th1-web-hooks-1a {[regexp \ {<title>Unnamed Fossil Project: 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>}} ############################################################################### fossil settings th1-setup $savedTh1Setup ############################################################################### test_cleanup |
Added test/th1-repo.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 | # # Copyright (c) 2011 D. Richard Hipp # Copyright (c) 2015 Ch. Drexler # # 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/ # # Chris Drexler <ckolumbus@ac-drexler.de> # ############################################################################ # # TH1 tests that may modify the repository # set path [file dirname [info script]] require_no_open_checkout ######################################## # Setup: Add Files and Commit # ######################################## test_setup; set rootDir [file normalize [pwd]] write_file f1.md "f1" write_file f2.md "f2" write_file f3.txt "f3" write_file f4.md "f4" file mkdir [file join $rootDir subdirA] # NOTE: There are no files in subdirA. file mkdir [file join $rootDir subdirB] write_file [file join $rootDir subdirB f5.md] "f5" write_file [file join $rootDir subdirB f6.md] "f6" write_file [file join $rootDir subdirB f7.txt] "f7" write_file [file join $rootDir subdirB f8.md] "f8" write_file [file join $rootDir subdirB f9.wiki] "f9" file mkdir [file join $rootDir subdirC] write_file [file join $rootDir subdirC f10.md] "f10" write_file [file join $rootDir subdirC f11t.xt] "f11" set files_md [list subdirB/f5.md subdirB/f6.md subdirB/f8.md subdirC/f10.md] fossil add $rootDir fossil commit -m "c1" ############################################################################### fossil test-th-eval --open-config "dir trunk subdir*/*.md" test th1-dir-1 {[llength $RESULT] eq [llength $files_md]} set n 1 foreach i $RESULT j $files_md { test th1-dir-2.$n {$i eq $j} set n [expr {$n + 1}] } ############################################################################### set dateTime {\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}} fossil test-th-eval --open-config "dir trunk subdir*/*.md 1" test th1-dir-3.1 {[lindex [lindex $RESULT 0] 0] eq "subdirB/f5.md"} test th1-dir-3.2 {[lindex [lindex $RESULT 0] 1] == 2} test th1-dir-3.3 {[regexp -- $dateTime [lindex [lindex $RESULT 0] 2]]} test th1-dir-3.4 {[lindex [lindex $RESULT 1] 0] eq "subdirB/f6.md"} test th1-dir-3.5 {[lindex [lindex $RESULT 1] 1] == 2} test th1-dir-3.6 {[regexp -- $dateTime [lindex [lindex $RESULT 1] 2]]} test th1-dir-3.7 {[lindex [lindex $RESULT 2] 0] eq "subdirB/f8.md"} test th1-dir-3.8 {[lindex [lindex $RESULT 2] 1] == 2} test th1-dir-3.9 {[regexp -- $dateTime [lindex [lindex $RESULT 2] 2]]} test th1-dir-3.10 {[lindex [lindex $RESULT 3] 0] eq "subdirC/f10.md"} test th1-dir-3.11 {[lindex [lindex $RESULT 3] 1] == 3} test th1-dir-3.12 {[regexp -- $dateTime [lindex [lindex $RESULT 3] 2]]} ############################################################################### test_cleanup |
Added test/th1-tcl.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 | # # 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/Tcl integration # set path [file dirname [info script]] ############################################################################### fossil test-th-eval "hasfeature tcl" if {[normalize_result] ne "1"} { puts "Fossil was not compiled with Tcl support." test_cleanup_then_return } ############################################################################### test_setup ############################################################################### set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test. ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $path th1-tcl1.txt]] test th1-tcl-1 {[regexp -- {^tclReady\(before\) = 0 tclReady\(after\) = 1 \d+ \d+ \d+ via Tcl invoke 4 4 two words one_word three words now \d+ two words 4 \d+ two words 4 \d+ one_word three words now$} [normalize_result]]} ############################################################################### if {[catch {package require sqlite3}] == 0} { fossil test-th-render --open-config \ [file nativename [file join $path th1-tcl2.txt]] test th1-tcl-2 {[regexp -- {^\d+$} [normalize_result]]} } else { puts stderr "Skipping 'th1-tcl-2', SQLite package for Tcl not available" } ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $path 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 $path 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 $path 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 $path 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 $path 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 $path 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 $path th1-tcl9.txt]] # Under cygwin, the printed name with Usage: strips the extension if { $::is_cygwin && [file extension $fossilexe] eq ".exe" } { set fossilexeref [string range $fossilexe 0 end-4] } else { set fossilexeref $fossilexe } test th1-tcl-9 {[string trim $RESULT] eq [list [file tail $fossilexeref] 3 \ [list test-th-render --open-config [file nativename [file join $path \ th1-tcl9.txt]]]]} ############################################################################### fossil test-th-eval "tclMakeSafe a" test th1-tcl-10 {[normalize_result] eq \ {TH_ERROR: wrong # args: should be "tclMakeSafe"}} ############################################################################### fossil test-th-eval "list \[tclIsSafe\] \[tclMakeSafe\] \[tclIsSafe\]" test th1-tcl-11 {[normalize_result] eq {0 {} 1}} ############################################################################### fossil test-th-eval "tclMakeSafe; tclMakeSafe" test th1-tcl-12 {[normalize_result] eq \ {TH_ERROR: Tcl interpreter is already 'safe'}} ############################################################################### fossil test-th-eval "tclEval pwd; tclMakeSafe; tclEval pwd" test th1-tcl-13 {[normalize_result] eq {TH_ERROR: invalid command name "pwd"}} ############################################################################### fossil test-th-eval "tclMakeSafe; tclExpr {0 + \[string length \[pwd\]\]}" test th1-tcl-14 {[normalize_result] eq {TH_ERROR: invalid command name "pwd"}} ############################################################################### fossil test-th-eval "tclInvoke pwd; tclMakeSafe; tclInvoke pwd" test th1-tcl-15 {[normalize_result] eq {TH_ERROR: Tcl command not found: pwd}} ############################################################################### fossil test-th-eval "tclMakeSafe; tclEval set x 2" test th1-tcl-16 {[normalize_result] eq {2}} ############################################################################### fossil test-th-eval "tclMakeSafe; tclEval set x 2; tclEval info vars x" test th1-tcl-17 {[normalize_result] eq {x}} ############################################################################### test_cleanup |
Added test/th1-tcl1.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 | <th1> # # This is a "TH1 fragment" used to test the Tcl integration features of TH1. # The corresponding test file executes this file using the test-th-render # Fossil command. # proc doOut {msg} {puts $msg; puts \n} doOut "tclReady(before) = [tclReady]" set channel stdout; tclInvoke set channel $channel doOut "tclReady(after) = [tclReady]" doOut [tclEval clock seconds] doOut [tclEval {set x [clock seconds]}] tclEval {puts $channel "[clock seconds]"} tclInvoke puts $channel "via Tcl invoke" doOut [tclExpr 2+2] doOut [tclExpr 2 + 2] doOut [tclInvoke set x "two words"] doOut [tclInvoke eval set y one_word] doOut [tclInvoke eval {set z "three words now"}] doOut [set x [tclEval {set x [clock seconds]}]] doOut [tclInvoke th1Eval {set y "two words"}] doOut [set z [tclInvoke th1Expr {2+2}]] doOut $x doOut $y doOut $z doOut [tclEval set x] doOut [tclEval set y] doOut [tclEval set z] </th1> |
Added test/th1-tcl2.txt.
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <th1> # # This is a "TH1 fragment" used to test the Tcl integration features of TH1. # The corresponding test file executes this file using the test-th-render # Fossil command. # # NOTE: This test requires that the SQLite package be available for the Tcl # interpreter that is linked to the Fossil executable. # tclInvoke set repository_name [repository 1] proc doOut {msg} {puts $msg; puts \n} doOut [tclEval { package require sqlite3 sqlite3 db $repository_name -readonly true set x [db eval {SELECT COUNT(*) FROM user;}] db close return $x }] </th1> |
Added test/th1-tcl3.txt.
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | <th1> # # This is a "TH1 fragment" used to test the Tcl integration features of TH1. # The corresponding test file executes this file using the test-th-render # Fossil command. # proc doOut {msg} {puts $msg; puts \n} doOut [tclEval bad_command] </th1> |
Added test/th1-tcl4.txt.
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | <th1> # # This is a "TH1 fragment" used to test the Tcl integration features of TH1. # The corresponding test file executes this file using the test-th-render # Fossil command. # proc doOut {msg} {puts $msg; puts \n} doOut [tclExpr 2/0] </th1> |
Added test/th1-tcl5.txt.
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | <th1> # # This is a "TH1 fragment" used to test the Tcl integration features of TH1. # The corresponding test file executes this file using the test-th-render # Fossil command. # proc doOut {msg} {puts $msg; puts \n} doOut [tclInvoke bad_command] </th1> |
Added test/th1-tcl6.txt.
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | <th1> # # This is a "TH1 fragment" used to test the Tcl integration features of TH1. # The corresponding test file executes this file using the test-th-render # Fossil command. # proc doOut {msg} {puts $msg; puts \n} doOut [tclEval th1Eval bad_command] </th1> |
Added test/th1-tcl7.txt.
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <th1> # # This is a "TH1 fragment" used to test the Tcl integration features of TH1. # The corresponding test file executes this file using the test-th-render # Fossil command. # proc doOut {msg} {puts $msg; puts \n} # # BUGBUG: Attempting to divide by zero will crash TH1 with the error: # "child killed: floating-point exception" # # doOut [tclEval th1Expr 2/0] # # NOTE: For now, just cause an expression syntax error. # doOut [tclEval th1Expr 2**0] </th1> |
Added test/th1-tcl8.txt.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <th1> # # This is a "TH1 fragment" used to test the Tcl integration features of TH1. # The corresponding test file executes this file using the test-th-render # Fossil command. # proc doOut {msg} {puts $msg; puts \n} if {[tclInvoke set tcl_version] >= 8.6} { doOut [tclInvoke tailcall set x 1] } else { error "This test requires Tcl 8.6 or higher." } </th1> |
Added test/th1-tcl9.txt.
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | <th1> # # This is a "TH1 fragment" used to test the Tcl integration features of TH1. # The corresponding test file executes this file using the test-th-render # Fossil command. # set channel stdout; tclInvoke set channel $channel tclEval {puts $channel [list [file tail $argv0] $argc $argv]} </th1> |
Added test/th1.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 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 | # # 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 Commands # set path [file dirname [info script]]; test_setup ############################################################################### set th1Tcl [is_tcl_usable_by_fossil] set th1Hooks [are_th1_hooks_usable_by_fossil] ############################################################################### 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 || $RESULT eq "on"} ############################################################################### fossil test-th-eval --open-config "setting -strict autosync" test th1-setting-6 {$RESULT eq 0 || $RESULT eq 1 || $RESULT eq "on"} ############################################################################### 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"}} ############################################################################### set skip_anycap 1 if {$::outside_fossil_repo} { puts "Skipping th1-anycap-*-1 perm tests: not in Fossil repo checkout." } elseif ($::dirty_ckout) { puts "Skipping th1-anycap-*-1 perm tests: uncommitted changes in Fossil checkout." } else { set skip_anycap 0 } # NOTE: The 'd' permission is no longer used. foreach perm [list \ a b c e f g h i j k l m n o p q r s t u v w x y z \ A D \ 2 3 4 5 6 7 ] { if {$perm eq "u"} continue; # NOTE: Skip "reader" meta-permission. if {$perm eq "v"} continue; # NOTE: Skip "developer" meta-permission. set ::env(TH1_TEST_USER_CAPS) sxy fossil test-th-eval "anycap $perm" test th1-anycap-no-$perm-1 {$RESULT eq {0}} fossil test-th-eval "hascap $perm" test th1-hascap-no-$perm-1 {$RESULT eq {0}} fossil test-th-eval "anoncap $perm" test th1-anoncap-no-$perm-1 {$RESULT eq {0}} if {$skip_anycap} { continue } run_in_checkout { set ::env(TH1_TEST_USER_CAPS) sxy fossil test-th-eval --set-user-caps "anycap $perm" test th1-anycap-yes-$perm-1 {$RESULT eq {1}} set ::env(TH1_TEST_USER_CAPS) 1; # NOTE: Bad permission. fossil test-th-eval --set-user-caps "anycap $perm" test th1-anycap-no-$perm-1 {$RESULT eq {0}} set ::env(TH1_TEST_USER_CAPS) sxy fossil test-th-eval --set-user-caps "hascap $perm" test th1-hascap-yes-$perm-1 {$RESULT eq {1}} set ::env(TH1_TEST_USER_CAPS) 1; # NOTE: Bad permission. fossil test-th-eval --set-user-caps "hascap $perm" test th1-hascap-no-$perm-1 {$RESULT eq {0}} unset ::env(TH1_TEST_USER_CAPS) set ::env(TH1_TEST_ANON_CAPS) sxy fossil test-th-eval --set-anon-caps "anoncap $perm" test th1-anoncap-yes-$perm-1 {$RESULT eq {1}} set ::env(TH1_TEST_ANON_CAPS) 1; # NOTE: Bad permission. fossil test-th-eval --set-anon-caps "anoncap $perm" test th1-anoncap-no-$perm-1 {$RESULT eq {0}} unset ::env(TH1_TEST_ANON_CAPS) } } ############################################################################### fossil test-th-eval "anycap oh" test th1-anycap-no-multiple-1 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "hascap oh" test th1-hascap-no-multiple-1 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "hascap o h" test th1-hascap-no-multiple-2 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "anoncap oh" test th1-anoncap-no-multiple-1 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "anoncap o h" test th1-anoncap-no-multiple-2 {$RESULT eq {0}} ############################################################################### test_block_in_checkout "test-anoncap-*" { fossil test-th-eval --set-user-caps "anycap oh" test th1-anycap-yes-multiple-1 {$RESULT eq {1}} set ::env(TH1_TEST_USER_CAPS) o fossil test-th-eval --set-user-caps "anycap oh" test th1-anycap-yes-multiple-2 {$RESULT eq {1}} unset ::env(TH1_TEST_USER_CAPS) fossil test-th-eval --set-user-caps "hascap oh" test th1-hascap-yes-multiple-1 {$RESULT eq {1}} set ::env(TH1_TEST_USER_CAPS) o fossil test-th-eval --set-user-caps "hascap oh" test th1-hascap-no-multiple-3 {$RESULT eq {0}} unset ::env(TH1_TEST_USER_CAPS) fossil test-th-eval --set-user-caps "hascap o h" test th1-hascap-yes-multiple-2 {$RESULT eq {1}} set ::env(TH1_TEST_USER_CAPS) o fossil test-th-eval --set-user-caps "hascap o h" test th1-hascap-no-multiple-4 {$RESULT eq {0}} unset ::env(TH1_TEST_USER_CAPS) fossil test-th-eval --set-anon-caps "anoncap oh" test th1-anoncap-yes-multiple-1 {$RESULT eq {1}} set ::env(TH1_TEST_ANON_CAPS) o fossil test-th-eval --set-anon-caps "anoncap oh" test th1-anoncap-no-multiple-3 {$RESULT eq {0}} unset ::env(TH1_TEST_ANON_CAPS) fossil test-th-eval --set-anon-caps "anoncap o h" test th1-anoncap-yes-multiple-2 {$RESULT eq {1}} set ::env(TH1_TEST_ANON_CAPS) o fossil test-th-eval --set-anon-caps "anoncap o h" test th1-anoncap-no-multiple-4 {$RESULT eq {0}} unset ::env(TH1_TEST_ANON_CAPS) } ############################################################################### test_in_checkout th1-checkout-1 { # NOTE: The "1" here forces the checkout to be opened. fossil test-th-eval "checkout 1" } {[string length $RESULT] > 0} ############################################################################### test_in_checkout th1-checkout-2 { if {$th1Hooks} { fossil test-th-eval "checkout" } else { # NOTE: No TH1 hooks, force checkout to be populated. fossil test-th-eval --open-config "checkout" } } {[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 {}" -expectError set normalized_result [normalize_result] regsub -- {\n/\*\*\*\*\* Subprocess \d+ exit\(\d+\) \*\*\*\*\*/} \ $normalized_result {} normalized_result if {$th1Hooks} { test th1-trace-2 {$normalized_result eq \ {------------------ BEGIN TRACE LOG ------------------ th1-init 0x0 => 0x0<br> ------------------- END TRACE LOG -------------------}} } else { test th1-trace-2 {$normalized_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.}" -expectError set normalized_result [normalize_result] regsub -- {\n/\*\*\*\*\* Subprocess \d+ exit\(\d+\) \*\*\*\*\*/} \ $normalized_result {} normalized_result if {$th1Hooks} { test th1-trace-4 {$normalized_result eq \ {------------------ BEGIN TRACE LOG ------------------ th1-init 0x0 => 0x0<br> this is a trace message. ------------------- END TRACE LOG -------------------}} } else { test th1-trace-4 {$normalized_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 "defHeader {Page Title Here}" test th1-defHeader-1 {$RESULT eq \ {TH_ERROR: wrong # args: should be "defHeader"}} ############################################################################### fossil test-th-eval "defHeader" test th1-defHeader-2 {[string match *<body> [normalize_result]] || \ [string match "*<body class=\"\$current_feature\ rpage-\$requested_page\ cpage-\$canonical_page\">" [normalize_result]]} ############################################################################### fossil test-th-eval "styleHeader {Page Title Here}" test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}} ############################################################################### test_in_checkout th1-header-2 { fossil test-th-eval --open-config "styleHeader {Page Title Here}" } {[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>\n</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}} ############################################################################### test_in_checkout th1-artifact-3 { fossil test-th-eval --open-config "artifact tip" } {[regexp -- {F test/th1\.test [0-9a-f]{40,64}} $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}} ############################################################################### test_in_checkout th1-artifact-7 { fossil test-th-eval --open-config "artifact tip test/th1.test" } {[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}} ############################################################################### test_in_checkout th1-globalState-1 { if {$th1Hooks} { fossil test-th-eval "globalState checkout" } else { # NOTE: No TH1 hooks, force checkout to be populated. fossil test-th-eval --open-config "globalState checkout" } } {[string length $RESULT] > 0} ############################################################################### test_block_in_checkout th1-globalState-2 { if {$th1Hooks} { fossil test-th-eval "globalState checkout" test th1-globalState-2 {$RESULT eq [fossil test-th-eval checkout]} } else { # NOTE: No TH1 hooks, force checkout to be populated. fossil test-th-eval --open-config "globalState checkout" test th1-globalState-2 {$RESULT eq \ [fossil test-th-eval --open-config 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"} ############################################################################### test_in_checkout th1-globalState-8 { if {$th1Hooks} { fossil test-th-eval "globalState repository" } else { # NOTE: No TH1 hooks, force repository to be populated. fossil test-th-eval --open-config "globalState repository" } } {[string length $RESULT] > 0} ############################################################################### test_block_in_checkout th1-globalState-9 { if {$th1Hooks} { fossil test-th-eval "globalState repository" test th1-globalState-9 {$RESULT eq [fossil test-th-eval repository]} } else { # NOTE: No TH1 hooks, force repository to be populated. fossil test-th-eval --open-config "globalState repository" test th1-globalState-9 {$RESULT eq \ [fossil test-th-eval --open-config 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 {$is_windows || $is_cygwin} { 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 ""} ############################################################################### # # NOTE: This test will fail if the command names are added to TH1, or # moved from Tcl builds to plain or the reverse. Sorting the # command lists eliminates a dependence on order. # fossil test-th-eval "info commands" set sorted_result [lsort $RESULT] protOut "Sorted: $sorted_result" set base_commands {anoncap anycap array artifact break breakpoint \ builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \ combobox continue copybtn date decorate defHeader dir enable_htmlify \ enable_output encode64 error expr for foreach getParameter glob_match \ globalState hascap hasfeature html htmlize http httpize if info \ insertCsrf lappend lindex linecount list llength lsearch markdown nonce \ proc puts query randhex redirect regexp reinitialize rename render \ repository return searchable set setParameter setting stime string \ styleFooter styleHeader styleScript submenu tclReady trace unset \ unversioned uplevel upvar utime verifyCsrf verifyLogin wiki} set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe} if {$th1Tcl} { test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]} } else { test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]} } ############################################################################### fossil test-th-eval "info vars" if {$th1Hooks} { test th1-info-vars-1 {[lsort $RESULT] eq \ [lsort "th_stack_trace cmd_flags tcl_platform cmd_name cmd_args"]} } else { test th1-info-vars-1 {$RESULT eq "tcl_platform"} } ############################################################################### fossil test-th-eval "set x 1; info vars" if {$th1Hooks} { test th1-info-vars-2 {[lsort $RESULT] eq \ [lsort "x th_stack_trace cmd_flags tcl_platform cmd_name cmd_args"]} } else { test th1-info-vars-2 {[lsort $RESULT] eq [lsort "x tcl_platform"]} } ############################################################################### fossil test-th-eval "set x 1; unset x; info vars" if {$th1Hooks} { test th1-info-vars-3 {[lsort $RESULT] eq \ [lsort "th_stack_trace cmd_flags tcl_platform cmd_name cmd_args"]} } else { test th1-info-vars-3 {$RESULT eq "tcl_platform"} } ############################################################################### fossil test-th-eval "proc foo {} {set x 1; info vars}; foo" test th1-info-vars-4 {$RESULT eq "x"} ############################################################################### fossil test-th-eval "set y 1; proc foo {} {set x 1; uplevel 1 {info vars}}; foo" if {$th1Hooks} { test th1-info-vars-5 {[lsort $RESULT] eq \ [lsort "th_stack_trace y cmd_flags tcl_platform cmd_name cmd_args"]} } else { test th1-info-vars-5 {[lsort $RESULT] eq [lsort "y tcl_platform"]} } ############################################################################### fossil test-th-eval "array exists foo" test th1-array-exists-1 {$RESULT eq "0"} ############################################################################### fossil test-th-eval "set foo(x) 1; array exists foo" test th1-array-exists-2 {$RESULT eq "1"} ############################################################################### fossil test-th-eval "set foo(x) 1; unset foo(x); array exists foo" test th1-array-exists-3 {$RESULT eq "1"} ############################################################################### fossil test-th-eval "set foo(x) 1; unset foo; array exists foo" test th1-array-exists-4 {$RESULT eq "0"} ############################################################################### fossil test-th-eval "set foo 1; array exists foo" test th1-array-exists-5 {$RESULT eq "0"} ############################################################################### fossil test-th-eval "array names foo" test th1-array-names-1 {$RESULT eq ""} ############################################################################### fossil test-th-eval "set foo 2; array names foo" test th1-array-names-2 {$RESULT eq ""} ############################################################################### fossil test-th-eval "set foo 2; unset foo; set foo(x) 2; array names foo" test th1-array-names-3 {$RESULT eq "x"} ############################################################################### fossil test-th-eval "set foo(x) 2; array names foo" test th1-array-names-4 {$RESULT eq "x"} ############################################################################### fossil test-th-eval "set foo(x) 2; set foo(y) 2; array names foo" test th1-array-names-5 {$RESULT eq "x y"} ############################################################################### fossil test-th-eval "set foo(x) 2; unset foo(x); array names foo" test th1-array-names-6 {$RESULT eq ""} ############################################################################### fossil test-th-eval "lsearch" test th1-lsearch-1 {$RESULT eq \ {TH_ERROR: wrong # args: should be "lsearch list string"}} ############################################################################### fossil test-th-eval "lsearch a" test th1-lsearch-2 {$RESULT eq \ {TH_ERROR: wrong # args: should be "lsearch list string"}} ############################################################################### fossil test-th-eval "lsearch a a a" test th1-lsearch-3 {$RESULT eq \ {TH_ERROR: wrong # args: should be "lsearch list string"}} ############################################################################### fossil test-th-eval "lsearch {a b c} a" test th1-lsearch-4 {$RESULT eq "0"} ############################################################################### fossil test-th-eval "lsearch {a b c} b" test th1-lsearch-5 {$RESULT eq "1"} ############################################################################### fossil test-th-eval "lsearch {a b c} c" test th1-lsearch-6 {$RESULT eq "2"} ############################################################################### fossil test-th-eval "lsearch {a b c} d" test th1-lsearch-7 {$RESULT eq "-1"} ############################################################################### fossil test-th-eval "lsearch {a b c} aa" test th1-lsearch-8 {$RESULT eq "-1"} ############################################################################### fossil test-th-eval "lsearch {aa b c} a" test th1-lsearch-9 {$RESULT eq "-1"} ############################################################################### fossil test-th-eval "lsearch \"\{aa b c\" a" test th1-lsearch-10 {$RESULT eq "TH_ERROR: Expected list, got: \"\{aa b c\""} ############################################################################### fossil test-th-eval "glob_match" test th1-glob-match-1 {$RESULT eq \ {TH_ERROR: wrong # args: should be "glob_match ?-one? ?--? patternList string"}} ############################################################################### fossil test-th-eval "glob_match -one" test th1-glob-match-2 {$RESULT eq \ {TH_ERROR: wrong # args: should be "glob_match ?-one? ?--? patternList string"}} ############################################################################### fossil test-th-eval "glob_match --" test th1-glob-match-3 {$RESULT eq \ {TH_ERROR: wrong # args: should be "glob_match ?-one? ?--? patternList string"}} ############################################################################### fossil test-th-eval "glob_match -one --" test th1-glob-match-4 {$RESULT eq \ {TH_ERROR: wrong # args: should be "glob_match ?-one? ?--? patternList string"}} ############################################################################### fossil test-th-eval "glob_match -one -- 1" test th1-glob-match-5 {$RESULT eq \ {TH_ERROR: wrong # args: should be "glob_match ?-one? ?--? patternList string"}} ############################################################################### fossil test-th-eval "glob_match -one -- 1 2 3" test th1-glob-match-6 {$RESULT eq \ {TH_ERROR: wrong # args: should be "glob_match ?-one? ?--? patternList string"}} ############################################################################### fossil test-th-eval {list [glob_match a A] [glob_match A a]} test th1-glob-match-7 {$RESULT eq "0 0"} ############################################################################### fossil test-th-eval {list [glob_match a,b a] [glob_match a,b b]} test th1-glob-match-8 {$RESULT eq "1 2"} ############################################################################### fossil test-th-eval {list [glob_match -one a,b a] [glob_match -one a,b b]} test th1-glob-match-9 {$RESULT eq "0 0"} ############################################################################### fossil test-th-eval {list [glob_match -one a,b a,b] [glob_match -one a b,a]} test th1-glob-match-10 {$RESULT eq "1 0"} ############################################################################### fossil test-th-eval {list [glob_match a*c abc] [glob_match abc a*c]} test th1-glob-match-11 {$RESULT eq "1 0"} ############################################################################### fossil test-th-eval {list [glob_match a?c abc] [glob_match abc a?c]} test th1-glob-match-12 {$RESULT eq "1 0"} ############################################################################### fossil test-th-eval {list [glob_match {a[bd]c} abc] [glob_match abc {a[bd]c}]} test th1-glob-match-13 {$RESULT eq "1 0"} ############################################################################### fossil test-th-eval {string is} test th1-string-is-1 {$RESULT eq \ {TH_ERROR: wrong # args: should be "string is class string"}} ############################################################################### fossil test-th-eval {string is something} test th1-string-is-2 {$RESULT eq \ {TH_ERROR: wrong # args: should be "string is class string"}} ############################################################################### fossil test-th-eval {string is not something else} test th1-string-is-3 {$RESULT eq \ {TH_ERROR: wrong # args: should be "string is class string"}} ############################################################################### fossil test-th-eval {string is other 123} test th1-string-is-4 {$RESULT eq \ "TH_ERROR: Expected alnum, double, integer, or list, got: other"} ############################################################################### fossil test-th-eval {string is alnum 123} test th1-string-is-5 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is alnum abc} test th1-string-is-6 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is alnum 123abc} test th1-string-is-7 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is alnum abc123} test th1-string-is-8 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is alnum _abc123} test th1-string-is-9 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is alnum abc.123} test th1-string-is-10 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is alnum abc123_} test th1-string-is-11 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is list ""} test th1-string-is-12 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is list 1} test th1-string-is-13 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is list "1 2 3"} test th1-string-is-14 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is list "\{"} test th1-string-is-15 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is list "1 2 3 \{"} test th1-string-is-16 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is list "1 2 3 \{\}"} test th1-string-is-17 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is list "1 2 3 \{\{\}"} test th1-string-is-18 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is double 123} test th1-string-is-19 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is double 123.456} test th1-string-is-20 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is double 123abc} test th1-string-is-21 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is double 123_456} test th1-string-is-22 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is integer 123} test th1-string-is-23 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is integer 123.456} test th1-string-is-24 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is integer 123abc} test th1-string-is-25 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is integer 0b11001001} test th1-string-is-26 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is integer 0b11001002} test th1-string-is-27 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is integer 0o777} test th1-string-is-28 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is integer 0o778} test th1-string-is-29 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string is integer 0xC0DEF00D} test th1-string-is-30 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {string is integer 0xC0DEF00Z} test th1-string-is-31 {$RESULT eq "0"} ############################################################################### fossil test-th-eval {string index "" -1} test th1-string-index-1 {$RESULT eq ""} ############################################################################### fossil test-th-eval {string index "" 0} test th1-string-index-2 {$RESULT eq ""} ############################################################################### fossil test-th-eval {string index "" 1} test th1-string-index-3 {$RESULT eq ""} ############################################################################### fossil test-th-eval {string index "" 2} test th1-string-index-4 {$RESULT eq ""} ############################################################################### fossil test-th-eval {string index "" end} test th1-string-index-5 {$RESULT eq ""} ############################################################################### fossil test-th-eval {string index A -1} test th1-string-index-6 {$RESULT eq ""} ############################################################################### fossil test-th-eval {string index A 0} test th1-string-index-7 {$RESULT eq "A"} ############################################################################### fossil test-th-eval {string index A 1} test th1-string-index-8 {$RESULT eq ""} ############################################################################### fossil test-th-eval {string index A 2} test th1-string-index-9 {$RESULT eq ""} ############################################################################### fossil test-th-eval {string index A end} test th1-string-index-10 {$RESULT eq "A"} ############################################################################### fossil test-th-eval {string index ABC -1} test th1-string-index-11 {$RESULT eq ""} ############################################################################### fossil test-th-eval {string index ABC 0} test th1-string-index-12 {$RESULT eq "A"} ############################################################################### fossil test-th-eval {string index ABC 1} test th1-string-index-13 {$RESULT eq "B"} ############################################################################### fossil test-th-eval {string index ABC 2} test th1-string-index-14 {$RESULT eq "C"} ############################################################################### fossil test-th-eval {string index ABC end} test th1-string-index-15 {$RESULT eq "C"} ############################################################################### fossil test-th-eval {markdown} test th1-markdown-1 {$RESULT eq \ {TH_ERROR: wrong # args: should be "markdown STRING"}} ############################################################################### fossil test-th-eval {markdown one two} test th1-markdown-2 {$RESULT eq \ {TH_ERROR: wrong # args: should be "markdown STRING"}} ############################################################################### fossil test-th-eval {markdown "*This is a test.*"} test th1-markdown-3 {[normalize_result] eq {{} {<div class="markdown"> <p><em>This is a test.</em></p> </div> }}} ############################################################################### fossil test-th-eval {markdown "Test1\n=====\n*This is a test.*"} test th1-markdown-4 {[normalize_result] eq {Test1 {<div class="markdown"> <p><em>This is a test.</em></p> </div> }}} ############################################################################### set markdown [read_file [file join $path markdown-test1.md]] fossil test-th-eval [string map \ [list %markdown% $markdown] {markdown {%markdown%}}] test th1-markdown-5 {[normalize_result] eq \ {{Markdown Formatter Test Document} {<div class="markdown"> <p>This document is designed to test the markdown formatter.</p> <ul> <li>A bullet item. <ul> <li>A subitem</li> </ul></li> <li>Second bullet</li> </ul> <p>More text</p> <ol> <li>Enumeration 1.1. Subitem 1 1.2. Subitem 2</li> <li>Second enumeration.</li> </ol> <p>Another paragraph.</p> <h2>Other Features</h2> <p>Text can show <em>emphasis</em> or <em>emphasis</em> or <strong>strong emphassis</strong>.</p> </div> }}} ############################################################################### # Verify that quoted delimiters like * and _ don't terminate their # span. This was fixed by checkin [dd41f85acf]. fossil test-th-eval {markdown "*This is\\*a test.*\n_second\\_test_"} test th1-markdown-6 {[normalize_result] eq {{} {<div class="markdown"> <p><em>This is*a test.</em> <em>second_test</em></p> </div> }}} ############################################################################### fossil test-th-eval {encode64 test} test th1-encode64-1 {$RESULT eq "dGVzdA=="} ############################################################################### fossil test-th-eval {encode64 test\x00} test th1-encode64-2 {$RESULT eq "dGVzdAA="} ############################################################################### # # This test will fail if the Fossil source file named below changes. Update # the expected result string below if that happens. # test_in_checkout th1-encode64-3 { fossil test-th-eval --open-config \ {encode64 [artifact trunk ajax/cgi-bin/fossil-json.cgi.example]} } { $RESULT eq "IyEvcGF0aC90by9mb3NzaWwvYmluYXJ5CnJlcG9zaXRvcnk6IC9wYXRoL3RvL3JlcG8uZnNsCg==" } ############################################################################### fossil test-th-eval {array exists tcl_platform} test th1-platform-1 {$RESULT eq "1"} ############################################################################### fossil test-th-eval {array names tcl_platform} test th1-platform-2 {$RESULT eq "engine platform"} ############################################################################### fossil test-th-eval {set tcl_platform(engine)} test th1-platform-3 {$RESULT eq "TH1"} ############################################################################### fossil test-th-eval {set tcl_platform(platform)} test th1-platform-4 {$RESULT eq "windows" || $RESULT eq "unix"} ############################################################################### set th1FileName [file join $::tempPath th1-[pid].th1] write_file $th1FileName { set x "" for {set i 0} {$i < 10} {set i [expr {$i + 1}]} { set x "$x $i" } return [string trim $x] set y; # NOTE: Never hit. } fossil test-th-source $th1FileName test th1-source-1 {$RESULT eq {TH_RETURN: 0 1 2 3 4 5 6 7 8 9}} file delete $th1FileName ############################################################################### # Tests for the TH1 unversioned command require at least one # unversioned file in the repository. All tests run in a freshly # created checkout of a freshly created repo, so we can just # create a file or two for the purpose. write_file ten.txt "0123456789" fossil unversioned add ten.txt fossil unversioned list # unversioned list fossil test-th-eval --open-config "unversioned list" test th1-unversioned-1 {[normalize_result] eq {ten.txt}} # unversioned content fossil test-th-eval --open-config \ {string length [unversioned content ten.txt]} test th1-unversioned-2 {$RESULT eq {10}} ############################################################################### test_cleanup |
Added test/unversioned.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 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 | # # Copyright (c) 2016 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/ # ############################################################################ # # The "unversioned" command. # set path [file dirname [info script]] if {[catch {package require sha1}] != 0} { puts "The \"sha1\" package is not available." test_cleanup_then_return } require_no_open_checkout test_setup; set rootDir [file normalize [pwd]] # Avoid delays from the backoffice. fossil set backoffice-disable 1 fossil test-th-eval --open-config {repository} set repository [normalize_result] if {[string length $repository] == 0} { puts "Detection of the open repository file failed." test_cleanup_then_return } write_file unversioned1.txt "This is unversioned file #1." write_file unversioned2.txt " This is unversioned file #2. " write_file "unversioned space.txt" "\nThis is unversioned file #3.\n" write_file unversioned4.txt "This is unversioned file #4." write_file unversioned5.txt "This is unversioned file #5." set env(VISUAL) [appendArgs \ [info nameofexecutable] " " [file join $path fake-editor.tcl]] ############################################################################### # Under cygwin, the printed name with Usage: strips the extension if { $::is_cygwin && [file extension $fossilexe] eq ".exe" } { set fossilexeref [string range $fossilexe 0 end-4] } else { set fossilexeref $fossilexe } fossil unversioned -expectError test unversioned-1 {[normalize_result] eq \ [string map [list %fossil% [file nativename $fossilexeref]] {Usage: %fossil%\ unversioned add|cat|edit|export|list|revert|remove|sync|touch}]} ############################################################################### fossil unversioned list test unversioned-2 {[normalize_result] eq {}} ############################################################################### fossil unversioned cat not-found.txt test unversioned-3 {[normalize_result] eq {}} ############################################################################### fossil unversioned cat unversioned1.txt test unversioned-4 {[normalize_result] eq {}} ############################################################################### fossil unversioned add unversioned1.txt test unversioned-5 {[normalize_result] eq {}} ############################################################################### fossil unversioned cat unversioned1.txt test unversioned-6 {[normalize_result] eq {This is unversioned file #1.}} ############################################################################### fossil unversioned list test unversioned-7 {[regexp \ {^[0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 28 28\ unversioned1\.txt$} [normalize_result]]} ############################################################################### fossil unversioned ls test unversioned-8 {[normalize_result] eq {unversioned1.txt}} ############################################################################### fossil unversioned remove unversioned1.txt test unversioned-9 {[normalize_result] eq {}} ############################################################################### fossil unversioned list test unversioned-10 {[normalize_result] eq {}} ############################################################################### fossil unversioned ls test unversioned-11 {[normalize_result] eq {}} ############################################################################### fossil unversioned list --all test unversioned-12 {[regexp \ {^\(deleted\) \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 0 0\ unversioned1\.txt$} [normalize_result]]} ############################################################################### fossil unversioned ls --all test unversioned-13 {[normalize_result] eq {unversioned1.txt}} ############################################################################### fossil unversioned add "unversioned space.txt" -expectError test unversioned-14 {[normalize_result] eq \ {unversioned filenames may not contain whitespace: 'unversioned space.txt'}} ############################################################################### fossil unversioned add "unversioned space.txt" --as unversioned3.txt test unversioned-15 {[normalize_result] eq {}} ############################################################################### fossil unversioned list test unversioned-16 {[regexp \ {^[0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 30 30\ unversioned3\.txt$} [normalize_result]]} ############################################################################### fossil unversioned ls --l test unversioned-17 {[regexp \ {^[0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 30 30\ unversioned3\.txt$} [normalize_result]]} ############################################################################### fossil unversioned ls test unversioned-18 {[normalize_result] eq {unversioned3.txt}} ############################################################################### fossil unversioned add unversioned2.txt --mtime 2016-10-01 test unversioned-19 {[normalize_result] eq {}} ############################################################################### fossil unversioned list test unversioned-20 {[regexp \ {^[0-9a-f]{12} 2016-10-01 00:00:00 30 30\ unversioned2\.txt [0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 30 30\ unversioned3\.txt$} [normalize_result]]} ############################################################################### fossil unversioned ls test unversioned-21 {[normalize_result] eq {unversioned2.txt unversioned3.txt}} ############################################################################### fossil unversioned cat unversioned1.txt test unversioned-22 {[normalize_result] eq {}} ############################################################################### fossil unversioned cat unversioned2.txt test unversioned-23 {[::sha1::sha1 $RESULT] eq \ {962f96ebd613e4fdd9aa2d20bd9fe21a64e925f2}} ############################################################################### fossil unversioned cat unversioned3.txt -keepNewline test unversioned-24 {[::sha1::sha1 $RESULT] eq \ {c6b95509120d9703cc4fbe5cdfcb435b5912b3e4}} ############################################################################### fossil unversioned rm unversioned3.txt test unversioned-25 {[normalize_result] eq {}} ############################################################################### fossil unversioned add unversioned4.txt test unversioned-26 {[normalize_result] eq {}} ############################################################################### fossil unversioned cat unversioned4.txt set hash(before) [::sha1::sha1 $RESULT] test unversioned-27 {$hash(before) eq \ {b48ba8e2d0b498321dfd13de84867effda399af5}} ############################################################################### fossil unversioned edit unversioned4.txt test unversioned-28 {[normalize_result] eq {}} ############################################################################### fossil unversioned cat unversioned4.txt set hash(after) [::sha1::sha1 $RESULT] test unversioned-29 {$hash(after) ne $hash(before)} test unversioned-30 {[regexp { \d+ (?:-)?\d+$} $RESULT]} ############################################################################### fossil unversioned edit unversioned4.txt --mtime 2016-10-01 test unversioned-31 {[normalize_result] eq {}} ############################################################################### fossil unversioned cat unversioned4.txt test unversioned-32 {[regexp { \d+ (?:-)?\d+ \d+ (?:-)?\d+$} $RESULT]} ############################################################################### fossil unversioned list test unversioned-33 {[regexp \ {^[0-9a-f]{12} 2016-10-01 00:00:00 30 30\ unversioned2\.txt [0-9a-f]{12} 2016-10-01 00:00:00 \d+ \d+\ unversioned4\.txt$} [normalize_result]]} ############################################################################### fossil unversioned export unversioned2.txt unversioned2-ex.txt test unversioned-34 {[normalize_result] eq {}} test unversioned-35 {[::sha1::sha1 -hex -filename unversioned2-ex.txt] eq \ {962f96ebd613e4fdd9aa2d20bd9fe21a64e925f2}} ############################################################################### fossil unversioned hash test unversioned-36 {[regexp {^[0-9a-f]{40,64}$} [normalize_result]]} ############################################################################### fossil unversioned hash --debug test unversioned-37 {[regexp \ {^unversioned2\.txt 2016-10-01 00:00:00 [0-9a-f]{40,64} unversioned4\.txt 2016-10-01 00:00:00 [0-9a-f]{40,64} [0-9a-f]{40,64}$} [normalize_result]]} ############################################################################### fossil unversioned remove unversioned4.txt --mtime "2016-10-02 13:47:29" test unversioned-38 {[normalize_result] eq {}} ############################################################################### fossil unversioned list --all test unversioned-39 {[regexp \ {^\(deleted\) \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 0 0\ unversioned1\.txt [0-9a-f]{12} 2016-10-01 00:00:00 30 30 unversioned2\.txt \(deleted\) \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 0 0\ unversioned3\.txt \(deleted\) 2016-10-02 13:47:29 0 0 unversioned4\.txt$} \ [normalize_result]]} ############################################################################### fossil unversioned touch unversioned1.txt --mtime "2016-10-03 23:01:44" test unversioned-40 {[normalize_result] eq {}} ############################################################################### fossil unversioned list --all test unversioned-41 {[regexp \ {^\(deleted\) 2016-10-03 23:01:44 0 0\ unversioned1\.txt [0-9a-f]{12} 2016-10-01 00:00:00 30 30 unversioned2\.txt \(deleted\) \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 0 0\ unversioned3\.txt \(deleted\) 2016-10-02 13:47:29 0 0 unversioned4\.txt$} \ [normalize_result]]} ############################################################################### fossil unversioned add unversioned5.txt test unversioned-42 {[normalize_result] eq {}} ############################################################################### fossil unversioned touch unversioned5.txt test unversioned-43 {[normalize_result] eq {}} ############################################################################### fossil unversioned list test unversioned-44 {[regexp \ {^[0-9a-f]{12} 2016-10-01 00:00:00 30 30 unversioned2\.txt [0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 28 28\ unversioned5\.txt$} [normalize_result]]} ############################################################################### set password [string trim [clock seconds] -] fossil user new uvtester "Unversioned Test User" $password fossil user capabilities uvtester oy ############################################################################### foreach {pid port outTmpFile} [test_start_server $repository stopArg] {} if {! $::QUIET} { puts [appendArgs "Started Fossil server, pid \"" $pid \" ", port \"" $port \".] } set remote [appendArgs http://uvtester: $password @localhost: $port /] ############################################################################### set clientDir [file join $tempPath [appendArgs \ uvtest_ [string trim [clock seconds] -] _ [getSeqNo]]] set savedPwd [pwd] file mkdir $clientDir; cd $clientDir if {! $::QUIET} { puts [appendArgs "Now in client directory \"" [pwd] \".] } write_file unversioned-client1.txt "This is unversioned client file #1." ############################################################################### fossil clone --save-http-password $remote uvrepo.fossil fossil open -f uvrepo.fossil ############################################################################### fossil unversioned list test unversioned-45 {[normalize_result] eq {}} ############################################################################### fossil_maybe_answer y unversioned sync $remote test unversioned-46 {[regexp \ {Round-trips: 1 Artifacts sent: 0 received: 0 Round-trips: 1 Artifacts sent: 0 received: 0 Round-trips: 2 Artifacts sent: 0 received: 0 Round-trips: 2 Artifacts sent: 0 received: 2 \n? done, wire bytes sent: \d+ received: \d+ remote: (?:127\.0\.0\.1|::1)} \ [normalize_result]]} ############################################################################### fossil unversioned ls test unversioned-47 {[normalize_result] eq {unversioned2.txt unversioned5.txt}} ############################################################################### set env(FAKE_EDITOR_SCRIPT) "append data this_is_a_test"; # deterministic fossil unversioned edit unversioned2.txt test unversioned-48 {[normalize_result] eq {}} unset env(FAKE_EDITOR_SCRIPT) ############################################################################### fossil unversioned cat unversioned2.txt test unversioned-49 {[::sha1::sha1 $RESULT] eq \ {e15d4b576fc04e3bb5e44a33d44d104dd5b19428}} ############################################################################### fossil unversioned remove unversioned5.txt test unversioned-50 {[normalize_result] eq {}} ############################################################################### fossil unversioned list --all test unversioned-51 {[regexp \ {^[0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 44 44\ unversioned2\.txt \(deleted\) \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 0 0\ unversioned5\.txt$} [normalize_result]]} ############################################################################### fossil_maybe_answer y unversioned revert $remote test unversioned-52 {[regexp \ {Round-trips: 1 Artifacts sent: 0 received: 0 Round-trips: 1 Artifacts sent: 0 received: 0 Round-trips: 2 Artifacts sent: 0 received: 0 Round-trips: 2 Artifacts sent: 0 received: 2 \n? done, wire bytes sent: \d+ received: \d+ remote: (?:127\.0\.0\.1|::1)} \ [normalize_result]]} ############################################################################### fossil unversioned list test unversioned-53 {[regexp \ {^[0-9a-f]{12} 2016-10-01 00:00:00 30 30\ unversioned2\.txt [0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 28 28\ unversioned5\.txt$} [normalize_result]]} ############################################################################### fossil unversioned add unversioned-client1.txt test unversioned-54 {[normalize_result] eq {}} ############################################################################### fossil_maybe_answer y unversioned sync $remote test unversioned-55 {[regexp \ {Round-trips: 1 Artifacts sent: 0 received: 0 Round-trips: 1 Artifacts sent: 0 received: 0 Round-trips: 2 Artifacts sent: 1 received: 0 Round-trips: 2 Artifacts sent: 1 received: 0 \n? done, wire bytes sent: \d+ received: \d+ remote: (?:127\.0\.0\.1|::1)} \ [normalize_result]]} ############################################################################### fossil close test unversioned-56 {[normalize_result] eq {}} ############################################################################### cd $savedPwd; unset savedPwd file delete -force $clientDir if {! $::QUIET} { puts [appendArgs "Now in server directory \"" [pwd] \".] } ############################################################################### set stopped [test_stop_server $stopArg $pid $outTmpFile] if {! $::QUIET} { puts [appendArgs \ [expr {$stopped ? "Stopped" : "Could not stop"}] \ " Fossil server, pid \"" $pid "\", using argument \"" \ $stopArg \".] } ############################################################################### fossil unversioned list test unversioned-57 {[regexp \ {^[0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 35 35\ unversioned-client1\.txt [0-9a-f]{12} 2016-10-01 00:00:00 30 30 unversioned2\.txt [0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 28 28\ unversioned5\.txt$} [normalize_result]]} ############################################################################### fossil unversioned cat unversioned-client1.txt test unversioned-58 {[::sha1::sha1 $RESULT] eq \ {a34606f714afe309bb531fba6051eaf25201e8a2}} ############################################################################### test_cleanup |
Added test/update-test-1.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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #!/bin/sh # # Run this script in an empty directory. A single argument is the full # pathname of the fossil binary. Example: # # sh update-test-1.sh /home/drh/fossil/m1/fossil # export FOSSIL=$1 rm -rf aaa bbb update-test-1.fossil # Create a test repository $FOSSIL new update-test-1.fossil # In checkout aaa, add file one.txt mkdir aaa cd aaa $FOSSIL open ../update-test-1.fossil echo one >one.txt $FOSSIL add one.txt $FOSSIL commit -m add-one --tag add-one # Open checkout bbb. mkdir ../bbb cd ../bbb $FOSSIL open ../update-test-1.fossil # Back in aaa, add file two.txt cd ../aaa echo two >two.txt $FOSSIL add two.txt $FOSSIL commit -m add-two --tag add-two # In bbb, delete file one.txt. Then update the change from aaa that # adds file two. Verify that one.txt says deleted. cd ../bbb $FOSSIL rm one.txt $FOSSIL changes echo '========================================================================' $FOSSIL update echo '======== The previous should show "ADD two.txt" ========================' $FOSSIL changes echo '======== The previous should show "DELETE one.txt" =====================' $FOSSIL commit --test -m check-in echo '======== Only file two.txt is checked in ===============================' |
Added test/update-test-2.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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #!/bin/sh # # Run this script in an empty directory. A single argument is the full # pathname of the fossil binary. Example: # # sh update-test-2.sh /home/drh/fossil/m1/fossil # export FOSSIL=$1 rm -rf aaa bbb update-test-2.fossil # Create a test repository $FOSSIL new update-test-2.fossil # In checkout aaa, add file one.txt. mkdir aaa cd aaa $FOSSIL open ../update-test-2.fossil echo one >one.txt $FOSSIL add one.txt $FOSSIL commit -m add-one --tag add-one # Create checkout bbb. mkdir ../bbb cd ../bbb $FOSSIL open ../update-test-2.fossil # Back in aaa, make changes to one.txt. Add file two.txt. cd ../aaa echo change >>one.txt echo two >two.txt $FOSSIL add two.txt $FOSSIL commit -m 'chng one and add two' --tag add-two # In bbb, remove one.txt, then update. cd ../bbb $FOSSIL rm one.txt $FOSSIL changes echo '========================================================================' $FOSSIL update echo '======== Previous should show "ADD two.txt" and conflict on one.txt ====' $FOSSIL changes echo '======== The previous should show "DELETE one.txt" =====================' $FOSSIL commit --test -m 'check-in' echo '======== Only file two.txt is checked in ===============================' |
Added test/update.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 | # # Copyright (c) 2024 Preben Guldnerg <preben@guldberg.org> # # 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 for the "update" command. # # Track number of tests we have set up in test_update_setup. This helps ensure # that generated files are ordered in `fossil update --verbose` mode. set UPDATE_TEST 0 proc test_update_setup {desc} { global UPDATE_TEST incr UPDATE_TEST fossil revert fossil update return [format "test-%02u-%s.txt" $UPDATE_TEST $desc] } # The output is in file name order, so massage $RESULT to remove initial UNCHANGED # files. Only do this if we have the expected branch information. proc test_update {testname message changes {fossil_args ""}} { fossil update --verbose {*}$fossil_args if { [regsub {\n-{79}\nupdated-from: [0-9a-z]{40} .*} $::RESULT {} test_result ] } { regsub {^(?:UNCHANGED [-a-z0-9.]+\n)*} $test_result {} test_result } else { set test_result $::RESULT } test "update-message-$testname" {$message == $test_result} fossil changes test "update-changes-$testname" {$changes == $::RESULT} } # Use a sequence number for file content that is not important for the test. set UPDATE_SEQ_NO 0 proc write_seq_to_file {fname} { global UPDATE_SEQ_NO incr UPDATE_SEQ_NO write_file $fname "$UPDATE_SEQ_NO\n" } # Make sure we are not in an open repository and initialize new repository test_setup ############################################################################### fossil update --verbose test update-already-up-to-date { [regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date$} $RESULT] } # Remaining tests are carried out in the order update_cmd() performs checks. # # Common approach for tests below: # 1. Set the testname # 2. Set the file name, done by calling update_setup # 3. Set message and changes, the expected message message and subsequent changes # 3. Optionally set up and commit a common base for the next steps # 4. Commit a change to the repository (new tip) # 5. Update to the previous version # 6. Make changes # 7. Call test_update to attempt and update to tip set testname "conflict-standard" set fname [test_update_setup $testname] set message "CONFLICT $fname" set changes "EDITED $fname" write_seq_to_file $fname fossil add $fname fossil commit -m "Add $fname" fossil up previous write_seq_to_file $fname fossil add $fname test_update $testname $message $changes -expectError set testname "add-overwrites" set fname [test_update_setup $testname] set message "ADD $fname - overwrites an unmanaged file, original copy backed up locally" set changes "" write_seq_to_file $fname fossil add $fname fossil commit -m "Add $fname" fossil up previous write_seq_to_file $fname test_update $testname $message $changes -expectError set testname "add-standard" set fname [test_update_setup $testname] set message "ADD $fname" set changes "" write_seq_to_file $fname fossil add $fname fossil commit -m "Add $fname" fossil up previous test_update $testname $message $changes set testname "update-change" set fname [test_update_setup $testname] set message "UPDATE $fname - change to unmanaged file" set changes "DELETED $fname" write_seq_to_file $fname fossil add $fname fossil commit -m "Add $fname" write_seq_to_file $fname fossil commit -m "Update $fname" fossil up previous fossil rm --hard $fname test_update $testname $message $changes set testname "update-standard" set fname [test_update_setup $testname] set message "UPDATE $fname" set changes "" write_seq_to_file $fname fossil add $fname fossil commit -m "Add $fname" write_seq_to_file $fname fossil commit -m "Update $testname" fossil up previous test_update $testname $message $changes set testname "update-missing" set fname [test_update_setup $testname] set message "UPDATE $fname" set changes "" write_seq_to_file $fname fossil add $fname fossil commit -m "Add $fname" write_seq_to_file $fname fossil commit -m "Update $fname" fossil up previous file delete $fname test_update $testname $message $changes set testname "conflict-deleted" set fname [test_update_setup $testname] set message "CONFLICT $fname - edited locally but deleted by update" set changes "" write_seq_to_file $fname fossil add $fname fossil commit -m "Add $fname" fossil rm --hard $fname fossil commit -m "Remove $fname" fossil up previous file delete $fname test_update $testname $message $changes -expectError set testname "remove" set fname [test_update_setup $testname] set message "REMOVE $fname" set changes "" write_seq_to_file $fname fossil add $fname fossil commit -m "Add $fname" fossil rm --hard $fname fossil commit -m "Remove $fname" fossil up previous test_update $testname $message $changes set testname "merge-renamed" set fname [test_update_setup $testname] set message "MERGE $fname -> $fname.renamed" set changes "EDITED $fname.renamed" write_file $fname "center\n" fossil add $fname fossil commit -m "Add $fname" write_file $fname "top\ncenter\n" fossil mv --hard $fname "$fname.renamed" fossil commit -m "Update and rename $fname" fossil up previous write_file $fname "center\nbelow\n" test_update $testname $message $changes set testname "merge-standard" set fname [test_update_setup $testname] set message "MERGE $fname" set changes "EDITED $fname" write_file $fname "center\n" fossil add $fname fossil commit -m "Add $fname" write_file $fname "top\ncenter\n" fossil commit -m "Update $fname" fossil up previous write_file $fname "center\nbelow\n" test_update $testname $message $changes # TODO: test for "Cannot merge symlink" would be platform dependent set testname "merge-conflict" set fname [test_update_setup $testname] set message "MERGE $fname\n***** 1 merge conflicts in $fname" set changes "CONFLICT $fname" write_seq_to_file $fname fossil add $fname fossil commit -m "Add $fname" write_seq_to_file $fname fossil commit -m "Update $fname" fossil up previous write_seq_to_file $fname test_update $testname $message $changes -expectError # TODO: test for "Cannot merge binary file"? set testname "edited" set fname [test_update_setup $testname] set message "EDITED $fname\nADD $fname.other" set changes "EDITED $fname" write_seq_to_file $fname fossil add $fname fossil commit -m "Add $fname" write_seq_to_file "$fname.other" fossil add $fname.other fossil commit -m "Add $fname.other" fossil up previous write_seq_to_file $fname test_update $testname $message $changes set testname "unchanged" set fname [test_update_setup $testname] set message "ADD $fname\nUNCHANGED $fname.unchanged" set changes "" write_seq_to_file "$fname.unchanged" fossil add "$fname.unchanged" fossil commit -m "Add $fname.unchanged" write_seq_to_file "$fname" fossil add "$fname" fossil commit -m "Add $fname" fossil up previous test_update $testname $message $changes ############################################################################### test_cleanup |
Added 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. |
Added test/valgrind-www.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 | #!/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 /reportlist /setup /dir /wcontent } { set seen($url) 1 set pending($url) 1 } set limit 1000 set npending [llength [array names pending]] proc get_pending {} { global pending npending set res [lindex [array names pending] [expr {int(rand()*$npending)}]] unset pending($res) incr npending -1 return $res } for {set i 0} {$npending>0 && $i<$limit} {incr i} { set url [get_pending] puts "====== ([expr {$i+1}]) $url ======" set x [run_query $url] while {[regexp {<[aA] .*?href="(/[a-z].*?)".*?>(.*)$} $x all url tail]} { set u2 [string map {< < > > " \" & &} $url] if {![info exists seen($u2)]} { set pending($u2) 1 set seen($u2) 1 incr npending } set x $tail } } |
Added test/wiki.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 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 | # # Copyright (c) 2016 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 wiki and attachment command Support # test_setup # Disable backoffice for this test, otherwise its process lingers for some # time after the test has completed. # Perhaps, this should be done in test_setup and enabled explicitly only # when needed. fossil set backoffice-disable 1 # Return true if two files are similar (i.e. not only compress trailing spaces # from a line, but remove any final LF from the file as well) proc similar_file {a b} { set x "" if {[file exists $a]} { set x [read_file $a] regsub -all { +\n} $x \n x regsub -all {\n$} $x {} x } set y "" if {[file exists $b]} { set y [read_file $b] regsub -all { +\n} $y \n y regsub -all {\n$} $y {} y } return [expr {$x==$y}] } # Return the mime type in the manifest for a given wiki page # Defaults to "error: some text" if the manifest can't be located and # "text/x-fossil-wiki" (the default mimetype for rendering) # if the N card is omitted in the manifest. # Note: Makes fossil calls, so $CODE and $RESULT will be corrupted proc get_mime_type {name} { global CODE RESULT fossil http << "GET /wiki?name=$name" if {$CODE != 0} { return "error: /wiki?name=$name $CODE $RESULT" } fossil whatis --type w $name if {$CODE != 0} { return "error: fossil whatis --type w $name $CODE $RESULT" } set CODE [regexp -line {^artifact:\s*([0-9a-f]+)$} $RESULT match info] if {$CODE == 0} { return "error: whatis returned no info for wiki page $name" } fossil artifact $info if {$CODE != 0} { return "error: fossil artifact $info $CODE $RESULT" } set CODE [regexp -line {^N (.*)$} $RESULT match mimetype] if {$CODE == 0} { return "text/x-fossil-wiki" } return $mimetype } ############################################################################### # Initially there should be no wiki entries fossil wiki list test wiki-0 {[normalize_result] eq {}} ############################################################################### # Adding an entry should add it to the wiki list write_file f1 "first wiki note" fossil wiki create tcltest f1 test wiki-1 {$CODE == 0} fossil wiki list test wiki-2 {[normalize_result] eq {tcltest}} ############################################################################### # Trying to add the same entry should fail fossil wiki create tcltest f1 -expectError test wiki-3 {$CODE != 0} ############################################################################### # exporting the wiki page should give back similar text fossil wiki export tcltest a1 test wiki-4 {[similar_file f1 a1]} ############################################################################### # commiting a change to an existing page should replace the page on export write_file f2 "second version of the page" fossil wiki commit tcltest f2 test wiki-5 {$CODE == 0} fossil wiki export tcltest a2 test wiki-6 {[similar_file f2 a2]} ############################################################################### # But we shouldn't be able to update non-existant pages fossil wiki commit doesntexist f1 -expectError test wiki-7 {$CODE != 0} ############################################################################### # There shouldn't be any tech notes at this point fossil wiki list --technote test wiki-8 {[normalize_result] eq {}} ############################################################################### # Creating a tech note with a specified timestamp should add a technote write_file f3 "A technote" fossil wiki create technote f3 --technote {2016-01-01 12:34} test wiki-9 {$CODE == 0} fossil wiki list --technote test wiki-10 {[normalize_result] eq {2016-01-01 12:34:00}} fossil wiki list --technote --show-technote-ids set technotelist [split $RESULT "\n"] set veryfirsttechnoteid [lindex [split [lindex $technotelist 0]] 0] ############################################################################### # exporting that technote should give back similar text fossil wiki export a3 --technote {2016-01-01 12:34:00} test wiki-11 {[similar_file f3 a3]} ############################################################################### # Trying to add a technote with the same timestamp should succeed and create a # second tech note fossil wiki create 2ndnote f3 -technote {2016-01-01 12:34} test wiki-13 {$CODE == 0} fossil wiki list --technote set technotelist [split $RESULT "\n"] test wiki-13.1 {[llength $technotelist] == 2} ############################################################################### # commiting a change to an existing technote should replace the page on export # (this should update the tech note from wiki-13 as that the most recently # updated one, that should also be the one exported by the export command) write_file f4 "technote 2nd variant" fossil wiki commit technote f4 --technote {2016-01-01 12:34} test wiki-14 {$CODE == 0} fossil wiki export a4 --technote {2016-01-01 12:34} test wiki-15 {[similar_file f4 a4]} # Also check that the tech note with the same timestamp, but modified less # recently still has its original text fossil wiki export a4.1 --technote $veryfirsttechnoteid test wiki-15.1 {[similar_file f3 a4.1]} ############################################################################### # But we shouldn't be able to update non-existant pages fossil wiki commit doesntexist f1 -expectError test wiki-16 {$CODE != 0} ############################################################################### # Check specifying tags for a technote is OK write_file f5 "technote with tags" fossil wiki create {tagged technote} f5 --technote {2016-01-02 12:34} --technote-tags {A B} test wiki-17 {$CODE == 0} write_file f5.1 "editted and tagged technote" fossil wiki commit {tagged technote} f5 --technote {2016-01-02 12:34} --technote-tags {C D} test wiki-18 {$CODE == 0} ############################################################################### # Check specifying a bgcolor for a technote is OK write_file f6 "bgcolored technote" fossil wiki create bgcolor f6 --technote {2016-01-03 12:34} --technote-bgcolor red test wiki-19 {$CODE == 0} write_file f6.1 "editted technote with a background color" fossil wiki commit bgcolor f6.1 --technote {2016-01-03 12:34} --technote-bgcolor yellow test wiki-20 {$CODE == 0} ############################################################################### # Test adding an attachment to both a non-existant (should fail) and existing wiki page write_file fa "This is a file to be attached" fossil attachment add doesntexist fa -expectError test wiki-21 {$CODE != 0} fossil attachment add tcltest fa test wiki-22 {$CODE == 0} ############################################################################### # Test adding an attachment to both a non-existant (should fail) and existing tech note fossil attachment add fa --technote {2016-07-22 12:00} -expectError test wiki-23 {$CODE != 0} fossil attachment add fa --technote {2016-01-03 12:34} test wiki-24 {$CODE == 0} ############################################################################### # Check that a wiki page with an attachment can be updated fossil wiki commit tcltest f1 test wiki-25 {$CODE == 0} ############################################################################### # Check that a technote with an attachment can be updated fossil wiki commit technote f6 --technote {2016-01-03 12:34} test wiki-26 {$CODE == 0} fossil wiki commit technote f6 --technote {2016-01-03 12:34} --technote-tags {E F} test wiki-27 {$CODE == 0} fossil wiki commit technote f6 --technote {2016-01-03 12:34} --technote-bgcolor blue test wiki-28 {$CODE == 0} ############################################################################### # Check longest form of timestamp for the technote write_file f7 "Different timestamps" fossil wiki create technotenow f7 --technote {2016-01-04 12:34:56+00:00} test wiki-29 {$CODE == 0} ############################################################################### # Check a technote appears on the timeline write_file f8 "Contents of a 'unique' tech note" fossil wiki create {Unique technote} f8 --technote {2016-01-05 01:02:03} fossil timeline test wiki-30 {[string match *Unique*technote* $RESULT]} ############################################################################### # Check for a collision between an attachment and a note, this was a # bug that resulted from some code treating the attachment entry as if it # were a technote when it isn't really. # # First, wait for the top of the next second so the attachment # happens at a known time, then add an attachment to an existing note # and a new note immediately after. set t0 [clock seconds] while {$t0 == [clock seconds]} { after 100 } set t1 [clock format [clock seconds] -gmt 1 -format "%Y-%m-%d %H:%M:%S"] write_file f9 "Timestamp: $t1" fossil attachment add f9 --technote {2016-01-05 01:02:03} test wiki-31 {$CODE == 0} fossil wiki create {Attachment collision} f9 --technote now test wiki-32 {$CODE == 0} # # Now waste time until the next second so that the remaining tests # don't have to worry about a potential collision set t0 [clock seconds] while {$t0 == [clock seconds]} { after 100 } ############################################################################### # Check a technote with no timestamp cannot be created, but that # "now" is a valid stamp. set t2 [clock format [clock seconds] -gmt 1 -format "%Y-%m-%d %H:%M:%S"] write_file f10 "Even unstampted notes are delivered.\nStamped $t2" fossil wiki create "Unstamped Note" f10 --technote -expectError test wiki-33 {$CODE != 0} fossil wiki create "Unstamped Note" f10 --technote now test wiki-34 {$CODE == 0} fossil wiki list -t test wiki-35 {[string match "*$t2*" $RESULT]} ############################################################################### # Check an attachment to it in the same second works. write_file f11 "Time Stamp was $t2" fossil attachment add f11 --technote $t2 test wiki-36 {$CODE == 0} fossil timeline test wiki-36-1 {$CODE == 0} fossil wiki list -t test wiki-36-2 {$CODE == 0} ############################################################################### # Check that we have the expected number of tech notes on the list (and not # extra ones from other events (such as the attachments) - 8 tech notes # expected created by tests 9, 13, 17, 19, 29, 31, 32 and 34 fossil wiki list --technote set technotelist [split $RESULT "\n"] test wiki-37 {[llength $technotelist] == 8} ############################################################################### # Check that using the show-technote-ids shows the same tech notes in the same # order (with the technote id as the first word of the line) fossil wiki list --technote --show-technote-ids set technoteidlist [split $RESULT "\n"] test wiki-38 {[llength $technotelist] == 8} for {set i 0} {$i < [llength $technotelist]} {incr i} { set match "???????????????????????????????????????? " append match [lindex $technotelist $i] test "wiki-39-$i" {[string match $match [lindex $technoteidlist $i]]} } ############################################################################### # Create new tech note with a old timestamp so that it is oldest and then check that # the contents of the oldest tech note (by tech note id, both full and short) match up write_file f12 "A really old tech note" fossil wiki create {Old tech note} f12 --technote {2001-07-07 09:08:07} fossil wiki list --technote --show-technote-ids set technotelist [split $RESULT "\n"] set anoldtechnoteid [lindex [split [lindex $technotelist [llength $technotelist]-1]] 0] fossil wiki export a12 --technote $anoldtechnoteid test wiki-40 {[similar_file f12 a12]} ############################################################################### # Also check that we can specify a prefix of the tech note id (note: with # 9 items in the tech note at this point there is a chance of a collision. # However with a 20 character prefix the chance of the collision is # approximately 1 in 10^22 so this test ignores that possibility.) fossil wiki export a12.1 --technote [string range $anoldtechnoteid 0 20] test wiki-41 {[similar_file f12 a12.1]} ############################################################################### # Now we need to force a collision in the first four characters of the tech # note id if we don't already have one so we can check we get an error if the # tech note id is ambiguous set idcounts [dict create] set maxcount 0 fossil wiki list --technote --show-technote-ids set technotelist [split $RESULT "\n"] for {set i 0} {$i < [llength $technotelist]} {incr i} { set fullid [lindex $technotelist $i] set id [string range $fullid 0 3] dict incr idcounts $id if {[dict get $idcounts $id] > $maxcount} { set maxid $id incr maxcount } } # get i so that, as a julian date, it is in the 1800s, i.e., older than # any other tech note, but after 1 AD set i 2400000 while {$maxcount < 2} { # keep getting older incr i -1 write_file f13 "A tech note with timestamp of jday=$i" fossil wiki create "timestamp of $i" f13 --technote "$i" fossil wiki list --technote --show-technote-ids set technotelist [split $RESULT "\n"] set oldesttechnoteid [lindex [split [lindex $technotelist [llength $technotelist]-1]] 0] set id [string range $oldesttechnoteid 0 3] dict incr idcounts $id if {[dict get $idcounts $id] > $maxcount} { set maxid $id incr maxcount } } # Save the duplicate id for this and later tests set duplicateid $maxid fossil wiki export a13 --technote $duplicateid -expectError test wiki-42 {$CODE != 0} ############################################################################### # Check we can update technote by its id write_file f14 "Updated text for the really old tech note" fossil wiki commit {Old tech note} f14 --technote $anoldtechnoteid fossil wiki export a14 --technote $anoldtechnoteid test wiki-43 {[similar_file f14 a14]} ############################################################################### # Check we can add attachments to a technote by its id fossil attachment add fa --technote $anoldtechnoteid test wiki-44 {$CODE == 0} ############################################################################### # Also check that we can specify a prefix of the tech note id write_file f15 "Updated text for the really old tech note specified by its id" fossil wiki commit {Old tech note} f15 --technote [string range $anoldtechnoteid 0 20] fossil wiki export a15 --technote $anoldtechnoteid test wiki-45 {[similar_file f15 a15]} ############################################################################### # Check we can add attachments to a technote by a prefix of its id fossil attachment add fa --technote [string range $anoldtechnoteid 0 20] test wiki-46 {$CODE == 0} ############################################################################### # And we get an error for the ambiguous tech note id fossil wiki commit {Old tech note} f15 --technote $duplicateid -expectError test wiki-47 {$CODE != 0} fossil attachment add fa --technote $duplicateid -expectError test wiki-48 {$CODE != 0} ############################################################################### # Check the default mimetype is text/x-fossil-wiki test wiki-49 {[get_mime_type tcltest] == "text/x-fossil-wiki"} ############################################################################### # Check long form of the mimetypes are recorded correctly fossil wiki create tcltest-x-fossil f1 -mimetype text/x-fossil-wiki test wiki-50 {[get_mime_type tcltest-x-fossil] == "text/x-fossil-wiki"} fossil wiki create tcltest-x-markdown f1 -mimetype text/x-markdown test wiki-51 {[get_mime_type tcltest-x-markdown] == "text/x-markdown"} fossil wiki create tcltest-plain f1 -mimetype text/plain test wiki-52 {[get_mime_type tcltest-plain] == "text/plain"} fossil wiki create tcltest-x-random f1 -mimetype text/x-random test wiki-53 {[get_mime_type tcltest-x-random] == "text/x-fossil-wiki"} ############################################################################### # Check short form of the mimetypes are recorded correctly fossil wiki create tcltest-x-fossil-short f1 -mimetype wiki test wiki-54 {[get_mime_type tcltest-x-fossil-short] == "text/x-fossil-wiki"} fossil wiki create tcltest-x-markdown-short f1 -mimetype markdown test wiki-55 {[get_mime_type tcltest-x-markdown-short] == "text/x-markdown"} fossil wiki create tcltest-plain-short f1 -mimetype plain test wiki-56 {[get_mime_type tcltest-plain-short] == "text/plain"} fossil wiki create tcltest-x-random-short f1 -mimetype random test wiki-57 {[get_mime_type tcltest-x-random-short] == "text/x-fossil-wiki"} ############################################################################### test_cleanup |
Added tools/co-rsync.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 | #!/bin/sh # # This is a TCL script that tries to sync the changes in a local # Fossil checkout to another machine. The changes are gathered into # a tarball, then sent via ssh to the remote and unpacked. # # Usage: # # co-rsync.tcl REMOTE # # Where REMOTE is the root of the remote repository into which changes # are to be moved. # # Use Case: # # Sometimes while in the middle of an edit it is useful to transfer # the incomplete changes to another machine for testing. This could # be accomplished using scp, but doing it that was is tedious if many # files in multiple directories have changed. This command does all # the necessary transfer using a single command. # # A Tcl comment, whose contents don't matter \ exec tclsh "$0" "$@" # Begin by changing directories to the root of the check-out. # set remote {} set dryrun 0 proc usage {} { puts stderr "Usage: $::argv0 REMOTE" puts stderr "Options:" puts stderr " --dryrun No-op but print what would have happened" exit 1 } foreach arg $argv { if {$arg=="--dryrun" || $arg=="--dry-run"} { set dryrun 1 continue } if {$remote!=""} { usage } set remote $arg } if {$remote==""} usage set in [open {|fossil status} rb] set status [read $in] if {[catch {close $in} msg]} { puts stderr $msg exit 1 } set root {} regexp {local-root: +([^\n]+)} $status all root if {$root==""} { puts stderr "not in a fossil check-out" exit 1 } cd $root set tmpname filelist- for {set i 0} {$i<3} {incr i} { append tmpname [format %08x [expr {int(rand()*0xffffffff)}]] } set out [open $tmpname wb] puts $out [exec fossil changes --no-classify --no-merge] close $out set cmd "rsync -v --files-from=$tmpname . $remote" puts $cmd if {!$dryrun} { exec {*}$cmd } file delete $tmpname |
Added tools/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 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 | /* ** 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 or HTML or a URL or JSON, ** detect and warn about possible injection attacks. */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <assert.h> /* ** Debugging switch */ static int eVerbose = 0; /* ** 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; } if( z[0]=='\\' && (z[1]=='\n' || (z[1]=='\r' && z[2]=='\n')) ){ *pType = TK_SPACE; return 1; } *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; } /* ** Remove excess whitespace and nested "()" from string z. */ static char *simplify_expr(char *z){ int n = (int)strlen(z); while( n>0 ){ if( isspace(z[0]) ){ z++; n--; continue; } if( z[0]=='(' && z[n-1]==')' ){ z++; n -= 2; continue; } break; } z[n] = 0; 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); if( strcmp(z, "NULL")==0 ) return 1; 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", "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_sql_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; } /* ** Return true if the input is an argument that is never safe for use ** with %s. */ static int never_safe(const char *z){ if( strstr(z,"/*safe-for-%s*/")!=0 ) return 0; if( z[0]=='P' ){ if( strncmp(z,"PIF(",4)==0 ) return 0; if( strncmp(z,"PCK(",4)==0 ) return 0; return 1; } if( strncmp(z,"cgi_param",9)==0 ) return 1; return 0; } /* ** Processing flags */ #define FMT_SQL 0x00001 /* Generator for SQL text */ #define FMT_HTML 0x00002 /* Generator for HTML text */ #define FMT_URL 0x00004 /* Generator for URLs */ #define FMT_JSON 0x00008 /* Generator for JSON */ #define FMT_SAFE 0x00010 /* Generator for human-readable text */ #define FMT_LIT 0x00020 /* Just verify that a string literal */ #define FMT_PX 0x00040 /* Must have a literal prefix in format string */ /* ** A list of internal Fossil interfaces that take a printf-style format ** string. */ struct FmtFunc { const char *zFName; /* Name of the function */ int iFmtArg; /* Index of format argument. Leftmost is 1. */ unsigned fmtFlags; /* Processing flags */ } aFmtFunc[] = { { "admin_log", 1, FMT_SAFE }, { "ajax_route_error", 2, FMT_SAFE }, { "audit_append", 3, FMT_SAFE }, { "backofficeTrace", 1, FMT_SAFE }, { "backoffice_log", 1, FMT_SAFE }, { "blob_append_sql", 2, FMT_SQL }, { "blob_appendf", 2, FMT_SAFE }, { "cgi_debug", 1, FMT_SAFE }, { "cgi_panic", 1, FMT_SAFE }, { "cgi_printf", 1, FMT_HTML }, { "cgi_printf_header", 1, FMT_HTML }, { "cgi_redirectf", 1, FMT_URL }, { "chref", 2, FMT_URL }, { "CX", 1, FMT_HTML }, { "db_blob", 2, FMT_SQL }, { "db_debug", 1, FMT_SQL }, { "db_double", 2, FMT_SQL }, { "db_err", 1, FMT_SAFE }, { "db_exists", 1, FMT_SQL }, { "db_get_mprintf", 2, FMT_SAFE }, { "db_int", 2, FMT_SQL }, { "db_int64", 2, FMT_SQL }, { "db_lset", 1, FMT_LIT }, { "db_lset_int", 1, FMT_LIT }, { "db_multi_exec", 1, FMT_SQL }, { "db_optional_sql", 2, FMT_SQL }, { "db_prepare", 2, FMT_SQL }, { "db_prepare_ignore_error", 2, FMT_SQL }, { "db_set", 1, FMT_LIT }, { "db_set_int", 1, FMT_LIT }, { "db_set_mprintf", 3, FMT_PX }, { "db_static_prepare", 2, FMT_SQL }, { "db_text", 2, FMT_SQL }, { "db_unset", 1, FMT_LIT }, { "db_unset_mprintf", 2, FMT_PX }, { "emailerError", 2, FMT_SAFE }, { "entry_attribute", 4, FMT_LIT }, { "fileedit_ajax_error", 2, FMT_SAFE }, { "form_begin", 2, FMT_URL }, { "fossil_error", 2, FMT_SAFE }, { "fossil_errorlog", 1, FMT_SAFE }, { "fossil_fatal", 1, FMT_SAFE }, { "fossil_fatal_recursive", 1, FMT_SAFE }, { "fossil_panic", 1, FMT_SAFE }, { "fossil_print", 1, FMT_SAFE }, { "fossil_trace", 1, FMT_SAFE }, { "fossil_warning", 1, FMT_SAFE }, { "gitmirror_message", 2, FMT_SAFE }, { "href", 1, FMT_URL }, { "json_new_string_f", 1, FMT_SAFE }, { "json_set_err", 2, FMT_SAFE }, { "json_warn", 2, FMT_SAFE }, { "mprintf", 1, FMT_SAFE }, { "multiple_choice_attribute", 3, FMT_LIT }, { "onoff_attribute", 3, FMT_LIT }, { "pop3_print", 2, FMT_SAFE }, { "smtp_send_line", 2, FMT_SAFE }, { "smtp_server_send", 2, FMT_SAFE }, { "socket_set_errmsg", 1, FMT_SAFE }, { "ssl_set_errmsg", 1, FMT_SAFE }, { "style_copy_button", 5, FMT_SAFE }, { "style_header", 1, FMT_HTML }, { "style_set_current_page", 1, FMT_URL }, { "style_submenu_element", 2, FMT_URL }, { "style_submenu_sql", 3, FMT_SQL }, { "textarea_attribute", 5, FMT_LIT }, { "tktsetup_generic", 1, FMT_LIT }, { "webpage_error", 1, FMT_SAFE }, { "webpage_notfound_error", 1, FMT_SAFE }, { "xfersetup_generic", 1, FMT_LIT }, { "xhref", 2, FMT_URL }, }; /* ** Comparison function for two FmtFunc entries */ static int fmtfunc_cmp(const void *pAA, const void *pBB){ const struct FmtFunc *pA = (const struct FmtFunc*)pAA; const struct FmtFunc *pB = (const struct FmtFunc*)pBB; return strcmp(pA->zFName, pB->zFName); } /* ** 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]. ** ** Store the number of initial literal characters of the format string ** in *pInit. */ static int formatArgCount(const char *z, int nType, char *cType, int *pInit){ int nArg = 0; int i, k; int len; int eType; int ln = 0; *pInit = 0; while( z[0] ){ len = token_length(z, &eType, &ln); if( eType==TK_STR ){ for(i=1; i<len-1 && isalpha(z[i]); i++){} *pInit = i-1; 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; int nInit = 0; 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] ){ char cEnd; len = distance_to(z, ','); cEnd = z[len]; z[len] = 0; azArg = safe_realloc((char*)azArg, (sizeof(azArg[0])+1)*(nArg+1)); azArg[nArg++] = simplify_expr(z); if( cEnd==0 ) break; 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( fmtFlags & FMT_LIT ){ if( !is_string_lit(zFmt) ){ printf("%s:%d: argument %d to %.*s() should be a string literal\n", zFilename, lnFCall, fmtArg, szFName, zFCall); nErr++; } }else if( !is_string_lit(zFmt) ){ printf("%s:%d: %.*s() has non-constant format on arg[%d]\n", zFilename, lnFCall, szFName, zFCall, fmtArg-1); nErr++; }else if( (k = formatArgCount(zFmt, nArg, acType, &nInit))>=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_PX)!=0 ){ if( nInit==0 ){ printf("%s:%d: format string on %.*s() should have" " an ASCII character prefix\n", zFilename, lnFCall, szFName, zFCall); nErr++; } }else if( (fmtFlags & FMT_SAFE)==0 ){ for(i=0; i<nArg && i<k; i++){ if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') ){ const char *zExpr = azArg[fmtArg+i]; if( never_safe(zExpr) ){ printf("%s:%d: Argument %d to %.*s() is not safe for" " a query parameter\n", zFilename, lnFCall, i+fmtArg, szFName, zFCall); nErr++; }else if( (fmtFlags & FMT_SQL)!=0 && !is_sql_safe(zExpr) ){ 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]); } }else if( eVerbose>1 ){ printf("%s:%d: %.*s() ok for %d arguments\n", zFilename, lnFCall, szFName, zFCall, nArg); } 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 = 0; int ePrev = 0; int szPrev = 0; int lnPrev = 0; 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. ** ** The eVerbose global variable is incremented with each "-v" argument. */ int main(int argc, char **argv){ int i; int nErr = 0; qsort(aFmtFunc, sizeof(aFmtFunc)/sizeof(aFmtFunc[0]), sizeof(aFmtFunc[0]), fmtfunc_cmp); for(i=1; i<argc; i++){ char *zFile; if( strcmp(argv[i],"-v")==0 ){ eVerbose++; continue; } if( eVerbose>0 ) printf("Processing %s...\n", argv[i]); zFile = read_file(argv[i]); nErr += scan_file(argv[i], zFile); free(zFile); } return nErr; } |
Changes to tools/cvs2fossil/changeset.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # individuals. For exact contribution history, see the revision # history and logs, available at http://fossil-scm.hwaci.com/fossil # # ## ### ##### ######## ############# ##################### ## Helper application, debugging of cvs2fossil. This application ## extracts all information about a changeset and writes it nicely ## formatted to stdout. The changeset is specified by its internal | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # individuals. For exact contribution history, see the revision # history and logs, available at http://fossil-scm.hwaci.com/fossil # # ## ### ##### ######## ############# ##################### ## Helper application, debugging of cvs2fossil. This application ## extracts all information about a changeset and writes it nicely ## formatted to stdout. The changeset is specified by its internal ## numerical id. # # ## ### ##### ######## ############# ##################### ## Requirements, extended package management for local packages. lappend auto_path [file join [file dirname [info script]] lib] package require Tcl 8.4 ; # Required runtime. |
︙ | ︙ |
Changes to tools/cvs2fossil/lib/c2f_pbreakacycle.tcl.
︙ | ︙ | |||
268 269 270 271 272 273 274 | foreach item [array names limits] { set mins $minsa($item) set maxp $maxp($item) # Note that for the min successor position "" represents # +infinity integrity assert { | | | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | foreach item [array names limits] { set mins $minsa($item) set maxp $maxp($item) # Note that for the min successor position "" represents # +infinity integrity assert { ($mins eq "") || ($maxp < $mins) } {Item <$item> is backward at file level ($maxp >= $mins)} } # Save the limits for the splitter, and compute the border at # which to split as the minimum of all minimal successor # positions. |
︙ | ︙ |
Changes to tools/cvs2fossil/lib/c2f_prev.tcl.
︙ | ︙ | |||
1177 1178 1179 1180 1181 1182 1183 | # List of all known changesets of a type. typevariable mytchangesets -array { sym::branch {} sym::tag {} rev {} } | | | 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 | # List of all known changesets of a type. typevariable mytchangesets -array { sym::branch {} sym::tag {} rev {} } typevariable myitemmap -array {} ; # Map from items (tagged) # to the list of changesets # containing it. Each item # can be used by only one # changeset. typevariable myidmap -array {} ; # Map from changeset id to # changeset. |
︙ | ︙ |
Changes to tools/cvs2fossil/lib/mem.tcl.
︙ | ︙ | |||
46 47 48 49 50 51 52 | variable lcba variable lmba variable mid struct::list assign [minfo] _ _ _ cba _ mba | | | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | variable lcba variable lmba variable mid struct::list assign [minfo] _ _ _ cba _ mba set dc [expr $cba - $lcba] ; set lcba $cba set dm [expr $mba - $lmba] ; set lmba $mba # projection: 1 2 3 4 5 6 7 6 8 10 return "[F [incr mid]] | [F $cba] | [F $dc] | [F $mba] | [F $dm] |=| " } proc mark {} { variable track ; if {!$track} return |
︙ | ︙ |
Added tools/decode-email.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 | /* ** This program reads a raw email file and attempts to decode it into ** a more human-readable format. The following decodings are done: ** ** (1) Header values are prefixed by "| " at the left margin. ** ** (2) Content-Transfer-Encoding is recognized and the content is ** decoded for display. */ #define _GNU_SOURCE #include <stdio.h> #include <string.h> #include <ctype.h> #define BINARY 0 #define BASE64 1 #define QUOTED 2 static int decode_hex(char c){ if( c>='0' && c<='9' ) return c - '0'; if( c>='A' && c<='F' ) return c - 'A' + 10; if( c>='a' && c<='f' ) return c - 'a' + 10; return -1; } static void convert_file(const char *zFilename, FILE *in){ int inHdr = 1; int n; int nBoundary; int decodeType = 0; int textMimetype = 1; char *zB; char zBoundary[200]; char zLine[5000]; char zOut[5000]; while( fgets(zLine, sizeof(zLine), in) ){ if( !inHdr && zLine[0]=='-' && zLine[1]=='-' && strncmp(zLine+2,zBoundary,nBoundary)==0 ){ printf("|----------------- end of body section ---------|\n"); inHdr = 1; } if( !inHdr ){ if( textMimetype && decodeType==BASE64 ){ int ii, jj, c, x, y; int bits = 0; for(ii=jj=0; (c = zLine[ii])!=0; ii++){ if( c>='A' && c<='Z' ){ x = c - 'A'; }else if( c>='a' && c<='z' ){ x = c - 'a' + 26; }else if( c>='0' && c<='9' ){ x = c - '0' + 52; }else if( c=='+' ){ x = 62; }else if( c=='/' ){ x = 63; }else if( c=='=' ){ x = 0; }else{ continue; } if( bits==0 ){ y = x; bits = 6; }else if( bits==6 ){ zOut[jj++] = ((y<<2) & 0xfc) | ((x>>4) & 0x03); y = x & 0xf; bits = 4; }else if( bits==4 ){ zOut[jj++] = ((y<<4) & 0xf0) | ((x>>2) & 0x0f); y = x & 0x3; bits = 2; }else if( bits==2 ){ zOut[jj++] = ((y<<6) & 0xc0) | (x & 0x3f); bits = 0; } } zOut[jj] = 0; printf("%s", zOut); }else if( textMimetype && decodeType==QUOTED ){ int ii, jj, c; for(ii=jj=0; (c = zLine[ii])!=0; ii++){ if( c=='=' ){ int x1 = decode_hex(zLine[ii+1]); int x2 = decode_hex(zLine[ii+2]); if( x1>=0 && x2>=0 ){ zOut[jj++] = (x1<<4) | x2; ii += 2; }else if( zLine[ii+1]=='\r' && zLine[ii+2]=='\n' ){ ii += 2; } }else{ zOut[jj++] = c; } } zOut[jj] = 0; printf("%s", zOut); }else{ printf("%s", zLine); } continue; } n = (int)strlen(zLine); while( n>0 && isspace(zLine[n-1]) ){ n--; } zLine[n] = 0; if( n==0 ){ inHdr = 0; printf("|----------------- end of header ---------------|\n"); continue; } printf("| %s\n", zLine); if( strncasecmp(zLine,"Content-Type:", 13)==0 ){ textMimetype = strstr(zLine, "text/")!=0; printf("|** %s content type **|\n", textMimetype ? "Text" : "Non-text"); } if( strncasecmp(zLine,"Content-Transfer-Encoding:", 26)==0 ){ if( strcasestr(zLine, "base64") ){ decodeType = BASE64; }else if( strcasestr(zLine, "quoted-printable") ){ decodeType = QUOTED; }else{ decodeType = BINARY; } printf("|** Content encoding %s **|\n", decodeType==BASE64 ? "BASE64" : decodeType==QUOTED ? "QUOTED" : "BINARY"); } zB = strstr(zLine, "boundary=\""); if( zB ){ int kk; zB += 10; for(kk=0; zB[kk] && zB[kk]!='"' && kk<sizeof(zBoundary)-1; kk++){ zBoundary[kk] = zB[kk]; } zBoundary[kk] = 0; nBoundary = kk; printf("|** boundary [%s] **|\n", zBoundary); } } } int main(int argc, char **argv){ if( argc==1 ){ convert_file("<stdin>", stdin); return 0; }else{ int i; for(i=1; i<argc; i++){ FILE *in = fopen(argv[i], "rb"); if( in==0 ){ fprintf(stderr, "cannot open \"%s\"", argv[i]); }else{ convert_file(argv[i], in); fclose(in); } } } return 0; } |
Added tools/email-monitor.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 | #!/usr/bin/tcl # # Monitor the database file named on the command line for # incoming email messages. Print the "To:" line of each # email on standard output as it is received. # # It should be relatively easy to modify this scribe to actually # deliver the emails to a real email transfer agent such as # Postfix. # # For long-term use, set the polling interval to something # greater than the default 100 milliseconds. Polling once # every 10 seconds is probably sufficient. # set POLLING_INTERVAL 100 ;# milliseconds set dbfile [lindex $argv 0] if {[llength $argv]!=1} { puts stderr "Usage: $argv0 DBFILE" exit 1 } package require sqlite3 puts "SQLite version [sqlite3 -version]" sqlite3 db $dbfile db timeout 2000 catch {db eval {PRAGMA journal_mode=WAL}} db eval { CREATE TABLE IF NOT EXISTS email( emailid INTEGER PRIMARY KEY, msg TXT ); } while {1} { db transaction immediate { set n 0 db eval {SELECT msg FROM email} { set email ??? regexp {To: \S*} $msg to puts "$to ([string length $msg] bytes)" incr n } if {$n>0} { db eval {DELETE FROM email} } # Hold the write lock a little longer in order to exercise # the SQLITE_BUSY handling logic on the writing inside of # Fossil. Probably comment-out this line for production use. after 100 } after $POLLING_INTERVAL } |
Added tools/email-sender.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 | #!/usr/bin/tcl # # Monitor the database file named by the DBFILE variable # looking for email messages sent by Fossil. Forward each # to /usr/sbin/sendmail. # set POLLING_INTERVAL 10000 ;# milliseconds set DBFILE /home/www/fossil/emailqueue.db set PIPE "/usr/sbin/sendmail -ti" package require sqlite3 # puts "SQLite version [sqlite3 -version]" sqlite3 db $DBFILE db timeout 5000 catch {db eval {PRAGMA journal_mode=WAL}} db eval { CREATE TABLE IF NOT EXISTS email( emailid INTEGER PRIMARY KEY, msg TXT ); } while {1} { db transaction immediate { set n 0 db eval {SELECT msg FROM email} { set pipe $PIPE if {[regexp {\nFrom:[^\n]*<([^>]+)>} $msg all addr]} { append pipe " -f $addr" } set out [open |$pipe w] puts -nonewline $out $msg flush $out close $out incr n } if {$n>0} { db eval {DELETE FROM email} } } after $POLLING_INTERVAL } |
Added tools/emcc.sh.in.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/bash ######################################################################## # WARNING: emcc.sh is generated from emcc.sh.in by the configure # process. Do not edit emcc.sh directly, as it may be deleted or # overwritten by the configure script. # # A wrapper around the emcc compiler which uses configure-time state # to locate the Emscripten SDK and import the SDK's environment # script, if needed. ######################################################################## # EMSDK_HOME comes from the configure --with-emsdk=/dir flag. # EMSDK_ENV is ${thatDir}/emsdk_env.sh and is also set by the # configure process. EMSDK_HOME="@EMSDK_HOME@" EMSDK_ENV="@EMSDK_ENV@" emcc=$(which emcc 2>/dev/null) if [ x = "x${emcc}" ]; then # If emcc is not found in the path, try to find it via an emsdk # installation. The SDK variant is the official installation # style supported by the Emscripten folks, but emcc is also # available via package managers on some OSes. if [ x = "x${EMSDK_HOME}" ]; then echo "EMSDK_HOME is not set. Pass --with-emsdk=/path/to/emsdk" \ "to the configure script." 1>&2 exit 1 fi if [ x = "x${EMSDK_ENV}" ]; then if [ -f "${EMSDK_HOME}/emsdk_env.sh" ]; then EMSDK_ENV="${EMSDK_HOME}/emsdk_env.sh" else echo "EMSDK_ENV is not set. Expecting configure script to set it." 1>&2 exit 2 fi fi if [ ! -f "${EMSDK_ENV}" ]; then echo "emsdk_env script not found: $EMSDK_ENV" 1>&2 exit 3 fi # $EMSDK is part of the state set by emsdk_env.sh. if [ x = "x${EMSDK}" ]; then source "${EMSDK_ENV}" >/dev/null 2>&1 || { # ^^^ unfortunately outputs lots of noise to stderr rc=$? echo "Error sourcing ${EMSDK_ENV}" exit $rc } fi emcc=$(which emcc 2>/dev/null) if [ x = "x${emcc}" ]; then echo "emcc not found in PATH. Normally that's set up by EMSDK_ENV." 1>&2 exit 4 fi fi exec emcc "$@" |
Added tools/fossil-autocomplete.bash.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # Command name completion for Fossil. # Mailing-list contribution by Stuart Rackham. function _fossil() { local cur commands cur=${COMP_WORDS[COMP_CWORD]} commands=$(fossil help --all) if [ $COMP_CWORD -eq 1 ] || [ ${COMP_WORDS[1]} = help ]; then # Command name completion for 1st argument or 2nd if help command. COMPREPLY=( $(compgen -W "$commands" $cur) ) else # File name completion for other arguments. COMPREPLY=( $(compgen -f $cur{}) ) fi } complete -o default -F _fossil fossil f |
Added tools/fossil-autocomplete.zsh.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #compdef fossil # Origin: https://chiselapp.com/user/lifepillar/repository/fossil-zsh-completion ################################################################################# # # # Copyright 2020 Lifepillar # # # # Permission is hereby granted, free of charge, to any person obtaining a copy # # of this software and associated documentation files (the "Software"), to deal # # in the Software without restriction, including without limitation the rights # # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # # copies of the Software, and to permit persons to whom the Software is # # furnished to do so, subject to the following conditions: # # # # The above copyright notice and this permission notice shall be included in # # all copies or substantial portions of the Software. # # # # 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 OR COPYRIGHT HOLDERS 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. # # # ################################################################################# # To reload the completion function after it has been modified: # # $ unfunction _fossil # $ autoload -U _fossil # # See also: http://zsh.sourceforge.net/Doc/Release/Completion-System.html # See also: https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org ################################################################################ # Functions that help build this completion file # ################################################################################ # This function can be used to generate scaffolding code for the options of all # the commands. Copy and paste the result at the suitable spot in this script # to update it. To parse all commands: # # __fossil_parse_help -a # # To parse all test commands: # # __fossil_parse_help -t # # NOTE: The code must be adapted manually. Diff with previous version! function __fossil_parse_help() { echo ' case "$words[1]" in' for c in `fossil help $1 | xargs -n1 | sort`; do echo " ($c)" echo ' _arguments \\' __fossil_format_options $c; echo " '(- *)'--help'[Show help and exit]' \\" echo " '*:files:_files'" echo '' echo ' ;;' done; echo ' esac' echo ' ;;' } # Extract the options of a command and format it in a way that can be used in # a ZSH completion script. # Use `__fossil_format_options -o` to extract the common options. function __fossil_format_options() { fossil help $1 2>&1 \ | grep '^\s\{1,3\}-' \ | sed -E 's/^ +//' \ | awk -F ' +' '{ v=match($1,/\|/) split($1,y,"|") printf " " if (v>0) printf "\"(--help %s %s)\"{%s,%s}",y[1],y[2],y[1],y[2]; else printf "\"(--help %s)\"%s",y[1],y[1]; $1="" gsub(/^ +| +$/,"",$0); gsub(/^ +| +$/,"",$0); gsub(/\x27/,"\x27\"\x27\"\x27",$0); print "\x27["$0"]\x27 \\"; }' } ################################################################################ # Helper functions used for completion. # ################################################################################ function __fossil_commands() { fossil help --all } function __fossil_test_commands() { fossil help --test } function __fossil_all_commands() { __fossil_commands __fossil_test_commands } function __fossil_users() { fossil user ls 2>/dev/null | awk '{print $1}' } function __fossil_branches() { fossil branch ls -a 2>/dev/null | sed 's/\* *//' } function __fossil_tags() { fossil tag ls 2>/dev/null } function __fossil_repos() { ls | grep .fossil fossil all ls 2>/dev/null } function __fossil_remotes() { fossil remote list 2>/dev/null | awk '{print $1}' } function __fossil_wiki_pages() { fossil wiki list 2>/dev/null } function __fossil_areas() { compadd all email project shun skin ticket user alias subscriber return 0 } function __fossil_settings() { fossil help --setting } function __fossil_urls() { local u u=($(__fossil_remotes)) compadd -a u compadd -S '' file:// http:// https:// ssh:// return 0 } ################################################################################ # Main # ################################################################################ function _fossil() { local context state state_descr line typeset -A opt_args local -a _common_options # Scaffolding code for common options can be generated with `__fossil_format_options -o`. _common_options=( "(--help --args)"--args'[FILENAME Read additional arguments and options from FILENAME]:file:_files' "(--help --cgitrace)"--cgitrace'[Active CGI tracing]' "(--help --comfmtflags --comment-format)"--comfmtflags'[VALUE Set comment formatting flags to VALUE]:value:' "(--help --comment-format --comfmtflags)"--comment-format'[VALUE Alias for --comfmtflags]:value:' "(--help --errorlog)"--errorlog'[FILENAME Log errors to FILENAME]:file:_files' "(- --help)"--help'[Show help on the command rather than running it]' "(--help --httptrace)"--httptrace'[Trace outbound HTTP requests]' "(--help --localtime)"--localtime'[Display times using the local timezone]' "(--help --no-th-hook)"--no-th-hook'[Do not run TH1 hooks]' "(--help --quiet)"--quiet'[Reduce the amount of output]' "(--help --sqlstats)"--sqlstats'[Show SQL usage statistics when done]' "(--help --sqltrace)"--sqltrace'[Trace all SQL commands]' "(--help --sshtrace)"--sshtrace'[Trace SSH activity]' "(--help --ssl-identity)"--ssl-identity'[NAME Set the SSL identity to NAME]:name:' "(--help --systemtrace)"--systemtrace'[Trace calls to system()]' "(--help --user -U)"{--user,-U}'[USER Make the default user be USER]:user:($(__fossil_users))' "(--help --utc)"--utc'[Display times using UTC]' "(--help --vfs)"--vfs'[NAME Cause SQLite to use the NAME VFS]:name:' ) local -a _fossil_clean_options _fossil_clean_options=( "(--help --allckouts)"--allckouts'[Check for empty directories within any checkouts]' "(--help --case-sensitive)"--case-sensitive'[BOOL Override case-sensitive setting]:bool:(yes no)' "(--help --dirsonly)"--dirsonly'[Only remove empty directories]' "(--help --disable-undo)"--disable-undo'[Disables use of the undo]' "(--help --dotfiles)"--dotfiles'[Include files beginning with a dot (".")]' "(--help --emptydirs)"--emptydirs'[Remove empty directories]' "(--help -f --force)"{-f,--force}'[Remove files without prompting]' "(--help -i --prompt)"{-i,--prompt}'[Prompt before removing each file]' "(--help -x --verily)"{-x,--verily}'[Remove everything that is not managed]' "(--help --clean)"--clean'[CSG Never prompt to delete files matching CSG glob pattern]:pattern:' "(--help --ignore)"--ignore'[CSG Ignore files matching CSG glob pattern]:pattern:' "(--help --keep)"--keep'[CSG Keep files matching CSG glob pattern]:pattern:' "(--help -n --dry-run)"{-n,--dry-run}'[Delete nothing, but display what would have been deleted]' "(--help --no-prompt)"--no-prompt'[Assume NO for every question]' "(--help --temp)"--temp'[Remove only Fossil-generated temporary files]' "(--help -v --verbose)"{-v,--verbose}'[Show all files as they are removed]' ) local -a _fossil_rebuild_options _fossil_rebuild_options=( "(--help --analyze)"--analyze'[Run ANALYZE on the database after rebuilding]' "(--help --cluster)"--cluster'[Compute clusters for unclustered artifacts]' "(--help --compress)"--compress'[Strive to make the database as small as possible]' "(--help --compress-only)"--compress-only'[Skip the rebuilding step. Do --compress only]' "(--help --deanalyze)"--deanalyze'[Remove ANALYZE tables from the database]' "(--help --ifneeded)"--ifneeded'[Only do the rebuild if it would change the schema version]' "(--help --index)"--index'[Always add in the full-text search index]' "(--help --noverify)"--noverify'[Skip the verification of changes to the BLOB table]' "(--help --noindex)"--noindex'[Always omit the full-text search index]' "(--help --pagesize)"--pagesize'[N Set the database pagesize to N. (512..65536 and power of 2)]:number:' "(--help --quiet)"--quiet'[Only show output if there are errors]' "(--help --stats)"--stats'[Show artifact statistics after rebuilding]' "(--help --vacuum)"--vacuum'[Run VACUUM on the database after rebuilding]' "(--help --wal)"--wal'[Set Write-Ahead-Log journalling mode on the database]' ) local -a _fossil_dbstat_options _fossil_dbstat_options=( "(--help --brief -b)"{--brief,-b}'[Only show essential elements]' "(--help --db-check)"--db-check'[Run "PRAGMA quick_check" on the repository database]' "(--help --db-verify)"--db-verify'[Run a full verification of the repository integrity]' "(--help --omit-version-info)"--omit-version-info'[Omit the SQLite and Fossil version information]' ) local -a _fossil_diff_options _fossil_diff_options=( "(--help --binary)"--binary'[PATTERN Treat files that match the glob PATTERN as binary]:pattern:' "(--help --branch)"--branch'[BRANCH Show diff of all changes on BRANCH]:branch:($(__fossil_branches))' "(--help --brief)"--brief'[Show filenames only]' "(--help --checkin)"--checkin'[VERSION Show diff of all changes in VERSION]:version:' "(--help --command)"--command'[PROG External diff program - overrides "diff-command"]:program:' "(--help --context -c)"{--context,-c}'[N Use N lines of context]:number:' "(--help --diff-binary)"--diff-binary'[BOOL Include binary files when using external commands]:bool:(yes no)' "(--help --exec-abs-paths)"--exec-abs-paths'[Force absolute path names with external commands]' "(--help --exec-rel-paths)"--exec-rel-paths'[Force relative path names with external commands]' "(--help --from -r)"{--from,-r}'[VERSION Select VERSION as source for the diff]:version:' "(--help --internal -i)"{--internal,-i}'[Use internal diff logic]' "(--help --new-file -N)"{--new-file,-N}'[Show complete text of added and deleted files]' "(--help --numstat)"--numstat'[Show only the number of lines delete and added]' "(--help --side-by-side -y)"{--side-by-side,-y}'[Side-by-side diff]' "(--help --strip-trailing-cr)"--strip-trailing-cr'[Strip trailing CR]' "(--help --tclsh)"--tclsh'[PATH Tcl/Tk used for --tk (default: "tclsh")]' "(--help --tk)"--tk'[Launch a Tcl/Tk GUI for display]' "(--help --to)"--to'[VERSION Select VERSION as target for the diff]:version:' "(--help --undo)"--undo'[Diff against the "undo" buffer]' "(--help --unified)"--unified'[Unified diff]' "(--help -v --verbose)"{-v,--verbose}'[Output complete text of added or deleted files]' "(--help -w --ignore-all-space)"{-w,--ignore-all-space}'[Ignore white space when comparing lines]' "(--help -W --width)"{-W,--width}'[NUM Width of lines in side-by-side diff]:number:' "(--help -Z --ignore-trailing-space)"{-Z,--ignore-trailing-space}'[Ignore changes to end-of-line whitespace]' ) local -a _fossil_extras_options _fossil_extras_options=( "(--help --abs-paths)"--abs-paths'[Display absolute pathnames]' "(--help --case-sensitive)"--case-sensitive'[BOOL Override case-sensitive setting]:bool:(yes no)' "(--help --dotfiles)"--dotfiles'[Include files beginning with a dot (".")]' "(--help --header)"--header'[Identify the repository if there are extras]' "(--help --ignore)"--ignore'[CSG Ignore files matching patterns from the argument]:pattern:' "(--help --rel-paths)"--rel-paths'[Display pathnames relative to the current working]' ) local -a _fossil_server_options _fossil_server_options=( "(--help --baseurl)"--baseurl'[URL Use URL as the base]:url:__fossil_urls' "(--help --create)"--create'[Create a new REPOSITORY if it does not already exist]' "(--help --extroot)"--extroot'[DIR Document root for the /ext extension mechanism]:directory:_files -/' "(--help --files)"--files'[GLOBLIST Comma-separated list of glob patterns for static files]:pattern:' "(--help --localauth)"--localauth'[Enable automatic login for requests from localhost]' "(--help --localhost)"--localhost'[Listen on 126.0.0.1 only]' "(--help --https)"--https'[Input passes through a reverse HTTPS->HTTP proxy]' "(--help --jsmode)"--jsmode'[MODE Determine how JavaScript is delivered with pages]:mode:(inline separate bundled)' "(--help --max-latency)"--max-latency'[N Do not let any single HTTP request run for more than N seconds]:number:' "(--help --nocompress)"--nocompress'[Do not compress HTTP replies]' "(--help --nojail)"--nojail'[Drop root privileges but do not enter the chroot jail]' "(--help --nossl)"--nossl'[Signal that no SSL connections are available]' "(--help --notfound)"--notfound'[URL Redirect]:url:__fossil_urls' "(--help --page)"--page'[PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci"]:number:' "(--help -P --port)"{-P,--port}'[TCPPORT Listen to request on port TCPPORT]:number:' "(--help --th-trace)"--th-trace'[Trace TH0 execution (for debugging purposes)]' "(--help --repolist)"--repolist'[If REPOSITORY is dir, URL "/" lists repos]' "(--help --scgi)"--scgi'[Accept SCGI rather than HTTP]' "(--help --skin)"--skin'[LABEL Use override skin LABEL]:label:' "(--help --usepidkey)"--usepidkey'[Use saved encryption key from parent process]' ) _arguments -C \ ${_common_options[@]} \ '1:command:->command' \ '*::args:->args' case $state in (command) if [[ $line =~ '^te' ]]; then _arguments '*:test commands:($(__fossil_test_commands))' else _arguments '*:commands:($(__fossil_commands))' fi ;; (args) if [[ $line[1] =~ '^test-' ]]; then __fossil_complete_test_commands return 0 fi case $line[1] in (3-way-merge) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (add) _arguments \ "(--help --case-sensitive)"--case-sensitive'[BOOL Override the case-sensitive setting]:bool:(yes no)' \ "(--help --dotfiles)"--dotfiles'[include files beginning with a dot (".")]' \ "(--help -f --force)"{-f,--force}'[Add files without prompting]' \ "(--help --ignore)"--ignore'[CSG Ignore unmanaged files matching Comma Separated Glob]:pattern:' \ "(--help --clean)"--clean'[CSG Also ignore files matching Comma Separated Glob]:pattern:' \ "(--help --reset)"--reset'[Reset the ADDED state of a checkout]' \ "(--help -v --verbose)"{-v,--verbose}'[Outputs information about each --reset file]' \ "(--help -n --dry-run)"{-n,--dry-run}'[Display instead of run actions]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (addremove) _arguments \ "(--help --case-sensitive)"--case-sensitive'[BOOL Override the case-sensitive setting]:bool:(yes no)' \ "(--help --dotfiles)"--dotfiles'[Include files beginning with a dot (".")]' \ "(--help --ignore)"--ignore'[CSG Ignore unmanaged files matching Comma Separated Glob]:pattern:' \ "(--help --clean)"--clean'[CSG Also ignore files matching Comma Separated Glob]:pattern:' \ "(--help -n --dry-run)"{-n,--dry-run}'[If given, display instead of run actions]' \ "(--help --reset)"--reset'[Reset the ADDED/DELETED state of a checkout]' \ "(--help --verbose -v)"{--verbose,-v}'[Outputs information about each --reset file]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (alerts) __fossil_alerts ;; (all) __fossil_all ;; (amend) _arguments \ ':hash:' \ "(--help --author)"--author'[USER Make USER the author for check-in]:user:($(__fossil_users))' \ "(--help -m --comment)"{-m,--comment}'[COMMENT Make COMMENT the check-in comment]:comment:' \ "(--help -M --message-file)"{-M,--message-file}'[FILE Read the amended comment from FILE]:file:_files' \ "(--help -e --edit-comment)"{-e,--edit-comment}'[Launch editor to revise comment]' \ "(--help --date)"--date'[DATETIME Make DATETIME the check-in time]:datetime:(now)' \ "(--help --bgcolor)"--bgcolor'[COLOR Apply COLOR to this check-in]:color:' \ "(--help --branchcolor)"--branchcolor'[COLOR Apply and propagate COLOR to the branch]:color:' \ "(--help --tag)"--tag'[TAG Add new TAG to this check-in]:tag:($(__fossil_tags))' \ "(--help --cancel)"--cancel'[TAG Cancel TAG from this check-in]:tag:($(__fossil_tags))' \ "(--help --branch)"--branch'[NAME Make this check-in the start of branch NAME]:name:($(__fossil_branches))' \ "(--help --hide)"--hide'[Hide branch starting from this check-in]' \ "(--help --close)"--close'[Mark this "leaf" as closed]' \ "(--help -n --dry-run)"{-n,--dry-run}'[Print control artifact, but make no changes]' \ "(--help --date-override)"--date-override'[DATETIME Set the change time on the control artifact]:datetime:(now)' \ "(--help --user-override)"--user-override'[USER Set the user name on the control artifact]:user:($(__fossil_users))' \ '(- *)'--help'[Show help and exit]' ;; (annotate|blame|praise) _arguments \ "(--help --filevers)"--filevers'[Show file version numbers]' \ "(--help -r --revision)"{-r,--revision}'[VERSION The specific check-in containing the file]:version:' \ "(--help -l --log)"{-l,--log}'[List all versions analyzed]' \ "(--help -n --limit)"{-n,--limit}'[LIMIT Limit versions]:limit:(none)' \ "(--help -o --origin)"{-o,--origin}'[VERSION The origin check-in]:version:' \ "(--help -w --ignore-all-space)"{-w,--ignore-all-space}'[Ignore white space when comparing lines]' \ "(--help -Z --ignore-trailing-space)"{-Z,--ignore-trailing-space}'[Ignore whitespace at line end]' \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (artifact) _arguments \ "(--help -R --repository)"{-R,--repository}'[FILE Extract artifacts from repository FILE]:fossils:($(__fossil_repos))' \ '(- *)'--help'[Show help and exit]' \ '1:artifact id:' \ '2::output file:_files' ;; (attachment) _arguments \ "(--help -t --technote)"{-t,--technote}'[DATETIME The timestamp of the technote]:datetime:(now)' \ "(--help -t --technote)"{-t,--technote}'[TECHNOTE-ID The technote to be updated]:technote-id:' \ '(- *)'--help'[Show help and exit]' \ '1:what:(add)' \ '::pagename:($(__fossil_wiki_pages))' \ ':file:_files' ;; (backoffice) _arguments \ "(--help --debug)"--debug'[Show what this command is doing]' \ "(--help --logfile)"--logfile'[FILE Append a log of backoffice actions onto FILE]:file:_files' \ "(--help --min)"--min'[N Invoke backoffice at least once every N seconds]:number:' \ "(--help --poll)"--poll'[N Polling frequency]:number:' \ "(--help --trace)"--trace'[Enable debugging output on stderr]' \ "(--help --nodelay)"--nodelay'[Do not queue up or wait for a backoffice job]' \ "(--help --nolease)"--nolease'[Always run backoffice]' \ '(- *)'--help'[Show help and exit]' \ '*:fossils:($(__fossil_repos))' ;; (backup) _arguments \ "(--help --overwrite)"--overwrite'[OK to overwrite an existing file]' \ "(--help -R)"-R'[NAME Filename of the repository to backup]:fossils:($(__fossil_repos))' \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (bisect) __fossil_bisect ;; (branch) __fossil_branch ;; (bundle) __fossil_bundle ;; (cache) __fossil_cache ;; (cat) _arguments \ "(--help -R --repository)"{-R,--repository}'[FILE Extract artifacts from repository FILE]:fossils:($(__fossil_repos))' \ "(--help -r)"-r'[VERSION The specific check-in containing the file]:version:' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (cgi) _arguments \ '(- *)'--help'[Show help and exit]' \ '::cgi:' \ ':file:_files' ;; (changes|status) _arguments \ "(--help --abs-paths)"--abs-paths'[Display absolute pathnames]' \ "(--help --rel-paths)"--rel-paths'[Display pathnames relative to the current directory]' \ "(--help --hash)"--hash'[Verify file status using hashing]' \ "(--help --case-sensitive)"--case-sensitive'[BOOL Override case-sensitive setting]:bool:(yes no)' \ "(--help --dotfiles)"--dotfiles'[Include unmanaged files beginning with a dot]' \ "(--help --ignore)"--ignore'[CSG Ignore unmanaged files matching CSG glob patterns]:pattern:' \ "(--help --header)"--header'[Identify the repository if report is non-empty.]' \ "(--help -v --verbose)"{-v,--verbose}'[Say "(none)" if the change report is empty]' \ "(--help --classify)"--classify'[Start each line with the file'"'"'s change type]' \ "(--help --no-classify)"--no-classify'[Do not print file change types]' \ "(--help --edited)"--edited'[Display edited, merged, and conflicted files]' \ "(--help --updated)"--updated'[Display files updated by merge/integrate]' \ "(--help --changed)"--changed'[Combination of --edited and --updated]' \ "(--help --missing)"--missing'[Display missing files]' \ "(--help --added)"--added'[Display added files]' \ "(--help --deleted)"--deleted'[Display deleted files]' \ "(--help --renamed)"--renamed'[Display renamed files]' \ "(--help --conflict)"--conflict'[Display files having merge conflicts]' \ "(--help --meta)"--meta'[Display files with metadata changes]' \ "(--help --unchanged)"--unchanged'[Display unchanged files]' \ "(--help --all)"--all'[Display all managed files]' \ "(--help --extra)"--extra'[Display unmanaged files]' \ "(--help --differ)"--differ'[Display modified and extra files]' \ "(--help --merge)"--merge'[Display merge contributors]' \ "(--help --no-merge)"--no-merge'[Do not display merge contributors]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (checkout|co) _arguments \ "(--help --force)"--force'[Ignore edited files in the current checkout]' \ "(--help --keep)"--keep'[Only update the manifest and manifest.uuid files]' \ "(--help --force-missing)"--force-missing'[Force checkout even if content is missing]' \ "(--help --setmtime)"--setmtime'[Set timestamps of all files to match their SCM-side]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (ci|commit) _arguments \ "(--help --allow-conflict)"--allow-conflict'[Allow unresolved merge conflicts]' \ "(--help --allow-empty)"--allow-empty'[Allow a commit with no changes]' \ "(--help --allow-fork)"--allow-fork'[Allow the commit to fork]' \ "(--help --allow-older)"--allow-older'[Allow a commit older than its ancestor]' \ "(--help --baseline)"--baseline'[Use a baseline manifest in the commit process]' \ "(--help --bgcolor)"--bgcolor'[COLOR Apply COLOR to this one check-in only]:color:' \ "(--help --branch)"--branch'[NEW-BRANCH-NAME check in to this new branch]:new branch:' \ "(--help --branchcolor)"--branchcolor'[COLOR Apply given COLOR to the branch]:color:' \ "(--help --close)"--close'[Close the branch being committed]' \ "(--help --date-override)"--date-override'[DATETIME DATE to use instead of '"'"'now'"'"']:datetime:' \ "(--help --delta)"--delta'[Use a delta manifest in the commit process]' \ "(--help --hash)"--hash'[Verify file status using hashing]' \ "(--help --integrate)"--integrate'[Close all merged-in branches]' \ "(--help -m --comment)"{-m,--comment}'[COMMENT Use COMMENT as commit comment]:comment:' \ "(--help -M --message-file)"{-M,--message-file}'[FILE Read the commit comment from given file]:file:_files' \ "(--help --mimetype)"--mimetype'[MIMETYPE Mimetype of check-in comment]:mimetype:' \ "(--help -n --dry-run)"{-n,--dry-run}'[If given, display instead of run actions]' \ "(--help --no-prompt)"--no-prompt'[Assume NO for every question]' \ "(--help --no-warnings)"--no-warnings'[Omit all warnings about file contents]' \ "(--help --no-verify)"--no-verify'[Do not run before-commit hooks]' \ "(--help --nosign)"--nosign'[Do not attempt to sign this commit with gpg]' \ "(--help --override-lock)"--override-lock'[Allow a check-in even though parent is locked]' \ "(--help --private)"--private'[Do not sync changes and their descendants]' \ "(--help --tag)"--tag'[TAG-NAME Assign given tag TAG-NAME to the check-in]:tag:($(__fossil_tags))' \ "(--help --trace)"--trace'[Debug tracing]' \ "(--help --user-override)"--user-override'[USER Use USER instead of the current default]:user:($(__fossil_users))' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (clean) _arguments \ ${_fossil_clean_options[@]} \ '*:files:_files' ;; (clone) _arguments \ "(--help --admin-user -A)"{--admin-user,-A}'[USERNAME Make USERNAME the administrator]:user:($(__fossil_users))' \ "(--help --httpauth -B)"{--httpauth,-B}'[USER:PASS Add HTTP Basic Authorization to requests]:user pass:' \ "(--help --nocompress)"--nocompress'[Omit extra delta compression]' \ "(--help --once)"--once'[Don'"'"'t remember the URI]' \ "(--help --private)"--private'[Also clone private branches]' \ "(--help --save-http-password)"--save-http-password'[Remember the HTTP password without asking]' \ "(--help --ssh-command -c)"{--ssh-command,-c}'[SSH Use SSH as the "ssh" command]:ssh command:' \ "(--help --ssl-identity)"--ssl-identity'[FILENAME Use the SSL identity if requested by the server]:file:_files' \ "(--help -u --unversioned)"{-u,--unversioned}'[Also sync unversioned content]' \ "(--help -v --verbose)"{-v,--verbose}'[Show more statistics in output]' \ '(- *)'--help'[Show help and exit]' \ '1:uri:__fossil_urls' \ '2:file:_files' ;; (close) _arguments \ "(--help --force -f)"{--force,-f}'[Close a check out with uncommitted changes]' \ '(- *)'--help'[Show help and exit]' ;; (configuration) __fossil_configuration ;; (dbstat) _arguments \ ${_fossil_dbstat_options[@]} \ '(- *)'--help'[Show help and exit]' ;; (deconstruct) _arguments \ "(--help -R --repository)"{-R,--repository}'[REPOSITORY Deconstruct given REPOSITORY]:fossils:($(__fossil_repos))' \ "(--help -K --keep-rid1)"{-K,--keep-rid1}'[Save the filename of the artifact with RID=1]' \ "(--help -L --prefixlength)"{-L,--prefixlength}'[N Set the length of the names of the DESTINATION]:number:' \ "(--help --private)"--private'[Include private artifacts]' \ "(--help -P --keep-private)"{-P,--keep-private}'[Save the list of private artifacts to .private]' \ '(- *)'--help'[Show help and exit]' \ '1:destination:_files -/' ;; (delete|forget|rm) _arguments \ "(--help --soft)"--soft'[Skip removing files from the checkout]' \ "(--help --hard)"--hard'[Remove files from the checkout]' \ "(--help --case-sensitive)"--case-sensitive'[BOOL Override the case-sensitive setting]:bool:(yes no)' \ "(--help -n --dry-run)"{-n,--dry-run}'[If given, display instead of run actions]' \ "(--help --reset)"--reset'[Reset the DELETED state of a checkout]' \ "(--help --verbose -v)"{--verbose,-v}'[Outputs information about each --reset file]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (descendants) _arguments \ "(--help -R --repository)"{-R,--repository}'[FILE Extract info from repository FILE]:fossils:($(__fossil_repos))' \ "(--help -W --width)"{-W,--width}'[NUM Width of lines]:number:' \ '(- *)'--help'[Show help and exit]' \ '1::check-in:' ;; (diff|gdiff) _arguments \ ${_fossil_diff_options[@]} \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (export) _arguments \ '(- *)'--help'[Show help and exit]' ;; (extras) _arguments \ ${_fossil_extras_options[@]} \ '(- *)'--help'[Show help and exit]' \ '*:files:_files -/' ;; (finfo) _arguments \ "(--help -b --brief)"{-b,--brief}'[Display a brief (one line / revision) summary]' \ "(--help --case-sensitive)"--case-sensitive'[BOOL Enable or disable case-sensitive filenames]:bool:(yes no)' \ "(--help -l --log)"{-l,--log}'[Select log mode (the default)]' \ "(--help -n --limit)"{-n,--limit}'[N Display only the first N changes]:number:' \ "(--help --offset)"--offset'[P Skip P changes]:number:' \ "(--help -p --print)"{-p,--print}'[Select print mode]' \ "(--help -r --revision)"{-r,--revision}'[R Print the given revision]:version:' \ "(--help -s --status)"{-s,--status}'[Select status mode (print a status indicator for FILE)]' \ "(--help -W --width)"{-W,--width}'[NUM Width of lines]:number:' \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (fts-config) __fossil_fts_config ;; (git) __fossil_git ;; (grep) _arguments \ "(--help -c --count)"{-c,--count}'[Suppress normal output; print count of files]' \ "(--help -i --ignore-case)"{-i,--ignore-case}'[Ignore case]' \ "(--help -l --files-with-matches)"{-l,--files-with-matches}'[List only hash for each match]' \ "(--help --once)"--once'[Stop searching after the first match]' \ "(--help -s --no-messages)"{-s,--no-messages}'[Suppress error messages]' \ "(--help -v --invert-match)"{-v,--invert-match}'[Invert the sense of matching]' \ "(--help --verbose)"--verbose'[Show each file as it is analyzed]' \ '(- *)'--help'[Show help and exit]' \ '1:pattern:' \ '*:files:_files' ;; (hash-policy) _arguments \ '(- *)'--help'[Show help and exit]' \ '1::policy:(sha1 auto sha3 sha3-only shun-sha1)' ;; (help) _arguments \ "(- :)"{-a,--all}'[List both common and auxiliary commands]' \ "(- :)"{-o,--options}'[List command-line options common to all commands]' \ "(- :)"{-s,--setting}'[List setting names]' \ "(- :)"{-t,--test}'[List unsupported "test" commands]' \ "(- :)"{-x,--aux}'[List only auxiliary commands]' \ "(- :)"{-w,--www}'[List all web pages]' \ "(- :):all commands:($(__fossil_all_commands))" \ "(- *)"--html'[Format output as HTML rather than plain text]' ;; (hook) __fossil_hook ;; (http) _arguments \ "(--help --baseurl)"--baseurl'[URL Base URL (useful with reverse proxies)]:url:__fossil_urls' \ "(--help --extroot)"--extroot'[DIR Document root for the /ext extension mechanism]:file:_files -/' \ "(--help --files)"--files'[GLOB Comma-separate glob patterns for static file to serve]:pattern:' \ "(--help --host)"--host'[NAME Specify hostname of the server]:name:' \ "(--help --https)"--https'[Signal a request coming in via https]' \ "(--help --in)"--in'[FILE Take input from FILE instead of standard input]:file:_files' \ "(--help --ipaddr)"--ipaddr'[ADDR Assume the request comes from the given IP address]:address:' \ "(--help --jsmode)"--jsmode'[MODE Determine how JavaScript is delivered with pages]:mode:(inline separate bundled)' \ "(--help --localauth)"--localauth'[Enable automatic login for local connections]' \ "(--help --nocompress)"--nocompress'[Do not compress HTTP replies]' \ "(--help --nodelay)"--nodelay'[Omit backoffice processing if it would delay process exit]' \ "(--help --nojail)"--nojail'[Drop root privilege but do not enter the chroot jail]' \ "(--help --nossl)"--nossl'[Signal that no SSL connections are available]' \ "(--help --notfound)"--notfound'[URL Use URL as "HTTP 404, object not found" page]:url:__fossil_urls' \ "(--help --out)"--out'[FILE Write results to FILE instead of to standard output]:file:_files' \ "(--help --repolist)"--repolist'[If REPOSITORY is directory, URL "/" lists all repos]' \ "(--help --scgi)"--scgi'[Interpret input as SCGI rather than HTTP]' \ "(--help --skin)"--skin'[LABEL Use override skin LABEL]:label:' \ "(--help --th-trace)"--th-trace'[Trace TH1 execution (for debugging purposes)]' \ "(--help --usepidkey)"--usepidkey'[Use saved encryption key from parent process]' \ '(- *)'--help'[Show help and exit]' \ '1::fossils:_files' ;; (import) _arguments \ "(--help --git)"--git'[Import from the git-fast-export file format (default)]' \ "(--help --import-marks)"--import-marks'[FILE Restore marks table from FILE]:file:_files' \ "(--help --export-marks)"--export-marks'[FILE Save marks table to FILE]:files:_files' \ "(--help --rename-master)"--rename-master'[NAME Renames the master branch to NAME]:name:' \ "(--help --use-author)"--use-author'[Uses author as the committer]:user:($(__fossil_users))' \ "(--help --svn)"--svn'[Import from the svnadmin-dump file format]' \ "(--help --trunk)"--trunk'[FOLDER Name of trunk folder]:file:_files -/' \ "(--help --branches)"--branches'[FOLDER Name of branches folder]:file:_files -/' \ "(--help --tags)"--tags'[FOLDER Name of tags folder]:file:_files -/' \ "(--help --base)"--base'[PATH Path to project root in repository]:file:_files' \ "(--help --flat)"--flat'[The whole dump is a single branch]' \ "(--help --rev-tags)"--rev-tags'[Tag each revision, implied by -i]' \ "(--help --no-rev-tags)"--no-rev-tags'[Disables tagging effect of -i]' \ "(--help --rename-rev)"--rename-rev'[PAT Rev tag names, default "svn-rev-%"]:pattern:' \ "(--help --ignore-tree)"--ignore-tree'[DIR Ignores subtree rooted at DIR]:file:_files -/' \ "(--help -i --incremental)"{-i,--incremental}'[Allow importing into an existing repository]' \ "(--help -f --force)"{-f,--force}'[Overwrite repository if already exists]' \ "(--help -q --quiet)"{-q,--quiet}'[Omit progress output]' \ "(--help --no-rebuild)"--no-rebuild'[Skip the "rebuilding metadata" step]' \ "(--help --no-vacuum)"--no-vacuum'[Skip the final VACUUM of the database file]' \ "(--help --rename-trunk)"--rename-trunk'[NAME use NAME as name of imported trunk branch]:name:' \ "(--help --rename-branch)"--rename-branch'[PAT Rename all branch names using PAT pattern]:pattern:' \ "(--help --rename-tag)"--rename-tag'[PAT Rename all tag names using PAT pattern]:pattern:' \ "(--help --admin-user -A)"{--admin-user,-A}'[NAME Use NAME for the admin user]:user:($(__fossil_users))' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (info) _arguments \ "(--help -R --repository)"{-R,--repository}'[FILE Extract info from repository FILE]:fossils:($(__fossil_repos))' \ "(--help -v --verbose)"{-v,--verbose}'[Show extra information about repositories]' \ '(- *)'--help'[Show help and exit]' \ '1:fossils:($(__fossil_repos))' ;; (init|new) _arguments \ "(--help --template)"--template'[FILE Copy settings from repository file]:file:_files' \ "(--help --admin-user -A)"{--admin-user,-A}'[USERNAME Select given USERNAME as admin user]:user:($(__fossil_users))' \ "(--help --date-override)"--date-override'[DATETIME Use DATETIME as time of the initial check-in]:datetime:(now)' \ "(--help --sha1)"--sha1'[Use an initial hash policy of "sha1"]' \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (json) __fossil_json ;; (leaves) _arguments \ "(--help -a --all)"{-a,--all}'[Show ALL leaves]' \ "(--help --bybranch)"--bybranch'[Order output by branch name]' \ "(--help -c --closed)"{-c,--closed}'[Show only closed leaves]' \ "(--help -m --multiple)"{-m,--multiple}'[Show only cases with multiple leaves on a single branch]' \ "(--help --recompute)"--recompute'[Recompute the "leaf" table in the repository DB]' \ "(--help -W --width)"{-W,--width}'[NUM Width of lines (default is to auto-detect)]:number:' \ '(- *)'--help'[Show help and exit]' ;; (login-group) __fossil_login_group ;; (ls) _arguments \ "(--help --age)"--age'[Show when each file was committed]' \ "(--help -v --verbose)"{-v,--verbose}'[Provide extra information about each file]' \ "(--help -t)"-t'[Sort output in time order]' \ "(--help -r)"-r'[VERSION The specific check-in to list]:version:' \ "(--help -R --repository)"{-R,--repository}'[FILE Extract info from repository FILE]:fossils:($(__fossil_repos))' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (md5sum) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (merge) _arguments \ "(--help --backout)"--backout'[Do a reverse cherrypick merge against VERSION]' \ "(--help --baseline)"--baseline'[BASELINE Use BASELINE as the "pivot" of the merge instead]:version:' \ "(--help --binary)"--binary'[GLOBPATTERN Treat files that match GLOBPATTERN as binary]:pattern:' \ "(--help --case-sensitive)"--case-sensitive'[BOOL Override the case-sensitive setting]:bool:(yes no)' \ "(--help --cherrypick)"--cherrypick'[Do a cherrypick merge VERSION into the current checkout]' \ "(--help -f --force)"{-f,--force}'[Force the merge even if it would be a no-op]' \ "(--help --force-missing)"--force-missing'[Force the merge even if there is missing content]' \ "(--help --integrate)"--integrate'[Merged branch will be closed when committing]' \ "(--help -K --keep-merge-files)"{-K,--keep-merge-files}'[On merge conflict, retain the temporary files]' \ "(--help -n --dry-run)"{-n,--dry-run}'[If given, display instead of run actions]' \ "(--help -v --verbose)"{-v,--verbose}'[Show additional details of the merge]' \ '(- *)'--help'[Show help and exit]' \ '1:version:' ;; (mv|rename) _arguments \ "(--help --soft)"--soft'[Skip moving files within the checkout]' \ "(--help --hard)"--hard'[Move files within the checkout]' \ "(--help --case-sensitive)"--case-sensitive'[BOOL Override the case-sensitive setting]:bool:(yes no)' \ "(--help -n --dry-run)"{-n,--dry-run}'[If given, display instead of run actions]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (open) _arguments \ "(--help --empty)"--empty'[Initialize checkout as being empty, but still connected]' \ "(--help --force)"--force'[Continue even if the directory is not empty]' \ "(--help --force-missing)"--force-missing'[Force opening a repository with missing content]' \ "(--help --keep)"--keep'[Only modify the manifest and manifest.uuid files]' \ "(--help --nested)"--nested'[Allow opening a repository inside an opened checkout]' \ "(--help --repodir)"--repodir'[DIR Store the clone in DIR]:directory:_files -/' \ "(--help --setmtime)"--setmtime'[Set timestamps of all files to match their SCM-side times]' \ "(--help --workdir)"--workdir'[DIR Use DIR as the working directory]:directory:_files -/' \ '(- *)'--help'[Show help and exit]' \ '1:fossils:($(__fossil_repos))' \ '::version:' ;; (pop3d) _arguments \ "(--help --logdir)"--logdir'[DIR Create log files inside DIR]:directory:_files -/' \ '(- *)'--help'[Show help and exit]' \ '1:fossils:($(__fossil_repos))' ;; (publish) _arguments \ '(--help --only)'--only'[Publish only the specific artifacts identified by the tags]' \ '(- *)'--help'[Show help and exit]' \ '*:tags:($(__fossil_tags))' ;; (pull) _arguments \ "(--help -B --httpauth)"{-B,--httpauth}'[USER:PASS Credentials for the simple HTTP auth protocol]:user pass:' \ "(--help --from-parent-project)"--from-parent-project'[Pull content from the parent project]' \ "(--help --ipv4)"--ipv4'[Use only IPv4, not IPv6]' \ "(--help --once)"--once'[Do not remember URL for subsequent syncs]' \ "(--help --private)"--private'[Pull private branches too]' \ "(--help --project-code)"--project-code'[CODE Use CODE as the project code]:project code:' \ "(--help --proxy)"--proxy'[PROXY Use the specified HTTP proxy]:proxy:' \ "(--help -R --repository)"{-R,--repository}'[REPO Local repository to pull into]:fossils:($(__fossil_repos))' \ "(--help --ssl-identity)"--ssl-identity'[FILE Local SSL credentials]:ssl credentials:' \ "(--help --ssh-command)"--ssh-command'[SSH Use SSH as the "ssh" command]:ssh command:' \ "(--help -v --verbose)"{-v,--verbose}'[Additional (debugging) output]' \ "(--help --verily)"--verily'[Exchange extra information with the remote]' \ '(- *)'--help'[Show help and exit]' \ '1:url:__fossil_urls' ;; (purge) __fossil_purge ;; (push) _arguments \ "(--help -B --httpauth)"{-B,--httpauth}'[USER:PASS Credentials for the simple HTTP auth protocol]:user pass:' \ "(--help --ipv4)"--ipv4'[Use only IPv4, not IPv6]' \ "(--help --once)"--once'[Do not remember URL for subsequent syncs]' \ "(--help --proxy)"--proxy'[PROXY Use the specified HTTP proxy]' \ "(--help --private)"--private'[Push private branches too]' \ "(--help -R --repository)"{-R,--repository}'[REPO Local repository to push from]:fossils:($(__fossil_repos))' \ "(--help --ssl-identity)"--ssl-identity'[FILE Local SSL credentials]:ssl credentials:' \ "(--help --ssh-command)"--ssh-command'[SSH Use SSH as the "ssh" command]:ssh command:' \ "(--help -v --verbose)"{-v,--verbose}'[Additional (debugging) output]' \ "(--help --verily)"--verily'[Exchange extra information with the remote]' \ '(- *)'--help'[Show help and exit]' \ '1::url:__fossil_urls' ;; (rebuild) _arguments \ ${_fossil_rebuild_options[@]} \ "(--help --force)"--force'[Force the rebuild to complete even if errors are seen]' \ "(--help --randomize)"--randomize'[Scan artifacts in a random order]' \ '(- *)'--help'[Show help and exit]' \ '1:fossils:($(__fossil_repos))' ;; (reconstruct) _arguments \ "(--help -K --keep-rid1)"{-K,--keep-rid1}'[Read the filename of the artifact with RID=1 from .rid]' \ "(--help -P --keep-private)"{-P,--keep-private}'[Mark the artifacts listed in .private as private]' \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' \ '2:directory:_files -/' ;; (redo|undo) _arguments \ "(--help -n --dry-run)"{-n,--dry-run}'[Do not make changes but show what would be done]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (remote|remote-url) __fossil_remote ;; (reparent) _arguments \ "(--help --test)"--test'[Make database entries but do not add the tag artifact]' \ "(--help -n --dryrun)"{-n,--dryrun}'[Do not actually change the database]' \ "(--help --date-override)"--date-override'[DATETIME Set the change time on the control artifact]:datetime:' \ "(--help --user-override)"--user-override'[USER Set the user name on the control artifact]:user:($(__fossil_users))' \ '(- *)'--help'[Show help and exit]' \ '1:check-in:' \ '*:parents:' ;; (revert) _arguments \ "(--help -r --revision)"{-r,--revision}'[VERSION Revert to given VERSION]:version:' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (rss) _arguments \ "(--help -type -y)"{-type,-y}'[FLAG]:type:(all ci t w)' \ "(--help -limit -n)"{-limit,-n}'[LIMIT The maximum number of items to show]:number:' \ "(--help -tkt)"-tkt'[HASH Filters for only those events for the specified ticket]:hash:' \ "(--help -tag)"-tag'[TAG Filters for a tag]:tag:($(__fossil_tags))' \ "(--help -wiki)"-wiki'[NAME Filters on a specific wiki page]:wiki page:($(__fossil_wiki_pages))' \ "(--help -name)"-name'[FILENAME filters for a specific file]:file:_files' \ "(--help -url)"-url'[STRING Sets the RSS feed'"'"'s root URL to the given string]:url:__fossil_urls' \ '(- *)'--help'[Show help and exit]' ;; (scrub) _arguments \ "(--help --force)"--force'[Do not prompt for confirmation]' \ "(--help --private)"--private'[Only private branches are removed from the repository]' \ "(--help --verily)"--verily'[Scrub real thoroughly (see above)]' \ '(- *)'--help'[Show help and exit]' \ '1::fossils:($(__fossil_repos))' ;; (search) _arguments \ "(--help -a --all)"{-a,--all}'[Output all matches, not just best matches]' \ "(--help -n --limit)"{-n,--limit}'[N Limit output to N matches]:number:' \ "(--help -W --width)"{-W,--width}'[WIDTH Set display width to WIDTH columns]:number:' \ '(- *)'--help'[Show help and exit]' \ '*:patterns:' ;; (server|ui) _arguments \ ${_fossil_server_options[@]} \ '1::fossils:($(__fossil_repos))' ;; (set|settings) _arguments \ "(--global)"--global'[Set or unset the given property globally]' \ "(--exact)"--exact'[Only consider exact name matches]' \ "1:setting:($(__fossil_settings))" \ '2:value:' ;; (sha1sum) _arguments \ "(--help -h --dereference)"{-h,--dereference}'[Resolve symbolic links]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (sha3sum) _arguments \ "(--help --224 --256 --384 --512 --size)"--224'[Compute a SHA3-224 hash]' \ "(--help --256 --224 --384 --512 --size)"--256'[Compute a SHA3-256 hash (the default)]' \ "(--help --384 --224 --256 --512 --size)"--384'[Compute a SHA3-384 hash]' \ "(--help --512 --224 --256 --384 --size)"--512'[Compute a SHA3-512 hash]' \ "(--help --size --224 --256 --384 --512 --size)"--size'[N An N-bit hash]:number:' \ "(--help -h --dereference)"{-h,--dereference}'[Resolve symbolic links]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (shell) _arguments \ '(- *)'--help'[Show help and exit]' ;; (smtpd) _arguments \ "(--help --dryrun)"--dryrun'[Do not record any emails in the database]' \ "(--help --trace)"--trace'[Print a transcript of the conversation on stderr]' \ "(--help --ipaddr)"--ipaddr'[ADDR The SMTP connection originates at ADDR]:address:' \ '(- *)'--help'[Show help and exit]' \ '1:fossils:($(__fossil_repos))' ;; (sql|sqlite3) _arguments \ "(--help --no-repository)"--no-repository'[Skip opening the repository database]' \ "(--help --readonly)"--readonly'[Open the repository read-only]' \ "(--help -R)"-R'[REPOSITORY Use REPOSITORY as the repository database]:fossils:($(__fossil_repos))' \ '(- *)'--help'[Show help and exit]' ;; (sqlar) _arguments \ "(--help -X --exclude)"{-X,--exclude}'[GLOBLIST Comma-separated list of GLOBs of files to exclude]:pattern:' \ "(--help --include)"--include'[GLOBLIST Comma-separated list of GLOBs of files to include]:pattern:' \ "(--help --name)"--name'[DIR The name of the top-level directory in the archive]:directory:_files -/' \ "(--help -R)"-R'[REPOSITORY Specify a Fossil repository]:fossils:($(__fossil_repos))' \ '(- *)'--help'[Show help and exit]' \ '1:version:' \ '2:output file:_files' ;; (stash) __fossil_stash ;; (sync) _arguments \ "(--help -B --httpauth)"{-B,--httpauth}'[USER:PASS Credentials for the simple HTTP auth protocol]:user pass:' \ "(--help --ipv3)"--ipv4'[Use only IPv4, not IPv6]' \ "(--help --once)"--once'[Do not remember URL for subsequent syncs]' \ "(--help --proxy)"--proxy'[PROXY Use the specified HTTP proxy]:proxy:' \ "(--help --private)"--private'[Sync private branches too]' \ "(--help -R --repository)"{-R,--repository}'[REPO Local repository to sync with]:fossils:($(__fossil_repos))' \ "(--help --ssl-identity)"--ssl-identity'[FILE Local SSL credentials]:ssl credentials:' \ "(--help --ssh-command)"--ssh-command'[SSH Use SSH as the "ssh" command]:ssh command:' \ "(--help -u --unversioned)"{-u,--unversioned}'[Also sync unversioned content]' \ "(--help -v --verbose)"{-v,--verbose}'[Additional (debugging) output]' \ "(--help --verily)"--verily'[Exchange extra information with the remote]' \ '(- *)'--help'[Show help and exit]' \ '1::url:__fossil_urls' ;; (tag) __fossil_tag ;; (tarball) _arguments \ "(--help -X --exclude)"{-X,--exclude}'[GLOBLIST Comma-separated list of GLOBs of files to exclude]:pattern:' \ "(--help --include)"--include'[GLOBLIST Comma-separated list of GLOBs of files to include]:pattern:' \ "(--help --name)"--name'[DIR The name of the top-level directory in the archive]:directory:_files -/' \ "(--help -R)"-R'[REPOSITORY Specify a Fossil repository]:fossils:($(__fossil_repos))' \ '(- *)'--help'[Show help and exit]' \ '1:version:' \ '2:ouput file:_files' ;; (ticket) __fossil_ticket ;; (timeline) _arguments \ "(--help -n --limit)"{-n,--limit}'[N Output the first N entries]:number:' \ "(--help -p --path)"{-p,--path}'[PATH Output items affecting PATH only]:path:_files' \ "(--help --offset)"--offset'[P skip P changes]:number:' \ "(--help --sql)"--sql'[Show the SQL used to generate the timeline]' \ "(--help -t --type)"{-t,--type}'[TYPE Output items from the given types only]:type:(ci e t w)' \ "(--help -v --verbose)"{-v,--verbose}'[Output the list of files changed by each commit]' \ "(--help -W --width)"{-W,--width}'[NUM Width of lines (default is to auto-detect)]:number:' \ "(--help -R)"-R'[REPO Specifies the repository db to use]:fossils:($(__fossil_repos))' \ '(- *)'--help'[Show help and exit]' \ '::when:(before after descendants children ancestors parents)' \ '::check-in:' \ '::datetime:' ;; (tls-config) __fossil_tls_config ;; (touch) _arguments \ "(--help --now -c --checkin -C --checkout)"--now'[Stamp each affected file with the current time]' \ "(--help -c --checkin --now -C --checkout)"{-c,--checkin}'[Stamp with the last modification time]' \ "(--help -C --checkout -c --checkin --now)"{-C,--checkout}'[Stamp with the time of the current checkout]' \ "(--help -g)"-g'[GLOBLIST Comma-separated list of glob patterns]:pattern:' \ "(--help -G)"-G'[GLOBFILE Like -g but reads globs from glob default file]:pattern:' \ "(--help -v -verbose)"{-v,-verbose}'[Outputs extra information about its globs]' \ "(--help -n --dry-run)"{-n,--dry-run}'[Do not touch anything]' \ "(--help -q --quiet)"{-q,--quiet}'[Suppress warnings]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (unpublished) _arguments \ "(--help --all)"--all'[Show all artifacts, not just check-ins]' \ '(- *)'--help'[Show help and exit]' ;; (unset) _arguments \ "(--global)"--global'[Set or unset the given property globally]' \ "(--exact)"--exact'[Only consider exact name matches]' \ "1:setting:($(__fossil_settings))" ;; (unversioned|uv) __fossil_unversioned ;; (update) _arguments \ "(--help --case-sensitive)"--case-sensitive'[BOOL Override case-sensitive setting]:bool:(yes no)' \ "(--help --debug)"--debug'[Print debug information on stdout]' \ "(--help --latest)"--latest'[Update to latest version]' \ "(--help --force-missing)"--force-missing'[Force update if missing content after sync]' \ "(--help -n --dry-run)"{-n,--dry-run}'[If given, display instead of run actions]' \ "(--help -v --verbose)"{-v,--verbose}'[Print status information about all files]' \ "(--help -W --width)"{-W,--width}'[WIDTH Width of lines (default is to auto-detect)]:number:' \ "(--help --setmtime)"--setmtime'[Set timestamps of all files to match their SCM-side times]' \ "(--help -K --keep-merge-files)"{-K,--keep-merge-files}'[On merge conflict, retain the temporary files]' \ '(- *)'--help'[Show help and exit]' \ '1::version:' \ '*::files:_files' ;; (user) __fossil_user ;; (version) _arguments \ "(--help -v --verbose)"{-v,--verbose}'[Additional details about optional features]' \ '(- *)'--help'[Show help and exit]' ;; (whatis) _arguments \ "(--help --type)"--type'[TYPE Only find artifacts of TYPE]:type:(ci t w g e)' \ "(--help -v --verbose)"{-v,--verbose}'[Provide extra information (such as the RID)]' \ '(- *)'--help'[Show help and exit]' \ '1:name:' ;; (wiki) __fossil_wiki ;; (zip) _arguments \ "(--help -X --exclude)"{-X,--exclude}'[GLOBLIST Comma-separated list of GLOBs of files to exclude]:pattern:' \ "(--help --include)"--include'[GLOBLIST Comma-separated list of GLOBs of files to include]:pattern:' \ "(--help --name)"--name'[DIR The name of the top-level directory in the archive]:directory:_files -/' \ "(--help -R)"-R'[REPOSITORY Specify a Fossil repository]:fossils:($(__fossil_repos))' \ '(- *)'--help'[Show help and exit]' \ '1:version:' \ '2:output file:_files' ;; esac ;; *) esac return 0 } ################################################################################ # Subcommands # ################################################################################ ######################################## # fossil alerts # ######################################## function __fossil_alerts_settings() { fossil alerts settings 2>/dev/null } function __fossil_alerts_subscribers() { fossil alerts subscribers 2>/dev/null } function __fossil_alerts() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(--help -R --repository)"{-R,--repository}'[FILE Run command on repository FILE]:fossils:($(__fossil_repos))' "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ pending reset send settings status subscribers \ test-message unsubscribe ;; (options) case $line[1] in (pending|reset) _arguments \ ${_common_options[@]} ;; (send) _arguments \ ${_common_options[@]} \ "(--help --digest)"--digest'[Send digests]' \ "(--help --test)"--test'[Write to standard output]' ;; (set|settings) _arguments \ ${_common_options[@]} \ '1::name:($(__fossil_alerts_settings))' \ '2::value:' ;; (subscribers) _arguments \ ${_common_options[@]} \ '1::pattern:' ;; (test-message) _arguments \ ${_common_options[@]} \ "(--help --body)"--body'[FILENAME]:file:_files' \ "(--help --smtp-trace)"--smtp-trace'[]' \ "(--help --stdout)"--stdout'[]' \ "(--help --subject -S)"{--subject,-S}'[SUBJECT]:subject:' \ '1:recipient:' ;; unsubscribe) _arguments \ ${_common_options[@]} \ '1:email:($(__fossil_alerts_subscribers))' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil all # ######################################## function __fossil_all() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' "(--help --showfile)"--showfile'[Show the repository or checkout being operated upon]' "(--help --dontstop)"--dontstop'[Continue with other repositories even after an error]' "(--help --dry-run)"--dry-run'[If given, display instead of run actions]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ backup cache changes clean config dbstat extras fts-config info \ pull push rebuild sync set unset server ui add ignore list ls ;; (options) case $line[1] in (backup) _arguments \ ${_common_options[@]} \ '1:output directory:_files -/' ;; (cache) __fossil_cache ;; (clean) _arguments \ ${_common_options[@]} \ ${_fossil_clean_options[@]} \ "(--help --whatif)"--whatif'[Review the files to be deleted]' ;; (config) _arguments \ ${_common_options[@]} \ '1:what:(pull)' \ '2:area:' ;; (dbstat) _arguments \ ${_fossil_dbstat_options[@]} ;; (extras) _arguments \ ${_fossil_extras_options[@]} ;; (fts-config) __fossil_fts_config ;; (pull|push) _arguments \ ${_common_options[@]} \ "(--help -v --verbose)"{-v,--verbose}'[Additional (debugging) output]' ;; (rebuild) _arguments \ ${_common_options[@]} \ ${_fossil_rebuild_options[@]} ;; (sync) _arguments \ ${_common_options[@]} \ "(--help -u --unversioned)"{-u,--unversioned}'[Also sync unversioned content]' \ "(--help -v --verbose)"{-v,--verbose}'[Additional (debugging) output]' ;; (set|unset) _arguments \ ${_common_options[@]} \ "1:setting:($(__fossil_settings))" \ '2:value:' \ "(--help --global)"--global'[Set or unset the given property globally]' \ "(--help --exact)"--exact'[Only consider exact name matches]' ;; (server|ui) _arguments \ ${_common_options[@]} \ ${_fossil_server_options[@]} ;; (add) _arguments \ ${_common_options[@]} \ '*:fossils:($(__fossil_repos))' ;; (ignore) _arguments \ ${_common_options[@]} \ "(--help -c --ckout)"{-c,--ckout}'[Ignore local checkouts instead of repositories]' \ '*:fossils:($(__fossil_repos))' ;; (list|ls) _arguments \ "(-)"--help'[Show help and exit]' \ "(--help -c --ckout)"{-c,--ckout}'[List local checkouts instead of repositories]' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil bisect # ######################################## function __fossil_bisect() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ bad good log chart next options reset skip vlist ls status ui undo ;; (options) case $line[1] in (bad|good|skip) _arguments \ '1:version:' ;; (options) _arguments \ "1::name:($(fossil bisect options 2>/dev/null | awk '{print $1}'))" \ '2::value:' ;; (vlist|ls|status) _arguments \ "(-a --all)"{-a,--all}'[List all]' ;; (ui) _arguments \ ${_fossil_server_options[@]} ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil branch # ######################################## function __fossil_branch() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' "(--help -R --repository)"{-R,--repository}'[FILE Run commands on repository FILE]:fossils:($(__fossil_repos))' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ current info list ls new ;; (options) case $line[1] in (current) _arguments \ ${_common_options[@]} ;; (info) _arguments \ ${_common_options[@]} \ ':branch:($(__fossil_branches))' ;; (list|ls) _arguments \ ${_common_options[@]} \ "(--help -a --all)"{-a,--all}'[List all branches]' \ "(--help -c --closed)"{-c,--closed}'[List closed branches]' \ "(--help -r)"-r'[Reverse the sort order]' \ "(--help -t)"-t'[Show recently changed branches first]' ;; (new) _arguments \ ${_common_options[@]} \ "(--help --private)"--private'[Branch is private]' \ "(--help --bgcolor)"--bgcolor'[COLOR Use COLOR instead of automatic background]:color:' \ "(--help --nosign)"--nosign'[Do not sign contents on this branch]' \ "(--help --date-override)"--date-override'[DATE Use DATE instead of '"'"'now'"'"']:date:(now)' \ "(--help --user-override)"--user-override'[USER Use USER instead of the default]:user:($(__fossil_users))' \ '1:branch name:' \ '2:base check-in:' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil bundle # ######################################## function __fossil_bundle() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ append cat export extend import ls purge ;; (options) case $line[1] in (append) _arguments \ '1:bundle:_files' \ '*:files:_files' ;; (bundle) _arguments \ '1:what:(cat)' \ '2:bundle:_files' \ '*:hashes:' ;; (export) _arguments \ "(--branch)"--branch'[BRANCH Package all check-ins on BRANCH]:branch:($(__fossil_branches))' \ "(--from)"--from'[TAG1 --to TAG2 Package check-ins starting with TAG1]:tag:($(__fossil_tags))' \ "(--to)"--to'[TAG2 Package check-ins up to TAG2]:tag:($(__fossil_tags))' \ "(--checkin)"--checkin'[TAG Package the single check-in TAG]:tag:($(__fossil_tags))' \ "(--standalone)"--standalone'[Do no use delta-encoding against]' \ '1:bundle:_files' ;; (extend|ls|purge) _arguments \ '1:bundle:_files' ;; (import) _arguments \ "(--publish)"--publish'[Make the import public]' \ "(--force)"--force'[Import even if project codes do not match]' \ '1:bundle:_files' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil cache # ######################################## function __fossil_cache_subcommand { } function __fossil_cache() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ clear init list ls status ;; *) _message 'no more arguments' ;; esac return 0 } ######################################## # fossil configuration # ######################################## function __fossil_configuration() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(--help -R --repository)"{-R,--repository}'[FILE Extract info from repository FILE]:fossils:($(__fossil_repos))' "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:method:->method' \ '*::options:->options' case $state in (method) _values 'method' \ export import merge pull push reset sync ;; (options) case $line[1] in (export) _arguments \ ${_common_options[@]} \ '1:area:__fossil_areas' \ '2:file:_files' ;; (import|merge) _arguments \ ${_common_options[@]} \ '1:file:_files' ;; (pull) _arguments \ ${_common_options[@]} \ "(--help --overwrite)"--overwrite'[Replace local settings]' \ '1:area:__fossil_areas' \ '2::url:__fossil_urls' ;; (push|sync) _arguments \ ${_common_options[@]} \ '1:area:__fossil_areas' \ '2::url:__fossil_urls' ;; (reset) _arguments \ ${_common_options[@]} \ '1:area:__fossil_areas' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil fts-config # ######################################## function __fossil_fts_config() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ reindex index enable disable stemmer ;; (options) case $line[1] in index|stemmer) _arguments \ "(- *):setting:(on off)" ;; enable|disable) _arguments \ "(- *):setting:(cdtwe)" ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil git # ######################################## function __fossil_git() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in subcommand) _values 'subcommand' \ export import status ;; options) case $line[1] in (export) _arguments \ ${_common_options[@]} \ "(--help --autopush)"--autopush'[URL Automatically do a '"'"'git push'"'"' to URL]:url:__fossil_urls' \ "(--help --debug)"--debug'[FILE Write fast-export text to FILE]:file:_files' \ "(--help --force -f)"{--force,-f}'[Do the export even if nothing has changed]' \ "(--help --limit)"--limit'[N Add no more than N new check-ins to MIRROR]:number:' \ "()*"{--quiet,-q}'[Reduce output. Repeat for even less output]' \ "(--verbose -v)"{--verbose,-v}'[More output]' ;; (import) _arguments \ ${_common_options[@]} \ ':url:__fossil_urls' ;; (status) _arguments \ ${_common_options[@]} ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil hook # ######################################## function __fossil_hook() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ add delete edit list status test ;; (options) case $line[1] in (add) _arguments \ ${_common_options[@]} \ "(--help --command)"--command'[COMMAND]:command:' \ "(--help --type)"--type'[TYPE]:type:' \ "(--help --sequence)"--sequence'[NUM]:number:' ;; (delete) _arguments \ ${_common_options[@]} \ '*:IDs:' ;; (edit) _arguments \ ${_common_options[@]} \ "(--help --command)"--command'[COMMAND]:command:' \ "(--help --type)"--type'[TYPE]:type:' \ "(--help --sequence)"--sequence'[NUM]:number:' \ '*:IDs:' ;; (list|status) _arguments \ ${_common_options[@]} \ ;; (test) _arguments \ ${_common_options[@]} \ "(--help --dry-run)"--dry-run'[Print the script on stdout rather than run it]' \ "(--help --base-rcvid)"--base-rcvid'[N Pretend that the hook-last-rcvid value is N]:number:' \ "(--help --new-rcvid)"--new-rcvid'[M Pretend that the last rcvid valud is M]:number:' \ "(--help --aux-file)"--aux-file'[NAME NAME is substituted for %A in the script]:name:' \ '1:ID:' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil json # ######################################## function __fossil_json_subcommands() { _values 'subcommand' \ anonymousPassword artifact branch cap config diff dir g login logout \ query rebuild report resultCodes stat tag timeline user version \ whoami wiki } function __fossil_json() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' "(--help -R --repository)"{-R,--repository}'[FILE Run commands on repository FILE]:fossils:($(__fossil_repos))' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _arguments \ '(- *)'-json-input'[FILE Read JSON data from FILE]:file:_files' \ '1:subcommand:__fossil_json_subcommands' ;; (options) case $line[1] in -json-input) _arguments \ ${_common_options[@]} \ '1:file:_files' ;; *) _message 'no more arguments' ;; esac ;; *) esac return 0 } ######################################## # fossil login-group # ######################################## function __fossil_login_group() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ join leave ;; (options) case $line[1] in (join) _arguments \ "(-name)"-name'[Specified the name of the login group]:name:' \ ':fossils:($(__fossil_repos))' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil purge # ######################################## function __fossil_purge() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(--help --explain --dry-run)"--explain'[Make no changes, but show what would happen]' "(--help --dry-run --explain)"--dry-run'[An alias for --explain]' "(-)"--help'[Show help and exit]' ) _arguments -C \ "(-)"--help'[Show help and exit]' \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ artifacts cat checkins files list ls obliterate tickets undo wiki ;; (options) case $line[1] in (artifacts|cat) _arguments \ ${_common_options[@]} \ '*:hashes:' ;; (checkins) _arguments \ ${_common_options[@]} \ '*:tags:($(__fossil_tags))' ;; (files) _arguments \ ${_common_options[@]} \ '*:files:_files' ;; (list|ls) _arguments \ ${_common_options[@]} \ "(--help -l)"-l'[Provide more details]' ;; (obliterate) _arguments \ ${_common_options[@]} \ "(--help --force)"--force'[Suppress confirmation prompt]' \ '*:IDs:' ;; (tickets) _arguments \ ${_common_options[@]} \ '*:ticket names:' ;; (undo) _arguments \ ${_common_options[@]} \ '1:ID:' ;; (wiki) _arguments \ ${_common_options[@]} \ '*:wiki pages:($(__fossil_wiki_pages))' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil remote # ######################################## function __fossil_remote() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ add delete list off ;; (options) case $line[1] in (add) _arguments \ '1:name:' \ '2:url:__fossil_urls' ;; (delete) _arguments \ '1:name:($(__fossil_remotes))' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil stash # ######################################## function __fossil_stash_ids() { fossil stash ls | grep '^\s*\d\+:' | awk -F ':' '{print $1}' | sed 's/^ *//' } function __fossil_stash() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ save snapshot list ls show gshow pop apply goto drop diff gdiff ;; (options) case $line[1] in (save|snapshot) _arguments \ "(-m --comment)"{-m,--comment}'[MSG Add comment]:comment:' \ '*:files:_files' ;; (list|ls) _arguments \ "(-v --verbose)"{-v,--verbose}'[Show info about individual files]' \ "(-W --width)"{-W,--width}'[N Set width]:number:' ;; (show|cat|diff|gdiff) _arguments \ ${_fossil_diff_options[@]} \ '1::stash ID:($(__fossil_stash_ids))' ;; (apply|goto) _arguments \ '1::stash ID:($(__fossil_stash_ids))' ;; (drop|rm) _arguments \ "(-a --all)"{-a,--all}'[Forget the whole stash (CANNOT UNDO!)]' \ '1::stash ID:($(__fossil_stash_ids))' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil tag # ######################################## function __fossil_tag() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ add cancel find list ls ;; (options) case $line[1] in (add) _arguments \ ${_common_options[@]} \ "(--help --raw)"--raw'[Raw tag name]' \ "(--help --propagate)"--propagate'[Propagating tag]' \ "(--help --date-override)"--date-override'[DATETIME Set date and time added]:datetime:' \ "(--help --user-override)"--user-override'[USER Name USER when adding the tag]:user:($(__fossil_users))' \ "(--help --dryrun -n)"{--dryrun,-n}'[Display the tag text, but do not]' \ '1:tag name:($(__fossil_tags))' \ '2:check-in:' \ '3::value:' ;; (cancel) _arguments \ ${_common_options[@]} \ "(--help --raw)"--raw'[Raw tag name]' \ "(--help --date-override)"--date-override'[DATETIME Set date and time deleted]:datetime:' \ "(--help --user-override)"--user-override'[USER Name USER when deleting the tag]:user:($(__fossil_users))' \ "(--help --dryrun -n)"{--dryrun,-n}'[Display the control artifact, but do]' \ '1:tag name:($(__fossil_tags))' \ '2:check-in:' ;; (find) _arguments \ ${_common_options[@]} \ "(--help --raw)"--raw'[Raw tag name]' \ "(--help -t --type)"{-t,--type}'[TYPE One of "ci", or "e"]:(ci e)' \ "(--help -n --limit)"{-n,--limit}'[N Limit to N results]:number:' \ '1:tag name:($(__fossil_tags))' ;; (list|ls) _arguments \ ${_common_options[@]} \ "(--help --raw)"--raw'[List tags raw names of tags]' \ "(--help --tagtype)"--tagtype'[TYPE List only tags of type TYPE]:tag type:(ci e)' \ "(--help -v --inverse)"{-v,--inverse}'[Inverse the meaning of --tagtype TYPE]' \ '1::check-in:' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil ticket # ######################################## function __fossil_ticket() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ show list ls set change add history ;; (options) case $line[1] in (show) _arguments \ ${_common_options[@]} \ "(--help -l --limit)"{-l,--limit}'[LIMITCHAR]:limit:' \ "(--help -q --quote)"{-q,--quote}'[]' \ "(--help -R --repository)"{-R,--repository}'[FILE]:fossils:($(__fossil_repos))' \ '1:report title/nr:' \ '2::ticket filter:' ;; (list|ls) _arguments \ ':what:(fields reports)' ;; (set|change) _arguments \ ${_common_options[@]} \ "(--help -q --quote)"{-q,--quote}'[]' \ '1:ticket UUID:' \ '*:field/value:' ;; (add) _arguments \ ${_common_options[@]} \ "(--help -q --quote)"{-q,--quote}'[]' \ '*:field/value:' ;; (history) _arguments \ ':ticket UUID:' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil tls-config # ######################################## function __fossil_tls_config() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ show remove-exception ;; (options) case $line[1] in (remove-exception) _arguments \ '*:domains:' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil unversioned/uv # ######################################## function __fossil_unversioned() { local curcontext="$curcontext" state line typeset -A opt_args local -a _common_options _common_options=( "(--help -R --repository)"{-R,--repository}'[FILE Use FILE as the repository]:fossils:($(__fossil_repos))' "(--help --mtime)"--mtime'[TIMESTAMP Use TIMESTAMP instead of "now"]:timestamp:' "(-)"--help'[Show help and exit]' ) _arguments -C \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ add cat edit export list ls revert remove rm delete sync touch ;; (options) case $line[1] in (add) _arguments \ ${_common_options[@]} \ "(--help --as)"--as'[UVFILE]:file:_files' \ '*:files:_files' ;; (cat) _arguments \ ${_common_options[@]} \ '*:files:_files' ;; (edit) _arguments \ ${_common_options[@]} \ '1:file:_files' ;; (export) _arguments \ ${_common_options[@]} \ '1:file:_files' \ '2:output file:_files' ;; (list|ls) _arguments \ ${_common_options[@]} \ "(--help --glob)"--glob'[PATTERN Show only files that match]:pattern:' \ "(--help --like)"--like'[PATTERN Show only files that match]:pattern:' ;; (revert) _arguments \ ${_common_options[@]} \ "(--help -v --verbose)"{-v,--verbose}'[Extra diagnostic output]' \ "(--help -n --dryrun)"{-n,--dryrun}'[Show what would have happened]' \ '1::url:__fossil_urls' ;; (remove|rm|delete) _arguments \ ${_common_options[@]} \ "(--help --glob)"--glob'[PATTERN Remove files that match]:pattern:' \ "(--help --like)"--like'[PATTERN Remove files that match]:pattern:' \ '*:files:_files' ;; (sync) _arguments \ ${_common_options[@]} \ "(--help -v --verbose)"{-v,--verbose}'[Extra diagnostic output]' \ "(--help -n --dryrun)"{-n,--dryrun}'[Show what would have happened]' \ '1::url:__fossil_urls' ;; (touch) _arguments \ ${_common_options[@]} \ '*:files:_files' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil user # ######################################## function __fossil_user() { local curcontext="$curcontext" state line typeset -a opt_args local -a _common_options _common_options=( "(--help -R --repository)"{-R,--repository}'[FILE Apply command to repository FILE]:fossils:($(__fossil_repos))' "(-)"--help'[show help and exit]' ) _arguments -c \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ capabilities default list ls new password ;; (options) case $line[1] in (capabilities) _arguments \ ${_common_options[@]} \ '1:user:($(__fossil_users))' \ '2::string:' ;; (default) _arguments \ ${_common_options[@]} \ '1::user:($(__fossil_users))' ;; (list|ls) _arguments \ ${_common_options[@]} ;; (new) _arguments \ ${_common_options[@]} \ '1::username:' \ '2::contact info:' \ '3::password:' ;; (password) _arguments \ ${_common_options[@]} \ '1:user:($(__fossil_users))' \ '2::password:' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil wiki # ######################################## function __fossil_wiki() { local curcontext="$curcontext" state line typeset -a opt_args local -a _common_options _common_options=( "(-)"--help'[show help and exit]' ) _arguments -c \ ${_common_options[@]} \ '1:subcommand:->subcommand' \ '*::options:->options' case $state in (subcommand) _values 'subcommand' \ export create commit list ;; (options) case $line[1] in (export) _arguments \ ${_common_options[@]} \ "(--help --technote -t)"{--technote,-t}'[DATETIME|TECHNOTE-ID Export a technote]:datetime/technote-id:(now)' \ "(--help -h --html -H --HTML)"--html'[Render only HTML body]' \ "(--help -H --HTML -h --html)"--HTML'[Like -h|-html but wraps the output in <html>/</html>]' \ "(--help -p --pre)"{-p,--pre}'[Wrap into <pre>...</pre>]' \ '::pagename:($(__fossil_wiki_pages))' \ '::file:_files' ;; (create|commit) _arguments \ ${_common_options[@]} \ "(--help -M --mimetype)"{-M,--mimetype}'[TEXT-FORMAT The mime type of the update]:mimetype:' \ "(--help --technote -t)"{--technote,-t}'[DATETIME|TECHNOTE-ID]:datetime/technote-id:(now)' \ "(--help --technote-tags)"--technote-tags'[TAGS The set of tags for a technote]:tags:' \ "(--help --technote-bgcolor)"--technote-bgcolor'[COLOR The color used for the technote]:color:' \ '1:pagename:($(__fossil_wiki_pages))' \ '2::file:_files' ;; (list|ls) _arguments \ ${_common_options[@]} \ "(--help -t --technote)"{-t,--technote}'[List technotes]' \ "(--help -s --show-technote-ids)"{-s,--show-technote-ids}'[The id of the tech note will be listed]' ;; *) _message 'no more arguments' ;; esac ;; esac return 0 } ######################################## # fossil test commands # ######################################## function __fossil_complete_test_commands() { case $line[1] in (test-add-alerts) _arguments \ "(--help --backoffice)"--backoffice'[Run alert_backoffice() after all alerts have been added]' \ "(--help --debug)"--debug'[Like --backoffice, but print to stdout]' \ "(--help --digest)"--digest'[Process emails using SENDALERT_DIGEST]' \ '(- *)'--help'[Show help and exit]' \ '*:event IDs:' ;; (test-agg-cksum) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-alert) _arguments \ "(--help --digest)"--digest'[Generate digest alert text]' \ "(--help --needmod)"--needmod'[Assume all events are pending moderator approval]' \ '(- *)'--help'[Show help and exit]' \ '*:event IDs:' ;; (test-all-help) _arguments \ "(--help -e --everything)"{-e,--everything}'[Show all commands and pages]' \ "(--help -t --test)"{-t,--test}'[Include test- commands]' \ "(--help -w --www)"{-w,--www}'[Show WWW pages]' \ "(--help -s --settings)"{-s,--settings}'[Show settings]' \ "(--help -h --html)"{-h,--html}'[Transform output to HTML]' \ "(--help -r --raw)"{-r,--raw}'[No output formatting]' \ '(- *)'--help'[Show help and exit]' ;; (test-ambiguous) _arguments \ "(--help --minsize)"--minsize'[N Show hases with N characters or more]:number:' \ '(- *)'--help'[Show help and exit]' ;; (test-ancestor-path) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:version 1:' \ '2:version 2:' ;; (test-approx-match) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-backlinks) _arguments \ "(--help --mtime)"--mtime'[DATETIME Use an alternative date/time]:datetime:' \ "(--help --mimetype)"--mimetype'[TYPE Use an alternative mimetype]:mimetype:' \ '(- *)'--help'[Show help and exit]' \ '1:srctype:' \ '2:srcid:' \ '3:input file:_files' ;; (test-backoffice-lease) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-builtin-get) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:name:' \ '2::output file:_files' ;; (test-builtin-list) _arguments \ "(--help --verbose)"--verbose'[Output total item count and size]' \ '(- *)'--help'[Show help and exit]' ;; (test-canonical-name) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-captcha) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:numbers:' ;; (test-ci-mini) _arguments \ "(--help --repository -R)"{--repository,-R}'[REPO The repository file to commit to]:fossils:($(__fossil_repos))' \ "(--help --as)"--as'[FILENAME The repository-side name of the input file]:file:_files' \ "(--help --comment -m)"{--comment,-m}'[COMMENT Required checkin comment]:comment:' \ "(--help --comment-file -M)"{--comment-file,-M}'[FILE Reads checkin comment from the given file]:file:_files' \ "(--help --revision -r)"{--revision,-r}'[VERSION Commit from this version]:version:' \ "(--help --allow-fork)"--allow-fork'[Allow commit against a non-leaf parent]' \ "(--help --allow-merge-conflict)"--allow-merge-conflict'[Allow checkin of a file with conflict markers]' \ "(--help --user-override)"--user-override'[USER User to use instead of the default]:user:($(__fossil_users))' \ "(--help --date-override)"--date-override'[DATETIME Date to use instead of '"'"'now'"'"']:datetime:(now)' \ "(--help --allow-older)"--allow-older'[Allow a commit to be older than its ancestor]' \ "(--help --convert-eol-inherit)"--convert-eol-inherit'[Inherit EOL style from previous content]' \ "(--help --convert-eol-unix)"--convert-eol-unix'[Convert the EOL style to Unix]' \ "(--help --convert-eol-windows)"--convert-eol-windows'[Convert the EOL style to Windows]' \ "(--help --delta)"--delta'[Prefer to generate a delta manifest]' \ "(--help --allow-new-file)"--allow-new-file'[Allow addition of a new file this way]' \ "(--help --dump-manifest -d)"{--dump-manifest,-d}'[Dumps the generated manifest to stdout]' \ "(--help --save-manifest)"--save-manifest'[FILE Saves the generated manifest to a file]:file:_files' \ "(--help --wet-run)"--wet-run'[Disables the default dry-run mode]' \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (test-clusters) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-command-stats) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-comment-format) _arguments \ "(--help --file)"--file'[The comment text is really just a file name to read it from]' \ "(--help --decode)"--decode'[Decode the text using the same method used for a C-card from a manifest]' \ "(--help --legacy)"--legacy'[Use the legacy comment printing algorithm]' \ "(--help --trimcrlf)"--trimcrlf'[Enable trimming of leading/trailing CR/LF]' \ "(--help --trimspace)"--trimspace'[Enable trimming of leading/trailing spaces]' \ "(--help --wordbreak)"--wordbreak'[Attempt to break lines on word boundaries]' \ "(--help --origbreak)"--origbreak'[Attempt to break when the original comment text is detected]' \ "(--help --indent)"--indent'[NUM Number of spaces to indent]:number:' \ "(--help -W --width)"{-W,--width}'[NUM Width of lines]:number:' \ '(- *)'--help'[Show help and exit]' \ '1:prefix:' \ '2:text:' \ '3::origtext:' ;; (test-commit-warning) _arguments \ "(--help --no-settings)"--no-settings'[Do not consider any glob settings]' \ "(--help -v --verbose)"{-v,--verbose}'[Show per-file results for all pre-commit checks]' \ '(- *)'--help'[Show help and exit]' ;; (test-compress) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:input file:_files' \ '2:output file:_files' ;; (test-compress-2) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:input file 1:_files' \ '2:input file 2:_files' \ '3:output file:_files' ;; (test-contains-selector) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' \ '2:css selector:' ;; (test-content-deltify) _arguments \ "(--help --force)"--force'[]' \ '(- *)'--help'[Show help and exit]' \ '1:rid:' \ '*:src id:' ;; (test-content-erase) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:rid:' ;; (test-content-put) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (test-content-rawget) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-content-undelta) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:record id:' ;; (test-convert-stext) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' \ '2:mimetype:' ;; (test-create-clusters) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-crosslink) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:record id:' ;; (test-cycle-compress) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-database-names) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-date-format) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:date string:' ;; (test-db-exec-error) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-decode-email) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (test-decode64) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:string:' ;; (test-delta) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' \ '2:file:_files' ;; (test-delta-analyze) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' \ '2:file:_files' ;; (test-delta-apply) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' \ '2:delta:_files' ;; (test-delta-create) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' \ '2:file:_files' \ '3:delta:_files' ;; (test-describe-artifacts) _arguments \ "(--help --from)"--from'[S An artifact]:artifact:' \ "(--help --count)"--count'[N Number of artifacts]:number:' \ '(- *)'--help'[Show help and exit]' ;; (test-detach) _arguments \ '(- *)'--help'[Show help and exit]' \ '1::fossils:($(__fossil_repos))' ;; (test-diff) _arguments \ ${_fossil_diff_options[@]} \ '1:file 1:_files' \ '2:file 2:_files' ;; (test-dir-size) _arguments \ "(--help --nodots)"--nodots'[Omit files that begin with '"'"'.'"'"']' \ '(- *)'--help'[Show help and exit]' \ '1:directory:_files -/' \ '2::glob pattern:' ;; (test-echo) _arguments \ "(--help --hex)"--hex'[Show the output as hexadecimal]' \ '(- *)'--help'[Show help and exit]' \ '*:args:' ;; (test-emailblob-refcheck) _arguments \ "(--help --repair)"--repair'[Fix up the enref field]' \ "(--help --full)"--full'[Give a full report]' \ "(--help --clean)"--clean'[Used with --repair, removes entries with enref==0]' \ '(- *)'--help'[Show help and exit]' ;; (test-encode64) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:string:' ;; (test-escaped-arg) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:args:' ;; (test-etag) _arguments \ "(--help --key)"--key'[KEYNUM]:key number:' \ "(--help --hash)"--hash'[HASH]:hash:' \ '(- *)'--help'[Show help and exit]' ;; (test-file-copy) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:source:_files' \ '2:destination:_files' ;; (test-file-environment) _arguments \ "(--help --allow-symlinks)"--allow-symlinks'[BOOL Temporarily turn allow-symlinks on/off]:bool:(yes no)' \ "(--help --open-config)"--open-config'[Open the configuration database first]' \ "(--help --slash)"--slash'[Trailing slashes, if any, are retaine]' \ "(--help --reset)"--reset'[Reset cached stat() info for each file]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-fileage) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:checkin:' ;; (test-filezip) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-find-mx) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:domain:' ;; (test-find-pivot) _arguments \ "(--help --ignore-merges)"--ignore-merges'[Ignore merges for discovering name pivots]' \ '(- *)'--help'[Show help and exit]' \ '*:args:' ;; (test-fingerprint) _arguments \ '(- *)'--help'[Show help and exit]' \ '1::rcvid:' ;; (test-forumthread) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:thread id:' ;; (test-fossil-system) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-fuzz) _arguments \ "(--help --type)"--type'[TYPE]:type:(wiki markdown artifact)' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-glob) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:pattern:' \ '2:string:' ;; (test-grep) _arguments \ "(--help -i --ignore-case)"{-i,--ignore-case}'[Ignore case]' \ '(- *)'--help'[Show help and exit]' \ '1:regexp:' \ '*:files:_files' ;; (test-gzip) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (test-hash-color) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:tags:($(__fossil_tags))' ;; (test-hash-passwords) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:fossils:($(__fossil_repos))' ;; (test-html-tidy) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-html-to-text) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-html-tokenize) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-http) _arguments \ "(--help --th-trace)"--th-trace'[Trace TH1 execution (for debugging purposes)]' \ "(--help --usercap)"--usercap'[CAP user capability string]:capability string:' \ '(- *)'--help'[Show help and exit]' ;; (test-httpmsg) _arguments \ "(--help --compress)"--compress'[Use ZLIB compression on the payload]' \ "(--help --mimetype)"--mimetype'[TYPE Mimetype of the payload]:mimetype:' \ "(--help --out)"--out'[FILE Store the reply in FILE]:file:_files' \ "(--help -v)"-v'[Verbose output]' \ '(- *)'--help'[Show help and exit]' \ '1:url:__fossil_urls' \ '2::payload:' ;; (test-integrity) _arguments \ "(--help -d --db-only)"{-d,--db-only}'[Run "PRAGMA integrity_check" on the database only]' \ "(--help --parse)"--parse'[Parse all manifests, wikis, tickets, events, etc]' \ "(--help -q --quick)"{-q,--quick}'[Run "PRAGMA quick_check" on the database only]' \ '(- *)'--help'[Show help and exit]' ;; (test-is-reserved-name|test-is-ckout-db) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-ishuman) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-isspace) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-leaf-ambiguity) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:names:' ;; (test-list-page) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:directory:_files -/' ;; (test-list-webpage) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-loadavg) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-looks-like-utf) _arguments \ "(--help -n --limit)"{-n,--limit}'[NUM Repeat looks-like function NUM times]:number:' \ "(--help --utf8)"--utf8'[Ignoring BOM and file size, force UTF-8 checking]' \ "(--help --utf16)"--utf16'[Ignoring BOM and file size, force UTF-16 checking]' \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (test-mailbox-hashname) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:human name:' ;; (test-markdown-render) _arguments \ "(--help --safe)"--safe'[Restrict the output to use only "safe" HTML]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-match) _arguments \ "(--help --begin)"--begin'[TEXT Text to insert before each match]:text:' \ "(--help --end)"--end'[TEXT Text to insert after each match]:text:' \ "(--help --gap)"--gap'[TEXT Text to indicate elided content]:text:' \ "(--help --html)"--html'[Input is HTML]' \ "(--help --static)"--static'[Use the static Search object]' \ '(- *)'--help'[Show help and exit]' \ '1:search string:' \ '*:files:_files' ;; (test-mimetype) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-missing) _arguments \ "(--help --notshunned)"--notshunned'[Do not report shunned artifacts]' \ "(--help --quiet)"--quiet'[Only show output if there are errors]' \ '(- *)'--help'[Show help and exit]' ;; (test-move-repository) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:pathname:_files' ;; (test-name-changes) _arguments \ "(--help --debug)"--debug'[Enable debugging]' \ '(- *)'--help'[Show help and exit]' \ '1:version 1:' \ '2:version 2:' ;; (test-name-to-id) _arguments \ "(--help --count)"--count'[N Repeat the conversion N times]:number:' \ '(- *)'--help'[Show help and exit]' \ '*:name:' ;; (test-obscure) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:args:' ;; (test-orphans) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-parse-all-blobs) _arguments \ "(--help --limit)"--limit'[N Parse no more than N blobs]:number:' \ '(- *)'--help'[Show help and exit]' ;; (test-parse-manifest) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' \ '2::n:' ;; (test-phantoms) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-process-id) _arguments \ "(--help --sleep)"--sleep'[N Sleep for N seconds before exiting]:number:' \ '(- *)'--help'[Show help and exit]' \ '*:process id:' ;; (test-prompt-password) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:prompt:' \ '2:verify:(0 1 2)' ;; (test-prompt-user) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:prompt:' ;; (test-random-password) _arguments \ '(- *)'--help'[Show help and exit]' \ '1::length:' ;; (test-rawdiff) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file 1:_files' \ '2:file 2:_files' ;; (test-relative-name) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-reserved-names) _arguments \ "(--help -omitrepo)"-omitrepo'[]' \ '(- *)'--help'[Show help and exit]' ;; (test-safe-html) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-sanitize-name) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:args:' ;; (test-search-stext) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:type:(c d e t w)' \ '2:rid:' \ '3:name:' ;; (test-set-mtime) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' \ '2:date/time:' ;; (test-shortest-path) _arguments \ "(--help --no-merge)"--no-merge'[Follow only direct parent-child paths]' \ '(- *)'--help'[Show help and exit]' \ '1:version 1:' \ '2:version 2:' ;; (test-simplify-name) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-smtp-probe) _arguments \ "(--help --direct)"--direct'[Use DOMAIN directly without going through MX]' \ "(--help --port)"--port'[N Talk on TCP port N]:number:' \ '(- *)'--help'[Show help and exit]' \ '1:domain:' \ '2::me:' ;; (test-smtp-send) _arguments \ "(--help --direct)"--direct'[Bypass MX lookup]' \ "(--help --relayhost)"--relayhost'[HOST Use HOST as relay for delivery]:host:' \ "(--help --port)"--port'[Use TCP port N instead of 25]:number:' \ "(--help --trace)"--trace'[Show the SMTP conversation on the console]' \ '(- *)'--help'[Show help and exit]' \ '1:email:_files' \ '2:from:' \ '*:to:' ;; (test-smtp-senddata) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (test-subtree) _arguments \ "(--help --branch)"--branch'[BRANCH Include only check-ins on BRANCH]:branch:($(__fossil_branches))' \ "(--help --from)"--from'[TAG Start the subtree at TAG]:tag:($(__fossil_tags))' \ "(--help --to)"--to'[TAG End the subtree at TAG]:tag:($(__fossil_tags))' \ "(--help --checkin)"--checkin'[TAG The subtree is the single check-in TAG]:tag:($(__fossil_tags))' \ "(--help --all)"--all'[Include FILE and TAG artifacts]' \ "(--help --exclusive)"--exclusive'[Include FILES exclusively on check-ins]' \ '(- *)'--help'[Show help and exit]' ;; (test-tag) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:tag:($(__fossil_tags))' \ '2:artifact id:' \ '3::value:' ;; (test-tarball) _arguments \ "(--help -h --dereference)"{-h,--dereference}'[Follow symlinks; archive the files they point to]' \ '(- *)'--help'[Show help and exit]' ;; (test-tempname) _arguments \ "(--help --time)"--time'[SUFFIX Generate names based on the time of the day]:suffix:' \ "(--help --tag)"--tag'[NAME Try to use NAME as the differentiator]:name:' \ '(- *)'--help'[Show help and exit]' \ '*:basename:' ;; (test-terminal-size) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-th-eval) _arguments \ "(--help --cgi)"--cgi'[Include a CGI response header in the output]' \ "(--help --http)"--http'[Include an HTTP response header in the output]' \ "(--help --open-config)"--open-config'[Open the configuration database]' \ "(--help --set-anon-caps)"--set-anon-caps'[Set anonymous login capabilities]' \ "(--help --set-user-caps)"--set-user-caps'[Set user login capabilities]' \ "(--help --th-trace)"--th-trace'[Trace TH1 execution]' \ '(- *)'--help'[Show help and exit]' \ '1:script:_files' ;; (test-th-render) _arguments \ "(--help --cgi)"--cgi'[Include a CGI response header in the output]' \ "(--help --http)"--http'[Include an HTTP response header in the output]' \ "(--help --open-config)"--open-config'[Open the configuration database]' \ "(--help --set-anon-caps)"--set-anon-caps'[Set anonymous login capabilities]' \ "(--help --set-user-caps)"--set-user-caps'[Set user login capabilities]' \ "(--help --th-trace)"--th-trace'[Trace TH1 execution]' \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (test-th-source) _arguments \ "(--help --cgi)"--cgi'[Include a CGI response header in the output]' \ "(--help --http)"--http'[Include an HTTP response header in the output]' \ "(--help --open-config)"--open-config'[Open the configuration database]' \ "(--help --set-anon-caps)"--set-anon-caps'[Set anonymous login capabilities]' \ "(--help --set-user-caps)"--set-user-caps'[Set user login capabilities]' \ "(--help --th-trace)"--th-trace'[Trace TH1 execution]' \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (test-ticket-rebuild) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:ticket id:(all)' ;; (test-timespan) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:timestamp:' ;; (test-timewarp-list) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-topological-sort) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-tree-name) _arguments \ "(--help --absolute)"--absolute'[Return an absolute path instead of a relative one]' \ "(--help --case-sensitive)"--case-sensitive'[BOOL Enable or disable case-sensitive filenames]:bool:(yes no)' \ '(- *)'--help'[Show help and exit]' ;; (test-unclustered) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-uncompress) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:in:_files' \ '2:out:_files' ;; (test-unsent) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-urlparser) _arguments \ "(--help --remember)"--remember'[Store results in last-sync-url]' \ "(--help --prompt-pw)"--prompt-pw'[Prompt for password if missing]' \ '(- *)'--help'[Show help and exit]' \ '1:url:__fossil_urls' ;; (test-usernames) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-valid-for-windows) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-var-get) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:var:' \ '2::file:_files' ;; (test-var-list) _arguments \ "(--help --unset)"--unset'[Delete entries instead of displaying them]' \ "(--help --mtime)"--mtime'[Show last modification time]' \ '(- *)'--help'[Show help and exit]' \ '1::pattern:' ;; (test-var-set) _arguments \ "(--help --blob --file)"--blob'[FILE Binary file to read from]:file:_files' \ "(--help --file --blob)"--file'[FILE File to read from]:file:_files' \ '(- *)'--help'[Show help and exit]' \ '1:var:' \ '2::value:' ;; (test-verify-all) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-whatis-all) _arguments \ '(- *)'--help'[Show help and exit]' ;; (test-which) _arguments \ '(- *)'--help'[Show help and exit]' \ '*:executable name:' ;; (test-wiki-relink) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:wiki page:($(__fossil_wiki_pages))' ;; (test-wiki-render) _arguments \ "(--help --buttons)"--buttons'[Set the WIKI_BUTTONS flag]' \ "(--help --htmlonly)"--htmlonly'[Set the WIKI_HTMLONLY flag]' \ "(--help --linksonly)"--linksonly'[Set the WIKI_LINKSONLY flag]' \ "(--help --nobadlinks)"--nobadlinks'[Set the WIKI_NOBADLINKS flag]' \ "(--help --inline)"--inline'[Set the WIKI_INLINE flag]' \ "(--help --noblock)"--noblock'[Set the WIKI_NOBLOCK flag]' \ '(- *)'--help'[Show help and exit]' \ '1:file:_files' ;; (test-without-rowid) _arguments \ "(--help -n --dryrun)"{-n,--dryrun}'[Just print what would happen]' \ '(- *)'--help'[Show help and exit]' \ '*:files:_files' ;; (test-xfer) _arguments \ '(- *)'--help'[Show help and exit]' \ '1:xfer message:' ;; esac } ################################################################################ _fossil |
Added tools/fossil-diff-log.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/env perl # Fossil emulation of the "git log --patch / -p" feature: emit a stream # of diffs from one version to the next for each file named on the # command line. # # LIMITATIONS: It does not assume "all files" if you give no args, and # it cannot take a directory to mean "all files under this parent". # # PREREQUISITES: This script needs several CPAN modules to run properly. # There are multiple methods to install them: # # sudo dnf install perl-File-Which perl-IO-Interactive # sudo apt install libfile-which-perl libio-interactive-perl # sudo cpanm File::Which IO::Interactive # ...etc... use strict; use warnings; use Carp; use File::Which; use IO::Interactive qw(is_interactive); die "usage: $0 <files...>\n\n" unless @ARGV; my $out; if (is_interactive()) { my $pager = $ENV{PAGER} || which('less') || which('more'); open $out, '|-', $pager or croak "Cannot pipe to $pager: $!"; } else { $out = *STDOUT; } open my $bcmd, '-|', 'fossil branch current' or die "Cannot get branch: $!\n"; my $cbranch = <$bcmd>; chomp $cbranch; close $bcmd; for my $file (@ARGV) { my $lastckid; open my $finfo, '-|', "fossil finfo --brief --limit 0 '$file'" or die "Failed to get file info: $!\n"; my @filines = <$finfo>; close $finfo; for my $line (@filines) { my ($currckid, $date, $user, $branch, @cwords) = split ' ', $line; next unless $branch eq $cbranch; if (defined $lastckid and defined $branch) { my $comment = join ' ', @cwords; open my $diff, '-|', 'fossil', 'diff', $file, '--from', $currckid, '--to', $lastckid, or die "Failed to diff $currckid -> $lastckid: $!\n"; my @dl = <$diff>; close $diff; my $patch = join '', @dl; print $out <<"OUT" Checkin ID $currckid to $branch by $user on $date Comment: $comment $patch OUT } $lastckid = $currckid; } } |
Added tools/fossil-stress.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 | #!/usr/bin/tclsh # # Run this script, giving the url of a Fossil server instances as the # argument, and this script will start sending HTTP requests into the # that server instance as fast as it can, as a stress test for the # server implementation. # set nthread 10 for {set i 0} {$i<[llength $argv]} {incr i} { set x [lindex $argv $i] if {[regexp {^--[a-z]} $x]} { set x [string range $x 1 end] } if {$x=="-threads"} { incr i set nthread [lindex $argv $i] } elseif {[string index $x 0]=="-"} { error "unknown option \"$x\"" } elseif {[info exists url]} { error "unknown argument \"$x\"" } else { set url $x } } if {![info exists url]} { error "Usage: $argv0 [-threads N] URL" } if {![regexp {^https?://([^/:]+)(:\d+)?(/.*)$} $url all domain port path]} { error "could not parse the URL [list $url] -- should be of the\ form \"http://domain/path\"" } set useragent {Mozilla/5.0 (fossil-stress.tcl) Gecko/20100101 Firefox/57.0} set path [string trimright $path /] set port [string trimleft $port :] if {$port==""} {set port 80} proc send_one_request {tid domain port path} { while {[catch { set x [socket $domain $port] fconfigure $x -translation binary -blocking 0 puts $x "GET $path HTTP/1.0\r" if {$port==80} { puts $x "Host: $domain\r" } else { puts $x "Host: $domain:$port\r" } puts $x "User-Agent: $::useragent\r" puts $x "Accept: text/html,q=0.9,*/*;q=0.8\r" puts $x "Accept-Language: en-US,en;q=0.5\r" puts $x "Connection: close\r" puts $x "\r" } msg]} { puts "ERROR: $msg" after 1000 } global cnt stime threadid set cnt($x) 0 set stime($x) [clock seconds] set threadid($x) $tid flush $x fileevent $x readable [list get_reply $tid $path $x] } proc close_connection {x} { global cnt stime tid close $x unset -nocomplain cnt($x) unset -nocomplain stime($x) unset -nocomplain threadid($x) } proc get_reply {tid info x} { global cnt if {[eof $x]} { puts "[format %3d: $tid] $info ($cnt($x) bytes)" flush stdout close_connection $x start_another_request $tid } else { incr cnt($x) [string length [read $x]] } } set pages { /timeline?n=20 /timeline?n=20&a=1970-01-01 /home /brlist /info/trunk /info/2015-01-01 /vdiff?from=2015-01-01&to=trunk&diff=0 /wcontent /fileage /dir /tree /uvlist /stat /test_env /sitemap /hash-collisions /artifact_stats /bloblist /bigbloblist /wiki_rules /md_rules /help /test-all-help /timewarps /taglist } set pageidx 0 proc start_another_request {tid} { global pages pageidx domain port path set p [lindex $pages $pageidx] incr pageidx if {$pageidx>=[llength $pages]} {set pageidx 0} send_one_request $tid $domain $port $path$p } proc unhang_stalled_threads {} { global stime threadid set now [clock seconds] # puts "checking for stalled threads...." foreach x [array names stime] { # puts -nonewline " $threadid($x)=[expr {$now-$stime($x)}]" if {$stime($x)+0<$now-10} { set t $threadid($x) puts "RESTART thread $t" flush stdout close_connection $x start_another_request $t } } # puts "" flush stdout after 10000 unhang_stalled_threads } unhang_stalled_threads for {set i 1} {$i<=$nthread} {incr i} { start_another_request $i } vwait forever |
Deleted tools/fossil_chat.tcl.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added tools/fossilwiki.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/perl # vim: cin : $repofile = shift; $repocmd = ''; $repocmd = "-R $repofile" if -f $repofile; $mainpage = ''; @rep = (); if ( ! -f $repofile ) { @rep = `fossil info | grep 'project-name'`; } else { @rep = `fossil info $repofile | grep 'project-name'`; } $mainpage = $rep[0]; chomp $mainpage; $mainpage =~ s/^project-name:\s+//; @pages = `fossil wiki list $repocmd`; %pages = (); foreach $page ( @pages ) { chomp $page; $text = `fossil wiki export "$page" $repocmd`; $pages{$page} = $text; } @orphans = (); @nointernals = (); @terminals = (); @empties = (); %badlinks = (); %alllinks = (); %links = (); foreach $page ( keys %pages ) { my @links = (); my $text = $pages{$page}; while ( $text =~ m/\[([^][]+)\]/g ) { push @links,$1; } $numlinks = $#links; if (@links == ()) { push @terminals, $page; } else { my @internals = grep { $_ !~ /(http:)|(mailto:)|(https:)/ } @links; if (@internals == ()) { push @nointernals, $page; } else { @{$links{$page}{'links'}} = map {my ($a,$b) = split /\|/; $a;} @internals; foreach $internal ( @internals ) { my ($int_link, $display) = split /\|/, $internal; ${$links{$int_link}{'refs'}}++; $alllinks{$int_link} = 1; } } } if ($text eq '' || $text =~ m/^<i>Empty Page<\/i>/) { chomp $tail; my ($head, $tail) = split /\/i>/ , $text; if ($tail =~ m/^\s*$/s) { push @empties, $page; } } } foreach $page ( keys %links ) { if ($page ne $mainpage && (${$links{$page}{'refs'}} == 0)) { push @orphans, $page; } } foreach $link (keys %alllinks ) { if (! exists($pages{$link}) && $link !~ /^\./ && $link !~ /^\//) { $badlinks{$link} = 1; } } foreach $empty ( @empties ) { print ("empty: '$empty'\n"); } foreach $nointernals ( @nointernals ) { print ("nointernals: '$nointernals'\n"); } foreach $terminal ( @terminals ) { print ("terminal: '$terminal'\n"); } foreach $orphan ( @orphans ) { print ("orphan: '$orphan'\n"); } foreach $link ( keys %badlinks ) { print ("badlink: '$link'\n"); } foreach $page ( sort keys %links ) { my @links = @{$links{$page}{'links'}}; if (@links != ()) { if ($page eq $mainpage) { print "links: *** '$page' *** -> ", join (", ", @links), "\n"; } else { print "links: '$page' -> ", join (", ", @links), "\n"; } } } |
Added tools/fslsrv.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/bin/bash FOSSIL=fossil PGARGS="-P 1" OLDPID=`pgrep -P 1 fossil` SITE=https://example.com PORT=12345 if [ "$1" = "-f" ] ; then PGARGS= ; shift ; fi if [ -n "$OLDPID" ] then echo "Killing running Fossil server first..." pkill $PGARGS fossil for i in $(seq 30) do if [ -n "$(pgrep $PGARGS fossil)" ] then if [ $i -eq 1 ] then echo -n "Waiting for it to die..." else echo -n . fi sleep '0.1' else break fi echo done killall -9 fossil 2> /dev/null fi if [ -x ./fossil ] then # We're running from a build tree, so use that version instead FOSSIL=./fossil fi function start_one() { bn=$1 ln="$2" $FOSSIL server $extra \ --scgi \ --localhost \ --port $PORT \ --jsmode bundled \ --baseurl ${SITE}/$bn \ --errorlog ~/log/fossil/$bn-errors.log \ ~/museum/$bn.fossil > ~/log/fossil/$bn-stdout.log & echo Started $ln Fossil server, port $PORT, PID $!. PORT=$(($PORT + 1)) } start_one first "First Project" start_one second "Second Project" start_one third "Third Project" |
Added tools/makeheaders.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 | /* ** 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".) ** ** Copyright 1993 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 "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 author 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. ** ** 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. */ #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. */ #ifdef DEBUG static int debugMask = 0; # define debug0(F,M) if( (F)&debugMask ){ fprintf(stderr,M); } # define debug1(F,M,A) if( (F)&debugMask ){ fprintf(stderr,M,A); } # define debug2(F,M,A,B) if( (F)&debugMask ){ fprintf(stderr,M,A,B); } # define debug3(F,M,A,B,C) if( (F)&debugMask ){ fprintf(stderr,M,A,B,C); } # define PARSER 0x00000001 # define DECL_DUMP 0x00000002 # define TOKENIZER 0x00000004 #else # define debug0(Flags, Format) # define debug1(Flags, Format, A) # define debug2(Flags, Format, A, B) # define debug3(Flags, Format, A, B, C) #endif /* ** The following macros are purely for the purpose of testing this ** program on itself. They don't really contribute to the code. */ #define INTERFACE 1 #define EXPORT_INTERFACE 1 #define EXPORT /* ** Each token in a source file is represented by an instance of ** the following structure. Tokens are collected onto a list. */ typedef struct Token Token; struct Token { const char *zText; /* The text of the token */ int nText; /* Number of characters in the token's text */ int eType; /* The type of this token */ int nLine; /* The line number on which the token starts */ Token *pComment; /* Most recent block comment before this token */ Token *pNext; /* Next token on the list */ Token *pPrev; /* Previous token on the list */ }; /* ** During tokenization, information about the state of the input ** stream is held in an instance of the following structure */ typedef struct InStream InStream; struct InStream { const char *z; /* Complete text of the input */ int i; /* Next character to read from the input */ int nLine; /* The line number for character z[i] */ }; /* ** Each declaration in the C or C++ source files is parsed out and stored as ** an instance of the following structure. ** ** A "forward declaration" is a declaration that an object exists that ** 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 */ int flags; /* See the "Properties" below */ Token *pComment; /* A block comment associated with this declaration */ Token tokenCode; /* Implementation of functions and procedures */ Decl *pSameName; /* Next declaration with the same "zName" */ Decl *pSameHash; /* Next declaration with same hash but different zName */ Decl *pNext; /* Next declaration with a different name */ }; /* ** Properties associated with declarations. ** ** DP_Forward and DP_Declared are used during the generation of a single ** header file in order to prevent duplicate declarations and definitions. ** DP_Forward is set after the object has been given a forward declaration ** and DP_Declared is set after the object gets a full declarations. ** (Example: A forward declaration is "typedef struct Abc Abc;" and the ** full declaration is "struct Abc { int a; float b; };".) ** ** The DP_Export and DP_Local flags are more permanent. They mark objects ** that have EXPORT scope and LOCAL scope respectively. If both of these ** marks are missing, then the object has library scope. The meanings of ** the scopes are as follows: ** ** LOCAL scope The object is only usable within the file in ** which it is declared. ** ** library scope The object is visible and usable within other ** files in the same project. By if the project is ** 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 */ #define DP_Local 0x008 /* Declare in its home file only */ #define DP_Flag 0x010 /* Use to mark a subset of a Decl list ** for special processing */ #define DP_Cplusplus 0x020 /* Has C++ linkage and cannot appear in a ** C header file */ #define DP_ExternCReqd 0x040 /* Prepend 'extern "C"' in a C++ header. ** Prepend nothing in a C header */ #define DP_ExternReqd 0x080 /* Prepend 'extern "C"' in a C++ header if ** DP_Cplusplus is not also set. If DP_Cplusplus ** is set or this is a C header then ** prepend 'extern' */ /* ** Convenience macros for dealing with declaration properties */ #define DeclHasProperty(D,P) (((D)->flags&(P))==(P)) #define DeclHasAnyProperty(D,P) (((D)->flags&(P))!=0) #define DeclSetProperty(D,P) (D)->flags |= (P) #define DeclClearProperty(D,P) (D)->flags &= ~(P) /* ** These are state properties of the parser. Each of the values is ** distinct from the DP_ values above so that both can be used in ** 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 */ #define PS_Local2 0x080000 /* If "LOCAL" seen. */ #define PS_Public 0x100000 /* If "PUBLIC" seen. */ #define PS_Protected 0x200000 /* If "PROTECTED" seen. */ #define PS_Private 0x400000 /* If "PRIVATE" seen. */ #define PS_PPP 0x700000 /* If any of PUBLIC, PRIVATE, PROTECTED */ /* ** The following set of flags are ORed into the "flags" field of ** a Decl in order to identify what type of object is being ** declared. */ #define TY_Class 0x00100000 #define TY_Subroutine 0x00200000 #define TY_Macro 0x00400000 #define TY_Typedef 0x00800000 #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 */ int flags; /* Can hold PS_Export, PS_Interface or PS_Local flags */ }; /* ** When parsing a file, we need to keep track of what other files have ** be #include-ed. For each #include found, we create an instance of ** the following structure. */ typedef struct Include Include; struct Include { char *zFile; /* The name of file include. Includes "" or <> */ char *zIf; /* If not NULL, #include should be enclosed in #if */ char *zLabel; /* A unique label used to test if this #include has * appeared already in a file or not */ Include *pNext; /* Previous include file, or NULL if this is the first */ }; /* ** Identifiers found in a source file that might be used later to provoke ** the copying of a declaration into the corresponding header file are ** stored in a hash table as instances of the following structure. */ typedef struct Ident Ident; struct Ident { char *zName; /* The text of this identifier */ Ident *pCollide; /* Next identifier with the same hash */ Ident *pNext; /* Next identifier in a list of them all */ }; /* ** A complete table of identifiers is stored in an instance of ** the next structure. */ #define IDENT_HASH_SIZE 2237 typedef struct IdentTable IdentTable; struct IdentTable { Ident *pList; /* List of all identifiers in this table */ Ident *apTable[IDENT_HASH_SIZE]; /* The hash table */ }; /* ** The following structure holds all information for a single ** source file named on the command line of this program. */ typedef struct InFile InFile; struct InFile { char *zSrc; /* Name of input file */ 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 nul terminator) */ char *zText; /* Text of the string */ }; /* ** The following structure contains a lot of state information used ** while generating a .h file. We put the information in this structure ** and pass around a pointer to this structure, rather than pass around ** all of the information separately. This helps reduce the number of ** arguments to generator functions. */ typedef struct GenState GenState; struct GenState { String *pStr; /* Write output to this string */ IdentTable *pTable; /* A table holding the zLabel of every #include that * has already been generated. Used to avoid * generating duplicate #includes. */ const char *zIf; /* If not NULL, then we are within a #if with * this argument. */ int nErr; /* Number of errors */ const char *zFilename; /* Name of the source file being scanned */ int flags; /* Various flags (DP_ and PS_ flags above) */ }; /* ** 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; /* ** A list of all files that have been #included so far in a file being ** parsed. */ static Include *includeList = 0; /* ** The last block comment seen. */ static Token *blockComment = 0; /* ** The following flag is set if the -doc flag appears on the ** command line. */ static int doc_flag = 0; /* ** If the following flag is set, then makeheaders will attempt to ** generate prototypes for static functions and procedures. */ static int proto_static = 0; /* ** A list of all declarations. The list is held together using the ** pNext field of the Decl structure. */ static Decl *pDeclFirst; /* First on the list */ static Decl *pDeclLast; /* Last on the list */ /* ** A hash table of all declarations */ #define DECL_HASH_SIZE 3371 static Decl *apTable[DECL_HASH_SIZE]; /* ** The TEST macro must be defined to something. Make sure this is the ** case. */ #ifndef TEST # define TEST 0 #endif #ifdef NOT_USED /* ** We do our own assertion macro so that we can have more control ** over debugging. */ #define Assert(X) if(!(X)){ CantHappen(__LINE__); } #define CANT_HAPPEN CantHappen(__LINE__) static void CantHappen(int iLine){ fprintf(stderr,"Assertion failed on line %d\n",iLine); *(char*)1 = 0; /* Force a core-dump */ } #endif /* ** Memory allocation functions that are guaranteed never to return NULL. */ static void *SafeMalloc(int nByte){ void *p = malloc( nByte ); if( p==0 ){ fprintf(stderr,"Out of memory. Can't allocate %d bytes.\n",nByte); exit(1); } return p; } static void SafeFree(void *pOld){ if( pOld ){ free(pOld); } } static void *SafeRealloc(void *pOld, int nByte){ void *p; if( pOld==0 ){ p = SafeMalloc(nByte); }else{ p = realloc(pOld, nByte); if( p==0 ){ fprintf(stderr, "Out of memory. Can't enlarge an allocation to %d bytes\n",nByte); exit(1); } } return p; } static char *StrDup(const char *zSrc, int nByte){ char *zDest; if( nByte<=0 ){ nByte = strlen(zSrc); } zDest = SafeMalloc( nByte + 1 ); strncpy(zDest,zSrc,nByte); zDest[nByte] = 0; return zDest; } /* ** Return TRUE if the character X can be part of an identifier */ #define ISALNUM(X) ((X)=='_' || isalnum(X)) /* ** Routines for dealing with unbounded strings. */ static void StringInit(String *pStr){ pStr->nAlloc = 0; pStr->nUsed = 0; pStr->zText = 0; } static void StringReset(String *pStr){ SafeFree(pStr->zText); StringInit(pStr); } static void StringAppend(String *pStr, const char *zText, int nByte){ if( nByte<=0 ){ nByte = strlen(zText); } if( pStr->nUsed + nByte >= pStr->nAlloc ){ if( pStr->nAlloc==0 ){ pStr->nAlloc = nByte + 100; pStr->zText = SafeMalloc( pStr->nAlloc ); }else{ pStr->nAlloc = pStr->nAlloc*2 + nByte; pStr->zText = SafeRealloc(pStr->zText, pStr->nAlloc); } } strncpy(&pStr->zText[pStr->nUsed],zText,nByte); pStr->nUsed += nByte; pStr->zText[pStr->nUsed] = 0; } #define StringGet(S) ((S)->zText?(S)->zText:"") /* ** Compute a hash on a string. The number returned is a non-negative ** value between 0 and 2**31 - 1 */ static int Hash(const char *z, int n){ int h = 0; if( n<=0 ){ n = strlen(z); } while( n-- ){ h = h ^ (h<<5) ^ *z++; } return h & 0x7fffffff; } /* ** Given an identifier name, try to find a declaration for that ** identifier in the hash table. If found, return a pointer to ** the Decl structure. If not found, return 0. */ static Decl *FindDecl(const char *zName, int len){ int h; Decl *p; if( len<=0 ){ len = strlen(zName); } h = Hash(zName,len) % DECL_HASH_SIZE; p = apTable[h]; while( p && (strncmp(p->zName,zName,len)!=0 || p->zName[len]!=0) ){ p = p->pSameHash; } return p; } /* ** Install the given declaration both in the hash table and on ** the list of all declarations. */ static void InstallDecl(Decl *pDecl){ int h; Decl *pOther; h = Hash(pDecl->zName,0) % DECL_HASH_SIZE; pOther = apTable[h]; while( pOther && strcmp(pDecl->zName,pOther->zName)!=0 ){ pOther = pOther->pSameHash; } if( pOther ){ pDecl->pSameName = pOther->pSameName; pOther->pSameName = pDecl; }else{ pDecl->pSameName = 0; pDecl->pSameHash = apTable[h]; apTable[h] = pDecl; } pDecl->pNext = 0; if( pDeclFirst==0 ){ pDeclFirst = pDeclLast = pDecl; }else{ pDeclLast->pNext = pDecl; pDeclLast = pDecl; } } /* ** Look at the current ifStack. If anything declared at the current ** position must be surrounded with ** ** #if STUFF ** #endif ** ** Then this routine computes STUFF and returns a pointer to it. Memory ** to hold the value returned is obtained from malloc(). */ static char *GetIfString(void){ Ifmacro *pIf; char *zResult = 0; int hasIf = 0; String str; for(pIf = ifStack; pIf; pIf=pIf->pNext){ if( pIf->zCondition==0 || *pIf->zCondition==0 ) continue; if( !hasIf ){ hasIf = 1; StringInit(&str); }else{ StringAppend(&str," && ",4); } StringAppend(&str,pIf->zCondition,0); } if( hasIf ){ zResult = StrDup(StringGet(&str),0); StringReset(&str); }else{ zResult = 0; } return zResult; } /* ** Create a new declaration and put it in the hash table. Also ** return a pointer to it so that we can fill in the zFwd and zDecl ** fields, and so forth. */ static Decl *CreateDecl( const char *zName, /* Name of the object being declared. */ int nName /* Length of the name */ ){ Decl *pDecl; pDecl = SafeMalloc( sizeof(Decl) + nName + 1); memset(pDecl,0,sizeof(Decl)); pDecl->zName = (char*)&pDecl[1]; memcpy(pDecl->zName, zName, nName); pDecl->zName[nName] = 0; pDecl->zFile = zFilename; pDecl->pInclude = includeList; pDecl->zIf = GetIfString(); InstallDecl(pDecl); return pDecl; } /* ** Insert a new identifier into an table of identifiers. Return TRUE if ** a new identifier was inserted and return FALSE if the identifier was ** already in the table. */ static int IdentTableInsert( IdentTable *pTable, /* The table into which we will insert */ const char *zId, /* Name of the identifiers */ int nId /* Length of the identifier name */ ){ int h; Ident *pId; if( nId<=0 ){ nId = strlen(zId); } h = Hash(zId,nId) % IDENT_HASH_SIZE; for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){ if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){ /* printf("Already in table: %.*s\n",nId,zId); */ return 0; } } pId = SafeMalloc( sizeof(Ident) + nId + 1 ); pId->zName = (char*)&pId[1]; memcpy(pId->zName, zId, nId); pId->zName[nId] = 0; pId->pNext = pTable->pList; pTable->pList = pId; pId->pCollide = pTable->apTable[h]; pTable->apTable[h] = pId; /* printf("Add to table: %.*s\n",nId,zId); */ return 1; } /* ** Check to see if the given value is in the given IdentTable. Return ** true if it is and false if it is not. */ static int IdentTableTest( IdentTable *pTable, /* The table in which to search */ const char *zId, /* Name of the identifiers */ int nId /* Length of the identifier name */ ){ int h; Ident *pId; if( nId<=0 ){ nId = strlen(zId); } h = Hash(zId,nId) % IDENT_HASH_SIZE; for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){ if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){ return 1; } } return 0; } /* ** Remove every identifier from the given table. Reset the table to ** its initial state. */ static void IdentTableReset(IdentTable *pTable){ Ident *pId, *pNext; for(pId = pTable->pList; pId; pId = pNext){ pNext = pId->pNext; SafeFree(pId); } memset(pTable,0,sizeof(IdentTable)); } #ifdef DEBUG /* ** Print the name of every identifier in the given table, one per line */ static void IdentTablePrint(IdentTable *pTable, FILE *pOut){ Ident *pId; for(pId = pTable->pList; pId; pId = pId->pNext){ fprintf(pOut,"%s\n",pId->zName); } } #endif /* ** Read an entire file into memory. Return a pointer to the memory. ** ** The memory is obtained from SafeMalloc and must be freed by the ** calling function. ** ** If the read fails for any reason, 0 is returned. */ 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"); if( pIn==0 ){ return 0; } zBuf = SafeMalloc( sStat.st_size + 1 ); n = fread(zBuf,1,sStat.st_size,pIn); zBuf[n] = 0; fclose(pIn); return zBuf; } /* ** Write the contents of a string into a file. Return the number of ** errors */ static int WriteFile(const char *zFilename, const char *zOutput){ FILE *pOut; pOut = fopen(zFilename,"w"); if( pOut==0 ){ return 1; } fwrite(zOutput,1,strlen(zOutput),pOut); fclose(pOut); return 0; } /* ** Major token types */ #define TT_Space 1 /* Contiguous white space */ #define TT_Id 2 /* An identifier */ #define TT_Preprocessor 3 /* Any C preprocessor directive */ #define TT_Comment 4 /* Either C or C++ style comment */ #define TT_Number 5 /* Any numeric constant */ #define TT_String 6 /* String or character constants. ".." or '.' */ #define TT_Braces 7 /* All text between { and a matching } */ #define TT_EOF 8 /* End of file */ #define TT_Error 9 /* An error condition */ #define TT_BlockComment 10 /* A C-Style comment at the left margin that * spans multiple lines */ #define TT_Other 0 /* None of the above */ /* ** Get a single low-level token from the input file. Update the ** file pointer so that it points to the first character beyond the ** token. ** ** A "low-level token" is any token except TT_Braces. A TT_Braces token ** consists of many smaller tokens and is assembled by a routine that ** calls this one. ** ** The function returns the number of errors. An error is an ** unterminated string or character literal or an unterminated ** comment. ** ** Profiling shows that this routine consumes about half the ** CPU time on a typical run of makeheaders. */ static int GetToken(InStream *pIn, Token *pToken){ int i; const char *z; int cStart; int c; int startLine; /* Line on which a structure begins */ int nlisc = 0; /* True if there is a new-line in a ".." or '..' */ int nErr = 0; /* Number of errors seen */ z = pIn->z; i = pIn->i; pToken->nLine = pIn->nLine; pToken->zText = &z[i]; switch( z[i] ){ case 0: pToken->eType = TT_EOF; pToken->nText = 0; break; case '#': if( i==0 || z[i-1]=='\n' || (i>1 && z[i-1]=='\r' && z[i-2]=='\n')){ /* We found a preprocessor statement */ pToken->eType = TT_Preprocessor; i++; while( z[i]!=0 && z[i]!='\n' ){ if( z[i]=='\\' ){ i++; if( z[i]=='\n' ) pIn->nLine++; } i++; } pToken->nText = i - pIn->i; }else{ /* Just an operator */ pToken->eType = TT_Other; pToken->nText = 1; } break; case ' ': case '\t': case '\r': case '\f': case '\n': while( isspace(z[i]) ){ if( z[i]=='\n' ) pIn->nLine++; i++; } pToken->eType = TT_Space; pToken->nText = i - pIn->i; break; case '\\': pToken->nText = 2; pToken->eType = TT_Other; if( z[i+1]=='\n' ){ pIn->nLine++; pToken->eType = TT_Space; }else if( z[i+1]==0 ){ pToken->nText = 1; } break; case '\'': case '\"': cStart = z[i]; startLine = pIn->nLine; do{ i++; c = z[i]; if( c=='\n' ){ if( !nlisc ){ fprintf(stderr, "%s:%d: (warning) Newline in string or character literal.\n", zFilename, pIn->nLine); nlisc = 1; } pIn->nLine++; } if( c=='\\' ){ i++; c = z[i]; if( c=='\n' ){ pIn->nLine++; } }else if( c==cStart ){ i++; c = 0; }else if( c==0 ){ fprintf(stderr, "%s:%d: Unterminated string or character literal.\n", zFilename, startLine); nErr++; } }while( c ); pToken->eType = TT_String; pToken->nText = i - pIn->i; break; case '/': if( z[i+1]=='/' ){ /* C++ style comment */ while( z[i] && z[i]!='\n' ){ i++; } pToken->eType = TT_Comment; pToken->nText = i - pIn->i; }else if( z[i+1]=='*' ){ /* C style comment */ int isBlockComment = i==0 || z[i-1]=='\n'; i += 2; startLine = pIn->nLine; while( z[i] && (z[i]!='*' || z[i+1]!='/') ){ if( z[i]=='\n' ){ pIn->nLine++; if( isBlockComment ){ if( z[i+1]=='*' || z[i+2]=='*' ){ isBlockComment = 2; }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++; } } pToken->eType = TT_Number; pToken->nText = i - pIn->i; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': while( isdigit(z[i]) ){ i++; } if( (c=z[i])=='.' ){ i++; while( isdigit(z[i]) ){ i++; } c = z[i]; if( c=='e' || c=='E' ){ i++; if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; } while( isdigit(z[i]) ){ i++; } c = z[i]; } if( c=='f' || c=='F' || c=='l' || c=='L' ){ i++; } }else if( c=='e' || c=='E' ){ i++; if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; } while( isdigit(z[i]) ){ i++; } }else if( c=='L' || c=='l' ){ i++; c = z[i]; if( c=='u' || c=='U' ){ i++; } }else if( c=='u' || c=='U' ){ i++; c = z[i]; if( c=='l' || c=='L' ){ i++; } } pToken->eType = TT_Number; pToken->nText = i - pIn->i; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': 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; break; } pIn->i += pToken->nText; return nErr; } /* ** This routine recovers the next token from the input file which is ** not a space or a comment or any text between an "#if 0" and "#endif". ** ** This routine returns the number of errors encountered. An error ** is an unterminated token or unmatched "#if 0". ** ** Profiling shows that this routine uses about a quarter of the ** CPU time in a typical run. */ static int GetNonspaceToken(InStream *pIn, Token *pToken){ int nIf = 0; int inZero = 0; const char *z; int value; int startLine; int nErr = 0; startLine = pIn->nLine; while( 1 ){ nErr += GetToken(pIn,pToken); /* printf("%04d: Type=%d nIf=%d [%.*s]\n", pToken->nLine,pToken->eType,nIf,pToken->nText, pToken->eType!=TT_Space ? pToken->zText : "<space>"); */ pToken->pComment = blockComment; switch( pToken->eType ){ case TT_Comment: /*0123456789 12345678 */ if( strncmp(pToken->zText, "/*MAKEHEADERS-STOP", 18)==0 ) return nErr; break; case TT_Space: break; case TT_BlockComment: if( doc_flag ){ blockComment = SafeMalloc( sizeof(Token) ); *blockComment = *pToken; } break; case TT_EOF: if( nIf ){ fprintf(stderr,"%s:%d: Unterminated \"#if\"\n", zFilename, startLine); nErr++; } return nErr; case TT_Preprocessor: z = &pToken->zText[1]; while( *z==' ' || *z=='\t' ) z++; if( sscanf(z,"if %d",&value)==1 && value==0 ){ nIf++; inZero = 1; }else if( inZero ){ if( strncmp(z,"if",2)==0 ){ nIf++; }else if( strncmp(z,"endif",5)==0 ){ nIf--; if( nIf==0 ) inZero = 0; } }else{ return nErr; } break; default: if( !inZero ){ return nErr; } 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; int go = 1; sIn.z = pToken->zText; sIn.i = 1; sIn.nLine = 1; while( go && sIn.i < pToken->nText ){ GetToken(&sIn,&sToken); switch( sToken.eType ){ case TT_Id: IdentTableInsert(pTable,sToken.zText,sToken.nText); break; case TT_EOF: go = 0; break; default: break; } } } /* ** This routine gets the next token. Everything contained within ** {...} is collapsed into a single TT_Braces token. Whitespace is ** omitted. ** ** If pTable is not NULL, then insert every identifier seen into the ** IdentTable. This includes any identifiers seen inside of {...}. ** ** The number of errors encountered is returned. An error is an ** unterminated token. */ static int GetBigToken(InStream *pIn, Token *pToken, IdentTable *pTable){ const char *zStart; int iStart; int nBrace; int c; int nLine; int nErr; nErr = GetNonspaceToken(pIn,pToken); switch( pToken->eType ){ case TT_Id: if( pTable!=0 ){ IdentTableInsert(pTable,pToken->zText,pToken->nText); } return nErr; case TT_Preprocessor: if( pTable!=0 ){ FindIdentifiersInMacro(pToken,pTable); } return nErr; case TT_Other: if( pToken->zText[0]=='{' ) break; return nErr; default: return nErr; } iStart = pIn->i; zStart = pToken->zText; nLine = pToken->nLine; nBrace = 1; while( nBrace ){ nErr += GetNonspaceToken(pIn,pToken); /* printf("%04d: nBrace=%d [%.*s]\n",pToken->nLine,nBrace, pToken->nText,pToken->zText); */ switch( pToken->eType ){ case TT_EOF: fprintf(stderr,"%s:%d: Unterminated \"{\"\n", zFilename, nLine); nErr++; pToken->eType = TT_Error; 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: if( (c = pToken->zText[0])=='{' ){ nBrace++; }else if( c=='}' ){ nBrace--; } break; default: break; } } pToken->eType = TT_Braces; pToken->nText = 1 + pIn->i - iStart; pToken->zText = zStart; pToken->nLine = nLine; return nErr; } /* ** This routine frees up a list of Tokens. The pComment tokens are ** not cleared by this. So we leak a little memory when using the -doc ** option. So what. */ static void FreeTokenList(Token *pList){ Token *pNext; while( pList ){ pNext = pList->pNext; SafeFree(pList); pList = pNext; } } /* ** Tokenize an entire file. Return a pointer to the list of tokens. ** ** Space for each token is obtained from a separate malloc() call. The ** calling function is responsible for freeing this space. ** ** If pTable is not NULL, then fill the table with all identifiers seen in ** the input file. */ static Token *TokenizeFile(const char *zFile, IdentTable *pTable){ InStream sIn; Token *pFirst = 0, *pLast = 0, *pNew; int nErr = 0; sIn.z = zFile; sIn.i = 0; sIn.nLine = 1; blockComment = 0; while( sIn.z[sIn.i]!=0 ){ pNew = SafeMalloc( sizeof(Token) ); nErr += GetBigToken(&sIn,pNew,pTable); debug3(TOKENIZER, "Token on line %d: [%.*s]\n", pNew->nLine, pNew->nText<50 ? pNew->nText : 50, pNew->zText); if( pFirst==0 ){ pFirst = pLast = pNew; pNew->pPrev = 0; }else{ pLast->pNext = pNew; pNew->pPrev = pLast; pLast = pNew; } if( pNew->eType==TT_EOF ) break; } if( pLast ) pLast->pNext = 0; blockComment = 0; if( nErr ){ FreeTokenList(pFirst); pFirst = 0; } return pFirst; } #if TEST==1 /* ** Use the following routine to test or debug the tokenizer. */ void main(int argc, char **argv){ char *zFile; Token *pList, *p; IdentTable sTable; if( argc!=2 ){ fprintf(stderr,"Usage: %s filename\n",*argv); exit(1); } memset(&sTable,0,sizeof(sTable)); zFile = ReadFile(argv[1]); 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: printf("%4d: Preprocessor %.*s\n",p->nLine,p->nText,p->zText); break; case TT_Comment: printf("%4d: Comment\n",p->nLine); break; case TT_BlockComment: printf("%4d: Block Comment\n",p->nLine); break; case TT_Number: printf("%4d: Number %.*s\n",p->nLine,p->nText,p->zText); break; case TT_String: printf("%4d: String %.*s\n",p->nLine,p->nText,p->zText); break; case TT_Other: printf("%4d: Other %.*s\n",p->nLine,p->nText,p->zText); break; case TT_Braces: for(j=0; j<p->nText && j<30 && p->zText[j]!='\n'; j++){} printf("%4d: Braces %.*s...}\n",p->nLine,j,p->zText); break; case TT_EOF: printf("%4d: End of file\n",p->nLine); break; default: printf("%4d: type %d\n",p->nLine,p->eType); break; } } FreeTokenList(pList); SafeFree(zFile); IdentTablePrint(&sTable,stdout); } #endif #ifdef DEBUG /* ** For debugging purposes, write out a list of tokens. */ static void PrintTokens(Token *pFirst, Token *pLast){ int needSpace = 0; int c; pLast = pLast->pNext; while( pFirst!=pLast ){ switch( pFirst->eType ){ case TT_Preprocessor: printf("\n%.*s\n",pFirst->nText,pFirst->zText); needSpace = 0; break; case TT_Id: 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; } } #endif /* ** Convert a sequence of tokens into a string and return a pointer ** to that string. Space to hold the string is obtained from malloc() ** and must be freed by the calling function. ** ** Certain keywords (EXPORT, PRIVATE, PUBLIC, PROTECTED) are always ** skipped. ** ** If pSkip!=0 then skip over nSkip tokens beginning with pSkip. ** ** If zTerm!=0 then append the text to the end. */ static char *TokensToString( Token *pFirst, /* First token in the string */ Token *pLast, /* Last token in the string */ char *zTerm, /* Terminate the string with this text if not NULL */ Token *pSkip, /* Skip this token if not NULL */ int nSkip /* Skip a total of this many tokens */ ){ char *zReturn; String str; int needSpace = 0; int c; 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; case 7: skipOne = !strncmp(pFirst->zText,"PRIVATE",7); break; case 9: skipOne = !strncmp(pFirst->zText,"PROTECTED",9); break; default: break; } break; default: break; } if( skipOne ){ pFirst = pFirst->pNext; skipOne = 0; continue; } /* Fall thru to the next case */ case TT_Number: if( needSpace ){ StringAppend(&str," ",1); } StringAppend(&str,pFirst->zText,pFirst->nText); needSpace = 1; break; default: c = pFirst->zText[0]; if( needSpace && (c=='*' || c=='{') ){ StringAppend(&str," ",1); } StringAppend(&str,pFirst->zText,pFirst->nText); /* needSpace = pFirst->zText[0]==','; */ needSpace = 0; break; } pFirst = pFirst->pNext; } if( zTerm && *zTerm ){ StringAppend(&str,zTerm,strlen(zTerm)); } zReturn = StrDup(StringGet(&str),0); StringReset(&str); return zReturn; } /* ** This routine is called when we see one of the keywords "struct", ** "enum", "union" or "class". This might be the beginning of a ** type declaration. This routine will process the declaration and ** remove the declaration tokens from the input stream. ** ** If this is a type declaration that is immediately followed by a ** semicolon (in other words it isn't also a variable definition) ** then set *pReset to ';'. Otherwise leave *pReset at 0. The ** *pReset flag causes the parser to skip ahead to the next token ** that begins with the value placed in the *pReset flag, if that ** value is different from 0. */ static int ProcessTypeDecl(Token *pList, int flags, int *pReset){ Token *pName, *pEnd; Decl *pDecl; String str; int need_to_collapse = 1; int type = 0; *pReset = 0; if( pList==0 || pList->pNext==0 || pList->pNext->eType!=TT_Id ){ return 0; } pName = pList->pNext; /* Catch the case of "struct Foo;" and skip it. */ if( pName->pNext && pName->pNext->zText[0]==';' ){ *pReset = ';'; return 0; } for(pEnd=pName->pNext; pEnd && pEnd->eType!=TT_Braces; pEnd=pEnd->pNext){ switch( pEnd->zText[0] ){ case '(': case ')': case '*': case '[': case '=': case ';': return 0; } } if( pEnd==0 ){ return 0; } /* ** At this point, we know we have a type declaration that is bounded ** by pList and pEnd and has the name pName. */ /* ** If the braces are followed immediately by a semicolon, then we are ** dealing a type declaration only. There is not variable definition ** following the type declaration. So reset... */ if( pEnd->pNext==0 || pEnd->pNext->zText[0]==';' ){ *pReset = ';'; need_to_collapse = 0; }else{ need_to_collapse = 1; } if( proto_static==0 && (flags & (PS_Local|PS_Export|PS_Interface))==0 ){ /* Ignore these objects unless they are explicitly declared as interface, ** or unless the "-local" command line option was specified. */ *pReset = ';'; return 0; } #ifdef DEBUG if( debugMask & PARSER ){ printf("**** Found type: %.*s %.*s...\n", pList->nText, pList->zText, pName->nText, pName->zText); PrintTokens(pList,pEnd); printf(";\n"); } #endif /* ** Create a new Decl object for this definition. Actually, if this ** is a C++ class definition, then the Decl object might already exist, ** so check first for that case before creating a new one. */ switch( *pList->zText ){ case 'c': type = TY_Class; break; case 's': type = TY_Structure; break; case 'e': type = TY_Enumeration; break; case 'u': type = TY_Union; break; default: /* Can't Happen */ break; } if( type!=TY_Class ){ pDecl = 0; }else{ pDecl = FindDecl(pName->zText, pName->nText); if( pDecl && (pDecl->flags & type)!=type ) pDecl = 0; } if( pDecl==0 ){ pDecl = CreateDecl(pName->zText,pName->nText); } if( (flags & PS_Static) || !(flags & (PS_Interface|PS_Export)) ){ DeclSetProperty(pDecl,DP_Local); } DeclSetProperty(pDecl,type); /* The object has a full declaration only if it is contained within ** "#if INTERFACE...#endif" or "#if EXPORT_INTERFACE...#endif" or ** "#if LOCAL_INTERFACE...#endif". Otherwise, we only give it a ** forward declaration. */ if( flags & (PS_Local | PS_Export | PS_Interface) ){ pDecl->zDecl = TokensToString(pList,pEnd,";\n",0,0); }else{ pDecl->zDecl = 0; } pDecl->pComment = pList->pComment; StringInit(&str); StringAppend(&str,"typedef ",0); StringAppend(&str,pList->zText,pList->nText); StringAppend(&str," ",0); StringAppend(&str,pName->zText,pName->nText); StringAppend(&str," ",0); StringAppend(&str,pName->zText,pName->nText); StringAppend(&str,";\n",2); pDecl->zFwd = StrDup(StringGet(&str),0); StringReset(&str); StringInit(&str); StringAppend(&str,pList->zText,pList->nText); StringAppend(&str," ",0); StringAppend(&str,pName->zText,pName->nText); StringAppend(&str,";\n",2); pDecl->zFwdCpp = StrDup(StringGet(&str),0); StringReset(&str); if( flags & PS_Export ){ DeclSetProperty(pDecl,DP_Export); }else if( flags & PS_Local ){ DeclSetProperty(pDecl,DP_Local); } /* Here's something weird. ANSI-C doesn't allow a forward declaration ** of an enumeration. So we have to build the typedef into the ** definition. */ if( pDecl->zDecl && DeclHasProperty(pDecl, TY_Enumeration) ){ StringInit(&str); StringAppend(&str,pDecl->zDecl,0); StringAppend(&str,pDecl->zFwd,0); SafeFree(pDecl->zDecl); SafeFree(pDecl->zFwd); pDecl->zFwd = 0; pDecl->zDecl = StrDup(StringGet(&str),0); StringReset(&str); } if( pName->pNext->zText[0]==':' ){ DeclSetProperty(pDecl,DP_Cplusplus); } if( pName->nText==5 && strncmp(pName->zText,"class",5)==0 ){ DeclSetProperty(pDecl,DP_Cplusplus); } /* ** Remove all but pList and pName from the input stream. */ if( need_to_collapse ){ while( pEnd!=pName ){ Token *pPrev = pEnd->pPrev; pPrev->pNext = pEnd->pNext; pEnd->pNext->pPrev = pPrev; SafeFree(pEnd); pEnd = pPrev; } } return 0; } /* ** Given a list of tokens that declare something (a function, procedure, ** variable or typedef) find the token which contains the name of the ** thing being declared. ** ** Algorithm: ** ** The name is: ** ** 1. The first identifier that is followed by a "[", or ** ** 2. The first identifier that is followed by a "(" where the ** "(" is followed by another identifier, or ** ** 3. The first identifier followed by "::", or ** ** 4. If none of the above, then the last identifier. ** ** In all of the above, certain reserved words (like "char") are ** not considered identifiers. */ static Token *FindDeclName(Token *pFirst, Token *pLast){ Token *pName = 0; Token *p; int c; if( pFirst==0 || pLast==0 ){ 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; } if( !IdentTableTest(&sReserved,p->zText,p->nText) ){ pName = p; } }else if( p==pFirst ){ continue; }else if( (c=p->zText[0])=='[' && pName ){ break; }else if( c=='(' && p->pNext && p->pNext->eType==TT_Id && pName ){ break; }else if( c==':' && p->zText[1]==':' && pName ){ break; } } return pName; } /* ** This routine is called when we see a method for a class that begins ** with the PUBLIC, PRIVATE, or PROTECTED keywords. Such methods are ** added to their class definitions. */ static int ProcessMethodDef(Token *pFirst, Token *pLast, int flags){ Token *pClass; char *zDecl; Decl *pDecl; String str; int type; pLast = pLast->pPrev; while( pFirst->zText[0]=='P' ){ int rc = 1; switch( pFirst->nText ){ case 6: rc = strncmp(pFirst->zText,"PUBLIC",6); break; case 7: rc = strncmp(pFirst->zText,"PRIVATE",7); break; case 9: rc = strncmp(pFirst->zText,"PROTECTED",9); break; default: break; } if( rc ) break; pFirst = pFirst->pNext; } pClass = FindDeclName(pFirst,pLast); if( pClass==0 ){ fprintf(stderr,"%s:%d: Unable to find the class name for this method\n", zFilename, pFirst->nLine); return 1; } pDecl = FindDecl(pClass->zText, pClass->nText); if( pDecl==0 || (pDecl->flags & TY_Class)!=TY_Class ){ pDecl = CreateDecl(pClass->zText, pClass->nText); DeclSetProperty(pDecl, TY_Class); } StringInit(&str); if( pDecl->zExtra ){ StringAppend(&str, pDecl->zExtra, 0); SafeFree(pDecl->zExtra); pDecl->zExtra = 0; } type = flags & PS_PPP; if( pDecl->extraType!=type ){ if( type & PS_Public ){ StringAppend(&str, "public:\n", 0); pDecl->extraType = PS_Public; }else if( type & PS_Protected ){ StringAppend(&str, "protected:\n", 0); pDecl->extraType = PS_Protected; }else if( type & PS_Private ){ StringAppend(&str, "private:\n", 0); pDecl->extraType = PS_Private; } } StringAppend(&str, " ", 0); zDecl = TokensToString(pFirst, pLast, ";\n", pClass, 2); if(strncmp(zDecl, pClass->zText, pClass->nText)==0){ /* If member initializer list is found after a constructor, ** skip that part. */ char * colon = strchr(zDecl, ':'); if(colon!=0 && colon[1]!=0){ *colon++ = ';'; *colon++ = '\n'; *colon = 0; } } StringAppend(&str, zDecl, 0); SafeFree(zDecl); pDecl->zExtra = StrDup(StringGet(&str), 0); StringReset(&str); return 0; } /* ** This routine is called when we see a function or procedure definition. ** We make an entry in the declaration table that is a prototype for this ** function or procedure. */ static int ProcessProcedureDef(Token *pFirst, Token *pLast, int flags){ Token *pName; Decl *pDecl; Token *pCode; if( pFirst==0 || pLast==0 ){ return 0; } if( flags & PS_Method ){ if( flags & PS_PPP ){ return ProcessMethodDef(pFirst, pLast, flags); }else{ return 0; } } if( (flags & PS_Static)!=0 && !proto_static ){ 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; } pName = FindDeclName(pFirst,pLast); if( pName==0 ){ fprintf(stderr,"%s:%d: Malformed function or procedure definition.\n", zFilename, pFirst->nLine); return 1; } if( strncmp(pName->zText,"main",pName->nText)==0 ){ /* skip main() decl. */ return 0; } /* ** At this point we've isolated a procedure declaration between pFirst ** and pLast with the name pName. */ #ifdef DEBUG if( debugMask & PARSER ){ printf("**** Found routine: %.*s on line %d...\n", pName->nText, pName->zText, pFirst->nLine); PrintTokens(pFirst,pLast); printf(";\n"); } #endif pDecl = CreateDecl(pName->zText,pName->nText); pDecl->pComment = pFirst->pComment; if( pCode && pCode->eType==TT_Braces ){ pDecl->tokenCode = *pCode; } DeclSetProperty(pDecl,TY_Subroutine); pDecl->zDecl = TokensToString(pFirst,pLast,";\n",0,0); if( (flags & (PS_Static|PS_Local2))!=0 ){ DeclSetProperty(pDecl,DP_Local); }else if( (flags & (PS_Export2))!=0 ){ DeclSetProperty(pDecl,DP_Export); } if( flags & DP_Cplusplus ){ DeclSetProperty(pDecl,DP_Cplusplus); }else{ DeclSetProperty(pDecl,DP_ExternCReqd); } return 0; } /* ** This routine is called whenever we see the "inline" keyword. We ** need to seek-out the inline function or procedure and make a ** declaration out of the entire definition. */ static int ProcessInlineProc(Token *pFirst, int flags, int *pReset){ Token *pName; Token *pEnd; Decl *pDecl; for(pEnd=pFirst; pEnd; pEnd = pEnd->pNext){ if( pEnd->zText[0]=='{' || pEnd->zText[0]==';' ){ *pReset = pEnd->zText[0]; break; } } if( pEnd==0 ){ *pReset = ';'; fprintf(stderr,"%s:%d: incomplete inline procedure definition\n", zFilename, pFirst->nLine); return 1; } pName = FindDeclName(pFirst,pEnd); if( pName==0 ){ 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; DeclSetProperty(pDecl,TY_Subroutine); pDecl->zDecl = TokensToString(pFirst,pEnd,";\n",0,0); if( (flags & (PS_Static|PS_Local|PS_Local2)) ){ DeclSetProperty(pDecl,DP_Local); }else if( flags & (PS_Export|PS_Export2) ){ DeclSetProperty(pDecl,DP_Export); } if( flags & DP_Cplusplus ){ DeclSetProperty(pDecl,DP_Cplusplus); }else{ DeclSetProperty(pDecl,DP_ExternCReqd); } return 0; } /* ** Determine if the tokens between pFirst and pEnd form a variable ** definition or a function prototype. Return TRUE if we are dealing ** with a variable defintion and FALSE for a prototype. ** ** pEnd is the token that ends the object. It can be either a ';' or ** a '='. If it is '=', then assume we have a variable definition. ** ** 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; } pFirst = pFirst->pNext; } return 1; } /* ** Return TRUE if pFirst is the first token of a static assert. */ static int isStaticAssert(Token *pFirst){ if( (pFirst->nText==13 && strncmp(pFirst->zText, "static_assert", 13)==0) || (pFirst->nText==14 && strncmp(pFirst->zText, "_Static_assert", 14)==0) ){ return 1; }else{ return 0; } } /* ** This routine is called whenever we encounter a ";" or "=". The stuff ** between pFirst and pLast constitutes either a typedef or a global ** variable definition. Do the right thing. */ static int ProcessDecl(Token *pFirst, Token *pEnd, int flags){ Token *pName; Decl *pDecl; int isLocal = 0; int isVar; int nErr = 0; if( pFirst==0 || pEnd==0 ){ return 0; } if( flags & PS_Typedef ){ if( (flags & (PS_Export2|PS_Local2))!=0 ){ fprintf(stderr,"%s:%d: \"EXPORT\" or \"LOCAL\" ignored before typedef.\n", zFilename, pFirst->nLine); nErr++; } if( (flags & (PS_Interface|PS_Export|PS_Local|DP_Cplusplus))==0 ){ /* It is illegal to duplicate a typedef in C (but OK in C++). ** So don't record typedefs that aren't within a C++ file or ** within #if INTERFACE..#endif */ return nErr; } if( (flags & (PS_Interface|PS_Export|PS_Local))==0 && proto_static==0 ){ /* Ignore typedefs that are not with "#if INTERFACE..#endif" unless ** the "-local" command line option is used. */ return nErr; } if( (flags & (PS_Interface|PS_Export))==0 ){ /* typedefs are always local, unless within #if INTERFACE..#endif */ isLocal = 1; } }else if( flags & (PS_Static|PS_Local2) ){ if( proto_static==0 && (flags & PS_Local2)==0 ){ /* Don't record static variables unless the "-local" command line ** 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; }else if( isStaticAssert(pFirst) ){ return 0; } 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 ){ if( pFirst->nText==4 && strncmp(pFirst->zText,"enum",4)==0 ){ /* Ignore completely anonymous enums. See documentation section 3.8.1. */ return nErr; }else{ fprintf(stderr,"%s:%d: Can't find a name for the object declared here.\n", zFilename, pFirst->nLine); return nErr+1; } } #ifdef DEBUG if( debugMask & PARSER ){ if( flags & PS_Typedef ){ printf("**** Found typedef %.*s at line %d...\n", pName->nText, pName->zText, pName->nLine); }else if( isVar ){ printf("**** Found variable %.*s at line %d...\n", pName->nText, pName->zText, pName->nLine); }else{ printf("**** Found prototype %.*s at line %d...\n", pName->nText, pName->zText, pName->nLine); } PrintTokens(pFirst,pEnd->pPrev); printf(";\n"); } #endif pDecl = CreateDecl(pName->zText,pName->nText); if( (flags & PS_Typedef) ){ DeclSetProperty(pDecl, TY_Typedef); }else if( isVar ){ DeclSetProperty(pDecl,DP_ExternReqd | TY_Variable); if( !(flags & DP_Cplusplus) ){ DeclSetProperty(pDecl,DP_ExternCReqd); } }else{ DeclSetProperty(pDecl, TY_Subroutine); if( !(flags & DP_Cplusplus) ){ DeclSetProperty(pDecl,DP_ExternCReqd); } } pDecl->pComment = pFirst->pComment; pDecl->zDecl = TokensToString(pFirst,pEnd->pPrev,";\n",0,0); if( isLocal || (flags & (PS_Local|PS_Local2))!=0 ){ DeclSetProperty(pDecl,DP_Local); }else if( flags & (PS_Export|PS_Export2) ){ DeclSetProperty(pDecl,DP_Export); } if( flags & DP_Cplusplus ){ DeclSetProperty(pDecl,DP_Cplusplus); } return nErr; } /* ** Push an if condition onto the if stack */ static void PushIfMacro( const char *zPrefix, /* A prefix, like "define" or "!" */ const char *zText, /* The condition */ int nText, /* Number of characters in zText */ int nLine, /* Line number where this macro occurs */ int flags /* Either 0, PS_Interface, PS_Export or PS_Local */ ){ Ifmacro *pIf; int nByte; nByte = sizeof(Ifmacro); if( zText ){ if( zPrefix ){ nByte += strlen(zPrefix) + 2; } nByte += nText + 1; } pIf = SafeMalloc( nByte ); if( zText ){ pIf->zCondition = (char*)&pIf[1]; if( zPrefix ){ int nPrefix = (int)strlen(zPrefix); memcpy(pIf->zCondition, zPrefix, nPrefix); pIf->zCondition[nPrefix] = '('; memcpy(&pIf->zCondition[nPrefix+1], zText, nText); memcpy(&pIf->zCondition[nPrefix+nText+1], ")", 2); }else{ memcpy(pIf->zCondition, zText, nText); pIf->zCondition[nText] = 0; } }else{ pIf->zCondition = 0; } pIf->nLine = nLine; pIf->flags = flags; pIf->pNext = ifStack; ifStack = pIf; } /* ** This routine is called to handle all preprocessor directives. ** ** This routine will recompute the value of *pPresetFlags to be the ** logical or of all flags on all nested #ifs. The #ifs that set flags ** are as follows: ** ** conditional flag set ** ------------------------ -------------------- ** #if INTERFACE PS_Interface ** #if EXPORT_INTERFACE PS_Export ** #if LOCAL_INTERFACE PS_Local ** ** For example, if after processing the preprocessor token given ** by pToken there is an "#if INTERFACE" on the preprocessor ** stack, then *pPresetFlags will be set to PS_Interface. */ static int ParsePreprocessor(Token *pToken, int flags, int *pPresetFlags){ const char *zCmd; int nCmd; const char *zArg; int nArg; int nErr = 0; Ifmacro *pIf; zCmd = &pToken->zText[1]; while( isspace(*zCmd) && *zCmd!='\n' ){ zCmd++; } if( !isalpha(*zCmd) ){ return 0; } 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++; } if( *zArg==0 || *zArg=='\n' ){ return 0; } for(nArg=0; ISALNUM(zArg[nArg]); nArg++){} if( nArg==0 ){ return 0; } pDecl = CreateDecl(zArg,nArg); pDecl->pComment = pToken->pComment; DeclSetProperty(pDecl,TY_Macro); pDecl->zDecl = SafeMalloc( pToken->nText + 2 ); memcpy(pDecl->zDecl, pToken->zText, pToken->nText); memcpy(&pDecl->zDecl[pToken->nText], "\n", 2); 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++; } for(nArg=0; !isspace(zArg[nArg]); nArg++){} if( (zArg[0]=='"' && zArg[nArg-1]!='"') ||(zArg[0]=='<' && zArg[nArg-1]!='>') ){ fprintf(stderr,"%s:%d: malformed #include statement.\n", zFilename,pToken->nLine); return 1; } zIf = GetIfString(); if( zIf ){ pInclude = SafeMalloc( sizeof(Include) + nArg*2 + strlen(zIf) + 10 ); pInclude->zFile = (char*)&pInclude[1]; pInclude->zLabel = &pInclude->zFile[nArg+1]; memcpy(pInclude->zFile, zArg, nArg); pInclude->zFile[nArg] = 0; memcpy(pInclude->zLabel, zArg, nArg); pInclude->zLabel[nArg] = ':'; memcpy(&pInclude->zLabel[nArg+1], zIf, strlen(zIf)+1); pInclude->zIf = &pInclude->zLabel[nArg+1]; SafeFree(zIf); }else{ pInclude = SafeMalloc( sizeof(Include) + nArg + 1 ); pInclude->zFile = (char*)&pInclude[1]; memcpy(pInclude->zFile, zArg, nArg); pInclude->zFile[nArg] = 0; pInclude->zIf = 0; pInclude->zLabel = pInclude->zFile; } pInclude->pNext = includeList; includeList = pInclude; }else if( nCmd==2 && strncmp(zCmd,"if",2)==0 ){ /* ** Push an #if. Watch for the special cases of INTERFACE ** and EXPORT_INTERFACE and LOCAL_INTERFACE */ zArg = &zCmd[2]; while( *zArg && isspace(*zArg) && *zArg!='\n' ){ zArg++; } if( *zArg==0 || *zArg=='\n' ){ return 0; } nArg = pToken->nText + (int)(pToken->zText - zArg); if (pToken->zText[pToken->nText-1] == '\r') { nArg--; } if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){ PushIfMacro(0,0,0,pToken->nLine,PS_Interface); }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){ 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 if( nArg==15 && strncmp(zArg,"MAKEHEADERS_STOPLOCAL_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; } nArg = pToken->nText + (int)(pToken->zText - zArg); if (pToken->zText[pToken->nText-1] == '\r') { nArg--; } PushIfMacro("defined",zArg,nArg,pToken->nLine,0); }else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){ /* ** Push an #ifndef. */ zArg = &zCmd[6]; while( *zArg && isspace(*zArg) && *zArg!='\n' ){ zArg++; } if( *zArg==0 || *zArg=='\n' ){ return 0; } nArg = pToken->nText + (int)(pToken->zText - zArg); if (pToken->zText[pToken->nText-1] == '\r') { nArg--; } 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; int presetFlags = initFlags; int resetFlag = 0; includeList = 0; while( pList ){ switch( pList->eType ){ case TT_EOF: goto end_of_loop; case TT_Preprocessor: nErr += ParsePreprocessor(pList,flags,&presetFlags); pStart = 0; presetFlags |= initFlags; flags = presetFlags; break; case TT_Other: switch( pList->zText[0] ){ 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; } if( pList==0 ) goto end_of_loop; flags = presetFlags; break; case ':': if( pList->zText[1]==':' ){ flags |= PS_Method; } break; default: break; } break; case TT_Braces: nErr += ProcessProcedureDef(pStart,pList,flags); pStart = 0; flags = presetFlags; break; case TT_Id: if( pStart==0 ){ pStart = pList; flags = presetFlags; } resetFlag = 0; switch( pList->zText[0] ){ case 'c': if( pList->nText==5 && strncmp(pList->zText,"class",5)==0 ){ nErr += ProcessTypeDecl(pList,flags,&resetFlag); } break; case 'E': if( pList->nText==6 && strncmp(pList->zText,"EXPORT",6)==0 ){ flags |= PS_Export2; /* pStart = 0; */ } break; case 'e': if( pList->nText==4 && strncmp(pList->zText,"enum",4)==0 ){ if( pList->pNext && pList->pNext->eType==TT_Braces ){ pList = pList->pNext; }else{ nErr += ProcessTypeDecl(pList,flags,&resetFlag); } }else if( pList->nText==6 && strncmp(pList->zText,"extern",6)==0 ){ pList = pList->pNext; if( pList && pList->nText==3 && strncmp(pList->zText,"\"C\"",3)==0 ){ pList = pList->pNext; flags &= ~DP_Cplusplus; }else{ flags |= PS_Extern; } pStart = pList; } break; case 'i': if( pList->nText==6 && strncmp(pList->zText,"inline",6)==0 && (flags & PS_Static)==0 ){ nErr += ProcessInlineProc(pList,flags,&resetFlag); } break; case 'L': if( pList->nText==5 && strncmp(pList->zText,"LOCAL",5)==0 ){ flags |= PS_Local2; pStart = pList; } break; case 'P': if( pList->nText==6 && strncmp(pList->zText, "PUBLIC",6)==0 ){ flags |= PS_Public; pStart = pList; }else if( pList->nText==7 && strncmp(pList->zText, "PRIVATE",7)==0 ){ flags |= PS_Private; pStart = pList; }else if( pList->nText==9 && strncmp(pList->zText,"PROTECTED",9)==0 ){ flags |= PS_Protected; pStart = pList; } break; case 's': if( pList->nText==6 && strncmp(pList->zText,"struct",6)==0 ){ if( pList->pNext && pList->pNext->eType==TT_Braces ){ pList = pList->pNext; }else{ nErr += ProcessTypeDecl(pList,flags,&resetFlag); } }else if( pList->nText==6 && strncmp(pList->zText,"static",6)==0 ){ flags |= PS_Static; } break; case 't': if( pList->nText==7 && strncmp(pList->zText,"typedef",7)==0 ){ flags |= PS_Typedef; } break; case 'u': if( pList->nText==5 && strncmp(pList->zText,"union",5)==0 ){ if( pList->pNext && pList->pNext->eType==TT_Braces ){ pList = pList->pNext; }else{ nErr += ProcessTypeDecl(pList,flags,&resetFlag); } } break; default: break; } if( resetFlag!=0 ){ while( pList && pList->zText[0]!=resetFlag ){ pList = pList->pNext; } if( pList==0 ) goto end_of_loop; pStart = 0; flags = presetFlags; } break; case TT_String: case TT_Number: break; default: pStart = pList; flags = presetFlags; break; } pList = pList->pNext; } end_of_loop: /* Verify that all #ifs have a matching "#endif" */ while( ifStack ){ Ifmacro *pIf = ifStack; ifStack = pIf->pNext; fprintf(stderr,"%s:%d: This '#if' has no '#endif'\n",zFilename, pIf->nLine); SafeFree(pIf); } return nErr; } /* ** If the given Decl object has a non-null zExtra field, then the text ** of that zExtra field needs to be inserted in the middle of the ** zDecl field before the last "}" in the zDecl. This routine does that. ** If the zExtra is NULL, this routine is a no-op. ** ** zExtra holds extra method declarations for classes. The declarations ** have to be inserted into the class definition. */ static void InsertExtraDecl(Decl *pDecl){ int i; String str; if( pDecl==0 || pDecl->zExtra==0 || pDecl->zDecl==0 ) return; i = strlen(pDecl->zDecl) - 1; while( i>0 && pDecl->zDecl[i]!='}' ){ i--; } StringInit(&str); StringAppend(&str, pDecl->zDecl, i); StringAppend(&str, pDecl->zExtra, 0); StringAppend(&str, &pDecl->zDecl[i], 0); SafeFree(pDecl->zDecl); SafeFree(pDecl->zExtra); 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); if( DeclHasProperty(pDecl,DP_Local) && pDecl->zFile!=zFilename ){ DeclSetProperty(pDecl,DP_Forward|DP_Declared); } } } /* ** Forward declaration of the ScanText() function. */ static void ScanText(const char*, GenState *pState); /* ** The output in pStr is currently within an #if CONTEXT where context ** is equal to *pzIf. (*pzIf might be NULL to indicate that we are ** not within any #if at the moment.) We are getting ready to output ** some text that needs to be within the context of "#if NEW" where ** NEW is zIf. Make an appropriate change to the context. */ static void ChangeIfContext( const char *zIf, /* The desired #if context */ GenState *pState /* Current state of the code generator */ ){ if( zIf==0 ){ if( pState->zIf==0 ) return; StringAppend(pState->pStr,"#endif\n",0); pState->zIf = 0; }else{ if( pState->zIf ){ if( strcmp(zIf,pState->zIf)==0 ) return; StringAppend(pState->pStr,"#endif\n",0); pState->zIf = 0; } ScanText(zIf, pState); if( pState->zIf!=0 ){ StringAppend(pState->pStr,"#endif\n",0); } StringAppend(pState->pStr,"#if ",0); StringAppend(pState->pStr,zIf,0); StringAppend(pState->pStr,"\n",0); pState->zIf = zIf; } } /* ** Add to the string pStr a #include of every file on the list of ** include files pInclude. The table pTable contains all files that ** have already been #included at least once. Don't add any ** duplicates. Update pTable with every new #include that is added. */ static void AddIncludes( Include *pInclude, /* Write every #include on this list */ GenState *pState /* Current state of the code generator */ ){ if( pInclude ){ if( pInclude->pNext ){ AddIncludes(pInclude->pNext,pState); } if( IdentTableInsert(pState->pTable,pInclude->zLabel,0) ){ ChangeIfContext(pInclude->zIf,pState); StringAppend(pState->pStr,"#include ",0); StringAppend(pState->pStr,pInclude->zFile,0); StringAppend(pState->pStr,"\n",1); } } } /* ** Add to the string pStr a declaration for the object described ** in pDecl. ** ** If pDecl has already been declared in this file, detect that ** fact and abort early. Do not duplicate a declaration. ** ** If the needFullDecl flag is false and this object has a forward ** declaration, then supply the forward declaration only. A later ** call to CompleteForwardDeclarations() will finish the declaration ** for us. But if needFullDecl is true, we must supply the full ** declaration now. Some objects do not have a forward declaration. ** For those objects, we must print the full declaration now. ** ** Because it is illegal to duplicate a typedef in C, care is taken ** to insure that typedefs for the same identifier are only issued once. */ static void DeclareObject( Decl *pDecl, /* The thing to be declared */ GenState *pState, /* Current state of the code generator */ int needFullDecl /* Must have the full declaration. A forward * declaration isn't enough */ ){ 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) ){ DeclSetProperty(p,DP_Forward); if( strncmp(p->zFwd,"typedef",7)==0 ){ if( doneTypedef ) continue; doneTypedef = 1; } ChangeIfContext(p->zIf,pState); StringAppend(pState->pStr,isCpp ? p->zFwdCpp : p->zFwd,0); } } } /* ** Early out if everything is already suitably declared. ** ** This is a very important step because it prevents us from ** executing the code the follows in a recursive call to this ** function with the same value for pDecl. */ flag = needFullDecl ? DP_Declared|DP_Forward : DP_Forward; for(p=pDecl; p; p=p->pSameName){ if( !DeclHasProperty(p,flag) ) break; } if( p==0 ){ return; } /* ** Make sure we have all necessary #includes */ for(p=pDecl; p; p=p->pSameName){ AddIncludes(p->pInclude,pState); } /* ** Go ahead an mark everything as being declared, to prevent an ** infinite loop thru the ScanText() function. At the same time, ** we decide which objects need a full declaration and mark them ** 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); } } /* ** Call ScanText() recursively (this routine is called from ScanText()) ** to include declarations required to come before these declarations. */ for(p=pDecl; p; p=p->pSameName){ if( DeclHasProperty(p,DP_Flag) ){ if( p->zDecl[0]=='#' ){ ScanText(&p->zDecl[1],pState); }else{ InsertExtraDecl(p); ScanText(p->zDecl,pState); } } } /* ** Output the declarations. Do this in two passes. First ** output everything that isn't a typedef. Then go back and ** get the typedefs by the same name. */ for(p=pDecl; p; p=p->pSameName){ if( DeclHasProperty(p,DP_Flag) && !DeclHasProperty(p,TY_Typedef) ){ if( DeclHasAnyProperty(p,TY_Enumeration) ){ if( doneTypedef ) continue; doneTypedef = 1; } ChangeIfContext(p->zIf,pState); if( !isCpp && DeclHasAnyProperty(p,DP_ExternReqd) ){ StringAppend(pState->pStr,"extern ",0); }else if( isCpp && DeclHasProperty(p,DP_Cplusplus|DP_ExternReqd) ){ StringAppend(pState->pStr,"extern ",0); }else if( isCpp && DeclHasAnyProperty(p,DP_ExternCReqd|DP_ExternReqd) ){ StringAppend(pState->pStr,"extern \"C\" ",0); } InsertExtraDecl(p); StringAppend(pState->pStr,p->zDecl,0); if( !isCpp && DeclHasProperty(p,DP_Cplusplus) ){ fprintf(stderr, "%s: C code ought not reference the C++ object \"%s\"\n", pState->zFilename, p->zName); pState->nErr++; } DeclClearProperty(p,DP_Flag); } } for(p=pDecl; p && !doneTypedef; p=p->pSameName){ if( DeclHasProperty(p,DP_Flag) ){ /* This has to be a typedef */ doneTypedef = 1; ChangeIfContext(p->zIf,pState); InsertExtraDecl(p); StringAppend(pState->pStr,p->zDecl,0); } } } /* ** This routine scans the input text given, and appends to the ** string in pState->pStr the text of any declarations that must ** occur before the text in zText. ** ** If an identifier in zText is immediately followed by '*', then ** only forward declarations are needed for that identifier. If the ** identifier name is not followed immediately by '*', we must supply ** a full declaration. */ static void ScanText( const char *zText, /* The input text to be scanned */ GenState *pState /* Current state of the code generator */ ){ int nextValid = 0; /* True is sNext contains valid data */ InStream sIn; /* The input text */ Token sToken; /* The current token being examined */ Token sNext; /* The next non-space token */ /* printf("BEGIN SCAN TEXT on %s\n", zText); */ sIn.z = zText; sIn.i = 0; sIn.nLine = 1; while( sIn.z[sIn.i]!=0 ){ if( nextValid ){ sToken = sNext; nextValid = 0; }else{ GetNonspaceToken(&sIn,&sToken); } if( sToken.eType==TT_Id ){ int needFullDecl; /* True if we need to provide the full declaration, ** not just the forward declaration */ Decl *pDecl; /* The declaration having the name in sToken */ /* ** 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{ needFullDecl = 1; nextValid = sNext.eType==TT_Id; } /* ** Generate the needed declaration. */ DeclareObject(pDecl,pState,needFullDecl); }else if( sToken.eType==TT_Preprocessor ){ sIn.i -= sToken.nText - 1; } } /* printf("END SCANTEXT\n"); */ } /* ** Provide a full declaration to any object which so far has had only ** a forward declaration. */ 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 ); } /* ** Generate an include file for the given source file. Return the number ** of errors encountered. ** ** if nolocal_flag is true, then we do not generate declarations for ** objected marked DP_Local. */ static int MakeHeader(InFile *pFile, FILE *report, int nolocal_flag){ int nErr = 0; GenState sState; String outStr; IdentTable includeTable; Ident *pId; char *zNewVersion; char *zOldVersion; if( pFile->zHdr==0 || *pFile->zHdr==0 ) return 0; sState.pStr = &outStr; StringInit(&outStr); StringAppend(&outStr,zTopLine,nTopLine); sState.pTable = &includeTable; memset(&includeTable,0,sizeof(includeTable)); sState.zIf = 0; sState.nErr = 0; sState.zFilename = pFile->zSrc; sState.flags = pFile->flags & DP_Cplusplus; ResetDeclFlags(nolocal_flag ? "no" : pFile->zSrc); for(pId = pFile->idTable.pList; pId; pId=pId->pNext){ Decl *pDecl = FindDecl(pId->zName,0); if( pDecl ){ DeclareObject(pDecl,&sState,1); } } CompleteForwardDeclarations(&sState); ChangeIfContext(0,&sState); nErr += sState.nErr; zOldVersion = ReadFile(pFile->zHdr); zNewVersion = StringGet(&outStr); if( report ) fprintf(report,"%s: ",pFile->zHdr); if( zOldVersion==0 ){ if( report ) fprintf(report,"updated\n"); if( WriteFile(pFile->zHdr,zNewVersion) ){ fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr); nErr++; } }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){ if( report ) fprintf(report,"error!\n"); fprintf(stderr, "%s: Can't overwrite this file because it wasn't previously\n" "%*s generated by 'makeheaders'.\n", pFile->zHdr, (int)strlen(pFile->zHdr), ""); nErr++; }else if( strcmp(zOldVersion,zNewVersion)!=0 ){ if( report ) fprintf(report,"updated\n"); 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 ** declarations. If the forExport flag is true, then only those ** objects that are exported are included in the header file. */ static int MakeGlobalHeader(int forExport){ GenState sState; String outStr; IdentTable includeTable; Decl *pDecl; sState.pStr = &outStr; StringInit(&outStr); /* StringAppend(&outStr,zTopLine,nTopLine); */ sState.pTable = &includeTable; memset(&includeTable,0,sizeof(includeTable)); sState.zIf = 0; sState.nErr = 0; sState.zFilename = "(all)"; sState.flags = 0; ResetDeclFlags(0); for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){ if( forExport==0 || DeclHasProperty(pDecl,DP_Export) ){ 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. */ static int ClipTrailingNewline(char *z){ int n = strlen(z); while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ){ n--; } return n; } /* ** Dump the entire declaration list for debugging purposes */ static void DumpDeclList(void){ Decl *pDecl; for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){ printf("**** %s from file %s ****\n",pDecl->zName,pDecl->zFile); if( pDecl->zIf ){ printf("If: [%.*s]\n",ClipTrailingNewline(pDecl->zIf),pDecl->zIf); } if( pDecl->zFwd ){ printf("Decl: [%.*s]\n",ClipTrailingNewline(pDecl->zFwd),pDecl->zFwd); } if( pDecl->zDecl ){ InsertExtraDecl(pDecl); printf("Def: [%.*s]\n",ClipTrailingNewline(pDecl->zDecl),pDecl->zDecl); } if( pDecl->flags ){ static struct { int mask; char *desc; } flagSet[] = { { TY_Class, "class" }, { TY_Enumeration, "enum" }, { TY_Structure, "struct" }, { TY_Union, "union" }, { TY_Variable, "variable" }, { TY_Subroutine, "function" }, { TY_Typedef, "typedef" }, { TY_Macro, "macro" }, { DP_Export, "export" }, { DP_Local, "local" }, { DP_Cplusplus, "C++" }, }; int i; printf("flags:"); for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){ if( flagSet[i].mask & pDecl->flags ){ printf(" %s", flagSet[i].desc); } } printf("\n"); } if( pDecl->pInclude ){ Include *p; printf("includes:"); for(p=pDecl->pInclude; p; p=p->pNext){ printf(" %s",p->zFile); } printf("\n"); } } } #endif /* ** When the "-doc" command-line option is used, this routine is called ** to print all of the database information to standard output. */ static void DocumentationDump(void){ Decl *pDecl; static struct { int mask; char flag; } flagSet[] = { { TY_Class, 'c' }, { TY_Enumeration, 'e' }, { TY_Structure, 's' }, { TY_Union, 'u' }, { TY_Variable, 'v' }, { TY_Subroutine, 'f' }, { TY_Typedef, 't' }, { TY_Macro, 'm' }, { DP_Export, 'x' }, { DP_Local, 'l' }, { DP_Cplusplus, '+' }, }; for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){ int i; int nLabel = 0; char *zDecl; char zLabel[50]; for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){ if( DeclHasProperty(pDecl,flagSet[i].mask) ){ zLabel[nLabel++] = flagSet[i].flag; } } if( nLabel==0 ) continue; zLabel[nLabel] = 0; InsertExtraDecl(pDecl); zDecl = pDecl->zDecl; if( zDecl==0 ) zDecl = pDecl->zFwd; printf("%s %s %s %p %d %d %d %d %d\n", pDecl->zName, zLabel, pDecl->zFile, pDecl->pComment, pDecl->pComment ? pDecl->pComment->nText+1 : 0, pDecl->zIf ? (int)strlen(pDecl->zIf)+1 : 0, zDecl ? (int)strlen(zDecl) : 0, pDecl->pComment ? pDecl->pComment->nLine : 0, pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0 ); if( pDecl->pComment ){ printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText); } if( pDecl->zIf ){ printf("%s\n",pDecl->zIf); } if( zDecl ){ printf("%s",zDecl); } if( pDecl->tokenCode.nText ){ printf("%.*s\n",pDecl->tokenCode.nText, pDecl->tokenCode.zText); } } } /* ** Given the complete text of an input file, this routine prints a ** documentation record for the header comment at the beginning of the ** file (if the file has a header comment.) */ void PrintModuleRecord(const char *zFile, const char *zFilename){ int i; static int addr = 5; while( isspace(*zFile) ){ zFile++; } if( *zFile!='/' || zFile[1]!='*' ) return; for(i=2; zFile[i] && (zFile[i-1]!='/' || zFile[i-2]!='*'); i++){} if( zFile[i]==0 ) return; printf("%s M %s %d %d 0 0 0 0\n%.*s\n", zFilename, zFilename, addr, i+1, i, zFile); addr += 4; } /* ** Given an input argument to the program, construct a new InFile ** object. */ 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. */ zSrc = zArg; for(nSrc=2; zSrc[nSrc] && zArg[nSrc]!=':'; nSrc++){} pFile = SafeMalloc( sizeof(InFile) ); memset(pFile,0,sizeof(InFile)); pFile->zSrc = StrDup(zSrc,nSrc); /* Figure out if we are dealing with C or C++ code. Assume any ** file with ".c" or ".h" is C code and all else is C++. */ if( nSrc>2 && zSrc[nSrc-2]=='.' && (zSrc[nSrc-1]=='c' || zSrc[nSrc-1]=='h')){ pFile->flags &= ~DP_Cplusplus; }else{ pFile->flags |= DP_Cplusplus; } /* ** If a separate header file is specified, use it */ if( zSrc[nSrc]==':' ){ int nHdr; char *zHdr; zHdr = &zSrc[nSrc+1]; for(nHdr=0; zHdr[nHdr]; nHdr++){} pFile->zHdr = StrDup(zHdr,nHdr); } /* Look for any 'c' or 'C' in the suffix of the file name and change ** that character to 'h' or 'H' respectively. If no 'c' or 'C' is found, ** then assume we are dealing with a header. */ else{ int foundC = 0; pFile->zHdr = StrDup(zSrc,nSrc); for(i = nSrc-1; i>0 && pFile->zHdr[i]!='.'; i--){ if( pFile->zHdr[i]=='c' ){ foundC = 1; pFile->zHdr[i] = 'h'; }else if( pFile->zHdr[i]=='C' ){ foundC = 1; pFile->zHdr[i] = 'H'; } } if( !foundC ){ 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 ** work around. ** ** If the parameters "-f FILENAME" appear anywhere on the command line, ** then the named file is scanned for additional command line arguments. ** These arguments are substituted in place of the "FILENAME" argument ** in the original argument list. ** ** This first parameter to this routine is the index of the "-f" ** parameter in the argv[] array. The argc and argv are passed by ** pointer so that they can be changed. ** ** Parsing of the parameters in the file is very simple. Parameters ** can be separated by any amount of white-space (including newlines ** and carriage returns.) There are now quoting characters of any ** kind. The length of a token is limited to about 1000 characters. */ static void AddParameters(int index, int *pArgc, char ***pArgv){ int argc = *pArgc; /* The original argc value */ char **argv = *pArgv; /* The original argv value */ int newArgc; /* Value for argc after inserting new arguments */ char **zNew = 0; /* The new argv after this routine is done */ char *zFile; /* Name of the input file */ int nNew = 0; /* Number of new entries in the argv[] file */ int nAlloc = 0; /* Space allocated for zNew[] */ int i; /* Loop counter */ int n; /* Number of characters in a new argument */ int c; /* Next character of input */ int startOfLine = 1; /* True if we are where '#' can start a comment */ FILE *in; /* The input file */ char zBuf[1000]; /* A single argument is accumulated here */ if( index+1==argc ) return; zFile = argv[index+1]; in = fopen(zFile,"r"); if( in==0 ){ fprintf(stderr,"Can't open input file \"%s\"\n",zFile); exit(1); } 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; while( c!=EOF && !isspace(c) ){ if( n<sizeof(zBuf)-1 ){ zBuf[n++] = c; } startOfLine = 0; c = getc(in); } zBuf[n] = 0; if( n>0 ){ 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 ); } } } } fclose(in); newArgc = argc + nNew - 1; for(i=0; i<=index; i++){ zNew[i] = argv[i]; } for(i=nNew + index + 1; i<newArgc; i++){ zNew[i] = argv[i + 1 - nNew]; } zNew[newArgc] = 0; *pArgc = newArgc; *pArgv = zNew; } #ifdef NOT_USED /* ** Return the time that the given file was last modified. If we can't ** locate the file (because, for example, it doesn't exist), then ** return 0. */ static unsigned int ModTime(const char *zFilename){ unsigned int mTime = 0; struct stat sStat; if( stat(zFilename,&sStat)==0 ){ mTime = sStat.st_mtime; } return mTime; } #endif /* ** Print a usage comment for this program. */ static void Usage(const char *argv0, const char *argvN){ fprintf(stderr,"%s: Illegal argument \"%s\"\n",argv0,argvN); fprintf(stderr,"Usage: %s [options] filename...\n" "Options:\n" " -h Generate a single .h to standard output.\n" " -H Like -h, but only output EXPORT declarations.\n" " -v (verbose) Write status information to the screen.\n" " -doc Generate no header files. Instead, output information\n" " that can be used by an automatic program documentation\n" " and cross-reference generator.\n" " -local Generate prototypes for \"static\" functions and\n" " procedures.\n" " -f FILE Read additional command-line arguments from the file named\n" " \"FILE\".\n" #ifdef DEBUG " -! MASK Set the debugging mask to the number \"MASK\".\n" #endif " -- Treat all subsequent comment-line parameters as filenames,\n" " even if they begin with \"-\".\n", argv0 ); } /* ** 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" "#define PROTECTED\n" ; #if TEST==0 int main(int argc, char **argv){ int i; /* Loop counter */ int nErr = 0; /* Number of errors encountered */ Token *pList; /* List of input tokens for one file */ InFile *pFileList = 0;/* List of all input files */ InFile *pTail = 0; /* Last file on the list */ InFile *pFile; /* for looping over the file list */ int h_flag = 0; /* True if -h is present. Output unified header */ int H_flag = 0; /* True if -H is present. Output EXPORT header */ int v_flag = 0; /* Verbose */ int noMoreFlags; /* True if -- has been seen. */ FILE *report; /* Send progress reports to this, if not NULL */ noMoreFlags = 0; for(i=1; i<argc; i++){ if( argv[i][0]=='-' && !noMoreFlags ){ switch( argv[i][1] ){ case 'h': h_flag = 1; break; case 'H': H_flag = 1; break; case 'v': v_flag = 1; break; case 'd': doc_flag = 1; proto_static = 1; break; case 'l': proto_static = 1; break; case 'f': AddParameters(i, &argc, &argv); break; case '-': noMoreFlags = 1; break; #ifdef DEBUG case '!': i++; debugMask = strtol(argv[i],0,0); break; #endif default: Usage(argv[0],argv[i]); return 1; } }else{ pFile = CreateInFile(argv[i],&nErr); if( pFile ){ if( pFileList ){ pTail->pNext = pFile; pTail = pFile; }else{ pFileList = pTail = pFile; } } } } if( h_flag && H_flag ){ h_flag = 0; } if( v_flag ){ report = (h_flag || H_flag) ? stderr : stdout; }else{ report = 0; } if( nErr>0 ){ return nErr; } for(pFile=pFileList; pFile; pFile=pFile->pNext){ char *zFile; zFilename = pFile->zSrc; if( zFilename==0 ) continue; zFile = ReadFile(zFilename); if( zFile==0 ){ fprintf(stderr,"Can't read input file \"%s\"\n",zFilename); nErr++; continue; } if( strncmp(zFile,zTopLine,nTopLine)==0 ){ pFile->zSrc = 0; }else{ if( report ) fprintf(report,"Reading %s...\n",zFilename); pList = TokenizeFile(zFile,&pFile->idTable); if( pList ){ nErr += ParseFile(pList,pFile->flags); FreeTokenList(pList); }else if( zFile[0]==0 ){ fprintf(stderr,"Input file \"%s\" is empty.\n", zFilename); nErr++; }else{ fprintf(stderr,"Errors while processing \"%s\"\n", zFilename); nErr++; } } if( !doc_flag ) SafeFree(zFile); if( doc_flag ) PrintModuleRecord(zFile,zFilename); } if( nErr>0 ){ return nErr; } #ifdef DEBUG if( debugMask & DECL_DUMP ){ DumpDeclList(); return nErr; } #endif if( doc_flag ){ DocumentationDump(); return nErr; } zFilename = "--internal--"; pList = TokenizeFile(zInit,0); if( pList==0 ){ return nErr+1; } ParseFile(pList,PS_Interface); FreeTokenList(pList); if( h_flag || H_flag ){ nErr += MakeGlobalHeader(H_flag); }else{ for(pFile=pFileList; pFile; pFile=pFile->pNext){ if( pFile->zSrc==0 ) continue; nErr += MakeHeader(pFile,report,0); } } return nErr; } #endif |
Added tools/makeheaders.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 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 | <html> <head><title>The Makeheaders Program</title></head> <body bgcolor=white> <h1 align=center>The Makeheaders Program</h1> <p> This document describes <em>makeheaders</em>, a tool that automatically generates “<code>.h</code>” files for a C or C++ programming project. </p> <h2>Table Of Contents</h2> <ul> <li><a href="#H0002">1,0 Background</a> <ul> <li><a href="#H0003">1.1 Problems With The Traditional Approach</a> <li><a href="#H0004">1.2 The Makeheaders Solution</a> </ul> <li><a href="#H0005">2.0 Running The Makeheaders Program</a> <li><a href="#H0006">3.0 Preparing Source Files For Use With Makeheaders</a> <ul> <li><a href="#H0007">3.1 The Basic Setup</a> <li><a href="#H0008">3.2 What Declarations Get Copied</a> <li><a href="#H0009">3.3 How To Avoid Having To Write Any Header Files</a> <li><a href="#H0010">3.4 Designating Declarations For Export</a> <li><a href="#H0011">3.5 Local declarations processed by makeheaders</a> <li><a href="#H0012">3.6 Using Makeheaders With C++ Code</a> <li><a href="#H0013">3.7 Conditional Compilation</a> <li><a href="#H0014">3.8 Caveats</a> </ul> <li><a href="#H0015">4.0 Using Makeheaders To Generate Documentation</a> <li><a href="#H0016">5.0 Compiling The Makeheaders Program</a> <li><a href="#H0017">6.0 History</a> <li><a href="#H0018">7.0 Summary And Conclusion</a> </ul><a name="H0002"></a> <h2>1.0 Background</h2> <p> A piece of C source code can be one of two things: a <em>declaration</em> or a <em>definition</em>. A declaration is source text that gives information to the compiler but doesn't directly result in any code being generated. A definition is source text that results in executable machine instructions or initialization data. (These two terms are sometimes used inconsistently by other authors. In particular, many people reverse the meanings of these words when discussing Pascal or Ada code. The meanings described here are the same as used in the ANSI-C standards document.) </p> <p> Declarations in C include things such as the following: <ul> <li> Typedefs. <li> Structure, union and enumeration declarations. <li> Function and procedure prototypes. <li> Preprocessor macros and #defines. <li> “<code>extern</code>” variable declarations. </ul> </p> <p> Definitions in C, on the other hand, include these kinds of things: <ul> <li> Variable definitions. <li> The bodies of functions and procedures. <li> Initialization data. </ul> </p> <p> The distinction between a declaration and a definition is common in modern software engineering. Another way of looking at the difference is that the declaration is the <em>interface</em> and the definition is the <em>implementation</em>. </p> <p> In C programs, it has always been the tradition that declarations are put in files with the “<code>.h</code>” suffix and definitions are placed in “<code>.c</code>” files. The .c files contain “<code>#include</code>” preprocessor statements that cause the contents of .h files to be included as part of the source code when the .c file is compiled. In this way, the .h files define the interface to a subsystem and the .c files define how the subsystem is implemented. </p> <a name="H0003"></a> <h3>1.1 Problems With The Traditional Approach</h3> <p> As the art of computer programming continues to advance, and the size and complexity of programs continues to swell, the traditional C approach of placing declarations and definitions in separate files begins to present the programmer with logistics and maintenance problems. To wit: </p> <p> <ol> <p><li> In large codes with many source files, it becomes difficult to determine which .h files should be included in which .c files. <p><li> It is typically the case that a .h file will be forced to include another .h files, which in turn might include other .h files, and so forth. The .c file must be recompiled when any of the .h files in this chain are altered, but it can be difficult to determine what .h files are found in the include chain. A frequent Makefile error is to omit some .h files from a dependency list even though those files are on the include file chain. <p><li> Some information is common to both the declaration and the definition of an object in C, and so must be repeated in both the .h and the .c files for that object. In a large project, it can become increasingly difficult to keep the two files in sync. <p><li> When a .c file includes a .h file and the .h files changes, the .c file must be recompiled, even if the part of the .h file that changed is not actually used by the .c file. In a large program, it is generally the case that almost every .c file ends up depending on one or two of the more important .h files, and so when those .h files change, the entire program must be recompiled. It also happens that those important .h files tend to be the ones that change most frequently. This means that the entire program must be recompiled frequently, leading to a lengthy modify-compile-test cycle and a corresponding decrease in programmer productivity. <p><li> The C programming language requires that declarations depending upon each other must occur in a particular order. In a program with complex, interwoven data structures, the correct declaration order can become very difficult to determine manually, especially when the declarations involved are spread out over several files. </ol> </p> <a name="H0004"></a> <h3>1.2 The Makeheaders Solution</h3> <p> The makeheaders program is designed to ameliorate the problems associated with the traditional C programming model by automatically generating the interface information in the .h files from interface information contained in other .h files and from implementation information in the .c files. When the makeheaders program is run, it scans the source files for a project, then generates a series of new .h files, one for each .c file. The generated .h files contain exactly those declarations required by the corresponding .c files, no more and no less. </p> <p> The makeheaders programming model overcomes all of the objections to the traditional C programming model. <ol> <p><li> Because all declarations needed by a .c file are contained in a single .h file, there is never any question about what .h files a .c will need to include. If the .c file is named <code>alpha.c</code> then it must include only the single .h file named <code>alpha.h</code>. (The .c file might also use some include files from the standard library, such as <code><stdio.h></code>, but that is another matter.) <p><li> The generated .h files do not include other .h files, and so there are no include chains to worry about. The file <code>alpha.c</code> depends on <code>alpha.h</code> and nothing more. <p><li> There is still duplication in the .h and the .c file, but because the duplicate information is automatically generated, it is no longer a problem. Simply rerun makeheaders to resynchronize everything. <p><li> The generated .h file contains the minimal set of declarations needed by the .c file. This means that when something changes, a minimal amount of recompilation is required to produce an updated executable. Experience has shown that this gives a dramatic improvement in programmer productivity by facilitating a rapid modify-compile-test cycle during development. <p><li> The makeheaders program automatically sorts declarations into the correct order, completely eliminating the wearisome and error-prone task of sorting declarations by hand. </ol> <p> <p> In addition, the makeheaders program is fast and unintrusive. It is a simple matter to incorporate makeheaders into a Makefile so that makeheaders will be run automatically whenever the project is rebuilt. And the burden of running makeheaders is light. It will easily process tens of thousands of lines of source code per second. </p> <a name="H0005"></a> <h2>2.0 Running The Makeheaders Program</h2> <p> The makeheaders program is very easy to run. If you have a collection of C source code and include files in the working directory, then you can run makeheaders to generate appropriate .h files using the following command: <pre> makeheaders *.[ch] </pre> That's really all there is to it! This command will generate one .h file for every .c file. Any .h files that were generated by a prior run of makeheaders are ignored, but manually entered .h files that contain structure declarations and so forth will be scanned and the declarations will be copied into the generated .h files as appropriate. But if makeheaders sees that the .h file that it has generated is no different from the .h file it generated last time, it doesn't update the file. This prevents the corresponding .c files from having to be needlessly recompiled. </p> <p> There are several options to the makeheaders program that can be used to alter its behavior. The default behavior is to write a single .h file for each .c file and to give the .h file the same base name as the .c file. Instead of generating a whole mess of .h files, you can, if you choose, generate a single big .h file that contains all declarations needed by all the .c files. Do this using the -h option to makeheaders. As follows: <pre> makeheaders -h *.[ch] >common.h </pre> With the -h option, the .h file is not actually written to a disk file but instead appears on standard output, where you are free to redirect it into the file of your choice. </p> <p> A similar option is -H. Like the lower-case -h option, big -H generates a single include file on standard output. But unlike small -h, the big -H only emits prototypes and declarations that have been designated as “exportable”. The idea is that -H will generate an include file that defines the interface to a library. More will be said about this in section 3.4. </p> <p> Sometimes you want the base name of the .c file and the .h file to be different. For example, suppose you want the include file for <code>alpha.c</code> to be called <code>beta.h</code>. In this case, you would invoke makeheaders as follows: <pre> makeheaders alpha.c:beta.h </pre> Any time a filename argument contains a colon, the name before the colon is taken to be the name of the .c file and the name after the colon is taken to be the name of the .h file. You can't use the shell's wildcard mechanism with this approach, but that normally isn't a problem in Makefiles, which is where this stuff comes in handy. </p> <p> If you want a particular file to be scanned by makeheaders but you don't want makeheaders to generate a header file for that file, then you can supply an empty header filename, like this: <pre> makeheaders alpha.c beta.c gamma.c: </pre> In this example, makeheaders will scan the three files named “<code>alpha.c</code>”, “<code>beta.c</code>” and “<code>gamma.c</code>” but because of the colon on the end of third filename it will only generate headers for the first two files. Unfortunately, it is not possible to get makeheaders to process any file whose name contains a colon. </p> <p> In a large project, the length of the command line for makeheaders can become very long. If the operating system doesn't support long command lines (example: DOS and Win32) you may not be able to list all of the input files in the space available. In that case, you can use the “<code>-f</code>” option followed by the name of a file to cause makeheaders to read command line options and filename from the file instead of from the command line. For example, you might prepare a file named “<code>mkhdr.dat</code>” that contains text like this: <pre> src/alpha.c:hdr/alpha.h src/beta.c:hdr/beta.h src/gamma.c:hdr/gamma.h ... </pre> Then invoke makeheaders as follows: <pre> makeheaders -f mkhdr.dat </pre> </p> <p> The “<code>-local</code>” option causes makeheaders to generate of prototypes for “<code>static</code>” functions and procedures. Such prototypes are normally omitted. </p> <p> Finally, makeheaders also includes a “<code>-doc</code>” option. This command line option prevents makeheaders from generating any headers at all. Instead, makeheaders will write to standard output information about every definition and declaration that it encounters in its scan of source files. The information output includes the type of the definition or declaration and any comment that precedes the definition or declaration. The output is in a format that can be easily parsed, and is intended to be read by another program that will generate documentation about the program. We'll talk more about this feature later. </p> <p> If you forget what command line options are available, or forget their exact name, you can invoke makeheaders using an unknown command line option (like “<code>--help</code>” or “<code>-?</code>”) and it will print a summary of the available options on standard error. If you need to process a file whose name begins with “<code>-</code>”, you can prepend a “<code>./</code>” to its name in order to get it accepted by the command line parser. Or, you can insert the special option “<code>--</code>” on the command line to cause all subsequent command line arguments to be treated as filenames even if their names begin with “<code>-</code>”. </p> <a name="H0006"></a> <h2>3.0 Preparing Source Files For Use With Makeheaders</h2> <p> Very little has to be done to prepare source files for use with makeheaders since makeheaders will read and understand ordinary C code. But it is important that you structure your files in a way that makes sense in the makeheaders context. This section will describe several typical uses of makeheaders. </p> <a name="H0007"></a> <h3>3.1 The Basic Setup</h3> <p> The simplest way to use makeheaders is to put all definitions in one or more .c files and all structure and type declarations in separate .h files. The only restriction is that you should take care to chose basenames for your .h files that are different from the basenames for your .c files. Recall that if your .c file is named (for example) “<code>alpha.c</code>” makeheaders will attempt to generate a corresponding header file named “<code>alpha.h</code>”. For that reason, you don't want to use that name for any of the .h files you write since that will prevent makeheaders from generating the .h file automatically. </p> <p> The structure of a .c file intended for use with makeheaders is very simple. All you have to do is add a single “<code>#include</code>” to the top of the file that sources the header file that makeheaders will generate. Hence, the beginning of a source file named “<code>alpha.c</code>” might look something like this: </p> <pre> /* * Introductory comment... */ #include "alpha.h" /* The rest of your code... */ </pre> <p> Your manually generated header files require no special attention at all. Code them as you normally would. However, makeheaders will work better if you omit the “<code>#if</code>” statements people often put around the outside of header files that prevent the files from being included more than once. For example, to create a header file named “<code>beta.h</code>”, many people will habitually write the following: <pre> #ifndef BETA_H #define BETA_H /* declarations for beta.h go here */ #endif </pre> You can forego this cleverness with makeheaders. Remember that the header files you write will never really be included by any C code. Instead, makeheaders will scan your header files to extract only those declarations that are needed by individual .c files and then copy those declarations to the .h files corresponding to the .c files. Hence, the “<code>#if</code>” wrapper serves no useful purpose. But it does make makeheaders work harder, forcing it to put the statements <pre> #if !defined(BETA_H) #endif </pre> around every declaration that it copies out of your header file. No ill effect should come of this, but neither is there any benefit. </p> <p> Having prepared your .c and .h files as described above, you can cause makeheaders to generate its .h files using the following simple command: <pre> makeheaders *.[ch] </pre> The makeheaders program will scan all of the .c files and all of the manually written .h files and then automatically generate .h files corresponding to all .c files. </p> <p> Note that the wildcard expression used in the above example, “<code>*.[ch]</code>”, will expand to include all .h files in the current directory, both those entered manually be the programmer and others generated automatically by a prior run of makeheaders. But that is not a problem. The makeheaders program will recognize and ignore any files it has previously generated that show up on its input list. </p> <a name="H0008"></a> <h3>3.2 What Declarations Get Copied</h3> <p> The following list details all of the code constructs that makeheaders will extract and place in the automatically generated .h files: </p> <ul> <p><li> When a function is defined in any .c file, a prototype of that function is placed in the generated .h file of every .c file that calls the function.</p> <P>If the “<code>static</code>” keyword of C appears at the beginning of the function definition, the prototype is suppressed. If you use the “<code>LOCAL</code>” keyword where you would normally say “<code>static</code>”, then a prototype is generated, but it will only appear in the single header file that corresponds to the source file containing the function. For example, if the file <code>alpha.c</code> contains the following: <pre> LOCAL int testFunc(void){ return 0; } </pre> Then the header file <code>alpha.h</code> will contain <pre> #define LOCAL static LOCAL int testFunc(void); </pre> However, no other generated header files will contain a prototype for <code>testFunc()</code> since the function has only file scope.</p> <p>When the “<code>LOCAL</code>” keyword is used, makeheaders will also generate a #define for LOCAL, like this: <pre> #define LOCAL static </pre> so that the C compiler will know what it means.</p> <p>If you invoke makeheaders with a “<code>-local</code>” command-line option, then it treats the “<code>static</code>” keyword like “<code>LOCAL</code>” and generates prototypes in the header file that corresponds to the source file containing the function definition.</p> <p><li> When a global variable is defined in a .c file, an “<code>extern</code>” declaration of that variable is placed in the header of every .c file that uses the variable. </p> <p><li> When a structure, union or enumeration declaration or a function prototype or a C++ class declaration appears in a manually produced .h file, that declaration is copied into the automatically generated .h files of all .c files that use the structure, union, enumeration, function or class. But declarations that appear in a .c file are considered private to that .c file and are not copied into any automatically generated files. </p> <p><li> All #defines and typedefs that appear in manually produced .h files are copied into automatically generated .h files as needed. Similar constructs that appear in .c files are considered private to those files and are not copied. </p> <p><li> When a structure, union or enumeration declaration appears in a .h file, makeheaders will automatically generate a typedef that allows the declaration to be referenced without the “<code>struct</code>”, “<code>union</code>” or “<code>enum</code>” qualifier. In other words, if makeheaders sees the code: <pre> struct Examp { /* ... */ }; </pre> it will automatically generate a corresponding typedef like this: <pre> typedef struct Examp Examp; </pre> </p> <p><li> Makeheaders generates an error message if it encounters a function or variable definition within a .h file. The .h files are supposed to contain only interface, not implementation. C compilers will not enforce this convention, but makeheaders does. </ul> <p> As a final note, we observe that automatically generated declarations are ordered as required by the ANSI-C programming language. If the declaration of some structure “<code>X</code>” requires a prior declaration of another structure “<code>Y</code>”, then Y will appear first in the generated headers. </p> <a name="H0009"></a> <h3>3.3 How To Avoid Having To Write Any Header Files</h3> <p> In my experience, large projects work better if all of the manually written code is placed in .c files and all .h files are generated automatically. This is slightly different from the traditional C method of placing the interface in .h files and the implementation in .c files, but it is a refreshing change that brings a noticeable improvement to the coding experience. Others, I believe, share this view since I've noticed recent languages (ex: java, tcl, perl, awk) tend to support the one-file approach to coding as the only option. </p> <p> The makeheaders program supports putting both interface and implementation into the same source file. But you do have to tell makeheaders which part of the source file is the interface and which part is the implementation. Makeheaders has to know this in order to be able to figure out whether or not structures declarations, typedefs, #defines and so forth should be copied into the generated headers of other source files. </p> <p> You can instruct makeheaders to treat any part of a .c file as if it were a .h file by enclosing that part of the .c file within: <pre> #if INTERFACE #endif </pre> Thus any structure definitions that appear after the “<code>#if INTERFACE</code>” but before the corresponding “<code>#endif</code>” are eligible to be copied into the automatically generated .h files of other .c files. </p> <p> If you use the “<code>#if INTERFACE</code>” mechanism in a .c file, then the generated header for that .c file will contain a line like this: <pre> #define INTERFACE 0 </pre> In other words, the C compiler will never see any of the text that defines the interface. But makeheaders will copy all necessary definitions and declarations into the .h file it generates, so .c files will compile as if the declarations were really there. This approach has the advantage that you don't have to worry with putting the declarations in the correct ANSI-C order -- makeheaders will do that for you automatically. </p> <p> Note that you don't have to use this approach exclusively. You can put some declarations in .h files and others within the “<code>#if INTERFACE</code>” regions of .c files. Makeheaders treats all declarations alike, no matter where they come from. You should also note that a single .c file can contain as many “<code>#if INTERFACE</code>” regions as desired. </p> <a name="H0010"></a> <h3>3.4 Designating Declarations For Export</h3> <p> In a large project, one will often construct a hierarchy of interfaces. For example, you may have a group of 20 or so files that form a library used in several other parts of the system. Each file in this library will present two interfaces. One interface will be the routines and data structures it is willing to share with other files in the same library, and the second interface is those routines and data structures it wishes to make available to other subsystems. (The second interface is normally a subset of the first.) Ordinary C does not provide support for a tiered interface like this, but makeheaders does. </p> <p> Using makeheaders, it is possible to designate routines and data structures as being for “<code>export</code>”. Exported objects are visible not only to other files within the same library or subassembly but also to other libraries and subassemblies in the larger program. By default, makeheaders only makes objects visible to other members of the same library. </p> <p> That isn't the complete truth, actually. The semantics of C are such that once an object becomes visible outside of a single source file, it is also visible to any user of the library that is made from the source file. Makeheaders can not prevent outsiders from using non-exported resources, but it can discourage the practice by refusing to provide prototypes and declarations for the services it does not want to export. Thus the only real effect of the making an object exportable is to include it in the output makeheaders generates when it is run using the -H command line option. This is not a perfect solution, but it works well in practice. </p> <p> But trouble quickly arises when we attempt to devise a mechanism for telling makeheaders which prototypes it should export and which it should keep local. The built-in “<code>static</code>” keyword of C works well for prohibiting prototypes from leaving a single source file, but because C doesn't support a linkage hierarchy, there is nothing in the C language to help us. We'll have to invite our own keyword: “<code>EXPORT</code>” </p> <p> Makeheaders allows the EXPORT keyword to precede any function or procedure definition. The routine following the EXPORT keyword is then eligable to appear in the header file generated using the -H command line option. Note that if a .c file contains the EXPORT keyword, makeheaders will put the macro <pre> #define EXPORT </pre> in the header file it generates for the .c file so that the EXPORT keyword will never be seen by the C compiler. </p> <p> But the EXPORT keyword only works for function and procedure definitions. For structure, union and enum definitions, typedefs, #defines and class declarations, a second mechanism is used. Just as any declarations or definition contained within <pre> #if INTERFACE #endif </pre> are visible to all files within the library, any declarations or definitions within <pre> #if EXPORT_INTERFACE #endif </pre> will become part of the exported interface. The “<code>#if EXPORT_INTERFACE</code>” mechanism can be used in either .c or .h files. (The “<code>#if INTERFACE</code>” can also be used in both .h and .c files, but since it's use in a .h file would be redundant, we haven't mentioned it before.) </p> <a name="H0011"></a> <h3>3.5 Local declarations processed by makeheaders</h3> <p> Structure declarations and typedefs that appear in .c files are normally ignored by makeheaders. Such declarations are only intended for use by the source file in which they appear and so makeheaders doesn't need to copy them into any generated header files. We call such declarations “<code>private</code>”. </p> <p> Sometimes it is convenient to have makeheaders sort a sequence of private declarations into the correct order for us automatically. Or, we could have static functions and procedures for which we would like makeheaders to generate prototypes, but the arguments to these functions and procedures uses private declarations. In both of these cases, we want makeheaders to be aware of the private declarations and copy them into the local header file, but we don't want makeheaders to propagate the declarations outside of the file in which they are declared. </p> <p> When this situation arises, enclose the private declarations within <pre> #if LOCAL_INTERFACE #endif </pre> A “<code>LOCAL_INTERFACE</code>” block works very much like the “<code>INTERFACE</code>” and “<code>EXPORT_INTERFACE</code>” blocks described above, except that makeheaders insures that the objects declared in a LOCAL_INTERFACE are only visible to the file containing the LOCAL_INTERFACE. </p> <a name="H0012"></a> <h3>3.6 Using Makeheaders With C++ Code</h3> <p> You can use makeheaders to generate header files for C++ code, in addition to C. Makeheaders will recognize and copy both “<code>class</code>” declarations and inline function definitions, and it knows not to try to generate prototypes for methods. </p> <p> In fact, makeheaders is smart enough to be used in projects that employ a mixture of C and C++. For example, if a C function is called from within a C++ code module, makeheaders will know to prepend the text <pre> extern "C" </pre> to the prototype for that function in the C++ header file. Going the other way, if you try to call a C++ function from within C, an appropriate error message is issued, since C++ routines can not normally be called by C code (due to fact that most C++ compilers use name mangling to facilitate type-safe linkage.) </p> <p> No special command-line options are required to use makeheaders with C++ input. Makeheaders will recognize that its source code is C++ by the suffix on the source code filename. Simple ".c" or ".h" suffixes are assumed to be ANSI-C. Anything else, including ".cc", ".C" and ".cpp" is assumed to be C++. The name of the header file generated by makeheaders is derived from the name of the source file by converting every "c" to "h" and every "C" to "H" in the suffix of the filename. Thus the C++ source file “<code>alpha.cpp</code>” will induce makeheaders to generate a header file named “<code>alpha.hpp</code>”. </p> <p> Makeheaders augments class definitions by inserting prototypes to methods where appropriate. If a method definition begins with one of the special keywords <b>PUBLIC</b>, <b>PROTECTED</b>, or <b>PRIVATE</b> (in upper-case to distinguish them from the regular C++ keywords with the same meaning) then a prototype for that method will be inserted into the class definition. If none of these keywords appear, then the prototype is not inserted. For example, in the following code, the constructor is not explicitly declared in the class definition but makeheaders will add it there because of the PUBLIC keyword that appears before the constructor definition. </p> <blockquote><pre> #if INTERFACE class Example1 { private: int v1; }; #endif PUBLIC Example1::Example1(){ v1 = 0; } </pre></blockquote> <p> The code above is equivalent to the following: </p> <blockquote><pre> #if INTERFACE class Example1 { private: int v1; public: Example1(); }; #endif Example1::Example1(){ v1 = 0; } </pre></blockquote> <p> The first form is preferred because only a single declaration of the constructor is required. The second form requires two declarations, one in the class definition and one on the definition of the constructor. </p> <h4>3.6.1 C++ Limitations</h4> <p> Makeheaders does not understand more recent C++ syntax such as templates and namespaces. Perhaps these issues will be addressed in future revisions. </p> <a name="H0013"></a> <h3>3.7 Conditional Compilation</h3> <p> The makeheaders program understands and tracks the conditional compilation constructs in the source code files it scans. Hence, if the following code appears in a source file <pre> #ifdef UNIX # define WORKS_WELL 1 #else # define WORKS_WELL 0 #endif </pre> then the next patch of code will appear in the generated header for every .c file that uses the WORKS_WELL constant: <pre> #if defined(UNIX) # define WORKS_WELL 1 #endif #if !defined(UNIX) # define WORKS_WELL 0 #endif </pre> The conditional compilation constructs can be nested to any depth. Makeheaders also recognizes the special case of <pre> #if 0 #endif </pre> and treats the enclosed text as a comment. </p> <a name="H0014"></a> <h3>3.8 Caveats</h3> <p> The makeheaders system is designed to be robust but it is possible for a devious programmer to fool the system, usually with unhelpful consequences. This subsection is a guide to helping you avoid trouble. </p> <p> Makeheaders does not understand the old K&R style of function and procedure definitions. It only understands the modern ANSI-C style, and will probably become very confused if it encounters an old K&R function. Therefore you should take care to avoid putting K&R function definitions in your code. </p> <p> Makeheaders does not understand when you define more than one global variable with the same type separated by a comma. In other words, makeheaders does not understand this: <pre> int a = 4, b = 5; </pre> The makeheaders program wants every variable to have its own definition. Like this: <pre> int a = 4; int b = 5; </pre> Notice that this applies to global variables only, not to variables you declare inside your functions. Since global variables ought to be exceedingly rare, and since it is good style to declare them separately anyhow, this restriction is not seen as a terrible hardship. </p> <p> Makeheaders does not support defining an enumerated or aggregate type in the same statement as a variable declaration. None of the following statements work completely: <pre> struct {int field;} a; struct Tag {int field;} b; struct Tag c; </pre> Instead, define types separately from variables: <pre> #if INTERFACE struct Tag {int field;}; #endif Tag a; Tag b; /* No more than one variable per declaration. */ Tag c; /* So must put each on its own line. */ </pre> See <a href="#H0008">3.2 What Declarations Get Copied</a> for details, including on the automatic typedef. </p> <p> The makeheaders program processes its source file prior to sending those files through the C preprocessor. Hence, if you hide important structure information in preprocessor defines, makeheaders might not be able to successfully extract the information it needs from variables, functions and procedure definitions. For example, if you write this: <pre> #define BEGIN { #define END } </pre> at the beginning of your source file, and then try to create a function definition like this: <pre> char *StrDup(const char *zSrc) BEGIN /* Code here */ END </pre> then makeheaders won't be able to find the end of the function definition and bad things are likely to happen. </p> <p> For most projects the code constructs that makeheaders cannot handle are very rare. As long as you avoid excessive cleverness, makeheaders will probably be able to figure out what you want and will do the right thing. </p> <p> Makeheaders has limited understanding of enums. In particular, it does not realize the significance of enumerated values, so the enum is not emitted in the header files when its enumerated values are used unless the name associated with the enum is also used. Moreover, enums can be completely anonymous, e.g. “<code>enum {X, Y, Z};</code>”. Makeheaders ignores such enums so they can at least be used within a single source file. Makeheaders expects you to use #define constants instead. If you want enum features that #define lacks, and you need the enum in the interface, bypass makeheaders and write a header file by hand, or teach makeheaders to emit the enum definition when any of the enumerated values are used, rather than only when the top-level name (if any) is used. </p> <a name="H0015"></a> <h2>4.0 Using Makeheaders To Generate Documentation</h2> <p> Many people have observed the advantages of generating program documentation directly from the source code: <ul> <li> Less effort is involved. It is easier to write a program than it is to write a program and a document. <li> The documentation is more likely to agree with the code. When documentation is derived directly from the code, or is contained in comments immediately adjacent to the code, it is much more likely to be correct than if it is contained in a separate unrelated file in a different part of the source tree. <li> Information is kept in only one place. When a change occurs in the code, it is not necessary to make a corresponding change in a separate document. Just rerun the documentation generator. </ul> The makeheaders program does not generate program documentation itself. But you can use makeheaders to parse the program source code, extract the information that is relevant to the documentation and to pass this information to another tool to do the actual documentation preparation. </p> <p> When makeheaders is run with the “<code>-doc</code>” option, it emits no header files at all. Instead, it does a complete dump of its internal tables to standard output in a form that is easily parsed. This output can then be used by another program (the implementation of which is left as an exercise to the reader) that will use the information to prepare suitable documentation. </p> <p> The “<code>-doc</code>” option causes makeheaders to print information to standard output about all of the following objects: <ul> <li> C++ class declarations <li> Structure and union declarations <li> Enumerations <li> Typedefs <li> Procedure and function definitions <li> Global variables <li> Preprocessor macros (ex: “<code>#define</code>”) </ul> For each of these objects, the following information is output: <ul> <li> The name of the object. <li> The type of the object. (Structure, typedef, macro, etc.) <li> Flags to indicate if the declaration is exported (contained within an EXPORT_INTERFACE block) or local (contained with LOCAL_INTERFACE). <li> A flag to indicate if the object is declared in a C++ file. <li> The name of the file in which the object was declared. <li> The complete text of any block comment that precedes the declarations. <li> If the declaration occurred inside a preprocessor conditional (“<code>#if</code>”) then the text of that conditional is provided. <li> The complete text of a declaration for the object. </ul> The exact output format will not be described here. It is simple to understand and parse and should be obvious to anyone who inspects some sample output. </p> <a name="H0016"></a> <h2>5.0 Compiling The Makeheaders Program</h2> <p> The source code for makeheaders is a single file of ANSI-C code, approximately 3000 lines in length. The program makes only modest demands of the system and C library and should compile without alteration on most ANSI C compilers and on most operating systems. It is known to compile using several variations of GCC for Unix as well as Cygwin32 and MSVC 5.0 for Win32. </p> <a name="H0017"></a> <h2>6.0 History</h2> <p> The makeheaders program was first written by D. Richard Hipp (also the original author of <a href="https://sqlite.org/">SQLite</a> and <a href="https://fossil-scm.org/">Fossil</a>) in 1993. Hipp open-sourced the project immediately, but it never caught on with any other developers and it continued to be used mostly by Hipp himself for over a decade. When Hipp was first writing the Fossil version control system in 2006 and 2007, he used makeheaders on that project to help simplify the source code. As the popularity of Fossil increased, the makeheaders that was incorporated into the Fossil source tree became the "official" makeheaders implementation. </p> <p> As this paragraph is being composed (2016-11-05), Fossil is the only project known to Hipp that is still using makeheaders. On the other hand, makeheaders has served the Fossil project well and there are no plans remove it. </p> <a name="H0018"></a> <h2>7.0 Summary And Conclusion</h2> <p> The makeheaders program will automatically generate a minimal header file for each of a set of C source and header files, and will generate a composite header file for the entire source file suite, for either internal or external use. It can also be used as the parser in an automated program documentation system. </p> <p> The makeheaders program has been in use since 1994, in a wide variety of projects under both UNIX and Win32. In every project where it has been used, makeheaders has proven to be a very helpful aid in the construction and maintenance of large C codes. In at least two cases, makeheaders has facilitated development of programs that would have otherwise been all but impossible due to their size and complexity. </p> </body> </html> |
Added tools/makemake.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 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 | #!/usr/bin/tclsh # # ### Run this Tcl script EVERY time you modify it in any way! ### # ### It must be run from the directory it lives in so that ### # ### directories resolve properly! ### # # This Tcl script generates make files for various platforms. The makefiles # then need to be committed. # # If you modify this file then: # # 1. cd tools; tclsh makemake.tcl # # 2. if errors are reported, fix them and go to step 1 # # 3. if "fossil diff" reports changes in any of the generated # files, commit the changed files to the repo # # 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 # # Add new source files by listing the files (without their .c suffix) # in the "src" variable. Add new resource files to the "extra_files" # variable. There are other variables that you can alter, down to # the "STOP HERE" comment. The stuff below "STOP HERE" should rarely need # to change. After modification, go to step 1 above. # # Delete unused source files in the "src" variable, then go to step 1 above. # ############################################################################# # $srcDir is used to set the target source dir in several places. Not # all code-generation bits use $srcDir and instead hard-code, so # replacing it only here (should it ever changes) is not sufficient. # set srcDir ../src # Directory $srcDirExt houses single-file source code solutions which # are imported directly into the fossil source tree. set srcDirExt ../extsrc # 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 ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event extcgi export file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_transport import info interwiki 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 patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip http_ssl } # Source files which live under $srcDirExt, but only those for which # we need to run makeheaders. External sources which have their own # header files must not be in this list. set src_ext { pikchr } # Additional resource files that get built into the executable. # These paths are all resolved from the src/ directory, so must # be relative to that. set extra_files { diff.tcl markdown.md wiki.wiki *.js default.css style.*.css ../skins/*/*.txt sounds/*.wav alerts/*.wav ../extsrc/pikchr.wasm ../extsrc/pikchr*.js } # Options used to compile the included SQLite library. # set SQLITE_OPTIONS { -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP } #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 Pikchr library. # set PIKCHR_OPTIONS { -DPIKCHR_TOKEN_LIMIT=10000 } # Options used to compile the included SQLite shell. # set SHELL_OPTIONS [concat $SQLITE_OPTIONS { -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc }] # 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 # STOP HERE. # Unless the build procedures changes, you should not have to edit anything # below this line. ############################################################################# # 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] } } # Expand any wildcards in "extra_files" set new_extra_files {} foreach file $extra_files { # we need $file to resolve from $srcDir, but simply prepending # $srcDir to each name breaks how the names are stringified and # looked up from C. set cwd [pwd] cd $srcDir foreach x [glob $file] { # -nocomplain flag? lappend new_extra_files $x } cd $cwd } set extra_files $new_extra_files ############################################################################## ############################################################################## ############################################################################## # Start by generating the "main.mk" makefile used for all unix systems. # set mainMk $srcDir/main.mk puts "building $mainMk" set output_file [open $mainMk w] fconfigure $output_file -translation binary writeln {# ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "tools/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. # XBCC = $(BCC) $(BCCFLAGS) XTCC = $(TCC) $(CFLAGS_INCLUDE) -I$(OBJDIR) $(TCCFLAGS) TESTFLAGS := -quiet } 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 [string map [list \ <<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \ <<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \ <<<PIKCHR_OPTIONS>>> [join $PIKCHR_OPTIONS " \\\n "] \ <<<NEXT_LINE>>> \\] { all: $(OBJDIR) $(APPNAME) install: all mkdir -p $(INSTALLDIR) cp $(APPNAME) $(INSTALLDIR) codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1 $(OBJDIR)/codecheck1 $(TRANS_SRC) $(OBJDIR): -mkdir $(OBJDIR) $(OBJDIR)/translate: $(SRCDIR_tools)/translate.c $(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c $(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c $(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c $(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c $(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c $(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c $(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c $(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c $(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c $(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c # Run the test suite. # Other flags that can be included in TESTFLAGS are: # # -halt Stop testing after the first failed test # -keep Keep the temporary workspace for debugging # -prot Write a detailed log of the tests to the file ./prot # -verbose Include even more details in the output # -quiet Hide most output from the terminal # -strict Treat known bugs as failures # # TESTFLAGS can also include names of specific test files to limit # the run to just those test cases. # test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid <<<NEXT_LINE>>> $(SRCDIR)/../manifest <<<NEXT_LINE>>> $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h $(OBJDIR)/phony.h: # Force rebuild of VERSION.h every time we run "make" # 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 Pikchr formatter. PIKCHR_OPTIONS = <<<PIKCHR_OPTIONS>>> # The USE_SYSTEM_SQLITE variable may be undefined, set to 0 or 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. # # Closely related is SQLITE3_ORIGIN, with the same numeric mapping plus # a value of 2 means that we are building a client-provided sqlite3.c. SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ.1 = $(OBJDIR)/sqlite3-see.o # SQLITE3_OBJ.2 is set by the configure process SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_LINENOISE variable may be undefined, set to 0, or set # to 1. If it is set to 0, then there is no need to build or link # the linenoise.o object. LINENOISE_DEF.0 = LINENOISE_DEF.1 = -DHAVE_LINENOISE LINENOISE_DEF. = $(LINENOISE_DEF.0) LINENOISE_OBJ.0 = LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o LINENOISE_OBJ. = $(LINENOISE_OBJ.0) # The USE_SEE variable may be undefined, 0 or 1. If undefined or 0, # in-tree SQLite is used. If 1, then sqlite3-see.c (not part of the # source tree) is used and extra flags are provided to enable the # SQLite Encryption Extension. SQLITE3_SRC.0 = $(SRCDIR_extsrc)/sqlite3.c SQLITE3_SRC.1 = $(SRCDIR_extsrc)/sqlite3-see.c # SQLITE3_SRC.2 is set by top-level configure/makefile process. SQLITE3_SRC. = $(SRCDIR_extsrc)/sqlite3.c SQLITE3_SRC = $(SQLITE3_SRC.$(SQLITE3_ORIGIN)) SQLITE3_SHELL_SRC.0 = $(SRCDIR_extsrc)/shell.c SQLITE3_SHELL_SRC.1 = $(SRCDIR_extsrc)/shell-see.c # SQLITE3_SHELL_SRC.2 comes from the configure process SQLITE3_SHELL_SRC. = $(SRCDIR_extsrc)/shell.c SQLITE3_SHELL_SRC = $(SQLITE3_SHELL_SRC.$(SQLITE3_ORIGIN)) SEE_FLAGS.0 = SEE_FLAGS.1 = -DSQLITE_HAS_CODEC -DSQLITE_SHELL_DBKEY_PROC=fossil_key SEE_FLAGS. = SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE)) }] writeln [string map [list <<<NEXT_LINE>>> \\] { EXTRAOBJ = <<<NEXT_LINE>>> $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) <<<NEXT_LINE>>> $(LINENOISE_OBJ.$(USE_LINENOISE)) <<<NEXT_LINE>>> $(OBJDIR)/pikchr.o <<<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 $(EXTRAOBJ) $(OBJ) $(OBJDIR)/codecheck1 $(TRANS_SRC) $(TCC) $(TCCFLAGS) -o $(APPNAME) $(EXTRAOBJ) $(OBJ) $(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) { } } foreach s [lsort $src_ext] { append mhargs "\$(SRCDIR_extsrc)/${s}.c:\$(OBJDIR)/$s.h <<<NEXT_LINE>>>" set extra_h($s) { } } append mhargs "\$(SRCDIR_extsrc)/sqlite3.h <<<NEXT_LINE>>>" append mhargs "\$(SRCDIR)/th.h <<<NEXT_LINE>>>" #append mhargs "\$(SRCDIR_extsrc)/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(dispatch) " \$(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 "\$(SQLITE3_OBJ):\t\$(SQLITE3_SRC)" writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) \$(SEE_FLAGS) \\" writeln "\t\t-c \$(SQLITE3_SRC) -o \$@" writeln "\$(OBJDIR)/shell.o:\t\$(SQLITE3_SHELL_SRC) \$(SRCDIR_extsrc)/sqlite3.h" writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) \$(SEE_FLAGS) \$(LINENOISE_DEF.\$(USE_LINENOISE)) -c \$(SQLITE3_SHELL_SRC) -o \$@\n" writeln "\$(OBJDIR)/linenoise.o:\t\$(SRCDIR_extsrc)/linenoise.c \$(SRCDIR_extsrc)/linenoise.h" writeln "\t\$(XTCC) -c \$(SRCDIR_extsrc)/linenoise.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 [string map [list <<<NEXT_LINE>>> \\] { $(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>> -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore <<<NEXT_LINE>>> -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>> -sENVIRONMENT=web <<<NEXT_LINE>>> -sMODULARIZE <<<NEXT_LINE>>> -sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>> --minify 0 @chmod -x $(SRCDIR_extsrc)/pikchr.wasm wasm: $(SRCDIR_extsrc)/pikchr.js # # compile_commands.json support... # # We have to avoid applying compile_commands support to the in-tree # tools, as those compile with BCC, which may differ from TCC. # e.g. BCC might be gcc (which does not support -MJ ...) while TCC is # clang (which does). # # What follows is more verbose than strictly necessary because we're # limited to POSIX make syntax. all: compile-commands-dir.yes = $(OBJDIR) compile-commands-dir.no = compile-commands-dir = $(compile-commands-dir.$(MAKE_COMPILATION_DB)) compile-command-args.yes = -MJ $(TOPDIR)/$(compile-commands-dir)/$(@F:.o=.o.json) compile-command-args.no = TCCFLAGS += $(compile-command-args.$(MAKE_COMPILATION_DB)) compile_commands.json = $(TOPDIR)/compile_commands.json # compile_commands.json is a concatenation of the .o.json files # generated by the compilation process via TCCFLAGS. We have a # potential race condition in parallel builds, where a .o.json file is # not yet written to completion before compile_commands.json is # processed. How to resolve that in a way compatible with POSIX make # is unclear. # # This obscure sed bit ensures that the resulting JSON array does not # have a trailing comma. $(compile_commands.json): $(OBJ) @-rm -f $@ @{ echo '['; cat $(compile-commands-dir)/*.o.json | tr '\n' ' ' | sed -e 's/, $$//'; echo ']'; } > $@ @echo "Generated $@" compile-commands.no: compile-commands.yes: $(compile_commands.json) all: compile-commands.$(MAKE_COMPILATION_DB) clean: compile-commands-clean compile-commands-clean: rm -fr $(compile_commands.json) # # End compile_commands.json support # # # 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 .PHONY: compile-commands-clean compile-commands-dir }] close $output_file # # End of the main.mk output ############################################################################## ############################################################################## ############################################################################## # Begin win/Makefile.mingw output # puts "building ../win/Makefile.mingw" set output_file [open ../win/Makefile.mingw w] fconfigure $output_file -translation binary writeln {#!/usr/bin/make # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "tools/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 is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using # MinGW or MinGW-w64. # # Some of the special options which can be passed to make # USE_WINDOWS=1 if building under a windows command prompt # X64=1 if using an unprefixed 64-bit mingw compiler # #### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. # By default, this is an empty string (i.e. use the native compiler). # PREFIX = # PREFIX = mingw32- # PREFIX = i686-pc-mingw32- # PREFIX = i686-w64-mingw32- # PREFIX = x86_64-w64-mingw32- #### The toplevel directory of the source tree. Fossil can be built # in a directory that is separate from the source tree. Just change # the following to point from the build directory to the src/ folder. # SRCDIR = src SRCDIR_extsrc = extsrc SRCDIR_tools = tools #### The directory into which object code files should be written. # OBJDIR = wbld #### C compiler for use in building executables that will run on # the platform that is doing the build. This is used to compile # code-generator programs as part of the build process. See TCC # and TCCEXE below for the C compiler for building the finished # binary. # BCCEXE = gcc #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. This is used # to compile code-generator programs as part of the build process. # See TCC below for the C compiler for building the finished binary. # BCC = $(BCCEXE) #### Enable compiling with debug symbols (much larger binary) # # FOSSIL_ENABLE_SYMBOLS = 1 #### Enable JSON (https://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 relative paths in external diff/gdiff # # FOSSIL_ENABLE_EXEC_REL_PATHS = 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 POSIX memory APIs from "sys/mman.h" # # USE_MMAN_H = 1 #### Use the SQLite Encryption Extension # # USE_SEE = 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 ZLIBCONFIG = ZLIBTARGETS = else SSLCONFIG = mingw64 ZLIBCONFIG = ZLIBTARGETS = endif #### Disable creation of the OpenSSL shared libraries. Also, disable support # for SSLv3 (i.e. thereby forcing the use of TLS). # SSLCONFIG += no-ssl3 no-weak-ssl-ciphers 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). # SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib #### The directories where the OpenSSL include and library files are located. # OPENSSLDIR = $(SRCDIR)/../compat/openssl 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 # here is to use the Sysinternals junction tool to create a hard # link between a "tcl-8.x" sub-directory of the Fossil source code # directory and the target Tcl directory. This removes the need to # hard-code the necessary paths in this Makefile. # TCLDIR = $(SRCDIR)/../compat/tcl-8.6 #### The Tcl source code directory. This defaults to the same value as # TCLDIR macro (above), which may not be correct. This value will # only be used if the FOSSIL_TCL_SOURCE macro is defined. # TCLSRCDIR = $(TCLDIR) #### The Tcl include and library directories. These values will only be # used if the FOSSIL_TCL_SOURCE macro is not defined. # TCLINCDIR = $(TCLDIR)/include TCLLIBDIR = $(TCLDIR)/lib #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)? # ifdef FOSSIL_ENABLE_TCL_STUBS ifndef FOSSIL_ENABLE_TCL_PRIVATE_STUBS LIBTCL = -ltclstub86 endif TCLTARGET = libtclstub86.a else LIBTCL = -ltcl86 TCLTARGET = binaries endif #### C compiler for use in building executables that will run on the # target platform. This is usually the same as BCCEXE, unless you # are cross-compiling. This C compiler builds the finished binary # for fossil. See BCC and BCCEXE above for the C compiler for # building intermediate code-generator tools. # TCCEXE = gcc #### C compiler and options for use in building executables that will # run on the target platform. This is usually the almost 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)$(TCCEXE) -Wall -Wdeclaration-after-statement TCC += -I$(SRCDIR_extsrc) #### Add the necessary command line options to build with debugging # symbols, if enabled. # ifdef FOSSIL_ENABLE_SYMBOLS TCC += -g else TCC += -Os endif TCC += -L$(ZLIBDIR) -I$(ZINCDIR) #### Compile resources for use in building executables that will run # on the target platform. # RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR) RCC += -I$(SRCDIR_extsrc) # 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 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 relative paths in external diff/gdiff ifdef FOSSIL_ENABLE_EXEC_REL_PATHS TCC += -DFOSSIL_ENABLE_EXEC_REL_PATHS=1 RCC += -DFOSSIL_ENABLE_EXEC_REL_PATHS=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 TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS ifdef FOSSIL_ENABLE_TCL_PRIVATE_STUBS TCC += -DFOSSIL_ENABLE_TCL_PRIVATE_STUBS=1 RCC += -DFOSSIL_ENABLE_TCL_PRIVATE_STUBS=1 endif else TCC += -DSTATIC_BUILD RCC += -DSTATIC_BUILD endif endif # With JSON support ifdef FOSSIL_ENABLE_JSON TCC += -DFOSSIL_ENABLE_JSON=1 RCC += -DFOSSIL_ENABLE_JSON=1 endif # With "sys/mman.h" support ifdef USE_MMAN_H TCC += -DUSE_MMAN_H=1 RCC += -DUSE_MMAN_H=1 endif # With SQLite Encryption Extension support ifdef USE_SEE TCC += -DUSE_SEE=1 RCC += -DUSE_SEE=1 endif #### The option -static has no effect on MinGW(-w64), only dynamic # executables can be built when linking with MSVCRT. OpenSSL # (optional) and zlib (required) however are always linked in # statically. Therefore, the FOSSIL_DYNAMIC_BUILD option does # not really apply to MinGW (i.e. since ALL external libraries # are NOT linked dynamically). # # 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 -lcrypt32 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 #### zlib is required. # LIB += -lz #### 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 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Library required for DNS lookups. # LIB += -ldnsapi #### 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 #-------------------------------------------------------- XBCC = $(BCC) $(CFLAGS) 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 $(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 $(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)) $(CP) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR)) else $(MKDIR) $(INSTALLDIR) $(CP) $(APPNAME) $(INSTALLDIR) endif $(OBJDIR): ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(OBJDIR)) else $(MKDIR) $(OBJDIR) endif $(TRANSLATE): $(SRCDIR_tools)/translate.c $(XBCC) -o $@ $(SRCDIR_tools)/translate.c $(MAKEHEADERS): $(SRCDIR_tools)/makeheaders.c $(XBCC) -o $@ $(SRCDIR_tools)/makeheaders.c $(MKINDEX): $(SRCDIR_tools)/mkindex.c $(XBCC) -o $@ $(SRCDIR_tools)/mkindex.c $(MKBUILTIN): $(SRCDIR_tools)/mkbuiltin.c $(XBCC) -o $@ $(SRCDIR_tools)/mkbuiltin.c $(MKVERSION): $(SRCDIR_tools)/mkversion.c $(XBCC) -o $@ $(SRCDIR_tools)/mkversion.c $(CODECHECK1): $(SRCDIR_tools)/codecheck1.c $(XBCC) -o $@ $(SRCDIR_tools)/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) $(OBJDIR)/phony.h $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ $(OBJDIR)/phony.h: # Force rebuild of VERSION.h every time "make" is run # The USE_SYSTEM_SQLITE variable may be undefined, set to 0 or 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. # # Closely related is SQLITE3_ORIGIN, with the same 0/1 mapping, # plus a value of 2 means that we are building a client-provided # sqlite3.c. SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ.1 = $(OBJDIR)/sqlite3-see.o # SQLITE3_OBJ.2 is set by the configure process SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_SEE variable may be undefined, 0 or 1. If undefined or 0, # in-tree SQLite is used. If 1, then sqlite3-see.c (not part of the # source tree) is used and extra flags are provided to enable the # SQLite Encryption Extension. SQLITE3_SRC.0 = $(SRCDIR_extsrc)/sqlite3.c SQLITE3_SRC.1 = $(SRCDIR_extsrc)/sqlite3-see.c # SQLITE3_SRC.2 is set by top-level configure/makefile process. SQLITE3_SRC. = $(SRCDIR_extsrc)/sqlite3.c SQLITE3_SRC = $(SQLITE3_SRC.$(SQLITE3_ORIGIN)) SQLITE3_SHELL_SRC.0 = $(SRCDIR_extsrc)/shell.c SQLITE3_SHELL_SRC.1 = $(SRCDIR_extsrc)/shell-see.c # SQLITE3_SHELL_SRC.2 comes from the configure process SQLITE3_SHELL_SRC. = $(SRCDIR_extsrc)/shell.c SQLITE3_SHELL_SRC = $(SQLITE3_SHELL_SRC.$(SQLITE3_ORIGIN)) SEE_FLAGS.0 = SEE_FLAGS.1 = -DSQLITE_HAS_CODEC -DSQLITE_SHELL_DBKEY_PROC=fossil_key SEE_FLAGS. = SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE)) } writeln [string map [list <<<NEXT_LINE>>> \\] { EXTRAOBJ = <<<NEXT_LINE>>> $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) <<<NEXT_LINE>>> $(OBJDIR)/pikchr.o <<<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: $(ZLIBTARGETS) $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) $(ZLIBCONFIG) -f win32/Makefile.gcc libz.a clean-zlib: $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) -f win32/Makefile.gcc clean BLDTARGETS = zlib openssl: $(BLDTARGETS) cd $(OPENSSLLIBDIR);./Configure --cross-compile-prefix=$(PREFIX) $(SSLCONFIG) sed -i -e 's/^PERL=C:\\.*$$/PERL=perl.exe/i' $(OPENSSLLIBDIR)/Makefile $(MAKE) -C $(OPENSSLLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) build_libs clean-openssl: $(MAKE) -C $(OPENSSLLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) clean tcl: cd $(TCLSRCDIR)/win;./configure $(MAKE) -C $(TCLSRCDIR)/win PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) $(TCLTARGET) clean-tcl: $(MAKE) -C $(TCLSRCDIR)/win PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) distclean APPTARGETS += $(BLDTARGETS) ifdef FOSSIL_BUILD_SSL APPTARGETS += openssl endif $(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(CODECHECK1) $(TRANS_SRC) $(TCC) -o $@ $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(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: 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 " <<<NEXT_LINE>>>"} append mhargs "\$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h" set extra_h($s) { } } foreach s [lsort $src_ext] { if {[string length $mhargs] > 0} {append mhargs " <<<NEXT_LINE>>>"} append mhargs "\$(SRCDIR_extsrc)/${s}.c:\$(OBJDIR)/$s.h" set extra_h($s) { } } append mhargs " <<<NEXT_LINE>>>\$(SRCDIR_extsrc)/sqlite3.h" append mhargs " <<<NEXT_LINE>>>\$(SRCDIR)/th.h" append mhargs " <<<NEXT_LINE>>>\$(OBJDIR)/VERSION.h" set mhargs [string map [list <<<NEXT_LINE>>> \\\n\t] $mhargs] 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" } writeln {MINGW_OPTIONS = -D_HAVE__MINGW_H } 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 {$(MINGW_OPTIONS)} lappend MINGW_SQLITE_OPTIONS -DSQLITE_USE_MALLOC_H lappend MINGW_SQLITE_OPTIONS -DSQLITE_USE_MSIZE set MINGW_PIKCHR_OPTIONS $PIKCHR_OPTIONS set j " \\\n " writeln "SQLITE_OPTIONS = [join $MINGW_SQLITE_OPTIONS $j]\n" writeln "SHELL_OPTIONS = [join $SHELL_WIN32_OPTIONS $j]\n" writeln "PIKCHR_OPTIONS = [join $MINGW_PIKCHR_OPTIONS $j]\n" writeln "\$(SQLITE3_OBJ):\t\$(SQLITE3_SRC) \$(SRCDIR)/../win/Makefile.mingw" writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) \$(SEE_FLAGS) \\" writeln "\t\t-c \$(SQLITE3_SRC) -o \$@\n" writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR_extsrc)/cson_amalgamation.c" writeln "\t\$(XTCC) -c \$(SRCDIR_extsrc)/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\$(SQLITE3_SHELL_SRC) \$(SRCDIR_extsrc)/sqlite3.h \$(SRCDIR)/../win/Makefile.mingw" writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) \$(SEE_FLAGS) -c \$(SQLITE3_SHELL_SRC) -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)/pikchr.o:\t\$(SRCDIR_extsrc)/pikchr.c" writeln "\t\$(XTCC) \$(PIKCHR_OPTIONS) -c \$(SRCDIR_extsrc)/pikchr.c -o \$@\n" close $output_file # # End of the win/Makefile.mingw output ############################################################################## ############################################################################## ############################################################################## # Begin win/Makefile.dmc output # puts "building ../win/Makefile.dmc" set output_file [open ../win/Makefile.dmc w] fconfigure $output_file -translation binary writeln {# ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "tools/makemake.tcl") ############################################################################## # # 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 SRCDIR_extsrc = $B\extsrc SRCDIR_tools = $B\tools OBJDIR = . O = .obj E = .exe # Maybe DMDIR, SSL or INCL needs adjustment DMDIR = c:\DM INCL = -I. -I$(SRCDIR) -I$(SRCDIR_extsrc) -I$B\win\include -I$(DMDIR)\extra\include #SSL = -DFOSSIL_ENABLE_SSL=1 SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi } writeln "SQLITE_OPTIONS = [join $SQLITE_OPTIONS { }]\n" writeln "SHELL_OPTIONS = [join $SHELL_WIN32_OPTIONS { }]\n" writeln "PIKCHR_OPTIONS = [join $PIKCHR_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 " foreach s [lsort $src] { writeln -nonewline "$s " } writeln "shell sqlite3 th th_lang > \$@" writeln "\t+echo fossil >> \$@" writeln "\t+echo fossil >> \$@" writeln "\t+echo \$(LIBS) >> \$@" writeln "\t+echo. >> \$@" writeln "\t+echo fossil >> \$@" writeln { translate$E: $(SRCDIR_tools)\translate.c $(BCC) -o$@ $** makeheaders$E: $(SRCDIR_tools)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR_tools)\mkindex.c $(BCC) -o$@ $** mkbuiltin$E: $(SRCDIR_tools)\mkbuiltin.c $(BCC) -o$@ $** mkversion$E: $(SRCDIR_tools)\mkversion.c $(BCC) -o$@ $** codecheck1$E: $(SRCDIR_tools)\codecheck1.c $(BCC) -o$@ $** $(OBJDIR)\shell$O : $(SRCDIR_extsrc)\shell.c $(TCC) -o$@ -c $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $** $(OBJDIR)\sqlite3$O : $(SRCDIR_extsrc)\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_extsrc)\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 $(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 "\$(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 " } foreach s [lsort $src_ext] { writeln -nonewline "\$(SRCDIR_extsrc)\\${s}.c:$s.h " } writeln "\$(SRCDIR_extsrc)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR_extsrc)\\cson_amalgamation.h" writeln "\t@copy /Y nul: headers" close $output_file # # End of the win/Makefile.dmc output ############################################################################## ############################################################################## ############################################################################## # Begin win/Makefile.msc output # puts "building ../win/Makefile.msc" set output_file [open ../win/Makefile.msc w] fconfigure $output_file -translation binary writeln {# ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "tools/makemake.tcl") ############################################################################## # # # 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 SRCDIR_extsrc = $(B)\extsrc SRCDIR_tools = $(B)\tools T = . OBJDIR = $(T) OX = $(OBJDIR) O = .obj E = .exe P = .pdb DBGOPTS = /Od INSTALLDIR = . !ifdef DESTDIR INSTALLDIR = $(DESTDIR)\$(INSTALLDIR) !endif # When building out of source, this Makefile needs to know the path to the base # top-level directory for this project. Pass it on NMAKE command line via make # variable B: # NMAKE /f "path\to\this\Makefile" B="path/to/fossil/root" # # NOTE: Make sure B path has no trailing backslash, UNIX-style path is OK too. # !if !exist("$(B)\.fossil-settings") !error Please specify path to project base directory: B="path/to/fossil" !endif # Perl is only necessary if OpenSSL support is enabled and it is built from # source code. The PERLDIR environment variable, if it exists, should point # to the directory containing the main Perl executable specified here (i.e. # "perl.exe"). PERL = perl.exe # Enable use of available compiler optimizations? !ifndef OPTIMIZATIONS OPTIMIZATIONS = 2 !endif # Enable debugging symbols? !ifndef DEBUG DEBUG = 0 !endif !ifdef FOSSIL_DEBUG DEBUG = 1 !endif # Build the OpenSSL libraries? !ifndef FOSSIL_BUILD_SSL FOSSIL_BUILD_SSL = 0 !endif # Build the included zlib library? !ifndef FOSSIL_BUILD_ZLIB FOSSIL_BUILD_ZLIB = 1 !endif # Link everything except SQLite dynamically? !ifndef FOSSIL_DYNAMIC_BUILD FOSSIL_DYNAMIC_BUILD = 0 !endif # Enable relative paths in external diff/gdiff? !ifndef FOSSIL_ENABLE_EXEC_REL_PATHS FOSSIL_ENABLE_EXEC_REL_PATHS = 0 !endif # Enable the JSON API? !ifndef FOSSIL_ENABLE_JSON FOSSIL_ENABLE_JSON = 0 !endif # Enable OpenSSL support? !ifndef FOSSIL_ENABLE_SSL FOSSIL_ENABLE_SSL = 0 !endif # Enable the Tcl integration subsystem? !ifndef FOSSIL_ENABLE_TCL FOSSIL_ENABLE_TCL = 0 !endif # Enable TH1 scripts in embedded documentation files? !ifndef FOSSIL_ENABLE_TH1_DOCS FOSSIL_ENABLE_TH1_DOCS = 0 !endif # Enable TH1 hooks for commands and web pages? !ifndef FOSSIL_ENABLE_TH1_HOOKS FOSSIL_ENABLE_TH1_HOOKS = 0 !endif # Enable support for Windows XP with Visual Studio 201x? !ifndef FOSSIL_ENABLE_WINXP FOSSIL_ENABLE_WINXP = 0 !endif # Enable support for the SQLite Encryption Extension? !ifndef USE_SEE USE_SEE = 0 !endif !if $(FOSSIL_ENABLE_SSL)!=0 SSLDIR = $(B)\compat\openssl SSLINCDIR = $(SSLDIR)\include !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLLIBDIR = $(SSLDIR) !else SSLLIBDIR = $(SSLDIR) !endif SSLLIB = libssl.lib libcrypto.lib user32.lib gdi32.lib crypt32.lib !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" !message Using 'x64' platform for OpenSSL... SSLCONFIG = VC-WIN64A no-asm no-ssl3 no-weak-ssl-ciphers !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLCONFIG = $(SSLCONFIG) shared !else SSLCONFIG = $(SSLCONFIG) no-shared !endif !elseif "$(PLATFORM)"=="ia64" !message Using 'ia64' platform for OpenSSL... SSLCONFIG = VC-WIN64I no-asm no-ssl3 no-weak-ssl-ciphers !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLCONFIG = $(SSLCONFIG) shared !else SSLCONFIG = $(SSLCONFIG) no-shared !endif !else !message Assuming 'x86' platform for OpenSSL... SSLCONFIG = VC-WIN32 no-asm no-ssl3 no-weak-ssl-ciphers !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLCONFIG = $(SSLCONFIG) shared !else SSLCONFIG = $(SSLCONFIG) no-shared !endif !endif !endif !if $(FOSSIL_ENABLE_TCL)!=0 TCLDIR = $(B)\compat\tcl-8.6 TCLSRCDIR = $(TCLDIR) TCLINCDIR = $(TCLSRCDIR)\generic !endif # zlib options ZINCDIR = $(B)\compat\zlib ZLIBDIR = $(B)\compat\zlib !if $(FOSSIL_DYNAMIC_BUILD)!=0 ZLIB = zdll.lib !else ZLIB = zlib.lib !endif INCL = /I. /I"$(OX)" /I"$(SRCDIR)" /I"$(SRCDIR_extsrc)" /I"$(B)\win\include" INCL = $(INCL) /I"$(ZINCDIR)" !if $(FOSSIL_ENABLE_SSL)!=0 INCL = $(INCL) /I"$(SSLINCDIR)" !endif !if $(FOSSIL_ENABLE_TCL)!=0 INCL = $(INCL) /I"$(TCLINCDIR)" !endif CFLAGS = /nologo /W2 /WX /utf-8 LDFLAGS = CFLAGS = $(CFLAGS) /D_CRT_SECURE_NO_DEPRECATE /D_CRT_SECURE_NO_WARNINGS CFLAGS = $(CFLAGS) /D_CRT_NONSTDC_NO_DEPRECATE /D_CRT_NONSTDC_NO_WARNINGS !if $(FOSSIL_DYNAMIC_BUILD)!=0 LDFLAGS = $(LDFLAGS) /MANIFEST !else LDFLAGS = $(LDFLAGS) /NODEFAULTLIB:msvcrt /MANIFEST:NO !endif !if $(FOSSIL_ENABLE_WINXP)!=0 XPCFLAGS = $(XPCFLAGS) /D_WIN32_WINNT=0x0501 /D_USING_V110_SDK71_=1 CFLAGS = $(CFLAGS) $(XPCFLAGS) # # NOTE: For regular builds, /OSVERSION defaults to the /SUBSYSTEM version and # explicit initialization is redundant, but is required for post-built edits. # !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" XPLDFLAGS = $(XPLDFLAGS) /OSVERSION:5.02 /SUBSYSTEM:CONSOLE,5.02 !else XPLDFLAGS = $(XPLDFLAGS) /OSVERSION:5.01 /SUBSYSTEM:CONSOLE,5.01 !endif LDFLAGS = $(LDFLAGS) $(XPLDFLAGS) # # NOTE: Only XPCFLAGS is forwarded to the OpenSSL configuration, and XPLDFLAGS # is applied in a separate post-build step, see below for more information. # !if $(FOSSIL_ENABLE_SSL)!=0 SSLCONFIG = $(SSLCONFIG) $(XPCFLAGS) !endif !endif !if $(FOSSIL_DYNAMIC_BUILD)!=0 !if $(DEBUG)!=0 CRTFLAGS = /MDd !else CRTFLAGS = /MD !endif !else !if $(DEBUG)!=0 CRTFLAGS = /MTd !else CRTFLAGS = /MT !endif !endif !if $(OPTIMIZATIONS)>3 RELOPTS = /Os !elseif $(OPTIMIZATIONS)>2 RELOPTS = /Ox !elseif $(OPTIMIZATIONS)>1 RELOPTS = /O2 !elseif $(OPTIMIZATIONS)>0 RELOPTS = /O1 !else RELOPTS = !endif !if $(DEBUG)!=0 CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG LDFLAGS = $(LDFLAGS) /DEBUG !else CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS) !endif BCC = $(CC) $(CFLAGS) TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) MTC = mt LIBS = ws2_32.lib advapi32.lib dnsapi.lib LIBDIR = !if $(FOSSIL_DYNAMIC_BUILD)!=0 TCC = $(TCC) /DFOSSIL_DYNAMIC_BUILD=1 RCC = $(RCC) /DFOSSIL_DYNAMIC_BUILD=1 !endif LIBS = $(LIBS) $(ZLIB) LIBDIR = $(LIBDIR) /LIBPATH:"$(ZLIBDIR)" !if $(FOSSIL_ENABLE_JSON)!=0 TCC = $(TCC) /DFOSSIL_ENABLE_JSON=1 RCC = $(RCC) /DFOSSIL_ENABLE_JSON=1 !endif !if $(FOSSIL_ENABLE_SSL)!=0 TCC = $(TCC) /DFOSSIL_ENABLE_SSL=1 RCC = $(RCC) /DFOSSIL_ENABLE_SSL=1 LIBS = $(LIBS) $(SSLLIB) LIBDIR = $(LIBDIR) /LIBPATH:"$(SSLLIBDIR)" !endif !if $(FOSSIL_ENABLE_EXEC_REL_PATHS)!=0 TCC = $(TCC) /DFOSSIL_ENABLE_EXEC_REL_PATHS=1 RCC = $(RCC) /DFOSSIL_ENABLE_EXEC_REL_PATHS=1 !endif !if $(FOSSIL_ENABLE_TH1_DOCS)!=0 TCC = $(TCC) /DFOSSIL_ENABLE_TH1_DOCS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TH1_DOCS=1 !endif !if $(FOSSIL_ENABLE_TH1_HOOKS)!=0 TCC = $(TCC) /DFOSSIL_ENABLE_TH1_HOOKS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TH1_HOOKS=1 !endif !if $(FOSSIL_ENABLE_TCL)!=0 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 !if $(USE_SEE)!=0 TCC = $(TCC) /DUSE_SEE=1 RCC = $(RCC) /DUSE_SEE=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 $PIKCHR_OPTIONS { }] {/D} MSC_PIKCHR_OPTIONS set j " \\\n " writeln "PIKCHR_OPTIONS = [join $MSC_PIKCHR_OPTIONS $j]\n" writeln -nonewline "SRC = " set i 0 foreach s [lsort $src] { if {$i > 0} { writeln " \\" writeln -nonewline " " } writeln -nonewline "\"\$(OX)\\${s}_.c\""; incr i } foreach s [lsort $src_ext] { writeln " \\" writeln -nonewline " " writeln -nonewline "\"\$(SRCDIR_extsrc)\\${s}.c\""; incr i } writeln "\n" writeln -nonewline "EXTRA_FILES = " set i 0 foreach s [lsort $extra_files] { if {$i > 0} { writeln " \\" writeln -nonewline " " } set s [regsub -all / $s \\] writeln -nonewline "\"\$(SRCDIR)\\${s}\""; incr i } writeln "\n" set AdditionalObj [list shell sqlite3 th th_lang th_tcl cson_amalgamation pikchr] 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 -nonewline " \"\$(OX)\\fossil.res\"\n\n" writeln [string map [list <<<NEXT_LINE>>> \\] { !ifndef BASEAPPNAME BASEAPPNAME = fossil !endif APPNAME = $(OX)\$(BASEAPPNAME)$(E) PDBNAME = $(OX)\$(BASEAPPNAME)$(P) APPTARGETS = all: "$(OX)" "$(APPNAME)" $(BASEAPPNAME): "$(APPNAME)" $(BASEAPPNAME)$(E): "$(APPNAME)" install: "$(APPNAME)" echo F | xcopy /Y "$(APPNAME)" "$(INSTALLDIR)"\* !if $(DEBUG)!=0 echo F | xcopy /Y "$(PDBNAME)" "$(INSTALLDIR)"\* !endif $(OX): @-mkdir $@ zlib: @echo Building zlib from "$(ZLIBDIR)"... !if $(FOSSIL_ENABLE_WINXP)!=0 @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 clean-zlib: @pushd "$(ZLIBDIR)" && $(MAKE) /f win32\Makefile.msc clean && popd !if $(FOSSIL_ENABLE_SSL)!=0 openssl: @echo Building OpenSSL from "$(SSLDIR)"... !if $(FOSSIL_ENABLE_WINXP)!=0 @echo Passing XPCFLAGS = [ $(XPCFLAGS) ] to the OpenSSL configuration... !endif !ifdef PERLDIR @pushd "$(SSLDIR)" && "$(PERLDIR)\$(PERL)" Configure $(SSLCONFIG) && popd !else @pushd "$(SSLDIR)" && "$(PERL)" Configure $(SSLCONFIG) && popd !endif @pushd "$(SSLDIR)" && $(MAKE) && popd !if $(FOSSIL_ENABLE_WINXP)!=0 && $(FOSSIL_DYNAMIC_BUILD)!=0 # # NOTE: Appending custom linker flags to the OpenSSL default linker flags is # somewhat difficult, as summarized in this Fossil Forum post: # # https://fossil-scm.org/forum/forumpost/a9a2d6af28b # # Therefore the custom linker flags required for Windows XP dynamic builds are # applied in a separate post-build step. # # If the build stops here, or if the custom linker flags are outside the scope # of `editbin` or `link /EDIT` (i.e. additional libraries), consider tweaking # the OpenSSL makefile by hand. # # Also note that this step changes the subsystem for the OpenSSL DLLs from # WINDOWS to CONSOLE, but which has no effect on DLLs. # @echo Applying XPLDFLAGS = [ $(XPLDFLAGS) ] to the OpenSSL DLLs... @for /F "usebackq delims=" %F in (`dir /A:-D/B "$(SSLDIR)\*.dll" 2^>nul`) <<<NEXT_LINE>>> do @( <<<NEXT_LINE>>> echo %F & <<<NEXT_LINE>>> link /EDIT /NOLOGO $(XPLDFLAGS) "$(SSLDIR)\%F" || exit 1 <<<NEXT_LINE>>> ) !endif clean-openssl: @pushd "$(SSLDIR)" && $(MAKE) clean && popd !endif !if $(FOSSIL_BUILD_ZLIB)!=0 APPTARGETS = $(APPTARGETS) zlib !endif !if $(FOSSIL_ENABLE_SSL)!=0 !if $(FOSSIL_BUILD_SSL)!=0 APPTARGETS = $(APPTARGETS) openssl !endif !endif "$(APPNAME)" : $(APPTARGETS) "$(OBJDIR)\translate$E" "$(OBJDIR)\mkindex$E" "$(OBJDIR)\codecheck1$E" "$(OX)\headers" $(OBJ) "$(OX)\linkopts" "$(OBJDIR)\codecheck1$E" $(SRC) link $(LDFLAGS) /OUT:$@ /PDB:$(@D)\ $(LIBDIR) Wsetargv.obj "$(OX)\fossil.res" @"$(OX)\linkopts" if exist "$(B)\win\fossil.exe.manifest" <<<NEXT_LINE>>> $(MTC) -nologo -manifest "$(B)\win\fossil.exe.manifest" -outputresource:$@;1 "$(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 "\techo \$(LIBS) $redir \$@" writeln { "$(OBJDIR)\translate$E": "$(SRCDIR_tools)\translate.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** "$(OBJDIR)\makeheaders$E": "$(SRCDIR_tools)\makeheaders.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** "$(OBJDIR)\mkindex$E": "$(SRCDIR_tools)\mkindex.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** "$(OBJDIR)\mkbuiltin$E": "$(SRCDIR_tools)\mkbuiltin.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** "$(OBJDIR)\mkversion$E": "$(SRCDIR_tools)\mkversion.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** "$(OBJDIR)\codecheck1$E": "$(SRCDIR_tools)\codecheck1.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** !if $(USE_SEE)!=0 SEE_FLAGS = /DSQLITE_HAS_CODEC=1 /DSQLITE_SHELL_DBKEY_PROC=fossil_key SQLITE3_SHELL_SRC = $(SRCDIR)\shell-see.c SQLITE3_SRC = $(SRCDIR_extsrc)\sqlite3-see.c !else SEE_FLAGS = SQLITE3_SHELL_SRC = $(SRCDIR_extsrc)\shell.c SQLITE3_SRC = $(SRCDIR_extsrc)\sqlite3.c !endif "$(OX)\shell$O" : "$(SQLITE3_SHELL_SRC)" "$(B)\win\Makefile.msc" $(TCC) /Fo$@ /Fd$(@D)\ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $(SEE_FLAGS) -c "$(SQLITE3_SHELL_SRC)" "$(OX)\sqlite3$O" : "$(SQLITE3_SRC)" "$(B)\win\Makefile.msc" $(TCC) /Fo$@ /Fd$(@D)\ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) "$(SQLITE3_SRC)" "$(OX)\th$O" : "$(SRCDIR)\th.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\th_lang$O" : "$(SRCDIR)\th_lang.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\th_tcl$O" : "$(SRCDIR)\th_tcl.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\pikchr$O" : "$(SRCDIR_extsrc)\pikchr.c" $(TCC) $(PIKCHR_OPTIONS) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" "$(B)\phony.h" "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" > $@ "$(B)\phony.h" : rem Force rebuild of VERSION.h whenever nmake is run "$(OX)\cson_amalgamation$O" : "$(SRCDIR_extsrc)\cson_amalgamation.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC) $** > $@ "$(OX)\builtin_data.h": "$(OBJDIR)\mkbuiltin$E" "$(OX)\builtin_data.reslist" "$(OBJDIR)\mkbuiltin$E" --prefix "$(SRCDIR)/" --reslist "$(OX)\builtin_data.reslist" > $@ cleanx: -del "$(OX)\*.obj" 2>NUL -del "$(OBJDIR)\*.obj" 2>NUL -del "$(OX)\*_.c" 2>NUL -del "$(OX)\*.h" 2>NUL -del "$(OX)\*.ilk" 2>NUL -del "$(OX)\*.map" 2>NUL -del "$(OX)\*.res" 2>NUL -del "$(OX)\*.reslist" 2>NUL -del "$(OX)\headers" 2>NUL -del "$(OX)\linkopts" 2>NUL -del "$(OX)\vc*.pdb" 2>NUL clean: cleanx -del "$(APPNAME)" 2>NUL -del "$(PDBNAME)" 2>NUL -del "$(OBJDIR)\translate$E" 2>NUL -del "$(OBJDIR)\translate$P" 2>NUL -del "$(OBJDIR)\mkindex$E" 2>NUL -del "$(OBJDIR)\mkindex$P" 2>NUL -del "$(OBJDIR)\makeheaders$E" 2>NUL -del "$(OBJDIR)\makeheaders$P" 2>NUL -del "$(OBJDIR)\mkversion$E" 2>NUL -del "$(OBJDIR)\mkversion$P" 2>NUL -del "$(OBJDIR)\mkcss$E" 2>NUL -del "$(OBJDIR)\mkcss$P" 2>NUL -del "$(OBJDIR)\codecheck1$E" 2>NUL -del "$(OBJDIR)\codecheck1$P" 2>NUL -del "$(OBJDIR)\mkbuiltin$E" 2>NUL -del "$(OBJDIR)\mkbuiltin$P" 2>NUL realclean: clean "$(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" } writeln {"$(OX)\builtin_data.reslist": $(EXTRA_FILES) "$(B)\win\Makefile.msc"} set redir {>} foreach s [lsort $extra_files] { writeln "\techo \"\$(SRCDIR)\\${s}\" $redir \$@" set redir {>>} } foreach s [lsort $src] { set extra_h($s) {} } set extra_h(builtin) " \"\$(OX)\\builtin_data.h\"" set extra_h(dispatch) " \"\$(OX)\\page_index.h\"" writeln "" foreach s [lsort $src] { writeln "\"\$(OX)\\$s\$O\" : \"\$(OX)\\${s}_.c\" \"\$(OX)\\${s}.h\"$extra_h($s)" writeln "\t\$(TCC) /Fo\$@ /Fd\$(@D)\\ -c \"\$(OX)\\${s}_.c\"\n" writeln "\"\$(OX)\\${s}_.c\" : \"\$(SRCDIR)\\$s.c\"" writeln "\t\"\$(OBJDIR)\\translate\$E\" \$** > \$@\n" } writeln "\"\$(OX)\\fossil.res\" : \"\$(B)\\win\\fossil.rc\"" writeln "\t\$(RCC) /fo \$@ \$**\n" writeln "\"\$(OX)\\headers\": \"\$(OBJDIR)\\makeheaders\$E\" \"\$(OX)\\page_index.h\" \"\$(OX)\\builtin_data.h\" \"\$(OX)\\VERSION.h\"" writeln -nonewline "\t\"\$(OBJDIR)\\makeheaders\$E\" " set i 0 foreach s [lsort $src] { if {$i > 0} { writeln " \\" writeln -nonewline "\t\t\t" } writeln -nonewline "\"\$(OX)\\${s}_.c\":\"\$(OX)\\$s.h\""; incr i } foreach s [lsort $src_ext] { writeln " \\" writeln -nonewline "\t\t\t" writeln -nonewline "\"\$(SRCDIR_extsrc)\\${s}.c\":\"\$(OX)\\$s.h\""; incr i } writeln " \\\n\t\t\t\"\$(SRCDIR_extsrc)\\sqlite3.h\" \\" writeln "\t\t\t\"\$(SRCDIR)\\th.h\" \\" writeln "\t\t\t\"\$(OX)\\VERSION.h\" \\" writeln "\t\t\t\"\$(SRCDIR_extsrc)\\cson_amalgamation.h\"" writeln "\t@copy /Y nul: $@" close $output_file # # End of the win/Makefile.msc output ############################################################################## ############################################################################## |
Added tools/man_page_command_list.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 | #!/usr/bin/env tclsh # man_page_command_list.tcl - generates common command list for fossil.1 # Tunable configuration. set columns 5 set width 15 # The only supported command-line argument is the optional output filename. if {[llength $argv] == 1} { set file [lindex $argv 0] } # Get list of common commands. set commands [exec fossil help] regsub -nocase {.*?\ncommon commands:.*\n} $commands {} commands regsub -nocase {\nthis is fossil version.*} $commands {} commands regsub -all {\s+} $commands " " commands set commands [lsort $commands] # Compute number of rows. set rows [expr {([llength $commands] + $columns - 1) / $columns}] # Generate text one line at a time. set text {} for {set row 0} {$row < $rows} {incr row} { # Separate rows with line break. if {$row} { append text .br\n } # Generate the row of commands. for {set col 0} {$col < $columns} {incr col} { set i [expr {$col * $rows + $row}] if {$i < [llength $commands]} { append text [format %-*s $width [lindex $commands $i]] } } append text \n } # Strip trailing whitespace from each line. regsub -all {\s+\n} $text \n text # Output text. if {[info exists file]} { # If a filename was specified, read the file for use as a template. set chan [open $file] set data [read $chan] close $chan # Locate the part of the file to replace. if {[regexp -indices {\n\.SH Common COMMANDs:\n\n(.*?)\n\.SH} $data\ _ range]} { # If found, replace with the updated command list. set chan [open $file w] puts -nonewline $chan [string replace $data\ [lindex $range 0] [lindex $range 1] $text] close $chan } else { # If not found, abort. error "could not find command list in man file \"$file\"" } } else { # If no filename was specified, write to stdout. puts $text } # vim: set sts=4 sw=4 tw=80 et ft=tcl: |
Added tools/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 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 | /* ** 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. ** ** Additionally, the input files may be listed in a separate list file (one ** resource name per line, optionally enclosed in double quotes). Pass the list ** via '--reslist <the-list-file>' option. Both lists, from the command line and ** the list file, are merged; duplicate file names skipped from processing. ** This option is useful to get around the command line length limitations ** under some OS, like Windows. ** ** The makefiles use this utility to 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> #include <ctype.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; } #ifndef FOSSIL_DEBUG /* ** Try to compress a javascript file by removing unnecessary whitespace. ** ** Warning: This compression routine does not necessarily work for any ** arbitrary Javascript source file. But it should work ok for the ** well-behaved source files in this project. */ static void compressJavascript(unsigned char *z, int *pn){ int n = *pn; int i, j, k; for(i=j=0; i<n; i++){ unsigned char c = z[i]; if( c=='/' && (i==0 || z[i-1]!=':')){ if( z[i+1]=='*' ){ while( j>0 && (z[j-1]==' ' || z[j-1]=='\t') ){ j--; } for(k=i+3; k<n && (z[k]!='/' || z[k-1]!='*'); k++){} i = k; continue; }else if( z[i+1]=='/' ){ while( j>0 && (z[j-1]==' ' || z[j-1]=='\t') ){ j--; } for(k=i+2; k<n && z[k]!='\n'; k++){} i = k-1; continue; } } if( c=='\n' ){ if( j==0 ) continue; while( j>0 && isspace(z[j-1]) ) j--; z[j++] = '\n'; while( i+1<n && isspace(z[i+1]) ) i++; continue; } z[j++] = c; } z[j] = 0; *pn = j; } #endif /* FOSSIL_DEBUG */ /* ** There is an instance of the following for each file translated. */ typedef struct Resource Resource; struct Resource { char *zName; int nByte; int idx; }; typedef struct ResourceList ResourceList; struct ResourceList { Resource *aRes; int nRes; char *buf; long bufsize; }; Resource *read_reslist(char *name, ResourceList *list){ #define RESLIST_BUF_MAXBYTES (1L<<20) /* 1 MB of text */ FILE *in; long filesize = 0L; long linecount = 0L; char *p = 0; char *pb = 0; memset(list, 0, sizeof(*list)); if( (in = fopen(name, "rb"))==0 ){ return list->aRes; } fseek(in, 0L, SEEK_END); filesize = ftell(in); rewind(in); if( filesize > RESLIST_BUF_MAXBYTES ){ fprintf(stderr, "List file [%s] must be smaller than %ld bytes\n", name, RESLIST_BUF_MAXBYTES); return list->aRes; } list->bufsize = filesize; list->buf = (char *)calloc((list->bufsize + 2), sizeof(list->buf[0])); if( list->buf==0 ){ fprintf(stderr, "failed to allocated %ld bytes\n", list->bufsize + 1); list->bufsize = 0L; return list->aRes; } filesize = fread(list->buf, sizeof(list->buf[0]),list->bufsize, in); if ( filesize!=list->bufsize ){ fprintf(stderr, "failed to read [%s]\n", name); return list->aRes; } fclose(in); /* ** append an extra newline (if missing) for a correct line count */ if( list->buf[list->bufsize-1]!='\n' ) list->buf[list->bufsize]='\n'; linecount = 0L; for( p = strchr(list->buf, '\n'); p && p <= &list->buf[list->bufsize-1]; p = strchr(++p, '\n') ){ ++linecount; } list->aRes = (Resource *)calloc(linecount+1, sizeof(list->aRes[0])); for( pb = list->buf, p = strchr(pb, '\n'); p && p <= &list->buf[list->bufsize-1]; pb = ++p, p = strchr(pb, '\n') ){ char *path = pb; char *pe = p - 1; /* strip leading and trailing whitespace */ while( path < p && isspace(*path) ) ++path; while( pe > path && isspace(*pe) ){ *pe = '\0'; --pe; } /* strip outer quotes */ while( path < p && *path=='\"') ++path; while( pe > path && *pe=='\"' ){ *pe = '\0'; --pe; } *p = '\0'; /* skip empty path */ if( *path ){ list->aRes[list->nRes].zName = path; ++(list->nRes); } } return list->aRes; } void free_reslist(ResourceList *list){ if( list ){ if( list->buf ) free(list->buf); if( list->aRes) free(list->aRes); memset(list, 0, sizeof(*list)); } } /* ** Compare two Resource objects for sorting purposes. They sort ** in zName order so that Fossil can search for resources using ** a binary search. */ typedef int (*QsortCompareFunc)(const void *, const void*); static int compareResource(const Resource *a, const Resource *b){ return strcmp(a->zName, b->zName); } int remove_duplicates(ResourceList *list){ char dupNameAsc[64] = "\255"; char dupNameDesc[64] = ""; Resource dupResAsc; Resource dupResDesc; Resource *pDupRes; int dupcount = 0; int i; if( list->nRes==0 ){ return list->nRes; } /* ** scan for duplicates and assign their names to a string that would sort to ** the bottom, then re-sort and truncate the duplicates */ memset(dupNameAsc, dupNameAsc[0], sizeof(dupNameAsc)-2); memset(dupNameDesc, dupNameDesc[0], sizeof(dupNameDesc)-2); memset(&dupResAsc, 0, sizeof(dupResAsc)); dupResAsc.zName = dupNameAsc; memset(&dupResDesc, 0, sizeof(dupResDesc)); dupResDesc.zName = dupNameDesc; pDupRes = (compareResource(&dupResAsc, &dupResDesc) > 0 ? &dupResAsc : &dupResDesc); qsort(list->aRes, list->nRes, sizeof(list->aRes[0]), (QsortCompareFunc)compareResource); for( i=0; i<list->nRes-1 ; ++i){ Resource *res = &list->aRes[i]; while( i<list->nRes-1 && compareResource(res, &list->aRes[i+1])==0 ){ fprintf(stderr, "Skipped a duplicate file [%s]\n", list->aRes[i+1].zName); memcpy(&list->aRes[i+1], pDupRes, sizeof(list->aRes[0])); ++dupcount; ++i; } } if( dupcount == 0){ return list->nRes; } qsort(list->aRes, list->nRes, sizeof(list->aRes[0]), (QsortCompareFunc)compareResource); list->nRes -= dupcount; memset(&list->aRes[list->nRes], 0, sizeof(list->aRes[0])); return list->nRes; } int main(int argc, char **argv){ int i, sz; int j, n; ResourceList resList; Resource *aRes; int nRes; unsigned char *pData; int nErr = 0; int nSkip; int nPrefix = 0; #ifndef FOSSIL_DEBUG int nName; #endif if( argc==1 ){ fprintf(stderr, "usage\t:%s " "[--prefix path] [--reslist file] [resource-file1 ...]\n", argv[0] ); return 1; } if( argc>3 && strcmp(argv[1],"--prefix")==0 ){ nPrefix = (int)strlen(argv[2]); argc -= 2; argv += 2; } memset(&resList, 0, sizeof(resList)); if( argc>2 && strcmp(argv[1],"--reslist")==0 ){ if( read_reslist(argv[2], &resList)==0 ){ fprintf(stderr, "Failed to load resource list from [%s]", argv[2]); free_reslist(&resList); return 1; } argc -= 2; argv += 2; } if( argc>1 ){ aRes = realloc(resList.aRes, (resList.nRes+argc-1)*sizeof(resList.aRes[0])); if( aRes==0 || aRes==resList.aRes ){ fprintf(stderr, "realloc failed\n"); free_reslist(&resList); return 1; } resList.aRes = aRes; for(i=0; i<argc-1; i++){ resList.aRes[resList.nRes].zName = argv[i+1]; ++resList.nRes; } } if( resList.nRes==0 ){ fprintf(stderr,"No resource files to process\n"); free_reslist(&resList); return 1; } remove_duplicates(&resList); nRes = resList.nRes; aRes = resList.aRes; qsort(aRes, nRes, sizeof(aRes[0]), (QsortCompareFunc)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++; } #ifndef FOSSIL_DEBUG /* Compress javascript source files */ nName = (int)strlen(aRes[i].zName); if( (nName>3 && strcmp(&aRes[i].zName[nName-3],".js")==0) || (nName>7 && strcmp(&aRes[i].zName[nName-7], "/js.txt")==0) ){ int x = sz-nSkip; compressJavascript(pData+nSkip, &x); sz = x + nSkip; } #endif 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++){ char *z = aRes[i].zName; if( strlen(z)>=nPrefix ) z += nPrefix; while( z[0]=='.' || z[0]=='/' || z[0]=='\\' ){ z++; } aRes[i].zName = z; while( z[0] ){ if( z[0]=='\\' ) z[0] = '/'; z++; } } qsort(aRes, nRes, sizeof(aRes[0]), (QsortCompareFunc)compareResource); for(i=0; i<nRes; i++){ printf(" { \"%s\", bidata%d, %d },\n", aRes[i].zName, aRes[i].idx, aRes[i].nByte); } printf("};\n"); free_reslist(&resList); return nErr; } |
Added tools/mkindex.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 | /* ** Copyright (c) 2002 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 utility program scans Fossil source text looking for specially ** formatted comments and generates C source code for constant tables ** that define the behavior of commands, webpages, and settings. ** ** The source code is scanned for comment lines of the form: ** ** WEBPAGE: /abc/xyz ** COMMAND: cmdname ** SETTING: access-log ** ** The WEBPAGE and COMMAND comments should be followed by a function that ** implements the webpage or command. The form of this function is: ** ** void function_name(void){ ** ** Command names can divided into three classes: 1st-tier, 2nd-tier, ** and test. 1st-tier commands are the most frequently used and the ** ones that show up with "fossil help". 2nd-tier are seldom-used and/or ** legacy commands. Test commands are unsupported commands used for testing ** and analysis only. ** ** Commands are 1st-tier by default. If the command name begins with ** "test-" or if the command name has a "test" argument, then it becomes ** a test command. If the command name has a "2nd-tier" argument or ends ** with a "*" character, it is second tier. If the command name has an "alias" ** argument or ends with a "#" character, it is an alias: another name ** (a one-to-one replacement) for a command. Examples: ** ** COMMAND: abcde* ** COMMAND: fghij 2nd-tier ** COMMAND: mnopq# ** COMMAND: rstuv alias ** COMMAND: test-xyzzy ** COMMAND: xyzzy test ** ** A SETTING: may be followed by arguments that give additional attributes ** to that setting: ** ** SETTING: clean-blob versionable width=40 block-text ** SETTING: auto-shun boolean default=on ** ** New arguments may be added in future releases that set additional ** bits in the eCmdFlags field. ** ** Additional lines of comment after the COMMAND: or WEBPAGE: or SETTING: ** become the built-in help text for that command or webpage or setting. ** Backslashes must be escaped ("\\" in comment yields "\" in the help text.) ** ** Multiple COMMAND: entries can be attached to the same command, thus ** creating multiple aliases for that command. Similarly, multiple ** WEBPAGE: entries can be attached to the same webpage function, to give ** that page aliases. ** ** For SETTING: entries, the default value for the setting can be specified ** using a default=VALUE argument if the default contains no spaces. If the ** default value does contain spaces, use a separate line like this: ** ** SETTING: pgp-command ** DEFAULT: gpg --clearsign -o ** ** If no default is supplied, the default is assumed to be an empty string ** or "off" in the case of a boolean. */ #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> /*************************************************************************** ** These macros must match similar macros in dispatch.c. ** ** Allowed values for CmdOrPage.eCmdFlags. */ #define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */ #define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */ #define CMDFLAG_TEST 0x0004 /* Commands for testing only */ #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ #define CMDFLAG_COMMAND 0x0010 /* A command */ #define CMDFLAG_SETTING 0x0020 /* A setting */ #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */ #define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */ #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ /**************************************************************************/ /* ** Each entry looks like this: */ typedef struct Entry { int eType; /* CMDFLAG_* values */ char *zIf; /* Enclose in #if */ char *zFunc; /* Name of implementation */ char *zPath; /* Webpage or command name */ char *zHelp; /* Help text */ char *zDflt; /* Default value for settings */ char *zVar; /* config.name for settings, if different from zPath */ int iHelp; /* Index of Help text */ int iWidth; /* Display width for SETTING: values */ } Entry; /* ** Maximum number of entries */ #define N_ENTRY 5000 /* ** Maximum size of a help message */ #define MX_HELP 250000 /* ** Table of entries */ Entry aEntry[N_ENTRY]; /* ** Current help message accumulator */ char zHelp[MX_HELP]; int nHelp; /* ** Most recently encountered #if */ char zIf[2000]; /* ** How many entries are used */ int nUsed; int nFixed; /* ** Current filename and line number */ char *zFile; int nLine; /* ** Number of errors */ int nErr = 0; /* ** Duplicate N characters of a string. */ char *string_dup(const char *zSrc, int n){ char *z; if( n<0 ) n = strlen(zSrc); z = malloc( n+1 ); if( z==0 ){ fprintf(stderr,"Out of memory!\n"); exit(1); } strncpy(z, zSrc, n); z[n] = 0; return z; } /* ** Safe isspace macro. Works with signed characters. */ int fossil_isspace(char c){ return c==' ' || (c<='\r' && c>='\t'); } /* ** Safe isident macro. Works with signed characters. */ int fossil_isident(char c){ if( c>='a' && c<='z' ) return 1; if( c>='A' && c<='Z' ) return 1; if( c>='0' && c<='9' ) return 1; if( c=='_' ) return 1; return 0; } /* ** Scan a line looking for comments containing zLabel. Make ** new entries if found. */ void scan_for_label(const char *zLabel, char *zLine, int eType){ int i, j; int len = strlen(zLabel); if( nUsed>=N_ENTRY ) return; for(i=0; fossil_isspace(zLine[i]) || zLine[i]=='*'; i++){} if( zLine[i]!=zLabel[0] ) return; if( strncmp(&zLine[i],zLabel, len)==0 ){ i += len; }else{ return; } while( fossil_isspace(zLine[i]) ){ i++; } if( zLine[i]=='/' ) i++; for(j=0; zLine[i+j] && !fossil_isspace(zLine[i+j]); j++){} aEntry[nUsed].eType = eType; if( eType & CMDFLAG_WEBPAGE ){ aEntry[nUsed].zPath = string_dup(&zLine[i-1], j+1); aEntry[nUsed].zPath[0] = '/'; }else{ aEntry[nUsed].zPath = string_dup(&zLine[i], j); } aEntry[nUsed].zFunc = 0; if( (eType & CMDFLAG_COMMAND)!=0 ){ if( strncmp(&zLine[i], "test-", 5)==0 ){ /* Commands that start with "test-" are test-commands */ aEntry[nUsed].eType |= CMDFLAG_TEST; }else if( zLine[i+j-1]=='*' ){ /* If the command name ends in '*', remove the '*' from the name ** but move the command into the second tier */ aEntry[nUsed].zPath[j-1] = 0; aEntry[nUsed].eType |= CMDFLAG_2ND_TIER; }else if( zLine[i+j-1]=='#' ){ /* If the command name ends in '#', remove the '#' from the name ** but move the command into aliases */ aEntry[nUsed].zPath[j-1] = 0; aEntry[nUsed].eType |= CMDFLAG_ALIAS; }else{ /* Otherwise, this is a first-tier command */ aEntry[nUsed].eType |= CMDFLAG_1ST_TIER; } } /* Process additional flags that might follow the command name */ while( zLine[i+j]!=0 ){ i += j; while( fossil_isspace(zLine[i]) ){ i++; } if( zLine[i]==0 ) break; for(j=0; zLine[i+j] && !fossil_isspace(zLine[i+j]); j++){} if( j==8 && strncmp(&zLine[i], "1st-tier", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_2ND_TIER|CMDFLAG_TEST|CMDFLAG_ALIAS); aEntry[nUsed].eType |= CMDFLAG_1ST_TIER; }else if( j==8 && strncmp(&zLine[i], "2nd-tier", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_TEST|CMDFLAG_ALIAS); aEntry[nUsed].eType |= CMDFLAG_2ND_TIER; }else if( j==4 && strncmp(&zLine[i], "test", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_2ND_TIER|CMDFLAG_ALIAS); aEntry[nUsed].eType |= CMDFLAG_TEST; }else if( j==5 && strncmp(&zLine[i], "alias", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_2ND_TIER|CMDFLAG_TEST); aEntry[nUsed].eType |= CMDFLAG_ALIAS; }else if( j==11 && strncmp(&zLine[i], "raw-content", j)==0 ){ aEntry[nUsed].eType |= CMDFLAG_RAWCONTENT; }else if( j==7 && strncmp(&zLine[i], "boolean", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_BLOCKTEXT); aEntry[nUsed].iWidth = 0; aEntry[nUsed].eType |= CMDFLAG_BOOLEAN; }else if( j==10 && strncmp(&zLine[i], "block-text", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_BOOLEAN); aEntry[nUsed].eType |= CMDFLAG_BLOCKTEXT; }else if( j==10 && strncmp(&zLine[i], "keep-empty", j)==0 ){ aEntry[nUsed].eType |= CMDFLAG_KEEPEMPTY; }else if( j==11 && strncmp(&zLine[i], "versionable", j)==0 ){ aEntry[nUsed].eType |= CMDFLAG_VERSIONABLE; }else if( j==9 && strncmp(&zLine[i], "sensitive", j)==0 ){ aEntry[nUsed].eType |= CMDFLAG_SENSITIVE; }else if( j>6 && strncmp(&zLine[i], "width=", 6)==0 ){ aEntry[nUsed].iWidth = atoi(&zLine[i+6]); }else if( j>8 && strncmp(&zLine[i], "default=", 8)==0 ){ aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8); }else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){ aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9); }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){ aEntry[nUsed].eType |= CMDFLAG_HIDDEN; }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){ aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT; }else{ fprintf(stderr, "%s:%d: unknown option: '%.*s'\n", zFile, nLine, j, &zLine[i]); nErr++; } } nUsed++; return; } /* ** Check to see if the current line is an #if and if it is, add it to ** the zIf[] string. If the current line is an #endif or #else or #elif ** then cancel the current zIf[] string. */ void scan_for_if(const char *zLine){ int i; int len; if( zLine[0]!='#' ) return; for(i=1; fossil_isspace(zLine[i]); i++){} if( zLine[i]==0 ) return; len = strlen(&zLine[i]); if( strncmp(&zLine[i],"if",2)==0 ){ zIf[0] = '#'; memcpy(&zIf[1], &zLine[i], len+1); }else if( zLine[i]=='e' ){ zIf[0] = 0; } } /* ** Check to see if the current line is a "** DEFAULT: ..." line for a ** SETTING definition. If so, remember the default value. */ void scan_for_default(const char *zLine){ int len; const char *z; if( nUsed<1 ) return; if( (aEntry[nUsed-1].eType & CMDFLAG_SETTING)==0 ) return; if( strncmp(zLine, "** DEFAULT: ", 12)!=0 ) return; z = zLine + 12; while( fossil_isspace(z[0]) ) z++; len = (int)strlen(z); while( len>0 && fossil_isspace(z[len-1]) ){ len--; } aEntry[nUsed-1].zDflt = string_dup(z,len); } /* ** Scan a line for a function that implements a web page or command. */ void scan_for_func(char *zLine){ int i,j,k; char *z; int isSetting; if( nUsed<=nFixed ) return; if( strncmp(zLine, "**", 2)==0 && fossil_isspace(zLine[2]) && strlen(zLine)<sizeof(zHelp)-nHelp-1 && nUsed>nFixed && strncmp(zLine,"** COMMAND:",11)!=0 && strncmp(zLine,"** WEBPAGE:",11)!=0 && strncmp(zLine,"** SETTING:",11)!=0 && strncmp(zLine,"** DEFAULT:",11)!=0 ){ if( zLine[2]=='\n' ){ zHelp[nHelp++] = '\n'; }else{ if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0; strcpy(&zHelp[nHelp], &zLine[3]); nHelp += strlen(&zHelp[nHelp]); } return; } for(i=0; fossil_isspace(zLine[i]); i++){} if( zLine[i]==0 ) return; isSetting = (aEntry[nFixed].eType & CMDFLAG_SETTING)!=0; if( !isSetting ){ if( strncmp(&zLine[i],"void",4)!=0 ){ if( zLine[i]!='*' ) goto page_skip; return; } i += 4; if( !fossil_isspace(zLine[i]) ) goto page_skip; while( fossil_isspace(zLine[i]) ){ i++; } for(j=0; fossil_isident(zLine[i+j]); j++){} if( j==0 ) goto page_skip; }else{ j = 0; } for(k=nHelp-1; k>=0 && fossil_isspace(zHelp[k]); k--){} nHelp = k+1; zHelp[nHelp] = 0; for(k=0; k<nHelp && fossil_isspace(zHelp[k]); k++){} if( k<nHelp ){ z = string_dup(&zHelp[k], nHelp-k); }else{ z = ""; } for(k=nFixed; k<nUsed; k++){ aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0; aEntry[k].zFunc = isSetting ? "0" : string_dup(&zLine[i], j); aEntry[k].zHelp = z; z = 0; aEntry[k].iHelp = nFixed; } if( !isSetting ){ i+=j; while( fossil_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; } /* ** Compare two entries */ int e_compare(const void *a, const void *b){ const Entry *pA = (const Entry*)a; const Entry *pB = (const Entry*)b; return strcmp(pA->zPath, pB->zPath); } /* ** Build the binary search table. */ void build_table(void){ int i; int nWeb = 0; int mxLen = 0; qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare); printf( "/* Automatically generated code\n" "** DO NOT EDIT!\n" "**\n" "** This file was generated by the mkindex.exe program based on\n" "** comments in other Fossil source files.\n" "*/\n" ); /* Output declarations for all the action functions */ for(i=0; i<nFixed; i++){ if( aEntry[i].eType & CMDFLAG_SETTING ) continue; if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); printf("extern void %s(void);\n", aEntry[i].zFunc); if( aEntry[i].zIf ) printf("#endif\n"); } /* Output strings for all the help text */ for(i=0; i<nFixed; i++){ char *z = aEntry[i].zHelp; if( z==0 ) continue; if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); printf("static const char zHelp%03d[] =\n \"", aEntry[i].iHelp); while( *z ){ if( *z=='\n' ){ printf("\\n\"\n \""); }else if( *z=='"' ){ printf("\\\""); }else{ putchar(*z); } z++; } printf("\";\n"); if( aEntry[i].zIf ) printf("#endif\n"); } /* Generate the aCommand[] table */ printf("static const CmdOrPage aCommand[] = {\n"); for(i=0; i<nFixed; i++){ const char *z = aEntry[i].zPath; int n = strlen(z); if( n>mxLen ) mxLen = n; if( aEntry[i].zIf ){ printf("%s", aEntry[i].zIf); }else if( (aEntry[i].eType & CMDFLAG_WEBPAGE)!=0 ){ nWeb++; } printf(" { \"%.*s\",%*s%s,%*szHelp%03d, %3d, 0x%03x },\n", n, z, 25-n, "", aEntry[i].zFunc, (int)(29-strlen(aEntry[i].zFunc)), "", aEntry[i].iHelp, aEntry[i].iHelp, aEntry[i].eType ); if( aEntry[i].zIf ) printf("#endif\n"); } printf("};\n"); printf("#define FOSSIL_FIRST_CMD %d\n", nWeb); printf("#define FOSSIL_MX_CMDNAME %d /* max length of any command name */\n", mxLen); printf("#define FOSSIL_MX_CMDIDX %d /* max index for commands */\n", nFixed); /* Generate the aSetting[] table */ printf("const Setting aSetting[] = {\n"); for(i=0; i<nFixed; i++){ const char *z; const char *zVar; const char *zDef; if( (aEntry[i].eType & CMDFLAG_SETTING)==0 ) continue; z = aEntry[i].zPath; zVar = aEntry[i].zVar; zDef = aEntry[i].zDflt; if( zDef==0 ) zDef = ""; if( aEntry[i].zIf ){ printf("%s", aEntry[i].zIf); } printf(" { \"%s\",%*s", z, (int)(20-strlen(z)), ""); if( zVar ){ printf(" \"%s\",%*s", zVar, (int)(15-strlen(zVar)), ""); }else{ printf(" 0,%*s", 16, ""); } printf(" %3d, %d, %d, %d, \"%s\"%*s },\n", aEntry[i].iWidth, (aEntry[i].eType & CMDFLAG_VERSIONABLE)!=0, (aEntry[i].eType & CMDFLAG_BLOCKTEXT)!=0, (aEntry[i].eType & CMDFLAG_SENSITIVE)!=0, zDef, (int)(10-strlen(zDef)), "" ); if( aEntry[i].zIf ){ printf("#endif\n"); } } printf("{0,0,0,0,0,0,0}};\n"); } /* ** Process a single file of input */ void process_file(void){ FILE *in = fopen(zFile, "r"); char zLine[2000]; if( in==0 ){ fprintf(stderr,"%s: cannot open\n", zFile); return; } nLine = 0; while( fgets(zLine, sizeof(zLine), in) ){ nLine++; scan_for_if(zLine); scan_for_label("WEBPAGE:",zLine,CMDFLAG_WEBPAGE); scan_for_label("COMMAND:",zLine,CMDFLAG_COMMAND); scan_for_func(zLine); scan_for_label("SETTING:",zLine,CMDFLAG_SETTING); scan_for_default(zLine); } fclose(in); nUsed = nFixed; } int main(int argc, char **argv){ int i; memset(aEntry, 0, sizeof(Entry) * N_ENTRY); for(i=1; i<argc; i++){ zFile = argv[i]; process_file(); } build_table(); return nErr; } |
Added tools/mkversion.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 | /* ** 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. ** ** The output becomes the "VERSION.h" file. The output is a C-language ** header that contains #defines for various properties of the build: ** ** MANIFEST_UUID These values are text strings that ** MANIFEST_VERSION identify the Fossil check-in to which ** the source tree belongs. They do not ** take into account any uncommitted edits. ** ** FOSSIL_BUILD_HASH A hexadecimal string that is a strong hash ** of the MANIFEST_UUID together with the ** current time of the build. We normally want ** this to be different on each build, as the ** value is used to expire ETag: fields in ** HTTP requests. But if you need to do ** repeatable byte-for-byte identical builds, ** add the -DFOSSIL_BUILD_EPOCH=n option. ** ** MANIFEST_DATE The date/time of the source-code check-in ** MANIFEST_YEAR in various formats. ** MANIFEST_NUMERIC_DATE ** MANIFEST_NUMERIC_TIME ** ** RELEASE_VERSION The version number (from the VERSION source ** RELEASE_VERSION_NUMBER file) in various format. ** RELEASE_RESOURCE_VERSION ** ** New #defines may be added in the future. */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <time.h> #if defined(_MSC_VER) && (_MSC_VER < 1800) /* MSVS 2013 */ # define strtoll _strtoi64 #endif static FILE *open_for_reading(const char *zFilename){ FILE *f = fopen(zFilename, "r"); if( f==0 ){ fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename); exit(1); } return f; } /* ** Given an arbitrary-length input string key zIn, generate ** an N-byte hexadecimal hash of that string into zOut. */ static void hash(const char *zIn, int N, char *zOut){ unsigned char i, j, t; int m, n; unsigned char s[256]; for(m=0; m<256; m++){ s[m] = m; } for(j=0, m=n=0; m<256; m++, n++){ j += s[m] + zIn[n]; if( zIn[n]==0 ){ n = -1; } t = s[j]; s[j] = s[m]; s[m] = t; } i = j = 0; for(n=0; n<N-2; n+=2){ i++; t = s[i]; j += t; s[i] = s[j]; s[j] = t; t += s[i]; zOut[n] = "0123456789abcdef"[(t>>4)&0xf]; zOut[n+1] = "0123456789abcdef"[t&0xf]; } zOut[n] = 0; } int main(int argc, char *argv[]){ FILE *m,*u,*v; char *z; #if defined(__DMC__) /* e.g. 0x857 */ int i = 0; #endif int j = 0, x = 0, d = 0; size_t n; int vn[3]; char b[1000]; char vx[1000]; if( argc!=4 ){ fprintf(stderr, "Usage: %s manifest.uuid manifest VERSION\n", argv[0]); exit(1); } memset(b,0,sizeof(b)); memset(vx,0,sizeof(vx)); u = open_for_reading(argv[1]); 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); n = strlen(b); if( n + 50 < sizeof(b) ){ #ifdef FOSSIL_BUILD_EPOCH #define str(s) #s snprintf(b+n, sizeof(b)-n, "%d", (int)strtoll(str(FOSSIL_BUILD_EPOCH), 0, 10)); #else const char *zEpoch = getenv("SOURCE_DATE_EPOCH"); if( zEpoch && isdigit(zEpoch[0]) ){ snprintf(b+n, sizeof(b)-n, "%d", (int)strtoll(zEpoch, 0, 10)); }else{ snprintf(b+n, sizeof(b)-n, "%d", (int)time(0)); } #endif hash(b,33,vx); printf("#define FOSSIL_BUILD_HASH \"%s\"\n", vx); } m = open_for_reading(argv[2]); while(b == fgets(b, sizeof(b)-1,m)){ if(0 == strncmp("D ",b,2)){ int k, n; char zDateNum[30]; printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13); printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2); n = 0; for(k=0; k<10; k++){ if( isdigit(b[k+2]) ) zDateNum[n++] = b[k+2]; } zDateNum[n] = 0; printf("#define MANIFEST_NUMERIC_DATE %s\n", zDateNum); n = 0; for(k=0; k<8; k++){ if( isdigit(b[k+13]) ) zDateNum[n++] = b[k+13]; } zDateNum[n] = 0; for(k=0; zDateNum[k]=='0'; k++){} printf("#define MANIFEST_NUMERIC_TIME %s\n", zDateNum+k); } } fclose(m); v = open_for_reading(argv[3]); 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); z=b; vn[0] = vn[1] = vn[2] = 0; while(1){ if( z[0]>='0' && z[0]<='9' ){ x = x*10 + z[0] - '0'; }else{ if( j<3 ) vn[j++] = x; x = 0; if( z[0]==0 ) break; } z++; } for(z=vx; z[0]=='0'; z++){} printf("#define RELEASE_VERSION_NUMBER %d%02d%02d\n", vn[0], vn[1], vn[2]); memset(vx,0,sizeof(vx)); strcpy(vx,b); for(z=vx; z[0]; z++){ if( z[0]=='-' ){ z[0] = 0; break; } if( z[0]!='.' ) continue; if ( d<3 ){ z[0] = ','; d++; }else{ 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 */ /* _MSC_FULL_VER also defined, e.g. 193030709 */ d = (_MSC_VER / 100); /* major */ x = (_MSC_VER % 100); /* minor */ printf("#define COMPILER_VERSION \"%d.%02d.", d, x); printf("%05d\"\n",(int)(_MSC_FULL_VER % 100000)); /* build (patch) */ #endif return 0; } |
Added tools/skintxt2config.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 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* ** Copyright (c) 2021 Stephan Beal (https://wanderinghorse.net/home/stephan/) ** ** 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. ** ******************************************************************************* ** ** This application reads in Fossil SCM skin configuration files and emits ** them in a form suitable for importing directly into a fossil database ** using the (fossil config import) command. ** ** As input it requires one or more skin configuration files (css.txt, ** header.txt, footer.txt, details.txt, js.txt) and all output goes to ** stdout unless redirected using the -o FILENAME flag. ** ** Run it with no arguments or one of (help, --help, -?) for help text. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <time.h> #include <stdarg.h> static struct App_ { const char * argv0; time_t now; FILE * ostr; } App = { 0, 0, 0 }; static void err(const char *zFmt, ...){ va_list vargs; va_start(vargs, zFmt); fputs("ERROR: ",stderr); vfprintf(stderr, zFmt, vargs); fputc('\n', stderr); va_end(vargs); } static void app_usage(int isErr){ FILE * const ios = isErr ? stderr : stdout; fprintf(ios, "Usage: %s ?OPTIONS? input-filename...\n\n", App.argv0); fprintf(ios, "Each filename must be one file which is conventionally " "part of a Fossil SCM skin set:\n" " css.txt, header.txt, footer.txt, details.txt, js.txt\n"); fprintf(ios, "\nOptions:\n"); fprintf(ios, "\n\t-o FILENAME = send output to the given file. " "'-' means stdout (the default).\n"); fputc('\n', ios); } /* ** Reads file zFilename, stores its contents in *zContent, and sets the ** length of its contents to *nContent. ** ** Returns 0 on success. On error, *zContent and *nContent are not ** modified and it may emit a message describing the problem. */ int read_file(char const *zFilename, unsigned char ** zContent, int * nContent){ long fpos; int rc = 0; unsigned char * zMem = 0; FILE * f = fopen(zFilename, "rb"); if(!f){ err("Cannot open file %s. Errno=%d", zFilename, errno); return errno; } fseek(f, 0L, SEEK_END); rc = errno; if(rc){ err("Cannot seek() file %s. Errno=%d", zFilename, rc); goto end; } fpos = ftell(f); fseek(f, 0L, SEEK_SET); zMem = (unsigned char *)malloc((size_t)fpos + 1); if(!zMem){ err("Malloc failed."); rc = ENOMEM; goto end; } zMem[fpos] = 0; if(fpos && (size_t)1 != fread(zMem, (size_t)fpos, 1, f)){ rc = EIO; err("Error #%d reading file %s", rc, zFilename); goto end; } end: fclose(f); if(rc){ free(zMem); }else{ *zContent = zMem; *nContent = fpos; } return rc; } /* ** Expects zFilename to be one of the conventional skin filename ** parts. This routine converts it to config format and emits it to ** App.ostr. */ int dispatch_file(char const *zFilename){ const char * zKey = 0; int nContent = 0, nContent2 = 0, nOut = 0, nTime = 0, rc = 0; time_t theTime = App.now; unsigned char * zContent = 0; unsigned char * z = 0; if(strstr(zFilename, "css.txt")){ zKey = "css"; }else if(strstr(zFilename, "header.txt")){ zKey = "header"; }else if(strstr(zFilename, "footer.txt")){ zKey = "footer"; }else if(strstr(zFilename, "details.txt")){ zKey = "details"; }else if(strstr(zFilename, "js.txt")){ zKey = "js"; }else { err("Cannot determine skin part from filename: %s", zFilename); return 1; } rc = read_file(zFilename, &zContent, &nContent); if(rc) return rc; for( z = zContent; z < zContent + nContent; ++z ){ /* Count file content length with ' characters doubled */ nContent2 += ('\'' == *z) ? 2 : 1; } while(theTime > 0){/* # of digits in time */ ++nTime; theTime /= 10; } fprintf(App.ostr, "config /config %d\n", (int)(nTime + 12/*"value"+spaces+quotes*/ + (int)strlen(zKey) + nContent2)); fprintf(App.ostr, "%d '%s' value '", (int)App.now, zKey); for( z = zContent; z < zContent + nContent; ++z ){ /* Emit file content with ' characters doubled */ if('\'' == (char)*z){ fputc('\'', App.ostr); } fputc((char)*z, App.ostr); } free(zContent); fprintf(App.ostr, "'\n"); return 0; } int main(int argc, char const * const * argv){ int rc = 0, i ; App.argv0 = argv[0]; App.ostr = stdout; if(argc<2){ app_usage(1); rc = 1; goto end; } App.now = time(0); for( i = 1; i < argc; ++i ){ const char * zArg = argv[i]; if(0==strcmp(zArg,"help") || 0==strcmp(zArg,"--help") || 0==strcmp(zArg,"-?")){ app_usage(0); rc = 0; break; }else if(0==strcmp(zArg,"-o")){ /* -o OUTFILE (- == stdout) */ ++i; if(i==argc){ err("Missing filename for -o flag"); rc = 1; break; }else{ const char *zOut = argv[i]; if(App.ostr != stdout){ err("Cannot specify -o more than once."); rc = 1; break; } if(0!=strcmp("-",zOut)){ FILE * o = fopen(zOut, "wb"); if(!o){ err("Could not open file %s for writing. Errno=%d", zOut, errno); rc = errno; break; } App.ostr = o; } } }else if('-' == zArg[0]){ err("Unhandled argument: %s", zArg); rc = 1; break; }else{ rc = dispatch_file(zArg); if(rc) break; } } end: if(App.ostr != stdout){ fclose(App.ostr); } return rc ? EXIT_FAILURE : EXIT_SUCCESS; } |
Added tools/sqlcompattest.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) 2019 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 is NOT part of the Fossil executable. It is called from ** auto.def in the autosetup system. ** ** This file contains a test program used by ../configure with the ** the --disable-internal-sqlite option to determine whether or ** not the system SQLite library is sufficient to support Fossil. ** ** This must be compiled with -D MINIMUM_SQLITE_VERSION set in auto.def. ** ** It is preferred to statically link Fossil with the sqlite3.c source ** file that is part of the source tree and not use any SQLite shared ** library that is included with the system. But some packagers do not ** like to do this. Hence, we provide the option to link Fossil against ** the system SQLite shared library. But Fossil is very particular about ** the version and build options for SQLite. Unless a recent version of ** SQLite is available, and unless that SQLite is built using some ** non-default features, the system library won't meet the needs of ** Fossil. This program attempts to determine if the system library ** SQLite is sufficient for Fossil. ** ** Compile this program, linking it against the system SQLite library, ** and run it. If it returns with a zero exit code, then all is well. ** But if it returns a non-zero exit code, then the system SQLite library ** lacks some capability that Fossil uses. A message on stdout describes ** the missing feature. */ #include "sqlite3.h" #include <stdio.h> #include <string.h> int main(int argc, char **argv){ #if !defined(MINIMUM_SQLITE_VERSION) #error "Must set -DMINIMUM_SQLITE_VERSION=nn.nn.nn in auto.def" #endif #define QUOTE(VAL) #VAL #define STR(MACRO_VAL) QUOTE(MACRO_VAL) char zMinimumVersionNumber[8]="nn.nn.nn"; strncpy((char *)&zMinimumVersionNumber,STR(MINIMUM_SQLITE_VERSION), sizeof(zMinimumVersionNumber)); long major, minor, release, version; sscanf(zMinimumVersionNumber, "%li.%li.%li", &major, &minor, &release); version=(major*1000000)+(minor*1000)+release; int i; static const char *zRequiredOpts[] = { "ENABLE_FTS4", /* Required for repository search */ "ENABLE_DBSTAT_VTAB", /* Required by /repo-tabsize page */ }; /* Check minimum SQLite version number */ if( sqlite3_libversion_number()<version ){ printf("found system SQLite version %s but need %s or later, " "consider removing --disable-internal-sqlite\n", sqlite3_libversion(),STR(MINIMUM_SQLITE_VERSION)); return 1; } for(i=0; i<sizeof(zRequiredOpts)/sizeof(zRequiredOpts[0]); i++){ if( !sqlite3_compileoption_used(zRequiredOpts[i]) ){ printf("system SQLite library omits required build option -DSQLITE_%s\n", zRequiredOpts[i]); return 1; } } /* Success! */ return 0; } |
Added tools/translate.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 | /* ** Copyright (c) 2002 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/ ** ******************************************************************************* ** ** 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" (where "|" is really "/") ** 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. ** ** Enhancement #3: ** ** If a non-enhancement #1 line ends in backslash, the backslash and the ** newline (\n) are not included in the argument to cgi_printf(). This ** is used to split one long output line across multiple source lines. */ #include <stdio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> /* ** Space to hold arguments at the end of the cgi_printf() */ #define MX_ARG_SP 10000 static char zArg[MX_ARG_SP]; static int nArg = 0; /* ** True if we are currently in a cgi_printf() */ static int inPrint = 0; /* ** True if we are currently doing a free string */ static int inStr = 0; /* ** Name of files being processed */ static const char *zInFile = "(stdin)"; /* ** Terminate an active cgi_printf() or free string */ static void end_block(FILE *out){ if( inPrint ){ zArg[nArg] = 0; fprintf(out, "%s);\n", zArg); nArg = 0; inPrint = 0; } } /* ** Translate the input stream into the output stream */ static void trans(FILE *in, FILE *out){ int i, j, k; /* Loop counters */ char c1, c2; /* Characters used to start a comment */ int lastWasEq = 0; /* True if last non-whitespace character was "=" */ int lastWasComma = 0; /* True if last non-whitespace character was "," */ int lineNo = 0; /* Line number */ char zLine[2000]; /* A single line of input */ char zOut[4000]; /* The input line translated into appropriate output */ c1 = c2 = '-'; while( fgets(zLine, sizeof(zLine), in) ){ lineNo++; for(i=0; zLine[i] && isspace(zLine[i]); i++){} if( zLine[i]!='@' ){ if( inPrint || inStr ) end_block(out); fprintf(out,"%s",zLine); /* 0123456789 12345 */ if( strncmp(zLine, "/* @-comment: ", 14)==0 ){ c1 = zLine[14]; c2 = zLine[15]; } i += strlen(&zLine[i]); while( i>0 && isspace(zLine[i-1]) ){ i--; } lastWasEq = i>0 && zLine[i-1]=='='; lastWasComma = i>0 && zLine[i-1]==','; }else if( lastWasEq || lastWasComma){ /* If the last non-whitespace character before the first @ was ** an "="(var init/set) or a ","(const definition in list) then ** generate a string literal. But skip comments ** consisting of all text between c1 and c2 (default "--") ** and end of line. */ int indent, omitline; char *zNewline = "\\n"; 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+1]==0 || zLine[i+1]=='\r' || zLine[i+1]=='\n') ){ zLine[i] = 0; zNewline = ""; /* fprintf(stderr, "%s:%d: omit newline\n", zInFile, lineNo); */ break; } if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; } zOut[j++] = zLine[i]; } if( zNewline[0] ) while( j>0 && isspace(zOut[j-1]) ){ j--; } zOut[j] = 0; if( j<=0 && omitline ){ fprintf(out,"\n"); }else{ fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline); } }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. Each '*' character ** present in C (max two) causes one more "(...)" sequence to be consumed. ** For example, "%*.*d(4)(2)(1)" converts to "%*.*d" with arguments "4", ** "2", and "1", which will be used as the field width, precision, and ** value, respectively, producing a final formatted result of " 01". */ const char *zNewline = "\\n"; int indent; int nC; int nParam; char c; i++; if( isspace(zLine[i]) ){ i++; } indent = i; for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r' || zLine[i+1]=='\n') ){ zNewline = ""; break; } if( zLine[i]=='"' || zLine[i]=='\\' ){ zOut[j++] = '\\'; } zOut[j++] = zLine[i]; if( zLine[i]!='%' || zLine[i+1]=='%' || zLine[i+1]==0 ) continue; nParam=1; for(nC=1; zLine[i+nC] && zLine[i+nC]!='('; nC++){ if( zLine[i+nC]=='*' && nParam < 3 ) nParam++; } if( zLine[i+nC]!='(' || !isalpha(zLine[i+nC-1]) ) continue; while( --nC ) zOut[j++] = zLine[++i]; do{ zArg[nArg++] = ','; k = 0; i++; if( zLine[i]!='(' ) break; while( (c = zLine[i])!=0 ){ zArg[nArg++] = c; if( c==')' ){ k--; if( k==0 ) break; }else if( c=='(' ){ k++; } i++; } }while( --nParam ); } zOut[j] = 0; if( !inPrint ){ fprintf(out,"%*scgi_printf(\"%s%s\"",indent-2,"", zOut, zNewline); inPrint = 1; }else{ fprintf(out,"\n%*s\"%s%s\"",indent+5, "", zOut, zNewline); } } } } static void print_source_ref(const char *zSrcFile, FILE *out){ /* Set source line reference to the original source file. * This makes compiler show the original file name in the compile error * messages, instead of referring to the translated file. * NOTE: This somewhat complicates stepping in debugger, as the resuling * code would not match the referenced sources. */ #ifndef FOSSIL_DEBUG const char *arg; if( !*zSrcFile ){ return; } fprintf(out,"#line 1 \""); for(arg=zSrcFile; *arg; arg++){ if( *arg!='\\' ){ fprintf(out,"%c", *arg); }else{ fprintf(out,"\\\\"); } } fprintf(out,"\"\n"); #endif } int main(int argc, char **argv){ if( argc==2 ){ FILE *in = fopen(argv[1], "r"); if( in==0 ){ fprintf(stderr,"can not open %s\n", argv[1]); exit(1); } zInFile = argv[1]; print_source_ref(zInFile, stdout); trans(in, stdout); fclose(in); }else{ trans(stdin, stdout); } return 0; } |
Added win/Makefile.dmc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "tools/makemake.tcl") ############################################################################## # # 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 SRCDIR_extsrc = $B\extsrc SRCDIR_tools = $B\tools OBJDIR = . O = .obj E = .exe # Maybe DMDIR, SSL or INCL needs adjustment DMDIR = c:\DM INCL = -I. -I$(SRCDIR) -I$(SRCDIR_extsrc) -I$B\win\include -I$(DMDIR)\extra\include #SSL = -DFOSSIL_ENABLE_SSL=1 SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.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 patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$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)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$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)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$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)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$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)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$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)\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 ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki 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 patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ translate$E: $(SRCDIR_tools)\translate.c $(BCC) -o$@ $** makeheaders$E: $(SRCDIR_tools)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR_tools)\mkindex.c $(BCC) -o$@ $** mkbuiltin$E: $(SRCDIR_tools)\mkbuiltin.c $(BCC) -o$@ $** mkversion$E: $(SRCDIR_tools)\mkversion.c $(BCC) -o$@ $** codecheck1$E: $(SRCDIR_tools)\codecheck1.c $(BCC) -o$@ $** $(OBJDIR)\shell$O : $(SRCDIR_extsrc)\shell.c $(TCC) -o$@ -c $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $** $(OBJDIR)\sqlite3$O : $(SRCDIR_extsrc)\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_extsrc)\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 $(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 $(OBJDIR)\add$O : add_.c add.h $(TCC) -o$@ -c add_.c add_.c : $(SRCDIR)\add.c +translate$E $** > $@ $(OBJDIR)\ajax$O : ajax_.c ajax.h $(TCC) -o$@ -c ajax_.c ajax_.c : $(SRCDIR)\ajax.c +translate$E $** > $@ $(OBJDIR)\alerts$O : alerts_.c alerts.h $(TCC) -o$@ -c alerts_.c alerts_.c : $(SRCDIR)\alerts.c +translate$E $** > $@ $(OBJDIR)\allrepo$O : allrepo_.c allrepo.h $(TCC) -o$@ -c allrepo_.c allrepo_.c : $(SRCDIR)\allrepo.c +translate$E $** > $@ $(OBJDIR)\attach$O : attach_.c attach.h $(TCC) -o$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c +translate$E $** > $@ $(OBJDIR)\backlink$O : backlink_.c backlink.h $(TCC) -o$@ -c backlink_.c backlink_.c : $(SRCDIR)\backlink.c +translate$E $** > $@ $(OBJDIR)\backoffice$O : backoffice_.c backoffice.h $(TCC) -o$@ -c backoffice_.c backoffice_.c : $(SRCDIR)\backoffice.c +translate$E $** > $@ $(OBJDIR)\bag$O : bag_.c bag.h $(TCC) -o$@ -c bag_.c bag_.c : $(SRCDIR)\bag.c +translate$E $** > $@ $(OBJDIR)\bisect$O : bisect_.c bisect.h $(TCC) -o$@ -c bisect_.c bisect_.c : $(SRCDIR)\bisect.c +translate$E $** > $@ $(OBJDIR)\blob$O : blob_.c blob.h $(TCC) -o$@ -c blob_.c blob_.c : $(SRCDIR)\blob.c +translate$E $** > $@ $(OBJDIR)\branch$O : branch_.c branch.h $(TCC) -o$@ -c branch_.c branch_.c : $(SRCDIR)\branch.c +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)\capabilities$O : capabilities_.c capabilities.h $(TCC) -o$@ -c capabilities_.c capabilities_.c : $(SRCDIR)\capabilities.c +translate$E $** > $@ $(OBJDIR)\captcha$O : captcha_.c captcha.h $(TCC) -o$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c +translate$E $** > $@ $(OBJDIR)\cgi$O : cgi_.c cgi.h $(TCC) -o$@ -c cgi_.c cgi_.c : $(SRCDIR)\cgi.c +translate$E $** > $@ $(OBJDIR)\chat$O : chat_.c chat.h $(TCC) -o$@ -c chat_.c chat_.c : $(SRCDIR)\chat.c +translate$E $** > $@ $(OBJDIR)\checkin$O : checkin_.c checkin.h $(TCC) -o$@ -c checkin_.c checkin_.c : $(SRCDIR)\checkin.c +translate$E $** > $@ $(OBJDIR)\checkout$O : checkout_.c checkout.h $(TCC) -o$@ -c checkout_.c checkout_.c : $(SRCDIR)\checkout.c +translate$E $** > $@ $(OBJDIR)\clearsign$O : clearsign_.c clearsign.h $(TCC) -o$@ -c clearsign_.c clearsign_.c : $(SRCDIR)\clearsign.c +translate$E $** > $@ $(OBJDIR)\clone$O : clone_.c clone.h $(TCC) -o$@ -c clone_.c clone_.c : $(SRCDIR)\clone.c +translate$E $** > $@ $(OBJDIR)\color$O : color_.c color.h $(TCC) -o$@ -c color_.c color_.c : $(SRCDIR)\color.c +translate$E $** > $@ $(OBJDIR)\comformat$O : comformat_.c comformat.h $(TCC) -o$@ -c comformat_.c comformat_.c : $(SRCDIR)\comformat.c +translate$E $** > $@ $(OBJDIR)\configure$O : configure_.c configure.h $(TCC) -o$@ -c configure_.c configure_.c : $(SRCDIR)\configure.c +translate$E $** > $@ $(OBJDIR)\content$O : content_.c content.h $(TCC) -o$@ -c content_.c content_.c : $(SRCDIR)\content.c +translate$E $** > $@ $(OBJDIR)\cookies$O : cookies_.c cookies.h $(TCC) -o$@ -c cookies_.c cookies_.c : $(SRCDIR)\cookies.c +translate$E $** > $@ $(OBJDIR)\db$O : db_.c db.h $(TCC) -o$@ -c db_.c db_.c : $(SRCDIR)\db.c +translate$E $** > $@ $(OBJDIR)\delta$O : delta_.c delta.h $(TCC) -o$@ -c delta_.c delta_.c : $(SRCDIR)\delta.c +translate$E $** > $@ $(OBJDIR)\deltacmd$O : deltacmd_.c deltacmd.h $(TCC) -o$@ -c deltacmd_.c deltacmd_.c : $(SRCDIR)\deltacmd.c +translate$E $** > $@ $(OBJDIR)\deltafunc$O : deltafunc_.c deltafunc.h $(TCC) -o$@ -c deltafunc_.c deltafunc_.c : $(SRCDIR)\deltafunc.c +translate$E $** > $@ $(OBJDIR)\descendants$O : descendants_.c descendants.h $(TCC) -o$@ -c descendants_.c descendants_.c : $(SRCDIR)\descendants.c +translate$E $** > $@ $(OBJDIR)\diff$O : diff_.c diff.h $(TCC) -o$@ -c diff_.c diff_.c : $(SRCDIR)\diff.c +translate$E $** > $@ $(OBJDIR)\diffcmd$O : diffcmd_.c diffcmd.h $(TCC) -o$@ -c diffcmd_.c diffcmd_.c : $(SRCDIR)\diffcmd.c +translate$E $** > $@ $(OBJDIR)\dispatch$O : dispatch_.c dispatch.h $(TCC) -o$@ -c dispatch_.c dispatch_.c : $(SRCDIR)\dispatch.c +translate$E $** > $@ $(OBJDIR)\doc$O : doc_.c doc.h $(TCC) -o$@ -c doc_.c doc_.c : $(SRCDIR)\doc.c +translate$E $** > $@ $(OBJDIR)\encode$O : encode_.c encode.h $(TCC) -o$@ -c encode_.c encode_.c : $(SRCDIR)\encode.c +translate$E $** > $@ $(OBJDIR)\etag$O : etag_.c etag.h $(TCC) -o$@ -c etag_.c etag_.c : $(SRCDIR)\etag.c +translate$E $** > $@ $(OBJDIR)\event$O : event_.c event.h $(TCC) -o$@ -c event_.c event_.c : $(SRCDIR)\event.c +translate$E $** > $@ $(OBJDIR)\export$O : export_.c export.h $(TCC) -o$@ -c export_.c export_.c : $(SRCDIR)\export.c +translate$E $** > $@ $(OBJDIR)\extcgi$O : extcgi_.c extcgi.h $(TCC) -o$@ -c extcgi_.c extcgi_.c : $(SRCDIR)\extcgi.c +translate$E $** > $@ $(OBJDIR)\file$O : file_.c file.h $(TCC) -o$@ -c file_.c file_.c : $(SRCDIR)\file.c +translate$E $** > $@ $(OBJDIR)\fileedit$O : fileedit_.c fileedit.h $(TCC) -o$@ -c fileedit_.c fileedit_.c : $(SRCDIR)\fileedit.c +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)\forum$O : forum_.c forum.h $(TCC) -o$@ -c forum_.c forum_.c : $(SRCDIR)\forum.c +translate$E $** > $@ $(OBJDIR)\fshell$O : fshell_.c fshell.h $(TCC) -o$@ -c fshell_.c fshell_.c : $(SRCDIR)\fshell.c +translate$E $** > $@ $(OBJDIR)\fusefs$O : fusefs_.c fusefs.h $(TCC) -o$@ -c fusefs_.c fusefs_.c : $(SRCDIR)\fusefs.c +translate$E $** > $@ $(OBJDIR)\fuzz$O : fuzz_.c fuzz.h $(TCC) -o$@ -c fuzz_.c fuzz_.c : $(SRCDIR)\fuzz.c +translate$E $** > $@ $(OBJDIR)\glob$O : glob_.c glob.h $(TCC) -o$@ -c glob_.c glob_.c : $(SRCDIR)\glob.c +translate$E $** > $@ $(OBJDIR)\graph$O : graph_.c graph.h $(TCC) -o$@ -c graph_.c graph_.c : $(SRCDIR)\graph.c +translate$E $** > $@ $(OBJDIR)\gzip$O : gzip_.c gzip.h $(TCC) -o$@ -c gzip_.c gzip_.c : $(SRCDIR)\gzip.c +translate$E $** > $@ $(OBJDIR)\hname$O : hname_.c hname.h $(TCC) -o$@ -c hname_.c hname_.c : $(SRCDIR)\hname.c +translate$E $** > $@ $(OBJDIR)\hook$O : hook_.c hook.h $(TCC) -o$@ -c hook_.c hook_.c : $(SRCDIR)\hook.c +translate$E $** > $@ $(OBJDIR)\http$O : http_.c http.h $(TCC) -o$@ -c http_.c http_.c : $(SRCDIR)\http.c +translate$E $** > $@ $(OBJDIR)\http_socket$O : http_socket_.c http_socket.h $(TCC) -o$@ -c http_socket_.c http_socket_.c : $(SRCDIR)\http_socket.c +translate$E $** > $@ $(OBJDIR)\http_ssl$O : http_ssl_.c http_ssl.h $(TCC) -o$@ -c http_ssl_.c http_ssl_.c : $(SRCDIR)\http_ssl.c +translate$E $** > $@ $(OBJDIR)\http_transport$O : http_transport_.c http_transport.h $(TCC) -o$@ -c http_transport_.c http_transport_.c : $(SRCDIR)\http_transport.c +translate$E $** > $@ $(OBJDIR)\import$O : import_.c import.h $(TCC) -o$@ -c import_.c import_.c : $(SRCDIR)\import.c +translate$E $** > $@ $(OBJDIR)\info$O : info_.c info.h $(TCC) -o$@ -c info_.c info_.c : $(SRCDIR)\info.c +translate$E $** > $@ $(OBJDIR)\interwiki$O : interwiki_.c interwiki.h $(TCC) -o$@ -c interwiki_.c interwiki_.c : $(SRCDIR)\interwiki.c +translate$E $** > $@ $(OBJDIR)\json$O : json_.c json.h $(TCC) -o$@ -c json_.c json_.c : $(SRCDIR)\json.c +translate$E $** > $@ $(OBJDIR)\json_artifact$O : json_artifact_.c json_artifact.h $(TCC) -o$@ -c json_artifact_.c json_artifact_.c : $(SRCDIR)\json_artifact.c +translate$E $** > $@ $(OBJDIR)\json_branch$O : json_branch_.c json_branch.h $(TCC) -o$@ -c json_branch_.c json_branch_.c : $(SRCDIR)\json_branch.c +translate$E $** > $@ $(OBJDIR)\json_config$O : json_config_.c json_config.h $(TCC) -o$@ -c json_config_.c json_config_.c : $(SRCDIR)\json_config.c +translate$E $** > $@ $(OBJDIR)\json_diff$O : json_diff_.c json_diff.h $(TCC) -o$@ -c json_diff_.c json_diff_.c : $(SRCDIR)\json_diff.c +translate$E $** > $@ $(OBJDIR)\json_dir$O : json_dir_.c json_dir.h $(TCC) -o$@ -c json_dir_.c json_dir_.c : $(SRCDIR)\json_dir.c +translate$E $** > $@ $(OBJDIR)\json_finfo$O : json_finfo_.c json_finfo.h $(TCC) -o$@ -c json_finfo_.c json_finfo_.c : $(SRCDIR)\json_finfo.c +translate$E $** > $@ $(OBJDIR)\json_login$O : json_login_.c json_login.h $(TCC) -o$@ -c json_login_.c json_login_.c : $(SRCDIR)\json_login.c +translate$E $** > $@ $(OBJDIR)\json_query$O : json_query_.c json_query.h $(TCC) -o$@ -c json_query_.c json_query_.c : $(SRCDIR)\json_query.c +translate$E $** > $@ $(OBJDIR)\json_report$O : json_report_.c json_report.h $(TCC) -o$@ -c json_report_.c json_report_.c : $(SRCDIR)\json_report.c +translate$E $** > $@ $(OBJDIR)\json_status$O : json_status_.c json_status.h $(TCC) -o$@ -c json_status_.c json_status_.c : $(SRCDIR)\json_status.c +translate$E $** > $@ $(OBJDIR)\json_tag$O : json_tag_.c json_tag.h $(TCC) -o$@ -c json_tag_.c json_tag_.c : $(SRCDIR)\json_tag.c +translate$E $** > $@ $(OBJDIR)\json_timeline$O : json_timeline_.c json_timeline.h $(TCC) -o$@ -c json_timeline_.c json_timeline_.c : $(SRCDIR)\json_timeline.c +translate$E $** > $@ $(OBJDIR)\json_user$O : json_user_.c json_user.h $(TCC) -o$@ -c json_user_.c json_user_.c : $(SRCDIR)\json_user.c +translate$E $** > $@ $(OBJDIR)\json_wiki$O : json_wiki_.c json_wiki.h $(TCC) -o$@ -c json_wiki_.c json_wiki_.c : $(SRCDIR)\json_wiki.c +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 $** > $@ $(OBJDIR)\lookslike$O : lookslike_.c lookslike.h $(TCC) -o$@ -c lookslike_.c lookslike_.c : $(SRCDIR)\lookslike.c +translate$E $** > $@ $(OBJDIR)\main$O : main_.c main.h $(TCC) -o$@ -c main_.c main_.c : $(SRCDIR)\main.c +translate$E $** > $@ $(OBJDIR)\manifest$O : manifest_.c manifest.h $(TCC) -o$@ -c manifest_.c manifest_.c : $(SRCDIR)\manifest.c +translate$E $** > $@ $(OBJDIR)\markdown$O : markdown_.c markdown.h $(TCC) -o$@ -c markdown_.c markdown_.c : $(SRCDIR)\markdown.c +translate$E $** > $@ $(OBJDIR)\markdown_html$O : markdown_html_.c markdown_html.h $(TCC) -o$@ -c markdown_html_.c markdown_html_.c : $(SRCDIR)\markdown_html.c +translate$E $** > $@ $(OBJDIR)\md5$O : md5_.c md5.h $(TCC) -o$@ -c md5_.c md5_.c : $(SRCDIR)\md5.c +translate$E $** > $@ $(OBJDIR)\merge$O : merge_.c merge.h $(TCC) -o$@ -c merge_.c merge_.c : $(SRCDIR)\merge.c +translate$E $** > $@ $(OBJDIR)\merge3$O : merge3_.c merge3.h $(TCC) -o$@ -c merge3_.c merge3_.c : $(SRCDIR)\merge3.c +translate$E $** > $@ $(OBJDIR)\moderate$O : moderate_.c moderate.h $(TCC) -o$@ -c moderate_.c moderate_.c : $(SRCDIR)\moderate.c +translate$E $** > $@ $(OBJDIR)\name$O : name_.c name.h $(TCC) -o$@ -c name_.c name_.c : $(SRCDIR)\name.c +translate$E $** > $@ $(OBJDIR)\patch$O : patch_.c patch.h $(TCC) -o$@ -c patch_.c patch_.c : $(SRCDIR)\patch.c +translate$E $** > $@ $(OBJDIR)\path$O : path_.c path.h $(TCC) -o$@ -c path_.c path_.c : $(SRCDIR)\path.c +translate$E $** > $@ $(OBJDIR)\piechart$O : piechart_.c piechart.h $(TCC) -o$@ -c piechart_.c piechart_.c : $(SRCDIR)\piechart.c +translate$E $** > $@ $(OBJDIR)\pikchrshow$O : pikchrshow_.c pikchrshow.h $(TCC) -o$@ -c pikchrshow_.c pikchrshow_.c : $(SRCDIR)\pikchrshow.c +translate$E $** > $@ $(OBJDIR)\pivot$O : pivot_.c pivot.h $(TCC) -o$@ -c pivot_.c pivot_.c : $(SRCDIR)\pivot.c +translate$E $** > $@ $(OBJDIR)\popen$O : popen_.c popen.h $(TCC) -o$@ -c popen_.c popen_.c : $(SRCDIR)\popen.c +translate$E $** > $@ $(OBJDIR)\pqueue$O : pqueue_.c pqueue.h $(TCC) -o$@ -c pqueue_.c pqueue_.c : $(SRCDIR)\pqueue.c +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 $** > $@ $(OBJDIR)\regexp$O : regexp_.c regexp.h $(TCC) -o$@ -c regexp_.c regexp_.c : $(SRCDIR)\regexp.c +translate$E $** > $@ $(OBJDIR)\repolist$O : repolist_.c repolist.h $(TCC) -o$@ -c repolist_.c repolist_.c : $(SRCDIR)\repolist.c +translate$E $** > $@ $(OBJDIR)\report$O : report_.c report.h $(TCC) -o$@ -c report_.c report_.c : $(SRCDIR)\report.c +translate$E $** > $@ $(OBJDIR)\rss$O : rss_.c rss.h $(TCC) -o$@ -c rss_.c rss_.c : $(SRCDIR)\rss.c +translate$E $** > $@ $(OBJDIR)\schema$O : schema_.c schema.h $(TCC) -o$@ -c schema_.c schema_.c : $(SRCDIR)\schema.c +translate$E $** > $@ $(OBJDIR)\search$O : search_.c search.h $(TCC) -o$@ -c search_.c search_.c : $(SRCDIR)\search.c +translate$E $** > $@ $(OBJDIR)\security_audit$O : security_audit_.c security_audit.h $(TCC) -o$@ -c security_audit_.c security_audit_.c : $(SRCDIR)\security_audit.c +translate$E $** > $@ $(OBJDIR)\setup$O : setup_.c setup.h $(TCC) -o$@ -c setup_.c setup_.c : $(SRCDIR)\setup.c +translate$E $** > $@ $(OBJDIR)\setupuser$O : setupuser_.c setupuser.h $(TCC) -o$@ -c setupuser_.c setupuser_.c : $(SRCDIR)\setupuser.c +translate$E $** > $@ $(OBJDIR)\sha1$O : sha1_.c sha1.h $(TCC) -o$@ -c sha1_.c sha1_.c : $(SRCDIR)\sha1.c +translate$E $** > $@ $(OBJDIR)\sha1hard$O : sha1hard_.c sha1hard.h $(TCC) -o$@ -c sha1hard_.c sha1hard_.c : $(SRCDIR)\sha1hard.c +translate$E $** > $@ $(OBJDIR)\sha3$O : sha3_.c sha3.h $(TCC) -o$@ -c sha3_.c sha3_.c : $(SRCDIR)\sha3.c +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 $** > $@ $(OBJDIR)\smtp$O : smtp_.c smtp.h $(TCC) -o$@ -c smtp_.c smtp_.c : $(SRCDIR)\smtp.c +translate$E $** > $@ $(OBJDIR)\sqlcmd$O : sqlcmd_.c sqlcmd.h $(TCC) -o$@ -c sqlcmd_.c sqlcmd_.c : $(SRCDIR)\sqlcmd.c +translate$E $** > $@ $(OBJDIR)\stash$O : stash_.c stash.h $(TCC) -o$@ -c stash_.c stash_.c : $(SRCDIR)\stash.c +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 $** > $@ $(OBJDIR)\sync$O : sync_.c sync.h $(TCC) -o$@ -c sync_.c sync_.c : $(SRCDIR)\sync.c +translate$E $** > $@ $(OBJDIR)\tag$O : tag_.c tag.h $(TCC) -o$@ -c tag_.c tag_.c : $(SRCDIR)\tag.c +translate$E $** > $@ $(OBJDIR)\tar$O : tar_.c tar.h $(TCC) -o$@ -c tar_.c tar_.c : $(SRCDIR)\tar.c +translate$E $** > $@ $(OBJDIR)\terminal$O : terminal_.c terminal.h $(TCC) -o$@ -c terminal_.c terminal_.c : $(SRCDIR)\terminal.c +translate$E $** > $@ $(OBJDIR)\th_main$O : th_main_.c th_main.h $(TCC) -o$@ -c th_main_.c th_main_.c : $(SRCDIR)\th_main.c +translate$E $** > $@ $(OBJDIR)\timeline$O : timeline_.c timeline.h $(TCC) -o$@ -c timeline_.c timeline_.c : $(SRCDIR)\timeline.c +translate$E $** > $@ $(OBJDIR)\tkt$O : tkt_.c tkt.h $(TCC) -o$@ -c tkt_.c tkt_.c : $(SRCDIR)\tkt.c +translate$E $** > $@ $(OBJDIR)\tktsetup$O : tktsetup_.c tktsetup.h $(TCC) -o$@ -c tktsetup_.c tktsetup_.c : $(SRCDIR)\tktsetup.c +translate$E $** > $@ $(OBJDIR)\undo$O : undo_.c undo.h $(TCC) -o$@ -c undo_.c undo_.c : $(SRCDIR)\undo.c +translate$E $** > $@ $(OBJDIR)\unicode$O : unicode_.c unicode.h $(TCC) -o$@ -c unicode_.c unicode_.c : $(SRCDIR)\unicode.c +translate$E $** > $@ $(OBJDIR)\unversioned$O : unversioned_.c unversioned.h $(TCC) -o$@ -c unversioned_.c unversioned_.c : $(SRCDIR)\unversioned.c +translate$E $** > $@ $(OBJDIR)\update$O : update_.c update.h $(TCC) -o$@ -c update_.c update_.c : $(SRCDIR)\update.c +translate$E $** > $@ $(OBJDIR)\url$O : url_.c url.h $(TCC) -o$@ -c url_.c url_.c : $(SRCDIR)\url.c +translate$E $** > $@ $(OBJDIR)\user$O : user_.c user.h $(TCC) -o$@ -c user_.c user_.c : $(SRCDIR)\user.c +translate$E $** > $@ $(OBJDIR)\utf8$O : utf8_.c utf8.h $(TCC) -o$@ -c utf8_.c utf8_.c : $(SRCDIR)\utf8.c +translate$E $** > $@ $(OBJDIR)\util$O : util_.c util.h $(TCC) -o$@ -c util_.c util_.c : $(SRCDIR)\util.c +translate$E $** > $@ $(OBJDIR)\verify$O : verify_.c verify.h $(TCC) -o$@ -c verify_.c verify_.c : $(SRCDIR)\verify.c +translate$E $** > $@ $(OBJDIR)\vfile$O : vfile_.c vfile.h $(TCC) -o$@ -c vfile_.c vfile_.c : $(SRCDIR)\vfile.c +translate$E $** > $@ $(OBJDIR)\wiki$O : wiki_.c wiki.h $(TCC) -o$@ -c wiki_.c wiki_.c : $(SRCDIR)\wiki.c +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 $** > $@ $(OBJDIR)\xfer$O : xfer_.c xfer.h $(TCC) -o$@ -c xfer_.c xfer_.c : $(SRCDIR)\xfer.c +translate$E $** > $@ $(OBJDIR)\xfersetup$O : xfersetup_.c xfersetup.h $(TCC) -o$@ -c xfersetup_.c xfersetup_.c : $(SRCDIR)\xfersetup.c +translate$E $** > $@ $(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 ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.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 capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.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 interwiki_.c:interwiki.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 patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.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 repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.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 terminal_.c:terminal.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 unversioned_.c:unversioned.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 xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h @copy /Y nul: headers |
Added win/Makefile.mingw.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/make # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "tools/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 is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using # MinGW or MinGW-w64. # # Some of the special options which can be passed to make # USE_WINDOWS=1 if building under a windows command prompt # X64=1 if using an unprefixed 64-bit mingw compiler # #### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. # By default, this is an empty string (i.e. use the native compiler). # PREFIX = # PREFIX = mingw32- # PREFIX = i686-pc-mingw32- # PREFIX = i686-w64-mingw32- # PREFIX = x86_64-w64-mingw32- #### The toplevel directory of the source tree. Fossil can be built # in a directory that is separate from the source tree. Just change # the following to point from the build directory to the src/ folder. # SRCDIR = src SRCDIR_extsrc = extsrc SRCDIR_tools = tools #### The directory into which object code files should be written. # OBJDIR = wbld #### C compiler for use in building executables that will run on # the platform that is doing the build. This is used to compile # code-generator programs as part of the build process. See TCC # and TCCEXE below for the C compiler for building the finished # binary. # BCCEXE = gcc #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. This is used # to compile code-generator programs as part of the build process. # See TCC below for the C compiler for building the finished binary. # BCC = $(BCCEXE) #### Enable compiling with debug symbols (much larger binary) # # FOSSIL_ENABLE_SYMBOLS = 1 #### Enable JSON (https://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 relative paths in external diff/gdiff # # FOSSIL_ENABLE_EXEC_REL_PATHS = 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 POSIX memory APIs from "sys/mman.h" # # USE_MMAN_H = 1 #### Use the SQLite Encryption Extension # # USE_SEE = 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 ZLIBCONFIG = ZLIBTARGETS = else SSLCONFIG = mingw64 ZLIBCONFIG = ZLIBTARGETS = endif #### Disable creation of the OpenSSL shared libraries. Also, disable support # for SSLv3 (i.e. thereby forcing the use of TLS). # SSLCONFIG += no-ssl3 no-weak-ssl-ciphers 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). # SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib #### The directories where the OpenSSL include and library files are located. # OPENSSLDIR = $(SRCDIR)/../compat/openssl 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 # here is to use the Sysinternals junction tool to create a hard # link between a "tcl-8.x" sub-directory of the Fossil source code # directory and the target Tcl directory. This removes the need to # hard-code the necessary paths in this Makefile. # TCLDIR = $(SRCDIR)/../compat/tcl-8.6 #### The Tcl source code directory. This defaults to the same value as # TCLDIR macro (above), which may not be correct. This value will # only be used if the FOSSIL_TCL_SOURCE macro is defined. # TCLSRCDIR = $(TCLDIR) #### The Tcl include and library directories. These values will only be # used if the FOSSIL_TCL_SOURCE macro is not defined. # TCLINCDIR = $(TCLDIR)/include TCLLIBDIR = $(TCLDIR)/lib #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)? # ifdef FOSSIL_ENABLE_TCL_STUBS ifndef FOSSIL_ENABLE_TCL_PRIVATE_STUBS LIBTCL = -ltclstub86 endif TCLTARGET = libtclstub86.a else LIBTCL = -ltcl86 TCLTARGET = binaries endif #### C compiler for use in building executables that will run on the # target platform. This is usually the same as BCCEXE, unless you # are cross-compiling. This C compiler builds the finished binary # for fossil. See BCC and BCCEXE above for the C compiler for # building intermediate code-generator tools. # TCCEXE = gcc #### C compiler and options for use in building executables that will # run on the target platform. This is usually the almost 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)$(TCCEXE) -Wall -Wdeclaration-after-statement TCC += -I$(SRCDIR_extsrc) #### Add the necessary command line options to build with debugging # symbols, if enabled. # ifdef FOSSIL_ENABLE_SYMBOLS TCC += -g else TCC += -Os endif TCC += -L$(ZLIBDIR) -I$(ZINCDIR) #### Compile resources for use in building executables that will run # on the target platform. # RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR) RCC += -I$(SRCDIR_extsrc) # 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 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 relative paths in external diff/gdiff ifdef FOSSIL_ENABLE_EXEC_REL_PATHS TCC += -DFOSSIL_ENABLE_EXEC_REL_PATHS=1 RCC += -DFOSSIL_ENABLE_EXEC_REL_PATHS=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 TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS ifdef FOSSIL_ENABLE_TCL_PRIVATE_STUBS TCC += -DFOSSIL_ENABLE_TCL_PRIVATE_STUBS=1 RCC += -DFOSSIL_ENABLE_TCL_PRIVATE_STUBS=1 endif else TCC += -DSTATIC_BUILD RCC += -DSTATIC_BUILD endif endif # With JSON support ifdef FOSSIL_ENABLE_JSON TCC += -DFOSSIL_ENABLE_JSON=1 RCC += -DFOSSIL_ENABLE_JSON=1 endif # With "sys/mman.h" support ifdef USE_MMAN_H TCC += -DUSE_MMAN_H=1 RCC += -DUSE_MMAN_H=1 endif # With SQLite Encryption Extension support ifdef USE_SEE TCC += -DUSE_SEE=1 RCC += -DUSE_SEE=1 endif #### The option -static has no effect on MinGW(-w64), only dynamic # executables can be built when linking with MSVCRT. OpenSSL # (optional) and zlib (required) however are always linked in # statically. Therefore, the FOSSIL_DYNAMIC_BUILD option does # not really apply to MinGW (i.e. since ALL external libraries # are NOT linked dynamically). # # 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 -lcrypt32 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 #### zlib is required. # LIB += -lz #### 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 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Library required for DNS lookups. # LIB += -ldnsapi #### 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 #-------------------------------------------------------- XBCC = $(BCC) $(CFLAGS) XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/ajax.c \ $(SRCDIR)/alerts.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/backlink.c \ $(SRCDIR)/backoffice.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)/capabilities.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/chat.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/color.c \ $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/deltafunc.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/extcgi.c \ $(SRCDIR)/file.c \ $(SRCDIR)/fileedit.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/forum.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/fuzz.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/hname.c \ $(SRCDIR)/hook.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/import.c \ $(SRCDIR)/info.c \ $(SRCDIR)/interwiki.c \ $(SRCDIR)/json.c \ $(SRCDIR)/json_artifact.c \ $(SRCDIR)/json_branch.c \ $(SRCDIR)/json_config.c \ $(SRCDIR)/json_diff.c \ $(SRCDIR)/json_dir.c \ $(SRCDIR)/json_finfo.c \ $(SRCDIR)/json_login.c \ $(SRCDIR)/json_query.c \ $(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)/patch.c \ $(SRCDIR)/path.c \ $(SRCDIR)/piechart.c \ $(SRCDIR)/pikchrshow.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)/repolist.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/setupuser.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/sha1hard.c \ $(SRCDIR)/sha3.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/smtp.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)/terminal.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ $(SRCDIR)/unicode.c \ $(SRCDIR)/unversioned.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)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c EXTRA_FILES = \ $(SRCDIR)/../extsrc/pikchr-worker.js \ $(SRCDIR)/../extsrc/pikchr.js \ $(SRCDIR)/../extsrc/pikchr.wasm \ $(SRCDIR)/../skins/ardoise/css.txt \ $(SRCDIR)/../skins/ardoise/details.txt \ $(SRCDIR)/../skins/ardoise/footer.txt \ $(SRCDIR)/../skins/ardoise/header.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/darkmode/css.txt \ $(SRCDIR)/../skins/darkmode/details.txt \ $(SRCDIR)/../skins/darkmode/footer.txt \ $(SRCDIR)/../skins/darkmode/header.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/etienne/css.txt \ $(SRCDIR)/../skins/etienne/details.txt \ $(SRCDIR)/../skins/etienne/footer.txt \ $(SRCDIR)/../skins/etienne/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/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/accordion.js \ $(SRCDIR)/alerts/bflat2.wav \ $(SRCDIR)/alerts/bflat3.wav \ $(SRCDIR)/alerts/bloop.wav \ $(SRCDIR)/alerts/plunk.wav \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/copybtn.js \ $(SRCDIR)/default.css \ $(SRCDIR)/diff.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/fossil.bootstrap.js \ $(SRCDIR)/fossil.confirmer.js \ $(SRCDIR)/fossil.copybutton.js \ $(SRCDIR)/fossil.diff.js \ $(SRCDIR)/fossil.dom.js \ $(SRCDIR)/fossil.fetch.js \ $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.brlist.js \ $(SRCDIR)/fossil.page.chat.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.pikchrshow.js \ $(SRCDIR)/fossil.page.pikchrshowasm.js \ $(SRCDIR)/fossil.page.whistory.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.pikchr.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ $(SRCDIR)/sounds/4.wav \ $(SRCDIR)/sounds/5.wav \ $(SRCDIR)/sounds/6.wav \ $(SRCDIR)/sounds/7.wav \ $(SRCDIR)/sounds/8.wav \ $(SRCDIR)/sounds/9.wav \ $(SRCDIR)/sounds/a.wav \ $(SRCDIR)/sounds/b.wav \ $(SRCDIR)/sounds/c.wav \ $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.chat.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/style.pikchrshow.css \ $(SRCDIR)/style.wikiedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/ajax_.c \ $(OBJDIR)/alerts_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/backlink_.c \ $(OBJDIR)/backoffice_.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)/capabilities_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/chat_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/color_.c \ $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/deltafunc_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/extcgi_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/fileedit_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/forum_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/fuzz_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/hook_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ $(OBJDIR)/import_.c \ $(OBJDIR)/info_.c \ $(OBJDIR)/interwiki_.c \ $(OBJDIR)/json_.c \ $(OBJDIR)/json_artifact_.c \ $(OBJDIR)/json_branch_.c \ $(OBJDIR)/json_config_.c \ $(OBJDIR)/json_diff_.c \ $(OBJDIR)/json_dir_.c \ $(OBJDIR)/json_finfo_.c \ $(OBJDIR)/json_login_.c \ $(OBJDIR)/json_query_.c \ $(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)/patch_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/piechart_.c \ $(OBJDIR)/pikchrshow_.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)/repolist_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/setupuser_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/sha1hard_.c \ $(OBJDIR)/sha3_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/smtp_.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)/terminal_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ $(OBJDIR)/unicode_.c \ $(OBJDIR)/unversioned_.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)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/ajax.o \ $(OBJDIR)/alerts.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/backlink.o \ $(OBJDIR)/backoffice.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)/capabilities.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/chat.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/color.o \ $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/deltafunc.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/extcgi.o \ $(OBJDIR)/file.o \ $(OBJDIR)/fileedit.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/forum.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/fuzz.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/hook.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/import.o \ $(OBJDIR)/info.o \ $(OBJDIR)/interwiki.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)/patch.o \ $(OBJDIR)/path.o \ $(OBJDIR)/piechart.o \ $(OBJDIR)/pikchrshow.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)/repolist.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/setupuser.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/sha1hard.o \ $(OBJDIR)/sha3.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/smtp.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)/terminal.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ $(OBJDIR)/unicode.o \ $(OBJDIR)/unversioned.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)/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 $(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 $(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)) $(CP) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR)) else $(MKDIR) $(INSTALLDIR) $(CP) $(APPNAME) $(INSTALLDIR) endif $(OBJDIR): ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(OBJDIR)) else $(MKDIR) $(OBJDIR) endif $(TRANSLATE): $(SRCDIR_tools)/translate.c $(XBCC) -o $@ $(SRCDIR_tools)/translate.c $(MAKEHEADERS): $(SRCDIR_tools)/makeheaders.c $(XBCC) -o $@ $(SRCDIR_tools)/makeheaders.c $(MKINDEX): $(SRCDIR_tools)/mkindex.c $(XBCC) -o $@ $(SRCDIR_tools)/mkindex.c $(MKBUILTIN): $(SRCDIR_tools)/mkbuiltin.c $(XBCC) -o $@ $(SRCDIR_tools)/mkbuiltin.c $(MKVERSION): $(SRCDIR_tools)/mkversion.c $(XBCC) -o $@ $(SRCDIR_tools)/mkversion.c $(CODECHECK1): $(SRCDIR_tools)/codecheck1.c $(XBCC) -o $@ $(SRCDIR_tools)/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) $(OBJDIR)/phony.h $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ $(OBJDIR)/phony.h: # Force rebuild of VERSION.h every time "make" is run # The USE_SYSTEM_SQLITE variable may be undefined, set to 0 or 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. # # Closely related is SQLITE3_ORIGIN, with the same 0/1 mapping, # plus a value of 2 means that we are building a client-provided # sqlite3.c. SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ.1 = $(OBJDIR)/sqlite3-see.o # SQLITE3_OBJ.2 is set by the configure process SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_SEE variable may be undefined, 0 or 1. If undefined or 0, # in-tree SQLite is used. If 1, then sqlite3-see.c (not part of the # source tree) is used and extra flags are provided to enable the # SQLite Encryption Extension. SQLITE3_SRC.0 = $(SRCDIR_extsrc)/sqlite3.c SQLITE3_SRC.1 = $(SRCDIR_extsrc)/sqlite3-see.c # SQLITE3_SRC.2 is set by top-level configure/makefile process. SQLITE3_SRC. = $(SRCDIR_extsrc)/sqlite3.c SQLITE3_SRC = $(SQLITE3_SRC.$(SQLITE3_ORIGIN)) SQLITE3_SHELL_SRC.0 = $(SRCDIR_extsrc)/shell.c SQLITE3_SHELL_SRC.1 = $(SRCDIR_extsrc)/shell-see.c # SQLITE3_SHELL_SRC.2 comes from the configure process SQLITE3_SHELL_SRC. = $(SRCDIR_extsrc)/shell.c SQLITE3_SHELL_SRC = $(SQLITE3_SHELL_SRC.$(SQLITE3_ORIGIN)) SEE_FLAGS.0 = SEE_FLAGS.1 = -DSQLITE_HAS_CODEC -DSQLITE_SHELL_DBKEY_PROC=fossil_key SEE_FLAGS. = SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE)) EXTRAOBJ = \ $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) \ $(OBJDIR)/pikchr.o \ $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ $(OBJDIR)/th_lang.o \ $(OBJDIR)/th_tcl.o \ $(OBJDIR)/cson_amalgamation.o zlib: $(ZLIBTARGETS) $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) $(ZLIBCONFIG) -f win32/Makefile.gcc libz.a clean-zlib: $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) -f win32/Makefile.gcc clean BLDTARGETS = zlib openssl: $(BLDTARGETS) cd $(OPENSSLLIBDIR);./Configure --cross-compile-prefix=$(PREFIX) $(SSLCONFIG) sed -i -e 's/^PERL=C:\\.*$$/PERL=perl.exe/i' $(OPENSSLLIBDIR)/Makefile $(MAKE) -C $(OPENSSLLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) build_libs clean-openssl: $(MAKE) -C $(OPENSSLLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) clean tcl: cd $(TCLSRCDIR)/win;./configure $(MAKE) -C $(TCLSRCDIR)/win PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) $(TCLTARGET) clean-tcl: $(MAKE) -C $(TCLSRCDIR)/win PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) distclean APPTARGETS += $(BLDTARGETS) ifdef FOSSIL_BUILD_SSL APPTARGETS += openssl endif $(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(CODECHECK1) $(TRANS_SRC) $(TCC) -o $@ $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(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: 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)/ajax_.c:$(OBJDIR)/ajax.h \ $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.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)/capabilities_.c:$(OBJDIR)/capabilities.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/chat_.c:$(OBJDIR)/chat.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)/color_.c:$(OBJDIR)/color.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/deltafunc_.c:$(OBJDIR)/deltafunc.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/extcgi_.c:$(OBJDIR)/extcgi.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/fileedit_.c:$(OBJDIR)/fileedit.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/fuzz_.c:$(OBJDIR)/fuzz.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/hook_.c:$(OBJDIR)/hook.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)/interwiki_.c:$(OBJDIR)/interwiki.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)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/piechart_.c:$(OBJDIR)/piechart.h \ $(OBJDIR)/pikchrshow_.c:$(OBJDIR)/pikchrshow.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)/repolist_.c:$(OBJDIR)/repolist.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)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/setupuser_.c:$(OBJDIR)/setupuser.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/sha1hard_.c:$(OBJDIR)/sha1hard.h \ $(OBJDIR)/sha3_.c:$(OBJDIR)/sha3.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/smtp_.c:$(OBJDIR)/smtp.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)/terminal_.c:$(OBJDIR)/terminal.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)/unversioned_.c:$(OBJDIR)/unversioned.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)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ $(SRCDIR_extsrc)/pikchr.c:$(OBJDIR)/pikchr.h \ $(SRCDIR_extsrc)/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)/ajax_.c: $(SRCDIR)/ajax.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/ajax.c >$@ $(OBJDIR)/ajax.o: $(OBJDIR)/ajax_.c $(OBJDIR)/ajax.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/ajax.o -c $(OBJDIR)/ajax_.c $(OBJDIR)/ajax.h: $(OBJDIR)/headers $(OBJDIR)/alerts_.c: $(SRCDIR)/alerts.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/alerts.c >$@ $(OBJDIR)/alerts.o: $(OBJDIR)/alerts_.c $(OBJDIR)/alerts.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/alerts.o -c $(OBJDIR)/alerts_.c $(OBJDIR)/alerts.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)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/backlink.c >$@ $(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h: $(OBJDIR)/headers $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/backoffice.o -c $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.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)/capabilities_.c: $(SRCDIR)/capabilities.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/capabilities.c >$@ $(OBJDIR)/capabilities.o: $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/capabilities.o -c $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.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)/chat_.c: $(SRCDIR)/chat.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/chat.c >$@ $(OBJDIR)/chat.o: $(OBJDIR)/chat_.c $(OBJDIR)/chat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/chat.o -c $(OBJDIR)/chat_.c $(OBJDIR)/chat.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)/color_.c: $(SRCDIR)/color.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/color.c >$@ $(OBJDIR)/color.o: $(OBJDIR)/color_.c $(OBJDIR)/color.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/color.o -c $(OBJDIR)/color_.c $(OBJDIR)/color.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)/cookies_.c: $(SRCDIR)/cookies.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/cookies.c >$@ $(OBJDIR)/cookies.o: $(OBJDIR)/cookies_.c $(OBJDIR)/cookies.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cookies.o -c $(OBJDIR)/cookies_.c $(OBJDIR)/cookies.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)/deltafunc_.c: $(SRCDIR)/deltafunc.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/deltafunc.c >$@ $(OBJDIR)/deltafunc.o: $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltafunc.o -c $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.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)/dispatch_.c: $(SRCDIR)/dispatch.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/dispatch.c >$@ $(OBJDIR)/dispatch.o: $(OBJDIR)/dispatch_.c $(OBJDIR)/dispatch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/dispatch.o -c $(OBJDIR)/dispatch_.c $(OBJDIR)/dispatch.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)/etag_.c: $(SRCDIR)/etag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/etag.c >$@ $(OBJDIR)/etag.o: $(OBJDIR)/etag_.c $(OBJDIR)/etag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/etag.o -c $(OBJDIR)/etag_.c $(OBJDIR)/etag.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)/extcgi_.c: $(SRCDIR)/extcgi.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/extcgi.c >$@ $(OBJDIR)/extcgi.o: $(OBJDIR)/extcgi_.c $(OBJDIR)/extcgi.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/extcgi.o -c $(OBJDIR)/extcgi_.c $(OBJDIR)/extcgi.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)/fileedit_.c: $(SRCDIR)/fileedit.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fileedit.c >$@ $(OBJDIR)/fileedit.o: $(OBJDIR)/fileedit_.c $(OBJDIR)/fileedit.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fileedit.o -c $(OBJDIR)/fileedit_.c $(OBJDIR)/fileedit.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)/forum_.c: $(SRCDIR)/forum.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/forum.c >$@ $(OBJDIR)/forum.o: $(OBJDIR)/forum_.c $(OBJDIR)/forum.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/forum.o -c $(OBJDIR)/forum_.c $(OBJDIR)/forum.h: $(OBJDIR)/headers $(OBJDIR)/fshell_.c: $(SRCDIR)/fshell.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fshell.c >$@ $(OBJDIR)/fshell.o: $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fshell.o -c $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.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)/fuzz_.c: $(SRCDIR)/fuzz.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fuzz.c >$@ $(OBJDIR)/fuzz.o: $(OBJDIR)/fuzz_.c $(OBJDIR)/fuzz.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fuzz.o -c $(OBJDIR)/fuzz_.c $(OBJDIR)/fuzz.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)/hname_.c: $(SRCDIR)/hname.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/hname.c >$@ $(OBJDIR)/hname.o: $(OBJDIR)/hname_.c $(OBJDIR)/hname.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/hname.o -c $(OBJDIR)/hname_.c $(OBJDIR)/hname.h: $(OBJDIR)/headers $(OBJDIR)/hook_.c: $(SRCDIR)/hook.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/hook.c >$@ $(OBJDIR)/hook.o: $(OBJDIR)/hook_.c $(OBJDIR)/hook.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/hook.o -c $(OBJDIR)/hook_.c $(OBJDIR)/hook.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)/interwiki_.c: $(SRCDIR)/interwiki.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/interwiki.c >$@ $(OBJDIR)/interwiki.o: $(OBJDIR)/interwiki_.c $(OBJDIR)/interwiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/interwiki.o -c $(OBJDIR)/interwiki_.c $(OBJDIR)/interwiki.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)/patch_.c: $(SRCDIR)/patch.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/patch.c >$@ $(OBJDIR)/patch.o: $(OBJDIR)/patch_.c $(OBJDIR)/patch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/patch.o -c $(OBJDIR)/patch_.c $(OBJDIR)/patch.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)/piechart_.c: $(SRCDIR)/piechart.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/piechart.c >$@ $(OBJDIR)/piechart.o: $(OBJDIR)/piechart_.c $(OBJDIR)/piechart.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/piechart.o -c $(OBJDIR)/piechart_.c $(OBJDIR)/piechart.h: $(OBJDIR)/headers $(OBJDIR)/pikchrshow_.c: $(SRCDIR)/pikchrshow.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/pikchrshow.c >$@ $(OBJDIR)/pikchrshow.o: $(OBJDIR)/pikchrshow_.c $(OBJDIR)/pikchrshow.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pikchrshow.o -c $(OBJDIR)/pikchrshow_.c $(OBJDIR)/pikchrshow.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)/repolist_.c: $(SRCDIR)/repolist.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/repolist.c >$@ $(OBJDIR)/repolist.o: $(OBJDIR)/repolist_.c $(OBJDIR)/repolist.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/repolist.o -c $(OBJDIR)/repolist_.c $(OBJDIR)/repolist.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)/security_audit_.c: $(SRCDIR)/security_audit.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/security_audit.c >$@ $(OBJDIR)/security_audit.o: $(OBJDIR)/security_audit_.c $(OBJDIR)/security_audit.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/security_audit.o -c $(OBJDIR)/security_audit_.c $(OBJDIR)/security_audit.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)/setupuser_.c: $(SRCDIR)/setupuser.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/setupuser.c >$@ $(OBJDIR)/setupuser.o: $(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setupuser.o -c $(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.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)/sha1hard_.c: $(SRCDIR)/sha1hard.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sha1hard.c >$@ $(OBJDIR)/sha1hard.o: $(OBJDIR)/sha1hard_.c $(OBJDIR)/sha1hard.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha1hard.o -c $(OBJDIR)/sha1hard_.c $(OBJDIR)/sha1hard.h: $(OBJDIR)/headers $(OBJDIR)/sha3_.c: $(SRCDIR)/sha3.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sha3.c >$@ $(OBJDIR)/sha3.o: $(OBJDIR)/sha3_.c $(OBJDIR)/sha3.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha3.o -c $(OBJDIR)/sha3_.c $(OBJDIR)/sha3.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)/smtp_.c: $(SRCDIR)/smtp.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/smtp.c >$@ $(OBJDIR)/smtp.o: $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/smtp.o -c $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.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)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/terminal.c >$@ $(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.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)/unversioned_.c: $(SRCDIR)/unversioned.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/unversioned.c >$@ $(OBJDIR)/unversioned.o: $(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/unversioned.o -c $(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.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)/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 MINGW_OPTIONS = -D_HAVE__MINGW_H SQLITE_OPTIONS = -DNDEBUG=1 \ -DSQLITE_DQS=0 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ -DSQLITE_OMIT_DECLTYPE \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_PROGRESS_CALLBACK \ -DSQLITE_OMIT_SHARED_CACHE \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_MAX_EXPR_DEPTH=0 \ -DSQLITE_ENABLE_LOCKING_STYLE=0 \ -DSQLITE_DEFAULT_FILE_FORMAT=4 \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_DBSTAT_VTAB \ -DSQLITE_ENABLE_FTS5 \ -DSQLITE_ENABLE_STMTVTAB \ -DSQLITE_HAVE_ZLIB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_TRUSTED_SCHEMA=0 \ -DHAVE_USLEEP \ -DSQLITE_WIN32_NO_ANSI \ $(MINGW_OPTIONS) \ -DSQLITE_USE_MALLOC_H \ -DSQLITE_USE_MSIZE SHELL_OPTIONS = -DNDEBUG=1 \ -DSQLITE_DQS=0 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ -DSQLITE_OMIT_DECLTYPE \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_PROGRESS_CALLBACK \ -DSQLITE_OMIT_SHARED_CACHE \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_MAX_EXPR_DEPTH=0 \ -DSQLITE_ENABLE_LOCKING_STYLE=0 \ -DSQLITE_DEFAULT_FILE_FORMAT=4 \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_DBSTAT_VTAB \ -DSQLITE_ENABLE_FTS5 \ -DSQLITE_ENABLE_STMTVTAB \ -DSQLITE_HAVE_ZLIB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_TRUSTED_SCHEMA=0 \ -DHAVE_USLEEP \ -Dmain=sqlite3_shell \ -DSQLITE_SHELL_IS_UTF8=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname \ -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc \ -Daccess=file_access \ -Dsystem=fossil_system \ -Dgetenv=fossil_getenv \ -Dfopen=fossil_fopen PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 $(SQLITE3_OBJ): $(SQLITE3_SRC) $(SRCDIR)/../win/Makefile.mingw $(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) \ -c $(SQLITE3_SRC) -o $@ $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(XTCC) -c $(SRCDIR_extsrc)/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: $(SQLITE3_SHELL_SRC) $(SRCDIR_extsrc)/sqlite3.h $(SRCDIR)/../win/Makefile.mingw $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) $(SEE_FLAGS) -c $(SQLITE3_SHELL_SRC) -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)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
Added win/Makefile.mingw.mistachkin.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/make # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "tools/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 is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using # MinGW or MinGW-w64. # # Some of the special options which can be passed to make # USE_WINDOWS=1 if building under a windows command prompt # X64=1 if using an unprefixed 64-bit mingw compiler # #### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. # By default, this is an empty string (i.e. use the native compiler). # PREFIX = # PREFIX = mingw32- # PREFIX = i686-pc-mingw32- # PREFIX = i686-w64-mingw32- # PREFIX = x86_64-w64-mingw32- #### The toplevel directory of the source tree. Fossil can be built # in a directory that is separate from the source tree. Just change # the following to point from the build directory to the src/ folder. # SRCDIR = src SRCDIR_extsrc = extsrc SRCDIR_tools = tools #### The directory into which object code files should be written. # OBJDIR = wbld #### C compiler for use in building executables that will run on # the platform that is doing the build. This is used to compile # code-generator programs as part of the build process. See TCC # and TCCEXE below for the C compiler for building the finished # binary. # BCCEXE = gcc #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. This is used # to compile code-generator programs as part of the build process. # See TCC below for the C compiler for building the finished binary. # BCC = $(BCCEXE) #### Enable compiling with debug symbols (much larger binary) # # FOSSIL_ENABLE_SYMBOLS = 1 #### Enable JSON (https://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 relative paths in external diff/gdiff # # FOSSIL_ENABLE_EXEC_REL_PATHS = 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 POSIX memory APIs from "sys/mman.h" # # USE_MMAN_H = 1 #### Use the SQLite Encryption Extension # # USE_SEE = 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 ZLIBCONFIG = ZLIBTARGETS = else SSLCONFIG = mingw64 ZLIBCONFIG = ZLIBTARGETS = endif #### Disable creation of the OpenSSL shared libraries. Also, disable support # for SSLv3 (i.e. thereby forcing the use of TLS). # SSLCONFIG += no-ssl3 no-weak-ssl-ciphers 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). # SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib #### The directories where the OpenSSL include and library files are located. # OPENSSLDIR = $(SRCDIR)/../compat/openssl 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 # here is to use the Sysinternals junction tool to create a hard # link between a "tcl-8.x" sub-directory of the Fossil source code # directory and the target Tcl directory. This removes the need to # hard-code the necessary paths in this Makefile. # TCLDIR = $(SRCDIR)/../compat/tcl-8.6 #### The Tcl source code directory. This defaults to the same value as # TCLDIR macro (above), which may not be correct. This value will # only be used if the FOSSIL_TCL_SOURCE macro is defined. # TCLSRCDIR = $(TCLDIR) #### The Tcl include and library directories. These values will only be # used if the FOSSIL_TCL_SOURCE macro is not defined. # TCLINCDIR = $(TCLDIR)/include TCLLIBDIR = $(TCLDIR)/lib #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)? # ifdef FOSSIL_ENABLE_TCL_STUBS ifndef FOSSIL_ENABLE_TCL_PRIVATE_STUBS LIBTCL = -ltclstub86 endif TCLTARGET = libtclstub86.a else LIBTCL = -ltcl86 TCLTARGET = binaries endif #### C compiler for use in building executables that will run on the # target platform. This is usually the same as BCCEXE, unless you # are cross-compiling. This C compiler builds the finished binary # for fossil. See BCC and BCCEXE above for the C compiler for # building intermediate code-generator tools. # TCCEXE = gcc #### C compiler and options for use in building executables that will # run on the target platform. This is usually the almost 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)$(TCCEXE) -Wall -Wdeclaration-after-statement TCC += -I$(SRCDIR_extsrc) #### Add the necessary command line options to build with debugging # symbols, if enabled. # ifdef FOSSIL_ENABLE_SYMBOLS TCC += -g else TCC += -Os endif TCC += -L$(ZLIBDIR) -I$(ZINCDIR) #### Compile resources for use in building executables that will run # on the target platform. # RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR) RCC += -I$(SRCDIR_extsrc) # 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 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 relative paths in external diff/gdiff ifdef FOSSIL_ENABLE_EXEC_REL_PATHS TCC += -DFOSSIL_ENABLE_EXEC_REL_PATHS=1 RCC += -DFOSSIL_ENABLE_EXEC_REL_PATHS=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 TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS ifdef FOSSIL_ENABLE_TCL_PRIVATE_STUBS TCC += -DFOSSIL_ENABLE_TCL_PRIVATE_STUBS=1 RCC += -DFOSSIL_ENABLE_TCL_PRIVATE_STUBS=1 endif else TCC += -DSTATIC_BUILD RCC += -DSTATIC_BUILD endif endif # With JSON support ifdef FOSSIL_ENABLE_JSON TCC += -DFOSSIL_ENABLE_JSON=1 RCC += -DFOSSIL_ENABLE_JSON=1 endif # With "sys/mman.h" support ifdef USE_MMAN_H TCC += -DUSE_MMAN_H=1 RCC += -DUSE_MMAN_H=1 endif # With SQLite Encryption Extension support ifdef USE_SEE TCC += -DUSE_SEE=1 RCC += -DUSE_SEE=1 endif #### The option -static has no effect on MinGW(-w64), only dynamic # executables can be built when linking with MSVCRT. OpenSSL # (optional) and zlib (required) however are always linked in # statically. Therefore, the FOSSIL_DYNAMIC_BUILD option does # not really apply to MinGW (i.e. since ALL external libraries # are NOT linked dynamically). # # 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 -lcrypt32 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 #### zlib is required. # LIB += -lz #### 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 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Library required for DNS lookups. # LIB += -ldnsapi #### 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 #-------------------------------------------------------- XBCC = $(BCC) $(CFLAGS) XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/ajax.c \ $(SRCDIR)/alerts.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/backlink.c \ $(SRCDIR)/backoffice.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)/capabilities.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/chat.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/color.c \ $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/deltafunc.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/extcgi.c \ $(SRCDIR)/file.c \ $(SRCDIR)/fileedit.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/forum.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/fuzz.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/hname.c \ $(SRCDIR)/hook.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/import.c \ $(SRCDIR)/info.c \ $(SRCDIR)/interwiki.c \ $(SRCDIR)/json.c \ $(SRCDIR)/json_artifact.c \ $(SRCDIR)/json_branch.c \ $(SRCDIR)/json_config.c \ $(SRCDIR)/json_diff.c \ $(SRCDIR)/json_dir.c \ $(SRCDIR)/json_finfo.c \ $(SRCDIR)/json_login.c \ $(SRCDIR)/json_query.c \ $(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)/patch.c \ $(SRCDIR)/path.c \ $(SRCDIR)/piechart.c \ $(SRCDIR)/pikchrshow.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)/repolist.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/setupuser.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/sha1hard.c \ $(SRCDIR)/sha3.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/smtp.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)/terminal.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ $(SRCDIR)/unicode.c \ $(SRCDIR)/unversioned.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)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c EXTRA_FILES = \ $(SRCDIR)/../extsrc/pikchr-worker.js \ $(SRCDIR)/../extsrc/pikchr.js \ $(SRCDIR)/../extsrc/pikchr.wasm \ $(SRCDIR)/../skins/ardoise/css.txt \ $(SRCDIR)/../skins/ardoise/details.txt \ $(SRCDIR)/../skins/ardoise/footer.txt \ $(SRCDIR)/../skins/ardoise/header.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/darkmode/css.txt \ $(SRCDIR)/../skins/darkmode/details.txt \ $(SRCDIR)/../skins/darkmode/footer.txt \ $(SRCDIR)/../skins/darkmode/header.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/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/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/accordion.js \ $(SRCDIR)/alerts/bflat2.wav \ $(SRCDIR)/alerts/bflat3.wav \ $(SRCDIR)/alerts/bloop.wav \ $(SRCDIR)/alerts/plunk.wav \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/copybtn.js \ $(SRCDIR)/default.css \ $(SRCDIR)/diff.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/fossil.bootstrap.js \ $(SRCDIR)/fossil.confirmer.js \ $(SRCDIR)/fossil.copybutton.js \ $(SRCDIR)/fossil.diff.js \ $(SRCDIR)/fossil.dom.js \ $(SRCDIR)/fossil.fetch.js \ $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.brlist.js \ $(SRCDIR)/fossil.page.chat.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.pikchrshow.js \ $(SRCDIR)/fossil.page.pikchrshowasm.js \ $(SRCDIR)/fossil.page.whistory.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.pikchr.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ $(SRCDIR)/sounds/4.wav \ $(SRCDIR)/sounds/5.wav \ $(SRCDIR)/sounds/6.wav \ $(SRCDIR)/sounds/7.wav \ $(SRCDIR)/sounds/8.wav \ $(SRCDIR)/sounds/9.wav \ $(SRCDIR)/sounds/a.wav \ $(SRCDIR)/sounds/b.wav \ $(SRCDIR)/sounds/c.wav \ $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.chat.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/style.pikchrshow.css \ $(SRCDIR)/style.wikiedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/ajax_.c \ $(OBJDIR)/alerts_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/backlink_.c \ $(OBJDIR)/backoffice_.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)/capabilities_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/chat_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/color_.c \ $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/deltafunc_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/extcgi_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/fileedit_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/forum_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/fuzz_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/hook_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ $(OBJDIR)/import_.c \ $(OBJDIR)/info_.c \ $(OBJDIR)/interwiki_.c \ $(OBJDIR)/json_.c \ $(OBJDIR)/json_artifact_.c \ $(OBJDIR)/json_branch_.c \ $(OBJDIR)/json_config_.c \ $(OBJDIR)/json_diff_.c \ $(OBJDIR)/json_dir_.c \ $(OBJDIR)/json_finfo_.c \ $(OBJDIR)/json_login_.c \ $(OBJDIR)/json_query_.c \ $(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)/patch_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/piechart_.c \ $(OBJDIR)/pikchrshow_.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)/repolist_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/setupuser_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/sha1hard_.c \ $(OBJDIR)/sha3_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/smtp_.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)/terminal_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ $(OBJDIR)/unicode_.c \ $(OBJDIR)/unversioned_.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)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/ajax.o \ $(OBJDIR)/alerts.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/backlink.o \ $(OBJDIR)/backoffice.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)/capabilities.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/chat.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/color.o \ $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/deltafunc.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/extcgi.o \ $(OBJDIR)/file.o \ $(OBJDIR)/fileedit.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/forum.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/fuzz.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/hook.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/import.o \ $(OBJDIR)/info.o \ $(OBJDIR)/interwiki.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)/patch.o \ $(OBJDIR)/path.o \ $(OBJDIR)/piechart.o \ $(OBJDIR)/pikchrshow.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)/repolist.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/setupuser.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/sha1hard.o \ $(OBJDIR)/sha3.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/smtp.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)/terminal.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ $(OBJDIR)/unicode.o \ $(OBJDIR)/unversioned.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)/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 $(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 $(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)) $(CP) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR)) else $(MKDIR) $(INSTALLDIR) $(CP) $(APPNAME) $(INSTALLDIR) endif $(OBJDIR): ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(OBJDIR)) else $(MKDIR) $(OBJDIR) endif $(TRANSLATE): $(SRCDIR_tools)/translate.c $(XBCC) -o $@ $(SRCDIR_tools)/translate.c $(MAKEHEADERS): $(SRCDIR_tools)/makeheaders.c $(XBCC) -o $@ $(SRCDIR_tools)/makeheaders.c $(MKINDEX): $(SRCDIR_tools)/mkindex.c $(XBCC) -o $@ $(SRCDIR_tools)/mkindex.c $(MKBUILTIN): $(SRCDIR_tools)/mkbuiltin.c $(XBCC) -o $@ $(SRCDIR_tools)/mkbuiltin.c $(MKVERSION): $(SRCDIR_tools)/mkversion.c $(XBCC) -o $@ $(SRCDIR_tools)/mkversion.c $(CODECHECK1): $(SRCDIR_tools)/codecheck1.c $(XBCC) -o $@ $(SRCDIR_tools)/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) $(OBJDIR)/phony.h $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ $(OBJDIR)/phony.h: # Force rebuild of VERSION.h every time "make" is run # The USE_SYSTEM_SQLITE variable may be undefined, set to 0 or 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. # # Closely related is SQLITE3_ORIGIN, with the same 0/1 mapping, # plus a value of 2 means that we are building a client-provided # sqlite3.c. SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ.1 = $(OBJDIR)/sqlite3-see.o # SQLITE3_OBJ.2 is set by the configure process SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_SEE variable may be undefined, 0 or 1. If undefined or 0, # in-tree SQLite is used. If 1, then sqlite3-see.c (not part of the # source tree) is used and extra flags are provided to enable the # SQLite Encryption Extension. SQLITE3_SRC.0 = $(SRCDIR_extsrc)/sqlite3.c SQLITE3_SRC.1 = $(SRCDIR_extsrc)/sqlite3-see.c # SQLITE3_SRC.2 is set by top-level configure/makefile process. SQLITE3_SRC. = $(SRCDIR_extsrc)/sqlite3.c SQLITE3_SRC = $(SQLITE3_SRC.$(SQLITE3_ORIGIN)) SQLITE3_SHELL_SRC.0 = $(SRCDIR_extsrc)/shell.c SQLITE3_SHELL_SRC.1 = $(SRCDIR_extsrc)/shell-see.c # SQLITE3_SHELL_SRC.2 comes from the configure process SQLITE3_SHELL_SRC. = $(SRCDIR_extsrc)/shell.c SQLITE3_SHELL_SRC = $(SQLITE3_SHELL_SRC.$(SQLITE3_ORIGIN)) SEE_FLAGS.0 = SEE_FLAGS.1 = -DSQLITE_HAS_CODEC -DSQLITE_SHELL_DBKEY_PROC=fossil_key SEE_FLAGS. = SEE_FLAGS = $(SEE_FLAGS.$(USE_SEE)) EXTRAOBJ = \ $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) \ $(OBJDIR)/pikchr.o \ $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ $(OBJDIR)/th_lang.o \ $(OBJDIR)/th_tcl.o \ $(OBJDIR)/cson_amalgamation.o zlib: $(ZLIBTARGETS) $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) $(ZLIBCONFIG) -f win32/Makefile.gcc libz.a clean-zlib: $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) -f win32/Makefile.gcc clean BLDTARGETS = zlib openssl: $(BLDTARGETS) cd $(OPENSSLLIBDIR);./Configure --cross-compile-prefix=$(PREFIX) $(SSLCONFIG) sed -i -e 's/^PERL=C:\\.*$$/PERL=perl.exe/i' $(OPENSSLLIBDIR)/Makefile $(MAKE) -C $(OPENSSLLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) build_libs clean-openssl: $(MAKE) -C $(OPENSSLLIBDIR) PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) clean tcl: cd $(TCLSRCDIR)/win;./configure $(MAKE) -C $(TCLSRCDIR)/win PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) $(TCLTARGET) clean-tcl: $(MAKE) -C $(TCLSRCDIR)/win PREFIX=$(PREFIX) CC=$(PREFIX)$(TCCEXE) distclean APPTARGETS += $(BLDTARGETS) ifdef FOSSIL_BUILD_SSL APPTARGETS += openssl endif $(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(CODECHECK1) $(TRANS_SRC) $(TCC) -o $@ $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(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: 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)/ajax_.c:$(OBJDIR)/ajax.h \ $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \ $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.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)/capabilities_.c:$(OBJDIR)/capabilities.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/chat_.c:$(OBJDIR)/chat.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)/color_.c:$(OBJDIR)/color.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/deltafunc_.c:$(OBJDIR)/deltafunc.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/extcgi_.c:$(OBJDIR)/extcgi.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/fileedit_.c:$(OBJDIR)/fileedit.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/fuzz_.c:$(OBJDIR)/fuzz.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/hook_.c:$(OBJDIR)/hook.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)/interwiki_.c:$(OBJDIR)/interwiki.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)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/piechart_.c:$(OBJDIR)/piechart.h \ $(OBJDIR)/pikchrshow_.c:$(OBJDIR)/pikchrshow.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)/repolist_.c:$(OBJDIR)/repolist.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)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/setupuser_.c:$(OBJDIR)/setupuser.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/sha1hard_.c:$(OBJDIR)/sha1hard.h \ $(OBJDIR)/sha3_.c:$(OBJDIR)/sha3.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/smtp_.c:$(OBJDIR)/smtp.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)/terminal_.c:$(OBJDIR)/terminal.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)/unversioned_.c:$(OBJDIR)/unversioned.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)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ $(SRCDIR_extsrc)/pikchr.c:$(OBJDIR)/pikchr.h \ $(SRCDIR_extsrc)/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)/ajax_.c: $(SRCDIR)/ajax.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/ajax.c >$@ $(OBJDIR)/ajax.o: $(OBJDIR)/ajax_.c $(OBJDIR)/ajax.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/ajax.o -c $(OBJDIR)/ajax_.c $(OBJDIR)/ajax.h: $(OBJDIR)/headers $(OBJDIR)/alerts_.c: $(SRCDIR)/alerts.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/alerts.c >$@ $(OBJDIR)/alerts.o: $(OBJDIR)/alerts_.c $(OBJDIR)/alerts.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/alerts.o -c $(OBJDIR)/alerts_.c $(OBJDIR)/alerts.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)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/backlink.c >$@ $(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h: $(OBJDIR)/headers $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/backoffice.o -c $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.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)/capabilities_.c: $(SRCDIR)/capabilities.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/capabilities.c >$@ $(OBJDIR)/capabilities.o: $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/capabilities.o -c $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.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)/chat_.c: $(SRCDIR)/chat.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/chat.c >$@ $(OBJDIR)/chat.o: $(OBJDIR)/chat_.c $(OBJDIR)/chat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/chat.o -c $(OBJDIR)/chat_.c $(OBJDIR)/chat.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)/color_.c: $(SRCDIR)/color.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/color.c >$@ $(OBJDIR)/color.o: $(OBJDIR)/color_.c $(OBJDIR)/color.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/color.o -c $(OBJDIR)/color_.c $(OBJDIR)/color.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)/cookies_.c: $(SRCDIR)/cookies.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/cookies.c >$@ $(OBJDIR)/cookies.o: $(OBJDIR)/cookies_.c $(OBJDIR)/cookies.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cookies.o -c $(OBJDIR)/cookies_.c $(OBJDIR)/cookies.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)/deltafunc_.c: $(SRCDIR)/deltafunc.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/deltafunc.c >$@ $(OBJDIR)/deltafunc.o: $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltafunc.o -c $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.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)/dispatch_.c: $(SRCDIR)/dispatch.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/dispatch.c >$@ $(OBJDIR)/dispatch.o: $(OBJDIR)/dispatch_.c $(OBJDIR)/dispatch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/dispatch.o -c $(OBJDIR)/dispatch_.c $(OBJDIR)/dispatch.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)/etag_.c: $(SRCDIR)/etag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/etag.c >$@ $(OBJDIR)/etag.o: $(OBJDIR)/etag_.c $(OBJDIR)/etag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/etag.o -c $(OBJDIR)/etag_.c $(OBJDIR)/etag.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)/extcgi_.c: $(SRCDIR)/extcgi.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/extcgi.c >$@ $(OBJDIR)/extcgi.o: $(OBJDIR)/extcgi_.c $(OBJDIR)/extcgi.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/extcgi.o -c $(OBJDIR)/extcgi_.c $(OBJDIR)/extcgi.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)/fileedit_.c: $(SRCDIR)/fileedit.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fileedit.c >$@ $(OBJDIR)/fileedit.o: $(OBJDIR)/fileedit_.c $(OBJDIR)/fileedit.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fileedit.o -c $(OBJDIR)/fileedit_.c $(OBJDIR)/fileedit.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)/forum_.c: $(SRCDIR)/forum.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/forum.c >$@ $(OBJDIR)/forum.o: $(OBJDIR)/forum_.c $(OBJDIR)/forum.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/forum.o -c $(OBJDIR)/forum_.c $(OBJDIR)/forum.h: $(OBJDIR)/headers $(OBJDIR)/fshell_.c: $(SRCDIR)/fshell.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fshell.c >$@ $(OBJDIR)/fshell.o: $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fshell.o -c $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.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)/fuzz_.c: $(SRCDIR)/fuzz.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fuzz.c >$@ $(OBJDIR)/fuzz.o: $(OBJDIR)/fuzz_.c $(OBJDIR)/fuzz.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fuzz.o -c $(OBJDIR)/fuzz_.c $(OBJDIR)/fuzz.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)/hname_.c: $(SRCDIR)/hname.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/hname.c >$@ $(OBJDIR)/hname.o: $(OBJDIR)/hname_.c $(OBJDIR)/hname.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/hname.o -c $(OBJDIR)/hname_.c $(OBJDIR)/hname.h: $(OBJDIR)/headers $(OBJDIR)/hook_.c: $(SRCDIR)/hook.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/hook.c >$@ $(OBJDIR)/hook.o: $(OBJDIR)/hook_.c $(OBJDIR)/hook.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/hook.o -c $(OBJDIR)/hook_.c $(OBJDIR)/hook.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)/interwiki_.c: $(SRCDIR)/interwiki.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/interwiki.c >$@ $(OBJDIR)/interwiki.o: $(OBJDIR)/interwiki_.c $(OBJDIR)/interwiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/interwiki.o -c $(OBJDIR)/interwiki_.c $(OBJDIR)/interwiki.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)/patch_.c: $(SRCDIR)/patch.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/patch.c >$@ $(OBJDIR)/patch.o: $(OBJDIR)/patch_.c $(OBJDIR)/patch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/patch.o -c $(OBJDIR)/patch_.c $(OBJDIR)/patch.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)/piechart_.c: $(SRCDIR)/piechart.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/piechart.c >$@ $(OBJDIR)/piechart.o: $(OBJDIR)/piechart_.c $(OBJDIR)/piechart.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/piechart.o -c $(OBJDIR)/piechart_.c $(OBJDIR)/piechart.h: $(OBJDIR)/headers $(OBJDIR)/pikchrshow_.c: $(SRCDIR)/pikchrshow.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/pikchrshow.c >$@ $(OBJDIR)/pikchrshow.o: $(OBJDIR)/pikchrshow_.c $(OBJDIR)/pikchrshow.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pikchrshow.o -c $(OBJDIR)/pikchrshow_.c $(OBJDIR)/pikchrshow.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)/repolist_.c: $(SRCDIR)/repolist.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/repolist.c >$@ $(OBJDIR)/repolist.o: $(OBJDIR)/repolist_.c $(OBJDIR)/repolist.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/repolist.o -c $(OBJDIR)/repolist_.c $(OBJDIR)/repolist.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)/security_audit_.c: $(SRCDIR)/security_audit.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/security_audit.c >$@ $(OBJDIR)/security_audit.o: $(OBJDIR)/security_audit_.c $(OBJDIR)/security_audit.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/security_audit.o -c $(OBJDIR)/security_audit_.c $(OBJDIR)/security_audit.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)/setupuser_.c: $(SRCDIR)/setupuser.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/setupuser.c >$@ $(OBJDIR)/setupuser.o: $(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setupuser.o -c $(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.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)/sha1hard_.c: $(SRCDIR)/sha1hard.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sha1hard.c >$@ $(OBJDIR)/sha1hard.o: $(OBJDIR)/sha1hard_.c $(OBJDIR)/sha1hard.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha1hard.o -c $(OBJDIR)/sha1hard_.c $(OBJDIR)/sha1hard.h: $(OBJDIR)/headers $(OBJDIR)/sha3_.c: $(SRCDIR)/sha3.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sha3.c >$@ $(OBJDIR)/sha3.o: $(OBJDIR)/sha3_.c $(OBJDIR)/sha3.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha3.o -c $(OBJDIR)/sha3_.c $(OBJDIR)/sha3.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)/smtp_.c: $(SRCDIR)/smtp.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/smtp.c >$@ $(OBJDIR)/smtp.o: $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/smtp.o -c $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.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)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/terminal.c >$@ $(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.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)/unversioned_.c: $(SRCDIR)/unversioned.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/unversioned.c >$@ $(OBJDIR)/unversioned.o: $(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/unversioned.o -c $(OBJDIR)/unversioned_.c $(OBJDIR)/unversioned.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)/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 MINGW_OPTIONS = -D_HAVE__MINGW_H SQLITE_OPTIONS = -DNDEBUG=1 \ -DSQLITE_DQS=0 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ -DSQLITE_OMIT_DECLTYPE \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_PROGRESS_CALLBACK \ -DSQLITE_OMIT_SHARED_CACHE \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_MAX_EXPR_DEPTH=0 \ -DSQLITE_ENABLE_LOCKING_STYLE=0 \ -DSQLITE_DEFAULT_FILE_FORMAT=4 \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_DBSTAT_VTAB \ -DSQLITE_ENABLE_FTS5 \ -DSQLITE_ENABLE_STMTVTAB \ -DSQLITE_HAVE_ZLIB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_TRUSTED_SCHEMA=0 \ -DHAVE_USLEEP \ -DSQLITE_WIN32_NO_ANSI \ $(MINGW_OPTIONS) \ -DSQLITE_USE_MALLOC_H \ -DSQLITE_USE_MSIZE SHELL_OPTIONS = -DNDEBUG=1 \ -DSQLITE_DQS=0 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_DEFAULT_MEMSTATUS=0 \ -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ -DSQLITE_OMIT_DECLTYPE \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_PROGRESS_CALLBACK \ -DSQLITE_OMIT_SHARED_CACHE \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_MAX_EXPR_DEPTH=0 \ -DSQLITE_ENABLE_LOCKING_STYLE=0 \ -DSQLITE_DEFAULT_FILE_FORMAT=4 \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_DBSTAT_VTAB \ -DSQLITE_ENABLE_FTS5 \ -DSQLITE_ENABLE_STMTVTAB \ -DSQLITE_HAVE_ZLIB \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_TRUSTED_SCHEMA=0 \ -DHAVE_USLEEP \ -Dmain=sqlite3_shell \ -DSQLITE_SHELL_IS_UTF8=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname \ -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc \ -Daccess=file_access \ -Dsystem=fossil_system \ -Dgetenv=fossil_getenv \ -Dfopen=fossil_fopen PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 $(SQLITE3_OBJ): $(SQLITE3_SRC) $(SRCDIR)/../win/Makefile.mingw.mistachkin $(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) \ -c $(SQLITE3_SRC) -o $@ $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(XTCC) -c $(SRCDIR_extsrc)/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: $(SQLITE3_SHELL_SRC) $(SRCDIR_extsrc)/sqlite3.h $(SRCDIR)/../win/Makefile.mingw.mistachkin $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) $(SEE_FLAGS) -c $(SQLITE3_SHELL_SRC) -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)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
Added win/Makefile.msc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "tools/makemake.tcl") ############################################################################## # # # 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 SRCDIR_extsrc = $(B)\extsrc SRCDIR_tools = $(B)\tools T = . OBJDIR = $(T) OX = $(OBJDIR) O = .obj E = .exe P = .pdb DBGOPTS = /Od INSTALLDIR = . !ifdef DESTDIR INSTALLDIR = $(DESTDIR)\$(INSTALLDIR) !endif # When building out of source, this Makefile needs to know the path to the base # top-level directory for this project. Pass it on NMAKE command line via make # variable B: # NMAKE /f "path\to\this\Makefile" B="path/to/fossil/root" # # NOTE: Make sure B path has no trailing backslash, UNIX-style path is OK too. # !if !exist("$(B)\.fossil-settings") !error Please specify path to project base directory: B="path/to/fossil" !endif # Perl is only necessary if OpenSSL support is enabled and it is built from # source code. The PERLDIR environment variable, if it exists, should point # to the directory containing the main Perl executable specified here (i.e. # "perl.exe"). PERL = perl.exe # Enable use of available compiler optimizations? !ifndef OPTIMIZATIONS OPTIMIZATIONS = 2 !endif # Enable debugging symbols? !ifndef DEBUG DEBUG = 0 !endif !ifdef FOSSIL_DEBUG DEBUG = 1 !endif # Build the OpenSSL libraries? !ifndef FOSSIL_BUILD_SSL FOSSIL_BUILD_SSL = 0 !endif # Build the included zlib library? !ifndef FOSSIL_BUILD_ZLIB FOSSIL_BUILD_ZLIB = 1 !endif # Link everything except SQLite dynamically? !ifndef FOSSIL_DYNAMIC_BUILD FOSSIL_DYNAMIC_BUILD = 0 !endif # Enable relative paths in external diff/gdiff? !ifndef FOSSIL_ENABLE_EXEC_REL_PATHS FOSSIL_ENABLE_EXEC_REL_PATHS = 0 !endif # Enable the JSON API? !ifndef FOSSIL_ENABLE_JSON FOSSIL_ENABLE_JSON = 0 !endif # Enable OpenSSL support? !ifndef FOSSIL_ENABLE_SSL FOSSIL_ENABLE_SSL = 0 !endif # Enable the Tcl integration subsystem? !ifndef FOSSIL_ENABLE_TCL FOSSIL_ENABLE_TCL = 0 !endif # Enable TH1 scripts in embedded documentation files? !ifndef FOSSIL_ENABLE_TH1_DOCS FOSSIL_ENABLE_TH1_DOCS = 0 !endif # Enable TH1 hooks for commands and web pages? !ifndef FOSSIL_ENABLE_TH1_HOOKS FOSSIL_ENABLE_TH1_HOOKS = 0 !endif # Enable support for Windows XP with Visual Studio 201x? !ifndef FOSSIL_ENABLE_WINXP FOSSIL_ENABLE_WINXP = 0 !endif # Enable support for the SQLite Encryption Extension? !ifndef USE_SEE USE_SEE = 0 !endif !if $(FOSSIL_ENABLE_SSL)!=0 SSLDIR = $(B)\compat\openssl SSLINCDIR = $(SSLDIR)\include !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLLIBDIR = $(SSLDIR) !else SSLLIBDIR = $(SSLDIR) !endif SSLLIB = libssl.lib libcrypto.lib user32.lib gdi32.lib crypt32.lib !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" !message Using 'x64' platform for OpenSSL... SSLCONFIG = VC-WIN64A no-asm no-ssl3 no-weak-ssl-ciphers !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLCONFIG = $(SSLCONFIG) shared !else SSLCONFIG = $(SSLCONFIG) no-shared !endif !elseif "$(PLATFORM)"=="ia64" !message Using 'ia64' platform for OpenSSL... SSLCONFIG = VC-WIN64I no-asm no-ssl3 no-weak-ssl-ciphers !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLCONFIG = $(SSLCONFIG) shared !else SSLCONFIG = $(SSLCONFIG) no-shared !endif !else !message Assuming 'x86' platform for OpenSSL... SSLCONFIG = VC-WIN32 no-asm no-ssl3 no-weak-ssl-ciphers !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLCONFIG = $(SSLCONFIG) shared !else SSLCONFIG = $(SSLCONFIG) no-shared !endif !endif !endif !if $(FOSSIL_ENABLE_TCL)!=0 TCLDIR = $(B)\compat\tcl-8.6 TCLSRCDIR = $(TCLDIR) TCLINCDIR = $(TCLSRCDIR)\generic !endif # zlib options ZINCDIR = $(B)\compat\zlib ZLIBDIR = $(B)\compat\zlib !if $(FOSSIL_DYNAMIC_BUILD)!=0 ZLIB = zdll.lib !else ZLIB = zlib.lib !endif INCL = /I. /I"$(OX)" /I"$(SRCDIR)" /I"$(SRCDIR_extsrc)" /I"$(B)\win\include" INCL = $(INCL) /I"$(ZINCDIR)" !if $(FOSSIL_ENABLE_SSL)!=0 INCL = $(INCL) /I"$(SSLINCDIR)" !endif !if $(FOSSIL_ENABLE_TCL)!=0 INCL = $(INCL) /I"$(TCLINCDIR)" !endif CFLAGS = /nologo /W2 /WX /utf-8 LDFLAGS = CFLAGS = $(CFLAGS) /D_CRT_SECURE_NO_DEPRECATE /D_CRT_SECURE_NO_WARNINGS CFLAGS = $(CFLAGS) /D_CRT_NONSTDC_NO_DEPRECATE /D_CRT_NONSTDC_NO_WARNINGS !if $(FOSSIL_DYNAMIC_BUILD)!=0 LDFLAGS = $(LDFLAGS) /MANIFEST !else LDFLAGS = $(LDFLAGS) /NODEFAULTLIB:msvcrt /MANIFEST:NO !endif !if $(FOSSIL_ENABLE_WINXP)!=0 XPCFLAGS = $(XPCFLAGS) /D_WIN32_WINNT=0x0501 /D_USING_V110_SDK71_=1 CFLAGS = $(CFLAGS) $(XPCFLAGS) # # NOTE: For regular builds, /OSVERSION defaults to the /SUBSYSTEM version and # explicit initialization is redundant, but is required for post-built edits. # !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" XPLDFLAGS = $(XPLDFLAGS) /OSVERSION:5.02 /SUBSYSTEM:CONSOLE,5.02 !else XPLDFLAGS = $(XPLDFLAGS) /OSVERSION:5.01 /SUBSYSTEM:CONSOLE,5.01 !endif LDFLAGS = $(LDFLAGS) $(XPLDFLAGS) # # NOTE: Only XPCFLAGS is forwarded to the OpenSSL configuration, and XPLDFLAGS # is applied in a separate post-build step, see below for more information. # !if $(FOSSIL_ENABLE_SSL)!=0 SSLCONFIG = $(SSLCONFIG) $(XPCFLAGS) !endif !endif !if $(FOSSIL_DYNAMIC_BUILD)!=0 !if $(DEBUG)!=0 CRTFLAGS = /MDd !else CRTFLAGS = /MD !endif !else !if $(DEBUG)!=0 CRTFLAGS = /MTd !else CRTFLAGS = /MT !endif !endif !if $(OPTIMIZATIONS)>3 RELOPTS = /Os !elseif $(OPTIMIZATIONS)>2 RELOPTS = /Ox !elseif $(OPTIMIZATIONS)>1 RELOPTS = /O2 !elseif $(OPTIMIZATIONS)>0 RELOPTS = /O1 !else RELOPTS = !endif !if $(DEBUG)!=0 CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG LDFLAGS = $(LDFLAGS) /DEBUG !else CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS) !endif BCC = $(CC) $(CFLAGS) TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) MTC = mt LIBS = ws2_32.lib advapi32.lib dnsapi.lib LIBDIR = !if $(FOSSIL_DYNAMIC_BUILD)!=0 TCC = $(TCC) /DFOSSIL_DYNAMIC_BUILD=1 RCC = $(RCC) /DFOSSIL_DYNAMIC_BUILD=1 !endif LIBS = $(LIBS) $(ZLIB) LIBDIR = $(LIBDIR) /LIBPATH:"$(ZLIBDIR)" !if $(FOSSIL_ENABLE_JSON)!=0 TCC = $(TCC) /DFOSSIL_ENABLE_JSON=1 RCC = $(RCC) /DFOSSIL_ENABLE_JSON=1 !endif !if $(FOSSIL_ENABLE_SSL)!=0 TCC = $(TCC) /DFOSSIL_ENABLE_SSL=1 RCC = $(RCC) /DFOSSIL_ENABLE_SSL=1 LIBS = $(LIBS) $(SSLLIB) LIBDIR = $(LIBDIR) /LIBPATH:"$(SSLLIBDIR)" !endif !if $(FOSSIL_ENABLE_EXEC_REL_PATHS)!=0 TCC = $(TCC) /DFOSSIL_ENABLE_EXEC_REL_PATHS=1 RCC = $(RCC) /DFOSSIL_ENABLE_EXEC_REL_PATHS=1 !endif !if $(FOSSIL_ENABLE_TH1_DOCS)!=0 TCC = $(TCC) /DFOSSIL_ENABLE_TH1_DOCS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TH1_DOCS=1 !endif !if $(FOSSIL_ENABLE_TH1_HOOKS)!=0 TCC = $(TCC) /DFOSSIL_ENABLE_TH1_HOOKS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TH1_HOOKS=1 !endif !if $(FOSSIL_ENABLE_TCL)!=0 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 !if $(USE_SEE)!=0 TCC = $(TCC) /DUSE_SEE=1 RCC = $(RCC) /DUSE_SEE=1 !endif SQLITE_OPTIONS = /DNDEBUG=1 \ /DSQLITE_DQS=0 \ /DSQLITE_THREADSAFE=0 \ /DSQLITE_DEFAULT_MEMSTATUS=0 \ /DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ /DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ /DSQLITE_OMIT_DECLTYPE \ /DSQLITE_OMIT_DEPRECATED \ /DSQLITE_OMIT_PROGRESS_CALLBACK \ /DSQLITE_OMIT_SHARED_CACHE \ /DSQLITE_OMIT_LOAD_EXTENSION \ /DSQLITE_MAX_EXPR_DEPTH=0 \ /DSQLITE_ENABLE_LOCKING_STYLE=0 \ /DSQLITE_DEFAULT_FILE_FORMAT=4 \ /DSQLITE_ENABLE_EXPLAIN_COMMENTS \ /DSQLITE_ENABLE_FTS4 \ /DSQLITE_ENABLE_DBSTAT_VTAB \ /DSQLITE_ENABLE_FTS5 \ /DSQLITE_ENABLE_STMTVTAB \ /DSQLITE_HAVE_ZLIB \ /DSQLITE_ENABLE_DBPAGE_VTAB \ /DSQLITE_TRUSTED_SCHEMA=0 \ /DHAVE_USLEEP \ /DSQLITE_WIN32_NO_ANSI SHELL_OPTIONS = /DNDEBUG=1 \ /DSQLITE_DQS=0 \ /DSQLITE_THREADSAFE=0 \ /DSQLITE_DEFAULT_MEMSTATUS=0 \ /DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ /DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ /DSQLITE_OMIT_DECLTYPE \ /DSQLITE_OMIT_DEPRECATED \ /DSQLITE_OMIT_PROGRESS_CALLBACK \ /DSQLITE_OMIT_SHARED_CACHE \ /DSQLITE_OMIT_LOAD_EXTENSION \ /DSQLITE_MAX_EXPR_DEPTH=0 \ /DSQLITE_ENABLE_LOCKING_STYLE=0 \ /DSQLITE_DEFAULT_FILE_FORMAT=4 \ /DSQLITE_ENABLE_EXPLAIN_COMMENTS \ /DSQLITE_ENABLE_FTS4 \ /DSQLITE_ENABLE_DBSTAT_VTAB \ /DSQLITE_ENABLE_FTS5 \ /DSQLITE_ENABLE_STMTVTAB \ /DSQLITE_HAVE_ZLIB \ /DSQLITE_ENABLE_DBPAGE_VTAB \ /DSQLITE_TRUSTED_SCHEMA=0 \ /DHAVE_USLEEP \ /Dmain=sqlite3_shell \ /DSQLITE_SHELL_IS_UTF8=1 \ /DSQLITE_OMIT_LOAD_EXTENSION=1 \ /DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ /DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname \ /DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc \ /Daccess=file_access \ /Dsystem=fossil_system \ /Dgetenv=fossil_getenv \ /Dfopen=fossil_fopen PIKCHR_OPTIONS = /DPIKCHR_TOKEN_LIMIT=10000 SRC = "$(OX)\add_.c" \ "$(OX)\ajax_.c" \ "$(OX)\alerts_.c" \ "$(OX)\allrepo_.c" \ "$(OX)\attach_.c" \ "$(OX)\backlink_.c" \ "$(OX)\backoffice_.c" \ "$(OX)\bag_.c" \ "$(OX)\bisect_.c" \ "$(OX)\blob_.c" \ "$(OX)\branch_.c" \ "$(OX)\browse_.c" \ "$(OX)\builtin_.c" \ "$(OX)\bundle_.c" \ "$(OX)\cache_.c" \ "$(OX)\capabilities_.c" \ "$(OX)\captcha_.c" \ "$(OX)\cgi_.c" \ "$(OX)\chat_.c" \ "$(OX)\checkin_.c" \ "$(OX)\checkout_.c" \ "$(OX)\clearsign_.c" \ "$(OX)\clone_.c" \ "$(OX)\color_.c" \ "$(OX)\comformat_.c" \ "$(OX)\configure_.c" \ "$(OX)\content_.c" \ "$(OX)\cookies_.c" \ "$(OX)\db_.c" \ "$(OX)\delta_.c" \ "$(OX)\deltacmd_.c" \ "$(OX)\deltafunc_.c" \ "$(OX)\descendants_.c" \ "$(OX)\diff_.c" \ "$(OX)\diffcmd_.c" \ "$(OX)\dispatch_.c" \ "$(OX)\doc_.c" \ "$(OX)\encode_.c" \ "$(OX)\etag_.c" \ "$(OX)\event_.c" \ "$(OX)\export_.c" \ "$(OX)\extcgi_.c" \ "$(OX)\file_.c" \ "$(OX)\fileedit_.c" \ "$(OX)\finfo_.c" \ "$(OX)\foci_.c" \ "$(OX)\forum_.c" \ "$(OX)\fshell_.c" \ "$(OX)\fusefs_.c" \ "$(OX)\fuzz_.c" \ "$(OX)\glob_.c" \ "$(OX)\graph_.c" \ "$(OX)\gzip_.c" \ "$(OX)\hname_.c" \ "$(OX)\hook_.c" \ "$(OX)\http_.c" \ "$(OX)\http_socket_.c" \ "$(OX)\http_ssl_.c" \ "$(OX)\http_transport_.c" \ "$(OX)\import_.c" \ "$(OX)\info_.c" \ "$(OX)\interwiki_.c" \ "$(OX)\json_.c" \ "$(OX)\json_artifact_.c" \ "$(OX)\json_branch_.c" \ "$(OX)\json_config_.c" \ "$(OX)\json_diff_.c" \ "$(OX)\json_dir_.c" \ "$(OX)\json_finfo_.c" \ "$(OX)\json_login_.c" \ "$(OX)\json_query_.c" \ "$(OX)\json_report_.c" \ "$(OX)\json_status_.c" \ "$(OX)\json_tag_.c" \ "$(OX)\json_timeline_.c" \ "$(OX)\json_user_.c" \ "$(OX)\json_wiki_.c" \ "$(OX)\leaf_.c" \ "$(OX)\loadctrl_.c" \ "$(OX)\login_.c" \ "$(OX)\lookslike_.c" \ "$(OX)\main_.c" \ "$(OX)\manifest_.c" \ "$(OX)\markdown_.c" \ "$(OX)\markdown_html_.c" \ "$(OX)\md5_.c" \ "$(OX)\merge_.c" \ "$(OX)\merge3_.c" \ "$(OX)\moderate_.c" \ "$(OX)\name_.c" \ "$(OX)\patch_.c" \ "$(OX)\path_.c" \ "$(OX)\piechart_.c" \ "$(OX)\pikchrshow_.c" \ "$(OX)\pivot_.c" \ "$(OX)\popen_.c" \ "$(OX)\pqueue_.c" \ "$(OX)\printf_.c" \ "$(OX)\publish_.c" \ "$(OX)\purge_.c" \ "$(OX)\rebuild_.c" \ "$(OX)\regexp_.c" \ "$(OX)\repolist_.c" \ "$(OX)\report_.c" \ "$(OX)\rss_.c" \ "$(OX)\schema_.c" \ "$(OX)\search_.c" \ "$(OX)\security_audit_.c" \ "$(OX)\setup_.c" \ "$(OX)\setupuser_.c" \ "$(OX)\sha1_.c" \ "$(OX)\sha1hard_.c" \ "$(OX)\sha3_.c" \ "$(OX)\shun_.c" \ "$(OX)\sitemap_.c" \ "$(OX)\skins_.c" \ "$(OX)\smtp_.c" \ "$(OX)\sqlcmd_.c" \ "$(OX)\stash_.c" \ "$(OX)\stat_.c" \ "$(OX)\statrep_.c" \ "$(OX)\style_.c" \ "$(OX)\sync_.c" \ "$(OX)\tag_.c" \ "$(OX)\tar_.c" \ "$(OX)\terminal_.c" \ "$(OX)\th_main_.c" \ "$(OX)\timeline_.c" \ "$(OX)\tkt_.c" \ "$(OX)\tktsetup_.c" \ "$(OX)\undo_.c" \ "$(OX)\unicode_.c" \ "$(OX)\unversioned_.c" \ "$(OX)\update_.c" \ "$(OX)\url_.c" \ "$(OX)\user_.c" \ "$(OX)\utf8_.c" \ "$(OX)\util_.c" \ "$(OX)\verify_.c" \ "$(OX)\vfile_.c" \ "$(OX)\wiki_.c" \ "$(OX)\wikiformat_.c" \ "$(OX)\winfile_.c" \ "$(OX)\winhttp_.c" \ "$(OX)\xfer_.c" \ "$(OX)\xfersetup_.c" \ "$(OX)\zip_.c" \ "$(SRCDIR_extsrc)\pikchr.c" EXTRA_FILES = "$(SRCDIR)\..\extsrc\pikchr-worker.js" \ "$(SRCDIR)\..\extsrc\pikchr.js" \ "$(SRCDIR)\..\extsrc\pikchr.wasm" \ "$(SRCDIR)\..\skins\ardoise\css.txt" \ "$(SRCDIR)\..\skins\ardoise\details.txt" \ "$(SRCDIR)\..\skins\ardoise\footer.txt" \ "$(SRCDIR)\..\skins\ardoise\header.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\darkmode\css.txt" \ "$(SRCDIR)\..\skins\darkmode\details.txt" \ "$(SRCDIR)\..\skins\darkmode\footer.txt" \ "$(SRCDIR)\..\skins\darkmode\header.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\etienne\css.txt" \ "$(SRCDIR)\..\skins\etienne\details.txt" \ "$(SRCDIR)\..\skins\etienne\footer.txt" \ "$(SRCDIR)\..\skins\etienne\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\xekri\css.txt" \ "$(SRCDIR)\..\skins\xekri\details.txt" \ "$(SRCDIR)\..\skins\xekri\footer.txt" \ "$(SRCDIR)\..\skins\xekri\header.txt" \ "$(SRCDIR)\accordion.js" \ "$(SRCDIR)\alerts\bflat2.wav" \ "$(SRCDIR)\alerts\bflat3.wav" \ "$(SRCDIR)\alerts\bloop.wav" \ "$(SRCDIR)\alerts\plunk.wav" \ "$(SRCDIR)\ci_edit.js" \ "$(SRCDIR)\copybtn.js" \ "$(SRCDIR)\default.css" \ "$(SRCDIR)\diff.js" \ "$(SRCDIR)\diff.tcl" \ "$(SRCDIR)\forum.js" \ "$(SRCDIR)\fossil.bootstrap.js" \ "$(SRCDIR)\fossil.confirmer.js" \ "$(SRCDIR)\fossil.copybutton.js" \ "$(SRCDIR)\fossil.diff.js" \ "$(SRCDIR)\fossil.dom.js" \ "$(SRCDIR)\fossil.fetch.js" \ "$(SRCDIR)\fossil.numbered-lines.js" \ "$(SRCDIR)\fossil.page.brlist.js" \ "$(SRCDIR)\fossil.page.chat.js" \ "$(SRCDIR)\fossil.page.fileedit.js" \ "$(SRCDIR)\fossil.page.forumpost.js" \ "$(SRCDIR)\fossil.page.pikchrshow.js" \ "$(SRCDIR)\fossil.page.pikchrshowasm.js" \ "$(SRCDIR)\fossil.page.whistory.js" \ "$(SRCDIR)\fossil.page.wikiedit.js" \ "$(SRCDIR)\fossil.pikchr.js" \ "$(SRCDIR)\fossil.popupwidget.js" \ "$(SRCDIR)\fossil.storage.js" \ "$(SRCDIR)\fossil.tabs.js" \ "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \ "$(SRCDIR)\graph.js" \ "$(SRCDIR)\hbmenu.js" \ "$(SRCDIR)\href.js" \ "$(SRCDIR)\login.js" \ "$(SRCDIR)\markdown.md" \ "$(SRCDIR)\menu.js" \ "$(SRCDIR)\scroll.js" \ "$(SRCDIR)\skin.js" \ "$(SRCDIR)\sorttable.js" \ "$(SRCDIR)\sounds\0.wav" \ "$(SRCDIR)\sounds\1.wav" \ "$(SRCDIR)\sounds\2.wav" \ "$(SRCDIR)\sounds\3.wav" \ "$(SRCDIR)\sounds\4.wav" \ "$(SRCDIR)\sounds\5.wav" \ "$(SRCDIR)\sounds\6.wav" \ "$(SRCDIR)\sounds\7.wav" \ "$(SRCDIR)\sounds\8.wav" \ "$(SRCDIR)\sounds\9.wav" \ "$(SRCDIR)\sounds\a.wav" \ "$(SRCDIR)\sounds\b.wav" \ "$(SRCDIR)\sounds\c.wav" \ "$(SRCDIR)\sounds\d.wav" \ "$(SRCDIR)\sounds\e.wav" \ "$(SRCDIR)\sounds\f.wav" \ "$(SRCDIR)\style.admin_log.css" \ "$(SRCDIR)\style.chat.css" \ "$(SRCDIR)\style.fileedit.css" \ "$(SRCDIR)\style.pikchrshow.css" \ "$(SRCDIR)\style.wikiedit.css" \ "$(SRCDIR)\tree.js" \ "$(SRCDIR)\useredit.js" \ "$(SRCDIR)\wiki.wiki" OBJ = "$(OX)\add$O" \ "$(OX)\ajax$O" \ "$(OX)\alerts$O" \ "$(OX)\allrepo$O" \ "$(OX)\attach$O" \ "$(OX)\backlink$O" \ "$(OX)\backoffice$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)\capabilities$O" \ "$(OX)\captcha$O" \ "$(OX)\cgi$O" \ "$(OX)\chat$O" \ "$(OX)\checkin$O" \ "$(OX)\checkout$O" \ "$(OX)\clearsign$O" \ "$(OX)\clone$O" \ "$(OX)\color$O" \ "$(OX)\comformat$O" \ "$(OX)\configure$O" \ "$(OX)\content$O" \ "$(OX)\cookies$O" \ "$(OX)\cson_amalgamation$O" \ "$(OX)\db$O" \ "$(OX)\delta$O" \ "$(OX)\deltacmd$O" \ "$(OX)\deltafunc$O" \ "$(OX)\descendants$O" \ "$(OX)\diff$O" \ "$(OX)\diffcmd$O" \ "$(OX)\dispatch$O" \ "$(OX)\doc$O" \ "$(OX)\encode$O" \ "$(OX)\etag$O" \ "$(OX)\event$O" \ "$(OX)\export$O" \ "$(OX)\extcgi$O" \ "$(OX)\file$O" \ "$(OX)\fileedit$O" \ "$(OX)\finfo$O" \ "$(OX)\foci$O" \ "$(OX)\forum$O" \ "$(OX)\fshell$O" \ "$(OX)\fusefs$O" \ "$(OX)\fuzz$O" \ "$(OX)\glob$O" \ "$(OX)\graph$O" \ "$(OX)\gzip$O" \ "$(OX)\hname$O" \ "$(OX)\hook$O" \ "$(OX)\http$O" \ "$(OX)\http_socket$O" \ "$(OX)\http_ssl$O" \ "$(OX)\http_transport$O" \ "$(OX)\import$O" \ "$(OX)\info$O" \ "$(OX)\interwiki$O" \ "$(OX)\json$O" \ "$(OX)\json_artifact$O" \ "$(OX)\json_branch$O" \ "$(OX)\json_config$O" \ "$(OX)\json_diff$O" \ "$(OX)\json_dir$O" \ "$(OX)\json_finfo$O" \ "$(OX)\json_login$O" \ "$(OX)\json_query$O" \ "$(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)\patch$O" \ "$(OX)\path$O" \ "$(OX)\piechart$O" \ "$(OX)\pikchr$O" \ "$(OX)\pikchrshow$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)\repolist$O" \ "$(OX)\report$O" \ "$(OX)\rss$O" \ "$(OX)\schema$O" \ "$(OX)\search$O" \ "$(OX)\security_audit$O" \ "$(OX)\setup$O" \ "$(OX)\setupuser$O" \ "$(OX)\sha1$O" \ "$(OX)\sha1hard$O" \ "$(OX)\sha3$O" \ "$(OX)\shell$O" \ "$(OX)\shun$O" \ "$(OX)\sitemap$O" \ "$(OX)\skins$O" \ "$(OX)\smtp$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)\terminal$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)\unversioned$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)\xfer$O" \ "$(OX)\xfersetup$O" \ "$(OX)\zip$O" \ "$(OX)\fossil.res" !ifndef BASEAPPNAME BASEAPPNAME = fossil !endif APPNAME = $(OX)\$(BASEAPPNAME)$(E) PDBNAME = $(OX)\$(BASEAPPNAME)$(P) APPTARGETS = all: "$(OX)" "$(APPNAME)" $(BASEAPPNAME): "$(APPNAME)" $(BASEAPPNAME)$(E): "$(APPNAME)" install: "$(APPNAME)" echo F | xcopy /Y "$(APPNAME)" "$(INSTALLDIR)"\* !if $(DEBUG)!=0 echo F | xcopy /Y "$(PDBNAME)" "$(INSTALLDIR)"\* !endif $(OX): @-mkdir $@ zlib: @echo Building zlib from "$(ZLIBDIR)"... !if $(FOSSIL_ENABLE_WINXP)!=0 @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 clean-zlib: @pushd "$(ZLIBDIR)" && $(MAKE) /f win32\Makefile.msc clean && popd !if $(FOSSIL_ENABLE_SSL)!=0 openssl: @echo Building OpenSSL from "$(SSLDIR)"... !if $(FOSSIL_ENABLE_WINXP)!=0 @echo Passing XPCFLAGS = [ $(XPCFLAGS) ] to the OpenSSL configuration... !endif !ifdef PERLDIR @pushd "$(SSLDIR)" && "$(PERLDIR)\$(PERL)" Configure $(SSLCONFIG) && popd !else @pushd "$(SSLDIR)" && "$(PERL)" Configure $(SSLCONFIG) && popd !endif @pushd "$(SSLDIR)" && $(MAKE) && popd !if $(FOSSIL_ENABLE_WINXP)!=0 && $(FOSSIL_DYNAMIC_BUILD)!=0 # # NOTE: Appending custom linker flags to the OpenSSL default linker flags is # somewhat difficult, as summarized in this Fossil Forum post: # # https://fossil-scm.org/forum/forumpost/a9a2d6af28b # # Therefore the custom linker flags required for Windows XP dynamic builds are # applied in a separate post-build step. # # If the build stops here, or if the custom linker flags are outside the scope # of `editbin` or `link /EDIT` (i.e. additional libraries), consider tweaking # the OpenSSL makefile by hand. # # Also note that this step changes the subsystem for the OpenSSL DLLs from # WINDOWS to CONSOLE, but which has no effect on DLLs. # @echo Applying XPLDFLAGS = [ $(XPLDFLAGS) ] to the OpenSSL DLLs... @for /F "usebackq delims=" %F in (`dir /A:-D/B "$(SSLDIR)\*.dll" 2^>nul`) \ do @( \ echo %F & \ link /EDIT /NOLOGO $(XPLDFLAGS) "$(SSLDIR)\%F" || exit 1 \ ) !endif clean-openssl: @pushd "$(SSLDIR)" && $(MAKE) clean && popd !endif !if $(FOSSIL_BUILD_ZLIB)!=0 APPTARGETS = $(APPTARGETS) zlib !endif !if $(FOSSIL_ENABLE_SSL)!=0 !if $(FOSSIL_BUILD_SSL)!=0 APPTARGETS = $(APPTARGETS) openssl !endif !endif "$(APPNAME)" : $(APPTARGETS) "$(OBJDIR)\translate$E" "$(OBJDIR)\mkindex$E" "$(OBJDIR)\codecheck1$E" "$(OX)\headers" $(OBJ) "$(OX)\linkopts" "$(OBJDIR)\codecheck1$E" $(SRC) link $(LDFLAGS) /OUT:$@ /PDB:$(@D)\ $(LIBDIR) Wsetargv.obj "$(OX)\fossil.res" @"$(OX)\linkopts" if exist "$(B)\win\fossil.exe.manifest" \ $(MTC) -nologo -manifest "$(B)\win\fossil.exe.manifest" -outputresource:$@;1 "$(OX)\linkopts": "$(B)\win\Makefile.msc" echo "$(OX)\add.obj" > $@ echo "$(OX)\ajax.obj" >> $@ echo "$(OX)\alerts.obj" >> $@ echo "$(OX)\allrepo.obj" >> $@ echo "$(OX)\attach.obj" >> $@ echo "$(OX)\backlink.obj" >> $@ echo "$(OX)\backoffice.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)\capabilities.obj" >> $@ echo "$(OX)\captcha.obj" >> $@ echo "$(OX)\cgi.obj" >> $@ echo "$(OX)\chat.obj" >> $@ echo "$(OX)\checkin.obj" >> $@ echo "$(OX)\checkout.obj" >> $@ echo "$(OX)\clearsign.obj" >> $@ echo "$(OX)\clone.obj" >> $@ echo "$(OX)\color.obj" >> $@ echo "$(OX)\comformat.obj" >> $@ echo "$(OX)\configure.obj" >> $@ echo "$(OX)\content.obj" >> $@ echo "$(OX)\cookies.obj" >> $@ echo "$(OX)\cson_amalgamation.obj" >> $@ echo "$(OX)\db.obj" >> $@ echo "$(OX)\delta.obj" >> $@ echo "$(OX)\deltacmd.obj" >> $@ echo "$(OX)\deltafunc.obj" >> $@ echo "$(OX)\descendants.obj" >> $@ echo "$(OX)\diff.obj" >> $@ echo "$(OX)\diffcmd.obj" >> $@ echo "$(OX)\dispatch.obj" >> $@ echo "$(OX)\doc.obj" >> $@ echo "$(OX)\encode.obj" >> $@ echo "$(OX)\etag.obj" >> $@ echo "$(OX)\event.obj" >> $@ echo "$(OX)\export.obj" >> $@ echo "$(OX)\extcgi.obj" >> $@ echo "$(OX)\file.obj" >> $@ echo "$(OX)\fileedit.obj" >> $@ echo "$(OX)\finfo.obj" >> $@ echo "$(OX)\foci.obj" >> $@ echo "$(OX)\forum.obj" >> $@ echo "$(OX)\fshell.obj" >> $@ echo "$(OX)\fusefs.obj" >> $@ echo "$(OX)\fuzz.obj" >> $@ echo "$(OX)\glob.obj" >> $@ echo "$(OX)\graph.obj" >> $@ echo "$(OX)\gzip.obj" >> $@ echo "$(OX)\hname.obj" >> $@ echo "$(OX)\hook.obj" >> $@ echo "$(OX)\http.obj" >> $@ echo "$(OX)\http_socket.obj" >> $@ echo "$(OX)\http_ssl.obj" >> $@ echo "$(OX)\http_transport.obj" >> $@ echo "$(OX)\import.obj" >> $@ echo "$(OX)\info.obj" >> $@ echo "$(OX)\interwiki.obj" >> $@ echo "$(OX)\json.obj" >> $@ echo "$(OX)\json_artifact.obj" >> $@ echo "$(OX)\json_branch.obj" >> $@ echo "$(OX)\json_config.obj" >> $@ echo "$(OX)\json_diff.obj" >> $@ echo "$(OX)\json_dir.obj" >> $@ echo "$(OX)\json_finfo.obj" >> $@ echo "$(OX)\json_login.obj" >> $@ echo "$(OX)\json_query.obj" >> $@ 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)\patch.obj" >> $@ echo "$(OX)\path.obj" >> $@ echo "$(OX)\piechart.obj" >> $@ echo "$(OX)\pikchr.obj" >> $@ echo "$(OX)\pikchrshow.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)\repolist.obj" >> $@ echo "$(OX)\report.obj" >> $@ echo "$(OX)\rss.obj" >> $@ echo "$(OX)\schema.obj" >> $@ echo "$(OX)\search.obj" >> $@ echo "$(OX)\security_audit.obj" >> $@ echo "$(OX)\setup.obj" >> $@ echo "$(OX)\setupuser.obj" >> $@ echo "$(OX)\sha1.obj" >> $@ echo "$(OX)\sha1hard.obj" >> $@ echo "$(OX)\sha3.obj" >> $@ echo "$(OX)\shell.obj" >> $@ echo "$(OX)\shun.obj" >> $@ echo "$(OX)\sitemap.obj" >> $@ echo "$(OX)\skins.obj" >> $@ echo "$(OX)\smtp.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)\terminal.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)\unversioned.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)\xfer.obj" >> $@ echo "$(OX)\xfersetup.obj" >> $@ echo "$(OX)\zip.obj" >> $@ echo $(LIBS) >> $@ "$(OBJDIR)\translate$E": "$(SRCDIR_tools)\translate.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** "$(OBJDIR)\makeheaders$E": "$(SRCDIR_tools)\makeheaders.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** "$(OBJDIR)\mkindex$E": "$(SRCDIR_tools)\mkindex.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** "$(OBJDIR)\mkbuiltin$E": "$(SRCDIR_tools)\mkbuiltin.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** "$(OBJDIR)\mkversion$E": "$(SRCDIR_tools)\mkversion.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** "$(OBJDIR)\codecheck1$E": "$(SRCDIR_tools)\codecheck1.c" $(BCC) /Fe$@ /Fo$(@D)\ /Fd$(@D)\ $** !if $(USE_SEE)!=0 SEE_FLAGS = /DSQLITE_HAS_CODEC=1 /DSQLITE_SHELL_DBKEY_PROC=fossil_key SQLITE3_SHELL_SRC = $(SRCDIR)\shell-see.c SQLITE3_SRC = $(SRCDIR_extsrc)\sqlite3-see.c !else SEE_FLAGS = SQLITE3_SHELL_SRC = $(SRCDIR_extsrc)\shell.c SQLITE3_SRC = $(SRCDIR_extsrc)\sqlite3.c !endif "$(OX)\shell$O" : "$(SQLITE3_SHELL_SRC)" "$(B)\win\Makefile.msc" $(TCC) /Fo$@ /Fd$(@D)\ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $(SEE_FLAGS) -c "$(SQLITE3_SHELL_SRC)" "$(OX)\sqlite3$O" : "$(SQLITE3_SRC)" "$(B)\win\Makefile.msc" $(TCC) /Fo$@ /Fd$(@D)\ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SEE_FLAGS) "$(SQLITE3_SRC)" "$(OX)\th$O" : "$(SRCDIR)\th.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\th_lang$O" : "$(SRCDIR)\th_lang.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\th_tcl$O" : "$(SRCDIR)\th_tcl.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\pikchr$O" : "$(SRCDIR_extsrc)\pikchr.c" $(TCC) $(PIKCHR_OPTIONS) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" "$(B)\phony.h" "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" > $@ "$(B)\phony.h" : rem Force rebuild of VERSION.h whenever nmake is run "$(OX)\cson_amalgamation$O" : "$(SRCDIR_extsrc)\cson_amalgamation.c" $(TCC) /Fo$@ /Fd$(@D)\ -c $** "$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC) $** > $@ "$(OX)\builtin_data.h": "$(OBJDIR)\mkbuiltin$E" "$(OX)\builtin_data.reslist" "$(OBJDIR)\mkbuiltin$E" --prefix "$(SRCDIR)/" --reslist "$(OX)\builtin_data.reslist" > $@ cleanx: -del "$(OX)\*.obj" 2>NUL -del "$(OBJDIR)\*.obj" 2>NUL -del "$(OX)\*_.c" 2>NUL -del "$(OX)\*.h" 2>NUL -del "$(OX)\*.ilk" 2>NUL -del "$(OX)\*.map" 2>NUL -del "$(OX)\*.res" 2>NUL -del "$(OX)\*.reslist" 2>NUL -del "$(OX)\headers" 2>NUL -del "$(OX)\linkopts" 2>NUL -del "$(OX)\vc*.pdb" 2>NUL clean: cleanx -del "$(APPNAME)" 2>NUL -del "$(PDBNAME)" 2>NUL -del "$(OBJDIR)\translate$E" 2>NUL -del "$(OBJDIR)\translate$P" 2>NUL -del "$(OBJDIR)\mkindex$E" 2>NUL -del "$(OBJDIR)\mkindex$P" 2>NUL -del "$(OBJDIR)\makeheaders$E" 2>NUL -del "$(OBJDIR)\makeheaders$P" 2>NUL -del "$(OBJDIR)\mkversion$E" 2>NUL -del "$(OBJDIR)\mkversion$P" 2>NUL -del "$(OBJDIR)\mkcss$E" 2>NUL -del "$(OBJDIR)\mkcss$P" 2>NUL -del "$(OBJDIR)\codecheck1$E" 2>NUL -del "$(OBJDIR)\codecheck1$P" 2>NUL -del "$(OBJDIR)\mkbuiltin$E" 2>NUL -del "$(OBJDIR)\mkbuiltin$P" 2>NUL realclean: clean "$(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)\builtin_data.reslist": $(EXTRA_FILES) "$(B)\win\Makefile.msc" echo "$(SRCDIR)\../extsrc/pikchr-worker.js" > $@ echo "$(SRCDIR)\../extsrc/pikchr.js" >> $@ echo "$(SRCDIR)\../extsrc/pikchr.wasm" >> $@ echo "$(SRCDIR)\../skins/ardoise/css.txt" >> $@ echo "$(SRCDIR)\../skins/ardoise/details.txt" >> $@ echo "$(SRCDIR)\../skins/ardoise/footer.txt" >> $@ echo "$(SRCDIR)\../skins/ardoise/header.txt" >> $@ echo "$(SRCDIR)\../skins/black_and_white/css.txt" >> $@ echo "$(SRCDIR)\../skins/black_and_white/details.txt" >> $@ echo "$(SRCDIR)\../skins/black_and_white/footer.txt" >> $@ echo "$(SRCDIR)\../skins/black_and_white/header.txt" >> $@ echo "$(SRCDIR)\../skins/blitz/css.txt" >> $@ echo "$(SRCDIR)\../skins/blitz/details.txt" >> $@ echo "$(SRCDIR)\../skins/blitz/footer.txt" >> $@ echo "$(SRCDIR)\../skins/blitz/header.txt" >> $@ echo "$(SRCDIR)\../skins/blitz/ticket.txt" >> $@ echo "$(SRCDIR)\../skins/darkmode/css.txt" >> $@ echo "$(SRCDIR)\../skins/darkmode/details.txt" >> $@ echo "$(SRCDIR)\../skins/darkmode/footer.txt" >> $@ echo "$(SRCDIR)\../skins/darkmode/header.txt" >> $@ echo "$(SRCDIR)\../skins/default/css.txt" >> $@ echo "$(SRCDIR)\../skins/default/details.txt" >> $@ echo "$(SRCDIR)\../skins/default/footer.txt" >> $@ echo "$(SRCDIR)\../skins/default/header.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/css.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/details.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/footer.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/header.txt" >> $@ echo "$(SRCDIR)\../skins/etienne/css.txt" >> $@ echo "$(SRCDIR)\../skins/etienne/details.txt" >> $@ echo "$(SRCDIR)\../skins/etienne/footer.txt" >> $@ echo "$(SRCDIR)\../skins/etienne/header.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/css.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/details.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/footer.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/header.txt" >> $@ echo "$(SRCDIR)\../skins/original/css.txt" >> $@ echo "$(SRCDIR)\../skins/original/details.txt" >> $@ echo "$(SRCDIR)\../skins/original/footer.txt" >> $@ echo "$(SRCDIR)\../skins/original/header.txt" >> $@ echo "$(SRCDIR)\../skins/plain_gray/css.txt" >> $@ echo "$(SRCDIR)\../skins/plain_gray/details.txt" >> $@ echo "$(SRCDIR)\../skins/plain_gray/footer.txt" >> $@ echo "$(SRCDIR)\../skins/plain_gray/header.txt" >> $@ echo "$(SRCDIR)\../skins/xekri/css.txt" >> $@ echo "$(SRCDIR)\../skins/xekri/details.txt" >> $@ echo "$(SRCDIR)\../skins/xekri/footer.txt" >> $@ echo "$(SRCDIR)\../skins/xekri/header.txt" >> $@ echo "$(SRCDIR)\accordion.js" >> $@ echo "$(SRCDIR)\alerts/bflat2.wav" >> $@ echo "$(SRCDIR)\alerts/bflat3.wav" >> $@ echo "$(SRCDIR)\alerts/bloop.wav" >> $@ echo "$(SRCDIR)\alerts/plunk.wav" >> $@ echo "$(SRCDIR)\ci_edit.js" >> $@ echo "$(SRCDIR)\copybtn.js" >> $@ echo "$(SRCDIR)\default.css" >> $@ echo "$(SRCDIR)\diff.js" >> $@ echo "$(SRCDIR)\diff.tcl" >> $@ echo "$(SRCDIR)\forum.js" >> $@ echo "$(SRCDIR)\fossil.bootstrap.js" >> $@ echo "$(SRCDIR)\fossil.confirmer.js" >> $@ echo "$(SRCDIR)\fossil.copybutton.js" >> $@ echo "$(SRCDIR)\fossil.diff.js" >> $@ echo "$(SRCDIR)\fossil.dom.js" >> $@ echo "$(SRCDIR)\fossil.fetch.js" >> $@ echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@ echo "$(SRCDIR)\fossil.page.brlist.js" >> $@ echo "$(SRCDIR)\fossil.page.chat.js" >> $@ echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@ echo "$(SRCDIR)\fossil.page.pikchrshowasm.js" >> $@ echo "$(SRCDIR)\fossil.page.whistory.js" >> $@ echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@ echo "$(SRCDIR)\fossil.pikchr.js" >> $@ echo "$(SRCDIR)\fossil.popupwidget.js" >> $@ echo "$(SRCDIR)\fossil.storage.js" >> $@ echo "$(SRCDIR)\fossil.tabs.js" >> $@ echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@ echo "$(SRCDIR)\graph.js" >> $@ echo "$(SRCDIR)\hbmenu.js" >> $@ echo "$(SRCDIR)\href.js" >> $@ echo "$(SRCDIR)\login.js" >> $@ echo "$(SRCDIR)\markdown.md" >> $@ echo "$(SRCDIR)\menu.js" >> $@ echo "$(SRCDIR)\scroll.js" >> $@ echo "$(SRCDIR)\skin.js" >> $@ echo "$(SRCDIR)\sorttable.js" >> $@ echo "$(SRCDIR)\sounds/0.wav" >> $@ echo "$(SRCDIR)\sounds/1.wav" >> $@ echo "$(SRCDIR)\sounds/2.wav" >> $@ echo "$(SRCDIR)\sounds/3.wav" >> $@ echo "$(SRCDIR)\sounds/4.wav" >> $@ echo "$(SRCDIR)\sounds/5.wav" >> $@ echo "$(SRCDIR)\sounds/6.wav" >> $@ echo "$(SRCDIR)\sounds/7.wav" >> $@ echo "$(SRCDIR)\sounds/8.wav" >> $@ echo "$(SRCDIR)\sounds/9.wav" >> $@ echo "$(SRCDIR)\sounds/a.wav" >> $@ echo "$(SRCDIR)\sounds/b.wav" >> $@ echo "$(SRCDIR)\sounds/c.wav" >> $@ echo "$(SRCDIR)\sounds/d.wav" >> $@ echo "$(SRCDIR)\sounds/e.wav" >> $@ echo "$(SRCDIR)\sounds/f.wav" >> $@ echo "$(SRCDIR)\style.admin_log.css" >> $@ echo "$(SRCDIR)\style.chat.css" >> $@ echo "$(SRCDIR)\style.fileedit.css" >> $@ echo "$(SRCDIR)\style.pikchrshow.css" >> $@ echo "$(SRCDIR)\style.wikiedit.css" >> $@ echo "$(SRCDIR)\tree.js" >> $@ echo "$(SRCDIR)\useredit.js" >> $@ echo "$(SRCDIR)\wiki.wiki" >> $@ "$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\add_.c" "$(OX)\add_.c" : "$(SRCDIR)\add.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\ajax$O" : "$(OX)\ajax_.c" "$(OX)\ajax.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\ajax_.c" "$(OX)\ajax_.c" : "$(SRCDIR)\ajax.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\alerts$O" : "$(OX)\alerts_.c" "$(OX)\alerts.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\alerts_.c" "$(OX)\alerts_.c" : "$(SRCDIR)\alerts.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\allrepo$O" : "$(OX)\allrepo_.c" "$(OX)\allrepo.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\allrepo_.c" "$(OX)\allrepo_.c" : "$(SRCDIR)\allrepo.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\attach$O" : "$(OX)\attach_.c" "$(OX)\attach.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\attach_.c" "$(OX)\attach_.c" : "$(SRCDIR)\attach.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\backlink$O" : "$(OX)\backlink_.c" "$(OX)\backlink.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\backlink_.c" "$(OX)\backlink_.c" : "$(SRCDIR)\backlink.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\backoffice$O" : "$(OX)\backoffice_.c" "$(OX)\backoffice.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\backoffice_.c" "$(OX)\backoffice_.c" : "$(SRCDIR)\backoffice.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\bag$O" : "$(OX)\bag_.c" "$(OX)\bag.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\bag_.c" "$(OX)\bag_.c" : "$(SRCDIR)\bag.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\bisect$O" : "$(OX)\bisect_.c" "$(OX)\bisect.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\bisect_.c" "$(OX)\bisect_.c" : "$(SRCDIR)\bisect.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\blob$O" : "$(OX)\blob_.c" "$(OX)\blob.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\blob_.c" "$(OX)\blob_.c" : "$(SRCDIR)\blob.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\branch$O" : "$(OX)\branch_.c" "$(OX)\branch.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\branch_.c" "$(OX)\branch_.c" : "$(SRCDIR)\branch.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\browse$O" : "$(OX)\browse_.c" "$(OX)\browse.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\browse_.c" "$(OX)\browse_.c" : "$(SRCDIR)\browse.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\builtin$O" : "$(OX)\builtin_.c" "$(OX)\builtin.h" "$(OX)\builtin_data.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\builtin_.c" "$(OX)\builtin_.c" : "$(SRCDIR)\builtin.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\bundle$O" : "$(OX)\bundle_.c" "$(OX)\bundle.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\bundle_.c" "$(OX)\bundle_.c" : "$(SRCDIR)\bundle.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\cache$O" : "$(OX)\cache_.c" "$(OX)\cache.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\cache_.c" "$(OX)\cache_.c" : "$(SRCDIR)\cache.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\capabilities$O" : "$(OX)\capabilities_.c" "$(OX)\capabilities.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\capabilities_.c" "$(OX)\capabilities_.c" : "$(SRCDIR)\capabilities.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\captcha$O" : "$(OX)\captcha_.c" "$(OX)\captcha.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\captcha_.c" "$(OX)\captcha_.c" : "$(SRCDIR)\captcha.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\cgi$O" : "$(OX)\cgi_.c" "$(OX)\cgi.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\cgi_.c" "$(OX)\cgi_.c" : "$(SRCDIR)\cgi.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\chat$O" : "$(OX)\chat_.c" "$(OX)\chat.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\chat_.c" "$(OX)\chat_.c" : "$(SRCDIR)\chat.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\checkin$O" : "$(OX)\checkin_.c" "$(OX)\checkin.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\checkin_.c" "$(OX)\checkin_.c" : "$(SRCDIR)\checkin.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\checkout$O" : "$(OX)\checkout_.c" "$(OX)\checkout.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\checkout_.c" "$(OX)\checkout_.c" : "$(SRCDIR)\checkout.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\clearsign$O" : "$(OX)\clearsign_.c" "$(OX)\clearsign.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\clearsign_.c" "$(OX)\clearsign_.c" : "$(SRCDIR)\clearsign.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\clone$O" : "$(OX)\clone_.c" "$(OX)\clone.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\clone_.c" "$(OX)\clone_.c" : "$(SRCDIR)\clone.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\color$O" : "$(OX)\color_.c" "$(OX)\color.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\color_.c" "$(OX)\color_.c" : "$(SRCDIR)\color.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\comformat$O" : "$(OX)\comformat_.c" "$(OX)\comformat.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\comformat_.c" "$(OX)\comformat_.c" : "$(SRCDIR)\comformat.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\configure$O" : "$(OX)\configure_.c" "$(OX)\configure.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\configure_.c" "$(OX)\configure_.c" : "$(SRCDIR)\configure.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\content$O" : "$(OX)\content_.c" "$(OX)\content.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\content_.c" "$(OX)\content_.c" : "$(SRCDIR)\content.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\cookies$O" : "$(OX)\cookies_.c" "$(OX)\cookies.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\cookies_.c" "$(OX)\cookies_.c" : "$(SRCDIR)\cookies.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\db$O" : "$(OX)\db_.c" "$(OX)\db.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\db_.c" "$(OX)\db_.c" : "$(SRCDIR)\db.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\delta$O" : "$(OX)\delta_.c" "$(OX)\delta.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\delta_.c" "$(OX)\delta_.c" : "$(SRCDIR)\delta.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\deltacmd$O" : "$(OX)\deltacmd_.c" "$(OX)\deltacmd.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\deltacmd_.c" "$(OX)\deltacmd_.c" : "$(SRCDIR)\deltacmd.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\deltafunc$O" : "$(OX)\deltafunc_.c" "$(OX)\deltafunc.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\deltafunc_.c" "$(OX)\deltafunc_.c" : "$(SRCDIR)\deltafunc.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\descendants$O" : "$(OX)\descendants_.c" "$(OX)\descendants.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\descendants_.c" "$(OX)\descendants_.c" : "$(SRCDIR)\descendants.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\diff$O" : "$(OX)\diff_.c" "$(OX)\diff.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\diff_.c" "$(OX)\diff_.c" : "$(SRCDIR)\diff.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\diffcmd$O" : "$(OX)\diffcmd_.c" "$(OX)\diffcmd.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\diffcmd_.c" "$(OX)\diffcmd_.c" : "$(SRCDIR)\diffcmd.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\dispatch$O" : "$(OX)\dispatch_.c" "$(OX)\dispatch.h" "$(OX)\page_index.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\dispatch_.c" "$(OX)\dispatch_.c" : "$(SRCDIR)\dispatch.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\doc$O" : "$(OX)\doc_.c" "$(OX)\doc.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\doc_.c" "$(OX)\doc_.c" : "$(SRCDIR)\doc.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\encode$O" : "$(OX)\encode_.c" "$(OX)\encode.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\encode_.c" "$(OX)\encode_.c" : "$(SRCDIR)\encode.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\etag$O" : "$(OX)\etag_.c" "$(OX)\etag.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\etag_.c" "$(OX)\etag_.c" : "$(SRCDIR)\etag.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\event$O" : "$(OX)\event_.c" "$(OX)\event.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\event_.c" "$(OX)\event_.c" : "$(SRCDIR)\event.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\export$O" : "$(OX)\export_.c" "$(OX)\export.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\export_.c" "$(OX)\export_.c" : "$(SRCDIR)\export.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\extcgi$O" : "$(OX)\extcgi_.c" "$(OX)\extcgi.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\extcgi_.c" "$(OX)\extcgi_.c" : "$(SRCDIR)\extcgi.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\file$O" : "$(OX)\file_.c" "$(OX)\file.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\file_.c" "$(OX)\file_.c" : "$(SRCDIR)\file.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\fileedit$O" : "$(OX)\fileedit_.c" "$(OX)\fileedit.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\fileedit_.c" "$(OX)\fileedit_.c" : "$(SRCDIR)\fileedit.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\finfo$O" : "$(OX)\finfo_.c" "$(OX)\finfo.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\finfo_.c" "$(OX)\finfo_.c" : "$(SRCDIR)\finfo.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\foci$O" : "$(OX)\foci_.c" "$(OX)\foci.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\foci_.c" "$(OX)\foci_.c" : "$(SRCDIR)\foci.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\forum$O" : "$(OX)\forum_.c" "$(OX)\forum.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\forum_.c" "$(OX)\forum_.c" : "$(SRCDIR)\forum.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\fshell$O" : "$(OX)\fshell_.c" "$(OX)\fshell.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\fshell_.c" "$(OX)\fshell_.c" : "$(SRCDIR)\fshell.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\fusefs$O" : "$(OX)\fusefs_.c" "$(OX)\fusefs.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\fusefs_.c" "$(OX)\fusefs_.c" : "$(SRCDIR)\fusefs.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\fuzz$O" : "$(OX)\fuzz_.c" "$(OX)\fuzz.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\fuzz_.c" "$(OX)\fuzz_.c" : "$(SRCDIR)\fuzz.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\glob$O" : "$(OX)\glob_.c" "$(OX)\glob.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\glob_.c" "$(OX)\glob_.c" : "$(SRCDIR)\glob.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\graph$O" : "$(OX)\graph_.c" "$(OX)\graph.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\graph_.c" "$(OX)\graph_.c" : "$(SRCDIR)\graph.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\gzip$O" : "$(OX)\gzip_.c" "$(OX)\gzip.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\gzip_.c" "$(OX)\gzip_.c" : "$(SRCDIR)\gzip.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\hname$O" : "$(OX)\hname_.c" "$(OX)\hname.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\hname_.c" "$(OX)\hname_.c" : "$(SRCDIR)\hname.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\hook$O" : "$(OX)\hook_.c" "$(OX)\hook.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\hook_.c" "$(OX)\hook_.c" : "$(SRCDIR)\hook.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\http$O" : "$(OX)\http_.c" "$(OX)\http.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\http_.c" "$(OX)\http_.c" : "$(SRCDIR)\http.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\http_socket$O" : "$(OX)\http_socket_.c" "$(OX)\http_socket.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\http_socket_.c" "$(OX)\http_socket_.c" : "$(SRCDIR)\http_socket.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\http_ssl$O" : "$(OX)\http_ssl_.c" "$(OX)\http_ssl.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\http_ssl_.c" "$(OX)\http_ssl_.c" : "$(SRCDIR)\http_ssl.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\http_transport$O" : "$(OX)\http_transport_.c" "$(OX)\http_transport.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\http_transport_.c" "$(OX)\http_transport_.c" : "$(SRCDIR)\http_transport.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\import$O" : "$(OX)\import_.c" "$(OX)\import.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\import_.c" "$(OX)\import_.c" : "$(SRCDIR)\import.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\info$O" : "$(OX)\info_.c" "$(OX)\info.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\info_.c" "$(OX)\info_.c" : "$(SRCDIR)\info.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\interwiki$O" : "$(OX)\interwiki_.c" "$(OX)\interwiki.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\interwiki_.c" "$(OX)\interwiki_.c" : "$(SRCDIR)\interwiki.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json$O" : "$(OX)\json_.c" "$(OX)\json.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_.c" "$(OX)\json_.c" : "$(SRCDIR)\json.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_artifact$O" : "$(OX)\json_artifact_.c" "$(OX)\json_artifact.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_artifact_.c" "$(OX)\json_artifact_.c" : "$(SRCDIR)\json_artifact.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_branch$O" : "$(OX)\json_branch_.c" "$(OX)\json_branch.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_branch_.c" "$(OX)\json_branch_.c" : "$(SRCDIR)\json_branch.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_config$O" : "$(OX)\json_config_.c" "$(OX)\json_config.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_config_.c" "$(OX)\json_config_.c" : "$(SRCDIR)\json_config.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_diff$O" : "$(OX)\json_diff_.c" "$(OX)\json_diff.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_diff_.c" "$(OX)\json_diff_.c" : "$(SRCDIR)\json_diff.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_dir$O" : "$(OX)\json_dir_.c" "$(OX)\json_dir.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_dir_.c" "$(OX)\json_dir_.c" : "$(SRCDIR)\json_dir.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_finfo$O" : "$(OX)\json_finfo_.c" "$(OX)\json_finfo.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_finfo_.c" "$(OX)\json_finfo_.c" : "$(SRCDIR)\json_finfo.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_login$O" : "$(OX)\json_login_.c" "$(OX)\json_login.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_login_.c" "$(OX)\json_login_.c" : "$(SRCDIR)\json_login.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_query$O" : "$(OX)\json_query_.c" "$(OX)\json_query.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_query_.c" "$(OX)\json_query_.c" : "$(SRCDIR)\json_query.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_report$O" : "$(OX)\json_report_.c" "$(OX)\json_report.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_report_.c" "$(OX)\json_report_.c" : "$(SRCDIR)\json_report.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_status$O" : "$(OX)\json_status_.c" "$(OX)\json_status.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_status_.c" "$(OX)\json_status_.c" : "$(SRCDIR)\json_status.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_tag$O" : "$(OX)\json_tag_.c" "$(OX)\json_tag.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_tag_.c" "$(OX)\json_tag_.c" : "$(SRCDIR)\json_tag.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_timeline$O" : "$(OX)\json_timeline_.c" "$(OX)\json_timeline.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_timeline_.c" "$(OX)\json_timeline_.c" : "$(SRCDIR)\json_timeline.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_user$O" : "$(OX)\json_user_.c" "$(OX)\json_user.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_user_.c" "$(OX)\json_user_.c" : "$(SRCDIR)\json_user.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\json_wiki$O" : "$(OX)\json_wiki_.c" "$(OX)\json_wiki.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\json_wiki_.c" "$(OX)\json_wiki_.c" : "$(SRCDIR)\json_wiki.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\leaf$O" : "$(OX)\leaf_.c" "$(OX)\leaf.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\leaf_.c" "$(OX)\leaf_.c" : "$(SRCDIR)\leaf.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\loadctrl$O" : "$(OX)\loadctrl_.c" "$(OX)\loadctrl.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\loadctrl_.c" "$(OX)\loadctrl_.c" : "$(SRCDIR)\loadctrl.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\login$O" : "$(OX)\login_.c" "$(OX)\login.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\login_.c" "$(OX)\login_.c" : "$(SRCDIR)\login.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\lookslike$O" : "$(OX)\lookslike_.c" "$(OX)\lookslike.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\lookslike_.c" "$(OX)\lookslike_.c" : "$(SRCDIR)\lookslike.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\main$O" : "$(OX)\main_.c" "$(OX)\main.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\main_.c" "$(OX)\main_.c" : "$(SRCDIR)\main.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\manifest$O" : "$(OX)\manifest_.c" "$(OX)\manifest.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\manifest_.c" "$(OX)\manifest_.c" : "$(SRCDIR)\manifest.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\markdown$O" : "$(OX)\markdown_.c" "$(OX)\markdown.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_.c" "$(OX)\markdown_.c" : "$(SRCDIR)\markdown.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\markdown_html$O" : "$(OX)\markdown_html_.c" "$(OX)\markdown_html.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_html_.c" "$(OX)\markdown_html_.c" : "$(SRCDIR)\markdown_html.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\md5$O" : "$(OX)\md5_.c" "$(OX)\md5.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\md5_.c" "$(OX)\md5_.c" : "$(SRCDIR)\md5.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\merge$O" : "$(OX)\merge_.c" "$(OX)\merge.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\merge_.c" "$(OX)\merge_.c" : "$(SRCDIR)\merge.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\merge3$O" : "$(OX)\merge3_.c" "$(OX)\merge3.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\merge3_.c" "$(OX)\merge3_.c" : "$(SRCDIR)\merge3.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\moderate$O" : "$(OX)\moderate_.c" "$(OX)\moderate.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\moderate_.c" "$(OX)\moderate_.c" : "$(SRCDIR)\moderate.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\name$O" : "$(OX)\name_.c" "$(OX)\name.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\name_.c" "$(OX)\name_.c" : "$(SRCDIR)\name.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\patch$O" : "$(OX)\patch_.c" "$(OX)\patch.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\patch_.c" "$(OX)\patch_.c" : "$(SRCDIR)\patch.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\path$O" : "$(OX)\path_.c" "$(OX)\path.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\path_.c" "$(OX)\path_.c" : "$(SRCDIR)\path.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\piechart$O" : "$(OX)\piechart_.c" "$(OX)\piechart.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\piechart_.c" "$(OX)\piechart_.c" : "$(SRCDIR)\piechart.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\pikchrshow$O" : "$(OX)\pikchrshow_.c" "$(OX)\pikchrshow.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\pikchrshow_.c" "$(OX)\pikchrshow_.c" : "$(SRCDIR)\pikchrshow.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\pivot$O" : "$(OX)\pivot_.c" "$(OX)\pivot.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\pivot_.c" "$(OX)\pivot_.c" : "$(SRCDIR)\pivot.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\popen$O" : "$(OX)\popen_.c" "$(OX)\popen.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\popen_.c" "$(OX)\popen_.c" : "$(SRCDIR)\popen.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\pqueue$O" : "$(OX)\pqueue_.c" "$(OX)\pqueue.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\pqueue_.c" "$(OX)\pqueue_.c" : "$(SRCDIR)\pqueue.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\printf$O" : "$(OX)\printf_.c" "$(OX)\printf.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\printf_.c" "$(OX)\printf_.c" : "$(SRCDIR)\printf.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\publish$O" : "$(OX)\publish_.c" "$(OX)\publish.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\publish_.c" "$(OX)\publish_.c" : "$(SRCDIR)\publish.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\purge$O" : "$(OX)\purge_.c" "$(OX)\purge.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\purge_.c" "$(OX)\purge_.c" : "$(SRCDIR)\purge.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\rebuild$O" : "$(OX)\rebuild_.c" "$(OX)\rebuild.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rebuild_.c" "$(OX)\rebuild_.c" : "$(SRCDIR)\rebuild.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\regexp$O" : "$(OX)\regexp_.c" "$(OX)\regexp.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\regexp_.c" "$(OX)\regexp_.c" : "$(SRCDIR)\regexp.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\repolist$O" : "$(OX)\repolist_.c" "$(OX)\repolist.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\repolist_.c" "$(OX)\repolist_.c" : "$(SRCDIR)\repolist.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c" "$(OX)\report_.c" : "$(SRCDIR)\report.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c" "$(OX)\rss_.c" : "$(SRCDIR)\rss.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\schema$O" : "$(OX)\schema_.c" "$(OX)\schema.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\schema_.c" "$(OX)\schema_.c" : "$(SRCDIR)\schema.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\search$O" : "$(OX)\search_.c" "$(OX)\search.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\search_.c" "$(OX)\search_.c" : "$(SRCDIR)\search.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\security_audit$O" : "$(OX)\security_audit_.c" "$(OX)\security_audit.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\security_audit_.c" "$(OX)\security_audit_.c" : "$(SRCDIR)\security_audit.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\setup$O" : "$(OX)\setup_.c" "$(OX)\setup.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\setup_.c" "$(OX)\setup_.c" : "$(SRCDIR)\setup.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\setupuser$O" : "$(OX)\setupuser_.c" "$(OX)\setupuser.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\setupuser_.c" "$(OX)\setupuser_.c" : "$(SRCDIR)\setupuser.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\sha1$O" : "$(OX)\sha1_.c" "$(OX)\sha1.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sha1_.c" "$(OX)\sha1_.c" : "$(SRCDIR)\sha1.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\sha1hard$O" : "$(OX)\sha1hard_.c" "$(OX)\sha1hard.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sha1hard_.c" "$(OX)\sha1hard_.c" : "$(SRCDIR)\sha1hard.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\sha3$O" : "$(OX)\sha3_.c" "$(OX)\sha3.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sha3_.c" "$(OX)\sha3_.c" : "$(SRCDIR)\sha3.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\shun$O" : "$(OX)\shun_.c" "$(OX)\shun.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\shun_.c" "$(OX)\shun_.c" : "$(SRCDIR)\shun.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\sitemap$O" : "$(OX)\sitemap_.c" "$(OX)\sitemap.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sitemap_.c" "$(OX)\sitemap_.c" : "$(SRCDIR)\sitemap.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\skins$O" : "$(OX)\skins_.c" "$(OX)\skins.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\skins_.c" "$(OX)\skins_.c" : "$(SRCDIR)\skins.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\smtp$O" : "$(OX)\smtp_.c" "$(OX)\smtp.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\smtp_.c" "$(OX)\smtp_.c" : "$(SRCDIR)\smtp.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\sqlcmd$O" : "$(OX)\sqlcmd_.c" "$(OX)\sqlcmd.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sqlcmd_.c" "$(OX)\sqlcmd_.c" : "$(SRCDIR)\sqlcmd.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\stash$O" : "$(OX)\stash_.c" "$(OX)\stash.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\stash_.c" "$(OX)\stash_.c" : "$(SRCDIR)\stash.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\stat$O" : "$(OX)\stat_.c" "$(OX)\stat.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\stat_.c" "$(OX)\stat_.c" : "$(SRCDIR)\stat.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\statrep$O" : "$(OX)\statrep_.c" "$(OX)\statrep.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\statrep_.c" "$(OX)\statrep_.c" : "$(SRCDIR)\statrep.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\style$O" : "$(OX)\style_.c" "$(OX)\style.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\style_.c" "$(OX)\style_.c" : "$(SRCDIR)\style.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\sync$O" : "$(OX)\sync_.c" "$(OX)\sync.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\sync_.c" "$(OX)\sync_.c" : "$(SRCDIR)\sync.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\tag$O" : "$(OX)\tag_.c" "$(OX)\tag.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\tag_.c" "$(OX)\tag_.c" : "$(SRCDIR)\tag.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\tar$O" : "$(OX)\tar_.c" "$(OX)\tar.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\tar_.c" "$(OX)\tar_.c" : "$(SRCDIR)\tar.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\terminal$O" : "$(OX)\terminal_.c" "$(OX)\terminal.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\terminal_.c" "$(OX)\terminal_.c" : "$(SRCDIR)\terminal.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\th_main$O" : "$(OX)\th_main_.c" "$(OX)\th_main.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\th_main_.c" "$(OX)\th_main_.c" : "$(SRCDIR)\th_main.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\timeline$O" : "$(OX)\timeline_.c" "$(OX)\timeline.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\timeline_.c" "$(OX)\timeline_.c" : "$(SRCDIR)\timeline.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\tkt$O" : "$(OX)\tkt_.c" "$(OX)\tkt.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\tkt_.c" "$(OX)\tkt_.c" : "$(SRCDIR)\tkt.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\tktsetup$O" : "$(OX)\tktsetup_.c" "$(OX)\tktsetup.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\tktsetup_.c" "$(OX)\tktsetup_.c" : "$(SRCDIR)\tktsetup.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\undo$O" : "$(OX)\undo_.c" "$(OX)\undo.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\undo_.c" "$(OX)\undo_.c" : "$(SRCDIR)\undo.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\unicode$O" : "$(OX)\unicode_.c" "$(OX)\unicode.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\unicode_.c" "$(OX)\unicode_.c" : "$(SRCDIR)\unicode.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\unversioned$O" : "$(OX)\unversioned_.c" "$(OX)\unversioned.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\unversioned_.c" "$(OX)\unversioned_.c" : "$(SRCDIR)\unversioned.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\update$O" : "$(OX)\update_.c" "$(OX)\update.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\update_.c" "$(OX)\update_.c" : "$(SRCDIR)\update.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\url$O" : "$(OX)\url_.c" "$(OX)\url.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\url_.c" "$(OX)\url_.c" : "$(SRCDIR)\url.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\user$O" : "$(OX)\user_.c" "$(OX)\user.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\user_.c" "$(OX)\user_.c" : "$(SRCDIR)\user.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\utf8$O" : "$(OX)\utf8_.c" "$(OX)\utf8.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\utf8_.c" "$(OX)\utf8_.c" : "$(SRCDIR)\utf8.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\util$O" : "$(OX)\util_.c" "$(OX)\util.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\util_.c" "$(OX)\util_.c" : "$(SRCDIR)\util.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\verify$O" : "$(OX)\verify_.c" "$(OX)\verify.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\verify_.c" "$(OX)\verify_.c" : "$(SRCDIR)\verify.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\vfile$O" : "$(OX)\vfile_.c" "$(OX)\vfile.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\vfile_.c" "$(OX)\vfile_.c" : "$(SRCDIR)\vfile.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\wiki$O" : "$(OX)\wiki_.c" "$(OX)\wiki.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wiki_.c" "$(OX)\wiki_.c" : "$(SRCDIR)\wiki.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\wikiformat$O" : "$(OX)\wikiformat_.c" "$(OX)\wikiformat.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wikiformat_.c" "$(OX)\wikiformat_.c" : "$(SRCDIR)\wikiformat.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\winfile$O" : "$(OX)\winfile_.c" "$(OX)\winfile.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\winfile_.c" "$(OX)\winfile_.c" : "$(SRCDIR)\winfile.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\winhttp$O" : "$(OX)\winhttp_.c" "$(OX)\winhttp.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\winhttp_.c" "$(OX)\winhttp_.c" : "$(SRCDIR)\winhttp.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\xfer$O" : "$(OX)\xfer_.c" "$(OX)\xfer.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\xfer_.c" "$(OX)\xfer_.c" : "$(SRCDIR)\xfer.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\xfersetup$O" : "$(OX)\xfersetup_.c" "$(OX)\xfersetup.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\xfersetup_.c" "$(OX)\xfersetup_.c" : "$(SRCDIR)\xfersetup.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\zip$O" : "$(OX)\zip_.c" "$(OX)\zip.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\zip_.c" "$(OX)\zip_.c" : "$(SRCDIR)\zip.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\fossil.res" : "$(B)\win\fossil.rc" $(RCC) /fo $@ $** "$(OX)\headers": "$(OBJDIR)\makeheaders$E" "$(OX)\page_index.h" "$(OX)\builtin_data.h" "$(OX)\VERSION.h" "$(OBJDIR)\makeheaders$E" "$(OX)\add_.c":"$(OX)\add.h" \ "$(OX)\ajax_.c":"$(OX)\ajax.h" \ "$(OX)\alerts_.c":"$(OX)\alerts.h" \ "$(OX)\allrepo_.c":"$(OX)\allrepo.h" \ "$(OX)\attach_.c":"$(OX)\attach.h" \ "$(OX)\backlink_.c":"$(OX)\backlink.h" \ "$(OX)\backoffice_.c":"$(OX)\backoffice.h" \ "$(OX)\bag_.c":"$(OX)\bag.h" \ "$(OX)\bisect_.c":"$(OX)\bisect.h" \ "$(OX)\blob_.c":"$(OX)\blob.h" \ "$(OX)\branch_.c":"$(OX)\branch.h" \ "$(OX)\browse_.c":"$(OX)\browse.h" \ "$(OX)\builtin_.c":"$(OX)\builtin.h" \ "$(OX)\bundle_.c":"$(OX)\bundle.h" \ "$(OX)\cache_.c":"$(OX)\cache.h" \ "$(OX)\capabilities_.c":"$(OX)\capabilities.h" \ "$(OX)\captcha_.c":"$(OX)\captcha.h" \ "$(OX)\cgi_.c":"$(OX)\cgi.h" \ "$(OX)\chat_.c":"$(OX)\chat.h" \ "$(OX)\checkin_.c":"$(OX)\checkin.h" \ "$(OX)\checkout_.c":"$(OX)\checkout.h" \ "$(OX)\clearsign_.c":"$(OX)\clearsign.h" \ "$(OX)\clone_.c":"$(OX)\clone.h" \ "$(OX)\color_.c":"$(OX)\color.h" \ "$(OX)\comformat_.c":"$(OX)\comformat.h" \ "$(OX)\configure_.c":"$(OX)\configure.h" \ "$(OX)\content_.c":"$(OX)\content.h" \ "$(OX)\cookies_.c":"$(OX)\cookies.h" \ "$(OX)\db_.c":"$(OX)\db.h" \ "$(OX)\delta_.c":"$(OX)\delta.h" \ "$(OX)\deltacmd_.c":"$(OX)\deltacmd.h" \ "$(OX)\deltafunc_.c":"$(OX)\deltafunc.h" \ "$(OX)\descendants_.c":"$(OX)\descendants.h" \ "$(OX)\diff_.c":"$(OX)\diff.h" \ "$(OX)\diffcmd_.c":"$(OX)\diffcmd.h" \ "$(OX)\dispatch_.c":"$(OX)\dispatch.h" \ "$(OX)\doc_.c":"$(OX)\doc.h" \ "$(OX)\encode_.c":"$(OX)\encode.h" \ "$(OX)\etag_.c":"$(OX)\etag.h" \ "$(OX)\event_.c":"$(OX)\event.h" \ "$(OX)\export_.c":"$(OX)\export.h" \ "$(OX)\extcgi_.c":"$(OX)\extcgi.h" \ "$(OX)\file_.c":"$(OX)\file.h" \ "$(OX)\fileedit_.c":"$(OX)\fileedit.h" \ "$(OX)\finfo_.c":"$(OX)\finfo.h" \ "$(OX)\foci_.c":"$(OX)\foci.h" \ "$(OX)\forum_.c":"$(OX)\forum.h" \ "$(OX)\fshell_.c":"$(OX)\fshell.h" \ "$(OX)\fusefs_.c":"$(OX)\fusefs.h" \ "$(OX)\fuzz_.c":"$(OX)\fuzz.h" \ "$(OX)\glob_.c":"$(OX)\glob.h" \ "$(OX)\graph_.c":"$(OX)\graph.h" \ "$(OX)\gzip_.c":"$(OX)\gzip.h" \ "$(OX)\hname_.c":"$(OX)\hname.h" \ "$(OX)\hook_.c":"$(OX)\hook.h" \ "$(OX)\http_.c":"$(OX)\http.h" \ "$(OX)\http_socket_.c":"$(OX)\http_socket.h" \ "$(OX)\http_ssl_.c":"$(OX)\http_ssl.h" \ "$(OX)\http_transport_.c":"$(OX)\http_transport.h" \ "$(OX)\import_.c":"$(OX)\import.h" \ "$(OX)\info_.c":"$(OX)\info.h" \ "$(OX)\interwiki_.c":"$(OX)\interwiki.h" \ "$(OX)\json_.c":"$(OX)\json.h" \ "$(OX)\json_artifact_.c":"$(OX)\json_artifact.h" \ "$(OX)\json_branch_.c":"$(OX)\json_branch.h" \ "$(OX)\json_config_.c":"$(OX)\json_config.h" \ "$(OX)\json_diff_.c":"$(OX)\json_diff.h" \ "$(OX)\json_dir_.c":"$(OX)\json_dir.h" \ "$(OX)\json_finfo_.c":"$(OX)\json_finfo.h" \ "$(OX)\json_login_.c":"$(OX)\json_login.h" \ "$(OX)\json_query_.c":"$(OX)\json_query.h" \ "$(OX)\json_report_.c":"$(OX)\json_report.h" \ "$(OX)\json_status_.c":"$(OX)\json_status.h" \ "$(OX)\json_tag_.c":"$(OX)\json_tag.h" \ "$(OX)\json_timeline_.c":"$(OX)\json_timeline.h" \ "$(OX)\json_user_.c":"$(OX)\json_user.h" \ "$(OX)\json_wiki_.c":"$(OX)\json_wiki.h" \ "$(OX)\leaf_.c":"$(OX)\leaf.h" \ "$(OX)\loadctrl_.c":"$(OX)\loadctrl.h" \ "$(OX)\login_.c":"$(OX)\login.h" \ "$(OX)\lookslike_.c":"$(OX)\lookslike.h" \ "$(OX)\main_.c":"$(OX)\main.h" \ "$(OX)\manifest_.c":"$(OX)\manifest.h" \ "$(OX)\markdown_.c":"$(OX)\markdown.h" \ "$(OX)\markdown_html_.c":"$(OX)\markdown_html.h" \ "$(OX)\md5_.c":"$(OX)\md5.h" \ "$(OX)\merge_.c":"$(OX)\merge.h" \ "$(OX)\merge3_.c":"$(OX)\merge3.h" \ "$(OX)\moderate_.c":"$(OX)\moderate.h" \ "$(OX)\name_.c":"$(OX)\name.h" \ "$(OX)\patch_.c":"$(OX)\patch.h" \ "$(OX)\path_.c":"$(OX)\path.h" \ "$(OX)\piechart_.c":"$(OX)\piechart.h" \ "$(OX)\pikchrshow_.c":"$(OX)\pikchrshow.h" \ "$(OX)\pivot_.c":"$(OX)\pivot.h" \ "$(OX)\popen_.c":"$(OX)\popen.h" \ "$(OX)\pqueue_.c":"$(OX)\pqueue.h" \ "$(OX)\printf_.c":"$(OX)\printf.h" \ "$(OX)\publish_.c":"$(OX)\publish.h" \ "$(OX)\purge_.c":"$(OX)\purge.h" \ "$(OX)\rebuild_.c":"$(OX)\rebuild.h" \ "$(OX)\regexp_.c":"$(OX)\regexp.h" \ "$(OX)\repolist_.c":"$(OX)\repolist.h" \ "$(OX)\report_.c":"$(OX)\report.h" \ "$(OX)\rss_.c":"$(OX)\rss.h" \ "$(OX)\schema_.c":"$(OX)\schema.h" \ "$(OX)\search_.c":"$(OX)\search.h" \ "$(OX)\security_audit_.c":"$(OX)\security_audit.h" \ "$(OX)\setup_.c":"$(OX)\setup.h" \ "$(OX)\setupuser_.c":"$(OX)\setupuser.h" \ "$(OX)\sha1_.c":"$(OX)\sha1.h" \ "$(OX)\sha1hard_.c":"$(OX)\sha1hard.h" \ "$(OX)\sha3_.c":"$(OX)\sha3.h" \ "$(OX)\shun_.c":"$(OX)\shun.h" \ "$(OX)\sitemap_.c":"$(OX)\sitemap.h" \ "$(OX)\skins_.c":"$(OX)\skins.h" \ "$(OX)\smtp_.c":"$(OX)\smtp.h" \ "$(OX)\sqlcmd_.c":"$(OX)\sqlcmd.h" \ "$(OX)\stash_.c":"$(OX)\stash.h" \ "$(OX)\stat_.c":"$(OX)\stat.h" \ "$(OX)\statrep_.c":"$(OX)\statrep.h" \ "$(OX)\style_.c":"$(OX)\style.h" \ "$(OX)\sync_.c":"$(OX)\sync.h" \ "$(OX)\tag_.c":"$(OX)\tag.h" \ "$(OX)\tar_.c":"$(OX)\tar.h" \ "$(OX)\terminal_.c":"$(OX)\terminal.h" \ "$(OX)\th_main_.c":"$(OX)\th_main.h" \ "$(OX)\timeline_.c":"$(OX)\timeline.h" \ "$(OX)\tkt_.c":"$(OX)\tkt.h" \ "$(OX)\tktsetup_.c":"$(OX)\tktsetup.h" \ "$(OX)\undo_.c":"$(OX)\undo.h" \ "$(OX)\unicode_.c":"$(OX)\unicode.h" \ "$(OX)\unversioned_.c":"$(OX)\unversioned.h" \ "$(OX)\update_.c":"$(OX)\update.h" \ "$(OX)\url_.c":"$(OX)\url.h" \ "$(OX)\user_.c":"$(OX)\user.h" \ "$(OX)\utf8_.c":"$(OX)\utf8.h" \ "$(OX)\util_.c":"$(OX)\util.h" \ "$(OX)\verify_.c":"$(OX)\verify.h" \ "$(OX)\vfile_.c":"$(OX)\vfile.h" \ "$(OX)\wiki_.c":"$(OX)\wiki.h" \ "$(OX)\wikiformat_.c":"$(OX)\wikiformat.h" \ "$(OX)\winfile_.c":"$(OX)\winfile.h" \ "$(OX)\winhttp_.c":"$(OX)\winhttp.h" \ "$(OX)\xfer_.c":"$(OX)\xfer.h" \ "$(OX)\xfersetup_.c":"$(OX)\xfersetup.h" \ "$(OX)\zip_.c":"$(OX)\zip.h" \ "$(SRCDIR_extsrc)\pikchr.c":"$(OX)\pikchr.h" \ "$(SRCDIR_extsrc)\sqlite3.h" \ "$(SRCDIR)\th.h" \ "$(OX)\VERSION.h" \ "$(SRCDIR_extsrc)\cson_amalgamation.h" @copy /Y nul: $@ |
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 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 | @ECHO OFF :: :: buildmsvc.bat -- :: :: This batch file attempts to build Fossil using the latest version of :: Microsoft Visual Studio installed on this machine. :: :: For VS 2017 and later, it uses the x64 build tools by default; :: pass "x86" as the first argument to use the x86 tools. :: 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 set up. 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 2017 / 2019 / 2022 REM CALL :fn_TryUseVsWhereExe IF NOT DEFINED VSWHEREINSTALLDIR GOTO skip_detectVisualStudio2017 SET VSVARS32=%VSWHEREINSTALLDIR%\VC\Auxiliary\Build\vcvars64.bat IF "%~1" == "x86" ( SET VSVARS32=%VSWHEREINSTALLDIR%\VC\Auxiliary\Build\vcvars32.bat ) IF EXIST "%VSVARS32%" ( %_AECHO% Using Visual Studio 2017 / 2019 / 2022... GOTO skip_detectVisualStudio ) :skip_detectVisualStudio2017 REM REM Visual Studio 2015 REM IF NOT DEFINED VS140COMNTOOLS GOTO skip_detectVisualStudio2015 SET VSVARS32=%VS140COMNTOOLS%\vsvars32.bat IF EXIST "%VSVARS32%" ( %_AECHO% Using Visual Studio 2015... GOTO skip_detectVisualStudio ) :skip_detectVisualStudio2015 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 In order to build using the current directory as the build REM output directory, use the following command before executing REM this tool: REM REM SET BUILDDIR=%CD% REM IF DEFINED BUILDDIR ( IF DEFINED BUILDSUFFIX ( CALL :fn_FindVarInVar BUILDSUFFIX BUILDDIR IF ERRORLEVEL 1 ( REM REM NOTE: The build suffix is already present, do nothing. REM ) ELSE ( REM REM NOTE: The build suffix is not present, add it now. REM SET BUILDDIR=%BUILDDIR%%BUILDSUFFIX% ) CALL :fn_ResetErrorLevel ) ) ELSE ( REM REM NOTE: By default, when BUILDDIR is unset, build in the "msvcbld" REM sub-directory relative to the root of the source checkout. REM This retains backward compatibility with third-party build REM scripts, etc, REM SET BUILDDIR=%ROOT%\msvcbld%BUILDSUFFIX% ) %_VECHO% BuildSuffix = '%BUILDSUFFIX%' %_VECHO% BuildDir = '%BUILDDIR%' IF NOT EXIST "%BUILDDIR%" ( %__ECHO% MKDIR "%BUILDDIR%" IF ERRORLEVEL 1 ( ECHO Could not make directory "%BUILDDIR%". GOTO errors ) ) REM REM NOTE: Attempt to change to the created build output directory so that REM the generated files will be placed there, if needed. REM %__ECHO2% PUSHD "%BUILDDIR%" IF ERRORLEVEL 1 ( ECHO Could not change to directory "%BUILDDIR%". GOTO errors ) SET NEED_POPD=1 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% Tools = '%TOOLS%' %_VECHO% Root = '%ROOT%' %_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 Also, pass the base directory of the Fossil source tree as this REM allows an out-of-source-tree build. REM %__ECHO% nmake /f "%TOOLS%\Makefile.msc" B="%ROOT%" %NMAKE_ARGS% %* IF ERRORLEVEL 1 ( GOTO errors ) REM REM NOTE: Attempt to restore the previously saved directory, if needed. REM IF DEFINED NEED_POPD ( %__ECHO2% POPD IF ERRORLEVEL 1 ( ECHO Could not restore directory. GOTO errors ) CALL :fn_UnsetVariable NEED_POPD ) 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" GOTO set_v110Sdk71A_lib_x64 SET LIB=%PFILES_SDK71A%\Microsoft SDKs\Windows\7.1A\Lib;%LIB% GOTO set_v110Sdk71A_lib_done :set_v110Sdk71A_lib_x64 SET LIB=%PFILES_SDK71A%\Microsoft SDKs\Windows\7.1A\Lib\x64;%LIB% :set_v110Sdk71A_lib_done CALL :fn_UnsetVariable PFILES_SDK71A SET NMAKE_ARGS=%NMAKE_ARGS% FOSSIL_ENABLE_WINXP=1 GOTO :EOF :fn_FindVarInVar IF NOT DEFINED %1 GOTO :EOF IF NOT DEFINED %2 GOTO :EOF SETLOCAL CALL :fn_UnsetVariable VALUE SET __ECHO_CMD=ECHO %%%2%% ^^^| FIND /I "%%%1%%" FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO ( SET VALUE=%%V ) IF DEFINED VALUE ( CALL :fn_SetErrorLevel ) ELSE ( CALL :fn_ResetErrorLevel ) ENDLOCAL GOTO :EOF :fn_UnsetVariable SETLOCAL SET VALUE=%1 IF DEFINED VALUE ( SET VALUE= ENDLOCAL SET %VALUE%= ) ELSE ( ENDLOCAL ) CALL :fn_ResetErrorLevel GOTO :EOF :fn_ResetErrorLevel VERIFY > NUL GOTO :EOF :fn_SetErrorLevel VERIFY MAYBE 2> NUL GOTO :EOF :fn_TryUseVsWhereExe IF DEFINED VSWHERE_EXE GOTO skip_setVsWhereExe SET VSWHERE_EXE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe IF NOT EXIST "%VSWHERE_EXE%" SET VSWHERE_EXE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe :skip_setVsWhereExe IF NOT EXIST "%VSWHERE_EXE%" ( %_AECHO% The "VsWhere" tool does not appear to be installed. GOTO :EOF ) SET VS_WHEREIS_CMD="%VSWHERE_EXE%" -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -latest IF DEFINED __ECHO ( %__ECHO% %VS_WHEREIS_CMD% REM REM NOTE: This will not be executed, any reasonable fake path will work. REM SET VSWHEREINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2017\Community GOTO skip_setVsWhereInstallDir ) FOR /F "delims=" %%D IN ('%VS_WHEREIS_CMD%') DO (SET VSWHEREINSTALLDIR=%%D) :skip_setVsWhereInstallDir %_VECHO% VsWhereInstallDir = '%VSWHEREINSTALLDIR%' IF NOT DEFINED VSWHEREINSTALLDIR ( %_AECHO% Visual Studio 2017 / 2019 / 2022 is not installed. GOTO :EOF ) %_AECHO% Visual Studio 2017 / 2019 / 2022 is installed. 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="*" 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="*" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly> </dependency> </assembly> |
Added win/fossil.ico.
cannot compute difference between binary files
Added win/fossil.rc.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Copyright (c) 2012 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 resource information for the executable on Windows. */ #if !defined(_WIN32_WCE) #include "winresrc.h" #else #include "windows.h" #endif /* !defined(_WIN32_WCE) */ #if !defined(VS_FF_NONE) # 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" #endif /* defined(FOSSIL_ENABLE_TCL) */ #if defined(FOSSIL_ENABLE_JSON) #include "json_detail.h" #endif /* defined(FOSSIL_ENABLE_JSON) */ /* * English (U.S.) resources */ #if defined(_WIN32) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif /* defined(_WIN32) */ /* * Icon */ #define IDI_FOSSIL 8001 IDI_FOSSIL ICON "fossil.ico" /* * Version */ VS_VERSION_INFO VERSIONINFO FILEVERSION RELEASE_RESOURCE_VERSION PRODUCTVERSION RELEASE_RESOURCE_VERSION FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #if defined(_DEBUG) FILEFLAGS VS_FF_DEBUG #else FILEFLAGS VS_FF_NONE #endif /* defined(_DEBUG) */ FILEOS VOS__WINDOWS32 FILETYPE VFT_APP 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_DYNAMIC_BUILD) VALUE "DynamicBuild", "Yes\0" #else VALUE "DynamicBuild", "No\0" #endif VALUE "ZlibVersion", "zlib " ZLIB_VERSION "\0" #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) */ VALUE "LegacyMvRm", "Yes\0" #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) VALUE "ExecRelPaths", "Yes\0" #else VALUE "ExecRelPaths", "No\0" #endif /* defined(FOSSIL_ENABLE_EXEC_REL_PATHS) */ #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) */ #if defined(FOSSIL_ENABLE_TCL_STUBS) VALUE "TclStubsEnabled", "Yes\0" #else VALUE "TclStubsEnabled", "No\0" #endif /* defined(FOSSIL_ENABLE_TCL_STUBS) */ #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) VALUE "TclPrivateStubsEnabled", "Yes\0" #else VALUE "TclPrivateStubsEnabled", "No\0" #endif /* defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) */ #endif /* defined(FOSSIL_ENABLE_TCL) */ #if defined(FOSSIL_ENABLE_JSON) VALUE "JsonEnabled", "Yes, cson " FOSSIL_JSON_API_VERSION "\0" #endif /* defined(FOSSIL_ENABLE_JSON) */ #if defined(USE_MMAN_H) VALUE "UseMmanEnabled", "Yes\0" #else VALUE "UseMmanEnabled", "No\0" #endif /* defined(USE_MMAN_H) */ #if defined(USE_SEE) VALUE "UseSeeEnabled", "Yes\0" #else VALUE "UseSeeEnabled", "No\0" #endif /* defined(USE_SEE) */ VALUE "MarkdownEnabled", "Yes\0" #if defined(FOSSIL_DEBUG) VALUE "Debug", "Yes\0" #else VALUE "Debug", "No\0" #endif /* defined(FOSSIL_DEBUG) */ #if defined(FOSSIL_OMIT_DELTA_CKSUM_TEST) VALUE "OmitDeltaCksumTest", "Yes\0" #else VALUE "OmitDeltaCksumTest", "No\0" #endif /* defined(FOSSIL_OMIT_DELTA_CKSUM_TEST) */ #if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES) VALUE "AllowOutOfOrderDates", "Yes\0" #else VALUE "AllowOutOfOrderDates", "No\0" #endif /* defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES) */ 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" |
Added win/include/dirent.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* * Dirent interface for Microsoft Visual Studio * Version 1.23.1 * * Copyright (C) 2006-2012 Toni Ronkko * This file is part of dirent. Dirent may be freely distributed * under the MIT license. For all details and documentation, see * https://github.com/tronkko/dirent */ #ifndef DIRENT_H #define DIRENT_H /* * Include windows.h without Windows Sockets 1.1 to prevent conflicts with * Windows Sockets 2.0. */ #ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif #include <windows.h> #include <stdio.h> #include <stdarg.h> #include <wchar.h> #include <string.h> #include <stdlib.h> #include <malloc.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> /* Indicates that d_type field is available in dirent structure */ #define _DIRENT_HAVE_D_TYPE /* Indicates that d_namlen field is available in dirent structure */ #define _DIRENT_HAVE_D_NAMLEN /* Entries missing from MSVC 6.0 */ #if !defined(FILE_ATTRIBUTE_DEVICE) # define FILE_ATTRIBUTE_DEVICE 0x40 #endif /* File type and permission flags for stat(), general mask */ #if !defined(S_IFMT) # define S_IFMT _S_IFMT #endif /* Directory bit */ #if !defined(S_IFDIR) # define S_IFDIR _S_IFDIR #endif /* Character device bit */ #if !defined(S_IFCHR) # define S_IFCHR _S_IFCHR #endif /* Pipe bit */ #if !defined(S_IFFIFO) # define S_IFFIFO _S_IFFIFO #endif /* Regular file bit */ #if !defined(S_IFREG) # define S_IFREG _S_IFREG #endif /* Read permission */ #if !defined(S_IREAD) # define S_IREAD _S_IREAD #endif /* Write permission */ #if !defined(S_IWRITE) # define S_IWRITE _S_IWRITE #endif /* Execute permission */ #if !defined(S_IEXEC) # define S_IEXEC _S_IEXEC #endif /* Pipe */ #if !defined(S_IFIFO) # define S_IFIFO _S_IFIFO #endif /* Block device */ #if !defined(S_IFBLK) # define S_IFBLK 0 #endif /* Link */ #if !defined(S_IFLNK) # define S_IFLNK 0 #endif /* Socket */ #if !defined(S_IFSOCK) # define S_IFSOCK 0 #endif /* Read user permission */ #if !defined(S_IRUSR) # define S_IRUSR S_IREAD #endif /* Write user permission */ #if !defined(S_IWUSR) # define S_IWUSR S_IWRITE #endif /* Execute user permission */ #if !defined(S_IXUSR) # define S_IXUSR 0 #endif /* Read group permission */ #if !defined(S_IRGRP) # define S_IRGRP 0 #endif /* Write group permission */ #if !defined(S_IWGRP) # define S_IWGRP 0 #endif /* Execute group permission */ #if !defined(S_IXGRP) # define S_IXGRP 0 #endif /* Read others permission */ #if !defined(S_IROTH) # define S_IROTH 0 #endif /* Write others permission */ #if !defined(S_IWOTH) # define S_IWOTH 0 #endif /* Execute others permission */ #if !defined(S_IXOTH) # define S_IXOTH 0 #endif /* Maximum length of file name */ #if !defined(PATH_MAX) # define PATH_MAX MAX_PATH #endif #if !defined(FILENAME_MAX) # define FILENAME_MAX MAX_PATH #endif #if !defined(NAME_MAX) # define NAME_MAX FILENAME_MAX #endif /* File type flags for d_type */ #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 * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are * only defined for compatibility. These macros should always return false * on Windows. */ #if !defined(S_ISFIFO) # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) #endif #if !defined(S_ISDIR) # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif #if !defined(S_ISREG) # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif #if !defined(S_ISLNK) # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #endif #if !defined(S_ISSOCK) # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) #endif #if !defined(S_ISCHR) # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #endif #if !defined(S_ISBLK) # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) #endif /* Return the exact length of the file name without zero terminator */ #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) /* Return the maximum size of a file name */ #define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) #ifdef __cplusplus extern "C" { #endif /* Wide-character version */ struct _wdirent { /* Always zero */ long d_ino; /* File position within stream */ long d_off; /* Structure size */ unsigned short d_reclen; /* Length of name without \0 */ size_t d_namlen; /* File type */ int d_type; /* File name */ wchar_t d_name[PATH_MAX+1]; }; typedef struct _wdirent _wdirent; struct _WDIR { /* Current directory entry */ struct _wdirent ent; /* Private file data */ WIN32_FIND_DATAW data; /* True if data is valid */ int cached; /* Win32 search handle */ HANDLE handle; /* Initial directory name */ wchar_t *patt; }; typedef struct _WDIR _WDIR; /* Multi-byte character version */ struct dirent { /* Always zero */ long d_ino; /* File position within stream */ long d_off; /* Structure size */ unsigned short d_reclen; /* Length of name without \0 */ size_t d_namlen; /* File type */ int d_type; /* File name */ char d_name[PATH_MAX+1]; }; typedef struct dirent dirent; struct DIR { struct dirent ent; struct _WDIR *wdirp; }; typedef struct DIR DIR; /* Dirent functions */ static DIR *opendir (const char *dirname); static _WDIR *_wopendir (const wchar_t *dirname); static struct dirent *readdir (DIR *dirp); static struct _wdirent *_wreaddir (_WDIR *dirp); static int readdir_r( DIR *dirp, struct dirent *entry, struct dirent **result); static int _wreaddir_r( _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); static int closedir (DIR *dirp); static int _wclosedir (_WDIR *dirp); static void rewinddir (DIR* dirp); static void _wrewinddir (_WDIR* dirp); static int scandir (const char *dirname, struct dirent ***namelist, int (*filter)(const struct dirent*), int (*compare)(const struct dirent**, const struct dirent**)); static int alphasort (const struct dirent **a, const struct dirent **b); static int versionsort (const struct dirent **a, const struct dirent **b); /* For compatibility with Symbian */ #define wdirent _wdirent #define WDIR _WDIR #define wopendir _wopendir #define wreaddir _wreaddir #define wclosedir _wclosedir #define wrewinddir _wrewinddir /* Internal utility functions */ static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count); static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, const wchar_t *wcstr, size_t count); static void dirent_set_errno (int error); /* * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory * entries. */ static _WDIR* _wopendir( const wchar_t *dirname) { _WDIR *dirp = NULL; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno (ENOENT); return NULL; } /* Allocate new _WDIR structure */ dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); if (dirp != NULL) { DWORD n; /* Reset _WDIR structure */ dirp->handle = INVALID_HANDLE_VALUE; dirp->patt = NULL; dirp->cached = 0; /* Compute the length of full path plus zero terminator * * Note that on WinRT there's no way to convert relative paths * into absolute paths, so just assume it is an absolute path. */ # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) n = wcslen(dirname); # else n = GetFullPathNameW (dirname, 0, NULL, NULL); # endif /* Allocate room for absolute directory name and search pattern */ dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); if (dirp->patt) { /* * Convert relative directory name to an absolute one. This * allows rewinddir() to function correctly even when current * working directory is changed between opendir() and rewinddir(). * * Note that on WinRT there's no way to convert relative paths * into absolute paths, so just assume it is an absolute path. */ # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) wcsncpy_s(dirp->patt, n+1, dirname, n); # else n = GetFullPathNameW (dirname, n, dirp->patt, NULL); # endif if (n > 0) { wchar_t *p; /* Append search pattern \* to the directory name */ p = dirp->patt + n; if (dirp->patt < p) { switch (p[-1]) { case '\\': case '/': case ':': /* Directory ends in path separator, e.g. c:\temp\ */ /*NOP*/; break; default: /* Directory name doesn't end in path separator */ *p++ = '\\'; } } *p++ = '*'; *p = '\0'; /* Open directory stream and retrieve the first entry */ if (dirent_first (dirp)) { /* Directory stream opened successfully */ error = 0; } else { /* Cannot retrieve first entry */ error = 1; dirent_set_errno (ENOENT); } } else { /* Cannot retrieve full path name */ dirent_set_errno (ENOENT); error = 1; } } else { /* Cannot allocate memory for search pattern */ error = 1; } } else { /* Cannot allocate _WDIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { _wclosedir (dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. * * Returns pointer to static directory entry which may be overwritten by * subsequent calls to _wreaddir(). */ static struct _wdirent* _wreaddir( _WDIR *dirp) { struct _wdirent *entry; /* * Read directory entry to buffer. We can safely ignore the return value * as entry will be set to NULL in case of error. */ (void) _wreaddir_r (dirp, &dirp->ent, &entry); /* Return pointer to statically allocated directory entry */ return entry; } /* * Read next directory entry. * * Returns zero on success. If end of directory stream is reached, then sets * result to NULL and returns zero. */ static int _wreaddir_r( _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result) { WIN32_FIND_DATAW *datap; /* Read next directory entry */ datap = dirent_next (dirp); if (datap) { size_t n; DWORD attr; /* * 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 < PATH_MAX && datap->cFileName[n] != 0) { entry->d_name[n] = datap->cFileName[n]; n++; } entry->d_name[n] = 0; /* Length of file name excluding zero terminator */ entry->d_namlen = n; /* File type */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entry->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entry->d_type = DT_DIR; } else { entry->d_type = DT_REG; } /* Reset dummy fields */ entry->d_ino = 0; entry->d_off = 0; entry->d_reclen = sizeof (struct _wdirent); /* Set result address */ *result = entry; } else { /* Return NULL to indicate end of directory */ *result = NULL; } return /*OK*/0; } /* * Close directory stream opened by opendir() function. This invalidates the * DIR structure as well as any directory entry read previously by * _wreaddir(). */ static int _wclosedir( _WDIR *dirp) { int ok; if (dirp) { /* Release search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; } /* Release search pattern */ if (dirp->patt) { free (dirp->patt); dirp->patt = NULL; } /* Release directory structure */ free (dirp); ok = /*success*/0; } else { /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream such that _wreaddir() returns the very first * file name again. */ static void _wrewinddir( _WDIR* dirp) { if (dirp) { /* Release existing search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); } /* Open new search handle */ dirent_first (dirp); } } /* Get first directory entry (internal) */ static WIN32_FIND_DATAW* dirent_first( _WDIR *dirp) { WIN32_FIND_DATAW *datap; /* Open directory and retrieve the first entry */ dirp->handle = FindFirstFileExW( dirp->patt, FindExInfoStandard, &dirp->data, FindExSearchNameMatch, NULL, 0); if (dirp->handle != INVALID_HANDLE_VALUE) { /* a directory entry is now waiting in memory */ datap = &dirp->data; dirp->cached = 1; } else { /* Failed to re-open directory: no directory entry in memory */ dirp->cached = 0; datap = NULL; } return datap; } /* * Get next directory entry (internal). * * Returns */ static WIN32_FIND_DATAW* dirent_next( _WDIR *dirp) { WIN32_FIND_DATAW *p; /* Get next directory entry */ if (dirp->cached != 0) { /* A valid directory entry already in memory */ p = &dirp->data; dirp->cached = 0; } else if (dirp->handle != INVALID_HANDLE_VALUE) { /* Get the next directory entry from stream */ if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { /* Got a file */ p = &dirp->data; } else { /* The very last entry has been processed or an error occurred */ FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; p = NULL; } } else { /* End of directory stream reached */ p = NULL; } return p; } /* * Open directory stream using plain old C-string. */ static DIR* opendir( const char *dirname) { struct DIR *dirp; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno (ENOENT); return NULL; } /* Allocate memory for DIR structure */ dirp = (DIR*) malloc (sizeof (struct DIR)); if (dirp) { wchar_t wname[PATH_MAX + 1]; size_t n; /* Convert directory name to wide-character string */ error = dirent_mbstowcs_s( &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); if (!error) { /* Open directory stream using wide-character name */ dirp->wdirp = _wopendir (wname); if (dirp->wdirp) { /* Directory stream opened */ error = 0; } else { /* Failed to open directory stream */ error = 1; } } else { /* * Cannot convert file name to wide-character string. This * occurs if the string contains invalid multi-byte sequences or * the output buffer is too small to contain the resulting * string. */ error = 1; } } else { /* Cannot allocate DIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { free (dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. */ static struct dirent* readdir( DIR *dirp) { struct dirent *entry; /* * Read directory entry to buffer. We can safely ignore the return value * as entry will be set to NULL in case of error. */ (void) readdir_r (dirp, &dirp->ent, &entry); /* Return pointer to statically allocated directory entry */ return entry; } /* * Read next directory entry into called-allocated buffer. * * Returns zero on success. If the end of directory stream is reached, then * sets result to NULL and returns zero. */ static int readdir_r( DIR *dirp, struct dirent *entry, struct dirent **result) { WIN32_FIND_DATAW *datap; /* Read next directory entry */ 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, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); /* * 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, entry->d_name, PATH_MAX + 1, datap->cAlternateFileName, PATH_MAX + 1); } if (!error) { DWORD attr; /* Length of file name excluding zero terminator */ entry->d_namlen = n - 1; /* File attributes */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entry->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entry->d_type = DT_DIR; } else { entry->d_type = DT_REG; } /* Reset dummy fields */ entry->d_ino = 0; entry->d_off = 0; entry->d_reclen = sizeof (struct dirent); } else { /* * Cannot convert file name to multi-byte string so construct * an erroneous directory entry and return that. Note that * we cannot return NULL as that would stop the processing * of directory entries completely. */ entry->d_name[0] = '?'; entry->d_name[1] = '\0'; entry->d_namlen = 1; entry->d_type = DT_UNKNOWN; entry->d_ino = 0; entry->d_off = -1; entry->d_reclen = 0; } /* Return pointer to directory entry */ *result = entry; } else { /* No more directory entries */ *result = NULL; } return /*OK*/0; } /* * Close directory stream. */ static int closedir( DIR *dirp) { int ok; if (dirp) { /* Close wide-character directory stream */ ok = _wclosedir (dirp->wdirp); dirp->wdirp = NULL; /* Release multi-byte character version */ free (dirp); } else { /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream to beginning. */ static void rewinddir( DIR* dirp) { /* Rewind wide-character string directory stream */ _wrewinddir (dirp->wdirp); } /* * Scan directory for entries. */ static int scandir( const char *dirname, struct dirent ***namelist, int (*filter)(const struct dirent*), int (*compare)(const struct dirent**, const struct dirent**)) { struct dirent **files = NULL; size_t size = 0; size_t allocated = 0; const size_t init_size = 1; DIR *dir = NULL; struct dirent *entry; struct dirent *tmp = NULL; size_t i; int result = 0; /* Open directory stream */ dir = opendir (dirname); if (dir) { /* Read directory entries to memory */ while (1) { /* Enlarge pointer table to make room for another pointer */ if (size >= allocated) { void *p; size_t num_entries; /* Compute number of entries in the enlarged pointer table */ if (size < init_size) { /* Allocate initial pointer table */ num_entries = init_size; } else { /* Double the size */ num_entries = size * 2; } /* Allocate first pointer table or enlarge existing table */ p = realloc (files, sizeof (void*) * num_entries); if (p != NULL) { /* Got the memory */ files = (dirent**) p; allocated = num_entries; } else { /* Out of memory */ result = -1; break; } } /* Allocate room for temporary directory entry */ if (tmp == NULL) { tmp = (struct dirent*) malloc (sizeof (struct dirent)); if (tmp == NULL) { /* Cannot allocate temporary directory entry */ result = -1; break; } } /* Read directory entry to temporary area */ if (readdir_r (dir, tmp, &entry) == /*OK*/0) { /* Did we get an entry? */ if (entry != NULL) { int pass; /* Determine whether to include the entry in result */ if (filter) { /* Let the filter function decide */ pass = filter (tmp); } else { /* No filter function, include everything */ pass = 1; } if (pass) { /* Store the temporary entry to pointer table */ files[size++] = tmp; tmp = NULL; /* Keep up with the number of files */ result++; } } else { /* * End of directory stream reached => sort entries and * exit. */ qsort (files, size, sizeof (void*), (int (*) (const void*, const void*)) compare); break; } } else { /* Error reading directory entry */ result = /*Error*/ -1; break; } } } else { /* Cannot open directory */ result = /*Error*/ -1; } /* Release temporary directory entry */ if (tmp) { free (tmp); } /* Release allocated memory on error */ if (result < 0) { for (i = 0; i < size; i++) { free (files[i]); } free (files); files = NULL; } /* Close directory stream */ if (dir) { closedir (dir); } /* Pass pointer table to caller */ if (namelist) { *namelist = files; } return result; } /* Alphabetical sorting */ static int alphasort( const struct dirent **a, const struct dirent **b) { return strcoll ((*a)->d_name, (*b)->d_name); } /* Sort versions */ static int versionsort( const struct dirent **a, const struct dirent **b) { /* FIXME: implement strverscmp and use that */ return alphasort (a, b); } /* Convert multi-byte string to wide character string */ static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count) { int error; #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ 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 resulting multi-byte string WITH zero terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; } else { /* Could not convert string */ error = 1; } #endif return error; } /* 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'; } /* Length of resulting multi-bytes string WITH zero-terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; } else { /* Cannot convert string */ error = 1; } #endif return error; } /* 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 */ errno = error; #endif } #ifdef __cplusplus } #endif #endif /*DIRENT_H*/ |
Added win/include/unistd.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #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__) #endif #if defined(_WIN32) #define _CRT_SECURE_NO_WARNINGS 1 #ifndef F_OK #define F_OK 0 #endif /* not F_OK */ #ifndef X_OK #define X_OK 1 #endif /* not X_OK */ #ifndef W_OK #define W_OK 2 #endif /* not W_OK */ #ifndef R_OK #define R_OK 4 #endif /* not R_OK */ #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif #define access _access #define ftruncate _chsize #define ssize_t int #endif /* unistd.h */ |
Deleted www/CollRev1.gif.
cannot compute difference between binary files
Deleted www/CollRev2.gif.
cannot compute difference between binary files
Deleted www/CollRev3.gif.
cannot compute difference between binary files
Deleted www/CollRev4.gif.
cannot compute difference between binary files
Added www/aboutcgi.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 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 | <title>How CGI Works In Fossil</title> <h2>Introduction</h2> CGI or "Common Gateway Interface" is a venerable yet reliable technique for generating dynamic web content. This article gives a quick background on how CGI works and describes how Fossil can act as a CGI service. This is a "how it works" guide. This document provides background information on the CGI protocol so that you can better understand what is going on behind the scenes. If you just want to set up Fossil as a CGI server, see the [./server/ | Fossil Server Setup] page. Or if you want to development CGI-based extensions to Fossil, see the [./serverext.wiki|CGI Server Extensions] page. <h2>A Quick Review Of CGI</h2> An HTTP request is a block of text that is sent by a client application (usually a web browser) and arrives at the web server over a network connection. The HTTP request contains a URL that describes the information being requested. The URL in the HTTP request is typically the same URL that appears in the URL bar at the top of the web browser that is making the request. The URL might contain a "?" character followed query parameters. The HTTP will usually also contain other information such as the name of the application that made the request, whether or not the requesting application can accept a compressed reply, POST parameters from forms, and so forth. The job of the web server is to interpret the HTTP request and formulate an appropriate reply. The web server is free to interpret the HTTP request in any way it wants. But most web servers follow a similar pattern, described below. (Note: details may vary from one web server to another.) Suppose the filename component of the URL in the HTTP request looks like this: <pre>/one/two/timeline/four</pre> Most web servers will search their content area for files that match some prefix of the URL. The search starts with <b>/one</b>, then goes to <b>/one/two</b>, then <b>/one/two/timeline</b>, and finally <b>/one/two/timeline/four</b> is checked. The search stops at the first match. Suppose the first match is <b>/one/two</b>. If <b>/one/two</b> is an ordinary file in the content area, then that file is returned as static content. The "<b>/timeline/four</b>" suffix is silently ignored. If <b>/one/two</b> is a CGI script (or program), then the web server executes the <b>/one/two</b> script. The output generated by the script is collected and repackaged as the HTTP reply. Before executing the CGI script, the web server will set up various environment variables with information useful to the CGI script: <table> <tr><th>Variable<th>Meaning <tr><td>GATEWAY_INTERFACE<td>Always set to "CGI/1.0" <tr><td>REQUEST_URI <td>The input URL from the HTTP request. <tr><td>SCRIPT_NAME <td>The prefix of the input URL that matches the CGI script name. In this example: "/one/two". <tr><td>PATH_INFO <td>The suffix of the URL beyond the name of the CGI script. In this example: "timeline/four". <tr><td>QUERY_STRING <td>The query string that follows the "?" in the URL, if there is one. </table> There are other CGI environment variables beyond those listed above. Many Fossil servers implement the [https://fossil-scm.org/home/test_env/two/three?abc=xyz|test_env] webpage that shows some of the CGI environment variables that Fossil pays attention to. In addition to setting various CGI environment variables, if the HTTP request contains POST content, then the web server relays the POST content to standard input of the CGI script. In summary, the task of the CGI script is to read the various CGI environment variables and the POST content on standard input (if any), figure out an appropriate reply, then write that reply on standard output. The web server will read the output from the CGI script, reformat it into an appropriate HTTP reply, and relay the result back to the requesting application. The CGI script exits as soon as it generates a single reply. The web server will (usually) persist and handle multiple HTTP requests, but a CGI script handles just one HTTP request and then exits. The above is a rough outline of how CGI works. There are many details omitted from this brief discussion. See other on-line CGI tutorials for further information. <h2>How Fossil Acts As A CGI Program</h2> An appropriate CGI script for running Fossil will look something like the following: <pre> #!/usr/bin/fossil repository: /home/www/repos/project.fossil </pre> The first line of the script is a "[https://en.wikipedia.org/wiki/Shebang_%28Unix%29|shebang]" that tells the operating system what program to use as the interpreter for this script. On unix, when you execute a script that starts with a shebang, the operating system runs the program identified by the shebang with a single argument that is the full pathname of the script itself. In our example, the interpreter is Fossil, and the argument might be something like "/var/www/cgi-bin/one/two" (depending on how your particular web server is configured). The Fossil program that is run as the script interpreter is the same Fossil that runs when you type ordinary Fossil commands like "fossil sync" or "fossil commit". But in this case, as soon as it launches, the Fossil program recognizes that the GATEWAY_INTERFACE environment variable is set to "CGI/1.0" and it therefore knows that it is being used as CGI rather than as an ordinary command-line tool, and behaves accordingly. When Fossil recognizes that it is being run as CGI, it opens and reads the file identified by its sole argument (the file named by <code>argv[1]</code>). In our example, the second line of that file tells Fossil the location of the repository it will be serving. Fossil then starts looking at the CGI environment variables to figure out what web page is being requested, generates that one web page, then exits. Usually, the webpage being requested is the first term of the PATH_INFO environment variable. (Exceptions to this rule are noted in the sequel.) For our example, the first term of PATH_INFO is "timeline", which means that Fossil will generate the [/help?cmd=/timeline|/timeline] webpage. With Fossil, terms of PATH_INFO beyond the webpage name are converted into the "name" query parameter. Hence, the following two URLs mean exactly the same thing to Fossil: <ol type='A'> <li> [https://fossil-scm.org/home/info/c14ecc43] <li> [https://fossil-scm.org/home/info?name=c14ecc43] </ol> In both cases, the CGI script is called "/fossil". For case (A), the PATH_INFO variable will be "info/c14ecc43" and so the "[/help?cmd=/info|/info]" webpage will be generated and the suffix of PATH_INFO will be converted into the "name" query parameter, which identifies the artifact about which information is requested. In case (B), the PATH_INFO is just "info", but the same "name" query parameter is set explicitly by the URL itself. <h2>Serving Multiple Fossil Repositories From One CGI Script</h2> The previous example showed how to serve a single Fossil repository using a single CGI script. On a website that wants to serve multiple repositories, one could simply create multiple CGI scripts, one script for each repository. But it is also possible to serve multiple Fossil repositories from a single CGI script. If the CGI script for Fossil contains a "directory:" line instead of a "repository:" line, then the argument to "directory:" is the name of a directory that contains multiple repository files, each ending with ".fossil". For example: <pre> #!/usr/bin/fossil directory: /home/www/repos </pre> Suppose the /home/www/repos directory contains files named <b>one.fossil</b>, <b>two.fossil</b>, and <b>subdir/three.fossil</b>. Further suppose that the name of the CGI script (relative to the root of the webserver document area) is "cgis/example2". Then to see the timeline for the "three.fossil" repository, the URL would be: <pre> http://example.com/cgis/example2/subdir/three/timeline </pre> Here is what happens: <ol> <li> The input URI on the HTTP request is <b>/cgis/example2/subdir/three/timeline</b> <li> The web server searches prefixes of the input URI until it finds the "cgis/example2" script. The web server then sets PATH_INFO to the "subdir/three/timeline" suffix and invokes the "cgis/example2" script. <li> Fossil runs and sees the "directory:" line pointing to "/home/www/repos". Fossil then starts pulling terms off the front of the PATH_INFO looking for a repository. It first looks at "/home/www/resps/subdir.fossil" but there is no such repository. So then it looks at "/home/www/repos/subdir/three.fossil" and finds a repository. The PATH_INFO is shortened by removing "subdir/three/" leaving it at just "timeline". <li> Fossil looks at the rest of PATH_INFO to see that the webpage requested is "timeline". </ol> <a id="cgivar"></a> The web server sets many environment variables in step 2 in addition to just PATH_INFO. The following diagram shows a few of these variables and their relationship to the request URL: <verbatim type="pikchr"> charwid = 0.075 thickness = 0 SCHEME: box "https://" mono fit DOMAIN: box "example.com" mono fit SCRIPT: box "/cgis/example2" mono fit PATH: box "/subdir/three/timeline" mono fit QUERY: box "?c=55d7e1" mono fit thickness = 0.01 DB: box at 0.3 below DOMAIN "HTTP_HOST" mono fit invis SB: box at 0.3 below SCRIPT "SCRIPT_NAME" mono fit invis PB: box at 0.3 below PATH "PATH_INFO" mono fit invis QB: box at 0.3 below QUERY "QUERY_STRING" mono fit invis RB: box at 0.5 above PATH "REQUEST_URI" mono fit invis color = lightgray box at SCHEME width SCHEME.width height SCHEME.height line fill 0x7799CC behind QUERY \ from SCRIPT.nw \ to RB.sw \ to RB.se \ to QUERY.ne \ close line fill 0x99CCFF behind DOMAIN \ from DOMAIN.nw \ to DOMAIN.sw \ to DB.n \ to DOMAIN.se \ to DOMAIN.ne \ close line fill 0xCCEEFF behind SCRIPT \ from SCRIPT.nw \ to SCRIPT.sw \ to SB.n \ to SCRIPT.se \ to SCRIPT.ne \ close line fill 0x99CCFF behind PATH \ from PATH.nw \ to PATH.sw \ to PB.n \ to PATH.se \ to PATH.ne \ close line fill 0xCCEEFF behind QUERY \ from QUERY.nw \ to QUERY.sw \ to QB.n \ to QUERY.se \ to QUERY.ne \ close </verbatim> <h2>Additional CGI Script Options</h2> The CGI script can have additional options used to fine-tune Fossil's behavior. See the [./cgi.wiki|CGI script documentation] for details. <h2>Additional Observations</h2> <ol type="I"> <li><p> Fossil does not distinguish between the various HTTP methods (GET, PUT, DELETE, etc). Fossil figures out what it needs to do purely from the webpage term of the URI.</p></li> <li><p> Fossil does not distinguish between query parameters that are part of the URI, application/x-www-form-urlencoded or multipart/form-data encoded parameter that are part of the POST content, and cookies. Each information source is seen as a space of key/value pairs which are loaded into an internal property hash table. The code that runs to generate the reply can then reference various properties values. Fossil does not care where the value of each property comes from (POST content, cookies, or query parameters) only that the property exists and has a value.</p></li> <li><p> The "[/help?cmd=ui|fossil ui]" and "[/help?cmd=server|fossil server]" commands are implemented using a simple built-in web server that accepts incoming HTTP requests, translates each request into a CGI invocation, then creates a separate child Fossil process to handle each request. In other words, CGI is used internally to implement "fossil ui/server". <br><br> SCGI is processed using the same built-in web server, just modified to parse SCGI requests instead of HTTP requests. Each SCGI request is converted into CGI, then Fossil creates a separate child Fossil process to handle each CGI request.</p></li> <li><p> Fossil is itself often launched using CGI. But Fossil can also then turn around and launch [./serverext.wiki|sub-CGI scripts to implement extensions].</p></li> </ol> |
Added www/aboutdownload.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 100 101 | <title>How The Fossil Download Page Works</title> <h2>1.0 Overview</h2> The [/uv/download.html|Download] page for the Fossil self-hosting repository is implemented using [./unvers.wiki|unversioned files]. The "download.html" screen itself, and the various build products are all stored as unversioned content. The download.html page uses XMLHttpRequest() to retrieve the [/help?cmd=/juvlist|/juvlist] webpage for a list of all unversioned files. Javascript in the [/uv/download.js?mimetype=text/plain|download.js] file (which is sourced by "download.html") then figures out which unversioned files are build products and paints appropriate icons on the displayed download page. Except, the "Source Tarball" download products are not stored as unversioned files. They are computed on-demand by the [/help?cmd=/tarball|/tarball web page]. When a new version is generated, the developers use the [/help?cmd=uv|fossil uv edit] command to make minor changes to the "[/uv/download.js?mimetype=text/plain|download.js]" file so that it knows about the new version number. Then the developers run the [/help?cmd=uv|fossil uv add] command for each build product. Finally, the [/help?cmd=uv|fossil uv sync] command is run to push all the content up to servers. All [./selfhost.wiki|three self-hosting repositories] for Fossil are updated automatically. <h2>2.0 Details</h2> The current text of the "download.html" and "download.js" files can be seen at: * [/uv/download.html?mimetype=text/plain] * [/uv/download.js?mimetype=text/plain] Notice how the hyperlinks above use the "mimetype=text/plain" query parameter in order to display the file as plain text instead of the usual HTML or Javascript. The default mimetype for "download.html" is text/html. But because the entire page is enclosed within <verbatim><div class='fossil-doc' data-title='Download Page'>...</div></verbatim> Fossil knows to add its standard header and footer information to the document, making it look just like any other page. See "[./embeddeddoc.wiki|embedded documentation]" for further details on how this <div class='fossil-doc'> markup works. With each new release, the "releases" variable in the javascript on the [/uv/download.js?mimetype=text/plain|download.js] page is edited (using "[/help?cmd=uv|fossil uv edit download.js]") to add details of the release. When the JavaScript in the "download.js" file runs, it requests a listing of all unversioned content using the /juvlist URL. ([/juvlist|sample /juvlist output]). The content of the download page is constructed by matching unversioned files against regular expressions in the "releases" variable. Build products need to be constructed on different machines. The precompiled binary for Linux is compiled on Linux, the precompiled binary for Windows is compiled on Windows11, and so forth. After a new release is tagged, the release manager goes around to each of the target platforms, checks out the release and compiles it, then runs [/help?cmd=uv|fossil uv add] for the build product followed by [/help?cmd=uv|fossil uv sync] to push the new build product to the [./selfhost.wiki|various servers]. This process is repeated for each build product. When older builds are retired from the download page, the [/uv/download.js?mimetype=text/plain|download.js] page is again edited to remove the corresponding entry from the "release" variable and the edit is synced using [/help?cmd=uv|fossil uv sync]. This causes the build products to disappear from the download page immediately. But those build products are still taking up space in the unversioned content table of the server repository. To purge the obsolete build products, one or more [/help?cmd=uv|fossil uv rm] commands are run, followed by another [/help?cmd=uv|fossil uv sync]. It is important to purge obsolete build products since they take up a lot of space. At [/repo-tabsize] you can see that the unversioned table takes up a substantial fraction of the repository. <h2>3.0 Security</h2> Only users with the [/setup_ulist_notes|"y" permission] are allowed to push unversioned content up to the servers. Having the ability to push check-ins (the [/setup_ulist_notes|"i" permission]) is not sufficient. On the Fossil project there are (as of 2023-07-31) 71 people who have check-in privileges. But only the project lead can push unversioned content and thus change the build products on the download page. Minimizing the number of people who can change the build products helps to ensure that rogue binaries do not slip onto the download page unnoticed. |
Added www/adding_code.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 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 | <title>Adding Features To Fossil</title> <h2>1.0 Introduction</h2> This article provides a brief overview of how to write new C-code code that extends or enhances the core Fossil binary. New features can be added to a Fossil server using [./serverext.wiki|external CGI programs], but that is not what this article is about. This article focuses on how to make changes to Fossil itself. <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. 2. The <b>makeheaders</b> preprocessor generates all the ".h" files automatically. Fossil programmers write ".c" files only and let the makeheaders preprocessor create the ".h" files. 3. The <b>translate</b> preprocessor converts source code lines that begin with "@" into string literals, or into print statements that generate web page output, depending on context. The [./makefile.wiki|Makefile] for Fossil takes care of running these preprocessors with all the right arguments and in the right order. So it is not necessary to understand the details of how these preprocessors work. (Though, the sources for all three preprocessors are included in the source tree and are well commented, if you want to dig deeper.) It is only necessary to know that these preprocessors exist and hence will effect the way you write code. <h2>3.0 Adding New Source Code Files</h2> New source code files are added in the "src/" subdirectory of the Fossil source tree. Suppose one wants to add a new source code file named "xyzzy.c". The first step is to add this file to the various makefiles. Do so by editing the file tools/makemake.tcl and adding "xyzzy" (without the final ".c") to the list of source modules at the top of that script. Save the result and then run the makemake.tcl script using a TCL interpreter. The command to run the makemake.tcl script is: <verbatim> tclsh makemake.tcl </verbatim> The working directory must be src/ when the command above is run. Note that TCL is not normally required to build Fossil, but it is required for this step. If you do not have a TCL interpreter on your system already, they are easy to install. A popular choice is the [http://www.activestate.com/activetcl|Active Tcl] installation from ActiveState. After the makefiles have been updated, create the xyzzy.c source file from the following template: <verbatim> /* ** Copyright boilerplate goes here. ***************************************************** ** High-level description of what this module goes ** here. */ #include "config.h" #include "xyzzy.h" #if INTERFACE /* Exported object (structure) definitions or #defines ** go here */ #endif /* INTERFACE */ /* New code goes here */ </verbatim> Note in particular the <b>#include "xyzzy.h"</b> line near the top. The "xyzzy.h" file is automatically generated by makeheaders. Every normal Fossil source file must have a #include at the top that imports its private header file. (Some source files, such as "sqlite3.c" are exceptions to this rule. Don't worry about those exceptions. The files you write will require this #include line.) The "#if INTERFACE ... #endif" section is optional and is only needed if there are structure definitions or typedefs or macros that need to be used by other source code files. The makeheaders preprocessor uses definitions in the INTERFACE section to help it generate header files. See [../tools/makeheaders.html | makeheaders.html] for additional information. After creating a template file such as shown above, and after updating the makefiles, you should be able to recompile Fossil and have it include your new source file, even before you source file contains any code. It is recommended that you try this. Be sure to [/help/add|fossil add] your new source file to the self-hosting Fossil repository and then [/help/commit|commit] your changes! <h2 id="newcmd">4.0 Creating A New Command</h2> By "commands" we mean the keywords that follow "fossil" when invoking Fossil from the command-line. So, for example, in <verbatim> fossil diff xyzzy.c </verbatim> The "command" is "diff". Commands may optionally be followed by arguments and/or options. To create new commands in Fossil, add code (either to an existing source file, or to a new source file created as described above) according to the following template: <verbatim> /* ** COMMAND: xyzzy ** ** Help text goes here. Backslashes must be escaped. */ void xyzzy_cmd(void){ /* Implement the command here */ fossil_print("Hello, World!\n"); } </verbatim> The example above creates a new command named "xyzzy" that prints the message "Hello, World!" on the console. This command is a normal command that will show up in the list of command from [/help/help|fossil help]. If you add an asterisk to the end of the command name, like this: <verbatim> ** COMMAND: xyzzy* </verbatim> Then the command will only show up if you add the "--all" option to [/help/help|fossil help]. Or, if the command name starts with "test" then the command will be considered experimental and will only show up when the --test option is used with [/help/help|fossil help]. The example above is a fully functioning Fossil command. You can add the text shown to an existing Fossil source file, recompiling then test it out by typing: <verbatim> ./fossil xyzzy ./fossil help xyzzy ./fossil xyzzy --help </verbatim> The name of the C function that implements the command can be anything 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 of existing commands by searching for "COMMAND: <i>name</i>" in the files of the "src/" directory. <h2 id="newpage">5.0 Creating A New Web Page</h2> As with commands, new webpages can be added simply by inserting a function that generates the webpage together with a special header comment. A template follows: <verbatim> /* ** WEBPAGE: helloworld */ void helloworld_page(void){ style_header("Hello World!"); @ <p>Hello, World!</p> style_footer(); } </verbatim> Add the code above to a new or existing Fossil source code file, then recompile fossil and run [/help/ui|fossil ui] then enter "http://localhost:8080/helloworld" in your web browser and the routine above will generate a web page that says "Hello World." It really is that simple. The special "WEBPAGE:" comment is picked up by the "mkindex" preprocessor and used to generate a table that maps the "helloworld" webpage name into a pointer to the "helloworld_page()" function. The function that implements a webpage can be named anything you like (as long as it does not collide with another name) but the traditional name is "<i>pagename</i><b>_page</b>". HTML pages begin with a call to style_header() and end with the call to style_footer(). Content is generated by the "@" lines that are translated (by the "translate" preprocessor) into printf-like code that generates the content of the webpage. Different techniques are used to generate non-HTML content. In the unlikely event that you need to generate non-HTML content, look at existing webpage implementations (ex: "logo" or "style.css") to see how that is done. There are lots of other things that a real web-page implementation will need to do, such verifying user credentials, parsing query parameters, and interacting with the repository. But now that you have the general idea of how webpages are implemented, you can look at the many other webpage implementations already built into Fossil to see how all that works. <h2>6.0 See Also</h2> * [./makefile.wiki|The Fossil Build Process] * [./tech_overview.wiki|A Technical Overview Of Fossil] * [./contribute.wiki|Contributing To The Fossil Project] * [./serverext.wiki|Adding CGI Extensions To A Fossil Server] |
Added www/alerts.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 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 | # Email Alerts ## Overview Beginning with version 2.7, Fossil can send email messages to subscribers to alert them to changes in the repository: * New [checkins](/help?cmd=ci) * [Ticket](./tickets.wiki) changes * [Wiki](./wikitheory.wiki) page changes * New and edited [forum](./forum.wiki) posts * Announcements Subscribers can elect to receive emails as soon as these events happen, or they can receive a daily digest of the events instead. Email alerts are sent by a [Fossil server](./server/), which must be [set up](#quick) by the Fossil administrator to send email. Email alerts do not currently work if you are only using Fossil from the command line. A bit of terminology: Fossil uses the terms "email alerts" and "notifications" interchangeably. We stick to the former term in this document except when referring to parts of the Fossil UI still using the latter term. ## Setup Prerequisites Much of this document describes how to set up Fossil's email alert system. To follow this guide, you will need a Fossil UI browser window open to the [Admin → Notification](/setup_notification) Fossil UI screen on the Fossil server that will be sending these email alerts, logged in as a user with [**Admin** capability](./caps/ref.html#a). It is not possible to work on a clone of the server's repository and push the configuration changes up to that repo as an Admin user, [on purpose](#backup). **Important:** Do not confuse that screen with Admin → Email-Server, which sets up a different subsystem within Fossil. That feature is related to this document's topic, but it is currently incomplete, so we do not cover it at this time. <a id="cd"></a> You will also need a CLI window open with its working directory changed to a checkout directory of the Fossil repository you are setting up to send email. If you don't `cd` to such a checkout directory first, you'll need to add `-R /path/to/repo.fossil` to each `fossil` command below to tell Fossil which repository you mean it to apply the command to. There are other prerequisites for email service, but since they vary depending on the configuration you choose, we'll cover these inline below. <a id="quick"></a> ## Quick Email Service Setup If you've already got a working Postfix, Exim, or Sendmail server on the machine running your Fossil instance(s), and you aren't using Fossil's `chroot` feature to wall Fossil off from the rest of the machine, it's fairly simple to set up email alerts. (Otherwise, skip [ahead](#advanced) to the sections on advanced email service setup.) This is our "quick setup" option even though setting up an SMTP mail server is not trivial, because there are many other reasons to have such a server set up already: internal project email service, `cron` notifications, server status monitoring notifications... With that out of the way, the Fossil-specific steps are easy: 1. Go to [Admin → Notification](/setup_notification) and fill out all of the **Required** fields: * **Canonical server URL** — Use the suggested URL * **"From" email address** — `forum-bounces@example.com` is traditional, but suit yourself * **Repository nickname** — See the suggested examples on the web page. 2. Set "Email Send Method" to "Pipe to a command" 3. Set the "Administrator email address" to a suitable valid email address on that machine. It could be the same value you used for the "From" address above, or it could be a different value like `admin@example.com`. Save your changes. At the command line, say $ fossil set email-send-command If that gives a blank value instead of `sendmail -ti`, say $ fossil set email-send-command "sendmail -ti" to force the setting. That works around a [known bug](https://fossil-scm.org/forum/forumpost/840b676410) which may be squished by the time you read this. If you're running Postfix or Exim, you might think that command is wrong, since you aren't running Sendmail. These mail servers provide a `sendmail` command for compatibility with software like Fossil that has no good reason to care exactly which SMTP server implementation is running at a given site. There may be other SMTP servers that also provide a compatible `sendmail` command, in which case they may work with Fossil using the same steps as above. <a id="status"></a> If you reload the Admin → Notification page, the Status section at the top should show: Outgoing Email: Piped to command "sendmail -ti" Pending Alerts: 0 normal, 0 digest Subscribers: 0 active, 0 total Before you move on to the next section, you might like to read up on [some subtleties](#pipe) with the "pipe to a command" method that we did not cover above. <a id="usage"></a> ## Usage and Testing Now that email service from Fossil is set up, you can test it and begin using it. <a id="sub" name="subscribe"></a> ### Subscribing to Alerts In the Status output above, we saw that there are no subscribers, so the next step is to add the first one. Go to the `/subscribe` page on your Fossil instance to sign up for email alerts. At the very least, you will need to sign up for "Forum Posts" and "Announcements" to complete the testing steps below. If you're logged in with a Fossil repository user account and put the same user name and email address into this forum as you used for your user information under Admin → Users, Fossil will simply tie your alert preferences to your login record, and the email address in your user's Contact Info field will be considered already-verified. Otherwise, Fossil will create an alert-only record, and you will have to verify the email address before Fossil will send alerts to it. This shows a key aspect of the way Fossil's email alerts system works, by the way: a user can be signed up for email alerts without having a full-fledged Fossil user account. Only when both user names are the same are the two records tied together under the hood. For more on this, see [Users vs Subscribers below](#uvs). If you are seeing the following complaint from Fossil: > Use a different login with greater privilege than FOO to access /subscribe ...then the repository's administrator forgot to give the [**EmailAlert** capability][cap7] to that user or to a user category that the user is a member of. After a subscriber signs up for alerts for the first time, a single verification email is sent to that subscriber's given email address. The new subscriber must click a link in that email in order to activate the subscription. Subscription verification emails are only sent once. This is a defense against malicious robots that try to harass innocent Internet users by having subscription pages send multiple verification emails. If the initial subscription verification does not go through correctly, an administrator must [intervene](#admin) to reset the subscription. Every subscriber-only email address has a [long random hexadecimal security code](#scode) that serves in place of a password. All email alerts contain a link in their footer back to the Fossil server, incorporating this security code, which allows the subscriber to adjust their subscription options. If a user doesn't have any of those emails, they can request a link via email by visiting the `/alerts` or `/unsubscribe` page on the repository. Those with Fossil repository logins can adjust their email alert settings by visiting the `/alerts` page on the repository. With the default skin, you can get there by clicking the "Logout" link in the upper right corner of any Fossil UI page then clicking the "Email Alerts" link. That link is also available via the Sitemap (`/sitemap`) and via the default skin's hamburger menu (☰). [cap7]: ./caps/ref.html#7 <a id="unsub" name="unsubscribe"></a> ### Unsubscribing To unsubscribe from alerts, visit the `/alerts` page on the repository, click the "Unsubscribe" button, then check the "Unsubscribe" checkbox to verify your action and press the "Unsubscribe" button a second time. This interlock is intended to prevent accidental unsubscription. <a id="test"></a> ### Test Email Service The easiest way to test email sending from Fossil is via the "[Send Announcement](/announce)" link at the top of the "Email Notification Setup" page. Put your email address in the "To:" line and a test message below, then press "Send Message" to verify that outgoing email is working. Another method is from the command line: $ fossil alerts test-message you@example.com --body README.md --subject Test That should send you an email with "Test" in the subject line and the contents of your project's `README.md` file in the body. That command assumes that your project contains a "readme" file, but of course it does, because you have followed the [Programming Style Guide Checklist][cl], right? Right. [cl]: https://sendgrid.com/blog/programming-style-guide-checklist/ <a id="cap7" name="ucap"></a> ### User Capabilities Once email alerts are working, you may need to [adjust the default user capabilities](./caps/) to give "[Email Alerts][cap7]" capability to any [user category](./caps/#ucat) or [individual user](./caps/#ucap) that needs to use the subscription setup pages, `/subscribe` and `/alerts`. [**Admin**][capa] and [**Setup**][caps] users always have this capability. To allow any passer-by on the Internet to subscribe, give the "Email Alerts" capability to the "nobody" user category. To require that a person solve a simple CAPTCHA first, give that capability to the "anonymous" user category instead. [capa]: ./caps/ref.html#a [caps]: ./caps/ref.html#s <a id="first" name="frist"></a> ### First Post I suggest taking the time to compose a suitable introductory message especially for your project's forum, one which a new user would find helpful. Wait a few seconds, and you should receive an email alert with the post's subject and body text in the email. <a id="trouble"></a> ### Troubleshooting If email alerts aren't working, there are several useful commands you can give to figure out why. (Be sure to [`cd` into a repo checkout directory](#cd) first!) $ fossil alerts status This should give much the same information as you saw [above](#status). One difference is that, since you've created a forum post, the `pending-alerts` value should only be zero if you did in fact get the requested email alert. If it's zero, check your mailer's spam folder. If it's nonzero, continue with these troubleshooting steps. $ fossil backoffice That forces Fossil to run its ["back office" process](./backoffice.md). Its only purpose at the time of this writing is to push out alert emails, but it might do other things later. Sometimes it can get stuck and needs to be kicked. For that reason, you might want to set up a crontab entry to make sure it runs occasionally. $ fossil alerts send This should also kick off the backoffice processing, if there are any pending alerts to send out. $ fossil alert pending Show any pending alerts. The number of lines output here should equal the [status output above](#status). $ fossil test-add-alerts f5900 $ fossil alert send Manually create an email alert and push it out immediately. The `f` in the first command's final parameter means you're scheduling a "forum" alert. The integer is the ID of a forum post, which you can find by visiting `/timeline?showid` on your Fossil instance. The second command above is necessary because the `test-add-alerts` command doesn't kick off a backoffice run. $ fossil ale send This only does the same thing as the final command above, rather than send you an ale, as you might be hoping. Sorry. <a id="advanced"></a> ## Advanced Email Setups Fossil offers several methods of sending email: 1. Pipe the email message text into a command. 2. Store email messages as entries in a SQLite database. 3. Store email messages as individual files in a directory. 4. Send emails to an SMTP relay. 5. Send emails directly to the recipients via SMTP. This wide range of options allows Fossil to talk to pretty much any SMTP setup. The first four options let Fossil delegate email handling to an existing [MTA] so that Fossil does not need to implement the [roughly two dozen][mprotos] separate [RFCs][rfcs] required in order to properly support SMTP email in this complex world we've built. As well, this design choice means you do not need to do duplicate configuration, such as to point Fossil at your server's TLS certificate in order to support users behind mail servers that require STARTTLS encryption. [mprotos]: http://sqlite.1065341.n5.nabble.com/Many-ML-emails-going-to-GMail-s-SPAM-tp98685p98722.html [rfcs]: https://en.wikipedia.org/wiki/Request_for_Comments <a id="pipe"></a> ### Method 1: Pipe to a Command This is our ["quick setup" option](#quick) above, but there are some details we ignored which we'll cover now. Fossil pipes the email message in [RFC 822 format][rfc822] to the standard input of the command you gave as the "Email Send Method", defaulting to `sendmail -ti`. This constitutes a protocol between Fossil and the SMTP [message transfer agent (MTA)][MTA]. Any other MTA which speaks the same protocol can be used in place of the most common options: Sendmail, Exim, and Postfix. The `-t` option tells the command to expect the list of email recipients in a `To` header in the RFC 822 message presented on its standard input. Without this option, the `sendmail` command expects to receive the recipient list on the command line, but that's not possible with the current design of this email sending method. Therefore, if you're attempting to use a less common MTA which cannot parse the recipient list from the `To` header in the email message, you might need to look for a different MTA. The `-i` option is only needed for MTAs that take a dot/period at the beginning of a line of standard input text as "end of message." Fossil doesn't attempt to escape such dots, so if the line wrapping happens to occur such that a dot or period in an alert message is at the beginning of a line, you'll get a truncated email message without this option. Statistically, this will happen about once every 70 or so messages, so it is important to give this option if your MTA treats leading dots on a line this way. <a id="msmtp"></a> The [`msmtp`][msmtp] SMTP client is compatible with this protocol if you give it the `-t` option. It’s a useful option on a server hosting a Fossil repository which doesn't otherwise require a separate SMTP server for other purposes, such as because you’ve got a separate provider for your email and merely need a way to let Fossil feed messages into it. It is probably also possible to configure [`procmail`][pmdoc] to work with this protocol. If you know how to do it, a patch to this document or a how-to on [the Fossil forum][ff] would be appreciated. [ff]: https://fossil-scm.org/forum/ [msmtp]: https://marlam.de/msmtp/ [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent [pmdoc]: http://pm-doc.sourceforge.net/doc/ [rfc822]: https://www.w3.org/Protocols/rfc822/ <a id="db"></a> ### Method 2: Store in a Database The self-hosting Fossil repository at <https://fossil-scm.org/> currently uses this method rather than [the pipe method](#pipe) because it is running inside of a restrictive [chroot jail][cj] which is unable to hand off messages to the local MTA directly. When you configure a Fossil server this way, it adds outgoing email messages to a SQLite database file. A separate daemon process can then extract those messages for further disposition. Fossil includes a copy of [the daemon](/file/tools/email-sender.tcl) used on `fossil-scm.org`: it is just a short Tcl script that continuously monitors this database for new messages and hands any that it finds off to a local MTA using the same [pipe to MTA protocol](#pipe) as above. In this way, outbound email alerts escape the chroot jail without requiring that we insert a separate MTA configuration inside that jail. We only need to arrange that the same SQLite DB file be visible both inside and outside the chroot jail, which we do by naming the database file in the "Store Emails In This Database" setting under Admin → Notification. The Tcl script has this path hard-coded as `/home/www/fossil/emailqueue.db`, but you will probably need to adjust that for your local purposes. This method may work with other similar technologies besides `chroot`: Docker containers, LXC containers, BSD jails, Solaris zones, etc. With suitable file share mappings, this method may even work with virtual machine or distributed computing setups where the MTA and Fossil servers are not on the same machine, though beware the [risk of DB corruption][rdbc] if used with a file sharing technology that doesn't use proper file locking. You can start this Tcl script as a daemon automatically on most Unix and Unix-like systems by adding the following line to the `/etc/rc.local` file of the server that hosts the repository sending email alerts: /usr/bin/tclsh /home/www/fossil/email-sender.tcl & [cj]: https://en.wikipedia.org/wiki/Chroot [rdbc]: https://www.sqlite.org/howtocorrupt.html#_filesystems_with_broken_or_missing_lock_implementations <a id="dir"></a> ### Method 3: Store in a Directory This method is functionally very similar to [the DB method](#db), differing only in that messages are written to a directory in the filesystem. You should therefore read that section and make the minor adjustments required by the storage method. This method may work over a file sharing mechanism that doesn't do file locking properly, as long as the reading process is somehow restricted from reading a message file as it's being written. It might be useful in testing and debugging to temporarily switch to this method, since you can easily read the generated email messages without needing to involve an [MTA]. <a id="relay"></a> ### Method 4: SMTP Relay In this configuration, the Fossil server contacts an open SMTP relay and sends the messages to it. This method is only appropriate when: 1. You have a local MTA that doesn't accept [the pipe protocol](#pipe). 2. The MTA is willing to accept anonymous submissions, since Fossil currently has no way to authenticate itself to the MTA. This is [an unsafe configuration][omr] in most cases, but some SMTP servers make an exception for connections coming from a `localhost` or LAN address, choosing to accept such submissions as inherently safe. If you have a local MTA meeting criterion #1 but not #2, we'd suggest using a more powerful SMTP client such as [msmtp](#msmtp) along with one of the other methods above. [omr]: https://en.wikipedia.org/wiki/Open_mail_relay <a id="direct"></a> ### Method 5: Direct SMTP Send As of Fossil 2.7, the code to support this method is incomplete, so you cannot currently select it as an option in Admin → Notification. <a id="uvs"></a> ## Users vs Subscribers Fossil makes a distinction between "users" and "subscribers". A user is someone with a username and password: that is, someone who can log into the Fossil repository. A subscriber is someone who receives email alerts. Users can also be subscribers and subscribers can be users, but that does not have to be the case. It is possible to be a user without being a subscriber and to be a subscriber without being a user. In the repository database file, users are tracked with the `user` table and subscribers are tracked via the `subscriber` table. <a id="admin"></a> ## Administrator Activities The "[List Subscribers](/subscribers)" button at the top of the Admin → Notification screen gives a list of subscribers, which gives a Fossil server administrator a lot of power over those subscriptions. Clicking an email address in this subscriber list opens the same `/alerts` page that the user can see for their own subscription, but with more information and functionality than normal users get: * Subscription creation and modification timestamps. * The IP address the user had when they last made a change via either `/subscribe` or `/alert`. * The user's login name, if they are not [a mere subscriber](#uvs). A Fossil Admin user is allowed to modify this, either to tie a subscription-only record to an existing Fossil user account or to break that tie. * The "Do not call" checkbox allows a Fossil Admin user to mark a given email address so that Fossil never sends email to that address. This is distinct from unsubscribing that email address because it prevents Fossil from accepting a new subscription for that address. * The Verified checkbox is initially unchecked for subscriber-only email addresses until the user clicks the link in the verification email. This checkbox lets the Fossil Admin user manually verify the user, such as in the case where the verification email message got lost. Unchecking this box does not cause another verification email to be sent. This screen also allows a Fossil Admin user to perform other activities on behalf of a subscriber which they could do themselves, such as to [unsubscribe](#unsub) them. <a id="backup"></a> ## Cloning, Syncing, and Backups That’s [covered elsewhere](./backup.md#alerts). <a id="pages" name="commands"></a> ## Controlling the Email Alert System This section collects the list of Fossil UI pages and CLI commands that control the email alert system, some of which have not been mentioned so far: Commands: * The [`alerts`](/help?cmd=alerts) command * The [`test-alert`](/help?cmd=test-alert) command * The [`test-add-alerts`](/help?cmd=test-add-alerts) command Web pages available to users and subscribers: * The [`/subscribe`](/help?cmd=/subscribe) page * The [`/alerts`](/help?cmd=/alerts) page * The [`/unsubscribe`](/help?cmd=/unsubscribe) page * The [`/renew`](/help?cmd=/renew) page * The [`/contact_admin`](/help?cmd=/contact_admin) page Administrator-only web pages: * The [`/setup_notification`](/help?cmd=/setup_notification) page * The [`/subscribers`](/help?cmd=/subscribers) page <a id="design"></a> ## Design of Email Alerts This section describes the low-level design of the email alert system in Fossil. This expands on the high-level administration focused material above with minimal repetition. This section assumes expert-level systems knowledge. If the material above sufficed for your purposes, feel free to skip this section, which runs to the end of this document. <a id="datades"></a> ### Data Design There are two new tables in the repository database, starting with Fossil 2.7. These tables are not created in new repositories by default. The tables only come into existence as needed when email alerts are configured and used. * <b>SUBSCRIBER</b> → The subscriber table records the email address for people who want to receive email notifications. Each subscriber has a `subscriberCode` which is a random 32-byte blob that uniquely identifies the subscriber. There are also fields to indicate what kinds of notifications the subscriber wishes to receive, whether or not the email address of the subscriber has been verified, etc. * <b>PENDING\_ALERT</b> → The PENDING\_ALERT table contains records that define events about which alert emails might need to be sent. A pending\_alert always refers to an entry in the EVENT table. The EVENT table is part of the standard schema and records timeline entries. In other words, there is one row in the EVENT table for each possible timeline entry. The PENDING\_ALERT table refers to EVENT table entries for which we might need to send alert emails. As pointed out above, ["subscribers" are distinct from "users"](#uvs). The SUBSCRIBER.SUNAME field is the optional linkage between users and subscribers. <a id="stdout"></a> ### The "stdout" Method The [list of mail sending methods](#advanced) above left out an internal-only method called "stdout" which simply writes the text of the email message on standard output. The "stdout" method is used for testing and debugging. If you need something similar and can't modify your local Fossil instance to use this method, you might temporarily switch to [the "dir" method](#dir) instead. <a id="msgfmt"></a> ### Message Format The email messages generated by Fossil have a [well-formed header][rfc822]. The downstream processing is expected to extract the "To:", "From:", "Subject:" and whatever other attributes it needs from the email header text. These emails use the `text/plain` MIME type with the UTF-8 character set. We currently use a transfer encoding of `quoted-printable`, but there is commented-out code in Fossil to switch to `base64` encoding, which Fossil used in the early days leading up to the 2.7 release. If you switch Fossil back to `base64` mode, you may want to build a utility program that ships in the Fossil source tree named ["tools/decode-email.c"](/file/tools/decode-email.c) which can decode these messages into a human-readable format. <a id="inbound" name="bounces"></a> ### Dealing with Inbound Email Inbound email messages — for example, bounces from failed alert emails — should be relayed to the `fossil email inbound` command. That command is currently a no-op place-holder. At some point, we will need to design and write a bounce-message processing system for Fossil. <a id="password" name="scode" name="verification"></a> ### Passwords vs Subscriber Codes When anonymous passers-by on the Internet sign up for email alerts, their email address must first be verified. An email message is sent to the address supplied inviting the user to click on a link. The link includes a pseudorandom 128-bit blob encoded as 32 hexadecimal digits, which serves in place of a password for that email address. (This is stored in the database as `subscriber.subscriberCode`.) If anyone visits the link, the email address is verified. Knowledge of the `subscriberCode` is sufficient to control a subscription. Because this code is included in plain text in email alert messages, it is not as secure as a separate password, but it has several virtues: * It is easier for the average subscriber to deal with in that they don't have to come up with yet another password and store it safely. * If the `subscriberCode` is stolen, the worst that can happen is that the thief can change that email address's subscription settings. Contrast a password which may be shared with other services, which then compromises those other services. * No PII other than the subscriber's email address is available to an attacker with the `subscriberCode`. Nor can knowledge of the `subscriberCode` lead to an email flood or other annoyance attack, as far as I can see. If the `subscriberCodes` for a Fossil repository are ever compromised, new ones can be generated as follows: UPDATE subscriber SET subscriberCode=randomblob(32); Since this then affects all new email alerts going out from Fossil, your end users may never even realize that they're getting new codes, as long as they don't click on the URLs in the footer of old alert messages. With that in mind, a Fossil server administrator could choose to randomize the `subscriberCodes` periodically, such as just before the daily digest emails are sent out each day. **Important:** All of the above is distinct from the passwords for users with a Fossil repository login. Such users also have subscriber codes, but those codes can only be used to modify the user's email alert settings. That code cannot allow a user to log into the user's Fossil repository account. <a id="processing"></a> ### Internal Processing Flow Almost all of the email alert code is found in the [`src/alerts.c`](/file/src/alerts.c) source file. When email alerts are enabled, a trigger is created in the schema (`email_trigger1`) that adds a new entry to the `PENDING_ALERT` table every time a row is added to the `EVENT` table. During a `fossil rebuild`, the `EVENT` table is rebuilt from scratch; since we do not want users to get alerts for every historical check-in, the trigger is disabled during `rebuild`. Email alerts are sent out by the `alert_send_alerts()` function, which is normally called automatically due to the `email-autoexec` setting, which defaults to enabled. If that setting is disabled or if the user simply wants to force email alerts to be sent immediately, they can give a `fossil alert send` command, such as via a `cron` script. Each time this function is called, the alert messages are moved further down the chain, so you cannot cause duplicate alerts by calling it too often. Digests are handled by recording the time of the last digest in the `email-last-digest` setting, and only sending a new digest if the current time is one day or later after the last digest. Individual emails are sent to each subscriber. I (drh) ran tests and found that I could send about 1200 emails/second, which is fast enough that I do not need to resort to trying to notify multiple subscribers with a single email. Because each subscriber gets a separate email, the system can include information in the email that is unique to the subscriber, such as a link to the page to edit their subscription. That link includes the `subscriberCode`. |
Added www/antibot.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 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 | <title>Defense Against Robots</title> A typical Fossil website can have millions of pages, and many of those pages (for example diffs and annotations and tarballs) can be expensive to compute. If a robot walks a Fossil-generated website, it can present a crippling bandwidth and CPU load. A Fossil website is intended to be used interactively by humans, not walked by robots. This article describes the techniques used by Fossil to try to welcome human users while keeping out robots. <h2>The Hyperlink User Capability</h2> Every Fossil web session has a "user". For random passers-by on the internet (and for robots) that user is "nobody". The "anonymous" user is also available for humans who do not wish to identify themselves. The difference is that "anonymous" requires a login (using a password supplied via a CAPTCHA) whereas "nobody" does not require a login. The site administrator can also create logins with passwords for specific individuals. Users without the <b>[./caps/ref.html#h | Hyperlink]</b> capability do not see most Fossil-generated hyperlinks. This is a simple defense against robots, since [./caps/#ucat | the "nobody" user category] does not have this capability by default. Users must log in (perhaps as "anonymous") before they can see any of the hyperlinks. A robot that cannot log into your Fossil repository will be unable to walk its historical check-ins, create diffs between versions, pull zip archives, etc. by visiting links, because there are no links. A text message appears at the top of each page in this situation to invite humans to log in as anonymous in order to activate hyperlinks. But requiring a login, even an anonymous login, can be annoying. Fossil provides other techniques for blocking robots which are less cumbersome to humans. <h2>Automatic Hyperlinks Based on UserAgent</h2> Fossil has the ability to selectively enable hyperlinks for users that lack the <b>Hyperlink</b> capability based on their UserAgent string in the HTTP request header and on the browsers ability to run Javascript. The UserAgent string is a text identifier that is included in the header of most HTTP requests that identifies the specific maker and version of the browser (or robot) that generated the request. Typical UserAgent strings look like this: <ul> <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 robot 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 requester as human whereas the second two identify the requester as a robot. Note that the UserAgent string is completely under the control of the requester and so a malicious robot can forge a UserAgent string that makes it look like a human. But most robots want to "play nicely" on the internet and are quite open about the fact that they are a robot. And so the UserAgent string provides a good first-guess about whether or not a request originates from a human or a robot. In Fossil, under the Admin/Robot-Defense menu, there is a setting entitled "<b>Enable hyperlinks based on User-Agent and/or Javascript</b>". If this setting is set to "UserAgent only" or "UserAgent and Javascript", and if the UserAgent string looks like a human and not a robot, then Fossil will enable hyperlinks even if the <b>Hyperlink</b> capability is omitted from the user permissions. This settingn gives humans easy access to the hyperlinks while preventing robots from walking the billions of pages on a typical Fossil site. If the setting is "UserAgent only", then the hyperlinks are simply enabled and that is all. But if the setting is "UserAgent and Javascript", then the hyperlinks are not enabled directly . Instead, the HTML code that is generated contains anchor tags ("<a>") with "href=" attributes that point to [/honeypot] rather than the correct link. JavaScript code is added to the end of the page that goes back and fills in the correct "href=" attributes of the anchor tags with the true hyperlink targets, thus enabling the hyperlinks. This extra step of using JavaScript to enable the hyperlink targets is a security measure against robots that forge a human-looking UserAgent string. Most robots do not bother to run JavaScript and so to the robot the empty anchor tag will be useless. But all modern web browsers implement JavaScript, so hyperlinks will show up normally for human users. <h2>Further Defenses</h2> Recently (as of this writing, in the spring of 2013) the Fossil server on the SQLite website ([http://www.sqlite.org/src/]) has been hit repeatedly by Chinese robots that use forged UserAgent strings to make them look like normal web browsers and which interpret JavaScript. We do not believe these attacks to be nefarious since SQLite is public domain and the attackers could obtain all information they ever wanted to know about SQLite simply by cloning the repository. Instead, we believe these "attacks" are coming from "script kiddies". But regardless of whether or not malice is involved, these attacks do present an unnecessary load on the server which reduces the responsiveness of the SQLite website for well-behaved and socially responsible users. For this reason, additional defenses against robots have been put in place. On the Admin/Robot-Defense page of Fossil, just below the "<b>Enable hyperlinks using User-Agent and/or Javascript</b>" setting, there are now two additional sub-settings that can be optionally enabled to control hyperlinks. The first new sub-setting is a delay (in milliseconds) before setting the "href=" attributes on anchor tags. The default value for this delay is 10 milliseconds. The idea here is that a robots will try to interpret the links on the page immediately, and will not wait for delayed scripts to be run, and thus will never enable the true links. The second sub-setting waits to run the JavaScript that sets the "href=" attributes on anchor tags until after at least one "mousedown" or "mousemove" event has been detected on the <body> element of the page. The thinking here is that robots will not be simulating mouse motion and so no mouse events will ever occur and hence the hyperlinks will never become enabled for robots. See also [./loadmgmt.md|Managing Server Load] for a description of how expensive pages can be disabled when the server is under heavy load. <h2>The Ongoing Struggle</h2> Fossil currently does a very good job of providing easy access to humans while keeping out troublesome robots. However, robots continue to grow more sophisticated, requiring ever more advanced defenses. This "arms race" is unlikely to ever end. The developers of Fossil will continue to try improve the robot defenses of Fossil so check back from time to time for the latest releases and updates. Readers of this page who have suggestions on how to improve the robot defenses in Fossil are invited to submit your ideas to the Fossil Users forum: [https://fossil-scm.org/forum]. |
Added www/apple-touch-icon.png.
cannot compute difference between binary files
Added www/background.jpg.
cannot compute difference between binary files
Added www/backoffice.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 | Backoffice ========== This is technical documentation about the internal workings of Fossil. Ordinary Fossil users do not need to know about anything covered by this document. The information here is intended for people who want to enhance or extend the Fossil code, or who just want a deeper understanding of the internal workings of Fossil. What Is The Backoffice ---------------------- The backoffice is a mechanism used by a [Fossil server](./server/) to do low-priority background work that is not directly related to the user interface. Here are some examples of the kinds of work that backoffice performs: 1. Sending email alerts and notifications 2. Sending out daily digests of email notifications 3. Other background email handling chores 4. Automatic syncing of peer repositories 5. Repository maintenance and optimization (As of 2018-08-07, only items 1 and 2 have actually been implemented.) The idea is that the backoffice handles behind-the-scenes work that does not have tight latency requirements. When Backoffice Runs -------------------- A backoffice process is usually launched automatically by a webpage request. After each webpage is generated, Fossil checks to see if any backoffice work needs to be done. If there is work to do, and no other process is already assigned to do the work, then a new backoffice process is started to do the work. This happens for every webpage, regardless of how that webpage is launched, and regardless of the purpose of the webpage. This also happens on the server for "[fossil sync](/help?cmd=sync)" and [fossil clone](/help?cmd=clone)" commands which are implemented as web requests - albeit requests that the human user never sees. Web requests can arrive at the Fossil server via direct TCP/IP (for example when Fossil is started using commands like "[fossil server](/help?cmd=server)") or via [CGI](./server/any/cgi.md) or [SCGI](./server/any/scgi.md) or via SSH. A backoffice process might be started regardless of the origin of the request. The backoffice is not a daemon. Each backoffice process runs for a short while and then exits. This helps keep Fossil easy to manage, since there are no daemons to start and stop. To upgrade Fossil to a new version, you simply replace the older "fossil" executable with the newer one, and the backoffice processes will (within a minute or so) start using the new one. (Upgrading the executable on Windows is more complicated, since on Windows it is not possible to replace an executable file that is in active use. But Windows users probably already know this.) The backoffice is serialized and rate limited. No more than a single backoffice process will be running at once, and backoffice runs will not occur more frequently than once every 60 seconds. (The 60-second spacing is controlled by the BKOFCE_LEASE_TIME macro in the [backoffice.c](/file/src/backoffice.c) source file.) If a Fossil server is idle, then no backoffice processes will be running. That means there are no extra processes sitting around taking up memory and process table slots for seldom accessed repositories. The backoffice is an on-demand system. A busy repository will usually have a backoffice running at all times. But an infrequently accessed repository will only have backoffice processes running for a minute or two following the most recent access. Manually Running The Backoffice ------------------------------- The automatic backoffice runs are sufficient for most installations. However, the daily digest of email notifications is handled by the backoffice. If a Fossil server can sometimes go more than a day without being accessed, then the automatic backoffice will never run, and the daily digest might not go out until somebody does visit a webpage. If this is a problem, an administrator can set up a cron job to periodically run: fossil backoffice _REPOSITORY_ That command will cause backoffice processing to occur immediately. Note that this is almost never necessary for an internet-facing Fossil repository, since most repositories will get multiple accesses per day from random robots, which will be sufficient to kick off the daily digest emails. And even for a private server, if there is very little traffic, then the daily digests are probably a no-op anyhow and won't be missed. Automatic Backoffice Does Not Work On Some Systems -------------------------------------------------- We have observed that the automatic backoffice does not work on some system - OpenBSD in particular. We still do not understand why this is. (If you have insights, please share them on the [Fossil Forum](https://fossil-scm.org/forum) so that we can perhaps fix the problem.) For now, the backoffice must be run manually on OpenBSD systems. To set up fully-manual backoffice, first disable the automatic backoffice using the "[backoffice-disable](/help?cmd=backoffice-disable)" setting. fossil setting backoffice-disable on Then arrange to invoke the backoffice separately using a command like this: fossil backoffice --poll 30 _REPOSITORY-LIST_ Multiple repositories can be named. This one command will handle launching the backoffice for all of them. There are additional useful command-line options. See the "[fossil backoffice](/help?cmd=backoffice)" documentation for details. The backoffice processes run manually using the "fossil backoffice" command do not normally use a lease. That means that you run the "fossil backoffice" command with --poll and you forget to disable automatic backoffice by setting the "backoffice-disable" flag, then you might have one backoffice running due command and another due to a webpage access, both at the same time. This is harmless. The only downside is that it uses extra CPU time. How Backoffice Is Implemented ----------------------------- The backoffice is implemented by the "[backoffice.c](/file/src/backoffice.c)" source file. Serialization and rate limiting is handled by a single entry in the repository database CONFIG table named "backoffice". This entry is called "the lease". The value of the lease is a text string representing four integers, which are respectively: 1. The process id of the "current" backoffice process 2. The lease expiration time of the current backoffice process 3. The process id of the "next" backoffice process 4. The lease expiration time for the next backoffice process Times are expressed in seconds since 1970. A process id of zero means "no process". Sometimes the process id will be non-zero even if there is no corresponding process. Fossil knows how to figure out whether or not a process still exists. You can print out a decoded copy of the current backoffice lease using this command: fossil test-backoffice-lease -R _REPOSITORY_ If a system has been idle for a long time, then there will be no backoffice processes. (Either the process id entries in the lease will be zero, or there will exist no process associated with the process id.) When a new web request comes in, the system sees that no backoffice process is active and so it kicks off a separate process to run backoffice. The new backoffice process becomes the "current" process. It sets a lease expiration time for itself to be 60 seconds in the future. Then it does the backoffice processing and exits. Note that usually the backoffice process will exit long before its lease expires. That is ok. The lease is there to limit the rate at which backoffice processes run. If a new backoffice process starts up and sees that the "current" lease has yet to expire, the new process makes itself the "next" backoffice process and sets its expiration time to be 60 seconds past the expiration time of the "current" backoffice process. The "next" process then puts itself to sleep until the "current" lease expires. After the "current" lease expires and the "current" process has itself exited, then the "next" process promotes itself to the new "current" process. It sets the current lease expiration to be 60 seconds in the future, runs whatever backoffice work is needed, then exits. If a new backoffice process starts up and finds that there is already a "current" lease and a "next" process, it exits without doing anything. This should happen only rarely, since the lease information is checked prior to spawning the backoffice process, so a conflict will only happen in a race. Because the "backoffice" entry of the CONFIG table is in the repository database, access to the lease is serialized. The lease prevents more than one backoffice process from running at a time. It prevents backoffice processes from running more frequently than once every 60 seconds. And, it guarantees (assuming processes are not killed out-of-band) that every web request will be followed within 60 seconds by a backoffice run. Debugging The Backoffice ------------------------ The backoffice should "just work". It should not require administrator attention. However, if you suspect that something is not working right, there are some debugging aids. We have already mentioned the command that shows the backoffice lease for a repository: fossil test-backoffice-lease -R _REPOSITORY_ Running that command every few seconds should show what is going on with backoffice processing in a particular repository. There are also settings that control backoffice behavior. The "backoffice-nodelay" setting prevents the "next" process from taking a lease and sleeping. If "backoffice-nodelay" is set, that causes all backoffice processes to exit either immediately or after doing whatever backoffice works needs to be done. If something is going wrong and backoffice leases are causing delays in webpage processing, then setting "backoffice-nodelay" to true can work around the problem until the bug can be fixed. The "backoffice-logfile" setting is the name of a log file onto which is appended a short message everything a backoffice process actually starts to do the backoffice work. This log file can be used to verify that backoffice really is running, if there is any doubt. The "backoffice-disable" setting prevents automatic backoffice processing, if true. Use this to completely disable backoffice processing that occurs automatically after each HTTP request. The "backoffice-disable" setting does not affect the operation of the manual "fossil backoffice" command. Most installations should leave "backoffice-nodelay" and "backoffice-disable" set to their default values of off and leave "backoffice-logfile" unset or set to an empty string. |
Added www/backup.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 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 | # Backing Up a Remote Fossil Repository One of the great benefits of Fossil and other [distributed version control systems][dvcs] is that cloning a repository makes a backup. If you are running a project with multiple developers who share their work using a [central server][server] and the server hardware catches fire, the clones of the repository on each developer workstation *may* serve as a suitable backup. [dvcs]: wikipedia:/wiki/Distributed_version_control [server]: ./server/whyuseaserver.wiki We say “may†because it turns out not everything in a Fossil repository is copied when cloning. You don’t even always get copies of all historical file artifacts. More than that, a Fossil repository typically contains other useful information that is not always shared as part of a clone, which might need to be backed up separately. To wit: ## <a id="pii"></a> Sensitive Information Fossil purposefully does not clone certain sensitive information unless you’re logged in as a user with [Setup] capability. As an example, a local clone may have a different `user` table than the remote, because only a Setup user is allowed to see the full version for privacy and security reasons. ## <a id="config"></a> Configuration Drift Fossil allows the local configuration to differ in several areas from that of the remote. You get a copy of *some* of these configuration areas on initial clone — not all! — but after that, remote configuration changes mostly do not sync down automatically. #### <a id="skin"></a> Skin Changes to the remote’s skin don’t sync down, on purpose, since you may want to have a different skin on the local clone than on the remote. You can ask for updates with [`fossil config pull skin`][cfg], but that does not happen automatically during the course of normal development. #### <a id="alerts"></a> Email Alerts The Admin → Notification settings do not get copied on clone or sync, and it is not possible to push such settings from one repository to another. We did this on purpose because you may have a network of peer repositories, and you only want one repository sending email alerts. If Fossil were to automatically replicate the email alert settings to a separate repository, subscribers would get multiple alerts for each event, which would be *bad.* The only element of the email alert configuration that can be pulled over the sync protocol on demand is the subscriber list, via [`fossil config pull subscriber`][cfg]. #### <a id="project"></a> Project Configuration This is normally generated once during `fossil init` and never changed, so Fossil doesn’t pull this information without being forced, on purpose. You could accidentally merge two separate Fossil repos by pushing one repo’s project config up to another, for example. #### <a id="other-cfg"></a> Others A repo’s URL aliases, [interwiki configuration](./interwiki.md), and [ticket customizations](./custom_tcket.wiki) also do not normally sync. [cfg]: /help?cmd=configuration ## <a id="private"></a> Private Branches The very nature of Fossil’s [private branch feature][pbr] ensures that remote clones don’t get a copy of those branches. Normally this is exactly what you want, but in the case of making backups, you probably want to back up these branches as well. One of the two backup methods below provides this. ## <a id="shun"></a> Shunned Artifacts Fossil purposefully doesn’t sync [shunned artifacts][shun]. If you want your local clone to be a precise match to the remote, it needs to track changes to the shun table as well. ## <a id="uv"></a> Unversioned Artifacts Data in Fossil’s [unversioned artifacts table][uv] doesn’t sync down by default unless you specifically ask for it. Like local configuration data, it doesn’t get pulled as part of a normal `fossil sync`, but *unlike* the config data, you don’t get unversioned files as part of the initial clone unless you ask for it by passing the `--unversioned/-u` flag. ## <a id="ait"></a>Autosync Is Intransitive If you’re using Fossil in a truly distributed mode, rather than the simple central-and-clones model that is more common, there may be no single source of truth in the network because Fossil’s autosync feature isn’t transitive. That is, if you cloned from server A, and then you stand that up on a server B, then if I clone from your server as my repository C, your changes to B autosync up to A, but not down to me on C until I do something locally that triggers autosync. The inverse is also true: if I commit something on C, it will autosync up to B, but A won’t get a copy until someone on B does something to trigger a sync there. An easy way to run into this problem is to set up failover servers `svr1` thru `svr3.example.com`, then set `svr2` and `svr3` up to sync with the first. If all of the users normally clone from `svr1`, their commits don’t get to `svr2` and `svr3` until something on one of the servers pushes or pulls the changes down to the next server in the sync chain. Likewise, if `svr1` falls over and all of the users re-point their local clones at `svr2`, then `svr1` later reappears, `svr1` is likely to remain a stale copy of the old version of the repository until someone causes it to sync with `svr2` or `svr3` to catch up again. And then if you originally designed the sync scheme to treat `svr1` as the primary source of truth, those users still syncing with `svr2` won’t have their commits pushed up to `svr1` unless you’ve set up bidirectional sync, rather than have the two backup servers do `pull` only. # <a id="sync-solution"></a> Solution 1: Explicit Pulls The following script solves most of the above problems for the use case where you want a *nearly-complete* clone of the remote repository using nothing but the normal Fossil sync protocol. It only does so if you are logged into the remote as a user with Setup capability, however. ``` shell #!/bin/sh fossil sync --unversioned fossil configuration pull all fossil rebuild ``` The last step is needed to ensure that shunned artifacts on the remote are removed from the local clone. The second step includes `fossil conf pull shun`, but until those artifacts are actually rebuilt out of existence, your backup will be “more than complete†in the sense that it will continue to have information that the remote says should not exist any more. That would be not so much a “backup†as an “archive,†which might not be what you want. # <a id="sql-solution"></a> Solution 2: SQL-Level Backup The first method doesn’t get you a copy of the remote’s [private branches][pbr], on purpose. It may also miss other info on the remote, such as SQL-level customizations that the sync protocol can’t see. (Some [ticket system customization][tkt] schemes rely on this ability, for example.) You can solve such problems if you have access to the remote server, which allows you to get a SQL-level backup by delegating handling of locking and transaction isolation to [the `backup` command][bu], allowing the user to safely back up an in-use repository. If you have SSH access to the remote server, something like this will work: ``` shell #!/bin/bash bf=repo-$(date +%Y-%m-%d).fossil ssh example.com "cd museum ; fossil backup -R repo.fossil backups/$bf" && scp example.com:museum/backups/$bf ~/museum/backups ``` Beware that this method does not solve [the intransitive sync problem](#ait), in and of itself: if you do a SQL-level backup of a stale repo DB, you have a *stale backup!* You should therefore run this on every node that may need to serve as a backup so that at least *one* of the backups is also up-to-date. # <a id="enc"></a> Encrypted Off-Site Backups A useful refinement that you can apply to both methods above is encrypted off-site backups. You may wish to store backups of your repositories off-site on a service such as Dropbox, Google Drive, iCloud, or Microsoft OneDrive, where you don’t fully trust the service not to leak your information. This addition to the prior scripts will encrypt the resulting backup in such a way that the cloud copy is a useless blob of noise to anyone without the key: ```shell iter=152830 pass="h8TixP6Mt6edJ3d6COaexiiFlvAM54auF2AjT7ZYYn" gd="$HOME/Google Drive/Fossil Backups/$bf.xz.enc" fossil sql -R ~/museum/backups/"$bf" .dump | xz -9 | openssl enc -e -aes-256-cbc -pbkdf2 -iter $iter -pass pass:"$pass" -out "$gd" ``` If you’re adding this to the first script above, remove the “`-R repo-name`†bit so you get a dump of the repository backing the current working directory. Change the `pass` value to some other long random string, and change the `iter` value to something in the hundreds of thousands range. A good source for the first is [here][grcp], and for the second, [here][rint]. You may find posts online written by people recommending millions of iterations for PBKDF2, but they’re generally talking about this in the context of memorizable passwords, where adding even one more character to the password is a significant burden. Given our script’s purely random maximum-length passphrase, there isn’t much more that increasing the key derivation iteration count can do for us. Conversely, if you were to reduce the passphrase to 41 characters, that would drop the key strength by roughly 2â¶, being the entropy value per character for using most of printable ASCII in our passphrase. To make that lost strength up on the PBKDF2 end, you’d have to multiply your iterations by 2â¶Â = 64 times. It’s easier to use a max-length passphrase in this situation than get crazy with key derivation iteration counts. (This, by the way, is why the example passphrase above is 42 characters: with 6 bits of entropy per character, that gives you a key size of 252, as close as we can get to our chosen encryption algorithm’s 256-bit key size without going over. If it pleases you to give it 43 random characters for a passphrase in order to pick up those last four bits of security, you’re welcome to do so.) Compressing the data before encrypting it removes redundancies that can make decryption easier, and it results in a smaller backup than you get with the previous script alone, at the expense of a lot of CPU time during the backup. You may wish to switch to a less space-efficient compression algorithm that takes less CPU power, such as [`lz4`][lz4]. Changing up the compression algorithm also provides some security-thru-obscurity, which is useless on its own, but it *is* a useful adjunct to strong encryption. This requires OpenSSL 1.1 or higher. If you’re on 1.0 or older, you won’t have the `-pbkdf2` and `-iter` options, and you may have to choose a different cipher algorithm; both changes are likely to weaken the encryption significantly, so you should install a newer version rather than work around the lack of these features. Beware that macOS ships a fork of OpenSSL called [LibreSSL][lssl] that lacked this capability until Ventura (13.0). If you’re on Monterey (12) or older, we recommend use of the [Homebrew][hb] OpenSSL package rather than give up on the security afforded by use of configurable-iteration PBKDF2. To avoid a conflict with the platform’s `openssl` binary, Homebrew’s installation is [unlinked][hbul] by default, so you have to give an explicit path to it, one of: /usr/local/opt/openssl/bin/openssl ... # Intel x86 Macs /opt/homebrew/opt/openssl/bin/openssl ... # ARM Macs (“Apple siliconâ€) [lssl]: https://www.libressl.org/ ## <a id="rest"></a> Restoring From An Encrypted Backup The “restore†script for the above fragment is basically an inverse of it, but it’s worth showing it because there are some subtleties to take care of. If all variables defined in earlier scripts are available, then restoration is: ``` openssl enc -d -aes-256-cbc -pbkdf2 -iter $iter -pass pass:"$pass" -in "$gd" | xz -d | fossil sql --no-repository ~/museum/restored-repo.fossil ``` We changed the `-e` to `-d` on the `openssl` command to get decryption, and we changed the `-out` to `-in` so it reads from the encrypted backup file and writes the result to stdout. The decompression step is trivial. The last change is tricky: we used `fossil sql` above to ensure that we’re using the same version of SQLite to write the encrypted backup DB as was used to maintain the repository. We must also do that on restoration: Fossil serves as a dogfooding project for SQLite, often making use of the latest features, so it is quite likely that a given random `sqlite3` binary in your `PATH` will be unable to understand the file created by “`fossil sql .dump`â€! The tricky bit is, you can’t just pipe the decrypted SQL dump into `fossil sql`, because on startup, Fossil normally goes looking for tables created by `fossil init`, and it won’t find them in a newly-created repo DB. We get around this by passing the `--no-repository` flag, which suppresses this behavior. Doing it this way saves you from needing to go and build a matching version of `sqlite3` just to restore the backup. [bu]: /help?cmd=backup [grcp]: https://www.grc.com/passwords.htm [hb]: https://brew.sh [hbul]: https://docs.brew.sh/FAQ#what-does-keg-only-mean [lz4]: https://lz4.github.io/lz4/ [pbr]: ./private.wiki [rint]: https://www.random.org/integers/?num=1&min=100000&max=1000000&col=5&base=10&format=html&rnd=new [Setup]: ./caps/admin-v-setup.md#apsu [shun]: ./shunning.wiki [tkt]: ./tickets.wiki [uv]: ./unvers.wiki |
Added www/blame.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 | <title>The Annotate Algorithm</title> <h2>1.0 Introduction</h2> The [/help?cmd=annotate|fossil annotate], [/help?cmd=blame|fossil blame], and [/help?cmd=praise|fossil praise] commands, and the [/help?cmd=/annotate|/annotate], [/help?cmd=/blame|/blame], and [/help?cmd=/praise|/praise] web pages are all used to show the most recent check-in that modified each line of a particular file. This article overviews the algorithm used to compute the annotation for a file in Fossil. <h2>2.0 Algorithm</h2> <ol type='1'> <li>Locate the check-in that contains the file that is to be annotated. Call this check-in C0. <li>Find all direct ancestors of C0. A direct ancestor is the closure of the primary parent of C0. Merged in branches are not part of the direct ancestors of C0. <li>Prune the list of ancestors of C0 so that it contains only check-in in which the file to be annotated was modified. <li>Load the complete text of the file to be annotated from check-in C0. Call this version of the file F0. <li>Parse F0 into lines. Mark each line as "unchanged". <li>For each ancestor of C0 on the pruned list (call the ancestor CX), beginning with the most recent ancestor and moving toward the oldest ancestor, do the following steps: <ol type='a'> <li>Load the text for the file to be annotated as it existed in check-in CX. Call this text FX. <li>Compute a diff going from FX to F0. <li>For each line of F0 that is changed in the diff and which was previously marked "unchanged", update the mark to indicated that line was modified by CX. </ol> <li>Show each line of F0 together with its change mark, appropriately formatted. </ol> <h2>3.0 Discussion and Notes</h2> The time-consuming part of this algorithm is step 6b - computing the diff from all historical versions of the file to the version of the file under analysis. For a large file that has many historical changes, this can take several seconds. For this reason, the default [/help?cmd=/annotate|/annotate] webpage only shows those lines that where changed by the 20 most recent modifications to the file. This allows the loop on step 6 to terminate after only 19 diffs instead of the hundreds or thousands of diffs that might be required for a frequently modified file. As currently implemented (as of 2015-12-12) the annotate algorithm does not follow files across name changes. File name change information is available in the database, and so the algorithm could be enhanced to follow files across name changes by modifications to step 3. Step 2 is interesting in that it is [/artifact/6cb824a0417?ln=196-201 | implemented] using a [https://www.sqlite.org/lang_with.html#recursivecte|recursive common table expression]. |
Added www/blockchain.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 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 | # Is Fossil A Blockchain? The Fossil version control system shares a lot of similarities with other blockchain based technologies, but it also differs from the more common sorts of blockchains. This document will discuss the term’s applicability, so you can decide whether applying the term to Fossil makes sense to you. ## The Dictionary Argument The [Wikipedia definition of "blockchain"][bcwp] begins: > "A blockchain…is a growing list of records, called blocks, which are linked using cryptography… Each block contains a cryptographic hash of the previous block, a timestamp, and transaction data (generally represented as a Merkle tree)." Point-for-point, Fossil follows this partial definition. The blocks are Fossil’s ["manifest" artifacts](./fileformat.wiki#manifest). Each manifest has a cryptographically-strong [SHA-1] or [SHA-3] hash linking it to one or more “parent†blocks. The manifest also contains a timestamp and the transactional data needed to express a commit to the repository. To traverse the Fossil repository from the tips of its [DAG] to the root by following the parent hashes in each manifest is to traverse a Merkle tree. Every change in Fossil starts by adding one or more manifests to the repository, extending this Merkle tree. [bcwp]: https://en.wikipedia.org/wiki/Blockchain [DAG]: https://en.wikipedia.org/wiki/Directed_acyclic_graph [SHA-1]: https://en.wikipedia.org/wiki/SHA-1 [SHA-3]: https://en.wikipedia.org/wiki/SHA-3 <a id="currency"></a> ## Cryptocurrency Because blockchain technology was first popularized as Bitcoin, many people associate the term with cryptocurrency. Fossil has nothing to do with cryptocurrency, so a claim that “Fossil is a blockchain†may fail to communicate the speaker’s concepts clearly due to conflation with cryptocurrency. Cryptocurrency has several features and requirements that Fossil doesn’t provide, either because it doesn’t need them or because we haven’t gotten around to creating the feature. Whether these are essential to the definition of “blockchain†and thus disqualify Fossil as a blockchain is for you to decide. Cryptocurrencies must prevent three separate types of fraud to be useful: * **Type 1** is modification of existing currency. To draw an analogy to paper money, we wish to prevent someone from using green and black markers to draw extra zeroes on a US $10 bill so that it claims to be a $100 bill. * **Type 2** is creation of new fraudulent currency that will pass in commerce. To extend our analogy, it is the creation of new US $10 bills. There are two sub-types to this fraud. In terms of our analogy, they are: * **Type 2a**: copying an existing legitimate $10 bill<br><br> * **Type 2b**: printing a new $10 bill that is unlike an existing legitimate one, yet which will still pass in commerce * **Type 3** is double-spending existing legitimate cryptocurrency. There is no analogy in paper money due to its physical form; it is a problem unique to digital currency due to its infinitely-copyable nature. How does all of this compare to Fossil? 1. <a id="signatures"></a>**Signatures.** Cryptocurrencies use a chain of [digital signatures][dsig] to prevent Type 1 and Type 3 frauds. This chain forms an additional link between the blocks, separate from the hash chain that applies an ordering and lookup scheme to the blocks. [_Blockchain: Simple Explanation_][bse] explains this “hash chain†vs. “block chain†distinction in more detail. These signatures prevent modification of the face value of each transaction (Type 1 fraud) by ensuring that only the one signing a new block has the private signing key that could change an issued block after the fact. The fact that these signatures are also *chained* prevents Type 3 frauds by making the *prior* owner of a block sign it over to the new owner. To avoid an O(n²) auditing problem as a result, cryptocurrencies add a separate chain of hashes to make checking for double-spending quick and easy. Fossil has [a disabled-by-default feature][cs] to call out to an external copy of [PGP] or [GPG] to sign commit manifests before inserting them into the repository. You can couple that with a server-side [after-receive hook][arh] to reject unsigned commits. Although there are several distinctions you can draw between the way Fossil’s commit signing scheme works and the way block signing works in cryptocurrencies, only one is of material interest for our purposes here: Fossil commit signatures apply only to a single commit. Fossil does not sign one commit over to the next “owner†of that commit in the way that a blockchain-based cryptocurrency must when transferring currency from one user to another, beacuse there is no useful analog to the double-spending problem in Fossil. The closest you can come to this is double-insert of commits into the blockchain, which we’ll address shortly. What Fossil commit signatures actually do is provide in-tree forgery prevention, both Type 1 and Type 2. You cannot modify existing commits (Type 1 forgery) because you do not have the original committer’s private signing key, and you cannot forge new commits attesting to come from some other trusted committer (Type 2) because you don’t have any of their private signing keys, either. Cryptocurrencies use the work problem to prevent Type 2 forgeries, but the application of that to Fossil is a matter we get to [later](#work). Although you have complete control over the contents of your local Fossil repository clone, you cannot perform Type 1 forgery on its contents short of executing a [preimage attack][prei] on the hash algorithm. ([SHA3-256][SHA-3] by default in the current version of Fossil.) Even if you could, Fossil’s sync protocol will prevent the modification from being pushed into another repository: the remote Fossil instance says, “I’ve already got that one, thanks,†and ignores the push. Thus, short of breaking into the remote server and modifying the repository in place, you couldn’t make use of a preimage attack even if you had that power. Further, that would be an attack on the server itself, not on Fossil’s data structures, so while it is useful to think through this problem, it is not helpful in answering our questions here. The Fossil sync protocol’s duplication detection also prevents the closest analog to Type 3 frauds in Fossil: copying a commit manifest in your local repo clone won’t result in a double-commit on sync. In the absence of digital signatures, Fossil’s [RBAC system][caps] restricts Type 2 forgery to trusted committers. Thus once again we’re reduced to an infosec problem, not a data structure design question. (Inversely, enabling commit clearsigning is a good idea if you have committers on your repo whom you don’t trust not to commit Type 2 frauds. But let us be clear: your choice of setting does not answer the question of whether Fossil is a blockchain.) If Fossil signatures prevent Type 1 and Type 2 frauds, you may wonder why they are not enabled by default. It is because they are defense-in-depth measures, not the minimum sufficient measures needed to prevent repository fraud, unlike the equivalent protections in a cryptocurrency blockchain. Fossil provides its primary protections through other means, so it doesn’t need to mandate signatures. Also, Fossil is not itself a [PKI], and there is no way for regular users of Fossil to link it to a PKI, since doing so would likely result in an unwanted [PII] disclosure. There is no email address in a Fossil commit manifest that you could use to query one of the public PGP keyservers, for example. It therefore becomes a local policy matter as to whether you even *want* to have signatures, because they’re not without their downsides. 2. <a id="work"></a>**Work Contests.** Cryptocurrencies prevent Type 2b forgeries by setting up some sort of contest that ensures that new coins can come into existence only by doing some difficult work task. This “mining†activity results in a coin that took considerable work to create, which thus has economic value by being a) difficult to re-create, and b) resistant to [debasement][dboc]. Fossil repositories are most often used to store the work product of individuals, rather than cryptocoin mining machines. There is generally no contest in trying to produce the most commits. There may be an implicit contest to produce the “best†commits, but that is a matter of project management, not something that can be automatically mediated through objective measures. Incentives to commit to the repository come from outside of Fossil; they are not inherent to its nature, as with cryptocurrencies. Moreover, there is no useful sense in which we could say that one commit “re-creates†another. Commits are generally products of individual human intellect, thus necessarily unique in all but trivial cases. This is foundational to copyright law. 3. <a id="lcr"></a>**Longest Chain Rule.** Cryptocurrencies generally need some way to distinguish which blocks are legitimate and which not. They do this in part by identifying the linear chain with the greatest cumulative [work time](#work) as the legitimate chain. All blocks not on that linear chain are considered “orphans†and are ignored by the cryptocurrency software. Its inverse is sometimes called the “51% attack†because a single actor would have to do slightly more work than the entire rest of the community using a given cryptocurrency in order for their fork of the currency to be considered the legitimate fork. This argument soothes concerns that a single bad actor could take over the network. The closest we can come to that notion in Fossil is the default “trunk†branch, but there’s nothing in Fossil that delegitimizes other branches just because they’re shorter, nor is there any way in Fossil to score the amount of work that went into a commit. Indeed, [forks and branches][fb] are *valuable and desirable* things in Fossil. This much is certain: Fossil is definitely not a cryptocurrency. Whether this makes it “not a blockchain†is a subjective matter. [arh]: ./hooks.md [bse]: https://www.researchgate.net/publication/311572122_What_is_Blockchain_a_Gentle_Introduction [caps]: ./caps/ [cs]: /help?cmd=clearsign [dboc]: https://en.wikipedia.org/wiki/Debasement [dsig]: https://en.wikipedia.org/wiki/Digital_signature [fb]: ./branching.wiki [GPG]: https://gnupg.org/ [PGP]: https://www.openpgp.org/ [PII]: https://en.wikipedia.org/wiki/Personal_data [PKI]: https://en.wikipedia.org/wiki/Public_key_infrastructure [pow]: https://en.wikipedia.org/wiki/Proof_of_work [prei]: https://en.wikipedia.org/wiki/Preimage_attack <a id="dlt"></a> ## Distributed Ledgers Cryptocurrencies are an instance of [distributed ledger technology][dlt]. If we can convince ourselves that Fossil is also a distributed ledger, then we might think of Fossil as a peer technology, having at least some qualifications toward being considered a blockchain. A key tenet of DLT is that records be unmodifiable after they’re committed to the ledger, which matches quite well with Fossil’s design and everyday use cases. Fossil puts up multiple barriers to prevent modification of existing records and injection of incorrect records. Yet, Fossil also has [purge] and [shunning][shun]. Doesn’t that mean Fossil cannot be a distributed ledger? These features only remove existing commits from the repository. If you want a currency analogy, they are ways to burn a paper bill or to melt a [fiat coin][fc] down to slag. In a cryptocurrency, you can erase your “wallet†file, effectively destroying money in a similar way. These features do not permit forgery of either type described above: you can’t use them to change the value of existing commits (Type 1) or add new commits to the repository (Type 2). What if we removed those features from Fossil, creating an append-only Fossil variant? Is it a DLT then? Arguably still not, because [today’s Fossil is an AP-mode system][ctap], which means there can be no guaranteed consensus on the content of the ledger at any given time. An AP-mode accounts receivable system would allow different bottom-line totals at different sites, because you’ve cast away “C†to get AP-mode operation. (See the prior link or [Wikipedia’s article on the CAP theorem][cap] if you aren’t following this terminology.) By the same token, you cannot guarantee that the command “`fossil info tip`†gives the same result everywhere. You would need to recast Fossil as a CA or CP-mode system to solve that. (Everyone not partitioned away from the majority of the network at any rate, in the CP case.) What are the prospects for CA-mode or CP-mode Fossil? [We don’t want CA-mode Fossil][ctca], but [CP-mode could be useful][ctcp]. Until the latter exists, this author believes Fossil is not a distributed ledger in a technologically defensible sense. The most common technologies answering to the label “blockchain†are all DLTs, so if Fossil is not a DLT, then it is not a blockchain in that sense. [ctap]: ./cap-theorem.md#ap [ctca]: ./cap-theorem.md#ca [ctcp]: ./cap-theorem.md#cp [cap]: https://en.wikipedia.org/wiki/CAP_theorem [dlt]: https://en.wikipedia.org/wiki/Distributed_ledger [DVCS]: https://en.wikipedia.org/wiki/Distributed_version_control [fc]: https://en.wikipedia.org/wiki/Fiat_money [purge]: /help?cmd=purge [shun]: ./shunning.wiki <a id="dpc"></a> ## Distributed Partial Consensus If we can’t get DLT, can we at least get some kind of distributed consensus at the level of individual Fossil’s commits? Many blockchain based technologies have this property: given some element of the blockchain, you can make certain proofs that it either is a legitimate part of the whole blockchain, or it is not. Unfortunately, this author doesn’t see a way to do that with Fossil. Given only one “block†in Fossil’s putative “blockchain†— a commit, in Fossil terminology — all you can prove is whether it is internally consistent, that it is not corrupt. That then points you at the parent(s) of that commit, which you can repeat the exercise on, back to the root of the DAG. This is what the enabled-by-default [`repo-cksum` setting][rcks] does. If cryptocurrencies worked this way, you wouldn’t be able to prove that a given cryptocoin was legitimate without repeating the proof-of-work calculations for the entire cryptocurrency scheme! Instead, you only need to check a certain number of signatures and proofs-of-work in order to be reasonably certain that you are looking at a legitimate section of the whole blockchain. What would it even mean to prove that a given Fossil commit “*belongs*†to the repository you’ve extracted it from? For a software project, isn’t that tantamount to automatic code review, where the server would be able to reliably accept or reject a commit based solely on its content? That sounds nice, but this author believes we’ll need to invent [AGI] first. A better method to provide distributed consensus for Fossil would be to rely on the *natural* intelligence of its users: that is, distributed commit signing, so that a commit is accepted into the blockchain only once some number of users countersign it. This amounts to a code review feature, which Fossil doesn’t currently have. Solving that problem basically requires solving the [PKI] problem first, since you can’t verify the proofs of these signatures if you can’t first prove that the provided signatures belong to people you trust. This is a notoriously hard problem in its own right. A future version of Fossil could instead provide [consensus in the CAP sense][ctcp]. For instance, you could say that if a quorum of servers all have a given commit, it “belongs.†Fossil’s strong hashing tech would mean that querying whether a given commit is part of the “blockchain†would be as simple as going down the list of servers and sending each an HTTP GET `/info` query for the artifact ID, concluding that the commit is legitimate once you get enough HTTP 200 status codes back. All of this is hypothetical, because Fossil doesn’t do this today. [AGI]: https://en.wikipedia.org/wiki/Artificial_general_intelligence [rcks]: /help?cmd=repo-cksum <a id="anon"></a> ## Anonymity Many blockchain based technologies go to extraordinary lengths to allow anonymous use of their service. As typically configured, Fossil does not: commits synced between servers always at least have a user name associated with them, which the remote system must accept through its [RBAC system][caps]. That system can run without having the user’s email address, but it’s needed if [email alerts][alert] are enabled on the server. The remote server logs the IP address of the commit for security reasons. That coupled with the timestamp on the commit could sufficiently deanonymize users in many common situations. It is possible to configure Fossil so it doesn’t do this: * You can give [Write capability][capi] to user category “nobody,†so that anyone that can reach your server can push commits into its repository. * You could give that capability to user category “anonymous†instead, which requires that the user log in with a CAPTCHA, but which doesn’t require that the user otherwise identify themselves. * You could enable [the `self-register` setting][sreg] and choose not to enable [commit clear-signing][cs] so that anonymous users could push commits into your repository under any name they want. On the server side, you can also [scrub] the logging that remembers where each commit came from. Commit source info isn’t transmitted from the remote server on clone or pull: the size of the `rcvfrom` table after initial clone is 1, containing only the remote server’s IP address. On each pull containing new artifacts, your local `fossil` instance adds another entry to this table, likely with the same IP address unless the server has moved or you’re using [multiple remotes][mrep]. This table is far more interesting on the server side, containing the IP addresses of all contentful pushes; thus [the `scrub` command][scrub]. Because Fossil doesn’t remember IP addresses in commit manifests or require commit signing, it allows at least *pseudonymous* commits. When someone clones a remote repository, they don’t learn the email address, IP address, or any other sort of [PII] of prior committers, on purpose. Some people say that private, permissioned blockchains (as you may imagine Fossil to be) are inherently problematic by the very reason that they don’t bake anonymous contribution into their core. The very existence of an RBAC is a moving piece that can break. Isn’t it better, the argument goes, to have a system that works even in the face of anonymous contribution, so that you don’t need an RBAC? Cryptocurrencies do this, for example: anyone can “mine†a new coin and push it into the blockchain, and there is no central authority restricting the transfer of cryptocurrency from one user to another. We can draw an analogy to encryption, where an algorithm is considered inherently insecure if it depends on keeping any information from an attacker other than the key. Encryption schemes that do otherwise are derided as “security through obscurity.†You may be wondering what any of this has to do with whether Fossil is a blockchain, but that is exactly the point: all of this is outside Fossil’s core hash-chained repository data structure. If you take the position that you don’t have a “blockchain†unless it allows anonymous contribution, with any needed restrictions provided only by the very structure of the managed data, then Fossil does not qualify. Why do some people care about this distinction? Consider Bitcoin, wherein an anonymous user cannot spam the blockchain with bogus coins because its [proof-of-work][pow] protocol allows such coins to be rejected immediately. There is no equivalent in Fossil: it has no technology that allows the receiving server to look at the content of a commit and automatically judge it to be “good.†Fossil relies on its RBAC system to provide such distinctions: if you have a commit bit, your commits are *ipso facto* judged “good,†insofar as any human work product can be so judged by a blob of compiled C code. This takes us back to the [digital ledger question](#dlt), where we can talk about what it means to later correct a bad commit that got through the RBAC check. We may be willing to accept pseudonymity, rather than full anonymity. If we configure Fossil as above, either bypassing the RBAC or abandoning human control over it, scrubbing IP addresses, etc., is it then a public permissionless blockchain in that sense? We think not, because there is no [longest chain rule](#lcr) or anything like it in Fossil. For a fair model of how a Fossil repository might behave under such conditions, consider GitHub: here one user can fork another’s repository and make an arbitrary number of commits to their public fork. Imagine this happens 10 times. How does someone come along later and *automatically* evaluate which of the 11 forks of the code (counting the original repository among their number) is the “best†one? For a computer software project, the best we could do to approximate this devolves to a [software project cost estimation problem][scost]. These methods are rather questionable in their own right, being mathematical judgement values on human work products, but even if we accept their usefulness, then we still cannot say which fork is better based solely on their scores under these metrics. We may well prefer to use the fork of a software program that took *less* effort, being smaller, more self-contained, and with a smaller attack surface. [alert]: ./alerts.md [capi]: ./caps/ref.html#i [mrep]: /help?cmd=remote [scost]: https://en.wikipedia.org/wiki/Software_development_effort_estimation [scrub]: /help?cmd=scrub [sreg]: /help?cmd=self-register # Conclusion This author believes it is technologically indefensible to call Fossil a “blockchain†in any sense likely to be understood by a majority of those you’re communicating with. Using a term in a nonstandard way just because you can defend it means you’ve failed any goal that requires clear communication. The people you’re communicating your ideas to must have the same concept of the terms you use. What term should you use instead? Fossil stores a DAG of hash-chained commits, so an indisputably correct term is a [Merkle tree][mt], named after [its inventor][drrm]. You could also use the more generic term “hash tree.†Fossil is a technological peer to many common sorts of blockchain technology. There is a lot of overlap in concepts and implementation details, but when speaking of what most people understand as “blockchain,†Fossil is not that. [drrm]: https://en.wikipedia.org/wiki/Ralph_Merkle [mt]: https://en.wikipedia.org/wiki/Merkle_tree |
Deleted www/branch01.gif.
cannot compute difference between binary files
Deleted www/branch02.gif.
cannot compute difference between binary files
Deleted www/branch03.gif.
cannot compute difference between binary files
Deleted www/branch04.gif.
cannot compute difference between binary files
Deleted www/branch05.gif.
cannot compute difference between binary files
Changes to www/branching.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 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 | <title>Branching, Forking, Merging, and Tagging</title> <h2>Background</h2> In a simple and perfect world, the development of a project would proceed linearly, as shown in Figure 1. <verbatim type="pikchr center toggle"> ALL: [circle rad 0.1in thickness 1.5px "1" arrow right 40% circle same "2" arrow same circle same "3" arrow same circle same "4"] box invis "Figure 1" big fit with .n at .3cm below ALL.s </verbatim> Each circle represents a check-in. For the sake of clarity, the check-ins are given small consecutive numbers. In a real system, of course, the check-in numbers would be long hexadecimal hashes since it is not possible to allocate collision-free sequential numbers in a distributed system. But as sequential numbers are easier to read, we will substitute them for the long 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. <h2 id="dag">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: <verbatim type="pikchr center toggle"> ALL: [circle rad 0.1in thickness 1.5px "1" arrow right 40% circle same "2" circle same "3" at 2nd circle+(.4,.3) arrow from 2nd circle to 3rd circle chop circle same "4" at 2nd circle+(.4,-.3) arrow from 2nd circle to 4th circle chop] box invis "Figure 2" big fit with .n at .3cm below ALL.s </verbatim> 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, primarily through its "[./concepts.wiki#workflow | autosync]" mechanism. Suppose two programmers named Alice and Bob are each editing check-in 2 separately. Alice finishes her edits and commits her changes first, resulting in check-in 3. When Bob later 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" to pull 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 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 <b>[/help?cmd=commit | fossil commit]</b> command. For any of these reasons, two commits against check-in 2 have occurred, so the DAG now has two leaves. In such a condition, a person working with this repository has a dilemma: 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, which is why we would ideally prefer to have linear check-in graphs. Fossil resolves such problems using the check-in time on the leaves to decide which leaf to use as the parent of new leaves. When a branch is forked as in Figure 2, Fossil will choose check-in 4 as the parent for a later check-in 5, but <i>only</i> if it has sync'd that check-in down into the local repository. If autosync is disabled or the user is off-network when that fifth check-in occurs so that check-in 3 is the latest on that branch at the time within that clone of the repository, Fossil will make check-in 3 the parent of check-in 5! We show practical consequences of this [#bad-fork | later in this article]. Fossil also uses a forked branch's leaf check-in timestamps when checking out that branch: it gives you the fork with the latest check-in, which in turn selects which parent your next check-in will be a child of. This situation means development on that branch can fork into two independent lines of development, based solely on which branch tip is newer at the time the next user starts his work on it. Because of these potential problems, we strongly recommend that you do not intentionally create forks on long-lived shared working branches with "--allow-fork". (Prime example: trunk.) The inverse case — intentional forks on short-lived single-developer branches — is far easier to justify, since presumably the lone developer is never confused about why there are two or more leaves on that branch. Further justifications for intentional forking are [#forking | given below]. Let us return to Figure 2. To resolve such situations before they can become a real problem, Alice can use the <b>[/help?cmd=merge | fossil merge]</b> command to merge Bob's changes into her local copy of check-in 3. Without arguments, that command merges all leaves on the current branch. Alice can then verify that the merge is sensible and if so, commit the results as check-in 5. This results in a DAG as shown in Figure 3. <verbatim type="pikchr center toggle"> ALL: [circle rad 0.1in thickness 1.5px "1" arrow right 40% circle same "2" circle same "3" at 2nd circle+(.4,.3) arrow from 2nd circle to 3rd circle chop circle same "4" at 2nd circle+(.4,-.3) arrow from 2nd circle to 4th circle chop circle same "5" at 3rd circle+(.4,-.3) arrow from 3rd circle to 5th circle chop arrow dashed .03 from 4th circle to 5th circle chop] box invis "Figure 3" big fit with .n at .2cm below ALL.s </verbatim> Check-in 5 is a child of check-in 3 because it was created by editing check-in 3, but since check-in 5 also inherits the changes from check-in 4 by virtue of the merge, 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, the fork would have never occurred. The resulting graph would have been linear, as shown in Figure 1. Realize that the graph of Figure 1 is a subset of Figure 3. If you hold your hand over the ④ in Figure 3, it looks exactly like Figure 1 except that the leaf has a different check-in number. That is just a notational difference: the two check-ins have exactly the same content. Inversely, Figure 3 is a superset of Figure 1. The check-in 4 of Figure 3 captures additional state which is omitted from Figure 1. Check-in 4 of Figure 3 holds a copy of Bob's local checkout before he merged in Alice's changes. That snapshot of Bob's changes, which is independent of Alice's changes, is omitted from Figure 1. Some people say that the development approach taken in Figure 3 is better because it preserves this extra intermediate state. Others say that the approach taken in Figure 1 is better because it is much easier to visualize linear 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 id="branching">The Alternative to Forking: 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. <verbatim type="pikchr center toggle"> ALL: [circle rad 0.1in thickness 1.5px fill white "1" arrow 40% C2: circle same "2" arrow same circle same "3" arrow same C5: circle same "5" arrow same C7: circle same "7" arrow same C8: circle same "8" arrow same C10: circle same "10" C4: circle same at 3rd circle-(0,.35) "4" C6: circle same at (1/2 way between C5 and C7,C4) "6" C9: circle same at (1/2 way between C8 and C10,C4) "9" arrow from C2 to C4 chop arrow from C4 to C6 chop arrow from C6 to C9 chop arrow dashed 0.03 from C6 to C7 chop arrow same from C9 to C10 layer = 0 box fill 0x9bcdfc color 0x9bcdfc wid (C10.e.x - C2.w.x) ht C6.height*1.5 at C6.c box invis "test" fit with .sw at last box.sw] box invis "Figure 4" big with .n at 0 below ALL.s </verbatim> Figure 4 diagrams the following scenario: 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 with 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. In both Figures 2 and 4, check-in 2 has two children. In Figure 2, we call this a "fork." In diagram 4, we call it a "branch." What is the difference? As far as the internal Fossil data structures are concerned, there is no difference. The distinction is in the intent. In Figure 2, the fact that check-in 2 has multiple children is an accident that stems from concurrent development. In Figure 4, giving check-in 2 multiple children is a deliberate act. To a good approximation, we define forking to be by accident and branching to be by intent. Apart from that, they are the same. When the fork is intentional, it helps humans to understand what is going on if we <i>name</i> the forks. This is not essential to Fossil's internal data model, but humans have trouble working with long-lived branches identified only by the commit ID currently at its tip, being a long string of hex digits. Therefore, Fossil conflates two concepts: branching as intentional forking and the naming of forks as branches. They are in fact separate concepts, but since Fossil is intended to be used primarily by humans, we combine them in Fossil's human user interfaces. <p class="blockquote"> <b>Key Distinction:</b> A branch is a <i>named, intentional</i> fork. </p> Unnamed forks <i>may</i> be intentional, but most of the time, they're accidental and left unnamed. Fossil offers two primary ways to create named, intentional forks, a.k.a. branches. First: <pre> $ fossil commit --branch my-new-branch-name </pre> This is the method we recommend for most cases: it creates a branch as part of a check-in using the version in the current checkout directory as its basis. (This is normally the tip of the current branch, though it doesn't have to be. You can create a branch from an ancestor check-in on a branch as well.) After making this branch-creating check-in, your local working directory is switched to that branch, so that further check-ins occur on that branch as well, as children of the tip check-in on that branch. The second, more complicated option is: <pre> $ fossil branch new my-new-branch-name trunk $ fossil update my-new-branch-name $ fossil commit </pre> Not only is this three commands instead of one, the first of which is longer than the entire simpler command above, you must give the second command before creating any check-ins, because until you do, your local working directory remains on the same branch it was on at the time you issued the command, so that the commit would otherwise put the new material on the original branch instead of the new one. In addition to those problems, the second method is a violation of the [https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it|YAGNI Principle]. We recommend that you wait until you actually need the branch before you create it using the first command above. The "trunk" is just another named branch in Fossil. It is simply the default branch name for the first check-in and every check-in made as one of its direct descendants. It is special only in that it is Fossil's default when it has no better idea of which branch you mean. <h2 id="forking">Justifications For Forking</h2> The primary cases where forking is justified over branching are all when it is done purely in software in order to avoid losing information: <ol> <li><p id="offline">By Fossil itself when two users check in children to the same leaf of a branch, as in Figure 2. <br><br> If the fork occurs because autosync is disabled on one or both of the repositories or because the user doing the check-in has no network connection at the moment of the commit, Fossil has no way of knowing that it is creating a fork until the two repositories are later synchronized.</p></li> <li><p id="dist-clone">By Fossil when the cloning hierarchy is more than 2 levels deep. <br><br> [./sync.wiki|Fossil's synchronization protocol] is a two-party negotiation; syncs don't automatically propagate up the clone tree beyond that. Because of that, if you have a master repository and Alice clones it, then Bobby clones from Alice's repository, a check-in by Bobby that autosyncs with Alice's repo will <i>not</i> also autosync with the master repo. The master doesn't get a copy of Bobby's check-in until Alice <i>separately</i> syncs with the master. If Carol cloned from the master repo and checks something in that creates a fork relative to Bobby's check-in, the master repo won't know about that fork until Alice syncs her repo with the master. Even then, realize that Carol still won't know about the fork until she subsequently syncs with the master repo. <br><br> One way to deal with this is to just accept it as a fact of using a [https://en.wikipedia.org/wiki/Distributed_version_control|Distributed Version Control System] like Fossil. <br><br> Another option, which we recommend you consider carefully, is to make it a local policy that check-ins be made only directly against the master repo or one of its immediate child clones so that the autosync algorithm can do its job most effectively. Any clones deeper than that should be treated as read-only and thus get a copy of the new state of the world only once these central repos have negotiated that new state. This policy avoids a class of inadvertent fork you might not need to tolerate. Since [#bad-fork|forks on long-lived shared working branches can end up dividing a team's development effort], a team may easily justify this restriction on distributed cloning.</p></li> <li><p id="automation">You've automated Fossil, so you use <b>fossil commit --allow-fork</b> commands to prevent Fossil from refusing the check-in simply because it would create a fork. <br><br> If you are writing such a tool — e.g. a shell script to make multiple manipulations on a Fossil repo — it's better to make it smart enough to detect this condition and cope with it, such as by making a call to <b>[/help?cmd=update | fossil update]</b> and checking for a merge conflict. That said, if the alternative is losing information, you may feel justified in creating forks that an interactive user must later manually clean up with <b>fossil merge</b> commands.</p></li> </ol> That leaves only one case where we can recommend use of "--allow-fork" by interactive users: when you're working on a personal branch so that creating a dual-tipped branch isn't going to cause any other user an inconvenience or risk [#bad-fork | inadvertently forking the development effort]. In such a case, the lone developer working on that branch is not confused, since the fork in development is intentional. Sometimes it simply makes no sense to bother creating a name, cluttering the global branch namespace, simply to convert an intentional fork into a "branch." This is especially the case when the fork is short-lived. There's a common generalization of that case: you're a solo developer, so that the problems with branching vs forking simply don't matter. In that case, feel free to use "--allow-fork" as much as you like. <h2 id="fix">Fixing Forks</h2> If your local checkout is on a forked branch, you can usually fix a fork automatically with: <pre> $ fossil merge </pre> Normally you need to pass arguments to <b>fossil merge</b> to tell it what you want to merge into the current basis view of the repository, but without arguments, the command seeks out and fixes forks. <h2 id="tags">Tags And Properties</h2> Tags and properties are used in Fossil to help express the intent, and thus to distinguish between forks and branches. Figure 5 shows the same scenario as Figure 4 but with tags and properties added: <verbatim type="pikchr center toggle"> ALL: [arrowht = 0.07 C1: circle rad 0.1in thickness 1.5px fill white "1" arrow 40% C2: circle same "2" arrow same circle same "3" arrow same C5: circle same "5" arrow same C7: circle same "7" arrow same C8: circle same "8" arrow same C10: circle same "10" C4: circle same at 3rd circle-(0,.35) "4" C6: circle same at (1/2 way between C5 and C7,C4) "6" C9: circle same at (1/2 way between C8 and C10,C4) "9" arrow from C2 to C4 chop arrow from C4 to C6 chop arrow from C6 to C9 chop arrow dashed 0.03 from C6 to C7 chop arrow same from C9 to C10 layer = 0 box fill 0x9bcdfc color 0x9bcdfc wid (C10.e.x - C2.w.x) ht C6.height*1.5 at C6.c text " test" above ljust at last box.sw box fill lightgray "branch=trunk" "sym-trunk" fit with .ne at C1-(0.05,0.3); line color gray from last box.ne to C1 chop box same "branch=test" "sym-test" "bgcolor=blue" "cancel=sym-trunk" fit \ with .n at C4-(0,0.3) line color gray from last box.n to C4 chop box same "sym-release-1.0" "closed" fit with .n at C9-(0,0.3) line color gray from last box.n to C9 chop] box invis "Figure 5" big fit with .n at 0.2cm below ALL.s </verbatim> 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 hexadecimal 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 "trunk" branch and to have the symbolic name "trunk." Check-in 4 has a <b>branch</b> tag which changes the name of the branch to "test." The branch tag on check-in 4 propagates to check-ins 6 and 9. But because tag propagation does not follow merge links, the <b>branch=test</b> tag does not propagate to check-ins 7, 8, or 10. Note also that the <b>branch</b> tag on check-in 4 blocks the propagation of <b>branch=trunk</b> 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 id="bad-fork">How Can Forks Divide Development Effort?</h2> [#dist-clone|Above], we stated that forks carry a risk that development effort on a branch can be divided among the forks. It might not be immediately obvious why this is so. To see it, consider this swim lane diagram: <verbatim type="pikchr center toggle"> $laneh = 0.75 ALL: [ # Draw the lanes down box width 3.5in height $laneh fill 0xacc9e3 box same fill 0xc5d8ef box same as first box box same as 2nd box line from 1st box.sw+(0.2,0) up until even with 1st box.n \ "Alan" above aligned line from 2nd box.sw+(0.2,0) up until even with 2nd box.n \ "Betty" above aligned line from 3rd box.sw+(0.2,0) up until even with 3rd box.n \ "Charlie" above aligned line from 4th box.sw+(0.2,0) up until even with 4th box.n \ "Darlene" above aligned # fill in content for the Alice lane right A1: circle rad 0.1in at end of first line + (0.2,-0.2) \ fill white thickness 1.5px "1" arrow right 50% circle same "2" arrow right until even with first box.e - (0.65,0.0) ellipse "future" fit fill white height 0.2 width 0.5 thickness 1.5px A3: circle same at A1+(0.8,-0.3) "3" fill 0xc0c0c0 arrow from A1 to last circle chop "fork!" below aligned # content for the Betty lane B1: circle same as A1 at A1-(0,$laneh) "1" arrow right 50% circle same "2" arrow right until even with first ellipse.w ellipse same "future" B3: circle same at A3-(0,$laneh) "3" arrow right 50% circle same as A3 "4" arrow from B1 to 2nd last circle chop # content for the Charlie lane C1: circle same as A1 at B1-(0,$laneh) "1" arrow 50% circle same "2" arrow right 0.8in "goes" "offline" C5: circle same as A3 "5" arrow right until even with first ellipse.w \ "back online" above "pushes 5" below "pulls 3 & 4" below ellipse same "future" # content for the Darlene lane D1: circle same as A1 at C1-(0,$laneh) "1" arrow 50% circle same "2" arrow right until even with C5.w circle same "5" arrow 50% circle same as A3 "6" arrow right until even with first ellipse.w ellipse same "future" D3: circle same as B3 at B3-(0,2*$laneh) "3" arrow 50% circle same "4" arrow from D1 to D3 chop ] box invis "Figure 6" big fit with .n at 0.2cm below ALL.s </verbatim> This is a happy, cooperating team. That is an important restriction on our example, because you must understand that this sort of problem can arise without any malice, selfishness, or willful ignorance in sight. All users on this diagram start out with the same view of the repository, cloned from the same master repo, and all of them are working toward their shared vision of a unified future. All users, except possibly Alan, start out with the same two initial check-ins in their local working clones, 1 & 2. It might be that Alan starts out with only check-in 1 in his local clone, but we'll deal with that detail later. It doesn't matter which branch this happy team is working on, only that our example makes the most sense if you think of it as a long-lived shared working branch like trunk. Each user makes only one check-in, shaded light gray in the diagram. <h3 id="bf-alan">Step 1: Alan</h3> Alan sets the stage for this problem by creating a fork from check-in 1 as check-in 3. How and why Alan did this doesn't affect what happens next, though we will walk through the possible cases and attempt to assign blame [#post-mortem|in the <i>post mortem</i>]. For now, you can assume that Alan did this out of unavoidable ignorance. <h3 id="bf-betty">Step 2: Betty</h3> Because Betty's local clone is autosyncing with the same upstream repository as Alan's clone, there are a number of ways she can end up seeing Alan's check-in 3 as the latest on that branch: <ol> <li><p>The working check-out directory she's using at the moment was on a different branch at the time Alan made check-in 3, so Fossil sees that as the tip at the time she switches her working directory to that branch with a <b>fossil update $BRANCH</b> command. (There is an implicit autosync in that command, if the option was enabled at the time of the update.)</p></li> <li><p>The same thing, only in a fresh checkout directory with a <b>[/help?cmd=open | fossil open $REPO $BRANCH]</b> command.</p></li> <li><p>Alan makes his check-in 3 while Betty has check-in 1 or 2 as the tip in her local clone, but because she's working with an autosync'd connection to the same upstream repository as Alan, on attempting what will become check-in 4, she gets the "would fork" message from <b>fossil commit</b>, so she dutifully updates her clone and tries again, moving her work to be a child of the new tip, check-in 3. (If she doesn't update, she creates a <i>second</i> fork, which simply complicates matters beyond what we need here for our illustration.)</p></li> </ol> For our purposes here, it doesn't really matter which one happened. All that matters is that Alan's check-in 3 becomes the parent of Betty's check-in 4 because it was the newest tip of the working branch at the time Betty does her check-in. <h3 id="bf-charlie">Step 3: Charlie</h3> Meanwhile, Charlie went offline after syncing his repo with check-in 2 as the latest on that branch. When he checks his changes in, it is as a child of 2, not of 4, because Charlie doesn't know about check-ins 3 & 4 yet. He does this at an absolute wall clock time <i>after</i> Alan and Betty made their check-ins, so when Charlie comes back online and pushes his check-in 5 to the master repository and learns about check-ins 3 and 4 during Fossil sync, Charlie inadvertently revives the other side of the fork. <h3 id="bf-darlene">Step 4: Darlene</h3> Darlene sees all of this, because she joins in on the work on this branch after Alan, Betty, and Charlie made their check-ins and pushed them to the master repository. She's taking one of the same three steps as we [#bf-betty|outlined for Betty above]. Regardless of her path to this view, it happens after Charlie pushed his check-in 5 to the master repo, so Darlene sees that as the latest on the branch, causing her work to be saved as a child of check-in 5, not of check-in 4, as it would if Charlie didn't come back online and sync before Darlene started work on that branch. <h3 id="post-mortem">Post Mortem</h3> The end result of all of this is that even though everyone makes only one check-in and no one disables autosync without genuine need, half of the check-ins end up on one side of the fork and half on the other. A future user — his mother calls him Edward, but please call him Eddie — can then join in on the work on this branch and end up on <i>either</i> side of the fork. If Eddie joins in with the state of the repository as drawn above, he'll end up on the top side of the fork, because check-in 6 is the latest, but if Alan or Betty makes a seventh check-in to that branch first, it will be as a child of check-in 4 since that's the version in their local check-out directories. Since that check-in 7 will then be the latest, Eddie will end up on the bottom side of the fork instead. In all of this, realize that neither side of the fork is obviously "correct." Every participant was doing the right thing by their own lights at the time they made their lone check-in. Who, then, is to blame? We can only blame the consequences of creating the fork on Alan if he did so on purpose, as by passing "--allow-fork" when creating a check-in on a shared working branch. Alan might have created it inadvertently by going offline while check-in 1 was the tip of the branch in his local clone, so that by the time he made his check-in 3, check-in 2 had arrived at the shared parent repository from someone else. (Francine?) When Alan rejoins the network and does an autosync, he learns about check-in 2. Since his #3 is already checked into his local clone because autosync was off or blocked, the sync creates an unavoidable fork. We can't blame either Alan or Francine here: they were both doing the right thing given their imperfect view of the state of the global situation. The same is true of Betty, Charlie, and Darlene. None of them tried to create a fork, and none of them chose a side in this fork to participate in. They just took Fossil's default and assumed it was correct. The only blame I can assign here is on any of these users who believed forks couldn't happen before this did occur, and I blame them only for their avoidable ignorance. (You, dear reader, have been ejected from that category by reading this very document.) Any time someone can work without getting full coordination from every other clone of the repo, forks are possible. Given enough time, they're all but inevitable. This is a general property of DVCSes, not just of Fossil. This sort of consequence is why forks on shared working branches are bad, which is why [./concepts.wiki#workflow|Fossil tries so hard to avoid them], why it warns you about it when they do occur, and why it makes it relatively [#fix|quick and painless to fix them] when they do occur. <h2>Review Of Terminology</h2> <dl> <dt><b>Branch</b></dt> <dd><p>A branch is a set of check-ins with the same value for their "branch" property.</p></dd> <dt><b>Leaf</b></dt> <dd><p>A leaf is a check-in with no children in the same branch.</p></dd> <dt><b>Closed Leaf</b></dt> <dd><p>A closed leaf is any leaf with the <b>closed</b> tag. These leaves are intended to never be extended with descendants and hence are omitted from lists of leaves in the command-line and web interface.</p></dd> <dt><b>Open Leaf</b></dt> <dd><p>A open leaf is a leaf that is not closed.</p></dd> <dt><b>Fork</b></dt> <dd><p>A fork is when a check-in has two or more direct (non-merge) children in the same branch.</p></dd> <dt><b>Branch Point</b></dt> <dd><p>A branch point occurs when a check-in has two or more direct (non-merge) children in different branches. A branch point is similar to a fork, except that the children are in different branches.</p></dd> </dl> Check-in 4 of Figure 3 is not a leaf because it has a child (check-in 5) in the same branch. Check-in 9 of Figure 5 also has a child (check-in 10) but that child is in a different branch, so check-in 9 is a leaf. Because of the <b>closed</b> tag on check-in 9, it is a closed leaf. Check-in 2 of Figure 3 is considered a "fork" because it has two children in the same branch. Check-in 2 of Figure 5 also has two children, but each child is in a different branch, hence in Figure 5, check-in 2 is considered a "branch point." <h2>Differences With Other DVCSes</h2> <h3 id="single">Single DAG</h3> Fossil keeps all check-ins on a single DAG. Branches are identified with tags. This means that check-ins can be freely moved between branches simply by altering their tags. Most other DVCSes maintain a separate DAG for each branch. <h3 id="unique">Branch Names Need Not Be Unique</h3> Fossil does not require that branch names be unique, as in some VCSes, most notably Git. Just as with unnamed branches (which we call forks) Fossil resolves such ambiguities using the timestamps on the latest check-in in each branch. If you have two branches named "foo" and you say <b>fossil update foo</b>, you get the tip of the "foo" branch with the most recent check-in. This fact is helpful because it means you can reuse branch names, which is especially useful with utility branches. There are several of these in the SQLite and Fossil repositories: "broken-build," "declined," "mistake," etc. As you might guess from these names, such branch names are used in renaming the tip of one branch to shunt it off away from the mainline of that branch due to some human error. (See <b>[/help?cmd=amend | fossil amend]</b> and the Fossil UI check-in amendment features.) This is a workaround for Fossil's [./shunning.wiki|normal inability to forget history]: we usually don't want to actually <i>remove</i> history, but would like to sometimes set some of it aside under a new label. Because some VCSes can't cope with duplicate branch names, Fossil collapses such names down on export using the same time stamp based arbitration logic, so that only the branch with the newest check-in gets the branch name in the export. All of the above is true of tags in general, not just branches. |
Changes to www/bugtheory.wiki.
|
| < | > | 1 2 3 4 5 6 7 8 9 | <title>Bug-Tracking In Fossil</title> <h2>Introduction</h2> A bug-report in fossil is called a "ticket". Tickets are tracked separately from code check-ins. Some other distributed bug-tracking systems store tickets as files within the source tree and thereby leverage the syncing and merging capabilities of the versioning system to sync and merge tickets. This approach is |
︙ | ︙ | |||
25 26 27 28 29 30 31 | be permitted to create tickets. Recall that a fossil repository consists of an unordered collection of <i>artifacts</i>. (See the <a href="fileformat.wiki">file format document</a> for details.) Some artifacts have a special format, and among those are <a href="fileformat.wiki#tktchng">Ticket Change Artifacts</a>. | | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | be permitted to create tickets. Recall that a fossil repository consists of an unordered collection of <i>artifacts</i>. (See the <a href="fileformat.wiki">file format document</a> for details.) Some artifacts have a special format, and among those are <a href="fileformat.wiki#tktchng">Ticket Change Artifacts</a>. One or more ticket change artifacts are associated with each ticket. A ticket is created by a ticket change artifact. Each subsequent modification of the ticket is a separate artifact. The "push", "pull", and "sync" algorithms share ticket change artifacts between repositories in the same way as every other artifact. In fact, the sync algorithm has no knowledge of the meaning of the artifacts it is syncing. As far as the sync algorithm is concerned, all artifacts are |
︙ | ︙ | |||
110 111 112 113 114 115 116 | to repopulate the table using the new column names. Note that the TICKET table schema and content is part of the local state of a repository and is not shared with other repositories during a sync, push, or pull. Each repository also defines scripts used to generate web pages for creating new tickets, viewing existing tickets, and modifying an existing ticket. These scripts consist of HTML with an embedded | | | < < | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | to repopulate the table using the new column names. Note that the TICKET table schema and content is part of the local state of a repository and is not shared with other repositories during a sync, push, or pull. Each repository also defines scripts used to generate web pages for creating new tickets, viewing existing tickets, and modifying an existing ticket. These scripts consist of HTML with an embedded scripts written a Tcl-like language called "[./th1.md|TH1]". Every new fossil repository is created with default scripts. Administrators wishing to customize their ticket entry, viewing, and editing screens should modify the default scripts to suit their needs. These screen generator scripts are part of the local state of a repository and are not shared with other repositories during a sync, push, or pull. <i>To be continued...</i> |
Changes to www/build-icons/mac.gif.
cannot compute difference between binary files
Added www/build-icons/openbsd.gif.
cannot compute difference between binary files
Changes to www/build.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 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 | <title>Compiling and Installing Fossil</title> <h2>0.0 Using A Pre-compiled Binary</h2> [/uv/download.html|Pre-compiled binaries] are available for recent releases. Just download the appropriate executable for your platform and put it on your $PATH. To uninstall, simply delete the executable. To upgrade from an older release, just overwrite the older binary with the newer one. For details about how those binaries are built, see [/wiki?name=Release+Build+How-To | the Release Build How-To wiki page]. <h2>0.1 Executive Summary</h2> Building and installing is very simple. Three steps: <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> <hr> <h2>1.0 Obtaining The Source Code</h2> 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 [/uv/download.html|downloads page]. To obtain a development version of fossil, follow these steps: <ol> <li>Point your web browser to [https://fossil-scm.org/]</li> <li>Click on the [/timeline|Timeline] link at the top of the page.</li> <li>Select a version of Fossil you want to download. The latest version on the trunk branch is usually a good choice. Click on its link.</li> <li>Finally, click on one of the "Zip Archive" or "Tarball" links, according to your preference. These links will build a ZIP archive or a gzip-compressed tarball of the complete source code and download it to your computer.</li> </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 [https://fossil-scm.org/ | 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 [https://fossil-scm.org/home], 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"> Unpack the ZIP or tarball you downloaded then <b>cd</b> into the directory created.</li> <li><i>(Optional, Debian-compatible Linux only)</i> Make sure you have all the necessary tools and libraries at hand by running: <b>sudo apt install tcl-dev tk libssl-dev zlib1g-dev</b>. <li><i>(Optional, Unix only)</i> Run <b>./configure</b> to construct a makefile. <ol type="a"> <li> The build system for Fossil on Unix-like systems assumes that the OpenSSL development and runtime files are available on your system, because unprotected repositories are trivial to attack otherwise. Indeed, some public Fossil repositories — including Fossil's own — today run in an HTTPS-only mode, so that you can't even do an anonymous clone from them without using the TLS features added to Fossil by OpenSSL. To weaken that stance could allow a [https://en.wikipedia.org/wiki/Man-in-the-middle_attack|man in the middle attack], such as one that substitutes malicious code into your Fossil repository clone. You can force the Fossil build system to avoid searching for, building against, and linking to the OpenSSL library by passing <b>--with-openssl=none</b> to the <tt>configure</tt> script. If you do not have the OpenSSL development libraries on your system, we recommend that you install them, typically via your OS's package manager. The Fossil build system goes to a lot of effort to seek these out wherever they may be found, so that is typically all you need to do. For more advanced use cases, see the [./ssl.wiki#openssl-bin|OpenSSL discussion in the "TLS and Fossil" document]. </li> <li> To build a statically linked binary, you can <i>try</i> adding the <b>--static</b> option, but [https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead | it may well not work]. If your platform of choice is affected by this, the simplest workaround we're aware of is to build a Fossil container, then [./containers.md#static | extract the static executable from it]. </li> <li> To enable the native [./th1.md#tclEval | Tcl integration feature] feature, add the <b>--with-tcl=1</b> and <b>--with-tcl-private-stubs=1</b> options. </li> <li> Other configuration options can be seen by running <b>./configure --help</b> </li> </ol> <li>Run "<b>make</b>" to build the "fossil" or "fossil.exe" executable. The details depend on your platform and compiler.</li> <ol type="a"> <li><i>Unix</i> → the configure-generated Makefile should work on all Unix and Unix-like systems. Simply type "<b>make</b>".</li> <li><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> <li><i>MinGW / MinGW-w64</i> → The best-supported path is to build via the MinGW specific Makefile under a POSIX build of GNU make: "<b>make -f win/Makefile.mingw</b>".</li> There is limited support for building under MinGW's native Windows port of GNU Make instead by defining the <tt>USE_WINDOWS=1</tt> variable, but it's better to build under MSYS, Cygwin, or WSL on Windows since this mode doesn't take care of cases such as the "openssl" target, which depends on <tt>sed</tt>. We've gone as far down this path as is practical short of breaking cross-compilation under Linux, macOS, and so forth, as we'd have to do to make everything work under <tt>cmd.exe</tt>. Unless you're building under MSYS where commands like "<tt>gcc</tt>" give MinGW's GCC and not some other version, you will need to make minor edits to win/Makefile.mingw to configure the cross-compilation environment. It should suffice to switch to one of the predefined <tt>PREFIX</tt> values, causing the build to be done using "<tt>x86_64-w64-mingw32-gcc</tt>" for example, yielding a 64-bit native Windows binary. To enable the native [./th1.md#tclEval | Tcl integration feature], use a command line like the following (all on one line): <b>make -f win/Makefile.mingw FOSSIL_ENABLE_TCL=1 FOSSIL_ENABLE_TCL_STUBS=1 FOSSIL_ENABLE_TCL_PRIVATE_STUBS=1</b> Alternatively, running <b>./configure</b> under MSYS should give a suitable top-level Makefile. However, options passed to configure that are not applicable on Windows may cause the configuration or compilation to fail (e.g. fusefs, internal-sqlite, etc). <li><i>MSVC</i> → Use the MSVC makefile.</li> <em>NB:</em> Run the following <code>nmake</code> commands from a "x64 Native Tools Command Prompt"; <code>buildmsvc.bat</code> is able to automatically load the build tools (x64 by default, pass "x86" as the first argument to use the x86 tools), so it can be called from a normal command prompt. 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</b>" subdirectory within the local [/tree?ci=trunk&name=compat | compat] directory 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: <pre> nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin </pre> <pre> buildmsvc.bat FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin </pre> To enable the optional native [./th1.md#tclEval | Tcl integration feature], run one of the following commands or add the "FOSSIL_ENABLE_TCL=1" argument to one of the other NMAKE command lines: <pre> nmake /f Makefile.msc FOSSIL_ENABLE_TCL=1 </pre> <pre> buildmsvc.bat FOSSIL_ENABLE_TCL=1 </pre> <li><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.</li> </ol> </ol> <h2>3.0 Installing</h2> <ol> <li value="9"> 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. </li> <li> <b>(Optional:)</b> To uninstall, just delete the binary. </li> </ol> <h2>4.0 Additional Considerations</h2> <ul> <li> If the makefiles that come with Fossil do not work for you, or for some other reason you want to know how to build Fossil manually, then refer to the [./makefile.wiki | Fossil Build Process] document which describes in detail what the makefiles do behind the scenes. </li> <li> The fossil executable is self-contained and stand-alone and usually requires no special libraries or other software to be installed. However, the "--tk" option to the [/help/diff|diff command] requires that Tcl/Tk be installed on the local machine. You can get Tcl/Tk from [http://www.activestate.com/activetcl|ActiveState]. </li> <li> To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile generated by configure to add the following lines: <pre> TCC += -DSQLITE_WITHOUT_ZONEMALLOC TCC += -D_BSD_SOURCE TCC += -DWITHOUT_ICONV TCC += -Dsocketlen_t=int TCC += -DSQLITE_MAX_MMAP_SIZE=0 </pre> </li> </ul> <h2 id="docker" name="oci">5.0 Building a Docker Container</h2> The information on building Fossil inside an [https://opencontainers.org/ | OCI container] is now in [./containers.md | a separate document]. This includes the instructions on using the OCI container as an expedient intermediary for building a statically-linked Fossil binary on modern Linux platforms, which otherwise make this difficult. <h2>6.0 Building on/for Android</h2> <h3>6.1 Cross-compiling from Linux</h3> The following instructions for building Fossil for Android via Linux, without requiring a rooted OS, are adapted from [forum:/forumpost/e0e9de4a7e | a forum post]. On the development machine, from the fossil source tree: <pre><code>export CC=$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang ./configure --with-openssl=none make </code></pre> On the Android device, enable the <em>USB debugging</em> option from Developer menu in Device Options. Connect the device to the development system with USB. If it's configured and connected properly, the device should show up in the output of <code>adb devices</code>: <pre><code>sudo adb devices </code></pre> Copy the resulting fossil binary onto the device... <pre><code>sudo adb push fossil /data/local/tmp </code></pre> And run it from an <code>adb</code> shell: <pre><code>sudo adb shell > cd /data/local/tmp # Fossil requires a HOME directory to work with: > export HOME=$PWD > export PATH=$PWD:$PATH > fossil version This is fossil version 2.11 [e5653a4ceb] 2020-03-26 18:54:02 UTC </code></pre> The output might, or might not, include warnings such as: <pre><code>WARNING: linker: ./fossil: unused DT entry: type 0x6ffffef5 arg 0x1464 WARNING: linker: ./fossil: unused DT entry: type 0x6ffffffe arg 0x1ba8 WARNING: linker: ./fossil: unused DT entry: type 0x6fffffff arg 0x2 </code></pre> The source of such warnings is not 100% certain. Some information about these (reportedly harmless) warnings can be found [https://stackoverflow.com/a/41900551 | on this StackOverflow post]. <a id='fuzzer'></a> <h2>7.0 Building for Fuzz Testing</h2> This feature is primarily intended for fossil's developers and may change at any time. It is only known to work on Linux systems and has been seen to work on x86/64 and ARM. Fossil has builtin support for processing specific features using <tt>libfuzzer</tt>. The features which can be tested this way are found in the help text for the [/help?cmd=test-fuzz|test-fuzz command]. Fuzzing requires: * Customizing the build of fossil a small bit. * The clang C compiler. * libfuzzer. On Ubuntu-derived systems, it can be installed with <tt>apt install libfuzzer-XYZ</tt>, where XYZ is a version number (several versions may be available on any given system) First, modify the top-level <tt>Makefile.in</tt>: * Extend the <tt>TCCFLAGS</tt> variable with: <tt>-fsanitize=fuzzer -DFOSSIL_FUZZ</tt> (and see [/finfo/src/fuzz.c | src/fuzz.c] for more options). * Rename <tt>APPNAME</tt> from <tt>fossil</tt> to <tt>fossil-fuzz</tt>. Then rebuild: <pre></code>$ make clean $ ./configure CC=/path/to/clang $ make </code></pre> If clang is your default compiler, the <tt>CC</tt> configure option is not required. The resulting <tt>fossil-fuzz</tt> binary differs from the standard one primarily in that it runs the <tt>test-fuzz</tt> command by default. It needs to be told what to fuzz and needs to be given a directory of input files to seed the fuzzer with: <pre></code>$ mkdir cases # Copy input files into ./cases. e.g. when fuzzing the markdown # processor, copy any to-be-tested .md files into that directory. # Then start the fuzzer: $ ./fossil-fuzz --fuzztype markdown cases </code></pre> As it works, it writes its mutated test files into the test-input directory, each one named in the form of a hash. When it finds a problem it will produce a stack trace for the offending code, will output the name of the file which triggered the crash (named <tt>cases/SOME_HASH</tt>) and may, depending on the nature of the problem, produce a file named <tt>crash-SOMETHING</tt>. In theory the crash file can be fed directly back into the fuzzer to reproduce the problem: <pre></code>$ ./fossil-fuzz --fuzztype markdown crash-SOMETHING </code></pre> But whether or not it will genuinely crash may depend on static app-level state which might not trigger the crash when running an individual test. For a detailed information about the fuzzer's flags and features, see: * [https://llvm.org/docs/LibFuzzer.html] * [https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md] Flags for the fuzzer can be passed directly to fossil, e.g. <tt>-jobs=4</tt> to start four fuzzer jobs in parallel, but doing so may cause the fuzzer to <em>strip the --fuzztype flag</em>, leading to it testing the wrong thing. When passing on fuzzer-specific flags along with <tt>--fuzztype</tt>, be sure to check your system's process list to ensure that your <tt>--fuzztype</tt> flag is there. <a id='wasm'></a> <h2>8.0 Building WebAssembly Components</h2> Fossil uses one component built as [https://developer.mozilla.org/en-US/docs/WebAssembly | WebAssembly] a.k.a. WASM. Because compiling WASM code requires non-trivial client-side tooling, the repository includes compiled copies of these pieces. Most Fossil hackers should never need to concern themselves with the WASM parts, but this section describes how to for those who want or need to do so. <strong>The bits described in this section are necessary when updating <tt>extsrc/pikchr.c</tt></strong> from the upstream source, or the fossil binary will use a different version of pikchr than [/pikchrshow] does (as the latter runs the WASM build of pikchr). These instructions have only ever been tested on Linux systems. They "should" work on any Unix-like system supported by Emscripten. The fossil makefiles for Windows builds <em>do not</em> include any of the WASM-related components (patches to add that would be welcomed, of course). The first step is to configure the tree with support for [https://emscripten.org/|Emscripten]. This requires that the system has the Emscripten SDK (a.k.a. emsdk) installed, as documented at: [https://emscripten.org/docs/getting_started/downloads.html] For instructions on keeping the SDK up to date, see: [https://emscripten.org/docs/tools_reference/emsdk.html] <div class="sidebar">Getting Emscripten up and running is trivial and painless, at least on Linux systems, but the installer downloads many hundreds of megabytes of tools and dependencies, all of which will be installed under the single SDK directory (as opposed to being installed at the system level). It does, however, require that python3 be installed at the system level and it can optionally make use of a system-level cmake for certain tasks unrelated to how fossil uses the SDK.</div> After installing the SDK, configure the fossil tree with emsdk support: <pre><code>$ ./configure --with-emsdk=/path/to/emsdk \ --and-other-options... </code></pre> If the <tt>--with-emsdk</tt> flag is not provided, the configure script will check for the environment variable <tt>EMSDK</tt>, which is one of the standard variables the SDK environment uses. If that variable is found, its value will implicitly be used in place of the missing <tt>--with-emsdk</tt> flag. Thus, if the <tt>emsdk_env.sh</tt> script is sourced into the shell before running the configure script, the SDK will be detected even without the config flag. The configure script installs some makefile variables which tell the build where to find the SDK and it generates a script named <tt>tools/emcc.sh</tt> (from the template file <tt>[/file/tools/emcc.sh.in|/tools/emcc.sh.in]</tt>), which is a wrapper around the Emscripten C compiler (<tt>emcc</tt>). The wrapper script uses the configure-time state to attempt to set up the various environment variables which are required by <tt>emcc</tt> and will fail if it cannot do so. Once it's set up the environment, it passes on all of its arguments to <tt>emcc</tt>. The WASM-related build parts are set up such that none of them should ever trigger implicity (e.g. via dependencies resolution) in a normal build cycle. They are instead explicitly built as described below. From the top of the source tree, all WASM-related components can be built with: <pre><code>$ make wasm</code></pre> <div class="sidebar">The file <tt>[/file/extsrc/pikcher-worker.js|extsrc/pikcher-worker.js]</tt> is hand-coded and intended to be loaded as a "Worker" in JavaScript. That file loads the main module and provides an interface via which a main JavaScript thread can communicate with pikchr running in a Worker thread. The file <tt>[/file/src/fossil.page.pikchrshowasm.js|src/fossil.page.pikchrshowasm.js]</tt> implements the [/pikchrshow] app and demonstrates how <tt>pikchr-worker.js</tt> is used.</div> As of this writing, those parts include: * <tt>extsrc/pikchr.wasm</tt> is a WASM-compiled form of <tt>extsrc/pikchr.c</tt>. * <tt>extsrc/pikchr.js</tt> is JS/WASM glue code generated by Emscripten to give JS code access to the API exported by the WASM file. When a new version of <tt>extsrc/pikchr.c</tt> is installed, the files <tt>pikchr.{js,wasm}</tt> will need to be recompiled to account for that. Running <tt>make wasm</tt> will, if the build is set up for the emsdk, recompile those: <pre><code>$ make wasm ./tools/emcc.sh -o extsrc/pikchr.js ... $ ls -la extsrc/pikchr.{js,wasm} -rw-rw-r-- 1 stephan stephan 17263 Jun 8 03:59 extsrc/pikchr.js -rw-rw-r-- 1 stephan stephan 97578 Jun 8 03:59 extsrc/pikchr.wasm </code></pre> <div class="sidebar">If that fails with a message along the lines of “<code>setting `EXPORTED_RUNTIME_METHODS` expects `<class 'list'>` but got `<class 'str'>`</code>†then the emcc being invoked is too old: emcc changed the format of list-type arguments at some point. The required minimum version is unknown, but any SDK version from May 2022 or later "should" (as of this writing) suffice. Any older version may or may not work.</div> After that succeeds, we need to run the normal build so that those generated files can be compiled in to the fossil binary, accessible via the [/help?cmd=/builtin|/builtin page]: <pre><code>$ make</code></pre> Before checking in those newly-built files, they need to be tested by running the [/pikchrshow] page. If that page loads, the compilation process fundamentally worked (a load failure will be made obvious to the viewer). If it fails to load then the browser's dev tools console likely provides at least a small hint (and <em>sometimes</em> a useful hint) about the nature of the problem. Don't check those files in until [/pikchrshow] runs, though! Should pikchr's C interface ever change, <tt>pikchr-worker.js</tt> will need to be updated to accommodate it, but such modification is typically trivial. <h3>8.1 Solutions other than Emscripten?</h3> Emscripten is not the only option for building C as WASM, but it provides a complete toolchain which eliminates many other steps which must otherwise be accounted for on a per-project basis. Despite its convenience, it behooves us to explore other build options for the sake of portability and avoiding what amounts to vendor lock-in. For later refererence, here are articles specifically covering building WASM projects without using Emscripten: * [https://surma.dev/things/c-to-webassembly/] * [https://schellcode.github.io/webassembly-without-emscripten] |
Added www/cap-theorem.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 | # Fossil and the CAP Theorem [The CAP theorem][cap] is a fundamental mathematical proof about distributed systems. A software system can no more get around it than a physical system can get past *c*, the [speed of light][sol] constant. Fossil is a distributed system, so it can be useful to think about it in terms of the CAP theorem. We won’t discuss the theorem itself or how you reason using its results here. For that, we recommend [this article][tut]. [cap]: https://en.wikipedia.org/wiki/CAP_theorem [sol]: https://en.wikipedia.org/wiki/Speed_of_light [tut]: https://www.ibm.com/cloud/learn/cap-theorem <a id="ap"></a> ## Fossil Is an AP-Mode System As with all common [DVCSes][dvcs], Fossil is an AP-mode system, meaning that your local clone isn’t necessarily consistent with all other clones (C), but the system is always available for use (A) and partition-tolerant (P). This is what allows you to turn off Fossil’s autosync mode, go off-network, and continue working with Fossil, even though only a single node (your local repo clone) is accessible at the time. You may consider that going back online restores “Câ€, because upon sync, you’re now consistent with the repo you cloned from. But, if another user has gone offline in the meantime, and they’ve made commits to their disconnected repo, *you* aren’t consistent with *them.* Besides which, if another user commits to the central repo, that doesn’t push the change down to you automatically: even if all users of a Fossil system are online at the same instant, and they’re all using autosync, Fossil doesn’t guarantee consistency across the network. There’s no getting around the CAP theorem! [dvcs]: https://en.wikipedia.org/wiki/Distributed_version_control <a id="ca"></a> ## CA-Mode Fossil What would it mean to redesign Fossil to be CA-mode? It means we get a system that is always consistent (C) and available (A) as long as there are no partitions (P). That’s basically [CVS] and [Subversion][svn]: you can only continue working with the repository itself as long as your connection to the central repo server functions. It’s rather trivial to talk about single-point-of-failure systems like CVS or Subversion as CA-mode. Another common example used this way is a classical RDBMS, but aren’t we here to talk about distributed systems? What’s a good example of a *distributed* CA-mode system? A better example is [Kafka], which in its default configuration assumes it being run on a corporate LAN in a single data center, so network partitions are exceedingly rare. It therefore sacrifices partition tolerance to get the advantages of CA-mode operation. In its particular application of this mode, a message isn’t “committed†until all running brokers have a copy of it, at which point the message becomes visible to the client(s). In that way, all clients always see the same message store as long as all of the Kafka servers are up and communicating. How would that work in Fossil terms? If there is only one central server and I clone it on my local laptop, then CA mode means I can only commit if the remote Fossil is available, so in that sense, it devolves to the old CVS model. What if there are three clones? Perhaps there is a central server *A*, the clone *B* on my laptop, and the clone *C* on your laptop. Doesn’t CA mode now mean that my commit on *B* doesn’t exist after I commit it to the central repo *A* until you, my coworker, *also* pull down the copy of that commit to your laptop *C*, validating the commit through the network? That’s one way to design the system, but another way would be to scope the system to only talk about proper *servers*, not about the clients. In that model, a CA-mode Fossil alternative might require 2+ servers to be running for proper replication. When I make a commit, if all of the configured servers aren’t online, I can’t commit. This is basically CVS with replication, but without any useful amount of failover. [CVS]: https://en.wikipedia.org/wiki/Concurrent_Versions_System [Kafka]: https://engineering.linkedin.com/kafka/intra-cluster-replication-apache-kafka [svn]: https://en.wikipedia.org/wiki/Apache_Subversion <a id="cp"></a> ## CP-Mode Fossil What if we modify our CA-mode system above with “warm sparesâ€? We can say that commits must go to all of the spares as well as the active servers, but a loss of one active server requires that one warm spare come into active state, and all of the clients learn that the spare is now considered “active.†At this point, you have a CP-mode system, not a CA-mode system, because it’s now partition-tolerant (P) but it becomes unavailable when there aren’t enough active servers or warm spares to promote to active status. CP is your classical [BFT] style distributed consensus system, where the system is available only if the client can contact a *majority* of the servers. This is a formalization of the warm spare concept above: with *N* server nodes, you need at least ⌊*N* / 2⌋ + 1 of them to be online for a commit to succeed. Many distributed database systems run in CP mode because consistency (C) and partition-tolerance (P) is a useful combination. What you lose is always-available (A) operation: with a suitably bad partition, the system goes down for users on the small side of that partition. An optional CP mode for Fossil would be attractive in some ways since in some sense Fossil is a distributed DBMS, but in practical terms, it means Fossil would then not be a [DVCS] in the most useful sense, being that you could work while your client is disconnected from the remote Fossil it cloned from. A fraught question is whether the non-server Fossil clones count as “nodes†in this sense. If they do count, then if there are only two systems, the central server and the clone on my laptop, then it stands to reason from the formula above that I can only commit if the central server is available. In that scheme, a CP-mode Fossil is basically like CVS. But what happens if my company hires a coworker to help me with the project, and this person makes their own clone of the central repo? The equation says I still need 2 nodes to be available for a commit, so if my new coworker goes off-network, that doesn’t affect whether I can make commits. Likewise, if I go off-network, my coworker can make commits to the central server. But what happens if the central server goes down? The equation says we still have 2 nodes, so we should be able to commit, right? Sure, but only if my laptop and communicate directly to my coworker’s laptop! If it can’t, that’s also a network partition, so *N=1* on both sides in that case. The implication is that for a true CP-mode Fossil, we’d need some kind of peer-to-peer networking layer so that our laptops can accept commits from the other, so that when the central server comes online, one of us can send the results up to it to get it caught up. But doesn’t that then mean there is no security? How does [Fossil’s RBAC system][caps] work if peer-to-peer commits are allowed? You can instead reconceptualize the system as “node†meaning only server nodes, so that client-only systems don’t count. This allows you to have an RBAC system again. With just one central server, ⌊1/2⌋+1=1, so you get CVS-like behavior: if the server’s up, you can commit. If you set up 2 servers for redundancy, both must be up for commits to be allowed, since otherwise you could end up with half the commits going to the server on one side of a network partition, half going to the other, and no way to arbitrate among the two once the partition is lifted. (Today’s AP-mode Fossil has this capability, but the necessary cost is “Câ€, consistency! Once again, you can’t get around the CAP theorem.) 3 servers is more sensible: any client that can see at least 2 of them can commit. Will there ever be a CP-mode Fossil? This author doubts it, but as I’ve shown, it would be useful in contexts where you’d rather have a guarantee of consistency than availability. [BFT]: https://en.wikipedia.org/wiki/Byzantine_fault [caps]: ./caps/ |
Added www/caps/admin-v-setup.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 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 | # Differences Between Setup and Admin Users This document explains the distinction between [Setup users][caps] and [Admin users][capa]. For other information about use types, see: * [Administering User Capabilities](./) * [How Moderation Works](../forum.wiki#moderation) * [Users vs Subscribers](../alerts.md#uvs) * [Defense Against Spiders](../antibot.wiki) ## <a id="philosophy"></a>Philosophical Core The Setup user "owns" the Fossil repository and may delegate a subset of that power to one or more Admin users. The Setup user can grant Admin capability and take it away, but Admin users cannot grant themselves Setup capability, either directly via the Admin → Users UI page or via any indirect means. (If you discover indirect means to elevate Admin privilege to Setup, it's a bug, so please [report it][forum]!) It is common for the Setup user to have administrative control over the host system running the Fossil repository, whereas it makes no sense for Admin users to have that ability. If an Admin-only user had `root` access on a Linux box running the Fossil instance they are an Admin on, they could elevate their capability to Setup in several ways. (The `fossil user` command, the `fossil sql` command, editing the repository DB file directly, etc.) Therefore, if you wish to grant someone Setup-like capability on a Fossil repository but you're unwilling to give them a login on the host system, you probably want to grant them Admin capability instead. Admin power is delegated from Setup. When a Setup user grants Admin capability, it is an expression of trust in that user's judgement. Admin-only users must not fight against the policies of the Setup user. Such a rift would be just cause for the Setup user to strip the Admin user's capabilities. This may then create a fork in the project’s development effort as the ex-Admin takes their clone and stands it up elsewhere, so they may become that fork’s Setup user. A useful rule of thumb here is that Admin users should only change things that the Setup user has not changed from the stock configuration. In this way, an Admin-only user can avoid overriding the Setup user's choices. You can also look at the role of Admin from the other direction, up through the [user power hierarchy][ucap] rather than down from Setup. An Admin user is usually a “super-developer†role, given full control over the repository’s managed content: versioned artifacts in [the hash tree][bc], [unversioned content][uv], forum posts, wiki articles, tickets, etc. We’ll explore these distinctions in the rest of this document. [bc]: ../blockchain.md [ucap]: ./index.md#ucap [uv]: ../unvers.wiki ## <a id="binary"></a>No Granularity Fossil doesn’t make any distinction between these two user types beyond this binary choice: Setup or Admin. A few features of Fossil are broken down so that only part of the feature is accessible to Admin, with the rest left only to Setup users, but for the most part each feature affected by this distinction is either Admin + Setup or Setup-only. We could add more capability letters to break down individual sub-features, but we’d run out of ASCII alphanumerics pretty quickly, and we might even run out of ASCII punctuation and symbols. Then would we need to shift to Unicode? Consider the Admin → Settings page, which is currently restricted to Setup users only: you might imagine breaking this up into several subsets so that some settings can be changed by Admin users. Is that a good idea? Maybe, but it should be done only after due consideration. It would definitely be wrong to assign a user capability bit to *each* setting on that page. Now consider the opposite sort of case, Admin → Skins. Fossil grants Admin users full access to this page so that the Admins can maintain and extend the skin as the repository evolves, not so Admins can switch the entire skin to another without consulting with the Setup user first. How would Fossil decide, using user capabilities only, which skin changes the Admin user is allowed to do, and which must be left to Setup? Do we assign a separate capability letter to each step in `/setup_skin`? Do we assign one more each to the five sections of a skin? (Header, Footer, CSS, JavaScript, and Details.) It quickly becomes unmanageable. ## <a id="capgroups"></a>Capability Groups We can break up the set of powers the Admin user capability grants into several groups, then defend each group as a coherent whole. ### <a id="security"></a>Security While establishing the Fossil repository's security policy is a task for the Setup user, *maintaining* that policy is something that Fossil allows a Setup user to delegate to trustworthy users via the Admin user capability: * **Manage users**: The only thing an Admin-only user cannot do on the Admin → Users page is grant Setup capability, either to themselves or to other users. The intent is that Admin users be able to take some of the load of routine user management tasks off the shoulders of the Setup user: delete accounts created by spammers, fix email alert subscriptions, reset passwords, etc. * **Security audit**: The Admin → Security-Audit page runs several tests on the Fossil repository's configuration, then reports potential problems it found and offers canned solutions. Those canned solutions do not do anything that an Admin-user could not do via other means, so this page offers the Admin-only user no more power than they otherwise had. For example, this page's "Take it Private" feature can also be done manually via Admin → Users. This page is a convenience, not a grant of new power to the Admin-only user. * **Logging**:<a id="log"></a> Admin-only users get to see the various Fossil logs in case they need to use them to understand a problem they're empowered to solve. An obvious example is a spam attack: the Admin might want to find the user's last-used IP, see if they cloned the repository, see if they attempted to brute-force an existing login before self-registering, etc. Some security-conscious people might be bothered by the fact that Admin-only users have these abilities. Think of a large IT organization: if the CIO hires a [tiger team][tt] to test the company's internal IT defenses, the line grunts fix the reported problems, not the CIO. ### <a id="administrivia"></a>Administrivia It is perfectly fine for a Fossil repository to only have Setup users, no Admin users. The smaller the repository, the more likely the repository has no Admin-only users. If the Setup user neither needs nor wants to grant Admin power to others, there is no requirement in Fossil to do so. [Setup capability is a pure superset of Admin capability.][sia] As the number of users on a Fossil repository grows, the value in delegating administrivia also grows, because the Setup user typically has other time sinks they consider more important. Admin users can take over the following routine tasks on behalf of the Setup user: * **Shunning**: After user management, this is one of the greatest powers of an Admin-only user. Fossil grants access to the Admin → Shunned page to Admin users rather than reserve it to Setup users because one of the primary purposes of [the Fossil shunning system][shun] is to clean up after a spammer, and that's exactly the sort of administrivia we wish to delegate to Admin users. Coupled with the Rebuild button on the same page, an Admin user has the power to delete the repository's entire [hash tree][bc]! This makes this feature a pretty good razor in deciding whether to grant someone Admin capability: do you trust that user to shun Fossil artifacts responsibly? Realize that shunning is cooperative in Fossil. As long as there are surviving repository clones, an Admin-only user who deletes the whole hash tree has merely caused a nuisance. An Admin-only user cannot permanently destroy the repository unless the Setup user has been so silly as to have no up-to-date clones. * **Moderation**: According to [the user power hierarchy][ucap], Admins are greater than Moderators, so control over what Moderators can do clearly belongs to both Admins and to the Setup user(s). * **Status**: Although the Fossil `/stat` page is visible to every user with Read capability, there are several additional things this page gives access to when a user also has the Admin capability: * <p>[Email alerts][ale] and [backoffice](../backoffice.md) status. Admin-only users cannot modify the email alerts setup, but they can see some details about its configuration and current status.</p> * <p>The `/urllist` page, which is a read-only page showing the ways the repository can be accessed and how it has been accessed in the past. Logically, this is an extension to logging, [covered above](#log).</p> * <p>The Fossil repository SQL schema. This is not particularly sensitive information, since you get more or less the same information when you clone the repository. It's restricted to Admin because it's primarily useful in debugging SQL errors, which happen most often when Fossil itself is in flux and the schema isn't being automatically updated correctly. That puts this squarely into the "administrivia" category.</p> * <p>Web cache status, environment, and logging: more administrivia meant to help the Admin debug problems.</p> * **Configure search** [ale]: ../alerts.md [shun]: ../shunning.wiki ### <a id="cosmetics"></a>Cosmetics While the Setup user is responsible for setting up the initial "look" of a Fossil repository, the Setup user entrusts Admin users with *maintaining* that look. An Admin-only user therefore has the following special abilities: * Modify the repository skin * Create and modify URL aliases * Manage the "ad units" feature, if enabled. * Adjust the `/timeline` display preferences. * Change the "logo" element displayed by some skins. These capabilities allow an Admin-only user to affect the branding and possibly even the back-end finances of a project. This is why we began this document with a philosophical discussion: if you cannot entrust a user with these powers, you should not grant that user Admin capability. ## <a id="clones"></a>Clones and Backups Fossil is a *distributed* version control system, which has direct effects on the “Setup user†concept in the face of clones. When you clone a repository, your local user becomes a Setup user on the local clone even if you are not one on the remote repository. This may be surprising to you, but it should also be sensible once you realize that your operating system will generally give you full control over the local repository file. What use trying to apply remote restrictions on the local file, then? The distinctions above therefore are intransitive: they apply only within a single repository instance. Fossil behaves differently when you do a clone as a user with Setup capability on the remote repository, which primarily has effects on the fidelity of clone-as-backup, which we cover [elsewhere](../backup.md). We strongly encourage you to read that document if you expect to use a clone as a complete replacement for the remote repository. ## <a id="apsu"></a>The All-Powerful Setup User Setup users get [every user capability](./ref.html) of Fossil except for [two exceptionally dangerous capabilities](#dcap), which they can later grant to themselves or to others. In addition, Setup users can use every feature of the Fossil UI. If Fossil can do a thing, a Setup user on that repo can make Fossil do it. Setup users can do many things that Admin users cannot. They may not only use all of the Admin UI features, they may also: * See record IDs (RIDs) on screens that show them * See the MIME type of attachments on [`/ainfo` pages](/help?cmd=/ainfo) * See a remote repo’s HTTP [cache status](/help?cmd=/cachestat) and [pull cache entries](/help?cmd=/cacheget) * Edit a Setup user’s account! The “Admin†feature of Fossil UI is so-named because Admin users can use about half of its functions, but only Setup can use these pages: * **Access**: This page falls under the [Security](#security) category above, but like Configuration, it's generally something set up once and never touched, so only Setup users should change it. * **Configuration**: This page nominally falls under [Cosmetics](#cosmetics) above, but it's such a core part of the Fossil configuration — something every Setup user is expected to fully specify on initial repository setup — that we have trouble justifying any case where an Admin-only user would have good cause to modify any of it. This page is generally set up once and then never touched again. * **Email-Server**: This is an experimental SMTP server feature which is currently unused in Fossil. Should we get it working, it will likely remain Setup-only, since it will likely be used as a replacement for the platform’s default SMTP server, a powerful position for a piece of software to take. * **Login-Group**: [Login groups][lg] allow one Fossil repository to delegate user access to another. Since an Admin-only user on one repo might not have such access to another repo on the same host system, this must be a Setup-only task. * **Notification**: This is the main UI for setting up integration with a platform’s SMTP service, for use in sending out [email notifications][ale]. Because this screen can set commands to execute on the host, and because finishing the configuration requires a login on the Fossil host system, it is not appropriate to give Admin users access to it. * **Settings**: The [repository settings][rs] available via Admin → Settings have too wide a range of power to allow modification by Admin-only users: * <p><b>Harmless</b>: Admin-only users on a repository may well have checkin rights on the repository, so the fact that versionable settings like `crlf-glob` can also be set at the repository level seems like a thing we might want to allow Admin-only users the ability to change. Since Fossil currently has no way to allow only some settings to be changed by Admin-only users and some not, we can't just show these harmless settings to Admin-only users.</p> * <p><b>Low-Risk</b>: The <tt>admin-log</tt> setting controls whether the Fossil admin log is generated. Since we've <a href="#log">already decided</a> that Admin-only users can see this log, it seems fine that the Admin users can choose whether this log gets generated in the first place.</p> <p>There's a small risk that a rogue Admin user could disable the log before doing something evil that the log would capture, so ideally, we'd want to restrict changing this setting from 1 to 0 to Setup only while allowing Admin-only users to change it from 0 to 1. Fossil doesn't currently allow that.</p> * <p><b>Risky</b>: The <tt>https-login</tt> setting falls under the "Security" section above, but it should probably never be adjusted by Admin-only users. Sites that want it on will never want it to be disabled without a very good reason.</p> <p>There is also an inverse risk: if the site has a front-end HTTPS proxy that uses HTTP to communicate over localhost to Fossil, enabling this setting will create an infinite redirect loop! (Ask me how I know.)</p> * <p><b>Dangerous</b>: The <tt>email-send-command</tt> setting could allow a rogue Admin to run arbitrary commands on the host system, unless it's prevented via some kind of host-specific restriction. (chroot, jails, SELinux, VMs, etc.) Since it makes no sense to trust Admin-only users with <tt>root</tt> level access on the host system, we almost certainly don't want to allow them to change such settings.</p> * **SQL**: The Admin → SQL feature allows the Setup user to enter raw SQL queries against the Fossil repository via Fossil UI. This not only allows arbitrary ability to modify the repository hash tree and its backing data tables, it can probably also be used to damage the host such as via `PRAGMA temp_store = FILE`. * **Tickets**: This section allows input of arbitrary TH1 code that runs on the server, affecting the way the Fossil ticketing system works. The justification in the **TH1** section below therefore applies. * **TH1**: The [TH1 language][th1] is quite restricted relative to the Tcl language it descends from, so this author does not believe there is a way to damage the Fossil repository or its host via the Admin → TH1 feature, which allows execution of arbitrary TH1 code within the repository's execution context. Nevertheless, interpreters are a well-known source of security problems, so it seems best to restrict this feature to Setup-only users as long as we lack a good reason for Admin-only users to have access to it. * **Transfers**: This is for setting up TH1 hooks on various actions, so the justification in the **TH1** section above applies. * **Wiki**: These are mainly cosmetic and usability settings. We might open this up to Admin users in the future. Just remember, [user caps affect Fossil’s web interfaces only][webo]. A user is a Setup user by default on their local clone of a repo, and Fossil’s ability to protect itself against malicious (or even simply incorrect) pushes is limited. Someone with clone and push capability on your repo could clone it, modify their local repo, and then push the changes back to your repo. Be careful who you give that combination of capabilities to! When you run [`fossil ui`][fui], you are the Setup user on that repo through that UI instance, regardless of the capability set defined in the repo’s user table. This is true even if you cloned a remote repo where you do not have Setup caps. This is why `ui` always binds to `localhost` without needing the `--localhost` flag: in this mode, anyone who can connect to that repo’s web UI has full power over that repo. ## <a id="dcap"></a>Dangerous Capabilities Initially Denied to Everyone There are two capabilities that Fossil doesn’t grant by default to Setup or Admin users automatically. They are exceptionally dangerous, so Fossil makes these users grant themselves (or others) these capabilities deliberately, hopefully after careful consideration. ### <a id="y"></a>Write Unversioned Fossil currently doesn’t distinguish the sub-operations of [`fossil uv`](/help?cmd=uv); they’re all covered by [**WrUnver**][capy] (“yâ€) capability. Since some of these operations are unconditionally destructive due to the nature of unversioned content, and since this goes against Fossil’s philosophy of immutable history, nobody gets cap “y†on a Fossil repo by default, not even the Setup or Admin users. A Setup or Admin user must grant cap “y†to someone — not necessarily themselves! — before modifications to remote unversioned content are possible. Operations on unversioned content made without this capability affect your local clone only. In this way, your local unversioned file table can have different content from that in its parent repo. This state of affairs will continue until your user either gets cap “y†and syncs that content with its parent or you say `fossil uv revert` to make your local unversioned content table match that of its parent repo. ### <a id="x"></a>Private Branch Push For private branches to remain private, they must never be accidentally pushed to a public repository. It can be [difficult to impossible][shun] to recover from such a mistake, so nobody gets [**Private**][capx] (“xâ€) capability on a Fossil repo by default, not even Admin or Setup users. There are two common uses for private branches. One use is part of a local social contract allowing individual developers to work on some things in private until they’re ready to push them up to the parent repository. This goes against [a core tenet][fdp] of Fossil’s design philosophy, but Fossil allows it, so some development organizations do this. If yours is one of these, you might give cap “x†to the “developer†category. The other use is in development organizations that follow the Fossil philosophy, where you do not work in private unless you absolutely must. You may have a public-facing project — let’s call it “SQLite†for the sake of argument — but then someone comes along and commissions a custom modification to your project which they wish to keep proprietary. You do your work on a private branch, which you absolutely must never push to the public repo, because that would be illegal. (Breach of contract, copyright violation on a work-for-hire agreement, etc.) If you are using Fossil in this way, we recommend that you give “x†capability to a special developer account only, if at all, to minimize the chance of an accidental push. [capa]: ./ref.html#a [caps]: ./ref.html#s [capx]: ./ref.html#x [capy]: ./ref.html#y [fcp]: https://fossil-scm.org/home/help?cmd=configuration [fdp]: ../fossil-v-git.wiki#devorg [forum]: https://fossil-scm.org/forum/ [fui]: /help?cmd=ui [lg]: ./login-groups.md [rs]: https://fossil-scm.org/home/doc/trunk/www/settings.wiki [sia]: https://fossil-scm.org/home/artifact?ln=1259-1260&name=0fda31b6683c206a [snoy]: https://fossil-scm.org/forum/forumpost/00e1c4ecff [th1]: ../th1.md [tt]: https://en.wikipedia.org/wiki/Tiger_team#Security [webo]: ./#webonly |
Added www/caps/impl.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 | # Implementation Details of User Capabilities ## <a id="choices"></a>Capability Letter Choices We [assigned][ref] user capability characters using only lowercase ASCII letters at first, so those are the most important within Fossil: they control the functions most core to Fossil’s operation. Once we used up most of the lowercase letters, we started using uppercase, and then during the development of the [forum feature][for] we assigned most of the decimal numerals. All of the lowercase ASCII letters are now assigned. Eventually, we might have to start using ASCII punctuation and symbols. We expect to run out of reasons to define new caps before we’re forced to switch to Unicode, though the possibilities for [mnemonic][mn] assignments with emoji are intriguing. <span style="vertical-align: bottom">😉</span> The existing caps are usually mnemonic, especially among the earliest and therefore most central assignments, made when we still had lots of letters to choose from. There is still hope for good future mnemonic assignments among the uppercase letters, which are mostly still unused. ## <a id="bitfield"></a>Why Not Bitfields? Some may question the use of ASCII character strings for [capability sets][ucap] instead of bitfields, which are more efficient, both in terms of storage and processing time. Fossil handles these character strings in one of two ways. For most HTTP hits, Fossil [expands][sexp] the string into a [`struct` full of flags][sff] so that later code can just do simple Boolean tests. In a minority of cases, where Fossil only needs to check for the presence of a single flag, it just does a [`strchr()` call][sc] on the string instead. Both methods are slower than bit testing in a bitfield, but keep the execution context in mind: at the front end of an HTTP request handler, where the nanosecond differences in such implementation details are completely swamped by the millisecond scale ping time of that repo’s network connection, followed by the required I/O to satisfy the request. Either method is plenty fast in that context. In exchange for this immeasurable cost per hit, we get human-readable capability sets. ## <a id="filter"></a>Why Doesn’t Fossil Filter “Bad†Artifacts on Sync? Fossil is more trusting about the content it receives from a remote clone during sync than you might expect. Common manifestations of this design choice are: 1. A user may be able to impersonate other users. This can be [accidental](./index.md#defuser) as well as purposeful. 2. If your local system clock is out-of-sync with absolute time, artifacts committed to that repo will appear with the “wrong†time when sync’d. If the time sync error is big enough, it can make check-ins appear to go back in time and other bad effects. 3. You can purposely overwrite good timestamps with bad ones and push those changes up to the remote with no interference, even though Fossil tries to make that a Setup-only operation. All of this falls out of two of Fossil’s design choices: sync is all-or-nothing, and [the Fossil hash tree][bc] is immutable. Fossil would have to violate one or both of these principles to filter such problems out of incoming syncs. We have considered auto-[shunning][shun] “bad†content on sync, but this is [difficult][asd] due to [the design of the sync protocol][dsp]. This is not an impossible set of circumstances, but implementing a robust filter on this input path would be roughly as difficult as writing a basic [inter-frame video codec][ifvc]: do-able, but still a lot of work. Patches to do this will be thoughtfully considered. We can’t simply change content as it arrives. Such manipulations would change the artifact manifests, which would change the hashes, which would require rewriting all parts of the block chain from that point out to the tips of those branches. The local Fossil repo must then go through the same process as the remote one on subsequent syncs in order to build up a sync sequence that the remote can understand. Even if you’re willing to accept all of that, this would break all references to the old artifact IDs in forum posts, wiki articles, check-in comments, tickets, etc. The bottom line here is that [**Clone**](./ref.html#g) and [**Write**](./ref.html#i) are a potent combination of user capabilities. Be careful who you give that pair to! ----- *[Back to Administering User Capabilities](./)* <!-- add padding so anchor links always scroll ref’d section to top --> <div style="height: 75em"></div> [asd]: https://fossil-scm.org/forum/forumpost/ce4a3b5f3e [bc]: ../blockchain.md [dsp]: https://fossil-scm.org/fossil/doc/trunk/www/sync.wiki [for]: ./forum.wiki [ifvc]: https://en.wikipedia.org/wiki/Inter_frame [mn]: https://en.wikipedia.org/wiki/Mnemonic [ref]: ./ref.html [sexp]: /artifact?ln=1223-1298&name=889d6724 [sff]: /artifact?ln=80-117&name=52d2860f [sc]: https://en.cppreference.com/w/c/string/byte/strchr [shun]: ../shunning.wiki [ucap]: ./index.md#ucap |
Added www/caps/index.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 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 | # Administering User Capabilities (a.k.a. Permissions) Fossil includes a powerful [role-based access control system][rbac] which affects which users have which capabilities(^Some parts of the Fossil code call these “permissions†instead, but since there is [a clear and present risk of confusion](#webonly) with operating system level file permissions in this context, we avoid using that term for Fossil’s RBAC capability flags in these pages.) within a given [served][svr] Fossil repository. We call this the “caps†system for short. Fossil stores a user’s caps as an unordered string of ASCII characters, one capability per, [currently](./impl.md#choices) limited to [alphanumerics][an]. Caps are case-sensitive: “**A**†and “**a**†are different user capabilities. This is a complex topic, so some sub-topics have their own documents: 1. [Login Groups][lg] 2. [Implementation Details](./impl.md) 3. [User Capability Reference](./ref.html) [an]: https://en.wikipedia.org/wiki/Alphanumeric [avs]: ./admin-v-setup.md [lg]: ./login-groups.md [rbac]: https://en.wikipedia.org/wiki/Role-based_access_control ## <a id="ucat"></a>User Categories Before we explain individual user capabilities and their proper administration, we want to talk about an oft-overlooked and misunderstood feature of Fossil: user categories. Fossil defines four user categories. Two of these apply based on the user’s login status: **nobody** and **anonymous**. The other two act like Unix or LDAP user groups: **reader** and **developer**. Because we use the word “group†for [another purpose][lg] in Fossil, we will avoid using it that way again in this document. The correct term in Fossil is “category.†Fossil user categories give you a way to define capability sets for four hard-coded situations within the Fossil C source code. Logically speaking: > *(developer* ∨ *reader)* ≥ *anonymous* ≥ *nobody* When a user visits a [served Fossil repository][svr] via its web UI, they initially get the capabilities of the “nobody†user category. This category would be better named “everybody†because it applies whether you’re logged in or not. When a user logs in as “anonymous†via [`/login`](/help?name=/login) they get all of the “nobody†category’s caps plus those assigned to the “anonymous†user category. It would be better named “user†because it affects all logged-in users, not just those logged in via Fossil’s anonymous user feature. When a user with either the “reader†([**u**][u]) or “developer†([**v**][v]) capability letter logs in, they get their [individual user caps](#ucap) plus those assigned to this special user category. They also get those assigned to the “anonymous†and “nobody†categories. Because “developer†users do not automatically inherit “reader†caps, it is standard practice to give both letters to your “developer†users: **uv**. You could instead just assign cap **u** to the “developer†category. Fossil shows how these capabilities apply hierarchically in the user editing screen (Admin → Users → name) with the `[N]` `[A]` `[D]` `[R]` tags next to each capability check box. If a user gets a capability from one of the user categories already assigned to it, there is no value in redundantly assigning that same cap to the user explicitly. For example, with the default **ei** cap set for the “developer†category, the cap set **ve** is redundant because **v** grants **ei**, which includes **e**. We suggest that you lean heavily on these fixed user categories when setting up new users. Ideally, your users will group neatly into one of the predefined categories, but if not, you might be able to shoehorn them into our fixed scheme. For example, the administrator of a wiki-only Fossil repo for non-developers could treat the “developer†user category as if it were called “author,†and a forum-only repo could treat the same category as if it were called “member.†There is currently no way to define custom user categories. [svr]: ../server/ ## <a id="ucap"></a>Individual User Capabilities When one or more users need to be different from the basic capabilities defined in user categories, you can assign caps to individual users. You may want to have the [cap reference][ref] open when doing such work. It is useful at this time to expand on the logical expression [above](#cat), which covered only the four fixed user categories. When we bring the individual user capabilities into it, the complete expression of the way Fossil implements user power becomes: > *setup* ≥ *admin* ≥ *moderator* ≥ *(developer* ∨ *reader)* ≥ *[subscriber]* ≥ *anonymous* ≥ *nobody* The two additions at the top are clear: [setup is all-powerful][apsu], and since admin users have [all capabilities][ref] except for Setup capability, they are [subordinate only to the setup user(s)][avsp]. The moderator insertion could go anywhere from where it’s shown now down to above the “anonymous†level, depending on what other caps you give to your moderators. Also, there is not just one type of moderator: Fossil has [wiki][l], [ticket][q], and [forum][5] moderators, each independent of the others. Usually your moderators are fairly high-status users, with developer capabilities or higher, but Fossil does allow the creation of low-status moderators. The placement of “subscriber†in that hierarchy is for the sort of subscriber who has registered an account on the repository purely to [receive email alerts and announcements][7]. Users with additional caps can also be subscribers, but not all users *are* in fact subscribers, which is why we show it in square brackets. (See [Users vs Subscribers](../alerts.md#uvs).) [apsu]: ./admin-v-setup.md#apsu [avsp]: ./admin-v-setup.md#philosophy ## <a id="new"></a>New Repository Defaults Fossil creates one user account in new repos, which is named after your OS user name [by default](#defuser). Fossil gives the initial repository user the [all-powerful Setup capability][apsu]. Users who visit a [served repository][svr] without logging in get the “nobody†user category’s caps which default to **[g][g][j][j][o][o][r][r][z][z]**: clone the repo, read the wiki, check-out files via the web UI, view tickets, and pull version archives. This default is suited to random passers-by on a typical FOSS project’s public web site and its code repository. Users who [prove they are not a bot][bot] by logging in — even if only as “anonymous†— get the “nobody†capability set plus **[h][h][m][m][n][n][c][c]**: see internal hyperlinks, append to existing wiki articles, file new tickets, and comment on existing tickets. We chose these additional capabilities as those we don’t want bots to have, but which a typical small FOSS project would be happy to give anonymous humans visiting the project site. The “reader†user category is typically assigned to users who want to be identified within the repository but who primarily have a passive role in the project. The default capability set on a Fossil repo adds **[k][k][p][p][t][t][w][w]** caps to those granted by “nobody†and “anonymousâ€. This category is not well-named, because the default caps are all about modifying repository content: edit existing wiki pages, change one’s own password, create new ticket report formats, and modify existing tickets. This category would be better named “participantâ€. Those in the “developer†category get the “nobody†and “anonymous†cap sets plus **[e][e][i][i]**: view sensitive user material and check in changes. [bot]: ../antibot.wiki ## <a id="pvt"></a>Consequences of Taking a Repository Private When you click Admin → Security-Audit → “Take it private,†one of the things it does is set the user capabilities for the “nobody†and “anonymous†user categories to blank, so that users who haven’t logged in can’t even see your project’s home page, and the option to log in as “anonymous†isn’t even offered. Until you log in with a user name, all you see is the repository’s skin and those few UI elements that work without any user capability checks at all, such as the “Login†link. Beware: Fossil does not reassign the capabilities these users had to other users or to the “reader†or “developer†user category! All users except those with Setup capability will lose all capabilities they inherited from “nobody†and “anonymous†categories. Setup is the [lone exception][apsu]. If you will have non-Setup users in your private repo, you should parcel out some subset of the capability set the “nobody†and “anonymous†categories had to other categories or to individual users first. ## <a id="read-v-clone"></a>Reading vs. Cloning Fossil has two capabilities that are often confused: [**Read**](./ref.html#o) and [**Clone**](./ref.html#g). The **Read** capability has nothing to do with reading data from a local repository, because [caps affect Fossil’s web interfaces only](#webonly). Once you’ve cloned a remote repository to your local machine, you can do any reading you want on that repository irrespective of whether your local user within that repo has <b>Read</b> capability. The repo clone is completely under your user’s power at that point, affected only by OS file permissions and such. If you need to prevent that, you want to deny **Clone** capability instead. Withholding the **Read** capability has a different effect: it prevents a web client from viewing [embedded documentation][edoc], using [the file browser](/help?name=/dir), exploring the [history](/help?name=/timeline) of check-ins, and pulling file content via the [`/artifact`](/help?name=/artifact), [`/file`](/help?name=/file), and [`/raw`](/help?name=/raw) URLs. It is common to withhold **Read** capability from low-status visitors on private or semi-private repos to prevent them from pulling individual elements of the repo over the web one at a time, as someone may do when denied the bulk **Clone** capability. [edoc]: ../embeddeddoc.wiki ## <a id="defuser"></a>Default User Name By default, Fossil assumes your OS user account name is the same as the one you use in any Fossil repository. It is the [default for a new repository](#new), though you can override this with [the `--admin-user` option][auo]. Fossil has other ways of overriding this in other contexts such as the `name@` syntax in clone URLs. It’s simplest to stick with the default; a mismatch can cause problems. For example, if you clone someone else’s repo anonymously, turn off autosync, and make check-ins to that repository, they will be assigned to your OS user name by default. If you later get a login on the remote repository under a different name and sync your repo with it, your earlier “private†check-ins will get synced to the remote under your OS user name! When such problems occur, you can amend the check-in to hide the incorrect name from Fossil reports, but the original values remain in the repository [forever][shun]. It is [difficult enough][fos] to fix such problems automatically during sync that we are unlikely to ever do so. [auo]: /help?name=new [fos]: ./impl.md#filter [shun]: ../shunning.wiki ## <a id="utclone"></a>Cloning the User Table When cloning over HTTP, the initial user table in the local clone is set to its “[new state:](#new)†only one user with Setup capability, named after either your OS user account, per the default above, or after the user given in the clone URL. There is one exception: if you clone as a named Setup user, you get a complete copy of the user information. This restriction keeps the user table private except for the only user allowed to make absolutely complete clones of a remote repo, such as for failover or backup purposes. Every other user’s clone is missing this and a few other items, either for information security or PII privacy reasons. When cloning with file system paths, `file://` URLs, or over SSH, you get a complete clone, including the parent repo’s complete user table. All of the above applies to [login groups][lg] as well. ## <a id="webonly"></a>Caps Affect Web Interfaces Only Fossil’s user capability system only affects accesses over `http[s]://` URLs. This includes clone, sync/push/pull, the [UI pages][wp], and [the JSON API][japi]. For everything else, the user caps aren’t consulted at all. The only checks made when working directly with a local repository are the operating system’s file system permissions. This should strike you as sensible, since if you have read access to the repository file, you can do anything you want to that repo DB including giving your user’s record the [**Setup**][s] capability, after which Fossil’s user capability system is effectively bypassed. (Or, create another Setup user, with the same end effect.) If you’re objecting that you need *write* access to the DB file to achieve this, realize that you can copy a read-only file to another location, giving yourself write access to it. This is why the `fossil ui` command gives you Setup permissions within Fossil UI: it can’t usefully prevent you from doing anything through the UI since only the local file system permissions actually matter, and you can’t start `fossil ui` without having at least read access to that file. What may be more surprising to you is that this is also true when working on a *clone* done over a local file path, except that there are then two sets of file system permission checks: once to modify the working check-out’s repo clone DB file, then again on [sync][sync] with the parent DB file. The Fossil capability checks are effectively defeated because your user has [**Setup**][s] capability on both sides of the sync. Be aware that those file checks do still matter, however: Fossil requires write access to a repo DB while cloning from it, so you can’t clone from a read-only repo DB file over a local file path. Even more surprising to you may be the fact that user caps do not affect cloning and syncing over SSH! (Not unless you go [out of your way][sshfc] patch around it, at any rate.) When you make a change to such a repository, the stock Fossil behavior is that the change first goes to the local repo clone where file system permissions are all that matter, but then upon sync, the situation is effectively the same as when the parent repo is on the local file system. The reason behind this is that if you can log into the remote system over SSH and that user has the necessary file system permissions on that remote repo DB file to allow clone and sync operations, then we’re back in the same situation as with local files: there’s no point trying to enforce the Fossil user capabilities when you can just modify the remote DB directly, so the operation proceeds unimpeded by any user capability settings on the remote repo. Where this gets confusing is that *all* Fossil syncs are done over the HTTP protocol, including those done over `file://` and `ssh://` URLs, not just those done over `http[s]://` URLs: * For `ssh://` URLs, Fossil pipes the HTTP conversation through a local SSH client to a remote instance of Fossil running the [`test-http`](/help?name=test-http) command to receive the tunneled HTTP connection. [This interface is intentionally permissionless][sxycap]. * For `file://` URLs — as opposed to plain local file paths — the “sending†Fossil instance writes its side of the HTTP conversation out to a temporary file in the same directory as the local repo clone and then calls itself on the “receiving†repository to read that same HTTP transcript file back in to apply those changes to that repository. Presumably Fossil does this instead of using a pipe to ease portability to Windows. Despite use of HTTP for these URL types, the fact remains that checks for capabilities like [**Read**][o] and [**Write**][i] within the HTTP conversation between two Fossil instances only have a useful effect when done over an `http[s]://` URL. [sshfc]: ../server/any/http-over-ssh.md [sxycap]: /file?ci=ec5efceb8aac6cb4&name=src/main.c&ln=2748-2752 ## <a id="pubpg"></a>Public Pages In Admin → Access, there is an option for giving a list of [globs][glob] to name URLs which get treated as if the visitor had [the default cap set](#defcap). For example, you could take the [**Read**][o] capability away from the “nobody†user category, who has it by default, to prevent users without logins from pulling down your repository contents one artifact at a time, yet give those users the ability to read the project documentation by setting the glob to match your [embedded documentation][edoc]’s URL root. ## <a id="defcap"></a>Default User Capability Set In Admin → Access, you can define a default user capability set, which is used as: 1. the default caps for users newly created by an Admin or Setup user 2. the default caps for self-registered users, an option in that same UI 3. the effective caps for URIs considered [public pages](#pubpg) This defaults to [**Reader**][u]. <!-- add padding so anchor links always scroll ref’d section to top --> <div style="height: 75em"></div> [ref]: ./ref.html [a]: ./ref.html#a [b]: ./ref.html#b [c]: ./ref.html#c [d]: ./ref.html#d [e]: ./ref.html#e [f]: ./ref.html#f [g]: ./ref.html#g [h]: ./ref.html#h [i]: ./ref.html#i [j]: ./ref.html#j [k]: ./ref.html#k [l]: ./ref.html#l [m]: ./ref.html#m [n]: ./ref.html#n [o]: ./ref.html#o [p]: ./ref.html#p [q]: ./ref.html#q [r]: ./ref.html#r [s]: ./ref.html#s [t]: ./ref.html#t [u]: ./ref.html#u [v]: ./ref.html#v [w]: ./ref.html#w [x]: ./ref.html#x [y]: ./ref.html#y [z]: ./ref.html#z [2]: ./ref.html#2 [3]: ./ref.html#3 [4]: ./ref.html#4 [5]: ./ref.html#5 [6]: ./ref.html#6 [7]: ./ref.html#7 [glob]: https://en.wikipedia.org/wiki/Glob_(programming) [japi]: https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/view#heading=h.6k0k5plm18p1 [sp]: ../sync.wiki [sync]: /help?name=sync [wp]: /help#webpages |
Added www/caps/login-groups.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 | # Login Groups The Admin → Login-Groups UI feature and its corresponding [`login-group` command][lg] solve a common problem with Fossil: you’ve created multiple repositories that some subset of users need access to, and you don’t want to redundantly administer the user credentials for each repository. ## Restrictions This feature ties changes to the “`user`†table in one repo to that in one or more other repos, keyed by the user’s name. The interactions are non-obvious because although the goal is for the end result to “just work,†there are practical security and administration matters that complicate things: 1. Login group handling only works between joined repositories for the subset of users with the same name. 1. If you’re logged in on one repo in a group, any other repo in that group that has a matching user record will accept your valid login. 1. When you set up a login group between two repos, the user tables aren’t merged, so even though you may have users that appear in both, they will retain their initial passwords, credentials, and so forth. 1. The same is true after the login group is created: changes you make to the user table in one repo in the group only propagate to the other repos in the group when you check the “Apply changes to all repositories“ box in the “Scope†section of the user edit screen. Otherwise, user changes remain local to the repo you made them on. 1. Login groups only affect [the HTTP interfaces][wo]. Contrast things like `ssh://` clones, where unless you [go out of your way][sh] to force them to run over one of the HTTP interfaces that pays attention to Fossil’s RBAC system, login groups aren’t consulted. ## Interactions These restrictions combine in subtle and interesting ways. Examples: * **#1 and #2**: If you are logged into repo C as “charlie†and then try to visit joined repo A where “charlie†doesn’t exist, your valid login on C won’t get you into A. * **#2 and #3**: If “alice†exists in both of these same repos, logging in on A gets her into C, but if she has different user capabilities on each from the time before the two repos joined the login group, her caps on A don’t apply to C, nor vice versa. Let us say F is a forum-only repo, and W is a wiki-only repo, and that Alice has forum-posting rights on F and wiki-editing rights on W. If both repos are joined by a login group, Alice can log in on F and then access W without logging in on it separately, but she cannot then post a forum message on W even though she could on F. * **#3 and #4**: If you change the caps for user “alice†on one repo in a group and tell Fossil to apply the changes to all repos in the group, the new caps will *overwrite* those on the other repos, not merge with them. To extend the practical example from the prior point, let us say you wish to grant Alice the “write unversioned†capability on both F and W. If you check that single user cap box on F plus the “apply to all†option, then “Apply Changes,†she will end up with forum + unversioned caps on repo W, losing her wiki-editing caps in the process. If you want user caps to differ on each repo, you must administer them separately even if there is a common subset of caps between all repos in the group for that user. Remember: selecting the “apply to all†box calls for an overwrite operation, not a merge. * **#4 and #1**: If you make a change to an existing user “bob†in repo B and select the “apply to all†option, it will only affect other repos in the group that have a user “bob†configured. But, if you are instead creating user “bob†for the first time and select that option, that user *will* be created in all repos. The same is true of user deletion: that destructive action will propagate through the group if you request it. * **#5 and #1**: If you have a user “daisy†on both repos A and B in a login group, logging in over the web to A doesn’t let you push changes into B over SSH. Without the workaround linked above, SSH only pays attention to the operating system’s user authentication system, not Fossil’s. Inversely, if Daisy successfully logs in over SSH to repo B, she gains no access to any of the other repos in that group. She needs at least one valid login over HTTP to one of the group’s repos. ## Discussion The end result of all of this is that you can have a subset of users with credentials only on repo A, a different subset only on B, and a third subset common to both. The only thing selecting which case applies is restriction #1 above. Login groups have names. A repo can be in only one of these named login groups at a time. Trust in login groups is transitive within a single server. Consider this sequence: $ cd /path/to/A/checkout $ fossil login-group join --name G ~/museum/B.fossil $ cd /path/to/C/checkout $ fossil login-group join ~/museum/B.fossil That creates login group G joining repo A to B, then joins C to B. Although we didn’t explicitly tie C to A, a successful login on C gets you into both A and B, within the restrictions set out above. Changes are transitive in the same way, provided you check that “apply to all†box on the user edit screen. [lg]: /help?cmd=login-group [sh]: ../server/any/http-over-ssh.md [wo]: ./index.md#webonly ----- *[Back to Administering User Capabilities](./)* |
Added www/caps/ref.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 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 | <div class='fossil-doc' data-title="User Capability Reference"> <style type="text/css"> p#backlink { /* Make empty space below the table so hyperlinks to named anchors near the bottom of the table still scroll that row to the top of the user's browser, even on tall screens. */ margin-bottom: 75em; } tr > th { background-color: #e8e8e8; vertical-align: top; } body.fossil-dark-style tr > th { color: #000; opacity: 0.85; } tr.cols th { white-space: nowrap; } td, th { padding: 0.4em; } </style> <p>Here we document each currently-defined user capability character in more detail than the brief summary on the <a href="/setup_ucap_list">“key†page</a> in the Fossil user editor. Each row gives the capability letter used in the Fossil user editor followed by the C code’s name for that cap within the <tt>FossilUserPerms</tt> object, so you can use this reference both from the UI down and from the C code up.</p> <p>The <a href="https://en.wikipedia.org/wiki/Mnemonic">mnemonics</a> given here vary from obviously-correct to <i>post facto</i> rationalizations to the outright fanciful. To <a href="./impl.md#choices">some extent</a>, this is unavoidable.</p> <h2>Reference</h2> <table> <tr class="cols"> <th>?</th> <th>Name</th> <th style="text-align: left">Description</th> </tr> <tr id="a"> <th>a</th> <th>Admin</th> <td> Admin users have <em>all</em> of the capabilities below except for <a href="#s">setup</a>, <a herf="#x">Private</a>, and <a href="#y">WrUnver</a>. See <a href="admin-v-setup.md">Admin vs. Setup</a> for a more nuanced discussion. Mnemonic: <b>a</b>dministrate. </td> </tr> <tr id="b"> <th>b</th> <th>Attach</th> <td> Add attachments to wiki articles or tickets. Mnemonics: <b>b</b>ind, <b>b</b>utton, <b>b</b>ond, or <b>b</b>olt. </td> </tr> <tr id="c"> <th>c</th> <th>ApndTkt</th> <td> Append comments to existing tickets. Mnemonic: <b>c</b>omment. </td> </tr> <tr id="d"> <th>d</th> <th>n/a</th> <td> Legacy capability letter from Fossil's forebear <a href="http://cvstrac.org/">CVSTrac</a>, which has no useful meaning in Fossil due to the nature of its durable Merkle tree design. We recommend that you remove it in case we ever reuse this letter for another purpose. See <a href="https://fossil-scm.org/forum/forumpost/43c78f4bef">this post</a> for details. </td> </tr> <tr id="e"> <th>e</th> <th>RdAddr</th> <td> View <a href="https://en.wikipedia.org/wiki/Personal_data">personal identifying information</a> (PII) about other users such as email addresses. Mnemonics: show <b>e</b>mail addresses; or <b>E</b>urope, home of <a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation">GDPR</a>. </td> </tr> <tr id="f"> <th>f</th> <th>NewWiki</th> <td> Create new wiki articles. Mnemonic: <b>f</b>ast, English translation of the Hawaiian word <a href="https://en.wikipedia.org/wiki/History_of_wikis#WikiWikiWeb,_the_first_wiki"><i>wiki</i></a>. </td> </tr> <tr id="g"> <th>g</th> <th>Clone</th> <td> Clone the repository. Note that this is distinct from <a href="#o">check-out capability, <b>o</b></a>; and that upon cloning not just files, but also tickets, wikis, technotes and forum posts are tranferred. Mnemonic: <b>g</b>et. </td> </tr> <tr id="h"> <th>h</th> <th>Hyperlink</th> <td> Get hyperlinks in generated HTML which link you to other parts of the repository. This capability exists so we can deny it to the “nobody†category, to <a href="../antibot.wiki">prevent bots from wandering around aimlessly</a> in the site’s hyperlink web, <a href="../loadmgmt.md">chewing up server resources</a> to little good purpose. Mnemonic: <b>h</b>yperlink. </td> </tr> <tr id="i"> <th>i</th> <th>Write</th> <td> Check changes into the repository. Note that a lack of this capability does not prevent you from checking changes into your local clone, only from syncing those changes up to the parent repo, and then <a href="./basics.md#webonly">only over HTTP</a>. Also note that not just files, but also tickets, wikis, technotes and forum posts will be accepted from clones upon syncronization. Granting this capability also grants <b>o (Read)</b> Mnemonics: <b>i</b>nput, check <b>i</b>n changes. </td> </tr> <tr id="j"> <th>j</th> <th>RdWiki</th> <td> View wiki articles. Mnemonic: in<b>j</b>est page content. (All right, you critics, you do better, then.) </td> </tr> <tr id="k"> <th>k</th> <th>WrWiki</th> <td> Edit wiki articles. Granting this capability also grants <a href="#j"><b>RdWiki</b></a> and <a href="#m"><b>ApndWiki</b></a>, but it does <em>not</em> grant <a href="#f"><b>NewWiki</b></a>! Mnemonic: <b>k</b>ontribute. </td> </tr> <tr id="l"> <th>l</th> <th>ModWiki</th> <td> Moderate <a href="#m">wiki article appends</a>. Appends do not get saved permanently to the receiving repo’s block chain until <a href="#s">Setup</a> or someone with this cap approves it. Mnemonic: a<b>l</b>low. </td> </tr> <tr id="m"> <th>m</th> <th>ApndWiki</th> <td> Append content to existing wiki articles. Mnemonic: a<b>m</b>end wiki </td> </tr> <tr id="n"> <th>n</th> <th>NewTkt</th> <td> File new tickets. Mnemonic: <b>n</b>ew ticket. </td> </tr> <tr id="o"> <th>o</th> <th>Read</th> <td> Read content and history of files from a remote Fossil instance over HTTP. See <a href="index.md#read-v-clone">Reading vs. Cloning</a>. Mnemonic: check <b>o</b>ut remote repo contents. </td> </tr> <tr id="p"> <th>p</th> <th>Password</th> <td> Change one’s own password. Mnemonic: <b>p</b>assword. </td> </tr> <tr id="q"> <th>q</th> <th>ModTkt</th> <td> Moderate tickets: delete comments appended to tickets. Mnemonic: <b>q</b>uash noise commentary. </td> </tr> <tr id="r"> <th>r</th> <th>RdTkt</th> <td> View existing tickets. Mnemonic: <b>r</b>ead tickets. </td> </tr> <tr id="s"> <th>s</th> <th>Setup</th> <td> The <a href="./admin-v-setup.md#apsu">all-powerful Setup user</a>. Mnemonics: <b>s</b>etup or <b>s</b>uperuser. </td> </tr> <tr id="t"> <th>t</th> <th>TktFmt</th> <td> Create new ticket report formats. Note that although this allows the user to provide SQL code to be run in the server’s context, and this capability is given to the untrusted “anonymous†user category by default, this is a safe capability to give to users because it is internally restricted to read-only queries on the tickets table only. (This restriction is done with a SQLite authorization hook, not by any method so weak as SQL text filtering.) Mnemonic: new <b>t</b>icket report. </td> </tr> <tr id="u"> <th>u</th> <th>n/a</th> <td> Inherit all capabilities of the “reader†user category; does not have a dedicated flag internally within Fossil. Mnemonic: <a href="./index.md#ucat"><b>u</b>ser</a> </td> </tr> <tr id="v"> <th>v</th> <th>n/a</th> <td> Inherit all capabilities of the “developer†user category; does not have a dedicated flag internally within Fossil. Mnemonic: de<b>v</b>eloper. </td> </tr> <tr id="w"> <th>w</th> <th>WrTkt</th> <td> Edit existing tickets. Granting this capability also grants <a href="#r"><b>RdTkt</b></a>, <a href="#c"><b>ApndTkt</b></a>, and <a href="#n"><b>NewTkt</b></a>. Mnemonic: <b>w</b>rite to ticket. </td> </tr> <tr id="x"> <th>x</th> <th>Private</th> <td> Push or pull <a href="../private.wiki">private branches</a>. Mnemonic: e<b>x</b>clusivity; “x†connotes unknown material in many Western languages due to its <a href="https://en.wikipedia.org/wiki/La_Géométrie#The_text">traditional use in mathematics</a>. </td> </tr> <tr id="y"> <th>y</th> <th>WrUnver</th> <td> Push <a href="../unvers.wiki">unversioned content</a>. Mnemonic: <b>y</b>ield, <a href="https://en.wiktionary.org/wiki/yield">sense 4</a>: “hand over.†</td> </tr> <tr id="z"> <th>z</th> <th>Zip</th> <td> Pull archives of particular repository versions via <a href="/help?cmd=/zip"><tt>/zip</tt></a>, <a href="/help?cmd=/tarball"><tt>/tarball</tt></a>, and <a href="/help?cmd=/sqlar"><tt>/sqlar</tt></a> URLs. This is an expensive capability to grant, because creating such archives can put a large load on <a href="../server/">a Fossil server</a> which you may then need to <a href="../loadmgmt.md">manage</a>. Mnemonic: <b>z</b>ip file download. </td> </tr> <tr id="2"> <th>2</th> <th>RdForum</th> <td> Read <a href="../forum.wiki">forum posts</a> by other users. Mnemonic: from thee <b>2</b> me. </td> </tr> <tr id="3"> <th>3</th> <th>WrForum</th> <td> Create new forum threads, reply to threads created by others, and edit one’s own posts. New posts are <a href="../forum.wiki#moderation">held for moderation</a> and do not appear in repo clones or syncs. Granting this capability also grants <a href="#2"><b>RdForum</b></a>. Mnemonic: post for <b>3</b> audiences: me, <a href="#5">the mods</a>, and <a href="https://en.wikipedia.org/wiki/The_Man">the Man</a>. </td> </tr> <tr id="4"> <th>4</th> <th>WrTForum</th> <td> Extends <a href="#3"><b>WrForum</b></a>, bypassing the moderation and sync restrictions. Mnemonic: post <b>4</b> immediate release. </td> </tr> <tr id="5"> <th>5</th> <th>ModForum</th> <td> <a href="../forum.wiki#moderation">Moderate forum posts</a>. Granting this capability also grants <a href="#4"><b>WrTForum</b></a> and <a href="#2"><b>RdForum</b></a>, so a user with this cap never has to moderate their own posts. Mnemonic: “May I have <b>5</b> seconds of your time, honored Gatekeeper?†</td> </tr> <tr id="6"> <th>6</th> <th>AdminForum</th> <td> Users with this capability see a checkbox on unmoderated forum posts labeled “Trust user X so that future posts by user X do not require moderation.†Checking that box and then clicking the moderator-only “Approve†button on that post grants <a href="#4"><b>WrTForum</b></a> capability to that post’s author. There is currently no UI for a user with this cap to <em>revoke</em> trust from a user once it is granted; only <a href="#a"><b>Admin</b></a> and <a href="#s"><b>Setup</b></a> can currently revoke granted caps. Granting this capability also grants <a href="#5"><b>ModForum</b></a> and those it in turn grants. Mnemonic: “I’m <b>6</b> [sick] of hitting Approve on your posts!†</td> </tr> <tr id="7"> <th>7</th> <th>EmailAlert</th> <td> User can sign up for <a href="../alerts.md">email alerts</a>. Mnemonic: <a href="https://en.wikipedia.org/wiki/Heaven_Can_Wait">Seven can wait</a>, I’ve got email to read now. </td> </tr> <tr id="A"> <th>A</th> <th>Announce</th> <td> Send email announcements to users <a href="#7">signed up to receive them</a>. Mnemonic: <b>a</b>nnounce. </td> </tr> <tr id="C"> <th>C</th> <th>Chat</th> <td> Allow access to the <tt>/chat</tt> room. </td> </tr> <tr id="D"> <th>D</th> <th>Debug</th> <td> Enable debugging features. Mnemonic: <b>d</b>ebug. </td> </tr> <tr id="L"> <th>L</th> <th>Is-logged-in</th> <td> This is not a real capability, but is used in certain capability checks, e.g. via <a href="../th1.md#capexpr">capexpr</a>. It resolves to true if the current user is logged in. Mnemonic: <b>L</b>ogged in. </td> </tr> </table> <hr/> <p id="backlink"><a href="./"><em>Back to Administering User Capabilities</em></a></p> |
Added www/cgi.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 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 | <title>CGI Script Configuration Options</title> <h1>Summary</h1> It is not necessary to have a central server in order to use Fossil. But a central server can help a project run more smoothly by giving developers a common point of rendezvous for syncing, and by providing a web-based portal where developers and non-developers alike can learn about the project and its current state. Setting up a server using Fossil is easy. A [./server/|separate document] talks about all of the many different methods for setting up a Fossil server, one of which is [./server/any/cgi.md | as a CGI script]. CGI is the technique that the three [./selfhost.wiki|self-hosting Fossil repositories] all use. Setting up a Fossil server using CGI is mostly about writing a short script (usually just 2 lines line) in the cgi-bin folder of an ordinary web-server. But there are a lot of extra options that can be added to this script, to customize the configuration. This article describes those options. <h1>CGI Script Options</h1> The CGI script used to launch a Fossil server will usually look something like this: <verbatim> #!/usr/bin/fossil repository: /home/www/fossils/myproject.fossil </verbatim> Of course, pathnames will likely be different. The first line (the "[wikipedia:/wiki/Shebang_(Unix)|shebang]") always gives the name of the Fossil executable. Subsequent lines are of the form "<b>property: argument ...</b>". The remainder of this document describes the available properties and their arguments. <hr> <h2 id="repository">repository: <i>PATH</i></h2> This property defines the Fossil repository that the server will use. Every Fossil CGI requires either this property or the [#directory|<b>directory:</b>] property (but not both). Many Fossil CGI scripts have this one property and no other. <h2 id="directory">directory: <i>PATH</i></h2> The PATH is the name of a directory that contains one or more Fossil repository files having the suffix ".fossil". If this property is used instead of [#repository|<b>repository:</b>], then the Fossil server is able to serve all of the repositories in the directory. The specific repository used is selected by a prefix on the PATH_INFO. See the notes for the [#repolist|<b>repolist</b>] option regarding name collisions between subdirectories and repository files. <h2 id="notfound">notfound: <i>URL</i></h2> If the [#directory|<b>directory:</b>] option is used and if the PATH_INFO of the HTTP request does not correspond to any Fossil repository, then the request redirects to URL. <h2 id="repolist">repolist</h2> This is a Boolean property. If it is present, and if the [#directory:|<b>directory:</b>] option is used, and if the PATH_INFO string is empty, then Fossil will show a list of available Fossil repositories. The "skin" of the reply is determined by the first repository in the list that has a non-zero [/help?cmd=repolist-skin|repolist-skin] setting. If no repository has such a non-zero repolist-skin setting, then the repository list is generic HTML without any decoration. The repolist-generated page recurses into subdirectories and will list all <tt>*.fossil</tt> files found, with the following exceptions: * Filenames starting with a period are treated as "hidden" and skipped. * Subdirectory names which match the base name of a fossil file in the same directory are listed in the resulting page but are not hyperlinked because the links would be ambiguous and the repositories in the subdirectories would be unreachable to clients. For example, any repositories under subdirectory <tt>XYZ</tt> are unreachable if <tt>XYZ.fossil</tt> exists in the same directory as <tt>XYZ</tt>, noting that this particular name check is case-insensitive. The entries for such repositories are clearly marked in the repolist page's output to make the user aware of the problem. To make them accessible, move them into a directory which does not share a base name with a repository file. <h2 id="localauth">localauth</h2> This is a Boolean property. If it is present, [./caps/ref.html#s | setup capability] is granted to any HTTP request that comes in over a loopback interface, such as 127.0.0.1. <h2 id="skin">skin: <i>NAME</i></h2> If NAME is the name of one of the built-in skins supported by Fossil, then this option causes Fossil to display using that built-in skin, and to ignore any custom skin that might be configured in the repository itself. So, if you wanted to set up a server for a single Fossil project, but also give users the option to use several of the different built-in skins, you could create multiple CGI scripts, each with a different "<b>skin:</b>" property, but all pointing to the same <b>repository:</b>. Then users can select which skin to use by using the appropriate CGI. <h2 id="files">files: </i>GLOBLIST</i></h2> The GLOBLIST argument is a comma-separate list of "globs" that specify filenames. In [#directory|<b>directory:</b> mode], if the PATH_INFO does not identify any Fossil repository, but it does refer some other file in the directory, and that filename matches one of the glob patterns in the GLOBLIST, then the file is returned as static content. <h2 id="setenv">setenv: <i>NAME VALUE</i></h2> This parameter causes additional environment variable NAME to have VALUE. This parameter can be repeated as many times as necessary. <h2 id="HOME">HOME: <i>PATH</i></h2> This parameter is a short-hand for "<b>setenv HOME <i>PATH</i></b>". <h2 id="cgi-debug">cgi-debug: <i>FILE</i></h2> Cause CGI-related debugging information to be appended in <i>FILE</i>. Use this to help debug CGI problems. <h2 id="errorlog">errorlog: <i>FILENAME</i></h2> This setting causes the server to log any errors in FILENAME. It is ok for multiple Fossil CGIs to share the same error log. Setting up an error log for Fossil servers is not required, but it is recommended. <h2 id="timeout">timeout: <i>N</i></h2> This property changes the timeout on each CGI request to N seconds. If N is zero, then there is no timeout. If this property is omitted, then the default timeout is 300 seconds (5 minutes). <h2 id="extroot">extroot: <i>PATH</i></h2> This property defines the DOCUMENT_ROOT for the [./serverext.wiki|CGI Server Extensions]. If this property is present, then CGI Server Extensions are enabled. When this property is omitted, CGI Server Extensions are disabled. A cascade of CGI invocations can occur here. Fossil itself is started as CGI, then Fossil can turn around and invoke a sub-CGI extension. The sub-CGI extension outputs reply text, when Fossil then (optionally) augments with its own header and footer and returns to the original requestor. The property controls the DOCUMENT_ROOT of the sub-CGI. <h2 id="redirect">redirect: <i>REPO URL</i></h2> Extract the "name" query parameter and search REPO for a check-in or ticket that matches the value of "name", then redirect to URL. There can be multiple "redirect:" lines that are processed in order. If the repo name is "*", then an unconditional redirect to URL is taken. <h2 id="jsmode">jsmode: <i>VALUE</i></h2> Specifies the delivery mode for JavaScript files. See "[/help?cmd=http | http --jsmode]" for the allowed values and their meanings. <h2 id="mainmenu">mainmenu: <i>FILE</i></h2> This parameter causes the contents of the given file to override the site's <tt>mainmenu</tt> configuration setting, much in the same way that the <tt>skin</tt> setting overrides the skin. This can be used to apply a common main menu to a number of sites, and centrally maintain it, without having to copy its contents into each site. Note, however, that the contents of this setting are not stored in the repository and will not be cloned along with the repository. |
Added www/changes.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 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 | <title>Change Log</title> <h2 id='v2_24'>Changes for version 2.24 (pending)</h2> * Apache change work-around → As part of a security fix, the Apache webserver mod_cgi module has stopped relaying the Content-Length field of the HTTP reply header from the CGI programs back to the client in cases where the connection is to be closed and the client is able to read until end-of-file. The HTTP and CGI specs allow for this, though it does seem rude. Older versions of Fossil was depending on the Content-Length header field being set. To work around the change to Apache, Fossil has been enhanced to cope with a missing Content-Length in the reply header. See [forum:/forumpost/12ac403fd29cfc89|forum thread 12ac403fd29cfc89]. * [./customskin.md|Skin] enhancements: <ul> <li> Reworked the default skin to make everything more readable: larger fonts, more whitespace, deeper indents to show hierarchy and to offset command examples, etc. Adjusted colors slightly to bring things into better accord with the WCAG accessibility guidelines. This constitutes a <strong>breaking change</strong> for those with custom skins; see [./customskin.md#version-2.24 | this section of the docs] for migration advice. <li> Add a new link added to the [/login] page that allows the user to [/skins|select their preferred skin]. This preference is stored in the [/fdscookie|fossil display_settings cookie]. <li> The /setup_skin_admin page is simplified to let administrators easily select one of the built-in skins as a default, or to specify a custom skin. </ul> * If an "ssh:" sync fails in a way that suggests that the fossil executable could not be found on the remote host, then retry after adding a PATH= prefix to the command. This helps "ssh:" to "just work" when the server is a Mac. * Enhancements to the [/help?cmd=/timeline|/timeline page]: <ul> <li> Add the x= query paramater <li> Add the shortcut tl= and rl= query parameters <li> Add support for from=,ft= and from=,bt= query parameter combinations <li> Automatically highlight the endpoints for from=,to= queries. <li> Add the to2=Z query parameter to augment from=X,to=Y so that the path from X to Z is shown if Y cannot be found. </ul> * Moved the /museum/repo.fossil file referenced from the Dockerfile from the ENTRYPOINT to the CMD part to allow use of --repolist mode. * The [/uvlist] page now shows the hash algorithm used so that viewers don't have to guess. And the hash is shows in a fixed-width font for a more visually pleasing display. * If the [/help?cmd=autosync|autosync setting] contains keyword "all", the automatic sync occurs against all defined remote repositories, not just the default. * Markdown formatter: improved handling of indented fenced code blocks that contain blank lines. * When doing a "[/help?cmd=add|fossil add]" on a system with case-insensitive but case-preserving filenames (Mac and Windows) try to use the filename case as it is known to the filesystem, not the case entered by the user on the command-line. See [forum:/forumpost/30d9c0d131610f53|forum thread 30d9c0d131610f53]. * Fix problems with one-click unsubscribe on email notifications. * Import the latest [/doc/trunk/www/pikchr.md|Pikchr] containing support for "diamond" objects. * Add ability to render committed Pikchr files to SVG via <samp>/doc/…/foo.pikchr?popup</samp> URLs. * Update Fossil's internal robot detection logic so that it correctly identifies the new GoogleOther crawler as a robot. <h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2> * Add ability to "close" forum threads, such that unprivileged users may no longer respond to them. Only administrators can close threads or respond to them by default, and the [/help?cmd=forum-close-policy|forum-close-policy setting] can be used to add that capability to moderators. * Add the [/help?cmd=all|fossil all whatis] command. * The [/help?cmd=status|fossil status] command and relevant UI pages now correctly report files which were both renamed <b>and</b> edited as such. * Show default value of settings that have a default in [/help?cmd=help|fossil help SETTING] output. * On timeline graphs, show closed check-ins using an X in the middle of the node circle or box. * New options for email notification: Get email only for the first post in each new thread, and/or posts that are in reply to my posts. * Fix a regression bug introduced in version 2.22 that caused FTS5 searches to fail for terms containing non-ASCII characters. * Improved defense-in-depth against malicious attack: <ul> <li> When an attempted SQL injection attack is detected, return HTTP result code 418, which can signal the web server to sanction the attacking IP address. <li> Better defense against cross-site request forgery (CSRF) attacks. <li> Improvements to static analysis of source code (the codecheck1.c file in the source tree). </ul> * Enhance the [/help?cmd=/dir|treeview file listings] ([/dir?type=tree&ci=trunk|example]) by displaying file sizes and adding the option to sort by file size. * The [/help?cmd=fts-config|fossil fts-config] command now shows how much repository space is used by the full-text index. * Changing a setting to an empty string is now the same as deleting the setting, in most cases. There are a few exceptions, indicated by the keep-empty flag on the setting definition. * The [/help?cmd=branch|fossil branch list] command can now filter branches that have/have not been merged into the current branch. * Improvements to interactions with remote repositories over SSH: <ul> <li> Print the text of the SSH command that is run to do remote interaction, for full disclosure to the operator. <li> Add a PATH= argument to the [/help?cmd=ui|fossil ui remote:/] and [/help?cmd=patch|fossil patch push/pull remote:...] commands so that they work when the "remote" machine is a Mac and the "fossil" executable is in the $HOME/bin directory. </ul> * Update built-in libraries SQLite, ZLib, Pikchr to their latest versions. * Documentation enhancements and typo fixes. <h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2> * Enhancements to the [/help?cmd=/timeline|/timeline webpage]: <ol type="a"> <li> Add the ft=TAG query parameter which in combination with d=Y shows all descendants of Y up to TAG <li> Enhance the s=PATTERN (search) query parameter so that forum post text is also searched when the "vfx" query parameter is used <li> Fix the u= (user) query parameter so that it works with a= and b= <li> Add the oldestfirst query parameter to show the events in reverse order. Useful in combination with y=f and vfs and perhaps also u= to show all forum events in chronological order <li> For the p=X and bt=Y query parameter combination, if Y is a tag that identifies multiple check-ins, search backwards in time for Y beginning at X </ol> * Administrators can select to skip sending notifications about new forum posts. * If the value N is negative in "--context N" or "-c N" to the various diff commands, then treat it as infinite and show the complete file content. * The stock OCI container no longer includes BusyBox, thus no longer needs to start as root to chroot that power away. That in turn frees us from needing to build and install the container as root, since it no longer has to create a private <tt>/dev</tt> tree inside the jail for Fossil's use. * Add support for the trigram tokenizer for FTS5 search to enable searching in Chinese. * Comment lines (starting with a '#') are now supported inside [./settings.wiki#versionable|versioned settings]. * Default permissions for anonymous users in new repositories are changed to "hz". * The [/help?cmd=status|fossil status] command now detects when a file used to be a symlink and has been replaced by a regular file. (It previously checked for the inverse case only.) * The [/help?cmd=empty-dirs|empty-dirs setting] now reuses the same parser as the *-glob settings instead of its prior idiosyncratic parser, allowing quoted whitespace in patterns. * Enhancements to the [/help?cmd=/reports|/reports webpage]: <ol type="a"> <li> The by-week, by-month, and by-year options now show an estimated size of the current week, month, or year as a dashed box. <li> New sub-categories "Merge Check-ins" and "Non-Merge Check-ins". </ol> <h2 id='v2_21'>Changes for version 2.21 (2023-02-25)</h2> * Users can request a password reset. This feature is disabled by default. Use the new [/help?cmd=self-pw-reset|self-pw-reset property] to enable it. New web pages [/help?cmd=/resetpw|/resetpw] and [/help?cmd=/reqpwreset|/reqpwreset] added. * Add the [/help?cmd=repack|fossil repack] command (together with [/help?cmd=all|fossil all repack]) as a convenient way to optimize the size of one or all of the repositories on a system. * Add the ability to put text descriptions on ticket report formats. * Upgrade the test-find-pivot command to the [/help/merge-base|merge-base command]. * The [/help?cmd=/chat|/chat page] can now embed fossil-rendered views of wiki/markdown/pikchr file attachments with the caveat that such embedding happens in an iframe and thus does not inherit styles and such from the containing browser window. * The [/help?cmd=all|fossil all remote] subcommand added to "fossil all". * Passwords for remembered remote repositories are now stored as irreversible hashes rather than obscured clear-text, for improved security. * Add the "nossl" and "nocompress" options to CGI. * Update search infrastructure from FTS4 to FTS5. * Add the [/help?cmd=/deltachain|/deltachain] page for debugging purposes. * Writes to the database are disabled by default if the HTTP request does not come from the same origin. This enhancement is a defense in depth measure only; it does not address any known vulnerabilities. * Improvements to automatic detection and mitigation of attacks from malicious robots. <h2 id='v2_20'>Changes for version 2.20 (2022-11-16)</h2> * Added the [/help?cmd=chat-timeline-user|chat-timeline-user setting]. If it is not an empty string, then any changes that would appear on the timeline are announced in [./chat.md|the chat room]. * The /unsubscribe page now requests confirmation. [./alerts.md|Email notifications] now contain only an "Unsubscribe" link, and not a link to subscription management. * Added the "[/help?cmd=branch|fossil branch lsh]" subcommand to list the most recently modified branches. * More elements of the /info page are now inside of an accordion. * Replace the <tt>--dryrun</tt> flag with <tt>--dry-run</tt> in all commands which still used the former name, for consistency. * Rebuilt [/file/Dockerfile | the stock Dockerfile] to create a "from scratch" Busybox based container image via an Alpine Linux intermediary * Added [/doc/trunk/www/containers.md | a new document] describing how to customize, use, and run that container. * Added "by hour of day" report to [/reports?view=byhour|the /reports page]. * Improved correctness, usability, and efficiency for the case [/timeline?r=deltify-tkt-blobs|when values in a TICKET's column tend to be long and volatile]. * Fixed a bug [/info/ea5afad31f478396 | introduced in 2.17] that prevented <tt>clone --unversioned</tt> from completing the retrieval of UV files from the remote repo. While fixing that, enabled UV tracing output with <tt>clone --unversioned --verbose</tt>, making it consonant with <tt>uv sync --verbose</tt>. <h2 id='v2_19'>Changes for version 2.19 (2022-07-21)</h2> * On file listing pages, sort filenames using the "uintnocase" collating sequence, so that filenames that contains embedded integers sort in numeric order even if they contain a different number of digits. (Example: "fossil_80_..." comes before "fossil_100.png" in the [/dir?ci=92fd091703a28c07&name=skins/blitz|/skins/blitz] directory listing.) * Enhancements to the graph layout algorithm design to improve readability and promote better situational awareness. * Performance enhancement for the [./checkin_names.wiki#root|"root:BRANCHNAME" style of tag], accomplished using a Common Table Expression in the underlying SQL. * Sort tag listings (command line and webpage) by taking numbers into consideration so as to cater for tags that follow semantic versioning. * On the wiki listings, omit by default wiki pages that are associated with check-ins and branches. * Add the new "[/help?cmd=describe|fossil describe]" command. * Markdown subsystem extended with [../src/markdown.md#ftnts|footnotes support]. See corresponding [../test/markdown-test3.md|test cases], [/wiki?name=branch/markdown-footnotes#il|known limitations] and [forum:/forumthread/ee1f1597e46ec07a|discussion]. * Add the new special name "start:BRANCH" to refer to the first check-in of the branch. * Support [/wiki?name=branch/generated-tkt-mimetype&p|generated "mimetype"] columns in the <var>TICKET</var> and <var>TICKETCHNG</var> tables. * Fix [/timeline?r=fix_remote_url_overwrite_with_proxy|remote-url-overwrite] bug where remote-url is overwritten by the proxy setting during sync operation. Also require explicit "system" proxy setting to use "http_proxy" environment variable. * Reimplemented the [/pikchrshow] app to use a WebAssembly build of pikchr so that it can render pikchrs on the client instead of requiring a server round-trip. * Add the [/help?cmd=email-listid|email-listid setting]. If set, it is used as the List-ID header for all outbound notification emails. * Add the "--branch" option to the "[/help?cmd=timeline|timeline]" command to restrict the displayed items to a specific branch. * Add the "--versions" option to "[/help?cmd=diff|fossil diff]" to display details about the compared versions into the patch header. * Numerous other minor enhancements. <h2 id='v2_18'>Changes for version 2.18 (2022-02-23)</h2> * Added support for [./ssl-server.md|SSL/TLS server mode] for commands like "[/help?cmd=server|fossil server]" and "[/help?cmd=http|fossil http]" * The new [/help?cmd=cherry-pick|cherry-pick command] is an alias for [/help?cmd=merge|merge --cherrypick]. * Add new setting "[/help?cmd=large-file-size|large-file-size]". If the size of any file in a commit exceeds this size, a warning is issued. * Query parameter "year=YYYY" is now accepted by [/help?cmd=/timeline|/timeline]. * The [/help?cmd=tar|tar] and [/help?cmd=zip|zip commands] no longer sterilize the manifest file. * Further improvement to diff alignment in cases that involve both edits and indentation changes. * [/doc/trunk/www/chat.md|Chat] improvements:<ul> <li> [/help?cmd=/chat|The /chat page] input options have been reworked again for better cross-browser portability. <li> When sending a [/help?cmd=/chat|/chat] message fails, it is no longer immediately lost and sending may optionally be retried. <li> [/help?cmd=/chat|/chat] can now optionally embed attachments of certain types directly into message bodies via an iframe. <li> Add the "--as FILENAME" option to the "[/help?cmd=chat|fossil chat send]" command. <li> Added the "[/help?cmd=chat|fossil chat pull]" command, available to administrators only, for backing up the chat conversation. </ul> * Promote the test-detach command into the [/help?cmd=detach|detach command]. * For "[/help?cmd=pull|fossil pull]" with the --from-parent-project option, if no URL is specified then use the last URL from the most recent prior "fossil pull --from-parent-project". * Add options --project-name and --project-desc to the "[/help?cmd=init|fossil init]" command. * The [/help?cmd=/ext|/ext page] generates the SERVER_SOFTWARE environment variable for clients. * Fix the REQUEST_URI [/doc/trunk/www/aboutcgi.wiki#cgivar|CGI variable] such that it includes the query string. This is how most other systems understand REQUEST_URI. * Added the --transport-command option to [/help?cmd=sync|fossil sync] and similar. <h2 id='v2_17'>Changes for version 2.17 (2021-10-09)</h2> * Major improvements to the "diff" subsystem, including: <ul> <li> Added new [/help?cmd=diff|formatting options]: --by, -b, --webpage, --json, --tcl. <li> Partial-line matching for unified diffs <li> Better partial-line matching for side-by-side diffs <li> Buttons on web-based diffs to show more context <li> Performance improvements </ul> * The --branchcolor option on [/help?cmd=commit|fossil commit] and [/help?cmd=amend|fossil amend] can now take the value "auto" to force Fossil to use its built-in automatic color choosing algorithm. * Fossil now [./concepts.wiki#workflow|autosyncs] prior to running [/help?cmd=open|fossil open]. * Add the [/help?cmd=ticket-default-report|ticket-default-report setting], which if set to the title of a ticket report causes that ticket report to be displayed below the search box in the /ticket page. * The "nc" query parameter to the [/help?cmd=/timeline|/timeline] page causes all graph coloring to be omitted. * Improvements and bug fixes to the new "[/help?cmd=ui|fossil ui REMOTE]" feature so that it works better on a wider variety of platforms. * In [/help?cmd=/wikiedit|/wikiedit], show the list of attachments for the current page and list URLs suitable for pasting them into the page. * Add the --no-http-compression option to [/help?cmd=sync|fossil sync] and similar. * Print total payload bytes on a [/help?cmd=sync|fossil sync] when using the --verbose option. * Add the <tt>close</tt>, <tt>reopen</tt>, <tt>hide</tt>, and </tt>unhide</tt> subcommands to [/help?cmd=branch|the branch command]. * The "-p" option to [/help?cmd=branch|fossil branch list] shows only private branches. * The [/md_rules|Markdown formatter] now interprets the content of block HTML markup (such as <table>) in most cases. Only content of <pre> and <script> is passed through verbatim. * The [/help?cmd=wiki|wiki list command] no longer lists "deleted" pages by default. Use the new <tt>--all</tt> option to include deleted pages in the output. * The [/help?cmd=all|fossil all git status] command only shows reports for the subset of repositories that have a configured Git export. * The [/help?cmd=/chat|/chat] configuration was reimplemented and provides new options, including the ability for a repository administrator to [./chat.md#notifications|extend the selection of notification sounds] using unversioned files. * Chat now uses fossil's full complement of markdown features, instead of the prior small subset of markup it previously supported. This retroactively applies to all chat messages, as they are markdown-processed when they are sent instead of when they are saved. * Added a chat message preview mode so messages can be previewed before being sent. Similarly, added a per-message ability to view the raw un-parsed message text. * The hotkey to activate preview mode in [/help?cmd=/wikiedit|/wikiedit], [/help?cmd=/fileedit|/fileedit], and [/help?cmd=/pikchrshow|/pikchrshow] was changed from ctrl-enter to shift-enter in order to align with [/help?cmd=/chat|/chat]'s new preview feature and related future changes. <h2 id='v2_16'>Changes for Version 2.16 (2021-07-02)</h2> * <b>Security:</b> Fix the client-side TLS so that it verifies that the server hostname matches its certificate. * The default "ssh" command on Windows is changed to "ssh" instead of the legacy "plink", as ssh is now generally available on Windows systems. Installations that still need to use the legacy "plink" can make that happen by running: '<tt>fossil set ssh-command "plink -ssh" --global</tt>'. * Added the [./patchcmd.md|fossil patch] command. * The [/help?cmd=ui|fossil ui] command is enhanced in multiple ways:<ol> <li> The REPOSITORY argument can be the name of a check-out directory. <li> If the REPOSITORY argument is prefixed by "HOST:" or "USER@HOST:" then the ui is run on the remote machine and tunnelled back to the local machine using ssh. (The latest version of fossil must be installed on both the local and the remote for this to work correctly.) <li> The new --nobrowser and --fossilcmd options is provided. </ol> * The [/brlist|/brlist web page] allows the user to select multiple branches to be displayed together in a single timeline. * The [./forum.wiki|Forum] provides a hyperlink on the author of each post that goes to a timeline of recent posts by that same author. * Added the "[/help?cmd=bisect|fossil bisect run]" command for improved automation of bisects. * The [/help?cmd=merge|fossil merge] command now does a better job merging branches where files have been renamed between the current branch and the branch being merged. * The [/help?cmd=open|fossil open] command allows the repository file to be inside the working directory without requiring the --force flag. * The [/help?cmd=/wikiedit|/wikiedit] and [/help?cmd=/wikinew|/wikinew] pages now default to markdown format. * The [/help?cmd=/login|/login] page now links to a user's forum post timeline if the repository has forum posts. * Tags may now be propagated for forum posts, wiki pages, and technotes. The [/help?cmd=tag|tag command] can now manipulate and list such tags. * [./caps/login-groups.md|Login-Groups] are now shown on the repository list of the "[/help?cmd=all|fossil all ui]" command. * Administrators can configure [./alerts.md|email alerts] to expire a specific number of days (ex: 365) after the last user contact with the Fossil server. This prevents alert emails being sent to abandoned email accounts forever. * SQL that defines [/tktsetup_tab|database objects for tickets] now [/timeline?c=c717f1ef9a1a4c91|can DROP] a VIEW or an INDEX provided that its name starts with '<code>ticket</code>' or '<code>fx_</code>'. * Update the built-in SQLite to version 3.36.0. * Numerous other minor enhancements. <h2 id='v2_15'>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07) and 2.15.2 on (2021-06-15)</h2> * <b>Patch 2.15.2:</b> Fix the client-side TLS so that it verifies that the server hostname matches its certificate. <b>Upgrading to the patch is recommended.</b> * <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to the patch is recommended.</b> * The [./defcsp.md|default CSP] has been relaxed slightly to allow images to be loaded from any URL. All other resources are still locked down by default. * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]" setting to determine the content of the main menu. The ability to edit the "mainmenu" setting is added on the /Admin/Configuration page. * The hamburger menu is now available on most of the built-in skins. * Any built-in skin named "X" can be used instead of the standard repository skin by adding the URL parameter <tt>skin=X</tt> to the request. The selection is persisted using the display preferences cookie unless the "once" query parameter is also included. The [/skins] page may be used to select a skin. * The [/cookies] page now gives the user an opportunity to delete individual cookies. And the /cookies page is linked from the /sitemap, so that it appears in hamburger menus. * The [/sitemap] extensions are now specified by a single new "[/help?cmd=sitemap-extra|sitemap-extra setting]", rather than a cluster of various "sitemap-*" settings. The older settings are no longer used. <b>This change might require minor server configuration adjustments on servers that use /sitemap extensions.</b> The /Admin/Configuration page provides the ability to edit the new "sitemap-extra" setting. * Added the "--ckout-alias NAME" option to [/help?cmd=ui|fossil ui], [/help?cmd=server|fossil server], and [/help?cmd=http|fossil http]. This option causes Fossil to understand URIs of the form "/doc/NAME/..." as if they were "[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of [./embeddeddoc.wiki|embedded documentation] changes prior to check-in. * For diff web pages, if the diff type (unified versus side-by-side) is not specified by a query parameter, and if the "[/help?cmd=preferred-diff-type|preferred-diff-type]" setting is omitted or less than 1, then select the diff type based on a guess of whether or not the request is coming from a mobile device. Mobile gets unified and desktop gets side-by-side. * The various pages which show diffs now have toggles to show/hide individual diffs. * Add the "[/help?cmd=preferred-diff-type|preferred-diff-type]" setting to allow an admin to force a default diff type. * The "pikchr-background" setting is now available in "detail.txt" skin files, for better control of Pikchr colors in inverted color schemes. * Add the <tt>--list</tt> option to the [/help?cmd=tarball|tarball], [/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar] commands. * The javascript used to implement the hamburger menu on the default built-in skin has been made generic so that it is usable by a variety of skins, and promoted to an ordinary built-in javascript file. * New TH1 commands: "[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]", "[/doc/trunk/www/th1.md#capexpr|capexpr]", "foreach", "lappend", and "string match" * The [/help/leaves|leaves command] now shows the branch point of each leaf. * The [/help?cmd=add|fossil add] command refuses to add files whose names are reserved by Windows (ex: "aux") unless the --allow-reserved option is included. This helps prevent Unix users from accidentally creating check-ins that are unreadable by Windows users. * Add the "re=" query parameter to the [/help?cmd=/dir|/dir] webpage, for symmetry with the [/help?cmd=/tree|/tree] page. * Update the built-in SQLite to version 3.35.0. * The ./configure script now has the --print-minimum-sqlite-version option that prints the minimum SQLite version required by the current version of Fossil. This might be used by integrators who insist on building Fossil to link against the system SQLite library rather than the built-in copy of SQLite, to verify that their system SQLite library is recent enough. * Webpage that shows [/help?cmd=/whistory|history of a wiki page] gained client-side UI to help with comparison between two arbitrary versions of a wiki (by the means of anchoring a "baseline" version) and the ability to squeeze several sequential edits made by the same user into a single "recycled" row (the latest edit in that sequence). <h2 id='v2_14'>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07) and 2.14.2 on (2021-06-15)</h2> * <b>Patch 2.14.2:</b> Fix the client-side TLS so that it verifies that the server hostname matches its certificate. <b>Upgrading to the patch is recommended.</b> * <b>Patch 2.14.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to the patch is recommended.</b> * <b>Schema Update Notice #1:</b> This release drops a trigger from the database schema (replacing it with a TEMP trigger that is created as needed). This change happens automatically the first time you add content to a repository using Fossil 2.14 or later. No action is needed on your part. However, if you upgrade to version 2.14 and then later downgrade or otherwise use an earlier version of Fossil, the email notification mechanism may fail to send out notifications for some events, due to the missing trigger. If you want to permanently downgrade an installation, then you should run "[/help?cmd=rebuild|fossil rebuild]" after the downgrade to get email notifications working again. If you are not using email notification, then the schema change will not affect you in any way. * <b>Schema Update Notice #2:</b> This release changes how the descriptions of wiki edits are stored in the EVENT table, for improved display on timelines. You must run "[/help?cmd=rebuild|fossil rebuild]" to take advantage of this enhancement. Everything will still work without "fossil rebuild", except you will get goofy descriptions of wiki updates in the timeline. * Add support for [./chat.md|Fossil chat]. * The "[/help?cmd=clone|fossil clone]" command is enhanced so that if the repository filename is omitted, an appropriate name is derived from the remote URL and the newly cloned repo is opened. This makes the clone command work more like Git, thus making it easier for people transitioning from Git. * Added the --mainbranch option to the [/help?cmd=git|fossil git export] command. * Added the --format option to the "[/help?cmd=timeline|fossil timeline]" command. * Enhance the --numstat option on the "[/help?cmd=diff|fossil diff]" command so that it shows a total number of lines added and deleted and total number of files modified. * Add the "contact" sub-command to [/help?cmd=user|fossil user]. * Added commands "[/help?cmd=all|fossil all git export]" and "[/help?cmd=all|fossil all git status]". * Added the "df=CHECKIN" query parameter to the [/help?cmd=/timeline|/timeline page]. * Improvements to the "[/sitemap]" page. Add subpages [/sitemap-timeline] and [/sitemap-test]. * Better text position in cylinder objects of Pikchr diagrams. * New "details.txt" settings available to custom skins to better control the rendering of Pikchr diagrams: <ul> <li> pikchr-foreground <li> pikchr-scale <li> pikchr-fontscale </ul> * Allow the use of SQL functions inside the ticket table definition for custom ticket configurations. * The built-in SQLite is updated to version 3.35.0 alpha containing performance optimizations, especially performance associated with startup, and minor improvements to the CLI. * Performance optimizations to Fossil itself. * Countless improvements and enhancements to the documentation <h2 id='v2_13'>Changes for Version 2.13 (2020-11-01)</h2> * Added support for [./interwiki.md|interwiki links]. * Enable <del> and <ins> markup in wiki. * Improvements to the Forum threading display. * Added support for embedding [./pikchr.md|pikchr] markup in markdown and fossil-wiki content. * The new "[/help?cmd=pikchr|pikchr]" command can render pikchr scripts, optionally pre-processed with [/doc/trunk/www/th1.md|TH1] blocks and variables exactly like site skins are. * The new [/help?cmd=/pikchrshow|pikchrshow] page provides an editor and previewer for pikchr markup. * In [/help?cmd=/wikiedit|/wikiedit] and [/help?cmd=/fileedit|/fileedit], Ctrl-Enter can now be used initiate a preview and to toggle between the editor and preview tabs. * The <tt>/artifact</tt> and <tt>/file</tt> views, when in line-number mode, now support interactive selection of a range of lines to hyperlink to. * Enhance the [/help?cmd=/finfo|/finfo] webpage so that when query parameters identify both a filename and a checkin, the resulting graph tracks the identified file across renames. * The built-in SQLite is updated to an alpha of version 3.34.0, and the minimum SQLite version is increased to 3.34.0 because the /finfo change in the previous bullet depends on enhancements to recursive common table expressions that are only available in SQLite 3.34.0 and later. * Countless other minor refinements and documentation improvements. <h2 id='v2_12'>Changes for Version 2.12.1 (2020-08-20)</h2> * (2.12.1): Fix client-side vulnerabilities discovered by Max Justicz. * Security fix in the "[/help?cmd=git|fossil git export]" command. The same fix is also backported to version 2.10.1 and 2.11.1. New "safety-net" features were added to prevent similar problems in the future. * Enhancements to the graph display for cases when there are many cherry-pick merges into a single check-in. [/timeline?f=2d75e87b760c0a9|Example] * Enhance the [/help?cmd=open|fossil open] command with the new --workdir option and the ability to accept a URL as the repository name, causing the remote repository to be cloned automatically. Do not allow "fossil open" to open in a non-empty working directory unless the --keep option or the new --force option is used. * Enhance the markdown formatter to more closely follow the [https://spec.commonmark.org/0.29/#emphasis-and-strong-emphasis|CommonMark specification] with regard to text highlighting. Underscores in the middle of identifiers (ex: fossil_printf()) no longer need to be escaped. * The markdown-to-html translator can prevent unsafe HTML (for example: <script>) on user-contributed pages like forum and tickets and wiki. The admin can adjust this behavior using the [/help?cmd=safe-html|safe-html setting] on the Admin/Wiki page. The default is to disallow unsafe HTML everywhere. [https://fossil-scm.org/forum/forumpost/3714e6568f|Example]. * Added the "collapse" and "expand" capability for long forum posts. [https://fossil-scm.org/forum/forumpost/9297029862|Example] * The "[/help?cmd=remote-url|fossil remote]" command now has options for specifying multiple persistent remotes with symbolic names. Currently only one remote can be used at a time, but that might change in the future. * Add the "Remember me?" checkbox on the login page. Use a session cookie for the login if it is not checked. * Added the experimental "[/help?cmd=hook|fossil hook]" command for managing "hook scripts" that run before checkin or after a push. * Enhance the [/help?cmd=revert|fossil revert] command so that it is able to revert all files beneath a directory. * Add the [/help?cmd=bisect|fossil bisect skip] command. * Add the [/help?cmd=backup|fossil backup] command. * Enhance [/help?cmd=bisect|fossil bisect ui] so that it shows all unchecked check-ins in between the innermost "good" and "bad" check-ins. * Added the <tt>--reset</tt> flag to the "[/help?cmd=add|fossil add]", "[/help?cmd=rm|fossil rm]", and "[/help?cmd=addremove|fossil addremove]" commands. * Added the "<tt>--min</tt> <i>N</i>" and "<tt>--logfile</tt> <i>FILENAME</i>" flags to the [/help?cmd=backoffice|backoffice] command, as well as other enhancements to make the backoffice command a viable replacement for automatic backoffice. Other incremental backoffice improvements. * Added the [/help?cmd=/fileedit|/fileedit page], which allows editing of text files online. Requires explicit activation by a setup user. * Translate built-in help text into HTML for display on web pages. [/help?cmd=help|Example]. * On the [/help?cmd=/timeline|/timeline] webpage, the combination of query parameters "p=CHECKIN" and "bt=ANCESTOR" draws all ancestors of CHECKIN going back to ANCESTOR. For example, [/timeline?p=202006271506&bt=version-2.11] shows all ancestors of the checkin that occurred on 2020-06-27 15:06 going back to the 2.11 release. * Update the built-in SQLite so that the "[/help?cmd=sql|fossil sql]" command supports new output modes ".mode box" and ".mode json". * Add the "<tt>obscure()</tt>" SQL function to the "[/help?cmd=sql|fossil sql]" command. * Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to the "[/help?cmd=sql|fossil sql]" command, providing access to the dispatch table including all help text, and the builtin data files, respectively. * [./delta_format.wiki|Delta compression] is now applied to forum edits. * The [/help?cmd=/wikiedit|wiki editor] has been modernized and is now Ajax-based. The WYSIWYG editing option for Fossil-format wiki pages was removed. (Please let us know, via the site's Forum menu, if that removal unduly impacts you.) This also changes the semantics of the wiki "Sandbox": that pseudo-page may be freely edited but no longer saved via the UI (the [/help?cmd=wiki|wiki CLI command] can, though). * The [/help?cmd=allow-symlinks|allow-symlinks setting] no longer syncs. It must be activated individually on any clones which require symlinks. * Countless documentation enhancements. <h2 id='v2_11'>Changes for Version 2.11 (2020-05-25)</h2> * (2.11.2): Backport security fixes from 2.12.1 * (2.11.1): Backport security fix for the "fossil git export" command. * Support [/md_rules|Markdown] in the default ticket configuration. * Timestamp strings in [./checkin_names.wiki|object names] can now omit punctation. So, for example, "202004181942" and "2020-04-18 19:42" mean the same thing. * Enhance backlink processing so that it works with Markdown-formatted tickets and so that it works for wiki pages. Ticket [a3572c6a5b47cd5a]. <ul><li> "[/help?cmd=rebuild|fossil rebuild]" is needed to take full advantage of this fix. Fossil will continue to work without the rebuild, but the new backlinks will be missing.</ul> * The algorithm for finding the [./tech_overview.wiki#configloc|location of the configuration database] is enhanced to be XDG-compliant. * Add a hide/show feature to [./wikitheory.wiki#assocwiki|associated wiki] display on check-in and branch information pages. * Enhance the "[/help?cmd=info|fossil info]" command so that it works with no arguments even if not within an open check-out. * Many improvements to the forum and especially email notification of forum posts, in response to community feedback after switching SQLite support from a mailing list over to the forum. * Minimum length of a self-registered user ID increased from 3 to 6 characters. * When the "vfx" query parameter is used on the "[/help?cmd=/timeline|/timeline]" page, it causes the complete text of forum posts to be displayed. * Rework the "[/help?cmd=grep|fossil grep]" command to be more useful. * Expose the [/help?cmd=redirect-to-https|redirect-to-https] setting to the [/help?cmd=settings|settings] command. * Improve support for CGI on IIS web servers. * The [./serverext.wiki|/ext page] can now render index files, in the same way as the embedded docs. * Most commands now support the Unix-conventional "<tt>--</tt>" flag to treat all following arguments as filenames instead of flags. * Added the [/help?cmd=mimetypes|mimetypes config setting] (versionable) to enable mimetype overrides and custom definitions. * Add an option on the /Admin/Timeline setup page to set a default timeline style other than "Modern". * In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated into the check-in hash for the document currently being viewed. * Added the [/help?cmd=/phantoms|/phantoms] webpage that shows all phantom artifacts. * Enhancements to phantom processing to try to reduce bandwidth-using chatter about phantoms on the sync protocol. * Security: Fossil now assumes that the schema of every database it opens has been tampered with by an adversary and takes extra precautions to ensure that such tampering is harmless. * Security: Fossil now puts the Content-Security-Policy in the HTTP reply header, in addition to also leaving it in the HTML <head> section, so that it is always available, even if a custom skin overrides the HTML <head> and omits the CSP in the process. * Output of the [/help?cmd=diff|fossil diff -y] command automatically adjusts according to the terminal width. * The Content-Security-Policy is now set using the [/help?cmd=default-csp|default-csp setting]. * Merge conflicts caused via the [/help?cmd=merge|merge] and [/help?cmd=update|update] commands no longer leave temporary files behind unless the new <tt>--keep-merge-files</tt> flag is used. * The [/help?cmd=/artifact_stats|/artifact_stats page] is now accessible to all users if the new "artifact_stats_enable" setting is turned on. There is a new checkbox under the /Admin/Access menu to turn that capability on and off. * Add the [/help?cmd=tls-config|fossil tls-config] command for viewing the TLS configuration and the list of SSL Cert exceptions. * Captchas all include a button to read the captcha using an audio file, so that they can be completed by the visually impaired. * Stop using the IP address as part of the login cookie. * Bug fix: fix the SSL cert validation logic so that if an exception is allowed for particular site, the exception expires as soon as the cert changes values. * Bug fix: the FTS search into for forum posts is now kept up-to-date correctly. * Bug fix: the "fossil git export" command is now working on Windows * Bug fix: display Technote items on the timeline correctly * Bug fix: fix the capability summary matrix of the Security Audit page so that it does not add "anonymous" capabilities to the "nobody" user. * Update internal Unicode character tables, used in regular expression handling, from version 12.1 to 13. * Many documentation enhancements. * Many minor enhancements to existing features. <h2 id='v2_10'>Changes for Version 2.10 (2019-10-04)</h2> * (2.10.2): backport security fixes from 2.12.1 * (2.10.1): backport security fix for the "fossil git export" command. * Added support for [./serverext.wiki|CGI-based Server Extensions]. * Added the [/help?cmd=repolist-skin|repolist-skin] setting used to add style to repository list pages. * Enhance the hierarchical display of Forum threads to do less indentation and to provide links back to the previous message in the thread. Provide sequential numbers for all messages in a forum thread. * Add support for fenced code blocks and improved hyperlink processing to the [/md_rules|markdown formatter]. * Add support for hyperlinks to wiki pages in the [/md_rules|markdown formatter]. * Enhance the [/help?cmd=/stat|/stat] page so that it gives the option to show a breakdown of forum posts. * The special check-in name "merge-in:BRANCH" means the source of the most recent merge-in from the parent branch of BRANCH. * Add hyperlinks to branch-diffs on the /info page and from timelines of a branch. * Add graphical context on the [/help?cmd=/vdiff|/vdiff] page. * Uppercase query parameters, POST parameters, and cookie names are converted to all lowercase and entered into the parameter set, instead of being discarded. * Change the default [./hashpolicy.wiki|hash policy] to SHA3. * Timeout [./server/any/cgi.md|CGI requests] after 300 seconds, or some other value set by the [./cgi.wiki#timeout|"timeout:" property] in the CGI script. * The check-in lock interval is reduced from 24 hours to 60 seconds, though the interval is now configurable using a setting. An additional check for conflicts is added after interactive check-in comment entry, to compensate for the reduced lock interval. * Performance optimizations. * Many documentation improvements. <h2 id='v2_9'>Changes for Version 2.9 (2019-07-13)</h2> * Added the [/help?cmd=git|fossil git export] command and instructions for [./mirrortogithub.md|creating a GitHub mirror of a Fossil project]. * Improved handling of relative hyperlinks on the [/help?cmd=/artifact|/artifact] pages for wiki. For example, hyperlinks and the lizard <img> now work correctly for both [/artifact/2ff24ab0887cf522] and [/doc/0d7ac90d575004c2415/www/index.wiki]. * Enhancements to the timeline graph layout, to show more information with less clutter. * Added tool-tips to the /timeline graph. On by default but can be disabled by setting the "Tooltip dwell time" to 0 in the timeline configuration. * Copy buttons added to various check-in hash and branch name links. * Double-clicking on a /timeline graph node now jumps to the /info page for the check-in. So, repurpose the timestamp hyperlink to show all activity around that check-in in time. * Added the [/help?cmd=touch|fossil touch] command, and the --setmtime option on the [/help?cmd=open|fossil open] and [/help?cmd=update|fossil update] commands. * Many documentation enhancements. * For the "[/help?cmd=update|fossil update]" and "[/help?cmd=checkout|fossil checkout]" commands, if a managed file is removed because it is no longer part of the target check-in and the directory containing the file is empty after the file is removed and the directory is not the current working directory and is not on the [/help?cmd=empty-dirs|empty-dirs] list, then also remove the directory. * Update internal Unicode character tables, used in regular expression handling, from version 11.0 to 12.1. * In "[/help?cmd=regexp|fossil regexp]", "[/help?cmd=grep|fossil grep]" and the TH1 "regexp" command, the -nocase option now removes multiple diacritics from the same character (derived from SQLite's remove_diacritics=2) * Added the [/help?cmd=/secureraw|/secureraw] page that requires the complete SHA1 or SHA3 hash, not just a prefix, before it will deliver content. * Accept purely numeric ISO8601 date/time strings as long as they do not conflict with a hash. Example: "20190510134217" instead of "2019-05-10 13:42:17". This helps keep URLs shorter and less complicated * Support both "1)" and "1." for numbered lists in markdown, as commonmark does. * The sync and clone HTTP requests omit the extra /xfer path element from the end of the request URI. All servers since 2010 know that the HTTP request is for a sync or clone from the mimetype so the extra path element is not needed. * If an automatic sync gets a permanent redirect request, then update the saved remote URL to the new address. * Temporary filenames (for example used for external "diff" commands) try to preserve the suffix of the original file. * Added the [/help?cmd=/thisdayinhistory|/thisdayinhistory] web page. * Enhanced parsing of [/help?cmd=/timeline|/timeline] query parameters "ymd=", "ym=", and "yw=". All arguments are option (in which case they default to the current time) and all accept ISO8601 date/times without punctuation. * Automatically disapprove pending moderation requests for a user when that user is deleted. This helps in dealing with spam-bots. * Improvements to the "Capability Summary" section in the [/help?cmd=/secaudit0|Security Audit] web-page. * Use new "ci-lock" and "ci-lock-failed" pragmas in the [./sync.wiki|sync protocol] to try to prevent accident forks caused by concurrent commits when operating in auto-sync mode. * Fix a bug ([https://fossil-scm.org/forum/forumpost/c51b9a1169|details]) that can cause repository databases to be overwritten with debugging output, thus corrupting the repository. This is only a factor when CGI debugging is enabled, and even then is a rare occurrence, but it is obviously an important fix. <h2 id='v2_8'>Changes for Version 2.8 (2019-02-20)</h2> * Show cherry-pick merges as dotted lines on the timeline graph. → The "fossil rebuild" command must be run to create and populate the new "cherrypick" table in the repository in order for this feature to operate. * Add the ability to associate branches, check-ins, and tags with specially-named Wiki pages. This gives the ability to better document branches and tags, and provide more documentation on check-ins beyond the check-in comment. The associated Wiki is automatically displayed on /info pages for check-ins, and on /timeline?r=BRANCH and /timeline?t=TAG pages for branches and tags. This feature is on by default, but can be disabled in on the Admin/Wiki page. * Enhance the repository list page (shown for example by "fossil all ui") so that it shows the name and last check-in time for each project. The implementation of the repository list page is now broken out into a separate source file (repolist.c). * Allow users with Forum Supervisor permission ('6') to add Forum Write Trusted permission ('4') to users as they are approving a forum post by that user. * When running a bisect, report the number of check-ins still in the search range and the estimated number of bisect steps remaining. Do this at each step of the bisect. * Provide a permanent link to a bisect timeline using the bid= query parameter. * Make the chronological forum display feature available to all users, and make it the default format on mobile devices. * Break out Wiki setup into a separate /setup_wiki page, accessible on the standard menus through Admin/Wiki. * Add "Next" and "Previous" buttons on the /wdiff page, allowing the user to step through the versions of a wiki page. * Improve the display of the /whistory page. * Omit the "HH:MM" timestamps on timeline graphs on narrow-screen devices, to improve horizontal space uses. This helps make Fossil more mobile-friendly. * Enhance /wcontent to show a sortable list of Wiki pages together with the number of revisions and the most recent change time for each page. * Hyperlinks to Wiki pages on the /timeline go to the specific version of the Wiki page named in the timeline, not to the latest version. * Enhancements to the "amend", "tag", and "reparent" commands, including adding options --override-date, --override-user, and --dry-run. * Add the global --comment-format command-line option and the comment-format setting to control the display of the command-line timeline. * Change the "fossil reparent" command so that it only works from within an active checkout. * On the /setup_ucap_list, show administrators how many users have each capability. The counts are a hyperlink to the /setup_ulist page showing the subset of users that have that capability. * Provide the ability to redirect all HTTP pages to HTTPS. Formerly one could cause this to occur for the /login page only. That option still exists, but the redirect can now also be done for all pages. * "Compress" the built-in javascript by omitting comments and leading and trailing whitespace. * Detect when the repository used by a checkout is swapped out for a clone that uses different RID values, and make appropriate adjustments to the checkout database to avoid any problems. * Add the backoffice-disable setting to completely disable the backoffice feature. * Update the built-in SQLite to version 3.27.1. * Various other small enhancements to webpages and documentation. <h2 id='v2_7'>Changes for Version 2.7 (2018-09-22)</h2> * Add the [./alerts.md|email alerts] feature for commits, ticket changes, wiki changes, forum posts, and announcements. This is still a work in progress. It is functional, but it is not as easy to setup and use as it ought to be. * Add the [./forum.wiki|discussion forum] feature. * Add new user capabilities letters needed to support alerts and forum. Formerly, user capabilities were letters from [a-z], but with the enhancements, the supply of lower case letters was exhausted. User capabilities are now letters in [a-zA-Z0-9]. * The built-in skins are now responsive, providing better layout on small screens, including mobile devices. * The default skin now includes a hamburger menu that is generated by the [/sitemap] page. * All of the built-in skins now use a [https://en.wikipedia.org/wiki/Content_Security_Policy|Content Security Policy (CSP)] to help prevent cross-site injection and forgery attacks. There are no known vulnerabilities in Fossil. The added CSP does not fix anything; it merely adds another layer of defense. * The [/sitemap] and other list pages show as multiple columns if the viewing window is wide enough. * There is an optional "js" file for each skin that can be used to hold javascript. This file can be loaded by reference or can be included in the header or footer. * Add the [./backoffice.md|backoffice]. * Update internal Unicode character tables, used in regular expression handling, from version 10.0 to 11.0. * Improvements to the "Security Audit" administration page * Add the [/help?cmd=branch|fossil branch current] command. * Add the [./grep.md|grep] command. * Update the built-in SQLite to version 3.25.1. * Some code and interfaces are in place to support sending and receiving email directly via SMTP, but this feature is not yet complete or ready for production use. * The `mv-rm-files` setting is now compiled into Fossil in the default Fossil configuration; no longer must you say <tt>./configure --with-legacy-mv-rm</tt> to make it available. The setting remains disabled by default, however, so you must still say <tt>fossil set mv-rm-files 1</tt> to enable it on each repository where you want hard <tt>mv/rm</tt> behavior. <h2 id='v2_6'>Changes for Version 2.6 (2018-05-04)</h2> * Fix a bug that was causing crashes while trying to clone the TCL repository. This fix is the main reason for the current release. * Added the new "Classic" timeline viewing mode. "Classic" is the same as "Verbose" in the previous release. The "Verbose" mode is now like "Compact" except the extra check-in details are shown by default. * Add support for ETags:, Last-Modified:, and If-Modified-Since: cache control mechanisms. * Enhance the [/help?cmd=/tarball|/tarball], [/help?cmd=/zip|/zip], and [/help?cmd=/sqlar|/sqlar] pages so that the checkin name to be downloaded can be expressed as part of the URI, and without the need for query parameters. * On the [/help?cmd=/timeline|/timeline] webpage, add the days=N query parameter and enhance the ymd=DATE and yw=DATE query parameters to accept 'now' as an argument to show the latest day or week. * In the web page that comes up in response to the [/help?cmd=all|fossil all ui] command, show the last modification time for each repository, and allow click-to-sort on the modification time column. * In the tarball cache replacement algorithm, give extra weight to tarballs that have been accessed more than once. * Additional defenses against web-based attacks. There have not been any known vulnerabilities. We are just being paranoid. * Update the built-in SQLite to an alpha version of 3.24.0. <h2 id='v2_5'>Changes for Version 2.5 (2018-02-07)</h2> * Numerous enhancements to the look and feel of the web interface. Especially: Added separate "Modern", "Compact", "Verbose", and "Columnar" view options on timelines. * Common display settings (such as the "view" option and the number of rows in a timeline) are held in a cookie and thus persist across multiple pages. * Rework the skin editing process so that changes are implemented on one of nine /draft pages, evaluated, then merged back to the default. * Added the [https://fossil-scm.org/skins/ardoise/timeline|Ardoise] skin. * Fix the "fossil server" command on Unix to be much more responsive to multiple simultaneous web requests. * Use the IPv6 stack for the "fossil ui" and "fossil server" commands on Windows. * Support for [https://sqlite.org/sqlar|SQL Archives] as a download option. * Fossil now automatically generates the <html><head>...</head><body> at the beginning of each web page if the configurable header lacks a <body> tag. * Added the /artifact_stats page, currently accessible only by the administrator. * Upgrade to the latest versions of SQLite and OpenSSL. * Improved key bindings on the Tk diff screen generated by "fossil diff --tk". * Begin factoring out in-line javascript into separately loaded script files. This is a step along the road toward supporting a strict Content Security Policy. More work is to be done. * Initial infrastructure is in place to make use of the pledge() system call in OpenBSD. More work is to be done. <h2 id='v2_4'>Changes for Version 2.4 (2017-11-03)</h2> * New feature: URL Aliases. URL Aliases allow an administrator to define their own URLs on the web interface that are rewritten to built-in URLs with specific parameters. Create and configure URL Aliases using the /Setup/URL_Aliases menu option in the web interface. * Add tech-note search capability. * Add the -r|--revision and -o|--origin options to the [/help?cmd=annotate|annotate] command. * Add the origin= query parameter to the [/help?cmd=/annotate|/annotate] webpage. * The [/help?cmd=annotate|fossil annotate] command and the [/help?cmd=/annotate|/annotate] web page go backwards in time as far as can be computed in 30 milliseconds by default, rather than stopping after 20 steps. The new limit= query parameter or the --limit command-line option can be used to alter this timeout. * Provide separate [/help#settings|on-line help screens for each setting]. * Back out support for the --no-dir-symlinks option * Remove support from the legacy configuration sync protocol. The only way now to do a configuration push or pull is to use the new protocol that was added in 2011. * Add the from= and to= query parameters to [/help?cmd=/fdiff|/fdiff] in order to get a diff of two files in the same check-in. * Fix the "ssh://" protocol to prevent an attack whereby the attacker convinces a victim to run a "clone" with a dodgy URL and thereby gains access to their system. * Provide a checkbox that will temporarily disable all ad-units. * Improvements to the [/help?cmd=/stat|/stat] page * Various new hyperlinks to the [/help?cmd=/bloblist|/bloblist] and [/help?cmd=/bigbloblist|/bigbloblist] pages. * Correct the [/help?cmd=/doc|/doc] page to support read-only repositories. * Correct [/help?cmd=/zip|/zip], [/help?cmd=/tarball|/tarball], [/help?cmd=zip|zip], and [/help?cmd=tarball|tarball] pages and commands to honor the versioned manifest setting when outside of an open checkout directory. * The admin-log and access-log settings are now on by default for new repositories. * Update the built-in SQLite to version 3.21.0. <h2 id='v2_3'>Changes for Version 2.3 (2017-07-21)</h2> * Update the built-in SQLite to version 3.20.0 (beta). * Update internal Unicode character tables, used in regular expression handling, from version 9.0 to 10.0. * Show the last-sync-URL on the [/help?cmd=/urllist|/urllist] page. * Added the "Event Summary" activity report. [/reports?type=ci&view=lastchng|example] * Added the "Security Audit" page, available to administrators only * Added the Last Login time to the user list page, for administrators only * Added the --numstat option to the [/help?cmd=diff|fossil diff] command * Limit the size of the heap and stack on unix systems, as a proactive defense against the [https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt|Stack Clash] attack. * Fix "database locked" warnings caused by "PRAGMA optimize". * Fix a potential XSS vulnerability on the [/help?cmd=/help|/help] webpage. * Documentation updates <h2 id='v2_2'>Changes for Version 2.2 (2017-04-11)</h2> * GIT comment tags are now handled by Fossil during import/export. * Show the content of README files on directory listings. ([/file/skins|example]) * Support for Basic Authentication if enabled (default off). * Show the hash algorithms used on the [/help?cmd=/rcvfromlist|/rcvfromlist] page. * The [/help?cmd=/tarball|/tarball] and [/help?cmd=/zip|/zip] pages now use the the r= query parameter to select which check-in to deliver. The uuid= query parameter is still accepted for backwards compatibility. * Update the built-in SQLite to version 3.18.0. * Run "[https://www.sqlite.org/pragma.html#pragma_optimize|PRAGMA optimize]" on the database connection as it is closing. <h2 id='v2_1'>Changes for Version 2.1 (2017-03-10)</h2> * Add support for [./hashpolicy.wiki|hash policies] that control which of the Hardened-SHA1 or SHA3-256 algorithms is used to name new artifacts. * Add the "gshow" and "gcat" subcommands to [/help?cmd=stash|fossil stash]. * Add the [/help?cmd=/juvlist|/juvlist] web page and use it to construct the [/uv/download.html|Download Page] of the Fossil self-hosting website using Ajax. <h2 id='v2_0'>Changes for Version 2.0 (2017-03-03)</h2> * Use the [https://github.com/cr-marcstevens/sha1collisiondetection|hardened SHA1] implementation by Marc Stevens and Dan Shumow. * Add the ability to read and understand [./fileformat.wiki#names|artifact names] that are based on SHA3-256 rather than SHA1, but do not actually generate any such names. * Added the [/help?cmd=sha3sum|sha3sum] command. * Update the built-in SQLite to version 3.17.0. <h2 id='v1_37'>Changes for Version 1.37 (2017-01-16)</h2> * Add checkbox widgets to various web pages. See [/technote/8d18bf27e9| this technote] for more information. To get the checkboxes to look as intended, you must update the CSS in your repository and all clones. * Add the [/help/all|fossil all ui] command * Add the [/help?cmd=/file|/file] webpage * Enhance the [/help?cmd=/brlist|/brlist] webpage to make use of branch colors. * Add support for the ms=EXACT|LIKE|GLOB|REGEXP query parameter on the [/help?cmd=/timeline|/timeline] webpage, with associated form widgets. * Enhance the [/help/changes|changes] and [/help/status|status] commands with many new filter options so that specific kinds of changes can be found without having to pipe through grep or sed. * Enhanced the [/help/sqlite3|fossil sql] command so that it opens the [./tech_overview.wiki#localdb|checkout database] and the [./tech_overview.wiki#configdb|configuration database] in addition to the repository database. * TH1 enhancements: <ul><li>Add <nowiki>[unversioned content]</nowiki> command.</li> <li>Add <nowiki>[unversioned list]</nowiki> command.</li> <li>Add project_description variable.</li> </ul> * Rename crnl-glob [/help/settings|setting] to crlf-glob, but keep crnl-glob as a compatibility alias. * Added the --command option to the [/help/diff|diff] command. * Fix a C99-ism that prevents the 1.36 release from building with MSVC. * Fix [/help?cmd=ticket|ticket set] when using the "+" prefix with fields from the "ticketchng" table. * Remove the "fusefs" command from builds that do not have the underlying support enabled. * Fixes for incremental git import/export. * Minor security enhancements to [./encryptedrepos.wiki|encrypted repositories]. * Update the built-in SQLite to version 3.16.2. * Update the built-in Zlib to version 1.2.11. <h2 id='v1_36'>Changes for Version 1.36 (2016-10-24)</h2> * Add support for [./unvers.wiki|unversioned content], the [/help?cmd=unversioned|fossil unversioned] command and the [/help?cmd=/uv|/uv] and [/uvlist] web pages. * The [/uv/download.html|download page] is moved into [./unvers.wiki|unversioned content] so that the self-hosting Fossil websites no longer uses any external content. * Added the "Search" button to the graphical diff generated by the --tk option on the [/help?cmd=diff|diff] command. * Added the "--checkin VERSION" option to the [/help?cmd=diff|diff] command. * Various performance enhancements to the [/help?cmd=diff|diff] command. * Update internal Unicode character tables, used in regular expression handling, from version 8.0 to 9.0. * Update the built-in SQLite to version 3.15. Fossil now requires the SQLITE_DBCONFIG_MAINDBNAME interface of SQLite which is only available in SQLite version 3.15 and later and so Fossil will not work with earlier SQLite versions. * Fix [https://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg23618.html|multi-line timeline bug] * Enhance the [/help?cmd=purge|fossil purge] command. * New command [/help?cmd=shell|fossil shell]. * SQL parameters whose names are all lower-case in Ticket Report SQL queries are filled in using HTTP query parameter values. * Added support for [./childprojects.wiki|child projects] that are able to pull from their parent but not push. * Added the -nocomplain option to the TH1 "query" command. * Added support for the chng=GLOBLIST query parameter on the [/help?cmd=/timeline|/timeline] webpage. <h2 id='v1_35'>Changes for Version 1.35 (2016-06-14)</h2> * Enable symlinks by default on all non-Windows platforms. * Enhance the [/md_rules|Markdown formatting] so that hyperlinks that begin with "/" are relative to the root of the Fossil repository. * Rework the [/help?cmd=/setup_ulist|/setup_list page] (the User List page) to display all users in a click-to-sort table. * Fix backslash-octal escape on filenames while importing from git * When markdown documents begin with <h1> HTML elements, use that header at the document title. * Added the [/help?cmd=/bigbloblist|/bigbloblist page]. * Enhance the [/help?cmd=/finfo|/finfo page] so that when it is showing the ancestors of a particular file version, it only shows direct ancestors and omits changes on branches, thus making it show the same set of ancestors that are used for [/help?cmd=/blame|/blame]. * Added the --page option to the [/help?cmd=ui|fossil ui] command * Added the [/help?cmd=bisect|fossil bisect ui] command * Enhanced the [/help?cmd=diff|fossil diff] command so that it accepts directory names as arguments and computes diffs on all files contained within those directories. * Fix the [/help?cmd=add|fossil add] command so that it shows "SKIP" for files added that were already under management. * TH1 enhancements: <ul><li>Add <nowiki>[array exists]</nowiki> command.</li> <li>Add minimal <nowiki>[array names]</nowiki> command.</li> <li>Add tcl_platform(engine) and tcl_platform(platform) array elements.</li> </ul> * Get autosetup working with MinGW. * Fix autosetup detection of zlib in the source tree. * Added autosetup detection of OpenSSL when it may be present under the "compat" subdirectory of the source tree. * Added the [/help?cmd=reparent|fossil reparent] command * Added --include and --exclude options to [/help?cmd=tarball|fossil tarball] and [/help?cmd=zip|fossil zip] and the in= and ex= query parameters to the [/help?cmd=/tarball|/tarball] and [/help?cmd=/zip|/zip] web pages. * Add support for [./encryptedrepos.wiki|encrypted Fossil repositories]. * If the FOSSIL_PWREADER environment variable is set, then use the program it names in place of getpass() to read passwords and passphrases * Option --baseurl now works on Windows. * Numerous documentation improvements. * Update the built-in SQLite to version 3.13.0. <h2 id='v1_34'>Changes for Version 1.34 (2015-11-02)</h2> * Make the [/help?cmd=clean|fossil clean] command undoable for files less than 10MiB. * Update internal Unicode character tables, used in regular expression handling, from version 7.0 to 8.0. * Add the new [/help?cmd=amend|amend] command which is used to modify tags of a "check-in". * Fix bug in [/help?cmd=import|import] command, handling version 3 of the svndump format for subversion. * Add the [/help?cmd=all|all cache] command. * TH1 enhancements: <ul><li>Add minimal <nowiki>[lsearch]</nowiki> command. Only exact case-sensitive matching is supported.</li> <li>Add the <nowiki>[glob_match]</nowiki>, <nowiki>[markdown]</nowiki>, <nowiki>[dir]</nowiki>, and <nowiki>[encode64]</nowiki> commands.</li> <li>Add the <nowiki>[tclIsSafe] and [tclMakeSafe]</nowiki> commands to the Tcl integration subsystem.</li> <li>Add 'double', 'integer', and 'list' classes to the <nowiki>[string is]</nowiki> command.</li> </ul> * Add the --undo option to the [/help?cmd=diff|diff] command. * Build-in Antirez's "linenoise" command-line editing library for use with the [/help?cmd=sqlite3|fossil sql] command on Unix platforms. * Add [/help?cmd=stash|stash cat] as an alias for the [/help?cmd=stash|stash show] command. * Automatically pull before [/help?cmd=merge|fossil merge] when auto-sync is enabled. * Fix --hard option to [/help?cmd=mv|fossil mv] and [/help?cmd=rm|fossil rm] to enable them to work properly with certain relative paths. * Change the mimetype for ".n" and ".man" files to text/plain. * Display improvements in the [/help?cmd=bisect|fossil bisect chart] command. * Updated the built-in SQLite to version 3.9.1 and activated JSON1 and FTS5 support (both currently unused within Fossil). <h2 id='v1_33'>Changes for Version 1.33 (2015-05-23)</h2> * Improved fork detection on [/help?cmd=update|fossil update], [/help?cmd=status|fossil status] and related commands. * Change the default skin to what used to be called "San Francisco Modern". * Add the [/repo-tabsize] web page * Add [/help?cmd=import|fossil import --svn], for importing a subversion repository into fossil which was exported using "svnadmin dump". * Add the "--compress-only" option to [/help?cmd=rebuild|fossil rebuild]. * Use a pie chart on the [/reports?view=byuser] page. * Enhanced [/help?cmd=clean|fossil clean --verily] so that it ignores keep-glob and ignore-glob settings. Added the -x alias for --verily. * Add the --soft and --hard options to [/help?cmd=rm|fossil rm] and [/help?cmd=mv|fossil mv]. The default is still --soft, but that is now configurable at compile-time or by the mv-rm-files setting. * Improved ability to [./customgraph.md|customize the timeline graph]. * Improvements to the [/sitemap] page. * Automatically adjust the [/help?cmd=timeline|CLI timeline] to the terminal width on Linux. * Added <nowiki>[info commands] and [info vars]</nowiki> commands to TH1. These commands perform the same function as their Tcl counterparts, except they do not accept a pattern argument. * Fix some obscure issues with TH1 expression processing. * Fix titles in search results for documents that are not wiki, markdown, or HTML. * Formally translate TH1 to Tcl return codes and vice-versa, where necessary, in the Tcl integration subsystem. * Add [/help?cmd=leaves|fossil leaves -multiple], for finding multiple leaves on the same branch. * Added the "Blitz" skin option. * Removed the ".fossil-settings/keep-glob" file. It should not have been checked into the repository. * Update the built-in SQLite to version 3.8.10.2. * Make [/help?cmd=open|fossil open] honor ".fossil-settings/allow-symlinks". * Allow [/help?cmd=add|fossil add] to be used on symlinks to nonexistent or unreadable files in the same way as [/help?cmd=addremove|fossil addremove]. * Added fork warning to be issued if sync produced a fork * Update the [/help?cmd=/info|info] page to report when a file becomes a symlink. Additionally show the UUID for files whose types have changed without changing contents or symlink target. * Have [/help?cmd=changes|fossil changes] and [/help?cmd=status|fossil status] report when executable or symlink status changes on otherwise unmodified files. * Permit filtering weekday and file [/help?cmd=/reports|reports] by user. Also ensure the user parameter is preserved when changing types. Add a field for direct entry of the user name to each applicable report. * Create parent directories of [/help?cmd=settings|empty-dirs] if they don't already exist. * Inhibit timeline links to wiki pages that have been deleted. <h2 id='v1_33'>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 id='v1_31'>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 id='v1_30'>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 id='v1_29'>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 change 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 servers which do not support them. * Minor improvements to sync support on Windows. * Added <tt>--scgi</tt> option to [/help?cmd=server | fossil server]. * Internal improvements to the sync process. * The internals of the JSON API are now MIT-licensed, so downstream users/packagers are no longer affected by the "do no evil" license clause. <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. * Consistent use of the -n or --dry-run command line options. * Win32: Fossil now understands Cygwin paths containing one or more of the characters <nowiki>"*:<>?|</nowiki>. Those are normally forbidden in win32. This means that the win32 fossil.exe is better usable in a Cygwin environment. See [http://cygwin.com/cygwin-ug-net/using-specialnames.html#pathnames-specialchars]. * Cygwin: Fossil now understands win32 absolute paths starting with a drive 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 hyperlinks inside of <nowiki>[...]</nowiki> are decorated.) * Only decorate <nowiki>[...]</nowiki> in check-in comments and tickets if the contented text really is a valid hyperlink target. * Improvements to the side-by-side diff algorithm, for a more human-friendly display in some complex cases. * Added <nowiki>[utime] and [stime]</nowiki> commands to TH1. These commands can be used for things such as displaying the page rendering time in the footer. * Add the ability to pass command-line options of "fossil rebuild" to "fossil all rebuild". * Add the --deanalyze option to "fossil rebuild" (and "fossil all rebuild") * Do not run the graphical merging tool nor leave merge-droppings after a 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 "fossil 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 timeline page so that by default the path between the two specified check-ins avoids merges. * Add the --baseurl option to "fossil server" and "fossil http" commands, so that those commands can be used with reverse proxies. * If unable to determine the command-line user, do not guess. Instead issue an error message. This helps prevent check-ins from accidentally occurring under the wrong username. * Include branch information in the output of file change listings (the "finfo" webpage). * Make the simplified view of file history, rather than the full view, the default. * In the "fossil configuration" command, allow the "css" option for synchronizing, importing, or exporting just the CSS file. This makes it easier to share CSS files across repositories by exporting from one and importing to another. * Add the (unsupported) "fossil test-orphans" command. * Add the --template option to the "fossil init" command, to facilitate creating new repositories based on a template repository. * Add the diff-binary setting, which if enabled causes binary files to be passed to the "gdiff" command for it to deal with, rather than simply printing a "cannot diff binary files" error. * Add the --unified option to the "fossil diff" command to force a unified diff even if the --tk option (which normally implies a side-by-side diff) is used. * Present a choice of nearby branches and versions to diff against on the check-in information page. * Add the --force option to the "fossil merge" command that will force the merge to occur even if it would be a no-op. This is sometimes useful for documentation purposes. * Add another built-in skin: "Enhanced Default". Other minor tweaks to the existing skins. * Add the "urllist" webpage, showing a list of URLs by which a server instance of Fossil has been accessed. Requires "Administrator" privileges. A link is on the "Setup" main page. * Enable dynamic loading of the Tcl runtime for installations that want 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 an 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 subrepositories. * Added the --type option to the "fossil tag find" command. * In contexts where only a check-in makes sense, resolve branch and tag names to checkins only, never events or other artifacts. * Improved display of file renames on a diff. A rebuild is required to take full advantage of this change. * Update the built-in SQLite to version 3.7.7. |
Added www/chat.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 242 243 244 | # Fossil Chat ## Introduction Fossil’s developer chatroom feature provides an ephemeral discussion venue for insiders. Design goals include: * **Simple but functional** → Fossil chat is designed to provide a convenient real-time communication mechanism for geographically dispersed developers. Fossil chat is *not* intended as a replacement or competitor for IRC, Slack, Discord, Telegram, Google Hangouts, etc. * **Low administration** → You can activate the chatroom in seconds without having to mess with configuration files or install new software. In an existing [server setup](./server/), simply enable the [C capability](/setup_ucap_list) for users whom you want to give access to the chatroom. * **Ephemeral** → Chat messages do not sync to peer repositories, and they are automatically deleted after a configurable delay (default: 7 days). Individual messages or the entire conversation can be deleted at any time without impacting any other part of the system. Fossil chat is designed for use by insiders - people with check-in privileges or higher. It is not intended as a general-purpose gathering place for random passers-by on the internet. Fossil chat seeks to provide a communication venue for discussion that does *not* become part of the permanent record for the project. For persistent and durable discussion, use the [Forum](./forum.wiki). Because the conversation is intended to be ephemeral, the chat messages are local to a single repository. Chat content does not sync. ## Setup A Fossil repository must be functioning as a [server](./server/) in order for chat to work. To activate chat, simply add the [C capability](/setup_ucap_list) to every user who is authorized to participate. Anyone who can read chat can also post to chat. Setup ("s") and Admin ("a") users always have access to chat, without needing the "C" capability. A common configuration is to add the "C" capability to "Developer" so that any individual user who has the "v" capability will also have access to chat. There are also some settings under /Admin/Chat that control the behavior of chat, though the default settings are reasonable so in most cases those settings can be ignored. The settings control things like the amount of time that chat messages are retained before being purged from the repository database. ## <a id="usage"></a>Usage For users with appropriate permissions, simply browse to the [/chat](/help?cmd=/chat) to start up a chat session. The default skin includes a "Chat" entry on the menu bar on wide screens for people with chat privilege. There is also a "Chat" option on the [Sitemap page](/sitemap), which means that chat will appear as an option under the hamburger menu for many [skins](./customskin.md). Chat messages are subject to [Fossil's full range of Markdown processing](/md_rules). Because chat messages are stored as-is when they arrive from a client, this change applies retroactively to messages stored by previous fossil versions. Files may be sent via chat using the file selection element at the bottom of the page. If the desktop environment system supports it, files may be dragged and dropped onto that element. Files are not automatically sent - selection of a file can be cancelled using the Cancel button which appears only when a file is selected. When the Send button is pressed, any pending text is submitted along with the selected file. Image files sent this way will, by default, appear inline in messages, but each user may toggle that via the settings popup menu, such that images instead appear as downloadable links. Non-image files always appear in messages as download links. ### Deletion of Messages <div class="sidebar">Message deletion is itself a type of message, which is why deletions count towards updates in the recent activity list. (It is counted for the person who performed the deletion, not the author of the deleted comment.) That can potentially lead to odd corner cases where a user shows up in the list but has no messages which are currently visible because they were deleted, or an admin user who has not posted anything but deleted a message. That is a known minor cosmetic-only bug with a resolution of "will not fix."</div> Any user may *locally* delete a given message by clicking on the "tab" at the top of the message and clicking the button which appears. Such deletions are local-only, and the messages will reappear if the page is reloaded. The user who posted a given message, or any Admin users, may additionally choose to globally delete a message from the chat record, which deletes it not only from their own browser but also propagates the removal to all connected clients the next time they poll for new messages. ### <a id='notifications'></a>Customizing New-message Notification Sounds By default, the list of new-message notification sounds is limited to a few built in to the fossil binary. In addition, any [unversioned files](./unvers.wiki) named `alert-sounds/*.{mp3,wav,ogg}` will be included in that list. To switch sounds, tap the "settings" button. ### <a id='connection'></a> Who's Online? Because the chat app has to be able to work over transient CGI-based connections, as opposed to a stable socket connection to the server, real-time tracking of "who's online" is not feasible. Chat offers an optional feature, toggleable in the settings, which can list users who have posted messages in the client's current list of loaded messages. This is not the same thing as tracking who's online, but it gives an overview of which users have been active most recently, noting that "lurkers" (people who post no messages) will not show up in that list, nor does the chat infrastructure have a way to track and present those. That list can be used to filter messages on a specific user by tapping on that user's name, tapping a second time to remove the filter. ### <a id="cli"></a> The `fossil chat` Command Type [fossil chat](/help?cmd=chat) from within any open check-out to bring up a chatroom for the project that is in that checkout. The new chat window will attempt to connect to the default sync target for that check-out (the server whose URL is shown by the [fossil remote](/help?cmd=remote) command). ### <a id="robots"></a> Chat Messages From Robots The [fossil chat send](/help?cmd=chat) can be used by project-specific robots to send notifications to the chatroom. For example, on the [SQLite project](https://sqlite.org/) (for which the Fossil chatroom feature, and indeed all of Fossil, was invented) there are long-running fuzz servers that sometimes run across obscure problems. Whenever this happens, a message is sent to the SQLite developers chatroom alerting them to the problem. The recommended way to allow robots to send chat messages is to create a new user on the server for each robot. Give each such robot account the "C" privilege only. That means that the robot user account will be able to send chat messages, but not do anything else. Then, in the program or script that runs the robot, when it wants to send a chat message, have it run a command like this: ~~~~ fossil chat send --remote https://robot:PASSWORD@project.org/fossil \ --message 'MESSAGE TEXT' --file file-to-attach.txt ~~~~ Substitute the appropriate project URL, robot account name and password, message text and file attachment, of course. ### <a id="chat-robot"></a> Chat Messages For Timeline Events If the [chat-timeline-user setting](/help?cmd=chat-timeline-user) is not a empty string, then any change to the repository that would normally result in a new timeline entry is announced in the chatroom. The announcement appears to come from a user whose name is given by the chat-timeline-user setting. This mechanism is similar to [email notification](./alerts.md) except that the notification is sent via chat instead of via email. ## Implementation Details *You do not need to understand how Fossil chat works in order to use it. But many developers prefer to know how their tools work. This section is provided for the benefit of those curious developers.* The [/chat](/help?cmd=/chat) webpage downloads a small amount of HTML and a small amount of javascript to run the chat session. The javascript uses XMLHttpRequest (XHR) to download chat content, post new content, or delete historical messages. The following web interfaces are used by the XHR: * [/chat-poll](/help?name=/chat-poll) → Downloads chat content as JSON. Chat messages are numbered sequentially. The client tells the server the largest chat message it currently holds, and the server sends back subsequent messages. If there are no subsequent messages, the /chat-poll page blocks until new messages are available. * [/chat-send](/help?name=/chat-send) → Sends a new chat message to the server. * [/chat-delete](/help?name=/chat-delete) → Deletes a chat message. Fossil chat uses the venerable "hanging GET" or "[long polling](wikipedia:/wiki/Push_technology#Long_polling)" technique to recieve asynchronous notification of new messages. This is done because long polling works well with CGI and SCGI, which are the usual mechanisms for setting up a Fossil server. More advanced notification techniques such as [Server-sent events](wikipedia:/wiki/Server-sent_events) and especially [WebSockets](wikipedia:/wiki/WebSocket) might seem more appropriate for a chat system, but those technologies are not compatible with CGI. Downloading of posted files and images uses a separate, non-XHR interface: * [/chat-download](/help?name=/chat-download) → Fetches the file content associated with a post (one file per post, maximum). In the UI, this is accessed via links to uploaded files and via inlined image tags. Chat messages are stored on the server-side in the CHAT table of the repository. ~~~ CREATE TABLE repository.chat( msgid INTEGER PRIMARY KEY AUTOINCREMENT, mtime JULIANDAY, -- Time for this entry - Julianday Zulu lmtime TEXT, -- Client YYYY-MM-DDZHH:MM:SS when message originally sent xfrom TEXT, -- Login of the sender xmsg TEXT, -- Raw, unformatted text of the message fname TEXT, -- Filename of the uploaded file, or NULL fmime TEXT, -- MIMEType of the upload file, or NULL mdel INT, -- msgid of another message to delete file BLOB -- Text of the uploaded file, or NULL ); ~~~ The CHAT table is not cross-linked with any other tables in the repository schema. An administrator can "DROP TABLE chat;" at any time, without harm (apart from deleting all chat history, of course). The CHAT table is dropped when running [fossil scrub --verily](/help?cmd=scrub). On the server-side, message text is stored exactly as entered by the users. The /chat-poll page queries the CHAT table and constructs a JSON reply described in the [/chat-poll documentation](/help?cmd=/chat-poll). The message text is translated into HTML before being converted to JSON so that the text can be safely added to the display using assignment to `innerHTML`. Though `innerHTML` assignment is generally considered unsafe, it is only so with untrusted content from untrusted sources. The chat content goes through sanitization steps which eliminate any potential security vulnerabilities of assigning that content to `innerHTML`. |
Added www/checkin.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 | <title>Check-in Checklist</title> <h2><u>Always</u> run the following checklist prior to <u>every</u> check-in or commit to the Fossil repository:</h2> Before every check-in: 0. <b>fossil user default</b> → your username is correct. 1. <b>fossil diff</b> → <ol type="a"> <li> No stray changes <li> All changes comply with the license <li> All inputs are scrubbed before use <li> No injection attacks via %s formats </ol> 2. <b>fossil extra</b> → no unmanaged files need to be added. 3. The check-in will go onto the desired branch. → Check-ins to trunk normally require approval from the lead programmer (drh). 4. auto-sync is on, or the system clock is verified 5. If sources files have been added or removed, ensure all makefiles and configure scripts have been updated accordingly. Before every check-in to <b>trunk</b>: 6. No compiler warnings on the development machine. 7. The fossil executable that results from a build actually works. <hr> <h2>Commentary</h2> Before you go ahead and push content back to the servers, make sure that the username you are using by default matches your username within the project. Also remember to enable the localauth setting if you intend to make changes via a locally served web UI. Item 1 is the most important step. Consider using <b>gdiff</b> instead of <b>diff</b> if you have a graphical differ configured. Or use the command-line option <b>--tk</b>. Also consider the <b>-v</b> command-line option to show the complete text of newly added files. The recommended command for completing checklist item 1 is: <b>fossil diff --tk -v</b> Look carefully at every changed line in item 1. Make sure that you are not about to commit unrelated changes. If there are two or more unrelated changes present, consider breaking up the commit into two or more separate commits. Always make 100% sure that all changes are compatible with the BSD license, that you have the authority to commit the code in accordance with the [/doc/trunk/www/copyright-release.html | CLA] that you have signed and have on file, and that no NDAs, copyrights, patents, or trademarks are infringed by the check-in. Also check very carefully to make sure that you are not introducing security vulnerabilities. Pay particular attention to the possibility of SQL or HTML injection attacks. Item 2 verifies that you have not added source files but failed to do the necessary "<b>fossil add</b>" to manage them. If you commit without bringing the new file under source control, the check-in will be broken. That, in turn, can cause complications far in the future when we are bisecting for a bug. For item 3, Run "<b>fossil status</b>" or the equivalent to make sure your changes are going into the branch you think they are. Also double-check the branch name when entering change comments. Never check into trunk unless you are expressly authorized to do so. For Item 4, if you are on-network, make sure auto-sync is enabled. This will minimize the risk of an unintended fork. It will also give you a warning if you system clock is set incorrectly. If you are off-network, make sure that your system clock is correct or at least close to correct so that your check-in does not appear out-of-sequence on timelines. On-network commits are preferred, especially for trunk commits. Items 6 and 7 help to ensure that check-ins on the trunk always work. Knowing that the trunk always works makes bisecting much easier. Items 6 and 7 are recommended for all check-ins, even those that are on a branch. But they are especially important for trunk check-ins. We desire that all trunk check-ins work at all times. Any experimental, unstable, or questionable changes should go on a branch and be merged into trunk after further testing. |
Added www/checkin_names.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 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 | <title>Check-in Names</title> <div class="sidebar no-label"> <b>Quick Reference</b> <ul> <li> Hash prefix <li> Branch name <li> Tag name <li> Timestamp: <i>YYYY-MM-DD HH:MM:SS</i> <li> <i>tag-name</i> <big><b>:</b></big> <i>timestamp</i> <li> <b>root <big>:</big></b> <i>branchname</i> <li> <b>start <big>:</big></b> <i>branchname</i> <li> <b>merge-in <big>:</big></b> <i>branchname</i> <li> Special names: <ul> <li> <b>tip</b> <li> <b>current</b> <li> <b>next</b> <li> <b>previous</b> or <b>prev</b> <li> <b>ckout</b> (<a href='./embeddeddoc.wiki'>embedded docs</a> only) </ul> </ul> </div> Many Fossil [/help|commands] and [./webui.wiki | web interface] URLs accept check-in names as an argument. For example, the "[/help/info|info]" command accepts an optional check-in name to identify the specific check-in about which information is desired: <pre style="white-space: pre-wrap"> fossil info <i>checkin-name</i> </pre> You are perhaps reading this page from the following URL: <verbatim> https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki </verbatim> This is an example of an [./embeddeddoc.wiki | embedded documentation] page URL. The "trunk" element of the pathname is a [./glossary.md#check-in | check-in] name that 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 id="canonical">Canonical Check-in Name</h2> The canonical name of a check-in is the hash of its [./fileformat.wiki#manifest | manifest] expressed as a [./hashes.md | long lowercase hexadecimal number]. For example: <pre> fossil info e5a734a19a9826973e1d073b49dc2a16aa2308f9 </pre> The full 40 or 64 character hash is unwieldy to remember and type, though, so Fossil also accepts a unique prefix of the hash, using any combination of upper and lower case letters, as long as the prefix is at least 4 characters long. Hence the following commands all accomplish the same thing as the above: <pre> fossil info e5a734a19a9 fossil info E5a734A fossil info e5a7 </pre> Fossil uses this feature itself, identifying check-ins by 8 to 16-character prefixes of the canonical name in places where it doesn't want to chew up the screen real estate required to display the whole hash. <h2 id="tags">Tags And Branch Names</h2> Using a tag or branch name where a check-in name is expected causes Fossil to choose the most recent check-in with that tag or branch name. So for example, the most recent check-in that is tagged with "release" as of this writing is [b98ce23d4fc]. The command: <pre> fossil info release </pre> …results in the following output: <pre> hash: b98ce23d4fc3b734cdc058ee8a67e6dad675ca13 2020-08-20 13:27:04 UTC parent: 40feec329163103293d98dfcc2d119d1a16b227a 2020-08-20 13:01:51 UTC tags: release, branch-2.12, version-2.12.1 comment: Version 2.12.1 (user: drh) </pre> There are multiple check-ins that are tagged with "release" but (as of this writing) the [b98ce23d4fc] check-in is the most recent so it is the one that is selected. Note that unlike some other version control systems, a "branch" in Fossil is not anything special: it is simply a sequence of check-ins that share a common tag, so the same mechanism that resolves tag names also resolves branch names. <a id="tagpfx"></a> Note also that there can — in theory, if rarely in practice — be an ambiguity between tag names and canonical names. Suppose, for example, you had a check-in with the canonical name deed28aa99… and you also happened to have tagged a different check-in with "deed2". If you use the "deed2" name, does it choose the canonical name or the tag name? In such cases, you can prefix the tag name with "tag:". For example: <pre> fossil info tag:deed2 </pre> The "tag:deed2" name will refer to the most recent check-in tagged with "deed2" rather than the check-in whose canonical name begins with "deed2". <h2 id="whole-branches">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 id="timestamps">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: 1. <i>YYYY-MM-DD</i> 2. <i>YYYY-MM-DD HH:MM</i> 3. <i>YYYY-MM-DD HH:MM:SS</i> 4. <i>YYYY-MM-DD HH:MM:SS.SSS</i> 5. <i>YYYYMMDD</i> 6. <i>YYYYMMDDHHMM</i> 7. <i>YYYYMMDDHHMMSS</i> In the second through the fourth forms, the space between the day and the year can optionally be replaced by an uppercase <b>T</b>, and the entire timestamp can optionally be followed by "<b>z</b>" or "<b>Z</b>". In the fourth form with fractional seconds, any number of digits may follow the decimal point, though due to precision limits only the first three digits will be significant. The final three pure-digit forms without punctuation are only valid if the number they encode is not also the prefix of an artifact hash. 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 a 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. You may prefix a timestamp with the string “date:â€, in which case processing stops immediately, whether the string is parsed correctly and refers to anything within the repository or not. The prefix is therefore useful when the date could be misinterpreted as a tag. For example, a repo could have release tags like “2020-04-01â€, the date the release was cut, but you could force Fossil to interpret that string as a date rather than as a tag by passing “date:2020-04-01â€. For an example of how timestamps are useful, consider the homepage for the Fossil website itself: <pre> https://fossil-scm.org/home/doc/<b>trunk</b>/www/index.wiki </pre> The bold component of that URL is a check-in name. To see the stored content of the Fossil website repository as of January 1, 2009, one has merely to change the URL to the following: <pre> https://fossil-scm.org/home/doc/<b>2009-01-01</b>/www/index.wiki </pre> (Note that this won't roll you back to the <i>skin</i> and other cosmetic configurations as of that date. It also won't change screens like the timeline, which has an independent date selector.) <h2 id="tag-ts">Tag And Timestamp</h2> A check-in name can also take the form of a tag or branch name followed by a colon and then a timestamp. The combination means to take the most recent check-in with the given tag or branch which is not more recent than the timestamp. So, for example: <pre> fossil update trunk:2010-07-01T14:30 </pre> Would cause Fossil to update the working check-out to be the most recent check-in on the trunk that is not more recent than 14:30 (UTC) on July 1, 2010. <h2 id="root">Root Of A Branch</h2> A branch name that begins with the "<tt>root:</tt>" prefix refers to the last check-in on the parent branch prior to the beginning of the branch. Such a label is useful, for example, in computing all diffs for a single branch. The following example will show all changes in the hypothetical branch "xyzzy": <pre> fossil diff --from root:xyzzy --to xyzzy </pre> <a id="merge-in"></a> That doesn't do what you might expect after you merge the parent branch's changes into the child branch: the above command will include changes made on the parent branch as well. You can solve this by using the prefix "<tt>merge-in:</tt>" instead of "<tt>root:</tt>" to tell Fossil to find the most recent merge-in point for that branch. The resulting diff will then show only the changes in the branch itself, omitting any changes that have already been merged in from the parent branch. <a id="start"></a> The prefix "<tt>start:</tt>" gives the first check-in of the named branch. The prefixes "<tt>root:</tt>", "<tt>start:</tt>", and "<tt>merge-in:</tt>" can be chained: one can say for example <pre> fossil info merge-in:xyzzy:2022-03-01 </pre> to get informations about the most recent merge-in point on the branch "xyzzy" that happened on or before March 1, 2022. <h2 id="special">Special Tags</h2> The tag "tip" means the most recent check-in. The "tip" tag is practically equivalent to the timestamp "9999-12-31". This special name works anywhere you can pass a "NAME", such as with <tt>/info</tt> URLs: <pre> http://localhost:8080/info/tip </pre> There are several other special names, but they only work from within a check-out directory because they are relative to the current checked-out version: * "current": the current checked-out version * "next": the youngest child of the current checked-out version * "previous" or "prev": the primary (non-merge) parent of "current" Therefore, you can use these names in a <tt>fossil info</tt> command, but not in an <tt>/info</tt> URL, for example. For embedded documentation URLs only, there is one more special name, "ckout". See [./embeddeddoc.wiki#ckout | its coverage elsewhere] for more details. You cannot currently use "ckout" anywhere other than in <tt>/doc</tt> URLs. <h2 id="examples">Additional Examples</h2> To view the changes in the most recent check-in prior to the version currently checked out: <pre> fossil diff --from previous --to current </pre> Suppose you are of the habit of tagging each release with a "release" tag. Then to see everything that has changed on the trunk since the last release: <pre> fossil diff --from release --to trunk </pre> <h2 id="order">Resolution Order</h2> Fossil currently resolves name strings to artifact hashes in the following order: # Exact matches on [#special | the special names] # [#timestamps | Timestamps], with preference to ISO8601 forms # [#tagpfx | tag:TAGNAME] # [#root | root:BRANCH] # [#start | start:BRANCH] # [#merge-in | merge-in:BRANCH] # [#tag-ts | TAGNAME:timestamp] # Full artifact hash or hash prefix. # Any other type of symbolic name that Fossil extracts from artifacts. <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
Added www/childprojects.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 | <title>Child Projects</title> <h2>Background</h2> The default behavior of Fossil is to share everything (all check-ins, tickets, wiki, etc) between all clients and all servers. Such a policy helps to promote a cohesive design for a cathedral-style project run by a small cliche of developers - the sort of project for which Fossil was designed. But sometimes it is desirable to branch off a side project that does not sync back to the master but does continue to track changes in the master. For example, the master project might be an open-source project like [https://www.sqlite.org/|SQLite] and a team might want to do a proprietary closed-source enhancement to that master project in a separate repository. All changes in the master project should flow forward into the derived project, but care must be taken to prevent proprietary content from the derived project from leaking back into the master. <h2>Child Projects</h2> A scenario such as the above can be accomplished in Fossil by creating a child project. The child project is able to freely pull from the parent, but the parent cannot push or pull from the child nor is the child able to push to the parent. Content flows from parent to child only, and then only at the request of the child. <h2>Creating a Child Project</h2> To create a new child project, first clone the parent. Then make manual SQL changes to the child repository as follows: <verbatim> UPDATE config SET name='parent-project-code' WHERE name='project-code'; UPDATE config SET name='parent-project-name' WHERE name='project-name'; INSERT INTO config(name,value) VALUES('project-code',lower(hex(randomblob(20)))); INSERT INTO config(name,value) VALUES('project-name','CHILD-PROJECT-NAME'); </verbatim> Modify the CHILD-PROJECT-NAME in the last statement to be the name of the child project, of course. The repository is now a separate project, independent from its parent. Clone the new project to the developers as needed. The child project and the parent project will not normally be able to sync with one another, since they are now separate projects with distinct project codes. However, if the "--from-parent-project" command-line option is provided to the "[/help?cmd=pull|fossil pull]" command in the child, and the URL of parent repository is also provided on the command-line, then updates to the parent project that occurred after the child was created will be added to the child repository. Thus, by periodically doing a pull --from-parent-project, the child project is able to stay up to date with all the latest changes in the parent. |
Added www/chroot.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 | # The Server Chroot Jail If you run Fossil as root in any mode that [serves data on the network][srv], and you're running it on Unix or a compatible OS, Fossil will drop itself into a [`chroot(2)` jail][cj] shortly after starting up, once it's done everything that requires root access. Most commonly, you run Fossil as root to allow it to bind to TCP port 80 for HTTP service, since normal users are restricted to ports 1024 and up on OSes where this behavior occurs. Fossil uses the owner of the Fossil repository file as its new user ID when dropping root privileges. When this happens, Fossil needs to have all of its dependencies inside the chroot jail in order to continue work. There are several things you typically need in order to make things work properly: * the repository file(s) * `/dev/null` — create it with `mknod(8)` inside the jail directory ([Linux example][mnl], [OpenBSD example][obsd]) * `/dev/urandom` — ditto * `/proc` — you might need to mount this virtual filesystem inside the jail on Linux systems that make use of [Fossil’s server load shedding feature][fls] * any shared libraries your `fossil` binary is linked to, unless you [configured Fossil with `--static`][bld] to avoid it Fossil does all of this in order to protect the host OS. You can make it bypass the jail part of this by passing <tt>--nojail</tt> to <tt>fossil server</tt>, but you cannot make it skip the dropping of root privileges, on purpose. [bld]: https://fossil-scm.org/home/doc/trunk/www/build.wiki [cj]: https://en.wikipedia.org/wiki/Chroot [fls]: ./loadmgmt.md [mnl]: https://fossil-scm.org/forum/forumpost/90caff30cb [srv]: ./server/ [obsd]: ./server/openbsd/fastcgi.md#chroot |
Added www/ckout-workflows.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 | # Check-Out Workflows Because Fossil separates the concept of “check-out directory†from “repository DB file,†it gives you the freedom to choose from several working styles. Contrast Git, where the two concepts are normally intermingled in a single working directory, which strongly encourages the “update in place†working style. ## <a id="mcw"></a> Multiple-Checkout Workflow With Fossil, it is routine to have multiple check-outs from the same repository: fossil clone https://example.com/repo /path/to/repo.fossil mkdir -p ~/src/my-project/trunk cd ~/src/my-project/trunk fossil open /path/to/repo.fossil # implicitly opens “trunk†mkdir ../release cd ../release fossil open /path/to/repo.fossil release mkdir ../my-other-branch cd ../my-other-branch fossil open /path/to/repo.fossil my-other-branch mkdir ../scratch cd ../scratch fossil open /path/to/repo.fossil abcd1234 mkdir ../test cd ../test fossil open /path/to/repo.fossil 2019-04-01 Now you have five separate check-out directories: one each for: * trunk * the latest tagged public release * an alternate branch you’re working on * a “scratch†directory for experiments you don’t want to do in the other check-out directories; and * a “test†directory where you’re currently running a long-running test to evaluate a user bug report against the version as of last April Fool’s Day. Each check-out operates independently of the others. This multiple-checkouts working style is especially useful when Fossil stores source code in programming languages where there is a “build†step that transforms source files into files you actually run or distribute. Contrast a switch-in-place workflow, where you have to rebuild all outputs from the source files that differ between those versions whenever you switch versions. In the above model, you switch versions with a “`cd`†command instead, so that you only have to rebuild outputs from files you yourself change. This style is also useful when a check-out directory may be tied up with some long-running process, as with the “test†example above, where you might need to run an hours-long brute-force replication script to tickle a [Heisenbug][hb], forcing it to show itself. While that runs, you can open a new terminal tab, “`cd ../trunk`â€, and get back to work. [hb]: https://en.wikipedia.org/wiki/Heisenbug ## <a id="scw"></a> Single-Checkout Workflows Nevertheless, it is possible to work in a more typical Git sort of style, switching between versions in a single check-out directory. #### <a id="idiomatic"></a> The Idiomatic Fossil Way The most idiomatic way is as follows: fossil clone https://example.com/repo /path/to/repo.fossil mkdir work-dir cd work-dir fossil open /path/to/repo.fossil ...work on trunk... fossil update my-other-branch ...work on your other branch in the same directory... Basically, you replace the `cd` commands in the multiple checkouts workflow above with `fossil up` commands. #### <a id="open"></a> Opening a Repository by URI You can instead open the repo’s URI directly: mkdir work-dir cd work-dir fossil open https://example.com/repo Now you have “trunk†open in `work-dir`, with the repo file stored as `repo.fossil` in that same directory. Users of Git may be surprised that it doesn’t create a directory for you and that you `cd` into it *before* the clone-and-open step, not after. This is because we’re overloading the “open†command, which already had the behavior of opening into the current working directory. Changing it to behave like `git clone` would therefore make the behavior surprising to Fossil users. (See [our discussions][caod] if you want the full details.) #### <a id="clone"></a> Git-Like Clone-and-Open Fossil also supports a more Git-like alternative: fossil clone https://fossil-scm.org/fossil cd fossil This results in a `fossil.fossil` repo DB file and a `fossil/` working directory. Note that our `clone URI` behavior does not commingle the repo and check-out, solving our major problem with the Git design. If you want the repo to be named something else, adjust the URL: fossil clone https://fossil-scm.org/fossil/fsl That gets you `fsl.fossil` checked out into `fsl/`. For sites where the repo isn’t served from a subdirectory like this, you might need another form of the URL. For example, you might have your repo served from `dev.example.com` and want it cloned as `my-project`: fossil clone https://dev.example.com/repo/my-project The `/repo` addition is the key: whatever comes after is used as the repository name. [See the docs][clone] for more details. [caod]: https://fossil-scm.org/forum/forumpost/3f143cec74 [clone]: /help?cmd=clone <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
Deleted www/cmd_.wiki-template.
|
| < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_add.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_all.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_cgi.wiki.
|
| < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_changes.wiki.
|
| < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_checkout.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_extra.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_ls.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_mv.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_new.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_rm.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_status.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_sync.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_update.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/cmd_version.wiki.
|
| < < < < < < < < < < < < |
Added www/co-vs-up.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 | # Checkout vs Update Fossil has two commands that look like they do the same thing on initial examination, [`fossil update`][up] and [`fossil checkout`][co], but there are several key differences: 1. `fossil checkout` aborts if there are changed files in the local directory unless you give the `--force` option, whereas `fossil update` merges upstream changes with your local changes. Since Fossil tends to follow the CVS command design, and CVS popularized the [merge on update][cvsmu] workflow, we expect that Fossil’s update behavior is more likely to be what you want. 2. Update triggers an autosync attempt; checkout does not. 3. Several features in `fossil update` do not exist in `fossil checkout`, so developing a habit to type `fossil up` means you’re more likely to have the features you want at hand. 4. Inversely, the `fossil checkout --keep` feature doesn’t exist in `fossil update`, but it’s a rarely-needed operation, so it doesn’t provide a good reason to develop a habit of using `fossil checkout` instead. In summary, these are two separate commands; neither is an alias for the other. They overlap enough that they can be used interchangeably for some use cases, but `update` is more powerful and more broadly useful. [co]: /help?cmd=checkout [cvsmu]: http://web.mit.edu/gnu/doc/html/cvs_7.html#SEC37 [up]: /help?cmd=update |
Added www/collisions.ipynb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "*Pull in the packages we need:*" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "require(stats)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Parameters\n", "\n", "* **cpd** - Checkins per day. Defaults to 20.\n", "\n", "* **days** - Number of days to simulate. Defaults to 10000, or roughly\n", " 40 working years.\n", "\n", "* **winSz** - Size of the commit window as a fraction of `workSec`. Defaults\n", " to 0.01%, which is roughly 3 seconds for the default 8-hour work day, a\n", " a long-ish commit time for Fossil.\n", "\n", "* **workSec** - Seconds in working day, defaulting to 8 hours. This value\n", " only affects the reporting output, not the results of the underlying\n", " simulation. It's a scaling parameter to humanize the results.\n", " \n", "* **spread** - Adjustment factor in computing the standard deviation for our \n", " normally-distributed random numbers. The default gives \"nice\" distributions,\n", " spread over the working day with a low chance of generating values outside\n", " the working day.\n", " \n", " The smaller this value gets (<4), the more spread out the checkins, and\n", " so the lower the chance of collisions. You might want to decrease it a bit\n", " to simulate early and late workers.\n", " \n", " As you increase this value (>4) you're simulating a work environment\n", " where people tend to check their work in closer and closer to mid-day,\n", " which increases the chance of collision." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "collisions <- function(\n", " cpd = 20,\n", " days = 10000,\n", " winSz = 0.01 / 100,\n", " workSec = 8 * 60 * 60,\n", " spread = 4)\n", "{\n", " cat(\"Running simulation...\\n\")\n", "\n", " day = 0\n", " collisions = 0\n", " winSec = workSec * winSz\n", " mean = workSec / 2\n", " sd = workSec / spread\n", "\n", " while (day < days) {\n", " # Create the commit time vector as random values in a normal\n", " # distribution.\n", " times = sort(rnorm(cpd, mean, sd))\n", "\n", " # Are there any pairs in the time vector that are within the\n", " # passed window size? If so, that's a Fossil checkin collision.\n", " i = 1\n", " while (i < cpd) {\n", " if (abs(times[i] - times[i + 1]) < winSec) {\n", " collisions = collisions + 1\n", " }\n", " i = i + 1\n", " }\n", " \n", " day = day + 1\n", " }\n", " \n", " cat(\"Found\", collisions, \"collisions in\", days, (workSec / 3600),\n", " \"hour working days with\", winSec, \"second windows.\\n\")\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Run the following cell, possibly changing parameters documented above:*" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Running simulation...\n", "Found 422 collisions in 10000 8 hour working days with 2.88 second windows.\n" ] } ], "source": [ "collisions()" ] } ], "metadata": { "kernelspec": { "display_name": "R", "language": "R", "name": "ir" }, "language_info": { "codemirror_mode": "r", "file_extension": ".r", "mimetype": "text/x-r-source", "name": "R", "pygments_lexer": "r", "version": "3.3.2" } }, "nbformat": 4, "nbformat_minor": 2 } |
Deleted www/concept1.gif.
cannot compute difference between binary files
Deleted www/concept2.gif.
cannot compute difference between binary files
Changes to www/concepts.wiki.
1 | <title>Fossil Concepts</title> | < | > > > > > | > > > > > > > > > > > > > > > > | | | | > | | 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 | <title>Fossil Concepts</title> <h2>1.0 Introduction</h2> [./index.wiki | Fossil] is a [http://en.wikipedia.org/wiki/Software_configuration_management | software configuration management] system. Fossil is software that is designed to control and track the development of a software project and to record the history of the project. There are many such systems in use today. Fossil strives to distinguish itself from the others by being extremely simple to setup and operate. This document is intended as a quick introduction to the concepts behind Fossil. See also: * [./glossary.md | Glossary] * [./quickstart.wiki | Quick start guide] <h2>2.0 Composition Of A Project</h2> <verbatim type="pikchr float-right"> R1: cylinder "Remote" "Repository" fill 0xadd8e6 rad 70% R2: cylinder same "Remote" "Repository" at 2.5*R1.wid right of R1 spline <-> from R1.e to 0.6<R1.se,R2.sw> then to 0.4<R1.ne,R2.nw> then to R2.w text "HTTPS" at .5<R1.ne,R2.nw> R3: cylinder same "Local" "Repository" fill 0x90ee90 \ at dist(R1.e,R2.w) below .5<R1,R2> spline <-> from .5<R1.s,R1.se> to 0.6<R1.s,R3.w> to 0.5<R1.se,R3.n> to .5<R3.nw,R3.n> \ "HTTPS" above behind R1 spline <-> from R2.sw to .6<R2.sw,R3.n> to .5<R2.s,R3.e> to R3.ne "HTTPS" ljust T1: line from 1.0cm heading 200 from R3.sw go 2.2cm heading 150 then 2.2cm west close \ fill 0xffff00 "Local" below "Source Tree" below T2: line from 1.0cm heading 160 from R3.se same "Local" below "Source Tree" below line <-> from R3.sw to T1.start line <-> from R3.se to T2.start </verbatim> A software project normally consists of a "source tree". A source tree is a hierarchy of files that are used to generate the end product. The source tree changes over time as the software grows and expands and as features are added and bugs are fixed. A snapshot of the source tree at any point in time is called a "version" or "revision" or a "baseline" of the product. In Fossil, we use the name "check-in". A "repository" is a database that contains copies of all historical check-ins for a project. Check-ins are normally stored in the repository in a highly space-efficient compressed format (delta encoding). But that is an implementation detail that you the user need not worry over. Think of the repository as a safe place where all your old check-ins are securely stored away and available for retrieval whenever you need them. A repository in Fossil is a single file on your disk. This file might be rather large (dozens or hundreds of megabytes for a large or long running project) but it is nevertheless just a file. You can move it around, rename it, write it out to a memory stick, or do anything else you normally do with files. Each source tree that is controlled by Fossil is associated with a single repository on the local disk drive. You can tie two or more source trees to a single repository if you want (though one tree per repository is the most common configuration.) So a single repository can be associated with many source trees, but each source tree is associated with only one repository. Fossil source trees may not overlap. A Fossil source tree is identified by a file named "_FOSSIL_" (or ".fslckout", but this article will always use the name "_FOSSIL_") in the root directory of the source tree. Every file that is a sibling of _FOSSIL_ and every file in every subfolder is considered potentially a part of the source tree. The _FOSSIL_ file contains (among other things) the pathname of the repository with which the source tree is associated. On the other hand, the repository has no record of its source trees. So you are free to delete a source tree or move it around without consequence. But if you move or rename or delete a repository, then any source trees associated with that repository |
︙ | ︙ | |||
67 68 69 70 71 72 73 | remote repository into a local repository. Or one can do a "sync" which is a shortcut for doing both a push and a pull at the same time. Fossil also has the concept of "cloning". A "clone" is like a "pull", except that instead of beginning with an existing local repository, a clone begins with nothing and creates a new local repository that is a duplicate of a remote repository. | | > | | | | > | > > | < | > | | | | | | | | | | | | 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 | remote repository into a local repository. Or one can do a "sync" which is a shortcut for doing both a push and a pull at the same time. Fossil also has the concept of "cloning". A "clone" is like a "pull", except that instead of beginning with an existing local repository, a clone begins with nothing and creates a new local repository that is a duplicate of a remote repository. Communication between repositories is normally via HTTPS. (SSH is also supported, as is unencrypted HTTP.) Remote repositories are identified by URL. You can also point a web browser at a repository and get human-readable status, history, and tracking information about the project. <h3 id="artifacts">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> or <a href="http://en.wikipedia.org/wiki/SHA3">SHA3-256</a> hash of the content of that file expressed as either 40 or 64 characters of lower-case hexadecimal. (See the [./hashpolicy.wiki|hash policy document] for information on which algorithm is used, when.) Such a hash is referred to as the Artifact ID. These hash algorithms were created with Fossil's purpose in mind: to provide a highly forgery-resistant identifier for a blob of data, such as a file. Given any file, it is simple to find the artifact ID for that file. But given an artifact ID, it is computationally intractable to generate a file that will have that same artifact ID. Artifact IDs look something like this: <pre> 6089f0b563a9db0a6d90682fe47fd7161ff867c8 59712614a1b3ccfd84078a37fa5b606e28434326 19dbf73078be9779edd6a0156195e610f81c94f9 b4104959a67175f02d6b415480be22a239f1f077 997c9d6ae03ad114b2b57f04e9eeef17dcb82788 </pre> When referring to an artifact using Fossil, you can use a unique prefix of the artifact ID that is four characters or longer. This saves a lot of typing. When displaying artifact IDs, Fossil will usually only show the first 10 digits since that is normally enough to uniquely identify a file. Changing (or adding or removing) a single byte in a file results in a completely different artifact ID. And since the artifact ID is the name of the artifact, making any change to a file results in a new artifact. In this way, artifacts are immutable. |
︙ | ︙ | |||
120 121 122 123 124 125 126 | such a way that it can be handed a set of artifacts in any 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> | | > | | | > > > > > > > > | | | | < | | | | | | | | | | | | | > > > > | | | | < | < | < | > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | 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 | such a way that it can be handed a set of artifacts in any 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. The manifest file is not normally a real file on disk. Instead, the manifest is computed in memory by Fossil whenever it needs it. However, the "fossil setting manifest on" command will cause the manifest file to be materialized to disk, if desired. Both Fossil itself, and SQLite cause the manifest file to be materialized to disk so that the makefiles for these project can read the manifest and embed version information in generated binaries. Fossil automatically generates a manifest whenever you "commit" a new check-in. So this is not something that you, the developer, need to worry with. The format of a manifest is intentionally designed to be simple to parse, so that if you want to read and interpret a manifest, either by hand or with a script, that is easy to do. But you will probably never need to do so. In addition to identifying all files in the check-in, a manifest also contains a check-in comment, the date and time when the check-in was established, who created the check-in, and links to other check-ins from which the current check-in is derived. There is also a couple of checksums used to verify the integrity of the check-in. And the whole manifest might be PGP clearsigned. <h3 id="keyconc">2.3 Key concepts</h3> <ul> <li>A <b>check-in</b> is a set of files arranged in a hierarchy.</li> <li>A <b>repository</b> keeps a record of historical check-ins.</li> <li>Repositories share their changes using <b>push</b>, <b>pull</b>, <b>sync</b>, and <b>clone</b>.</li> <li>A particular <u>version</u> of a particular file is an <b>artifact</b> that is identified by an <b>artifact ID</b>.</li> <li>Artifacts tracked by Fossil are inherently immutable.</li> <li>Fossil automatically generates a <b>manifest</b> file that identifies 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="https://fossil-scm.org/home/uv/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 is a good policy to run it even if it is not strictly necessary. To use Fossil, simply type the name of the executable in your shell, followed by one of the various built-in commands and arguments appropriate for that command. For example: <pre>fossil help</pre> In the next section, when we say things like "use the <b>help</b> command" we mean to use the command name "help" as the first token after the name of the Fossil executable, as shown above. <h2 id="workflow">4.0 Workflow</h2> <verbatim type="pikchr float-right"> down R1: cylinder "Remote" "Repository" fill 0xadd8e6 rad 70% move 150% R2: cylinder same "Local" "Repository" fill 0x90ee90 move 120% T1: line go 2.2cm heading 150 then 2.2cm west close \ fill 0xffff00 "Local" below "Source Tree" below arrow from R2.n+(-0.25cm,+0.25cm) to R1.s+(-0.25cm,-0.25cm) \ "push " rjust arrow from R1.s+(+0.25cm,-0.25cm) to R2.n+(+0.25cm,+0.25cm) \ " pull" ljust " clone" ljust arrow from T1.start+(-0.25cm,+0cm) to R2.s+(-0.25cm,-0.25cm) \ "commit " rjust arrow from R2.s+(+0.25cm,-0.25cm) to T1.start+(+0.25cm,+0cm) \ " open" ljust " update" ljust " merge" ljust </verbatim> Fossil has two modes of operation: <i>"autosync"</i> and <i>"manual-merge"</i> Autosync mode is reminiscent of CVS or SVN in that it automatically keeps your changes in synchronization with your co-workers through the use of a central server. The manual-merge mode is the standard workflow for GIT or Mercurial in that your local repository develops independently of your coworkers and you share and merge your changes manually. An interesting feature of Fossil is that it supports both autosync and manual-merge work flows. The default setting for Fossil is to be in autosync mode. You can change the autosync setting or check the current autosync setting using commands like: <pre> fossil setting autosync on fossil setting autosync off fossil settings </pre> By default, Fossil runs with autosync mode turned on. The authors finds that projects run more smoothly in autosync mode since autosync helps to prevent pointless forking and merging and helps keeps all collaborators working on exactly the same code rather than on their own personal forks of the code. In the author's view, manual-merge mode should be reserved for disconnected operation. <h3>4.1 Autosync Workflow</h3> <ol> <li> Establish a local repository using either the <b>new</b> command to start a new project, or the <b>clone</b> command to make a clone of a repository for an existing project. </li> <li> Establish one or more source trees using the <b>open</b> command with the name of the repository file as its argument. </li> <li> The <b>open</b> command in the previous step populates your local source tree with a copy of the latest check-in. Usually this is what you want. In the rare cases where it is not, use the <b>update</b> command to switch to a different check-in. Use the <b>timeline</b> or <b>leaves</b> commands to identify alternative check-ins to switch to. </li> <li> Edit the code. Add new files to the source tree using the <b>add</b> command. Omit files from future check-ins using the <b>rm</b> command. (Even when you remove files from future check-ins, those files continue to exist in historical check-ins.) Test your changes. </li> <li> Create a new check-in using the <b>commit</b> command. You will be prompted for a check-in comment and also for your GPG key if you have GPG installed. The commit copies the edits you have made in your local source tree into your local repository. After your commit completes, Fossil will automatically <b>push</b> your changes back to the server you cloned from or whatever server you most recently synced with. </li> <li> When your coworkers make their own changes, you can merge those changes into your local local source tree using the <b>update</b> command. In autosync mode, <b>update</b> will first go back to the server you cloned from or with which you most recently synced, and pull down all recent changes into your local repository. Then it will merge recent changes into your local source tree. If you do an <b>update</b> and find that it messes something up in your source tree (perhaps a co-worker checked in incompatible changes) you can use the <b>undo</b> command to back out the changes. </li> <li> Repeat all of the above until you have generated great software. </li> </ol> |
︙ | ︙ | |||
357 358 359 360 361 362 363 | 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 | | | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | 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 |
︙ | ︙ | |||
379 380 381 382 383 384 385 | </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 | | < | | < | < < < < < < | < < < < < | < < < < < > | | | | | > > > | < < < < < | < < < < | < < < < < < < < < < < < < < < < < < < < < < | 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 | </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: # <b>Stand-alone server.</b> Simply run the [/help?cmd=server|fossil server] or [/help?cmd=ui|fossil ui] command from the command-line. <br><br> # <b>CGI.</b> Install a 2-line CGI script on a CGI-enabled web-server like Apache. <br><br> # <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. <br><br> # <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. See the [./server/ | 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> |
︙ | ︙ |
Added www/contact.md.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # Contact Information ## Questions, Suggestions, and Bug Reports The developers for Fossil monitor the [Fossil Forum][1]. Post there with questions, improvement suggestions, and/or bug reports. [1]: https://fossil-scm.org/forum/forum ## Security Problems and Vulnerabilities If you think you have discovered a security vulnerability in Fossil and want to report the problem privately, send email to "drh at sqlite dot org". |
Added www/containers.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 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 | # OCI Containers This document shows how to build Fossil into [OCI] compatible containers and how to use those containers in interesting ways. We start off using the original and still most popular container development and runtime platform, [Docker], but since you have more options than that, we will show some of these options later on. [Docker]: https://www.docker.com/ [OCI]: https://opencontainers.org/ ## 1. Quick Start Fossil ships a `Dockerfile` at the top of its source tree, [here][DF], which you can build like so: $ docker build -t fossil . If the image built successfully, you can create a container from it and test that it runs: $ docker run --name fossil -p 9999:8080/tcp fossil This shows us remapping the internal TCP listening port as 9999 on the host. This feature of OCI runtimes means there’s little point to using the “`fossil server --port`†feature inside the container. We can let Fossil default to 8080 internally, then remap it to wherever we want it on the host instead. Our stock `Dockerfile` configures Fossil with the default feature set, so you may wish to modify the `Dockerfile` to add configuration options, add APK packages to support those options, and so forth. The Fossil `Makefile` provides two convenience targets, “`make container-image`†and “`make container-run`â€. The first creates a versioned container image, and the second does that and then launches a fresh container based on that image. You can pass extra arguments to the first command via the Makefile’s `DBFLAGS` variable and to the second with the `DCFLAGS` variable. (DB is short for “`docker build`â€, and DC is short for “`docker create`â€, a sub-step of the “run†target.) To get the custom port setting as in second command above, say: $ make container-run DCFLAGS='-p 9999:8080/tcp' Contrast the raw “`docker`†commands above, which create an _unversioned_ image called `fossil:latest` and from that a container simply called `fossil`. The unversioned names are more convenient for interactive use, while the versioned ones are good for CI/CD type applications since they avoid a conflict with past versions; it lets you keep old containers around for quick roll-backs while replacing them with fresh ones. [DF]: /file/Dockerfile ## 2. <a id="storage"></a>Repository Storage Options If you want the container to serve an existing repository, there are at least two right ways to do it. The wrong way is to use the `Dockerfile COPY` command, because by baking the repo into the image at build time, it will become one of the image’s base layers. The end result is that each time you build a container from that image, the repo will be reset to its build-time state. Worse, restarting the container will do the same thing, since the base image layers are immutable. This is almost certainly not what you want. The correct ways put the repo into the _container_ created from the _image_, not in the image itself. ### <a id="repo-inside"></a> 2.1 Storing the Repo Inside the Container The simplest method is to stop the container if it was running, then say: $ docker cp /path/to/my-project.fossil fossil:/museum/repo.fossil $ docker start fossil $ docker exec fossil chown -R 499 /museum That copies the local Fossil repo into the container where the server expects to find it, so that the “start†command causes it to serve from that copied-in file instead. Since it lives atop the immutable base layers, it persists as part of the container proper, surviving restarts. Notice that the copy command changes the name of the repository database. The container configuration expects it to be called `repo.fossil`, which it almost certainly was not out on the host system. This is because there is only one repository inside this container, so we don’t have to name it after the project it contains, as is traditional. A generic name lets us hard-code the server start command. If you skip the “chown†command above and put “`http://localhost:9999/`†into your browser, expecting to see the copied-in repo’s home page, you will get an opaque “Not Found†error. This is because the user and group ID of the file will be that of your local user on the container’s host machine, which is unlikely to map to anything in the container’s `/etc/passwd` and `/etc/group` files, effectively preventing the server from reading the copied-in repository file. 499 is the default “`fossil`†user ID inside the container, causing Fossil to run with that user’s privileges after it enters the chroot. (See [below](#args) for how to change this default.) You don’t have to restart the server after fixing this with `chmod`: simply reload the browser, and Fossil will try again. ### 2.2 <a id="bind-mount"></a>Storing the Repo Outside the Container The simple storage method above has a problem: containers are designed to be killed off at the slightest cause, rebuilt, and redeployed. If you do that with the repo inside the container, it gets destroyed, too. The solution is to replace the “run†command above with the following: $ docker run \ --publish 9999:8080 \ --name fossil-bind-mount \ --volume ~/museum:/museum \ fossil Because this bind mount maps a host-side directory (`~/museum`) into the container, you don’t need to `docker cp` the repo into the container at all. It still expects to find the repository as `repo.fossil` under that directory, but now both the host and the container can see that repo DB. Instead of a bind mount, you could instead set up a separate [volume](https://docs.docker.com/storage/volumes/), at which point you _would_ need to `docker cp` the repo file into the container. Either way, files in these mounted directories have a lifetime independent of the container(s) they’re mounted into. When you need to rebuild the container or its underlying image — such as to upgrade to a newer version of Fossil — the external directory remains behind and gets remapped into the new container when you recreate it with `--volume/-v`. #### 2.2.1 <a id="wal-mode"></a>WAL Mode Interactions You might be aware that OCI containers allow mapping a single file into the repository rather than a whole directory. Since Fossil repositories are specially-formatted SQLite databases, you might be wondering why we don’t say things like: --volume ~/museum/my-project.fossil:/museum/repo.fossil That lets us have a convenient file name for the project outside the container while letting the configuration inside the container refer to the generic “`/museum/repo.fossil`†name. Why should we have to name the repo generically on the outside merely to placate the container? The reason is, you might be serving that repo with [WAL mode][wal] enabled. If you map the repo DB alone into the container, the Fossil instance inside the container will write the `-journal` and `-wal` files alongside the mapped-in repository inside the container. That’s fine as far as it goes, but if you then try using the same repo DB from outside the container while there’s an active WAL, the Fossil instance outside won’t know about it. It will think it needs to write *its own* `-journal` and `-wal` files *outside* the container, creating a high risk of [database corruption][dbcorr]. If we map a whole directory, both sides see the same set of WAL files. [Testing](https://tangentsoft.com/sqlite/dir/walbanger?ci=trunk) gives us a reasonable level of confidence that using WAL across a container boundary is safe when used in this manner. [dbcorr]: https://www.sqlite.org/howtocorrupt.html#_deleting_a_hot_journal [wal]: https://www.sqlite.org/wal.html ## 3. <a id="security"></a>Security ### 3.1 <a id="chroot"></a>Why Not Chroot? Prior to 2023.03.26, the stock Fossil container relied on [the chroot jail feature](./chroot.md) to wall away the shell and other tools provided by [BusyBox]. It included that as a bare-bones operating system inside the container on the off chance that someone might need it for debugging, but the thing is, Fossil is self-contained, needing none of that power in the main-line use cases. Our weak “you might need it†justification collapsed when we realized you could restore this basic shell environment with a one-line change to the `Dockerfile`, as shown [below](#run). [BusyBox]: https://www.busybox.net/BusyBox.html ### 3.2 <a id="caps"></a>Dropping Unnecessary Capabilities The example commands above create the container with [a default set of Linux kernel capabilities][defcap]. Although Docker strips away almost all of the traditional root capabilities by default, and Fossil doesn’t need any of those it does take away, Docker does leave some enabled that Fossil doesn’t actually need. You can tighten the scope of capabilities by adding “`--cap-drop`†options to your container creation commands. Specifically: * **`AUDIT_WRITE`**: Fossil doesn’t write to the kernel’s auditing log, and we can’t see any reason you’d want to be able to do that as an administrator shelled into the container, either. Auditing is something done on the host, not from inside each individual container. * **`CHOWN`**: The Fossil server never even calls `chown(2)`, and our image build process sets up all file ownership properly, to the extent that this is possible under the limitations of our automation. Curiously, stripping this capability doesn’t affect your ability to run commands like “`chown -R fossil:fossil /museum`†when you’re using bind mounts or external volumes — as we recommend [above](#bind-mount) — because it’s the host OS’s kernel capabilities that affect the underlying `chown(2)` call in that case, not those of the container. If for some reason you did have to change file ownership of in-container files, it’s best to do that by changing the `Dockerfile` to suit, then rebuilding the container, since that bakes the need for the change into your reproducible build process. If you had to do it without rebuilding the container, [there’s a workaround][capchg] for the fact that capabilities are a create-time change, baked semi-indelibly into the container configuration. * **`FSETID`**: Fossil doesn’t use the SUID and SGID bits itself, and our build process doesn’t set those flags on any of the files. Although the second fact means we can’t see any harm from leaving this enabled, we also can’t see any good reason to allow it, so we strip it. * **`KILL`**: The only place Fossil calls `kill(2)` is in the [backoffice], and then only for processes it created on earlier runs; it doesn’t need the ability to kill processes created by other users. You might wish for this ability as an administrator shelled into the container, but you can pass the “`docker exec --user`†option to run commands within your container as the legitimate owner of the process, removing the need for this capability. * **`MKNOD`**: As of 2023.03.26, the stock container uses the runtime’s default `/dev` node tree. Prior to this, we had to create `/dev/null` and `/dev/urandom` inside [the chroot jail](#chroot), but even then, these device nodes were created at build time and were never changed at run time, so we didn’t need this run-time capability even then. * **`NET_BIND_SERVICE`**: With containerized deployment, Fossil never needs the ability to bind the server to low-numbered TCP ports, not even if you’re running the server in production with TLS enabled and want the service bound to port 443. It’s perfectly fine to let the Fossil instance inside the container bind to its default port (8080) because you can rebind it on the host with the “`docker create --publish 443:8080`†option. It’s the container’s _host_ that needs this ability, not the container itself. (Even the container runtime might not need that capability if you’re [terminating TLS with a front-end proxy](./ssl.wiki#server). You’re more likely to say something like “`-p localhost:12345:8080`†and then configure the reverse proxy to translate external HTTPS calls into HTTP directed at this internal port 12345.) * **`NET_RAW`**: Fossil itself doesn’t use raw sockets, and while you could [swap out the run layer](#run) for something more functional that *does* make use of raw sockets, there’s little call for it. The best reason I can come up with is to be able to run utilities like `ping` and `traceroute`, but since we aren’t doing anything clever with the networking configuration, there’s no particularly compelling reason to run these from inside the container. If you need to ping something, do it on the host. If we did not take this hard-line stance, an attacker that broke into the container and gained root privileges might use raw sockets to do a wide array of bad things to any network the container is bound to. * **`SETFCAP, SETPCAP`**: There isn’t much call for file permission granularity beyond the classic Unix ones inside the container, so we drop root’s ability to change them. All together, we recommend adding the following options to your “`docker run`†commands, as well as to any “`docker create`†command that will be followed by “`docker start`â€: --cap-drop AUDIT_WRITE \ --cap-drop CHOWN \ --cap-drop FSETID \ --cap-drop KILL \ --cap-drop MKNOD \ --cap-drop NET_BIND_SERVICE \ --cap-drop NET_RAW \ --cap-drop SETFCAP \ --cap-drop SETPCAP In the next section, we’ll show a case where you create a container without ever running it, making these options pointless. [backoffice]: ./backoffice.md [defcap]: https://docs.docker.com/engine/security/#linux-kernel-capabilities [capchg]: https://stackoverflow.com/a/45752205/142454 ## 4. <a id="static"></a>Extracting a Static Binary Our 2-stage build process uses Alpine Linux only as a build host. Once we’ve got everything reduced to a single static Fossil binary, we throw all the rest of it away. A secondary benefit falls out of this process for free: it’s arguably the easiest way to build a purely static Fossil binary for Linux. Most modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s back-to-basics nature makes static builds work the way they used to, back in the day. If that’s all you’re after, you can do so as easily as this: $ docker build -t fossil . $ docker create --name fossil-static-tmp fossil $ docker cp fossil-static-tmp:/bin/fossil . $ docker container rm fossil-static-tmp The result is six or seven megs, depending on the CPU architecture you build for. It’s built stripped. [lsl]: https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead ## 5. <a id="custom" name="args"></a>Customization Points ### <a id="pkg-vers"></a> 5.1 Fossil Version The default version of Fossil fetched in the build is the version in the checkout directory at the time you run it. You could override it to get a release build like so: $ docker build -t fossil --build-arg FSLVER=version-2.20 . Or equivalently, using Fossil’s `Makefile` convenience target: $ make container-image DBFLAGS='--build-arg FSLVER=version-2.20' While you could instead use the generic “`release`†tag here, it’s better to use a specific version number since container builders cache downloaded files, hoping to reuse them across builds. If you ask for “`release`†before a new version is tagged and then immediately after, you might expect to get two different tarballs, but because the underlying source tarball URL remains the same when you do that, you’ll end up reusing the old tarball from cache. This will occur even if you pass the “`docker build --no-cache`†option. This is why we default to pulling the Fossil tarball by checkin ID rather than let it default to the generic “`trunk`†tag: so the URL will change each time you update your Fossil source tree, forcing the builder to pull a fresh tarball. ### 5.2 <a id="uids"></a>User & Group IDs The “`fossil`†user and group IDs inside the container default to 499. Why? Regular user IDs start at 500 or 1000 on most Unix type systems, leaving those below it for system users like this Fossil daemon owner. Since it’s typical for these to start at 0 and go upward, we started at 500 and went *down* one instead to reduce the chance of a conflict to as close to zero as we can manage. To change it to something else, say: $ make container-image DBFLAGS='--build-arg UID=501' This is particularly useful if you’re putting your repository on a separate volume since the IDs “leak†out into the host environment via file permissions. You may therefore wish them to mean something on both sides of the container barrier rather than have “499†appear on the host in “`ls -l`†output. ### 5.3 <a id="cengine"></a>Container Engine Although the Fossil container build system defaults to Docker, we allow for use of any OCI container system that implements the same interfaces. We go into more details about this [below](#light), but for now, it suffices to point out that you can switch to Podman while using our `Makefile` convenience targets unchanged by saying: $ make CENGINE=podman container-run ### 5.4 <a id="config"></a>Fossil Configuration Options You can use this same mechanism to enable non-default Fossil configuration options in your build. For instance, to turn on the JSON API and the TH1 docs extension: $ make container-image \ DBFLAGS='--build-arg FSLCFG="--json --with-th1-docs"' If you also wanted [the Tcl evaluation extension](./th1.md#tclEval), that brings us to [the next point](#run). ### 5.5 <a id="run"></a>Elaborating the Run Layer If you want a basic shell environment for temporary debugging of the running container, that’s easily added. Simply change this line in the `Dockerfile`… FROM scratch AS run …to this: FROM busybox AS run Rebuild and redeploy to give your Fossil container a [BusyBox]-based shell environment that you can get into via: $ docker exec -it -u fossil $(make container-version) sh That command assumes you built it via “`make container`†and are therefore using its versioning scheme. You will likely want to remove the `PATH` override in the “RUN†stage when doing this since it’s written for the case where everything is in `/bin`, and that will no longer be the case with a more full-featured “`run`†layer. As long as the parent layer’s `PATH` value contains `/bin`, delegating to it is more likely the correct thing. Another useful case to consider is that you’ve installed a [server extension](./serverext.wiki) and you need an interpreter for that script. The first option above won’t work except in the unlikely case that it’s written for one of the bare-bones script interpreters that BusyBox ships.(^[BusyBox]’s `/bin/sh` is based on the old 4.4BSD Lite Almquist shell, implementing little more than what POSIX specified in 1989, plus equally stripped-down versions of `awk` and `sed`.) Let’s say the extension is written in Python. Because this is one of the most popular programming languages in the world, we have many options for achieving this. For instance, there is a whole class of “[distroless]†images that will do this efficiently by changing “`STAGE 2`†in the `Dockefile` to this: ## --------------------------------------------------------------------- ## STAGE 2: Pare that back to the bare essentials, plus Python. ## --------------------------------------------------------------------- FROM cgr.dev/chainguard/python:latest USER root ARG UID=499 ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" COPY --from=builder /tmp/fossil /bin/ COPY --from=builder /bin/busybox.static /bin/busybox RUN [ "/bin/busybox", "--install", "/bin" ] RUN set -x \ && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ && echo "fossil:x:${UID}:fossil" >> /etc/group \ && install -d -m 700 -o fossil -g fossil log museum You will also have to add `busybox-static` to the APK package list in STAGE 1 for the `RUN` script at the end of that stage to work, since the [Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to install root-level binaries is why we change `USER` temporarily here. Build it and test that it works like so: $ make container-run && docker exec -i $(make container-version) python --version 3.11.2 The compensation for the hassle of using Chainguard over something more general purpose like changing the `run` layer to Alpine and then adding a “`apk add python`†command to the `Dockerfile` is huge: we no longer leave a package manager sitting around inside the container, waiting for some malefactor to figure out how to abuse it. Beware that there’s a limit to this über-jail’s ability to save you when you go and provide a more capable runtime layer like this. The container layer should stop an attacker from accessing any files out on the host that you haven’t explicitly mounted into the container’s namespace, but it can’t stop them from making outbound network connections or modifying the repo DB inside the container. [cgimgs]: https://github.com/chainguard-images/images/tree/main/images [distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent ### 5.6 <a id="alerts"></a>Email Alerts The nature of our single static binary container precludes two of the options for [sending email alerts](./alerts.md) from Fossil: * pipe to a command * SMTP relay host There is no `/usr/sbin/sendmail` inside the container, and the container cannot connect out to a TCP service on the host by default. While it is possible to get around the first lack by [elaborating the run layer](#run), to inject a full-blown Sendmail setup into the container would go against the whole idea of containerization. Forwarding an SMTP relay port into the container isn’t nearly as bad, but it’s still bending the intent behind containers out of shape. A far better option in this case is the “store emails in database†method since the containerized Fossil binary knows perfectly well how to write SQLite DB files without relying on any external code. Using the paths in the configuration recommended above, the database path should be set to something like `/museum/mail.db`. This, along with the use of [bind mounts](#bind-mount) means you can have a process running outside the container that passes the emails along to the host-side MTA. The included [`email-sender.tcl`](/file/tools/email-sender.tcl) script works reasonably well for this, though in my own usage, I had to make two changes to it: 1. The shebang line at the top has to be `#!/usr/bin/tclsh` on my server. 2. I parameterized the `DBFILE` variable at the top thus: set DBFILE [lindex $argv 0] I then wanted a way to start this Tcl script on startup and keep it running, which made me reach for systemd. My server is set to allow user services to run at boot(^â€Desktop†class Linuxes tend to disable that by default under the theory that you don’t want those services to run until you’ve logged into the GUI as that user. If you find yourself running into this, [enable linger mode](https://www.freedesktop.org/software/systemd/man/loginctl.html).) so I was able to create a unit file called `~/.local/share/systemd/user/alert-sender@.service` with these contents: [Unit] Description=Fossil email alert sender for %I [Service] WorkingDirectory=/home/fossil/museum ExecStart=/home/fossil/bin/alert-sender %I/mail.db Restart=always RestartSec=3 [Install] WantedBy=default.target I was then able to enable email alert forwarding for select repositories after configuring them per [the docs](./alerts.md) by saying: $ systemctl --user daemon-reload $ systemctl --user enable alert-sender@myproject $ systemctl --user start alert-sender@myproject Because this is a parameterized script and we’ve set our repository paths predictably, you can do this for as many repositories as you need to by passing their names after the “`@`†sign in the commands above. ## 6. <a id="light"></a>Lightweight Alternatives to Docker Those afflicted with sticker shock at seeing the size of a [Docker Desktop][DD] installation — 1.65 GB here — might’ve immediately “noped†out of the whole concept of containers. The first thing to realize is that when it comes to actually serving simple containers like the ones shown above is that [Docker Engine][DE] suffices, at about a quarter of the size. Yet on a small server — say, a $4/month ten gig Digital Ocean droplet — that’s still a big chunk of your storage budget. It takes ~60:1 overhead merely to run a Fossil server container? Once again, I wouldn’t blame you if you noped right on out of here, but if you will be patient, you will find that there are ways to run Fossil inside a container even on entry-level cloud VPSes. These are well-suited to running Fossil; you don’t have to resort to [raw Fossil service][srv] to succeed, leaving the benefits of containerization to those with bigger budgets. For the sake of simple examples in this section, we’ll assume you’re integrating Fossil into a larger web site, such as with our [Debian + nginx + TLS][DNT] plan. This is why all of the examples below create the container with this option: --publish 127.0.0.1:9999:8080 The assumption is that there’s a reverse proxy running somewhere that redirects public web hits to localhost port 9999, which in turn goes to port 8080 inside the container. This use of port publishing effectively replaces the use of the “`fossil server --localhost`†option. For the nginx case, you need to add `--scgi` to these commands, and you might also need to specify `--baseurl`. Containers are a fine addition to such a scheme as they isolate the Fossil sections of the site from the rest of the back-end resources, thus greatly reducing the chance that they’ll ever be used to break into the host as a whole. (If you wanted to be double-safe, you could put the web server into another container, restricting it to reading from the static web site directory and connecting across localhost to back-end dynamic content servers such as Fossil. That’s way outside the scope of this document, but you can find ready advice for that elsewhere. Seeing how we do this with Fossil should help you bridge the gap in extending this idea to the rest of your site.) [DD]: https://www.docker.com/products/docker-desktop/ [DE]: https://docs.docker.com/engine/ [DNT]: ./server/debian/nginx.md [srv]: ./server/ ### 6.1 <a id="nerdctl" name="containerd"></a>Stripping Docker Engine Down The core of Docker Engine is its [`containerd`][ctrd] daemon and the [`runc`][runc] container runtime. Add to this the out-of-core CLI program [`nerdctl`][nerdctl] and you have enough of the engine to run Fossil containers. The big things you’re missing are: * **BuildKit**: The container build engine, which doesn’t matter if you’re building elsewhere and shipping the images to the target. A good example is using a container registry as an intermediary between the build and deployment hosts. * **SwarmKit**: A powerful yet simple orchestrator for Docker that you probably aren’t using with Fossil anyway. In exchange, you get a runtime that’s about half the size of Docker Engine. The commands are essentially the same as above, but you say “`nerdctl`†instead of “`docker`â€. You might alias one to the other, because you’re still going to be using Docker to build and ship your container images. [ctrd]: https://containerd.io/ [nerdctl]: https://github.com/containerd/nerdctl [runc]: https://github.com/opencontainers/runc ### 6.2 <a id="podman"></a>Podman A lighter-weight [rootless][rl] [drop-in replacement][whatis] that doesn’t give up the image builder is [Podman]. Initially created by Red Hat and thus popular on that family of OSes, it will run on any flavor of Linux. It can even be made to run [on macOS via Homebrew][pmmac] or [on Windows via WSL2][pmwin]. On Ubuntu 22.04, the installation size is about 38 MiB, roughly a tenth the size of Docker Engine. For our purposes here, the only thing that changes relative to the examples at the top of this document are the initial command: $ podman build -t fossil . $ podman run --name fossil -p 9999:8080/tcp fossil Your Linux package repo may have a `podman-docker` package which provides a “`docker`†script that calls “`podman`†for you, eliminating even the command name difference. With that installed, the `make` commands above will work with Podman as-is. The only difference that matters here is that Podman doesn’t have the same [default Linux kernel capability set](#caps) as Docker, which affects the `--cap-drop` flags recommended above to: $ podman create \ --name fossil \ --cap-drop CHOWN \ --cap-drop FSETID \ --cap-drop KILL \ --cap-drop NET_BIND_SERVICE \ --cap-drop SETFCAP \ --cap-drop SETPCAP \ --publish 127.0.0.1:9999:8080 \ localhost/fossil $ podman start fossil [pmmac]: https://podman.io/getting-started/installation.html#macos [pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md [Podman]: https://podman.io/ [rl]: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md [whatis]: https://podman.io/whatis.html ### 6.3 <a id="nspawn"></a>`systemd-container` If even the Podman stack is too big for you, the next-best option I’m aware of is the `systemd-container` infrastructure on modern Linuxes, available since version 239 or so. Its runtime tooling requires only about 1.4 MiB of disk space: $ sudo apt install systemd-container btrfs-tools That command assumes the primary test environment for this guide, Ubuntu 22.04 LTS with `systemd` 249. For best results, `/var/lib/machines` should be a btrfs volume, because [`$REASONS`][mcfad]. For CentOS Stream 9 and other Red Hattish systems, you will have to make several adjustments, which we’ve collected [below](#nspawn-centos) to keep these examples clear. We’ll assume your Fossil repository stores something called “`myproject`†within `~/museum/myproject/repo.fossil`, named according to the reasons given [above](#repo-inside). We’ll make consistent use of this naming scheme in the examples below so that you will be able to replace the “`myproject`†element of the various file and path names. If you use [the stock `Dockerfile`][DF] to generate your base image, `nspawn` won’t recognize it as containing an OS unless you change the “`FROM scratch AS os`†line at the top of the second stage to something like this: FROM gcr.io/distroless/static-debian11 AS os Using that as a base image provides all the files `nspawn` checks for to determine whether the container is sufficiently close to a Linux VM for the following step to proceed: $ make container $ docker container export $(make container-version) | machinectl import-tar - myproject Next, create `/etc/systemd/nspawn/myproject.nspawn`: ---- [Exec] WorkingDirectory=/ Parameters=bin/fossil server \ --baseurl https://example.com/myproject \ --create \ --jsmode bundled \ --localhost \ --port 9000 \ --scgi \ --user admin \ museum/repo.fossil DropCapability= \ CAP_AUDIT_WRITE \ CAP_CHOWN \ CAP_FSETID \ CAP_KILL \ CAP_MKNOD \ CAP_NET_BIND_SERVICE \ CAP_NET_RAW \ CAP_SETFCAP \ CAP_SETPCAP ProcessTwo=yes LinkJournal=no Timezone=no [Files] Bind=/home/fossil/museum/myproject:/museum [Network] VirtualEthernet=no ---- If you recognize most of that from the `Dockerfile` discussion above, congratulations, you’ve been paying attention. The rest should also be clear from context. Some of this is expected to vary: * The references to `example.com` and `myproject` are stand-ins for your actual web site and repository name. * The command given in the `Parameters` directive assumes you’re setting up [SCGI proxying via nginx][DNT], but with adjustment, it’ll work with the other repository service methods we’ve [documented][srv]. * The path in the host-side part of the `Bind` value must point at the directory containing the `repo.fossil` file referenced in said command so that `/museum/repo.fossil` refers to your repo out on the host for the reasons given [above](#bind-mount). That being done, we also need a generic `systemd` unit file called `/etc/systemd/system/fossil@.service`, containing: ---- [Unit] Description=Fossil %i Repo Service Wants=modprobe@tun.service modprobe@loop.service After=network.target systemd-resolved.service modprobe@tun.service modprobe@loop.service [Service] ExecStart=systemd-nspawn --settings=override --read-only --machine=%i bin/fossil [Install] WantedBy=multi-user.target ---- You shouldn’t have to change any of this because we’ve given the `--setting=override` flag, meaning any setting in the nspawn file overrides the setting passed to `systemd-nspawn`. This arrangement not only keeps the unit file simple, it allows multiple services to share the base configuration, varying on a per-repo level through adjustments to their individual `*.nspawn` files. You may then start the service in the normal way: $ sudo systemctl enable fossil@myproject $ sudo systemctl start fossil@myproject You should then find it running on localhost port 9000 per the nspawn configuration file above, suitable for proxying Fossil out to the public using nginx via SCGI. If you aren’t using a front-end proxy and want Fossil exposed to the world via HTTPS, you might say this instead in the `*.nspawn` file: Parameters=bin/fossil server \ --cert /path/to/cert.pem \ --create \ --jsmode bundled \ --port 443 \ --user admin \ museum/repo.fossil You would also need to un-drop the `CAP_NET_BIND_SERVICE` capability to allow Fossil to bind to this low-numbered port. We use the `systemd` template file feature to allow multiple Fossil servers running on a single machine, each on a different TCP port, as when proxying them out as subdirectories of a larger site. To add another project, you must first clone the base “machine†layer: $ sudo machinectl clone myproject otherthing That will not only create a clone of `/var/lib/machines/myproject` as `../otherthing`, it will create a matching `otherthing.nspawn` file for you as a copy of the first one. Adjust its contents to suit, then enable and start it as above. [mcfad]: https://www.freedesktop.org/software/systemd/man/machinectl.html#Files%20and%20Directories ### 6.3.1 <a id="nspawn-rhel"></a>Getting It Working on a RHEL Clone The biggest difference between doing this on OSes like CentOS versus Ubuntu is that RHEL (thus also its clones) doesn’t ship btrfs in its kernel, thus ships with no package repositories containing `mkfs.btrfs`, which [`machinectl`][mctl] depends on for achieving its various purposes. Fortunately, there are workarounds. First, the `apt install` command above becomes: $ sudo dnf install systemd-container Second, you have to hack around the lack of `machinectl import-tar`: $ rootfs=/var/lib/machines/fossil $ sudo mkdir -p $rootfs $ docker container export fossil | sudo tar -xf -C $rootfs - The parent directory path in the `rootfs` variable is important, because although we aren’t able to use `machinectl` on such systems, the `systemd-nspawn` developers assume you’re using them together; when you give `--machine`, it assumes the `machinectl` directory scheme. You could instead use `--directory`, allowing you to store the rootfs wherever you like, but why make things difficult? It’s a perfectly sensible default, consistent with the [LHS] rules. The final element — the machine name — can be anything you like so long as it matches the nspawn file’s base name. Finally, since you can’t use `machinectl clone`, you have to make a wasteful copy of `/var/lib/machines/myproject` when standing up multiple Fossil repo services on a single machine. (This is one of the reasons `machinectl` depends on `btrfs`: cheap copy-on-write subvolumes.) Because we give the `--read-only` flag, you can simply `cp -r` one machine to a new name rather than go through the export-and-import dance you used to create the first one. [LHS]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html [mctl]: https://www.freedesktop.org/software/systemd/man/machinectl.html ### 6.3.2 <a id="nspawn-weaknesses"></a>What Am I Missing Out On? For all the runtime size savings in this method, you may be wondering what you’re missing out on relative to Podman, which takes up roughly 27× more disk space. Short answer: lots. Long answer: 1. **Build system.** You’ll have to build and test your containers some other way. This method is only suitable for running them once they’re built. 2. **Orchestration.** All of the higher-level things like “compose†files, Docker Swarm mode, and Kubernetes are unavailable to you at this level. You can run multiple instances of Fossil, but on a single machine only and with a static configuration. 3. **Image layer sharing.** When you update an image using one of the above methods, Docker and Podman are smart enough to copy only changed layers. Furthermore, when you base multiple containers on a single image, they don’t make copies of the base layers; they can share them, because base layers are immutable, thus cannot cross-contaminate. Because we use `systemd-nspawn --read-only`, we get *some* of this benefit, particularly when using `machinectl` with `/var/lib/machines` as a btrfs volume. Even so, the disk space and network I/O optimizations go deeper in the Docker and Podman worlds. 4. **Tooling.** Hand-creating and modifying those `systemd` files sucks compared to “`podman container create ...`†This is but one of many affordances you will find in the runtimes aimed at daily-use devops warriors. 5. **Network virtualization.** In the scheme above, we turn off the `systemd` private networking support because in its default mode, it wants to hide containerized services entirely. While there are [ways][ndcmp] to expose Fossil’s single network service port under that scheme, it adds a lot of administration complexity. In the big-boy container runtimes, `docker create --publish` fixes all this up in a single option, whereas `systemd-nspawn --port` does approximately *none* of that despite the command’s superficial similarity. From a purely functional point of view, this isn’t a huge problem if you consider the inbound service direction only, being external connections to the Fossil service we’re providing. Since we do want this Fossil service to be exposed — else why are we running it? — we get all the control we need via `fossil server --localhost` and similar options. The complexity of the `systemd` networking infrastructure’s interactions with containers make more sense when you consider the outbound path. Consider what happens if you enable Fossil’s optional TH1 docs feature plus its Tcl evaluation feature. That would enable anyone with the rights to commit to your repository the ability to make arbitrary network connections on the Fossil host. Then, let us say you have a client-server DBMS server on that same host, bound to localhost for private use by other services on the machine. Now that DBMS is open to access by a rogue Fossil committer because the host’s loopback interface is mapped directly into the container’s network namespace. Proper network virtualization would protect you in this instance. This author expects that the set of considerations is broader than presented here, but that it suffices to make our case as it is: if you can afford the space of Podman or Docker, we strongly recommend using either of them over the much lower-level `systemd-container` infrastructure. You’re getting a considerable amount of value for the higher runtime cost; it isn’t pointless overhead. (Incidentally, these are essentially the same reasons why we no longer talk about the `crun` tool underpinning Podman in this document. It’s even more limited than `nspawn`, making it even more difficult to administer while providing no runtime size advantage. The `runc` tool underpinning Docker is even worse on this score, being scarcely easier to use than `crun` while having a much larger footprint.) [ndcmp]: https://wiki.archlinux.org/title/systemd-networkd#Usage_with_containers ### 6.3.3 <a id="nspawn-assumptions"></a>Violated Assumptions The `systemd-container` infrastructure has a bunch of hard-coded assumptions baked into it. We papered over these problems above, but if you’re using these tools for other purposes on the machine you’re serving Fossil from, you may need to know which assumptions our container violates and the resulting consequences. Some of it we discussed above already, but there’s one big class of problems we haven’t covered yet. It stems from the fact that our stock container starts a single static executable inside a bare-bones container rather than “boot†an OS image. That causes a bunch of commands to fail: * **`machinectl poweroff`** will fail because the container isn’t running dbus. * **`machinectl start`** will try to find an `/sbin/init` program in the rootfs, which we haven’t got. We could rename `/bin/fossil` to `/sbin/init` and then hack the chroot scheme to match, but ick. (This, incidentally, is why we set `ProcessTwo=yes` above even though Fossil is perfectly capable of running as PID 1, a fact we depend on in the other methods above.) * **`machinectl shell`** will fail because there is no login daemon running, which we purposefully avoided adding by creating a “`FROM scratch`†container. (If you need a shell, say: `sudo systemd-nspawn --machine=myproject /bin/sh`) * **`machinectl status`** won’t give you the container logs because we disabled the shared journal, which was in turn necessary because we don’t run `systemd` *inside* the container, just outside. If these are problems for you, you may wish to build a fatter container using `debootstrap` or similar. ([External tutorial][medtut].) [medtut]: https://medium.com/@huljar/setting-up-containers-with-systemd-nspawn-b719cff0fb8d <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
Added www/contribute.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 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>Contributing To Fossil</title> Fossil users are encouraged to contributed enhancements back to the project. This note outlines some of the procedures for making useful contributions. <h2>1.0 Contributor Agreement</h2> In order to accept non-trivial contributions, we <u>must</u> have a [./copyright-release.pdf | Contributor Agreement (PDF)] (or [./copyright-release.html | as HTML]) on file for you. We require this in order to maintain clear title to the Fossil code and prevent the introduction of code with incompatible licenses or other entanglements that might cause legal problems for Fossil users. Many 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. We've made exceptions for "trivial" changes in the past, but the definition of that term is up to the project leader. <h2>2.0 Submitting Patches</h2> Suggested changes or bug fixes can be submitted by creating a patch against the current source tree: <pre>fossil diff -i > my-change.patch</pre> Alternatively, you can create a binary patch: <pre>fossil patch create my-change.db</pre> Post patches to [https://fossil-scm.org/forum | the forum] or email them to <a href="mailto:drh@sqlite.org">drh@sqlite.org</a>. Be sure to describe in detail what the patch does and which version of Fossil it is written against. It's best to make patches against tip-of-trunk rather than against past releases. If your change is more complicated than a patch can properly encode, you may submit [/help?cmd=bundle | a Fossil bundle] instead. Unlike patches, bundles can contain multiple commits, check-in comments, file renames, file deletions, branching decisions, and more which <tt>patch(1)</tt> files cannot. It's best to make a bundle of a new branch so the change can be integrated, tested, enhanced, and merged down to trunk in a controlled fashion. A contributor agreement is not strictly necessary to submit a patch or bundle, but without a contributor agreement on file, your contribution will be used for reference only: it will not be applied to the code. This may delay acceptance of your contribution. Your contribution might not be accepted even if you do have a contributor agreement on file. Please do not take this personally or as an affront to your coding ability. Sometimes contributions are rejected because they seem to be taking the project in a direction that the architect does not want to go. In other cases, there might be an alternative implementation of the same feature being prepared separately. <h2>3.0 Check-in Privileges</h2> Check-in privileges are granted on a case-by-case basis. Your chances of getting check-in privileges are much improved if you have a history of submitting quality patches and/or making thoughtful posts on [https://fossil-scm.org/forum | the forum]. A signed 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 follow 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 note card beside their workstations for quick reference. Contributors should review the [./style.wiki | Coding Style Guidelines] and mimic the coding style used through the rest of the Fossil source code. Your code should blend in. A third-party reader should be unable to distinguish your code from any other code in the source corpus. <h2>4.0 Testing</h2> Fossil's [../test/release-checklist.wiki | release checklist] is of primary benefit to the project leader, followed by him at release time, but contributors are encouraged to run through its steps when making major changes, since if the change doesn't pass this checklist, it won't be included in the next release. <h2>5.0 UI and Documentation Language</h2> The Fossil project uses American English in its web interface and documentation. Until there is some provision for translating the UI and docs into other languages and dialects, we ask that you do not commit changes that conflict with this. We aren't opposed to such a project, but it would be a huge amount of work, which no one's stepped up to do yet. Not only is each individual translation a large ongoing job its own right, there is no infrastructure for it yet, so the first few translations will be harder than any future translation built on that infrastructure. More immediately, we're likely to reject, revert, or rework commits that use other English dialects. One example that comes up occasionally is "artefact" versus "artifact." The UI and docs use the American English spelling pervasively, so you have poor options if you insist on "artefact:" * attempt to slip one-off changes by your peers * attempt to change all American English usages to Commonwealth English * make the Fossil UI and docs translatable, then contribute a Commonwealth English translation Only the latter is likely to succeed. <h2>6.0 See Also</h2> * [./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] |
Changes to www/copyright-release.html.
1 | <h1 align="center"> | < | > > | < | < | < | > | > | < > > > > > > | | < < > > | > | > > | > > > > > > > > > | > | > > > > | > | | > > > > > > | < < > > | > > > | > > | > | | > > > | > | | > | | > | > | | < | | < < | > | | > > | > > | < | 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 | <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 and signing at the bottom of this document. If your contribution is on behalf of a company, the term "you" also means the company identified in the signature area below. <ol> <li><p> With respect to any worldwide copyrights, or copyright applications and registrations, in your contribution: <ul> <li> You hereby assign to Hwaci joint ownership, and to the extent that such assignment is or becomes invalid, ineffective or unenforceable, you hereby grant to Hwaci a perpetual, irrevocable, non-exclusive, worldwide, no-charge, royalty-free, unrestricted license to exercise all rights under those copyrights, including the right to sublicense. <li> You agree that both you and Hwaci can do all things in relation to your contribution as if each of us were the sole owners, and if one of us makes a derivative work of your contribution, the one who makes (or has made) the derivative work will be the sole owner of that derivative work. <li> You agree that you will not assert any moral rights in your contribution against Hwaci, Hwaci's licensees or transferees, or any other user or consumer of your contribution. <li> You agree that Hwaci may register a copyright in your contribution and exercise all ownership rights associated with it. <li> You agree that neither you nor Hwaci has any duty to consult with, obtain the consent of, or pay or render an accounting to the other for any use or distribution of your contribution. </ul> <li><p> With respect to any patents you own, or that you can license without payment to any third party, and which are relevant to your contribution, you hereby grant to Hwaci a perpetual, irrevocable, non-exclusive, worldwide, no-charge, royalty-free license to make, have made, use, sell, offer to sell, import, and otherwise transfer your contribution in whole or in part, alone or in combination with or included in any product, work or materials arising out of the Fossil project, and to sublicense these same rights. <li><p> Except as set out above, you keep all right, title, and interest in your contribution. The rights that you grant to Hwaci under this agreement are effective on the date that you first submitted your contribution to the Fossil project, even if your submission took place before the date that you sign this agreement. <li><p> You represent and warrant the following: <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> <p><table width="80%" border="1" cellpadding="0" cellspacing="0" align="center"> <tr><td width="20%" valign="top">Your name & email:</td><td width="80%"> <!-- Replace this line with your name and email --> <p> </td></tr> <tr><td valign="top">Company name:<br>(if applicable)</td><td> <!-- Replace this line with your company name --> <p> </td></tr> <tr><td valign="top">Postal address:</td><td> <!-- Replace this line and the next line with your postal address --> <p> </p><p> </p><p> </p> </td></tr> <tr><td valign="top">Signature:</td><td> <p> </td></tr> <tr><td valign="top">Date:</td><td> <p> </td></tr> </table> <p>Send completed forms to: <blockquote> Hipp, Wyrick & Company, Inc.<br> 6200 Maple Cove Lane<br> Charlotte, NC 28269-1086<br> USA </p> |
Added www/copyright-release.pdf.
cannot compute difference between binary files
Added www/css-tricks.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 | # Fossil CSS Tips and Tricks Many aspects of Fossil's appearance can be customized by [customizing the site skin](customskin.md). This document details certain specific CSS tweaks which users have asked about via the forums. This is a "living document" - please feel free to suggest additions via [the Fossil forum](https://fossil-scm.org/forum/). This document is *not* an introduction to CSS - the web is full of tutorials on that topic. It covers only the specifics of customizing certain CSS-based behaviors in a Fossil UI. That said... ## Is it Really `!important`? By and large, CSS's `!important` qualifier is not needed when customizing Fossil's CSS. On occasion, however, particular styles may be set directly on DOM elements when Fossil generates its HTML, and such cases require the use of `!important` to override them. <!-- ============================================================ --> # Main UI CSS ## Number of Columns in `/dir` View The width of columns on the [`/dir` page](/dir) is calculated dynamically as the page is generated, to attempt to fit the widest name in a given directory. The number of columns is determined automatically by CSS. To modify the number of columns and/or the entry width: ```css div.columns { columns: WIDTH COLUMN_COUNT !important; /* Examples: columns: 20ex 3 !important columns: auto auto !important */ } /* The default rule uses div.columns, but it can also be selected using: */ div.columns.files { ... } ``` The `!important` qualifier is required here because the style values are dynamically calculated and applied when the HTML is emitted. The file list itself can be further customized via: ```css div.columns > ul { ... } ul.browser { ... } ``` <!-- ============================================================ --> # Forum-specific CSS ## Limiting Display Length of Long Posts Excessively long posts can make scrolling through threads problematic, especially on mobile devices. The amount of a post which is visible can be configured using: ```css div.forumPostBody { max-height: 25em; /* change to the preferred maximum effective height */ overflow: auto; /* tells the browser to add scrollbars as needed */ } ``` |
Added www/css/diff.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 | Notes On Diff Formatting ======================== There are two main kinds of diff display for the web interface: unified and side-by-side. Both displays are implemented using a <table>. The unified diff is a 4-column table, and the side-by-side diff is a 5-column table. In a page like /info that might show multiple file diffs, each file diff is in a separate <table>. For side-by-side diffs, a small amount of Javascript code is used to resize the text columns so that they fill the screen width and to keep horizontal scrollbars in sync. For the unified diff, the basic structure is like this: > ~~~~ <table class='diff udiff'> <tr> <td class='diffln difflnl'><pre> Line numbers for the left-hand file </pre></td> <td class='diffln difflnr'><pre> Line numbers for the right-hand file </pre></td> <td class='diffsep'><pre> Change marks. "+" or "=" or nothing </pre></td> <td class='difftxt difftxtu'><pre> The text </pre></td> </tr> </table> ~~~~ The structure for a side-by-side diff follows the same basic pattern, though with 5 columns instead of 4, and slightly different class names: > ~~~~ <table class='diff splitdiff'> <tr> <td class='diffln difflnl'><pre> Line numbers for the left-hand file </pre></td> <td class='difftxt difftxtl'><pre> The text for the left side </pre></td> <td class='diffsep'><pre> Change marks. "+" or "=" or nothing </pre></td> <td class='diffln difflnr'><pre> Line numbers for the right-hand file </pre></td> <td class='difftxt difftxtr'><pre> The text on the right-hand side </pre></td> </tr> </table> ~~~~ The outer <table> always has class "diff". The "diffu" class is added for unified diffs and the "splitdiff" class is added for side-by-side diffs. All line-number columns have the "diffln" class. They also always have one of "difflnl" or "difflnr" depending on whether they hold line numbers for the left or right files, respectively. Text is always kept in a separate column so that it can be scraped and copied by the user. All text columns have the "difftxt" class. One additional class "difftxtu", "difftxtl", or "difftxtr" is added depending on if the text is for a unified diff, the left column of a side-by-side diff, or the right column of a side-by-side diff. The content of all columns is a single <pre> that contains the appropriate diff-text for that column. Scrolling is done on the <pre> element. Within text columns, highlighting is done with <del> and <ins> markup. All text on a line that contains an isert or delete is surrounded by <ins>...</ins> or <del>..</del>. Within that line, specific characters of text that specifically inserted deleted have an additional layer of <ins> or <del> markup. Thus CSS like the following is appropriate: > ~~~~ td.difftxt ins { background-color: #dafbe1; /* Light green for the whole line */ text-decoration: none; } td.difftxt ins > ins { background-color: #a0e4b2; /* Dark green for specific characters that change */ text-decoration: none; } ~~~~ In a side-by-side diff, if an interior <ins> or <del> that mark specific characters that change correspond to a delete/insert on the other side, they they have the "edit" class tag. (ex: <ins class='edit'> or <del class='edit'>). Some skins choose to paint these "modified" regions blue: > ~~~~ td.difftxt ins > ins.edit { background-color: #c0c0ff; /* Blue for "modified" text region */ text-decoration: none; } ~~~~ Line number text also has <ins> and <del> tags for lines which are pure insert or pure delete. But the tags do not nest for line numbers. |
Added www/css/index.md.
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | Cascading Style Sheet Notes =========================== This is a collection of technical notes that document the design of the Document Object Model (DOM) and corresponding Cascading Style Sheet (CSS) attributes used for customing the look-and-feel of Fossil. These notes are of interest to people who want to customize the Fossil skin or enhance the internal display logic. This is a collection of documents that we hope will grow over time. * [Diff styling](./diff.md) |
Changes to www/custom_ticket.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 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 | <title>Customizing The Ticket System</title> <h2>Introduction</h2> This guide will explain how to add the "assigned_to" and "submitted_by" fields to the ticket system in Fossil, as well as making the system more useful. You must have "admin" access to the repository to implement these instructions. <h2>First modify the TICKET table</h2> Click on the "Admin" menu, then "Tickets", then "Table". After the other fields and before the final ")", insert: <pre> , assigned_to TEXT, opened_by TEXT </pre> And "Apply Changes". You have just added two more fields to the ticket database! NOTE: I won't tell you to "Apply Changes" after each step from here on out. Now, how do you use these fields? <h2>Next add assignees</h2> Back to the "Tickets" admin page, and click "Common". Add something like this: <pre> set assigned_choices { unassigned tom dick harriet } </pre> Obviously, choose names corresponding to the logins on your system. The 'unassigned' entry is important, as it prevents you from having a NULL in that field (which causes problems later when editing). <h2>Now modify the 'new ticket' page</h2> Back to the "Tickets" admin page, and click "New Ticket Page". This is a little more tricky. Edit the top part: <verbatim> if {[info exists submit]} { set status Open set opened_by $login set assigned_to "unassigned" submit_ticket } </verbatim> Note the "set opened_by" bit -- that will automatically set the "opened_by" field to the login name of the bug reporter. Now, skip to the part with "EMail" and modify it like so: <verbatim> <th1>enable_output expr { "$login" eq "anonymous"}</th1> <tr> <td align="right"> EMail: <input type="text" name="private_contact" value="$<private_contact>" size="30"> </td> <td> <u>Not publicly visible</u>. Used by developers to contact you with questions. </td> </tr> <th1>enable_output 1</th1> </verbatim> This bit of code will get rid of the "email" field entry for logged-in users. Since we know the user's information, we don't have to ask for it. NOTE: it might be good to automatically scoop up the user's email and put it here. You might also want to enable people to actually assign the ticket to a specific person during creation. For this to work, you need to add the code for "assigned_to" as shown below under the heading "Modify the 'edit ticket' page". This will give you an additional combobox where you can choose a person during ticket creation. <h2>Modify the 'view ticket' page</h2> Look for the text "Contact:" (about halfway through). Then insert these lines after the closing tr tag and before the "enable_output" line: <verbatim> <td align="right">Assigned to:</td><td bgcolor="#d0d0d0"> $<assigned_to> </td> <td align="right">Opened by:</td><td bgcolor="#d0d0d0"> $<opened_by> </td> </verbatim> This will add a row which displays these two fields, in the event the user has <a href="./caps/ref.html#w">ticket "edit" capability</a>. <h2>Modify the 'edit ticket' page</h2> Before the "Severity:" line, add this: <verbatim> <tr> <td align="right">Assigned to:</td> <td> <th1>combobox assigned_to $assigned_choices 1</th1> </td> </tr> </verbatim> That will give you a drop-down list of assignees. The first argument to the TH1 command 'combobox' is the database field which the combobox is associated to. The next argument is the list of choices you want to show in the combobox (and that you specified in the second step above. The last argument should be 1 for a true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for details). Now, similar to the previous section, look for "Contact:" and add this: <verbatim> <tr> <td align="right">Reported by:</td> <td> <input type="text" name="opened_by" size="40" value="$<opened_by>"> </td> </tr> </verbatim> <h2>What next?</h2> Now you can add custom reports which select based on the person to whom the ticket is assigned. For example, an "Assigned to me" report could be: <verbatim> 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', substr(tkt_uuid,1,10) AS '#', datetime(tkt_mtime) AS 'mtime', type, status, subsystem, title FROM ticket WHERE assigned_to=user() </verbatim> |
Added www/customgraph.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 | # Customizing the Timeline Graph Beginning with version 1.33, Fossil gives users and skin authors significantly more control over the look and feel of the timeline graph. ## <a id="basic-style"></a>Basic Style Options Fossil includes several options for changing the graph's style without having to delve into CSS. These can be found in the details.txt file of your skin or under Admin/Skins/Details in the web UI. * **`timeline-arrowheads`** Set this to `0` to hide arrowheads on primary child lines. * **`timeline-circle-nodes`** Set this to `1` to make check-in nodes circular instead of square. * **`timeline-color-graph-lines`** Set this to `1` to colorize primary child lines. * **`white-foreground`** Set this to `1` if your skin uses white (or any light color) text. This tells Fossil to generate darker background colors for branches. ## <a id="adv-style"></a>Advanced Styling If the above options aren't enough for you, it's time to get your hands dirty with CSS. To get started, I recommend first copying all the [graph-related CSS rules](#default-css) to your stylesheet. Then it's simply a matter of making the necessary changes to achieve the look you want. So, next, let's look at the various graph elements and what purpose they serve. Each element used to construct the timeline graph falls into one of two categories: visible elements and positioning elements. We'll start with the latter, less obvious type. ## <a id="pos-elems"></a>Positioning Elements These elements aren't intended to be seen. They're only used to help position the graph and its visible elements. * <a id="tl-canvas"></a>**`.tl-canvas`** Set the left and right margins on this class to give the desired amount of space between the graph and its adjacent columns in the timeline. **Additional Classes** * `.sel`: See [`.tl-node`](#tl-node) for more information. * <a id="tl-rail"></a>**`.tl-rail`** Think of rails as invisible vertical lines on which check-in nodes are placed. The more simultaneous branches in a graph, the more rails required to draw it. Setting the `width` property on this class determines the maximum spacing between rails. This spacing is automatically reduced as the number of rails increases. If you change the `width` of `.tl-node` elements, you'll probably need to change this value, too. * <a id="tl-mergeoffset"></a>**`.tl-mergeoffset`** A merge line often runs vertically right beside a primary child line. This class's `width` property specifies the maximum spacing between the two. Setting this value to `0` will eliminate the vertical merge lines. Instead, the merge arrow will extend directly off the primary child line. As with rail spacing, this is also adjusted automatically as needed. * <a id="tl-nodemark"></a>**`.tl-nodemark`** In the timeline table, the second cell in each check-in row contains an invisible div with this class. These divs are used to determine the vertical position of the nodes. By setting the `margin-top` property, you can adjust this position. ## <a id="vis-elems"></a>Visible Elements These are the elements you can actually see on the timeline graph: the nodes, arrows, and lines. Each of these elements may also have additional classes attached to them, depending on their context. * <a id="tl-node"></a>**`.tl-node`** A node exists for each check-in in the timeline. **Additional Classes** * `.leaf`: Specifies that the check-in is a leaf (i.e. that it has no children in the same branch). * `.merge`: Specifies that the check-in contains a merge. * `.sel`: When the user clicks a node to designate it as the beginning of a diff, this class is added to both the node itself and the [`.tl-canvas`](#tl-canvas) element. The class is removed from both elements when the node is clicked again. * <a id="tl-arrow"></a>**`.tl-arrow`** Arrows point from parent nodes to their children. Technically, this class is just for the arrowhead. The rest of the arrow is composed of [`.tl-line`](#tl-line) elements. There are six additional classes that are used to distinguish the different types of arrows. However, only these combinations are valid: * `.u`: Up arrow that points to a child from its primary parent. * `.u.sm`: Smaller up arrow, used when there is limited space between parent and child nodes. * `.merge.l` or `.merge.r`: Merge arrow pointing either to the left or right. * `.warp`: A timewarped arrow (always points to the right), used when a misconfigured clock makes a check-in appear to have occurred before its parent ([example](https://www.sqlite.org/src/timeline?c=2010-09-29&nd)). * <a id="tl-line"></a>**`.tl-line`** Along with arrows, lines connect parent and child nodes. Line thickness is determined by the `width` property, regardless of whether the line is horizontal or vertical. You can also use borders to create special line styles. Here's a CSS snippet for making dotted merge lines: .tl-line.merge { width: 0; background: transparent; border: 0 dotted #000; } .tl-line.merge.h { border-top-width: 1px; } .tl-line.merge.v { border-left-width: 1px; } **Additional Classes** * `.merge`: A merge line. * `.h` or `.v`: Horizontal or vertical. * `.warp`: A timewarped line. ## <a id="default-css"></a>Default Timeline Graph CSS .tl-canvas { margin: 0 6px 0 10px; } .tl-rail { width: 18px; } .tl-mergeoffset { width: 2px; } .tl-nodemark { margin-top: 5px; } .tl-node { width: 10px; height: 10px; border: 1px solid #000; background: #fff; cursor: pointer; } .tl-node.leaf:after { content: ''; position: absolute; top: 3px; left: 3px; width: 4px; height: 4px; background: #000; } .tl-node.sel:after { content: ''; position: absolute; top: 2px; left: 2px; width: 6px; height: 6px; background: red; } .tl-arrow { width: 0; height: 0; transform: scale(.999); border: 0 solid transparent; } .tl-arrow.u { margin-top: -1px; border-width: 0 3px; border-bottom: 7px solid #000; } .tl-arrow.u.sm { border-bottom: 5px solid #000; } .tl-line { background: #000; width: 2px; } .tl-arrow.merge { height: 1px; border-width: 2px 0; } .tl-arrow.merge.l { border-right: 3px solid #000; } .tl-arrow.merge.r { border-left: 3px solid #000; } .tl-line.merge { width: 1px; } .tl-arrow.warp { margin-left: 1px; border-width: 3px 0; border-left: 7px solid #600000; } .tl-line.warp { background: #600000; } |
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 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 | # Skinning the Fossil Web Interface The Fossil web interface comes with a pre-configured look and feel. The default look and feel works fine in many situations. However, you may want to change the look and feel (the "skin") of Fossil to better suite your own individual tastes. This document provides background information to aid you in that task. ## <a id="builtin"></a>Built-in Skins Fossil comes with [multiple built-in skins](/skins). If the default skin does not suite your tastes, perhaps one of the other built-in skins will work better. If nothing else, the built-in skins can serve as examples or templates that you can use to develop your own custom skin. The sources to these built-ins can be found in the Fossil source tree under the skins/ folder. The [skins/](/dir?ci=trunk&name=skins) folder contains a separate subfolder for each built-in skin, with each subfolders holding at least these five files: * css.txt * details.txt * footer.txt * header.txt * js.txt Try out the built-in skins by using the --skin option on the [fossil ui](/help?cmd=ui) or [fossil server](/help?cmd=server) commands. ## <a id="sharing"></a>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 the new repository is initialized to the skin of the repository from which it was cloned. # Structure Of A Fossil Web Page Every HTML page generated by Fossil has the same basic structure: | Fossil-Generated HTML Header | | Skin Header | | Fossil-Generated Content | | Skin Footer | | Fossil-Generated HTML Footer | By default, Fossil starts every generated HTML page with this: <html> <head> <base href="..."> <meta http-equiv="Content-Security-Policy" content="...."> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>....</title> <link rel="stylesheet" href="..." type="text/css"> </head> <body class="FEATURE"> Fossil used to require a static version of this in every skin’s Header area, but over time, we have found good cause to generate multiple elements at runtime. One such is the `FEATURE` element, being either the top-level HTTP request routing element (e.g. `doc`) or an aggregate feature class that groups multiple routes under a single name. A prime example is `forum`, which groups the `/forummain`, `/forumpost`, and `/forume2` routes, allowing per-feature CSS. For instance, to style `<blockquote>` tags specially for forum posts written in Markdown, leaving all other block quotes alone, you could say: body.forum div.markdown blockquote { margin-left: 10px; } You can [override this generated HTML header](#override) by including a “`<body>`†tag somewhere in the Header area of the skin, but it is almost always best to limit a custom skin’s Header section to something like this: <div class="sidebar" id="version-2.24">Prior to Fossil 2.24, we used generic `<div>` elements to mark up these sections of the header, but we switched to these semantic tag names to give browser accessibility features more freedom to do intelligent things with the page content. Those who made custom skins based on the old way of doing things will need to track this change when upgrading, else the corresponding CSS will mistarget the page header elements. Also, if you’re using Fossil’s chat feature, failing to track this change will cause it to miscalculate the message area size, resulting in double scrollbars. Simply diffing your custom header in the skin editor against the stock version should be sufficient to show what you need to change.</div> <header> ... </header> <nav class="mainmenu" title="Main Menu"> ... </nav> <nav id="hbdrop" class="hbdrop" title="sitemap"></nav> See the stock skins’ headers for ideas of what to put in place of the ellipses. The Fossil-generated Content section immediately follows this Header. It will look like this: <div class="content"> ... Fossil-generated content here ... </div> After the Content is the custom Skin Footer section which should follow this template: <footer> ... skin-specific stuff here ... </footer> As with the `<header>` change called out above, this, too, is a breaking change in Fossil 2.24. Finally, Fossil always adds its own footer (unless overridden) to close out the generated HTML: </body> </html> ## <a id="mainmenu"></a>Changing the Main Menu Contents The actual text content of the skin’s main menu is not part of the skin proper if you’re using one of the stock skins. If you look at the Header section of the skin, you’ll find a `<div class="mainmenu">` element whose contents are set by a short [TH1](./th1.md) script from the contents of the **Main Menu** section of the Setup → Configuration screen. This feature allows the main menu contents to stay the same across different skins, so you no longer have to reapply menu customizations when trying different skins. See the [`capexpr`](./th1.md#capexpr) section of the TH1 docs for help on interpreting the default contents of this block. ## <a id="override"></a>Overriding the HTML Header and Footer Notice that the `<html>`, `<head>`, and opening `<body>` elements at the beginning of the document, and the closing `</body>` and `</html>` elements at the end are automatically generated by Fossil. This is recommended. However, for maximum design flexibility, Fossil allows those elements to be supplied as part of the configurable Skin Header and Skin Footer. If the Skin Header contains the text "`<body`", then Fossil assumes that the Skin Header and Skin Footer will handle all of the `<html>`, `<head>`, and `<body>` text itself, and the Fossil-generated header and footer will be blank. When overriding the HTML Header in this way, you will probably want to use some of the [TH1 variables documented below](#vars) such as `$stylesheet_url` to avoid hand-writing code that Fossil can generate for you. # Designing, Debugging, and Installing A Custom Skin It is possible to develop a new skin from scratch. But a better and easier approach is to use one of the existing built-in skins as a baseline and make incremental modifications, testing after each step, to obtain the desired result. The skin is controlled by five files: <dl> <dt><b>css.txt</b></dt> <dd>The css.txt file is the text of the CSS for Fossil. Fossil might add additional CSS elements after the css.txt file, if it sees that the css.txt omits some CSS components that Fossil needs. But for the most part, the content of the css.txt is the CSS for the page.</dd> <dt><b>details.txt</b><dt> <dd>The details.txt file is short list of settings that control the look and feel, mostly of the timeline. The default details.txt file looks like this: <pre> pikchr-background: "" pikchr-fontscale: "" pikchr-foreground: "" pikchr-scale: "" timeline-arrowheads: 1 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 </pre> The three "timeline-" settings in details.txt control the appearance of certain aspects of the timeline graph. The number on the right is a boolean - "1" to activate the feature and "0" to disable it. The "white-foreground:" setting should be set to "1" if the page color has light-color text on a darker background, and "0" if the page has dark text on a light-colored background. If the "pikchr-foreground" setting is defined and is not an empty string then it specifies a foreground color to use for [pikchr diagrams](./pikchr.md). The default pikchr foreground color is black, or white if the "white-foreground" boolean is set. The "pikchr-background" settings does the same for the pikchr diagram background color. If the "pikchr-fontscale" and "pikchr-scale" values are not empty strings, then they should be floating point values (close to 1.0) that specify relative scaling of the fonts in pikchr diagrams and other elements of the diagrams, respectively. </dd> <dt><b>footer.txt</b> and <b>header.txt</b></dt> <dd>The footer.txt and header.txt files contain the Skin Footer and Skin Header respectively. Of these, the Skin Header is the most important, as it contains the markup used to generate the banner and menu bar for each page. Both the footer.txt and header.txt file are [processed using TH1](#headfoot) prior to being output as part of the overall web page.</dd> <dt><b>js.txt</b></dt> <dd>The js.txt file is optional. It is intended to be javascript. The complete text of this javascript might be inserted into the Skin Footer, after being processed using TH1, using code like the following in the "footer.txt" file: <pre> <script nonce="$nonce"> <th1>styleScript</th1> </script> </pre> The js.txt file was originally used to insert javascript that controls the hamburger menu in the default skin. More recently, the javascript for the hamburger menu was moved into a separate built-in file. Skins that use the hamburger menu typically cause the javascript to be loaded by including the following TH1 code in the "header.txt" file: <pre> <th1>builtin_request_js hbmenu.js</th1> </pre> The difference between `styleScript` and `builtin_request_js` is that the `styleScript` command interprets the file using TH1 and injects the content directly into the output stream, whereas the `builtin_request_js` command inserts the Javascript verbatim and does so at some unspecified future time down inside the Fossil-generated footer. You can use either approach in custom skins that you create. Note that the "js.txt" file is *not* automatically inserted into the generate HTML for a page. You, the skin designer, must cause the javascript to be inserted by issuing appropriate TH1 commands in the "header.txt" or "footer.txt" files.</dd> </dl> Developing a new skin is simply a matter of creating appropriate versions of these five control files. ### Skin Development Using The Web Interface Users with admin privileges can use the Admin/Skin configuration page on the web interface to develop a new skin. The development of a new skin occurs without disrupting the existing skin. So you can work on a new skin for a Fossil instance while the existing skin is still in active use. The new skin is a "draft" skin. You initialize one of 9 draft skins to either the current skin or to one of the built-in skins. Then use forms to edit the 5 control files described above. The new skin can be tested after each edit. Finally, once the new skin is working as desired, the draft skin is "published" and becomes the new live skin that most users see. ### Skin Development Using A Local Text Editor An alternative approach is to copy the five control files for your baseline skin into a temporary working directory (here called "./newskin") and then launch the [fossil ui](/help?cmd=ui) command with the "--skin ./newskin" option. If the argument to the --skin option contains a "/" character, then the five control files are read out of the directory named. You can then edit the control files in the ./newskin folder using you favorite text editor, and press "Reload" on your browser to see the effects. ### Disabling The Web Browser Cache During Development Fossil is aggressive about asking the web browser to cache resources. While developing a new skin, it is often helpful to put your web browser into developer mode and disable the cache. If you fail to do this, then you might make some change to your skin under development and press "Reload" only to find that the display did not change. After you have finished work your skin, the caches should synchronize with your new design and you can reactivate your web browser's cache and take it out of developer mode. ## <a id="headfoot"></a>Header and Footer Processing The `header.txt` and `footer.txt` control files of a skin are the HTML text of the Skin Header and Skin Footer, except that before being inserted into the output stream, the text is run through a [TH1 interpreter](./th1.md) that might adjust the text as follows: * All text within <th1>...</th1> is omitted from the output and 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 of the form "$NAME" or "$<NAME>" is replaced with the value of the TH1 variable NAME. For example, first few lines of a typical Skin Header will look like this: <div class="header"> <div class="title"><h1>$<project_name></h1>$<title>/div> After variables are substituted by TH1, that will look more like this: <div class="header"> <div class="title"><h1>Project Name</h1>Page Title</div> As you can see, two TH1 variable substitutions were done. 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. ## <a id="menu"></a>Customizing the ≡ Hamburger Menu The menu bar of the default skin has an entry to open a drop-down menu with additional navigation links, represented by the ≡ button (hence the name "hamburger menu"). The Javascript logic to open and close the hamburger menu when the button is clicked is usually handled by a script named "hbmenu.js" that is one of the [built-in resource files](/test-builtin-files) that are part of Fossil. The ≡ button for the hamburger menu is added to the menu bar by the following TH1 commands in the `header.txt` file, right before the menu bar links: html "<a id='hbbtn' href='$home/sitemap'>☰</a>" builtin_request_js hbmenu.js The hamburger button can be repositioned between the other menu links (but the drop-down menu is always left-aligned with the menu bar), or it can be removed by deleting the above statements. The "html" statement inserts the appropriate `<a>` for the hamburger menu button (some skins require something slightly different - for example the ardoise skins wants "`<li><a>`"). The "builtin_request_js hbmenu.js" asks Fossil to include the "hbmenu.js" resource files in the Fossil-generated footer. The hbmenu.js script requires the following `<div>` element somewhere in your header, in which to build the hamburger menu. <div id='hbdrop'></div> Out of the box, the contents of the panel is populated with the [Site Map](/sitemap), but only if the panel does not already contain any HTML elements (that is, not just comments, plain text or non-presentational white space). So the hamburger menu can be customized by replacing the empty `<div id='hbdrop'></div>` element with a menu structure knitted according to the following template: <div id="hbdrop" data-anim-ms="400"> <ul class="columns" style="column-width: 20em; column-count: auto"> <!-- NEW GROUP WITH HEADING LINK --> <li> <a href="$home$index_page">Link: Home</a> <ul> <li><a href="$home/timeline">Link: Timeline</a></li> <li><a href="$home/dir?ci=tip">Link: File List</a></li> </ul> </li> <!-- NEW GROUP WITH HEADING TEXT --> <li> Heading Text <ul> <li><a href="$home/doc/trunk/www/customskin.md">Link: Theming</a></li> <li><a href="$home/doc/trunk/www/th1.md">Link: TH1 Scripts</a></li> </ul> </li> <!-- NEXT GROUP GOES HERE --> </ul> </div> The custom `data-anim-ms` attribute can be added to the panel element to direct the Javascript logic to override the default menu animation duration of 400 ms. A faster animation duration of 80-200 ms may be preferred for smaller menus. The animation is disabled by setting the attribute to `"0"`. ## <a id="vars"></a>TH1 Variables Before expanding the TH1 within the header and footer, Fossil first initializes a number of TH1 variables to values that depend on repository 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. * **`project_description`** - The project_description variable is filled with the description 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. * **`default_csp`** - [Fossil’s default CSP](./defcsp.md) unless [overridden by custom TH1 code](./defcsp.md#th1). Useful within the skin for inserting the CSP into a `<meta>` tag within [a custom `<head>` element](#headfoot). * **`nonce`** - The value of the cryptographic nonce for the request being processed. * **`release_version`** - The release version of Fossil. Ex: "1.31" * **`manifest_version`** - A prefix on the 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. * **`logo_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. ## <a id="procedure"></a>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 *.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. ## See Also * [Customizing the Timeline Graph](customgraph.md) |
Added www/defcsp.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 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 | # The Default Content Security Policy (CSP) When Fossil’s web interface generates an HTML page, it normally includes a [Content Security Policy][csp] (CSP) in the `<head>`. The CSP specifies allowed sources for external resources such as images, CSS, javascript, and so forth. The purpose of CSP is to provide an extra layer of protection against [cross-site scripting][xss] (XSS) and code injection attacks. Compatible web browsers will not use external resources unless they are specifically allowed by the CSP, which dramatically reduces the attack surface of the application. Fossil does not rely on CSP for security. A Fossil server should be secure from attack even without CSP. Fossil includes built-in server-side content filtering logic. For example, Fossil purposely breaks `<script>` tags when it finds them in Markdown and Fossil Wiki documents. And the Fossil build process scans the source code for potential injection vulnerabilities and refuses to compile if any problems are found. However, CSP provides an additional layer of defense against undetected bugs that might lead to a vulnerability. ## The Default Restrictions The default CSP used by Fossil is as follows: <pre> default-src 'self' data:; script-src 'self' 'nonce-$nonce'; style-src 'self' 'unsafe-inline'; img-src * data:; </pre> The default is recommended for most installations. However, the site administrators can overwrite this default CSP using the [default-csp setting](/help?cmd=default-csp). For example, CSP restrictions can be completely disabled by setting the default-csp to: default-src *; The following sections detail the maining of the default CSP setting. ### <a id="base"></a> default-src 'self' data: This policy means mixed-origin content isn’t allowed, so you can’t refer to resources on other web domains. Browsers will ignore a link like the one in the following Markdown under our default CSP: ![fancy 3D Fossil logotype](https://i.imgur.com/HalpMgt.png) If you look in the browser’s developer console, you should see a CSP error when attempting to render such a page. The default policy does allow inline `data:` URIs, which means you could [data-encode][de] your image content and put it inline within the document: ![small inline image](data:image/gif;base64,R0lGODlh...) That method is best used for fairly small resources. Large `data:` URIs are hard to read and edit. There are secondary problems as well: if you put a large image into a Fossil forum post this way, anyone subscribed to email alerts will get a copy of the raw URI text, which can amount to pages and pages of [ugly Base64-encoded text][b64]. For inline images within [embedded documentation][ed], it suffices to store the referred-to files in the repo and then refer to them using repo-relative URLs: ![large inline image](./inlineimage.jpg) This avoids bloating the doc text with `data:` URI blobs: There are many other cases, [covered below](#serving). [b64]: https://en.wikipedia.org/wiki/Base64 [svr]: ./server/ ### <a id="img"></a> img-src * data: It was not always thus, but after careful consideration, we’ve chosen to leave the source of inline images unrestricted by default in Fossil. This allows you to pull them in from remote systems, to pull them from within the Fossil repository itself, or to use `data:` URIs. If you are certain all images come from only within the repository, you can close off certain risks — tracking pixels, broken image format decoders, system dialog box spoofing, etc. — by changing this to “`img-src 'self'`†possibly followed by “`data:`†if you will also use `data:` URIs. ### <a id="style"></a> style-src 'self' 'unsafe-inline' This policy allows CSS information to come from separate files hosted under the Fossil repo server’s Internet domain. It also allows inline CSS `<style>` tags within the document text. The `'unsafe-inline'` declaration allows CSS within individual HTML elements: <p style="margin-left: 4em">Indented text.</p> As the "`unsafe-`" prefix on the name implies, the `'unsafe-inline'` feature is suboptimal for security. However, there are a few places in the Fossil-generated HTML that benefit from this flexibility and the work-arounds are verbose and difficult to maintain. Furthermore, the harm that can be done with style injections is far less than the harm possible with injected javascript. And so the `'unsafe-inline'` compromise is accepted for now, though it might go away in some future release of Fossil. ### <a id="script"></a> script-src 'self' 'nonce-%s' This policy disables in-line JavaScript and only allows `<script>` elements if the `<script>` includes a `nonce` attribute that matches the one declared by the CSP. That nonce is a large random number, unique for each HTTP page generated by Fossil, so an attacker cannot guess the value, so the browser will ignore an attacker’s injected JavaScript. That nonce can only come from one of three sources, all of which should be protected at the system administration level on the Fossil server: * **Fossil server C code:** All code paths in Fossil that emit `<script>` elements include the `nonce` attribute. There are several cases, such as the “JavaScript†section of a [custom skin][cs]. That text is currently inserted into each HTML page generated by Fossil,¹ which means it needs to include a `nonce` attribute to allow it to run under this default CSP. We consider JavaScript emitted via these paths to be safe because it’s audited by the Fossil developers. We assume that you got your Fossil server’s code from a trustworthy source and that an attacker cannot replace your Fossil server binary. * **TH1 code:** The Fossil TH1 interpreter pre-defines the [`$nonce` variable](./th1.md#nonce) for use in [custom skins][cs]. For example, some of the stock skins that ship with Fossil include a wall clock feature up in the corner that updates once a minute. These paths are safe in the default Fossil configuration because only the [all-powerful Setup user][su] can write TH1 code that executes in the server’s running context. There is, however, [a default-disabled path](#xss) to beware of, covered in the next section. * **[CGI server extensions][ext]:** Fossil exports the nonce to the CGI in the `FOSSIL_NONCE` environment variable, which it can then use in `<script>` elements it generates. Because these extensions can only be installed by the Fossil server’s system administrator, this path is also considered safe. [ext]: ./serverext.wiki [su]: ./caps/admin-v-setup.md#apsu #### <a id="xss"></a>Cross-Site Scripting via Ordinary User Capabilities We’re so restrictive about how we treat JavaScript because it can lead to difficult-to-avoid scripting attacks. If we used the same CSP for `<script>` tags [as for `<style>` tags](#style), anyone with check-in rights on your repository could add a JavaScript file to your repository and then refer to it from other content added to the site. Since JavaScript code can access any data from any URI served under its same Internet domain, and many Fossil users host multiple Fossil repositories under a single Internet domain, such a CSP would only be safe if all of those repositories are trusted equally. Consider [the Chisel hosting service](http://chiselapp.com/), which offers free Fossil repository hosting to anyone on the Internet, all served under the same `http://chiselapp.com/user/$NAME/$REPO` URL scheme. Any one of those hundreds of repositories could trick you into visiting their repository home page, set to [an HTML-formatted embedded doc page][hfed] via Admin → Configuration → Index Page, with this content: <script src="/doc/trunk/bad.js"></script> That script can then do anything allowed in JavaScript to *any other* Chisel repository your browser can access. The possibilities for mischief are *vast*. For just one example, if you have login cookies on four different Chisel repositories, your attacker could harvest the login cookies for all of them through this path if we allowed Fossil to serve JavaScript files under the same CSP policy as we do for CSS files. This is why the default configuration of Fossil has no way for [embedded docs][ed], [wiki articles][wiki], [tickets][tkt], [forum posts][fp], or [tech notes][tn] to automatically insert a nonce into the page content. This is all user-provided content, which could link to user-provided JavaScript via check-in rights, effectively giving all such users a capability that is usually reserved to the repository’s administrator. The default-disabled [TH1 documents feature][edtf] is the only known path around this restriction. If you are serving a Fossil repository that has any user you do not implicitly trust to a level that you would willingly run any JavaScript code they’ve provided, blind, you **must not** give the `--with-th1-docs` option when configuring Fossil, because that allows substitution of the [pre-defined `$nonce` TH1 variable](./th1.md#nonce) into [HTML-formatted embedded docs][hfed]: <script src="/doc/trunk/bad.js" nonce="$nonce"></script> Even with this feature enabled, you cannot put `<script>` tags into Fossil Wiki or Markdown-formatted content, because our HTML generators for those formats purposely strip or disable such tags in the output. Therefore, if you trust those users with check-in rights to provide JavaScript but not those allowed to file tickets, append to wiki articles, etc., you might justify enabling TH1 docs on your repository, since the only way to create or modify HTML-formatted embedded docs is through check-ins. [ed]: ./embeddeddoc.wiki [edtf]: ./embeddeddoc.wiki#th1 [hfed]: ./embeddeddoc.wiki#html ## <a id="serving"></a>Serving Files Within the Limits There are several ways to serve files within the above restrictions, avoiding the need to [override the default CSP](#override). In decreasing order of simplicity and preference: 1. Within [embedded documentation][ed] (only!) you can refer to files stored in the repo using document-relative file URLs: ![inline image](./inlineimage.jpg) 2. Relative file URLs don’t work from [wiki articles][wiki], [tickets][tkt], [forum posts][fp], or [tech notes][tn], but you can still refer to them inside the repo with [`/doc`][du] or [`/raw`][ru] URLs: ![inline image](/doc/trunk/images/inlineimage.jpg) <img src="/raw/logo.png" style="float: right; margin-left: 2em"> 3. Store the files as [unversioned content][uv], referred to using [`/uv`][uu] URLs instead: ![logo](/uv/logo.png) 4. Use the [optional CGI server extensions feature](./serverext.wiki) to serve such content via `/ext` URLs. 5. Put Fossil behind a [front-end proxy server][svr] as a virtual subdirectory within the site, so that our default CSP’s “self†rules match static file routes on that same site. For instance, your repo might be at `https://example.com/code`, allowing documents in that repo to refer to: * images as `/image/foo.png` * JavaScript files as `/js/bar.js` * CSS style sheets as `/style/qux.css` Although those files are all outside the Fossil repo at `/code`, keep in mind that it is the browser’s notion of “self†that matters here, not Fossil’s. All resources come from the same Internet domain, so the browser cannot distinguish Fossil-provided content from static content served directly by the proxy server. This method opens up many other potential benefits, such as [TLS encryption][tls], high-performance tuning via custom HTTP headers, integration with other web technologies like PHP, etc. You might wonder why we rank in-repo content as most preferred above. It is because the first two options are the only ones that cause such resources to be included in an initial clone or in subsequent repo syncs. The methods further down the list have a number of undesirable properties: 1. Relative links to out-of-repo files break in `fossil ui` when run on a clone. 2. Absolute links back to the public repo instance solve that: ![inline image](https://example.com/images/logo.png) ...but using them breaks some types of failover and load-balancing schemes, because it creates a [single point of failure][spof]. 3. Absolute links fail when one’s purpose in using a clone is to recover from the loss of a project web site by standing that clone up [as a server][svr] elsewhere. You probably forgot to copy such external resources in the backup copies, so that when the main repo site disappears, so do those files. Unversioned content is in the middle of the first list above — between fully-external content and fully in-repo content — because it isn’t included in a clone unless you give the `--unversioned` flag. If you then want updates to the unversioned content to be included in syncs, you have to give the same flag to [a `sync` command](/help?cmd=sync). There is no equivalent with other commands such as `up` and `pull`, so you must then remember to give `fossil uv` commands when necessary to pull new unversioned content down. Thus our recommendation that you refer to in-repo resources exclusively. [du]: /help?cmd=/doc [fp]: ./forum.wiki [ru]: /help?cmd=/raw [spof]: https://en.wikipedia.org/wiki/Single_point_of_failure [tkt]: ./tickets.wiki [tn]: ./event.wiki [tls]: ./server/debian/nginx.md [uu]: /help?cmd=/uv [uv]: ./unvers.wiki [wiki]: ./wikitheory.wiki ## <a id="override"></a>Overriding the Default CSP If you wish to relax the default CSP’s restrictions or to tighten them further, there are multiple ways to accomplish that. The following methods are listed in top-down order to give the simplest and most straightforward method first. Further methods dig down deeper into the stack, which is helpful to understand even if you end up using a higher-level method. ### <a id="cspsetting"></a>The `default-csp` Setting If the [`default-csp` setting](/help?cmd=default-csp) is defined and is not an empty string, its value is injected into the page using [TH1](./th1.md) via one or more of the methods below, depending on the skin you’re using and local configuration. Changing this setting is the easiest way to set a nonstandard CSP on your site. Because a blank setting tells Fossil to use its hard-coded default CSP, you have to say something like the following to get a repository without content security policy restrictions: $ fossil set -R /path/to/served/repo.fossil default-csp 'default-src *' We recommend that instead of using the command line to change this setting that you do it via the repository’s web interface, in Admin → Settings. Write your CSP rules in the edit box marked "`default-csp`". Do not add hard newlines in that box: the setting needs to be on a single long line. Beware that changes take effect immediately, so be careful with your edits: you could end up locking yourself out of the repository with certain CSP changes! There are a few reasons why changing this setting via the command line is inadvisable, except for very short settings like the example above: 1. You have to be sure to set it on the repository where you want the CSP to apply. Changing this setting on your local clone doesn’t affect the remote repo you cloned from, which is most likely where you want the CSP restrictions. 2. For more complicated CSPs, the quoting rules for your shell and the CSP syntax may interact, making it difficult or impossible to set your desired CSP via the command line. Setting it via the web UI doesn’t have this problem. ### <a id="th1"></a>TH1 Setup Hook Fossil sets [the TH1 variable `$default_csp`][thvar] from the `default-csp` setting and uses *that* to inject the value into generated HTML pages in its stock configuration. This means that another way you can override this value is to use the [`th1-setup` hook script](./th1-hooks.md), which runs before TH1 processing happens during skin processing: $ fossil set th1-setup "set default_csp {default-src 'self'}" After [the above](#admin-ui), this is the cleanest method. [thvar]: ./customskin.md#vars ### <a id="csrc"></a>Fossil C Source Code When you do neither of the above things, Fossil uses [a hard-coded default](/info?ln=527-530&name=65a555d0d4fb846b). We tell you about this not to suggest that you hack the Fossil C source code to change the CSP but simply to document the next step before we move down-stack. ### <a id="header"></a>Skin Header [In the normal case](./customskin.md#override), Fossil injects the CSP retrieved by one of the above methods into the header of all HTML documents it generates: ```HTML <head>... <meta http-equiv="Content-Security-Policy" content="..."> ... ``` Fossil skips this when you’re using a custom skin *and* its [Header section](./customskin.md#headfoot) includes a `<body>` tag. This is because prior to Fossil 2.5, the Header for a custom skin normally contained everything from the opening `<html>` tag through the leading `<body>` tag. From that version onward, Fossil now generates that header when possible, so that the skin’s Header normally provides only the opening tags of the document body, rather than the HTML header. When we added CSP support in Fossil 2.7, we made use of that mechanism to inject the CSP into the generated HTML document header. For backwards compatibility, Fossil skips this when the skin’s Header includes a `<body>` tag. Fossil takes that as a hint that it’s dealing with a skin made in the pre-Fossil-2.5 days and doesn’t try to blindly override it. The problem then is that you may be a Fossil user from the days before Fossil 2.5, and you may be using a custom skin. This includes users who selected one of the stock skins, since for the purposes of this section, there is no difference between the cases. If you go into Admin → Skins → Header and find a `<body>` tag, none of the above will apply to your repo since Fossil will not be injecting its CSP into your pages. If you selected one of the stock skins (e.g. Khaki) prior to upgrading to Fossil 2.5+ and didn’t make any changes to it since that time, you can take the simplest option, which is to simply revert to the stock version of the skin, so your pages will have the CSP injected, at which point this document will begin describing what Fossil does with that repo. If you’re using a customized version of one of the stock skins, the skinning mechanism has a diff feature to make it easier to fold your local changes into the stock version. If you’re using a fully customized skin, we recommend replicating the method that [the Bootstrap skin uses][dcinj].² Alone among the stock Fossil skins, Bootstrap still does old-style Header processing, providing the entire HTML header and the start of the document body. We do *not* recommend injecting an explicit `Content-Security-Policy` meta tag into a header to override Fossil’s default CSP. That means you have to edit the skin every time you want to change the CSP. Use the TH1 `$default_csp` variable like the Bootstrap skin does so you can use one of the methods above with your custom skin, so the CSP can vary independently of the skin. [dcinj]: /info?ln=7&name=bef080a6929a3e6f ### <a id="fep"></a>Front-End Proxy If your Fossil repo is behind some sort of HTTP [front-end proxy][svr], the [preferred method][pmcsp] for setting the CSP is via a custom HTTP header, which most HTTP reverse proxy programs allow. Beware that if you have a CSP set via both the HTTP and HTML headers that the two CSPs [merge](https://stackoverflow.com/a/51153816/142454), taking the most restrictive elements of each CSP. If you wish the proxy layer’s setting to completely override Fossil’s setting, you will need to combine that with one of the methods above to either remove the Fossil-provided CSP or to make Fossil provide a no-restrictions CSP which the front-end proxy can then tighten down. [pmcsp]: https://developers.google.com/web/fundamentals/security/csp/#the_meta_tag ------------ **Asides and Digressions:** 1. Fossil might someday switch to serving the “JavaScript†section of a custom skin as a virtual text file, allowing it to be cached by the browser, reducing page load times. 2. The stock Bootstrap skin *did* provide redundant CSP text from Fossil 2.7 through Fossil 2.9, so setting the CSP via the higher level methods did not work with that skin. We fixed this in Fossil 2.10, but if you selected the Bootstrap skin prior to that, you’re now running on a *copy* of it stored in your repo settings table, so the change to the stock version of the skin won’t affect that repo automatically. You will have to either merge the diffs in with your local changes or revert to the stock version of the skin. [cs]: ./customskin.md [csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP [de]: https://dopiaza.org/tools/datauri/index.php [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting |
Added www/delta-manifests.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 242 243 244 245 246 | # Delta Manifests <div class="sidebar">Do not confuse these with the core [Fossil delta format](./delta_format.wiki). This document describes an optional feature not enabled by default.</div> This article describes "delta manifests," a special-case form of checkin manifest which is intended to take up far less space than a normal checkin manifest, in particular for repositories with many files. We'll see, however, that the space savings, if indeed there are any, come with some caveats. This article assumes that the reader is at least moderately familiar with Fossil's [artifact file format](./fileformat.wiki), in particular the structure of checkin manifests, and it won't make much sense to readers unfamiliar with that topic. # Background and Motivation of Delta Manifests A checkin manifest includes a list of every file in that checkin. A moderately-sized project can easily have a thousand files, and every checkin manifest will include those thousand files. As of this writing Fossil's own checkins contain 989 files and the manifests are 80kb each. Thus a checkin which changes only 2 bytes of source code ostensibly costs another 80kb of storage for the manifest for that change. Delta manifests were conceived as a mechanism to help combat that storage overhead. # Makeup of a Delta Manifest A delta manifest is structured like a normal manifest (called a "baseline" manifest) except that it has *two types of parents*: the P-card which is part of (nearly) every manifest and a so-called baseline (denoted by a B-card). The P-card tells us which artifact(s) is/are the parents for purposes of the SCM version DAG. The B-card tells us which manifest to use as a basis for this delta. The B-card need not be, and often is not, the same as the P-card. Here's an example: ``` B c04ce8aaf1170966c6f8abcce8b57e72a0fa2b81 C Minor\sdoc\supdates... D 2021-03-11T18:56:24.686 F bindings/s2/shell_extend.c 6d8354c693120a48cfe4798812cd24499be174b2 <15 F-cards snipped for brevity> F src/repo.c 2f224cb0e59ccd90ba89c597e40b8e8d87506638 P 61d3e64e6fb1a93d4a7b0182e4c6b94d178d66d9 R a84ec2e8e1eb37ff0d94cac262795e23 U stephan Z 536e6d26dd8dbe2779d9e5f52a15518e ``` The B-card names another manifest, by its unique ID, the same way that a P-card does. A manifest may have multiple P-card parents (the second and subsequent ones denoting merge parents) but B-cards always refer to exactly one parent. What unambiguously distinguishes this as a delta is the existence of the B-card. All deltas have a B-card and no other type of artifact has one. What also, but not unambiguously, distinguishes it as a delta is that it has only 17 F-cards, whereas a baseline manifest in that same repository has (as of this writing) 291 F-cards. In this particular case, the delta manifest is 1363 bytes, compared to 20627 bytes for the next checkin - a baseline manifest. That's a significant saving in F-cards, especially if a repository contains thousands of files. That savings, however, comes with caveats which we'll address below. Trivia regarding the B-card: - The B-card always refers to a baseline manifest, not another delta. - Deltas may not chain with another delta, but any number of deltas may have the same B-card. It is quite common for a series of delta manifest checkins, each of which derives (in the P-card sense) from the one before it, to have the same B-card. A delta manifest is functionally identical to a normal manifest except that it has a B-card and how it records F-cards. Namely, it only records F-cards which have changed at some point between this delta and the version represented by the delta's B-card. This recording of F-card *differences* also means that delta manifests, unlike normal manifests, have to explicitly record deleted F-cards. Baseline manifests do not record deletions. Instead, they include a list of every file which is part of that checkin. Deltas, however, record the differences between their own version and a baseline version, and thus have to record deletions. They do this by including F-cards which have only a file name and no hash. Iterating over F-cards in a manifest is something several important internal parts of Fossil have to do. Iterating over a baseline manifest, e.g. when performing a checkout, is straightforward: simply walk through the list in the order the cards are listed. A delta, however, introduces a significant wrinkle to that process. In short, when iterating over a delta's F-cards, code has to compare the delta's list to the baseline's list. If the delta has an entry the parent does not have, or which is a newer entry for the same file, the delta's entry is used. If the delta is missing an entry which the baseline has, the baseline's entry is used. When a deletion F-card is discovered in the delta (recall that baselines do not record deletions), iteration over that card is skipped - the internal algorithms which iterate over F-cards never report deletions to the code iterating over those cards. The reason for that is consistency: only deltas record file deletions, but the fact that it's a delta is an internal detail, not something which higher-level code should concern itself with. If higher-level iteration code were shown file deletions, they would effectively be dealing with a leaky abstraction and special-case handling which only applies to delta manifests. The F-card iteration API hides such details from its users (other Fossil-internal APIs). # When does Fossil Create Deltas? By default, Fossil never creates delta manifests. It can be told to do so using the `--delta` flag to the [`commit` command](/help/commit). (Before doing so in your own repositories, please read the section below about the caveats!) When a given repository gets a delta manifest for the first time, Fossil records that fact in the repository's `config` table with an entry named `seen-delta-manifest`. If, in later sessions, Fossil sees that that setting has a true value, it will *consider* creating delta manifests by default. Conversely, the [`forbid-delta-manifests` repository config setting](/help/forbid-delta-manifests) may be used to force Fossil to *never* create deltas. That setting will propagate to other repository clones via the sync process, to try to ensure that no clone introduces a delta manifests. We'll cover reasons why one might want to use that setting later on. After creating a delta manifest during the commit process, Fossil examines the size of the delta. If, in Fossil's opinion, the space savings are not significant enough to warrant the delta's own overhead, it will discard the delta and create a new baseline manifest instead. (The heuristic it uses for that purpose is tucked away in Fossil's checkin algorithm.) # Caveats Delta manifests may appear, on the surface, to be a great way to save a few bytes of repository space. There are, however, caveats... ## Space Savings? Though deltas were conceived as a way to save storage space, that benefit is *not truly achieved* because... When a manifest is created, Fossil stores its parent version as a [fossil delta](./delta_format.wiki) (as opposed to a delta manifest) which succinctly descibes the differences between the parent and its new child. This form of compression is extremely space-efficient and can reduce the real storage space requirements of a manifest from tens or hundreds of kilobytes down to a kilobyte or less for checkins which modify only a few files. As an example, as of this writing, Fossil's [tip checkin baseline manifest](/artifact/decd537016bf) is 80252 bytes (uncompressed), and the delta-compressed baseline manifest of the [previous checkin](/artifact/2f7c93f49c0e) is stored as a mere 726 bytes of Fossil-delta'd data (not counting the z-lib compression which gets applied on top of that). In this case, the tip version modified 7 files compared to its parent version. Thus delta manifests do not *actually* save much storage space. They save *some*, in particular in the tip checkin version: Fossil delta-compresses *older* versions of checkins against the child versions, as opposed to delta-compressing the children against the parents. The reason is to speed up access for the most common case - the latest version. Thus tip-version delta manifests are more storage-space efficient than tip-version baseline manifests. Once the next version is committed, though, and Fossil deltification is applied to those manifests, that difference in space efficiency shrinks tremendously, often to the point of insignificance. We can observe the Fossil-delta compression savings using a bit of 3rd-party code which can extract Fossil-format blobs both with and without applying their deltas: ``` $ f-acat tip > A # tip version's manifest $ f-acat prev --raw > B # previous manifest in its raw fossil-deltified form $ f-acat prev > C # previous manifest fossil-undelta'd $ ls -la A B C -rw-rw-r-- 1 user user 80252 Mar 12 07:09 A # tip -rw-rw-r-- 1 user user 726 Mar 12 07:09 B # previous: delta'd -rw-rw-r-- 1 user user 80256 Mar 12 07:09 C # previous: undelta'd ``` For comparison's sake, when looking at a separate repository which uses delta manifests, a delta-compressed delta manifest takes up approximately the same space as a delta-compressed baseline manifest (to within 10 bytes for the test samples). i.e. delta manifests may not save any storage space except for the tip version! (*Surprise!*) In terms of RAM costs, deltas usually cost more memory than baseline manifests. The reason is because traversing a delta requires having not only that delta in memory, but also its baseline version. Delta manifests are seldom used in ways which do not require also loading their baselines. Thus Fossil internally requires two manifest objects for most operations with a delta manifest, whereas a baseline has but one. The difference in RAM cost is directly proportional to the size of the delta manifest. ## Manifests as Proof of Code Integrity Delta manifests have at least one more notable caveat, this one arguably more significant than an apparent lack of space savings: they're useless for purposes of publishing a manifest which downstream clients can use to verify the integrity of their copy of the software. Consider this use case: [the SQLite project](https://sqlite.org) publishes source code to many thousands of downstream consumers, many of whom would like to be able to verify that the copy they have downloaded is actually the copy published by the project. This is easily achieved by providing a copy of the downloaded version's manifest, as it contains a hash of every single file the project published and the manifest itself has a well-known hash and is cryptographically tamper-proof. It's mathematically extremely improbable for a malicious party to modify such a manifest and re-publish it as an "official" one, as the various hashes (F-cards, R-card, Z-card, *and* the hash of the manifest itself) would not line up. A collision-based attack would have to defeat *all four of those hashes*, which is practically impossible to do. Thus a Fossil checkin manifest can be used to provide strong assurances that a given copy of the software has not been tampered with since being exported by Fossil. *However*, that use case is *only possible with baseline manifests*. A delta manifest is *essentially useless* for that purpose. The algorithm for traversing F-cards of a delta manifest is not trivial for arbitrary clients to reproduce, e.g. using a shell script. While it *could* be done in any higher-level programming language (or some truly unsightly shell code), it would be an onerous burden on downstream consumers and would not be without risks of having bugs which invalidate the strong guarantees provided by the manifest. It's worth noting that the core Fossil project repository does not use delta manifests, at least in part for the same reason the SQLite project does not: the ability to provide a manifest which clients can easily use to verify the integrity of the code they've downloaded. The [`forbid-delta-manifests` config setting](/help/forbid-delta-manifests) is used to ensure that none are introduced into the repository beyond the few which were introduced solely for testing purposes. |
Deleted www/delta1.gif.
cannot compute difference between binary files
Deleted www/delta2.gif.
cannot compute difference between binary files
Deleted www/delta3.gif.
cannot compute difference between binary files
Deleted www/delta4.gif.
cannot compute difference between binary files
Deleted www/delta5.gif.
cannot compute difference between binary files
Deleted www/delta6.gif.
cannot compute difference between binary files
Changes to www/delta_encoder_algorithm.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 | <title>Fossil Delta Encoding Algorithm</title> <h2>Abstract</h2> <p>A key component for the efficient storage of multiple revisions of a file in fossil repositories is the use of delta-compression, i.e. to store only the changes between revisions instead of the whole file.</p> <p>This document describes the encoding algorithm used by Fossil to generate deltas. It is targeted at developers working on either <a href="index.wiki">fossil</a> itself, or on tools compatible with it. The exact format of the generated byte-sequences, while in general not necessary to understand encoder operation, can be found in the companion specification titled "<a href="delta_format.wiki">Fossil Delta Format</a>". </p> <p>The algorithm is inspired by <a href="http://samba.anu.edu.au/rsync/">rsync</a>.</p> <h2 id="argresparam">1.0 Arguments, Results, and Parameters</h2> <p>The encoder takes two byte-sequences as input, the "original", and the "target", and returns a single byte-sequence containing the "delta" which transforms the original into the target upon its application.</p> <p>Note that the data of a "byte-sequence" includes its length, i.e. the number of bytes contained in the sequence.</p> <p>The algorithm has one parameter named "NHASH", the size of the "sliding window" for the "rolling hash", in bytes. These two terms are explained in the next section. The value of this parameter has to be a power of two for the algorithm to work. For Fossil the value of this parameter is set to "16".</p> <h2 id="operation">2.0 Operation</h2> <p>The algorithm is split into three phases which generate the <a href="delta_format.wiki#header">header</a>, <a href="delta_format.wiki#slist">segment list</a>, and <a href="delta_format.wiki#trailer">trailer</a> of the delta, per its general <a href="delta_format.wiki#structure">structure</a>.</p> <p>The two phases generating header and trailer are not covered here as their implementation trivially follows directly from the specification of the <a href="delta_format.wiki">delta format</a>.</p> <p>This leaves the segment-list. Its generation is done in two phases, a pre-processing step operating on the "original" byte-sequence, followed by the processing of the "target" byte-sequence using the information gathered by the first step.</p> <h3 id="preprocessing">2.1 Preprocessing the original</h3> <p>A major part of the processing of the "target" is to find a range in the "original" which contains the same content as found at the current location in the "target".</p> <p>A naive approach to this would be to search the whole "original" for such content. This however is very inefficient as it would search |
︙ | ︙ | |||
82 83 84 85 86 87 88 | computed. </li> <li>A hash table is filled, mapping from the hashes of the chunks to the list of chunk locations having this hash. </li> </ol> | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | computed. </li> <li>A hash table is filled, mapping from the hashes of the chunks to the list of chunk locations having this hash. </li> </ol> <h3 id="processing">2.2 Processing the target</h3> <p>This, the main phase of the encoder, processes the target in a loop from beginning to end. The state of the encoder is captured by two locations, the "base" and the "slide". "base" points to the first byte of the target for which no delta output has been generated yet, and "slide" is the location of the window used to look in the "origin" for commonalities. This window is NHASH bytes long.</p> |
︙ | ︙ | |||
106 107 108 109 110 111 112 | to <a href="delta_format.wiki#copyrange">copy a range</a>, or </li> <li>move the window forward one byte. </li> </ul> </p> | > > > > > > > > > > > > | > > > > > > > > > > > > > > | 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 | to <a href="delta_format.wiki#copyrange">copy a range</a>, or </li> <li>move the window forward one byte. </li> </ul> </p> <verbatim type="pikchr float-right"> TARGET: [ scale = 0.8 down "Target" bold box fill palegreen width 150% height 200% "Processed" GI: box same as first box fill yellow height 25% "Gap → Insert" CC: box same fill orange height 200% "Common → Copy" W: box same as GI fill lightgray width 125% height 200% "Window" bold box same as CC height 125% "" box same fill white "" ] [ "Base" bold ; right ; arrow 33% ] with .e at TARGET.GI.nw [ "Slide" bold ; right ; arrow 33% ] with .e at TARGET.W.nw ORIGIN: [ down "Origin" bold B1: box fill white B2: box fill orange height 200% B3: box fill white height 200% ] with .nw at 0.75 right of TARGET.ne arrow from TARGET.W.e to ORIGIN.B2.w "Signature" aligned above </verbatim> <p>To make this decision the encoder first computes the hash value for the NHASH bytes in the window and then looks at all the locations in the "origin" which have the same signature. This part uses the hash table created by the pre-processing step to efficiently find these locations.</p> <p>For each of the possible candidates the encoder finds the maximal |
︙ | ︙ | |||
152 153 154 155 156 157 158 | needed more bytes to encode the range than there were bytes in the range, then no instructions are emitted and the window is moved one byte forward. The "base" is left unchanged in that case.</p> <p>The processing loop stops at one of two conditions: <ol> <li>The encoder decided to move the window forward, but the end of the | | | | | < | | | < | < | | | < | 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 | needed more bytes to encode the range than there were bytes in the range, then no instructions are emitted and the window is moved one byte forward. The "base" is left unchanged in that case.</p> <p>The processing loop stops at one of two conditions: <ol> <li>The encoder decided to move the window forward, but the end of the window reached the end of the "target". </li> <li>After the emission of instructions the new "base" location is within NHASH bytes of end of the "target", i.e. there are no more than at most NHASH bytes left. </li> </ol> </p> <p>If the processing loop left bytes unencoded, i.e. "base" not exactly at the end of the "target", as is possible for both end conditions, then one last insert instruction is emitted to put these bytes into the delta.<p> <h2 id="exceptions">3.0 Exceptions</h2> <p>If the "original" is at most NHASH bytes long no compression of changes is possible, and the segment-list of the delta consists of a single literal which contains the entire "target".</p> <p>This is actually equivalent to the second end condition of the processing loop described in the previous section, just checked before actually entering the loop.</p> <h2 id="rollhash">4.0 The rolling hash</h2> <p>The rolling hash described below and used to compute content signatures was chosen not only for good hashing properties, but also to enable the easy (incremental) recalculation of its value for a sliding window, i.e. where the oldest byte is removed from the window and a new byte is shifted in.<p> <h3 id="rhdef">4.1 Definition</h3> <p>Assuming an array Z of NHASH bytes (indexing starting at 0) the hash V is computed via</p> <div align="center"><img src="encode1.gif"></div> <div align="center"><img src="encode2.gif"></div> <div align="center"><img src="encode3.gif"></div> where A and B are unsigned 16-bit integers (hence the <u>mod</u>), and V is a 32-bit unsigned integer with B as MSB, A as LSB. <h3 id="rhincr">4.2 Incremental recalculation</h3> <p>Assuming an array Z of NHASH bytes (indexing starting at 0) with hash V (and components A and B), the dropped byte <img src="encode4.gif" align="center">, and the new byte <img src="encode5.gif" align="center"> , the new hash can be computed incrementally via: </p> <div align="center"><img src="encode6.gif"></div> <div align="center"><img src="encode7.gif"></div> <div align="center"><img src="encode8.gif"></div> <p>For A, the regular sum, it can be seen easily that this the correct way recomputing that component.</p> <p>For B, the weighted sum, note first that <img src="encode4.gif" align="center"> has the weight NHASH in the sum, so that is what has to be removed. Then adding in <img src="encode9.gif" align="center"> adds one weight factor to all the other values of Z, and at last adds in <img src="encode5.gif" align="center"> with weight 1, also generating the correct new sum</p> |
Changes to www/delta_format.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 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 | <title>Fossil Delta Format</title> <h1>1.0 Overview</h1> Fossil achieves efficient storage and low-bandwidth synchronization through the use of delta-compression. Instead of storing or transmitting the complete content of an artifact, Fossil stores or transmits only the changes relative to a related artifact. This document describes the delta-encoding format used by Fossil. The intended audience is developers working on either <a href="index.wiki">Fossil</a> itself, or on tools compatible with Fossil. Understanding of this document is <em>not</em> required for ordinary users of Fossil. This document is an implementation detail. This document only describes the delta file format. A [./delta_encoder_algorithm.wiki|separate document] describes one possible algorithm for generating deltas in this format. <h2>1.1 Sample Software And Analysis Tools</h2> The core routines used to generate and apply deltas in this format are contained in the [../src/delta.c|delta.c] source file. Interface logic, including "test-*" commands to directly manipulate deltas are contained in the [../src/deltacmd.c|deltacmd.c] source file. SQL functions to create, apply, and analyze deltas are implemented by code in the [../src/deltafunc.c|deltafunc.c] source file. The following command-line tools are available to create and apply deltas and to test the delta logic: * [/help?cmd=test-delta|fossil test-delta] → Run self-tests of the delta logic * [/help?cmd=test-delta-create|fossil test-delta-create X Y] → compute a delta that converts file X into file Y. Output that delta. * [/help?cmd=test-delta-apply|fossil test-delta-apply X D] → apply delta D to input file X and output the result. * [/help?cmd=test-delta-analyze|fossil test-delta-analyze X Y] → compute and delta that converts file X into file Y but instead of writing the delta to output, write performance information about the delta. When running the [/help?cmd=sqlite3|fossil sql] command to get an interactive SQL session connected to the repository, the following additional SQL functions are provided: * <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> → Compute a data that carries blob X into blob Y and return that delta as a blob. * <b>delta_apply(</b><i>X</i><b>,</b><i>D</i><b>)</b> → Apply delta D to input blob X return a new blob which is the result. * <b>delta_output_size(</b><i>D</i>)</b> → Return the size of the output that would result from applying delta D. * <b>delta_parse(</b><i>D</i>)</b> → This is a table-valued function that returns one row for the header, for the trailer, and for each segment in delta D. <h1 id="structure">2.0 Structure</h1> <verbatim type="pikchr"> leftmargin = 0.1 box height 50% "Header" box same "Segments" box same "Trailer" </verbatim> A delta consists of three parts, a "header", a "trailer", and a "segment-list" between them. Both header and trailer provide information about the target helping the decoder, and the segment-list describes how the target can be constructed from the original. <h2 id="header">2.1 Header</h2> <verbatim type="pikchr"> leftmargin = 0.1 box height 50% "Size" box same "\"\\n\"" </verbatim> The header consists of a single number followed by a newline character (ASCII 0x0a). The number is the length of the target in bytes. This means that, given a delta, the decoder can compute the size of the target (and allocate any necessary memory based on that) by simply reading the first line of the delta and decoding the number found there. In other words, before it has to decode everything else. <h2 id="trailer">2.2 Trailer</h2> <verbatim type="pikchr"> leftmargin = 0.1 box height 50% "Checksum" box same "\";\"" </verbatim> The trailer consists of a single number followed by a semicolon (ASCII 0x3b). This number is a checksum of the target and can be used by a decoder to verify that the delta applied correctly, reconstructing the target from the original. The checksum is computed by treating the target as a series of 32-bit integer numbers (MSB first), and summing these up, modulo 2^32-1. A target whose length is not a multiple of 4 is padded with 0-bytes (ASCII 0x00) at the end. By putting this information at the end of the delta a decoder has it available immediately after the target has been reconstructed fully. <h2 id="slist">2.3 Segment-List</h2> <verbatim type="pikchr"> leftmargin = 0.1 PART1: [ B1: box height 50% width 15% "" B2: box same "" B3: box same "" "***" box height 50% width 15% "" I1: line down 50% from B2 .s arrow right until even with B3.e box "Insert Literal" height 50% line down 75% from I1 .s arrow right until even with B3.e box "Copy Range" height 50% ] down PART2: [ "" box "Length" height 50% right box "\":\"" same box "Bytes" same ] with .nw at previous.sw </verbatim> 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. 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. <h3 id="insertlit">2.3.1 Insert Literal</h3> A literal is specified by two elements, the size of the literal in bytes, and the bytes of the literal itself. <verbatim type="pikchr"> leftmargin = 0.1 box "Length" height 50% box "\":\"" same box "Bytes" same </verbatim> The length is written first, followed by a colon character (ASCII 0x3a), followed by the bytes of the literal. <h3 id="copyrange">2.3.2 Copy Range</h3> A range to copy is specified by two numbers, the offset of the first byte in the original to copy, and the size of the range, in bytes. The size zero is special, its usage indicates that the range extends to the end of the original. <verbatim type="pikchr"> leftmargin = 0.1 box "Length" height 50% box "\"@\"" same box "Offset" same box "\",\"" same </verbatim> The length is written first, followed by an "at" character (ASCII 0x40), then the offset, followed by a comma (ASCII 0x2c). <h1 id="intcoding">3.0 Encoding of integers</h1> The format currently handles only 32 bit integer numbers. They are written base-64 encoded, MSB first, and without leading "0"-characters, except if they are significant (i.e. 0 => "0"). The base-64 encoding uses one character for each 6 bits of the integer to be encoded. The encoding characters are: <pre> 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ </pre> The least significant 6 bits of the integer are encoded by the first character, followed by the next 6 bits, and so on until all non-zero bits of the integer are encoded. The minimum number of encoding characters is used. Note that for integers less than 10, the base-64 coding is a ASCII decimal rendering of the number itself. <h1 id="examples">4.0 Examples</h1> <h2 id="examplesint">4.1 Integer encoding</h2> <table> <tr> <th>Value</th> <th>Encoding</th> </tr> <tr> <td>0</td> <td>0</td> </tr> <tr> <td>6246</td> <td>1Xb</td> </tr> <tr> <td>-1101438770</td> <td>2zMM3E</td> </tr> </table> <h2 id="examplesdelta">4.2 Delta encoding</h2> An example of a delta using the specified encoding is: <pre> 1Xb 4E@0,2:thFN@4C,6:scenda1B@Jd,6:scenda5x@Kt,6:pieces79@Qt,F: Example: eskil~E@Y0,2zMM3E;</pre> </pre> This can be taken apart into the following parts: <table> <tr><th>What </th> <th>Encoding </th><th>Meaning </th><th>Details</th></tr> <tr><td>Header</td> <td>1Xb </td><td>Size </td><td> 6246 </td></tr> <tr><td>S-List</td> <td>4E@0, </td><td>Copy </td><td> 270 @ 0 </td></tr> <tr><td> </td> <td>2:th </td><td>Literal </td><td> 2 'th' </td></tr> <tr><td> </td> <td>FN@4C, </td><td>Copy </td><td> 983 @ 268 </td></tr> <tr><td> </td> <td>6:scenda </td><td>Literal </td><td> 6 'scenda' </td></tr> <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> The unified diff behind the above delta is <verbatim> bluepeak:(761) ~/Projects/Tcl/Fossil/Devel/devel > diff -u ../DELTA/old ../DELTA/new --- ../DELTA/old 2007-08-23 21:14:40.000000000 -0700 +++ ../DELTA/new 2007-08-23 21:14:33.000000000 -0700 @@ -5,7 +5,7 @@ * If the server does not have write permission on the database file, or on the directory containing the database file (and - it is thus unable to update database because it cannot create + it is thus unable to update the database because it cannot create a rollback journal) then it currently fails silently on a push. It needs to return a helpful error. @@ -27,8 +27,8 @@ * Additional information displayed for the "vinfo" page: + All leaves of this version that are not included in the - descendant list. With date, user, comment, and hyperlink. - Leaves in the descendant table should be marked as such. + descendant list. With date, user, comment, and hyperlink. + Leaves in the descendant table should be marked as such. See the compute_leaves() function to see how to find all leaves. + Add file diff links to the file change list. @@ -37,7 +37,7 @@ * The /xfer handler (for push, pull, and clone) does not do delta compression. This results in excess bandwidth usage. - There are some code in xfer.c that are sketches of ideas on + There are some pieces in xfer.c that are sketches of ideas on how to do delta compression, but nothing has been implemented. * Enhancements to the diff and tkdiff commands in the cli. @@ -45,7 +45,7 @@ single file. Allow diffs against any two arbitrary versions, not just diffs against the current check-out. Allow configuration options to replace tkdiff with some other - visual differ of the users choice. + visual differ of the users choice. Example: eskil. * Ticketing interface (expand this bullet) </verbatim> <h1 id="notes">Notes</h1> <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 | <title>Project Documentation</title> 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. |
︙ | ︙ | |||
22 23 24 25 26 27 28 | 3. Only people with check-in privileges can modify the documentation. (This might be either an advantage or disadvantage, depending on the nature of your project.) We will call documentation that is included as files in the source tree "embedded documentation". | | | | | < | | > | | | > | | | < | | | | > | | > > > > > > > > > > > > > > > > > | | > | | | | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | < | < | < | | 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 | 3. Only people with check-in privileges can modify the documentation. (This might be either an advantage or disadvantage, depending on the nature of your project.) We will call documentation that is included as files in the source tree "embedded documentation". <h1>1.0 Fossil Support For Embedded Documentation</h1> The fossil web interface supports embedded documentation using the "/doc" page. To access embedded documentation, one points a web browser to a fossil URL of the following form: <pre> <i><baseurl></i><big><b>/doc/</b></big><i><version></i><big><b>/</b></big><i><filename></i> </pre> The <i><baseurl></i> is the main URL used to access the fossil web server. For example, the <i><baseurl></i> for the fossil project itself is [https://fossil-scm.org/home]. If you launch the web server using the "[/help?cmd=ui|fossil ui]" command line, then the <i><baseurl></i> is usually <b>http://localhost:8080/</b>. The <i><version></i> is the [./checkin_names.wiki|name of a check-in] that contains the embedded document. This might be a hash prefix for the check-in, or it might be the name of a branch or tag, or it might be a timestamp. See the prior link for more possibilities and examples. The <i id="ckout"><version></i> can also be the special identifier "<b>ckout</b>". The "<b>ckout</b>" keywords means to pull the documentation file from the local source tree on disk, not from the any check-in. The "<b>ckout</b>" keyword only works when you start your server using the "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]" commands. The "/doc/ckout" URL is intended to show a preview of the documentation you are currently editing but have not yet checked in. The original designed purpose of the "ckout" feature is to allow the user to preview local changes to documentation before committing the change. This is an important facility, since unlike other document languages like HTML, there is still a lot of variation among rendering engines for Fossil's markup languages, Markdown and Fossil Wiki. Your changes may look fine in your whizzy GUI Markdown editor, but what actually matters is how <i>Fossil</i> will interpret your Markdown text. Therefore, you should always preview your edits before committing them. To help make "ckout" easier to use, the "[/help?cmd=ui|fossil ui]" command has the "--ckout-alias NAME" option that makes NAME an alias for "ckout". If you are editing a collection of documents that have hardcoded links to one another in the form of "/doc/trunk/...", for example, you can test your changes by running "fossil ui --ckout-alias trunk" and all of the links will point to your uncommitted edits rather than to the latest trunk check-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 | fossil wiki markup] - 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 language]. 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. <h2 id="html">1.1 HTML Rendering With Fossil Headers And Footers</h2> 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 Fossil Documentation Index www/permutedindex.html: * [/file/www/permutedindex.html?txt|source text for <b>www/permutedindex.html</b>] * [/doc/trunk/www/permutedindex.html|<b>www/permutedindex.html</b> rendered as HTML] Beware that such HTML files render in the same browser security context as all other embedded documentation served from Fossil; they are not fully-independent web pages. One practical consequence of this is that embedded <tt><script></tt> tags will cause a [https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP | Content Security Policy] error in your browser with the default CSP as served by Fossil. See the documentation on [./customskin.md#headfoot | Header and Footer Processing] and [./defcsp.md | The Default CSP]. <h1>2.0 Server-Side Text Substitution</h1> Fossil can do a few types of substitution of server-side information into the embedded document. <h2>2.1 "$ROOT" In HTML and Markdown Hyperlinks</h2> Hyperlinks in Markdown and HTML embedded documents can reference the root of the Fossil repository using the special text "$ROOT" at the beginning of a URL. For example, a Markdown hyperlink to the Markdown formatting rules might be written in the embedded document like this: <verbatim> [Markdown formatting rules]($ROOT/wiki_rules) </verbatim> Depending on how the how the Fossil server is configured, that hyperlink might be renderer like one of the following: <verbatim> <a href="/wiki_rules">Wiki formatting rule</a> <a href="/cgi-bin/fossil/wiki_rules">Wiki formatting rules</a> </verbatim> So, in other words, the "$ROOT" text is converted into whatever the "<baseurl>" is for the document. This substitution works for HTML and Markdown documents. It does not work for Wiki embedded documents, since with Wiki you can just begin a URL with "/" and it automatically knows to prepend the $ROOT. <h2>2.2 "$CURRENT" In "/doc/" Hyperlinks</h2> Similarly, URLs of the form "/doc/$CURRENT/..." have the check-in hash of the check-in currently being viewed substituted in place of the "$CURRENT" text. This feature, in combination with the "$ROOT" substitution above, allows an absolute path to be used for hyperlinks. For example, if an embedded document documented wanted to reference some other document in a separate file named "www/otherdoc.md", it could use a URL like this: <verbatim> [Other Document]($ROOT/doc/$CURRENT/www/otherdoc.md) </verbatim> As with "$ROOT", this substitution only works for Markdown and HTML documents. For Wiki documents, you would need to use a relative URL. <h2 id="th1">2.3 TH1 Documents</h2> Fossil will substitute the value of [./th1.md | TH1 expressions] within <tt>{</tt> curly braces <tt>}</tt> into the output HTML if you have configured it with the <tt>--with-th1-docs</tt> option, which is disabled by default. Since TH1 is a full scripting language, this feature essential grants the ability to execute code on the server to anyone with check-in privilege for the project. This is a security risk that needs to be carefully managed. The feature is off by default. Administrators should understand and carefully assess the risks before enabling the use of TH1 within embedded documentation. <h1>3.0 Examples</h1> 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 file using the URL: <pre>[https://fossil-scm.org/home/doc/trunk/www/embeddeddoc.wiki]</pre> The first part of this path, the "[https://fossil-scm.org/home]", is the base URL. You might have originally typed: [https://fossil-scm.org/]. The web server at the fossil-scm.org site automatically redirects such links by appending "home". The "home" file on fossil-scm.org is really a [./server/any/cgi.md|CGI script] which runs the fossil web service in CGI mode. The "home" CGI script looks like this: <pre> #!/usr/bin/fossil repository: /fossil/fossil.fossil </pre> This is one of the many ways to set up a <a href="./server/">Fossil server</a>. The "<b>/trunk/</b>" part of the URL tells fossil to use the documentation files from the most recent trunk check-in. If you wanted to see an historical version of this document, you could substitute the name of a check-in for "<b>/trunk/</b>". For example, to see the version of this document associated with check-in [9be1b00392], simply replace the "<b>/trunk/</b>" with "<b>/9be1b00392/</b>". You can also substitute the symbolic name for a particular version or branch. For example, you might replace "<b>/trunk/</b>" with "<b>/experimental/</b>" to get the latest version of this document in the "experimental" branch. The symbolic name can also be a date and time string in any of the following formats:</p> <ul> <li> <i>YYYY-MM-DD</i> <li> <i>YYYY-MM-DD<b>T</b>HH:MM</i> <li> <i>YYYY-MM-DD<b>T</b>HH:MM:SS</i> </ul> When the symbolic name is a date and time, fossil shows the version of the document that was most recently checked in as of the date and time specified. So, for example, to see what the fossil website looked like at the beginning of 2010, enter: <pre><a href="/doc/2010-01-01/www/index.wiki">https://fossil-scm.org/home/doc/<b>2010-01-01</b>/www/index.wiki </a></pre> The file that encodes this document is stored in the fossil source tree under the name "<b>www/embeddeddoc.wiki</b>" and so that name forms the last part of the URL for this document. As I sit writing this documentation file, I am testing my work by running the "<b>fossil ui</b>" command line and viewing <b>http://localhost:8080/doc/ckout/www/embeddeddoc.wiki</b> in Firefox. I am doing this even though I have not yet checked in the "<b>www/embeddeddoc.wiki</b>" file for the first time. Using the special "<b>ckout</b>" version identifier on the "<b>/doc</b>" page it is easy to make multiple changes to multiple files and see how they all look together before committing anything to the repository. |
Deleted www/encode10.gif.
cannot compute difference between binary files
Added www/encryptedrepos.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 | <title>How To Use Encrypted Repositories</title> <h2>Introduction</h2> Fossil can be compiled so that it works with encrypted repositories using the [https://www.sqlite.org/see/doc/trunk/www/readme.wiki|SQLite Encryption Extension]. This technical note explains the process. <h2>Building An Encryption-Enabled Fossil</h2> The SQLite Encryption Extension (SEE) is proprietary software and requires [https://sqlite.org/purchase/see|purchasing a license]. Assuming you have an SEE license, the first step of compiling Fossil to use SEE is to create an SEE-enabled version of the SQLite database source code. This alternative SQLite database source file should be called "sqlite3-see.c" and should be placed in the extsrc/ subfolder of the Fossil sources, right beside the public-domain "sqlite3.c" source file. Also make a copy of the SEE-enabled "shell.c" file, renamed as "shell-see.c", and place it in the extsrc/ subfolder beside the original "shell.c". Add the --with-see command-line option to the configuration script to enable the use of SEE on unix-like systems. <pre> ./configure --with-see; make </pre> To build for Windows using MSVC, add the "USE_SEE=1" argument to the "nmake" command line. <pre> nmake -f makefile.msc USE_SEE=1 </pre> <h2>Using Encrypted Repositories</h2> Any Fossil repositories whose filename ends with ".efossil" is taken to be an encrypted repository. Fossil will prompt for the encryption password and attempt to open the repository database using that password. Every invocation of fossil on an encrypted repository requires retyping the encryption password. To avoid excess password typing, consider using the "fossil shell" command which prompts for the password just once, then reuses it for each subsequent Fossil command entered at the prompt. On Windows, the "fossil server", "fossil ui", and "fossil shell" commands do not (currently) work on an encrypted repository. <h2>Additional Security</h2> Use the FOSSIL_SECURITY_LEVEL environment for additional protection. <pre> export FOSSIL_SECURITY_LEVEL=1 </pre> A setting of 1 or greater prevents fossil from trying to remember the previous sync password. <pre> export FOSSIL_SECURITY_LEVEL=2 </pre> A setting of 2 or greater causes all password prompts to be preceded by a random translation matrix similar to the following: <pre> abcde fghij klmno pqrst uvwyz qresw gjymu dpcoa fhkzv inlbt </pre> When entering the password, the user must substitute the letter on the second line that corresponds to the letter on the first line. Uppercase substitutes for uppercase inputs, and lowercase substitutes for lowercase inputs. Letters that are not in the translation matrix (digits, punctuation, and "x") are not modified. For example, given the translation matrix above, if the password is "pilot-9crazy-xube", then the user must type "fmpav-9ekqtb-xirw". This simple substitution cypher helps prevent password capture by keyloggers. |
Added www/env-opts.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 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 | Environment Variables and Global Options ======================================== Fossil uses a number of environment variables and supports a number of global options. Most of these seem to be primarily documented in the source code, with the primary exception of `--args` which is described in the usage text printed by running fossil with no arguments at all. Global Options -------------- The following options are understood by the fossil command itself, and are collected before any subcommand begins processing. `--args FILENAME`: Read the file `FILENAME` and replace these two arguments with its content. Each line of the file is assumed to be an argument unless it starts with '-' and contains a space, in which case it is assumed to be another flag and is treated as such. `--args FILENAME` may be used in conjunction with any other flags. `--case-sensitive BOOL`: Override the `case-sensitive` setting, which can override the native preferences of the platform for case sensitive file names: insensitive on Windows, sensitive on Unix. There are probably odd interactions possible if you mix case sensitive and case insensitive file systems on any single platform. This option or the global setting should be used to force the case sensitivity to the most sensible condition. `--cgitrace`: Active CGI tracing. `--chdir DIRECTORY`: Change to the named directory before processing any commands. `--comfmtflags NUMBER`: Specify flags that control how check-in comments and certain other text outputs are formatted for display. The flags are individual bits in `NUMBER`, which must be specified in base 10: * _0_ — Uses the revised algorithm with no special handling. * _1_ — Uses the legacy algorithm, other flags are ignored. * _2_ — 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). * _4_ — 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). * _8_ — Attempts to break lines on word boundaries while honoring the logical line length. * _16_ — 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. `--comment-format NUMBER`: Alias for `--comfmtflags NUMBER`. `--errorlog ERRLOG`: Name a file to which fossil will log panics, errors, and warnings. `--help`: If `--help` is found anywhere on the command line, translate the command to `fossil help cmdname` where `cmdname` is the first argument that does not begin with a `-` character. If all arguments start with `-`, translate to `fossil help argv[1] argv[2]...`. `--httptrace`: (Sets `g.fHttpTrace`.) Trace outbound HTTP requests. `--localtime`: Override the `timeline-utc` option to explicitly use local time. `--nocgi`: Prevent fossil from acting as a CGI by default even if the `GATEWAY_INTERFACE` environment variable is set. `--no-th-hook`: (Sets `g.fNoThHook`.) Override the `th1-hooks` setting and prevent any TH1 hooks from being executed. `--quiet`: (Sets `g.fQuiet`.) Cause fossil to suppress various messages and progress indicators that would otherwise be printed. `--sqltrace`: (Sets `g.SqlTrace`.) Implies `--sqlstats`. Trace certain SQLite database activity, especially showing every SQL query processed. `--sqlstats`: (Sets `g.fSqlStats`.) Print a number of performance statistics about each SQLite database used when it is closed. `--sshtrace`: (Sets `g.fSshTrace`.) `--ssl-identity`: The fully qualified name of the file containing the client certificate and private key to use, in PEM format. It can be created by concatenating the client certificate and private key files. This identity will be presented to SSL servers to authenticate the client, in addition to the normal password authentication. `--systemtrace`: (Sets `g.fSystemTrace`.) Trace all commands launched as sub processes. `--user LOGIN`: (Sets `g.zLogin`) Also `-U LOGIN`. Set the user name used with the repository. `--utc`: Override the `timeline-utc` option to explicitly use UTC time. `--vfs VFSNAME`: Load the named VFS into SQLite. Environment Variables --------------------- The location of the user's account-wide [configuration database][configdb] depends on the operating system and on the existence of various environment variables and/or files. See the discussion of the [configuration database location algorithm][configloc] for details. `EDITOR`: Name the editor to use for check-in and stash comments. Overridden by the local or global `editor` setting or the `VISUAL` environment variable. `FOSSIL_BREAK`: If set, an opportunity will be created to attach a debugger to the Fossil process prior to any significant work being performed. `FOSSIL_FORCE_TICKET_MODERATION`: If 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. `FOSSIL_FORCE_WIKI_MODERATION`: If 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. `FOSSIL_HOME`: Location of [configuration database][configdb]. See the [configuration database location][configloc] description for additional information. `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for SEE as text to be hashed into the actual encryption key. This has no effect if Fossil was not compiled with SEE support enabled. `FOSSIL_USER`: Name of the default user account if the checkout, local or global `default-user` setting is not present. The first environment variable found in the environment from the list `FOSSIL_USER`, `USER`, `LOGNAME`, and `USERNAME` is the user name. If none of those are set, then the default user name is "root". See the discussion of Fossil Username below for a lot more detail. `FOSSIL_SECURITY_LEVEL`: If set to any of the values listed below, additional measures for password security will be enabled (also see [How To Use Encrypted Repositories][encryptedrepos.wiki]): [encryptedrepos.wiki]: /doc/trunk/www/encryptedrepos.wiki * _≥1_ — Do not remember passwords. * _≥2_ — Use a scrambled matrix for password input. `FOSSIL_TCL_PATH`: When Tcl stubs support is configured, point to a specific file or folder containing the version of Tcl to load at run time. `FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT`: When set to the literal value `YES_DO_IT`, the test suite will relax the constraint that some tests may not run within an open checkout. This is subject to removal in the future. `FOSSIL_VFS`: Name a VFS to load into SQLite. `GATEWAY_INTERFACE`: If present and the `--nocgi` option is not, assume fossil is invoked from a web server as a CGI command, and act accordingly. `HOME`: Potential location of the [configuration database][configdb]. See the [configuration database location][configloc] description for details. `HOMEDRIVE`, `HOMEPATH`: (Windows) Location of the `~/.fossil` file. The first environment variable found in the environment from the list `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is used as the location of the `~/.fossil` file. `HTTP_HOST`: If defined, included in error log messages. `http_proxy`: If the global or local settings `proxy` is not set, this is used as the default value for the `proxy` setting. `HTTP_USER_AGENT`: If defined, included in error log messages. `LOCALAPPDATA`: (Windows) Location of the `~/.fossil` file. The first environment variable found in the environment from the list `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is used as the location of the `~/.fossil` file. `LOGNAME`: Name of the logged in user on many Unix-like platforms. Used as the fossil user name if `FOSSIL_USER` is not specified. See the discussion of Fossil Username below for a lot more detail. `PATH`: Used by most platforms to locate programs invoked without a fully qualified name. Explicitly used by `fossil ui` on certain platforms to choose the browser to launch. `PATH_INFO`: If defined, included in error log messages. `QUERY_STRING`: If defined, included in error log messages. `REMOTE_ADDR`: If defined, included in error log messages. `REMOTE_HOST`: Used by `fossil http` run from `stunnel` to identify the remote host. `REQUEST_METHOD`: If defined, included in error log messages. `REQUEST_URI`: If defined, included in error log messages. `SCRIPT_NAME`: If defined, included in error log messages. `SSH_CONNECTION`: Informs CGI processing if the remote client is SSH. `SSL_CERT_FILE`, `SSL_CERT_DIR`: Override the [`ssl-ca-location`] (/help?cmd=ssl-ca-location) setting. `SQLITE_FORCE_PROXY_LOCKING`: From `sqlite3.c`, 1 means force always use proxy, 0 means never use proxy, and undefined means use proxy for non-local files only. `SQLITE_TMPDIR`: Names the temporary file location for SQLite. When set, this will be used instead of `TMPDIR`. `SYSTEMROOT`: (Windows) Used to locate `notepad.exe` as a fall back comment editor. `TERM`: If the linenoise library is used (almost certainly not on Windows), it will check `TERM` to verify that the interactive terminal is not named on a short list on terminals known to not work with linenoise. Linenoise is a library that provides command history and command line editing to interactive programs, and can be used in the `fossil sqlite3` command. `TH1_DELETE_INTERP`: Set this variable to ask fossil to explicitly delete the TH1 interpreter, if it is loaded, then check that it released all of its allocated memory, when exiting fossil. This is not strictly necessary, but makes debugging memory leaks easier. See [main.c near line 386](/artifact/e75796be5338a81c?ln=386,391) for the code. `TH1_ENABLE_DOCS`: Override the local or global setting `tcl-docs` to enable TH1 documents in fossil. `TH1_ENABLE_HOOKS`: Override the local or global setting `tcl-hooks` to enable TH1 hooks in fossil. `TH1_ENABLE_TCL`: Override the local or global setting `tcl` to enable Tcl in fossil. `TH1_TEST_ANON_CAPS`: Override the default anonymous permissions used when processing the `--set-anon-caps` option for the `test-th-eval`, `test-th-render`, and `test-th-source` test commands. `TH1_TEST_USER_CAPS`: Override the default user permissions used when processing the `--set-user-caps` option for the `test-th-eval`, `test-th-render`, and `test-th-source` test commands. `TMPDIR`: Names the temporary file location for SQLite. `USER`: Name of the logged in user on many Unix-like platforms. Used as the fossil user name if `FOSSIL_USER` is not specified. See the discussion of Fossil Username below for a lot more detail. `USERNAME`: Name of the logged in user on Windows platforms. Used as the fossil user name if `FOSSIL_USER` is not specified. See the discussion of Fossil Username below for a lot more detail. `VISUAL`: Name the editor to use for check-in and stash comments. Overrides the `EDITOR` environment variable. Overridden by the local or global `editor` setting. Notes on Related Values ----------------------- ### CGI and JSON Parameters The JSON API implementation looks up many values in the first of several places searched. This unifies the parameter handling logic, allows the caller to choose whether to prefer URL parameters, request headers, or the POST payload, and allows the `fossil json` command to share most of the same logic as the `/json` API path. The search order is a POST payload, GET/COOKIE/non-JSON POST, JSON POST, the system environment. See the comment above the implementation of [`json_getenv`][json.c] for some further discussion. [json.c]: /artifact/6df1d80dece8968b?ln=277,290 ### Comment Editor The editor used to edit a check-in or stash comment is named by the local or global setting `editor`. If neither is set, then the environment variables `VISUAL`, and `EDITOR` are checked in that order. On Windows, if no editor is named, then Notepad is used. Note that the operation will be aborted if `notepad.exe` is not found in the Windows folder. On Unix-like platforms, if no editor is named, then a message is displayed on stdout, and stdin is read until a single line containing only a dot is seen. ### Error logging If logging errors to a file, fossil will include the values of the following environment variables in the error log entry if they are defined: `HTTP_HOST`, `HTTP_USER_AGENT`, `PATH_INFO`, `QUERY_STRING`, `REMOTE_ADDR`, `REQUEST_METHOD`, `REQUEST_URI`, and `SCRIPT_NAME`. ### Fossil Username In absence of any explicit setting, fossil will use the same name you logged in to your platform with, as the user name when interacting with local and remote repositories. Note that only the name is shared, fossil makes no attempt to share or leverage any platform's authentication mechanisms or passwords. When logging in to a repository, it tries a series of sources for the user name, and the first non-blank name that succeeds is the logged in user. The order is: 1. The --user and -U command-line options. 2. If running within an open checkout (the local database is open), check in its table of values stored per open checkout for the value stored by `fossil user default USERNAME`. 3. The default user in the repository (setting `default-user`) 4. The `FOSSIL_USER` environment variable. 5. The `USER` environment variable. 6. The `LOGNAME` environment variable. 7. The `USERNAME` environment variable. 8. Check if the user can be extracted from the remote URL, if there is a remote URL. Items 2 and 3 are both set by `fossil user default USERNAME`, the first within an open checkout, the second outside and using the `-R REPOSITORY` option to identify the repository. Both cases require that the named user be present in the repository when the default user is assigned. Although the default user is internally stored as if it were a setting named `default-user`, it is not accessible through the `fossil set` command. Items 5, 6, and 7 cover most of the names of an environment variable set automatically by the platform with the name of the platform's logged in user for use by programs. Historically, `USER` comes from Unix System-V, `LOGNAME` from BSD, and `USERNAME` from Windows, but many Linux distributions will set both `USER` and `LOGNAME` for broad compatibility. When creating a new repository, fossil needs a user name for the admin user granted the "s" permission. But since fossil generally expects that `fossil new` or `fossil clone` are used outside of any checkout (especially when run for the first time without any checkouts at all or the users's global settings database), it looks in a shorter list of places for a non-blank name. In the special case of a clone, `default-user` can be copied from the original, and so it can be set in the clone even before any users have been created, and in that case it will be the new admin user. If `default-user` is not set, then the first found environment variable from the list `FOSSIL_USER`, `USER`, `LOGNAME`, and `USERNAME`, is the user name. As a final fallback, if none of those are set, then the default user name is "root". ### Configuration Database Location Fossil keeps some information pertinent to each user in the user's [configuration database file][configdb]. The configuration database file includes the global settings and the list of repositories and checkouts used by `fossil all`. The location of the configuration database file depends on the operating system and on the existence of various environment variables and/or files. In brief, the configuration database is usually: * Traditional unix → "`$HOME/.fossil`" * Windows → "`%LOCALAPPDATA%/_fossil`" * [XDG-unix][xdg] → "`$HOME/.config/fossil.db`" [xdg]: https://www.freedesktop.org/wiki/ See the [configuration database location algorithm][configloc] discussion for full information. ### SQLite VFS to use See [the SQLite documentation](http://www.sqlite.org/vfs.html) for an explanation of what a VFS actually is and what it does. If the default VFS underneath SQLite is not suitable, an alternative can be selected with either the `--vfs VFSNAME` option or the `FOSSIL_VFS` environment variable. The `--vfs` option takes precedence. ### <a id="temp"></a>Temporary File Location Fossil places some temporary files in the checkout directory. Most notably, supporting files related to merge conflicts are placed in the same folder as the merge result. Other temporary files need a different home. The rules for choosing one are complicated. Fossil-specific code uses `FOSSIL_TEMP`, `TEMP`, and `TMP`, in that order. Fossil’s own test suite prepends `FOSSIL_TEST_TEMP` to that list. The underlying SQLite code uses several different path sets for its temp files, depending on the platform type. On Unix-like platforms, excepting Cygwin, SQLite first checks the environment variables `SQLITE_TMPDIR` and `TMPDIR`, in that order. If neither is defined, it falls back to a hard-coded list of paths: `/var/tmp`, `/usr/tmp`, and `/tmp`. If all of that fails, it uses the current working directory. For Cygwin builds, SQLite instead uses the first defined variable in this list: `SQLITE_TMPDIR`, `TMPDIR`, `TMP`, `TEMP`, and `USERPROFILE`. For native Windows builds, SQLite simply calls the OS’s [`GetTempPath()` API][gtp]. See that reference page for details. [gtp]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx That said, it is not unusual for utilities on all platforms to assume that `TEMP` or `TMP` point somewhere safe for temporary files. If the identified temporary folder is not writable, then weird things will happen on all platforms. ### Web browser Occasionally, fossil wants to launch a web browser for the user, most obviously as part of the `fossil ui` command. In that specific case, the browser is launched pointing at the web server started by `fossil ui` listening on a private TCP port. On all platforms, if the local or global settings `web-browser` is set, that is the command used to open an URL. Otherwise, the specific actions vary by platform. On Unix-like platforms other than Apple's, it looks for the first program from the list `xdg-open`, `gnome-open`, `firefox`, and `google-chrome` that it can find on the `PATH`. On Apple platforms, it assumes that `open` is the command to open an URL in the user's configured default browser. On Windows platforms, it assumes that `start` is the command to open an URL in the user's configured default browser. [configdb]: ./tech_overview.wiki#configdb [configloc]: ./tech_overview.wiki#configloc |
Added 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 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 | <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 rationale 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. The technote can be added from a script. * <b>News Articles</b>. Significant occurrences in the life cycle 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. * <b>Signed Checksums</b>. Technotes containing cryptographically signed checksums can be linked to repository artifacts, thereby creating a traceable, auditable chain so that users can readily verify the integrity and authenticity of project deliverables. And the command line interface to technotes enables embedding such processes in scripts. 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, Editing and Viewing 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. Technotes can also be created using the <b>wiki create</b> command: <verbatim> fossil wiki create TestTechnote -t now --technote-bgcolor lightgreen technote.md Created new tech note 2021-03-15 13:05:56 </verbatim> This command inserts a light green technote in the timeline at 2021-03-15 13:05:56, with the contents of file <b>technote.md</b> and comment "TestTechnote". Specifying a different time using <b>-t DATETIME</b> will insert the technote at the specified timestamp location in the timeline. Different technotes can have the same timestamp. The first argument to create, <b>TECHNOTE-COMMENT</b>, is the title text for the technote that appears in the timeline. To view all technotes, use the <b>wiki ls</b> command: <verbatim> fossil wiki ls --technote --show-technote-ids z739263a134bf0da1d28e939f4c4367f51ef4c51 2020-12-19 13:20:19 e15a918a8bed71c2ac091d74dc397b8d3340d5e1 2018-09-22 17:40:10 </verbatim> A technote ID is the UUID of the technote. To view an individual technote, use the <b>wiki export</b> command: <verbatim> fossil wiki export --technote version-2.16 Release Notes 2021-07-02 This note describes changes in the Fossil snapshot for ... </verbatim> The <b>-t|--technote</b> option to the <b>export</b> subcommand takes one of three identifiers: <b>DATETIME</b>; <b>TECHNOTE-ID</b>; and <b>TAG</b>. See the [/help?cmd=wiki | wiki help] for specifics. 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 | #!/usr/bin/tclsh # # Run this to generate the FAQ # set cnt 1 proc faq {question answer} { set ::faq($::cnt) [list [string trim $question] [string trim $answer]] incr ::cnt } faq { What GUIs are available for fossil? } { | | > | | | | | | < < | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > > > | | | | | > > | | | > > | | | > > > > > > > > | < | < | | | 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 | #!/usr/bin/tclsh # # Run this to generate the FAQ # set cnt 1 proc faq {question answer} { set ::faq($::cnt) [list [string trim $question] [string trim $answer]] incr ::cnt } faq { What GUIs are available for fossil? } { The fossil executable comes with a [./webui.wiki | web-based GUI] built in. Just run: <pre> <b>fossil [/help/ui|ui]</b> <i>REPOSITORY-FILENAME</i> </pre> And your default web browser should pop up and automatically point to the fossil interface. (Hint: You can omit the <i>REPOSITORY-FILENAME</i> if you are within an open check-out.) } faq { What is the difference between a "branch" and a "fork"? } { This is a big question - too big to answer in a FAQ. Please read the <a href="branching.wiki">Branching, Forking, Merging, and Tagging</a> document. } faq { How do I create a new branch? } { There are lots of ways: When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add the option "--branch <i>BRANCH-NAME</i>" to make the new check-in be the first check-in for a new branch. If you want to create a new branch whose initial content is the same as an existing check-in, use this command: <pre> <b>fossil [/help/branch|branch] new</b> <i>BRANCH-NAME BASIS</i> </pre> The <i>BRANCH-NAME</i> argument is the name of the new branch and the <i>BASIS</i> argument is the name of the check-in that the branch splits 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? } { There are several ways: When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add a tag to that check-in using the "--tag <i>TAGNAME</i>" command-line option. You can repeat the --tag option to give a check-in multiple tags. Tags need not be unique. So, for example, it is common to give every released version a "release" tag. If you want add a tag to an existing check-in, you can use the <b>[/help/tag|tag]</b> command. For example: <pre> <b>fossil [/help/branch|tag] add</b> <i>TAGNAME</i> <i>CHECK-IN</i> </pre> 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. Of course, you can also keep your branch private forever simply by not merging the changes in the private branch back into the trunk. [./private.wiki | Additional information] } faq { How can I delete inappropriate content from my fossil repository? } { See the article on [./shunning.wiki | "shunning"] for details. } faq { How do I make a clone of the fossil self-hosting repository? } { Any of the following commands should work: <pre> fossil [/help/clone|clone] https://fossil-scm.org/ fossil.fossil fossil [/help/clone|clone] https://www2.fossil-scm.org/ fossil.fossil fossil [/help/clone|clone] https://www3.fossil-scm.org/site.cgi fossil.fossil </pre> Once you have the repository cloned, you can open a local check-out as follows: <pre> mkdir src; cd src; fossil [/help/open|open] ../fossil.fossil </pre> Thereafter you should be able to keep your local check-out up to date with the latest code in the public repository by typing: <pre> fossil [/help/update|update] </pre> } faq { How do I import or export content from and to other version control systems? } { Please see [./inout.wiki | Import And Export] } ############################################################################# # Code to actually generate the FAQ # puts "<title>Fossil FAQ</title>\n" puts "Note: See also <a href=\"qandc.wiki\">Questions and Criticisms</a>.\n" puts {<ol>} for {set i 1} {$i<$cnt} {incr i} { puts "<li><a href=\"#q$i\">[lindex $faq($i) 0]</a></li>" } puts {</ol>} puts {<hr>} for {set i 1} {$i<$cnt} {incr i} { puts "<p id=\"q$i\"><b>($i) [lindex $faq($i) 0]</b></p>\n" set body [lindex $faq($i) 1] regsub -all "\n *" [string trim $body] "\n" body puts "$body</li>\n" } puts {</ol>} |
Changes to www/faq.wiki.
1 | <title>Fossil FAQ</title> | < | | > | | | > < | | > | | | | < | | | < | | | | < < | | | | | | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > | | | < | | > | | | | | > > | | | > > | | | > > > > | 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 | <title>Fossil FAQ</title> Note: See also <a href="qandc.wiki">Questions and Criticisms</a>. <ol> <li><a href="#q1">What GUIs are available for fossil?</a></li> <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li> <li><a href="#q3">How do I create a new branch?</a></li> <li><a href="#q4">How do I tag a check-in?</a></li> <li><a href="#q5">How do I create a private branch that won't get pushed back to the main repository.</a></li> <li><a href="#q6">How can I delete inappropriate content from my fossil repository?</a></li> <li><a href="#q7">How do I make a clone of the fossil self-hosting repository?</a></li> <li><a href="#q8">How do I import or export content from and to other version control systems?</a></li> </ol> <hr> <p id="q1"><b>(1) What GUIs are available for fossil?</b></p> The fossil executable comes with a [./webui.wiki | web-based GUI] built in. Just run: <pre> <b>fossil [/help/ui|ui]</b> <i>REPOSITORY-FILENAME</i> </pre> And your default web browser should pop up and automatically point to the fossil interface. (Hint: You can omit the <i>REPOSITORY-FILENAME</i> if you are within an open check-out.)</li> <p id="q2"><b>(2) What is the difference between a "branch" and a "fork"?</b></p> This is a big question - too big to answer in a FAQ. Please read the <a href="branching.wiki">Branching, Forking, Merging, and Tagging</a> document.</li> <p id="q3"><b>(3) How do I create a new branch?</b></p> There are lots of ways: When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add the option "--branch <i>BRANCH-NAME</i>" to make the new check-in be the first check-in for a new branch. If you want to create a new branch whose initial content is the same as an existing check-in, use this command: <pre> <b>fossil [/help/branch|branch] new</b> <i>BRANCH-NAME BASIS</i> </pre> The <i>BRANCH-NAME</i> argument is the name of the new branch and the <i>BASIS</i> argument is the name of the check-in that the branch splits 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.</li> <p id="q4"><b>(4) How do I tag a check-in?</b></p> There are several ways: When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add a tag to that check-in using the "--tag <i>TAGNAME</i>" command-line option. You can repeat the --tag option to give a check-in multiple tags. Tags need not be unique. So, for example, it is common to give every released version a "release" tag. If you want add a tag to an existing check-in, you can use the <b>[/help/tag|tag]</b> command. For example: <pre> <b>fossil [/help/branch|tag] add</b> <i>TAGNAME</i> <i>CHECK-IN</i> </pre> 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.</li> <p id="q5"><b>(5) How do I create a private branch that won't get pushed back to the main repository.</b></p> 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. Of course, you can also keep your branch private forever simply by not merging the changes in the private branch back into the trunk. [./private.wiki | Additional information]</li> <p id="q6"><b>(6) How can I delete inappropriate content from my fossil repository?</b></p> See the article on [./shunning.wiki | "shunning"] for details.</li> <p id="q7"><b>(7) How do I make a clone of the fossil self-hosting repository?</b></p> Any of the following commands should work: <pre> fossil [/help/clone|clone] https://fossil-scm.org/ fossil.fossil fossil [/help/clone|clone] https://www2.fossil-scm.org/ fossil.fossil fossil [/help/clone|clone] https://www3.fossil-scm.org/site.cgi fossil.fossil </pre> Once you have the repository cloned, you can open a local check-out as follows: <pre> mkdir src; cd src; fossil [/help/open|open] ../fossil.fossil </pre> Thereafter you should be able to keep your local check-out up to date with the latest code in the public repository by typing: <pre> fossil [/help/update|update] </pre></li> <p id="q8"><b>(8) How do I import or export content from and to other version control systems?</b></p> Please see [./inout.wiki | Import And Export]</li> </ol> |
Added www/fileedit-page.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 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 | # The fileedit Page This document describes the limitations of, caveats for, and disclaimers for the [](/fileedit) page, which provides users with [checkin privileges](./caps/index.md) basic editing features for files via the web interface. # Important Caveats and Disclaimers Predictably, the ability to edit files in a repository from a web browser halfway around the world comes with several obligatory caveats and disclaimers... ## <a id="cap"></a> `/fileedit` Does *Nothing* by Default. In order to "activate" it, a user with [the "setup" permission](./caps/index.md) must set the [fileedit-glob](/help?cmd=fileedit-glob) repository setting to a comma- or newline-delimited list of globs representing a whitelist of files which may be edited online. Any user with commit access may then edit files matching one of those globs. Certain pages within the UI get an "edit" link added to them when the current user's permissions and the whitelist both permit editing of that file. ## <a id="csrf"></a> CSRF & HTTP Referrer Headers In order to protect against [Cross-site Request Forgery (CSRF)][csrf] attacks, Fossil UI features which write to the database require that the browser send the so-called [HTTP `Referer` header][referer] (noting that the misspelling of "referrer" is a historical accident which has long-since been standardized!). Modern browsers, by default, include such information automatically for *interactive* actions which lead to a request, e.g. clicking on a link back to the same server. However, `/fileedit` uses asynchronous ["XHR"][xhr] connections, which browsers *may* treat differently than strictly interactive elements. - **Firefox**: configuration option `network.http.sendRefererHeader` specifies whether the `Referer` header is sent. It must have a value of 2 (which is the default) for XHR requests to get the `Referer` header. Purely interactive Fossil features, in which users directly activate links or forms, work with a level of 1 or higher. - **Chrome**: apparently requires an add-on in order to change this policy, so Chrome without such an add-on will not suppress this header. - **Safari**: ??? - **Other browsers**: ??? If `/fileedit` shows an error message saying "CSRF violation," the problem is that the browser is not sending a `Referer` header to XHR connections. Fossil does not offer a way to disable its CSRF protections. [referer]: https://en.wikipedia.org/wiki/HTTP_referer [csrf]: https://en.wikipedia.org/wiki/Cross-site_request_forgery [xhr]: https://en.wikipedia.org/wiki/XMLHttpRequest ## <a id="commit"></a> `/fileedit` **Works by Creating Commits** Thus any edits made via that page become a normal part of the repository. ## <a id="intent"></a> `/fileedit` is *Intended* for use with Embedded Docs ... and similar text files, and is most certainly **not intended for editing code**. Editing files with unusual syntax requirements, e.g. hard tabs in makefiles, may break them. *You Have Been Warned.* Similarly, though every effort is made to retain the end-of-line style used by being-edited files, the round-trip through an HTML textarea element may change the EOLs. The Commit section of the page offers three different options for how to treat newlines when saving changes. **Files with mixed EOL styles** *will be normalized to a single EOL style* when modified using `/fileedit`. When "inheriting" the EOL style from a previous version which has mixed styles, the first EOL style detected in the previous version of the file is used. ## <a id="checkout"></a> `/fileedit` **is Not a Replacement for a Checkout** A full-featured checkout allows far more possibilities than this basic online editor permits, and the feature scope of `/fileedit` is intentionally kept small, implementing only the bare necessities needed for performing basic edits online. It *is not, and will never be, a replacement for a checkout.* It is to be expected that users will want to do "more" with this page, and we generally encourage feature requests, but be aware that certain types of ostensibly sensible feature requests *will be rejected* for `/fileedit`. These include, but are not limited to: - Features which are already provided by other pages, e.g. the ability to create a new named branch or add tags. - Features which would require re-implementing significant capabilities provided only within a checkout (e.g. merging files). - The ability to edit/manipulate files which are in a local checkout. (If you have a checkout, use your local editor, not `/fileedit`.) - Editing of non-text files, e.g. images. Use a checkout and your preferred graphics editor. - Support for syncing/pulling/pushing of a repository before and/or after edits. Those features cannot be *reliably* provided via a web interface for several reasons. Similarly, some *potential* features have significant downsides, abuses, and/or implementation hurdles which make the decision of whether or not to implement them subject to notable contributor debate. e.g. the ability to add new files or remove/rename older files. ## <a id="storage"></a> `/fileedit` **Stores Only Limited Local Edits While Working** When changes are made to a given checkin/file combination, `/fileedit` will, if possible, store them in [`window.localStorage` or `window.sessionStorage`][html5storage], if available, but... - Which storage is used is unspecified and may differ across environments. - If neither of those is available, the storage is transient and will not survive a page reload. In this case, the UI issues a clear warning in the editor tab. - It stores only the most recent checkin/file combinations which have been modified (exactly how many may differ - the number will be noted somewhere in the UI). Note that changing the "executable bit" is counted as a modification, but the checkin *comment* is *not* and is reset after a commit. - If its internal limit on the number of modified files is exceeded, it silently discards the oldest edits to keep the list at its limit. Edits are saved whenever the editor component fires its "change" event, which essentially means as soon as it loses input focus. Thus to force the browser to save any pending changes, simply click somwhere on the page outside of the editor. Exactly how long `localStorage` will survive, and how much it or `sessionStorage` can hold, is environment-dependent. `sessionStorage` will survive until the current browser tab is closed, but it survives across reloads of the same tab. If `/fileedit` determines that no persistent storage is available a warning is displayed on the editor page. [html5storage]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API ## <a id="power"></a> The Power is Yours, but... > "With great power comes great responsibility." **Use this feature judiciously, *if at all*.** Now, with those warnings and caveats out of the way... ----- # <a id="tips"></a> Tips and Tricks ## <a id="global-js"></a> `fossil` Global-scope JS Object `/fileedit` is largely implemented in JavaScript, and makes heavy use of the global-scope `fossil` object, which provides infrastructure-level features intended for use by Fossil UI pages. (That said, that infrastructure was introduced with `/fileedit`, and most pages do not use it.) The `fossil.page` object represents the UI's current page (on pages which make use of this API - most do not). That object supports listening to page-specific events so that JS code installed via [client-side edits to the site skin's footer](customskin.md) may react to those changes somehow. The next section describes one such use for such events... ## <a id="syn-hl"></a> Integrating Syntax Highlighting Assuming a repository has integrated a 3rd-party syntax highlighting solution, it can probably (depending on its API) be told how to highlight `/fileedit`'s wiki/markdown-format previews. Here are instructions for doing so with [highlightjs](https://highlightjs.org/): At the very bottom of the [site skin's footer](customskin.md), add a script tag similar to the following: ```javascript <script nonce="$<nonce>"> if(window.fossil && fossil.page && fossil.page.name==='fileedit'){ fossil.page.addEventListener( 'fileedit-preview-updated', (ev)=>{ if(ev.detail.previewMode==='wiki'){ ev.detail.element.querySelectorAll( 'code[class^=language-]' ).forEach((e)=>hljs.highlightBlock(e)); } } ); } </script> ``` Note that the `nonce="$<nonce>"` part is intended to be entered literally as shown above. It will be expanded to contain the current request's nonce value when the page is rendered. The first line of the script just ensures that the expected JS-level infrastructure is loaded. It's only loaded in the `/fileedit` page and possibly pages added or "upgraded" since `/fileedit`'s introduction. The part in the `if` block adds an event listener to the `/fileedit` app which gets called when the preview is refreshed. That event contains 3 properties: - `previewMode`: a string describing the current preview mode: `wiki` (which includes Fossil-native wiki and markdown), `text`, `htmlInline`, `htmlIframe`. We should "probably" only highlight wiki text, and thus the example above limits its work to that type of preview. It won't work with `htmlIframe`, as that represents an iframe element which contains a complete HTML document. - `element`: the DOM element in which the preview is rendered. - `mimetype`: the mimetype of the being-previewed content, as determined by Fossil (by its file extension). The event listener callback shown above doesn't use the `mimetype`, but makes used of the other two. It fishes all `code` blocks out of the preview which explicitly have a CSS class named `language-`something, and then asks highlightjs to highlight them. ## <a id="editor"></a> Integrating a Custom Editor Widget (These instructions also work for the `/wikiedit` page by replacing "fileedit" with "wikiedit" in any strings or symbol names!) It is possible to replace `/fileedit`'s basic text-editing widget (a `textarea` element) with a fancy 3rd-party editor widget by following these instructions... All JavaScript code which follows is assumed to be in a script tag similar to the one shown in the previous section: ```javascript <script nonce="$<nonce>"> if(window.fossil && fossil.page && fossil.page.name==='fileedit'){ // code specific to the fileedit page goes here } </script> ``` First, install proxy functions so that `fossil.page.fileContent()` can get and set your content: ``` fossil.page.setContentMethods( function(){ return text-form content of your widget }, function(content){ set text-form content of your widget } }; ``` Secondly, we need to alert the editor app when there are changes so that it can do things like store edits locally so that they are not lost on a page reload. How that is done is completely dependent on the 3rd-party editor widget, but it generically looks something like: ``` myCustomWidget.on('eventName', ()=>fossil.page.notifyOfChange()); ``` Lastly, if the 3rd-party editor does *not* hide or remove the native editor widget, and does not inject itself into the DOM on the caller's behalf, we can replace the native widget with the 3rd-party one with: ```javascript fossil.page.replaceEditorWidget(yourNewWidgetElement); ``` That method must be passed a DOM element and may only be called once: it *removes itself* the first time it is called. That should be all there is to it. When `fossil.page` needs to get the being-edited content, it will call the installed content-getter function with no arguments, and when it sets the content (immediately after (re)loading a file or grabbing local edits), it will pass that content to the installed content-setter method. Those, in turn will trigger the installed proxies and fire any relevant events. Below is an example of Fossil skin footer content which plugs in the TinyMCE HTML editor into the `/wikiedit` page, but the process is identical for `/fileedit` (noting that `/fileedit` may need to be able to edit multiple types of files for which a special-purpose editor like TinyMCE may not be suitable). Note that any paths to CSS and JS resources of course need to be modified to suit one's own installation. ``` <!-- TinyMCE CSS and JS: --> <link href="$<home>/doc/ckout/skin.min.css" rel="stylesheet" type="text/css"> <link href="$<home>/doc/ckout/content.min.css" rel="stylesheet" type="text/css"> <script src='$<home>/doc/ckout/tinymce.min.js'></script> <script src='$<home>/doc/ckout/theme.min.js'></script> <script src='$<home>/doc/ckout/icons.min.js'></script> <!-- Integrate TinyMCE into /wikiedit: --> <script nonce="$<nonce>"> if(window.fossil && window.fossil.page.name==='wikiedit'){ window.fossil.onPageLoad( function(){ const elemId = 'wikiedit-content-editor'; tinymce.init({selector: 'textarea#'+elemId}); const widget = tinymce.get(elemId); fossil.page.setContentMethods( function(){return widget.getContent()}, function(content){widget.setContent(content)} ); widget.on('change', function(){ if(widget.isDirty()) fossil.page.notifyOfChange(); }); }); } </script> ``` |
Changes to www/fileformat.wiki.
1 | <title>Fossil File Formats</title> | < < < | | > > > > | | > | | > > | | | > | > > > | > > > > > > > > | > > > > > | > > > | | > > > > > > > > > > > > > > | | < | | | | < < < < < < < < < < < < < < < < < < < < < > > | > | > | > | > > > > > > > | | | | | | < | | | | < < | | | | | | | > > > > | | | > | | | | | > > > > | | | | | | | | | > > > > > > > > > > > > > > > > > > > > | | | | | | > | | | | | | | | | > < | | | < < < < < < < < < < < < < < < < > | | | | | | < < | | < < < < < < < > | | | | | > | | | | | | | | | | | < | > | < < | < > > > | | | | > > > | | | | | > > > > > > > | < | | | | | | | > | | | | | | | | | | < | > | | | > | | | | | | | | > > > > | | | > > | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | > > | > > > > | > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > | | > > | | > > > > > > | > > > > > > > | > | | | | | > | > > > > > > | | > > > | > > > > > > > > > > > > > > | | > | > > > > > > > > > > > > > > > > > > > > > > | | | | | | | > > | | | | | | > > | | | | | | > > > | > > > > > > > > > | > > | > | | | | | | | > > | > > > > | > > > > > > > | | | | | | > > | | | | | | | > > | > | | | | | > | | | | | > > | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <title>Fossil File Formats</title> The global state of a fossil repository is kept simple so that it can endure in useful form for decades or centuries. A fossil repository is intended to be readable, searchable, and extensible by people not yet born. The global state of a fossil repository is an unordered set of <i>artifacts</i>. An artifact might be a source code file, the text of a wiki page, part of a trouble ticket, a description of a check-in including all the files in that check-in with the check-in comment and so forth. Artifacts are broadly grouped into two types: content artifacts and structural artifacts. Content artifacts are the raw project source-code files that are checked into the repository. Structural artifacts have special formatting rules and are used to show the relationships between other artifacts in the repository. It is possible for an artifact to be both a structure artifact and a content artifact, though this is rare. Artifacts can be text or binary. In addition to the global state, each fossil repository also contains local state. The local state consists of web-page formatting preferences, authorized users, ticket display and reporting formats, and so forth. The global state is shared in common among all repositories for the same project, whereas the local state is often different in separate repositories. 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. <h2 id="names">1.0 Artifact Names</h2> Each artifact in the repository is named by a hash of its content. No prefixes, suffixes, or other information is added to an artifact before the hash is computed. The artifact name is just the (lower-case hexadecimal) hash of the raw artifact. Fossil currently computes artifact names using either SHA1 or SHA3-256. It is relatively easy to add new algorithms in the future, but there are no plans to do so at this time. When referring to artifacts in using tty commands or webpage URLs, it is sufficient to specify a unique prefix for the artifact name. If the input prefix is not unique, Fossil will show an error. Within a structural artifact, however, all references to other artifacts must be the complete hash. Prior to Fossil version 2.0, all names were formed from the SHA1 hash of the artifact. The key innovation in Fossil 2.0 was adding support for alternative hash algorithms. <h2 id="structural">2.0 Structural Artifacts</h2> A structural artifact is an artifact with a particular format that is used to define the relationships between other artifacts in the repository. Fossil recognizes the following kinds of structural artifacts: <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> <li> [#forum | Forum Posts] </li> </ul> These eight structural artifact types are described in subsections below. Structural artifacts are UTF-8 text. The artifact may be PGP clearsigned. After removal of the PGP clearsign header and suffix (if any) a structural artifact consists of one or more "cards" separated by a single newline (ASCII: 0x0a) character. Each card begins with a single character "card type". Zero or more arguments may follow the card type. All arguments are separated from each other and from the card-type character by a single space character. There is no surplus white space between arguments and no leading or trailing whitespace except for the newline character that acts as the card separator. All cards must be in strict lexicographical order (except for an [./fileformat.wiki#outofordercards|historical bug compatibility]). There may not be any duplicate cards. In the current implementation (as of 2017-02-27) 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. <h3 id="manifest">2.1 The Manifest</h3> A manifest defines a check-in. A manifest contains a list of artifacts for each file in the project and the corresponding filenames, as well as information such as parent check-ins, the username of the programmer who created the check-in, the date and time when the check-in was created, and any check-in comments associated with the check-in. Allowed cards in the manifest are as follows: <div class="indent"> <b>B</b> <i>baseline-manifest</i><br> <b>C</b> <i>checkin-comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br> <b>F</b> <i>filename</i> ?<i>hash</i>? ?<i>permissions</i>? ?<i>old-name</i>?<br> <b>N</b> <i>mimetype</i><br> <b>P</b> <i>artifact-hash</i>+<br> <b>Q</b> (<b>+</b>|<b>-</b>)<i>artifact-hash</i> ?<i>artifact-hash</i>?<br> <b>R</b> <i>repository-checksum</i><br> <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <b>*</b> ?<i>value</i>?<br> <b>U</b> <i>user-login</i><br> <b>Z</b> <i>manifest-checksum</i> </div> A manifest may optionally have a single <b>B</b> card. The <b>B</b> card specifies another manifest that serves as the "baseline" for this manifest. A manifest that has a <b>B</b> card is called a delta-manifest and a manifest that omits the <b>B</b> card is a baseline-manifest. The other manifest identified by the argument of the <b>B</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 <b>C</b> card. The sole argument to the <b>C</b> 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 newline (ASCII 0x0a) is "\n" (ASCII 0x5C, x6E). A backslash (ASCII 0x5C) is represented as two backslashes "\\". Apart from space and newline, no other whitespace characters are allowed in the check-in comment. Nor are any unprintable characters allowed in the comment. A manifest must have exactly one <b>D</b> card. The sole argument to the <b>D</b> card is a date-time stamp in the ISO8601 format. The date and time should be in coordinated universal time (UTC). The format one of: <pre class="indent"><i>YYYY-MM-DD<b>T</b>HH:MM:SS YYYY-MM-DD<b>T</b>HH:MM:SS.SSS</i></pre> A manifest has zero or more <b>F</b> cards. Each <b>F</b> 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 <b>C</b> 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 <b>F</b> card is the lower-case hexadecimal artifact 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 <b>F</b> 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 <b>F</b> 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 <b>N</b> cards. The <b>N</b> card specifies the mimetype for the text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype is used. A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card. The <b>P</b> card has a varying number of arguments that define other manifests from which the current manifest is derived. Each argument is a lowercase hexadecimal artifact hash of a predecessor manifest. All arguments to the <b>P</b> card must be unique within that card. The first argument is the artifact hash of the direct ancestor of the manifest. Other arguments define manifests with which the first was merged to yield the current manifest. Most manifests have a <b>P</b> card with a single argument. The first manifest in the project has no ancestors and thus has no <b>P</b> card or (depending on the Fossil version) an empty <b>P</b> card (no arguments). A manifest has zero or more <b>Q</b> cards. A <b>Q</b> card is similar to a <b>P</b> card in that it defines a predecessor to the current check-in. But whereas a <b>P</b> card defines the immediate ancestor or a merge ancestor, the <b>Q</b> 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 <b>Q</b> 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 <b>Q</b> 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. The <b>Q</b> card was added to the interface specification on 2011-02-26. Older versions of Fossil will reject manifests that contain <b>Q</b> cards. A manifest may optionally have a single <b>R</b> card. The <b>R</b> card has a single argument which is the MD5 checksum of all files in the check-in except the manifest itself. The checksum is expressed as 32 characters of lowercase hexadecimal. The checksum is computed as follows: For each file in the check-in (except for the manifest itself) in strict sorted lexicographical order, take the pathname of the file relative to the root of the repository, append a single space (ASCII 0x20), the size of the file in ASCII decimal, a single newline character (ASCII 0x0A), and the complete text of the file. Compute the MD5 checksum of the result. A manifest might contain one or more <b>T</b> cards used to set [./branching.wiki#tags | tags or properties] on the check-in. The format of the <b>T</b> card is the same as described in <i>Control Artifacts</i> section below, except that the second argument is the single character "<b>*</b>" instead of an artifact ID. 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". <b>T</b> cards are typically added to manifests in order to set the <b>branch</b> property and a symbolic name when the check-in is intended to start a new branch. Each manifest has a single <b>U</b> card. The argument to the <b>U</b> card is the login of the user who created the manifest. The login name is encoded using the same character escapes as is used for the check-in comment argument to the <b>C</b> card. A manifest must have a single <b>Z</b> card as its last line. The argument to the <b>Z</b> card is a 32-character lowercase hexadecimal MD5 hash of all prior lines of the manifest up to and including the newline character that immediately precedes the "Z", excluding any PGP clear-signing prefix. The <b>Z</b> card is a sanity check to prove that the manifest is well-formed and consistent. A sample manifest from Fossil itself can be seen [/artifact/28987096ac | here]. <h3 id="cluster">2.2 Clusters</h3> 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. Allowed cards in the cluster are as follows: <div class="indent"> <b>M</b> <i>artifact-id</i><br /> <b>Z</b> <i>checksum</i> </div> A cluster contains one or more <b>M</b> cards followed by a single <b>Z</b> card. Each <b>M</b> card has a single argument which is the artifact ID of another artifact in the repository. The <b>Z</b> card works exactly like the <b>Z</b> card of a manifest. The argument to the <b>Z</b> card is the lower-case hexadecimal representation of the MD5 checksum of all prior cards in the cluster. The <b>Z</b> card is required. An example cluster from Fossil can be seen [/artifact/d03dbdd73a2a8 | here]. <h3 id="ctrl">2.3 Control Artifacts</h3> Control artifacts are used to assign properties to other artifacts within the repository. Allowed cards in a control artifact are as follows: <div class="indent"> <b>D</b> <i>time-and-date-stamp</i><br /> <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i><br /> </div> A control artifact must have one <b>D</b> card, one <b>U</b> card, one <b>Z</b> card and one or more <b>T</b> cards. No other cards or other text is allowed in a control artifact. Control artifacts might be PGP clearsigned. The <b>D</b> card and the <b>Z</b> card of a control artifact are the same as in a manifest. The <b>T</b> card represents a [./branching.wiki#tags | tag or property] that is applied to some other artifact. The <b>T</b> card has two or three values. The second argument is the 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 for display purposes. The "user" tag overrides the name of the check-in user. The "date" tag overrides the check-in date. The "branch" tag sets the name of the branch that at check-in belongs to. Symbolic tags begin with the "sym-" prefix. The <b>U</b> card is the name of the user that created the control artifact. The <b>Z</b> card is the usual required artifact checksum. An example control artifact can be seen [/info/9d302ccda8 | here]. <h3 id="wikichng">2.4 Wiki Pages</h3> A wiki artifact defines a single version of a single wiki page. Wiki artifacts accept the following card types: <div class="indent"> <b>C</b> <i>change-comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br /> <b>L</b> <i>wiki-title</i><br /> <b>N</b> <i>mimetype</i><br /> <b>P</b> <i>parent-artifact-id</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> </div> The <b>D</b> card is the date and time when the wiki page was edited. The <b>P</b> card specifies the parent wiki pages, if any. The <b>L</b> card gives the name of the wiki page. The optional <b>N</b> card specifies the mimetype of the wiki text. If the <b>N</b> card is omitted, the mimetype is assumed to be text/x-fossil-wiki. The <b>U</b> card specifies the login of the user who made this edit to the wiki page. The <b>Z</b> card is the usual checksum over the entire artifact and is required. The <b>W</b> card is used to specify the text of the wiki page. The argument to the <b>W</b> 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 <b>W</b> card. The wiki text is always followed by one extra newline. The <b>C</b> card on a wiki page is optional. The argument is a comment that explains why the changes was made. The ability to have a <b>C</b> card on a wiki page artifact was added on 2019-12-02 at the suggestion of user George Krivov and is not currently used or generated by the implementation. Older versions of Fossil will reject a wiki-page artifact that includes a <b>C</b> card. An example wiki artifact can be seen [/artifact?name=7b2f5fd0e0&txt=1 | here]. <h3 id="tktchng">2.5 Ticket Changes</h3> A ticket-change artifact represents a change to a trouble ticket. The following cards are allowed on a ticket change artifact: <div class="indent"> <b>D</b> <i>time-and-date-stamp</i><br /> <b>J</b> ?<b>+</b>?<i>name</i> ?<i>value</i>?<br /> <b>K</b> <i>ticket-id</i><br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i> </div> The <b>D</b> card is the usual date and time stamp and represents the point in time when the change was entered. The <b>U</b> card is the login of the programmer who entered this change. The <b>Z</b> card is the required checksum over the entire artifact. Every ticket has a distinct ticket-id: 40-character lower-case hexadecimal number. The ticket-id is given in the <b>K</b> card. A ticket exists if it contains one or more changes. The first "change" to a ticket is what brings the ticket into existence. <b>J</b> cards specify changes to the "value" of "fields" in the ticket. If the <i>value</i> parameter of the <b>J</b> card is omitted, then the field is set to an empty string. Each fossil server has a ticket configuration which specifies the fields its understands. The ticket configuration is part of the local state for the repository and thus can vary from one repository to another. Hence a <b>J</b> card might specify a <i>field</i> that do not exist in the local ticket configuration. If a <b>J</b> card specifies a <i>field</i> that is not in the local configuration, then that <b>J</b> card is simply ignored. The first argument of the <b>J</b> card is the field name. The second value is the field value. If the field name begins with "+" then the value is appended to the prior value. Otherwise, the value on the <b>J</b> card replaces any previous value of the field. The field name and value are both encoded using the character escapes defined for the <b>C</b> card of a manifest. An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <h3 id="attachment">2.6 Attachments</h3> 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: <div class="indent"> <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> </div> The <b>A</b> card specifies a filename for the attachment in its first argument. The second argument to the <b>A</b> 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 lower-case artifact ID of the attachment itself. A missing third argument means that the attachment should be deleted. The <b>C</b> card is an optional comment describing what the attachment is about. The <b>C</b> card is optional, but there can only be one. A single <b>D</b> card is required to give the date and time when the attachment was applied. There may be zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype of the comment text provided in the <b>C</b> card. If the <b>N</b> card is omitted, the <b>C</b> card mimetype is taken to be text/plain. A single <b>U</b> card gives the name of the user who added the attachment. If an attachment is added anonymously, then the <b>U</b> card may be omitted. The <b>Z</b> card is the usual checksum over the rest of the attachment artifact. The <b>Z</b> card is required. <h3 id="event">2.7 Technical Notes</h3> 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: <div class="indent"> <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> </div> The <b>C</b> card contains text that is displayed on the timeline for the technote. The <b>C</b> card is optional, but there can only be one. A single <b>D</b> 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 <b>E</b> 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 <b>D</b> card is the only one used. The technote-id must be a 40-character lower-case hexadecimal string. The optional <b>N</b> card specifies the mimetype of the text of the technote that is contained in the <b>W</b> card. If the <b>N</b> card is omitted, then the <b>W</b> card text mimetype is assumed to be text/x-fossil-wiki, which is the Fossil wiki format. The optional <b>P</b> card specifies a prior technote with the same technote-id from which the current technote is an edit. The <b>P</b> 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 <b>T</b> cards used to set [./branching.wiki#tags | tags or properties] on the technote. The format of the <b>T</b> 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, <b>T</b> cards are normally used to set the background display color for timelines. The optional <b>U</b> card gives name of the user who entered the technote. A single <b>W</b> card provides wiki text for the document associated with the technote. The format of the <b>W</b> card is exactly the same as for a [#wikichng | wiki artifact]. The <b>Z</b> card is the required checksum over the rest of the artifact. <h3 id="forum">2.8 Forum Posts</h3> Forum posts are intended as a mechanism for users and developers to discuss a project. Forum posts are like messages on a mailing list. The following cards are allowed on an forum post artifact: <div class="indent"> <b>D</b> <i>time-and-date-stamp</i><br /> <b>G</b> <i>thread-root</i><br /> <b>H</b> <i>thread-title</i><br /> <b>I</b> <i>in-reply-to</i><br /> <b>N</b> <i>mimetype</i><br /> <b>P</b> <i>parent-artifact-id</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> </div> Every forum post must have either one <b>I</b> card and one <b>G</b> card or one <b>H</b> card. Forum posts are organized into topic threads. The initial post for a thread (the root post) has an <b>H</b> card giving the title or subject for that thread. The argument to the <b>H</b> card is a string in the same format as a comment string in a <b>C</b> card. All follow-up posts have an <b>I</b> card that indicates which prior post in the same thread the current forum post is replying to, and a <b>G</b> card specifying the root post for the entire thread. The argument to <b>G</b> and <b>I</b> cards is the artifact hash for the prior forum post to which the card refers. In theory, it is sufficient for follow-up posts to have only an <b>I</b> card, since the <b>G</b> card value could be computed by following a chain of <b>I</b> cards. However, the <b>G</b> card is required in order to associate the artifact with a forum thread in the case where an intermediate artifact in the <b>I</b> card chain is shunned or otherwise becomes unreadable. A single <b>D</b> card is required to give the date and time when the forum post was created. The optional <b>N</b> card specifies the mimetype of the text of the technote that is contained in the <b>W</b> card. If the <b>N</b> card is omitted, then the <b>W</b> card text mimetype is assumed to be text/x-fossil-wiki, which is the Fossil wiki format. The optional <b>P</b> card specifies a prior forum post for which this forum post is an edit. For display purposes, only the child post is shown, though the historical post is retained as a record. If <b>P</b> cards are used and there exist multiple versions of the same forum post, then <b>I</b> cards for other artifacts refer to whichever version of the post was current at the time the reply was made, but <b>G</b> cards refer to the initial, unedited root post for the thread. Thus, following the chain of <b>I</b> cards back to the root of the thread may land on a different post than the one given in the <b>G</b> card. However, following the chain of <b>I</b> cards back to the thread root, then following <b>P</b> cards back to the initial version of the thread root must give the same artifact as is provided by the <b>G</b> card, otherwise the artifact containing the <b>G</b> card is considered invalid and should be ignored. In general, <b>P</b> cards may contain multiple arguments, indicating a merge. But since forum posts cannot be merged, the <b>P</b> card of a forum post may only contain a single argument. The <b>U</b> card gives name of the user who entered the forum post. A single <b>W</b> card provides wiki text for the forum post. The format of the <b>W</b> card is exactly the same as for a [#wikichng | wiki artifact]. The <b>Z</b> card is the required checksum over the rest of the artifact. <h2 id="summary">3.0 Card Summary</h2> The following table summarizes the various kinds of cards that appear on Fossil artifacts. A blank entry means that combination of card and artifact is not legal. A number or range of numbers indicates the number of times a card may (or must) appear in the corresponding artifact type. e.g. a value of 1 indicates a required unique card and 1+ indicates that one or more such cards are required. <table> <tr> <th>⇩ Card Format / Used By ⇨</th> <th>Manifest</th> <th>Cluster</th> <th>Control</th> <th>Wiki</th> <th>Ticket</th> <th>Attachment</th> <th>Technote</th> <th>Forum</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> <td> </td> <td align=center><b>1</b></td> <td> </td> <td> </td> </tr> <tr> <td><b>B</b> <i>baseline</i></td> <td align=center><b>0-1</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>C</b> <i>comment-text</i></td> <td align=center><b>1</b></td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> <td> </td> <td align=center><b>0-1</b></td> <td align=center><b>0-1</b></td> <td> </td> </tr> <tr> <td><b>D</b> <i>date-time-stamp</i></td> <td align=center><b>1</b></td> <td> </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> <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> <td> </td> </tr> <tr> <td><b>F</b> <i>filename</i> ?<i>uuid</i>? ?<i>permissions</i>? ?<i>oldname</i>?</td> <td align=center><b>0+</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>G</b> <i>thread-root</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> </tr> <tr> <td><b>H</b> <i>thread-title</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>0-1</b><sup><nowiki>[4]</nowiki></sup></td> </tr> <tr> <td><b>I</b> <i>in-reply-to</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>0-1</b><sup><nowiki>[4]</nowiki></sup></td> </tr> <tr> <td><b>J</b> <i>name</i> ?<i>value</i>?</td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>1+</b></td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>K</b> <i>ticket-uuid</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>1</b></td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>L</b> <i>wiki-title</i></td> <td> </td> <td> </td> <td> </td> <td align=center><b>1</b></td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>M</b> <i>uuid</i></td> <td> </td> <td align=center><b>1+</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>N</b> <i>mimetype</i></td> <td align=center><b>0-1</b></td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> <td> </td> <td align=center><b>0-1</b></td> <td align=center><b>0-1</b></td> <td align=center><b>0-1</b></td> </tr> <tr> <td><b>P</b> <i>uuid ...</i></td> <td align=center><b>0-1</b></td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> <td align=center><b>0-1</b><sup><nowiki>[5]</nowiki></sup></td> </tr> <tr> <td><b>Q</b> (<b>+</b>|<b>-</b>)<i>uuid</i> ?<i>uuid</i>?</td> <td align=center><b>0+</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>R</b> <i>md5sum</i></td> <td align=center><b>0-1</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <tr> <td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname</i> <i>uuid</i> ?<i>value</i>?<sup><nowiki>[1]</nowiki></sup></td> <td align=center><b>0+</b></td> <td> </td> <td align=center><b>1+</b><sup><nowiki>[2]</nowiki></sup></td> <td> </td> <td> </td> <td> </td> <td align=center><b>0+</b><sup><nowiki>[3]</nowiki></sup></td> <td> </td> </tr> <tr> <td><b>U</b> <i>username</i></td> <td align=center><b>1</b></td> <td> </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>0-1</b></td> <td align=center><b>0-1</b></td> <td align=center><b>1</b></td> </tr> <tr> <td><b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b></td> <td> </td> <td> </td> <td> </td> <td align=center><b>1</b></td> <td> </td> <td> </td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> </tr> <tr> <td><b>Z</b> <i>md5sum</i></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> <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> </table> Footnotes: 1) T-card names may not be made up of only hexadecimal characters, as they would be indistinguishable from a hash prefix. 2) Tags in [#ctrl | Control Artifacts] may not be self-referential. i.e. their target hash may not be <tt>*</tt>. 3) Tags in [#event | Technotes] must be self-referential. i.e. their target hash must be <tt>*</tt>. Similarly, technote tags may only be non-propagating "add" tags. i.e. their name prefix must be <tt>+</tt>. 4) [#forum | Forum Posts] must have either one H-card or one I-card, not both. 5) [#forum | Forum Post] P-cards may have only a single parent hash. i.e. they may not have merge parents. <h2 id="addenda">4.0 Addenda</h2> This section contains additional information which may be useful when implementing algorithms described above. <h3 id="outofordercards">4.1 Relaxed Card Ordering Due To An Historical Bug</h3> All cards of a structural artifact should be in lexicographical order. The Fossil implementation verifies this and rejects any structural artifact which has out-of-order cards. Futhermore, when Fossil is generating new structural artifacts, it runs the generated artifact through the parser to confirm that all cards really are in the correct order before committing the transaction. In this way, Fossil prevents bugs in the code from accidentally inserting misformatted artifacts. The test parse of newly created artifacts is part of the [./selfcheck.wiki|self-check strategy] of Fossil. It takes a few more CPU cycles to double check each artifact before inserting it. The developers consider those CPU cycles well-spent. However, the card-order safety check was accidentally disabled due to [15d04de574383d61|a bug]. And while that bug was lurking undetected in the code, [5e67a7f4041a36ad|another bug] caused the N cards of Technical Notes to occur after the P card rather than before. Thus for a span of several years, Technical Note artifacts were being inserted into Fossil repositories that had their N and P cards in the wrong order. Both bugs have now been fixed. However, to prevent historical Technical Note artifacts that were inserted by users in good faith from being rejected by newer Fossil builds, the card ordering requirement is relaxed slightly. The actual implementation is this: <p class=blockquote> "All cards must be in strict lexicographic order, except that the N and P cards of a Technical Note artifact are allowed to be interchanged." </p> Future versions of Fossil might strengthen this slightly to only allow the out of order N and P cards for Technical Notes entered before a certain date. <h3>4.2 R-Card Hash Calculation</h3> Given a manifest file named <tt>MF</tt>, the following Bash shell code demonstrates how to compute the value of the <b>R</b> card in that manifest. This example uses manifest [28987096ac]. Lines starting with <tt>#</tt> are shell input and other lines are output. This demonstration assumes that the file versions represented by the input manifest are checked out under the current directory. <nowiki><pre> # head MF -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 C Make\sthe\s"clearsign"\sPGP\ssigning\sdefault\sto\soff. D 2010-02-23T15:33:14 F BUILD.txt 4f7988767e4e48b29f7eddd0e2cdea4555b9161c F COPYRIGHT-GPL2.txt 06877624ea5c77efe3b7e39b0f909eda6e25a4ec ... # grep '^R ' MF R c0788982781981c96a0d81465fec7192 # for i in $(awk '/^F /{print $2}' MF); do \ echo $i $(stat -c '%s' $i); \ cat $i; \ done | md5sum c0788982781981c96a0d81465fec7192 - </pre></nowiki> Minor caveats: the above demonstration will work only when none of the filenames in the manifest are "fossilized" (encoded) because they contain spaces. In that case the shell-generated hash would differ because the <tt>stat</tt> calls will fail to find such files (which are output in encoded form here). That approach also won't work for delta manifests. Calculating the <b>R</b> card for delta manifests requires traversing both the delta and its baseline in lexical order of the files, preferring the delta's copy if both contain a given file. |
Added www/fiveminutes.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 | <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> 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. <h2>Create a new repository</h2> <tt>fossil new c:\test.repo</tt> 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. <h2>Open the repository</h2> <tt>cd c:\temp\test.fossil <br> fossil open c:\test.repo</tt> 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>). <h2>Add new files</h2> <tt>fossil add .</tt> To tell Fossil to add new files to the repository. The files aren't actually added until you run "<tt>fossil commit</tt>. 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. Note: To tell Fossil to ignore some extensions: <tt>fossil settings ignore-glob "*.o,*.obj,*.exe" --global</tt> <h2>Remove files that haven't been committed yet</h2> <tt>fossil delete myfile.c</tt> This will simply remove the item from the list of files that were previously added through "<tt>fossil add</tt>". <h2>Check current status</h2> <tt>fossil changes</tt> This shows the list of changes that have been done and will be committed the next time you run "<tt>fossil commit</tt>". It's a useful command to run before running "<tt>fossil commit</tt>" just to check that things are OK before proceeding. <h2>Commit changes</h2> 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. <tt>fossil commit -m "Added stuff"</tt> 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> If you wish to compare the last revision of a file and its checked out version in your work directory: <tt>fossil gdiff myfile.c</tt> If you wish to compare two different revisions of a file in the repository: <tt>fossil finfo myfile</tt> Note the first hash, which is the hash of the commit when the file was committed. <tt>fossil gdiff --from HASH#1 --to HASH#2 myfile.c</tt> <h2>Cancel changes and go back to previous revision</h2> <tt>fossil revert myfile.c</tt> 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. |
Added www/forum.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 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 | <title>Fossil Forums</title> <h2>Introduction</h2> Fossil includes a built-in discussion forum, designed to substitute for a mailing list. Email notification is available to receive posts, but the web-based UI must be used to enter new posts. Advantages of the forum include: * <b>Easy to Administer:</b> If you have already set up a [./server/|Fossil server] with [./alerts.md|email alerts] then turning on the forum feature is just a matter of flipping some permission bits. There is no new software to install and configure, and the same logins and passwords work. * <b>Consistent Display:</b> Forum posts can be in [/md_rules|Markdown], [/wiki_rules|Fossil Wiki], or plain text. Whichever format is used, the result is displayed consistently across all platforms and operating systems and between mobile devices and desktops. * <b>Editable:</b> Forum posts can be amended after they are sent, to fix typos or provide updates. The original posts are preserved as part of the historical record, but only the amended posts are displayed by default. * <b>Built-in Full-Text Search:</b> Forum posts can be included in the index used by the built-in Fossil search logic. * <b>Off-Line Access:</b> Because forum posts are synced along with all other artifacts, you can search the forum, or add new posts, or edit existing posts, all while off-line. * <b>Automatically Cross-Referenced To Other Fossil Artifacts:</b> Because forum posts are normal Fossil artifacts, you can link from them to other Fossil artifacts (check-ins, wiki, tickets) and from those other artifacts to forum posts. The reverse links are recognized and displayed automatically on the receiver. * <b>Malefactor Resistant:</b> Because Fossil accepts forum posts only via the web UI, it is more resistent to spam. Passers-by can post to the forum anonymously (subject to moderation), without the hassle of a sign-up process. * <b>Distributed and Tamper-Proof:</b> Posts are stored in the Fossil repository using the same [./fileformat.wiki | DAG/Merkle-tree design] that Fossil uses to store your check-ins, wiki documents, etc. Forum posts sync to cloned repositories. <h2>Example Installations</h2> Both the [forum:/forum|Fossil project itself] and the [https://sqlite.org/forum/forum|SQLite project] use the Fossil forum in place of mailing lists. The forum has worked well on both projects. The ability to post anonymously provides a low-resistance path for people to report problems, resulting in more problems being reported and fixed. The ability to moderate and amend forum posts means that the forums contain better information. And backups and archives are as easy as running "clone". Both Fossil and SQLite keep their forums as separate repositories. But there is no requirement to do this. A forum can be coresident in the same repository with the source code. <h2 id="setup">Setting up a Fossil Forum</h2> <h3 id="caps">Capabilities</h3> By default, no Fossil user has permission to use the forums except for users with Setup and Admin capabilities, which get these as part of the large package of other capabilities they get. For public Fossil repositories that wish to accept new users without involving a human, go into Admin → Access and enable the "Allow users to register themselves" setting. You may also wish to give users in [./caps/#ucat | the <tt>anonymous</tt> user category] the <b>[./caps/ref.html#2 | RdForum]</b> and <b>[./caps/ref.html#3 | WrForum]</b> capabilities: this allows people to post without creating an account simply by solving [./antibot.wiki | a simple CAPTCHA]. For a private repository, you probably won't want to give the <tt>anonymous</tt> user any forum access, but you may wish to give the <b>RdForum</b> capability to users in the <tt>reader</tt> category. For either type of repository, you are likely to want to give at least the <b>[./caps/ref.html#4 | WrTForum]</b> capability to users in the <tt>developer</tt> category. If you did not give the <b>RdForum</b> capability to <tt>anonymous</tt> above, you should give <tt>developer</tt> that capability here if you choose to give it <b>WrForum</b> or <b>WrTForum</b> capability. If you want to use the email alert feature, by default only those users in the Setup and Admin user categories can make use of it. Grant the <b>[./caps/ref.html#7 | EmailAlert]</b> capability to give others access to this feature. Alternately, you can handle alert signups outside of Fossil, with a Setup or Admin users manually signing users up via Admin → Notification. You'll want to grant this capability to the <tt>nobody</tt> user category if you want anyone to sign up without any restrictions. Give it to <tt>anonymous</tt> instead if you want the user to solve a simple CAPTCHA before signing up. Or, give it to <tt>reader</tt> or <tt>developer</tt> if you want only users with Fossil logins to have this ability. (That's assuming you give one or both of these capabilities to every user on your Fossil repository.) By following this advice, you should not need to tediously add capabilities to individual accounts except in atypical cases, such as to grant the <b>[./caps/ref.html#5 | ModForum]</b> capability to an uncommonly highly-trusted user. <h3 id="skin">Skin Setup</h3> If you create a new Fossil repository with version 2.7 or newer, its default skin is already set up correctly for typical forum configurations. If you have an existing repository, you have two choices if you want its skin to be upgraded to support forums: <ol> <li>Go into Admin → Skins and switch from your current skin to one of the stock skins. If you were on a stock skin, just switch away from your current one to the actual stock skin, since they will be different after the upgrade.</li> <li>If you have local customization that you do not want to throw away, you can use the diff feature of Fossil's skin editor to show how the skins differ.</li> </ol> The remainder of this section summarizes the differences you're expected to see when taking option #2. The first thing is that you'll need to add something like the following to the Header part of the skin to create the navbar link: <verbatim> if {[anycap 23456] || [anoncap 2] || [anoncap 3]} { menulink /forum Forum } </verbatim> These rules say that any logged-in user with any [./caps/ref.html#2 | forum-related capability] or an anonymous user <b>RdForum</b> or <b>WrForum</b> capability will see the "Forum" navbar link, which just takes you to <tt>/forum</tt>. The exact code you need here varies depending on which skin you're using. Follow the style you see for the other navbar links. The new forum feature also brings many new CSS styles to the table. If you're using the stock skin or something sufficiently close, the changes may work with your existing skin as-is. Otherwise, you might need to adjust some things, such as the background color used for the selected forum post: <verbatim> div.forumSel { background-color: rgba(0, 0, 0, 0.05); } </verbatim> That overrides the default — a hard-coded light cyan — with a 95% transparent black overlay instead, which simply darkens your skin's normal background color underneath the selected post. That should work with almost any background color except for very dark background colors. For dark skins, an inverse of the above trick will work better: <verbatim> div.forumSel { background-color: rgba(255, 255, 255, 0.05); } </verbatim> That overlays the background with 5% white to lighten it slightly. Another new forum-related CSS style you might want to reflect into your existing skin is: <verbatim> div.forumPosts a:visited { color: #6A7F94; } </verbatim> This changes the clicked-hyperlink color for the forum post links on the main <tt>/forum</tt> page only, which allows your browser's history mechanism to show which threads a user has read and which not. The link color will change back to the normal link color — indicating "unread" — when a reply is added to an existing thread because that changes where the link from the <tt>/forum</tt> page points, taking you to the newest post in the thread. The color given above is suitable for the stock skin. Beware that when changing this example, there are some [https://hacks.mozilla.org/2010/03/privacy-related-changes-coming-to-css-vistited/ | stringent restrictions] in modern browsers to prevent snoopy web sites from brute-forcing your browsing history. (See the link for the method, which explains the restrictions.) <h3 id="search">Enable Forum Search</h3> One of the underlying assumptions of the forum feature is that you will want to be able to search the forum archives, so the <tt>/forum</tt> page always includes a search box. Since that depends on search being enabled on the Fossil repository, Fossil warns that search is disabled until you go into Admin → Search and enable the "Search Forum" setting. You may want to enable some of the other Fossil search features while you're in there. All of this does come at some CPU and I/O cost, which is why it's disabled by default. <h3 id="sso">Single Sign-On</h3> If you choose to host your discussion forums within the same repository as your project's other Fossil-managed content, you inherently have a single sign-on system. Contrast third-party mailing list and forum software where you either end up with two separate user tables and permission sets, or you must go to significant effort to integrate the two login systems. You may instead choose to host your forums in a Fossil repository separate from your project's main Fossil repository. A good reason to do this is that you have a public project where very few of those participating in the forum have special capability bits for project assets managed by Fossil, so you wish to segregate the two user sets. Yet, what of the users who will have logins on both repositories? Some users will be trusted with access to the project's main Fossil repository, and these users will probably also participate in the project's Fossil-hosted forum. Fossil has a feature to solve this problem: [./caps/login-groups.md | login groups]. <h3 id="alerts">Email Alerts (a.k.a. Notifications)</h3> Internet email service has become rather complicated since its initial simple and insecure implementation decades ago. Fossil's role in all of this is rather small at the moment, but the details of the integration are complex enough to justify [./alerts.md | a separate document]. (The other reason that document is separate is that Fossil's email alerts system also gets used by features of Fossil other than the forum.) <h2 id="access">Accessing the Forum</h2> There are many paths to a repository's Fossil forum: <ul> <li> If you're using the default Fossil skin as shipped with Fossil 2.7+ or one [#skin | updated] to support it, there is a Forum button in the navbar which appears for users able to access the forum. With the default skin, that button will only appear if the user's browser window is at least 1200 pixels wide. The Fossil admin can adjust this limit in the skin's CSS section, down near the bottom in the definition of the `wideonly` style. </li> <li>The other stock skins have this button in them as of 2.7 as well, without the screen width restriction, since the navbar in those skins wraps on narrow screens more gracefully than the default skin does.</li> <li>Users who set up their Fossil repository under prior versions and who now have local skin changes they don't want to overwrite by reverting to the stock 2.7 version of the skin they chose to start with can easily [#skin | edit their skin] to include these links.</li> <li>A "Forum" link appears in the drop-down panel when you click the default skin's hamburger menu (☰) while logged in as any user with one or more of the [#caps | user capabilities listed above].</li> <li>That same link also appears on the repository's <tt>/sitemap</tt> page, since it provides the content for the hamburger menu's panel.</li> </ul> <h2 id="moderation">How Moderation Works</h2> In this section, we're going to call all of the following a "forum update:" * create a new post * reply to an existing post * edit a post or reply When a person with the normal <b>WrForum</b> capability updates the forum, Fossil saves the update in its block chain, but this update is impermanent because of two other table updates made at the same time: <ol> <li>Fossil saves the update artifact's ID in its <tt>private</tt> table, preventing Fossil from sending such artifacts to any of the repository's clones. (This is the same mechanism behind [./private.wiki | private branches].)</li> <li>Fossil also adds a reference to that artifact in the <tt>modreq</tt> table, which backs the moderation feature. This is what causes Fossil to leave out the Reply button when rendering that post's HTML in the forum's web interface.</li> </ol> When a moderator approves an update, Fossil deletes these table entries, making the update [./shunning.wiki | semi-permanent]. This changes how Fossil renders the HTML for that update. It also means the artifact will now sync to users with <b>[./caps/ref.html#g | Clone]</b> capability. When a forum user edits a moderator-approved artifact, what actually happens under the hood is that Fossil writes another artifact to the repository which refers to the original version as its parent, causing Fossil UI to present the new version instead of the original. The original version remains in the repository, just as with historical checkins. The parent must remain in the repository for referential integrity purposes. When you "Delete" a moderator-approved post or reply through Fossil UI, it's actually an edit with blank replacement content. The only way to truly delete such artifacts is through [./shunning.wiki | shunning]. When a user with <b>WrTForum</b> capability updates the forum, it happens in the same way except that Fossil skips the <tt>private</tt> and <tt>modreq</tt> table insertions. When a moderator rejects an update, that artifact is unceremoniously removed from the tip of the block chain. This is safe because Fossil prevents replies to a reply or post awaiting moderator approval, so referential integrity cannot be harmed. Rejecting an edit is even safer, since the original post remains behind, so that replies continue to refer to that original post. <h2 id="mod-user">Using the Moderation Feature</h2> Having described all of the work that Fossil performs under the hood on behalf of its users, we can now give the short list of work left for the repository's administrators and moderators: <ol> <li>Add the <b>[./caps/ref.html#5 | ModForum]</b> capability to any of your users who should have this ability. You don't need to do this for any user with <b>[./caps/ref.html#s | Setup]</b> or <b>[./caps/ref.html#a | Admin]</b> capability; it's [/artifact/b16221ffb736caa2?ln=1246-1257 | already included].</li> <li>When someone updates the forum, an entry will appear in the timeline if the type filter is set to "Forum" or "Any Type". If that user has only the <b>WrForum</b> capability, any other user with the <b>ModForum</b> capability will see a conditional link appear at the top of the main forum page: "Moderation Requests". Clicking this takes the moderator to the <tt>/modreq</tt> page. A moderator may wish to keep the main forum page open in a browser tab, reloading it occasionally to see when the "Moderation Requests" link reappears.</li> <li>A moderator viewing an update pending moderation sees two buttons at the bottom, "Approve" and "Reject" in place of the "Delete" button that the post's creator sees. Beware that both actions are durable and have no undo. Be careful!</li> </ol> <h2 id="close-post">Closing Forum Posts</h2> As of version 2.23, the forum interface supports the notion of "closing" posts. By default, only users with the [./caps/index.md|'s' and 'a' capabilities] may close or re-open posts, or reply to closed posts. If the [/help?cmd=forum-close-policy|forum-close-policy configuration option] is enabled then users with [./caps/index.md|forum-moderator permissions] may also perform those actions. Closing a post has the following implications: * Only authorized users may edit or respond to such posts, recursively through all responses of that post. * Only authorized users may re-open a closed post. A forum thread may be closed at any given point in the conversation, not just the starting point of the thread, and affects that specific post and all existing responses to it. Note that closing a post is effectively an "advisory lock" and may be bypassed. Any user, admin or otherwise, who can push changes to a repository may bypass closure of a post by setting the appropriate artifact tags on a local copy and pushing those changes to a remote copy of the forum. The option to close a post, permissions permitting, appears as a "Close" button on the currently-selected post. If the selected post is alread closed, a "Re-open" button will be shown instead. In order to re-open a post, <em>the closed post itself</em> must be selected. Selecting a response to that post, which is implicitly closed by the closure of its parent, will <em>not</em> offer a re-open option. Though forum users are permitted to delete their own posts, they are not permitted, without appropriate permissions, to close their own posts. This is intentional, as closing one's own post can be used to antagonize other forum users. For example, by posting something trollish or highly contraversial in nature and closing the post to further responses. |
Added www/foss-cklist.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 100 101 102 103 104 105 106 107 108 109 110 111 112 | <title>Checklist For Successful Open-Source Projects</title> <nowiki> 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>). 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. This checklist is an inversion of Tom's original post in that it strives to say what the project should do in order to succeed rather than what it should not do to avoid failure. The point values are omitted. See also: <ul> <li><a href="http://offog.org/articles/packaging/"> http://offog.org/articles/packaging/</a> <li> <a href="http://www.gnu.org/prep/standards/standards.html#Managing-Releases"> http://www.gnu.org/prep/standards/standards.html#Managing-Releases</a> </ul> <hr> <ol> <li>The source code size is less than 100 MB, uncompressed. <li>The project uses a Version Control System (VCS). <ol type="a"> <li>The VCS has a working web interface. <li>There is documentation on how to use the VCS. <li>The VCS is general-purpose, not something hacked together for this one project. </ol> <li>The project comes with documentation on how to build from source and that documentation is lucid, correct, and up-to-date. <li>The project is configurable using an autoconf-generated configure script, or the equivalent, and does not require: <ol type="a"> <li>Manually editing flat files <li>Editing code header files </ol> <li>The project should be buildable using commonly available and standard tools like "make". <li>The project does not depend on 3rd-party proprietary build tools. <li>The project is able to dynamically link against standard libraries such as zlib and libjpeg. <ol type="a"> <li>The project does not ship with the sources to standard libraries. <i>(On the Fossil project, we will allow the SQLite library sources to be included in the source tree as long as a system-installed SQLite library can be used in its stead.)</i> <li>The project does not use slightly modified versions of standard libraries. Any required bug fixes in standard libraries are pushed back upstream. </ol> <li>"make install" works and can be configured to target any directory of the installer's choosing. <ol type="a"> <li>The project contains no unconfigurable hard-coded pathnames like "/opt" or "/usr/local". <li>After installation, the source tree can be moved or deleted and the application will continue working. </ol> <li>The source code uses only \n for line endings, not \r\n. <li>The code does not depend on any special compiler features or bugs. <li>The project has a mailing list and announces releases on the mailing list. <li>The project has a bug tracker. <li>The project has a website. <li>Release version numbers are in the traditional X.Y or X.Y.Z format. <li>Releases can be downloaded as tarball using gzip or bzip2 compression. <li>Releases unpack into a versioned top-level directory. (ex: "projectname-1.2.3/"). <li>A statement of license appears at the top of every source code file and the complete text of the license is included in the source code tarball. <li>There are no incompatible licenses in the code. <li>The project has not been blithely proclaimed "public domain" without having gone through the tedious and exacting legal steps to actually put it in the public domain. <li>There is an accurate change log in the code and on the website. <li>There is documentation in the code and on the website. </ol> |
Added www/fossil-from-msvc.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 | <h1>Integrating Fossil in the Microsoft Express 2010 IDE</h1> <i>Contributed by Gilles Ganault on 2013-05-24.</i> The Express version of Visual Studio doesn't support add-in's and plug-in's, but it's not an issue since it's still possible to use Fossil through the External Tools menu and Fossil is a CLI application anyway: <ol type="1"> <li>Tools > Settings > Expert Settings</li> <li>Tools > External Tools, where the items in this list map to "External Tool X" that we'll add to our own Fossil menu later: </li> <ol type="1"> <li>Rename the default "[New Tool 1]" to eg. "Commit" 2. </li> <li>Change Command to where Fossil is located eg. "c:\fossil.exe"</li> <li>Change Arguments to the required command, eg. "commit -m". The user will be prompted to type the comment that Commit expects</li> <li>Set "Initial Directory" to point it to the work directory where the source files are currently checked out by Fossil (eg. c:\Workspace). It's also possible to use system variables such as "$(ProjectDir)" instead of hard-coding the path</li> <li>Check "Prompt for arguments", since Commit requires typing a comment. Useless for commands like Changes that don't require arguments</li> <li>Uncheck "Close on Exit", so we can see what Fossil says before closing the DOS box. Note that "Use Output Window" will display the output in a child window within the IDE instead of opening a DOS box</li> <li>Click on OK</li> </ol> <li>Tools > Customize > Commands</li> <ol type="1"> <li>With "Menu bar = Menu Bar" selected, click on "Add New Menu". A new "Fossil" menu is displayed in the IDE's menu bar</li> <li>Click on "Modify Selection" to rename it "Fossil", and...</li> <li>Use the "Move Down" button to move it lower in the list</li> </ol> <li>Still in Customize dialog: In the "Menu bar" combo, select the new Fossil menu you just created, and Click on "Add Command...": From Categories, select Tools, and select "External Command 1". Click on Close. It's unfortunate that the IDE doesn't say which command maps to "External Command X".</li> </ol> |
Added www/fossil-is-not-relational.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 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 | # Fossil is not Relational ***An Introduction to the Fossil Data Model*** Upon hearing that Fossil is based on sqlite, it's natural for people unfamiliar with its internals to assume that Fossil stores its SCM-relevant data in a database-friendly way and that the SCM history can be modified via SQL. The truth, however, is *far stranger than that.* This document introduces, at a relatively high level: 1) The underlying enduring and immutable data format, which is independent of any specific storage engine. 2) The `blob` table: Fossil's single point of SCM-relevant data storage. 3) The transformation of (1) from its immutable raw form to a *transient* database-friendly form. 4) Some of the consequences of this model. # Part 1: Artifacts ```pikchr center AllObjects: [ A: file "Artifacts" fill lightskyblue; down; move to A.s; move 50%; F: file "Client" "files"; right; move 1; up; move 50%; B: cylinder "blob table" right; arrow from A.e to B.w; arrow from F.e to B.w; arrow dashed from B.e; C: box rad 0.1 "Crosslink" "process"; arrow AUX: cylinder "Auxiliary" "tables" arc -> cw dotted from AUX.s to B.s; ] # end of AllObjects ``` The centerpiece of Fossil's architecture is a data format which describes what we call "artifacts." Each artifact represents the state of one atomic unit of SCM-relevant data, such as a single checkin, a single wiki page edit, a single modification to a ticket, creation or cancellation of tags, and similar SCM constructs. In the cases of checkins and ticket updates, an artifact may record changes to multiple files resp. ticket fields, but the change as a whole is atomic. Though we often refer to both fossil-specific SCM data and client-side content as artifacts, this document uses the term artifact solely for the former purpose. From [the data format's main documentation][dataformat]: > The global state of a fossil repository is kept simple so that it > can endure in useful form for decades or centuries. A fossil > repository is intended to be readable, searchable, and extensible by > people not yet born. [dataformat]: ./fileformat.wiki This format has the following major properties: - It is <u>**syntactically simple**</u>, easily and efficiently parsable in any programming language. It is also entirely human-readable. - It is <u>**immutable**</u>. An artifact is identified by its unique hash value. Any modification to an artifact changes that hash, thereby changing its identity. - It is <u>**not generic**</u>. It is custom-made for its purpose and makes no attempt at providing a generic format. It contains *only* what it *needs* to function, with zero bloat. - It <u>**holds all SCM-relevant data except for client-level file content**</u>, the latter instead being referenced by their unique hash values. Storage of the client-side content is an implementation detail delegated to higher-level applications. - <u>**Auditability**</u>. By following the hash references in artifacts it is possible to unambiguously trace the origin of any modification to the SCM state. Combined with higher-level tools (specifically, Fossil's database), this audit trail can easily be traced both backwards and forwards in time, using any given version in the SCM history as a starting point. Notably, the artifact file format <u>does not</u>... - Specify any specific storage mechanism for the SCM's raw bytes, which includes both artifacts themselves and client-side file content. The file format refers to all such content solely by its unique hash value. - Specify any optimimizations such as storing file-level changes as deltas between two versions of that content. Such aspects are all considered to be implementation details of higher-level applications (be they in the main fossil binary or a hypothetical 3rd-party application), and have no effect on the underlying artifact data model. That said, in Fossil: - All raw byte content (artifacts and client files) is stored in the `blob` database table. - Fossil uses delta and zlib compression to keep the storage size of changes from one version of a piece of content to the next to a minimum. ## Sidebar: SCM-relevant vs Non-SCM-relevant State Certain data in Fossil are "SCM-relevant" and certain data are not. In short, SCM-relevant data are managed in a way consistent with controlled versioning of that data. Conversely, non-SCM-relevant data are essentially any state neither specified by nor unambiguously refererenced by the artifact file format and are therefore not versioned. SCM-relevant state includes: - Any and all data stored in the bodies of artifacts. This includes, but is not limited to: wiki/ticket/forum content, tags, file names and Fossil-side permissions, the name of each user who introduces any given artifact into the data store, the timestamp of each such change, the inheritance tree of checkins, and many other pieces of metadata. - Raw file content of versioned files. These data are external to artifacts, which refer to them by their hashes. How they are stored is not the concern of the data model, but (spoiler alert!) Fossil stores in them an sqlite database, one record per distinct hash, in its `blob` table (which we will cover more very soon). Non-SCM-relevant state includes: - Fossil's list of users and their metadata (permissions, email address, etc.). Artifacts themselves reference users only by their user names. Artifacts neither care whether, nor guarantee that, user "drh" in one artifact is in fact the same "drh" referenced in another artifact. - All Fossil UI configuration, e.g. the site's skin, config settings, and project name. - In short, any tables in a Fossil repository file except for the `blob` table. Most, but not all, of these tables are transient caches for the data specified by the artifact files (which are stored in the `blob` table), and can safely be destroyed and rebuilt from the collection of artifacts with no loss of state to the repository. *All* of them, except for `blob` and `delta`, can be destroyed with no loss of *SCM-relevant* data. ## Terminology Hair-splitting: Manifest vs. Artifact We sometimes refer to artifacts as "manifests," which is technically a term for artifacts which record checkins. The various other artifact types are arguably not "manifests," but are sometimes referred to as such because the internal APIs use that term. ## A Very Basic Example The following artifact, truncated for brevity, represents a typical checkin artifact (a.k.a. a manifest): ``` C Bug\sfix\sin\sthe\slocal\sdatabase\sfinder. D 2007-07-30T13:01:08 F src/VERSION 24bbb3aad63325ff33c56d777007d7cd63dc19ea F src/add.c 1a5dfcdbfd24c65fa04da865b2e21486d075e154 F src/blob.c 8ec1e279a6cd0cfd5f1e3f8a39f2e9a1682e0113 <SNIP> F www/selfcheck.html 849df9860df602dc2c55163d658c6b138213122f P 01e7596a984e2cd2bc12abc0a741415b902cbeea R 74a0432d81b956bfc3ff5a1a2bb46eb5 U drh Z c9dcc06ecead312b1c310711cb360bc3 ``` Each line is a single data record called a "card." The first letter of each line tells us the type of data stored on that line and the following space-separated tokens contain the data for that line. Tokens which themselves contain spaces (notably the checkin comment) have those escaped as `\s`. The raw text of wiki pages/comments, forum posts, and ticket bodies/comments is stored directly in the corresponding artifact, but is stored in a way which makes such escaping unnecessary. The hashes seen above are a critical component of the architecture: - The `F` (file) records refer to the content of those files by the hash of that content. Where that content is stored is *not* specified by the data model. - The `P` (parent) line is the hash code of the parent version (itself an artifact). - The `Z` line is a hash of all of the content of *this artifact* which precedes the `Z` line. Thus any change to the content of an artifact changes both the artifact's identity (its hash) and its `Z` value, making it impossible to inject modified artifacts into an existing artifact tree. - The `R` line is yet another consistency-checking hash which we won't go into here except to say that it's an internal consistency check/line of defense against modification of file content referenced by the artifact. # Part 2: The `blob` Table ```pikchr center AllObjects: [ A: file "Artifacts"; down; move to A.s; move 50%; F: file "Client" "files" fill lightskyblue; right; move 1; up; move 50%; B: cylinder "blob table" fill lightskyblue; right; arrow from A.e to B.w; arrow from F.e to B.w; arrow dashed from B.e; C: box rad 0.1 "Crosslink" "process"; arrow AUX: cylinder "Auxiliary" "tables" arc -> cw dotted from AUX.s to B.s; ] # end of AllObjects ``` The `blob` table is the core-most storage of a Fossil repository database, storing all SCM-relevant data (and *only* SCM-relevant data). Each row of this table holds a single artifact or the content for a single version of a single client-side file. Slightly truncated for clarity, its schema contains the following fields: - **`uuid`**: the hash code of the blob's contents. - **`rid`**: a unique integer key for this record. This is how the blob table is mapped to other (transient) tables, but the RIDs are specific to one given copy of a repository and must not be used for cross-repository referencing. The RID is a private/internal value of no use to a user unless they're building SQL queries for use with the Fossil db schema. - **`size`**: the size, in bytes, of the blob's contents, or -1 for "phantom" blobs (those which Fossil knows should exist because it's seen them referenced somewhere, but for which it has not been given any content). - **`content`**: the blob's raw content bytes, with the caveat that Fossil is free to store it in an "alternate representation." Specifically, the `content` field often holds a zlib-compressed delta from a previous version of the blob's content (a separate entry in the `blob` table), and an auxiliary table named `delta` maps such blobs to their previous versions, such that Fossil can reconstruct the real content from them by applying the delta to its previous version (and such deltas may be chained). Thus extraction of the content from this field cannot be performed via vanilla SQL, and requires a Fossil-specific function which knows how to convert any internal representations of the content to its original form. ## Sidebar: How does `blob` Distinguish Between Artifacts and Client Content? Notice that the `blob` table has no flag saying "this record is an artifact" or "this record is client data." Similarly, there is no place in the database dedicated to keeping track of which `blob` records are artifacts and which are file content. That said, (A) the type of a blob can be implied via certain table relationships and (B) the `event` table (the `/timeline`'s main data source) incidentally has a list of artifacts and their sub-types (checkin, wiki, tag, etc.). However, given that all of those relationships, including the timeline, are *transient*, how can Fossil distinguish between the two types of data? Fossil's artifact format is extremely rigid and is *strictly* enforced internally, with zero room provided for leniency. Every artifact which is internally created is re-parsed for validity before it is committed to the database, making it impossible that Fossil can inject an invalid artifact into the repository. Because of the strictness of the artifact parser, the chances that any given piece of arbitrary client data could be successfully parsed as an artifact, even if it is syntactically 99% similar to an artifact, are *effectively zero*. Thus Fossil's rule of interpreting the contents of the blob table is: if it can be parsed as an artifact, it *is* an artifact, else it is opaque client-side data. That rule is most often relevant in operations like `rebuild` and `reconstruct`, both of which necessarily have to sort out artifacts and non-artifact blobs from arbitrary collections of blobs. It is, in fact, possible to store an artifact unrelated to the current repository in that repository, and it *will be parsed and processed as an artifact* (see below), but it likely refers to other artifacts or blobs which are not part of the current repository, thereby possibly introducing "strange" data into the UI. If this happens, it's potentially slightly confusing but is functionally harmless. # Part 3: Crosslinking ```pikchr center AllObjects: [ A: file "Artifacts"; down; move to A.s; move 50%; F: file "Client" "files"; right; move 1; up; move 50%; B: cylinder "blob table" right; arrow from A.e to B.w; arrow from F.e to B.w; arrow dashed from B.e; C: box rad 0.1 "Crosslink" "process" fill lightskyblue; arrow AUX: cylinder "Auxiliary" "tables" fill lightskyblue; arc -> cw dotted from AUX.s to B.s; ] # end of AllObjects ``` Once an artifact is stored in the `blob` table, how does one perform SQL queries against its plain-text format? In short: *One Does Not Simply Query the Artifacts*. Crosslinking, as its colloquially known, is a one-way processing step which transforms an immutable artifact's state into something database-friendly. Crosslinking happens automatically every time Fossil generates, or is given, a new artifact. Crosslinking of any given artifact may update many different auxiliary tables, *all* of which are transient in the sense that they may be destroyed and then recreated by crosslinking all artifacts from the `blob` table (which is exactly what the `rebuild` command does). The overwhelming majority of individual database records in any Fossil repository are found in these transient auxiliary tables, though the `blob` table tends to account for the overwhelming majority of a repository's disk space. This approach to mapping data from artifacts to the db gives Fossil the freedom to change its database model, effectively at will, with minimal client-side disruption (at most, a call to `rebuild`). This allows, for example, Fossil to take advantage of new improvements in sqlite without affecting compatibility with older repositories. Auxiliary tables hold data mappings such as: - Child/parent relationships of checkins. (The `plink` table.) - Records of file names and changes to files. (The `mlink` and `filename` tables.) - Timeline entries. (The `event` table.) And numerous other bits and pieces. The many auxiliary tables maintained by the app-level code reference the `blob` table via its RID field, as that's far more efficient than using hashes (`blob.uuid`) as foreign keys. The contexts of those auxiliary data unambiguously tell us whether the referenced blobs are artifacts or file content, so there is no efficiency penalty there for hosting both opaque blobs and artifacts in the `blob` table. The complete SQL schemas for the core-most auxiliary tables can be found at: [](/finfo/src/schema.c?ci=trunk) Noting, however, that all database tables are effectively internal APIs, with no API stability guarantees and subject to change at any time. Thus their structures generally should not be relied upon in client-side scripts. # Part 4: Implications and Consequences of the Model *Some* of the implications and consequences of Fossil's data model combined with the higher-level access via SQL include: - **Provable immutability of history.** Fossil offers only one option for modifying history: "shunning" is the forceful removal of an artifact from the `blob` table and the creation of a db record stating that the shunned hash may no longer be synced into this repository. Shunning effectively leaves a hole in the SCM history, and is only intended to be used for removal of illegal, dangerous, or private information which should never have been added to the repository. - **Complete separation of SCM-relevant data and app-level data structures**. This allows the application to update its structures at will without significant backwards-compatibility concerns. In Fossil's case, "data structures" primarily refers to the SQL schema. Bringing a given repository schema up to date vis a vis a given fossil binary version simply means rebuilding the repository with that fossil binary. There are exceptionally rare cases, namely the switch from SHA1 to SHA3-256 ushered in with Fossil 2.0, which can lead to true incompatibility. e.g. a Fossil 1.x client cannot use a repository database which contains SHA3 hashes, regardless of a rebuild. - **Two-way compatibility with other hypothetical clients** which also implement the same underlying data model. So far there are none, but it's conceivably possible. - **Provides a solid basis for reporting.** Fossil's real-time metrics and reporting options are arguably the most powerful and flexible yet seen in an SCM. - Very probably several more things. |
Added www/fossil-v-git.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 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 | <title>Fossil Versus Git</title> <h2>1.0 Don't Stress!</h2> The feature sets of Fossil and [http://git-scm.com | Git] overlap in many ways. Both are [https://en.wikipedia.org/wiki/Distributed_version_control | distributed version control systems] which store a tree of check-in objects to a local repository clone. In both systems, the local clone starts out as a full copy of the remote parent. New content gets added to the local clone and then later optionally pushed up to the remote, and changes to the remote can be pulled down to the local clone at will. Both systems offer diffing, patching, branching, merging, cherry-picking, bisecting, private branches, a stash, etc. Fossil has inbound and outbound Git conversion features, so if you start out using one DVCS and later decide you like the other better, you can easily [./inout.wiki | move your version-controlled file content].¹ In this document, we set all of that similarity and interoperability aside and focus on the important differences between the two, especially those that impact the user experience. Keep in mind that you are reading this on a Fossil website, and though we try to be fair, the information here might be biased in favor of Fossil, if only because we spend most of our time using Fossil, not Git. Ask around for second opinions from people who have used <em>both</em> Fossil and Git. If you want a more practical, less philosophical guide to moving from Git to Fossil, see our [./gitusers.md | Git to Fossil Translation Guide]. <h2>2.0 Differences Between Fossil And Git</h2> Differences between Fossil and Git are summarized by the following table, with further description in the text that follows. <table style="width: fit-content"> <tr><th>GIT</th><th>FOSSIL</th><th>more</th></tr> <tr> <td>File versioning only</td> <td> VCS, tickets, wiki, docs, notes, forum, chat, UI, [https://en.wikipedia.org/wiki/Role-based_access_control|RBAC] </td> <td><a href="#features">2.1 ↓</a></td> </tr> <tr> <td>A federation of many small programs</td> <td>One self-contained, stand-alone executable</td> <td><a href="#selfcontained">2.2 ↓</a></td> </tr> <tr> <td>Custom key/value data store</td> <td>[https://sqlite.org/mostdeployed.html|The most used SQL database in the world]</td> <td><a href="#durable">2.3 ↓</a></td> </tr> <tr> <td>Runs natively on POSIX systems</td> <td>Runs natively on both POSIX and Windows</td> <td><a href="#portable">2.4 ↓</a></td> </tr> <tr> <td>Bazaar-style development</td> <td>Cathedral-style development</td> <td><a href="#devorg">2.5.1 ↓</a></td> </tr> <tr> <td>Designed for Linux kernel development</td> <td>Designed for SQLite development</td> <td><a href="#scale">2.5.2 ↓</a></td> </tr> <tr> <td>Many contributors</td> <td>Select contributors</td> <td><a href="#contrib">2.5.3 ↓</a></td> </tr> <tr> <td>Focus on individual branches</td> <td>Focus on the entire tree of changes</td> <td><a href="#branches">2.5.4 ↓</a></td> </tr> <tr> <td>One check-out per repository</td> <td>Many check-outs per repository</td> <td><a href="#checkouts">2.6 ↓</a></td> </tr> <tr> <td>Remembers what you should have done</td> <td>Remembers what you actually did</td> <td><a href="#history">2.7 ↓</a></td> </tr> <tr> <td>Commit first</td> <td>Test first</td> <td><a href="#testing">2.8 ↓</a></td> </tr> <tr> <td>SHA-1 or SHA-2</td> <td>SHA-1 and/or SHA-3, in the same repository</td> <td><a href="#hash">2.9 ↓</a></td> </tr> </table> <h3 id="features">2.1 Featureful</h3> Git provides file versioning services only, whereas Fossil adds an integrated [./wikitheory.wiki | wiki], [./bugtheory.wiki | ticketing & bug tracking], [./embeddeddoc.wiki | embedded documentation], [./event.wiki | technical notes], a [./forum.wiki | web forum], and a [./chat.md | chat service], all within a single nicely-designed [./customskin.md|skinnable] web [/help?cmd=ui|UI], protected by [./caps/ | a fine-grained role-based access control system]. These additional capabilities are available for Git as 3rd-party add-ons, but with Fossil they are integrated into the design, to the point that it approximates "[https://github.com/ | GitHub]-in-a-box." Even if you only want straight version control, Fossil has affordances not available in Git. For instance, Fossil can do operations over all local repo clones and check-out directories with a single command. You can say "<tt>fossil all sync</tt>" on a laptop prior to taking it off the network hosting those repos, as before going on a trip. It doesn't matter if those repos are private and restricted to your company network or public Internet-hosted repos, you get synced up with everything you need while off-network. You get the same capability with several other Fossil sub-commands as well, such as "<tt>fossil all changes</tt>" to get a list of files that you forgot to commit prior to the end of your working day, across all repos. Whenever Fossil is told to modify the local checkout in some destructive way ([/help?cmd=rm|fossil rm], [/help?cmd=update|fossil update], [/help?cmd=revert|fossil revert], etc.) Fossil remembers the prior state and is able to return the check-out directory to that state with a <tt>fossil undo</tt> command. While you cannot undo a commit in Fossil — [#history | on purpose!] — as long as the change remains confined to the local check-out directory only, Fossil makes undo [https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things|easier than in Git]. For developers who choose to self-host projects rather than rely on a 3rd-party service such as GitHub, Fossil is much easier to set up: the stand-alone Fossil executable together with a [./server/any/cgi.md|2-line CGI script] suffice to instantiate a full-featured developer website. To accomplish the same using Git requires locating, installing, configuring, integrating, and managing a wide assortment of separate tools. Standing up a developer website using Fossil can be done in minutes, whereas doing the same using Git requires hours or days. Fossil is small, complete, and self-contained. If you clone [https://github.com/git/git|Git's self-hosting repository], you get just Git's source code. If you clone Fossil's self-hosting repository, you get the entire Fossil website — source code, documentation, ticket history, and so forth.² That means you get a copy of this very article and all of its historical versions, plus the same for all of the other public content on this site. <h3 id="selfcontained" name="selfcontained">2.2 Self Contained</h3> Git is actually a collection of many small tools, each doing one small part of the job, which can be recombined (by experts) to perform powerful operations. Git has a lot of complexity and many dependencies, so that most people end up installing it via some kind of package manager, simply because the creation of complicated binary packages is best delegated to people skilled in their creation. Normal Git users are not expected to build Git from source and install it themselves. Fossil is a single self-contained stand-alone executable which depends only on common platform libraries in its default configuration. To install one of [https://fossil-scm.org/home/uv/download.html | our precompiled binaries], unpack the executable from the archive and put it somewhere in your <tt>PATH</tt>. To uninstall it, delete the executable. This policy is particularly useful when running Fossil inside a restrictive container, anything from [./chroot.md | classic chroot jails] to modern [https://en.wikipedia.org/wiki/OS-level_virtualization | OS-level virtualization mechanisms] such as [https://en.wikipedia.org/wiki/Docker_(software) | Docker]. Our [./containers.md | stock container image] is under 8 MB when uncompressed and running. It contains nothing but a single statically-linked binary. If you build a dynamically linked binary instead, Fossil's on-disk size drops to around 6 MB, and it's dependent only on widespread platform libraries with stable ABIs such as glibc, zlib, and openssl. Full static linking is easier on Windows, so our precompiled Windows binaries are just a ZIP archive containing only "<tt>fossil.exe</tt>". There is no "<tt>setup.exe</tt>" to run. Fossil is easy to build from sources. Just run "<tt>./configure && make</tt>" on POSIX systems and "<tt>nmake /f Makefile.msc</tt>" on Windows. Contrast a basic installation of Git, which takes up about 15 MiB on Debian 10 across 230 files, not counting the contents of <tt>/usr/share/doc</tt> or <tt>/usr/share/locale</tt>. If you need to deploy to any platform where you cannot count on facilities like the POSIX shell, Perl interpreter, and Tcl/Tk platform needed to fully use Git as part of the base platform, the full footprint of a Git installation extends to more like 45 MiB and thousands of files. This complicates several common scenarios: Git for Windows, chrooted Git servers, Docker images... Some say that Git more closely adheres to the Unix philosophy, summarized as "many small tools, loosely joined," but we have many examples of other successful Unix software that violates that principle to good effect, from Apache to Python to ZFS. We can infer from that that this is not an absolute principle of good software design. Sometimes "many features, tightly-coupled" works better. What actually matters is effectiveness and efficiency. We believe Fossil achieves this. The above size comparisons aren't apples-to-apples anyway. We've compared the size of Fossil with all of its [#features | many built-in features] to a fairly minimal Git installation. You must add a lot of third-party software to Git to give it a Fossil-equivalent feature set. Consider [https://about.gitlab.com/|GitLab], a third-party extension to Git wrapping it in many features, making it roughly Fossil-equivalent, though [https://docs.gitlab.com/ee/install/requirements.html|much more resource hungry] and hence more costly to run than the equivalent Fossil setup. [https://hub.docker.com/r/gitlab/gitlab-ce/ | The official GitLab Community Edition container] currently clocks in at 2.66 GiB! GitLab's requirements are easy to accept when you're dedicating a local rack server or blade to it, since its minimum requirements are more or less a description of the smallest thing you could call a "server" these days, but when you go to host that in the cloud, you can expect to pay about 8 times as much to comfortably host GitLab as for Fossil.³ This difference is largely due to basic technology choices: Ruby and PostgreSQL vs C and SQLite. The Fossil project itself is [./selfhost.wiki|hosted on a small and inexpensive VPS]. A bare-bones $5/month VPS or a spare Raspberry Pi is sufficient to run a full-up project site, complete with tickets, wiki, chat, and forum, in addition to being a code repository. <h3 id="durable" name="database">2.3 Query Language</h3> The baseline data structures for Fossil and Git are the same, modulo formatting details. Both systems manage a [https://en.wikipedia.org/wiki/Directed_acyclic_graph | directed acyclic graph] (DAG) of [https://en.wikipedia.org/wiki/Merkle_tree | Merkle tree] structured check-in objects. Check-ins are identified by a cryptographic hash of the check-in contents, and each check-in refers to its parent via the parent's hash. The difference is that Git stores its objects as individual files in the <tt>.git</tt> folder or compressed into bespoke key/value [https://git-scm.com/book/en/v2/Git-Internals-Packfiles|pack-files], whereas Fossil stores its objects in a [https://www.sqlite.org/|SQLite] database file which provides ACID transactions and a high-level query language. This difference is more than an implementation detail. It has important practical consequences. One notable consequence is that it is difficult to find the descendants of check-ins in Git. One can easily locate the ancestors of a particular Git check-in by following the pointers embedded in the check-in object, but it is difficult to go the other direction and locate the descendants of a check-in. It is so difficult, in fact, that neither native Git nor GitHub provide this capability short of crawling the [https://www.git-scm.com/docs/git-log|commit log]. With Fossil, on the other hand, finding descendants is a simple SQL query. It is common in Fossil to ask to see [/timeline?df=release&y=ci|all check-ins since the last release]. Git lets you see "what came before". Fossil makes it just as easy to also see "what came after". Leaf check-ins in Git that lack a "ref" become "detached," making them difficult to locate and subject to garbage collection. This [http://gitfaq.org/1/01/what-is-a-detached-head/|detached head state] problem has caused grief for [https://www.google.com/search?q=git+detached+head+state | many Git users]. With Fossil, detached heads are simply impossible because we can always find our way back into the Merkle tree using one or more of the relations in the SQL database. The SQL query capabilities of Fossil make it easier to track the changes for one particular file within a project. For example, you can easily find [/finfo/www/fossil-v-git.wiki|the complete edit history of this one document], or even [/finfo/www/fossil-v-git.wiki?ubg|the same history color-coded by committer], Both questions are simple SQL query in Fossil, with procedural code only being used to format the result for display. The same result could be obtained from Git, but because the data is in a key/value store, much more procedural code has to be written to walk the data and compute the result. And since that is a lot more work, the question is seldom asked. The ease of querying Fossil data using SQL means that status or history information about the project under management is easier to obtain. Being easier means that it is more likely to happen. Fossil reports tend to be more detailed and useful. Compare [/timeline?c=6df7a853ec16865b|this Fossil timeline] to [https://github.com/drhsqlite/fossil-mirror/commits/master?after=f720c106d297ca1f61bccb30c5c191b88a626d01+34 | its closest equivalent in GitHub]. Judge for yourself: which of those reports is more useful to a developer trying to understand what happened? The bottom line is that even though Fossil and Git are built around the same low-level data structure, the use of SQL to query this data makes the data more accessible in Fossil, resulting in more detailed information being available to the user. This improves situational awareness and makes working on the project easier. <h3 id="portable">2.4 Portable</h3> Fossil is largely written in ISO C, almost purely conforming to the original 1989 standard. We make very little use of [https://en.wikipedia.org/wiki/C99|C99], and we do not knowingly make any use of [https://en.wikipedia.org/wiki/C11_(C_standard_revision)|C11]. Fossil does call POSIX and Windows APIs where necessary, but it's about as portable as you can ask given that ISO C doesn't define all of the facilities Fossil needs to do its thing. (Network sockets, file locking, etc.) There are certainly well-known platforms Fossil hasn't been ported to yet, but that's most likely due to lack of interest rather than inherent difficulties in doing the port. We believe the most stringent limit on its portability is that it assumes at least a 32-bit CPU and several megs of flat-addressed memory.â´ Fossil isn't quite as [https://www.sqlite.org/custombuild.html|portable as SQLite], but it's close. Over half of the C code in Fossil is actually an embedded copy of the current version of SQLite. Much of what is Fossil-specific after you set SQLite itself aside is SQL code calling into SQLite. The number of lines of SQL code in Fossil isn't large by percentage, but since SQL is such an expressive, declarative language, it has an outsized contribution to Fossil's user-visible functionality. Fossil isn't entirely C and SQL code. Its web UI [./javascript.md | uses JavaScript where necessary]. The server-side UI scripting uses a custom minimal [https://en.wikipedia.org/wiki/Tcl|Tcl] dialect called [https://fossil-scm.org/xfer/doc/trunk/www/th1.md|TH1], which is embedded into Fossil itself. Fossil's build system and test suite are largely based on Tcl.âµ All of this is quite portable. About half of Git's code is POSIX C, and about a third is POSIX shell code. This is largely why the so-called "Git for Windows" distributions (both [https://git-scm.com/download/win|first-party] and [https://gitforwindows.org/|third-party]) are actually an [https://www.msys2.org/wiki/Home/|MSYS POSIX portability environment] bundled with all of the Git stuff, because it would be too painful to port Git natively to Windows. Git is a foreign citizen on Windows, speaking to it only through a translator.ⶠWhile Fossil does lean toward POSIX norms when given a choice — LF-only line endings are treated as first-class citizens over CR+LF, for example — the Windows build of Fossil is truly native. The third-party extensions to Git tend to follow this same pattern. [https://docs.gitlab.com/ee/install/install_methods.html#microsoft-windows | GitLab isn't portable to Windows at all], for example. For that matter, GitLab isn't even officially supported on macOS, the BSDs, or uncommon Linuxes! We have many users who regularly build and run Fossil on all of these systems. <h3 id="vs-linux">2.5 Linux vs. SQLite</h3> Fossil and Git promote different development styles because each one was specifically designed to support the creator's main software development project: [https://en.wikipedia.org/wiki/Linus_Torvalds|Linus Torvalds] designed Git to support development of [https://www.kernel.org/|the Linux kernel], and [https://en.wikipedia.org/wiki/D._Richard_Hipp|D. Richard Hipp] designed Fossil to support the development of [https://sqlite.org/|SQLite]. Both projects must rank high on any objective list of "most important FOSS projects," yet these two projects are almost entirely unlike one another, so it is natural that the DVCSes created to support these projects also differ in many ways. In the following sections, we will explain how four key differences between the Linux and SQLite software development projects dictated the design of each DVCS's low-friction usage path. When deciding between these two DVCSes, you should ask yourself, "Is my project more like Linux or more like SQLite?" <h4 id="devorg">2.5.1 Development Organization</h4> Eric S. Raymond's seminal essay-turned-book "[https://en.wikipedia.org/wiki/The_Cathedral_and_the_Bazaar|The Cathedral and the Bazaar]" details the two major development organization styles found in [https://en.wikipedia.org/wiki/Free_and_open-source_software|FOSS] projects. As it happens, Linux and SQLite fall on opposite sides of this dichotomy. Differing development organization styles dictate a different design and low-friction usage path in the tools created to support each project. Git promotes the Linux kernel's bazaar development style, in which a loosely-associated mass of developers contribute their work through [https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows#_dictator_and_lieutenants_workflow|a hierarchy of lieutenants] who manage and clean up these contributions for consideration by Linus Torvalds, who has the power to cherry-pick individual contributions into his version of the Linux kernel. Git allows an anonymous developer to rebase and push specific locally-named private branches, so that a Git repo clone often isn't really a clone at all: it may have an arbitrary number of differences relative to the repository it originally cloned from. Git encourages siloed development. Select work in a developer's local repository may remain private indefinitely. All of this is exactly what one wants when doing bazaar-style development. Fossil's normal mode of operation differs on every one of these points, with the specific designed-in goal of promoting SQLite's cathedral development model: * <b>Personal engagement:</b> SQLite's developers know each other by name and work together daily on the project. * <b>Trust over hierarchy:</b> SQLite's developers check changes into their local repository, and these are immediately and automatically synchronized up to the central repository; there is no "[https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows#_dictator_and_lieutenants_workflow|dictator and lieutenants]" hierarchy as with Linux kernel contributions. D. Richard Hipp rarely overrides decisions made by those he has trusted with commit access on his repositories. Fossil allows you to give [./caps/admin-v-setup.md|some users] more power over what they can do with the repository, but Fossil [./caps/index.md#ucap | only loosely supports] the enforcement of a development organization's social and power hierarchies. Fossil is a great fit for [https://en.wikipedia.org/wiki/Flat_organization|flat organizations]. * <b>No easy drive-by contributions:</b> Git [https://www.git-scm.com/docs/git-request-pull|pull requests] offer a low-friction path to accepting [https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by contributions]. Fossil's closest equivalents are its unique [/help?cmd=bundle|bundle] and [/help?cmd=patch|patch] features, which require higher engagement than firing off a PR.â· This difference comes directly from the initial designed purpose for each tool: the SQLite project doesn't accept outside contributions from previously-unknown developers, but the Linux kernel does. * <b>No rebasing:</b> When your local repo clone syncs changes up to its parent, those changes are sent exactly as they were committed locally. [#history|There is no rebasing mechanism in Fossil, on purpose.] * <b>Sync over push:</b> Explicit pushes are uncommon in Fossil-based projects: the default is to rely on [/help?cmd=autosync|autosync mode] instead, in which each commit syncs immediately to its parent repository. This is a mode so you can turn it off temporarily when needed, such as when working offline. Fossil is still a truly distributed version control system; it's just that its starting default is to assume you're rarely out of communication with the parent repo. <br><br> This is not merely a reflection of modern always-connected computing environments. It is a conscious decision in direct support of SQLite's cathedral development model: we don't want developers going dark, then showing up weeks later with a massive bolus of changes for us to integrate all at once. [https://en.wikipedia.org/wiki/Jim_McCarthy_(author)|Jim McCarthy] put it well in his book on software project management, <i>[https://www.amazon.com/dp/0735623198/|Dynamics of Software Development]</i>: "[https://www.youtube.com/watch?v=oY6BCHqEbyc|Beware of a guy in a room]." * <b>Branch names sync:</b> Unlike in Git, branch names in Fossil are not purely local labels. They sync along with everything else, so everyone sees the same set of branch names. Fossil's design choice here is a direct reflection of the Linux vs. SQLite project outlook: SQLite's developers collaborate closely on a single coherent project, whereas Linux's developers go off on tangents and occasionally send selected change sets to each other. * <b>Private branches are rare:</b> [/doc/trunk/www/private.wiki|Private branches exist in Fossil], but they're normally used to handle rare exception cases, whereas in many Git projects, they're part of the straight-line development process. * <b>Identical clones:</b> Fossil's autosync system tries to keep each local clone identical to the repository it cloned from. Where Git encourages siloed development, Fossil fights against it. Fossil places a lot of emphasis on synchronizing everyone's work and on reporting on the state of the project and the work of its developers, so that everyone — especially the project leader — can maintain a better mental picture of what is happening, leading to better situational awareness. By contrast, "…[https://docs.github.com/en/get-started/quickstart/contributing-to-projects|forking is at the core of social coding at GitHub]". As of January 2022, [https://github.com/search?q=is:public|Github hosts 47 million distinct software projects], most of which were created by forking a previously-existing project. Since this is [https://evansdata.com/reports/viewRelease.php?reportID=9 | roughly twice the number of developers in the world], it beggars belief that most of these forks are still under active development. The vast bulk of these must be abandoned one-off efforts. This is part of the nature of bazaar style development. You can think about this difference in terms of [https://en.wikipedia.org/wiki/Feedback | feedback loop size], which we know from the mathematics of [https://en.wikipedia.org/wiki/Control_theory | control theory] to directly affect the speed at which any system can safely make changes. The larger the feedback loop, the slower the whole system must run in order to avoid loss of control. The same concept shows up in other contexts, such as in the [https://en.wikipedia.org/wiki/OODA_loop | OODA loop] concept. Committing your changes to private branches in order to delay a public push to the parent repo increases the size of your collaborators' control loops, either causing them to slow their work in order to safely react to your work, or to over-correct in response to each change. Each DVCS can be used in the opposite style, but doing so works against their low-friction paths. <h4 id="scale">2.5.2 Scale</h4> The Linux kernel has a far bigger developer community than that of SQLite: there are thousands and thousands of contributors to Linux, most of whom do not know each other's names. These thousands are responsible for producing roughly 89× more code than is in SQLite. (10.7 [https://en.wikipedia.org/wiki/Source_lines_of_code|MLOC] vs. 0.12 MLOC according to [https://dwheeler.com/sloccount/|SLOCCount].) The Linux kernel and its development process were already uncommonly large back in 2005 when Git was designed, specifically to support the consequences of having such a large set of developers working on such a large code base. 95% of the code in SQLite comes from just four programmers, and 64% of it is from the lead developer alone. The SQLite developers know each other well and interact daily. Fossil was designed for this development model. When choosing your DVCS, we think you should ask yourself whether the scale of your software configuration management problems is closer to those Linus Torvalds designed Git to cope with or whether your work's scale is closer to that of SQLite, for which D. Richard Hipp designed Fossil. An [https://en.wikipedia.org/wiki/Impact_wrench|automotive air impact wrench] running at 8000 RPM driving an M8 socket-cap bolt at 16 cm/s is not the best way to hang a picture on the living room wall. Fossil works well for projects several times the size of SQLite, [https://core.tcl-lang.org/tcl/ | such as Tcl], with a repository over twice the size and with many more core committers. <h4 id="branches">2.5.3 Individual Branches vs. The Entire Change History</h4> Both Fossil and Git store history as a directed acyclic graph (DAG) of changes, but Git tends to focus more on individual branches of the DAG, whereas Fossil puts more emphasis on the entire DAG. For example, the default behavior in Git is to only synchronize a single branch, whereas with Fossil the only sync option is to sync the entire DAG. Git commands, GitHub, and GitLab tend to show only a single branch at a time, whereas Fossil usually shows all parallel branches at once. Git has commands like "rebase" that help keep all relevant changes on a single branch, whereas Fossil encourages a style of many concurrent branches constantly springing into existence, undergoing active development in parallel for a few days or weeks, then merging back into the main line and disappearing. This difference in emphasis arises from the different purposes of the two systems. Git focuses on individual branches, because that is exactly what you want for a highly-distributed bazaar-style project such as Linux. Linus Torvalds does not want to see every check-in by every contributor to Linux: such extreme visibility does not scale well. Contrast Fossil, which was written for the cathedral-style SQLite project and its handful of active committers. Seeing all changes on all branches all at once helps keep the whole team up-to-date with what everybody else is doing, resulting in a more tightly focused and cohesive implementation. <h3 id="checkouts">2.6 One vs. Many Check-outs per Repository</h3> Because Git commingles the repository data with the initial checkout of that repository, the default mode of operation in Git is to stick to that single work/repo tree, even when that's a shortsighted way of working. Fossil doesn't work that way. A Fossil repository is a SQLite database file which is normally stored outside the working checkout directory. You can [/help?cmd=open | open] a Fossil repository any number of times into any number of working directories. A common usage pattern is to have one working directory per active working branch, so that switching branches is done with a <tt>cd</tt> command rather than by checking out the branches successively in a single working directory. Fossil does allow you to switch branches within a working checkout directory, and this is also often done. It is simply that there is no inherent penalty to either choice in Fossil as there is in Git. The standard advice is to use a switch-in-place workflow in Fossil when the disturbance from switching branches is small, and to use multiple checkouts when you have long-lived working branches that are different enough that switching in place is disruptive. While you can [./gitusers.md#worktree | use Git in the Fossil style], Git's default tie between working directory and repository means the standard method for working with a Git repo is to have one working directory only. Most Git tutorials teach this style, so it is how most people learn to use Git. Because relatively few people use Git with multiple working directories per repository, there are [https://duckduckgo.com/?q=git+worktree+problem | several known problems] with that way of working, problems which don't happen in Fossil because of the clear [./ckout-workflows.md | separation] between a Fossil repository and each working directory. This distinction matters because switching branches inside a single working directory loses local context on each switch. For instance, in any software project where the runnable program must be built from source files, you invalidate build objects on each switch, artificially increasing the time required to switch versions. Most obviously, this affects software written in statically-compiled programming languages such as C, Java, and Haskell, but it can even affect programs written in dynamic languages like JavaScript. A typical [https://en.wikipedia.org/wiki/Single-page_application | SPA] build process involves several passes: [http://browserify.org/ | Browserify] to convert [https://nodejs.org/ | Node] packages so they'll run in a web browser, [https://sass-lang.com | SASS] to CSS translation, transpilation of [https://www.typescriptlang.org | Typescript] to JavaScript, [https://github.com/mishoo/UglifyJS | uglification], etc. Once all that processing work is done for a given input file in a given working directory, why re-do that work just to switch versions? If most of the files that differ between versions don't change very often, you can save substantial time by switching branches with <tt>cd</tt> rather than swapping versions in-place within a working checkout directory. For another example, you might have an active long-running test grinding away in a working directory, then get a call from a customer requiring that you switch to a stable branch to answer questions in terms of the version that customer is running. You don't want to stop the test in order to switch your lone working directory to the stable branch. Disk space is cheap. Having several working directories — each with its own local state — makes switching versions cheap and fast. Plus, <tt>cd</tt> is faster to type than <tt>git checkout</tt> or <tt>fossil update</tt>. <h3 id="history">2.7 What you should have done vs. What you actually did</h3> Git puts a lot of emphasis on maintaining a "clean" check-in history. Extraneous and experimental branches by individual developers often never make it into the main repository. Branches may be rebased before being pushed to make it appear as if development had been linear, or "squashed" to make it appear that multiple commits were made as a single commit. There are [https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History | other history rewriting mechanisms in Git] as well. Git strives to record what the development of a project should have looked like had there been no mistakes. Fossil, in contrast, puts more emphasis on recording exactly what happened, including all of the messy errors, dead-ends, experimental branches, and so forth. One might argue that this makes the history of a Fossil project "messy," but another point of view is that this makes the history "accurate." In actual practice, the superior reporting tools available in Fossil mean that this incidental mess is not a factor. Like Git, Fossil has an [/help?cmd=amend|amend command] for modifying prior commits, but unlike in Git, this works not by replacing data in the repository, but by adding a correction record to the repository that affects how later Fossil operations present the corrected data. The old information is still there in the repository, it is just overridden from the amendment point forward. Fossil lacks almost every other history rewriting mechanism listed on the Git documentation page linked above. [./rebaseharm.md | There is no rebase] in Fossil, on purpose, thus no way to reorder or copy commits around in the commit hash tree. There is no commit squashing, dropping, or interactive patch-based cherry-picking of commit elements in Fossil. There is nothing like Git's <tt>filter-branch</tt> in Fossil. The lone exception is deleting commits. Fossil has two methods for doing that, both of which have stringent limitations, on purpose. The first is [/doc/trunk/www/shunning.wiki | shunning]. See that document for details, but briefly, you only get mandatory compliance for shun requests within a single repository. Shun requests do not propagate automatically between repository clones. A Fossil repository administrator can <i>cooperatively</i> pull another repo's shun requests across a sync boundary, so that two admins can get together and agree to shun certain committed artifacts, but a person cannot force their local shun requests into another repo without having admin-level control over the receiving repo as well. Fossil's shun feature isn't for fixing up everyday bad commits, it's for dealing with extreme situations: public commits of secret material, ticket/wiki/forum spam, law enforcement takedown demands, etc. There is also the experimental [/help?cmd=purge | <tt>purge</tt> command], which differs from shunning in ways that aren't especially important in the context of this document. At a 30000 foot level, you can think of purging as useful only when you've turned off Fossil's autosync feature and want to pluck artifacts out of its hash tree before they get pushed. In that sense, it's approximately the same as <tt>git rebase -i, drop</tt>. However, given that Fossil defaults to having autosync enabled [#devorg | for good reason], the purge command isn't very useful in practice: once a commit has been pushed into another repo, shunning is more useful if you need to delete it from history. If these accommodations strike you as incoherent with respect to Fossil's philosophy of durable, unchanging commits, realize that if shunning and purging were removed from Fossil, you could still remove artifacts from the repository with SQL <tt>DELETE</tt> statements; the repository database file is, after all, directly modifiable, being writable by your user. Where the Fossil philosophy really takes hold is in making it difficult to violate the integrity of the hash tree. It's somewhat tangential, but the document [./blockchain.md | "Is Fossil a Blockchain?"] touches on this and related topics. One commentator characterized Git as recording history according to the victors, whereas Fossil records history as it actually happened. <h3 id="testing">2.8 Test Before Commit</h3> One of the things that falls out of Git's default separation of commit from push is that there are several Git sub-commands that jump straight to the commit step before a change could possibly be tested. Fossil, by contrast, makes the equivalent change to the local working check-out only, requiring a separate check-in step to commit the change. This design difference falls naturally out of Fossil's default-enabled autosync feature and its philosophy of [#history | not offering history rewriting features]. The prime example in Git is rebasing: the change happens to the local repository immediately if successful, even though you haven't tested the change yet. It's possible to argue for such a design in a tool like Git since it lacks an autosync feature, because you can still test the change before pushing local changes to the parent repo, but in the meantime you've made a durable change to your local Git repository. You must do something drastic like <tt>git reset --hard</tt> to revert that rebase or rewrite history before pushing it if the rebase causes a problem. If you push your rebased local repo up to the parent without testing first, you cannot fix it without violating [https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing | the golden rule of rebasing]. Lesser examples are the Git <tt>merge</tt>, <tt>cherry-pick</tt>, and <tt>revert</tt> commands, all of which apply work from one branch onto another, and all of which commit their change to the local repository immediately without giving you an opportunity to test the change first unless you give the <tt>--no-commit</tt> option. Otherwise, you're back in the same boat: reset the local repository or rewrite history to fix things, then maybe retry. Fossil cannot sensibly work that way because of its default-enabled autosync feature and its purposeful paucity of commands for modifying commits, as discussed in [#history | the prior section]. Instead of jumping straight to the commit step, Fossil applies the proposed merge to the local working directory only, requiring a separate check-in step before the change is committed to the repository. This gives you a chance to test the change first, either manually or by running your software's automatic tests. (Ideally, both!) Thus, Fossil doesn't need rebase, squashing, <tt>reset --hard</tt>, or other Git commit mutating mechanisms. Because Fossil requires an explicit commit for a merge, it has the nice side benefit that it makes you give an explicit commit <i>message</i> for each merge, whereas Git writes that commit message itself by default unless you give the optional <tt>--edit</tt> flag to override it. We don't look at this difference as a workaround in Fossil for autosync, but instead as a test-first philosophical difference: <tt>fossil commit</tt> is a <i>commitment</i>. When every commit is pushed to the parent repo by default, it encourages a working style in which every commit is tested first. It encourages thinking before acting. We believe this is an inherently good thing. Incidentally, this is a good example of Git's messy command design. These three commands: <pre> $ git merge HASH $ git cherry-pick HASH $ git revert HASH </pre> ...are all the same command in Fossil: <pre> $ fossil merge HASH $ fossil merge --cherrypick HASH $ fossil merge --backout HASH </pre> If you think about it, they're all the same function: apply work done on one branch to another. All that changes between these commands is how much work gets applied — just one check-in or a whole branch — and the merge direction. This is the sort of thing we mean when we point out that Fossil's command interface is simpler than Git's: there are fewer concepts to keep track of in your mental model of Fossil's internal operation. Fossil's implementation of the feature is also simpler to describe. The brief online help for <tt>[/help?cmd=merge | fossil merge]</tt> is currently 41 lines long, to which you want to add the 600 lines of [./branching.wiki | the branching document]. The equivalent documentation in Git is the aggregation of the man pages for the above three commands, which is over 1000 lines, much of it mutually redundant. (e.g. Git's <tt>--edit</tt> and <tt>--no-commit</tt> options get described three times, each time differently.) Fossil's documentation is not only more concise, it gives a nice split of brief online help and full online documentation. <h3 id="hash">2.9 Hash Algorithm: SHA-3 vs SHA-2 vs SHA-1</h3> Fossil started out using 160-bit SHA-1 hashes to identify check-ins, just as in Git. That changed in early 2017 when news of the [https://shattered.io/|SHAttered attack] broke, demonstrating that SHA-1 collisions were now practical to create. Two weeks later, the creator of Fossil delivered a new release allowing a clean migration to [https://en.wikipedia.org/wiki/SHA-3|256-bit SHA-3] with [./hashpolicy.wiki|full backwards compatibility] to old SHA-1 based repositories. In October 2019, after the last of the major binary package repos offering Fossil upgraded to Fossil 2.<i>x</i>, we switched the default hash mode so that the conversion to SHA-3 is fully automatic. This not only solves the SHAttered problem, it should prevent a reoccurrence of similar problems for the foreseeable future. Meanwhile, the Git community took until August 2018 to publish [https://git-scm.com/docs/hash-function-transition/|their first plan] for solving the same problem by moving to SHA-256, a variant of the [https://en.wikipedia.org/wiki/SHA-2 | older SHA-2 algorithm]. As of this writing in February 2020, that plan hasn't been implemented, as far as this author is aware, but there is now [https://lwn.net/ml/git/20200113124729.3684846-1-sandals@crustytoothpaste.net/ | a competing SHA-256 based plan] which requires complete repository conversion from SHA-1 to SHA-256, breaking all public hashes in the repo. One way to characterize such a massive upheaval in Git terms is a whole-project rebase, which violates [https://blog.axosoft.com/golden-rule-of-rebasing-in-git/ | Git's own Golden Rule of Rebasing]. Regardless of the eventual implementation details, we fully expect Git to move off SHA-1 eventually and for the changes to take years more to percolate through the community. Almost three years after Fossil solved this problem, the [https://sha-mbles.github.io/ | SHAmbles attack] was published, further weakening the case for continuing to use SHA-1. The practical impact of attacks like SHAttered and SHAmbles on the Git and Fossil Merkle trees isn't clear, but you want to have your repositories moved over to a stronger hash algorithm before someone figures out how to make use of the weaknesses in the old one. Fossil has had this covered for years now, so that the solution is now almost universally deployed. <hr/> <h3>Asides and Digressions</h3> <i><small><ol> <li><p>[./mirrorlimitations.md|Many things are lost] in making a Git mirror of a Fossil repo due to limitations of Git relative to Fossil. GitHub adds some of these missing features to stock Git, but because they're not part of Git proper, [./mirrortogithub.md|exporting a Fossil repository to GitHub] will still not include them; Fossil tickets do not become GitHub issues, for example. <li><p>The <tt>fossil-scm.org</tt> web site is actually hosted in several parts, so that it is not strictly true that "everything" on it is in the self-hosting Fossil project repo. The web forum is hosted as [https://fossil-scm.org/forum/|a separate Fossil repo] from the [https://fossil-scm.org/home/|main Fossil self-hosting repo] for administration reasons, and the Download page content isn't normally synchronized with a "<tt>fossil clone</tt>" command unless you add the "-u" option. (See "[./aboutdownload.wiki|How the Download Page Works]" for details.) Chat history is deliberately not synced as chat messages are intended to be ephemeral. There may also be some purely static elements of the web site served via D. Richard Hipp's own lightweight web server, <tt>[https://sqlite.org/althttpd/|althttpd]</tt>, which is configured as a front end to Fossil running in CGI mode on these sites. <li><p>That estimate is based on pricing at Digital Ocean in mid-2019: Fossil will run just fine on the smallest instance they offer, at US $5/month, but the closest match to GitLab's minimum requirements among Digital Ocean's offerings currently costs $40/month. <li><p>This means you can give up waiting for Fossil to be ported to the PDP-11, but we remain hopeful that someone may eventually port it to [https://en.wikipedia.org/wiki/Z/OS|z/OS]. <li><p>"Why is there all this Tcl in and around Fossil?" you may ask. It is because D. Richard Hipp is a long-time Tcl user and contributor. SQLite started out as an embedded database for Tcl specifically. ([https://sqlite.org/tclsqlite.html | [Reference]]) When he then created Fossil to manage the development of SQLite, it was natural for him to use Tcl-based tools for its scripting, build system, test system, etc. It came full circle in 2011 when [https://www.reddit.com/r/programming/comments/fwrx5/tcl_and_tk_move_away_from_cvs_to_fossil/ | the Tcl and Tk projects moved from CVS to Fossil]. <li><p>A minority of the pieces of the Git core software suite are written in other languages, primarily Perl, Python, and Tcl. (e.g. <tt>git-send-mail</tt>, <tt>git-p4</tt>, and <tt>gitk</tt>, respectively.) Although these interpreters are quite portable, they aren't installed by default everywhere, and on some platforms you can't count on them at all. (Not just Windows, but also the BSDs and many other non-Linux platforms.) This expands the dependency footprint of Git considerably. It is why the current Git for Windows distribution is 44.7 MiB but the current <tt>fossil.exe</tt> zip file for Windows is 2.24 MiB. Fossil is much smaller despite using a roughly similar amount of high-level scripting code because its interpreters are compact and built into Fossil itself. <li><p>Both Fossil and Git support [https://en.wikipedia.org/wiki/Patch_(Unix)|<tt>patch(1)</tt> files] — unified diff formatted output — for accepting drive-by contributions, but it's a lossy contribution path for both systems. Unlike Git PRs and Fossil bundles, patch files collapse multiple checkins together, they don't include check-in comments, and they cannot encode changes made above the individual file content layer: you lose branching decisions, tag changes, file renames, and more when using patch files. The [./patchcmd.md | <tt>fossil patch</tt> command] also solves these problems, but it is because it works like a Fossil bundle, only for uncommitted changes; it doesn't use Larry Wall's <tt>patch</tt> tool to apply unified diff output to the receiving Fossil checkout.</p></li> </ol></i></small> |
Changes to www/fossil.eps.
|
| | | 1 2 3 4 5 6 7 8 | %!PS-Adobe-3.0 EPSF-3.0 %%BoundingBox: 0 0 249 284 %%Pages: 0 %%Creator: Sun Microsystems, Inc. %%Title: none %%CreationDate: none %%LanguageLevel: 2 %%EndComments |
︙ | ︙ | |||
46 47 48 49 50 51 52 | %%EndProlog %%BeginSetup %%EndSetup %%Page: 1 1 %%BeginPageSetup %%EndPageSetup pum | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | %%EndProlog %%BeginSetup %%EndSetup %%Page: 1 1 %%BeginPageSetup %%EndPageSetup pum 0.02835 0.02837 s 0 -10008 t /tm matrix currentmatrix def tm setmatrix -4875 -2599 t 1 1 s 0.500 c 7207 8311 m 7036 8281 6990 7727 7105 7073 ct 7221 6420 7453 5915 7625 5945 ct 7796 5975 7842 6529 7727 7183 ct 7612 7836 7379 8342 7207 8311 ct p ef 7607 10301 m 7445 10335 7221 9926 7107 9387 ct 6992 8847 7030 8382 7192 8348 ct 7354 8313 7578 8723 7693 9262 ct 7807 9802 7769 10267 7607 10301 ct p ef 8749 11736 m 8626 11826 8289 11574 7995 11173 ct 7701 10773 7563 10375 7686 10285 ct 7809 10195 8146 10446 8440 10847 ct 8734 11248 8872 11645 8749 11736 ct p ef 10691 12070 m 10668 12234 10205 12303 9658 12223 ct 9111 12143 8686 11946 8710 11782 ct 8734 11618 9197 11550 9744 11629 ct 10291 11709 10715 11906 10691 12070 ct p ef 10761 12053 m 10721 11951 11064 11722 11526 11541 ct 11989 11360 12396 11296 12436 11398 ct 12476 11500 12134 11729 11671 11910 ct 11208 12091 10801 12155 10761 12053 ct p ef 12410 11353 m 12365 11314 12499 11087 12709 10847 ct 12920 10607 13126 10444 13171 10483 ct 13216 10522 13082 10749 12872 10989 ct 12662 11230 12455 11392 12410 11353 ct p ef 7901 12525 m 7790 12525 7700 12212 7700 11826 ct 7700 11440 7790 11127 7901 11127 ct 8012 11127 8102 11440 8102 11826 ct 8102 12212 8012 12525 7901 12525 ct p ef 7826 12575 m 7765 12668 7415 12547 7044 12306 ct 6672 12064 6420 11793 6480 11700 ct 6541 11607 6891 11728 7262 11970 ct 7634 12211 7886 12482 7826 12575 ct p ef 6460 11694 m 6435 11723 6333 11673 6234 11584 ct 6134 11494 6073 11399 6099 11371 ct 6124 11343 6225 11392 6325 11482 ct 6425 11571 6485 11666 6460 11694 ct p ef 13183 10437 m 13129 10414 13185 10157 13309 9863 ct 13433 9569 13579 9350 13633 9373 ct 13688 9396 13632 9653 13508 9947 ct 13384 10241 13238 10460 13183 10437 ct p ef 9900 11524 m 9790 11524 9701 11211 9701 10825 ct 9701 10439 9790 10126 9900 10126 ct 10009 10126 10098 10439 10098 10825 ct 10098 11211 10009 11524 9900 11524 ct p ef 9828 11574 m 9767 11667 9417 11546 9046 11305 ct 8674 11063 8422 10792 8482 10699 ct 8543 10606 8893 10727 9264 10969 ct 9636 11210 9888 11481 9828 11574 ct p ef 6085 9230 m 5976 9131 6097 8819 6357 8533 ct 6616 8247 6915 8095 7024 8194 ct 7133 8293 7012 8605 6752 8892 ct 6493 9178 6194 9329 6085 9230 ct p ef 5910 9182 m 5805 9210 5629 8884 5516 8456 ct 5403 8027 5396 7657 5501 7629 ct 5605 7601 5782 7927 5895 8356 ct 6008 8784 6014 9154 5910 9182 ct p ef 8630 9345 m 8548 9270 8691 8978 8951 8692 ct 9210 8406 9487 8234 9569 8309 ct 9651 8383 9507 8675 9248 8961 ct 8989 9247 8712 9419 8630 9345 ct p ef 8566 9556 m 8478 9624 8188 9394 7918 9043 ct 7647 8692 7499 8352 7587 8285 ct 7675 8217 7965 8447 8235 8798 ct 8506 9149 8654 9489 8566 9556 ct p ef 6579 11626 m 6549 11638 6480 11543 6425 11413 ct 6371 11283 6352 11167 6382 11154 ct 6412 11141 6481 11237 6535 11367 ct 6590 11497 6609 11613 6579 11626 ct p ef 5952 11674 m 5959 11642 6081 11642 6223 11674 ct 6366 11707 6475 11759 6468 11791 ct 6461 11823 6339 11823 6197 11790 ct 6054 11758 5945 11706 5952 11674 ct p ef 5384 7615 m 5359 7644 5257 7594 5158 7505 ct 5058 7415 4997 7320 5023 7292 ct 5048 7264 5149 7313 5249 7403 ct 5349 7492 5409 7587 5384 7615 ct p ef 5503 7547 m 5473 7559 5404 7464 5349 7334 ct 5295 7204 5276 7088 5306 7075 ct 5336 7062 5405 7158 5459 7288 ct 5514 7418 5533 7534 5503 7547 ct p ef 4875 7595 m 4882 7562 5004 7563 5147 7595 ct 5289 7628 5399 7680 5392 7712 ct 5385 7744 5263 7744 5120 7711 ct 4977 7679 4868 7627 4875 7595 ct p ef 9764 8248 m 9739 8220 9798 8125 9898 8036 ct 9997 7946 10097 7897 10123 7925 ct 10148 7952 10088 8047 9989 8137 ct 9890 8226 9789 8276 9764 8248 ct p ef 9641 8179 m 9611 8167 9631 8051 9685 7920 ct 9740 7790 9809 7694 9839 7707 ct 9869 7720 9850 7836 9795 7966 ct 9741 8097 9672 8192 9641 8179 ct p ef 10269 8227 m 10276 8259 10167 8311 10024 8343 ct 9882 8375 9760 8375 9753 8343 ct 9746 8311 9856 8259 9998 8227 ct 10141 8195 10262 8195 10269 8227 ct p ef 9749 10085 m 9724 10113 9623 10064 9523 9974 ct 9424 9885 9363 9790 9389 9762 ct 9414 9734 9515 9783 9615 9872 ct 9714 9961 9775 10057 9749 10085 ct p ef 9868 10017 m 9838 10029 9770 9934 9715 9804 ct 9661 9674 9641 9558 9671 9545 ct 9702 9533 9770 9628 9824 9758 ct 9879 9888 9898 10004 9868 10017 ct p ef 9242 10064 m 9249 10032 9371 10032 9513 10064 ct 9656 10097 9765 10149 9758 10181 ct 9751 10213 9629 10213 9487 10180 ct 9344 10148 9235 10096 9242 10064 ct p ef 6841 4401 m 6726 4212 7272 3670 8061 3191 ct 8849 2711 9581 2475 9696 2664 ct 9811 2853 9265 3395 8477 3875 ct 7688 4355 6956 4590 6841 4401 ct p ef 9097 6042 m 8862 6353 8161 6221 7532 5745 ct 6902 5270 6583 4632 6818 4321 ct 7053 4009 7754 4142 8384 4617 ct 9013 5092 9333 5730 9097 6042 ct p ef 7880 5222 m 7747 5124 7957 4611 8349 4075 ct 8742 3540 9168 3185 9302 3283 ct 9435 3381 9225 3894 8833 4430 ct 8440 4965 8014 5320 7880 5222 ct p ef 9003 6100 m 8784 6059 8750 5255 8927 4303 ct 9103 3351 9423 2612 9642 2652 ct 9861 2693 9895 3498 9719 4450 ct 9543 5401 9222 6140 9003 6100 ct p ef 0 10008 t pom count op_count sub {pop} repeat countdictstack dict_count sub {end} repeat b4_inc_state restore %%PageTrailer %%Trailer %%EOF |
Changes to www/fossil2.eps.
|
| | | 1 2 3 4 5 6 7 8 | %!PS-Adobe-3.0 EPSF-3.0 %%BoundingBox: 0 0 555 735 %%Pages: 0 %%Creator: Sun Microsystems, Inc. %%Title: none %%CreationDate: none %%LanguageLevel: 2 %%EndComments |
︙ | ︙ | |||
46 47 48 49 50 51 52 | %%EndProlog %%BeginSetup %%EndSetup %%Page: 1 1 %%BeginPageSetup %%EndPageSetup pum | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | %%EndProlog %%BeginSetup %%EndSetup %%Page: 1 1 %%BeginPageSetup %%EndPageSetup pum 0.02833 0.02833 s 0 -25940 t /tm matrix currentmatrix def gs tm setmatrix -1000 -1000 t 1 1 s 1000 1000 m 20589 1000 l 20589 26939 l 1000 26939 l 1000 1000 l eoclip newpath gs 1000 1000 m 20589 1000 l 20589 26939 l 1000 26939 l 1000 1000 l eoclip newpath 1000 1000 m 20590 1000 l 20590 26940 l 1000 26940 l 1000 1000 l eoclip newpath 0.500 c 7207 8311 m 7036 8281 6989 7726 7104 7073 ct 7220 6420 7453 5913 7624 5944 ct 7796 5974 7842 6529 7727 7182 ct 7612 7835 7378 8341 7207 8311 ct p ef 7607 10301 m 7446 10335 7220 9925 7106 9386 ct 6991 8847 7030 8381 7191 8347 ct 7353 8312 7578 8723 7693 9262 ct 7807 9800 7768 10267 7607 10301 ct p ef 8749 11736 m 8627 11825 8288 11574 7995 11173 ct 7701 10772 7562 10374 7685 10284 ct 7808 10194 8146 10446 8440 10847 ct 8734 11247 8872 11646 8749 11736 ct p ef 10683 12127 m 10664 12260 10204 12303 9657 12224 ct 9111 12145 8683 11972 8702 11840 ct 8721 11707 9181 11664 9727 11743 ct 10274 11822 10702 11995 10683 12127 ct p ef 10761 12053 m 10721 11951 11064 11721 11526 11540 ct 11989 11359 12397 11296 12437 11397 ct 12477 11499 12134 11729 11671 11910 ct 11209 12091 10801 12154 10761 12053 ct p ef 12410 11353 m 12366 11314 12500 11087 12710 10847 ct 12920 10607 13127 10444 13171 10483 ct 13216 10522 13082 10749 12872 10989 ct 12662 11229 12455 11392 12410 11353 ct p ef 7901 12525 m 7790 12525 7700 12212 7700 11826 ct 7700 11440 7790 11127 7901 11127 ct 8012 11127 8102 11440 8102 11826 ct 8102 12212 8012 12525 7901 12525 ct p ef 7825 12576 m 7765 12669 7414 12548 7043 12306 ct 6671 12065 6419 11793 6479 11700 ct 6540 11607 6891 11728 7262 11969 ct 7633 12211 7886 12483 7825 12576 ct p ef 6460 11695 m 6434 11723 6333 11674 6233 11584 ct 6133 11495 6072 11399 6098 11371 ct 6123 11342 6225 11392 6325 11481 ct 6425 11571 6485 11666 6460 11695 ct p ef 13184 10437 m 13129 10414 13185 10156 13309 9862 ct 13434 9569 13580 9349 13634 9372 ct 13688 9395 13632 9653 13508 9947 ct 13384 10240 13238 10460 13184 10437 ct p ef 9899 11524 m 9790 11524 9700 11211 9700 10825 ct 9700 10439 9790 10126 9899 10126 ct 10008 10126 10098 10439 10098 10825 ct 10098 11211 10008 11524 9899 11524 ct p ef 9827 11575 m 9767 11668 9416 11547 9045 11305 ct 8673 11064 8421 10792 8481 10699 ct 8542 10606 8893 10727 9264 10968 ct 9635 11210 9888 11482 9827 11575 ct p ef 6085 9230 m 5976 9132 6098 8819 6357 8533 ct 6616 8247 6915 8096 7024 8194 ct 7133 8293 7012 8605 6753 8892 ct 6493 9178 6194 9329 6085 9230 ct p ef 5910 9183 m 5806 9210 5629 8885 5516 8456 ct 5403 8028 5396 7658 5501 7630 ct 5605 7602 5782 7928 5895 8357 ct 6008 8785 6014 9155 5910 9183 ct p ef 8630 9344 m 8547 9270 8691 8977 8950 8691 ct 9209 8405 9486 8234 9568 8308 ct 9650 8383 9507 8675 9248 8961 ct 8989 9247 8712 9419 8630 9344 ct p ef 8566 9557 m 8478 9625 8187 9395 7917 9044 ct 7646 8693 7498 8353 7586 8285 ct 7674 8217 7965 8448 8235 8799 ct 8505 9150 8654 9490 8566 9557 ct p ef 6578 11626 m 6548 11638 6479 11543 6424 11413 ct 6370 11283 6351 11166 6381 11153 ct 6412 11141 6481 11236 6535 11366 ct 6589 11496 6609 11613 6578 11626 ct p ef 5952 11673 m 5959 11641 6081 11641 6224 11674 ct 6366 11706 6476 11759 6469 11791 ct 6462 11823 6340 11823 6197 11791 ct 6055 11758 5945 11706 5952 11673 ct p ef 5384 7616 m 5358 7644 5257 7595 5157 7505 ct 5057 7416 4996 7320 5022 7292 ct 5047 7263 5149 7313 5249 7402 ct 5349 7492 5409 7587 5384 7616 ct p ef 5502 7547 m 5472 7559 5403 7464 5348 7334 ct 5294 7204 5275 7087 5305 7074 ct 5336 7062 5405 7157 5459 7287 ct 5513 7417 5533 7534 5502 7547 ct p ef 4875 7594 m 4882 7562 5004 7562 5147 7594 ct 5289 7627 5399 7679 5392 7712 ct 5385 7744 5263 7744 5120 7711 ct 4978 7679 4868 7626 4875 7594 ct p ef 9763 8248 m 9738 8220 9798 8124 9897 8035 ct 9996 7946 10097 7896 10122 7924 ct 10147 7951 10087 8047 9988 8136 ct 9889 8225 9787 8275 9763 8248 ct p ef 9639 8179 m 9608 8166 9628 8050 9682 7920 ct 9737 7790 9806 7694 9836 7707 ct 9867 7719 9847 7836 9793 7966 ct 9739 8096 9669 8192 9639 8179 ct p ef 10269 8228 m 10277 8260 10166 8312 10024 8344 ct 9881 8376 9759 8376 9752 8344 ct 9745 8311 9855 8259 9998 8227 ct 10140 8195 10262 8196 10269 8228 ct p ef 9749 10085 m 9724 10113 9622 10064 9523 9975 ct 9424 9886 9363 9791 9388 9762 ct 9414 9734 9516 9784 9615 9872 ct 9714 9961 9774 10057 9749 10085 ct p ef 9868 10017 m 9839 10029 9770 9933 9715 9803 ct 9661 9673 9642 9557 9671 9544 ct 9701 9532 9770 9628 9824 9758 ct 9879 9888 9898 10004 9868 10017 ct p ef 9242 10063 m 9249 10031 9371 10031 9514 10064 ct 9656 10096 9766 10149 9759 10181 ct 9752 10213 9630 10213 9487 10181 ct 9345 10148 9235 10096 9242 10063 ct p ef 6841 4401 m 6726 4212 7272 3669 8060 3190 ct 8848 2710 9581 2475 9696 2664 ct 9811 2853 9265 3396 8477 3875 ct 7689 4354 6956 4590 6841 4401 ct p ef 9098 6041 m 8863 6352 8161 6220 7532 5745 ct 6903 5271 6583 4632 6818 4321 ct 7053 4009 7755 4142 8384 4617 ct 9013 5091 9333 5730 9098 6041 ct p ef 7879 5222 m 7746 5124 7956 4610 8348 4075 ct 8740 3540 9167 3185 9300 3283 ct 9433 3380 9224 3895 8832 4430 ct 8440 4964 8012 5319 7879 5222 ct p ef 9004 6100 m 8786 6059 8751 5255 8927 4303 ct 9103 3351 9424 2612 9642 2652 ct 9861 2693 9896 3498 9719 4449 ct 9543 5401 9222 6140 9004 6100 ct p ef 106 lw 1 lj 9435 13668 m 9429 13652 l 9421 13637 l 9412 13622 l 9401 13607 l 9390 13593 l 9377 13579 l 9363 13565 l 9347 13552 l 9331 13539 l 9313 13526 l 9295 13515 l 9275 13503 l 9254 13493 l 9233 13482 l 9211 13473 l 9188 13464 l 9164 13456 l 9139 13448 l 9114 13441 l 9088 13435 l 9062 13430 l 9035 13425 l 9008 13422 l 8981 13419 l 8953 13416 l 8925 13415 l 8898 13414 l 8870 13414 l 8842 13415 l 8814 13417 l 8787 13419 l 8759 13422 l 8732 13426 l 8706 13431 l 8680 13437 l 8654 13443 l 8629 13450 l 8605 13458 l 8581 13466 l 8558 13475 l 8536 13485 l 8514 13495 l 8494 13506 l 8475 13517 l 8456 13529 l 8439 13542 l 8423 13555 l 8408 13568 l 8394 13582 l 8381 13596 l 8370 13611 l 8360 13626 l 8351 13641 l 8344 13656 l 8338 13672 l 8333 13687 l 8330 13703 l 8328 13719 l 8327 13735 l 8328 13751 l 8330 13767 l 8334 13783 l 8338 13798 l 8345 13814 l 8352 13829 l 8361 13844 l 8372 13859 l 8383 13874 l 8396 13888 l 8410 13902 l 8425 13915 l 8441 13928 l 8459 13940 l 8477 13952 l 8497 13964 l 8517 13974 l 8539 13985 l 8561 13994 l 8584 14003 l 8608 14011 l 8632 14019 l 8657 14026 l 8683 14032 l 8709 14037 l 8736 14042 l 8763 14046 l 8790 14049 l 8818 14052 l 8846 14053 l 8873 14054 l 8901 14054 l 8929 14053 l 8957 14051 l ps 8347 14443 m 8354 14459 l 8362 14474 l 8371 14489 l 8382 14504 l 8394 14518 l 8407 14533 l 8421 14546 l 8436 14560 l 8453 14573 l 8471 14585 l 8490 14597 l 8510 14608 l 8530 14619 l 8552 14629 l 8575 14639 l 8598 14647 l 8622 14656 l 8647 14663 l 8673 14670 l 8699 14676 l 8725 14681 l 8752 14685 l 8779 14689 l 8807 14692 l 8835 14694 l 8863 14695 l 8891 14696 l 8919 14696 l 8947 14695 l 8975 14693 l 9002 14690 l 9030 14687 l 9057 14682 l 9083 14677 l 9109 14672 l 9135 14665 l 9160 14658 l 9184 14650 l 9208 14641 l 9231 14632 l 9253 14622 l 9274 14612 l 9294 14600 l 9313 14589 l 9332 14576 l 9349 14564 l 9364 14550 l 9379 14537 l 9393 14523 l 9405 14508 l 9416 14494 l 9425 14479 l 9434 14463 l 9441 14448 l 9446 14432 l 9451 14416 l 9453 14400 l 9455 14384 l 9455 14368 l 9453 14352 l 9451 14336 l 9446 14320 l 9441 14305 l 9434 14289 l 9426 14274 l 9416 14259 l 9405 14244 l 9393 14230 l 9380 14216 l 9365 14202 l 9349 14189 l 9332 14176 l 9314 14164 l 9295 14152 l 9275 14141 l 9254 14130 l 9232 14120 l 9209 14111 l 9185 14102 l 9161 14094 l 9136 14087 l 9110 14081 l 9084 14075 l 9057 14070 l 9030 14066 l 9003 14062 l 8975 14059 l 8948 14057 l ps 10974 13668 m 10968 13652 l 10960 13637 l 10951 13622 l 10940 13607 l 10929 13593 l 10916 13579 l 10902 13565 l 10886 13552 l 10870 13539 l 10853 13526 l 10834 13515 l 10814 13503 l 10794 13493 l 10772 13482 l 10750 13473 l 10727 13464 l 10703 13456 l 10679 13448 l 10653 13441 l 10628 13435 l 10602 13430 l 10575 13425 l 10548 13422 l 10521 13419 l 10493 13416 l 10465 13415 l 10438 13414 l 10410 13414 l 10382 13415 l 10354 13417 l 10327 13419 l 10300 13422 l 10273 13426 l 10246 13431 l 10220 13437 l 10194 13443 l 10169 13450 l 10145 13458 l 10121 13466 l 10098 13475 l 10076 13485 l 10055 13495 l 10035 13506 l 10015 13517 l 9997 13529 l 9980 13542 l 9964 13555 l 9949 13568 l 9935 13582 l 9922 13596 l 9911 13611 l 9901 13626 l 9892 13641 l 9885 13656 l 9879 13672 l 9874 13687 l 9871 13703 l 9869 13719 l 9868 13735 l 9869 13751 l 9871 13767 l 9875 13783 l 9879 13798 l 9886 13814 l 9893 13829 l 9902 13844 l 9912 13859 l 9924 13874 l 9937 13888 l 9951 13902 l 9966 13915 l 9982 13928 l 10000 13940 l 10018 13952 l 10037 13964 l 10058 13974 l 10079 13985 l 10101 13994 l 10124 14003 l 10148 14011 l 10173 14019 l 10198 14026 l 10223 14032 l 10250 14037 l 10276 14042 l 10303 14046 l 10330 14049 l 10358 14052 l 10386 14053 l 10413 14054 l 10441 14054 l 10469 14053 l 10497 14051 l ps 9888 14443 m 9895 14459 l 9903 14474 l 9912 14489 l 9923 14504 l 9934 14518 l 9948 14533 l 9962 14546 l 9977 14560 l 9994 14573 l 10012 14585 l 10031 14597 l 10050 14608 l 10071 14619 l 10093 14629 l 10115 14639 l 10139 14647 l 10163 14656 l 10188 14663 l 10213 14670 l 10239 14676 l 10265 14681 l 10292 14685 l 10320 14689 l 10347 14692 l 10375 14694 l 10403 14695 l 10431 14696 l 10459 14696 l 10487 14695 l 10514 14693 l 10542 14690 l 10569 14687 l 10596 14682 l 10623 14677 l 10649 14672 l 10675 14665 l 10700 14658 l 10724 14650 l 10748 14641 l 10770 14632 l 10792 14622 l 10813 14612 l 10834 14600 l 10853 14589 l 10871 14576 l 10888 14564 l 10904 14550 l 10918 14537 l 10932 14523 l 10944 14508 l 10955 14494 l 10965 14479 l 10973 14463 l 10980 14448 l 10985 14432 l 10990 14416 l 10992 14400 l 10994 14384 l 10994 14368 l 10992 14352 l 10990 14336 l 10986 14320 l 10980 14305 l 10973 14289 l 10965 14274 l 10955 14259 l 10944 14244 l 10932 14230 l 10919 14216 l 10904 14202 l 10888 14189 l 10871 14176 l 10853 14164 l 10834 14152 l 10814 14141 l 10793 14130 l 10771 14120 l 10748 14111 l 10725 14102 l 10700 14094 l 10675 14087 l 10650 14081 l 10624 14075 l 10597 14070 l 10570 14066 l 10543 14062 l 10515 14059 l 10488 14057 l ps 7205 14684 m 6849 14684 6559 14412 6559 14078 ct 6559 13744 6849 13472 7205 13472 ct 7561 13472 7851 13744 7851 14078 ct 7851 14412 7561 14684 7205 14684 ct pc 5427 13589 m 5427 14657 l ps 5374 13560 m 6242 13560 l ps 5374 13914 m 6090 13914 l ps 11776 13397 m 11776 14697 l ps 12625 13414 m 12625 14680 l ps 12577 14640 m 13493 14640 l ps gr gs 1000 1000 m 20589 1000 l 20589 26939 l 1000 26939 l 1000 1000 l eoclip newpath gr gr 0 25940 t pom count op_count sub {pop} repeat countdictstack dict_count sub {end} repeat b4_inc_state restore %%PageTrailer %%Trailer %%EOF |
Changes to www/fossil3.gif.
cannot compute difference between binary files
Changes to www/fossil_logo_small.gif.
cannot compute difference between binary files
Added www/fossil_prompt.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 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 | #------------------------------------------------------------------------- # get_fossil_data() # # If the current directory is part of a fossil checkout, then populate # a series of global variables based on the current state of that # checkout. Variables are populated based on the output of the [fossil info] # command. # # If the current directory is not part of a fossil checkout, set global # variable $fossil_info_project_name to an empty string and return. # function get_fossil_data() { fossil_info_project_name="" eval `get_fossil_data2` } function get_fossil_data2() { fossil info 2> /dev/null |tr '\042\047\140' _|grep "^[^ ]*:" | while read LINE ; do local field=`echo $LINE | sed 's/:.*$//' | sed 's/-/_/'` local value=`echo $LINE | sed 's/^[^ ]*: *//'` echo fossil_info_${field}=\"${value}\" done } #------------------------------------------------------------------------- # set_prompt() # # Set the PS1 variable. If the current directory is part of a fossil # checkout then the prompt contains information relating to the state # of the checkout. # # Otherwise, if the current directory is not part of a fossil checkout, it # is set to a fairly standard bash prompt containing the host name, user # name and current directory. # function set_prompt() { get_fossil_data if [ -n "$fossil_info_project_name" ] ; then # Color the path part of the prompt blue if this is a clean checkout # Or red if it has been edited in any way at all. Set $c1 to the escape # sequence required to change the type to the required color. And $c2 # to the sequence that changes it back. # if [ -n "`fossil chang`" ] ; then c1="\[\033[1;31m\]" # red else c1="\[\033[1;34m\]" # blue fi c2="\[\033[0m\]" PS1="\[\033[01;32m\]\u@\h\[\033[00m\]:$c1\w\$$c2 " else PS1="\[\033[01;32m\]\u@\h\[\033[00m\]:\w\$ " fi } PROMPT_COMMAND=set_prompt |
Added www/fossil_prompt.wiki.
> > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <title>Fossilized Bash Prompt</title> Dan Kennedy has contributed a [./fossil_prompt.sh?mimetype=text/plain | bash script] that manipulates the bash prompt to show the status of the Fossil repository that the user is currently visiting. The prompt shows the branch, version, and time stamp for the current checkout, and the prompt changes colors from blue to red when there are uncommitted changes. To try out this script, simply download it from the link above, then type: <pre> . fossil_prompt.sh </pre> For a permanent installation, you can graft the code into your <tt>.bashrc</tt> file in your home directory. The code is very simple (only 32 non-comment lines, as of this writing) and hence easy to customized. |
Added www/gitusers.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 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 | # Git to Fossil Translation Guide ## Introduction Fossil shares many similarities with Git. In many cases, the sub-commands are identical: [`fossil bisect`][fbis] does essentially the same thing as [`git bisect`][gbis], for example. Yet, Fossil is not merely Git with a bunch of commands misspelled. If that were the case, we could give you a two-column translation table which would tell you [how to say things like “`git reset --hard HEAD`â€](#reset) in this funny ol’ Fossil dialect of Git and be done. The purpose of this document is to cover all the cases where there is no simple 1:1 mapping, usually because of intentional design differences in Fossil that prevent it from working exactly like Git. We choose to explain these differences since to understand the conversion, you need to know why each difference exists. We focus on practical command examples here, leaving discussions of the philosophical underpinnings that drive these command differences to [another document][fvg]. The [case studies](#cs1) do get a bit philosophical, but it is with the aim of illustrating how these Fossil design differences cause Fossil to behave materially differently from Git in everyday operation. We present this from the perspective of Git users moving to Fossil, but it is also possible to read this document as a Fossil user who speaks only pidgin Git, who may often have questions of the form, “Now how do I do X in Git again?†This document’s authors are intimately familiar with Fossil, so it is difficult for us to anticipate the perspective of people who are intimately familiar with Git. If you have a lot of prior Git experience, we welcome your contributions and questions on the [Fossil Forum][ffor]. While we do try to explain Fossil-specific terminology inline here as-needed, you may find it helpful to skim [the Fossil glossary][gloss]. It will give you another take on our definitions here, and it may help you to understand some of the other Fossil docs better. [fbis]: /help?cmd=bisect [gbis]: https://git-scm.com/docs/git-bisect [ffor]: https://fossil-scm.org/forum [fvg]: ./fossil-v-git.wiki <a id="mwd"></a> ## Repositories and Checkouts Are Distinct A repository and a check-out are distinct in Fossil, allowing them to be stored in separate directory trees, whereas the two are commingled by default with Git, with the repository stored in a `.git` subdirectory underneath your working directory. This difference shows up in several separate places when it comes to moving from Git to Fossil. #### <a id="cwork" name="scw"></a> Checkout Workflows A Fossil repository is a SQLite database storing the entire history of a project. It is not normally stored inside the working tree. A Fossil working tree — [also called a check-out](./glossary.md#check-out) — is a directory that contains a snapshot of your project that you are currently working on, extracted for you from the repository database file by the `fossil` program. There are ways to [emulate the Fossil working style in Git](#worktree), but because they’re not designed into the core concept of the tool, Git tutorials usually advocate a switch-in-place working mode instead, so that is how most users end up working with Git. Contrast [Fossil’s check-out workflow document][ckwf] to see the practical differences. There is one Git-specific detail we wish to add beyond what that document already covers. This command: git checkout some-branch …is best given as: fossil update some-branch …in Fossil. There is a [`fossil checkout`][co] command, but it has [several differences](./co-vs-up.md) that make it less broadly useful than [`fossil update`][up] in everyday operation, so we recommend that Git users moving to Fossil develop a habit of typing `fossil up` rather than `fossil checkout`. That said, one of those differences does match up with Git users’ expectations: `fossil checkout` doesn’t pull changes from the remote repository into the local clone as `fossil update` does. We think this is less broadly useful, but that’s the subject of the next section. [ckwf]: ./ckout-workflows.md [co]: /help?cmd=checkout #### <a id="pullup"></a> Update vs Pull The closest equivalent to [`git pull`][gpull] is not [`fossil pull`][fpull], but in fact [`fossil up`][up]. This is because Fossil tends to follow the CVS command design: `cvs up` pulls changes from the central CVS repository and merges them into the local working directory, so that’s what `fossil up` does, too. (This design choice also tends to make Fossil feel comfortable to Subversion expatriates.) The `fossil pull` command is simply the reverse of `fossil push`, so that `fossil sync` [is functionally equivalent to](./sync.wiki#sync): fossil push ; fossil pull There is no implicit “and update the local working directory†step in Fossil’s push, pull, or sync commands, as there is with `git pull`. Someone coming from the Git perspective may perceive that `fossil up` has two purposes: * Without the optional `VERSION` argument, it updates the working check-out to the tip of the current branch, as `git pull` does. * Given a `VERSION` argument, it updates to the named version. If that’s the name of a branch, it updates to the *tip* of that branch, as `git checkout BRANCH` does. In fact, these are the same operation, so they’re the same command in Fossil. The first form simply allows the `VERSION` to be implicit: the tip of the current branch. We think this is a more sensible command design than `git pull` vs `git checkout`. ([…vs `git checkout` vs `git checkout`!][gcokoan]) [fpull]: /help?cmd=pull [gpull]: https://git-scm.com/docs/git-pull [gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well #### <a id="close" name="dotfile"></a> Closing a Check-Out The [`fossil close`][close] command dissociates a check-out directory from the Fossil repository database, nondestructively inverting [`fossil open`][open]. (Contrast [its closest inverse](#worktree), `git worktree remove`, which *is* destructive in Git!) This Fossil command does not remove the managed files, and unless you give the `--force` option, it won’t let you close the check-out with uncommitted changes to those managed files. The `close` command also refuses to run without `--force` when you have certain other precious per-checkout data that Fossil stores in the `.fslckout` file at the root of a check-out directory. This is a SQLite database that keeps track of local state such as what version you have checked out, the contents of the [stash] for that working directory, the [undo] buffers, per-checkout [settings][set], and so forth. The stash and undo buffers are considered precious uncommitted changes, so you have to force Fossil to discard these as part of closing the check-out. Thus, `.fslckout` is not the same thing as `.git`! In native Windows builds of Fossil — that is, excluding Cygwin and WSL builds, which follow POSIX conventions — this file is called `_FOSSIL_` instead to get around the historical 3-character extension limit with certain legacy filesystems. Closing a check-out directory is a rare operation. One use case is that you’re about to delete the directory, so you want Fossil to forget about it for the purposes of commands like [`fossil all`][all]. Even that isn’t necessary, because Fossil will detect that this has happened and forget the working directory for you. [all]: /help?cmd=all #### <a id="worktree"></a> Git Worktrees There are at least three different ways to get [Fossil-style multiple check-out directories][mcw] with Git. The old way is to simply symlink the `.git` directory between working trees: mkdir ../foo-branch ln -s ../actual-clone-dir/.git . git checkout foo-branch The symlink trick has a number of problems, the largest being that symlinks weren’t available on Windows until Vista, and until the Windows 10 Creators Update was released in spring of 2017, you had to be an Administrator to use the feature besides. ([Source][wsyml]) Git 2.5 solved this problem back when Windows XP was Microsoft’s current offering by adding the `git-worktree` command: git worktree add ../foo-branch foo-branch cd ../foo-branch That is approximately equivalent to this in Fossil: mkdir ../foo-branch cd ../foo-branch fossil open /path/to/repo.fossil foo-branch The Fossil alternative is wordier, but since this tends to be one-time setup, not something you do everyday, the overhead is insignificant. This author keeps a “scratch†check-out for cases where it’s inappropriate to reuse the “trunk†check-out, isolating all of my expedient switch-in-place actions to that one working directory. Since the other peer check-outs track long-lived branches, and that set rarely changes once a development machine is set up, I rarely pay the cost of these wordier commands. That then leads us to the closest equivalent in Git to [closing a Fossil check-out](#close): git worktree remove . Note, however, that unlike `fossil close`, once the Git command determines that there are no uncommitted changes, it blows away all of the checked-out files! Fossil’s alternative is shorter, easier to remember, and safer. There’s another way to get Fossil-like separate worktrees in Git: git clone --separate-git-dir repo.git https://example.com/repo This allows you to have your Git repository directory entirely separate from your working tree, with `.git` in the check-out directory being a file that points to `../repo.git`, in this example. [mcw]: ./ckout-workflows.md#mcw [wsyml]: https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ #### <a id="iip"></a> Init in Place To illustrate the differences that Fossil’s separation of repository from working directory creates in practice, consider this common Git “init in place†method for creating a new repository from an existing tree of files, perhaps because you are placing that project under version control for the first time: cd long-established-project git init git add * git commit -m "Initial commit of project." The closest equivalent in Fossil is: cd long-established-project fossil init .fsl fossil open --force .fsl fossil add * fossil ci -m "Initial commit of project." Note that unlike in Git, you can abbreviate the “`commit`†command in Fossil as “`ci`†for compatibility with CVS, Subversion, etc. This creates a `.fsl` repo DB at the root of the project check-out to emulate the `.git` repo dir. We have to use the `--force` flag on opening the new repo because Fossil expects you to open a repo into an empty directory in order to avoid spamming the contents of a repo over an existing directory full of files. Here, we know the directory contains files that will soon belong in the repository, though, so we override this check. From then on, Fossil works like Git, for the purposes of this example. We’ve drawn this example to create a tight parallel between Fossil and Git, not to commend this `.fsl`-at-project-root trick to you. A better choice would be `~/museum/home/long-established-project.fossil`, if you’re following [the directory scheme exemplified in the glossary](./glossary.md#repository). That said, it does emphasize an earlier point: Fossil doesn’t care where you put the repo DB file or what you name it. [clone]: /help?cmd=clone [close]: /help?cmd=close [gloss]: ./glossary.md [open]: /help?cmd=open [set]: /help?cmd=setting [server]: /help?cmd=server [stash]: /help?cmd=stash [undo]: /help?cmd=undo ## <a id="log"></a> Fossil’s Timeline Is the “Log†Git users often need to use the `git log` command to dig linearly through commit histories due to its [weak data model][wdm], giving [O(n) performance][ocomp]. Fossil parses a huge amount of information out of commits that allow it to produce its [timeline CLI][tlc] and [its `/timeline` web view][tlw] using indexed SQL lookups, which generally have the info you would have to manually extract from `git log`, produced much more quickly than Git can, all else being equal: operations over [SQLite’s B-tree data structures][btree] generally run in O(log n) time, faster than O(n) for equal *n* when the constants are equal. Yet the constants are *not* equal because Fossil reads from a single disk file rather than visit potentially many files in sequence as Git must, so the OS’s buffer cache can result in [still better performance][35pct]. Unlike Git’s log, Fossil’s timeline shows info across branches by default, a feature for maintaining better situational awareness. Although the `fossil timeline` command has no way to show a single branch’s commits, you can restrict your view like this using the web UI equivalent by clicking the name of a branch on the `/timeline` or `/brlist` page. (Or manually, by adding the `r=` query parameter.) Note that even in this case, the Fossil timeline still shows other branches where they interact with the one you’ve referenced in this way; again, better situational awareness. #### <a id="emu-log"></a> Emulating `git log` If you truly need a backwards-in-time-only view of history in Fossil to emulate `git log`, this is as close as you can currently come: fossil timeline parents current Again, though, this isn’t restricted to a single branch, as `git log` is. Another useful rough equivalent is: git log --raw fossil time -v This shows what changed in each version, though Fossil’s view is more a summary than a list of raw changes. To dig deeper into single commits, you can use Fossil’s [`info` command][infoc] or its [`/info` view][infow]. Inversely, you may more exactly emulate the default `fossil timeline` output with `git log --name-status`. #### <a id="whatchanged"></a> What Changed? A related — though deprecated — command is `git whatchanged`, which gives results similar to `git log --raw`, so we cover it here. Though there is no `fossil whatchanged` command, the same sort of information is available. For example, to pull the current changes from the remote repository and then inspect them before updating the local working directory, you might say this in Git: git fetch git whatchanged ..@{u} …which you can approximate in Fossil as: fossil pull fossil up -n fossil diff --from tip To invert the `diff` to show a more natural patch, the command needs to be a bit more complicated, since you can’t currently give `--to` without `--from`. fossil diff --from current --to tip Rather than use the “dry run†form of [the `update` command][up], you can say: fossil timeline after current …or if you want to restrict the output to the current branch: fossil timeline descendants current #### <a id="ckin-names"></a> Symbolic Check-In Names Note the use of [human-readable symbolic version names][scin] in Fossil rather than [Git’s cryptic notations][gcn]. For a more dramatic example of this, let us ask Git, “What changed since the beginning of last month?†being October 2020 as I write this: git log master@{2020-10-01}..HEAD That’s rather obscure! Fossil answers the same question with a simpler command: fossil timeline after 2020-10-01 You may need to add `-n 0` to bypass the default output limit of `fossil timeline`, 20 entries. Without that, this command reads almost like English. Some Git users like to write commands like the above so: git log @{2020-10-01}..@ Is that better? “@†now means two different things: an at-time reference and a shortcut for `HEAD`! If you are one of those that like short commands, Fossil’s method is less cryptic: it lets you shorten words in most cases up to the point that they become ambiguous. For example, you may abbreviate the last `fossil` command in the prior section: fossil tim d c …beyond which the `timeline` command becomes ambiguous with `ticket`. Some Fossil users employ shell aliases, symlinks, or scripts to shorten the command still further: alias f=fossil f tim d c Granted, that’s rather obscure, but you you can also choose something intermediate like “`f time desc curr`â€, which is reasonably clear. [35pct]: https://www.sqlite.org/fasterthanfs.html [btree]: https://sqlite.org/btreemodule.html [gcn]: https://git-scm.com/docs/gitrevisions [infoc]: /help?cmd=info [infow]: /help?cmd=/info [ocomp]: https://www.bigocheatsheet.com/ [tlc]: /help?cmd=timeline [tlw]: /help?cmd=/timeline [up]: /help?cmd=update [wdm]: ./fossil-v-git.wiki#durable ## <a id="dhead"></a> Detached HEAD State The SQL indexes in Fossil which we brought up above have a useful side benefit: you cannot have a [detached HEAD state][gdh] in Fossil, the source of untold pain and data loss in Git. It simply cannot be done in Fossil, because the indexes always let us find our way back into the hash tree. ## <a id="slcom"></a> Summary Line Convention in Commit Comments The Git convention of a [length-limited summary line][lsl] at the start of commit comments is not enforced or obeyed by default in Fossil. However, there is a setting under Admin → Timeline → “Truncate comment at first blank line (Git-style)†to change this for `/timeline` displays. Alternately, you could enable the “Allow block-markup in timeline†setting under Admin → Timeline, then apply [local skin customizations][cskin] to put that first comment in bold or whatever suits. Because this isn’t a typical Fossil convention, you’re likely to find other odd differences between it and Git-based infrastructure. For instance, Vim doesn’t ship with syntax support for Fossil commit messages if you set `EDITOR=vim` in your shell environment, so you won’t get over-limit highlighting for first-line text beyond the 50th character and such, because it doesn’t recognize Fossil commit messages and apply similar rules as to Git commit messages. [cskin]: ./customskin.md [lsl]: https://chris.beams.io/posts/git-commit/#limit-50 <a id="staging"></a> ## There Is No Staging Area Fossil omits the "Git index" or "staging area" concept. When you type "`fossil commit`" _all_ changes in your check-out are committed, automatically. There is no need for the "-a" option as with Git. If you only want to commit _some_ of the changes, list the names of the files or directories you want to commit as arguments, like this: fossil commit src/feature.c doc/feature.md examples/feature Note that the last element is a directory name, meaning “any changed file under the `examples/feature` directory.†Although there are currently no <a id="csplit"></a>[commit splitting][gcspl] features in Fossil like `git add -p`, `git commit -p`, or `git rebase -i`, you can get the same effect by converting an uncommitted change set to a patch and then running it through [Patchouli]. Rather than use `fossil diff -i` to produce such a patch, a safer and more idiomatic method would be: fossil stash save -m 'my big ball-o-hackage' fossil stash diff > my-changes.patch That stores your changes in the stash, then lets you operate on a copy of that patch. Each time you re-run the second command, it will take the current state of the working directory into account to produce a potentially different patch, likely smaller because it leaves out patch hunks already applied. In this way, the combination of working tree and stash replaces the need for Git’s index feature. This also solves a philosophical problem with `git commit -p`: how can you test that a split commit doesn’t break anything if you do it as part of the commit action? Git’s lack of an autosync feature means you can commit locally and then rewrite history if the commit doesn’t work out, but we’d rather make changes only to the working directory, test the changes there, and only commit once we’re sure it’s right. This also explains why we don’t have anything like `git rebase -i` to split an existing commit: in Fossil, commits are *commitments,* not something you’re allowed to go back and rewrite later. If someone does [contribute][ctrb] a commit splitting feature to Fossil, we’d expect it to be an interactive form of [`fossil stash apply`][stash], rather than follow Git’s ill-considered design leads. Until then, there’s the third-party tool [`fnc`][fnc] and [its interactive `stash` command][fncsta]. [ctrb]: https://fossil-scm.org/fossil/doc/trunk/www/contribute.wiki [fnc]: https://fnc.bsdbox.org/ [fncsta]: https://fnc.bsdbox.org/uv/doc/fnc.1.html#stash [gcspl]: https://git-scm.com/docs/git-rebase#_splitting_commits [Patchouli]: https://pypi.org/project/patchouli/ <a id="bneed"></a> ## Create Branches at Point of Need, Rather Than Ahead of Need Fossil prefers that you create new branches as part of the first commit on that branch: fossil commit --branch my-branch If that commit is successful, your local check-out directory is then switched to the tip of that branch, so subsequent commits don’t need the “`--branch`†option. You simply say `fossil commit` again to continue adding commits to the tip of that branch. To switch back to the parent branch, say something like: fossil update trunk (This is approximately equivalent to `git checkout master`.) Fossil does also support the Git style, creating the branch ahead of need: fossil branch new my-branch fossil up my-branch ...work on first commit... fossil commit This is more verbose, giving the same overall effect though the initial actions are inverted: create a new branch for the first commit, switch the check-out directory to that branch, and make that first commit. As above, subsequent commits are descendants of that initial branch commit. We think you’ll agree that creating a branch as part of the initial commit is simpler. Fossil also allows you to move a check-in to a different branch *after* you commit it, using the "`fossil amend`" command. For example: fossil amend current --branch my-branch This works by inserting a tag into the repository that causes the web UI to relabel commits from that point forward with the new name. Like Git, Fossil’s fundamental data structure is the interlinked DAG of commit hashes; branch names are supplemental data for making it easier for the humans to understand this DAG, so this command does not change the core history of the project, only annotate it for better display to the humans. (The version string “current†is one of the [special check-in names][scin] in Fossil. See that document for the many other names you can give to “`amend`â€, or indeed to any other Fossil command documented to accept a `VERSION` or `NAME` string.) [scin]: ./checkin_names.wiki <a id="syncall"></a> ## Sync Is All-or-Nothing Fossil does not support the concept of syncing, pushing, or pulling individual branches. When you sync/push/pull in Fossil, it processes all artifacts in its hash tree: branches, tags, wiki articles, tickets, forum posts, technotes… This is [not quite “everything,†full stop][bu], but it’s close. [Fossil is an AP-mode system][capt], which in this case means it works *very hard* to ensure that all repos are as close to identical as it can make them under this eventually-consistent design philosophy. Branch *names* sync automatically in Fossil, not just the content of those branches. That means this common Git command: git push origin master …is simply this in Fossil: fossil push Fossil doesn’t need to be told what to push or where to push it: it just keeps using the same remote server URL you gave it last until you [tell it to do something different][rem]. It pushes all branches, not just one named local branch. [capt]: ./cap-theorem.md [rem]: /help?cmd=remote <a id="autosync"></a> ## Autosync Fossil’s [autosync][wflow] feature, normally enabled, has no equivalent in Git. If you want Fossil to behave like Git, you can turn it off: fossil set autosync 0 Let’s say that you have a typical server-and-workstations model with two working clones on different machines, that you have disabled autosync, and that this common sequence then occurs: 1. Alice commits to her local clone and *separately* pushes the change up to Condor — their central server — in typical Git fashion. 2. Bob does the same. 3. Alice brings Bob’s changes down from Condor with “`fossil pull`,†sees what he did to their shared working branch, and becomes most wrathful. (Tsk, tsk.) We’ll get to what you do about this situation [below](#reset), but for now let us focus on the fact that disabling autosync makes it easier for [forks] to occur in the development history. If all three machines had been online and syncing at the time the sequence above began, Bob would have been warned in step 2 that committing to the central repo would create a fork and would be invited to fix it before committing. Likewise, it would allow Fossil to warn Alice about the new tip-of-branch commit the next time she triggers an implicit autosync at step 3, giving her a chance to bring Bob’s changes down in a non-conflicting manner, allowing work to proceed with minimal fuss. Fossil, being a distributed version control system, cannot guarantee that sequence of events. Because it allows Alice’s work to proceed asynchronously, it gives her the chance to create *another* inadvertent fork before she can trigger an autosync. This is not a serious problem; Fossil resolves it the same way as with Bob, by inviting her to fix this second fork in the same manner as it did with Bob. It gets both parties back onto a single track as expeditiously as possible by moving the synchronization point out of the expensive human-time workflow and into the software system, where it’s cheapest to resolve. Autosync provides Fossil with most of the advantages of a centralized version control system while retaining the advantages of distributed version control: 1. Your work stays synced up with your coworkers’ efforts as long as your machine can connect to the remote repository. At need, you can go off-network and continue work atop the last version you synced with the remote. 2. You get implicit off-machine backup of your commits. Unlike centralized version control, though, you can still work while disconnected; your changes will sync up with the remote once you get back online. 3. Because there is [little distinction][bu] between the clones in the Fossil model — unlike in Git, where clones often quickly diverge from each other, quite possibly on purpose — the backup advantage applies in inverse as well: if the remote server falls over dead, one of those with a clone of that repository can stand it back up, and everyone can get back to work simply by re-pointing their local repo at the new remote. If the failed remote comes back later, it can sync with the new central version, then perhaps take over as the primary source of truth once again. [bu]: ./backup.md [forks]: ./branching.wiki [setup]: ./caps/admin-v-setup.md#apsu [wflow]: ./concepts.wiki#workflow <a id="reset"></a> ## Resetting the Repository Extending from [the prior item](#syncall), you may correctly infer that “[delete the project and download a fresh copy][x1597]†has no part in the Fossil Way. Ideally, you should never find yourself forced into desperate measures like this:(^Parsing the output of `fossil status` is usually a mistake since it relies on a potentially unstable interface. We make no guarantee that there will always be a line beginning with “`repo`†and that it will be separated from the repository’s file name by a colon. The simplified example above is also liable to become confused by whitespace in file names.) ``` $ repo=$(fossil status | grep ^repo | cut -f2 -d:) $ url=$(fossil remote) $ fossil close # Stop here and think if it warns you! $ mv $repo ${repo}.old $ fossil clone $url $repo $ fossil open --force $repo ``` What, then, should you as a Git transplant do instead when you find yourself reaching for “`git reset`â€? Since the correct answer to that depends on why you think it’s a good solution to your immediate problem, we’ll take our motivating scenario from the problem setup above, where we discussed Fossil’s [autosync] feature. Let us further say Alice’s pique results from a belief that Bob’s commit is objectively wrong-headed and should be expunged henceforth. Since Fossil goes out of its way to ensure that [commits are durable][wdm], it should be no further surprise that there is no easier method to reset Bob’s clone in favor of Alice’s than the above sequence in Fossil’s command set. Except in extreme situations, we believe that sort of thing is unnecessary. Instead, Bob can say something like this: ``` fossil amend --branch MISTAKE --hide --close -m "mea culpa" tip fossil up trunk fossil push ``` Unlike in Git, the “`amend`†command doesn’t modify prior committed artifacts. Bob’s first command doesn’t delete anything, merely tells Fossil to hide his mistake from timeline views by inserting a few new records into the local repository to change how the client interprets the data it finds there henceforth.(^One to change the tag marking this commit’s branch name to “`MISTAKE`,†one to mark that branch as hidden, and one to close it to further commits.) Bob’s second command switches his working directory back to the prior commit on that branch. We’re presuming it was “`trunk`†for the sake of the example, but it works for any parent branch name. The command works because the name “`trunk`†now means something different to Fossil by virtue of the first command. Bob’s third command pushes the changes up to the central machine to inform everyone else of his amendment.(^Amendments don’t autosync in Fossil because they don’t change any previous commits, allowing the other clones to continue working safely with their existing commit hashes.) In this scheme, Alice then needs to say “`fossil update trunk`†in order to return her check-out’s parent commit to the previous version lest her next attempted commit land atop this mistake branch. The fact that Bob marked the branch as closed will prevent that from going thru, cluing Alice into what she needs to do to remedy the situation, but that merely shows why it’s a better workflow if Alice makes the amendment herself: ``` fossil amend --branch MISTAKE --hide --close \ -m "shunt Bob’s erroneous commit off" tip fossil up trunk fossil push ``` Then she can fire off an email listing Bob’s assorted failings and go about her work. This asynchronous workflow solves the problem without requiring explicit coordination with Bob. When he gets his email, he can then say “`fossil up trunk`†himself, which by default will trigger an autosync, pulling down Alice’s amendments and getting him back onto her development track. Remember that [branch names need not be unique](#btnames) in Fossil. You are free to reuse this “`MISTAKE`†branch name as often as you need to. [autosync]: #autosync [x1597]: https://xkcd.com/1597/ <a id="trunk"></a> ## The Main Branch Is Called "`trunk`" In Fossil, the default name for the main branch is "`trunk`". The "`trunk`" branch in Fossil corresponds to the "`master`" branch in stock Git or to [the “`main`†branch in GitHub][mbgh]. Because the `fossil git export` command has to work with both stock Git and with GitHub, Fossil uses Git’s traditional default rather than GitHub’s new default: your Fossil repo’s “trunk†branch becomes “master†when [mirroring to GitHub][mirgh] unless you give the `--mainbranch` option. We do not know what happens on subsequent exports if you later rename this branch on the GitHub side. [mbgh]: https://github.com/github/renaming [mirgh]: ./mirrortogithub.md <a id="unmanaged"></a> ## Status Does Not Show Unmanaged Files The "`fossil status`" command shows you what files in your check-out have been edited and scheduled for adding or removing at the next commit. But unlike "`git status`", the "`fossil status`" command does not warn you about unmanaged files in your local check-out. There is a separate "`fossil extras`" command for that. <a id="rebase"></a> ## There Is No Rebase Fossil does not support rebase, [on purpose][3]. This is a deliberate design decision that the Fossil community has thought about carefully and discussed many times, resulting in the linked document. If you are fond of rebase, you should read it carefully before expressing your views: it not only answers many of the common arguments in favor of rebase known at the time the document’s first draft was written, it’s been revised multiple times to address less common objections as well. Chances are not good that you are going to come up with a new objection that we haven’t already considered and addressed there. There is only one sub-feature of `git rebase` that is philosophically compatible with Fossil yet which currently has no functional equivalent. We [covered this and the workaround for its lack](#csplit) above. [3]: ./rebaseharm.md ## <a id="cdiff"></a> Colorized Diffs When you run `git diff` on an ANSI X3.64 capable terminal, it uses color to distinguish insertions, deletions, and replacements, but as of this writing, `fossil diff` produces traditional uncolored [unified diff format][udiff] output, suitable for producing a [patch file][pfile]. Nevertheless, there are multiple ways to get colorized diff output from Fossil: * The most direct method is to delegate diff behavior back to Git: fossil set --global diff-command 'git diff --no-index' The flag permits it to diff files that aren’t inside a Git repository. * Another method is to install [`colordiff`][cdiff] — included in [many package systems][cdpkg] — then say: fossil set --global diff-command 'colordiff -wu' Because this is unconditional, unlike `git diff --color=auto`, you will then have to remember to add the `-i` option to `fossil diff` commands when you want color disabled, such as when producing `patch(1)` files or piping diff output to another command that doesn’t understand ANSI escape sequences. There’s an example of this [below](#dstat). * Use the Fossil web UI to diff existing commits. * To diff the current working directory contents against some parent instead, Fossil’s diff command can produce colorized HTML output and open it in the OS’s default web browser. For example, `fossil diff -by` will show side-by-side diffs. * Use the older `fossil diff --tk` option to do much the same using Tcl/Tk instead of a browser. Viewed this way, Fossil doesn’t lack colorized diffs, it simply has *one* method where they *aren’t* colorized. [cdpkg]: https://repology.org/project/colordiff/versions [pfile]: https://en.wikipedia.org/wiki/Patch_(Unix) [udiff]: https://en.wikipedia.org/wiki/Diff#Unified_format ## <a id="show"></a> Showing Information About Commits While there is no direct equivalent to Git’s “`show`†command, similar functionality is present in Fossil under other commands: #### <a id="patch"></a> Show a Patch for a Commit git show -p COMMIT_ID …gives much the same output as fossil diff --checkin COMMIT_ID …only without the patch email header. Git comes out of the [LKML] world, where emailing a patch is a normal thing to do. Fossil is [designed for cohesive teams][devorg] where drive-by patches are rarer. You can use any of [Fossil’s special check-in names][scin] in place of the `COMMIT_ID` in this and later examples. Fossil docs usually say “`VERSION`†or “`NAME`†where this is allowed, since the version string or name might not refer to a commit ID, but instead to a forum post, a wiki document, etc. For instance, the following command answers the question “What did I just commit?†fossil diff --checkin tip …or equivalently using a different symbolic commit name: fossil diff --from prev [devorg]: ./fossil-v-git.wiki#devorg [LKML]: https://lkml.org/ #### <a id="cmsg"></a> Show a Specific Commit Message git show -s COMMIT_ID …is fossil time -n 1 COMMIT_ID …or with a shorter, more obvious command, though with more verbose output: fossil info COMMIT_ID The `fossil info` command isn’t otherwise a good equivalent to `git show`; it just overlaps its functionality in some areas. Much of what’s missing is present in the corresponding [`/info` web view][infow], though. #### <a id="dstat"></a> Diff Statistics Fossil’s closest internal equivalent to commands like `git show --stat` is: fossil diff -i --from 2020-04-01 --numstat The `--numstat` output is a bit cryptic, so we recommend delegating this task to [the widely-available `diffstat` tool][dst], which gives a histogram in its default output mode rather than bare integers: fossil diff -i -v --from 2020-04-01 | diffstat We gave the `-i` flag in both cases to force Fossil to use its internal diff implementation, bypassing [your local `diff-command` setting][dcset]. The `--numstat` option has no effect when you have an external diff command set, and some diff command alternatives like [`colordiff`][cdiff] (covered [above](#cdiff)) produce output that confuses `diffstat`. If you leave off the `-v` flag in the second example, the `diffstat` output won’t include info about any newly-added files. [cdiff]: https://www.colordiff.org/ [dcset]: https://fossil-scm.org/home/help?cmd=diff-command [dst]: https://invisible-island.net/diffstat/diffstat.html <a id="btnames"></a> ## Branch and Tag Names Fossil has no special restrictions on the names of tags and branches, though you might want to keep [Git's tag and branch name restrictions][gcrf] in mind if you plan on [mirroring your Fossil repository to GitHub][mirgh]. Fossil does not require tag and branch names to be unique. It is common, for example, to put a “`release`†tag on every release for a Fossil-hosted project. This does not create a conflict in Fossil, since Fossil resolves the ambiguity in a predictable way: the newest match wins. Therefore, “`fossil up release`†always gets you the current release in a project that uses this tagging convention. [The `fossil git export` command][fge] squashes repeated tags down to a single instance to avoid confusing Git, exporting only the newest tag, emulating Fossil’s own ambiguity resolution rule as best it can within Git’s limitations. [fge]: /help?cmd=git [gcrf]: https://git-scm.com/docs/git-check-ref-format <a id="cpickrev"></a> ## Cherry-Picking and Reverting Commits Git’s separate "`git cherry-pick`" and “`git revert`†commands are options to the [`fossil merge` command][merge]: `--cherrypick` and `--backout`, respectively. We view this as sensible, since these are both merge operations, and the two actions differ only in direction. Unlike in Git, the Fossil file format remembers cherrypicks and backouts and can later show them as dashed lines on the graphical timeline. [merge]: /help?cmd=merge <a id="mvrm"></a> ## File Moves and Renames Are Soft by Default The "[`fossil mv`][mv]" and "[`fossil rm`][rm]" commands work like they do in CVS in that they schedule the changes for the next commit by default: they do not actually rename or delete the files in your check-out. If you don’t like that default, you can change it globally: fossil setting --global mv-rm-files 1 Now these commands behave like in Git in any Fossil repository where this setting hasn’t been overridden locally. If you want to keep Fossil’s soft `mv/rm` behavior most of the time, you can cast it away on a per-command basis: fossil mv --hard old-name new-name [mv]: /help?cmd=mv [rm]: /help?cmd=rm ---- ## <a id="cvdate" name="cs1"></a> Case Study 1: Checking Out a Version by Date Let’s get into something a bit more complicated: a case study showing how the concepts lined out above cause Fossil to materially differ in day-to-day operation from Git. Why would you want to check out a version of a project by date? Perhaps your customer gave you a vague bug report referencing only a date rather than a version. Or, you may be poking semi-randomly through history to find a “good†version to anchor the start point of a [`fossil bisect`][fbis] operation. My search engine’s first result for “git checkout by date†is [this highly-upvoted accepted Stack Overflow answer][gcod]. The first command it gives is based on Git’s [`rev-parse` feature][grp]: git checkout master@{2020-03-17} There are a number of weaknesses in this command. From least to most critical: 1. It’s a bit cryptic. Leave off the refname or punctuation, and it means something else. You cannot simplify the cryptic incantation in the typical use case. 2. A date string in Git without a time will be interpreted as “[at the local wall clock time on the given date][gapxd],†so the command means something different from one second to the next! This can be a serious problem if there are multiple commits on that date, because the command will give different results depending on the time of day you run it. 3. It gives misleading output if there is no close match for the date in the local [reflog]. It starts out empty after a fresh clone, and while it does build up as you use that clone, Git [automatically prunes][gle] the reflog to 90 days of history by default. This means the command above can give different results from one machine to the next, or even from one day to the next on the same clone. The command won’t fail outright if the reflog can’t resolve the given date: it simply gives the closest commit it can come up with, even if it’s months or years out from your target! Sometimes it gives a warning about the reflog not going back far enough to give a useful result, and sometimes it doesn’t. If you’re on a fresh clone, you are likely to get the “tip†commit’s revision ID no matter what date value you give. Git tries its best, but because it’s working from a purgeable and possibly-stale local cache, you cannot trust its results. Consequently, we cannot recommend this command at all. It’s unreliable even in the best case. That same Stack Overflow answer therefore goes on to recommend an entirely different command: git checkout $(git rev-list -n 1 --first-parent --before="2020-03-17" master) We believe you get such answers to Git help requests in part because of its lack of an always-up-to-date [index into its log](#log) and in part because of its “small tools loosely joined†design philosophy. This sort of command is therefore composed piece by piece: <p style="text-align:center">◆ ◆ ◆</p> “Oh, I know, I’ll search the rev-list, which outputs commit IDs by parsing the log backwards from `HEAD`! Easy!†git rev-list --before=2020-03-17 “Blast! Forgot the commit ID!†git rev-list --before=2020-03-17 master “Double blast! It just spammed my terminal with revision IDs! I need to limit it to the single closest match: git rev-list -n 1 --before=2020-03-17 master “Okay, it gives me a single revision ID now, but is it what I’m after? Let’s take a look…†git show $(git rev-list -n 1 --before=2020-03-17 master) “Oops, that’s giving me a merge commit, not what I want. Off to search the web… Okay, it says I need to give either the `--first-parent` or `--no-merges` flag to show only regular commits, not merge-commits. Let’s try the first one:†git show $(git rev-list -n 1 --first-parent --before=2020-03-17 master) “Better. Let’s check it out:†git checkout $(git rev-list -n 1 --first-parent --before=2020-03-17 master) “Success, I guess?†<p style="text-align:center">◆ ◆ ◆</p> This vignette is meant to explain some of Git’s popularity: it rewards the sort of people who enjoy puzzles, many of whom are software developers and thus need a tool like Git. Too bad if you’re just a normal user. And too bad if you’re a Windows user who doesn’t want to use [Git Bash][gbash], since neither of the stock OS command shells have a command interpolation feature needed to run that horrid command. This alternative command still has weakness #2 above: if you run the second `git show` command above on [Git’s own repository][gitgh], your results may vary because there were four non-merge commits to Git on the 17th of March, 2020. You may be asking with an exasperated huff, “What is your *point*, man?†The point is that the equivalent in Fossil is simply: fossil up 2020-03-17 …which will *always* give the commit closest to midnight UTC on the 17th of March, 2020, no matter whether you do it on a fresh clone or a stale one. The answer won’t shift about from one clone to the next or from one local time of day to the next. We owe this reliability and stability to three Fossil design choices: * Parse timestamps from all commits on clone into a local commit index, then maintain that index through subsequent commits and syncs. * Use an indexed SQL `ORDER BY` query to match timestamps to commit IDs for a fast and consistent result. * Round incomplete timestamp strings up using [rules][frud] consistent across computers and local time of day. [frud]: https://fossil-scm.org/home/file/src/name.c?ci=d2a59b03727bc3&ln=122-141 [gbash]: https://appuals.com/what-is-git-bash/ [gapxd]: https://github.com/git/git/blob/7f7ebe054a/date.c#L1298-L1300 [gcod]: https://stackoverflow.com/a/6990682/142454 [gdh]: https://www.git-tower.com/learn/git/faq/detached-head-when-checkout-commit/ [gitgh]: https://github.com/git/git/ [gle]: https://git-scm.com/docs/git-reflog#_options_for_expire [gmc]: https://github.com/git/git/commit/67b0a24910fbb23c8f5e7a2c61c339818bc68296 [grp]: https://git-scm.com/docs/git-rev-parse [reflog]: https://git-scm.com/docs/git-reflog ---- ## <a id="morigin" name="cs2"></a> Case Study 2: Multiple "origin" Servers Now let us consider a common use case at the time of this writing — during the COVID-19 pandemic — where you’re working from home a lot, going into the office one part-day a week only to do things that have to be done on-site at the office. Let us also say you have no remote access back into the work LAN, such as because your site IT is paranoid about security. You may still want off-machine backups of your commits while working from home, so you need the ability to quickly switch between the “home†and “work†remote repositories, with your laptop acting as a kind of [sneakernet][sn] link between the big development server at the office and your family’s home NAS. #### Git Method We first need to clone the work repo down to our laptop, so we can work on it at home: git clone https://dev-server.example.com/repo cd repo git remote rename origin work The last command is optional, strictly speaking. We could continue to use Git’s default name for the work repo’s origin — sensibly enough called “`origin`†— but it makes later commands harder to understand, so we rename it here. This will also make the parallel with Fossil easier to draw. The first time we go home after this, we have to reverse-clone the work repo up to the NAS: ssh my-nas.local 'git init --bare /SHARES/dayjob/repo.git' git push --all ssh://my-nas.local//SHARES/dayjob/repo.git Realize that this is carefully optimized down to these two long commands. In practice, we’d expect a user typing these commands by hand from memory to need to give four or more commands here instead. Packing the “`git init`†call into the “`ssh`†call is something more often done in scripts and documentation examples than done interactively, which then necessitates a third command before the push, “`exit`â€. There’s also a good chance that you’ll forget the need for the `--bare` option here to avoid a fatal complaint from Git that the laptop can’t push into a non-empty repo. If you fall into this trap, among the many that Git lays for newbies, you have to nuke the incorrectly initted repo, search the web or Git man pages to find out about `--bare`, and try again. Having navigated that little minefield, we can tell Git that there is a second origin, a “home†repo in addition to the named “work†repo we set up earlier: git remote add home ssh://my-nas.local//SHARES/dayjob/repo.git git config master.remote home We don’t have to push or pull because the remote repo is a complete clone of the repo on the laptop at this point, so we can just get to work now, committing along the way to get our work safely off-machine and onto our home NAS, like so: git add git commit git push We didn’t need to give a remote name on the push because we told it the new upstream is the home NAS earlier. Now Friday comes along, and one of your office-mates needs a feature you’re working on. You agree to come into the office later that afternoon to sync up via the dev server: git push work master # send your changes from home up git pull work master # get your coworkers’ changes Alternately, we could add “`--set-upstream/-u work`†to the first command if we were coming into work long enough to do several Git-based things, not just pop in and sync. That would allow the second to be just “`git pull`â€, but the cost is that when returning home, you’d have to manually reset the upstream again. This example also shows a consequence of that fact that [Git doesn’t sync branch names](#syncall): you have to keep repeating yourself like an obsequious supplicant: “Master, master.†Didn’t we invent computers to serve humans, rather than the other way around? #### Fossil Method Now we’re going to do the same thing using Fossil, with the commands arranged in blocks corresponding to those above for comparison. We start the same way, cloning the work repo down to the laptop: fossil clone https://dev-server.example.com/repo cd repo fossil remote add work https://dev-server.example.com/repo We’ve chosen the new “`fossil clone URI`†syntax rather than separate `clone` and `open` commands to make the parallel with Git clearer. [See above](#mwd) for more on that topic. Our [`remote` command][rem] is longer than the Git equivalent because Fossil currently has no short command to rename an existing remote. Worse, unlike with Git, we can’t just keep using the default remote name because Fossil uses that slot in its configuration database to store the *current* remote name, so on switching from work to home, the home URL will overwrite the work URL if we don’t give it an explicit name first. Although the Fossil commands are longer, so far, keep it in perspective: they’re one-time setup costs, easily amortized to insignificance by the shorter day-to-day commands below. On first beginning to work from home, we reverse-clone the Fossil repo up to the NAS: rsync repo.fossil my-nas.local:/SHARES/dayjob/ Now we’re beginning to see the advantage of Fossil’s simpler model, relative to the tricky “`git init && git push`†sequence above. Fossil’s alternative is almost impossible to get wrong: copy this to that. *Done.* We’re relying on the `rsync` feature that creates up to one level of missing directory (here, `dayjob/`) on the remote. If you know in advance that the remote directory already exists, you could use a slightly shorter `scp` command instead. Even with the extra 2 characters in the `rsync` form, it’s much shorter because a Fossil repository is a single SQLite database file, not a tree containing a pile of assorted files. Because of this, it works reliably without any of [the caveats inherent in using `rsync` to clone a Git repo][grsync]. Now we set up the second remote, which is again simpler in the Fossil case: fossil remote add home ssh://my-nas.local//SHARES/dayjob/repo.fossil fossil remote home The first command is nearly identical to the Git version, but the second is considerably simpler. And to be fair, you won’t find the “`git config`†command above in all Git tutorials. The more common alternative we found with web searches is even longer: “`git push --set-upstream home master`â€. Where Fossil really wins is in the next step, making the initial commit from home: fossil ci It’s one short command for Fossil instead of three for Git — or two if you abbreviate it as “`git commit -a && git push`†— because of Fossil’s [autosync] feature and deliberate omission of a [staging feature](#staging). The “Friday afternoon sync-up†case is simpler, too: fossil remote work fossil sync Back at home, it’s simpler still: we may be able to do away with the second command, saying just “`fossil remote home`†because the sync will happen as part of the next commit, thanks once again to Fossil’s autosync feature. If the working branch now has commits from other developers after syncing with the central repository, though, you’ll want to say “`fossil up`†to avoid creating an inadvertent fork in the branch. (Which is easy to fix if it occurs: `fossil merge` without arguments means “merge open tips on the current branch.â€) [grsync]: https://stackoverflow.com/q/1398018/142454 [qs]: ./quickstart.wiki [shwmd]: ./fossil-v-git.wiki#checkouts [sn]: https://en.wikipedia.org/wiki/Sneakernet |
Added www/globs.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 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 | # File Name Glob Patterns A [glob pattern][glob] is a text expression that matches one or more file names using wildcards familiar to most users of a command line. For example, `*` is a glob that matches any name at all, and `Readme.txt` is a glob that matches exactly one file. For purposes of Fossil's globs, a complete path name is just a string, and the globs do not apply any special meaning to the directory part of the name. Thus, the glob `*` matches any name, including any directory prefix, and `*/*` matches a name with _one or more_ directory components. A glob should not be confused with a [regular expression][regexp] (RE) even though they use some of the same special characters for similar purposes. [They are not fully compatible][greinc] pattern matching languages. Fossil uses globs when matching file names with the settings described in this document, not REs. [glob]: https://en.wikipedia.org/wiki/Glob_(programming) [greinc]: https://unix.stackexchange.com/a/57958/138 [regexp]: https://en.wikipedia.org/wiki/Regular_expression [Fossil’s `*-glob` settings](#settings) hold one or more patterns to cause Fossil to give matching named files special treatment. Glob patterns are also accepted in options to certain commands and as query parameters to certain Fossil UI web pages. For consistency, settings such as `empty-dirs` are parsed as a glob even though they aren’t then *applied* as a glob since it allows [the same syntax rules](#syntax) to apply. Where Fossil also accepts globs in commands, this handling may interact with your OS’s command shell or its C runtime system, because they may have their own glob pattern handling. We will detail such interactions below. ## <a id="syntax"></a>Syntax Where Fossil accepts glob patterns, it will usually accept a *list* of individual patterns separated from the others by whitespace or commas. The parser allows whitespace and commas in a pattern by quoting _the entire pattern_ with either single or double quotation marks. Internal quotation marks are treated literally. Moreover, a pattern that begins with a quote mark ends when the first instance of the same mark occurs, _not_ at a whitespace or comma. Thus, this: "foo bar"qux …constitutes _two_ patterns rather than one with an embedded space, in contravention of normal shell quoting rules. A list matches a file when any pattern in that list matches. A pattern must consume and match the *entire* file name to succeed. Partial matches are failed matches. Most characters in a glob pattern consume a single character of the file name and must match it exactly. For instance, “a†in a glob simply matches the letter “a†in the file name unless it is inside a special character sequence. Other characters have special meaning, and they may include otherwise normal characters to give them special meaning: :Pattern |:Effect --------------------------------------------------------------------- `*` | Matches any sequence of zero or more characters `?` | Matches exactly one character `[...]` | Matches one character from the enclosed list of characters `[^...]` | Matches one character *not* in the enclosed list Note that unlike [POSIX globs][pg], these special characters and sequences are allowed to match `/` directory separators as well as the initial `.` in the name of a hidden file or directory. This is because Fossil file names are stored as complete path names. The distinction between file name and directory name is “underneath†Fossil in this sense. [pg]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13 The bracket expressions above require some additional explanation: * A range of characters may be specified with `-`, so `[a-f]` matches exactly the same characters as `[abcdef]`. Ranges reflect Unicode code points without any locale-specific collation sequence. Therefore, this particular sequence never matches the Unicode pre-composed character `é`, for example. (U+00E9) * This dependence on character/code point ordering may have other effects to surprise you. For example, the glob `[A-z]` not only matches upper and lowercase ASCII letters, it also matches several punctuation characters placed between `Z` and `a` in both ASCII and Unicode: `[`, `\`, `]`, `^`, `_`, and <tt>\`</tt>. * You may include a literal `-` in a list by placing it last, just before the `]`. * You may include a literal `]` in a list by making the first character after the `[` or `[^`. At any other place, `]` ends the list. * You may include a literal `^` in a list by placing it anywhere except after the opening `[`. * Beware that a range must be specified from low value to high value: `[z-a]` does not match any character at all, preventing the entire glob from matching. Some examples of character lists: :Pattern |:Effect --------------------------------------------------------------------- `[a-d]` | Matches any one of `a`, `b`, `c`, or `d` but not `ä` `[^a-d]` | Matches exactly one character other than `a`, `b`, `c`, or `d` `[0-9a-fA-F]` | Matches exactly one hexadecimal digit `[a-]` | Matches either `a` or `-` `[][]` | Matches either `]` or `[` `[^]]` | Matches exactly one character other than `]` `[]^]` | Matches either `]` or `^` `[^-]` | Matches exactly one character other than `-` White space means the specific ASCII characters TAB, LF, VT, FF, CR, and SPACE. Note that this does not include any of the many additional spacing characters available in Unicode such as U+00A0, NO-BREAK SPACE. Because both LF and CR are white space and leading and trailing spaces are stripped from each glob in a list, a list of globs may be broken into lines between globs when the list is stored in a file, as for a versioned setting. Note that 'single quotes' and "double quotes" are the ASCII straight quote characters, not any of the other quotation marks provided in Unicode and specifically not the "curly" quotes preferred by typesetters and word processors. ## File Names to Match Before it is compared to a glob pattern, each file name is transformed to a canonical form: * all directory separators are changed to `/` * redundant slashes are removed * all `.` path components are removed * all `..` path components are resolved (There are additional details we are ignoring here, but they cover rare edge cases and follow the principle of least surprise.) The glob must match the *entire* canonical file name to be considered a match. The goal is to have a name that is the simplest possible for each particular file, and that will be the same regardless of the platform you run Fossil on. This is important when you have a repository cloned from multiple platforms and have globs in versioned settings: you want those settings to be interpreted the same way everywhere. Beware, however, that all glob matching in Fossil is case sensitive regardless of host platform and file system. This will not be a surprise on POSIX platforms where file names are usually treated case sensitively. However, most Windows file systems are case preserving but case insensitive. That is, on Windows, the names `ReadMe` and `README` are usually names of the same file. The same is true in other cases, such as by default on macOS file systems and in the file system drivers for Windows file systems running on non-Windows systems. (e.g. exfat on Linux.) Therefore, write your Fossil glob patterns to match the name of the file as checked into the repository. Some example cases: :Pattern |:Effect -------------------------------------------------------------------------------- `README` | Matches only a file named `README` in the root of the tree. It does not match a file named `src/README` because it does not include any characters that consume (and match) the `src/` part. `*/README` | Matches `src/README`. Unlike Unix file globs, it also matches `src/library/README`. However it does not match the file `README` in the root of the tree. `*README` | Matches `src/README` as well as the file `README` in the root of the tree as well as `foo/bar/README` or any other file named `README` in the tree. However, it also matches `A-DIFFERENT-README` and `src/DO-NOT-README`, or any other file whose name ends with `README`. `src/README` | Matches `src\README` on Windows because all directory separators are rewritten as `/` in the canonical name before the glob is matched. This makes it much easier to write globs that work on both Unix and Windows. `*.[ch]` | Matches every C source or header file in the tree at the root or at any depth. Again, this is (deliberately) different from Unix file globs and Windows wild cards. ## Where Globs are Used ### <a id="settings"></a>Settings that are Globs These settings are all lists of glob patterns: :Setting |:Description -------------------------------------------------------------------------------- `binary-glob` | Files that should be treated as binary files for committing and merging purposes `clean-glob` | Files that the [`clean`][] command will delete without prompting or allowing undo `crlf-glob` | Files in which it is okay to have `CR`, `CR`+`LF` or mixed line endings. Set to "`*`" to disable CR+LF checking `crnl-glob` | Alias for the `crlf-glob` setting `encoding-glob` | Files that the [`commit`][] command will ignore when issuing warnings about text files that may use another encoding than ASCII or UTF-8. Set to "`*`" to disable encoding checking `ignore-glob` | Files that the [`add`][], [`addremove`][], [`clean`][], and [`extras`][] commands will ignore `keep-glob` | Files that the [`clean`][] command will keep All may be [versioned, local, or global](settings.wiki). Use `fossil settings` to manage local and global settings, or a file in the repository's `.fossil-settings/` folder at the root of the tree named for each for versioned setting. Using versioned settings for these not only has the advantage that they are tracked in the repository just like the rest of your project, but you can more easily keep longer lists of more complicated glob patterns than would be practical in either local or global settings. The `ignore-glob` is an example of one setting that frequently grows to be an elaborate list of files that should be ignored by most commands. This is especially true when one (or more) IDEs are used in a project because each IDE has its own ideas of how and where to cache information that speeds up its browsing and building tasks but which need not be preserved in your project's history. Although the `empty-dirs` setting is not a list of glob patterns as such, it is *parsed* that way for consistency among the settings, allowing [the list parsing rules above](#syntax) to apply. ### <a id="commands"></a>Commands that Refer to Globs Many of the commands that respect the settings containing globs have options to override some or all of the settings. These options are usually named to correspond to the setting they override, such as `--ignore` to override the `ignore-glob` setting. These commands are: * [`add`][] * [`addremove`][] * [`changes`][] * [`clean`][] * [`commit`][] * [`extras`][] * [`merge`][] * [`settings`][] * [`status`][] * [`touch`][] * [`unset`][] The commands [`tarball`][] and [`zip`][] produce compressed archives of a specific checkin. They may be further restricted by options that specify glob patterns that name files to include or exclude rather than archiving the entire checkin. The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that implement or support with web servers provide a mechanism to name some files to serve with static content where a list of glob patterns specifies what content may be served. [`add`]: /help?cmd=add [`addremove`]: /help?cmd=addremove [`changes`]: /help?cmd=changes [`clean`]: /help?cmd=clean [`commit`]: /help?cmd=commit [`extras`]: /help?cmd=extras [`merge`]: /help?cmd=merge [`settings`]: /help?cmd=settings [`status`]: /help?cmd=status [`touch`]: /help?cmd=touch [`unset`]: /help?cmd=unset [`tarball`]: /help?cmd=tarball [`zip`]: /help?cmd=zip [`http`]: /help?cmd=http [`cgi`]: /help?cmd=cgi [`server`]: /help?cmd=server [`ui`]: /help?cmd=ui ### Web Pages that Refer to Globs The [`/timeline`][] page supports the query parameter `chng=GLOBLIST` that names a list of glob patterns defining which files to focus the timeline on. It also has the query parameters `t=TAG` and `r=TAG` that names a tag to focus on, which can be configured with `ms=STYLE` to use a glob pattern to match tag names instead of the default exact match or a couple of other comparison styles. The pages [`/tarball`][] and [`/zip`][] generate compressed archives of a specific checkin. They may be further restricted by query parameters that specify glob patterns that name files to include or exclude rather than taking the entire checkin. [`/timeline`]: /help?cmd=/timeline [`/tarball`]: /help?cmd=/tarball [`/zip`]: /help?cmd=/zip ## Platform Quirks Fossil glob patterns are based on the glob pattern feature of POSIX shells. Fossil glob patterns also have a quoting mechanism, discussed [above](#syntax). Because other parts of your operating system may interpret glob patterns and quotes separately from Fossil, it is often difficult to give glob patterns correctly to Fossil on the command line. Quotes and special characters in glob patterns are likely to be interpreted when given as part of a `fossil` command, causing unexpected behavior. These problems do not affect [versioned settings files](settings.wiki) or Admin → Settings in Fossil UI. Consequently, it is better to set long-term `*-glob` settings via these methods than to use `fossil settings` commands. That advice does not help you when you are giving one-off glob patterns in `fossil` commands. The remainder of this section gives remedies and workarounds for these problems. ### <a id="posix"></a>POSIX Systems If you are using Fossil on a system with a POSIX-compatible shell — Linux, macOS, the BSDs, Unix, Cygwin, WSL etc. — the shell may expand the glob patterns before passing the result to the `fossil` executable. Sometimes this is exactly what you want. Consider this command for example: $ fossil add RE* If you give that command in a directory containing `README.txt` and `RELEASE-NOTES.txt`, the shell will expand the command to: $ fossil add README.txt RELEASE-NOTES.txt …which is compatible with the `fossil add` command's argument list, which allows multiple files. Now consider what happens instead if you say: $ fossil add --ignore RE* src/*.c This *does not* do what you want because the shell will expand both `RE*` and `src/*.c`, causing one of the two files matching the `RE*` glob pattern to be ignored and the other to be added to the repository. You need to say this in that case: $ fossil add --ignore 'RE*' src/*.c The single quotes force a POSIX shell to pass the `RE*` glob pattern through to Fossil untouched, which will do its own glob pattern matching. There are other methods of quoting a glob pattern or escaping its special characters; see your shell's manual. Beware that Fossil's `--ignore` option does not override explicit file mentions: $ fossil add --ignore 'REALLY SECRET STUFF.txt' RE* You might think that would add everything beginning with `RE` *except* for `REALLY SECRET STUFF.txt`, but when a file is both given explicitly to Fossil and also matches an ignore rule, Fossil asks what you want to do with it in the default case; and it does not even ask if you gave the `-f` or `--force` option along with `--ignore`. The spaces in the ignored file name above bring us to another point: such file names must be quoted in Fossil glob patterns, lest Fossil interpret it as multiple glob patterns, but the shell interprets quotation marks itself. One way to fix both this and the previous problem is: $ fossil add --ignore "'REALLY SECRET STUFF.txt'" READ* The nested quotation marks cause the inner set to be passed through to Fossil, and the more specific glob pattern at the end — that is, `READ*` vs `RE*` — avoids a conflict between explicitly-listed files and `--ignore` rules in the `fossil add` command. Another solution would be to use shell escaping instead of nested quoting: $ fossil add --ignore "\"REALLY SECRET STUFF.txt\"" READ* It bears repeating that the two glob patterns here are not interpreted the same way when running this command from a *subdirectory* of the top checkout directory as when running it at the top of the checkout tree. If these files were in a subdirectory of the checkout tree called `doc` and that was your current working directory, the command would instead have to be: $ fossil add --ignore "'doc/REALLY SECRET STUFF.txt'" READ* The Fossil glob pattern still needs the `doc/` prefix because Fossil always interprets glob patterns from the base of the checkout directory, not from the current working directory as POSIX shells do. When in doubt, use `fossil status` after running commands like the above to make sure the right set of files were scheduled for insertion into the repository before checking the changes in. You never want to accidentally check something like a password, an API key, or the private half of a public cryptographic key into Fossil repository that can be read by people who should not have such secrets. ### <a id="windows"></a>Windows Before we get into Windows-specific details here, beware that this section does not apply to the several Microsoft Windows extensions that provide POSIX semantics to Windows, for which you want to use the advice in [the POSIX section above](#posix) instead: * the ancient and rarely-used [Microsoft POSIX subsystem][mps]; * its now-discontinued replacement feature, [Services for Unix][sfu]; or * their modern replacement, the [Windows Subsystem for Linux][wsl] [mps]: https://en.wikipedia.org/wiki/Microsoft_POSIX_subsystem [sfu]: https://en.wikipedia.org/wiki/Windows_Services_for_UNIX [wsl]: https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux (The latter is sometimes incorrectly called "Bash on Windows" or "Ubuntu on Windows," but the feature provides much more than just Bash or Ubuntu for Windows.) Neither standard Windows command shell — `cmd.exe` or PowerShell — expands glob patterns the way POSIX shells do. Windows command shells rely on the command itself to do the glob pattern expansion. The way this works depends on several factors: * the version of Windows you are using * which OS upgrades have been applied to it * the compiler that built your Fossil executable * whether you are running the command interactively * whether the command is built against a runtime system that does this at all * whether the Fossil command is being run from a file named `*.BAT` vs being named `*.CMD` Usually (but not always!) the C runtime library that your `fossil.exe` executable is built against does this glob expansion on Windows so the program proper does not have to. This may then interact with the way the Windows command shell you’re using handles argument quoting. Because of these differences, it is common to find perfectly valid Fossil command examples that were written and tested on a POSIX system which then fail when tried on Windows. The most common problem is figuring out how to get a glob pattern passed on the command line into `fossil.exe` without it being expanded by the C runtime library that your particular Fossil executable is linked to, which tries to act like [the POSIX systems described above](#posix). Windows is not strongly governed by POSIX, so it has not historically hewed closely to its strictures. For example, consider how you would set `crlf-glob` to `*` in order to get normal Windows text files with CR+LF line endings past Fossil's "looks like a binary file" check. The naïve approach will not work: C:\...> fossil setting crlf-glob * The C runtime library will expand that to the list of all files in the current directory, which will probably cause a Fossil error because Fossil expects either nothing or option flags after the setting's new value, not a list of file names. (To be fair, the same thing will happen on POSIX systems, only at the shell level, before `.../bin/fossil` even gets run by the shell.) Let's try again: C:\...> fossil setting crlf-glob '*' Quoting the argument like that will work reliably on POSIX, but it may or may not work on Windows. If your Windows command shell interprets the quotes, it means `fossil.exe` will see only the bare `*` so the C runtime library it is linked to will likely expand the list of files in the current directory before the `setting` command gets a chance to parse the command line arguments, causing the same failure as above. This alternative only works if you’re using a Windows command shell that passes the quotes through to the executable *and* you have linked Fossil to a C runtime library that interprets the quotes properly itself, resulting in a bare `*` getting clear down to Fossil’s `setting` command parser. An approach that *will* work reliably is: C:\...> echo * | fossil setting crlf-glob --args - This works because the built-in Windows command `echo` does not expand its arguments, and the `--args -` option makes Fossil read further command arguments from its standard input, which is connected to the output of `echo` by the pipe. (`-` is a common Unix convention meaning "standard input," which Fossil obeys.) A [batch script][fng.cmd] to automate this trick was posted on the now-inactive Fossil Mailing List. [fng.cmd]: https://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg25099.html (Ironically, this method will *not* work on POSIX systems because it is not up to the command to expand globs. The shell will expand the `*` in the `echo` command, so the list of file names will be passed to the `fossil` standard input, just as with the first example above!) Another (usually) correct approach which will work on both Windows and POSIX systems: C:\...> fossil setting crlf-glob *, This works because the trailing comma prevents the glob pattern from matching any files, unless you happen to have files named with a trailing comma in the current directory. If the pattern matches no files, it is passed into Fossil's `main()` function as-is by the C runtime system. Since Fossil uses commas to separate multiple glob patterns, this means "all files from the root of the Fossil checkout directory downward and nothing else," which is of course equivalent to "all managed files in this repository," our original goal. ## Experimenting To preview the effects of command line glob pattern expansion for various glob patterns (unquoted, quoted, comma-terminated), for any combination of command shell, OS, C run time, and Fossil version, precede the command you want to test with [`test-echo`][] like so: $ fossil test-echo setting crlf-glob "*" C:\> echo * | fossil test-echo setting crlf-glob --args - The [`test-glob`][] command is also handy to test if a string matches a glob pattern. [`test-echo`]: /help?cmd=test-echo [`test-glob`]: /help?cmd=test-glob ## Converting `.gitignore` to `ignore-glob` Many other version control systems handle the specific case of ignoring certain files differently from Fossil: they have you create individual "ignore" files in each folder, which specify things ignored in that folder and below. Usually some form of glob patterns are used in those files, but the details differ from Fossil. In many simple cases, you can just store a top level "ignore" file in `.fossil-settings/ignore-glob`. But as usual, there will be lots of edge cases. [Git has a rich collection of ignore files][gitignore] which accumulate rules that affect the current command. There are global files, per-user files, per workspace unmanaged files, and fully version controlled files. Some of the files used have no set name, but are called out in configuration files. [gitignore]: https://git-scm.com/docs/gitignore In contrast, Fossil has a global setting and a local setting, but the local setting overrides the global rather than extending it. Similarly, a Fossil command's `--ignore` option replaces the `ignore-glob` setting rather than extending it. With that in mind, translating a `.gitignore` file into `.fossil-settings/ignore-glob` may be possible in many cases. Here are some of features of `.gitignore` and comments on how they relate to Fossil: * "A blank line matches no files...": same in Fossil. * "A line starting with # serves as a comment...": same in Fossil, including the possibility of escaping an initial `#` with a backslash to allow globs beginning with a hash. * "Trailing spaces are ignored unless they are quoted..." is similar in Fossil. All whitespace before and after a glob is trimmed in Fossil unless quoted with single or double quotes. Git uses backslash quoting instead, which Fossil does not. * "An optional prefix "!" which negates the pattern...": not in Fossil. * Git's globs are relative to the location of the `.gitignore` file: Fossil's globs are relative to the root of the workspace. * Git's globs and Fossil's globs treat directory separators differently. Git includes a notation for zero or more directories that is not needed in Fossil. ### Example In a project with source and documentation: work +-- doc +-- src The file `doc/.gitignore` might contain: # Finished documents by pandoc via LaTeX *.pdf # Intermediate files *.tex *.toc *.log *.out *.tmp Entries in `.fossil-settings/ignore-glob` with similar effect, also limited to the `doc` folder: doc/*.pdf doc/*.tex, doc/*.toc, doc/*.log, doc/*.out, doc/*.tmp ## Implementation and References The implementation of the Fossil-specific glob pattern handling is here: :File |:Description -------------------------------------------------------------------------------- [`src/glob.c`][] | pattern list loading, parsing, and generic matching code [`src/file.c`][] | application of glob patterns to file names [`src/glob.c`]: https://fossil-scm.org/home/file/src/glob.c [`src/file.c`]: https://fossil-scm.org/home/file/src/file.c See the [Adding Features to Fossil][aff] document for broader details about finding and working with such code. The actual pattern matching leverages the `GLOB` operator in SQLite, so you may find [its documentation][gdoc], [source code][gsrc] and [test harness][gtst] helpful. [aff]: ./adding_code.wiki [gdoc]: https://sqlite.org/lang_expr.html#like [gsrc]: https://www.sqlite.org/src/artifact?name=9d52522cc8ae7f5c&ln=570-768 [gtst]: https://www.sqlite.org/src/artifact?name=66a2c9ac34f74f03&ln=586-673 |
Added www/glossary.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 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 | # Glossary There are several terms-of-art in Fossil that have specific meanings which are either not immediately obvious to an outsider or which have technical associations that can lead someone to either use the terms incorrectly or to get the wrong idea from someone using those terms correctly. We hope to teach users how to properly “speak Fossil†with this glossary. The bullet-point lists following each definition are meant to be clarifying and illustrative. They are not part of the definitions themselves. ## <a id="project"></a>Project A collection of one or more computer files that serve some conceptually unified purpose, which purpose changes and evolves over time, with the history of that project being a valuable record. * We qualify the Fossil definition of this common word like this to set aside cases where a zip file or tarball would suffice. If you can pack your project up into such an archive once and be done with it, Fossil is overkill. And yet that is often just the beginning, since there is often a need for something to be changed, so now you have “version 2†of the archive file. If you can foresee yourself creating versioned archive files for your project, then you probably should be using Fossil for it instead, then using Fossil’s [zip] or [tarball] command to automatically produce archives of the latest version rather than manually produce and track versions of the archive. The web version of these commands ([`/zip`][zw] and [`/tarball`][tw]) are particularly useful for public distribution of the latest version of a project’s files. * Fossil was designed to host the SQLite software project, which is comprised of source code, makefiles, scripts, documentation files, and so forth. Fossil is also useful for many other purposes, such as a fiction book project where each chapter is held in a separate file and assembled into a finished whole deliverable. * We speak of projects being more than one file because even though Fossil can be made to track the history of a single file, it is far more often the case that when you get to something of a scale sufficient to be called a “project,†there is more than one version-tracked file involved, if not at the start, then certainly by the end of the project. To take the example of a fiction book above, instead of putting each chapter in a separate file, you could use a single AsciiDoc file for the entire book project rather than make use of its [include facility][AIF] to assemble it from chapter files, since that does at least solve the [key problems][IFRS] inherent in version-tracking something like Word’s DOCX format with Fossil instead. While Fossil will happily track the single file containing the prose of your book project for you, you’re still likely to want separate files for the cover artwork, a style sheet for use in converting the source document to HTML, scripts to convert that intermediate output to PDF and ePub in a reliably repeatable fashion, a `README` file containing instructions to the printing house, and so forth. * Fossil requires that all the files for a project be collected into a single directory hierarchy, owned by a single user with full rights to modify those files. Fossil is not a good choice for managing a project that has files scattered hither and yon all over the file system, nor of collections of files with complicated ownership and access rights. A project made of an operating system installation’s configuration file set is not a good use of Fossil, because you’ll have all of your OS’s *other* files intermixed. Worse, Fossil doesn’t track OS permissions, so even if you were to try to use Fossil as a system deployment tool by archiving versions of the OS configuration files and then unpacking them on a new system, the extracted project files would have read/write access by the user who did the extraction, which probably isn’t want you were wanting. Even with these problems aside, do you really want a `.fslckout` SQLite database at the root of your filesystem? Are you prepared for the consequences of saying `fossil clean --verily` on such a system? You can constrain that with [the `ignore-glob` setting][IGS], but are you prepared to write and maintain all the rules needed to keep Fossil from blowing away the untracked portions of the file system? We believe Fossil is a poor choice for a whole-system configuration backup utility. As a counterexample, a project tracking your [Vim] configuration history is a much better use of Fossil, because it’s all held within `~/.vim`, and your user has full rights to that subdirectory. [AIF]: https://docs.asciidoctor.org/asciidoc/latest/directives/include/ [IGS]: /help?cmd=ignore-glob [IFRS]: ./image-format-vs-repo-size.md [tarball]: /help?cmd=tarball [tw]: /help?cmd=/tarball [Vim]: https://www.vim.org/ [zip]: /help?cmd=zip [zw]: /help?cmd=/zip ## Repository <a id="repository" name="repo"></a> A single file that contains all historical versions of all files in a project, which can be [cloned] to other machines and [synchronized][sync] with them. Jargon: repo. * A Fossil repo is similar to an archive file in that it is a single file that stores compressed versions of one or more files. Files can be extracted from the repo, and new files can be added to the repo, but a Fossil repo has other capabilities above and beyond what simple archive formats can do. * Fossil does not care what you name your repository files, though we do suggest “`.fossil`†as a standard extension. There is only one place in Fossil where that convention is required, being the [`fossil server DIRECTORY`][svrcmd] command, since it serves up `*.fossil` files from `DIRECTORY`. If you don’t use that feature, you can name your repo files anything you like. * Cloned and synced repos redundantly store all available information about that project, so if any one repo is lost, all of the cloned historical content of the project as of the last sync is preserved in each surviving repo. Each user generally clones the project repository down to each computer they use to participate in that project, and there is usually at least one central host for the project as well, so there is usually plenty of redundancy in any given Fossil-based project. That said, a Fossil repository clone is a backup only in a limited sense, because some information can’t be cloned, some doesn’t sync by default, and some data is neither clonable nor syncable. We cover these limitations and the workarounds for them in a separate document, [Backing Up a Remote Fossil Repository][backup]. * Rather than be a backup, a Fossil repository clone is a communication method for coordinating work on the project among multiple machines and users: local work done against one repository is communicated up to its parent repository on the next sync, and work done on other repositories that were previously synced up to that parent get pulled down into the local repo clone. This bidirectional sync is automatic and on by default in Fossil. You can [disable it][asdis], or you can [push] or [pull] unidirectionally, if you wish. The Fossil philosophy is that all project information resides in each clone of the repository. In the ideal world, this would occur instantly and automatically, but in actual use, Fossil falls somewhat short of that mark. Some limitations are simply technological: a given clone may be temporarily out of communication with its parent repository, network delays exist, and so forth. Fossil is [an AP mode system][CAP]. (This is sometimes called “eventual consistency.â€) Other cases come down to administrative necessity, as covered in [the backup doc][backup]. * Fossil doesn’t require that you have redundant clones. Whether you do or not is a local decision based on usage needs, communication requirements, desire for backups, and so forth. * Fossil doesn’t care where the repositories are stored, but we recommend keeping them all in a single subdirectory such as "`~/fossils`" or "`%USERPROFILE%\Fossils`". A flat set of files suffices for simple purposes, but you may have use for something more complicated. This author uses a scheme like the following on mobile machines that shuttle between home and the office: ``` pikchr toggle indent box "~/museum/" fit move right 0.1 line right dotted move right 0.05 box invis "where one stores valuable fossils" ljust arrow down 50% from first box.s then right 50% box "work/" fit move right 0.1 line dotted move right 0.05 box invis "projects from $DAYJOB" ljust arrow down 50% from 2nd vertex of previous arrow then right 50% box "home/" fit move right 0.1 line dotted right until even with previous line.end move right 0.05 box invis "personal at-home projects" ljust arrow down 50% from 2nd vertex of previous arrow then right 50% box "other/" fit move right 0.1 line dotted right until even with previous line.end move right 0.05 box invis "clones of Fossil itself, SQLite, etc." ljust ``` [asdis]: /help?cmd=autosync [backup]: ./backup.md [CAP]: ./cap-theorem.md [cloned]: /help?cmd=clone [pull]: /help?cmd=pull [push]: /help?cmd=push [svrcmd]: /help?cmd=server [sync]: /help?cmd=sync [repository]: #repo [repositories]: #repo ## Version / Revision / Hash / UUID <a id="version" name="hash"></a> These terms all mean the same thing: a long hexadecimal [SHA hash value](./hashes.md) that uniquely identifies a particular [check-in](#ci). We’ve listed the alternatives in decreasing preference order: * **Version** and **revision** are near-synonyms in common usage. Fossil’s code and documentation use both interchangeably because Fossil was created to manage the development of the SQLite project, which formerly used [CVS], the Concurrent Versions System. CVS in turn started out as a front-end to [RCS], the Revision Control System, but even though CVS uses “version†in its name, it numbers check-ins using a system derived from RCS’s scheme, which it calls “Revisions†in user-facing output. Fossil inherits this confusion honestly. * **Hash** refers to the [SHA1 or SHA3-256 hash](./hashes.md) of the content of the checked-in data, uniquely identifying that version of the managed files. It is a strictly correct synonym, used more often in low-level contexts than the term “version.†* **UUID** is a deprecated term still found in many parts of the Fossil internals and (decreasingly) its documentation. The problem with using this as a synonym for a Fossil-managed version of the managed files is that there are [standards][UUID] defining the format of a “UUID,†none of which Fossil follows, not even the [version 4][ruuid] (random) format, the type of UUID closest in meaning and usage to a Fossil hash.(^A pre-Fossil 2.0 style SHA1 hash is 160 bits, not the 128 bits many people expect for a proper UUID, and even if you truncate it to 128 bits to create a “good enough†version prefix, the 6 bits reserved in the UUID format for the variant code cannot make a correct declaration except by a random 1:64 chance. The SHA3-256 option allowed in Fossil 2.0 and higher doesn’t help with this confusion, making a Fossil version hash twice as large as a proper UUID. Alas, the term will never be fully obliterated from use since there are columns in the Fossil repository format that use the obsolete term; we cannot change this without breaking backwards compatibility.) You will find all of these synonyms used in the Fossil documentation. Some day we may settle on a single term, but it doesn’t seem likely. [CVS]: https://en.wikipedia.org/wiki/Concurrent_Versions_System [hash]: #version [RCS]: https://en.wikipedia.org/wiki/Revision_Control_System [ruuid]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random) [snfs]: https://en.wikipedia.org/wiki/Snapshot_(computer_storage)#File_systems [UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random) [version]: #version ## Check-in <a id="check-in" name="ci"></a> A [version] of the project’s files that have been committed to the [repository]; as such, it is sometimes called a “commit†instead. A check-in is a snapshot of the project at an instant in time, as seen from a single [check-out’s](#co) perspective. It is sometimes styled “`CHECKIN`â€, especially in command documentation where any [valid check-in name][ciname] can be used. * There is a harmless conflation of terms here: any of the various synonyms for [version] may be used where “check-in†is more accurate, and vice versa, because there is a 1:1 relation between them. A check-in *has* a version, but a version suffices to uniquely look up a particular commit.[^snapshot] * Combining both sets of synonyms results in a list of terms that is confusing to new Fossil users, but it’s easy enough to internalize the concepts. [Committing][commit] creates a *commit.* It may also be said to create a checked-in *version* of a particular *revision* of the project’s files, thus creating an immutable *snapshot* of the project’s state at the time of the commit. Fossil users find each of these different words for the same concept useful for expressive purposes among ourselves, but to Fossil proper, they all mean the same thing. * Check-ins are immutable. * Check-ins exist only inside the repository. Contrast a [check-out](#co). * Check-ins may have [one or more names][ciname], but only the [hash] is globally unique, across all time; we call it the check-in’s canonical name. The other names are either imprecise, contextual, or change their meaning over time and across [repositories]. [^snapshot]: You may sometimes see the term “snapshot†used as a synonym for a check-in or the version number identifying said check-in. We must warn against this usage because there is a potential confusion here: [the `stash` command][stash] uses the term “snapshot,†as does [the `undo` system][undo] to make a distinction with check-ins. Nevertheless, there is a conceptual overlap here between Fossil and systems that do use the term “snapshot,†the primary distinction being that Fossil will capture only changes to files you’ve [added][add] to the [repository], not to everything in [the check-out directory](#co) at the time of the snapshot. (Thus [the `extras` command][extras].) Contrast a snapshot taken by a virtual machine system or a [snapshotting file system][snfs], which captures changes to everything on the managed storage volume. [add]: /help?cmd=add [ciname]: ./checkin_names.wiki [extras]: /help?cmd=extras [stash]: /help?cmd=stash [undo]: /help?cmd=undo ## Check-out <a id="check-out" name="co"></a> A set of files extracted from a [repository] that represent a particular [check-in](#ci) of the [project](#project). * Unlike a check-in, a check-out is mutable. It may start out as a version of a particular check-in extracted from the repository, but the user is then free to make modifications to the checked-out files. Once those changes are formally [committed][commit], they become a new immutable check-in, derived from its parent check-in. * You can switch from one check-in to another within a check-out directory by passing those names to [the `fossil update` command][update]. * Check-outs relate to repositories in a one-to-many fashion: it is common to have a single repo clone on a machine but to have it [open] in [multiple working directories][mwd]. Check-out directories are associated with the repos they were created from by settings stored in the check-out directory. This is in the `.fslckout` file on POSIX type systems, but for historical compatibility reasons, it’s called `_FOSSIL_` by native Windows builds of Fossil. (Contrast the Cygwin and WSL Fossil binaries, which use POSIX file naming rules.) * In the same way that one cannot extract files from a zip archive without having a copy of that zip file, one cannot make check-outs without access to the repository file or a clone thereof. * Because a Fossil repository is a SQLite database file, the same rules for avoiding data corruption apply to it. In particular, it is [nearly a hard requirement][h2cflp] that the repository clone be on the same machine as the one where you make check-outs and the subsequent check-ins. That said, the relative locations of the repo and the check-out within the local file system are arbitrary. The repository may be located inside the folder holding the check-out, but it certainly does not have to be, and it usually is not. As an example, [the Fossil plugin for Visual Studio Code][fpvsc] defaults to storing the repo clone within the project directory as a file called `.fsl`, but this is because VSCode’s version control features assume it’s being used with Git, where the repository is the `.git` subdirectory contents. With Fossil, [different check-out workflows][cwork] are preferred. [commit]: /help?cmd=commit [cwork]: ./ckout-workflows.md [h2cflp]: https://www.sqlite.org/howtocorrupt.html#_file_locking_problems [fpvsc]: https://marketplace.visualstudio.com/items?itemName=koog1000.fossil [open]: /help?cmd=open [mwd]: ./ckout-workflows.md#mcw [update]: /help?cmd=update ## <a id="docs"></a>Embedded Documentation Serving as an alternative to Fossil’s built-in [wiki], the [embedded documentation feature][edoc] stores the same type of marked-up text files, but under Fossil’s powerful version control features. * The simple rule for determining whether to use the wiki or embedded docs for any given document is whether the content is considered “evergreen,†as with a Wikipedia article. While Fossil’s wiki feature does store the history of each document’s changes, Fossil always presents the current version of the document unless you manually go out of your way to dig back into the history. Then, having done so, links from that historical version of the wiki document take you to the current versions of the target documents, not to the version contemporaneous with the source document. The consequence is that if you say something like… $ fossil up 2020-04-01 $ fossil ui --page wcontent …you will **not** see the list of wiki articles as of April Fool’s Day in 2020, but instead the list of *current* wiki article versions, the same as if you ran it from a check-out of the tip-of-trunk. Contrast embedded docs, which are not only version-controlled as normal files are in Fossil, they participate in all the tagging, branching, and other versioning features. There are several consequences of this, such as that Fossil’s [special check-in names][ciname] work with embedded doc URLs: * <p>If you visit an embedded doc as `/doc/release/file.md` and then click on a relative link from that document, you will remain on the release branch. This lets you see not only the release version of a software project but also the documentation as of that release.</p> * <p>If you visit `/doc/2020-04-01/file.md`, you will not only pull up the version of `file.md` as of that date, relative links will take you to contemporaneous versions of those embedded docs as well.</p> * <p>If you say `fossil up 2020-04-01 && fossil ui` and then visit `/doc/ckout/file.md`, you’ll not only see the checked-out version of the file as of that date, relative links will show you other files within that checkout.</p> * Fossil’s wiki presents a flat list of articles, while embedded docs are stored in the repository’s file hierarchy, a powerful organizational tool well-suited to complicated documentation. * Your repository’s Home page is a good candidate for the wiki, as is documentation meant for use only with the current version of the repository’s contents. * If you are at all uncertain whether to use the wiki or the embedded documentation feature, prefer the latter, since it is inherently more powerful, and when you use the [`/fileedit` feature][fef], the workflow is scarcely different from using the wiki. (This very file is embedded documentation: clone [Fossil’s self-hosting repository][fshr] and you will find it as `www/glossary.md`.) [edoc]: ./embeddeddoc.wiki [fef]: ./fileedit-page.md [fshr]: ./selfhost.wiki [wiki]: ./wikitheory.wiki ## <a id="cap"></a>Capability Fossil includes a powerful [role-based access control system][rbac] which affects which users have permission to do certain things within a given [repository]. You can read more about this complex topic [here](./caps/). Some people — and indeed certain parts of Fossil’s own code — use the term “permissions†instead, but since [operating system file permissions also play into this](./caps/#webonly), we prefer the term “capabilities†(or “caps†for short) when talking about Fossil’s RBAC system to avoid a confusion here. [rbac]: https://en.wikipedia.org/wiki/Role-based_access_control <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
Added www/grep.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 | # Fossil grep vs POSIX grep As of Fossil 2.7, there is a `grep` command which acts roughly like POSIX's `grep -E` over all historical versions of one or more managed files. This document explains the commonalities and divergences between [POSIX `grep`](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html) and Fossil `grep`. ## Options Fossil `grep` implements about half of the options specified for POSIX `grep`: | Option | Meaning |--------|------------------------------------------------------------- | `-c` | report the count of matches rather than the matched text | `-i` | ignore case in matches | `-l` | list a checkin ID prefix for matching historical versions of the file | `-q` | no output; return only a status code indicating the success of the match | `-s` | suppress error output about missing files | `-v` | invert the sense of the match That leaves several divergences at the option level from POSIX `grep`: * You cannot give more than one pattern, as with `grep -e` or `grep -f`. * There is no equivalent of `grep -F` to do literal fixed-string matches only. * There is no `-x` option for doing a whole-line match. * `fossil grep -l` does not do precisely the same thing as POSIX `grep -l`: it lists checkin ID prefixes, not file names. * Fossil always gives the line number in its output, which is to say that it acts like `grep -n`. There is no way to disable the line number in `fossil grep` output. Patches to remove those limitations will be thoughtfully considered. Fossil `grep` doesn’t support any of the GNU and BSD `grep` extensions. For instance, it doesn’t support the common `-R` extension to POSIX, which would presumably search a subtree of managed files. If Fossil does one day get this feature, it would have a different option letter, since `-R` in Fossil has a different meaning, by convention. Until then, you can get the same effect on systems with a POSIX shell like so: $ fossil grep COMMAND: $(fossil ls src) If you run that in a check-out of the [Fossil self-hosting source repository][fshsr], that returns the first line of the built-in documentation for each Fossil command, across all historical verisons. Fossil `grep` has extensions relative to these other `grep` standards, such as `--verbose` to print each checkin ID considered, regardless of whether it matches. This one is noteworthy here because the behavior used to be under `-v` before we reassigned it to give POSIX-like `grep -v` behavior. [fshsr]: ./selfhost.wiki ## Regular Expression Dialect Fossil contains a built-in regular expression engine implementing a subset of the [POSIX extended regular expression][ere] dialect: [ere]: https://en.wikipedia.org/wiki/Regular_expression#POSIX_extended | Atom | Meaning |---------|------------------------------------------------------------- | `X*` | zero or more occurrences of X | `X+` | one or more occurrences of X | `X?` | zero or one occurrences of X | `X{p,q}`| between p and q occurrences of X, inclusive | `(X)` | match X | <tt>X\|Y</tt>| X or Y | `^X` | X occurring at the beginning of a line | `X$` | X occurring at the end of a line | `.` | Match any single character | `\c` | Character `c` where `c` is one of <tt>{}()[]\|\*+?.\\</tt> | `\c` | C-language escapes for `c` in `afnrtv`. ex: `\t` or `\n` | `\uXXXX`| Where XXXX is exactly 4 hex digits, Unicode value XXXX | `\xXX` | Where XX is exactly 2 hex digits, Unicode value XX | `[abc]` | Any single character from the set `abc` | `[^abc]`| Any single character not in the set `abc` | `[a-z]` | Any single character in the range `a-z` | `[^a-z]`| Any single character not in the range `a-z` | `\b` | Word boundary | `\w` | Word character: `[A-Za-z0-9_]` | `\W` | Non-word character: `[^A-Za-z0-9_]` | `\d` | Digit: `[0-9]` | `\D` | Non-digit: `[^0-9]` | `\s` | Whitespace character: `[ \t\r\n\v\f]` | `\S` | Non-whitespace character: `[^ \t\r\n\v\f]` There are several restrictions in Fossil `grep` relative to a fully POSIX compatible regular expression engine. Among them are: * There is currently no support for POSIX character classes such as `[:lower:]`. * Fossil `grep` does not currently attempt to take your operating system's locale settings into account when doing this match. Since Fossil has no way to mark a given file as having a particular encoding, Fossil `grep` assumes the input files are in UTF-8 format. This means Fossil `grep` will not work correctly if the files in your repository are in an encoding that is not backwards-compatible with ASCII, such as UTF-16. Partially compatible encodings such as ISO 8859 should work with Fossil `grep` as long as you stick to their ASCII-compatible subset. The Fossil `grep` language is not a strict subset of POSIX extended regular expressions. Some of the features documented above are well-understood extensions to it, such as the "word" features `\b`, `\w` and `\W`. Fossil `grep` is based on the Unicode engine from [SQLite's FTS5 feature][fts5]. This means it does do things like Unicode-aware case folding. Therefore, it is usually a user error to attempt to substitute `[a-z]` for a lack of the POSIX character class `[:lower:]` if you are grepping over pretty much any human written language other than English. Use `fossil grep -i` instead, which does Unicode case folding. [fts5]: https://www.sqlite.org/fts5.html ## Algorithm Details Fossil `grep` uses a [nondeterministic finite automaton][nfa] for matching, so the performance is bounded by ***O(nm)*** where ***n*** is the size of the regular expression and ***m*** is the size of the input string. [nfa]: https://en.wikipedia.org/wiki/Nondeterministic_finite_automaton In order to avoid [ReDoS attacks][redos], the Fossil regular expression matcher was purposely written to avoid [implementation choices][rei] which have the potential to require exponential evaluation time. This constrains the possible feature set we can support in the Fossil `grep` dialect. For instance, we are unlikely to ever add support for [backtracking][bt]. [redos]: https://en.wikipedia.org/wiki/ReDoS [rei]: https://en.wikipedia.org/wiki/Regular_expression#Implementations_and_running_times [bt]: https://en.wikipedia.org/wiki/Backtracking The `X{p,q}` operator expands to `p` copies of `X` followed by `q-p` copies of `X?` before RE evaluation. The ***O(nm)*** performance bound above remains true for this case, but realize that it applies to the RE *after* this expansion, not to the form as given by the user. In other words, as `q-p` increases, so does the RE evaluation time. |
Added www/gsoc-ideas.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 | # List of Projects and Tasks This list was made for the Fossil project's application for [Google Summer of Code](https://summerofcode.withgoogle.com/) in 2021. That application was unsuccessful, but still this list is a starting point for anyone looking for a place to start. We welcome newcomers, and invite developers to follow the simple [procedures for contributing to Fossil](./contribute.wiki). The [hacker how-to](./hacker-howto.wiki) is recommended reading. There are two implementations of the Fossil data model: * [the classic Fossil project](https://fossil-scm.org) , which is where this file is maintained and which is as of 2021 how everyone interacts with Fossil objects * [libfossil](https://fossil.wanderinghorse.net/r/libfossil), which is an independent project to manipulate Fossil objects from a library, or using commandline tools which are thin wrappers to the library As of 2021 the two implementations have an identical implementation of the Fossil data model, are 100% compatible in terms of data access since they use the same SQL, and are 100% binary compatible in terms of on-disk storage. The projects listed here are grouped by functionality - User Interface, Integration, Email, etc. If you are looking for something easy to start with, then depending where your interests lie, there are some small libfossil tasks and small features to work on in the UI. # UI, Look and Feel Tasks for those interested in graphic/web design: * Add a quote button to the Forum, such as [discussed in this thread](https://fossil-scm.org/forum/forumpost/7ad03cd73d) * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history) * Allow diffing of Forum posts * General touch-ups in the existing skins. This may, depending on how deep one cares to dig, require digging into C code to find, and potentially modify, how the HTML is generated. * Creation of one or more new skins. This does not specifically require any C know-how. * Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator # Projects Relating to Fossil Integration * Fossil hooks for pipelines with CI/CD such as static analysis, Buildbot, Gerrit, Travis and Jenkins are not well-documented and may need some further development. Make this work better, with configuration examples * Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown * Create a [Pandoc filter that handles Pikchr](https://groups.google.com/g/pandoc-discuss/c/zZSspnHHsg0?pli=1) (Pikchr can be used with many kinds of layout, not just Markdown) * Editor integration: [improve the Fossil VSCode plugin](https://marketplace.visualstudio.com/items?itemName=koog1000.fossil) or [create a Fossil plugin for Eclipse](https://marketplace.eclipse.org/taxonomy/term/26%2C31) * Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil * Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis # Adding Inbound (Receiving) Email to Fossil This task involves designing a new feature and working with Fossil developers to see how it can be feasible in practice. Fossil can [send email alerts](./alerts.md), but cannot receive email at all. That is a good thing, because a complete [SMTP MTA](https://en.wikipedia.org/wiki/MTA) is complicated and requires constant maintenance, so Fossil should not try to be an MTA or ever listen to mail ports on the Internet. There is one specific type of email reception that make sense for Fossil to handle. When there is inbound mail related to a message that Fossil has previously generated with a unique hash, Fossil already knows the context of that message. An unknown sender cannot guess a valid hash although a malicious sender could of course find a way to receive a valid hash and then use that to gain access. The risk of automatic and non-specific spam is very low. A proposal to handle that would be to implement a Fossil command like this: ``` fossil email -R repo receive -t TYPE-OF-EMAIL -h HASH ``` Where the type of email would be one of a list something like this: * mail_bounce * ticket_reply * forum_reply This command is a non-network-aware [Mail Delivery Agent](https://en.wikipedia.org/wiki/Mail_delivery_agent), and would be called by an SMTP MTA such as Postfix, Courier or Exim. The MTA would need to be configured to recognise that this is an email intended for Fossil, and what type of email, and to extract its hash. People who configure MTAs are used to doing this sort of thing, but no doubt Fossil would include a sample [Postfix mail filter](http://www.postfix.org/FILTER_README.html#simple_filter) and an equivalent driver for Exim. The Fossil command would reject anything that doesn't look like a bounce it is expecting. It is not certain that this design is the best one to address the inbound mail problem. That is why the first part of this task is to find a workable design. # Work relating to the ticketing system in Fossil The Fossil SCM project uses tickets in a [somewhat unusual manner](https://fossil-scm.org/home/reportlist) because the social programming model has evolved to often use the Forum instead of ticketing. Other Fossil-using projects use tickets in a more traditional report-a-bug manner. So this means that the Fossil ticketing system user interface is underdeveloped. On the other hand, pretty much every software developer uses a ticketing system at some point in their workflow, and Fossil is intended to be usable by most developers. That means the ticketing system really needs to be further developed. The underlying technology for the Fossil ticketing system is guaranteed, so to improve it requires only user interface changes. Projects relating to the ticketing system include: * Improving the [Fossil cli for tickets](https://fossil-scm.org/forum/forumpost/d8e8a1cf92) which is confusing, as pointed out in that ticket. This is still classified as a "user interface" even though it isn't graphical. * Alternatively, instead of improving Fossil's cli, implement a comprehensive ticket commandline with [libfossil's primitives](https://fossil.wanderinghorse.net/r/libfossil/wiki/home), look under the f-apps/ directory. * Improving the Fossil web UI for ticketing, which is clunky to say the least. Fossil tries not be a heavy user of Javascript and Javascript libraries, but the wikiedit, chat and Forum code are all more advanced than ticketing, and have UI features that would improve ticketing * If there is an inbound email system as per the previous section "Adding Inbound (Receiving) Email to Fossil", then implement this system for ticketing # Tasks Requiring Fossil Data Model Knowledge The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model is designed to [endure for centuries](./fileformat.wiki), be [easily accessible](./fossil-v-git.wiki#durable), and is [non-relational](./fossil-is-not-relational.md). You will need to understand the data model to work on the following tasks: * Add the ability to tag non-checkin artifacts, something supported by the data model but not the current CLI and UIs. This would open the door to numerous new features, such as "sticky" forum posts and per-file extended attributes. This could also relate to the RBAC system. * Implement "merge" and "stash" in libfossil * Analyse the different kinds of [split/export/shallow clone](https://fossil-scm.org/forum/forumpost/1aa4f8ea8c6f96) use cases for Fossil including [complete bifurcation](https://fossil-scm.org/forum/forumpost/6434a06871). There are many proposals, relating to many different use cases, and a good analysis would help us to work out what should be implemented, and what should be implemented in Fossil and what is instead a libfossil wrapper # Fossil is cool There are many reasons why Fossil is just plain cool: * Fossil is symbiotically connected with [SQL and SQLite](5631123d66d96) * Fossil is highly portable accross different operating systems * Fossil is the [only credible alternative to Git](./fossil-v-git.wiki) * Fossil is both ultra-long-term stable and has a high rate of development and new features * Fossil has thought deeply about Comp Sci principles including [CAP Theorem](./cap-theorem.md) and [whether Fossil is a blockchain](./blockchain.md) * Fossil has two independent implementations of the same data model: Fossil and libfossil and a lot, lot more, in the source, docs, forum and more. ``` pikchr center toggle // Click to see the rendered diagram this describes, // written in Fossil's built-in pikchr language, see https://pikchr.org // // based on pikchr script by Kees Nuyt, licensed // https://creativecommons.org/licenses/by-nc-sa/4.0/ scale = 1.0 eh = 0.5cm ew = 0.2cm ed = 2 * eh er = 0.4cm lws = 4.0cm lwm = lws + er lwl = lwm + er ellipse height eh width ew fill Bisque color CadetBlue L1: line width lwl from last ellipse.n line "click for" bold above width lwm from last ellipse.s LV: line height eh down move right er down ed from last ellipse.n ellipse height eh width ew fill Bisque color CadetBlue L3: line "example of Fossil" bold width lws right from last ellipse.n to LV.end then down eh right ew line width lwm right from last ellipse.s then to LV.start move right er down ed from last ellipse.n ellipse height eh width ew fill Bisque color CadetBlue line width lwl right from last ellipse.n then to L1.end line "coolness" bold width lwl right from last ellipse.s then up eh ``` |
Added www/hacker-howto.wiki.
> > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <title>Fossil Developer How-To</title> The following links are of interest to programmers who want to modify or enhance Fossil itself. Ordinary users can safely ignore this information. * [./build.wiki | How To Compile And Install Fossil] * [./customskin.md | Theming Fossil] * [./adding_code.wiki#newcmd | Adding New Commands To Fossil] * [./makefile.wiki | The Fossil Build Process] * [./tech_overview.wiki | A Technical Overview of Fossil] * [./contribute.wiki|Contributing Code Or Enhancements To The Fossil Project] * [./fileformat.wiki|Fossil Artifact File Format] * [./sync.wiki|The Sync Protocol] * [./style.wiki | Coding Style Guidelines] * [./checkin.wiki | Pre-checkin Checklist] * [../test/release-checklist.wiki | Release Checklist] * [./backoffice.md | The "backoffice" subsystem] |
Added www/hashes.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 | # Hashes: Fossil Artifact Identification All artifacts in Fossil are identified by a unique hash, currently using [the SHA3 algorithm by default][hpol], but historically using the SHA1 algorithm: | Algorithm | Raw Bits | Hexadecimal digits | |-----------|----------|--------------------| | SHA3-256 | 256 | 64 | | SHA1 | 160 | 40 | There are many types of artifacts in Fossil: commits (a.k.a. check-ins), tickets, ticket comments, wiki articles, forum postings, file data belonging to check-ins, etc. ([More info...](./concepts.wiki#artifacts)). There is a loose hierarchy of terms used instead of “hash†in various parts of the Fossil UI, which we cover in the sections below. ## Names Several Fossil interfaces accept [a wide variety of check-in names][cin]: commit artifact hashes, ISO8601 date strings, branch names, etc. Fossil interfaces that accept any of these options usually document the parameter as “NAMEâ€, so we will use that form to refer to this specialized use. Artifact hashes are only one of many different types of NAME. We use the broad term “NAME†to refer to the whole class of options. We use more specific terms when we mean one particular type of NAME. ## Versions When an artifact hash refers to a specific commit, Fossil sometimes calls it a “VERSION,†a “commit ID,†or a “check-in ID.†We may eventually settle on one of these terms, but all three are currently in common use within Fossil’s docs, UI, and programming interfaces. A VERSION is a specific type of artifact hash, distinct from, let us say, a wiki article artifact hash. A unique prefix of a VERSION hash is itself a VERSION. That is, if your repository has exactly one commit artifact with a hash prefix of “abc123â€, then that is a valid version string as long as it remains unambiguous. ## <a id="uvh"></a>UUIDs Fossil uses the term “UUID†as a short alias for “artifact hash†in its internals. There are a few places where this leaks out into external interfaces, which we cover in the sections below. Going forward, we prefer one of the terms above in public interfaces instead. Whether this short alias is correct is debateable. One argument is that since "UUID" is an acronym for “Universally Unique Identifier,†and both SHA1 and SHA3-256 are larger and stronger than the 128-bit algorithms used by “proper†UUIDs, Fossil artifact hashes are *more universally unique*. It is therefore quibbling to say that Fossil UUIDs are not actually UUIDs. One wag suggested that Fossil artifact hashes be called MUIDs: multiversally unique IDs. The common counterargument is that the acronym “UUID†was created for [a particular type of universally-unique ID][uuid], with particular ASCII and bitfield formats, and with particular meaning given to certain of its bits. In that sense, no Fossil “UUID†can be used as a proper UUID. Be warned: attempting to advance the second position on the Fossil discussion forum will get you nowhere at this late date. We’ve had the debates, we’ve done the engineering, and we’ve made our evaluation. It’s a settled matter: internally within Fossil, “UUID†is defined as in this section’s leading paragraph. To those who remain unconvinced, “fixing†this would require touching almost every source code file in Fossil in a total of about a thousand separate locations. (Not exaggeration, actual data.) This would be a massive undertaking simply to deal with a small matter of terminology, with a high risk of creating bugs and downstream incompatibilities. Therefore, we are highly unlikely to change this ourselves, and we are also unlikely to accept a patch that attempts to fix it. ### Repository DB Schema The primary place where you find "UUID" in Fossil is in the `blob.uuid` table column, in code dealing with that column, and in code manipulating *other* data that *refers* to that column. This is a key lookup column in the most important Fossil DB table, so it influences broad swaths of the Fossil internals. For example, C code that refers to SQL result data on `blob.uuid` usually calls the variable `zUuid`. That value may then be inserted into a table like `ticket.tkt_uuid`, creating a reference back to `blob.uuid`, and then be passed to a function like `uuid_to_rid()`. There is no point renaming a single one of these in isolation: it would create needless terminology conflicts, making the code hard to read and understand, risking the creation of new bugs. You may have local SQL code that digs into the repository DB using these column names. While you may rest easy, assured now that we are highly unlikely to ever rename these columns, the Fossil repository DB schema is not considered an external user interface, and internal interfaces are subject to change at any time. We suggest switching to a more stable API: [the JSON API][japi], [`timeline.rss`][trss], [TH1][th1], etc. ### TH1 Scripting Interfaces Some [TH1][th1] interfaces expose Fossil internals flowing from `blob.uuid`, so “UUID†is a short alias for “artifact hash†in TH1. For example, the `$tkt_uuid` variable — available when [customizing the ticket system][ctkt] — is a ticket artifact hash, exposing the `ticket.tkt_uuid` column, which has a SQL relation to `blob.uuid`. TH1 is a longstanding public programming interface. We cannot rename its interfaces without breaking existing TH1 Fossil customizations. We are also unlikely to provide a parallel set of variables with “better†names, since that would create a mismatch with respect to the internals they expose, creating a different sort of developer confusion in its place. ### JSON API Parameters and Outputs [The JSON API][japi] frequently uses the term “UUID†in the same sort of way, most commonly in [artifact][jart] and [timeline][jtim] APIs. As with TH1, we can’t change this without breaking code that uses the JSON API as originally designed, so we take the same stance. ### `manifest.uuid` If you have [the `manifest` setting][mset] enabled, Fossil writes a file called `manifest.uuid` at the root of the check-out tree containing the commit hash for the current checked-out version. Because this is a public interface that existing code depends on, we are unwilling to rename the file. [cin]: ./checkin_names.wiki [ctkt]: ./custom_ticket.wiki [hpol]: ./hashpolicy.wiki [japi]: ./json-api/ [jart]: ./json-api/api-artifact.md [jtim]: ./json-api/api-timeline.md [mset]: /help?cmd=manifest [th1]: ./th1.md [trss]: /help?cmd=/timeline.rss [tvb]: ./branching.wiki [uuid]: https://en.wikipedia.org/wiki/Universally_unique_identifier |
Added www/hashpolicy.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 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 | <title>Hash Policy</title> <h2>Executive Summary</h2> <b>Or: How To Avoid Reading This Article</b> There was much angst over the [http://www.shattered.io|SHAttered attack] against SHA1 when it was announced in early 2017. If you are concerned about this and its implications for Fossil, simply [./quickstart.wiki#install|upgrade to Fossil 2.1 or later], and the problem will go away. Everything will continue to work as before. * Legacy repositories will continue working just as they always have, without any conversions or upgrades. * Historical check-ins will keep their same historical SHA1 names. * New check-ins will get more secure SHA3-256 hash names. * Everything will continue as if nothing happened. * Your workflow will be unchanged. But if you are curious and want a deeper understanding of what is going on, read on... <h2>Introduction</h2> The first snapshot-based distributed version control system was [http://www.monotone.ca|Monotone]. Many of the ideas behind the design of Fossil were copied from Monotone, including the use of a SHA1 hash to assign names to artifacts. Git and Mercurial did the same thing. The SHA1 hash algorithm is used only to create names for artifacts in Fossil (and in Git, Mercurial, and Monotone). It is not used for security. Nevertheless, when the [http://www.shattered.io|SHAttered attack] found two different PDF files with the same SHA1 hash, many users learned that "SHA1 is broken". They see that Fossil (and Git, Mercurial, and Monotone) use SHA1 and they therefore conclude that "Fossil is broken". This is not true, but it is a public relations problem. So the decision was made to migrate Fossil away from SHA1. This article describes how that migration is occurring. <h2>Use Of Hardened SHA1</h2> In Fossil version 2.0 ([/timeline?c=version-2.0|2017-03-03]), the internal SHA1 implementation was changed from a generic FIPS PUB 180-4 SHA1 implementation to a "Hardened SHA1" [[https://github.com/cr-marcstevens/sha1collisiondetection|1]] [[https://marc-stevens.nl/research/papers/C13-S.pdf|2]]. The Hardened SHA1 algorithm automatically detects when the artifact being hashed is specifically designed to exploit the known weaknesses in the SHA1 algorithm, and when it detects such an attack it changes the hash algorithm (by increasing the number of rounds in the compression function) to make the algorithm secure again. If the attack detection gets a false-positive, that means that Hardened SHA1 will get a different answer than the standard FIPS PUB 180-4 SHA1, but the creators of Hardened SHA1 (see the second paper [[https://marc-stevens.nl/research/papers/C13-S.pdf|2]]) report that the probability of a false-positive is vanishingly small - less than 1 false positive out of 10<sup><font size=1>27</font></sup> hashes. Hardened SHA1 is slower (and a lot bigger) but Fossil does not do that much hashing, so performance is not really an issue. All versions of Fossil moving forward will use Hardened SHA1. So if someone says "SHA1 is broken, and Fossil uses SHA1, therefore Fossil is broken," you can rebut the argument by pointing out that Fossil uses <em>Hardened SHA1</em>, not generic SHA1, and Hardened SHA1 is <em>not</em> broken. <h2>Support For SHA3-256</h2> Prior to Fossil version 2.0 ([/timeline?c=version-2.0|2017-03-03]), all artifacts in all Fossil repositories were named by only a SHA1 hash. Version 2.0 extended the [./fileformat.wiki|Fossil file format] to allow artifacts to be named by either SHA1 or SHA3-256 hashes. (SHA3-256 is the only variant of SHA3 that Fossil uses for artifact naming, so for the remainder of this article it will be called simply "SHA3". Similarly, "Hardened SHA1" will shortened to "SHA1" in the remaining text.) To be clear: Fossil (version 2.0 and later) allows the SHA1 and SHA3 hashes to be mixed within the same repository. Older check-ins, created years ago, continue to be named using their legacy SHA1 hashes while newer check-ins are named using modern SHA3 hashes. There is no need to "convert" a repository from SHA1 over to SHA3. You can see this in Fossil itself. The recent [9d9ef82234f63758] check-in uses a SHA3 hash whereas the older [1669115ab9d05c18] check-in uses a SHA1 hash. Other than permitting the use of SHA3 in addition to SHA1, there were no file format changes in Fossil version 2.0 relative to the previous version 1.37. Both Fossil 2.0 and Fossil 1.37 read and write all the same repositories and sync with one another, as long as none of the repositories contain artifacts named using SHA3. If a repository does contain artifacts named using SHA3, Fossil 1.37 will not know how to interpret those artifacts and will generate various warnings and errors. <h2>How Fossil Decides Which Hash Algorithm To Use For New Artifacts</h2> If newer versions of Fossil are able to use either SHA1 or SHA3 to name artifacts, which hash algorithm is actually used? That question is answered by the "hash policy". These are the supported hash policies: <table cellpadding=10> <tr> <td valign='top'>sha1</td> <td>Name all new artifacts using the (Hardened) SHA1 hash algorithm.</td> </tr> <tr> <td valign='top'>auto</td> <td>Name new artifacts using the SHA1 hash algorithm, but if any artifacts are encountered which are already named using SHA3, then automatically switch the hash policy to "sha3"</td> </tr> <tr> <td valign='top'>sha3</td> <td>Name new artifacts using the SHA3 hash algorithm if the artifact does not already have a SHA1 name. If the artifact already has a SHA1 name, then continue to use the older SHA1 name. Use SHA3 for new artifacts that have never before been encountered.</td> </tr> <tr> <td valign='top'>sha3-only</td> <td>Name new artifacts using the SHA3 hash algorithm even if the artifact already has a SHA1 name. In other words, force the use of SHA3. This can cause some artifacts to be added to the repository twice, once under their SHA1 name and again under their SHA3 name, but delta compression will prevent that from causing repository size problems.</td> </tr> <tr> <td valign='top'>shun-sha1</td> <td>Like "sha3-only" but at this level do not accept a push of SHA1-named artifacts. If another Fossil instance tries to push a SHA1-named artifact, that artifact is discarded and ignored. </tr> </table> For Fossil 2.0, and obviously also for Fossil 1.37 and before, the only hash policy supported was the one now called "sha1", meaning that all new artifacts were named using a SHA1 hash. Even though Fossil 2.0 added the capability of understanding SHA3 hashes, it never actually generates any SHA3 hashes. From Fossil 2.1 through 2.9, the default hash policy for legacy repositories changed to "auto", meaning that Fossil continued to generate only SHA1 hashes until it encountered one artifact with a SHA3 hash. Once those older versions of Fossil saw a single SHA3 hash, they automatically switched to "sha3" mode and thereafter generated only SHA3 hashes. When a new repository is created by cloning, the hash policy is copied from the parent. For new repositories created using the [/help?cmd=new|fossil new] command the default hash policy is "sha3". That means new repositories will normally hold nothing except SHA3 hashes. The hash policy for new repositories can be overridden using the "--sha1" option to the "fossil new" command. If you are still on Fossil 2.1 through 2.9 but you want Fossil to go ahead and start using SHA3 hashes, change the hash policy to "sha3" using a command like this: <verbatim> fossil hash-policy sha3 </verbatim> The next check-in will use a SHA3 hash, so that when that check-in is pushed to colleagues, their clones will include the new SHA3-named artifact, so their local Fossil instances will automatically convert their clones to "sha3" mode as well. Of course, if some members of your team stubbornly refuse to upgrade past Fossil 1.37, you should avoid changing the hash policy and creating artifacts with SHA3 names, because once you do that your recalcitrant coworkers will no longer be able to collaborate. <h2>A Pure SHA3 Future</h2> Fossil 2.10 changed the default hash policy to "sha3" mode even for legacy repositories, so if you upgrade to the latest version of Fossil, all of your new artifacts will use a SHA3 hash. Legacy SHA1 artifacts continue to use their original names, but new artifacts will use SHA3 names. You might not even notice this automatic change over to stronger hashes. We decided to make the change to pure SHA3 since the last known distributor of Fossil 1.x binaries — Debian 9 — was finally replaced in June 2019 by Debian 10, which included Fossil 2.8. All other known sources of Fossil 1.x binaries upgraded well before that point. |
Added www/hints.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 | <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 -x]" command is a great alternative to "make clean". You can use "[/help/clean | fossil clean -f]" as a slightly safer alternative if the "ignore-glob" setting is not set. WARNING: make sure you did a "fossil add" for all source-files you plan to commit, otherwise those files will be deleted without warning. 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 [/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=xdiff | fossil xdiff --tk $file1 $file2]" to get a Tk 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 "xdiff", not "diff". Change <nobr>--tk</nobr> to <nobr>--by</nobr> to see the diff in your web browser. 9. On web pages showing the content of a file (for example [/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: [/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. |
Added www/history.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 | # The History And Purpose Of Fossil Fossil is a [distributed version control system (DVCS)][100] written beginning in [2007][105] by the [architect of SQLite][110] for the purpose of managing the [SQLite project][115]. [100]: https://en.wikipedia.org/wiki/Distributed_version_control [105]: /timeline?a=1970-01-01&n1=10 [110]: https://sqlite.org/crew.html [115]: https://sqlite.org/ Though Fossil was originally written specifically to support SQLite, it is now also used by countless other projects. The SQLite architect (drh) is still the top committer to Fossil, but there are also [many other contributors][120]. [120]: /reports?type=ci&view=byuser ## History The SQLite project start out using [CVS][300], as CVS was the most commonly used version control system in that era (circa 2000). CVS was an amazing version control system for its day in that it allowed multiple developers to be editing the same file at the same time. [300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System Though innovative and much loved in its time, CVS was not without problems. Among those was a lack of visibility into the project history and the lack of integrated bug tracking. To address these deficiencies, the SQLite author developed the [CVSTrac][305] wrapper for CVS beginning in [2002][310]. [305]: http://cvstrac.org/ [310]: http://cvstrac.org/fossil/timeline?a=19700101&n1=10 CVSTrac greatly improved the usability of CVS and was adopted by other projects. CVSTrac also [inspired the design][315] of [Trac][320], which was a similar system that was (and is) far more widely used. [315]: https://trac.edgewall.org/wiki/TracHistory [320]: https://trac.edgewall.org/ Historians can see the influence of CVSTrac on the development of SQLite. [Early SQLite check-ins][325] that happened before CVSTrac often had a check-in comment that was just a "smiley". That was not an unreasonable check-in comment, as check-in comments were scarcely seen and of questionable utility in raw CVS. CVSTrac changed that, making check-in comments more visible and more useful. The SQLite developers reacted by creating [better check-in comments][330]. [325]: https://sqlite.org/src/timeline?a=19700101&n1=10 [330]: https://sqlite.org/src/timeline?c=20030101&n1=10&nd At about this same time, the [Monotone][335] system appeared. Monotone was one of the first distributed version control systems. As far as this author is aware, Monotone was the first VCS to make use of SHA1 to identify artifacts. Monotone stored its content in an SQLite database, which is what brought it to the attention of the SQLite architect. These design choices were a major source of inspiration for Fossil. [335]: https://www.monotone.ca/ Beginning around 2005, the need for a better version control system for SQLite began to become evident. The SQLite architect looked around for a suitable replacement. Monotone, Git, and Mercurial were all considered. But at that time, none of these supported sync over ordinary HTTP, none could be run from an inexpensive shell account on a leased server (this was before the widespread availability of affordable virtual private servers), and none of them supported anything resembling the wiki and ticket features of CVSTrac that had been found to be so useful. And so, the SQLite architect began writing his own DVCS. Early prototypes were done in [TCL][340]. As experiments proceeded, however, it was found that the low-level byte manipulates needed for things like delta compression and computing diffs were better implemented in plain old C. Experiments continued. Finally, a prototype capable of self-hosting was devised on [2007-07-16][345]. [340]: https://www.tcl.tk/ [345]: https://fossil-scm.org/fossil/timeline?c=200707211410&n1=10 The first project hosted by Fossil was Fossil itself. After a few months of development work, the code was considered stable enough to begin hosting the [SQLite documentation repository][350] which was split off from the main SQLite CVS repository on [2007-11-12][355]. After two years of development work on Fossil, the SQLite source code itself was transferred to Fossil on [2009-08-11][360]. [350]: https://www.sqlite.org/docsrc/doc/trunk/README.md [355]: https://www.sqlite.org/docsrc/timeline?c=200711120345&n1=10 [360]: https://sqlite.org/src/timeline?c=b0848925babde524&n1=12&y=ci |
Added www/hooks.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 | # Hooks Hooks are short scripts that Fossil runs at defined points of processing. Administrators can use hooks to help enforce policy or connect Fossil to a continuous integration (CI) system. ## Interim Documentation. * This is a work-in-progress. The interface is in flux. For the time being, the documentation as a list of bullet points. We hope to transform this into a proper document later, after things settle down. * Contributions and suggestions to the hook system and/or the documentation are welcomed. ## General Notes. * Each hooks has a "type", a "sequence", and a "command". The command is a shell command that runs at the appropriate time. The type is currently one of "after-receive", "before-commit", "commit-msg", or "disabled". The sequence is an arbitrary integer. * There can be multiple hooks of the same type. When that is the case, the hooks are run in order of ascending sequence. * Use the "fossil hook" command to create, edit, and delete hooks. * Use the "fossil hook test" command to test new hooks. ## Hook Scripts * All scripts are expected to run relatively quickly. If a long-running process is started by a hook, it should be run in the background so that the original script can return. * The "%F" sequence inside the script is translated into the name of the fossil executable. * The "%R" sequence in the script is translated in to the name of the repository. * The "%A" sequence becomes the name of an auxiliary input files, the meaning of which depends on the hook type. The auxiliary filename might be an empty string. Take care to use appropriate quoting! ## Disabled Hooks * Hooks with type "disabled" never run. They are a place-holder for scripts that might be converted to some other hook-type later. For example, the command "fossil hook edit --type disabled ID" can be used to temporarily disable the hook named ID, and then "fossil hook edit --type after-receive ID" can be used to reenable it later. ## After-Receive Hooks * The "after-receive" hook is run by [the backoffice](./backoffice.md) whenever new artifacts are received into the repository. The artifacts have already been committed and so there is nothing that the after-receive hook can do to block them. * The after-receive hooks are intended to be run on a server to start up a background testing or CI process. But they can also be run on the client side. The key point is that after-receive hooks are invoked by backoffice, so backoffice must be running in order to fire after-receive hooks. * The exit code from the after-receive script is ignored. * The standard input to the after-receive hook is a list of new artifacts, one per line. The first token on each line is the hash of the new artifact. After the hash is a human-readable text description of what the artifact represents. * Sometimes the same artifact can represent two or more things. For example, the same artifact might represent two or more files in the check-out (assuming the files hold identical content). In that case, the text description that is input to the after-receive hook only shows one of the possible uses for the artifact. * If two or more pushes occur against a repository at about the same time, then the set of artifacts added by both pushes might be combined into a single after-receive callback. * Fossil holds a write transaction on the repository while the after-receive hook is running. If the script needs to access the database, then the database will need to be in WAL mode so that readers can co-exist with the writer. Or the script might just launch a background process that waits until the hook script finishes and the transaction commits before it tries to access the repository database. * A push might not deliver all of the artifacts for a checkin. If Fossil knows that a /xfer HTTP request is incomplete, it will defer running the after-receive push for 60 seconds, or until a complete /xfer request is received. This helps to prevent after-receive hooks from running when incomplete checkins exist in the repository, but it does not provide hard guarantees, as there is no way to do that in a distributed system. * The list of artifacts delivered to standard input of the after-receive hook will not contain more than 24-hours worth of artifacts. If the backoffice has been shut down for a while such that after-receive hooks have not been running, and more than 24-hours of changes have accumulated since the last run of an after-receive hook, then only the most recent 24-hours is included in the input. ## Before-Commit Hooks * Before-commit hooks run during the "fossil commit" command before the user is prompted for the check-in comment. Fossil holds a write-transaction on the repository when the before-commit hook is running, so the repository needs to be in WAL mode if the script needs to access the repository. * The %A substitution is the name of a "commit description file" that shows the details of the commit in progress. To see what a "commit description file" looks like, set a before-commit hook with a command of "cat %Q" and then run a sample commit with the --dry-run option. * If any before-commit hook returns a non-zero exit code, then the commit is abandoned. All before-commit hooks must exit(0) in order for the commit to proceed. * The --no-validate flag to the "fossil commit" command prevents any before-commit hooks from running. * The --trace flag to the "fossil commit" command shows each before-commit hook as it is run. * If a before-commit hook fails, it should print an error message on standard output or standard error. Otherwise, the user won't know what went wrong, because Fossil won't tell them. * Nothing is written to standard input of the before-commit hook. The information transmitted to the before-commit hook is contained in the "%A" auxiliary file. The before-commit hook must open and read that file if it wants access to the commit information. ## Commit-Msg Hooks * Commit-msg hooks are not yet implemented. * The commit-msg hooks run during "fossil commit" after the check-in messages has been entered by the user. The "%A" argument to the commit-msg hook is the text of the commit message. The intent of the commit-msg hook is to validate the text of the commit message to (for example) check for typos or ensure that it conforms to standards. * If any commit-msg hook returns a non-zero exit code, then the commit is abandoned. All commit-msg hooks must exit(0) in order for the commit to proceed. * Commit-msg hooks are advisory only. Each developer is in total control of the local repository and can easily bypass the hooks to cause a non-conforming checkin to be committed. |
Added www/image-format-vs-repo-size.ipynb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Image Format vs Fossil Repository Size\n", "\n", "## Prerequisites\n", "\n", "This notebook was originally developed with standalone [JupyterLab] and Python 2 but was later moved to JupyterLab under [Anaconda] with Python 3. Backporting to Python 2 may require manual adjustment. Getting it running under stock JupyterLab or plain-old-Jupyter should be straightforward for one familiar with the tools. We will assume you're following in my footsteps, using Anaconda.\n", "\n", "One of the reasons we switched to Anaconda is that it comes with all but one of this notebook's prerequisites, that last remaining one of which you install so:\n", "\n", " $ pip install wand\n", "\n", "That should be done in a shell where \"`pip`\" is the version that came with Anaconda. Otherwise, the package will likely end up in some *other* Python package tree, which Anaconda's Python kernel may not be smart enough to find on its own.\n", "\n", "Note that you do *not* use `conda` for this: as of this writing, [Wand] is not available in a form that installs via `conda`.\n", "\n", "This notebook was written and tested on a macOS system where `/tmp` exists. Other platforms may require adjustments to the scripts below.\n", "\n", "[Anaconda]: https://www.anaconda.com/distribution/\n", "[JupyterLab]: https://github.com/jupyterlab/\n", "[Wand]: http://wand-py.org/\n", "\n", "\n", "## Running\n", "\n", "The next cell generates the test repositories. This takes about 3 seconds to run on my machine. If you have to uncomment the \"`sleep`\" call in the inner loop, this will go up to about 45 seconds.\n", "\n", "The next cell produces the bar chart from the collected data, all but instantaneously.\n", "\n", "This split allows you to generate the expensive experimental data in a single pass, then play as many games as you like with the generated data.\n", "\n", "\n", "## Discussion\n", "\n", "That is kept in [a separate document](image-format-vs-repo-size.md) so we can share that document with Fossil's Markdown renderer." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Created test directory /tmp/image-format-vs-repo-size\n", "Created ../test-jpeg.fossil for format JPEG.\n", "Created ../test-bmp.fossil for format BMP.\n", "Created ../test-tiff.fossil for format TIFF.\n", "Created ../test-png.fossil for format PNG.\n", "Experiment completed in 3.0627901554107666 seconds.\n" ] } ], "source": [ "import os\n", "import random\n", "import subprocess\n", "import time\n", "\n", "from wand.color import Color\n", "from wand.drawing import Drawing\n", "from wand.image import Image\n", "\n", "import pandas as pd\n", "\n", "size = 256\n", "iterations = 10\n", "start = time.time()\n", "repo_sizes = []\n", "fossil = '/usr/local/bin/fossil'\n", "\n", "if not os.path.isfile(fossil): raise RuntimeError(\"No such executable \" + fossil)\n", "if not os.access(fossil, os.X_OK): raise RuntimeError(\"Cannot execute \" + fossil)\n", "\n", "tdir = os.path.join('/tmp', 'image-format-vs-repo-size')\n", "if not os.path.isdir(tdir): os.mkdir(tdir, 0o700)\n", "print(\"Created test directory \" + tdir)\n", " \n", "formats = ['JPEG', 'BMP', 'TIFF', 'PNG']\n", "for f in formats:\n", " ext = f.lower()\n", " wdir = os.path.join(tdir, 'work-' + ext)\n", " if not os.path.isdir(wdir): os.mkdir(wdir, 0o700)\n", " os.chdir(wdir)\n", " repo = '../test-' + ext + '.fossil'\n", " ifn = 'test.' + ext\n", " ipath = os.path.join(wdir, ifn)\n", " rs = []\n", " \n", " def add_repo_size():\n", " rs.append(os.path.getsize(repo) / 1024.0 / 1024.0)\n", " \n", " def set_repo_page_size(n):\n", " subprocess.run([\n", " fossil,\n", " 'rebuild',\n", " '--compress',\n", " '--pagesize',\n", " str(n),\n", " '--vacuum'\n", " ])\n", "\n", " try:\n", " # Create test repo\n", " subprocess.run([fossil, 'init', repo])\n", " subprocess.run([fossil, 'open', repo])\n", " subprocess.run([fossil, 'set', 'binary-glob', \"*.{0}\".format(ext)])\n", " set_repo_page_size(512) # minimum\n", " add_repo_size()\n", " set_repo_page_size(8192) # default\n", " print(\"Created \" + repo + \" for format \" + f + \".\")\n", "\n", " # Create test image and add it to the repo\n", " img = Image(width = size, height = size, depth = 8,\n", " background = 'white')\n", " img.alpha_channel = 'remove'\n", " img.evaluate('gaussiannoise', 1.0)\n", " img.save(filename = ipath)\n", " subprocess.run([fossil, 'add', ifn])\n", " subprocess.run([fossil, 'ci', '-m', 'initial'])\n", " #print(\"Added initial \" + f + \" image.\")\n", " add_repo_size()\n", "\n", " # Change a random pixel to a random RGB value and check it in\n", " # $iterations times.\n", " for i in range(iterations - 1):\n", " with Drawing() as draw:\n", " x = random.randint(0, size - 1)\n", " y = random.randint(0, size - 1)\n", "\n", " r = random.randint(0, 255)\n", " g = random.randint(0, 255)\n", " b = random.randint(0, 255)\n", " \n", " draw.fill_color = Color('rgb({0},{1},{2})'.format(\n", " r, g, b\n", " ))\n", " draw.color(x, y, 'point')\n", " draw(img)\n", " img.save(filename = ipath)\n", " \n", " # You might need to uncomment the next line if you find that\n", " # the repo size doesn't change as expected. In some versions\n", " # of Wand (or is it the ImageMagick underneath?) we have seen\n", " # what appear to be asynchronous saves, with a zero-length file\n", " # here if you don't wait for the save to complete.\n", " #time.sleep(1.0)\n", " \n", " subprocess.run([fossil, 'ci', '-m', '\"change {0} step {1}'.format(\n", " f, i\n", " )])\n", " add_repo_size()\n", " \n", " # Repo complete for this format\n", " repo_sizes.append(pd.Series(rs, name=f))\n", "\n", " finally:\n", " if os.path.exists(ipath): os.remove(ipath)\n", " if os.path.exists(tdir):\n", " if os.path.isfile(repo):\n", " subprocess.run([fossil, 'close', '-f'])\n", " os.unlink(repo)\n", " os.chdir(tdir);\n", " os.rmdir(wdir)\n", " if os.path.exists(repo): os.remove(repo)\n", " \n", "print(\"Experiment completed in \" + str(time.time() - start) + \" seconds.\")" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n", "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n", " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", "<!-- Created with matplotlib (https://matplotlib.org/) -->\n", "<svg height=\"265.243125pt\" version=\"1.1\" viewBox=\"0 0 385.78125 265.243125\" width=\"385.78125pt\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", " <defs>\n", " <style type=\"text/css\">\n", "*{stroke-linecap:butt;stroke-linejoin:round;}\n", " </style>\n", " </defs>\n", " <g id=\"figure_1\">\n", " <g id=\"patch_1\">\n", " <path d=\"M 0 265.243125 \n", "L 385.78125 265.243125 \n", "L 385.78125 0 \n", "L 0 0 \n", "z\n", "\" style=\"fill:none;\"/>\n", " </g>\n", " <g id=\"axes_1\">\n", " <g id=\"patch_2\">\n", " <path d=\"M 43.78125 224.64 \n", "L 378.58125 224.64 \n", "L 378.58125 7.2 \n", "L 43.78125 7.2 \n", "z\n", "\" style=\"fill:#ffffff;\"/>\n", " </g>\n", " <g id=\"patch_3\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 51.907464 224.64 \n", "L 58.408434 224.64 \n", "L 58.408434 138.354286 \n", "L 51.907464 138.354286 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_4\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 84.412318 224.64 \n", "L 90.913289 224.64 \n", "L 90.913289 126.849524 \n", "L 84.412318 126.849524 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_5\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 116.917172 224.64 \n", "L 123.418143 224.64 \n", "L 123.418143 118.220952 \n", "L 116.917172 118.220952 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_6\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 149.422027 224.64 \n", "L 155.922998 224.64 \n", "L 155.922998 112.468571 \n", "L 149.422027 112.468571 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_7\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 181.926881 224.64 \n", "L 188.427852 224.64 \n", "L 188.427852 109.592381 \n", "L 181.926881 109.592381 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_8\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 214.431735 224.64 \n", "L 220.932706 224.64 \n", "L 220.932706 103.84 \n", "L 214.431735 103.84 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_9\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 246.93659 224.64 \n", "L 253.437561 224.64 \n", "L 253.437561 98.087619 \n", "L 246.93659 98.087619 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_10\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 279.441444 224.64 \n", "L 285.942415 224.64 \n", "L 285.942415 98.087619 \n", "L 279.441444 98.087619 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_11\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 311.946299 224.64 \n", "L 318.447269 224.64 \n", "L 318.447269 82.268571 \n", "L 311.946299 82.268571 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_12\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 344.451153 224.64 \n", "L 350.952124 224.64 \n", "L 350.952124 77.954286 \n", "L 344.451153 77.954286 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_13\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 58.408434 224.64 \n", "L 64.909405 224.64 \n", "L 64.909405 125.411429 \n", "L 58.408434 125.411429 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_14\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 90.913289 224.64 \n", "L 97.41426 224.64 \n", "L 97.41426 105.278095 \n", "L 90.913289 105.278095 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_15\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 123.418143 224.64 \n", "L 129.919114 224.64 \n", "L 129.919114 105.278095 \n", "L 123.418143 105.278095 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_16\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 155.922998 224.64 \n", "L 162.423968 224.64 \n", "L 162.423968 105.278095 \n", "L 155.922998 105.278095 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_17\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 188.427852 224.64 \n", "L 194.928823 224.64 \n", "L 194.928823 105.278095 \n", "L 188.427852 105.278095 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_18\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 220.932706 224.64 \n", "L 227.433677 224.64 \n", "L 227.433677 105.278095 \n", "L 220.932706 105.278095 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_19\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 253.437561 224.64 \n", "L 259.938532 224.64 \n", "L 259.938532 105.278095 \n", "L 253.437561 105.278095 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_20\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 285.942415 224.64 \n", "L 292.443386 224.64 \n", "L 292.443386 105.278095 \n", "L 285.942415 105.278095 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_21\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 318.447269 224.64 \n", "L 324.94824 224.64 \n", "L 324.94824 105.278095 \n", "L 318.447269 105.278095 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_22\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 350.952124 224.64 \n", "L 357.453095 224.64 \n", "L 357.453095 105.278095 \n", "L 350.952124 105.278095 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_23\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 64.909405 224.64 \n", "L 71.410376 224.64 \n", "L 71.410376 128.287619 \n", "L 64.909405 128.287619 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_24\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 97.41426 224.64 \n", "L 103.915231 224.64 \n", "L 103.915231 108.154286 \n", "L 97.41426 108.154286 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_25\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 129.919114 224.64 \n", "L 136.420085 224.64 \n", "L 136.420085 108.154286 \n", "L 129.919114 108.154286 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_26\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 162.423968 224.64 \n", "L 168.924939 224.64 \n", "L 168.924939 108.154286 \n", "L 162.423968 108.154286 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_27\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 194.928823 224.64 \n", "L 201.429794 224.64 \n", "L 201.429794 108.154286 \n", "L 194.928823 108.154286 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_28\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 227.433677 224.64 \n", "L 233.934648 224.64 \n", "L 233.934648 108.154286 \n", "L 227.433677 108.154286 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_29\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 259.938532 224.64 \n", "L 266.439502 224.64 \n", "L 266.439502 108.154286 \n", "L 259.938532 108.154286 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_30\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 292.443386 224.64 \n", "L 298.944357 224.64 \n", "L 298.944357 108.154286 \n", "L 292.443386 108.154286 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_31\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 324.94824 224.64 \n", "L 331.449211 224.64 \n", "L 331.449211 108.154286 \n", "L 324.94824 108.154286 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_32\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 357.453095 224.64 \n", "L 363.954066 224.64 \n", "L 363.954066 108.154286 \n", "L 357.453095 108.154286 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_33\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 71.410376 224.64 \n", "L 77.911347 224.64 \n", "L 77.911347 129.725714 \n", "L 71.410376 129.725714 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_34\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 103.915231 224.64 \n", "L 110.416201 224.64 \n", "L 110.416201 111.030476 \n", "L 103.915231 111.030476 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_35\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 136.420085 224.64 \n", "L 142.921056 224.64 \n", "L 142.921056 80.830476 \n", "L 136.420085 80.830476 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_36\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 168.924939 224.64 \n", "L 175.42591 224.64 \n", "L 175.42591 75.078095 \n", "L 168.924939 75.078095 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_37\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 201.429794 224.64 \n", "L 207.930765 224.64 \n", "L 207.930765 70.76381 \n", "L 201.429794 70.76381 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_38\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 233.934648 224.64 \n", "L 240.435619 224.64 \n", "L 240.435619 56.382857 \n", "L 233.934648 56.382857 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_39\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 266.439502 224.64 \n", "L 272.940473 224.64 \n", "L 272.940473 40.56381 \n", "L 266.439502 40.56381 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_40\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 298.944357 224.64 \n", "L 305.445328 224.64 \n", "L 305.445328 30.497143 \n", "L 298.944357 30.497143 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_41\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 331.449211 224.64 \n", "L 337.950182 224.64 \n", "L 337.950182 26.182857 \n", "L 331.449211 26.182857 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"patch_42\">\n", " <path clip-path=\"url(#p70301a0bed)\" d=\"M 363.954066 224.64 \n", "L 370.455036 224.64 \n", "L 370.455036 17.554286 \n", "L 363.954066 17.554286 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"matplotlib.axis_1\">\n", " <g id=\"xtick_1\">\n", " <g id=\"line2d_1\">\n", " <defs>\n", " <path d=\"M 0 0 \n", "L 0 3.5 \n", "\" id=\"m639e59548b\" style=\"stroke:#000000;stroke-width:0.8;\"/>\n", " </defs>\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"64.909405\" xlink:href=\"#m639e59548b\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_1\">\n", " <!-- 1 -->\n", " <defs>\n", " <path d=\"M 12.40625 8.296875 \n", "L 28.515625 8.296875 \n", "L 28.515625 63.921875 \n", "L 10.984375 60.40625 \n", "L 10.984375 69.390625 \n", "L 28.421875 72.90625 \n", "L 38.28125 72.90625 \n", "L 38.28125 8.296875 \n", "L 54.390625 8.296875 \n", "L 54.390625 0 \n", "L 12.40625 0 \n", "z\n", "\" id=\"DejaVuSans-49\"/>\n", " </defs>\n", " <g transform=\"translate(67.66878 238.0025)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-49\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"xtick_2\">\n", " <g id=\"line2d_2\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"97.41426\" xlink:href=\"#m639e59548b\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_2\">\n", " <!-- 2 -->\n", " <defs>\n", " <path d=\"M 19.1875 8.296875 \n", "L 53.609375 8.296875 \n", "L 53.609375 0 \n", "L 7.328125 0 \n", "L 7.328125 8.296875 \n", "Q 12.9375 14.109375 22.625 23.890625 \n", "Q 32.328125 33.6875 34.8125 36.53125 \n", "Q 39.546875 41.84375 41.421875 45.53125 \n", "Q 43.3125 49.21875 43.3125 52.78125 \n", "Q 43.3125 58.59375 39.234375 62.25 \n", "Q 35.15625 65.921875 28.609375 65.921875 \n", "Q 23.96875 65.921875 18.8125 64.3125 \n", "Q 13.671875 62.703125 7.8125 59.421875 \n", "L 7.8125 69.390625 \n", "Q 13.765625 71.78125 18.9375 73 \n", "Q 24.125 74.21875 28.421875 74.21875 \n", "Q 39.75 74.21875 46.484375 68.546875 \n", "Q 53.21875 62.890625 53.21875 53.421875 \n", "Q 53.21875 48.921875 51.53125 44.890625 \n", "Q 49.859375 40.875 45.40625 35.40625 \n", "Q 44.1875 33.984375 37.640625 27.21875 \n", "Q 31.109375 20.453125 19.1875 8.296875 \n", "z\n", "\" id=\"DejaVuSans-50\"/>\n", " </defs>\n", " <g transform=\"translate(100.173635 238.0025)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-50\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"xtick_3\">\n", " <g id=\"line2d_3\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"129.919114\" xlink:href=\"#m639e59548b\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_3\">\n", " <!-- 3 -->\n", " <defs>\n", " <path d=\"M 40.578125 39.3125 \n", "Q 47.65625 37.796875 51.625 33 \n", "Q 55.609375 28.21875 55.609375 21.1875 \n", "Q 55.609375 10.40625 48.1875 4.484375 \n", "Q 40.765625 -1.421875 27.09375 -1.421875 \n", "Q 22.515625 -1.421875 17.65625 -0.515625 \n", "Q 12.796875 0.390625 7.625 2.203125 \n", "L 7.625 11.71875 \n", "Q 11.71875 9.328125 16.59375 8.109375 \n", "Q 21.484375 6.890625 26.8125 6.890625 \n", "Q 36.078125 6.890625 40.9375 10.546875 \n", "Q 45.796875 14.203125 45.796875 21.1875 \n", "Q 45.796875 27.640625 41.28125 31.265625 \n", "Q 36.765625 34.90625 28.71875 34.90625 \n", "L 20.21875 34.90625 \n", "L 20.21875 43.015625 \n", "L 29.109375 43.015625 \n", "Q 36.375 43.015625 40.234375 45.921875 \n", "Q 44.09375 48.828125 44.09375 54.296875 \n", "Q 44.09375 59.90625 40.109375 62.90625 \n", "Q 36.140625 65.921875 28.71875 65.921875 \n", "Q 24.65625 65.921875 20.015625 65.03125 \n", "Q 15.375 64.15625 9.8125 62.3125 \n", "L 9.8125 71.09375 \n", "Q 15.4375 72.65625 20.34375 73.4375 \n", "Q 25.25 74.21875 29.59375 74.21875 \n", "Q 40.828125 74.21875 47.359375 69.109375 \n", "Q 53.90625 64.015625 53.90625 55.328125 \n", "Q 53.90625 49.265625 50.4375 45.09375 \n", "Q 46.96875 40.921875 40.578125 39.3125 \n", "z\n", "\" id=\"DejaVuSans-51\"/>\n", " </defs>\n", " <g transform=\"translate(132.678489 238.0025)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-51\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"xtick_4\">\n", " <g id=\"line2d_4\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"162.423968\" xlink:href=\"#m639e59548b\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_4\">\n", " <!-- 4 -->\n", " <defs>\n", " <path d=\"M 37.796875 64.3125 \n", "L 12.890625 25.390625 \n", "L 37.796875 25.390625 \n", "z\n", "M 35.203125 72.90625 \n", "L 47.609375 72.90625 \n", "L 47.609375 25.390625 \n", "L 58.015625 25.390625 \n", "L 58.015625 17.1875 \n", "L 47.609375 17.1875 \n", "L 47.609375 0 \n", "L 37.796875 0 \n", "L 37.796875 17.1875 \n", "L 4.890625 17.1875 \n", "L 4.890625 26.703125 \n", "z\n", "\" id=\"DejaVuSans-52\"/>\n", " </defs>\n", " <g transform=\"translate(165.183343 238.0025)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-52\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"xtick_5\">\n", " <g id=\"line2d_5\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"194.928823\" xlink:href=\"#m639e59548b\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_5\">\n", " <!-- 5 -->\n", " <defs>\n", " <path d=\"M 10.796875 72.90625 \n", "L 49.515625 72.90625 \n", "L 49.515625 64.59375 \n", "L 19.828125 64.59375 \n", "L 19.828125 46.734375 \n", "Q 21.96875 47.46875 24.109375 47.828125 \n", "Q 26.265625 48.1875 28.421875 48.1875 \n", "Q 40.625 48.1875 47.75 41.5 \n", "Q 54.890625 34.8125 54.890625 23.390625 \n", "Q 54.890625 11.625 47.5625 5.09375 \n", "Q 40.234375 -1.421875 26.90625 -1.421875 \n", "Q 22.3125 -1.421875 17.546875 -0.640625 \n", "Q 12.796875 0.140625 7.71875 1.703125 \n", "L 7.71875 11.625 \n", "Q 12.109375 9.234375 16.796875 8.0625 \n", "Q 21.484375 6.890625 26.703125 6.890625 \n", "Q 35.15625 6.890625 40.078125 11.328125 \n", "Q 45.015625 15.765625 45.015625 23.390625 \n", "Q 45.015625 31 40.078125 35.4375 \n", "Q 35.15625 39.890625 26.703125 39.890625 \n", "Q 22.75 39.890625 18.8125 39.015625 \n", "Q 14.890625 38.140625 10.796875 36.28125 \n", "z\n", "\" id=\"DejaVuSans-53\"/>\n", " </defs>\n", " <g transform=\"translate(197.688198 238.0025)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-53\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"xtick_6\">\n", " <g id=\"line2d_6\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"227.433677\" xlink:href=\"#m639e59548b\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_6\">\n", " <!-- 6 -->\n", " <defs>\n", " <path d=\"M 33.015625 40.375 \n", "Q 26.375 40.375 22.484375 35.828125 \n", "Q 18.609375 31.296875 18.609375 23.390625 \n", "Q 18.609375 15.53125 22.484375 10.953125 \n", "Q 26.375 6.390625 33.015625 6.390625 \n", "Q 39.65625 6.390625 43.53125 10.953125 \n", "Q 47.40625 15.53125 47.40625 23.390625 \n", "Q 47.40625 31.296875 43.53125 35.828125 \n", "Q 39.65625 40.375 33.015625 40.375 \n", "z\n", "M 52.59375 71.296875 \n", "L 52.59375 62.3125 \n", "Q 48.875 64.0625 45.09375 64.984375 \n", "Q 41.3125 65.921875 37.59375 65.921875 \n", "Q 27.828125 65.921875 22.671875 59.328125 \n", "Q 17.53125 52.734375 16.796875 39.40625 \n", "Q 19.671875 43.65625 24.015625 45.921875 \n", "Q 28.375 48.1875 33.59375 48.1875 \n", "Q 44.578125 48.1875 50.953125 41.515625 \n", "Q 57.328125 34.859375 57.328125 23.390625 \n", "Q 57.328125 12.15625 50.6875 5.359375 \n", "Q 44.046875 -1.421875 33.015625 -1.421875 \n", "Q 20.359375 -1.421875 13.671875 8.265625 \n", "Q 6.984375 17.96875 6.984375 36.375 \n", "Q 6.984375 53.65625 15.1875 63.9375 \n", "Q 23.390625 74.21875 37.203125 74.21875 \n", "Q 40.921875 74.21875 44.703125 73.484375 \n", "Q 48.484375 72.75 52.59375 71.296875 \n", "z\n", "\" id=\"DejaVuSans-54\"/>\n", " </defs>\n", " <g transform=\"translate(230.193052 238.0025)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-54\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"xtick_7\">\n", " <g id=\"line2d_7\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"259.938532\" xlink:href=\"#m639e59548b\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_7\">\n", " <!-- 7 -->\n", " <defs>\n", " <path d=\"M 8.203125 72.90625 \n", "L 55.078125 72.90625 \n", "L 55.078125 68.703125 \n", "L 28.609375 0 \n", "L 18.3125 0 \n", "L 43.21875 64.59375 \n", "L 8.203125 64.59375 \n", "z\n", "\" id=\"DejaVuSans-55\"/>\n", " </defs>\n", " <g transform=\"translate(262.697907 238.0025)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-55\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"xtick_8\">\n", " <g id=\"line2d_8\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"292.443386\" xlink:href=\"#m639e59548b\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_8\">\n", " <!-- 8 -->\n", " <defs>\n", " <path d=\"M 31.78125 34.625 \n", "Q 24.75 34.625 20.71875 30.859375 \n", "Q 16.703125 27.09375 16.703125 20.515625 \n", "Q 16.703125 13.921875 20.71875 10.15625 \n", "Q 24.75 6.390625 31.78125 6.390625 \n", "Q 38.8125 6.390625 42.859375 10.171875 \n", "Q 46.921875 13.96875 46.921875 20.515625 \n", "Q 46.921875 27.09375 42.890625 30.859375 \n", "Q 38.875 34.625 31.78125 34.625 \n", "z\n", "M 21.921875 38.8125 \n", "Q 15.578125 40.375 12.03125 44.71875 \n", "Q 8.5 49.078125 8.5 55.328125 \n", "Q 8.5 64.0625 14.71875 69.140625 \n", "Q 20.953125 74.21875 31.78125 74.21875 \n", "Q 42.671875 74.21875 48.875 69.140625 \n", "Q 55.078125 64.0625 55.078125 55.328125 \n", "Q 55.078125 49.078125 51.53125 44.71875 \n", "Q 48 40.375 41.703125 38.8125 \n", "Q 48.828125 37.15625 52.796875 32.3125 \n", "Q 56.78125 27.484375 56.78125 20.515625 \n", "Q 56.78125 9.90625 50.3125 4.234375 \n", "Q 43.84375 -1.421875 31.78125 -1.421875 \n", "Q 19.734375 -1.421875 13.25 4.234375 \n", "Q 6.78125 9.90625 6.78125 20.515625 \n", "Q 6.78125 27.484375 10.78125 32.3125 \n", "Q 14.796875 37.15625 21.921875 38.8125 \n", "z\n", "M 18.3125 54.390625 \n", "Q 18.3125 48.734375 21.84375 45.5625 \n", "Q 25.390625 42.390625 31.78125 42.390625 \n", "Q 38.140625 42.390625 41.71875 45.5625 \n", "Q 45.3125 48.734375 45.3125 54.390625 \n", "Q 45.3125 60.0625 41.71875 63.234375 \n", "Q 38.140625 66.40625 31.78125 66.40625 \n", "Q 25.390625 66.40625 21.84375 63.234375 \n", "Q 18.3125 60.0625 18.3125 54.390625 \n", "z\n", "\" id=\"DejaVuSans-56\"/>\n", " </defs>\n", " <g transform=\"translate(295.202761 238.0025)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-56\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"xtick_9\">\n", " <g id=\"line2d_9\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"324.94824\" xlink:href=\"#m639e59548b\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_9\">\n", " <!-- 9 -->\n", " <defs>\n", " <path d=\"M 10.984375 1.515625 \n", "L 10.984375 10.5 \n", "Q 14.703125 8.734375 18.5 7.8125 \n", "Q 22.3125 6.890625 25.984375 6.890625 \n", "Q 35.75 6.890625 40.890625 13.453125 \n", "Q 46.046875 20.015625 46.78125 33.40625 \n", "Q 43.953125 29.203125 39.59375 26.953125 \n", "Q 35.25 24.703125 29.984375 24.703125 \n", "Q 19.046875 24.703125 12.671875 31.3125 \n", "Q 6.296875 37.9375 6.296875 49.421875 \n", "Q 6.296875 60.640625 12.9375 67.421875 \n", "Q 19.578125 74.21875 30.609375 74.21875 \n", "Q 43.265625 74.21875 49.921875 64.515625 \n", "Q 56.59375 54.828125 56.59375 36.375 \n", "Q 56.59375 19.140625 48.40625 8.859375 \n", "Q 40.234375 -1.421875 26.421875 -1.421875 \n", "Q 22.703125 -1.421875 18.890625 -0.6875 \n", "Q 15.09375 0.046875 10.984375 1.515625 \n", "z\n", "M 30.609375 32.421875 \n", "Q 37.25 32.421875 41.125 36.953125 \n", "Q 45.015625 41.5 45.015625 49.421875 \n", "Q 45.015625 57.28125 41.125 61.84375 \n", "Q 37.25 66.40625 30.609375 66.40625 \n", "Q 23.96875 66.40625 20.09375 61.84375 \n", "Q 16.21875 57.28125 16.21875 49.421875 \n", "Q 16.21875 41.5 20.09375 36.953125 \n", "Q 23.96875 32.421875 30.609375 32.421875 \n", "z\n", "\" id=\"DejaVuSans-57\"/>\n", " </defs>\n", " <g transform=\"translate(327.707615 238.0025)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-57\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"xtick_10\">\n", " <g id=\"line2d_10\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"357.453095\" xlink:href=\"#m639e59548b\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_10\">\n", " <!-- 10 -->\n", " <defs>\n", " <path d=\"M 31.78125 66.40625 \n", "Q 24.171875 66.40625 20.328125 58.90625 \n", "Q 16.5 51.421875 16.5 36.375 \n", "Q 16.5 21.390625 20.328125 13.890625 \n", "Q 24.171875 6.390625 31.78125 6.390625 \n", "Q 39.453125 6.390625 43.28125 13.890625 \n", "Q 47.125 21.390625 47.125 36.375 \n", "Q 47.125 51.421875 43.28125 58.90625 \n", "Q 39.453125 66.40625 31.78125 66.40625 \n", "z\n", "M 31.78125 74.21875 \n", "Q 44.046875 74.21875 50.515625 64.515625 \n", "Q 56.984375 54.828125 56.984375 36.375 \n", "Q 56.984375 17.96875 50.515625 8.265625 \n", "Q 44.046875 -1.421875 31.78125 -1.421875 \n", "Q 19.53125 -1.421875 13.0625 8.265625 \n", "Q 6.59375 17.96875 6.59375 36.375 \n", "Q 6.59375 54.828125 13.0625 64.515625 \n", "Q 19.53125 74.21875 31.78125 74.21875 \n", "z\n", "\" id=\"DejaVuSans-48\"/>\n", " </defs>\n", " <g transform=\"translate(360.21247 244.365)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-49\"/>\n", " <use x=\"63.623047\" xlink:href=\"#DejaVuSans-48\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"text_11\">\n", " <!-- Checkin index -->\n", " <defs>\n", " <path d=\"M 64.40625 67.28125 \n", "L 64.40625 56.890625 \n", "Q 59.421875 61.53125 53.78125 63.8125 \n", "Q 48.140625 66.109375 41.796875 66.109375 \n", "Q 29.296875 66.109375 22.65625 58.46875 \n", "Q 16.015625 50.828125 16.015625 36.375 \n", "Q 16.015625 21.96875 22.65625 14.328125 \n", "Q 29.296875 6.6875 41.796875 6.6875 \n", "Q 48.140625 6.6875 53.78125 8.984375 \n", "Q 59.421875 11.28125 64.40625 15.921875 \n", "L 64.40625 5.609375 \n", "Q 59.234375 2.09375 53.4375 0.328125 \n", "Q 47.65625 -1.421875 41.21875 -1.421875 \n", "Q 24.65625 -1.421875 15.125 8.703125 \n", "Q 5.609375 18.84375 5.609375 36.375 \n", "Q 5.609375 53.953125 15.125 64.078125 \n", "Q 24.65625 74.21875 41.21875 74.21875 \n", "Q 47.75 74.21875 53.53125 72.484375 \n", "Q 59.328125 70.75 64.40625 67.28125 \n", "z\n", "\" id=\"DejaVuSans-67\"/>\n", " <path d=\"M 54.890625 33.015625 \n", "L 54.890625 0 \n", "L 45.90625 0 \n", "L 45.90625 32.71875 \n", "Q 45.90625 40.484375 42.875 44.328125 \n", "Q 39.84375 48.1875 33.796875 48.1875 \n", "Q 26.515625 48.1875 22.3125 43.546875 \n", "Q 18.109375 38.921875 18.109375 30.90625 \n", "L 18.109375 0 \n", "L 9.078125 0 \n", "L 9.078125 75.984375 \n", "L 18.109375 75.984375 \n", "L 18.109375 46.1875 \n", "Q 21.34375 51.125 25.703125 53.5625 \n", "Q 30.078125 56 35.796875 56 \n", "Q 45.21875 56 50.046875 50.171875 \n", "Q 54.890625 44.34375 54.890625 33.015625 \n", "z\n", "\" id=\"DejaVuSans-104\"/>\n", " <path d=\"M 56.203125 29.59375 \n", "L 56.203125 25.203125 \n", "L 14.890625 25.203125 \n", "Q 15.484375 15.921875 20.484375 11.0625 \n", "Q 25.484375 6.203125 34.421875 6.203125 \n", "Q 39.59375 6.203125 44.453125 7.46875 \n", "Q 49.3125 8.734375 54.109375 11.28125 \n", "L 54.109375 2.78125 \n", "Q 49.265625 0.734375 44.1875 -0.34375 \n", "Q 39.109375 -1.421875 33.890625 -1.421875 \n", "Q 20.796875 -1.421875 13.15625 6.1875 \n", "Q 5.515625 13.8125 5.515625 26.8125 \n", "Q 5.515625 40.234375 12.765625 48.109375 \n", "Q 20.015625 56 32.328125 56 \n", "Q 43.359375 56 49.78125 48.890625 \n", "Q 56.203125 41.796875 56.203125 29.59375 \n", "z\n", "M 47.21875 32.234375 \n", "Q 47.125 39.59375 43.09375 43.984375 \n", "Q 39.0625 48.390625 32.421875 48.390625 \n", "Q 24.90625 48.390625 20.390625 44.140625 \n", "Q 15.875 39.890625 15.1875 32.171875 \n", "z\n", "\" id=\"DejaVuSans-101\"/>\n", " <path d=\"M 48.78125 52.59375 \n", "L 48.78125 44.1875 \n", "Q 44.96875 46.296875 41.140625 47.34375 \n", "Q 37.3125 48.390625 33.40625 48.390625 \n", "Q 24.65625 48.390625 19.8125 42.84375 \n", "Q 14.984375 37.3125 14.984375 27.296875 \n", "Q 14.984375 17.28125 19.8125 11.734375 \n", "Q 24.65625 6.203125 33.40625 6.203125 \n", "Q 37.3125 6.203125 41.140625 7.25 \n", "Q 44.96875 8.296875 48.78125 10.40625 \n", "L 48.78125 2.09375 \n", "Q 45.015625 0.34375 40.984375 -0.53125 \n", "Q 36.96875 -1.421875 32.421875 -1.421875 \n", "Q 20.0625 -1.421875 12.78125 6.34375 \n", "Q 5.515625 14.109375 5.515625 27.296875 \n", "Q 5.515625 40.671875 12.859375 48.328125 \n", "Q 20.21875 56 33.015625 56 \n", "Q 37.15625 56 41.109375 55.140625 \n", "Q 45.0625 54.296875 48.78125 52.59375 \n", "z\n", "\" id=\"DejaVuSans-99\"/>\n", " <path d=\"M 9.078125 75.984375 \n", "L 18.109375 75.984375 \n", "L 18.109375 31.109375 \n", "L 44.921875 54.6875 \n", "L 56.390625 54.6875 \n", "L 27.390625 29.109375 \n", "L 57.625 0 \n", "L 45.90625 0 \n", "L 18.109375 26.703125 \n", "L 18.109375 0 \n", "L 9.078125 0 \n", "z\n", "\" id=\"DejaVuSans-107\"/>\n", " <path d=\"M 9.421875 54.6875 \n", "L 18.40625 54.6875 \n", "L 18.40625 0 \n", "L 9.421875 0 \n", "z\n", "M 9.421875 75.984375 \n", "L 18.40625 75.984375 \n", "L 18.40625 64.59375 \n", "L 9.421875 64.59375 \n", "z\n", "\" id=\"DejaVuSans-105\"/>\n", " <path d=\"M 54.890625 33.015625 \n", "L 54.890625 0 \n", "L 45.90625 0 \n", "L 45.90625 32.71875 \n", "Q 45.90625 40.484375 42.875 44.328125 \n", "Q 39.84375 48.1875 33.796875 48.1875 \n", "Q 26.515625 48.1875 22.3125 43.546875 \n", "Q 18.109375 38.921875 18.109375 30.90625 \n", "L 18.109375 0 \n", "L 9.078125 0 \n", "L 9.078125 54.6875 \n", "L 18.109375 54.6875 \n", "L 18.109375 46.1875 \n", "Q 21.34375 51.125 25.703125 53.5625 \n", "Q 30.078125 56 35.796875 56 \n", "Q 45.21875 56 50.046875 50.171875 \n", "Q 54.890625 44.34375 54.890625 33.015625 \n", "z\n", "\" id=\"DejaVuSans-110\"/>\n", " <path id=\"DejaVuSans-32\"/>\n", " <path d=\"M 45.40625 46.390625 \n", "L 45.40625 75.984375 \n", "L 54.390625 75.984375 \n", "L 54.390625 0 \n", "L 45.40625 0 \n", "L 45.40625 8.203125 \n", "Q 42.578125 3.328125 38.25 0.953125 \n", "Q 33.9375 -1.421875 27.875 -1.421875 \n", "Q 17.96875 -1.421875 11.734375 6.484375 \n", "Q 5.515625 14.40625 5.515625 27.296875 \n", "Q 5.515625 40.1875 11.734375 48.09375 \n", "Q 17.96875 56 27.875 56 \n", "Q 33.9375 56 38.25 53.625 \n", "Q 42.578125 51.265625 45.40625 46.390625 \n", "z\n", "M 14.796875 27.296875 \n", "Q 14.796875 17.390625 18.875 11.75 \n", "Q 22.953125 6.109375 30.078125 6.109375 \n", "Q 37.203125 6.109375 41.296875 11.75 \n", "Q 45.40625 17.390625 45.40625 27.296875 \n", "Q 45.40625 37.203125 41.296875 42.84375 \n", "Q 37.203125 48.484375 30.078125 48.484375 \n", "Q 22.953125 48.484375 18.875 42.84375 \n", "Q 14.796875 37.203125 14.796875 27.296875 \n", "z\n", "\" id=\"DejaVuSans-100\"/>\n", " <path d=\"M 54.890625 54.6875 \n", "L 35.109375 28.078125 \n", "L 55.90625 0 \n", "L 45.3125 0 \n", "L 29.390625 21.484375 \n", "L 13.484375 0 \n", "L 2.875 0 \n", "L 24.125 28.609375 \n", "L 4.6875 54.6875 \n", "L 15.28125 54.6875 \n", "L 29.78125 35.203125 \n", "L 44.28125 54.6875 \n", "z\n", "\" id=\"DejaVuSans-120\"/>\n", " </defs>\n", " <g transform=\"translate(175.885938 255.963437)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-67\"/>\n", " <use x=\"69.824219\" xlink:href=\"#DejaVuSans-104\"/>\n", " <use x=\"133.203125\" xlink:href=\"#DejaVuSans-101\"/>\n", " <use x=\"194.726562\" xlink:href=\"#DejaVuSans-99\"/>\n", " <use x=\"249.707031\" xlink:href=\"#DejaVuSans-107\"/>\n", " <use x=\"307.617188\" xlink:href=\"#DejaVuSans-105\"/>\n", " <use x=\"335.400391\" xlink:href=\"#DejaVuSans-110\"/>\n", " <use x=\"398.779297\" xlink:href=\"#DejaVuSans-32\"/>\n", " <use x=\"430.566406\" xlink:href=\"#DejaVuSans-105\"/>\n", " <use x=\"458.349609\" xlink:href=\"#DejaVuSans-110\"/>\n", " <use x=\"521.728516\" xlink:href=\"#DejaVuSans-100\"/>\n", " <use x=\"585.205078\" xlink:href=\"#DejaVuSans-101\"/>\n", " <use x=\"646.712891\" xlink:href=\"#DejaVuSans-120\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"matplotlib.axis_2\">\n", " <g id=\"ytick_1\">\n", " <g id=\"line2d_11\">\n", " <defs>\n", " <path d=\"M 0 0 \n", "L -3.5 0 \n", "\" id=\"m7e5aed6441\" style=\"stroke:#000000;stroke-width:0.8;\"/>\n", " </defs>\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"43.78125\" xlink:href=\"#m7e5aed6441\" y=\"224.64\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_12\">\n", " <!-- 0.0 -->\n", " <defs>\n", " <path d=\"M 10.6875 12.40625 \n", "L 21 12.40625 \n", "L 21 0 \n", "L 10.6875 0 \n", "z\n", "\" id=\"DejaVuSans-46\"/>\n", " </defs>\n", " <g transform=\"translate(20.878125 228.439219)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-48\"/>\n", " <use x=\"63.623047\" xlink:href=\"#DejaVuSans-46\"/>\n", " <use x=\"95.410156\" xlink:href=\"#DejaVuSans-48\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"ytick_2\">\n", " <g id=\"line2d_12\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"43.78125\" xlink:href=\"#m7e5aed6441\" y=\"187.824762\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_13\">\n", " <!-- 0.2 -->\n", " <g transform=\"translate(20.878125 191.623981)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-48\"/>\n", " <use x=\"63.623047\" xlink:href=\"#DejaVuSans-46\"/>\n", " <use x=\"95.410156\" xlink:href=\"#DejaVuSans-50\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"ytick_3\">\n", " <g id=\"line2d_13\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"43.78125\" xlink:href=\"#m7e5aed6441\" y=\"151.009524\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_14\">\n", " <!-- 0.4 -->\n", " <g transform=\"translate(20.878125 154.808743)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-48\"/>\n", " <use x=\"63.623047\" xlink:href=\"#DejaVuSans-46\"/>\n", " <use x=\"95.410156\" xlink:href=\"#DejaVuSans-52\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"ytick_4\">\n", " <g id=\"line2d_14\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"43.78125\" xlink:href=\"#m7e5aed6441\" y=\"114.194286\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_15\">\n", " <!-- 0.6 -->\n", " <g transform=\"translate(20.878125 117.993504)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-48\"/>\n", " <use x=\"63.623047\" xlink:href=\"#DejaVuSans-46\"/>\n", " <use x=\"95.410156\" xlink:href=\"#DejaVuSans-54\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"ytick_5\">\n", " <g id=\"line2d_15\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"43.78125\" xlink:href=\"#m7e5aed6441\" y=\"77.379048\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_16\">\n", " <!-- 0.8 -->\n", " <g transform=\"translate(20.878125 81.178266)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-48\"/>\n", " <use x=\"63.623047\" xlink:href=\"#DejaVuSans-46\"/>\n", " <use x=\"95.410156\" xlink:href=\"#DejaVuSans-56\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"ytick_6\">\n", " <g id=\"line2d_16\">\n", " <g>\n", " <use style=\"stroke:#000000;stroke-width:0.8;\" x=\"43.78125\" xlink:href=\"#m7e5aed6441\" y=\"40.56381\"/>\n", " </g>\n", " </g>\n", " <g id=\"text_17\">\n", " <!-- 1.0 -->\n", " <g transform=\"translate(20.878125 44.363028)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-49\"/>\n", " <use x=\"63.623047\" xlink:href=\"#DejaVuSans-46\"/>\n", " <use x=\"95.410156\" xlink:href=\"#DejaVuSans-48\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"text_18\">\n", " <!-- Repo size (MiB) -->\n", " <defs>\n", " <path d=\"M 44.390625 34.1875 \n", "Q 47.5625 33.109375 50.5625 29.59375 \n", "Q 53.5625 26.078125 56.59375 19.921875 \n", "L 66.609375 0 \n", "L 56 0 \n", "L 46.6875 18.703125 \n", "Q 43.0625 26.03125 39.671875 28.421875 \n", "Q 36.28125 30.8125 30.421875 30.8125 \n", "L 19.671875 30.8125 \n", "L 19.671875 0 \n", "L 9.8125 0 \n", "L 9.8125 72.90625 \n", "L 32.078125 72.90625 \n", "Q 44.578125 72.90625 50.734375 67.671875 \n", "Q 56.890625 62.453125 56.890625 51.90625 \n", "Q 56.890625 45.015625 53.6875 40.46875 \n", "Q 50.484375 35.9375 44.390625 34.1875 \n", "z\n", "M 19.671875 64.796875 \n", "L 19.671875 38.921875 \n", "L 32.078125 38.921875 \n", "Q 39.203125 38.921875 42.84375 42.21875 \n", "Q 46.484375 45.515625 46.484375 51.90625 \n", "Q 46.484375 58.296875 42.84375 61.546875 \n", "Q 39.203125 64.796875 32.078125 64.796875 \n", "z\n", "\" id=\"DejaVuSans-82\"/>\n", " <path d=\"M 18.109375 8.203125 \n", "L 18.109375 -20.796875 \n", "L 9.078125 -20.796875 \n", "L 9.078125 54.6875 \n", "L 18.109375 54.6875 \n", "L 18.109375 46.390625 \n", "Q 20.953125 51.265625 25.265625 53.625 \n", "Q 29.59375 56 35.59375 56 \n", "Q 45.5625 56 51.78125 48.09375 \n", "Q 58.015625 40.1875 58.015625 27.296875 \n", "Q 58.015625 14.40625 51.78125 6.484375 \n", "Q 45.5625 -1.421875 35.59375 -1.421875 \n", "Q 29.59375 -1.421875 25.265625 0.953125 \n", "Q 20.953125 3.328125 18.109375 8.203125 \n", "z\n", "M 48.6875 27.296875 \n", "Q 48.6875 37.203125 44.609375 42.84375 \n", "Q 40.53125 48.484375 33.40625 48.484375 \n", "Q 26.265625 48.484375 22.1875 42.84375 \n", "Q 18.109375 37.203125 18.109375 27.296875 \n", "Q 18.109375 17.390625 22.1875 11.75 \n", "Q 26.265625 6.109375 33.40625 6.109375 \n", "Q 40.53125 6.109375 44.609375 11.75 \n", "Q 48.6875 17.390625 48.6875 27.296875 \n", "z\n", "\" id=\"DejaVuSans-112\"/>\n", " <path d=\"M 30.609375 48.390625 \n", "Q 23.390625 48.390625 19.1875 42.75 \n", "Q 14.984375 37.109375 14.984375 27.296875 \n", "Q 14.984375 17.484375 19.15625 11.84375 \n", "Q 23.34375 6.203125 30.609375 6.203125 \n", "Q 37.796875 6.203125 41.984375 11.859375 \n", "Q 46.1875 17.53125 46.1875 27.296875 \n", "Q 46.1875 37.015625 41.984375 42.703125 \n", "Q 37.796875 48.390625 30.609375 48.390625 \n", "z\n", "M 30.609375 56 \n", "Q 42.328125 56 49.015625 48.375 \n", "Q 55.71875 40.765625 55.71875 27.296875 \n", "Q 55.71875 13.875 49.015625 6.21875 \n", "Q 42.328125 -1.421875 30.609375 -1.421875 \n", "Q 18.84375 -1.421875 12.171875 6.21875 \n", "Q 5.515625 13.875 5.515625 27.296875 \n", "Q 5.515625 40.765625 12.171875 48.375 \n", "Q 18.84375 56 30.609375 56 \n", "z\n", "\" id=\"DejaVuSans-111\"/>\n", " <path d=\"M 44.28125 53.078125 \n", "L 44.28125 44.578125 \n", "Q 40.484375 46.53125 36.375 47.5 \n", "Q 32.28125 48.484375 27.875 48.484375 \n", "Q 21.1875 48.484375 17.84375 46.4375 \n", "Q 14.5 44.390625 14.5 40.28125 \n", "Q 14.5 37.15625 16.890625 35.375 \n", "Q 19.28125 33.59375 26.515625 31.984375 \n", "L 29.59375 31.296875 \n", "Q 39.15625 29.25 43.1875 25.515625 \n", "Q 47.21875 21.78125 47.21875 15.09375 \n", "Q 47.21875 7.46875 41.1875 3.015625 \n", "Q 35.15625 -1.421875 24.609375 -1.421875 \n", "Q 20.21875 -1.421875 15.453125 -0.5625 \n", "Q 10.6875 0.296875 5.421875 2 \n", "L 5.421875 11.28125 \n", "Q 10.40625 8.6875 15.234375 7.390625 \n", "Q 20.0625 6.109375 24.8125 6.109375 \n", "Q 31.15625 6.109375 34.5625 8.28125 \n", "Q 37.984375 10.453125 37.984375 14.40625 \n", "Q 37.984375 18.0625 35.515625 20.015625 \n", "Q 33.0625 21.96875 24.703125 23.78125 \n", "L 21.578125 24.515625 \n", "Q 13.234375 26.265625 9.515625 29.90625 \n", "Q 5.8125 33.546875 5.8125 39.890625 \n", "Q 5.8125 47.609375 11.28125 51.796875 \n", "Q 16.75 56 26.8125 56 \n", "Q 31.78125 56 36.171875 55.265625 \n", "Q 40.578125 54.546875 44.28125 53.078125 \n", "z\n", "\" id=\"DejaVuSans-115\"/>\n", " <path d=\"M 5.515625 54.6875 \n", "L 48.1875 54.6875 \n", "L 48.1875 46.484375 \n", "L 14.40625 7.171875 \n", "L 48.1875 7.171875 \n", "L 48.1875 0 \n", "L 4.296875 0 \n", "L 4.296875 8.203125 \n", "L 38.09375 47.515625 \n", "L 5.515625 47.515625 \n", "z\n", "\" id=\"DejaVuSans-122\"/>\n", " <path d=\"M 31 75.875 \n", "Q 24.46875 64.65625 21.28125 53.65625 \n", "Q 18.109375 42.671875 18.109375 31.390625 \n", "Q 18.109375 20.125 21.3125 9.0625 \n", "Q 24.515625 -2 31 -13.1875 \n", "L 23.1875 -13.1875 \n", "Q 15.875 -1.703125 12.234375 9.375 \n", "Q 8.59375 20.453125 8.59375 31.390625 \n", "Q 8.59375 42.28125 12.203125 53.3125 \n", "Q 15.828125 64.359375 23.1875 75.875 \n", "z\n", "\" id=\"DejaVuSans-40\"/>\n", " <path d=\"M 9.8125 72.90625 \n", "L 24.515625 72.90625 \n", "L 43.109375 23.296875 \n", "L 61.8125 72.90625 \n", "L 76.515625 72.90625 \n", "L 76.515625 0 \n", "L 66.890625 0 \n", "L 66.890625 64.015625 \n", "L 48.09375 14.015625 \n", "L 38.1875 14.015625 \n", "L 19.390625 64.015625 \n", "L 19.390625 0 \n", "L 9.8125 0 \n", "z\n", "\" id=\"DejaVuSans-77\"/>\n", " <path d=\"M 19.671875 34.8125 \n", "L 19.671875 8.109375 \n", "L 35.5 8.109375 \n", "Q 43.453125 8.109375 47.28125 11.40625 \n", "Q 51.125 14.703125 51.125 21.484375 \n", "Q 51.125 28.328125 47.28125 31.5625 \n", "Q 43.453125 34.8125 35.5 34.8125 \n", "z\n", "M 19.671875 64.796875 \n", "L 19.671875 42.828125 \n", "L 34.28125 42.828125 \n", "Q 41.5 42.828125 45.03125 45.53125 \n", "Q 48.578125 48.25 48.578125 53.8125 \n", "Q 48.578125 59.328125 45.03125 62.0625 \n", "Q 41.5 64.796875 34.28125 64.796875 \n", "z\n", "M 9.8125 72.90625 \n", "L 35.015625 72.90625 \n", "Q 46.296875 72.90625 52.390625 68.21875 \n", "Q 58.5 63.53125 58.5 54.890625 \n", "Q 58.5 48.1875 55.375 44.234375 \n", "Q 52.25 40.28125 46.1875 39.3125 \n", "Q 53.46875 37.75 57.5 32.78125 \n", "Q 61.53125 27.828125 61.53125 20.40625 \n", "Q 61.53125 10.640625 54.890625 5.3125 \n", "Q 48.25 0 35.984375 0 \n", "L 9.8125 0 \n", "z\n", "\" id=\"DejaVuSans-66\"/>\n", " <path d=\"M 8.015625 75.875 \n", "L 15.828125 75.875 \n", "Q 23.140625 64.359375 26.78125 53.3125 \n", "Q 30.421875 42.28125 30.421875 31.390625 \n", "Q 30.421875 20.453125 26.78125 9.375 \n", "Q 23.140625 -1.703125 15.828125 -13.1875 \n", "L 8.015625 -13.1875 \n", "Q 14.5 -2 17.703125 9.0625 \n", "Q 20.90625 20.125 20.90625 31.390625 \n", "Q 20.90625 42.671875 17.703125 53.65625 \n", "Q 14.5 64.65625 8.015625 75.875 \n", "z\n", "\" id=\"DejaVuSans-41\"/>\n", " </defs>\n", " <g transform=\"translate(14.798438 154.609062)rotate(-90)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-82\"/>\n", " <use x=\"69.419922\" xlink:href=\"#DejaVuSans-101\"/>\n", " <use x=\"130.943359\" xlink:href=\"#DejaVuSans-112\"/>\n", " <use x=\"194.419922\" xlink:href=\"#DejaVuSans-111\"/>\n", " <use x=\"255.601562\" xlink:href=\"#DejaVuSans-32\"/>\n", " <use x=\"287.388672\" xlink:href=\"#DejaVuSans-115\"/>\n", " <use x=\"339.488281\" xlink:href=\"#DejaVuSans-105\"/>\n", " <use x=\"367.271484\" xlink:href=\"#DejaVuSans-122\"/>\n", " <use x=\"419.761719\" xlink:href=\"#DejaVuSans-101\"/>\n", " <use x=\"481.285156\" xlink:href=\"#DejaVuSans-32\"/>\n", " <use x=\"513.072266\" xlink:href=\"#DejaVuSans-40\"/>\n", " <use x=\"552.085938\" xlink:href=\"#DejaVuSans-77\"/>\n", " <use x=\"638.365234\" xlink:href=\"#DejaVuSans-105\"/>\n", " <use x=\"666.148438\" xlink:href=\"#DejaVuSans-66\"/>\n", " <use x=\"734.751953\" xlink:href=\"#DejaVuSans-41\"/>\n", " </g>\n", " </g>\n", " </g>\n", " <g id=\"patch_43\">\n", " <path d=\"M 43.78125 224.64 \n", "L 43.78125 7.2 \n", "\" style=\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;\"/>\n", " </g>\n", " <g id=\"patch_44\">\n", " <path d=\"M 378.58125 224.64 \n", "L 378.58125 7.2 \n", "\" style=\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;\"/>\n", " </g>\n", " <g id=\"patch_45\">\n", " <path d=\"M 43.78125 224.64 \n", "L 378.58125 224.64 \n", "\" style=\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;\"/>\n", " </g>\n", " <g id=\"patch_46\">\n", " <path d=\"M 43.78125 7.2 \n", "L 378.58125 7.2 \n", "\" style=\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;\"/>\n", " </g>\n", " <g id=\"legend_1\">\n", " <g id=\"patch_47\">\n", " <path d=\"M 50.78125 73.9125 \n", "L 105.828125 73.9125 \n", "Q 107.828125 73.9125 107.828125 71.9125 \n", "L 107.828125 14.2 \n", "Q 107.828125 12.2 105.828125 12.2 \n", "L 50.78125 12.2 \n", "Q 48.78125 12.2 48.78125 14.2 \n", "L 48.78125 71.9125 \n", "Q 48.78125 73.9125 50.78125 73.9125 \n", "z\n", "\" style=\"fill:#ffffff;opacity:0.8;stroke:#cccccc;stroke-linejoin:miter;\"/>\n", " </g>\n", " <g id=\"patch_48\">\n", " <path d=\"M 52.78125 23.798437 \n", "L 72.78125 23.798437 \n", "L 72.78125 16.798437 \n", "L 52.78125 16.798437 \n", "z\n", "\" style=\"fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"text_19\">\n", " <!-- JPEG -->\n", " <defs>\n", " <path d=\"M 9.8125 72.90625 \n", "L 19.671875 72.90625 \n", "L 19.671875 5.078125 \n", "Q 19.671875 -8.109375 14.671875 -14.0625 \n", "Q 9.671875 -20.015625 -1.421875 -20.015625 \n", "L -5.171875 -20.015625 \n", "L -5.171875 -11.71875 \n", "L -2.09375 -11.71875 \n", "Q 4.4375 -11.71875 7.125 -8.046875 \n", "Q 9.8125 -4.390625 9.8125 5.078125 \n", "z\n", "\" id=\"DejaVuSans-74\"/>\n", " <path d=\"M 19.671875 64.796875 \n", "L 19.671875 37.40625 \n", "L 32.078125 37.40625 \n", "Q 38.96875 37.40625 42.71875 40.96875 \n", "Q 46.484375 44.53125 46.484375 51.125 \n", "Q 46.484375 57.671875 42.71875 61.234375 \n", "Q 38.96875 64.796875 32.078125 64.796875 \n", "z\n", "M 9.8125 72.90625 \n", "L 32.078125 72.90625 \n", "Q 44.34375 72.90625 50.609375 67.359375 \n", "Q 56.890625 61.8125 56.890625 51.125 \n", "Q 56.890625 40.328125 50.609375 34.8125 \n", "Q 44.34375 29.296875 32.078125 29.296875 \n", "L 19.671875 29.296875 \n", "L 19.671875 0 \n", "L 9.8125 0 \n", "z\n", "\" id=\"DejaVuSans-80\"/>\n", " <path d=\"M 9.8125 72.90625 \n", "L 55.90625 72.90625 \n", "L 55.90625 64.59375 \n", "L 19.671875 64.59375 \n", "L 19.671875 43.015625 \n", "L 54.390625 43.015625 \n", "L 54.390625 34.71875 \n", "L 19.671875 34.71875 \n", "L 19.671875 8.296875 \n", "L 56.78125 8.296875 \n", "L 56.78125 0 \n", "L 9.8125 0 \n", "z\n", "\" id=\"DejaVuSans-69\"/>\n", " <path d=\"M 59.515625 10.40625 \n", "L 59.515625 29.984375 \n", "L 43.40625 29.984375 \n", "L 43.40625 38.09375 \n", "L 69.28125 38.09375 \n", "L 69.28125 6.78125 \n", "Q 63.578125 2.734375 56.6875 0.65625 \n", "Q 49.8125 -1.421875 42 -1.421875 \n", "Q 24.90625 -1.421875 15.25 8.5625 \n", "Q 5.609375 18.5625 5.609375 36.375 \n", "Q 5.609375 54.25 15.25 64.234375 \n", "Q 24.90625 74.21875 42 74.21875 \n", "Q 49.125 74.21875 55.546875 72.453125 \n", "Q 61.96875 70.703125 67.390625 67.28125 \n", "L 67.390625 56.78125 \n", "Q 61.921875 61.421875 55.765625 63.765625 \n", "Q 49.609375 66.109375 42.828125 66.109375 \n", "Q 29.4375 66.109375 22.71875 58.640625 \n", "Q 16.015625 51.171875 16.015625 36.375 \n", "Q 16.015625 21.625 22.71875 14.15625 \n", "Q 29.4375 6.6875 42.828125 6.6875 \n", "Q 48.046875 6.6875 52.140625 7.59375 \n", "Q 56.25 8.5 59.515625 10.40625 \n", "z\n", "\" id=\"DejaVuSans-71\"/>\n", " </defs>\n", " <g transform=\"translate(80.78125 23.798437)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-74\"/>\n", " <use x=\"29.492188\" xlink:href=\"#DejaVuSans-80\"/>\n", " <use x=\"89.794922\" xlink:href=\"#DejaVuSans-69\"/>\n", " <use x=\"152.978516\" xlink:href=\"#DejaVuSans-71\"/>\n", " </g>\n", " </g>\n", " <g id=\"patch_49\">\n", " <path d=\"M 52.78125 38.476562 \n", "L 72.78125 38.476562 \n", "L 72.78125 31.476562 \n", "L 52.78125 31.476562 \n", "z\n", "\" style=\"fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"text_20\">\n", " <!-- BMP -->\n", " <g transform=\"translate(80.78125 38.476562)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-66\"/>\n", " <use x=\"68.603516\" xlink:href=\"#DejaVuSans-77\"/>\n", " <use x=\"154.882812\" xlink:href=\"#DejaVuSans-80\"/>\n", " </g>\n", " </g>\n", " <g id=\"patch_50\">\n", " <path d=\"M 52.78125 53.154687 \n", "L 72.78125 53.154687 \n", "L 72.78125 46.154687 \n", "L 52.78125 46.154687 \n", "z\n", "\" style=\"fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"text_21\">\n", " <!-- TIFF -->\n", " <defs>\n", " <path d=\"M -0.296875 72.90625 \n", "L 61.375 72.90625 \n", "L 61.375 64.59375 \n", "L 35.5 64.59375 \n", "L 35.5 0 \n", "L 25.59375 0 \n", "L 25.59375 64.59375 \n", "L -0.296875 64.59375 \n", "z\n", "\" id=\"DejaVuSans-84\"/>\n", " <path d=\"M 9.8125 72.90625 \n", "L 19.671875 72.90625 \n", "L 19.671875 0 \n", "L 9.8125 0 \n", "z\n", "\" id=\"DejaVuSans-73\"/>\n", " <path d=\"M 9.8125 72.90625 \n", "L 51.703125 72.90625 \n", "L 51.703125 64.59375 \n", "L 19.671875 64.59375 \n", "L 19.671875 43.109375 \n", "L 48.578125 43.109375 \n", "L 48.578125 34.8125 \n", "L 19.671875 34.8125 \n", "L 19.671875 0 \n", "L 9.8125 0 \n", "z\n", "\" id=\"DejaVuSans-70\"/>\n", " </defs>\n", " <g transform=\"translate(80.78125 53.154687)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-84\"/>\n", " <use x=\"61.083984\" xlink:href=\"#DejaVuSans-73\"/>\n", " <use x=\"90.576172\" xlink:href=\"#DejaVuSans-70\"/>\n", " <use x=\"148.095703\" xlink:href=\"#DejaVuSans-70\"/>\n", " </g>\n", " </g>\n", " <g id=\"patch_51\">\n", " <path d=\"M 52.78125 67.832812 \n", "L 72.78125 67.832812 \n", "L 72.78125 60.832812 \n", "L 52.78125 60.832812 \n", "z\n", "\" style=\"fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;\"/>\n", " </g>\n", " <g id=\"text_22\">\n", " <!-- PNG -->\n", " <defs>\n", " <path d=\"M 9.8125 72.90625 \n", "L 23.09375 72.90625 \n", "L 55.421875 11.921875 \n", "L 55.421875 72.90625 \n", "L 64.984375 72.90625 \n", "L 64.984375 0 \n", "L 51.703125 0 \n", "L 19.390625 60.984375 \n", "L 19.390625 0 \n", "L 9.8125 0 \n", "z\n", "\" id=\"DejaVuSans-78\"/>\n", " </defs>\n", " <g transform=\"translate(80.78125 67.832812)scale(0.1 -0.1)\">\n", " <use xlink:href=\"#DejaVuSans-80\"/>\n", " <use x=\"60.302734\" xlink:href=\"#DejaVuSans-78\"/>\n", " <use x=\"135.107422\" xlink:href=\"#DejaVuSans-71\"/>\n", " </g>\n", " </g>\n", " </g>\n", " </g>\n", " </g>\n", " <defs>\n", " <clipPath id=\"p70301a0bed\">\n", " <rect height=\"217.44\" width=\"334.8\" x=\"43.78125\" y=\"7.2\"/>\n", " </clipPath>\n", " </defs>\n", "</svg>\n" ], "text/plain": [ "<Figure size 432x288 with 1 Axes>" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%config InlineBackend.figure_formats = ['svg']\n", "\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "\n", "os.chdir(tdir)\n", "\n", "# Merge per-format test data into a single DataFrame without the first\n", "# first row, being the boring initial empty repo state.\n", "data = pd.concat(repo_sizes, axis=1).drop(range(1))\n", "\n", "mpl.rcParams['figure.figsize'] = (6, 4)\n", "ax = data.plot(kind = 'bar', colormap = 'coolwarm',\n", " grid = False, width = 0.8,\n", " edgecolor = 'white', linewidth = 2)\n", "ax.axes.set_xlabel('Checkin index')\n", "ax.axes.set_ylabel('Repo size (MiB)')\n", "plt.savefig('image-format-vs-repo-size.svg', transparent=True)\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" } }, "nbformat": 4, "nbformat_minor": 4 } |
Added www/image-format-vs-repo-size.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 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 | # Image Format vs Fossil Repo Size ## The Problem Fossil has a [delta compression][dc] feature which removes redundant information from a file relative to its parent on check-in.[^delta-prgs] That delta is then [zlib][zl]-compressed before being stored in the Fossil repository database file. Storing pre-compressed data files in a Fossil repository defeats both of these space-saving measures: 1. Binary data compression algorithms turn the file data into pseudorandom noise.[^prn] Typical data compression algorithms are not [hash functions][hf], where the goal is that a change to each bit in the input has a statistically even chance of changing every bit in the output, but because they do approach that pathological condition, pre-compressed data tends to defeat Fossil’s delta compression algorithm, there being so little correlation between two different outputs from the binary data compression algorithm. 2. An ideal lossless binary data compression algorithm cannot be applied more than once to make the data even smaller, since random noise is incompressible. The consequence for our purposes here is that pre-compressed data doesn’t benefit from Fossil’s zlib compression. You might then ask, what does it matter if the space savings comes from the application file format (e.g. JPEG, DOCX, Zip, etc.) or from Fossil itself? It really doesn’t, as far as point 2 above goes, but point 1 causes the Fossil repository to balloon out of proportion to the size of the input data change on each checkin. This article will illustrate that problem, quantify it, and give a solution to it. [dc]: ./delta_format.wiki [hf]: https://en.wikipedia.org/wiki/Hash_function [zl]: http://www.zlib.net/ ## <a id="formats"></a>Affected File Formats In this article’s core experiment, we use 2D image file formats, but this article’s advice also applies to many other file types. For just a few examples out of what must be thousands: * **Microsoft Office**: The [OOXML document format][oox] used from Office 2003 onward (`.docx`, `.xlsx`, `.pptx`, etc.) are Zip files containing an XML document file and several collateral files. * **Libre Office**: For the purposes of this article, its [OpenDocument Format][odf] is designed the same basic way as OOXML. * **Java**: A Java [`.jar` file][jcl] is a Zip file containing JVM `.class` files, manifest files, and more. * **Windows Installer:** An [`*.msi` file][wi] is a proprietary database format that contains, among other things, [Microsoft Cabinet][cab]-compressed files, which in turn may hold Windows executables, which [may themselves be compressed][exc]. * **SVG, PDF, TIFF, etc.**: Many file formats are available in both compressed and uncompressed forms. You should use the uncompressed form with Fossil wherever practical, as we will show below. [cab]: https://en.wikipedia.org/wiki/Cabinet_(file_format) [exc]: https://en.wikipedia.org/wiki/Executable_compression [jcl]: https://en.wikipedia.org/wiki/Java_(programming_language) [odf]: https://en.wikipedia.org/wiki/OpenDocument [oox]: https://en.wikipedia.org/wiki/Office_Open_XML [wi]: https://en.wikipedia.org/wiki/Windows_Installer ## <a id="demo"></a>Demonstration The companion `image-format-vs-repo-size.ipynb` file ([download][nbd], [preview][nbp]) is a [JupyterLab][jl] notebook implementing the following experiment: 1. Create a new minimum-size Fossil repository. Save this initial size. 2. Use [ImageMagick][im] via [Wand][wp] to generate a JPEG file of a particular size — currently 256 px² — filled with Gaussian noise to make data compression more difficult than with a solid-color image. 3. Check that image into the new Fossil repo, and remember that size. 4. Change a random pixel in the image to a random RGB value, save that image, check it in, and remember the new Fossil repo size. 5. Iterate on step 4 some number of times — currently 10 — and remember the Fossil repo size at each step. 6. Repeat the above steps for BMP, PNG, and TIFF.[^tiff-cmp] 7. Create a bar chart showing how the Fossil repository size changes with each checkin. We chose to use JupyterLab for this because it makes it easy for you to modify the notebook to try different things. Want to see how the results change with a different image size? Easy, change the `size` value in the second cell of the notebook. Want to try more image formats? You can put anything ImageMagick can recognize into the `formats` list. Want to find the break-even point for images like those in your own repository? Easily done with a small amount of code. [im]: https://www.imagemagick.org/ [jl]: https://jupyter.org/ [nbd]: ./image-format-vs-repo-size.ipynb [nbp]: https://nbviewer.jupyter.org/urls/fossil-scm.org/fossil/doc/trunk/www/image-format-vs-repo-size.ipynb [wp]: http://wand-py.org/ ## <a id="results"></a>Results Running the notebook gives a bar chart something like[^variance] this: ![results bar chart](./image-format-vs-repo-size.svg) There are a few key things we want to draw your attention to in that chart: * BMP and uncompressed TIFF are nearly identical in size for all checkins, and the repository growth rate is negligible past the first commit.[^size-jump] We owe this economy to Fossil’s delta compression feature: it is encoding each of those single-pixel changes in a very small amount of repository space. * The JPEG and PNG bars increase by large amounts on most checkins even though each checkin *also* encodes only a *single-pixel change*. * The size of the first checkin in the BMP and TIFF cases is roughly the same as that for the PNG case, because both PNG and Fossil use the zlib binary data compression algorithm. This shows that for repos where the image files are committed only once, there is virtually no penalty to using BMP or TIFF over PNG. The file sizes likely differ only because of differences in zlib settings between the cases. * Because JPEG’s lossy nature allows it to start smaller and have smaller size increases than PNG, the crossover point with BMP/TIFF isn’t until 7-9 checkins in typical runs of this [Monte Carlo experiment][mce]. Given a choice among these four file formats and a willingness to use lossy image compression, a rational tradeoff is to choose JPEG for repositories where each image will change fewer than that number of times. [mce]: https://en.wikipedia.org/wiki/Monte_Carlo_method ## <a id="makefile"></a>Automated Recompression Since programs that produce and consume binary-compressed data files often make it either difficult or impossible to work with the uncompressed form, we want an automated method for producing the uncompressed form to make Fossil happy while still having the compressed form to keep our content creation applications happy. This `Makefile` should[^makefile] do that for BMP, PNG, SVG, and XLSX files: .SUFFIXES: .bmp .png .svg .svgz .svgz.svg: gzip -dc < $< > $@ .svg.svgz: gzip -9c < $< > $@ .bmp.png: convert -quality 95 $< $@ .png.bmp: convert $< $@ SS_FILES := $(wildcard spreadsheet/*) all: $(SS_FILES) illus.svg image.bmp doc-big.pdf reconstitute: illus.svgz image.png ( cd spreadsheet ; zip -9 ../spreadsheet.xlsx) * ) qpdf doc-big.pdf doc-small.pdf $(SS_FILES): spreadsheet.xlsx unzip $@ -d $< doc-big.pdf: doc-small.pdf qpdf --stream-data=uncompress $@ $< This `Makefile` allows you to treat the compressed version as the process input, but to actually check in only the changes against the uncompressed version by typing “`make`†before “`fossil ci`â€. This is not actually an extra step in practice, since if you’ve got a `Makefile`-based project, you should be building — and testing — it before checking each change in anyway! Because this technique is based on dependency rules, only the necessary files are generated on each `make` command. You only have to run “`make reconstitute`†*once* after opening a fresh Fossil checkout to produce those compressed sources. After that, you work with the compressed files in your content creation programs. Your build system might include some kind of bootstrapping or auto-configuration step that you could attach this to, so that it doesn’t need to be run by hand. This `Makefile` illustrates two primary strategies: ### Input and Output File Formats Differ by Extension In the case of SVG and the bitmap image formats, the file name extension differs between the cases, so we can use `make` suffix rules to get the behavior we want. The top half of the `Makefile` just tells `make` how to map from `*.svg` to `*.svgz` and vice versa, and the same for `*.bmp` to/from `*.png`. ### Input and Output Use the Same Extension We don’t have that luxury for Excel and PDF files, each for a different reason: * **Excel:** Excel has no way to work with the unpacked Zip file contents at all, so we have to unpack it into a subdirectory, which is what we check into Fossil. On making a fresh Fossil checkout, we have to pack that subdirectory’s contents back up into an `*.xlsx` file with “`make reconstitute`†so we can edit it with Excel again. * **PDF:** All PDF readers can display an uncompressed PDF file, but many PDF-*producing* programs have no option for uncompressed output. Since the file name extension is the same either way, we treat the compressed PDF as the source to the process, yielding an automatically-uncompressed PDF for the benefit of Fossil. Unlike with the Excel case, there is no simple “file base name to directory name†mapping, so we just created the `-big` to `-small` name scheme here. [^delta-prgs]: This problem is not Fossil-specific. Several other programs also do delta compression, so they’ll also be affected by this problem: [rsync][rs], [Unison][us], [Git][git], etc. You should take this article’s advice when using all such programs, not just Fossil. When using file copying and synchronization programs *without* delta compression, on the other hand, it’s best to use the most highly-compressed file format you can tolerate, since they copy the whole file any time any bit of it changes. [^prn]: In fact, a good way to gauge the effectiveness of a given compression scheme is to run its output through the same sort of tests we use to gauge how “random†a given [PRNG][prng] is. Another way to look at it is that if there is a discernible pattern in the output of a compression scheme, that constitutes *information* (in [the technical sense of that word][ith]) that could be further compressed. [^tiff-cmp]: We're using *uncompressed* TIFF here, not [LZW][lzw]- or Zip-compressed TIFF, either of which would give similar results to PNG, which is always zlib-compressed. [^variance]: The raw data changes somewhat from one run to the next due to the use of random noise in the image to make the zlib/PNG compression more difficult, and the random pixel changes. Those test design choices make this a [Monte Carlo experiment][mce]. We’ve found that the overall character of the results doesn’t change from one run to the next. [^size-jump]: It’s not clear to me why there is a one-time jump in size for BMP and TIFF past the first commit. I suspect it is due to the SQLite indices being initialized for the first time. Page size inflation might have something to do with it as well, though we tried to control that by rebuilding the initial DB with a minimal page size. If you re-run the program often enough, you will sometimes see the BMP or TIFF bar jump higher than the other, again likely due to one of the repos crossing a page boundary. Another curious artifact in the data is that the BMP is slightly larger than for the TIFF. This goes against expectation because a low-tech format like BMP should have a small edge in this test because TIFF metadata includes the option for multiple timestamps, UUIDs, etc., which bloat the checkin size by creating many small deltas. [^makefile]: The `Makefile` above is not battle-tested. Please report bugs and needed extensions [on the forum][for]. [for]: https://fossil-scm.org/forum/forumpost/15e677f2c8 [git]: https://git-scm.com/ [ith]: https://en.wikipedia.org/wiki/Information_theory [lzw]: https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch [prng]: https://en.wikipedia.org/wiki/Pseudorandom_number_generator [rs]: https://rsync.samba.org/ [us]: http://www.cis.upenn.edu/~bcpierce/unison/ |
Added www/image-format-vs-repo-size.svg.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <?xml version="1.0" encoding="utf-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <!-- Created with matplotlib (https://matplotlib.org/) --> <svg height="288pt" version="1.1" viewBox="0 0 432 288" width="432pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <style type="text/css"> *{stroke-linecap:butt;stroke-linejoin:round;} </style> </defs> <g id="figure_1"> <g id="patch_1"> <path d="M 0 288 L 432 288 L 432 0 L 0 0 z " style="fill:none;"/> </g> <g id="axes_1"> <g id="patch_2"> <path d="M 54 252 L 388.8 252 L 388.8 34.56 L 54 34.56 z " style="fill:none;"/> </g> <g id="patch_3"> <path clip-path="url(#p9518871844)" d="M 62.126214 252 L 68.627184 252 L 68.627184 165.714286 L 62.126214 165.714286 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_4"> <path clip-path="url(#p9518871844)" d="M 94.631068 252 L 101.132039 252 L 101.132039 154.209524 L 94.631068 154.209524 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_5"> <path clip-path="url(#p9518871844)" d="M 127.135922 252 L 133.636893 252 L 133.636893 145.580952 L 127.135922 145.580952 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_6"> <path clip-path="url(#p9518871844)" d="M 159.640777 252 L 166.141748 252 L 166.141748 139.828571 L 159.640777 139.828571 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_7"> <path clip-path="url(#p9518871844)" d="M 192.145631 252 L 198.646602 252 L 198.646602 136.952381 L 192.145631 136.952381 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_8"> <path clip-path="url(#p9518871844)" d="M 224.650485 252 L 231.151456 252 L 231.151456 131.2 L 224.650485 131.2 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_9"> <path clip-path="url(#p9518871844)" d="M 257.15534 252 L 263.656311 252 L 263.656311 125.447619 L 257.15534 125.447619 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_10"> <path clip-path="url(#p9518871844)" d="M 289.660194 252 L 296.161165 252 L 296.161165 125.447619 L 289.660194 125.447619 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_11"> <path clip-path="url(#p9518871844)" d="M 322.165049 252 L 328.666019 252 L 328.666019 109.628571 L 322.165049 109.628571 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_12"> <path clip-path="url(#p9518871844)" d="M 354.669903 252 L 361.170874 252 L 361.170874 105.314286 L 354.669903 105.314286 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_13"> <path clip-path="url(#p9518871844)" d="M 68.627184 252 L 75.128155 252 L 75.128155 152.771429 L 68.627184 152.771429 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_14"> <path clip-path="url(#p9518871844)" d="M 101.132039 252 L 107.63301 252 L 107.63301 132.638095 L 101.132039 132.638095 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_15"> <path clip-path="url(#p9518871844)" d="M 133.636893 252 L 140.137864 252 L 140.137864 132.638095 L 133.636893 132.638095 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_16"> <path clip-path="url(#p9518871844)" d="M 166.141748 252 L 172.642718 252 L 172.642718 132.638095 L 166.141748 132.638095 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_17"> <path clip-path="url(#p9518871844)" d="M 198.646602 252 L 205.147573 252 L 205.147573 132.638095 L 198.646602 132.638095 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_18"> <path clip-path="url(#p9518871844)" d="M 231.151456 252 L 237.652427 252 L 237.652427 132.638095 L 231.151456 132.638095 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_19"> <path clip-path="url(#p9518871844)" d="M 263.656311 252 L 270.157282 252 L 270.157282 132.638095 L 263.656311 132.638095 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_20"> <path clip-path="url(#p9518871844)" d="M 296.161165 252 L 302.662136 252 L 302.662136 132.638095 L 296.161165 132.638095 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_21"> <path clip-path="url(#p9518871844)" d="M 328.666019 252 L 335.16699 252 L 335.16699 132.638095 L 328.666019 132.638095 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_22"> <path clip-path="url(#p9518871844)" d="M 361.170874 252 L 367.671845 252 L 367.671845 132.638095 L 361.170874 132.638095 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_23"> <path clip-path="url(#p9518871844)" d="M 75.128155 252 L 81.629126 252 L 81.629126 155.647619 L 75.128155 155.647619 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_24"> <path clip-path="url(#p9518871844)" d="M 107.63301 252 L 114.133981 252 L 114.133981 135.514286 L 107.63301 135.514286 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_25"> <path clip-path="url(#p9518871844)" d="M 140.137864 252 L 146.638835 252 L 146.638835 135.514286 L 140.137864 135.514286 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_26"> <path clip-path="url(#p9518871844)" d="M 172.642718 252 L 179.143689 252 L 179.143689 135.514286 L 172.642718 135.514286 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_27"> <path clip-path="url(#p9518871844)" d="M 205.147573 252 L 211.648544 252 L 211.648544 135.514286 L 205.147573 135.514286 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_28"> <path clip-path="url(#p9518871844)" d="M 237.652427 252 L 244.153398 252 L 244.153398 135.514286 L 237.652427 135.514286 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_29"> <path clip-path="url(#p9518871844)" d="M 270.157282 252 L 276.658252 252 L 276.658252 135.514286 L 270.157282 135.514286 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_30"> <path clip-path="url(#p9518871844)" d="M 302.662136 252 L 309.163107 252 L 309.163107 135.514286 L 302.662136 135.514286 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_31"> <path clip-path="url(#p9518871844)" d="M 335.16699 252 L 341.667961 252 L 341.667961 135.514286 L 335.16699 135.514286 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_32"> <path clip-path="url(#p9518871844)" d="M 367.671845 252 L 374.172816 252 L 374.172816 135.514286 L 367.671845 135.514286 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_33"> <path clip-path="url(#p9518871844)" d="M 81.629126 252 L 88.130097 252 L 88.130097 157.085714 L 81.629126 157.085714 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_34"> <path clip-path="url(#p9518871844)" d="M 114.133981 252 L 120.634951 252 L 120.634951 138.390476 L 114.133981 138.390476 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_35"> <path clip-path="url(#p9518871844)" d="M 146.638835 252 L 153.139806 252 L 153.139806 108.190476 L 146.638835 108.190476 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_36"> <path clip-path="url(#p9518871844)" d="M 179.143689 252 L 185.64466 252 L 185.64466 102.438095 L 179.143689 102.438095 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_37"> <path clip-path="url(#p9518871844)" d="M 211.648544 252 L 218.149515 252 L 218.149515 98.12381 L 211.648544 98.12381 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_38"> <path clip-path="url(#p9518871844)" d="M 244.153398 252 L 250.654369 252 L 250.654369 83.742857 L 244.153398 83.742857 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_39"> <path clip-path="url(#p9518871844)" d="M 276.658252 252 L 283.159223 252 L 283.159223 67.92381 L 276.658252 67.92381 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_40"> <path clip-path="url(#p9518871844)" d="M 309.163107 252 L 315.664078 252 L 315.664078 57.857143 L 309.163107 57.857143 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_41"> <path clip-path="url(#p9518871844)" d="M 341.667961 252 L 348.168932 252 L 348.168932 53.542857 L 341.667961 53.542857 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="patch_42"> <path clip-path="url(#p9518871844)" d="M 374.172816 252 L 380.673786 252 L 380.673786 44.914286 L 374.172816 44.914286 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="matplotlib.axis_1"> <g id="xtick_1"> <g id="line2d_1"> <defs> <path d="M 0 0 L 0 3.5 " id="m769b2cfcfb" style="stroke:#000000;stroke-width:0.8;"/> </defs> <g> <use style="stroke:#000000;stroke-width:0.8;" x="75.128155" xlink:href="#m769b2cfcfb" y="252"/> </g> </g> <g id="text_1"> <!-- 1 --> <defs> <path d="M 12.40625 8.296875 L 28.515625 8.296875 L 28.515625 63.921875 L 10.984375 60.40625 L 10.984375 69.390625 L 28.421875 72.90625 L 38.28125 72.90625 L 38.28125 8.296875 L 54.390625 8.296875 L 54.390625 0 L 12.40625 0 z " id="DejaVuSans-49"/> </defs> <g transform="translate(77.88753 265.3625)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-49"/> </g> </g> </g> <g id="xtick_2"> <g id="line2d_2"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="107.63301" xlink:href="#m769b2cfcfb" y="252"/> </g> </g> <g id="text_2"> <!-- 2 --> <defs> <path d="M 19.1875 8.296875 L 53.609375 8.296875 L 53.609375 0 L 7.328125 0 L 7.328125 8.296875 Q 12.9375 14.109375 22.625 23.890625 Q 32.328125 33.6875 34.8125 36.53125 Q 39.546875 41.84375 41.421875 45.53125 Q 43.3125 49.21875 43.3125 52.78125 Q 43.3125 58.59375 39.234375 62.25 Q 35.15625 65.921875 28.609375 65.921875 Q 23.96875 65.921875 18.8125 64.3125 Q 13.671875 62.703125 7.8125 59.421875 L 7.8125 69.390625 Q 13.765625 71.78125 18.9375 73 Q 24.125 74.21875 28.421875 74.21875 Q 39.75 74.21875 46.484375 68.546875 Q 53.21875 62.890625 53.21875 53.421875 Q 53.21875 48.921875 51.53125 44.890625 Q 49.859375 40.875 45.40625 35.40625 Q 44.1875 33.984375 37.640625 27.21875 Q 31.109375 20.453125 19.1875 8.296875 z " id="DejaVuSans-50"/> </defs> <g transform="translate(110.392385 265.3625)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-50"/> </g> </g> </g> <g id="xtick_3"> <g id="line2d_3"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="140.137864" xlink:href="#m769b2cfcfb" y="252"/> </g> </g> <g id="text_3"> <!-- 3 --> <defs> <path d="M 40.578125 39.3125 Q 47.65625 37.796875 51.625 33 Q 55.609375 28.21875 55.609375 21.1875 Q 55.609375 10.40625 48.1875 4.484375 Q 40.765625 -1.421875 27.09375 -1.421875 Q 22.515625 -1.421875 17.65625 -0.515625 Q 12.796875 0.390625 7.625 2.203125 L 7.625 11.71875 Q 11.71875 9.328125 16.59375 8.109375 Q 21.484375 6.890625 26.8125 6.890625 Q 36.078125 6.890625 40.9375 10.546875 Q 45.796875 14.203125 45.796875 21.1875 Q 45.796875 27.640625 41.28125 31.265625 Q 36.765625 34.90625 28.71875 34.90625 L 20.21875 34.90625 L 20.21875 43.015625 L 29.109375 43.015625 Q 36.375 43.015625 40.234375 45.921875 Q 44.09375 48.828125 44.09375 54.296875 Q 44.09375 59.90625 40.109375 62.90625 Q 36.140625 65.921875 28.71875 65.921875 Q 24.65625 65.921875 20.015625 65.03125 Q 15.375 64.15625 9.8125 62.3125 L 9.8125 71.09375 Q 15.4375 72.65625 20.34375 73.4375 Q 25.25 74.21875 29.59375 74.21875 Q 40.828125 74.21875 47.359375 69.109375 Q 53.90625 64.015625 53.90625 55.328125 Q 53.90625 49.265625 50.4375 45.09375 Q 46.96875 40.921875 40.578125 39.3125 z " id="DejaVuSans-51"/> </defs> <g transform="translate(142.897239 265.3625)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-51"/> </g> </g> </g> <g id="xtick_4"> <g id="line2d_4"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="172.642718" xlink:href="#m769b2cfcfb" y="252"/> </g> </g> <g id="text_4"> <!-- 4 --> <defs> <path d="M 37.796875 64.3125 L 12.890625 25.390625 L 37.796875 25.390625 z M 35.203125 72.90625 L 47.609375 72.90625 L 47.609375 25.390625 L 58.015625 25.390625 L 58.015625 17.1875 L 47.609375 17.1875 L 47.609375 0 L 37.796875 0 L 37.796875 17.1875 L 4.890625 17.1875 L 4.890625 26.703125 z " id="DejaVuSans-52"/> </defs> <g transform="translate(175.402093 265.3625)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-52"/> </g> </g> </g> <g id="xtick_5"> <g id="line2d_5"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="205.147573" xlink:href="#m769b2cfcfb" y="252"/> </g> </g> <g id="text_5"> <!-- 5 --> <defs> <path d="M 10.796875 72.90625 L 49.515625 72.90625 L 49.515625 64.59375 L 19.828125 64.59375 L 19.828125 46.734375 Q 21.96875 47.46875 24.109375 47.828125 Q 26.265625 48.1875 28.421875 48.1875 Q 40.625 48.1875 47.75 41.5 Q 54.890625 34.8125 54.890625 23.390625 Q 54.890625 11.625 47.5625 5.09375 Q 40.234375 -1.421875 26.90625 -1.421875 Q 22.3125 -1.421875 17.546875 -0.640625 Q 12.796875 0.140625 7.71875 1.703125 L 7.71875 11.625 Q 12.109375 9.234375 16.796875 8.0625 Q 21.484375 6.890625 26.703125 6.890625 Q 35.15625 6.890625 40.078125 11.328125 Q 45.015625 15.765625 45.015625 23.390625 Q 45.015625 31 40.078125 35.4375 Q 35.15625 39.890625 26.703125 39.890625 Q 22.75 39.890625 18.8125 39.015625 Q 14.890625 38.140625 10.796875 36.28125 z " id="DejaVuSans-53"/> </defs> <g transform="translate(207.906948 265.3625)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-53"/> </g> </g> </g> <g id="xtick_6"> <g id="line2d_6"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="237.652427" xlink:href="#m769b2cfcfb" y="252"/> </g> </g> <g id="text_6"> <!-- 6 --> <defs> <path d="M 33.015625 40.375 Q 26.375 40.375 22.484375 35.828125 Q 18.609375 31.296875 18.609375 23.390625 Q 18.609375 15.53125 22.484375 10.953125 Q 26.375 6.390625 33.015625 6.390625 Q 39.65625 6.390625 43.53125 10.953125 Q 47.40625 15.53125 47.40625 23.390625 Q 47.40625 31.296875 43.53125 35.828125 Q 39.65625 40.375 33.015625 40.375 z M 52.59375 71.296875 L 52.59375 62.3125 Q 48.875 64.0625 45.09375 64.984375 Q 41.3125 65.921875 37.59375 65.921875 Q 27.828125 65.921875 22.671875 59.328125 Q 17.53125 52.734375 16.796875 39.40625 Q 19.671875 43.65625 24.015625 45.921875 Q 28.375 48.1875 33.59375 48.1875 Q 44.578125 48.1875 50.953125 41.515625 Q 57.328125 34.859375 57.328125 23.390625 Q 57.328125 12.15625 50.6875 5.359375 Q 44.046875 -1.421875 33.015625 -1.421875 Q 20.359375 -1.421875 13.671875 8.265625 Q 6.984375 17.96875 6.984375 36.375 Q 6.984375 53.65625 15.1875 63.9375 Q 23.390625 74.21875 37.203125 74.21875 Q 40.921875 74.21875 44.703125 73.484375 Q 48.484375 72.75 52.59375 71.296875 z " id="DejaVuSans-54"/> </defs> <g transform="translate(240.411802 265.3625)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-54"/> </g> </g> </g> <g id="xtick_7"> <g id="line2d_7"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="270.157282" xlink:href="#m769b2cfcfb" y="252"/> </g> </g> <g id="text_7"> <!-- 7 --> <defs> <path d="M 8.203125 72.90625 L 55.078125 72.90625 L 55.078125 68.703125 L 28.609375 0 L 18.3125 0 L 43.21875 64.59375 L 8.203125 64.59375 z " id="DejaVuSans-55"/> </defs> <g transform="translate(272.916657 265.3625)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-55"/> </g> </g> </g> <g id="xtick_8"> <g id="line2d_8"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="302.662136" xlink:href="#m769b2cfcfb" y="252"/> </g> </g> <g id="text_8"> <!-- 8 --> <defs> <path d="M 31.78125 34.625 Q 24.75 34.625 20.71875 30.859375 Q 16.703125 27.09375 16.703125 20.515625 Q 16.703125 13.921875 20.71875 10.15625 Q 24.75 6.390625 31.78125 6.390625 Q 38.8125 6.390625 42.859375 10.171875 Q 46.921875 13.96875 46.921875 20.515625 Q 46.921875 27.09375 42.890625 30.859375 Q 38.875 34.625 31.78125 34.625 z M 21.921875 38.8125 Q 15.578125 40.375 12.03125 44.71875 Q 8.5 49.078125 8.5 55.328125 Q 8.5 64.0625 14.71875 69.140625 Q 20.953125 74.21875 31.78125 74.21875 Q 42.671875 74.21875 48.875 69.140625 Q 55.078125 64.0625 55.078125 55.328125 Q 55.078125 49.078125 51.53125 44.71875 Q 48 40.375 41.703125 38.8125 Q 48.828125 37.15625 52.796875 32.3125 Q 56.78125 27.484375 56.78125 20.515625 Q 56.78125 9.90625 50.3125 4.234375 Q 43.84375 -1.421875 31.78125 -1.421875 Q 19.734375 -1.421875 13.25 4.234375 Q 6.78125 9.90625 6.78125 20.515625 Q 6.78125 27.484375 10.78125 32.3125 Q 14.796875 37.15625 21.921875 38.8125 z M 18.3125 54.390625 Q 18.3125 48.734375 21.84375 45.5625 Q 25.390625 42.390625 31.78125 42.390625 Q 38.140625 42.390625 41.71875 45.5625 Q 45.3125 48.734375 45.3125 54.390625 Q 45.3125 60.0625 41.71875 63.234375 Q 38.140625 66.40625 31.78125 66.40625 Q 25.390625 66.40625 21.84375 63.234375 Q 18.3125 60.0625 18.3125 54.390625 z " id="DejaVuSans-56"/> </defs> <g transform="translate(305.421511 265.3625)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-56"/> </g> </g> </g> <g id="xtick_9"> <g id="line2d_9"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="335.16699" xlink:href="#m769b2cfcfb" y="252"/> </g> </g> <g id="text_9"> <!-- 9 --> <defs> <path d="M 10.984375 1.515625 L 10.984375 10.5 Q 14.703125 8.734375 18.5 7.8125 Q 22.3125 6.890625 25.984375 6.890625 Q 35.75 6.890625 40.890625 13.453125 Q 46.046875 20.015625 46.78125 33.40625 Q 43.953125 29.203125 39.59375 26.953125 Q 35.25 24.703125 29.984375 24.703125 Q 19.046875 24.703125 12.671875 31.3125 Q 6.296875 37.9375 6.296875 49.421875 Q 6.296875 60.640625 12.9375 67.421875 Q 19.578125 74.21875 30.609375 74.21875 Q 43.265625 74.21875 49.921875 64.515625 Q 56.59375 54.828125 56.59375 36.375 Q 56.59375 19.140625 48.40625 8.859375 Q 40.234375 -1.421875 26.421875 -1.421875 Q 22.703125 -1.421875 18.890625 -0.6875 Q 15.09375 0.046875 10.984375 1.515625 z M 30.609375 32.421875 Q 37.25 32.421875 41.125 36.953125 Q 45.015625 41.5 45.015625 49.421875 Q 45.015625 57.28125 41.125 61.84375 Q 37.25 66.40625 30.609375 66.40625 Q 23.96875 66.40625 20.09375 61.84375 Q 16.21875 57.28125 16.21875 49.421875 Q 16.21875 41.5 20.09375 36.953125 Q 23.96875 32.421875 30.609375 32.421875 z " id="DejaVuSans-57"/> </defs> <g transform="translate(337.926365 265.3625)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-57"/> </g> </g> </g> <g id="xtick_10"> <g id="line2d_10"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="367.671845" xlink:href="#m769b2cfcfb" y="252"/> </g> </g> <g id="text_10"> <!-- 10 --> <defs> <path d="M 31.78125 66.40625 Q 24.171875 66.40625 20.328125 58.90625 Q 16.5 51.421875 16.5 36.375 Q 16.5 21.390625 20.328125 13.890625 Q 24.171875 6.390625 31.78125 6.390625 Q 39.453125 6.390625 43.28125 13.890625 Q 47.125 21.390625 47.125 36.375 Q 47.125 51.421875 43.28125 58.90625 Q 39.453125 66.40625 31.78125 66.40625 z M 31.78125 74.21875 Q 44.046875 74.21875 50.515625 64.515625 Q 56.984375 54.828125 56.984375 36.375 Q 56.984375 17.96875 50.515625 8.265625 Q 44.046875 -1.421875 31.78125 -1.421875 Q 19.53125 -1.421875 13.0625 8.265625 Q 6.59375 17.96875 6.59375 36.375 Q 6.59375 54.828125 13.0625 64.515625 Q 19.53125 74.21875 31.78125 74.21875 z " id="DejaVuSans-48"/> </defs> <g transform="translate(370.43122 271.725)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-49"/> <use x="63.623047" xlink:href="#DejaVuSans-48"/> </g> </g> </g> <g id="text_11"> <!-- Checkin index --> <defs> <path d="M 64.40625 67.28125 L 64.40625 56.890625 Q 59.421875 61.53125 53.78125 63.8125 Q 48.140625 66.109375 41.796875 66.109375 Q 29.296875 66.109375 22.65625 58.46875 Q 16.015625 50.828125 16.015625 36.375 Q 16.015625 21.96875 22.65625 14.328125 Q 29.296875 6.6875 41.796875 6.6875 Q 48.140625 6.6875 53.78125 8.984375 Q 59.421875 11.28125 64.40625 15.921875 L 64.40625 5.609375 Q 59.234375 2.09375 53.4375 0.328125 Q 47.65625 -1.421875 41.21875 -1.421875 Q 24.65625 -1.421875 15.125 8.703125 Q 5.609375 18.84375 5.609375 36.375 Q 5.609375 53.953125 15.125 64.078125 Q 24.65625 74.21875 41.21875 74.21875 Q 47.75 74.21875 53.53125 72.484375 Q 59.328125 70.75 64.40625 67.28125 z " id="DejaVuSans-67"/> <path d="M 54.890625 33.015625 L 54.890625 0 L 45.90625 0 L 45.90625 32.71875 Q 45.90625 40.484375 42.875 44.328125 Q 39.84375 48.1875 33.796875 48.1875 Q 26.515625 48.1875 22.3125 43.546875 Q 18.109375 38.921875 18.109375 30.90625 L 18.109375 0 L 9.078125 0 L 9.078125 75.984375 L 18.109375 75.984375 L 18.109375 46.1875 Q 21.34375 51.125 25.703125 53.5625 Q 30.078125 56 35.796875 56 Q 45.21875 56 50.046875 50.171875 Q 54.890625 44.34375 54.890625 33.015625 z " id="DejaVuSans-104"/> <path d="M 56.203125 29.59375 L 56.203125 25.203125 L 14.890625 25.203125 Q 15.484375 15.921875 20.484375 11.0625 Q 25.484375 6.203125 34.421875 6.203125 Q 39.59375 6.203125 44.453125 7.46875 Q 49.3125 8.734375 54.109375 11.28125 L 54.109375 2.78125 Q 49.265625 0.734375 44.1875 -0.34375 Q 39.109375 -1.421875 33.890625 -1.421875 Q 20.796875 -1.421875 13.15625 6.1875 Q 5.515625 13.8125 5.515625 26.8125 Q 5.515625 40.234375 12.765625 48.109375 Q 20.015625 56 32.328125 56 Q 43.359375 56 49.78125 48.890625 Q 56.203125 41.796875 56.203125 29.59375 z M 47.21875 32.234375 Q 47.125 39.59375 43.09375 43.984375 Q 39.0625 48.390625 32.421875 48.390625 Q 24.90625 48.390625 20.390625 44.140625 Q 15.875 39.890625 15.1875 32.171875 z " id="DejaVuSans-101"/> <path d="M 48.78125 52.59375 L 48.78125 44.1875 Q 44.96875 46.296875 41.140625 47.34375 Q 37.3125 48.390625 33.40625 48.390625 Q 24.65625 48.390625 19.8125 42.84375 Q 14.984375 37.3125 14.984375 27.296875 Q 14.984375 17.28125 19.8125 11.734375 Q 24.65625 6.203125 33.40625 6.203125 Q 37.3125 6.203125 41.140625 7.25 Q 44.96875 8.296875 48.78125 10.40625 L 48.78125 2.09375 Q 45.015625 0.34375 40.984375 -0.53125 Q 36.96875 -1.421875 32.421875 -1.421875 Q 20.0625 -1.421875 12.78125 6.34375 Q 5.515625 14.109375 5.515625 27.296875 Q 5.515625 40.671875 12.859375 48.328125 Q 20.21875 56 33.015625 56 Q 37.15625 56 41.109375 55.140625 Q 45.0625 54.296875 48.78125 52.59375 z " id="DejaVuSans-99"/> <path d="M 9.078125 75.984375 L 18.109375 75.984375 L 18.109375 31.109375 L 44.921875 54.6875 L 56.390625 54.6875 L 27.390625 29.109375 L 57.625 0 L 45.90625 0 L 18.109375 26.703125 L 18.109375 0 L 9.078125 0 z " id="DejaVuSans-107"/> <path d="M 9.421875 54.6875 L 18.40625 54.6875 L 18.40625 0 L 9.421875 0 z M 9.421875 75.984375 L 18.40625 75.984375 L 18.40625 64.59375 L 9.421875 64.59375 z " id="DejaVuSans-105"/> <path d="M 54.890625 33.015625 L 54.890625 0 L 45.90625 0 L 45.90625 32.71875 Q 45.90625 40.484375 42.875 44.328125 Q 39.84375 48.1875 33.796875 48.1875 Q 26.515625 48.1875 22.3125 43.546875 Q 18.109375 38.921875 18.109375 30.90625 L 18.109375 0 L 9.078125 0 L 9.078125 54.6875 L 18.109375 54.6875 L 18.109375 46.1875 Q 21.34375 51.125 25.703125 53.5625 Q 30.078125 56 35.796875 56 Q 45.21875 56 50.046875 50.171875 Q 54.890625 44.34375 54.890625 33.015625 z " id="DejaVuSans-110"/> <path id="DejaVuSans-32"/> <path d="M 45.40625 46.390625 L 45.40625 75.984375 L 54.390625 75.984375 L 54.390625 0 L 45.40625 0 L 45.40625 8.203125 Q 42.578125 3.328125 38.25 0.953125 Q 33.9375 -1.421875 27.875 -1.421875 Q 17.96875 -1.421875 11.734375 6.484375 Q 5.515625 14.40625 5.515625 27.296875 Q 5.515625 40.1875 11.734375 48.09375 Q 17.96875 56 27.875 56 Q 33.9375 56 38.25 53.625 Q 42.578125 51.265625 45.40625 46.390625 z M 14.796875 27.296875 Q 14.796875 17.390625 18.875 11.75 Q 22.953125 6.109375 30.078125 6.109375 Q 37.203125 6.109375 41.296875 11.75 Q 45.40625 17.390625 45.40625 27.296875 Q 45.40625 37.203125 41.296875 42.84375 Q 37.203125 48.484375 30.078125 48.484375 Q 22.953125 48.484375 18.875 42.84375 Q 14.796875 37.203125 14.796875 27.296875 z " id="DejaVuSans-100"/> <path d="M 54.890625 54.6875 L 35.109375 28.078125 L 55.90625 0 L 45.3125 0 L 29.390625 21.484375 L 13.484375 0 L 2.875 0 L 24.125 28.609375 L 4.6875 54.6875 L 15.28125 54.6875 L 29.78125 35.203125 L 44.28125 54.6875 z " id="DejaVuSans-120"/> </defs> <g transform="translate(186.104688 283.323438)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-67"/> <use x="69.824219" xlink:href="#DejaVuSans-104"/> <use x="133.203125" xlink:href="#DejaVuSans-101"/> <use x="194.726562" xlink:href="#DejaVuSans-99"/> <use x="249.707031" xlink:href="#DejaVuSans-107"/> <use x="307.617188" xlink:href="#DejaVuSans-105"/> <use x="335.400391" xlink:href="#DejaVuSans-110"/> <use x="398.779297" xlink:href="#DejaVuSans-32"/> <use x="430.566406" xlink:href="#DejaVuSans-105"/> <use x="458.349609" xlink:href="#DejaVuSans-110"/> <use x="521.728516" xlink:href="#DejaVuSans-100"/> <use x="585.205078" xlink:href="#DejaVuSans-101"/> <use x="646.712891" xlink:href="#DejaVuSans-120"/> </g> </g> </g> <g id="matplotlib.axis_2"> <g id="ytick_1"> <g id="line2d_11"> <defs> <path d="M 0 0 L -3.5 0 " id="m80d71b6281" style="stroke:#000000;stroke-width:0.8;"/> </defs> <g> <use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#m80d71b6281" y="252"/> </g> </g> <g id="text_12"> <!-- 0.0 --> <defs> <path d="M 10.6875 12.40625 L 21 12.40625 L 21 0 L 10.6875 0 z " id="DejaVuSans-46"/> </defs> <g transform="translate(31.096875 255.799219)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-48"/> <use x="63.623047" xlink:href="#DejaVuSans-46"/> <use x="95.410156" xlink:href="#DejaVuSans-48"/> </g> </g> </g> <g id="ytick_2"> <g id="line2d_12"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#m80d71b6281" y="215.184762"/> </g> </g> <g id="text_13"> <!-- 0.2 --> <g transform="translate(31.096875 218.983981)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-48"/> <use x="63.623047" xlink:href="#DejaVuSans-46"/> <use x="95.410156" xlink:href="#DejaVuSans-50"/> </g> </g> </g> <g id="ytick_3"> <g id="line2d_13"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#m80d71b6281" y="178.369524"/> </g> </g> <g id="text_14"> <!-- 0.4 --> <g transform="translate(31.096875 182.168743)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-48"/> <use x="63.623047" xlink:href="#DejaVuSans-46"/> <use x="95.410156" xlink:href="#DejaVuSans-52"/> </g> </g> </g> <g id="ytick_4"> <g id="line2d_14"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#m80d71b6281" y="141.554286"/> </g> </g> <g id="text_15"> <!-- 0.6 --> <g transform="translate(31.096875 145.353504)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-48"/> <use x="63.623047" xlink:href="#DejaVuSans-46"/> <use x="95.410156" xlink:href="#DejaVuSans-54"/> </g> </g> </g> <g id="ytick_5"> <g id="line2d_15"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#m80d71b6281" y="104.739048"/> </g> </g> <g id="text_16"> <!-- 0.8 --> <g transform="translate(31.096875 108.538266)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-48"/> <use x="63.623047" xlink:href="#DejaVuSans-46"/> <use x="95.410156" xlink:href="#DejaVuSans-56"/> </g> </g> </g> <g id="ytick_6"> <g id="line2d_16"> <g> <use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#m80d71b6281" y="67.92381"/> </g> </g> <g id="text_17"> <!-- 1.0 --> <g transform="translate(31.096875 71.723028)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-49"/> <use x="63.623047" xlink:href="#DejaVuSans-46"/> <use x="95.410156" xlink:href="#DejaVuSans-48"/> </g> </g> </g> <g id="text_18"> <!-- Repo size (MiB) --> <defs> <path d="M 44.390625 34.1875 Q 47.5625 33.109375 50.5625 29.59375 Q 53.5625 26.078125 56.59375 19.921875 L 66.609375 0 L 56 0 L 46.6875 18.703125 Q 43.0625 26.03125 39.671875 28.421875 Q 36.28125 30.8125 30.421875 30.8125 L 19.671875 30.8125 L 19.671875 0 L 9.8125 0 L 9.8125 72.90625 L 32.078125 72.90625 Q 44.578125 72.90625 50.734375 67.671875 Q 56.890625 62.453125 56.890625 51.90625 Q 56.890625 45.015625 53.6875 40.46875 Q 50.484375 35.9375 44.390625 34.1875 z M 19.671875 64.796875 L 19.671875 38.921875 L 32.078125 38.921875 Q 39.203125 38.921875 42.84375 42.21875 Q 46.484375 45.515625 46.484375 51.90625 Q 46.484375 58.296875 42.84375 61.546875 Q 39.203125 64.796875 32.078125 64.796875 z " id="DejaVuSans-82"/> <path d="M 18.109375 8.203125 L 18.109375 -20.796875 L 9.078125 -20.796875 L 9.078125 54.6875 L 18.109375 54.6875 L 18.109375 46.390625 Q 20.953125 51.265625 25.265625 53.625 Q 29.59375 56 35.59375 56 Q 45.5625 56 51.78125 48.09375 Q 58.015625 40.1875 58.015625 27.296875 Q 58.015625 14.40625 51.78125 6.484375 Q 45.5625 -1.421875 35.59375 -1.421875 Q 29.59375 -1.421875 25.265625 0.953125 Q 20.953125 3.328125 18.109375 8.203125 z M 48.6875 27.296875 Q 48.6875 37.203125 44.609375 42.84375 Q 40.53125 48.484375 33.40625 48.484375 Q 26.265625 48.484375 22.1875 42.84375 Q 18.109375 37.203125 18.109375 27.296875 Q 18.109375 17.390625 22.1875 11.75 Q 26.265625 6.109375 33.40625 6.109375 Q 40.53125 6.109375 44.609375 11.75 Q 48.6875 17.390625 48.6875 27.296875 z " id="DejaVuSans-112"/> <path d="M 30.609375 48.390625 Q 23.390625 48.390625 19.1875 42.75 Q 14.984375 37.109375 14.984375 27.296875 Q 14.984375 17.484375 19.15625 11.84375 Q 23.34375 6.203125 30.609375 6.203125 Q 37.796875 6.203125 41.984375 11.859375 Q 46.1875 17.53125 46.1875 27.296875 Q 46.1875 37.015625 41.984375 42.703125 Q 37.796875 48.390625 30.609375 48.390625 z M 30.609375 56 Q 42.328125 56 49.015625 48.375 Q 55.71875 40.765625 55.71875 27.296875 Q 55.71875 13.875 49.015625 6.21875 Q 42.328125 -1.421875 30.609375 -1.421875 Q 18.84375 -1.421875 12.171875 6.21875 Q 5.515625 13.875 5.515625 27.296875 Q 5.515625 40.765625 12.171875 48.375 Q 18.84375 56 30.609375 56 z " id="DejaVuSans-111"/> <path d="M 44.28125 53.078125 L 44.28125 44.578125 Q 40.484375 46.53125 36.375 47.5 Q 32.28125 48.484375 27.875 48.484375 Q 21.1875 48.484375 17.84375 46.4375 Q 14.5 44.390625 14.5 40.28125 Q 14.5 37.15625 16.890625 35.375 Q 19.28125 33.59375 26.515625 31.984375 L 29.59375 31.296875 Q 39.15625 29.25 43.1875 25.515625 Q 47.21875 21.78125 47.21875 15.09375 Q 47.21875 7.46875 41.1875 3.015625 Q 35.15625 -1.421875 24.609375 -1.421875 Q 20.21875 -1.421875 15.453125 -0.5625 Q 10.6875 0.296875 5.421875 2 L 5.421875 11.28125 Q 10.40625 8.6875 15.234375 7.390625 Q 20.0625 6.109375 24.8125 6.109375 Q 31.15625 6.109375 34.5625 8.28125 Q 37.984375 10.453125 37.984375 14.40625 Q 37.984375 18.0625 35.515625 20.015625 Q 33.0625 21.96875 24.703125 23.78125 L 21.578125 24.515625 Q 13.234375 26.265625 9.515625 29.90625 Q 5.8125 33.546875 5.8125 39.890625 Q 5.8125 47.609375 11.28125 51.796875 Q 16.75 56 26.8125 56 Q 31.78125 56 36.171875 55.265625 Q 40.578125 54.546875 44.28125 53.078125 z " id="DejaVuSans-115"/> <path d="M 5.515625 54.6875 L 48.1875 54.6875 L 48.1875 46.484375 L 14.40625 7.171875 L 48.1875 7.171875 L 48.1875 0 L 4.296875 0 L 4.296875 8.203125 L 38.09375 47.515625 L 5.515625 47.515625 z " id="DejaVuSans-122"/> <path d="M 31 75.875 Q 24.46875 64.65625 21.28125 53.65625 Q 18.109375 42.671875 18.109375 31.390625 Q 18.109375 20.125 21.3125 9.0625 Q 24.515625 -2 31 -13.1875 L 23.1875 -13.1875 Q 15.875 -1.703125 12.234375 9.375 Q 8.59375 20.453125 8.59375 31.390625 Q 8.59375 42.28125 12.203125 53.3125 Q 15.828125 64.359375 23.1875 75.875 z " id="DejaVuSans-40"/> <path d="M 9.8125 72.90625 L 24.515625 72.90625 L 43.109375 23.296875 L 61.8125 72.90625 L 76.515625 72.90625 L 76.515625 0 L 66.890625 0 L 66.890625 64.015625 L 48.09375 14.015625 L 38.1875 14.015625 L 19.390625 64.015625 L 19.390625 0 L 9.8125 0 z " id="DejaVuSans-77"/> <path d="M 19.671875 34.8125 L 19.671875 8.109375 L 35.5 8.109375 Q 43.453125 8.109375 47.28125 11.40625 Q 51.125 14.703125 51.125 21.484375 Q 51.125 28.328125 47.28125 31.5625 Q 43.453125 34.8125 35.5 34.8125 z M 19.671875 64.796875 L 19.671875 42.828125 L 34.28125 42.828125 Q 41.5 42.828125 45.03125 45.53125 Q 48.578125 48.25 48.578125 53.8125 Q 48.578125 59.328125 45.03125 62.0625 Q 41.5 64.796875 34.28125 64.796875 z M 9.8125 72.90625 L 35.015625 72.90625 Q 46.296875 72.90625 52.390625 68.21875 Q 58.5 63.53125 58.5 54.890625 Q 58.5 48.1875 55.375 44.234375 Q 52.25 40.28125 46.1875 39.3125 Q 53.46875 37.75 57.5 32.78125 Q 61.53125 27.828125 61.53125 20.40625 Q 61.53125 10.640625 54.890625 5.3125 Q 48.25 0 35.984375 0 L 9.8125 0 z " id="DejaVuSans-66"/> <path d="M 8.015625 75.875 L 15.828125 75.875 Q 23.140625 64.359375 26.78125 53.3125 Q 30.421875 42.28125 30.421875 31.390625 Q 30.421875 20.453125 26.78125 9.375 Q 23.140625 -1.703125 15.828125 -13.1875 L 8.015625 -13.1875 Q 14.5 -2 17.703125 9.0625 Q 20.90625 20.125 20.90625 31.390625 Q 20.90625 42.671875 17.703125 53.65625 Q 14.5 64.65625 8.015625 75.875 z " id="DejaVuSans-41"/> </defs> <g transform="translate(25.017187 181.969063)rotate(-90)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-82"/> <use x="69.419922" xlink:href="#DejaVuSans-101"/> <use x="130.943359" xlink:href="#DejaVuSans-112"/> <use x="194.419922" xlink:href="#DejaVuSans-111"/> <use x="255.601562" xlink:href="#DejaVuSans-32"/> <use x="287.388672" xlink:href="#DejaVuSans-115"/> <use x="339.488281" xlink:href="#DejaVuSans-105"/> <use x="367.271484" xlink:href="#DejaVuSans-122"/> <use x="419.761719" xlink:href="#DejaVuSans-101"/> <use x="481.285156" xlink:href="#DejaVuSans-32"/> <use x="513.072266" xlink:href="#DejaVuSans-40"/> <use x="552.085938" xlink:href="#DejaVuSans-77"/> <use x="638.365234" xlink:href="#DejaVuSans-105"/> <use x="666.148438" xlink:href="#DejaVuSans-66"/> <use x="734.751953" xlink:href="#DejaVuSans-41"/> </g> </g> </g> <g id="patch_43"> <path d="M 54 252 L 54 34.56 " style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> </g> <g id="patch_44"> <path d="M 388.8 252 L 388.8 34.56 " style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> </g> <g id="patch_45"> <path d="M 54 252 L 388.8 252 " style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> </g> <g id="patch_46"> <path d="M 54 34.56 L 388.8 34.56 " style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/> </g> <g id="legend_1"> <g id="patch_47"> <path d="M 61 101.2725 L 116.046875 101.2725 Q 118.046875 101.2725 118.046875 99.2725 L 118.046875 41.56 Q 118.046875 39.56 116.046875 39.56 L 61 39.56 Q 59 39.56 59 41.56 L 59 99.2725 Q 59 101.2725 61 101.2725 z " style="fill:#ffffff;opacity:0.8;stroke:#cccccc;stroke-linejoin:miter;"/> </g> <g id="patch_48"> <path d="M 63 51.158437 L 83 51.158437 L 83 44.158437 L 63 44.158437 z " style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="text_19"> <!-- JPEG --> <defs> <path d="M 9.8125 72.90625 L 19.671875 72.90625 L 19.671875 5.078125 Q 19.671875 -8.109375 14.671875 -14.0625 Q 9.671875 -20.015625 -1.421875 -20.015625 L -5.171875 -20.015625 L -5.171875 -11.71875 L -2.09375 -11.71875 Q 4.4375 -11.71875 7.125 -8.046875 Q 9.8125 -4.390625 9.8125 5.078125 z " id="DejaVuSans-74"/> <path d="M 19.671875 64.796875 L 19.671875 37.40625 L 32.078125 37.40625 Q 38.96875 37.40625 42.71875 40.96875 Q 46.484375 44.53125 46.484375 51.125 Q 46.484375 57.671875 42.71875 61.234375 Q 38.96875 64.796875 32.078125 64.796875 z M 9.8125 72.90625 L 32.078125 72.90625 Q 44.34375 72.90625 50.609375 67.359375 Q 56.890625 61.8125 56.890625 51.125 Q 56.890625 40.328125 50.609375 34.8125 Q 44.34375 29.296875 32.078125 29.296875 L 19.671875 29.296875 L 19.671875 0 L 9.8125 0 z " id="DejaVuSans-80"/> <path d="M 9.8125 72.90625 L 55.90625 72.90625 L 55.90625 64.59375 L 19.671875 64.59375 L 19.671875 43.015625 L 54.390625 43.015625 L 54.390625 34.71875 L 19.671875 34.71875 L 19.671875 8.296875 L 56.78125 8.296875 L 56.78125 0 L 9.8125 0 z " id="DejaVuSans-69"/> <path d="M 59.515625 10.40625 L 59.515625 29.984375 L 43.40625 29.984375 L 43.40625 38.09375 L 69.28125 38.09375 L 69.28125 6.78125 Q 63.578125 2.734375 56.6875 0.65625 Q 49.8125 -1.421875 42 -1.421875 Q 24.90625 -1.421875 15.25 8.5625 Q 5.609375 18.5625 5.609375 36.375 Q 5.609375 54.25 15.25 64.234375 Q 24.90625 74.21875 42 74.21875 Q 49.125 74.21875 55.546875 72.453125 Q 61.96875 70.703125 67.390625 67.28125 L 67.390625 56.78125 Q 61.921875 61.421875 55.765625 63.765625 Q 49.609375 66.109375 42.828125 66.109375 Q 29.4375 66.109375 22.71875 58.640625 Q 16.015625 51.171875 16.015625 36.375 Q 16.015625 21.625 22.71875 14.15625 Q 29.4375 6.6875 42.828125 6.6875 Q 48.046875 6.6875 52.140625 7.59375 Q 56.25 8.5 59.515625 10.40625 z " id="DejaVuSans-71"/> </defs> <g transform="translate(91 51.158437)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-74"/> <use x="29.492188" xlink:href="#DejaVuSans-80"/> <use x="89.794922" xlink:href="#DejaVuSans-69"/> <use x="152.978516" xlink:href="#DejaVuSans-71"/> </g> </g> <g id="patch_49"> <path d="M 63 65.836562 L 83 65.836562 L 83 58.836562 L 63 58.836562 z " style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="text_20"> <!-- BMP --> <g transform="translate(91 65.836562)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-66"/> <use x="68.603516" xlink:href="#DejaVuSans-77"/> <use x="154.882812" xlink:href="#DejaVuSans-80"/> </g> </g> <g id="patch_50"> <path d="M 63 80.514687 L 83 80.514687 L 83 73.514687 L 63 73.514687 z " style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="text_21"> <!-- TIFF --> <defs> <path d="M -0.296875 72.90625 L 61.375 72.90625 L 61.375 64.59375 L 35.5 64.59375 L 35.5 0 L 25.59375 0 L 25.59375 64.59375 L -0.296875 64.59375 z " id="DejaVuSans-84"/> <path d="M 9.8125 72.90625 L 19.671875 72.90625 L 19.671875 0 L 9.8125 0 z " id="DejaVuSans-73"/> <path d="M 9.8125 72.90625 L 51.703125 72.90625 L 51.703125 64.59375 L 19.671875 64.59375 L 19.671875 43.109375 L 48.578125 43.109375 L 48.578125 34.8125 L 19.671875 34.8125 L 19.671875 0 L 9.8125 0 z " id="DejaVuSans-70"/> </defs> <g transform="translate(91 80.514687)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-84"/> <use x="61.083984" xlink:href="#DejaVuSans-73"/> <use x="90.576172" xlink:href="#DejaVuSans-70"/> <use x="148.095703" xlink:href="#DejaVuSans-70"/> </g> </g> <g id="patch_51"> <path d="M 63 95.192813 L 83 95.192813 L 83 88.192813 L 63 88.192813 z " style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/> </g> <g id="text_22"> <!-- PNG --> <defs> <path d="M 9.8125 72.90625 L 23.09375 72.90625 L 55.421875 11.921875 L 55.421875 72.90625 L 64.984375 72.90625 L 64.984375 0 L 51.703125 0 L 19.390625 60.984375 L 19.390625 0 L 9.8125 0 z " id="DejaVuSans-78"/> </defs> <g transform="translate(91 95.192813)scale(0.1 -0.1)"> <use xlink:href="#DejaVuSans-80"/> <use x="60.302734" xlink:href="#DejaVuSans-78"/> <use x="135.107422" xlink:href="#DejaVuSans-71"/> </g> </g> </g> </g> </g> <defs> <clipPath id="p9518871844"> <rect height="217.44" width="334.8" x="54" y="34.56"/> </clipPath> </defs> </svg> |
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 100 101 102 103 104 105 106 107 108 | <title>A Coherent Software Configuration Management System</title> <h3>What Is It?</h3> <div class="nomargins" style='float:right;border:2px solid #446979;padding:0 15px 10px 0;margin:0 50px 0 10px'> <ul> <li> [/uv/download.html | Download] <li> [./quickstart.wiki | Quick Start] <li> [./build.wiki | Install] <li> [https://fossil-scm.org/forum | Support/Forum ] <li> [./hints.wiki | Tips & Hints] <li> [./changes.wiki | Change Log] <li> [../COPYRIGHT-BSD2.txt | License] <li> [./userlinks.wiki | User Links] <li> [./hacker-howto.wiki | Hacker How-To] <li> [./fossil-v-git.wiki | Fossil vs. Git] <li> [./permutedindex.html | Doc Index] </ul> <p style="text-align:center"><img src="fossil3.gif" alt="Fossil logo"></p> </div> Fossil is a simple, high-reliability, distributed [https://en.wikipedia.org/wiki/Software_configuration_management | SCM] system with these advanced features: 1. <b>Project Management</b> — In addition to doing [./concepts.wiki | distributed version control] like Git and Mercurial, Fossil also supports [./bugtheory.wiki | bug tracking], [./wikitheory.wiki | wiki], [./forum.wiki | forum], [./alerts.md|email alerts], [./chat.md | chat], and [./event.wiki | technotes]. 2. <b>Built-in Web Interface</b> — Fossil has a built-in, [/skins | themeable], [./serverext.wiki | extensible], and intuitive [./webui.wiki | web interface] with a rich variety of information pages ([./webpage-ex.md|examples]) promoting situational awareness. <br><br> 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] or (in the case of the [/uv/download.html|download] page) [./unvers.wiki | unversioned files]. 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. 3. <b>All-in-one</b> — Fossil is a single self-contained, stand-alone executable. To install, simply download a [/uv/download.html | precompiled binary] for Linux, Mac, or Windows and put it on your $PATH. [./build.wiki | Easy-to-compile source code] is also available. 4. <b>Self-host Friendly</b> — Stand up a project website in minutes using [./server/ | a variety of techniques]. Fossil is CPU and memory efficient. Most projects can be hosted comfortably on a $5/month VPS or a Raspberry Pi. You can also set up an automatic [./mirrortogithub.md | GitHub mirror]. 5. <b>Simple Networking</b> — Fossil uses ordinary HTTPS (or SSH if you prefer) for network communications, so it works fine from behind firewalls and [./quickstart.wiki#proxy|proxies]. The protocol is [./stats.wiki | bandwidth efficient] to the point that Fossil can be used comfortably over dial-up, weak 3G, or airliner Wifi. 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> — [../COPYRIGHT-BSD2.txt|2-clause BSD license]. <hr> <h3>Latest Release: 2.23 ([/timeline?c=version-2.23|2023-11-01])</h3> * [/uv/download.html|Download] * [./changes.wiki#v2_23|Change Summary] * [/timeline?p=version-2.23&bt=version-2.22&y=ci|Check-ins in version 2.23] * [/timeline?df=version-2.23&y=ci|Check-ins derived from the 2.23 release] * [/timeline?t=release|Timeline of all past releases] <hr> <h3>Quick Start</h3> 1. [/uv/download.html|Download] or install using a package manager or [./build.wiki|compile from sources]. 2. <tt>fossil init</tt> <i>REPOSITORY-DIR/new-repository</i> 3. <tt>fossil open</tt> <i>REPOSITORY-DIR/new-repository</i> 4. <tt>fossil add</tt> <i>files-or-directories</i> 5. <tt>fossil commit -m</tt> "<i>commit message</i>" 6. <tt>fossil ui</tt> 7. Repeat steps 4, 5, and 6, in any order, as necessary. See the [./quickstart.wiki|Quick Start Guide] for more detail. |
Added www/inout.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 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 | <title>Import And Export</title> Fossil has the ability to import and export repositories from and to [http://git-scm.com/ | Git]. And since most other version control systems will also import/export from Git, that means that you can import/export a Fossil repository to most version control systems using Git as an intermediary. <h2>Git → Fossil</h2> To import a Git repository into Fossil, say something like: <pre> cd git-repo git fast-export --all | fossil import --git new-repo.fossil </pre> The 3rd argument to the "fossil import" command is the name of a new Fossil repository that is created to hold the Git content. The --git option is not actually required. The git-fast-export file format is currently the only VCS interchange format that Fossil understands. But future versions of Fossil might be enhanced to understand other VCS interchange formats, and so for compatibility, use of the --git option is recommended. <a id="fx_git"></a> Note that in new imports, Fossil defaults to using the email component of the Git <em>committer</em> (or <em>author</em> if <code>--use-author</code> is passed) to attribute check-ins in the imported repository. Alternatively, the [/help?cmd=import | <code>--attribute</code>] option can be passed to have all commits by a given committer attributed to a desired username. This will create and populate the new <code>fx_git</code> table in the repository database to maintain a record of correspondent usernames and email addresses that can be used in subsequent exports or incremental imports. <h3>Converting Repositories on Windows</h3> The above commands work best on proper POSIX systems like Linux, macOS, and the BSDs, where everything <tt>git</tt> sends is consumed by <tt>fossil</tt> as soon as it can manage, with both programs working concurrently. For some reason, the current version of PowerShell included with Windows chokes on the conversion when the in-flight repository size exceeds available memory. We do not know why it buffers the entire stream emitted by "<tt>git fast-export</tt>" before sending it along to Fossil, but we've seen the problem recur on multiple machines. While one workaround is to fall back to <tt>cmd.exe</tt> — which doesn't seem to be affected by this problem — we instead recommend using Mirosoft's own [https://learn.microsoft.com/en-us/windows/wsl/ | Windows Subsystem for Linux] or either of the two popular "Git for Windows" distributions based on MSYS2. They handle pipes the POSIX way, avoiding any dependency on the amount of data involved. <h2>Fossil → Git</h2> To convert a Fossil repository into a Git repository, run commands like this: <pre> git init new-repo cd new-repo fossil export --git ../repo.fossil | git fast-import </pre> In other words, create a new Git repository, then pipe the output from the "fossil export --git" command into the "git fast-import" command. Note that the "fossil export --git" command only exports the versioned files. Tickets and wiki and events are not exported, since Git does not understand 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. <h2>Mirror A Fossil Repository In Git</h2> Fossil version 2.9 and later supports a simple mechanism for doing a Git or [./mirrortogithub.md|GitHub mirror of a Fossil repository]. See that separate document for details. Fossil is self-hosting, but a [https://github.com/drhsqlite/fossil-mirror|GitHub mirror of Fossil] is available as a proof-of-concept. <h2>Bidirectional Synchronization</h2> Fossil also has the ability to synchronize with a Git repository via repeated imports and/or exports. To do this, it uses marks files to store a record of artifacts which are known by both Git and Fossil to exist at a given point in time. To illustrate, consider the example of a remote Fossil repository that a user wants to import into a local Git repository. First, the user would clone the remote repository and import it into a new Git repository: <pre> fossil clone /path/to/remote/repo.fossil repo.fossil mkdir repo cd repo fossil open ../repo.fossil mkdir ../repo.git cd ../repo.git git init . fossil export --git --export-marks ../repo/fossil.marks \ ../repo.fossil | git fast-import \ --export-marks=../repo/git.marks </pre> Once the import has completed, the user would need to <tt>git checkout trunk</tt>. At any point after this, new changes can be imported from the remote Fossil repository: <pre> cd ../repo fossil pull cd ../repo.git fossil export --git --import-marks ../repo/fossil.marks \ --export-marks ../repo/fossil.marks \ ../repo.fossil | git fast-import \ --import-marks=../repo/git.marks \ --export-marks=../repo/git.marks </pre> Changes in the Git repository can be exported to the Fossil repository and then pushed to the remote: <pre> git fast-export --import-marks=../repo/git.marks \ --export-marks=../repo/git.marks --all | fossil import --git \ --incremental --import-marks ../repo/fossil.marks \ --export-marks ../repo/fossil.marks ../repo.fossil cd ../repo fossil push </pre> |
Added www/interwiki.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 | # Interwiki Links Interwiki links are a short-hand notation for links that target external wikis or websites. For example, the following two hyperlinks mean the same thing (assuming an appropriate [intermap](#intermap) configuration): * [](wikipedia:MediaWiki#Interwiki_links) * [](https://en.wikipedia.org/wiki/MediaWiki#Interwiki_links) Another example: The Fossil Forum is hosted in a separate repository from the Fossil source code. This page is part of the source code repository. Interwiki links can be used to more easily refer to the forum repository: * [](forum:d5508c3bf44c6393df09c) * [](https://fossil-scm.org/forum/info/d5508c3bf44c6393df09c) ## Advantages Over Full URL Targets * Interwiki links are easier to write. There is less typing, and fewer opportunities to make mistakes. * Interwiki links are easier to read. With well-chosen intermap tags, the links are easier to understand. * Interwiki links continue to work after a domain change on the target. If the target of a link moves to a different domain, an interwiki link will continue to work, if the intermap is adjusted, but a hard-coded link will be permanently broken. * Interwiki links allow clones to use a different target domain from the original repository. ## Details Fossil supports interwiki links in both the [Fossil Wiki](/wiki_rules) and [Markdown](/md_rules) markup styles. An interwiki link consists of a tag followed by a colon and the link target: > <i>Tag</i><b>:</b><i>PageName</i> The Tag must consist of ASCII alphanumeric characters only - no punctuation or whitespace or characters greater than U+007A. The PageName is the link notation on the target wiki. Three different classes of PageNames are recognized by Fossil: 1. <b>Path Links</b> → the PageName begins with the "/" character or is an empty string. 2. <b>Hash Links</b> → the PageName is a hexadecimal number with at least four digits. 3. <b>Wiki Links</b> → An PageName that is not a Path or Hash. The Intermap defines a base URL for each Tag. Path links are appended directly to the URL contained in the Intermap. The Intermap can define additional text to put in between the base URL and the PageName for Hash and Wiki links, respectively. <a id="intermap"></a> ## Intermap The intermap defines a mapping from interwiki Tags to full URLs. The Intermap can be viewed and managed using the [fossil interwiki][iwiki] command or the [/intermap][imap] webpages. [iwiki]: /help?cmd=interwiki [imap]: /intermap The current intermap for a server is seen on the [/intermap][imap] page (which is read-only for non-Setup users) and at the bottom of the built-in [Fossil Wiki rules](/wiki_rules) and [Markdown rules](/md_rules) documentation pages. Each intermap entry stores, at a minimum, the base URL for the remote wiki. The intermap entry might also store additional path text that is used for Hash and Wiki links. If only the base URL is provided, then the intermap will only allow Path style interwiki links. The Hash and Wiki style interwiki links are only allowed if the necessary extensions for provided in the intermap. ## Disadvantages and Limitations * Configuration is required. The intermap must be set up correctly before interwiki links will work. This contrasts with ordinary links that just work without any configuration. Cloning a repository copies the intermap, but normal syncs to not keep the intermap in sync. Use the "[fossil config pull interwiki][fcfg]" command to synchronize the intermap. * The is no backlink tracking. For ordinary intrawiki links, Fossil keeps track of both the source and target, and when displaying targets it commonly shows links to that target. For example, if you mention a check-in as part of a comment of another check-in, that new check-in shows up in the "References" section of the target check-in. ([example](31af805348690958). In other words, Fossil tracks not just "_source→target_", but it also tracks "_target→source_". But backtracking do not work for interwiki links, since the Fossil running on the target has no way of scanning the source text and hence has no way of knowing that it is a target of a link from the source. [fcfg]: /help?cmd=config ## Intermap Storage Details The intermap is stored in the CONFIG table of the repository database, in entries with names of the form "<tt>interwiki:</tt><i>Tag</i>". The value for each such entry is a JSON string that defines the base URL and extensions for Hash and Wiki links. ## See Also 1. [](https://www.mediawiki.org/wiki/Manual:Interwiki) 2. [](https://duckduckgo.com/?q=interwiki+links&ia=web) |
Added www/javascript.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 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 | # Use of JavaScript in Fossil ## Philosophy & Policy The Fossil development project’s policy is to use JavaScript where it helps make its web UI better, but to offer graceful fallbacks wherever practical. The intent is that the UI be usable with JavaScript entirely disabled. In almost all places where Fossil uses JavaScript, it is an enhancement to provided functionality, and there is always another way to accomplish a given end without using JavaScript. This is not to say that Fossil’s fall-backs for such cases are always as elegant and functional as a no-JS purist might wish. That is simply because [the vast majority of web users leave JavaScript unconditionally enabled](#stats), and of the small minority of those that do not, a large chunk use some kind of [conditional blocking](#block) instead, rather than disable JavaScript entirely. Fossil’s active developers do not deviate from that norm enough that we have many no-JS purists among us, so the no-JS case doesn’t get as much attention as some might want. We do [accept code contributions][cg], and we are philosophically in favor of graceful fall-backs, so you are welcome to appoint yourself the position of no-JS czar for the Fossil project! Evil is in actions, not in objects: we do not believe JavaScript *can* be evil. It is an active technology, but the actions that matter here are those of writing the code and checking it into the Fossil project repository. None of the JavaScript code in Fossil is evil, a fact we enforce by being careful about who we give check-in rights on the repository to and by policing what code does get contributed. The Fossil project does not accept non-trivial outside contributions. We think it’s better to ask not whether Fossil requires JavaScript but whether Fossil uses JavaScript *well*, so that [you can decide](#block) to block or allow Fossil’s use of JavaScript. The Fossil developers want to see the project thrive, and we achieve that best by making it usable and friendly to a wider audience than the minority of static web app purists. Modern users generally expect a smoother experience than was available with 1990s style HTTP POST-and-response `<form>` based interaction. We also increase the set of potential Fossil developers if we do not restrict them to such antiquated methods. JavaScript is not perfect, but it's what we have, so we will use it where we find it advantageous. [cg]: ./contribute.wiki ## <a id="debate"></a>Arguments Against JavaScript & Our Rebuttals There are many common arguments against the use of JavaScript. Rather than rehash these same arguments on the [forum][ffor], we distill the common ones we’ve heard before and give our stock answers to them here: 1. “**It increases the size of the page download.**†The heaviest such pages served by Fossil only have about 15 kB of compressed JavaScript. (You have to go out of your way to get Fossil to serve uncompressed pages.) This is negligible, even over very slow data connections. If you are still somehow on a 56 kbit/sec analog telephone modem, this extra script code would download in a few seconds. Most JavaScript-based Fossil pages use less code than that. Atop that, Fossil sends HTTP headers to the browser that allow it to perform aggressive caching so that typical page loads will skip re-loading this content on subsequent loads. These features are currently optional: you must either set the new [`fossil server --jsmode bundle` option][fsrv] or the corresponding `jsmode` control line in your [`fossil cgi`][fcgi] script when setting up your [Fossil server][fshome]. That done, Fossil’s JavaScript files will load almost instantly from the browser’s cache after the initial page load, rather than be re-transferred over the network. Between the improved caching and the fact that it’s quicker to transfer a partial Ajax page load than reload the entire page, the aggregate cost of such pages is typically *lower* than the older methods based on HTTP POST with a full server round-trip. You can expect to recover the cost of the initial page load in 1-2 round-trips. If we were to double the amount of JavaScript code in Fossil, the payoff time would increase to 2-4 round-trips. 2. “**JavaScript is slow.**†It *was*, before September 2008. Google's introduction of [their V8 JavaScript engine][v8] taught the world that JavaScript need not be slow. This competitive pressure caused the other common JavaScript interpreters to either improve or be replaced by one of the engines that did improve to approach V8’s speed. Nowadays JavaScript is, as a rule, astoundingly fast. As the world continues to move more and more to web-based applications and services, JavaScript engine developers have ample motivation to keep their engines fast and competitive. Ajax partial page updates are faster than the no-JS alternative, a full HTTP POST round-trip to submit new data to the remote server, retrieve an entire new HTML document, and re-render the whole thing client-side. 3. <a id="3pjs"></a>“**Third-party JavaScript cannot be trusted.**†Fossil does not use any third-party JavaScript libraries, not even very common ones like jQuery. Every bit of JavaScript served by the stock version of Fossil was written specifically for the Fossil project and is stored [in its code repository][fsrc]. Therefore, if you want to hack on the JavaScript code served by Fossil and mechanisms like [skin editing][cskin] don’t suffice for your purposes, you can hack on the JavaScript in your local instance directly, just as you can hack on its C, SQL, and Tcl code. Fossil is free and open source software, under [a single license][2cbsd]. 4. <a id="snoop"></a>“**JavaScript and cookies are used to snoop on web users.**†There is no tracking or other snooping technology in Fossil other than that necessary for basic security, such as IP address logging on check-ins. (This is in part why we have no [comprehensive user statistics](#stats)!) Fossil attempts to set two cookies on all web clients: a login session cookie and a display preferences cookie. These cookies are restricted to the Fossil instance, so even this limited data cannot leak between Fossil instances or into other web sites. 5. “**JavaScript is fundamentally insecure.**†JavaScript is certainly sometimes used for nefarious ends, but if we wish to have more features in Fossil, the alternative is to add more code to the Fossil binary, [most likely in C][fslpl], a language implicated in [over 4× more security vulnerabilities][whmsl]. Therefore, does it not make sense to place approximately four times as much trust in Fossil’s JavaScript code as in its C code? The question is not whether JavaScript is itself evil, it is whether its *authors* are evil. *Every byte* of JavaScript code used within the Fossil UI is: * ...written by the Fossil developers, vetted by their peers. * ...[open source][flic] and [available][fsrc] to be inspected, audited, and changed by its users. * ...compiled directly into the `fossil` binary in a non-obfuscated form during the build process, so there are no third-party servers delivering mysterious, obfuscated JavaScript code blobs to the user. Local administrators can [modify the repository’s skin][cskin] to inject additional JavaScript code into pages served by their Fossil server. A typical case is to add a syntax highlighter like [Prism.js][pjs] or [highlightjs][hljs] to the local repository. At that point, your trust concern is not with Fossil’s use of JavaScript, but with your trust in that repository’s administrator. Fossil's [default content security policy][dcsp] (CSP) prohibits execution of JavaScript code which is delivered from anywhere but the Fossil server which delivers the page. A local administrator can change this CSP, but again this comes down to a matter of trust with the administrator, not with Fossil itself. 6. “**Cross-browser compatibility is poor.**†It most certainly was in the first decade or so of JavaScript’s lifetime, resulting in the creation of powerful libraries like jQuery to patch over the incompatibilities. Over time, the need for such libraries has dropped as browser vendors have fixed the incompatibilities. Cross-browser JavaScript compatibility issues which affect web developers are, by and large, a thing of the past. 7. “**Fossil UI works fine today without JavaScript. Why break it?**†While this is true today, and we have no philosophical objection to it remaining true, we do not intend to limit ourselves to only those features that can be created without JavaScript. The mere availability of alternatives is not a good justification for holding back on notable improvements when they're within easy reach. The no-JS case is a [minority position](#stats), so those that want Fossil to have no-JS alternatives and graceful fallbacks will need to get involved with the development if they want this state of affairs to continue. 8. <a id="stats"></a>“**A large number of users run without JavaScript enabled.**†That’s not what web audience measurements say: * [What percentage of browsers with javascript disabled?][s1] * [How many people are missing out on JavaScript enhancement?][s2] * [Just how many web users really disable cookies or JavaScript?][s3] Our sense of this data is that only about 0.2% of web users had JavaScript disabled while participating in these studies. The Fossil user community is not typical of the wider web, but if we were able to comprehensively survey our users, we’d expect to find an interesting dichotomy. Because Fossil is targeted at software developers, who in turn are more likely to be power-users, we’d expect to find Fossil users to be more in favor of some amount of JavaScript blocking than the average web user. Yet, we’d also expect to find that our user base has a disproportionately high number who run [powerful conditional blocking plugins](#block) in their browsers, rather than block JavaScript entirely. We suspect that between these two forces, the number of no-JS purists among Fossil’s user base is still a tiny minority. 9. <a id="block"></a>“**I block JavaScript entirely in my browser. That breaks Fossil.**†First, see our philosophy statements above. Briefly, we intend that there always be some other way to get any given result without using JavaScript, developer interest willing. But second, it doesn’t have to be all-or-nothing. We recommend that those interested in blocking problematic uses of JavaScript use tools like [NoScript][ns] or [uBlock Origin][ubo] to *selectively* block JavaScript so the rest of the web can use the technology productively, as it was intended. There are doubtless other useful tools of this sort. We recommend these two only from our limited experience, not out of any wish to exclude other tools. The primary difference between these two for our purposes is that NoScript lets you select scripts to run on a page on a case-by-case basis, whereas uBlock Origin delegates those choices to a group of motivated volunteers who maintain allow/block lists to control all of this; you can then override UBO’s stock rules as needed. 10. “**My browser doesn’t even *have* a JavaScript interpreter.**†The Fossil open source project has no full-time developers, and only a few of these part-timers are responsible for the bulk of the code in Fossil. If you want Fossil to support such niche use cases, then you will have to [get involved with its development][cg]: it’s *your* uncommon itch. 11. <a id="compat"></a>“**Fossil’s JavaScript code isn’t compatible with my browser.**†The Fossil project’s developers aim to remain compatible with the largest portions of the client-side browser base. We use only standards-defined JavaScript features which are known to work in the overwhelmingly vast majority of browsers going back approximately 5 years, at minimum, as documented by [Can I Use...?][ciu] We avoid use of features added to the language more recently or those which are still in flux in standards committees. We set this threshold based on the amount of time it typically takes for new standards to propagate through the installed base. As of this writing, this means we are only using features defined in [ECMAScript 2015][es2015], colloquially called “JavaScript 6.†That is a sufficiently rich standard that it more than suffices for our purposes, and it is [widely deployed][es6dep]. The biggest single outlier remaining is MSIE 11, and [even Microsoft is moving their own products off of it][ie11x]. [2cbsd]: https://fossil-scm.org/home/doc/trunk/COPYRIGHT-BSD2.txt [ciu]: https://caniuse.com/ [cskin]: ./customskin.md [dcsp]: ./defcsp.md [es2015]: https://ecma-international.org/ecma-262/6.0/ [es6dep]: https://caniuse.com/#feat=es6 [fcgi]: /help?cmd=cgi [ffor]: https://fossil-scm.org/forum/ [flic]: /doc/trunk/COPYRIGHT-BSD2.txt [fshome]: /doc/trunk/www/server/ [fslpl]: /doc/trunk/www/fossil-v-git.wiki#portable [fsrc]: https://fossil-scm.org/home/file/src [fsrv]: /help?cmd=server [hljs]: https://fossil-scm.org/forum/forumpost/9150bc22ca [ie11x]: https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666 [ns]: https://noscript.net/ [pjs]: https://fossil-scm.org/forum/forumpost/1198651c6d [s1]: https://blockmetry.com/blog/javascript-disabled [s2]: https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/ [s3]: https://w3techs.com/technologies/overview/client_side_language/all [ubo]: https://github.com/gorhill/uBlock/ [v8]: https://en.wikipedia.org/wiki/V8_(JavaScript_engine) [whmsl]: https://www.whitesourcesoftware.com/most-secure-programming-languages/ ---- ## <a id="uses"></a>Places Where Fossil’s Web UI Uses JavaScript This section documents the areas where Fossil currently uses JavaScript and what it does when these uses are blocked. It also gives common workarounds where necessary. ### <a id="timeline"></a>Timeline Graph Fossil’s [web timeline][wt] uses JavaScript to render the graph connecting the visible check-ins to each other, so you can visualize parent/child relationships, merge actions, etc. We’re not sure it’s even possible to render this in static HTML, even with the aid of SVG, due to the vagaries of web layout among browser engines, screen sizes, etc. Fossil also uses JavaScript to handle clicks on the graph nodes to allow diffs between versions, to display tooltips showing local context, etc. _Graceful Fallback:_ When JavaScript is disabled, this column of the timeline simply collapses to zero width. All of the information you can get from the timeline can be retrieved from Fossil in other ways not using JavaScript: the “`fossil timeline`†command, the “`fossil info`†command, by clicking around within the web UI, etc. _Potential Workaround:_ The timeline could be enhanced with `<noscript>` tags that replace the graph with a column of checkboxes that control what a series of form submit buttons do when clicked, replicating the current JavaScript-based features of the graph using client-server round-trips. For example, you could click two of those checkboxes and then a button labeled “Diff Selected†to replicate the current “click two nodes to diff them†feature. [wt]: https://fossil-scm.org/home/timeline ### <a id="wedit"></a>The New Wiki Editor The [new wiki editor][fwt] has many new features, a few of which are impossible to get without use of JavaScript. First, it allows in-browser previews without losing client-side editor state, such as where your cursor is. With the old editor, you had to re-locate the place you were last editing on each preview, which would reduce the incentive to use the preview function. In the new wiki editor, you just click the Preview tab to see how Fossil interprets your markup, then click back to the Editor tab to resume work with the prior context undisturbed. Second, it continually saves your document state in client-side storage in the background while you’re editing it so that if the browser closes without saving the changes back to the Fossil repository, you can resume editing from the stored copy without losing work. This feature is not so much about saving you from crashes of various sorts, since computers are so much more reliable these days. It is far more likely to save you from the features of mobile OSes like Android and iOS which aggressively shut down and restart apps to save on RAM. That OS design philosophy assumes that there is a way for the app to restore its prior state from persistent media when it’s restarted, giving the illusion that it was never shut down in the first place. This feature of Fossil’s new wiki editor provides that. With this change, we lost the old WYSIWYG wiki editor, available since Fossil version 1.24. It hadn’t been maintained for years, it was disabled by default, and no one stepped up to defend its existence when this new editor was created, replacing it. If someone rescues that feature, merging it in with the new editor, it will doubtless require JavaScript in order to react to editor button clicks like the “**B**†button, meaning “make \[selected\] text boldface.†There is no standard WYSIWYG editor component in browsers, doubtless because it’s relatively straightforward to create one using JavaScript. _Graceful Fallback:_ Fossil’s lack of a script-free wiki editor mode is not from lack of desire, but because the person who wrote the new wiki editor didn’t want to maintain three different editors. (New Ajaxy editor, old script-free HTML form based editor, and the old WYSIWYG JavaScript-based editor.) If someone wants to implement a `<noscript>` alternative to the new wiki editor, we will likely accept that [contribution][cg] as long as it doesn’t interfere with the new editor. (The same goes for adding a WYSIWYG mode to the new Ajaxy wiki editor.) _Workaround:_ You don’t have to use the browser-based wiki editor to maintain your repository’s wiki at all. Fossil’s [`wiki` command][fwc] lets you manipulate wiki documents from the command line. For example, consider this Vi based workflow: ```shell $ vi 'My Article.wiki' # begin work on new article ...write, write, write... :w # save changes to disk copy :!fossil wiki create 'My Article' '%' # current file (%) to new article ...write, write, write some more... :w # save again :!fossil wiki commit 'My Article' '%' # update article from disk :q # done writing for today ....days later... $ vi # work sans named file today :r !fossil wiki export 'My Article' - # pull article text into vi buffer ...write, write, write yet more... :w !fossil wiki commit - # vi buffer updates article ``` Extending this concept to other text editors is an exercise left to the reader. [fwc]: /help?cmd=wiki [fwt]: ./wikitheory.wiki ### <a id="fedit"></a>The File Editor Fossil’s [optional file editor feature][fedit] works much like [the new wiki editor](#wedit), only on files committed to the repository. The original designed purpose for this feature is to allow [embedded documentation][edoc] to be interactively edited in the same way that wiki articles can be. (Indeed, the associated `fileedit-glob` feature allows you to restrict the editor to working *only* on files that can be treated as embedded documentation.) This feature operates in much the same way as the new wiki editor, so most of what we said above applies. _Workaround:_ This feature is an alternative to Fossil’s traditional mode of file management: clone the repository, open it somewhere, edit a file locally, and commit the changes. _Graceful Fallback:_ There is no technical reason why someone could not write a `<noscript>` wrapped alternative to the current JavaScript based `/fileedit` implementation. It would have all of the same downsides as the old wiki editor: the users would lose their place on each save, they would have no local backup if something crashes, etc. Still, we are likely to accept such a [contribution][cg] as long as it doesn’t interfere with the new editor. [edoc]: /doc/trunk/www/embeddeddoc.wiki [fedit]: /doc/trunk/www/fileedit-page.md ### <a id="ln"></a>Line Numbering When viewing source files, Fossil offers to show line numbers in some cases. ([Example][mainc].) Toggling them on and off is currently handled in JavaScript, rather than forcing a page-reload via a button click. _Workaround:_ Manually edit the URL to give the “`ln`†query parameter per [the `/file` docs](/help?cmd=/file). _Potential Better Workaround:_ Someone sufficiently interested could [provide a patch][cg] to add a `<noscript>` wrapped HTML button that would reload the page with this parameter included/excluded to implement the toggle via a server round-trip. A related feature is Fossil’s JavaScript-based interactive method for selecting a range of lines by clicking the line numbers when they’re visible. JavaScript lets us copy the resulting URL to the clipboard to share your selection with others. _Workaround:_ These interactive features would be difficult and expensive (in terms of network I/O) to implement without JavaScript. A far simpler alternative is to manually edit the URL, per above. [mainc]: https://fossil-scm.org/home/artifact?ln&name=87d67e745 ### <a id="sxsdiff"></a>Side-by-Side Diff Mode The default “diff†view is a side-by-side mode. If either of the boxes of output — the “from†and “to†versions of the repo contents for that check-in — requires a horizontal scroll bar given the box content, font size, browser window width, etc., both boxes will usually end up needing to scroll since they should contain roughly similar content. Fossil therefore scrolls both boxes when you drag the scroll bar on one because if you want to examine part of a line scrolled out of the HTML element in one box, you probably want to examine the same point on that line in the other box. _Graceful Fallback:_ Manually scroll both boxes to sync their views. ### <a id="diffcontext"></a>Diff Context Loading Fossil’s diff views can dynamically load more lines of context around changed blocks. The UI controls for this feature are injected using JavaScript when the page initializes and make use of XHR requests to fetch data from the fossil instance. _Graceful Fallback:_ The UI controls for this feature do not appear when JS is unavailable, leaving the user with the "legacy" static diff view. ### <a id="sort"></a>Table Sorting On pages showing a data table, the column headers may be clickable to do a client-side sort of the data on that column. _Potential Workaround:_ This feature could be enhanced to do the sort on the server side using a page re-load. ### <a id="tree"></a>File Browser Tree View The [file browser’s tree view mode][tv] uses JavaScript to handle clicks on folders so they fold and unfold without needing to reload the entire page. _Graceful Fallback:_ When JavaScript is disabled, clicks on folders reload the page showing the folder contents instead. You then have to use the browser’s Back button to return to the higher folder level. [tv]: https://fossil-scm.org/home/dir?type=tree ### <a id="hash"></a>Version Hashes In several places where the Fossil web UI shows a check-in hash or similar, hovering over that check-in shows a tooltip with details about the type of artifact the hash refers to and allows you to click to copy the hash to the clipboard. _Graceful Fallback:_ When JavaScript is disabled, these tooltips simply don’t appear, but you can still select and copy the hash using your platform’s “copy selected text†feature. ### <a id="bots"></a>Anti-Bot Defenses Fossil has [anti-bot defenses][abd], and it has some JavaScript code that, if run, can drop some of these defenses if it decides a given page was loaded on behalf of a human, rather than a bot. _Graceful Fallback:_ You can use Fossil’s anonymous login feature to convince the remote Fossil instance that you are not a bot. Coupled with [the Fossil user capability system][caps], you can restore all functionality that Fossil’s anti-bot defenses deny to random web clients by default. [abd]: ./antibot.wiki [caps]: ./caps/ ### <a id="hbm"></a>Hamburger Menu Several of the stock skins (including the default) include a “hamburger menu†(☰) which uses JavaScript to show a simplified version of the Fossil UI site map using an animated-in dropdown. _Graceful Fallback:_ Clicking the hamburger menu button with JavaScript disabled will take you to the `/sitemap` page instead of showing a simplified version of that page’s content in a drop-down. _Workaround:_ You can remove this button by [editing the skin][cskin] header. ### <a id="clock"></a>Clock Some stock Fossil skins include JavaScript-based features such as the current time of day. The Xekri skin includes this in its header, for example. A clock feature requires JavaScript to get the time on initial page load and then to update it once a minute. You may observe that the server could provide the current time when generating the page, but the client and server may not be in the same time zone, and there is no reliably-provided information from the client that would let the server give the page load time in the client’s local time zone. The server could only tell you *its* local time at page request time, not the client’s time. That still wouldn’t be a “clock,†since without client-side JavaScript code running, that part of the page couldn’t update once a second. _Potential Graceful Fallback:_ You may consider showing the server’s page generation time rather than the client’s wall clock time in the local time zone to be a useful fallback for the current feature, so [a patch to do this][cg] may well be accepted. Since this is not a *necessary* Fossil feature, an interested user is unlikely to get the core developers to do this work for them. ### <a id="chat"></a>Chat The [chat feature](./chat.md) is deeply dependent on JavaScript. There is no obvious way to do this sort of thing without active client-side code of some sort. _Potential Workaround:_ It would not be especially difficult for someone sufficiently motivated to build a Fossil chat gateway, connecting to IRC, Jabber, etc. The messages are stored in the repository’s `chat` table with monotonically increasing IDs, so a poller that did something like SELECT xfrom, xmsg FROM chat WHERE msgid > 1234; …would pull the messages submitted since the last poll. Making the gateway bidirectional should be possible as well, as long as it properly uses SQLite transactions. ### <a id="brlist"></a>List of branches The [`/brlist`](/brlist) page uses JavaScript to enable selection of several branches for further study via `/timeline`. Client-side script interactively responds to checkboxes' events and constructs a special hyperlink in the submenu. Clicking this hyperlink loads a `/timeline` page that shows only these selected branches (and the related check-ins). _Potential Workaround:_ A user can manually construct an appropriate regular expession and put it into the "Tag Filter" entry of the `/timeline` page (in its advanced mode). ---- ## <a id="future"></a>Future Plans for JavaScript in Fossil As of mid-2020, the informal provisional plan is to increase Fossil UI's use of JavaScript considerably compared to its historically minimal uses. To that end, a framework of Fossil-centric APIs is being developed in conjunction with new features to consolidate Fossil's historical hodgepodge of JavaScript snippets into a coherent code base. When deciding which features to port to JavaScript, the rules of thumb for this ongoing effort are: - Pages which primarily display data (e.g. the timeline) will remain largely static HTML with graceful fallbacks for all places they do use JavaScript. Though JavaScript can be used effectively to power all sorts of wonderful data presentation, Fossil currently doesn't benefit greatly from doing so. We use JavaScript on these pages only to improve their usability, not to define their primary operations. - Pages which act as editors of some sort (e.g. the `/info` page) are prime candidates for getting the same treatment as the old wiki editor: reimplemented from the ground up in JavaScript using Ajax type techniques. Similarly, a JS-driven overhaul is planned for the forum’s post editor. These are guidelines, not immutable requirements. Our development direction is guided by our priorities: 1) Features the developers themselves want to have and/or work on. 2) Features end users request which catch the interest of one or more developers, provided the developer(s) in question are in a position to expend the effort. 3) Features end users and co-contributors can convince a developer into coding even when they really don't want to. In all of this, Fossil's project lead understandably has the final say-so in whether any given feature indeed gets merged into the mainline trunk. Development of any given feature, no matter how much effort was involved, does not guarantee its eventual inclusion into the public releases. |
Added www/json-api/_template.md.
> > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # JSON API: X ([⬑JSON API Index](index.md)) Jump to: * [Foo](#foo) * [Bar](#bar) * [Baz](#baz) --- <a id="foo"></a> # Foo <a id="bar"></a> # Bar <a id="baz"></a> # Baz # Footnotes |
Added www/json-api/api-artifact.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 | # JSON API: /artifact ([⬑JSON API Index](index.md)) Jump to: * [Checkin Artifacts (Commits)](#checkin) * [File Artifacts](#file) * [Wiki Artifacts](#wiki) --- <a id="checkin"></a> # Checkin Artifacts (Commits) Returns information about checkin artifacts (commits). **Status:** implemented 201110xx **Request:** `/json/artifact/COMMIT_HASH` **Required permissions:** "o" (was "h" prior to 20120408) **Response payload example: (CHANGED SIGNIFICANTLY ON 20120713)** ```json { "type":"checkin", "name":"18dd383e5e7684e", // as given on CLI "uuid":"18dd383e5e7684ecee327d3de7d3ff846069d1b2", "isLeaf":false, "user":"drh", "comment":"Merge wideAnnotateUser and jsonWarnings into trunk.", "timestamp":1330090810, "parents":[ // 1st entry is primary parent hash: "3a44f95f40a193739aaafc2409f155df43e74a6f", // Remaining entries are merged-in branch hashes: "86f6e675eb3f8761d70d8b82b052ce2b297fffd2",\ "dbf4ecf414881c9aad6f4f125dab9762589ef3d7"\ ], "tags":["trunk"], "files":[{ "name":"src/diff.c", // BLOB hashes, NOT commit hashes: "uuid":"78c74c3b37e266f8f7e570d5cf476854b7af9d76", "parent":"b1fa7e636cf4e7b6ed20bba2d2680397f80c096a", "state":"modified", "downloadPath":"/raw/src/diff.c?name=78c74c3b37e266f8f7e570d5cf476854b7af9d76" }, ...] } ``` The "parents" property lists the parent hashes of the checkin. The "parent" property of file entries refers to the parent hash of that file. In the case of a merge there may be essentially an arbitrary number. The first entry in the list is the "primary" parent. The primary parent is the parent which was not pulled in via a merge operation. The ordering of remaining entries is unspecified and may even change between calls. For example: if, from branch C, we merge in A and B and then commit, then in the artifact response for that commit the hash of branch C will be in the first (primary) position, with the hashes for branches A and B in the following entries (in an unspecified, and possibly unstable, order). Note that the "uuid" and "parent" properties of the "files" entries refer to raw blob hashes, not commit (a.k.a. check-in) hashes. See also [the UUID vs. Hash discussion][uvh]. <a id="file"></a> # File Artifacts Fetches information about file artifacts. **FIXME:** the content type guessing is currently very primitive, and may (but i haven't seen this) mis-diagnose some non-binary files as binary. Fossil doesn't yet have a mechanism for mime-type mappings. **Status:** implemented 20111020 **Required permissions:** "o" **Request:** `/json/artifact/FILE_HASH` **Request options:** - `format=(raw|html|none)` (default=none). If set, the contents of the artifact are included if they are text, else they are not (JSON does not do binary). The "html" flag runs it through the wiki parser. The results of doing so are unspecified for non-embedded-doc files. The "raw" format means to return the content as-is. "none" is the same as not specifying this flag, and elides the content from the response. - DEPRECATED (use format instead): `includeContent=bool` (=false) (CLI: `--content|-c`). If true, the full content of the artifact is returned for text-only artifacts (but not for non-text artifacts). As of 20120713 this option is only inspected if "format" is not specified. **Response payload example: (CHANGED SIGNIFICANTLY ON 20120713)** ```json { "type":"file", "name":"same name specified as FILE_HASH argument", "size": 12345, // in bytes, independent of format=... "parent": "hash of parent file blob. Not set for first generation.", "checkins":[{ "name":"src/json_detail.h", "timestamp":1319058803, "comment":"...", "user":"stephan", "checkin":"d2c1ae23a90b24f6ca1d7637193a59d5ecf3e680", "branch":"json", "state":"added|modified|removed" }, ...], /* The following "content" properties are only set if format=raw|html */ "content": "file contents", "contentSize": "size of content field, in bytes. Affected by the format option!", "contentType": "text/plain", /* currently always text/plain */ "contentFormat": "html|raw" } ``` The "checkins" array lists all checkins which include this file, and a file might have different names across different branches. The size and hash, however, are the same across all checkins for a given blob. <a id="wiki"></a> # Wiki Artifacts Returns information about wiki artifacts. **Status:** implemented 20111020, fixed to return the requested version (instead of the latest) on 20120302. **Request:** `/json/artifact/WIKI_HASH` **Required permissions:** "j" **Options:** - DEPRECATED (use format instead): `bool includeContent` (=false). If true then the raw content is returned with the wiki page, else no content is returned.\ CLI: `--includeContent|-c` - The `--format` option works as for [`/json/wiki/get`](api-wiki.md#get), and if set then it implies the `includeContent` option. **Response payload example:** Currently the same as [`/json/wiki/get`](api-wiki.md#get). [uvh]: ../hashes.md#uvh |
Added www/json-api/api-auth.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 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 | # JSON API: X ([⬑JSON API Index](index.md)) Jump to: * [Introduction](#intro) * [Capabilities (Access Rights)](#capabilities) * [Login](#login) * [Anonymous User Logins](#login-anonymous) * [Logout](#logout) * [Whoami](#whoami) --- <a id="intro"></a> # Introduction **FIXME:** Ross found a bug: [](/info/479aadb1d2645601) (sending the authToken via `POST.envelope` isn't working. It should be.) The authentication-related operations are described in this section (ordered alphabetically by operation name). The JSON API ties in to/piggybacks on fossil's existing cookie-based login mechanism, but we must also keep in mind that not all JSON-using clients support cookies. Cookie-capable clients can rely on cookies for authentication, whereas non-cookie-aware clients will need to keep track of the so-called "auth token" which is created via a login request, and pass it in with each request (either as a GET parameter or a property of the top-level POST request envelope) to prove to fossil that they are authorized to access the requested resources. For most intents and purposes, the "auth token" and the "login cookie" are the same thing (or serve the same purpose), and the auth token is in fact just the value part of the login cookie (which has a project-specific key). Note that fossil has two conventional user names which can show up in various response but do not refer to specific people: nobody and anonymous. The nobody user is anyone who is not logged in. The anonymous user is logged in but has no persistent user data (no associated user name, email address, or similar). Normally the guest (nobody) user has more access restrictions. The distinction between the two is largely historical - it is a mechanism to keep bots from following the multitudes of links generated by the HTML interface (especially the ZIP files), while allowing interested users to do so by logging in as the anonymous user (which bots have (or *had*, at the time) a harder time doing because the password is randomly generated and protected from brute-force attacks by hashing). In the JSON API, the distinction between anonymous and nobody is not expected to be as prominent as it is in the HTML interface because the reasons for the distinction don't apply in *quite* the same ways to the JSON interface. It is possible, however, that a given repo locks out guest access to, e.g. the wiki or tickets, while still allowing anonymous (logged in) access. <a id="capabilities"></a> # Capabilities (Access Rights) The "cap" request returns information about the so-called "capabilities" (access rights) of the currently logged in user. This command is basically the same as the "whoami" command, but returns capabilities in a more exanded form (same data, different representation) and does not include the authToken in the response. TODO: consider combining this and [whoami](#whoami) into a single whoami response. We don't need both. We also don't really need the permissionFlags member - the same info is already \[in a more cryptic form\] in the capabilities string. **Status:** implemented 201109xx. **Required privileges:** none **Request:** `/json/cap` In CLI mode, permissions are not used/honored, and this command will report that the caller has all permissions (which he effectively does). **Response payload example:** ```json { "userName":"json-demo", "capabilities":"hgjorxz", /* raw fossil permissions list */ "permissionFlags":{ /* Same info in a somewhat friendlier form */ "setup": false, "admin": false, "delete": false, "password": false, "query": false, "checkin": false, "checkout": true, "history": true, "clone": false, "readWiki": true, "createWiki": false, "appendWiki": false, "editWiki": false, "readTicket": true, "createTicket": false, "appendTicket": false, "editTicket": false, "attachFile": false, "createTicketReport": false, "readPrivate": false, "zip": true, "xferPrivate": false } } ``` **FIXME:** several new permissions have been added to fossil since this API was implemented. <a id="login"></a> # Login **Status:** implemented 20110915 (anonymous login 20110918) **Required privileges:** none **Request:** (see below for Anonymous user special case) - `/json/login?name=...&password=...` - `/json/login?n=...&p=...` (for symmetry with existing login mechanism) Or `POST.payload`: `{ "name": ..., "password": ...}` (POST is highly preferred because it keeps the password out of web server logs!) **Response payload example:** (structure changed 20111001) ```json { "authToken":"...", "name":"json-demo", "capabilities":"hgjorxz", "loginCookieName": "fossil-XXXXX" /*project-specific cookie name*/ /* TODO: add authTokenExpiry timestamp (cookie expiry time) */ } ``` We "should" be able to inherit fossil's `REMOTE_USER` handling without any special support, but that is untested so far. (If you happen to test this, please update this doc with (or otherwise report) your results!) The response *also* sets the conventional fossil login cookie (for clients which can make use of cookies), using fossil's existing mechanism for this. Further requests which require authentication must include the `authToken` (from the returned payload value) in the request (or it must be available via fossil's standard cookie) or access may (depending on the request) be denied. The `authToken` may optionally be set in the request envelope or as a GET parameter, and it *must* be given if the request requires restricted access to a resource. e.g. if reading tickets is disabled for the guest user then all non-guest users must send authentication info in their requests in order to be able to fetch ticket info. Cookie-aware clients should send the login-generated cookie with each request, in which case they do not need explicitly include the `authToken` in the JSON envelope/GET arguments. If submitted, the `authToken` is used, otherwise the cookie, if set, is used. Note that fossil uses a project-dependent cookie name in order to help thwart attacks, so there is no simple mapping of cookie *name* to auth token. That said, the cookie's *value* is also the auth token's value. > Special case: when accessing fossil over a local server instance which was started with the `--localauth` flag, the `authToken` is ignored (neither validated nor used for any form of authentication). <a id="login-anonymous"></a> ## Anonymous User Logins The Anonymous user requires special handling because he has a random password. First fetch the password and the so-called "captcha seed" via this request: `/json/anonymousPassword` It will return a payload in the form: ```json { "seed": a_32_bit_unsigned_integer, "password": "1234abcd" /*hexadecimal STRING*/ } ``` The "seed" and "password" values of the response payload must be set as the "anonymousSeed" and "password" fields (respectively) of the subsequent login request. The login request is identical to non-anonymous login except that extra "anonymousSeed" property is required. The password value *may* be time-limited, and *may* eventually become invalidated due to old age. This is unspecified. ***Potential***** (low-probability) bug regarding the seed value:** from what i hear, some unusual JSON platforms don't support full 32-bit precision. If absolutely necessary we could chop off a bit or two from the seed value (*if* it ever becomes a problem and if DRH blesses it). Or we could just make it a double. <a id="logout"></a> # Logout **Status:** implemented 20110916 **Required privileges:** none, but must be logged in **Request:** - `/json/logout?authToken=...token fetched by /login` Or: set the `authToken` property of the POST envelope (as opposed to the `POST.payload`) Or: fossil's normal cookie mechanism is the fallback for the auth token. **Response payload:** The same as the "whoami" response, containing the info for the "nobody" user. This request requires the authentication token, and subsequent logouts without an intervening login will fail with the "auth token not provided" error. In effect this request removes the login entry from the user account, making the token invalid for future requests. In HTTP mode, on success fossil's login cookie is unset by this call. <a id="whoami"></a> # Whoami This request fetches the current user's login name, capabilities, and auth token. This can be used to check whether a login is active when the client has not explicitly logged in (e.g. was logged in automatically via a pre-existing cookie). **Status:** implemented 20110922 **Required privileges:** none **Request:** `/json/whoami` **Response payload example:** ```json { "name": "nobody", "capabilities": "o", "authToken": "fossil auth token (only for logged-in users)" } ``` The reason authToken is included in the response is because it gives client-side JavaScript code a way of fetching/checking for the auth token at app startup. The token is normally sent as a cookie but parsing the cookies in the browser is tedious, and fossil has a project-dependent cookie name (which complicates parsing a bit). If client code digs the cookie out of the browser, the app still wouldn't know if the token is still valid, whereas whoami won't (or shouldn't!) return an expired auth token. If the request does not include authentication info (via the cookie, GET param, or request envelope) then the response will not contain the authToken property and the user's name will be "nobody". |
Added www/json-api/api-branch.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 | # JSON API: /branch ([⬑JSON API Index](index.md)) Jump to: * [Branch List](#list) * [Create Branch](#create) --- <a id="list"></a> # Branch List **Status:** implemented, at least in draft form, on 20110921. **Required privileges:** "o" **Request:** `/json/branch/list` **Response payload example:** ```json { "range":"closed", "current":"json", /* only when there is a local opened checkout */ "branches":[ "artifact_description", "bch", "ben-changes-report", "ben-safe-make", "ben-security", "ben-testing", … ] } ``` *Potential* TODO: add "tip" property which names the most recently modified branch? (How to get this?) HTTP GET/`POST.payload` options: - `range`: a string in the set ("open", "closed", "all"), case-sensitive, but only the first letter is actually evaluated. Default="open". Only branches with this state are returned. CLI mode options (same semantics as HTTP equivalents), must come last on the CLI: - `-r|--range all|closed|open` - `-a` (equivalent to `-r all`) - `-c` (equivalent to `-r closed`). Only one of `-a`/`-c` may be specified, and if both are specified then which one takes precedence is unspecified. <a id="create"></a> # Create Branch **Status:** implemented 20111002 **Required privileges:** "w" **Request:** `/json/branch/create` **Request options:** - `name=string` (REQUIRED) Name of new branch - `basis=string` (default=trunk) Name of parent branch to branch from. - `bgColor=string` (default=something ugly) In `#RRGGBB` form. (FIXME: change the default to use fossil's random bgcolor technique.) - `private=bool` (default=false) Determines whether the branch is private or not. **Response payload example:** ```json { "name":"my-branch", "basis":"my-other-branch", "uuid":"de8115db4ce388ed8d0af666ae7d90e1410be4ca", "isPrivate":true, "bgColor":"#ff0000" } ``` |
Added www/json-api/api-checkout.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 | # JSON API: /status ([⬑JSON API Index](index.md)) # Status of Local Checkout **Status:** implemented 20130223 **Required permissions:** n/a (local access only) **Request:** `/json/status` This command requires a local checkout and is analog to the "fossil status" command. **Request Options:** currently none. Payload example: ```json { "repository":"/home/stephan/cvs/fossil/fossil.fsl", "localRoot":"/home/stephan/cvs/fossil/fossil/", "checkout":{ "uuid":"b38bb4f9bd27d0347b62ecfac63c4b8f57b5c93b", "tags":["trunk"], "datetime":"2013-02-22 17:34:19 UTC", "timestamp":1361554459 }, /* "parent" info is currently missing. */ "files":[ {"name":"src/checkin.c", "status":"edited"} ...], "errorCount":0 /* see notes below */ } ``` Notes: - The `checkout.tags` property follows the framework-wide convention that the first tag in the list is the primary branch and any others are secondary. - `errorCount` is +1 for each missing file. Conflicts are not treated as errors because the CLI 'status' command does not treat them as such. - The `"status"` entry for each of the `"files"` entries will be either a single string containing a single description of the status change, or an array of strings if more than one change was made, e.g. `"edited"` and `"renamed"`. The status strings include:\ `deleted`, `new`, `notAFile`, `missing`, `updatedByMerge`, `updatedByIntegrate`, `addedBymerge`, `addedByIntegrate`, `conflict`, `edited`, `renamed` - File objects with a `"renamed"` status will contain a `"priorName"` property in addition to the `"name"` property reported in all cases. - TODO: Info for the parent version is currently missing. - TODO: "merged with" info for the checkout is currently missing. |
Added www/json-api/api-config.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 | # JSON API: /config ([⬑JSON API Index](index.md)) Jump to: * [Get Config](#get) * [Set Config](#set) --- <a id="get"></a> # Fetch Config **Status:** Implemented 20120217 **Required permissions:** "s" **Request:** `/json/config/get/Area[/Area2/...AreaN]` Where "Area" can be any combination of: *skin*, *ticket*, *project*, *all*, or *skin-backup* (which is not included in "all" by default). **Response payload example:** ```json { "ignore-glob":"*~", "project-description":"For testing Fossil's JSON API.", "project-name":"fossil-json-tests" } ``` <a id="set"></a> # Set/Modify Config Not implemented. |
Added www/json-api/api-diff.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 | # JSON API: /diff ([⬑JSON API Index](index.md)) # Diffs **Status:** implemented 20111007 **Required permissions:** "o" **Request:** `/json/diff[/version1[/version2]]` **Request options:** - `v1=string` Is the "from" version. It may optionally be the first positional parameter/path element after the command name. - `v2=string` Is the "to" version. It may optionally be the first positional parameter/path element after the v1 part. - `context=integer` (default=unspecified) Defines the number of context lines to display in the diff.\ CLI: `--context|-c` - `sbs=bool` (default=false) Generates "side-by-side" diffs, but their utility in JSON mode is questionable. - `html=bool` (default=false) causes the output to be marked up with HTML in the same manner as it is in the HTML interface. **Response payload example:** ```json { "from":"7a83a5cbd0424cefa2cdc001de60153aede530f5", "to":"96920e7c04746c55ceed6e24fc82879886cb8197", "diff":"@@ -1,7 +1,7 @@\\n-C factored\\\\sout..." } ``` TODOs: - Unlike the standard diff command, which apparently requires a commit hash, this one diffs individual file versions. If a commit hash is provided, a diff of the manifests is returned. (That should be considered a bug - we should return a combined diff in that case.) - If hashes from two different types of artifacts are given, results are unspecified. Garbage in, garbage out, and all that. - For file diffs, add the file name(s) to the response payload. |
Added www/json-api/api-dir.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 | # JSON API: /dir ([⬑JSON API Index](index.md)) # Directory Listing **Status:** implemented 20120316 **Required privileges:** "o". Was "h" prior to 20120720, but the HTML version of /dir changed permissions and this API was modified to match it. **Request:** `/json/dir` Options: - `checkin=commit` (use "tip" for the latest). If checkin is not set then all files from all versions of the tree are returned (but only once per file - not with complete version info for each file in all branches).\ CLI: `--checkin|-ci CHECKIN` - `name=subdirectory` name. To fetch the root directory, don't pass this option, or use an empty value or "/".\ CLI: use `--name|-n NAME` or pass it as the first argument after the `dir` subcommand. **Response payload example:** ```json { "name":"/", /* dir name */ "uuid":"ac6366218035ed62254c8d458f30801273e5d4fc", "checkin":"tip", "entries":[{ "name": "ajax", /* file name/dir name fragment */ "isDir": true, /* only set for directories */ /* The following properties are ONLY set if the 'checkin' parameter is provided. */ "uuid": "..." /*only for files, not dirs*/, "size": number, "timestamp": number },...] } ``` The checkin property is only set if the request includes it. The entry-specific uuid and size properties (e.g. `entries[0].uuid`) are only set if the checkin request property is set and they refer to the latest version of that file for the given checkin. The `isDir` property is only set on directory entries. This command does not recurse into subdirectories, though it "should be" simple enough to add the option to do so. |
Added www/json-api/api-finfo.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 | # JSON API: /finfo ([⬑JSON API Index](index.md)) # File Information **Status:** implemented 2012-something, but output structure is likely to change. **Required privileges:** "o" **Request:** `/json/finfo?name=path/to/file` Options: - `name=string`. Required. Use the absolute name of the file, including any directory parts, and without a leading slash. e.g. `"path/to/my.c"`.\ CLI mode: `--name` or positional argument. - `checkin=string`. Only return info related to this specific checkin, as opposed to listing all checkins. If set, neither "before" nor "after" have any effect.\ CLI mode: `--checkin|-ci` - `before=DATETIME` only lists checkins from on or before this time.\ CLI mode: `--before|-b` - `after=DATETIME` only lists checkins from on or before this time. Using this option swaps the sort order, to provide reasonable behaviour in conjunction with the limit option.\ Only one of "before" and "after" may be specified, and if both are specified then which one takes precedence is unspecified.\ CLI mode: `--after|-a` - `limit=integer` limits the output to (at most) the given number of associated checkins.\ CLI mode: `--limit|-n` **Response payload example (very likely to change!):** ```json { "name":"ajax/i-test/rhino-shell.js", "checkins":[{ "checkin":"6b7ddfefbfb871f793378d8f276fe829106ca49b", "uuid":"2b735676d55e3d06d670ffbc643e5d3f748b95ea", "timestamp":1329514170, "user":"viriketo", "comment":"<...snip...>", "size":6293, "state":"added|modified|removed" },…] } ``` **FIXME:** there is a semantic discrepancy between `/json/artifact`'s `payload.checkins[N].uuid` and this command's. |
Added www/json-api/api-misc.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 | # JSON API: Misc. APIs ([⬑JSON API Index](index.md)) Some operations simply don't fit into a specific category (well, none except "misc")... Jump to: * [Global State ("g")](#g) * [Rebuild Repository](#rebuild) * [Result Code Descriptions](#result-codes) --- <a id="g"></a> # Global State ("g") Fossil's internal state is maintained in a global object called "g". And thus this command is named "g"... **Status:** implemented 20111009 **Required permissions:** "a" or "s" **Request:** `/json/g` **Response payload example:** this is a debugging-only request, and has no stable response payload structure. The result object contains all kinds of details gleaned from the fossil environment. Easter egg: this output can be added to ANY response by passing the `debugFossilG` boolean in the POST envelope or GET parameters, or as the `--json-debug-g` flag in CLI mode. This requires admin or setup privileges, though, and it is silently ignored for other users. <a id="rebuild"></a> # Rebuild Repository This request does the same as the "fossil rebuild" command, rebuilding the repo-internal structure. This is often required after upgrading the fossil binary on a system. There "are very probably" cases where calling this over HTTP will not work (e.g. if the user table has changed enough that the access rights cannot be validated without a rebuild, i.e. a chicken/egg scenario). Another consideration is that this request can take a long time to run - rebuilding the fossil repo on my laptop takes about 21 seconds, which is likely longer than the timeout set on an XHR request, meaning that the rebuild transaction will fail. It can safely be run in CLI mode, where timeouts are not normally a concern. As a preliminary benchmark: rebuilding the fossil repo (as of late 2011) takes just over 21 seconds on my 32-bit 1.6GHz netbook. That said, most repos are much smaller and rebuild in under a few seconds. **Status:** implemented 20110929 **Required privileges:** "a" **Request:** `/json/rebuild` Requires admin access rights. **Response payload:** none (response envelope `resultCode` reports failure). Potential TODO: return the amount of time it took to rebuild. <a id="result-codes"></a> # Result Code Descriptions This request returns the full list of result codes documented for Fossil's JSON API. Each result code is returned as an object containing metadata about the result code. **Status:** implemented 20111006 **Required permissions:** none **Request:** `/json/resultCodes` **Response payload example:** ```json [{ "resultCode":"FOSSIL-1000", "cSymbol":"FSL_JSON_E_GENERIC", "number":1000, "description":"Generic error" }, … many more objects with the same structure … ] ``` |
Added www/json-api/api-query.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 | # JSON API: /query ([⬑JSON API Index](index.md)) # SQL Query **Status:** implemented 20111008 **Required privileges:** "a" or "s" **Request:** `/json/query` Potential FIXME: restrict this to queries which return results, as opposed to those which may modify data. Options: - `sql=string` The SQL code to run. It is expected that it be a SELECT statement, but that is not enforced. This parameter may be set as a POST.payload property, as the POST.payload itself, GET, or as a positional parameter coming after the command name (CLI and HTTP modes, though the escaping would be unsightly in HTTP mode). - `format=string` (default="o"). "o" specifies that each result row should be in the form of key/value pairs (o=object). "a" means each row should be an array of values. **Example request:** POST to: `/json/query` ```json { "authToken": "...", "payload": { "sql": "SELECT * FROM reportfmt", "format": "o" } } ``` **Response payload example:** (assuming the above example) ``` { "columns":[ "rn", "owner", "title", "mtime", "cols", "sqlcode" ], "rows":[ { "rn":1, "owner":"drh", "title":"All Tickets", "mtime":1303870798, }, … ] } ``` The column names are provided in a separate field is because their order is guaranteed to match the order of the query columns, whereas object key/value pairs might get reordered (typically sorted by key) when travelling through different JSON implementations. In this manner, clients can e.g. be sure to render the columns in the proper (query-specified) order. When in "array" mode the "rows" results will be an array of arrays. For example, the above "rows" property would instead look like: `[ [1, "drh", "All Tickets", 1303870798, … ], … ]` Note the column *names* are never *guaranteed* to be exactly as they appear in the SQL *unless* they are qualified with an AS, e.g. `SELECT foo AS foo...`. When generating reports which need fixed column names, it is highly recommended to use an AS qualifier for every column, even if they use the same name as the column. This is the only way to guaranty that the result column names will be stable. (FYI: that behaviour comes from sqlite3, not the JSON bits, and this behaviour *has* been known to change between sqlite3 versions (so this is not just an idle threat of *potential* future incompatibility).) |
Added www/json-api/api-settings.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 | # JSON API: /settings ([⬑JSON API Index](index.md)) Jump to: * [Fetch Settings](#get) * [Set Settings](#set) --- <a id="get"></a> # Fetch Settings **Status:** Implemented 20230120 **Required permissions:** "o" **Request:** `/json/settings/get[?version=X]` **Response payload example:** ```json { "access-log":{ "versionable":false, "sensitive":false, "defaultValue":"off", "valueSource":null, "value":null }, ... "binary-glob":{ "versionable":true, "sensitive":false, "defaultValue":null, "valueSource":"versioned", "value":"*.gif\n*.ico\n*.jpg\n*.odp\n*.dia\n*.pdf\n*.png\n*.wav..." }, ... "web-browser":{ "versionable":false, "sensitive":true, "defaultValue":null, "valueSource":"repo", "value":"firefox" } } ``` Each key in the payload is the name of a fossil-recognized setting, modeled as an object. The keys of that are: - `defaultValue`: the setting's default value, or `null` if no default is defined. - `value`: The current value of that setting. - `valueSource`: one of (`"repo"`, `"checkout"`, `"versioned"`, or `null`), specifying the data source where the setting was found. The settings sources are checked in this order and the first one found is the result: - If `version=X` is provided, check-in `X` is searched for a versionable-settings file. If found, its value is used and `valueSource` will be `"versioned"`. If `X` is not a checkin, an error response is produced with code `FOSSIL-3006`. - If a versionable setting is found in the current checkout, its value is used and `valueSource` will be `"versioned"` - If the setting is found in checkout database's `vvar` table, its value is used and `valueSource` will be `"checkout"`. - If the setting is found in repository's `config` table, its value is used and `valueSource` will be `"repo"`. - If no value is found, `null` is used for both the `value` and `valueSource` results. Note that _global settings are never checked_ because they can leak information which have nothing specifically to do with the given repository. - `sensitive`: a value which fossil has flagged as sensitive can only be fetched by a Setup user. For other users, they will always have a `value` and `valueSource` of `null`. - `versionable`: `true` if the setting is tagged as versionable, else `false`. Note that settings are internally stored as strings, even if they're semantically treated as numbers. The way settings are stored and handled does not give us enough information to recognize their exact data type here so they are passed on as-is. <a id="set"></a> # Set Settings **Status:** Implemented 20230120 **Required permissions:** "s" **Request:** `/json/settings/set` This call requires that the input payload be an object containing a mapping of fossil-known configuration keys (case-sensitive) to values. For example: ```json { "editor": "emacs", "admin-log": true, "auto-captcha": false } ``` It iterates through each property, which must have a data type of `null`, boolean, number, or string. A value of `null` _unsets_ (deletes) the setting. Boolean values are stored as integer 0 or 1. All other types are stored as-is. It only updates the `repository.config` database and never updates a checkout or global config database, nor is it capable of updating versioned settings (^Updating versioned settings requires creating a full check-in.). It has no result payload but this may be changed in the future it practice shows that it should return something specific. Error responses include: - `FOSSIL-2002`: called without "setup" permissions. - `FOSSIL-3002`: called without a payload object. - `FOSSIL-3001`: passed an unknown config option. - `FOSSIL-3000`: a value has an unsupported data type. If an error is triggered, any settings made by this call up until that point are discarded. |
Added www/json-api/api-stat.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 | # JSON API: /stat ([⬑JSON API Index](index.md)) # Repository Stats **Status:** implemented **Required privileges:** "o" **Request:** `/json/stat` **Response payload example:** (fields marked with `*` are omitted in "brief" mode) ```json { "projectName":"Fossil", "projectDescription":"Fossil SCM", /* added 20120217 */ "repositorySize":24464384, * "blobCount":13612, * "deltaCount":9348, * "uncompressedArtifactSize":589205834, * "averageArtifactSize":43292, * "maxArtifactSize":4620758, * "compressionRatio":"24:1", * "checkinCount":3150, * "fileCount":456, * "wikiPageCount":23, * "ticketCount":940, "ageDays":1512, "ageYears":4.139744, "projectCode":"25d3a4b83202c0d616a5ed17334f180dac4434dc", "compiler":"gcc-4.1.2 20080704 (Red Hat 4.1.2-50)", "sqlite":{ "version":"2011-09-04 01:27:00 [6b657ae750] (3.7.8)", "pageCount":23891, "pageSize":1024, "freeList":2705, "encoding":"UTF-8", "journalMode":"delete" } } ``` **Options:** - "Full detail" mode:\ **HTTP/payload parameter:** full=bool\ **CLI MODE:** -f|--full with no value.\ If true then all properties are included, else certain properties are omitted from the payload (because they take a relatively long time to calculate).\ **TODO:** rename this to verbose, for consistency.\ **Default=false**. *This is in contrast to the HTML interface*, which defaults to full detail mode. Testing shows stat to have a relatively high per-call cost/run time, so it defaults to "brief" mode by default. Full-detail mode can, on slow hardware, take half a minute to respond, whereas non-full mode takes well under one second. |
Added www/json-api/api-tag.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 | # JSON API: /tag ([⬑JSON API Index](index.md)) Jump to: * [Add](#add) * [Cancel](#cancel) * [Find](#find) * [List](#list) --- <a id="add"></a> # Add Tag **Status:** implemented 20111006 **Required permissions:** "i" **Request:** `/json/tag/add[/name[/checkin[/value]]]` **Request options:** - `name=string` The tag name. - `checkin=string` The checkin to tag. May be a symbolic branch name. - raw=bool (=false) If true, then the name is set as it is provided by the client, else it gets "sym-" prefixed to it. Do not use this unless you really know what you're doing. - `value=string` (=null) An optional value. While tags *may* have values in fossil, it is unusual for them to have a value. (This probably has some interesting uses in custom UIs.) - `propagate=bool` (=false) Sets the tag to propagate to all descendants of the given checkin. In CLI modes, the name, checkin, and value parameters may optionally be supplied as positional parameters (in that order, after the command name). In HTTP mode they may optionally be the 4th-6th path elements or specified via GET/`POST.envelope` parameters. **Response payload example:** ```json { "name":"my-tag", "value":"abc", "propagate":true, "raw":false, "appliedTo":"626ab2f3743543122cc11bc082a0603d2b5b2b1b" } ``` The `appliedTo` property is the hash of the check-in to which the tag was applied. This is the "resolved" version of the check-in name provided by the client. <a id="cancel"></a> # Cancel Tag **Status:** implemented 20111006 **Required permissions:** "i" **Request:** `/json/tag/cancel[/name[/checkin]]` **Request options:** - `name=string` The tag name. May optionally be provided as the 4th path element. - `checkin=string` The checkin to untag. May be a symbolic branch name. May optionally be provided as the 5th path element. In CLI modes, the name and checkin parameters may optionally be supplied as positional parameters (in that order, after the command name) or using the `-name NAME` and `-checkin NAME` options. In HTTP mode they may optionally be the 4th and 5th path elements. **Response payload:** none (resultCode indicates failure) <a id="find"></a> # Find Tag Fetches information about artifacts having a particular tag. Achtung: the output of this response is based on the HTML-mode implementation, but it's not yet certain that it's exactly what we want for JSON mode. The request options and response format may change. **Status:** implemented 20111006 **Required permissions:** "o" **Request:** `/json/tag/find[/tagName]` The response format differs somewhat depending on the options: - `name=string` The tag name to search for. Can optionally be the 3rd path element. - `limit=int` (defalt=0) Limits the number of results (0=no limit). Since they are ordered from oldest to newest, the newest N results will be returned. - `type=string` (default=`*`) Searches only for the given type of artifact (using fossil's conventional type naming: ci, e, t, w. - `raw=bool` (=false) If enabled, the response is an array of hashes of the requested artifact type; otherwise, it is an array of higher-level objects. If this is true, the "name" property is interpretted as-is. If it is false, the name is automatically prepended with "sym-" (meaning a branch). (FIXME: the current semantics are confusing and hard to remember. Re-do them.) **Response payload example, in RAW mode: (expect this format to change at some point!)** ```json { "name":"sym-trunk" "raw":true, "type":"*", "limit":2, "artifacts":[ "a28c83647dfa805f05f3204a7e146eb1f0d90505", "dbda8d6ce9ddf01a16f6c81db2883f546298efb7" ] } ``` Very likely todo: return more information with that (at least the artifact type and timestamp). Once the `/json/artifact` family of bits is finished we could use that to return artifact-type-dependent values here. **Response payload example, in non-raw mode:** ``` { "name":"trunk", "raw":false, "type":"*", "limit":1, "artifacts":[{ "uuid":"4b0f813b8c59ac8f7fbbe33c0a369acc65407841", "timestamp":1317833899, "comment":"fixed [fc825dcf52]", "user":"ron", "eventType":"checkin" }] } ``` <a id="list"></a> # List Tags **Status:** implemented 20111006 **Required permissions:** "o" **Request:** `/json/tag/list[/checkinName]` Potential fixme: we probably have too many different response formats here. We should probably break this into multiple subcommands. The response format differs somewhat depending on the options: - `checkin=string` Lists the tags only for the given CHECKIN (can be a branch name). If set, includeTickets is ignored (meaningless in this combination). This option can be set as either a GET/`POST.payload` option, as the last element of the request path, e.g. `/json/tag/list/MYBRANCH` *or* with `POST.payload` set to a string value. - `raw=bool` (default=false) uses "raw" tag names - `includeTickets=bool` (default=false) Determines whether `tkt-` tags are included. There is one of these for each ticket, so there can be many of them (over 900 in the main fossil repo as of this writing). **Response format when raw=false and no checkin is specified:** ```json { "raw":false, "includeTickets":false, "tags":[ "bgcolor", "branch", "closed", …all tag names... ] } ``` Enabling the `raw` option will leave the internal `sym-` prefix on tags which have them but will not change the structure. If `includeTickets` is true then `tkt-` entries (possibly very many!) will be included in the output, else they are elided. **General notes:** The `tags` property will be null if there are no tags, every non-empty repo has at least one tag (for the trunk branch). **Response format when raw=false and checkin is specified:** ```json { "raw":false, "tags":{ "json":null, "json-multitag-test":null } } ``` The `null`s there are the tag values (most tags don't have values). If `raw=true` then the tags property changes slightly: ```json { "raw":true, "tags":{ "branch":"json", "sym-json":null, "sym-json-multitag-test":null } } ``` TODO?: change the tag values to objects in the form `{value:..., tipUuid:string, propagating:bool}`. |
Added www/json-api/api-ticket.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 | # JSON API: Tickets ([⬑JSON API Index](index.md)) Jump to: * [Ticket Reports](#reports) * [Fetch a Report](#report-get) * [List Reports](#report-list) * [Run a Report](#report-run) --- # Tickets This API is incomplete. It is missing at least the following features: - Content for a given ticket ID - History for a given ticket ID? - An option to enable/disable the generation of hyperlinks, as links won't be useful in most non-browser clients. <a id="reports"></a> # Ticket Reports <a id="report-get"></a> ## Fetch a Report **Status:** implemented 20111008 **Required privileges:** "t" (the thinking being that only those permitted to create reports should be able to read their SQL code) **Request:** `/json/report/get[/REPORT_NUMBER]` **Request options:** - `report=number` The report number to fetch.\ CLI: `-report|-r` \ (Design note: `--number/-n` was not used because that parameter has a different meaning (limit response count) in several commands.) May optionally be defined via the 4th GET path element or CLI arg. **Response payload example:** ```json { "report":1, "owner":"drh", "title":"All Tickets", "timestamp":"112443570187200", "columns":"#ffffff Key:\r\n#f2dcdc Active\r\n...", "sqlCode":"..." } ``` <a id="report-list"></a> ## List Reports **Status:** implemented 20111008 **Required privileges:** "r" or "n" **Request:** `/json/report/list` **Response payload example:** ```json [ { "report":1, "title":"All Tickets", "owner":"drh" }, … ] ``` <a id="report-run"></a> ## Run a Report **Status:** implemented 20111008 **Required privileges:** "r" or "n" **Request:** `/json/report/run[/REPORT_NUMBER]` Request options: - `report|-r=int` Specifies which report to run. May optionally be supplied as the 4th CLI arg or URL path element. - `format|-f=string` (default="o") Specifies "array" or "object" output format. **Response payload example:** ```json { "report":1, "title":"All Tickets", "sqlcode": "only set if requester has 't' privileges.", "columnNames":[ … list of column names … ], "tickets":[ { "bgcolor":"#cfe8bd", "#":"fc825dcf52", "timestamp":"112443570187200", "type":"Code_Defect", "status":"Fixed", "subsystem":null, "title":"\"config pull all\" asks to approve ssl cert" }, … ] } ``` Note that the column names of ticket reports are determined by the reports themselves, and not C code. That means that we cannot return a standard set of column names here. Fossil requires certain column naming conventions for purposes of styling the HTML interface, e.g. the "\#" column is always the uuid of the record and "bgcolor" (note the different casing than bgColor used throughout the rest of this API!) has a specific meaning to the HTML report browser. Fossil also allows the tickets to be extended with client-specified fields, so we cannot generically make these results fit into the API-wide naming scheme. Full details are here: [](/doc/trunk/www/custom_ticket.wiki) and: [](/rptsql?rn=1) (That one may require non-default permission.) |
Added www/json-api/api-timeline.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 | # JSON API: /timeline ([⬑JSON API Index](index.md)) Jump to: * [Introduction](#intro) * [Branch Timeline](#branch) * [Technote (formerly Event) Timeline](#technote) * [Ticket Timeline](#ticket) * [Wiki Timeline](#wiki) --- <a id="intro"></a> # Introduction These requests return overview-level information about various types of changes. The response payload differs for each artifact type, and the current structures are almost certainly not "final" (e.g. we are still undecided on how/whether to handle artifact links within commit messages and whatnot). By default the entries are returned in chronological order from newest to oldest, but some options might change that. FIXME (20120623): we have some inconsistent `type` vs. `eventType` in the result sets. `type` is the current preferred choice (and it seems unlikely that `eventType` is actually used in any client code). We don't actually need either one (but a use for `type` is easily envisioned), and we may get rid of both. **Common request options (via CLI, GET or POST.payload):** - `limit=integer` limits the number of entries in the response. Default is unspecified (but is "quite possibly 20 or so"). A limit value of 0 disables any limit, fetching all entries (which can take a really long time and might overload clients which have very limited memory).\ CLI mode: `--limit|-n #` - `after="YYYY-MM-DD[ HH:mm:ss]"` limits the search to items on or after the given date string. Reverses the normal timeline sort order. Alias: "a". Only one of "after" or "before" can be used, and if both are specified then which one takes precedence is unspecified.\ CLI mode: `--after|-a "DATE[ TIME]"` - `before="YYYY-MM-DD[ HH:mm:ss]"` limits the search to items on or before the given date string.\ CLI mode: `--before|-b "DATE[ TIME]" - TODOs, still to be ported from the HTML-mode timeline: - circa=DATETIME - tag=string - related=tag name - string=search string <a id="branch"></a> # Branch Timeline **Status:** partially implemented but undocumented because the utility of the current impl is under question. It also doesn't understand most of the common timeline options. <a id="checkin"></a> # Checkin Timeline **Status:** implemented 201109xx **Required privileges:** "o" **Request:** `/json/timeline/checkin` **Response payload example:** ```json { "limit": number, /* if not set, all records are returned */ "timeline":[{ "uuid":"be700e84336941ef1bcd08d676310b75b9070f43", "timestamp":1317094090, "comment":"Added /json/timeline/ci showFiles to ajax test page.", "user":"stephan", "isLeaf":true, "bgColor":null, /* not quite sure why this is null? */ "type":"ci", "parents": ["primary parent hash", "...other parent hashes"], "tags":["json"], "files":[{ "name":"ajax/index.html", "uuid":"9f00773a94cea6191dc3289aa24c0811b6d0d8fe", "parent":"50e337c33c27529e08a7037a8679fb84b976ad0b", "state":"modified" }] },...] } ``` (Achtung: the `parents` property was called `prevUuid` prior to 20120316.) The `parents` property lists the checkins which were parents of this commit. The first entry in the array is the "primary parent" - the one which was not involved in a merge with the child. **Request options:** - `files=bool` toggles the addition of a "files" array property which contains objects describing the files changed by the commit, including their hash, previous hash, and state change type (modified, added, or removed). ([“uuid†here means hash][uvh])\ CLI mode: `--show-files|-f` - `tag|branch=string` selects only entries with the given tag or "close to" the given branch. Only one of these may be specified and if both are specified, which one takes precedence is unspecified. If the given tag/branch does not exist, an error response is generated. The difference between the two is subtle - tag filters only on the given tag (analog to the HTML interface's "r" option) whereas branch can also return entries from other branches which were merged into the requested branch (analog to the HTML interface's "b" option). If one of these is specified, the response payload will contain a "tag" *or* "branch" property with the tag/branch name given by the client. <a id="technote"></a> # Technote (formerly Event) Timeline **Status:** implemented 20180803 **Required privileges:** "j" **Request:** - `/json/timeline/technote` - DEPRECATED: `/json/timeline/event` (technotes were formerly called `events`) **Response payload example:** ```json { "limit": number, /* if not set, all records are returned */ "timeline":[{ "name":"8d18bf27e9f9ff8b9017edd55afc35701407d418", "uuid":"b23962c88c123924a77fd663e91af094780d920a", "timestamp":1478376113, "comment":"Style update due to [8d880f0bb4]", "user":"andygoth", "eventType":"e" },...] } ``` The `uuid` of each entry can be passed to `/json/artifact` to fetch the raw event content. <a id="ticket"></a> # Ticket Timeline **Status:** implemented 201109xx **Required privileges:** "r" or "o" **Request:** `/json/timeline/ticket` **Response payload example:** ```json { "limit": number, /* if not set, all records are returned */ "timeline":[{ "uuid":"5065a5da060e181da49a618f8ae5dc245215e95b", "timestamp":1316511322, "user":"stephan", "eventType":"t", "comment":"Ticket [b64435dba9] <i>How to...</i>", "briefComment":"Ticket [b64435dba9]: 2 changes", "ticketUuid":"b64435dba9cceb709bd54fbc5883884d73f93491" },...] } ``` **Notice that there are two [hashes][uvh] for tickets** - `uuid` is the change hash and `ticketUuid` is the actual ticket’s hash. This is an unfortunate discrepancy vis-a-vis the other timeline entries, which only have one hash. We may want to swap `uuid` to mean the ticket hash and change `uuid` to `commitHash`. <a id="wiki"></a> # Wiki Timeline **Status:** implemented 201109xx **Required privileges:** "j" or "o" **Requests:** - `/json/timeline/wiki` - `/json/wiki/timeline` (alias) **Response payload example:** ```json { "limit": number, /* if not set, all records are returned */ "timeline":[{ "uuid":"4b2026f06eb48eaf187209fcb05ba5438c3b0ef0", "timestamp":1331351121, "comment":"Changes to wiki page [Page3]", "user":"stephan", "eventType":"w" },...] } ``` The `uuid` of each entry can be passed to `/json/artifact` or `/json/wiki/get?uuid=...` to fetch the raw page and the hash of the parent version. [uvh]: ../hashes.md#uvh |
Added www/json-api/api-user.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 | # JSON API: /user ([⬑JSON API Index](index.md)) Jump to: * [Get User Info](#get) * [List Users](#list) * [Save User](#save) --- <a id="get"></a> # Get User Info **Status:** implemented 20110927. **Required privileges:** "a" or "s" **Request:** - POST `/json/user/get`\ with `POST.payload.name=USERNAME` - `/json/user/get?name=USERNAME` **Response payload example:** ```json { "uid":1, "name":"stephan", "capabilities":"abcdefhgijkmnopqrstuvwxz", "info":"https://wanderinghorse.net/home/stephan/", "timestamp":1316122562 } ``` (What does that timestamp field represent, anyway?) <a id="list"></a> # List Users **Status:** implemented 20110927. **Required privileges:** "a" or "s" **Request:** `/json/user/list` **Response payload example:** ```json [ { "uid":1, "name":"stephan", "capabilities":"abcdefhgijkmnoprstuvwxz", "info":"", "timestamp":1316122562 }, ... more users... ] ``` <a id="save"></a> # Save User Only admin/setup users may modify accounts other than their own. **Status:** implemented 20111021 *but* it is missing "login group" support, so changes do not yet propagate to other repos within a group. **Required privileges:** 'p' or 'a' or 's', depending on the context. **Request:** `/json/user/save` All request options must come from the `POST.payload` and/or GET/CLI parameters (exception: "name" must come from POST.payload or CLI). GET/CLI parameters take precedence over those in `POST.payload`, the intention being to use an input file as a template and overriding the template's defaults via the CLI. The options include: - `name=string` Specifies the user name to change. When changing a user's name, the current uid and the new name must be specified.\ **Achtung:** due to fossil-internal ambiguity in the handling of the "name" parameter, this parameter must come from the `POST.payload` data or it will not be recognized. In CLI mode it may be specified with the `--name` flag. - `uid=int` Specifies the uid to change. At least one of uid or name are required. A uid of -1 means to create a new user, in which case the name must be provided. - `password=string` Optionally changes the user's password. When renaming existing or creating new users, be sure to always provide a new password because any old password hash is invalidated by the name change. - `info=string` Optionally changes the user's info field. - `capabilities=string` Optionally changes the user's capabilities field. - `forceLogout=bool` (=false, or true when renaming) Optionally clears any current login info for the current user, which will invalidate any active session. Requires 'a' or 's' privileges. Intended to be used when disabling a user account, to ensure that any open session is invalidated. When a user is renamed this option is implied (and cannot be disabled) because renaming invalidates any currently stored auth token (because the old name is part of the hash equation). Fields which are not provided in the request will not be modified. Non-admin/setup users cannot edit other users and may only change their own data if they have the 'p' (password) privilege. As of 20120217, users who do not have the setup privilege may neither change the setup privilege for any user nor edit another user who has that privilege. That is, only users with setup access may propagate or remove setup status and accounts with the setup privilege may only be edited by themselves and other setup users. **Response payload:** Same as user/get, using the new/saved state of the modified user. Example usage from the command line: ```console $ fossil json user save --name drh --password sqlite3 \ --capabilities "as" --info "DRH" $ fossil json user save --uid 1 --name richard \ --password fossil \ --info "Previously known as drh" ``` **Warnings:** - When creating a new user or renaming a user, if no (new) password is specified in the save request then the user will not be able to log in because the previous password (for existing users) is hashed against the old name. - Renaming a user invalidates any active login token because his old name is a part of the hash. i.e. the user must log back in with the new name after being renamed. **TODOs:** - Login group support. |
Added www/json-api/api-version.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 | # JSON API: /version ([⬑JSON API Index](index.md)) # Version (a.k.a. HAI) **Status:** implemented **Required privileges:** none **Requests:** - `/json/version` - `/json/HAI` (alias borrowed from LOLCATZ jargon) **Response payload example:** ```json { "manifestUuid":"20ff808f9809541d2eca6c49a17d5cbd16e1b93f", "manifestVersion":"[20ff808f98]", "manifestDate":"2011-09-09 16:49:23", "manifestYear":"2011", "releaseVersion":"1.19", "releaseVersionNumber":119, "jsonApiVersion": "YYYYMMDD" // added 20120409 } ``` Those particular payload fields were chosen only because they're defined in `VERSION.h`. We may want to add other information, but nothing comes to mind at this time. |
Added www/json-api/api-wiki.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 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 | # JSON API: /wiki ([⬑JSON API Index](index.md)) Jump to: * [Page List](#list) * [Fetch a Page](#get) * [Create or Save Page](#create-save) * [Wiki Diffs](#diffs) * [Preview](#preview) * [Notes and TODOs](#todo) --- <a id="list"></a> # Page List Returns a list of all pages, not including their content (which can be arbitrarily large). **Status:** implemented 201109xx **Required privileges:** "j" or "o" **Request:** `/json/wiki/list` **Options:** - `bool verbose` (=false) Changes the results to return much more information. Added 20120219. - `glob=wildcard` string (default=`*`). If set, only page names matching the given wildcard are returned. Added 20120325.\ CLI: `--glob|-g STRING` - `like=SQL LIKE string` (default=`%`). If set, only page names matching the given SQL LIKE string are returned. Note that this match is case-INsensitive. If both glob and like are given then only one will work and which one takes precedence is unspecified. Added 20120325.\ CLI: `--like|-l STRING` - `invert=bool` (default=false). If set to a true value, the glob/like filter has a reverse meaning (pages *not* matching the wildcard are returned). Added 20120329.\ CLI: `-i/--invert` **Response payload: format depends on "verbose" option** Non-verbose mode: ```json ["PageName1",..."PageNameN"] ``` In verbose mode: ```json [{ "name":"Apache On Windows XP", "uuid":"a7e68df71b95d35321b9d9aeec3c8068f991926c", "user":"jeffrimko", "timestamp":1310227825, "size":793 /* in bytes */ },...] ``` The verbose-mode output is the same as the [`/json/wiki/get`](#get) output, but without the content. The size property of each reflects the *byte* length of the raw (non-HTMLized) page content. **Potential TODOs:** - Allow specifying (in the request) a list/array of names, as opposed to listing all pages. The page count is rarely very high, though, so an "overload" is very unlikely. (i have one wiki with about 47 pages in it.) <a id="get"></a> # Fetch a Page Fetches a single wiki page, including content and significant metadata. **Status:** implemented 20110922, but response format may change. **Required privileges:** "j" or "o" **Request:** - GET: `/json/wiki/get?name=PageName` - GET: `/json/wiki/get/PageName` - POST: `/json/wiki/get` with page name as `POST.payload` or `POST.payload.name`. **Response payload example:** ```json { "name": "Fossil", "uuid": "...hex string...", "parent": "uuid of parent (not set for first version of page)", "user": "anonymous", "timestamp": 1286143975, "size": 1906, /* In bytes, not UTF8 characters! Affected by format option! */ "content": "..." } ``` **FIXME:** it's missing the mimetype (that support was added to fossil after this was implemented). If given no page to load, or if asked to get a page which does not exist, an error response is generated (a usage- or resource-not-found error, respectively). **Options (via CLI/GET/`POST.payload`):** - `name=string`. The page to fetch. The latest version is fetched unless the uuid paramter is supplied (in which case name is ignored). \ CLI: `--name|-n string`\ Optionally, the name may be the 4th positional argument/request path element. - `uuid=string`. Fetches a specific version. The name parameter is ignored when this is specified.\ CLI: `--uid|-u string` - `format=string ("raw"|"html"|"none")` (default="raw"). Specifies the format of the "content" payload value.\ CLI: `--format|-f string` \ The "none" format means to return no content. In that case the size refers to the same size as the "raw" format. **TODOs:** - Support passing an array of names in the request (and change response to return an array). <a id="create-save"></a> # Create or Save Page **Status:** implemented 20110922. **Required privileges:** "k" (save) or "f" (create) **Request:** - `/json/wiki/create` - `/json/wiki/save` These work only in HTTP mode, not CLI mode. (FIXME: now that we can simulate POST from a file, these could be used in CLI mode.) The semantic differences between save and create are: - Save will fail if the page doesn't already exist whereas create will fail if it does. The createIfNotExists option (described below) can be used to create new pages using the save operation. - The content property is optional for the create request, whereas it is required to be a string for save requests (but it *may* be an empty string). This requirement for save is *almost* arbitrary, and is intended to prevent accidental erasing of existing page content via API misuse. **Response payload example:** The same as for [`/json/wiki/get`](#get) but the page content is not included in the response (only the metadata). **Request options** (via GET or `POST.payload` object): - `name=string` specifies the page name. - `content=string` is the body text.\ Content is required for save (unless `createIfNotExists` is true *and* the page does not exist), optional for create. It *may* be an empty string. - `mimetype=string` specifies the mimetype for the body, noting any any unrecognized/unsupported mimetype is silently treated as `text/x-fossil-wiki`. - Save (not create) supports a `createIfNotExists` boolean option which makes it a functional superset of the create/save features. i.e. it will create if needed, else it will update. If createIfNotExists is false (the default) then save will fail if given a page name which does not refer to an existing page. - **TODO:** add `commitMessage` string property. The fossil internals don't have a way to do this at the moment (they can as of late 2019). Since fossil wiki commits have always had the same default commit message, this is not a high-priority addition. See:\ [](/doc/trunk/www/fileformat.wiki#wikichng) - **Potential TODO:** we *could* optionally also support multi-page saving using an array of pages in the request payload:\ `[… page objects … ]` <a id="diffs"></a> # Wiki Diffs **Status:** implemented 20120304 **Required privileges:** "h" **Request:** - `/json/wiki/diff[/version1_UUID/version2_UUID]` **Response payload example:** ```json { "v1":"e32ccdcda59e930c77c1e01cebace5d71253f621", "v2":"e15992f475760cdf3a9564d8f88cacb659ab4b07", "diff":"@@ -1,4 +1,9 @@...<SNIP>..." } ``` **Options:** - `v1=uuid` and `v2=uuid` specify the two versions to diff, and are required parameters. They may optionally be specified as the two URL/CLI parameters following the "diff" sub-command/path. This command does not verify that both UUIDs actually refer to the same page name, but do verify that they refer to wiki content. Trivia: passing the same UUIDs to the `/json/diff` command will produce very different results - that one diffs the manifests of the commits. **TODOs:** - Add options for changing the format of the diff, e.g. side-by-side and enabling the HTML markup supported by the main fossil HTML GUI. - Potentially do a name comparison to verify that the diff is against the same page. That said, when "renaming" pages it might be useful to diff two different pages. <a id="preview"></a> # Preview **Status:** implemented 20120310 **Required privileges:** "k" (to limit its use to only those who can edit wiki pages). This limitation is up for debate/reconsideration. **Request:** - `/json/wiki/preview` This command wiki-processes arbitrary text sent from the client. To help curb potential abuse, its use is restricted to those with "k" access rights. The `POST.payload` property must be either: 1) A string containing Fossil wiki markup. 2) An Object with a `body` property holding the text to render and a `mimetype` property describing the wiki format: `text/x-fossil-wiki` (the default), `text/x-markdown`, or `text/plain`. Any unknown type is treated as `text/x-fossil-wiki`. The response payload is a string containing the rendered page. Whether or not "all HTML" is allowed depends on site-level configuration options, and that changes how the input is processed. Note that the links in the generated page are for the HTML interface, and will not work as-is for arbitrary JSON clients. In order to integrate the parsed content with JSON-based clients the HTML will probably need to be post-processed, e.g. using jQuery to fish out the links and re-map wiki page links to a JSON-capable page handler. <a id="todo"></a> # Notes and TODOs - When server-parsing the wiki content, the generated intra-wiki/intra-site links will only be useful in the context of the original fossil UI (or a work-alike), not arbitrary JSON client apps. Potential TODOs: - `/wiki/history` analog to the [](/whistory) page. |
Added www/json-api/conventions.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 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 | # JSON API: General Conventions ([⬑JSON API Index](index.md)) Jump to: * [JSON Property Naming](#property-names) * [HTTP GET Requests](#http-get) * [HTTP POST Requests](#http-post) * [POST Request Envelope](#request-envelope) * [Request Parameter Data Types](#request-param-types) * [Response Envelope](#response-envelope) * [HTTP Response Headers](#http-response-header) * [CLI vs. HTTP Mode](#cli-vs-http) * [Simulating POSTed data](#simulating-post-data) * [Indentation/Formatting of JSON Output](#json-indentation) * [JSONP](#jsonp) * [API Result Codes](#result-codes) --- <a id="property-names"></a> # JSON Property Naming Since most JSON usage conventionally happens in JavaScript environments, this API has (without an explicit decision ever having been made) adopted the ubiquitous JavaScript convention of `camelCaseWithStartingLower` for naming properties in JSON objects. <a id="http-get"></a> # HTTP GET Requests Many (if not most) requests can be made via simple GET requests, e.g. we *could* use any of the following patterns for a hypothetical JSON-format timeline: - `https://..../timeline/json` - `/timeline?format=json` - `/timeline?json=1` - `/timeline.json` - `/json/timeline?...options...` The API settled on the `/json/...` convention, primarily because it simplifies dispatching and argument-handling logic compared to the `/[.../]foo.json` approach. Using `/json/...` allows us to unify that logic for all JSON sub-commands, for both CLI and HTTP modes. <a id="http-post"></a> # HTTP Post Requests Certain requests, mainly things like editing checkin messages and committing new files entirely, require POST data. This is fundamentally very simple to do - clients post plain/unencoded JSON using a common wrapper envelope which contains the request-specific data to submit as well as some request-independent information (like authentication data). <a id="request-envelope"></a> ## POST Request Envelope POST requests are sent to the same URL as their GET counterpart (if any, else their own path), and are sent as plain-text/unencoded JSON wrapped in a common request envelope with the following properties: - `requestId`: Optional arbitrary JSON value, not used by fossil, but returned as-is in responses. - `command`: Provides a secondary mechanism for specifying which JSON command should be run. A request path of /json/foo/bar is equivalent to a request with path=/json and command=foo/bar. Note that subpaths do not work this way. e.g. path=/json/foo, command=bar will not work, but path=/json, command=foo/bar will. This option is particularly useful when generating JSON for piping in to CLI mode, but it also has some response-dispatching uses on the client side. - `authToken`: Authentication token. Created by a login request. Determines what access rights the user has, and any given request may require specific rights. In principle this is required by any request which needs non-guest privileges, but cookie-aware clients do not manually need to track this (it is managed as a cookie by the agent/browser). - Note that when accessing fossil over a local server instance which was started with the `--localauth` flag, the `authToken` will be ignored and need not be sent with any requests. The user will automatically be given full privileges, as if they were using CLI mode. - `payload`: Command-specific parameters. Most can optionally come in via GET parameters, but those taking complex structures expect them to be placed here. - `indent`: Optionally specifies indentation for the output. 0=no indention. 1=a single TAB character for each level of indentation. >1 means that many spaces per level. e.g. indent=7 means to indent 7 spaces per object/array depth level. cson also supports other flags for fine-tuning the output spacing, and adding them to this interface might be interesting at some point. e.g. whether or not to add a newline to the output. CLI mode adds extra indentation by default, whereas CGI/server modes produce unindented output by default. - `jsonp`: Optional String (client function name). Requests which include this will be returned with `Content-Type application/javascript` and will be wrapped up in a function call using the given name. e.g. if `jsonp=foo` then the result would look like:\ `foo( {...the response envelope...} )` The API allows most of those (normally all but the payload) to come in as either GET parameters or properties of the top-level POSTed request JSON envelope, with GET taking priority over POST. (Reminder to self: we could potentially also use values from cookies. Fossil currently only uses 1 cookie (the login token), and i'd prefer to keep it that way.) POST requests without such an envelope will be rejected, generating a Fossil/JSON error response (as opposed to an HTTP error response). GET requests, by definition, never have an envelope. POSTed client requests *must* send a Content-Type header of either `application/json` , `application/javascript`, or `text/plain`, or the JSON-handling code will never see the POST data. The POST handler optimistically assumes that type `text/plain` "might be JSON", since `application/json` is a newer convention which many existing clients do not use (as of the time these docs were written, back in 2011). ## POST Envelope vs. `POST.payload` When this document refers to POST data, it is referring to top-level object in JSON-format POSTed input data. When we say `POST.payload` we refer to the "payload" property of the POST data. While fossil's core handles *form-urlencoded* POST data, if such data is sent in then parsing it as JSON cannot succeed, which means that (at worst) the JSON-mode bits will "not see" any POST data. Data POSTed to the JSON API must be sent non-form-urlencoded (i.e. as plain text). Framework-level configuration options are always set via the top-level POST envelope object or GET parameters. Request-specific options are set either in `POST.payload` or GET parameters (though the former is required in some cases). Here is an example which demonstrates the possibly not-so-obvious difference between the two types of options (framework vs. request-specific): ```json { "requestId":"my request", // standard envelope property (optional) "command": "timeline/wiki", // also standard "indent":2, // output indention is a framework-level option "payload":{ // *anything* in the payload is request-specific "limit":1 } } ``` When a given parameter is set in two places, e.g. GET and POST, or POST-from-a-file and CLI parameters, which one takes precedence depends on the concrete command handler (and may be unspecified). Most will give precedence to CLI and GET parameters, but POSTed values are technically preferred for non-string data because no additional "type guessing" or string-to-whatever conversion has to be made (GET/CLI parameters are *always* strings, even if they look like a number or boolean). <a id="request-param-types"></a> # Request Parameter Data Types When parameters are sent in the form of GET or CLI arguments, they are inherently strings. When they come in from JSON they keep their full type (boolean, number, etc.). All parameters in this API specify what *type* (or types) they must (or may) be. For strings, there is no internal conversion/interpretation needed for GET- or CLI-provided parameters, but for other types we sometimes have to convert strings to other atomic types. This section describes how those string-to-whatever conversions behave. No higher-level constructs, e.g. JSON **arrays** or **objects**, are accepted in string form. Such parameters must be set in the POST envelope or payload, as specified by the specific API. This API does not currently use any **floating-point** parameters, but does return floating-point results in a couple places. For **integer** parameters we use a conventional string-to-int algorithm and assume base 10 (analog to atoi(3)). The API may err on the side of usability when given technically invalid values. e.g. "123abc" will likely be interpreted as the integer 123. No APIs currently rely on integer parameters with more than 32 bits (signedness is call-dependent but few, if any, use negative values). **Boolean** parameters are a bit schizophrenic... In **CLI mode**, boolean flags do not have a value, per se, and thus require no string-to-bool conversion. e.g. `fossil foo -aBoolOpt -non-bool-opt value`. Those which arrive as strings via **GET parameters** treat any of the following as true: a string starting with a character in the set `[1-9tT]`. All other string values are considered to be false for this purpose. Those which are part of the **POST data** are normally (but not always - it depends on the exact context) evaluated as the equivalent of JavaScript booleans. e.g. if we have `POST.envelope.foo="f"`, and evaluate it as a JSON boolean (as opposed to a string-to-bool conversion), the result will be true because the underlying JSON API follows JavaScript semantics for any-type-to-bool conversions. As long as clients always send "proper" booleans in their POST data, the difference between GET/CLI-provided booleans should never concern them. TODO: consider changing the GET-value-to-bool semantics to match the JS semantics, for consistency (within the JSON API at least, but that might cause inconsistencies vis-a-vis the HTML interface). <a id="response-envelope"></a> # Response Envelope Every response comes in the form of a HTTP response or (in CLI mode) JSON sent to stdout. The body of the response is a JSON object following a common envelope format. The envelope has the following properties: - `fossil`: Fossil server version string. This property is basically "the official response envelope marker" - if it is set, clients can "probably safely assume" that the object indeed came from one of the Fossil/JSON APIs. This API never creates responses which do not contain this property. - `requestId`: Only set if the request contained it, and then it is echoed back to the caller as-is. This can be use to determine (client-side) which request a given response is coming in for (assuming multiple asynchronous requests are pending). In practice this generally isn’t needed because response handling tends to be done by closures associated with the original request object (at least in JavaScript code). In languages without closures it might have some use. It may be any legal JSON value - it need not be confined to a string or number. - `resultCode`: Standardized result code string in the form `FOSSIL-####`. Only error responses contain a `resultCode`. - `resultText`: Possibly a descriptive string, possibly empty. Supplements the resultCode, but can also be set on success responses (but normally isn't). Clients must not rely on any specific values being set here. - `payload`: Request-specific response payload (data type/structure is request-specific). The payload is never set for error responses, only for success responses (and only those which actually have a payload - not all do). - `timestamp`: Response timestamp (GMT Unix Epoch). We use seconds precision because i did not know at the time that Fossil actually records millisecond precision. - `payloadVersion`: Not initially needed, but reserved for future use in maintaining version compatibility when the format of a given response type's payload changes. If needed, the "first version" value is assumed to be 0, for semantic [near-]compatibility with the undefined value clients see when this property is not set. - `command`: Normalized form of the command being run. It consists of the "command" (non-argument) parts of the request path (or CLI positional arguments), excluding the initial "/json/" part. e.g. the "command" part of "/json/timeline/checkin?a=b" (CLI: json timeline checkin...) is "timeline/checkin" (both in CLI and HTTP modes). - `apiVersion`: Not yet used, but reserved for a numeric value which represents the JSON API's version (which can be used to determine if it has a given feature or not). This will not be implemented until it's needed. - `warnings`: Reserved for future use as a standard place to put non-fatal warnings in responses. Will be an array but the warning structure/type is not yet specified. Intended primarily as a debugging tool, and will "probably not" become part of the public client interface. - `g`: Fossil administrators (those with the "a" or "s" permissions) may set the `debugFossilG` boolean request parameter (CLI: `--json-debug-g`) to enable this property for any given response. It contains a good deal of the server-side internal state at the time the response was generated, which is often useful in debuggering problems. Trivia: it is called "g" because that's the name of fossil's internal global state object. - `procTimeMs`: For debugging only - generic clients must not rely on this property. Contains the number of milliseconds the JSON command processor needed to dispatch and process the command. TODO: move the timer into the fossil core so that we can generically time its responses and include the startup overhead in the time calculation. <a id="http-response-header"></a> # HTTP Response Headers The Content-Type HTTP header of a response will be either application/json, application/javascript, or text/plain, depending on whether or not we are in JSONP mode or (failing that) the contents of the "Accept" header sent in the request. The response type will be text/plain if it cannot figure out what to do. The response's Content-Type header *may* contain additional metadata, e.g. it might look like: application/json; charset=utf-8 Apropos UTF-8: note that JSON is, by definition, Unicode and recommends UTF-8 encoding (which is what we use). That means if your console cannot handle UTF-8 then using this API in CLI mode might (depending on the content) render garbage on your screen. <a id="cli-vs-http"></a> # CLI vs. HTTP Mode CLI (command-line interface) and HTTP modes (CGI and standalone server) are consolidated in the same implementations and behave essentially identically, with only minor exceptions. An HTTP path of `/json/foo` translates to the CLI command `fossil json foo`. CLI mode takes options in the fossil-convention forms (e.g. `--foo 3` or `-f 3`) whereas HTTP mode takes them via GET/POST data (e.g. `?foo=1`). (Note that per long-standing fossil convention CLI parameters taking a value do not use an equal sign before the value!) For example: - HTTP: `/json/timeline/wiki?after=2011-09-01&limit=3` - CLI: `fossil json timeline wiki --after 2011-09-01 --limit 3` Some commands may only work in one mode or the other (for various reasons). In CLI mode the user automatically has full setup/admin access. In HTTP mode, request-specific options can also be specified in the `POST.payload` data, and doing so actually has an advantage over specifying them as URL parameters: posting JSON data retains the full type information of the values, whereas GET-style parameters are always strings and must be explicitly type-checked/converted (which may produce unpredictable results when given invalid input). That said, oftentimes it is more convenient to pass the options via URL parameters, rather than generate the request envelope and payload required by POST requests, and the JSON API makes some extra effort to treat GET-style parameters type-equivalent to their POST counterparts. If a property appears in both GET and `POST.payload`, GET-style parameters *typically* take precedence over `POST.payload` by long-standing convention (=="PHP does it this way by default"). (That is, however, subject to eventual reversal because of the stronger type safety provided by POSTed JSON. Philosophically speaking, though, GET *should* take precedence, in the same way that CLI-provided options conventionally override app-configuration-level options.) One notable functional difference between CLI and HTTP modes is that in CLI mode error responses *might* be accompanied by a non-0 exit status (they "should" always be, but there might be cases where that does not yet happen) whereas in HTTP mode we always try to exit with code 0 to avoid generating an HTTP 500 ("internal server error"), which could keep the JSON response from being delivered. The JSON code only intentionally allows an HTTP 500 when there is a serious internal error like allocation or assertion failure. HTTP clients are expected to catch errors by evaluating the response object, not the HTTP result code. <a id="simulating-post-data"></a> # Simulating POSTed data We have a mechanism to feed request data to CLI mode via files (simulating POSTed data), as demonstrated in this example: ```console $ cat in.json { "command": "timeline/wiki", "indent":2, "payload":{"limit":1}} $ fossil json --json-input in.json # use filename - for stdin ``` The above is equivalent to: ```console $ echo '{"indent":2, "payload":{"limit":1}}' \ | fossil json timeline wiki --json-input - ``` Note that the "command" JSON parameter is only checked when no json subcommand is provided on the CLI or via the HTTP request path. Thus we cannot pass the CLI args "json timeline" in conjunction with a "command" string of "wiki" this way. ***HOWEVER...*** Much of the existing JSON code was written before the `--json-input` option was possible. Because of this, there might be some "misinteractions" when providing request-specific options via *both* CLI options and simulated POST data. Those cases will eventually be ironed out (with CLI options taking precedence). Until then, when "POSTing" data in CLI mode, for consistent/predictible results always provide any options via the JSON request data, not CLI arguments. That said, there "should not" be any blatant incompatibilities, but some routines will prefer `POST.payload` over CLI/GET arguments, so there are some minor inconsistencies across various commands with regards to which source (POST/GET/CLI) takes precedence for a given option. The precedence "should always be the same," but currently cannot be due to core fossil implementation details (the internal consolidation of GET/CLI/POST vars into a single set). <a id="json-indentation"></a> # Indentation/Formatting of JSON Output CLI mode accepts the `--indent|-I #` option to set the indention level and HTTP mode accepts `indent=#` as a GET/POST parameter. The semantics of the indention level are derived from the underlying JSON library and have the following meanings: 0 (zero) or less disables all superfluous indentation (this is the default in HTTP mode). A value of 1 uses 1 hard TAB character (ASCII 0x09) per level of indention (the default in CLI mode). Values greater than 1 use that many whitespaces (ASCII 32d) per level of indention. e.g. a value of 7 uses 7 spaces per level of indention. There is no way to specify one whitespace per level, but if you *really* want one whitespace instead of one tab (same data size) you can filter the output to globally replace ASCII 9dec (TAB) with ASCII 32dec (space). Because JSON string values *never* contain hard tabs (they are represented by `\t`) there is no chance that such a global replacement will corrupt JSON string contents - only the formatting will be affected. Potential TODO: because extraneous indention "could potentially" be used as a form DoS, the option *might* be subject to later removed from HTTP mode (in CLI it's fine). In HTTP mode no trailing newline is added to the output, whereas in CLI mode one is normally appended (exception: in JSONP mode no newline is appended, to (rather pedantically and arbitraily) allow the client to add a semicolon at the end if he likes). There is currently no option to control the newline behaviour, but the underlying JSON code supports this option, so adding it to this API is just a matter of adding the CLI/HTTP args for it. Pedantic note: internally the indention level is stored as a single byte, so giving large indention values will cause harmless numeric overflow (with only cosmetic effects), meaning, e.g., 257 will overflow to the value 1. Potential TODO: consider changing cson's indention mechanism to use a *signed* number, using negative values for tabs and positive for whitespace count (or the other way around). This would require more doc changes than code changes :/. <a id="jsonp"></a> # JSONP The API supports JSONP-style output. The caller specifies the callback name and the JSON response will be wrapped in a function call to that name. For HTTP mode pass the `jsonp=string` option (via GET or POST envelope) and for CLI use `--jsonp string`. For example, if we pass the JSONP name `myCallback` then a response will look like: ```js myCallback({...response...}) ``` Note that fossil does not evaluate the callback name itself, other than to verify that it is-a string, so "garbage in, garbage out," and all that. (Remember that CLI and GET parameters are *always* strings, even if they *look* like numbers.) <a id="result-codes"></a> # API Result Codes Result codes are strings which tell the client whether or not a given API call succeeded or failed, and if it failed *perhaps* some hint as to why it failed. The result code is available via the resultCode property of every *error* response envelope. Since having a result code value for success responses is somewhat redundant, success responses contain no resultCode property. In practice this simplifies error checking on the client side. The codes are strings in the form `FOSSIL-####`, where `####` is a 4-digit integral number, left-padded with zeros. The numbers follow these conventions: - The number 0000 is reserved for the "not an error" (OK) case. Since success responses do not contain a result code, clients won't see this value (except in documentation). - All numbers with a leading 0 are reserved for *potential* future use in reporting non-fatal warnings. - Despite *possibly* having leading zeros, the numbers are decimal, not octal. Script code which uses eval() or similar to produce integers from them may need to take that into account. - The 1000ths and 100ths places of the number describe the general category of the error, e.g. authentication- vs. database- vs. usage errors. The 100ths place is more specific than the 1000ths place, allowing two levels of sub-categorization (which "should be enough" for this purpose). This separation allows the server administrator to configure the level of granularity of error reporting. e.g. some admins consider error messages to be security-relevant and like to "dumb them down" on their way to the client, whereas developers normally want to see very specific error codes when tracking down a problem. We can offer a configuration option to "dumb down" error codes to their generic category by simply doing a modulo 100 (or 1000) against the native error code number. e.g. FOSSIL-1271 could (via a simple modulo) be reduced to FOSSIL-1200 or FOSSIL-1000, depending on the paranoia level of the sysadmin. i have tried to order the result code numbers so that a dumb-down level of 2 provides reasonably usable results without giving away too much detail to malicious clients.\ (**TODO:** `g.json.errorDetailParanoia` is used to set the default dumb-down level, but it is currently set at compile-time. It needs to be moved to a config option. We have a chicken/egg scenario with error reporting and db access there (where the config is stored).) - Once a number is assigned to a given error condition (and actually used somewhere), it may not be changed/redefined. JSON clients need to be able to rely on stable result codes in order to provide adequate error reporting to their clients, and possibly for some error recovery logic as well (i.e. to decide whether to abort or retry). The *tentative* list of result codes is shown in the following table. These numbers/ranges are "nearly arbitrarily" chosen except for the "special" value 0000. **Maintenance reminder:** these codes are defined in [`src/json_detail.h`](/finfo/src/json_detail.h) (enum `FossilJsonCodes`) and assigned default `resultText` values in [`src/json.c:json_err_cstr()`](/finfo/src/json.c). Changes there need to be reflected here (and vice versa). Also, we have assertions in place to ensure that C-side codes are in the range 1000-9999, so do not just go blindly change the numeric ranges used by the enum. **`FOSSIL-0###`: Non-error Category** - `FOSSIL-0000`: Success/not an error. Succesful responses do not contain a resultCode, so clients should never see this. - `FOSSIL-0###`: Reserved for potential future use in reporting non-fatal warnings. **`FOSSIL-1000`: Generic Errors Category** - `FOSSIL-1101`: Invalid request. Request envelope is invalid or missing. - `FOSSIL-1102`: Unknown JSON command. - `FOSSIL-1103`: Unknown/unspecified error - `FOSSIL-1104`: RE-USE - `FOSSIL-1105`: A server-side timeout was reached. (i’m not sure we can actually implement this one, though.) - `FOSSIL-1106`: Assertion failed (or would have had we continued). Note: if an `assert()` fails in CGI/server modes, the HTTP response will be code 500 (Internal Server Error). We want to avoid that and return a JSON response instead. All of that said - there seems to be little reason to implementi this, since assertions are "truly serious" errors. - `FOSSIL-1107`: Allocation/out of memory error. This cannot be reasonably reported because fossil aborts if an allocation fails. - `FOSSIL-1108`: Requested API is not yet implemented. - `FOSSIL-1109`: Panic! Fossil's `fossil_panic()` or `cgi_panic()` was called. In non-JSON HTML mode this produces an HTTP 500 error. Clients "should" report this as a potential bug, as it "possibly" indicates that the C code has incorrect argument- or error handling somewhere. - `FOSSIL-1110`: Reading of artifact manifest failed. Time to contact your local fossil guru. - `FOSSIL-1111`: Opening of file failed (e.g. POST data provided to CLI mode). **`FOSSIL-2000`: Authentication/Access Error Category** - `FOSSIL-2001`: Privileged request was missing authentication token/cookie. - `FOSSIL-2002`: Access to requested resource was denied. Oftentimes the `resultText` property will contain a human-language description of the access rights needed for the given command. - `FOSSIL-2003`: Requested command is not available in the current operating mode. Returned in CLI mode by commands which require HTTP mode (e.g. login), and vice versa. FIXME: now that we can simulate POST in CLI mode, we can get rid of this distinction for some of the commands. - `FOSSIL-2100`: Login Failed. - `FOSSIL-2101`: Anonymous login attempt is missing the "anonymousSeed" property (fetched via [the `/json/anonymousPassword` request](api-auth.md#login-anonymous)). Note that this is more specific form of `FOSSIL-3002`. ONLY FOR TESTING purposes should the remaning 210X sub-codes be enabled (they are potentially security-relevant, in that the client knows which part of the request was valid/invalid): - `FOSSIL-2102`: Name not supplied in login request - `FOSSIL-2103`: Password not supplied in login request - `FOSSIL-2104`: No name/password match found **`FOSSIL-3000`: Usage Error Category** - `FOSSIL-3001`: Invalid argument/parameter type(s) or value(s) in request - `FOSSIL-3002`: Required argument(s)/parameter(s) missing from request - `FOSSIL-3003`: Requested resource identifier is ambiguous (e.g. a shortened hash that matches multiple artifacts, an abbreviated date that matches multiple commits, etc.) - `FOSSIL-3004`: Unresolved resource identifier. A branch/tag/uuid provided by client code could not be resolved. This is a special case of #3006. - `FOSSIL-3005`: Resource already exists and overwriting/replacing is not allowed. e.g. trying to create a wiki page or user which already exists. FIXME? Consolidate this and resource-not-found into a separate category for dumb-down purposes? - `FOSSIL-3006`: Requested resource not found. e.g artifact ID, branch name, etc. **`FOSSIL-4000`: Database-related Error Category** - `FOSSIL-4001`: Statement preparation failed. - `FOSSIL-4002`: Parameter binding failed. - `FOSSIL-4003`: Statement execution failed. - `FOSSIL-4004`: Database locked (this is not used anywhere, but reserved for future use). Special-case DB-related errors... - `FOSSIL-4101`: Fossil Schema out of date (repo rebuild required). - `FOSSIL-4102`: Fossil repo db could not be found. - `FOSSIL-4103`: Repository db is not valid (possibly corrupt). - `FOSSIL-4104`: Check-out not found. This is similar to FOSSIL-4102 but indicates that a local checkout is required (but was not found). Note that the 4102 gets triggered earlier than this one, and so can appear in cases when a user might otherwise expect a 4104 error. Some of those error codes are of course "too detailed" for the client to do anything with (e.g.. 4001-4004), but their intention is to make it easier for Fossil developers to (A) track down problems and (B) support clients who report problems. If a client reports, "I get a FOSSIL-4000, how can I fix it?" then the developers/support personnel can't say much unless they know if it's a 4001, 4002, 4003, 4004, or 4101 (in which case they can probably zero in on the problem fairly quickly, since they know which API call triggered it and they know (from the error code) the general source of the problem). ## Why Standard/Immutable Result Codes are Important - They are easily internationalized (i.e. associated with non-English error text) - Clients may be able to add automatic retry strategies for certain problem types by examining the result code. e.g. if fossil returns a locking or timeout error \[it currently does no special timeout/locking handling, by the way\] the client could re-try, whereas a usage error cannot be sensibly retried with the same inputs. - The "category" structure described above allows us some degree of flexibility in how detailed the reported errors are reported. - While the string prefix "FOSSIL-" on the error codes may seem superfluous, it has one minor *potential* advantage on the client side: when managing several unrelated data sources, these error codes can be immediately identified (by higher-level code which may be ignorant of the data source) as having come from the fossil API. Think "ORA-111" vs. "111". |
Added www/json-api/hacking.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 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 | # JSON API: Hacker's Guide ([⬑JSON API Index](index.md)) Jump to: * [Before Committing Changes](#before-committing) * [JSON C API](#json-c-api) * [Reporting Errors](#reporting-errors) * [Getting Command Arguments](#command-args) * [Creating JSON Data](#creating-json) * [Creating JSON Values](#creating-json-values) * [Converting SQL Query Results to JSON](#query-to-json) This section will only be of interest to those wanting to work on the Fossil/JSON code. That said... If you happen to hack on the code and find something worth noting here for others, please feel free to expand this section. It will only improve via feedback from those working on the code. --- <a id="before-committing"></a> # Before Committing Changes... Because this code lives in the trunk, there are certain guidelines which must be followed before committing any changes: 1. Read the [checkin preparation list](/doc/trunk/www/checkin.wiki). 2. Changes to the files `src/json_*.*`, and its related support code (e.g. `ajax/*.*`), may be made freely without affecting mainline users. Changes to other files, unless they are trivial or made for purposes outside the JSON API (e.g. an unrelated bug fix), must be reviewed carefully before committing. When in doubt, create a branch and post a request for a review. 3. The Golden Rule is: *do not break the trunk build*. <a id="json-c-api"></a> # JSON C API libcson, the underlying JSON API, is a separate project, included in fossil in "amalgamation" form: see `extsrc/cson_amalgamation.[ch]`. It has thorough API docs and a good deal of information is in its wiki: [](https://fossil.wanderinghorse.net/wikis/cson/) In particular: [](https://fossil.wanderinghorse.net/wikis/cson/?page=CsonArchitecture) gives an overview of its architecture. Occasionally new versions of it are pulled into the Fossil tree, but other developers generally need not concern themselves with that. (Trivia: the cson wiki's back-end is fossil using this very JSON API, living on top of a custom JavaScript+HTML5 application.) Only a small handful of low-level fossil routines actually input or output JSON text (only for reading in POST data and sending the response). In the C code we work with the higher-level JSON value abstractions provided by cson (conceptually similar to an XML DOM). All of the JSON-defined data types are supported, and we can construct JSON output of near arbitrary complexity with the caveat that *cyclic data structures are strictly forbidden*, and *will* cause memory corruption, crashes, double free()'s, or other undefined behaviour. Because JSON cannot, without client-specific semantic extensions to JSON, represent cyclic structures, it is not anticipated that this will be a problem/limitation when generating output for fossil. <a id="json-commands"></a> # Architecture of JSON Commands In order to consolidate CLI/HTTP modes for JSON handling, this code foregoes fossil's conventional command/path dispatching mechanism. Only the top-most "json" command/path is dispatched directly by fossil's core. The disadvantages of this are that we lose fossil's conventional help text mechanism (which is based on code comments in the command/path's dispatcher impl) and the ability to write abbreviated command names in CLI mode ("json" itself may be abbreviated, but not the subcommands). The advantages are that we can handle CLI/HTTP modes almost identically (there are a couple minor differences) by unifying them under the same callback functions much more easily. The top-level "json" command/path uses its own dispatching mechanism which uses either the path (in HTTP mode) or CLI positional arguments to dispatch commands (stopping at the first "flag option" (e.g. -foo) in CLI mode). The command handlers are simply callback functions which return a cson\_value pointer (the C representation of an arbitrary JSON value), representing the "payload" of the response (or NULL - not all responses need a payload). On error these callbacks set the internal JSON error state (detailed in a subsection below) and return NULL. The top-level dispatcher then creates a response envelope and returns the "payload" from the command (if any) to the caller. If a callback sets the error state, the top-level dispatcher takes care to set the error information in the response envelope. In summary: - The top-level dispatchers (`json_page_top()` and `json_cmd_top()`) are called by fossil's core when the "json" command/path is called. They initialize the JSON-mode global state, dispatch the requested command, and handle the creation of the response envelope. They prepare all the basic things which the individual subcommands need in order to function. - The command handlers (most are named `json_page_something()`) implement the `fossil_json_f()` callback interface (see [`src/json_detail.h`](/finfo/src/json_detail.h)). They are responsible for permissions checking, setting any error state, and passing back a payload (if needed - not all commands return a payload). It is strictly forbidden for these callbacks to produce any output on stdout/stderr, and doing so effectively corrupts the out-bound JSON and HTTP headers. There is a wrench in all of that, however: the vast majority of fossil's commands "fail fast" - they will `exit()` if they encounter an error. To handle that, the fossil core error reporting routines have been refactored a small bit to operate differently when we are running in JSON mode. Instead of the conventional output, they generate a JSON error response. In HTTP mode they exit with code 0 to avoid causing an HTTP 500 error, whereas in CLI mode they will exit with a non-0 code. Those routines still `exit()`, as in the conventional CLI/HTTP modes, but they will exit differently. Because of this, it is perfectly fine for a command handler to exit via one of fossil's conventional mechanisms (e.g. `db_prepare()` can be fatal, and callbacks may call `fossil_panic()` if they really want to). One exception is `fossil_exit()`, which does _not_ generate any extra output and will `exit()` the app. In the JSON API, as a rule of thumb, `fossil_exit()` is only used when we *want* a failed request to cause an HTTP 500 error, and it is reserved for allocation errors and similar truly catostrophic failures. That said... libcson has been hacked to use `fossil_alloc()` and friends for memory management, and those routines exit on error, so alloc error handling in the JSON command handler code can afford to be a little lax (the majority of *potential* errors clients get from the cson API have allocation failure as their root cause). As a side-note: the vast majority (if not all) of the cson API calls are "NULL-safe", meaning that will return an error code (or be a no-op) if passed NULL arguments. e.g. the following chain of calls will not crash if the value we're looking for does not exist, is-not-a String (see `cson_value_get_string()` for important details), or if `myObj` is NULL: ```c const char * str = cson_string_cstr( // get the C-string form of a cson_string cson_value_get_string( // get its cson_string form cson_object_get(myObj,"foo") // search for key in an Object ) ); ``` If `"foo"` is not found in `myObj` (or if `myObj` is NULL) then v will be NULL, as opposed to stepping on a NULL pointer somewhere in that call chain. Note that all cson JSON values except Arrays and Objects are *immutable* - you cannot change a string's or number's value, for example. They also use reference counting to manage ownership, as documented and demonstrated on this page: [](https://fossil.wanderinghorse.net/wikis/cson/?page=TipsAndTricks) In short, after creating a new value you must eventually *either* add it to a container (Object or Array) to transfer ownership *or* call `cson_value_free()` to clean it up (exception: the Fossil/JSON command callbacks *return* a value to transfer ownership to the dispatcher). Sometimes it's more complex than that, but not normally. Any given value may legally be stored in any number of containers (or multiple times within one container), as long as *no cycles* are introduced (cycles *will* cause undefined behaviour). Ownership is shared using reference counting and the value will eventually be freed up when its last remaining reference is freed (e.g. when the last container holding it is cleaned up). For many examples of using cson in the context of fossil, see the existing `json_page_xxx()` functions in `json_*.c`. <a id="reporting-errors"></a> # Reporting Errors To report an error from a command callback, one abstractly needs to: - Set g.json.resultCode to one of the `FSL_JSON_E_xxx` values (defined in [`src/json_detail.h`](/finfo/src/json_detail.h)). - *Optionally* set `g.zErrMsg` to contain the (dynamically-allocated!) error string to be sent to the client. If no error string is set then a standard/generic string is used for the given error code. - Clean up any resources created so far by the handler. - Return NULL. If it returns non-NULL, the dispatcher will destroy the value and not include it in the error response. That normally looks something like this: ``` if(!g.perm.Read){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); return NULL; } ``` `json_set_err()` is a variadic printf-like function, and can use the printf extensions supported by mprintf() and friends (e.g. `%Q` and `%q`) (but they are normally not needed in the context of JSON). If the error string is NULL or empty then `json_err_cstr(errorCode)` is used to fetch the standard/generic error string for the given code. When control returns to the top-level dispatching function it will check `g.json.resultCode` and, if it is not 0, create an error response using the `g.json.resultCode` and `g.zErrMsg` to construct the response's `resultCode` and `resultText` properties. If a function wants to output an error and exit by itself, as opposed to returning to the dispatcher, then it must behave slightly differently. See the docs for `json_err()` (in [`src/json.c`](/finfo/src/json.c)) for details, and search that file for various examples of its usage. It is also used by fossil's core error-reporting APIs, e.g. `fossil_panic()` (defined in [`src/main.c`](/finfo/src/main.c)). That said, it would be "highly unusual" for a callback to need to do this - it is *far* simpler (and more consistent/reliable) to set the error state and return to the dispatcher. <a id="command-args"></a> # Getting Command Arguments Positional parameters can be fetched usinig `json_command_arg(N)`, where N is the argument position, with position 0 being the "json" command/path. In CLI mode positional arguments have their obvious meaning. In HTTP mode the request path (or the "command" request property) is used to build up the "command path" instead. For example: CLI: `fossil json a b c` HTTP: `/json/a/b/c` HTTP POST or CLI with `--json-input`: /json with POSTed envelope `{"command": "a/b/c" …}` Those will have identical "command paths," and `json_command_path(2)` would return the "b" part. Caveat: a limitation of this support is that all CLI flags must come *after* all *non-flag* positional arguments (e.g. file names or subcommand names). Any argument starting with a dash ("-") is considered by this code to be a potential "flag" argument, and all arguments after it are ignored (because the generic handling cannot know if a flag requires an argument, which changes how the rest of the arguments need to be interpreted). To get named parameters, there are several approaches (plus some special cases). Named parameters can normally come from any of the following sources: - CLI arguments, e.g. `--foo bar` - GET parameters: `/json/...?foo=bar` - Properties of the POST envelope - Properties of the `POST.payload` object (if any). To try to simplify the guessing process the API has a number of functions which behave ever so slightly differently. A summary: - `json_getenv()` and `json_getenv_TYPE()` search the so-called "JSON environment," which is a superset of the GET/POST/`POST.payload` (if `POST.payload` is-a Object). - `json_find_option_TYPE()`: searches the CLI args (only when in CLI mode) and the JSON environment. - The use of fossil's `P()` and `PD()` macros is discourages in JSON callbacks because they can only handle String data from the CLI or GET parameters (not POST/`POST.payload`). (Note that `P()` and `PD()` *normally* also handle POSTed keys, but they only "see" values posted as form-urlencoded fields, and not JSON format.) - `find_option()` (from `src/main.c`) "should" also be avoided in JSON API handlers because it removes flag from the g.argv arguments list. That said, the JSON API does use `find_option()` in several of its option-finding convenience wrappers. For example code: the existing command callbacks demonstrate all kinds of uses and the various styles of parameter/option inspection. Check out any of the functions named `json_page_SOMETHING()`. <a href="creating-json"></a> # Creating JSON Data <a href="creating-json-values"></a> ## Creating JSON Values cson has a fairly rich API for creating and manipulating the various JSON-defined value types. For a detailed overview and demonstration i recommend reading: [](https://fossil.wanderinghorse.net/wikis/cson/?page=HowTo) That said, the Fossil/JSON API has several convenience wrappers to save a few bytes of typing: - `json_new_string("foo")` is easier to use than `cson_value_new_string("foo", 3)`, and `json_new_string_f("%s","foo")` is more flexible. - `json_new_int()` is easier to type than `cson_value_new_integer()`. - `cson_output_Blob()` and `cson_parse_Blob()` can write/read JSON to/from fossil `Blob`-type objects. It also provides several lower-level JSON features which aren't of general utility but provide necessary functionality for some of the framework-level code (e.g. `cson_data_dest_cgi()`), which is only used by the deepest of the JSON internals). <a href="query-to-json"></a> ## Converting SQL Query Results to JSON The `cson_sqlite3_xxx()` family of functions convert `sqlite3_stmt` rows to Arrays or Objects, or convert single columns to a JSON-compatible form. See `json_stmt_to_array_of_obj()`, `json_stmt_to_array_of_array()` (both in `src/json.c`), and `cson_sqlite3_column_to_value()` and friends (in `extsrc/cson_amalgamation.h`). They work in an intuitive way for numeric types, but they optimistically/natively *assume* that any fields of type TEXT or BLOB are actually UTF8 data, and treat them as such. cson's string class only handles UTF8 data and it is semantically illegal to feed them anything but UTF8. Violating this will likely result in down-stream errors (e.g. when emiting the JSON string output). **The moral of this story is:** *do not use these APIs to fetch binary data*. JSON doesn't do binary and the `cson_string` class does not protect itself against clients feeding it non-UTF8 data. Here's a basic example of using these features: ```c Stmt q = empty_Stmt; cson_value * rows = NULL; db_prepare(&q, "SELECT a AS a, b AS b, c AS c FROM foo"); rows = json_stmt_to_array_of_obj( &sql, NULL ); db_finalize(&q); // side note: if db_prepare()/finalize() fail (==they exit()) // then a JSON-format error reponse will be generated. ``` On success (and if there were results), `rows` is now an Array value, each entry of which contains an Object containing the fields (key/value pairs) of each row. `json_stmt_to_array_of_array()` returns each row as an Array containing the column values (with no column name information). **Note the seemingly superfluous use of the "AS" clause in the above SQL.** Having them is actually significant! If a query does *not* use AS clauses, the row names returned by the db driver *might* be different than they appear in the query (this is documented behaviour of sqlite3). Because the JSON API needs to return stable field names, we need to use AS clauses to be guaranteed that the db driver will return the column names we want. Note that the AS clause is often used to translate column names into something more JSON-conventional or user-friendly, e.g. "SELECT cap AS capabilities...". Alternately, we can convert the individual `sqlite3_stmt` column values to JSON using `cson_sqlite3_column_to_value()`, without refering directly to the db-reported column name. |
Added www/json-api/index.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 | # JSON API Index This is the client-side documentation of Fossil's JSON API. The JSON API aims to provide access to many of the primary fossil features via AJAX-style interfaces. * [Introduction](intro.md) * [General API Conventions](conventions.md) * [Tips & Tricks](tips.md) * [Hacking Guide](hacking.md) General warnings regarding the APIs linked to in the following list: - **NOTE** that request/response examples shown in the individual API pages do not show [the standard request/response envelope](conventions.md) (for brevity and sanity). - **Achtung:** just because a given feature is described as being implemented does not mean that the implementation is "final" - it may be changed at any time until we find/implement useful APIs. The APIs, alphabetically by category: * [Artifact Info](api-artifact.md) * [Authentication](api-auth.md) * [Branches](api-branch.md) * [Checkout Status](api-checkout.md) * [Config](api-config.md) * [Diffs](api-diff.md) * [Directory Listing](api-dir.md) * [File Info](api-finfo.md) * [The Obligatory Misc. Category](api-misc.md) * [Repository Stats](api-stat.md) * [Settings](api-settings.md) * [SQL Query](api-query.md) * [Tags](api-tag.md) * [Tickets](api-ticket.md) * [Timeline](api-timeline.md) * [User Management](api-user.md) * [Version](api-version.md) * [Wiki](api-wiki.md) |
Added www/json-api/intro.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 | # JSON API Introduction ([⬑JSON API Index](index.md)) Jump to: * [Why?](#why) * [Building JSON Support](#builing) * [Goals & Non-goals](#goals) * [Potential Client-side Uses](#potential-uses) * [Technical Problems and Considerations](#considerations) --- <a id="why"></a> # Why? In September, 2011, Fossil contributor Stephan Beal had the great pleasure of meeting D. Richard Hipp, Fossil's author, for lunch in Munich, Germany. During the conversation Richard asked, "what does Fossil need next?" Stephan's first answer was, "refactoring into a library/client, as opposed to a monolithic app." We very quickly agreed that the effort required would be "herculean," and second choice was voiced, "a JSON API." They briefly discussed the idea and Richard gave his blessing. That night work began. Why a JSON API? Because it is the next best thing to the "librification" of Fossil, in that it makes Fossil's features available to near-arbitrary applications using a simple, globally available data format. <a id="building"></a> # Building JSON Support In environments supported by fossil's `configure` script, simply pass `--enable-json` to it: ``` $ ./configure --prefix=$HOME --enable-json ... ``` When built without that option, JSON support is disabled. **When reconfiguring the source tree**, ***always be sure to do a "make clean"*** (or equivalent for your platform) between builds (preferably *before* reconfiguring), to ensure that everything is rebuilt properly. If you fail to do that after enabling JSON on a tree which has already been built, most of the sources will not be rebuilt properly. The reason is that the JSON files are actually unconditionally compiled, but when built without `--enable-json` they compile to empty object files. Thus after a reconfigure the (empty) object files are still up-to-date vis-a-vis the sources, and won't be rebuilt. To build Fossil with JSON support on Windows using the Microsoft C compiler: ``` cd win nmake -f Makefile.msc FOSSIL_ENABLE_JSON=1 ``` It has been seen to compile in VC versions 6 and higher. <a id="goals"></a> # Goals & Non-goals The API described here is most certainly not [*REST*](http://en.wikipedia.org/wiki/Representational_state_transfer)-conformant, but is instead JSON over HTTP. The error reporting techniques of the REST conventions (using HTTP error codes) "does not mesh" with my ideas of separation of transport- vs. app-side errors. Additionally, REST requires HTTP methods which are not specified by CGI (namely PUT and DELETE), which means we can't possibly implement a REST-compatible interface on top of fossil (which uses CGI mode even for its built-in server). The **overall goals** of this effort include: - A JSON-based API off of which clients can build customized Fossil UIs and special-purpose applications. e.g. a desktop notification applet which polls for new timeline data. - Arbitrary JSON-using clients should be able to use it. Though JSON originates from JavaScript, it is truly a cross-platform data format with a very high adoption rate. (There’s even a JSON implementation for Oracle PL/SQL.) - Fossil’s CGI and Server modes are the main targets and should be supported equally. CLI JSON mode is of secondary concern (but is in practice easier to test, so it’s generally implemented first). The ***non-goals*** include: - We won’t be able to implement *every* feature of Fossil via a JSON interface, and we won’t try to. - Binary data (e.g. commits of binary files or downloading ZIP files) is not an initial goal, but "might be interesting" once the overall infrastructure is in place and working well. See below for more details about binary data in JSON. - A "pure REST" interface is seemingly not possible due to REST relying on HTTP methods not specified in the CGI standard (PUT and DELETE). Additionally, REST-style error reporting cannot be used by non-HTTP clients (which this code supports). Adding JSON support also gives us a framework off of which to build/enhance other features. Some examples include: - **Internationalization**. Errors are reported via standard codes and the raw artifact data is language-independent. - The ability to author **special-case clients**, e.g. a ticket poller. - Use **arbitrary HTTP-capable languages** to implement such tools. Programming languages which can execute programs and intercept their stdout output can use the JSON API via a local fossil binary. - **Automatable tests.** Many of fossil's test results currently have to be "visually reviewed" for correctness after changes (e.g. changes in the HTML interface). JSON structures can be programmatically checked for correctness. Artifacts are immutable, which allows us to be very specific in what data to expect as output (for artifact-specific requests the payload data will often (but not always) be the same across all requests and all time). <a id="potential-uses"></a> # Potential Client-side Uses Some of the potential client-side uses of this API include... - Custom apps/applets to fetch timeline/ticket/etc. information from arbitrary repositories. There are many possibilities here, including "dashboard" sites which monitor several repositories. - Custom post-commit triggers, by polling for changes and reacting to them (e.g. sending mails). - A custom wiki front-end which uses fossil as the back-end storage, inheriting its versioning and user access support while providing a completely custom wiki-centric UI. Such a wiki need not have, on the surface, anything to do with fossil or source control, as fossil would just become a glorified wiki back-end. This approach also allows clients to serve wiki pages in a format of their choice - since all rendering would be done client-side, they could use whatever format they like. <a id="considerations"></a> # Technical Problems and Considerations A random list of considerations which need to be made and potential problem areas... - **Binary data:** JSON is a text serialization method, and it takes up the “payload†area of each HTTP request, so there is no reasonable way to include binary data in the JSON message without some sort of codec like Base64, for which there is no provision in the current JSON API. You will therefore find no JSON API for committing changes to a file in the repository, for example. Other Fossil APIs such as [`/raw`](/help?cmd=/raw) or [`/fileedit`](../fileedit-page.md) may serve you better. - **64-bit integers:** The JSON standard does not specify integer precision, because it targets many different platforms, and not all of them can support more than 32 bits. JavaScript (from which JSON derives) supports 53 bits of integer precision, which may affect how a given client-side JSON implementation sends large integers to Fossil’s JSON API. Our JSON parser can cope with integers larger than 32 bits on input, and it can emit them, but it requires platform support. If you’re running Fossil on a 64-bit host, you should not run into problems in this area, but if you’re on a legacy 32-bit only or a mixed 32/64-bit system, it’s possible that some integers in the API could be clipped. Realize however that this is a rare case: Fossil currently cannot store files large enough to exceed a 32-bit `size_t` value, and `time_t` won’t roll past 32-bit integers until 2038. We’re aware of no other uses of integers in this API that could even in principle exceed the range of a 32-bit integer. - **Timestamps:** For portability, this API uses UTC Unix epoch timestamps. (`time_t`) They are the most portable time representation out there, easily usable in most programming environments. (In hindsight, we might better have used a higher-precision time format, but changing that now would break API compatibility.) |
Added www/json-api/tips.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 | # JSON API: Tips and Tricks ([⬑JSON API Index](index.md)) Jump to: * [Beware of Content-Type and Encoding...](#content-type) * [Using `curl` and `wget`](#curl-wget) * [Example JavaScript](#javascript) * [Demo Apps](#demo-apps) --- <a id="content-type"></a> # Beware of Content-Type and Encoding... When posting data to fossil, make sure that the request sends: - **Content-Type** of `application/json`. Fossil also (currently) accepts `application/javascript` and `text/plain` as JSON input, but `application/json` is preferred. The client may optionally send `;charset=utf-8` with the Content-Type, but any other encoding produces undefined results. Behaviour without the charset or with `;charset=utf-8` suffix is identical. - **POST data must be an non-form-encoded JSON string** (ASCII or UTF-8). jQuery, by default, form-urlencodes it, which the fossil json bits cannot read. e.g. post the result of `JSON.stringify(requestObject)`, without any additional encoding on top of it. - **When POSTing via jQuery**, set these AJAX options: - `contentType:'application/json'` - `dataType:'text'` - `data:JSON.stringify(requestObject)` - **When POSTing via XMLHttpRequest** (XHR), be sure to: - `xhr.open( … )` - `xhr.setRequestHeader("Content-Type", "application/json")` - `xhr.send( JSON.stringify( requestObject ) )` The response will be (except in the case of an HTTP 500 error or similar) a JSON or JSONP string, ready to be parsed by your favourite `JSON.parse()` implementation or `eval()`'d directly. <a id="curl-wget"></a> ## Using `curl` and `wget` Both [curl](https://curl.haxx.se/) and [wget](https://www.gnu.org/software/wget/) can be used to post data to this API from the command line or scripts, but both require an extra parameter to set the request encoding. Example: ```console $ cat x.json { "payload": { "sql": "SELECT * FROM reportfmt limit 1", "format": "o" } } # Fossil has been started locally with: # fossil server --localauth # which allows the following requests to work without extra # authenticaion: $ wget -q -O- \ --post-file=x.json \ --header="Content-Type: application/json" \ 'http://localhost:8080/json/query' $ curl \ --data-binary @x.json \ --header 'Content-Type: application/json' \ 'http://localhost:8080/json/query' ``` The relevant parts for encoding are the `--header` flag for `wget` and `curl`, noting that they have different syntaxes for each (`--header=X` vs `--header X`). <a id="javascript"></a> # Example JavaScript (Browser and Shell) In the fossil source tree, [in the ajax directory](/dir/ajax), is test/demo code implemented in HTML+JavaScript. While it is still quite experimental, it demonstrates one approach to creating client-side wrapper APIs for remote Fossil/JSON repositories. There is some additional JS test code, which uses the Rhino JS engine (i.e. from the console, not the browser), under [`ajax/i-test`](/dir/ajax/-itest). That adds a Rhino-based connection back-end to the AJAJ API and uses it for running integration-style tests against an arbitrary JSON-capable repository. <a id="demo-apps"></a> # Demo Apps Known in-the-wild apps using this API: - The wiki browsers/editors at [](https://fossil.wanderinghorse.net/wikis/) |
Added www/loadmgmt.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 | # Managing Server Load A Fossil server is very efficient and normally presents a very light load on the server. The Fossil [self-hosting server][sh] is a 1/24th slice VM at [Linode.com][lin] hosting 65 other repositories in addition to Fossil, including some very high-traffic sites such as <http://www.sqlite.org> and <http://system.data.sqlite.org>. This small VM 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. 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 [`/zip`](/help/zip), [`/tarball`](/help/tarball), [`/annotate`](/help/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. Fossil provides two capabilities to help avoid server overload problems due to excessive requests to expensive pages: 1. 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. 2. Page requests can be configured to fail with a “[503 Server Overload][503]†HTTP error if any request is received while the host load average is too high. Both of these load-control mechanisms are turned off by default, but they are recommended for high-traffic sites. Users with [admin permissions](caps/index.md) are exempt from these restrictions, provided they are logged in before the load gets too high (login is disabled under high load). The webpage cache is activated using the [`fossil cache init`](/help/cache) 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 to give the Fossil server write permission for the user ID of the web server; this is a separate file in the same directory and with the same name as the repository but with the “`.fossil`†suffix changed to “`.cache`â€. To activate the server load control feature visit the Admin → Access setup page in the administrative web interface; in the “**Server Load Average Limit**†box enter the load average threshold above which “503 Server Overload†replies will be issued for expensive requests. On the self-hosting Fossil server, that value is set to 1.5, but you could easily set it higher on a multi-core server. The maximum load average can also be set on the command line using commands like this: fossil set max-loadavg 1.5 fossil all set max-loadavg 1.5 The second form is especially useful for changing the maximum load average simultaneously on a large number of repositories. Note that this load-average limiting feature is only available on operating systems that support the [`getloadavg()`][gla] API. Most modern Unix systems have this interface, but Windows does not, so the feature will not work on Windows. Because Linux implements `getloadavg()` by accessing the `/proc/loadavg` virtual file, you will need to make sure `/proc` is available to the Fossil server. The most common reason for it to not be available is that you are running a Fossil instance [inside a `chroot(2)` jail](./chroot.md) and you have not mounted the `/proc` virtual file system inside that jail. On the [self-hosting Fossil repositories][sh], this was accomplished by adding a line to the `/etc/fstab` file: chroot_jail_proc /home/www/proc proc ro 0 0 The `/home/www/proc` pathname should be adjusted so that the `/proc` component is at the root of the chroot jail, of course. To see if the load-average limiter is functional, visit the [`/test_env`][hte] 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. This can either be because it is in a `chroot(2)` 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. [503]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4 [hte]: /help?cmd=/test_env [gla]: https://linux.die.net/man/3/getloadavg [lin]: http://www.linode.com [sh]: ./selfhost.wiki |
Added www/makefile.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 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 | <title>The Fossil Build Process</title> <h1>1.0 Introduction</h1> The build process for Fossil is tricky in that the source code needs to be processed by three different preprocessor programs before it is compiled. Most users will download a [https://fossil-scm.org/home/uv/download.html | precompiled binary] so this is of no consequence to them, and even those who want to compile the code themselves can use one of the [./build.wiki | existing makefiles]. So must people do not need to be concerned with the build complexities of Fossil. But hard-core developers who desire a deep understanding of how Fossil is put together can benefit from reviewing this article. <h1 id="srctour">2.0 Source Code Tour</h1> The source code for Fossil is found in the [/dir?ci=trunk&name=src | src/] subdirectory of the source tree. The src/ subdirectory contains all code, including the code for the separate preprocessor programs. Each preprocessor program is a separate C program implemented in a single file of C source code. The three preprocessor programs are: 1. [/file/tools/mkindex.c | mkindex.c] 2. [/file/tools/translate.c | translate.c] 3. [/file/tools/makeheaders.c | makeheaders.c] Fossil uses [http://www.sqlite.org/ | SQLite] for on-disk storage. The SQLite implementation is contained in three source code files that do not participate in the preprocessing steps. These three files that implement SQLite are: 4. sqlite3.c 5. sqlite3.h 6. shell.c All three SQLite source files are byte-for-byte copies of files by the same name in the standard [http://www.sqlite.org/amalgamation.html | amalgamation]. The sqlite3.c file implements the database engine. The shell.c file implements the command-line shell, which is accessed in fossil using the [/help?cmd=sqlite3 | fossil sql] command. The shell.c command-line shell uses the [https://github.com/antirez/linenoise | linenoise] library to implement line editing. linenoise comprises two source files which were copied from the upstream repository with only very minor portability edits: 7. linenoise.c 8. linenoise.h The TH1 script engine is implemented using files: 9. th.c 10. th.h The proprocessing steps are omitted for all of these imported files. The VERSION.h header file is generated from other information sources using a small program called: 11. [/file/tools/mkversion.c | 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: 12 [/file/tools/mkbuiltin.c | mkbuiltin.c] Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl] script used to implement the --tk option to [/help?cmd=diff| fossil diff], the [/file/src/markdown.md | markdown documentation], and the various CSS scripts, headers, and footers used to implement built-in skins. New resources files are added to the "extra_files" variable in [/file/tools/makemake.tcl | makemake.tcl]. The src/ subdirectory also contains documentation about the makeheaders preprocessor program: 13. [../tools/makeheaders.html | makeheaders.html] Click on the link to read this documentation. In addition there is a [http://www.tcl-lang.org/ | Tcl] script used to build the various makefiles: 14. 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 rebuilt. There is an optional code verification step implemented using 15. [/file/tools/codecheck1.c | codecheck1.c] This file implements a small utility program ("codecheck1") that scans other Fossil source files looking for errors in printf-style format strings. The codecheck1 utility detects missing or surplus arguments on printf-like functions and dangerous uses of "%s" that might permit SQL injection or cross-site scripting attacks. This code check step is run automatically on each build of Fossil, and can also be run separately by typing "make codecheck". Note that the built-in printf format checking of GCC does not function for Fossil since Fossil implements its own printf (in the [/file/src/printf.c | printf.c] source file) that includes special features and formatting letters that are useful to Fossil. The codecheck1 utility can be seen as an enhanced application-specific replacement for the GCC printf format checker. Finally, there is one of the makefiles generated by makemake.tcl: 16. 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 this writing) are C source code files that are subject to the preprocessing steps described below. In the sequel, we will call these other files "src.c" in order to have a convenient name. The reader should understand that whenever "src.c" or "src.h" is used in the text that follows, we really mean all (79) other source files other than the exceptions described above. <h1>3.0 Automatically generated files</h1> The "VERSION.h" header file contains some C preprocessor macros that identify the version of Fossil that is to be build. The VERSION.h file is generated automatically from information extracted from the "manifest", "manifest.uuid", and "VERSION" source files in the root directory of the source tree. (The "manifest" and "manifest.uuid" files are automatically generated and 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: tools/mkversion.c. To run the VERSION.h generator, first compile the tools/mkversion.c source file into a command-line program (named "mkversion.exe") then run: <pre> mkversion.exe manifest.uuid manifest VERSION >VERSION.h </pre> 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: tools/mkbuiltin.c. The builtin_data.h file contains C-language 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: <pre> mkbuiltin.exe diff.tcl <i>OtherFiles...</i> >builtin_data.h </pre> 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. <h1 id="preprocessing">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. <h2>4.1 The mkindex preprocessor</h2> The mkindex program scans the "src.c" source files looking for special comments that identify routines that implement various Fossil commands, web interface methods, and help text comments. The mkindex program generates some C code that Fossil uses in order to dispatch commands and HTTP requests and to show on-line help. Compile the mkindex program from the mkindex.c source file. Then run: <pre> ./mkindex src.c >page_index.h </pre> Note that "src.c" in the above is a stand-in for the (79) regular source files of Fossil - all source files except for the exceptions described in section 2.0 above. The output of the mkindex program is a header file that is #include-ed by the main.c source file during the final compilation step. <h2>4.2 The translate preprocessor</h2> The translate preprocessor looks for lines of source code that begin with "@" and converts those lines into string constants or (depending on context) into special "printf" operations for generating the output of an HTTP request. The translate preprocessor is a simple C program whose sources are in the translate.c source file. The translate preprocess is run on each of the other ordinary source files separately, like this: <pre> ./translate src.c >src_.c </pre> In this case, the "src.c" file represents any single source file from the set of ordinary source files as described in section 2.0 above. Note that each source file is translated separately. By convention, the names of the translated source files are the names of the input sources with a single "_" character at the end. But a new makefile can use any naming convention it wants - the "_" is not critical to the build process. After being translated, the output files (the "src_.c" files) should be used for all subsequent preprocessing and compilation steps. <h2>4.3 The makeheaders preprocessor</h2> For each C source module "src.c", there is an automatically generated header module "src.h" that contains all of the datatype and procedure declarations needed by the source module. These header files are generated automatically by the makeheaders program. The sources to makeheaders are contained in a single file "makeheaders.c". Additional documentation on makeheaders can be found in [../tools/makeheaders.html | tools/makeheaders.html]. The makeheaders program is run once. It scans all inputs source files and generates header files for each one. Note that the sqlite3.c and shell.c source files are not scanned by makeheaders. Makeheaders only runs over "ordinary" source files, not the exceptional source files. However, makeheaders also uses some extra header files as input. The general format is like this: <pre> makeheaders src_.c:src.h sqlite3.h th.h VERSION.h </pre> In the example above the "src_.c" and "src.h" names represent all of the (79) ordinary C source files, each as a separate argument. <h1>5.0 Compilation</h1> After all generated files have been created and all ordinary source files have been preprocessed, the generated and preprocessed files can be combined into a single executable using a C compiler. This can be done 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_DBSTAT_VTAB=1 * -DSQLITE_ENABLE_FTS4=1 * -DSQLITE_ENABLE_LOCKING_STYLE=0 * -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 * -DSQLITE_THREADSAFE=0 * -DSQLITE_DEFAULT_FILE_FORMAT=4 * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1 The first three symbol definitions above are required; the others are merely recommended. Extension loading is omitted as a security measure. The dbstat virtual table is needed for the [/help?cmd=/repo-tabsize|/repo-tabsize] page. 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() to prevent it from colliding with the real main() in Fossil, and to give Fossil an entry point to jump to when the [/help/sqlite3 | fossil sql] command is invoked. All the other source code files can be compiled without any special options. <h1>6.0 Linkage</h1> Fossil needs to be linked against [http://www.zlib.net | zlib]. If the HTTPS option is enabled, then it will also need to link against the appropriate SSL implementation. And, of course, Fossil needs to link against the standard C library. No other libraries or external dependences are used. <h1>7.0 Debugging</h1> Debug mode is controlled via FOSSIL_DEBUG preprocessor macro which could be set explicitly at the make command for the target platform. However, in practice it is instead recommended to add a respective configure option for the target platform and then perform a clean build. This way the Debug flags are consistently applied across the whole build process. For example, use these Debug flags in addition to other flags passed to the configure scripts: On Linux, *NIX and similar platforms: <pre> ./configure --fossil-debug </pre> On Windows: <pre> win\buildmsvc.bat FOSSIL_DEBUG=1 </pre> The resulting fossil binary could then be loaded into a platform-specific debugger. Source files displayed in the debugger correspond to the ones generated from the translation stage of the build process, that is what was actually compiled into the object files. <h1>8.0 See Also</h1> * [./tech_overview.wiki | A Technical Overview Of Fossil] * [./adding_code.wiki | How To Add Features To Fossil] |
Added www/mdtest/test1.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 | # Markdown Link-test This document exist solely as a test for some of the hyperlinking capabilities of Markdown as implemented by Fossil. ## Relative-Path Links * The index: [](../index.wiki) * Load management: [](../loadmgmt.md) * Site-map: [](../../../../sitemap) * Windows CGI: [](../server/windows/cgi.md) ## The Magic $ROOT Path Prefix In text of the form `href="$ROOT/..."` in the HTML that markdown generates, the $ROOT is replaced by the complete URI for the root of the document tree. Note that the $ROOT translation only occurs within the `<a href="...">` element, not within the text of the hyperlink. So you should see the $ROOT text on this page, but if you mouse-over the hyperlink the $ROOT value should have been expanded to the actual document root. * Timeline: []($ROOT/timeline) * Site-map: []($ROOT/sitemap) The $ROOT prefix on markdown links is superfluous. The same link works without the $ROOT prefix. (Though: the $ROOT prefix is required for HTML documents.) * Timeline: [](/timeline) * Help: [](/help?cmd=help) * Site-map: [](/sitemap) ## The Magic $CURRENT Document Version Translation In URI text of the form `.../doc/$CURRENT/...` the $CURRENT value is converted to the version number of the document currently being displayed. This conversion happens after translation into HTML and only occurs on href='...' attributes so it does not occur for plain text. * Document index: [](/doc/$CURRENT/www/index.wiki) Both the $ROOT and the $CURRENT conversions can occur on the same link. * Document index: []($ROOT/doc/$CURRENT/www/index.wiki) The translations must be contained within HTML markup in order to work. They do not work for ordinary text that appears to be an href= attribute. * `x href='$ROOT/timeline'` * `x action="$ROOT/whatever"` * `x href="https://some-other-site.com/doc/$CURRENT/tail"` |
Added www/mirrorlimitations.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 | # Limitations On Git Mirrors The "<tt>[fossil git export](/help?cmd=git)</tt>" command can be used to mirror a Fossil repository to Git. ([Setup instructions](./mirrortogithub.md) and an [example](https://github.com/drhsqlite/fossil-mirror).) But the export to Git is not perfect. Some information is lost during export due to limitations in Git. This page describes what content of Fossil is not included in an export to Git. ## (1) Wiki, Tickets, Technotes, Forum Git only supports version control. The additional features of Fossil such as Wiki, Tickets, Technotes, and the Forum are not supported in Git, so those features are not included in an export. Third-party Git based tooling may add some of these features (e.g. GitHub, GitLab) but because their data are not stored in the Git repository, there is no single destination for Fossil to convert its equivalent data *to*. For instance, Fossil tickets do not become GitHub issues, because that is a proprietary feature of GitHub separate from Git proper, stored outside the repository on the GitHub servers. You can also see the problem in its inverse case: you do not get a copy of your GitHub issues when cloning the Git repository. You *do* get the Fossil tickets, wiki, forum posts, etc. when cloning a remote Fossil repo. ## (2) Cherrypick Merges The Git client supports cherrypick merges but does not record the cherrypick parent(s). Fossil tracks cherrypick merges in its repository and displays cherrypicks in its timeline. (As an example, the dashed lines [here](/timeline?c=0a9f12ce6655b7a5) are cherrypicks.) Because Git does not have a way to represent this same information in its repository, the history of Fossil cherrypicks cannot be exported to Git, only their direct effects on the managed file data. ## (3) Named Branches Git has only limited support for named branches. Git identifies the head check-in of each branch. Depending on the check-in graph topology, this is sufficient to infer the branch for many historical check-ins as well. However, complex histories with lots of cross-merging can lead to ambiguities. Fossil keeps track of historical branch names unambiguously, but the extra details about branch names that Fossil keeps at hand cannot be exported to Git. ## (4) Non-unique Tags Git requires tags to be unique: each tag must refer to exactly one check-in. Fossil does not have this restriction, and so it is common in Fossil to tag multiple check-ins with the same name. For example, it is common in Fossil to tag each check-in creating a release both with a unique version tag *and* a common tag like "release" so that all historical releases can be found at once. ([Example](/timeline?t=release).) Git does not allow this. The "release" tag must refer to just one check-in. The work-around is that the non-unique tag in the Git export is made to refer to only the most recent check-in with that tag. This is why the ["release" tag view][ghrtv] in the GitHub mirror of this repository shows only the latest release version; contrast the prior example. Both URLs are asking the repository the same question, but because of Git's relatively impoverished data model, it cannot give the same answer that Fossil does. [ghrtv]: https://github.com/drhsqlite/fossil-mirror/tree/release ## (5) Amendments To Check-ins Check-ins are immutable in both Fossil and Git. However, Fossil has a mechanism by which tags can be added to its repository to provide after-the-fact corrections to prior check-ins. For example, tags can be added to check-ins that correct typos in the check-in comment. The original check-in is immutable and so the original comment is preserved in addition to the correction. But software that displays the check-ins knows to look for the comment-change tag and if present displays the corrected comment rather than the original. ([Example](/info/8ed91bbe44d0d383) changing the typo "os" into "so".) Git has no mechanism for providing corrections or clarifications to historical check-ins. When exporting from Fossil to Git, the latest corrections to a Fossil check-in are used to generate the corresponding Git check-in. But once the Git check-in has been created, any subsequent corrections are omitted as there is no way to transfer them to Git. |
Added www/mirrortogithub.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 | # How To Mirror A Fossil Repository On GitHub Beginning with Fossil version 2.9, you can mirror a Fossil-based project on GitHub (with [limitations](./mirrorlimitations.md)) by following these steps: 1. Create an account on GitHub if you do not have one already. Log into that account. 2. Create a new project. GitHub will ask you if you want to prepopulate your project with various things like a README file. Answer "no" to everything. You want a completely blank project. GitHub will then supply you with a URL for your project that will look something like this: https://github.com/username/project.git 3. Back on your workstation, move to a checkout for your Fossil project and type: <blockquote> <pre> $ fossil git export /path/to/git/repo --autopush \ https://<font color="orange">username</font>:<font color="red">password</font>@github.com/username/project.git </pre> </blockquote> In place of the <code>/path/to...</code> argument above, put in some directory name that is <i>outside</i> of your Fossil checkout. If you keep multiple Fossil checkouts in a directory of their own, consider using <code>../git-mirror</code> to place the Git export mirror alongside them, for example. Fossil will create this directory if necessary. This directory will become a Git repository that holds a translation of your Fossil repository. The <code>--autopush</code> option tells Fossil that you want to push the Git translation up to GitHub every time it is updated. The URL parameter is the same as the one GitHub gave you, but with your GitHub <font color="orange">username</font> and <font color="red">password</font> added. If your GitHub account uses two-factor authentication (2FA), you will have to <a href="https://github.com/settings/tokens">generate a personal access token</a> and use that in place of your actual password in the URL. This token should have “repo†scope enabled, only. You can also run the command above outside of any open checkout of your project by supplying the “<code>-R repository</code>†option. 4. Get some coffee. Depending on the size of your project, the initial "<code>fossil git export</code>" command in the previous step might run for several minutes. 5. And you are done! Assuming everything worked, your project is now mirrored on GitHub. 6. Whenever you update your project, simply run this command to update the mirror: $ fossil git export Unlike with the first time you ran that command, you don’t need the remaining arguments, because Fossil remembers those things. Subsequent mirror updates should usually happen in a fraction of a second. 7. To see the status of your mirror, run: $ fossil git status ## Notes: * Unless you specify --force, the mirroring only happens if the Fossil repo has changed, with Fossil reporting "no changes", because Fossil does not care about the success or failure of the mirror run. If a mirror run failed (for example, due to an incorrect password, or a transient error at github.com), Fossil will not retry until there has been a repo change or --force is supplied. * The mirroring is one-way. If you check in changes on GitHub, those changes will not be reabsorbed by Fossil. There are technical problems that make a two-way mirror all but impossible. (This is not to be confused with the ability to import a Fossil mirror from Github back into a Fossil repository. That works, but it is not a mirror.) This also means that you cannot accept pull requests on GitHub. * The "`fossil git export`" command creates subprocesses that run "`git`" commands, so you must have Git installed on your machine for any of this to work. * The Git repository will have an extra unmanaged top-level directory named "`.mirror_state`" that contains one or more files. Those files are used to store the intermediate state of the translation so that subsequent invocations of "`fossil git export`" will know where you left off the last time and what new content needs to be moved over into Git. Be careful not to mess with the `.mirror_state` directory or any of its contents. Do not put those files under Git management. Do not edit or delete them. * The name of the "trunk" branch is automatically translated into "master" in the Git mirror unless you give the `--mainbranch` option. * Only check-ins and simple tags are translated to Git. Git does not support wiki or tickets or unversioned content or any of the other features of Fossil that make it so convenient to use, so those other elements cannot be mirrored in Git. * In Git, all tags must be unique. If your Fossil repository has the same tag on two or more check-ins, the tag will only be preserved on the chronologically newest check-in. * There is a [long list of restrictions](https://git-scm.com/docs/git-check-ref-format) on tag and branch names in Git. If any of your Fossil tag or branch names violate these rules, then the names are translated prior to being exported to Git. The translation usually involves converting the offending characters into underscores. * If your Fossil user contact info is not set and this repository was not initially [imported from Git](./inout.wiki), `fossil git export` will construct a generic `user@noemail.net` for the Git *committer* and *author* email fields of each commit. However, Fossil will first attempt to parse an email address from your user contact info, which can be set through a Fossil [UI][ui] browser window or with the [`user contact`][usercmd] subcommand on the command line. Alternatively, if this repository was previously imported from Git using the [`--attribute`][attr] option, the [`fx_git`][fxgit] table will be queried for correspondent email addresses. Only if neither of these methods produce a user specified email will the abovementioned generic address be used. [attr]: /help?cmd=import [fxgit]: ./inout.wiki#fx_git [ui]: /help?cmd=ui [usercmd]: /help?cmd=user ## <a id='ex1'></a>Example GitHub Mirrors As of this writing (2019-03-16) Fossil’s own repository is mirrored on GitHub at: > <https://github.com/drhsqlite/fossil-mirror> In addition, an official Git mirror of SQLite is available: > <https://github.com/sqlite/sqlite> The Fossil source repositories for these mirrors are at <https://www2.fossil-scm.org/fossil> and <https://www2.sqlite.org/src>, respectively. Both repositories are hosted on the same VM at [Linode](https://www.linode.com). On that machine, there is a [cron job](https://linux.die.net/man/8/cron) that runs at 17 minutes after the hour, every hour that does: /usr/bin/fossil sync -u -R /home/www/fossil/fossil.fossil /usr/bin/fossil sync -R /home/www/fossil/sqlite.fossil /usr/bin/fossil git export -R /home/www/fossil/fossil.fossil /usr/bin/fossil git export -R /home/www/fossil/sqlite.fossil The initial two "sync" commands pull in changes from the primary Fossil repositories for Fossil and SQLite. The last two lines export the changes to Git and push the results up to GitHub. |
Deleted www/mkdownload.tcl.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added www/mkindex.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 | #!/usr/bin/env tclsh # # Run this TCL script to generate a WIKI page that contains a # permuted index of the various documentation files. # # tclsh mkindex.tcl # # 2021-02-26: The permuted index feature has been removed because # moderns don't understand such things, and seeing so many entries # confuses them. # set doclist { aboutcgi.wiki {How CGI Works In Fossil} aboutdownload.wiki {How The Download Page Works} adding_code.wiki {Adding New Features To Fossil} adding_code.wiki {Hacking Fossil} alerts.md {Email Alerts And Notifications} antibot.wiki {Defense against Spiders and Robots} backoffice.md {The "Backoffice" mechanism of Fossil} backup.md {Backing Up a Remote Fossil Repository} blame.wiki {The Annotate/Blame Algorithm Of Fossil} blockchain.md {Is Fossil A Blockchain?} branching.wiki {Branching, Forking, Merging, and Tagging} bugtheory.wiki {Bug Tracking In Fossil} build.wiki {Compiling and Installing Fossil} cap-theorem.md {Fossil and the CAP Theorem} caps/ {Administering User Capabilities (a.k.a. Permissions)} caps/admin-v-setup.md {Differences Between Setup and Admin Users} caps/ref.html {User Capability Reference} cgi.wiki {CGI Script Configuration Options} changes.wiki {Fossil Changelog} chat.md {Fossil Chat} checkin_names.wiki {Check-in And Version Names} checkin.wiki {Check-in Checklist} childprojects.wiki {Child Projects} chroot.md {Server Chroot Jail} ckout-workflows.md {Check-Out Workflows} co-vs-up.md {Checkout vs Update} copyright-release.html {Contributor License Agreement} concepts.wiki {Fossil Core Concepts} contact.md {Developer Contact Information} containers.md {OCI Containers} contribute.wiki {Contributing Code or Documentation To The Fossil Project} css-tricks.md {Fossil CSS Tips and Tricks} customgraph.md {Theming: Customizing the Timeline Graph} customskin.md {Theming: Customizing The Appearance of Web Pages} customskin.md {Custom Skins} custom_ticket.wiki {Customizing The Ticket System} defcsp.md {The Default Content Security Policy} delta-manifests.md {Delta Manifests} delta_encoder_algorithm.wiki {Fossil Delta Encoding Algorithm} delta_format.wiki {Fossil Delta Format} embeddeddoc.wiki {Embedded Project Documentation} encryptedrepos.wiki {How To Use Encrypted Repositories} env-opts.md {Environment Variables and Global Options} event.wiki {Events} faq.wiki {Frequently Asked Questions} fileedit-page.md {The fileedit Page} fileformat.wiki {Fossil File Format} fiveminutes.wiki {Up and Running in 5 Minutes as a Single User} forum.wiki {Fossil Forums} foss-cklist.wiki {Checklist For Successful Open-Source Projects} fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE} fossil-is-not-relational.md {Introduction to the (Non-relational) Fossil Data Model} fossil_prompt.wiki {Fossilized Bash Prompt} fossil-v-git.wiki {Fossil Versus Git} gitusers.md {Git to Fossil Translation Guide} globs.md {File Name Glob Patterns} glossary.md {Glossary} grep.md {Fossil grep vs POSIX grep} hacker-howto.wiki {Hacker How-To} hacker-howto.wiki {Fossil Developers Guide} hashes.md {Hashes: Fossil Artifact Identification} hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256} /help {Lists of Commands and Webpages} hints.wiki {Fossil Tips And Usage Hints} history.md {The Purpose And History Of Fossil} index.wiki {Home Page} inout.wiki {Import And Export To And From Git} interwiki.md {Interwiki Links} image-format-vs-repo-size.md {Image Format vs Fossil Repo Size} javascript.md {Use of JavaScript in Fossil} json-api/index.md {JSON API} loadmgmt.md {Managing Server Load} makefile.wiki {The Fossil Build Process} mirrorlimitations.md {Limitations On Git Mirrors} mirrortogithub.md {How To Mirror A Fossil Repository On GitHub} /md_rules {Markdown Formatting Rules} newrepo.wiki {How To Create A New Fossil Repository} patchcmd.md {The "fossil patch" Command} password.wiki {Password Management And Authentication} pikchr.md {The Pikchr Diagram Language} pop.wiki {Principles Of Operation} private.wiki {Creating, Syncing, and Deleting Private Branches} qandc.wiki {Questions And Criticisms} quickstart.wiki {Fossil Quick Start Guide} quotes.wiki {Quotes: What People Are Saying About Fossil, Git, and DVCSes in General} ../test/release-checklist.wiki {Pre-Release Testing Checklist} rebaseharm.md {Rebase Considered Harmful} reviews.wiki {Reviews} selfcheck.wiki {Fossil Repository Integrity Self Checks} selfhost.wiki {Fossil Self Hosting Repositories} server/ {How To Configure A Fossil Server} serverext.wiki {CGI Server Extensions} serverext.wiki {Adding Extensions To A Fossil Server Using CGI Scripts} settings.wiki {Fossil Settings} /sitemap {Site Map} shunning.wiki {Shunning: Deleting Content From Fossil} stats.wiki {Performance Statistics} style.wiki {Source Code Style Guidelines} ssl.wiki {Using SSL with Fossil} ssl-server.md {SSL/TLS Server Mode} 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} theory1.wiki {Thoughts On The Design Of The Fossil DVCS} tickets.wiki {The Fossil Ticket System} unvers.wiki {Unversioned Files} webpage-ex.md {Webpage Examples} webui.wiki {The Fossil Web Interface} whyallinone.md {Why Bundle Forum, Wiki, and other Web Software With Your DVCS?} whyusefossil.wiki {Why You Should Use Fossil} whyusefossil.wiki {Benefits Of Version Control} wikitheory.wiki {Wiki In Fossil} /wiki_rules {Wiki Formatting Rules} } set permindex {} set stopwords { a about against and are as by for fossil from in of on or should the to use used with } foreach {file title} $doclist { set n [llength $title] regsub -all {\s+} $title { } title lappend permindex [list $title $file 1] # Disable the permutations. # for {set i 0} {$i<$n-1} {incr i} { # set prefix [lrange $title 0 $i] # set suffix [lrange $title [expr {$i+1}] end] # set firstword [string tolower [lindex $suffix 0]] # if {[lsearch $stopwords $firstword]<0} { # lappend permindex [list "$suffix — $prefix" $file 0] # } # } } 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 { <form action='$ROOT/docsrch' method='GET' style="text-align:center"> <input type="text" name="s" size="40" autofocus> <input type="submit" value="Search Docs"> </form> <h2>Primary Documents:</h2> <ul> <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> <li> <a href='history.md'>Purpose and History of Fossil</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> <li><a href='$ROOT/wiki?name=Release Build How-To'>Release Build How-To</a>, a.k.a. how deliverables are built</li> </li> <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> <li> <a href='https://fossil-scm.org/fossil-book/'>Fossil book</a> </ul> <h2 id="pindex">Other Documents:</h2> <ul>} foreach entry $permindex { foreach {title file bold} $entry break # if {$bold} {set title <b>$title</b>} if {[string match /* $file]} {set file ../../..$file} puts $out "<li><a href=\"$file\">$title</a></li>" } puts $out "</ul></div>" |
Changes to www/newrepo.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 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 | <title>How To Create A New Fossil Repository</title> The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how to get up and running with fossil. But once you're running, what can you do with it? This document will walk you through the process of creating a fossil repository, populating it with files, and then sharing it over the web. The first thing we need to do is create a fossil repository file: <verbatim> $ fossil new demo.fossil project-id: 9d8ccff5671796ee04e60af6932aa7788f0a990a server-id: 145fe7d71e3b513ac37ac283979d73e12ca04bfe admin-user: stephan (initial password is ******) </verbatim> The numbers it spits out are unimportant (they are version numbers). Now we have an empty repository file named <tt>demo.fossil</tt>. There is nothing magical about the extension <tt>.fossil</tt> - it's just a convention. You may name your files anything you like. The first thing we normally want to do is to run fossil as a local server so that you can configure the access rights to the repo: <verbatim> $ fossil ui demo.fossil </verbatim> The <tt>ui</tt> command starts up a server (with an optional <tt>-port NUMBER</tt> argument) and launches a web browser pointing at the fossil server. From there it takes just a few moments to configure the repo. Most importantly, go to the Admin menu, then the Users link, and set your account name and password, and grant your account all access privileges. (I also like to grant Clone access to the anonymous user, but that's personal preference.) Once you are done, kill the fossil server (with Ctrl-C or equivalent) and close the browser window. <div class="sidebar"> It is not strictly required to configure a repository this way, but if you are going to share a repo over the net then it is highly recommended. If you are only going to work with the repo locally, you can skip the configuration step and do it later if you decide you want to share your repo. </div> The next thing we need to do is <em>open</em> the repository. To do so we create a working directory and then <tt>cd</tt> to it: <verbatim> $ mkdir demo $ cd demo $ fossil open ../demo.fossil </verbatim> That creates a file called <tt>_FOSSIL_</tt> in the current directory, and this file contains all kinds of fossil-related information about your local repository. You can ignore it for all purposes, but be sure not to accidentally remove it or otherwise damage it - it belongs to fossil, not you. The next thing we need to do is add files to our repository. As it happens, we have a few C source files lying around, which we'll simply copy into our working directory. <verbatim> $ cp ../csnip/*.{c,h} . $ ls clob.c clob.h clobz.c mkdep.c test-clob.c tokenize_path.c tokenize_path.h vappendf.c vappendf.h </verbatim> Fossil doesn't know about those files yet. Telling fossil about a new file is a two-step process. First we <em>add</em> the file to the repository, then we <em>commit</em> the file. This is a familiar process for anyone who's worked with SCM systems before: <verbatim> $ fossil add *.{c,h} $ fossil commit -m "egg" New_Version: d1296b4a08b9f8b943bb6c73698e51eed23f8f91 </verbatim> We now have a working repository! The file <tt>demo.fossil</tt> is the central storage, and we can share it amongst an arbitrary number of trees. As a silly example: <verbatim> $ cd ~/fossil $ mkdir demo2 $ cd demo2 $ fossil open ../demo.fossil ADD clob.c ADD clob.h ADD clobz.c ADD mkdep.c ADD test-clob.c ADD tokenize_path.c ADD tokenize_path.h ADD vappendf.c </verbatim> You may modify the repository (e.g. add, remove, or commit files) from both working directories, and doing so might be useful when working on a branch or experimental code. Making your repository available over the web is trivial to do. We assume you have some web space where you can store your fossil file and run a CGI script. If not, then this option is not for you. If you do, then here's how... Copy the fossil repository file to your web server (it doesn't matter where, really, but it "should" be unreachable by web browser traffic). In your <tt>cgi-bin</tt> (or equivalent) directory, create a file which looks like this: <verbatim> #!/path/to/fossil repository: /path/to/my_repo.fossil </verbatim> Make that script executable, and you're all ready to go: <verbatim> $ chmod +x ~/www/cgi-bin/myrepo.cgi </verbatim> Now simply point your browser to <tt>https://my.domain/cgi-bin/myrepo.cgi</tt> and you should be able to manage the repository from there. To check out a copy of your remote repository, use the <em>clone</em> command: <verbatim> $ fossil clone \ https://MyAccountName:MyAccountPassword@my.domain/cgi-bin/myrepo.cgi </verbatim> If you do not provide your password in the URL, fossil will interactively prompt you for it. A clone is a local copy of a remote repository, and can be opened just like a local one (as shown above). It is treated identically to your local repository, with one very important difference. When you commit changes to a cloned remote repository, they will be pushed back to the remote repository. If you have <tt>autosync</tt> on then this sync happens automatically, otherwise you will need to use the <em>pull</em> command to get remote changes and the <em>push</em> command to push your local commits to the remote repository. You must of course have authorization to commit changes (access is configured via the Admin/Users page mentioned above). You may always use the <em>server</em> or <em>ui</em> commands to browse a cloned repository. You can even edit create or wiki entries, etc., and they will be pushed to the remote side the next time you push data to the remote. |
Changes to www/password.wiki.
1 | <title>Fossil Password Management</title> | < | | | | < | | | | | | | | | | | | | < < < < < < < < < < < < < < | | | | 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 | <title>Fossil Password Management</title> 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. The SHA1 hash in the USER.PW field is a hash of a string composed of the project-code, the user login, and the user cleartext password. Suppose user "alice" with password "asdfg" had an account on the Fossil self-hosting repository. Then the value of USER.PW for alice would be the SHA1 hash of <pre> CE59BB9F186226D80E49D1FA2DB29F935CCA0333/alice/asdfg </pre> Note that by including the project-code and the login as part of the hash, a different USER.PW value results even if two or more users on the repository select the same "asdfg" password or if user alice reuses the same password on multiple projects. Whenever a password is changed using the web interface or using the "user" command-line method, the new password is stored using the SHA1 encoding. Thus, cleartext passwords will gradually migrate to become SHA1 passwords. All remaining cleartext passwords can be converted to SHA1 passwords using the following command: <pre> fossil test-hash-passwords <i>REPOSITORY-NAME</i> </pre> Remember that converting from cleartext to SHA1 passwords is an irreversible operation. The only way to insert a new cleartext password into the USER table is to do so manually using SQL commands. For example: <pre> UPDATE user SET pw='asdfg' WHERE login='alice'; </pre> Note that an password that is an empty string or NULL will disable all login for that user. Thus, to lock a user out of the system, one has only to set their password to an empty string, using either the web interface or direct SQL manipulation of the USER table. Note also that the password field is essentially ignored for the special users named "anonymous", "developer", "reader", and "nobody". It is not possible to authenticate as users "developer", "reader", or "nobody" and the authentication protocol for "anonymous" uses one-time captchas not persistent passwords. <h2>Web Interface Authentication</h2> 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. Note that in order to log into a Fossil server, it is necessary to write information into the repository database. Hence, login is not possible on a Fossil repository with a read-only database file. The user password is sent over the wire as cleartext on the initial 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 |
︙ | ︙ | |||
130 131 132 133 134 135 136 | This means that when USER.PW holds a cleartext password, the login card will work for both older and newer clients. If the USER.PW on the server only holds the SHA1 hash of the password, then only newer clients will be able to authenticate to the server. The client normally gets the login and password from the "remote URL". | | | | | | | | | | 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 | This means that when USER.PW holds a cleartext password, the login card will work for both older and newer clients. If the USER.PW on the server only holds the SHA1 hash of the password, then only newer clients will be able to authenticate to the server. The client normally gets the login and password from the "remote URL". <pre> http://<span style="color:blue">login:password</span>@servername.org/path </pre> 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. <pre> http://<span style="color:blue">login:*password</span>@servername.org/path </pre> 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/patchcmd.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 | # The "fossil patch" command The "[fossil patch](/help?cmd=patch)" command is designed to transfer uncommitted changes from one check-out to another, including transfering those changes to other machines. For example, if you are working on a Windows desktop and you want to test your changes on a Linux server before you commit, you can use the "fossil patch push" command to make a copy of all your changes on the remote Linux server: fossil patch push linuxserver:/path/to/checkout In the previous "linuxserver" is the name of the remote machine and "/path/to/checkout" is an existing checkout directory for the same project on the remote machine. The "fossil patch push" command works by first creating a patch file, then transfering that patch file to the remote machine using "ssh", then applying the patch. If you do not have ssh available, you can break these steps apart as follows: 1. On the local machine: `fossil patch create mypatch.patch` 2. Move "mypatch.patch" to the remote machine. 3. On the remote machine: `fossil patch apply mypatch.patch` Step 2 can be accomplished by a variety of means including posting the mypatch.patch file on [chat](./chat.md) or sending it as an email attachment. ## Setup The "fossil patch push" and "fossil patch pull" commands will only work if you have "ssh" available on the local machine and if "fossil" is on the default PATH on the remote machine. To check if Fossil is installed correctly on the remote, try a command like this: ssh -T remote "fossil version" If the command above shows a recent version of Fossil, then you should be set to go. If you get "fossil not found", or if the version shown is too old, put a newer fossil executable on the default PATH. The default PATH can be shown using: ssh -T remote 'echo $PATH' ### Custom PATH Caveat On Unix-like systems, the init script for the user's login shell (e.g. `~/.profile` or `~/.bash_profile`) may be configured to *not do anything* when running under a non-interactive shell. Thus a fossil binary installed to a custom directory might not be found. To allow the patch command to use a fossil binary installed in a directory which is normally added to the PATH via the interactive shell's init script, it may be useful to disable that check. For example, Ubuntu-derived systems sometimes start their default `.bashrc` with something like: ``` # If not running interactively, don't do anything: [ -z "$PS1" ] && return # Or: case $- in *i*) ;; *) return;; esac ``` Commenting out that check will allow the patch command to run, for example, `~/bin/fossil` if `~/bin` is added to the PATH via the init script. To disable that check *only* when the shell is *not* running over an SSH connection, something like the following should suffice: ``` if [ -z "$SSH_CONNECTION" ]; then # ... the is-interactive check goes here ... fi ``` ## Implementation Details The "fossil patch create" command records all of the local, uncommitted changes in an SQLite database file. If the argument to "fossil patch create" is a filename, then the patch-file database is written into that file. If the argument is "-" then the database is written on standard output. The "fossil patch apply" command reads the database that is the patch file and applies it to the local check-out. If a filename is given as an argument, then the database is read from that file. If the argument is "-" then the database is read from standard input. Hence the command: fossil patch push remote:projectA Is equivalent to: fossil patch create - | ssh -T remote 'cd projectA;fossil patch apply -' Likewise, a command like this: fossil patch pull remote:projB Could be entered like this: ssh -T remote 'cd projB;fossil patch create -' | fossil patch apply - The "fossil patch view" command just opens the database file and prints a summary of its contents on standard output. |
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 | <div class='fossil-doc' data-title='Index Of Fossil Documentation'> <form action='$ROOT/docsrch' method='GET' style="text-align:center"> <input type="text" name="s" size="40" autofocus> <input type="submit" value="Search Docs"> </form> <h2>Primary Documents:</h2> <ul> <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> <li> <a href='history.md'>Purpose and History of Fossil</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> <li><a href='$ROOT/wiki?name=Release Build How-To'>Release Build How-To</a>, a.k.a. how deliverables are built</li> </li> <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> <li> <a href='https://fossil-scm.org/fossil-book/'>Fossil book</a> </ul> <h2 id="pindex">Other Documents:</h2> <ul> <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> <li><a href="serverext.wiki">Adding Extensions To A Fossil Server Using CGI Scripts</a></li> <li><a href="adding_code.wiki">Adding New Features To Fossil</a></li> <li><a href="caps/">Administering User Capabilities (a.k.a. Permissions)</a></li> <li><a href="backup.md">Backing Up a Remote Fossil Repository</a></li> <li><a href="whyusefossil.wiki">Benefits Of Version Control</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="cgi.wiki">CGI Script Configuration Options</a></li> <li><a href="serverext.wiki">CGI Server Extensions</a></li> <li><a href="checkin_names.wiki">Check-in And Version Names</a></li> <li><a href="checkin.wiki">Check-in Checklist</a></li> <li><a href="ckout-workflows.md">Check-Out Workflows</a></li> <li><a href="foss-cklist.wiki">Checklist For Successful Open-Source Projects</a></li> <li><a href="co-vs-up.md">Checkout vs Update</a></li> <li><a href="childprojects.wiki">Child Projects</a></li> <li><a href="build.wiki">Compiling and Installing Fossil</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="private.wiki">Creating, Syncing, and Deleting Private Branches</a></li> <li><a href="customskin.md">Custom Skins</a></li> <li><a href="custom_ticket.wiki">Customizing The Ticket System</a></li> <li><a href="antibot.wiki">Defense against Spiders and Robots</a></li> <li><a href="delta-manifests.md">Delta Manifests</a></li> <li><a href="contact.md">Developer Contact Information</a></li> <li><a href="caps/admin-v-setup.md">Differences Between Setup and Admin Users</a></li> <li><a href="alerts.md">Email Alerts And Notifications</a></li> <li><a href="embeddeddoc.wiki">Embedded Project Documentation</a></li> <li><a href="env-opts.md">Environment Variables and Global Options</a></li> <li><a href="event.wiki">Events</a></li> <li><a href="globs.md">File Name Glob Patterns</a></li> <li><a href="cap-theorem.md">Fossil and the CAP Theorem</a></li> <li><a href="changes.wiki">Fossil Changelog</a></li> <li><a href="chat.md">Fossil Chat</a></li> <li><a href="concepts.wiki">Fossil Core Concepts</a></li> <li><a href="css-tricks.md">Fossil CSS Tips and Tricks</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="hacker-howto.wiki">Fossil Developers Guide</a></li> <li><a href="fileformat.wiki">Fossil File Format</a></li> <li><a href="forum.wiki">Fossil Forums</a></li> <li><a href="grep.md">Fossil grep vs POSIX grep</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="fossil_prompt.wiki">Fossilized Bash Prompt</a></li> <li><a href="faq.wiki">Frequently Asked Questions</a></li> <li><a href="gitusers.md">Git to Fossil Translation Guide</a></li> <li><a href="glossary.md">Glossary</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="hashpolicy.wiki">Hash Policy: Choosing Between SHA1 and SHA3-256</a></li> <li><a href="hashes.md">Hashes: Fossil Artifact Identification</a></li> <li><a href="index.wiki">Home Page</a></li> <li><a href="aboutcgi.wiki">How CGI Works In Fossil</a></li> <li><a href="aboutdownload.wiki">How The Download Page Works</a></li> <li><a href="server/">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="mirrortogithub.md">How To Mirror A Fossil Repository On GitHub</a></li> <li><a href="encryptedrepos.wiki">How To Use Encrypted Repositories</a></li> <li><a href="image-format-vs-repo-size.md">Image Format vs Fossil Repo Size</a></li> <li><a href="inout.wiki">Import And Export To And From Git</a></li> <li><a href="fossil-from-msvc.wiki">Integrating Fossil in the Microsoft Express 2010 IDE</a></li> <li><a href="interwiki.md">Interwiki Links</a></li> <li><a href="fossil-is-not-relational.md">Introduction to the (Non-relational) Fossil Data Model</a></li> <li><a href="blockchain.md">Is Fossil A Blockchain?</a></li> <li><a href="json-api/index.md">JSON API</a></li> <li><a href="mirrorlimitations.md">Limitations On Git Mirrors</a></li> <li><a href="../../../help">Lists of Commands and Webpages</a></li> <li><a href="loadmgmt.md">Managing Server Load</a></li> <li><a href="../../../md_rules">Markdown Formatting Rules</a></li> <li><a href="containers.md">OCI Containers</a></li> <li><a href="password.wiki">Password Management And Authentication</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 Operation</a></li> <li><a href="qandc.wiki">Questions And Criticisms</a></li> <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> <li><a href="rebaseharm.md">Rebase Considered Harmful</a></li> <li><a href="reviews.wiki">Reviews</a></li> <li><a href="chroot.md">Server Chroot Jail</a></li> <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> <li><a href="../../../sitemap">Site Map</a></li> <li><a href="style.wiki">Source Code Style Guidelines</a></li> <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> <li><a href="ssl-server.md">SSL/TLS Server Mode</a></li> <li><a href="backoffice.md">The "Backoffice" mechanism of Fossil</a></li> <li><a href="patchcmd.md">The "fossil patch" Command</a></li> <li><a href="blame.wiki">The Annotate/Blame Algorithm Of Fossil</a></li> <li><a href="defcsp.md">The Default Content Security Policy</a></li> <li><a href="fileedit-page.md">The fileedit Page</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="pikchr.md">The Pikchr Diagram Language</a></li> <li><a href="history.md">The Purpose And History Of Fossil</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="customgraph.md">Theming: Customizing the Timeline Graph</a></li> <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li> <li><a href="unvers.wiki">Unversioned Files</a></li> <li><a href="fiveminutes.wiki">Up and Running in 5 Minutes as a Single User</a></li> <li><a href="javascript.md">Use of JavaScript in Fossil</a></li> <li><a href="caps/ref.html">User Capability Reference</a></li> <li><a href="ssl.wiki">Using SSL with Fossil</a></li> <li><a href="webpage-ex.md">Webpage Examples</a></li> <li><a href="whyallinone.md">Why Bundle Forum, Wiki, and other Web Software With Your DVCS?</a></li> <li><a href="whyusefossil.wiki">Why You Should Use Fossil</a></li> <li><a href="../../../wiki_rules">Wiki Formatting Rules</a></li> <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> </ul></div> |
Added www/pikchr.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 | # The Pikchr Diagram Language Pikchr (pronounced "picture") is a [PIC][1]-like markup language for creating diagrams in technical documentation. Pikchr diagrams source text can be embedded directly in either [Markdown][2] or [Fossil Wiki][3]. Fossil translates the Pikchr source text into SVG which is displayed as part of the rendered wiki. [1]: wikipedia:/wiki/Pic_language [2]: /md_rules [3]: /wiki_rules For example, this document is written in Markdown. The following is a sample Pikchr diagram: ``` pikchr arrow right 200% "Markdown" "Source" box rad 10px "Markdown" "Formatter" "(markdown.c)" fit arrow right 200% "HTML+SVG" "Output" arrow <-> down 70% from last box.s box same "Pikchr" "Formatter" "(pikchr.c)" fit ``` The diagram above was generated by the following lines of Markdown: ~~~~~ ``` pikchr arrow right 200% "Markdown" "Source" box rad 10px "Markdown" "Formatter" "(markdown.c)" fit arrow right 200% "HTML+SVG" "Output" arrow <-> down 70% from last box.s box same "Pikchr" "Formatter" "(pikchr.c)" fit ``` ~~~~~ See the [original Markdown source text of this document][4] for an example of Pikchr in operation. [4]: ./pikchr.md?mimetype=text/plain Fossil allows Pikchr diagrams to appear anywhere that Markdown or Fossil Wiki markup or used, including: * [Embedded documentation](./embeddeddoc.wiki) * Stand-alone wiki pages * [Wiki pages associated with particular branches or check-ins](./wikitheory.wiki#assocwiki) * Check-in comments * [Technical notes](./event.wiki) * [Forum posts](./forum.wiki) * [Bug reports and trouble tickets](./bugtheory.wiki) ## Pikchr Is A Separate Project Even though the original author of Pikchr is the same as the original creator of Fossil, the sources to the Pikchr formatter are maintained as a [separate project named "pikchr.org"](https://pikchr.org). Pikchr is a delivered as a single file of C code. The "pikchr.c" file from the Pikchr project is periodically copied into the Fossil source tree. Pikchr is maintained as a project distinct from Fossil so that it can be used independently of Fossil. ### Pikchr User Manual And Tutorials Complete documentation on the Pikchr language can be found on the Pikchr project page: * <https://pikchr.org/> That website contains a user manual, tutorials, a language specification, a summary of differences between Pikchr and legacy PIC, and it hosts copies of historical PIC documentation. ## How To Include Pikchr Diagrams In Fossil Documents To illustrate how to include Pikchr in Fossil markup, we will use the following one-line Pikchr. Click to see the code: ~~~ pikchr toggle arrow; box "Hello" "World!" fit; arrow ~~~ For Markdown, the Pikchr code is put inside of a [fenced code block][fcb]. A fenced code block is the text in between ``` ... ``` or between ~~~ ... ~~~ using three or more ` or ~ characters. The fenced code block normally displays its content verbatim, but if an "info string" of "pikchr" follows the opening ``` or ~~~, then the content is interpreted as Pikchr script and is replaced by the equivalent SVG. So either of these work: [fcb]: https://spec.commonmark.org/0.29/#fenced-code-blocks ~~~~~~ ~~~ pikchr arrow; box "Hello" "World!" fit; arrow ~~~ ``` pikchr arrow; box "Hello" "World!" fit; arrow ``` ~~~~~~ For Fossil Wiki, the Pikchr code goes within `<verbatim type="pikchr"> ... </verbatim>`. Normally `<verbatim>` content is displayed verbatim. The extra `type="pikchr"` attribute causes the content to be interpreted as Pikchr and replaced by SVG. ~~~~~~ <verbatim type="pikchr"> arrow; box "Hello" "World!" fit; arrow </verbatim> ~~~~~~ ## Extra Arguments In "Pikchr" Code Blocks Extra formatting arguments can be included in the fenced code block start tag, or in the "`type=`" attribute of `<verbatim>`, to change the formatting of the diagram. * **indent** → The diagram is indented from the left margin. * **center** → The diagram is centered * **toggle** → Clicking on the diagram toggles between showing the rendered SVG and the original Pikchr source text. You can always do this by holding down the Ctrl or Alt keys and clicking. The "toggle" option just means you can toggle without holding down any extra keys. * **float-left** → The diagram is shown at the left margin and text fills in around the diagram. * **float-right** → The diagram is shown at the right margin and text fills in around the diagram. * **source** → The display starts out showing the Pikchr source text. The reader must click (or Alt-click or Ctrl-click) to set the diagram. |
Changes to www/pop.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 | <title>Principles Of Operation</title> This page attempts to define the foundational principals upon which Fossil is built. * A project consists of source files, wiki pages, and trouble tickets, and control files (collectively "artifacts"). All historical copies of all artifacts are saved. The project maintains an audit trail. * A project resides in one or more repositories. Each repository is administered and operates independently of the others. * Each repository has both global and local state. The global state is common to all repositories (or at least has the potential to be shared in common when the repositories are fully synchronized). The local state for each repository is private to that repository. The global state represents the content of the project. The local state identifies the authorized users and access policies for a particular repository. * The global state of a repository is an unordered collection of artifacts. Each artifact is named by a cryptographic hash (SHA1 or SHA3-256) 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. * Because artifacts are named by a cryptographic 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. * 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 consider it to be an impossibility. * The signature of an artifact is the cryptographic hash of the artifact itself, exactly as it would appear in a disk file. No prefix or meta-information about the artifact is added before computing the hash. So you can always find the signature of a file by using the "sha1sum" or "sha3sum" or similar command-line utilities. * The artifacts that comprise the global state of a repository are the complete global state of that repository. The SQLite database that holds the repository contains additional information about linkages between artifacts, but all of that added information can be discarded and reconstructed by rescanning the content artifacts. * Two repositories for the same project can synchronize their global states simply by sharing artifacts. The local state of repositories is not normally synchronized or shared. * Every check-in has a special file at the top-level named "manifest" which is an index of all other files in that check-in. The manifest is automatically created and maintained by the system. * The <a href="fileformat.wiki">file formats</a> used by Fossil are all very simple so that with access to the original content files, one can easily reconstruct the content of a check-in without the need for any special tools or software. |
Added www/private.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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | <title>Private Branches</title> By default, everything you check into a Fossil repository is shared to all clones of that repository. In Fossil, you don't push and pull individual branches; you push and pull everything all at once. But sometimes users want to keep some private work that is not shared with others. This might be a preliminary or experimental change that needs further refinement before it is shared and which might never be shared at all. To do this in Fossil, simply commit the change with the --private command-line option: <pre> fossil commit --private </pre> 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: <pre> fossil update trunk fossil merge private fossil commit </pre> The private branch remains private and is not recorded as a parent in the merge manifest's P-card, but all of the changes associated with the private branch are now folded into the public branch and are hence visible to other users of the project. A private branch created with Fossil version 1.30 or newer can also be converted into a public branch using the <code>fossil publish</code> command. However, there is no way to convert a private branch created with older versions of Fossil into a public branch. <div class="sidebar"> To avoid generating a missing artifact reference on peer repositories without the private branch, the merge parent is not recorded when merging the private branch into a public branch. As a consequence, the web UI timeline does not draw a merge line from the private merge parent to the public merge child. Moreover, repeat private-to-public merge operations (without the [/help?cmd=merge | --force option]) with files added on the private branch may only work once, but later abort with "WARNING: no common ancestor for FILE", as the parent-child relationship is not recorded. (See the [/doc/trunk/www/branching.wiki | Branching, Forking, Merging, and Tagging] document for more information.) </div> The <code>--integrate</code> option of <code>fossil merge</code> (to close the merged branch when committing) is ignored for a private branch -- or the check-in manifest of the resulting merge child would include a <code>+close</code> tag referring to the leaf check-in on the private branch, and generate a missing artifact reference on repository clones without that private branch. It's still possible to close the leaf of the private branch (after committing the merge child) with the <code>fossil amend --close</code> command. <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: <verbatim> fossil clone --private http://user@linux.localnetwork:8080/ mac-clone.fossil </verbatim> You'll have to supply a username and password in order for this to work. Fossil will not clone (or sync) private branches anonymously. By default, there are no users that can do private branch syncing. You will have to give a user the "Private" capability ("x") if you want them to be able to do this. We deny such capability for normal users by default to add a barrier to accidental syncing of a private branch to a public server. It is highly recommended that you leave the "x" capability turned off on all repositories used for collaboration (repositories to which many people push and pull) and only enable "x" for local repositories when you need to share private branches. Private branch sync only works if you use the --private command-line option. Private branches are never synced via the auto-sync mechanism. Once again, this restriction is designed to make it hard to accidentally push private branches beyond their intended audience. <h2>Purging Private Branches</h2> You can remove all private branches from a repository using this command: <pre> fossil scrub --private </pre> Note that the above is a permanent and irreversible change. You will be asked to confirm before continuing. Once the private branches are removed, they cannot be retrieved (unless you have synced them to another repository.) So be careful with the command. <h2>Additional Notes</h2> All of the features above apply to <u>all</u> private branches in a single repository at once. There is no mechanism in Fossil (currently) that allows you to push, pull, clone, sync, or scrub an individual private branch within a repository that contains multiple private branches. |
Changes to www/qandc.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 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 | <title>Questions And Criticisms</title> This page is a collection of real questions and criticisms that were raised against Fossil early in its history (circa 2008). This page is old and has not been kept up-to-date. See the [/finfo?name=www/qandc.wiki|change history of this page]. <b>Fossil sounds like a lot of reinvention of the wheel. Why create your own DVCS when you could have reused mercurial?</b> <div class="indent"> I wrote fossil because none of the other available DVCSes met my needs. If the other DVCSes do meet your needs, then you might not need fossil. But they don't meet mine, and so fossil is necessary for me. Features provided by fossil that one does not get with other DVCSes include: <ol> <li> Integrated <a href="wikitheory.wiki">wiki</a>. </li> <li> Integrated <a href="bugtheory.wiki">bug tracking</a> </li> <li> Immutable artifacts </li> <li> Self-contained, stand-alone executable that can be run in a <a href="http://en.wikipedia.org/wiki/Chroot">chroot jail</a> </li> <li> Simple, well-defined, <a href="fileformat.wiki">enduring file format</a> </li> <li> Integrated <a href="webui.wiki">web interface</a> </li> </ol> </div> <b>Why should I use this rather than Trac?</b> <div class="indent"> <ol> <li> Fossil is distributed. You can view and/or edit tickets, wiki, and code while off network, then sync your changes later. With Trac, you can only view and edit tickets and wiki while you are connected to the server. </li> <li> Fossil is lightweight and fully self-contained. It is very easy to setup on a low-resource machine. Fossil does not require an administrator.</li> <li> Fossil integrates code versioning into the same repository with wiki and tickets. There is nothing extra to add or install. Fossil is an all-in-one turnkey solution. </li> </ol> </div> <b>Love the concept here. Anyone using this for real work yet?</b> <div class="indent"> Fossil is <a href="https://fossil-scm.org/">self-hosting</a>. In fact, this page was probably delivered to your web-browser via a working fossil instance. The same virtual machine that hosts https://fossil-scm.org/ (a <a href="http://www.linode.com/">Linode 720</a>) also hosts 24 other fossil repositories for various small projects. The documentation files for <a href="http://www.sqlite.org/">SQLite</a> are hosted in a fossil repository <a href="http://www.sqlite.org/docsrc/">here</a>, for example. Other projects are also adopting fossil. But fossil does not yet have the massive user base of git or mercurial. </div> <b>Fossil looks like the bug tracker that would be in your Linksys Router's administration screen.</b> <div class="indent"> I take a pragmatic approach to software: form follows function. To me, it is more important to have a reliable, fast, efficient, enduring, and simple DVCS than one that looks pretty. On the other hand, if you have patches that improve the appearance of Fossil without seriously compromising its reliability, performance, and/or maintainability, I will be happy to accept them. Fossil is self-hosting. Send email to request a password that will let you push to the main fossil repository. </div> <b>It would be useful to have a separate application that keeps the bug-tracking database in a versioned file. That file can then be pushed and pulled along with the rest repository.</b> <div class="indent"> Fossil already <u>does</u> push and pull bugs along with the files in your repository. But fossil does <u>not</u> track bugs as files in the source tree. That approach to bug tracking was rejected for three reasons: <ol> <li> Check-ins in fossil are immutable. So if tickets were part of the check-in, then there would be no way to add new tickets to a check-in as new bugs are discovered. <li> Any project of reasonable size and complexity will generate thousands and thousands of tickets, and we do not want all those ticket files cluttering the source tree. <li> We want tickets to be managed from the web interface and to have a permission system that is distinct from check-in permissions. In other words, we do not want to restrict the creation and editing of tickets to developers with check-in privileges and an installed copy of the fossil executable. Casual passers-by on the internet should be permitted to create tickets. </ol> These points are reiterated in the opening paragraphs of the <a href="bugtheory.wiki">Bug-Tracking In Fossil</a> document. </div> <b>Fossil is already the name of a plan9 versioned append-only filesystem.</b> <div class="indent"> I did not know that. Perhaps they selected the name for the same reason that I did: because a repository with immutable artifacts preserves an excellent fossil record of a long-running project. </div> <b>The idea of storing a repository in a binary blob like an SQLite database terrifies me.</b> <div class="indent"> The use of SQLite to store the database is likely more stable and secure than any other approach, due to the fact that SQLite is transactional. Fossil also implements several internal <a href="selfcheck.wiki">self-checks</a> to insure that no information is ever lost. </div> <b>I am dubious of the benefits of including wikis and bug trackers directly in the VCS - either they are under-featured compared to full software like Trac, or the VCS is massively bloated compared to Subversion or Bazaar.</b> <div class="indent"> I have no doubt that Trac has many features that fossil lacks. But that is not the point. Fossil has several key features that Trac lacks and that I need: most notably the fact that fossil supports disconnected operation. 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. </div> |
Changes to www/quickstart.wiki.
1 | <title>Fossil Quick Start Guide</title> | < < | | | | | | > | | | | > | > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | < | | | | < < < < < > > > | < | | > > > > > > > > > > > | > | | > | > > > | | | < | | < > > > > > > | > | > > > | | > > > > > | > > > > | > | < | < > > | | < | < < < < < < > < | > > > > | | | | < > > > > > | < > > > > > > > > | < > > < < > | > > | > > | | | > | | > > | > | | > | | > | > > > > | > | | | > > | | < < > > < > > > > > > > > | > | < | > > | < > < > > | < | > > > > > | > > > > > > > > > > > | < < < | < > > | < < > > | < < > > > | < < | > > | < < > > > > > > | | > | | < > | < | < > > > | < > > | | > > > | > | > > > > > | > > > < > > < > > > | > | > > | < | > > | > > > | > | > | | > > > | | > | < | > > | > | | < > > > | | | < > > > > | < | > | < > > > > > > < | > > | > > > > | > | > > > > > > | > > > > | > | | > | | < < < < > > > > > > > > > > > > > | > > > > > > | | < > | < | | > > > > > | < < < < < | | < < | < | > | | > > | | > > | > > > > > > > > > > > | > | | > > > > > > | > > > > > | | < > > | > > | | | < | > | | < > > | > | > | | < > > > | < | > | < > > > > > | > | > > > > > > | | 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 | <title>Fossil Quick Start Guide</title> This is a guide to help you get started using the Fossil [https://en.wikipedia.org/wiki/Distributed_version_control|Distributed Version Control System] quickly and painlessly. <h2 id="install">Installing</h2> Fossil is a single self-contained C program. You need to either download a [https://fossil-scm.org/home/uv/download.html|precompiled binary] or <a href="build.wiki">compile it yourself</a> from sources. Install Fossil by putting the fossil binary someplace on your $PATH. You can test that Fossil is present and working like this: <pre><b>fossil version This is fossil version 2.13 [309af345ab] 2020-09-28 04:02:55 UTC </b></pre> <h2 id="workflow" name="fslclone">General Work Flow</h2> Fossil works with repository files (a database in a single file with the project's complete history) and with checked-out local trees (the working directory you use to do your work). (See [./glossary.md | the glossary] for more background.) The workflow looks like this: <ul> <li>Create or clone a repository file. ([/help/init|fossil init] or [/help/clone | fossil clone]) <li>Check out a local tree. ([/help/open | fossil open]) <li>Perform operations on the repository (including repository configuration). </ul> Fossil can be entirely driven from the command line. Many features can also be conveniently accessed from the built-in web user interface. The following sections give a brief overview of these operations. <h2 id="new">Starting A New Project</h2> To start a new project with fossil create a new empty repository this way: ([/help/init | more info]) <pre><b>fossil init</b> <i>repository-filename</i> </pre> You can name the database anything you like, and you can place it anywhere in the filesystem. The <tt>.fossil</tt> extension is traditional but only required if you are going to use the <tt>[/help/server | fossil server DIRECTORY]</tt> feature.†<h2 id="clone">Cloning An Existing Repository</h2> Most fossil operations interact with a repository that is on the local disk drive, not on a remote system. Hence, before accessing a remote repository it is necessary to make a local copy of that repository. Making a local copy of a remote repository is called "cloning". Clone a remote repository as follows: ([/help/clone | more info]) <pre><b>fossil clone</b> <i>URL repository-filename</i> </pre> The <i>URL</i> specifies the fossil repository you want to clone. The <i>repository-filename</i> is the new local filename into which the cloned repository will be written. For example, to clone the source code of Fossil itself: <pre><b>fossil clone https://fossil-scm.org/ myclone.fossil</b></pre> If your logged-in username is 'exampleuser', you should see output something like this: <pre><b>Round-trips: 8 Artifacts sent: 0 received: 39421 Clone done, sent: 2424 received: 42965725 ip: 10.10.10.0 Rebuilding repository meta-data... 100% complete... Extra delta compression... Vacuuming the database... project-id: 94259BB9F186226D80E49D1FA2DB29F935CCA0333 server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42 admin-user: exampleuser (password is "yoWgDR42iv")> </b></pre> If the remote repository requires a login, include a userid in the URL like this: <pre><b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b></pre> You will be prompted separately for the password. Use [https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_reserved_characters|"%HH"] escapes for special characters in the userid. For example "/" would be replaced by "%2F" meaning that a userid of "Projects/Budget" would become "Projects%2FBudget") If you are behind a restrictive firewall, you might need to <a href="#proxy">specify an HTTP proxy</a>. 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 by copying, consider running the [/help/scrub|fossil scrub] command to remove sensitive information before transmitting the file. <h2 id="import">Importing From Another Version Control System</h2> Rather than start a new project, or clone an existing Fossil project, you might prefer to <a href="./inout.wiki">import an existing Git project</a> into Fossil using the [/help/import | fossil import] command. You can even decide to export your project back into git using the [/help/git | fossil git] command, which is how the Fossil project maintains [https://github.com/drhsqlite/fossil-mirror | its public GitHub mirror]. There is no limit to the number of times a tree can be imported and exported between Fossil and git. The [https://git-scm.com/docs/git-fast-export|Git fast-export format] has become a popular way to move files between version management systems, including from [https://www.mercurial-scm.org/|Mercurial]. Fossil can also import [https://subversion.apache.org/|Subversion projects] directly. <h2 id="checkout">Checking Out A Local Tree</h2> To work on a project in fossil, you need to check out a local copy of the source tree. Create the directory you want to be the root of your tree and cd into that directory. Then do this: ([/help/open | more info]) <pre><b>fossil open</b> <i>repository-filename</i></pre> for example: <pre><b>fossil open ../myclone.fossil BUILD.txt COPYRIGHT-BSD2.txt README.md ︙ </tt></b></pre> (or "fossil open ..\myclone.fossil" on Windows). This leaves you with the newest version of the tree checked out. From anywhere underneath the root of your local tree, you can type commands like the following to find out the status of your local tree: <pre> <b>[/help/info | fossil info]</b> <b>[/help/status | fossil status]</b> <b>[/help/changes | fossil changes]</b> <b>[/help/diff | fossil diff]</b> <b>[/help/timeline | fossil timeline]</b> <b>[/help/ls | fossil ls]</b> <b>[/help/branch | fossil branch]</b> </pre> If you created a new repository using "fossil init" some commands will not produce much output. 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. To switch a checkout between different versions and branches, use:< <pre> <b>[/help/update | fossil update]</b> <b>[/help/checkout | fossil checkout]</b> </pre> [/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. <h2 id="changes">Making and Committing Changes</h2> To add new files to your project or remove existing ones, use these commands: <pre> <b>[/help/add | fossil add]</b> <i>file...</i> <b>[/help/rm | fossil rm]</b> <i>file...</i> <b>[/help/addremove | fossil addremove]</b> <i>file...</i> </pre> The command: <pre><b>[/help/changes | fossil changes]</b></pre> lists files that have changed since the last commit to the repository. For example, if you edit the file "README.md": <pre><b>fossil changes EDITED README.md </b></pre> To see exactly what change was made you can use the command <b>[/help/diff | fossil diff]</b>: <pre><b>fossil diff Index: README.md ============================================================ --- README.md +++ README.md @@ -1,5 +1,6 @@ +Made some changes to the project # Original text </b></pre> "fossil diff" shows the difference between your tree on disk now and as the tree was when you last committed changes. If you haven't committed yet, then it shows the difference relative to the tip-of-trunk commit in the repository, being what you get when you "fossil open" a repository without specifying a version, populating the working directory. To see the most recent changes made to the repository by other users, use "fossil timeline" to find out the most recent commit, and then "fossil diff" between that commit and the current tree: <pre><b>fossil timeline === 2021-03-28 === 03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk) === 2021-03-27 === 23:58:05 [ab975c6632] Update README.md. (user: user-two tags: trunk) ⋮ fossil diff --from current --to ab975c6632 Index: frobnicate.c ============================================================ --- frobnicate.c +++ frobnicate.c @@ -1,10 +1,11 @@ +/* made a change to the source file */ # Original text </b></pre> "current" is an alias for the checkout version, so the command "fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results. To commit your changes to a local-only repository: <pre><b>fossil commit</b> <i>(... Fossil will start your editor, if defined)</i><b> # Enter a commit message for this check-in. Lines beginning with # are ignored. # # user: exampleuser # tags: trunk # # EDITED README.md Edited file to add description of code changes New_Version: 7b9a416ced4a69a60589dde1aedd1a30fde8eec3528d265dbeed5135530440ab </b></pre> You will be prompted for check-in comments using whatever editor is specified by your VISUAL or EDITOR environment variable. If none is specified Fossil uses line-editing in the terminal. To commit your changes to a repository that was cloned from a remote repository, you give the same command, but the results are different. Fossil defaults to [./concepts.wiki#workflow|autosync] mode, a single-stage commit that sends all changes committed to the local repository immediately on to the remote parent repository. This only works if you have write permission to the remote respository. <h2 id="naming">Naming of Files, Checkins, and Branches</h2> Fossil deals with information artifacts. This Quickstart document only deals with files and collections of files, but be aware there are also tickets, wiki pages and more. Every artifact in Fossil has a universally-unique hash id, and may also have a human-readable name. The following are all equivalent ways of identifying a Fossil file, checkin or branch artifact: <ul> <li> the full unique SHA-256 hash, such as be836de35a821523beac2e53168e135d5ebd725d7af421e5f736a28e8034673a <li> an abbreviated hash prefix, such as the first ten characters: be836de35a . This won't be universally unique, but it is usually unique within any one repository. As an example, the [https://fossil-scm.org/home/hash-collisions|Fossil project hash collisions] showed at the time of writing that there are no artifacts with identical first 8 characters <li> a branch name, such as "special-features" or "juliet-testing". Each branch also has a unique SHA-256 hash </ul> A special convenience branch is "trunk", which is Fossil's default branch name for the first checkin, and the default for any time a branch name is needed but not specified. This will get you started on identifying checkins. The <a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including how timestamps can also be used. <h2 id="config">Configuring Your Local Repository</h2> 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 web server like this: ([/help/ui | more info]) <pre> <b>fossil ui</b> <i>repository-filename</i> </pre> You can omit the <i>repository-filename</i> from the command above if you are inside a checked-out local tree. This starts a web server then automatically launches your web browser and makes it point to this web server. If your system has an unusual configuration, fossil might not be able to figure out how to start your web browser. In that case, first tell fossil where to find your web browser using a command like this: <pre> <b>fossil setting web-browser</b> <i>path-to-web-browser</i> </pre> By default, fossil does not require a login for HTTP connections coming in from the IP loopback address 127.0.0.1. You can, and perhaps should, change this after you create a few users. When you are finished configuring, just press Control-C or use the <b>kill</b> command to shut down the mini-server. <h2 id="sharing">Sharing Changes</h2> When [./concepts.wiki#workflow|autosync] is turned off, the changes you [/help/commit | commit] are only on your local repository. To share those changes with other repositories, do: <pre> <b>[/help/push | fossil push]</b> <i>URL</i> </pre> Where <i>URL</i> is the http: URL of the server repository you want to share your changes with. If you omit the <i>URL</i> argument, fossil will use whatever server you most recently synced with. The [/help/push | push] command only sends your changes to others. To Receive changes from others, use [/help/pull | pull]. Or go both ways at once using [/help/sync | sync]: <pre> <b>[/help/pull | fossil pull]</b> <i>URL</i> <b>[/help/sync | fossil sync]</b> <i>URL</i> </pre> When you pull in changes from others, they go into your repository, not into your checked-out local tree. To get the changes into your local tree, use [/help/update | update]: <pre> <b>[/help/update | fossil update]</b> <i>VERSION</i> </pre> The <i>VERSION</i> can be the name of a branch or tag or any 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. 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. <pre> <b>[/help/checkout | fossil checkout]</b> <i>VERSION</i> </pre> 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. <h2 id="branch" name="merge">Branching And Merging</h2> 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 until they are ready to commit. To merge two branches back together, first [/help/update | update] to the branch you want to merge into. Then do a [/help/merge|merge] of the other branch that you want to incorporate the changes from. For example, to merge "featureX" changes into "trunk" do this: <pre> <b>fossil [/help/update|update] trunk</b> <b>fossil [/help/merge|merge] featureX</b> <i># make sure the merge didn't break anything...</i> <b>fossil [/help/commit|commit] </pre> The argument to the [/help/merge|merge] command can be any of the version identifier forms that work for [/help/update|update]. ([./checkin_names.wiki|more info].) The merge command has options to cherry-pick individual changes, or to back out individual changes, if you don't want to do a full merge. The merge command puts all changes in your working check-out. No changes are made to the repository. You must run [/help/commit|commit] separately to add the merge changes into your repository to make them persistent and so that your coworkers can see them. But before you do that, you will normally want to run a few tests to verify that the merge didn't cause logic breaks in your code. The same branch can be merged multiple times without trouble. Fossil automatically keeps up with things and avoids conflicts when doing multiple merges. So even if you have merged the featureX branch into trunk previously, you can do so again and Fossil will automatically know to pull in only those changes that have occurred since the previous merge. If a merge or update doesn't work out (perhaps something breaks or there are many merge conflicts) then you back up using: <pre> <b>[/help/undo | fossil undo]</b> </pre> This will back out the changes that the merge or update made to the working checkout. There is also a [/help/redo|redo] command if you undo by mistake. Undo and redo only work for changes that have not yet been checked in using commit and there is only a single level of undo/redo. <h2 id="server">Setting Up A Server</h2> Fossil can act as a stand-alone web server using one of these commands: <pre> <b>[/help/server | fossil server]</b> <i>repository-filename</i> <b>[/help/ui | fossil ui]</b> <i>repository-filename</i> </pre> The <i>repository-filename</i> can be omitted when these commands are run from within an open check-out, which is a particularly useful shortcut with the <b>fossil ui</b> command. The <b>ui</b> command is intended for accessing the web user interface from a local desktop. (We sometimes call this mode "Fossil UI.") The <b>ui</b> command differs from the <b>server</b> command by binding to the loopback IP address only (thus making the web UI visible only on the local machine) and by automatically starting your default web browser, pointing it at the running UI server. The localhost restriction exists because it also gives anyone who can access the resulting web UI full control over the repository. (This is the [./caps/admin-v-setup.md#apsu | all-powerful Setup capabliity].) For cross-machine collaboration, use the <b>server</b> command instead, which binds on all IP addresses, does not try to start a web browser, and enforces [./caps/ | Fossil's role-based access control system]. Servers are also easily configured as: <ul> <li>[./server/any/inetd.md|inetd] <li>[./server/debian/service.md|systemd] <li>[./server/any/cgi.md|CGI] <li>[./server/any/scgi.md|SCGI] </ul> …along with [./server/#matrix | several other options]. The [./selfhost.wiki | self-hosting fossil repositories] use CGI. You might <i>need</i> to set up a server, whether you know it yet or not. See the [./server/whyuseaserver.wiki | Benefits of a Fossil Server] article for details. <h2 id="proxy">HTTP Proxies</h2> 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 in three different ways. You can tell fossil about your proxy using a command-line option on commands that use the network, <b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>. <pre> <b>fossil clone </b><i>URL</i> <b>--proxy</b> <i>Proxy-URL</i> </pre> It is annoying to have to type in the proxy URL every time you sync your project, though, so you can make the proxy configuration persistent using the [/help/setting | setting] command: <pre> <b>fossil setting proxy </b><i>Proxy-URL</i> </pre> Or, you can set the "<b>http_proxy</b>" environment variable: <pre> <b>export http_proxy=</b><i>Proxy-URL</i> </pre> To stop using the proxy, do: <pre> <b>fossil setting proxy off</b> </pre> Or unset the environment variable. The fossil setting for the HTTP proxy takes precedence over the environment variable and the command-line option overrides both. If you have a persistent proxy setting that you want to override for a one-time sync, that is easily done on the command-line. For example, to sync with a co-worker's repository on your LAN, you might type: <pre> <b>fossil sync http://192.168.1.36:8080/ --proxy off</b> </pre> <h2 id="links">Other Resources</h2> <ul> <li> <a href="./gitusers.md">Hints For Users With Prior Git Experience</a> <li> <a href="./whyusefossil.wiki">Why You Should Use Fossil</a> <li> <a href="./history.md">The History and Purpose of Fossil</a> <li> <a href="./branching.wiki">Branching, Forking, and Tagging</a> <li> <a href="./hints.wiki">Fossil Tips and Usage Hints</a> <li> <a href="./permutedindex.html">Comprehensive Fossil Doc Index</a> </ul> |
Added www/quotes.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 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 | <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. <p class="local-indent"> <i>by mml at [http://news.ycombinator.com/item?id=1433387]</i> </p> <li><nowiki>It's simplest to think of the state of your [git] repository as a point in a high-dimensional "code-space", in which branches are represented as n-dimensional membranes, mapping the spatial loci of successive commits onto the projected manifold of each cloned repository.</nowiki> <p class="local-indent"> <i>by Jonathan Hartley at [https://www.tartley.com/posts/a-guide-to-git-using-spatial-analogies]; <br>Quoted here: [https://lwn.net/Articles/420152/].</i> </p> <li>Git is not a Prius. Git is a Model T. Its plumbing and wiring sticks out all over the place. You have to be a mechanic to operate it successfully or you'll be stuck on the side of the road when it breaks down. And it <b>will</b> break down. <p class="local-indent"> <i>Nick Farina at [http://nfarina.com/post/9868516270/git-is-simpler]</i> </p> <li>Initial revision of "git", The information manager from hell <p class="local-indent"> <i>Linus Torvalds - 2005-04-07 22:13:13<br> Commit comment on the very first source-code check-in for git </p> <li>I've been experimenting a lot with git at work. Damn, it's complicated. It has things to trip you up with that sane people just wouldn't ever both with including the ability to allow you to commit stuff in such a way that you can't find it again afterwards (!!!) Demented workflow complexity on acid? <p>* dkf really wishes he could use fossil instead</p> <p class="local-indent"> <i>by Donal K. Fellow (dkf) on the Tcl/Tk chatroom, 2013-04-09.</i> </p> <li>[G]it is <i>designed</i> to forget things. <p class="local-indent"> <i>[http://www.cs.cmu.edu/~davide/howto/git_lose.html] </p> <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. <p class="local-indent"> <i>Stephan Beal on the [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg17181.html|Fossil mailing list] 2014-09-01.</i> </p> <li>If programmers _really_ wanted to help scientists, they'd build a version control system that was more usable than Git. <p class="local-indent"> <i>Tweet by Greg Wilson @gvwilson on 2015-02-22 17:47</i> </p> <li><img src='xkcd-git.gif' align='top'> <p class="local-indent"><i>Randall Munroe. [http://xkcd.com/1597/]</i><p> </ol> <h2>On The Usability Of Fossil</h2> <ol> <li value=11> Fossil mesmerizes me with simplicity especially after I struggled to get a bug-tracking system to work with mercurial. <p class="local-indent"> <i>rawjeev at [https://stackoverflow.com/a/2100469/142454]</i> </p> <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. <p class="local-indent"> <i>Joe Prostko at [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg16716.html] </p> <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! <p class="local-indent"> <i>thunderbong commenting on hacker news: [https://news.ycombinator.com/item?id=9131619]</i> </p> </ol> <h2>On Git Versus Fossil</h2> <ol> <li value=14> After prolonged exposure to fossil, i tend to get the jitters when I work with git... <p class="local-indent"> <i>sriku - at [https://news.ycombinator.com/item?id=16104427]</i> </p> <li> Just want to say thanks for fossil making my life easier.... Also <nowiki>[for]</nowiki> not having a misanthropic command line interface. <p class="local-indent"> <i>Joshua Paine at [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg02736.html]</i> </p> <li>We use it at a large university to manage code that small teams write. The runs everywhere, ease of installation and portability is something that seems to be a good fit with the environment we have (highly ditrobuted, sometimes very restrictive firewalls, OSX/Win/Linux). We are happy with it and teaching a Msc/Phd student (read complete novice) fossil has just been a smoother ride than Git was. <p class="local-indent"> <i>viablepanic at [https://www.reddit.com/r/programming/comments/bxcto/why_not_fossil_scm/c0p30b4?utm_source=share&utm_medium=web2x&context=3]</i> </p> <li>In the fossil community - and hence in fossil itself - development history is pretty much sacrosanct. The very name "fossil" was to chosen to reflect the unchanging nature of things in that history. <br><br> In git (or rather, the git community), the development history is part of 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. <p class="local-indent"> <i>Mike Meyer on the Fossil mailing list, 2011-10-04</i> </p> <li>github is such a pale shadow of what fossil does. <p class="local-indent"> <i>dkf on the Tcl chatroom, 2013-12-06</i> </p> <li>[With fossil] I actually enjoy keeping track of source files again. <p class="local-indent"> <a href="https://wholesomedonut.prose.sh/using-fossil-not-git">https://wholesomedonut.prose.sh/using-fossil-not-git</a> </p> </ol> |
Added www/rebaseharm.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 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 | # Rebase Considered Harmful Fossil deliberately omits a "rebase" command because the original designer of Fossil (and [original author][vhist] of this article) considers rebase to be an anti-pattern to be avoided. This article attempts to explain that point of view. [vhist]: /finfo?name=www/rebaseharm.md&ubg ## 1.0 Rebasing is dangerous Most people, even strident advocates of rebase, agree that rebase can cause problems when misused. The Git rebase documentation talks about the [golden rule of rebasing][golden]: never rebase on a public branch. Horror stories of misused rebase abound, and the rebase documentation devotes considerable space toward explaining how to recover from rebase errors and/or misuse. ## <a id="cap-loss"></a>2.0 Rebase provides no new capabilities Sometimes sharp and dangerous tools are justified, because they accomplish things that cannot be done otherwise, or at least cannot be done easily. Rebase does not fall into that category, because it provides no new capabilities. ### <a id="orphaning"></a>2.1 A rebase is just a merge with historical references omitted A rebase is really nothing more than a merge (or a series of merges) that deliberately forgets one of the parents of each merge step. To help illustrate this fact, consider the first rebase example from the [Git documentation][gitrebase]. The merge looks like this: ~~~ pikchr toggle center scale = 0.8 circle "C0" fit arrow right 50% circle same "C1" arrow same circle same "C2" arrow same circle same "C3" arrow same circle same "C5" circle same "C4" at 1cm above C3 arrow from C2 to C4 chop arrow from C4 to C5 chop ~~~ And the rebase looks like this: ~~~ pikchr toggle center scale = 0.8 circle "C0" fit arrow right 50% circle same "C1" arrow same circle same "C2" arrow same circle same "C3" arrow same circle same "C4'" circle same "C4" at 1cm above C3 arrow from C2 to C4 chop ~~~ As the [Git documentation][gitrebase] points out, check-ins C4\' and C5 are identical. The only difference between C4\' and C5 is that C5 records the fact that C4 is its merge parent but C4\' does not. Thus, a rebase is just a merge that forgets where it came from. The Git documentation acknowledges this fact (in so many words) and justifies it by saying "rebasing makes for a cleaner history." I read that sentence as a tacit admission that the Git history display capabilities are weak and need active assistance from the user to keep things manageable. Surely a better approach is to record the complete ancestry of every check-in but then fix the tool to show a "clean" history in those instances where a simplified display is desirable and edifying, but retain the option to show the real, complete, messy history for cases where detail and accuracy are more important. So, another way of thinking about rebase is that it is a kind of merge that intentionally forgets some details in order to not overwhelm the weak history display mechanisms available in Git. Wouldn't it be better, less error-prone, and easier on users to enhance the history display mechanisms in Git so that rebasing for a clean, linear history became unnecessary? ### <a id="clean-diffs"></a>2.2 Rebase does not actually provide better feature-branch diffs Another argument, often cited, is that rebasing a feature branch allows one to see just the changes in the feature branch without the concurrent changes in the main line of development. Consider a hypothetical case: ~~~ pikchr toggle center scale = 0.8 circle "C0" fit fill white arrow right 50% circle same "C1" arrow same circle same "C2" arrow same circle same "C4" arrow same circle same "C6" circle same "C3" at last arrow.width + C0.rad*2 heading 30 from C2 arrow right 50% circle same "C5" arrow from C2 to C3 chop box ht C3.y-C2.y wid C6.e.x-C0.w.x+1.5*C1.rad at C2 behind C0 fill 0xc6e2ff color 0xaac5df box ht previous.ht wid previous.wid*0.55 with .se at previous.ne \ behind C0 fill 0x9accfc color 0xaac5df text "feature" with .s at previous.n text "main" with .n at first box.s ~~~ In the above, a feature branch consisting of check-ins C3 and C5 is run concurrently with the main line in check-ins C4 and C6. Advocates for rebase say that you should rebase the feature branch to the tip of main in order to remove main-line development differences from the feature branch's history: ~~~ pikchr toggle center # Duplicated below in section 5.0 scale = 0.8 circle "C0" fit fill white arrow right 50% circle same "C1" arrow same circle same "C2" arrow same circle same "C4" arrow same circle same "C6" circle same "C3" at last arrow.width + C0.rad*2 heading 30 from C2 arrow right 50% circle same "C5" arrow from C2 to C3 chop C3P: circle same "C3'" at first arrow.width + C0.rad*2 heading 30 from C6 arrow right 50% from C3P.e C5P: circle same "C5'" arrow from C6 to C3P chop box ht C3.y-C2.y wid C5P.e.x-C0.w.x+1.5*C1.rad with .w at 0.5*(first arrow.wid) west of C0.w \ behind C0 fill 0xc6e2ff color 0xaac5df box ht previous.ht wid previous.e.x - C2.w.x with .se at previous.ne \ behind C0 fill 0x9accfc color 0xaac5df ~~~ You could choose to collapse C3\' and C5\' into a single check-in as part of this rebase, but that's a side issue we'll deal with [separately](#collapsing). Because Fossil purposefully lacks rebase, the closest you can get to this same check-in history is the following merge: ~~~ pikchr toggle center scale = 0.8 circle "C0" fit fill white arrow right 50% circle same "C1" arrow same circle same "C2" arrow same circle same "C4" arrow same circle same "C6" circle same "C3" at last arrow.width + C0.rad*2 heading 30 from C2 arrow right 50% circle same "C5" arrow same circle same "C7" arrow from C2 to C3 chop arrow from C6 to C7 chop box ht C3.y-C2.y wid C7.e.x-C0.w.x+1.5*C1.rad with .w at 0.5*(first arrow.wid) west of C0.w \ behind C0 fill 0xc6e2ff color 0xaac5df box ht previous.ht wid previous.e.x - C2.w.x with .se at previous.ne \ behind C0 fill 0x9accfc color 0xaac5df ~~~ Check-ins C5\' and C7 check-ins hold identical code. The only difference is in their history. The argument from rebase advocates is that with merge it is difficult to see only the changes associated with the feature branch without the commingled mainline changes. In other words, diff(C2,C7) shows changes from both the feature branch and from the mainline, whereas in the rebase case diff(C6,C5\') shows only the feature branch changes. But that argument is comparing apples to oranges, since the two diffs do not have the same baseline. The correct way to see only the feature branch changes in the merge case is not diff(C2,C7) but rather diff(C6,C7). <div align=center> | Rebase | Merge | What You See | |---------------|-------------|----------------------------------------| | diff(C2,C5\') | diff(C2,C7) | Commingled branch and mainline changes | | diff(C6,C5\') | diff(C6,C7) | Branch changes only | </div> Remember: C7 and C5\' are bit-for-bit identical, so the output of the diff is not determined by whether you select C7 or C5\' as the target of the diff, but rather by your choice of the diff source, C2 or C6. So, to help with the problem of viewing changes associated with a feature branch, perhaps what is needed is not rebase but rather better tools to help users identify an appropriate baseline for their diffs. ## <a id="siloing"></a>3.0 Rebase encourages siloed development The [golden rule of rebasing][golden] is that you should never do it on public branches, so if you are using rebase as intended, that means you are keeping private branches. Or, to put it another way, you are doing siloed development. You are not sharing your intermediate work with collaborators. This is not good for product quality. [Nagappan, et. al][nagappan] studied bugs in Windows Vista and found that best predictor of bugs is the distance on the org-chart between the stake-holders. The bug rate is inversely related to the amount of communication among the engineers. Similar findings arise in other disciplines. Keeping private branches does not prove that developers are communicating insufficiently, but it is a key symptom that problem. [Weinberg][weinberg] argues programming should be "egoless." That is to say, programmers should avoid linking their code with their sense of self, as that makes it more difficult for them to find and respond to bugs, and hence makes them less productive. Many developers are drawn to private branches out of sense of ego. "I want to get the code right before I publish it." I sympathize with this sentiment, and am frequently guilty of it myself. It is humbling to display your stupid mistake to the whole world on an Internet that never forgets. And yet, humble programmers generate better code. What is the fastest path to solid code? Is it to continue staring at your private branch to seek out every last bug, or is it to publish it as-is, whereupon the many eyeballs will immediately see that last stupid error in the code? Testing and development are often done by separate groups within a larger software development organization, because developers get too close to their own code to see every problem in it. Given that, is it better for those many eyeballs to find your problems while they're still isolated on a feature branch, or should that vetting wait until you finally push a collapsed version of a private working branch to the parent repo? Will the many eyeballs even see those errors when they’re intermingled with code implementing some compelling new feature? ## <a id="timestamps"></a>4.0 Rebase causes timestamp confusion Consider the earlier example of rebasing a feature branch: ~~~ pikchr toggle center # Copy of second diagram in section 2.2 above scale = 0.8 circle "C0" fit fill white arrow right 50% circle same "C1" arrow same circle same "C2" arrow same circle same "C4" arrow same circle same "C6" circle same "C3" at last arrow.width + C0.rad*2 heading 30 from C2 arrow right 50% circle same "C5" arrow from C2 to C3 chop C3P: circle same "C3'" at first arrow.width + C0.rad*2 heading 30 from C6 arrow right 50% from C3P.e C5P: circle same "C5'" arrow from C6 to C3P chop box ht C3.y-C2.y wid C5P.e.x-C0.w.x+1.5*C1.rad with .w at 0.5*(first arrow.wid) west of C0.w \ behind C0 fill 0xc6e2ff color 0xaac5df box ht previous.ht wid previous.e.x - C2.w.x with .se at previous.ne \ behind C0 fill 0x9accfc color 0xaac5df ~~~ What timestamps go on the C3\' and C5\' check-ins? If you choose the same timestamps as the original C3 and C5, then you have the odd situation C3' is older than its parent C6. We call that a "timewarp" in Fossil. Timewarps can also happen due to misconfigured system clocks, so they are not unique to rebase, but they are very confusing and so best avoided. The other option is to provide new unique timestamps for C3' and C5' but then you lose the information about when those check-ins were originally created, which can make historical analysis of changes more difficult. It might also complicate the legal defense of prior art claims. ## <a id="lying"></a>5.0 Rebase misrepresents the project history By discarding parentage information, rebase attempts to deceive the reader about how the code actually came together. Git’s rebase feature is more than just an alternative to merging: it also provides mechanisms for changing the project history in order to make editorial changes. Fossil shows that you can get similar effects without modifying historical records, allowing users to: 1. Edit check-in comments to fix typos or enhance clarity 2. Attach supplemental notes to check-ins or whole branches 3. Hide ill-conceived or now-unused branches from routine display 4. Fix faulty check-in date/times resulting from misconfigured system clocks 5. Cross-reference check-ins with each other, or with wiki, tickets, forum posts, and/or embedded documentation …and so forth. Fossil allows all of this not by removing or modifying existing repository entries, but rather by adding new supplemental records. Fossil keeps the original incorrect or unclear inputs and makes them readily accessible, preserving the original historical record. Fossil doesn’t make the user tell counter-factual “stories,†it only allows the user to provide annotations to provide a more readable edited presentation for routine display purposes. Git needs rebase because it lacks these annotation facilities. Rather than consider rebase a desirable feature missing in Fossil, ask instead why Git lacks support for making editorial changes to check-ins without modifying history? Wouldn't it be better to fix the version control tool rather than requiring users to fabricate a fictitious project history? ## <a id="collapsing"></a>6.0 Collapsing check-ins throws away valuable information One of the oft-cited advantages of rebasing in Git is that it lets you collapse multiple check-ins down to a single check-in to make the development history “clean.†The intent is that development appear as though every feature were created in a single step: no multi-step evolution, no back-tracking, no false starts, no mistakes. This ignores actual developer psychology: ideas rarely spring forth from fingers to files in faultless finished form. A wish for collapsed, finalized check-ins is a wish for a counterfactual situation. The common counterargument is that collapsed check-ins represent a better world, the ideal we're striving for. What that argument overlooks is that we must throw away valuable information to get there. ### <a id="empathy"></a>6.1 Individual check-ins support mutual understanding Ideally, future developers of our software can understand every feature in it using only context available in the version of the code they start work with. Prior to widespread version control, developers had no choice but to work that way. Pre-existing codebases could only be understood as-is or not at all. Developers in that world had an incentive to develop software that was easy to understand retrospectively, even if they were selfish people, because they knew they might end up being those future developers! Yet, sometimes we come upon a piece of code that we simply cannot understand. If you have never asked yourself, "What was this code's developer thinking?" you haven't been developing software for very long. When a developer can go back to the individual check-ins leading up to the current code, they can work out the answers to such questions using only the level of personal brilliance necessary to be a good developer. To understand such code using only the finished form, you are asking future developers to make intuitive leaps that the original developer was unable to make. In other words, you are asking your future maintenance developers to be smarter than the original developers! That's a beautiful wish, but there's a sharp limit to how far you can carry it. Eventually you hit the limits of human brilliance. When the operation of some bit of code is not obvious, both Fossil and Git let you run a [`blame`](/help?cmd=blame) on the code file to get information about each line of code, and from that which check-in last touched a given line of code. If you squash the check-ins on a branch down to a single check-in, you throw away the information leading up to that finished form. Fossil not only preserves the check-ins surrounding the one that included the line of code you're trying to understand, its [superior data model][sdm] lets you see the surrounding check-ins in both directions; not only what lead up to it, but what came next. Git can't do that short of crawling the block-chain backwards from the tip of the branch to the check-in you’re looking at, an expensive operation. We believe it is easier to understand a line of code from the 10-line check-in it was a part of — and then to understand the surrounding check-ins as necessary — than it is to understand a 500-line check-in that collapses a whole branch's worth of changes down to a single finished feature. [sdm]: ./fossil-v-git.wiki#durable ### <a id="bisecting"></a>6.2 Bisecting works better on small check-ins Git lets a developer write a feature in ten check-ins but collapse it down to an eleventh check-in and then deliberately push only that final collapsed check-in to the parent repo. Someone else may then do a bisect that blames the merged check-in as the source of the problem they’re chasing down; they then have to manually work out which of the 10 steps the original developer took to create it to find the source of the actual problem. An equivalent push in Fossil will send all 11 check-ins to the parent repository so that a later investigator doing the same sort of bisect sees the complete check-in history. That bisect will point the investigator at the single original check-in that caused the problem. ### <a id="comments"></a>6.3 Multiple check-ins require multiple check-in comments The more comments you have from a given developer on a given body of code, the more concise documentation you have of that developer's thought process. To resume the bisecting example, a developer trying to work out what the original developer was thinking with a given change will have more success given a check-in comment that explains what the one check-in out of ten blamed by the "bisect" command was trying to accomplish than if they must work that out from the eleventh check-in's comment, which only explains the "clean" version of the collapsed feature. ### <a id="cherrypicking"></a>6.4 Cherry-picks work better with small check-ins While working on a new feature in one branch, you may come across a bug in the pre-existing code that you need to fix in order for work on that feature to proceed. You could choose to switch briefly back to the parent branch, develop the fix there, check it in, then merge the parent back up to the feature branch in order to continue work, but that's distracting. If the fix isn't for a critical bug, fixing it on the parent branch can wait, so it's better to maintain your mental working state by fixing the problem in place on the feature branch, then check the fix in on the feature branch, resume work on the feature, and later merge that fix down into the parent branch along with the feature. But now what happens if another branch *also* needs that fix? Let us say our code repository has a branch for the current stable release, a development branch for the next major version, and feature branches off of the development branch. If we rebase each feature branch down into the development branch as a single check-in, pushing only the rebase check-in up to the parent repo, only that fix's developer has the information locally to perform the cherry-pick of the fix onto the stable branch. Developers working on new features often do not care about old stable versions, yet that stable version may have an end user community that depends on that version, who either cannot wait for the next stable version or who wish to put off upgrading to it for some time. Such users want backported bug fixes, yet the developers creating those fixes have poor incentives to provide those backports. Thus the existence of maintenance and support organizations, who end up doing such work. (There is [a famous company][rh] that built a multi-billion dollar enterprise on such work.) This work is far easier when each cherry-pick transfers completely and cleanly from one branch to another, and we increase the likelihood of achieving that state by working from the smallest check-ins that remain complete. If a support organization must manually disentangle a fix from a feature check-in, they are likely to introduce new bugs on the stable branch. Even if they manage to do their work without error, it takes them more time to do the cherry-pick that way. [rh]: https://en.wikipedia.org/wiki/Red_Hat ### <a id="backouts"></a>6.5 Back-outs also work better with small check-ins The inverse of the cherry-pick merge is the back-out merge. If you push only a collapsed version of a private working branch up to the parent repo, those working from that parent repo cannot automatically back out any of the individual check-ins that went into that private branch. Others must either manually disentangle the problematic part of your merge check-in or back out the entire feature. ## <a id="better-plan"></a>7.0 Cherry-pick merges work better than rebase Perhaps there are some cases where a rebase-like transformation is actually helpful, but those cases are rare, and when they do come up, running a series of cherry-pick merges achieves the same topology with several advantages: 1. In Fossil, cherry-pick merges preserve an honest and clear record of history. Fossil remembers where a cherry-pick came from, and it shows this in its timeline, so other developers can understand how a cherry-pick based commit came together. Git lacks the ability to remember the source of a cherry-pick as part of the commit. This fact has no direct bearing on this document’s thesis, but we can make a few observations. First, Git forgets history in more cases than in rebasing. Second, if Git remembered the source of cherry-picks in commits, Git users might have a better argument for avoiding rebase, because they’d have an alternative that *didn’t* lose history. 2. Fossil’s [test before commit philosophy][tbc] means you can test a cherry-pick before committing it. Because Fossil allows multiple cherry-picks in a single commit and it remembers them all, you can do this for a complicated merge in step-wise fashion. Git commits cherry-picks straight to the repository, so that if it results in a bad state, you have to do something drastic like `git reset --hard` to repair the damage. 3. Cherry-picks keep both the original and the revised check-ins, so both timestamps are preserved. [tbc]: ./fossil-v-git.wiki#testing ## <a id="conclusion"></a>8.0 Summary and conclusion Rebasing is an anti-pattern. It is dishonest. It deliberately omits historical information. It causes problems for collaboration. And it has no offsetting benefits. For these reasons, rebase is intentionally and deliberately omitted from the design of Fossil. [golden]: https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing [gitrebase]: https://git-scm.com/book/en/v2/Git-Branching-Rebasing [nagappan]: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-2008-11.pdf [weinberg]: https://books.google.com/books?id=76dIAAAAMAAJ |
Deleted www/reference.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added www/relatedwork.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 | # Related Work ## Support Projects * [SQLite]: C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine * [pikchr]: PIC-like markup language for diagrams in technical documentation * [althttpd]: simple, secure, and low resource usage webserver that has run the https://sqlite.org/ website since 2004 * [Lemon Parser Generator][lemon]: re-entrant and thread-safe LALR(1) parser with a less error-prone grammar syntax than YACC or BISON * [Makeheaders]: automatically generate header files for C/C++ projects ## Fossil Inspired Projects * [libfossil]: 3rd party Fossil SCM Library API * [fnc]: interactive text-based user interface for Fossil * [ChiselApp]: Free Fossil SCM hosting! * [Inskinerator]: The Fossil Skin Generator * [Fuel]: cross-platform GUI front-end for the excellent Fossil SCM * [fsl]: Tcl/Expect wrapper script extending Fossil functionality ## Editor Plugins * [Emacs-Fossil][emacsfsl]: GNU Emacs VC backend for the Fossil version control system * [VS Code][vscode]: Integrated Fossil source control in Visual Studio Code * [Qt Creator Plugin][qtfsl]: Fossil SCM plugin for the Qt Creator IDE * [Jetbrains IDE Plugin][jetbrains]: Fossil SCM plugin for [CLion], [IntelliJ], [GoLand], and more * [NetBeans Plugin][netbeans]: NetBeans plugin module to use Fossil SCM ## Version Control/Software Configuration Management * [Git]: Free and open source distributed version control system * [Subversion]: Apache's open source version control system * [Mercurial]: free, distributed source control management tool * [Game of Trees][got]: version control which prioritizes ease of use and simplicity over flexibility * [Darcs]: free and open source, cross-platform version control system * [Pijul]: patch-based distributed version control system * [Sapling]: A Scalable, User-Friendly Source Control System ## Podcasts * [Corecursive #066][corec66]: The Untold Story of SQLite * [The Changelog #454][changelog454]: Richard Hipp returns * [The Changelog #201][changelog201]: Why SQLite succeeded as a database * [bsdtalk194][bsdtalk]: Interview with D. Richard Hipp * [Two Weeks of Databases][db2w]: Richard Hipp interviewed by Federico Razzoli * [Software Engineering Daily][swed]: SQLite with D. Richard Hipp * [Floss Weekly 26][floss26]: Interview with D. Richard Hipp, creator of SQLite ## Miscellany * [Tcl]: a simple-to-learn yet very powerful programming language [althttpd]: https://sqlite.org/althttpd/doc/trunk/althttpd.md [bsdtalk]: https://bsdtalk.blogspot.com/2010/07/bsdtalk194-fossil-scm-with-d-richard.html [changelog201]: https://changelog.com/podcast/201 [changelog454]: https://changelog.com/podcast/454 [ChiselApp]: https://chiselapp.com/ [CLion]: https://www.jetbrains.com/clion/ [corec66]: https://corecursive.com/066-sqlite-with-richard-hipp/ [Darcs]: http://darcs.net/ [db2w]: https://youtu.be/2eaQzahCeh4 [emacsfsl]: https://chiselapp.com/user/venks/repository/emacs-fossil/doc/tip/README.md [floss26]: https://twit.tv/shows/floss-weekly/episodes/26 [fnc]: https://fnc.bsdbox.org [fsl]: http://fossil.0branch.com/fsl [Fuel]: https://fuel-scm.org/fossil/index [Git]: https://git-scm.com [GoLand]: https://www.jetbrains.com/go/ [got]: https://gameoftrees.org [Inskinerator]: https://tangentsoft.com/inskinerator [IntelliJ]: https://www.jetbrains.com/idea/ [jetbrains]: https://plugins.jetbrains.com/plugin/7479-fossil-integration [lemon]: https://www.hwaci.com/sw/lemon/ [libfossil]: https://fossil.wanderinghorse.net/r/libfossil/wiki/home [Makeheaders]: https://fossil-scm.org/home/doc/trunk/tools/makeheaders.html [Mercurial]: https://www.mercurial-scm.org/ [netbeans]: https://chiselapp.com/user/backendzeit/repository/netbeans-fossil-plugin/index [Pijul]: https://pijul.org [pikchr]: https://pikchr.org [qtfsl]: https://code.qt.io/cgit/qt-creator/plugin-fossil-scm.git/ [Sapling]: https://sapling-scm.com [SQLite]: https://sqlite.org/index.html [Subversion]: https://subversion.apache.org/ [swed]: https://softwareengineeringdaily.com/2015/11/13/sqlite-with-d-richard-hipp/ [Tcl]: https://core.tcl-lang.org/tcl/wiki?name=Index [VSCode]: https://marketplace.visualstudio.com/items?itemName=koog1000.fossil |
Changes to www/reviews.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 | <title>Reviews</title> <b>External links:</b> * [https://www.nixtu.info/2010/03/fossil-dvcs-on-go-first-impressions.html | Fossil DVCS on the Go - First Impressions] <b>See Also:</b> * [./quotes.wiki | Short Quotes on Fossil, Git, And DVCSes] <b>Daniel writes on 2009-01-06:</b> <div class="indent"> The reasons I use fossil are that it's the only version control I have found that I can get working through the VERY annoying MS firewalls at work.. (albeit through an ntlm proxy) and I just love single .exe applications! </div> <b>Joshua Paine on 2010-10-22:</b> <div class="indent"> With one of my several hats on, I'm in a small team using git. Another team member just checked some stuff into trunk that should have been on a branch. Nothing else had happened since, so in fossil I would have just edited that commit and put it on a new branch. In git that can't actually be done without danger once other people have pulled, so I had to create a new commit rolling back the changes, then branch and cherry pick the earlier changes, then figure out how to make my new branch shared instead of private. Just want to say thanks for fossil making my life easier on most of my projects, and being able to move commits to another branch after the fact and shared-by-default branches are good features. Also not having a misanthropic command line interface. </div> <b>Stephan Beal writes on 2009-01-11:</b> <div class="indent"> Sometime in late 2007 I came across a link to fossil on <a href="http://www.sqlite.org/">sqlite.org</a>. It was a good thing I bookmarked it, because I was never able to find the link again (it might have been in a bug report or something). The reasons I first took a close look at it were (A) it stemmed from the sqlite project, which I've held in high regards for years (e.g. I wrote JavaScript bindings for it: <a href="http://spiderape.sourceforge.net/plugins/sqlite/"> http://spiderape.sourceforge.net/plugins/sqlite/</a>), and (B) it could run as a CGI. That second point might seem a bit archaic, but in practice CGI is the only way most hosted sites can set up a shared source repository with multiple user IDs. (i'm not about to give out my only account password or SSH key for my hosted sites, no matter how much I trust the other developers, and none of my hosters allow me to run standalone servers or add Apache modules.) So I tried it out. The thing which bugged me most about it was having to type "commit" or "com" instead of "ci" for checking in (as is custom in all other systems I've used), despite the fact that fossil uses "ci" as a filter in things like the timeline view. Looking back now, I have used fossil for about about 95% of my work in the past year (<a href="http://blog.s11n.net/?p=71"><i>dead link</i></a>), in over 15 source trees, and I now get tripped up when I have to use svn or cvs. So, having got over typing "fossil com -m ...", here's why I love it so much... Point #1: CGI Again, this sounds archaic, but fossil has allowed me to share source trees which I cannot justifiably host in other projects I work on |
︙ | ︙ | |||
102 103 104 105 106 107 108 | Firefox, or the Linux Kernel), but 99.9% of projects never reach anywhere near that size or complexity. In summary: I remember my first reaction to fossil being, "this will be an | | | | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | Firefox, or the Linux Kernel), but 99.9% of projects never reach anywhere near that size or complexity. In summary: I remember my first reaction to fossil being, "this will be an excellent solution for small projects (like the dozens we've all got sitting on our hard drives but which don't justify the hassle of version control)." A year of daily use in over 15 source trees has confirmed that, and I continue to heartily recommend fossil to other developers I know who also have their own collection of "unhosted" pet projects. </div> |
Added www/scgi.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 | <title>Fossil SCGI</title> To run Fossil using SCGI, start the [/help/server|fossil server] command with the --scgi command-line option. You will probably also want to specific an alternative TCP/IP port using --port. For example: <pre> fossil server $REPOSITORY --port 9000 --scgi </pre> Then configure your SCGI-aware web-server to send SCGI requests to port 9000 on the machine where Fossil is running. A typical configuration for this in Nginx is: <pre> location ~ ^/demo_project/ { include scgi_params; scgi_pass localhost:9000; scgi_param SCRIPT_NAME "/demo_project"; scgi_param HTTPS "on"; } </pre> 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.
1 2 | <title>Fossil Repository Integrity Self-Checks</title> | < < | | > > | | > | | | | | | | | | | | | | | | | | | | | | | | | > | 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 | <title>Fossil Repository Integrity Self-Checks</title> Fossil is designed with features to give it a high level of integrity so that users can have confidence that content will never be mangled or lost by Fossil. This note describes the defensive measures that Fossil uses to help prevent information loss due to bugs. Fossil has been hosting itself and many other projects for years now. Many bugs have been encountered. But, thanks in large part to the defensive measures described here, no data has been lost. The integrity checks are doing their job well.</p> <h2>Atomic Check-ins With Rollback</h2> The Fossil repository is stored in an <a href="http://www.sqlite.org/">SQLite</a> database file. ([./tech_overview.wiki | Addition information] about the repository file format.) SQLite is very mature and stable and has been in wide-spread use for many years, so we are confident it will not cause repository corruption. SQLite databases do not corrupt even if a program or system crash or power failure occurs in the middle of the update. If some kind of crash does occur in the middle of a change, then all the changes are rolled back the next time that the database is accessed. A check-in operation in Fossil makes many changes to the repository database. But all these changes happen within a single transaction. If something goes wrong in the middle of the commit, even if that something is a power failure or OS crash, then the transaction is rolled back and the database is unchanged. <h2>Verification Of Delta Encodings Prior To Transaction Commit</h2> The content files that comprise the global state of a Fossil repository are stored in the repository as a tree. The leaves of the tree are stored as zlib-compressed BLOBs. Interior nodes are deltas from their descendants. A lot of encoding is going on. There is zlib-compression which is relatively well-tested but still might cause corruption if used improperly. And there is the relatively new [./delta_encoder_algorithm.wiki | 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 and the SHA1 or SHA3-256 hash of the original content of that file. Then just before transaction commit, Fossil re-extracts the original content of all files that were written, recomputes the hash, and verifies that the recomputed hash still matches. If anything does not match up, an error message is printed and the transaction rolls back. So, in other words, Fossil always checks to make sure it can re-extract a file before it commits a change to that file. Hence bugs in Fossil are unlikely to corrupt the repository in a way that prevents us from extracting historical versions of 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 algorithm (MD5) in order to avoid common-mode failures in the hash algorithm implementation. <h2>Checksums On Structural Artifacts And Deltas</h2> Every [./fileformat.wiki | structural artifact] in a Fossil repository contains a "Z-card" bearing an MD5 checksum over the rest of the artifact. Any mismatch causes the structural artifact to be ignored. The [./delta_format.wiki | file delta format] includes a 32-bit checksum of the target file. Whenever a file is reconstructed from a delta, that checksum is verified to make sure the reconstruction was done correctly. <h2>Reliability Versus Performance</h2> Some version control systems make a big deal out of being "high performance" or the "fastest version control system". Fossil makes no such claims and has no such ambition. Indeed, profiling indicates that Fossil bears a substantial performance cost for doing all of the checksumming and verification outlined above. Fossil takes the philosophy of the <a href="http://en.wikipedia.org/wiki/The_Tortoise_and_the_Hare">tortoise</a>: reliability is more important than raw speed. The developers of Fossil see no merit in getting the wrong answer quickly. Fossil may not be the fastest versioning system, but it is <i>fast enough</i>. Fossil runs quickly enough to stay out of the developer's way. Most operations complete in milliseconds, faster than you can press the "Enter" key. |
Changes to www/selfhost.wiki.
1 | <title>Fossil Self-Hosting Repositories</title> | < | | < | | | > > > | | | | | | | | | | | > | | | | | | | > > > > > > > | | | | | | | | > > | 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 | <title>Fossil Self-Hosting Repositories</title> Fossil has self-hosted since 2007-07-21. As of 2017-07-25 there are three publicly accessible repositories for the Fossil source code: 1. [https://fossil-scm.org/] 2. [https://www2.fossil-scm.org/] 3. [https://www3.fossil-scm.org/site.cgi] The canonical repository is (1). Repositories (2) and (3) automatically stay in synchronization with (1) via a <a href="http://en.wikipedia.org/wiki/Cron">cron job</a> that invokes "fossil sync" at regular intervals. Repository (2) also publishes a [https://github.com/drhsqlite/fossil-mirror|GitHub mirror of Fossil] as a demonstration of [./mirrortogithub.md|how that can be done]. Note that the two secondary repositories are more than just read-only mirrors. All three servers support full read/write capabilities. Changes (such as new tickets or wiki or check-ins) can be implemented on any of the three servers and those changes automatically propagate to the other two servers. Server (1) runs as a [./aboutcgi.wiki|CGI script] on a <a href="http://www.linode.com/">Linode 8192</a> located in Dallas, TX - on the same virtual machine that hosts <a href="http://www.sqlite.org/">SQLite</a> and over a dozen other smaller projects. This demonstrates that Fossil can run on a low-power host processor. Multiple fossil-based projects can easily be hosted on the same machine, even if that machine is itself one of several dozen virtual machines on single physical box. The CGI script that runs the canonical Fossil self-hosting repository is as follows: <pre> #!/usr/bin/fossil repository: /fossil/fossil.fossil </pre> Server (3) ran for 10 years as a CGI script on a shared hosting account at <a href="http://www.he.net/">Hurricane Electric</a> in Fremont, CA. This server demonstrated 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 ran on the Hurricane Electric server was the same as the CGI script shown above, except that the pathnames are modified to suit the environment: <pre> #!/home/hwaci/bin/fossil repository: /home/hwaci/fossil/fossil.fossil </pre> In recent years, virtual private servers have become a more flexible and less expensive hosting option compared to shared hosting accounts. So on 2017-07-25, server (3) was moved onto a $5/month "droplet" [https://en.wikipedia.org/wiki/Virtual_private_server|VPS] from [https://www.digitalocean.com|Digital Ocean] located in San Francisco. Server (3) is synchronized with the canonical server (1) by running a command similar to the following via cron: <pre> /usr/local/bin/fossil all sync -u </pre> Server (2) is a <a href="http://www.linode.com/">Linode 4096</a> located in Newark, NJ and set up just like the canonical server (1) with the addition of a cron job for synchronization. The same cron job also runs the [/help?cmd=git|fossil git export] command after each sync in order to [./mirrortogithub.md#ex1|mirror all changes to GitHub]. |
Deleted www/server.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added www/server/any/althttpd.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 | # Serving via althttpd [Althttpd][althttpd] is a light-weight web server that has been used to implement the SQLite and Fossil websites for well over a decade. Althttpd strives for simplicity, security, ease of configuration, and low resource usage. To set up a Fossil server as CGI on a host running the althttpd web server, follow these steps. <ol> <li>Get the althttpd webserver running on the host. This is easily done by following the [althttpd documentation][althttpd]. <li>Create a CGI script for your Fossil repository. The script will be typically be two lines of code that look something like this: ~~~ #!/usr/bin/fossil repository: /home/yourlogin/fossils/project.fossil ~~~ Modify the filenames to conform to your system, of course. The CGI script accepts [other options][cgi] besides the repository:" line. You can add in other options as you desire, but the single "repository:" line is normally all that is needed to get started. <li>Make the CGI script executable. <li>Verify that the fossil repository file and the directory that contains the repository are both writable by whatever user the web server is running and. </ol> And you are done. Visit the URL that corresponds to the CGI script you created to start using your Fossil server. *[Return to the top-level Fossil server article.](../)* [althttpd]: https://sqlite.org/althttpd/ [cgi]: ../../cgi.wiki |
Added www/server/any/cgi.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 | # Serving via CGI A Fossil server can be run from most ordinary web servers as a CGI program. This feature allows Fossil to seamlessly integrate into a larger website. The [self-hosting Fossil repository web site](../../selfhost.wiki) is implemented using CGI. See the [How CGI Works](../../aboutcgi.wiki) page for background information on the CGI protocol. To run Fossil as CGI, create a CGI script (here called "repo") in the CGI directory of your web server with content like this: #!/usr/bin/fossil repository: /home/fossil/repo.fossil Adjust the paths appropriately. It may be necessary to set certain permissions on this file or to modify an `.htaccess` file or make other server-specific changes. Consult the documentation for your particular web server. The following permissions are *normally* required, but, again, may be different for a particular configuration: * The Fossil binary (`/usr/bin/fossil` in the example above) must be readable/executable. * *All* directories leading up to the Fossil binary must be readable by the process which executes the CGI. * The CGI script must be executable for the user under which it will run, which often differs from the one running the web server. Consult your site's documentation or the web server’s system administrator. * *All* directories leading to the CGI script must be readable by the web server. * The repository file *and* the directory containing it must be writable by the same account which executes the Fossil binary. (This might differ from the user the web server normally runs under.) The directory holding the repository file(s) needs to be writable so that SQLite can write its journal files. When using another access control system, such as AppArmor or SELinux, it may be necessary to explicitly permit that account to read and write the necessary files. * Fossil must be able to create temporary files in a [directory that varies by host OS](../../env-opts.md#temp). When the CGI process is operating [within a chroot](../../chroot.md), ensure that this directory exists and is readable/writeable by the user who executes the Fossil binary. Once the CGI script is set up correctly, and assuming your server is also set correctly, you should be able to access your repository with a URL like: <b>http://mydomain.org/cgi-bin/repo</b> This is assuming you are running a web server like Apache that uses a “`cgi-bin`†directory for scripts like our “`repo`†example. To serve multiple repositories from a directory using CGI, use the "directory:" tag in the CGI script rather than "repository:". You might also want to add a "notfound:" tag to tell where to redirect if the particular repository requested by the URL is not found: #!/usr/bin/fossil directory: /home/fossil/repos notfound: http://url-to-go-to-if-repo-not-found/ Once deployed, a URL like: <b>http://mydomain.org/cgi-bin/repo/XYZ</b> will serve up the repository `/home/fossil/repos/XYZ.fossil` if it exists. Additional options available to the CGI script are [documented separately](../../cgi.wiki). #### CGI with Apache behind an Nginx proxy For the case where the Fossil repositories live on a computer, itself behind an Internet-facing machine that employs Nginx to reverse proxy HTTP(S) requests and take care of the TLS part of the connections in a transparent manner for the downstream web servers, the CGI parameter `HTTPS=on` might not be set. However, Fossil in CGI mode needs it in order to generate the correct links. Apache can be instructed to pass this parameter further to the CGI scripts for TLS connections with a stanza like SetEnvIf X-Forwarded-Proto "https" HTTPS=on in its config file section for CGI, provided that proxy_set_header X-Forwarded-Proto $scheme; has been be added in the relevant proxying section of the Nginx config file. *[Return to the top-level Fossil server article.](../)* #### Apache mod_cgi and `CONTENT_LENGTH` At some point in its 2.4.x family, Apache's `mod_cgi` stopped relaying the Content-Length header in the HTTP reply from CGIs back to clients. However, Fossil clients prior to 2024-04-17 depended on seeing the Content-Length header and were unable to handle HTTP replies without one. The change in Apache behavior caused "fossil clone" and "fossil sync" to stop working. There are two possible fixes to this problem: 1. Restore legacy behavior in Apache by adding the following to the Apache configuration, scoped to the `<Directory>` entry or entries in which fossil is being served via CGI: <blockquote><pre> <Directory ...> SetEnv ap_trust_cgilike_cl "yes" </Directory> </pre></blockquote> 2. Upgrade your Fossil client to any trunk check-in after 2024-04-17, as Fossil was upgraded to be able to deal with the missing Content-Length field by [check-in a8e33fb161f45b65](/info/a8e33fb161f45b65). |
Added www/server/any/http-over-ssh.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 | # Forcing Use of Fossil’s RBAC over SSH Andy Bradford posted a [clever solution][sshfc] to the problem of Fossil’s RBAC system [being ignored](../../caps/#webonly) over `ssh://` URLs: use OpenSSH’s `ForceCommand` feature to route the sync transfer protocol data over `fossil http` rather than `fossil test-http`. The setup for this is complicated, but it’s a worthy option when you need encrypted communications between the client and server, you already have SSH set up, and [the HTTPS alternative](../../ssl.wiki) is unworkable for some reason. ## 1. Force remote Fossil access through a wrapper script <a id="sshd"></a> Put something like the following into the `sshd_config` file on the Fossil repository server: ``` ssh-config Match Group fossil X11Forwarding no AllowTcpForwarding no AllowAgentForwarding no ForceCommand /home/fossil/bin/wrapper ``` This file is usually found in `/etc/ssh`, but some OSes put it elsewhere. The first line presumes that we will put all users who need to use our Fossil repositories into the `fossil` group, as we will do [below](#perms). You could instead say something like: ``` ssh-config Match User alice,bob,carol,dave ``` You have to list the users allowed to use Fossil in this case because your system likely has a system administrator that uses SSH for remote shell access, so you want to *exclude* that user from the list. For the same reason, you don’t want to put the `ForceCommand` directive outside a `Match` block of some sort. You could instead list the exceptions: ``` ssh-config Match User !evi ``` This would permit only [Evi the System Administrator][evi] to bypass this mechanism. [evi]: https://en.wikipedia.org/wiki/Evi_Nemeth If you have a user that needs both interactive SSH shell access *and* Fossil access, exclude that user from the `Match` rule and use Fossil’s normal `ssh://` URL scheme for those cases. This user will bypass the Fossil RBAC, but they effectively have Setup capability on those repositories anyway by having full read/write access to the DB files via the shell. ## 2. Rewrite the sync command with that wrapper <a id="wrapper"></a> When Fossil syncs over SSH, it attempts to launch a remote Fossil instance with certain parameters in order to set up the HTTP-based sync protocol over that SSH tunnel. We need to preserve some of this command and rewrite other parts to make this work. Here is a simpler variant of Andy’s original wrapper script: ``` sh #!/bin/bash set -- $SSH_ORIGINAL_COMMAND while [ $# -gt 1 ] ; do shift ; done export REMOTE_USER="$USER" ROOT=/home/fossil exec "$ROOT/bin/fossil" http "$ROOT/museum/$(/bin/basename "$1")" ``` The substantive changes are: 1. Move the command rewriting bits to the start. 2. Be explicit about executable paths. You might extend this idea by using chroot, BSD jails, Linux containers, etc. 3. Restrict the Fossil repositories to a single flat subdirectory under the `fossil` user’s home directory. This scheme is easier to secure than one allowing subdirectories, since you’d need to take care of `../` and such to prevent a sandbox escape. 4. Don’t take the user name via the SSH command; to this author’s mind, the user should not get to override their Fossil user name on the remote server, as that permits impersonation. The identity you present to the SSH server must be the same identity that the Fossil repository you’re working with knows you by. Since the users selected by “`Match`†block above are dedicated to using only Fossil in this setup, this restriction shouldn’t present a practical problem. The script’s shebang line assumes `/bin/sh` is POSIX-compliant, but that is not the case everywhere. If the script fails to run on your system, try changing this line to point at `bash`, `dash`, `ksh`, or `zsh`. Also check the absolute paths for local correctness: is `/bin/basename` installed on your system, for example? Under this scheme, you clone with a command like: $ fossil clone ssh://HOST/repo.fossil This will clone the remote `/home/fossil/museum/repo.fossil` repository to your local machine under the same name and open it into a “`repo/`†subdirectory. Notice that we didn’t have to give the `museum/` part of the path: it’s implicit per point #3 above. This presumes your local user name matches the remote user name. Unlike with `http[s]://` URLs, you don’t have to provide the `USER@` part to get authenticated access since this scheme doesn’t permit anonymous cloning. Only if these two user names are different do you need to add the `USER@` bit to the URL. ## 3. Set permissions <a id="perms"></a> This scheme assumes that the users covered by the `Match` rule can read the wrapper script from where you placed it and execute it, and that they have read/write access on the directory where the Fossil repositories are stored. You can achieve all of this on a Linux box with: ``` shell sudo adduser fossil for u in alice bob carol dave ; do sudo adduser $u sudo gpasswd -a fossil $u done sudo -i -u fossil chmod 710 . mkdir -m 750 bin mkdir -m 770 museum ln -s /usr/local/bin/fossil bin ``` You then need to copy the Fossil repositories into `~fossil/museum` and make them readable and writable by group `fossil`. These repositories presumably already have Fossil users configured, with the necessary [user capabilities](../../caps/), the point of this article being to show you how to make Fossil-over-SSH pay attention to those caps. You must also permit use of `REMOTE_USER` on each shared repository. Fossil only pays attention to this environment variable in certain contexts, of which “`fossil http`†is not one. Run this command against each repo to allow that: ``` shell echo "INSERT OR REPLACE INTO config VALUES ('remote_user_ok',1,strftime('%s','now'));" | fossil sql -R museum/repo.fossil ``` Now you can configure SSH authentication for each user. Since Fossil’s password-saving feature doesn’t work in this case, I suggest setting up SSH keys via `~USER/.ssh/authorized_keys` since the SSH authentication occurs on each sync, which Fossil’s default-enabled autosync setting makes frequent. Equivalent commands for other OSes should be readily discerned from the script above. [sshfc]: forum:/forumpost/0d7d6c3df41fcdfd <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
Added www/server/any/index.md.
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | # Portable Fossil Service Options - [Fossil as a standalone HTTP server](./none.md) - [SCGI under your web server of choice](./scgi.md) - [CGI under your web server of choice](./cgi.md) - [CGI under `althttpd`](./althttpd.md) - [Behind `stunnel` to get TLS encryption](./stunnel.md) - [`inetd`](./inetd.md) - [`xinetd`](./xinetd.md) *[Return to the top-level Fossil server article.](../)* |
Added www/server/any/inetd.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 | # Serving via inetd A Fossil server can be launched on-demand by `inetd` by using the [`fossil http`](/help/http) command. To do so, add a line like the following to its configuration file, typically `/etc/inetd.conf`: 80 stream tcp nowait.1000 root /usr/bin/fossil /usr/bin/fossil http /home/fossil/repo.fossil In this example, you are telling `inetd` that when an incoming connection appears on TCP port 80 that it should launch the program `/usr/bin/fossil` 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. 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: fossil 12345/tcp # fossil server and use the symbolic name “`fossil`†instead of the numeric TCP port number (“12345†in the above example) in `inetd.conf`. Notice that we configured `inetd` to launch Fossil as root. See the top-level section on “[The Fossil Chroot Jail](../../chroot.md)†for the consequences of this and alternatives to it. You can instead configure `inetd` to bind to a higher-numbered TCP port, allowing Fossil to be run as a normal user. In that case, Fossil will not put itself into a chroot jail, because it assumes you have set up file permissions and such on the server appropriate for that user. The `inetd` daemon must be enabled for this to work, and it must be restarted whenever its configuration file changes. This is a more complicated method than the [standalone HTTP server method](./none.md), but it has the advantage of only using system resources when an actual connection is attempted. If no one ever connects to that port, a Fossil server will not (automatically) run. It has the disadvantage of requiring "root" access, which may not be available to you, either due to local IT policy or because of restrictions at your shared Internet hosting service. For further details, see the relevant section in your system's documentation. The FreeBSD Handbook covers `inetd` in [this chapter](https://www.freebsd.org/doc/en/books/handbook/network-inetd.html). *[Return to the top-level Fossil server article.](../)* |
Added www/server/any/none.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 | # Standalone HTTP Server The easiest way to set up a Fossil server is to use either the [`server`](/help/server) or [`ui`](/help/ui) command: * **fossil server** _REPOSITORY_ * **fossil ui** _REPOSITORY_ The _REPOSITORY_ argument is either the name of the repository file or a directory containing many repositories named “`*.fossil`â€. Both of these commands start a Fossil server, usually on TCP port 8080, though a higher numbered port will be used instead if 8080 is already occupied. You can access these using URLs of the form **http://localhost:8080/**, or if _REPOSITORY_ is a directory, URLs of the form **http://localhost:8080/**_repo_**/** where _repo_ is the base name of the repository file without the “`.fossil`†suffix. There are several key differences between “`ui`†and “`server`â€: * “`ui`†always binds the server to the loopback IP address (127.0.0.1) so that it cannot serve to other machines. * Anyone who visits this URL is treated as the all-powerful Setup user, which is why the first difference exists. * “`ui`†launches a local web browser pointed at this URL. You can omit the _REPOSITORY_ argument if you run one of the above commands from within a Fossil checkout directory to serve that repository: $ fossil ui # or... $ fossil server You can abbreviate Fossil sub-commands as long as they are unambiguous. “`server`†can currently be as short as “`ser`â€. You can serve a directory containing multiple `*.fossil` files like so: $ fossil server --port 9000 --repolist /path/to/repo/dir There is an [example script](/file/tools/fslsrv) in the Fossil distribution that wraps `fossil server` to produce more complicated effects. Feel free to take it, study it, and modify it to suit your local needs. See the [online documentation](/help/server) for more information on the options and arguments you can give to these commands. *[Return to the top-level Fossil server article.](../)* |
Added www/server/any/scgi.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 | # Serving via SCGI There is an alternative to running Fossil as a [standalone HTTP server](./none.md), which is to run it in SimpleCGI (a.k.a. SCGI) mode, which uses the same [`fossil server`](/help/server) command as for HTTP service. Simply add the `--scgi` command-line option and the stand-alone server will speak the SCGI protocol rather than raw HTTP. This can be used with a web server such as [nginx](http://nginx.org) which does not support [Fossil’s CGI mode](./cgi.md). A basic nginx configuration to support SCGI with Fossil looks like this: location /code/ { include scgi_params; scgi_param SCRIPT_NAME "/code"; scgi_pass localhost:9000; } The `scgi_params` file comes with nginx, and it simply translates nginx internal variables to `scgi_param` directives to create SCGI environment variables for the proxied program; in this case, Fossil. Our explicit `scgi_param` call to define `SCRIPT_NAME` adds one more variable to this set, which is necessary for this configuration to work properly, because our repo isn’t at the root of the URL hierarchy. Without it, when Fossil generates absolute URLs, they’ll be missing the `/code` part at the start, which will typically cause [404 errors][404]. The final directive simply tells nginx to proxy all calls to URLs under `/code` down to an SCGI program on TCP port 9000. We can temporarily set Fossil up as a server on that port like so: $ fossil server /path/to/repo.fossil --scgi --localhost --port 9000 & The `--scgi` option switches Fossil into SCGI mode from its default, which is [stand-alone HTTP server mode](./none.md). All of the other options discussed in that linked document — 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. The `--localhost` option is simply good security: we’re using nginx to expose Fossil service to the outside world, so there is no good reason to allow outsiders to contact this Fossil SCGI server directly. Giving an explicit non-default TCP port number via `--port` is a good idea to avoid conflicts with use of Fossil’s default TCP service port, 8080, which may conflict with local uses of `fossil ui` and such. We characterized the SCGI service start command above as “temporary†because running Fossil in the background like that means it won’t start back up on a reboot of the server. A simple solution to that is to add that command to `/etc/rc.local` on systems that have it. However, you might want to consider setting Fossil up as an OS service instead, so that you get the benefits of the platform’s service management framework: * [Linux (systemd)](../debian/service.md) * [Windows service](../windows/service.md) * [macOS (launchd)](../macos/service.md) * [xinetd](../any/xinetd.md) * [inetd](../any/inetd.md) We go into more detail on nginx service setup with Fossil in our [Debian/Ubuntu specific guide](../debian/nginx.md), which also gets you TLS service. Similarly, our [OpenBSD specific guide](../openbsd/fastcgi.md) details how to setup a Fossil server using httpd and FastCGI on OpenBSD. *[Return to the top-level Fossil server article.](../)* [404]: https://en.wikipedia.org/wiki/HTTP_404 |
Added www/server/any/stunnel.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 | # Serving via stunnel [`stunnel`](https://www.stunnel.org/) is a TLS/SSL proxy for programs that themselves serve only via HTTP, such as Fossil. (Fossil *can* speak HTTPS, but only as a client.) `stunnel` decodes the HTTPS data from the outside world as HTTP before passing it to Fossil, and it encodes the HTTP replies from Fossil as HTTPS before sending them to the remote host that made the request. You can run `stunnel` in one of two modes: socket listener — much like in our [`inetd` doc](./inetd.md) — and as an HTTP reverse proxy. We’ll cover both cases here, separately. ## <a id="sa"></a>Socket Activation The following `stunnel.conf` configuration configures it to run Fossil in socket listener mode, launching Fossil only when an HTTPS hit comes in, then shutting it back down as soon as the transaction is complete: ```dosini [fossil] accept = 443 TIMEOUTclose = 0 exec = /usr/bin/fossil execargs = /usr/bin/fossil http /home/fossil/ubercool.fossil --https cert = /etc/letsencrypt/live/ubercool-project.org/fullchain.pem key = /etc/letsencrypt/live/ubercool-project.org/privkey.pem ciphers = ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES128-SHA:DES-CBC3-SHA options = CIPHER_SERVER_PREFERENCE ``` This configuration shows the TLS certificate generated by the [Let’s Encrypt](https://letsencrypt.org) [Certbot](https://certbot.eff.org) in [certonly mode](https://certbot.eff.org/lets-encrypt/debianbuster-other). There are other ways to get TLS certificates, but this is a popular and free option. You will need to adjust the site names and paths in this example. Where this file goes varies by OS type, so check the man pages on your system to find out where it should be locally. See the `stunnel` documentation for further details about this configuration file. It is important that the [`fossil http`](/help/http) command in that configuration include the `--https` option to let Fossil know to use “`https://`†instead of “`http://`†in generated hyperlinks. ## <a id="proxy"></a>Reverse Proxy You can instead have Fossil running in the background in [standalone HTTP server mode](./none.md), bound to a high random TCP port number on localhost via the `--localhost` and `--port` flags, then configure `stunnel` to reverse proxy public HTTPS connections down to it via HTTP. The configuration is the same as the above except that you drop the `exec` and `execargs` directives and add this instead: ```dosini connect = 9000 ``` That tells `stunnel` to connect to an already-running process listening on the given TCP port number. There are a few advantages to this mode: 1. At the cost of some server memory and a tiny bit of idle CPU time, Fossil remains running so that hits can be served a smidge faster than in socket listener mode, where the Fossil binary has to be loaded and re-initialized on each HTTPS hit. 2. The socket listener mode doesn’t work on all platforms that `stunnel` runs on, particularly [on Windows](../windows/stunnel.md). *[Return to the top-level Fossil server article.](../)* <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
Added www/server/any/xinetd.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 | # Serving via xinetd Some operating systems have replaced the old Unix `inetd` daemon with `xinetd`, which has a similar mission but with a very different configuration file format. The typical configuration file is either `/etc/xinetd.conf` or a subfile in the `/etc/xinetd.d` directory. You need a configuration something like this for Fossil: service http { port = 80 socket_type = stream wait = no user = root server = /usr/bin/fossil server_args = http /home/fossil/repos/ } This example configures Fossil to serve multiple repositories under the `/home/fossil/repos/` directory. Beyond this, see the general commentary in our article on [the `inetd` method](./inetd.md) as they also apply to service via `xinetd`. *[Return to the top-level Fossil server article.](../)* |
Added www/server/debian/index.md.
> > > > > > | 1 2 3 4 5 6 | # Debian/Ubuntu Specific Fossil Service Options - [SCGI under nginx](./nginx.md) - [`systemd`](./service.md) *[Return to the top-level Fossil server article.](../)* |
Added www/server/debian/nginx.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 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 | # Serving via nginx on Debian and Ubuntu This document is an extension of [the platform-independent SCGI instructions][scgii], which may suffice for your purposes if your needs are simple. Here, we add more detailed information on nginx itself, plus details about running it on Debian type OSes. This document was originally written for and tested on Debian 10 (Buster) and Ubuntu 20.04, which were common Tier 1 OS offerings for [virtual private servers][vps] at the time. The same configuration appears to run on Ubuntu 22.04 LTS without change. This material may not work for older OSes. It is known in particular to not work as given for Debian 9 and older! We also cover [adding TLS](#tls) to the basic configuration, because several details depend on the host OS and web stack details. Besides, TLS is widely considered part of the baseline configuration these days. [scgii]: ../any/scgi.md [vps]: https://en.wikipedia.org/wiki/Virtual_private_server ## <a id="benefits"></a>Benefits This scheme is considerably more complicated than the [standalone HTTP server](../any/none.md) and [CGI options](../any/cgi.md). Even with the benefit of this guide and pre-built binary packages, it requires quite a bit of work to set it up. Why should you put up with this complexity? Because it gives many benefits that are difficult or impossible to get with the less complicated options: * **Power** — nginx is one of the most powerful web servers in the world. The chance that you will run into a web serving wall that you can’t scale with nginx is very low. To give you some idea of the sort of thing you can readily accomplish with nginx, your author runs a single public web server that provides transparent name-based virtual hosting for four separate domains: * <p>One is entirely static, not involving any dynamic content or Fossil integration at all.</p> * <p>Another is served almost entirely by Fossil, with a few select static content exceptions punched past Fossil, which are handled entirely via nginx.</p> * <p>The other two domains are aliases for one another — e.g. `example.com` and `example.net` — with most of the content being static. This pair of domains has several unrelated Fossil repo proxies attached to various sections of the URI hierarchy.</p> By using nginx, I was able to do all of the above with minimal repetition between the site configurations. * **Integration** — Because nginx is so popular, it integrates with many different technologies, and many other systems integrate with it in turn. This makes it great middleware, sitting between the outer web world and interior site services like Fossil. It allows Fossil to participate seamlessly as part of a larger web stack. * **Availability** — nginx is already in most operating system binary package repositories, so you don’t need to go out of your way to get it. ## <a id="modes"></a>Fossil Service Modes Fossil provides four major ways to access a repository it’s serving remotely, three of which are straightforward to use with nginx: * **HTTP** — Fossil has [a built-in HTTP server](../any/none.md). While this method is efficient and it’s possible to use nginx to proxy access to another HTTP server, we don’t see any particularly good reason to make nginx reinterpret Fossil’s own implementation of HTTP when we have a better option. (But see [below](#http).) * **SSH** — This method exists primarily to avoid the need for HTTPS, but we *want* HTTPS. (We’ll get to that [below](#tls).) There is probably a way to get nginx to proxy Fossil to HTTPS via SSH, but it would be pointlessly complicated. * **CGI** — This method is simple but inefficient, because it launches a separate Fossil instance on every HTTP hit. Since Fossil is a relatively small self-contained program, and it’s designed to start up quickly, this method can work well in a surprisingly large number of cases. Nevertheless, we will avoid this option in this document because we’re already buying into a certain amount of complexity here in order to gain power. There’s no sense in throwing away any of that hard-won performance on CGI overhead. * **SCGI** — The [SCGI protocol][scgip] provides the simplicity of CGI without its performance problems. SCGI it is, then. [scgip]: https://en.wikipedia.org/wiki/Simple_Common_Gateway_Interface ## <a id="deps"></a>Installing the Dependencies The first step is to install some non-default packages we’ll need. SSH into your server, then say: $ sudo apt install fossil nginx You can leave “`fossil`†out of that if you’re building Fossil from source to get a more up-to-date version than is shipped with the host OS. ## <a id="scgi"></a>Running Fossil in SCGI Mode For the following nginx configuration to work, it needs to contact a background Fossil instance speaking the SCGI protocol. There are [many ways](../) to set that up, such as [with `systemd`](./service.md) on mainstream Linux distros. Another way is to [containerize][ctz] your repository servers, then use the [`fslsrv` wrapper for Podman][fspm] to generate `systemd` units for use by the front-end proxy. However you do it, you need to match up the TCP port numbers between it and those in the nginx configuration below. [ctz]: ../../containers.md [fspm]: https://tangentsoft.com/fossil/dir/bin ## <a id="config"></a>Configuration On Debian and Ubuntu systems the primary user-level configuration file for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this file contain only a list of include statements, one for each site that server hosts: include local/example.com include local/foo.net Those files then each define one domain’s configuration. Here, `/etc/nginx/local/example.com` contains the configuration for `*.example.com` and its alias `*.example.net`; and `local/foo.net` contains the configuration for `*.foo.net`. The configuration for our `example.com` web site, stored in `/etc/nginx/sites-enabled/local/example.com` is: ---- server { server_name .example.com .example.net ""; include local/generic; include local/code; access_log /var/log/nginx/example.com-https-access.log; error_log /var/log/nginx/example.com-https-error.log; # Bypass Fossil for the static documentation generated from # our source code by Doxygen, so it merges into the embedded # doc URL hierarchy at Fossil’s $ROOT/doc without requiring that # these generated files actually be stored in the repo. This # also lets us set aggressive caching on these docs, since # they rarely change. location /code/doc/html { root /var/www/example.com/code/doc/html; location ~* \.(html|ico|css|js|gif|jpg|png)$ { add_header Vary Accept-Encoding; access_log off; expires 7d; } } # Redirect everything under /code to the Fossil instance location /code { include local/code; # Extended caching for URLs that include unique IDs location ~ "/(artifact|doc|file|raw)/[0-9a-f]{40,64}" { add_header Cache-Control "public, max-age=31536000, immutable"; include local/code; access_log off; } # Lesser caching for URLs likely to be quasi-static location ~* \.(css|gif|ico|js|jpg|png)$ { add_header Vary Accept-Encoding; include local/code; access_log off; expires 7d; } } } ---- As you can see, this is a pure extension of [the basic nginx service configuration for SCGI][scgii], showing off a few ideas you might want to try on your own site, such as static asset proxying. You also need a `local/code` file containing: include scgi_params; scgi_pass 127.0.0.1:12345; scgi_param SCRIPT_NAME "/code"; We separate that out because nginx refuses to inherit certain settings between nested location blocks, so rather than repeat them, we extract them to this separate file and include it from both locations where it’s needed. You see this above where we set far-future expiration dates on files served by Fossil via URLs that contain hashes that change when the content changes. It tells your browser that the content of these URLs can never change without the URL itself changing, which makes your Fossil-based site considerably faster. Similarly, the `local/generic` file referenced above helps us reduce unnecessary repetition among the multiple sites this configuration hosts: root /var/www/$host; listen 80; listen [::]:80; charset utf-8; There are some configuration directives that nginx refuses to substitute variables into, citing performance considerations, so there is a limit to how much repetition you can squeeze out this way. One such example are the `access_log` and `error_log` directives, which follow an obvious pattern from one host to the next. Sadly, you must tolerate some repetition across `server { }` blocks when setting up multiple domains on a single server. The configuration for `foo.net` is similar. See [the nginx docs](https://nginx.org/en/docs/) for more ideas. ## <a id="http"></a>Proxying HTTP Anyway [Above](#modes), we argued that proxying SCGI is a better option than making nginx reinterpret Fossil’s own implementation of HTTP. If you want Fossil to speak HTTP, just [set Fossil up as a standalone server](../any/none.md). And if you want nginx to [provide TLS encryption for Fossil](#tls), proxying HTTP instead of SCGI provides no benefit. However, it is still worth showing the proper method of proxying Fossil’s HTTP server through nginx if only to make reading nginx documentation on other sites easier: location /code { rewrite ^/code(/.*) $1 break; proxy_pass http://127.0.0.1:12345; } The most common thing people get wrong when hand-rolling a configuration like this is to get the slashes wrong. Fossil is sensitive to this. For instance, Fossil will not collapse double slashes down to a single slash, as some other HTTP servers will. ## <a id="large-uv"></a> Allowing Large Unversioned Files By default, nginx only accepts HTTP messages [up to a meg](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size) in size. Fossil chunks its sync protocol such that this is not normally a problem, but when sending [unversioned content][uv], it uses a single message for the entire file. Therefore, if you will be storing files larger than this limit as unversioned content, you need to raise the limit. Within the `location` block: # Allow large unversioned file uploads, such as PDFs client_max_body_size 20M; [uv]: ../../unvers.wiki ## <a id="fail2ban"></a> Integrating `fail2ban` One of the nice things that falls out of proxying Fossil behind nginx is that it makes it easier to configure `fail2ban` to recognize attacks on Fossil and automatically block them. Fossil logs the sorts of errors we want to detect, but it does so in places like the repository’s admin log, a SQL table, which `fail2ban` doesn’t know how to query. By putting Fossil behind an nginx proxy, we convert these failures to log file form, which `fail2ban` is designed to handle. First, install `fail2ban`, if you haven’t already: sudo apt install fail2ban We’d like `fail2ban` to react to Fossil `/login` failures. The stock configuration of `fail2ban` only detects a few common sorts of SSH attacks by default, and its included (but disabled) nginx attack detectors don’t include one that knows how to detect an attack on Fossil. We have to teach it by putting the following into `/etc/fail2ban/filter.d/nginx-fossil-login.conf`: [Definition] failregex = ^<HOST> - .*POST .*/login HTTP/..." 401 That teaches `fail2ban` how to recognize the errors logged by Fossil. Then in `/etc/fail2ban/jail.local`, add this section: [nginx-fossil-login] enabled = true logpath = /var/log/nginx/*-https-access.log The last line is the key: it tells `fail2ban` where we’ve put all of our per-repo access logs in the nginx config above. There’s a [lot more you can do][dof2b], but that gets us out of scope of this guide. [dof2b]: https://www.digitalocean.com/community/tutorials/how-to-protect-an-nginx-server-with-fail2ban-on-ubuntu-14-04 ## <a id="tls"></a> Adding TLS (HTTPS) Support One of the [many ways](../../ssl.wiki) to provide TLS-encrypted HTTP access (a.k.a. HTTPS) to Fossil is to run it behind a web proxy that supports TLS. Because one such option is nginx, it’s best to delegate TLS to it if you were already using nginx for some other reason, such as static content serving, with only part of the site being served by Fossil. The simplest way by far to do this is to use [Let’s Encrypt][LE]’s [Certbot][CB], which can configure nginx for you and keep its certificates up to date. You need but follow their [nginx on Ubuntu 20 guide][CBU]. We had trouble with this in the past, but either Certbot has gotten smarter or our nginx configurations have gotten simpler, so we have removed the manual instructions we used to have here. You may wish to include something like this from each `server { }` block in your configuration to enable TLS in a common, secure way: ``` # Tell nginx to accept TLS-encrypted HTTPS on the standard TCP port. listen 443 ssl; listen [::]:443 ssl; # Reference the TLS cert files produced by Certbot. ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # Load the Let's Encrypt Diffie-Hellman parameters generated for # this server. Without this, the server is vulnerable to Logjam. ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # Tighten things down further, per Qualys’ and Certbot’s advice. ssl_session_cache shared:le_nginx_SSL:1m; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_session_timeout 1440m; # Offer OCSP certificate stapling. ssl_stapling on; ssl_stapling_verify on; # Enable HSTS. include local/enable-hsts; ``` The [HSTS] step is optional and should be applied only after due consideration, since it has the potential to lock users out of your site if you later change your mind on the TLS configuration. The `local/enable-hsts` file it references is simply: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; It’s a separate file because nginx requires that headers like this be applied separately for each `location { }` block. We’ve therefore factored this out so you can `include` it everywhere you need it. The [OCSP] step is optional, but recommended. You may find [Qualys’ SSL Server Test][QSLT] helpful in verifying that you have set all this up correctly, and that the configuration is strong. We’ve found their [best practices doc][QSLC] to be helpful. As of this writing, the above configuration yields an A+ rating when run on Ubuntu 22.04.01 LTS. [CB]: https://certbot.eff.org/ [CBU]: https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal [LE]: https://letsencrypt.org/ [HSTS]: https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ [OCSP]: https://en.wikipedia.org/wiki/OCSP_stapling [QSLC]: https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices [QSLT]: https://www.ssllabs.com/ssltest/ <div style="height:50em" id="this-space-intentionally-left-blank"></div> *[Return to the top-level Fossil server article.](../)* |
Added www/server/debian/service.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 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | # Serving via systemd on Debian and Ubuntu [`systemd`][sdhome] is the service management framework in all major in-support versions of Linux. There are multiple ways to run Fossil under `systemd`. [sdhome]: https://www.freedesktop.org/wiki/Software/systemd/ [wpa]: https://en.wikipedia.org/wiki/Systemd#Adoption ## Containerized Service Two of the methods for running [containerized Fossil][cntdoc] integrate with `systemd`, potentially obviating the more direct methods below: * If you take [the Podman method][podman] of running containerized Fossil, it opens the `podman generate systemd` option for you, as exemplified in [the `fslsrv` script][fslsrv] used on this author’s public Fossil-based web site. That script pulls its container images from [my Docker Hub repo][dhrepo] to avoid the need for my public Fossil server to have build tools and a copy of the Fossil source tree. You’re welcome to use my images as-is, or you may use these tools to bounce custom builds up through a separate container image repo you manage. * If you’re willing to give up [a lot of features][nsweak] relative to Podman, and you’re willing to tolerate a lot more manual administrivia, [the nspawn method][nspawn] has a lot less overhead, being a direct feature of `systemd` itself. Both of these options provide [better security][cntsec] than running Fossil directly under `systemd`, among [other benefits][cntdoc]. [cntdoc]: ../../containers.md [cntsec]: ../../containers.md#security [dhrepo]: https://hub.docker.com/r/tangentsoft/fossil [fslsrv]: https://tangentsoft.com/fossil/dir?name=bin [nspawn]: ../../containers.md#nspawn [nsweak]: ../../containers.md#nspawn-weaknesses [podman]: ../../containers.md#podman ## User Service A fun thing you can easily do with `systemd` that you can’t directly do with older technologies like `inetd` and `xinetd` is to set a server up as a “user†service. You can’t listen on TCP port 80 with this method due to security restrictions on TCP ports in every OS where `systemd` runs, but you can create a listener socket on a high-numbered (≥ 1024) TCP port, suitable for sharing a Fossil repo to a workgroup on a private LAN. To do this, write the following in `~/.local/share/systemd/user/fossil.service`: ```dosini [Unit] Description=Fossil user server After=network-online.target [Service] WorkingDirectory=/home/fossil/museum ExecStart=/home/fossil/bin/fossil server --port 9000 repo.fossil Restart=always RestartSec=3 [Install] WantedBy=multi-user.target ``` Unlike with `inetd` and `xinetd`, we don’t need to tell `systemd` which user and group to run this service as, because we’ve installed it under the account we’re logged into, which `systemd` will use as the service’s owner. The result is essentially [the standalone server method](../any/none.md) coupled with an intelligent service manager that will start it automatically in the background on system boot, perform automatic service restarts with back-off logic, and more, making this much more robust than the by-hand launches of `fossil` in the platform-independent Fossil server instructions. The service will stay up until we explicitly tell it to shut down. This scheme couples well with [the generic SCGI instructions][scgi] as it requires a way to run the underlying repository server in the background. Given that its service port is then proxied by SCGI, it follows that it doesn’t need to run as a system service. A user service works perfectly well for this. Because we’ve set this up as a user service, the commands you give to manipulate the service vary somewhat from the sort you’re more likely to find online: $ systemctl --user daemon-reload $ systemctl --user enable fossil $ systemctl --user start fossil $ systemctl --user status fossil -l $ systemctl --user stop fossil That is, we don’t need to talk to `systemd` with `sudo` privileges, but we do need to tell it to look at the user configuration rather than the system-level configuration. This scheme isolates the permissions needed by the Fossil server, which reduces the amount of damage it can do if there is ever a remotely-triggerable security flaw found in Fossil. On some `systemd` based OSes, user services only run while that user is logged in interactively. This is common on systems aiming to provide desktop environments, where this is the behavior you often want. To allow background services to continue to run after logout, say: $ sudo loginctl enable-linger $USER You can paste the command just like that into your terminal, since `$USER` will expand to your login name. [scgi]: ../any/scgi.md ### System Service Alternative There are some common reasons that you’d have good cause to install Fossil as a system-level service rather than the prior user-level one: * You’re using [the new `fossil server --cert` feature][sslsrv] to get TLS service and want it to listen directly on port 443, rather than be proxied, as one had to do before Fossil got the ability to act as a TLS server itself. That requires root privileges, so you can’t run it as a user-level service. * You’re proxying Fossil with [nginx](./nginx.md) or similar, allowing it to bind to high-numbered ports, but because it starts as a system service, you can’t get Fossil into the same dependency chain to ensure things start up and shut down in the proper order unless it *also* runs as a system service. * You want to make use of Fossil’s [chroot jail feature][cjail], which requires the server to start as root. There are just a small set of changes required: 1. Install the unit file to one of the persistent system-level unit file directories. Typically, these are: /etc/systemd/system /lib/systemd/system 2. Add `User` and `Group` directives to the `[Service]` section so Fossil runs as a normal user, preferably one with access only to the Fossil repo files, rather than running as `root`. [sslsrv]: ../../ssl-server.md [cjail]: ../../chroot.md ## Socket Activation Another useful method to serve a Fossil repo via `systemd` is via a socket listener, which `systemd` calls “[socket activation][sa],†roughly equivalent to [the ancient `inetd` method](../any/inetd.md). It’s more complicated, but it has some nice properties. We first need to define the privileged socket listener by writing `/etc/systemd/system/fossil.socket`: ```dosini [Unit] Description=Fossil socket [Socket] Accept=yes ListenStream=80 NoDelay=true [Install] WantedBy=sockets.target ``` Note the change of configuration directory from the `~/.local` directory to the system level. We need to start this socket listener at the root level because of the low-numbered TCP port restriction we brought up above. This configuration says more or less the same thing as the socket part of an `inted` entry [exemplified elsewhere in this documentation](../any/inetd.md). Next, create the service definition file in that same directory as `fossil@.service`: ```dosini [Unit] Description=Fossil socket server After=network-online.target [Service] WorkingDirectory=/home/fossil/museum ExecStart=/home/fossil/bin/fossil http repo.fossil StandardInput=socket [Install] WantedBy=multi-user.target ``` Notice that we haven’t told `systemd` which user and group to run Fossil under. Since this is a system-level service definition, that means it will run as root, which then causes Fossil to [automatically drop into a `chroot(2)` jail](../../chroot.md) rooted at the `WorkingDirectory` we’ve configured above, shortly after each `fossil http` call starts. The `Restart*` directives we had in the user service configuration above are unnecessary for this method, since Fossil isn’t supposed to remain running under it. Each HTTP hit starts one Fossil instance, which handles that single client’s request and then immediately shuts down. Next, you need to tell `systemd` to reload its system-level configuration files and enable the listening socket: $ sudo systemctl daemon-reload $ sudo systemctl enable fossil.socket And now you can manipulate the socket listener: $ sudo systemctl start fossil.socket $ sudo systemctl status -l fossil.socket $ sudo systemctl stop fossil.socket Notice that we’re working with the *socket*, not the *service*. The fact that we’ve given them the same base name and marked the service as an instantiated service with the “`@`†notation allows `systemd` to automatically start an instance of the service each time a hit comes in on the socket that `systemd` is monitoring on Fossil’s behalf. To see this service instantiation at work, visit a long-running Fossil page (e.g. `/tarball`) and then give a command like this: $ sudo systemctl --full | grep fossil This will show information about the `fossil` socket and service instances, which should show your `/tarball` hit handler, if it’s still running: fossil@20-127.0.0.1:80-127.0.0.1:38304.service You can feed that service instance description to a `systemctl kill` command to stop that single instance without restarting the whole `fossil` service, for example. In all of this, realize that we’re able to manipulate a single socket listener or single service instance at a time, rather than reload the whole externally-facing network configuration as with the far more primitive `inetd` service. [sa]: http://0pointer.de/blog/projects/socket-activation.html *[Return to the top-level Fossil server article.](../)* |
Added www/server/index.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 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 | <div class='fossil-doc' data-title="How To Configure A Fossil Server"> <style type="text/css"> .doc > .content th.fep { font-family: "Helvetica Neue", "Arial Narrow", "Myriad Pro", "Avenir Next Condensed"; font-stretch: condensed; min-width: 3em; padding: 0.4em; white-space: nowrap; } .doc > .content th.host { font-family: "Helvetica Neue", "Arial Narrow", "Myriad Pro", "Avenir Next Condensed"; font-stretch: condensed; padding: 0.4em; text-align: right; } .doc > .content td.doc { text-align: center; } </style> <h2>No Server Required</h2> <p>Fossil does not require a central server, but <a href="whyuseaserver.wiki">a server can be useful</a>.</p> <p>A Fossil server does not require much memory, CPU, or disk space and can run comfortably on a generic $5/month virtual host or on a small device like a Raspberry Pi, or it can co-exist on a host running other services without getting in the way. <p>This article is a quick-reference guide for setting up your own Fossil server, with links to more detailed instructions specific to particular systems, should you want extra help.</p> <h2 id="prep">Repository Prep</h2> <p>Prior to serving a Fossil repository to others, consider running <a href="$ROOT/help?cmd=ui"><tt>fossil ui</tt></a> locally and taking these minimum recommended preparation steps:</p> <ol> <li><p>Fossil creates only one user in a <a href="$ROOT/help?cmd=new">new repository</a> and gives it the <a href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>. The 10-digit random password generated for that user is fairly strong against remote attack, even without explicit password guess rate limiting, but because that user has so much power, you may want to give it a much stronger password under Admin → Users.</a></li> <li><p>Run the Admin → Security-Audit tool to verify that other security-related permissions and settings are as you want them. Consider clicking the “Take it private†link on that page to lock down the security on that site to a level appropriate to a private repository, even if you will eventually want some public service. It's better to start from a secure position and open up service feature-by-feature as necessary than it is to start from a fully open position and lock down features one by one to achieve a secure stance.</p></li> </ol> <p>With the repository secured, it is safe to upload a copy of the repository file to your server and proceed with server setup, below. Further configuration steps can wait until <a href="#postsetup">after the server is running</a>.</p> <h2 id="methods">Activation Methods</h2> <p>There are basically five ways to run a Fossil server:</p> <ol> <li><a href="any/cgi.md">CGI</a> <li><a href="#slist">Socket listener</a> <li><a href="any/none.md">Stand-alone HTTP server</a> <li><a href="any/scgi.md">SCGI</a> <li><a href="#ssh">SSH</a> </ol> <p>All of these methods can serve either a single repository or a directory hierarchy containing multiple repositories.</p> <p>You are not restricted to a single server setup. The same Fossil repository can be served using two or more of the above techniques at the same time. These methods use clean, well-defined, standard interfaces (CGI, SCGI, and HTTP) which allow you to easily migrate from one method to another in response to changes in hosting providers or administrator preferences.</p> <h3 id="cgi">CGI</h3> <p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a CGI script</a>. This method is known to work with Apache, <tt>lighttpd</tt>, and <a href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server administrator places a <a href="$ROOT/help?cmd=cgi">short CGI script</a> in the web server's document hierarchy and when a client requests the URL that corresponds to that script, Fossil runs and generates the response.</p> <p>CGI is a good choice for merging Fossil into an existing web site, particularly on hosts that have CGI set up and working. The Fossil <a href="../selfhost.wiki">self-hosting repositories</a> are implemented with CGI underneath <tt>althttpd</tt>.</p> <h3 id="slist">Socket Listener</h3> <p>Socket listener daemons such as <a id="inetd" href="any/inetd.md"><tt>inetd</tt></a>, <a id="xinetd" href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel" href="any/stunnel.md"><tt>stunnel</tt></a>, <a href="macos/service.md"><tt>launchd</tt></a>, and <a href="debian/service.md"><tt>systemd</tt></a> can be configured to invoke the the <a href="$ROOT/help?cmd=http"><tt>fossil http</tt></a> command to handle each incoming HTTP request. The "<tt>fossil http</tt>" command reads the HTTP request off of standard input, computes an appropriate reply, and writes the reply on standard output. There is a separate invocation of the "<tt>fossil http</tt>" command for each HTTP request. The socket listener daemon takes care of relaying content to and from the client, and (in the case of <a href="any/stunnel.md">stunnel</a>) handling TLS decryption and encryption. <h3 id="standalone">Stand-alone HTTP Server</h3> <p>This is the <a href="any/none.md">easiest method</a>. A stand-alone server uses the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command to run a process that listens for incoming HTTP requests on a socket and then dispatches a copy of itself to deal with each incoming request. You can expose Fossil directly to the clients in this way or you can interpose a <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a> layer between the clients and Fossil.</p> <h3 id="scgi">SCGI</h3> <p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>. When the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command is run with the extra <tt>--scgi</tt> option, it listens for incoming SCGI requests rather than HTTP requests. This allows Fossil to respond to requests from web servers <a href="debian/nginx.md">such as nginx</a> that don't support CGI. SCGI is a simpler protocol to proxy than HTTP, since the HTTP doesn't have to be re-interpreted in terms of the proxy's existing HTTP implementation, but it's more complex to set up because you also have to set up an SCGI-to-HTTP proxy for it. It is worth taking on this difficulty only when you need to integrate Fossil into an existing web site already being served by an SCGI-capable web server.</p> <h3 id="ssh">SSH</h3> <p>Fossil supports <tt>ssh://</tt> URLs, but there are <a href="../caps/#webonly">practical limitations</a> with the default behavior. You can get the full power of <a href="../caps/">Fossil's RBAC system</a> over SSH <a href="any/http-over-ssh.md">with a bit of clever configuration</a>.</p> <h2 id="matrix">Activation Tutorials</h2> <p>We've broken the configuration for each method out into a series of sub-articles. Some of these are generic, while others depend on particular operating systems or front-end software:</p> <div class="indent"><table> <tr> <th class="host">⇩ OS / Method ⇨</th> <th class="fep">direct</th> <th class="fep">inetd</th> <th class="fep">stunnel</th> <th class="fep">CGI</th> <th class="fep">SCGI</th> <th class="fep">FastCGI</th> <th class="fep">althttpd</th> <th class="fep">proxy</th> <th class="fep">service</th> </tr> <tr> <th class="host"><a href="any/">Any</a></th> <td class="doc"><a href="any/none.md">✅</a></td> <td class="doc"><a href="any/inetd.md">✅</a></td> <td class="doc"><a href="any/stunnel.md">✅</a></td> <td class="doc"><a href="any/cgi.md">✅</a></td> <td class="doc"><a href="any/scgi.md">✅</a></td> <td class="doc">âŒ</td> <td class="doc"><a href="any/althttpd.md">✅</a></td> <td class="doc">âŒ</td> <td class="doc">âŒ</td> </tr> <tr> <th class="host"><a href="debian/">Debian/Ubuntu</a></th> <td class="doc"><a href="any/none.md">✅</a></td> <td class="doc"><a href="any/inetd.md">✅</a></td> <td class="doc"><a href="any/stunnel.md">✅</a></td> <td class="doc"><a href="any/cgi.md">✅</a></td> <td class="doc"><a href="any/scgi.md">✅</a></td> <td class="doc">âŒ</td> <td class="doc"><a href="any/althttpd.md">✅</a></td> <td class="doc"><a href="debian/nginx.md">✅</a></td> <td class="doc"><a href="debian/service.md">✅</a></td> </tr> <tr> <th class="host"><a href="macos/">macOS</a></th> <td class="doc"><a href="any/none.md">✅</a></td> <td class="doc">âŒ</td> <td class="doc"><a href="any/stunnel.md">✅</a></td> <td class="doc"><a href="any/cgi.md">✅</a></td> <td class="doc"><a href="any/scgi.md">✅</a></td> <td class="doc">âŒ</td> <td class="doc"><a href="any/althttpd.md">✅</a></td> <td class="doc">âŒ</td> <td class="doc"><a href="macos/service.md">✅</a></td> </tr> <tr> <th class="host"><a href="openbsd/">OpenBSD</a></th> <td class="doc"><a href="any/none.md">✅</a></td> <td class="doc">âŒ</td> <td class="doc"><a href="any/stunnel.md">✅</a></td> <td class="doc"><a href="any/cgi.md">✅</a></td> <td class="doc"><a href="any/scgi.md">✅</a></td> <td class="doc"><a href="openbsd/fastcgi.md">✅</a></td> <td class="doc"><a href="any/althttpd.md">✅</a></td> <td class="doc">âŒ</td> <td class="doc"><a href="openbsd/service.wiki">✅</a></td> </tr> <tr> <th class="host"><a href="windows/">Windows</a></th> <td class="doc"><a href="windows/none.md">✅</a></td> <td class="doc">âŒ</td> <td class="doc"><a href="windows/stunnel.md">✅</a></td> <td class="doc"><a href="windows/cgi.md">✅</a></td> <td class="doc">âŒ</td> <td class="doc">âŒ</td> <td class="doc">âŒ</td> <td class="doc"><a href="windows/iis.md">✅</a></td> <td class="doc"><a href="windows/service.md">✅</a></td> </tr> </table></div> <p>Where there is a check mark in the "<b>Any</b>" row, the method for that is generic enough that it works across OSes that Fossil is known to work on. The check marks below that usually just link to this generic documentation.</p> <p>The method in the "<b>proxy</b>" column is for the platform's default web server configured as a <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a> for Fossil's built-in HTTP server: <a href="debian/nginx.md">nginx</a>, <a href="windows/iis.md">IIS</a>, Apache, etc.</p> <p>We welcome <a href="../contribute.wiki">contributions</a> to fill gaps (<font size="-2">âŒ</font>) in the table above.</p> </noscript> <h2 id="postsetup">Post-Activation Configuration</h2> <p>After the server is up and running, log into it as the Setup user and visit the Admin menu to finish configuring that repository for service:</p> <ol> <li><p>Add user accounts for your other team members. Use <a href="../caps/index.md#ucat">categories</a> to define access policies rather than redundantly give each new user the same <a href="../caps/index.md#ucap">individual capabilities</a>.</p></li> <li><p>Test access to the repository from each category of non-Setup user that you created. You may have to give your user categories some overlooked capabilities, particularly if you followed <a href="#prep">our earlier advice</a> to take the repository private prior to setting up the server.</p></li> <li><p>Modify the repository's look and feel by <a href="../customskin.md">customizing the skin</a>.</p></li> <li><p>If the repository includes <a href="../embeddeddoc.wiki">embedded documentation</a>, consider activating the search feature (Admin → Search) so that visitors can do full-text search on your documentation.</p></li> <li><p>Now that others can be making changes to the repository, consider monitoring them via <a href="../alerts.md">email alerts</a> or the <a href="$ROOT/help?cmd=/timeline.rss">timeline RSS feed</a>.</p></li> <li><p>Turn on the various logging features.</p></li> </ol> <p>Reload the Admin → Security-Audit page occasionally during this process to double check that you have not mistakenly configured the server in a way that might expose information that you want to keep private.</p> <h2 id="more">Further Details</h2> <ul> <li><a id="chroot" href="../chroot.md" >The Server Chroot Jail</a> <li><a id="loadmgmt" href="../loadmgmt.md" >Managing Server Load</a> <li><a id="bkofc" href="../backoffice.md" >The Backoffice</a> <li><a id="tls" href="../ssl.wiki" >Securing a Repository with TLS</a> <li><a id="ext" href="../serverext.wiki">CGI Server Extensions</a> <li><a id="about" href="../aboutcgi.wiki" >How CGI Works In Fossil</a> <li><a id="sync" href="../sync.wiki" >The Fossil Sync Protocol</a> </ul> </div> |
Added www/server/macos/index.md.
> > > > > | 1 2 3 4 5 | # macOS Specific Fossil Service Options - [Running Fossil under `launchd`](./service.md) *[Return to the top-level Fossil server article.](../)* |
Added www/server/macos/service.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 | # Serving via launchd on macOS [`launchd`][ldhome] is the default service management framework on macOS [since the release of Tiger in 2005][wpa]. If you want a Fossil server to launch in the background on a Mac, it’s the way Apple wants you to do it. `launchd` is to macOS as `systemd` is to most modern Linux desktop systems. (Indeed, `systemd` arguably reinvented the perfectly good, pre-existing `launchd` wheel.) Unlike in [our `systemd` article](../debian/service.md), we’re not going to show the per-user method here, because those so-called [LaunchAgents][la] only start when a user is logged into the GUI, and they stop when that user logs out. This does not strike us as proper “server†behavior, so we’ll stick to system-level LaunchDaemons instead. However, we will still give two different configurations, just as in the `systemd` article: one for a standalone HTTP server, and one using socket activation. For more information on `launchd`, the single best resource we’ve found is [launchd.info](https://launchd.info). The next best is: $ man launchd.plist [la]: http://www.grivet-tools.com/blog/2014/launchdaemons-vs-launchagents/ [ldhome]: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html [wpa]: https://en.wikipedia.org/wiki/Launchd ## Standalone HTTP Server To configure `launchd` to start Fossil as a standalone HTTP server, write the following as `com.example.dev.FossilHTTP.plist`: ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.example.dev.FossilHTTP</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/fossil</string> <string>server</string> <string>--port</string> <string>9000</string> <string>repo.fossil</string> </array> <key>WorkingDirectory</key> <string>/Users/you/museum</string> <key>KeepAlive</key> <true/> <key>RunAtLoad</key> <true/> <key>StandardErrorPath</key> <string>/tmp/fossil-error.log</string> <key>StandardOutPath</key> <string>/tmp/fossil-info.log</string> <key>UserName</key> <string>you</string> <key>GroupName</key> <string>staff</string> <key>InitGroups</key> <true/> </dict> </plist> ``` In this example, we’re assuming your development organization uses the domain name “`dev.example.org`â€, that your short macOS login name is “`you`â€, and that you store your Fossils in “`~/museum`â€. Adjust these elements of the plist file to suit your local situation. You might be wondering about the use of `UserName`: isn’t Fossil supposed to drop privileges and enter [a `chroot(2)` jail](../../chroot.md) when it’s started as root like this? Why do we need to give it a user name? Won’t Fossil use the owner of the repository file to set that? All I can tell you is that in testing here, if you leave the user and group configuration at the tail end of that plist file out, Fossil will remain running as root! Install that file and set it to start with: $ sudo install -o root -g wheel -m 644 com.example.dev.FossilHTTP.plist \ /Library/LaunchDaemons/ $ sudo launchctl load -w /Library/LaunchDaemons/com.example.dev.FossilHTTP.plist Because we set the `RunAtLoad` key, this will also launch the daemon. Stop the daemon with: $ sudo launchctl unload -w /Library/LaunchDaemons/com.example.dev.FossilHTTP.plist ## Socket Listener Another useful method to serve a Fossil repo via `launchd` is by setting up a socket listener: ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.example.dev.FossilSocket</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/fossil</string> <string>http</string> <string>repo.fossil</string> </array> <key>Sockets</key> <dict> <key>Listeners</key> <dict> <key>SockServiceName</key> <string>9001</string> <key>SockType</key> <string>stream</string> <key>SockProtocol</key> <string>TCP</string> <key>SockFamily</key> <string>IPv4</string> </dict> </dict> <key>inetdCompatibility</key> <dict> <key>Wait</key> <false/> </dict> <key>WorkingDirectory</key> <string>/Users/you/museum</string> <key>UserName</key> <string>you</string> <key>GroupName</key> <string>staff</string> <key>InitGroups</key> <true/> </dict> </plist> ``` Save it as “`com.example.dev.FossilSocket.plist`†and install and load it into `launchd` as above. This version differs in several key ways: 1. We’re calling Fossil as `fossil http` rather than `fossil server` to make it serve a single request and then shut down immediately. 2. We’ve told `launchd` to listen on our TCP port number instead of passing it to `fossil`. 3. We’re running the daemon in `inetd` compatibility mode of `launchd` with “wait†mode off, which tells it to attach the connected socket to the `fossil` process’s stdio handles. 4. We’ve removed the `Standard*Path` keys because they interfere with our use of stdio handles for HTTP I/O. You might therefore want to start with the first method and then switch over to this one only once you’ve got the daemon launching debugged, since once you tie up stdio this way, you won’t be able to get logging information from Fossil via that path. (Fossil does have some internal logging mechanisms, but you can’t get at them until Fossil is launching!) 5. We’ve removed the `KeepAlive` and `RunAtLoad` keys because those options aren’t appropriate to this type of service. 6. Because we’re running it via a socket listener instead of as a standalone HTTP server, the Fossil service only takes system resources when it’s actually handling an HTTP hit. If your Fossil server is mostly idle, this method will be a bit more efficient than the first option. *[Return to the top-level Fossil server article.](../)* |
Added www/server/openbsd/fastcgi.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 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 | # Serving via httpd on OpenBSD [`httpd`][httpd] is the default web server that is included in the base install on OpenBSD. It's minimal and lightweight but secure and capable, and provides a clean interface for setting up a Fossil server using FastCGI. This article will detail the steps required to setup a TLS-enabled `httpd` configuration that serves multiple Fossil repositories out of a single directory within a chroot, and allow `ssh` access to create new repositories remotely. **NOTE:** The following instructions assume an OpenBSD 6.7 installation. [httpd]: https://www.openbsd.org/papers/httpd-asiabsdcon2015.pdf ## <a id="fslinstall"></a>Install Fossil Use the OpenBSD package manager `pkg_add` to install Fossil, making sure to select the statically linked binary. ```console $ doas pkg_add fossil quirks-3.325 signed on 2020-06-12T06:24:53Z Ambiguous: choose package for fossil 0: <None> 1: fossil-2.10v0 2: fossil-2.10v0-static Your choice: 2 fossil-2.10v0-static: ok ``` This installs Fossil into the chroot. To facilitate local use, create a symbolic link of the fossil executable into `/usr/local/bin`. ```console $ doas ln -s /var/www/bin/fossil /usr/local/bin/fossil ``` As a privileged user, create the file `/var/www/cgi-bin/scm` with the following contents to make the CGI script that `httpd` will execute in response to `fsl.domain.tld` requests; all paths are relative to the `/var/www` chroot. ```sh #!/bin/fossil directory: /htdocs/fsl.domain.tld notfound: https://domain.tld repolist errorlog: /logs/fossil.log ``` The `directory` directive instructs Fossil to serve all repositories found in `/var/www/htdocs/fsl.domain.tld`, while `errorlog` sets logging to be saved to `/var/www/logs/fossil.log`; create the repository directory and log file—making the latter owned by the `www` user, and the script executable. ```console $ doas mkdir /var/www/htdocs/fsl.domain.tld $ doas touch /var/www/logs/fossil.log $ doas chown www /var/www/logs/fossil.log $ doas chmod 660 /var/www/logs/fossil.log $ doas chmod 755 /var/www/cgi-bin/scm ``` ## <a id="chroot"></a>Setup chroot Fossil needs both `/dev/random` and `/dev/null`, which aren't accessible from within the chroot, so need to be constructed; `/var`, however, is mounted with the `nodev` option. Rather than removing this default setting, create a small memory filesystem and then mount it on to `/var/www/dev` with [`mount_mfs(8)`][mfs] so that the `random` and `null` device files can be created. In order to avoid necessitating a startup script to recreate the device files at boot, create a template of the needed ``/dev`` tree to automatically populate the memory filesystem. ```console $ doas mkdir /var/www/dev $ doas install -d -g daemon /template/dev $ cd /template/dev $ doas /dev/MAKEDEV urandom $ doas mknod -m 666 null c 2 2 $ doas mount_mfs -s 1M -P /template/dev /dev/sd0b /var/www/dev $ ls -l total 0 crw-rw-rw- 1 root daemon 2, 2 Jun 20 08:56 null lrwxr-xr-x 1 root daemon 7 Jun 18 06:30 random@ -> urandom crw-r--r-- 1 root wheel 45, 0 Jun 18 06:30 urandom ``` [mfs]: https://man.openbsd.org/mount_mfs.8 To make the mountable memory filesystem permanent, open `/etc/fstab` as a privileged user and add the following line to automate creation of the filesystem at startup: ```console swap /var/www/dev mfs rw,-s=1048576,-P=/template/dev 0 0 ``` The same user that executes the fossil binary must have writable access to the repository directory that resides within the chroot; on OpenBSD this is `www`. In addition, grant repository directory ownership to the user who will push to, pull from, and create repositories. ```console $ doas chown -R user:www /var/www/htdocs/fsl.domain.tld $ doas chmod 770 /var/www/htdocs/fsl.domain.tld ``` ## <a id="httpdconfig"></a>Configure httpd On OpenBSD, [httpd.conf(5)][httpd] is the configuration file for `httpd`. To setup the server to serve all Fossil repositores within the directory specified in the CGI script, and automatically redirect standard HTTP requests to HTTPS—apart from [Let's Encrypt][LE] challenges issued in response to [acme-client(1)][acme] certificate requests—create `/etc/httpd.conf` as a privileged user with the following contents. [LE]: https://letsencrypt.org [acme]: https://man.openbsd.org/acme-client.1 [httpd.conf(5)]: https://man.openbsd.org/httpd.conf.5 ```apache server "fsl.domain.tld" { listen on * port http root "/htdocs/fsl.domain.tld" location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location * { block return 301 "https://$HTTP_HOST$REQUEST_URI" } location "/*" { fastcgi { param SCRIPT_FILENAME "/cgi-bin/scm" } } } server "fsl.domain.tld" { listen on * tls port https root "/htdocs/fsl.domain.tld" tls { certificate "/etc/ssl/domain.tld.fullchain.pem" key "/etc/ssl/private/domain.tld.key" } hsts { max-age 15768000 preload subdomains } connection max request body 104857600 location "/*" { fastcgi { param SCRIPT_FILENAME "/cgi-bin/scm" } } location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } } ``` [The default limit][dlim] for HTTP messages in OpenBSD’s `httpd` server is 1 MiB. Fossil chunks its sync protocol such that this is not normally a problem, but when sending [unversioned content][uv], it uses a single message for the entire file. Therefore, if you will be storing files larger than this limit as unversioned content, you need to raise the limit as we’ve done above with the “`connection max request body`†setting, raising the limit to 100 MiB. [dlim]: https://man.openbsd.org/httpd.conf.5#connection [uv]: ../../unvers.wiki **NOTE:** If not already in possession of a HTTPS certificate, comment out the `https` server block and proceed to securing a free [Let's Encrypt Certificate](#letsencrypt); otherwise skip to [Start `httpd`](#starthttpd). ## <a id="letsencrypt"></a>Let's Encrypt Certificate In order for `httpd` to serve HTTPS, secure a free certificate from Let's Encrypt using `acme-client`. Before issuing the request, however, ensure you have a zone record for the subdomain with your registrar or nameserver. Then open `/etc/acme-client.conf` as a privileged user to configure the request. ```dosini authority letsencrypt { api url "https://acme-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-privkey.pem" } authority letsencrypt-staging { api url "https://acme-staging.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-staging-privkey.pem" } domain domain.tld { alternative names { www.domain.tld fsl.domain.tld } domain key "/etc/ssl/private/domain.tld.key" domain certificate "/etc/ssl/domain.tld.crt" domain full chain certificate "/etc/ssl/domain.tld.fullchain.pem" sign with letsencrypt } ``` Start `httpd` with the new configuration file, and issue the certificate request. ```console $ doas rcctl start httpd $ doas acme-client -vv domain.tld acme-client: /etc/acme/letsencrypt-privkey.pem: account key exists (not creating) acme-client: /etc/acme/letsencrypt-privkey.pem: loaded RSA account key acme-client: /etc/ssl/private/domain.tld.key: generated RSA domain key acme-client: https://acme-v01.api.letsencrypt.org/directory: directories acme-client: acme-v01.api.letsencrypt.org: DNS: 172.65.32.248 ... N(Q????Z???j?j?>W#????b???? H????eb??T??*? DNosz(???n{L}???D???4[?B] (1174 bytes) acme-client: /etc/ssl/domain.tld.crt: created acme-client: /etc/ssl/domain.tld.fullchain.pem: created ``` A successful result will output the public certificate, full chain of trust, and private key into the `/etc/ssl` directory as specified in `acme-client.conf`. ```console $ doas ls -lR /etc/ssl -r--r--r-- 1 root wheel 2.3K Mar 2 01:31:03 2018 domain.tld.crt -r--r--r-- 1 root wheel 3.9K Mar 2 01:31:03 2018 domain.tld.fullchain.pem /etc/ssl/private: -r-------- 1 root wheel 3.2K Mar 2 01:31:03 2018 domain.tld.key ``` Make sure to reopen `/etc/httpd.conf` to uncomment the second server block responsible for serving HTTPS requests before proceeding. ## <a id="starthttpd"></a>Start `httpd` With `httpd` configured to serve Fossil repositories out of `/var/www/htdocs/fsl.domain.tld`, and the certificates and key in place, enable and start `slowcgi`—OpenBSD's FastCGI wrapper server that will execute the above Fossil CGI script—before checking that the syntax of the `httpd.conf` configuration file is correct, and (re)starting the server (if still running from requesting a Let's Encrypt certificate). ```console $ doas rcctl enable slowcgi $ doas rcctl start slowcgi slowcgi(ok) $ doas httpd -vnf /etc/httpd.conf configuration OK $ doas rcctl start httpd httpd(ok) ``` ## <a id="clientconfig"></a>Configure Client To facilitate creating new repositories and pushing them to the server, add the following function to your `~/.cshrc` or `~/.zprofile` or the config file for whichever shell you are using on your development box. ```sh finit() { fossil init $1.fossil && \ chmod 664 $1.fossil && \ fossil open $1.fossil && \ fossil user password $USER $PASSWD && \ fossil remote-url https://$USER:$PASSWD@fsl.domain.tld/$1 && \ rsync --perms $1.fossil $USER@fsl.domain.tld:/var/www/htdocs/fsl.domain.tld/ >/dev/null && \ chmod 644 $1.fossil && \ fossil ui } ``` This enables a new repository to be made with `finit repo`, which will create the fossil repository file `repo.fossil` in the current working directory; by default, the repository user is set to the environment variable `$USER`. It then opens the repository and sets the user password to the `$PASSWD` environment variable (which you can either set with `export PASSWD 'password'` on the command line or add to a *secured* shell environment file), and the `remote-url` to `https://fsl.domain.tld/repo` with the credentials of `$USER` who is authenticated with `$PASSWD`. Finally, it `rsync`'s the file to the server before opening the local repository in your browser where you can adjust settings such as anonymous user access, and set pertinent repository details. Thereafter, you can add files with `fossil add`, and commit with `fossil ci -m 'commit message'` where Fossil, by default, will push to the `remote-url`. It's suggested you read the [Fossil documentation][documentation]; with a sane and consistent development model, the system is much more efficient and cohesive than `git`—so the learning curve is not steep at all. [documentation]: https://fossil-scm.org/home/doc/trunk/www/permutedindex.html *[Return to the top-level Fossil server article.](../)* |
Added www/server/openbsd/index.md.
> > > > > > | 1 2 3 4 5 6 | # Debian/Ubuntu Specific Fossil Service Options - [Serving Fossil under OpenBSD’s `httpd` via FastCGI](./fastcgi.md) - [Running Fossil as a service on OpenBSD](./service.wiki) *[Return to the top-level Fossil server article.](../)* |
Added www/server/openbsd/service.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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | <title>Serving via rc on OpenBSD</title> OpenBSD provides [https://man.openbsd.org/rc.subr.8|rc.subr(8)], a framework for writing [https://man.openbsd.org/rc.8|rc(8)] scripts. <h2>Creating the daemon</h2> Create the file /etc/rc.d/fossil with contents like the following. <pre> #!/bin/ksh daemon="/usr/local/bin/fossil" # fossil executable daemon_user="_fossil" # user to run fossil as daemon_flags="server /home/_fossil/example --repolist --port 8888" # fossil command . /etc/rc.d/rc.subr # pexp="$daemon server .*" # See below. rc_reload=NO # Unsupported by Fossil; 'rcctl reload fossil' kills the process. rc_bg=YES # Run in the background, since fossil serve does not daemonize itself rc_cmd $1 </pre> <h3>pexp</h3> You may need to uncomment the "pexp=". rc.subr typically finds the daemon process based by matching the process name and argument list. Without the "pexp=" line, rc.subr would look for this exact command: <pre> /usr/local/bin/fossil server /home/_fossil/example --repolist --port 8888 </pre> Depending on the arguments and their order, fossil may rewrite the arguments for display in the process listing ([https://man.openbsd.org/ps.1|ps(1)]), so rc.subr may fail to find the process through the default match. The example above does not get rewritten, but the same commands in a different order can be rewritten. For example, when I switch the order of the arguments in "daemon_flags", <pre> /usr/local/bin/fossil server --repolist --port 8888 /home/_fossil/example </pre> the process command is changed to this. <pre> /usr/local/bin/fossil server /home/_fossil/example /home/_fossil/example 8888 /home/_fossil/example </pre> The commented "pexp=" line instructs rc.subr to choose the process whose command and arguments text starts with this: <pre> /usr/local/bin/fossil server </pre> <h2>Enabling the daemon</h2> Once you have created /etc/rc.d/fossil, run these commands. <pre> rcctl enable fossil # add fossil to pkg_scripts in /etc/rc.conf.local rcctl start fossil # start the daemon now </pre> The daemon should now be running and set to start at boot. <h2>Multiple daemons</h2> You may want to serve multiple fossil instances with different options. For example, * If different users own different repositories, you may want different users to serve different repositories. * You may want to serve different repositories on different ports so you can control them differently with, for example, HTTP reverse proxies or [https://man.openbsd.org/pf.4|pf(4)]. To run multiple fossil daemons, create multiple files in /etc/rc.d, and enable each of them. Here are two approaches for creating the files in /etc/rc.d: Symbolic links and copies. <h3>Symbolic links</h3> Suppose you want to run one fossil daemon as user "user1" on port 8881 and another as user "user2" on port 8882. Create the files with [https://man.openbsd.org/ln.1|ln(1)], and configure them to run different fossil commands. <pre> cd /etc/rc.d ln -s fossil fossil1 ln -s fossil fossil2 rcctl enable fossil1 fossil2 rcctl set fossil1 user user1 rcctl set fossil2 user user2 rcctl set fossil1 flags 'server /home/user1/repo1.fossil --port 8881' rcctl set fossil2 flags 'server /home/user2/repo2.fossil --port 8882' rcctl start fossil1 fossil2 </pre> <h3>Copies</h3> You may want to run fossil daemons that are too different to configure just with [https://man.openbsd.org/rcctl.8|rcctl(8)]. In particular, you can't change the "pexp" with rcctl. If you want to run fossil commands that are more different, you may prefer to create separate files in /etc/rc.d. Replace "ln -s" above with "cp" to accomplish this. <pre> cp /etc/rc.d/fossil /etc/rc.d/fossil-user1 cp /etc/rc.d/fossil /etc/rc.d/fossil-user2 </pre> You can still use commands like "rcctl set fossil-user1 flags", but you can also edit the "/etc/rc.d/fossil-user1" file. |
Added www/server/whyuseaserver.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 | <title>Benefits of a Fossil Server</title> <h2>No Server Required</h2> Fossil does not require a central server. Data sharing and synchronization can be entirely peer-to-peer. Fossil uses [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type | conflict-free replicated data types] to ensure that (in the limit) all participating peers see the same content. <h2>But, a Server Can Be Useful</h2> Fossil does not require a server, but a server can be very useful. Here are a few reasons to set up a Fossil server for your project: 1. <b>A server works as a complete project website.</b> Fossil does more than just version control. It also supports [../tickets.wiki|trouble-tickets], [../wikitheory.wiki|wiki], a [../chat.md|developer chat room], and a [../forum.wiki|forum]. The [../embeddeddoc.wiki|embedded documentation] feature provides a great mechanism for providing project documentation. The [../unvers.wiki|unversioned files] feature is a convenient way to host builds and downloads on the project website. 2. <b>A server gives developers a common point of rendezvous for syncing their work.</b> It is possible for developers to synchronize peer-to-peer but that requires the developers coordinate the sync, which in turn requires that the developers both want to sync at the same moment. A server alleviates this time dependency by allowing each developer to sync whenever it is convenient. For example, a developer may choose to automatically sync after each commit and before each update. Developers all stay in sync with each other without having to interrupt each other constantly to set up a peer-to-peer sync. 3. <b>A server provides project leaders with up-to-date status.</b> Project coordinators and BDFLs can click on a link or two at the central Fossil server for a project and quickly tell what is going on. They can do this from anywhere — even from their phones — without needing to actually sync to the device they are using. 4. <b>A server provides automatic off-site backups.</b> A Fossil server is an automatic remote backup for all the work going into a project. ([../backup.md | Within limits].) You can even set up multiple servers at multiple sites with automatic synchronization between them for added redundancy. Such a setup means that no work is lost due to a single machine failure. 5. <b>A server consolidates [https://www.sqlite.org/howtocorrupt.html | SQLite corruption risk mitigation] to a single point.</b> The concerns in section 1 of that document assume you have direct access to the central DB files, which isn't the case when the server is remote and secure against tampering. Section 2 is about file locking, which concerns disappear when Fossil's on the other side of an HTTP boundary and your server is set up properly. Sections 3.1, 4 thru 6, and 8 apply to all Fossil configurations, but setting up a server lets you address the risks in a single place. Once a given commit is sync'd to the server, you can be reasonably sure any client-side corruption can be fixed with a fresh clone. Ultimately, this is an argument for off-machine backups, which returns us to reason #4 above. Sections 3.2 and the entirety of section 7 are no concern with Fossil at all, since it's primarily written by the creator and primary maintainer of SQLite, so you can be certain Fossil doesn't actively pursue coding strategies known to risk database corruption. For another take on this topic, see the article "[https://sqlite.org/useovernet.html | SQLite Over a Network, Caveats and Considerations]". Fossil runs in rollback mode by default per recommendation #3 at the end of that article, and a Fossil server operates as a network proxy for the underlying SQLite repository DB per recommendation #2. This <i>may</i> permit you to safely switch it into WAL mode (<b>fossil rebuild --wal</b>) depending on the underlying storage used by the server itself. 6. <b>A server allows [../caps/ | Fossil's RBAC system] to work.</b> The role-based access control (RBAC) system in Fossil only works when the remote system is on the other side of an HTTP barrier. ([../caps/#webonly | Details].) If you want its benefits, you need a Fossil server setup of some kind. |
Added www/server/windows/cgi-bin-perm.png.
cannot compute difference between binary files
Added www/server/windows/cgi-exec-perm.png.
cannot compute difference between binary files
Added www/server/windows/cgi-install-iis.png.
cannot compute difference between binary files
Added www/server/windows/cgi-script-map.png.
cannot compute difference between binary files
Added www/server/windows/cgi.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 | # Serving via IIS + CGI ## This Is Not the Method You Are Looking For Setting up CGI service under IIS is surprisingly complicated compared to running Fossil as a CGI under most other operating systems. We recommend that you use the simpler [reverse proxying method](./iis.md) instead unless there is some compelling reason why that method cannot work for you, such as its dependence on non-stock IIS extensions. (Keep in mind that both extensions it requires are by Microsoft, not third parties!) Once you’ve got this scheme working, it gives the same benefits as those listed at the top of the linked-to document. There is a small benefit you get from using CGI over reverse proxying on other OSes, which is that the Fossil program only runs briefly in order to serve each HTTP hit. Once the request is done, that Fossil instance shuts back down, releasing all of its resources. You don’t need to keep a background Fossil HTTP server running full-time to provide CGI-based Fossil service. You lose a lot of that benefit on Windows: 1. It only matters to start with on servers that are highly RAM constrained. (Roughly ≤ 128 MiB.) Our configuration steps below assume you’re using the Windows and IIS GUIs, which have RAM requirements well in excess of this, making Fossil’s resource requirements a drop in the bucket next to them. On the [Azure B1s][b1s] virtual machine I used to prepare these instructions, the Windows Server Manager GUI kept filling the VM’s 1 GiB of RAM during feature installation and crashing. I had to upgrade the VM’s RAM to 2 GiB just to get useful work done! 2. Process creation on Windows is [much more expensive][cp] than on the other OSes Fossil runs on, so the benefits of firing up a Fossil executable to process each HTTP request are partially swamped by the overhead of doing so. Therefore, unless you’re willing to replace all of the GUI configuration steps below with command line equivalents, or shut the GUI down entirely after configuring IIS, CGI is a much less compelling option on Windows. **WARNING:** The following tutorial appears to fail with the current (2019-08-17) version of Fossil, [apparently][fbug] due to an inability of Fossil to detect that it’s being run in CGI mode. [b1s]: https://azure.microsoft.com/en-us/blog/introducing-b-series-our-new-burstable-vm-size/ [cp]: https://stackoverflow.com/a/48244/142454 [fbug]: https://fossil-scm.org/forum/forumpost/de18dc32c0 ## Install IIS with CGI Support The steps for this are identical to those for the [reverse proxying IIS setup](./iis.md#install) except that you need to enable CGI in the last step, since it isn’t installed by default. For Windows Server, the path is: ![Install CGI in IIS](./cgi-install-iis.png) The path is similar on the consumer-focused versions of Windows, once you get to that last step. ## Setup 1. Install the Fossil executable to `c:\inetpub\wwwroot\bin` on the web server. We can’t use an executable you might already have because IIS runs under a separate user account, so we need to give that executable special permissions, and that’s easiest to do under the IIS tree: ![IIS fossil.exe execute permission](./cgi-bin-perm.png) 2. In IIS Manager (a.k.a. `INETMGR`) drill down into the Sites folder in the left-side pane and right-click your web site’s configuration. (e.g. “Default Web Siteâ€) 3. On that menu say “Add Virtual Directory.†Give it the alias “`cgi`†and point it at a suitable directory, such as “`c:\inetpub\wwwroot\cgi`â€. 4. Double-click the “Handler Mappings†icon, then in the right-side pane, click “Add Script Map...†Apply the following settings: ![IIS script map dialog](./cgi-script-map.png) The Executable path must point to the path we set up in step 1, not to some other `fossil.exe` you may have elsewhere on your system. You will need to change the default “`*.dll`†filter in the Open dialog to “`*.exe`†in order to see it when browsing via the “`...`†button. 5. Create a file called `repo.fslcgi` within the CGI directory you chose in step 3, with a single line like this: repository: c:\Users\SOMEONE\museum\repo.fossil Give the actual path to the repository, of course. 6. Up at the top level of IIS Manager, double-click the “ISAPI and CGI Restrictions†icon, then click “Add...†in the right-side pane. Give the script you just created permission to execute: ![IIS CGI execute permission](./cgi-exec-perm.png) 7. In the right-side pane, click “Restart†to apply this configuration, then test it by visiting the newly-available URL in a browser: http://localhost/cgi/repo.fslcgi For more complicated setups such as “directory†mode, see [the generic CGI instructions](../any/cgi.md). *[Return to the top-level Fossil server article.](../)* |
Added www/server/windows/iis.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 | # Serving via IIS ## Why Bother? The first part of the scheme below sets Fossil up as an HTTP server, so you might be wondering why you wouldn’t just modify that to make it listen on all network interfaces on TCP port 80, so you can avoid the need for IIS entirely. For simple use cases, you can indeed do without IIS, but there are several use cases where adding it is helpful: 1. Proxying Fossil with IIS lets you [add TLS encryption][tls], which [Fossil does not currently speak](../../ssl.wiki) in its server role. 2. The URL rewriting we do below allows Fossil to be part of a larger site already being served with IIS. 3. You can have a mixed-mode site, with Fossil acting as a powerful dynamic content management service and IIS as a fast static content server. The pure-Fossil alternative requires that you check all of your static content into Fossil as versioned or unversioned artifacts. This article shows how you can get any combination of those benefits by using IIS as a reverse proxy for `fossil server`. There are other ways to use IIS to serve Fossil, such as [via CGI](./cgi.md). ## Background Fossil Service Setup You will need to have the Fossil HTTP server running in the background, serving some local repository, bound to localhost on a fixed high-numbered TCP port. For the purposes of testing, simply start it by hand in your command shell of choice: fossil serve --port 9000 --localhost repo.fossil That command assumes you’ve got `fossil.exe` in your `%PATH%` and you’re in a directory holding `repo.fossil`. See [the platform-independent instructions](../any/none.md) for further details. For a more robust setup, we recommend that you [install Fossil as a Windows service](./service.md), which will allow Fossil to start at system boot, before anyone has logged in interactively. ## <a id="install"></a>Install IIS IIS might not be installed in your system yet, so follow the path appropriate to your host OS. We’ve tested only the latest Microsoft OSes as of the time of this writing, but the basic process should be similar on older OSes. ### Windows Server 2019 1. Start “Server Manager†2. Tell it you want to “Add roles and features†3. Select “Role-based or feature-based installation†4. Select your local server 5. In the Server Roles section, enable “Web Server (IIS)†### Windows 1. Open Control Panel 2. Go to “Programs†3. Select “Turn Windows features on or off†in the left-side pane 4. In the “Windows Features†dialog, enable “Internet Information Services†The default set of IIS features there will suffice for this tutorial, but you might want to enable additional features. ## Setting up the Proxy The stock IIS setup doesn’t have reverse proxying features, but they’re easily added through extensions. You will need to install the [Application Request Routing][arr] and [URL Rewrite][ure] extensions. In my testing here, URL Rewrite showed up immediately after installing it, but I had to reboot the server to get ARR to show up. (Yay Windows.) You can install these things through the direct links above, or you can do it via the Web Platform Installer feature of IIS Manager (a.k.a. `INETMGR`). Set these extensions up in IIS Manager like so: 1. Double-click the “Application Request Routing Cache†icon. 2. Right-click in the window that results, and select “Server Proxy Settings...†3. Check the “Enable Proxy†box in the dialog. Click the “Apply†text in the right-side pane. 4. Return to the top server-level configuration area of IIS Manager and double-click the “URL Rewrite†icon. Alternately, you might find “URL Rewrite†in the right-side pane from within the ARR settings. 5. Right click in the window that results, and click “Add Rule(s)...†Tell it you want a “Blank rule†under “Inbound rulesâ€. 6. In the dialog that results, create a new rule called “Fossil repo proxy.†Set the “Pattern†to “`^(.*)$`†and “Rewrite URL†set to “`http://localhost:9000/{R:1}`â€. That tells it to take everything in the path part of the URL and send it down to localhost:9000, where `fossil server` is listening. 7. Click “Apply†in the right-side pane, then get back to the top level configuration for the server, and click “Restart†in that same pane. At this point, if you go to `http://localhost/` in your browser, you should see your Fossil repository’s web interface instead of the default IIS web site, as before you did all of the above. This is a very simple configuration. You can do more complicated and interesting things with this, such as redirecting only `/code` URLs to Fossil by setting the Pattern in step 6 to “`^/code(.*)$`â€. (You would also need to pass `--baseurl http://example.com/code` in the `fossil server` command to make this work properly.) IIS would then directly serve all other URLs. You could also intermix ASP.NET applications in the URL scheme in this way. See the documentation on [URL Rewrite rules][urr] for more ideas. *[Return to the top-level Fossil server article.](../)* [arr]: https://www.iis.net/downloads/microsoft/application-request-routing [tls]: https://docs.microsoft.com/en-us/iis/manage/configuring-security/understanding-iis-url-authorization [ure]: https://www.iis.net/downloads/microsoft/url-rewrite [urr]: https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/creating-rewrite-rules-for-the-url-rewrite-module |
Added www/server/windows/index.md.
> > > > > > > > | 1 2 3 4 5 6 7 8 | # Using Windows as a Fossil Server - [Fossil server command](./none.md) - [Fossil as CGI (IIS)](./iis.md) - [Fossil as a Service](./service.md) - [Using stunnel with Fossil on Windows](./stunnel.md) *[Return to the top-level Fossil server article.](../)* |
Added www/server/windows/none.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 | # Serving as a Standalone Server on Windows On Windows, this method works more or less identically to how it’s documented in [the generic instructions](../any/none.md). ...but only while `fossil.exe` is actually running, which is the source of much trouble on Windows. This problem has two halves: ## No App Startup Without Desktop The easy methods for starting a program in Windows at system start all require an interactive desktop. There is no *easy* way to start an arbitrary program on Windows at boot before anyone has logged in. In Unix terms, Windows has no simple equivalent to [the `/etc/rc.local` file][rcl]. You can partially get around the first problem by setting your `fossil server` call up as one of the user’s interactive startup items. Windows 10 has its own [idiosyncratic way of doing this][si10], and in older systems you have [several alternatives to this][si7]. Regardless of the actual mechanism, these will cause the Fossil standalone HTTP server to start on an *interactive desktop login* only. While you’re sitting at the Windows login screen, the Fossil server is *down*. [rcl]: http://nixdoc.net/man-pages/FreeBSD/man8/rc.local.8.html [si10]: https://www.tenforums.com/tutorials/2944-add-delete-enable-disable-startup-items-windows-10-a.html [si7]: https://www.wikihow.com/Change-Startup-Programs-in-Windows-7 ## No Simple Background Mode Windows also lacks a direct equivalent of the Bourne shell’s “`&`†control operator to run a program in the background, which you can give in Unix’s `rc.local` file, which is just a normal Bourne shell script. By “background,†I mean “not attached to any interactive user’s login session.†When the `rc.local` script exits in Unix, any program it backgrounded *stays running*. There is no simple and direct equivalent to this mechanism in Windows. If you set `fossil server` to run on interactive login, as above, it will shut right back down again when that user logs back out. With Windows 10, it’s especially problematic because you can no longer make the OS put off updates arbitrarily: your Fossil server will go down every time Windows Update decides it needs to reboot your computer, and then Fossil service will *stay* down until someone logs back into that machine interactively. ## Better Solutions Because of these problems, we only recommend setting `fossil server` up on Windows this way when you’re a solo developer or you work in a small office where everyone arrives more or less at the same time each day, and everyone goes home about the same time each day, so that one user can keep the Fossil server up through the working day. If your needs go at all beyond this, you should expect proper “server†behavior, which you can get on Windows by [registering Fossil as a Windows service](./service.md), which solves the interactive startup and shutdown problems above, at a bit of complexity over the Startup Items method. You may also want to consider putting that service behind [an IIS front-end proxy](./iis.md) to add additional web serving features. *[Return to the top-level Fossil server article.](../)* |
Added www/server/windows/service.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 | # Fossil as a Windows Service If you need Fossil to start automatically on Windows, it is suggested to install Fossil as a Windows Service. ## Assumptions 1. You have Administrative access to a Windows 2012r2 or above server. 2. You have PowerShell 5.1 or above installed. ## Place Fossil on Server However you obtained your copy of Fossil, it is recommended that you follow Windows conventions and place it within `\Program Files\FossilSCM`, the proper location for the official 64-bit binary. This way Fossil is at an expected location and you will have minimal issues with Windows interfering in your ability to run Fossil as a service. You will need Administrative rights to place fossil at the recommended location. If you will only be running Fossil as a service, you do not need to add this location to the path, though you may do so if you wish. ## Installing Fossil as a Service Luckily the hard work to use Fossil as a Windows Service has been done by the Fossil team. We simply have to install it with the proper command line options. Fossil on Windows has a command `fossil winsrv` to allow installing Fossil as a service on Windows. This command is only documented on the windows executable of Fossil. You must also run the command as administrator for it to be successful. ### Fossil winsrv Example The simplest form of the command is: ``` fossil winsrv create --repository D:/Path/to/Repo.fossil ``` This will create a windows service named 'Fossil-DSCM' running under the local system account and accessible on port 8080 by default. `fossil winsrv` can also start, stop, and delete the service. For all available options, please execute `fossil help winsrv` on a windows install of Fossil. If you wish to server a directory of repositories, the `fossil winsrv` command requires a slightly different set of options vs. `fossil server`: ``` fossil winsrv create --repository D:/Path/to/Repos --repolist ``` ### Choice of Directory Considerations When the Fossil server will be used at times that files may be locked during virus scanning, it is prudent to arrange that its directory used for temporary files is exempted from such scanning. Ordinarily, this will be a subdirectory named "fossil" in the temporary directory given by the Windows GetTempPath(...) API, [namely](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw#remarks) the value of the first existing environment variable from `%TMP%`, `%TEMP%`, `%USERPROFILE%`, and `%SystemRoot%`; you can look for their actual values in your system by accessing the `/test_env` webpage. Excluding this subdirectory will avoid certain rare failures where the fossil.exe process is unable to use the directory normally during a scan. ### <a id='PowerShell'></a>Advanced service installation using PowerShell As great as `fossil winsrv` is, it does not have one to one reflection of all of the `fossil server` [options](/help?cmd=server). When you need to use some of the more advanced options, such as `--https`, `--skin`, or `--extroot`, you will need to use PowerShell to configure and install the Windows service. PowerShell provides the [New-Service](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-5.1) command, which we can use to install and configure Fossil as a service. The below should all be entered as a single line in an Administrative PowerShell console. ```PowerShell New-Service -Name fossil -DisplayName fossil -BinaryPathName '"C:\Program Files\FossilSCM\fossil.exe" server --port 8080 --repolist "D:/Path/to/Repos"' -StartupType Automatic ``` Please note the use of forward slashes in the repolist path passed to Fossil. Windows will accept either back slashes or forward slashes in path names, but Fossil has a preference for forward slashes. The use of `--repolist` will make this a multiple repository server. If you want to serve only a single repository, then leave off the `--repolist` parameter and provide the full path to the proper repository file. Other options are listed in the [fossil server](/help?cmd=server) documentation. The service will be installed by default to use the Local Service account. Since Fossil only needs access to local files, this is fine and causes no issues. The service will not be running once installed. You will need to start it to proceed (the `-StartupType Automatic` parameter to `New-Service` will result in the service auto-starting on boot). This can be done by entering ```PowerShell Start-Service -Name fossil ``` in the PowerShell console. Congratulations, you now have a base http accessible Fossil server running on Windows. ### Removing the Windows Service If you want to remove the Fossil service, execute the following from an Administrative PowerShell or Command Prompt console: ``` sc.exe delete fossil ``` If you have Powershell version 6.0 or later, you can use: ```PowerShell Remove-Service -Name fossil ``` with the same effect. *[Return to the top-level Fossil server article.](../)* |
Added www/server/windows/stunnel.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 | # Using stunnel with Fossil on Windows While there are many ways to configure Fossil as a server using various web servers (Apache, IIS, nginx, etc.), this document will focus on setting up a minimal Fossil server using only Fossil's native [server capabilities](../any/none.md) and [stunnel](https://www.stunnel.org/) to provide a TLS proxy. It is recommended for public repositories to go to the extra step of configuring stunnel to provide a proper HTTPS setup. ## Assumptions 1. You have Administrative access to a Windows 2012r2 or above server. 2. You have PowerShell 5.1 or above installed. 3. You have acquired a certificate either from a Public CA or an Internal CA. ## Configure Fossil Service for https Due to the need for the `--https` option for successfully using Fossil with stunnel, we will use [Advanced service installation using PowerShell](./service.md#PowerShell). We will need to change the command to install the Fossil Service to configure it properly for use with stunnel as an https proxy. Run the following: ```PowerShell New-Service -Name fossil-secure -DisplayName fossil-secure -BinaryPathName '"C:\Program Files\FossilSCM\fossil.exe" server --localhost --port 9000 --https --repolist "D:/Path/to/Repos"' -StartupType Automatic ``` The use of `--localhost` means Fossil will only listen for traffic on the local host on the designated port - 9000 in this case - and will not respond to network traffic. Using `--https` will tell Fossil to generate HTTPS URLs rather than HTTP ones. `New-Service` does not automatically start a service on install, so you will need to enter the following to avoid rebooting the server: ```PowerShell Start-Service -Name fossil-secure ``` To remove the service, run the following in a Powershell or cmd console: ``` sc.exe delete fossil ``` or (in a Powershell console) ```PowerShell Remove-Service -Name fossil ``` if your version of Powershell is 6.0 or above. ## Install stunnel 5.55 Download stunnel from the [downloads](https://www.stunnel.org/downloads.html) page. Select the latest stunnel windows package (at the time of writing this is `stunnel-5.55-win64-installer.exe`). Execute the installer and make sure you install openSSL tools when you install stunnel. You will need this to convert your certificate from PFX to PEM format. Even though the installer says it is for win64, it installs stunnel by default to `\Program Files (x86)\stunnel`. ## Get your certificate ready for Stunnel Whether you use a Public Certificate Authority or Internal Certificate Authority, the next step is exporting the certificate from Windows into a format useable by Stunnel. ### Export Certificate from Windows If your certificate is installed via Windows Certificate Management, you will need to export the certificate into a usable format. You can do this either using the Windows Certificate Management Console, or PowerShell. #### Certificate Management Console Start `mmc.exe` as an Administrator. Select 'File>Add/Remove Snapin', select 'Certificates' from the list, and click 'Add'. Select 'Computer Account', 'Next', 'Local Computer', and then 'Finish'. In the Console Root, expand 'Certificates', then 'Personal', and select 'Certificates'. In the middle pane find and select your certificate. Right click the certificate and select 'All Tasks>Export'. You want to export as PFX the Private Key, include all certificates in the certification path, and use a password only to secure the file. Enter a path and file name to a working directory and complete the export. Continue with [Convert Certificate from PFX to PEM](#convert). #### PowerShell If you know the Friendly Name of the Certificate this is relatively easy. Since you need to export the private key as well, you must run the following from an Administrative PowerShell console. ```PowerShell $passwd = ConvertTo-SecureString -string "yourpassword" -Force -AsPlainText Get-ChildItem Cert:\LocalMachine\My | Where{$_.FriendlyName -eq "FriendlyName"} | Export-PfxCertificate -FilePath fossil-scm.pfx -Password $passwd ``` You will now have your certificate stored as a PFX file. <a id="convert"></a> ### Convert Certificate from PFX to PEM For this step you will need the openssl tools that were installed with stunnel. ```PowerShell # Add stunnel\bin directory to path for this session. $env:PATH += ";${env:ProgramFiles(x86)}\stunnel\bin" # Export Private Key openssl.exe pkcs12 -in fossil-scm.pfx -out fossil-scm.key -nocerts -nodes # Export the Certificate openssl.exe pkcs12 -in fossil-scm.pfx -out fossil-scm.pem -nokeys ``` Now move `fossil-scm.key` and `fossil-scm.pem` to your stunnel config directory (by default this should be located at `\Program Files (x86)\stunne\config`). ## stunnel Configuration Use the reverse proxy configuration given in the generic [Serving via stunnel document](../any/stunnel.md#proxy). On Windows, the `stunnel.conf` file is located at `\Program Files (x86)\stunnel\config`. You will need to modify it to point at the PEM and key files generated above. After completing the above configuration restart the stunnel service in Windows with the following: ```PowerShell Restart-Service -Name stunnel ``` ## Open up port 443 in the Windows Firewall The following instructions are for the [Windows Advanced Firewall](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-firewall/windows-firewall-with-advanced-security). If you are using a different Firewall, please consult your Firewall documentation for how to open port 443 for inbound traffic. The following command should be entered all on one line. ```PowerShell New-NetFirewallRule -DisplayName "Allow Fossil Inbound" -Description "Allow Fossil inbound on port 443 using Stunnel as TLS Proxy." -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow -Program "C:\Program Files (x86)\Stunnel\bin\stunnel.exe" ``` You should now be able to access your new Fossil Server via HTTPS. *[Return to the top-level Fossil server article.](../)* |
Added www/serverext.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 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 | <title>CGI Server Extensions</title> <h2>1.0 Introduction</h2> If you have a [./server/|Fossil server] for your project, you can add [./aboutcgi.wiki|CGI] extensions to that server. These extensions work like any other CGI program, except that they also have access to the Fossil login information and can (optionally) leverage the "[./customskin.md|skins]" of Fossil so that they appear to be more tightly integrated into the project. An example of where this is useful is the [https://sqlite.org/src/ext/checklist|checklist application] on the [https://sqlite.org/|SQLite] project. The checklist helps the SQLite developers track which release tests have passed, or failed, or are still to be done. The checklist program began as a stand-alone CGI which kept its own private user database and implemented its own permissions and login system and provided its own CSS. By converting checklist into a Fossil extension, the same login that works for the [https://sqlite.org/src|main SQLite source repository] also works for the checklist. Permission to change elements of the checklist is tied on permission to check-in to the main source repository. And the standard Fossil header menu and footer appear on each page of the checklist. <h2>2.0 How It Works</h2> CGI Extensions are disabled by default. An administrator activates the CGI extension mechanism by specifying an "Extension Root Directory" or "extroot" as part of the [./server/index.html|server setup]. If the Fossil server is itself run as [./server/any/cgi.md|CGI], then add a line to the [./cgi.wiki#extroot|CGI script file] that says: <pre> extroot: <i>DIRECTORY</i> </pre> Or, if the Fossil server is being run using the "[./server/any/none.md|fossil server]" or "[./server/any/none.md|fossil ui]" or "[./server/any/inetd.md|fossil http]" commands, then add an extra "--extroot <i>DIRECTORY</i>" option to that command. The <i>DIRECTORY</i> is the DOCUMENT_ROOT for the CGI. Files in the DOCUMENT_ROOT are accessed via URLs like this: <pre> https://example-project.org/ext/<i>FILENAME</i> </pre> In other words, access files in DOCUMENT_ROOT by appending the filename relative to DOCUMENT_ROOT to the [/help?cmd=/ext|/ext] page of the Fossil server. Files that are readable but not executable are returned as static content. Files that are executable are run as CGI. <h3>2.1 Example #1</h3> The source code repository for SQLite is a Fossil server that is run as CGI. The URL for the source code repository is [https://sqlite.org/src]. The CGI script looks like this: <verbatim> #!/usr/bin/fossil repository: /fossil/sqlite.fossil errorlog: /logs/errors.txt extroot: /sqlite-src-ext </verbatim> The "extroot: /sqlite-src-ext" line tells Fossil that it should look for extension CGIs in the /sqlite-src-ext directory. (All of this is happening inside of a chroot jail, so putting the document root in a top-level directory is a reasonable thing to do.) When a URL like "https://sqlite.org/src/ext/checklist" is received by the main webserver, it figures out that the /src part refers to the main Fossil CGI script and so it runs that script. Fossil gets the remainder of the URL to work with: "/ext/checklist". Fossil extracts the "/ext" prefix and uses that to determine that this a CGI extension request. Then it takes the leftover "/checklist" part and appends it to the "extroot" to get the filename "/sqlite-src-ext/checklist". Fossil finds that file to be executable, so it runs it as CGI and returns the result. The /sqlite-src-ext/checklist file is a [https://wapp.tcl.tk|Wapp program]. The current source code to the this program can be seen at [https://www.sqlite.org/src/ext/checklist/3070700/self] and recent historical versions are available at [https://sqlite.org/docsrc/finfo/misc/checklist.tcl] with older legacy at [https://sqlite.org/checklistapp/timeline?n1=all] There is a cascade of CGIs happening here. The web server that receives the initial HTTP request runs Fossil as a CGI based on the "https://sqlite.org/src" portion of the URL. The Fossil instance then runs the checklist sub-CGI based on the "/ext/checklists" suffix. The output of the sub-CGI is read by Fossil and then relayed on to the main web server which in turn relays the result back to the original client. <h3>2.2 Example #2</h3> The [https://fossil-scm.org/home|Fossil self-hosting repository] is also a CGI that looks like this: <verbatim> #!/usr/bin/fossil repository: /fossil/fossil.fossil errorlog: /logs/errors.txt extroot: /fossil-extroot </verbatim> The extroot for this Fossil server is /fossil-extroot and in that directory is an executable file named "fileup1" - another [https://wapp.tcl.tk|Wapp] script. (The extension mechanism is not required to use Wapp. You can use any kind of program you like. But the creator of SQLite and Fossil is fond of [https://www.tcl.tk|Tcl/Tk] and so he tends to gravitate toward Tcl-based technologies like Wapp.) The fileup1 script is a demo program that lets the user upload a file using a form, and then displays that file in the reply. There is a link on the page that causes the fileup1 script to return a copy of its own source-code, so you can see how it works. <h2 id="cgi-inputs">3.0 CGI Inputs</h2> The /ext extension mechanism is an ordinary CGI interface. Parameters are passed to the CGI program using environment variables. The following standard CGI environment variables are supported: * AUTH_TYPE * AUTH_CONTENT * CONTENT_LENGTH * CONTENT_TYPE * DOCUMENT_ROOT * GATEWAY_INTERFACE * HTTPS * HTTP_ACCEPT * HTTP_ACCEPT_ENCODING * HTTP_COOKIE * HTTP_HOST * HTTP_IF_MODIFIED_SINCE * HTTP_IF_NONE_MATCH * HTTP_REFERER * HTTP_USER_AGENT * PATH_INFO * QUERY_STRING * REMOTE_ADDR * REMOTE_USER * REQUEST_METHOD * REQUEST_SCHEME * REQUEST_URI * SCRIPT_DIRECTORY * SCRIPT_FILENAME * SCRIPT_NAME * SERVER_NAME * SERVER_PORT * SERVER_PROTOCOL * SERVER_SOFTWARE Do a web search for "[https://duckduckgo.com/?q=cgi+environment_variables|cgi environment variables]" to find more detail about what each of the above variables mean and how they are used. Live listings of the values of some or all of these environment variables can be found at links like these: * [https://fossil-scm.org/home/test_env] * [https://sqlite.org/src/ext/checklist/top/env] In addition to the standard CGI environment variables listed above, Fossil adds the following: * FOSSIL_CAPABILITIES * FOSSIL_NONCE * FOSSIL_REPOSITORY * FOSSIL_URI * FOSSIL_USER The FOSSIL_USER string is the name of the logged-in user. This variable is missing or is an empty string if the user is not logged in. The FOSSIL_CAPABILITIES string is a list of [./caps/ref.html|Fossil capabilities] that indicate what permissions the user has on the Fossil repository. The FOSSIL_REPOSITORY environment variable gives the filename of the Fossil repository that is running. The FOSSIL_URI variable shows the prefix of the REQUEST_URI that is the Fossil CGI script, or is an empty string if Fossil is being run by some method other than CGI. The [https://sqlite.org/src/ext/checklist|checklist application] uses the FOSSIL_USER environment variable to determine the name of the user and the FOSSIL_CAPABILITIES variable to determine if the user is allowed to mark off changes to the checklist. Only users with check-in permission to the Fossil repository are allowed to mark off checklist items. That means that the FOSSIL_CAPABILITIES string must contain the letter "i". Search for "FOSSIL_CAPABILITIES" in the [https://sqlite.org/src/ext/checklist/top/self|source listing] to see how this happens. If the CGI output is one of the forms for which Fossil inserts its own header and footer, then the inserted header will include a Content Security Policy (CSP) restriction on the use of javascript within the webpage. Any <script>...</script> elements within the CGI output must include a nonce or else they will be suppressed by the web browser. The FOSSIL_NONCE variable contains the value of that nonce. So, in other words, to get javascript to work, it must be enclosed in: <verbatim> <script nonce='$FOSSIL_NONCE'>...</script> </verbatim> Except, of course, the $FOSSIL_NONCE is replaced by the value of the FOSSIL_NONCE environment variable. <h3>3.1 Input Content</h3> If the HTTP request includes content (for example if this is a POST request) then the CONTENT_LENGTH value will be positive and the data for the content will be readable on standard input. <h2>4.0 CGI Outputs</h2> CGI programs construct a reply by writing to standard output. The first few lines of output are parameters intended for the web server that invoked the CGI. These are followed by a blank line and then the content. Typical parameter output looks like this: <verbatim> Status: 200 OK Content-Type: text/html </verbatim> CGI programs can return any content type they want - they are not restricted to text replies. It is OK for a CGI program to return (for example) image/png. The fields of the CGI response header can be any valid HTTP header fields. Those that Fossil does not understand are simply relayed back to up the line to the requester. Fossil takes special action with some content types. If the Content-Type is "text/x-fossil-wiki" or "text/x-markdown" then Fossil converts the content from [/wiki_rules|Fossil-Wiki] or [/md_rules|Markdown] into HTML, adding its own header and footer text according to the repository skin. Content of type "text/html" is normally passed straight through unchanged. However, if the text/html content is of the form: <verbatim> <div class='fossil-doc' data-title='DOCUMENT TITLE'> ... HTML content there ... </div> </verbatim> In other words, if the outer-most markup of the HTML is a <div> element with a single class of "fossil-doc", then Fossil will adds its own header and footer to the HTML. The page title contained in the added header will be extracted from the "data-title" attribute. Except for the three cases noted above, Fossil makes no changes or additions to the CGI-generated content. Fossil just passes the verbatim content back up the stack towards the requester. <h3>4.1 <tt>GATEWAY_INTERFACE</tt> and Recursive Calls to fossil</h3> Like many CGI-aware applications, if fossil sees the environment variable <tt>GATEWAY_INTERFACE</tt> when it starts up, it assumes it is running in a CGI environment and behaves differently than when it is run in a non-CGI interactive session. If you intend to run fossil itself from within an extension CGI script, e.g. to run a query against the repository or simply fetch the fossil binary version, make sure to <em>unset</em> the <tt>GATEWAY_INTERFACE</tt> environment variable before doing so, otherwise the invocation will behave as if it's being run in CGI mode. <h2>5.0 Filename Restrictions</h2> For security reasons, Fossil places restrictions on the names of files in the extroot directory that can participate in the extension CGI mechanism: 1. Filenames must consist of only ASCII alphanumeric characters, ".", "_", and "-", and of course "/" as the file separator. Files with names that includes spaces or other punctuation or special characters are ignored. 2. No element of the pathname can begin with "." or "-". Files or directories whose names begin with "." or "-" are ignored. If a CGI program requires separate data files, it is safe to put those files in the same directory as the CGI program itself as long as the names of the data files contain special characters that cause them to be ignored by Fossil. <h2>6.0 Access Permissions</h2> CGI extension files and programs are accessible to everyone. When CGI extensions have been enabled (using either "extroot:" in the CGI file or the --extroot option for other server methods) all files in the extension root directory hierarchy, except special filenames identified previously, are accessible to all users. Users do not have to have "Read" privilege, or any other privilege, in order to access the extensions. This is by design. The CGI extension mechanism is intended to operate in the same way as a traditional web-server. CGI programs that want to restrict access can examine the FOSSIL_CAPABILITIES and/or FOSSIL_USER environment variables. In other words, access control is the responsibility of the individual extension programs. <h2>7.0 Trouble-Shooting Hints</h2> Remember that the /ext will return any file in the extroot directory hierarchy as static content if the file is readable but not executable. When initially setting up the /ext mechanism, it is sometimes helpful to verify that you are able to receive static content prior to starting work on your CGIs. Also remember that CGIs must be executable files. Fossil likes to run inside a chroot jail, and will automatically put itself inside a chroot jail if it can. The sub-CGI program will also run inside this same chroot jail. Make sure all embedded pathnames have been adjusted accordingly and that all resources needed by the CGI program are available within the chroot jail. If anything goes wrong while trying to process an /ext page, Fossil returns a 404 Not Found error with no details. However, if the requester is logged in as a user that has <b>[./caps/ref.html#D | Debug]</b> capability then additional diagnostic information may be included in the output. If the /ext page has a "fossil-ext-debug=1" query parameter and if the requester is logged in as a user with Debug privilege, then the CGI output is returned verbatim, as text/plain and with the original header intact. This is useful for diagnosing problems with the CGI script. |
Added www/settings.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 | <title>Fossil Settings</title> <h2>Using Fossil Settings</h2> Settings control the behaviour of fossil. They are set with the <tt>fossil settings</tt> command, or through the web interface in the Settings page in the Admin section. For a list of all settings, view the Settings page, or type <tt>fossil help settings</tt> from the command line. <h3 id="repo">Repository settings</h3> Settings are set on a per-repository basis. When you clone a repository, a subset of settings are copied to your local repository. If you make a change to a setting on your local repository, it is not synced back to the server when you <tt>push</tt> or <tt>sync</tt>. If you make a change on the server, you need to manually make the change on all repositories which are cloned from this repository. You can also set a setting globally on your local machine. The value will be used for all repositories cloned to your machine, unless overridden explicitly in a particular repository. Global settings can be set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> command. <h3 id="versionable">"Versionable" settings</h3> Most of the settings control the behaviour of fossil on your local machine, largely acting to reflect your preference on how you want to use Fossil, how you communicate with the server, or options for hosting a repository on the web. However, for historical reasons, some settings affect how you work with versioned files. These are <tt>clean-glob</tt>, <tt>binary-glob</tt>, <tt>crlf-glob</tt> (and its alias <tt>crnl-glob</tt>), <tt>empty-dirs</tt>, <tt>encoding-glob</tt>, <tt>ignore-glob</tt>, <tt>keep-glob</tt>, <tt>manifest</tt>, and <tt>mimetypes</tt>. The most important is <tt>ignore-glob</tt> which specifies which files should be ignored when looking for unmanaged files with the <tt>extras</tt> command. Because these options can change over time, and the inconvenience of replicating changes, these settings are "versionable". As well as being able to be set using the <tt>settings</tt> command or the web interface, you can create versioned files in the <tt>.fossil-settings</tt> subdirectory of the check-out root, named with the setting name. The contents of the file is the value of the setting, and these files are checked in, committed, merged, and so on, as with any other file. Where a setting is a list of values, such as <tt>ignore-glob</tt>, you can use a newline as a separator as well as a comma. For example, to set the list of ignored files, create a <tt>.fossil-settings/ignore-glob</tt> file where each line contains a glob for ignored files. If you set the value of a setting using the <tt>settings</tt> command as well as a versioned file, the versioned setting will take precedence. A warning will be displayed. |
Changes to www/shunning.wiki.
1 | <title>Deleting Content From Fossil</title> | | > | < > | | < | | | > > | | < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | > | > > | | | | 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 | <title>Deleting Content From Fossil</title> <h2>Good Reasons for Removing Content from a Fossil Repository</h2> Fossil is designed to keep all historical content forever. Fossil purposely makes it difficult for users to delete content. Old content is part of the project's <i>*ahem*</i> fossil record and should be maintained indefinitely to maintain an accurate history of the project. Nevertheless, there may occasionally arise legitimate reasons for deleting content. Such reasons include: * Spammers inserted inappropriate content into a wiki page, forum post, or ticket. Fossil lets you easily hide or amend such content, but since it is not a legitimate part of the project's history, there is no value in keeping it, so it is best removed permanently. * A file that contains trade secrets or that is under someone else's copyright was accidentally committed and needs to be backed out. * A malformed control artifact was inserted and is disrupting the operation of Fossil. * A legitimate legal request was received requiring content to be removed. This would most likely be related to the accidental intellectual property error or spam cases listed above. Some countries recognise software patents, and so allow legal claims targetting code commits. Some countries can require publicly-available encryption software to be taken down if it is committed to the DAG without the correct government authorisation. <h2>Alternatives</h2> All of these are rare cases: Fossil is [./antibot.wiki | designed to foil spammers up front], legally problematic check-ins should range from rare to nonexistent, and you have to go way out of your way to force Fossil to insert bad control artifacts. Therefore, before we get to methods of permanently deleting content from a Fossil repos, let's give some alternatives that usually suffice, which don't damage the project's fossil record: * When a forum post or wiki article is "deleted," what actually happens is that a new empty version is added to the Fossil repository. The web interface interprets this as "deleted," but the prior version remains available if you go digging for it. * When you close a ticket, it's marked in a way that causes it to not show up in the normal ticket reports. You usually want to give it a Resolution such as "Rejected" when this happens, plus possibly a comment explaining why you're closing it. This is all new information added to the ticket, not deletion. * When you <tt>fossil rm</tt> a file, a new manifest is checked into the repository with the same file list as for the prior version minus the "removed" file. The file is still present in the repository; it just isn't part of that version forward on that branch. * If you make a bad check-in, you can shunt it off to the side by amending it to put it on a different branch, then continuing development on the prior branch: <br><br> <code>$ fossil amend abcd1234 --branch BOGUS --hide<br> $ fossil up trunk</code> <br><br> The first command moves check-in ID <tt>abcd1234</tt> (and any subsequent check-ins on that branch!) to a branch called <tt>BOGUS</tt>, then hides it so it doesn't show up on the timeline. You can call this branch anything you like, and you can re-use the same name as many times as you like. No content is actually deleted: it's just shunted off to the side and hidden away. You might find it easier to do this from the Fossil web UI in the "edit" function for a check-in. <br><br> The second command returns to the last good check-in on that branch so you can continue work from that point. * When the check-in you want to remove is followed by good check-ins on the same branch, you can't use the previous method, because it will move the good check-ins, too. The solution is: <br><br> <tt>$ fossil merge --backout abcd1234</tt> <br><br> That creates a diff in the check-out directory that backs out the bad check-in <tt>abcd1234</tt>. You then fix up any merge conflicts, build, test, etc., then check the reverting change into the repository. Again, nothing is actually deleted; you're just adding more information to the repository which corrects a prior check-in. <h2>Exception: Non-versioned Content</h2> It is normal and expected to delete data which is not versioned, such as usernames and passwords in the user table. The [/help/scrub|fossil scrub] command will remove all sensitive non-versioned data from a repository. The scrub command will remove user 'bertina', along with their password, any supplied IP address, any concealed email address etc. However, in the DAG, commits by 'bertina' will continue to be visible unchanged even though there is no longer any such user in Fossil. <h2>Shunning</h2> Fossil provides a mechanism called "shunning" for removing content from a repository. Every Fossil repository maintains a list of the hash names of "shunned" artifacts. Fossil will refuse to push or pull any shunned artifact. Furthermore, all shunned artifacts (but not the shunning list 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" users will usually see a "Shun" option in the submenu that will take them directly to the shunning page and enable that artifact to be shunned with a single additional mouse click. |
Added www/ssl-server.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 | # SSL/TLS Server Mode ## History Fossil has supported [client-side SSL/TLS][0] since [2010][1]. This means that commands like "[fossil sync](/help?cmd=sync)" could use SSL/TLS when contacting a server. But on the server side, commands like "[fossil server](/help?cmd=server)" operated in clear-text only. To implement an encrypted server, you had to put Fossil behind a web server or reverse proxy that handled the SSL/TLS decryption/encryption and passed cleartext down to Fossil. [0]: ./ssl.wiki [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13 Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13), this has been fixed. Commands like * "[fossil server](/help?cmd=server)" * "[fossil ui](/help?cmd=ui)", and * "[fossil http](/help?cmd=http)" now all handle server-mode SSL/TLS encryption natively. It is now possible to run a secure Fossil server without having to put Fossil behind an encrypting web server or reverse proxy. Hence, it is now possible to stand up a complete Fossil project website on an inexpensive VPS with no added software other than Fossil itself and something like [certbot](https://certbot.eff.org) for obtaining a CA-signed certificate. ## Usage To put any of the Fossil server commands into SSL/TLS mode, simply add the "--cert" command-line option. fossil ui --cert unsafe-builtin The --cert option is what tells Fossil to use TLS encryption. Normally, the argument to --cert is the name of a file containing the certificate (the "fullchain.pem" file) for the website. In this example, the magic name "unsafe-builtin" is used, which causes Fossil to use a self-signed cert rather than a real cert obtained from a [Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority) or "CA". As the name implies, this self-signed cert is not secure and should only be used for testing. Your web-browser will complain bitterly and will refuse to display the pages using the "unsafe-builtin" cert. Firefox will allow you to click an "I know the risks" button and continue. Other web browsers will stubornly refuse to display the page, under the theory that weak encryption is worse than no encryption at all. Continue reading to see how to solve this. ## About Certs Certs are based on public-key or asymmetric cryptography. To create a cert, you first create a new "key pair" consisting of a public key and a private key. The public key can be freely shared with the world, but you must keep the private key secret. If anyone gains access to your private key then he will be able to impersonate you and break into your system. To obtain a cert, you send your public key and the name of the domain you want to protect to a certificate authority. The CA then digitally signs the combination of those two things using their own private key and sends the signed combination back to you. The CA's digital signature of your public key and domain name is the cert. SSL/TLS servers need two things in order to prove their identity to clients: 1. The cert that was signed by a CA 2. The private key The SSL/TLS servers send the cert to each client, so that the client can verify it. But the private key is kept strictly private and is never shared with anyone. ## How To Tell Fossil About Your Cert And Private Key If you do not have your own cert and private key, you can ask Fossil to use "unsafe-builtin", which is a self-signed cert that is built into Fossil. This is wildly insecure, since the private key is not really private - it is [in plain sight](/info/c2a7b14c3f541edb96?ln=89-116) in the Fossil source tree for anybody to read. <b>Never add the private key that is built into Fossil to your OS's trust store</b> as doing so will severely compromise your computer. The built-in cert is only useful for testing. If you want actual security, you will need to come up with your own private key and cert. Fossil wants to read certs and public keys in the [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail). PEM is a pure ASCII text format. The private key consists of text like this: -----BEGIN PRIVATE KEY----- *base-64 encoding of the private key* -----END PRIVATE KEY----- Similarly, a PEM-encoded cert will look like this: -----BEGIN CERTIFICATE----- *base-64 encoding of the certificate* -----END CERTIFICATE----- In both formats, text outside of the delimiters is ignored. That means that if you have a PEM-formatted private key and a separate PEM-formatted certificate, you can concatenate the two into a single file and the individual components will still be easily accessible. If you have a single file that holds both your private key and your cert, you can hand it off to the "[fossil server](/help?cmd=server)" command using the --cert option. Like this: fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil The command above is sufficient to run a fully-encrypted web site for the "myproject.fossil" Fossil repository. This command must be run as root, since it wants to listen on TCP port 443, and only root processes are allowed to do that. This is safe, however, since before reading any information off of the wire, Fossil will put itself inside a chroot jail at /home/www and drop all root privileges. ### Keeping The Cert And Private Key In Separate Files If you do not want to combine your cert and private key into a single big PEM file, you can keep them separate using the --pkey option to Fossil. fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil ## The ACME Protocol The [ACME Protocol][2] is used to prove to a CA that you control a website. CAs require proof that you control a domain before they will issue a cert for that domain. The usual means of dealing with ACME is to run the separate [certbot](https://certbot.eff.org) tool. Here is, in a nutshell, what certbot will do to obtain your cert: 1. Certbot sends your "signing request" (the document that contains your public key and your domain name) to the CA. 2. After receiving the signing request, the CA needs to verify that you control the domain of the cert. To do this (or, one common way of doing this, at least) the CA sends a secret token back to certbot through a secure backchannel, and instructs certbot to make that token accessible on the (unencrypted, ordinary "http:") web site for the domain in a particular file under the ".well-known" subdirectory. 3. Certbot puts the token where the CA requested it, then notifies the CA that it is there. 4. The CA accesses the token to confirm that you do indeed control the website. It then creates the cert and sends it back to certbot. 5. Certbot stores your cert and deletes the ".well-known" token. In order for all of this to happen, certbot needs to be able to create a subdirectory named ".well-known", within a directory you specify, and then populate that subdirectory with a token file of some kind. To support this, the "[fossil server](/help?cmd=server)" and "[fossil http](/help?cmd=http)" commands have the --acme option. When the --acme option is specified and Fossil sees a URL where the path begins with ".well-known", then instead of doing its normal processing, it looks for a file with that pathname and returns it to the client. If the "server" or "http" command is referencing a single Fossil repository, then the ".well-known" sub-directory should be in the same directory as the repository file. If the "server" or "http" command are run against a directory full of Fossil repositories, then the ".well-known" sub-directory should be in that top-level directory. Thus, to set up a project website, you should first run Fossil in ordinary unencrypted HTTP mode like this: fossil server --port 80 --acme /home/www/myproject.fossil Then you create your public/private key pair and run certbot, giving it a --webroot of /home/www. Certbot will create the sub-directory named "/home/www/.well-known" and put token files there, which the CA will verify. Then certbot will store your new cert in a particular file. Once certbot has obtained your cert, then you can concatenate that cert with your private key and run Fossil in SSL/TLS mode as shown above. [2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment |
Added www/ssl.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 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 | <title>Securing a Repository with TLS</title> <h2>Using TLS-Encrypted Communications with Fossil</h2> If you are storing sensitive information in a repository accessible over a network whose integrity you do not fully trust, you should use TLS to encrypt all communications with it. This is most true for repositories accessed over the Internet, especially if they will be accessed from edge networks you do not control, since that admits of various forms of [https://en.wikipedia.org/wiki/Man-in-the-middle_attack|man in the middle attack]. TLS protects the credentials used to access the server, prevents eavesdropping, prevents in-flight data modification, prevents server identify spoofing, and more. There are two major aspects to this, both of which have to be addressed in different ways. Those are the subjects of the next two major sections. <h2 id="client">Client-Side Configuration</h2> You can build Fossil against [https://www.openssl.org/ | OpenSSL] to allow it to clone and sync with a remote Fossil repository via <tt>https</tt> URIs. <h3 id="openssl-bin">Building Against OpenSSL Automatically</h3> The <tt>configure</tt> script will attempt to find OpenSSL on your system automatically. It first tries asking the <tt>pkg-config</tt> system where the OpenSSL development files are, and if that fails, it falls back to looking through a list of likely directories. If it can't find the files it needs, the most common solution is to install the OpenSSL development package on your system via your OS's package manager. Examples: * <b>RHEL & Fedora</b>: <tt>sudo dnf install openssl-devel</tt> * <b>Debian & Ubuntu</b>: <tt>sudo apt install libssl-dev</tt> * <b>FreeBSD</b>: <tt>su -c 'pkg install openssl'</tt> * <b>macOS</b>: <tt>sudo brew install openssl</tt> * <b>Cygwin</b>: Install <tt>openssl-devel</tt> via Cygwin's <tt>setup-*.exe</tt> program The macOS case requires explanation. Apple last shipped OpenSSL develpoment files in OS X 10.6 (Snow Leopard), choosing to deprecate it from that point forward. (Apple wants you to use their proprietary platform-specific encryption methods instead.) Since macOS has no built-in package manager, a number have sprung up out of the FOSS world. It is not known to this author whether Fossil's current build system can find OpenSSL as installed with any of these other package managers, so unless you have a particular reason to avoid it, we recomend that you use [https://brew.sh|Homebrew] on macOS to install OpenSSL as above. Fossil's build system will seek it out and use it automatically. <h3 id="openssl-src">Building Against a Non-Platform Version of OpenSSL</h3> The Fossil build system has a few other methods for finding OpenSSL when the automatic methods fail or when you'd prefer that Fossil use a different version of OpenSSL than the one Fossil's build system picks on its own. A good reason to do this is when the Fossil build system finds a functioning version of OpenSSL which is nevertheless unsuitable. One common case is that your OS is sufficiently outdated that the platform version of OpenSSL can no longer communicate with remote systems adhering to the latest advice on secure communications. An old OpenSSL might not support any of the [https://en.wikipedia.org/wiki/Cipher_suite|cipher suites] the remote Fossil repository's HTTPS proxy is willing to offer, for example, so that even though both sides are speaking a variant of TLS/SSL, the peers cannot come to an agreement on the cryptography. If you've installed the OpenSSL development files somewhere that Fossil's build system cannot find on its own, you can clue it in by passing the <tt>--with-openssl</tt> option to the <tt>configure</tt> script. Type <tt>./configure --help</tt> for details. Another option is to download the source code to OpenSSL and build Fossil against that private version of OpenSSL: <pre> cd compat # relative to the Fossil source tree root tar xf /path/to/openssl-*.tar.gz ln -fs openssl-x.y.z openssl cd openssl ./config # or, e.g. ./Configure darwin64-x86_64-cc make -j11 cd ../.. ./configure --with-openssl=tree make -j11 </pre> That will get you a Fossil binary statically linked to this in-tree version of OpenSSL. Beware, taking this path typically opens you up to new problems, which are conveniently covered in the next section! <h3 id="certs">Certificates</h3> To verify the identify of a server, TLS uses [https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates], a scheme that depends on a trust hierarchy of so-called [https://en.wikipedia.org/wiki/Certificate_authority | Certificate Authorities]. The tree of trust relationships ultimately ends in the CA roots, which are considered the ultimate arbiters of who to trust in this scheme. The question then is, what CA roots does Fossil trust? If you are using a self-signed certificate, Fossil will initially not know that it can trust your certificate, so you'll be asked if you want to accept the certificate the first time you communicate with the server. Verify the certificate fingerprint is correct, then answer "always" if you want Fossil to remember your decision. If you are cloning from or syncing to Fossil servers that use a certificate signed by a well-known CA or one of its delegates, Fossil still has to know which CA roots to trust. When this fails, you get an error message that looks like this: <pre> Unable to verify SSL cert from fossil-scm.org subject: CN = sqlite.org issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 sha256: bf26092dd97df6e4f7bf1926072e7e8d200129e1ffb8ef5276c1e5dd9bc95d52 accept this cert and continue (y/N)? </pre> In older versions, the message was much longer and began with this line: <pre> SSL verification failed: unable to get local issuer certificate </pre> Fossil relies on the OpenSSL library to have some way to check a trusted list of CA signing keys. There are two common ways this fails: # The OpenSSL library Fossil is linked to doesn't have a CA signing key set at all, so that it initially trusts no certificates at all. # The OpenSSL library does have a CA cert set, but your Fossil server's TLS certificate was signed by a CA that isn't in that set. A common reason to fall into the second trap is that you're using certificates signed by a local private CA, as often happens in large enterprises. You can solve this sort of problem by getting your local CA's signing certificate in PEM format and pointing OpenSSL at it: <pre> fossil set --global ssl-ca-location /path/to/local-ca.pem </pre> The use of <tt>--global</tt> with this option is common, since you may have multiple repositories served under certificates signed by that same CA. However, if you have a mix of publicly-signed and locally-signed certificates, you might want to drop the <tt>--global</tt> flag and set this option on a per-repository basis instead. A common way to run into the broader first problem is that you're on FreeBSD, which does not install a CA certificate set by default, even as a dependency of the OpenSSL library. If you're using a certificate signed by one of the major public CAs, you can solve this by installing the <tt>ca_root_nss</tt> package. That package contains the Mozilla NSS certificate bundle, which gets installed in a location that OpenSSL checks by default, so you don't need to change any Fossil settings. (This is the same certificate set that ships with Firefox, by the way.) The same sort of thing happens with the Windows build of OpenSSL, but for a different core reason: Windows does ship with a stock CA certificate set, but it's not in a format that OpenSSL understands how to use. Rather than try to find a way to convert the data format, you may find it acceptable to use the same Mozilla NSS cert set. I do not know of a way to easily get this from Mozilla themselves, but I did find a [https://curl.se/docs/caextract.html | third party source] for the <tt>cacert.pem</tt> file. I suggest placing the file into your Windows user home directory so that you can then point Fossil at it like so: <pre> fossil set --global ssl-ca-location %userprofile%\cacert.pem </pre> This can also happen if you've linked Fossil to a version of OpenSSL [#openssl-src|built from source]. That same <tt>cacert.pem</tt> fix can work in that case, too. When you build Fossil on Linux platforms against the binary OpenSSL package provided with the OS, you typically get a root cert store along with the platform OpenSSL package, either built-in or as a hard dependency. <h4>Client-Side Certificates</h4> You can also use client side certificates to add an extra layer of authentication, over and above Fossil's built in user management. If you are particularly paranoid, you'll want to use this to remove the ability of anyone on the internet from making any request to Fossil. Without presenting a valid client side certificate, the web server won't invoke the Fossil CGI handler. Configure your server to request a client side certificate, and set up a certificate authority to sign your client certificates. For each person who needs to access the repository, create a private key and certificate signed with that CA. The PEM encoded private key and certificate should be stored in a single file, simply by concatenating the key and certificate files. Specify the location of this file with the <tt>ssl-identity</tt> setting, or the <tt>--ssl-identity</tt> option to the <tt>clone</tt> command. If you've password protected the private key, the password will be requested every time you connect to the server. This password is not stored by fossil, as doing so would defeat the purpose of having a password. If you attempt to connect to a server which requests a client certificate, but don't provide one, fossil will show an error message which explains what to do to authenticate with the server. <h2 id="server">Server-Side Configuration</h2> Before Fossil's built-in HTTP server gained [./ssl-server.md | TLS support], system administrators that wanted to add this had to put it behind a reverse proxy that would do the translation. Since advantages remain for delegating TLS to another layer in the stack, instructions for doing so continue to be included in our documentation, such as: * <a id="stunnel" href="./server/any/stunnel.md">Serving via stunnel</a> * <a id="althttpd" href="./server/any/althttpd.md">Serving via stunnel + althttpd</a> * <a id="nginx" href="./server/debian/nginx.md#tls">Serving via SCGI with nginx on Debian</a> <h2 id="enforcing">Enforcing TLS Access</h2> To use TLS encryption in cloning and syncing to a remote Fossil repository, be sure to use the <tt>https:</tt> URI scheme in <tt>clone</tt> and <tt>sync</tt> commands. If your server is configured to serve the repository via both HTTP and HTTPS, it's easy to accidentally use unencrypted HTTP if you forget the all-important 's'. As of Fossil 2.9, using an <tt>http://</tt> URI with <tt>fossil clone</tt> or <tt>sync</tt> on a site that forwards to HTTPS will cause Fossil to remember the secure URL. However, there's a [https://en.wikipedia.org/wiki/Trust_on_first_use | TOFU problem] with this: it's still better to use <tt>https://</tt> from the start. As of Fossil 2.8, there is a setting in the Fossil UI under Admin → Access called "Redirect to HTTPS," which is set to "Off" by default. Changing this only affects web UI access to the Fossil repository. It doesn't affect clones and syncs done via the <tt>http</tt> URI scheme. In Fossil 2.7 and earlier, there was a much weaker form of this setting affecting the <tt>/login</tt> page only. If you're using this setting, you should migrate to the new setting as soon as possible, because the old setting allows multiple ways of defeating it. <b id="rloop">WARNING:</b> Enabling HTTPS redirects at the Fossil repo level while running Fossil behind an HTTPS proxy can result in an infinite redirect loop. It happens when the proxy mechanism presents "<tt>http</tt>" URIs to Fossil, so Fossil issues a redirect, so the browser fetches the page again, causing Fossil to see an "<tt>http</tt>" URI again, so it issues a redirect...'round and 'round it goes until the web browser detects it's in a redirect loop and gives up. This problem prevents you from getting back into the Admin UI to fix it, but there are several ways to fix it: # <b>Reset via CLI.</b> You can turn the setting back off from the CLI with the command "<tt>fossil -R /path/to/repo.fossil set redirect-to-https 0</tt>". (Currently doesn't work.) # <b>Backup first.</b> This setting is stored in the Fossil repository, so if you make a backup first <i>on the server</i>, you can restore the repo file if enabling this feature creates a redirect loop. # <b>Download, fix, and restore.</b> You can copy the remote repository file down to a local machine, use <tt>fossil ui</tt> to fix the setting, and then upload it to the repository server again. It's best to enforce TLS-only access at the front-end proxy level anyway. It not only avoids the problem entirely, it can be significantly more secure. The [./server/debian/nginx.md#tls | nginx-on-Debian proxy guide] shows one way to achieve this. <h2>Terminology Note</h2> This document is called <tt>ssl.wiki</tt> for historical reasons. The TLS protocol was originally called SSL, and it went through several revisions before being replaced by TLS. Years before this writing, SSL finally became entirely obsolete due to weaknesses in the protocol fixed in the later TLS series of protocols. Some people still use the term "SSL" when they actually mean "TLS," but in the Fossil project, we always use "TLS" except when we must preserve some sort of historical compatibility, as with this document's name in order to avoid broken external URLs. The Fossil TLS-related settings also often use "<tt>ssl</tt>" in their names for the same reason. This series of protocols is also called "HTTPS" after the URI scheme used to specify "HTTP over TLS." |
Changes to www/stats.wiki.
1 | <title>Fossil Performance</title> | < | | > | | < > | | > > > | > > | > > > | > | | | | | | | | | | < | | | | | | | | | | < | | | | < | | | | | | | | < | | | | < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | | | | | | | | | > > | | | 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 | <title>Fossil Performance</title> 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 2018-06-04.) Explanation and analysis follows the table. <table> <tr> <th>Project</th> <th>Number Of Artifacts</th> <th>Number Of Check-ins</th> <th>Project Duration<br>(as of 2018-06-04)</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>77492 <td>20686 <td>6580 days<br>18.02 years <td>5.6 GB <td>70.0 MB <td>80:1 <td>51.1 MB </tr> <tr align="center"> <td>[http://core.tcl.tk/tcl/timeline | TCL] <td>161991 <td>23146 <td>7375 days<br>20.19 years <td>8.0 GB <td>222.0 MB <td>36:1 <td>150.5 MB </tr> <tr align="center"> <td>[/timeline | Fossil] <td>39148 <td>11266 <td>3971 days<br>10.87 years <td>3.8 GB <td>42.0 MB <td>90:1 <td>27.4 MB </tr> <tr align="center"> <td>[http://www.sqlite.org/slt/timeline | SLT] <td>2384 <td>169 <td>3474 days<br>9.51 years <td>2.1 GB <td>145.9 MB <td>14:1 <td>143.4 MB </tr> <tr align="center"> <td>[http://www.sqlite.org/th3.html | TH3] <td>12406 <td>3718 <td>3539 days<br>9.69 years <td>544 MB <td>18.0 MB <td>30:1 <td>14.7 MB </tr> <tr align="center"> <td>[http://www.sqlite.org/docsrc/timeline | SQLite Docs] <td>8752 <td>2783 <td>3857 days<br>10.56 years <td>349.9 MB <td>16.3 MB <td>21:1 <td>13.57 MB </tr> </table> <h2>Measured Attributes</h2> In Fossil, every version of every file, every wiki page, every change to every ticket, and every check-in is a separate "artifact". One way to think of a Fossil project is as a bag of artifacts. Of course, there is a lot more than this going on in Fossil. Many of the artifacts have meaning and are related to other artifacts. But at a low level (for example when synchronizing two instances of the same project) the only thing that matters is the unordered collection of artifacts. In fact, one of the key characteristics of Fossil is that the entire project history can be reconstructed simply by scanning the artifacts in an arbitrary order. The number of check-ins is the number of times that the "commit" command has been run. A single check-in might change a 3 or 4 files, or it might change dozens or hundreds of files. Regardless of the number of files 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 data points 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 80: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. |
Added www/style.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 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 | <title>Coding Style</title> Fossil source code should following the style guidelines below. <em> The Fossil source tree includes a few files taken from external sources (examples: [https://github.com/antirez/linenoise|linenoise] and [http://zlib.net/|zLib]) and this externally sourced code might not comply with these style guidelines. </em> <b>1. General points</b>: <ol> <li value=10> No line of code exceeds 80 characters in length. (Occasional exceptions are made for HTML text on @-lines.) <li> There are no tab characters. <li> Line terminators are \n only. Do not use a \r\n line terminator. <li> 2-space indentation is used. Remember: No tabs. <li> Comments contain no spelling or grammatical errors. (Abbreviations and sentence fragments are acceptable when trying to fit a comment on a single line as long as the meaning is clear.) <li> The tone of comments is professional and courteous. Comments contain no profanity, obscenity, or innuendo. <li> All C-code conforms to ANSI C-89. Three well-defined existing exceptions are: <ol type="a"> <li> -Wno-overlength-strings: The Fossil build system converts (some of the) source code comments into strings, which may exceed the 509 character limit defined by ANSI. (example: bld/page_index.h) <li> -Wno-long-long: Fossil uses the 'long long' integer type, which is not strictly ANSI C-89 (defined in C99). The use of 'long long' resolves many problems with 64-bit arithmetics, especially on 32-bit machines. (http_ssl.c, sha3.c, shell.c, util.c) <li> alloca(): By default, sqlite3.c is compiled with the -DSQLITE_USE_ALLOCA flag to use the alloca() function. alloca() is not considered ANSI C, and normally not recommended due to portability issues, but performance and/or memory consumption improvement may be a stronger argument in favor of its usage. (sqlite3.c) </ol> <li> All comments and identifiers are in English. <li> 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.) </ol> <b>2. C preprocessor macros</b>: <ol> <li value=20> The purpose of every preprocessor macros is clearly explained in a comment associated with its definition. <li> Every preprocessor macro is used at least once. <li> The names of preprocessor macros clearly reflect their use. <li> Assumptions about the relative values of related macros are verified by asserts. Example: <tt>assert(READ_LOCK+1==WRITE_LOCK);</tt> </ol> <b>3. Function header comments</b>: <ol> <li value=30> Every function has a header comment describing the purpose and use of the function. <li> 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. <li> Functions that perform dynamic memory allocation (either directly or indirectly via subfunctions) say so in their header comments. </ol> <b>4. Function bodies</b>: <ol> <li value=40> The name of a function clearly reflects its purpose. <li> Automatic variables are small, not large objects or arrays. Avoid excessive stack usage. <li> The check-list items for functions also apply to major subsections within a function. <li> All code subblocks are enclosed in {...}. <li> <b>assert() macros are used as follows</b>: <ol type="a"> <li> Function preconditions are clearly stated and verified by asserts. <li> Invariants are identified by asserts. </ol> </ol> <b>5. Class (struct) declarations</b>: <ol> <li value=50> The purpose and use of every class (a.k.a. structure) is clearly defined in the header comment of its declaration. <li> The purpose and use of every class member is clearly defined either in the header comment of the class declaration or when the member is declared or both. <li> The names of class members clearly reflect their use. <li> Invariants for classes are clearly defined. </ol> <b>6. Variables and class instances</b>: <ol> <li value=60> The purpose and use of every variable is defined by a comment at the variable definition. <li> The names of variables clearly reflect their use. <li> Related variables have related names. (ex: aSavepoint and nSavepoint.) <li> Variables have minimum practical scope. <li> Automatic variables are small, not large objects or arrays. <li> Constants are "const". <li> Invariants on variables or groups of variables are defined and tested by asserts. <li> When a variable that refers to the same value is used within multiple scopes, the same name is used in all cases. <li> When variables refer to different values, different names are used even when the names are in different scopes. <li> Variable names with wide scope are sufficiently distinctive to allow searching for them using grep. </ol> |
Changes to www/sync.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 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 | <title>The Fossil Sync Protocol</title> This document describes the wire protocol used to synchronize content between two Fossil repositories. <h2>1.0 Overview</h2> The global state of a fossil repository consists of an unordered [./fileformat.wiki|collection of artifacts]. Each artifact is identified by a cryptographic hash of its content, expressed as a lower-case hexadecimal string. Synchronization is the process of sharing artifacts between repositories so that all repositories have copies of all artifacts. Because artifacts are unordered, the order in which artifacts are received is unimportant. It is assumed that the hash names of artifacts are unique - that every artifact has a different hash. To a first approximation, synchronization proceeds by sharing lists of hashes for available artifacts, then sharing the content of artifacts whose names are missing from one side or the other of the connection. In practice, a repository might contain millions of artifacts. The list of hash names for this many artifacts can be large. So optimizations are employed that usually reduce the number of hashes that need to be shared to a few dozen. Each repository also has local state. The local state determines the web-page formatting preferences, authorized users, ticket formats, and similar information that varies from one repository to another. The local state is not usually transferred during a sync. Except, some local state is transferred during a [/help?cmd=clone|clone] in order to initialize the local state of the new repository. Also, an administrator can sync local state using the [/help?cmd=configuration|config push] and [/help?cmd=configuration|config pull] commands. <h3 id="crdt">1.1 Conflict-Free Replicated Datatypes</h3> The "bag of artifacts" data model used by Fossil is apparently an implementation of a particular [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|Conflict-Free Replicated Datatype (CRDT)] called a "G-Set" or "Grow-only Set". The academic literature on CRDTs only began to appear in about 2011, and Fossil predates that research by at least 4 years. But it is nice to know that theorists have now proven that the underlying data model of Fossil can provide strongly-consistent replicas using only peer-to-peer communication and without any kind of central authority. If you are already familiar with CRDTs and were wondering if Fossil used them, the answer is "yes". We just don't call them by that name. <h2 id="transport">2.0 Transport</h2> All communication between client and server is via HTTP requests. The server is listening for incoming HTTP requests. The client issues one or more HTTP requests and receives replies for each request. The server might be running as an independent server using the [/help?cmd=server|"fossil server" command], or it might be launched from inetd or xinetd using the ["fossil http" command|/help?cmd=http]. Or the server might be [./server/any/cgi.md|launched from CGI] or from [./server/any/scgi.md|SCGI]. (See "[./server/|How To Configure A Fossil Server]" for details.) The specifics of how the server listens for incoming HTTP requests is immaterial to this protocol. The important point is that the server is listening for requests and the client is the issuer of the requests. A single [/help?cmd=push|push], [/help?cmd=pull|pull], or [/help?cmd=sync|sync] might involve multiple HTTP requests. The client maintains state between all requests. But on the server side, each request is independent. The server does not preserve any information about the client from one request to the next. Note: Throughout this article, we use the terms "server" and "client" to represent the listener and initiator of the interaction, respectively. Nothing in this protocol requires that the server actually be a back-room processor housed in a datacenter, nor does the client need to be a desktop or handheld device. For the purposes of this article "client" simply means the repository that initiates the conversation and "server" is the repository that responds. Nothing more. <h4 id="https">2.0.1 HTTPS Transport</h4> HTTPS differs from HTTP only in that the HTTPS protocol is encrypted as it travels over the wire. The underlying protocol is the same. This document describes only the underlying, unencrypted messages that go client to server and back to client. Whether or not those messages are encrypted does not come into play in this document. Fossil includes built-in [./ssl-server.md|support for HTTPS encryption] in both client and server. <h4 id="ssh">2.0.2 SSH Transport</h4> When doing a sync using an "<code>ssh:…</code>" URL, the same HTTP transport protocol is used. Fossil simply uses [https://en.wikipedia.org/wiki/Secure_Shell|ssh] to start an instance of the [/help?cmd=test-http|fossil test-http] command running on the remote machine. It then sends HTTP requests and gets back HTTP replies over the SSH connection, rather than sending and receiving over an internet socket. To see the specific "ssh" command that the Fossil client runs in order to set up a connection, add either of the the "--httptrace" or "--sshtrace" options to the "fossil sync" command line. This method is dependent on the remote <var>PATH</var> set by the SSH daemon, which may not be the same as your interactive shell's <var>PATH</var> on that same server. It is common to find <var>$HOME/bin</var> in the latter but not the former, for instance, leading to failures to sync over <code>ssh:…</code> URLs when you install the <code>fossil</code> binary in a nonstandard location, as with <verbatim>./configure --prefix=$HOME && make install</verbatim> The simpler of the two solutions to this problem is to install Fossil where sshd expects to find it, but when that isn't an option, you can instead give a URL like this: <verbatim>fossil clone ssh://myserver.example.com/path/to/repo.fossil?fossil=/home/me/bin/fossil</verbatim> That gives the local Fossil instance the absolute path to the binary on the remote machine for use when calling that Fossil instance through the SSH tunnel. <h4 id="file">2.0.3 FILE Transport</h4> When doing a sync using a "file:..." URL, the same HTTP protocol is still used. But instead of sending each HTTP request over a socket or via SSH, the HTTP request is written into a temporary file. The client then invokes the [/help?cmd=http|fossil http] command in a subprocess to process the request and and generate a reply. The client then reads the HTTP reply out of a temporary file on disk, and deletes the two temporary files. To see the specific "fossil http" command that is run in order to implement the "file:" transport, add the "--httptrace" option to the "fossil sync" command. <h3 id="srv-id">2.1 Server Identification</h3> The server is identified by a URL argument that accompanies the push, pull, or sync command on the client. (As a convenience to users, the URL can be omitted on the client command and the same URL from the most recent push, pull, or sync will be reused. This saves typing in the common case where the client does multiple syncs to the same server.) The client modifies the URL by appending the method name "<b>/xfer</b>" to the end. For example, if the URL specified on the client command line is <pre>https://fossil-scm.org/fossil</pre> Then the URL that is really used to do the synchronization will be: <pre>https://fossil-scm.org/fossil/xfer</pre> <h3 id="req-format">2.2 HTTP Request Format</h3> The client always sends a POST request to the server. The general format of the POST request is as follows: <pre> POST /fossil/xfer HTTP/1.0 Host: fossil-scm.hwaci.com:80 Content-Type: application/x-fossil Content-Length: 4216 </pre> <i><pre>content...</pre></i> In the example above, the pathname given after the POST keyword on the first line is a copy of the URL pathname. The Host: parameter is also taken from the URL. The content type is always either "application/x-fossil" or "application/x-fossil-debug". The "x-fossil" content type is the default. The only difference is that "x-fossil" content is compressed using zlib whereas "x-fossil-debug" is sent uncompressed. A typical reply from the server might look something like this: <pre> HTTP/1.0 200 OK Date: Mon, 10 Sep 2007 12:21:01 GMT Connection: close Cache-control: private Content-Type: application/x-fossil; charset=US-ASCII Content-Length: 265 </pre> <i><pre>content...</pre></i> The content type of the reply is always the same as the content type of the request. <h2 id="content">3.0 Fossil Synchronization Content</h2> 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. <h3 id="lines">3.1 Line-oriented Format</h3> 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. 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. <h3 id="login">3.2 Login Cards</h3> Every message from client to server begins with one or more login cards. Each login card has the following format: <pre><b>login</b> <i>userid nonce signature</i></pre> The userid is the name of the user that is requesting service from the server. The nonce is the SHA1 hash of the remainder of the message - all text that follows the newline character that terminates the login card. The signature is the SHA1 hash of the concatenation of the nonce and the users password. For each login card, the server looks up the user and verifies that the nonce matches the SHA1 hash of the remainder of the message. It then checks the signature hash to make sure the signature matches. If everything checks out, then the client is granted all privileges of the specified user. Privileges are cumulative. There can be multiple successful login cards. The session privilege is the union of all privileges from all login cards. <h3 id="file">3.3 File Cards</h3> Artifacts are transferred using either "file" cards, or "cfile" or "uvfile" cards. The name "file" card comes from the fact that most artifacts correspond to files that are under version control. The "cfile" name is an abbreviation for "compressed file". The "uvfile" name is an abbreviation for "unversioned file". <h4 id="ordinary-fc">3.3.1 Ordinary File Cards</h4> 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_format.wiki|delta] from some other artifact. <pre> <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i> <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i> </pre> File cards are followed by in-line "payload" data. The content of the artifact or the artifact delta is the first <i>size</i> bytes of the x-fossil content that immediately follow the newline that terminates the file card. 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 name hash for 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_format.wiki|delta] and the second argument is the ID of another artifact that is the source of the delta. 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. <h4 id="compressed-fc">3.3.2 Compressed File Cards</h4> 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. 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. Cfile cards come in two different formats depending on whether the artifact is sent directly or as a delta from some other artifact. <pre> <b>cfile</b> <i>artifact-id usize csize</i> <b>\n</b> <i>content</i> <b>cfile</b> <i>artifact-id delta-artifact-id usize csize</i> <b>\n</b> <i>content</i> </pre> 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 name hash for 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. 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. <h4 id="private">3.3.3 Private artifacts</h4> "Private" content consist of artifacts that are not normally synced. However, private content will be synced when the the [/help?cmd=sync|fossil sync] command includes the "--private" option. Private content is marked by a "private" card: <pre><b>private</b></pre> The private card has no arguments and must directly precede a file card that contains the private content. <h4 id="uv-fc">3.3.4 Unversioned File Cards</h4> Unversioned content is sent in both directions (client to server and server to client) using "uvfile" cards in the following format: <pre><b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i></pre> The <i>name</i> field is the name of the unversioned file. The <i>mtime</i> is the last modification time of the file in seconds since 1970. The <i>hash</i> field is the hash of the content for the unversioned file, or "<b>-</b>" for deleted content. The <i>size</i> field is the (uncompressed) size of the content in bytes. The <i>flags</i> field is an integer which is interpreted as an array of bits. The 0x0004 bit of <i>flags</i> indicates that the <i>content</i> is to be omitted. The content might be omitted if it is too large to transmit, or if the sender merely wants to update the modification time of the file without changing the files content. The <i>content</i> is the (uncompressed) content of the file. The receiver should only accept the uvfile card if the hash and size match the content and if the mtime is newer than any existing instance of the same file held by the receiver. The sender will not normally transmit a uvfile card unless all these constraints are true, but the receiver should double-check. A server will only accept uvfile cards if the login user has the "y" write-unversioned permission. Servers send uvfile cards in response to uvgimme cards received from the client. Clients send uvfile cards when they determine that the server needs the content based on uvigot cards previously received from the server. <h3 id="push" name="pull">3.4 Push and Pull Cards</h3> 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: <pre> <b>push</b> <i>servercode projectcode</i> <b>pull</b> <i>servercode projectcode</i> </pre> 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. 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. The <i>servercode</i> argument is currently unused. <h3 id="clones">3.5 Clone Cards</h3> A clone card works like a pull card in that it is sent from client to server in order to tell the server that the client wants to pull content. The clone card comes in two formats. Older clients use the no-argument format and newer clients use the two-argument format. <pre> <b>clone</b> <b>clone</b> <i>protocol-version sequence-number</i> </pre> <h4>3.5.1 Protocol 3</h4> 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 <h4>3.5.2 Protocol 2</h4> 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. The server will also send a single "clone_seqno" card to the client so that the client can know where the server left off. <pre> <b>clone_seqno</b> <i>sequence-number</i> </pre> 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. 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. <h4>3.5.3 Legacy Protocol</h4> 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. The legacy protocol works well for smaller repositories (50MB with 50,000 artifacts) but is too slow and unwieldy for larger repositories. The version 2 protocol is an effort to improve performance. Further performance improvements with higher-numbered clone protocols are possible in future versions of Fossil. <h3 id="igot">3.6 Igot Cards</h3> An igot card can be sent from either client to server or from server to client in order to indicate that the sender holds a copy of a particular artifact. The format is: <pre> <b>igot</b> <i>artifact-id</i> ?<i>flag</i>? </pre> The first argument of the igot card is the ID of the artifact that the sender possesses. The receiver of an igot card will typically check to see if it also holds the same artifact and if not it will request the artifact using a gimme card in either the reply or in the next message. If the second argument exists and is "1", then the artifact identified by the first argument is private on the sender and should be ignored unless a "--private" [/help?cmd=sync|sync] is occurring. The name "igot" comes from the English slang expression "I got" meaning "I have". <h4>3.6.1 Unversioned Igot Cards</h4> Zero or more "uvigot" cards are sent from server to client when synchronizing unversioned content. The format of a uvigot card is as follows: <pre> <b>uvigot</b> <i>name mtime hash size</i> </pre> The <i>name</i> argument is the name of an unversioned file. The <i>mtime</i> is the last modification time of the unversioned file in seconds since 1970. The <i>hash</i> is the SHA1 or SHA3-256 hash of the unversioned file content, or "<b>-</b>" if the file has been deleted. The <i>size</i> is the uncompressed size of the file in bytes. When the server sees a "pragma uv-hash" card for which the hash does not match, it sends uvigot cards for every unversioned file that it holds. The client will use this information to figure out which unversioned files need to be synchronized. The server might also send a uvigot card when it receives a uvgimme card but its reply message size is already oversized and hence unable to hold the usual uvfile reply. When a client receives a "uvigot" card, it checks to see if the file needs to be transferred from client to server or from server to client. If a client-to-server transmission is needed, the client schedules that transfer to occur on a subsequent HTTP request. If a server-to-client transfer is needed, then the client sends a "uvgimme" card back to the server to request the file content. <h3 id="gimme">3.7 Gimme Cards</h3> A gimme card is sent from either client to server or from server to client. The gimme card asks the receiver to send a particular artifact back to the sender. The format of a gimme card is this: <pre> <b>gimme</b> <i>artifact-id</i> </pre> The argument to the gimme card is the ID of the artifact that the sender wants. The receiver will typically respond to a gimme card by sending a file card in its reply or in the next message. The "gimme" name means "give me". The imperative "give me" is pronounced as if it were a single word "gimme" in some dialects of English (including the dialect spoken by the original author of Fossil). <h4>3.7.1 Unversioned Gimme Cards</h4> Sync synchronizing unversioned content, the client may send "uvgimme" cards to the server. A uvgimme card requests that the server send unversioned content to the client. The format of a uvgimme card is as follows: <pre> <b>uvgimme</b> <i>name</i> </pre> The <i>name</i> is the name of the unversioned file found on the server that the client would like to have. When a server sees a uvgimme card, it normally responses with a uvfile card, though it might also send another uvigot card if the HTTP reply is already oversized. <h3 id="cookie">3.8 Cookie Cards</h3> A cookie card can be used by a server to record a small amount of state information on a client. The server sends a cookie to the client. The client sends the same cookie back to the server on its next request. The cookie card has a single argument which is its payload. <pre> <b>cookie</b> <i>payload</i> </pre> The client is not required to return the cookie to the server on its next request. Or the client might send a cookie from a different server on the next request. So the server must not depend on the cookie and the server must structure the cookie payload in such a way that it can tell if the cookie it sees is its own cookie or a cookie from another server. (Typically the server will embed its servercode as part of the cookie.) <h3 id="reqconfig">3.9 Request-Configuration Cards</h3> A request-configuration or "reqconfig" card is sent from client to server in order to request that the server send back "configuration" data. "Configuration" data is information about users or website appearance or other administrative details which are not part of the persistent and versioned state of the project. For example, the "name" of the project, the default Cascading Style Sheet (CSS) for the web-interface, and the project logo displayed on the web-interface are all configuration data elements. The reqconfig card is normally sent in response to the "fossil configuration pull" command. The format is as follows: <pre> <b>reqconfig</b> <i>configuration-name</i> </pre> As of 2018-06-04, the configuration-name must be one of the following values: <table border=0 align="center"> <tr><td valign="top"> <ul> <li> css <li> header <li> footer <li> details <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 <li> adunit <li> adunit-omit-if-admin <li> adunit-omit-if-user <ul></td><td valign="top"><ul> <li> th1-docs <li> th1-hooks <li> th1-setup <li> tcl <li> tcl-setup <li> project-name <li> short-project-name <li> project-description <li> index-page <li> manifest <li> binary-glob <li> clean-glob <li> ignore-glob <li> keep-glob <li> crlf-glob <ul></td><td valign="top"><ul> <li> crnl-glob <li> encoding-glob <li> empty-dirs <li> <s title="removed 2020-08, version 2.12.1">allow-symlinks</s> <li> dotfiles <li> parent-project-code <li> parent-projet-name <li> hash-policy <li> mv-rm-files <li> ticket-table <li> ticket-common <li> ticket-change <li> ticket-newpage <li> ticket-viewpage <li> ticket-editpage <ul></td><td valign="top"><ul> <li> ticket-reportlist <li> ticket-report-template <li> ticket-key-template <li> ticket-title-expr <li> ticket-closed-expr <li> xfer-common-script <li> xfer-push-script <li> xfer-commit-script <li> xfer-ticket-script <li> @reportfmt <li> @user <li> @concealed <li> @shun </ul></td></tr> </table> New configuration-names are likely to be added in future releases of Fossil. If the server receives a configuration-name that it does not understand, the entire reqconfig card is silently ignored. The reqconfig card might also be ignored if the user lacks sufficient privilege to access the requested information. The configuration-names that begin with an alphabetic character refer to values in the "config" table of the server database. For example, the "logo-image" configuration item refers to the project logo image that is configured on the Admin page of the [./webui.wiki | web-interface]. The value of the configuration item is returned to the client using a "config" card. If the configuration-name begins with "@", that refers to a class of values instead of a single value. The content of these configuration items is returned in a "config" card that contains pure SQL text that is intended to be evaluated by the client. The @user and @concealed configuration items contain sensitive information and are ignored for clients without sufficient privilege. <h3 id="config">3.10 Configuration Cards</h3> A "config" card is used to send configuration information from client to server (in response to a "fossil configuration push" command) or from server to client (in response to a "fossil configuration pull" or "fossil clone" command). The format is as follows: <pre> <b>config</b> <i>configuration-name size</i> <b>\n</b> <i>content</i> </pre> The server will only accept a config card if the user has "Admin" privilege. A client will only accept a config card if it had sent a corresponding reqconfig card in its request. The content of the configuration item is used to overwrite the corresponding configuration data in the receiver. <h3 id="pragma">3.11 Pragma Cards</h3> The client may try to influence the behavior of the server by issuing a pragma card: <pre> <b>pragma</i> <i>name value...</i> </pre> The "pragma" card has at least one argument which is the pragma name. The pragma name defines what the pragma does. A pragma might have zero or more "value" arguments depending on the pragma name. New pragma names may be added to the protocol from time to time in order to enhance the capabilities of Fossil. Unknown pragmas are silently ignored, for backwards compatibility. The following are the known pragma names as of 2019-06-30: <ol> <li><b>send-private</b> The send-private pragma instructs the server to send all of its private artifacts to the client. The server will only obey this request if the user has the "x" or "Private" privilege. <li><b>send-catalog</b> The send-catalog pragma instructs the server to transmit igot cards for every known artifact. This can help the client and server to get back in synchronization after a prior protocol error. The "--verily" option to the [/help?cmd=sync|fossil sync] command causes the send-catalog pragma to be transmitted. <li><b>uv-hash</b> <i>HASH</i> The uv-hash pragma is sent from client to server to provoke a synchronization of unversioned content. The <i>HASH</i> is a SHA1 hash of the names, modification times, and individual hashes of all unversioned files on the client. If the unversioned content hash from the client does not match the unversioned content hash on the server, then the server will reply with either a "pragma uv-push-ok" or "pragma uv-pull-only" card followed by one "uvigot" card for each unversioned file currently held on the server. The collection of "uvigot" cards sent in response to a "uv-hash" pragma is called the "unversioned catalog". The client will used the unversioned catalog to figure out which files (if any) need to be synchronized between client and server and send appropriate "uvfile" or "uvgimme" cards on the next HTTP request. If a client sends a uv-hash pragma and does not receive back either a uv-pull-only or uv-push-ok pragma, that means that the content on the server exactly matches the content on the client and no further synchronization is required. <li><b>uv-pull-only</b></i> A server sends the uv-pull-only pragma to the client in response to a uv-hash pragma with a mismatched content hash argument. This pragma indicates that there are differences in unversioned content between the client and server but that content can only be transferred from server to client. The server is unwilling to accept content from the client because the client login lacks the "write-unversioned" permission. <li><b>uv-push-ok</b></i> A server sends the uv-push-ok pragma to the client in response to a uv-hash pragma with a mismatched content hash argument. This pragma indicates that there are differences in unversioned content between the client and server and that content can be transferred in either direction. The server is willing to accept content from the client because the client login has the "write-unversioned" permission. <li><b>ci-lock</b> <i>CHECKIN-HASH CLIENT-ID</i> A client sends the "ci-lock" pragma to the server to indicate that it is about to add a new check-in as a child of the CHECKIN-HASH check-in and on the same branch as CHECKIN-HASH. If some other client has already indicated that it was also trying to commit against CHECKIN-HASH, that indicates that a fork is about to occur, and the server will reply with a "ci-lock-fail" pragma (see below). Check-in locks automatically expire when the check-in actually occurs, or after a timeout (currently one minute but subject to change). <li><b>ci-lock-fail</b> <i>LOGIN MTIME</i> When a server receives two or more "ci-lock" pragma messages for the same check-in but from different clients, the second a subsequent ci-lock will provoke a ci-lock-fail pragma in the reply to let the client know that it if continues with the check-in it will likely generate a fork. The LOGIN and MTIME arguments are intended to provide information to the client to help it generate a more useful error message. <li><b>ci-unlock</b> <i>CLIENT-ID</i> A client sends the "ci-unlock" pragma to the server after a successful commit. This instructs the server to release any lock on any check-in previously held by that client. The ci-unlock pragma helps to avoid false-positive lock warnings that might arise if a check-in is aborted and then restarted on a branch. </ol> <h3 id="comment">3.12 Comment Cards</h3> Any card that begins with "#" (ASCII 0x23) is a comment card and is silently ignored. <h3 id="error">3.13 Message and Error Cards</h3> If the server discovers anything wrong with a request, it generates an error card in its reply. When the client sees the error card, it displays an error message to the user and aborts the sync operation. An error card looks like this: <pre> <b>error</b> <i>error-message</i> </pre> The error message is English text that is encoded in order to be a single token. A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash (ASCII 0x5C) is represented as two backslashes "\\". Apart from space and newline, no other whitespace characters nor any unprintable characters are allowed in the error message. The server can also send a message card that also prints a message on the client console, but which is not an error: <pre> <b>message</b> <i>message-text</i> </pre> The message-text uses the same format as an error message. <h3 id="unknown">3.14 Unknown Cards</h3> If either the client or the server sees a card that is not described above, then it generates an error and aborts. <h2 id="phantoms" name="clusters">4.0 Phantoms And Clusters</h2> 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. 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. 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. 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. Any artifact that does not match the specifications of a cluster exactly is not a cluster. There must be no extra whitespace in the artifact. There must be one or more M cards. There must be a single Z card with a correct MD5 checksum. And all cards must be in strict lexicographical order. <h3 id="unclustered">4.1 The Unclustered Table</h3> Every repository maintains a table named "<b>unclustered</b>" which records the identity of every artifact and phantom it holds that is not mentioned in a cluster. The entries in the unclustered table can be thought of as leaves on a tree of artifacts. Some of the unclustered artifacts will be other clusters. Those clusters may contain other clusters, which might contain still more clusters, and so forth. Beginning with the artifacts in the unclustered table, one can follow the chain of clusters to find every artifact in the repository. <h2 id="strategies">5.0 Synchronization Strategies</h2> <h3 id="pull-strategy">5.1 Pull</h3> A typical pull operation proceeds as shown below. Details of the actual implementation may very slightly but the gist of a pull is captured in the following steps: <ol> <li>The client sends login and pull cards. <li>The client sends a cookie card if it has previously received a cookie. <li>The client sends gimme cards for every phantom that it holds. <hr> <li>The server checks the login password and rejects the session if |
︙ | ︙ | |||
397 398 399 400 401 402 403 | <li>The client adds the content of file cards to its repository. <li>The client creates a phantom for every igot card in the server reply that mentions an artifact that the client does not possess. <li>The client creates a phantom for the delta source of file cards when the delta source is an artifact that the client does not possess. </ol> | | | | | | | | | | | | | 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 | <li>The client adds the content of file cards to its repository. <li>The client creates a phantom for every igot card in the server reply that mentions an artifact that the client does not possess. <li>The client creates a phantom for the delta source of file cards when the delta source is an artifact that the client does not possess. </ol> These ten steps represent a single HTTP round-trip request. The first three steps are the processing that occurs on the client to generate the request. The middle four steps are processing that occurs on the server to interpret the request and generate a reply. And the last three steps are the processing that the client does to interpret the reply. During a pull, the client will keep sending HTTP requests until it holds all artifacts that exist on the server. Note that the server tries to limit the size of its reply message to something reasonable (usually about 1MB) so that it might stop sending file cards as described in step (6) if the reply becomes too large. Step (5) is the only way in which new clusters can be created. By only creating clusters on the server, we hope to minimize the amount of overlap between clusters in the common configuration where there is a single server and many clients. The same synchronization protocol will continue to work even if there are multiple servers or if servers and clients sometimes change roles. The only negative effects of these unusual arrangements is that more than the minimum number of clusters might be generated. <h3 id="push-stragegy">5.2 Push</h3> A typical push operation proceeds roughly as shown below. As with a pull, the actual implementation may vary slightly. <ol> <li>The client sends login and push cards. <li>The client sends file cards for any artifacts that it holds that have never before been pushed - artifacts that come from local check-ins. <li>If this is the second or later cycle in a push, then the client sends file cards for any gimme cards that the server sent |
︙ | ︙ | |||
449 450 451 452 453 454 455 | it does not possess. <li>The server issues gimme cards for all phantoms. <hr> <li>The client remembers the gimme cards from the server so that it can generate file cards in reply on the next cycle. </ol> | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | it does not possess. <li>The server issues gimme cards for all phantoms. <hr> <li>The client remembers the gimme cards from the server so that it can generate file cards in reply on the next cycle. </ol> As with a pull, the steps of a push operation repeat until the server knows all artifacts that exist on the client. Also, as with pull, the client attempts to keep the size of the request from growing too large by suppressing file cards once the size of the request reaches 1MB. <h3 id="sync-strategy">5.3 Sync</h3> A sync is just a pull and a push that happen at the same time. The first three steps of a pull are combined with the first five steps of a push. Steps (4) through (7) of a pull are combined with steps (5) through (8) of a push. And steps (8) through (10) of a pull are combined with step (9) of a push. <h3 id="uv-strategy">5.4 Unversioned File Sync</h3> "Unversioned files" are files held in the repository where only the most recent version of the file is kept rather than the entire change history. Unversioned files are intended to be used to store ephemeral content, such as compiled binaries of the most recent release. Unversioned files are identified by name and timestamp (mtime). Only the most recent version of each file (the version with the largest mtime value) is retained. Unversioned files are synchronized using the [/help?cmd=unversioned|fossil unversioned sync] command. A schematic of an unversioned file synchronization is as follows: <ol> <li>The client sends a "pragma uv-hash" card to the server. The argument to the uv-hash pragma is a hash of all filesnames, mtimes, and content hashes for the unversioned files held by the client. <hr> <li>If the unversioned content hash from the client matches the unversioned content hash on the server, then nothing needs to be done and the server no-ops. But if the hashes are different, then the server replies with either a uv-pull-only or a uv-push-ok pragma followed by uvigot cards for all unversioned files held on the server. <hr> <li>The client examines the uvigot cards received from the server and determines which unversioned files need to be exchanged in order to bring the client and server into synchronization. The client then sends appropriate "uvgimme" or "uvfile" cards back to the server. <hr> <li>The server updates its unversioned file store with received "uvfile" cards and answers "uvgimme" cards with "uvfile" cards in its reply. </ol> The last two steps might be repeated multiple times if there is more unversioned content to be transferred than will fit comfortably in a single HTTP request. <h2 id="summary">6.0 Summary</h2> Here are the key points of the synchronization protocol: <ol> <li>The client sends one or more PUSH HTTP requests to the server. The request and reply content type is "application/x-fossil". <li>HTTP request content is compressed using zlib. <li>The content of request and reply consists of cards with one card per line. <li>Card formats are: <ul> <li> <b>login</b> <i>userid nonce signature</i> <li> <b>push</b> <i>servercode projectcode</i> <li> <b>pull</b> <i>servercode projectcode</i> <li> <b>clone</b> <li> <b>clone_seqno</b> <i>sequence-number</i> <li> <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i> <li> <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i> <li> <b>cfile</b> <i>artifact-id size</i> <b>\n</b> <i>content</i> <li> <b>cfile</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i> <li> <b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i> <li> <b>private</b> <li> <b>igot</b> <i>artifact-id</i> ?<i>flag</i>? <li> <b>uvigot</b> <i>name mtime hash size</i> <li> <b>gimme</b> <i>artifact-id</i> <li> <b>uvgimme</b> <i>name</i> <li> <b>cookie</b> <i>cookie-text</i> <li> <b>reqconfig</b> <i>parameter-name</i> <li> <b>config</b> <i>parameter-name size</i> <b>\n</b> <i>content</i> <li> <b>pragma</b> <i>name</i> <i>value...</i> <li> <b>error</b> <i>error-message</i> <li> <b>message</b> <i>text-messate</i> <li> <b>#</b> <i>arbitrary-text...</i> </ul> <li>Phantoms are artifacts that a repository knows exist but does not possess. <li>Clusters are artifacts that contain IDs of other artifacts. <li>Clusters are created automatically on the server during a pull. <li>Repositories keep track of all artifacts that are not named in any cluster and send igot messages for those artifacts. <li>Repositories keep track of all the phantoms they hold and send gimme messages for those artifacts. </ol> <h2 id="troubleshooting">7.0 Troubleshooting And Debugging Hints</h2> If you run the [/help?cmd=sync|fossil sync] command (or [/help?cmd=pull|pull] or [/help?cmd=push|push] or [/help?cmd=clone|clone]) with the --httptrace option, Fossil will keep a copy of each HTTP request and reply in files named: <ul> <li> <tt>http-request-</tt><i>N</i><tt>.txt</tt> <li> <tt>http-reply-</tt><i>N</i><tt>.txt</tt> </ul> In the above, <i>N</i> is an integer that increments with each round-trip. If you are having trouble on the server side, you can run the "[/help?cmd=test-http|fossil test-http]" command in a debugger using one the "http-request-N.txt" files as input and single step through the processing performed by the server. The "--transport-command CMD" option on [/help?cmd=sync|fossil sync] (and similar) causes the external program "CMD" to be used to move the sync message to the server and retrieve the sync reply. The CMD is given three arguments: <ol> <li> The URL of the server <li> The name of a temporary file that contains the output-bound sync protocol text, with the HTTP headers <li> The name of a temporary file into which the CMD should write the reply sync protocol text, again without any HTTP headers </ol> In a complex debugging situation, you can run the command "fossil sync --transport-command ./debugging_script" where "debugging_script" is some script of your own that invokes the anomolous behavior your are trying to debug. |
Added www/tech_overview.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 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 | <title>A Technical Overview of Fossil's Design & Implementation</title> <h2>1.0 Introduction</h2> At its lowest level, a Fossil repository consists of an unordered set of immutable "artifacts". You might think of these artifacts as "files", since in many cases the artifacts are exactly that. But other "structural artifacts" are also included in the mix. These structural artifacts define the relationships between artifacts - which files go together to form a particular version of the project, who checked in that version and when, what was the check-in comment, what wiki pages are included with the project, what are the edit histories of each wiki page, what bug reports or tickets are included, who contributed to the evolution of each ticket, and so forth. This low-level file format is called the "global state" of the repository, since this is the information that is synced to peer repositories using push and pull operations. The low-level file format is also called "enduring" since it is intended to last for many years. The details of the low-level, enduring, global file format are [./fileformat.wiki | described separately]. This article is about how Fossil is currently implemented. Instead of dealing with vague abstractions of "enduring file formats" as the [./fileformat.wiki | other document] does, this article provides some detail on how Fossil actually stores information on disk. <h2>2.0 Three Databases</h2> Fossil stores state information in [http://www.sqlite.org/ | SQLite] database files. SQLite keeps an entire relational database, including multiple tables and indices, in a single disk file. The SQLite library allows the database files to be efficiently queried and updated using the industry-standard SQL language. SQLite updates are atomic, so even in the event of a system crashes or power failure the repository content is protected. Fossil uses three separate classes of SQLite databases: <ol> <li>The configuration database <li>Repository databases <li>Checkout databases </ol> The configuration database is a one-per-user database that holds global configuration information used by Fossil. There is one repository database per project. The repository database is the file that people are normally referring to when they say "a Fossil repository". The checkout database is found in the working checkout for a project and contains state information that is unique to that working checkout. Fossil does not always use all three database files. The web interface, for example, typically only uses the repository database. And the [/help/settings | fossil settings] command only opens the configuration database when the --global option is used. But other commands use all three databases at once. For example, the [/help/status | fossil status] command will first locate the checkout database, then use the checkout database to find the repository database, then open the configuration database. Whenever multiple databases are used at the same time, they are all opened on the same SQLite database connection using SQLite's [http://www.sqlite.org/lang_attach.html | ATTACH] command. The chart below provides a quick summary of how each of these database files are used by Fossil, with detailed discussion following. <table align="center"> <tr valign="bottom"> <th style="text-align:center">Configuration Database<br>"~/.fossil" or<br> "~/.config/fossil.db" <th style="text-align:center">Repository Database<br>"<i>project</i>.fossil" <th style="text-align:center">Checkout Database<br>"_FOSSIL_" or ".fslckout" <tr valign="top"> <td><ul> <li>Global [/help/settings |settings] <li>List of active repositories used by the [/help/all | all] command </ul></td> <td><ul> <li>[./fileformat.wiki | Global state of the project] encoded using delta-compression <li>Local [/help/settings|settings] <li>Web interface display preferences <li>User credentials and permissions <li>Metadata about the global state to facilitate rapid queries </ul></td> <td><ul> <li>The repository database used by this checkout <li>The version currently checked out <li>Other versions [/help/merge | merged] in but not yet [/help/commit | committed] <li>Changes from the [/help/add | add], [/help/delete | delete], and [/help/rename | rename] commands that have not yet been committed <li>"mtime" values and other information used to efficiently detect local edits <li>The "[/help/stash | stash]" <li>Information needed to "[/help/undo|undo]" or "[/help/redo|redo]" </ul></td> </tr> </table> <h3 id="configdb">2.1 The Configuration Database</h3> The configuration database holds cross-repository preferences and a list of all repositories for a single user. The [/help/settings | fossil settings] command can be used to specify various operating parameters and preferences for Fossil repositories. Settings can apply to a single repository, or they can apply globally to all repositories for a user. If both a global and a repository value exists for a setting, then the repository-specific value takes precedence. All of the settings have reasonable defaults, and so many users will never need to change them. But if changes to settings are desired, the configuration database provides 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. <h4 id="configloc">2.1.1 Location Of The Configuration Database</h4> On Unix systems, the configuration database is named by the following algorithm: <table> <tr><td>1. if environment variable FOSSIL_HOME exists <td> → <td>$FOSSIL_HOME/.fossil <tr><td>2. if file ~/.fossil exists <td> →<td>~/.fossil <tr><td>3. if environment variable XDG_CONFIG_HOME exists <td> →<td>$XDG_CONFIG_HOME/fossil.db <tr><td>4. if the directory ~/.config exists <td> →<td>~/.config/fossil.db <tr><td>5. Otherwise<td> →<td>~/.fossil </table> Another way of thinking of this algorithm is the following: * Use "$FOSSIL_HOME/.fossil" if the FOSSIL_HOME variable is defined * Use the XDG-compatible name (usually ~/.config/fossil.db) on XDG systems if the ~/.fossil file does not already exist * Otherwise, use the traditional unix name of "~/.fossil" This algorithm is complex due to the need for historical compatibility. Originally, the database was always just "~/.fossil". Then support for the FOSSIL_HOME environment variable as added. Later, support for the [https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html|XDG-compatible configation filenames] was added. Each of these changes needed to continue to support legacy installations. On Windows, the configuration database is the first of the following for which the corresponding environment variables exist: * %FOSSIL_HOME%/_fossil * %LOCALAPPDATA%/_fossil * %APPDATA%/_fossil * %USERPROFILES%/_fossil * %HOMEDRIVE%%HOMEPATH%/_fossil The second case is the one that usually determines the name. Note that the FOSSIL_HOME environment variable can always be set to determine the location of the configuration database. Note also that the configuration database file itself is called ".fossil" or "fossil.db" on unix but "_fossil" on windows. The [/help?cmd=info|fossil info] command will show the location of the configuration database on a line that starts with "config-db:". <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 the name of the project, with a ".fossil" suffix. For example, the repository database for the self-hosting Fossil repository is called "fossil.fossil" and the repository database for SQLite is called "sqlite.fossil". <h4>2.2.1 Global Project State</h4> The bulk of the repository database (typically 75 to 85%) consists of the artifacts that comprise the [./fileformat.wiki | enduring, global, shared state] of the project. The artifacts are stored as BLOBs, compressed using [http://www.zlib.net/ | zlib compression] and, where applicable, using [./delta_encoder_algorithm.wiki | delta compression]. The combination of zlib and delta compression results in a considerable space savings. For the SQLite project (when this paragraph was last updated on 2020-02-08) the total size of all artifacts is over 7.1 GB but thanks to the combined zlib and delta compression, that content only takes less than 97 MB of space in the repository database, for a compression ratio of about 74:1. The median size of all content BLOBs after delta and zlib compression have been applied is 156 bytes. The median size of BLOBs without compression is 45,312 bytes. Note that the zlib and delta compression is not an inherent part of the Fossil file format; it is just an optimization. The enduring file format for Fossil is the unordered set of artifacts. The compression techniques are just a detail of how the current implementation of Fossil happens to store these artifacts efficiently on disk. All of the original uncompressed and un-delta'd artifacts can be extracted from a Fossil repository database using the [/help/deconstruct | fossil deconstruct] command. Individual artifacts can be extracted using the [/help/artifact | fossil artifact] command. When accessing the repository database using raw SQL and the [/help/sqlite3 | fossil sql] command, the extension function "<tt>content()</tt>" with a single argument which is the SHA1 or SHA3-256 hash of an artifact will return the complete uncompressed content of that artifact. Going the other way, the [/help/reconstruct | fossil reconstruct] command will scan a directory hierarchy and add all files found to a new repository database. The [/help/import | fossil import] command works by reading the input git-fast-export stream and using it to construct corresponding artifacts which are then written into the repository database. <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 not invalidate the repository. Remember that the metadata contains no new information - only information that has been extracted from the canonical artifacts and saved in a more useful form. Hence, when the metadata schema changes, the prior metadata can be discarded and the entire metadata corpus can be recomputed from the canonical artifacts. That is what the [/help/rebuild | fossil rebuild] command does. <h4>2.2.3 Display And Processing Preferences</h4> The repository database also holds information used to help format the display of web pages and configuration settings that override the global configuration settings for the specific repository. All of this information (and the user credentials and privileges too) is local to each repository database; it is not shared between repositories by [/help/sync | fossil sync]. That is because it is entirely reasonable that two different websites for the same project might have completely different display preferences and user communities. One instance of the project might be a fork of the other, for example, which pulls from the other but never pushes and extends the project in ways that the keepers of the other website disapprove of. Display and processing information includes the following: * The name and description of the project * The CSS file, header, and footer used by all web pages * The project logo image * Fields of tickets that are considered "significant" and which are therefore collected from artifacts and made available for display * Templates for screens to view, edit, and create tickets * Ticket report formats and display preferences * Local values for [/help/settings | settings] that override the global values defined in the per-user configuration database. Though the display and processing preferences do not move between repository instances using [/help/sync | fossil sync], this information can be shared between repositories using the [/help/config | fossil config push] and [/help/config | fossil config pull] commands. The display and processing information is also copied into new repositories when they are created using [/help/clone | fossil clone]. <h4>2.2.4 User Credentials And Privileges</h4> Just because two development teams are collaborating on a project and allow push and/or pull between their repositories does not mean that they trust each other enough to share passwords and access privileges. Hence the names and emails and passwords and privileges of users are considered private information that is kept locally in each repository. Each repository database has a table holding the username, privileges, and login credentials for users authorized to interact with that particular database. In addition, there is a table named "concealed" that maps the SHA1 hash of each users email address back into their true email address. The concealed table allows just the SHA1 hash of email addresses to be stored in tickets, and thus prevents actual email addresses from falling into the hands of spammers who happen to clone the repository. The content of the user and concealed tables can be pushed and pulled using the [/help/config | fossil config push] and [/help/config | fossil config pull] commands with the "user" and "email" as the AREA argument, but only if you have administrative privileges on the remote repository. <h4>2.2.5 Shunned Artifact List</h4> The set of canonical artifacts for a project - the global state for the project - is intended to be an append-only database. In other words, new artifacts can be added but artifacts can never be removed. But it sometimes happens that inappropriate content is mistakenly or maliciously added to a repository. The only way to get rid of the undesired content is to [./shunning.wiki | "shun"] it. The "shun" table in the repository database records the hash values for all shunned artifacts. The shun table can be pushed or pulled using the [/help/config | fossil config] command with the "shun" AREA argument. The shun table is also copied during a [/help/clone | clone]. <h3 id="localdb">2.3 Checkout Databases</h3> Fossil allows a single repository to have multiple working checkouts. Each working checkout has a single database in its root directory that records the state of that checkout. The checkout database is named "_FOSSIL_" or ".fslckout". The checkout database records information such as the following: * 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 first thing that happens is that Fossil locates the checkout database. Fossil first looks in the current directory. If not found there, it looks in the parent directory. If not found there, the parent of the parent. And so forth until either the checkout database is found or the search reaches the root of the file system. (In the latter case, Fossil returns an error, of course.) Once the checkout database is located, it is used to locate the repository database. Notice that the checkout database contains a pointer to the repository database but that the repository database has no record of the checkout databases. That means that a working checkout directory tree can be freely renamed or copied or deleted without consequence. But the repository database file, on the other hand, has to stay in the same place with the same name or else the open checkout databases will not be able to find it. A checkout database is created by the [/help/open | fossil open] command. A checkout database is deleted by [/help/close | fossil close]. The fossil close command really isn't needed; one can accomplish the same thing simply by deleting the checkout database. Note that the stash, the undo stack, and the state of the bisect command are all contained within the checkout database. That means that the fossil close command will delete all stash content, the undo stack, and the bisect state. The close command is not undoable. Use it with care. <h2>3.0 See Also</h2> * [./makefile.wiki | The Fossil Build Process] * [./contribute.wiki | How To Contribute Code To Fossil] * [./adding_code.wiki | Adding New Features To Fossil] |
Added www/th1-hooks.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 | TH1 Hooks ========= <big><big><big><font color="red">** DRAFT **</font></big></big></big> The **TH1 hooks** feature allows <a href="th1.md">TH1</a> scripts to be configured that can monitor, create, alter, or cancel the execution of Fossil commands and web pages. This feature requires the TH1 hooks feature to be enabled at compile-time. Additionally, the "th1-hooks" repository setting must be enabled at runtime in order to successfully make use of this feature. TH1 Hook Related User-Defined Procedures ---------------------------------------- In order to activate TH1 hooks, one or more of the following user-defined procedures should be defined, generally from within the "th1-setup" script (setting) for a repository. The following bullets summarize the available TH1 hooks: * command\_hook -- _Called before execution of a command._ * command\_notify -- _Called after execution of a command._ * webpage\_hook -- _Called before rendering of a web page._ * webpage\_notify -- _Called after rendering of a web page._ TH1 Hook Related Variables for Commands --------------------------------------- * cmd\_name -- _Name of command being executed._ * cmd\_args -- _Current command line arguments._ * cmd\_flags -- _Bitmask of CMDFLAG values for the command being executed._ TH1 Hook Related Variables for Web Pages ---------------------------------------- * web\_name -- _Name of web page being rendered._ * web\_args -- _Current web page arguments._ * web\_flags -- _Bitmask of CMDFLAG values for the web page being rendered._ <a id="cmdReturnCodes"></a>TH1 Hook Related Return Codes for Commands ----------------------------------------------------------------------- * TH\_OK -- _Command will be executed, notification will be executed._ * TH\_ERROR -- _Command will be skipped, notification will be skipped, error message will be emitted._ * TH\_BREAK -- _Command will be skipped, notification will be skipped._ * TH\_RETURN -- _Command will be executed, notification will be skipped._ * TH\_CONTINUE -- _Command will be skipped, notification will be executed._ For commands that are not included in the Fossil binary, allowing their execution will cause the standard "unknown command" error message to be generated, which will typically exit the process. Therefore, adding a new command generally requires using the TH_CONTINUE return code. <a id="webReturnCodes"></a>TH1 Hook Related Return Codes for Web Pages ------------------------------------------------------------------------ * TH\_OK -- _Web page will be rendered, notification will be executed._ * TH\_ERROR -- _Web page will be skipped, notification will be skipped, error message will be emitted._ * TH\_BREAK -- _Web page will be skipped, notification will be skipped._ * TH\_RETURN -- _Web page will be rendered, notification will be skipped._ * TH\_CONTINUE -- _Web page will be skipped, notification will be executed._ For web pages that are not included in the Fossil binary, allowing their rendering will cause the standard "Not Found" error message to be generated, which will cause an HTTP 404 status code to be sent. Therefore, adding a new web page generally requires using the TH_CONTINUE return code. <a id="triggerReturnCodes"></a>Triggering TH1 Return Codes from a Script -------------------------------------------------------------------------- * TH\_OK -- _This is the default return code, nothing special needed._ * TH\_ERROR -- _Use the **error** command._ * TH\_BREAK -- _Use the **break** command._ * TH\_RETURN -- _Use the **return -code 5** command._ * TH\_CONTINUE -- _Use the **continue** command._ <a id="command_hook"></a>TH1 command_hook Procedure ----------------------------------------------------- * command\_hook This user-defined procedure, if present, is called just before the execution of a command. The name of the command being executed will be stored in the "cmd\_name" global variable. The arguments to the command being executed will be stored in the "cmd\_args" global variable. The associated CMDFLAG value will be stored in the "cmd\_flags" global variable. Before exiting, the procedure should trigger the return <a href="#cmdReturnCodes">code</a> that corresponds to the desired action to take next. <a id="command_notify"></a>TH1 command_notify Procedure --------------------------------------------------------- * command\_notify This user-defined procedure, if present, is called just after the execution of a command. The name of the command being executed will be stored in the "cmd\_name" global variable. The arguments to the command being executed will be stored in the "cmd\_args" global variable. The associated CMDFLAG value will be stored in the "cmd\_flags" global variable. Before exiting, the procedure should trigger the return <a href="#cmdReturnCodes">code</a> that corresponds to the desired action to take next. <a id="webpage_hook"></a>TH1 webpage_hook Procedure ----------------------------------------------------- * webpage\_hook This user-defined procedure, if present, is called just before the rendering of a web page. The name of the web page being rendered will be stored in the "web\_name" global variable. The arguments to the web page being rendered will be stored in the "web\_args" global variable. The associated CMDFLAG value will be stored in the "web\_flags" global variable. Before exiting, the procedure should trigger the return <a href="#webReturnCodes">code</a> that corresponds to the desired action to take next. <a id="webpage_notify"></a>TH1 webpage_notify Procedure --------------------------------------------------------- * webpage\_notify This user-defined procedure, if present, is called just after the rendering of a web page. The name of the web page being rendered will be stored in the "web\_name" global variable. The arguments to the web page being rendered will be stored in the "web\_args" global variable. The associated CMDFLAG value will be stored in the "web\_flags" global variable. Before exiting, the procedure should trigger the return <a href="#webReturnCodes">code</a> that corresponds to the desired action to take next. |
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 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 | 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. Fossil was first being designed at about the same time that TH1 was being developed for testing SQLite on SymbianOS. 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 sense, TH1 syntax is similar to the familiar command-line shell syntax. A token is any sequence of characters other than whitespace and semicolons. Text inside double-quotes is a single token even if it includes whitespace and semicolons. Text within {...} pairs is also a single token, which is useful because curly braces are easier to “pair†and nest properly than double-quotes. 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 evaluates its first argument (the second token) as an expression, and if that expression is true, evaluates 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. All of this also explains the emphasis on *unescaped* characters above: the curly braces `{ }` are string quoting characters in Tcl/TH1, not block delimiters as in C. This is how we can have a command that extends over multiple lines. It is also why the `else` keyword must be cuddled up with the closing brace for the `if` clause's scriptlet. The following is invalid Tcl/TH1: if {$current eq "dev"} { puts "hello" } else { puts "world" } If you try to run this under either Tcl or TH1, the interpreter will tell you that there is no `else` command, because with the newline on the third line, you terminated the `if` command. Occasionally in Tcl/TH1 scripts, you may need to use a backslash at the end of a line to allow a command to extend over multiple lines without being considered two separate commands. Here's an example from one of Fossil's test scripts: return [lindex [regexp -line -inline -nocase -- \ {^uuid:\s+([0-9A-F]{40}) } [eval [getFossilCommand \ $repository "" info trunk]]] end] Those backslashes allow the command to wrap nicely within a standard terminal width while telling the interpreter to consider those three lines as a single command. Summary of Core TH1 Commands ---------------------------- The original Tcl language (after which 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: * array exists VARNAME * array names VARNAME * break * catch SCRIPT ?VARIABLE? * continue * error ?STRING? * expr EXPR * for INIT-SCRIPT TEST-EXPR NEXT-SCRIPT BODY-SCRIPT * foreach VARIABLE-LIST VALUE-LIST BODY-SCRIPT * if EXPR SCRIPT (elseif EXPR SCRIPT)* ?else SCRIPT? * info commands * info exists VARNAME * info vars * lappend VARIABLE TERM ... * lindex LIST INDEX * list ARG ... * llength LIST * lsearch LIST STRING * 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 index STRING INDEX * string is CLASS STRING * string last NEEDLE HAYSTACK ?START-INDEX? * string match PATTERN STRING * 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 work as in the original Tcl. Refer to the <a href="https://www.tcl-lang.org/man/tcl/contents.htm">Tcl documentation</a> for details. Summary of Core TH1 Variables ----------------------------- * tcl\_platform(engine) -- _This will always have the value "TH1"._ * tcl\_platform(platform) -- _This will have the value "windows" or "unix"._ * th\_stack\_trace -- _This will contain error stack information._ 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](#anoncap) * [anycap](#anycap) * [artifact](#artifact) * [builtin_request_js](#bireqjs) * [capexpr](#capexpr) * [captureTh1](#captureTh1) * [cgiHeaderLine](#cgiHeaderLine) * [checkout](#checkout) * [combobox](#combobox) * [copybtn](#copybtn) * [date](#date) * [decorate](#decorate) * [defHeader](#defHeader) * [dir](#dir) * [enable\_htmlify](#enable_htmlify) * [enable\_output](#enable_output) * [encode64](#encode64) * [getParameter](#getParameter) * [glob\_match](#glob_match) * [globalState](#globalState) * [hascap](#hascap) * [hasfeature](#hasfeature) * [html](#html) * [htmlize](#htmlize) * [http](#http) * [httpize](#httpize) * [insertCsrf](#insertCsrf) * [linecount](#linecount) * [markdown](#markdown) * [nonce](#nonce) * [puts](#puts) * [query](#query) * [randhex](#randhex) * [redirect](#redirect) * [regexp](#regexp) * [reinitialize](#reinitialize) * [render](#render) * [repository](#repository) * [searchable](#searchable) * [setParameter](#setParameter) * [setting](#setting) * [stime](#stime) * [styleHeader](#styleHeader) * [styleFooter](#styleFooter) * [styleScript](#styleScript) * [submenu](#submenu) * [tclEval](#tclEval) * [tclExpr](#tclExpr) * [tclInvoke](#tclInvoke) * [tclIsSafe](#tclIsSafe) * [tclMakeSafe](#tclMakeSafe) * [tclReady](#tclReady) * [trace](#trace) * [unversioned content](#unversioned_content) * [unversioned list](#unversioned_list) * [utime](#utime) * [verifyCsrf](#verifyCsrf) * [verifyLogin](#verifyLogin) * [wiki](#wiki) Each of the commands above is documented by a block comment above their implementation in the th\_main.c or th\_tcl.c source files. All commands starting with "tcl", with the exception of "tclReady", require the Tcl integration subsystem be included at compile-time. Additionally, the "tcl" repository setting must be enabled at runtime in order to successfully make use of these commands. <a id="anoncap"></a>TH1 anoncap Command ----------------------------------------- Deprecated: prefer [capexpr](#capexpr) instead. * anoncap STRING... Returns true if the anonymous user has all of the capabilities listed in STRING. <a id="anycap"></a>TH1 anycap Command --------------------------------------- Deprecated: prefer [capexpr](#capexpr) instead. * anycap STRING Returns true if the current user user has any one of the capabilities listed in STRING. <a id="artifact"></a>TH1 artifact 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. <a id="bireqjs"></a>TH1 builtin_request_js Command -------------------------------------------------- * builtin_request_js NAME NAME must be the name of one of the [built-in javascript source files](/dir?ci=trunk&type=flat&name=src&re=js$). This command causes that javascript file to be appended to the delivered document. <a id="capexpr"></a>TH1 capexpr Command ----------------------------------------------------- * capexpr CAPABILITY-EXPR The capability expression is a list. Each term of the list is a cluster of [capability letters](./caps/ref.html). The overall expression is true if any one term is true. A single term is true if all letters within that term are true. Or, if the term begins with "!", then the term is true if none of the terms or true. Or, if the term begins with "@" then the term is true if all of the capability letters in that term are available to the "anonymous" user. Or, if the term is "*" then it is always true. Examples: ``` capexpr {j o r} True if any one of j, o, or r are available capexpr {oh} True if both o and h are available capexpr {@2 @3 4 5 6} 2 or 3 available for anonymous or one of 4, 5 or 6 is available for the user capexpr L True if the user is logged in capexpr !L True if the user is not logged in ``` The `L` pseudo-capability is intended only to be used on its own or with the `!` prefix for implementing login/logout menus via the `mainmenu` site configuration option: ``` Login /login !L {} Logout /logout L {} ``` i.e. if the user is logged in, show the "Logout" link, else show the "Login" link. <a id="captureTh1"></a>TH1 captureTh1 Command ----------------------------------------------------- * captureTh1 STRING Executes its single argument as TH1 code and captures any TH1-generated output as a string, which becomes the result of the function call. e.g. any `puts` calls made from that block will not generate any output, and instead their output will become part of the result string. <a id="cgiHeaderLine"></a>TH1 cgiHeaderLine Command ----------------------------------------------------- * cgiHeaderLine line Adds the specified line to the CGI header. <a id="checkout"></a>TH1 checkout 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. <a id="combobox"></a>TH1 combobox Command ------------------------------------------- * combobox NAME TEXT-LIST NUMLINES Generates and emits 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. <a id="copybtn"></a>TH1 copybtn Command ----------------------------------------- * copybtn TARGETID FLIPPED TEXT ?COPYLENGTH? Output TEXT with a click-to-copy button next to it. Loads the copybtn.js Javascript module, and generates HTML elements with the following IDs: * TARGETID: The `<span>` wrapper around TEXT. * copy-TARGETID: The `<span>` for the copy button. If the FLIPPED argument is non-zero, the copy button is displayed after TEXT. The optional COPYLENGTH argument defines the length of the substring of TEXT copied to clipboard: * <= 0: No limit (default if the argument is omitted). * >= 3: Truncate TEXT after COPYLENGTH (single-byte) characters. * 1: Use the "hash-digits" setting as the limit. * 2: Use the length appropriate for URLs as the limit (defined at compile-time by `FOSSIL_HASH_DIGITS_URL`, defaults to 16). <a id="date"></a>TH1 date Command ----------------------------------- * date ?-local? Return a strings which is the current time and date. If the -local option is used, the date appears using localtime instead of UTC. <a id="decorate"></a>TH1 decorate Command ------------------------------------------- * decorate STRING Renders STRING as wiki content; however, only links are handled. No other markup is processed. <a id="defHeader"></a>TH1 defHeader Command --------------------------------------------- * defHeader Returns the default page header. <a id="dir"></a>TH1 dir Command --------------------------------- * dir CHECKIN ?GLOB? ?DETAILS? Returns a list containing all files in CHECKIN. If GLOB is given only the files matching the pattern GLOB within CHECKIN will be returned. If DETAILS is non-zero, the result will be a list-of-lists, with each element containing at least three elements: the file name, the file size (in bytes), and the file last modification time (relative to the time zone configured for the repository). <a id="enable_htmlify"></a>TH1 enable\_htmlify Command ------------------------------------------------------ * enable\_htmlify * enable\_htmlify ?TRACE-LABEL? BOOLEAN By default, certain output from `puts` and similar commands is escaped for HTML. The first call form returns the current state of that feature: `1` for on and `0` for off. The second call form enables (non-0) or disables (0) that feature and returns the *pre-call* state of that feature (so that a second call can pass that value to restore it to its previous state). The optional `TRACE-LABEL` argument causes the TH1 tracing output (if enabled) to add a marker when the second form of this command is invoked, and includes that label and the boolean argument's value in the trace. If tracing is disabled, that argument has no effect. <a id="enable_output"></a>TH1 enable\_output Command ------------------------------------------------------ * enable\_output BOOLEAN Enable or disable sending output when the combobox, copybtn, puts, or wiki commands are used. <a id="encode64"></a>TH1 encode64 Command ------------------------------------------- * encode64 STRING Encode the specified string using Base64 and return the result. <a id="getParameter"></a>TH1 getParameter Command --------------------------------------------------- * getParameter NAME ?DEFAULT? Returns the value of the specified query parameter or the specified default value when there is no matching query parameter. <a id="glob_match"></a>TH1 glob\_match Command ------------------------------------------------ * glob\_match ?-one? ?--? patternList string Checks the string against the specified glob pattern -OR- list of glob patterns and returns non-zero if there is a match. <a id="globalState"></a>TH1 globalState Command ------------------------------------------------- * globalState NAME ?DEFAULT? Returns a string containing the value of the specified global state variable -OR- the specified default value. The supported items are: 1. **checkout** -- _Active local checkout directory, if any._ 1. **configuration** -- _Active configuration database file name, if any._ 1. **executable** -- _Fully qualified executable file name._ 1. **flags** -- _TH1 initialization flags._ 1. **log** -- _Error log file name, if any._ 1. **repository** -- _Active local repository file name, if any._ 1. **top** -- _Base path for the active server instance, if applicable._ 1. **user** -- _Active user name, if any._ 1. **vfs** -- _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. <a id="hascap"></a>TH1 hascap Command --------------------------------------- Deprecated: prefer [capexpr](#capexpr) instead. * hascap STRING... Returns true if the current user has all of the capabilities listed in STRING. <a id="hasfeature"></a>TH1 hasfeature Command ----------------------------------------------- * hasfeature STRING Returns true if the binary has the given compile-time feature enabled. The possible features are: 1. **ssl** -- _Support for the HTTPS transport._ 1. **legacyMvRm** -- _Support for legacy mv/rm command behavior._ 1. **execRelPaths** -- _Use relative paths with external diff/gdiff._ 1. **th1Docs** -- _Support for TH1 in embedded documentation._ 1. **th1Hooks** -- _Support for TH1 command and web page hooks._ 1. **tcl** -- _Support for Tcl integration._ 1. **useTclStubs** -- _Tcl stubs enabled in the Tcl headers._ 1. **tclStubs** -- _Uses Tcl stubs (i.e. linking with stubs library)._ 1. **tclPrivateStubs** -- _Uses Tcl private stubs (i.e. header-only)._ 1. **json** -- _Support for the JSON APIs._ 1. **markdown** -- _Support for Markdown documentation format._ 1. **unicodeCmdLine** -- _The command line arguments are Unicode._ 1. **dynamicBuild** -- _Dynamically linked to libraries._ 1. **mman** -- _Uses POSIX memory APIs from "sys/mman.h"._ 1. **see** -- _Uses the SQLite Encryption Extension._ Specifying an unknown feature will return a value of false, it will not raise a script error. <a id="html"></a>TH1 html Command ----------------------------------- * html STRING Outputs the STRING escaped for HTML. <a id="htmlize"></a>TH1 htmlize Command ----------------------------------------- * htmlize STRING Escape all characters of STRING which have special meaning in HTML. Returns the escaped string. <a id="http"></a>TH1 http Command ----------------------------------- * http ?-asynchronous? ?--? url ?payload? Performs 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. <a id="httpize"></a>TH1 httpize Command ----------------------------------------- * httpize STRING Escape all characters of STRING which have special meaning in URI components. Returns the escaped string. <a id="insertCsrf"></a>TH1 insertCsrf Command ----------------------------------------------- * insertCsrf While rendering a form, call this command to add the Anti-CSRF token as a hidden element of the form. <a id="linecount"></a>TH1 linecount Command --------------------------------------------- * linecount STRING MAX MIN Returns one more than the number of \n characters in STRING. But never returns less than MIN or more than MAX. <a id="markdown"></a>TH1 markdown Command ------------------------------------------- * markdown STRING Renders the input string as markdown. The result is a two-element list. The first element contains the body, rendered as HTML. The second element is the text-only title string. <a id="nonce"></a>TH1 nonce Command ------------------------------------- * nonce Returns the value of the cryptographic nonce for the request being processed. <a id="puts"></a>TH1 puts Command ----------------------------------- * puts STRING Outputs the STRING unchanged, where "unchanged" might, depending on the context, mean "with some characters escaped for HTML." <a id="query"></a>TH1 query Command ------------------------------------- * query ?-nocomplain? SQL CODE Runs 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. <a id="randhex"></a>TH1 randhex Command ----------------------------------------- * randhex N Returns a string of N*2 random hexadecimal digits with N<50. If N is omitted, use a value of 10. <a id="redirect"></a>TH1 redirect Command ------------------------------------------- * redirect URL ?withMethod? Issues an HTTP redirect to the specified URL and then exits the process. By default, an HTTP status code of 302 is used. If the optional withMethod argument is present and non-zero, an HTTP status code of 307 is used, which should force the user agent to preserve the original method for the request (e.g. GET, POST) instead of (possibly) forcing the user agent to change the method to GET. <a id="regexp"></a>TH1 regexp 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. <a id="reinitialize"></a>TH1 reinitialize Command --------------------------------------------------- * reinitialize ?FLAGS? Reinitializes the TH1 interpreter using the specified flags. <a id="render"></a>TH1 render Command --------------------------------------- * render STRING Renders the TH1 template and writes the results. <a id="repository"></a>TH1 repository Command ----------------------------------------------- * repository ?BOOLEAN? Returns 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. <a id="searchable"></a>TH1 searchable 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. The possible document classes are: 1. **c** -- _Check-in comments_ 1. **d** -- _Embedded documentation_ 1. **t** -- _Tickets_ 1. **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. <a id="setParameter"></a>TH1 setParameter Command --------------------------------------------------- * setParameter NAME VALUE Sets the value of the specified query parameter. <a id="setting"></a>TH1 setting Command ----------------------------------------- * setting name Gets and returns the value of the specified setting. <a id="stime"></a>TH1 stime Command ------------------------------------- * stime Returns the number of microseconds of CPU time consumed by the current process in system space. <a id="styleHeader"></a>TH1 styleHeader Command ------------------------------------------------- * styleHeader TITLE Render the configured style header for the selected skin. <a id="styleFooter"></a>TH1 styleFooter Command ------------------------------------------------- * styleFooter Render the configured style footer for the selected skin. <a id="styleScript"></a>TH1 styleScript Command ------------------------------------------------- * styleScript Render the configured JavaScript for the selected skin. <a id="submenu"></a>TH1 submenu Command ----------------------------------------- * submenu link LABEL URL Add hyperlink to the submenu of the current page. <a id="tclEval"></a>TH1 tclEval Command ----------------------------------------- **This command requires the Tcl integration feature.** * tclEval arg ?arg ...? Evaluates the Tcl script and returns its result verbatim. If a Tcl script error is generated, it will be transformed into a TH1 script error. The Tcl interpreter will be created automatically if it has not been already. <a id="tclExpr"></a>TH1 tclExpr Command ----------------------------------------- **This command requires the Tcl integration feature.** * tclExpr arg ?arg ...? Evaluates the Tcl expression and returns its result verbatim. If a Tcl script error is generated, it will be transformed into a TH1 script error. The Tcl interpreter will be created automatically if it has not been already. <a id="tclInvoke"></a>TH1 tclInvoke Command --------------------------------------------- **This command requires the Tcl integration feature.** * tclInvoke command ?arg ...? Invokes the Tcl command using the supplied arguments. No additional substitutions are performed on the arguments. The Tcl interpreter will be created automatically if it has not been already. <a id="tclIsSafe"></a>TH1 tclIsSafe Command --------------------------------------------- **This command requires the Tcl integration feature.** * tclIsSafe Returns non-zero if the Tcl interpreter is "safe". The Tcl interpreter will be created automatically if it has not been already. <a id="tclMakeSafe"></a>TH1 tclMakeSafe Command ------------------------------------------------- **This command requires the Tcl integration feature.** * tclMakeSafe Forces the Tcl interpreter into "safe" mode by removing all "unsafe" commands and variables. This operation cannot be undone. The Tcl interpreter will remain "safe" until the process terminates. The Tcl interpreter will be created automatically if it has not been already. <a id="tclReady"></a>TH1 tclReady Command ------------------------------------------- * tclReady Returns true if the binary has the Tcl integration feature enabled and it is currently available for use by TH1 scripts. <a id="trace"></a>TH1 trace Command ------------------------------------- * trace STRING Generates a TH1 trace message if TH1 tracing is enabled. <a id="unversioned_content"></a>TH1 unversioned content Command ----------------------------------------------------------------- * unversioned content FILENAME Attempts to locate the specified unversioned file and return its contents. An error is generated if the repository is not open or the unversioned file cannot be found. <a id="unversioned_list"></a>TH1 unversioned list Command ----------------------------------------------------------- * unversioned list Returns a list of the names of all unversioned files held in the local repository. An error is generated if the repository is not open. <a id="utime"></a>TH1 utime Command ------------------------------------- * utime Returns the number of microseconds of CPU time consumed by the current process in user space. <a id="verifyCsrf"></a>TH1 verifyCsrf Command ----------------------------------------------- * verifyCsrf Before using the results of a form, first call this command to verify that this Anti-CSRF token is present and is valid. If the Anti-CSRF token is missing or is incorrect, that indicates a cross-site scripting attack. If the event of an attack is detected, an error message is generated and all further processing is aborted. <a id="verifyLogin"></a>TH1 verifyLogin Command ------------------------------------------------- * verifyLogin Returns non-zero if the specified user name and password represent a valid login for the repository. <a id="wiki"></a>TH1 wiki Command ----------------------------------- * wiki STRING Renders STRING as wiki content. Tcl Integration Commands ------------------------ When the Tcl integration subsystem is enabled, several commands are added to the Tcl interpreter. They are used to allow Tcl scripts access to the Fossil functionality provided via TH1. The following is a summary of the Tcl commands: * th1Eval * th1Expr <a id="th1Eval"></a>Tcl th1Eval Command ----------------------------------------- **This command requires the Tcl integration feature.** * th1Eval arg Evaluates the TH1 script and returns its result verbatim. If a TH1 script error is generated, it will be transformed into a Tcl script error. <a id="th1Expr"></a>Tcl th1Expr Command ----------------------------------------- **This command requires the Tcl integration feature.** * th1Expr arg Evaluates the TH1 expression and returns its result verbatim. If a TH1 script error is generated, it will be transformed into a Tcl script error. |
Changes to www/theory1.wiki.
1 | <title>Thoughts On The Design Of The Fossil DVCS</title> | < | | | > | 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 | <title>Thoughts On The Design Of The Fossil DVCS</title> Two questions (or criticisms) that arise frequently regarding Fossil can be summarized as follows: 1. Why is Fossil based on SQLite instead of a distributed NoSQL database? 2. Why is Fossil written in C instead of a modern high-level language? Neither question can be answered directly because they are both based on false assumptions. We claim that Fossil is not based on SQLite at all and that Fossil is not based on a distributed NoSQL database because Fossil is a distributed NoSQL database. And, Fossil does use a modern high-level language for its implementation, namely SQL. <h2>Fossil Is A NoSQL Database</h2> We begin with the first question: Fossil is not based on a distributed NoSQL database because Fossil <u><i>is</i></u> a distributed NoSQL database. Fossil is <u>not</u> based on SQLite. The current implementation of Fossil uses SQLite as a local store for the content of the distributed database and as a cache for meta-information about the distributed database that is precomputed for quick and easy presentation. But the use of SQLite in this role is an implementation detail and is not fundamental to the design. Some future version of Fossil might do away with SQLite and substitute a pile-of-files or a key/value database in place of SQLite. (Actually, that is very unlikely to happen since SQLite works amazingly well in its current role, but the point is that omitting SQLite from Fossil is a theoretical possibility.) The underlying database that Fossil implements has nothing to do with SQLite, or SQL, or even relational database theory. The underlying database is very simple: it is an unordered collection of "artifacts". An artifact is a list of bytes - a "file" in the usual manner of thinking. Many artifacts are simply the content of source files that have been checked into the Fossil repository. Call these "content artifacts". Other artifacts, known as "control artifacts", contain ASCII text in a particular format that defines relationships between other artifacts, such as which content artifacts that go together to form a particular version of the project. Each artifact is named by its SHA1 or SHA3-256 hash and is thus immutable. Artifacts can be added to the database but not removed (if we ignore the exceptional case of [./shunning.wiki | shunning].) Repositories synchronize by computing the union of their artifact sets. SQL and relation theory play no role in any of this. SQL enters the picture only in the implementation details. The current implementation of Fossil stores each artifact as a BLOB in an SQLite |
︙ | ︙ | |||
62 63 64 65 66 67 68 | So really, Fossil works with two separate databases. There is the bag-of-artifacts database which is non-relational and distributed (like a NoSQL database) and there is the local relational database. The bag-of-artifacts database has a fixed format and is what defines a Fossil repository. Fossil will never modify the file format of the bag-of-artifacts database in an incompatible way because to do so would be to make something that is no longer "Fossil". The local relational database, on the other hand, | | | | | | | | 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 | So really, Fossil works with two separate databases. There is the bag-of-artifacts database which is non-relational and distributed (like a NoSQL database) and there is the local relational database. The bag-of-artifacts database has a fixed format and is what defines a Fossil repository. Fossil will never modify the file format of the bag-of-artifacts database in an incompatible way because to do so would be to make something that is no longer "Fossil". The local relational database, on the other hand, is a cache that contains information derived from the bag-of-artifacts. The schema of the local relational database changes from time to time as the Fossil implementation is enhanced, and the content is recomputed from the unchanging bag of artifacts. The local relational database is an implementation detail which currently happens to use SQLite. Another way to think of the relational tables in a Fossil repository is as an index for the artifacts. Without the relational tables, to generate a report like a timeline would require scanning every artifact - the equivalent of a full table scan. The relational tables hold pointers the relevant artifacts in presorted order so that generating a timeline is much more efficient. So like an index in a relational database, the relational tables in an Fossil repository do not add any new information, they merely make the information in the artifacts faster and easier to look up. Fossil is not "based" on SQLite. Fossil simply exploits SQLite as a powerful tool to make the implementation easier. And Fossil doesn't use a distributed NoSQL database because Fossil is a distributed NoSQL database. That answers the first question. <h2>SQL Is A High-Level Scripting Language</h2> The second concern states that Fossil does not use a high-level scripting language. But that is not true. Fossil uses SQL (as implemented by SQLite) as its scripting language. This misunderstanding likely arises because people fail to appreciate that SQL is a programming language. People are taught that SQL is a "query language" as if that were somehow different from a "programming language". But they really are two different flavors of the same thing. I find that people do better with SQL if they think of SQL as a programming language and each statement of SQL is a separate program. SQL is a peculiar programming language in that one uses SQL to specify <i>what</i> to compute whereas in most other programming languages one specifies <i>how</i> to carry out the computation. This difference means that SQL is an extraordinary high-level programming language, but it is still just a programming language. For certain types of problems, SQL has a huge advantage over other programming languages because it is so high level and because it allows programmers to focus more on the <i>what</i> and less on the <i>how</i> of a computation. In other words, programmers tend to think about problems at a much higher level when using SQL; this can result in better applications. SQL is also very dense. In practice, this often means that a few lines of SQL can often replace hundreds or thousands of lines of procedural code, with a corresponding decrease in programming effort and opportunities to introduce bugs. Fossil happens to be one of those problems for which SQL is well suited. Much of the "heavy lifting" within the Fossil implementation is carried out using SQL statements. It is true that these SQL statements are glued together with C code, but it turns out that C works surprisingly well in that role. Several early prototypes of Fossil were written in a scripting language (TCL). We normally find that TCL programs are shorter than the equivalent C code by a factor of 10 or more. But in the case of Fossil, the use of TCL was actually making the code longer and more difficult to understand. And so in the final design, we switched from TCL to C in order to make the code easier to implement and debug. Without the advantages of having SQLite built in, the design might well have followed a different path. Most reports generated by Fossil involve a complex set of queries against the relational tables of the repository database. These queries are normally implemented in only a few dozen lines of SQL code. But if those queries had been implemented procedurally using a key/value or pile-of-files database, it may have well been the case that a high-level scripting language such as Tcl, Python, or Ruby may have worked out better than C. |
Added www/tickets.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 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 | <title>The Fossil Ticket System</title> <h2>1.0 File Format</h2> At its lowest level, the tickets of Fossil consist solely of [./fileformat.wiki#tktchng | ticket change artifacts]. Each ticket change artifact corresponds to a single change to a ticket. The act of creating a ticket is considered a change. Each ticket change artifact contains the following information: <ul> <li>The ID of the ticket that was changed <li>The time stamp for when the change occurred <li>The user who made the change <li>A list of key/value pairs that show what changed in the ticket </ul> To determine the current state of a particular ticket, Fossil orders the change artifacts for that ticket from oldest to most recent, then applies each change in time stamp order. On each change artifact, there are one or more key/value pairs that implement the change. The key corresponds to a field of the ticket that is modified. The value may either replace the earlier value for that key, or the value may be appended to the prior value. <h2>2.0 Ticket Tables</h2> The low-level artifact format for ticket content is tedious and cumbersome to access in real time. To facility reporting and display of tickets, the low-level artifact information is collected and summarized in a pair of SQL tables in each local repository. Display and reporting of tickets is accomplished by querying these two tables. Note that only the low-level ticket change artifacts are synced. The content of the two ticket tables can always be reconstructed from the ticket change artifacts. And, indeed, the reconstruction of the ticket tables from low-level artifacts happens automatically whenever new ticket change artifacts are received by the system. The important point to remember is that display of tickets is accomplished using SQL tables but that recording and syncing of ticket information is accomplished using ticket change artifacts. <h3>2.1 Ticket Table Schema</h3> The two ticket tables are called TICKET and TICKETCHNG. The default schema (as of this writing) for these two tables is shown below: <verbatim> CREATE TABLE ticket( -- Do not change any column that begins with tkt_ tkt_id INTEGER PRIMARY KEY, tkt_uuid TEXT UNIQUE, tkt_mtime DATE, tkt_ctime DATE, -- Add as many fields as required below this line type TEXT, status TEXT, subsystem TEXT, priority TEXT, severity TEXT, foundin TEXT, private_contact TEXT, resolution TEXT, title TEXT, comment TEXT ); CREATE TABLE ticketchng( -- Do not change any column that begins with tkt_ tkt_id INTEGER REFERENCES ticket, tkt_rid INTEGER REFERENCES blob, tkt_mtime DATE, -- Add as many fields as required below this line login TEXT, username TEXT, mimetype TEXT, icomment TEXT ); CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime); </verbatim> Generally speaking, there is one row in the TICKETCHNG table for each change to each ticket. In other words, there is one row in the TICKETCHNG table for each low-level ticket change artifact. The TICKET table, on the other hand, contains a summary of the current status of each ticket. Fields of the TICKET and TICKETCHNG tables that begin with "tkt_" are used internally by Fossil. The logic inside of Fossil that converts ticket change artifacts into row data for the two ticket tables expects the "tkt_" fields to always be present. All of the other fields of the TICKET and TICKETCHNG tables are "user defined" in the sense that they can be anything the administrator of the system wants them to be. The user-defined fields should correspond to keys in the key/value pairs of the ticket change artifacts. 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. All the other fields of the TICKET and TICKETCHNG tables are available for customization for individual projects. None of the remaining fields are required, but all of them are needed in order to use the default ticket creating, viewing, and editing scripts. It is recommended that the other fields be retained and that customizations be restricted to adding new fields above and beyond the default. <h3>2.2 Translating Artifacts To Tables</h3> Each row in the TICKETCHNG table corresponds to a single ticket change artifact. The tkt_id field is the integer primary key of the TICKET table entry for the corresponding ticket. The tkt_rid field is the integer primary key for the BLOB table entry that contains the low-level artifact text. The tkt_mtime field is the time stamp on the ticket change artifact, expressed as a Julian day number. If the ticket change artifact contains a key/value pair where the key is "login", then the corresponding value is stored in the login field of the TICKETCHNG table. The same it true for "username", "mimetype", and "icomment" fields. Any time there is a key/value pair in the ticket change artifact and the key corresponds to the name of a field in the TICKETCHNG table, then the value of that key/value pair is stored in the TICKETCHNG table. If the TICKETCHNG table has a field for which there is no corresponding key/value pair in the artifact, then that field of the TICKETCHNG table is NULL. If there are key/value pairs in the artifact that have no corresponding field in the TICKETCHNG table, those key/value pairs are silently ignored. Each row in the TICKET table records the overall status of a ticket. The tkt_id field is a unique integer primary key for the ticket. the tkt_uuid field is the global ticket identifier - a larger random hexadecimal constant. The tkt_mtime and tkt_ctime fields hold the times of the most recent and the oldest ticket change artifacts for this ticket, respectively. To reconstruct the TICKET table, the ticket change artifacts are visited in time stamp 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 time stamp order. |
Added www/tls-nginx.md.
> > > | 1 2 3 | # Proxying Fossil via HTTPS with nginx This document has [moved](./server/debian/nginx.md#tls). |
Added www/uitest.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 | <html> <head> <title>Fossil UI Test</title> </head> <body> <script> var aTest = [ /////////////////////////////////////////////////////////////////////////// /// Add pages to be tested below: ////////////////////////////////////////////////////////////////////////// { url: "timeline", desc: "Simple timeline of most recent check-ins. Verify that all submenus work." }, { url: "timeline?n1=125", desc: "Timeline with 125 entries. Verify that submenus preserve the entry count." }, { url: "wiki", desc: "The wiki homepage" } ////////////////////////////////////////////////////////////////////////////// /// End of testing data ///////////////////////////////////////////////////////////////////////////// ]; var iTest = 0; var nTest = aTest.length; var totalTest = nTest; var firstTest = aTest[0]; function gebi(x){ return document.getElementById(x); } </script> <style type="text/css"> a { padding-top:5px; padding-bottom:5px; padding-left:10px; padding-right:10px; text-align: right; color: #000; background-color: #eef; vertical-align:middle; box-shadow: 0px 3px 4px #999; border-radius: 10px; } </style> <p>Test frame for the Fossil server at <span id="x1">???</span>.</p> <ul> <li> <span id="x2">0</span> of <span id="x3">0</span> pages checked so far. <li> Current page: <b><span id="x5"></span></b> <li> <span id="x6">Press "Begin" to begin testing</span> </ul> <a id="x-start" target="fossiltest" onclick="startTest()">Begin</a> <a id="x-prev" target="fossiltest" onclick="prevTest()">Previous</a> <a id="x-next" target="fossiltest" onclick="nextTest()">Next</a> <a id="x-pass" target="fossiltest" onclick="passTest()">Test Passes</a> <p id="x-done" style="color:green;">Testing Complete!</p> <script> var re = new RegExp("/doc/[^/]+/www/.*$"); var baseURI = document.location.href.replace(re,"/"); gebi("x1").innerHTML = '"' + baseURI + '"'; gebi("x3").innerHTML = nTest; var xprev = gebi("x-prev"); var xnext = gebi("x-next"); var xpass = gebi("x-pass"); var xstart = gebi("x-start"); gebi("x-done").hidden = 1; function loadPage(){ var x = aTest[iTest]; gebi("x5").innerHTML = x.url; gebi("x6").innerHTML = x.desc; gebi("x2").innerHTML = totalTest-nTest; xstart.hidden = 1; xpass.hidden = 0; if( iTest>0 ){ var y = aTest[iTest-1]; xprev.href = baseURI + y.url; xprev.hidden = 0; }else{ xprev.hidden = 1; } if( iTest+1<nTest ){ var z = aTest[iTest+1]; xnext.href = baseURI + z.url; xpass.href = baseURI + z.url; xnext.hidden = 0; }else{ xnext.hidden = 1; if( nTest>1 ) xpass.href = xprev.href; } } gebi("x3").innerHTML = nTest; xprev.hidden = 1; xnext.hidden = 1; xpass.hidden = 1; xstart.hidden = 0; xstart.href = baseURI + aTest[0].url; function startTest(){ setTimeout(loadPage,1); } function prevTest(){ if( iTest<=0 ) return false; iTest--; setTimeout(loadPage,1); } function nextTest(){ if( iTest+1>=nTest ) return false; iTest++; setTimeout(loadPage,1); } function passTest(){ if( nTest==1 ){ xpass.hidden = 1; xnext.hidden = 1; xprev.hidden = 1; gebi("x2").innerHTML = totalTest; gebi("x-done").hidden = 0; return false; }else{ aTest.splice(iTest, 1); nTest--; if( iTest>=nTest ) iTest = nTest-1; setTimeout(loadPage,1); } } </script> |
Added www/unvers.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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | <title>Unversioned Content</title> "Unversioned content" or "unversioned files" are files stored in a Fossil repository without history, meaning it retains the newest version of each such file, and that alone. Though it omits history, Fossil does sync unversioned content between repositories. In the event of a conflict during a sync, it retains the most recent version of each unversioned file, discarding older versions. Unversioned files are useful for storing ephemeral content such as builds or frequently changing web pages. We store the [https://fossil-scm.org/home/uv/download.html|download] page of the self-hosting Fossil repository as unversioned content, for example. <h2>Accessing Unversioned Files</h2> Unversioned files are <u>not</u> a part of a check-out. Unversioned files are intended to be accessible as web pages using URLs of the form: "<tt>https://example.com/cgi-script/<b>uv</b>/<i>FILENAME</i></tt>". In other words, the URI method "<b>uv</b>" (short for "unversioned") followed by the name of the unversioned file will retrieve the content of the file. The MIME type is inferred from the filename suffix. The content of unversioned files can also be retrieved using the [/help?cmd=unversioned|fossil unvers cat <i>FILENAME</i>] command. A list of all unversioned files on a server can be seen using the [/help?cmd=/uvlist|/uvlist] URL. ([/uvlist|example]). <h2>Syncing Unversioned Files</h2> Unversioned content does not sync between repositories by default. One must request it via commands such as: <pre> fossil sync <b>-u</b> fossil clone <b>-u</b> <i>URL local-repo-name</i> fossil unversioned sync </pre> The [/help?cmd=sync|fossil sync] and [/help?cmd=clone|fossil clone] commands will synchronize unversioned content if and only if they're given the "-u" (or "--unversioned") command-line option. The [/help?cmd=unversioned|fossil unversioned sync] command synchronizes the unversioned content without synchronizing anything else. Notice that the "-u" option does not work on [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull]. The "-u" option is only available on "sync" and "clone". A rough equivalent of an unversioned pull would be the [/help?cmd=unversioned|fossil unversioned revert] command. The "unversioned revert" command causes the unversioned content on the local repository to overwritten by the unversioned content found on the remote repository. Beware that because unversioned file sync is an uncommonly dangerous capability — there being no history to revert to in the case of human error — even the all-powerful Fossil "setup" user does not get unversioned file sync capability by default. See [./caps/admin-v-setup.md#dcap | this] for the full details, but the short-and-sweet takeaway is that a user needs the "y" capability on the remote before they can sync unversioned content. Until then, you're allowed to add such files to your local repo, but they will not sync. <h2>Implementation Details</h2> <i>(This section outlines the current implementation of unversioned files. This is not an interface spec and hence subject to change.)</i> Unversioned content is stored in the repository in the "unversioned" table: <pre> CREATE TABLE unversioned( uvid INTEGER PRIMARY KEY AUTOINCREMENT, -- unique ID for this file name TEXT UNIQUE, -- Name of the file rcvid INTEGER, -- From whence this file was received mtime DATETIME, -- Last change (seconds since 1970) hash TEXT, -- SHA1 or SHA3-256 hash of uncompressed content sz INTEGER, -- Size of uncompressed content encoding INT, -- 0: plaintext 1: zlib compressed content BLOB -- File content ); </pre> Fossil does not create the table ahead of need. If there are no unversioned files in the repository, the "unversioned" table will not exist. Consequently, one simple way to purge all unversioned content from a repository is to run: <pre> fossil sql "DROP TABLE unversioned; VACUUM;" </pre> Lacking history for unversioned files, Fossil does not attempt delta compression on them. Fossil servers exchange unversioned content whole; it does not attempt to "diff" your local version against the remote and send only the changes. We point this out because one use-case for unversioned content is to send large, frequently-changing files. Appreciate the consequences before making each change. There are two bandwidth-saving measures in "<tt>fossil uv sync</tt>". The first is the regular HTTP payload compression step, done on all syncs. The second is that Fossil sends hash exchanges to determine when it can avoid sending duplicate content over the wire unnecessarily. See the [./sync.wiki|synchronization protocol documentation] for further information. |
Added www/userlinks.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 | <title>Links For Fossil Users:</title> * [./relatedwork.md | Related Work]: projects and links related to Fossil and version control. * [./permutedindex.html | Documentation index] with [/search?c=d | full text search]. * [./reviews.wiki | Testimonials] from satisfied Fossil users and [./quotes.wiki | Quotes] about Fossil and other DVCSes. * [./faq.wiki | Frequently Asked Questions] * The [./concepts.wiki | concepts] behind Fossil. [./glossary.md | Another viewpoint]. * [./quickstart.wiki | Quick Start] guide to using Fossil. * [./qandc.wiki | Questions & Criticisms] directed at Fossil. * [./build.wiki | Compiling and Installing] * Fossil supports [./embeddeddoc.wiki | embedded documentation] that is versioned along with project source code. * Fossil uses an [./fileformat.wiki | enduring file format] that is designed to be readable, searchable, and extensible by people not yet born. * A tutorial on [./branching.wiki | branching], what it means and how to do it using Fossil. * The [./selfcheck.wiki | automatic self-check] mechanism helps insure project integrity. * Fossil contains a [./wikitheory.wiki | built-in wiki]. * An [./event.wiki | Event] is a special kind of wiki page associated with a point in time rather than a name. * [./settings.wiki | Settings] control the behaviour of Fossil. * [./ssl.wiki | Use SSL] to encrypt communication with the server. * The [https://fossil-scm.org/forum|Fossil forum] is, as of mid-2018, the project's central communication channel. The [https://www.mail-archive.com/fossil-users@lists.fossil-scm.org | read-only mailing list archives] house discussions spanning Fossil's first decade. * [./stats.wiki | Performance statistics] taken from real-world projects hosted on Fossil. * How to [./shunning.wiki | delete content] from a Fossil repository. * How Fossil does [./password.wiki | password management]. * On-line [/help | help]. * List of [./th1.md | TH1 commands provided by Fossil itself] that expose its key functionality to TH1 scripts. * List of [./th1-hooks.md | TH1 hooks exposed by Fossil] that enable customization of commands and web pages. * A free hosting server for Fossil repositories is available at [http://chiselapp.com/]. * How to [./server/ | set up a server] for your repository. * Customizing the [./custom_ticket.wiki | ticket system]. * 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]. * [./wsl_caveats.wiki | Using Fossil on WSL], caveats and guidance. |
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 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 | Web-Page Examples ================= Here are just a few examples of the many web pages supported by Fossil. Follow hyperlinks on the examples below to see many other examples. * <a target='_blank' class='exbtn' href='$ROOT/timeline?y=ci&n1=100'>(Example)</a> → 100 most recent check-ins. * <a target='_blank' class='exbtn' href='$ROOT/finfo?name=src/file.c'>(Example)</a> → All changes to the <b>src/file.c</b> source file. * <a target='_blank' class='exbtn' href='$ROOT/timeline?n1=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='$ROOT/timeline?n1=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='$ROOT/timeline?df=release&y=ci'>(Example)</a> → All check-ins derived from the most recent release. * <a target='_blank' class='exbtn' href='$ROOT/timeline?n1=11&y=ci&c=2014-01-01&v=1'>(Example)</a> → The previous example augmented with file changes. * <a target='_blank' class='exbtn' href='$ROOT/timeline?n1=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='$ROOT/timeline?n1=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='$ROOT/timeline?n1=200&t=svn-import'>(Example)</a> → All check-ins of the "svn-import" branch only. * <a target='_blank' class='exbtn' href='$ROOT/timeline?n1=100&y=ci&ubg'>(Example)</a> → 100 most recent check-ins color coded by committer rather than by branch. * <a target='_blank' class='exbtn' href='$ROOT/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 * <a target='_blank' class='exbtn' href='$ROOT/timeline?namechng'>(Example)</a> → Show check-ins that contain file name changes * <a target='_blank' class='exbtn' href='$ROOT/timeline?u=drh&c=2014-01-08&y=ci'>(Example)</a> → Show check-ins circa 2014-01-08 by user "drh". * <a target='_blank' class='exbtn' href='$ROOT/timeline?from=version-1.34&to=version-1.35&chng=src/timeline.c,src/doc.c'>(Example)</a> → Show all check-ins between version-1.34 and version-1.35 that make changes to either of the files src/timeline.c or src/doc.c. <big><b>→</b></big> (Hint: In the pages above, click the graph nodes for any two check-ins or files to see a diff.) <big><b>←</b></big> * <a target='_blank' class='exbtn' href='$ROOT/search?s=interesting+pages'>(Example)</a> → Full-text search for "interesting pages". * <a target='_blank' class='exbtn' href='$ROOT/tree?ci=daff9d20621&type=tree'>(Example)</a> → All files for a particular check-in (daff9d20621480) * <a target='_blank' class='exbtn' href='$ROOT/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='$ROOT/fileage?name=svn-import'>(Example)</a> → Age of all files in the latest checking for branch "svn-import". * <a target='_blank' class='exbtn' href='$ROOT/brlist'>(Example)</a> → Table of branches. (Click on column headers to sort.) * <a target='_blank' class='exbtn' href='$ROOT/stat'>(Example)</a> → Overall repository status. * <a target='_blank' class='exbtn' href='$ROOT/reports?type=ci&view=byuser'>(Example)</a> → Number of check-ins per committer. * <a target='_blank' class='exbtn' href='$ROOT/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='$ROOT/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='$ROOT/taglist'>(Example)</a> → List of tags on check-ins. * <a target='_blank' class='exbtn' href='$ROOT/bigbloblist'>(Example)</a> → The largest objects in the repository. * <a target='_blank' class='exbtn' href='$ROOT/hash-collisions'>(Example)</a> → Hash prefix collisions * <a target='_blank' class='exbtn' href='$ROOT/sitemap'>(Example)</a> → The "sitemap" containing links to many other pages |
Changes to www/webui.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 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 | <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] * [./event.wiki | Technical notes] * [./forum.wiki | Forum] * [./chat.md | Chatroom] * Timelines * Full text search over all of the above * Status information * Graphs of revision and branching history * 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, the entire [./index.wiki | Fossil website], including the document you are now reading, is rendered using the Fossil web interface, with no enhancements, and little customization. <div class="indent"> <b>Key point:</b> <i>The Fossil website is just a running instance of Fossil! </div> Note also that because Fossil is a distributed system, you can run the web interface on your local machine while off network (for example, while on an airplane) including making changes to wiki pages and/or trouble ticket, then synchronize with your co-workers after you reconnect. When you clone a Fossil repository, you don't just get the project source code, you get the entire project management website. <h2>Very Simple Startup</h2> To start using the built-in Fossil web interface on an existing Fossil repository, simply type this: <pre>fossil ui existing-repository.fossil</pre> Substitute the name of your repository, of course. The "ui" command will start a web server running (it figures out an available TCP port to use on its own) and then automatically launches your web browser to point at that server. If you run the "ui" command from within an open check-out, you can omit the repository name: <pre>fossil ui</pre> The latter case is a very useful short-cut when you are working on a Fossil project and you want to quickly do some work with the web interface. Notice that Fossil automatically finds an unused TCP port to run the server on and automatically points your web browser to the correct URL. So there is never any fumbling around trying to find an open port or to type arcane strings into your browser URL entry box. The interface just pops right up, ready to run. The Fossil web interface is also very easy to setup and run on a network server, as either a CGI program or from inetd, or as an SCGI server. Details on how to do that are described further below. <h2>Things To Do Using The Web Interface</h2> You can view <b>timelines</b> of changes to the project. The default "Timeline" link on the menu bar takes you to a page that shows the 20 most recent check-ins, wiki page edits, ticket/bug-report changes, and/or blog entries. This gives a very useful snapshot of what has been happening lately on the project. You can click to go further back in time, if needed. Or follow hyperlinks to see details, including diffs and annotated diffs, of individual check-ins, wiki page edits, ticket changes, and blog edits. You can view and edit <b>tickets and bug reports</b> by following the "Tickets" link on the menu bar. Fossil is backed by an SQL database, so users with appropriate permissions can write new ticket report formats based on SQL query statements. Fossil is careful to prevent ticket report formats from doing any mischief on the database (it only allows SELECT statements to run) and it restricts access to sensitive data such as user passwords. So it is actually safe to let anonymous users on the internet write their own ticket formats if you like. In addition to viewing and/or creating report formats, you can also create new tickets or look at summaries or complete histories of existing tickets. Any changes you make will automatically merge with changes from your co-workers the next time your repository is synchronized. You can view and edit <b>wiki</b> by following the "Wiki" link on the menu bar. Fossil has its own easy-to-remember [/wiki_rules | markup rules], or if you prefer, it also supports [/md_rules | Markdown]. 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 sub-teams 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 can be viewed as if they were ordinary web pages. This document that you are now reading is an example of embedded documentation. <h2>Customizing The Web Interface Appearance</h2> Users with appropriate permissions can customize the look and feel of the web interface using the "Admin" link on the main menu of the web interface. Templates for the header and footer of each page can be edited, as can the CSS for the entire page. You can even change around the main menu. Timeline display preferences can be edited. The page that is brought up as the "Home" page can be changed. It is often useful to set the "Home" page to be a wiki page or an embedded document. The built-in pages <b>/home</b> and <b>/index</b> can be used as the "Home" page. They have identical effect, which is to instruct Fossil to find and display a wiki page with the same name as the project, or if that does not exist, <b>/README.md</b> or <b>/index.wiki</b>. An embedded document link such as <b>doc/trunk/README.md</b> can be used for the "Home" page. If you specify one of the built-in keywords <b>/home</b> or <b>/index</b>, the page will not be treated as an embedded document. <h2>Installing On A Network Server</h2> When you create a new Fossil project and after you have configured it like you want it using the web interface, you can make the project available to a distributed team by simply copying the single repository file up to a web server that supports CGI or SCGI. To run Fossil as CGI, just put the <b>sample-project.fossil</b> file in a directory where CGI scripts have both read and write permission on the file and the directory that contains the file, then add a CGI script that looks something like this: <verbatim>#!/usr/local/bin/fossil repository: /home/www/sample-project.fossil</verbatim> Adjust the script above so that the paths are correct for your system, of course, and also make sure the Fossil binary is installed on the server. But that is <u>all</u> you have to do. You now have everything you need to host a distributed software development project in less than five minutes using a two-line CGI script. Instructions for setting up an SCGI server are [./scgi.wiki | available separately]. You don't have a CGI- or SCGI-capable web server running on your server machine? Not a problem. The Fossil interface can also be launched via inetd or xinetd. An inetd configuration line sufficient to launch the Fossil web interface looks like this: <verbatim>80 stream tcp nowait.1000 root /usr/local/bin/fossil \ /usr/local/bin/fossil http /home/www/sample-project.fossil</verbatim> As always, you'll want to adjust the pathnames to whatever is appropriate for your system. The xinetd setup uses a different syntax but follows the same idea. |
Added www/whyallinone.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 | # Why Add Forum, Wiki, and Web Software To Your DVCS? One notable feature of Fossil is that it bundles [bug tracking](./bugtheory.wiki), [wiki](./wikitheory.wiki), [forum](./forum.wiki), [chat](./chat.md), and [technotes](./event.wiki) with distributed version control to give you an all-in-one software project management system. A commenter on [Hacker News](https://news.ycombinator.com/item?id=27437895) takes exception to this idea, writing: > *I don't want forum/web software built into my dvcs.* > *I don't see how this improves over git.* This commenter may hold whatever opinions he wishes, of course. However, there are many good reasons why bundling other project management features with the DVCS might be useful for a given project: 1. There is a single software package to install and manage for the project website. The alternative is to select, install, configure, learn about, manage, and maintain separate DVCS, wiki, ticketing, forum, chat, documentation, and whatever other software packages your project needs. Less time spent on project administration details means more time available to spend on the project itself. 2. Fossil’s autosync feature gives you an implicit backup of the wiki, tickets, forum, and so forth simply by cloning the repository to another machine and using that clone regularly. Since the typical Fossil usage pattern is to stand the repo up on a central server and have the developers clone that repository down to their personal machines, if the server falls over, the last developer to do anything that resulted in an autosync has a functional and up-to-date backup. There are [limitations to relying on Fossil’s autosync feature for backup purposes](./backup.md), but that document gives two methods for more complete backups, both of which are easily automated. The Fossil project itself is distributed across three data centers in this manner via cron. 3. Remote workers get more than just the source code: they get the entire website including versioned documentation, wiki articles, tickets, forum posts, and so forth. This supports off-network development when traveling, when riding out Internet service failures, and when workers must sync with multiple remote servers, as when working alternately from home and in some central office. Feature-competitive Fossil alternatives typically solve this same problem with centralization, which generally means that only the DVCS piece still works in these situations where the developer is unable to contact the central server. Why accept the limitation of having a distributed clone of the code repo alone? Centralization doesn’t work for every project. If you enjoy the benefits of truly distributed (read: non-centralized) version control, you may also benefit from distributed forums, distributed ticket tracking, distributed wiki article publishing, and so forth. 4. Integration of all of these features allows easy hyperlinks between check-in comments, wiki pages, forum posts, and tickets. More, because the software sees both sides of the link, referrer and referent, it can provide automatic back-references. A common situation in a Fossil project is that: * a forum post refers to a versioned project document that shows that the software isn’t behaving as documented; * a developer triages that forum report as a verified bug, filing a ticket to prioritize and track the resolution; * developers chat about the problem, referring to the ticket and thereby indirectly referring to the forum post, plus perhaps to other Fossil-managed resources such as a wiki document giving design principles that guide the proper fix; and finally * the commit message resolving the ticket includes a reference to the ticket it resolves. Since Fossil sees that the commit refers to a ticket, the ticket page automatically also refers back to the commit, closing the loop. A latecomer may arrive at the ticket via a web search, and from that see that it was closed following a commit. They can follow the link from the initial ticket message to the forum thread to catch up on the discussion leading to the fix and likely find a follow-up post from the initial reporting user saying whether the fix worked for them. If further work was needed, the latecomer can likely find it from that forum thread. This works even in a remote off-network clone: the developer can pull up the project web site via an `http://localhost` link and follow these links around the loop. Fossil allows breaking some of these project facilities out into separate repositories, as when the public forum is kept separate from the actual software development repository for administration reasons. By using Fossil’s [interwiki link feature](./interwiki.md), you can get this same internal linking from ticket to commit to forum to wiki even across these administrative boundaries, even with remote off-network clones, simply by adjusting the interwiki map to match the remote clone’s network configuration. 5. Bundling all of these services gives [single sign-on][SSO] (SSO) for all aspects of the project. The same username/password works for code, wiki, forum, tickets, and chat. If you choose to administratively separate some of these features by setting up multiple cooperating Fossil repositories, its [login groups feature](./caps/login-groups.md) allows asymmetric SSO across these administrative boundaries so that, for example, users allowed to commit to the code repository also get a forum repository login, but self-registered forum users don’t automatically get the ability to commit to the code repo. 6. Bundling all of these features reduces the number of external dependencies for the project. Take the first two points above: standing up a Fossil repo backup on a new server may be as simple as copying the backup to the new server and [configuring its stock HTTP server to point at the backup repository via CGI](./server/any/cgi.md). Consider: If you had good backups for all of the elements in a Git + [Jira] + [Discord] + [MediaWiki] + [Sphinx] lash-up, how long would it take you to stand up a replacement? That lash-up certainly has more features combined than Fossil alone, but are they worth the administration and hosting costs they impose? Fossil’s feature set suffices for the SQLite project it was created to serve, as well as for many others; is your project sufficiently more complex, such that it *needs* all of those extra features and their concomitant complexity? Considerations such as these push many into centralized hosting services such as GitHub, GitLab, Bitbucket, and so forth, but that just takes you back to point 3 above. 7. Hosting all of these elements within a single service gives a consistent look-and-feel across all aspects of the project. Skinning independent software packages’ web interfaces to make them appear unified is more work than skinning everything once, as in Fossil, and even then, you can’t make independently-developed software look like it was produced by a single entity without resorting to heroic levels of customization. If you use a separate DVCS web front end, chat system, forum manager, documentation system, ticket tracker, and so on, you are likely to be relegated to simply matching colors and fonts; you *might* also get the ability to add a common logo to the header of all of these independent pieces. The pieces won’t look unified, because they weren’t developed that way. The Fossil project’s skinning system lets you affect all of its elements globally from the single skin editor. Or not: there’s a feature in Fossil that lets skin customizations apply to only *some* Fossil features. The initial impetus behind this feature was that one of our users wanted Markdown to be rendered with different indentation in forum posts than in [embedded documentation][edoc] owing to the inherent differences between the two presentation modalities. A user taking advantage of this per-feature CSS capability who wishes to change a UI element common to all Fossil features — say, to change the font for literal code blocks — may still make such a change globally. Opting into this per-feature CSS doesn’t fork all skinning efforts: UI elements not explicitly reskinned on a per-feature basis inherit the global skinning. But it goes futher. Fossil has a feature for [project-specific extensions](./serverext.wiki), which backs the [SQLite Release Checklist][srckl], for instance. You wouldn’t know by looking at that page that it’s produced by software that isn’t actually part of Fossil: the extension only delivers the core of the page, and Fossil’s skining wraps it in a way that lets it inherit all of the project-level skinning customizations. 8. Unifying all of these features within Fossil means we have a single Markdown interpreter common to all elements. If you lash multiple software systems together, even if they can all agree on Markdown as a common document markup language — hardly a given, as shown by the MediaWiki and Sphinx elements in point 6’s example above — they’re likely to render your text using different — possibly even incompatibly-different — Markdown dialects. This costs you in mental gear-switching when moving from the code repository to the documentation system to the forums to the ticket tracker. More than that, though, a developer might write a forum post that later gets promoted to a wiki article or to an embedded version-controlled project document. A developer on a Fossil-backed project may simply copy-paste the forum post text into the new document and save it, not needing to carefully check that it still renders properly under the second Markdown rendering engine. Similarly, if a user reports a potential bug via the forum, the developer can copy interesting pieces of the Markdown from the post into a ticket comment, again without needing to fiddle with dialect incompatibilities. 9. Fossil is [free, open-source software](../COPYRIGHT-BSD2.txt), through and through. Git-backed lash-ups tend to incorporate either proprietary add-ons or proprietary hosting systems that produce vendor lock-in. Fossil gives you the freedom to take your complete backup (point 2) of the project including its idiosyncratic customizations and stand it up elsewhere on commodity hardware and software stacks. All of this having been said, the non-DVCS features of Fossil are optional. Its forum and chat features are disabled by default, and you can disable the ticket-tracking and wiki features with a quick configuration change to its [role-based access control system](./caps/). When you’re ready to turn these additional features on, you can do so with a few mouse clicks. Because Fossil is web-native out of the box, if you’ve delegated these features to outside systems to flesh out Git’s DVCS-only nature, Fossil can link out to these systems, and they back into Fossil, letting you use Fossil in the same DVCS-only mode. [Discord]: https://discord.com/ [edoc]: ./embeddeddoc.wiki [Jira]: https://www.atlassian.com/software/jira [MediaWiki]: https://www.mediawiki.org/ [Sphinx]: https://www.sphinx-doc.org/en/master/ [SSO]: https://en.wikipedia.org/wiki/Single_sign-on [srckl]: https://www.sqlite.org/src/ext/checklist/top/index |
Added www/whyusefossil.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 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 | <title>Why You Should Use Fossil</title> <h4>(Or if not Fossil, at least some kind of modern version control such as Git, Mercurial, or Subversion.)</h4> <h5>I. Benefits of Version Control</h5> <ol type='A'> <li><p><b>Immutable file and version identification</b> <ol type='i'> <li>Simplified and unambiguous communication between developers <li>Detect accidental or surreptitious changes <li>Locate the origin of discovered files </ol> <li><p><b>Parallel development</b> <ol type='i'> <li>Multiple developers on the same project <li>Single developer with multiple subprojects <li>Experimental features do not contaminate the main line <li>Development/Testing/Release branches <li>Incorporate external changes into the baseline </ol> <li><p><b>Historical record</b> <ol type='i'> <li>Exactly reconstruct historical builds <li>Locate when and by whom faults were injected <li>Find when and why content was added or removed <li>Team members see the big picture <li>Research the history of project features or subsystems <li>Copyright and patent documentation <li>Regulatory compliance </ol> <li><p><b>Automatic replication and backup</b> <ol type='i'> <li>Everyone always has the latest code <li>Failed disk-drives cause no loss of work <li>Avoid wasting time doing manual file copying <li>Avoid human errors during manual backups </ol> </ol> <h5 id="definitions">II. Definitions</h5> <div class="indent">Moved to [./glossary.md | a separate document].</div> <h5>III. Basic Fossil commands</h5> <ul> <li><p><b>clone</b> → Make a copy of a repository. The original repository is usually (but not always) on a remote machine and the copy is on the local machine. The copy remembers the network location from which it was copied and (by default) tries to keep itself synchronized with the original. <li><p><b>open</b> → Create a new check-out from a repository on the local machine. <li><p><b>update</b> → Modify an existing check-out so that it is derived from a different version of the same project. <li><p><b>commit</b> → Create a new version (a new check-in) of the project that is a snapshot of the current check-out. <li><p><b>revert</b> → Undo all local edits on a check-out. Make the check-out be an exact copy of its associated check-in. <li><p><b>push</b> → Copy content found in a local repository over to a remote repository. (Fossil usually does this automatically in response to a "commit" and so this command is seldom used, but it is important to understand it.) <li><p><b>pull</b> → Copy new content found in a remote repository into a local repository. A "pull" by itself does not modify any check-out. The "pull" command only moves content between repositories. However, the "update" command will (often) automatically do a "pull" before attempting to update the local check-out. <li><p><b>sync</b> → Do both a "push" and a "pull" at the same time. <li><p><b>add</b> → Add a new file to the local check-out. The file must already be on disk. This command tells Fossil to start tracking and managing the file. This command affects only the local check-out and does not modify any repository. The new file is inserted into the repository at the next "commit" command. <li><p><b>rm/mv</b> → Short for 'remove' and 'move', these commands are like "add" in that they specify pending changes to the structure of the check-out. As with "add", no changes are made to the repository until the next "commit". </ul> <h5>IV. The history of a project is a Directed Acyclic Graph (DAG)</h5> <ul> <li><p>Fossil (and other distributed VCSes like Git and Mercurial, but not Subversion) represent the history of a project as a directed acyclic graph (DAG). <ul> <li><p>Each check-in is a node in the graph <li><p>If check-in Y is derived from check-in X then there is an arc in the graph from node X to node Y. <li><p>The older check-in (X) is call the "parent" and the newer check-in (Y) is the "child". The child is derived from the parent. </ul> <li><p>Two users (or the same user working in different check-outs) might commit different changes against the same check-in. This results in one parent node having two or more children. <li><p>Command: <b>merge</b> → combines the work of multiple check-ins into a single check-out. That check-out can then be committed to create a new check-in that has two (or more) parents. <ul> <li><p>Most check-ins have just one parent, and either zero or one child. <li><p>When a check-in has two or more parents, one of those parents is the "primary parent". All the other parent nodes are "secondary" or "merge" parents. Conceptually, the primary parent shows the main line of development. Content from the merge parents is added into the main line. <li><p>The "direct children" of a check-in X are all children that have X as their primary parent. <li><p>A check-in node with no direct children is sometimes called a "leaf". <li><p>The "merge" command changes only the check-out. The "commit" command must be run subsequently to make the merge a permanent part of project. </ul> <li><p>Definition: <b>branch</b> → a sequence of check-ins that are all linked together in the DAG through the primary parent. <ul> <li><p>Branches are often given names which propagate to direct children. The tradition in Fossil is to call the main branch "trunk". In Git, it's called "master" by default, though some call it something else, like "main". <li><p>It is possible to have multiple branches with the same name. Fossil has no problem with this, but it can be confusing to humans, so best practice is to give each branch a unique name. <li><p>The name of a branch can be changed by adding special tags to the first check-in of a branch. The name assigned by this special tag automatically propagates to all direct children. </ul> </ul> <h5>V. Why version control is important (reprise)</h5> <ol> <li><p>Every check-in and every individual file has a unique name - its SHA1 or SHA3-256 hash. Team members can unambiguously identify any specific version of the overall project or any specific version of an individual file. <li><p>Any historical version of the whole project or of any individual file can be easily recreated at any time and by any team member. <li><p>Accidental changes to files can be detected by recomputing their cryptographic hash. <li><p>Files of unknown origin can be identified using their hash. <li><p>Developers are able to work in parallel, review each others work, and easily merge their changes together. External revisions to the baseline can be easily incorporated into the latest changes. <li><p>Developers can follow experimental lines of development, then revert back to an earlier stable version if the experiment does not work out. Creativity is enhanced by allowing crazy ideas to be investigated without destabilizing the project. <li><p>Developers can work on several independent subprojects, flipping back and forth from one subproject to another at will, and merge patches together or back into the main line of development as they mature. <li><p>Older changes can be easily backed out of recent revisions, for example if bugs are found long after the code was committed. <li><p>Enhancements in a branch can be easily copied into other branches, or into the trunk. <li><p>The complete history of all changes is plainly visible to all team members. Project leaders can easily keep track of what all team members are doing. Check-in comments help everyone to understand and/or remember the reason for each change. <li><p>New team members can be brought up-to-date with all of the historical code, quickly and easily. <li><p>New developers, interns, or inexperienced staff members who still do not understand all the details of the project or who are otherwise prone to making mistakes can be assigned significant subprojects to be carried out in branches without risking main line stability. <li><p>Code is automatically synchronized across all machines. No human effort is wasted copying files from machine to machine. The risk of human errors during file transfer and backup is eliminated. <li><p>A hardware failure results in minimal lost work because all previously committed changes will have been automatically replicated on other machines. <li><p>The complete work history of the project is conveniently archived in a single file, simplifying long-term record keeping. <li><p>A precise historical record is maintained which can be used to support copyright and patent claims or regulatory compliance. </ol> </ol> |
Changes to www/wikitheory.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 | <title>Wiki In Fossil</title> <h2>Introduction</h2> Fossil uses [/wiki_rules | Fossil wiki markup] and/or [/md_rules | Markdown markup] for many things: * Stand-alone wiki pages. * Description and comments in [./bugtheory.wiki | bug reports]. * Check-in comments. (For historical reasons, these must currently be in fossil-wiki text format.) * [./embeddeddoc.wiki | Embedded documentation] files whose name ends in ".wiki" or ".md" or ".markdown". * [./event.wiki | Technical notes]. * [./forum.wiki | Forum messages]. * Auxiliary notes on check-ins and branches. 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. The [/md_rules | Markdown formatting rules] are more complex, but are also more widely known, and are thus provided as an alternative. <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. The current implementation of the wiki shows the version of the wiki page that has the most recent time stamp. In other words, if two users make unrelated changes to the same wiki page on separate repositories and those repositories are synced, the wiki page will fork. The web interface will display whichever edit was checked in last. The other edit can be found in the history. The file format will support merging the branches back together, but there is no mechanism in the user interface (yet) to perform the merge. Every change to a wiki page is a separate [./fileformat.wiki | control artifact] of type [./fileformat.wiki#wikichng | "Wiki Page"]. <h2>Embedded Documentation</h2> Files in the source tree that use the ".wiki", ".md", or ".markdown" suffixes can be accessed and displayed using special URLs to the fossil server. This allows project documentation to be stored in the source tree and accessed online. (Details are described [./embeddeddoc.wiki | separately].) Some projects prefer to store their documentation in wiki. There is nothing wrong with that. But other projects prefer to keep documentation as part of the source tree, so that it is versioned along with the source tree and so that only developers with check-in privileges can change it. Embedded documentation serves this latter purpose. Both forms of documentation use the exact same markup. Some projects may choose to use both forms of documentation at the same time. Because the same format is used, it is trivial to move a file from wiki to embedded documentation or back again as the project evolves. <h2>Bug-reports and check-in comments and Forum messages</h2> The comments on check-ins, forum posts, and the text in the descriptions of bug reports both use wiki formatting. Exactly the same set of formatting rules apply. There is never a need to learn one formatting language for documentation and a different markup for bugs or for check-in comments. Minor caveat: check-in messages are currently limited to the fossil-wiki format. <h2 id="assocwiki">Auxiliary notes attached to check-ins or branches</h2> Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>" or "checkin/<i>HASH</i>" are associated with the corresponding branch or check-in. The wiki text appears in an "About" section of timelines and info screens. Examples: * [/timeline?r=graph-test-branch] shows the text of the [/wiki?name=branch/graph-test-branch&p|branch/graph-test-branch] wiki page at the top of the timeline * [/info/19c60b7fc9e2] shows the text of the [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6&p|checkin/19c60b7fc9e2...] wiki page in the "About" section. This special wiki pages are very useful for recording historical notes. |
Added www/wsl_caveats.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 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 | <title>Caveats and Precautions for Fossil Usage with Windows Subsystem for Linux</title> <h2>When These Issues Matter</h2> The discussion following is relevant to those who: * Are using the Windows Subsystem for Linux (aka "WSL"); * Create a Fossil checkout in a directory accessible from WSL and Windows; * Use both Linux Fossil and Windows tools to modify files in a checkout; * Desire to or must preserve execute permissions set for repository files; * and Use Linux Fossil to commit changes made within the checkout. Note that these criteria apply conjunctively; if any are not met, then the consequences of the issues below are at worst annoying and otherwise harmless or absent. <h2>What Can Go Wrong (Why It Matters)</h2> The most readily seen manifestation of the problem occurs when "<tt>fossil status</tt>" or "<tt>fossil changes</tt>" is run, using Linux Fossil from WSL after Windows tools (including fossil.exe) have been used to modify files within a checkout. Unless filter options block it, those subcommands will tag some (and often many) checkout files with <b>EXECUTABLE</b> or <b>NOEXEC</b>, indicating that the file's user execute permission has been altered such that it differs from what is recorded in the repository for that version of the file. This "user execute permission" is referred to as "the X-bit" hereafter, referring to either the recorded version state or a checkout file attributes state. This is merely annoying and distracting if the altered X-bit will never be committed using Linux Fossil. It can be quite distracting because those tags tend to mask the presence or absence of other changes whose detection is the usual reason for using Fossil's changes or status subcommands. However, in the problematic usage scenario, those tags will most often represent inadvertant toggling of the X-bit on the affected file. The X-bit is kept in the repository for good reason (usually), and arbitrary changes to it by means of a commit when that change is not intended is virtually always a bad result. (At best, the change causes useless churn; at worst it frustrates the intended purpose of having an X-bit.) <h2>Technical Discusion of the Problem</h2> The genesis of altered X-bits, while not obvious at first glance, involves obvious facts. The Windows OS does not deal with the triple of user/group/other executable permissions the way that Unix and similar operating systems do. Hence, tools which run on Windows, including Fossil built for Windows, do not manage the X-bit; it may not even exist yet for files which have not had their permissions set by any Linux program running in WSL. When such tools modify a file which has had its X-bit set (or cleared) by a program in WSL, an existing X-bit value may not be preserved depending upon how the modification is effected. The WSL infrastructure (or virtual system) compensates for the absence of an X-bit in Windows filesystems with two stratagems: (1) Establishing a default for its value when no Linux program has yet set it; and (2) stashing Linux "mode" bits in a "special" place for each file once it has been subject to a chmod() call. That default's default can be changed by way of /etc/wsl.conf content. However, this default cannot be right for files which are tracked in a Fossil repository as having the other value. And Windows tools generally are not written to deal with "mode" bits in that "special" place. (They are kept in a NTFS extended file attribute named $LXMOD, not accessible through the WIN32 API; the OS layer below WIN32 must be used to get at them.) Hence, inadvertant X-bit changes are unavoidable, or avoided only by luck, in the general usage case noted above. <h2>Problematic Usage Scenarios</h2> <h3>A Simple Example</h3> * Open a checkout in Windows (using fossil.exe) from a project whose files have a mixture of executable and non-executable files. Use a checkout directory visible when running under WSL. * Navigate to the same directory in a Linux shell on WSL, then run "fossil status". * Depending upon /etc/wsl.conf content (or defaults in its absence), the status ouput will tag checkout files as EXECUTABLE or NOEXEC. <h3>Continuation of Simple Example</h3> * In the same checkout as above "Simple Example", on WSL, run "fossil revert" to correct all those errant X-bit values. * Run "fossil status" again in WSL to verify absence of toggled X-bits. * Run "ls -l" from WSL to find two files, one with its X-bit set and the other with it clear. * From Windows, perform these steps on each of those files:<br> (1) read the_file content into a buffer<br> (2) rename the_file the_file.bak<br> (3) write buffer content to new file, the_file<br> (4) del the_file.bak (or leave it)<br> (Note that this sequence is similar to what many editors do when a user modifies a file then uses undo to reverse the changes.) * Run "fossil status" again in WSL and observe that one of the two files has had its X-bit toggled. <h3>A Fossil-Only Example</h3> * In the another (different) checkout of the same version, somehow cause "legitimate" X-bit toggles of two files whose X-bits differ. (This "somehow" probably will involve WSL to toggle file bits and fossil on WSL to commit the toggles.) * In the Simple Example checkout, use fossil.exe on Windows to update the checkout, ostensibly bringing the X-bit toggles into the affected checkout files. * In the Simple Example checkout, use fossil on WSL to run "fossil status", and observe at least one X-bit discrepancy. <h2>Recommended Workflow</h2> There are two simple approaches for dealing with this issue when one wishes to continue using the same checkout directory from Windows and WSL. Either one is effective. These are: * Do not use fossil on WSL for any operations which will modify the repository. Instead, block those operations in some manner. * Do not use any tools on Windows, (including certain subcommands of fossil.exe,) which may modify the X-bits on files within the shared checkout, instead restricting use of Windows tools to those which are known to only (and actually) modify file content in place while preserving X-bit values. (The "actually" proviso emphasizes that tools which only simulate in-place file modification, but do so via create combined with delete and rename, are to be avoided. A simulation which works flawlessly on Windows may not preserve the WSL X-bit.) There are more complex ways to deal with this issue, involving use of fossil on WSL to fix (or revert) toggled X-bits prior to any commit, together with actions needed to preserve all intended changes to the checkout as fossil revert is done. Such methods are overly clever or fragile for elaboration here. Another way to deal with this issue is to correct any toggled X-bits within a checkout before using "fossil commit" on WSL by means other than "fossil revert". <h2>Corrective Measures or Mitigation</h2> It is possible, by either manual or automated means, to perform a pre-commit check and/or correction for mis-toggled X-bits. The X-bit states are available from the repository for whatever versions it has stored. And several Linux tools are able to read or alter the X-bit state of files. With these components, a tool can be readily built to aid avoidance of a commit (via fossil on WSL) that would record mis-toggled X-bits into the repository. Fossil itself on WSL will detect mis-toggled X-bits for files which have not been otherise modified, but altered file content masks such detection, and it is just such modification that is among the problematic scenarios. So Fossil alone cannot yet reliably do the detection or correction needed to avoid or remedy the mis-toggled X-bit commit problem. It is also feasible to detect or correct the mis-toggled X-bit problem within Windows with a special-purpose tool which can read, create or modify the X-bits stored by WSL for any file which has been subject to the Linux chmod(...) system call. Creation of these tools is beyond the scope of this document. |
Added www/xkcd-git.gif.
cannot compute difference between binary files